|
|
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
1.1.1.3 root 9: #include "memmap.h" // struct e820entry
1.1 root 10: #include "farptr.h" // GET_FARVAR
11: #include "biosvar.h" // GET_BDA
12:
1.1.1.3 root 13: // Information on a reserved area.
14: struct allocinfo_s {
15: struct allocinfo_s *next, **pprev;
16: void *data, *dataend, *allocend;
17: };
18:
19: // Information on a tracked memory allocation.
20: struct allocdetail_s {
21: struct allocinfo_s detailinfo;
22: struct allocinfo_s datainfo;
23: u32 handle;
24: };
25:
26: // The various memory zones.
1.1 root 27: struct zone_s {
1.1.1.3 root 28: struct allocinfo_s *info;
1.1 root 29: };
30:
1.1.1.5 ! root 31: struct zone_s ZoneLow, ZoneHigh, ZoneFSeg, ZoneTmpLow, ZoneTmpHigh;
1.1 root 32:
1.1.1.5 ! root 33: static struct zone_s *Zones[] = {
1.1 root 34: &ZoneTmpLow, &ZoneLow, &ZoneFSeg, &ZoneTmpHigh, &ZoneHigh
35: };
36:
37:
38: /****************************************************************
1.1.1.3 root 39: * low-level memory reservations
40: ****************************************************************/
41:
42: // Find and reserve space from a given zone
43: static void *
44: allocSpace(struct zone_s *zone, u32 size, u32 align, struct allocinfo_s *fill)
45: {
46: struct allocinfo_s *info;
1.1.1.5 ! root 47: for (info = zone->info; info; info = info->next) {
! 48: void *dataend = info->dataend;
! 49: void *allocend = info->allocend;
1.1.1.3 root 50: void *newallocend = (void*)ALIGN_DOWN((u32)allocend - size, align);
51: if (newallocend >= dataend && newallocend <= allocend) {
52: // Found space - now reserve it.
1.1.1.5 ! root 53: struct allocinfo_s **pprev = info->pprev;
1.1.1.3 root 54: if (!fill)
55: fill = newallocend;
1.1.1.5 ! root 56: fill->next = info;
! 57: fill->pprev = pprev;
! 58: fill->data = newallocend;
! 59: fill->dataend = newallocend + size;
! 60: fill->allocend = allocend;
! 61:
! 62: info->allocend = newallocend;
! 63: info->pprev = &fill->next;
! 64: *pprev = fill;
1.1.1.3 root 65: return newallocend;
66: }
67: }
68: return NULL;
69: }
70:
71: // Release space allocated with allocSpace()
72: static void
73: freeSpace(struct allocinfo_s *info)
74: {
1.1.1.5 ! root 75: struct allocinfo_s *next = info->next;
! 76: struct allocinfo_s **pprev = info->pprev;
! 77: *pprev = next;
1.1.1.3 root 78: if (next) {
1.1.1.5 ! root 79: if (next->allocend == info->data)
! 80: next->allocend = info->allocend;
! 81: next->pprev = pprev;
1.1.1.3 root 82: }
83: }
84:
85: // Add new memory to a zone
86: static void
87: addSpace(struct zone_s *zone, void *start, void *end)
88: {
89: // Find position to add space
90: struct allocinfo_s **pprev = &zone->info, *info;
91: for (;;) {
1.1.1.5 ! root 92: info = *pprev;
! 93: if (!info || info->data < start)
1.1.1.3 root 94: break;
95: pprev = &info->next;
96: }
97:
98: // Add space using temporary allocation info.
99: struct allocdetail_s tempdetail;
100: tempdetail.datainfo.next = info;
101: tempdetail.datainfo.pprev = pprev;
102: tempdetail.datainfo.data = tempdetail.datainfo.dataend = start;
103: tempdetail.datainfo.allocend = end;
1.1.1.5 ! root 104: *pprev = &tempdetail.datainfo;
1.1.1.3 root 105: if (info)
1.1.1.5 ! root 106: info->pprev = &tempdetail.datainfo.next;
1.1.1.3 root 107:
108: // Allocate final allocation info.
109: struct allocdetail_s *detail = allocSpace(
110: &ZoneTmpHigh, sizeof(*detail), MALLOC_MIN_ALIGN, NULL);
111: if (!detail) {
112: detail = allocSpace(&ZoneTmpLow, sizeof(*detail)
113: , MALLOC_MIN_ALIGN, NULL);
114: if (!detail) {
1.1.1.5 ! root 115: *tempdetail.datainfo.pprev = tempdetail.datainfo.next;
1.1.1.3 root 116: if (tempdetail.datainfo.next)
1.1.1.5 ! root 117: tempdetail.datainfo.next->pprev = tempdetail.datainfo.pprev;
1.1.1.3 root 118: warn_noalloc();
119: return;
120: }
121: }
122:
123: // Replace temp alloc space with final alloc space
1.1.1.5 ! root 124: memcpy(&detail->datainfo, &tempdetail.datainfo, sizeof(detail->datainfo));
! 125: detail->handle = PMM_DEFAULT_HANDLE;
1.1.1.3 root 126:
1.1.1.5 ! root 127: *tempdetail.datainfo.pprev = &detail->datainfo;
1.1.1.3 root 128: if (tempdetail.datainfo.next)
1.1.1.5 ! root 129: tempdetail.datainfo.next->pprev = &detail->datainfo.next;
1.1.1.3 root 130: }
131:
132: // Search all zones for an allocation obtained from allocSpace()
133: static struct allocinfo_s *
134: findAlloc(void *data)
135: {
136: int i;
137: for (i=0; i<ARRAY_SIZE(Zones); i++) {
1.1.1.5 ! root 138: struct zone_s *zone = Zones[i];
1.1.1.3 root 139: struct allocinfo_s *info;
1.1.1.5 ! root 140: for (info = zone->info; info; info = info->next)
! 141: if (info->data == data)
1.1.1.3 root 142: return info;
143: }
144: return NULL;
145: }
146:
147: // Return the last sentinal node of a zone
148: static struct allocinfo_s *
149: findLast(struct zone_s *zone)
150: {
1.1.1.5 ! root 151: struct allocinfo_s *info = zone->info;
1.1.1.3 root 152: if (!info)
153: return NULL;
154: for (;;) {
1.1.1.5 ! root 155: struct allocinfo_s *next = info->next;
1.1.1.3 root 156: if (!next)
157: return info;
158: info = next;
159: }
160: }
161:
162:
163: /****************************************************************
164: * Setup
165: ****************************************************************/
166:
167: void
168: malloc_setup(void)
169: {
170: ASSERT32FLAT();
171: dprintf(3, "malloc setup\n");
172:
173: // Populate temp high ram
174: u32 highram = 0;
175: int i;
176: for (i=e820_count-1; i>=0; i--) {
177: struct e820entry *en = &e820_list[i];
178: u64 end = en->start + en->size;
179: if (end < 1024*1024)
180: break;
181: if (en->type != E820_RAM || end > 0xffffffff)
182: continue;
183: u32 s = en->start, e = end;
184: if (!highram) {
185: u32 newe = ALIGN_DOWN(e - CONFIG_MAX_HIGHTABLE, MALLOC_MIN_ALIGN);
186: if (newe <= e && newe >= s) {
187: highram = newe;
188: e = newe;
189: }
190: }
191: addSpace(&ZoneTmpHigh, (void*)s, (void*)e);
192: }
193:
194: // Populate other regions
195: addSpace(&ZoneTmpLow, (void*)BUILD_STACK_ADDR, (void*)BUILD_EBDA_MINIMUM);
196: addSpace(&ZoneFSeg, BiosTableSpace, &BiosTableSpace[CONFIG_MAX_BIOSTABLE]);
197: addSpace(&ZoneLow, (void*)BUILD_LOWRAM_END, (void*)BUILD_LOWRAM_END);
198: if (highram) {
199: addSpace(&ZoneHigh, (void*)highram
200: , (void*)highram + CONFIG_MAX_HIGHTABLE);
201: add_e820(highram, CONFIG_MAX_HIGHTABLE, E820_RESERVED);
202: }
203: }
204:
1.1.1.5 ! root 205: // Update pointers after code relocation.
! 206: void
! 207: malloc_fixupreloc(void)
! 208: {
! 209: ASSERT32FLAT();
! 210: if (!CONFIG_RELOCATE_INIT)
! 211: return;
! 212: dprintf(3, "malloc fixup reloc\n");
! 213:
! 214: int i;
! 215: for (i=0; i<ARRAY_SIZE(Zones); i++) {
! 216: struct zone_s *zone = Zones[i];
! 217: zone->info->pprev = &zone->info;
! 218: }
! 219:
! 220: // Add space free'd during relocation in f-segment to ZoneFSeg
! 221: extern u8 code32init_end[];
! 222: if ((u32)code32init_end > BUILD_BIOS_ADDR) {
! 223: memset((void*)BUILD_BIOS_ADDR, 0, (u32)code32init_end - BUILD_BIOS_ADDR);
! 224: addSpace(&ZoneFSeg, (void*)BUILD_BIOS_ADDR, code32init_end);
! 225: }
! 226: }
! 227:
1.1.1.3 root 228: void
229: malloc_finalize(void)
230: {
1.1.1.5 ! root 231: ASSERT32FLAT();
1.1.1.3 root 232: dprintf(3, "malloc finalize\n");
233:
234: // Reserve more low-mem if needed.
235: u32 endlow = GET_BDA(mem_size_kb)*1024;
236: add_e820(endlow, BUILD_LOWRAM_END-endlow, E820_RESERVED);
237:
238: // Give back unused high ram.
239: struct allocinfo_s *info = findLast(&ZoneHigh);
240: if (info) {
241: u32 giveback = ALIGN_DOWN(info->allocend - info->dataend, PAGE_SIZE);
242: add_e820((u32)info->dataend, giveback, E820_RAM);
243: dprintf(1, "Returned %d bytes of ZoneHigh\n", giveback);
244: }
245: }
246:
247:
248: /****************************************************************
1.1 root 249: * ebda movement
250: ****************************************************************/
251:
252: // Move ebda
253: static int
254: relocate_ebda(u32 newebda, u32 oldebda, u8 ebda_size)
255: {
256: u32 lowram = GET_BDA(mem_size_kb) * 1024;
257: if (oldebda != lowram)
258: // EBDA isn't at end of ram - give up.
259: return -1;
260:
1.1.1.5 ! root 261: // Do copy
! 262: memmove((void*)newebda, (void*)oldebda, ebda_size * 1024);
1.1 root 263:
264: // Update indexes
265: dprintf(1, "ebda moved from %x to %x\n", oldebda, newebda);
266: SET_BDA(mem_size_kb, newebda / 1024);
267: SET_BDA(ebda_seg, FLATPTR_TO_SEG(newebda));
268: return 0;
269: }
270:
271: // Support expanding the ZoneLow dynamically.
272: static void
273: zonelow_expand(u32 size, u32 align)
274: {
1.1.1.3 root 275: struct allocinfo_s *info = findLast(&ZoneLow);
276: if (!info)
277: return;
1.1.1.5 ! root 278: u32 oldpos = (u32)info->allocend;
1.1 root 279: u32 newpos = ALIGN_DOWN(oldpos - size, align);
1.1.1.5 ! root 280: u32 bottom = (u32)info->dataend;
1.1 root 281: if (newpos >= bottom && newpos <= oldpos)
282: // Space already present.
283: return;
284: u16 ebda_seg = get_ebda_seg();
285: u32 ebda_pos = (u32)MAKE_FLATPTR(ebda_seg, 0);
286: u8 ebda_size = GET_EBDA2(ebda_seg, size);
287: u32 ebda_end = ebda_pos + ebda_size * 1024;
1.1.1.3 root 288: if (ebda_end != bottom)
1.1 root 289: // Something else is after ebda - can't use any existing space.
1.1.1.3 root 290: newpos = ALIGN_DOWN(ebda_end - size, align);
1.1 root 291: u32 newbottom = ALIGN_DOWN(newpos, 1024);
292: u32 newebda = ALIGN_DOWN(newbottom - ebda_size * 1024, 1024);
293: if (newebda < BUILD_EBDA_MINIMUM)
294: // Not enough space.
295: return;
296:
297: // Move ebda
298: int ret = relocate_ebda(newebda, ebda_pos, ebda_size);
299: if (ret)
300: return;
301:
302: // Update zone
1.1.1.3 root 303: if (ebda_end == bottom) {
1.1.1.5 ! root 304: info->data = (void*)newbottom;
! 305: info->dataend = (void*)newbottom;
1.1.1.3 root 306: } else
307: addSpace(&ZoneLow, (void*)newbottom, (void*)ebda_end);
1.1 root 308: }
309:
1.1.1.3 root 310: // Check if can expand the given zone to fulfill an allocation
1.1 root 311: static void *
1.1.1.3 root 312: allocExpandSpace(struct zone_s *zone, u32 size, u32 align
313: , struct allocinfo_s *fill)
1.1 root 314: {
1.1.1.3 root 315: void *data = allocSpace(zone, size, align, fill);
316: if (data || zone != &ZoneLow)
317: return data;
318:
319: // Make sure to not move ebda while an optionrom is running.
320: if (unlikely(wait_preempt())) {
321: data = allocSpace(zone, size, align, fill);
322: if (data)
323: return data;
1.1 root 324: }
325:
1.1.1.3 root 326: zonelow_expand(size, align);
327: return allocSpace(zone, size, align, fill);
1.1 root 328: }
329:
330:
331: /****************************************************************
332: * tracked memory allocations
333: ****************************************************************/
334:
335: // Allocate memory from the given zone and track it as a PMM allocation
1.1.1.3 root 336: void * __malloc
1.1 root 337: pmm_malloc(struct zone_s *zone, u32 handle, u32 size, u32 align)
338: {
1.1.1.5 ! root 339: ASSERT32FLAT();
1.1.1.3 root 340: if (!size)
341: return NULL;
342:
343: // Find and reserve space for bookkeeping.
344: struct allocdetail_s *detail = allocSpace(
345: &ZoneTmpHigh, sizeof(*detail), MALLOC_MIN_ALIGN, NULL);
346: if (!detail) {
347: detail = allocSpace(&ZoneTmpLow, sizeof(*detail)
348: , MALLOC_MIN_ALIGN, NULL);
349: if (!detail)
1.1 root 350: return NULL;
351: }
1.1.1.3 root 352:
353: // Find and reserve space for main allocation
354: void *data = allocExpandSpace(zone, size, align, &detail->datainfo);
355: if (!data) {
356: freeSpace(&detail->detailinfo);
1.1 root 357: return NULL;
358: }
1.1.1.3 root 359:
1.1 root 360: dprintf(8, "pmm_malloc zone=%p handle=%x size=%d align=%x"
1.1.1.3 root 361: " ret=%p (detail=%p)\n"
1.1 root 362: , zone, handle, size, align
1.1.1.3 root 363: , data, detail);
1.1.1.5 ! root 364: detail->handle = handle;
1.1 root 365:
1.1.1.3 root 366: return data;
1.1 root 367: }
368:
369: // Free a data block allocated with pmm_malloc
370: int
371: pmm_free(void *data)
372: {
1.1.1.5 ! root 373: ASSERT32FLAT();
1.1.1.3 root 374: struct allocinfo_s *info = findAlloc(data);
1.1.1.5 ! root 375: if (!info || data == (void*)info || data == info->dataend)
1.1.1.3 root 376: return -1;
377: struct allocdetail_s *detail = container_of(
378: info, struct allocdetail_s, datainfo);
379: dprintf(8, "pmm_free %p (detail=%p)\n", data, detail);
380: freeSpace(info);
381: freeSpace(&detail->detailinfo);
382: return 0;
1.1 root 383: }
384:
385: // Find the amount of free space in a given zone.
386: static u32
387: pmm_getspace(struct zone_s *zone)
388: {
389: // XXX - doesn't account for ZoneLow being able to grow.
1.1.1.3 root 390: // XXX - results not reliable when CONFIG_THREAD_OPTIONROMS
391: u32 maxspace = 0;
392: struct allocinfo_s *info;
1.1.1.5 ! root 393: for (info = zone->info; info; info = info->next) {
! 394: u32 space = info->allocend - info->dataend;
1.1.1.3 root 395: if (space > maxspace)
396: maxspace = space;
397: }
398:
1.1 root 399: if (zone != &ZoneTmpHigh && zone != &ZoneTmpLow)
1.1.1.3 root 400: return maxspace;
1.1 root 401: // Account for space needed for PMM tracking.
1.1.1.3 root 402: u32 reserve = ALIGN(sizeof(struct allocdetail_s), MALLOC_MIN_ALIGN);
403: if (maxspace <= reserve)
1.1 root 404: return 0;
1.1.1.3 root 405: return maxspace - reserve;
1.1 root 406: }
407:
408: // Find the data block allocated with pmm_malloc with a given handle.
409: static void *
410: pmm_find(u32 handle)
411: {
1.1.1.3 root 412: int i;
413: for (i=0; i<ARRAY_SIZE(Zones); i++) {
1.1.1.5 ! root 414: struct zone_s *zone = Zones[i];
1.1.1.3 root 415: struct allocinfo_s *info;
1.1.1.5 ! root 416: for (info = zone->info; info; info = info->next) {
! 417: if (info->data != (void*)info)
1.1.1.3 root 418: continue;
419: struct allocdetail_s *detail = container_of(
420: info, struct allocdetail_s, detailinfo);
1.1.1.5 ! root 421: if (detail->handle == handle)
! 422: return detail->datainfo.data;
1.1.1.3 root 423: }
1.1 root 424: }
1.1.1.3 root 425: return NULL;
1.1 root 426: }
427:
428:
429: /****************************************************************
430: * pmm interface
431: ****************************************************************/
432:
433: struct pmmheader {
434: u32 signature;
435: u8 version;
436: u8 length;
437: u8 checksum;
438: u16 entry_offset;
439: u16 entry_seg;
440: u8 reserved[5];
441: } PACKED;
442:
443: extern struct pmmheader PMMHEADER;
444:
445: #define PMM_SIGNATURE 0x4d4d5024 // $PMM
446:
447: #if CONFIG_PMM
448: struct pmmheader PMMHEADER __aligned(16) VAR16EXPORT = {
449: .version = 0x01,
450: .length = sizeof(PMMHEADER),
451: .entry_seg = SEG_BIOS,
452: };
453: #endif
454:
455: #define PMM_FUNCTION_NOT_SUPPORTED 0xffffffff
456:
457: // PMM - allocate
458: static u32
459: handle_pmm00(u16 *args)
460: {
461: u32 length = *(u32*)&args[1], handle = *(u32*)&args[3];
462: u16 flags = args[5];
463: dprintf(3, "pmm00: length=%x handle=%x flags=%x\n"
464: , length, handle, flags);
465: struct zone_s *lowzone = &ZoneTmpLow, *highzone = &ZoneTmpHigh;
466: if (flags & 8) {
467: // Permanent memory request.
468: lowzone = &ZoneLow;
469: highzone = &ZoneHigh;
470: }
471: if (!length) {
472: // Memory size request
473: switch (flags & 3) {
474: default:
475: case 0:
476: return 0;
477: case 1:
478: return pmm_getspace(lowzone);
479: case 2:
480: return pmm_getspace(highzone);
481: case 3: {
482: u32 spacelow = pmm_getspace(lowzone);
483: u32 spacehigh = pmm_getspace(highzone);
484: if (spacelow > spacehigh)
485: return spacelow;
486: return spacehigh;
487: }
488: }
489: }
490: u32 size = length * 16;
491: if ((s32)size <= 0)
492: return 0;
493: u32 align = MALLOC_MIN_ALIGN;
494: if (flags & 4) {
495: align = 1<<__ffs(size);
496: if (align < MALLOC_MIN_ALIGN)
497: align = MALLOC_MIN_ALIGN;
498: }
499: switch (flags & 3) {
500: default:
501: case 0:
502: return 0;
503: case 1:
504: return (u32)pmm_malloc(lowzone, handle, size, align);
505: case 2:
506: return (u32)pmm_malloc(highzone, handle, size, align);
507: case 3: {
508: void *data = pmm_malloc(lowzone, handle, size, align);
509: if (data)
510: return (u32)data;
511: return (u32)pmm_malloc(highzone, handle, size, align);
512: }
513: }
514: }
515:
516: // PMM - find
517: static u32
518: handle_pmm01(u16 *args)
519: {
520: u32 handle = *(u32*)&args[1];
521: dprintf(3, "pmm01: handle=%x\n", handle);
522: if (handle == PMM_DEFAULT_HANDLE)
523: return 0;
524: return (u32)pmm_find(handle);
525: }
526:
527: // PMM - deallocate
528: static u32
529: handle_pmm02(u16 *args)
530: {
531: u32 buffer = *(u32*)&args[1];
532: dprintf(3, "pmm02: buffer=%x\n", buffer);
533: int ret = pmm_free((void*)buffer);
534: if (ret)
535: // Error
536: return 1;
537: return 0;
538: }
539:
540: static u32
541: handle_pmmXX(u16 *args)
542: {
543: return PMM_FUNCTION_NOT_SUPPORTED;
544: }
545:
1.1.1.5 ! root 546: u32 VISIBLE32INIT
1.1 root 547: handle_pmm(u16 *args)
548: {
1.1.1.5 ! root 549: ASSERT32FLAT();
1.1 root 550: if (! CONFIG_PMM)
551: return PMM_FUNCTION_NOT_SUPPORTED;
552:
553: u16 arg1 = args[0];
554: dprintf(DEBUG_HDL_pmm, "pmm call arg1=%x\n", arg1);
555:
1.1.1.5 ! root 556: int oldpreempt;
! 557: if (CONFIG_THREAD_OPTIONROMS) {
! 558: // Not a preemption event - don't wait in wait_preempt()
! 559: oldpreempt = CanPreempt;
! 560: CanPreempt = 0;
! 561: }
! 562:
! 563: u32 ret;
1.1 root 564: switch (arg1) {
1.1.1.5 ! root 565: case 0x00: ret = handle_pmm00(args); break;
! 566: case 0x01: ret = handle_pmm01(args); break;
! 567: case 0x02: ret = handle_pmm02(args); break;
! 568: default: ret = handle_pmmXX(args); break;
1.1 root 569: }
1.1.1.5 ! root 570:
! 571: if (CONFIG_THREAD_OPTIONROMS)
! 572: CanPreempt = oldpreempt;
! 573:
! 574: return ret;
1.1 root 575: }
576:
577: // romlayout.S
1.1.1.2 root 578: extern void entry_pmm(void);
1.1 root 579:
580: void
1.1.1.2 root 581: pmm_setup(void)
1.1 root 582: {
583: if (! CONFIG_PMM)
584: return;
585:
586: dprintf(3, "init PMM\n");
587:
588: PMMHEADER.signature = PMM_SIGNATURE;
589: PMMHEADER.entry_offset = (u32)entry_pmm - BUILD_BIOS_ADDR;
590: PMMHEADER.checksum -= checksum(&PMMHEADER, sizeof(PMMHEADER));
591: }
592:
593: void
1.1.1.2 root 594: pmm_finalize(void)
1.1 root 595: {
596: if (! CONFIG_PMM)
597: return;
598:
599: dprintf(3, "finalize PMM\n");
600:
601: PMMHEADER.signature = 0;
602: PMMHEADER.entry_offset = 0;
603: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.