1 /* 2 * Copyright 2004-2008, Axel Dörfler, axeld@pinc-software.de. 3 * Based on code written by Travis Geiselbrecht for NewOS. 4 * 5 * Distributed under the terms of the MIT License. 6 */ 7 8 9 #include "mmu.h" 10 11 #include <boot/platform.h> 12 #include <boot/stdio.h> 13 #include <boot/kernel_args.h> 14 #include <boot/stage2.h> 15 #include <arch/cpu.h> 16 #include <arch_kernel.h> 17 #include <platform/openfirmware/openfirmware.h> 18 #ifdef __ARM__ 19 #include <arm_mmu.h> 20 #endif 21 #include <kernel.h> 22 23 #include <board_config.h> 24 25 #include <OS.h> 26 27 #include <string.h> 28 29 /*! This implements boot loader mmu support for Book-E PowerPC, 30 which only support a limited number of TLB and no hardware page table walk, 31 and does not standardize at how to use the mmu, requiring vendor-specific 32 code. 33 34 Like Linux, we pin one of the TLB entries to a fixed translation, 35 however we use it differently. 36 cf. http://kernel.org/doc/ols/2003/ols2003-pages-340-350.pdf 37 38 This translation uses a single large page (16 or 256MB are possible) which 39 directly maps the begining of the RAM. 40 We use it as a linear space to allocate from at boot time, 41 loading the kernel and modules into it, and other required data. 42 Near the end we reserve a page table (it doesn't need to be aligned), 43 but unlike Linux we use the same globally hashed page table that is 44 implemented by Classic PPC, to allow reusing code if possible, and also 45 to limit fragmentation which would occur by using a tree-based page table. 46 However this means we might actually run out of page table entries in case 47 of too many collisions. 48 49 The kernel will then create areas to cover this already-mapped space. 50 This also means proper permission bits (RWX) will not be applicable to 51 separate areas which are enclosed by this mapping. 52 53 We put the kernel stack at the end of the mapping so that the guard page is 54 outsite and thus unmapped. (we don't support SMP) 55 */ 56 57 /*! The (physical) memory layout of the boot loader is currently as follows: 58 0x00000000 kernel 59 0x00400000 ...modules 60 61 (at least on the Sam460ex U-Boot; we'll need to accomodate other setups) 62 0x01000000 boot loader 63 0x01800000 Flattened Device Tree 64 0x01900000 boot.tgz (= ramdisk) 65 0x02000000 boot loader uimage 66 67 68 boot loader heap (should be discarded later on) 69 ... 256M-Kstack page hash table 70 ... 256M kernel stack 71 kernel stack guard page 72 73 The kernel is mapped at KERNEL_BASE, all other stuff mapped by the 74 loader (kernel args, modules, driver settings, ...) comes after 75 0x80040000 which means that there is currently only 4 MB reserved for 76 the kernel itself (see kMaxKernelSize). FIXME: downsize kernel_ppc 77 */ 78 79 80 int32 of_address_cells(int package); 81 int32 of_size_cells(int package); 82 83 extern bool gIs440; 84 // XXX:use a base class for Book-E support? 85 extern status_t arch_mmu_setup_pinned_tlb_amcc440(phys_addr_t totalRam, 86 size_t &tableSize, size_t &tlbSize); 87 88 #define TRACE_MMU 89 #ifdef TRACE_MMU 90 # define TRACE(x) dprintf x 91 #else 92 # define TRACE(x) ; 93 #endif 94 95 #define TRACE_MEMORY_MAP 96 // Define this to print the memory map to serial debug. 97 98 static const size_t kMaxKernelSize = 0x400000; // 4 MB for the kernel 99 100 static addr_t sNextPhysicalAddress = kMaxKernelSize; //will be set by mmu_init 101 static addr_t sNextVirtualAddress = KERNEL_BASE + kMaxKernelSize; 102 //static addr_t sMaxVirtualAddress = KERNEL_BASE + kMaxKernelSize; 103 104 // working page directory and page table 105 static void *sPageTable = 0; 106 static bool sHeapRegionAllocated = false; 107 108 109 static addr_t 110 get_next_virtual_address(size_t size) 111 { 112 addr_t address = sNextVirtualAddress; 113 sNextPhysicalAddress += size; 114 sNextVirtualAddress += size; 115 116 return address; 117 } 118 119 120 #if 0 121 static addr_t 122 get_next_physical_address(size_t size) 123 { 124 addr_t address = sNextPhysicalAddress; 125 sNextPhysicalAddress += size; 126 sNextVirtualAddress += size; 127 128 return address; 129 } 130 #endif 131 132 133 // #pragma mark - 134 135 136 extern "C" addr_t 137 mmu_map_physical_memory(addr_t physicalAddress, size_t size, uint32 flags) 138 { 139 panic("WRITEME"); 140 return 0; 141 } 142 143 144 /*! Sets up the final and kernel accessible GDT and IDT tables. 145 BIOS calls won't work any longer after this function has 146 been called. 147 */ 148 extern "C" void 149 mmu_init_for_kernel(void) 150 { 151 TRACE(("mmu_init_for_kernel\n")); 152 153 // TODO: remove all U-Boot TLB 154 155 #ifdef TRACE_MEMORY_MAP 156 { 157 uint32 i; 158 159 dprintf("phys memory ranges:\n"); 160 for (i = 0; i < gKernelArgs.num_physical_memory_ranges; i++) { 161 dprintf(" base 0x%" B_PRIxPHYSADDR 162 ", length 0x%" B_PRIxPHYSADDR "\n", 163 gKernelArgs.physical_memory_range[i].start, 164 gKernelArgs.physical_memory_range[i].size); 165 } 166 167 dprintf("allocated phys memory ranges:\n"); 168 for (i = 0; i < gKernelArgs.num_physical_allocated_ranges; i++) { 169 dprintf(" base 0x%" B_PRIxPHYSADDR 170 ", length 0x%" B_PRIxPHYSADDR "\n", 171 gKernelArgs.physical_allocated_range[i].start, 172 gKernelArgs.physical_allocated_range[i].size); 173 } 174 175 dprintf("allocated virt memory ranges:\n"); 176 for (i = 0; i < gKernelArgs.num_virtual_allocated_ranges; i++) { 177 dprintf(" base 0x%" B_PRIxPHYSADDR 178 ", length 0x%" B_PRIxPHYSADDR "\n", 179 gKernelArgs.virtual_allocated_range[i].start, 180 gKernelArgs.virtual_allocated_range[i].size); 181 } 182 } 183 #endif 184 } 185 186 187 //TODO:move this to generic/ ? 188 static status_t 189 find_physical_memory_ranges(phys_addr_t &total) 190 { 191 int memory = -1; 192 int package; 193 dprintf("checking for memory...\n"); 194 if (of_getprop(gChosen, "memory", &memory, sizeof(int)) == OF_FAILED) 195 package = of_finddevice("/memory"); 196 else 197 package = of_instance_to_package(memory); 198 if (package == OF_FAILED) 199 return B_ERROR; 200 201 total = 0; 202 203 // Memory base addresses are provided in 32 or 64 bit flavors 204 // #address-cells and #size-cells matches the number of 32-bit 'cells' 205 // representing the length of the base address and size fields 206 int root = of_finddevice("/"); 207 int32 regAddressCells = of_address_cells(root); 208 int32 regSizeCells = of_size_cells(root); 209 if (regAddressCells == OF_FAILED || regSizeCells == OF_FAILED) { 210 dprintf("finding base/size length counts failed, assume 32-bit.\n"); 211 regAddressCells = 1; 212 regSizeCells = 1; 213 } 214 215 // NOTE : Size Cells of 2 is possible in theory... but I haven't seen it yet. 216 if (regAddressCells > 2 || regSizeCells > 1) { 217 panic("%s: Unsupported OpenFirmware cell count detected.\n" 218 "Address Cells: %" B_PRId32 "; Size Cells: %" B_PRId32 219 " (CPU > 64bit?).\n", __func__, regAddressCells, regSizeCells); 220 return B_ERROR; 221 } 222 223 // On 64-bit PowerPC systems (G5), our mem base range address is larger 224 if (regAddressCells == 2) { 225 struct of_region<uint64, uint32> regions[64]; 226 int count = of_getprop(package, "reg", regions, sizeof(regions)); 227 if (count == OF_FAILED) 228 count = of_getprop(memory, "reg", regions, sizeof(regions)); 229 if (count == OF_FAILED) 230 return B_ERROR; 231 count /= sizeof(regions[0]); 232 233 for (int32 i = 0; i < count; i++) { 234 if (regions[i].size <= 0) { 235 dprintf("%ld: empty region\n", i); 236 continue; 237 } 238 dprintf("%" B_PRIu32 ": base = %" B_PRIu64 "," 239 "size = %" B_PRIu32 "\n", i, regions[i].base, regions[i].size); 240 241 total += regions[i].size; 242 243 if (insert_physical_memory_range((addr_t)regions[i].base, 244 regions[i].size) != B_OK) { 245 dprintf("cannot map physical memory range " 246 "(num ranges = %" B_PRIu32 ")!\n", 247 gKernelArgs.num_physical_memory_ranges); 248 return B_ERROR; 249 } 250 } 251 return B_OK; 252 } 253 254 // Otherwise, normal 32-bit PowerPC G3 or G4 have a smaller 32-bit one 255 struct of_region<uint32, uint32> regions[64]; 256 int count = of_getprop(package, "reg", regions, sizeof(regions)); 257 if (count == OF_FAILED) 258 count = of_getprop(memory, "reg", regions, sizeof(regions)); 259 if (count == OF_FAILED) 260 return B_ERROR; 261 count /= sizeof(regions[0]); 262 263 for (int32 i = 0; i < count; i++) { 264 if (regions[i].size <= 0) { 265 dprintf("%ld: empty region\n", i); 266 continue; 267 } 268 dprintf("%" B_PRIu32 ": base = %" B_PRIu32 "," 269 "size = %" B_PRIu32 "\n", i, regions[i].base, regions[i].size); 270 271 total += regions[i].size; 272 273 if (insert_physical_memory_range((addr_t)regions[i].base, 274 regions[i].size) != B_OK) { 275 dprintf("cannot map physical memory range " 276 "(num ranges = %" B_PRIu32 ")!\n", 277 gKernelArgs.num_physical_memory_ranges); 278 return B_ERROR; 279 } 280 } 281 282 return B_OK; 283 } 284 285 286 extern "C" void 287 mmu_init(void* fdt) 288 { 289 size_t tableSize, tlbSize; 290 status_t err; 291 TRACE(("mmu_init\n")); 292 293 // get map of physical memory (fill in kernel_args structure) 294 295 phys_addr_t total; 296 if (find_physical_memory_ranges(total) != B_OK) { 297 dprintf("Error: could not find physical memory ranges!\n"); 298 return /*B_ERROR*/; 299 } 300 dprintf("total physical memory = %" B_PRId64 "MB\n", total / (1024 * 1024)); 301 302 // XXX: ugly, and wrong, there are several 440 mmu types... FIXME 303 if (gIs440) { 304 err = arch_mmu_setup_pinned_tlb_amcc440(total, tableSize, tlbSize); 305 dprintf("setup_pinned_tlb: 0x%08lx table %zdMB tlb %zdMB\n", 306 err, tableSize / (1024 * 1024), tlbSize / (1024 * 1024)); 307 } else { 308 panic("Unknown MMU type!"); 309 return; 310 } 311 312 // remember the start of the allocated physical pages 313 gKernelArgs.physical_allocated_range[0].start 314 = gKernelArgs.physical_memory_range[0].start; 315 gKernelArgs.physical_allocated_range[0].size = tlbSize; 316 gKernelArgs.num_physical_allocated_ranges = 1; 317 318 // Save the memory we've virtually allocated (for the kernel and other 319 // stuff) 320 gKernelArgs.virtual_allocated_range[0].start = KERNEL_BASE; 321 gKernelArgs.virtual_allocated_range[0].size 322 = tlbSize + KERNEL_STACK_GUARD_PAGES * B_PAGE_SIZE; 323 gKernelArgs.num_virtual_allocated_ranges = 1; 324 325 326 sPageTable = (void *)(tlbSize - tableSize - KERNEL_STACK_SIZE); 327 // we put the page table near the end of the pinned TLB 328 TRACE(("page table at 0x%p to 0x%p\n", sPageTable, 329 (uint8 *)sPageTable + tableSize)); 330 331 // map in a kernel stack 332 gKernelArgs.cpu_kstack[0].start = (addr_t)(tlbSize - KERNEL_STACK_SIZE); 333 gKernelArgs.cpu_kstack[0].size = KERNEL_STACK_SIZE 334 + KERNEL_STACK_GUARD_PAGES * B_PAGE_SIZE; 335 336 TRACE(("kernel stack at 0x%Lx to 0x%Lx\n", gKernelArgs.cpu_kstack[0].start, 337 gKernelArgs.cpu_kstack[0].start + gKernelArgs.cpu_kstack[0].size)); 338 339 #ifdef __ARM__ 340 init_page_directory(); 341 342 // map the page directory on the next vpage 343 gKernelArgs.arch_args.vir_pgdir = mmu_map_physical_memory( 344 (addr_t)sPageDirectory, MMU_L1_TABLE_SIZE, kDefaultPageFlags); 345 #endif 346 } 347 348 349 // #pragma mark - 350 351 352 extern "C" status_t 353 platform_allocate_region(void **_address, size_t size, uint8 protection, 354 bool /*exactAddress*/) 355 { 356 TRACE(("platform_allocate_region(&%p, %zd)\n", *_address, size)); 357 358 //get_next_virtual_address 359 size = (size + B_PAGE_SIZE - 1) / B_PAGE_SIZE * B_PAGE_SIZE; 360 // roundup to page size for clarity 361 362 if (*_address != NULL) { 363 // This special path is almost only useful for loading the 364 // kernel into memory; it will only allow you to map the 365 // 'kMaxKernelSize' bytes following the kernel base address. 366 // Also, it won't check for already mapped addresses, so 367 // you better know why you are here :) 368 addr_t address = (addr_t)*_address; 369 370 // is the address within the valid range? 371 if (address < KERNEL_BASE 372 || address + size >= KERNEL_BASE + kMaxKernelSize) { 373 TRACE(("mmu_allocate in illegal range\n address: %" B_PRIx32 374 " KERNELBASE: %" B_PRIx32 " KERNEL_BASE + kMaxKernelSize:" 375 " %" B_PRIx32 " address + size : %" B_PRIx32 " \n", 376 (uint32)address, (uint32)KERNEL_BASE, 377 KERNEL_BASE + kMaxKernelSize, (uint32)(address + size))); 378 return B_ERROR; 379 } 380 TRACE(("platform_allocate_region: allocated %zd bytes at %08lx\n", size, 381 address)); 382 383 return B_OK; 384 } 385 386 void *address = (void *)get_next_virtual_address(size); 387 if (address == NULL) 388 return B_NO_MEMORY; 389 390 TRACE(("platform_allocate_region: allocated %zd bytes at %p\n", size, 391 address)); 392 *_address = address; 393 return B_OK; 394 } 395 396 397 extern "C" status_t 398 platform_free_region(void *address, size_t size) 399 { 400 TRACE(("platform_free_region(%p, %zd)\n", address, size)); 401 #ifdef __ARM__ 402 mmu_free(address, size); 403 #endif 404 return B_OK; 405 } 406 407 408 ssize_t 409 platform_allocate_heap_region(size_t size, void **_base) 410 { 411 if (sHeapRegionAllocated) 412 return B_NO_MEMORY; 413 sHeapRegionAllocated = true; 414 415 // the heap is put right before the pagetable 416 void *heap = (uint8 *)sPageTable - size; 417 //FIXME: use phys addresses to allow passing args to U-Boot? 418 419 *_base = heap; 420 TRACE(("boot heap at 0x%p\n", *_base)); 421 return size; 422 } 423 424 425 void 426 platform_free_heap_region(void *_base, size_t size) 427 { 428 //XXX 429 // It will be freed automatically, since it is in the 430 // identity mapped region, and not stored in the kernel's 431 // page tables. 432 } 433