From 0224bc8fd291ec5805f8903e6a940bf0b9acdefb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sandro=20R=C3=BCegge?= Date: Thu, 24 Mar 2022 14:18:15 +0000 Subject: [PATCH] Extra/pass paging state --- errors/errno.fugu | 4 + include/aos/paging.h | 2 + include/aos/paging_types.h | 1 + include/barrelfish_kpi/init.h | 3 +- include/spawn/spawn.h | 1 + include/time.h | 1 + lib/aos/init.c | 2 +- lib/aos/paging.c | 36 ++++--- lib/grading/test_mm.c | 14 +++ lib/grading/test_paging.c | 7 +- lib/libc/gen/time.c | 9 +- lib/spawn/spawn.c | 174 +++++++++++++++++++++++++++++++++- 12 files changed, 233 insertions(+), 21 deletions(-) diff --git a/errors/errno.fugu b/errors/errno.fugu index 5afd45b..ebbbac8 100755 --- a/errors/errno.fugu +++ b/errors/errno.fugu @@ -600,6 +600,7 @@ errors spawn SPAWN_ERR_ { failure COPY_VNODE "Failure copying top-level VNode from existing domain", failure CREATE_DISPATCHER "Failure creating DCB", failure CREATE_DISPATCHER_FRAME "Failure creating dispatcher frame", + failure CREATE_VSPACE_BUF_FRAME "Failure creating vspace buf frame", failure CREATE_SELFEP "Failure creating endpoint to self", failure CREATE_ARGSPG "Failure creating page for arguments", failure CREATE_FDSPG "Failure creating page for file descriptors", @@ -613,6 +614,8 @@ errors spawn SPAWN_ERR_ { // setup_vspace failure MAP_DISPATCHER_TO_NEW "Failure mapping dispatcher frame to new domain", failure MAP_DISPATCHER_TO_SELF "Failure mapping dispatcher frame to parent", + failure MAP_VSPACE_BUF_TO_NEW "Failure mapping vspace buffer frame to new domain", + failure MAP_VSPACE_BUF_TO_SELF "Failure mapping vspace buffer frame to parent", failure MAP_ARGSPG_TO_NEW "Failure mapping arguments page to new domain", failure MAP_ARGSPG_TO_SELF "Failure mapping arguments page to parent", failure MAP_FDSPG_TO_NEW "Failure mapping file descriptors page to new domain", @@ -626,6 +629,7 @@ errors spawn SPAWN_ERR_ { failure UNMAP_MODULE "Failed unmapping module", failure CREATE_SEGCN "Failed to create segment CNode", failure CREATE_SMALLCN "Failed to create small RAM caps CNode", + failure SERIALISE_VSPACE_TOO_LARGE "Failure serializing vspace, using too many capabilities", // setup env failure ARGSPG_OVERFLOW "Overflow in arguments page: too many arguments or environment variables", diff --git a/include/aos/paging.h b/include/aos/paging.h index eb71e2b..f16fe1f 100644 --- a/include/aos/paging.h +++ b/include/aos/paging.h @@ -21,6 +21,7 @@ #include #include #include +#include struct paging_state; @@ -32,6 +33,7 @@ errval_t paging_init_state_foreign(struct paging_state *st, lvaddr_t start_vaddr struct capref pdir, struct slot_allocator * ca); /// initialize self-paging module errval_t paging_init(void); +errval_t paging_init_params(struct spawn_domain_params *params); errval_t paging_init_onthread(struct thread *t); diff --git a/include/aos/paging_types.h b/include/aos/paging_types.h index ad35644..c13fd3d 100644 --- a/include/aos/paging_types.h +++ b/include/aos/paging_types.h @@ -26,6 +26,7 @@ #define VREGION_FLAGS_MPB 0x10 // Message passing buffer #define VREGION_FLAGS_GUARD 0x20 // Guard page #define VREGION_FLAGS_MASK 0x2f // Mask of all individual VREGION_FLAGS +#define PT_CHILD_ARRAY_SIZE (PTABLE_ENTRIES * sizeof(void *)) // size of array holding references to page table children #define VREGION_FLAGS_READ_WRITE \ (VREGION_FLAGS_READ | VREGION_FLAGS_WRITE) diff --git a/include/barrelfish_kpi/init.h b/include/barrelfish_kpi/init.h index 2c9108a..883aace 100644 --- a/include/barrelfish_kpi/init.h +++ b/include/barrelfish_kpi/init.h @@ -110,7 +110,8 @@ #define TASKCN_SLOT_PROC_MNG 18 ///< Cap for the process manager #define TASKCN_SLOT_DOMAINID 19 ///< Domain ID cap #define TASKCN_SLOT_DEVMAN 20 ///< DeviceID manager capability -#define TASKCN_SLOTS_USER 21 ///< First free slot in taskcn for user +#define TASKCN_SLOT_VSPACE 21 ///< Vspace frame slot +#define TASKCN_SLOTS_USER 22 ///< First free slot in taskcn for user /* Page CNode */ #define PAGECN_SLOT_VROOT 0 ///< First slot of page cnode is root page table diff --git a/include/spawn/spawn.h b/include/spawn/spawn.h index b88fdba..dc177b8 100644 --- a/include/spawn/spawn.h +++ b/include/spawn/spawn.h @@ -50,6 +50,7 @@ struct spawninfo { struct capref cspace_cap_rootcn; // < Contains a capability for the root (L1) CNode. struct capref cspace_cap_dispframe; // < A capability to the dispatcher frame, used to communicate between a process and the CPU driver. struct capref cspace_cap_argspage; // < A page containing a list of command line arguments. + struct capref cspace_cap_vspace; // < The frame capability used to store the serialized vspace struct capref vspace_cap_l0_pagetable; }; diff --git a/include/time.h b/include/time.h index 90d946a..1e6456a 100644 --- a/include/time.h +++ b/include/time.h @@ -153,6 +153,7 @@ size_t strftime(char * __restrict, size_t, const char * __restrict, const struct tm * __restrict); time_t time(time_t *); void time_wait_ms(unsigned long); +long time_to_ms(struct timespec tt); #if __POSIX_VISIBLE >= 200112 struct sigevent; int timer_create(clockid_t, struct sigevent *__restrict, timer_t *__restrict); diff --git a/lib/aos/init.c b/lib/aos/init.c index afd03b4..2221cb5 100644 --- a/lib/aos/init.c +++ b/lib/aos/init.c @@ -128,7 +128,7 @@ errval_t barrelfish_init_onthread(struct spawn_domain_params *params) return err_push(err, LIB_ERR_RAM_ALLOC_SET); } - err = paging_init(); + err = paging_init_params(params); if (err_is_fail(err)) { return err_push(err, LIB_ERR_VSPACE_INIT); } diff --git a/lib/aos/paging.c b/lib/aos/paging.c index 2f4e7bf..30eb179 100644 --- a/lib/aos/paging.c +++ b/lib/aos/paging.c @@ -16,7 +16,6 @@ #include #include #include -#include #include "threads_priv.h" #include @@ -29,7 +28,7 @@ static struct paging_state current; #define PT_META_MAX_SIZE MAX(sizeof(struct pt_t), sizeof(struct pt_vaddr_reg_t)) #define PT_PT_SLAB_INITIAL_SPACE SLAB_STATIC_SIZE(PT_PT_SLAB_MIN_SPACE, PT_META_MAX_SIZE) -#define PT_CHILDREN_SLAB_INITIAL_SPACE SLAB_STATIC_SIZE(PT_CHILDREN_SLAB_MIN_SPACE, BASE_PAGE_SIZE) +#define PT_CHILDREN_SLAB_INITIAL_SPACE SLAB_STATIC_SIZE(PT_CHILDREN_SLAB_MIN_SPACE, PT_CHILD_ARRAY_SIZE) /** * \brief Helper function that allocates a slot and @@ -275,7 +274,7 @@ errval_t paging_init_state(struct paging_state *st, lvaddr_t start_vaddr, // initialize slab allocators slab_init(&st->pt_slabs, PT_META_MAX_SIZE, NULL); - slab_init(&st->pt_children_slabs, BASE_PAGE_SIZE, NULL); + slab_init(&st->pt_children_slabs, PT_CHILD_ARRAY_SIZE, NULL); // initialize shadow page tables st->l0_pt.cap_pt = pdir; @@ -328,12 +327,7 @@ errval_t paging_init_state_foreign(struct paging_state *st, lvaddr_t start_vaddr return paging_init_state(st, start_vaddr, pt_cap, ca); } -/** - * @brief This function initializes the paging for this domain - * - * Note: The function is called once before main. - */ -errval_t paging_init(void) +errval_t paging_init_params(struct spawn_domain_params *params) { errval_t err; debug_printf("paging_init\n"); @@ -353,10 +347,28 @@ errval_t paging_init(void) slab_grow(¤t.pt_slabs, pt_pt_slab_buf, PT_PT_SLAB_INITIAL_SPACE); slab_grow(¤t.pt_children_slabs, pt_children_slab_buf, PT_CHILDREN_SLAB_INITIAL_SPACE); + // if we got additional vspace info use it + if(params != NULL && params->vspace_buf != NULL) { + current.l0_pt = **(struct pt_t **)params->vspace_buf; + current.vaddr_head = **(struct pt_vaddr_reg_t **)(params->vspace_buf + sizeof(struct pt_t *)); + debug_printf("Received initial paging state:\n"); + pt_print_state(¤t); + } + set_current_paging_state(¤t); return SYS_ERR_OK; } +/** + * @brief This function initializes the paging for this domain + * + * Note: The function is called once before main. + */ +errval_t paging_init(void) +{ + return paging_init_params(NULL); +} + /** * @brief Initializes the paging functionality for the calling thread @@ -565,7 +577,7 @@ errval_t paging_map_fixed_attr(struct paging_state *st, lvaddr_t vaddr, assert(vaddr % BASE_PAGE_SIZE == 0); assert(bytes % BASE_PAGE_SIZE == 0); lvaddr_t end_vaddr = vaddr + bytes; - assert(end_vaddr <= PTABLE_ENTRIES * PTABLE_ENTRIES *PTABLE_ENTRIES * PTABLE_ENTRIES * BASE_PAGE_SIZE); + assert(end_vaddr <= PTABLE_ENTRIES * PTABLE_ENTRIES * PTABLE_ENTRIES * PTABLE_ENTRIES * BASE_PAGE_SIZE); assert(st != NULL); assert(st->slot_alloc != NULL); @@ -590,7 +602,6 @@ errval_t paging_map_fixed_attr(struct paging_state *st, lvaddr_t vaddr, // make sure the virtual address space is reserved for this mapping. For this we require either an allocated vaddr reg to // precisely exist as required or not at all, i.e. it is not overlapping multiple existing regions - // TODO rueegges: not sure this is the best way to do this struct pt_vaddr_reg_t *vaddr_reg = &st->vaddr_head; for(;vaddr_reg != NULL; vaddr_reg = vaddr_reg->next) { // we have found the region it belongs to @@ -611,7 +622,7 @@ errval_t paging_map_fixed_attr(struct paging_state *st, lvaddr_t vaddr, return LIB_ERR_PMAP_ADDR_NOT_FREE; } - // TODO rueegges: cleanup partially completed mapping? + // TODO: cleanup partially completed mapping? size_t mapping_size; for(lvaddr_t current_vaddr = vaddr; current_vaddr < end_vaddr; current_vaddr += mapping_size * BASE_PAGE_SIZE) { capaddr_t l0_index = VMSAv8_64_L0_INDEX(current_vaddr); @@ -662,6 +673,7 @@ errval_t paging_map_fixed_attr(struct paging_state *st, lvaddr_t vaddr, DEBUG_ERR(err, "Failed to refill slabs before adding page mapping."); return LIB_ERR_SLAB_ALLOC_FAIL; } + pt_entry->cap_pt = NULL_CAP; pt_entry->children = NULL; pt_entry->mapping_size = mapping_size; // allocate the new mapping diff --git a/lib/grading/test_mm.c b/lib/grading/test_mm.c index d370018..0967a4a 100644 --- a/lib/grading/test_mm.c +++ b/lib/grading/test_mm.c @@ -1,6 +1,7 @@ #include #include #include +#include // ASSESSMENT M1: can be used to show the speed issues in mm_alloc #define TEST_MM_SMALL_COUNT 100 @@ -140,20 +141,33 @@ static errval_t test_mm_rand(struct mm *mm) { static errval_t test_mm_many(struct mm *mm) { errval_t err; + struct timespec tt; + clock_gettime(CLOCK_REALTIME, &tt); + long start_ms = time_to_ms(tt); 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); } + if(!((i + 1) % 1000)) { + clock_gettime(CLOCK_REALTIME, &tt); + debug_printf("Mapped %d after %ld\n", i + 1, time_to_ms(tt) - start_ms); + } } // free loads of small memory + clock_gettime(CLOCK_REALTIME, &tt); + start_ms = time_to_ms(tt); 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); } + if(!((i + 1) % 1000)) { + clock_gettime(CLOCK_REALTIME, &tt); + debug_printf("Freed %d after %ld\n", i + 1, time_to_ms(tt) - start_ms); + } } return SYS_ERR_OK; } diff --git a/lib/grading/test_paging.c b/lib/grading/test_paging.c index 20fa9df..8eb28ce 100644 --- a/lib/grading/test_paging.c +++ b/lib/grading/test_paging.c @@ -154,6 +154,11 @@ static errval_t test_paging_unmap(struct paging_state *paging_state) { if (err_is_fail(err)) { return err_push(err, LIB_ERR_PMAP_NOT_MAPPED); } + + // check that it is mapped + *(uint8_t *) retaddr = 1; + assert(*(uint8_t *)retaddr == 1); + err = paging_unmap(paging_state, retaddr); if (err_is_fail(err)) { return err_push(err, LIB_ERR_PMAP_UNMAP); @@ -167,7 +172,7 @@ static errval_t test_paging_unmap(struct paging_state *paging_state) { return err_push(err, LIB_ERR_PMAP_NOT_MAPPED); } - *(uint8_t *) retaddr = 1; + // check that it is mapped assert(*(uint8_t *)retaddr == 1); return SYS_ERR_OK; diff --git a/lib/libc/gen/time.c b/lib/libc/gen/time.c index 43991bf..a480ac9 100644 --- a/lib/libc/gen/time.c +++ b/lib/libc/gen/time.c @@ -51,6 +51,11 @@ time(time_t *t) return (retval); } +long +time_to_ms(struct timespec tt) { + return tt.tv_sec * 1000 + tt.tv_nsec / 1000 / 1000; +} + /** * \brief Wait for ms milliseconds before returning. * @@ -64,10 +69,10 @@ time_wait_ms(unsigned long ms) { struct timespec tt; clock_gettime(CLOCK_REALTIME, &tt); - long start = tt.tv_sec * 1000 + tt.tv_nsec / 1000 / 1000; + long start = time_to_ms(tt); long now; do { clock_gettime(CLOCK_REALTIME, &tt); - now = tt.tv_sec * 1000 + tt.tv_nsec / 1000 / 1000; + now = time_to_ms(tt); } while(start + ms > now); } \ No newline at end of file diff --git a/lib/spawn/spawn.c b/lib/spawn/spawn.c index 3a3f287..afbbee3 100644 --- a/lib/spawn/spawn.c +++ b/lib/spawn/spawn.c @@ -119,6 +119,166 @@ static errval_t elf_allocate( return SYS_ERR_OK; } +static size_t spawn_serialice_vspace_pt_size(struct pt_t *pt) { + size_t sum = 0; + + sum += sizeof(struct pt_t); + + if(pt->children != NULL) { + // children array + sum += PT_CHILD_ARRAY_SIZE; + + for(size_t i = 0; i < PTABLE_ENTRIES; ++i) { + if(pt->children[i] != NULL) { + sum += spawn_serialice_vspace_pt_size(pt->children[i]); + } + } + } + + return sum; +} + +// places pt as first thing in buf, then copies all else and increments curr_buf accordingly +static errval_t spawn_serialize_pt(struct spawninfo *si, struct pt_t *pt, void *self_buf, void *child_buf, size_t *curr_offset, cslot_t *pagecn_slot) { + errval_t err; + + if (*pagecn_slot >= L2_CNODE_SLOTS - 2) { + return SPAWN_ERR_SERIALISE_VSPACE_TOO_LARGE; + } + + // write pt to buffer + struct pt_t *pt_child = (struct pt_t *) (self_buf + *curr_offset); + memcpy(pt_child, pt, sizeof(struct pt_t)); + *curr_offset += sizeof(struct pt_t); + + // copy capability references to child + if(!capref_is_null(pt->cap_pt)) { + pt_child->cap_pt.cnode = si->cspace_l2_cnode_pagecn; + pt_child->cap_pt.slot = *pagecn_slot; + *pagecn_slot = *pagecn_slot + 1; + err = cap_copy(pt_child->cap_pt, pt->cap_pt); + if (err_is_fail(err)) { + return err_push(err, LIB_ERR_CAP_COPY_FAIL); + } + } + + if(!capref_is_null(pt->cap_mapping)) { + pt_child->cap_mapping.cnode = si->cspace_l2_cnode_pagecn; + pt_child->cap_mapping.slot = *pagecn_slot; + *pagecn_slot = *pagecn_slot + 1; + err = cap_copy(pt_child->cap_mapping, pt->cap_mapping); + if (err_is_fail(err)) { + return err_push(err, LIB_ERR_CAP_COPY_FAIL); + } + } + + if (pt->children == NULL) { + assert(pt_child->children == NULL); + return SYS_ERR_OK; + } + + // serialize all children + pt_child->children = (struct pt_t **)(child_buf + *curr_offset); + struct pt_t ** children_self = (struct pt_t **)(self_buf + *curr_offset); + *curr_offset += PT_CHILD_ARRAY_SIZE; + for(size_t i = 0; i < PTABLE_ENTRIES; ++i) { + if (pt->children[i] == NULL) { + children_self[i] = NULL; + } else { + children_self[i] = (struct pt_t *)(child_buf + *curr_offset); + err = spawn_serialize_pt(si, pt->children[i], self_buf, child_buf, curr_offset, pagecn_slot); + if (err_is_fail(err)) { + return err; + } + } + } + + return SYS_ERR_OK; +} + +// serializes the vspace in a way that does not require deserialization +// vspace_buf->|l0_pt*,vaddr_head*,data....| +// so the child can just take the two pointers and store them into its current paging_state +static errval_t spawn_serialize_vspace(struct spawninfo *si, struct paging_state *paging_state, struct spawn_domain_params *domain_params) { + errval_t err; + + // first calculate required vspace size + size_t pt_space = spawn_serialice_vspace_pt_size(&paging_state->l0_pt); + + size_t vaddr_space = 0; + for(struct pt_vaddr_reg_t *cur_vaddr = &paging_state->vaddr_head; cur_vaddr != NULL; cur_vaddr = cur_vaddr->next) { + vaddr_space += sizeof(struct pt_vaddr_reg_t); + } + + size_t pointer_space = 2 * sizeof(void *); + + size_t total_space = pointer_space + pt_space + vaddr_space; + size_t reserve_space = + // additional structs for the vaddr allocation that is required + 2 * sizeof(struct pt_vaddr_reg_t) + + // space for potential l1,l2,l3 page tables + 6 * (sizeof(struct pt_t) + PT_CHILD_ARRAY_SIZE) + + // space for the page table entries when mapping up to 512 contiguous pages + 2 * sizeof(struct pt_t); + total_space = ROUND_UP(total_space + reserve_space, BASE_PAGE_SIZE); + // require this limit to restrict the ammount of additionally reserved space + if(total_space > LARGE_PAGE_SIZE) { + return SPAWN_ERR_SERIALISE_VSPACE_TOO_LARGE; + } + + // allocate memory to store the state + void *vspace_buf_self; + void *vspace_buf_child; + err = frame_create(si->cspace_cap_vspace, total_space, NULL); + if (err_is_fail(err)) { + return err_push(err, SPAWN_ERR_CREATE_VSPACE_BUF_FRAME); + } + err = paging_map_frame_attr( + get_current_paging_state(), &vspace_buf_self, + total_space, si->cspace_cap_vspace, VREGION_FLAGS_READ_WRITE + ); + if (err_is_fail(err)) return err_push(err, SPAWN_ERR_MAP_VSPACE_BUF_TO_SELF); + err = paging_map_frame_attr( + paging_state, &vspace_buf_child, + total_space, si->cspace_cap_vspace, VREGION_FLAGS_READ_WRITE + ); + if (err_is_fail(err)) return err_push(err, SPAWN_ERR_MAP_VSPACE_BUF_TO_NEW); + + // first store all the page table structs + cslot_t pagecn_slot = PAGECN_SLOT_VROOT + 1; + size_t curr_offset = 2 * sizeof(void *); + *(void **) vspace_buf_self = vspace_buf_child + curr_offset; + err = spawn_serialize_pt(si, &paging_state->l0_pt, vspace_buf_self, vspace_buf_child, &curr_offset, &pagecn_slot); + if(err_is_fail(err)) { + return err; + } + + // then store all the vaddr region structs + *(void **)(vspace_buf_self + sizeof(void *)) = vspace_buf_child + curr_offset; + for(struct pt_vaddr_reg_t *cur_vaddr = &paging_state->vaddr_head; cur_vaddr != NULL; cur_vaddr = cur_vaddr->next) { + struct pt_vaddr_reg_t *vaddr_child = (struct pt_vaddr_reg_t *)(vspace_buf_self + curr_offset); + memcpy(vaddr_child, cur_vaddr, sizeof(struct pt_vaddr_reg_t)); + curr_offset += sizeof(struct pt_vaddr_reg_t); + if(cur_vaddr->next != NULL) { + vaddr_child->next = (struct pt_vaddr_reg_t *)(vspace_buf_child + curr_offset); + } + } + + // unmap in self + err = paging_unmap(get_current_paging_state(), vspace_buf_self); + if (err_is_fail(err)) { + return err_push(err, LIB_ERR_PMAP_UNMAP); + } + + assert(curr_offset <= total_space); + + debug_printf("SPAWN: Passing vspace info at 0x%lx\n", vspace_buf_child); + domain_params->vspace_buf = vspace_buf_child; + domain_params->vspace_buf_len = total_space; + + return SYS_ERR_OK; +} + /** * (M2): Implement this function. @@ -205,6 +365,9 @@ errval_t spawn_load_argv(int argc, char *argv[], struct spawninfo *si, si->cspace_cap_argspage.cnode = si->cspace_l2_cnode_taskcn; si->cspace_cap_argspage.slot = TASKCN_SLOT_ARGSPAGE; + si->cspace_cap_vspace.cnode = si->cspace_l2_cnode_taskcn; + si->cspace_cap_vspace.slot = TASKCN_SLOT_VSPACE; + err = cnode_create_foreign_l2(si->cspace_l1_cnode_cap, ROOTCN_SLOT_SLOT_ALLOC0, &si->cspace_l2_cnode_slot_alloc_0); if (err_is_fail(err)) return err; err = cnode_create_foreign_l2(si->cspace_l1_cnode_cap, ROOTCN_SLOT_SLOT_ALLOC1, &si->cspace_l2_cnode_slot_alloc_1); @@ -353,16 +516,19 @@ errval_t spawn_load_argv(int argc, char *argv[], struct spawninfo *si, // afeer: put the arguments into the argspage struct spawn_domain_params * domain_params = (struct spawn_domain_params *) arguments_page_in_self; - domain_params->argc = argc; + + err = spawn_serialize_vspace(si, &child_paging_state, domain_params); + if (err_is_fail(err)) { + return err_push(err, SPAWN_ERR_SERIALISE_VSPACE); + } // afeer: TODO: what do i put here? do we need to set these fields to some value? - // domain_params->vspace_buf = NULL; - // domain_params->vspace_buf_len = 0; // domain_params->tls_init_base = NULL; // domain_params->tls_init_len = 0; // domain_params->tls_total_len = 0; // domain_params->pagesize = BASE_PAGE_SIZE; - + + domain_params->argc = argc; size_t offset = sizeof(struct spawn_domain_params); for (int i = 0; i < argc; ++i) { // afeer: put the argument into page in self