f5c0fb331139788c3ab99006feef52b7663a7021
[akaros.git] / kern / src / mm.c
1 /*
2  * Copyright (c) 2009 The Regents of the University of California
3  * Barret Rhoden <brho@cs.berkeley.edu>
4  * See LICENSE for details.
5  *
6  */
7
8 #include <ros/common.h>
9 #include <ros/mman.h>
10 #include <pmap.h>
11 #include <mm.h>
12 #include <process.h>
13 #include <stdio.h>
14 #include <syscall.h>
15
16 void *mmap(struct proc *p, uintptr_t addr, size_t len, int prot, int flags,
17            int fd, size_t offset)
18 {
19         printd("mmap(addr %x, len %x, prot %x, flags %x, fd %x, off %x)\n",addr,len,prot,flags,fd,offset);
20         if (flags & MAP_SHARED) {
21                 printk("[kernel] mmap() for files requires !MAP_SHARED.\n");
22                 return (void*)-1;
23         }
24
25         /*if (fd || offset) {
26                 printk("[kernel] mmap() does not support files yet.\n");
27                 return (void*SAFE)TC(-1);
28         }*/
29         /* TODO: make this work, instead of a ghetto hack
30          * Find a valid range, make sure it doesn't run into the kernel
31          * make sure there's enough memory (not exceeding quotas)
32          * allocate and map the pages, update appropriate structures (vm_region)
33          * return appropriate pointer
34          * Right now, all we can do is give them the range they ask for.
35          */
36         if (!(flags & MAP_FIXED))
37         {
38                 addr = MIN(addr,UMMAP_START);
39                 addr = (uintptr_t)get_free_va_range(p->env_pgdir,UMMAP_START,len);
40         }
41         else if(PGOFF(addr)) { // MAP_FIXED with unaligned address
42                 printk("[kernel] mmap() page align your addr.\n");
43                 return (void*SAFE)TC(-1);
44         }
45
46         int num_pages = ROUNDUP(len, PGSIZE) / PGSIZE;
47         pte_t *a_pte;
48         // TODO: grab the appropriate mm_lock
49         spin_lock_irqsave(&p->proc_lock);
50         // make sure all pages are available, and in a reasonable range
51         // TODO: can probably do this better with vm_regions.
52         // can also consider not mapping to 0x00000000
53         for (int i = 0; i < num_pages; i++) {
54                 a_pte = pgdir_walk(p->env_pgdir, (void*SNT)(addr + i*PGSIZE), 0);
55                 if (!(flags & MAP_FIXED) && a_pte && *a_pte & PTE_P)
56                         goto mmap_abort;
57                 if (addr + i*PGSIZE >= USTACKBOT)
58                         goto mmap_abort;
59         }
60         page_t *a_page;
61         for (int i = 0; i < num_pages; i++) {
62                 if (upage_alloc(p, &a_page, 1))
63                         goto mmap_abort;
64                 // TODO: TLB shootdown if replacing an old mapping
65                 if (page_insert(p->env_pgdir, a_page, (void*SNT)(addr + i*PGSIZE),
66                                 1||(prot & PROT_WRITE) ? PTE_USER_RW : PTE_USER_RO)) {
67                         page_free(a_page);
68                         goto mmap_abort;
69                 }
70         }
71
72         // This is dumb--should not read until faulted in.
73         // This is just to get it correct at first
74         if(fd >= 0)
75         {
76                 char buf[PGSIZE];
77                 int ret = sys_pread(p,fd,(void*)addr,len,offset);
78                 if(ret == -1)
79                         goto mmap_abort;
80         }
81
82         // TODO: release the appropriate mm_lock
83         spin_unlock_irqsave(&p->proc_lock);
84         return (void*SAFE)TC(addr);
85
86         // TODO: if there's a failure, we should go back through the addr+len range
87         // and dealloc everything.  or at least define what we want to do if we run
88         // out of memory.
89         mmap_abort:
90                 // TODO: release the appropriate mm_lock
91                 spin_unlock_irqsave(&p->proc_lock);
92                 // not a kernel problem, like if they ask to mmap a mapped location.
93                 printk("[kernel] mmap() aborted!\n");
94                 // mmap's semantics.  we need a better error propagation system
95                 return (void*SAFE)TC(-1); // this is also ridiculous
96 }
97
98 intreg_t sys_mprotect(struct proc* p, void* addr, size_t len, int prot)
99 {
100         printd("mprotect(addr %x, len %x, prot %x)\n",addr,len,prot);
101         if((intptr_t)addr % PGSIZE || (len == 0 && (prot & PROT_UNMAP)))
102         {
103                 set_errno(current_tf,EINVAL);
104                 return -1;
105         }
106
107         char* end = ROUNDUP((char*)addr+len,PGSIZE);
108         if(addr >= (void*)UTOP || end >= (char*)UTOP)
109         {
110                 set_errno(current_tf, (prot & PROT_UNMAP) ? EINVAL : ENOMEM);
111                 return -1;
112         }
113
114         int newperm = (prot & PROT_WRITE) ? PTE_USER_RW :
115                       (prot & (PROT_READ|PROT_EXEC)) ? PTE_USER_RO : 0;
116
117         for(char* a = (char*)addr; a < end; a += PGSIZE)
118         {
119                 pte_t* pte = pgdir_walk(p->env_pgdir,a,0);
120                 if(pte && *pte & PTE_P)
121                 {
122                         if(prot & PROT_UNMAP)
123                         {
124                                 page_t* page = ppn2page(PTE2PPN(*pte));
125                                 *pte = 0;
126                                 page_decref(page);
127                         }
128                         else
129                                 *pte = (*pte & ~PTE_PERM) | newperm;
130                 }
131                 else
132                 {
133                         set_errno(current_tf,ENOMEM);
134                         return -1;
135                 }
136         }
137
138         //TODO: TLB shootdown
139         tlbflush();
140         return 0;
141 }
142
143 intreg_t sys_munmap(struct proc* p, void* addr, size_t len)
144 {
145         return sys_mprotect(p,addr,len,PROT_UNMAP);
146 }