]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/i386/i386/sys_machdep.c
Get kernel profiling on SMP systems closer to working by replacing the
[FreeBSD/FreeBSD.git] / sys / i386 / i386 / sys_machdep.c
1 /*-
2  * Copyright (c) 1990 The Regents of the University of California.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *      This product includes software developed by the University of
16  *      California, Berkeley and its contributors.
17  * 4. Neither the name of the University nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  *
33  *      from: @(#)sys_machdep.c 5.5 (Berkeley) 1/19/91
34  * $FreeBSD$
35  *
36  */
37
38 #include <sys/param.h>
39 #include <sys/systm.h>
40 #include <sys/lock.h>
41 #include <sys/malloc.h>
42 #include <sys/mutex.h>
43 #include <sys/proc.h>
44 #include <sys/smp.h>
45 #include <sys/sysproto.h>
46 #include <sys/user.h>
47
48 #include <vm/vm.h>
49 #include <vm/pmap.h>
50 #include <vm/vm_map.h>
51 #include <vm/vm_extern.h>
52
53 #include <machine/cpu.h>
54 #include <machine/pcb_ext.h>    /* pcb.h included by sys/user.h */
55 #include <machine/sysarch.h>
56
57 #include <vm/vm_kern.h>         /* for kernel_map */
58
59 #define MAX_LD 8192
60 #define LD_PER_PAGE 512
61 #define NEW_MAX_LD(num)  ((num + LD_PER_PAGE) & ~(LD_PER_PAGE-1))
62 #define SIZE_FROM_LARGEST_LD(num) (NEW_MAX_LD(num) << 3)
63
64
65
66 static int i386_get_ldt __P((struct proc *, char *));
67 static int i386_set_ldt __P((struct proc *, char *));
68 static int i386_get_ioperm      __P((struct proc *, char *));
69 static int i386_set_ioperm      __P((struct proc *, char *));
70 #ifdef SMP
71 static void set_user_ldt_rv     __P((struct pcb *));
72 #endif
73
74 #ifndef _SYS_SYSPROTO_H_
75 struct sysarch_args {
76         int op;
77         char *parms;
78 };
79 #endif
80
81 int
82 sysarch(p, uap)
83         struct proc *p;
84         register struct sysarch_args *uap;
85 {
86         int error = 0;
87
88         switch(uap->op) {
89         case I386_GET_LDT:
90                 error = i386_get_ldt(p, uap->parms);
91                 break;
92
93         case I386_SET_LDT:
94                 error = i386_set_ldt(p, uap->parms);
95                 break;
96         case I386_GET_IOPERM:
97                 error = i386_get_ioperm(p, uap->parms);
98                 break;
99         case I386_SET_IOPERM:
100                 error = i386_set_ioperm(p, uap->parms);
101                 break;
102         case I386_VM86:
103                 error = vm86_sysarch(p, uap->parms);
104                 break;
105         default:
106                 error = EOPNOTSUPP;
107                 break;
108         }
109         return (error);
110 }
111
112 int
113 i386_extend_pcb(struct proc *p)
114 {
115         int i, offset;
116         u_long *addr;
117         struct pcb_ext *ext;
118         struct soft_segment_descriptor ssd = {
119                 0,                      /* segment base address (overwritten) */
120                 ctob(IOPAGES + 1) - 1,  /* length */
121                 SDT_SYS386TSS,          /* segment type */
122                 0,                      /* priority level */
123                 1,                      /* descriptor present */
124                 0, 0,
125                 0,                      /* default 32 size */
126                 0                       /* granularity */
127         };
128
129         ext = (struct pcb_ext *)kmem_alloc(kernel_map, ctob(IOPAGES+1));
130         if (ext == 0)
131                 return (ENOMEM);
132         bzero(ext, sizeof(struct pcb_ext)); 
133         ext->ext_tss.tss_esp0 = (unsigned)p->p_addr + ctob(UPAGES) - 16;
134         ext->ext_tss.tss_ss0 = GSEL(GDATA_SEL, SEL_KPL);
135         /*
136          * The last byte of the i/o map must be followed by an 0xff byte.
137          * We arbitrarily allocate 16 bytes here, to keep the starting
138          * address on a doubleword boundary.
139          */
140         offset = PAGE_SIZE - 16;
141         ext->ext_tss.tss_ioopt = 
142             (offset - ((unsigned)&ext->ext_tss - (unsigned)ext)) << 16;
143         ext->ext_iomap = (caddr_t)ext + offset;
144         ext->ext_vm86.vm86_intmap = (caddr_t)ext + offset - 32;
145
146         addr = (u_long *)ext->ext_vm86.vm86_intmap;
147         for (i = 0; i < (ctob(IOPAGES) + 32 + 16) / sizeof(u_long); i++)
148                 *addr++ = ~0;
149
150         ssd.ssd_base = (unsigned)&ext->ext_tss;
151         ssd.ssd_limit -= ((unsigned)&ext->ext_tss - (unsigned)ext);
152         ssdtosd(&ssd, &ext->ext_tssd);
153
154         KASSERT(p == curproc, ("giving a TSS to non-curproc"));
155         KASSERT(p->p_addr->u_pcb.pcb_ext == 0, ("already have a TSS!"));
156         mtx_lock_spin(&sched_lock);
157         p->p_addr->u_pcb.pcb_ext = ext;
158         
159         /* switch to the new TSS after syscall completes */
160         need_resched(p);
161         mtx_unlock_spin(&sched_lock);
162
163         return 0;
164 }
165
166 static int
167 i386_set_ioperm(p, args)
168         struct proc *p;
169         char *args;
170 {
171         int i, error;
172         struct i386_ioperm_args ua;
173         char *iomap;
174
175         if ((error = copyin(args, &ua, sizeof(struct i386_ioperm_args))) != 0)
176                 return (error);
177
178         if ((error = suser(p)) != 0)
179                 return (error);
180         if (securelevel > 0)
181                 return (EPERM);
182         /*
183          * XXX 
184          * While this is restricted to root, we should probably figure out
185          * whether any other driver is using this i/o address, as so not to
186          * cause confusion.  This probably requires a global 'usage registry'.
187          */
188
189         if (p->p_addr->u_pcb.pcb_ext == 0)
190                 if ((error = i386_extend_pcb(p)) != 0)
191                         return (error);
192         iomap = (char *)p->p_addr->u_pcb.pcb_ext->ext_iomap;
193
194         if (ua.start + ua.length > IOPAGES * PAGE_SIZE * NBBY)
195                 return (EINVAL);
196
197         for (i = ua.start; i < ua.start + ua.length; i++) {
198                 if (ua.enable) 
199                         iomap[i >> 3] &= ~(1 << (i & 7));
200                 else
201                         iomap[i >> 3] |= (1 << (i & 7));
202         }
203         return (error);
204 }
205
206 static int
207 i386_get_ioperm(p, args)
208         struct proc *p;
209         char *args;
210 {
211         int i, state, error;
212         struct i386_ioperm_args ua;
213         char *iomap;
214
215         if ((error = copyin(args, &ua, sizeof(struct i386_ioperm_args))) != 0)
216                 return (error);
217         if (ua.start >= IOPAGES * PAGE_SIZE * NBBY)
218                 return (EINVAL);
219
220         if (p->p_addr->u_pcb.pcb_ext == 0) {
221                 ua.length = 0;
222                 goto done;
223         }
224
225         iomap = (char *)p->p_addr->u_pcb.pcb_ext->ext_iomap;
226
227         i = ua.start;
228         state = (iomap[i >> 3] >> (i & 7)) & 1;
229         ua.enable = !state;
230         ua.length = 1;
231
232         for (i = ua.start + 1; i < IOPAGES * PAGE_SIZE * NBBY; i++) {
233                 if (state != ((iomap[i >> 3] >> (i & 7)) & 1))
234                         break;
235                 ua.length++;
236         }
237                         
238 done:
239         error = copyout(&ua, args, sizeof(struct i386_ioperm_args));
240         return (error);
241 }
242
243 /*
244  * Update the GDT entry pointing to the LDT to point to the LDT of the
245  * current process.
246  *
247  * This must be called with sched_lock held.  Unfortunately, we can't use a
248  * mtx_assert() here because cpu_switch() calls this function after changing
249  * curproc but before sched_lock's owner is updated in mi_switch().
250  */   
251 void
252 set_user_ldt(struct pcb *pcb)
253 {
254         struct pcb_ldt *pcb_ldt;
255
256         pcb_ldt = pcb->pcb_ldt;
257 #ifdef SMP
258         gdt[PCPU_GET(cpuid) * NGDT + GUSERLDT_SEL].sd = pcb_ldt->ldt_sd;
259 #else
260         gdt[GUSERLDT_SEL].sd = pcb_ldt->ldt_sd;
261 #endif
262         lldt(GSEL(GUSERLDT_SEL, SEL_KPL));
263         PCPU_SET(currentldt, GSEL(GUSERLDT_SEL, SEL_KPL));
264 }
265
266 #ifdef SMP
267 static void
268 set_user_ldt_rv(struct pcb *pcb)
269 {
270
271         if (pcb != PCPU_GET(curpcb))
272                 return;
273
274         mtx_lock_spin(&sched_lock);
275         set_user_ldt(pcb);
276         mtx_unlock_spin(&sched_lock);
277 }
278 #endif
279
280 /*
281  * Must be called with either sched_lock free or held but not recursed.
282  * If it does not return NULL, it will return with it owned.
283  */
284 struct pcb_ldt *
285 user_ldt_alloc(struct pcb *pcb, int len)
286 {
287         struct pcb_ldt *pcb_ldt, *new_ldt;
288
289         if (mtx_owned(&sched_lock))
290                 mtx_unlock_spin(&sched_lock);
291         mtx_assert(&sched_lock, MA_NOTOWNED);
292         MALLOC(new_ldt, struct pcb_ldt *, sizeof(struct pcb_ldt),
293                 M_SUBPROC, M_WAITOK);
294
295         new_ldt->ldt_len = len = NEW_MAX_LD(len);
296         new_ldt->ldt_base = (caddr_t)kmem_alloc(kernel_map,
297                 len * sizeof(union descriptor));
298         if (new_ldt->ldt_base == NULL) {
299                 FREE(new_ldt, M_SUBPROC);
300                 return NULL;
301         }
302         new_ldt->ldt_refcnt = 1;
303         new_ldt->ldt_active = 0;
304
305         mtx_lock_spin(&sched_lock);
306         gdt_segs[GUSERLDT_SEL].ssd_base = (unsigned)new_ldt->ldt_base;
307         gdt_segs[GUSERLDT_SEL].ssd_limit = len * sizeof(union descriptor) - 1;
308         ssdtosd(&gdt_segs[GUSERLDT_SEL], &new_ldt->ldt_sd);
309
310         if ((pcb_ldt = pcb->pcb_ldt)) {
311                 if (len > pcb_ldt->ldt_len)
312                         len = pcb_ldt->ldt_len;
313                 bcopy(pcb_ldt->ldt_base, new_ldt->ldt_base,
314                         len * sizeof(union descriptor));
315         } else {
316                 bcopy(ldt, new_ldt->ldt_base, sizeof(ldt));
317         }
318         return new_ldt;
319 }
320
321 /*
322  * Must be called either with sched_lock free or held but not recursed.
323  * If pcb->pcb_ldt is not NULL, it will return with sched_lock released.
324  */
325 void
326 user_ldt_free(struct pcb *pcb)
327 {
328         struct pcb_ldt *pcb_ldt = pcb->pcb_ldt;
329
330         if (pcb_ldt == NULL)
331                 return;
332
333         if (!mtx_owned(&sched_lock))
334                 mtx_lock_spin(&sched_lock);
335         mtx_assert(&sched_lock, MA_OWNED | MA_NOTRECURSED);
336         if (pcb == PCPU_GET(curpcb)) {
337                 lldt(_default_ldt);
338                 PCPU_SET(currentldt, _default_ldt);
339         }
340
341         pcb->pcb_ldt = NULL;
342         if (--pcb_ldt->ldt_refcnt == 0) {
343                 mtx_unlock_spin(&sched_lock);
344                 kmem_free(kernel_map, (vm_offset_t)pcb_ldt->ldt_base,
345                         pcb_ldt->ldt_len * sizeof(union descriptor));
346                 FREE(pcb_ldt, M_SUBPROC);
347         } else
348                 mtx_unlock_spin(&sched_lock);
349 }
350
351 static int
352 i386_get_ldt(p, args)
353         struct proc *p;
354         char *args;
355 {
356         int error = 0;
357         struct pcb *pcb = &p->p_addr->u_pcb;
358         struct pcb_ldt *pcb_ldt = pcb->pcb_ldt;
359         int nldt, num;
360         union descriptor *lp;
361         struct i386_ldt_args ua, *uap = &ua;
362
363         if ((error = copyin(args, uap, sizeof(struct i386_ldt_args))) < 0)
364                 return(error);
365
366 #ifdef  DEBUG
367         printf("i386_get_ldt: start=%d num=%d descs=%p\n",
368             uap->start, uap->num, (void *)uap->descs);
369 #endif
370
371         /* verify range of LDTs exist */
372         if ((uap->start < 0) || (uap->num <= 0))
373                 return(EINVAL);
374
375         if (pcb_ldt) {
376                 nldt = pcb_ldt->ldt_len;
377                 num = min(uap->num, nldt);
378                 lp = &((union descriptor *)(pcb_ldt->ldt_base))[uap->start];
379         } else {
380                 nldt = sizeof(ldt)/sizeof(ldt[0]);
381                 num = min(uap->num, nldt);
382                 lp = &ldt[uap->start];
383         }
384         if (uap->start > nldt)
385                 return(EINVAL);
386
387         error = copyout(lp, uap->descs, num * sizeof(union descriptor));
388         if (!error)
389                 p->p_retval[0] = num;
390
391         return(error);
392 }
393
394 static int
395 i386_set_ldt(p, args)
396         struct proc *p;
397         char *args;
398 {
399         int error = 0, i, n;
400         int largest_ld;
401         struct pcb *pcb = &p->p_addr->u_pcb;
402         struct pcb_ldt *pcb_ldt = pcb->pcb_ldt;
403         struct i386_ldt_args ua, *uap = &ua;
404         caddr_t old_ldt_base;
405         int old_ldt_len;
406         critical_t savecrit;
407
408         if ((error = copyin(args, uap, sizeof(struct i386_ldt_args))) < 0)
409                 return(error);
410
411 #ifdef  DEBUG
412         printf("i386_set_ldt: start=%d num=%d descs=%p\n",
413             uap->start, uap->num, (void *)uap->descs);
414 #endif
415
416         /* verify range of descriptors to modify */
417         if ((uap->start < 0) || (uap->start >= MAX_LD) || (uap->num < 0) ||
418                 (uap->num > MAX_LD))
419         {
420                 return(EINVAL);
421         }
422         largest_ld = uap->start + uap->num - 1;
423         if (largest_ld >= MAX_LD)
424                 return(EINVAL);
425
426         /* allocate user ldt */
427         if (!pcb_ldt || largest_ld >= pcb_ldt->ldt_len) {
428                 struct pcb_ldt *new_ldt = user_ldt_alloc(pcb, largest_ld);
429                 if (new_ldt == NULL)
430                         return ENOMEM;
431                 if (pcb_ldt) {
432                         old_ldt_base = pcb_ldt->ldt_base;
433                         old_ldt_len = pcb_ldt->ldt_len;
434                         pcb_ldt->ldt_sd = new_ldt->ldt_sd;
435                         pcb_ldt->ldt_base = new_ldt->ldt_base;
436                         pcb_ldt->ldt_len = new_ldt->ldt_len;
437                         mtx_unlock_spin(&sched_lock);
438                         kmem_free(kernel_map, (vm_offset_t)old_ldt_base,
439                                 old_ldt_len * sizeof(union descriptor));
440                         FREE(new_ldt, M_SUBPROC);
441 #ifndef SMP
442                         mtx_lock_spin(&sched_lock);
443 #endif
444                 } else {
445                         pcb->pcb_ldt = pcb_ldt = new_ldt;
446 #ifdef SMP
447                         mtx_unlock_spin(&sched_lock);
448 #endif
449                 }
450 #ifdef SMP
451                 /* signal other cpus to reload ldt */
452                 smp_rendezvous(NULL, (void (*)(void *))set_user_ldt_rv, NULL, pcb);
453 #else
454                 set_user_ldt(pcb);
455                 mtx_unlock_spin(&sched_lock);
456 #endif
457         }
458
459         /* Check descriptors for access violations */
460         for (i = 0, n = uap->start; i < uap->num; i++, n++) {
461                 union descriptor desc, *dp;
462                 dp = &uap->descs[i];
463                 error = copyin(dp, &desc, sizeof(union descriptor));
464                 if (error)
465                         return(error);
466
467                 switch (desc.sd.sd_type) {
468                 case SDT_SYSNULL:       /* system null */ 
469                         desc.sd.sd_p = 0;
470                         break;
471                 case SDT_SYS286TSS: /* system 286 TSS available */
472                 case SDT_SYSLDT:    /* system local descriptor table */
473                 case SDT_SYS286BSY: /* system 286 TSS busy */
474                 case SDT_SYSTASKGT: /* system task gate */
475                 case SDT_SYS286IGT: /* system 286 interrupt gate */
476                 case SDT_SYS286TGT: /* system 286 trap gate */
477                 case SDT_SYSNULL2:  /* undefined by Intel */ 
478                 case SDT_SYS386TSS: /* system 386 TSS available */
479                 case SDT_SYSNULL3:  /* undefined by Intel */
480                 case SDT_SYS386BSY: /* system 386 TSS busy */
481                 case SDT_SYSNULL4:  /* undefined by Intel */ 
482                 case SDT_SYS386IGT: /* system 386 interrupt gate */
483                 case SDT_SYS386TGT: /* system 386 trap gate */
484                 case SDT_SYS286CGT: /* system 286 call gate */ 
485                 case SDT_SYS386CGT: /* system 386 call gate */
486                         /* I can't think of any reason to allow a user proc
487                          * to create a segment of these types.  They are
488                          * for OS use only.
489                          */
490                         return EACCES;
491                         /*NOTREACHED*/
492
493                 /* memory segment types */
494                 case SDT_MEMEC:   /* memory execute only conforming */
495                 case SDT_MEMEAC:  /* memory execute only accessed conforming */
496                 case SDT_MEMERC:  /* memory execute read conforming */
497                 case SDT_MEMERAC: /* memory execute read accessed conforming */
498                          /* Must be "present" if executable and conforming. */
499                         if (desc.sd.sd_p == 0)
500                                 return (EACCES);
501                         break;
502                 case SDT_MEMRO:   /* memory read only */
503                 case SDT_MEMROA:  /* memory read only accessed */
504                 case SDT_MEMRW:   /* memory read write */
505                 case SDT_MEMRWA:  /* memory read write accessed */
506                 case SDT_MEMROD:  /* memory read only expand dwn limit */
507                 case SDT_MEMRODA: /* memory read only expand dwn lim accessed */
508                 case SDT_MEMRWD:  /* memory read write expand dwn limit */  
509                 case SDT_MEMRWDA: /* memory read write expand dwn lim acessed */
510                 case SDT_MEME:    /* memory execute only */ 
511                 case SDT_MEMEA:   /* memory execute only accessed */
512                 case SDT_MEMER:   /* memory execute read */
513                 case SDT_MEMERA:  /* memory execute read accessed */
514                         break;
515                 default:
516                         return(EINVAL);
517                         /*NOTREACHED*/
518                 }
519
520                 /* Only user (ring-3) descriptors may be present. */
521                 if ((desc.sd.sd_p != 0) && (desc.sd.sd_dpl != SEL_UPL))
522                         return (EACCES);
523         }
524
525         /* Fill in range */
526         savecrit = critical_enter();
527         error = copyin(uap->descs, 
528             &((union descriptor *)(pcb_ldt->ldt_base))[uap->start],
529             uap->num * sizeof(union descriptor));
530         if (!error)
531                 p->p_retval[0] = uap->start;
532         critical_exit(savecrit);
533
534         return(error);
535 }