cregit-Linux how code gets into the kernel

Release 4.11 drivers/scsi/scsi_debug.c

Directory: drivers/scsi
/*
 * vvvvvvvvvvvvvvvvvvvvvvv Original vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
 *  Copyright (C) 1992  Eric Youngdale
 *  Simulate a host adapter with 2 disks attached.  Do a lot of checking
 *  to make sure that we are not getting blocks mixed up, and PANIC if
 *  anything out of the ordinary is seen.
 * ^^^^^^^^^^^^^^^^^^^^^^^ Original ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 *
 * Copyright (C) 2001 - 2016 Douglas Gilbert
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2, or (at your option)
 * any later version.
 *
 *  For documentation see http://sg.danny.cz/sg/sdebug26.html
 *
 */



#define pr_fmt(fmt) KBUILD_MODNAME ":%s: " fmt, __func__

#include <linux/module.h>

#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/jiffies.h>
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/string.h>
#include <linux/genhd.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/proc_fs.h>
#include <linux/vmalloc.h>
#include <linux/moduleparam.h>
#include <linux/scatterlist.h>
#include <linux/blkdev.h>
#include <linux/crc-t10dif.h>
#include <linux/spinlock.h>
#include <linux/interrupt.h>
#include <linux/atomic.h>
#include <linux/hrtimer.h>
#include <linux/uuid.h>
#include <linux/t10-pi.h>

#include <net/checksum.h>

#include <asm/unaligned.h>

#include <scsi/scsi.h>
#include <scsi/scsi_cmnd.h>
#include <scsi/scsi_device.h>
#include <scsi/scsi_host.h>
#include <scsi/scsicam.h>
#include <scsi/scsi_eh.h>
#include <scsi/scsi_tcq.h>
#include <scsi/scsi_dbg.h>

#include "sd.h"
#include "scsi_logging.h"

/* make sure inq_product_rev string corresponds to this version */

#define SDEBUG_VERSION "1.86"

static const char *sdebug_version_date = "20160430";


#define MY_NAME "scsi_debug"

/* Additional Sense Code (ASC) */

#define NO_ADDITIONAL_SENSE 0x0

#define LOGICAL_UNIT_NOT_READY 0x4

#define LOGICAL_UNIT_COMMUNICATION_FAILURE 0x8

#define UNRECOVERED_READ_ERR 0x11

#define PARAMETER_LIST_LENGTH_ERR 0x1a

#define INVALID_OPCODE 0x20

#define LBA_OUT_OF_RANGE 0x21

#define INVALID_FIELD_IN_CDB 0x24

#define INVALID_FIELD_IN_PARAM_LIST 0x26

#define UA_RESET_ASC 0x29

#define UA_CHANGED_ASC 0x2a

#define TARGET_CHANGED_ASC 0x3f

#define LUNS_CHANGED_ASCQ 0x0e

#define INSUFF_RES_ASC 0x55

#define INSUFF_RES_ASCQ 0x3

#define POWER_ON_RESET_ASCQ 0x0

#define BUS_RESET_ASCQ 0x2	
/* scsi bus reset occurred */

#define MODE_CHANGED_ASCQ 0x1	
/* mode parameters changed */

#define CAPACITY_CHANGED_ASCQ 0x9

#define SAVING_PARAMS_UNSUP 0x39

#define TRANSPORT_PROBLEM 0x4b

#define THRESHOLD_EXCEEDED 0x5d

#define LOW_POWER_COND_ON 0x5e

#define MISCOMPARE_VERIFY_ASC 0x1d

#define MICROCODE_CHANGED_ASCQ 0x1	
/* with TARGET_CHANGED_ASC */

#define MICROCODE_CHANGED_WO_RESET_ASCQ 0x16

/* Additional Sense Code Qualifier (ASCQ) */

#define ACK_NAK_TO 0x3

/* Default values for driver parameters */

#define DEF_NUM_HOST   1

#define DEF_NUM_TGTS   1

#define DEF_MAX_LUNS   1
/* With these defaults, this driver will make 1 host with 1 target
 * (id 0) containing 1 logical unit (lun 0). That is 1 device.
 */

#define DEF_ATO 1

#define DEF_JDELAY   1		
/* if > 0 unit is a jiffy */

#define DEF_DEV_SIZE_MB   8

#define DEF_DIF 0

#define DEF_DIX 0

#define DEF_D_SENSE   0

#define DEF_EVERY_NTH   0

#define DEF_FAKE_RW	0

#define DEF_GUARD 0

#define DEF_HOST_LOCK 0

#define DEF_LBPU 0

#define DEF_LBPWS 0

#define DEF_LBPWS10 0

#define DEF_LBPRZ 1

#define DEF_LOWEST_ALIGNED 0

#define DEF_NDELAY   0		
/* if > 0 unit is a nanosecond */

#define DEF_NO_LUN_0   0

#define DEF_NUM_PARTS   0

#define DEF_OPTS   0

#define DEF_OPT_BLKS 1024

#define DEF_PHYSBLK_EXP 0

#define DEF_OPT_XFERLEN_EXP 0

#define DEF_PTYPE   TYPE_DISK

#define DEF_REMOVABLE false

#define DEF_SCSI_LEVEL   7    
/* INQUIRY, byte2 [6->SPC-4; 7->SPC-5] */

#define DEF_SECTOR_SIZE 512

#define DEF_UNMAP_ALIGNMENT 0

#define DEF_UNMAP_GRANULARITY 1

#define DEF_UNMAP_MAX_BLOCKS 0xFFFFFFFF

#define DEF_UNMAP_MAX_DESC 256

#define DEF_VIRTUAL_GB   0

#define DEF_VPD_USE_HOSTNO 1

#define DEF_WRITESAME_LENGTH 0xFFFF

#define DEF_STRICT 0

#define DEF_STATISTICS false

#define DEF_SUBMIT_QUEUES 1

#define DEF_UUID_CTL 0

#define JDELAY_OVERRIDDEN -9999


#define SDEBUG_LUN_0_VAL 0

/* bit mask values for sdebug_opts */

#define SDEBUG_OPT_NOISE		1

#define SDEBUG_OPT_MEDIUM_ERR		2

#define SDEBUG_OPT_TIMEOUT		4

#define SDEBUG_OPT_RECOVERED_ERR	8

#define SDEBUG_OPT_TRANSPORT_ERR	16

#define SDEBUG_OPT_DIF_ERR		32

#define SDEBUG_OPT_DIX_ERR		64

#define SDEBUG_OPT_MAC_TIMEOUT		128

#define SDEBUG_OPT_SHORT_TRANSFER	0x100

#define SDEBUG_OPT_Q_NOISE		0x200

#define SDEBUG_OPT_ALL_TSF		0x400

#define SDEBUG_OPT_RARE_TSF		0x800

#define SDEBUG_OPT_N_WCE		0x1000

#define SDEBUG_OPT_RESET_NOISE		0x2000

#define SDEBUG_OPT_NO_CDB_NOISE		0x4000

#define SDEBUG_OPT_ALL_NOISE (SDEBUG_OPT_NOISE | SDEBUG_OPT_Q_NOISE | \
                              SDEBUG_OPT_RESET_NOISE)

#define SDEBUG_OPT_ALL_INJECTING (SDEBUG_OPT_RECOVERED_ERR | \
                                  SDEBUG_OPT_TRANSPORT_ERR | \
                                  SDEBUG_OPT_DIF_ERR | SDEBUG_OPT_DIX_ERR | \
                                  SDEBUG_OPT_SHORT_TRANSFER)
/* When "every_nth" > 0 then modulo "every_nth" commands:
 *   - a missing response is simulated if SDEBUG_OPT_TIMEOUT is set
 *   - a RECOVERED_ERROR is simulated on successful read and write
 *     commands if SDEBUG_OPT_RECOVERED_ERR is set.
 *   - a TRANSPORT_ERROR is simulated on successful read and write
 *     commands if SDEBUG_OPT_TRANSPORT_ERR is set.
 *
 * When "every_nth" < 0 then after "- every_nth" commands:
 *   - a missing response is simulated if SDEBUG_OPT_TIMEOUT is set
 *   - a RECOVERED_ERROR is simulated on successful read and write
 *     commands if SDEBUG_OPT_RECOVERED_ERR is set.
 *   - a TRANSPORT_ERROR is simulated on successful read and write
 *     commands if _DEBUG_OPT_TRANSPORT_ERR is set.
 * This will continue on every subsequent command until some other action
 * occurs (e.g. the user * writing a new value (other than -1 or 1) to
 * every_nth via sysfs).
 */

/* As indicated in SAM-5 and SPC-4 Unit Attentions (UAs) are returned in
 * priority order. In the subset implemented here lower numbers have higher
 * priority. The UA numbers should be a sequence starting from 0 with
 * SDEBUG_NUM_UAS being 1 higher than the highest numbered UA. */

#define SDEBUG_UA_POR 0		
/* Power on, reset, or bus device reset */

#define SDEBUG_UA_BUS_RESET 1

#define SDEBUG_UA_MODE_CHANGED 2

#define SDEBUG_UA_CAPACITY_CHANGED 3

#define SDEBUG_UA_LUNS_CHANGED 4

#define SDEBUG_UA_MICROCODE_CHANGED 5	
/* simulate firmware change */

#define SDEBUG_UA_MICROCODE_CHANGED_WO_RESET 6

#define SDEBUG_NUM_UAS 7

/* when 1==SDEBUG_OPT_MEDIUM_ERR, a medium error is simulated at this
 * sector on read commands: */

#define OPT_MEDIUM_ERR_ADDR   0x1234 
/* that's sector 4660 in decimal */

#define OPT_MEDIUM_ERR_NUM    10     
/* number of consecutive medium errs */

/* If REPORT LUNS has luns >= 256 it can choose "flat space" (value 1)
 * or "peripheral device" addressing (value 0) */

#define SAM2_LUN_ADDRESS_METHOD 0

/* SDEBUG_CANQUEUE is the maximum number of commands that can be queued
 * (for response) per submit queue at one time. Can be reduced by max_queue
 * option. Command responses are not queued when jdelay=0 and ndelay=0. The
 * per-device DEF_CMD_PER_LUN can be changed via sysfs:
 * /sys/class/scsi_device/<h:c:t:l>/device/queue_depth
 * but cannot exceed SDEBUG_CANQUEUE .
 */

#define SDEBUG_CANQUEUE_WORDS  3	
/* a WORD is bits in a long */

#define SDEBUG_CANQUEUE  (SDEBUG_CANQUEUE_WORDS * BITS_PER_LONG)

#define DEF_CMD_PER_LUN  255


#define F_D_IN			1

#define F_D_OUT			2

#define F_D_OUT_MAYBE		4	
/* WRITE SAME, NDOB bit */

#define F_D_UNKN		8

#define F_RL_WLUN_OK		0x10

#define F_SKIP_UA		0x20

#define F_DELAY_OVERR		0x40

#define F_SA_LOW		0x80	
/* cdb byte 1, bits 4 to 0 */

#define F_SA_HIGH		0x100	
/* as used by variable length cdbs */

#define F_INV_OP		0x200

#define F_FAKE_RW		0x400

#define F_M_ACCESS		0x800	
/* media access */


#define FF_RESPOND (F_RL_WLUN_OK | F_SKIP_UA | F_DELAY_OVERR)

#define FF_DIRECT_IO (F_M_ACCESS | F_FAKE_RW)

#define FF_SA (F_SA_HIGH | F_SA_LOW)


#define SDEBUG_MAX_PARTS 4


#define SDEBUG_MAX_CMD_LEN 32



struct sdebug_dev_info {
	
struct list_head dev_list;
	
unsigned int channel;
	
unsigned int target;
	
u64 lun;
	
uuid_be lu_name;
	
struct sdebug_host_info *sdbg_host;
	
unsigned long uas_bm[1];
	
atomic_t num_in_q;
	
atomic_t stopped;
	
bool used;
};


struct sdebug_host_info {
	
struct list_head host_list;
	
struct Scsi_Host *shost;
	
struct device dev;
	
struct list_head dev_info_list;
};


#define to_sdebug_host(d)	\
	container_of(d, struct sdebug_host_info, dev)


struct sdebug_defer {
	
struct hrtimer hrt;
	
struct execute_work ew;
	
int sqa_idx;	/* index of sdebug_queue array */
	
int qc_idx;	/* index of sdebug_queued_cmd array within sqa_idx */
	
int issuing_cpu;
};


struct sdebug_queued_cmd {
	/* corresponding bit set in in_use_bm[] in owning struct sdebug_queue
         * instance indicates this slot is in use.
         */
	
struct sdebug_defer *sd_dp;
	
struct scsi_cmnd *a_cmnd;
	
unsigned int inj_recovered:1;
	
unsigned int inj_transport:1;
	
unsigned int inj_dif:1;
	
unsigned int inj_dix:1;
	
unsigned int inj_short:1;
};


struct sdebug_queue {
	
struct sdebug_queued_cmd qc_arr[SDEBUG_CANQUEUE];
	
unsigned long in_use_bm[SDEBUG_CANQUEUE_WORDS];
	
spinlock_t qc_lock;
	
atomic_t blocked;	/* to temporarily stop more being queued */
};


static atomic_t sdebug_cmnd_count;   
/* number of incoming commands */

static atomic_t sdebug_completions;  
/* count of deferred completions */

static atomic_t sdebug_miss_cpus;    
/* submission + completion cpus differ */

static atomic_t sdebug_a_tsf;	     
/* 'almost task set full' counter */


struct opcode_info_t {
	
u8 num_attached;	/* 0 if this is it (i.e. a leaf); use 0xff */
				/* for terminating element */
	
u8 opcode;		/* if num_attached > 0, preferred */
	
u16 sa;			/* service action */
	
u32 flags;		/* OR-ed set of SDEB_F_* */
	
int (*pfp)(struct scsi_cmnd *, struct sdebug_dev_info *);
	
const struct opcode_info_t *arrp;  /* num_attached elements or NULL */
	
u8 len_mask[16];	/* len=len_mask[0], then mask for cdb[1]... */
				/* ignore cdb bytes after position 15 */
};

/* SCSI opcodes (first byte of cdb) of interest mapped onto these indexes */

enum sdeb_opcode_index {
	
SDEB_I_INVALID_OPCODE =	0,
	
SDEB_I_INQUIRY = 1,
	
SDEB_I_REPORT_LUNS = 2,
	
SDEB_I_REQUEST_SENSE = 3,
	
SDEB_I_TEST_UNIT_READY = 4,
	
SDEB_I_MODE_SENSE = 5,		/* 6, 10 */
	
SDEB_I_MODE_SELECT = 6,		/* 6, 10 */
	
SDEB_I_LOG_SENSE = 7,
	
SDEB_I_READ_CAPACITY = 8,	/* 10; 16 is in SA_IN(16) */
	
SDEB_I_READ = 9,		/* 6, 10, 12, 16 */
	
SDEB_I_WRITE = 10,		/* 6, 10, 12, 16 */
	
SDEB_I_START_STOP = 11,
	
SDEB_I_SERV_ACT_IN = 12,	/* 12, 16 */
	
SDEB_I_SERV_ACT_OUT = 13,	/* 12, 16 */
	
SDEB_I_MAINT_IN = 14,
	
SDEB_I_MAINT_OUT = 15,
	
SDEB_I_VERIFY = 16,		/* 10 only */
	
SDEB_I_VARIABLE_LEN = 17,
	
SDEB_I_RESERVE = 18,		/* 6, 10 */
	
SDEB_I_RELEASE = 19,		/* 6, 10 */
	
SDEB_I_ALLOW_REMOVAL = 20,	/* PREVENT ALLOW MEDIUM REMOVAL */
	
SDEB_I_REZERO_UNIT = 21,	/* REWIND in SSC */
	
SDEB_I_ATA_PT = 22,		/* 12, 16 */
	
SDEB_I_SEND_DIAG = 23,
	
SDEB_I_UNMAP = 24,
	
SDEB_I_XDWRITEREAD = 25,	/* 10 only */
	
SDEB_I_WRITE_BUFFER = 26,
	
SDEB_I_WRITE_SAME = 27,		/* 10, 16 */
	
SDEB_I_SYNC_CACHE = 28,		/* 10 only */
	
SDEB_I_COMP_WRITE = 29,
	
SDEB_I_LAST_ELEMENT = 30,	/* keep this last */
};



static const unsigned char opcode_ind_arr[256] = {
/* 0x0; 0x0->0x1f: 6 byte cdbs */
	SDEB_I_TEST_UNIT_READY, SDEB_I_REZERO_UNIT, 0, SDEB_I_REQUEST_SENSE,
	    0, 0, 0, 0,
	SDEB_I_READ, 0, SDEB_I_WRITE, 0, 0, 0, 0, 0,
	0, 0, SDEB_I_INQUIRY, 0, 0, SDEB_I_MODE_SELECT, SDEB_I_RESERVE,
	    SDEB_I_RELEASE,
	0, 0, SDEB_I_MODE_SENSE, SDEB_I_START_STOP, 0, SDEB_I_SEND_DIAG,
	    SDEB_I_ALLOW_REMOVAL, 0,
/* 0x20; 0x20->0x3f: 10 byte cdbs */
	0, 0, 0, 0, 0, SDEB_I_READ_CAPACITY, 0, 0,
	SDEB_I_READ, 0, SDEB_I_WRITE, 0, 0, 0, 0, SDEB_I_VERIFY,
	0, 0, 0, 0, 0, SDEB_I_SYNC_CACHE, 0, 0,
	0, 0, 0, SDEB_I_WRITE_BUFFER, 0, 0, 0, 0,
/* 0x40; 0x40->0x5f: 10 byte cdbs */
	0, SDEB_I_WRITE_SAME, SDEB_I_UNMAP, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, SDEB_I_LOG_SENSE, 0, 0,
	0, 0, 0, SDEB_I_XDWRITEREAD, 0, SDEB_I_MODE_SELECT, SDEB_I_RESERVE,
	    SDEB_I_RELEASE,
	0, 0, SDEB_I_MODE_SENSE, 0, 0, 0, 0, 0,
/* 0x60; 0x60->0x7d are reserved, 0x7e is "extended cdb" */
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
	0, SDEB_I_VARIABLE_LEN,
/* 0x80; 0x80->0x9f: 16 byte cdbs */
	0, 0, 0, 0, 0, SDEB_I_ATA_PT, 0, 0,
	SDEB_I_READ, SDEB_I_COMP_WRITE, SDEB_I_WRITE, 0, 0, 0, 0, 0,
	0, 0, 0, SDEB_I_WRITE_SAME, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, SDEB_I_SERV_ACT_IN, SDEB_I_SERV_ACT_OUT,
/* 0xa0; 0xa0->0xbf: 12 byte cdbs */
	SDEB_I_REPORT_LUNS, SDEB_I_ATA_PT, 0, SDEB_I_MAINT_IN,
	     SDEB_I_MAINT_OUT, 0, 0, 0,
	SDEB_I_READ, SDEB_I_SERV_ACT_OUT, SDEB_I_WRITE, SDEB_I_SERV_ACT_IN,
	     0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0,
/* 0xc0; 0xc0->0xff: vendor specific */
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
};

static int resp_inquiry(struct scsi_cmnd *, struct sdebug_dev_info *);
static int resp_report_luns(struct scsi_cmnd *, struct sdebug_dev_info *);
static int resp_requests(struct scsi_cmnd *, struct sdebug_dev_info *);
static int resp_mode_sense(struct scsi_cmnd *, struct sdebug_dev_info *);
static int resp_mode_select(struct scsi_cmnd *, struct sdebug_dev_info *);
static int resp_log_sense(struct scsi_cmnd *, struct sdebug_dev_info *);
static int resp_readcap(struct scsi_cmnd *, struct sdebug_dev_info *);
static int resp_read_dt0(struct scsi_cmnd *, struct sdebug_dev_info *);
static int resp_write_dt0(struct scsi_cmnd *, struct sdebug_dev_info *);
static int resp_start_stop(struct scsi_cmnd *, struct sdebug_dev_info *);
static int resp_readcap16(struct scsi_cmnd *, struct sdebug_dev_info *);
static int resp_get_lba_status(struct scsi_cmnd *, struct sdebug_dev_info *);
static int resp_report_tgtpgs(struct scsi_cmnd *, struct sdebug_dev_info *);
static int resp_unmap(struct scsi_cmnd *, struct sdebug_dev_info *);
static int resp_rsup_opcodes(struct scsi_cmnd *, struct sdebug_dev_info *);
static int resp_rsup_tmfs(struct scsi_cmnd *, struct sdebug_dev_info *);
static int resp_write_same_10(struct scsi_cmnd *, struct sdebug_dev_info *);
static int resp_write_same_16(struct scsi_cmnd *, struct sdebug_dev_info *);
static int resp_xdwriteread_10(struct scsi_cmnd *, struct sdebug_dev_info *);
static int resp_comp_write(struct scsi_cmnd *, struct sdebug_dev_info *);
static int resp_write_buffer(struct scsi_cmnd *, struct sdebug_dev_info *);


static const struct opcode_info_t msense_iarr[1] = {
	{0, 0x1a, 0, F_D_IN, NULL, NULL,
	    {6,  0xe8, 0xff, 0xff, 0xff, 0xc7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
};


static const struct opcode_info_t mselect_iarr[1] = {
	{0, 0x15, 0, F_D_OUT, NULL, NULL,
	    {6,  0xf1, 0, 0, 0xff, 0xc7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
};


static const struct opcode_info_t read_iarr[3] = {
	{0, 0x28, 0, F_D_IN | FF_DIRECT_IO, resp_read_dt0, NULL,/* READ(10) */
	    {10,  0xff, 0xff, 0xff, 0xff, 0xff, 0x1f, 0xff, 0xff, 0xc7, 0, 0,
	     0, 0, 0, 0} },
	{0, 0x8, 0, F_D_IN | FF_DIRECT_IO, resp_read_dt0, NULL, /* READ(6) */
	    {6,  0xff, 0xff, 0xff, 0xff, 0xc7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
	{0, 0xa8, 0, F_D_IN | FF_DIRECT_IO, resp_read_dt0, NULL,/* READ(12) */
	    {12,  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x9f,
	     0xc7, 0, 0, 0, 0} },
};


static const struct opcode_info_t write_iarr[3] = {
	{0, 0x2a, 0, F_D_OUT | FF_DIRECT_IO, resp_write_dt0, NULL,   /* 10 */
	    {10,  0xfb, 0xff, 0xff, 0xff, 0xff, 0x1f, 0xff, 0xff, 0xc7, 0, 0,
	     0, 0, 0, 0} },
	{0, 0xa, 0, F_D_OUT | FF_DIRECT_IO, resp_write_dt0, NULL,    /* 6 */
	    {6,  0xff, 0xff, 0xff, 0xff, 0xc7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
	{0, 0xaa, 0, F_D_OUT | FF_DIRECT_IO, resp_write_dt0, NULL,   /* 12 */
	    {12,  0xfb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x9f,
	     0xc7, 0, 0, 0, 0} },
};


static const struct opcode_info_t sa_in_iarr[1] = {
	{0, 0x9e, 0x12, F_SA_LOW | F_D_IN, resp_get_lba_status, NULL,
	    {16,  0x12, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
	     0xff, 0xff, 0xff, 0, 0xc7} },
};


static const struct opcode_info_t vl_iarr[1] = {	/* VARIABLE LENGTH */
	{0, 0x7f, 0xb, F_SA_HIGH | F_D_OUT | FF_DIRECT_IO, resp_write_dt0,
	    NULL, {32,  0xc7, 0, 0, 0, 0, 0x1f, 0x18, 0x0, 0xb, 0xfa,
		   0, 0xff, 0xff, 0xff, 0xff} },	/* WRITE(32) */
};


static const struct opcode_info_t maint_in_iarr[2] = {
	{0, 0xa3, 0xc, F_SA_LOW | F_D_IN, resp_rsup_opcodes, NULL,
	    {12,  0xc, 0x87, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0,
	     0xc7, 0, 0, 0, 0} },
	{0, 0xa3, 0xd, F_SA_LOW | F_D_IN, resp_rsup_tmfs, NULL,
	    {12,  0xd, 0x80, 0, 0, 0, 0xff, 0xff, 0xff, 0xff, 0, 0xc7, 0, 0,
	     0, 0} },
};


static const struct opcode_info_t write_same_iarr[1] = {
	{0, 0x93, 0, F_D_OUT_MAYBE | FF_DIRECT_IO, resp_write_same_16, NULL,
	    {16,  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
	     0xff, 0xff, 0xff, 0x1f, 0xc7} },
};


static const struct opcode_info_t reserve_iarr[1] = {
	{0, 0x16, 0, F_D_OUT, NULL, NULL,	/* RESERVE(6) */
	    {6,  0x1f, 0xff, 0xff, 0xff, 0xc7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
};


static const struct opcode_info_t release_iarr[1] = {
	{0, 0x17, 0, F_D_OUT, NULL, NULL,	/* RELEASE(6) */
	    {6,  0x1f, 0xff, 0, 0, 0xc7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
};


/* This array is accessed via SDEB_I_* values. Make sure all are mapped,
 * plus the terminating elements for logic that scans this table such as
 * REPORT SUPPORTED OPERATION CODES. */

static const struct opcode_info_t opcode_info_arr[SDEB_I_LAST_ELEMENT + 1] = {
/* 0 */
	{0, 0, 0, F_INV_OP | FF_RESPOND, NULL, NULL,
	    {0,  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
	{0, 0x12, 0, FF_RESPOND | F_D_IN, resp_inquiry, NULL,
	    {6,  0xe3, 0xff, 0xff, 0xff, 0xc7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
	{0, 0xa0, 0, FF_RESPOND | F_D_IN, resp_report_luns, NULL,
	    {12,  0xe3, 0xff, 0, 0, 0, 0xff, 0xff, 0xff, 0xff, 0, 0xc7, 0, 0,
	     0, 0} },
	{0, 0x3, 0, FF_RESPOND | F_D_IN, resp_requests, NULL,