diff --git a/include/aos/paging.h b/include/aos/paging.h index f0b958b..eb71e2b 100644 --- a/include/aos/paging.h +++ b/include/aos/paging.h @@ -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 diff --git a/include/aos/paging_types.h b/include/aos/paging_types.h index 3e0c081..fae9254 100644 --- a/include/aos/paging_types.h +++ b/include/aos/paging_types.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; }; diff --git a/include/test_mm.h b/include/test_mm.h new file mode 100644 index 0000000..177a4eb --- /dev/null +++ b/include/test_mm.h @@ -0,0 +1,15 @@ +#ifndef __TEST_MM_H +#define __TEST_MM_H + +#include +#include + +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 */ diff --git a/include/test_paging.h b/include/test_paging.h new file mode 100644 index 0000000..137a252 --- /dev/null +++ b/include/test_paging.h @@ -0,0 +1,11 @@ +#ifndef __PAGING_TESTS_H +#define __PAGING_TESTS_H + +#include + +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 */ \ No newline at end of file diff --git a/lib/aos/paging.c b/lib/aos/paging.c index 5a34b59..6a4fce2 100644 --- a/lib/aos/paging.c +++ b/lib/aos/paging.c @@ -23,14 +23,22 @@ 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 * creates a aarch64 page table capability for a certain level */ -static errval_t pt_alloc(struct paging_state * st, enum objtype type, - struct capref *ret) +static errval_t pt_alloc(struct paging_state * st, enum objtype type, + struct capref *ret) { errval_t err; err = st->slot_alloc->alloc(st->slot_alloc, ret); @@ -56,18 +64,231 @@ __attribute__((unused)) static errval_t pt_alloc_l2(struct paging_state * st, st return pt_alloc(st, ObjType_VNode_AARCH64_l2, ret); } -__attribute__((unused)) static errval_t pt_alloc_l3(struct paging_state * st, struct capref *ret) +__attribute__((unused)) static errval_t pt_alloc_l3(struct paging_state * st, struct capref *ret) { 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. * TODO(M4): Improve this function. * \brief Initialize the paging_state struct for the paging * state of the calling process. - * + * * \param st The struct to be initialized, must not be NULL. * \param start_vaddr Virtual address allocation should start at * this address. @@ -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; } @@ -106,7 +346,7 @@ errval_t paging_init_state(struct paging_state *st, lvaddr_t start_vaddr, * TODO(M4): Improve this function. * \brief Initialize the paging_state struct for the paging state * of a child process. - * + * * \param st The struct to be initialized, must not be NULL. * \param start_vaddr Virtual address allocation should start at * this address. @@ -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,42 +483,98 @@ 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); + + // consistentcy checks + assert(st != NULL); + assert(st->slot_alloc != NULL); + assert(st->l0_pt->level == 0); - // Initialize if not done yet. - if (capref_is_null(st->l2_vnode)) { - err = init_vnodes(st); - if (err_is_fail(err)) return err; - } + // debug_printf("DEBUG rueegges: paging_map_fixed_attr(%p, 0x%lx, cap, %lu, %d)\n", st, vaddr, bytes, flags); - 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); + // 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; } - // 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; + // 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; } - 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); + // 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; + } - 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; } diff --git a/lib/grading/Hakefile b/lib/grading/Hakefile index 4ba5638..bc43c6c 100644 --- a/lib/grading/Hakefile +++ b/lib/grading/Hakefile @@ -15,7 +15,9 @@ target = "grading", cFiles = [ "rpc.c", - "grading.c" + "grading.c", + "test_mm.c", + "test_paging.c" ], addLibraries = [ ] diff --git a/lib/grading/grading.c b/lib/grading/grading.c index b965992..6bd5572 100644 --- a/lib/grading/grading.c +++ b/lib/grading/grading.c @@ -8,6 +8,8 @@ #include #include +#include +#include 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); diff --git a/lib/grading/test_mm.c b/lib/grading/test_mm.c new file mode 100644 index 0000000..3fd49d1 --- /dev/null +++ b/lib/grading/test_mm.c @@ -0,0 +1,173 @@ +#include +#include +#include + +// 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; +} \ No newline at end of file diff --git a/lib/grading/test_paging.c b/lib/grading/test_paging.c new file mode 100644 index 0000000..6aea57b --- /dev/null +++ b/lib/grading/test_paging.c @@ -0,0 +1,116 @@ +#include +#include +#include + +// 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; +}