Here's my setup:
- My kernel is an ELF file, which expects to be loaded in the higher-half (i.e. the entry point of the ELF is at 0xffff800000100000)
- In order to load the kernel code where it expects to run, I have a "bootloader" that runs first, parses the ELF headers, and configures an initial set of page tables that loads the kernel into the higher-half, then jumps into the kernel entry point. (This bootloader and the kernel ELF are concatenated together to form the kernel8.img that's actually booted)
- The kernel then creates it's own set of page tables that maps itself into the higher-half (this winds up basically creating a copy of the existing page tables, but in memory that's controlled by the kernel, not the bootloader)
- The kernel then sets TTBR1_EL, causing the MMU to use the new, kernel-defined tables.
So I read about MAIR_EL1 and how I should probably be setting most memory to be Normal, but leave the peripheral IO area as Device nGnRnE. I modified the bootloader to do that, and things do in fact run much faster, but now when the kernel attempts to set TTBR1_EL1, the CPU thinks that nothing is mapped, and I get an endless loop of exceptions (since the exception handler itself is now no longer mapped).
My initial suspicion was that I was creating the new page tables incorrectly, so I added some code to walk the entire hierarchy of tables and print out the entries. Printing all the entries is slow, but just printing them caused the problem to go away!
That made me think that this is actually a caching issue, and the writes to the new pages tables haven't actually been written to RAM before the TTBR gets updated. In that case, I just needed to make sure the writes to the new pages tables were flushed before swapping the page tables, but that doesn't seem to help. Here's my most recent attempt:
Code: Select all
dsb ish // Ensure writes to tables have completed. msr ttbr1_el1, %0 // Set the root table. tlbi vmalle1 // Flush TLB. dsb ish // Ensure TLB flush has completed. isb // Re-fetch instructions using new page tables.
I though the first "dsb ish" instruction would make sure that any pending writes in the cache were written out to main memory before the "msr" instruction could update the TTBR, but it doesn't seem to help. I also tried "dsb ishst", but that didn't seem to make a difference.
Here are the values of some other relevant registers:
Code: Select all
TCR_EL1 = 0x80100010 MAIR_EL1 = 0xff00 SCTLR_EL1 = 0x501805
Can someone with more experience working with the MMU help me figure out what I'm doing wrong?