summaryrefslogtreecommitdiff
path: root/kernel/kalloc.c
blob: e197c0cab1b022e61fbb55e24b5a409a7c99054b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
#include "kernel/lib.h"
#include "kernel/kalloc.h"
#include "kernel/spinlock.h"

extern uint32_t _heap_start;
extern uint32_t _heap_end;

union Page
{
    union Page* next;
    char dummy[PAGE_SIZE];
};
typedef union Page Page;
static_assert(sizeof(Page) == PAGE_SIZE);

static inline void* align_page_down(void* p)
{
    return (void*)((uint32_t)p & ~(PAGE_SIZE - 1));
}

static inline void* align_page_up(void* p)
{
    return align_page_down((char*)p + PAGE_SIZE - 1);
}

static inline bool is_page_aligned(void* p)
{
    return ((uint32_t)p & (PAGE_SIZE - 1)) == 0;
}

static Page* g_page_start;
static Page* g_page_end;
static Page* g_free_list;
static Spinlock g_lock;

void kalloc_init()
{
    spinlock_init(&g_lock);
    
    g_free_list  = NULL;
    g_page_start = (Page*)align_page_up(&_heap_start);
    g_page_end   = (Page*)align_page_down(&_heap_end);

    for (Page* p = g_page_end - 1; p >= g_page_start; --p)
    {
        p->next = g_free_list;
        g_free_list = p;
    }
}

void* kalloc()
{
    spinlock_acquire(&g_lock);
    Page* page = g_free_list;
    if (!page)
    {
        panic("kalloc: Out of memory");
    }
    g_free_list = page->next;
    spinlock_release(&g_lock);
    return page;
}

void* kzalloc()
{
    void* page = kalloc();
    uint32_t* p = (uint32_t*)page;
    for (uint32_t i = 0; i < PAGE_SIZE / 4; ++i)
    {
        *p++ = 0U;
    }
    return page;
}

void kfree(void* ptr)
{
    if (ptr < (void*)g_page_start || ptr >= (void*)g_page_end || !is_page_aligned(ptr))
    {
        panic("kfree: Invalid page");
    }
    
    spinlock_acquire(&g_lock);
    Page* page = (Page*)ptr;
    page->next = g_free_list;
    g_free_list = page;
    spinlock_release(&g_lock);
}