/* * 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 #include #include #include #include #include #include #include #include #include #include #include // ------------------------------------------------------------------------ // 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; } // NOTE rueegges: The capability owner for page table mappings is never set so we do it here // otherwise unmap does not work on cores with id > 0 mapping_cte->mdbnode.owner = my_core_id; /* 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(); } }