@ -25,6 +25,7 @@
# include <linux/bio.h>
# include <linux/bitops.h>
# include <linux/blkdev.h>
# include <linux/compat.h>
# include <linux/delay.h>
# include <linux/errno.h>
# include <linux/fs.h>
@ -3038,6 +3039,152 @@ int nvme_sg_io(struct nvme_ns *ns, struct sg_io_hdr __user *u_hdr)
return retcode ;
}
# ifdef CONFIG_COMPAT
typedef struct sg_io_hdr32 {
compat_int_t interface_id ; /* [i] 'S' for SCSI generic (required) */
compat_int_t dxfer_direction ; /* [i] data transfer direction */
unsigned char cmd_len ; /* [i] SCSI command length ( <= 16 bytes) */
unsigned char mx_sb_len ; /* [i] max length to write to sbp */
unsigned short iovec_count ; /* [i] 0 implies no scatter gather */
compat_uint_t dxfer_len ; /* [i] byte count of data transfer */
compat_uint_t dxferp ; /* [i], [*io] points to data transfer memory
or scatter gather list */
compat_uptr_t cmdp ; /* [i], [*i] points to command to perform */
compat_uptr_t sbp ; /* [i], [*o] points to sense_buffer memory */
compat_uint_t timeout ; /* [i] MAX_UINT->no timeout (unit: millisec) */
compat_uint_t flags ; /* [i] 0 -> default, see SG_FLAG... */
compat_int_t pack_id ; /* [i->o] unused internally (normally) */
compat_uptr_t usr_ptr ; /* [i->o] unused internally */
unsigned char status ; /* [o] scsi status */
unsigned char masked_status ; /* [o] shifted, masked scsi status */
unsigned char msg_status ; /* [o] messaging level data (optional) */
unsigned char sb_len_wr ; /* [o] byte count actually written to sbp */
unsigned short host_status ; /* [o] errors from host adapter */
unsigned short driver_status ; /* [o] errors from software driver */
compat_int_t resid ; /* [o] dxfer_len - actual_transferred */
compat_uint_t duration ; /* [o] time taken by cmd (unit: millisec) */
compat_uint_t info ; /* [o] auxiliary information */
} sg_io_hdr32_t ; /* 64 bytes long (on sparc32) */
typedef struct sg_iovec32 {
compat_uint_t iov_base ;
compat_uint_t iov_len ;
} sg_iovec32_t ;
static int sg_build_iovec ( sg_io_hdr_t __user * sgio , void __user * dxferp , u16 iovec_count )
{
sg_iovec_t __user * iov = ( sg_iovec_t __user * ) ( sgio + 1 ) ;
sg_iovec32_t __user * iov32 = dxferp ;
int i ;
for ( i = 0 ; i < iovec_count ; i + + ) {
u32 base , len ;
if ( get_user ( base , & iov32 [ i ] . iov_base ) | |
get_user ( len , & iov32 [ i ] . iov_len ) | |
put_user ( compat_ptr ( base ) , & iov [ i ] . iov_base ) | |
put_user ( len , & iov [ i ] . iov_len ) )
return - EFAULT ;
}
if ( put_user ( iov , & sgio - > dxferp ) )
return - EFAULT ;
return 0 ;
}
int nvme_sg_io32 ( struct nvme_ns * ns , unsigned long arg )
{
sg_io_hdr32_t __user * sgio32 = ( sg_io_hdr32_t __user * ) arg ;
sg_io_hdr_t __user * sgio ;
u16 iovec_count ;
u32 data ;
void __user * dxferp ;
int err ;
int interface_id ;
if ( get_user ( interface_id , & sgio32 - > interface_id ) )
return - EFAULT ;
if ( interface_id ! = ' S ' )
return - EINVAL ;
if ( get_user ( iovec_count , & sgio32 - > iovec_count ) )
return - EFAULT ;
{
void __user * top = compat_alloc_user_space ( 0 ) ;
void __user * new = compat_alloc_user_space ( sizeof ( sg_io_hdr_t ) +
( iovec_count * sizeof ( sg_iovec_t ) ) ) ;
if ( new > top )
return - EINVAL ;
sgio = new ;
}
/* Ok, now construct. */
if ( copy_in_user ( & sgio - > interface_id , & sgio32 - > interface_id ,
( 2 * sizeof ( int ) ) +
( 2 * sizeof ( unsigned char ) ) +
( 1 * sizeof ( unsigned short ) ) +
( 1 * sizeof ( unsigned int ) ) ) )
return - EFAULT ;
if ( get_user ( data , & sgio32 - > dxferp ) )
return - EFAULT ;
dxferp = compat_ptr ( data ) ;
if ( iovec_count ) {
if ( sg_build_iovec ( sgio , dxferp , iovec_count ) )
return - EFAULT ;
} else {
if ( put_user ( dxferp , & sgio - > dxferp ) )
return - EFAULT ;
}
{
unsigned char __user * cmdp ;
unsigned char __user * sbp ;
if ( get_user ( data , & sgio32 - > cmdp ) )
return - EFAULT ;
cmdp = compat_ptr ( data ) ;
if ( get_user ( data , & sgio32 - > sbp ) )
return - EFAULT ;
sbp = compat_ptr ( data ) ;
if ( put_user ( cmdp , & sgio - > cmdp ) | |
put_user ( sbp , & sgio - > sbp ) )
return - EFAULT ;
}
if ( copy_in_user ( & sgio - > timeout , & sgio32 - > timeout ,
3 * sizeof ( int ) ) )
return - EFAULT ;
if ( get_user ( data , & sgio32 - > usr_ptr ) )
return - EFAULT ;
if ( put_user ( compat_ptr ( data ) , & sgio - > usr_ptr ) )
return - EFAULT ;
err = nvme_sg_io ( ns , sgio ) ;
if ( err > = 0 ) {
void __user * datap ;
if ( copy_in_user ( & sgio32 - > pack_id , & sgio - > pack_id ,
sizeof ( int ) ) | |
get_user ( datap , & sgio - > usr_ptr ) | |
put_user ( ( u32 ) ( unsigned long ) datap ,
& sgio32 - > usr_ptr ) | |
copy_in_user ( & sgio32 - > status , & sgio - > status ,
( 4 * sizeof ( unsigned char ) ) +
( 2 * sizeof ( unsigned short ) ) +
( 3 * sizeof ( int ) ) ) )
err = - EFAULT ;
}
return err ;
}
# endif
int nvme_sg_get_version_num ( int __user * ip )
{
return put_user ( sg_version_num , ip ) ;