diff --git a/src/kernel/memory_management/include/physical_memory/manager.h b/src/kernel/memory_management/include/physical_memory/manager.h index a13aed34..7ef7abac 100644 --- a/src/kernel/memory_management/include/physical_memory/manager.h +++ b/src/kernel/memory_management/include/physical_memory/manager.h @@ -54,22 +54,24 @@ static inline memory_region_t phys_mem_reg_to_reg(physical_memory_region_t pmr) /// @ingroup kmm /// @ingroup pmm typedef enum : u8 { - FRAME_SIZE_4KiB = 0, - FRAME_SIZE_8KiB = 1, - FRAME_SIZE_16KiB = 2, - FRAME_SIZE_32KiB = 3, - FRAME_SIZE_64KiB = 4, - FRAME_SIZE_128KiB = 5, - FRAME_SIZE_256KiB = 6, - FRAME_SIZE_512KiB = 7, - FRAME_SIZE_1MiB = 8, - FRAME_SIZE_2MiB = 9, - FRAME_SIZE_1GiB = 18, -} frame_size_t; // NOTE: value of frame_size_t is (4KiB << frame_size) + FRAME_ORDER_4KiB = 0, + FRAME_ORDER_8KiB = 1, + FRAME_ORDER_16KiB = 2, + FRAME_ORDER_32KiB = 3, + FRAME_ORDER_64KiB = 4, + FRAME_ORDER_128KiB = 5, + FRAME_ORDER_256KiB = 6, + FRAME_ORDER_512KiB = 7, + FRAME_ORDER_1MiB = 8, + FRAME_ORDER_2MiB = 9, + FRAME_ORDER_1GiB = 18, +} frame_order_t; // NOTE: value is the order of 4KiB frames (frame count = 1 << order) + +#define PAGE_SIZE 0x1000UL /// @ingroup kmm /// @ingroup pmm -u64 phys_mem_get_frame_size_in_bytes(frame_size_t fs); +u64 phys_mem_get_frame_size_in_bytes(frame_order_t fs); /** * @ingroup kmm @@ -97,7 +99,7 @@ error_t phys_mem_init(const physical_memory_region_t* pmrs, size_t pmr_count, co * @retval ERR_OUT_OF_MEMORY The block of specified size was not able to be allocated * */ [[gnu::nonnull]] -error_t phys_mem_alloc_frame(frame_size_t frame_size, phys_addr_t* addrOUT); +error_t phys_mem_alloc_frame(frame_order_t frame_size, phys_addr_t* addrOUT); /** * @ingroup kmm @@ -105,6 +107,6 @@ error_t phys_mem_alloc_frame(frame_size_t frame_size, phys_addr_t* addrOUT); * @retval ERR_NONE * @retval ERR_NOT_VALID The address being freed was reported as not allocated * */ -error_t phys_mem_free_frame(phys_addr_t addr); +error_t phys_mem_free_frame(frame_order_t frame_size, phys_addr_t addr); #endif //! BIGOS_KERNEL_MEMORY_MANAGMENT_PHYSICAL_MEMORY_MANAGMENT diff --git a/src/kernel/memory_management/physical_memory/allocator.c b/src/kernel/memory_management/physical_memory/allocator.c index 0c8bc47c..3ffec2e7 100644 --- a/src/kernel/memory_management/physical_memory/allocator.c +++ b/src/kernel/memory_management/physical_memory/allocator.c @@ -2,34 +2,151 @@ #include "memory_management/include/physical_memory/manager.h" #include "stdbigos/error.h" +#include "stdbigos/math.h" +#include "stdbigos/string.h" -error_t pmallocator_get_header(memory_area_t area, const memory_area_t* reserved_areas, u32 count, +typedef struct { + uintptr_t area_base_addr; + size_t area_size; + u64 bitmap[]; +} pmallocator_header_t; + +typedef struct { + size_t first_bit; + size_t bit_count; +} bitmap_range_t; + +static void bitmap_set(u64* bitmap, size_t bit) { + bitmap[bit / 64] |= (1ULL << (bit % 64)); +} + +static void bitmap_clear(u64* bitmap, size_t bit) { + bitmap[bit / 64] &= ~(1ULL << (bit % 64)); +} + +static bool bitmap_test(const u64* bitmap, size_t bit) { + return bitmap[bit / 64] & (1ULL << (bit % 64)); +} + +static void bitmap_set_range(u64* bitmap, size_t start, size_t count) { + for (size_t j = start; j < start + count; ++j) { + bitmap_set(bitmap, j); + } +} + +static size_t calculate_header_size(memory_area_t area) { + const size_t bitmap_bits = area.size / PAGE_SIZE; + const size_t bitmap_bytes = ALIGN_UP(bitmap_bits, 64) / 8; + const size_t total = sizeof(pmallocator_header_t) + bitmap_bytes; + return ALIGN_UP(total, PAGE_SIZE); +} + +static bitmap_range_t addr_range_to_bitmap_range(uintptr_t range_addr, size_t range_size, uintptr_t base_addr) { + const uintptr_t aligned_start = ALIGN_DOWN(range_addr, PAGE_SIZE); + const uintptr_t aligned_end = ALIGN_UP(range_addr + range_size, PAGE_SIZE); + + bitmap_range_t result = { + .first_bit = (aligned_start - base_addr) / PAGE_SIZE, + .bit_count = (aligned_end - aligned_start) / PAGE_SIZE, + }; + return result; +} + +error_t pmallocator_get_header(memory_area_t area, const memory_area_t* reserved_areas, u32 reserved_areas_count, memory_area_t* headerOUT) { - (void)area; - (void)reserved_areas; - (void)count; - (void)headerOUT; - return ERR_NOT_IMPLEMENTED; + const size_t header_size = calculate_header_size(area); + + for (uintptr_t i = area.addr; i + header_size <= area.addr + area.size; i += PAGE_SIZE) { + memory_area_t potential_header = { + .addr = i, + .size = header_size, + }; + bool overlaps_reserved = false; + for (u32 j = 0; j < reserved_areas_count; ++j) { + if (do_memory_areas_overlap(potential_header, reserved_areas[j])) { + overlaps_reserved = true; + break; + } + } + if (!overlaps_reserved) { + *headerOUT = potential_header; + return ERR_NONE; + } + } + + return ERR_NOT_ENOUGH_MEMORY; } -error_t pmallocator_init_region(memory_area_t area, memory_region_t header, const memory_area_t* reserved_areas, - u32 count) { - (void)area; - (void)header; - (void)reserved_areas; - (void)count; - return ERR_NOT_IMPLEMENTED; +error_t pmallocator_init_region(memory_area_t area, memory_region_t header_region, const memory_area_t* reserved_areas, + u32 reserved_areas_count) { + pmallocator_header_t* header = header_region.addr; + header->area_size = area.size; + header->area_base_addr = area.addr; + + const size_t header_size = calculate_header_size(area); + const size_t bitmap_size = header_size - sizeof(pmallocator_header_t); + + memset(header->bitmap, 0, bitmap_size); + + for (u32 i = 0; i < reserved_areas_count; ++i) { + memory_area_t reserved_area = reserved_areas[i]; + bitmap_range_t bitmap_range = addr_range_to_bitmap_range(reserved_area.addr, reserved_area.size, area.addr); + + bitmap_set_range(header->bitmap, bitmap_range.first_bit, bitmap_range.bit_count); + } + + bitmap_range_t bitmap_range = + addr_range_to_bitmap_range((uintptr_t)header_region.addr, header_region.size, area.addr); + bitmap_set_range(header->bitmap, bitmap_range.first_bit, bitmap_range.bit_count); + + return ERR_NONE; } -error_t pmallocator_allocate(u8 frame_order, memory_region_t header, phys_addr_t* addrOUT) { - (void)frame_order; - *addrOUT = *addrOUT; - (void)header; - return ERR_NOT_IMPLEMENTED; +error_t pmallocator_allocate(frame_order_t frame_order, memory_region_t header_region, phys_addr_t* addrOUT) { + pmallocator_header_t* header = header_region.addr; + + const size_t bitmap_bits = header->area_size / PAGE_SIZE; + const size_t frame_count = 1ULL << frame_order; + + for (size_t i = 0; i + frame_count <= bitmap_bits; i += frame_count) { + bool all_free = true; + + for (size_t j = i; j < i + frame_count; ++j) { + if (bitmap_test(header->bitmap, j)) { + all_free = false; + break; + } + } + + if (all_free) { + bitmap_set_range(header->bitmap, i, frame_count); + *addrOUT = (phys_addr_t)(header->area_base_addr + (i * PAGE_SIZE)); + return ERR_NONE; + } + } + + return ERR_NOT_ENOUGH_MEMORY; } -error_t pmallocator_free(phys_addr_t addr, memory_region_t header) { - (void)addr; - (void)header; - return ERR_NOT_IMPLEMENTED; +error_t pmallocator_free(frame_order_t frame_order, phys_addr_t addr, memory_region_t header_region) { + pmallocator_header_t* header = header_region.addr; + + const size_t frame_count = 1ULL << frame_order; + const uintptr_t phys_addr = (uintptr_t)addr; + const size_t addr_bit = (phys_addr - header->area_base_addr) / PAGE_SIZE; + const size_t total_pages = header->area_size / PAGE_SIZE; + + if (phys_addr < header->area_base_addr || addr_bit + frame_count > total_pages) + return ERR_OUT_OF_BOUNDS; + + for (size_t j = addr_bit; j < addr_bit + frame_count; ++j) { + if (!bitmap_test(header->bitmap, j)) + return ERR_NOT_VALID; + } + + for (size_t j = addr_bit; j < addr_bit + frame_count; ++j) { + bitmap_clear(header->bitmap, j); + } + + return ERR_NONE; } diff --git a/src/kernel/memory_management/physical_memory/allocator.h b/src/kernel/memory_management/physical_memory/allocator.h index c4fc113b..04f3f0a0 100644 --- a/src/kernel/memory_management/physical_memory/allocator.h +++ b/src/kernel/memory_management/physical_memory/allocator.h @@ -12,69 +12,71 @@ * @ingroup kmm * @ingroup palloc * - * @brief Looks for an area of suitable size and alignmnet to store all metadata about the @p area that the allocator + * @brief Looks for an area of suitable size and alignment to store all metadata about the @p area that the allocator * needs * - * @param area The phiscal memory area to allocate from. Will be aligned to at least 4KiB boundry + * @param area The physical memory area to allocate from. Will be aligned to at least 4KiB boundary * @param reserved_areas An array of reserved areas from which allocations are not allowed - * @param count The count of reserved areas + * @param reserved_areas_count The count of reserved areas * * @retval ERR_NONE Success - * @retval ERR_BAD_ARG if @p reserved_areas is null and @p count is non zero or vice versa. + * @retval ERR_BAD_ARG if @p reserved_areas is null and @p reserved_areas_count is non zero or vice versa. * */ [[gnu::nonnull(4)]] -error_t pmallocator_get_header(memory_area_t area, const memory_area_t* reserved_areas, u32 count, +error_t pmallocator_get_header(memory_area_t area, const memory_area_t* reserved_areas, u32 reserved_areas_count, memory_area_t* headerOUT); /** * @ingroup kmm * @ingroup palloc * - * @param area The phiscal memory area to allocate from. Will be aligned to at least 4KiB boundry - * @param header A memory region of size at least `pmallocator_get_header_size(@p area)` aligned to 4KiB boundry + * @param area The physical memory area to allocate from. Will be aligned to at least 4KiB boundary + * @param header_region A memory region of size at least `pmallocator_get_header_size(@p area)` aligned to 4KiB + * boundary * @param reserved_areas An array of reserved areas from which allocations are not allowed - * @param count The count of reserved areas + * @param reserved_areas_count The count of reserved areas * * @retval ERR_NONE Success - * @retval ERR_BAD_ARG if @p reserved_areas is null and @p count is non zero or vice versa. + * @retval ERR_BAD_ARG if @p reserved_areas is null and @p reserved_areas_count is non zero or vice versa. * * @note The header region overlaps with area, it must be marked and not be allocated from. * area. * @note All pointers will break upon change of address space, because this is initialized before and will be used * after the change, no pointers can be stored inside the `header` region. * */ -error_t pmallocator_init_region(memory_area_t area, memory_region_t header, const memory_area_t* reserved_areas, - u32 count); +error_t pmallocator_init_region(memory_area_t area, memory_region_t header_region, const memory_area_t* reserved_areas, + u32 reserved_areas_count); /** * @ingroup kmm * @ingroup palloc * - * @param frame_order The frame size gives as `(1 << frame_order)` - * @param addrOUT Pointer the the variable to which the return address will be written to. - * The return address will be aligned to the frame size - * @param header + * @param frame_order The number of 4KiB frames to allocate is `(1 << frame_order)` + * @param header_region + * @param addrOUT Pointer to the variable to which the return address will be written to. + * The return address will be aligned to the allocation size * * @retval ERR_NONE Success - * @retval ERR_NOT_VALID The requested frame size is not supported by the allocator - * @retval ERR_PHYSICAL_MEMORY_FULL Not enough memory in the area represented by @p header to allocate a frame of + * @retval ERR_NOT_VALID The requested frame order is not supported by the allocator + * @retval ERR_NOT_ENOUGH_MEMORY Not enough memory in the area represented by @p header_region to allocate a frame of * desired size * - * @note 4KiB frame (or @p frame_order = 12) must be a valid frame size. + * @note FRAME_ORDER_4KiB (order 0) must be a valid frame order. * */ [[gnu::nonnull]] -error_t pmallocator_allocate(u8 frame_order, memory_region_t header, phys_addr_t* addrOUT); +error_t pmallocator_allocate(frame_order_t frame_order, memory_region_t header_region, phys_addr_t* addrOUT); /** * @ingroup kmm * @ingroup palloc * + * @param frame_order * @param addr - * @param header + * @param header_region * * @retval ERR_NONE Success * @retval ERR_NOT_VALID The area represented by @p addr is already free. * */ -error_t pmallocator_free(phys_addr_t addr, memory_region_t header); +error_t pmallocator_free(frame_order_t frame_order, phys_addr_t addr, memory_region_t header_region); #endif diff --git a/src/kernel/memory_management/physical_memory/manager.c b/src/kernel/memory_management/physical_memory/manager.c index af99855e..8ca8037e 100644 --- a/src/kernel/memory_management/physical_memory/manager.c +++ b/src/kernel/memory_management/physical_memory/manager.c @@ -52,9 +52,9 @@ static error_t store_header(physical_memory_region_t header) { } else { physical_memory_region_t new_reg = { .addr = nullptr, - .size = phys_mem_get_frame_size_in_bytes(FRAME_SIZE_4KiB), + .size = PAGE_SIZE, }; - error_t err = phys_mem_alloc_frame(FRAME_SIZE_4KiB, &new_reg.addr); + error_t err = phys_mem_alloc_frame(FRAME_ORDER_4KiB, &new_reg.addr); if (err) return err; err = init_header_storage_node(new_reg); @@ -73,7 +73,7 @@ static error_t store_header(physical_memory_region_t header) { // Public // ========================================== -u64 phys_mem_get_frame_size_in_bytes(frame_size_t fs) { +u64 phys_mem_get_frame_size_in_bytes(frame_order_t fs) { const u64 size_4KiB = 0x1000; return (size_4KiB << fs); } @@ -113,10 +113,10 @@ error_t phys_mem_init(const physical_memory_region_t* pmrs, size_t pmr_count, co } if (g_root_header_storage_node == nullptr) { physical_memory_region_t new_reg = { - .size = phys_mem_get_frame_size_in_bytes(FRAME_SIZE_4KiB), + .size = PAGE_SIZE, .addr = nullptr, }; - err = pmallocator_allocate(12, header_region, &new_reg.addr); + err = pmallocator_allocate(FRAME_ORDER_4KiB, header_region, &new_reg.addr); // NOTE: Since we failed to allocate the smallest possible frame size, we assume that this region is // useless. if (err) { @@ -149,13 +149,13 @@ error_t phys_mem_init(const physical_memory_region_t* pmrs, size_t pmr_count, co } // NOLINTEND readability-function-cognitive-complexity -error_t phys_mem_alloc_frame(frame_size_t frame_size, phys_addr_t* addrOUT) { +error_t phys_mem_alloc_frame(frame_order_t frame_size, phys_addr_t* addrOUT) { u32 idx = 0; physical_memory_region_t header_pmr; while (get_header_pmr(idx, &header_pmr) == ERR_NONE) { memory_region_t header_region = phys_mem_reg_to_reg(header_pmr); phys_addr_t frame_data = nullptr; - error_t err = pmallocator_allocate(frame_size + 12, header_region, &frame_data); + error_t err = pmallocator_allocate(frame_size, header_region, &frame_data); if (err) { ++idx; continue; @@ -169,12 +169,12 @@ error_t phys_mem_alloc_frame(frame_size_t frame_size, phys_addr_t* addrOUT) { return ERR_OUT_OF_MEMORY; } -error_t phys_mem_free_frame(phys_addr_t addr) { +error_t phys_mem_free_frame(frame_order_t frame_size, phys_addr_t addr) { u32 idx = 0; physical_memory_region_t header_pmr; while (get_header_pmr(idx, &header_pmr) == ERR_NONE) { memory_region_t header_region = phys_mem_reg_to_reg(header_pmr); - error_t err = pmallocator_free(addr, header_region); + error_t err = pmallocator_free(frame_size, addr, header_region); if (err) { ++idx; continue;