I'm exploring the i.MX53 QSB and the TrustZone extensions. I run an OS in the secure world thanks to the U-Boot bootloader. Now I'm in the secure world. I have three questions :
- The first question is when should I share the memory and the interrupts between the secure and normal world ?
- The second one is how can I move to the normal world in order to run a Rich OS on it?
- The third question concerns the monitor mode code, where should I write it ? Is it a kernel module in the secure world ?
.. when should I share the memory and the interrupts between the secure and normal world ?
Memory sharing depends on your system requirements/design. It is possible to use the smc
to only use registers to share information. No one can give a generic answer on memory sharing.
It rarely makes sense to share interrupts. You would need a driver in both worlds. The whole point of trustzone is to partition hardware.
Some hardware is trustzone aware. Ie, it can change it's register set/view based on what world is executing. Generally, this hardware only has an interrupt for one world or a separate interrupt number. If you do not have a device that is trustzone aware, this is probably a foolish thing to try.
..how can I move to the normal world in order to run a Rich OS on it?
Well, this is fairly simple when you have a monitor mode. So, from the secure boot (maybe a secure OS task/thread),
- Load the normal world OS to memory.
- Setup monitor mode stack and other contexts; monitor mode will need a memory buffer to store world contexts.
- Switch to monitor mode.
- Setup memory partitioning (intially allow everything for the normal world).
- Change the
NS
bit to set normal world CP15.
- Configure CP15 registers as per boot default. Many OSs will expect that they are booting as per normal. Most trustzone CPUs do not setup the normal world CP15 registers by default.
- Mask interrupts, turn off cache, etc as required to boot normal OS.
- With
NS
bit still set, do a world switch.
The world switch is dependent on your system design. If the secure world OS only used registers R0-R12 the instructions might be like,
# NS bit is set.
msr spsr_fsxc, lr # mon_lr contains normal world mode, etc.
ldm sp, {r0 - r12, pc}^ # monitor 'sp' is a context pointer.
The ldm rX, {xxx, pc}^
will do a mode switch. The monitor 'sp' could have 13 zeros (for r0-r12) and then a normal world entry point for the 'PC'. The monitor 'lr' would have the starting mode (interrupt masked, etc) for the normal world.
NOTE: This is a simple example and it not meant for your particular OS. It is only conceptual. Specifics depend on specific normal/secure world OS requirements. Typically, you need to do all the things a boot loader would do for that platform/OS without TrustZone. As well, you need to initialize all registers in all modes. You may not care about registers the secure world doesn't use (NEON/VFP) and leave them as per boot defaults; this is more true for actual 'world switch' code.
...concerning the monitor mode code, where should I write it? Is it a kernel module in the secure world?
Monitor mode will always USE the CP15 registers of the secure world. This implies monitor mode has the MMU view, cache, etc of the secure OS. When the 'NS' bit is set and monitor mode does a mcr
or mrc
, it is setting the normal world registers. Well, technically it could be 'separate' there will probably be a lot of interaction between the secure OS and the monitor. Again, it depends on specifics. There are many types of OSs (or world contexts),
- Polling mode
- Non-preemptive
- Pre-emptive
You have permutations of the above for both the secure and normal world and the world switch handling will depend on the requirement of both. For the most complex case (Pre-emptive secure/normal), you need integration of schedulers which is OS dependent.
Think of the secure monitor code as a kind of hypervisor that mediates between the secure OS and the non-secure OS. Typically it would be some standalone bare-metal firmware which mostly just dispatches calls and interrupts to the secure OS - I suppose technically it could be made entirely integral to the secure OS, but that hurts reusability and opens up the potential for far more security holes, so would generally be strongly discouraged.
The monitor code is also what should be solely responsible for world-switching - once the secure OS is up and running it should call into the monitor, which sets SCR.NS and performs an exception return down to the non-secure world to kick off the non-secure bootloader.
As for sharing resources between secure and non-secure, that's entirely down to what you want to do - a relatively simple secure payload like a software TPM might need no shared resources at all; something like full-path content protection involves handing off buffers and entire devices between worlds and is far more complex.