sys_machdep.c revision 32012
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 * $Id: sys_machdep.c,v 1.30 1997/11/26 22:45:47 joerg Exp $ 35 * 36 */ 37 38#include "opt_user_ldt.h" 39#include "opt_vm86.h" 40 41#include <sys/param.h> 42#include <sys/systm.h> 43#include <sys/sysproto.h> 44#include <sys/proc.h> 45 46#include <vm/vm.h> 47#include <sys/lock.h> 48#include <vm/pmap.h> 49#include <vm/vm_map.h> 50#include <vm/vm_extern.h> 51 52#include <sys/user.h> 53 54#include <machine/cpu.h> 55#include <machine/pcb_ext.h> /* pcb.h included by sys/user.h */ 56#include <machine/sysarch.h> 57 58#include <vm/vm_kern.h> /* for kernel_map */ 59 60#define MAX_LD 8192 61#define LD_PER_PAGE 512 62#define NEW_MAX_LD(num) ((num + LD_PER_PAGE) & ~(LD_PER_PAGE-1)) 63#define SIZE_FROM_LARGEST_LD(num) (NEW_MAX_LD(num) << 3) 64 65 66 67void set_user_ldt __P((struct pcb *pcb)); 68#ifdef USER_LDT 69static int i386_get_ldt __P((struct proc *, char *)); 70static int i386_set_ldt __P((struct proc *, char *)); 71#endif 72#ifdef VM86 73static int i386_get_ioperm __P((struct proc *, char *)); 74static int i386_set_ioperm __P((struct proc *, char *)); 75int i386_extend_pcb __P((struct proc *)); 76#endif 77 78#ifndef _SYS_SYSPROTO_H_ 79struct sysarch_args { 80 int op; 81 char *parms; 82}; 83#endif 84 85int 86sysarch(p, uap) 87 struct proc *p; 88 register struct sysarch_args *uap; 89{ 90 int error = 0; 91 92 switch(uap->op) { 93#ifdef USER_LDT 94 case I386_GET_LDT: 95 error = i386_get_ldt(p, uap->parms); 96 break; 97 98 case I386_SET_LDT: 99 error = i386_set_ldt(p, uap->parms); 100 break; 101#endif 102#ifdef VM86 103 case I386_GET_IOPERM: 104 error = i386_get_ioperm(p, uap->parms); 105 break; 106 case I386_SET_IOPERM: 107 error = i386_set_ioperm(p, uap->parms); 108 break; 109 case I386_VM86: 110 error = vm86_sysarch(p, uap->parms); 111 break; 112#endif 113 default: 114 error = EINVAL; 115 break; 116 } 117 return (error); 118} 119 120#ifdef VM86 121int 122i386_extend_pcb(struct proc *p) 123{ 124 int i, offset; 125 u_long *addr; 126 struct pcb_ext *ext; 127 struct segment_descriptor sd; 128 struct soft_segment_descriptor ssd = { 129 0, /* segment base address (overwritten) */ 130 ctob(IOPAGES + 1) - 1, /* length */ 131 SDT_SYS386TSS, /* segment type */ 132 0, /* priority level */ 133 1, /* descriptor present */ 134 0, 0, 135 0, /* default 32 size */ 136 0 /* granularity */ 137 }; 138 139 ext = (struct pcb_ext *)kmem_alloc(kernel_map, ctob(IOPAGES+1)); 140 if (ext == 0) 141 return (ENOMEM); 142 p->p_addr->u_pcb.pcb_ext = ext; 143 bzero(&ext->ext_tss, sizeof(struct i386tss)); 144 ext->ext_tss.tss_esp0 = (unsigned)p->p_addr + ctob(UPAGES) - 16; 145 ext->ext_tss.tss_ss0 = GSEL(GDATA_SEL, SEL_KPL); 146 /* 147 * The last byte of the i/o map must be followed by an 0xff byte. 148 * We arbitrarily allocate 16 bytes here, to keep the starting 149 * address on a doubleword boundary. 150 */ 151 offset = PAGE_SIZE - 16; 152 ext->ext_tss.tss_ioopt = 153 (offset - ((unsigned)&ext->ext_tss - (unsigned)ext)) << 16; 154 ext->ext_iomap = (caddr_t)ext + offset; 155 ext->ext_vm86.vm86_intmap = (caddr_t)ext + offset - 32; 156 ext->ext_vm86.vm86_inited = 0; 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 /* switch to the new TSS after syscall completes */ 167 need_resched(); 168 169 return 0; 170} 171 172struct i386_ioperm_args { 173 u_short start; 174 u_short length; 175 u_char enable; 176}; 177 178static int 179i386_set_ioperm(p, args) 180 struct proc *p; 181 char *args; 182{ 183 int i, error = 0; 184 struct i386_ioperm_args ua; 185 char *iomap; 186 187 if (error = copyin(args, &ua, sizeof(struct i386_ioperm_args))) 188 return (error); 189 190 /* Only root can do this */ 191 if (error = suser(p->p_ucred, &p->p_acflag)) 192 return (error); 193 /* 194 * XXX 195 * While this is restricted to root, we should probably figure out 196 * whether any other driver is using this i/o address, as so not to 197 * cause confusion. This probably requires a global 'usage registry'. 198 */ 199 200 if (p->p_addr->u_pcb.pcb_ext == 0) 201 if (error = i386_extend_pcb(p)) 202 return (error); 203 iomap = (char *)p->p_addr->u_pcb.pcb_ext->ext_iomap; 204 205 if ((int)(ua.start + ua.length) > 0xffff) 206 return (EINVAL); 207 208 for (i = ua.start; i < (int)(ua.start + ua.length) + 1; i++) { 209 if (ua.enable) 210 iomap[i >> 3] &= ~(1 << (i & 7)); 211 else 212 iomap[i >> 3] |= (1 << (i & 7)); 213 } 214 return (error); 215} 216 217static int 218i386_get_ioperm(p, args) 219 struct proc *p; 220 char *args; 221{ 222 int i, state, error = 0; 223 struct i386_ioperm_args ua; 224 char *iomap; 225 226 if (error = copyin(args, &ua, sizeof(struct i386_ioperm_args))) 227 return (error); 228 229 if (p->p_addr->u_pcb.pcb_ext == 0) { 230 ua.length = 0; 231 goto done; 232 } 233 234 iomap = (char *)p->p_addr->u_pcb.pcb_ext->ext_iomap; 235 236 i = ua.start; 237 state = (iomap[i >> 3] >> (i & 7)) & 1; 238 ua.enable = !state; 239 ua.length = 1; 240 241 for (i = ua.start + 1; i < 0x10000; i++) { 242 if (state != ((iomap[i >> 3] >> (i & 7)) & 1)) 243 break; 244 ua.length++; 245 } 246 247done: 248 error = copyout(&ua, args, sizeof(struct i386_ioperm_args)); 249 return (error); 250} 251#endif /* VM86 */ 252 253#ifdef USER_LDT 254/* 255 * Update the GDT entry pointing to the LDT to point to the LDT of the 256 * current process. 257 */ 258void 259set_user_ldt(struct pcb *pcb) 260{ 261 gdt_segs[GUSERLDT_SEL].ssd_base = (unsigned)pcb->pcb_ldt; 262 gdt_segs[GUSERLDT_SEL].ssd_limit = (pcb->pcb_ldt_len * sizeof(union descriptor)) - 1; 263 ssdtosd(&gdt_segs[GUSERLDT_SEL], &gdt[GUSERLDT_SEL].sd); 264 lldt(GSEL(GUSERLDT_SEL, SEL_KPL)); 265 currentldt = GSEL(GUSERLDT_SEL, SEL_KPL); 266} 267 268struct i386_get_ldt_args { 269 int start; 270 union descriptor *desc; 271 int num; 272}; 273 274static int 275i386_get_ldt(p, args) 276 struct proc *p; 277 char *args; 278{ 279 int error = 0; 280 struct pcb *pcb = &p->p_addr->u_pcb; 281 int nldt, num; 282 union descriptor *lp; 283 int s; 284 struct i386_get_ldt_args ua; 285 struct i386_get_ldt_args *uap = &ua; 286 287 if ((error = copyin(args, uap, sizeof(struct i386_get_ldt_args))) < 0) 288 return(error); 289 290#ifdef DEBUG 291 printf("i386_get_ldt: start=%d num=%d descs=%x\n", uap->start, 292 uap->num, uap->desc); 293#endif 294 295 /* verify range of LDTs exist */ 296 if ((uap->start < 0) || (uap->num <= 0)) 297 return(EINVAL); 298 299 s = splhigh(); 300 301 if (pcb->pcb_ldt) { 302 nldt = pcb->pcb_ldt_len; 303 num = min(uap->num, nldt); 304 lp = &((union descriptor *)(pcb->pcb_ldt))[uap->start]; 305 } else { 306 nldt = sizeof(ldt)/sizeof(ldt[0]); 307 num = min(uap->num, nldt); 308 lp = &ldt[uap->start]; 309 } 310 if (uap->start > nldt) { 311 splx(s); 312 return(EINVAL); 313 } 314 315 error = copyout(lp, uap->desc, num * sizeof(union descriptor)); 316 if (!error) 317 p->p_retval[0] = num; 318 319 splx(s); 320 return(error); 321} 322 323struct i386_set_ldt_args { 324 int start; 325 union descriptor *desc; 326 int num; 327}; 328 329static int 330i386_set_ldt(p, args) 331 struct proc *p; 332 char *args; 333{ 334 int error = 0, i, n; 335 int largest_ld; 336 struct pcb *pcb = &p->p_addr->u_pcb; 337 int s; 338 struct i386_set_ldt_args ua, *uap; 339 340 if ((error = copyin(args, &ua, sizeof(struct i386_set_ldt_args))) < 0) 341 return(error); 342 343 uap = &ua; 344 345#ifdef DEBUG 346 printf("i386_set_ldt: start=%d num=%d descs=%x\n", uap->start, uap->num, uap->desc); 347#endif 348 349 /* verify range of descriptors to modify */ 350 if ((uap->start < 0) || (uap->start >= MAX_LD) || (uap->num < 0) || 351 (uap->num > MAX_LD)) 352 { 353 return(EINVAL); 354 } 355 largest_ld = uap->start + uap->num - 1; 356 if (largest_ld >= MAX_LD) 357 return(EINVAL); 358 359 /* allocate user ldt */ 360 if (!pcb->pcb_ldt || (largest_ld >= pcb->pcb_ldt_len)) { 361 union descriptor *new_ldt = (union descriptor *)kmem_alloc( 362 kernel_map, SIZE_FROM_LARGEST_LD(largest_ld)); 363 if (new_ldt == NULL) { 364 return ENOMEM; 365 } 366 if (pcb->pcb_ldt) { 367 bcopy(pcb->pcb_ldt, new_ldt, pcb->pcb_ldt_len 368 * sizeof(union descriptor)); 369 kmem_free(kernel_map, (vm_offset_t)pcb->pcb_ldt, 370 pcb->pcb_ldt_len * sizeof(union descriptor)); 371 } else { 372 bcopy(ldt, new_ldt, sizeof(ldt)); 373 } 374 pcb->pcb_ldt = (caddr_t)new_ldt; 375 pcb->pcb_ldt_len = NEW_MAX_LD(largest_ld); 376 if (pcb == curpcb) 377 set_user_ldt(pcb); 378 } 379 380 /* Check descriptors for access violations */ 381 for (i = 0, n = uap->start; i < uap->num; i++, n++) { 382 union descriptor desc, *dp; 383 dp = &uap->desc[i]; 384 error = copyin(dp, &desc, sizeof(union descriptor)); 385 if (error) 386 return(error); 387 388 switch (desc.sd.sd_type) { 389 case SDT_SYSNULL: /* system null */ 390 desc.sd.sd_p = 0; 391 break; 392 case SDT_SYS286TSS: /* system 286 TSS available */ 393 case SDT_SYSLDT: /* system local descriptor table */ 394 case SDT_SYS286BSY: /* system 286 TSS busy */ 395 case SDT_SYSTASKGT: /* system task gate */ 396 case SDT_SYS286IGT: /* system 286 interrupt gate */ 397 case SDT_SYS286TGT: /* system 286 trap gate */ 398 case SDT_SYSNULL2: /* undefined by Intel */ 399 case SDT_SYS386TSS: /* system 386 TSS available */ 400 case SDT_SYSNULL3: /* undefined by Intel */ 401 case SDT_SYS386BSY: /* system 386 TSS busy */ 402 case SDT_SYSNULL4: /* undefined by Intel */ 403 case SDT_SYS386IGT: /* system 386 interrupt gate */ 404 case SDT_SYS386TGT: /* system 386 trap gate */ 405 case SDT_SYS286CGT: /* system 286 call gate */ 406 case SDT_SYS386CGT: /* system 386 call gate */ 407 /* I can't think of any reason to allow a user proc 408 * to create a segment of these types. They are 409 * for OS use only. 410 */ 411 return EACCES; 412 413 /* memory segment types */ 414 case SDT_MEMEC: /* memory execute only conforming */ 415 case SDT_MEMEAC: /* memory execute only accessed conforming */ 416 case SDT_MEMERC: /* memory execute read conforming */ 417 case SDT_MEMERAC: /* memory execute read accessed conforming */ 418 /* Must be "present" if executable and conforming. */ 419 if (desc.sd.sd_p == 0) 420 return (EACCES); 421 break; 422 case SDT_MEMRO: /* memory read only */ 423 case SDT_MEMROA: /* memory read only accessed */ 424 case SDT_MEMRW: /* memory read write */ 425 case SDT_MEMRWA: /* memory read write accessed */ 426 case SDT_MEMROD: /* memory read only expand dwn limit */ 427 case SDT_MEMRODA: /* memory read only expand dwn lim accessed */ 428 case SDT_MEMRWD: /* memory read write expand dwn limit */ 429 case SDT_MEMRWDA: /* memory read write expand dwn lim acessed */ 430 case SDT_MEME: /* memory execute only */ 431 case SDT_MEMEA: /* memory execute only accessed */ 432 case SDT_MEMER: /* memory execute read */ 433 case SDT_MEMERA: /* memory execute read accessed */ 434 break; 435 default: 436 return(EINVAL); 437 /*NOTREACHED*/ 438 } 439 440 /* Only user (ring-3) descriptors may be present. */ 441 if ((desc.sd.sd_p != 0) && (desc.sd.sd_dpl != SEL_UPL)) 442 return (EACCES); 443 } 444 445 s = splhigh(); 446 447 /* Fill in range */ 448 error = copyin(uap->desc, 449 &((union descriptor *)(pcb->pcb_ldt))[uap->start], 450 uap->num * sizeof(union descriptor)); 451 if (!error) 452 p->p_retval[0] = uap->start; 453 454 splx(s); 455 return(error); 456} 457#endif /* USER_LDT */ 458