feature: both physical and virtual allocators should be done
This commit is contained in:
parent
9ade568a64
commit
a66f9174f4
@ -18,7 +18,7 @@ enum { BPZ = 128, PAGES_TINY = 16, PAGES_SMALL = 64, MEM_ALIGN = 8 };
|
||||
typedef enum { TINY, SMALL, LARGE } block_type_t;
|
||||
|
||||
/* METADATA:
|
||||
* ptr: the ptr to return with kalloc (aligned)
|
||||
* ptr: the ptr to return with kmalloc (aligned)
|
||||
* size: the actual size
|
||||
* sub_size: the size asked by the user (different
|
||||
* from size only if krealloc and krealloc size < size)
|
||||
@ -67,7 +67,8 @@ typedef struct Zone {
|
||||
* For TINY and SMALL, the zone will be divided in blocks.
|
||||
* For LARGE, it will be entire page(s).
|
||||
*/
|
||||
extern Zone *zones[3];
|
||||
extern Zone *kzones[3];
|
||||
extern Zone *vzones[3];
|
||||
|
||||
/*----------- UTILS ----------*/
|
||||
block_type_t get_type(size_t size);
|
||||
@ -76,10 +77,15 @@ size_t align_mem(size_t addr);
|
||||
/*----------------------------*/
|
||||
|
||||
/*-------- ALLOCATOR ---------*/
|
||||
int new_zone(block_type_t type, size_t size);
|
||||
int new_kzone(block_type_t type, size_t size);
|
||||
int new_vzone(block_type_t type, size_t size);
|
||||
/*----------------------------*/
|
||||
|
||||
void *kalloc(size_t size);
|
||||
void *kmalloc(size_t size);
|
||||
void kfree(void *ptr);
|
||||
void *krealloc(void *ptr, size_t size);
|
||||
void show_alloc_mem(void);
|
||||
void *vmalloc(size_t size);
|
||||
void vfree(void *ptr);
|
||||
void *vrealloc(void *ptr, size_t size);
|
||||
void show_kalloc_mem(void);
|
||||
void show_valloc_mem(void);
|
||||
|
@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#define PRESENT (1 << 0)
|
||||
#define RW (1 << 1)
|
||||
@ -10,5 +10,7 @@
|
||||
#define PAGE_SIZE 4096
|
||||
|
||||
void init_memory(void);
|
||||
void *kalloc_frame(uint32_t nb_frames);
|
||||
int kfree_frame(void *frame, uint32_t nb_frames);
|
||||
void *alloc_frames(size_t size);
|
||||
int free_frames(void *frame_ptr, size_t size);
|
||||
void *alloc_pages(size_t size);
|
||||
int free_pages(void *page_ptr, size_t size);
|
||||
|
@ -1,5 +1,6 @@
|
||||
#include "alloc.h"
|
||||
#include "gdt.h"
|
||||
#include "kpanic.h"
|
||||
#include "kprintf.h"
|
||||
#include "memory.h"
|
||||
#include "shell.h"
|
||||
@ -32,5 +33,6 @@ void kernel_main(void)
|
||||
kprintf(KERN_NOTICE "KERN_NOTICE\n");
|
||||
kprintf(KERN_INFO "KERN_INFO\n");
|
||||
kprintf(KERN_DEBUG "KERN_DEBUG\n");
|
||||
vmalloc(10);
|
||||
shell_init();
|
||||
}
|
||||
|
@ -4,9 +4,9 @@
|
||||
|
||||
#include "kprintf.h"
|
||||
#include "memory.h"
|
||||
#include "utils.h"
|
||||
|
||||
#define MAX_FRAMES 1048319
|
||||
#define CEIL(x, y) (((x) + (y) - 1) / (y))
|
||||
#define GET_FRAME(i) (frame_table[i / 8] & (1 << (i % 8)))
|
||||
#define SET_FRAME(i, used) \
|
||||
do { \
|
||||
@ -21,7 +21,7 @@ extern uint32_t end_kernel;
|
||||
static uint8_t frame_table[CEIL(MAX_FRAMES, 8)];
|
||||
static uint32_t remaining_frames = MAX_FRAMES;
|
||||
|
||||
void *kalloc_frame(size_t size)
|
||||
void *alloc_frames(size_t size)
|
||||
{
|
||||
const uint32_t nb_frames = CEIL(size, PAGE_SIZE);
|
||||
if (nb_frames > remaining_frames) {
|
||||
@ -50,15 +50,15 @@ end:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int kfree_frame(void *frame, size_t size)
|
||||
int free_frames(void *frame_ptr, size_t size)
|
||||
{
|
||||
const uint32_t nb_frames = CEIL(size, PAGE_SIZE);
|
||||
const uint32_t start = (frame - (void *)&end_kernel) / PAGE_SIZE;
|
||||
const uint32_t start = (frame_ptr - (void *)&end_kernel) / PAGE_SIZE;
|
||||
|
||||
if (start > MAX_FRAMES || frame < (void *)&end_kernel) {
|
||||
if (start > MAX_FRAMES || frame_ptr < (void *)&end_kernel) {
|
||||
kprintf(KERN_WARNING "Address out of range\n");
|
||||
return -1;
|
||||
} else if ((uint32_t)frame % PAGE_SIZE) {
|
||||
} else if ((uint32_t)frame_ptr % PAGE_SIZE) {
|
||||
kprintf(KERN_WARNING "Invalid address\n");
|
||||
return -1;
|
||||
} else if (start + nb_frames > MAX_FRAMES) {
|
||||
|
@ -1,5 +1,6 @@
|
||||
#include "memory.h"
|
||||
#include "string.h"
|
||||
#include <stdint.h>
|
||||
|
||||
extern void load_page_directory(uint32_t *);
|
||||
extern void enable_paging(void);
|
||||
|
73
src/memory/page.c
Normal file
73
src/memory/page.c
Normal file
@ -0,0 +1,73 @@
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "kprintf.h"
|
||||
#include "memory.h"
|
||||
#include "utils.h"
|
||||
|
||||
#define PT_SIZE 1024
|
||||
#define MAX_TLB_ENTRIES 32
|
||||
|
||||
extern uint32_t page_table_entries[PT_SIZE] __attribute__((aligned(4096)));
|
||||
|
||||
static int16_t find_next_block(size_t nb_pages)
|
||||
{
|
||||
for (size_t i = 0; i < PT_SIZE; i++) {
|
||||
if (page_table_entries[i] >> 12 == i) {
|
||||
size_t j = i + 1;
|
||||
for (; j - i < nb_pages && j < PT_SIZE; j++)
|
||||
if (page_table_entries[j] >> 12 != j)
|
||||
break;
|
||||
if (j - i == nb_pages)
|
||||
return i;
|
||||
i = j - 1;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void *alloc_pages(size_t size)
|
||||
{
|
||||
const uint32_t nb_pages = CEIL(size, PAGE_SIZE);
|
||||
const int16_t index = find_next_block(nb_pages);
|
||||
|
||||
if (index < 0) {
|
||||
kprintf(KERN_CRIT "Not enough pages (max: %d)\n", PT_SIZE);
|
||||
return NULL;
|
||||
}
|
||||
for (size_t i = index; i - (size_t)index < nb_pages; i++) {
|
||||
void *frame = alloc_frames(PAGE_SIZE);
|
||||
if (!frame) {
|
||||
for (size_t j = index; j < i; j++)
|
||||
free_frames(
|
||||
(void *)(page_table_entries[j] >> 12),
|
||||
PAGE_SIZE);
|
||||
return NULL;
|
||||
}
|
||||
page_table_entries[i] = (uint32_t)frame << 12;
|
||||
}
|
||||
return (void *)(page_table_entries[index] >> 12);
|
||||
}
|
||||
|
||||
int free_pages(void *page_ptr, size_t size)
|
||||
{
|
||||
const uint32_t nb_pages = CEIL(size, PAGE_SIZE);
|
||||
const uint32_t page_index = (uint32_t)page_ptr / PAGE_SIZE;
|
||||
|
||||
if ((uint32_t)page_ptr > PT_SIZE * PAGE_SIZE) {
|
||||
kprintf(KERN_WARNING "Address out of range\n");
|
||||
return -1;
|
||||
} else if ((uint32_t)page_ptr % PAGE_SIZE) {
|
||||
kprintf(KERN_WARNING "Invalid address\n");
|
||||
return -1;
|
||||
} else if (page_index + nb_pages > PT_SIZE) {
|
||||
kprintf(KERN_WARNING "Invalid number of frames\n");
|
||||
return -1;
|
||||
}
|
||||
for (size_t i = page_index; i < page_index + nb_pages; i++) {
|
||||
free_frames((void *)(page_table_entries[i] >> 12), PAGE_SIZE);
|
||||
page_table_entries[i] = (i >> 12) | INIT_FLAGS;
|
||||
}
|
||||
return 0;
|
||||
}
|
64
src/memory/phys/allocator.c
Normal file
64
src/memory/phys/allocator.c
Normal file
@ -0,0 +1,64 @@
|
||||
#include "alloc.h"
|
||||
#include "kprintf.h"
|
||||
#include "memory.h"
|
||||
|
||||
Zone *kzones[3];
|
||||
|
||||
static void add_zone(Zone *zone, block_type_t type)
|
||||
{
|
||||
// We put the zone at the beginning of the list
|
||||
if (kzones[type]) {
|
||||
zone->next = kzones[type];
|
||||
kzones[type]->prev = zone;
|
||||
}
|
||||
kzones[type] = zone;
|
||||
}
|
||||
|
||||
static void new_block(Zone *zone, size_t zone_size)
|
||||
{
|
||||
Block *new_block = (Block *)align_mem((size_t)zone + sizeof(Zone));
|
||||
|
||||
// Metadata
|
||||
new_block->in_use = false;
|
||||
new_block->size = zone_size - sizeof(Zone) - sizeof(Block);
|
||||
new_block->sub_size = new_block->size;
|
||||
new_block->ptr = (Block *)((size_t)new_block + sizeof(Block));
|
||||
new_block->zone = zone;
|
||||
|
||||
// Init future linked lists
|
||||
new_block->prev = NULL;
|
||||
new_block->prev_free = NULL;
|
||||
new_block->prev_used = NULL;
|
||||
new_block->next = NULL;
|
||||
new_block->next_free = NULL;
|
||||
new_block->next_used = NULL;
|
||||
|
||||
if (zone->free) {
|
||||
zone->free->prev = new_block;
|
||||
zone->free->prev_free = new_block;
|
||||
new_block->next = zone->free;
|
||||
new_block->next_free = zone->free;
|
||||
}
|
||||
zone->free = new_block;
|
||||
}
|
||||
|
||||
int new_kzone(block_type_t type, size_t size)
|
||||
{
|
||||
void *heap = alloc_frames(size);
|
||||
if (heap == NULL) {
|
||||
kprintf(KERN_ERR "error: alloc_frames failed\n");
|
||||
return (-1);
|
||||
}
|
||||
|
||||
Zone *zone = (Zone *)heap;
|
||||
zone->type = type;
|
||||
zone->size = size;
|
||||
zone->used = NULL;
|
||||
zone->next = NULL;
|
||||
zone->prev = NULL;
|
||||
|
||||
new_block(zone, size);
|
||||
add_zone(heap, type);
|
||||
|
||||
return (0);
|
||||
}
|
@ -4,20 +4,20 @@
|
||||
// FULL_INFO is to display (or not) both used and unused blocks
|
||||
#define FULL_INFO 1
|
||||
|
||||
void show_alloc_mem(void)
|
||||
void show_kalloc_mem(void)
|
||||
{
|
||||
char *const zones_name[3] = {"TINY", "SMALL", "LARGE"};
|
||||
char *const kzones_name[3] = {"TINY", "SMALL", "LARGE"};
|
||||
size_t total_size = 0;
|
||||
|
||||
for (block_type_t type = 0; type < 3; ++type) {
|
||||
int count = 0;
|
||||
for (Zone *zone_it = zones[type]; zone_it != NULL;
|
||||
for (Zone *zone_it = kzones[type]; zone_it != NULL;
|
||||
zone_it = zone_it->next) {
|
||||
#if FULL_INFO
|
||||
if (zone_it->free)
|
||||
kprintf("---------- AVAILABLE %s [n°%d - %p] "
|
||||
"----------\n",
|
||||
zones_name[type], count, zone_it);
|
||||
kzones_name[type], count, zone_it);
|
||||
for (Block *block_it = zone_it->free; block_it != NULL;
|
||||
block_it = block_it->next_free) {
|
||||
kprintf("%p - %p : %u bytes\n", block_it->ptr,
|
||||
@ -31,7 +31,7 @@ void show_alloc_mem(void)
|
||||
if (zone_it->used)
|
||||
kprintf("---------- IN_USE %s [n°%d - %p] "
|
||||
"----------\n",
|
||||
zones_name[type], count, zone_it);
|
||||
kzones_name[type], count, zone_it);
|
||||
for (Block *block_it = zone_it->used; block_it != NULL;
|
||||
block_it = block_it->next_used) {
|
||||
kprintf("%p - %p : %u bytes\n", block_it->ptr,
|
@ -2,20 +2,20 @@
|
||||
#include "kprintf.h"
|
||||
#include "memory.h"
|
||||
|
||||
static void remove_used(Block *to_kfree)
|
||||
static void remove_used(Block *to_free)
|
||||
{
|
||||
Block *left = to_kfree->prev_used;
|
||||
Block *right = to_kfree->next_used;
|
||||
Block *left = to_free->prev_used;
|
||||
Block *right = to_free->next_used;
|
||||
|
||||
to_kfree->next_used = NULL;
|
||||
to_kfree->prev_used = NULL;
|
||||
to_free->next_used = NULL;
|
||||
to_free->prev_used = NULL;
|
||||
|
||||
if (!left && !right) {
|
||||
to_kfree->zone->used = NULL;
|
||||
to_free->zone->used = NULL;
|
||||
return;
|
||||
}
|
||||
if (!left)
|
||||
to_kfree->zone->used = right;
|
||||
to_free->zone->used = right;
|
||||
else
|
||||
left->next_used = right;
|
||||
if (right)
|
||||
@ -24,7 +24,7 @@ static void remove_used(Block *to_kfree)
|
||||
|
||||
/*
|
||||
* If all the blocks of the zone have been kfreed,
|
||||
* we can unmap the zone and delete it from the list of zones
|
||||
* we can unmap the zone and delete it from the list of kzones
|
||||
*/
|
||||
static int unmap_zone(Zone *zone)
|
||||
{
|
||||
@ -36,17 +36,17 @@ static int unmap_zone(Zone *zone)
|
||||
zone->next = NULL;
|
||||
|
||||
if (!left && !right) {
|
||||
zones[type] = NULL;
|
||||
kzones[type] = NULL;
|
||||
goto unmap;
|
||||
}
|
||||
if (!left)
|
||||
zones[type] = right;
|
||||
kzones[type] = right;
|
||||
else
|
||||
left->next = right;
|
||||
if (right)
|
||||
right->prev = left;
|
||||
unmap:
|
||||
err = kfree_frame((void *)zone, zone->size);
|
||||
err = free_frames((void *)zone, zone->size);
|
||||
if (err)
|
||||
kprintf(KERN_ERR "error: munmap failed\n");
|
||||
return (err);
|
||||
@ -97,19 +97,19 @@ void kfree(void *ptr)
|
||||
{
|
||||
if (ptr == NULL)
|
||||
return;
|
||||
Block *to_kfree = (Block *)((size_t)ptr - sizeof(Block));
|
||||
Block *to_free = (Block *)((size_t)ptr - sizeof(Block));
|
||||
Block *to_merge = NULL;
|
||||
to_kfree->in_use = false;
|
||||
remove_used(to_kfree);
|
||||
if (to_kfree->prev && !to_kfree->prev->in_use) {
|
||||
to_merge = to_kfree;
|
||||
to_kfree = merge_blocks(to_kfree->prev, to_kfree);
|
||||
to_free->in_use = false;
|
||||
remove_used(to_free);
|
||||
if (to_free->prev && !to_free->prev->in_use) {
|
||||
to_merge = to_free;
|
||||
to_free = merge_blocks(to_free->prev, to_free);
|
||||
}
|
||||
if (to_kfree->next && !to_kfree->next->in_use) {
|
||||
to_merge = to_kfree->next;
|
||||
to_kfree = merge_blocks(to_kfree, to_kfree->next);
|
||||
if (to_free->next && !to_free->next->in_use) {
|
||||
to_merge = to_free->next;
|
||||
to_free = merge_blocks(to_free, to_free->next);
|
||||
}
|
||||
int err = add_available(to_kfree, to_merge);
|
||||
int err = add_available(to_free, to_merge);
|
||||
if (err)
|
||||
kprintf(KERN_ERR "kfree: fatal error\n");
|
||||
}
|
@ -29,7 +29,7 @@ static Block *find_block(Zone *head, size_t size)
|
||||
* After the allocation, this will become
|
||||
* ... -> [5] -> [new] -> [6] -> ...
|
||||
*
|
||||
* For an example of [5].size = 32 and requiring a kalloc of 10
|
||||
* 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]
|
||||
* ^ ^ ^
|
||||
@ -105,7 +105,7 @@ static void save_block(Zone *head, Block *block, Zone *zone)
|
||||
*
|
||||
* 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
|
||||
* the kzones 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)
|
||||
@ -114,18 +114,18 @@ static void save_block(Zone *head, Block *block, Zone *zone)
|
||||
*
|
||||
* ptr: returns the aligned pointer of the block (after the metadata)
|
||||
*/
|
||||
void *kalloc(size_t size)
|
||||
void *kmalloc(size_t size)
|
||||
{
|
||||
void *ptr = NULL;
|
||||
|
||||
if (size == 0) {
|
||||
kprintf(KERN_WARNING "kalloc: can't kalloc(0)\n");
|
||||
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 = zones[type];
|
||||
Zone *head = kzones[type];
|
||||
|
||||
// Find an available block in a zone of type "type"
|
||||
Block *available = find_block(head, size);
|
||||
@ -135,9 +135,9 @@ void *kalloc(size_t size)
|
||||
full_size = size + sizeof(Block) + sizeof(Zone);
|
||||
else
|
||||
full_size = get_zone_size(type);
|
||||
if (new_zone(type, full_size) == -1)
|
||||
if (new_kzone(type, full_size) == -1)
|
||||
return NULL;
|
||||
head = zones[type];
|
||||
head = kzones[type];
|
||||
available = find_block(head, size);
|
||||
}
|
||||
assert(available != NULL);
|
@ -1,9 +1,9 @@
|
||||
#include "alloc.h"
|
||||
#include "string.h"
|
||||
|
||||
// Prototype for kfree and kalloc
|
||||
// Prototype for kfree and kmalloc
|
||||
void kfree(void *ptr);
|
||||
void *kalloc(size_t size);
|
||||
void *kmalloc(size_t size);
|
||||
|
||||
/*
|
||||
* ptr: block to resize (undefined behavior if invalid)
|
||||
@ -27,7 +27,7 @@ void *krealloc(void *ptr, size_t size)
|
||||
block->sub_size = size;
|
||||
return (ptr);
|
||||
}
|
||||
new_ptr = kalloc(size);
|
||||
new_ptr = kmalloc(size);
|
||||
if (new_ptr == NULL)
|
||||
return NULL;
|
||||
memmove(new_ptr, ptr, block->size);
|
@ -2,16 +2,16 @@
|
||||
#include "kprintf.h"
|
||||
#include "memory.h"
|
||||
|
||||
Zone *zones[3];
|
||||
Zone *vzones[3];
|
||||
|
||||
static void add_zone(Zone *zone, block_type_t type)
|
||||
{
|
||||
// We put the zone at the beginning of the list
|
||||
if (zones[type]) {
|
||||
zone->next = zones[type];
|
||||
zones[type]->prev = zone;
|
||||
if (vzones[type]) {
|
||||
zone->next = vzones[type];
|
||||
vzones[type]->prev = zone;
|
||||
}
|
||||
zones[type] = zone;
|
||||
vzones[type] = zone;
|
||||
}
|
||||
|
||||
static void new_block(Zone *zone, size_t zone_size)
|
||||
@ -42,11 +42,11 @@ static void new_block(Zone *zone, size_t zone_size)
|
||||
zone->free = new_block;
|
||||
}
|
||||
|
||||
int new_zone(block_type_t type, size_t size)
|
||||
int new_vzone(block_type_t type, size_t size)
|
||||
{
|
||||
void *heap = kalloc_frame(size);
|
||||
void *heap = alloc_pages(size);
|
||||
if (heap == NULL) {
|
||||
kprintf(KERN_ERR "error: syscall mmap failed\n");
|
||||
kprintf(KERN_ERR "error: alloc_pages failed\n");
|
||||
return (-1);
|
||||
}
|
||||
|
49
src/memory/virt/info.c
Normal file
49
src/memory/virt/info.c
Normal file
@ -0,0 +1,49 @@
|
||||
#include "alloc.h"
|
||||
#include "kprintf.h"
|
||||
|
||||
// FULL_INFO is to display (or not) both used and unused blocks
|
||||
#define FULL_INFO 1
|
||||
|
||||
void show_valloc_mem(void)
|
||||
{
|
||||
char *const vzones_name[3] = {"TINY", "SMALL", "LARGE"};
|
||||
size_t total_size = 0;
|
||||
|
||||
for (block_type_t type = 0; type < 3; ++type) {
|
||||
int count = 0;
|
||||
for (Zone *zone_it = vzones[type]; zone_it != NULL;
|
||||
zone_it = zone_it->next) {
|
||||
#if FULL_INFO
|
||||
if (zone_it->free)
|
||||
kprintf("---------- AVAILABLE %s [n°%d - %p] "
|
||||
"----------\n",
|
||||
vzones_name[type], count, zone_it);
|
||||
for (Block *block_it = zone_it->free; block_it != NULL;
|
||||
block_it = block_it->next_free) {
|
||||
kprintf("%p - %p : %u bytes\n", block_it->ptr,
|
||||
(size_t)block_it->ptr +
|
||||
block_it->sub_size + sizeof(Block),
|
||||
block_it->sub_size);
|
||||
}
|
||||
if (zone_it->free)
|
||||
kprintf("\n");
|
||||
#endif
|
||||
if (zone_it->used)
|
||||
kprintf("---------- IN_USE %s [n°%d - %p] "
|
||||
"----------\n",
|
||||
vzones_name[type], count, zone_it);
|
||||
for (Block *block_it = zone_it->used; block_it != NULL;
|
||||
block_it = block_it->next_used) {
|
||||
kprintf("%p - %p : %u bytes\n", block_it->ptr,
|
||||
(size_t)block_it->ptr +
|
||||
block_it->sub_size + sizeof(Block),
|
||||
block_it->sub_size);
|
||||
total_size += block_it->sub_size;
|
||||
}
|
||||
if (zone_it->used)
|
||||
kprintf("\n");
|
||||
count++;
|
||||
}
|
||||
}
|
||||
kprintf("Total: %u\n", total_size);
|
||||
}
|
115
src/memory/virt/vfree.c
Normal file
115
src/memory/virt/vfree.c
Normal file
@ -0,0 +1,115 @@
|
||||
#include "alloc.h"
|
||||
#include "kprintf.h"
|
||||
#include "memory.h"
|
||||
|
||||
static void remove_used(Block *to_free)
|
||||
{
|
||||
Block *left = to_free->prev_used;
|
||||
Block *right = to_free->next_used;
|
||||
|
||||
to_free->next_used = NULL;
|
||||
to_free->prev_used = NULL;
|
||||
|
||||
if (!left && !right) {
|
||||
to_free->zone->used = NULL;
|
||||
return;
|
||||
}
|
||||
if (!left)
|
||||
to_free->zone->used = right;
|
||||
else
|
||||
left->next_used = right;
|
||||
if (right)
|
||||
right->prev_used = left;
|
||||
}
|
||||
|
||||
/*
|
||||
* If all the blocks of the zone have been kfreed,
|
||||
* we can unmap the zone and delete it from the list of vzones
|
||||
*/
|
||||
static int unmap_zone(Zone *zone)
|
||||
{
|
||||
int err = 0;
|
||||
block_type_t type = zone->type;
|
||||
Zone *left = zone->prev;
|
||||
Zone *right = zone->next;
|
||||
zone->prev = NULL;
|
||||
zone->next = NULL;
|
||||
|
||||
if (!left && !right) {
|
||||
vzones[type] = NULL;
|
||||
goto unmap;
|
||||
}
|
||||
if (!left)
|
||||
vzones[type] = right;
|
||||
else
|
||||
left->next = right;
|
||||
if (right)
|
||||
right->prev = left;
|
||||
unmap:
|
||||
err = free_pages((void *)zone, zone->size);
|
||||
if (err)
|
||||
kprintf(KERN_ERR "error: munmap failed\n");
|
||||
return (err);
|
||||
}
|
||||
|
||||
/*
|
||||
* If the newly kfreed block is next to another previously
|
||||
* kfreed block, merge both of these and update the size
|
||||
*/
|
||||
static Block *merge_blocks(Block *left, Block *right)
|
||||
{
|
||||
if (right->next)
|
||||
right->next->prev = left;
|
||||
if (right->next_free) {
|
||||
right->next_free->prev_free = left;
|
||||
left->next_free = right->next_free;
|
||||
}
|
||||
left->next = right->next;
|
||||
left->size += right->size + sizeof(Block);
|
||||
return (left);
|
||||
}
|
||||
|
||||
// Simply add the new block to the list of available blocks
|
||||
static int add_available(Block *available, Block *merged)
|
||||
{
|
||||
Zone *zone = available->zone;
|
||||
if (merged != zone->free && available != zone->free)
|
||||
available->next_free = zone->free;
|
||||
if (zone->free)
|
||||
zone->free->prev_free = available;
|
||||
zone->free = available;
|
||||
if (zone->type == LARGE)
|
||||
return (unmap_zone(zone));
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* ptr: pointer to kfree, if the pointer is invalid the kfree()
|
||||
* function will have an undefined behavior (most likely segfault)
|
||||
*
|
||||
* First, we remove the block from the list of in_use blocks
|
||||
* Then, we check if the block needs to be merged with another
|
||||
* neighboring block, if so we replace the previous block by the
|
||||
* newly merged block
|
||||
* Finally, we add the block to the list of available blocks
|
||||
*/
|
||||
void vfree(void *ptr)
|
||||
{
|
||||
if (ptr == NULL)
|
||||
return;
|
||||
Block *to_free = (Block *)((size_t)ptr - sizeof(Block));
|
||||
Block *to_merge = NULL;
|
||||
to_free->in_use = false;
|
||||
remove_used(to_free);
|
||||
if (to_free->prev && !to_free->prev->in_use) {
|
||||
to_merge = to_free;
|
||||
to_free = merge_blocks(to_free->prev, to_free);
|
||||
}
|
||||
if (to_free->next && !to_free->next->in_use) {
|
||||
to_merge = to_free->next;
|
||||
to_free = merge_blocks(to_free, to_free->next);
|
||||
}
|
||||
int err = add_available(to_free, to_merge);
|
||||
if (err)
|
||||
kprintf(KERN_ERR "kfree: fatal error\n");
|
||||
}
|
152
src/memory/virt/vmalloc.c
Normal file
152
src/memory/virt/vmalloc.c
Normal file
@ -0,0 +1,152 @@
|
||||
#include "alloc.h"
|
||||
#include "kprintf.h"
|
||||
|
||||
int new_zone(block_type_t type, size_t size);
|
||||
|
||||
/*
|
||||
* Find first available (not in_use) block
|
||||
* in a zone matching the size we need
|
||||
*/
|
||||
static Block *find_block(Zone *head, size_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->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 vmalloc 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, size_t size)
|
||||
{
|
||||
Block *new_block = (Block *)align_mem((size_t)old_block + size);
|
||||
assert(!(new_block >=
|
||||
(Block *)((size_t)zone + get_zone_size(zone->type))));
|
||||
|
||||
// Newly created block metadata
|
||||
new_block->size = old_block->size - size;
|
||||
new_block->sub_size = new_block->size;
|
||||
new_block->in_use = false;
|
||||
new_block->ptr = (void *)((size_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 = old_block->next_free;
|
||||
|
||||
if (zone->free == old_block)
|
||||
zone->free = new_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 use 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 vzones 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 *vmalloc(size_t size)
|
||||
{
|
||||
void *ptr = NULL;
|
||||
|
||||
if (size == 0) {
|
||||
kprintf(KERN_WARNING "vmalloc: can't vmalloc(0)\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Find the zone we need to search
|
||||
block_type_t type = get_type(size);
|
||||
Zone *head = vzones[type];
|
||||
|
||||
// Find an available block in a zone of type "type"
|
||||
Block *available = find_block(head, size);
|
||||
if (available == NULL) {
|
||||
size_t full_size;
|
||||
if (type == LARGE)
|
||||
full_size = size + sizeof(Block) + sizeof(Zone);
|
||||
else
|
||||
full_size = get_zone_size(type);
|
||||
if (new_vzone(type, full_size) == -1)
|
||||
return NULL;
|
||||
head = vzones[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;
|
||||
}
|
36
src/memory/virt/vrealloc.c
Normal file
36
src/memory/virt/vrealloc.c
Normal file
@ -0,0 +1,36 @@
|
||||
#include "alloc.h"
|
||||
#include "string.h"
|
||||
|
||||
// Prototype for kfree and vmalloc
|
||||
void kfree(void *ptr);
|
||||
void *vmalloc(size_t size);
|
||||
|
||||
/*
|
||||
* ptr: block to resize (undefined behavior if invalid)
|
||||
* size: size needed by the user to get vreallocated
|
||||
*
|
||||
* If we have a size <= to the previous size, we don't have
|
||||
* to do anything, we just change sub_size for info purposes
|
||||
* and return the same pointer
|
||||
* Else, we allocate a new block and copy the content of
|
||||
* the previous block in the new one and kfree the old block
|
||||
*
|
||||
* ptr: returns the aligned pointer of the vreallocated block
|
||||
*/
|
||||
void *vrealloc(void *ptr, size_t size)
|
||||
{
|
||||
void *new_ptr = NULL;
|
||||
if (ptr == NULL)
|
||||
return NULL;
|
||||
Block *block = (Block *)((size_t)ptr - sizeof(Block));
|
||||
if (block->size >= size) {
|
||||
block->sub_size = size;
|
||||
return (ptr);
|
||||
}
|
||||
new_ptr = vmalloc(size);
|
||||
if (new_ptr == NULL)
|
||||
return NULL;
|
||||
memmove(new_ptr, ptr, block->size);
|
||||
kfree(ptr);
|
||||
return (new_ptr);
|
||||
}
|
@ -1,7 +1,16 @@
|
||||
#include "alloc.h"
|
||||
#include "kprintf.h"
|
||||
#include "string.h"
|
||||
|
||||
void heap_cmd(char *arg)
|
||||
{
|
||||
(void)arg;
|
||||
show_alloc_mem();
|
||||
if (!arg)
|
||||
kprintf(KERN_INFO "You must specify an argument (phys/virt)\n");
|
||||
else if (!strcmp(arg, "phys"))
|
||||
show_kalloc_mem();
|
||||
else if (!strcmp(arg, "virt"))
|
||||
show_valloc_mem();
|
||||
else
|
||||
kprintf(KERN_INFO "%s: invalid argument to heap (phys/virt)\n",
|
||||
arg);
|
||||
}
|
Loading…
Reference in New Issue
Block a user