|
|
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];
1.1.1.6 ! root 217: if (zone->info)
! 218: zone->info->pprev = &zone->info;
1.1.1.5 root 219: }
220:
221: // Add space free'd during relocation in f-segment to ZoneFSeg
222: extern u8 code32init_end[];
223: if ((u32)code32init_end > BUILD_BIOS_ADDR) {
224: memset((void*)BUILD_BIOS_ADDR, 0, (u32)code32init_end - BUILD_BIOS_ADDR);
225: addSpace(&ZoneFSeg, (void*)BUILD_BIOS_ADDR, code32init_end);
226: }
227: }
228:
1.1.1.3 root 229: void
230: malloc_finalize(void)
231: {
1.1.1.5 root 232: ASSERT32FLAT();
1.1.1.3 root 233: dprintf(3, "malloc finalize\n");
234:
235: // Reserve more low-mem if needed.
236: u32 endlow = GET_BDA(mem_size_kb)*1024;
237: add_e820(endlow, BUILD_LOWRAM_END-endlow, E820_RESERVED);
238:
239: // Give back unused high ram.
240: struct allocinfo_s *info = findLast(&ZoneHigh);
241: if (info) {
242: u32 giveback = ALIGN_DOWN(info->allocend - info->dataend, PAGE_SIZE);
243: add_e820((u32)info->dataend, giveback, E820_RAM);
244: dprintf(1, "Returned %d bytes of ZoneHigh\n", giveback);
245: }
246: }
247:
248:
249: /****************************************************************
1.1 root 250: * ebda movement
251: ****************************************************************/
252:
253: // Move ebda
254: static int
255: relocate_ebda(u32 newebda, u32 oldebda, u8 ebda_size)
256: {
257: u32 lowram = GET_BDA(mem_size_kb) * 1024;
258: if (oldebda != lowram)
259: // EBDA isn't at end of ram - give up.
260: return -1;
261:
1.1.1.5 root 262: // Do copy
263: memmove((void*)newebda, (void*)oldebda, ebda_size * 1024);
1.1 root 264:
265: // Update indexes
266: dprintf(1, "ebda moved from %x to %x\n", oldebda, newebda);
267: SET_BDA(mem_size_kb, newebda / 1024);
268: SET_BDA(ebda_seg, FLATPTR_TO_SEG(newebda));
269: return 0;
270: }
271:
272: // Support expanding the ZoneLow dynamically.
273: static void
274: zonelow_expand(u32 size, u32 align)
275: {
1.1.1.3 root 276: struct allocinfo_s *info = findLast(&ZoneLow);
277: if (!info)
278: return;
1.1.1.5 root 279: u32 oldpos = (u32)info->allocend;
1.1 root 280: u32 newpos = ALIGN_DOWN(oldpos - size, align);
1.1.1.5 root 281: u32 bottom = (u32)info->dataend;
1.1 root 282: if (newpos >= bottom && newpos <= oldpos)
283: // Space already present.
284: return;
285: u16 ebda_seg = get_ebda_seg();
286: u32 ebda_pos = (u32)MAKE_FLATPTR(ebda_seg, 0);
287: u8 ebda_size = GET_EBDA2(ebda_seg, size);
288: u32 ebda_end = ebda_pos + ebda_size * 1024;
1.1.1.3 root 289: if (ebda_end != bottom)
1.1 root 290: // Something else is after ebda - can't use any existing space.
1.1.1.3 root 291: newpos = ALIGN_DOWN(ebda_end - size, align);
1.1 root 292: u32 newbottom = ALIGN_DOWN(newpos, 1024);
293: u32 newebda = ALIGN_DOWN(newbottom - ebda_size * 1024, 1024);
294: if (newebda < BUILD_EBDA_MINIMUM)
295: // Not enough space.
296: return;
297:
298: // Move ebda
299: int ret = relocate_ebda(newebda, ebda_pos, ebda_size);
300: if (ret)
301: return;
302:
303: // Update zone
1.1.1.3 root 304: if (ebda_end == bottom) {
1.1.1.5 root 305: info->data = (void*)newbottom;
306: info->dataend = (void*)newbottom;
1.1.1.3 root 307: } else
308: addSpace(&ZoneLow, (void*)newbottom, (void*)ebda_end);
1.1 root 309: }
310:
1.1.1.3 root 311: // Check if can expand the given zone to fulfill an allocation
1.1 root 312: static void *
1.1.1.3 root 313: allocExpandSpace(struct zone_s *zone, u32 size, u32 align
314: , struct allocinfo_s *fill)
1.1 root 315: {
1.1.1.3 root 316: void *data = allocSpace(zone, size, align, fill);
317: if (data || zone != &ZoneLow)
318: return data;
319:
320: // Make sure to not move ebda while an optionrom is running.
321: if (unlikely(wait_preempt())) {
322: data = allocSpace(zone, size, align, fill);
323: if (data)
324: return data;
1.1 root 325: }
326:
1.1.1.3 root 327: zonelow_expand(size, align);
328: return allocSpace(zone, size, align, fill);
1.1 root 329: }
330:
331:
332: /****************************************************************
333: * tracked memory allocations
334: ****************************************************************/
335:
336: // Allocate memory from the given zone and track it as a PMM allocation
1.1.1.3 root 337: void * __malloc
1.1 root 338: pmm_malloc(struct zone_s *zone, u32 handle, u32 size, u32 align)
339: {
1.1.1.5 root 340: ASSERT32FLAT();
1.1.1.3 root 341: if (!size)
342: return NULL;
343:
344: // Find and reserve space for bookkeeping.
345: struct allocdetail_s *detail = allocSpace(
346: &ZoneTmpHigh, sizeof(*detail), MALLOC_MIN_ALIGN, NULL);
347: if (!detail) {
348: detail = allocSpace(&ZoneTmpLow, sizeof(*detail)
349: , MALLOC_MIN_ALIGN, NULL);
350: if (!detail)
1.1 root 351: return NULL;
352: }
1.1.1.3 root 353:
354: // Find and reserve space for main allocation
355: void *data = allocExpandSpace(zone, size, align, &detail->datainfo);
356: if (!data) {
357: freeSpace(&detail->detailinfo);
1.1 root 358: return NULL;
359: }
1.1.1.3 root 360:
1.1 root 361: dprintf(8, "pmm_malloc zone=%p handle=%x size=%d align=%x"
1.1.1.3 root 362: " ret=%p (detail=%p)\n"
1.1 root 363: , zone, handle, size, align
1.1.1.3 root 364: , data, detail);
1.1.1.5 root 365: detail->handle = handle;
1.1 root 366:
1.1.1.3 root 367: return data;
1.1 root 368: }
369:
370: // Free a data block allocated with pmm_malloc
371: int
372: pmm_free(void *data)
373: {
1.1.1.5 root 374: ASSERT32FLAT();
1.1.1.3 root 375: struct allocinfo_s *info = findAlloc(data);
1.1.1.5 root 376: if (!info || data == (void*)info || data == info->dataend)
1.1.1.3 root 377: return -1;
378: struct allocdetail_s *detail = container_of(
379: info, struct allocdetail_s, datainfo);
380: dprintf(8, "pmm_free %p (detail=%p)\n", data, detail);
381: freeSpace(info);
382: freeSpace(&detail->detailinfo);
383: return 0;
1.1 root 384: }
385:
386: // Find the amount of free space in a given zone.
387: static u32
388: pmm_getspace(struct zone_s *zone)
389: {
390: // XXX - doesn't account for ZoneLow being able to grow.
1.1.1.3 root 391: // XXX - results not reliable when CONFIG_THREAD_OPTIONROMS
392: u32 maxspace = 0;
393: struct allocinfo_s *info;
1.1.1.5 root 394: for (info = zone->info; info; info = info->next) {
395: u32 space = info->allocend - info->dataend;
1.1.1.3 root 396: if (space > maxspace)
397: maxspace = space;
398: }
399:
1.1 root 400: if (zone != &ZoneTmpHigh && zone != &ZoneTmpLow)
1.1.1.3 root 401: return maxspace;
1.1 root 402: // Account for space needed for PMM tracking.
1.1.1.3 root 403: u32 reserve = ALIGN(sizeof(struct allocdetail_s), MALLOC_MIN_ALIGN);
404: if (maxspace <= reserve)
1.1 root 405: return 0;
1.1.1.3 root 406: return maxspace - reserve;
1.1 root 407: }
408:
409: // Find the data block allocated with pmm_malloc with a given handle.
410: static void *
411: pmm_find(u32 handle)
412: {
1.1.1.3 root 413: int i;
414: for (i=0; i<ARRAY_SIZE(Zones); i++) {
1.1.1.5 root 415: struct zone_s *zone = Zones[i];
1.1.1.3 root 416: struct allocinfo_s *info;
1.1.1.5 root 417: for (info = zone->info; info; info = info->next) {
418: if (info->data != (void*)info)
1.1.1.3 root 419: continue;
420: struct allocdetail_s *detail = container_of(
421: info, struct allocdetail_s, detailinfo);
1.1.1.5 root 422: if (detail->handle == handle)
423: return detail->datainfo.data;
1.1.1.3 root 424: }
1.1 root 425: }
1.1.1.3 root 426: return NULL;
1.1 root 427: }
428:
429:
430: /****************************************************************
431: * pmm interface
432: ****************************************************************/
433:
434: struct pmmheader {
435: u32 signature;
436: u8 version;
437: u8 length;
438: u8 checksum;
439: u16 entry_offset;
440: u16 entry_seg;
441: u8 reserved[5];
442: } PACKED;
443:
444: extern struct pmmheader PMMHEADER;
445:
446: #define PMM_SIGNATURE 0x4d4d5024 // $PMM
447:
448: #if CONFIG_PMM
449: struct pmmheader PMMHEADER __aligned(16) VAR16EXPORT = {
450: .version = 0x01,
451: .length = sizeof(PMMHEADER),
452: .entry_seg = SEG_BIOS,
453: };
454: #endif
455:
456: #define PMM_FUNCTION_NOT_SUPPORTED 0xffffffff
457:
458: // PMM - allocate
459: static u32
460: handle_pmm00(u16 *args)
461: {
462: u32 length = *(u32*)&args[1], handle = *(u32*)&args[3];
463: u16 flags = args[5];
464: dprintf(3, "pmm00: length=%x handle=%x flags=%x\n"
465: , length, handle, flags);
466: struct zone_s *lowzone = &ZoneTmpLow, *highzone = &ZoneTmpHigh;
467: if (flags & 8) {
468: // Permanent memory request.
469: lowzone = &ZoneLow;
470: highzone = &ZoneHigh;
471: }
472: if (!length) {
473: // Memory size request
474: switch (flags & 3) {
475: default:
476: case 0:
477: return 0;
478: case 1:
479: return pmm_getspace(lowzone);
480: case 2:
481: return pmm_getspace(highzone);
482: case 3: {
483: u32 spacelow = pmm_getspace(lowzone);
484: u32 spacehigh = pmm_getspace(highzone);
485: if (spacelow > spacehigh)
486: return spacelow;
487: return spacehigh;
488: }
489: }
490: }
491: u32 size = length * 16;
492: if ((s32)size <= 0)
493: return 0;
494: u32 align = MALLOC_MIN_ALIGN;
495: if (flags & 4) {
496: align = 1<<__ffs(size);
497: if (align < MALLOC_MIN_ALIGN)
498: align = MALLOC_MIN_ALIGN;
499: }
500: switch (flags & 3) {
501: default:
502: case 0:
503: return 0;
504: case 1:
505: return (u32)pmm_malloc(lowzone, handle, size, align);
506: case 2:
507: return (u32)pmm_malloc(highzone, handle, size, align);
508: case 3: {
509: void *data = pmm_malloc(lowzone, handle, size, align);
510: if (data)
511: return (u32)data;
512: return (u32)pmm_malloc(highzone, handle, size, align);
513: }
514: }
515: }
516:
517: // PMM - find
518: static u32
519: handle_pmm01(u16 *args)
520: {
521: u32 handle = *(u32*)&args[1];
522: dprintf(3, "pmm01: handle=%x\n", handle);
523: if (handle == PMM_DEFAULT_HANDLE)
524: return 0;
525: return (u32)pmm_find(handle);
526: }
527:
528: // PMM - deallocate
529: static u32
530: handle_pmm02(u16 *args)
531: {
532: u32 buffer = *(u32*)&args[1];
533: dprintf(3, "pmm02: buffer=%x\n", buffer);
534: int ret = pmm_free((void*)buffer);
535: if (ret)
536: // Error
537: return 1;
538: return 0;
539: }
540:
541: static u32
542: handle_pmmXX(u16 *args)
543: {
544: return PMM_FUNCTION_NOT_SUPPORTED;
545: }
546:
1.1.1.5 root 547: u32 VISIBLE32INIT
1.1 root 548: handle_pmm(u16 *args)
549: {
1.1.1.5 root 550: ASSERT32FLAT();
1.1 root 551: if (! CONFIG_PMM)
552: return PMM_FUNCTION_NOT_SUPPORTED;
553:
554: u16 arg1 = args[0];
555: dprintf(DEBUG_HDL_pmm, "pmm call arg1=%x\n", arg1);
556:
1.1.1.5 root 557: int oldpreempt;
558: if (CONFIG_THREAD_OPTIONROMS) {
559: // Not a preemption event - don't wait in wait_preempt()
560: oldpreempt = CanPreempt;
561: CanPreempt = 0;
562: }
563:
564: u32 ret;
1.1 root 565: switch (arg1) {
1.1.1.5 root 566: case 0x00: ret = handle_pmm00(args); break;
567: case 0x01: ret = handle_pmm01(args); break;
568: case 0x02: ret = handle_pmm02(args); break;
569: default: ret = handle_pmmXX(args); break;
1.1 root 570: }
1.1.1.5 root 571:
572: if (CONFIG_THREAD_OPTIONROMS)
573: CanPreempt = oldpreempt;
574:
575: return ret;
1.1 root 576: }
577:
578: // romlayout.S
1.1.1.2 root 579: extern void entry_pmm(void);
1.1 root 580:
581: void
1.1.1.2 root 582: pmm_setup(void)
1.1 root 583: {
584: if (! CONFIG_PMM)
585: return;
586:
587: dprintf(3, "init PMM\n");
588:
589: PMMHEADER.signature = PMM_SIGNATURE;
590: PMMHEADER.entry_offset = (u32)entry_pmm - BUILD_BIOS_ADDR;
591: PMMHEADER.checksum -= checksum(&PMMHEADER, sizeof(PMMHEADER));
592: }
593:
594: void
1.1.1.2 root 595: pmm_finalize(void)
1.1 root 596: {
597: if (! CONFIG_PMM)
598: return;
599:
600: dprintf(3, "finalize PMM\n");
601:
602: PMMHEADER.signature = 0;
603: PMMHEADER.entry_offset = 0;
604: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.