Deleted Added
full compact
sys_machdep.c (114029) sys_machdep.c (114349)
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

--- 17 unchanged lines hidden (view full) ---

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
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

--- 17 unchanged lines hidden (view full) ---

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: head/sys/amd64/amd64/sys_machdep.c 114029 2003-04-25 20:04:02Z jhb $
34 * $FreeBSD: head/sys/amd64/amd64/sys_machdep.c 114349 2003-05-01 01:05:25Z peter $
35 *
36 */
37
35 *
36 */
37
38#include "opt_kstack_pages.h"
39#include "opt_mac.h"
40
41#include <sys/param.h>
42#include <sys/systm.h>
43#include <sys/lock.h>
38#include <sys/param.h>
39#include <sys/systm.h>
40#include <sys/lock.h>
44#include <sys/mac.h>
45#include <sys/malloc.h>
46#include <sys/mutex.h>
47#include <sys/proc.h>
41#include <sys/proc.h>
48#include <sys/smp.h>
49#include <sys/sysproto.h>
42#include <sys/sysproto.h>
50#include <sys/user.h>
51
43
52#include <vm/vm.h>
53#include <vm/pmap.h>
54#include <vm/vm_map.h>
55#include <vm/vm_extern.h>
56
57#include <machine/cpu.h>
58#include <machine/pcb_ext.h> /* pcb.h included by sys/user.h */
59#include <machine/proc.h>
60#include <machine/sysarch.h>
61
62#include <vm/vm_kern.h> /* for kernel_map */
63
64#define MAX_LD 8192
65#define LD_PER_PAGE 512
66#define NEW_MAX_LD(num) ((num + LD_PER_PAGE) & ~(LD_PER_PAGE-1))
67#define SIZE_FROM_LARGEST_LD(num) (NEW_MAX_LD(num) << 3)
68
69
70
71static int i386_get_ldt(struct thread *, char *);
72static int i386_set_ldt(struct thread *, char *);
73static int i386_get_ioperm(struct thread *, char *);
74static int i386_set_ioperm(struct thread *, char *);
75#ifdef SMP
76static void set_user_ldt_rv(struct thread *);
77#endif
78
79#ifndef _SYS_SYSPROTO_H_
80struct sysarch_args {
81 int op;
82 char *parms;
83};
84#endif
85
86int
87sysarch(td, uap)
88 struct thread *td;
89 register struct sysarch_args *uap;
90{
91 int error;
92
44#ifndef _SYS_SYSPROTO_H_
45struct sysarch_args {
46 int op;
47 char *parms;
48};
49#endif
50
51int
52sysarch(td, uap)
53 struct thread *td;
54 register struct sysarch_args *uap;
55{
56 int error;
57
93 mtx_lock(&Giant);
94 switch(uap->op) {
58 switch(uap->op) {
95 case I386_GET_LDT:
96 error = i386_get_ldt(td, uap->parms);
97 break;
98
99 case I386_SET_LDT:
100 error = i386_set_ldt(td, uap->parms);
101 break;
102 case I386_GET_IOPERM:
103 error = i386_get_ioperm(td, uap->parms);
104 break;
105 case I386_SET_IOPERM:
106 error = i386_set_ioperm(td, uap->parms);
107 break;
108 case I386_VM86:
109 error = vm86_sysarch(td, uap->parms);
110 break;
111 default:
112 error = EINVAL;
113 break;
114 }
59 default:
60 error = EINVAL;
61 break;
62 }
115 mtx_unlock(&Giant);
116 return (error);
117}
63 return (error);
64}
118
119int
120i386_extend_pcb(struct thread *td)
121{
122 int i, offset;
123 u_long *addr;
124 struct pcb_ext *ext;
125 struct soft_segment_descriptor ssd = {
126 0, /* segment base address (overwritten) */
127 ctob(IOPAGES + 1) - 1, /* length */
128 SDT_SYS386TSS, /* segment type */
129 0, /* priority level */
130 1, /* descriptor present */
131 0, 0,
132 0, /* default 32 size */
133 0 /* granularity */
134 };
135
136 if (td->td_proc->p_flag & P_THREADED)
137 return (EINVAL); /* XXXKSE */
138/* XXXKSE All the code below only works in 1:1 needs changing */
139 ext = (struct pcb_ext *)kmem_alloc(kernel_map, ctob(IOPAGES+1));
140 if (ext == 0)
141 return (ENOMEM);
142 bzero(ext, sizeof(struct pcb_ext));
143 /* -16 is so we can convert a trapframe into vm86trapframe inplace */
144 ext->ext_tss.tss_esp0 = td->td_kstack + ctob(KSTACK_PAGES) -
145 sizeof(struct pcb) - 16;
146 ext->ext_tss.tss_ss0 = GSEL(GDATA_SEL, SEL_KPL);
147 /*
148 * The last byte of the i/o map must be followed by an 0xff byte.
149 * We arbitrarily allocate 16 bytes here, to keep the starting
150 * address on a doubleword boundary.
151 */
152 offset = PAGE_SIZE - 16;
153 ext->ext_tss.tss_ioopt =
154 (offset - ((unsigned)&ext->ext_tss - (unsigned)ext)) << 16;
155 ext->ext_iomap = (caddr_t)ext + offset;
156 ext->ext_vm86.vm86_intmap = (caddr_t)ext + offset - 32;
157
158 addr = (u_long *)ext->ext_vm86.vm86_intmap;
159 for (i = 0; i < (ctob(IOPAGES) + 32 + 16) / sizeof(u_long); i++)
160 *addr++ = ~0;
161
162 ssd.ssd_base = (unsigned)&ext->ext_tss;
163 ssd.ssd_limit -= ((unsigned)&ext->ext_tss - (unsigned)ext);
164 ssdtosd(&ssd, &ext->ext_tssd);
165
166 KASSERT(td->td_proc == curthread->td_proc, ("giving TSS to !curproc"));
167 KASSERT(td->td_pcb->pcb_ext == 0, ("already have a TSS!"));
168 mtx_lock_spin(&sched_lock);
169 td->td_pcb->pcb_ext = ext;
170
171 /* switch to the new TSS after syscall completes */
172 td->td_flags |= TDF_NEEDRESCHED;
173 mtx_unlock_spin(&sched_lock);
174
175 return 0;
176}
177
178static int
179i386_set_ioperm(td, args)
180 struct thread *td;
181 char *args;
182{
183 int i, error;
184 struct i386_ioperm_args ua;
185 char *iomap;
186
187 if ((error = copyin(args, &ua, sizeof(struct i386_ioperm_args))) != 0)
188 return (error);
189
190#ifdef MAC
191 if ((error = mac_check_sysarch_ioperm(td->td_ucred)) != 0)
192 return (error);
193#endif
194 if ((error = suser(td)) != 0)
195 return (error);
196 if ((error = securelevel_gt(td->td_ucred, 0)) != 0)
197 return (error);
198 /*
199 * XXX
200 * While this is restricted to root, we should probably figure out
201 * whether any other driver is using this i/o address, as so not to
202 * cause confusion. This probably requires a global 'usage registry'.
203 */
204
205 if (td->td_pcb->pcb_ext == 0)
206 if ((error = i386_extend_pcb(td)) != 0)
207 return (error);
208 iomap = (char *)td->td_pcb->pcb_ext->ext_iomap;
209
210 if (ua.start + ua.length > IOPAGES * PAGE_SIZE * NBBY)
211 return (EINVAL);
212
213 for (i = ua.start; i < ua.start + ua.length; i++) {
214 if (ua.enable)
215 iomap[i >> 3] &= ~(1 << (i & 7));
216 else
217 iomap[i >> 3] |= (1 << (i & 7));
218 }
219 return (error);
220}
221
222static int
223i386_get_ioperm(td, args)
224 struct thread *td;
225 char *args;
226{
227 int i, state, error;
228 struct i386_ioperm_args ua;
229 char *iomap;
230
231 if ((error = copyin(args, &ua, sizeof(struct i386_ioperm_args))) != 0)
232 return (error);
233 if (ua.start >= IOPAGES * PAGE_SIZE * NBBY)
234 return (EINVAL);
235
236 if (td->td_pcb->pcb_ext == 0) {
237 ua.length = 0;
238 goto done;
239 }
240
241 iomap = (char *)td->td_pcb->pcb_ext->ext_iomap;
242
243 i = ua.start;
244 state = (iomap[i >> 3] >> (i & 7)) & 1;
245 ua.enable = !state;
246 ua.length = 1;
247
248 for (i = ua.start + 1; i < IOPAGES * PAGE_SIZE * NBBY; i++) {
249 if (state != ((iomap[i >> 3] >> (i & 7)) & 1))
250 break;
251 ua.length++;
252 }
253
254done:
255 error = copyout(&ua, args, sizeof(struct i386_ioperm_args));
256 return (error);
257}
258
259/*
260 * Update the GDT entry pointing to the LDT to point to the LDT of the
261 * current process.
262 *
263 * This must be called with sched_lock held. Unfortunately, we can't use a
264 * mtx_assert() here because cpu_switch() calls this function after changing
265 * curproc but before sched_lock's owner is updated in mi_switch().
266 */
267void
268set_user_ldt(struct mdproc *mdp)
269{
270 struct proc_ldt *pldt;
271
272 pldt = mdp->md_ldt;
273#ifdef SMP
274 gdt[PCPU_GET(cpuid) * NGDT + GUSERLDT_SEL].sd = pldt->ldt_sd;
275#else
276 gdt[GUSERLDT_SEL].sd = pldt->ldt_sd;
277#endif
278 lldt(GSEL(GUSERLDT_SEL, SEL_KPL));
279 PCPU_SET(currentldt, GSEL(GUSERLDT_SEL, SEL_KPL));
280}
281
282#ifdef SMP
283static void
284set_user_ldt_rv(struct thread *td)
285{
286
287 if (td->td_proc != curthread->td_proc)
288 return;
289
290 mtx_lock_spin(&sched_lock);
291 set_user_ldt(&td->td_proc->p_md);
292 mtx_unlock_spin(&sched_lock);
293}
294#endif
295
296/*
297 * Must be called with either sched_lock free or held but not recursed.
298 * If it does not return NULL, it will return with it owned.
299 */
300struct proc_ldt *
301user_ldt_alloc(struct mdproc *mdp, int len)
302{
303 struct proc_ldt *pldt, *new_ldt;
304
305 if (mtx_owned(&sched_lock))
306 mtx_unlock_spin(&sched_lock);
307 mtx_assert(&sched_lock, MA_NOTOWNED);
308 MALLOC(new_ldt, struct proc_ldt *, sizeof(struct proc_ldt),
309 M_SUBPROC, M_WAITOK);
310
311 new_ldt->ldt_len = len = NEW_MAX_LD(len);
312 new_ldt->ldt_base = (caddr_t)kmem_alloc(kernel_map,
313 len * sizeof(union descriptor));
314 if (new_ldt->ldt_base == NULL) {
315 FREE(new_ldt, M_SUBPROC);
316 return NULL;
317 }
318 new_ldt->ldt_refcnt = 1;
319 new_ldt->ldt_active = 0;
320
321 mtx_lock_spin(&sched_lock);
322 gdt_segs[GUSERLDT_SEL].ssd_base = (unsigned)new_ldt->ldt_base;
323 gdt_segs[GUSERLDT_SEL].ssd_limit = len * sizeof(union descriptor) - 1;
324 ssdtosd(&gdt_segs[GUSERLDT_SEL], &new_ldt->ldt_sd);
325
326 if ((pldt = mdp->md_ldt)) {
327 if (len > pldt->ldt_len)
328 len = pldt->ldt_len;
329 bcopy(pldt->ldt_base, new_ldt->ldt_base,
330 len * sizeof(union descriptor));
331 } else {
332 bcopy(ldt, new_ldt->ldt_base, sizeof(ldt));
333 }
334 return new_ldt;
335}
336
337/*
338 * Must be called either with sched_lock free or held but not recursed.
339 * If md_ldt is not NULL, it will return with sched_lock released.
340 */
341void
342user_ldt_free(struct thread *td)
343{
344 struct mdproc *mdp = &td->td_proc->p_md;
345 struct proc_ldt *pldt = mdp->md_ldt;
346
347 if (pldt == NULL)
348 return;
349
350 if (!mtx_owned(&sched_lock))
351 mtx_lock_spin(&sched_lock);
352 mtx_assert(&sched_lock, MA_OWNED | MA_NOTRECURSED);
353 if (td == PCPU_GET(curthread)) {
354 lldt(_default_ldt);
355 PCPU_SET(currentldt, _default_ldt);
356 }
357
358 mdp->md_ldt = NULL;
359 if (--pldt->ldt_refcnt == 0) {
360 mtx_unlock_spin(&sched_lock);
361 kmem_free(kernel_map, (vm_offset_t)pldt->ldt_base,
362 pldt->ldt_len * sizeof(union descriptor));
363 FREE(pldt, M_SUBPROC);
364 } else
365 mtx_unlock_spin(&sched_lock);
366}
367
368static int
369i386_get_ldt(td, args)
370 struct thread *td;
371 char *args;
372{
373 int error = 0;
374 struct proc_ldt *pldt = td->td_proc->p_md.md_ldt;
375 int nldt, num;
376 union descriptor *lp;
377 struct i386_ldt_args ua, *uap = &ua;
378
379 if ((error = copyin(args, uap, sizeof(struct i386_ldt_args))) < 0)
380 return(error);
381
382#ifdef DEBUG
383 printf("i386_get_ldt: start=%d num=%d descs=%p\n",
384 uap->start, uap->num, (void *)uap->descs);
385#endif
386
387 /* verify range of LDTs exist */
388 if ((uap->start < 0) || (uap->num <= 0))
389 return(EINVAL);
390
391 if (pldt) {
392 nldt = pldt->ldt_len;
393 num = min(uap->num, nldt);
394 lp = &((union descriptor *)(pldt->ldt_base))[uap->start];
395 } else {
396 nldt = sizeof(ldt)/sizeof(ldt[0]);
397 num = min(uap->num, nldt);
398 lp = &ldt[uap->start];
399 }
400 if (uap->start + num > nldt)
401 return(EINVAL);
402
403 error = copyout(lp, uap->descs, num * sizeof(union descriptor));
404 if (!error)
405 td->td_retval[0] = num;
406
407 return(error);
408}
409
410static int
411i386_set_ldt(td, args)
412 struct thread *td;
413 char *args;
414{
415 int error = 0, i, n;
416 int largest_ld;
417 struct mdproc *mdp = &td->td_proc->p_md;
418 struct proc_ldt *pldt = mdp->md_ldt;
419 struct i386_ldt_args ua, *uap = &ua;
420 union descriptor *descs;
421 caddr_t old_ldt_base;
422 int descs_size, old_ldt_len;
423 register_t savecrit;
424
425 if ((error = copyin(args, uap, sizeof(struct i386_ldt_args))) < 0)
426 return(error);
427
428#ifdef DEBUG
429 printf("i386_set_ldt: start=%d num=%d descs=%p\n",
430 uap->start, uap->num, (void *)uap->descs);
431#endif
432
433 /* verify range of descriptors to modify */
434 if ((uap->start < 0) || (uap->start >= MAX_LD) || (uap->num < 0) ||
435 (uap->num > MAX_LD))
436 {
437 return(EINVAL);
438 }
439 largest_ld = uap->start + uap->num - 1;
440 if (largest_ld >= MAX_LD)
441 return(EINVAL);
442
443 /* allocate user ldt */
444 if (!pldt || largest_ld >= pldt->ldt_len) {
445 struct proc_ldt *new_ldt = user_ldt_alloc(mdp, largest_ld);
446 if (new_ldt == NULL)
447 return ENOMEM;
448 if (pldt) {
449 old_ldt_base = pldt->ldt_base;
450 old_ldt_len = pldt->ldt_len;
451 pldt->ldt_sd = new_ldt->ldt_sd;
452 pldt->ldt_base = new_ldt->ldt_base;
453 pldt->ldt_len = new_ldt->ldt_len;
454 mtx_unlock_spin(&sched_lock);
455 kmem_free(kernel_map, (vm_offset_t)old_ldt_base,
456 old_ldt_len * sizeof(union descriptor));
457 FREE(new_ldt, M_SUBPROC);
458#ifndef SMP
459 mtx_lock_spin(&sched_lock);
460#endif
461 } else {
462 mdp->md_ldt = pldt = new_ldt;
463#ifdef SMP
464 mtx_unlock_spin(&sched_lock);
465#endif
466 }
467#ifdef SMP
468 /* signal other cpus to reload ldt */
469 smp_rendezvous(NULL, (void (*)(void *))set_user_ldt_rv,
470 NULL, td);
471#else
472 set_user_ldt(mdp);
473 mtx_unlock_spin(&sched_lock);
474#endif
475 }
476
477 descs_size = uap->num * sizeof(union descriptor);
478 descs = (union descriptor *)kmem_alloc(kernel_map, descs_size);
479 if (descs == NULL)
480 return (ENOMEM);
481 error = copyin(&uap->descs[0], descs, descs_size);
482 if (error) {
483 kmem_free(kernel_map, (vm_offset_t)descs, descs_size);
484 return (error);
485 }
486 /* Check descriptors for access violations */
487 for (i = 0, n = uap->start; i < uap->num; i++, n++) {
488 union descriptor *dp;
489 dp = &descs[i];
490
491 switch (dp->sd.sd_type) {
492 case SDT_SYSNULL: /* system null */
493 dp->sd.sd_p = 0;
494 break;
495 case SDT_SYS286TSS: /* system 286 TSS available */
496 case SDT_SYSLDT: /* system local descriptor table */
497 case SDT_SYS286BSY: /* system 286 TSS busy */
498 case SDT_SYSTASKGT: /* system task gate */
499 case SDT_SYS286IGT: /* system 286 interrupt gate */
500 case SDT_SYS286TGT: /* system 286 trap gate */
501 case SDT_SYSNULL2: /* undefined by Intel */
502 case SDT_SYS386TSS: /* system 386 TSS available */
503 case SDT_SYSNULL3: /* undefined by Intel */
504 case SDT_SYS386BSY: /* system 386 TSS busy */
505 case SDT_SYSNULL4: /* undefined by Intel */
506 case SDT_SYS386IGT: /* system 386 interrupt gate */
507 case SDT_SYS386TGT: /* system 386 trap gate */
508 case SDT_SYS286CGT: /* system 286 call gate */
509 case SDT_SYS386CGT: /* system 386 call gate */
510 /* I can't think of any reason to allow a user proc
511 * to create a segment of these types. They are
512 * for OS use only.
513 */
514 kmem_free(kernel_map, (vm_offset_t)descs, descs_size);
515 return EACCES;
516 /*NOTREACHED*/
517
518 /* memory segment types */
519 case SDT_MEMEC: /* memory execute only conforming */
520 case SDT_MEMEAC: /* memory execute only accessed conforming */
521 case SDT_MEMERC: /* memory execute read conforming */
522 case SDT_MEMERAC: /* memory execute read accessed conforming */
523 /* Must be "present" if executable and conforming. */
524 if (dp->sd.sd_p == 0) {
525 kmem_free(kernel_map, (vm_offset_t)descs,
526 descs_size);
527 return (EACCES);
528 }
529 break;
530 case SDT_MEMRO: /* memory read only */
531 case SDT_MEMROA: /* memory read only accessed */
532 case SDT_MEMRW: /* memory read write */
533 case SDT_MEMRWA: /* memory read write accessed */
534 case SDT_MEMROD: /* memory read only expand dwn limit */
535 case SDT_MEMRODA: /* memory read only expand dwn lim accessed */
536 case SDT_MEMRWD: /* memory read write expand dwn limit */
537 case SDT_MEMRWDA: /* memory read write expand dwn lim acessed */
538 case SDT_MEME: /* memory execute only */
539 case SDT_MEMEA: /* memory execute only accessed */
540 case SDT_MEMER: /* memory execute read */
541 case SDT_MEMERA: /* memory execute read accessed */
542 break;
543 default:
544 kmem_free(kernel_map, (vm_offset_t)descs, descs_size);
545 return(EINVAL);
546 /*NOTREACHED*/
547 }
548
549 /* Only user (ring-3) descriptors may be present. */
550 if ((dp->sd.sd_p != 0) && (dp->sd.sd_dpl != SEL_UPL)) {
551 kmem_free(kernel_map, (vm_offset_t)descs, descs_size);
552 return (EACCES);
553 }
554 }
555
556 /* Fill in range */
557 savecrit = intr_disable();
558 bcopy(descs,
559 &((union descriptor *)(pldt->ldt_base))[uap->start],
560 uap->num * sizeof(union descriptor));
561 td->td_retval[0] = uap->start;
562 intr_restore(savecrit);
563 kmem_free(kernel_map, (vm_offset_t)descs, descs_size);
564 return (0);
565}