Lab 2 - paging
Recall from the lectures on Paging, how the main memory is divided into chunks (generally of 4KB) called pages for efficient memory management and process isolation.
What does it mean in code?
-
Note that the CPU generates addresses through the load and store instructions. For instance
ld x1, 0(x2)
expresses that the value stored at address = 0 + value ofx2
should be written to x1. But this address is the virtual address and we cannot use it to access the memory. -
Thus we need a address translation system (which is stored as a look-up table, indexed using the virtual address).
-
In any paging system (single or mutli level), we simply need the mapping between the virtual and the physical page numbers, since the offset within a page remains the same.
-
Since each process has its own translation, the
satp
register specifies where is the root page table located in memory (note that this is a physical address, in fact all page table entries contain physical addresses only, the hardware has nothing to do with virtual addresses) -
So all we have to do is get the physical page number, index inside it using the offset and we are all set.
Steps for the lab:
-
In main switch from machine mode to supervisor mode.
-
Initialise your page tables as given below. We will perform Sv39 paging
- In the data section of the code, set
satp_config
to0x8000000000081000
. This tells your OS where to look for when the translation is needed. - Note that the first 4 bits specify the mode (8/9/10 for Sv39, Sv48 and Sv57 resp.). The last 12 bits of the address are dropped since we want to specify the start of the page only. Therefore the address becomes
0x8000000000081000
- Make sure to do
.align 12
. This is required to make sure that data and instructions remain in different pages. - Map the page table entries to the pages shown. For example for setting the 0th entry of the root page table to the page with base address
0x82001000
:
li t0, 0x0081000000 li t1, 0x0082001 slli t1, t1, 10 ori t1, t1, 0x1 sd t1, 0(t0)
Recall that the last 10 bits are the permission bits. For non-leaf pages we may get away with setting only the valid bit to 1.
- In the data section of the code, set
-
Now set the leaf page table's entries to pages that contain your data and instructions.
- The code section (without translation, bare metal) by default starts at
0x80000000
. This should be retained i.e., this should not change after translation. 0x0
should map to where the user code starts i.e.,0x80001000
and0x1000
should map to the data section0x80002000
. (Hint : Try to figure out which Page table entries should be filled with which values, the three entries u should fill are indicated in the diagram)- Note that now we will need more permissions!
- DO NOT set the dirty bit to 1
- USER bit should be 1 only in pages concerning user code and data
- R,W,X,V,G,A should also be 1
- The code section (without translation, bare metal) by default starts at
-
Prepare to jump into the supervisor mode.
-
Copy paste the following snippet for satp_configuration and TLB settings.
la t1, satp_config # load satp val
ld t2, 0(t1)
sfence.vma zero, zero
csrrw zero, satp, t2
sfence.vma zero, zero
li t4, 0
csrrw zero, sepc, t4
sret
- Write the following user code:( use
.align 12
)
user_code:
la t1,var1
la t2,var2
la t3,var3
la t4,var4
j user_code
- Add the following to the data section of your code:
.data
.align 12
var1: .word 1
var2: .word 2
var3: .word 3
var4: .word 4
-
After jumping into the user mode, you should observe that the addresses are now virtual in spike. This means your translation was successful and while writing user code u can freely assume addresses to be virtual and write programs accordingly!!
-
To compile use the following commands:
$ riscv64-unknown-elf-gcc -nostartfiles -T linker.ld <your-code>.S
$ riscv64-unknown-elf-objdump -D a.out > dump