summaryrefslogtreecommitdiffstats
path: root/sys
diff options
context:
space:
mode:
authorian <ian@FreeBSD.org>2016-01-24 22:00:36 +0000
committerian <ian@FreeBSD.org>2016-01-24 22:00:36 +0000
commit650a9addf1aa6411b6dfefe94fcf78fe0b856fe8 (patch)
tree9ab101a5aad57290be783adabad116d206466c4d /sys
parent33902405d5bbd7b2931c254ec445012ce3f55107 (diff)
downloadFreeBSD-src-650a9addf1aa6411b6dfefe94fcf78fe0b856fe8.zip
FreeBSD-src-650a9addf1aa6411b6dfefe94fcf78fe0b856fe8.tar.gz
MFC r293053, r293061, r293063, r293064, r293065, r293775, r293792:
Use 64-bit math when finding a block of ram to hold the kernel. This fixes a problem on 32-bit systems which have ram occupying the end of the physical address space -- for example, a block of ram at 0x80000000 with a size of 0x80000000 was overflowing 32 bit math and ending up with a calculated size of zero. Use 64-bit math when processing the lists of physical and excluded memory to generate the phys_avail and dump_avail arrays. Work around problems that happen when there is ram at the end of the physical address space. Cast pointer through uintptr_t on the way to uint64_t to squelch a warning. Reword the comment to better describe what I found while researching the problem that led to this temporary workaround (and also so I can properly cite the PR in the commit this time). Cast using uintfptr_t and eliminate the cast to uint64_t which is uneeded because rounding down cannot increase the number of bits needed to express the result. Go back to using uintptr_t, because code that actually compiles is infinitely less buggy than code that is theoretically correct in some alternate universe. PR: 201614
Diffstat (limited to 'sys')
-rw-r--r--sys/arm/arm/physmem.c29
-rw-r--r--sys/boot/uboot/lib/copy.c22
2 files changed, 33 insertions, 18 deletions
diff --git a/sys/arm/arm/physmem.c b/sys/arm/arm/physmem.c
index bc72ce2..8c41fbb 100644
--- a/sys/arm/arm/physmem.c
+++ b/sys/arm/arm/physmem.c
@@ -161,7 +161,7 @@ static size_t
regions_to_avail(vm_paddr_t *avail, uint32_t exflags, long *pavail)
{
size_t acnt, exi, hwi;
- vm_paddr_t end, start, xend, xstart;
+ uint64_t end, start, xend, xstart;
long availmem;
const struct region *exp, *hwp;
@@ -171,7 +171,7 @@ regions_to_avail(vm_paddr_t *avail, uint32_t exflags, long *pavail)
for (hwi = 0, hwp = hwregions; hwi < hwcnt; ++hwi, ++hwp) {
start = hwp->addr;
end = hwp->size + start;
- realmem += arm32_btop(end - start);
+ realmem += arm32_btop((vm_offset_t)(end - start));
for (exi = 0, exp = exregions; exi < excnt; ++exi, ++exp) {
/*
* If the excluded region does not match given flags,
@@ -212,9 +212,10 @@ regions_to_avail(vm_paddr_t *avail, uint32_t exflags, long *pavail)
* could affect the remainder of this hw region.
*/
if ((xstart > start) && (xend < end)) {
- avail[acnt++] = start;
- avail[acnt++] = xstart;
- availmem += arm32_btop(xstart - start);
+ avail[acnt++] = (vm_paddr_t)start;
+ avail[acnt++] = (vm_paddr_t)xstart;
+ availmem +=
+ arm32_btop((vm_offset_t)(xstart - start));
start = xend;
continue;
}
@@ -233,9 +234,9 @@ regions_to_avail(vm_paddr_t *avail, uint32_t exflags, long *pavail)
* available entry for it.
*/
if (end > start) {
- avail[acnt++] = start;
- avail[acnt++] = end;
- availmem += arm32_btop(end - start);
+ avail[acnt++] = (vm_paddr_t)start;
+ avail[acnt++] = (vm_paddr_t)end;
+ availmem += arm32_btop((vm_offset_t)(end - start));
}
if (acnt >= MAX_AVAIL_ENTRIES)
panic("Not enough space in the dump/phys_avail arrays");
@@ -279,10 +280,22 @@ arm_physmem_hardware_region(vm_paddr_t pa, vm_size_t sz)
/*
* Filter out the page at PA 0x00000000. The VM can't handle it, as
* pmap_extract() == 0 means failure.
+ *
+ * Also filter out the page at the end of the physical address space --
+ * if addr is non-zero and addr+size is zero we wrapped to the next byte
+ * beyond what vm_paddr_t can express. That leads to a NULL pointer
+ * deref early in startup; work around it by leaving the last page out.
+ *
+ * XXX This just in: subtract out a whole megabyte, not just 1 page.
+ * Reducing the size by anything less than 1MB results in the NULL
+ * pointer deref in _vm_map_lock_read(). Better to give up a megabyte
+ * than leave some folks with an unusable system while we investigate.
*/
if (pa == 0) {
pa = PAGE_SIZE;
sz -= PAGE_SIZE;
+ } else if (pa + sz == 0) {
+ sz -= 1024 * 1024;
}
/*
diff --git a/sys/boot/uboot/lib/copy.c b/sys/boot/uboot/lib/copy.c
index bb658e3..76f2faf 100644
--- a/sys/boot/uboot/lib/copy.c
+++ b/sys/boot/uboot/lib/copy.c
@@ -69,11 +69,11 @@ uint64_t
uboot_loadaddr(u_int type, void *data, uint64_t addr)
{
struct sys_info *si;
- uintptr_t sblock, eblock, subldr, eubldr;
- uintptr_t biggest_block, this_block;
- size_t biggest_size, this_size;
+ uint64_t sblock, eblock, subldr, eubldr;
+ uint64_t biggest_block, this_block;
+ uint64_t biggest_size, this_size;
int i;
- char * envstr;
+ char *envstr;
if (addr == 0) {
/*
@@ -101,13 +101,14 @@ uboot_loadaddr(u_int type, void *data, uint64_t addr)
biggest_block = 0;
biggest_size = 0;
subldr = rounddown2((uintptr_t)_start, KERN_ALIGN);
- eubldr = roundup2(uboot_heap_end, KERN_ALIGN);
+ eubldr = roundup2((uint64_t)uboot_heap_end, KERN_ALIGN);
for (i = 0; i < si->mr_no; i++) {
if (si->mr[i].flags != MR_ATTR_DRAM)
continue;
- sblock = roundup2(si->mr[i].start, KERN_ALIGN);
- eblock = rounddown2(si->mr[i].start + si->mr[i].size,
+ sblock = roundup2((uint64_t)si->mr[i].start,
KERN_ALIGN);
+ eblock = rounddown2((uint64_t)si->mr[i].start +
+ si->mr[i].size, KERN_ALIGN);
if (biggest_size == 0)
sblock += KERN_MINADDR;
if (subldr >= sblock && subldr < eblock) {
@@ -127,9 +128,10 @@ uboot_loadaddr(u_int type, void *data, uint64_t addr)
if (biggest_size == 0)
panic("Not enough DRAM to load kernel\n");
#if 0
- printf("Loading kernel into region 0x%08x-0x%08x (%u MiB)\n",
- biggest_block, biggest_block + biggest_size - 1,
- biggest_size / 1024 / 1024);
+ printf("Loading kernel into region 0x%08jx-0x%08jx (%ju MiB)\n",
+ (uintmax_t)biggest_block,
+ (uintmax_t)biggest_block + biggest_size - 1,
+ (uintmax_t)biggest_size / 1024 / 1024);
#endif
return (biggest_block);
}
OpenPOWER on IntegriCloud