It will be used by code in `mmu.rs` to request attributes for a virtual address
and the translation of the address. The function scans `KERNEL_VIRTUAL_LAYOUT`
for a descriptor that contains the queried address, and returns the respective
findings for the first entry that is a hit. If no entry is found, it returns
default attributes for normal chacheable DRAM and the input address, hence
telling the `MMU` code that the requested address should be `identity mapped`.
The page tables we install alternate between `2 MiB` blocks and `4 KiB` blocks.
Due to this default return, it is not needed to define normal cacheable DRAM
regions in `KERNEL_VIRTUAL_LAYOUT`.
The first `2 MiB` of memory are identity mapped, and therefore contain our code
and the stack. We use a single table with a `4 KiB` granule to differentiate
between code, RO-data and RW-data. The linker script was adapted to adhere to
the pagetable sizes.
### mmu.rs
Next, we map the UART into the second `2 MiB` block to show the effects of
virtual memory.
This file contains the `AArch64` specific code. It is a driver, if you like, and
the split in paging granule mentioned before is hardcoded here (`4 KiB` page
descriptors for the first `2 MiB` and `2 MiB` block descriptors for everything
else).
Everyting else is, for reasons of convenience, again identity mapped using `2
MiB` blocks.
Two static page table arrays are instantiated, `LVL2_TABLE` and `LVL3_TABLE`,
and they are populated using `get_virt_addr_properties()` and a bunch of utility
functions that convert our own descriptors to the actual `64 bit` descriptor
entries needed by the MMU hardware for the page table arrays.
Hopefully, in a later tutorial, we will write or use (e.g. from the `cortex-a`
crate) proper modules for page table handling, that, among others, cover topics
such as using recursive mapping for maintenace.
Afterwards, the [Translation Table Base Register 0 - EL1](https://docs.rs/crate/cortex-a/2.4.0/source/src/regs/ttbr0_el1.rs) is set up with the base address of the `LVL3_TABLE` and
the [Translation Control Register - EL1](https://docs.rs/crate/cortex-a/2.4.0/source/src/regs/tcr_el1.rs) is
configured.
## Adress translation with the 4 KiB LVL3 table
Finally, the MMU is turned on through the [System Control Register - EL1](https://docs.rs/crate/cortex-a/2.4.0/source/src/regs/sctlr_el1.rs). The last step also enables caching for data and instructions.
The following block diagram shows address translation by example of the UART's
Control Register (CR).
## Address translation examples
For educational purposes, in `memory.rs`, a layout is defined which allows to
access the `UART` via two different virtual addresses:
- Since we identity map the whole `Device MMIO` region, it is accessible by
asserting its physical base address (`0x3F20_1000`) after the `MMU` is turned
on.
- Additionally, it is also mapped into the last `4 KiB` entry of the `LVL3`
table, making it accessible through base address `0x001F_F000`.
The following two block diagrams visualize the underlying translations for the
two mappings, accessing the UART's Control Register (`CR`, offset `0x30`).
### Adress translation using a 2 MiB block descriptor
The MMU init code is a good example to see the great potential of Rust's
The MMU init code is also a good example to see the great potential of Rust's
zero-cost abstractions[[1]](https://blog.rust-lang.org/2015/05/11/traits.html)[[2]](https://ruudvanasseldonk.com/2016/11/30/zero-cost-abstractions) for embedded programming.
Take this piece of code for setting up the `MAIR_EL1` register using the
@ -57,7 +149,7 @@ MAIR_EL1.write(
);
```
This piece of code is super expressive, and it makes us of `traits`, different
This piece of code is super expressive, and it makes use of `traits`, different
`types` and `constants` to provide type-safe register manipulation.
In the end, this code sets the first four bytes of the register to certain
@ -84,6 +176,5 @@ ferris@box:~$ make raspboot
[i] MMU: Up to 40 Bit physical address range supported!
[2] MMU online.
Writing through the virtual mapping at 0x00000000001FF000.
Writing through the virtual mapping at base address 0x00000000001FF000.