forked from rasendubi/akernel
-
Notifications
You must be signed in to change notification settings - Fork 0
/
page_alloc.c
111 lines (91 loc) · 2.78 KB
/
page_alloc.c
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
#include <page_alloc.h>
#include <stddef.h>
#include <svc.h>
#define MAX_ADDRESS 0x10000000
#define MAX_PAGES (MAX_ADDRESS/PAGE_SIZE)
#define MAX_LEVEL 8
#define PAGE_CLEAN 0
#define PAGE_DIRTY 1
extern unsigned end_kernel;
static unsigned char page_status[2*MAX_PAGES];
static int pages_on_level(unsigned level) {
return MAX_PAGES >> level;
}
static unsigned char *base_for_level(unsigned level) {
unsigned char *base = page_status;
for (unsigned i = 0; i < level; ++i) {
base += pages_on_level(i);
}
return base;
}
static size_t entry_on_level(size_t page, unsigned level) {
return page >> level;
}
static void mark_dirty(const size_t page) {
unsigned char *base = page_status;
for (unsigned level = 0; level <= MAX_LEVEL &&
base[entry_on_level(page, level)] == PAGE_CLEAN; ++level) {
base[entry_on_level(page, level)] = PAGE_DIRTY;
base += pages_on_level(level);
}
}
static void mark_dirty_in_level(int level, int entry) {
const int first_page = entry << level;
const int end_page = first_page + (1<<level);
for (int i = first_page; i < end_page; ++i) {
mark_dirty(i);
}
}
static size_t addr_to_page(void *addr) {
return (size_t)addr / PAGE_SIZE;
}
extern char _binary_ramdisk_tar_start;
extern char _binary_ramdisk_tar_end;
void init_page_alloc(void) {
unsigned kernel_end = (unsigned)&end_kernel;
for (size_t i = 0; i <= kernel_end/PAGE_SIZE; ++i) {
mark_dirty(i);
}
size_t ramdisk_first = addr_to_page(&_binary_ramdisk_tar_start);
size_t ramdisk_end = addr_to_page(&_binary_ramdisk_tar_end);
for (size_t page = ramdisk_first; page <= ramdisk_end; ++page) {
mark_dirty(page);
}
}
void *page_alloc(int level) {
unsigned char *base = base_for_level(level);
int entries_on_level = MAX_PAGES >> level;
for (int i = 0; i < entries_on_level; ++i) {
if (base[i] == PAGE_CLEAN) {
mark_dirty_in_level(level, i);
return (void*)((i << level)*PAGE_SIZE);
}
}
return NULL;
}
void handle_page_alloc(task_struct *ts) {
ts->stack[r0] = (unsigned)page_alloc(ts->stack[r0]);
}
/* TODO: Refactor this method */
void page_free(void *page_start, int level) {
unsigned char *base = page_status;
int entry_on_level = ((unsigned)page_start/PAGE_SIZE) >> level;
int big_brother = entry_on_level - entry_on_level%2;
for (int cur_level = 0; cur_level < level; ++cur_level) {
int entry_count = 1 << (level - cur_level);
int start_entry = ((unsigned)page_start/PAGE_SIZE) >> cur_level;
for (int i = 0; i < entry_count; ++i) {
base[start_entry + i] = PAGE_CLEAN;
}
base += MAX_PAGES >> cur_level;
}
base[entry_on_level] = PAGE_CLEAN;
if (base[big_brother] == PAGE_CLEAN && base[big_brother+1] ==
PAGE_CLEAN) {
base += MAX_PAGES >> level;
base[entry_on_level/2] = PAGE_CLEAN;
}
}
void handle_page_free(task_struct *ts) {
page_free((void*)ts->stack[r0], ts->stack[r1]);
}