int linux_ioctl_fdio(struct proc *p, struct linux_sys_ioctl_args *uap, register_t *retval) { struct filedesc *fdp; struct file *fp; int error; int (*ioctlf)(struct file *, u_long, caddr_t, struct proc *); u_long com; struct fd_type fparams; struct linux_floppy_struct lflop; struct linux_floppy_drive_struct ldrive; com = (u_long)SCARG(uap, data); fdp = p->p_fd; if ((fp = fd_getfile(fdp, SCARG(uap, fd))) == NULL) return (EBADF); FREF(fp); com = SCARG(uap, com); ioctlf = fp->f_ops->fo_ioctl; retval[0] = error = 0; switch (com) { case LINUX_FDMSGON: case LINUX_FDMSGOFF: case LINUX_FDTWADDLE: case LINUX_FDCLRPRM: /* whatever you say */ break; case LINUX_FDPOLLDRVSTAT: /* * Just fill in some innocent defaults. */ memset(&ldrive, 0, sizeof ldrive); ldrive.fd_ref = 1; ldrive.maxblock = 2; ldrive.maxtrack = ldrive.track = 1; ldrive.flags = LINUX_FD_DISK_WRITABLE; error = copyout(&ldrive, SCARG(uap, data), sizeof ldrive); break; case LINUX_FDGETPRM: error = ioctlf(fp, FD_GTYPE, (caddr_t)&fparams, p); if (error != 0) break; lflop.size = fparams.heads * fparams.sectrac * fparams.tracks; lflop.sect = fparams.sectrac; lflop.head = fparams.heads; lflop.track = fparams.tracks; lflop.stretch = fparams.step == 2 ? 1 : 0; lflop.spec1 = fparams.steprate; lflop.gap = fparams.gap1; lflop.fmt_gap = fparams.gap2; lflop.rate = fparams.rate; error = copyout(&lflop, SCARG(uap, data), sizeof lflop); break; case LINUX_FDSETPRM: /* * Should use FDIOCSETFORMAT here, iff its interface * is extended. */ case LINUX_FDDEFPRM: case LINUX_FDFMTBEG: case LINUX_FDFMTTRK: case LINUX_FDFMTEND: case LINUX_FDSETEMSGTRESH: case LINUX_FDFLUSH: case LINUX_FDSETMAXERRS: case LINUX_FDGETMAXERRS: case LINUX_FDGETDRVTYP: case LINUX_FDSETDRVPRM: case LINUX_FDGETDRVPRM: case LINUX_FDGETDRVSTAT: case LINUX_FDRESET: case LINUX_FDGETFDCSTAT: case LINUX_FDWERRORCLR: case LINUX_FDWERRORGET: case LINUX_FDRAWCMD: case LINUX_FDEJECT: default: error = EINVAL; } FRELE(fp, p); return 0; }
int linux_ioctl_hdio(struct proc *p, struct linux_sys_ioctl_args *uap, register_t *retval) { u_long com; int error, error1; caddr_t sg; struct filedesc *fdp; struct file *fp; int (*ioctlf)(struct file *, u_long, caddr_t, struct proc *); struct ataparams *atap, ata; struct atareq req; struct disklabel label, *labp; struct partinfo partp; struct linux_hd_geometry hdg; struct linux_hd_big_geometry hdg_big; fdp = p->p_fd; if ((fp = fd_getfile(fdp, SCARG(uap, fd))) == NULL) return (EBADF); FREF(fp); com = SCARG(uap, com); ioctlf = fp->f_ops->fo_ioctl; retval[0] = error = 0; com = SCARG(uap, com); switch (com) { case LINUX_HDIO_OBSOLETE_IDENTITY: case LINUX_HDIO_GET_IDENTITY: sg = stackgap_init(p->p_emul); atap = stackgap_alloc(&sg, DEV_BSIZE); if (atap == NULL) { error = ENOMEM; break; } req.flags = ATACMD_READ; req.command = WDCC_IDENTIFY; req.databuf = (caddr_t)atap; req.datalen = DEV_BSIZE; req.timeout = 1000; error = ioctlf(fp, ATAIOCCOMMAND, (caddr_t)&req, p); if (error != 0) break; if (req.retsts != ATACMD_OK) { error = EIO; break; } error = copyin(atap, &ata, sizeof ata); if (error != 0) break; /* * 142 is the size of the old structure used by Linux, * which doesn't seem to be defined anywhere anymore. */ error = copyout(&ata, SCARG(uap, data), com == LINUX_HDIO_GET_IDENTITY ? sizeof ata : 142); break; case LINUX_HDIO_GETGEO: error = linux_machdepioctl(p, uap, retval); if (error == 0) break; error = ioctlf(fp, DIOCGDINFO, (caddr_t)&label, p); error1 = ioctlf(fp, DIOCGPART, (caddr_t)&partp, p); if (error != 0 && error1 != 0) { error = error1; break; } labp = error != 0 ? &label : partp.disklab; hdg.start = error1 != 0 ? partp.part->p_offset : 0; hdg.heads = labp->d_ntracks; hdg.cylinders = labp->d_ncylinders; hdg.sectors = labp->d_nsectors; error = copyout(&hdg, SCARG(uap, data), sizeof hdg); break; case LINUX_HDIO_GETGEO_BIG: error = linux_machdepioctl(p, uap, retval); if (error == 0) break; case LINUX_HDIO_GETGEO_BIG_RAW: error = ioctlf(fp, DIOCGDINFO, (caddr_t)&label, p); error1 = ioctlf(fp, DIOCGPART, (caddr_t)&partp, p); if (error != 0 && error1 != 0) { error = error1; break; } labp = error != 0 ? &label : partp.disklab; hdg_big.start = error1 != 0 ? partp.part->p_offset : 0; hdg_big.heads = labp->d_ntracks; hdg_big.cylinders = labp->d_ncylinders; hdg_big.sectors = labp->d_nsectors; error = copyout(&hdg_big, SCARG(uap, data), sizeof hdg_big); break; case LINUX_HDIO_GET_UNMASKINTR: case LINUX_HDIO_GET_MULTCOUNT: case LINUX_HDIO_GET_KEEPSETTINGS: case LINUX_HDIO_GET_32BIT: case LINUX_HDIO_GET_NOWERR: case LINUX_HDIO_GET_DMA: case LINUX_HDIO_GET_NICE: case LINUX_HDIO_DRIVE_RESET: case LINUX_HDIO_TRISTATE_HWIF: case LINUX_HDIO_DRIVE_TASK: case LINUX_HDIO_DRIVE_CMD: case LINUX_HDIO_SET_MULTCOUNT: case LINUX_HDIO_SET_UNMASKINTR: case LINUX_HDIO_SET_KEEPSETTINGS: case LINUX_HDIO_SET_32BIT: case LINUX_HDIO_SET_NOWERR: case LINUX_HDIO_SET_DMA: case LINUX_HDIO_SET_PIO_MODE: case LINUX_HDIO_SCAN_HWIF: case LINUX_HDIO_SET_NICE: case LINUX_HDIO_UNREGISTER_HWIF: error = EINVAL; } FRELE(fp); return error; }
/* * We come here in a last attempt to satisfy a Linux ioctl() call */ int linux_machdepioctl(struct proc *p, void *v, register_t *retval) { struct linux_sys_ioctl_args /* { syscallarg(int) fd; syscallarg(u_long) com; syscallarg(caddr_t) data; } */ *uap = v; struct sys_ioctl_args bia; u_long com; int error; #if (NWSDISPLAY > 0 && defined(WSDISPLAY_COMPAT_USL)) struct vt_mode lvt; caddr_t bvtp, sg; #endif struct filedesc *fdp; struct file *fp; int fd; int (*ioctlf)(struct file *, u_long, caddr_t, struct proc *); struct ioctl_pt pt; fd = SCARG(uap, fd); SCARG(&bia, fd) = SCARG(uap, fd); SCARG(&bia, data) = SCARG(uap, data); com = SCARG(uap, com); fdp = p->p_fd; if ((fp = fd_getfile(fdp, fd)) == NULL) return (EBADF); switch (com) { #if (NWSDISPLAY > 0 && defined(WSDISPLAY_COMPAT_USL)) case LINUX_KDGKBMODE: com = KDGKBMODE; break; case LINUX_KDSKBMODE: com = KDSKBMODE; if ((unsigned)SCARG(uap, data) == LINUX_K_MEDIUMRAW) SCARG(&bia, data) = (caddr_t)K_RAW; break; case LINUX_KIOCSOUND: SCARG(&bia, data) = (caddr_t)(((unsigned long)SCARG(&bia, data)) & 0xffff); /* FALLTHROUGH */ case LINUX_KDMKTONE: com = KDMKTONE; break; case LINUX_KDSETMODE: com = KDSETMODE; break; case LINUX_KDGETMODE: #if NWSDISPLAY > 0 && defined(WSDISPLAY_COMPAT_USL) com = WSDISPLAYIO_GMODE; #else com = KDGETMODE; #endif break; case LINUX_KDENABIO: com = KDENABIO; break; case LINUX_KDDISABIO: com = KDDISABIO; break; case LINUX_KDGETLED: com = KDGETLED; break; case LINUX_KDSETLED: com = KDSETLED; break; case LINUX_VT_OPENQRY: com = VT_OPENQRY; break; case LINUX_VT_GETMODE: { int sig; SCARG(&bia, com) = VT_GETMODE; if ((error = sys_ioctl(p, &bia, retval))) return error; if ((error = copyin(SCARG(uap, data), (caddr_t)&lvt, sizeof (struct vt_mode)))) return error; /* We need to bounds check here in case there is a race with another thread */ if ((error = bsd_to_linux_signal(lvt.relsig, &sig))) return error; lvt.relsig = sig; if ((error = bsd_to_linux_signal(lvt.acqsig, &sig))) return error; lvt.acqsig = sig; if ((error = bsd_to_linux_signal(lvt.frsig, &sig))) return error; lvt.frsig = sig; return copyout((caddr_t)&lvt, SCARG(uap, data), sizeof (struct vt_mode)); } case LINUX_VT_SETMODE: { int sig; com = VT_SETMODE; if ((error = copyin(SCARG(uap, data), (caddr_t)&lvt, sizeof (struct vt_mode)))) return error; if ((error = linux_to_bsd_signal(lvt.relsig, &sig))) return error; lvt.relsig = sig; if ((error = linux_to_bsd_signal(lvt.acqsig, &sig))) return error; lvt.acqsig = sig; if ((error = linux_to_bsd_signal(lvt.frsig, &sig))) return error; lvt.frsig = sig; sg = stackgap_init(p->p_emul); bvtp = stackgap_alloc(&sg, sizeof (struct vt_mode)); if ((error = copyout(&lvt, bvtp, sizeof (struct vt_mode)))) return error; SCARG(&bia, data) = bvtp; break; } case LINUX_VT_DISALLOCATE: /* XXX should use WSDISPLAYIO_DELSCREEN */ return 0; case LINUX_VT_RELDISP: com = VT_RELDISP; break; case LINUX_VT_ACTIVATE: com = VT_ACTIVATE; break; case LINUX_VT_WAITACTIVE: com = VT_WAITACTIVE; break; case LINUX_VT_GETSTATE: com = VT_GETSTATE; break; case LINUX_KDGKBTYPE: { char tmp = KB_101; /* This is what Linux does */ return copyout(&tmp, SCARG(uap, data), sizeof(char)); } #endif default: /* * Unknown to us. If it's on a device, just pass it through * using PTIOCLINUX, the device itself might be able to * make some sense of it. * XXX hack: if the function returns EJUSTRETURN, * it has stuffed a sysctl return value in pt.data. */ FREF(fp); ioctlf = fp->f_ops->fo_ioctl; pt.com = SCARG(uap, com); pt.data = SCARG(uap, data); error = ioctlf(fp, PTIOCLINUX, (caddr_t)&pt, p); FRELE(fp); if (error == EJUSTRETURN) { retval[0] = (register_t)pt.data; error = 0; } if (error == ENOTTY) printf("linux_machdepioctl: invalid ioctl %08lx\n", com); return (error); } SCARG(&bia, com) = com; return sys_ioctl(p, &bia, retval); }
int sys_mmap(struct proc *p, void *v, register_t *retval) { struct sys_mmap_args /* { syscallarg(void *) addr; syscallarg(size_t) len; syscallarg(int) prot; syscallarg(int) flags; syscallarg(int) fd; syscallarg(long) pad; syscallarg(off_t) pos; } */ *uap = v; vaddr_t addr; struct vattr va; off_t pos; vsize_t size, pageoff; vm_prot_t prot, maxprot; int flags, fd; vaddr_t vm_min_address = VM_MIN_ADDRESS; struct filedesc *fdp = p->p_fd; struct file *fp = NULL; struct vnode *vp; caddr_t handle; int error; /* * first, extract syscall args from the uap. */ addr = (vaddr_t) SCARG(uap, addr); size = (vsize_t) SCARG(uap, len); prot = SCARG(uap, prot); flags = SCARG(uap, flags); fd = SCARG(uap, fd); pos = SCARG(uap, pos); /* * Fixup the old deprecated MAP_COPY into MAP_PRIVATE, and * validate the flags. */ if ((prot & VM_PROT_ALL) != prot) return (EINVAL); if ((flags & MAP_FLAGMASK) != flags) return (EINVAL); if (flags & MAP_COPY) flags = (flags & ~MAP_COPY) | MAP_PRIVATE; if ((flags & (MAP_SHARED|MAP_PRIVATE)) == (MAP_SHARED|MAP_PRIVATE)) return (EINVAL); if (flags & MAP_DENYWRITE) return (EINVAL); /* * align file position and save offset. adjust size. */ ALIGN_ADDR(pos, size, pageoff); /* * now check (MAP_FIXED) or get (!MAP_FIXED) the "addr" */ if (flags & MAP_FIXED) { /* adjust address by the same amount as we did the offset */ addr -= pageoff; if (addr & PAGE_MASK) return (EINVAL); /* not page aligned */ if (addr > SIZE_MAX - size) return (EINVAL); /* no wrapping! */ if (VM_MAXUSER_ADDRESS > 0 && (addr + size) > VM_MAXUSER_ADDRESS) return (EINVAL); if (vm_min_address > 0 && addr < vm_min_address) return (EINVAL); } else { /* * not fixed: make sure we skip over the largest possible heap. * we will refine our guess later (e.g. to account for VAC, etc) */ if (addr == 0) addr = uvm_map_hint(p, prot); else if (!(flags & MAP_TRYFIXED) && addr < (vaddr_t)p->p_vmspace->vm_daddr) addr = uvm_map_hint(p, prot); } /* * check for file mappings (i.e. not anonymous) and verify file. */ if ((flags & MAP_ANON) == 0) { if ((fp = fd_getfile(fdp, fd)) == NULL) return (EBADF); FREF(fp); if (fp->f_type != DTYPE_VNODE) { error = ENODEV; /* only mmap vnodes! */ goto out; } vp = (struct vnode *)fp->f_data; /* convert to vnode */ if (vp->v_type != VREG && vp->v_type != VCHR && vp->v_type != VBLK) { error = ENODEV; /* only REG/CHR/BLK support mmap */ goto out; } if (vp->v_type == VREG && (pos + size) < pos) { error = EINVAL; /* no offset wrapping */ goto out; } /* special case: catch SunOS style /dev/zero */ if (vp->v_type == VCHR && iszerodev(vp->v_rdev)) { flags |= MAP_ANON; FRELE(fp); fp = NULL; goto is_anon; } /* * Old programs may not select a specific sharing type, so * default to an appropriate one. * * XXX: how does MAP_ANON fit in the picture? */ if ((flags & (MAP_SHARED|MAP_PRIVATE)) == 0) { #if defined(DEBUG) printf("WARNING: defaulted mmap() share type to " "%s (pid %d comm %s)\n", vp->v_type == VCHR ? "MAP_SHARED" : "MAP_PRIVATE", p->p_pid, p->p_comm); #endif if (vp->v_type == VCHR) flags |= MAP_SHARED; /* for a device */ else flags |= MAP_PRIVATE; /* for a file */ } /* * MAP_PRIVATE device mappings don't make sense (and aren't * supported anyway). However, some programs rely on this, * so just change it to MAP_SHARED. */ if (vp->v_type == VCHR && (flags & MAP_PRIVATE) != 0) { flags = (flags & ~MAP_PRIVATE) | MAP_SHARED; } #ifdef ANOUBIS /* Force DENYWRITE mappings if file->denywrite is set. */ if (fp->denywrite) flags |= MAP_DENYWRITE; #endif /* * now check protection */ /* * Don't allow the file to be mapped into executable memory if * the underlying file system is marked as 'noexec'. */ if (prot & PROT_EXEC && vp->v_mount->mnt_flag & MNT_NOEXEC) { error = EACCES; goto out; } maxprot = VM_PROT_EXECUTE; /* check read access */ if (fp->f_flag & FREAD) maxprot |= VM_PROT_READ; else if (prot & PROT_READ) { error = EACCES; goto out; } /* PROT_EXEC only makes sense if the descriptor is readable. */ if (!(fp->f_flag & FREAD) && prot & PROT_EXEC) { error = EACCES; goto out; } /* check write access, shared case first */ if (flags & MAP_SHARED) { /* * if the file is writable, only add PROT_WRITE to * maxprot if the file is not immutable, append-only. * otherwise, if we have asked for PROT_WRITE, return * EPERM. */ if (fp->f_flag & FWRITE) { if ((error = VOP_GETATTR(vp, &va, p->p_ucred, p))) goto out; if ((va.va_flags & (IMMUTABLE|APPEND)) == 0) maxprot |= VM_PROT_WRITE; else if (prot & PROT_WRITE) { error = EPERM; goto out; } } else if (prot & PROT_WRITE) { error = EACCES; goto out; } } else { /* MAP_PRIVATE mappings can always write to */ maxprot |= VM_PROT_WRITE; } #ifdef MAC error = mac_vnode_check_mmap(p->p_ucred, vp, prot, flags); if (error) goto out; #endif vfs_mark_atime(vp, p->p_ucred); /* * set handle to vnode */ handle = (caddr_t)vp; } else { /* MAP_ANON case */ /* * XXX What do we do about (MAP_SHARED|MAP_PRIVATE) == 0? */ if (fd != -1) { error = EINVAL; goto out; } is_anon: /* label for SunOS style /dev/zero */ handle = NULL; maxprot = VM_PROT_ALL; pos = 0; } if ((flags & MAP_ANON) != 0 || ((flags & MAP_PRIVATE) != 0 && (prot & PROT_WRITE) != 0)) { if (size > (p->p_rlimit[RLIMIT_DATA].rlim_cur - ptoa(p->p_vmspace->vm_dused))) { error = ENOMEM; goto out; } } /* * now let kernel internal function uvm_mmap do the work. */ error = uvm_mmap(&p->p_vmspace->vm_map, &addr, size, prot, maxprot, flags, handle, pos, p->p_rlimit[RLIMIT_MEMLOCK].rlim_cur, p); if (error == 0) /* remember to add offset */ *retval = (register_t)(addr + pageoff); out: if (fp) FRELE(fp); return (error); }