blob: c4156ba6e73374f9f5edddd4b31513630e42c6ba [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'Connor9c985172012-05-12 23:49:33 -040012#include "optionroms.h" // OPTION_ROM_ALIGN
Kevin O'Connor0bf92702009-08-01 11:45:37 -040013
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -040014// Information on a reserved area.
15struct allocinfo_s {
16 struct allocinfo_s *next, **pprev;
17 void *data, *dataend, *allocend;
Kevin O'Connor0bf92702009-08-01 11:45:37 -040018};
19
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -040020// Information on a tracked memory allocation.
21struct allocdetail_s {
22 struct allocinfo_s detailinfo;
23 struct allocinfo_s datainfo;
24 u32 handle;
25};
26
27// The various memory zones.
28struct zone_s {
29 struct allocinfo_s *info;
30};
31
Kevin O'Connor70c94dd2013-03-08 19:39:49 -050032struct zone_s ZoneLow VARVERIFY32INIT, ZoneHigh VARVERIFY32INIT;
33struct zone_s ZoneFSeg VARVERIFY32INIT;
34struct zone_s ZoneTmpLow VARVERIFY32INIT, ZoneTmpHigh VARVERIFY32INIT;
Kevin O'Connor0bf92702009-08-01 11:45:37 -040035
Kevin O'Connor70c94dd2013-03-08 19:39:49 -050036static struct zone_s *Zones[] VARVERIFY32INIT = {
Kevin O'Connor0e9fd612009-08-22 21:31:58 -040037 &ZoneTmpLow, &ZoneLow, &ZoneFSeg, &ZoneTmpHigh, &ZoneHigh
Kevin O'Connor0bf92702009-08-01 11:45:37 -040038};
39
Kevin O'Connorf416fe92009-09-24 20:51:55 -040040
41/****************************************************************
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -040042 * low-level memory reservations
43 ****************************************************************/
44
45// Find and reserve space from a given zone
46static void *
47allocSpace(struct zone_s *zone, u32 size, u32 align, struct allocinfo_s *fill)
48{
49 struct allocinfo_s *info;
Kevin O'Connor1313b782011-07-16 13:39:26 -040050 for (info = zone->info; info; info = info->next) {
51 void *dataend = info->dataend;
52 void *allocend = info->allocend;
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -040053 void *newallocend = (void*)ALIGN_DOWN((u32)allocend - size, align);
54 if (newallocend >= dataend && newallocend <= allocend) {
55 // Found space - now reserve it.
Kevin O'Connor1313b782011-07-16 13:39:26 -040056 struct allocinfo_s **pprev = info->pprev;
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -040057 if (!fill)
58 fill = newallocend;
Kevin O'Connor1313b782011-07-16 13:39:26 -040059 fill->next = info;
60 fill->pprev = pprev;
61 fill->data = newallocend;
62 fill->dataend = newallocend + size;
63 fill->allocend = allocend;
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -040064
Kevin O'Connor1313b782011-07-16 13:39:26 -040065 info->allocend = newallocend;
66 info->pprev = &fill->next;
67 *pprev = fill;
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -040068 return newallocend;
69 }
70 }
71 return NULL;
72}
73
74// Release space allocated with allocSpace()
75static void
76freeSpace(struct allocinfo_s *info)
77{
Kevin O'Connor1313b782011-07-16 13:39:26 -040078 struct allocinfo_s *next = info->next;
79 struct allocinfo_s **pprev = info->pprev;
80 *pprev = next;
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -040081 if (next) {
Kevin O'Connor1313b782011-07-16 13:39:26 -040082 if (next->allocend == info->data)
83 next->allocend = info->allocend;
84 next->pprev = pprev;
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -040085 }
86}
87
88// Add new memory to a zone
89static void
90addSpace(struct zone_s *zone, void *start, void *end)
91{
92 // Find position to add space
93 struct allocinfo_s **pprev = &zone->info, *info;
94 for (;;) {
Kevin O'Connor1313b782011-07-16 13:39:26 -040095 info = *pprev;
96 if (!info || info->data < start)
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -040097 break;
98 pprev = &info->next;
99 }
100
101 // Add space using temporary allocation info.
102 struct allocdetail_s tempdetail;
103 tempdetail.datainfo.next = info;
104 tempdetail.datainfo.pprev = pprev;
105 tempdetail.datainfo.data = tempdetail.datainfo.dataend = start;
106 tempdetail.datainfo.allocend = end;
Kevin O'Connor1313b782011-07-16 13:39:26 -0400107 *pprev = &tempdetail.datainfo;
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -0400108 if (info)
Kevin O'Connor1313b782011-07-16 13:39:26 -0400109 info->pprev = &tempdetail.datainfo.next;
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -0400110
111 // Allocate final allocation info.
112 struct allocdetail_s *detail = allocSpace(
113 &ZoneTmpHigh, sizeof(*detail), MALLOC_MIN_ALIGN, NULL);
114 if (!detail) {
115 detail = allocSpace(&ZoneTmpLow, sizeof(*detail)
116 , MALLOC_MIN_ALIGN, NULL);
117 if (!detail) {
Kevin O'Connor1313b782011-07-16 13:39:26 -0400118 *tempdetail.datainfo.pprev = tempdetail.datainfo.next;
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -0400119 if (tempdetail.datainfo.next)
Kevin O'Connor1313b782011-07-16 13:39:26 -0400120 tempdetail.datainfo.next->pprev = tempdetail.datainfo.pprev;
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -0400121 warn_noalloc();
122 return;
123 }
124 }
125
126 // Replace temp alloc space with final alloc space
Kevin O'Connor1313b782011-07-16 13:39:26 -0400127 memcpy(&detail->datainfo, &tempdetail.datainfo, sizeof(detail->datainfo));
128 detail->handle = PMM_DEFAULT_HANDLE;
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -0400129
Kevin O'Connor1313b782011-07-16 13:39:26 -0400130 *tempdetail.datainfo.pprev = &detail->datainfo;
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -0400131 if (tempdetail.datainfo.next)
Kevin O'Connor1313b782011-07-16 13:39:26 -0400132 tempdetail.datainfo.next->pprev = &detail->datainfo.next;
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -0400133}
134
135// Search all zones for an allocation obtained from allocSpace()
136static struct allocinfo_s *
137findAlloc(void *data)
138{
139 int i;
140 for (i=0; i<ARRAY_SIZE(Zones); i++) {
Kevin O'Connor1313b782011-07-16 13:39:26 -0400141 struct zone_s *zone = Zones[i];
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -0400142 struct allocinfo_s *info;
Kevin O'Connor1313b782011-07-16 13:39:26 -0400143 for (info = zone->info; info; info = info->next)
144 if (info->data == data)
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -0400145 return info;
146 }
147 return NULL;
148}
149
150// Return the last sentinal node of a zone
151static struct allocinfo_s *
152findLast(struct zone_s *zone)
153{
Kevin O'Connor1313b782011-07-16 13:39:26 -0400154 struct allocinfo_s *info = zone->info;
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -0400155 if (!info)
156 return NULL;
157 for (;;) {
Kevin O'Connor1313b782011-07-16 13:39:26 -0400158 struct allocinfo_s *next = info->next;
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -0400159 if (!next)
160 return info;
161 info = next;
162 }
163}
164
165
166/****************************************************************
Kevin O'Connor5e019082012-05-20 21:11:43 -0400167 * 0xc0000-0xf0000 management
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -0400168 ****************************************************************/
169
Kevin O'Connor5e019082012-05-20 21:11:43 -0400170static u32 RomEnd = BUILD_ROM_START;
171static struct allocinfo_s *RomBase;
172
173#define OPROM_HEADER_RESERVE 16
174
Kevin O'Connor3733f6f2013-02-17 12:44:23 -0500175// Return the maximum memory position option roms may use.
Kevin O'Connor5e019082012-05-20 21:11:43 -0400176u32
Kevin O'Connor3733f6f2013-02-17 12:44:23 -0500177rom_get_max(void)
Kevin O'Connor5e019082012-05-20 21:11:43 -0400178{
179 return ALIGN_DOWN((u32)RomBase->allocend - OPROM_HEADER_RESERVE
180 , OPTION_ROM_ALIGN);
181}
182
Kevin O'Connor3733f6f2013-02-17 12:44:23 -0500183// Return the end of the last deployed option rom.
Kevin O'Connor5e019082012-05-20 21:11:43 -0400184u32
185rom_get_last(void)
186{
187 return RomEnd;
188}
189
190// Request space for an optionrom in 0xc0000-0xf0000 area.
191struct rom_header *
192rom_reserve(u32 size)
193{
194 u32 newend = ALIGN(RomEnd + size, OPTION_ROM_ALIGN) + OPROM_HEADER_RESERVE;
195 if (newend > (u32)RomBase->allocend)
196 return NULL;
Kevin O'Connorc9243442013-02-17 13:58:28 -0500197 if (newend < (u32)zonelow_base + OPROM_HEADER_RESERVE)
198 newend = (u32)zonelow_base + OPROM_HEADER_RESERVE;
Kevin O'Connor5e019082012-05-20 21:11:43 -0400199 RomBase->data = RomBase->dataend = (void*)newend;
200 return (void*)RomEnd;
201}
202
203// Confirm space as in use by an optionrom.
204int
205rom_confirm(u32 size)
206{
207 void *new = rom_reserve(size);
208 if (!new) {
209 warn_noalloc();
210 return -1;
211 }
212 RomEnd = ALIGN(RomEnd + size, OPTION_ROM_ALIGN);
213 return 0;
214}
215
216
217/****************************************************************
218 * Setup
219 ****************************************************************/
Kevin O'Connor9c985172012-05-12 23:49:33 -0400220
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -0400221void
Kevin O'Connord83c87b2013-01-21 01:14:12 -0500222malloc_preinit(void)
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -0400223{
224 ASSERT32FLAT();
Kevin O'Connor6afc6f82013-02-19 01:02:50 -0500225 dprintf(3, "malloc preinit\n");
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -0400226
Kevin O'Connor6ca04602013-01-21 11:38:49 -0500227 // Don't declare any memory between 0xa0000 and 0x100000
228 add_e820(BUILD_LOWRAM_END, BUILD_BIOS_ADDR-BUILD_LOWRAM_END, E820_HOLE);
229
230 // Mark known areas as reserved.
231 add_e820(BUILD_BIOS_ADDR, BUILD_BIOS_SIZE, E820_RESERVED);
232
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -0400233 // Populate temp high ram
234 u32 highram = 0;
235 int i;
236 for (i=e820_count-1; i>=0; i--) {
237 struct e820entry *en = &e820_list[i];
238 u64 end = en->start + en->size;
239 if (end < 1024*1024)
240 break;
241 if (en->type != E820_RAM || end > 0xffffffff)
242 continue;
243 u32 s = en->start, e = end;
244 if (!highram) {
Kevin O'Connore52ad392013-02-20 23:48:22 -0500245 u32 newe = ALIGN_DOWN(e - BUILD_MAX_HIGHTABLE, MALLOC_MIN_ALIGN);
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -0400246 if (newe <= e && newe >= s) {
247 highram = newe;
248 e = newe;
249 }
250 }
251 addSpace(&ZoneTmpHigh, (void*)s, (void*)e);
252 }
253
Kevin O'Connor0b314ab2013-02-19 01:15:36 -0500254 // Populate regions
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -0400255 addSpace(&ZoneTmpLow, (void*)BUILD_STACK_ADDR, (void*)BUILD_EBDA_MINIMUM);
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -0400256 if (highram) {
257 addSpace(&ZoneHigh, (void*)highram
Kevin O'Connore52ad392013-02-20 23:48:22 -0500258 , (void*)highram + BUILD_MAX_HIGHTABLE);
259 add_e820(highram, BUILD_MAX_HIGHTABLE, E820_RESERVED);
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -0400260 }
261}
262
David Woodhouse118469a2013-01-25 19:46:25 -0600263void
264csm_malloc_preinit(u32 low_pmm, u32 low_pmm_size, u32 hi_pmm, u32 hi_pmm_size)
265{
266 ASSERT32FLAT();
267
Kevin O'Connore52ad392013-02-20 23:48:22 -0500268 if (hi_pmm_size > BUILD_MAX_HIGHTABLE) {
David Woodhouse118469a2013-01-25 19:46:25 -0600269 void *hi_pmm_end = (void *)hi_pmm + hi_pmm_size;
Kevin O'Connore52ad392013-02-20 23:48:22 -0500270 addSpace(&ZoneTmpHigh, (void *)hi_pmm, hi_pmm_end - BUILD_MAX_HIGHTABLE);
271 addSpace(&ZoneHigh, hi_pmm_end - BUILD_MAX_HIGHTABLE, hi_pmm_end);
David Woodhouse118469a2013-01-25 19:46:25 -0600272 } else {
273 addSpace(&ZoneTmpHigh, (void *)hi_pmm, (void *)hi_pmm + hi_pmm_size);
274 }
275 addSpace(&ZoneTmpLow, (void *)low_pmm, (void *)low_pmm + low_pmm_size);
David Woodhouse118469a2013-01-25 19:46:25 -0600276}
277
Kevin O'Connorf85e4bc2013-02-19 01:33:45 -0500278u32 LegacyRamSize VARFSEG;
279
280// Calculate the maximum ramsize (less than 4gig) from e820 map.
281static void
282calcRamSize(void)
283{
284 u32 rs = 0;
285 int i;
286 for (i=e820_count-1; i>=0; i--) {
287 struct e820entry *en = &e820_list[i];
288 u64 end = en->start + en->size;
289 u32 type = en->type;
290 if (end <= 0xffffffff && (type == E820_ACPI || type == E820_RAM)) {
291 rs = end;
292 break;
293 }
294 }
295 LegacyRamSize = rs >= 1024*1024 ? rs : 1024*1024;
296}
297
Kevin O'Connor533b6282011-07-16 13:13:12 -0400298// Update pointers after code relocation.
299void
Kevin O'Connor6afc6f82013-02-19 01:02:50 -0500300malloc_init(void)
Kevin O'Connor533b6282011-07-16 13:13:12 -0400301{
302 ASSERT32FLAT();
Kevin O'Connor6afc6f82013-02-19 01:02:50 -0500303 dprintf(3, "malloc init\n");
Kevin O'Connor533b6282011-07-16 13:13:12 -0400304
Kevin O'Connor6afc6f82013-02-19 01:02:50 -0500305 if (CONFIG_RELOCATE_INIT) {
306 // Fixup malloc pointers after relocation
307 int i;
308 for (i=0; i<ARRAY_SIZE(Zones); i++) {
309 struct zone_s *zone = Zones[i];
310 if (zone->info)
311 zone->info->pprev = &zone->info;
312 }
Kevin O'Connor533b6282011-07-16 13:13:12 -0400313 }
Kevin O'Connor8b9137d2011-08-03 20:15:26 -0400314
Kevin O'Connor0b314ab2013-02-19 01:15:36 -0500315 // Initialize low-memory region
Kevin O'Connorc9243442013-02-17 13:58:28 -0500316 extern u8 varlow_start[], varlow_end[], final_varlow_start[];
317 memmove(final_varlow_start, varlow_start, varlow_end - varlow_start);
Kevin O'Connor0b314ab2013-02-19 01:15:36 -0500318 addSpace(&ZoneLow, zonelow_base + OPROM_HEADER_RESERVE, final_varlow_start);
319 RomBase = findLast(&ZoneLow);
Kevin O'Connor13fd0a52013-02-07 00:06:18 -0500320
Kevin O'Connor6afc6f82013-02-19 01:02:50 -0500321 // Add space available in f-segment to ZoneFSeg
Kevin O'Connor6d152642013-02-19 21:35:20 -0500322 extern u8 zonefseg_start[], zonefseg_end[];
323 memset(zonefseg_start, 0, zonefseg_end - zonefseg_start);
324 addSpace(&ZoneFSeg, zonefseg_start, zonefseg_end);
Kevin O'Connorf85e4bc2013-02-19 01:33:45 -0500325
326 calcRamSize();
Kevin O'Connor533b6282011-07-16 13:13:12 -0400327}
328
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -0400329void
Kevin O'Connord83c87b2013-01-21 01:14:12 -0500330malloc_prepboot(void)
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -0400331{
Kevin O'Connor1313b782011-07-16 13:39:26 -0400332 ASSERT32FLAT();
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -0400333 dprintf(3, "malloc finalize\n");
334
Kevin O'Connor9c985172012-05-12 23:49:33 -0400335 // Place an optionrom signature around used low mem area.
Kevin O'Connor3733f6f2013-02-17 12:44:23 -0500336 u32 base = rom_get_max();
Kevin O'Connor5e019082012-05-20 21:11:43 -0400337 struct rom_header *dummyrom = (void*)base;
338 dummyrom->signature = OPTION_ROM_SIGNATURE;
Kevin O'Connor33d51182012-05-23 23:58:11 -0400339 int size = (BUILD_BIOS_ADDR - base) / 512;
340 dummyrom->size = (size > 255) ? 255 : size;
Kevin O'Connor9c985172012-05-12 23:49:33 -0400341 memset((void*)RomEnd, 0, base-RomEnd);
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -0400342
Kevin O'Connor65802ad2012-07-21 11:56:09 -0400343 // Clear unused f-seg ram.
344 struct allocinfo_s *info = findLast(&ZoneFSeg);
345 memset(info->dataend, 0, info->allocend - info->dataend);
Kevin O'Connor89efc932013-02-17 12:46:53 -0500346 dprintf(1, "Space available for UMB: %x-%x, %x-%x\n"
347 , RomEnd, base, (u32)info->dataend, (u32)info->allocend);
Kevin O'Connor65802ad2012-07-21 11:56:09 -0400348
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -0400349 // Give back unused high ram.
Kevin O'Connor9c985172012-05-12 23:49:33 -0400350 info = findLast(&ZoneHigh);
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -0400351 if (info) {
352 u32 giveback = ALIGN_DOWN(info->allocend - info->dataend, PAGE_SIZE);
353 add_e820((u32)info->dataend, giveback, E820_RAM);
354 dprintf(1, "Returned %d bytes of ZoneHigh\n", giveback);
355 }
Kevin O'Connorf85e4bc2013-02-19 01:33:45 -0500356
357 calcRamSize();
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -0400358}
359
360
361/****************************************************************
Kevin O'Connor8f4409b2009-11-25 18:51:46 -0500362 * tracked memory allocations
Kevin O'Connor0bf92702009-08-01 11:45:37 -0400363 ****************************************************************/
364
Kevin O'Connor0bf92702009-08-01 11:45:37 -0400365// Allocate memory from the given zone and track it as a PMM allocation
Kevin O'Connorf9a774c2010-04-17 16:58:32 -0400366void * __malloc
Kevin O'Connor0bf92702009-08-01 11:45:37 -0400367pmm_malloc(struct zone_s *zone, u32 handle, u32 size, u32 align)
368{
Kevin O'Connor1313b782011-07-16 13:39:26 -0400369 ASSERT32FLAT();
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -0400370 if (!size)
371 return NULL;
372
373 // Find and reserve space for bookkeeping.
374 struct allocdetail_s *detail = allocSpace(
375 &ZoneTmpHigh, sizeof(*detail), MALLOC_MIN_ALIGN, NULL);
376 if (!detail) {
377 detail = allocSpace(&ZoneTmpLow, sizeof(*detail)
378 , MALLOC_MIN_ALIGN, NULL);
379 if (!detail)
Kevin O'Connord948e2b2009-10-12 12:54:56 -0400380 return NULL;
381 }
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -0400382
383 // Find and reserve space for main allocation
Kevin O'Connor9c985172012-05-12 23:49:33 -0400384 void *data = allocSpace(zone, size, align, &detail->datainfo);
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -0400385 if (!data) {
386 freeSpace(&detail->detailinfo);
Kevin O'Connor0bf92702009-08-01 11:45:37 -0400387 return NULL;
388 }
Kevin O'Connor0bf92702009-08-01 11:45:37 -0400389
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -0400390 dprintf(8, "pmm_malloc zone=%p handle=%x size=%d align=%x"
391 " ret=%p (detail=%p)\n"
392 , zone, handle, size, align
393 , data, detail);
Kevin O'Connor1313b782011-07-16 13:39:26 -0400394 detail->handle = handle;
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -0400395
396 return data;
Kevin O'Connor0bf92702009-08-01 11:45:37 -0400397}
398
399// Free a data block allocated with pmm_malloc
Kevin O'Connord948e2b2009-10-12 12:54:56 -0400400int
Kevin O'Connor0bf92702009-08-01 11:45:37 -0400401pmm_free(void *data)
402{
Kevin O'Connor1313b782011-07-16 13:39:26 -0400403 ASSERT32FLAT();
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -0400404 struct allocinfo_s *info = findAlloc(data);
Kevin O'Connor1313b782011-07-16 13:39:26 -0400405 if (!info || data == (void*)info || data == info->dataend)
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -0400406 return -1;
407 struct allocdetail_s *detail = container_of(
408 info, struct allocdetail_s, datainfo);
409 dprintf(8, "pmm_free %p (detail=%p)\n", data, detail);
410 freeSpace(info);
411 freeSpace(&detail->detailinfo);
412 return 0;
Kevin O'Connor0bf92702009-08-01 11:45:37 -0400413}
414
415// Find the amount of free space in a given zone.
416static u32
417pmm_getspace(struct zone_s *zone)
418{
Kevin O'Connorf416fe92009-09-24 20:51:55 -0400419 // XXX - doesn't account for ZoneLow being able to grow.
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -0400420 // XXX - results not reliable when CONFIG_THREAD_OPTIONROMS
421 u32 maxspace = 0;
422 struct allocinfo_s *info;
Kevin O'Connor1313b782011-07-16 13:39:26 -0400423 for (info = zone->info; info; info = info->next) {
424 u32 space = info->allocend - info->dataend;
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -0400425 if (space > maxspace)
426 maxspace = space;
427 }
428
Kevin O'Connord948e2b2009-10-12 12:54:56 -0400429 if (zone != &ZoneTmpHigh && zone != &ZoneTmpLow)
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -0400430 return maxspace;
Kevin O'Connord948e2b2009-10-12 12:54:56 -0400431 // Account for space needed for PMM tracking.
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -0400432 u32 reserve = ALIGN(sizeof(struct allocdetail_s), MALLOC_MIN_ALIGN);
433 if (maxspace <= reserve)
Kevin O'Connor0bf92702009-08-01 11:45:37 -0400434 return 0;
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -0400435 return maxspace - reserve;
Kevin O'Connor0bf92702009-08-01 11:45:37 -0400436}
437
438// Find the data block allocated with pmm_malloc with a given handle.
439static void *
440pmm_find(u32 handle)
441{
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -0400442 int i;
443 for (i=0; i<ARRAY_SIZE(Zones); i++) {
Kevin O'Connor1313b782011-07-16 13:39:26 -0400444 struct zone_s *zone = Zones[i];
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -0400445 struct allocinfo_s *info;
Kevin O'Connor1313b782011-07-16 13:39:26 -0400446 for (info = zone->info; info; info = info->next) {
447 if (info->data != (void*)info)
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -0400448 continue;
449 struct allocdetail_s *detail = container_of(
450 info, struct allocdetail_s, detailinfo);
Kevin O'Connor1313b782011-07-16 13:39:26 -0400451 if (detail->handle == handle)
452 return detail->datainfo.data;
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -0400453 }
Kevin O'Connor8f4409b2009-11-25 18:51:46 -0500454 }
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -0400455 return NULL;
Kevin O'Connor8f4409b2009-11-25 18:51:46 -0500456}
457
Kevin O'Connor0bf92702009-08-01 11:45:37 -0400458
459/****************************************************************
460 * pmm interface
461 ****************************************************************/
Kevin O'Connore54ee382009-07-26 19:33:13 -0400462
463struct pmmheader {
464 u32 signature;
465 u8 version;
466 u8 length;
467 u8 checksum;
Kevin O'Connor6156afe2013-01-20 10:43:54 -0500468 struct segoff_s entry;
Kevin O'Connore54ee382009-07-26 19:33:13 -0400469 u8 reserved[5];
470} PACKED;
471
472extern struct pmmheader PMMHEADER;
473
474#define PMM_SIGNATURE 0x4d4d5024 // $PMM
475
476#if CONFIG_PMM
Kevin O'Connor89a2f962013-02-18 23:36:03 -0500477struct pmmheader PMMHEADER __aligned(16) VARFSEG = {
Kevin O'Connor6156afe2013-01-20 10:43:54 -0500478 .signature = PMM_SIGNATURE,
Kevin O'Connore54ee382009-07-26 19:33:13 -0400479 .version = 0x01,
480 .length = sizeof(PMMHEADER),
Kevin O'Connore54ee382009-07-26 19:33:13 -0400481};
482#endif
483
Kevin O'Connor0bf92702009-08-01 11:45:37 -0400484#define PMM_FUNCTION_NOT_SUPPORTED 0xffffffff
Kevin O'Connore54ee382009-07-26 19:33:13 -0400485
486// PMM - allocate
487static u32
488handle_pmm00(u16 *args)
489{
490 u32 length = *(u32*)&args[1], handle = *(u32*)&args[3];
491 u16 flags = args[5];
Kevin O'Connor0bf92702009-08-01 11:45:37 -0400492 dprintf(3, "pmm00: length=%x handle=%x flags=%x\n"
Kevin O'Connore54ee382009-07-26 19:33:13 -0400493 , length, handle, flags);
Kevin O'Connor0e9fd612009-08-22 21:31:58 -0400494 struct zone_s *lowzone = &ZoneTmpLow, *highzone = &ZoneTmpHigh;
495 if (flags & 8) {
496 // Permanent memory request.
497 lowzone = &ZoneLow;
498 highzone = &ZoneHigh;
499 }
Kevin O'Connor0bf92702009-08-01 11:45:37 -0400500 if (!length) {
501 // Memory size request
502 switch (flags & 3) {
503 default:
504 case 0:
505 return 0;
506 case 1:
Kevin O'Connor0e9fd612009-08-22 21:31:58 -0400507 return pmm_getspace(lowzone);
Kevin O'Connor0bf92702009-08-01 11:45:37 -0400508 case 2:
Kevin O'Connor0e9fd612009-08-22 21:31:58 -0400509 return pmm_getspace(highzone);
Kevin O'Connor0bf92702009-08-01 11:45:37 -0400510 case 3: {
Kevin O'Connor0e9fd612009-08-22 21:31:58 -0400511 u32 spacelow = pmm_getspace(lowzone);
512 u32 spacehigh = pmm_getspace(highzone);
Kevin O'Connor0bf92702009-08-01 11:45:37 -0400513 if (spacelow > spacehigh)
514 return spacelow;
515 return spacehigh;
516 }
517 }
518 }
519 u32 size = length * 16;
520 if ((s32)size <= 0)
521 return 0;
522 u32 align = MALLOC_MIN_ALIGN;
523 if (flags & 4) {
524 align = 1<<__ffs(size);
525 if (align < MALLOC_MIN_ALIGN)
526 align = MALLOC_MIN_ALIGN;
527 }
528 switch (flags & 3) {
529 default:
530 case 0:
531 return 0;
532 case 1:
Kevin O'Connor0e9fd612009-08-22 21:31:58 -0400533 return (u32)pmm_malloc(lowzone, handle, size, align);
Kevin O'Connor0bf92702009-08-01 11:45:37 -0400534 case 2:
Kevin O'Connor0e9fd612009-08-22 21:31:58 -0400535 return (u32)pmm_malloc(highzone, handle, size, align);
Kevin O'Connor0bf92702009-08-01 11:45:37 -0400536 case 3: {
Kevin O'Connor0e9fd612009-08-22 21:31:58 -0400537 void *data = pmm_malloc(lowzone, handle, size, align);
Kevin O'Connor0bf92702009-08-01 11:45:37 -0400538 if (data)
539 return (u32)data;
Kevin O'Connor0e9fd612009-08-22 21:31:58 -0400540 return (u32)pmm_malloc(highzone, handle, size, align);
Kevin O'Connor0bf92702009-08-01 11:45:37 -0400541 }
542 }
Kevin O'Connore54ee382009-07-26 19:33:13 -0400543}
544
545// PMM - find
546static u32
547handle_pmm01(u16 *args)
548{
549 u32 handle = *(u32*)&args[1];
Kevin O'Connor0bf92702009-08-01 11:45:37 -0400550 dprintf(3, "pmm01: handle=%x\n", handle);
Kevin O'Connord948e2b2009-10-12 12:54:56 -0400551 if (handle == PMM_DEFAULT_HANDLE)
Kevin O'Connor0bf92702009-08-01 11:45:37 -0400552 return 0;
553 return (u32)pmm_find(handle);
Kevin O'Connore54ee382009-07-26 19:33:13 -0400554}
555
556// PMM - deallocate
557static u32
558handle_pmm02(u16 *args)
559{
560 u32 buffer = *(u32*)&args[1];
Kevin O'Connor0bf92702009-08-01 11:45:37 -0400561 dprintf(3, "pmm02: buffer=%x\n", buffer);
562 int ret = pmm_free((void*)buffer);
563 if (ret)
564 // Error
565 return 1;
Kevin O'Connore54ee382009-07-26 19:33:13 -0400566 return 0;
567}
568
569static u32
570handle_pmmXX(u16 *args)
571{
Kevin O'Connor0bf92702009-08-01 11:45:37 -0400572 return PMM_FUNCTION_NOT_SUPPORTED;
Kevin O'Connore54ee382009-07-26 19:33:13 -0400573}
574
Kevin O'Connor533b6282011-07-16 13:13:12 -0400575u32 VISIBLE32INIT
Kevin O'Connore54ee382009-07-26 19:33:13 -0400576handle_pmm(u16 *args)
577{
Kevin O'Connor1313b782011-07-16 13:39:26 -0400578 ASSERT32FLAT();
Kevin O'Connore54ee382009-07-26 19:33:13 -0400579 if (! CONFIG_PMM)
Kevin O'Connor0bf92702009-08-01 11:45:37 -0400580 return PMM_FUNCTION_NOT_SUPPORTED;
Kevin O'Connore54ee382009-07-26 19:33:13 -0400581
582 u16 arg1 = args[0];
583 dprintf(DEBUG_HDL_pmm, "pmm call arg1=%x\n", arg1);
584
Kevin O'Connor533b6282011-07-16 13:13:12 -0400585 u32 ret;
586 switch (arg1) {
587 case 0x00: ret = handle_pmm00(args); break;
588 case 0x01: ret = handle_pmm01(args); break;
589 case 0x02: ret = handle_pmm02(args); break;
590 default: ret = handle_pmmXX(args); break;
591 }
592
Kevin O'Connor533b6282011-07-16 13:13:12 -0400593 return ret;
Kevin O'Connore54ee382009-07-26 19:33:13 -0400594}
595
Kevin O'Connore54ee382009-07-26 19:33:13 -0400596void
Kevin O'Connord83c87b2013-01-21 01:14:12 -0500597pmm_init(void)
Kevin O'Connore54ee382009-07-26 19:33:13 -0400598{
599 if (! CONFIG_PMM)
600 return;
601
602 dprintf(3, "init PMM\n");
603
Kevin O'Connor6156afe2013-01-20 10:43:54 -0500604 PMMHEADER.entry = FUNC16(entry_pmm);
Kevin O'Connore54ee382009-07-26 19:33:13 -0400605 PMMHEADER.checksum -= checksum(&PMMHEADER, sizeof(PMMHEADER));
606}
607
608void
Kevin O'Connord83c87b2013-01-21 01:14:12 -0500609pmm_prepboot(void)
Kevin O'Connore54ee382009-07-26 19:33:13 -0400610{
611 if (! CONFIG_PMM)
612 return;
613
614 dprintf(3, "finalize PMM\n");
615
616 PMMHEADER.signature = 0;
Kevin O'Connor6156afe2013-01-20 10:43:54 -0500617 PMMHEADER.entry.segoff = 0;
Kevin O'Connore54ee382009-07-26 19:33:13 -0400618}