Add Post Memory Manager (PMM) support.

Complete the initial implementation of PMM.
Default new PMM code to be enabled.
Move malloc code from memmap.c to pmm.c.
Define new malloc zones for PMM (ZoneTmpLow and ZoneTmpHigh).
Change default READ/WRITE_SEG macros to use 32bit pointers (the 16bit
    PMM code use "big real" mode and requires 32bit accesses).
Allow pmm allocations to occur during bcv executions also.
Add low-memory clearing before boot.
Also, align the default f-seg memory.
diff --git a/src/acpi.c b/src/acpi.c
index ef4eaf9..2198805 100644
--- a/src/acpi.c
+++ b/src/acpi.c
@@ -7,7 +7,6 @@
 
 #include "acpi.h" // struct rsdp_descriptor
 #include "util.h" // memcpy
-#include "memmap.h" // malloc_fseg
 #include "pci.h" // pci_find_device
 #include "biosvar.h" // GET_EBDA
 #include "pci_ids.h" // PCI_VENDOR_ID_INTEL
diff --git a/src/biosvar.h b/src/biosvar.h
index 821aa35..0d175ef 100644
--- a/src/biosvar.h
+++ b/src/biosvar.h
@@ -1,6 +1,6 @@
 // Variable layouts of bios.
 //
-// Copyright (C) 2008  Kevin O'Connor <kevin@koconnor.net>
+// Copyright (C) 2008,2009  Kevin O'Connor <kevin@koconnor.net>
 //
 // This file may be distributed under the terms of the GNU LGPLv3 license.
 #ifndef __BIOSVAR_H
@@ -240,6 +240,8 @@
     DIV_ROUND_UP(sizeof(struct extended_bios_data_area_s), 1024)
 #define EBDA_SEGMENT_START \
     FLATPTR_TO_SEG((640 - EBDA_SIZE_START) * 1024)
+#define EBDA_SEGMENT_MINIMUM \
+    FLATPTR_TO_SEG((640 - 256) * 1024)
 
 // Accessor functions
 static inline u16 get_ebda_seg() {
diff --git a/src/config.h b/src/config.h
index 36f2a72..20e74c7 100644
--- a/src/config.h
+++ b/src/config.h
@@ -40,7 +40,7 @@
 // Support PnP BIOS entry point.
 #define CONFIG_PNPBIOS 1
 // Support Post Memory Manager (PMM) entry point.
-#define CONFIG_PMM 0
+#define CONFIG_PMM 1
 // Support int 19/18 system bootup support
 #define CONFIG_BOOT 1
 // Support an interactive boot menu at end of post.
diff --git a/src/coreboot.c b/src/coreboot.c
index 4ab2a7a..82c9f8a 100644
--- a/src/coreboot.c
+++ b/src/coreboot.c
@@ -11,7 +11,6 @@
 #include "mptable.h" // MPTABLE_SIGNATURE
 #include "biosvar.h" // GET_EBDA
 #include "lzmadecode.h" // LzmaDecode
-#include "memmap.h" // malloc_fseg
 
 
 /****************************************************************
diff --git a/src/farptr.h b/src/farptr.h
index a270f87..1f3df9d 100644
--- a/src/farptr.h
+++ b/src/farptr.h
@@ -1,6 +1,6 @@
 // Code to access multiple segments within gcc.
 //
-// Copyright (C) 2008  Kevin O'Connor <kevin@koconnor.net>
+// Copyright (C) 2008,2009  Kevin O'Connor <kevin@koconnor.net>
 //
 // This file may be distributed under the terms of the GNU LGPLv3 license.
 #ifndef __FARPTR_H
@@ -15,13 +15,13 @@
 
 // Low level macros for reading/writing memory via a segment selector.
 #define READ8_SEG(SEG, value, var)                      \
-    __asm__("movb %%" #SEG ":%1, %b0" : "=Qi"(value)    \
+    __asm__("addr32 movb %%" #SEG ":%1, %b0" : "=Qi"(value)    \
             : "m"(var), "m"(__segment_ ## SEG))
 #define READ16_SEG(SEG, value, var)                     \
-    __asm__("movw %%" #SEG ":%1, %w0" : "=ri"(value)    \
+    __asm__("addr32 movw %%" #SEG ":%1, %w0" : "=ri"(value)    \
             : "m"(var), "m"(__segment_ ## SEG))
 #define READ32_SEG(SEG, value, var)                     \
-    __asm__("movl %%" #SEG ":%1, %0" : "=ri"(value)     \
+    __asm__("addr32 movl %%" #SEG ":%1, %0" : "=ri"(value)     \
             : "m"(var), "m"(__segment_ ## SEG))
 #define READ64_SEG(SEG, value, var) do {                        \
         union u64_u32_u __value;                                \
@@ -31,18 +31,19 @@
         *(u64*)&(value) = __value.val;                          \
     } while (0)
 #define WRITE8_SEG(SEG, var, value)                             \
-    __asm__("movb %b1, %%" #SEG ":%0" : "=m"(var)               \
+    __asm__("addr32 movb %b1, %%" #SEG ":%0" : "=m"(var)               \
             : "Q"(value), "m"(__segment_ ## SEG))
 #define WRITE16_SEG(SEG, var, value)                            \
-    __asm__("movw %w1, %%" #SEG ":%0" : "=m"(var)               \
+    __asm__("addr32 movw %w1, %%" #SEG ":%0" : "=m"(var)               \
             : "r"(value), "m"(__segment_ ## SEG))
 #define WRITE32_SEG(SEG, var, value)                            \
-    __asm__("movl %1, %%" #SEG ":%0" : "=m"(var)                \
+    __asm__("addr32 movl %1, %%" #SEG ":%0" : "=m"(var)                \
             : "r"(value), "m"(__segment_ ## SEG))
 #define WRITE64_SEG(SEG, var, value) do {                       \
         union u64_u32_u __value;                                \
         union u64_u32_u *__w64_ptr = (union u64_u32_u *)&(var); \
-        __value.val = (value);                                  \
+        typeof(var) __value_tmp = (value);                      \
+        __value.val = *(u64*)&__value_tmp;                      \
         WRITE32_SEG(SEG, __w64_ptr->hi, __value.hi);            \
         WRITE32_SEG(SEG, __w64_ptr->lo, __value.lo);            \
     } while (0)
diff --git a/src/memmap.c b/src/memmap.c
index 9fc3626..005c474 100644
--- a/src/memmap.c
+++ b/src/memmap.c
@@ -132,93 +132,3 @@
 {
     dump_map();
 }
-
-
-/****************************************************************
- * malloc
- ****************************************************************/
-
-#define MINALIGN 16
-
-struct zone_s {
-    u32 top, bottom, cur;
-};
-
-static struct zone_s ZoneHigh, ZoneFSeg;
-
-static void *
-__malloc(struct zone_s *zone, u32 size)
-{
-    u32 newpos = (zone->cur - size) / MINALIGN * MINALIGN;
-    if ((s32)(newpos - zone->bottom) < 0)
-        // No space
-        return NULL;
-    zone->cur = newpos;
-    return (void*)newpos;
-}
-
-// Allocate memory at the top of 32bit ram.
-void *
-malloc_high(u32 size)
-{
-    return __malloc(&ZoneHigh, size);
-}
-
-// Allocate memory in the 0xf0000-0x100000 area of ram.
-void *
-malloc_fseg(u32 size)
-{
-    return __malloc(&ZoneFSeg, size);
-}
-
-void
-malloc_setup()
-{
-    dprintf(3, "malloc setup\n");
-
-    // Memory in 0xf0000 area.
-    memset(BiosTableSpace, 0, CONFIG_MAX_BIOSTABLE);
-    ZoneFSeg.bottom = (u32)BiosTableSpace;
-    ZoneFSeg.top = ZoneFSeg.cur = ZoneFSeg.bottom + CONFIG_MAX_BIOSTABLE;
-
-    // Find memory at the top of ram.
-    u32 top = 0;
-    int i;
-    for (i=e820_count-1; i>=0; i--) {
-        struct e820entry *e = &e820_list[i];
-        u64 end = e->start + e->size;
-        if (e->type != E820_RAM || end > 0xffffffff
-            || e->size < CONFIG_MAX_HIGHTABLE)
-            continue;
-        top = end;
-        break;
-    }
-    if (top < 1024*1024 + CONFIG_MAX_HIGHTABLE) {
-        // No memory above 1Meg
-        memset(&ZoneHigh, 0, sizeof(ZoneHigh));
-        return;
-    }
-    ZoneHigh.bottom = top - CONFIG_MAX_HIGHTABLE;
-    ZoneHigh.top = ZoneHigh.cur = ZoneHigh.bottom + CONFIG_MAX_HIGHTABLE;
-    add_e820(ZoneHigh.bottom, CONFIG_MAX_HIGHTABLE, E820_RESERVED);
-}
-
-void
-malloc_finalize()
-{
-    dprintf(3, "malloc finalize\n");
-
-    // Give back unused high ram.
-    u32 giveback = (ZoneHigh.cur - ZoneHigh.bottom) / 4096 * 4096;
-    add_e820(ZoneHigh.bottom, giveback, E820_RAM);
-
-    // Report statistics
-    u32 used = ZoneFSeg.top - ZoneFSeg.cur;
-    u32 avail = ZoneFSeg.top - ZoneFSeg.bottom;
-    dprintf(1, "malloc_fseg used=%d (%d%%)\n"
-            , used, (100 * used) / avail);
-    used = ZoneHigh.top - ZoneHigh.cur;
-    avail = ZoneHigh.top - ZoneHigh.bottom;
-    dprintf(1, "malloc_high used=%d (%d%%) (returned %d)\n"
-            , used, (100 * used) / avail, giveback);
-}
diff --git a/src/memmap.h b/src/memmap.h
index 8df9ed0..51471d6 100644
--- a/src/memmap.h
+++ b/src/memmap.h
@@ -20,11 +20,6 @@
 void memmap_setup();
 void memmap_finalize();
 
-void *malloc_high(u32 size);
-void *malloc_fseg(u32 size);
-void malloc_setup();
-void malloc_finalize();
-
 // e820 map storage (defined in system.c)
 extern struct e820entry e820_list[];
 extern int e820_count;
diff --git a/src/misc.c b/src/misc.c
index 956e545..167dfe5 100644
--- a/src/misc.c
+++ b/src/misc.c
@@ -15,7 +15,7 @@
 // Amount of continuous ram >4Gig
 u64 RamSizeOver4G;
 // Space for bios tables built an run-time.
-char BiosTableSpace[CONFIG_MAX_BIOSTABLE] VAR16_32;
+char BiosTableSpace[CONFIG_MAX_BIOSTABLE] __aligned(MALLOC_MIN_ALIGN) VAR16_32;
 
 
 /****************************************************************
diff --git a/src/mptable.c b/src/mptable.c
index 8f622ff..aeb1f94 100644
--- a/src/mptable.c
+++ b/src/mptable.c
@@ -6,7 +6,6 @@
 // This file may be distributed under the terms of the GNU LGPLv3 license.
 
 #include "util.h" // dprintf
-#include "memmap.h" // malloc_fseg
 #include "config.h" // CONFIG_*
 #include "mptable.h" // MPTABLE_SIGNATURE
 
diff --git a/src/pmm.c b/src/pmm.c
index bf53e3b..3e08da1 100644
--- a/src/pmm.c
+++ b/src/pmm.c
@@ -6,6 +6,270 @@
 
 #include "util.h" // checksum
 #include "config.h" // BUILD_BIOS_ADDR
+#include "memmap.h" // e820_list
+#include "farptr.h" // GET_FARVAR
+#include "biosvar.h" // EBDA_SEGMENT_MINIMUM
+
+
+/****************************************************************
+ * malloc
+ ****************************************************************/
+
+#if MODE16
+// The 16bit pmm entry points runs in "big real" mode, and can
+// therefore read/write to the 32bit malloc variables.
+#define GET_PMMVAR(var) GET_FARVAR(0, (var))
+#define SET_PMMVAR(var, val) SET_FARVAR(0, (var), (val))
+#else
+#define GET_PMMVAR(var) (var)
+#define SET_PMMVAR(var, val) do { (var) = (val); } while (0)
+#endif
+
+// Zone definitions
+struct zone_s {
+    u32 top, bottom, cur;
+};
+
+struct zone_s ZoneHigh VAR32VISIBLE, ZoneFSeg VAR32VISIBLE;
+struct zone_s ZoneTmpLow VAR32VISIBLE, ZoneTmpHigh VAR32VISIBLE;
+
+struct zone_s *Zones[] VAR32VISIBLE = {
+    &ZoneTmpLow, &ZoneFSeg, &ZoneTmpHigh, &ZoneHigh
+};
+
+// Obtain memory from a given zone.
+static void *
+zone_malloc(struct zone_s *zone, u32 size, u32 align)
+{
+    u32 newpos = (GET_PMMVAR(zone->cur) - size) / align * align;
+    if ((s32)(newpos - GET_PMMVAR(zone->bottom)) < 0)
+        // No space
+        return NULL;
+    SET_PMMVAR(zone->cur, newpos);
+    return (void*)newpos;
+}
+
+// Return memory to a zone (if it was the last to be allocated).
+static void
+zone_free(struct zone_s *zone, void *data, u32 olddata)
+{
+    if (! data || GET_PMMVAR(zone->cur) != (u32)data)
+        return;
+    SET_PMMVAR(zone->cur, olddata);
+}
+
+// Find the zone that contains the given data block.
+static struct zone_s *
+zone_find(void *data)
+{
+    int i;
+    for (i=0; i<ARRAY_SIZE(Zones); i++) {
+        struct zone_s *zone = GET_PMMVAR(Zones[i]);
+        if ((u32)data >= GET_PMMVAR(zone->cur)
+            && (u32)data < GET_PMMVAR(zone->top))
+            return zone;
+    }
+    return NULL;
+}
+
+// Report the status of all the zones.
+static void
+dumpZones()
+{
+    int i;
+    for (i=0; i<ARRAY_SIZE(Zones); i++) {
+        struct zone_s *zone = Zones[i];
+        u32 used = zone->top - zone->cur;
+        u32 avail = zone->top - zone->bottom;
+        dprintf(2, "zone %d: %08x-%08x used=%d (%d%%)\n"
+                , i, zone->bottom, zone->top, used, (100 * used) / avail);
+    }
+}
+
+// Allocate memory at the top of 32bit ram.
+void *
+malloc_high(u32 size)
+{
+    return zone_malloc(&ZoneHigh, size, MALLOC_MIN_ALIGN);
+}
+
+// Allocate memory in the 0xf0000-0x100000 area of ram.
+void *
+malloc_fseg(u32 size)
+{
+    return zone_malloc(&ZoneFSeg, size, MALLOC_MIN_ALIGN);
+}
+
+void
+malloc_setup()
+{
+    ASSERT32();
+    dprintf(3, "malloc setup\n");
+
+    // Memory in 0xf0000 area.
+    memset(BiosTableSpace, 0, CONFIG_MAX_BIOSTABLE);
+    ZoneFSeg.bottom = (u32)BiosTableSpace;
+    ZoneFSeg.top = ZoneFSeg.cur = ZoneFSeg.bottom + CONFIG_MAX_BIOSTABLE;
+
+    // Memory under 1Meg.
+    ZoneTmpLow.bottom = BUILD_STACK_ADDR;
+    ZoneTmpLow.top = ZoneTmpLow.cur = (u32)MAKE_FLATPTR(EBDA_SEGMENT_MINIMUM, 0);
+
+    // Find memory at the top of ram.
+    u32 top = 0, bottom = 0;
+    int i;
+    for (i=e820_count-1; i>=0; i--) {
+        struct e820entry *e = &e820_list[i];
+        u64 end = e->start + e->size;
+        if (e->type != E820_RAM || end > 0xffffffff
+            || e->size < CONFIG_MAX_HIGHTABLE)
+            continue;
+        top = end;
+        bottom = e->start;
+        break;
+    }
+    if (top < 1024*1024 + CONFIG_MAX_HIGHTABLE) {
+        // No memory above 1Meg
+        memset(&ZoneHigh, 0, sizeof(ZoneHigh));
+        return;
+    }
+
+    // Memory at top of ram.
+    ZoneHigh.bottom = top - CONFIG_MAX_HIGHTABLE;
+    ZoneHigh.top = ZoneHigh.cur = ZoneHigh.bottom + CONFIG_MAX_HIGHTABLE;
+    add_e820(ZoneHigh.bottom, CONFIG_MAX_HIGHTABLE, E820_RESERVED);
+
+    // Memory above 1Meg
+    ZoneTmpHigh.bottom = bottom;
+    ZoneTmpHigh.top = ZoneTmpHigh.cur = ZoneHigh.bottom;
+}
+
+void
+malloc_finalize()
+{
+    dprintf(3, "malloc finalize\n");
+
+    dumpZones();
+
+    // Give back unused high ram.
+    u32 giveback = (ZoneHigh.cur - ZoneHigh.bottom) / 4096 * 4096;
+    add_e820(ZoneHigh.bottom, giveback, E820_RAM);
+    dprintf(1, "Returned %d bytes of ZoneHigh\n", giveback);
+
+    // Clear low-memory allocations.
+    memset((void*)ZoneTmpLow.bottom, 0, ZoneTmpLow.top - ZoneTmpLow.bottom);
+}
+
+
+/****************************************************************
+ * pmm allocation
+ ****************************************************************/
+
+// Information on PMM tracked allocations
+struct pmmalloc_s {
+    void *data;
+    u32 olddata;
+    u32 handle;
+    struct pmmalloc_s *next;
+};
+
+struct pmmalloc_s *PMMAllocs VAR32VISIBLE;
+
+#define PMMALLOCSIZE ALIGN(sizeof(struct pmmalloc_s), MALLOC_MIN_ALIGN)
+
+// Allocate memory from the given zone and track it as a PMM allocation
+static void *
+pmm_malloc(struct zone_s *zone, u32 handle, u32 size, u32 align)
+{
+    struct pmmalloc_s *info = zone_malloc(&ZoneTmpHigh, sizeof(*info)
+                                          , MALLOC_MIN_ALIGN);
+    if (!info)
+        return NULL;
+    u32 olddata = GET_PMMVAR(zone->cur);
+    void *data = zone_malloc(zone, size, align);
+    if (! data) {
+        zone_free(&ZoneTmpHigh, info, (u32)info + PMMALLOCSIZE);
+        return NULL;
+    }
+    dprintf(8, "pmm_malloc zone=%p handle=%x size=%d align=%x"
+            " ret=%p (info=%p)\n"
+            , zone, handle, size, align
+            , data, info);
+    SET_PMMVAR(info->data, data);
+    SET_PMMVAR(info->olddata, olddata);
+    SET_PMMVAR(info->handle, handle);
+    SET_PMMVAR(info->next, GET_PMMVAR(PMMAllocs));
+    SET_PMMVAR(PMMAllocs, info);
+    return data;
+}
+
+// Free a raw data block (either from a zone or from pmm alloc list).
+static void
+pmm_free_data(struct zone_s *zone, void *data, u32 olddata)
+{
+    if (GET_PMMVAR(zone->cur) == (u32)data) {
+        zone_free(zone, data, olddata);
+        return;
+    }
+    struct pmmalloc_s *info;
+    for (info=GET_PMMVAR(PMMAllocs); info; info = GET_PMMVAR(info->next))
+        if (GET_PMMVAR(info->olddata) == (u32)data) {
+            SET_PMMVAR(info->olddata, olddata);
+            return;
+        }
+}
+
+// Free a data block allocated with pmm_malloc
+static int
+pmm_free(void *data)
+{
+    struct zone_s *zone = zone_find(GET_PMMVAR(data));
+    if (!zone)
+        return -1;
+    struct pmmalloc_s **pinfo = &PMMAllocs;
+    for (;;) {
+        struct pmmalloc_s *info = GET_PMMVAR(*pinfo);
+        if (!info)
+            return -1;
+        if (GET_PMMVAR(info->data) == data) {
+            SET_PMMVAR(*pinfo, GET_PMMVAR(info->next));
+            u32 olddata = GET_PMMVAR(info->olddata);
+            pmm_free_data(zone, data, olddata);
+            pmm_free_data(&ZoneTmpHigh, info, (u32)info + PMMALLOCSIZE);
+            dprintf(8, "pmm_free data=%p zone=%p olddata=%p info=%p\n"
+                    , data, zone, (void*)olddata, info);
+            return 0;
+        }
+        pinfo = &info->next;
+    }
+    return -1;
+}
+
+// Find the amount of free space in a given zone.
+static u32
+pmm_getspace(struct zone_s *zone)
+{
+    u32 space = GET_PMMVAR(zone->cur) - GET_PMMVAR(zone->bottom);
+    if (space <= PMMALLOCSIZE)
+        return 0;
+    return space - PMMALLOCSIZE;
+}
+
+// Find the data block allocated with pmm_malloc with a given handle.
+static void *
+pmm_find(u32 handle)
+{
+    struct pmmalloc_s *info;
+    for (info=GET_PMMVAR(PMMAllocs); info; info = GET_PMMVAR(info->next))
+        if (GET_PMMVAR(info->handle) == handle)
+            return GET_PMMVAR(info->data);
+    return NULL;
+}
+
+
+/****************************************************************
+ * pmm interface
+ ****************************************************************/
 
 struct pmmheader {
     u32 signature;
@@ -29,7 +293,7 @@
 };
 #endif
 
-#define FUNCTION_NOT_SUPPORTED 0xffffffff
+#define PMM_FUNCTION_NOT_SUPPORTED 0xffffffff
 
 // PMM - allocate
 static u32
@@ -37,10 +301,51 @@
 {
     u32 length = *(u32*)&args[1], handle = *(u32*)&args[3];
     u16 flags = args[5];
-    dprintf(1, "pmm00: length=%x handle=%x flags=%x\n"
+    dprintf(3, "pmm00: length=%x handle=%x flags=%x\n"
             , length, handle, flags);
-    // XXX
-    return 0;
+    if (!length) {
+        // Memory size request
+        switch (flags & 3) {
+        default:
+        case 0:
+            return 0;
+        case 1:
+            return pmm_getspace(&ZoneTmpLow);
+        case 2:
+            return pmm_getspace(&ZoneTmpHigh);
+        case 3: {
+            u32 spacelow = pmm_getspace(&ZoneTmpLow);
+            u32 spacehigh = pmm_getspace(&ZoneTmpHigh);
+            if (spacelow > spacehigh)
+                return spacelow;
+            return spacehigh;
+        }
+        }
+    }
+    u32 size = length * 16;
+    if ((s32)size <= 0)
+        return 0;
+    u32 align = MALLOC_MIN_ALIGN;
+    if (flags & 4) {
+        align = 1<<__ffs(size);
+        if (align < MALLOC_MIN_ALIGN)
+            align = MALLOC_MIN_ALIGN;
+    }
+    switch (flags & 3) {
+    default:
+    case 0:
+        return 0;
+    case 1:
+        return (u32)pmm_malloc(&ZoneTmpLow, handle, size, align);
+    case 2:
+        return (u32)pmm_malloc(&ZoneTmpHigh, handle, size, align);
+    case 3: {
+        void *data = pmm_malloc(&ZoneTmpLow, handle, size, align);
+        if (data)
+            return (u32)data;
+        return (u32)pmm_malloc(&ZoneTmpHigh, handle, size, align);
+    }
+    }
 }
 
 // PMM - find
@@ -48,9 +353,10 @@
 handle_pmm01(u16 *args)
 {
     u32 handle = *(u32*)&args[1];
-    dprintf(1, "pmm01: handle=%x\n", handle);
-    // XXX
-    return 0;
+    dprintf(3, "pmm01: handle=%x\n", handle);
+    if (handle == 0xFFFFFFFF)
+        return 0;
+    return (u32)pmm_find(handle);
 }
 
 // PMM - deallocate
@@ -58,22 +364,25 @@
 handle_pmm02(u16 *args)
 {
     u32 buffer = *(u32*)&args[1];
-    dprintf(1, "pmm02: buffer=%x\n", buffer);
-    // XXX
+    dprintf(3, "pmm02: buffer=%x\n", buffer);
+    int ret = pmm_free((void*)buffer);
+    if (ret)
+        // Error
+        return 1;
     return 0;
 }
 
 static u32
 handle_pmmXX(u16 *args)
 {
-    return FUNCTION_NOT_SUPPORTED;
+    return PMM_FUNCTION_NOT_SUPPORTED;
 }
 
 u32 VISIBLE16
 handle_pmm(u16 *args)
 {
     if (! CONFIG_PMM)
-        return FUNCTION_NOT_SUPPORTED;
+        return PMM_FUNCTION_NOT_SUPPORTED;
 
     u16 arg1 = args[0];
     dprintf(DEBUG_HDL_pmm, "pmm call arg1=%x\n", arg1);
@@ -112,6 +421,4 @@
 
     PMMHEADER.signature = 0;
     PMMHEADER.entry_offset = 0;
-
-    // XXX - zero low-memory allocations.
 }
diff --git a/src/post.c b/src/post.c
index e80146f..32d22c8 100644
--- a/src/post.c
+++ b/src/post.c
@@ -190,6 +190,9 @@
 
     optionrom_setup();
 
+    // Run BCVs
+    boot_prep();
+
     pmm_finalize();
     malloc_finalize();
     memmap_finalize();
@@ -210,13 +213,10 @@
     // Perform main setup code.
     post();
 
-    // Run BCVs
-    boot_prep();
-
     // Setup bios checksum.
     BiosChecksum -= checksum((u8*)BUILD_BIOS_ADDR, BUILD_BIOS_SIZE);
 
-    // Prep for boot process.
+    // Write protect bios memory.
     make_bios_readonly();
 
     // Invoke int 19 to start boot process.
diff --git a/src/smbios.c b/src/smbios.c
index fb03671..6fbddd9 100644
--- a/src/smbios.c
+++ b/src/smbios.c
@@ -6,7 +6,6 @@
 // This file may be distributed under the terms of the GNU LGPLv3 license.
 
 #include "util.h" // dprintf
-#include "memmap.h" // malloc_fseg
 #include "biosvar.h" // GET_EBDA
 
 
diff --git a/src/types.h b/src/types.h
index 9f93e0b..bbb38bb 100644
--- a/src/types.h
+++ b/src/types.h
@@ -1,6 +1,6 @@
 // Basic type definitions for X86 cpus.
 //
-// Copyright (C) 2008  Kevin O'Connor <kevin@koconnor.net>
+// Copyright (C) 2008,2009  Kevin O'Connor <kevin@koconnor.net>
 //
 // This file may be distributed under the terms of the GNU LGPLv3 license.
 #ifndef __TYPES_H
@@ -48,6 +48,8 @@
 # define VAR16EXPORT __section(".data16.export." UNIQSEC) __VISIBLE
 // Designate a variable at a specific 16bit address
 # define VAR16FIXED(addr) __aligned(1) __VISIBLE __section(".fixedaddr." __stringify(addr))
+// Designate a 32bit variable also available in 16bit "big real" mode.
+# define VAR32VISIBLE __section(".discard.var32." UNIQSEC) __VISIBLE __weak
 // Designate top-level assembler as 16bit only.
 # define ASM16(code) __ASM(code)
 // Designate top-level assembler as 32bit only.
@@ -59,6 +61,7 @@
 # define VAR16_32 VAR16 __VISIBLE __weak
 # define VAR16EXPORT VAR16_32
 # define VAR16FIXED(addr) VAR16_32
+# define VAR32VISIBLE __VISIBLE
 # define ASM16(code)
 # define ASM32(code) __ASM(code)
 #endif
@@ -69,6 +72,9 @@
 #define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d))
 #define ALIGN(x,a)              __ALIGN_MASK(x,(typeof(x))(a)-1)
 #define __ALIGN_MASK(x,mask)    (((x)+(mask))&~(mask))
+#define container_of(ptr, type, member) ({                      \
+        const typeof( ((type *)0)->member ) *__mptr = (ptr);    \
+        (type *)( (char *)__mptr - offsetof(type,member) );})
 
 #define NULL ((void *)0)
 
diff --git a/src/util.h b/src/util.h
index d369a49..c2fc6ca 100644
--- a/src/util.h
+++ b/src/util.h
@@ -80,6 +80,14 @@
     return val;
 }
 
+static inline u32 __ffs(u32 word)
+{
+    asm("bsf %1,%0"
+        : "=r" (word)
+        : "rm" (word));
+    return word;
+}
+
 #define call16_simpint(nr, peax, pflags) do {                           \
         ASSERT16();                                                     \
         asm volatile(                                                   \
@@ -232,8 +240,14 @@
 void pnp_setup();
 
 // pmm.c
+void *malloc_high(u32 size);
+void *malloc_fseg(u32 size);
+void malloc_setup();
+void malloc_finalize();
 void pmm_setup();
 void pmm_finalize();
+// Minimum alignment of malloc'd memory
+#define MALLOC_MIN_ALIGN 16
 
 // mtrr.c
 void mtrr_setup(void);