diff --git a/kernel/include/mm/memory_alloc.h b/kernel/include/mm/memory_alloc.h new file mode 100644 index 0000000..818b9fc --- /dev/null +++ b/kernel/include/mm/memory_alloc.h @@ -0,0 +1,42 @@ +#ifndef MEMORY_ALLOC_H_ZPNDLSE1 +#define MEMORY_ALLOC_H_ZPNDLSE1 + +#include + +/** + * Allocate memory of given size and return its starting address. + * + * @param size_t size of the memory to allocate + * @return void * address of the memory. NULL if allocation failed + */ +void *malloc(size_t); + +/** + * Reallocate a piece of memory with the new size. + * + * @param void * address of memory to reallocate + * @param size_t size of the new memory + * @return void * address of the newly reallocated memory. NULL if + * reallocation failed. + */ +void *realloc(void *, size_t); + +/** + * Allocates memory for the given number of array elements of the given array + * size. The memory is set to 0. + * + * @param size_t size of the array to allocate memory for + * @param size_t size of each array element + * @return void * address of the newly allocated memory. NULL if + * allocation failed. + */ +void *calloc(size_t, size_t); + +/** + * Free a piece of allocated memory. + * + * @param void * address of the allocated memory + */ +void free(void *); + +#endif /* end of include guard: MEMORY_ALLOC_H_ZPNDLSE1 */ diff --git a/kernel/kernel.c b/kernel/kernel.c index a90a527..885239f 100644 --- a/kernel/kernel.c +++ b/kernel/kernel.c @@ -1,6 +1,7 @@ #include #include #include +#include #include #include #include @@ -62,19 +63,37 @@ extern void kmain(multiboot_info_t *multiboot_info, uint32_t multiboot_magic) uintptr_t * addr1 = (void *)0x00400000; uintptr_t * addr2 = addr1 + 1024; + /* map both addresses to same physical memory page */ paging_map_page(page, addr1, 0x02); paging_map_page(page, addr2, 0x02); + /* update the values at both addresses */ *addr1 = 0xcafebabe; *addr2 = 0xdeadbeef; + + /* the second update should overwrite the first since the physical address + * is same */ kcheck(*addr1 == *addr2, "paging enabled"); + /* cleanup */ paging_unmap_page(page, addr1); paging_unmap_page(page, addr2); page_free(page); } #endif +#ifdef DEBUG + { /* sanity check for malloc */ + void *mem1 = malloc(10); + void *mem2 = malloc(10); + + kcheck(mem1 != mem2, "check malloc"); + + free(mem1); + free(mem2); + } +#endif + klog(LOG_INFO, "\nInitialization complete.\n"); kprintf("\n$ "); diff --git a/kernel/mm/memory_alloc.c b/kernel/mm/memory_alloc.c new file mode 100644 index 0000000..17c69db --- /dev/null +++ b/kernel/mm/memory_alloc.c @@ -0,0 +1,194 @@ + +#include +#include +#include +#include + +#define PAGE_SIZE 512 /* 4kb = 512 bytes */ + +typedef struct _block_t +{ + struct _block_t *prev; + struct _block_t *next; + size_t size; +} __attribute__((packed)) block_t; + +typedef struct _block_info_t +{ + size_t size; +} __attribute__((packed)) block_info_t; + +/** + * Store the list of free blocks + */ +block_t *free_mem_list = NULL; + +/** + * Allocate a new page, wrap it in a block and return it + * + * @return block_t * A new free block + */ +static inline block_t *allocate_new_block() +{ + struct page *page = page_alloc(); + block_t * block = page->address; + + block->prev = NULL; + block->next = NULL; + block->size = PAGE_SIZE; + + return block; +} + +/** + * Find a block that can fix the given size request. If a block does not + * already exist, it is created. + * + * @param size_t The requested size in bytes + * @return block_t * The block which can fit this size + */ +static inline block_t *find_block(size_t size) +{ + /* requested size should always be less than block size (for now) */ + assert(size <= PAGE_SIZE); + + /* if we don't have any blocks left, get a new block and return it */ + if (free_mem_list == NULL) { + free_mem_list = allocate_new_block(); + + return free_mem_list; + } + + block_t *block = free_mem_list; + int found = 0; + + while (1) { + if (block->size >= size) { + found = 1; + break; + } + + /* ensure that reference to block is always valid */ + if (block->next == NULL) { + break; + } + + block = block->next; + } + + if (!found) { + /* + * this block would always be the last block in the list. just update the + * `next` for it + */ + block->next = allocate_new_block(); + block->next->prev = block; + + /* set the new block as the current block */ + block = block->next; + } + + /* this block would definitely be bigger than or equal to the requested size + * (see `assert` at the start) */ + return block; +} + +/** + * Free a block and add it to the free_mem_list. + * + * @param block_info_t * The block info + */ +static inline void free_block(block_info_t *info) +{ + size_t size = info->size; + block_t *block = (void *)info; + + /* add the block to the head of the free_mem_list */ + + /* add the size of the block info also to the size of the block */ + block->size = size + sizeof(block_info_t); + block->prev = NULL; + block->next = free_mem_list; + + if (block->next) { + block->next->prev = block; + } + + free_mem_list = block; +} + +/** + * Allocate a block of memory from free_mem_list. The allocated block does not + * include the block info, neither does it reserve space for it. The called + * must include the size of block info before calling this function. + * + * @param size_t The size of the block to allocate + * @return void * The address of the allocated block + */ +static void *malloc_raw(size_t size) +{ + block_t *block = find_block(size); + + /* calculate the remaining size of the block after allocating the given sized + * block */ + size_t remaining = block->size - size; + + if (!remaining) { + /* remove this node from the list */ + + if (block->prev) { + block->prev->next = block->next; + } + + if (block->next) { + block->next->prev = block->prev; + } + + /* return the address of this block */ + return block; + } + + block->size = remaining; + + /* return the part at the end of this block */ + return (void *)block + remaining; +} + +void *malloc(size_t size) +{ + /* allocate memory for the requested sized block + info about the block */ + block_info_t *alloc = malloc_raw(size + sizeof(block_info_t)); + + /* store the info for the block */ + alloc->size = size; + + /* return the block after the info */ + return (void *)alloc + sizeof(block_info_t); +} + +void *realloc(void *block, size_t size) +{ + free(block); + + return malloc(size); +} + +void *calloc(size_t num_elements, size_t element_size) +{ + size_t size = num_elements * element_size; + + /* allocate memory for all the array elements */ + void *mem = malloc(size); + + /* set the memory to 0 */ + memset(mem, 0, size); + + return mem; +} + +void free(void *block) +{ + block_info_t *info = block - sizeof(block_info_t); + + free_block(info); +} diff --git a/kernel/mm/page_alloc.c b/kernel/mm/page_alloc.c index 5051a91..ca2b0db 100644 --- a/kernel/mm/page_alloc.c +++ b/kernel/mm/page_alloc.c @@ -31,6 +31,8 @@ void page_alloc_init() size_t pages_array_page_count = page_address_to_index(pages_array_mem_size) + 1; + /* we have some unused memory at this point but we'll ignore it for now */ + /* allocate the pages for the array */ pages = page_frame_n_alloc(pages_array_page_count); @@ -44,7 +46,7 @@ struct page *page_n_alloc(size_t count) for (size_t i = 0; i < count; i++) { pages[page_index + i].address = page_address; - page_address += 0x1000; + page_address += 0x1000; /* add 4kb to the page address (0x1000 = 4096) */ } return &pages[page_index]; diff --git a/kernel/mm/page_frame.c b/kernel/mm/page_frame.c index 775429d..c28e1df 100644 --- a/kernel/mm/page_frame.c +++ b/kernel/mm/page_frame.c @@ -35,7 +35,7 @@ static uint32_t frames_bitmap_size; static uint32_t frames_bitmap_num_pages; /* size of each frame that is allocated in bytes */ -#define FRAME_SIZE 4096 /* 4KB */ +#define FRAME_SIZE 4096 /* 4Kb */ #define FRAME_SIZE_BYTES 512 /* 4096/8 */ #define BLOCK_SIZE 1024 #define BLOCKS_PER_FRAME 4 /* each block is 1KB */