Documented GVF_SAVE_VAR alongside other flags, and removed a query/doubt
[AROS.git] / arch / ppc-sam440 / kernel / mmu.c
blob2351f15f0e7d0a040cd010e6448d6df6cc40dddb
1 /*
2 Copyright © 1995-2014, The AROS Development Team. All rights reserved.
3 $Id$
4 */
6 #include <aros/debug.h>
7 #include <asm/amcc440.h>
8 #include <asm/io.h>
9 #include <aros/kernel.h>
11 #include "kernel_intern.h"
13 struct tlb_info {
14 uint32_t bitmap[2];
15 int free;
18 /* Alloc TLB in the bitmap. Returns -1 if the allocation cannot be done */
19 static int alloc_tlb(struct tlb_info *info)
21 /* It should be done in locked state only! */
23 int bit = __builtin_clz(info->bitmap[0]);
24 if (bit == 32)
26 bit += __builtin_clz(info->bitmap[1]);
28 else
30 info->bitmap[0] &= ~(0x80000000 >> bit);
32 if (bit == 64)
34 return -1;
36 else
38 info->bitmap[1] &= ~(0x80000000 >> (bit-32));
40 info->free--;
41 return bit;
44 static void free_tlb(struct tlb_info *info, int entry)
46 if (entry >=0 && entry < 32)
48 asm volatile("tlbwe %0,%1,0;" ::"r"(0), "r"(entry));
49 if (info->bitmap[0] & (0x80000000 >> entry))
51 D(bug("[KRN] Freeing already free TLB!!!\n"));
53 else
55 info->bitmap[0] |= (0x80000000 >> entry);
56 info->free++;
59 else if (entry < 64)
61 asm volatile("tlbwe %0,%1,0;" ::"r"(0), "r"(entry));
62 entry -= 32;
63 if (info->bitmap[1] & (0x80000000 >> entry))
65 D(bug("[KRN] Freeing already free TLB!!!\n"));
67 else
69 info->bitmap[1] |= (0x80000000 >> entry);
70 info->free++;
73 else
75 D(bug("[KRN] Wrong TLB\n"));
76 return;
80 static struct mmu_page_size {
81 uint8_t code;
82 uintptr_t mask;
83 } allowable_pages[] = {
84 { 0x90, 0x0fffffff }, /* 256MB */
85 { 0x70, 0x00ffffff }, /* 16MB */
86 { 0x50, 0x000fffff }, /* 1MB */
87 { 0x40, 0x0003ffff }, /* 256KB */
88 { 0x30, 0x0000ffff }, /* 64KB */
89 { 0x20, 0x00003fff }, /* 16KB */
90 { 0x10, 0x00000fff }, /* 4KB */
91 { 0x00, 0x000003ff }, /* 1KB */
92 { 0xff, 0xffffffff }, /* END MARKER */
95 void map_region(struct tlb_info *info, uint8_t extra, uintptr_t physbase, uintptr_t virtbase, uintptr_t length, uint32_t prot)
97 long tlb_temp = info->free;
99 D(bug("[KRN] map_region(%08x, %08x, %08x, %04x): ", physbase, virtbase, length, prot));
101 /* While there is still something to map */
102 while (length)
104 int tlb;
105 int i = 0;
107 /* Check all available page sizes and try to match the best (the biggest) usable TLB entry */
108 while (allowable_pages[i].code != 0xff)
110 if ((length > allowable_pages[i].mask) && !(physbase & allowable_pages[i].mask) && !(virtbase & allowable_pages[i].mask))
111 break;
112 i++;
115 if (allowable_pages[i].code == 0xff)
117 D(bug("\n[KRN] map_region failed\n"));
118 return;
121 /* get free TLB */
122 tlb = alloc_tlb(info);
123 if (tlb == -1)
125 D(bug("\n[KRN] map_region: No more free TLB entries\n"));
126 return;
129 //D(bug("\n[KRN] TLB%02x: %08x - %08x : %08x - %08x: ", tlb,
130 //physbase, physbase + allowable_pages[i].mask,
131 //virtbase, virtbase + allowable_pages[i].mask));
133 /* Do really write to the tlb */
134 asm volatile("tlbwe %0,%3,0; tlbwe %1,%3,1; tlbwe %2,%3,2"
135 ::"r"(virtbase | allowable_pages[i].code | TLB_V), "r"(physbase | extra), "r"(prot), "r"(tlb));
136 //D(bug("%08x %08x %08x ", virtbase | allowable_pages[i].code | 0x200, physbase, prot));
138 length -= allowable_pages[i].mask + 1;
139 physbase += allowable_pages[i].mask + 1;
140 virtbase += allowable_pages[i].mask + 1;
143 tlb_temp -= info->free;
144 D(bug("%2d TLB%s\n", tlb_temp, tlb_temp > 1 ? "s":""));
147 static void free_remaining(struct tlb_info *info)
149 int tlb;
151 if (info->free == 0)
152 return;
154 tlb = alloc_tlb(info);
155 D(bug("[KRN] TLB%02x: Clear\n", tlb));
156 free_remaining(info);
157 free_tlb(info, tlb);
160 #if DEBUG
161 struct tlb { int tlb; uint32_t reg[3]; };
163 static void tlb_dump_entry(const struct tlb *tlb)
165 uint32_t tlb_0, tlb_1, tlb_2;
166 uint32_t phys, virt, size;
168 tlb_0 = tlb->reg[0];
169 tlb_1 = tlb->reg[1];
170 tlb_2 = tlb->reg[2];
172 if (!(tlb_0 & TLB_V))
173 return;
175 size = 1024 << (2 * ((tlb_0 >> 4) & 0xf));
176 virt = tlb_0 & ~((1 << 10)-1);
177 phys = tlb_1 & ~((1 << 10)-1);
179 D(bug("[KRN] TLB%02x: ",tlb->tlb));
180 D(bug("%c%c%c%c%c%c%c%c%c%c%c ",
181 (tlb_2 & TLB_W) ? 'W' : '-',
182 (tlb_2 & TLB_I) ? 'I' : '-',
183 (tlb_2 & TLB_M) ? 'M' : '-',
184 (tlb_2 & TLB_G) ? 'G' : '-',
185 (tlb_2 & TLB_E) ? 'E' : '-',
186 (tlb_2 & TLB_UR) ? 'r' : '-',
187 (tlb_2 & TLB_UW) ? 'w' : '-',
188 (tlb_2 & TLB_UX) ? 'x' : '-',
189 (tlb_2 & TLB_SR) ? 'r' : '-',
190 (tlb_2 & TLB_SW) ? 'w' : '-',
191 (tlb_2 & TLB_SX) ? 'x' : '-'));
192 D(bug("%08x - %08x : %08x: 0:%08x 1:%08x 2:%08x\n",
193 virt, virt + size - 1, phys,
194 tlb_0, tlb_1, tlb_2));
197 static int tlbcmp(const void *a, const void *b)
199 const struct tlb *ta =a , *tb = b;
201 if (ta->reg[0] < tb->reg[0])
202 return -1;
203 if (ta->reg[0] == tb->reg[0])
204 return 0;
205 return 1;
208 #include <stdlib.h>
210 static void tlb_dump(void)
212 int tlb;
213 struct tlb tlbs[64];
214 D(static int some_bss);
215 D(static int some_data=0xdeadcafe);
217 D(bug("[KRN] Executing at %p, stack at %p, bss at %p, data at %p\n", __builtin_return_address(0), __builtin_frame_address(0), &some_bss, &some_data));
218 for (tlb = 0; tlb < 64; tlb++) {
219 asm volatile("tlbre %0,%3,0; tlbre %1,%3,1; tlbre %2,%3,2"
220 :"=r" (tlbs[tlb].reg[0]),
221 "=r" (tlbs[tlb].reg[1]),
222 "=r" (tlbs[tlb].reg[2])
223 :"r"(tlb));
224 tlbs[tlb].tlb = tlb;
227 qsort(tlbs, 64, sizeof(tlbs[0]), tlbcmp);
229 for (tlb = 0; tlb < 64; tlb++)
230 tlb_dump_entry(&tlbs[tlb]);
232 #endif
234 void mmu_init(struct TagItem *tags)
236 uintptr_t krn_lowest = krnGetTagData(KRN_KernelLowest, 0, tags);
237 uintptr_t krn_highest = krnGetTagData(KRN_KernelHighest, 0, tags);
238 uintptr_t krn_base = krnGetTagData(KRN_KernelBase, 0, tags);
239 struct tlb_info info = {
240 .bitmap = { ~0, ~0 },
241 .free = 64
244 uint32_t pvr = rdspr(PVR);
246 D(bug("[KRN] MMU Init\n"));
247 D(bug("[KRN] lowest = %p, base = %p, highest = %p\n", krn_lowest, krn_base, krn_highest));
248 D(bug("[KRN] Kernel size: %dKB code, %dKB data\n", (krn_highest - krn_base)/1024, (krn_base - krn_lowest)/1024));
250 D(tlb_dump());
253 * In order to reduce the usage of TLB entries, align the kernel regions.
254 * It wastes a tiny bit of RAM but saves a lot of TLB entries.
257 /* 4K granularity for data sections */
258 krn_lowest &= 0xfffff000;
259 /* 64K granularity for code sections */
260 krn_highest = (krn_highest + 0xffff) & 0xffff0000;
262 /* MEMORY
264 * Please note the 'TLB_W' flag - this is needed to support
265 * the AmigaOS semantic of the CacheClearE() routines,
266 * which appear to assume Write-Through caching.
270 * The very first entry has to cover the executable part of kernel,
271 * where exception handlers are located
273 map_region(&info, 0x0, krn_base, 0xff000000 + krn_base, krn_highest - krn_base, TLB_SR | TLB_SX | TLB_SW | TLB_UR | TLB_UX | TLB_W);
274 /* Now the data area for kernel. Make it read/write for both user and supervisor. No execution allowed */
275 map_region(&info, 0x0, krn_lowest, 0xff000000 + krn_lowest, krn_base - krn_lowest, TLB_SR | TLB_SW | TLB_UR | TLB_UW | TLB_W);
276 /* The low memory will be RW for the supervisor mode, RO from user */
277 map_region(&info, 0x0, 0, 0xff000000, krn_lowest, TLB_SR | TLB_SW | TLB_UR | TLB_W);
279 /* The regular RAM, make 1GB of it - amcc440 cannot do more. */
280 map_region(&info, 0x0, krn_highest, krn_highest, 0x40000000 - krn_highest, TLB_SR | TLB_SW | TLB_UR | TLB_UW | TLB_SX | TLB_UX | TLB_W);
282 /* PERIPHERALS
284 * Note the 'TLB_G | TLB_I' flags - this is need to prevent caching, and
285 * ensure that all operations to peripheral address space is strictly
286 * ordered.
289 if (krnIsPPC440(pvr)) {
290 D(bug("[KRN] MMU: Configure for PPC440\n"));
291 /* map some 440EP peripherials bus */
292 map_region(&info, 0x0, 0x80000000, 0x80000000, 0x20000000, TLB_SR | TLB_SW | TLB_UR | TLB_UW | TLB_G | TLB_I );
293 /* map the PCI bus */
294 map_region(&info, 0x0, 0xa0000000, 0xa0000000, 0x40000000, TLB_SR | TLB_SW | TLB_UR | TLB_UW | TLB_G | TLB_I );
295 /* PCI control registers and onboard devices */
296 map_region(&info, 0x0, 0xe0000000, 0xe0000000, 0x10000000, TLB_SR | TLB_SW | TLB_UR | TLB_UW | TLB_G | TLB_I);
297 } else if (krnIsPPC460(pvr)) {
298 D(bug("[KRN] MMU: Configure for PPC460\n"));
299 /* PCI Memory 0x80000000-0xa0000000 */
300 map_region(&info, 0xc, 0x80000000, 0x80000000, 0x20000000, TLB_SR | TLB_SW | TLB_UR | TLB_UW | TLB_G | TLB_I );
301 /* PCI IO/Control 0xe8000000-0xef000000 */
302 map_region(&info, 0xc, 0x08000000, 0xe8000000, 0x07000000, TLB_SR | TLB_SW | TLB_UR | TLB_UW | TLB_G | TLB_I);
303 /* USB, SATA 0xef000000-0xef010000 */
304 map_region(&info, 0x4, 0xbffd0000, 0xef000000, 0x00010000, TLB_SR | TLB_SW | TLB_UR | TLB_UW | TLB_G | TLB_I);
305 /* UART, GPT, ZMII, EMAC 0xef600000-0xef610000 */
306 map_region(&info, 0x4, 0xef600000, 0xef600000, 0x00010000, TLB_SR | TLB_SW | TLB_UR | TLB_UW | TLB_G | TLB_I);
307 } else {
308 bug("[KRN] MMU: Cannot configure - unknown PVR model 0x%08x\n", pvr);
309 for(;;);
312 free_remaining(&info);
313 D(tlb_dump());
315 D(bug("[KRN] TLB status: %d used, %d free\n", 64 - info.free, info.free));
317 /* flush TLB shadow regs */
318 asm volatile("isync;");