From 349c99ee28c800cca8338621e90b603e7d452adc Mon Sep 17 00:00:00 2001 From: Matthew Dillon Date: Sun, 26 Jul 2015 01:03:46 -0700 Subject: [PATCH] boot - Put loader heap in high memory * We ran out of low BIOS memory. The initrd.img.gz unpacking requires a bit more than ~32KB of temporary space and it ran the loader out of low-BIOS heap memory. This can cause the loader to fail to load the image and prevent proper crypto or single-user bootstrapping. * Steal the heap from high-memory, just ender the kernel+modules load area limit. For now, steal 1MB, which gives the loader plenty of space again. The kernel will lose 1MB of physical memory, boo-hoo. * The loader must remove the heap from the SMAP passed to the kernel. * Move any objects requiring VTOPSEG/VTOPOFF out of malloc and into a static or stack declaration. Reported-by: cgag, multiple --- lib/libstand/sbrk.c | 10 ++++----- sys/boot/common/module.c | 11 ++++++++- sys/boot/common/rel_open.c | 14 +++++++----- sys/boot/pc32/libi386/biosdisk.c | 9 ++++---- sys/boot/pc32/libi386/biosmem.c | 4 ++++ sys/boot/pc32/libi386/biossmap.c | 38 +++++++++++++++++++++++++++++--- sys/boot/pc32/libi386/libi386.h | 1 + sys/boot/pc32/loader/main.c | 11 +++++++++ 8 files changed, 79 insertions(+), 19 deletions(-) diff --git a/lib/libstand/sbrk.c b/lib/libstand/sbrk.c index e33284bbbe..3c5fe1efa4 100644 --- a/lib/libstand/sbrk.c +++ b/lib/libstand/sbrk.c @@ -35,21 +35,21 @@ #include "stand.h" static size_t maxheap, heapsize = 0; -static void *heapbase; +static void *sbrkbase; void setheap(void *base, void *top) { /* Align start address to 16 bytes for the malloc code. Sigh. */ - heapbase = (void *)(((uintptr_t)base + 15) & ~15); - maxheap = top - heapbase; + sbrkbase = (void *)(((uintptr_t)base + 15) & ~15); + maxheap = top - sbrkbase; } char * getheap(size_t *sizep) { *sizep = maxheap; - return heapbase; + return sbrkbase; } char * @@ -58,7 +58,7 @@ sbrk(int incr) char *ret; if ((heapsize + incr) <= maxheap) { - ret = heapbase + heapsize; + ret = sbrkbase + heapsize; bzero(ret, incr); heapsize += incr; return(ret); diff --git a/sys/boot/common/module.c b/sys/boot/common/module.c index 6e43b1c35e..93e958f2a4 100644 --- a/sys/boot/common/module.c +++ b/sys/boot/common/module.c @@ -36,6 +36,7 @@ #include #include #include +#include "libi386/libi386.h" #include "bootstrap.h" @@ -425,11 +426,19 @@ file_loadraw(char *type, char *name) laddr = loadaddr; for (;;) { /* read in 4k chunks; size is not really important */ + if (laddr + 4096 > heapbase) { + sprintf(command_errbuf, "error reading '%s': out of load memory", + name); + free(name); + close(fd); + return(CMD_ERROR); + } got = archsw.arch_readin(fd, laddr, 4096); if (got == 0) /* end of file */ break; if (got < 0) { /* error */ - sprintf(command_errbuf, "error reading '%s': %s", name, strerror(errno)); + sprintf(command_errbuf, "error reading '%s': %s", + name, strerror(errno)); free(name); close(fd); return(CMD_ERROR); diff --git a/sys/boot/common/rel_open.c b/sys/boot/common/rel_open.c index 14df98811a..cde7020bb7 100644 --- a/sys/boot/common/rel_open.c +++ b/sys/boot/common/rel_open.c @@ -161,18 +161,21 @@ rel_open(const char *path, char **abspathp, int flags) ptr = malloc(strlen(DirBase) + strlen(path) + 1); sprintf(ptr, "%s%s", DirBase, path); fd = open(ptr, flags); - if (abspathp && fd >= 0) + if (abspathp && fd >= 0) { *abspathp = ptr; - else if (abspathp) + } else if (abspathp) { *abspathp = NULL; - else free(ptr); + } else { + free(ptr); + } } else { fd = open(path, flags); - if (abspathp && fd >= 0) + if (abspathp && fd >= 0) { *abspathp = strdup(path); - else if (abspathp) + } else if (abspathp) { *abspathp = NULL; + } } return(fd); } @@ -196,4 +199,3 @@ rel_stat(const char *path, struct stat *st) } return(res); } - diff --git a/sys/boot/pc32/libi386/biosdisk.c b/sys/boot/pc32/libi386/biosdisk.c index b761c7355a..48bd510df0 100644 --- a/sys/boot/pc32/libi386/biosdisk.c +++ b/sys/boot/pc32/libi386/biosdisk.c @@ -148,7 +148,11 @@ static int bd_bestslice(struct open_disk *od); static void bd_chainextended(struct open_disk *od, u_int32_t base, u_int32_t offset); -static caddr_t bounce_base; +/* + * Bounce buffers can no longer be malloc()'d because the malloc pool + * now uses high memory. Declare statically. + */ +static char bounce_base[BOUNCEBUF_SIZE]; /* * Translate between BIOS device numbers and our private unit numbers. @@ -183,9 +187,6 @@ bd_init(void) { int base, unit, nfd = 0; - if (bounce_base == NULL) - bounce_base = malloc(BOUNCEBUF_SIZE * 2); - /* sequence 0, 0x80 */ for (base = 0; base <= 0x80; base += 0x80) { for (unit = base; (nbdinfo < MAXBDDEV); unit++) { diff --git a/sys/boot/pc32/libi386/biosmem.c b/sys/boot/pc32/libi386/biosmem.c index 034643bbbd..c284b75f9f 100644 --- a/sys/boot/pc32/libi386/biosmem.c +++ b/sys/boot/pc32/libi386/biosmem.c @@ -33,7 +33,10 @@ #include "libi386.h" #include "btxv86.h" +#define LOADER_HEAP_SIZE (1024 * 1024) + vm_offset_t memtop; +vm_offset_t heapbase; u_int32_t bios_basemem, bios_extmem, bios_howmem; #define SMAPSIG 0x534D4150 @@ -127,5 +130,6 @@ bios_getmem(void) */ memtop = 0x100000 + bios_extmem; /* XXX ignored */ memtop = 64 * 1024 * 1024; + heapbase = memtop - LOADER_HEAP_SIZE; } diff --git a/sys/boot/pc32/libi386/biossmap.c b/sys/boot/pc32/libi386/biossmap.c index 75ffa17dfb..edf130bac7 100644 --- a/sys/boot/pc32/libi386/biossmap.c +++ b/sys/boot/pc32/libi386/biossmap.c @@ -85,10 +85,42 @@ bios_getsmap(void) v86.eax = 0xe820; v86.ecx = sizeof(struct smap); v86.edx = SMAPSIG; - v86.es = VTOPSEG(&smapbase[smaplen]); - v86.edi = VTOPOFF(&smapbase[smaplen]); + v86.es = VTOPSEG(&smap); + v86.edi = VTOPOFF(&smap); v86int(); - smaplen++; + + /* + * Our heap is now in high memory and must be removed from + * the smap so the kernel does not blow away passed-in + * arguments, smap, kenv, etc. + * + * This wastes a little memory. + */ + if (smap.type == 1 && + smap.base + smap.length > heapbase && + smap.base < memtop) { + if (smap.base <= heapbase) { + if (heapbase - smap.base) { + smapbase[smaplen] = smap; + smapbase[smaplen].length = + heapbase - smap.base; + ++smaplen; + } + } + if (smap.base + smap.length >= memtop) { + if (smap.base + smap.length - memtop) { + smapbase[smaplen] = smap; + smapbase[smaplen].base = memtop; + smapbase[smaplen].length = + smap.base + smap.length - + memtop; + ++smaplen; + } + } + } else { + smapbase[smaplen] = smap; + ++smaplen; + } if ((v86.efl & 1) || (v86.eax != SMAPSIG)) break; } while (v86.ebx != 0 && smaplen < n); diff --git a/sys/boot/pc32/libi386/libi386.h b/sys/boot/pc32/libi386/libi386.h index 1c8b1275bb..228cd50ef2 100644 --- a/sys/boot/pc32/libi386/libi386.h +++ b/sys/boot/pc32/libi386/libi386.h @@ -107,6 +107,7 @@ void bios_getmem(void); extern u_int32_t bios_basemem; /* base memory in bytes */ extern u_int32_t bios_extmem; /* extended memory in bytes */ extern vm_offset_t memtop; +extern vm_offset_t heapbase; void biosacpi_detect(void); diff --git a/sys/boot/pc32/loader/main.c b/sys/boot/pc32/loader/main.c index 9e6c64982d..9789ad9c72 100644 --- a/sys/boot/pc32/loader/main.c +++ b/sys/boot/pc32/loader/main.c @@ -173,6 +173,15 @@ main(void) bios_getmem(); memend = (char *)&memend - 0x8000; /* space for stack (16K) */ memend = (char *)((uintptr_t)memend & ~(uintptr_t)(0x1000 - 1)); + + /* + * Steal the heap from high memory. bios_basemem no longer has + * enough space, last failure was the gunzip code for initrd.img + * needing > ~32KB of temporary buffer space and we ran out. again. + * + * High memory reserves at least ~1MB for the loader. + */ +#if 0 if (memend < (char *)_end) { setheap((void *)_end, PTOV(bios_basemem)); } else { @@ -180,6 +189,8 @@ main(void) memend = (char *)PTOV(bios_basemem); setheap((void *)_end, memend); } +#endif + setheap((void *)heapbase, (void *)memtop); /* * XXX Chicken-and-egg problem; we want to have console output early, -- 2.41.0