Merge rueegges paging into jschaer code

This commit is contained in:
Sparchatus 2022-03-22 10:56:57 +00:00
parent 070111da22
commit 292ca0b63b
9 changed files with 696 additions and 114 deletions

View File

@ -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

View File

@ -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
View 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
View 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 */

View File

@ -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(&current, 0, cap_vroot, get_default_slot_allocator());
err = paging_init_state(&current, VADDR_OFFSET, cap_vroot, get_default_slot_allocator());
if (err_is_fail(err)) return err;
set_current_paging_state(&current);
@ -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;
}

View File

@ -15,7 +15,9 @@
target = "grading",
cFiles = [
"rpc.c",
"grading.c"
"grading.c",
"test_mm.c",
"test_paging.c"
],
addLibraries = [
]

View File

@ -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
View 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
View 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;
}