blob: c649fd838ffc8fc77fc90a593023adcda1502940 [file] [log] [blame]
Kevin O'Connore54ee382009-07-26 19:33:13 -04001// Post memory manager (PMM) calls
2//
3// Copyright (C) 2009 Kevin O'Connor <kevin@koconnor.net>
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
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -04009#include "memmap.h" // struct e820entry
Kevin O'Connor0bf92702009-08-01 11:45:37 -040010#include "farptr.h" // GET_FARVAR
Kevin O'Connorf416fe92009-09-24 20:51:55 -040011#include "biosvar.h" // GET_BDA
Kevin O'Connor0bf92702009-08-01 11:45:37 -040012
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -040013// Information on a reserved area.
14struct allocinfo_s {
15 struct allocinfo_s *next, **pprev;
16 void *data, *dataend, *allocend;
Kevin O'Connor0bf92702009-08-01 11:45:37 -040017};
18
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -040019// Information on a tracked memory allocation.
20struct allocdetail_s {
21 struct allocinfo_s detailinfo;
22 struct allocinfo_s datainfo;
23 u32 handle;
24};
25
26// The various memory zones.
27struct zone_s {
28 struct allocinfo_s *info;
29};
30
Kevin O'Connor1313b782011-07-16 13:39:26 -040031struct zone_s ZoneLow, ZoneHigh, ZoneFSeg, ZoneTmpLow, ZoneTmpHigh;
Kevin O'Connor0bf92702009-08-01 11:45:37 -040032
Kevin O'Connor1313b782011-07-16 13:39:26 -040033static struct zone_s *Zones[] = {
Kevin O'Connor0e9fd612009-08-22 21:31:58 -040034 &ZoneTmpLow, &ZoneLow, &ZoneFSeg, &ZoneTmpHigh, &ZoneHigh
Kevin O'Connor0bf92702009-08-01 11:45:37 -040035};
36
Kevin O'Connorf416fe92009-09-24 20:51:55 -040037
38/****************************************************************
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -040039 * low-level memory reservations
40 ****************************************************************/
41
42// Find and reserve space from a given zone
43static void *
44allocSpace(struct zone_s *zone, u32 size, u32 align, struct allocinfo_s *fill)
45{
46 struct allocinfo_s *info;
Kevin O'Connor1313b782011-07-16 13:39:26 -040047 for (info = zone->info; info; info = info->next) {
48 void *dataend = info->dataend;
49 void *allocend = info->allocend;
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -040050 void *newallocend = (void*)ALIGN_DOWN((u32)allocend - size, align);
51 if (newallocend >= dataend && newallocend <= allocend) {
52 // Found space - now reserve it.
Kevin O'Connor1313b782011-07-16 13:39:26 -040053 struct allocinfo_s **pprev = info->pprev;
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -040054 if (!fill)
55 fill = newallocend;
Kevin O'Connor1313b782011-07-16 13:39:26 -040056 fill->next = info;
57 fill->pprev = pprev;
58 fill->data = newallocend;
59 fill->dataend = newallocend + size;
60 fill->allocend = allocend;
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -040061
Kevin O'Connor1313b782011-07-16 13:39:26 -040062 info->allocend = newallocend;
63 info->pprev = &fill->next;
64 *pprev = fill;
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -040065 return newallocend;
66 }
67 }
68 return NULL;
69}
70
71// Release space allocated with allocSpace()
72static void
73freeSpace(struct allocinfo_s *info)
74{
Kevin O'Connor1313b782011-07-16 13:39:26 -040075 struct allocinfo_s *next = info->next;
76 struct allocinfo_s **pprev = info->pprev;
77 *pprev = next;
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -040078 if (next) {
Kevin O'Connor1313b782011-07-16 13:39:26 -040079 if (next->allocend == info->data)
80 next->allocend = info->allocend;
81 next->pprev = pprev;
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -040082 }
83}
84
85// Add new memory to a zone
86static void
87addSpace(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 (;;) {
Kevin O'Connor1313b782011-07-16 13:39:26 -040092 info = *pprev;
93 if (!info || info->data < start)
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -040094 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;
Kevin O'Connor1313b782011-07-16 13:39:26 -0400104 *pprev = &tempdetail.datainfo;
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -0400105 if (info)
Kevin O'Connor1313b782011-07-16 13:39:26 -0400106 info->pprev = &tempdetail.datainfo.next;
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -0400107
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) {
Kevin O'Connor1313b782011-07-16 13:39:26 -0400115 *tempdetail.datainfo.pprev = tempdetail.datainfo.next;
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -0400116 if (tempdetail.datainfo.next)
Kevin O'Connor1313b782011-07-16 13:39:26 -0400117 tempdetail.datainfo.next->pprev = tempdetail.datainfo.pprev;
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -0400118 warn_noalloc();
119 return;
120 }
121 }
122
123 // Replace temp alloc space with final alloc space
Kevin O'Connor1313b782011-07-16 13:39:26 -0400124 memcpy(&detail->datainfo, &tempdetail.datainfo, sizeof(detail->datainfo));
125 detail->handle = PMM_DEFAULT_HANDLE;
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -0400126
Kevin O'Connor1313b782011-07-16 13:39:26 -0400127 *tempdetail.datainfo.pprev = &detail->datainfo;
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -0400128 if (tempdetail.datainfo.next)
Kevin O'Connor1313b782011-07-16 13:39:26 -0400129 tempdetail.datainfo.next->pprev = &detail->datainfo.next;
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -0400130}
131
132// Search all zones for an allocation obtained from allocSpace()
133static struct allocinfo_s *
134findAlloc(void *data)
135{
136 int i;
137 for (i=0; i<ARRAY_SIZE(Zones); i++) {
Kevin O'Connor1313b782011-07-16 13:39:26 -0400138 struct zone_s *zone = Zones[i];
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -0400139 struct allocinfo_s *info;
Kevin O'Connor1313b782011-07-16 13:39:26 -0400140 for (info = zone->info; info; info = info->next)
141 if (info->data == data)
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -0400142 return info;
143 }
144 return NULL;
145}
146
147// Return the last sentinal node of a zone
148static struct allocinfo_s *
149findLast(struct zone_s *zone)
150{
Kevin O'Connor1313b782011-07-16 13:39:26 -0400151 struct allocinfo_s *info = zone->info;
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -0400152 if (!info)
153 return NULL;
154 for (;;) {
Kevin O'Connor1313b782011-07-16 13:39:26 -0400155 struct allocinfo_s *next = info->next;
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -0400156 if (!next)
157 return info;
158 info = next;
159 }
160}
161
162
163/****************************************************************
164 * Setup
165 ****************************************************************/
166
167void
168malloc_setup(void)
169{
170 ASSERT32FLAT();
171 dprintf(3, "malloc setup\n");
172
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -0400173 // 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
Kevin O'Connor533b6282011-07-16 13:13:12 -0400205// Update pointers after code relocation.
206void
207malloc_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];
Kevin O'Connor890d9852012-02-15 20:13:05 -0500217 if (zone->info)
218 zone->info->pprev = &zone->info;
Kevin O'Connor533b6282011-07-16 13:13:12 -0400219 }
Kevin O'Connor8b9137d2011-08-03 20:15:26 -0400220
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 }
Kevin O'Connor533b6282011-07-16 13:13:12 -0400227}
228
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -0400229void
230malloc_finalize(void)
231{
Kevin O'Connor1313b782011-07-16 13:39:26 -0400232 ASSERT32FLAT();
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -0400233 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 }
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -0400246}
247
248
249/****************************************************************
Kevin O'Connorf416fe92009-09-24 20:51:55 -0400250 * ebda movement
251 ****************************************************************/
252
253// Move ebda
254static int
255relocate_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
Kevin O'Connor1313b782011-07-16 13:39:26 -0400262 // Do copy
263 memmove((void*)newebda, (void*)oldebda, ebda_size * 1024);
Kevin O'Connorf416fe92009-09-24 20:51:55 -0400264
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.
Kevin O'Connord948e2b2009-10-12 12:54:56 -0400273static void
Kevin O'Connorf416fe92009-09-24 20:51:55 -0400274zonelow_expand(u32 size, u32 align)
275{
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -0400276 struct allocinfo_s *info = findLast(&ZoneLow);
277 if (!info)
278 return;
Kevin O'Connor1313b782011-07-16 13:39:26 -0400279 u32 oldpos = (u32)info->allocend;
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -0400280 u32 newpos = ALIGN_DOWN(oldpos - size, align);
Kevin O'Connor1313b782011-07-16 13:39:26 -0400281 u32 bottom = (u32)info->dataend;
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -0400282 if (newpos >= bottom && newpos <= oldpos)
283 // Space already present.
284 return;
Kevin O'Connorf416fe92009-09-24 20:51:55 -0400285 u16 ebda_seg = get_ebda_seg();
286 u32 ebda_pos = (u32)MAKE_FLATPTR(ebda_seg, 0);
Kevin O'Connorf416fe92009-09-24 20:51:55 -0400287 u8 ebda_size = GET_EBDA2(ebda_seg, size);
288 u32 ebda_end = ebda_pos + ebda_size * 1024;
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -0400289 if (ebda_end != bottom)
Kevin O'Connorf416fe92009-09-24 20:51:55 -0400290 // Something else is after ebda - can't use any existing space.
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -0400291 newpos = ALIGN_DOWN(ebda_end - size, align);
Kevin O'Connord948e2b2009-10-12 12:54:56 -0400292 u32 newbottom = ALIGN_DOWN(newpos, 1024);
Kevin O'Connorf416fe92009-09-24 20:51:55 -0400293 u32 newebda = ALIGN_DOWN(newbottom - ebda_size * 1024, 1024);
294 if (newebda < BUILD_EBDA_MINIMUM)
295 // Not enough space.
Kevin O'Connord948e2b2009-10-12 12:54:56 -0400296 return;
Kevin O'Connorf416fe92009-09-24 20:51:55 -0400297
298 // Move ebda
299 int ret = relocate_ebda(newebda, ebda_pos, ebda_size);
300 if (ret)
Kevin O'Connord948e2b2009-10-12 12:54:56 -0400301 return;
Kevin O'Connorf416fe92009-09-24 20:51:55 -0400302
303 // Update zone
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -0400304 if (ebda_end == bottom) {
Kevin O'Connor1313b782011-07-16 13:39:26 -0400305 info->data = (void*)newbottom;
306 info->dataend = (void*)newbottom;
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -0400307 } else
308 addSpace(&ZoneLow, (void*)newbottom, (void*)ebda_end);
Kevin O'Connorf416fe92009-09-24 20:51:55 -0400309}
310
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -0400311// Check if can expand the given zone to fulfill an allocation
312static void *
313allocExpandSpace(struct zone_s *zone, u32 size, u32 align
314 , struct allocinfo_s *fill)
Kevin O'Connor0bf92702009-08-01 11:45:37 -0400315{
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -0400316 void *data = allocSpace(zone, size, align, fill);
317 if (data || zone != &ZoneLow)
318 return data;
Kevin O'Connor0bf92702009-08-01 11:45:37 -0400319
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -0400320 // 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;
Kevin O'Connor0bf92702009-08-01 11:45:37 -0400325 }
Kevin O'Connor0bf92702009-08-01 11:45:37 -0400326
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -0400327 zonelow_expand(size, align);
328 return allocSpace(zone, size, align, fill);
Kevin O'Connor0bf92702009-08-01 11:45:37 -0400329}
330
Kevin O'Connor0bf92702009-08-01 11:45:37 -0400331
332/****************************************************************
Kevin O'Connor8f4409b2009-11-25 18:51:46 -0500333 * tracked memory allocations
Kevin O'Connor0bf92702009-08-01 11:45:37 -0400334 ****************************************************************/
335
Kevin O'Connor0bf92702009-08-01 11:45:37 -0400336// Allocate memory from the given zone and track it as a PMM allocation
Kevin O'Connorf9a774c2010-04-17 16:58:32 -0400337void * __malloc
Kevin O'Connor0bf92702009-08-01 11:45:37 -0400338pmm_malloc(struct zone_s *zone, u32 handle, u32 size, u32 align)
339{
Kevin O'Connor1313b782011-07-16 13:39:26 -0400340 ASSERT32FLAT();
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -0400341 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)
Kevin O'Connord948e2b2009-10-12 12:54:56 -0400351 return NULL;
352 }
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -0400353
354 // Find and reserve space for main allocation
355 void *data = allocExpandSpace(zone, size, align, &detail->datainfo);
356 if (!data) {
357 freeSpace(&detail->detailinfo);
Kevin O'Connor0bf92702009-08-01 11:45:37 -0400358 return NULL;
359 }
Kevin O'Connor0bf92702009-08-01 11:45:37 -0400360
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -0400361 dprintf(8, "pmm_malloc zone=%p handle=%x size=%d align=%x"
362 " ret=%p (detail=%p)\n"
363 , zone, handle, size, align
364 , data, detail);
Kevin O'Connor1313b782011-07-16 13:39:26 -0400365 detail->handle = handle;
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -0400366
367 return data;
Kevin O'Connor0bf92702009-08-01 11:45:37 -0400368}
369
370// Free a data block allocated with pmm_malloc
Kevin O'Connord948e2b2009-10-12 12:54:56 -0400371int
Kevin O'Connor0bf92702009-08-01 11:45:37 -0400372pmm_free(void *data)
373{
Kevin O'Connor1313b782011-07-16 13:39:26 -0400374 ASSERT32FLAT();
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -0400375 struct allocinfo_s *info = findAlloc(data);
Kevin O'Connor1313b782011-07-16 13:39:26 -0400376 if (!info || data == (void*)info || data == info->dataend)
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -0400377 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;
Kevin O'Connor0bf92702009-08-01 11:45:37 -0400384}
385
386// Find the amount of free space in a given zone.
387static u32
388pmm_getspace(struct zone_s *zone)
389{
Kevin O'Connorf416fe92009-09-24 20:51:55 -0400390 // XXX - doesn't account for ZoneLow being able to grow.
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -0400391 // XXX - results not reliable when CONFIG_THREAD_OPTIONROMS
392 u32 maxspace = 0;
393 struct allocinfo_s *info;
Kevin O'Connor1313b782011-07-16 13:39:26 -0400394 for (info = zone->info; info; info = info->next) {
395 u32 space = info->allocend - info->dataend;
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -0400396 if (space > maxspace)
397 maxspace = space;
398 }
399
Kevin O'Connord948e2b2009-10-12 12:54:56 -0400400 if (zone != &ZoneTmpHigh && zone != &ZoneTmpLow)
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -0400401 return maxspace;
Kevin O'Connord948e2b2009-10-12 12:54:56 -0400402 // Account for space needed for PMM tracking.
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -0400403 u32 reserve = ALIGN(sizeof(struct allocdetail_s), MALLOC_MIN_ALIGN);
404 if (maxspace <= reserve)
Kevin O'Connor0bf92702009-08-01 11:45:37 -0400405 return 0;
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -0400406 return maxspace - reserve;
Kevin O'Connor0bf92702009-08-01 11:45:37 -0400407}
408
409// Find the data block allocated with pmm_malloc with a given handle.
410static void *
411pmm_find(u32 handle)
412{
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -0400413 int i;
414 for (i=0; i<ARRAY_SIZE(Zones); i++) {
Kevin O'Connor1313b782011-07-16 13:39:26 -0400415 struct zone_s *zone = Zones[i];
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -0400416 struct allocinfo_s *info;
Kevin O'Connor1313b782011-07-16 13:39:26 -0400417 for (info = zone->info; info; info = info->next) {
418 if (info->data != (void*)info)
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -0400419 continue;
420 struct allocdetail_s *detail = container_of(
421 info, struct allocdetail_s, detailinfo);
Kevin O'Connor1313b782011-07-16 13:39:26 -0400422 if (detail->handle == handle)
423 return detail->datainfo.data;
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -0400424 }
Kevin O'Connor8f4409b2009-11-25 18:51:46 -0500425 }
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -0400426 return NULL;
Kevin O'Connor8f4409b2009-11-25 18:51:46 -0500427}
428
Kevin O'Connor0bf92702009-08-01 11:45:37 -0400429
430/****************************************************************
431 * pmm interface
432 ****************************************************************/
Kevin O'Connore54ee382009-07-26 19:33:13 -0400433
434struct 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
444extern struct pmmheader PMMHEADER;
445
446#define PMM_SIGNATURE 0x4d4d5024 // $PMM
447
448#if CONFIG_PMM
449struct pmmheader PMMHEADER __aligned(16) VAR16EXPORT = {
450 .version = 0x01,
451 .length = sizeof(PMMHEADER),
452 .entry_seg = SEG_BIOS,
453};
454#endif
455
Kevin O'Connor0bf92702009-08-01 11:45:37 -0400456#define PMM_FUNCTION_NOT_SUPPORTED 0xffffffff
Kevin O'Connore54ee382009-07-26 19:33:13 -0400457
458// PMM - allocate
459static u32
460handle_pmm00(u16 *args)
461{
462 u32 length = *(u32*)&args[1], handle = *(u32*)&args[3];
463 u16 flags = args[5];
Kevin O'Connor0bf92702009-08-01 11:45:37 -0400464 dprintf(3, "pmm00: length=%x handle=%x flags=%x\n"
Kevin O'Connore54ee382009-07-26 19:33:13 -0400465 , length, handle, flags);
Kevin O'Connor0e9fd612009-08-22 21:31:58 -0400466 struct zone_s *lowzone = &ZoneTmpLow, *highzone = &ZoneTmpHigh;
467 if (flags & 8) {
468 // Permanent memory request.
469 lowzone = &ZoneLow;
470 highzone = &ZoneHigh;
471 }
Kevin O'Connor0bf92702009-08-01 11:45:37 -0400472 if (!length) {
473 // Memory size request
474 switch (flags & 3) {
475 default:
476 case 0:
477 return 0;
478 case 1:
Kevin O'Connor0e9fd612009-08-22 21:31:58 -0400479 return pmm_getspace(lowzone);
Kevin O'Connor0bf92702009-08-01 11:45:37 -0400480 case 2:
Kevin O'Connor0e9fd612009-08-22 21:31:58 -0400481 return pmm_getspace(highzone);
Kevin O'Connor0bf92702009-08-01 11:45:37 -0400482 case 3: {
Kevin O'Connor0e9fd612009-08-22 21:31:58 -0400483 u32 spacelow = pmm_getspace(lowzone);
484 u32 spacehigh = pmm_getspace(highzone);
Kevin O'Connor0bf92702009-08-01 11:45:37 -0400485 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:
Kevin O'Connor0e9fd612009-08-22 21:31:58 -0400505 return (u32)pmm_malloc(lowzone, handle, size, align);
Kevin O'Connor0bf92702009-08-01 11:45:37 -0400506 case 2:
Kevin O'Connor0e9fd612009-08-22 21:31:58 -0400507 return (u32)pmm_malloc(highzone, handle, size, align);
Kevin O'Connor0bf92702009-08-01 11:45:37 -0400508 case 3: {
Kevin O'Connor0e9fd612009-08-22 21:31:58 -0400509 void *data = pmm_malloc(lowzone, handle, size, align);
Kevin O'Connor0bf92702009-08-01 11:45:37 -0400510 if (data)
511 return (u32)data;
Kevin O'Connor0e9fd612009-08-22 21:31:58 -0400512 return (u32)pmm_malloc(highzone, handle, size, align);
Kevin O'Connor0bf92702009-08-01 11:45:37 -0400513 }
514 }
Kevin O'Connore54ee382009-07-26 19:33:13 -0400515}
516
517// PMM - find
518static u32
519handle_pmm01(u16 *args)
520{
521 u32 handle = *(u32*)&args[1];
Kevin O'Connor0bf92702009-08-01 11:45:37 -0400522 dprintf(3, "pmm01: handle=%x\n", handle);
Kevin O'Connord948e2b2009-10-12 12:54:56 -0400523 if (handle == PMM_DEFAULT_HANDLE)
Kevin O'Connor0bf92702009-08-01 11:45:37 -0400524 return 0;
525 return (u32)pmm_find(handle);
Kevin O'Connore54ee382009-07-26 19:33:13 -0400526}
527
528// PMM - deallocate
529static u32
530handle_pmm02(u16 *args)
531{
532 u32 buffer = *(u32*)&args[1];
Kevin O'Connor0bf92702009-08-01 11:45:37 -0400533 dprintf(3, "pmm02: buffer=%x\n", buffer);
534 int ret = pmm_free((void*)buffer);
535 if (ret)
536 // Error
537 return 1;
Kevin O'Connore54ee382009-07-26 19:33:13 -0400538 return 0;
539}
540
541static u32
542handle_pmmXX(u16 *args)
543{
Kevin O'Connor0bf92702009-08-01 11:45:37 -0400544 return PMM_FUNCTION_NOT_SUPPORTED;
Kevin O'Connore54ee382009-07-26 19:33:13 -0400545}
546
Kevin O'Connor533b6282011-07-16 13:13:12 -0400547u32 VISIBLE32INIT
Kevin O'Connore54ee382009-07-26 19:33:13 -0400548handle_pmm(u16 *args)
549{
Kevin O'Connor1313b782011-07-16 13:39:26 -0400550 ASSERT32FLAT();
Kevin O'Connore54ee382009-07-26 19:33:13 -0400551 if (! CONFIG_PMM)
Kevin O'Connor0bf92702009-08-01 11:45:37 -0400552 return PMM_FUNCTION_NOT_SUPPORTED;
Kevin O'Connore54ee382009-07-26 19:33:13 -0400553
554 u16 arg1 = args[0];
555 dprintf(DEBUG_HDL_pmm, "pmm call arg1=%x\n", arg1);
556
Kevin O'Connor533b6282011-07-16 13:13:12 -0400557 int oldpreempt;
558 if (CONFIG_THREAD_OPTIONROMS) {
559 // Not a preemption event - don't wait in wait_preempt()
560 oldpreempt = CanPreempt;
561 CanPreempt = 0;
Kevin O'Connore54ee382009-07-26 19:33:13 -0400562 }
Kevin O'Connor533b6282011-07-16 13:13:12 -0400563
564 u32 ret;
565 switch (arg1) {
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;
570 }
571
572 if (CONFIG_THREAD_OPTIONROMS)
573 CanPreempt = oldpreempt;
574
575 return ret;
Kevin O'Connore54ee382009-07-26 19:33:13 -0400576}
577
578// romlayout.S
Kevin O'Connor1ca05b02010-01-03 17:43:37 -0500579extern void entry_pmm(void);
Kevin O'Connore54ee382009-07-26 19:33:13 -0400580
581void
Kevin O'Connor1ca05b02010-01-03 17:43:37 -0500582pmm_setup(void)
Kevin O'Connore54ee382009-07-26 19:33:13 -0400583{
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
594void
Kevin O'Connor1ca05b02010-01-03 17:43:37 -0500595pmm_finalize(void)
Kevin O'Connore54ee382009-07-26 19:33:13 -0400596{
597 if (! CONFIG_PMM)
598 return;
599
600 dprintf(3, "finalize PMM\n");
601
602 PMMHEADER.signature = 0;
603 PMMHEADER.entry_offset = 0;
Kevin O'Connore54ee382009-07-26 19:33:13 -0400604}