*-nemu,vme: refactor ptw code

This commit is contained in:
Zihao Yu 2020-03-06 15:55:23 +08:00
parent 885ee22f9a
commit 116db01939
5 changed files with 54 additions and 86 deletions

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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)];

View File

@ -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 *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)) {
pg_base = pgalloc_usr(PGSIZE);
*pte = PTE_P | PTE_W | PTE_U | (uintptr_t)pg_base;
}
}
PTE *pte = &((PTE*)PTE_ADDR(*pde))[PTX(va)];
if (!(*pte & PTE_P)) {
*pte = PTE_P | PTE_W | PTE_U | (uint32_t)pa;
*pte = PTE_P | PTE_W | PTE_U | (uintptr_t)pa;
}
}