hdd/scsi/
mod.rs

1/*!
2All things SCSI.
3
4* Use [`struct SCSIDevice`](struct.SCSIDevice.html) + [`trait SCSICommon`](trait.SCSICommon.html) to start sending SCSI commands to the [`Device`](../device/index.html).
5* Use [`data` module](data/index.html) to parse various low-level structures found in SCSI command replies.
6* Import traits from porcelain modules (like [`pages`](pages/index.html)) to do typical tasks without needing to compose commands and parse responses yourself.
7  * You can also use [`module ata`](../ata/index.html) to issue ATA commands using ATA PASS-THROUGH.
8*/
9
10pub mod data;
11pub mod pages;
12
13#[cfg(target_os = "linux")]
14mod linux;
15
16#[cfg(target_os = "freebsd")]
17mod freebsd;
18
19use std::io;
20use ata;
21use byteorder::{ReadBytesExt, BigEndian};
22use self::data::sense;
23
24use Direction;
25use Device;
26
27use utils::hexdump_8;
28
29quick_error! {
30	#[derive(Debug)]
31	pub enum Error {
32		IO(err: io::Error) {
33			from()
34			display("IO error: {}", err)
35			description(err.description())
36			cause(err)
37		}
38		// XXX make sure only non-deferred senses are used here
39		// XXX it makes no sense (sorry!) to put informational senses here (i.e. sense::SenseKey::{Ok, Recovered, Completed})
40		Sense(key: sense::key::SenseKey, asc: u8, ascq: u8) { // XXX do we need additional sense data? descriptors? flags? probably not
41			// no from() here, as SCSI sense is also used for informational purposes
42			description("SCSI error")
43			display("SCSI error: {:?} ({})",
44				key,
45				sense::key::decode_asc(*asc, *ascq)
46					.map(|x| x.to_string())
47					.unwrap_or_else(|| format!("unknown additional sense code: {:02x} {:02x}", asc, ascq)))
48		}
49		// this is for Sense::Fixed(FixedData::Invalid(_))
50		// pun definitely intented at this point
51		Nonsense {}
52	}
53}
54
55// FIXME naming: this isn't about ATA-level error, this is error related to ATA PASS-THROUGH command
56quick_error! {
57	#[derive(Debug)]
58	pub enum ATAError {
59		SCSI(err: Error) {
60			from()
61			from(err: io::Error) -> (Error::IO(err))
62			display("{}", err)
63		}
64		/// Device does not support ATA PASS-THROUGH command
65		NotSupported {}
66		// no non-deferred sense is available, or there's no descriptors for ATA registers to be found
67		NoRegisters {}
68	}
69}
70
71#[derive(Debug)]
72pub struct SCSIDevice {
73	device: Device,
74}
75
76impl SCSIDevice {
77	pub fn new(device: Device) -> Self {
78		Self { device }
79	}
80
81	// thin wrapper against platform-specific implementation, mainly exists to provide consistent logging between platforms
82	/// Executes `cmd` and returns tuple of `(sense, data)`.
83	pub fn do_cmd(&self, cmd: &[u8], dir: Direction, sense_len: usize, data_len: usize) -> Result<(Vec<u8>, Vec<u8>), io::Error> {
84		info!("SCSI cmd: dir={:?} cmd={:?}", dir, cmd);
85
86		// this one is implemented in `mod {linux,freebsd}`
87		let ret = Self::do_platform_cmd(self, cmd, dir, sense_len, data_len);
88		match ret {
89			Ok((ref sense, ref data)) => {
90				debug!("SCSI autosense: {}", hexdump_8(sense));
91				debug!("SCSI data: {}", hexdump_8(data));
92			},
93			ref err => {
94				debug!("SCSI err: {:?}", err);
95			}
96		}
97		ret
98	}
99}
100
101// TODO look for non-empty autosense and turn it into errors where appropriate
102pub trait SCSICommon {
103	// XXX DRY
104	fn do_cmd(&self, cmd: &[u8], dir: Direction, sense_len: usize, data_len: usize) -> Result<(Vec<u8>, Vec<u8>), io::Error>;
105
106	fn scsi_inquiry(&self, vital: bool, code: u8) -> Result<(Vec<u8>, Vec<u8>), Error> {
107		info!("issuing INQUIRY: code={:?} vital={:?}", code, vital);
108
109		// TODO as u16 argument, not const
110		const alloc: usize = 4096;
111
112		let cmd: [u8; 6] = [
113			0x12, // opcode: INQUIRY
114			if vital {1} else {0}, // reserved << 2 + cmddt (obsolete) << 1 + enable vital product data << 0
115			code,
116			(alloc >> 8) as u8,
117			(alloc & 0xff) as u8,
118			0, // control (XXX what's that?!)
119		];
120
121		Ok(self.do_cmd(&cmd, Direction::From, 32, alloc)?)
122	}
123
124	/// returns tuple of (sense, logical block address, block length in bytes)
125	fn read_capacity_10(&self, lba: Option<u32>) -> Result<(Vec<u8>, u32, u32), Error> {
126		info!("issuing READ CAPACITY(10): lba={:?}", lba);
127
128		// pmi is partial medium indicator
129		let (pmi, lba) = match lba {
130			Some(lba) => (true, lba),
131			None => (false, 0),
132		};
133
134		let cmd: [u8; 10] = [
135			0x25, // opcode
136			0, // reserved, obsolete
137			((lba >> 24) & 0xff) as u8,
138			((lba >> 16) & 0xff) as u8,
139			((lba >> 8)  & 0xff) as u8,
140			((lba)       & 0xff) as u8,
141			0, // reserved
142			0, // reserved
143			if pmi { 1 } else { 0 }, // reserved, pmi
144			0, // control (XXX what's that?!)
145		];
146
147		let (sense, data) = self.do_cmd(&cmd, Direction::From, 32, 8)?;
148
149		Ok((
150			sense,
151			(&data[0..4]).read_u32::<BigEndian>().unwrap(),
152			(&data[4..8]).read_u32::<BigEndian>().unwrap(),
153		))
154	}
155
156	// TODO? struct as a single argument, or maybe even resort to the builder pattern
157	/**
158	Executes LOG SENSE command.
159
160	Arguments are:
161
162	- `changed`: whether to return code values changed since the last LOG SELECT or LOG CHANGE command (obsolete)
163	- `save_params`: record log parameters marked as saveable into non-volatile, vendor-specific location (might not be supported)
164	- `default`: whether to return current or default values (?)
165	- `threshold`: whether to return cumulative or threshold values
166	- `page`, `subpage`: log page to return parameters from
167	- `param_ptr`: limit list of return values to parameters starting with id `param_ptr`
168	*/
169	fn log_sense(&self, changed: bool, save_params: bool, default: bool, threshold: bool, page: u8, subpage: u8, param_ptr: u16) -> Result<(Vec<u8>, Vec<u8>), Error> {
170		info!("issuing LOG SENSE: page={page:?} subpage={subpage:?} param_ptr={param_ptr:?} changed={changed:?} save_params={save_params:?} default={default:?} threshold={threshold:?}",
171			changed = changed,
172			save_params = save_params,
173			default = default,
174			threshold = threshold,
175			page = page,
176			subpage = subpage,
177			param_ptr = param_ptr,
178		);
179
180		// TODO as u16 argument, not const
181		const alloc: usize = 4096;
182
183		// Page Control field
184		let pc = match (default, threshold) {
185			(false, true) => 0b00, // > threshold values
186			(false, false) => 0b01, // > cumulative values
187			(true, true) => 0b10, // > default threshold values
188			(true, false) => 0b11, // > default cumulative values
189		};
190
191		let cmd: [u8; 10] = [
192			0x4d, // opcode
193			if changed {0b10} else {0} + if save_params {0b1} else {0}, // [reserved × 6][ppc][sp]
194			// TODO Err() if page >= 0b1'000'000
195			(pc << 6) + page,
196			subpage,
197			0, // reserved
198			(param_ptr >> 8) as u8,
199			(param_ptr & 0xff) as u8,
200			(alloc >> 8) as u8,
201			(alloc & 0xff) as u8,
202			0, // control (XXX what's that?!)
203		];
204
205		Ok(self.do_cmd(&cmd, Direction::From, 32, alloc)?)
206	}
207
208	fn ata_pass_through_16(&self, dir: Direction, regs: &ata::RegistersWrite) -> Result<(ata::RegistersRead, Vec<u8>), ATAError> {
209		info!("issuing ATA PASS-THROUGH (16): dir={:?} regs={:?}", dir, regs);
210
211		// see T10/04-262r8a ATA Command Pass-Through, 3.2.3
212		let extend = 0; // TODO
213		let protocol = match dir {
214			Direction::None => 3, // Non-data
215			Direction::From => 4, // PIO Data-In
216			Direction::To => unimplemented!(), //5, // PIO Data-Out
217			_ => unimplemented!(),
218		};
219		let multiple_count = 0; // TODO
220		let ata_cmd: [u8; 16] = [
221			0x85, // opcode: ATA PASS-THROUGH (16)
222			(multiple_count << 5) + (protocol << 1) + extend,
223			// 0b00: wait up to 2^(OFF_LINE+1)-2 seconds for valid ATA status register
224			// 0b1: CK_COND, return ATA register info in the sense data
225			// 0b0: reserved
226			// 0b1: T_DIR; transfer from ATA device
227			// 0b1: BYT_BLOK; T_LENGTH is in blocks, not in bytes
228			// 0b01: T_LENGTH itself
229			0b0010_1101,
230			0, regs.features,
231			0, regs.sector_count,
232			0, regs.sector,
233			0, regs.cyl_low,
234			0, regs.cyl_high,
235			regs.device,
236			regs.command,
237			0, // control (XXX what's that?!)
238		];
239
240		let (sense, data) = self.do_cmd(&ata_cmd, Direction::From, 32, 512)?;
241
242		// FIXME this block is full of super-awkward patterns
243		let descriptors = match sense::parse(&sense) {
244			// current sense in the descriptor format
245			Some((true, sense::Sense::Descriptor(sense::DescriptorData {
246				descriptors,
247				// Recovered Error / ATA PASS THROUGH INFORMATION AVAILABLE
248				key: 0x01, asc: 0x00, ascq: 0x1D,
249				..
250			}))) => {
251				descriptors
252			},
253
254			Some((true, sense::Sense::Descriptor(sense::DescriptorData {
255				descriptors,
256				// some devices/drivers return (Ok, 0, 0) as a sense;
257				// will validate its contents below
258				key: 0x00, asc: 0x00, ascq: 0x00,
259				..
260			}))) => {
261				descriptors
262			},
263
264			Some((true, sense::Sense::Fixed(sense::FixedData::Valid {
265				// Illegal Request / INVALID COMMAND OPERATION CODE
266				key: 0x05, asc: 0x20, ascq: 0x00, ..
267			}))) => {
268				return Err(ATAError::NotSupported);
269			},
270
271			Some((true, sense::Sense::Fixed(sense::FixedData::Valid {
272				key, asc, ascq, ..
273			})))
274			| Some((true, sense::Sense::Descriptor(sense::DescriptorData {
275				key, asc, ascq, ..
276			})))
277			=> {
278				// unexpected sense
279				return Err(Error::Sense(sense::key::SenseKey::from(key), asc, ascq))?;
280			},
281
282			Some((true, sense::Sense::Fixed(sense::FixedData::Invalid(_)))) => {
283				// invalid sense
284				return Err(Error::Nonsense)?;
285			},
286
287			Some((false, _)) | None => {
288				// no (current) sense
289				return Err(ATAError::NoRegisters);
290			},
291		};
292
293		for desc in descriptors {
294			if desc.code != 0x09 { continue; }
295			if desc.data.len() != 12 { continue; }
296
297			let d = desc.data;
298
299			// TODO? EXTEND bit, ATA PASS-THROUGH 12 vs 16
300			return Ok((ata::RegistersRead {
301				error: d[1],
302
303				sector_count: d[3],
304
305				sector: d[5],
306				cyl_low: d[7],
307				cyl_high: d[9],
308				device: d[10],
309
310				status: d[11],
311			}, data))
312		}
313
314		return Err(ATAError::NoRegisters);
315	}
316}
317
318impl SCSICommon for SCSIDevice {
319	// XXX DRY
320	fn do_cmd(&self, cmd: &[u8], dir: Direction, sense_len: usize, data_len: usize) -> Result<(Vec<u8>, Vec<u8>), io::Error> {
321		Self::do_cmd(self, cmd, dir, sense_len, data_len)
322	}
323}