In my last post we looked at the basic theory of an MMU and what it can do for us. In this post we are going to produce and understand the absolute minimum amount of code required (just 20 lines of assembler) to turn on an ARM MMU and come out the other side in one piece.
In my last post I wrote some bare metal code which ran on a BeagleBoard xM as an MLO – I’d like to extend this by running this code with the MMU switched on. I want to write the absolute minimum amount of code required to turn on an ARM MMU and to come out the other side in one piece. This post describes the basic principles of operation of an MMU – we’ll come on to writing code in my next post.
One of the most fundamental tasks of an MMU is to translate virtual addresses into physical addresses. How virtual addresses map onto physical addresses is entirely a matter of software design – the ARM MMU design provides great flexibility for helping you in this area. Just to illustrate this and to demonstrate the capability of these MMUs, I’ve come up with some perfectly valid schemes (though some of which at first may seem nonsensical):
The main difference between Linux and uClinux is that the latter is designed to work with systems without an MMU (Memory Management Unit). The main benefit of an MMU is that systems that utilise them can provide each process with it’s own virtual address space – by doing so each process is prevented from causing disruption to others.
uClinux systems do not have an MMU, all processes share a common address space and so there is often – but not always (see comments at the end of this post), no protection from processes interfering with each other. The security implications of this are widely known and understood – the applications of uClinux systems are usually such that this isn’t of great concern. However I wanted to see this for myself – I wanted to put the theory in practice and see how easy it is to disrupt another process and to see if privilege escalation can be achieved.