From d8f2439b7b5c9452328866ea9c4f5e9b870f7c83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Sch=C3=A4r?= Date: Thu, 7 Apr 2022 18:42:58 +0200 Subject: [PATCH] Implement heap allocation on page fault --- include/aos/paging.h | 3 + include/aos/paging_types.h | 3 +- lib/aos/init.c | 18 +++--- lib/aos/morecore.c | 29 +++++++--- lib/aos/paging.c | 112 ++++++++++++++++++++++++++++++------- 5 files changed, 128 insertions(+), 37 deletions(-) diff --git a/include/aos/paging.h b/include/aos/paging.h index f16fe1f..fce1c0a 100644 --- a/include/aos/paging.h +++ b/include/aos/paging.h @@ -38,6 +38,9 @@ errval_t paging_init_params(struct spawn_domain_params *params); errval_t paging_init_onthread(struct thread *t); +errval_t paging_alloc_ext(struct paging_state *st, void **buf, size_t bytes, + size_t alignment, bool heap); + /** * \brief Find a bit of free virtual address space that is large enough to * accomodate a buffer of size `bytes`. diff --git a/include/aos/paging_types.h b/include/aos/paging_types.h index c18a81e..4d4bb14 100644 --- a/include/aos/paging_types.h +++ b/include/aos/paging_types.h @@ -61,7 +61,8 @@ struct pt_t { struct pt_vaddr_reg_t { lvaddr_t base; size_t size; - uint8_t free; + uint8_t free:1; // Is the region free? + uint8_t heap:1; // Is the region part of the heap? struct pt_vaddr_reg_t *next; }; diff --git a/lib/aos/init.c b/lib/aos/init.c index 6649dc4..236180d 100644 --- a/lib/aos/init.c +++ b/lib/aos/init.c @@ -205,20 +205,20 @@ errval_t barrelfish_init_onthread(struct spawn_domain_params *params) // TODO MILESTONE 3: register ourselves with init /* allocate lmp channel structure */ - struct lmp_chan *init_chan = malloc(sizeof(struct lmp_chan)); - lmp_chan_init(init_chan); + static struct lmp_chan init_chan; + lmp_chan_init(&init_chan); /* create local endpoint */ - err = endpoint_create(DEFAULT_LMP_BUF_WORDS, &init_chan->local_cap, &init_chan->endpoint); + err = endpoint_create(DEFAULT_LMP_BUF_WORDS, &init_chan.local_cap, &init_chan.endpoint); if (err_is_fail(err)) { return err_push(err, LIB_ERR_ENDPOINT_CREATE); } /* set remote endpoint to init's endpoint */ - init_chan->remote_cap = cap_initep; + init_chan.remote_cap = cap_initep; /* set receive handler */ init_chan_initialized = false; - err = lmp_chan_register_recv(init_chan, get_default_waitset(), MKCLOSURE(handle_init_recv, init_chan)); + err = lmp_chan_register_recv(&init_chan, get_default_waitset(), MKCLOSURE(handle_init_recv, &init_chan)); /* send local ep to init */ - err = lmp_chan_send1(init_chan, LMP_FLAG_YIELD | LMP_FLAG_SYNC, init_chan->local_cap, RPC_MTYPE_CHILD_ENDPOINT); + err = lmp_chan_send1(&init_chan, LMP_FLAG_YIELD | LMP_FLAG_SYNC, init_chan.local_cap, RPC_MTYPE_CHILD_ENDPOINT); if (err_is_fail(err)) { // should never fail since init listens before invoking the dispatcher return err_push(err, LIB_ERR_LMP_CHAN_SEND); @@ -231,13 +231,13 @@ errval_t barrelfish_init_onthread(struct spawn_domain_params *params) } } /* initialize init RPC client with lmp channel */ - struct aos_rpc *init_rpc = malloc(sizeof(struct aos_rpc)); - err = aos_rpc_init(init_rpc, init_chan, params->rpc_shared_memory); + static struct aos_rpc init_rpc; + err = aos_rpc_init(&init_rpc, &init_chan, params->rpc_shared_memory); if (err_is_fail(err)){ return err_push(err, ERR_NOTIMP); } /* set init RPC client in our program state */ - set_init_rpc(init_rpc); + set_init_rpc(&init_rpc); /* TODO MILESTONE 3: now we should have a channel with init set up and can * use it for the ram allocator */ diff --git a/lib/aos/morecore.c b/lib/aos/morecore.c index dda829b..2207d20 100644 --- a/lib/aos/morecore.c +++ b/lib/aos/morecore.c @@ -26,8 +26,8 @@ extern morecore_free_func_t sys_morecore_free; // this define makes morecore use an implementation that just has a static // 16MB heap. -// TODO (M4): use a dynamic heap instead, -#define USE_STATIC_HEAP +// (M4): use a dynamic heap instead, +//#define USE_STATIC_HEAP #ifdef USE_STATIC_HEAP @@ -101,26 +101,39 @@ errval_t morecore_reinit(void) */ static void *morecore_alloc(size_t bytes, size_t *retbytes) { - USER_PANIC("NYI: implement me (M4)\n"); - return NULL; + errval_t err; + // struct morecore_state *state = get_morecore_state(); + + size_t aligned_bytes = ROUND_UP(bytes, LARGE_PAGE_SIZE); + void *ret = NULL; + err = paging_alloc_ext(get_current_paging_state(), &ret, aligned_bytes, LARGE_PAGE_SIZE, true); + if (err_is_fail(err)) { + DEBUG_ERR(err, "in morecore_alloc, size=%"PRIuPTR"\n", aligned_bytes); + aligned_bytes = 0; + } + *retbytes = aligned_bytes; + return ret; } static void morecore_free(void *base, size_t bytes) { - USER_PANIC("NYI: implement me (M4)\n"); } errval_t morecore_init(size_t alignment) { + struct morecore_state *state = get_morecore_state(); + debug_printf("initializing dynamic heap\n"); - USER_PANIC("NYI: implement me (M4)\n"); - return LIB_ERR_NOT_IMPLEMENTED; + thread_mutex_init(&state->mutex); + + sys_morecore_alloc = morecore_alloc; + sys_morecore_free = morecore_free; + return SYS_ERR_OK; } errval_t morecore_reinit(void) { - USER_PANIC("NYI \n"); return SYS_ERR_OK; } diff --git a/lib/aos/paging.c b/lib/aos/paging.c index 5a9e286..810ad55 100644 --- a/lib/aos/paging.c +++ b/lib/aos/paging.c @@ -34,6 +34,9 @@ static struct paging_state current; // initial page fault handler stack space static char pt_static_exception_stack[PT_STATIC_EXCEPTION_STACK_SIZE]; +static errval_t _paging_map_fixed_attr(struct paging_state *st, lvaddr_t vaddr, + struct capref frame, size_t bytes, int flags); + static char *pt_exception_type_to_string(enum exception_type type, int subtype) { switch(type) { @@ -63,9 +66,34 @@ static char *pt_exception_type_to_string(enum exception_type type, int subtype) } } +// Returns true if the page at vaddr is mapped. +static bool is_mapped(struct paging_state *st, lvaddr_t vaddr) +{ + capaddr_t l0_index = VMSAv8_64_L0_INDEX(vaddr); + capaddr_t l1_index = VMSAv8_64_L1_INDEX(vaddr); + capaddr_t l2_index = VMSAv8_64_L2_INDEX(vaddr); + capaddr_t l3_index = VMSAv8_64_L3_INDEX(vaddr); + + struct pt_t *l0_pt = &st->l0_pt; + if (l0_pt->children == NULL) return false; + + struct pt_t *l1_pt = l0_pt->children[l0_index]; + if(l1_pt == NULL) return false; + + struct pt_t *l2_pt = l1_pt->children[l1_index]; + if(l2_pt == NULL) return false; + + struct pt_t *l3_pt = l2_pt->children[l2_index]; + if(l3_pt == NULL) return false; + + if(l3_pt->children[l3_index] == NULL) return false; + return true; +} + static void pt_exception_handler(enum exception_type type, int subtype, void *addr, arch_registers_state_t *regs) { + errval_t err; lvaddr_t ip = registers_get_ip(regs); debug_printf("Page Fault: type=%s, addr=%p, ip=%p\n", @@ -81,7 +109,41 @@ static void pt_exception_handler(enum exception_type type, int subtype, USER_PANIC("[ERROR] NULL pointer dereference"); } - USER_PANIC("[ERROR] Page fault handling not yet implemented"); + lvaddr_t page_addr = ROUND_DOWN((lvaddr_t)addr, BASE_PAGE_SIZE); + struct paging_state *st = get_current_paging_state(); + + thread_mutex_lock_nested(&st->lock); + + struct pt_vaddr_reg_t *vaddr_reg = &st->vaddr_head; + for (; vaddr_reg != NULL; vaddr_reg = vaddr_reg->next) { + if (vaddr_reg->base <= page_addr && page_addr < vaddr_reg->base + vaddr_reg->size) { + break; + } + } + if (vaddr_reg == NULL || vaddr_reg->free) { + USER_PANIC("[ERROR] Page fault ouside allocated address space"); + } + if (!vaddr_reg->heap) { + USER_PANIC("[ERROR] Page fault in allocated address space but outside heap"); + } + + if (is_mapped(st, page_addr)) { + // Page has already been mapped by another thread before we took the lock. + return; + } + + struct capref frame; + err = frame_alloc(&frame, BASE_PAGE_SIZE, NULL); + if (err_is_fail(err)) { + USER_PANIC_ERR(err, "Failed to allocate frame in page fault handler"); + } + + err = _paging_map_fixed_attr(st, page_addr, frame, BASE_PAGE_SIZE, VREGION_FLAGS_READ_WRITE); + if (err_is_fail(err)) { + USER_PANIC_ERR(err, "Failed to map frame in page fault handler"); + } + + thread_mutex_unlock(&st->lock); } /** @@ -166,7 +228,7 @@ void pt_print_state(struct paging_state *st) { debug_printf("ranges:\n"); struct pt_vaddr_reg_t *reg = &st->vaddr_head; while (reg != NULL) { - debug_printf("- base=%lu, size=%lu, %s\n", reg->base, reg->size, reg->free ? "free" : "in use"); + debug_printf("- base=%lu, size=%lu, %s%s\n", reg->base, reg->size, reg->free ? "free" : "in use", reg->heap ? ", heap" : ""); reg = reg->next; } @@ -343,6 +405,7 @@ errval_t paging_init_state(struct paging_state *st, lvaddr_t start_vaddr, st->vaddr_head.base = start_vaddr; st->vaddr_head.size = VADDR_SIZE - start_vaddr; st->vaddr_head.free = true; + st->vaddr_head.heap = false; st->vaddr_head.next = NULL; st->slot_alloc = ca; @@ -392,7 +455,7 @@ errval_t paging_init_params(struct spawn_domain_params *params) { errval_t err; // (M2): Call paging_init_state for ¤t - // TODO (M4): initialize self-paging handler + // (M4): initialize self-paging handler // TIP: use thread_set_exception_handler() to setup a page fault handler // TIP: Think about the fact that later on, you'll have to make sure that // you can handle page faults in any thread of a domain. @@ -445,7 +508,7 @@ errval_t paging_init(void) */ errval_t paging_init_onthread(struct thread *t) { - // TODO (M4): + // (M4): // - setup exception handler for thread `t'. errval_t err; @@ -470,8 +533,10 @@ errval_t paging_init_onthread(struct thread *t) } -static errval_t paging_insert_vaddr_reg(struct paging_state *st, struct pt_vaddr_reg_t *target_region, size_t prefix_size, size_t alloc_size) -{ +static errval_t paging_insert_vaddr_reg( + struct paging_state *st, struct pt_vaddr_reg_t *target_region, + size_t prefix_size, size_t alloc_size, bool heap +) { assert(target_region->free); assert(target_region->size >= prefix_size + alloc_size); @@ -507,7 +572,6 @@ static errval_t paging_insert_vaddr_reg(struct paging_state *st, struct pt_vaddr if (prefix_size > 0) { prefix_reg->base = base; prefix_reg->size = prefix_size; - prefix_reg->free = true; main_reg->next = prefix_reg->next; prefix_reg->next = main_reg; @@ -518,6 +582,7 @@ static errval_t paging_insert_vaddr_reg(struct paging_state *st, struct pt_vaddr main_reg->base = base; main_reg->size = alloc_size; main_reg->free = false; + main_reg->heap = heap; base += alloc_size; @@ -525,6 +590,7 @@ static errval_t paging_insert_vaddr_reg(struct paging_state *st, struct pt_vaddr postfix_reg->base = base; postfix_reg->size = postfix_size; postfix_reg->free = true; + postfix_reg->heap = false; postfix_reg->next = main_reg->next; main_reg->next = postfix_reg; @@ -533,8 +599,10 @@ static errval_t paging_insert_vaddr_reg(struct paging_state *st, struct pt_vaddr return SYS_ERR_OK; } -static errval_t _paging_alloc(struct paging_state *st, void **buf, size_t bytes, size_t alignment) -{ +static errval_t _paging_alloc_ext( + struct paging_state *st, void **buf, size_t bytes, + size_t alignment, bool heap +) { errval_t err; /** @@ -565,7 +633,7 @@ static errval_t _paging_alloc(struct paging_state *st, void **buf, size_t bytes, if (vaddr_reg->free && vaddr_reg->size >= prefix_size + bytes) { // allocate the new region, potentially splitting off a prefix and postfix from the region - err = paging_insert_vaddr_reg(st, vaddr_reg, prefix_size, bytes); + err = paging_insert_vaddr_reg(st, vaddr_reg, prefix_size, bytes, heap); if (err_is_fail(err)) { return err; } @@ -580,6 +648,17 @@ static errval_t _paging_alloc(struct paging_state *st, void **buf, size_t bytes, return LIB_ERR_OUT_OF_VIRTUAL_ADDR; } +errval_t paging_alloc_ext(struct paging_state *st, void **buf, size_t bytes, size_t alignment, bool heap) +{ + errval_t err; + + thread_mutex_lock_nested(&st->lock); + err = _paging_alloc_ext(st, buf, bytes, alignment, heap); + thread_mutex_unlock(&st->lock); + + return err; +} + /** * @brief Find a free region of virtual address space that is large enough to accomodate a * buffer of size 'bytes'. @@ -593,13 +672,7 @@ static errval_t _paging_alloc(struct paging_state *st, void **buf, size_t bytes, */ errval_t paging_alloc(struct paging_state *st, void **buf, size_t bytes, size_t alignment) { - errval_t err; - - thread_mutex_lock_nested(&st->lock); - err = _paging_alloc(st, buf, bytes, alignment); - thread_mutex_unlock(&st->lock); - - return err; + return paging_alloc_ext(st, buf, bytes, alignment, false); } static errval_t _paging_map_frame_attr(struct paging_state *st, void **buf, size_t bytes, @@ -701,11 +774,12 @@ static errval_t _paging_map_fixed_attr(struct paging_state *st, lvaddr_t vaddr, if(vaddr_reg->base <= vaddr && end_vaddr <= vaddr_reg->base + vaddr_reg->size) { if (vaddr_reg->free == true) { // make sure the virtual memory is not used by anyone else - err = paging_insert_vaddr_reg(st, vaddr_reg, vaddr - vaddr_reg->base, bytes); + err = paging_insert_vaddr_reg(st, vaddr_reg, vaddr - vaddr_reg->base, bytes, false); if (err_is_fail(err)) { return err; } - } else if (!(vaddr_reg->base == vaddr && end_vaddr == vaddr_reg->base + vaddr_reg->size)) { + } else if (!(vaddr_reg->base == vaddr && end_vaddr == vaddr_reg->base + vaddr_reg->size) && + !vaddr_reg->heap) { return LIB_ERR_PMAP_ADDR_NOT_FREE; } break;