summaryrefslogtreecommitdiff
path: root/src/kernel/mem/heap.basic.c
blob: d746deb8d74b97a09c909dfe2bc073a67d1d71a3 (plain) (blame)
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
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
#include "heap.basic.h"

#include "paging.h"

#include <lib/mutex.h>
#include <core/sys.h>

void heap_create(struct heap *heap, size_t start, size_t datasize, size_t maxdatasize) {
	uint32_t i;
	if (start & 0x0FFF) start = (start & 0xFFFFF000) + 0x1000;
	
	// Allocate frames
	for (i = start; i < start + datasize; i += 0x1000) {
		page_map(pagedir_getPage(kernel_pagedir, i, 1), frame_alloc(), 0, 0);
	}

	heap->start_addr = start;
	heap->end_addr = start + datasize;
	heap->max_end = start + maxdatasize;

	struct heap_header *first = (struct heap_header*)start;
	first->magic = HH_FREE_MAGIC;
	first->prev = first->next = first;

	heap->first = first;
	heap->first_free = first;
	
	heap->mutex = 0;
}

// ***** UTILITY FUNCTIONS *****

#define BLK_SIZE(blk) (blk->next == blk ? ((size_t)heap->end_addr - (size_t)blk) : ((size_t)blk->next - (size_t)blk))
#define BLK_FREE(blk) (blk->magic == HH_FREE_MAGIC)

void blk_split(struct heap *heap, struct heap_header *hdr, size_t size) {
	ASSERT (size >= MIN_BLK_SIZE);
	ASSERT (BLK_SIZE(hdr) > size);
	ASSERT (BLK_SIZE(hdr) - size >= MIN_BLK_SIZE);
	ASSERT (BLK_FREE(hdr));

	struct heap_header *hdr_nxt = (struct heap_header*)((size_t)hdr + size);
	hdr_nxt->magic = HH_FREE_MAGIC;
	hdr_nxt->prev = hdr;
	if (hdr->next == hdr) {
		hdr_nxt->next = hdr_nxt;
	} else {
		hdr_nxt->next = hdr->next;
		hdr->next->prev = hdr_nxt;
	}
	hdr->next = hdr_nxt;
}

void blk_combine_next(struct heap *heap, struct heap_header *hdr) {
	ASSERT (hdr != hdr->next);	// make sure we are not the last block
	ASSERT (hdr->magic == HH_FREE_MAGIC);
	ASSERT (hdr->next->magic == HH_FREE_MAGIC);

	struct heap_header *hdr_nxt = hdr->next;
	if (hdr_nxt->next == hdr_nxt) {
		hdr->next = hdr;
	} else {
		hdr->next = hdr_nxt->next;
		hdr_nxt->next->prev = hdr;
	}
}

// ***** Allocation/Freeing functions *****

void *heap_alloc(struct heap *heap, size_t sz) {
	/* Algorithm :
	1 Use heap->first_free to find first effectively free block, update heap->first_free
	2 Find first free AND BIG ENOUGH block
		- If no block can be found, return 0 (no heap expand yet)
	3 If that block is too big, split it in two
	4 Mark that block as used and return it's address + sizeof(heap_header) */
	mutex_lock(&heap->mutex);

	sz += sizeof(struct heap_header);
	if (sz < MIN_BLK_SIZE) sz = MIN_BLK_SIZE;
	if (sz % 4 != 0) sz = sz + 4 - (sz % 4); 

	// 1
	struct heap_header *it = heap->first_free;
	while (!BLK_FREE(it) && it != it->next) it = it->next;
	if (!BLK_FREE(it) && it == it->next) {
		mutex_unlock(&heap->mutex);
		return 0;
	}
	heap->first_free = it;

	// 2
	while (BLK_SIZE(it) < sz && it != it->next) it = it->next;
	if (BLK_SIZE(it) < sz) {		// no block could be found
		mutex_unlock(&heap->mutex);
		return 0;
	}

	// 3
	if (BLK_SIZE(it) >= sz + MIN_BLK_SIZE) blk_split(heap, it, sz);

	// 4
	void *ret = (void*)((size_t)it + sizeof(struct heap_header));
	it->magic = HH_ALLOC_MAGIC;

	mutex_unlock(&heap->mutex);
	return ret;
}

void heap_free(struct heap *heap, void *ptr) {
	/* Algorithm :
	1 Get block pointer to the block we are freeing
	2 Mark that block as free
	3 If block->next is free, combine_next(block)
	4 If block->prev is free, block = block->prev; combine_next(block)
	5 If block < heap->first_free; heap->first_free = block
	*/
	mutex_lock(&heap->mutex);

	// 1
	struct heap_header *hdr = (struct heap_header*)((size_t)ptr - sizeof(struct heap_header));
	if (hdr->magic != HH_ALLOC_MAGIC) return;
	// 2
	hdr->magic = HH_FREE_MAGIC;
	// 3
	if (hdr->next != hdr && hdr->next->magic == HH_FREE_MAGIC) blk_combine_next(heap, hdr);
	// 4
	if (hdr->prev != hdr && hdr->prev->magic == HH_FREE_MAGIC) {
		hdr = hdr->prev;
		blk_combine_next(heap, hdr);
	}
	// 5
	if (hdr < heap->first_free) heap->first_free = hdr;

	mutex_unlock(&heap->mutex);
}