diff options
Diffstat (limited to 'drivers/block/nvme-scsi.c')
| -rw-r--r-- | drivers/block/nvme-scsi.c | 147 |
1 files changed, 147 insertions, 0 deletions
diff --git a/drivers/block/nvme-scsi.c b/drivers/block/nvme-scsi.c index 4a4ff4eb8e23..4a0ceb64e269 100644 --- a/drivers/block/nvme-scsi.c +++ b/drivers/block/nvme-scsi.c | |||
| @@ -25,6 +25,7 @@ | |||
| 25 | #include <linux/bio.h> | 25 | #include <linux/bio.h> |
| 26 | #include <linux/bitops.h> | 26 | #include <linux/bitops.h> |
| 27 | #include <linux/blkdev.h> | 27 | #include <linux/blkdev.h> |
| 28 | #include <linux/compat.h> | ||
| 28 | #include <linux/delay.h> | 29 | #include <linux/delay.h> |
| 29 | #include <linux/errno.h> | 30 | #include <linux/errno.h> |
| 30 | #include <linux/fs.h> | 31 | #include <linux/fs.h> |
| @@ -3038,6 +3039,152 @@ int nvme_sg_io(struct nvme_ns *ns, struct sg_io_hdr __user *u_hdr) | |||
| 3038 | return retcode; | 3039 | return retcode; |
| 3039 | } | 3040 | } |
| 3040 | 3041 | ||
| 3042 | #ifdef CONFIG_COMPAT | ||
| 3043 | typedef struct sg_io_hdr32 { | ||
| 3044 | compat_int_t interface_id; /* [i] 'S' for SCSI generic (required) */ | ||
| 3045 | compat_int_t dxfer_direction; /* [i] data transfer direction */ | ||
| 3046 | unsigned char cmd_len; /* [i] SCSI command length ( <= 16 bytes) */ | ||
| 3047 | unsigned char mx_sb_len; /* [i] max length to write to sbp */ | ||
| 3048 | unsigned short iovec_count; /* [i] 0 implies no scatter gather */ | ||
| 3049 | compat_uint_t dxfer_len; /* [i] byte count of data transfer */ | ||
| 3050 | compat_uint_t dxferp; /* [i], [*io] points to data transfer memory | ||
| 3051 | or scatter gather list */ | ||
| 3052 | compat_uptr_t cmdp; /* [i], [*i] points to command to perform */ | ||
| 3053 | compat_uptr_t sbp; /* [i], [*o] points to sense_buffer memory */ | ||
| 3054 | compat_uint_t timeout; /* [i] MAX_UINT->no timeout (unit: millisec) */ | ||
| 3055 | compat_uint_t flags; /* [i] 0 -> default, see SG_FLAG... */ | ||
| 3056 | compat_int_t pack_id; /* [i->o] unused internally (normally) */ | ||
| 3057 | compat_uptr_t usr_ptr; /* [i->o] unused internally */ | ||
| 3058 | unsigned char status; /* [o] scsi status */ | ||
| 3059 | unsigned char masked_status; /* [o] shifted, masked scsi status */ | ||
| 3060 | unsigned char msg_status; /* [o] messaging level data (optional) */ | ||
| 3061 | unsigned char sb_len_wr; /* [o] byte count actually written to sbp */ | ||
| 3062 | unsigned short host_status; /* [o] errors from host adapter */ | ||
| 3063 | unsigned short driver_status; /* [o] errors from software driver */ | ||
| 3064 | compat_int_t resid; /* [o] dxfer_len - actual_transferred */ | ||
| 3065 | compat_uint_t duration; /* [o] time taken by cmd (unit: millisec) */ | ||
| 3066 | compat_uint_t info; /* [o] auxiliary information */ | ||
| 3067 | } sg_io_hdr32_t; /* 64 bytes long (on sparc32) */ | ||
| 3068 | |||
| 3069 | typedef struct sg_iovec32 { | ||
| 3070 | compat_uint_t iov_base; | ||
| 3071 | compat_uint_t iov_len; | ||
| 3072 | } sg_iovec32_t; | ||
| 3073 | |||
| 3074 | static int sg_build_iovec(sg_io_hdr_t __user *sgio, void __user *dxferp, u16 iovec_count) | ||
| 3075 | { | ||
| 3076 | sg_iovec_t __user *iov = (sg_iovec_t __user *) (sgio + 1); | ||
| 3077 | sg_iovec32_t __user *iov32 = dxferp; | ||
| 3078 | int i; | ||
| 3079 | |||
| 3080 | for (i = 0; i < iovec_count; i++) { | ||
| 3081 | u32 base, len; | ||
| 3082 | |||
| 3083 | if (get_user(base, &iov32[i].iov_base) || | ||
| 3084 | get_user(len, &iov32[i].iov_len) || | ||
| 3085 | put_user(compat_ptr(base), &iov[i].iov_base) || | ||
| 3086 | put_user(len, &iov[i].iov_len)) | ||
| 3087 | return -EFAULT; | ||
| 3088 | } | ||
| 3089 | |||
| 3090 | if (put_user(iov, &sgio->dxferp)) | ||
| 3091 | return -EFAULT; | ||
| 3092 | return 0; | ||
| 3093 | } | ||
| 3094 | |||
| 3095 | int nvme_sg_io32(struct nvme_ns *ns, unsigned long arg) | ||
| 3096 | { | ||
| 3097 | sg_io_hdr32_t __user *sgio32 = (sg_io_hdr32_t __user *)arg; | ||
| 3098 | sg_io_hdr_t __user *sgio; | ||
| 3099 | u16 iovec_count; | ||
| 3100 | u32 data; | ||
| 3101 | void __user *dxferp; | ||
| 3102 | int err; | ||
| 3103 | int interface_id; | ||
| 3104 | |||
| 3105 | if (get_user(interface_id, &sgio32->interface_id)) | ||
| 3106 | return -EFAULT; | ||
| 3107 | if (interface_id != 'S') | ||
| 3108 | return -EINVAL; | ||
| 3109 | |||
| 3110 | if (get_user(iovec_count, &sgio32->iovec_count)) | ||
| 3111 | return -EFAULT; | ||
| 3112 | |||
| 3113 | { | ||
| 3114 | void __user *top = compat_alloc_user_space(0); | ||
| 3115 | void __user *new = compat_alloc_user_space(sizeof(sg_io_hdr_t) + | ||
| 3116 | (iovec_count * sizeof(sg_iovec_t))); | ||
| 3117 | if (new > top) | ||
| 3118 | return -EINVAL; | ||
| 3119 | |||
| 3120 | sgio = new; | ||
| 3121 | } | ||
| 3122 | |||
| 3123 | /* Ok, now construct. */ | ||
| 3124 | if (copy_in_user(&sgio->interface_id, &sgio32->interface_id, | ||
| 3125 | (2 * sizeof(int)) + | ||
| 3126 | (2 * sizeof(unsigned char)) + | ||
| 3127 | (1 * sizeof(unsigned short)) + | ||
| 3128 | (1 * sizeof(unsigned int)))) | ||
| 3129 | return -EFAULT; | ||
| 3130 | |||
| 3131 | if (get_user(data, &sgio32->dxferp)) | ||
| 3132 | return -EFAULT; | ||
| 3133 | dxferp = compat_ptr(data); | ||
| 3134 | if (iovec_count) { | ||
| 3135 | if (sg_build_iovec(sgio, dxferp, iovec_count)) | ||
| 3136 | return -EFAULT; | ||
| 3137 | } else { | ||
| 3138 | if (put_user(dxferp, &sgio->dxferp)) | ||
| 3139 | return -EFAULT; | ||
| 3140 | } | ||
| 3141 | |||
| 3142 | { | ||
| 3143 | unsigned char __user *cmdp; | ||
| 3144 | unsigned char __user *sbp; | ||
| 3145 | |||
| 3146 | if (get_user(data, &sgio32->cmdp)) | ||
| 3147 | return -EFAULT; | ||
| 3148 | cmdp = compat_ptr(data); | ||
| 3149 | |||
| 3150 | if (get_user(data, &sgio32->sbp)) | ||
| 3151 | return -EFAULT; | ||
| 3152 | sbp = compat_ptr(data); | ||
| 3153 | |||
| 3154 | if (put_user(cmdp, &sgio->cmdp) || | ||
| 3155 | put_user(sbp, &sgio->sbp)) | ||
| 3156 | return -EFAULT; | ||
| 3157 | } | ||
| 3158 | |||
| 3159 | if (copy_in_user(&sgio->timeout, &sgio32->timeout, | ||
| 3160 | 3 * sizeof(int))) | ||
| 3161 | return -EFAULT; | ||
| 3162 | |||
| 3163 | if (get_user(data, &sgio32->usr_ptr)) | ||
| 3164 | return -EFAULT; | ||
| 3165 | if (put_user(compat_ptr(data), &sgio->usr_ptr)) | ||
| 3166 | return -EFAULT; | ||
| 3167 | |||
| 3168 | err = nvme_sg_io(ns, sgio); | ||
| 3169 | if (err >= 0) { | ||
| 3170 | void __user *datap; | ||
| 3171 | |||
| 3172 | if (copy_in_user(&sgio32->pack_id, &sgio->pack_id, | ||
| 3173 | sizeof(int)) || | ||
| 3174 | get_user(datap, &sgio->usr_ptr) || | ||
| 3175 | put_user((u32)(unsigned long)datap, | ||
| 3176 | &sgio32->usr_ptr) || | ||
| 3177 | copy_in_user(&sgio32->status, &sgio->status, | ||
| 3178 | (4 * sizeof(unsigned char)) + | ||
| 3179 | (2 * sizeof(unsigned short)) + | ||
| 3180 | (3 * sizeof(int)))) | ||
| 3181 | err = -EFAULT; | ||
| 3182 | } | ||
| 3183 | |||
| 3184 | return err; | ||
| 3185 | } | ||
| 3186 | #endif | ||
| 3187 | |||
| 3041 | int nvme_sg_get_version_num(int __user *ip) | 3188 | int nvme_sg_get_version_num(int __user *ip) |
| 3042 | { | 3189 | { |
| 3043 | return put_user(sg_version_num, ip); | 3190 | return put_user(sg_version_num, ip); |
