boot - Put loader heap in high memory
authorMatthew Dillon <dillon@apollo.backplane.com>
Sun, 26 Jul 2015 08:03:46 +0000 (01:03 -0700)
committerMatthew Dillon <dillon@apollo.backplane.com>
Sun, 26 Jul 2015 08:03:46 +0000 (01:03 -0700)
* 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
sys/boot/common/module.c
sys/boot/common/rel_open.c
sys/boot/pc32/libi386/biosdisk.c
sys/boot/pc32/libi386/biosmem.c
sys/boot/pc32/libi386/biossmap.c
sys/boot/pc32/libi386/libi386.h
sys/boot/pc32/loader/main.c

index e33284b..3c5fe1e 100644 (file)
 #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);
index 6e43b1c..93e958f 100644 (file)
@@ -36,6 +36,7 @@
 #include <sys/linker.h>
 #include <sys/module.h>
 #include <sys/queue.h>
+#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);
index 14df988..cde7020 100644 (file)
@@ -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);
 }
-
index b761c73..48bd510 100644 (file)
@@ -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++) {
index 034643b..c284b75 100644 (file)
 #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;
 }    
 
index 75ffa17..edf130b 100644 (file)
@@ -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);
index 1c8b127..228cd50 100644 (file)
@@ -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);
 
index 9e6c649..9789ad9 100644 (file)
@@ -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,