From 4072d5d282ae23b8babd406326e9498cc8684fb1 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Tue, 18 Feb 2020 19:18:20 +0100 Subject: [PATCH] Implement READ CAPACITY 10. This allows the block count and block size to be determined when no block descriptors in the mode sense data are returned. --- usbscsid/src/main.rs | 2 ++ usbscsid/src/scsi/cmds.rs | 58 ++++++++++++++++++++++++++++++++++++++- usbscsid/src/scsi/mod.rs | 45 +++++++++++++++++++++++++----- 3 files changed, 97 insertions(+), 8 deletions(-) diff --git a/usbscsid/src/main.rs b/usbscsid/src/main.rs index 840dfb44bd..601512b558 100644 --- a/usbscsid/src/main.rs +++ b/usbscsid/src/main.rs @@ -91,9 +91,11 @@ fn main() { //syscall::setrens(0, 0).expect("scsid: failed to enter null namespace"); let mut scsi = Scsi::new(&mut *protocol).expect("usbscsid: failed to setup SCSI"); + println!("SCSI initialized"); let mut buffer = [0u8; 512]; scsi.read(&mut *protocol, 0, &mut buffer).unwrap(); println!("DISK CONTENT: {}", base64::encode(&buffer[..])); + let mut scsi_scheme = ScsiScheme::new(&mut scsi, &mut *protocol); // TODO: Use nonblocking and put all pending calls in a todo VecDeque. Use an eventfd as well. diff --git a/usbscsid/src/scsi/cmds.rs b/usbscsid/src/scsi/cmds.rs index ca6b2baf4e..8eb58a0cb6 100644 --- a/usbscsid/src/scsi/cmds.rs +++ b/usbscsid/src/scsi/cmds.rs @@ -1,5 +1,6 @@ use super::opcodes::Opcode; use std::{fmt, mem, slice}; +use std::convert::TryInto; #[repr(packed)] pub struct Inquiry { @@ -415,6 +416,47 @@ impl ModeParamHeader10 { } } +#[repr(packed)] +#[derive(Clone, Copy, Debug)] +pub struct ReadCapacity10 { + pub opcode: u8, + _rsvd1: u8, + obsolete_lba: u32, + _rsvd2: [u8; 3], + pub control: u8, +} +unsafe impl plain::Plain for ReadCapacity10 {} + +impl ReadCapacity10 { + pub const fn new(control: u8) -> Self { + Self { + opcode: Opcode::ReadCapacity10 as u8, + _rsvd1: 0, + obsolete_lba: 0, + _rsvd2: [0; 3], + control, + } + } +} +// TODO: ReadCapacity16 + +#[repr(packed)] +#[derive(Clone, Copy, Debug)] +pub struct ReadCapacity10ParamData { + pub max_lba: u32, + pub block_len: u32, +} +unsafe impl plain::Plain for ReadCapacity10ParamData {} + +impl ReadCapacity10ParamData { + pub const fn block_count(&self) -> u32 { + u32::from_be(self.max_lba) + } + pub const fn logical_block_len(&self) -> u32 { + u32::from_be(self.block_len) + } +} + #[repr(packed)] #[derive(Clone, Copy, Debug)] pub struct RwErrorRecoveryPage { @@ -428,6 +470,15 @@ pub struct RwErrorRecoveryPage { } unsafe impl plain::Plain for RwErrorRecoveryPage {} +#[repr(packed)] +#[derive(Clone, Copy, Debug)] +pub struct CachingModePage { + pub a: u8, + pub page_length: u8, + // TODO: more +} +unsafe impl plain::Plain for CachingModePage {} + pub(crate) struct ModePageIterRaw<'a> { buffer: &'a [u8], } @@ -446,7 +497,7 @@ impl<'a> Iterator for ModePageIterRaw<'a> { self.buffer[1] as usize + 1 } else { // item is sub_page mode - self.buffer[2] as usize + 3 + u16::from_be_bytes((&self.buffer[2..3]).try_into().ok()?) as usize + 3 }; if self.buffer.len() < page_len { return None; @@ -466,6 +517,7 @@ impl<'a> Iterator for ModePageIterRaw<'a> { #[derive(Clone, Copy, Debug)] pub enum AnyModePage<'a> { RwErrorRecovery(&'a RwErrorRecoveryPage), + Caching(&'a CachingModePage) } struct ModePageIter<'a> { @@ -487,6 +539,10 @@ impl<'a> Iterator for ModePageIter<'a> { Some(AnyModePage::RwErrorRecovery( plain::from_bytes(next_buf).ok()?, )) + } else if page_code == 0x08 { + Some(AnyModePage::Caching( + plain::from_bytes(next_buf).ok()?, + )) } else { println!("Unimplemented sub_page {}", base64::encode(next_buf)); None diff --git a/usbscsid/src/scsi/mod.rs b/usbscsid/src/scsi/mod.rs index 0bbf289d2a..7fa0b2eade 100644 --- a/usbscsid/src/scsi/mod.rs +++ b/usbscsid/src/scsi/mod.rs @@ -63,9 +63,20 @@ impl Scsi { let (block_size, block_count) = { let (_, blkdescs, mode_page_iter) = this.get_mode_sense10(protocol)?; + for page in mode_page_iter { + println!("PAGE: {:?}", page); + } + // TODO: Can there be multiple disks at all? - let only_blkdesc = blkdescs.get(0).unwrap(); - (only_blkdesc.block_size(), only_blkdesc.block_count()) + if let Some(only_blkdesc) = blkdescs.get(0) { + println!("Found block desc: {:?}", only_blkdesc); + (only_blkdesc.block_size(), only_blkdesc.block_count()) + } else { + println!("read_capacity10"); + let r = this.read_capacity(protocol)?; + println!("read_capacity10 result: {:?}", r); + (r.logical_block_len(), r.block_count().into()) + } }; this.block_size = block_size; @@ -100,6 +111,18 @@ impl Scsi { )?; Ok(()) } + pub fn read_capacity(&mut self, protocol: &mut dyn Protocol) -> Result<&cmds::ReadCapacity10ParamData> { + // The spec explicitly states that the allocation length is 8 bytes. + let read_capacity10 = self.cmd_read_capacity10(); + *read_capacity10 = cmds::ReadCapacity10::new(0); + self.data_buffer.resize(10usize, 0u8); + protocol + .send_command( + &self.command_buffer[..10], + DeviceReqData::In(&mut self.data_buffer[..8]), + )?; + Ok(self.res_read_capacity10()) + } pub fn get_mode_sense10( &mut self, protocol: &mut dyn Protocol, @@ -156,6 +179,9 @@ impl Scsi { pub fn cmd_request_sense(&mut self) -> &mut cmds::RequestSense { plain::from_mut_bytes(&mut self.command_buffer).unwrap() } + pub fn cmd_read_capacity10(&mut self) -> &mut cmds::ReadCapacity10 { + plain::from_mut_bytes(&mut self.command_buffer).unwrap() + } pub fn cmd_read16(&mut self) -> &mut cmds::Read16 { plain::from_mut_bytes(&mut self.command_buffer).unwrap() } @@ -221,6 +247,9 @@ impl Scsi { let buffer = &self.data_buffer[descs_start + header.block_desc_len() as usize..]; cmds::mode_page_iter(buffer) } + pub fn res_read_capacity10(&self) -> &cmds::ReadCapacity10ParamData { + plain::from_bytes(&self.data_buffer).unwrap() + } pub fn get_disk_size(&mut self) -> u64 { self.block_count * u64::from(self.block_size) } @@ -230,21 +259,23 @@ impl Scsi { lba: u64, buffer: &mut [u8], ) -> Result { - let blocks_to_read = buffer.len() as u64 / u64::from(self.block_size); - let bytes_to_read = blocks_to_read as usize * self.block_size as usize; - let transfer_len = u32::try_from(blocks_to_read).or(Err(ScsiError::Overflow( + let blocks_to_read = dbg!(buffer.len() as u64 / u64::from(dbg!(self.block_size))); + let bytes_to_read = dbg!(blocks_to_read as usize * self.block_size as usize); + let transfer_len = dbg!(u32::try_from(blocks_to_read).or(Err(ScsiError::Overflow( "number of blocks to read couldn't fit inside a u32", - )))?; + )))?); { let read = self.cmd_read16(); *read = cmds::Read16::new(lba, transfer_len, 0); } // TODO: Use the to-be-written TransferReadStream instead of relying on everything being // able to fit within a single buffer. + self.data_buffer.resize(bytes_to_read, 0u8); let status = protocol.send_command( &self.command_buffer[..16], - DeviceReqData::In(&mut buffer[..bytes_to_read]), + DeviceReqData::In(&mut self.data_buffer[..bytes_to_read]), )?; + buffer[..bytes_to_read].copy_from_slice(&self.data_buffer[..bytes_to_read]); Ok(status.bytes_transferred(bytes_to_read as u32)) } pub fn write(