Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions src/kernel/hal/arch/riscv/pagefault.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#ifndef HAL_RISCV_PAGEFAULT
#define HAL_RISCV_PAGEFAULT

#include <address_space/address_space.h>
#include <stddef.h>
#include <stdint.h>

#include "hal/arch/riscv/pmap.h"
#include "hal/arch/riscv/pte.h"
#include "hal/include/address_space.h"
#include "libcore/error.h"

/// Mock process address space
extern address_space_t as;
/**
* @brief Mock pagefault resolution in virtual address space map.
*/
error_t vm_handle_pagefault(uintptr_t va, paddr_t pt, hal_address_region_flag_t fault_prot, [[maybe_unused]] bool usermode) {
for(size_t i = 0; i < as.regions_count; ++i) {
address_space_region_t as_region = as.regions[i];
if(va >= (uintptr_t)as_region.addr && va < ((uintptr_t)as_region.addr + as_region.size)) {
if((as_region.flags & fault_prot) != 0) {
pte_t* ptep = pmap_pte_ptr_lookup(va, pt, PMAP_PAGE);
pte_t pte_flags = as_flags_to_pte((as_region.flags));

paddr_t pa;
error_t err = pmap_allocate_page(&pa, PMAP_PAGE);
if(err != ERR_NONE)
return err;
*ptep = pa_to_pte(pa) | pte_flags;
return ERR_NONE;
}
//Violated permission
return ERR_NOT_VALID;
}
}
return ERR_OUT_OF_BOUNDS;
}
#endif /*!HAL_RISCV_PAGEFAULT */
195 changes: 195 additions & 0 deletions src/kernel/hal/arch/riscv/pmap.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
#include "pmap.h"

#include <libcore/error.h>
#include <libcore/memory_types.h>
#include <libcore/string.h>
#include <memory_management/include/physical_memory/manager.h>

#include "hal/include/address_space.h"
#include "pte.h"

error_t pmap_allocate_page(paddr_t* pa, pmap_page_t page_type) {
physical_memory_region_t phys_region;
error_t err = phys_mem_alloc_frame(g_pmap_frame_order[page_type], &phys_region);
if (err == ERR_NONE) {
memset(physical_to_effective(phys_region.addr), 0, g_pmap_frame_order[page_type]);
*pa = (paddr_t)phys_region.addr;
}
return err;
}

error_t pmap_unallocate_page(paddr_t pa, pmap_page_t page_type) {
physical_memory_region_t phys_region = {.addr = (__phys void*)pa, .size = g_pmap_frame_order[page_type]};
error_t err = phys_mem_free_frame(phys_region);
if (err == ERR_NOT_VALID) {
/* TODO: Deal with unallocating invalid frame */
return ERR_NOT_VALID;
}
return ERR_NONE;
}

static error_t pmap_free_ptes_l0(pagetable_t* pt_l0) {
for (int i = 0; i < PTE_COUNT; i++) {
pte_t pte = pt_l0[i];
if (!pte_is_valid(pte))
continue;
error_t err = pmap_unallocate_page(pte, PMAP_PAGE);
if (err != ERR_NONE) {
return err;
}
pte = pte_mark_invalid(pte);
pt_l0[i] = pte;
}
return ERR_NONE;
};

static error_t pmap_free_ptes_l1(pagetable_t* pt_l1) {
error_t err = ERR_NONE;
for (int i = 0; i < PTE_COUNT; i++) {
pte_t pte = pt_l1[i];
if (!pte_is_valid(pte))
continue;
if (pte_is_leaf(pte)) {
err = pmap_unallocate_page(pte_to_pa(pte), PMAP_MEGAPAGE);
if (err == ERR_NOT_VALID) {
return ERR_NOT_VALID;
}
pt_l1[i] = pte_mark_invalid(pte);
continue;
}
pagetable_t* pt = (pagetable_t*)pa_to_da(pte_to_pa(pte));
// go one level down
err = pmap_free_ptes_l0(pt);
if (err == ERR_NOT_VALID) {
return ERR_NOT_VALID;
}
// actual pte
err = pmap_unallocate_page(pte_to_pa(pte), PMAP_PAGE);
if (err == ERR_NOT_VALID) {
return ERR_NOT_VALID;
}
pte = pte_mark_invalid(pte);
pt_l1[i] = pte;
}
return ERR_NONE;
};

static error_t pmap_free_ptes_l2(pagetable_t* pt_l2) {
error_t err = ERR_NONE;
for (int i = 0; i < PTE_COUNT; i++) {
pte_t pte = pt_l2[i];
if (!pte_is_valid(pte))
continue;
if (pte_is_leaf(pte)) {
err = pmap_unallocate_page(pte_to_pa(pte), PMAP_HUGEPAGE);
if (err == ERR_NOT_VALID) {
return ERR_NOT_VALID;
}
pt_l2[i] = pte_mark_invalid(pte);
continue;
}
pagetable_t* pt = (pagetable_t*)pa_to_da(pte_to_pa(pte));
// go level down
err = pmap_free_ptes_l1(pt);
if (err != ERR_NONE) {
return err;
}
// actual pte
err = pmap_unallocate_page(pte_to_pa(pte), PMAP_PAGE);
if (err == ERR_NOT_VALID) {
return ERR_NOT_VALID;
}
pte = pte_mark_invalid(pte);
pt_l2[i] = pte;
}
return ERR_NONE;
};

error_t pmap_free_pagetable(paddr_t pt) {
pagetable_t* root_pt = pa_to_da(pt);
if (root_pt == nullptr)
return ERR_BAD_ARG;
error_t err = pmap_free_ptes_l2(root_pt);
if(err != ERR_NONE)
return err;
err = pmap_unallocate_page(pt, PMAP_PAGE);
return err;
}

pte_t* pmap_pte_ptr_lookup_valid(pagetable_t pt, vaddr_t va) {
for (int level = PT_LEVELS - 1; level >= 0; --level) {
pte_t* ptep = pte_ptr(pt, va_vpn(level, va));
if (pte_is_valid(*ptep)) {
if (pte_is_leaf(*ptep)) {
return ptep;
}
pt = pte_to_pa(*ptep);
} else {
return nullptr;
}
}
return nullptr;
}

pte_t* pmap_pte_ptr_lookup(pagetable_t pt, vaddr_t va, pmap_page_t page_type) {
int desired_page_level = (int)page_type;
for (int level = PT_LEVELS - 1; level > desired_page_level; --level) {
pte_t* ptep = pte_ptr(pt, va_vpn(level, va));
if (pte_is_valid(*ptep)) {
if (pte_is_leaf(*ptep)) {
if (level == desired_page_level) {
return ptep;
}
return nullptr;
}
pt = pte_to_pa(*ptep);
} else {
if(level == desired_page_level) {
return ptep;
}
paddr_t pte_new_pa;
error_t err = pmap_allocate_page(&pte_new_pa, PMAP_PAGE);
if(err != ERR_NONE) {
/* TODO: Deal with allocation failure */
return nullptr;
}
*ptep = pte_mark_valid(pa_to_pte(pte_new_pa));
pt = pte_new_pa;
}
}
return pte_ptr(pt, va_vpn(desired_page_level, va));
}
/**
*
*/
bool pmap_pagefault_fixup(pagetable_t pt, vaddr_t va, hal_address_region_flag_t prot) {
pte_t* ptep = pmap_pte_ptr_lookup_valid(pt, va);
if (ptep == nullptr) {
return false;
}
pte_t pte = *ptep;
bool modified = false;
switch (prot) {
case HAL_ASR_FLAGS_READ:
if ((pte & (PTE_R | PTE_A)) == PTE_R) {
pte |= PTE_A;
modified = true;
}
break;
case HAL_ASR_FLAGS_WRITE:
if (((pte & PTE_W) != 0) && ((pte & PTE_DA) != PTE_DA)) {
pte |= PTE_A | PTE_D;
modified = true;
}
break;
case HAL_ASR_FLAGS_EXECUTE:
if ((pte & (PTE_X | PTE_A)) == PTE_X) {
pte |= PTE_A;
modified = true;
}
}
if (modified) {
*ptep = pte;
}
return modified;
}
117 changes: 117 additions & 0 deletions src/kernel/hal/arch/riscv/pmap.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
#ifndef HAL_RISCV_PMAP_H
#define HAL_RISCV_PMAP_H

#include <address_space/address_space.h>
#include <libcore/error.h>
#include <libcore/types.h>
#include <memory_management/include/physical_memory/manager.h>
#include <stdint.h>

#include "pte.h"

/**
* @brief Physical page types supported in SV39 mode.
* Underlying value specifies which level Pagetable contains it's
* Pagetable Entry.
* */
typedef enum : u8 {
PMAP_PAGE = 0,
PMAP_MEGAPAGE = 1,
PMAP_HUGEPAGE = 2,
} pmap_page_t;

/**
* @brief Correspondence between RISC V physical page and physical frame order
*/
static const frame_order_t g_pmap_frame_order[] = {
[PMAP_PAGE] = FRAME_ORDER_4KiB, [PMAP_HUGEPAGE] = FRAME_ORDER_2MiB, [PMAP_MEGAPAGE] = FRAME_ORDER_1GiB};

#define SATP_MODE_SV39 (8UL << 60)
#define SATP_MODE SATP_MODE_SV39
#define SATP_ASID_S 44
#define SATP_ASID_MASK 0x3FUL
#define PTE_COUNT 512 /* Number of PTEs in a pagetable */
#define PT_LEVELS 3 /* Number of pagetable indirection levels */

static inline reg_t make_satp(paddr_t pa, u64 asid) {
return SATP_MODE | (asid << SATP_ASID_S) | (pa >> PPN_OFFSET);
}

static inline paddr_t satp_to_pa(reg_t satp) {
return (satp & PPN_MASK) << PPN_OFFSET;
}

static inline u64 satp_to_asid(reg_t satp) {
return (satp >> SATP_ASID_S) & SATP_ASID_MASK;
}

/**
* @brief Translates between physical address and it's mapping in kernel
* address space (direct address).
*/
static inline void* pa_to_da(paddr_t pa) {
return physical_to_effective((__phys void*)pa);
}

/**
* @brief Extract pointer to a PTE from the pagetable.
*
* @param pt Pagetable physical address
* @param n Physical Page Number
* @returns Pointer to the n-th PTE in the pagetable.
*/
static inline pte_t* pte_ptr(pagetable_t pt, u64 n) {
pagetable_t* ptp = (pagetable_t*)pa_to_da(pt);
ptp = ptp + n;
return (pte_t*)ptp;
}

/**
* @brief Fixes pagefault caused by memory accesses to a page
* without appropriate Accessed and/or Dirty bits set.
*/
bool pmap_pagefault_fixup(pagetable_t, vaddr_t, hal_address_region_flag_t);

/**
* @brief Find valid PTE corresponding to the virtual address.
*/
pte_t* pmap_pte_ptr_lookup_valid(pagetable_t, vaddr_t);

/**
* @brief Find PTE corresponding to the virtual address and page type.
* Allocates intermediate Pagetable pages if necessary.
*
* @return Pointer to the PTE or nullptr if either allocation proved
* impossible or there already exists a valid PTE correspoding to virtual
* address, but it's type is different than expected.
*
* @note Does not allocate Leaf PTE and associated page frame,
* to allow mapping of already existing resource.
*/
pte_t* pmap_pte_ptr_lookup(pagetable_t, vaddr_t, pmap_page_t);
/**
* @brief Unallocate all pages associated with the pagetable.
*
*/
error_t pmap_free_pagetable(paddr_t);

/**
* @brief Allocate physical page
* @param[out] pa, physical address of the allocated page.
* @param[in] page_type, page_type .
* @return ERR_NOT_VALID, if allocation of physical memory fails
* ERR_NONE otherwise.
* @todo Physical page bookkeeping.
*/
error_t pmap_allocate_page(paddr_t* pa, pmap_page_t page_type);

/**
* @brief Unallocate physical page
* @param[in] pa, physical address of page to be unallocated.
* @param[in] page_type, page_type specifies size of the allocation.
* @return ERR_NOT_VALID, if unallocation of physical memory fails
* ERR_NONE otherwise.
* @todo Physical page bookkeeping.
*/
error_t pmap_unallocate_page(paddr_t pa, pmap_page_t page_type);
#endif /* !HAL_RISCV_PMAP_H */
Loading