From 10138df397b4e35d3003910e46761fd66ee55813 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Sch=C3=A4r?= Date: Thu, 17 Mar 2022 17:29:59 +0100 Subject: [PATCH] Implement Milestone 1 --- errors/errno.fugu | 10 +- include/aos/paging_types.h | 5 + include/aos/slab.h | 1 + include/mm/mm.h | 17 ++- lib/aos/paging.c | 147 +++++++++++++++++++++++-- lib/aos/slab.c | 40 ++++++- lib/grading/grading.c | 89 +++++++++++++++ lib/mm/mm.c | 219 ++++++++++++++++++++++++++++++++++++- usr/init/mem_alloc.c | 19 ++-- 9 files changed, 518 insertions(+), 29 deletions(-) diff --git a/errors/errno.fugu b/errors/errno.fugu index 8800ce0..0430e02 100755 --- a/errors/errno.fugu +++ b/errors/errno.fugu @@ -208,7 +208,7 @@ errors libaos LIB_ERR_ { failure NOT_IMPLEMENTED "functionality not implemented yet", failure SHOULD_NOT_GET_HERE "Should not get here", failure NOT_CNODE "Function invoked on a capref, that does not represent a CNode", - failure STRING_TOO_LONG "String argument too long", + failure STRING_TOO_LONG "String argument too long", // cspace failure CNODE_TYPE "Type requested for cnode creation is not valid cnode type", @@ -668,7 +668,7 @@ errors spawn SPAWN_ERR_ { failure DOMAIN_ALLOCATE "No more domain descriptors", failure DOMAIN_NOTFOUND "Domain not found", failure DOMAIN_RUNNING "Domain is running", - + failure IDENTIFY_PROC_MNGR_CAP "Failed to identify process manager cap", failure NOT_PROC_MNGR "Request did not come from the process manager", @@ -726,6 +726,8 @@ errors libmm MM_ERR_ { failure CHUNK_SLOT_ALLOC "Failure allocating slots for chunking", failure RESIZE_NODE "Nested failure in resize_node()", failure REALLOC_RANGE "Nested failure in realloc_range()", + failure BAD_ALIGNMENT "Cannot allocate with non-power of two alignment", + failure OUT_OF_RAM "Failed to allocate RAM with the requested size and alignment", }; // errors in init @@ -934,7 +936,7 @@ errors fs FS_ERR_ { failure NOTFOUND "The given name does not exist", failure EXISTS "The given name already exists", failure NOTEMPTY "The given directory is not empty", - + failure READ "Failure during file read", failure WRITE "Failure during writing the file", failure OPEN "Failure during open", @@ -1333,7 +1335,7 @@ errors queue QSERVICE_ERR_{ failure NO_VALID_EP "No valid EP could not initalized client", failure INVALID_CLIENT "Invalid client struct", failure INVALID_SERVICE "Invalid service struct", -}; +}; errors psci PSCI_ERR_{ failure NOT_SUPPORTED "Function not supported", diff --git a/include/aos/paging_types.h b/include/aos/paging_types.h index e39021a..3e0c081 100644 --- a/include/aos/paging_types.h +++ b/include/aos/paging_types.h @@ -43,6 +43,11 @@ typedef int paging_flags_t; // 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]; + struct capref free_l3_vnode; + lvaddr_t next_vaddr; }; diff --git a/include/aos/slab.h b/include/aos/slab.h index 6c85f79..8e999fd 100644 --- a/include/aos/slab.h +++ b/include/aos/slab.h @@ -46,6 +46,7 @@ void *slab_alloc(struct slab_allocator *slabs); void slab_free(struct slab_allocator *slabs, void *block); size_t slab_freecount(struct slab_allocator *slabs); errval_t slab_default_refill(struct slab_allocator *slabs); +errval_t slab_refill_pages(struct slab_allocator *slabs, size_t bytes); errval_t slab_refill_no_pagefault(struct slab_allocator *slabs, struct capref frame, size_t minbytes); diff --git a/include/mm/mm.h b/include/mm/mm.h index db4cb2f..49e56db 100644 --- a/include/mm/mm.h +++ b/include/mm/mm.h @@ -25,12 +25,23 @@ __BEGIN_DECLS +#define MM_BLOCK_BITS 12 +#define MM_BLOCK_SIZE BIT(MM_BLOCK_BITS) + +struct mm_root_node; /** * \brief Memory manager instance data * * This should be opaque from the perspective of the client, but to allow * them to allocate its memory, we declare it in the public header. + * + * Invariants: + * - either all or none of head, tail and current are NULL + * - if head != NULL, then after some number of ->next, + * we reach tail, and tail->next == NULL + * - if head != NULL, then after some number of ->next, + * we reach current */ struct mm { struct slab_allocator slabs; ///< Slab allocator used for allocating nodes @@ -38,7 +49,11 @@ struct mm { slot_refill_t slot_refill; ///< Slot allocator refill function void *slot_alloc_inst; ///< Opaque instance pointer for slot allocator enum objtype objtype; ///< Type of capabilities stored - // TODO: add your meta data tracking here... + struct mm_root_node *head; ///< First RAM root node + struct mm_root_node *tail; ///< Last RAM root node + struct mm_root_node *current; ///< RAM root node to allocate from next + size_t current_offset; ///< Offset in `current` to allocate from next + size_t unallocated_leafs; ///< Number of leafs which don't have a block allocated yet }; errval_t mm_init(struct mm *mm, enum objtype objtype, diff --git a/lib/aos/paging.c b/lib/aos/paging.c index 3c6831c..5a34b59 100644 --- a/lib/aos/paging.c +++ b/lib/aos/paging.c @@ -29,8 +29,8 @@ static struct paging_state current; * \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,7 +56,7 @@ __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); } @@ -67,7 +67,7 @@ __attribute__((unused)) static errval_t pt_alloc_l3(struct paging_state * st, st * 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. @@ -82,7 +82,23 @@ errval_t paging_init_state(struct paging_state *st, lvaddr_t start_vaddr, // TODO (M2): Implement state struct initialization // TODO (M4): Implement page fault handler that installs frames when a page fault // occurs and keeps track of the virtual address space. - return LIB_ERR_NOT_IMPLEMENTED; + + st->slot_alloc = ca; + + // 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; + + for (size_t i = 0; i < PTABLE_ENTRIES; i++) { + st->l3_vnodes[i] = NULL_CAP; + } + + st->free_l3_vnode = NULL_CAP; + + st->next_vaddr = VADDR_OFFSET; + + return SYS_ERR_OK; } /** @@ -90,7 +106,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. @@ -115,6 +131,7 @@ errval_t paging_init_state_foreign(struct paging_state *st, lvaddr_t start_vaddr */ errval_t paging_init(void) { + errval_t err; debug_printf("paging_init\n"); // TODO (M2): Call paging_init_state for ¤t // TODO (M4): initialize self-paging handler @@ -123,6 +140,10 @@ errval_t paging_init(void) // you can handle page faults in any thread of a domain. // 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()); + if (err_is_fail(err)) return err; + set_current_paging_state(¤t); return SYS_ERR_OK; } @@ -194,6 +215,77 @@ 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 @@ -210,8 +302,9 @@ errval_t paging_map_frame_attr(struct paging_state *st, void **buf, size_t bytes errval_t paging_map_fixed_attr(struct paging_state *st, lvaddr_t vaddr, struct capref frame, size_t bytes, int flags) { + errval_t err; /* - * TODO(M1): + * 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. @@ -220,7 +313,45 @@ errval_t paging_map_fixed_attr(struct paging_state *st, lvaddr_t vaddr, * Hint: * - think about what mapping configurations are actually possible */ - return LIB_ERR_NOT_IMPLEMENTED; + + 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); + + // Initialize if not done yet. + if (capref_is_null(st->l2_vnode)) { + err = init_vnodes(st); + if (err_is_fail(err)) return err; + } + + 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); + } + + // 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; + } + + 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); + + vaddr += l3_count * BASE_PAGE_SIZE; + } + + return SYS_ERR_OK; } diff --git a/lib/aos/slab.c b/lib/aos/slab.c index 7c373f3..2e5d0e7 100644 --- a/lib/aos/slab.c +++ b/lib/aos/slab.c @@ -177,12 +177,26 @@ size_t slab_freecount(struct slab_allocator *slabs) * \param slabs Pointer to slab allocator instance * \param bytes (Minimum) amount of memory to map */ -static errval_t slab_refill_pages(struct slab_allocator *slabs, size_t bytes) +errval_t slab_refill_pages(struct slab_allocator *slabs, size_t bytes) { + errval_t err; // Hint: you can't just use malloc here... // Hint: For M1, just use the fixed mapping funcionality, however you may want to replace // the fixed mapping later to avoid conflicts. - return LIB_ERR_NOT_IMPLEMENTED; + + struct capref frame; + err = slot_alloc(&frame); + if (err_is_fail(err)) { + return err_push(err, LIB_ERR_SLOT_ALLOC); + } + + err = slab_refill_no_pagefault(slabs, frame, bytes); + if (err_is_fail(err)) { + slot_free(frame); + return err; + } + + return SYS_ERR_OK; } @@ -202,7 +216,27 @@ errval_t slab_refill_no_pagefault(struct slab_allocator *slabs, struct capref fr { // Refill the slot allocator without causing a page fault // Hint: you can't just use malloc here... - return LIB_ERR_NOT_IMPLEMENTED; + errval_t err; + size_t alloc_bytes; + err = frame_create(frame, minbytes, &alloc_bytes); + if (err_is_fail(err)) return err_push(err, LIB_ERR_FRAME_ALLOC); + + struct paging_state *paging_state = get_current_paging_state(); + lvaddr_t vaddr = paging_state->next_vaddr; + paging_state->next_vaddr += alloc_bytes; + + err = paging_map_fixed_attr( + paging_state, vaddr, + frame, alloc_bytes, VREGION_FLAGS_READ_WRITE + ); + if (err_is_fail(err)) { + cap_delete(frame); + return err; + } + + slab_grow(slabs, (void *)vaddr, alloc_bytes); + + return SYS_ERR_OK; } /** diff --git a/lib/grading/grading.c b/lib/grading/grading.c index 2f78ea1..b965992 100644 --- a/lib/grading/grading.c +++ b/lib/grading/grading.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include @@ -20,8 +21,96 @@ void grading_setup_noninit(int *argc, char ***argv) { } +static void check_err(errval_t err) { + if (err_is_fail(err)) { + USER_PANIC_ERR(err, "Test failed."); + } +} + +static struct mm *testmm; + +static void *alloc_frame(size_t bytes, size_t alignment, + struct capref *ram_cap_ret, struct capref *frame_cap_ret) { + struct capref ram_cap; + if (ram_cap_ret == NULL) ram_cap_ret = &ram_cap; + struct capref frame_cap; + if (frame_cap_ret == NULL) frame_cap_ret = &frame_cap; + + assert(bytes == ROUND_UP(bytes, BASE_PAGE_SIZE)); + check_err(mm_alloc_aligned(testmm, bytes, alignment, ram_cap_ret)); + + check_err(slot_alloc(frame_cap_ret)); + check_err(cap_retype(*frame_cap_ret, *ram_cap_ret, 0, ObjType_Frame, bytes, 1)); + + struct paging_state *paging_state = get_current_paging_state(); + lvaddr_t vaddr = paging_state->next_vaddr; + paging_state->next_vaddr += bytes; + check_err(paging_map_fixed_attr( + paging_state, vaddr, + *frame_cap_ret, bytes, VREGION_FLAGS_READ_WRITE + )); + return (void *)vaddr; +} + void grading_test_mm(struct mm *test) { + errval_t err; + testmm = test; + + struct capref *caplist = alloc_frame(sizeof(struct capref) * 1024*1024, 16, NULL, NULL); + size_t alloc_count = 0; + + // Test partial free + struct capref big_block; + struct capref part1_of_big_block; + struct capref part2_of_big_block; + check_err(mm_alloc_aligned(testmm, 16*4096, 4096, &big_block)); + check_err(slot_alloc(&part1_of_big_block)); + check_err(slot_alloc(&part2_of_big_block)); + check_err(cap_retype(part1_of_big_block, big_block, 0, ObjType_RAM, 4096, 1)); + check_err(cap_retype(part2_of_big_block, big_block, 4096, ObjType_RAM, 15*4096, 1)); + check_err(cap_destroy(big_block)); + check_err(mm_free(testmm, part2_of_big_block)); + + // Allocate all available RAM and then deallocate it again, multiple times. + for (int it = 0; it < 5; it++) { + for (; alloc_count < 1024*1024; alloc_count++) { + err = mm_alloc_aligned(testmm, 256*BASE_PAGE_SIZE, BASE_PAGE_SIZE, &caplist[alloc_count]); + if (err_is_fail(err)) { + assert(err == MM_ERR_OUT_OF_RAM); + break; + } + if (alloc_count % 10000 == 0) debug_printf("TEST: allocated %lu\n", alloc_count); + } + debug_printf("TEST: Allocated %"PRIu64" MB of RAM.\n", alloc_count * 256*BASE_PAGE_SIZE / 1024 / 1024); + while (alloc_count > 0) { + alloc_count--; + check_err(mm_free(testmm, caplist[alloc_count])); + if (alloc_count % 1000 == 0) debug_printf("TEST: freeing %lu\n", alloc_count); + } + } + + // Test alignment ("e.g., a 4 KiB region must be aligned to a 1 MiB boundary.") + for (; alloc_count < 10; alloc_count++) { + check_err(mm_alloc_aligned(testmm, 4096, 1024*1024, &caplist[alloc_count])); + struct capability c; + check_err( cap_direct_identify(caplist[alloc_count], &c)); + genpaddr_t base = get_address(&c); + assert(base % (1024*1024) == 0); + } + while (alloc_count > 0) { + alloc_count--; + check_err(mm_free(testmm, caplist[alloc_count])); + } + + // Test paging + for (int i = 0; i < 40; i++) { + debug_printf("TEST: page %lu\n", i); + char *data = alloc_frame(5176*4096, 16, NULL, NULL); + memset(data, 33, 5176*4096); + } + + debug_printf("TEST: Finished!\n"); } void diff --git a/lib/mm/mm.c b/lib/mm/mm.c index 145ae6d..882ba7a 100644 --- a/lib/mm/mm.c +++ b/lib/mm/mm.c @@ -13,10 +13,27 @@ * ETH Zurich D-INFK, Haldeneggsteig 4, CH-8092 Zurich. Attn: Systems Group. */ +#include #include #include #include +#define BIT_INDEX(offset) (((offset) >> BASE_PAGE_BITS) & 7) +#define BYTE_INDEX(offset) (((offset) >> (BASE_PAGE_BITS + 3)) & (MM_BLOCK_SIZE - 1)) +#define LEAF_INDEX(offset) ((offset) >> (BASE_PAGE_BITS + 3 + MM_BLOCK_BITS)) + +#define MM_LEAF_COUNT 500 +#define MAX_REGION_SIZE (BASE_PAGE_SIZE * 8 * MM_BLOCK_SIZE * MM_LEAF_COUNT) + +typedef char mm_leaf_node_t[MM_BLOCK_SIZE]; +struct mm_root_node { + struct mm_root_node *next; + genpaddr_t base; + gensize_t size; + struct capref cap; + mm_leaf_node_t *leafs[MM_LEAF_COUNT]; +}; +STATIC_ASSERT(sizeof(struct mm_root_node) <= MM_BLOCK_SIZE, "struct mm_root_node too big"); errval_t mm_init(struct mm *mm, enum objtype objtype, @@ -25,7 +42,17 @@ errval_t mm_init(struct mm *mm, enum objtype objtype, slot_refill_t slot_refill_func, void *slot_alloc_inst) { - return LIB_ERR_NOT_IMPLEMENTED; + slab_init(&mm->slabs, MM_BLOCK_SIZE, slab_refill_func); + mm->slot_alloc = slot_alloc_func; + mm->slot_refill = slot_refill_func; + mm->slot_alloc_inst = slot_alloc_inst; + mm->objtype = objtype; + mm->head = NULL; + mm->tail = NULL; + mm->current = NULL; + mm->current_offset = 0; + mm->unallocated_leafs = 0; + return SYS_ERR_OK; } void mm_destroy(struct mm *mm) @@ -35,13 +62,162 @@ void mm_destroy(struct mm *mm) errval_t mm_add(struct mm *mm, struct capref cap) { - return LIB_ERR_NOT_IMPLEMENTED; + errval_t err; + struct capability c; + err = cap_direct_identify(cap, &c); + if (err_is_fail(err)) return err; + assert(c.type == mm->objtype); + genpaddr_t base = get_address(&c); + assert(base != 0); + gensize_t size = get_size(&c); + assert(size != 0); + + debug_printf("mm_add: adding region. base=%"PRIx64", size=%"PRIx64"\n", base, size); + + genpaddr_t base_rounded = ROUND_UP(base, BASE_PAGE_SIZE); + gensize_t size_rounded = ROUND_DOWN(size - (base_rounded - base), BASE_PAGE_SIZE); + + // Check for overflow of base or underflow of size + if (base_rounded < base || size_rounded > size || size_rounded < BASE_PAGE_SIZE * 20) { + debug_printf("WARNING: mm_add: region too small, skipping\n"); + return SYS_ERR_OK; + } + if (size_rounded > MAX_REGION_SIZE) { + debug_printf("WARNING: mm_add: region too big, truncating\n"); + size_rounded = MAX_REGION_SIZE; + } + + struct mm_root_node *root_node = slab_alloc(&mm->slabs); + if (root_node == NULL) { + return LIB_ERR_SLAB_ALLOC_FAIL; + } + root_node->base = base_rounded; + root_node->size = size_rounded; + root_node->cap = cap; + memset(root_node->leafs, 0, sizeof(root_node->leafs)); + root_node->next = NULL; + if (mm->head == NULL) { + mm->head = root_node; + mm->current = root_node; + } else { + mm->tail->next = root_node; + } + mm->tail = root_node; + mm->unallocated_leafs += LEAF_INDEX(size_rounded - 1) + 1; + + // Fill up the slab allocator + while (slab_freecount(&mm->slabs) < mm->unallocated_leafs + 2) { + err = slab_refill_pages(&mm->slabs, SLAB_STATIC_SIZE(15, MM_BLOCK_SIZE)); + if (err_is_fail(err)) { + return err; + } + } + + return SYS_ERR_OK; } errval_t mm_alloc_aligned(struct mm *mm, size_t size, size_t alignment, struct capref *retcap) { - return LIB_ERR_NOT_IMPLEMENTED; + errval_t err; + if ((alignment & (alignment - 1)) != 0) { + return MM_ERR_BAD_ALIGNMENT; + } + if (size > MAX_REGION_SIZE || alignment > MAX_REGION_SIZE) { + return MM_ERR_OUT_OF_RAM; + } + size = ROUND_UP(size, BASE_PAGE_SIZE); + if (size == 0) size = BASE_PAGE_SIZE; + + if (mm->head == NULL) { + return MM_ERR_OUT_OF_RAM; + } + + err = slot_alloc(retcap); + if (err_is_fail(err)) return err_push(err, LIB_ERR_SLOT_ALLOC); + + // Find a suitable range of free RAM + bool first_iteration = true; + struct mm_root_node *current = mm->current; + genpaddr_t alloc_base = current->base + mm->current_offset; + while (true) { + genpaddr_t current_base = current->base; + genpaddr_t current_end = current->base + current->size; + bool ok = false; + // Try to find a match starting at (current, current_offset) + while (true) { + alloc_base = ROUND_UP(alloc_base, alignment); + if (alloc_base + size > current_end) break; + + size_t offset = alloc_base - current_base; + size_t alloc_end = offset + size; + ok = true; + for (; ok && offset < alloc_end; offset += BASE_PAGE_SIZE) { + mm_leaf_node_t *leaf = current->leafs[LEAF_INDEX(offset)]; + if ( + leaf != NULL && + ((*leaf)[BYTE_INDEX(offset)] & (1 << BIT_INDEX(offset))) == 0 + ) { + ok = false; + } + } + if (ok) break; + alloc_base = current_base + offset; + } + + if (ok) break; + current = current->next; + if (current == NULL) current = mm->head; + if (current == mm->current && !first_iteration) { + err = slot_free(*retcap); + assert(!err_is_fail(err)); + return MM_ERR_OUT_OF_RAM; + } + first_iteration = false; + alloc_base = current->base; + } + + size_t alloc_offset = alloc_base - current->base; + size_t alloc_end = alloc_offset + size; + + // Allocate and initialize missing leaf nodes + for (size_t offset = ROUND_DOWN(alloc_offset, BASE_PAGE_SIZE * 8 * MM_BLOCK_SIZE); + offset < alloc_end; offset += BASE_PAGE_SIZE * 8 * MM_BLOCK_SIZE) { + mm_leaf_node_t **leaf_ptr = ¤t->leafs[LEAF_INDEX(offset)]; + if (*leaf_ptr == NULL) { + mm_leaf_node_t *leaf = slab_alloc(&mm->slabs); + assert(leaf != NULL); + *leaf_ptr = leaf; + mm->unallocated_leafs -= 1; + + memset(leaf, 0, sizeof(*leaf)); + size_t free_count = (current->size >> BASE_PAGE_BITS) - (LEAF_INDEX(offset) << (3 + MM_BLOCK_BITS)); + if (free_count > MM_BLOCK_SIZE * 8) free_count = MM_BLOCK_SIZE * 8; + size_t i = 0; + for (; i + 8 <= free_count; i += 8) (*leaf)[i >> 3] = 0xff; + for (; i < free_count; i++) (*leaf)[i >> 3] |= 1 << (i & 7); + } + } + + // Create a capability for the allocated memory + err = cap_retype(*retcap, current->cap, alloc_offset, mm->objtype, size, 1); + if (err_is_fail(err)) { + err = slot_free(*retcap); + assert(!err_is_fail(err)); + return err_push(err, LIB_ERR_CAP_RETYPE); + } + + mm->current = current; + mm->current_offset = alloc_offset + size; + + // Mark the allocated memory as used + for (size_t offset = alloc_offset; offset < alloc_end; offset += BASE_PAGE_SIZE) { + mm_leaf_node_t *leaf = current->leafs[LEAF_INDEX(offset)]; + assert(((*leaf)[BYTE_INDEX(offset)] & (1 << BIT_INDEX(offset))) != 0); + (*leaf)[BYTE_INDEX(offset)] &= ~(1 << BIT_INDEX(offset)); + } + + return SYS_ERR_OK; } errval_t mm_alloc(struct mm *mm, size_t size, struct capref *retcap) @@ -52,6 +228,41 @@ errval_t mm_alloc(struct mm *mm, size_t size, struct capref *retcap) errval_t mm_free(struct mm *mm, struct capref cap) { - return LIB_ERR_NOT_IMPLEMENTED; + errval_t err; + struct capability c; + err = cap_direct_identify(cap, &c); + if (err_is_fail(err)) return err; + assert(c.type == mm->objtype); + genpaddr_t base = get_address(&c); + assert(base != 0 && base % BASE_PAGE_SIZE == 0); + gensize_t size = get_size(&c); + assert(size != 0 && size % BASE_PAGE_SIZE == 0); + err = cap_destroy(cap); + if (err_is_fail(err)) { + return err_push(err, LIB_ERR_CAP_DESTROY); + } + + // Find the root node + struct mm_root_node *root = mm->head; + for (; root != NULL; root = root->next) { + if (base >= root->base && base + size <= root->base + root->size) { + break; + } + } + if (root == NULL) { + return MM_ERR_NOT_FOUND; + } + + // Mark the memory as free + size_t alloc_offset = base - root->base; + size_t alloc_end = alloc_offset + size; + for (size_t offset = alloc_offset; offset < alloc_end; offset += BASE_PAGE_SIZE) { + mm_leaf_node_t *leaf = root->leafs[LEAF_INDEX(offset)]; + assert(leaf != NULL); + assert(((*leaf)[BYTE_INDEX(offset)] & (1 << BIT_INDEX(offset))) == 0); + (*leaf)[BYTE_INDEX(offset)] |= 1 << BIT_INDEX(offset); + } + + return SYS_ERR_OK; } diff --git a/usr/init/mem_alloc.c b/usr/init/mem_alloc.c index 05eba11..cd8b01c 100644 --- a/usr/init/mem_alloc.c +++ b/usr/init/mem_alloc.c @@ -49,8 +49,8 @@ static inline errval_t initialize_ram_allocator(void) } // Give aos_mm a bit of memory for the initialization - // M1 TODO: grow be with some memory! - slab_grow(&aos_mm.slabs, NULL, 0); + static char init_slab_space[SLAB_STATIC_SIZE(2, MM_BLOCK_SIZE)]; + slab_grow(&aos_mm.slabs, init_slab_space, sizeof(init_slab_space)); return SYS_ERR_OK; } @@ -68,6 +68,14 @@ errval_t initialize_ram_alloc(void) return err; } + // Initialize the generic RAM allocator to use our local allocator. + // We do this here, because mm_add allocates memory for the slab allocator, + // and that uses ram_alloc. + err = ram_alloc_set(aos_ram_alloc_aligned); + if (err_is_fail(err)) { + return err_push(err, LIB_ERR_RAM_ALLOC_SET); + } + // Walk bootinfo and add all RAM caps to allocator handed to us by the kernel uint64_t mem_avail = 0; struct capref mem_cap = { @@ -101,15 +109,8 @@ errval_t initialize_ram_alloc(void) } debug_printf("Added %"PRIu64" MB of physical memory.\n", mem_avail / 1024 / 1024); - // Finally, we can initialize the generic RAM allocator to use our local allocator - err = ram_alloc_set(aos_ram_alloc_aligned); - if (err_is_fail(err)) { - return err_push(err, LIB_ERR_RAM_ALLOC_SET); - } - // Grading grading_test_mm(&aos_mm); return SYS_ERR_OK; } -