stdlibc: ~several fixes
[meinos.git] / kernel2 / memuser.c
blobb0b0f3141cc43bbaf113eeda74f5b655d67ed1b1
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 <memuser.h>
21 #include <llist.h>
22 #include <malloc.h>
23 #include <swap.h>
24 #include <memphys.h>
25 #include <paging.h>
26 #include <memmap.h>
27 #include <procm.h>
28 #include <string.h>
29 #include <sizes.h>
30 #include <syscall.h>
31 #include <debug.h>
32 #include <perm.h>
33 #include <vga.h>
35 /**
36 * Initializes User Memory Management
37 * @return Success?
39 int memuser_init() {
40 memuser_inited = 1;
41 memuser_debug = 0;
42 if (syscall_create(SYSCALL_MEM_MALLOC,memuser_alloc_syscall,1)==-1) return -1;
43 if (syscall_create(SYSCALL_MEM_FREE,memuser_free_syscall,1)==-1) return -1;
44 if (syscall_create(SYSCALL_MEM_GETPHYSADDR,memuser_getphysaddr_syscall,1)==-1) return -1;
45 if (syscall_create(SYSCALL_MEM_GETVGA,memuser_getvga,1)==-1) return -1;
46 if (syscall_create(SYSCALL_MEM_DMA_ALLOC,memuser_dma_alloc,1)==-1) return -1;
47 if (syscall_create(SYSCALL_MEM_DMA_FREE,memuser_dma_free,2)==-1) return -1;
48 if (memphys_dma_init()==-1) return -1;
49 return swap_init();
52 /**
53 * Find a free virtual address
54 * @param addrspace Address space
55 * @param pages Number of pages
56 * @return Address of first page
58 void *memuser_findvirt(addrspace_t *addrspace,size_t pages) {
59 void *virt;
60 size_t found = 0;
62 for (virt = (void*)USERDATA_ADDRESS;virt<(void*)USERDATA_ADDRESS+USERDATA_SIZE;virt+=PAGE_SIZE) {
63 // check if enough pages are found
64 if (found>=pages) return virt-found*PAGE_SIZE;
65 if (ADDR2PTE(virt)==0) {
66 // check for PDE
67 if (!paging_getpde_pd(virt,addrspace->pagedir).exists) {
68 found += 1024;
69 virt += 1023*PAGE_SIZE;
70 continue;
73 // check for PTE
74 if (paging_getpte_pd(virt,addrspace->pagedir).exists) found = 0;
75 else found++;
78 return NULL;
81 /**
82 * Creates an user pagedir
83 * @return User pagedir
85 pd_t memuser_create_pagedir() {
86 if (memuser_debug) kprintf("HELLO\n");
87 pd_t pagedir = paging_cleanpage(memphys_alloc());
88 // copy kernel PTEs
89 pde_t pde;
90 paging_physwrite(pagedir,(void*)PAGEDIR_ADDRESS,ADDR2PDE(KERNELDATA_ADDRESS+KERNELDATA_SIZE));
91 // link last PDE to PD
92 memset(&pde,0,sizeof(pde));
93 pde.page = ADDR2PAGE(pagedir);
94 pde.pagesize = PGSIZE_4K;
95 pde.user = 0;
96 pde.writable = 1;
97 pde.exists = 1;
98 paging_setpde_pd((void*)(4092*MBYTES),pde,pagedir);
99 return pagedir;
103 * Destroys an user pagedir
104 * @param pagedir Pagedir
105 * @return Success?
106 * @todo Check if there are really no user pages left
108 int memuser_destroy_pagedir(pd_t pagedir) {
109 if (paging_curpd==pagedir) paging_loadpagedir(paging_kernelpd);
110 memphys_free(pagedir);
111 return 0;
115 * Creates an user pagetable
116 * @param addrspace Address space
117 * @param virt Virtual address
118 * @return Success?
120 int memuser_create_pagetable(pd_t pagedir,void *virt) {
121 pde_t new;
122 memset(&new,0,sizeof(new));
123 memuser_debug++;
124 new.page = ADDR2PAGE(paging_cleanpage(memphys_alloc()));
125 memuser_debug--;
126 new.pagesize = PGSIZE_4K;
127 new.user = 1;
128 new.writable = 1;
129 new.exists = 1;
130 return paging_setpde_pd(virt,new,pagedir)>=0?0:-1;
134 * Creates an address space
135 * @return Address space
137 addrspace_t *memuser_create_addrspace(proc_t *proc) {
138 addrspace_t *new = malloc(sizeof(addrspace_t));
139 new->proc = proc;
140 new->pages_loaded = llist_create();
141 new->pages_imaginary = llist_create();
142 new->pages_swapped = llist_create();
143 new->pagedir = memuser_create_pagedir();
144 new->stack = NULL;
145 return new;
149 * Destroys an address space
150 * @param addrspace Address space
151 * @return Success?
153 int memuser_destroy_addrspace(addrspace_t *addrspace) {
154 if (paging_curpd!=addrspace->pagedir) return -1;
155 void *page;
157 if (addrspace->stack!=NULL) memuser_destroy_stack(addrspace->stack);
158 while ((page = llist_pop(addrspace->pages_loaded))!=NULL) memphys_free(paging_unmap(page));
159 while ((page = llist_pop(addrspace->pages_imaginary))!=NULL) paging_unmap(page);
160 while ((page = llist_pop(addrspace->pages_swapped))!=NULL) swap_remove(addrspace->proc,page);
161 llist_destroy(addrspace->pages_loaded);
162 llist_destroy(addrspace->pages_imaginary);
163 llist_destroy(addrspace->pages_swapped);
164 memuser_destroy_pagedir(addrspace->pagedir);
165 return 0;
169 * Loads an address space
170 * @param addrspace Address space
171 * @return Success?
173 int memuser_load_addrspace(addrspace_t *addrspace) {
174 if (paging_curpd!=addrspace->pagedir) return paging_loadpagedir(addrspace->pagedir);
175 else return 0;
179 * Allocates an user page
180 * @param addrspace Address space
181 * @param count How many bytes to allocated (should be devidable with PAGE_SIZE)
182 * @return Address
184 void *memuser_alloc(addrspace_t *addrspace,size_t count,int swappable) {
185 void *addr = memuser_findvirt(addrspace,count/PAGE_SIZE);
186 if (addr!=NULL) {
187 size_t i;
188 for (i=0;i<count/PAGE_SIZE;i++) {
189 void *virt = addr+i*PAGE_SIZE;
190 if (!paging_getpde_pd(virt,addrspace->pagedir).exists) memuser_create_pagetable(addrspace->pagedir,virt);
191 pte_t pte;
192 memset(&pte,0,sizeof(pte));
193 pte.exists = 1;
194 pte.swappable = swappable;
195 pte.swapped = 0;
196 pte.writable = 1;
197 pte.user = 1;
198 paging_setpte_pd(virt,pte,addrspace->pagedir);
199 llist_push(addrspace->pages_imaginary,virt);
202 return addr;
206 * Allocates an user page (Syscall)
207 * @param count How many bytes to allocated (should be devidable with PAGE_SIZE)
208 * @return Address
210 void *memuser_alloc_syscall(size_t count) {
211 return memuser_alloc(proc_current->addrspace,count,1);
215 * Frees an user page
216 * @param addrspace Address space
217 * @param page Page
218 * @return Success?
220 int memuser_free(addrspace_t *addrspace,void *page) {
221 if (page==NULL) return 0;
223 pte_t pte = paging_getpte_pd(addrspace->stack,addrspace->pagedir);
224 if (pte.in_memory) {
225 memphys_free(PAGE2ADDR(pte.page));
226 llist_remove(addrspace->pages_loaded,llist_find(addrspace->pages_loaded,page));
228 else if (pte.swapped) {
229 swap_remove(proc_current,page);
230 llist_remove(addrspace->pages_swapped,llist_find(addrspace->pages_swapped,page));
232 else if (pte.cow) {
233 paging_unmap(page);
235 else {
236 paging_unmap(page);
237 llist_remove(addrspace->pages_imaginary,llist_find(addrspace->pages_imaginary,page));
239 memset(&pte,0,sizeof(pte));
240 paging_setpte_pd(page,pte,addrspace->pagedir);
241 return 0;
245 * Frees an user page (Syscall)
246 * @param page Page
247 * @return Success?
249 int memuser_free_syscall(void *page) {
250 return memuser_free(proc_current->addrspace,page);
254 * Gets physical address of a virtual one in specified address space
255 * @param addrspace Address space
256 * @param virt Virtual address
257 * @return Physical address
259 void *memuser_getphysaddr(addrspace_t *addrspace,void *virt) {
260 pte_t pte = paging_getpte_pd(virt,addrspace->pagedir);
261 if (!pte.in_memory) return NULL;
262 return PAGE2ADDR(pte.page);
266 * Gets physical address of a virtual one in specified address space (Syscall)
267 * @param virt Virtual address
268 * @return Physical address
270 void *memuser_getphysaddr_syscall(void *virt) {
271 return memuser_getphysaddr(proc_current->addrspace,virt);
275 * Creates a stack
276 * @param addrspace Address space
277 * @return Stack address
279 void *memuser_create_stack(addrspace_t *addrspace) {
280 addrspace->stack = memuser_alloc(addrspace,PAGE_SIZE,0);
281 if (addrspace->stack!=NULL) return addrspace->stack+PAGE_SIZE-4;
282 else return NULL;
286 * Destroys a stack
287 * @param addrspace Address space
288 * @return Success?
290 int memuser_destroy_stack(addrspace_t *addrspace) {
291 return memuser_free(addrspace,addrspace->stack);
295 * User memory pagefault handler
296 * @param addr Address
297 * @return If Pagefault is a "real" Pagefault
299 int memuser_pagefault(void *addr) {
300 addrspace_t *addrspace = proc_current->addrspace;
301 void *page = PAGEDOWN(addr);
302 pte_t pte = paging_getpte(page);
303 if (!pte.exists) return -1;
304 else if (pte.swapped && pte.in_memory==0) {
305 kprintf("kernel: Catched access to out-swapped memory\n");
306 if (swap_in(proc_current,page)!=-1) {
307 llist_remove(addrspace->pages_swapped,llist_find(addrspace->pages_swapped,page));
308 llist_push(addrspace->pages_loaded,page);
309 swap_remove(proc_current,page);
310 return 0;
312 else return -1;
314 else if (pte.cow && pte.in_memory==1) {
315 void *old = PAGE2ADDR(pte.page);
316 void *new = memphys_alloc();
317 pte.page = ADDR2PAGE(new);
318 pte.cow = 0;
319 pte.writable = 1;
320 paging_setpte(page,pte);
321 paging_physread(page,old,PAGE_SIZE);
322 return 0;
324 else if (pte.in_memory==0) {
325 pte.page = ADDR2PAGE(memphys_alloc());
326 pte.in_memory = 1;
327 paging_setpte(page,pte);
328 llist_remove(addrspace->pages_imaginary,llist_find(addrspace->pages_imaginary,page));
329 llist_push(addrspace->pages_loaded,page);
330 return 0;
332 else return -1;
336 * Allocates a specified page in address space
337 * @param addrspace Address space
338 * @param addr Address to allocate
339 * @param writable If page is writable
340 * @return Success?
342 int memuser_alloc_at(addrspace_t *addrspace,void *addr,void *phys,int writable) {
343 if (!paging_getpde_pd(addr,addrspace->pagedir).exists) {
344 memuser_create_pagetable(addrspace->pagedir,addr);
347 pte_t pte = paging_getpte_pd(addr,addrspace->pagedir);
348 memset(&pte,0,sizeof(pte));
349 pte.exists = 1;
350 pte.in_memory = 1;
351 pte.swappable = 1;
352 pte.writable = writable;
353 pte.user = 1;
354 if (phys==NULL) phys = memphys_alloc();
355 pte.page = ADDR2PAGE(phys);
356 paging_setpte_pd(addr,pte,addrspace->pagedir);
357 llist_push(addrspace->pages_loaded,addr);
358 return 0;
362 * Syncronize current pagedir with all other pagedirs
364 int memuser_syncpds(void *addr) {
365 size_t i;
366 proc_t *proc;
367 pde_t pde = paging_getpde(addr);
369 for (i=0;(proc = llist_get(proc_all,i));i++) {
370 memuser_load_addrspace(proc->addrspace);
371 paging_setpde(addr,pde);
373 if (proc_current!=NULL) memuser_load_addrspace(proc_current->addrspace);
375 return 0;
379 * Maps VGA memory in address space
380 * @return Success?
382 void *memuser_getvga() {
383 void *virt;
384 if ((virt = memuser_findvirt(proc_current->addrspace,1))) paging_map(virt,(void*)VGA_TEXT_ADDRESS,1,proc_current->gid==PERM_ROOTGID);
385 return virt;
389 * Allocates DMA memory and maps it into address space (Syscall)
390 * @param size Size of DMA memory
391 * @return DMA memory
393 void *memuser_dma_alloc(size_t size) {
394 void *phys = memphys_dma_alloc(size/PAGE_SIZE);
395 if (phys!=NULL) {
396 void *virt = memuser_findvirt(proc_current->addrspace,size/PAGE_SIZE);
397 if (virt!=NULL) {
398 size_t i;
399 for (i=0;i<size;i+=PAGE_SIZE) {
400 paging_map(virt+i,phys+i,1,1);
401 //llist_push(proc_current->addrspace->pages_loaded,virt+i);
403 return virt;
406 return NULL;
410 * Unmaps DMA memory and frees it (Syscall)
411 * @param addr DMA memory
412 * @param size Size of DMA memory
414 int memuser_dma_free(void *addr,size_t size) {
415 size_t i;
416 for (i=0;i<size;i+=PAGE_SIZE) {
417 //llist_remove(proc_current->addrspace->pages_loaded,llist_find(proc_current->addrspace->pages_loaded,addr));
418 memphys_dma_free(paging_unmap(addr+i));
420 return 0;