summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--na/arch/riscv/include/arch/pmap.h5
-rw-r--r--na/arch/riscv/mm/pmap.cc90
2 files changed, 89 insertions, 6 deletions
diff --git a/na/arch/riscv/include/arch/pmap.h b/na/arch/riscv/include/arch/pmap.h
index 4504af7..a99825a 100644
--- a/na/arch/riscv/include/arch/pmap.h
+++ b/na/arch/riscv/include/arch/pmap.h
@@ -25,13 +25,18 @@ inline constexpr uint64 PTE_U = 1ULL << 4;
inline constexpr uint64 PTE_G = 1ULL << 5;
inline constexpr uint64 PTE_A = 1ULL << 6;
inline constexpr uint64 PTE_D = 1ULL << 7;
+inline constexpr uint64 PTE_COW = 1ULL << 8; /* Custom RSW bit for Copy-On-Write */
uint64 create();
void destroy(uint64 root_pa);
int map(uint64 root_pa, uint64 va, uint64 pa, uint64 perm);
int map_2mb(uint64 root_pa, uint64 va, uint64 pa, uint64 perm);
void unmap(uint64 root_pa, uint64 va);
+int protect(uint64 root_pa, uint64 va, uint64 perm);
+int protect_range(uint64 root_pa, uint64 start, uint64 end, uint64 perm);
bool lookup(uint64 root_pa, uint64 va, uint64* pa_out);
+bool is_cow(uint64 root_pa, uint64 va);
+int make_cow(uint64 root_pa, uint64 va);
void activate(uint64 root_pa);
/*
diff --git a/na/arch/riscv/mm/pmap.cc b/na/arch/riscv/mm/pmap.cc
index dafbeaf..ae21d0e 100644
--- a/na/arch/riscv/mm/pmap.cc
+++ b/na/arch/riscv/mm/pmap.cc
@@ -39,6 +39,7 @@ inline constexpr uint64 PTE_U = 1ULL << 4;
inline constexpr uint64 PTE_G = 1ULL << 5;
inline constexpr uint64 PTE_A = 1ULL << 6;
inline constexpr uint64 PTE_D = 1ULL << 7;
+inline constexpr uint64 PTE_COW = 1ULL << 8;
/* SV39: 3 levels, 9 bits per level, 12-bit page offset */
inline constexpr int kLevels = 3;
@@ -81,11 +82,11 @@ static uint64 alloc_pt_page()
if (memblock_is_active()) {
pa = memblock_alloc(kPageSize, kPageSize);
} else {
- Frame *f = pmm_alloc_frame();
- if (!f) {
+ Page *page = pmm_alloc_page();
+ if (!page) {
return 0;
}
- pa = frame_to_pa(f).raw;
+ pa = page_to_pa(page).raw;
}
if (!pa) {
return 0;
@@ -119,13 +120,13 @@ void destroy(uint64 root_pa)
if (!(l1[j] & (PTE_R | PTE_W | PTE_X))) {
/* L0 page table */
uint64 l0_pa = pte_to_pa(l1[j]);
- pmm_free_frame(pa_to_frame(phys_addr(l0_pa)));
+ pmm_free_page(pa_to_page(phys_addr(l0_pa)));
}
}
- pmm_free_frame(pa_to_frame(phys_addr(l1_pa)));
+ pmm_free_page(pa_to_page(phys_addr(l1_pa)));
}
}
- pmm_free_frame(pa_to_frame(phys_addr(root_pa)));
+ pmm_free_page(pa_to_page(phys_addr(root_pa)));
}
int map(uint64 root_pa, uint64 va, uint64 pa, uint64 perm)
@@ -189,6 +190,47 @@ void unmap(uint64 root_pa, uint64 va)
table[vpn(va, 0)] = 0;
}
+int protect(uint64 root_pa, uint64 va, uint64 perm)
+{
+ uint64 *table = pa_to_ptr(root_pa);
+ for (int level = 2; level >= 0; level--) {
+ uint64 idx = vpn(va, level);
+ uint64 pte = table[idx];
+ if (!(pte & PTE_V))
+ return -14; /* -EFAULT */
+ if (pte & (PTE_R | PTE_W | PTE_X)) {
+ uint64 pa = pte_to_pa(pte);
+ table[idx] = pa_to_pte(pa, perm | PTE_V);
+ return 0;
+ }
+ table = pa_to_ptr(pte_to_pa(pte));
+ }
+ return -14;
+}
+
+int protect_range(uint64 root_pa, uint64 start, uint64 end, uint64 perm)
+{
+ if (end <= start) {
+ return 0;
+ }
+
+ uint64 va = page_align_down(start);
+ uint64 va_end = page_align_up(end);
+
+ for (; va < va_end; va += kPageSize) {
+ int ret = protect(root_pa, va, perm);
+ if (ret == -14) {
+ continue; /* unmapped page: skip */
+ }
+ if (ret < 0) {
+ return ret;
+ }
+ }
+
+ arch::mmu::flush_tlb_all();
+ return 0;
+}
+
bool lookup(uint64 root_pa, uint64 va, uint64 *pa_out)
{
uint64 *table = pa_to_ptr(root_pa);
@@ -208,6 +250,42 @@ bool lookup(uint64 root_pa, uint64 va, uint64 *pa_out)
return false;
}
+bool is_cow(uint64 root_pa, uint64 va)
+{
+ uint64 *table = pa_to_ptr(root_pa);
+ for (int level = 2; level >= 0; level--) {
+ uint64 idx = vpn(va, level);
+ uint64 pte = table[idx];
+ if (!(pte & PTE_V))
+ return false;
+ if (pte & (PTE_R | PTE_W | PTE_X)) {
+ return (pte & PTE_COW) != 0;
+ }
+ table = pa_to_ptr(pte_to_pa(pte));
+ }
+ return false;
+}
+
+int make_cow(uint64 root_pa, uint64 va)
+{
+ uint64 *table = pa_to_ptr(root_pa);
+ for (int level = 2; level >= 0; level--) {
+ uint64 idx = vpn(va, level);
+ uint64 pte = table[idx];
+ if (!(pte & PTE_V))
+ return -14; /* -EFAULT */
+ if (pte & (PTE_R | PTE_W | PTE_X)) {
+ /* Clear write bit, set COW bit */
+ pte &= ~PTE_W;
+ pte |= PTE_COW;
+ table[idx] = pte;
+ return 0;
+ }
+ table = pa_to_ptr(pte_to_pa(pte));
+ }
+ return -14;
+}
+
void activate(uint64 root_pa)
{
arch::mmu::set_page_table(root_pa);