/** * \file * \brief A library for managing physical memory (i.e., caps) */ /* * Copyright (c) 2008, 2011, ETH Zurich. * Copyright (c), 2022, The University of British Columbia * All rights reserved. * * This file is distributed under the terms in the attached LICENSE file. * If you do not find this file, copies can be found by writing to: * 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"); static size_t number_of_bytes_allocated = 0; errval_t mm_init(struct mm *mm, enum objtype objtype, slab_refill_func_t slab_refill_func, slot_alloc_t slot_alloc_func, slot_refill_t slot_refill_func, void *slot_alloc_inst) { 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) { assert(!"NYI"); } errval_t mm_add(struct mm *mm, struct capref cap) { 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) { errval_t err; //TODO afeer: remove this after evaluation number_of_bytes_allocated += size; // debug_printf("[mm] number_of_bytes_allocated: %x\n", number_of_bytes_allocated); 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);/* condition */ 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) { return mm_alloc_aligned(mm, size, BASE_PAGE_SIZE, retcap); } errval_t mm_free(struct mm *mm, struct capref cap) { errval_t err; struct capability c; err = cap_direct_identify(cap, &c); if (err_is_fail(err)) return err; 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; }