Implement Milestone 1

This commit is contained in:
Jan Schär 2022-03-17 17:29:59 +01:00
parent da5181799d
commit 10138df397
9 changed files with 518 additions and 29 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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;
}
/**

View File

@ -4,6 +4,7 @@
#include <aos/capabilities.h>
#include <aos/ram_alloc.h>
#include <aos/aos_rpc.h>
#include <mm/mm.h>
#include <grading.h>
#include <spawn/spawn.h>
@ -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

View File

@ -13,10 +13,27 @@
* ETH Zurich D-INFK, Haldeneggsteig 4, CH-8092 Zurich. Attn: Systems Group.
*/
#include <string.h>
#include <mm/mm.h>
#include <aos/debug.h>
#include <aos/solution.h>
#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 = &current->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;
}

View File

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