//
const int ARM_L1_SCALE = 4;
- if (slot >= 1024) {
- printf("slot = %"PRIuCSLOT"\n",slot);
- panic("oops: slot id >= 1024");
- return SYS_ERR_VNODE_SLOT_INVALID;
- }
-
- if (pte_count != 1) {
- printf("pte_count = %zu\n",(size_t)pte_count);
- panic("oops: pte_count");
- return SYS_ERR_VM_MAP_SIZE;
- }
-
if (src->type != ObjType_VNode_ARM_l2) {
//large page mapping goes here
- printf("kernel large page\n");
- //panic("oops: wrong src type");
assert(0 == (kpi_paging_flags & ~KPI_PAGING_FLAGS_MASK));
- // ARM L1 has 4K entries, but we treat it as if it had 1K
- if (slot >= (256 * 4)) {
- panic("oops: slot >= (256 * 4)");
+ // ARM L1 has 4K entries, we need to fill in individual entries for
+ // 1M sections
+ // XXX: magic constant
+ if (slot >= 4096) {
+ panic("oops: slot >= 4096");
return SYS_ERR_VNODE_SLOT_INVALID;
}
}
// check offset within frame
- if ((offset + BYTES_PER_SECTION > get_size(src)) ||
+ if ((offset + pte_count * BYTES_PER_SECTION > get_size(src)) ||
((offset % BYTES_PER_SECTION) != 0)) {
+ printf("offset = %"PRIuPTR", pte_count=%"PRIuPTR
+ ", src->size = %"PRIuGENSIZE", src->type = %d\n",
+ offset, pte_count, get_size(src), src->type);
panic("oops: frame offset invalid");
return SYS_ERR_FRAME_OFFSET_INVALID;
}
// check mapping does not overlap leaf page table
- if (slot + pte_count > (256 * 4)) {
+ if (slot + pte_count > 4096) {
return SYS_ERR_VM_MAP_SIZE;
}
lpaddr_t dest_lpaddr = gen_phys_to_local_phys(get_address(dest));
lvaddr_t dest_lvaddr = local_phys_to_mem(dest_lpaddr);
- union arm_l1_entry* entry = (union arm_l1_entry*)dest_lvaddr + slot;
+ union arm_l1_entry* entry = ((union arm_l1_entry*)dest_lvaddr) + slot;
if (entry->invalid.type != L1_TYPE_INVALID_ENTRY) {
panic("Remapping valid page.");
}
entry->section.ap10 = (kpi_paging_flags & KPI_PAGING_FLAGS_READ)? 2:0;
entry->section.ap10 |= (kpi_paging_flags & KPI_PAGING_FLAGS_WRITE)? 3:0;
entry->section.ap2 = 0;
- entry->section.base_address = (src_lpaddr + i * BYTES_PER_SECTION) >> 12;
+ entry->section.base_address = (src_lpaddr + i * BYTES_PER_SECTION) >> 20;
entry++;
// Flush TLB if remapping.
cp15_invalidate_tlb();
return SYS_ERR_OK;
- return SYS_ERR_WRONG_MAPPING;
}
- if (slot >= ARM_L1_OFFSET(MEMORY_OFFSET) / ARM_L1_SCALE) {
+ // XXX: magic constant
+ if (slot >= 4096) {
+ printf("slot = %"PRIuCSLOT"\n",slot);
+ panic("oops: slot id >= 4096");
+ return SYS_ERR_VNODE_SLOT_INVALID;
+ }
+
+
+ // check offset within frame
+ if ((offset + pte_count * 1024 > get_size(src)) ||
+ ((offset % 1024) != 0)) {
+ printf("offset = %"PRIuPTR", pte_count=%"PRIuPTR
+ ", src->size = %"PRIuGENSIZE", src->type = %d\n",
+ offset, pte_count, get_size(src), src->type);
+ panic("oops: ptable offset invalid");
+ return SYS_ERR_FRAME_OFFSET_INVALID;
+ }
+
+ // check mapping does not overlap leaf page table
+ if (slot + pte_count > 4096) {
+ return SYS_ERR_VM_MAP_SIZE;
+ }
+
+
+ if (slot >= ARM_L1_OFFSET(MEMORY_OFFSET)) {
printf("slot = %"PRIuCSLOT"\n",slot);
panic("oops: slot id");
return SYS_ERR_VNODE_SLOT_RESERVED;
}
+ debug(SUBSYS_PAGING, "caps_map_l1: mapping %"PRIuPTR" L2 tables @%"PRIuCSLOT"\n",
+ pte_count, slot);
// Destination
lpaddr_t dest_lpaddr = gen_phys_to_local_phys(get_address(dest));
lvaddr_t dest_lvaddr = local_phys_to_mem(dest_lpaddr);
- union arm_l1_entry* entry = (union arm_l1_entry*)dest_lvaddr + (slot * ARM_L1_SCALE);
+ union arm_l1_entry* entry = (union arm_l1_entry*)dest_lvaddr + slot;
// Source
genpaddr_t src_gpaddr = get_address(src);
- lpaddr_t src_lpaddr = gen_phys_to_local_phys(src_gpaddr);
+ lpaddr_t src_lpaddr = gen_phys_to_local_phys(src_gpaddr) + offset;
- assert(offset == 0);
assert(aligned(src_lpaddr, 1u << 10));
assert((src_lpaddr < dest_lpaddr) || (src_lpaddr >= dest_lpaddr + 16384));
struct cte *src_cte = cte_for_cap(src);
src_cte->mapping_info.pte_count = pte_count;
- src_cte->mapping_info.pte = dest_lpaddr + (slot * ARM_L1_SCALE);
+ src_cte->mapping_info.pte = dest_lpaddr + slot;
src_cte->mapping_info.offset = 0;
- for (int i = 0; i < 4; i++, entry++)
+ for (int i = 0; i < pte_count; i++, entry++)
{
entry->raw = 0;
entry->page_table.type = L1_TYPE_PAGE_TABLE_ENTRY;
entry->page_table.base_address =
(src_lpaddr + i * BASE_PAGE_SIZE / ARM_L1_SCALE) >> 10;
debug(SUBSYS_PAGING, "L1 mapping %"PRIuCSLOT". @%p = %08"PRIx32"\n",
- slot * ARM_L1_SCALE + i, entry, entry->raw);
+ slot + i, entry, entry->raw);
}
cp15_invalidate_tlb();
paging_set_flags(entry, kpi_paging_flags);
entry->small_page.base_address = (src_lpaddr + i * BYTES_PER_PAGE) >> 12;
- entry++;
-
debug(SUBSYS_PAGING, "L2 mapping %08"PRIxLVADDR"[%"PRIuCSLOT"] @%p = %08"PRIx32"\n",
dest_lvaddr, slot, entry, entry->raw);
+
+ entry++;
}
// Flush TLB if remapping.
return unmapped_pages;
}
-static inline void read_pt_entry(struct capability *pgtable, size_t slot, genpaddr_t *paddr)
+static inline void read_pt_entry(struct capability *pgtable,
+ size_t slot, bool is_section, genpaddr_t *paddr)
{
assert(type_is_vnode(pgtable->type));
assert(paddr);
case ObjType_VNode_ARM_l1:
{
union arm_l1_entry *e = (union arm_l1_entry*)lv;
- *paddr = (genpaddr_t)(e->page_table.base_address) << 10;
- return;
+ if (is_section) {
+ *paddr = (genpaddr_t)(e->section.base_address) << 20;
+ return;
+ } else {
+ *paddr = (genpaddr_t)(e->page_table.base_address) << 10;
+ return;
+ }
}
case ObjType_VNode_ARM_l2:
{
assert(type_is_vnode(pgtable->type));
//printf("page_mappings_unmap(%zd pages, slot = %zd)\n", num_pages, slot);
+ bool is_section = false;
+ if (pgtable->type == ObjType_VNode_ARM_l1) {
+ // transform slot to hw slot
+ if (mapping->cap.type == ObjType_VNode_ARM_l2) {
+ // l2 table
+ debug(SUBSYS_PAGING, "unmapping l2 tables: %zu, #pages: %zu\n",
+ slot, num_pages);
+ }
+ else {
+ // section
+ is_section = true;
+ debug(SUBSYS_PAGING, "unmapping section: %zu, #pages: %zu\n",
+ slot, num_pages);
+ }
+ }
// get page table entry data
genpaddr_t paddr;
//lpaddr_t pte;
- read_pt_entry(pgtable, slot, &paddr);
+ read_pt_entry(pgtable, slot, is_section, &paddr);
lvaddr_t pt = local_phys_to_mem(gen_phys_to_local_phys(get_address(pgtable)));
// get virtual address of first page
//printf("state before unmap: num_pages = %zd\n", num_pages);
if (num_pages != mapping->mapping_info.pte_count) {
- printf("num_pages = %zu, mapping = %zu\n", num_pages, mapping->mapping_info.pte_count);
+ debug(SUBSYS_PAGING, "num_pages = %zu, mapping = %zu\n",
+ num_pages, mapping->mapping_info.pte_count);
// want to unmap a different amount of pages than was mapped
return SYS_ERR_VM_MAP_SIZE;
}
// get level2 table
union arm_l1_entry *l1_e = (union arm_l1_entry *)l1 + l1_index;
if (!l1_e->raw) { continue; }
- genpaddr_t ptable_gp = (genpaddr_t)(l1_e->page_table.base_address) << 10;
- lvaddr_t ptable_lv = local_phys_to_mem(gen_phys_to_local_phys(ptable_gp));
-
- for (int entry = 0; entry < ARM_L2_MAX_ENTRIES; entry++) {
- union arm_l2_entry *e =
- (union arm_l2_entry *)ptable_lv + entry;
- genpaddr_t paddr = (genpaddr_t)(e->small_page.base_address) << BASE_PAGE_BITS;
- if (!paddr) {
- continue;
+ if (l1_e->invalid.type == 2) { // section
+ genpaddr_t paddr = (genpaddr_t)(l1_e->section.base_address) << 20;
+ printf("%d: (section) 0x%"PRIxGENPADDR"\n", l1_index, paddr);
+ } else if (l1_e->invalid.type == 1) { // l2 table
+ genpaddr_t ptable_gp = (genpaddr_t)(l1_e->page_table.base_address) << 10;
+ lvaddr_t ptable_lv = local_phys_to_mem(gen_phys_to_local_phys(ptable_gp));
+
+ printf("%d: (l2table) 0x%"PRIxGENPADDR"\n", l1_index, ptable_gp);
+
+ for (int entry = 0; entry < ARM_L2_MAX_ENTRIES; entry++) {
+ union arm_l2_entry *e =
+ (union arm_l2_entry *)ptable_lv + entry;
+ genpaddr_t paddr = (genpaddr_t)(e->small_page.base_address) << BASE_PAGE_BITS;
+ if (!paddr) {
+ continue;
+ }
+ printf("%d.%d: 0x%"PRIxGENPADDR"\n", l1_index, entry, paddr);
}
- printf("%d.%d: 0x%"PRIxGENPADDR"\n", l1_index, entry, paddr);
}
}
}
*/
/*
- * Copyright (c) 2010-2013 ETH Zurich.
+ * Copyright (c) 2010-2015 ETH Zurich.
* All rights reserved.
*
* This file is distributed under the terms in the attached LICENSE file.
// Location of VSpace managed by this system.
#ifdef __ARM_ARCH_7M__
-//virtual section 0x40000000-0x40100000 can not be used as regular memory
+//virtual section 0x40000000-0x40100000 can not be used as regular memory
//because of "bit-banding".
//0x42000000-0x44000000 is also dangerous, so we start after that
-//XXX: there are more virtual regions we
+//XXX: there are more virtual regions we
//are not allowed to use -> find out where to reserve those
#define VSPACE_BEGIN ((lvaddr_t)(1UL*1024*1024*1024 + 64UL*1024*1024)) //0x44000000
#else //"normal" arm architectures
#define ARM_USER_L1_OFFSET(addr) ((uintptr_t)(addr >> 22) & 0x3ffu)
#define ARM_USER_L2_OFFSET(addr) ((uintptr_t)(addr >> 12) & 0x3ffu)
-//large mapping flags
-#define FLAGS_LARGE 0x0100
-#define FLAGS_SECTION 0x0200
-#define FLAGS_SUPERSECTION 0x0300
-
static inline uintptr_t
vregion_flags_to_kpi_paging_flags(vregion_flags_t flags)
{
return (uintptr_t)flags;
}
+// debug print preprocessor flag for this file
+//#define LIBBARRELFISH_DEBUG_PMAP
+
+/**
+ * \brief check whether region A = [start_a .. end_a) overlaps
+ * region B = [start_b .. end_b).
+ * \return true iff A overlaps B
+ */
+static bool is_overlapping(uint16_t start_a, uint16_t end_a, uint16_t start_b, uint16_t end_b)
+{
+ return
+ // B strict subset of A
+ (start_a < start_b && end_a >= end_b)
+ // start_a inside B
+ || (start_a >= start_b && start_a < end_b)
+ // end_a inside B
+ || (end_a > start_b && end_a < end_b);
+}
+
+/**
+ * \brief Check whether vnode `root' has entries in between [entry ..
+ * entry+len).
+ * \param root the vnode to look at
+ * \param entry first entry of the region to check
+ * \param len length of the region to check
+ * \param only_pages true == do not report previously allocated lower-level
+ * page tables that are empty
+ * \return true iff entries exist in region.
+ */
+#if defined(LIBBARRELFISH_DEBUG_PMAP)
+#define DEBUG_HAS_VNODE
+#endif
+static bool has_vnode(struct vnode *root, uint32_t entry, size_t len,
+ bool only_pages)
+{
+ assert(root != NULL);
+ assert(root->is_vnode);
+ struct vnode *n;
+
+ uint32_t end_entry = entry + len;
+#ifdef DEBUG_HAS_VNODE
+ debug_printf("%s: checking region [%"PRIu32"--%"PRIu32"], only_pages = %d\n",
+ __FUNCTION__, entry, end_entry, only_pages);
+#endif
+
+ bool found_pages = false;
+ for (n = root->u.vnode.children; n; n = n->next) {
+ // region to check [entry .. end_entry)
+ if (n->is_vnode) {
+#ifdef DEBUG_HAS_VNODE
+ debug_printf("%s: looking at vnode: entry = %d, mapped = %"PRIx8"\n",
+ __FUNCTION__, n->entry, n->u.vnode.mapped);
+#endif
+ // n is page table, we need to check if each mapped 1k L2 overlaps
+ // with the region to check [entry .. end_entry)
+ for (int i = 0; i < L2_PER_PAGE; i++) {
+ // ith 1k L2 is mapped
+#ifdef DEBUG_HAS_VNODE
+ debug_printf("%s: n->u.vnode.mapped & (1 << %d) == %d\n",
+ __FUNCTION__, i, n->u.vnode.mapped & (1 << i));
+#endif
+ if (L2_IS_MAPPED(n, i)) {
+#ifdef DEBUG_HAS_VNODE
+ debug_printf("%s: check overlapping: %"PRIu32"--%"PRIu32
+ " <> %"PRIu32"--%"PRIu32"\n",
+ __FUNCTION__, entry, end_entry,
+ n->entry + i, n->entry + i + 1);
+#endif
+ if (is_overlapping(entry, end_entry, n->entry + i, n->entry + i + 1)) {
+ if (only_pages) {
+ uint16_t rec_start = i * PTABLE_SIZE;
+#ifdef DEBUG_HAS_VNODE
+ debug_printf("%s: checking recursively in %"PRIu16"--%"PRIu16"\n",
+ __FUNCTION__, rec_start, rec_start + PTABLE_SIZE);
+#endif
+ found_pages = found_pages
+ || has_vnode(n, rec_start, PTABLE_SIZE, true);
+ if (found_pages) {
+ return true;
+ }
+ } else {
+ return true;
+ }
+ }
+ }
+ }
+ } else {
+ // not vnode
+ uint32_t end = n->entry + n->u.frame.pte_count;
+#ifdef DEBUG_HAS_VNODE
+ debug_printf("%s: looking at region: [%"PRIu32"--%"PRIu32"]\n",
+ __FUNCTION__, n->entry, end);
+#endif
+
+ // do checks
+ if (is_overlapping(entry, end_entry, n->entry, end)) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
/**
* \brief Starting at a given root, return the vnode with entry equal to #entry
+ * \return vnode at index `entry` or NULL
*/
-static struct vnode *find_vnode(struct vnode *root, uint32_t entry)
+#ifdef LIBBARRELFISH_DEBUG_PMAP
+#define DEBUG_FIND_VNODE
+#endif
+static struct vnode *find_vnode(struct vnode *root, uint16_t entry)
{
assert(root != NULL);
assert(root->is_vnode);
struct vnode *n;
+#ifdef DEBUG_FIND_VNODE
+ debug_printf("%s: looking for %"PRIu16"\n", __FUNCTION__, entry);
+#endif
+
for(n = root->u.vnode.children; n != NULL; n = n->next) {
- if(n->entry == entry) {
+ if (n->is_vnode &&
+ is_overlapping(entry, entry + 1, n->entry, n->entry + L2_PER_PAGE)) {
+#ifdef DEBUG_FIND_VNODE
+ debug_printf("%s: found ptable at [%"PRIu16"--%"PRIu16"]\n",
+ __FUNCTION__, n->entry, n->entry + L2_PER_PAGE);
+#endif
+ return n;
+ }
+ else if (n->is_vnode) {
+ assert(!is_overlapping(entry, entry + 1, n->entry, n->entry + L2_PER_PAGE));
+ // ignore all other vnodes;
+ continue;
+ }
+
+ // not vnode
+ assert(!n->is_vnode);
+ uint16_t end = n->entry + n->u.frame.pte_count;
+#ifdef DEBUG_FIND_VNODE
+ debug_printf("%s: looking at section [%"PRIu16"--%"PRIu16"]\n", __FUNCTION__, n->entry, end);
+#endif
+ if (n->entry <= entry && entry < end) {
+#ifdef DEBUG_FIND_VNODE
+ debug_printf("%d \\in [%d, %d]\n", entry, n->entry, end);
+#endif
return n;
}
}
return NULL;
}
+/**
+ * \brief check whether region [entry, entry+npages) is contained in a child
+ * of `root`.
+ */
static bool inside_region(struct vnode *root, uint32_t entry, uint32_t npages)
{
assert(root != NULL);
return false;
}
-static bool has_vnode(struct vnode *root, uint32_t entry, size_t len)
-{
- assert(root != NULL);
- assert(root->is_vnode);
- struct vnode *n;
-
- uint32_t end_entry = entry + len;
-
- for (n = root->u.vnode.children; n; n = n->next) {
- if (n->is_vnode && n->entry == entry) {
- return true;
- }
- // n is frame
- uint32_t end = n->entry + n->u.frame.pte_count;
- if (n->entry < entry && end > end_entry) {
- return true;
- }
- if (n->entry >= entry && n->entry < end_entry) {
- return true;
- }
- }
-
- return false;
-}
-
+/**
+ * \brief remove vnode `item` from linked list of children of `root`
+ */
static void remove_vnode(struct vnode *root, struct vnode *item)
{
assert(root->is_vnode);
prev = walk;
walk = walk->next;
}
- assert(!"Should not get here");
+ USER_PANIC("Should not get here");
+}
+
+static void unmap_l2_table(struct vnode *root, struct vnode *n, uint16_t e)
+{
+ errval_t err;
+ uint32_t entry = ROUND_DOWN(n->entry, L2_PER_PAGE) + e;
+ if (L2_IS_MAPPED(n, e)) {
+ err = vnode_unmap(root->u.vnode.cap[0], n->u.vnode.cap[e],
+ entry, 1);
+ if (err_is_fail(err)) {
+ debug_printf("remove_empty_vnodes: vnode_unmap: %s\n",
+ err_getstring(err));
+ abort();
+ }
+
+ // delete capability, if not entry 0
+ if (e) {
+ err = cap_destroy(n->u.vnode.cap[e]);
+ if (err_is_fail(err)) {
+ debug_printf("remove_empty_vnodes: cap_destroy: %s\n",
+ err_getstring(err));
+ abort();
+ }
+ }
+ }
+}
+
+/**
+ * \brief (recursively) remove empty page tables in region [entry ..
+ * entry+len) in vnode `root`.
+ */
+#ifdef LIBBARRELFISH_DEBUG_PMAP
+#define DEBUG_REMOVE_EMPTY_VNODES
+#endif
+static void remove_empty_vnodes(struct slab_allocator *vnode_alloc, struct vnode *root,
+ uint32_t entry, size_t len)
+{
+ // precondition: root does not have pages in [entry, entry+len)
+ assert(!has_vnode(root, entry, len, true));
+
+ errval_t err;
+ uint32_t end_entry = entry + len;
+ for (struct vnode *n = root->u.vnode.children; n; n = n->next) {
+ // sanity check and skip leaf entries
+ if (!n->is_vnode) {
+ continue;
+ }
+ // here we know that all vnodes we're interested in are
+ // page tables
+ assert(n->is_vnode);
+
+ if (entry < n->entry && end_entry >= n->entry + L2_PER_PAGE) {
+ // whole entry in region: completely unmap&free L2 page
+ for (int i = 0; i < L2_PER_PAGE; i++) {
+ unmap_l2_table(root, n, i);
+ }
+
+ // delete last copy of pt cap
+ err = cap_destroy(n->u.vnode.cap[0]);
+ assert(err_is_ok(err));
+
+ // remove vnode from list
+ remove_vnode(root, n);
+ slab_free(vnode_alloc, n);
+ } else if (entry >= n->entry && entry < n->entry + L2_PER_PAGE) {
+ // tail end of vnode in region:
+ uint16_t e = entry - n->entry;
+#ifdef DEBUG_REMOVE_EMPTY_VNODES
+ debug_printf("overlap: %"PRIu16" entries\n", e);
+#endif
+ for (; e < L2_PER_PAGE; e++) {
+ unmap_l2_table(root, n, e);
+ }
+ } else if (end_entry > n->entry && end_entry < n->entry + L2_PER_PAGE) {
+ // start of vnode in region
+ uint16_t e = end_entry - n->entry;
+#ifdef DEBUG_REMOVE_EMPTY_VNODES
+ debug_printf("overlap: %"PRIu16" entries\n", e);
+#endif
+ for (int i = 0; i < e; i++) {
+ unmap_l2_table(root, n, i);
+ }
+ }
+ }
}
/**
newvnode->is_vnode = true;
// The VNode capability
- err = slot_alloc(&newvnode->u.vnode.cap);
+ err = slot_alloc(&newvnode->u.vnode.cap[0]);
if (err_is_fail(err)) {
return err_push(err, LIB_ERR_SLOT_ALLOC);
}
- err = vnode_create(newvnode->u.vnode.cap, type);
+ err = vnode_create(newvnode->u.vnode.cap[0], type);
if (err_is_fail(err)) {
return err_push(err, LIB_ERR_VNODE_CREATE);
}
-
- err = vnode_map(root->u.vnode.cap, newvnode->u.vnode.cap, entry,
- KPI_PAGING_FLAGS_READ | KPI_PAGING_FLAGS_WRITE, 0, 1);
-
- if (err_is_fail(err)) {
- return err_push(err, LIB_ERR_VNODE_MAP);
+ for (int i = 1; i < L2_PER_PAGE; i++) {
+ newvnode->u.vnode.cap[i] = NULL_CAP;
}
// The VNode meta data
- newvnode->entry = entry;
+ newvnode->entry = ROUND_DOWN(entry, L2_PER_PAGE);
+ assert(newvnode->entry % L2_PER_PAGE == 0);
newvnode->next = root->u.vnode.children;
+ newvnode->u.vnode.mapped = 0x0; // no entries mapped
root->u.vnode.children = newvnode;
newvnode->u.vnode.children = NULL;
/**
* \brief Returns the vnode for the pagetable mapping a given vspace address
*/
+#ifdef LIBBARRELFISH_DEBUG_PMAP
+#define DEBUG_GET_PTABLE
+#endif
static errval_t get_ptable(struct pmap_arm *pmap,
genvaddr_t vaddr,
struct vnode **ptable)
// NB Strictly there are 12 bits in the ARM L1, but allocations unit
// of L2 is 1 page of L2 entries (4 tables) so we use 10 bits for the L1
// idx here
- uintptr_t idx = ARM_USER_L1_OFFSET(vaddr);
+ uintptr_t idx = ARM_L1_OFFSET(vaddr);
+ uintptr_t page_idx = L2_PAGE_IDX(idx);
if ((*ptable = find_vnode(&pmap->root, idx)) == NULL)
{
// L1 table entries point to L2 tables so allocate an L2
struct vnode *tmp = NULL; // Tmp variable for passing to alloc_vnode
+#ifdef DEBUG_GET_PTABLE
+ uintptr_t fidx = ROUND_DOWN(idx, L2_PER_PAGE);
+ debug_printf("allocating 4 x L2, entries = %"PRIuPTR"--%z"PRIuPTR"\n",
+ fidx, fidx+4);
+#endif
errval_t err = alloc_vnode(pmap, &pmap->root, ObjType_VNode_ARM_l2,
idx, &tmp);
if (err_is_fail(err)) {
assert(tmp != NULL);
*ptable = tmp; // Set argument to received value
-
if (err_is_fail(err)) {
return err_push(err, LIB_ERR_PMAP_ALLOC_VNODE);
}
}
+ assert(ptable);
+ struct vnode *pt = *ptable;
+ if (!pt->is_vnode) {
+ debug_printf("found section @%d, trying to get ptable for %d\n",
+ pt->entry, idx);
+ }
+ assert(pt->is_vnode);
+#ifdef DEBUG_GET_PTABLE
+ debug_printf("have ptable: %p\n", pt);
+ debug_printf("mapped = %x\n", pt->u.vnode.mapped);
+ debug_printf("page_idx = %d\n", page_idx);
+ debug_printf("l2_is_mapped: %d\n", L2_IS_MAPPED(pt, page_idx));
+#endif
+ if (!L2_IS_MAPPED(pt, page_idx)) {
+#ifdef DEBUG_GET_PTABLE
+ debug_printf("need to map entry %d\n", page_idx);
+#endif
+ errval_t err;
+ uintptr_t offset = L2_PAGE_OFFSET(idx);
+#ifdef DEBUG_GET_PTABLE
+ debug_printf("mapping L1 entry %d at offset %"PRIuPTR"\n", idx, offset);
+#endif
+
+ // create copy of ptable cap for this index, if it doesn't exist
+ // already
+ if (capref_is_null(pt->u.vnode.cap[page_idx])) {
+#ifdef DEBUG_GET_PTABLE
+ debug_printf("allocating slot for chunk %d\n", page_idx);
+#endif
+ err = slot_alloc(&pt->u.vnode.cap[page_idx]);
+ if (err_is_fail(err)) {
+ return err_push(err, LIB_ERR_VNODE_MAP);
+ }
+
+#ifdef DEBUG_GET_PTABLE
+ debug_printf("creating copy for chunk %d\n", page_idx);
+#endif
+ err = cap_copy(pt->u.vnode.cap[page_idx], pt->u.vnode.cap[0]);
+ if (err_is_fail(err)) {
+ return err_push(err, LIB_ERR_VNODE_MAP);
+ }
+ }
+
+#ifdef DEBUG_GET_PTABLE
+ debug_printf("calling vnode_map() for chunk %d\n", page_idx);
+#endif
+ // map single 1k ptable
+ err = vnode_map(pmap->root.u.vnode.cap[0], pt->u.vnode.cap[page_idx], idx,
+ KPI_PAGING_FLAGS_READ | KPI_PAGING_FLAGS_WRITE, offset, 1);
+
+ if (err_is_fail(err)) {
+ return err_push(err, LIB_ERR_VNODE_MAP);
+ }
+
+ // set 1k ptable as mapped
+ pt->u.vnode.mapped |= 1 << page_idx;
+ }
return SYS_ERR_OK;
}
{
// NB Strictly there are 12 bits in the ARM L1, but allocations unit
// of L2 is 1 page of L2 entries (4 tables) so
- uintptr_t idx = ARM_USER_L1_OFFSET(vaddr);
+ uintptr_t idx = ARM_L1_OFFSET(vaddr);
return find_vnode(&pmap->root, idx);
}
// Get the page table
struct vnode *ptable;
uintptr_t entry;
- if (flags&FLAGS_SECTION) {
+ bool is_large = false;
+
+ struct frame_identity fi;
+ err = invoke_frame_identify(frame, &fi);
+ if (err_is_fail(err)) {
+ return err_push(err, LIB_ERR_PMAP_FRAME_IDENTIFY);
+ }
+
+ if (flags & VREGION_FLAGS_LARGE &&
+ (vaddr & LARGE_PAGE_MASK) == 0 &&
+ fi.bits >= LARGE_PAGE_BITS &&
+ (fi.base & LARGE_PAGE_MASK) == 0) {
//section mapping (1MB)
//mapped in the L1 table at root
+ //
ptable = &pmap->root;
- entry = ARM_USER_L1_OFFSET(vaddr);
- printf("do_single_map: large path\n");
+ entry = ARM_L1_OFFSET(vaddr);
+ is_large = true;
+#ifdef LIBBARRELFISH_DEBUG_PMAP
+ printf("do_single_map: large path: entry=%zu\n", entry);
+#endif
} else {
+#ifdef LIBBARRELFISH_DEBUG_PMAP
+ debug_printf("%s: 4k path: mapping %"PRIxGENVADDR"\n", __FUNCTION__, vaddr);
+ debug_printf("4k path: L1 entry: %zu\n", ARM_USER_L1_OFFSET(vaddr));
+#endif
//4k mapping
+ // XXX: reassess the following note -SG
+ // NOTE: strictly speaking a l2 entry only has 8 bits, while a l1 entry
+ // has 12 bits, but due to the way Barrelfish allocates l1 and l2 tables,
+ // we use 10 bits for the entry here and in the map syscall
err = get_ptable(pmap, vaddr, &ptable);
+ if (err_is_fail(err)) {
+ DEBUG_ERR(err, "get_ptable() in do_single_map");
+ return err_push(err, LIB_ERR_PMAP_GET_PTABLE);
+ }
entry = ARM_USER_L2_OFFSET(vaddr);
+#ifdef LIBBARRELFISH_DEBUG_PMAP
+ debug_printf("%s: 4k path: L2 entry=%zu\n", __FUNCTION__, entry);
+ debug_printf("%s: ptable->is_vnode = %d\n",
+ __FUNCTION__, ptable->is_vnode);
+#endif
}
- if (err_is_fail(err)) {
- return err_push(err, LIB_ERR_PMAP_GET_PTABLE);
- }
- uintptr_t pmap_flags = vregion_flags_to_kpi_paging_flags(flags&~FLAGS_SUPERSECTION);
- // XXX: reassess the following note -SG
- // NOTE: strictly speaking a l2 entry only has 8 bits, but due to the way
- // Barrelfish allocates l1 and l2 tables, we use 10 bits for the tracking
- // idx here and in the map syscall
- uintptr_t idx = ARM_USER_L2_OFFSET(vaddr);
+
+ // convert flags
+ flags &= ~(VREGION_FLAGS_LARGE | VREGION_FLAGS_HUGE);
+ uintptr_t pmap_flags = vregion_flags_to_kpi_paging_flags(flags);
+
+ uintptr_t user_pte_count = pte_count;
+ if (is_large) {
+ user_pte_count = DIVIDE_ROUND_UP(pte_count, L2_PER_PAGE);
+ }
+
+ // check if there is an overlapping mapping
+ if (has_vnode(ptable, entry, pte_count, false)) {
+#ifdef LIBBARRELFISH_DEBUG_PMAP
+ debug_printf("has_vnode, only_pages=false returned true\n");
+#endif
+ if (has_vnode(ptable, entry, pte_count, true)) {
+ printf("page already exists in 0x%"
+ PRIxGENVADDR"--0x%"PRIxGENVADDR"\n", vaddr, vend);
+ return LIB_ERR_PMAP_EXISTING_MAPPING;
+ } else {
+#ifdef LIBBARRELFISH_DEBUG_PMAP
+ debug_printf("has_vnode, only_pages=true returned false, cleaning up empty ptables\n");
+#endif
+ // clean out empty page tables. We do this here because we benefit
+ // from having the page tables in place when doing lots of small
+ // mappings
+ // XXX: TODO: fix this + mapping of L2 to work on single 1k
+ // chunks
+ remove_empty_vnodes(&pmap->slab, ptable, entry, pte_count);
+ }
+ }
+
// Create user level datastructure for the mapping
- bool has_page = has_vnode(ptable, idx, pte_count);
- assert(!has_page);
struct vnode *page = slab_alloc(&pmap->slab);
assert(page);
page->is_vnode = false;
- page->entry = idx;
+ page->entry = entry;
page->next = ptable->u.vnode.children;
ptable->u.vnode.children = page;
page->u.frame.cap = frame;
page->u.frame.flags = flags;
- page->u.frame.pte_count = pte_count;
+ page->u.frame.pte_count = user_pte_count;
+ page->u.frame.kernel_pte_count = pte_count;
// Map entry into the page table
- err = vnode_map(ptable->u.vnode.cap, frame, idx,
+ err = vnode_map(ptable->u.vnode.cap[0], frame, entry,
pmap_flags, offset, pte_count);
if (err_is_fail(err)) {
return err_push(err, LIB_ERR_VNODE_MAP);
errval_t err;
size_t page_size;
size_t offset_level;
+
+ // get base address and size of frame
+ struct frame_identity fi;
+ err = invoke_frame_identify(frame, &fi);
+ if (err_is_fail(err)) {
+ return err_push(err, LIB_ERR_PMAP_DO_MAP);
+ }
+
// determine mapping specific parts
- if (flags&FLAGS_SECTION) {
+ if (flags & VREGION_FLAGS_LARGE &&
+ (vaddr & LARGE_PAGE_MASK) == 0 &&
+ fi.bits >= LARGE_PAGE_BITS &&
+ (fi.base & LARGE_PAGE_MASK) == 0) {
//section mapping (1MB)
page_size = LARGE_PAGE_SIZE;
offset_level = ARM_L1_OFFSET(vaddr);
+#ifdef LIBBARRELFISH_DEBUG_PMAP
printf("do_map: large path\n");
- printf("page_size: %i, size: %i\n", page_size, size);
+ printf("page_size: %zx, size: %zx\n", page_size, size);
+#endif
} else {
//normal 4k mapping
page_size = BASE_PAGE_SIZE;
size = ROUND_UP(size, page_size);
size_t pte_count = DIVIDE_ROUND_UP(size, page_size);
+ if (flags & VREGION_FLAGS_LARGE) {
+#ifdef LIBBARRELFISH_DEBUG_PMAP
+ printf("#pages: 0x%zu\n", pte_count);
+#endif
+ }
genvaddr_t vend = vaddr + size;
+ if ((1UL << fi.bits) < size) {
+ return LIB_ERR_PMAP_FRAME_SIZE;
+ }
+
//should be trivially true for section mappings
- if ((ARM_L1_OFFSET(vaddr) == ARM_L1_OFFSET(vend-1)) ||
- flags&FLAGS_SECTION) {
+ if ((ARM_L1_OFFSET(vaddr) == ARM_L1_OFFSET(vend)) ||
+ flags & VREGION_FLAGS_LARGE) {
// fast path
err = do_single_map(pmap, vaddr, vend, frame, offset, pte_count, flags);
if (err_is_fail(err)) {
}
static size_t max_slabs_required_large(size_t bytes)
{
- // similar to the above, but larger page size and mapped only in a higher lvl paging structure
- size_t pages = DIVIDE_ROUND_UP(bytes, LARGE_PAGE_SIZE);
- size_t l1entries = DIVIDE_ROUND_UP(pages, 1024);
- return pages + l1entries;
+ // always need only one slab, as we can represent any size section mapping
+ // in a single struct vnode.
+ return 1;
}
/**
{
struct pmap_arm *pmap_arm = (struct pmap_arm *)pmap;
+ errval_t err;
size_t base;
size_t page_size;
size_t slabs_required;
-
+
+ struct frame_identity fi;
+ err = invoke_frame_identify(frame, &fi);
+ if (err_is_fail(err)) {
+ return err_push(err, LIB_ERR_PMAP_FRAME_IDENTIFY);
+ }
+
// adjust the mapping to be on page boundaries
- if (flags&FLAGS_SECTION) {
+ if (flags & VREGION_FLAGS_LARGE &&
+ (vaddr & LARGE_PAGE_MASK) == 0 &&
+ fi.bits >= LARGE_PAGE_BITS &&
+ (fi.base & LARGE_PAGE_MASK) == 0) {
//section mapping (1MB)
base = LARGE_PAGE_OFFSET(offset);
page_size = LARGE_PAGE_SIZE;
slabs_required = max_slabs_required_large(size);
- printf("map: large path, page_size: %i, base: %i, slabs: %i, size: %i\n", page_size, base, slabs_required, size);
+#ifdef LIBBARRELFISH_DEBUG_PMAP
+ size_t frame_sz = 1ULL<<fi.bits;
+ printf("map: large path, page_size: %i, base: %i, slabs: %i, size: %i,"
+ "frame size: %zu\n", page_size, base, slabs_required, size, frame_sz);
+#endif
} else {
//4k mapping
base = BASE_PAGE_OFFSET(offset);
if (slabs_required > slabs_free) {
if (get_current_pmap() == pmap) {
- errval_t err = refill_slabs(pmap_arm, slabs_required);
+ err = refill_slabs(pmap_arm, slabs_required);
if (err_is_fail(err)) {
return err_push(err, LIB_ERR_SLAB_REFILL);
}
{
errval_t err;
struct vnode *pt = find_ptable(pmap, vaddr);
- if (pt) {
+ // pt->is_vnode == non-large mapping
+ if (pt && pt->is_vnode) {
// analog to do_single_map we use 10 bits for tracking pages in user space -SG
struct vnode *page = find_vnode(pt, ARM_USER_L2_OFFSET(vaddr));
if (page && page->u.frame.pte_count == pte_count) {
- err = vnode_unmap(pt->u.vnode.cap, page->u.frame.cap,
+ err = vnode_unmap(pt->u.vnode.cap[0], page->u.frame.cap,
page->entry, page->u.frame.pte_count);
if (err_is_fail(err)) {
DEBUG_ERR(err, "vnode_unmap");
else {
return LIB_ERR_PMAP_FIND_VNODE;
}
+ } else if (pt) {
+#ifdef LIBBARRELFISH_DEBUG_PMAP
+ debug_printf("section unmap: entry = %zu, pte_count = %zu\n",
+ pt->entry, pt->u.frame.kernel_pte_count);
+#endif
+ err = vnode_unmap(pmap->root.u.vnode.cap[0], pt->u.frame.cap,
+ pt->entry, pt->u.frame.kernel_pte_count);
+ if (err_is_fail(err)) {
+ DEBUG_ERR(err, "vnode_unmap");
+ return err_push(err, LIB_ERR_VNODE_UNMAP);
+ }
+
+ remove_vnode(&pmap->root, pt);
+ slab_free(&pmap->slab, pt);
+ } else {
+ return LIB_ERR_PMAP_FIND_VNODE;
}
return SYS_ERR_OK;
{
assert(pmap->vspace->head);
- assert(alignment <= BASE_PAGE_SIZE); // NYI
+ if (alignment == 0) {
+ alignment = BASE_PAGE_SIZE;
+ } else {
+ alignment = ROUND_UP(alignment, BASE_PAGE_SIZE);
+ }
+ size_t size = ROUND_UP(memobj->size, alignment);
struct vregion *walk = pmap->vspace->head;
while (walk->next) { // Try to insert between existing mappings
genvaddr_t walk_base = vregion_get_base_addr(walk);
- genvaddr_t walk_size = vregion_get_size(walk);
+ genvaddr_t walk_size = ROUND_UP(vregion_get_size(walk), BASE_PAGE_SIZE);
+ genvaddr_t walk_end = ROUND_UP(walk_base + walk_size, alignment);
genvaddr_t next_base = vregion_get_base_addr(walk->next);
- if (next_base > walk_base + walk_size + memobj->size &&
+ if (next_base > walk_end + size &&
walk_base + walk_size > VSPACE_BEGIN) { // Ensure mappings are larger than VSPACE_BEGIN
- *vaddr = walk_base + walk_size;
+ *vaddr = walk_end;
return SYS_ERR_OK;
}
walk = walk->next;
}
- *vaddr = vregion_get_base_addr(walk) + vregion_get_size(walk);
+ *vaddr = ROUND_UP((vregion_get_base_addr(walk)
+ + ROUND_UP(vregion_get_size(walk), alignment)),
+ alignment);
return SYS_ERR_OK;
}
sizeof(pmap_arm->slab_buffer));
pmap_arm->root.is_vnode = true;
- pmap_arm->root.u.vnode.cap = vnode;
+ pmap_arm->root.u.vnode.cap[0] = vnode;
pmap_arm->root.next = NULL;
pmap_arm->root.u.vnode.children = NULL;