|
|
1.1 ! root 1: // Post memory manager (PMM) calls ! 2: // ! 3: // Copyright (C) 2009 Kevin O'Connor <[email protected]> ! 4: // ! 5: // This file may be distributed under the terms of the GNU LGPLv3 license. ! 6: ! 7: #include "util.h" // checksum ! 8: #include "config.h" // BUILD_BIOS_ADDR ! 9: #include "memmap.h" // find_high_area ! 10: #include "farptr.h" // GET_FARVAR ! 11: #include "biosvar.h" // GET_BDA ! 12: ! 13: ! 14: #if MODE16 ! 15: // The 16bit pmm entry points runs in "big real" mode, and can ! 16: // therefore read/write to the 32bit malloc variables. ! 17: #define GET_PMMVAR(var) GET_FARVAR(0, (var)) ! 18: #define SET_PMMVAR(var, val) SET_FARVAR(0, (var), (val)) ! 19: #else ! 20: #define GET_PMMVAR(var) (var) ! 21: #define SET_PMMVAR(var, val) do { (var) = (val); } while (0) ! 22: #endif ! 23: ! 24: // Zone definitions ! 25: struct zone_s { ! 26: u32 top, bottom, cur; ! 27: }; ! 28: ! 29: struct zone_s ZoneLow VAR32VISIBLE, ZoneHigh VAR32VISIBLE; ! 30: struct zone_s ZoneFSeg VAR32VISIBLE; ! 31: struct zone_s ZoneTmpLow VAR32VISIBLE, ZoneTmpHigh VAR32VISIBLE; ! 32: ! 33: struct zone_s *Zones[] VAR32VISIBLE = { ! 34: &ZoneTmpLow, &ZoneLow, &ZoneFSeg, &ZoneTmpHigh, &ZoneHigh ! 35: }; ! 36: ! 37: ! 38: /**************************************************************** ! 39: * ebda movement ! 40: ****************************************************************/ ! 41: ! 42: // Move ebda ! 43: static int ! 44: relocate_ebda(u32 newebda, u32 oldebda, u8 ebda_size) ! 45: { ! 46: u32 lowram = GET_BDA(mem_size_kb) * 1024; ! 47: if (oldebda != lowram) ! 48: // EBDA isn't at end of ram - give up. ! 49: return -1; ! 50: ! 51: // Do copy ! 52: if (MODE16) ! 53: memcpy_far(FLATPTR_TO_SEG(newebda) ! 54: , (void*)FLATPTR_TO_OFFSET(newebda) ! 55: , FLATPTR_TO_SEG(oldebda) ! 56: , (void*)FLATPTR_TO_OFFSET(oldebda) ! 57: , ebda_size * 1024); ! 58: else ! 59: memmove((void*)newebda, (void*)oldebda, ebda_size * 1024); ! 60: ! 61: // Update indexes ! 62: dprintf(1, "ebda moved from %x to %x\n", oldebda, newebda); ! 63: SET_BDA(mem_size_kb, newebda / 1024); ! 64: SET_BDA(ebda_seg, FLATPTR_TO_SEG(newebda)); ! 65: return 0; ! 66: } ! 67: ! 68: // Support expanding the ZoneLow dynamically. ! 69: static void ! 70: zonelow_expand(u32 size, u32 align) ! 71: { ! 72: u32 oldpos = GET_PMMVAR(ZoneLow.cur); ! 73: u32 newpos = ALIGN_DOWN(oldpos - size, align); ! 74: u32 bottom = GET_PMMVAR(ZoneLow.bottom); ! 75: if (newpos >= bottom && newpos <= oldpos) ! 76: // Space already present. ! 77: return; ! 78: u16 ebda_seg = get_ebda_seg(); ! 79: u32 ebda_pos = (u32)MAKE_FLATPTR(ebda_seg, 0); ! 80: u8 ebda_size = GET_EBDA2(ebda_seg, size); ! 81: u32 ebda_end = ebda_pos + ebda_size * 1024; ! 82: if (ebda_end != bottom) { ! 83: // Something else is after ebda - can't use any existing space. ! 84: oldpos = ebda_end; ! 85: newpos = ALIGN_DOWN(oldpos - size, align); ! 86: } ! 87: u32 newbottom = ALIGN_DOWN(newpos, 1024); ! 88: u32 newebda = ALIGN_DOWN(newbottom - ebda_size * 1024, 1024); ! 89: if (newebda < BUILD_EBDA_MINIMUM) ! 90: // Not enough space. ! 91: return; ! 92: ! 93: // Move ebda ! 94: int ret = relocate_ebda(newebda, ebda_pos, ebda_size); ! 95: if (ret) ! 96: return; ! 97: ! 98: // Update zone ! 99: SET_PMMVAR(ZoneLow.cur, oldpos); ! 100: SET_PMMVAR(ZoneLow.bottom, newbottom); ! 101: } ! 102: ! 103: ! 104: /**************************************************************** ! 105: * zone allocations ! 106: ****************************************************************/ ! 107: ! 108: // Obtain memory from a given zone. ! 109: static void * ! 110: zone_malloc(struct zone_s *zone, u32 size, u32 align) ! 111: { ! 112: u32 oldpos = GET_PMMVAR(zone->cur); ! 113: u32 newpos = ALIGN_DOWN(oldpos - size, align); ! 114: if (newpos < GET_PMMVAR(zone->bottom) || newpos > oldpos) ! 115: // No space ! 116: return NULL; ! 117: SET_PMMVAR(zone->cur, newpos); ! 118: return (void*)newpos; ! 119: } ! 120: ! 121: // Find the zone that contains the given data block. ! 122: static struct zone_s * ! 123: zone_find(void *data) ! 124: { ! 125: int i; ! 126: for (i=0; i<ARRAY_SIZE(Zones); i++) { ! 127: struct zone_s *zone = GET_PMMVAR(Zones[i]); ! 128: if ((u32)data >= GET_PMMVAR(zone->cur) ! 129: && (u32)data < GET_PMMVAR(zone->top)) ! 130: return zone; ! 131: } ! 132: return NULL; ! 133: } ! 134: ! 135: // Return memory to a zone (if it was the last to be allocated). ! 136: static int ! 137: zone_free(void *data, u32 olddata) ! 138: { ! 139: struct zone_s *zone = zone_find(data); ! 140: if (!zone || !data || GET_PMMVAR(zone->cur) != (u32)data) ! 141: return -1; ! 142: SET_PMMVAR(zone->cur, olddata); ! 143: return 0; ! 144: } ! 145: ! 146: // Report the status of all the zones. ! 147: static void ! 148: dumpZones() ! 149: { ! 150: int i; ! 151: for (i=0; i<ARRAY_SIZE(Zones); i++) { ! 152: struct zone_s *zone = Zones[i]; ! 153: u32 used = zone->top - zone->cur; ! 154: u32 avail = zone->top - zone->bottom; ! 155: u32 pct = avail ? ((100 * used) / avail) : 0; ! 156: dprintf(2, "zone %d: %08x-%08x used=%d (%d%%)\n" ! 157: , i, zone->bottom, zone->top, used, pct); ! 158: } ! 159: } ! 160: ! 161: ! 162: /**************************************************************** ! 163: * tracked memory allocations ! 164: ****************************************************************/ ! 165: ! 166: // Information on PMM tracked allocations ! 167: struct pmmalloc_s { ! 168: void *data; ! 169: u32 olddata; ! 170: u32 handle; ! 171: u32 oldallocdata; ! 172: struct pmmalloc_s *next; ! 173: }; ! 174: ! 175: struct pmmalloc_s *PMMAllocs VAR32VISIBLE; ! 176: ! 177: // Allocate memory from the given zone and track it as a PMM allocation ! 178: void * ! 179: pmm_malloc(struct zone_s *zone, u32 handle, u32 size, u32 align) ! 180: { ! 181: u32 oldallocdata = GET_PMMVAR(ZoneTmpHigh.cur); ! 182: struct pmmalloc_s *info = zone_malloc(&ZoneTmpHigh, sizeof(*info) ! 183: , MALLOC_MIN_ALIGN); ! 184: if (!info) { ! 185: oldallocdata = GET_PMMVAR(ZoneTmpLow.cur); ! 186: info = zone_malloc(&ZoneTmpLow, sizeof(*info), MALLOC_MIN_ALIGN); ! 187: if (!info) ! 188: return NULL; ! 189: } ! 190: if (zone == &ZoneLow) ! 191: zonelow_expand(size, align); ! 192: u32 olddata = GET_PMMVAR(zone->cur); ! 193: void *data = zone_malloc(zone, size, align); ! 194: if (! data) { ! 195: zone_free(info, oldallocdata); ! 196: return NULL; ! 197: } ! 198: dprintf(8, "pmm_malloc zone=%p handle=%x size=%d align=%x" ! 199: " ret=%p (info=%p)\n" ! 200: , zone, handle, size, align ! 201: , data, info); ! 202: SET_PMMVAR(info->data, data); ! 203: SET_PMMVAR(info->olddata, olddata); ! 204: SET_PMMVAR(info->handle, handle); ! 205: SET_PMMVAR(info->oldallocdata, oldallocdata); ! 206: SET_PMMVAR(info->next, GET_PMMVAR(PMMAllocs)); ! 207: SET_PMMVAR(PMMAllocs, info); ! 208: return data; ! 209: } ! 210: ! 211: // Free a raw data block (either from a zone or from pmm alloc list). ! 212: static void ! 213: pmm_free_data(void *data, u32 olddata) ! 214: { ! 215: int ret = zone_free(data, olddata); ! 216: if (!ret) ! 217: // Success - done. ! 218: return; ! 219: struct pmmalloc_s *info; ! 220: for (info=GET_PMMVAR(PMMAllocs); info; info = GET_PMMVAR(info->next)) ! 221: if (GET_PMMVAR(info->olddata) == (u32)data) { ! 222: SET_PMMVAR(info->olddata, olddata); ! 223: return; ! 224: } else if (GET_PMMVAR(info->oldallocdata) == (u32)data) { ! 225: SET_PMMVAR(info->oldallocdata, olddata); ! 226: return; ! 227: } ! 228: } ! 229: ! 230: // Free a data block allocated with pmm_malloc ! 231: int ! 232: pmm_free(void *data) ! 233: { ! 234: struct pmmalloc_s **pinfo = &PMMAllocs; ! 235: for (;;) { ! 236: struct pmmalloc_s *info = GET_PMMVAR(*pinfo); ! 237: if (!info) ! 238: return -1; ! 239: if (GET_PMMVAR(info->data) == data) { ! 240: SET_PMMVAR(*pinfo, GET_PMMVAR(info->next)); ! 241: u32 oldallocdata = GET_PMMVAR(info->oldallocdata); ! 242: u32 olddata = GET_PMMVAR(info->olddata); ! 243: pmm_free_data(data, olddata); ! 244: pmm_free_data(info, oldallocdata); ! 245: dprintf(8, "pmm_free data=%p olddata=%p oldallocdata=%p info=%p\n" ! 246: , data, (void*)olddata, (void*)oldallocdata, info); ! 247: return 0; ! 248: } ! 249: pinfo = &info->next; ! 250: } ! 251: } ! 252: ! 253: // Find the amount of free space in a given zone. ! 254: static u32 ! 255: pmm_getspace(struct zone_s *zone) ! 256: { ! 257: // XXX - doesn't account for ZoneLow being able to grow. ! 258: u32 space = GET_PMMVAR(zone->cur) - GET_PMMVAR(zone->bottom); ! 259: if (zone != &ZoneTmpHigh && zone != &ZoneTmpLow) ! 260: return space; ! 261: // Account for space needed for PMM tracking. ! 262: u32 reserve = ALIGN(sizeof(struct pmmalloc_s), MALLOC_MIN_ALIGN); ! 263: if (space <= reserve) ! 264: return 0; ! 265: return space - reserve; ! 266: } ! 267: ! 268: // Find the data block allocated with pmm_malloc with a given handle. ! 269: static void * ! 270: pmm_find(u32 handle) ! 271: { ! 272: struct pmmalloc_s *info; ! 273: for (info=GET_PMMVAR(PMMAllocs); info; info = GET_PMMVAR(info->next)) ! 274: if (GET_PMMVAR(info->handle) == handle) ! 275: return GET_PMMVAR(info->data); ! 276: return NULL; ! 277: } ! 278: ! 279: void ! 280: malloc_setup() ! 281: { ! 282: ASSERT32(); ! 283: dprintf(3, "malloc setup\n"); ! 284: ! 285: PMMAllocs = NULL; ! 286: ! 287: // Memory in 0xf0000 area. ! 288: extern u8 code32_start[]; ! 289: if ((u32)code32_start > BUILD_BIOS_ADDR) ! 290: // Clear unused parts of f-segment ! 291: memset((void*)BUILD_BIOS_ADDR, 0, (u32)code32_start - BUILD_BIOS_ADDR); ! 292: memset(BiosTableSpace, 0, CONFIG_MAX_BIOSTABLE); ! 293: ZoneFSeg.bottom = (u32)BiosTableSpace; ! 294: ZoneFSeg.top = ZoneFSeg.cur = ZoneFSeg.bottom + CONFIG_MAX_BIOSTABLE; ! 295: ! 296: // Memory under 1Meg. ! 297: ZoneTmpLow.bottom = BUILD_STACK_ADDR; ! 298: ZoneTmpLow.top = ZoneTmpLow.cur = BUILD_EBDA_MINIMUM; ! 299: ! 300: // Permanent memory under 1Meg. ! 301: ZoneLow.bottom = ZoneLow.top = ZoneLow.cur = BUILD_LOWRAM_END; ! 302: ! 303: // Find memory at the top of ram. ! 304: struct e820entry *e = find_high_area(CONFIG_MAX_HIGHTABLE+MALLOC_MIN_ALIGN); ! 305: if (!e) { ! 306: // No memory above 1Meg ! 307: memset(&ZoneHigh, 0, sizeof(ZoneHigh)); ! 308: memset(&ZoneTmpHigh, 0, sizeof(ZoneTmpHigh)); ! 309: return; ! 310: } ! 311: u32 top = e->start + e->size, bottom = e->start; ! 312: ! 313: // Memory at top of ram. ! 314: ZoneHigh.bottom = ALIGN(top - CONFIG_MAX_HIGHTABLE, MALLOC_MIN_ALIGN); ! 315: ZoneHigh.top = ZoneHigh.cur = ZoneHigh.bottom + CONFIG_MAX_HIGHTABLE; ! 316: add_e820(ZoneHigh.bottom, CONFIG_MAX_HIGHTABLE, E820_RESERVED); ! 317: ! 318: // Memory above 1Meg ! 319: ZoneTmpHigh.bottom = ALIGN(bottom, MALLOC_MIN_ALIGN); ! 320: ZoneTmpHigh.top = ZoneTmpHigh.cur = ZoneHigh.bottom; ! 321: } ! 322: ! 323: void ! 324: malloc_finalize() ! 325: { ! 326: dprintf(3, "malloc finalize\n"); ! 327: ! 328: dumpZones(); ! 329: ! 330: // Reserve more low-mem if needed. ! 331: u32 endlow = GET_BDA(mem_size_kb)*1024; ! 332: add_e820(endlow, BUILD_LOWRAM_END-endlow, E820_RESERVED); ! 333: ! 334: // Give back unused high ram. ! 335: u32 giveback = ALIGN_DOWN(ZoneHigh.cur - ZoneHigh.bottom, PAGE_SIZE); ! 336: add_e820(ZoneHigh.bottom, giveback, E820_RAM); ! 337: dprintf(1, "Returned %d bytes of ZoneHigh\n", giveback); ! 338: ! 339: // Clear low-memory allocations. ! 340: memset((void*)ZoneTmpLow.bottom, 0, ZoneTmpLow.top - ZoneTmpLow.bottom); ! 341: } ! 342: ! 343: ! 344: /**************************************************************** ! 345: * pmm interface ! 346: ****************************************************************/ ! 347: ! 348: struct pmmheader { ! 349: u32 signature; ! 350: u8 version; ! 351: u8 length; ! 352: u8 checksum; ! 353: u16 entry_offset; ! 354: u16 entry_seg; ! 355: u8 reserved[5]; ! 356: } PACKED; ! 357: ! 358: extern struct pmmheader PMMHEADER; ! 359: ! 360: #define PMM_SIGNATURE 0x4d4d5024 // $PMM ! 361: ! 362: #if CONFIG_PMM ! 363: struct pmmheader PMMHEADER __aligned(16) VAR16EXPORT = { ! 364: .version = 0x01, ! 365: .length = sizeof(PMMHEADER), ! 366: .entry_seg = SEG_BIOS, ! 367: }; ! 368: #endif ! 369: ! 370: #define PMM_FUNCTION_NOT_SUPPORTED 0xffffffff ! 371: ! 372: // PMM - allocate ! 373: static u32 ! 374: handle_pmm00(u16 *args) ! 375: { ! 376: u32 length = *(u32*)&args[1], handle = *(u32*)&args[3]; ! 377: u16 flags = args[5]; ! 378: dprintf(3, "pmm00: length=%x handle=%x flags=%x\n" ! 379: , length, handle, flags); ! 380: struct zone_s *lowzone = &ZoneTmpLow, *highzone = &ZoneTmpHigh; ! 381: if (flags & 8) { ! 382: // Permanent memory request. ! 383: lowzone = &ZoneLow; ! 384: highzone = &ZoneHigh; ! 385: } ! 386: if (!length) { ! 387: // Memory size request ! 388: switch (flags & 3) { ! 389: default: ! 390: case 0: ! 391: return 0; ! 392: case 1: ! 393: return pmm_getspace(lowzone); ! 394: case 2: ! 395: return pmm_getspace(highzone); ! 396: case 3: { ! 397: u32 spacelow = pmm_getspace(lowzone); ! 398: u32 spacehigh = pmm_getspace(highzone); ! 399: if (spacelow > spacehigh) ! 400: return spacelow; ! 401: return spacehigh; ! 402: } ! 403: } ! 404: } ! 405: u32 size = length * 16; ! 406: if ((s32)size <= 0) ! 407: return 0; ! 408: u32 align = MALLOC_MIN_ALIGN; ! 409: if (flags & 4) { ! 410: align = 1<<__ffs(size); ! 411: if (align < MALLOC_MIN_ALIGN) ! 412: align = MALLOC_MIN_ALIGN; ! 413: } ! 414: switch (flags & 3) { ! 415: default: ! 416: case 0: ! 417: return 0; ! 418: case 1: ! 419: return (u32)pmm_malloc(lowzone, handle, size, align); ! 420: case 2: ! 421: return (u32)pmm_malloc(highzone, handle, size, align); ! 422: case 3: { ! 423: void *data = pmm_malloc(lowzone, handle, size, align); ! 424: if (data) ! 425: return (u32)data; ! 426: return (u32)pmm_malloc(highzone, handle, size, align); ! 427: } ! 428: } ! 429: } ! 430: ! 431: // PMM - find ! 432: static u32 ! 433: handle_pmm01(u16 *args) ! 434: { ! 435: u32 handle = *(u32*)&args[1]; ! 436: dprintf(3, "pmm01: handle=%x\n", handle); ! 437: if (handle == PMM_DEFAULT_HANDLE) ! 438: return 0; ! 439: return (u32)pmm_find(handle); ! 440: } ! 441: ! 442: // PMM - deallocate ! 443: static u32 ! 444: handle_pmm02(u16 *args) ! 445: { ! 446: u32 buffer = *(u32*)&args[1]; ! 447: dprintf(3, "pmm02: buffer=%x\n", buffer); ! 448: int ret = pmm_free((void*)buffer); ! 449: if (ret) ! 450: // Error ! 451: return 1; ! 452: return 0; ! 453: } ! 454: ! 455: static u32 ! 456: handle_pmmXX(u16 *args) ! 457: { ! 458: return PMM_FUNCTION_NOT_SUPPORTED; ! 459: } ! 460: ! 461: u32 VISIBLE16 ! 462: handle_pmm(u16 *args) ! 463: { ! 464: if (! CONFIG_PMM) ! 465: return PMM_FUNCTION_NOT_SUPPORTED; ! 466: ! 467: u16 arg1 = args[0]; ! 468: dprintf(DEBUG_HDL_pmm, "pmm call arg1=%x\n", arg1); ! 469: ! 470: switch (arg1) { ! 471: case 0x00: return handle_pmm00(args); ! 472: case 0x01: return handle_pmm01(args); ! 473: case 0x02: return handle_pmm02(args); ! 474: default: return handle_pmmXX(args); ! 475: } ! 476: } ! 477: ! 478: // romlayout.S ! 479: extern void entry_pmm(); ! 480: ! 481: void ! 482: pmm_setup() ! 483: { ! 484: if (! CONFIG_PMM) ! 485: return; ! 486: ! 487: dprintf(3, "init PMM\n"); ! 488: ! 489: PMMHEADER.signature = PMM_SIGNATURE; ! 490: PMMHEADER.entry_offset = (u32)entry_pmm - BUILD_BIOS_ADDR; ! 491: PMMHEADER.checksum -= checksum(&PMMHEADER, sizeof(PMMHEADER)); ! 492: } ! 493: ! 494: void ! 495: pmm_finalize() ! 496: { ! 497: if (! CONFIG_PMM) ! 498: return; ! 499: ! 500: dprintf(3, "finalize PMM\n"); ! 501: ! 502: PMMHEADER.signature = 0; ! 503: PMMHEADER.entry_offset = 0; ! 504: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.