*-nemu,vme: refactor ptw code
This commit is contained in:
parent
885ee22f9a
commit
116db01939
|
@ -17,22 +17,11 @@ static inline void outl(uintptr_t addr, uint32_t data) { *(volatile uint32_t *)a
|
|||
#define PTE_D 0x4
|
||||
|
||||
// Page directory and page table constants
|
||||
#define NR_PDE 1024 // # directory entries per page directory
|
||||
#define NR_PTE 1024 // # PTEs per page table
|
||||
#define PGSHFT 12 // log2(PGSIZE)
|
||||
#define PTXSHFT 12 // Offset of PTX in a linear address
|
||||
#define PDXSHFT 22 // Offset of PDX in a linear address
|
||||
|
||||
// +--------10------+-------10-------+---------12----------+
|
||||
// | Page Directory | Page Table | Offset within Page |
|
||||
// | Index | Index | |
|
||||
// +----------------+----------------+---------------------+
|
||||
// \--- PDX(va) --/ \--- PTX(va) --/\------ OFF(va) ------/
|
||||
typedef uint32_t PTE;
|
||||
typedef uint32_t PDE;
|
||||
#define PDX(va) (((uint32_t)(va) >> PDXSHFT) & 0x3ff)
|
||||
#define PTX(va) (((uint32_t)(va) >> PTXSHFT) & 0x3ff)
|
||||
#define OFF(va) ((uint32_t)(va) & 0xfff)
|
||||
|
||||
// Address in page table or page directory entry
|
||||
#define PTE_ADDR(pte) ((uint32_t)(pte) & ~0xfff)
|
||||
|
|
|
@ -35,33 +35,13 @@ enum { MODE_U = 0, MODE_S, MODE_H, MODE_M };
|
|||
#define PTE_X 0x08
|
||||
#define PTE_U 0x10
|
||||
|
||||
typedef uintptr_t PTE;
|
||||
#define PGSHFT 12 // log2(PGSIZE)
|
||||
#define PN(addr) ((uintptr_t)(addr) >> PGSHFT)
|
||||
#define OFF(va) ((uintptr_t)(va) & (PGSIZE - 1))
|
||||
|
||||
// Address in page table entry
|
||||
#define PTE_ADDR(pte) (((uintptr_t)(pte) & ~0x3ff) << 2)
|
||||
|
||||
typedef struct {
|
||||
int ptw_level;
|
||||
int vpn_width;
|
||||
} ptw_config;
|
||||
|
||||
#define PTW_SV32 ((ptw_config) { .ptw_level = 2, .vpn_width = 10 })
|
||||
#define PTW_SV39 ((ptw_config) { .ptw_level = 3, .vpn_width = 9 })
|
||||
#define PTW_SV48 ((ptw_config) { .ptw_level = 4, .vpn_width = 9 })
|
||||
|
||||
// Offset of VPN[i] in a virtual address
|
||||
static inline int VPNiSHFT(const ptw_config c, int i) {
|
||||
return (PGSHFT) + c.vpn_width * i;
|
||||
}
|
||||
// Extract the VPN[i] field in a virtual address
|
||||
static inline uintptr_t VPNi(const ptw_config c, uintptr_t va, int i) {
|
||||
uintptr_t vpn_mask = (1 << c.vpn_width) - 1;
|
||||
return (va >> VPNiSHFT(c, i)) & vpn_mask;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
|
@ -33,8 +33,6 @@
|
|||
# define FB_ADDR 0xa0000000
|
||||
#endif
|
||||
|
||||
#define PGSIZE 4096
|
||||
|
||||
#define MMIO_BASE 0xa0000000
|
||||
#define MMIO_SIZE 0x10000000
|
||||
|
||||
|
@ -45,4 +43,26 @@ extern char _pmem_start, _pmem_end;
|
|||
RANGE(0xa0000000, 0xa0000000 + 0x80000), /* vmem */ \
|
||||
RANGE(0xa1000000, 0xa1000000 + 0x1000) /* serial, rtc, screen, keyboard */
|
||||
|
||||
#define PGSIZE 4096
|
||||
#define PGSHFT 12 // log2(PGSIZE)
|
||||
#define PN(addr) ((uintptr_t)(addr) >> PGSHFT)
|
||||
#define OFF(va) ((uintptr_t)(va) & (PGSIZE - 1))
|
||||
|
||||
typedef uintptr_t PTE;
|
||||
|
||||
typedef struct {
|
||||
int ptw_level;
|
||||
int vpn_width;
|
||||
} ptw_config;
|
||||
|
||||
// Offset of VPN[i] in a virtual address
|
||||
static inline int VPNiSHFT(const ptw_config c, int i) {
|
||||
return (PGSHFT) + c.vpn_width * i;
|
||||
}
|
||||
// Extract the VPN[i] field in a virtual address
|
||||
static inline uintptr_t VPNi(const ptw_config c, uintptr_t va, int i) {
|
||||
uintptr_t vpn_mask = (1 << c.vpn_width) - 1;
|
||||
return (va >> VPNiSHFT(c, i)) & vpn_mask;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -17,7 +17,7 @@ int _vme_init(void* (*pgalloc_f)(size_t), void (*pgfree_f)(void*)) {
|
|||
}
|
||||
|
||||
void _protect(_AddressSpace *as) {
|
||||
as->ptr = (PDE*)(pgalloc_usr(PGSIZE));
|
||||
as->ptr = (PTE*)(pgalloc_usr(PGSIZE));
|
||||
as->pgsize = PGSIZE;
|
||||
as->area = USER_SPACE;
|
||||
}
|
||||
|
@ -25,7 +25,7 @@ void _protect(_AddressSpace *as) {
|
|||
void _unprotect(_AddressSpace *as) {
|
||||
}
|
||||
|
||||
static PDE *cur_pdir = NULL;
|
||||
static PTE *cur_pdir = NULL;
|
||||
void __am_get_cur_as(_Context *c) {
|
||||
c->pdir = cur_pdir;
|
||||
}
|
||||
|
@ -43,8 +43,8 @@ void __am_switch(_Context *c) {
|
|||
void _map(_AddressSpace *as, void *va, void *pa, int prot) {
|
||||
assert((uintptr_t)va % PGSIZE == 0);
|
||||
assert((uintptr_t)pa % PGSIZE == 0);
|
||||
PDE *pdir = (PDE*)as->ptr;
|
||||
PDE *pde = &pdir[PDX(va)];
|
||||
PTE *pdir = (PTE*)as->ptr;
|
||||
PTE *pde = &pdir[PDX(va)];
|
||||
if (!(*pde & PTE_V)) {
|
||||
*pde = PTE_V | (uint32_t)pgalloc_usr(PGSIZE);
|
||||
}
|
||||
|
@ -82,7 +82,7 @@ void __am_tlb_refill() {
|
|||
|
||||
uint32_t va = hi & ~0x1fff;
|
||||
assert(cur_pdir != NULL);
|
||||
PDE *pde = &cur_pdir[PDX(va)];
|
||||
PTE *pde = &cur_pdir[PDX(va)];
|
||||
assert(*pde & PTE_V);
|
||||
|
||||
PTE *pte = &((PTE*)PTE_ADDR(*pde))[PTX(va)];
|
||||
|
|
|
@ -2,24 +2,9 @@
|
|||
#include <nemu.h>
|
||||
#include <klib.h>
|
||||
|
||||
typedef uint32_t PTE;
|
||||
typedef uint32_t PDE;
|
||||
#define PTE_ADDR(pte) ((uintptr_t)(pte) & ~0xfff)
|
||||
|
||||
#define NR_PDE 1024 // # directory entries per page directory
|
||||
#define NR_PTE 1024 // # PTEs per page table
|
||||
#define PGSHFT 12 // log2(PGSIZE)
|
||||
#define PTXSHFT 12 // Offset of PTX in a linear address
|
||||
#define PDXSHFT 22 // Offset of PDX in a linear address
|
||||
|
||||
#define PDX(va) (((uint32_t)(va) >> PDXSHFT) & 0x3ff)
|
||||
#define PTX(va) (((uint32_t)(va) >> PTXSHFT) & 0x3ff)
|
||||
#define OFF(va) ((uint32_t)(va) & 0xfff)
|
||||
#define PTE_ADDR(pte) ((uint32_t)(pte) & ~0xfff)
|
||||
#define PGADDR(d, t, o) ((uint32_t)((d) << PDXSHFT | (t) << PTXSHFT | (o)))
|
||||
#define PG_ALIGN __attribute((aligned(PGSIZE)))
|
||||
|
||||
static PDE kpdirs[NR_PDE] PG_ALIGN = {};
|
||||
static PTE kptabs[(PMEM_SIZE + MMIO_SIZE) / PGSIZE] PG_ALIGN = {};
|
||||
static _AddressSpace kas; // Kernel address space
|
||||
static void* (*pgalloc_usr)(size_t) = NULL;
|
||||
static void (*pgfree_usr)(void*) = NULL;
|
||||
static int vme_enable = 0;
|
||||
|
@ -34,32 +19,19 @@ int _vme_init(void* (*pgalloc_f)(size_t), void (*pgfree_f)(void*)) {
|
|||
pgalloc_usr = pgalloc_f;
|
||||
pgfree_usr = pgfree_f;
|
||||
|
||||
kas.ptr = pgalloc_f(PGSIZE);
|
||||
// make all PTEs invalid
|
||||
memset(kas.ptr, 0, PGSIZE);
|
||||
|
||||
int i;
|
||||
|
||||
// make all PDEs invalid
|
||||
for (i = 0; i < NR_PDE; i ++) {
|
||||
kpdirs[i] = 0;
|
||||
}
|
||||
|
||||
PTE *ptab = kptabs;
|
||||
for (i = 0; i < LENGTH(segments); i ++) {
|
||||
uint32_t pdir_idx = (uintptr_t)segments[i].start / (PGSIZE * NR_PTE);
|
||||
uint32_t pdir_idx_end = (uintptr_t)segments[i].end / (PGSIZE * NR_PTE);
|
||||
for (; pdir_idx < pdir_idx_end; pdir_idx ++) {
|
||||
// fill PDE
|
||||
kpdirs[pdir_idx] = (uintptr_t)ptab | PTE_P;
|
||||
|
||||
// fill PTE
|
||||
PTE pte = PGADDR(pdir_idx, 0, 0) | PTE_P;
|
||||
PTE pte_end = PGADDR(pdir_idx + 1, 0, 0) | PTE_P;
|
||||
for (; pte < pte_end; pte += PGSIZE) {
|
||||
*ptab = pte;
|
||||
ptab ++;
|
||||
}
|
||||
void *va = segments[i].start;
|
||||
for (; va < segments[i].end; va += PGSIZE) {
|
||||
_map(&kas, va, va, 0);
|
||||
}
|
||||
}
|
||||
|
||||
set_cr3(kpdirs);
|
||||
set_cr3(kas.ptr);
|
||||
set_cr0(get_cr0() | CR0_PG);
|
||||
vme_enable = 1;
|
||||
|
||||
|
@ -67,14 +39,12 @@ int _vme_init(void* (*pgalloc_f)(size_t), void (*pgfree_f)(void*)) {
|
|||
}
|
||||
|
||||
void _protect(_AddressSpace *as) {
|
||||
PDE *updir = (PDE*)(pgalloc_usr(PGSIZE));
|
||||
PTE *updir = (PTE*)(pgalloc_usr(PGSIZE));
|
||||
as->ptr = updir;
|
||||
as->area = USER_SPACE;
|
||||
as->pgsize = PGSIZE;
|
||||
// map kernel space
|
||||
for (int i = 0; i < NR_PDE; i ++) {
|
||||
updir[i] = kpdirs[i];
|
||||
}
|
||||
memcpy(updir, kas.ptr, PGSIZE);
|
||||
}
|
||||
|
||||
void _unprotect(_AddressSpace *as) {
|
||||
|
@ -88,17 +58,26 @@ void __am_switch(_Context *c) {
|
|||
if (vme_enable && c->cr3 != NULL) { set_cr3(c->cr3); }
|
||||
}
|
||||
|
||||
#define PTW_CONFIG ((ptw_config) { .ptw_level = 2, .vpn_width = 10 })
|
||||
|
||||
void _map(_AddressSpace *as, void *va, void *pa, int prot) {
|
||||
assert((uintptr_t)va % PGSIZE == 0);
|
||||
assert((uintptr_t)pa % PGSIZE == 0);
|
||||
PDE *pt = (PDE*)as->ptr;
|
||||
PDE *pde = &pt[PDX(va)];
|
||||
if (!(*pde & PTE_P)) {
|
||||
*pde = PTE_P | PTE_W | PTE_U | (uint32_t)pgalloc_usr(PGSIZE);
|
||||
}
|
||||
PTE *pte = &((PTE*)PTE_ADDR(*pde))[PTX(va)];
|
||||
PTE *pg_base = as->ptr;
|
||||
PTE *pte;
|
||||
int level;
|
||||
for (level = PTW_CONFIG.ptw_level - 1; ; level --) {
|
||||
pte = &pg_base[VPNi(PTW_CONFIG, (uintptr_t)va, level)];
|
||||
pg_base = (PTE *)PTE_ADDR(*pte);
|
||||
if (level == 0) break;
|
||||
if (!(*pte & PTE_P)) {
|
||||
*pte = PTE_P | PTE_W | PTE_U | (uint32_t)pa;
|
||||
pg_base = pgalloc_usr(PGSIZE);
|
||||
*pte = PTE_P | PTE_W | PTE_U | (uintptr_t)pg_base;
|
||||
}
|
||||
}
|
||||
|
||||
if (!(*pte & PTE_P)) {
|
||||
*pte = PTE_P | PTE_W | PTE_U | (uintptr_t)pa;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue