You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Andre Richter 52e0f4c850
mmu: Add more descriptive comments
6 years ago
..
.cargo First part of tutorial: 0C_virtual_memory 6 years ago
raspi3_boot 🎉 Update to Rust 2018 🎉 6 years ago
src mmu: Add more descriptive comments 6 years ago
Cargo.lock 🎉 Update to Rust 2018 🎉 6 years ago
Cargo.toml 🎉 Update to Rust 2018 🎉 6 years ago
Makefile Add nm target 6 years ago
README.md First part of tutorial: 0C_virtual_memory 6 years ago
kernel8 🎉 Update to Rust 2018 🎉 6 years ago
kernel8.img 🎉 Update to Rust 2018 🎉 6 years ago
link.ld Alignment. Binaries from newer Rust version. 6 years ago

README.md

Tutorial 0C - Virtual Memory

This is a stub

TODO: Write rest of tutorial.

Virtual memory is an immensely complex, but exciting topic. In this first lesson, we start slow and switch on the MMU using handcrafted page tables for the first 1 GiB of memory. That is the amount of DRAM the usual Raspberry Pi 3 has. As we already know, the upper 16 MiB of this gigabyte-window are occupied by the Raspberry's peripherals such as the UART.

The page tables we install alternate between 2 MiB blocks and 4 KiB blocks.

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.

Next, we map the UART into the second 2 MiB block to show the effects of virtual memory.

Everyting else is, for reasons of convenience, again identity mapped using 2 MiB blocks.

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.

Zero-cost abstraction

The MMU init code is a good example to see the great potential of Rust's zero-cost abstractions[1][2] for embedded programming.

Take this piece of code for setting up the MAIR_EL1 register using the cortex-a crate:

// First, define the two memory types that we will map. Normal DRAM type and
// device.
MAIR_EL1.write(
    // Attribute 1
    MAIR_EL1::Attr1_HIGH::Device
        + MAIR_EL1::Attr1_LOW_DEVICE::Device_nGnRE
        // Attribute 0
        + MAIR_EL1::Attr0_HIGH::Memory_OuterWriteBack_NonTransient_ReadAlloc_WriteAlloc
        + MAIR_EL1::Attr0_LOW_MEMORY::InnerWriteBack_NonTransient_ReadAlloc_WriteAlloc,
);

This piece of code is super expressive, and it makes us 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 values according to the data sheet. Looking at the generated code, we can see that despite all the type-safety and abstractions, we get super lean code:

kernel8::mmu::init::h53df3fab6e51e098:
   ...
   80768:       ed 9f 80 52     mov     w13, #0x4ff
   ...
   80778:       0d a2 18 d5     msr     MAIR_EL1, x13
   ...