1use crate::SenseDataSlot;
9use crate::illegal_request_sense;
10use guestmem::MemoryWrite;
11use inspect::Inspect;
12use scsi::AdditionalSenseCode;
13use scsi::ScsiOp;
14use scsi::ScsiStatus;
15use scsi::SenseKey;
16use scsi::srb::SrbStatus;
17use scsi_buffers::RequestBuffers;
18use scsi_core::ASYNC_SCSI_DISK_STACK_SIZE;
19use scsi_core::AsyncScsiDisk;
20use scsi_core::Request;
21use scsi_core::ScsiResult;
22use scsi_core::ScsiSaveRestore;
23use scsi_core::save_restore::SavedSenseData;
24use scsi_core::save_restore::ScsiDvdSavedState;
25use scsi_core::save_restore::ScsiSavedState;
26use scsi_defs as scsi;
27use stackfuture::StackFuture;
28use std::sync::Arc;
29use vmcore::save_restore::RestoreError;
30use vmcore::save_restore::SaveError;
31use zerocopy::FromBytes;
32use zerocopy::IntoBytes;
33
34#[derive(Inspect)]
36pub struct AtapiScsiDisk {
37 #[inspect(flatten)]
38 inner: Arc<dyn AsyncScsiDisk>,
39 #[inspect(skip)]
40 sense_data: SenseDataSlot,
41}
42
43impl ScsiSaveRestore for AtapiScsiDisk {
44 fn save(&self) -> Result<Option<ScsiSavedState>, SaveError> {
45 let sense = self.sense_data.get();
46 let sense_data = sense.map(|sense| SavedSenseData {
47 sense_key: sense.header.sense_key.0,
48 additional_sense_code: sense.additional_sense_code.0,
49 additional_sense_code_qualifier: sense.additional_sense_code_qualifier,
50 });
51 let state = self.inner.save()?.unwrap();
52 match state {
53 ScsiSavedState::ScsiDvd(mut state) => {
54 state.sense_data = sense_data;
55 Ok(Some(ScsiSavedState::ScsiDvd(state)))
56 }
57 _ => Err(SaveError::InvalidChildSavedState(anyhow::anyhow!(
58 "saved state didn't match expected ScsiSavedState::ScsiDvd"
59 ))),
60 }
61 }
62
63 fn restore(&self, state: &ScsiSavedState) -> Result<(), RestoreError> {
64 self.inner.restore(state)?;
65 match state {
66 ScsiSavedState::ScsiDvd(state) => {
67 let ScsiDvdSavedState { sense_data, .. } = state;
68 self.sense_data.set(
70 sense_data
71 .map(|sense| {
72 scsi::SenseData::new(
73 SenseKey(sense.sense_key),
74 AdditionalSenseCode(sense.additional_sense_code),
75 sense.additional_sense_code_qualifier,
76 )
77 })
78 .as_ref(),
79 );
80
81 Ok(())
82 }
83 _ => Err(RestoreError::InvalidSavedState(anyhow::anyhow!(
84 "saved state didn't match expected ScsiSavedState::ScsiDvd"
85 ))),
86 }
87 }
88}
89
90impl AsyncScsiDisk for AtapiScsiDisk {
91 fn execute_scsi<'a>(
92 &'a self,
93 external_data: &'a RequestBuffers<'a>,
94 request: &'a Request,
95 ) -> StackFuture<'a, ScsiResult, { ASYNC_SCSI_DISK_STACK_SIZE }> {
96 StackFuture::from_or_box(async move {
97 let op = request.scsiop();
98 let result = match op {
99 ScsiOp::READ
100 | ScsiOp::READ12
101 | ScsiOp::TEST_UNIT_READY
102 | ScsiOp::READ_TOC
103 | ScsiOp::INQUIRY
104 | ScsiOp::GET_CONFIGURATION
105 | ScsiOp::MODE_SENSE
106 | ScsiOp::MODE_SENSE10
107 | ScsiOp::READ_DVD_STRUCTURE
108 | ScsiOp::READ_CAPACITY
109 | ScsiOp::GET_EVENT_STATUS
110 | ScsiOp::MEDIUM_REMOVAL
111 | ScsiOp::START_STOP_UNIT => self.inner.execute_scsi(external_data, request).await,
112 ScsiOp::REQUEST_SENSE => self.atapi_request_sense(external_data, request),
113 ScsiOp::REPORT_LUNS => self.atapi_report_luns(external_data, request),
114 ScsiOp::SEEK => {
115 self.atapi_noop()
117 }
118 _ => self.atapi_illegal_cmd(),
119 };
120
121 self.sense_data.set(result.sense_data.as_ref());
122 tracing::debug!(scsi_result = ?result, ?op, "Atapi execute_scsi_async.");
123 result
124 })
125 }
126}
127
128impl AtapiScsiDisk {
129 pub fn new(disk: Arc<dyn AsyncScsiDisk>) -> Self {
130 AtapiScsiDisk {
131 inner: disk,
132 sense_data: Default::default(),
133 }
134 }
135
136 fn atapi_request_sense(
137 &self,
138 external_data: &RequestBuffers<'_>,
139 request: &Request,
140 ) -> ScsiResult {
141 let cdb = scsi::CdbInquiry::read_from_prefix(&request.cdb[..])
142 .unwrap()
143 .0; let allocation_length = cdb.allocation_length.get() as usize;
145
146 let min = size_of::<scsi::SenseDataHeader>();
147 if allocation_length < min || allocation_length > external_data.len() {
148 tracelimit::error_ratelimited!(
149 allocation_length,
150 min,
151 external_data_len = external_data.len(),
152 "srb error"
153 );
154 return ScsiResult {
155 scsi_status: ScsiStatus::CHECK_CONDITION,
156 srb_status: SrbStatus::ERROR,
157 tx: 0,
158 sense_data: None,
159 };
160 }
161
162 let sense = self.sense_data.take().unwrap_or_else(|| {
163 scsi::SenseData::new(SenseKey::NO_SENSE, AdditionalSenseCode::NO_SENSE, 0x00)
164 });
165
166 let tx = std::cmp::min(allocation_length, size_of::<scsi::SenseData>());
167 let result = external_data.writer().write(&sense.as_bytes()[..tx]);
168
169 match result {
170 Err(err) => {
171 tracelimit::error_ratelimited!(
172 ?err,
173 "SCSIOP_REQUEST_SENSE hit memory access error"
174 );
175 ScsiResult {
176 scsi_status: ScsiStatus::CHECK_CONDITION,
177 srb_status: SrbStatus::INVALID_REQUEST,
178 tx: 0,
179 sense_data: Some(illegal_request_sense(AdditionalSenseCode::INVALID_CDB)),
180 }
181 }
182 Ok(_) => ScsiResult {
183 scsi_status: ScsiStatus::GOOD,
184 srb_status: SrbStatus::SUCCESS,
185 tx,
186 sense_data: None,
187 },
188 }
189 }
190
191 fn atapi_report_luns(
192 &self,
193 external_data: &RequestBuffers<'_>,
194 request: &Request,
195 ) -> ScsiResult {
196 let cdb = scsi::ReportLuns::read_from_prefix(&request.cdb[..])
197 .unwrap()
198 .0; let allocation_length = cdb.allocation_length.get() as usize;
200 if allocation_length == 0 {
201 return ScsiResult {
202 scsi_status: ScsiStatus::GOOD,
203 srb_status: SrbStatus::SUCCESS,
204 tx: 0,
205 sense_data: None,
206 };
207 }
208
209 let mut data: Vec<u64> = vec![0; 2];
211 let tx = data.as_bytes().len();
212 if allocation_length < tx || allocation_length > external_data.len() {
213 tracelimit::error_ratelimited!(
214 allocation_length,
215 tx,
216 external_data_len = external_data.len(),
217 "srb error"
218 );
219 return ScsiResult {
220 scsi_status: ScsiStatus::CHECK_CONDITION,
221 srb_status: SrbStatus::ERROR,
222 tx: 0,
223 sense_data: None,
224 };
225 }
226
227 const HEADER_SIZE: usize = size_of::<scsi::LunList>();
228 let header = scsi::LunList {
229 length: 8.into(),
230 reserved: [0; 4],
231 };
232 data.as_mut_bytes()[..HEADER_SIZE].copy_from_slice(header.as_bytes());
233 data[1].as_mut_bytes()[..2].copy_from_slice(&0_u16.to_be_bytes());
234
235 let result = external_data.writer().write(&data.as_bytes()[..tx]);
236
237 match result {
238 Err(err) => {
239 tracelimit::error_ratelimited!(?err, "SCSIOP_REPORT_LUNS hit memory access error");
240 ScsiResult {
241 scsi_status: ScsiStatus::CHECK_CONDITION,
242 srb_status: SrbStatus::INVALID_REQUEST,
243 tx: 0,
244 sense_data: Some(illegal_request_sense(AdditionalSenseCode::INVALID_CDB)),
245 }
246 }
247 Ok(_) => ScsiResult {
248 scsi_status: ScsiStatus::GOOD,
249 srb_status: SrbStatus::SUCCESS,
250 tx,
251 sense_data: None,
252 },
253 }
254 }
255
256 fn atapi_noop(&self) -> ScsiResult {
257 ScsiResult {
258 scsi_status: ScsiStatus::GOOD,
259 srb_status: SrbStatus::SUCCESS,
260 tx: 0,
261 sense_data: None,
262 }
263 }
264
265 fn atapi_illegal_cmd(&self) -> ScsiResult {
266 ScsiResult {
267 scsi_status: ScsiStatus::CHECK_CONDITION,
268 srb_status: SrbStatus::INVALID_REQUEST,
269 tx: 0,
270 sense_data: Some(illegal_request_sense(AdditionalSenseCode::ILLEGAL_COMMAND)),
271 }
272 }
273}