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/ppc/arch_mmu_amcc440.h> 17#include <arch_kernel.h> 18#include <platform/openfirmware/openfirmware.h> 19#ifdef __ARM__ 20#include <arm_mmu.h> 21#endif 22#include <kernel.h> 23 24#include <board_config.h> 25 26#include <OS.h> 27 28#include <string.h> 29 30int32 of_address_cells(int package); 31int32 of_size_cells(int package); 32 33#define TRACE_MMU 34#ifdef TRACE_MMU 35# define TRACE(x) dprintf x 36#else 37# define TRACE(x) ; 38#endif 39 40 41/*! Computes the recommended minimal page table size as 42 described in table 7-22 of the PowerPC "Programming 43 Environment for 32-Bit Microprocessors". 44 The page table size ranges from 64 kB (for 8 MB RAM) 45 to 32 MB (for 4 GB RAM). 46 FIXME: account for larger TLB descriptors for Book-E 47*/ 48static size_t 49suggested_page_table_size(phys_addr_t total) 50{ 51 uint32 max = 23; 52 // 2^23 == 8 MB 53 54 while (max < 32) { 55 if (total <= (1UL << max)) 56 break; 57 58 max++; 59 } 60 61 return 1UL << (max - 7); 62 // 2^(23 - 7) == 64 kB 63} 64 65 66static void 67read_TLB(int i, uint32 tlb[3], uint8 &pid) 68{ 69 //FIXME:read pid too 70 asm volatile( 71 "tlbre %0,%3,0\n" 72 "\ttlbre %1,%3,1\n" 73 "\ttlbre %2,%3,2" 74 : "=r"(tlb[0]), 75 "=r"(tlb[1]), 76 "=r"(tlb[2]) 77 : "r"(i) 78 ); 79} 80 81 82static void 83write_TLB(int i, uint32 tlb[3], uint8 pid) 84{ 85 //FIXME:write pid too 86 asm volatile( 87 "tlbwe %0,%3,0\n" 88 "\ttlbwe %1,%3,1\n" 89 "\ttlbwe %2,%3,2" 90 : : "r"(tlb[0]), 91 "r"(tlb[1]), 92 "r"(tlb[2]), 93 "r"(i) 94 ); 95} 96 97 98static void 99dump_TLBs(void) 100{ 101 int i; 102 for (i = 0; i < TLB_COUNT; i++) { 103 uint32 tlb[3];// = { 0, 0, 0 }; 104 uint8 pid; 105 read_TLB(i, tlb, pid); 106 dprintf("TLB[%02d]: %08lx %08lx %08lx %02x\n", 107 i, tlb[0], tlb[1], tlb[2], pid); 108 } 109} 110 111 112status_t 113arch_mmu_setup_pinned_tlb_amcc440(phys_addr_t totalRam, size_t &tableSize, 114 size_t &tlbSize) 115{ 116 dump_TLBs(); 117 tlb_length tlbLength = TLB_LENGTH_16MB; 118//XXX:totalRam = 4LL*1024*1024*1024; 119 120 size_t suggestedTableSize = suggested_page_table_size(totalRam); 121 dprintf("suggested page table size = %" B_PRIuSIZE "\n", 122 suggestedTableSize); 123 124 tableSize = suggestedTableSize; 125 126 // add 4MB for kernel and some more for modules... 127 tlbSize = tableSize + 8 * 1024 * 1024; 128 129 // round up to realistic TLB lengths, either 16MB or 256MB 130 // the unused space will be filled with SLAB areas 131 if (tlbSize < 16 * 1024 * 1024) 132 tlbSize = 16 * 1024 * 1024; 133 else { 134 tlbSize = 256 * 1024 * 1024; 135 tlbLength = TLB_LENGTH_256MB; 136 } 137 138 uint32 tlb[3]; 139 uint8 pid; 140 int i; 141 142 // Make sure the last TLB is free, else we are in trouble 143 // XXX: allow using a different TLB entry? 144 read_TLB(TLB_COUNT - 1, tlb, pid); 145 if ((tlb[0] & TLB_V) != 0) { 146 panic("Last TLB already in use. FIXME."); 147 return B_ERROR; 148 } 149 150 // TODO: remove existing mapping from U-Boot at KERNEL_BASE !!! 151 // (on Sam460ex it's pci mem) 152 // for now we just move it to AS1 which we don't use, until calling 153 // the kernel. 154 // we could probably swap it with our own KERNEL_BASE TLB to call U-Boot 155 // if required, but it'd be quite ugly. 156 for (i = 0; i < TLB_COUNT; i++) { 157 read_TLB(i, tlb, pid); 158 //dprintf("tlb[%d][0] = %08lx\n", i, tlb[0]); 159 // TODO: make the test more complete and correct 160 if ((tlb[0] & 0xfffffc00) == KERNEL_BASE) { 161 tlb[0] |= 0x100; // AS1 162 write_TLB(i, tlb, pid); 163 dprintf("Moved existing translation in TLB[%d] to AS1\n", i); 164 } 165 } 166 167 // pin the last TLB 168 //XXX:also maybe skip the FDT + initrd + loader ? 169 phys_addr_t physBase = gKernelArgs.physical_memory_range[0].start; 170 //TODO:make sure 1st range is large enough? 171 i = TLB_COUNT - 1; // last one 172 pid = 0; // the kernel's PID 173 tlb[0] = (KERNEL_BASE | tlbLength << 4 | TLB_V); 174 tlb[1] = ((physBase & 0xfffffc00) | (physBase >> 32)); 175 tlb[2] = (0x0000003f); // user:RWX kernel:RWX 176 write_TLB(i, tlb, pid); 177 178 return B_OK; 179} 180 181