Implement Milestone 1
This commit is contained in:
parent
da5181799d
commit
10138df397
@ -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",
|
||||
|
||||
@ -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;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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,
|
||||
|
||||
147
lib/aos/paging.c
147
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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -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
|
||||
|
||||
219
lib/mm/mm.c
219
lib/mm/mm.c
@ -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 = ¤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;
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user