2 Copyright © 1995-2014, The AROS Development Team. All rights reserved.
6 #include <aros/debug.h>
7 #include <asm/amcc440.h>
9 #include <aros/kernel.h>
11 #include "kernel_intern.h"
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]);
26 bit
+= __builtin_clz(info
->bitmap
[1]);
30 info
->bitmap
[0] &= ~(0x80000000 >> bit
);
38 info
->bitmap
[1] &= ~(0x80000000 >> (bit
-32));
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"));
55 info
->bitmap
[0] |= (0x80000000 >> entry
);
61 asm volatile("tlbwe %0,%1,0;" ::"r"(0), "r"(entry
));
63 if (info
->bitmap
[1] & (0x80000000 >> entry
))
65 D(bug("[KRN] Freeing already free TLB!!!\n"));
69 info
->bitmap
[1] |= (0x80000000 >> entry
);
75 D(bug("[KRN] Wrong TLB\n"));
80 static struct mmu_page_size
{
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 */
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
))
115 if (allowable_pages
[i
].code
== 0xff)
117 D(bug("\n[KRN] map_region failed\n"));
122 tlb
= alloc_tlb(info
);
125 D(bug("\n[KRN] map_region: No more free TLB entries\n"));
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
)
154 tlb
= alloc_tlb(info
);
155 D(bug("[KRN] TLB%02x: Clear\n", tlb
));
156 free_remaining(info
);
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
;
172 if (!(tlb_0
& TLB_V
))
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])
203 if (ta
->reg
[0] == tb
->reg
[0])
210 static void tlb_dump(void)
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])
227 qsort(tlbs
, 64, sizeof(tlbs
[0]), tlbcmp
);
229 for (tlb
= 0; tlb
< 64; tlb
++)
230 tlb_dump_entry(&tlbs
[tlb
]);
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 },
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));
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;
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
);
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
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
);
308 bug("[KRN] MMU: Cannot configure - unknown PVR model 0x%08x\n", pvr
);
312 free_remaining(&info
);
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;");