blob: c0424bf908b7ec93a14f1655e3482aab9f7663bb [file] [log] [blame]
#include <stdint.h>
#include "encoding.h"
#define get_field(reg, mask) (((reg) & (mask)) / ((mask) & ~((mask) << 1)))
#define set_field(reg, mask, val) (((reg) & ~(mask)) | (((val) * ((mask) & ~((mask) << 1))) & (mask)))
#if __riscv_xlen == 64
# define SATP_PPN SATP64_PPN
typedef uint64_t reg_t;
#else
# define SATP_PPN SATP32_PPN
typedef uint32_t reg_t;
#endif
static char page_buffer[4096 * 8];
static char *page_buffer_next = page_buffer;
typedef struct {
unsigned mode;
unsigned levels;
unsigned ppn_width_bits[5];
unsigned ppn_offset_bits[5];
unsigned entry_width_bytes;
unsigned vpn_width_bits;
unsigned vaddr_bits;
} virtual_memory_system_t;
static virtual_memory_system_t sv32 = {
.mode = SATP_MODE_SV32,
.levels = 2,
.ppn_width_bits = {12, 10, 10},
.ppn_offset_bits = {0, 12, 22},
.entry_width_bytes = 4,
.vpn_width_bits = 10,
.vaddr_bits = 32
};
static virtual_memory_system_t sv39 = {
.mode = SATP_MODE_SV39,
.levels = 3,
.ppn_width_bits = {12, 9, 9, 26},
.ppn_offset_bits = {0, 12, 21, 30},
.entry_width_bytes = 8,
.vpn_width_bits = 9,
.vaddr_bits = 39
};
static virtual_memory_system_t sv48 = {
.mode = SATP_MODE_SV48,
.levels = 4,
.ppn_width_bits = {12, 9, 9, 9, 26},
.ppn_offset_bits = {0, 12, 21, 30, 39},
.entry_width_bytes = 8,
.vpn_width_bits = 9,
.vaddr_bits = 48
};
static virtual_memory_system_t *vms;
void error()
{
while (1)
;
}
void assert(int condition)
{
if (!condition)
error();
}
// Return a 4Kb, aligned, page.
void *get_page()
{
page_buffer_next = (char *) (((unsigned long) page_buffer_next + 4095) & ~0xfff);
while (page_buffer_next + 4096 >= page_buffer + sizeof(page_buffer))
;
void *result = page_buffer_next;
page_buffer_next += 4096;
return result;
}
reg_t entry(char *table, unsigned index)
{
if (vms->entry_width_bytes == 4)
return ((uint32_t *) table)[index];
else if (vms->entry_width_bytes == 8)
return ((uint64_t *) table)[index];
else
assert(0);
}
void entry_set(char *table, unsigned index, uint64_t value)
{
if (vms->entry_width_bytes == 4)
((uint32_t *) table)[index] = value;
else if (vms->entry_width_bytes == 8)
((uint64_t *) table)[index] = value;
else
assert(0);
}
// Set up 1-to-1 for this entire table.
void setup_page_table(char *table, unsigned level, uint64_t physical)
{
for (unsigned i = 0; i < (1<<vms->vpn_width_bits); i++) {
uint64_t pte = PTE_V | PTE_R | PTE_W | PTE_X | PTE_U | PTE_A | PTE_D;
// Add in portion of physical address.
pte |= physical & (((1LL<<vms->vpn_width_bits)-1) <<
(PTE_PPN_SHIFT + (level+1) * vms->vpn_width_bits));
// Add in the index.
pte |= ((reg_t) i) << (PTE_PPN_SHIFT + level * vms->vpn_width_bits);
entry_set(table, i, pte);
}
}
// Return contents of vpn field for the given virtual address and level.
unsigned vpn(uint64_t virtual, unsigned level)
{
virtual >>= 12 + vms->vpn_width_bits * level;
return virtual & ((1<<vms->vpn_width_bits)-1);
}
// Add an entry to the given table, at the given level (0 for 4Kb page).
void add_entry(char *table, unsigned level, uint64_t virtual, uint64_t physical)
{
unsigned current_level = vms->levels - 1;
while (1) {
unsigned index = vpn(virtual, current_level);
if (current_level <= level) {
// Add the new entry.
entry_set(table, index, PTE_V | PTE_R | PTE_W | PTE_X | PTE_U | PTE_A | PTE_D |
((physical >> 2) & ~((1 <<
(PTE_PPN_SHIFT + current_level * vms->vpn_width_bits)) - 1)));
return;
}
reg_t pte = entry(table, index);
if (!(pte & PTE_V) ||
((pte & PTE_R) && (pte & PTE_W))) {
// Create a new page
void *new_page = get_page();
setup_page_table(new_page, current_level - 1, virtual);
entry_set(table, index, PTE_V | PTE_U | PTE_A | PTE_D |
((((reg_t) new_page) >> 2) & ~((1 << 10) - 1)));
table = new_page;
} else {
table = (char *) (pte & ~0xfff);
}
current_level--;
}
}
int main()
{
void *master_table = get_page();
setup_page_table(master_table, vms->levels-1, 0);
uint32_t *physical = get_page();
//uint32_t *virtual = (uint32_t *) (((reg_t) physical) ^ ((reg_t) 0x40000000));
uint32_t *virtual = (uint32_t *) (((reg_t) physical) ^ (((reg_t) 0xf) << (vms->vaddr_bits - 4)));
// Virtual addresses must be sign-extended.
if (vms->vaddr_bits < sizeof(virtual) * 8 && (reg_t) virtual & ((reg_t) 1<<(vms->vaddr_bits-1)))
virtual = (uint32_t *) (
(reg_t) virtual | ~(((reg_t) 1 << vms->vaddr_bits) - 1));
add_entry(master_table, 0, (reg_t) virtual, (reg_t) physical);
unsigned long satp = set_field(0, SATP_MODE, vms->mode);
satp = set_field(satp, SATP_PPN, ((unsigned long) master_table) >> 12);
write_csr(satp, satp);
satp = read_csr(satp);
if (get_field(satp, SATP_MODE) != vms->mode)
error();
reg_t mstatus = read_csr(mstatus);
mstatus |= MSTATUS_MPRV;
write_csr(mstatus, mstatus);
// Address translation is enabled.
physical[0] = 0xdeadbeef;
assert(virtual[0] == physical[0]);
virtual[1] = 0x55667788;
assert(virtual[1] == physical[1]);
active:
end:
while (1)
;
}