Jason Xu 9c5c2813a0 Update QEMU command in all README and Makefile 2 years ago
..
Makefile 426205e2a3 Added makefiles for LLVM Clang 5 years ago
Makefile.clang 9c5c2813a0 Update QEMU command in all README and Makefile 2 years ago
Makefile.gcc 9c5c2813a0 Update QEMU command in all README and Makefile 2 years ago
OLVASSEL.md df66fe7f9c More comments on using the MMU 5 years ago
README.md df66fe7f9c More comments on using the MMU 5 years ago
gpio.h ed7cb505f9 Fixed typos and more tutorials 6 years ago
kernel8.img 8ce0f75b47 Recompiled with latest gcc 4 years ago
link.ld ed7cb505f9 Fixed typos and more tutorials 6 years ago
main.c ed7cb505f9 Fixed typos and more tutorials 6 years ago
mbox.c 214885df63 compute the mailbox cmd only once 6 years ago
mbox.h ed7cb505f9 Fixed typos and more tutorials 6 years ago
mmu.c faf8ad78fd Fix the bug, which will cause the write to the first .data 4k pages abort. 5 years ago
mmu.h ed7cb505f9 Fixed typos and more tutorials 6 years ago
start.S ea4691947c Improve comments wrt stack setup 3 years ago
uart.c c59ad439f4 Enable UART0 FIFOs 3 years ago
uart.h ed7cb505f9 Fixed typos and more tutorials 6 years ago

README.md

Tutorial 10 - Virtual Memory

We came to the simplest and most difficult tutorial at the same time. It's simple as all we are going to do is fill up an array, and set some registers. The difficulty lies in knowing what values should we put in that array.

I assume you have a fair knowledge about page translation mechanism on AMD64. If not, I strongly suggest to do some tutorial on it before you continue. ARMv8's MMU is much much more complex and featureful than it's AMD64 counterpart. It is definitely not good to start with.

As AMD64's address translation is very simple, it has one paging table register, it splits memory into 4k pages only with 4 levels, and it has one well defined hole in the address space. ARMv8 is much more powerful. You can set the size of the pageframe, the number of translation levels, you can concatenate translation tables for a given level, and you can even configure the hole's size. It is impossible to cover all of these in one tutorial. Therefore what I'm going to do is configuring ARMv8 MMU to be similar to AMD64's as much as possible. That is: we're going to use 4k pageframes with 2M blocks and 512G address space (3 levels) with the 4th level in two registers. Think of it this way: on AMD64 you would have a 4th level table, pointed by CR3. On ARMv8, we have TTBR0 register which holds the first entry of that 4th level table, and TTBR1 which holds the last, 512th entry of the table, therefore we don't need the 4th level table at all. Everything between (memory mapped by the 2nd-511th entries) is in the hole, with other words they are non-canonical addresses.

The page translation table looks the same: we have 64 bit entries with a physical address and attribute bits in it at each level. But in ARMv8 you have far more options. You can set cacheability, shareability and accessibility as well. You also have a special register holding a page attribute array, and you index that with bits in the page translation attributes.

We are going to translate virtual address space as follows: lower half will be identity mapped in 2M blocks, except the first block which will be mapped by 4k frames. In the higher half, at -2M we will map the MMIO of UART0.

Maybe it's not obvious, but the translation tree can point to individual pages. This would be a nightmare to handle in C, therefore we do a little trick here. We use only one contiguous memory area for all the translation tables, and we access them with a single paging array. Because we choose 4k as page size, and one translation entry is 8 bytes, that means we have 512 entries on each page. Therefore indeces 0..511 belong to the first page, 512..1023 to the second and so forth. With other words, the address of paging[0] equals to _end (first page), and paging[512] equals to _end + PAGESIZE (second page).

Mmu.h, mmu.c

mmu_init() function to initialize Memory Management Unit. Here we set up the paging array and we tell the CPU to use it.

Start

This time we also have to grant access to the system control register.

Link.ld

This time we need page alignment for the data and the end label.

Main

We set up page translations, and then we print to the console with both identity mapped and higher half mapped MMIO.