163 lines
		
	
	
		
			4.3 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			163 lines
		
	
	
		
			4.3 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| #include "alloc.h"
 | |
| #include "debug.h"
 | |
| #include "kprintf.h"
 | |
| #include "terminal.h"
 | |
| #include <stdint.h>
 | |
| 
 | |
| /*
 | |
|  * Find first available (not in_use) block
 | |
|  * in a zone matching the size we need
 | |
|  */
 | |
| static Block *find_block(Zone *head, uint32_t size)
 | |
| {
 | |
| 	for (Zone *zone_it = head; zone_it != NULL; zone_it = zone_it->next) {
 | |
| 		for (Block *block_it = zone_it->free; block_it != NULL;
 | |
| 		     block_it = block_it->next_free) {
 | |
| 			assert(block_it);
 | |
| 			assert(!block_it->in_use);
 | |
| 			if (size <= block_it->size) {
 | |
| 				assert(block_it->zone == zone_it);
 | |
| 				return (block_it);
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 	return (NULL);
 | |
| }
 | |
| 
 | |
| // PARTIALLY DEPRECATED
 | |
| /*
 | |
|  * This will split the newly allocated block to use
 | |
|  * the remaining bytes for a new block
 | |
|  * This is our linked list of blocks
 | |
|  * ... -> [5] -> [6] -> ...
 | |
|  * After the allocation, this will become
 | |
|  * ... -> [5] -> [new] -> [6] -> ...
 | |
|  *
 | |
|  * For an example of [5].size = 32 and requiring a kmalloc of 10
 | |
|  * Let's say the metadata takes a size of 2:
 | |
|  * ... -> [metadata][data][remaining size] -> [6]
 | |
|  *            ^        ^         ^
 | |
|  *            2       10        20
 | |
|  *
 | |
|  * So now our block [new] will become:
 | |
|  * [5] -> [metadata][available data] -> [6]
 | |
|  *             ^           ^
 | |
|  *             2          18
 | |
|  * We can see that it now has its own metadata and available
 | |
|  * data and it points towards [6]
 | |
|  */
 | |
| static void frag_block(Zone *zone, Block *old_block, uint32_t size)
 | |
| {
 | |
| 	Block *new_block = (Block *)align_mem((uint32_t)old_block + size);
 | |
| 	assert(new_block <
 | |
| 	       (Block *)((uint32_t)zone + get_zone_size(zone->type)));
 | |
| 
 | |
| 	if (old_block->size - align_mem(size) < sizeof(Block)) {
 | |
| 		zone->free = NULL;
 | |
| 		goto last_block;
 | |
| 	}
 | |
| 
 | |
| 	// Newly created block metadata
 | |
| 	new_block->size = old_block->size - align_mem(size);
 | |
| 	new_block->sub_size = new_block->size;
 | |
| 	new_block->in_use = false;
 | |
| 	new_block->ptr = (void *)((uint32_t)new_block + sizeof(Block));
 | |
| 	new_block->zone = zone;
 | |
| 
 | |
| 	new_block->prev = old_block;
 | |
| 	new_block->next = old_block->next;
 | |
| 	old_block->next = new_block;
 | |
| 
 | |
| 	new_block->prev_used = NULL;
 | |
| 	new_block->next_used = NULL;
 | |
| 
 | |
| 	new_block->prev_free = old_block->prev_free;
 | |
| 	new_block->next_free = NULL;
 | |
| 	if (new_block != old_block->next_free)
 | |
| 		new_block->next_free = old_block->next_free;
 | |
| 
 | |
| 	if (zone->free == old_block)
 | |
| 		zone->free = new_block;
 | |
| 
 | |
| last_block:
 | |
| 	old_block->next_free = NULL;
 | |
| 	old_block->prev_free = NULL;
 | |
| 
 | |
| 	// Newly in_use block metadata
 | |
| 	old_block->in_use = true;
 | |
| 	old_block->size = size - sizeof(Block);
 | |
| 	old_block->sub_size = old_block->size;
 | |
| 
 | |
| 	if (zone->used == NULL) {
 | |
| 		zone->used = old_block;
 | |
| 		return;
 | |
| 	}
 | |
| 	old_block->prev_used = NULL;
 | |
| 	old_block->next_used = zone->used;
 | |
| 	zone->used->prev_used = old_block;
 | |
| 	zone->used = old_block;
 | |
| }
 | |
| 
 | |
| // Set the block to used and unset free
 | |
| static void save_block(Zone *head, Block *block, Zone *zone)
 | |
| {
 | |
| 	zone->free = NULL;
 | |
| 	block->in_use = true;
 | |
| 	if (head->used) {
 | |
| 		head->used->prev_used = block;
 | |
| 		head->used->prev = block;
 | |
| 		block->next = head->used;
 | |
| 		block->next_used = head->used;
 | |
| 	}
 | |
| 	head->used = block;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * size: size needed by the user to get allocated
 | |
|  *
 | |
|  * First, we init the allocator if it's the first time
 | |
|  * Then we search if there is an available block in all
 | |
|  * the zones currently mapped
 | |
|  * If no block has been found (NULL), we create 1 new zone of
 | |
|  * the corresponding type
 | |
|  * We then search again for an available block (should not be NULL)
 | |
|  * Finally, if type == LARGE, we just have to change the block to used
 | |
|  * else, we frag the block to use just what's needed
 | |
|  *
 | |
|  * ptr: returns the aligned pointer of the block (after the metadata)
 | |
|  */
 | |
| void *kmalloc(uint32_t size)
 | |
| {
 | |
| 	void *ptr = NULL;
 | |
| 
 | |
| 	if (size == 0) {
 | |
| 		kprintf(KERN_WARNING "kmalloc: can't kmalloc(0)\n");
 | |
| 		return NULL;
 | |
| 	}
 | |
| 
 | |
| 	// Find the zone we need to search
 | |
| 	block_type_t type = get_type(size);
 | |
| 	Zone *head = kzones[type];
 | |
| 
 | |
| 	// Find an available block in a zone of type "type"
 | |
| 	Block *available = find_block(head, size);
 | |
| 	if (available == NULL) {
 | |
| 		uint32_t full_size;
 | |
| 		if (type == LARGE)
 | |
| 			full_size = size + sizeof(Block) + sizeof(Zone);
 | |
| 		else
 | |
| 			full_size = get_zone_size(type);
 | |
| 		if (new_kzone(type, full_size) == -1)
 | |
| 			return NULL;
 | |
| 		head = kzones[type];
 | |
| 		available = find_block(head, size);
 | |
| 	}
 | |
| 	assert(available != NULL);
 | |
| 	if (type == LARGE)
 | |
| 		save_block(head, available, available->zone);
 | |
| 	else
 | |
| 		frag_block(available->zone, available, size + sizeof(Block));
 | |
| 	ptr = available->ptr;
 | |
| 	return ptr;
 | |
| }
 |