BOOT_CODE static bool_t create_untypeds_for_region( cap_t root_cnode_cap, region_t reg, slot_pos_t first_untyped_slot ) { uint32_t align_bits; uint32_t size_bits; while (!is_reg_empty(reg)) { /* Determine the maximum size of the region */ size_bits = WORD_BITS - 1 - boot_clz(reg.end - reg.start); /* Determine the alignment of the region */ align_bits = boot_ctz(reg.start); /* Reduce size bits to align if needed */ if (align_bits < size_bits) { size_bits = align_bits; } assert(size_bits >= WORD_BITS / 8); if (!provide_untyped_cap(root_cnode_cap, reg.start, size_bits, first_untyped_slot)) { return false; } reg.start += BIT(size_bits); } return true; }
BOOT_CODE bool_t insert_region(region_t reg) { word_t i; assert(reg.start <= reg.end); if (is_reg_empty(reg)) { return true; } for (i = 0; i < MAX_NUM_FREEMEM_REG; i++) { if (is_reg_empty(ndks_boot.freemem[i])) { ndks_boot.freemem[i] = reg; return true; } } return false; }
BOOT_CODE bool_t create_untypeds_for_region( cap_t root_cnode_cap, bool_t deviceMemory, region_t reg, slot_pos_t first_untyped_slot ) { word_t align_bits; word_t size_bits; while (!is_reg_empty(reg)) { /* Due to a limitation on the mdb we cannot give out an untyped to the * the start of the kernel region. The reason for this is that cte pointers * in mdb nodes are stored with the high bits masked out. To recreate a cte pointer * we then need to put the high bits back in after reading it out. HOWEVER, we * still need a way to store and identify a NULL pointer. This means reserving * the bottom address as the 'null' address so that no one creates an cnode * there resulting in a 'null' (yet valid) cte */ if (!deviceMemory && reg.start == kernelBase) { reg.start += BIT(PAGE_BITS); } /* Determine the maximum size of the region */ size_bits = WORD_BITS - 1 - boot_clzl(reg.end - reg.start); /* Determine the alignment of the region */ align_bits = boot_ctzl(reg.start); /* Reduce size bits to align if needed */ if (align_bits < size_bits) { size_bits = align_bits; } assert(size_bits >= WORD_BITS / 8); if (!provide_untyped_cap(root_cnode_cap, deviceMemory, reg.start, size_bits, first_untyped_slot)) { return false; } reg.start += BIT(size_bits); } return true; }
BOOT_CODE pptr_t alloc_region(word_t size_bits) { word_t i; word_t reg_index = 0; /* gcc cannot work out that this will not be used uninitialized */ region_t reg = REG_EMPTY; region_t rem_small = REG_EMPTY; region_t rem_large = REG_EMPTY; region_t new_reg; region_t new_rem_small; region_t new_rem_large; /* Search for a freemem region that will be the best fit for an allocation. We favour allocations * that are aligned to either end of the region. If an allocation must split a region we favour * an unbalanced split. In both cases we attempt to use the smallest region possible. In general * this means we aim to make the size of the smallest remaining region smaller (ideally zero) * followed by making the size of the largest remaining region smaller */ for (i = 0; i < MAX_NUM_FREEMEM_REG; i++) { /* Determine whether placing the region at the start or the end will create a bigger left over region */ if (ROUND_UP(ndks_boot.freemem[i].start, size_bits) - ndks_boot.freemem[i].start < ndks_boot.freemem[i].end - ROUND_DOWN(ndks_boot.freemem[i].end, size_bits)) { new_reg.start = ROUND_UP(ndks_boot.freemem[i].start, size_bits); new_reg.end = new_reg.start + BIT(size_bits); } else { new_reg.end = ROUND_DOWN(ndks_boot.freemem[i].end, size_bits); new_reg.start = new_reg.end - BIT(size_bits); } if (new_reg.end > new_reg.start && new_reg.start >= ndks_boot.freemem[i].start && new_reg.end <= ndks_boot.freemem[i].end) { if (new_reg.start - ndks_boot.freemem[i].start < ndks_boot.freemem[i].end - new_reg.end) { new_rem_small.start = ndks_boot.freemem[i].start; new_rem_small.end = new_reg.start; new_rem_large.start = new_reg.end; new_rem_large.end = ndks_boot.freemem[i].end; } else { new_rem_large.start = ndks_boot.freemem[i].start; new_rem_large.end = new_reg.start; new_rem_small.start = new_reg.end; new_rem_small.end = ndks_boot.freemem[i].end; } if ( is_reg_empty(reg) || (reg_size(new_rem_small) < reg_size(rem_small)) || (reg_size(new_rem_small) == reg_size(rem_small) && reg_size(new_rem_large) < reg_size(rem_large)) ) { reg = new_reg; rem_small = new_rem_small; rem_large = new_rem_large; reg_index = i; } } } if (is_reg_empty(reg)) { printf("Kernel init failing: not enough memory\n"); return 0; } /* Remove the region in question */ ndks_boot.freemem[reg_index] = REG_EMPTY; /* Add the remaining regions in largest to smallest order */ insert_region(rem_large); if (!insert_region(rem_small)) { printf("alloc_region(): wasted 0x%lx bytes due to alignment, try to increase MAX_NUM_FREEMEM_REG\n", (word_t)(rem_small.end - rem_small.start)); } return reg.start; }