summaryrefslogtreecommitdiff
path: root/src/memory/virtalloc.c
blob: b8135ef17a601010bef43f12ff49466c53ca2d09 (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
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
#include <stdint.h>
#include <stddef.h>
#include <memory.h>

#define MEMORY_INTERNAL
#include <memory/virtalloc.h>

struct addr_node {
	uintptr_t start;
	uintptr_t end;
	struct addr_node *next;
	struct addr_node *prev;
	uint8_t is_alloc;
	uint8_t is_bss;
};

#define BOOTSTRAP_BSS_NODES 16
static uint8_t bss_nodes = 0;
static struct addr_node nodes[BOOTSTRAP_BSS_NODES];

static struct addr_node *start_node;

static struct addr_node *alloc_node(void) {
	if (bss_nodes >= BOOTSTRAP_BSS_NODES) {
		//FIXME: alloc on heap
	} else {
		struct addr_node *node = &nodes[bss_nodes];
		bss_nodes += 1;
		node->is_bss = false;
		return node;
	}
	return NULL;
}

static void free_node(struct addr_node *node) {
	if (!node->is_bss)
		free(node);
}

void virtaddr_init(void) {
	struct addr_node init = {
		.start = 0,
		.end = UINT64_MAX,
		.next = NULL,
		.prev = NULL,
		.is_alloc = false,
		.is_bss = true,
	};
	nodes[0] = init;
	start_node = &nodes[0];
	bss_nodes++;
}

void *virtaddr_alloc(int n_pages) {

	long n_length = n_pages * PAGE_SIZE;
	struct addr_node *node = start_node;

	for (; node != NULL ; node = node->next) {

		if (node->is_alloc)
			continue;

		long length = node->end - node->start;
		if (length >= n_length) {
			struct addr_node *new = alloc_node();
			if (node == NULL)
				return NULL;
			new->next = node;
			node->prev = new;
			if (node->prev != NULL) {
				node->prev->next = new;
			}
			new->start = node->start;
			new->end = new->start + n_length;
			node->start = new->end;
			new->is_alloc = true;
			return (void *) new->start;
		}
	}
	
	return NULL;
}

static void merge_back(struct addr_node *node) {
	while(node->prev && !node->is_alloc) {
		struct addr_node *temp = node->prev;
		node->start = node->prev->start;
		node->prev = node->prev->prev;
		free_node(temp);
	}
	if (node->prev == NULL) {
		start_node = node;
	}
}

static void merge_forward(struct addr_node *node) {
	while(node->next && !node->is_alloc) {
		struct addr_node *temp = node->next;
		node->end = node->next->end;
		node->next = node->next->next;
		free_node(temp);
	}
}

long virtaddr_free(void *virtaddr) {

	uintptr_t virt = (uintptr_t) virtaddr;

	if (virt % PAGE_SIZE)
		return -1; // not page aligned, we did not give this out!!!

	struct addr_node *node = start_node;

	for (; node != NULL; node = node->next) {
		if (node->start == virt) {
			int length = node->end - node->start;
			int pages = length / PAGE_SIZE; 
			merge_back(node);
			merge_forward(node);
			return pages;
		}
	}

	return -1;
}