Skip to content

Commit

Permalink
BIOS: 2-level page table
Browse files Browse the repository at this point in the history
  • Loading branch information
jdryg committed Aug 30, 2017
1 parent 0b82394 commit 2d4f6f8
Show file tree
Hide file tree
Showing 7 changed files with 115 additions and 75 deletions.
4 changes: 2 additions & 2 deletions bin/layout.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
"size_x" = "673.000000",
"size_y" = "405.000000",
"status" = "0",
"active" = "true",
"active" = "false",
"opened" = "true",
"location" = "30",
"child0" = "-1",
Expand Down Expand Up @@ -110,7 +110,7 @@
"size_x" = "673.000000",
"size_y" = "405.000000",
"status" = "0",
"active" = "false",
"active" = "true",
"opened" = "true",
"location" = "30",
"child0" = "-1",
Expand Down
Binary file modified bios/src/bios
Binary file not shown.
115 changes: 73 additions & 42 deletions bios/src/libkernel/internal/page_table.c
Original file line number Diff line number Diff line change
@@ -1,33 +1,48 @@
#include "page_table.h"
#include "../memory.h"
#include "../kernel.h" // kassert()
#include "../../devices/ram.h"

#define PAGE_SHIFT 12
#define PAGE_NUMBER_MASK 0xFFFFF000
#define PAGE_OFFSET_MASK 0x00000FFF

// TODO: Pass the RAM device instead and let the PageTable allocate as much RAM
// as it needs (e.g. in case I decide to implement a 2-level PT);
PageTable* pageTableInit(void* mem)
void pageTableInit(PageTable* pt, RAM* ram)
{
if(!mem) {
return 0;
}

PageTable* pt = (PageTable*)mem;
kmemset(pt->m_PTE, 0, sizeof(PageTableEntry) * 1024);

return pt;
pt->m_RAM = ram;
pt->m_PTE = (PageTableEntry*)ramAllocPage(ram);
kmemset(pt->m_PTE, 0, sizeof(PageTableEntry) * 1024);
}

int pageTableInsert(PageTable* pt, uint32_t va, uint32_t pa, uint32_t r, uint32_t w, uint32_t x, uint32_t u, uint32_t g)
{
// kprintf("ptInsert(%08X, %08X, %u, %u, %u)\n", va, pa, r, w, x);

uint32_t vpn = (va & PAGE_NUMBER_MASK) >> PAGE_SHIFT;

PageTableEntry* pte = &pt->m_PTE[vpn & 1023];
kassert(pte->m_Valid == 0, "pageTableInsert(): Tried to allocate an already used page. 2-level page table is required.");
const uint32_t vpn = (va & PAGE_NUMBER_MASK) >> PAGE_SHIFT;
const uint32_t vpn0 = vpn & 1023;
const uint32_t vpn1 = (vpn >> 10) & 1023;

PageTableEntry* dirPage = &pt->m_PTE[vpn1];
if(dirPage->m_Valid == 0) {
// Allocate a page and assign it to this master page table entry.
uint32_t dirPagePhysicalAddr = (uint32_t)ramAllocPage(pt->m_RAM);
dirPage->m_Valid = 1;
dirPage->m_Read = 0;
dirPage->m_Write = 0;
dirPage->m_Execute = 0;
dirPage->m_UserModeAccessible = 0;
dirPage->m_Global = 0;
dirPage->m_Accessed = 0;
dirPage->m_Dirty = 0;
dirPage->m_RSW = 0;
dirPage->m_PhysicalPageNumber = (dirPagePhysicalAddr & PAGE_NUMBER_MASK) >> PAGE_SHIFT;

// Clear the newly allocated page.
kmemset((PageTableEntry*)dirPagePhysicalAddr, 0, sizeof(PageTableEntry) * 1024);
}

const uint32_t dirPagePhysicalAddr = dirPage->m_PhysicalPageNumber << PAGE_SHIFT;
PageTableEntry* pte = ((PageTableEntry*)dirPagePhysicalAddr) + vpn0;

kassert(pte->m_Valid == 0, "pageTableInsert(): Tried to allocate an already used page.");

pte->m_Valid = 1;
pte->m_Read = r;
Expand All @@ -40,43 +55,59 @@ int pageTableInsert(PageTable* pt, uint32_t va, uint32_t pa, uint32_t r, uint32_
pte->m_RSW = 0;
pte->m_PhysicalPageNumber = (pa & PAGE_NUMBER_MASK) >> PAGE_SHIFT;

return 1;
return 1;
}

uint32_t pageTableVPN2PPN(PageTable* pt, uint32_t vpn)
{
PageTableEntry* pte = &pt->m_PTE[vpn & 1023];
if(pte->m_Valid == 0) {
return 0;
}
const uint32_t vpn0 = vpn & 1023;
const uint32_t vpn1 = (vpn >> 10) & 1023;

PageTableEntry* dirPage = &pt->m_PTE[vpn1];
if(dirPage->m_Valid == 0) {
return 0;
}

return pte->m_PhysicalPageNumber;
const uint32_t dirPagePhysicalAddr = dirPage->m_PhysicalPageNumber << PAGE_SHIFT;
PageTableEntry* pte = ((PageTableEntry*)dirPagePhysicalAddr) + vpn0;
if(pte->m_Valid == 0) {
return 0;
}

return pte->m_PhysicalPageNumber;
}

void* pageTableVA2PA(PageTable* pt, void* va)
{
uint32_t vpn = ((uint32_t)va & PAGE_NUMBER_MASK) >> PAGE_SHIFT;
uint32_t offset = (uint32_t)va & PAGE_OFFSET_MASK;
uint32_t vpn = ((uint32_t)va & PAGE_NUMBER_MASK) >> PAGE_SHIFT;
uint32_t offset = (uint32_t)va & PAGE_OFFSET_MASK;

uint32_t ppn = pageTableVPN2PPN(pt, vpn);
if(ppn == 0) {
return 0;
}
uint32_t ppn = pageTableVPN2PPN(pt, vpn);
if(ppn == 0) {
return 0;
}

return (void*)((ppn << PAGE_SHIFT) | offset);
return (void*)((ppn << PAGE_SHIFT) | offset);
}

PageTableEntry* pageTableGetNextAllocPage(PageTable* pt, PageTableEntry* prev)
void pageTableFree(PageTable* pt)
{
prev = prev ? prev + 1 : &pt->m_PTE[0];

while((uint32_t)(prev - pt->m_PTE) < 1024) {
if(prev->m_Valid) {
return prev;
}

++prev;
}

return 0;
PageTableEntry* pde = pt->m_PTE;
for(uint32_t i = 0;i < 1024;++i) {
if(pde->m_Valid) {
PageTableEntry* dirPage = (PageTableEntry*)(pde->m_PhysicalPageNumber << PAGE_SHIFT);

PageTableEntry* pte = dirPage;
for(uint32_t j = 0;j < 1024;++j) {
if(pte->m_Valid) {
ramFreePage(pt->m_RAM, (void*)(pte->m_PhysicalPageNumber << PAGE_SHIFT));
}
++pte;
}
ramFreePage(pt->m_RAM, dirPage);
}

++pde;
}
ramFreePage(pt->m_RAM, pt->m_PTE);
}
9 changes: 6 additions & 3 deletions bios/src/libkernel/internal/page_table.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@

#include <stdint.h>

typedef struct RAM RAM;

typedef struct PageTableEntry
{
uint32_t m_Valid : 1;
Expand All @@ -19,15 +21,16 @@ typedef struct PageTableEntry

typedef struct PageTable
{
PageTableEntry m_PTE[1024];
RAM* m_RAM;
PageTableEntry* m_PTE;
} PageTable;

PageTable* pageTableInit(void* mem);
void pageTableInit(PageTable* pt, RAM* ram);
int pageTableInsert(PageTable* pt, uint32_t va, uint32_t pa, uint32_t r, uint32_t w, uint32_t x, uint32_t u, uint32_t g);

uint32_t pageTableVPN2PPN(PageTable* pt, uint32_t vpn);
void* pageTableVA2PA(PageTable* pt, void* va);

PageTableEntry* pageTableGetNextAllocPage(PageTable* pt, PageTableEntry* prev);
void pageTableFree(PageTable* pt);

#endif
17 changes: 5 additions & 12 deletions bios/src/libkernel/kernel.c
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,8 @@ int kexec(const char* path, int argc, char** argv)
fd_stderr->m_Type = FD_TYPE_TTY;

// Allocate a page of RAM for the Page Table and set it to the current Task.
PageTable* pageTable = pageTableInit(ramAllocPage(&g_ExternalRAM));
PageTable* pageTable = (PageTable*)kmalloc(sizeof(PageTable));
pageTableInit(pageTable, &g_ExternalRAM);
kassert(pageTable != 0, "(x) kexec(): Not enough memory for page table.");
taskSetPageTable(task, pageTable);

Expand Down Expand Up @@ -290,7 +291,7 @@ int kexec(const char* path, int argc, char** argv)
g_ProgramTask = task;

// Switch to U-mode
_switchToUMode((uint32_t)pageTable, hdr.e_entry, stackTop, &g_MainTask->m_Frame[0]);
_switchToUMode((uint32_t)pageTable->m_PTE, hdr.e_entry, stackTop, &g_MainTask->m_Frame[0]);

return 1;
}
Expand All @@ -303,16 +304,8 @@ int kkill()
taskFreeAllFileDescriptors(g_ProgramTask);

// Deallocate all allocated RAM pages including the PageTable itself.
PageTableEntry* pte = pageTableGetNextAllocPage(g_ProgramTask->m_PageTable, 0);
while(pte) {
uint32_t physicalAddr = (pte->m_PhysicalPageNumber << 12);
// kmemset((void*)physicalAddr, 0, PAGE_SIZE);
ramFreePage(&g_ExternalRAM, (void*)physicalAddr);

pte = pageTableGetNextAllocPage(g_ProgramTask->m_PageTable, pte);
}
kmemset(g_ProgramTask->m_PageTable, 0, PAGE_SIZE);
ramFreePage(&g_ExternalRAM, g_ProgramTask->m_PageTable);
pageTableFree(g_ProgramTask->m_PageTable);
kfree(g_ProgramTask->m_PageTable);

// Destroy the task
taskDestroy(g_ProgramTask);
Expand Down
3 changes: 2 additions & 1 deletion src/riscv/core_single_cycle.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,10 @@ void cpuPageTableWalk(CPU* cpu, MemoryMap* mm, TLB* tlb, uint32_t satp, uint32_t
// 1. Let pageTablePPN be satp.ppn * PAGESIZE, and let level = LEVELS - 1. (For Sv32, PAGESIZE = 212 and LEVELS = 2.)
uint32_t pageTablePhysicalAddr = (satp & SATP_PTPPN_MASK) << kPageShift;
uint32_t level = 1;
uint32_t vpnLevel[2] = { vpn & 1023, (vpn >> 10) & 1023 };
while (true) {
// 2. Let pte be the value of the PTE at address a + va.vpn[i] * PTESIZE. (For Sv32, PTESIZE=4.)
const uint32_t pageTableEntryPhysicalAddr = pageTablePhysicalAddr + (vpn * sizeof(PageTableEntry));
const uint32_t pageTableEntryPhysicalAddr = pageTablePhysicalAddr + (vpnLevel[level] * sizeof(PageTableEntry));
PageTableEntry pte;
if (!mmRead(mm, pageTableEntryPhysicalAddr, 0xFFFFFFFF, pte.m_Word)) {
// 2. If accessing pte violates a PMA or PMP check, raise an access exception.
Expand Down
42 changes: 27 additions & 15 deletions src/riscv/cpu.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -182,21 +182,33 @@ bool cpuMemGet(CPU* cpu, MemoryMap* mm, uint32_t virtualAddress, uint32_t byteMa

const uint32_t virtualPageNumber = (virtualAddress & kVirtualPageNumberMask) >> kPageShift;

// TODO: Multilevel page table walk (check RWX for all zeros and move to the next level).
const uint32_t pageTablePPN = satp & SATP_PTPPN_MASK;
const uint32_t pageTablePhysicalAddr = pageTablePPN << kPageShift; // PT address should be always aligned to page boundaries.
const uint32_t pageTableEntryPhysicalAddr = pageTablePhysicalAddr + (virtualPageNumber * sizeof(PageTableEntry));
PageTableEntry pte;
if (!mmRead(mm, pageTableEntryPhysicalAddr, 0xFFFFFFFF, pte.m_Word)) {
return false;
}

if (!pte.m_Fields.m_Valid) {
return false;
uint32_t pageTablePhysicalAddr = (satp & SATP_PTPPN_MASK) << kPageShift;
uint32_t level = 1;
uint32_t vpnLevel[2] = { virtualPageNumber & 1023, (virtualPageNumber >> 10) & 1023 };
while (true) {
const uint32_t pageTableEntryPhysicalAddr = pageTablePhysicalAddr + (vpnLevel[level] * sizeof(PageTableEntry));
PageTableEntry pte;
if (!mmRead(mm, pageTableEntryPhysicalAddr, 0xFFFFFFFF, pte.m_Word)) {
return false;
} else {
if (!pte.m_Fields.m_Valid || (pte.m_Fields.m_Read == 0 && pte.m_Fields.m_Write == 1)) {
return false;
} else {
if (pte.m_Fields.m_Read != 1 && pte.m_Fields.m_Execute != 1) {
if (level == 0) {
return false;
}

--level;
pageTablePhysicalAddr = pte.m_Fields.m_PhysicalPageNumber << kPageShift;
continue;
}

const uint32_t offset = virtualAddress & kAddressOffsetMask;
const TLB::physical_addr_t physicalAddress = (pte.m_Fields.m_PhysicalPageNumber << kPageShift) | offset;
return mmRead(mm, physicalAddress, byteMask, data);
}
}
}

const uint32_t offset = virtualAddress & kAddressOffsetMask;
const TLB::physical_addr_t physicalAddress = (pte.m_Fields.m_PhysicalPageNumber << kPageShift) | offset;
return mmRead(mm, physicalAddress, byteMask, data);
}
}

0 comments on commit 2d4f6f8

Please sign in to comment.