summaryrefslogtreecommitdiffstats
path: root/sys/compat
diff options
context:
space:
mode:
authormav <mav@FreeBSD.org>2014-07-02 10:16:12 +0000
committermav <mav@FreeBSD.org>2014-07-02 10:16:12 +0000
commit927a52fbbfa5d8d56eec3abf498e8d21614347b3 (patch)
tree09e7599c2534a64015e00045f4f1f3d425ecbdde /sys/compat
parentd0f1ea0d9e0d37768f65929fa6c904891670c408 (diff)
downloadFreeBSD-src-927a52fbbfa5d8d56eec3abf498e8d21614347b3.zip
FreeBSD-src-927a52fbbfa5d8d56eec3abf498e8d21614347b3.tar.gz
MFC r266981:
Overhaul CAM SG driver IOCTL interfaces. Make it really work for native FreeBSD programs. Before this it was broken for years due to different number of pointer dereferences in Linux and FreeBSD IOCTL paths, permanently returning errors to FreeBSD programs. This change breaks the driver FreeBSD IOCTL ABI, making it more strict, but since it was not working any way -- who bother. Add shims for 32-bit programs on 64-bit host, translating the argument of the SG_IO IOCTL for both FreeBSD and Linux ABIs. With this change I was able to run 32-bit Linux sg3_utils tools and simple 32 and 64-bit FreeBSD test tools on both 32 and 64-bit FreeBSD systems.
Diffstat (limited to 'sys/compat')
-rw-r--r--sys/compat/freebsd32/freebsd32_ioctl.c69
-rw-r--r--sys/compat/freebsd32/freebsd32_ioctl.h3
-rw-r--r--sys/compat/linux/linux_ioctl.c129
3 files changed, 196 insertions, 5 deletions
diff --git a/sys/compat/freebsd32/freebsd32_ioctl.c b/sys/compat/freebsd32/freebsd32_ioctl.c
index 1f90e58..552b26a 100644
--- a/sys/compat/freebsd32/freebsd32_ioctl.c
+++ b/sys/compat/freebsd32/freebsd32_ioctl.c
@@ -344,6 +344,71 @@ cleanup:
return (error);
}
+static int
+freebsd32_ioctl_sg(struct thread *td,
+ struct freebsd32_ioctl_args *uap, struct file *fp)
+{
+ struct sg_io_hdr io;
+ struct sg_io_hdr32 io32;
+ int error;
+
+ if ((error = copyin(uap->data, &io32, sizeof(io32))) != 0)
+ return (error);
+
+ CP(io32, io, interface_id);
+ CP(io32, io, dxfer_direction);
+ CP(io32, io, cmd_len);
+ CP(io32, io, mx_sb_len);
+ CP(io32, io, iovec_count);
+ CP(io32, io, dxfer_len);
+ PTRIN_CP(io32, io, dxferp);
+ PTRIN_CP(io32, io, cmdp);
+ PTRIN_CP(io32, io, sbp);
+ CP(io32, io, timeout);
+ CP(io32, io, flags);
+ CP(io32, io, pack_id);
+ PTRIN_CP(io32, io, usr_ptr);
+ CP(io32, io, status);
+ CP(io32, io, masked_status);
+ CP(io32, io, msg_status);
+ CP(io32, io, sb_len_wr);
+ CP(io32, io, host_status);
+ CP(io32, io, driver_status);
+ CP(io32, io, resid);
+ CP(io32, io, duration);
+ CP(io32, io, info);
+
+ if ((error = fo_ioctl(fp, SG_IO, (caddr_t)&io, td->td_ucred, td)) != 0)
+ return (error);
+
+ CP(io, io32, interface_id);
+ CP(io, io32, dxfer_direction);
+ CP(io, io32, cmd_len);
+ CP(io, io32, mx_sb_len);
+ CP(io, io32, iovec_count);
+ CP(io, io32, dxfer_len);
+ PTROUT_CP(io, io32, dxferp);
+ PTROUT_CP(io, io32, cmdp);
+ PTROUT_CP(io, io32, sbp);
+ CP(io, io32, timeout);
+ CP(io, io32, flags);
+ CP(io, io32, pack_id);
+ PTROUT_CP(io, io32, usr_ptr);
+ CP(io, io32, status);
+ CP(io, io32, masked_status);
+ CP(io, io32, msg_status);
+ CP(io, io32, sb_len_wr);
+ CP(io, io32, host_status);
+ CP(io, io32, driver_status);
+ CP(io, io32, resid);
+ CP(io, io32, duration);
+ CP(io, io32, info);
+
+ error = copyout(&io32, uap->data, sizeof(io32));
+
+ return (error);
+}
+
int
freebsd32_ioctl(struct thread *td, struct freebsd32_ioctl_args *uap)
{
@@ -393,6 +458,10 @@ freebsd32_ioctl(struct thread *td, struct freebsd32_ioctl_args *uap)
error = freebsd32_ioctl_pciocgetconf(td, uap, fp);
break;
+ case SG_IO_32:
+ error = freebsd32_ioctl_sg(td, uap, fp);
+ break;
+
default:
fdrop(fp, td);
ap.fd = uap->fd;
diff --git a/sys/compat/freebsd32/freebsd32_ioctl.h b/sys/compat/freebsd32/freebsd32_ioctl.h
index e0beb73..18cfc95 100644
--- a/sys/compat/freebsd32/freebsd32_ioctl.h
+++ b/sys/compat/freebsd32/freebsd32_ioctl.h
@@ -32,6 +32,8 @@
#ifndef _COMPAT_FREEBSD32_IOCTL_H_
#define _COMPAT_FREEBSD32_IOCTL_H_
+#include <cam/scsi/scsi_sg.h>
+
typedef __uint32_t caddr_t32;
struct ioc_toc_header32 {
@@ -122,5 +124,6 @@ struct pci_conf_io32 {
#define MEMRANGE_GET32 _IOWR('m', 50, struct mem_range_op32)
#define MEMRANGE_SET32 _IOW('m', 51, struct mem_range_op32)
#define PCIOCGETCONF_32 _IOWR('p', 5, struct pci_conf_io32)
+#define SG_IO_32 _IOWR(SGIOC, 0x85, struct sg_io_hdr32)
#endif /* _COMPAT_FREEBSD32_IOCTL_H_ */
diff --git a/sys/compat/linux/linux_ioctl.c b/sys/compat/linux/linux_ioctl.c
index 2a4016a..395bba5 100644
--- a/sys/compat/linux/linux_ioctl.c
+++ b/sys/compat/linux/linux_ioctl.c
@@ -91,6 +91,8 @@ __FBSDID("$FreeBSD$");
#include <contrib/v4l/videodev2.h>
#include <compat/linux/linux_videodev2_compat.h>
+#include <cam/scsi/scsi_sg.h>
+
CTASSERT(LINUX_IFNAMSIZ == IFNAMSIZ);
FEATURE(linuxulator_v4l, "V4L ioctl wrapper support in the linuxulator");
@@ -1645,9 +1647,32 @@ linux_ioctl_cdrom(struct thread *td, struct linux_ioctl_args *args)
}
case LINUX_SCSI_GET_BUS_NUMBER:
+ {
+ struct sg_scsi_id id;
+
+ error = fo_ioctl(fp, SG_GET_SCSI_ID, (caddr_t)&id,
+ td->td_ucred, td);
+ if (error)
+ break;
+ error = copyout(&id.channel, (void *)args->arg, sizeof(int));
+ break;
+ }
+
case LINUX_SCSI_GET_IDLUN:
- error = linux_ioctl_sg(td, args);
+ {
+ struct sg_scsi_id id;
+ struct scsi_idlun idl;
+
+ error = fo_ioctl(fp, SG_GET_SCSI_ID, (caddr_t)&id,
+ td->td_ucred, td);
+ if (error)
+ break;
+ idl.dev_id = (id.scsi_id & 0xff) + ((id.lun & 0xff) << 8) +
+ ((id.channel & 0xff) << 16) + ((id.host_no & 0xff) << 24);
+ idl.host_unique_id = id.host_no;
+ error = copyout(&idl, (void *)args->arg, sizeof(idl));
break;
+ }
/* LINUX_CDROM_SEND_PACKET */
/* LINUX_CDROM_NEXT_WRITABLE */
@@ -2617,12 +2642,20 @@ linux_ioctl_drm(struct thread *td, struct linux_ioctl_args *args)
return sys_ioctl(td, (struct ioctl_args *)args);
}
+#ifdef COMPAT_LINUX32
+#define CP(src,dst,fld) do { (dst).fld = (src).fld; } while (0)
+#define PTRIN_CP(src,dst,fld) \
+ do { (dst).fld = PTRIN((src).fld); } while (0)
+#define PTROUT_CP(src,dst,fld) \
+ do { (dst).fld = PTROUT((src).fld); } while (0)
+
static int
-linux_ioctl_sg(struct thread *td, struct linux_ioctl_args *args)
+linux_ioctl_sg_io(struct thread *td, struct linux_ioctl_args *args)
{
+ struct sg_io_hdr io;
+ struct sg_io_hdr32 io32;
cap_rights_t rights;
struct file *fp;
- u_long cmd;
int error;
error = fget(td, args->fd, cap_rights_init(&rights, CAP_IOCTL), &fp);
@@ -2630,12 +2663,98 @@ linux_ioctl_sg(struct thread *td, struct linux_ioctl_args *args)
printf("sg_linux_ioctl: fget returned %d\n", error);
return (error);
}
- cmd = args->cmd;
- error = (fo_ioctl(fp, cmd, (caddr_t)args->arg, td->td_ucred, td));
+ if ((error = copyin((void *)args->arg, &io32, sizeof(io32))) != 0)
+ goto out;
+
+ CP(io32, io, interface_id);
+ CP(io32, io, dxfer_direction);
+ CP(io32, io, cmd_len);
+ CP(io32, io, mx_sb_len);
+ CP(io32, io, iovec_count);
+ CP(io32, io, dxfer_len);
+ PTRIN_CP(io32, io, dxferp);
+ PTRIN_CP(io32, io, cmdp);
+ PTRIN_CP(io32, io, sbp);
+ CP(io32, io, timeout);
+ CP(io32, io, flags);
+ CP(io32, io, pack_id);
+ PTRIN_CP(io32, io, usr_ptr);
+ CP(io32, io, status);
+ CP(io32, io, masked_status);
+ CP(io32, io, msg_status);
+ CP(io32, io, sb_len_wr);
+ CP(io32, io, host_status);
+ CP(io32, io, driver_status);
+ CP(io32, io, resid);
+ CP(io32, io, duration);
+ CP(io32, io, info);
+
+ if ((error = fo_ioctl(fp, SG_IO, (caddr_t)&io, td->td_ucred, td)) != 0)
+ goto out;
+
+ CP(io, io32, interface_id);
+ CP(io, io32, dxfer_direction);
+ CP(io, io32, cmd_len);
+ CP(io, io32, mx_sb_len);
+ CP(io, io32, iovec_count);
+ CP(io, io32, dxfer_len);
+ PTROUT_CP(io, io32, dxferp);
+ PTROUT_CP(io, io32, cmdp);
+ PTROUT_CP(io, io32, sbp);
+ CP(io, io32, timeout);
+ CP(io, io32, flags);
+ CP(io, io32, pack_id);
+ PTROUT_CP(io, io32, usr_ptr);
+ CP(io, io32, status);
+ CP(io, io32, masked_status);
+ CP(io, io32, msg_status);
+ CP(io, io32, sb_len_wr);
+ CP(io, io32, host_status);
+ CP(io, io32, driver_status);
+ CP(io, io32, resid);
+ CP(io, io32, duration);
+ CP(io, io32, info);
+
+ error = copyout(&io32, (void *)args->arg, sizeof(io32));
+
+out:
fdrop(fp, td);
return (error);
}
+#endif
+
+static int
+linux_ioctl_sg(struct thread *td, struct linux_ioctl_args *args)
+{
+
+ switch (args->cmd) {
+ case LINUX_SG_GET_VERSION_NUM:
+ args->cmd = SG_GET_VERSION_NUM;
+ break;
+ case LINUX_SG_SET_TIMEOUT:
+ args->cmd = SG_SET_TIMEOUT;
+ break;
+ case LINUX_SG_GET_TIMEOUT:
+ args->cmd = SG_GET_TIMEOUT;
+ break;
+ case LINUX_SG_IO:
+ args->cmd = SG_IO;
+#ifdef COMPAT_LINUX32
+ return (linux_ioctl_sg_io(td, args));
+#endif
+ break;
+ case LINUX_SG_GET_RESERVED_SIZE:
+ args->cmd = SG_GET_RESERVED_SIZE;
+ break;
+ case LINUX_SG_GET_SCSI_ID:
+ args->cmd = SG_GET_SCSI_ID;
+ break;
+ default:
+ return (ENODEV);
+ }
+ return (sys_ioctl(td, (struct ioctl_args *)args));
+}
/*
* Video4Linux (V4L) ioctl handler
OpenPOWER on IntegriCloud