The SV32 page table format uses a two-level page table structure with 32-bit virtual addresses and 4KiB page sizes. The top-level page table has 1024 entries, each of which points to a second-level page table with 1024 entries. Each second-level page table entry points to a physical page frame or to another second-level page table.

Lets try to explain better using an analogy:

Imagine your computer’s memory as a vast library, carefully organized for efficient data management. In the Sv32 paging mode of RISC-V, this library is divided into sections to optimize memory access and storage.

Library: The library represents your computer’s entire memory space, typically around 4 GiB in Sv32. It’s like the grand collection of knowledge in your computer.

Catalog: Think of the catalog as the top-level page table. It’s a comprehensive index that guides you to the right section of the library. Each entry in the catalog corresponds to a specific bookshelf (page table) within the library. This top-level page table helps find the right location for data or code.

Bookshelves: In Sv32, there are 1024 of these, similar to 1024 bookshelves in our library. Each bookshelf is a second-level page table that manages a chunk of memory, usually 2 MiB. These bookshelves further subdivide the library for efficient organization and retrieval.

Books: Just as a library contains numerous books, the memory on each bookshelf is divided into smaller units called pages. In Sv32, these pages are typically 4 KiB in size. Each page can store valuable information, akin to the content of a book.

When you need to access specific data or code within your computer’s memory (the library), you begin by consulting the catalog (top-level page table).

The catalog guides you to the right bookshelf (second-level page table) that holds the data you seek. This is the first level of paging, similar to finding the right section in the library.

Once you’re on the correct bookshelf (second-level page table), you can pinpoint the exact page (4 KiB) that contains the data or code you require. This is the second level of paging, akin to finding the exact book on the bookshelf.

Page mapping code example

Below is an example of how to map a virtual page to a physical page in Sv32. This code is taken from my RISC-V kernel project, which can be found here.

void map_page(uint32_t* table1, uint32_t vaddr, paddr_t paddr, uint32_t flags) {
    if (!is_aligned(vaddr, PAGE_SIZE))
        PANIC("unaligned vaddr %x", vaddr);
 
    if (!is_aligned(paddr, PAGE_SIZE))
        PANIC("unaligned paddr %x", paddr);
 
    uint32_t vpn1 = (vaddr >> 22) & 0x3ff;  // shift 22 bits to right and mask 10 bits to extract vpn1 (First 10 bits)
    if ((table1[vpn1] & PAGE_V) == 0) {
        // Create a second level page table
        uint32_t pt_paddr = alloc_pages(1);                      // Allocate a physical page for the second level page table
        table1[vpn1] = ((pt_paddr / PAGE_SIZE) << 10) | PAGE_V;  // Set the PPN (Page Physical Number) and V (Valid) bit
    }
 
    // Add an entry to the second level page table
    uint32_t vpn0 = (vaddr >> 12) & 0x3ff;  // shift 12 bits to right and mask 10 bits to extract vpn0 (Next 10 bits)
    uint32_t* table0 = (uint32_t*)((table1[vpn1] >> 10) * PAGE_SIZE);  // Get the physical address of the second level page table
    table0[vpn0] = ((paddr / PAGE_SIZE) << 10) | flags | PAGE_V;       // Set the PPN (Page Physical Number) and V (Valid) bit
}

First the code checks if addresses are aligned to the page size. Then it extracts the vpn1 and vpn0 from the virtual address. The vpn1 is used to index into the top-level page table, and the vpn0 is used to index into the second-level page table. If the entry in the top-level page table is not valid, then a new second-level page table is created and the entry in the top-level page table is set to point to the new second-level page table. Finally, the entry in the second-level page table is set to point to the physical page frame and the flags are set.