Cannot initialize MMU on Raspberry Pi 2

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
itsmevjnk
Member
Member
Posts: 32
Joined: Fri Apr 13, 2018 10:18 am
Location: Melbourne, VIC, Australia

Cannot initialize MMU on Raspberry Pi 2

Post by itsmevjnk »

I'm trying to enable paging on Raspberry Pi 2 but to no avail. This is my implementation:
https://gitlab.com/weedboi6969/fusion/- ... 2/paging.c
https://gitlab.com/weedboi6969/fusion/- ... ging_asm.s
The problem with it is that as soon as the MMU is turned on

Code: Select all

mcr p15, 0, r0, c1, c0, 0
it goes straight to 0xC, the prefetch abort address. Any way to fix it?
Just a procrastinating uni student doing stupid things (or not doing them at all)...

SysX: https://github.com/itsmevjnk/sysx.git
User avatar
bzt
Member
Member
Posts: 1584
Joined: Thu Oct 13, 2016 4:55 pm
Contact:

Re: Cannot initialize MMU on Raspberry Pi 2

Post by bzt »

weedboi6969 wrote:

Code: Select all

mcr p15, 0, r0, c1, c0, 0
it goes straight to 0xC, the prefetch abort address. Any way to fix it?
I haven't checked your code, but that instruction (and the next) must be identity mapped for sure. Prefetch abort means as soon as the MMU is initialized, the CPU can't fetch the next instruction. In your exception handler, print out and decode the ESR_ELx register, that will tell you exactly where and what is wrong with your paging tables.

Cheers,
bzt
itsmevjnk
Member
Member
Posts: 32
Joined: Fri Apr 13, 2018 10:18 am
Location: Melbourne, VIC, Australia

Re: Cannot initialize MMU on Raspberry Pi 2

Post by itsmevjnk »

I don't have an exception handler yet, so I just placed a wfe instruction (opcode 0xe320f002) in each of the exception table entries (except the first one which is reset IIRC) so that the CPU hopefully halts when it hits an exception. I then used GDB to get the register values, and from what I can see, LR contains 0x10 which points back to the prefetch abort exception and ESR_EL2 is zero (?????). There's definitely something wrong with all of my table entries, but I can't figure out exactly what it was.
On a side note, yes, TTBR0 is storing the physical address to the translation table (but on GDB it's in TTBR0_S, and TTBR0 is still zero, can this be a problem?)
Just a procrastinating uni student doing stupid things (or not doing them at all)...

SysX: https://github.com/itsmevjnk/sysx.git
User avatar
bzt
Member
Member
Posts: 1584
Joined: Thu Oct 13, 2016 4:55 pm
Contact:

Re: Cannot initialize MMU on Raspberry Pi 2

Post by bzt »

Okay, first of all, the ARM architecture defines separate paging structures for each exception level. First you should get the CurrentEL from gdb. If that's EL2, then that means that the EL3 handler will be called, so ESR_EL3 should contain the correct error code. Also TTBR0_EL2 is used, not TTBR0_EL1. I know at first this looks like cumbersome, but believe me, in a real-OS having separated paging tables is extremely useful and makes the OS very resilient and bullet-proof. (You can also configure if an exception should raise the exception level or not, but let's not get into that just now.)

Now the real Pi has two operation modes: if you have "kernel_old" in config.txt, then no ARM initialization will be done for you, and you'll start at EL3. If you don't have config.txt, then the ARM will be initialized (see Pi zero/1 armstub and Pi2 armstub) and your code will start at EL2. So it is very important to figure out which EL your code is running at, because you'll need to check different system registers during debug.

For simplicity, I'd also recommend to use a small asm code to drop to EL1 regardless how the ARM was initialized (sorry this is AArch64 code, but the idea is the same for AArch32). Then all your code will run at supervisor privilege level, and you'll only have to care about *_EL1 registers. (EL2 and EL3 has some special and non-standard functionalities to implement secure monitor and hypervisor features. Paging in EL2 is not the same as in El1 or EL0.)

Cheers,
bzt
itsmevjnk
Member
Member
Posts: 32
Joined: Fri Apr 13, 2018 10:18 am
Location: Melbourne, VIC, Australia

Re: Cannot initialize MMU on Raspberry Pi 2

Post by itsmevjnk »

For simplicity, I'd also recommend to use a small asm code to drop to EL1 regardless how the ARM was initialized (sorry this is AArch64 code, but the idea is the same for AArch32). Then all your code will run at supervisor privilege level, and you'll only have to care about *_EL1 registers. (EL2 and EL3 has some special and non-standard functionalities to implement secure monitor and hypervisor features. Paging in EL2 is not the same as in El1 or EL0.)
Can you explain the code? I couldn't find any documentation on switching between exception levels on AArch32 that I can base on.
BTW, speaking of documentations, do you think that I should switch to developing for the RPi3+ first, considering that OS development documentations for the RPi3+ seem to be more easily accessible than that of the RPi1/2?
Just a procrastinating uni student doing stupid things (or not doing them at all)...

SysX: https://github.com/itsmevjnk/sysx.git
User avatar
bzt
Member
Member
Posts: 1584
Joined: Thu Oct 13, 2016 4:55 pm
Contact:

Re: Cannot initialize MMU on Raspberry Pi 2

Post by bzt »

weedboi6969 wrote:Can you explain the code?
Well, there's not really much to explain. It sets some system registers for the EL, and the stack for the lower EL, then it fakes an exception return to actually change the exception level. Then it's the same for EL2->EL1, just with more system registers. The comments tells you which does what.

Same code for AArch32 can be found here. I'd recommend rst's brilliant Circle library for further study btw. It's for C++ true, but has many things sorted out (including USB and NIC driver), and some things that are not in any documentation. Also available in AArch32 and AArch64, and for all Pi models.
weedboi6969 wrote:I couldn't find any documentation on switching between exception levels on AArch32 that I can base on.
Actually it's the same document for AArch32 and AArch64. Get ARM DDI0487. It is also very useful to study arm-trusted-firmware's source, which will make you realize that the ARM doc is not always correct, it is full of typos. The arm-trusted-firmware code on the other hand is always 100% correct, as it must compile and run without problems.
weedboi6969 wrote:BTW, speaking of documentations, do you think that I should switch to developing for the RPi3+ first, considering that OS development documentations for the RPi3+ seem to be more easily accessible than that of the RPi1/2?
I don't think so. I mean all Pis are pretty well documented, and the peripherals are mostly the same for RPi1/2/3/4/z (minor differences only), but that's true you have to look for some specs harder than usual. Most tutorials are both for AArch32 and AArch64, and many are compatible with all Pi models. I would only recommend AArch64 because it's Assembly is more readable, and because it has more potential in the future (but requires at least Pi3 or 4, won't run on Pi1/2/z as those only support AArch32). Now about the models, Pi3 has an advantage of being multi-core (there were a series or Pi2 with more cores too, but the basic Pi2 is single core), and at least 1G of RAM, that's why so many hobby OS targeting that. For Pi4 one reason would be even more RAM and of course GIC. Older models had a Pi-only Broadcom-specific interrupt controller, but GIC is more common and widespread one (you'll have much better chance to port your code to other ARM boards, and has lot more documentations and tutorials). If none of these matter to you, then it is really doesn't matter which Pi you choose.

Cheers,
bzt
Post Reply