[Milkymist-devel] Giving the LM32 an MMU

Philip Pemberton philpem at philpem.me.uk
Tue Apr 12 16:23:34 PDT 2011


Hi guys,

There's been some on-list chatter regarding adding an MMU to the 
LatticeMico32 core. I've been scribbling down some ideas regarding this, 
and figured it'd be nice to share :)

My first thought was "how do we handle supervisor mode?" -- nearly every 
MMU I've seen has some separation of user and kernel code. The x86 has 
Rings 0 to 3, the ARM has User, Supervisor, IRQ and FastIRQ modes (and 
OS Mode if you tie an ARM1 to a MEMC1), and so on.

So what we need is a way to differentiate between at least two privilege 
levels, and a way to allow Supervisor code to switch to User mode, but 
not the other way around.

There's a fairly obvious solution to this:

   - We have two bits of privilege store, giving four privilege levels.

   - We have two PRIVILEGE registers (2x2bits = one nibble). These are 
called "Last Privilege" and "Current Privilege".

   - Current Privilege is immutable. Last Privilege is mutable, but only 
to the extent that its current value can be reduced. That is to say, you 
change it from L3 (Supervisor) to L2, L1 or L0 (User), but only the CPU 
can set it back to L3.

   - On reset, both Current Priv and Last Priv are set to L3 (Supervisor)

   - When an Exception is invoked (SYSCALL, IRQ, NMI, or one of the 
Abort exceptions), the CPU copies Current Privilege into Last Privilege, 
at the same time that it copies IE into EIE.

   - When an ERET is executed, Last Priv is copied into Current Priv. 
This restores the privilege state of the code which was running before 
the exception occurred. We assume that an exception cannot occur while 
another

   - To drop privileges (assuming unallocated CSR 0x0b is used as the 
Privilege Control CSR):
        RCSR 0xb, r1
        AND r1, r1, 0xfff3  ; clear lastpriv
        ERET

     Assumption: the bit layout of CSR 0x0b is:
     [  -  | LastPriv | CurPriv ]
     31.. 4  3      2   1     0

   - To regain privileges after dropping them, we have to issue a 
SYSCALL, and set LastPriv to 0b11 before we ERET.

   - The MMU will need to know the privilege level of the 
currently-executing instruction. So we add two output pins for CurPriv.

   - Memory access rights enforcement is left up to an external MMU.

This leaves the problem of memory mapping. Whenever we change the 
mapping, we will need to flush the ICACHE and DCACHE... Other than that, 
the only potential issue is the instruction pipeline -- to deal with 
that, we add a few NOPs after the cache flush requests, and keep the 
Kernel stuff in a fixed area of memory (read as: the kernel remains 
static in both virtual and physical space; user code may move around as 
the memory allocator deems necessary).

In theory this should be backwards compatible with any previous code 
(RTEMS, the Linux-nommu port, ...), as long as the MMU defaults to a 
linear 1:1 logical/physical mapping on boot. This is similar to how the 
68000 did things -- if you didn't want privilege separation, you ignored 
that particular status bit and wrote all your code in supervisor mode. 
If you had an MMU, you could stop user code from meddling with hardware 
registers and such.


Any comments, guys?


Thanks,
-- 
Phil.
philpem at philpem.me.uk
http://www.philpem.me.uk/


More information about the Devel mailing list