Add support for 32bit PCI BIOS entry.
[seabios.git] / src / pmm.c
index 1732c69b28563236dda87e39a9ff3b031146f159..dab8fb36b13c48cf09813720e2680bd5af48b077 100644 (file)
--- a/src/pmm.c
+++ b/src/pmm.c
@@ -8,18 +8,19 @@
 #include "config.h" // BUILD_BIOS_ADDR
 #include "memmap.h" // find_high_area
 #include "farptr.h" // GET_FARVAR
-#include "biosvar.h" // EBDA_SEGMENT_MINIMUM
+#include "biosvar.h" // GET_BDA
 
 
-/****************************************************************
- * malloc
- ****************************************************************/
-
-#if MODE16
+#if MODESEGMENT
 // 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))
+#define GET_PMMVAR(var) ({                      \
+            SET_SEG(ES, 0);                     \
+            __GET_VAR("addr32 ", ES, (var)); })
+#define SET_PMMVAR(var, val) do {               \
+        SET_SEG(ES, 0);                         \
+        __SET_VAR("addr32 ", ES, (var), (val)); \
+    } while (0)
 #else
 #define GET_PMMVAR(var) (var)
 #define SET_PMMVAR(var, val) do { (var) = (val); } while (0)
@@ -30,16 +31,87 @@ struct zone_s {
     u32 top, bottom, cur;
 };
 
-struct zone_s ZoneLow VAR32VISIBLE, ZoneHigh VAR32VISIBLE;
-struct zone_s ZoneFSeg VAR32VISIBLE;
-struct zone_s ZoneTmpLow VAR32VISIBLE, ZoneTmpHigh VAR32VISIBLE;
+struct zone_s ZoneLow VAR32FLATVISIBLE, ZoneHigh VAR32FLATVISIBLE;
+struct zone_s ZoneFSeg VAR32FLATVISIBLE;
+struct zone_s ZoneTmpLow VAR32FLATVISIBLE, ZoneTmpHigh VAR32FLATVISIBLE;
 
-struct zone_s *Zones[] VAR32VISIBLE = {
+struct zone_s *Zones[] VAR32FLATVISIBLE = {
     &ZoneTmpLow, &ZoneLow, &ZoneFSeg, &ZoneTmpHigh, &ZoneHigh
 };
 
+
+/****************************************************************
+ * ebda movement
+ ****************************************************************/
+
+// Move ebda
+static int
+relocate_ebda(u32 newebda, u32 oldebda, u8 ebda_size)
+{
+    u32 lowram = GET_BDA(mem_size_kb) * 1024;
+    if (oldebda != lowram)
+        // EBDA isn't at end of ram - give up.
+        return -1;
+
+    // Do copy
+    if (MODESEGMENT)
+        memcpy_far(FLATPTR_TO_SEG(newebda)
+                   , (void*)FLATPTR_TO_OFFSET(newebda)
+                   , FLATPTR_TO_SEG(oldebda)
+                   , (void*)FLATPTR_TO_OFFSET(oldebda)
+                   , ebda_size * 1024);
+    else
+        memmove((void*)newebda, (void*)oldebda, ebda_size * 1024);
+
+    // Update indexes
+    dprintf(1, "ebda moved from %x to %x\n", oldebda, newebda);
+    SET_BDA(mem_size_kb, newebda / 1024);
+    SET_BDA(ebda_seg, FLATPTR_TO_SEG(newebda));
+    return 0;
+}
+
+// Support expanding the ZoneLow dynamically.
+static void
+zonelow_expand(u32 size, u32 align)
+{
+    u32 oldpos = GET_PMMVAR(ZoneLow.cur);
+    u32 newpos = ALIGN_DOWN(oldpos - size, align);
+    u32 bottom = GET_PMMVAR(ZoneLow.bottom);
+    if (newpos >= bottom && newpos <= oldpos)
+        // Space already present.
+        return;
+    u16 ebda_seg = get_ebda_seg();
+    u32 ebda_pos = (u32)MAKE_FLATPTR(ebda_seg, 0);
+    u8 ebda_size = GET_EBDA2(ebda_seg, size);
+    u32 ebda_end = ebda_pos + ebda_size * 1024;
+    if (ebda_end != bottom) {
+        // Something else is after ebda - can't use any existing space.
+        oldpos = ebda_end;
+        newpos = ALIGN_DOWN(oldpos - size, align);
+    }
+    u32 newbottom = ALIGN_DOWN(newpos, 1024);
+    u32 newebda = ALIGN_DOWN(newbottom - ebda_size * 1024, 1024);
+    if (newebda < BUILD_EBDA_MINIMUM)
+        // Not enough space.
+        return;
+
+    // Move ebda
+    int ret = relocate_ebda(newebda, ebda_pos, ebda_size);
+    if (ret)
+        return;
+
+    // Update zone
+    SET_PMMVAR(ZoneLow.cur, oldpos);
+    SET_PMMVAR(ZoneLow.bottom, newbottom);
+}
+
+
+/****************************************************************
+ * zone allocations
+ ****************************************************************/
+
 // Obtain memory from a given zone.
-void *
+static void *
 zone_malloc(struct zone_s *zone, u32 size, u32 align)
 {
     u32 oldpos = GET_PMMVAR(zone->cur);
@@ -51,15 +123,6 @@ zone_malloc(struct zone_s *zone, u32 size, u32 align)
     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)
@@ -74,6 +137,17 @@ zone_find(void *data)
     return NULL;
 }
 
+// Return memory to a zone (if it was the last to be allocated).
+static int
+zone_free(void *data, u32 olddata)
+{
+    struct zone_s *zone = zone_find(data);
+    if (!zone || !data || GET_PMMVAR(zone->cur) != (u32)data)
+        return -1;
+    SET_PMMVAR(zone->cur, olddata);
+    return 0;
+}
+
 // Report the status of all the zones.
 static void
 dumpZones()
@@ -89,63 +163,9 @@ dumpZones()
     }
 }
 
-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);
-
-    // Permanent memory under 1Meg.  XXX - not implemented yet.
-    ZoneLow.bottom = ZoneLow.top = ZoneLow.cur = 0xa0000;
-
-    // Find memory at the top of ram.
-    struct e820entry *e = find_high_area(CONFIG_MAX_HIGHTABLE+MALLOC_MIN_ALIGN);
-    if (!e) {
-        // No memory above 1Meg
-        memset(&ZoneHigh, 0, sizeof(ZoneHigh));
-        memset(&ZoneTmpHigh, 0, sizeof(ZoneTmpHigh));
-        return;
-    }
-    u32 top = e->start + e->size, bottom = e->start;
-
-    // Memory at top of ram.
-    ZoneHigh.bottom = ALIGN(top - CONFIG_MAX_HIGHTABLE, MALLOC_MIN_ALIGN);
-    ZoneHigh.top = ZoneHigh.cur = ZoneHigh.bottom + CONFIG_MAX_HIGHTABLE;
-    add_e820(ZoneHigh.bottom, CONFIG_MAX_HIGHTABLE, E820_RESERVED);
-
-    // Memory above 1Meg
-    ZoneTmpHigh.bottom = ALIGN(bottom, MALLOC_MIN_ALIGN);
-    ZoneTmpHigh.top = ZoneTmpHigh.cur = ZoneHigh.bottom;
-}
-
-void
-malloc_finalize()
-{
-    dprintf(3, "malloc finalize\n");
-
-    dumpZones();
-
-    // Give back unused high ram.
-    u32 giveback = ALIGN_DOWN(ZoneHigh.cur - ZoneHigh.bottom, PAGE_SIZE);
-    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
+ * tracked memory allocations
  ****************************************************************/
 
 // Information on PMM tracked allocations
@@ -157,24 +177,27 @@ struct pmmalloc_s {
     struct pmmalloc_s *next;
 };
 
-struct pmmalloc_s *PMMAllocs VAR32VISIBLE;
-
-// Memory zone that pmm allocation tracking info is stored in
-#define ZONEALLOC (&ZoneTmpHigh)
+struct pmmalloc_s *PMMAllocs VAR32FLATVISIBLE;
 
 // Allocate memory from the given zone and track it as a PMM allocation
-static void *
+void *
 pmm_malloc(struct zone_s *zone, u32 handle, u32 size, u32 align)
 {
-    u32 oldallocdata = GET_PMMVAR(ZONEALLOC->cur);
-    struct pmmalloc_s *info = zone_malloc(ZONEALLOC, sizeof(*info)
+    u32 oldallocdata = GET_PMMVAR(ZoneTmpHigh.cur);
+    struct pmmalloc_s *info = zone_malloc(&ZoneTmpHigh, sizeof(*info)
                                           , MALLOC_MIN_ALIGN);
-    if (!info)
-        return NULL;
+    if (!info) {
+        oldallocdata = GET_PMMVAR(ZoneTmpLow.cur);
+        info = zone_malloc(&ZoneTmpLow, sizeof(*info), MALLOC_MIN_ALIGN);
+        if (!info)
+            return NULL;
+    }
+    if (zone == &ZoneLow)
+        zonelow_expand(size, align);
     u32 olddata = GET_PMMVAR(zone->cur);
     void *data = zone_malloc(zone, size, align);
     if (! data) {
-        zone_free(ZONEALLOC, info, oldallocdata);
+        zone_free(info, oldallocdata);
         return NULL;
     }
     dprintf(8, "pmm_malloc zone=%p handle=%x size=%d align=%x"
@@ -192,12 +215,12 @@ pmm_malloc(struct zone_s *zone, u32 handle, u32 size, u32 align)
 
 // 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)
+pmm_free_data(void *data, u32 olddata)
 {
-    if (GET_PMMVAR(zone->cur) == (u32)data) {
-        zone_free(zone, data, olddata);
+    int ret = zone_free(data, olddata);
+    if (!ret)
+        // Success - done.
         return;
-    }
     struct pmmalloc_s *info;
     for (info=GET_PMMVAR(PMMAllocs); info; info = GET_PMMVAR(info->next))
         if (GET_PMMVAR(info->olddata) == (u32)data) {
@@ -210,12 +233,9 @@ pmm_free_data(struct zone_s *zone, void *data, u32 olddata)
 }
 
 // Free a data block allocated with pmm_malloc
-static int
+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);
@@ -225,12 +245,10 @@ pmm_free(void *data)
             SET_PMMVAR(*pinfo, GET_PMMVAR(info->next));
             u32 oldallocdata = GET_PMMVAR(info->oldallocdata);
             u32 olddata = GET_PMMVAR(info->olddata);
-            pmm_free_data(zone, data, olddata);
-            pmm_free_data(ZONEALLOC, info, oldallocdata);
-            dprintf(8, "pmm_free data=%p zone=%p olddata=%p oldallocdata=%p"
-                    " info=%p\n"
-                    , data, zone, (void*)olddata, (void*)oldallocdata
-                    , info);
+            pmm_free_data(data, olddata);
+            pmm_free_data(info, oldallocdata);
+            dprintf(8, "pmm_free data=%p olddata=%p oldallocdata=%p info=%p\n"
+                    , data, (void*)olddata, (void*)oldallocdata, info);
             return 0;
         }
         pinfo = &info->next;
@@ -241,9 +259,11 @@ pmm_free(void *data)
 static u32
 pmm_getspace(struct zone_s *zone)
 {
+    // XXX - doesn't account for ZoneLow being able to grow.
     u32 space = GET_PMMVAR(zone->cur) - GET_PMMVAR(zone->bottom);
-    if (zone != ZONEALLOC)
+    if (zone != &ZoneTmpHigh && zone != &ZoneTmpLow)
         return space;
+    // Account for space needed for PMM tracking.
     u32 reserve = ALIGN(sizeof(struct pmmalloc_s), MALLOC_MIN_ALIGN);
     if (space <= reserve)
         return 0;
@@ -261,6 +281,71 @@ pmm_find(u32 handle)
     return NULL;
 }
 
+void
+malloc_setup()
+{
+    ASSERT32FLAT();
+    dprintf(3, "malloc setup\n");
+
+    PMMAllocs = NULL;
+
+    // Memory in 0xf0000 area.
+    extern u8 code32flat_start[];
+    if ((u32)code32flat_start > BUILD_BIOS_ADDR)
+        // Clear unused parts of f-segment
+        memset((void*)BUILD_BIOS_ADDR, 0
+               , (u32)code32flat_start - BUILD_BIOS_ADDR);
+    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 = BUILD_EBDA_MINIMUM;
+
+    // Permanent memory under 1Meg.
+    ZoneLow.bottom = ZoneLow.top = ZoneLow.cur = BUILD_LOWRAM_END;
+
+    // Find memory at the top of ram.
+    struct e820entry *e = find_high_area(CONFIG_MAX_HIGHTABLE+MALLOC_MIN_ALIGN);
+    if (!e) {
+        // No memory above 1Meg
+        memset(&ZoneHigh, 0, sizeof(ZoneHigh));
+        memset(&ZoneTmpHigh, 0, sizeof(ZoneTmpHigh));
+        return;
+    }
+    u32 top = e->start + e->size, bottom = e->start;
+
+    // Memory at top of ram.
+    ZoneHigh.bottom = ALIGN(top - CONFIG_MAX_HIGHTABLE, MALLOC_MIN_ALIGN);
+    ZoneHigh.top = ZoneHigh.cur = ZoneHigh.bottom + CONFIG_MAX_HIGHTABLE;
+    add_e820(ZoneHigh.bottom, CONFIG_MAX_HIGHTABLE, E820_RESERVED);
+
+    // Memory above 1Meg
+    ZoneTmpHigh.bottom = ALIGN(bottom, MALLOC_MIN_ALIGN);
+    ZoneTmpHigh.top = ZoneTmpHigh.cur = ZoneHigh.bottom;
+}
+
+void
+malloc_finalize()
+{
+    dprintf(3, "malloc finalize\n");
+
+    dumpZones();
+
+    // Reserve more low-mem if needed.
+    u32 endlow = GET_BDA(mem_size_kb)*1024;
+    add_e820(endlow, BUILD_LOWRAM_END-endlow, E820_RESERVED);
+
+    // Give back unused high ram.
+    u32 giveback = ALIGN_DOWN(ZoneHigh.cur - ZoneHigh.bottom, PAGE_SIZE);
+    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 interface
@@ -355,7 +440,7 @@ handle_pmm01(u16 *args)
 {
     u32 handle = *(u32*)&args[1];
     dprintf(3, "pmm01: handle=%x\n", handle);
-    if (handle == 0xFFFFFFFF)
+    if (handle == PMM_DEFAULT_HANDLE)
         return 0;
     return (u32)pmm_find(handle);
 }
@@ -407,8 +492,6 @@ pmm_setup()
 
     dprintf(3, "init PMM\n");
 
-    PMMAllocs = NULL;
-
     PMMHEADER.signature = PMM_SIGNATURE;
     PMMHEADER.entry_offset = (u32)entry_pmm - BUILD_BIOS_ADDR;
     PMMHEADER.checksum -= checksum(&PMMHEADER, sizeof(PMMHEADER));