Modify the trapframe sigcontext, ucontext, etc. Add %gs to the trapframe
[dragonfly.git] / sys / vm / vm_vmspace.c
1 /*
2  * Copyright (c) 2006 The DragonFly Project.  All rights reserved.
3  * 
4  * This code is derived from software contributed to The DragonFly Project
5  * by Matthew Dillon <dillon@backplane.com>
6  * 
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in
15  *    the documentation and/or other materials provided with the
16  *    distribution.
17  * 3. Neither the name of The DragonFly Project nor the names of its
18  *    contributors may be used to endorse or promote products derived
19  *    from this software without specific, prior written permission.
20  * 
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
25  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  * 
34  * $DragonFly: src/sys/vm/vm_vmspace.c,v 1.8 2007/01/08 03:33:43 dillon Exp $
35  */
36 #include "opt_ddb.h"
37
38 #include <sys/param.h>
39 #include <sys/kernel.h>
40 #include <sys/systm.h>
41 #include <sys/sysproto.h>
42 #include <sys/kern_syscall.h>
43 #include <sys/mman.h>
44 #include <sys/proc.h>
45 #include <sys/malloc.h>
46 #include <sys/sysctl.h>
47 #include <sys/vkernel.h>
48 #include <sys/vmspace.h>
49 #include <sys/spinlock2.h>
50
51 #include <vm/vm_extern.h>
52 #include <vm/pmap.h>
53 #include <ddb/ddb.h>
54
55 #include <machine/vmparam.h>
56
57 static struct vmspace_entry *vkernel_find_vmspace(struct vkernel_common *vc,
58                                                   void *id);
59 static void vmspace_entry_delete(struct vmspace_entry *ve,
60                                  struct vkernel_common *vc);
61
62 static MALLOC_DEFINE(M_VKERNEL, "vkernel", "VKernel structures");
63
64 /*
65  * vmspace_create (void *id, int type, void *data)
66  *
67  * Create a VMSPACE under the control of the caller with the specified id.
68  * An id of NULL cannot be used.  The type and data fields must currently
69  * be 0.
70  *
71  * The vmspace starts out completely empty.  Memory may be mapped into the
72  * VMSPACE with vmspace_mmap() and MAP_VPAGETABLE section(s) controlled
73  * with vmspace_mcontrol().
74  */
75 int
76 sys_vmspace_create(struct vmspace_create_args *uap)
77 {
78         struct vkernel_common *vc;
79         struct vmspace_entry *ve;
80         struct vkernel *vk;
81
82         if (vkernel_enable == 0)
83                 return (EOPNOTSUPP);
84
85         /*
86          * Create a virtual kernel side-structure for the process if one
87          * does not exist.
88          */
89         if ((vk = curproc->p_vkernel) == NULL) {
90                 vk = kmalloc(sizeof(*vk), M_VKERNEL, M_WAITOK|M_ZERO);
91                 vc = kmalloc(sizeof(*vc), M_VKERNEL, M_WAITOK|M_ZERO);
92                 vc->vc_refs = 1;
93                 spin_init(&vc->vc_spin);
94                 RB_INIT(&vc->vc_root);
95                 vk->vk_common = vc;
96                 curproc->p_vkernel = vk;
97         }
98         vc = vk->vk_common;
99
100         /*
101          * Create a new VMSPACE
102          */
103         if (vkernel_find_vmspace(vc, uap->id))
104                 return (EEXIST);
105         ve = kmalloc(sizeof(struct vmspace_entry), M_VKERNEL, M_WAITOK|M_ZERO);
106         ve->vmspace = vmspace_alloc(VM_MIN_USER_ADDRESS, VM_MAX_USER_ADDRESS);
107         ve->id = uap->id;
108         pmap_pinit2(vmspace_pmap(ve->vmspace));
109         RB_INSERT(vmspace_rb_tree, &vc->vc_root, ve);
110         return (0);
111 }
112
113 /*
114  * vmspace_destroy (void *id)
115  *
116  * Destroy a VMSPACE.
117  */
118 int
119 sys_vmspace_destroy(struct vmspace_destroy_args *uap)
120 {
121         struct vkernel_common *vc;
122         struct vmspace_entry *ve;
123         struct vkernel *vk;
124
125         if ((vk = curproc->p_vkernel) == NULL)
126                 return (EINVAL);
127         vc = vk->vk_common;
128         if ((ve = vkernel_find_vmspace(vc, uap->id)) == NULL)
129                 return (ENOENT);
130         if (ve->refs)
131                 return (EBUSY);
132         vmspace_entry_delete(ve, vc);
133         return(0);
134 }
135
136 /*
137  * vmspace_ctl (void *id, int cmd, struct trapframe *tframe,
138  *              struct vextframe *vframe);
139  *
140  * Transfer control to a VMSPACE.  Control is returned after the specified
141  * number of microseconds or if a page fault, signal, trap, or system call
142  * occurs.  The context is updated as appropriate.
143  */
144 int
145 sys_vmspace_ctl(struct vmspace_ctl_args *uap)
146 {
147         struct vkernel_common *vc;
148         struct vmspace_entry *ve;
149         struct vkernel *vk;
150         struct proc *p;
151         int framesz;
152         int error;
153
154         if ((vk = curproc->p_vkernel) == NULL)
155                 return (EINVAL);
156         vc = vk->vk_common;
157         if ((ve = vkernel_find_vmspace(vc, uap->id)) == NULL)
158                 return (ENOENT);
159
160         switch(uap->cmd) {
161         case VMSPACE_CTL_RUN:
162                 /*
163                  * Save the caller's register context, swap VM spaces, and
164                  * install the passed register context.  Return with
165                  * EJUSTRETURN so the syscall code doesn't adjust the context.
166                  */
167                 p = curproc;
168                 ++ve->refs;
169                 framesz = sizeof(struct trapframe);
170                 vk->vk_current = ve;
171                 vk->vk_save_vmspace = p->p_vmspace;
172                 vk->vk_user_trapframe = uap->tframe;
173                 vk->vk_user_vextframe = uap->vframe;
174                 bcopy(uap->sysmsg_frame, &vk->vk_save_trapframe, framesz);
175                 bcopy(&curthread->td_tls, &vk->vk_save_vextframe.vx_tls,
176                       sizeof(vk->vk_save_vextframe.vx_tls));
177                 error = copyin(uap->tframe, uap->sysmsg_frame, framesz);
178                 if (error == 0)
179                         error = copyin(&uap->vframe->vx_tls, &curthread->td_tls, sizeof(struct savetls));
180                 if (error == 0)
181                         error = cpu_sanitize_frame(uap->sysmsg_frame);
182                 if (error == 0)
183                         error = cpu_sanitize_tls(&curthread->td_tls);
184                 if (error) {
185                         bcopy(&vk->vk_save_trapframe, uap->sysmsg_frame, framesz);
186                         bcopy(&vk->vk_save_vextframe.vx_tls, &curthread->td_tls,
187                               sizeof(vk->vk_save_vextframe.vx_tls));
188                         set_user_TLS();
189                         vk->vk_current = NULL;
190                         vk->vk_save_vmspace = NULL;
191                         --ve->refs;
192                 } else {
193                         pmap_deactivate(p);
194                         p->p_vmspace = ve->vmspace;
195                         pmap_activate(p);
196                         set_user_TLS();
197                         error = EJUSTRETURN;
198                 }
199                 break;
200         default:
201                 error = EOPNOTSUPP;
202                 break;
203         }
204         return(error);
205 }
206
207 /*
208  * vmspace_mmap(id, addr, len, prot, flags, fd, offset)
209  *
210  * map memory within a VMSPACE.  This function is just like a normal mmap()
211  * but operates on the vmspace's memory map.  Most callers use this to create
212  * a MAP_VPAGETABLE mapping.
213  */
214 int
215 sys_vmspace_mmap(struct vmspace_mmap_args *uap)
216 {
217         struct vkernel_common *vc;
218         struct vmspace_entry *ve;
219         struct vkernel *vk;
220         int error;
221
222         if ((vk = curproc->p_vkernel) == NULL)
223                 return (EINVAL);
224         vc = vk->vk_common;
225         if ((ve = vkernel_find_vmspace(vc, uap->id)) == NULL)
226                 return (ENOENT);
227         error = kern_mmap(ve->vmspace, uap->addr, uap->len,
228                           uap->prot, uap->flags,
229                           uap->fd, uap->offset, &uap->sysmsg_resultp);
230         return (error);
231 }
232
233 /*
234  * vmspace_munmap(id, addr, len)
235  *
236  * unmap memory within a VMSPACE.
237  */
238 int
239 sys_vmspace_munmap(struct vmspace_munmap_args *uap)
240 {
241         struct vkernel_common *vc;
242         struct vmspace_entry *ve;
243         struct vkernel *vk;
244         vm_offset_t addr;
245         vm_size_t size, pageoff;
246         vm_map_t map;
247
248         if ((vk = curproc->p_vkernel) == NULL)
249                 return (EINVAL);
250         vc = vk->vk_common;
251         if ((ve = vkernel_find_vmspace(vc, uap->id)) == NULL)
252                 return (ENOENT);
253
254         /*
255          * Copied from sys_munmap()
256          */
257         addr = (vm_offset_t)uap->addr;
258         size = uap->len;
259
260         pageoff = (addr & PAGE_MASK);
261         addr -= pageoff;
262         size += pageoff;
263         size = (vm_size_t)round_page(size);
264         if (addr + size < addr)
265                 return (EINVAL);
266         if (size == 0)
267                 return (0);
268
269         if (VM_MAX_USER_ADDRESS > 0 && addr + size > VM_MAX_USER_ADDRESS)
270                 return (EINVAL);
271         if (VM_MIN_USER_ADDRESS > 0 && addr < VM_MIN_USER_ADDRESS)
272                 return (EINVAL);
273         map = &ve->vmspace->vm_map;
274         if (!vm_map_check_protection(map, addr, addr + size, VM_PROT_NONE))
275                 return (EINVAL);
276         vm_map_remove(map, addr, addr + size);
277         return (0);
278 }
279
280 /* 
281  * vmspace_pread(id, buf, nbyte, flags, offset)
282  *
283  * Read data from a vmspace.  The number of bytes read is returned or
284  * -1 if an unrecoverable error occured.  If the number of bytes read is
285  * less then the request size, a page fault occured in the VMSPACE which
286  * the caller must resolve in order to proceed.
287  */
288 int
289 sys_vmspace_pread(struct vmspace_pread_args *uap)
290 {
291         struct vkernel_common *vc;
292         struct vmspace_entry *ve;
293         struct vkernel *vk;
294
295         if ((vk = curproc->p_vkernel) == NULL)
296                 return (EINVAL);
297         vc = vk->vk_common;
298         if ((ve = vkernel_find_vmspace(vc, uap->id)) == NULL)
299                 return (ENOENT);
300         return (EINVAL);
301 }
302
303 /*
304  * vmspace_pwrite(id, buf, nbyte, flags, offset)
305  *
306  * Write data to a vmspace.  The number of bytes written is returned or
307  * -1 if an unrecoverable error occured.  If the number of bytes written is
308  * less then the request size, a page fault occured in the VMSPACE which
309  * the caller must resolve in order to proceed.
310  */
311 int
312 sys_vmspace_pwrite(struct vmspace_pwrite_args *uap)
313 {
314         struct vkernel_common *vc;
315         struct vmspace_entry *ve;
316         struct vkernel *vk;
317
318         if ((vk = curproc->p_vkernel) == NULL)
319                 return (EINVAL);
320         vc = vk->vk_common;
321         if ((ve = vkernel_find_vmspace(vc, uap->id)) == NULL)
322                 return (ENOENT);
323         return (EINVAL);
324 }
325
326 /*
327  * vmspace_mcontrol(id, addr, len, behav, value)
328  *
329  * madvise/mcontrol support for a vmspace.
330  */
331 int
332 sys_vmspace_mcontrol(struct vmspace_mcontrol_args *uap)
333 {
334         struct vkernel_common *vc;
335         struct vmspace_entry *ve;
336         struct vkernel *vk;
337         vm_offset_t start, end;
338
339         if ((vk = curproc->p_vkernel) == NULL)
340                 return (EINVAL);
341         vc = vk->vk_common;
342         if ((ve = vkernel_find_vmspace(vc, uap->id)) == NULL)
343                 return (ENOENT);
344
345         /*
346          * This code is basically copied from sys_mcontrol()
347          */
348         if (uap->behav < 0 || uap->behav > MADV_CONTROL_END)
349                 return (EINVAL);
350
351         if (VM_MAX_USER_ADDRESS > 0 &&
352                 ((vm_offset_t) uap->addr + uap->len) > VM_MAX_USER_ADDRESS)
353                 return (EINVAL);
354         if (VM_MIN_USER_ADDRESS > 0 && uap->addr < VM_MIN_USER_ADDRESS)
355                 return (EINVAL);
356         if (((vm_offset_t) uap->addr + uap->len) < (vm_offset_t) uap->addr)
357                 return (EINVAL);
358
359         start = trunc_page((vm_offset_t) uap->addr);
360         end = round_page((vm_offset_t) uap->addr + uap->len);
361
362         return (vm_map_madvise(&ve->vmspace->vm_map, start, end,
363                                 uap->behav, uap->value));
364 }
365
366 /*
367  * Red black tree functions
368  */
369 static int rb_vmspace_compare(struct vmspace_entry *, struct vmspace_entry *);
370 RB_GENERATE(vmspace_rb_tree, vmspace_entry, rb_entry, rb_vmspace_compare);
371    
372 /* a->start is address, and the only field has to be initialized */
373 static int
374 rb_vmspace_compare(struct vmspace_entry *a, struct vmspace_entry *b)
375 {
376         if ((char *)a->id < (char *)b->id)
377                 return(-1);
378         else if ((char *)a->id > (char *)b->id)
379                 return(1);
380         return(0);
381 }
382
383 static
384 int
385 rb_vmspace_delete(struct vmspace_entry *ve, void *data)
386 {
387         struct vkernel_common *vc = data;
388
389         KKASSERT(ve->refs == 0);
390         vmspace_entry_delete(ve, vc);
391         return(0);
392 }
393
394 /*
395  * Remove a vmspace_entry from the RB tree and destroy it.  We have to clean
396  * up the pmap, the vm_map, then destroy the vmspace.
397  */
398 static
399 void
400 vmspace_entry_delete(struct vmspace_entry *ve, struct vkernel_common *vc)
401 {
402         RB_REMOVE(vmspace_rb_tree, &vc->vc_root, ve);
403
404         pmap_remove_pages(vmspace_pmap(ve->vmspace),
405                           VM_MIN_USER_ADDRESS, VM_MAX_USER_ADDRESS);
406         vm_map_remove(&ve->vmspace->vm_map,
407                       VM_MIN_USER_ADDRESS, VM_MAX_USER_ADDRESS);
408         vmspace_free(ve->vmspace);
409         kfree(ve, M_VKERNEL);
410 }
411
412
413 static
414 struct vmspace_entry *
415 vkernel_find_vmspace(struct vkernel_common *vc, void *id)
416 {
417         struct vmspace_entry *ve;
418         struct vmspace_entry key;
419
420         key.id = id;
421         ve = RB_FIND(vmspace_rb_tree, &vc->vc_root, &key);
422         return (ve);
423 }
424
425 /*
426  * Manage vkernel refs, used by the kernel when fork()ing or exit()ing
427  * a vkernel process.
428  */
429 void
430 vkernel_inherit(struct proc *p1, struct proc *p2)
431 {
432         struct vkernel_common *vc;
433         struct vkernel *vk;
434
435         vk = p1->p_vkernel;
436         vc = vk->vk_common;
437         KKASSERT(vc->vc_refs > 0);
438         atomic_add_int(&vc->vc_refs, 1);
439         vk = kmalloc(sizeof(*vk), M_VKERNEL, M_WAITOK|M_ZERO);
440         p2->p_vkernel = vk;
441         vk->vk_common = vc;
442 }
443
444 void
445 vkernel_exit(struct proc *p)
446 {
447         struct vkernel_common *vc;
448         struct vmspace_entry *ve;
449         struct vkernel *vk;
450         int freeme = 0;
451
452         vk = p->p_vkernel;
453         p->p_vkernel = NULL;
454         vc = vk->vk_common;
455         vk->vk_common = NULL;
456
457         /*
458          * Restore the original VM context if we are killed while running
459          * a different one.
460          *
461          * This isn't supposed to happen.  What is supposed to happen is
462          * that the process should enter vkernel_trap() before the handling
463          * the signal.
464          */
465         if ((ve = vk->vk_current) != NULL) {
466                 kprintf("Killed with active VC, notify kernel list\n");
467 #ifdef DDB
468                 db_print_backtrace();
469 #endif
470                 vk->vk_current = NULL;
471                 pmap_deactivate(p);
472                 p->p_vmspace = vk->vk_save_vmspace;
473                 pmap_activate(p);
474                 vk->vk_save_vmspace = NULL;
475                 KKASSERT(ve->refs > 0);
476                 --ve->refs;
477         }
478
479         /*
480          * Dereference the common area
481          */
482         KKASSERT(vc->vc_refs > 0);
483         spin_lock_wr(&vc->vc_spin);
484         if (--vc->vc_refs == 0) 
485                 freeme = 1;
486         spin_unlock_wr(&vc->vc_spin);
487
488         if (freeme) {
489                 RB_SCAN(vmspace_rb_tree, &vc->vc_root, NULL,
490                         rb_vmspace_delete, vc);
491                 kfree(vc, M_VKERNEL);
492         }
493         kfree(vk, M_VKERNEL);
494 }
495
496 /*
497  * A VM space under virtual kernel control trapped out or made a system call
498  * or otherwise needs to return control to the virtual kernel context.
499  */
500 int
501 vkernel_trap(struct proc *p, struct trapframe *frame)
502 {
503         struct vmspace_entry *ve;
504         struct vkernel *vk;
505         int error;
506
507         /*
508          * Which vmspace entry was running?
509          */
510         vk = p->p_vkernel;
511         ve = vk->vk_current;
512         vk->vk_current = NULL;
513         KKASSERT(ve != NULL);
514
515         /*
516          * Switch the process context back to the virtual kernel's VM space.
517          */
518         pmap_deactivate(p);
519         p->p_vmspace = vk->vk_save_vmspace;
520         pmap_activate(p);
521         vk->vk_save_vmspace = NULL;
522         KKASSERT(ve->refs > 0);
523         --ve->refs;
524
525         /*
526          * Copy the emulated process frame to the virtual kernel process.
527          * The emulated process cannot change TLS descriptors so don't
528          * bother saving them, we already have a copy.
529          *
530          * Restore the virtual kernel's saved context so the virtual kernel
531          * process can resume.
532          */
533         error = copyout(frame, vk->vk_user_trapframe, sizeof(*frame));
534         bcopy(&vk->vk_save_trapframe, frame, sizeof(*frame));
535         bcopy(&vk->vk_save_vextframe.vx_tls, &curthread->td_tls,
536               sizeof(vk->vk_save_vextframe.vx_tls));
537         set_user_TLS();
538         return(error);
539 }
540