MushOS  0.1
A UNIX-like OS prototype, written from scratch
Loading...
Searching...
No Matches
pages.c
1#include "pages.h"
2
3#include "../../lib/base/generic.h"
4#include "../../lib/base/stdio.h"
5#include "../../lib/base/memory.h"
6#include "../../lib/base/heap.h"
7
8#include "../drivers/screen.h"
9
10#include "kernel.h"
11
12
13#define paging_pool_start 0x100000
14#define paging_pool_end 0xf00000
15
16#define user_space_start 0x1000000
17#define user_space_end 0x10000000
18
19
20extern void* stack_pointer;
21extern u_dword stack_size;
22
23extern void copy_page_physical(u_dword source, u_dword destination);
24
25
26page_folder* kernel_directory, * current_directory;
27void* page_dirs, * page_tables;
28byte* paging_pool, * user_space_pool;
29
30
31static void* get_contents_as_page(page_pointer page) {
32 return (void*) (page.frame * page_size);
33}
34
35static page_pointer* get_contents_as_table(page_pointer page) {
36 return (page_pointer*) (page.frame * page_size);
37}
38
39
40static u_dword get_free_bit(u_byte search) {
41 if (search == full_byte) return search;
42 for (u_dword i = 0; i < 8; ++i) if (!(search & (1u << i))) return i;
43}
44
45static void* allocate_frame() {
46 for (u_dword i = 0; i < size_of(user_space_pool); ++i) {
47 u_dword free_bit = get_free_bit(user_space_pool[i]);
48 if (free_bit != full_byte) {
49 user_space_pool[i] |= (1u << free_bit);
50 return (void *) (user_space_start + (i * 8 + free_bit) * page_size);
51 }
52 }
53 return nullptr; // TODO: raise out of RAM exception!
54}
55
56static page_folder* allocate_page_folder() {
57 for (u_dword i = 0; i < size_of(paging_pool); ++i) {
58 u_dword free_bit = get_free_bit(paging_pool[i]);
59 if (free_bit != full_byte) {
60 paging_pool[i] |= (1u << free_bit);
61 void* address = (void *) (paging_pool_start + (i * 8 + free_bit) * page_size);
62 memory_clear((byte*) address, page_size, 0);
63 return address;
64 }
65 }
66 return nullptr; // TODO: raise out of RAM exception!
67}
68
69static void free_frame(void* address) {
70 u_dword index = (u_dword) address - user_space_start;
71 user_space_pool[index / 8] &= ~(0x1 << (index % 8));
72}
73
74static void free_page_table(page_folder* table, boolean top_level) {
75 u_dword index = (u_dword) table - paging_pool_start;
76 paging_pool[index / 8] &= ~(0x1 << (index % 8));
77 for (u_dword i = 0; i < pages_in_table; i++) {
78 void* contents = get_contents_as_page(table->contents[i]);
79 if (top_level) free_page_table((page_folder*) contents, false);
80 else free_frame(contents);
81 }
82}
83
84
85static page_pointer create_page_entry(const void* address, boolean is_kernel, boolean is_writeable) {
86 page_pointer page_table;
87 page_table.frame = (u_dword) address / page_size;
88 page_table.user = is_kernel ? 0 : 1;
89 page_table.rw = is_writeable ? 1 : 0;
90 page_table.present = 1;
91 return page_table;
92}
93
94static page_pointer* get_page(u_dword address, boolean make, page_folder *dir) {
95 if (address > user_space_end) return nullptr; //
96 // Turn the address into an index.
97 address /= page_size;
98 // Find the page table containing this address.
99 u_dword table_idx = address / pages_in_table;
100
101 u_dword entry = (u_dword) get_contents_as_page(dir->contents[table_idx]);
102 if (!entry) { // If this table is already assigned
103 if (!make) return nullptr; // TODO: throw page doesn't exist exception.
104 else dir->contents[table_idx] = create_page_entry((void*) allocate_page_folder(), true, true);
105 }
106 return &get_contents_as_table(dir->contents[table_idx])[address % pages_in_table];
107}
108
109
110static page_pointer clone_page_pointer(page_pointer page) {
111 page_pointer copy;
112 copy.present = page.present;
113 copy.rw = page.rw;
114 copy.user = page.user;
115 copy.write = page.write;
116 copy.cache = page.cache;
117 copy.accessed = page.accessed;
118 copy.dirty = page.dirty;
119 copy.zero = page.zero;
120 copy.global = page.global;
121 copy.unused = page.unused;
122 copy.frame = page.frame;
123 return copy;
124}
125
126static boolean contains_user_pages(page_pointer folder) {
127 page_pointer* directory = get_contents_as_table(folder);
128 boolean contains = false;
129 for (u_dword i = 0; i < pages_in_table; i++) contains |= directory[i].user;
130 return contains;
131}
132
133static page_pointer clone_table(page_pointer src) {
134 // Make a new page table, which is page aligned.
135 page_folder* table = allocate_page_folder();
136 page_pointer* pages = get_contents_as_table(src);
137
138 // For every entry in the table...
139 for (int i = 0; i < pages_in_table; i++) {
140 if (pages[i].present == 0) continue;
141 if (src.user || pages[i].user) {
142 // Clone the flags from source to destination.
143 table->contents[i] = clone_page_pointer(pages[i]);
144 // Get a new frame.
145 table->contents[i].frame = (u_dword) allocate_frame() / page_size;
146 // Physically copy the data across. This function is in process.s.
147 copy_page_physical(pages[i].frame * page_size, table->contents[i].frame * page_size);
148 } else table->contents[i] = pages[i];
149 }
150
151 page_pointer table_pointer = clone_page_pointer(src);
152 table_pointer.frame = (u_dword) table / page_size;
153 return table_pointer;
154}
155
156page_folder* clone_directory(page_folder* src) {
157 // Make a new page directory and obtain its physical address.
158 page_folder* dir = (page_folder*) allocate_page_folder();
159
160 // We should clone user-mode pages and copy kernel-mode pages, ignoring non-existing ones.
161 for (int i = 0; i < pages_in_table; i++) {
162 if (src->contents[i].present == 0) continue;
163 else if (src->contents[i].user || contains_user_pages(src->contents[i])) {
164 // It contains user-mode pages (or is a user-mode table), we should clone it.
165 dir->contents[i] = clone_table(src->contents[i]);
166 } else {
167 // It's completely in the kernel, so just use the same pointer.
168 dir->contents[i] = src->contents[i];
169 }
170 }
171 return dir;
172}
173
174
175void page_fault(registers* regs) {
176 // A page fault has occurred.
177 // The faulting address is stored in the CR2 register.
178 u_dword faulting_address;
179 asm ("mov %%cr2, %0" : "=r" (faulting_address));
180
181 // The error code gives us details of what happened.
182 boolean present = regs->err_code & 0x1; // Page not present
183 boolean rw = regs->err_code & 0x2; // Write operation?
184 boolean us = regs->err_code & 0x4; // Processor was in user-mode?
185 boolean reserved = regs->err_code & 0x8; // Overwritten CPU-reserved bits of page entry?
186 boolean id = regs->err_code & 0x10; // Caused by an instruction fetch?
187
188 // Output an error message.
189 PANIC("Page fault! (p: %d, w: %d, u: %d, r: %d) at %h (EIP: %h)", present, rw, us, reserved, faulting_address, regs->eip)
190}
191
192
193void initialise_paging() {
194 // The size of physical memory. For the moment we
195 // assume it is 256MB big.
196 u_dword paging_pool_size = (paging_pool_end - paging_pool_start) / page_size / 8;
197 paging_pool = ralloc(paging_pool_size);
198 memory_clear((byte*) paging_pool, paging_pool_size, 0);
199
200 u_dword user_space_pool_size = (user_space_end - user_space_start) / page_size / 8;
201 user_space_pool = ralloc(user_space_pool_size);
202 memory_clear((byte*) user_space_pool, user_space_pool_size, 0);
203
204 // Let's make a page directory.
205 kernel_directory = (page_folder*) allocate_page_folder();
206
207 for (u_dword i = 0; i < user_space_start; i += page_size) {
208 boolean in_stack = i >= (u_dword) stack_pointer - stack_size && i < (u_dword) stack_pointer;
209 *get_page(i, true, kernel_directory) = create_page_entry((const byte*) i, !in_stack, in_stack);
210 }
211
212 // Before we enable paging, we must register our page fault handler.
213 set_interrupt_handler(14, page_fault);
214
215 // Now, enable paging!
216 current_directory = kernel_directory;
217 asm volatile(
218 "mov %0, %%cr3;\n"
219 "mov %%cr0, %%eax;\n"
220 "or $0x80000000, %%eax;\n"
221 "mov %%eax, %%cr0;\n"
222 :: "r"(&current_directory->contents)
223 );
224}
#define size_of(structure)
Definition: heap.h:53
void * ralloc(u_dword size)