Virtual memory
Physical memory mapping in userland
Memory is addressed with 16 bits. A segment is the upper byte of that address, being 256 bytes. Physical memory is mapped to the userspace’s virtual memory. In turn, the MMU reads from the map in order to put the link between virtual (16 bit) and physical (also 16 bit) memory.
1 Address space
QCPU has a single virtual space, divided into multiple address spaces. Each process has its own userland, whilst a kernel is always loaded on the same address offset regardless of which process is currently being executed [1] [2, Ch. 5].
64 KiB
(16 bit)
0x0000 0xC000 0xFFFF
|------------------------|--------|
| userland | kernel |
48 KiB 16 KiB
A kernel can be loaded from address 0xC000
(which may be customisable1). QCPU will signal a segmentation fault when processes residing in userspace attempt to access the kernel address space when not in a privileged CPU mode. Likewise, trying to perform write instructions to segments with the unmapped
or readonly
flags will also signal a segfault.
1.1 Kernel space
Kernel space is divided into two parts. Text and global mutable data of the kernel is loaded in the first part. It primarily has all fixed kernel structures ready to be used. It also includes the kernel memory map, which maps the second part of the kernel space to physical memory2. It allows the kernel to generically modify data whilst still being in the virtual context [1]:
- ‘Fixed’ kernel space;
- ‘Variable’ kernel space.
16 KiB
0xC000 0xD000 0xFFFF
|-----------|-----------|
| userland | kfixed | kvariable |
12 KiB 4 KiB
System calls (like any other hardware interrupt) put the CPU into kernel mode and jump to designated parts of kernel space, which doesn’t trigger a segfault:
, 3 ; load address of string from stack
mldw sfb, 0x00 ; mask 0
imm rz@fopen sysc
Putting the CPU out of kernel mode is done by jumping out of the kernel section.
1.2 MMU: memory management unit
Address spaces are stored by reading from designated areas of the memory. In order to bootstrap the space, the fixed kernel space is set through the MMU device (specifically, the DMAC interface). A set segment in fixed kernel space directs which variable kernel space segments are loaded. Lastly, a set segment in variable kernel space directs which userspace segments are loaded.
Cleverly, this allows the kernel to switch userland processes by performing a couple of memory instructions: setting the userspace mapping reference in fixed kernel space. Flushing the MMU reloads its values, which a jump can take effect.
64 KiB
/-------------\
{ } /---------{ | }
|-----------|[M]--------|[M]--------|
| userland | kfixed | kvariable |
48 KiB 12 KiB 4 KiB
Figure above depicts the memory map chain from kfixed
, to kvariable
, to userland
. kfixed
is a special section mapped by the MMU through the DMAC interface.
1.3 DMAC: direct memory access controller
A DMAC operates the MMU and its managed memory. Through it’s I/O (device 1
, as device 0
is the master boot device) interface, only operable in kernel mode and mapped to a section in fixed kernel space, reading from and writing to its registers controls the physical operations on memory.
0 : kfixedl
: an address to the section of 12 KiB of linear physical memory;1 : kfixedh
: high byte of the above.
A ‘boot sector’ of a filesystem is executed in kernel mode at address 0xE000
, which aims to overlay its kernel address space from the boot firmware into the specific filesystem’s kernel by setting the appropriate kfixed*
registers of the DMAC.
2 Memory layout
For the userland, memory is divided into multiple sections per the process memory map. In between the square brackets is the amount of segments necessary for that section. Kernel objects are laid out the same with the only exceptions being that mappings (i.e. heap) don’t function.
userland 48K |[I][G][LLL][N][TTTT]stack... ...heap|
kfixed 12K |[I][G][MLL][N][TTTT]stack...|
kvariable 4K |[M]data... ext...|
I
: instance data (indirectly mutable)- pointers to global, text, stack, sig/int/sysc map
- for kernel: memory and sysc maps ptrs
- for userspace: signal map ptr, process/thread ids
- for userspace: context register copy
G
: globals (mutable)L
: g(l)obals extension (mutable)- pointed to by globals
T
: text (readonly)M
: memory mapN
: sig/int/sysc table- for kernel: hardware interrupts, including internal interrupts and syscall map
- for userspace: process signal map
TODO: shared objects and their address offsets in relation with store/loads, research dynamic linkers
TODO: custom memory layout through linker script
2.1 Extension space (kvariable
)
Kernel extensions (including filesystem extensions) are loaded at a specific address in kvariable
space in order to be dynamically loaded. Having them be dynamically mapped removes total space overhead compared to loading it into kfixed
space at all times [1].
Bootable partitions are also loaded in kvariable
space by the initial boot sector. Section 1.3 explains the exact DMAC behaviour.