New developer version 0.6.8; added select () function; added demonstrating example...
[ZeXOS.git] / kernel / arch / i386 / paging.c
blob9b86d5baf15b530aab9aeef525a3a6130fc96a75
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)
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 * Based on JamesM's paging tutorial
23 #include <arch/io.h>
24 #include <system.h>
25 #include <paging.h>
26 #include <config.h>
27 #include <task.h>
29 extern unsigned *code, *bss, *data, *end;
31 // A bitset of frames - used or free.
32 static unsigned page_nframes;
34 // Amount of RAM Memory in Bytes
35 static unsigned long mem_end_page;
37 // Static function to set a bit in the frames bitset
38 static void page_set_frame (page_ent_t *page_cover, unsigned frame_addr)
40 unsigned frame = frame_addr / 0x1000;
41 unsigned idx = index_from_bit (frame);
42 unsigned off = offset_from_bit (frame);
43 page_cover->frames[idx] |= (0x1 << off);
46 // Static function to clear a bit in the frames bitset
47 static void page_clear_frame (page_ent_t *page_cover, unsigned frame_addr)
49 unsigned frame = frame_addr / 0x1000;
50 unsigned idx = index_from_bit (frame);
51 unsigned off = offset_from_bit (frame);
52 page_cover->frames[idx] &= ~(0x1 << off);
55 // Static function to test if a bit is set.
56 static unsigned page_test_frame (page_ent_t *page_cover, unsigned frame_addr)
58 unsigned frame = frame_addr / 0x1000;
59 unsigned idx = index_from_bit (frame);
60 unsigned off = offset_from_bit (frame);
62 return (page_cover->frames[idx] & (0x1 << off));
65 // Static function to find the first free frame.
66 static unsigned page_first_frame (page_ent_t *page_cover)
68 unsigned i, j;
69 for (i = 0; i < index_from_bit (page_nframes); i++) {
70 // nothing free, exit early.
71 if (page_cover->frames[i] != ~0) {
72 // at least one bit is free here.
73 for (j = 0; j < 32; j++) {
74 unsigned toTest = 0x1 << j;
76 if (!(page_cover->frames[i] & toTest))
77 return (i * 4 * 8 + j);
82 return 0;
85 // Function to allocate a frame.
86 void page_alloc_frame (page_ent_t *page_cover, page_t *page, int supervisor, int rw)
88 if (page->frame != 0)
89 return; // Frame was already allocated, return straight away.
90 else {
91 unsigned idx = page_first_frame (page_cover); // idx is now the index of the first free frame.
93 if (idx == (unsigned) -1) {
94 // PANIC is just a macro that prints a message to the screen then hits an infinite loop.
95 kprintf ("No free frames!");
98 page_set_frame (page_cover, idx * 0x1000); // this frame is now ours!
99 page->present = 1; // Mark it as present.
100 page->rw = (rw) ? 1 : 0; // Should the page be writeable?
101 page->user = (supervisor) ? 0 : 1; // Should the page be user-mode?
102 page->frame = idx;
106 // Function to deallocate a frame.
107 void page_free_frame (page_ent_t *page_cover, page_t *page)
109 unsigned frame;
110 if (!(frame = page->frame)) {
111 return; // The given page didn't actually have an allocated frame!
112 } else {
113 page_clear_frame (page_cover, frame); // Frame is now free again.
114 page->frame = 0x0; // Page now doesn't have a frame.
118 void page_dir_switch (page_dir_t *page_dir)
120 #ifdef CONFIG_MEM_PAGING
121 page_cover_curr = page_dir;
123 write_cr3 (&page_dir->tables_phys); // put that page directory address into CR3
124 #endif
127 page_t *page_get (page_dir_t *dir, unsigned address, int make)
129 // Turn the address into an index.
130 address /= 0x1000;
131 // Find the page table containing this address.
132 unsigned table_idx = address / 1024;
134 // If this table is already assigned
135 if (dir->tables[table_idx])
136 return &dir->tables[table_idx]->pages[address % 1024];
137 else if (make) {
138 unsigned tmp;
139 dir->tables[table_idx] = (page_table_t *) pmalloc_ext (sizeof (page_table_t), (unsigned *) &tmp);
141 memset (dir->tables[table_idx], 0, 0x1000);
142 dir->tables_phys[table_idx] = tmp | 0x7; // PRESENT, RW, US.
144 return &dir->tables[table_idx]->pages[address % 1024];
147 return 0;
150 page_ent_t *page_cover_create ()
152 #ifdef CONFIG_MEM_PAGING
153 /* Alloc memory for new page_ent_t * structure */
154 page_ent_t *page_cover = (page_ent_t *) kmalloc (sizeof (page_ent_t));
156 if (!page_cover)
157 return 0;
159 // Alloc page aligned memory for new page directory
160 page_cover->page_dir = (page_dir_t *) pmalloc (sizeof (page_dir_t));
162 if (!page_cover->page_dir) {
163 kfree (page_cover);
164 return 0;
167 memset (page_cover->page_dir, 0, sizeof (page_dir_t));
169 page_cover->frames = (unsigned *) kmalloc (index_from_bit (page_nframes));
171 if (!page_cover->frames) {
172 pfree (page_cover->page_dir);
173 kfree (page_cover);
174 return 0;
177 memset (page_cover->frames, 0, index_from_bit (page_nframes));
179 return page_cover;
180 #else
181 return 0;
182 #endif
185 unsigned page_cover_delete (page_ent_t *page_cover)
187 if (!page_cover)
188 return 0;
190 if (!page_cover->page_dir)
191 return 0;
193 if (!page_cover->frames)
194 return 0;
196 pfree (page_cover->page_dir);
198 kfree (page_cover);
200 kfree (page_cover->frames);
202 return 1;
205 unsigned page_mmap (page_ent_t *page_cover, void *from, void *to, unsigned kernel, unsigned writeable)
207 #ifdef CONFIG_MEM_PAGING
208 if (!page_cover)
209 return 0;
211 // kprintf ("l: 0x%x - 0x%x : ", from, to);
213 page_cover_curr = page_cover->page_dir;
215 /* There is start address, where we begin mapping */
216 unsigned i = (unsigned) from;
218 /* Loop for increase next 4kB of memory from start */
219 while (i < ((unsigned) to) ) {
220 page_alloc_frame (page_cover, page_get (page_cover->page_dir, i, 1), kernel, writeable);
221 i += 0x1000;
223 #endif
224 return 1;
227 unsigned page_unmmap (page_ent_t *page_cover, void *from, void *to)
229 #ifdef CONFIG_MEM_PAGING
230 if (!page_cover)
231 return 0;
233 /* There is start address, where we begin unmapping */
234 unsigned i = (unsigned) from;
236 /* Loop for increase next 4kB of memory from start */
237 while (i < ((unsigned) to) ) {
238 page_free_frame (page_cover, page_get (page_cover->page_dir, i, 0));
240 unsigned address = i;
242 // Turn the address into an index.
243 address /= 0x1000;
244 // Find the page table containing this address.
245 unsigned table_idx = address / 1024;
247 pfree (page_cover->page_dir->tables[table_idx]);
249 i += 0x1000;
251 #endif
252 return 1;
255 unsigned page_fault (struct regs *r)
257 // A page fault has occurred.
258 // The faulting address is stored in the CR2 register.
259 unsigned faulting_address;
260 asm volatile("mov %%cr2, %0" : "=r" (faulting_address));
262 // The error code gives us details of what happened.
263 int present = !(r->err_code & 0x1); // Page not present
264 int rw = r->err_code & 0x2; // Write operation?
265 int us = r->err_code & 0x4; // Processor was in user-mode?
266 int reserved = r->err_code & 0x8; // Overwritten CPU-reserved bits of page entry?
267 int id = r->err_code & 0x10; // Caused by an instruction fetch?
269 video_color (14, 0);
271 // Output an error message.
272 kprintf ("\nERROR -> page flags: ");
274 if (present)
275 kprintf ("present ");
276 if (rw)
277 kprintf ("read-only ");
278 if (us)
279 kprintf ("user-mode ");
280 if (reserved)
281 kprintf ("reserved ");
283 kprintf ("\n\tFault at 0x%x address\n", faulting_address);
285 return proc_page_fault ();
288 unsigned paging_enable ()
290 #ifdef CONFIG_MEM_PAGING
291 unsigned long flags = read_cr0 ();
293 if (flags & PAGING_BIT)
294 return 0;
296 write_cr0 (flags | PAGING_BIT); // set the paging bit in CR0 to 1
297 #endif
298 return 1;
301 unsigned paging_disable ()
303 #ifdef CONFIG_MEM_PAGING
304 unsigned long flags = read_cr0 ();
306 if (!(flags & PAGING_BIT))
307 return 0;
309 flags &= ~PAGING_BIT;
311 write_cr0 (flags); // set the paging bit in CR0 to 0
312 #endif
315 unsigned int init_paging ()
317 #ifdef CONFIG_MEM_PAGING
318 // The size of physical memory.
319 mem_end_page = mem_ext * 1024 * 1024;
321 page_nframes = mem_end_page / 0x1000;
322 #endif
324 return 1;