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 #define PAGE_READ_ONLY 0x0002 29 #define PAGE_READ_WRITE 0x0001 30 31 // NULL is actually a possible physical address, so use -1 (which is 32 // misaligned, so not a valid address) as the invalid physical address. 33 #define PHYSINVAL ((void *)-1) 34 //#define PHYSINVAL NULL 35 36 //#define TRACE_MMU 37 #ifdef TRACE_MMU 38 # define TRACE(x...) dprintf(x) 39 #else 40 # define TRACE(x...) ; 41 #endif 42 43 44 unsigned int sMmuInstance; 45 unsigned int sMemoryInstance; 46 47 48 // begin and end of the boot loader 49 extern "C" uint8 __text_begin; 50 extern "C" uint8 _end; 51 52 53 static status_t 54 insert_virtual_range_to_keep(void *start, uint32 size) 55 { 56 return insert_address_range(gKernelArgs.arch_args.virtual_ranges_to_keep, 57 &gKernelArgs.arch_args.num_virtual_ranges_to_keep, 58 MAX_VIRTUAL_RANGES_TO_KEEP, (addr_t)start, size); 59 } 60 61 62 static status_t 63 remove_virtual_range_to_keep(void *start, uint32 size) 64 { 65 return remove_address_range(gKernelArgs.arch_args.virtual_ranges_to_keep, 66 &gKernelArgs.arch_args.num_virtual_ranges_to_keep, 67 MAX_VIRTUAL_RANGES_TO_KEEP, (addr_t)start, size); 68 } 69 70 71 static status_t 72 find_physical_memory_ranges(size_t &total) 73 { 74 TRACE("checking for memory...\n"); 75 intptr_t package = of_instance_to_package(sMemoryInstance); 76 77 total = 0; 78 79 // Memory base addresses are provided in 32 or 64 bit flavors 80 // #address-cells and #size-cells matches the number of 32-bit 'cells' 81 // representing the length of the base address and size fields 82 intptr_t root = of_finddevice("/"); 83 int32 regSizeCells = of_size_cells(root); 84 if (regSizeCells == OF_FAILED) { 85 dprintf("finding size of memory cells failed, assume 32-bit.\n"); 86 regSizeCells = 1; 87 } 88 89 int32 regAddressCells = of_address_cells(root); 90 if (regAddressCells == OF_FAILED) { 91 // Sun Netra T1-105 is missing this, but we can guess that if the size 92 // is 64bit, the address also likely is. 93 regAddressCells = regSizeCells; 94 } 95 96 if (regAddressCells != 2 || regSizeCells != 2) { 97 panic("%s: Unsupported OpenFirmware cell count detected.\n" 98 "Address Cells: %" B_PRId32 "; Size Cells: %" B_PRId32 99 " (CPU > 64bit?).\n", __func__, regAddressCells, regSizeCells); 100 return B_ERROR; 101 } 102 103 struct of_region<uint64, uint64> regions[64]; 104 int count = of_getprop(package, "reg", regions, sizeof(regions)); 105 if (count == OF_FAILED) 106 count = of_getprop(sMemoryInstance, "reg", regions, sizeof(regions)); 107 if (count == OF_FAILED) 108 return B_ERROR; 109 count /= sizeof(regions[0]); 110 111 for (int32 i = 0; i < count; i++) { 112 if (regions[i].size <= 0) { 113 TRACE("%d: empty region\n", i); 114 continue; 115 } 116 TRACE("%" B_PRIu32 ": base = %" B_PRIx64 "," 117 "size = %" B_PRIx64 "\n", i, regions[i].base, regions[i].size); 118 119 total += regions[i].size; 120 121 if (insert_physical_memory_range((addr_t)regions[i].base, 122 regions[i].size) != B_OK) { 123 dprintf("cannot map physical memory range " 124 "(num ranges = %" B_PRIu32 ")!\n", 125 gKernelArgs.num_physical_memory_ranges); 126 return B_ERROR; 127 } 128 } 129 130 return B_OK; 131 } 132 133 134 static bool 135 is_virtual_allocated(void *address, size_t size) 136 { 137 uint64 foundBase; 138 return !get_free_address_range(gKernelArgs.virtual_allocated_range, 139 gKernelArgs.num_virtual_allocated_ranges, (addr_t)address, size, 140 &foundBase) || foundBase != (addr_t)address; 141 } 142 143 144 static bool 145 is_physical_allocated(void *address, size_t size) 146 { 147 uint64 foundBase; 148 return !get_free_address_range(gKernelArgs.physical_allocated_range, 149 gKernelArgs.num_physical_allocated_ranges, (addr_t)address, size, 150 &foundBase) || foundBase != (addr_t)address; 151 } 152 153 154 static bool 155 is_physical_memory(void *address, size_t size = 1) 156 { 157 return is_address_range_covered(gKernelArgs.physical_memory_range, 158 gKernelArgs.num_physical_memory_ranges, (addr_t)address, size); 159 } 160 161 162 static bool 163 map_range(void *virtualAddress, void *physicalAddress, size_t size, uint16 mode) 164 { 165 // everything went fine, so lets mark the space as used. 166 int status = of_call_method(sMmuInstance, "map", 5, 0, (uint64)mode, size, 167 virtualAddress, 0, physicalAddress); 168 169 if (status != 0) { 170 dprintf("map_range(base: %p, size: %" B_PRIuSIZE ") " 171 "mapping failed\n", virtualAddress, size); 172 return false; 173 } 174 175 return true; 176 } 177 178 179 static status_t 180 find_allocated_ranges(void **_exceptionHandlers) 181 { 182 // we have to preserve the OpenFirmware established mappings 183 // if we want to continue to use its service after we've 184 // taken over (we will probably need less translations once 185 // we have proper driver support for the target hardware). 186 intptr_t mmu = of_instance_to_package(sMmuInstance); 187 188 struct translation_map { 189 void *PhysicalAddress() { 190 int64_t p = data; 191 #if 0 192 // The openboot own "map?" word does not do this, so it must not 193 // be needed 194 // Sign extend 195 p <<= 23; 196 p >>= 23; 197 #endif 198 199 // Keep only PA[40:13] 200 // FIXME later CPUs have some more bits here 201 p &= 0x000001FFFFFFE000ll; 202 203 return (void*)p; 204 } 205 206 int16_t Mode() { 207 int16_t mode; 208 if (data & 2) 209 mode = PAGE_READ_WRITE; 210 else 211 mode = PAGE_READ_ONLY; 212 return mode; 213 } 214 215 void *virtual_address; 216 intptr_t length; 217 intptr_t data; 218 } translations[64]; 219 220 int length = of_getprop(mmu, "translations", &translations, 221 sizeof(translations)); 222 if (length == OF_FAILED) { 223 dprintf("Error: no OF translations.\n"); 224 return B_ERROR; 225 } 226 length = length / sizeof(struct translation_map); 227 uint32 total = 0; 228 TRACE("found %d translations\n", length); 229 230 for (int i = 0; i < length; i++) { 231 struct translation_map *map = &translations[i]; 232 bool keepRange = true; 233 TRACE("%i: map: %p, length %ld -> phy %p mode %d: ", i, 234 map->virtual_address, map->length, 235 map->PhysicalAddress(), map->Mode()); 236 237 // insert range in physical allocated, if it points to physical memory 238 239 if (is_physical_memory(map->PhysicalAddress()) 240 && insert_physical_allocated_range((addr_t)map->PhysicalAddress(), 241 map->length) != B_OK) { 242 dprintf("cannot map physical allocated range " 243 "(num ranges = %" B_PRIu32 ")!\n", 244 gKernelArgs.num_physical_allocated_ranges); 245 return B_ERROR; 246 } 247 248 // insert range in virtual allocated 249 250 if (insert_virtual_allocated_range((addr_t)map->virtual_address, 251 map->length) != B_OK) { 252 dprintf("cannot map virtual allocated range " 253 "(num ranges = %" B_PRIu32 ")!\n", 254 gKernelArgs.num_virtual_allocated_ranges); 255 } 256 257 // insert range in virtual ranges to keep 258 259 if (keepRange) { 260 TRACE("keeping\n"); 261 262 if (insert_virtual_range_to_keep(map->virtual_address, 263 map->length) != B_OK) { 264 dprintf("cannot map virtual range to keep " 265 "(num ranges = %" B_PRIu32 ")\n", 266 gKernelArgs.num_virtual_allocated_ranges); 267 } 268 } else { 269 TRACE("dropping\n"); 270 } 271 272 total += map->length; 273 } 274 TRACE("total size kept: %" B_PRIu32 "\n", total); 275 276 // remove the boot loader code from the virtual ranges to keep in the 277 // kernel 278 if (remove_virtual_range_to_keep(&__text_begin, &_end - &__text_begin) 279 != B_OK) { 280 dprintf("%s: Failed to remove boot loader range " 281 "from virtual ranges to keep.\n", __func__); 282 } 283 284 return B_OK; 285 } 286 287 288 static void * 289 find_physical_memory_range(size_t size) 290 { 291 for (uint32 i = 0; i < gKernelArgs.num_physical_memory_ranges; i++) { 292 if (gKernelArgs.physical_memory_range[i].size > size) 293 return (void *)(addr_t)gKernelArgs.physical_memory_range[i].start; 294 } 295 return PHYSINVAL; 296 } 297 298 299 static void * 300 find_free_physical_range(size_t size) 301 { 302 // just do a simple linear search at the end of the allocated 303 // ranges (dumb memory allocation) 304 if (gKernelArgs.num_physical_allocated_ranges == 0) { 305 if (gKernelArgs.num_physical_memory_ranges == 0) 306 return PHYSINVAL; 307 308 return find_physical_memory_range(size); 309 } 310 311 for (uint32 i = 0; i < gKernelArgs.num_physical_allocated_ranges; i++) { 312 void *address 313 = (void *)(addr_t)(gKernelArgs.physical_allocated_range[i].start 314 + gKernelArgs.physical_allocated_range[i].size); 315 if (!is_physical_allocated(address, size) 316 && is_physical_memory(address, size)) { 317 return address; 318 } 319 } 320 return PHYSINVAL; 321 } 322 323 324 static void * 325 find_free_virtual_range(void *base, size_t size) 326 { 327 if (base && !is_virtual_allocated(base, size)) 328 return base; 329 330 void *firstFound = NULL; 331 void *firstBaseFound = NULL; 332 for (uint32 i = 0; i < gKernelArgs.num_virtual_allocated_ranges; i++) { 333 void *address 334 = (void *)(addr_t)(gKernelArgs.virtual_allocated_range[i].start 335 + gKernelArgs.virtual_allocated_range[i].size); 336 if (!is_virtual_allocated(address, size)) { 337 if (!base) 338 return address; 339 340 if (firstFound == NULL) 341 firstFound = address; 342 if (address >= base 343 && (firstBaseFound == NULL || address < firstBaseFound)) { 344 firstBaseFound = address; 345 } 346 } 347 } 348 return (firstBaseFound ? firstBaseFound : firstFound); 349 } 350 351 352 extern "C" void * 353 arch_mmu_allocate(void *_virtualAddress, size_t size, uint8 _protection, 354 bool exactAddress) 355 { 356 // we only know page sizes 357 size = ROUNDUP(size, B_PAGE_SIZE); 358 359 uint8 protection = 0; 360 if (_protection & B_WRITE_AREA) 361 protection = PAGE_READ_WRITE; 362 else 363 protection = PAGE_READ_ONLY; 364 365 // If no address is given, use the KERNEL_BASE as base address, since 366 // that avoids trouble in the kernel, when we decide to keep the region. 367 void *virtualAddress = _virtualAddress; 368 #if 0 369 if (!virtualAddress) 370 virtualAddress = (void*)KERNEL_BASE; 371 #endif 372 373 // find free address large enough to hold "size" 374 virtualAddress = find_free_virtual_range(virtualAddress, size); 375 if (virtualAddress == NULL) 376 return NULL; 377 378 // fail if the exact address was requested, but is not free 379 if (exactAddress && _virtualAddress && virtualAddress != _virtualAddress) { 380 dprintf("arch_mmu_allocate(): exact address requested, but virtual " 381 "range (base: %p, size: %" B_PRIuSIZE ") is not free.\n", 382 _virtualAddress, size); 383 return NULL; 384 } 385 386 #if 0 387 intptr_t status; 388 389 /* claim the address */ 390 status = of_call_method(sMmuInstance, "claim", 3, 1, 0, size, 391 virtualAddress, &_virtualAddress); 392 if (status != 0) { 393 dprintf("arch_mmu_allocate(base: %p, size: %" B_PRIuSIZE ") " 394 "failed to claim virtual address\n", virtualAddress, size); 395 return NULL; 396 } 397 398 #endif 399 // we have a free virtual range for the allocation, now 400 // have a look for free physical memory as well (we assume 401 // that a) there is enough memory, and b) failing is fatal 402 // so that we don't have to optimize for these cases :) 403 404 void *physicalAddress = find_free_physical_range(size); 405 if (physicalAddress == PHYSINVAL) { 406 dprintf("arch_mmu_allocate(base: %p, size: %" B_PRIuSIZE ") " 407 "no free physical address\n", virtualAddress, size); 408 return NULL; 409 } 410 411 // everything went fine, so lets mark the space as used. 412 413 #if 0 414 void* _physicalAddress; 415 status = of_call_method(sMemoryInstance, "claim", 3, 1, physicalAddress, 416 1, size, &_physicalAddress); 417 418 if (status != 0) { 419 dprintf("arch_mmu_allocate(base: %p, size: %" B_PRIuSIZE ") " 420 "failed to claim physical address\n", physicalAddress, size); 421 return NULL; 422 } 423 #endif 424 425 insert_virtual_allocated_range((addr_t)virtualAddress, size); 426 insert_physical_allocated_range((addr_t)physicalAddress, size); 427 428 if (!map_range(virtualAddress, physicalAddress, size, protection)) 429 return NULL; 430 431 return virtualAddress; 432 } 433 434 435 extern "C" status_t 436 arch_mmu_free(void *address, size_t size) 437 { 438 // TODO: implement freeing a region! 439 return B_OK; 440 } 441 442 443 // #pragma mark - OpenFirmware callbacks and public API 444 445 446 #if 0 447 static int 448 map_callback(struct of_arguments *args) 449 { 450 void *physicalAddress = (void *)args->Argument(0); 451 void *virtualAddress = (void *)args->Argument(1); 452 int length = args->Argument(2); 453 int mode = args->Argument(3); 454 intptr_t &error = args->ReturnValue(0); 455 456 // insert range in physical allocated if needed 457 458 if (is_physical_memory(physicalAddress) 459 && insert_physical_allocated_range((addr_t)physicalAddress, length) 460 != B_OK) { 461 error = -1; 462 return OF_FAILED; 463 } 464 465 // insert range in virtual allocated 466 467 if (insert_virtual_allocated_range((addr_t)virtualAddress, length) 468 != B_OK) { 469 error = -2; 470 return OF_FAILED; 471 } 472 473 // map range into the page table 474 475 map_range(virtualAddress, physicalAddress, length, mode); 476 477 return B_OK; 478 } 479 480 481 static int 482 unmap_callback(struct of_arguments *args) 483 { 484 /* void *address = (void *)args->Argument(0); 485 int length = args->Argument(1); 486 int &error = args->ReturnValue(0); 487 */ 488 // TODO: to be implemented 489 490 return OF_FAILED; 491 } 492 493 494 static int 495 translate_callback(struct of_arguments *args) 496 { 497 // could not find the translation 498 return OF_FAILED; 499 } 500 501 502 static int 503 alloc_real_mem_callback(struct of_arguments *args) 504 { 505 /* addr_t minAddress = (addr_t)args->Argument(0); 506 addr_t maxAddress = (addr_t)args->Argument(1); 507 int length = args->Argument(2); 508 int mode = args->Argument(3); 509 int &error = args->ReturnValue(0); 510 int &physicalAddress = args->ReturnValue(1); 511 */ 512 // ToDo: to be implemented 513 514 return OF_FAILED; 515 } 516 517 518 /** Dispatches the callback to the responsible function */ 519 520 static int 521 callback(struct of_arguments *args) 522 { 523 const char *name = args->name; 524 TRACE("OF CALLBACK: %s\n", name); 525 526 if (!strcmp(name, "map")) 527 return map_callback(args); 528 else if (!strcmp(name, "unmap")) 529 return unmap_callback(args); 530 else if (!strcmp(name, "translate")) 531 return translate_callback(args); 532 else if (!strcmp(name, "alloc-real-mem")) 533 return alloc_real_mem_callback(args); 534 535 return OF_FAILED; 536 } 537 #endif 538 539 540 extern "C" status_t 541 arch_set_callback(void) 542 { 543 #if 0 544 // set OpenFirmware callbacks - it will ask us for memory after that 545 // instead of maintaining it itself 546 547 void *oldCallback = NULL; 548 if (of_call_client_function("set-callback", 1, 1, &callback, &oldCallback) 549 == OF_FAILED) { 550 dprintf("Error: OpenFirmware set-callback failed\n"); 551 return B_ERROR; 552 } 553 TRACE("old callback = %p; new callback = %p\n", oldCallback, callback); 554 #endif 555 556 return B_OK; 557 } 558 559 560 extern "C" status_t 561 arch_mmu_init(void) 562 { 563 if (of_getprop(gChosen, "mmu", &sMmuInstance, sizeof(int)) == OF_FAILED) { 564 dprintf("%s: Error: no OpenFirmware mmu\n", __func__); 565 return B_ERROR; 566 } 567 568 if (of_getprop(gChosen, "memory", &sMemoryInstance, sizeof(int)) == OF_FAILED) { 569 dprintf("%s: Error: no OpenFirmware memory\n", __func__); 570 return B_ERROR; 571 } 572 // get map of physical memory (fill in kernel_args structure) 573 574 size_t total; 575 if (find_physical_memory_ranges(total) != B_OK) { 576 dprintf("Error: could not find physical memory ranges!\n"); 577 return B_ERROR; 578 } 579 TRACE("total physical memory = %luMB\n", total / (1024 * 1024)); 580 581 void *exceptionHandlers = (void *)-1; 582 if (find_allocated_ranges(&exceptionHandlers) != B_OK) { 583 dprintf("Error: find_allocated_ranges() failed\n"); 584 return B_ERROR; 585 } 586 587 #if 0 588 if (exceptionHandlers == (void *)-1) { 589 // TODO: create mapping for the exception handlers 590 dprintf("Error: no mapping for the exception handlers!\n"); 591 } 592 593 // Set the Open Firmware memory callback. From now on the Open Firmware 594 // will ask us for memory. 595 arch_set_callback(); 596 597 // set up new page table and turn on translation again 598 // TODO "set up new page table and turn on translation again" (see PPC) 599 #endif 600 601 // set kernel args 602 603 TRACE("virt_allocated: %" B_PRIu32 "\n", 604 gKernelArgs.num_virtual_allocated_ranges); 605 TRACE("phys_allocated: %" B_PRIu32 "\n", 606 gKernelArgs.num_physical_allocated_ranges); 607 TRACE("phys_memory: %" B_PRIu32 "\n", 608 gKernelArgs.num_physical_memory_ranges); 609 610 #if 0 611 // TODO set gKernelArgs.arch_args content if we have something to put in there 612 gKernelArgs.arch_args.page_table.start = (addr_t)sPageTable; 613 gKernelArgs.arch_args.page_table.size = tableSize; 614 615 gKernelArgs.arch_args.exception_handlers.start = (addr_t)exceptionHandlers; 616 gKernelArgs.arch_args.exception_handlers.size = B_PAGE_SIZE; 617 #endif 618 619 return B_OK; 620 } 621 622