blob: cd6ee892975c4f099141970579086237314545de [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
Kevin O'Connorfa9c66a2013-09-14 19:10:40 -04007#include "util.h" // dprintf
Kevin O'Connore54ee382009-07-26 19:33:13 -04008#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'Connore097a752013-06-08 21:49:12 -040013#include "list.h" // hlist_node
Kevin O'Connor3df600b2013-09-14 19:28:55 -040014#include "stacks.h" // wait_preempt
Kevin O'Connorfa9c66a2013-09-14 19:10:40 -040015#include "string.h" // memset
Kevin O'Connorb9c6a962013-09-14 13:01:30 -040016#include "x86.h" // __ffs
Kevin O'Connor0bf92702009-08-01 11:45:37 -040017
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -040018// Information on a reserved area.
19struct allocinfo_s {
Kevin O'Connore097a752013-06-08 21:49:12 -040020 struct hlist_node node;
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -040021 void *data, *dataend, *allocend;
Kevin O'Connor0bf92702009-08-01 11:45:37 -040022};
23
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -040024// Information on a tracked memory allocation.
25struct allocdetail_s {
26 struct allocinfo_s detailinfo;
27 struct allocinfo_s datainfo;
28 u32 handle;
29};
30
31// The various memory zones.
32struct zone_s {
Kevin O'Connore097a752013-06-08 21:49:12 -040033 struct hlist_head head;
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -040034};
35
Kevin O'Connor70c94dd2013-03-08 19:39:49 -050036struct zone_s ZoneLow VARVERIFY32INIT, ZoneHigh VARVERIFY32INIT;
37struct zone_s ZoneFSeg VARVERIFY32INIT;
38struct zone_s ZoneTmpLow VARVERIFY32INIT, ZoneTmpHigh VARVERIFY32INIT;
Kevin O'Connor0bf92702009-08-01 11:45:37 -040039
Kevin O'Connor70c94dd2013-03-08 19:39:49 -050040static struct zone_s *Zones[] VARVERIFY32INIT = {
Kevin O'Connor0e9fd612009-08-22 21:31:58 -040041 &ZoneTmpLow, &ZoneLow, &ZoneFSeg, &ZoneTmpHigh, &ZoneHigh
Kevin O'Connor0bf92702009-08-01 11:45:37 -040042};
43
Kevin O'Connorf416fe92009-09-24 20:51:55 -040044
45/****************************************************************
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -040046 * low-level memory reservations
47 ****************************************************************/
48
49// Find and reserve space from a given zone
50static void *
51allocSpace(struct zone_s *zone, u32 size, u32 align, struct allocinfo_s *fill)
52{
53 struct allocinfo_s *info;
Kevin O'Connore097a752013-06-08 21:49:12 -040054 hlist_for_each_entry(info, &zone->head, node) {
Kevin O'Connor1313b782011-07-16 13:39:26 -040055 void *dataend = info->dataend;
56 void *allocend = info->allocend;
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -040057 void *newallocend = (void*)ALIGN_DOWN((u32)allocend - size, align);
58 if (newallocend >= dataend && newallocend <= allocend) {
59 // Found space - now reserve it.
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -040060 if (!fill)
61 fill = newallocend;
Kevin O'Connor1313b782011-07-16 13:39:26 -040062 fill->data = newallocend;
63 fill->dataend = newallocend + size;
64 fill->allocend = allocend;
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -040065
Kevin O'Connor1313b782011-07-16 13:39:26 -040066 info->allocend = newallocend;
Kevin O'Connore097a752013-06-08 21:49:12 -040067 hlist_add_before(&fill->node, &info->node);
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'Connore097a752013-06-08 21:49:12 -040078 struct allocinfo_s *next = container_of_or_null(
79 info->node.next, struct allocinfo_s, node);
80 if (next && next->allocend == info->data)
81 next->allocend = info->allocend;
82 hlist_del(&info->node);
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -040083}
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
Kevin O'Connore097a752013-06-08 21:49:12 -040090 struct allocinfo_s *info;
91 struct hlist_node **pprev;
Kevin O'Connor030a58a2013-06-13 21:24:14 -040092 hlist_for_each_entry_pprev(info, pprev, &zone->head, node) {
Kevin O'Connore097a752013-06-08 21:49:12 -040093 if (info->data < start)
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -040094 break;
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -040095 }
96
97 // Add space using temporary allocation info.
98 struct allocdetail_s tempdetail;
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -040099 tempdetail.datainfo.data = tempdetail.datainfo.dataend = start;
100 tempdetail.datainfo.allocend = end;
Kevin O'Connore097a752013-06-08 21:49:12 -0400101 hlist_add(&tempdetail.datainfo.node, pprev);
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -0400102
103 // Allocate final allocation info.
104 struct allocdetail_s *detail = allocSpace(
105 &ZoneTmpHigh, sizeof(*detail), MALLOC_MIN_ALIGN, NULL);
106 if (!detail) {
107 detail = allocSpace(&ZoneTmpLow, sizeof(*detail)
108 , MALLOC_MIN_ALIGN, NULL);
109 if (!detail) {
Kevin O'Connore097a752013-06-08 21:49:12 -0400110 hlist_del(&tempdetail.datainfo.node);
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -0400111 warn_noalloc();
112 return;
113 }
114 }
115
116 // Replace temp alloc space with final alloc space
Kevin O'Connore097a752013-06-08 21:49:12 -0400117 pprev = tempdetail.datainfo.node.pprev;
118 hlist_del(&tempdetail.datainfo.node);
Kevin O'Connor1313b782011-07-16 13:39:26 -0400119 memcpy(&detail->datainfo, &tempdetail.datainfo, sizeof(detail->datainfo));
120 detail->handle = PMM_DEFAULT_HANDLE;
Kevin O'Connore097a752013-06-08 21:49:12 -0400121 hlist_add(&detail->datainfo.node, pprev);
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -0400122}
123
124// Search all zones for an allocation obtained from allocSpace()
125static struct allocinfo_s *
126findAlloc(void *data)
127{
128 int i;
129 for (i=0; i<ARRAY_SIZE(Zones); i++) {
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -0400130 struct allocinfo_s *info;
Kevin O'Connore097a752013-06-08 21:49:12 -0400131 hlist_for_each_entry(info, &Zones[i]->head, node) {
Kevin O'Connor1313b782011-07-16 13:39:26 -0400132 if (info->data == data)
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -0400133 return info;
Kevin O'Connore097a752013-06-08 21:49:12 -0400134 }
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -0400135 }
136 return NULL;
137}
138
139// Return the last sentinal node of a zone
140static struct allocinfo_s *
141findLast(struct zone_s *zone)
142{
Kevin O'Connore097a752013-06-08 21:49:12 -0400143 struct allocinfo_s *info, *last = NULL;
144 hlist_for_each_entry(info, &zone->head, node) {
145 last = info;
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -0400146 }
Kevin O'Connore097a752013-06-08 21:49:12 -0400147 return last;
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -0400148}
149
150
151/****************************************************************
Kevin O'Connor2b0fb8c2013-08-07 23:03:47 -0400152 * ebda movement
153 ****************************************************************/
154
155// Move ebda
156static int
157relocate_ebda(u32 newebda, u32 oldebda, u8 ebda_size)
158{
159 u32 lowram = GET_BDA(mem_size_kb) * 1024;
160 if (oldebda != lowram)
161 // EBDA isn't at end of ram - give up.
162 return -1;
163
164 // Do copy
165 memmove((void*)newebda, (void*)oldebda, ebda_size * 1024);
166
167 // Update indexes
168 dprintf(1, "ebda moved from %x to %x\n", oldebda, newebda);
169 SET_BDA(mem_size_kb, newebda / 1024);
170 SET_BDA(ebda_seg, FLATPTR_TO_SEG(newebda));
171 return 0;
172}
173
174// Support expanding the ZoneLow dynamically.
175static void *
176zonelow_expand(u32 size, u32 align, struct allocinfo_s *fill)
177{
178 // Make sure to not move ebda while an optionrom is running.
179 if (unlikely(wait_preempt())) {
180 void *data = allocSpace(&ZoneLow, size, align, fill);
181 if (data)
182 return data;
183 }
184
185 struct allocinfo_s *info = findLast(&ZoneLow);
186 if (!info)
187 return NULL;
188 u32 oldpos = (u32)info->allocend;
189 u32 newpos = ALIGN_DOWN(oldpos - size, align);
190 u32 bottom = (u32)info->dataend;
191 if (newpos >= bottom && newpos <= oldpos)
192 // Space already present.
193 return allocSpace(&ZoneLow, size, align, fill);
194 u16 ebda_seg = get_ebda_seg();
195 u32 ebda_pos = (u32)MAKE_FLATPTR(ebda_seg, 0);
196 u8 ebda_size = GET_EBDA(ebda_seg, size);
197 u32 ebda_end = ebda_pos + ebda_size * 1024;
198 if (ebda_end != bottom)
199 // Something else is after ebda - can't use any existing space.
200 newpos = ALIGN_DOWN(ebda_end - size, align);
201 u32 newbottom = ALIGN_DOWN(newpos, 1024);
202 u32 newebda = ALIGN_DOWN(newbottom - ebda_size * 1024, 1024);
203 if (newebda < BUILD_EBDA_MINIMUM)
204 // Not enough space.
205 return NULL;
206
207 // Move ebda
208 int ret = relocate_ebda(newebda, ebda_pos, ebda_size);
209 if (ret)
210 return NULL;
211
212 // Update zone
213 if (ebda_end == bottom) {
214 info->data = (void*)newbottom;
215 info->dataend = (void*)newbottom;
216 } else
217 addSpace(&ZoneLow, (void*)newbottom, (void*)ebda_end);
218
219 return allocSpace(&ZoneLow, size, align, fill);
220}
221
222
223/****************************************************************
Kevin O'Connord0d82a32013-06-02 13:09:30 -0400224 * tracked memory allocations
225 ****************************************************************/
226
227// Allocate memory from the given zone and track it as a PMM allocation
228void * __malloc
229pmm_malloc(struct zone_s *zone, u32 handle, u32 size, u32 align)
230{
231 ASSERT32FLAT();
232 if (!size)
233 return NULL;
234
235 // Find and reserve space for bookkeeping.
236 struct allocdetail_s *detail = allocSpace(
237 &ZoneTmpHigh, sizeof(*detail), MALLOC_MIN_ALIGN, NULL);
238 if (!detail) {
239 detail = allocSpace(&ZoneTmpLow, sizeof(*detail)
240 , MALLOC_MIN_ALIGN, NULL);
241 if (!detail)
242 return NULL;
243 }
244
245 // Find and reserve space for main allocation
246 void *data = allocSpace(zone, size, align, &detail->datainfo);
Kevin O'Connor2b0fb8c2013-08-07 23:03:47 -0400247 if (!CONFIG_MALLOC_UPPERMEMORY && !data && zone == &ZoneLow)
248 data = zonelow_expand(size, align, &detail->datainfo);
Kevin O'Connord0d82a32013-06-02 13:09:30 -0400249 if (!data) {
250 freeSpace(&detail->detailinfo);
251 return NULL;
252 }
253
254 dprintf(8, "pmm_malloc zone=%p handle=%x size=%d align=%x"
255 " ret=%p (detail=%p)\n"
256 , zone, handle, size, align
257 , data, detail);
258 detail->handle = handle;
259
260 return data;
261}
262
263// Free a data block allocated with pmm_malloc
264int
265pmm_free(void *data)
266{
267 ASSERT32FLAT();
268 struct allocinfo_s *info = findAlloc(data);
269 if (!info || data == (void*)info || data == info->dataend)
270 return -1;
271 struct allocdetail_s *detail = container_of(
272 info, struct allocdetail_s, datainfo);
273 dprintf(8, "pmm_free %p (detail=%p)\n", data, detail);
274 freeSpace(info);
275 freeSpace(&detail->detailinfo);
276 return 0;
277}
278
279// Find the amount of free space in a given zone.
280static u32
281pmm_getspace(struct zone_s *zone)
282{
283 // XXX - doesn't account for ZoneLow being able to grow.
284 // XXX - results not reliable when CONFIG_THREAD_OPTIONROMS
285 u32 maxspace = 0;
286 struct allocinfo_s *info;
Kevin O'Connore097a752013-06-08 21:49:12 -0400287 hlist_for_each_entry(info, &zone->head, node) {
Kevin O'Connord0d82a32013-06-02 13:09:30 -0400288 u32 space = info->allocend - info->dataend;
289 if (space > maxspace)
290 maxspace = space;
291 }
292
293 if (zone != &ZoneTmpHigh && zone != &ZoneTmpLow)
294 return maxspace;
295 // Account for space needed for PMM tracking.
296 u32 reserve = ALIGN(sizeof(struct allocdetail_s), MALLOC_MIN_ALIGN);
297 if (maxspace <= reserve)
298 return 0;
299 return maxspace - reserve;
300}
301
302// Find the data block allocated with pmm_malloc with a given handle.
303static void *
304pmm_find(u32 handle)
305{
306 int i;
307 for (i=0; i<ARRAY_SIZE(Zones); i++) {
Kevin O'Connord0d82a32013-06-02 13:09:30 -0400308 struct allocinfo_s *info;
Kevin O'Connore097a752013-06-08 21:49:12 -0400309 hlist_for_each_entry(info, &Zones[i]->head, node) {
Kevin O'Connord0d82a32013-06-02 13:09:30 -0400310 if (info->data != (void*)info)
311 continue;
312 struct allocdetail_s *detail = container_of(
313 info, struct allocdetail_s, detailinfo);
314 if (detail->handle == handle)
315 return detail->datainfo.data;
316 }
317 }
318 return NULL;
319}
320
321
322/****************************************************************
Kevin O'Connor5e019082012-05-20 21:11:43 -0400323 * 0xc0000-0xf0000 management
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -0400324 ****************************************************************/
325
Kevin O'Connor5e019082012-05-20 21:11:43 -0400326static u32 RomEnd = BUILD_ROM_START;
327static struct allocinfo_s *RomBase;
328
329#define OPROM_HEADER_RESERVE 16
330
Kevin O'Connor3733f6f2013-02-17 12:44:23 -0500331// Return the maximum memory position option roms may use.
Kevin O'Connor5e019082012-05-20 21:11:43 -0400332u32
Kevin O'Connor3733f6f2013-02-17 12:44:23 -0500333rom_get_max(void)
Kevin O'Connor5e019082012-05-20 21:11:43 -0400334{
Kevin O'Connor2b0fb8c2013-08-07 23:03:47 -0400335 if (CONFIG_MALLOC_UPPERMEMORY)
336 return ALIGN_DOWN((u32)RomBase->allocend - OPROM_HEADER_RESERVE
337 , OPTION_ROM_ALIGN);
338 extern u8 code32init_end[];
339 u32 end = (u32)code32init_end;
340 return end > BUILD_BIOS_ADDR ? BUILD_BIOS_ADDR : end;
Kevin O'Connor5e019082012-05-20 21:11:43 -0400341}
342
Kevin O'Connor3733f6f2013-02-17 12:44:23 -0500343// Return the end of the last deployed option rom.
Kevin O'Connor5e019082012-05-20 21:11:43 -0400344u32
345rom_get_last(void)
346{
347 return RomEnd;
348}
349
350// Request space for an optionrom in 0xc0000-0xf0000 area.
351struct rom_header *
352rom_reserve(u32 size)
353{
Kevin O'Connor915f64a2013-08-14 23:37:56 -0400354 u32 newend = ALIGN(RomEnd + size, OPTION_ROM_ALIGN);
355 if (newend > rom_get_max())
Kevin O'Connor5e019082012-05-20 21:11:43 -0400356 return NULL;
Kevin O'Connor915f64a2013-08-14 23:37:56 -0400357 if (CONFIG_MALLOC_UPPERMEMORY) {
358 if (newend < (u32)zonelow_base)
359 newend = (u32)zonelow_base;
360 RomBase->data = RomBase->dataend = (void*)newend + OPROM_HEADER_RESERVE;
361 }
Kevin O'Connor5e019082012-05-20 21:11:43 -0400362 return (void*)RomEnd;
363}
364
365// Confirm space as in use by an optionrom.
366int
367rom_confirm(u32 size)
368{
369 void *new = rom_reserve(size);
370 if (!new) {
371 warn_noalloc();
372 return -1;
373 }
374 RomEnd = ALIGN(RomEnd + size, OPTION_ROM_ALIGN);
375 return 0;
376}
377
378
379/****************************************************************
380 * Setup
381 ****************************************************************/
Kevin O'Connor9c985172012-05-12 23:49:33 -0400382
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -0400383void
Kevin O'Connord83c87b2013-01-21 01:14:12 -0500384malloc_preinit(void)
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -0400385{
386 ASSERT32FLAT();
Kevin O'Connor6afc6f82013-02-19 01:02:50 -0500387 dprintf(3, "malloc preinit\n");
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -0400388
Kevin O'Connor6ca04602013-01-21 11:38:49 -0500389 // Don't declare any memory between 0xa0000 and 0x100000
390 add_e820(BUILD_LOWRAM_END, BUILD_BIOS_ADDR-BUILD_LOWRAM_END, E820_HOLE);
391
392 // Mark known areas as reserved.
393 add_e820(BUILD_BIOS_ADDR, BUILD_BIOS_SIZE, E820_RESERVED);
394
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -0400395 // Populate temp high ram
396 u32 highram = 0;
397 int i;
398 for (i=e820_count-1; i>=0; i--) {
399 struct e820entry *en = &e820_list[i];
400 u64 end = en->start + en->size;
401 if (end < 1024*1024)
402 break;
403 if (en->type != E820_RAM || end > 0xffffffff)
404 continue;
405 u32 s = en->start, e = end;
406 if (!highram) {
Kevin O'Connore52ad392013-02-20 23:48:22 -0500407 u32 newe = ALIGN_DOWN(e - BUILD_MAX_HIGHTABLE, MALLOC_MIN_ALIGN);
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -0400408 if (newe <= e && newe >= s) {
409 highram = newe;
410 e = newe;
411 }
412 }
413 addSpace(&ZoneTmpHigh, (void*)s, (void*)e);
414 }
415
Kevin O'Connor0b314ab2013-02-19 01:15:36 -0500416 // Populate regions
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -0400417 addSpace(&ZoneTmpLow, (void*)BUILD_STACK_ADDR, (void*)BUILD_EBDA_MINIMUM);
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -0400418 if (highram) {
419 addSpace(&ZoneHigh, (void*)highram
Kevin O'Connore52ad392013-02-20 23:48:22 -0500420 , (void*)highram + BUILD_MAX_HIGHTABLE);
421 add_e820(highram, BUILD_MAX_HIGHTABLE, E820_RESERVED);
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -0400422 }
423}
424
David Woodhouse118469a2013-01-25 19:46:25 -0600425void
426csm_malloc_preinit(u32 low_pmm, u32 low_pmm_size, u32 hi_pmm, u32 hi_pmm_size)
427{
428 ASSERT32FLAT();
429
Kevin O'Connore52ad392013-02-20 23:48:22 -0500430 if (hi_pmm_size > BUILD_MAX_HIGHTABLE) {
David Woodhouse118469a2013-01-25 19:46:25 -0600431 void *hi_pmm_end = (void *)hi_pmm + hi_pmm_size;
Kevin O'Connore52ad392013-02-20 23:48:22 -0500432 addSpace(&ZoneTmpHigh, (void *)hi_pmm, hi_pmm_end - BUILD_MAX_HIGHTABLE);
433 addSpace(&ZoneHigh, hi_pmm_end - BUILD_MAX_HIGHTABLE, hi_pmm_end);
David Woodhouse118469a2013-01-25 19:46:25 -0600434 } else {
435 addSpace(&ZoneTmpHigh, (void *)hi_pmm, (void *)hi_pmm + hi_pmm_size);
436 }
437 addSpace(&ZoneTmpLow, (void *)low_pmm, (void *)low_pmm + low_pmm_size);
David Woodhouse118469a2013-01-25 19:46:25 -0600438}
439
Kevin O'Connorf85e4bc2013-02-19 01:33:45 -0500440u32 LegacyRamSize VARFSEG;
441
442// Calculate the maximum ramsize (less than 4gig) from e820 map.
443static void
444calcRamSize(void)
445{
446 u32 rs = 0;
447 int i;
448 for (i=e820_count-1; i>=0; i--) {
449 struct e820entry *en = &e820_list[i];
450 u64 end = en->start + en->size;
451 u32 type = en->type;
452 if (end <= 0xffffffff && (type == E820_ACPI || type == E820_RAM)) {
453 rs = end;
454 break;
455 }
456 }
457 LegacyRamSize = rs >= 1024*1024 ? rs : 1024*1024;
458}
459
Kevin O'Connor533b6282011-07-16 13:13:12 -0400460// Update pointers after code relocation.
461void
Kevin O'Connor6afc6f82013-02-19 01:02:50 -0500462malloc_init(void)
Kevin O'Connor533b6282011-07-16 13:13:12 -0400463{
464 ASSERT32FLAT();
Kevin O'Connor6afc6f82013-02-19 01:02:50 -0500465 dprintf(3, "malloc init\n");
Kevin O'Connor533b6282011-07-16 13:13:12 -0400466
Kevin O'Connor6afc6f82013-02-19 01:02:50 -0500467 if (CONFIG_RELOCATE_INIT) {
468 // Fixup malloc pointers after relocation
469 int i;
470 for (i=0; i<ARRAY_SIZE(Zones); i++) {
471 struct zone_s *zone = Zones[i];
Kevin O'Connore097a752013-06-08 21:49:12 -0400472 if (zone->head.first)
473 zone->head.first->pprev = &zone->head.first;
Kevin O'Connor6afc6f82013-02-19 01:02:50 -0500474 }
Kevin O'Connor533b6282011-07-16 13:13:12 -0400475 }
Kevin O'Connor8b9137d2011-08-03 20:15:26 -0400476
Kevin O'Connor0b314ab2013-02-19 01:15:36 -0500477 // Initialize low-memory region
Kevin O'Connorc9243442013-02-17 13:58:28 -0500478 extern u8 varlow_start[], varlow_end[], final_varlow_start[];
479 memmove(final_varlow_start, varlow_start, varlow_end - varlow_start);
Kevin O'Connor2b0fb8c2013-08-07 23:03:47 -0400480 if (CONFIG_MALLOC_UPPERMEMORY) {
481 addSpace(&ZoneLow, zonelow_base + OPROM_HEADER_RESERVE
482 , final_varlow_start);
483 RomBase = findLast(&ZoneLow);
484 } else {
485 addSpace(&ZoneLow, (void*)ALIGN_DOWN((u32)final_varlow_start, 1024)
486 , final_varlow_start);
487 }
Kevin O'Connor13fd0a52013-02-07 00:06:18 -0500488
Kevin O'Connor6afc6f82013-02-19 01:02:50 -0500489 // Add space available in f-segment to ZoneFSeg
Kevin O'Connor6d152642013-02-19 21:35:20 -0500490 extern u8 zonefseg_start[], zonefseg_end[];
491 memset(zonefseg_start, 0, zonefseg_end - zonefseg_start);
492 addSpace(&ZoneFSeg, zonefseg_start, zonefseg_end);
Kevin O'Connorf85e4bc2013-02-19 01:33:45 -0500493
494 calcRamSize();
Kevin O'Connor533b6282011-07-16 13:13:12 -0400495}
496
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -0400497void
Kevin O'Connord83c87b2013-01-21 01:14:12 -0500498malloc_prepboot(void)
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -0400499{
Kevin O'Connor1313b782011-07-16 13:39:26 -0400500 ASSERT32FLAT();
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -0400501 dprintf(3, "malloc finalize\n");
502
Kevin O'Connor3733f6f2013-02-17 12:44:23 -0500503 u32 base = rom_get_max();
Kevin O'Connor9c985172012-05-12 23:49:33 -0400504 memset((void*)RomEnd, 0, base-RomEnd);
Kevin O'Connor2b0fb8c2013-08-07 23:03:47 -0400505 if (CONFIG_MALLOC_UPPERMEMORY) {
506 // Place an optionrom signature around used low mem area.
507 struct rom_header *dummyrom = (void*)base;
508 dummyrom->signature = OPTION_ROM_SIGNATURE;
509 int size = (BUILD_BIOS_ADDR - base) / 512;
510 dummyrom->size = (size > 255) ? 255 : size;
511 }
512
513 // Reserve more low-mem if needed.
514 u32 endlow = GET_BDA(mem_size_kb)*1024;
515 add_e820(endlow, BUILD_LOWRAM_END-endlow, E820_RESERVED);
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -0400516
Kevin O'Connor65802ad2012-07-21 11:56:09 -0400517 // Clear unused f-seg ram.
518 struct allocinfo_s *info = findLast(&ZoneFSeg);
519 memset(info->dataend, 0, info->allocend - info->dataend);
Kevin O'Connor89efc932013-02-17 12:46:53 -0500520 dprintf(1, "Space available for UMB: %x-%x, %x-%x\n"
521 , RomEnd, base, (u32)info->dataend, (u32)info->allocend);
Kevin O'Connor65802ad2012-07-21 11:56:09 -0400522
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -0400523 // Give back unused high ram.
Kevin O'Connor9c985172012-05-12 23:49:33 -0400524 info = findLast(&ZoneHigh);
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -0400525 if (info) {
526 u32 giveback = ALIGN_DOWN(info->allocend - info->dataend, PAGE_SIZE);
527 add_e820((u32)info->dataend, giveback, E820_RAM);
528 dprintf(1, "Returned %d bytes of ZoneHigh\n", giveback);
529 }
Kevin O'Connorf85e4bc2013-02-19 01:33:45 -0500530
531 calcRamSize();
Kevin O'Connor42a1d4c2010-06-06 11:10:24 -0400532}
533
534
535/****************************************************************
Kevin O'Connor0bf92702009-08-01 11:45:37 -0400536 * pmm interface
537 ****************************************************************/
Kevin O'Connore54ee382009-07-26 19:33:13 -0400538
539struct pmmheader {
540 u32 signature;
541 u8 version;
542 u8 length;
543 u8 checksum;
Kevin O'Connor6156afe2013-01-20 10:43:54 -0500544 struct segoff_s entry;
Kevin O'Connore54ee382009-07-26 19:33:13 -0400545 u8 reserved[5];
546} PACKED;
547
548extern struct pmmheader PMMHEADER;
549
550#define PMM_SIGNATURE 0x4d4d5024 // $PMM
551
552#if CONFIG_PMM
Kevin O'Connor89a2f962013-02-18 23:36:03 -0500553struct pmmheader PMMHEADER __aligned(16) VARFSEG = {
Kevin O'Connor6156afe2013-01-20 10:43:54 -0500554 .signature = PMM_SIGNATURE,
Kevin O'Connore54ee382009-07-26 19:33:13 -0400555 .version = 0x01,
556 .length = sizeof(PMMHEADER),
Kevin O'Connore54ee382009-07-26 19:33:13 -0400557};
558#endif
559
Kevin O'Connor0bf92702009-08-01 11:45:37 -0400560#define PMM_FUNCTION_NOT_SUPPORTED 0xffffffff
Kevin O'Connore54ee382009-07-26 19:33:13 -0400561
562// PMM - allocate
563static u32
564handle_pmm00(u16 *args)
565{
566 u32 length = *(u32*)&args[1], handle = *(u32*)&args[3];
567 u16 flags = args[5];
Kevin O'Connor0bf92702009-08-01 11:45:37 -0400568 dprintf(3, "pmm00: length=%x handle=%x flags=%x\n"
Kevin O'Connore54ee382009-07-26 19:33:13 -0400569 , length, handle, flags);
Kevin O'Connor0e9fd612009-08-22 21:31:58 -0400570 struct zone_s *lowzone = &ZoneTmpLow, *highzone = &ZoneTmpHigh;
571 if (flags & 8) {
572 // Permanent memory request.
573 lowzone = &ZoneLow;
574 highzone = &ZoneHigh;
575 }
Kevin O'Connor0bf92702009-08-01 11:45:37 -0400576 if (!length) {
577 // Memory size request
578 switch (flags & 3) {
579 default:
580 case 0:
581 return 0;
582 case 1:
Kevin O'Connor0e9fd612009-08-22 21:31:58 -0400583 return pmm_getspace(lowzone);
Kevin O'Connor0bf92702009-08-01 11:45:37 -0400584 case 2:
Kevin O'Connor0e9fd612009-08-22 21:31:58 -0400585 return pmm_getspace(highzone);
Kevin O'Connor0bf92702009-08-01 11:45:37 -0400586 case 3: {
Kevin O'Connor0e9fd612009-08-22 21:31:58 -0400587 u32 spacelow = pmm_getspace(lowzone);
588 u32 spacehigh = pmm_getspace(highzone);
Kevin O'Connor0bf92702009-08-01 11:45:37 -0400589 if (spacelow > spacehigh)
590 return spacelow;
591 return spacehigh;
592 }
593 }
594 }
595 u32 size = length * 16;
596 if ((s32)size <= 0)
597 return 0;
598 u32 align = MALLOC_MIN_ALIGN;
599 if (flags & 4) {
600 align = 1<<__ffs(size);
601 if (align < MALLOC_MIN_ALIGN)
602 align = MALLOC_MIN_ALIGN;
603 }
604 switch (flags & 3) {
605 default:
606 case 0:
607 return 0;
608 case 1:
Kevin O'Connor0e9fd612009-08-22 21:31:58 -0400609 return (u32)pmm_malloc(lowzone, handle, size, align);
Kevin O'Connor0bf92702009-08-01 11:45:37 -0400610 case 2:
Kevin O'Connor0e9fd612009-08-22 21:31:58 -0400611 return (u32)pmm_malloc(highzone, handle, size, align);
Kevin O'Connor0bf92702009-08-01 11:45:37 -0400612 case 3: {
Kevin O'Connor0e9fd612009-08-22 21:31:58 -0400613 void *data = pmm_malloc(lowzone, handle, size, align);
Kevin O'Connor0bf92702009-08-01 11:45:37 -0400614 if (data)
615 return (u32)data;
Kevin O'Connor0e9fd612009-08-22 21:31:58 -0400616 return (u32)pmm_malloc(highzone, handle, size, align);
Kevin O'Connor0bf92702009-08-01 11:45:37 -0400617 }
618 }
Kevin O'Connore54ee382009-07-26 19:33:13 -0400619}
620
621// PMM - find
622static u32
623handle_pmm01(u16 *args)
624{
625 u32 handle = *(u32*)&args[1];
Kevin O'Connor0bf92702009-08-01 11:45:37 -0400626 dprintf(3, "pmm01: handle=%x\n", handle);
Kevin O'Connord948e2b2009-10-12 12:54:56 -0400627 if (handle == PMM_DEFAULT_HANDLE)
Kevin O'Connor0bf92702009-08-01 11:45:37 -0400628 return 0;
629 return (u32)pmm_find(handle);
Kevin O'Connore54ee382009-07-26 19:33:13 -0400630}
631
632// PMM - deallocate
633static u32
634handle_pmm02(u16 *args)
635{
636 u32 buffer = *(u32*)&args[1];
Kevin O'Connor0bf92702009-08-01 11:45:37 -0400637 dprintf(3, "pmm02: buffer=%x\n", buffer);
638 int ret = pmm_free((void*)buffer);
639 if (ret)
640 // Error
641 return 1;
Kevin O'Connore54ee382009-07-26 19:33:13 -0400642 return 0;
643}
644
645static u32
646handle_pmmXX(u16 *args)
647{
Kevin O'Connor0bf92702009-08-01 11:45:37 -0400648 return PMM_FUNCTION_NOT_SUPPORTED;
Kevin O'Connore54ee382009-07-26 19:33:13 -0400649}
650
Kevin O'Connor533b6282011-07-16 13:13:12 -0400651u32 VISIBLE32INIT
Kevin O'Connore54ee382009-07-26 19:33:13 -0400652handle_pmm(u16 *args)
653{
Kevin O'Connor1313b782011-07-16 13:39:26 -0400654 ASSERT32FLAT();
Kevin O'Connore54ee382009-07-26 19:33:13 -0400655 if (! CONFIG_PMM)
Kevin O'Connor0bf92702009-08-01 11:45:37 -0400656 return PMM_FUNCTION_NOT_SUPPORTED;
Kevin O'Connore54ee382009-07-26 19:33:13 -0400657
658 u16 arg1 = args[0];
659 dprintf(DEBUG_HDL_pmm, "pmm call arg1=%x\n", arg1);
660
Kevin O'Connor533b6282011-07-16 13:13:12 -0400661 u32 ret;
662 switch (arg1) {
663 case 0x00: ret = handle_pmm00(args); break;
664 case 0x01: ret = handle_pmm01(args); break;
665 case 0x02: ret = handle_pmm02(args); break;
666 default: ret = handle_pmmXX(args); break;
667 }
668
Kevin O'Connor533b6282011-07-16 13:13:12 -0400669 return ret;
Kevin O'Connore54ee382009-07-26 19:33:13 -0400670}
671
Kevin O'Connore54ee382009-07-26 19:33:13 -0400672void
Kevin O'Connord83c87b2013-01-21 01:14:12 -0500673pmm_init(void)
Kevin O'Connore54ee382009-07-26 19:33:13 -0400674{
675 if (! CONFIG_PMM)
676 return;
677
678 dprintf(3, "init PMM\n");
679
Kevin O'Connor6156afe2013-01-20 10:43:54 -0500680 PMMHEADER.entry = FUNC16(entry_pmm);
Kevin O'Connore54ee382009-07-26 19:33:13 -0400681 PMMHEADER.checksum -= checksum(&PMMHEADER, sizeof(PMMHEADER));
682}
683
684void
Kevin O'Connord83c87b2013-01-21 01:14:12 -0500685pmm_prepboot(void)
Kevin O'Connore54ee382009-07-26 19:33:13 -0400686{
687 if (! CONFIG_PMM)
688 return;
689
690 dprintf(3, "finalize PMM\n");
691
692 PMMHEADER.signature = 0;
Kevin O'Connor6156afe2013-01-20 10:43:54 -0500693 PMMHEADER.entry.segoff = 0;
Kevin O'Connore54ee382009-07-26 19:33:13 -0400694}