Merge rueegges paging into jschaer code
This commit is contained in:
parent
070111da22
commit
292ca0b63b
@ -126,4 +126,7 @@ static inline lvaddr_t paging_genvaddr_to_lvaddr(genvaddr_t genvaddr) {
|
||||
return (lvaddr_t) genvaddr;
|
||||
}
|
||||
|
||||
// NOTE rueegges: added debug helper
|
||||
void pt_print_state(struct paging_state *st);
|
||||
|
||||
#endif // LIBBARRELFISH_PAGING_H
|
||||
|
||||
@ -38,15 +38,39 @@
|
||||
typedef int paging_flags_t;
|
||||
|
||||
|
||||
// NOTE rueegges: page table metadata used to create child mappings and unmap
|
||||
struct pt_t {
|
||||
// the level of the page table. since we abuse this struct also to track l3 entries we set it to 4 there.
|
||||
size_t level;
|
||||
// capref that holds this page table
|
||||
struct capref cap_pt;
|
||||
// capref that maps this page table in the higher level page table
|
||||
struct capref cap_mapping;
|
||||
|
||||
// NOTE rueegges: points to an array with pointers for every potential next level page table
|
||||
// it is NULL for mapping a frame instead of a page table
|
||||
// entries are NULL if the corresponding mapping does not yet exist
|
||||
struct pt_t **children;
|
||||
};
|
||||
|
||||
// struct to store the paging status of a process
|
||||
struct paging_state {
|
||||
struct slot_allocator *slot_alloc;
|
||||
struct capref l0_vnode;
|
||||
struct capref l2_vnode;
|
||||
struct capref l3_vnodes[PTABLE_ENTRIES];
|
||||
|
||||
// NOTE rueegges: added to keep track of pt metadata
|
||||
struct pt_t *l0_pt;
|
||||
|
||||
// NOTE rueegges: added to allocate shadow page table metadata
|
||||
struct slab_allocator pt_slabs;
|
||||
struct slab_allocator pt_children_slabs;
|
||||
|
||||
// NOTE rueegges: remember if we are refilling so we can skip the checks
|
||||
uint8_t refilling;
|
||||
|
||||
// TODO rueegges: use to track an allocated slot in failure conditions
|
||||
struct capref free_l3_vnode;
|
||||
|
||||
// TODO rueegges: implement more precisely?
|
||||
lvaddr_t next_vaddr;
|
||||
};
|
||||
|
||||
|
||||
15
include/test_mm.h
Normal file
15
include/test_mm.h
Normal file
@ -0,0 +1,15 @@
|
||||
#ifndef __TEST_MM_H
|
||||
#define __TEST_MM_H
|
||||
|
||||
#include <aos/aos.h>
|
||||
#include <mm/mm.h>
|
||||
|
||||
uint8_t test_mm_run(errval_t func(struct mm *), char *name, struct mm *mm);
|
||||
errval_t test_mm_small(struct mm *mm);
|
||||
errval_t test_mm_track_slots(struct mm *mm);
|
||||
errval_t test_mm_fragments(struct mm *mm);
|
||||
errval_t test_mm_rand(struct mm *mm);
|
||||
errval_t test_mm_many(struct mm *mm);
|
||||
errval_t test_mm_oom(struct mm *mm);
|
||||
|
||||
#endif /* __TEST_MM_H */
|
||||
11
include/test_paging.h
Normal file
11
include/test_paging.h
Normal file
@ -0,0 +1,11 @@
|
||||
#ifndef __PAGING_TESTS_H
|
||||
#define __PAGING_TESTS_H
|
||||
|
||||
#include <aos/aos.h>
|
||||
|
||||
uint8_t test_paging_run(errval_t func(void), char *name);
|
||||
errval_t test_paging_full_l3(void);
|
||||
errval_t test_paging_full_l2(void);
|
||||
errval_t test_paging_big_regions(void);
|
||||
|
||||
#endif /* __PAGING_TESTS_H */
|
||||
435
lib/aos/paging.c
435
lib/aos/paging.c
@ -23,7 +23,15 @@
|
||||
|
||||
static struct paging_state current;
|
||||
|
||||
#define PT_PT_SLAB_MIN_SPACE 16
|
||||
#define PT_CHILDREN_SLAB_MIN_SPACE 12
|
||||
|
||||
#define PT_PT_SLAB_INITIAL_SPACE SLAB_STATIC_SIZE(PT_PT_SLAB_MIN_SPACE, sizeof(struct pt_t))
|
||||
#define PT_CHILDREN_SLAB_INITIAL_SPACE SLAB_STATIC_SIZE(PT_CHILDREN_SLAB_MIN_SPACE, BASE_PAGE_SIZE)
|
||||
|
||||
// NOTE rueegges: temporary (?) solution for initial slab space
|
||||
char pt_pt_slab_buf[PT_PT_SLAB_INITIAL_SPACE];
|
||||
char pt_children_slab_buf[PT_CHILDREN_SLAB_INITIAL_SPACE];
|
||||
|
||||
/**
|
||||
* \brief Helper function that allocates a slot and
|
||||
@ -61,6 +69,219 @@ __attribute__((unused)) static errval_t pt_alloc_l3(struct paging_state * st, st
|
||||
return pt_alloc(st, ObjType_VNode_AARCH64_l3, ret);
|
||||
}
|
||||
|
||||
void pt_print_state(struct paging_state *st) {
|
||||
// iterates over all the page table entries and prints them
|
||||
debug_printf("L0\n");
|
||||
for(size_t i0 = 0; i0 < PTABLE_ENTRIES; ++i0) {
|
||||
struct pt_t *l1_pt = st->l0_pt->children[i0];
|
||||
if (l1_pt == NULL) continue;
|
||||
|
||||
debug_printf(" %lu -> L1\n", i0);
|
||||
for(size_t i1 = 0; i1 < PTABLE_ENTRIES; ++i1) {
|
||||
struct pt_t *l2_pt = l1_pt->children[i1];
|
||||
if (l2_pt == NULL) continue;
|
||||
debug_printf(" %lu -> L2\n", i1);
|
||||
|
||||
for(size_t i2 = 0; i2 < PTABLE_ENTRIES; ++i2) {
|
||||
struct pt_t *l3_pt = l2_pt->children[i2];
|
||||
if (l3_pt == NULL) continue;
|
||||
debug_printf(" %lu -> L3\n", i2);
|
||||
|
||||
for(size_t i3 = 0; i3 < PTABLE_ENTRIES; ++i3) {
|
||||
struct pt_t *l4_pt = l3_pt->children[i3];
|
||||
if (l4_pt == NULL) continue;
|
||||
debug_printf(" %lu -> Map\n", i3);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE rueegges: each paging fixed call can use up to 4 pt slabs, up to 3 children slabs and up to 7 slots
|
||||
// slab refilling reserve cycles causing paging calls:
|
||||
// - this slab
|
||||
// - child slab
|
||||
// - mm slab
|
||||
static errval_t pt_ensure_slots_and_slabs(struct paging_state *st) {
|
||||
errval_t err;
|
||||
|
||||
// ensure there is enough space to refill the page tables at any time
|
||||
if(slab_freecount(&st->pt_slabs) <= PT_PT_SLAB_MIN_SPACE && !st->refilling) {
|
||||
// ASSESSMENT M1: show refilling
|
||||
// debug_printf("DEBUG rueegges: pt_ensure_slots_and_slabs - refilling pt_slab\n");
|
||||
|
||||
st->refilling = 1;
|
||||
|
||||
err = slab_default_refill(&st->pt_slabs);
|
||||
|
||||
st->refilling = 0;
|
||||
// debug_printf("DEBUG rueegges: pt_ensure_slots_and_slabs - refilling pt_slab DONE\n");
|
||||
|
||||
if(err_is_fail(err)) {
|
||||
return err_push(err, LIB_ERR_SLAB_REFILL);
|
||||
}
|
||||
}
|
||||
|
||||
if(slab_freecount(&st->pt_children_slabs) <= PT_CHILDREN_SLAB_MIN_SPACE && !st->refilling) {
|
||||
// ASSESSMENT M1: show refilling
|
||||
// debug_printf("DEBUG rueegges: pt_ensure_slots_and_slabs - refilling pt_children_slabs\n");
|
||||
|
||||
st->refilling = 1;
|
||||
|
||||
struct capref frame_cap;
|
||||
err = st->slot_alloc->alloc(st->slot_alloc, &frame_cap);
|
||||
if (err_is_fail(err)) {
|
||||
return err_push(err, LIB_ERR_SLOT_ALLOC);
|
||||
}
|
||||
// TODO rueegges: use fuctions for refill size
|
||||
err = slab_refill_no_pagefault(&st->pt_children_slabs, frame_cap, 10 * 4096);
|
||||
|
||||
st->refilling = 0;
|
||||
// debug_printf("DEBUG rueegges: pt_ensure_slots_and_slabs - refilling pt_children_slabs DONE\n");
|
||||
|
||||
if(err_is_fail(err)) {
|
||||
return err_push(err, LIB_ERR_SLAB_REFILL);
|
||||
}
|
||||
}
|
||||
|
||||
return SYS_ERR_OK;
|
||||
}
|
||||
|
||||
// NOTE rueegges: ensures that a page table exists at the specified index in the parent page table and return it
|
||||
static errval_t pt_ensure(struct paging_state *st, struct pt_t *pt_parent, size_t pt_index, struct pt_t **pt_ret){
|
||||
// debug_printf("DEBUG rueegges: pt_ensure l%u\n", pt_parent->level+1);
|
||||
|
||||
errval_t err;
|
||||
|
||||
// If this fails we got an l3 pt as the parent
|
||||
assert(pt_parent->children != NULL);
|
||||
assert(pt_parent->level < 3);
|
||||
|
||||
// MUST DO before check if table already exists since it might create it
|
||||
// make sure slot and slab refilling is performed in time
|
||||
err = pt_ensure_slots_and_slabs(st);
|
||||
if(err_is_fail(err)) {
|
||||
DEBUG_ERR(err, "Could not restock slots and slabs");
|
||||
return err;
|
||||
}
|
||||
|
||||
// check if the page table already exists
|
||||
struct pt_t *res = (pt_parent->children)[pt_index];
|
||||
if (res != NULL) {
|
||||
*pt_ret = res;
|
||||
return SYS_ERR_OK;
|
||||
}
|
||||
|
||||
// create the page table
|
||||
struct capref pt_cap;
|
||||
switch(pt_parent->level){
|
||||
case 0:
|
||||
err = pt_alloc_l1(st, &pt_cap);
|
||||
break;
|
||||
case 1:
|
||||
err = pt_alloc_l2(st, &pt_cap);
|
||||
break;
|
||||
case 2:
|
||||
err = pt_alloc_l3(st, &pt_cap);
|
||||
break;
|
||||
default:
|
||||
err = ERR_INVALID_ARGS;
|
||||
break;
|
||||
}
|
||||
if (err_is_fail(err)) {
|
||||
DEBUG_ERR(err, "Failed pt_alloc l%u", pt_parent->level + 1);
|
||||
return err;
|
||||
}
|
||||
|
||||
// TODO rueegges: store the allocated pt
|
||||
// check if the page table already exists
|
||||
res = (pt_parent->children)[pt_index];
|
||||
if (res != NULL) {
|
||||
*pt_ret = res;
|
||||
return SYS_ERR_OK;
|
||||
}
|
||||
|
||||
// debug_printf("DEBUG rueegges: allocated pt l%u\n", pt_parent->level+1);
|
||||
|
||||
// create new mapping
|
||||
struct capref pt_mapping;
|
||||
err = st->slot_alloc->alloc(st->slot_alloc, &pt_mapping);
|
||||
if (err_is_fail(err))
|
||||
{
|
||||
// NOTE rueegges: cannot free the pt allocated above since we do not have the ram cap
|
||||
|
||||
DEBUG_ERR(err, "Failed slot alloc for new pt mapping");
|
||||
return err;
|
||||
}
|
||||
err = vnode_map(pt_parent->cap_pt, pt_cap, pt_index, 0, 0, 1, pt_mapping);
|
||||
if (err_is_fail(err))
|
||||
{
|
||||
// free the already allocated capref
|
||||
errval_t err_err = st->slot_alloc->free(st->slot_alloc, pt_mapping);
|
||||
if (err_is_fail(err_err)) {
|
||||
DEBUG_ERR(err_err, "Failed to free capability slot during error handling");
|
||||
}
|
||||
|
||||
// NOTE rueegges: cannot free the pt allocated above since we do not have the ram cap
|
||||
|
||||
DEBUG_ERR(err, "Failed vnode_map for pt l%u", pt_parent->level + 1);
|
||||
return err_push(err, LIB_ERR_VNODE_MAP);
|
||||
}
|
||||
|
||||
// debug_printf("DEBUG rueegges: inserted mapping for pt l%u\n", pt_parent->level+1);
|
||||
|
||||
// allocate shadow page table space
|
||||
struct pt_t *pt_meta = (struct pt_t *) slab_alloc(&st->pt_slabs);
|
||||
if(pt_meta == NULL) {
|
||||
err = vnode_unmap(pt_cap, pt_mapping);
|
||||
if (err_is_fail(err)) {
|
||||
DEBUG_ERR(err, "Failed to unmap pt during error handling");
|
||||
}
|
||||
|
||||
err = st->slot_alloc->free(st->slot_alloc, pt_mapping);
|
||||
if (err_is_fail(err)) {
|
||||
DEBUG_ERR(err, "Failed to free capability slot during error handling");
|
||||
}
|
||||
|
||||
// NOTE rueegges: cannot free the pt allocated above since we do not have the ram cap
|
||||
|
||||
return LIB_ERR_SLAB_ALLOC_FAIL;
|
||||
}
|
||||
|
||||
struct pt_t **pt_children = (struct pt_t **) slab_alloc(&st->pt_children_slabs);
|
||||
if(pt_children == NULL) {
|
||||
err = vnode_unmap(pt_cap, pt_mapping);
|
||||
if (err_is_fail(err)) {
|
||||
DEBUG_ERR(err, "Failed to unmap pt during error handling");
|
||||
}
|
||||
|
||||
err = st->slot_alloc->free(st->slot_alloc, pt_mapping);
|
||||
if (err_is_fail(err)) {
|
||||
DEBUG_ERR(err, "Failed to free capability slot during error handling");
|
||||
}
|
||||
|
||||
slab_free(&st->pt_slabs, pt_meta);
|
||||
|
||||
// NOTE rueegges: cannot free the pt allocated above since we do not have the ram cap
|
||||
|
||||
return LIB_ERR_SLAB_ALLOC_FAIL;
|
||||
}
|
||||
// make sure all child pointers are initialized to NULL
|
||||
memset(pt_children, 0, st->pt_children_slabs.blocksize);
|
||||
|
||||
// debug_printf("DEBUG rueegges: completed shadow allocation for pt l%u\n", pt_parent->level+1);
|
||||
|
||||
pt_meta->level = pt_parent->level + 1;
|
||||
pt_meta->cap_pt = pt_cap;
|
||||
pt_meta->cap_mapping = pt_mapping;
|
||||
pt_meta->children = pt_children;
|
||||
|
||||
pt_parent->children[pt_index] = pt_meta;
|
||||
|
||||
*pt_ret = pt_meta;
|
||||
|
||||
return SYS_ERR_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO(M2): Implement this function.
|
||||
@ -83,20 +304,39 @@ errval_t paging_init_state(struct paging_state *st, lvaddr_t start_vaddr,
|
||||
// TODO (M4): Implement page fault handler that installs frames when a page fault
|
||||
// occurs and keeps track of the virtual address space.
|
||||
|
||||
assert(ca != NULL);
|
||||
|
||||
st->slot_alloc = ca;
|
||||
st->refilling = 0;
|
||||
|
||||
// Note: The slot allocator is not initialized yet, so we can't use it now.
|
||||
// So, defer the creation of vnodes until the first map operation.
|
||||
st->l0_vnode = pdir;
|
||||
st->l2_vnode = NULL_CAP;
|
||||
// initialize slab allocators
|
||||
// TODO rueegges: is this how we should initialize the slab allocators?
|
||||
slab_init(&st->pt_slabs, sizeof(struct pt_t), NULL);
|
||||
slab_init(&st->pt_children_slabs, BASE_PAGE_SIZE, NULL);
|
||||
slab_grow(&st->pt_slabs, pt_pt_slab_buf, PT_PT_SLAB_INITIAL_SPACE);
|
||||
slab_grow(&st->pt_children_slabs, pt_children_slab_buf, PT_CHILDREN_SLAB_INITIAL_SPACE);
|
||||
|
||||
for (size_t i = 0; i < PTABLE_ENTRIES; i++) {
|
||||
st->l3_vnodes[i] = NULL_CAP;
|
||||
// initialize shadow pages
|
||||
struct pt_t *l0_pt = slab_alloc(&st->pt_slabs);
|
||||
if(l0_pt == NULL) {
|
||||
debug_printf("Failed to alloc l0 meta\n");
|
||||
return LIB_ERR_SLAB_ALLOC_FAIL;
|
||||
}
|
||||
struct pt_t **l0_children = (struct pt_t **) slab_alloc(&st->pt_children_slabs);
|
||||
if(l0_children == NULL) {
|
||||
debug_printf("Failed to alloc l0 children\n");
|
||||
slab_free(&st->pt_slabs, l0_pt);
|
||||
return LIB_ERR_SLAB_ALLOC_FAIL;
|
||||
}
|
||||
// make sure all child pointers are initialized to NULL
|
||||
memset(l0_children, 0, st->pt_children_slabs.blocksize);
|
||||
|
||||
st->free_l3_vnode = NULL_CAP;
|
||||
l0_pt->level = 0;
|
||||
l0_pt->cap_pt = pdir;
|
||||
l0_pt->children = l0_children;
|
||||
|
||||
st->next_vaddr = VADDR_OFFSET;
|
||||
st->l0_pt = l0_pt;
|
||||
st->next_vaddr = start_vaddr;
|
||||
|
||||
return SYS_ERR_OK;
|
||||
}
|
||||
@ -141,7 +381,7 @@ errval_t paging_init(void)
|
||||
// TIP: it might be a good idea to call paging_init_state() from here to
|
||||
// avoid code duplication.
|
||||
|
||||
err = paging_init_state(¤t, 0, cap_vroot, get_default_slot_allocator());
|
||||
err = paging_init_state(¤t, VADDR_OFFSET, cap_vroot, get_default_slot_allocator());
|
||||
if (err_is_fail(err)) return err;
|
||||
|
||||
set_current_paging_state(¤t);
|
||||
@ -215,77 +455,6 @@ errval_t paging_map_frame_attr(struct paging_state *st, void **buf, size_t bytes
|
||||
return LIB_ERR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
static errval_t init_vnodes(struct paging_state *st)
|
||||
{
|
||||
errval_t err;
|
||||
struct capref l1_vnode;
|
||||
err = pt_alloc_l1(st, &l1_vnode);
|
||||
if (err_is_fail(err)) return err;
|
||||
|
||||
struct capref l1_vnode_mapping;
|
||||
err = st->slot_alloc->alloc(st->slot_alloc, &l1_vnode_mapping);
|
||||
if (err_is_fail(err)) return err_push(err, LIB_ERR_SLOT_ALLOC);
|
||||
|
||||
struct capref l2_vnode;
|
||||
err = pt_alloc_l2(st, &l2_vnode);
|
||||
if (err_is_fail(err)) return err;
|
||||
|
||||
struct capref l2_vnode_mapping;
|
||||
err = st->slot_alloc->alloc(st->slot_alloc, &l2_vnode_mapping);
|
||||
if (err_is_fail(err)) return err_push(err, LIB_ERR_SLOT_ALLOC);
|
||||
|
||||
if (capref_is_null(st->l2_vnode)) {
|
||||
st->l2_vnode = l2_vnode;
|
||||
err = vnode_map(st->l0_vnode, l1_vnode,
|
||||
1, VREGION_FLAGS_READ_WRITE, 0, 1, l1_vnode_mapping);
|
||||
if (err_is_fail(err)) return err_push(err, LIB_ERR_VNODE_MAP);
|
||||
|
||||
err = vnode_map(l1_vnode, st->l2_vnode,
|
||||
0, VREGION_FLAGS_READ_WRITE, 0, 1, l2_vnode_mapping);
|
||||
if (err_is_fail(err)) return err_push(err, LIB_ERR_VNODE_MAP);
|
||||
} else {
|
||||
// paging_map_fixed_attr was called recursively during an allocation above.
|
||||
// This can happen at most once, so it's fine to leak the allocations.
|
||||
}
|
||||
|
||||
return SYS_ERR_OK;
|
||||
}
|
||||
|
||||
|
||||
static errval_t allocate_l3_vnode(struct paging_state *st, size_t l2_index)
|
||||
{
|
||||
errval_t err;
|
||||
struct capref l3_vnode_mapping;
|
||||
err = st->slot_alloc->alloc(st->slot_alloc, &l3_vnode_mapping);
|
||||
if (err_is_fail(err)) return err_push(err, LIB_ERR_SLOT_ALLOC);
|
||||
|
||||
struct capref l3_vnode;
|
||||
if (!capref_is_null(st->free_l3_vnode)) {
|
||||
l3_vnode = st->free_l3_vnode;
|
||||
st->free_l3_vnode = NULL_CAP;
|
||||
} else {
|
||||
err = pt_alloc_l3(st, &l3_vnode);
|
||||
if (err_is_fail(err)) {
|
||||
st->slot_alloc->free(st->slot_alloc, l3_vnode_mapping);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
if (capref_is_null(st->l3_vnodes[l2_index])) {
|
||||
st->l3_vnodes[l2_index] = l3_vnode;
|
||||
err = vnode_map(st->l2_vnode, st->l3_vnodes[l2_index],
|
||||
l2_index, VREGION_FLAGS_READ_WRITE, 0, 1,
|
||||
l3_vnode_mapping);
|
||||
if (err_is_fail(err)) return err_push(err, LIB_ERR_VNODE_MAP);
|
||||
} else {
|
||||
// paging_map_fixed_attr was called recursively during an allocation above.
|
||||
err = st->slot_alloc->free(st->slot_alloc, l3_vnode_mapping);
|
||||
assert(!err_is_fail(err));
|
||||
st->free_l3_vnode = l3_vnode;
|
||||
}
|
||||
|
||||
return SYS_ERR_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief mapps the provided frame at the supplied address in the paging state
|
||||
@ -304,7 +473,7 @@ errval_t paging_map_fixed_attr(struct paging_state *st, lvaddr_t vaddr,
|
||||
{
|
||||
errval_t err;
|
||||
/*
|
||||
* M1:
|
||||
* TODO(M1):
|
||||
* - Map a frame assuming all mappings will fit into one leaf page table (L3)
|
||||
* TODO(M2):
|
||||
* - General case: you will need to handle mappings spanning multiple leaf page tables.
|
||||
@ -314,43 +483,99 @@ errval_t paging_map_fixed_attr(struct paging_state *st, lvaddr_t vaddr,
|
||||
* - think about what mapping configurations are actually possible
|
||||
*/
|
||||
|
||||
// preconditions
|
||||
assert(bytes % BASE_PAGE_SIZE == 0);
|
||||
lvaddr_t end_vaddr = vaddr + bytes;
|
||||
assert(VADDR_OFFSET <= vaddr && vaddr < end_vaddr &&
|
||||
end_vaddr <= VADDR_OFFSET + PTABLE_ENTRIES * PTABLE_ENTRIES * BASE_PAGE_SIZE);
|
||||
|
||||
// Initialize if not done yet.
|
||||
if (capref_is_null(st->l2_vnode)) {
|
||||
err = init_vnodes(st);
|
||||
if (err_is_fail(err)) return err;
|
||||
// consistentcy checks
|
||||
assert(st != NULL);
|
||||
assert(st->slot_alloc != NULL);
|
||||
assert(st->l0_pt->level == 0);
|
||||
|
||||
// debug_printf("DEBUG rueegges: paging_map_fixed_attr(%p, 0x%lx, cap, %lu, %d)\n", st, vaddr, bytes, flags);
|
||||
|
||||
// simple fix for now
|
||||
for(lvaddr_t current_vaddr = vaddr; current_vaddr < vaddr + bytes; current_vaddr += BASE_PAGE_SIZE) {
|
||||
capaddr_t l0_index = VMSAv8_64_L0_INDEX(current_vaddr);
|
||||
capaddr_t l1_index = VMSAv8_64_L1_INDEX(current_vaddr);
|
||||
capaddr_t l2_index = VMSAv8_64_L2_INDEX(current_vaddr);
|
||||
capaddr_t l3_index = VMSAv8_64_L3_INDEX(current_vaddr);
|
||||
|
||||
// Cannot map anything with l0_index = 0 since this part of the page table was created by the kernel for us
|
||||
assert(l0_index != 0);
|
||||
|
||||
// get l1 page table
|
||||
struct pt_t *l1_pt;
|
||||
err = pt_ensure(st, st->l0_pt, l0_index, &l1_pt);
|
||||
if(err_is_fail(err)) {
|
||||
return err;
|
||||
}
|
||||
assert(l1_pt->level == 1);
|
||||
assert(l1_pt->children != NULL);
|
||||
|
||||
// get l2 page table
|
||||
struct pt_t *l2_pt;
|
||||
err = pt_ensure(st, l1_pt, l1_index, &l2_pt);
|
||||
if(err_is_fail(err)) {
|
||||
return err;
|
||||
}
|
||||
assert(l2_pt->level == 2);
|
||||
assert(l2_pt->children != NULL);
|
||||
|
||||
// get l3 page table
|
||||
struct pt_t *l3_pt;
|
||||
err = pt_ensure(st, l2_pt, l2_index, &l3_pt);
|
||||
if(err_is_fail(err)) {
|
||||
return err;
|
||||
}
|
||||
assert(l3_pt->level == 3);
|
||||
assert(l3_pt->children != NULL);
|
||||
|
||||
// make sure we have enough slot and slab space left
|
||||
err = pt_ensure_slots_and_slabs(st);
|
||||
if(err_is_fail(err)) {
|
||||
DEBUG_ERR(err, "Could not ensure presence of sufficient slabs and slots");
|
||||
return err;
|
||||
}
|
||||
|
||||
while (vaddr != end_vaddr) {
|
||||
size_t l2_index = VMSAv8_64_L2_INDEX(vaddr);
|
||||
size_t l3_index = VMSAv8_64_L3_INDEX(vaddr);
|
||||
size_t l3_count;
|
||||
if (l2_index != VMSAv8_64_L2_INDEX(end_vaddr)) {
|
||||
l3_count = PTABLE_ENTRIES - l3_index;
|
||||
} else {
|
||||
l3_count = VMSAv8_64_L3_INDEX(end_vaddr - vaddr);
|
||||
// debug_printf("DEBUG rueegges: paging_map_fixed_attr - allocate mapping meta\n");
|
||||
|
||||
// create structures for the new metadata
|
||||
struct pt_t *pt_entry = (struct pt_t *) slab_alloc(&st->pt_slabs);
|
||||
if (pt_entry == NULL) {
|
||||
return LIB_ERR_SLAB_ALLOC_FAIL;
|
||||
}
|
||||
|
||||
// If needed, allocate L3 vnode
|
||||
if (capref_is_null(st->l3_vnodes[l2_index])) {
|
||||
err = allocate_l3_vnode(st, l2_index);
|
||||
if (err_is_fail(err)) return err;
|
||||
// set to null to indicate it is a frame mapping and not a page table mapping
|
||||
pt_entry->children = NULL;
|
||||
// NOTE rueegges: we set this to 4 to indicate its an entry not a map to another
|
||||
// table even though for superpages the level would be 3
|
||||
pt_entry->level = 4;
|
||||
// allocate the new mapping
|
||||
err = st->slot_alloc->alloc(st->slot_alloc, &pt_entry->cap_mapping);
|
||||
if (err_is_fail(err)) {
|
||||
return err;
|
||||
}
|
||||
|
||||
struct capref frame_mapping;
|
||||
err = st->slot_alloc->alloc(st->slot_alloc, &frame_mapping);
|
||||
if (err_is_fail(err)) return err_push(err, LIB_ERR_SLOT_ALLOC);
|
||||
// debug_printf("vnode_map l2_index=%lu, slot=%lu, count=%lu\n", l2_index, l3_index, l3_count);
|
||||
err = vnode_map(st->l3_vnodes[l2_index], frame, l3_index, flags, 0, l3_count, frame_mapping);
|
||||
if (err_is_fail(err)) return err_push(err, LIB_ERR_VNODE_MAP);
|
||||
|
||||
vaddr += l3_count * BASE_PAGE_SIZE;
|
||||
// debug_printf("DEBUG rueegges: paging_map_fixed_attr - add new mapping\n");
|
||||
// create the new mapping
|
||||
size_t mapping_size = 1;
|
||||
// if (mapping_size * BASE_PAGE_SIZE < bytes) ++mapping_size;
|
||||
// debug_printf("DEBUG rueegges: vnode_map(ll_pt, frame, %u, %d, %lu, %lu, cap_mapping)\n", l3_index, flags, 0, mapping_size);
|
||||
err = vnode_map(l3_pt->cap_pt, frame, l3_index, flags, current_vaddr - vaddr, mapping_size, pt_entry->cap_mapping);
|
||||
if (err_is_fail(err)) {
|
||||
debug_printf("Failed to map vnode at vaddr 0x%lx\n", current_vaddr);
|
||||
return err;
|
||||
}
|
||||
|
||||
// add the new page table metadata to the shadow tables
|
||||
l3_pt->children[l3_index] = pt_entry;
|
||||
}
|
||||
|
||||
// debug_printf("DEBUG rueegges: paging_map_fixed_attr - success\n");
|
||||
|
||||
return SYS_ERR_OK;
|
||||
}
|
||||
|
||||
|
||||
@ -15,7 +15,9 @@
|
||||
target = "grading",
|
||||
cFiles = [
|
||||
"rpc.c",
|
||||
"grading.c"
|
||||
"grading.c",
|
||||
"test_mm.c",
|
||||
"test_paging.c"
|
||||
],
|
||||
addLibraries = [
|
||||
]
|
||||
|
||||
@ -8,6 +8,8 @@
|
||||
#include <grading.h>
|
||||
#include <spawn/spawn.h>
|
||||
|
||||
#include <test_mm.h>
|
||||
#include <test_paging.h>
|
||||
|
||||
void
|
||||
grading_setup_bsp_init(int argc, char **argv) {
|
||||
@ -103,6 +105,17 @@ grading_test_mm(struct mm *test) {
|
||||
check_err(mm_free(testmm, caplist[alloc_count]));
|
||||
}
|
||||
|
||||
test_mm_run(test_mm_small, "test_mm_small", test);
|
||||
test_mm_run(test_mm_track_slots, "test_mm_track_slots", test);
|
||||
test_mm_run(test_mm_fragments, "test_mm_fragments", test);
|
||||
test_mm_run(test_mm_rand, "test_mm_rand", test);
|
||||
test_mm_run(test_mm_many, "test_mm_many", test);
|
||||
test_mm_run(test_mm_oom, "test_mm_oom", test);
|
||||
|
||||
test_paging_run(test_paging_full_l3, "test_paging_full_l3");
|
||||
test_paging_run(test_paging_full_l2, "test_paging_full_l2");
|
||||
test_paging_run(test_paging_big_regions, "test_paging_big_regions");
|
||||
|
||||
// Test paging
|
||||
for (int i = 0; i < 40; i++) {
|
||||
debug_printf("TEST: page %lu\n", i);
|
||||
|
||||
173
lib/grading/test_mm.c
Normal file
173
lib/grading/test_mm.c
Normal file
@ -0,0 +1,173 @@
|
||||
#include <aos/aos.h>
|
||||
#include <test_mm.h>
|
||||
#include <mm/mm.h>
|
||||
|
||||
// ASSESSMENT M1: can be used to show the speed issues in mm_alloc
|
||||
#define TEST_MM_SMALL_COUNT 100
|
||||
#define TEST_MM_BIG_COUNT 10000
|
||||
|
||||
// this is outside of the functions because otherwise we have issues with
|
||||
// the function stack space in M1
|
||||
struct capref caps_big[TEST_MM_BIG_COUNT];
|
||||
struct capref caps[TEST_MM_SMALL_COUNT];
|
||||
|
||||
uint8_t test_mm_run(errval_t func(struct mm *), char *name, struct mm *mm) {
|
||||
errval_t err;
|
||||
|
||||
// debug_printf("TEST_MM %19s: start\n", name);
|
||||
err = func(mm);
|
||||
|
||||
if (err_is_ok(err)) {
|
||||
debug_printf("TEST_MM %19s: OK\n", name);
|
||||
return 1;
|
||||
} else {
|
||||
debug_printf("TEST_MM %19s: ERR\n", name);
|
||||
DEBUG_ERR(err, "Test Failed with Error");
|
||||
// mm_print_state(mm);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if small allocation sizes work (<4KiB)
|
||||
errval_t test_mm_small(struct mm *mm) {
|
||||
errval_t err;
|
||||
|
||||
for(int i = 0; i < TEST_MM_SMALL_COUNT; ++i) {
|
||||
err = mm_alloc_aligned(mm, 20, 1, &caps[i]);
|
||||
if(err_is_fail(err)) {
|
||||
debug_printf("Iteration: %d\n", i);
|
||||
return err_push(err, MM_ERR_NEW_NODE);
|
||||
}
|
||||
}
|
||||
for(int i = 0; i < TEST_MM_SMALL_COUNT; ++i) {
|
||||
err = mm_free(mm, caps[i]);
|
||||
if(err_is_fail(err)) {
|
||||
debug_printf("Iteration: %d\n", i);
|
||||
return err_push(err, MM_ERR_MM_FREE);
|
||||
}
|
||||
}
|
||||
return SYS_ERR_OK;
|
||||
}
|
||||
|
||||
// ASSESSMENT M1: show that free capability slots are tracked
|
||||
errval_t test_mm_track_slots(struct mm *mm) {
|
||||
errval_t err;
|
||||
|
||||
for(int i = 0; i < TEST_MM_SMALL_COUNT; ++i) {
|
||||
err = mm_alloc(mm, 1 << 21, &caps[i]);
|
||||
if(err_is_fail(err)) {
|
||||
debug_printf("Iteration: %d\n", i);
|
||||
return err_push(err, MM_ERR_NEW_NODE);
|
||||
}
|
||||
}
|
||||
for(int i = 0; i < TEST_MM_SMALL_COUNT; ++i) {
|
||||
err = mm_free(mm, caps[i]);
|
||||
if(err_is_fail(err)) {
|
||||
debug_printf("Iteration: %d\n", i);
|
||||
return err_push(err, MM_ERR_MM_FREE);
|
||||
}
|
||||
}
|
||||
|
||||
for(int i = 0; i < TEST_MM_SMALL_COUNT; ++i) {
|
||||
err = mm_alloc(mm, 1 << 21, &caps[i]);
|
||||
if(err_is_fail(err)) {
|
||||
debug_printf("Iteration: %d\n", i);
|
||||
return err_push(err, MM_ERR_NEW_NODE);
|
||||
}
|
||||
}
|
||||
for(int i = 0; i < TEST_MM_SMALL_COUNT; ++i) {
|
||||
err = mm_free(mm, caps[i]);
|
||||
if(err_is_fail(err)) {
|
||||
debug_printf("Iteration: %d\n", i);
|
||||
return err_push(err, MM_ERR_MM_FREE);
|
||||
}
|
||||
}
|
||||
return SYS_ERR_OK;
|
||||
}
|
||||
|
||||
// create fragmented memory and then free it
|
||||
errval_t test_mm_fragments(struct mm *mm) {
|
||||
errval_t err;
|
||||
|
||||
// fragment some memory
|
||||
for(int i = 0; i < TEST_MM_SMALL_COUNT; ++i) {
|
||||
err = mm_alloc_aligned(mm, 1 << 10, 1 << 10, &caps[i]);
|
||||
if(err_is_fail(err)) {
|
||||
debug_printf("Iteration: %d\n", i);
|
||||
return err_push(err, MM_ERR_NEW_NODE);
|
||||
}
|
||||
}
|
||||
for(int i = 0; i < TEST_MM_SMALL_COUNT; i += 2) {
|
||||
err = mm_free(mm, caps[i]);
|
||||
if(err_is_fail(err)) {
|
||||
debug_printf("Iteration: %d\n", i);
|
||||
return err_push(err, MM_ERR_MM_FREE);
|
||||
}
|
||||
}
|
||||
|
||||
// free fragmented memory
|
||||
for(int i = 1; i < TEST_MM_SMALL_COUNT; i += 2) {
|
||||
err = mm_free(mm, caps[i]);
|
||||
if(err_is_fail(err)) {
|
||||
debug_printf("Iteration: %d\n", i);
|
||||
return err_push(err, MM_ERR_MM_FREE);
|
||||
}
|
||||
}
|
||||
return SYS_ERR_OK;
|
||||
}
|
||||
|
||||
// ASSESSMENT M1: these tests demonstrate slot/slab refilling
|
||||
// allocate memory of random sizes
|
||||
errval_t test_mm_rand(struct mm *mm) {
|
||||
errval_t err;
|
||||
|
||||
for(int i = 0; i < TEST_MM_SMALL_COUNT; ++i) {
|
||||
err = mm_alloc(mm, rand() % LARGE_PAGE_SIZE + 1, &caps[i]);
|
||||
if(err_is_fail(err)) {
|
||||
debug_printf("Iteration: %d\n", i);
|
||||
return err_push(err, MM_ERR_NEW_NODE);
|
||||
}
|
||||
}
|
||||
for(int i = 0; i < TEST_MM_SMALL_COUNT; ++i) {
|
||||
err = mm_free(mm, caps[i]);
|
||||
if(err_is_fail(err)) {
|
||||
debug_printf("Iteration: %d\n", i);
|
||||
return err_push(err, MM_ERR_MM_FREE);
|
||||
}
|
||||
}
|
||||
return SYS_ERR_OK;
|
||||
}
|
||||
|
||||
// allocate loads of memory
|
||||
errval_t test_mm_many(struct mm *mm) {
|
||||
errval_t err;
|
||||
|
||||
for(int i = 0; i < TEST_MM_BIG_COUNT; ++i) {
|
||||
err = mm_alloc_aligned(mm, 1 << 10, 1 << 10, &caps_big[i]);
|
||||
if(err_is_fail(err)) {
|
||||
debug_printf("Iteration: %d\n", i);
|
||||
return err_push(err, MM_ERR_NEW_NODE);
|
||||
}
|
||||
}
|
||||
// free loads of small memory
|
||||
for(int i = 0; i < TEST_MM_BIG_COUNT; ++i) {
|
||||
err = mm_free(mm, caps_big[i]);
|
||||
if(err_is_fail(err)) {
|
||||
debug_printf("Iteration: %d\n", i);
|
||||
return err_push(err, MM_ERR_MM_FREE);
|
||||
}
|
||||
}
|
||||
return SYS_ERR_OK;
|
||||
}
|
||||
|
||||
errval_t test_mm_oom(struct mm *mm){
|
||||
errval_t err;
|
||||
|
||||
// fail because of oom
|
||||
err = mm_alloc(mm, 1L << 31, &caps[0]);
|
||||
|
||||
if(!err_is_fail(err) || err_no(err) != MM_ERR_OUT_OF_RAM) {
|
||||
return ERR_NOTIMP;
|
||||
}
|
||||
return SYS_ERR_OK;
|
||||
}
|
||||
116
lib/grading/test_paging.c
Normal file
116
lib/grading/test_paging.c
Normal file
@ -0,0 +1,116 @@
|
||||
#include <aos/aos.h>
|
||||
#include <test_paging.h>
|
||||
#include <aos/paging.h>
|
||||
|
||||
// ASSESSMENT M1: these lines show mapping to fixed locations and read/write
|
||||
|
||||
// used to avoid conflicts between tests
|
||||
lvaddr_t vaddr = 0x0000100000000000L;
|
||||
|
||||
uint8_t test_paging_run(errval_t func(void), char *name) {
|
||||
errval_t err;
|
||||
|
||||
// debug_printf("TEST_PAGING %19s: start\n", name);
|
||||
err = func();
|
||||
|
||||
if (err_is_ok(err)) {
|
||||
debug_printf("TEST_PAGING %23s: OK\n", name);
|
||||
return 1;
|
||||
} else {
|
||||
debug_printf("TEST_PAGING %23s: ERR\n", name);
|
||||
DEBUG_ERR(err, "Test Failed with Error");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
errval_t test_paging_full_l3(void) {
|
||||
errval_t err;
|
||||
|
||||
size_t mapping_size = BASE_PAGE_SIZE;
|
||||
|
||||
// map all entries of a single l3 table
|
||||
for(size_t i = 0; i < VMSAv8_64_PTABLE_NUM_ENTRIES; ++i) {
|
||||
struct capref frame;
|
||||
err = frame_alloc(&frame, mapping_size, NULL);
|
||||
if (err_is_fail(err)) {
|
||||
return err_push(err, LIB_ERR_FRAME_ALLOC);
|
||||
}
|
||||
err = paging_map_fixed_attr(get_current_paging_state(), vaddr, frame, mapping_size, VREGION_FLAGS_READ_WRITE);
|
||||
if (err_is_fail(err)) {
|
||||
return err_push(err, LIB_ERR_PMAP_NOT_MAPPED);
|
||||
}
|
||||
|
||||
// verify mapping using a random value at a random location
|
||||
uint8_t val = rand() % 256;
|
||||
memset((void *) vaddr, val, mapping_size);
|
||||
if(*((uint8_t *) vaddr + (rand() % mapping_size)) != val) {
|
||||
return ERR_INVALID_ARGS;
|
||||
}
|
||||
|
||||
vaddr += mapping_size;
|
||||
}
|
||||
|
||||
return SYS_ERR_OK;
|
||||
}
|
||||
|
||||
errval_t test_paging_full_l2(void) {
|
||||
errval_t err;
|
||||
|
||||
size_t mapping_size = BASE_PAGE_SIZE;
|
||||
|
||||
// map a page in all slots of a single l2 node (the 0-th slot was already used in a previous test)
|
||||
for(size_t i = 1; i < VMSAv8_64_PTABLE_NUM_ENTRIES; ++i) {
|
||||
struct capref frame;
|
||||
err = frame_alloc(&frame, mapping_size, NULL);
|
||||
if (err_is_fail(err)) {
|
||||
return err_push(err, LIB_ERR_FRAME_ALLOC);
|
||||
}
|
||||
err = paging_map_fixed_attr(get_current_paging_state(), vaddr, frame, mapping_size, VREGION_FLAGS_READ_WRITE);
|
||||
if (err_is_fail(err)) {
|
||||
return err_push(err, LIB_ERR_PMAP_NOT_MAPPED);
|
||||
}
|
||||
|
||||
// verify mapping using a random value at a random location
|
||||
uint8_t val = rand() % 256;
|
||||
memset((void *) vaddr, val, mapping_size);
|
||||
if(*((uint8_t *) vaddr + (rand() % mapping_size)) != val) {
|
||||
return ERR_INVALID_ARGS;
|
||||
}
|
||||
|
||||
vaddr += LARGE_PAGE_SIZE;
|
||||
}
|
||||
|
||||
return SYS_ERR_OK;
|
||||
}
|
||||
|
||||
errval_t test_paging_big_regions(void) {
|
||||
errval_t err;
|
||||
|
||||
size_t mapping_size;
|
||||
|
||||
// map larger areas
|
||||
for(size_t i = 1; i <= VMSAv8_64_PTABLE_NUM_ENTRIES - 1; ++i) {
|
||||
mapping_size = i * BASE_PAGE_SIZE;
|
||||
|
||||
struct capref frame;
|
||||
err = frame_alloc(&frame, mapping_size, NULL);
|
||||
if (err_is_fail(err)) {
|
||||
return err_push(err, LIB_ERR_FRAME_ALLOC);
|
||||
}
|
||||
err = paging_map_fixed_attr(get_current_paging_state(), vaddr, frame, mapping_size, VREGION_FLAGS_READ_WRITE);
|
||||
if (err_is_fail(err)) {
|
||||
return err_push(err, LIB_ERR_PMAP_NOT_MAPPED);
|
||||
}
|
||||
|
||||
// verify mapping using a random value at a random location
|
||||
uint8_t val = rand() % 256;
|
||||
memset((void *) vaddr, val, mapping_size);
|
||||
if(*((uint8_t *) vaddr + (rand() % mapping_size)) != val) {
|
||||
return ERR_INVALID_ARGS;
|
||||
}
|
||||
|
||||
vaddr += LARGE_PAGE_SIZE;
|
||||
}
|
||||
|
||||
return SYS_ERR_OK;
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user