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 clear_nexus_state(&self) {
92 self.sense_data.set(None);
95 self.inner.clear_nexus_state();
96 }
97
98 fn execute_scsi<'a>(
99 &'a self,
100 external_data: &'a RequestBuffers<'a>,
101 request: &'a Request,
102 ) -> StackFuture<'a, ScsiResult, { ASYNC_SCSI_DISK_STACK_SIZE }> {
103 StackFuture::from_or_box(async move {
104 let op = request.scsiop();
105 let result = match op {
106 ScsiOp::READ
107 | ScsiOp::READ12
108 | ScsiOp::TEST_UNIT_READY
109 | ScsiOp::READ_TOC
110 | ScsiOp::INQUIRY
111 | ScsiOp::GET_CONFIGURATION
112 | ScsiOp::MODE_SENSE
113 | ScsiOp::MODE_SENSE10
114 | ScsiOp::READ_DVD_STRUCTURE
115 | ScsiOp::READ_CAPACITY
116 | ScsiOp::GET_EVENT_STATUS
117 | ScsiOp::MEDIUM_REMOVAL
118 | ScsiOp::START_STOP_UNIT => self.inner.execute_scsi(external_data, request).await,
119 ScsiOp::REQUEST_SENSE => self.atapi_request_sense(external_data, request),
120 ScsiOp::REPORT_LUNS => self.atapi_report_luns(external_data, request),
121 ScsiOp::SEEK => {
122 self.atapi_noop()
124 }
125 _ => self.atapi_illegal_cmd(),
126 };
127
128 self.sense_data.set(result.sense_data.as_ref());
129 tracing::debug!(scsi_result = ?result, ?op, "Atapi execute_scsi_async.");
130 result
131 })
132 }
133}
134
135impl AtapiScsiDisk {
136 pub fn new(disk: Arc<dyn AsyncScsiDisk>) -> Self {
137 AtapiScsiDisk {
138 inner: disk,
139 sense_data: Default::default(),
140 }
141 }
142
143 fn atapi_request_sense(
144 &self,
145 external_data: &RequestBuffers<'_>,
146 request: &Request,
147 ) -> ScsiResult {
148 let cdb = scsi::CdbInquiry::read_from_prefix(&request.cdb[..])
149 .unwrap()
150 .0; let allocation_length = cdb.allocation_length.get() as usize;
152
153 let min = size_of::<scsi::SenseDataHeader>();
154 if allocation_length < min || allocation_length > external_data.len() {
155 tracelimit::error_ratelimited!(
156 allocation_length,
157 min,
158 external_data_len = external_data.len(),
159 "srb error"
160 );
161 return ScsiResult {
162 scsi_status: ScsiStatus::CHECK_CONDITION,
163 srb_status: SrbStatus::ERROR,
164 tx: 0,
165 sense_data: None,
166 };
167 }
168
169 let sense = self.sense_data.take().unwrap_or_else(|| {
170 scsi::SenseData::new(SenseKey::NO_SENSE, AdditionalSenseCode::NO_SENSE, 0x00)
171 });
172
173 let tx = std::cmp::min(allocation_length, size_of::<scsi::SenseData>());
174 let result = external_data.writer().write(&sense.as_bytes()[..tx]);
175
176 match result {
177 Err(err) => {
178 tracelimit::error_ratelimited!(
179 ?err,
180 "SCSIOP_REQUEST_SENSE hit memory access error"
181 );
182 ScsiResult {
183 scsi_status: ScsiStatus::CHECK_CONDITION,
184 srb_status: SrbStatus::INVALID_REQUEST,
185 tx: 0,
186 sense_data: Some(illegal_request_sense(AdditionalSenseCode::INVALID_CDB)),
187 }
188 }
189 Ok(_) => ScsiResult {
190 scsi_status: ScsiStatus::GOOD,
191 srb_status: SrbStatus::SUCCESS,
192 tx,
193 sense_data: None,
194 },
195 }
196 }
197
198 fn atapi_report_luns(
199 &self,
200 external_data: &RequestBuffers<'_>,
201 request: &Request,
202 ) -> ScsiResult {
203 let cdb = scsi::ReportLuns::read_from_prefix(&request.cdb[..])
204 .unwrap()
205 .0; let allocation_length = cdb.allocation_length.get() as usize;
207 if allocation_length == 0 {
208 return ScsiResult {
209 scsi_status: ScsiStatus::GOOD,
210 srb_status: SrbStatus::SUCCESS,
211 tx: 0,
212 sense_data: None,
213 };
214 }
215
216 let mut data: Vec<u64> = vec![0; 2];
218 let tx = data.as_bytes().len();
219 if allocation_length < tx || allocation_length > external_data.len() {
220 tracelimit::error_ratelimited!(
221 allocation_length,
222 tx,
223 external_data_len = external_data.len(),
224 "srb error"
225 );
226 return ScsiResult {
227 scsi_status: ScsiStatus::CHECK_CONDITION,
228 srb_status: SrbStatus::ERROR,
229 tx: 0,
230 sense_data: None,
231 };
232 }
233
234 const HEADER_SIZE: usize = size_of::<scsi::LunList>();
235 let header = scsi::LunList {
236 length: 8.into(),
237 reserved: [0; 4],
238 };
239 data.as_mut_bytes()[..HEADER_SIZE].copy_from_slice(header.as_bytes());
240 data[1].as_mut_bytes()[..2].copy_from_slice(&0_u16.to_be_bytes());
241
242 let result = external_data.writer().write(&data.as_bytes()[..tx]);
243
244 match result {
245 Err(err) => {
246 tracelimit::error_ratelimited!(?err, "SCSIOP_REPORT_LUNS hit memory access error");
247 ScsiResult {
248 scsi_status: ScsiStatus::CHECK_CONDITION,
249 srb_status: SrbStatus::INVALID_REQUEST,
250 tx: 0,
251 sense_data: Some(illegal_request_sense(AdditionalSenseCode::INVALID_CDB)),
252 }
253 }
254 Ok(_) => ScsiResult {
255 scsi_status: ScsiStatus::GOOD,
256 srb_status: SrbStatus::SUCCESS,
257 tx,
258 sense_data: None,
259 },
260 }
261 }
262
263 fn atapi_noop(&self) -> ScsiResult {
264 ScsiResult {
265 scsi_status: ScsiStatus::GOOD,
266 srb_status: SrbStatus::SUCCESS,
267 tx: 0,
268 sense_data: None,
269 }
270 }
271
272 fn atapi_illegal_cmd(&self) -> ScsiResult {
273 ScsiResult {
274 scsi_status: ScsiStatus::CHECK_CONDITION,
275 srb_status: SrbStatus::INVALID_REQUEST,
276 tx: 0,
277 sense_data: Some(illegal_request_sense(AdditionalSenseCode::ILLEGAL_COMMAND)),
278 }
279 }
280}