virtual/physical address encapsulation

This commit is contained in:
uosfz 2025-03-13 18:10:21 +01:00
parent d2f2827691
commit 289bd04621
Signed by: uosfz
SSH key fingerprint: SHA256:FlktuluyhTQg3jHZNLKwxOOC5hbfrUXM0tz3IA3lGJo
2 changed files with 185 additions and 24 deletions

View file

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

View file

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