1 /* 2 * Copyright 2003-2009, Axel Dörfler, axeld@pinc-software.de. 3 * Copyright 2010-2011, Haiku, Inc. All Rights Reserved. 4 * All rights reserved. Distributed under the terms of the MIT License. 5 * 6 * Authors: 7 * Axel Dörfler, axeld@pinc-software.de. 8 * Alexander von Gluck, kallisti5@unixzen.com 9 */ 10 11 12 #include <OS.h> 13 14 #include <platform_arch.h> 15 #include <boot/addr_range.h> 16 #include <boot/kernel_args.h> 17 #include <boot/platform.h> 18 #include <boot/stage2.h> 19 #include <boot/stdio.h> 20 #include <platform/openfirmware/openfirmware.h> 21 #include <arch_cpu.h> 22 #include <arch_mmu.h> 23 #include <kernel.h> 24 25 #include "support.h" 26 27 28 // set protection to WIMGNPP: -----PP 29 // PP: 00 - no access 30 // 01 - read only 31 // 10 - read/write 32 // 11 - read only 33 #define PAGE_READ_ONLY 0x01 34 #define PAGE_READ_WRITE 0x02 35 36 // NULL is actually a possible physical address... 37 //#define PHYSINVAL ((void *)-1) 38 #define PHYSINVAL NULL 39 40 //#define TRACE_MMU 41 #ifdef TRACE_MMU 42 # define TRACE(x...) dprintf(x) 43 #else 44 # define TRACE(x...) ; 45 #endif 46 47 48 uint32 sPageTableHashMask; 49 50 51 // begin and end of the boot loader 52 extern "C" uint8 __text_begin; 53 extern "C" uint8 _end; 54 55 56 static status_t 57 find_physical_memory_ranges(size_t &total) 58 { 59 int memory; 60 dprintf("checking for memory...\n"); 61 if (of_getprop(gChosen, "memory", &memory, sizeof(int)) == OF_FAILED) 62 return B_ERROR; 63 int package = of_instance_to_package(memory); 64 65 total = 0; 66 67 // Memory base addresses are provided in 32 or 64 bit flavors 68 // #address-cells and #size-cells matches the number of 32-bit 'cells' 69 // representing the length of the base address and size fields 70 int root = of_finddevice("/"); 71 int32 regAddressCells = of_address_cells(root); 72 int32 regSizeCells = of_size_cells(root); 73 if (regAddressCells == OF_FAILED || regSizeCells == OF_FAILED) { 74 dprintf("finding base/size length counts failed, assume 32-bit.\n"); 75 regAddressCells = 1; 76 regSizeCells = 1; 77 } 78 79 // NOTE : Size Cells of 2 is possible in theory... but I haven't seen it yet. 80 if (regAddressCells > 2 || regSizeCells > 1) { 81 panic("%s: Unsupported OpenFirmware cell count detected.\n" 82 "Address Cells: %" B_PRId32 "; Size Cells: %" B_PRId32 83 " (CPU > 64bit?).\n", __func__, regAddressCells, regSizeCells); 84 return B_ERROR; 85 } 86 87 // On 64-bit PowerPC systems (G5), our mem base range address is larger 88 if (regAddressCells == 2) { 89 struct of_region<uint64> regions[64]; 90 int count = of_getprop(package, "reg", regions, sizeof(regions)); 91 if (count == OF_FAILED) 92 count = of_getprop(memory, "reg", regions, sizeof(regions)); 93 if (count == OF_FAILED) 94 return B_ERROR; 95 count /= sizeof(regions[0]); 96 97 for (int32 i = 0; i < count; i++) { 98 if (regions[i].size <= 0) { 99 dprintf("%d: empty region\n", i); 100 continue; 101 } 102 dprintf("%" B_PRIu32 ": base = %" B_PRIu64 "," 103 "size = %" B_PRIu32 "\n", i, regions[i].base, regions[i].size); 104 105 total += regions[i].size; 106 107 if (insert_physical_memory_range((addr_t)regions[i].base, 108 regions[i].size) != B_OK) { 109 dprintf("cannot map physical memory range " 110 "(num ranges = %" B_PRIu32 ")!\n", 111 gKernelArgs.num_physical_memory_ranges); 112 return B_ERROR; 113 } 114 } 115 return B_OK; 116 } 117 118 // Otherwise, normal 32-bit PowerPC G3 or G4 have a smaller 32-bit one 119 struct of_region<uint32> regions[64]; 120 int count = of_getprop(package, "reg", regions, sizeof(regions)); 121 if (count == OF_FAILED) 122 count = of_getprop(memory, "reg", regions, sizeof(regions)); 123 if (count == OF_FAILED) 124 return B_ERROR; 125 count /= sizeof(regions[0]); 126 127 for (int32 i = 0; i < count; i++) { 128 if (regions[i].size <= 0) { 129 dprintf("%d: empty region\n", i); 130 continue; 131 } 132 dprintf("%" B_PRIu32 ": base = %" B_PRIu32 "," 133 "size = %" B_PRIu32 "\n", i, regions[i].base, regions[i].size); 134 135 total += regions[i].size; 136 137 if (insert_physical_memory_range((addr_t)regions[i].base, 138 regions[i].size) != B_OK) { 139 dprintf("cannot map physical memory range " 140 "(num ranges = %" B_PRIu32 ")!\n", 141 gKernelArgs.num_physical_memory_ranges); 142 return B_ERROR; 143 } 144 } 145 146 return B_OK; 147 } 148 149 150 static bool 151 is_virtual_allocated(void *address, size_t size) 152 { 153 uint64 foundBase; 154 return !get_free_address_range(gKernelArgs.virtual_allocated_range, 155 gKernelArgs.num_virtual_allocated_ranges, (addr_t)address, size, 156 &foundBase) || foundBase != (addr_t)address; 157 } 158 159 160 static bool 161 is_physical_allocated(void *address, size_t size) 162 { 163 uint64 foundBase; 164 return !get_free_address_range(gKernelArgs.physical_allocated_range, 165 gKernelArgs.num_physical_allocated_ranges, (addr_t)address, size, 166 &foundBase) || foundBase != (addr_t)address; 167 } 168 169 170 static bool 171 is_physical_memory(void *address, size_t size) 172 { 173 return is_address_range_covered(gKernelArgs.physical_memory_range, 174 gKernelArgs.num_physical_memory_ranges, (addr_t)address, size); 175 } 176 177 178 static bool 179 is_physical_memory(void *address) 180 { 181 return is_physical_memory(address, 1); 182 } 183 184 185 static void 186 map_page(void *virtualAddress, void *physicalAddress, uint8 mode) 187 { 188 panic("%s: out of page table entries!\n", __func__); 189 } 190 191 192 static void 193 map_range(void *virtualAddress, void *physicalAddress, size_t size, uint8 mode) 194 { 195 for (uint32 offset = 0; offset < size; offset += B_PAGE_SIZE) { 196 map_page((void *)(intptr_t(virtualAddress) + offset), 197 (void *)(intptr_t(physicalAddress) + offset), mode); 198 } 199 } 200 201 202 static void * 203 find_physical_memory_range(size_t size) 204 { 205 for (uint32 i = 0; i < gKernelArgs.num_physical_memory_ranges; i++) { 206 if (gKernelArgs.physical_memory_range[i].size > size) 207 return (void *)(addr_t)gKernelArgs.physical_memory_range[i].start; 208 } 209 return PHYSINVAL; 210 } 211 212 213 static void * 214 find_free_physical_range(size_t size) 215 { 216 // just do a simple linear search at the end of the allocated 217 // ranges (dumb memory allocation) 218 if (gKernelArgs.num_physical_allocated_ranges == 0) { 219 if (gKernelArgs.num_physical_memory_ranges == 0) 220 return PHYSINVAL; 221 222 return find_physical_memory_range(size); 223 } 224 225 for (uint32 i = 0; i < gKernelArgs.num_physical_allocated_ranges; i++) { 226 void *address 227 = (void *)(addr_t)(gKernelArgs.physical_allocated_range[i].start 228 + gKernelArgs.physical_allocated_range[i].size); 229 if (!is_physical_allocated(address, size) 230 && is_physical_memory(address, size)) 231 return address; 232 } 233 return PHYSINVAL; 234 } 235 236 237 static void * 238 find_free_virtual_range(void *base, size_t size) 239 { 240 if (base && !is_virtual_allocated(base, size)) 241 return base; 242 243 void *firstFound = NULL; 244 void *firstBaseFound = NULL; 245 for (uint32 i = 0; i < gKernelArgs.num_virtual_allocated_ranges; i++) { 246 void *address 247 = (void *)(addr_t)(gKernelArgs.virtual_allocated_range[i].start 248 + gKernelArgs.virtual_allocated_range[i].size); 249 if (!is_virtual_allocated(address, size)) { 250 if (!base) 251 return address; 252 253 if (firstFound == NULL) 254 firstFound = address; 255 if (address >= base 256 && (firstBaseFound == NULL || address < firstBaseFound)) { 257 firstBaseFound = address; 258 } 259 } 260 } 261 return (firstBaseFound ? firstBaseFound : firstFound); 262 } 263 264 265 extern "C" void * 266 arch_mmu_allocate(void *_virtualAddress, size_t size, uint8 _protection, 267 bool exactAddress) 268 { 269 // we only know page sizes 270 size = ROUNDUP(size, B_PAGE_SIZE); 271 272 uint8 protection = 0; 273 if (_protection & B_WRITE_AREA) 274 protection = PAGE_READ_WRITE; 275 else 276 protection = PAGE_READ_ONLY; 277 278 // If no address is given, use the KERNEL_BASE as base address, since 279 // that avoids trouble in the kernel, when we decide to keep the region. 280 void *virtualAddress = _virtualAddress; 281 if (!virtualAddress) 282 virtualAddress = (void*)KERNEL_BASE; 283 284 // find free address large enough to hold "size" 285 virtualAddress = find_free_virtual_range(virtualAddress, size); 286 if (virtualAddress == NULL) 287 return NULL; 288 289 // fail if the exact address was requested, but is not free 290 if (exactAddress && _virtualAddress && virtualAddress != _virtualAddress) { 291 dprintf("arch_mmu_allocate(): exact address requested, but virtual " 292 "range (base: %p, size: %" B_PRIuSIZE ") is not free.\n", 293 _virtualAddress, size); 294 return NULL; 295 } 296 297 // we have a free virtual range for the allocation, now 298 // have a look for free physical memory as well (we assume 299 // that a) there is enough memory, and b) failing is fatal 300 // so that we don't have to optimize for these cases :) 301 302 void *physicalAddress = find_free_physical_range(size); 303 if (physicalAddress == PHYSINVAL) { 304 dprintf("arch_mmu_allocate(base: %p, size: %" B_PRIuSIZE ") " 305 "no free physical address\n", virtualAddress, size); 306 return NULL; 307 } 308 309 // everything went fine, so lets mark the space as used. 310 311 dprintf("mmu_alloc: va %p, pa %p, size %" B_PRIuSIZE "\n", virtualAddress, 312 physicalAddress, size); 313 insert_virtual_allocated_range((addr_t)virtualAddress, size); 314 insert_physical_allocated_range((addr_t)physicalAddress, size); 315 316 map_range(virtualAddress, physicalAddress, size, protection); 317 318 return virtualAddress; 319 } 320 321 322 extern "C" status_t 323 arch_mmu_free(void *address, size_t size) 324 { 325 // TODO: implement freeing a region! 326 return B_OK; 327 } 328 329 330 // #pragma mark - OpenFirmware callbacks and public API 331 332 333 static int 334 map_callback(struct of_arguments *args) 335 { 336 void *physicalAddress = (void *)args->Argument(0); 337 void *virtualAddress = (void *)args->Argument(1); 338 int length = args->Argument(2); 339 int mode = args->Argument(3); 340 intptr_t &error = args->ReturnValue(0); 341 342 // insert range in physical allocated if needed 343 344 if (is_physical_memory(physicalAddress) 345 && insert_physical_allocated_range((addr_t)physicalAddress, length) 346 != B_OK) { 347 error = -1; 348 return OF_FAILED; 349 } 350 351 // insert range in virtual allocated 352 353 if (insert_virtual_allocated_range((addr_t)virtualAddress, length) 354 != B_OK) { 355 error = -2; 356 return OF_FAILED; 357 } 358 359 // map range into the page table 360 361 map_range(virtualAddress, physicalAddress, length, mode); 362 363 return B_OK; 364 } 365 366 367 static int 368 unmap_callback(struct of_arguments *args) 369 { 370 /* void *address = (void *)args->Argument(0); 371 int length = args->Argument(1); 372 int &error = args->ReturnValue(0); 373 */ 374 // TODO: to be implemented 375 376 return OF_FAILED; 377 } 378 379 380 static int 381 translate_callback(struct of_arguments *args) 382 { 383 // could not find the translation 384 return OF_FAILED; 385 } 386 387 388 static int 389 alloc_real_mem_callback(struct of_arguments *args) 390 { 391 /* addr_t minAddress = (addr_t)args->Argument(0); 392 addr_t maxAddress = (addr_t)args->Argument(1); 393 int length = args->Argument(2); 394 int mode = args->Argument(3); 395 int &error = args->ReturnValue(0); 396 int &physicalAddress = args->ReturnValue(1); 397 */ 398 // ToDo: to be implemented 399 400 return OF_FAILED; 401 } 402 403 404 /** Dispatches the callback to the responsible function */ 405 406 static int 407 callback(struct of_arguments *args) 408 { 409 const char *name = args->name; 410 TRACE("OF CALLBACK: %s\n", name); 411 412 if (!strcmp(name, "map")) 413 return map_callback(args); 414 else if (!strcmp(name, "unmap")) 415 return unmap_callback(args); 416 else if (!strcmp(name, "translate")) 417 return translate_callback(args); 418 else if (!strcmp(name, "alloc-real-mem")) 419 return alloc_real_mem_callback(args); 420 421 return OF_FAILED; 422 } 423 424 425 extern "C" status_t 426 arch_set_callback(void) 427 { 428 // set OpenFirmware callbacks - it will ask us for memory after that 429 // instead of maintaining it itself 430 431 void *oldCallback = NULL; 432 if (of_call_client_function("set-callback", 1, 1, &callback, &oldCallback) 433 == OF_FAILED) { 434 dprintf("Error: OpenFirmware set-callback failed\n"); 435 return B_ERROR; 436 } 437 TRACE("old callback = %p; new callback = %p\n", oldCallback, callback); 438 439 return B_OK; 440 } 441 442 443 extern "C" status_t 444 arch_mmu_init(void) 445 { 446 // get map of physical memory (fill in kernel_args structure) 447 448 size_t total; 449 if (find_physical_memory_ranges(total) != B_OK) { 450 dprintf("Error: could not find physical memory ranges!\n"); 451 return B_ERROR; 452 } 453 dprintf("total physical memory = %luMB\n", total / (1024 * 1024)); 454 455 return B_OK; 456 } 457 458