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