aos/kernel/arch/armv8/paging.c
Daniel Schwyn a7a01f8ea1 Bug fixes
Signed-off-by: Daniel Schwyn <daniel.schwyn@inf.ethz.ch>
2022-04-04 15:16:17 +02:00

901 lines
28 KiB
C

/*
* Copyright (c) 2009-2012,2015,2016 ETH Zurich.
* Copyright (c) 2015, Hewlett Packard Enterprise Development LP.
* Copyright (c) 2022, The University of British Columbia
*
* All rights reserved.
*
* This file is distributed under the terms in the attached LICENSE file.
* If you do not find this file, copies can be found by writing to:
* ETH Zurich D-INFK, Universitaetstrasse 6, CH-8092 Zurich. Attn: Systems Group.
*/
#include <kernel.h>
#include <dispatch.h>
#include <sysreg.h>
#include <paging_kernel_arch.h>
#include <string.h>
#include <exceptions.h>
#include <arm_hal.h>
#include <cap_predicates.h>
#include <dispatch.h>
#include <mdb/mdb_tree.h>
#include <dev/armv8_dev.h>
#include <useraccess.h>
// ------------------------------------------------------------------------
// Utility declarations
inline static uintptr_t paging_round_down(uintptr_t address, uintptr_t size)
{
return address & ~(size - 1);
}
inline static uintptr_t paging_round_up(uintptr_t address, uintptr_t size)
{
return (address + size - 1) & ~(size - 1);
}
inline static int aligned(uintptr_t address, uintptr_t bytes)
{
return (address & (bytes - 1)) == 0;
}
static void
paging_write_l0_entry(union armv8_ttable_entry *l0_table, lvaddr_t va, union armv8_ttable_entry l0)
{
assert(l0_table);
// union armv8_l0_entry *l0_table;
// if (ttbase == 0) {
// if(va < MEMORY_OFFSET)
// ttbase = sysreg_read_ttbr0_el1() + MEMORY_OFFSET;
// else
// ttbase = sysreg_read_ttbr1_el1() + MEMORY_OFFSET;
// }
// l0_table = (union armv8_l0_entry *) ttbase;
l0_table[VMSAv8_64_L0_INDEX(va)] = l0;
}
static void
paging_write_l1_entry(union armv8_ttable_entry *l1_table, lvaddr_t va, union armv8_ttable_entry l1)
{
assert(l1_table);
// union armv8_l1_entry *l1_table;
// if (ttbase == 0) {
// if(va < MEMORY_OFFSET)
// ttbase = sysreg_read_ttbr0_el1() + MEMORY_OFFSET;
// else
// ttbase = sysreg_read_ttbr1_el1() + MEMORY_OFFSET;
// }
// l1_table = (union armv8_l1_entry *) ttbase;
l1_table[VMSAv8_64_L1_INDEX(va)] = l1;
}
static void
paging_write_l2_entry(union armv8_ttable_entry *l2_table, lvaddr_t va, union armv8_ttable_entry l2)
{
assert(l2_table);
l2_table[VMSAv8_64_L2_INDEX(va)] = l2;
}
static void
paging_write_l3_entry(union armv8_ttable_entry *l3_table, lvaddr_t va, union armv8_ttable_entry l3)
{
assert(l3_table);
l3_table[VMSAv8_64_L3_INDEX(va)] = l3;
}
// ------------------------------------------------------------------------
// Exported functions
/**
* \brief Return whether we have enabled the MMU. Useful for
* initialization assertions
*/
bool paging_mmu_enabled(void)
{
return true;
}
void paging_map_kernel_section(union armv8_ttable_entry *ttbase, lvaddr_t va, lpaddr_t pa)
{
union armv8_ttable_entry l2;
l2.raw = 0;
l2.block_l2.valid = 1;
l2.block_l2.mb0 = 0;
l2.block_l2.af = 1;
l2.block_l2.base = pa >> 21u;
paging_write_l2_entry(ttbase, va, l2);
}
void paging_map_kernel_l1_block(union armv8_ttable_entry *ttbase, lvaddr_t va, lpaddr_t pa)
{
union armv8_ttable_entry l1;
l1.raw = 0;
l1.block_l1.valid = 1;
l1.block_l1.mb0 = 0;
l1.block_l1.af = 1;
l1.block_l1.base = pa >> 30u;
paging_write_l1_entry(ttbase, va, l1);
}
void paging_map_memory(union armv8_ttable_entry *ttbase, lpaddr_t paddr, size_t bytes)
{
lpaddr_t pend = paging_round_up(paddr + bytes, VMSAv8_64_L1_BLOCK_SIZE);
while (paddr < pend) {
paging_map_kernel_l1_block(ttbase,
(paddr + MEMORY_OFFSET) - KERNEL_OFFSET,
paddr);
paddr += VMSAv8_64_L1_BLOCK_SIZE;
}
}
//static lvaddr_t dev_alloc;
lvaddr_t paging_map_device(lpaddr_t device_base, size_t device_bytes)
{
// TODO: Implement
if (device_base < KERNEL_OFFSET) {
return device_base + KERNEL_OFFSET;
} else {
return device_base;
}
}
void paging_map_table_l0(union armv8_ttable_entry *table_base, lvaddr_t va, lpaddr_t pa)
{
assert(aligned((uintptr_t)table_base, VMSAv8_64_PTABLE_SIZE));
assert(aligned(pa, VMSAv8_64_PTABLE_SIZE));
union armv8_ttable_entry e;
e.raw = 0;
e.d.valid = 1;
e.d.mb1 = 1;
e.d.base = (pa >> BASE_PAGE_BITS);
paging_write_l0_entry(table_base, va, e);
}
void paging_map_table_l1(union armv8_ttable_entry *table_base, lvaddr_t va, lpaddr_t pa)
{
assert(aligned((uintptr_t)table_base, VMSAv8_64_PTABLE_SIZE));
assert(aligned(pa, VMSAv8_64_PTABLE_SIZE));
union armv8_ttable_entry e;
e.raw = 0;
e.d.valid = 1;
e.d.mb1 = 1;
e.d.base = (pa >> BASE_PAGE_BITS);
paging_write_l1_entry(table_base, va, e);
}
void paging_map_block_l1(union armv8_ttable_entry *table_base, lvaddr_t va, lpaddr_t pa, uintptr_t flags)
{
assert(aligned((uintptr_t)table_base, VMSAv8_64_PTABLE_SIZE));
assert(aligned(pa, VMSAv8_64_PTABLE_SIZE));
union armv8_ttable_entry e;
e.raw = flags;
e.block_l1.valid = 1;
e.block_l1.mb0 = 0;
e.block_l1.af = 1;
e.block_l1.base = (pa >> BASE_PAGE_BITS);
paging_write_l1_entry(table_base, va, e);
}
void paging_map_table_l2(union armv8_ttable_entry *table_base, lvaddr_t va, lpaddr_t pa)
{
assert(aligned((uintptr_t)table_base, VMSAv8_64_PTABLE_SIZE));
assert(aligned(pa, VMSAv8_64_PTABLE_SIZE));
assert(pa);
union armv8_ttable_entry e;
e.raw = 0;
e.d.valid = 1;
e.d.mb1 = 1;
e.d.base = (pa >> BASE_PAGE_BITS);
// printf("map table l2@%p->l3@%p\n", table_base + VMSAv8_64_L2_INDEX(va), pa);
paging_write_l2_entry(table_base, va, e);
}
void paging_map_block_l2(union armv8_ttable_entry *table_base, lvaddr_t va, lpaddr_t pa, uintptr_t flags)
{
assert(aligned((uintptr_t)table_base, VMSAv8_64_PTABLE_SIZE));
assert(aligned(pa, VMSAv8_64_PTABLE_SIZE));
union armv8_ttable_entry e;
e.raw = 0;
e.block_l2.valid = 1;
e.block_l2.mb0 = 0;
e.block_l2.af = 1;
e.block_l2.base = (pa >> BASE_PAGE_BITS);
paging_write_l2_entry(table_base, va, e);
}
void paging_map_page_l3(union armv8_ttable_entry *table_base, lvaddr_t va, lpaddr_t pa, uintptr_t flags)
{
assert(aligned((uintptr_t)table_base, VMSAv8_64_PTABLE_SIZE));
assert(aligned(pa, VMSAv8_64_PTABLE_SIZE));
assert(0 == (flags & 0xfffff000));
assert(0 == (flags & 0x3));
assert(0 == (pa & 0xfff));
union armv8_ttable_entry e;
e.raw = flags;
e.page.valid = 1;
e.page.mb1 = 1;
e.page.af= 1;
e.page.base = (pa >> BASE_PAGE_BITS);
paging_write_l3_entry(table_base, va, e);
}
void paging_set_l3_entry(union armv8_ttable_entry *l3_entry, lpaddr_t pa, uintptr_t flags)
{
assert(0 == (flags & 0xfffff000));
assert(0 == (flags & 0x3));
assert(0 == (pa & 0xfff));
union armv8_ttable_entry e;
e.raw = flags;
e.page.valid = 1;
e.page.mb1 = 1;
e.page.af= 1;
e.page.base = (pa >> BASE_PAGE_BITS);
// printf("Setting l3 entry@%p -> (%p) %p raw: %p\n", l3_entry, e.page.base, e.page.base << BASE_PAGE_BITS, e.raw);
*l3_entry = e;
}
//void paging_set_l2_entry(union armv8_ttable_entry *l2e, lpaddr_t addr, uintptr_t flags)
//{
// assert(0 == (flags & 0xfffff000));
// assert(0 == (flags & 0x3));
// assert(0 == (addr & 0xfff));
//
// union armv8_ttable_entry e;
// e.raw = flags;
//
// e.d.valid = 1;
// e.d.mb1 = 1;
// e.d.base = (addr >> 12);
//
// *l2e = e;
//}
//
//void paging_set_l3_entry(union armv8_ttable_entry *l3e, lpaddr_t addr, uintptr_t flags)
//{
// assert(0 == (flags & 0xfffff000));
// assert(0 == (flags & 0x3));
// assert(0 == (addr & 0xfff));
//
// union armv8_ttable_entry e;
// e.raw = flags;
//
// e.page.valid = 1;
// e.page.mb1 = 1;
// e.page.af = 1;
// e.page.base = (addr >> 12);
//
// *l3e = e;
//}
static void
paging_set_flags(union armv8_ttable_entry *entry, uintptr_t kpi_paging_flags)
{
entry->page.sh = 3;
entry->page.ap = 0;
if (kpi_paging_flags & KPI_PAGING_FLAGS_NOCACHE) {
entry->page.attrindex = 1;
} else {
entry->page.attrindex = 0;
}
if(kpi_paging_flags & KPI_PAGING_FLAGS_WRITE)
entry->page.ap = 1;
else if (kpi_paging_flags & KPI_PAGING_FLAGS_READ)
entry->page.ap = 3;
else
entry->page.ap = 0;
entry->page.af = 1;
}
static errval_t
caps_map_l0(struct capability* dest,
cslot_t slot,
struct capability* src,
uintptr_t kpi_paging_flags,
uintptr_t offset,
uintptr_t pte_count,
struct cte* mapping_cte)
{
if (slot >= VMSAv8_64_PTABLE_NUM_ENTRIES) {
debug(SUBSYS_PAGING, "slot = %"PRIuCSLOT"\n",slot);
return SYS_ERR_VNODE_SLOT_INVALID;
}
if (pte_count != 1) {
debug(SUBSYS_PAGING, "pte_count = %zu\n",(size_t)pte_count);
return SYS_ERR_VM_MAP_SIZE;
}
if (src->type != ObjType_VNode_AARCH64_l1) {
char buf[128];
sprint_cap(buf, 128, src);
debug(SUBSYS_PAGING, "src: %s\n", buf);
return SYS_ERR_WRONG_MAPPING;
}
// Destination
lpaddr_t dest_lpaddr = gen_phys_to_local_phys(get_address(dest));
lvaddr_t dest_lvaddr = local_phys_to_mem(dest_lpaddr);
union armv8_ttable_entry* entry = (union armv8_ttable_entry*) dest_lvaddr + slot;
// Source
genpaddr_t src_gpaddr = get_address(src);
lpaddr_t src_lpaddr = gen_phys_to_local_phys(src_gpaddr);
//union armv8_l2_entry* entry1 = (union armv8_l2_entry*)local_phys_to_mem(src_gpaddr);
assert(offset == 0);
assert(aligned(src_lpaddr, 1u << 12));
assert((src_lpaddr < dest_lpaddr) || (src_lpaddr >= dest_lpaddr + 32));
if (entry->d.valid) {
// cleanup mapping info
debug(SUBSYS_PAGING, "slot in use\n");
return SYS_ERR_VNODE_SLOT_INUSE;
}
create_mapping_cap(mapping_cte, src, cte_for_cap(dest), slot, pte_count);
entry->raw = 0;
entry->d.valid = 1;
entry->d.mb1 = 1;
entry->d.base = (src_lpaddr) >> 12;
debug(SUBSYS_PAGING, "L0 mapping %"PRIuCSLOT". @%p = %08"PRIx32"\n",
slot, entry, entry->raw);
sysreg_invalidate_tlb();
return SYS_ERR_OK;
}
static errval_t
caps_map_l1(struct capability* dest,
cslot_t slot,
struct capability* src,
uintptr_t kpi_paging_flags,
uintptr_t offset,
uintptr_t pte_count,
struct cte* mapping_cte)
{
if (slot >= VMSAv8_64_PTABLE_NUM_ENTRIES) {
debug(SUBSYS_PAGING, "slot = %"PRIuCSLOT"\n",slot);
return SYS_ERR_VNODE_SLOT_INVALID;
}
if (pte_count != 1) {
debug(SUBSYS_PAGING, "pte_count = %zu\n",(size_t)pte_count);
return SYS_ERR_VM_MAP_SIZE;
}
if (src->type != ObjType_VNode_AARCH64_l2) {
return SYS_ERR_WRONG_MAPPING;
}
if (slot >= VMSAv8_64_PTABLE_NUM_ENTRIES) {
debug(SUBSYS_PAGING, "slot = %"PRIuCSLOT"\n",slot);
return SYS_ERR_VNODE_SLOT_RESERVED;
}
// Destination
lpaddr_t dest_lpaddr = gen_phys_to_local_phys(get_address(dest));
lvaddr_t dest_lvaddr = local_phys_to_mem(dest_lpaddr);
union armv8_ttable_entry* entry = (union armv8_ttable_entry*) dest_lvaddr + slot;
// Source
genpaddr_t src_gpaddr = get_address(src);
lpaddr_t src_lpaddr = gen_phys_to_local_phys(src_gpaddr);
//union armv8_l2_entry* entry1 = (union armv8_l2_entry*)local_phys_to_mem(src_gpaddr);
assert(offset == 0);
assert(aligned(src_lpaddr, 1u << 12));
assert((src_lpaddr < dest_lpaddr) || (src_lpaddr >= dest_lpaddr + 32));
if (entry->d.valid) {
// cleanup mapping info
debug(SUBSYS_PAGING, "slot in use\n");
return SYS_ERR_VNODE_SLOT_INUSE;
}
create_mapping_cap(mapping_cte, src, cte_for_cap(dest), slot, pte_count);
entry->raw = 0;
entry->d.valid = 1;
entry->d.mb1 = 1;
entry->d.base = (src_lpaddr) >> 12;
debug(SUBSYS_PAGING, "L1 mapping %"PRIuCSLOT". @%p = %08"PRIx32"\n",
slot, entry, entry->raw);
sysreg_invalidate_tlb();
return SYS_ERR_OK;
}
static errval_t caps_map_l2(struct capability *dest, cslot_t slot, struct capability *src,
uintptr_t kpi_paging_flags, uintptr_t offset,
uintptr_t pte_count, struct cte *mapping_cte)
{
if (slot >= VMSAv8_64_PTABLE_NUM_ENTRIES) {
debug(SUBSYS_PAGING, "slot = %" PRIuCSLOT "\n", slot);
return SYS_ERR_VNODE_SLOT_INVALID;
}
// Destination
lpaddr_t dest_lpaddr = gen_phys_to_local_phys(get_address(dest));
lvaddr_t dest_lvaddr = local_phys_to_mem(dest_lpaddr);
// source
genpaddr_t src_gpaddr = get_address(src) + offset;
lpaddr_t src_lpaddr = gen_phys_to_local_phys(src_gpaddr);
union armv8_ttable_entry *entry = (union armv8_ttable_entry *)dest_lvaddr + slot;
switch (src->type) {
case ObjType_VNode_AARCH64_l3:
if (pte_count != 1) {
debug(SUBSYS_PAGING, "pte_count = %zu\n", (size_t)pte_count);
return SYS_ERR_VM_MAP_SIZE;
}
if (offset != 0) {
debug(SUBSYS_PAGING, "offset = %zu\n", (size_t)offset);
return SYS_ERR_VM_MAP_OFFSET;
}
if (entry->d.valid) {
// cleanup mapping info
debug(SUBSYS_PAGING, "slot in use\n");
return SYS_ERR_VNODE_SLOT_INUSE;
}
assert(offset == 0);
assert(aligned(src_lpaddr, 1u << 12));
assert((src_lpaddr < dest_lpaddr) || (src_lpaddr >= dest_lpaddr + 4096));
entry->raw = 0;
entry->d.valid = 1;
entry->d.mb1 = 1;
entry->d.base = (src_lpaddr) >> 12;
debug(SUBSYS_PAGING, "L2 mapping %" PRIuCSLOT ". @%p = %08" PRIx32 "\n", slot,
entry, entry->raw);
break;
default:
return SYS_ERR_WRONG_MAPPING;
}
create_mapping_cap(mapping_cte, src, cte_for_cap(dest), slot, pte_count);
sysreg_invalidate_tlb();
return SYS_ERR_OK;
}
static errval_t
caps_map_l3(struct capability* dest,
cslot_t slot,
struct capability* src,
uintptr_t kpi_paging_flags,
uintptr_t offset,
uintptr_t pte_count,
struct cte* mapping_cte)
{
assert(0 == (kpi_paging_flags & ~KPI_PAGING_FLAGS_MASK));
// Check slot is valid and mapping does not overlap leaf page table
if (slot + pte_count > VMSAv8_64_PTABLE_NUM_ENTRIES) {
if (slot >= VMSAv8_64_PTABLE_NUM_ENTRIES) {
return SYS_ERR_VNODE_SLOT_INVALID;
}
else {
return SYS_ERR_VM_MAP_SIZE;
}
}
if (src->type != ObjType_Frame && src->type != ObjType_DevFrame) {
return SYS_ERR_WRONG_MAPPING;
}
// check offset within frame
if ((offset + pte_count * BASE_PAGE_SIZE > get_size(src)) ||
((offset % BASE_PAGE_SIZE) != 0)) {
return SYS_ERR_FRAME_OFFSET_INVALID;
}
// Destination
lpaddr_t dest_lpaddr = gen_phys_to_local_phys(get_address(dest));
lvaddr_t dest_lvaddr = local_phys_to_mem(dest_lpaddr);
union armv8_ttable_entry *entry = (union armv8_ttable_entry *)dest_lvaddr + slot;
for (int i = 0; i < pte_count; i++) {
if (entry[i].d.valid) {
// cleanup mapping info
debug(SUBSYS_PAGING, "ARMv8 L3 @ 0x%lx: slot %d in use\n", dest_lpaddr, slot + i);
return SYS_ERR_VNODE_SLOT_INUSE;
}
}
lpaddr_t src_lpaddr = gen_phys_to_local_phys(get_address(src) + offset);
if ((src_lpaddr & (BASE_PAGE_SIZE - 1))) {
return SYS_ERR_VM_FRAME_UNALIGNED;
}
create_mapping_cap(mapping_cte, src, cte_for_cap(dest), slot, pte_count);
for (int i = 0; i < pte_count; i++) {
entry->raw = 0;
entry->page.valid = 1;
entry->page.mb1 = 1;
paging_set_flags(entry, kpi_paging_flags);
entry->page.base = (src_lpaddr + i * BASE_PAGE_SIZE) >> 12;
debug(SUBSYS_PAGING, "L3 mapping %08"PRIxLVADDR"[%"PRIuCSLOT"] @%p = %08"PRIx64"\n",
dest_lvaddr, slot, entry, entry->raw);
entry++;
}
// Flush TLB if remapping.
sysreg_invalidate_tlb();
return SYS_ERR_OK;
}
typedef errval_t (*mapping_handler_t)(struct capability *dest_cap,
cslot_t dest_slot,
struct capability *src_cap,
uintptr_t flags, uintptr_t offset,
size_t pte_count,
struct cte *mapping_cte);
/// Dispatcher table for the type of mapping to create
static mapping_handler_t handler[ObjType_Num] = {
[ObjType_VNode_AARCH64_l0] = caps_map_l0,
[ObjType_VNode_AARCH64_l1] = caps_map_l1,
[ObjType_VNode_AARCH64_l2] = caps_map_l2,
[ObjType_VNode_AARCH64_l3] = caps_map_l3,
};
/// Create page mappings
errval_t caps_copy_to_vnode(struct cte *dest_vnode_cte, cslot_t dest_slot,
struct cte *src_cte, uintptr_t flags,
uintptr_t offset, uintptr_t pte_count,
struct cte *mapping_cte)
{
struct capability *src_cap = &src_cte->cap;
struct capability *dest_cap = &dest_vnode_cte->cap;
assert(mapping_cte->cap.type == ObjType_Null);
mapping_handler_t handler_func = handler[dest_cap->type];
assert(handler_func != NULL);
errval_t err;
err = handler_func(dest_cap, dest_slot, src_cap, flags, offset, pte_count,
mapping_cte);
if (err_is_fail(err)) {
assert(mapping_cte->cap.type == ObjType_Null);
debug(SUBSYS_PAGING,
"caps_copy_to_vnode: handler func returned %"PRIuERRV"\n", err);
return err;
}
/* insert mapping cap into mdb */
err = mdb_insert(mapping_cte);
if (err_is_fail(err)) {
printk(LOG_ERR, "%s: mdb_insert: %"PRIuERRV"\n", __FUNCTION__, err);
}
TRACE_CAP_MSG("created", mapping_cte);
return err;
}
size_t do_unmap(lvaddr_t pt, cslot_t slot, size_t num_pages)
{
size_t unmapped_pages = 0;
union armv8_ttable_entry *ptentry = (union armv8_ttable_entry *)pt + slot;
for (int i = 0; i < num_pages; i++) {
ptentry++->raw = 0;
unmapped_pages++;
}
return unmapped_pages;
}
static inline void read_pt_entry(struct capability *pgtable, size_t slot, genpaddr_t *paddr)
{
assert(type_is_vnode(pgtable->type));
assert(paddr);
genpaddr_t gp = get_address(pgtable);
lpaddr_t lp = gen_phys_to_local_phys(gp);
lvaddr_t lv = local_phys_to_mem(lp);
switch (pgtable->type) {
case ObjType_VNode_AARCH64_l0:
{
union armv8_ttable_entry *e = (union armv8_ttable_entry *) lv;
*paddr = (genpaddr_t) (e->d.base) << 12;
return;
}
case ObjType_VNode_AARCH64_l1:
{
union armv8_ttable_entry *e = (union armv8_ttable_entry *) lv;
*paddr = (genpaddr_t) (e->d.base) << 12;
return;
}
case ObjType_VNode_AARCH64_l2:
{
union armv8_ttable_entry *e = (union armv8_ttable_entry *) lv;
*paddr = (genpaddr_t) (e->d.base) << 12;
return;
}
case ObjType_VNode_AARCH64_l3:
{
union armv8_ttable_entry *e = (union armv8_ttable_entry *) lv;
*paddr = (genpaddr_t) (e->page.base) << 12;
return;
}
default:
assert(!"Should not get here");
}
}
errval_t paging_modify_flags(struct capability *mapping, uintptr_t offset,
uintptr_t pages, uintptr_t kpi_paging_flags)
{
assert(type_is_mapping(mapping->type));
struct Frame_Mapping *info = &mapping->u.frame_mapping;
// check flags
assert(0 == (kpi_paging_flags & ~KPI_PAGING_FLAGS_MASK));
/* Calculate location of page table entries we need to modify */
lvaddr_t base = local_phys_to_mem(get_address(&info->ptable->cap)) +
(info->entry + offset) * sizeof(union armv8_ttable_entry *);
for (int i = 0; i < pages; i++) {
union armv8_ttable_entry *entry =
(union armv8_ttable_entry *)base + i;
paging_set_flags(entry, kpi_paging_flags);
}
return paging_tlb_flush_range(cte_for_cap(mapping), 0, pages);
}
errval_t paging_copy_remap(struct cte *dest_vnode_cte, cslot_t dest_slot,
struct cte *src_cte, uintptr_t flags,
uintptr_t offset, uintptr_t pte_count,
struct cte *mapping_cte)
{
printk(LOG_ERR, "%s called on ARMv8: NYI!\n", __FUNCTION__);
return SYS_ERR_NOT_IMPLEMENTED;
}
bool paging_is_region_valid(lvaddr_t buffer, size_t size, uint8_t type)
{
// XXX: ARMv8 does not seem to have ro mappings? -SG,2018-10-25.
// this currently ignores type
lvaddr_t root_pt = local_phys_to_mem(dcb_current->vspace);
lvaddr_t end = buffer + size;
uint16_t first_l0idx = VMSAv8_64_L0_INDEX(buffer);
uint16_t last_l0idx = VMSAv8_64_L0_INDEX(end);
uint16_t first_l1idx, first_l2idx, first_l3idx;
uint16_t last_l1idx, last_l2idx, last_l3idx;
union armv8_ttable_entry *pte;
for (uint16_t l0idx = first_l0idx; l0idx <= last_l0idx; l0idx++) {
pte = (union armv8_ttable_entry *)root_pt + l0idx;
if (!pte->d.valid) { return false; }
// calculate which part of pdpt to check
first_l1idx = l0idx == first_l0idx ? VMSAv8_64_L1_INDEX(buffer) : 0;
last_l1idx = l0idx == last_l0idx ? VMSAv8_64_L1_INDEX(end) : PTABLE_ENTRIES;
// read pdpt base
lvaddr_t pdpt = local_phys_to_mem((genpaddr_t)pte->d.base << BASE_PAGE_BITS);
for (uint16_t l1idx = first_l1idx; l1idx <= last_l1idx; l1idx++) {
pte = (union armv8_ttable_entry *)pdpt + l1idx;
if (!pte->d.valid) { return false; }
// calculate which part of pdpt to check
first_l2idx = l1idx == first_l1idx ? VMSAv8_64_L2_INDEX(buffer) : 0;
last_l2idx = l1idx == last_l1idx ? VMSAv8_64_L2_INDEX(end) : PTABLE_ENTRIES;
// read pdpt base
lvaddr_t pdir = local_phys_to_mem((genpaddr_t)pte->d.base << BASE_PAGE_BITS);
for (uint16_t l2idx = first_l2idx; l2idx <= last_l2idx; l2idx++) {
pte = (union armv8_ttable_entry *)pdir + l2idx;
if (!pte->d.valid) { return false; }
// calculate which part of pdpt to check
first_l3idx = l2idx == first_l2idx ? VMSAv8_64_L3_INDEX(buffer) : 0;
last_l3idx = l2idx == last_l2idx ? VMSAv8_64_L3_INDEX(end) : PTABLE_ENTRIES;
// read pdpt base
lvaddr_t pt = local_phys_to_mem((genpaddr_t)pte->d.base << BASE_PAGE_BITS);
for (uint16_t l3idx = first_l3idx; l3idx < last_l3idx; l3idx++) {
pte = (union armv8_ttable_entry *)pt + l3idx;
if (!pte->page.valid) { return false; }
}
}
}
}
// if we never bailed early, the access is fine.
return true;
}
void paging_dump_tables(struct dcb *dispatcher, lvaddr_t addr)
{
if (!local_phys_is_valid(dispatcher->vspace)) {
printk(LOG_ERR, "dispatcher->vspace = 0x%"PRIxLPADDR": too high!\n" ,
dispatcher->vspace);
return;
}
lvaddr_t l0 = local_phys_to_mem(dispatcher->vspace);
if (addr < (VMSAv8_64_PTABLE_NUM_ENTRIES * VMSAv8_64_PTABLE_NUM_ENTRIES * HUGE_PAGE_SIZE)) {
uint32_t l0_index = VMSAv8_64_L0_INDEX(addr);
uint32_t l1_index = VMSAv8_64_L1_INDEX(addr);
uint32_t l2_index = VMSAv8_64_L2_INDEX(addr);
uint32_t l3_index = VMSAv8_64_L3_INDEX(addr);
printf("dumping table: %lx -> %u.%u.%u.%u\n", addr, l0_index, l1_index, l2_index, l3_index);
union armv8_ttable_entry *l0_e = (union armv8_ttable_entry *) l0 + l0_index;
if (!l0_e->d.valid) {
printf("l0[% 3d] -> INVALID\n", l0_index);
return;
}
genpaddr_t l1_gp = (genpaddr_t)(l0_e->d.base) << BASE_PAGE_BITS;
lvaddr_t l1 = local_phys_to_mem(gen_phys_to_local_phys(l1_gp));
printf("l0[% 3d] -> l1 @ %p\n", l0_index, l1);
union armv8_ttable_entry *l1_e = (union armv8_ttable_entry *)l1 + l1_index;
if (!l1_e->d.valid) {
printf(" l1[% 3d] -> INVALID\n", l1_index);
return;
}
// super page mapping
if (l1_e->block_l1.mb0 == 0) {
genpaddr_t l2_gp = (genpaddr_t)(l1_e->block_l1.base) << HUGE_PAGE_BITS;
printf(" l1[% 3d] -> HUGE_PAGE @ 0x%lx\n", l1_index, l2_gp);
return;
}
genpaddr_t l2_gp = (genpaddr_t)(l1_e->d.base) << BASE_PAGE_BITS;
lvaddr_t l2 = local_phys_to_mem(gen_phys_to_local_phys(l2_gp));
printf(" l1[% 3d] -> l2 @ %p\n", l1_index, l2);
union armv8_ttable_entry *l2_e = (union armv8_ttable_entry *)l2 + l2_index;
if (!l2_e->d.valid) {
printf(" l2[% 3d] -> INVALID\n", l2_index);
return;
}
if (l2_e->block_l2.mb0 == 0) {
genpaddr_t l3_gp = (genpaddr_t)(l1_e->block_l2.base) << LARGE_PAGE_BITS;
printf(" l2[% 3d] -> LARGE_PAGE @ 0x%lx\n", l1_index, l3_gp);
return;
}
genpaddr_t l3_gp = (genpaddr_t)(l2_e->d.base) << BASE_PAGE_BITS;
lvaddr_t l3 = local_phys_to_mem(gen_phys_to_local_phys(l3_gp));
printf(" l2 [% 3d] -> l3 @ %p\n", l2_index, l3);
union armv8_ttable_entry *l3_e = (union armv8_ttable_entry *)l3 + l3_index;
if (!l3_e->d.valid) {
printf(" l3[% 3d] -> INVALID\n", l3_index);
return;
}
genpaddr_t paddr = (genpaddr_t)(l3_e->page.base) << BASE_PAGE_BITS;
printf(" l3[% 3d] -> BASE_PAGE @ 0x%lx \n", l3_index, paddr);
return;
}
for (int l0_index = 0; l0_index < VMSAv8_64_PTABLE_NUM_ENTRIES; l0_index++) {
// get level0 table
union armv8_ttable_entry *l0_e = (union armv8_ttable_entry *) l0 + l0_index;
if (!l0_e->raw) {
continue;
}
genpaddr_t l1_gp = (genpaddr_t)(l0_e->d.base) << BASE_PAGE_BITS;
lvaddr_t l1 = local_phys_to_mem(gen_phys_to_local_phys(l1_gp));
printf("l0 %d -> %p\n", l0_index, l1);
for (int l1_index = 0; l1_index < VMSAv8_64_PTABLE_NUM_ENTRIES; l1_index++) {
// get level1 table
union armv8_ttable_entry *l1_e = (union armv8_ttable_entry *)l1 + l1_index;
if (!l1_e->raw) { continue; }
genpaddr_t l2_gp = (genpaddr_t)(l1_e->d.base) << BASE_PAGE_BITS;
lvaddr_t l2 = local_phys_to_mem(gen_phys_to_local_phys(l2_gp));
printf(" l1 %d -> %p\n", l1_index, l2);
for (int l2_index = 0; l2_index < VMSAv8_64_PTABLE_NUM_ENTRIES; l2_index++) {
// get level2 table
union armv8_ttable_entry *l2_e = (union armv8_ttable_entry *)l2 + l2_index;
if (!l2_e->raw) { continue; }
genpaddr_t l3_gp = (genpaddr_t)(l2_e->d.base) << BASE_PAGE_BITS;
lvaddr_t l3 = local_phys_to_mem(gen_phys_to_local_phys(l3_gp));
printf(" l2 %d -> %p\n", l2_index, l3);
for (int entry = 0; entry < VMSAv8_64_PTABLE_NUM_ENTRIES; entry++) {
union armv8_ttable_entry *e =
(union armv8_ttable_entry *)l3 + entry;
genpaddr_t paddr = (genpaddr_t)(e->page.base) << BASE_PAGE_BITS;
if (!paddr) {
continue;
}
printf("%d.%d.%d.%d: 0x%"PRIxGENPADDR" \n", l0_index, l1_index, l2_index, entry, paddr);
}
}
}
}
}
/* XXX - rewrite this. */
void paging_context_switch(lpaddr_t ttbr)
{
assert(ttbr < MEMORY_OFFSET);
//assert((ttbr & 0x3fff) == 0);
lpaddr_t old_ttbr = armv8_TTBR0_EL1_rd(NULL);
if (ttbr != old_ttbr)
{
armv8_TTBR0_EL1_wr(NULL, ttbr);
sysreg_invalidate_tlb();
//this isn't necessary on gem5, since gem5 doesn't implement the cache
//maintenance instructions, but ensures coherency by itself
//sysreg_invalidate_i_and_d_caches();
}
}