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
30 int32 of_address_cells(int package);
31 int32 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 */
48 static size_t
suggested_page_table_size(phys_addr_t total)49 suggested_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
66 static void
read_TLB(int i,uint32 tlb[3],uint8 & pid)67 read_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
82 static void
write_TLB(int i,uint32 tlb[3],uint8 pid)83 write_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
98 static void
dump_TLBs(void)99 dump_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
112 status_t
arch_mmu_setup_pinned_tlb_amcc440(phys_addr_t totalRam,size_t & tableSize,size_t & tlbSize)113 arch_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