Implement heap allocation on page fault

This commit is contained in:
Jan Schär 2022-04-07 18:42:58 +02:00
parent 0c224f9066
commit d8f2439b7b
5 changed files with 128 additions and 37 deletions

View File

@ -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_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 * \brief Find a bit of free virtual address space that is large enough to
* accomodate a buffer of size `bytes`. * accomodate a buffer of size `bytes`.

View File

@ -61,7 +61,8 @@ struct pt_t {
struct pt_vaddr_reg_t { struct pt_vaddr_reg_t {
lvaddr_t base; lvaddr_t base;
size_t size; 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; struct pt_vaddr_reg_t *next;
}; };

View File

@ -205,20 +205,20 @@ errval_t barrelfish_init_onthread(struct spawn_domain_params *params)
// TODO MILESTONE 3: register ourselves with init // TODO MILESTONE 3: register ourselves with init
/* allocate lmp channel structure */ /* allocate lmp channel structure */
struct lmp_chan *init_chan = malloc(sizeof(struct lmp_chan)); static struct lmp_chan init_chan;
lmp_chan_init(init_chan); lmp_chan_init(&init_chan);
/* create local endpoint */ /* 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)) { if (err_is_fail(err)) {
return err_push(err, LIB_ERR_ENDPOINT_CREATE); return err_push(err, LIB_ERR_ENDPOINT_CREATE);
} }
/* set remote endpoint to init's endpoint */ /* set remote endpoint to init's endpoint */
init_chan->remote_cap = cap_initep; init_chan.remote_cap = cap_initep;
/* set receive handler */ /* set receive handler */
init_chan_initialized = false; 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 */ /* 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)) { if (err_is_fail(err)) {
// should never fail since init listens before invoking the dispatcher // should never fail since init listens before invoking the dispatcher
return err_push(err, LIB_ERR_LMP_CHAN_SEND); 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 */ /* initialize init RPC client with lmp channel */
struct aos_rpc *init_rpc = malloc(sizeof(struct aos_rpc)); static struct aos_rpc init_rpc;
err = aos_rpc_init(init_rpc, init_chan, params->rpc_shared_memory); err = aos_rpc_init(&init_rpc, &init_chan, params->rpc_shared_memory);
if (err_is_fail(err)){ if (err_is_fail(err)){
return err_push(err, ERR_NOTIMP); return err_push(err, ERR_NOTIMP);
} }
/* set init RPC client in our program state */ /* 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 /* TODO MILESTONE 3: now we should have a channel with init set up and can
* use it for the ram allocator */ * use it for the ram allocator */

View File

@ -26,8 +26,8 @@ extern morecore_free_func_t sys_morecore_free;
// this define makes morecore use an implementation that just has a static // this define makes morecore use an implementation that just has a static
// 16MB heap. // 16MB heap.
// TODO (M4): use a dynamic heap instead, // (M4): use a dynamic heap instead,
#define USE_STATIC_HEAP //#define USE_STATIC_HEAP
#ifdef 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) static void *morecore_alloc(size_t bytes, size_t *retbytes)
{ {
USER_PANIC("NYI: implement me (M4)\n"); errval_t err;
return NULL; // 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) static void morecore_free(void *base, size_t bytes)
{ {
USER_PANIC("NYI: implement me (M4)\n");
} }
errval_t morecore_init(size_t alignment) errval_t morecore_init(size_t alignment)
{ {
struct morecore_state *state = get_morecore_state();
debug_printf("initializing dynamic heap\n"); debug_printf("initializing dynamic heap\n");
USER_PANIC("NYI: implement me (M4)\n"); thread_mutex_init(&state->mutex);
return LIB_ERR_NOT_IMPLEMENTED;
sys_morecore_alloc = morecore_alloc;
sys_morecore_free = morecore_free;
return SYS_ERR_OK;
} }
errval_t morecore_reinit(void) errval_t morecore_reinit(void)
{ {
USER_PANIC("NYI \n");
return SYS_ERR_OK; return SYS_ERR_OK;
} }

View File

@ -34,6 +34,9 @@ static struct paging_state current;
// initial page fault handler stack space // initial page fault handler stack space
static char pt_static_exception_stack[PT_STATIC_EXCEPTION_STACK_SIZE]; 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) static char *pt_exception_type_to_string(enum exception_type type, int subtype)
{ {
switch(type) { 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, static void pt_exception_handler(enum exception_type type, int subtype,
void *addr, arch_registers_state_t *regs) void *addr, arch_registers_state_t *regs)
{ {
errval_t err;
lvaddr_t ip = registers_get_ip(regs); lvaddr_t ip = registers_get_ip(regs);
debug_printf("Page Fault: type=%s, addr=%p, ip=%p\n", 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] 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"); debug_printf("ranges:\n");
struct pt_vaddr_reg_t *reg = &st->vaddr_head; struct pt_vaddr_reg_t *reg = &st->vaddr_head;
while (reg != NULL) { 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; 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.base = start_vaddr;
st->vaddr_head.size = VADDR_SIZE - start_vaddr; st->vaddr_head.size = VADDR_SIZE - start_vaddr;
st->vaddr_head.free = true; st->vaddr_head.free = true;
st->vaddr_head.heap = false;
st->vaddr_head.next = NULL; st->vaddr_head.next = NULL;
st->slot_alloc = ca; st->slot_alloc = ca;
@ -392,7 +455,7 @@ errval_t paging_init_params(struct spawn_domain_params *params)
{ {
errval_t err; errval_t err;
// (M2): Call paging_init_state for &current // (M2): Call paging_init_state for &current
// TODO (M4): initialize self-paging handler // (M4): initialize self-paging handler
// TIP: use thread_set_exception_handler() to setup a page fault 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 // 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. // 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) errval_t paging_init_onthread(struct thread *t)
{ {
// TODO (M4): // (M4):
// - setup exception handler for thread `t'. // - setup exception handler for thread `t'.
errval_t err; 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->free);
assert(target_region->size >= prefix_size + alloc_size); 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) { if (prefix_size > 0) {
prefix_reg->base = base; prefix_reg->base = base;
prefix_reg->size = prefix_size; prefix_reg->size = prefix_size;
prefix_reg->free = true;
main_reg->next = prefix_reg->next; main_reg->next = prefix_reg->next;
prefix_reg->next = main_reg; 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->base = base;
main_reg->size = alloc_size; main_reg->size = alloc_size;
main_reg->free = false; main_reg->free = false;
main_reg->heap = heap;
base += alloc_size; 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->base = base;
postfix_reg->size = postfix_size; postfix_reg->size = postfix_size;
postfix_reg->free = true; postfix_reg->free = true;
postfix_reg->heap = false;
postfix_reg->next = main_reg->next; postfix_reg->next = main_reg->next;
main_reg->next = postfix_reg; 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; 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; 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) { if (vaddr_reg->free && vaddr_reg->size >= prefix_size + bytes) {
// allocate the new region, potentially splitting off a prefix and postfix from the region // 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)) { if (err_is_fail(err)) {
return 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; 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 * @brief Find a free region of virtual address space that is large enough to accomodate a
* buffer of size 'bytes'. * 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 paging_alloc(struct paging_state *st, void **buf, size_t bytes, size_t alignment)
{ {
errval_t err; return paging_alloc_ext(st, buf, bytes, alignment, false);
thread_mutex_lock_nested(&st->lock);
err = _paging_alloc(st, buf, bytes, alignment);
thread_mutex_unlock(&st->lock);
return err;
} }
static errval_t _paging_map_frame_attr(struct paging_state *st, void **buf, size_t bytes, 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->base <= vaddr && end_vaddr <= vaddr_reg->base + vaddr_reg->size) {
if (vaddr_reg->free == true) { if (vaddr_reg->free == true) {
// make sure the virtual memory is not used by anyone else // 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)) { if (err_is_fail(err)) {
return 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; return LIB_ERR_PMAP_ADDR_NOT_FREE;
} }
break; break;