Fixed ZDE build - missing header file
[ZeXOS.git] / kernel / arch / i386 / paging.c
blobc288b34aca82e854127741bf8dade8b9a46031a8
1 /*
2 * ZeX/OS
3 * Copyright (C) 2007 Tomas 'ZeXx86' Jedrzejek (zexx86@zexos.org)
4 * Copyright (C) 2008 Tomas 'ZeXx86' Jedrzejek (zexx86@zexos.org)
5 * Copyright (C) 2009 Tomas 'ZeXx86' Jedrzejek (zexx86@zexos.org)
6 * Copyright (C) 2010 Tomas 'ZeXx86' Jedrzejek (zexx86@zexos.org)
8 * This program is free software: you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation, either version 3 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
21 * Based on JamesM's paging tutorial
24 #include <arch/io.h>
25 #include <system.h>
26 #include <paging.h>
27 #include <config.h>
28 #include <task.h>
30 // A bitset of frames - used or free.
31 static unsigned page_nframes;
33 // Amount of RAM Memory in Bytes
34 static unsigned long mem_end_page;
36 // Static function to set a bit in the frames bitset
37 static void page_set_frame (page_ent_t *page_cover, unsigned frame_addr)
39 unsigned frame = frame_addr / 0x1000;
40 unsigned idx = index_from_bit (frame);
41 unsigned off = offset_from_bit (frame);
42 page_cover->frames[idx] |= (0x1 << off);
45 // Static function to clear a bit in the frames bitset
46 static void page_clear_frame (page_ent_t *page_cover, unsigned frame_addr)
48 unsigned frame = frame_addr / 0x1000;
49 unsigned idx = index_from_bit (frame);
50 unsigned off = offset_from_bit (frame);
51 page_cover->frames[idx] &= ~(0x1 << off);
54 // Static function to test if a bit is set.
55 static unsigned page_test_frame (page_ent_t *page_cover, unsigned frame_addr)
57 unsigned frame = frame_addr / 0x1000;
58 unsigned idx = index_from_bit (frame);
59 unsigned off = offset_from_bit (frame);
61 return (page_cover->frames[idx] & (0x1 << off));
64 // Static function to find the first free frame.
65 static unsigned page_first_frame (page_ent_t *page_cover)
67 unsigned i, j;
68 for (i = 0; i < index_from_bit (page_nframes); i++) {
69 // nothing free, exit early.
70 if (page_cover->frames[i] != ~0) {
71 // at least one bit is free here.
72 for (j = 0; j < 32; j++) {
73 unsigned toTest = 0x1 << j;
75 if (!(page_cover->frames[i] & toTest))
76 return (i * 4 * 8 + j);
81 return 0;
84 // Function to allocate a frame.
85 void page_alloc_frame (page_ent_t *page_cover, page_t *page, int supervisor, int rw)
87 if (page->frame != 0)
88 return; // Frame was already allocated, return straight away.
89 else {
90 unsigned idx = page_first_frame (page_cover); // idx is now the index of the first free frame.
92 if (idx == (unsigned) -1) {
93 // PANIC is just a macro that prints a message to the screen then hits an infinite loop.
94 kprintf ("No free frames!");
97 page_set_frame (page_cover, idx * 0x1000); // this frame is now ours!
98 page->present = 1; // Mark it as present.
99 page->rw = (rw) ? 1 : 0; // Should the page be writeable?
100 page->user = (supervisor) ? 0 : 1; // Should the page be user-mode?
101 page->frame = idx;
105 // Function to deallocate a frame.
106 void page_free_frame (page_ent_t *page_cover, page_t *page)
108 unsigned frame;
109 if (!(frame = page->frame)) {
110 return; // The given page didn't actually have an allocated frame!
111 } else {
112 page_clear_frame (page_cover, frame); // Frame is now free again.
113 page->frame = 0x0; // Page now doesn't have a frame.
117 void page_dir_switch (page_dir_t *page_dir)
119 #ifdef CONFIG_MEM_PAGING
120 page_cover_curr = page_dir;
122 write_cr3 (&page_dir->tables_phys); // put that page directory address into CR3
123 #endif
126 page_t *page_get (page_dir_t *dir, unsigned address, int make)
128 // Turn the address into an index.
129 address /= 0x1000;
130 // Find the page table containing this address.
131 unsigned table_idx = address / 1024;
133 // If this table is already assigned
134 if (dir->tables[table_idx])
135 return &dir->tables[table_idx]->pages[address % 1024];
136 else if (make) {
137 unsigned tmp;
138 dir->tables[table_idx] = (page_table_t *) pmalloc_ext (sizeof (page_table_t), (unsigned *) &tmp);
140 memset (dir->tables[table_idx], 0, 0x1000);
141 dir->tables_phys[table_idx] = tmp | 0x7; // PRESENT, RW, US.
143 return &dir->tables[table_idx]->pages[address % 1024];
146 return 0;
149 page_ent_t *page_cover_create ()
151 #ifdef CONFIG_MEM_PAGING
152 /* Alloc memory for new page_ent_t * structure */
153 page_ent_t *page_cover = (page_ent_t *) kmalloc (sizeof (page_ent_t));
155 if (!page_cover)
156 return 0;
158 // Alloc page aligned memory for new page directory
159 page_cover->page_dir = (page_dir_t *) pmalloc (sizeof (page_dir_t));
161 if (!page_cover->page_dir) {
162 kfree (page_cover);
163 return 0;
166 memset (page_cover->page_dir, 0, sizeof (page_dir_t));
168 page_cover->frames = (unsigned *) kmalloc (index_from_bit (page_nframes));
170 if (!page_cover->frames) {
171 pfree (page_cover->page_dir);
172 kfree (page_cover);
173 return 0;
176 memset (page_cover->frames, 0, index_from_bit (page_nframes));
178 return page_cover;
179 #else
180 return 0;
181 #endif
184 unsigned page_cover_delete (page_ent_t *page_cover)
186 if (!page_cover)
187 return 0;
189 if (!page_cover->page_dir)
190 return 0;
192 if (!page_cover->frames)
193 return 0;
195 pfree (page_cover->page_dir);
197 kfree (page_cover);
199 kfree (page_cover->frames);
201 return 1;
204 unsigned page_mmap (page_ent_t *page_cover, void *from, void *to, unsigned kernel, unsigned writeable)
206 #ifdef CONFIG_MEM_PAGING
207 if (!page_cover)
208 return 0;
210 // kprintf ("l: 0x%x - 0x%x : ", from, to);
212 page_cover_curr = page_cover->page_dir;
214 /* There is start address, where we begin mapping */
215 unsigned i = (unsigned) from;
217 /* Loop for increase next 4kB of memory from start */
218 while (i < ((unsigned) to) ) {
219 page_alloc_frame (page_cover, page_get (page_cover->page_dir, i, 1), kernel, writeable);
220 i += 0x1000;
222 #endif
223 return 1;
226 unsigned page_unmmap (page_ent_t *page_cover, void *from, void *to)
228 #ifdef CONFIG_MEM_PAGING
229 if (!page_cover)
230 return 0;
232 /* There is start address, where we begin unmapping */
233 unsigned i = (unsigned) from;
235 /* Loop for increase next 4kB of memory from start */
236 while (i < ((unsigned) to) ) {
237 page_free_frame (page_cover, page_get (page_cover->page_dir, i, 0));
239 unsigned address = i;
241 // Turn the address into an index.
242 address /= 0x1000;
243 // Find the page table containing this address.
244 unsigned table_idx = address / 1024;
246 pfree (page_cover->page_dir->tables[table_idx]);
248 i += 0x1000;
250 #endif
251 return 1;
254 unsigned page_fault (struct regs *r)
256 // A page fault has occurred.
257 // The faulting address is stored in the CR2 register.
258 unsigned faulting_address;
259 asm volatile("mov %%cr2, %0" : "=r" (faulting_address));
261 // The error code gives us details of what happened.
262 int present = !(r->err_code & 0x1); // Page not present
263 int rw = r->err_code & 0x2; // Write operation?
264 int us = r->err_code & 0x4; // Processor was in user-mode?
265 int reserved = r->err_code & 0x8; // Overwritten CPU-reserved bits of page entry?
266 int id = r->err_code & 0x10; // Caused by an instruction fetch?
268 video_color (14, 0);
270 // Output an error message.
271 kprintf ("\nERROR -> page flags: ");
273 if (present)
274 kprintf ("present ");
275 if (rw)
276 kprintf ("read-only ");
277 if (us)
278 kprintf ("user-mode ");
279 if (reserved)
280 kprintf ("reserved ");
282 kprintf ("\n\tFault at 0x%x address\n", faulting_address);
284 return proc_page_fault ();
287 unsigned paging_enable ()
289 #ifdef CONFIG_MEM_PAGING
290 unsigned long flags = read_cr0 ();
292 if (flags & PAGING_BIT)
293 return 0;
295 write_cr0 (flags | PAGING_BIT); // set the paging bit in CR0 to 1
296 #endif
297 return 1;
300 unsigned paging_disable ()
302 #ifdef CONFIG_MEM_PAGING
303 unsigned long flags = read_cr0 ();
305 if (!(flags & PAGING_BIT))
306 return 0;
308 flags &= ~PAGING_BIT;
310 write_cr0 (flags); // set the paging bit in CR0 to 0
311 #endif
314 unsigned int init_paging ()
316 #ifdef CONFIG_MEM_PAGING
317 // The size of physical memory.
318 mem_end_page = mem_ext * 1024 * 1024;
320 page_nframes = mem_end_page / 0x1000;
321 #endif
323 return 1;