scsi: Add tests for the emulated target

The vast majority of this work was done by Gaelan Steele as part of a
GSoC project [1][2].

[1] https://github.com/rust-vmm/vhost-device/pull/4
[2] https://gist.github.com/Gaelan/febec4e4606e1320026a0924c3bf74d0

Co-developed-by: Erik Schilling <erik.schilling@linaro.org>
Signed-off-by: Erik Schilling <erik.schilling@linaro.org>
Signed-off-by: Gaelan Steele <gbs@canishe.com>
This commit is contained in:
Gaelan Steele 2023-03-08 16:02:08 +01:00 committed by Viresh Kumar
parent be1eaf3f79
commit 1de6d02fb8
6 changed files with 1112 additions and 1 deletions

View File

@ -1,5 +1,5 @@
{
"coverage_score": 67.6,
"coverage_score": 69.6,
"exclude_path": "",
"crate_features": ""
}

View File

@ -7,3 +7,5 @@ pub(crate) mod mode_page;
mod response_data;
pub(crate) mod target;
#[cfg(test)]
mod tests;

View File

@ -0,0 +1,198 @@
// SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause
use super::{do_command_fail_lun, do_command_in_lun, null_image};
use crate::scsi::{
emulation::{block_device::BlockDevice, target::EmulatedTarget},
sense,
};
#[test]
fn test_report_luns() {
let mut target = EmulatedTarget::new();
for _ in 0..5 {
let dev = BlockDevice::new(null_image());
target.add_lun(Box::new(dev));
}
let select_reports = &[0x0, 0x2]; // all but well known, all
for &sr in select_reports {
do_command_in_lun(
&mut target,
6,
&[
0xa0, // REPORT LUNS
0, // reserved
sr, // select report
0, 0, 0, // reserved
0, 0, 1, 0, // alloc length: 256
0, 0,
],
&[],
&[
0, 0, 0, 40, // length: 5*8 = 40
0, 0, 0, 0, // reserved
0, 0, 0, 0, 0, 0, 0, 0, // LUN 0
0, 1, 0, 0, 0, 0, 0, 0, // LUN 1
0, 2, 0, 0, 0, 0, 0, 0, // LUN 2
0, 3, 0, 0, 0, 0, 0, 0, // LUN 3
0, 4, 0, 0, 0, 0, 0, 0, // LUN 4
],
);
}
}
#[test]
fn test_report_luns_empty() {
let mut target = EmulatedTarget::new();
for _ in 0..5 {
let dev = BlockDevice::new(null_image());
target.add_lun(Box::new(dev));
}
// well-known only and several modes explictly defined to return an empty list
// for all but ceratin types of recieving LUNs
let select_reports = &[0x1, 0x10, 0x11, 0x12];
for &sr in select_reports {
do_command_in_lun(
&mut target,
6,
&[
0xa0, // REPORT LUNS
0, // reserved
sr, // select report
0, 0, 0, // reserved
0, 0, 1, 0, // alloc length: 256
0, 0,
],
&[],
&[
0, 0, 0, 0, // length: 0
0, 0, 0, 0, // reserved
],
);
}
}
#[test]
fn test_request_sense() {
let mut target = EmulatedTarget::new();
let dev = BlockDevice::new(null_image());
target.add_lun(Box::new(dev));
do_command_in_lun(
&mut target,
1,
&[
0x3, // REQUEST SENSE
0, // fixed format sense data
0, 0, // reserved
255, // alloc length
0, // control
],
&[],
&sense::LOGICAL_UNIT_NOT_SUPPORTED.to_fixed_sense(),
);
}
#[test]
fn test_request_sense_descriptor_format() {
let mut target = EmulatedTarget::new();
let dev = BlockDevice::new(null_image());
target.add_lun(Box::new(dev));
do_command_fail_lun(
&mut target,
1,
&[
0x3, // REQUEST SENSE
1, // descriptor format sense data
0, 0, // reserved
255, // alloc length
0, // control
],
sense::INVALID_FIELD_IN_CDB,
);
}
#[test]
fn test_inquiry() {
let mut target = EmulatedTarget::new();
let dev = BlockDevice::new(null_image());
target.add_lun(Box::new(dev));
do_command_in_lun(
&mut target,
1,
&[
0x12, // INQUIRY
0, // EVPD bit: 0
0, // page code
1, 0, // alloc length: 256
0, // control
],
&[],
// some empty comments to get rustfmt to do something vaguely sensible
&[
0x7f, // device not accessible, unknown type
0, // features
0x7, // version
0x12, // response data format v2, HiSup = 1
91, // addl length
0, 0, 0, // unsupported features
// vendor
b'r', b'u', b's', b't', b'-', b'v', b'm', b'm', //
// product
b'v', b'h', b'o', b's', b't', b'-', b'u', b's', b'e', b'r', b'-', b's', b'c', b's',
b'i', b' ', //
// revision
b'v', b'0', b' ', b' ', //
// reserved/obselete/vendor specific
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
// version descriptors
0x0, 0xc0, // SAM-6
0x05, 0xc0, // SPC-5 (no code assigned for 6 yet)
0x06, 0x0, // SBC-4
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //
// reserved
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
],
);
}
#[test]
fn test_other_command() {
let mut target = EmulatedTarget::new();
let dev = BlockDevice::new(null_image());
target.add_lun(Box::new(dev));
do_command_fail_lun(
&mut target,
1,
&[
0, // TEST UNIT READY
0, 0, 0, 0, // reserved
0, // control
],
sense::LOGICAL_UNIT_NOT_SUPPORTED,
);
}
#[test]
fn test_invalid_command() {
let mut target = EmulatedTarget::new();
let dev = BlockDevice::new(null_image());
target.add_lun(Box::new(dev));
do_command_fail_lun(
&mut target,
1,
&[
0xff, // vendor specific
0, 0, 0, 0, // reserved
0, // control
],
sense::INVALID_COMMAND_OPERATION_CODE,
);
}

View File

@ -0,0 +1,107 @@
// SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause
//! Tests for stuff shared between commands.
use std::io::ErrorKind;
use super::{do_command_fail, test_image};
use crate::scsi::{
emulation::{block_device::BlockDevice, target::EmulatedTarget},
sense, CmdError, Request, Target, TaskAttr,
};
#[test]
fn test_invalid_opcode() {
let mut target = EmulatedTarget::new();
let dev = BlockDevice::new(test_image());
target.add_lun(Box::new(dev));
do_command_fail(
&mut target,
&[
0xff, // vendor specific, unused by us
0, 0, 0, 0, 0,
],
sense::INVALID_COMMAND_OPERATION_CODE,
);
}
#[test]
fn test_invalid_service_action() {
let mut target = EmulatedTarget::new();
let dev = BlockDevice::new(test_image());
target.add_lun(Box::new(dev));
do_command_fail(
&mut target,
&[
0xa3, // MAINTAINANCE IN
0x1f, // vendor specific, unused by us
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
],
sense::INVALID_FIELD_IN_CDB,
);
}
#[test]
fn test_short_data_out_buffer() {
let mut target = EmulatedTarget::new();
let dev = BlockDevice::new(test_image());
target.add_lun(Box::new(dev));
let mut data_in: &mut [u8] = &mut [];
let mut data_out: &[u8] = &[0_u8; 511];
let res = target.execute_command(
0,
&mut data_out,
&mut data_in,
Request {
id: 0,
cdb: &[
0x28, // READ (10)
0, // flags
0, 0, 0, 15, // LBA: 5
0, // reserved, group #
0, 1, // transfer length: 1
0, // control
],
task_attr: TaskAttr::Simple,
crn: 0,
prio: 0,
},
);
if let CmdError::DataIn(e) = res.unwrap_err() {
assert_eq!(e.kind(), ErrorKind::WriteZero);
} else {
panic!();
}
}
#[test]
fn test_short_cdb() {
let mut target: EmulatedTarget = EmulatedTarget::new();
let dev = BlockDevice::new(test_image());
target.add_lun(Box::new(dev));
let mut data_in: &mut [u8] = &mut [];
let mut data_out: &[u8] = &[];
let res = target.execute_command(
0,
&mut data_out,
&mut data_in,
Request {
id: 0,
cdb: &[
0x28, // READ (10)
],
task_attr: TaskAttr::Simple,
crn: 0,
prio: 0,
},
);
assert!(matches!(res.unwrap_err(), CmdError::CdbTooShort));
}

View File

@ -0,0 +1,384 @@
// SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause
#![cfg(test)]
mod bad_lun;
mod generic;
mod report_supported_operation_codes;
use std::{fs::File, io::Write};
use tempfile::tempfile;
use super::{
block_device::{BlockDevice, FileBackend},
target::EmulatedTarget,
};
use crate::scsi::{
sense::{self, SenseTriple},
CmdOutput, Request, Target, TaskAttr,
};
fn null_image() -> FileBackend {
FileBackend::new(File::open("/dev/null").unwrap())
}
fn test_image() -> FileBackend {
let mut f = tempfile().unwrap();
// generate 16 512-byte sectors, each of which consist of a single
// repeated hex character, i.e.
// sector 00: 0000000....0000
// sector 15: fffffff....ffff
for chr in b'0'..=b'9' {
f.write_all(&[chr; 512]).unwrap();
}
for chr in b'a'..=b'f' {
f.write_all(&[chr; 512]).unwrap();
}
FileBackend::new(f)
}
fn do_command_in_lun(
target: &mut EmulatedTarget,
lun: u16,
cdb: &[u8],
data_out: &[u8],
expected_data_in: &[u8],
) {
let mut data_in = Vec::new();
let res = target.execute_command(
lun,
&mut &data_out[..],
&mut data_in,
Request {
id: 0,
cdb,
task_attr: TaskAttr::Simple,
crn: 0,
prio: 0,
},
);
assert_eq!(res.unwrap(), CmdOutput::ok());
assert_eq!(&data_in, expected_data_in);
}
fn do_command_fail_lun(
target: &mut EmulatedTarget,
lun: u16,
cdb: &[u8],
expected_error: SenseTriple,
) {
let mut data_in = Vec::new();
let mut data_out: &[u8] = &[];
let res = target.execute_command(
lun,
&mut data_out,
&mut data_in,
Request {
id: 0,
cdb,
task_attr: TaskAttr::Simple,
crn: 0,
prio: 0,
},
);
assert_eq!(res.unwrap(), CmdOutput::check_condition(expected_error));
assert_eq!(&data_in, &[]);
}
fn do_command_in(
target: &mut EmulatedTarget,
cdb: &[u8],
data_out: &[u8],
expected_data_in: &[u8],
) {
do_command_in_lun(target, 0, cdb, data_out, expected_data_in);
}
fn do_command_fail(target: &mut EmulatedTarget, cdb: &[u8], expected_error: SenseTriple) {
do_command_fail_lun(target, 0, cdb, expected_error);
}
#[test]
fn test_test_unit_ready() {
let mut target = EmulatedTarget::new();
let dev = BlockDevice::new(null_image());
target.add_lun(Box::new(dev));
do_command_in(&mut target, &[0, 0, 0, 0, 0, 0], &[], &[]);
}
#[test]
fn test_report_luns() {
let mut target = EmulatedTarget::new();
for _ in 0..5 {
let dev = BlockDevice::new(null_image());
target.add_lun(Box::new(dev));
}
do_command_in(
&mut target,
&[
0xa0, // REPORT LUNS
0, // reserved
0, // select report
0, 0, 0, // reserved
0, 0, 1, 0, // alloc length: 256
0, 0,
],
&[],
&[
0, 0, 0, 40, // length: 5*8 = 40
0, 0, 0, 0, // reserved
0, 0, 0, 0, 0, 0, 0, 0, // LUN 0
0, 1, 0, 0, 0, 0, 0, 0, // LUN 1
0, 2, 0, 0, 0, 0, 0, 0, // LUN 2
0, 3, 0, 0, 0, 0, 0, 0, // LUN 3
0, 4, 0, 0, 0, 0, 0, 0, // LUN 4
],
);
}
#[test]
fn test_read_10() {
let mut target = EmulatedTarget::new();
let dev = BlockDevice::new(test_image());
target.add_lun(Box::new(dev));
// TODO: this test relies on the default logical block size of 512. We should
// make that explicit.
do_command_in(
&mut target,
&[
0x28, // READ (10)
0, // flags
0, 0, 0, 5, // LBA: 5
0, // reserved, group #
0, 1, // transfer length: 1
0, // control
],
&[],
&[b'5'; 512],
);
}
#[test]
fn test_read_10_last_block() {
let mut target = EmulatedTarget::new();
let dev = BlockDevice::new(test_image());
target.add_lun(Box::new(dev));
// TODO: this test relies on the default logical block size of 512. We should
// make that explicit.
do_command_in(
&mut target,
&[
0x28, // READ (10)
0, // flags
0, 0, 0, 15, // LBA: 5
0, // reserved, group #
0, 1, // transfer length: 1
0, // control
],
&[],
&[b'f'; 512],
);
}
#[test]
fn test_read_10_out_of_range() {
let mut target = EmulatedTarget::new();
let dev = BlockDevice::new(test_image());
target.add_lun(Box::new(dev));
// TODO: this test relies on the default logical block size of 512. We should
// make that explicit.
do_command_fail(
&mut target,
&[
0x28, // READ (10)
0, // flags
0, 0, 0, 16, // LBA: 16
0, // reserved, group #
0, 1, // transfer length: 1
0, // control
],
sense::LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE,
);
}
#[test]
fn test_read_10_cross_out() {
let mut target = EmulatedTarget::new();
let dev = BlockDevice::new(null_image());
target.add_lun(Box::new(dev));
// TODO: this test relies on the default logical block size of 512. We should
// make that explicit.
do_command_fail(
&mut target,
&[
0x28, // READ (10)
0, // flags
0, 0, 0, 15, // LBA: 15
0, // reserved, group #
0, 2, // transfer length: 2
0, // control
],
sense::LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE,
);
}
#[test]
fn test_read_capacity_10() {
let mut target = EmulatedTarget::new();
let dev = BlockDevice::new(test_image());
target.add_lun(Box::new(dev));
// TODO: this test relies on the default logical block size of 512. We should
// make that explicit.
// TODO: we should test behavior with ≥ 2 TiB images. But not sure how we
// can do that reliably without risking using 2 TiB of disk
do_command_in(
&mut target,
&[
0x25, // READ CAPACITY (10)
0, 0, 0, 0, 0, 0, 0, 0, // flags
0, // control
],
&[],
&[
0, 0, 0, 15, // returned LBA (last valid LBA),
0, 0, 2, 0, // block size (512)
],
);
}
#[test]
fn test_read_capacity_16() {
let mut target = EmulatedTarget::new();
let dev = BlockDevice::new(test_image());
target.add_lun(Box::new(dev));
// TODO: this test relies on the default logical block size of 512. We should
// make that explicit.
do_command_in(
&mut target,
&[
0x9e, 0x10, // READ CAPACITY (16)
0, 0, 0, 0, 0, 0, 0, 0, // obsolete
0, 0, 0, 32, // allocation length: 32
0, // obselete/reserved
0, // control
],
&[],
&[
0, 0, 0, 0, 0, 0, 0, 15, // returned LBA (last valid LBA),
0, 0, 2, 0, // block size (512)
0, // reserved, zoned stuff, protection stuff
0, // one PB per LB
0xc0, // thin provisioning, unmapped blocks read 0
0, // LBA 0 is aligned (top bits above)
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // reserved
],
);
}
#[test]
fn test_inquiry() {
let mut target = EmulatedTarget::new();
let dev = BlockDevice::new(null_image());
target.add_lun(Box::new(dev));
do_command_in(
&mut target,
&[
0x12, // INQUIRY
0, // EVPD bit: 0
0, // page code
1, 0, // alloc length: 256
0, // control
],
&[],
// some empty comments to get rustfmt to do something vaguely sensible
&[
0, // accessible; direct acccess block device
0, // features
0x7, // version
0x12, // response data format v2, HiSup = 1
91, // addl length
0, 0, 0, // unsupported features
// vendor
b'r', b'u', b's', b't', b'-', b'v', b'm', b'm', //
// product
b'v', b'h', b'o', b's', b't', b'-', b'u', b's', b'e', b'r', b'-', b's', b'c', b's',
b'i', b' ', //
// revision
b'v', b'0', b' ', b' ', //
// reserved/obselete/vendor specific
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
// version descriptors
0x0, 0xc0, // SAM-6
0x05, 0xc0, // SPC-5 (no code assigned for 6 yet)
0x06, 0, // SBC-4
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //
// reserved
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
],
);
}
#[test]
fn test_request_sense() {
let mut target = EmulatedTarget::new();
let dev = BlockDevice::new(null_image());
target.add_lun(Box::new(dev));
do_command_in(
&mut target,
&[
0x3, // INQUIRY
0, // desc bit: 0
0, 0, // reserved
255, // alloc length
0, // control
],
&[],
// We'll always return this - modern SCSI has autosense, so any errors are sent with the
// response to the command that caused them (and therefore immediately cleared), and
// REQUEST SENSE returns an actual error only under some exceptional circumstances
// we don't implement.
&sense::NO_ADDITIONAL_SENSE_INFORMATION.to_fixed_sense(),
);
}
#[test]
fn test_request_sense_descriptor_format() {
let mut target = EmulatedTarget::new();
let dev = BlockDevice::new(null_image());
target.add_lun(Box::new(dev));
do_command_fail(
&mut target,
&[
0x3, // INQUIRY
1, // desc bit: 1
0, 0, // reserved
255, // alloc length
0, // control
],
// We don't support descriptor format sense data.
sense::INVALID_FIELD_IN_CDB,
);
}

View File

@ -0,0 +1,420 @@
// SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause
use super::{do_command_fail, do_command_in, null_image};
use crate::scsi::{
emulation::{block_device::BlockDevice, target::EmulatedTarget},
sense,
};
#[test]
fn test_one_command() {
let mut target = EmulatedTarget::new();
let dev = BlockDevice::new(null_image());
target.add_lun(Box::new(dev));
do_command_in(
&mut target,
&[
0xa3, 0x0c, // REPORT SUPPORTED OPERATION CODES
0b1, // reporting options: one command
0, 1, 2, // opcode: TEST UNIT READY, SA ignored
0, 0, 1, 0, // allocation length: 256
0, // reserved
0, // control
],
&[],
&[
0, 0b11, // flags, supported
0, 6, // cdb len
0, 0, 0, 0, 0, 0b0100, // usage data
],
);
}
#[test]
fn test_one_command_with_timeout_descriptor() {
let mut target = EmulatedTarget::new();
let dev = BlockDevice::new(null_image());
target.add_lun(Box::new(dev));
do_command_in(
&mut target,
&[
0xa3, 0x0c, // REPORT SUPPORTED OPERATION CODES
0x81, // request timeout descs, reporting options: one command
0, 1, 2, // opcode: TEST UNIT READY, SA ignored
0, 0, 1, 0, // allocation length: 256
0, // reserved
0, // control
],
&[],
&[
0, 0b11, // flags, supported
0, 6, // cdb len
0, 0, 0, 0, 0, 0b0100, // usage data
0, 0xa, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // no timeouts
],
);
}
#[test]
fn test_one_command_unsupported() {
let mut target = EmulatedTarget::new();
let dev = BlockDevice::new(null_image());
target.add_lun(Box::new(dev));
do_command_in(
&mut target,
&[
0xa3, 0x0c, // REPORT SUPPORTED OPERATION CODES
0b1, // reporting options: one command
0xff, 1, 2, // opcode: vendor specific, SA ignored
0, 0, 1, 0, // allocation length: 256
0, // reserved
0, // control
],
&[],
&[
0, 0b01, // flags, not supported
0, 0, // cdb len
],
);
}
#[test]
fn test_one_command_valid_service_action() {
let mut target = EmulatedTarget::new();
let dev = BlockDevice::new(null_image());
target.add_lun(Box::new(dev));
do_command_fail(
&mut target,
&[
0xa3, 0x0c, // REPORT SUPPORTED OPERATION CODES
0b1, // reporting options: one command
0x9e, 0, 0x10, // SERVICE ACTION IN (16), READ CAPACITY (16)
0, 0, 1, 0, // allocation length: 256
0, // reserved
0, // control
],
sense::INVALID_FIELD_IN_CDB,
);
}
#[test]
fn test_one_command_invalid_service_action() {
let mut target = EmulatedTarget::new();
let dev = BlockDevice::new(null_image());
target.add_lun(Box::new(dev));
do_command_fail(
&mut target,
&[
0xa3, 0x0c, // REPORT SUPPORTED OPERATION CODES
0b1, // reporting options: one command
0x9e, 0, 0xff, // SERVICE ACTION IN (16), invalid
0, 0, 1, 0, // allocation length: 256
0, // reserved
0, // control
],
sense::INVALID_FIELD_IN_CDB,
);
}
#[test]
fn test_one_service_action() {
let mut target = EmulatedTarget::new();
let dev = BlockDevice::new(null_image());
target.add_lun(Box::new(dev));
do_command_in(
&mut target,
&[
0xa3, 0x0c, // REPORT SUPPORTED OPERATION CODES
0b10, // reporting options: one service action
0x9e, 0, 0x10, // SERVICE ACTION IN (16), READ CAPACITY (16)
0, 0, 1, 0, // allocation length: 256
0, // reserved
0, // control
],
&[],
&[
0, 0b11, // flags, supported
0, 16, // cdb len
0x9e, 0x10, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 0xff, 0xff, 0,
0b0100, // usage data
],
);
}
#[test]
fn test_one_service_action_with_timeout_descriptor() {
let mut target = EmulatedTarget::new();
let dev = BlockDevice::new(null_image());
target.add_lun(Box::new(dev));
do_command_in(
&mut target,
&[
0xa3, 0x0c, // REPORT SUPPORTED OPERATION CODES
0x82, // request timeout descs, reporting options: one service action
0x9e, 0, 0x10, // SERVICE ACTION IN (16), READ CAPACITY (16)
0, 0, 1, 0, // allocation length: 256
0, // reserved
0, // control
],
&[],
&[
0, 0b11, // flags, supported
0, 16, // cdb len
0x9e, 0x10, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 0xff, 0xff, 0,
0b0100, // usage data
0, 0xa, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // no timeouts
],
);
}
#[test]
fn test_one_service_action_unknown_opcode() {
let mut target = EmulatedTarget::new();
let dev = BlockDevice::new(null_image());
target.add_lun(Box::new(dev));
// not entirely sure this behavior is correct; see comment in implementation
do_command_fail(
&mut target,
&[
0xa3, 0x0c, // REPORT SUPPORTED OPERATION CODES
0b10, // reporting options: one service action
0xff, 1, 2, // opcode: vendor specific, unimplemented
0, 0, 1, 0, // allocation length: 256
0, // reserved
0, // control
],
sense::INVALID_FIELD_IN_CDB,
);
}
#[test]
fn test_one_service_action_unknown_service_action() {
let mut target = EmulatedTarget::new();
let dev = BlockDevice::new(null_image());
target.add_lun(Box::new(dev));
do_command_in(
&mut target,
&[
0xa3, 0x0c, // REPORT SUPPORTED OPERATION CODES
0b10, // reporting options: one service action
0x9e, 0, 0xff, // SERVICE ACTION IN (16), invalid SA
0, 0, 1, 0, // allocation length: 256
0, // reserved
0, // control
],
&[],
&[
0, 0b01, // flags, not supported
0, 0, // cdb len
],
);
}
#[test]
fn test_one_service_action_not_service_action() {
let mut target = EmulatedTarget::new();
let dev = BlockDevice::new(null_image());
target.add_lun(Box::new(dev));
do_command_fail(
&mut target,
&[
0xa3, 0x0c, // REPORT SUPPORTED OPERATION CODES
0b10, // reporting options: one service action
0, 1, 2, // TEST UNIT READY
0, 0, 1, 0, // allocation length: 256
0, // reserved
0, // control
],
sense::INVALID_FIELD_IN_CDB,
);
}
// rest of these tests are for "mode 3", which the spec calls 011b and our
// implementation calls OneCommandOrServiceAction, but that's a mouthful so just
// use "mode 3" for test names
#[test]
fn test_mode_3_opcode_without_service_action() {
let mut target = EmulatedTarget::new();
let dev = BlockDevice::new(null_image());
target.add_lun(Box::new(dev));
do_command_in(
&mut target,
&[
0xa3, 0x0c, // REPORT SUPPORTED OPERATION CODES
0b11, // reporting options: mode 3
0, 0, 0, // opcode: TEST UNIT READY, SA: 0
0, 0, 1, 0, // allocation length: 256
0, // reserved
0, // control
],
&[],
&[
0, 0b11, // flags, supported
0, 6, // cdb len
0, 0, 0, 0, 0, 0b0100, // usage data
],
);
}
#[test]
fn test_mode_3_with_timeout_descriptor() {
let mut target = EmulatedTarget::new();
let dev = BlockDevice::new(null_image());
target.add_lun(Box::new(dev));
do_command_in(
&mut target,
&[
0xa3, 0x0c, // REPORT SUPPORTED OPERATION CODES
0x83, // request timeout descs, reporting options: mode 3
0, 0, 0, // opcode: TEST UNIT READY, SA: 0
0, 0, 1, 0, // allocation length: 256
0, // reserved
0, // control
],
&[],
&[
0, 0b11, // flags, supported
0, 6, // cdb len
0, 0, 0, 0, 0, 0b0100, // usage data
0, 0xa, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // no timeouts
],
);
}
#[test]
fn test_mode_3_opcode_with_unnecessary_service_action() {
let mut target = EmulatedTarget::new();
let dev = BlockDevice::new(null_image());
target.add_lun(Box::new(dev));
do_command_in(
&mut target,
&[
0xa3, 0x0c, // REPORT SUPPORTED OPERATION CODES
0b11, // reporting options: mode 3
0, 0, 1, // opcode: TEST UNIT READY, SA: 1
0, 0, 1, 0, // allocation length: 256
0, // reserved
0, // control
],
&[],
&[
0, 0b01, // flags, not supported
0, 0, // cdb len
],
);
}
#[test]
fn test_mode_3_invalid_opcode() {
let mut target = EmulatedTarget::new();
let dev = BlockDevice::new(null_image());
target.add_lun(Box::new(dev));
do_command_in(
&mut target,
&[
0xa3, 0x0c, // REPORT SUPPORTED OPERATION CODES
0b11, // reporting options: mode 3
0xff, 0, 0, // opcode: vendor specific
0, 0, 1, 0, // allocation length: 256
0, // reserved
0, // control
],
&[],
&[
0, 0b01, // flags, not supported
0, 0, // cdb len
],
);
}
#[test]
fn test_mode_3_service_action() {
let mut target = EmulatedTarget::new();
let dev = BlockDevice::new(null_image());
target.add_lun(Box::new(dev));
do_command_in(
&mut target,
&[
0xa3, 0x0c, // REPORT SUPPORTED OPERATION CODES
0b11, // reporting options: mode 3
0x9e, 0, 0x10, // opcode: SERVICE ACTION IN (16), READ CAPACITY (16)
0, 0, 1, 0, // allocation length: 256
0, // reserved
0, // control
],
&[],
&[
0, 0b11, // flags, supported
0, 16, // cdb len
0x9e, 0x10, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 0xff, 0xff, 0,
0b0100, // usage data
],
);
}
#[test]
fn test_mode_3_service_action_with_timeout_descriptor() {
let mut target = EmulatedTarget::new();
let dev = BlockDevice::new(null_image());
target.add_lun(Box::new(dev));
do_command_in(
&mut target,
&[
0xa3, 0x0c, // REPORT SUPPORTED OPERATION CODES
0x83, // request timeout desc, tireporting options: mode 3
0x9e, 0, 0x10, // opcode: SERVICE ACTION IN (16), READ CAPACITY (16)
0, 0, 1, 0, // allocation length: 256
0, // reserved
0, // control
],
&[],
&[
0, 0b11, // flags, supported
0, 16, // cdb len
0x9e, 0x10, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 0xff, 0xff, 0,
0b0100, // usage data
0, 0xa, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // no timeouts
],
);
}
#[test]
fn test_mode_3_invalid_service_action() {
let mut target = EmulatedTarget::new();
let dev = BlockDevice::new(null_image());
target.add_lun(Box::new(dev));
do_command_in(
&mut target,
&[
0xa3, 0x0c, // REPORT SUPPORTED OPERATION CODES
0b11, // reporting options: mode 3
0x9e, 0, 0xff, // opcode: SERVICE ACTION IN (16), invalid SA
0, 0, 1, 0, // allocation length: 256
0, // reserved
0, // control
],
&[],
&[
0, 0b01, // flags, not supported
0, 0, // cdb len
],
);
}