/* * Copyright 2004-2008, Axel Dörfler, axeld@pinc-software.de. * Based on code written by Travis Geiselbrecht for NewOS. * * Distributed under the terms of the MIT License. */ #include "mmu.h" #include #include #include #include #include #include #include #include #ifdef __ARM__ #include #endif #include #include #include #include int32 of_address_cells(int package); int32 of_size_cells(int package); #define TRACE_MMU #ifdef TRACE_MMU # define TRACE(x) dprintf x #else # define TRACE(x) ; #endif /*! Computes the recommended minimal page table size as described in table 7-22 of the PowerPC "Programming Environment for 32-Bit Microprocessors". The page table size ranges from 64 kB (for 8 MB RAM) to 32 MB (for 4 GB RAM). FIXME: account for larger TLB descriptors for Book-E */ static size_t suggested_page_table_size(phys_addr_t total) { uint32 max = 23; // 2^23 == 8 MB while (max < 32) { if (total <= (1UL << max)) break; max++; } return 1UL << (max - 7); // 2^(23 - 7) == 64 kB } static void read_TLB(int i, uint32 tlb[3], uint8 &pid) { //FIXME:read pid too asm volatile( "tlbre %0,%3,0\n" "\ttlbre %1,%3,1\n" "\ttlbre %2,%3,2" : "=r"(tlb[0]), "=r"(tlb[1]), "=r"(tlb[2]) : "r"(i) ); } static void write_TLB(int i, uint32 tlb[3], uint8 pid) { //FIXME:write pid too asm volatile( "tlbwe %0,%3,0\n" "\ttlbwe %1,%3,1\n" "\ttlbwe %2,%3,2" : : "r"(tlb[0]), "r"(tlb[1]), "r"(tlb[2]), "r"(i) ); } static void dump_TLBs(void) { int i; for (i = 0; i < TLB_COUNT; i++) { uint32 tlb[3];// = { 0, 0, 0 }; uint8 pid; read_TLB(i, tlb, pid); dprintf("TLB[%02d]: %08lx %08lx %08lx %02x\n", i, tlb[0], tlb[1], tlb[2], pid); } } status_t arch_mmu_setup_pinned_tlb_amcc440(phys_addr_t totalRam, size_t &tableSize, size_t &tlbSize) { dump_TLBs(); tlb_length tlbLength = TLB_LENGTH_16MB; //XXX:totalRam = 4LL*1024*1024*1024; size_t suggestedTableSize = suggested_page_table_size(totalRam); dprintf("suggested page table size = %" B_PRIuSIZE "\n", suggestedTableSize); tableSize = suggestedTableSize; // add 4MB for kernel and some more for modules... tlbSize = tableSize + 8 * 1024 * 1024; // round up to realistic TLB lengths, either 16MB or 256MB // the unused space will be filled with SLAB areas if (tlbSize < 16 * 1024 * 1024) tlbSize = 16 * 1024 * 1024; else { tlbSize = 256 * 1024 * 1024; tlbLength = TLB_LENGTH_256MB; } uint32 tlb[3]; uint8 pid; int i; // Make sure the last TLB is free, else we are in trouble // XXX: allow using a different TLB entry? read_TLB(TLB_COUNT - 1, tlb, pid); if ((tlb[0] & TLB_V) != 0) { panic("Last TLB already in use. FIXME."); return B_ERROR; } // TODO: remove existing mapping from U-Boot at KERNEL_BASE !!! // (on Sam460ex it's pci mem) // for now we just move it to AS1 which we don't use, until calling // the kernel. // we could probably swap it with our own KERNEL_BASE TLB to call U-Boot // if required, but it'd be quite ugly. for (i = 0; i < TLB_COUNT; i++) { read_TLB(i, tlb, pid); //dprintf("tlb[%d][0] = %08lx\n", i, tlb[0]); // TODO: make the test more complete and correct if ((tlb[0] & 0xfffffc00) == KERNEL_BASE) { tlb[0] |= 0x100; // AS1 write_TLB(i, tlb, pid); dprintf("Moved existing translation in TLB[%d] to AS1\n", i); } } // pin the last TLB //XXX:also maybe skip the FDT + initrd + loader ? phys_addr_t physBase = gKernelArgs.physical_memory_range[0].start; //TODO:make sure 1st range is large enough? i = TLB_COUNT - 1; // last one pid = 0; // the kernel's PID tlb[0] = (KERNEL_BASE | tlbLength << 4 | TLB_V); tlb[1] = ((physBase & 0xfffffc00) | (physBase >> 32)); tlb[2] = (0x0000003f); // user:RWX kernel:RWX write_TLB(i, tlb, pid); return B_OK; }