stdlibc: \!perror()
[meinos.git] / kernel2 / paging.c
blobf80496a820eaa1139891cbd89746210eb280cd08
1 /*
2 meinOS - A unix-like x86 microkernel operating system
3 Copyright (C) 2008 Janosch Gräf <janosch.graef@gmx.net>
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
19 #include <sys/types.h>
20 #include <paging.h>
21 #include <string.h>
22 #include <memphys.h>
23 #include <memmap.h>
24 #include <debug.h>
26 /**
27 * Initializes Paging
28 * @return 0=Success; -1=Failure
30 int paging_init() {
31 size_t i;
33 paging_enabled = 0;
34 paging_physrw_lastpage = NULL;
35 paging_physrw_lastpd = NULL;
37 // write initial pagedir
38 paging_kernelpd = (pd_t)KERNELPD_ADDRESS;
39 paging_curpd = paging_kernelpd;
40 memset(paging_kernelpd,0,PAGE_SIZE);
41 paging_kernelpd[0].page = ADDR2PAGE(KERNELPD_ADDRESS+PAGE_SIZE);
42 paging_kernelpd[0].pagesize = PGSIZE_4K;
43 paging_kernelpd[0].user = 1;
44 paging_kernelpd[0].writable = 1;
45 paging_kernelpd[0].exists = 1;
46 paging_kernelpd[1].page = ADDR2PAGE(KERNELPD_ADDRESS+PAGE_SIZE*2);
47 paging_kernelpd[1].pagesize = PGSIZE_4K;
48 paging_kernelpd[1].user = 1;
49 paging_kernelpd[1].writable = 1;
50 paging_kernelpd[1].exists = 1;
51 // write initial pagetables
52 memset((void*)(KERNELPD_ADDRESS+PAGE_SIZE),0,PAGE_SIZE);
53 for (i=0;i<KERNELCODE_ADDRESS+KERNELCODE_SIZE;i+=PAGE_SIZE) {
54 if ((i>=IVT_ADDRESS && i<IVT_ADDRESS+IVT_SIZE) || (i>=BIOSCODE_ADDRESS && i<BIOSCODE_ADDRESS+BIOSCODE_SIZE)) paging_map((void*)i,(void*)i,1,0);
55 else paging_map((void*)i,(void*)i,0,1);
57 // write second pagetables (maybe needed by stack)
58 memset((void*)(KERNELPD_ADDRESS+PAGE_SIZE*2),0,PAGE_SIZE);
60 // map pagedir as last pagetable
61 memset(paging_kernelpd+1023,0,sizeof(pde_t));
62 paging_kernelpd[1023].page = ADDR2PAGE(paging_kernelpd);
63 paging_kernelpd[1023].pagesize = PGSIZE_4K;
64 paging_kernelpd[1023].user = 0;
65 paging_kernelpd[1023].writable = 1;
66 paging_kernelpd[1023].exists = 1;
68 // enable paging
69 paging_loadpagedir(paging_kernelpd);
70 asm("mov %cr0,%eax; or $0x80000000,%eax; mov %eax,%cr0;");
71 paging_enabled = 1;
73 return 0;
76 /**
77 * Loads pagedir
78 * @param pd Pagedir
79 * @return Success?
81 int paging_loadpagedir(pd_t pd) {
82 asm("mov %0,%%cr3;"::"a"(pd));
83 paging_curpd = pd;
84 return 0;
87 /**
88 * Reads bytes from physical memory
89 * @param dest Destination for read data
90 * @param src Physical address as source
91 * @param count How many bytes to read
92 * @return How many bytes read
93 * @note Read does not work over page borders
95 int paging_physread(void *dest,void *src,size_t count) {
96 void *src_rounded = PAGEDOWN(src);
98 if (src_rounded!=paging_physrw_lastpage || paging_curpd!=paging_physrw_lastpd) {
99 if (paging_map((void*)BUFPAGE_ADDRESS,src_rounded,0,1)<0) return 0;
100 paging_physrw_lastpage = src_rounded;
101 paging_physrw_lastpd = paging_curpd;
103 memcpy(dest,((void*)BUFPAGE_ADDRESS)+PAGEOFF(src),count);
104 return count;
108 * Writes bytes to physical memory
109 * @param dest Physical address as destination
110 * @param src Source of data to write
111 * @param count How many bytes to write
112 * @return How many bytes written
113 * @note Write does not work over page borders
114 * @todo Enable caching
116 #include <memuser.h>
117 int paging_physwrite(void *dest,void *src,size_t count) {
118 void *dest_rounded = PAGEDOWN(dest);
120 if (dest_rounded!=paging_physrw_lastpage || paging_curpd!=paging_physrw_lastpd || 1) {
121 if (paging_map((void*)BUFPAGE_ADDRESS,dest_rounded,0,1)<0) return 0;
122 paging_physrw_lastpage = dest_rounded;
123 paging_physrw_lastpd = paging_curpd;
125 memcpy(((void*)BUFPAGE_ADDRESS)+PAGEOFF(dest),src,count);
126 return count;
130 * Clean a whole page
131 * @param page Physical address of page
133 void *paging_cleanpage(void *page) {
134 if (paging_enabled) {
135 void *page_rounded = PAGEDOWN(page);
136 if (page_rounded!=paging_physrw_lastpage || paging_curpd!=paging_physrw_lastpd) {
137 if (paging_map((void*)BUFPAGE_ADDRESS,page_rounded,0,1)<0) return 0;
138 paging_physrw_lastpage = page_rounded;
139 paging_physrw_lastpd = paging_curpd;
141 memset((void*)BUFPAGE_ADDRESS,0,PAGE_SIZE);
143 else memset(page,0,PAGE_SIZE);
144 return page;
148 * Gets a PDE
149 * @param virt Virtual address
150 * @return PDE
152 pde_t paging_getpde(void *virt) {
153 pde_t pde;
154 if (paging_enabled) pde = ((pd_t)PAGEDIR_ADDRESS)[ADDR2PDE(virt)];
155 else pde = paging_curpd[ADDR2PDE(virt)];
156 return pde;
160 * Sets a PDE
161 * @param virt Virtual address
162 * @param pde PDE
163 * @return 0=Success; -1=Failure
165 int paging_setpde(void *virt,pde_t pde) {
166 if (paging_enabled) ((pd_t)PAGEDIR_ADDRESS)[ADDR2PDE(virt)] = pde;
167 else paging_curpd[ADDR2PDE(virt)] = pde;
168 paging_flushtlb(virt);
169 return 0;
173 * Gets a PTE
174 * @param virt Virtual address
175 * @return PTE
177 pte_t paging_getpte(void *virt) {
178 pte_t pte;
179 pde_t pde = paging_getpde(virt);
180 if (!pde.exists) memset(&pte,0,sizeof(pte));
181 else {
182 if (paging_enabled) pte = ((pt_t)(PAGETABLES_ADDRESS+ADDR2PDE(virt)*PAGE_SIZE))[ADDR2PTE(virt)];
183 else pte = ((pt_t)PAGE2ADDR(pde.page))[ADDR2PTE(virt)];
185 return pte;
189 * Sets a PTE
190 * @param virt Virtual address
191 * @param pte PTE
192 * @return 0=Success; -1=Failure
194 int paging_setpte(void *virt,pte_t pte) {
195 pde_t pde = paging_getpde(virt);
196 if (!pde.exists) return -1;
197 else {
198 if (paging_enabled) ((pt_t)(PAGETABLES_ADDRESS+ADDR2PDE(virt)*PAGE_SIZE))[ADDR2PTE(virt)] = pte;
199 else ((pt_t)PAGE2ADDR(pde.page))[ADDR2PTE(virt)] = pte;
200 paging_flushtlb(virt);
201 return 0;
206 * Gets a PDE from not-loaded pagedir
207 * @param virt Virtual address
208 * @param pagedir Pagedir
209 * @return PDE
211 pde_t paging_getpde_pd(void *virt,pd_t pagedir) {
212 if (pagedir==paging_curpd) return paging_getpde(virt);
213 else {
214 pde_t pde;
215 paging_physread(&pde,pagedir+ADDR2PDE(virt),sizeof(pde));
216 return pde;
221 * Sets a PDE in a not-loaded pagedir
222 * @param virt Virtual address
223 * @param pde PDE
224 * @param pd pagedir
225 * @return 0=Success; -1=Failure
227 int paging_setpde_pd(void *virt,pde_t pde,pd_t pagedir) {
228 if (pagedir==paging_curpd) return paging_setpde(virt,pde);
229 else return paging_physwrite(pagedir+ADDR2PDE(virt),&pde,sizeof(pde))==sizeof(pde)?0:-1;
233 * Gets a PTE from not-loaded pagedir
234 * @param virt Virtual address
235 * @param pagedir Pagedir
236 * @return PTE
238 pte_t paging_getpte_pd(void *virt,pd_t pagedir) {
239 if (pagedir==paging_curpd) return paging_getpte(virt);
240 else {
241 pt_t pt = PAGE2ADDR(paging_getpde_pd(virt,pagedir).page);
242 pte_t pte;
243 if (pt==NULL) memset(&pte,0,sizeof(pte));
244 else paging_physread(&pte,pt+ADDR2PTE(virt),sizeof(pte));
245 return pte;
250 * Sets a PTE in a not-loaded pagedir
251 * @param virt Virtual address
252 * @param pte PTE
253 * @param pd pagedir
254 * @return 0=Success; -1=Failure
256 int paging_setpte_pd(void *virt,pte_t pte,pd_t pagedir) {
257 if (pagedir==paging_curpd) return paging_setpte(virt,pte);
258 else {
259 pt_t pt = PAGE2ADDR(paging_getpde_pd(virt,pagedir).page);
260 int ret = paging_physwrite(pt+ADDR2PTE(virt),&pte,sizeof(pte))==sizeof(pte)?0:-1;
261 paging_flushtlb(virt);
262 return ret;
267 * Maps a page (extended)
268 * @param virt Virtual address
269 * @param phys Physical address
270 * @param user Whether page is accessable by user
271 * @param writable Whether page is writable
272 * @param swappable Whether page is swappable
273 * @param cow Whether page is COW
274 * @param pagedir Pagedir to do mapping in
275 * @return 0=Success; -1=Failure
277 int paging_map_pd(void *virt,void *phys,int user,int writable,int swappable,int cow,pd_t pagedir) {
278 if (!paging_getpde_pd(virt,pagedir).exists) {
279 pt_t pagetable = (pt_t)(PAGETABLES_ADDRESS+ADDR2PDE(virt)*PAGE_SIZE);
280 pde_t new;
281 memset(&new,0,sizeof(new));
282 new.page = ADDR2PAGE(memphys_alloc());
283 new.pagesize = PGSIZE_4K;
284 new.user = 1;
285 new.writable = 1;
286 new.exists = 1;
287 if (paging_setpde_pd(virt,new,pagedir)<0) return -1;
288 paging_flushtlb(pagetable);
289 memset(pagetable,0,PAGE_SIZE);
290 if (pagedir!=paging_curpd) kprintf("kernel: new pagetable\n");
292 pte_t new;
293 memset(&new,0,sizeof(new));
294 new.page = ADDR2PAGE(phys);
295 new.exists = 1;
296 new.user = user?1:0;
297 new.writable = writable?1:0;
298 new.in_memory = 1;
299 new.swappable = swappable;
300 new.cow = cow;
301 if (paging_setpte_pd(virt,new,pagedir)<0) return -1;
302 if (pagedir!=paging_curpd) {
303 kprintf("kernel: mapped 0x%x to 0x%x\n",virt,PAGE2ADDR(new.page));
305 return 0;
309 * Unmaps a page and returns physical address of it
310 * @param virt Virtual address
311 * @return Physical address
313 void *paging_unmap(void *virt) {
314 void *addr = PAGE2ADDR(paging_getpte(virt).page);
315 pte_t pte;
316 if (!pte.in_memory) addr = NULL;
317 memset(&pte,0,sizeof(pte));
318 paging_setpte(virt,pte);
319 return addr;
323 * Gets physical address
324 * @param virt Virtual address
325 * @return Physical address
327 void *paging_getphysaddr(void *virt) {
328 pte_t pte = paging_getpte(virt);
329 if (pte.exists && pte.in_memory) return PAGE2ADDR(pte.page)+PAGEOFF(virt);
330 else return NULL;