virtual/physical address encapsulation
This commit is contained in:
parent
d2f2827691
commit
289bd04621
2 changed files with 185 additions and 24 deletions
|
|
@ -7,6 +7,44 @@
|
|||
void init_gdt();
|
||||
void init_idt();
|
||||
|
||||
// do not use these fields directly! use *_to_* functions.
|
||||
struct va {
|
||||
uint64_t value; // 48-bit
|
||||
};
|
||||
|
||||
struct vpn {
|
||||
uint64_t pagenum; // 36-bit
|
||||
};
|
||||
|
||||
struct pa {
|
||||
uint64_t value; // 48 - 12 = 36-bit
|
||||
};
|
||||
|
||||
struct ppn {
|
||||
uint64_t pagenum; // 36 - 12 = 24-bit
|
||||
};
|
||||
|
||||
struct va va_from_value(uint64_t value);
|
||||
struct va va_from_canonical(uint64_t canonical);
|
||||
struct va va_from_vpn_with_offset(struct vpn vpn, uint64_t offset);
|
||||
uint64_t va_to_value(struct va va);
|
||||
uint64_t va_to_canonical(struct va va);
|
||||
|
||||
struct vpn vpn_from_pagenum(uint64_t pagenum);
|
||||
struct vpn vpn_from_aligned_va(struct va va);
|
||||
struct vpn vpn_from_unaligned_va(struct va va);
|
||||
uint64_t vpn_to_pagenum(struct vpn vpn);
|
||||
|
||||
struct pa pa_from_value(uint64_t value);
|
||||
struct pa pa_from_ppn_with_offset(struct ppn ppn, uint64_t offset);
|
||||
uint64_t pa_to_value(struct pa pa);
|
||||
void *pa_to_pointer(struct pa pa);
|
||||
|
||||
struct ppn ppn_from_pagenum(uint64_t pagenum);
|
||||
struct ppn ppn_from_aligned_pa(struct pa pa);
|
||||
struct ppn ppn_from_unaligned_pa(struct pa pa);
|
||||
uint64_t ppn_to_pagenum(struct ppn ppn);
|
||||
|
||||
struct pt_entry {
|
||||
bool present;
|
||||
bool writable;
|
||||
|
|
@ -18,7 +56,7 @@ struct pt_entry {
|
|||
bool page_attr_table_low; // in high this is the page_size field to signal hugepages.
|
||||
bool global;
|
||||
bool page_attr_table_high;
|
||||
uint64_t addr;
|
||||
struct ppn ppn;
|
||||
// TODO MPK, NX
|
||||
};
|
||||
|
||||
|
|
|
|||
169
src/x86_64/mem.c
169
src/x86_64/mem.c
|
|
@ -99,10 +99,7 @@ void init_idt() {
|
|||
__asm__("lidt (%0)" ::"r"(idtr));
|
||||
}
|
||||
|
||||
#define IS_PAGE_ALIGNED(addr) (((addr) & 0xfffull) == 0)
|
||||
|
||||
uint64_t pt_entry_pack(const struct pt_entry *ent_in) {
|
||||
ASSERT(IS_PAGE_ALIGNED(ent_in->addr)); // TODO better assertions for hugepages
|
||||
return (uint64_t)ent_in->present
|
||||
| ((uint64_t)ent_in->writable) << 1
|
||||
| ((uint64_t)ent_in->supervisor) << 2
|
||||
|
|
@ -113,7 +110,7 @@ uint64_t pt_entry_pack(const struct pt_entry *ent_in) {
|
|||
| ((uint64_t)ent_in->page_attr_table_low) << 7
|
||||
| ((uint64_t)ent_in->global) << 8
|
||||
| ((uint64_t)ent_in->page_attr_table_high) << 12
|
||||
| ((uint64_t)ent_in->addr & 0x000ffffffffff000);
|
||||
| pa_to_value(pa_from_ppn_with_offset(ent_in->ppn, 0));
|
||||
}
|
||||
|
||||
void pt_entry_unpack(uint64_t ent_in, struct pt_entry *ent_out) {
|
||||
|
|
@ -127,7 +124,8 @@ void pt_entry_unpack(uint64_t ent_in, struct pt_entry *ent_out) {
|
|||
ent_out->page_attr_table_low = (ent_in & (0x1ull << 7)) != 0;
|
||||
ent_out->global = (ent_in & (0x1ull << 8)) != 0;
|
||||
ent_out->page_attr_table_high = (ent_in & (0x1ull << 12)) != 0;
|
||||
ent_out->addr = (ent_in & 0x000ffffffffff000);
|
||||
// this may panic if the address is 57 bit, which is intended here.
|
||||
ent_out->ppn = ppn_from_aligned_pa(pa_from_value(ent_in & 0x000ffffffffff000ull));
|
||||
}
|
||||
|
||||
void pt_entry_print(const struct pt_entry *ent) {
|
||||
|
|
@ -142,7 +140,7 @@ void pt_entry_print(const struct pt_entry *ent) {
|
|||
printf(" page_attr_table_low: %d\n", (int)ent->page_attr_table_low);
|
||||
printf(" global: %d\n", (int)ent->global);
|
||||
printf(" page_attr_table_high: %d\n", (int)ent->page_attr_table_high);
|
||||
printf(" addr: %p\n", ent->addr);
|
||||
printf(" page base: %p\n", pa_from_ppn_with_offset(ent->ppn, 0));
|
||||
printf("}\n");
|
||||
}
|
||||
|
||||
|
|
@ -153,16 +151,19 @@ void pt_entry_print(const struct pt_entry *ent) {
|
|||
|
||||
#define CR4_LA57 12
|
||||
|
||||
static uint64_t arange_virt_start;
|
||||
static struct vpn arange_vpn_start;
|
||||
static struct pt_entry arange_entry_start;
|
||||
static uint64_t arange_npages = 0;
|
||||
|
||||
static void arange_print() {
|
||||
uint64_t virt_canonical = va_to_canonical(va_from_vpn_with_offset(arange_vpn_start, 0));
|
||||
uint64_t phys_start = pa_to_value(pa_from_ppn_with_offset(arange_entry_start.ppn, 0));
|
||||
printf("virt: %p .. %p\n",
|
||||
arange_virt_start,
|
||||
arange_virt_start + (arange_npages << 12));
|
||||
virt_canonical,
|
||||
virt_canonical + (arange_npages << 12));
|
||||
printf("phys: %p .. %p\n",
|
||||
arange_entry_start.addr,
|
||||
arange_entry_start.addr + (arange_npages << 12));
|
||||
phys_start,
|
||||
phys_start + (arange_npages << 12));
|
||||
printf("attr: %c, %c, %s, %s, %s | npages: %lu, size: %lu]\n\n",
|
||||
arange_entry_start.writable ? 'w' : 'r',
|
||||
arange_entry_start.supervisor ? 's' : 'u',
|
||||
|
|
@ -176,11 +177,10 @@ static void arange_print() {
|
|||
#define LOWEST_LEVEL(depth, num_levels) ((depth) == (num_levels) - 1)
|
||||
#define LEAF(depth, num_levels, ent) (LOWEST_LEVEL(depth, num_levels) || (ent).page_attr_table_low)
|
||||
|
||||
static void pt_walk_single(uint64_t phys, int depth, int num_levels, uint64_t virt_prev) {
|
||||
uint64_t idmapped = PHYS_TO_IDMAPPED(phys);
|
||||
static void pt_walk_ranges(struct ppn ppn, int depth, int num_levels, uint64_t virt_prev) {
|
||||
struct pt_entry ent;
|
||||
for (uint64_t i = 0; i < 512; i++) {
|
||||
uint64_t entry = ((uint64_t *)idmapped)[i];
|
||||
uint64_t entry = *(uint64_t*)pa_to_pointer(pa_from_ppn_with_offset(ppn, i * 8));
|
||||
pt_entry_unpack(entry, &ent);
|
||||
if (!ent.present) {
|
||||
continue;
|
||||
|
|
@ -190,15 +190,16 @@ static void pt_walk_single(uint64_t phys, int depth, int num_levels, uint64_t vi
|
|||
uint64_t virt_new = virt_prev | virt_part;
|
||||
|
||||
if (LEAF(depth, num_levels, ent)) {
|
||||
struct vpn curr_vpn = vpn_from_aligned_va(va_from_value(virt_new));
|
||||
// for huge pages this is > 1
|
||||
uint64_t num_pages_covered = 1ull << (9 * (num_levels - depth - 1));
|
||||
if (arange_npages == 0) {
|
||||
arange_virt_start = virt_new;
|
||||
arange_vpn_start = curr_vpn;
|
||||
arange_entry_start = ent;
|
||||
arange_npages = num_pages_covered;
|
||||
} else {
|
||||
if (virt_new == arange_virt_start + (arange_npages << 12)
|
||||
&& ent.addr == arange_entry_start.addr + (arange_npages << 12)
|
||||
if (vpn_to_pagenum(curr_vpn) == vpn_to_pagenum(arange_vpn_start) + arange_npages
|
||||
&& ppn_to_pagenum(ent.ppn) == ppn_to_pagenum(arange_entry_start.ppn) + arange_npages
|
||||
&& ent.writable == arange_entry_start.writable
|
||||
&& ent.supervisor == arange_entry_start.supervisor
|
||||
&& ent.writethrough == arange_entry_start.writethrough
|
||||
|
|
@ -210,17 +211,49 @@ static void pt_walk_single(uint64_t phys, int depth, int num_levels, uint64_t vi
|
|||
} else {
|
||||
// print last range and start new one
|
||||
arange_print();
|
||||
arange_virt_start = virt_new;
|
||||
arange_vpn_start = vpn_from_aligned_va(va_from_value(virt_new));
|
||||
arange_entry_start = ent;
|
||||
arange_npages = num_pages_covered;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
pt_walk_single(ent.addr, depth + 1, num_levels, virt_new);
|
||||
pt_walk_ranges(ent.ppn, depth + 1, num_levels, virt_new);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
#define TRANSLATE_INVALID (~0ull)
|
||||
|
||||
static uint64_t *pt_get_leaf_ptr(uint64_t va, uint64_t cr3_addr, int num_levels, int *level_out) {
|
||||
ASSERT(num_levels == 4 || num_levels == 5);
|
||||
|
||||
int level = num_levels;
|
||||
uint64_t pa = cr3_addr;
|
||||
while (1) {
|
||||
uint64_t idx = (va >> (12 + (level - 1)*9)) & 0x1ffull;
|
||||
uint64_t *entry_ptr = PT_ENTRY_POINTER_AT(pa, idx);
|
||||
|
||||
struct pt_entry ent;
|
||||
pt_entry_unpack(*entry_ptr, &ent);
|
||||
|
||||
if (level == 1 || ((level == 2 || level == 3) && ent.page_attr_table_low)) {
|
||||
if (level_out != NULL) {
|
||||
*level_out = level;
|
||||
}
|
||||
return entry_ptr;
|
||||
}
|
||||
|
||||
if (!ent->present) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pa = ent.addr;
|
||||
level -= 1;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
void pt_walk() {
|
||||
uint64_t cr3;
|
||||
uint64_t cr4;
|
||||
|
|
@ -230,13 +263,103 @@ void pt_walk() {
|
|||
// find out if we have 5 levels or 4
|
||||
int levels = 4;
|
||||
if (cr4 & (1ull << CR4_LA57)) {
|
||||
levels = 5;
|
||||
PANIC("we don't support 5-level page tables");
|
||||
}
|
||||
printf("number of levels: %d\n", levels);
|
||||
|
||||
uint64_t phys = cr3 & 0x000ffffffffff000ull;
|
||||
struct ppn ppn = ppn_from_aligned_pa(pa_from_value(cr3 & 0x000ffffffffff000ull));
|
||||
|
||||
pt_walk_single(phys, 0, levels, 0);
|
||||
pt_walk_ranges(ppn, 0, levels, 0);
|
||||
// print last range
|
||||
arange_print();
|
||||
}
|
||||
|
||||
// --- address encapsulation ---
|
||||
|
||||
struct va va_from_value(uint64_t value) {
|
||||
ASSERT(value < (1ull << 48));
|
||||
struct va va = { .value = value };
|
||||
return va;
|
||||
}
|
||||
|
||||
struct va va_from_canonical(uint64_t canonical) {
|
||||
if (canonical & (1ull << 47)) {
|
||||
ASSERT(canonical >> 48 == 0xffffull);
|
||||
} else {
|
||||
ASSERT(canonical >> 48 == 0ull);
|
||||
}
|
||||
struct va va = { .value = canonical & 0x0000ffffffffffffull };
|
||||
return va;
|
||||
}
|
||||
|
||||
struct va va_from_vpn_with_offset(struct vpn vpn, uint64_t offset) {
|
||||
ASSERT(offset < (1ull << 12));
|
||||
return va_from_value((vpn.pagenum << 12) + offset);
|
||||
}
|
||||
|
||||
uint64_t va_to_value(struct va va) {
|
||||
return va.value;
|
||||
}
|
||||
|
||||
uint64_t va_to_canonical(struct va va) {
|
||||
if (va.value & (1ull << 47)) {
|
||||
return va.value | (0xffffull << 48);
|
||||
} else {
|
||||
return va.value;
|
||||
}
|
||||
}
|
||||
|
||||
struct vpn vpn_from_pagenum(uint64_t pagenum) {
|
||||
ASSERT(pagenum < (1ull << 36));
|
||||
return (struct vpn){ .pagenum = pagenum };
|
||||
}
|
||||
|
||||
struct vpn vpn_from_aligned_va(struct va va) {
|
||||
ASSERT((va_to_value(va) & 0xfffull) == 0);
|
||||
struct vpn vpn = { .pagenum = va_to_value(va) >> 12 };
|
||||
return vpn;
|
||||
}
|
||||
|
||||
struct vpn vpn_from_unaligned_va(struct va va) {
|
||||
struct vpn vpn = { .pagenum = va_to_value(va) >> 12 };
|
||||
return vpn;
|
||||
}
|
||||
|
||||
uint64_t vpn_to_pagenum(struct vpn vpn) {
|
||||
return vpn.pagenum;
|
||||
}
|
||||
|
||||
struct pa pa_from_value(uint64_t value) {
|
||||
ASSERT(value < (1ull << 36));
|
||||
return (struct pa){ .value = value };
|
||||
}
|
||||
|
||||
struct pa pa_from_ppn_with_offset(struct ppn ppn, uint64_t offset) {
|
||||
ASSERT(offset < (1ull << 12));
|
||||
return pa_from_value((ppn.pagenum << 12) + offset);
|
||||
}
|
||||
|
||||
uint64_t pa_to_value(struct pa pa) {
|
||||
return pa.value;
|
||||
}
|
||||
|
||||
void *pa_to_pointer(struct pa pa) {
|
||||
return (void*)PHYS_TO_IDMAPPED(pa.value);
|
||||
}
|
||||
|
||||
struct ppn ppn_from_pagenum(uint64_t pagenum) {
|
||||
ASSERT(pagenum < (1ull << 24));
|
||||
return (struct ppn){ .pagenum = pagenum };
|
||||
}
|
||||
|
||||
struct ppn ppn_from_aligned_pa(struct pa pa) {
|
||||
ASSERT((pa.value & 0xfffull) == 0);
|
||||
return (struct ppn){ .pagenum = pa_to_value(pa) >> 12 };
|
||||
}
|
||||
|
||||
struct ppn ppn_from_unaligned_pa(struct pa pa) {
|
||||
return (struct ppn){ .pagenum = pa_to_value(pa) >> 12 };
|
||||
}
|
||||
|
||||
uint64_t ppn_to_pagenum(struct ppn ppn) {
|
||||
return ppn.pagenum;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue