MM touchups
[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,
20                len, prot, flags, fd, offset);
21         #ifdef __i386__
22         if (fd || offset) {
23                 printk("[kernel] mmap() does not support files yet.\n");
24                 return (void*SAFE)TC(-1);
25         }
26         #endif
27         if (fd && (flags & MAP_SHARED)) {
28                 printk("[kernel] mmap() for files requires !MAP_SHARED.\n");
29                 return (void*)-1;
30         }
31
32         /* TODO: make this work, instead of a ghetto hack
33          * Find a valid range, make sure it doesn't run into the kernel
34          * make sure there's enough memory (not exceeding quotas)
35          * allocate and map the pages, update appropriate structures (vm_region)
36          * return appropriate pointer
37          * Right now, all we can do is give them the range they ask for.
38          * (or try to find one on sparc) */
39         // TODO: race here
40         if (!(flags & MAP_FIXED))
41         {
42                 addr = MIN(addr,UMMAP_START);
43                 addr = (uintptr_t)get_free_va_range(p->env_pgdir,UMMAP_START,len);
44                 assert(!PGOFF(addr));
45         }
46         else if(PGOFF(addr)) { // MAP_FIXED with unaligned address
47                 printk("[kernel] mmap() page align your addr.\n");
48                 return (void*SAFE)TC(-1);
49         }
50
51         int num_pages = ROUNDUP(len, PGSIZE) / PGSIZE;
52         pte_t *a_pte;
53         // TODO: grab the appropriate mm_lock
54         spin_lock_irqsave(&p->proc_lock);
55         // make sure all pages are available, and in a reasonable range
56         // TODO: can probably do this better with vm_regions, and not do it after
57         // getting the free va range
58         // can also consider not mapping to 0x00000000
59         for (int i = 0; i < num_pages; i++) {
60                 a_pte = pgdir_walk(p->env_pgdir, (void*SNT)(addr + i*PGSIZE), 0);
61                 if (a_pte && *a_pte & PTE_P)
62                         goto mmap_abort;
63                 if (addr + i*PGSIZE >= USTACKBOT)
64                         goto mmap_abort;
65         }
66         page_t *a_page;
67         for (int i = 0; i < num_pages; i++) {
68                 if (upage_alloc(p, &a_page, 1))
69                         goto mmap_abort;
70                 // TODO: TLB shootdown if replacing an old mapping (depends on semantics
71                 // of MAP_FIXED)
72                 // TODO: handle all PROT flags
73                 if (page_insert(p->env_pgdir, a_page, (void*SNT)(addr + i*PGSIZE),
74                                 (prot & PROT_WRITE) ? PTE_USER_RW : PTE_USER_RO)) {
75                         page_free(a_page);
76                         goto mmap_abort;
77                 }
78         }
79
80         #ifndef __i386__
81         // This is dumb--should not read until faulted in.
82         // This is just to get it correct at first
83         // TODO: use mmap2 semantics, offset is a PGSIZE
84         if(fd >= 0)
85         {
86                 char buf[PGSIZE];
87                 int ret = sys_pread(p,fd,(void*)addr,len,offset);
88                 if(ret == -1)
89                         goto mmap_abort;
90         }
91         #endif
92
93         // TODO: release the appropriate mm_lock
94         spin_unlock_irqsave(&p->proc_lock);
95         return (void*SAFE)TC(addr);
96
97         // TODO: if there's a failure, we should go back through the addr+len range
98         // and dealloc everything.  or at least define what we want to do if we run
99         // out of memory.
100         mmap_abort:
101                 // TODO: release the appropriate mm_lock
102                 spin_unlock_irqsave(&p->proc_lock);
103                 // not a kernel problem, like if they ask to mmap a mapped location.
104                 printk("[kernel] mmap() aborted!\n");
105                 // mmap's semantics.  we need a better error propagation system
106                 return (void*SAFE)TC(-1); // this is also ridiculous
107 }
108
109 int mprotect(struct proc* p, void* addr, size_t len, int prot)
110 {
111         printd("mprotect(addr %x, len %x, prot %x)\n",addr,len,prot);
112         if((intptr_t)addr % PGSIZE || (len == 0 && (prot & PROT_UNMAP)))
113         {
114                 set_errno(current_tf,EINVAL);
115                 return -1;
116         }
117
118         // overflow of end is handled in the for loop's parameters
119         char* end = ROUNDUP((char*)addr+len,PGSIZE);
120         if(addr >= (void*)UTOP || end >= (char*)UTOP)
121         {
122                 set_errno(current_tf, (prot & PROT_UNMAP) ? EINVAL : ENOMEM);
123                 return -1;
124         }
125
126         int newperm = (prot & PROT_WRITE) ? PTE_USER_RW :
127                       (prot & (PROT_READ|PROT_EXEC)) ? PTE_USER_RO : 0;
128
129         for(char* a = (char*)addr; a < end; a += PGSIZE)
130         {
131                 pte_t* pte = pgdir_walk(p->env_pgdir,a,0);
132                 if(pte && *pte & PTE_P)
133                 {
134                         // TODO: do munmap() in munmap(), instead of mprotect()
135                         if(prot & PROT_UNMAP)
136                         {
137                                 page_t* page = ppn2page(PTE2PPN(*pte));
138                                 *pte = 0;
139                                 page_decref(page);
140                         }
141                         else
142                                 *pte = (*pte & ~PTE_PERM) | newperm;
143                 }
144                 else
145                 {
146                         set_errno(current_tf,ENOMEM);
147                         return -1;
148                 }
149         }
150
151         //TODO: TLB shootdown - needs to be process wide
152         tlbflush();
153         return 0;
154 }
155
156 int munmap(struct proc* p, void* addr, size_t len)
157 {
158         return mprotect(p, addr, len, PROT_UNMAP);
159 }