1use super::AsyncScsiDisk;
5use super::SavedSenseData;
6use super::ScsiResult;
7use super::ScsiSaveRestore;
8use super::scsi;
9use crate::Request;
10use crate::ScsiSavedState;
11use crate::SenseDataSlot;
12use disk_backend::Disk;
13use disk_backend::DiskError;
14use guestmem::AccessError;
15use guestmem::MemoryRead;
16use guestmem::MemoryWrite;
17use parking_lot::Mutex;
18use parking_lot::RwLock;
19use scsi::AdditionalSenseCode;
20use scsi::FeatureNumber::FeaturePowerManagement;
21use scsi::FeatureNumber::FeatureRealTimeStreaming;
22use scsi::FeatureNumber::FeatureTimeout;
23use scsi::ScsiOp;
24use scsi::ScsiStatus;
25use scsi::SenseData;
26use scsi::SenseKey;
27use scsi::srb::SrbStatus;
28use scsi_buffers::RequestBuffers;
29use scsi_core::save_restore::DriveState;
30use scsi_core::save_restore::IsoMediumEvent;
31use scsi_core::save_restore::ScsiDvdSavedState;
32use stackfuture::StackFuture;
33use std::sync::Arc;
34use thiserror::Error;
35use tracing::Instrument;
36use vmcore::save_restore::RestoreError;
37use vmcore::save_restore::SaveError;
38use zerocopy::FromBytes;
39use zerocopy::FromZeros;
40use zerocopy::Immutable;
41use zerocopy::IntoBytes;
42use zerocopy::KnownLayout;
43
44enum Media {
45 Unloaded,
46 Loaded(Disk),
47}
48
49pub struct SimpleScsiDvd {
50 media: Arc<RwLock<Media>>,
51 media_state: Arc<Mutex<MediaState>>,
52 sense_data: SenseDataSlot,
53}
54
55#[derive(Debug, Default)]
56struct MediaState {
57 drive_state: DriveState,
58 pending_medium_event: IsoMediumEvent,
59 persistent: bool,
60 prevent: bool,
61}
62
63#[derive(Error, Debug)]
64enum ScsiDvdError {
65 #[error("illegal request, asc: {0:?}, qualifier: {1:?}")]
66 IllegalRequest(AdditionalSenseCode, u8),
67 #[error("data overrun")]
68 DataOverrun,
69 #[error("memory access error")]
70 MemoryAccess(#[source] AccessError),
71 #[error("io error")]
72 IoError(#[source] DiskError),
73 #[error("not ready, sense key: {0:?}, qualifier: {1}")]
74 SenseNotReady(AdditionalSenseCode, u8),
75 #[error("invalid request - no sense data")]
76 IllegalRequestNoSenseData,
77}
78
79struct RequestParametersIso {
80 tx: usize,
81 offset: u64,
82}
83
84impl AsyncScsiDisk for SimpleScsiDvd {
85 fn execute_scsi<'a>(
86 &'a self,
87 external_data: &'a RequestBuffers<'a>,
88 request: &'a Request,
89 ) -> StackFuture<'a, ScsiResult, { super::ASYNC_SCSI_DISK_STACK_SIZE }> {
90 StackFuture::from(async move {
91 let op = request.scsiop();
92
93 let sector_count = self.sector_count();
94 let result = match op {
95 ScsiOp::INQUIRY => self.handle_inquiry_iso(external_data, request),
96 ScsiOp::TEST_UNIT_READY => self.handle_test_unit_ready_iso(),
97 ScsiOp::READ | ScsiOp::READ12 | ScsiOp::READ16 => {
98 self.handle_data_cdb_async_iso(external_data, request, sector_count)
99 .instrument(tracing::trace_span!("handle_data_cdb_async", ?op,))
100 .await
101 }
102 ScsiOp::GET_EVENT_STATUS => self.handle_get_event_status(external_data, request),
103 ScsiOp::GET_CONFIGURATION => {
104 self.handle_get_configuration_iso(external_data, request)
105 }
106 ScsiOp::READ_CAPACITY => self.handle_read_capacity_iso(external_data, sector_count),
107 ScsiOp::READ_TOC => self.handle_read_toc(external_data, request),
108 ScsiOp::START_STOP_UNIT => self.handle_start_stop_unit(request).await,
109 ScsiOp::MODE_SENSE10 => self.handle_mode_sense_iso(external_data, request),
110 ScsiOp::REQUEST_SENSE => self.handle_request_sense_iso(external_data, request),
111 ScsiOp::MEDIUM_REMOVAL => self.handle_medium_removal_iso(request),
112 ScsiOp::MODE_SELECT10 => self.handle_mode_select_iso(request, external_data),
113 ScsiOp::READ_TRACK_INFORMATION => {
114 self.handle_read_track_information_iso(external_data, request)
115 }
116 ScsiOp::READ_DVD_STRUCTURE => {
117 self.handle_read_dvd_structure(external_data, request)
118 }
119 ScsiOp::GET_PERFORMANCE => self.handle_get_performance(external_data, request),
120 ScsiOp::MECHANISM_STATUS => self.handle_mechanism_status(external_data, request),
121 ScsiOp::READ_BUFFER_CAPACITY => {
122 self.handle_read_buffer_capacity(external_data, request)
123 }
124 ScsiOp::READ_DISC_INFORMATION => {
125 self.handle_read_disc_information(external_data, request)
126 }
127 ScsiOp::SET_STREAMING => self.handle_set_streaming(external_data, request),
128 _ => {
129 tracelimit::warn_ratelimited!(op = ?op, "illegal command");
130 Err(ScsiDvdError::IllegalRequest(
131 AdditionalSenseCode::ILLEGAL_COMMAND,
132 0,
133 ))
134 }
135 };
136
137 self.process_result(result, op)
138 })
139 }
140}
141
142impl inspect::Inspect for SimpleScsiDvd {
143 fn inspect(&self, req: inspect::Request<'_>) {
144 let mut resp = req.respond();
145
146 resp.field("drive_state", self.media_state.lock().drive_state);
147
148 if let Media::Loaded(disk) = &*self.media.read() {
149 resp.field("backend", disk);
150 }
151 }
152}
153
154const ISO_SECTOR_SIZE: u32 = 2048;
155
156impl SimpleScsiDvd {
157 pub fn new(disk: Option<Disk>) -> Self {
158 assert!(disk.as_ref().is_none() || disk.as_ref().unwrap().sector_size() <= ISO_SECTOR_SIZE);
159
160 let (media, pending_medium_event, drive_state) = if let Some(disk) = disk {
161 (
162 RwLock::new(Media::Loaded(disk)).into(),
163 IsoMediumEvent::MediaToMedia,
164 DriveState::MediumPresentTrayClosed,
165 )
166 } else {
167 (
168 RwLock::new(Media::Unloaded).into(),
169 IsoMediumEvent::MediaToNoMedia,
170 DriveState::MediumNotPresentTrayClosed,
171 )
172 };
173
174 SimpleScsiDvd {
175 media,
176 media_state: Arc::new(Mutex::new(MediaState {
177 pending_medium_event,
178 drive_state,
179 ..Default::default()
180 })),
181 sense_data: SenseDataSlot::default(),
182 }
183 }
184
185 pub fn change_media(&self, disk: Option<Disk>) {
186 if let Some(disk) = disk {
187 let mut media = self.media.write();
189
190 let mut media_state = self.media_state.lock();
191 media_state.drive_state = DriveState::MediumPresentTrayOpen;
192 media_state.pending_medium_event = IsoMediumEvent::NoMediaToMedia;
193
194 *media = Media::Loaded(disk);
195
196 tracing::debug!("completed host initiated insert");
197 } else {
198 let mut media = self.media.write();
200 let mut media_state = self.media_state.lock();
201
202 media_state.drive_state = DriveState::MediumNotPresentTrayOpen;
204 media_state.pending_medium_event = IsoMediumEvent::MediaToNoMedia;
205
206 *media = Media::Unloaded;
207
208 tracing::debug!("completed host initiated eject");
209 }
210 }
211
212 fn sector_shift(&self) -> u8 {
213 ISO_SECTOR_SIZE.trailing_zeros() as u8
214 }
215
216 fn sector_count(&self) -> u64 {
217 match &*self.media.read() {
218 Media::Unloaded => 0,
219 Media::Loaded(disk) => disk.sector_count() / self.balancer(),
220 }
221 }
222
223 fn balancer(&self) -> u64 {
224 match &*self.media.read() {
225 Media::Unloaded => unreachable!("cannot read/write from unloaded disk"),
226 Media::Loaded(disk) => {
227 (ISO_SECTOR_SIZE / disk.sector_size()) as u64
234 }
235 }
236 }
237}
238
239impl ScsiSaveRestore for SimpleScsiDvd {
240 fn save(&self) -> Result<Option<ScsiSavedState>, SaveError> {
241 let sense = self.sense_data.get();
242 let sense_data = sense.map(|sense| SavedSenseData {
243 sense_key: sense.header.sense_key.0,
244 additional_sense_code: sense.additional_sense_code.0,
245 additional_sense_code_qualifier: sense.additional_sense_code_qualifier,
246 });
247
248 let media_state = self.media_state.lock();
249 Ok(Some(ScsiSavedState::ScsiDvd(ScsiDvdSavedState {
250 sense_data,
251 persistent: media_state.persistent,
252 prevent: media_state.prevent,
253 drive_state: media_state.drive_state,
254 pending_medium_event: media_state.pending_medium_event,
255 })))
256 }
257
258 fn restore(&self, state: &ScsiSavedState) -> Result<(), RestoreError> {
259 if let ScsiSavedState::ScsiDvd(dvd_state) = state {
260 let ScsiDvdSavedState {
261 sense_data,
262 persistent,
263 prevent,
264 drive_state,
265 pending_medium_event,
266 } = *dvd_state;
267
268 self.sense_data.set(
270 sense_data
271 .map(|sense| {
272 SenseData::new(
273 SenseKey(sense.sense_key),
274 AdditionalSenseCode(sense.additional_sense_code),
275 sense.additional_sense_code_qualifier,
276 )
277 })
278 .as_ref(),
279 );
280
281 let mut media_state = self.media_state.lock();
282 media_state.drive_state = drive_state;
283 media_state.pending_medium_event = pending_medium_event;
284 media_state.persistent = persistent;
285 media_state.prevent = prevent;
286 Ok(())
287 } else {
288 Err(RestoreError::InvalidSavedState(anyhow::anyhow!(
289 "saved state didn't match expected format ScsiDvdSavedState"
290 )))
291 }
292 }
293}
294
295fn write_vpd_page<T: ?Sized + IntoBytes + Immutable + KnownLayout>(
300 external_data: &RequestBuffers<'_>,
301 allocation_length: usize,
302 page_code: u8,
303 page_data: &T,
304) -> Result<usize, ScsiDvdError> {
305 let header = scsi::VpdPageHeader {
306 device_type: scsi::READ_ONLY_DIRECT_ACCESS_DEVICE,
307 page_code,
308 reserved: 0,
309 page_length: size_of_val(page_data).try_into().unwrap(),
310 };
311
312 let tx = std::cmp::min(
313 allocation_length,
314 size_of_val(&header) + size_of_val(page_data),
315 );
316
317 let mut writer = external_data.writer();
318 writer
319 .write(header.as_bytes())
320 .map_err(ScsiDvdError::MemoryAccess)?;
321
322 writer
323 .write(&page_data.as_bytes()[..tx - size_of_val(&header)])
324 .map_err(ScsiDvdError::MemoryAccess)?;
325
326 Ok(tx)
327}
328
329impl SimpleScsiDvd {
330 fn handle_inquiry_iso(
331 &self,
332 external_data: &RequestBuffers<'_>,
333 request: &Request,
334 ) -> Result<usize, ScsiDvdError> {
335 let cdb = scsi::CdbInquiry::read_from_prefix(&request.cdb[..])
336 .unwrap()
337 .0; let allocation_length = cdb.allocation_length.get() as usize;
340
341 if external_data.len() < allocation_length {
342 return Err(ScsiDvdError::DataOverrun);
343 }
344 let enable_vpd = cdb.flags.vpd();
348 if cdb.page_code != 0 && !enable_vpd {
349 return Err(ScsiDvdError::IllegalRequest(
350 AdditionalSenseCode::INVALID_CDB,
351 0,
352 ));
353 }
354
355 if enable_vpd {
356 if allocation_length < size_of::<scsi::VpdPageHeader>() {
357 return Err(ScsiDvdError::DataOverrun);
358 }
359
360 match cdb.page_code {
361 scsi::VPD_SUPPORTED_PAGES => {
362 self.handle_vpd_supported_pages_iso(external_data, allocation_length)
363 }
364 scsi::VPD_DEVICE_IDENTIFIERS => {
365 self.handle_vpd_device_identifiers_iso(external_data, allocation_length)
366 }
367 _ => Err(ScsiDvdError::IllegalRequest(
368 AdditionalSenseCode::INVALID_CDB,
369 0,
370 )),
371 }
372 } else {
373 self.handle_no_vpd_page_iso(external_data, allocation_length)
374 }
375 }
376
377 fn handle_test_unit_ready_iso(&self) -> Result<usize, ScsiDvdError> {
378 let drive_state = self.media_state.lock().drive_state;
379
380 match drive_state {
381 DriveState::MediumPresentTrayOpen => Ok(0),
382 DriveState::MediumPresentTrayClosed => Ok(0),
383 DriveState::MediumNotPresentTrayOpen => Err(ScsiDvdError::SenseNotReady(
384 AdditionalSenseCode::NO_MEDIA_IN_DEVICE,
385 scsi::MEDIUM_NOT_PRESENT_TRAY_OPEN,
386 )),
387 DriveState::MediumNotPresentTrayClosed => Err(ScsiDvdError::SenseNotReady(
388 AdditionalSenseCode::NO_MEDIA_IN_DEVICE,
389 scsi::MEDIUM_NOT_PRESENT_TRAY_CLOSED,
390 )),
391 }
392 }
393
394 async fn handle_data_cdb_async_iso(
395 &self,
396 external_data: &RequestBuffers<'_>,
397 request: &Request,
398 sector_count: u64,
399 ) -> Result<usize, ScsiDvdError> {
400 let op = request.scsiop();
401 let p = self.validate_data_cdb_iso(external_data, request, sector_count, op)?;
402
403 if p.tx != 0 {
404 let mut media = None;
405 if let Media::Loaded(disk) = &*self.media.read() {
406 media = Some(disk.clone());
407 }
408
409 if let Some(disk) = media {
410 disk.read_vectored(&external_data.subrange(0, p.tx), p.offset)
411 .await
412 .map_err(ScsiDvdError::IoError)?;
413 }
414 }
415
416 Ok(p.tx)
417 }
418
419 fn handle_get_event_status(
420 &self,
421 external_data: &RequestBuffers<'_>,
422 request: &Request,
423 ) -> Result<usize, ScsiDvdError> {
424 let cdb = scsi::CdbGetEventStatusNotification::read_from_prefix(&request.cdb[..])
425 .unwrap()
426 .0; let allocation_length = cdb.event_list_length.get() as usize;
428 let mut pending_medium_event = IsoMediumEvent::None;
429
430 if allocation_length > external_data.len() {
432 return Err(ScsiDvdError::DataOverrun);
433 }
434
435 if !cdb.flags.immediate() {
437 return Err(ScsiDvdError::IllegalRequest(
438 AdditionalSenseCode::INVALID_CDB,
439 0x00,
440 ));
441 }
442
443 let mut media_status = scsi::NotificationMediaStatus::new_zeroed();
448 {
449 let mut media_state = self.media_state.lock();
450 if (cdb.notification_class_request & scsi::NOTIFICATION_MEDIA_STATUS_CLASS_MASK) != 0 {
451 pending_medium_event = media_state.pending_medium_event;
452 match media_state.pending_medium_event {
453 IsoMediumEvent::None => (),
454 IsoMediumEvent::NoMediaToMedia => {
455 media_status.media_event = scsi::NOTIFICATION_MEDIA_EVENT_NEW_MEDIA;
456 media_status.status_info.set_media_present(true);
457
458 media_state.drive_state = DriveState::MediumPresentTrayClosed;
459 media_state.pending_medium_event = IsoMediumEvent::None;
460 }
461 IsoMediumEvent::MediaToNoMedia => {
462 media_status.media_event = scsi::NOTIFICATION_MEDIA_EVENT_MEDIA_REMOVAL;
463 media_status.status_info.set_media_present(false);
464
465 media_state.pending_medium_event = IsoMediumEvent::None;
466 }
467 IsoMediumEvent::MediaToMedia => {
468 media_status.media_event = scsi::NOTIFICATION_MEDIA_EVENT_MEDIA_REMOVAL;
469 media_status.status_info.set_media_present(false);
470
471 media_state.drive_state = DriveState::MediumNotPresentTrayOpen;
472 media_state.pending_medium_event = IsoMediumEvent::NoMediaToMedia;
473 }
474 }
475
476 media_status
477 .status_info
478 .set_door_tray_open(media_state.drive_state.tray_open());
479 }
480 }
481
482 let data_transfer_length = if pending_medium_event != IsoMediumEvent::None {
483 let header_size = self.iso_init_event_header(
484 external_data,
485 allocation_length,
486 (size_of::<scsi::NotificationEventStatusHeader>() + size_of_val(&media_status)
487 - size_of::<u16>()) as u16,
488 false,
489 scsi::NOTIFICATION_MEDIA_STATUS_CLASS_EVENTS,
490 )?;
491
492 let body_size =
493 std::cmp::min(size_of_val(&media_status), allocation_length - header_size);
494
495 external_data
496 .subrange(header_size, external_data.len() - header_size)
497 .writer()
498 .write(&media_status.as_bytes()[..body_size])
499 .map_err(ScsiDvdError::MemoryAccess)?;
500
501 if media_status.media_event == scsi::NOTIFICATION_MEDIA_EVENT_NEW_MEDIA {
502 tracing::info!("media arrival");
503 } else if media_status.media_event == scsi::NOTIFICATION_MEDIA_EVENT_MEDIA_REMOVAL {
504 tracing::info!("media removal");
505 }
506 header_size + body_size
507 } else {
508 if (cdb.notification_class_request & scsi::NOTIFICATION_OPERATIONAL_CHANGE_CLASS_MASK)
509 != 0
510 {
511 let persistent_prevented = self.media_state.lock().persistent;
512 let operational_status = scsi::NotificationOperationalStatus {
513 operation_event: scsi::NOTIFICATION_POWER_EVENT_NO_CHANGE,
514 flags: scsi::OperationalStatusFlags::new()
515 .with_operational_status(scsi::NOTIFICATION_OPERATIONAL_STATUS_AVAILABLE)
516 .with_reserved2(0x00)
517 .with_persistent_prevented(persistent_prevented),
518 operation: 0.into(),
519 };
520
521 let header_size = self.iso_init_event_header(
522 external_data,
523 allocation_length,
524 (size_of::<scsi::NotificationEventStatusHeader>()
525 + size_of_val(&operational_status)
526 - size_of::<u16>()) as u16,
527 false,
528 scsi::NOTIFICATION_OPERATIONAL_CHANGE_CLASS_EVENTS,
529 )?;
530
531 let body_size = std::cmp::min(
532 size_of_val(&operational_status),
533 allocation_length - header_size,
534 );
535 external_data
536 .subrange(header_size, external_data.len() - header_size)
537 .writer()
538 .write(&operational_status.as_bytes()[..body_size])
539 .map_err(ScsiDvdError::MemoryAccess)?;
540
541 header_size + body_size
542 } else if (cdb.notification_class_request
543 & scsi::NOTIFICATION_POWER_MANAGEMENT_CLASS_MASK)
544 != 0
545 {
546 let header_size = self.iso_init_event_header(
547 external_data,
548 allocation_length,
549 (size_of::<scsi::NotificationEventStatusHeader>()
550 + size_of::<scsi::NotificationPowerStatus>()
551 - size_of::<u16>()) as u16,
552 false,
553 scsi::NOTIFICATION_POWER_MANAGEMENT_CLASS_EVENTS,
554 )?;
555 let data = scsi::NotificationPowerStatus {
556 power_event: scsi::NOTIFICATION_POWER_EVENT_NO_CHANGE,
557 power_status: scsi::NOTIFICATION_POWER_STATUS_ACTIVE,
558 reserved: [0x00, 0x00],
559 };
560 let body_size = std::cmp::min(
561 size_of::<scsi::NotificationPowerStatus>(),
562 allocation_length - header_size,
563 );
564 external_data
565 .subrange(header_size, external_data.len() - header_size)
566 .writer()
567 .write(&data.as_bytes()[..body_size])
568 .map_err(ScsiDvdError::MemoryAccess)?;
569
570 header_size + body_size
571 } else if (cdb.notification_class_request
572 & scsi::NOTIFICATION_EXTERNAL_REQUEST_CLASS_MASK)
573 != 0
574 {
575 let persistent_prevented = self.media_state.lock().persistent;
576 let external_status = scsi::NotificationExternalStatus {
577 external_event: scsi::NOTIFICATION_EXTERNAL_EVENT_NO_CHANGE,
578 flags: scsi::ExternalStatusFlags::new()
579 .with_external_status(scsi::NOTIFICATION_EXTERNAL_STATUS_READY)
580 .with_reserved2(0x00)
581 .with_persistent_prevented(persistent_prevented),
582 reserved: [0x00, 0x00],
583 };
584
585 let header_size = self.iso_init_event_header(
586 external_data,
587 allocation_length,
588 (size_of::<scsi::NotificationEventStatusHeader>()
589 + size_of_val(&external_status)
590 - size_of::<u16>()) as u16,
591 false,
592 scsi::NOTIFICATION_EXTERNAL_REQUEST_CLASS_EVENTS,
593 )?;
594 let body_size = std::cmp::min(
595 size_of_val(&external_status),
596 allocation_length - header_size,
597 );
598 external_data
599 .subrange(header_size, external_data.len() - header_size)
600 .writer()
601 .write(&external_status.as_bytes()[..body_size])
602 .map_err(ScsiDvdError::MemoryAccess)?;
603
604 header_size + body_size
605 } else if (cdb.notification_class_request & scsi::NOTIFICATION_MULTI_HOST_CLASS_MASK)
606 != 0
607 {
608 let header_size = self.iso_init_event_header(
609 external_data,
610 allocation_length,
611 (size_of::<scsi::NotificationEventStatusHeader>()
612 + size_of::<scsi::NotificationMultiHostStatus>()
613 - size_of::<u16>()) as u16,
614 false,
615 scsi::NOTIFICATION_MULTI_HOST_CLASS_EVENTS,
616 )?;
617 let multi_host_status = scsi::NotificationMultiHostStatus {
618 multi_host_event: scsi::NOTIFICATION_MULTI_HOST_EVENT_NO_CHANGE,
619 flags: scsi::MultiHostStatusFlags::new()
620 .with_multi_host_status(scsi::NOTIFICATION_MULTI_HOST_STATUS_READY)
621 .with_reserved2(0x00)
622 .with_persistent_prevented(self.media_state.lock().persistent),
623 priority: 0.into(),
624 };
625 let body_size = std::cmp::min(
626 size_of_val(&multi_host_status),
627 allocation_length - header_size,
628 );
629 external_data
630 .subrange(header_size, external_data.len() - header_size)
631 .writer()
632 .write(&multi_host_status.as_bytes()[..body_size])
633 .map_err(ScsiDvdError::MemoryAccess)?;
634
635 header_size + body_size
636 } else if (cdb.notification_class_request & scsi::NOTIFICATION_MEDIA_STATUS_CLASS_MASK)
637 != 0
638 {
639 let header_size = self.iso_init_event_header(
640 external_data,
641 allocation_length,
642 (size_of::<scsi::NotificationEventStatusHeader>() + size_of_val(&media_status)
643 - size_of::<u16>()) as u16,
644 false,
645 scsi::NOTIFICATION_MEDIA_STATUS_CLASS_EVENTS,
646 )?;
647 media_status
648 .status_info
649 .set_door_tray_open(self.media_state.lock().drive_state.tray_open());
650 media_status
651 .status_info
652 .set_media_present(self.media_state.lock().drive_state.medium_present());
653 let datab: &[u8] = media_status.as_bytes();
654 let body_size =
655 std::cmp::min(size_of_val(&media_status), allocation_length - header_size);
656 external_data
657 .subrange(header_size, external_data.len() - header_size)
658 .writer()
659 .write(&datab[..body_size])
660 .map_err(ScsiDvdError::MemoryAccess)?;
661 header_size + body_size
662 } else if (cdb.notification_class_request & scsi::NOTIFICATION_DEVICE_BUSY_CLASS_MASK)
663 != 0
664 {
665 let header_size = self.iso_init_event_header(
666 external_data,
667 allocation_length,
668 (size_of::<scsi::NotificationEventStatusHeader>()
669 + size_of::<scsi::NotificationBusyStatus>()
670 - size_of::<u16>()) as u16,
671 false,
672 scsi::NOTIFICATION_DEVICE_BUSY_CLASS_EVENTS,
673 )?;
674 let data = scsi::NotificationBusyStatus {
675 device_busy_event: scsi::NOTIFICATION_BUSY_EVENT_NO_EVENT,
676 device_busy_status: scsi::NOTIFICATION_BUSY_STATUS_NO_EVENT,
677 time: 0.into(),
678 };
679 let body_size = std::cmp::min(size_of_val(&data), allocation_length - header_size);
680 external_data
681 .subrange(header_size, external_data.len() - header_size)
682 .writer()
683 .write(&data.as_bytes()[..body_size])
684 .map_err(ScsiDvdError::MemoryAccess)?;
685
686 header_size + body_size
687 } else {
688 self.iso_init_event_header(
689 external_data,
690 allocation_length,
691 (size_of::<scsi::NotificationEventStatusHeader>() - size_of::<u16>()) as u16,
692 false,
693 scsi::NOTIFICATION_NO_CLASS_EVENTS,
694 )?
695 }
696 };
697
698 Ok(data_transfer_length)
699 }
700
701 fn handle_get_configuration_iso(
702 &self,
703 external_data: &RequestBuffers<'_>,
704 request: &Request,
705 ) -> Result<usize, ScsiDvdError> {
706 let cdb = scsi::CdbGetConfiguration::read_from_prefix(&request.cdb[..])
707 .unwrap()
708 .0; let request_type = cdb.flags.request_type();
710 let starting_feature = cdb.starting_feature.get() as usize;
711 let allocation_length = cdb.allocation_length.get() as usize;
712 let mut feature_size: usize = 0;
713
714 if allocation_length > external_data.len() {
715 return Err(ScsiDvdError::DataOverrun);
716 }
717 let mut bytes_used =
718 std::cmp::min(allocation_length, size_of::<scsi::GetConfigurationHeader>());
719 let header_size = bytes_used;
720 let mut bytes_req = size_of::<scsi::GetConfigurationHeader>();
721 match request_type {
722 scsi::RequestType::ALL => {
723 for i in 0..scsi::LIST_OF_FEATURES.len() {
725 if (scsi::LIST_OF_FEATURES[i] as usize) >= starting_feature {
726 bytes_used += self.iso_get_feature_page(
727 &external_data.subrange(bytes_used, external_data.len() - bytes_used),
728 allocation_length - bytes_used,
729 scsi::LIST_OF_FEATURES[i],
730 false,
731 &mut feature_size,
732 )?;
733 bytes_req += feature_size;
734 }
735 }
736
737 self.iso_init_configuration_header(
740 &external_data.subrange(0, header_size),
741 bytes_req,
742 )?;
743 }
744 scsi::RequestType::CURRENT => {
745 for i in 0..scsi::LIST_OF_FEATURES.len() {
747 if (scsi::LIST_OF_FEATURES[i] as usize) >= starting_feature {
748 bytes_used += self.iso_get_feature_page(
749 &external_data.subrange(bytes_used, external_data.len() - bytes_used),
750 allocation_length - bytes_used,
751 scsi::LIST_OF_FEATURES[i],
752 true,
753 &mut feature_size,
754 )?;
755 bytes_req += feature_size;
756 }
757 }
758
759 self.iso_init_configuration_header(
762 &external_data.subrange(0, header_size),
763 bytes_req,
764 )?;
765 }
766
767 scsi::RequestType::ONE => {
768 bytes_used += self.iso_get_feature_page(
770 &external_data.subrange(bytes_used, external_data.len() - bytes_used),
771 allocation_length - bytes_used,
772 scsi::FeatureNumber::try_from(starting_feature).unwrap(),
773 false,
774 &mut feature_size,
775 )?;
776
777 bytes_req += feature_size;
778
779 self.iso_init_configuration_header(
780 &external_data.subrange(0, header_size),
781 bytes_req,
782 )?;
783 }
784 _ => return Err(ScsiDvdError::IllegalRequestNoSenseData),
785 }
786
787 Ok(bytes_used)
788 }
789
790 fn handle_read_capacity_iso(
791 &self,
792 external_data: &RequestBuffers<'_>,
793 sector_count: u64,
794 ) -> Result<usize, ScsiDvdError> {
795 let tx = size_of::<scsi::ReadCapacityData>();
796
797 self.validate_media_for_read()?;
799
800 let last_lba = std::cmp::min(sector_count - 1, u32::MAX.into());
801 let data = scsi::ReadCapacityData {
802 logical_block_address: (last_lba as u32).into(),
803 bytes_per_block: ISO_SECTOR_SIZE.into(),
804 };
805
806 external_data
807 .writer()
808 .write(&data.as_bytes()[..tx])
809 .map_err(ScsiDvdError::MemoryAccess)?;
810
811 Ok(tx)
812 }
813
814 fn handle_read_toc(
815 &self,
816 external_data: &RequestBuffers<'_>,
817 request: &Request,
818 ) -> Result<usize, ScsiDvdError> {
819 let cdb = scsi::CdbReadToc::read_from_prefix(&request.cdb[..])
820 .unwrap()
821 .0; let allocation_length = cdb.allocation_length.get() as usize;
823 let format = cdb.format2 & 0x0f;
824 let msf = cdb.flag1.msf();
825 let mut formatted_toc = scsi::ReadTocFormattedToc {
826 length: 0x0802.into(),
827 first_complete_session: 0x01,
828 last_complete_session: 0x01,
829 track1: scsi::TrackData {
830 reserved: 0x00,
831 flag: scsi::TrackDataFlag::new().with_control(0x4).with_adr(0x1),
832 track_number: 0x01,
833 reserved1: 0x00,
834 address: [0; 4],
835 },
836 trackaa: scsi::TrackData {
837 reserved: 0x00,
838 flag: scsi::TrackDataFlag::new().with_control(0x4).with_adr(0x1),
839 track_number: 0xaa,
840 reserved1: 0x00,
841 address: [0; 4],
842 },
843 };
844 let mut session_data = scsi::CdromTocSessionData {
845 length: 0x000a.into(),
846 first_complete_session: 0x01,
847 last_complete_session: 0x01,
848 track_data: scsi::TrackData {
849 reserved: 0x00,
850 flag: scsi::TrackDataFlag::new().with_control(0x4).with_adr(0x1),
851 track_number: 0x01,
852 reserved1: 0x00,
853 address: [0; 4],
854 },
855 };
856 let data_transfer_length: usize;
857
858 self.validate_media_for_read()?;
859
860 let last_lba = std::cmp::min(self.sector_count() - 1, u32::MAX.into());
861 if allocation_length > external_data.len() {
862 return Err(ScsiDvdError::DataOverrun);
863 }
864 match format {
865 scsi::CDROM_READ_TOC_EX_FORMAT_TOC => {
866 if msf {
867 formatted_toc.track1.address = [0x00, 0x00, 0x02, 0x00];
868 formatted_toc.trackaa.address = [
869 0x00,
870 (((last_lba + 150) / 75) / 60) as u8,
871 (((last_lba + 150) / 75) % 60) as u8,
872 ((last_lba + 150) % 75) as u8,
873 ]
874 } else {
875 formatted_toc.track1.address = [0x00, 0x00, 0x00, 0x00];
876 formatted_toc.trackaa.address = [
877 (last_lba >> 24) as u8,
878 (last_lba >> 16) as u8,
879 (last_lba >> 8) as u8,
880 last_lba as u8,
881 ]
882 }
883 data_transfer_length =
884 std::cmp::min(allocation_length, size_of::<scsi::ReadTocFormattedToc>());
885 external_data
886 .writer()
887 .write(&formatted_toc.as_bytes()[..data_transfer_length])
888 .map_err(ScsiDvdError::MemoryAccess)?;
889 }
890 scsi::CDROM_READ_TOC_EX_FORMAT_SESSION => {
891 if msf {
892 session_data.track_data.address = [0x00, 0x00, 0x02, 0x00];
893 } else {
894 session_data.track_data.address = [0x00, 0x00, 0x00, 0x00];
895 }
896 data_transfer_length =
897 std::cmp::min(allocation_length, size_of::<scsi::CdromTocSessionData>());
898 external_data
899 .writer()
900 .write(&session_data.as_bytes()[..data_transfer_length])
901 .map_err(ScsiDvdError::MemoryAccess)?;
902 }
903 _ => {
904 return Err(ScsiDvdError::IllegalRequest(
905 AdditionalSenseCode::INVALID_CDB,
906 0x00,
907 ));
908 }
909 }
910 Ok(data_transfer_length)
911 }
912
913 async fn handle_start_stop_unit(&self, request: &Request) -> Result<usize, ScsiDvdError> {
914 let cdb = scsi::StartStop::read_from_prefix(&request.cdb[..])
915 .unwrap()
916 .0; let start = cdb.flag.start();
918 let load_eject = cdb.flag.load_eject();
919
920 match (load_eject, start) {
921 (false, false) => (), (false, true) => (), (true, false) => {
924 let mut previous_media = None;
925 {
926 let mut media_state = self.media_state.lock();
927 if !media_state.prevent {
929 if media_state.drive_state == DriveState::MediumPresentTrayClosed {
930 let mut media = self.media.write();
931
932 media_state.pending_medium_event = IsoMediumEvent::MediaToNoMedia;
934
935 previous_media = Some(std::mem::replace(&mut *media, Media::Unloaded));
936
937 tracing::debug!("handling guest initiated eject");
938 }
939
940 media_state.drive_state = DriveState::MediumNotPresentTrayOpen;
941 } else if media_state.drive_state.medium_present() {
942 return Err(ScsiDvdError::IllegalRequest(
943 AdditionalSenseCode::MEDIUM_REMOVAL_PREVENTED,
944 0x02,
945 ));
946 } else {
947 return Err(ScsiDvdError::SenseNotReady(
948 AdditionalSenseCode::MEDIUM_REMOVAL_PREVENTED,
949 0x02,
950 ));
951 }
952 }
953 if let Some(media) = previous_media {
954 if let Media::Loaded(disk) = media {
955 if let Err(e) = disk.eject().await {
956 tracelimit::error_ratelimited!(error = ?e, "eject error");
957 } else {
958 tracing::debug!("guest initiated eject complete");
959 }
960 } else {
961 tracelimit::warn_ratelimited!("attempted to eject unloaded media");
962 }
963 }
964 }
965 (true, true) => {
966 let mut media_state = self.media_state.lock();
967 if media_state.drive_state.tray_open() {
968 media_state.drive_state = DriveState::MediumNotPresentTrayClosed;
970 }
971 }
972 }
973
974 Ok(0)
975 }
976
977 fn handle_request_sense_iso(
978 &self,
979 external_data: &RequestBuffers<'_>,
980 request: &Request,
981 ) -> Result<usize, ScsiDvdError> {
982 let cdb = scsi::CdbRequestSense::read_from_prefix(&request.cdb[..])
983 .unwrap()
984 .0; let allocation_length = cdb.allocation_length as usize;
986 let new_sense_data =
987 SenseData::new(SenseKey::NO_SENSE, AdditionalSenseCode::NO_SENSE, 0x00);
988
989 self.sense_data.set(Some(&new_sense_data));
990
991 if external_data.len() < allocation_length {
992 Err(ScsiDvdError::DataOverrun)
993 } else {
994 let tx = std::cmp::min(allocation_length, size_of::<SenseData>());
995 external_data
996 .writer()
997 .write(&new_sense_data.as_bytes()[..tx])
998 .map_err(ScsiDvdError::MemoryAccess)?;
999 Ok(tx)
1000 }
1001 }
1002
1003 fn handle_mode_sense_iso(
1004 &self,
1005 external_data: &RequestBuffers<'_>,
1006 request: &Request,
1007 ) -> Result<usize, ScsiDvdError> {
1008 let cdb = scsi::ModeSense10::read_from_prefix(&request.cdb[..])
1009 .unwrap()
1010 .0; let page_code = cdb.flags2.page_code();
1012 let allocation_length = cdb.allocation_length.get() as usize;
1013 let pc = cdb.flags2.pc() << 6;
1014
1015 let mut bytes_used = std::cmp::min(allocation_length, super::MODE_PARAMETER_HEADER10_SIZE);
1016 let buffer_size_header = bytes_used;
1017 let mut bytes_required = super::MODE_PARAMETER_HEADER10_SIZE as u16;
1018 let mut mode_page_size: u16 = 0;
1019
1020 if external_data.len() < allocation_length {
1021 return Err(ScsiDvdError::DataOverrun);
1022 }
1023
1024 if pc == scsi::MODE_SENSE_SAVED_VALUES {
1025 return Err(ScsiDvdError::IllegalRequest(
1026 AdditionalSenseCode::SAVING_PARAMETER_NOT_SUPPORTED,
1027 0,
1028 ));
1029 }
1030
1031 match page_code {
1035 scsi::MODE_PAGE_ERROR_RECOVERY
1036 | scsi::MODE_PAGE_POWER_CONDITION
1037 | scsi::MODE_PAGE_CDVD_INACTIVITY => {
1038 bytes_used += self
1039 .iso_get_mode_page(
1040 &external_data.subrange(bytes_used, external_data.len() - bytes_used),
1041 allocation_length - bytes_used,
1042 page_code,
1043 &mut mode_page_size,
1044 )
1045 .unwrap();
1046 bytes_required += mode_page_size;
1047 self.iso_init_mode_sense_header(
1048 &external_data.subrange(0, super::MODE_PARAMETER_HEADER10_SIZE),
1049 allocation_length,
1050 bytes_required,
1051 )?;
1052 Ok(bytes_used)
1053 }
1054 scsi::MODE_SENSE_RETURN_ALL => {
1055 for i in 0..scsi::LIST_OF_MODE_PAGES.len() {
1056 bytes_used += self
1057 .iso_get_mode_page(
1058 &external_data.subrange(bytes_used, external_data.len() - bytes_used),
1059 allocation_length - bytes_used,
1060 scsi::LIST_OF_MODE_PAGES[i],
1061 &mut mode_page_size,
1062 )
1063 .unwrap();
1064 bytes_required += mode_page_size;
1065 }
1066 self.iso_init_mode_sense_header(
1067 &external_data.subrange(0, buffer_size_header),
1068 allocation_length,
1069 bytes_required,
1070 )?;
1071 Ok(bytes_used)
1072 }
1073 _ => Err(ScsiDvdError::IllegalRequest(
1074 AdditionalSenseCode::INVALID_CDB,
1075 0,
1076 )),
1077 }
1078 }
1079
1080 fn handle_medium_removal_iso(&self, request: &Request) -> Result<usize, ScsiDvdError> {
1081 let cdb = scsi::CdbMediaRemoval::read_from_prefix(&request.cdb[..])
1082 .unwrap()
1083 .0; let mut media_state = self.media_state.lock();
1087 media_state.persistent = cdb.flags.persistent();
1088 media_state.prevent = cdb.flags.prevent();
1089 Ok(0)
1090 }
1091
1092 fn handle_mode_select_iso(
1093 &self,
1094 request: &Request,
1095 external_data: &RequestBuffers<'_>,
1096 ) -> Result<usize, ScsiDvdError> {
1097 let cdb = scsi::ModeSelect10::read_from_prefix(&request.cdb[..])
1098 .unwrap()
1099 .0; let sp_bit = cdb.flags.spbit();
1102 let request_length = cdb.parameter_list_length.get() as usize;
1103 if request_length == 0 {
1104 return Ok(0);
1105 }
1106
1107 if request_length > external_data.len() {
1108 return Err(ScsiDvdError::DataOverrun);
1109 }
1110
1111 if request_length < super::MODE_PARAMETER_HEADER10_SIZE {
1116 return Err(ScsiDvdError::IllegalRequest(
1117 AdditionalSenseCode::PARAMETER_LIST_LENGTH,
1118 0x00,
1119 ));
1120 }
1121
1122 if sp_bit {
1124 return Err(ScsiDvdError::IllegalRequest(
1125 AdditionalSenseCode::INVALID_CDB,
1126 0x00,
1127 ));
1128 }
1129
1130 if request_length == super::MODE_PARAMETER_HEADER10_SIZE {
1131 return Ok(0);
1132 }
1133
1134 if request_length
1137 < super::MODE_PARAMETER_HEADER10_SIZE + size_of::<scsi::ModeReadWriteRecoveryPage>()
1138 {
1139 return Err(ScsiDvdError::IllegalRequest(
1140 AdditionalSenseCode::PARAMETER_LIST_LENGTH,
1141 0x00,
1142 ));
1143 }
1144
1145 let mut buffer: Vec<u8> = vec![0; request_length];
1146 external_data
1147 .reader()
1148 .read(&mut buffer)
1149 .map_err(ScsiDvdError::MemoryAccess)?;
1150
1151 let mode_page_error_recovery = scsi::ModeReadWriteRecoveryPage::read_from_prefix(
1152 &buffer[super::MODE_PARAMETER_HEADER10_SIZE
1153 ..super::MODE_PARAMETER_HEADER10_SIZE
1154 + size_of::<scsi::ModeReadWriteRecoveryPage>()],
1155 )
1156 .unwrap()
1157 .0; match mode_page_error_recovery.page_code {
1159 scsi::MODE_PAGE_ERROR_RECOVERY => {
1160 Ok(0)
1164 }
1165 scsi::MODE_PAGE_POWER_CONDITION => {
1166 Ok(0)
1170 }
1171 scsi::MODE_PAGE_CDVD_INACTIVITY => {
1172 Ok(0)
1176 }
1177 _ => Err(ScsiDvdError::IllegalRequestNoSenseData),
1178 }
1179 }
1180
1181 fn handle_read_track_information_iso(
1182 &self,
1183 external_data: &RequestBuffers<'_>,
1184 request: &Request,
1185 ) -> Result<usize, ScsiDvdError> {
1186 let cdb = scsi::CdbReadTrackInformation::read_from_prefix(&request.cdb[..])
1187 .unwrap()
1188 .0; let number_type = cdb.flag.number_type();
1190 let open = cdb.flag.open();
1191 let logical_track_number = cdb.logical_track_number.get() as usize;
1192 let allocation_length = cdb.allocation_length.get() as usize;
1193
1194 self.validate_media_for_read()?;
1195 let last_lba = std::cmp::min(self.sector_count() - 1, u32::MAX.into()) as u32;
1196
1197 if allocation_length > external_data.len() {
1198 return Err(ScsiDvdError::DataOverrun);
1199 }
1200
1201 if open {
1205 return Err(ScsiDvdError::IllegalRequest(
1206 AdditionalSenseCode::INVALID_CDB,
1207 0x00,
1208 ));
1209 }
1210
1211 match number_type {
1212 0x0 => {
1213 return Err(ScsiDvdError::IllegalRequest(
1217 AdditionalSenseCode::ILLEGAL_BLOCK,
1218 0x00,
1219 ));
1220 }
1221 0x1 => {
1222 if logical_track_number != 1 {
1223 return Err(ScsiDvdError::IllegalRequest(
1230 AdditionalSenseCode::INVALID_CDB,
1231 0x00,
1232 ));
1233 }
1234 }
1235 0x2 => {
1236 if logical_track_number != 1 {
1240 return Err(ScsiDvdError::IllegalRequest(
1241 AdditionalSenseCode::INVALID_CDB,
1242 0x00,
1243 ));
1244 }
1245 }
1246 _ => {
1247 return Err(ScsiDvdError::IllegalRequestNoSenseData);
1248 }
1249 }
1250 let track_information = scsi::TrackInformation3 {
1252 length: 0x002e.into(),
1253 track_number_lsb: 0x01,
1254 session_number_lsb: 0x01,
1255 reserved: 0x00,
1256 track_mode: 0x04,
1257 data_mode: 0b00100001,
1265 track_size: last_lba.into(),
1266 ..FromZeros::new_zeroed()
1267 };
1268 let tx = std::cmp::min(allocation_length, size_of::<scsi::TrackInformation3>());
1269 external_data
1270 .writer()
1271 .write(&track_information.as_bytes()[..tx])
1272 .map_err(ScsiDvdError::MemoryAccess)?;
1273 Ok(0)
1274 }
1275
1276 fn handle_read_dvd_structure(
1277 &self,
1278 external_data: &RequestBuffers<'_>,
1279 request: &Request,
1280 ) -> Result<usize, ScsiDvdError> {
1281 let cdb = scsi::CdbReadDVDStructure::read_from_prefix(&request.cdb[..])
1282 .unwrap()
1283 .0; let media_type = cdb.media_type;
1285 let layer = cdb.layer;
1286 let allocation_length = cdb.allocation_length.get() as usize;
1287
1288 self.validate_media_for_read()?;
1289
1290 if allocation_length > external_data.len() {
1291 return Err(ScsiDvdError::DataOverrun);
1292 }
1293
1294 if media_type != 0 {
1296 return Err(ScsiDvdError::IllegalRequest(
1297 AdditionalSenseCode::INVALID_MEDIA,
1298 scsi::SCSI_SENSEQ_INCOMPATIBLE_FORMAT,
1299 ));
1300 }
1301
1302 if layer != 0 {
1303 return Err(ScsiDvdError::IllegalRequestNoSenseData);
1304 }
1305
1306 match cdb.format {
1307 scsi::DVD_FORMAT_LEAD_IN => {
1308 let tx = std::cmp::min(
1309 allocation_length,
1310 size_of::<scsi::ReadDVDStructurePhysicalFormatInformation>(),
1311 );
1312 let data = scsi::ReadDVDStructurePhysicalFormatInformation {
1313 length: 0x802.into(),
1314 reserved: [0x00, 0x00],
1315 reserved2: 0x00, maximum_rate: 0x04, layer: 0x00,
1324 reserved3: 0x00, reserved4: 0x00,
1326 starting_physical_sector: [0x03, 0x00, 0x00],
1327 reserved5: 0x00,
1328 end_physical_sector: [0x00, 0x00, 0x00],
1329 reserved6: 0x00,
1330 end_physical_sector_in_layer0: [0x00, 0x00, 0x00],
1331 bca: 0x00,
1332 };
1333 external_data
1334 .writer()
1335 .write(&data.as_bytes()[..tx])
1336 .map_err(ScsiDvdError::MemoryAccess)?;
1337 Ok(tx)
1338 }
1339 scsi::DVD_FORMAT_COPYRIGHT => {
1340 let tx = std::cmp::min(
1341 allocation_length,
1342 size_of::<scsi::ReadDVDStructureCopyrightInformation>(),
1343 );
1344 let data = scsi::ReadDVDStructureCopyrightInformation {
1345 data_length: 0x0006.into(),
1346 reserved: 0x00,
1347 copyright_protection_system: 0x00,
1348 region_management_information: 0x00,
1349 reserved2: [0x00, 0x00],
1350 };
1351 external_data
1352 .writer()
1353 .write(&data.as_bytes()[..tx])
1354 .map_err(ScsiDvdError::MemoryAccess)?;
1355 Ok(tx)
1356 }
1357 scsi::DVD_FORMAT_BCA => {
1358 Err(ScsiDvdError::IllegalRequestNoSenseData)
1362 }
1363 scsi::DVD_FORMAT_MANUFACTURING => {
1364 let tx = std::cmp::min(
1365 allocation_length,
1366 size_of::<scsi::ReadDVDStructureManufacturingStructure>(),
1367 );
1368 let data = scsi::ReadDVDStructureManufacturingStructure {
1369 data_length: 0x0802.into(),
1370 reserved: [0x00, 0x00],
1371 };
1372 external_data
1373 .writer()
1374 .write(&data.as_bytes()[..tx])
1375 .map_err(ScsiDvdError::MemoryAccess)?;
1376 Ok(tx)
1377 }
1378 _ => Err(ScsiDvdError::IllegalRequest(
1379 AdditionalSenseCode::INVALID_CDB,
1380 0x00,
1381 )),
1382 }
1383 }
1384
1385 fn handle_get_performance(
1386 &self,
1387 external_data: &RequestBuffers<'_>,
1388 request: &Request,
1389 ) -> Result<usize, ScsiDvdError> {
1390 let cdb = scsi::CdbGetPerformance::read_from_prefix(&request.cdb[..])
1391 .unwrap()
1392 .0; let data_type = cdb.data_type;
1394 let write = cdb.flags.write();
1395 let tolerance = cdb.flags.tolerance();
1396 let except = cdb.flags.except();
1397 let mut nominal_performance = scsi::GetPerformanceNominalPerformanceDescriptor {
1398 start_lba: 0.into(),
1399 start_performance: scsi::PERFORMANCE_1000_BYTES_PER_SECOND.into(),
1400 end_lba: 0.into(),
1401 end_performance: scsi::PERFORMANCE_1000_BYTES_PER_SECOND.into(),
1402 };
1403
1404 let maximum_number_of_descriptors = cdb.maximum_number_of_descriptors.get();
1405 let mut data_transfer_length: u64;
1406
1407 self.validate_media_for_read()?;
1408
1409 let last_lba = std::cmp::min(self.sector_count() - 1, u32::MAX.into())
1410 .try_into()
1411 .expect("last_lba must be u32");
1412 match data_type {
1413 scsi::PERFORMANCE_TYPE_PERFORMANCE_DATA => {
1414 if write || (tolerance != 0x2) {
1417 return Err(ScsiDvdError::IllegalRequestNoSenseData);
1418 }
1419 match except {
1420 scsi::PERFORMANCE_EXCEPT_NOMINAL_PERFORMANCE => {
1421 nominal_performance.end_lba.set(last_lba);
1423 let header_size = self.iso_init_performance_header(
1426 external_data,
1427 external_data.len().try_into().unwrap(),
1428 (size_of::<scsi::GetPerformanceHeader>()
1429 + size_of::<scsi::GetPerformanceNominalPerformanceDescriptor>()
1430 - size_of::<u64>())
1431 .try_into()
1432 .unwrap(),
1433 0,
1434 )?;
1435 data_transfer_length = header_size.try_into().unwrap();
1436 if maximum_number_of_descriptors >= 1 {
1437 let body_size = std::cmp::min(
1438 external_data.len() - header_size,
1439 size_of::<scsi::GetPerformanceNominalPerformanceDescriptor>(),
1440 );
1441 external_data
1442 .subrange(header_size, external_data.len() - header_size)
1443 .writer()
1444 .write(&nominal_performance.as_bytes()[..body_size])
1445 .map_err(ScsiDvdError::MemoryAccess)?;
1446 data_transfer_length += body_size as u64;
1447 }
1448 }
1449 scsi::PERFORMANCE_EXCEPT_ENTIRE_PERFORMANCE_LIST
1450 | scsi::PERFORMANCE_EXCEPT_PERFORMANCE_EXCEPTIONS_ONLY => {
1451 let header_size = self.iso_init_performance_header(
1454 external_data,
1455 external_data.len().try_into().unwrap(),
1456 (size_of::<scsi::GetPerformanceHeader>() - size_of::<u64>())
1457 .try_into()
1458 .unwrap(),
1459 1,
1460 )?;
1461 data_transfer_length = header_size.try_into().unwrap();
1462 }
1463 _ => {
1464 return Err(ScsiDvdError::IllegalRequestNoSenseData);
1465 }
1466 }
1467 }
1468 _ => {
1469 return Err(ScsiDvdError::IllegalRequest(
1471 AdditionalSenseCode::INVALID_CDB,
1472 0x00,
1473 ));
1474 }
1475 }
1476 Ok(data_transfer_length as usize)
1477 }
1478
1479 fn handle_mechanism_status(
1480 &self,
1481 external_data: &RequestBuffers<'_>,
1482 request: &Request,
1483 ) -> Result<usize, ScsiDvdError> {
1484 let cdb = scsi::CdbMechStatus::read_from_prefix(&request.cdb[..])
1485 .unwrap()
1486 .0; let allocation_length = cdb.allocation_length.get() as usize;
1488
1489 if allocation_length > external_data.len() {
1490 return Err(ScsiDvdError::DataOverrun);
1491 }
1492
1493 let mechanism_status_header: scsi::MechanismStatusHeader = scsi::MechanismStatusHeader {
1496 flags: scsi::MechanismStatusHeaderFlags::new()
1497 .with_door_open(self.media_state.lock().drive_state.tray_open()),
1498 ..FromZeros::new_zeroed()
1499 };
1500 let tx = std::cmp::min(allocation_length, size_of::<scsi::MechanismStatusHeader>());
1501 external_data
1502 .writer()
1503 .write(&mechanism_status_header.as_bytes()[..tx])
1504 .map_err(ScsiDvdError::MemoryAccess)?;
1505
1506 Ok(tx)
1507 }
1508
1509 fn handle_read_buffer_capacity(
1510 &self,
1511 external_data: &RequestBuffers<'_>,
1512 request: &Request,
1513 ) -> Result<usize, ScsiDvdError> {
1514 let cdb = scsi::CdbReadBufferCapacity::read_from_prefix(&request.cdb[..])
1515 .unwrap()
1516 .0; let block_info = cdb.flags.block_info();
1518 let allocation_length = cdb.allocation_length.get() as usize;
1519
1520 if allocation_length > external_data.len() {
1521 return Err(ScsiDvdError::DataOverrun);
1522 }
1523
1524 let tx = std::cmp::min(allocation_length, size_of::<scsi::ReadBufferCapacityData>());
1525
1526 let data = scsi::ReadBufferCapacityData {
1527 data_length: (size_of::<scsi::ReadBufferCapacityData>() as u16 - 2).into(),
1528 block_data_returned: block_info as u8,
1529 ..FromZeros::new_zeroed()
1530 };
1531
1532 external_data
1533 .writer()
1534 .write(&data.as_bytes()[..tx])
1535 .map_err(ScsiDvdError::MemoryAccess)?;
1536
1537 Ok(tx)
1538 }
1539
1540 fn handle_read_disc_information(
1541 &self,
1542 external_data: &RequestBuffers<'_>,
1543 request: &Request,
1544 ) -> Result<usize, ScsiDvdError> {
1545 let cdb = scsi::CdbReadDiscInformation::read_from_prefix(&request.cdb[..])
1546 .unwrap()
1547 .0; let data_type = cdb.flags.data_type();
1549 let allocation_length = cdb.allocation_length.get() as usize;
1550
1551 self.validate_media_for_read()?;
1552 if allocation_length > external_data.len() {
1553 return Err(ScsiDvdError::DataOverrun);
1554 }
1555
1556 if data_type != 0x0 {
1558 return Err(ScsiDvdError::IllegalRequest(
1559 AdditionalSenseCode::INVALID_CDB,
1560 0x00,
1561 ));
1562 }
1563
1564 let tx = std::cmp::min(allocation_length, size_of::<scsi::DiscInformation>());
1565 let data = scsi::DiscInformation {
1566 length: 0x0020.into(),
1567 flags1: scsi::DiscInfoFlags1::new()
1568 .with_disc_status(0x2)
1569 .with_last_session_status(0x3)
1570 .with_erasable(false)
1571 .with_reserved1(0x0),
1572 first_track_number: 0x01,
1573 number_of_sessions_lsb: 0x01,
1574 last_session_first_track_lsb: 0x01,
1575 last_session_last_track_lsb: 0x01,
1576 flags2: scsi::DiscInfoFlags2::new()
1577 .with_mrw_status(0)
1578 .with_mrw_dirty_bit(false)
1579 .with_reserved2(0)
1580 .with_uru(true)
1581 .with_dbc_v(false)
1582 .with_did_v(false),
1583 ..FromZeros::new_zeroed()
1584 };
1585 external_data
1586 .writer()
1587 .write(&data.as_bytes()[..tx])
1588 .map_err(ScsiDvdError::MemoryAccess)?;
1589
1590 Ok(tx)
1591 }
1592
1593 fn handle_set_streaming(
1594 &self,
1595 external_data: &RequestBuffers<'_>,
1596 request: &Request,
1597 ) -> Result<usize, ScsiDvdError> {
1598 let cdb = scsi::CdbSetStreaming::read_from_prefix(&request.cdb[..])
1599 .unwrap()
1600 .0; let parameter_list_length = usize::from(cdb.parameter_list_length);
1602 let mut buffer = vec![0; parameter_list_length];
1603
1604 if parameter_list_length == 0 {
1605 return Ok(0);
1606 }
1607
1608 self.validate_media_for_read()?;
1609
1610 if parameter_list_length > external_data.len() {
1611 return Err(ScsiDvdError::DataOverrun);
1612 }
1613
1614 if parameter_list_length < size_of::<scsi::SetStreamingPerformanceDescriptor>() {
1618 return Err(ScsiDvdError::IllegalRequest(
1619 AdditionalSenseCode::PARAMETER_LIST_LENGTH,
1620 0x00,
1621 ));
1622 }
1623
1624 external_data
1627 .reader()
1628 .read(&mut buffer)
1629 .map_err(ScsiDvdError::MemoryAccess)?;
1630 let performance_descriptor =
1631 scsi::SetStreamingPerformanceDescriptor::read_from_prefix(&buffer[..])
1632 .unwrap()
1633 .0; if performance_descriptor.flags.rdd() {
1638 return Ok(0);
1639 }
1640 let last_lba = std::cmp::min(self.sector_count() - 1, u32::MAX.into()) as u32;
1641
1642 if u32::from(performance_descriptor.start_lba) > last_lba
1643 || u32::from(performance_descriptor.end_lba) > last_lba
1644 {
1645 return Err(ScsiDvdError::IllegalRequestNoSenseData);
1646 }
1647
1648 Ok(0)
1649 }
1650}
1651
1652impl SimpleScsiDvd {
1653 fn iso_init_event_header(
1654 &self,
1655 external_data: &RequestBuffers<'_>,
1656 buffer_size: usize,
1657 event_data_length: u16,
1658 nea: bool,
1659 notification_class: u8,
1660 ) -> Result<usize, ScsiDvdError> {
1661 let mut header: scsi::NotificationEventStatusHeader =
1662 scsi::NotificationEventStatusHeader::new_zeroed();
1663 header.event_data_length.set(event_data_length);
1664 header.flags.set_nea(nea);
1665 header.flags.set_notification_class(notification_class);
1666 header.supported_event_classes = scsi::NOTIFICATION_OPERATIONAL_CHANGE_CLASS_MASK
1667 | scsi::NOTIFICATION_POWER_MANAGEMENT_CLASS_MASK
1668 | scsi::NOTIFICATION_EXTERNAL_REQUEST_CLASS_MASK
1669 | scsi::NOTIFICATION_MEDIA_STATUS_CLASS_MASK
1670 | scsi::NOTIFICATION_MULTI_HOST_CLASS_MASK
1671 | scsi::NOTIFICATION_DEVICE_BUSY_CLASS_MASK;
1672 let tx: usize = std::cmp::min(
1673 size_of::<scsi::NotificationEventStatusHeader>(),
1674 buffer_size,
1675 );
1676 external_data
1677 .writer()
1678 .write(&header.as_bytes()[..tx])
1679 .map_err(ScsiDvdError::MemoryAccess)?;
1680 Ok(tx)
1681 }
1682
1683 fn iso_get_feature_page(
1684 &self,
1685 external_data: &RequestBuffers<'_>,
1686 buffer_size: usize,
1687 feature_code: scsi::FeatureNumber,
1688 current_only: bool,
1689 feature_size: &mut usize,
1690 ) -> Result<usize, ScsiDvdError> {
1691 let mut bytes_used: usize = 0;
1692 let mut profile_list = scsi::GetConfigurationFeatureDataProfileList {
1693 header: scsi::FeatureHeader {
1694 feature_code: (scsi::FeatureNumber::FeatureProfileList as u16).into(),
1695 flags: scsi::FeatureHeaderFlags::new()
1696 .with_current(true)
1697 .with_persistent(true)
1698 .with_version(0)
1699 .with_reserved(0),
1700 additional_length: (2 * (size_of::<scsi::FeatureDataProfileList>() as u8)),
1701 },
1702 profile: [
1703 scsi::FeatureDataProfileList {
1704 profile_number: scsi::PROFILE_DVD_ROM.into(),
1705 current: 0x01,
1706 reserved: 0x00,
1707 },
1708 scsi::FeatureDataProfileList {
1709 profile_number: scsi::PROFILE_CD_ROM.into(),
1710 current: 0x00,
1711 reserved: 0x00,
1712 },
1713 ],
1714 };
1715 let mut random_readable = scsi::FeatureDataRandomReadable {
1716 header: scsi::FeatureHeader {
1717 feature_code: (scsi::FeatureNumber::FeatureRandomReadable as u16).into(),
1718 flags: scsi::FeatureHeaderFlags::new()
1719 .with_current(true)
1720 .with_persistent(false)
1721 .with_version(0)
1722 .with_reserved(0),
1723 additional_length: 0x08,
1724 },
1725 logical_block_size: scsi::ISO_SECTOR_SIZE.into(),
1726 blocking: 16.into(),
1727 error_recovery_page_present: 0x01,
1728 reserved: 0x00,
1729 };
1730
1731 let mut dvd_read = scsi::FeatureDataDvdRead {
1732 header: scsi::FeatureHeader {
1733 feature_code: (scsi::FeatureNumber::FeatureDvdRead as u16).into(),
1734 flags: scsi::FeatureHeaderFlags::new()
1735 .with_current(true)
1736 .with_persistent(false)
1737 .with_version(0x01)
1738 .with_reserved(0),
1739 additional_length: 0x04,
1740 },
1741 multi_110: 0x00,
1742 reserved: 0x00,
1743 dual_dash_r: 0x00,
1744 reserved2: 0x00,
1745 };
1746
1747 let mut real_time_streaming = scsi::FeatureDataRealTimeStreaming {
1748 header: scsi::FeatureHeader {
1749 feature_code: (FeatureRealTimeStreaming as u16).into(),
1750 flags: scsi::FeatureHeaderFlags::new()
1751 .with_current(true)
1752 .with_persistent(true)
1753 .with_version(0x05)
1754 .with_reserved(0),
1755 additional_length: 0x04,
1756 },
1757 flags: scsi::RealTimeStreamingFlags::new()
1758 .with_stream_recording(false)
1759 .with_write_speed_in_get_perf(false)
1760 .with_write_speed_in_mp2_a(false)
1761 .with_set_cdspeed(false)
1762 .with_read_buffer_capacity_block(true)
1763 .with_reserved1(0x00),
1764 reserved: [0x00, 0x00, 0x00],
1765 };
1766
1767 *feature_size = 0;
1768 match feature_code {
1769 scsi::FeatureNumber::FeatureProfileList => {
1770 profile_list.profile[0].current =
1771 self.media_state.lock().drive_state.medium_present() as u8;
1772 *feature_size = size_of::<scsi::GetConfigurationFeatureDataProfileList>();
1773 bytes_used = std::cmp::min(*feature_size, buffer_size);
1774 if bytes_used > 0 {
1775 external_data
1776 .writer()
1777 .write(&profile_list.as_bytes()[..bytes_used])
1778 .map_err(ScsiDvdError::MemoryAccess)?;
1779 }
1780 }
1781 scsi::FeatureNumber::FeatureCore => {
1782 *feature_size = size_of::<scsi::FeatureDataCore>();
1783 bytes_used = std::cmp::min(*feature_size, buffer_size);
1784 let data = scsi::FeatureDataCore {
1785 header: scsi::FeatureHeader {
1786 feature_code: (scsi::FeatureNumber::FeatureCore as u16).into(),
1787 flags: scsi::FeatureHeaderFlags::new()
1788 .with_current(true)
1789 .with_persistent(true)
1790 .with_version(0x02)
1791 .with_reserved(0),
1792 additional_length: 0x08,
1793 },
1794 ..FromZeros::new_zeroed()
1795 };
1796 if bytes_used > 0 {
1797 external_data
1798 .writer()
1799 .write(&data.as_bytes()[..bytes_used])
1800 .map_err(ScsiDvdError::MemoryAccess)?;
1801 }
1802 }
1803 scsi::FeatureNumber::FeatureMorphing => {
1804 *feature_size = size_of::<scsi::FeatureDataMorphing>();
1805 bytes_used = std::cmp::min(*feature_size, buffer_size);
1806 let data = scsi::FeatureDataMorphing {
1807 header: scsi::FeatureHeader {
1808 feature_code: (scsi::FeatureNumber::FeatureMorphing as u16).into(),
1809 flags: scsi::FeatureHeaderFlags::new()
1810 .with_current(true)
1811 .with_persistent(true)
1812 .with_version(0x02)
1813 .with_reserved(0),
1814 additional_length: 0x08,
1815 },
1816 flags: scsi::FeatureMorphingFlags::new()
1817 .with_asynchronous(false)
1818 .with_ocevent(true)
1819 .with_reserved1(0x00),
1820 reserved2: [0x00, 0x00, 0x00],
1821 };
1822 if bytes_used > 0 {
1823 external_data
1824 .writer()
1825 .write(&data.as_bytes()[..bytes_used])
1826 .map_err(ScsiDvdError::MemoryAccess)?;
1827 }
1828 }
1829 scsi::FeatureNumber::FeatureRemovableMedium => {
1830 *feature_size = size_of::<scsi::FeatureDataRemovableMedium>();
1831 bytes_used = std::cmp::min(*feature_size, buffer_size);
1832 let data = scsi::FeatureDataRemovableMedium {
1833 header: scsi::FeatureHeader {
1834 feature_code: (scsi::FeatureNumber::FeatureRemovableMedium as u16).into(),
1835 flags: scsi::FeatureHeaderFlags::new()
1836 .with_current(true)
1837 .with_persistent(true)
1838 .with_version(0x01)
1839 .with_reserved(0),
1840 additional_length: 0x04,
1841 },
1842 flags: scsi::RemovableMediumFlags::new()
1843 .with_lockable(true)
1844 .with_dbml(false)
1845 .with_default_to_prevent(false)
1846 .with_eject(true)
1847 .with_load(true)
1848 .with_loading_mechanism(0x01),
1849 reserved2: [0x00, 0x00, 0x00],
1850 };
1851 if bytes_used > 0 {
1852 external_data
1853 .writer()
1854 .write(&data.as_bytes()[..bytes_used])
1855 .map_err(ScsiDvdError::MemoryAccess)?;
1856 }
1857 }
1858 scsi::FeatureNumber::FeatureRandomReadable => {
1859 let medium_present = self.media_state.lock().drive_state.medium_present();
1860 if !current_only || medium_present {
1861 random_readable.header.flags.set_current(medium_present);
1862 *feature_size = size_of::<scsi::FeatureDataRandomReadable>();
1863 bytes_used = std::cmp::min(*feature_size, buffer_size);
1864 if bytes_used > 0 {
1865 external_data
1866 .writer()
1867 .write(&random_readable.as_bytes()[..bytes_used])
1868 .map_err(ScsiDvdError::MemoryAccess)?;
1869 }
1870 }
1871 }
1872 scsi::FeatureNumber::FeatureCdRead => {
1873 if !current_only {
1874 *feature_size = size_of::<scsi::FeatureDataCdRead>();
1875 bytes_used = std::cmp::min(*feature_size, buffer_size);
1876 let data = scsi::FeatureDataCdRead {
1877 header: scsi::FeatureHeader {
1878 feature_code: (scsi::FeatureNumber::FeatureCdRead as u16).into(),
1879 flags: scsi::FeatureHeaderFlags::new()
1880 .with_current(false)
1881 .with_persistent(false)
1882 .with_version(0x02)
1883 .with_reserved(0),
1884 additional_length: 0x04,
1885 },
1886 flags: scsi::CDReadFlags::new()
1887 .with_cd_text(false)
1888 .with_c2_error_data(false)
1889 .with_reserved(0x00)
1890 .with_digital_audio_play(false),
1891 reserved2: [0x00, 0x00, 0x00],
1892 };
1893 if bytes_used > 0 {
1894 external_data
1895 .writer()
1896 .write(&data.as_bytes()[..bytes_used])
1897 .map_err(ScsiDvdError::MemoryAccess)?;
1898 }
1899 }
1900 }
1901 scsi::FeatureNumber::FeatureDvdRead => {
1902 let medium_present = self.media_state.lock().drive_state.medium_present();
1903 if !current_only || medium_present {
1904 *feature_size = size_of::<scsi::FeatureDataDvdRead>();
1905 bytes_used = std::cmp::min(*feature_size, buffer_size);
1906 dvd_read.header.flags.set_current(medium_present);
1907 if bytes_used > 0 {
1908 external_data
1909 .writer()
1910 .write(&dvd_read.as_bytes()[..bytes_used])
1911 .map_err(ScsiDvdError::MemoryAccess)?;
1912 }
1913 }
1914 }
1915 FeaturePowerManagement => {
1916 *feature_size = size_of::<scsi::FeatureDataPowerManagement>();
1917 bytes_used = std::cmp::min(*feature_size, buffer_size);
1918 let data = scsi::FeatureDataPowerManagement {
1919 header: scsi::FeatureHeader {
1920 feature_code: (FeaturePowerManagement as u16).into(),
1921 flags: scsi::FeatureHeaderFlags::new()
1922 .with_current(true)
1923 .with_persistent(true)
1924 .with_version(0x00)
1925 .with_reserved(0),
1926 additional_length: 0x00,
1927 },
1928 };
1929 if bytes_used > 0 {
1930 external_data
1931 .writer()
1932 .write(&data.as_bytes()[..bytes_used])
1933 .map_err(ScsiDvdError::MemoryAccess)?;
1934 }
1935 }
1936 FeatureTimeout => {
1937 *feature_size = size_of::<scsi::FeatureDataTimeout>();
1938 bytes_used = std::cmp::min(*feature_size, buffer_size);
1939 let data = scsi::FeatureDataTimeout {
1940 header: scsi::FeatureHeader {
1941 feature_code: (FeatureTimeout as u16).into(),
1942 flags: scsi::FeatureHeaderFlags::new()
1943 .with_current(true)
1944 .with_persistent(true)
1945 .with_version(0x01)
1946 .with_reserved(0),
1947 additional_length: 0x04,
1948 },
1949 group: 0x01,
1950 reserved: 0x00,
1951 unit_length: 512.into(),
1952 };
1953 if bytes_used > 0 {
1954 external_data
1955 .writer()
1956 .write(&data.as_bytes()[..bytes_used])
1957 .map_err(ScsiDvdError::MemoryAccess)?;
1958 }
1959 }
1960 FeatureRealTimeStreaming => {
1961 let medium_present = self.media_state.lock().drive_state.medium_present();
1962 if !current_only || medium_present {
1963 *feature_size = size_of::<scsi::FeatureDataRealTimeStreaming>();
1964 bytes_used = std::cmp::min(*feature_size, buffer_size);
1965 real_time_streaming.header.flags.set_current(medium_present);
1966 if bytes_used > 0 {
1967 external_data
1968 .writer()
1969 .write(&real_time_streaming.as_bytes()[..bytes_used])
1970 .map_err(ScsiDvdError::MemoryAccess)?;
1971 }
1972 }
1973 }
1974 _ => {}
1975 }
1976 Ok(bytes_used)
1977 }
1978
1979 fn iso_init_configuration_header(
1980 &self,
1981 external_data: &RequestBuffers<'_>,
1982 data_length: usize,
1983 ) -> Result<usize, ScsiDvdError> {
1984 let current_profile = if self.media_state.lock().drive_state.medium_present() {
1985 scsi::PROFILE_DVD_ROM
1986 } else {
1987 0
1988 };
1989
1990 let header = scsi::GetConfigurationHeader {
1993 data_length: (data_length as u32).into(),
1994 current_profile: current_profile.into(),
1995 ..FromZeros::new_zeroed()
1996 };
1997
1998 let tx = external_data.len();
1999
2000 external_data
2001 .writer()
2002 .write(&header.as_bytes()[..tx])
2003 .map_err(ScsiDvdError::MemoryAccess)?;
2004
2005 Ok(tx)
2006 }
2007
2008 fn handle_no_vpd_page_iso(
2009 &self,
2010 external_data: &RequestBuffers<'_>,
2011 allocation_length: usize,
2012 ) -> Result<usize, ScsiDvdError> {
2013 let data = scsi::InquiryData {
2015 header: scsi::InquiryDataHeader {
2016 device_type: 0x05,
2017 flags2: scsi::InquiryDataFlag2::new().with_removable_media(true),
2018 versions: 0x00,
2019 flags3: scsi::InquiryDataFlag3::new()
2020 .with_response_data_format(scsi::T10_RESPONSE_DATA_SPC3),
2021 additional_length: (scsi::INQUIRY_DATA_BUFFER_SIZE
2022 - (size_of::<scsi::InquiryDataHeader>() as u8)),
2023 },
2024 vendor_id: *b"Msft ",
2025 product_id: *b"Virtual DVD-ROM ",
2026 product_revision_level: *b"1.0 ",
2027 ..FromZeros::new_zeroed()
2028 };
2029
2030 let tx = std::cmp::min(allocation_length, size_of::<scsi::InquiryData>());
2031 external_data
2032 .writer()
2033 .write(&data.as_bytes()[..tx])
2034 .map_err(ScsiDvdError::MemoryAccess)?;
2035
2036 Ok(tx)
2037 }
2038
2039 fn handle_vpd_supported_pages_iso(
2040 &self,
2041 external_data: &RequestBuffers<'_>,
2042 allocation_length: usize,
2043 ) -> Result<usize, ScsiDvdError> {
2044 let page = [scsi::VPD_SUPPORTED_PAGES, scsi::VPD_DEVICE_IDENTIFIERS];
2045
2046 write_vpd_page(
2047 external_data,
2048 allocation_length,
2049 scsi::VPD_SUPPORTED_PAGES,
2050 &page,
2051 )
2052 }
2053
2054 fn handle_vpd_device_identifiers_iso(
2055 &self,
2056 external_data: &RequestBuffers<'_>,
2057 allocation_length: usize,
2058 ) -> Result<usize, ScsiDvdError> {
2059 let context_guid = [
2061 0x73, 0x05, 0xe3, 0x43, 0x77, 0x03, 0x54, 0x46, 0x94, 0x95, 0x7d, 0x7c, 0xed, 0x62,
2062 0x4a, 0x7d,
2063 ];
2064 let page = scsi::IsoVpdIdentifiers {
2065 id_page: scsi::VpdIdentificationDescriptor {
2066 code_set: scsi::VPD_CODE_SET_BINARY,
2067 identifiertype: scsi::VPD_IDENTIFIER_TYPE_VENDOR_ID,
2068 reserved3: 0x00,
2069 identifier_length: (size_of::<scsi::IsoVpdIdentifiers>()
2070 - size_of::<scsi::VpdIdentificationDescriptor>())
2071 as u8,
2072 },
2073 vendor_id: *b"Msft ",
2074 context_guid,
2075 };
2076
2077 write_vpd_page(
2078 external_data,
2079 allocation_length,
2080 scsi::VPD_DEVICE_IDENTIFIERS,
2081 &page,
2082 )
2083 }
2084
2085 fn validate_data_cdb_iso(
2086 &self,
2087 external_data: &RequestBuffers<'_>,
2088 request: &Request,
2089 sector_count: u64,
2090 op: ScsiOp,
2091 ) -> Result<RequestParametersIso, ScsiDvdError> {
2092 let (len, offset) = match op {
2093 ScsiOp::READ => {
2094 let cdb = scsi::Cdb10::read_from_prefix(&request.cdb[..]).unwrap().0; (
2096 cdb.transfer_blocks.get() as u64,
2097 cdb.logical_block.get() as u64,
2098 )
2099 }
2100 ScsiOp::READ12 => {
2101 let cdb = scsi::Cdb12::read_from_prefix(&request.cdb[..]).unwrap().0; (
2103 cdb.transfer_blocks.get() as u64,
2104 cdb.logical_block.get() as u64,
2105 )
2106 }
2107 ScsiOp::READ16 => {
2108 let cdb = scsi::Cdb16::read_from_prefix(&request.cdb[..]).unwrap().0; (cdb.transfer_blocks.get() as u64, cdb.logical_block.get())
2110 }
2111 _ => unreachable!(),
2112 };
2113
2114 if len == 0 {
2115 return Ok(RequestParametersIso {
2116 tx: len as usize,
2117 offset: 0,
2118 });
2119 }
2120
2121 self.validate_media_for_read()?;
2122
2123 let sector_shift = self.sector_shift();
2124 let max = external_data.len() >> sector_shift;
2125 if len as usize > max {
2126 tracelimit::error_ratelimited!(len, max, "illegal block");
2127 return Err(ScsiDvdError::IllegalRequest(
2128 AdditionalSenseCode::ILLEGAL_BLOCK,
2129 0,
2130 ));
2131 }
2132
2133 if sector_count <= offset || sector_count - offset < len {
2134 tracelimit::error_ratelimited!(sector_count, offset, len, "illegal block");
2135 return Err(ScsiDvdError::IllegalRequest(
2136 AdditionalSenseCode::ILLEGAL_BLOCK,
2137 0,
2138 ));
2139 }
2140
2141 let tx = (len as usize) << sector_shift;
2142 Ok(RequestParametersIso {
2143 tx,
2144 offset: (offset * self.balancer()),
2145 })
2146 }
2147
2148 fn iso_get_mode_page(
2149 &self,
2150 external_data: &RequestBuffers<'_>,
2151 buffer_size: usize,
2152 page_code: u8,
2153 mode_page_size: &mut u16,
2154 ) -> Result<usize, ScsiDvdError> {
2155 let tx: usize;
2156 let page_size: usize;
2157 match page_code {
2158 scsi::MODE_PAGE_ERROR_RECOVERY => {
2159 page_size = size_of::<scsi::ModeReadWriteRecoveryPage>();
2160 tx = std::cmp::min(page_size, buffer_size);
2161 let data = scsi::ModeReadWriteRecoveryPage {
2162 page_code: 0x01,
2163 page_length: 0x0a,
2164 bit_info: 0u8,
2165 read_retry_count: 0u8,
2166 reserved: [0u8, 0u8, 0u8, 0u8],
2167 write_retry_count: 0u8,
2168 reserved2: [0u8, 0u8, 0u8],
2169 };
2170 external_data
2171 .writer()
2172 .write(&data.as_bytes()[..tx])
2173 .map_err(ScsiDvdError::MemoryAccess)?;
2174 *mode_page_size = page_size as u16;
2175 }
2176 scsi::MODE_PAGE_POWER_CONDITION => {
2177 page_size = size_of::<scsi::PowerConditionPage>();
2178 tx = std::cmp::min(page_size, buffer_size);
2179 let data = scsi::PowerConditionPage {
2180 page_code: 0x1a,
2181 page_length: 0x0a,
2182 ..FromZeros::new_zeroed()
2183 };
2184 external_data
2185 .writer()
2186 .write(&data.as_bytes()[..tx])
2187 .map_err(ScsiDvdError::MemoryAccess)?;
2188 *mode_page_size = page_size as u16;
2189 }
2190 scsi::MODE_PAGE_CDVD_INACTIVITY => {
2191 page_size = size_of::<scsi::ModeSenseModePageTimeoutProtect>();
2192 tx = std::cmp::min(page_size, buffer_size);
2193 let data = scsi::ModeSenseModePageTimeoutProtect {
2194 page_code: 0x1d,
2195 page_length: 0x0a,
2196 reserved: [0u8, 0u8],
2197 bit_info: 0x00,
2198 reserved2: 0x00,
2199 group_one_minimum_timeout: [0x00, 0x05],
2200 group_two_minimum_timeout: [0x00, 0x14],
2201 group_three_timeout: [0x00, 0x0a],
2202 };
2203 external_data
2204 .writer()
2205 .write(&data.as_bytes()[..tx])
2206 .map_err(ScsiDvdError::MemoryAccess)?;
2207 *mode_page_size = page_size as u16;
2208 }
2209 _ => {
2210 *mode_page_size = 0;
2211 tx = 0;
2212 }
2213 };
2214 Ok(tx)
2215 }
2216
2217 fn iso_init_mode_sense_header(
2218 &self,
2219 external_data: &RequestBuffers<'_>,
2220 buffer_size: usize,
2221 data_length: u16,
2222 ) -> Result<usize, ScsiDvdError> {
2223 let data = scsi::ModeParameterHeader10 {
2224 mode_data_length: (data_length - (size_of::<u16>() as u16)).into(),
2225 block_descriptor_length: 0.into(),
2226 ..FromZeros::new_zeroed()
2227 };
2228 let tx = std::cmp::min(super::MODE_PARAMETER_HEADER10_SIZE, buffer_size);
2229 external_data
2230 .writer()
2231 .write(&data.as_bytes()[..tx])
2232 .map_err(ScsiDvdError::MemoryAccess)?;
2233 Ok(tx)
2234 }
2235
2236 fn validate_media_for_read(&self) -> Result<(), ScsiDvdError> {
2237 let drive_state = self.media_state.lock().drive_state;
2238
2239 match drive_state {
2240 DriveState::MediumPresentTrayOpen => Ok(()),
2241 DriveState::MediumPresentTrayClosed => Ok(()),
2242 DriveState::MediumNotPresentTrayOpen => Err(ScsiDvdError::SenseNotReady(
2243 AdditionalSenseCode::NO_MEDIA_IN_DEVICE,
2244 scsi::MEDIUM_NOT_PRESENT_TRAY_OPEN,
2245 )),
2246 DriveState::MediumNotPresentTrayClosed => Err(ScsiDvdError::SenseNotReady(
2247 AdditionalSenseCode::NO_MEDIA_IN_DEVICE,
2248 scsi::MEDIUM_NOT_PRESENT_TRAY_CLOSED,
2249 )),
2250 }
2251 }
2252
2253 fn iso_init_performance_header(
2254 &self,
2255 external_data: &RequestBuffers<'_>,
2256 buffer_size: u64,
2257 data_length: u64,
2258 except: u8,
2259 ) -> Result<usize, ScsiDvdError> {
2260 let header = scsi::GetPerformanceHeader {
2261 total_data_length: (data_length as u32).into(),
2262 except: except & 0x01,
2263 ..FromZeros::new_zeroed()
2264 };
2265
2266 let tx =
2267 std::cmp::min(size_of::<scsi::GetPerformanceHeader>() as u64, buffer_size) as usize;
2268 external_data
2269 .writer()
2270 .write(&header.as_bytes()[..tx])
2271 .map_err(ScsiDvdError::MemoryAccess)?;
2272 Ok(tx)
2273 }
2274
2275 fn process_result(&self, result: Result<usize, ScsiDvdError>, op: ScsiOp) -> ScsiResult {
2276 let result = match result {
2277 Ok(tx) => ScsiResult {
2278 scsi_status: ScsiStatus::GOOD,
2279 srb_status: SrbStatus::SUCCESS,
2280 tx,
2281 sense_data: None,
2282 },
2283 Err(err) => match err {
2284 ScsiDvdError::IllegalRequest(sense_code, sense_qualifier) => ScsiResult {
2285 scsi_status: ScsiStatus::CHECK_CONDITION,
2286 srb_status: SrbStatus::ERROR,
2287 tx: 0,
2288 sense_data: Some(illegal_request_sense_iso(sense_code, sense_qualifier)),
2289 },
2290 ScsiDvdError::DataOverrun => ScsiResult {
2291 scsi_status: ScsiStatus::CHECK_CONDITION,
2292 srb_status: SrbStatus::DATA_OVERRUN,
2293 tx: 0,
2294 sense_data: None,
2295 },
2296 ScsiDvdError::MemoryAccess(_) => ScsiResult {
2297 scsi_status: ScsiStatus::CHECK_CONDITION,
2298 srb_status: SrbStatus::INVALID_REQUEST,
2299 tx: 0,
2300 sense_data: Some(illegal_request_sense_iso(
2301 AdditionalSenseCode::INVALID_CDB,
2302 0,
2303 )),
2304 },
2305 ScsiDvdError::SenseNotReady(sense_code, sense_qualifier) => ScsiResult {
2306 scsi_status: ScsiStatus::CHECK_CONDITION,
2307 srb_status: SrbStatus::ERROR,
2308 tx: 0,
2309 sense_data: Some(SenseData::new(
2310 SenseKey::NOT_READY,
2311 sense_code,
2312 sense_qualifier,
2313 )),
2314 },
2315 ScsiDvdError::IoError(err) => match err {
2316 DiskError::UnsupportedEject => ScsiResult {
2317 scsi_status: ScsiStatus::CHECK_CONDITION,
2318 srb_status: SrbStatus::INVALID_REQUEST,
2319 tx: 0,
2320 sense_data: Some(illegal_request_sense_iso(
2321 AdditionalSenseCode::ILLEGAL_COMMAND,
2322 0,
2323 )),
2324 },
2325 DiskError::ReservationConflict => ScsiResult {
2326 scsi_status: ScsiStatus::RESERVATION_CONFLICT,
2327 srb_status: SrbStatus::ERROR,
2328 tx: 0,
2329 sense_data: Some(SenseData::new(
2330 SenseKey::ILLEGAL_REQUEST,
2331 AdditionalSenseCode::COMMAND_SEQUENCE_ERROR,
2332 scsi::SCSI_SENSEQ_CAPACITY_DATA_CHANGED,
2333 )),
2334 },
2335 _ => ScsiResult {
2336 scsi_status: ScsiStatus::CHECK_CONDITION,
2337 srb_status: SrbStatus::ERROR,
2338 tx: 0,
2339 sense_data: None,
2340 },
2341 },
2342 ScsiDvdError::IllegalRequestNoSenseData => ScsiResult {
2343 scsi_status: ScsiStatus::CHECK_CONDITION,
2344 srb_status: SrbStatus::INVALID_REQUEST,
2345 tx: 0,
2346 sense_data: None,
2347 },
2348 },
2349 };
2350
2351 self.sense_data.set(result.sense_data.as_ref());
2352 tracing::trace!(scsi_result = ?result, ?op, "process_result completed.");
2353
2354 result
2355 }
2356}
2357
2358fn illegal_request_sense_iso(sense_code: AdditionalSenseCode, sense_qualifier: u8) -> SenseData {
2359 match sense_code {
2360 AdditionalSenseCode::ILLEGAL_COMMAND
2361 | AdditionalSenseCode::INVALID_CDB
2362 | AdditionalSenseCode::NO_SENSE
2363 | AdditionalSenseCode::INVALID_FIELD_PARAMETER_LIST
2364 | AdditionalSenseCode::PARAMETER_LIST_LENGTH
2365 | AdditionalSenseCode::ILLEGAL_BLOCK
2366 | AdditionalSenseCode::INVALID_MEDIA
2367 | AdditionalSenseCode::SAVING_PARAMETER_NOT_SUPPORTED
2368 | AdditionalSenseCode::MEDIUM_REMOVAL_PREVENTED => {
2369 SenseData::new(SenseKey::ILLEGAL_REQUEST, sense_code, sense_qualifier)
2370 }
2371 _ => unreachable!(),
2372 }
2373}
2374
2375#[cfg(test)]
2376mod tests {
2377 use super::Media;
2378 use crate::SavedSenseData;
2379 use crate::ScsiSaveRestore;
2380 use crate::ScsiSavedState;
2381 use crate::scsi;
2382 use crate::scsidvd::ISO_SECTOR_SIZE;
2383 use crate::scsidvd::SimpleScsiDvd;
2384 use disk_backend::Disk;
2385 use disk_backend::DiskError;
2386 use disk_backend::DiskIo;
2387 use guestmem::GuestMemory;
2388 use guestmem::MemoryWrite;
2389 use inspect::Inspect;
2390 use pal_async::async_test;
2391 use scsi::AdditionalSenseCode;
2392 use scsi::ScsiOp;
2393 use scsi::SenseKey;
2394 use scsi_buffers::OwnedRequestBuffers;
2395 use scsi_buffers::RequestBuffers;
2396 use scsi_core::AsyncScsiDisk;
2397 use scsi_core::Request;
2398 use scsi_core::save_restore::ScsiDvdSavedState;
2399
2400 use zerocopy::IntoBytes;
2401
2402 #[derive(Debug)]
2403 struct TestDisk {
2404 sector_count: u64,
2405 sector_size: u32,
2406 storage: Vec<u8>,
2407 read_only: bool,
2408 }
2409
2410 impl Inspect for TestDisk {
2411 fn inspect(&self, req: inspect::Request<'_>) {
2412 req.respond();
2413 }
2414 }
2415
2416 impl TestDisk {
2417 pub fn new(sector_size: u32, sector_count: u64, read_only: bool) -> TestDisk {
2418 let buffer = make_repeat_data_buffer(sector_count as usize, sector_size as usize);
2419
2420 TestDisk {
2421 sector_count,
2422 sector_size,
2423 storage: buffer,
2424 read_only,
2425 }
2426 }
2427 }
2428
2429 impl DiskIo for TestDisk {
2430 fn disk_type(&self) -> &str {
2431 "test"
2432 }
2433
2434 fn sector_count(&self) -> u64 {
2435 self.sector_count
2436 }
2437
2438 fn sector_size(&self) -> u32 {
2439 self.sector_size
2440 }
2441
2442 fn is_read_only(&self) -> bool {
2443 self.read_only
2444 }
2445
2446 fn disk_id(&self) -> Option<[u8; 16]> {
2447 None
2448 }
2449
2450 fn physical_sector_size(&self) -> u32 {
2451 self.sector_size
2452 }
2453
2454 fn is_fua_respected(&self) -> bool {
2455 false
2456 }
2457
2458 async fn eject(&self) -> Result<(), DiskError> {
2459 Err(DiskError::UnsupportedEject)
2460 }
2461
2462 async fn read_vectored(
2463 &self,
2464 buffers: &RequestBuffers<'_>,
2465 sector: u64,
2466 ) -> Result<(), DiskError> {
2467 let offset = sector as usize * self.sector_size() as usize;
2468 let end_point = offset + buffers.len();
2469
2470 if self.storage.len() < end_point {
2471 return Err(DiskError::IllegalBlock);
2472 }
2473
2474 buffers.writer().write(&self.storage[offset..end_point])?;
2475 Ok(())
2476 }
2477
2478 async fn write_vectored(
2479 &self,
2480 _buffers: &RequestBuffers<'_>,
2481 _sector: u64,
2482 _fua: bool,
2483 ) -> Result<(), DiskError> {
2484 todo!()
2485 }
2486
2487 async fn sync_cache(&self) -> Result<(), DiskError> {
2488 todo!()
2489 }
2490
2491 async fn unmap(
2492 &self,
2493 _sector: u64,
2494 _count: u64,
2495 _block_level_only: bool,
2496 ) -> Result<(), DiskError> {
2497 Ok(())
2498 }
2499
2500 fn unmap_behavior(&self) -> disk_backend::UnmapBehavior {
2501 disk_backend::UnmapBehavior::Ignored
2502 }
2503 }
2504
2505 fn new_scsi_dvd(sector_size: u32, sector_count: u64, read_only: bool) -> SimpleScsiDvd {
2506 let disk = TestDisk::new(sector_size, sector_count, read_only);
2507 let scsi_dvd = SimpleScsiDvd::new(Some(Disk::new(disk).unwrap()));
2508 let sector_shift = ISO_SECTOR_SIZE.trailing_zeros() as u8;
2509 assert_eq!(scsi_dvd.sector_count(), sector_count / scsi_dvd.balancer());
2510 assert_eq!(scsi_dvd.sector_shift(), sector_shift);
2511 if let Media::Loaded(disk) = &*scsi_dvd.media.read() {
2512 assert_eq!(disk.is_read_only(), read_only);
2513 assert_eq!(disk.sector_size(), sector_size);
2514 } else {
2515 panic!("unexpected Media::Unloaded");
2516 }
2517 scsi_dvd
2518 }
2519
2520 fn make_repeat_data_buffer(sector_count: usize, sector_size: usize) -> Vec<u8> {
2521 let mut buf = vec![0u8; sector_count * sector_size];
2522 let mut temp = vec![0u8; sector_size];
2523 assert!(sector_size > 2);
2524 temp[sector_size / 2 - 1] = 2;
2525 temp[sector_size / 2] = 3;
2526
2527 for i in (0..buf.len()).step_by(temp.len()) {
2528 let end_point = i + temp.len();
2529 buf[i..end_point].copy_from_slice(&temp);
2530 }
2531
2532 buf
2533 }
2534
2535 async fn check_execute_scsi(
2536 scsi_dvd: &mut SimpleScsiDvd,
2537 external_data: &RequestBuffers<'_>,
2538 request: &Request,
2539 pass: bool,
2540 ) {
2541 let result = scsi_dvd.execute_scsi(external_data, request).await;
2542 match pass {
2543 true if result.scsi_status != scsi::ScsiStatus::GOOD => {
2544 panic!(
2545 "execute_scsi failed! request: {:?} result: {:?}",
2546 request, result
2547 );
2548 }
2549 false if result.scsi_status == scsi::ScsiStatus::GOOD => {
2550 panic!(
2551 "execute_scsi passed! request: {:?} result: {:?}",
2552 request, result
2553 );
2554 }
2555 _ => (),
2556 }
2557 }
2558
2559 fn make_cdb16_request(operation_code: ScsiOp, start_lba: u64, lba_count: u32) -> Request {
2560 let cdb = scsi::Cdb16 {
2561 operation_code,
2562 flags: scsi::Cdb16Flags::new(),
2563 logical_block: start_lba.into(),
2564 transfer_blocks: lba_count.into(),
2565 reserved2: 0,
2566 control: 0,
2567 };
2568 let mut data = [0u8; 16];
2569 data[..].copy_from_slice(cdb.as_bytes());
2570 Request {
2571 cdb: data,
2572 srb_flags: 0,
2573 }
2574 }
2575
2576 fn check_guest_memory(
2577 guest_mem: &GuestMemory,
2578 start_lba: u64,
2579 buff: &[u8],
2580 sector_size: usize,
2581 ) -> bool {
2582 let mut b = vec![0u8; buff.len()];
2583 if guest_mem.read_at(start_lba, &mut b).is_err() {
2584 panic!("guest_mem read error");
2585 };
2586 buff[..].eq(&b[..]) && (b[sector_size / 2 - 1] == 2) && (b[sector_size / 2] == 3)
2587 }
2588
2589 fn save_scsi_dvd(scsi_dvd: &SimpleScsiDvd) -> ScsiDvdSavedState {
2590 let saved_state =
2591 if let Some(ScsiSavedState::ScsiDvd(saved_state)) = scsi_dvd.save().unwrap() {
2592 saved_state
2593 } else {
2594 panic!("saved_state cannot be none")
2595 };
2596 let media_state = scsi_dvd.media_state.lock();
2597 assert_eq!(saved_state.persistent, media_state.persistent);
2598 assert_eq!(saved_state.prevent, media_state.prevent);
2599 assert_eq!(saved_state.drive_state, media_state.drive_state);
2600 assert_eq!(
2601 saved_state.pending_medium_event,
2602 media_state.pending_medium_event
2603 );
2604 let sense = scsi_dvd.sense_data.get();
2605 let sense_data = sense.map(|sense| SavedSenseData {
2606 sense_key: sense.header.sense_key.0,
2607 additional_sense_code: sense.additional_sense_code.0,
2608 additional_sense_code_qualifier: sense.additional_sense_code_qualifier,
2609 });
2610 assert_eq!(saved_state.sense_data, sense_data);
2611 saved_state
2612 }
2613
2614 fn restore_scsi_dvd(saved_state: ScsiDvdSavedState, scsi_dvd: &SimpleScsiDvd) {
2615 if scsi_dvd
2616 .restore(&ScsiSavedState::ScsiDvd(saved_state))
2617 .is_err()
2618 {
2619 panic!("restore scsi dvd failed. saved_state {:?}", saved_state);
2620 }
2621
2622 let media_state = scsi_dvd.media_state.lock();
2623 assert_eq!(saved_state.persistent, media_state.persistent);
2624 assert_eq!(saved_state.prevent, media_state.prevent);
2625 assert_eq!(saved_state.drive_state, media_state.drive_state);
2626 assert_eq!(
2627 saved_state.pending_medium_event,
2628 media_state.pending_medium_event
2629 );
2630 let sense = scsi_dvd.sense_data.get();
2631 let sense_data = sense.map(|sense| SavedSenseData {
2632 sense_key: sense.header.sense_key.0,
2633 additional_sense_code: sense.additional_sense_code.0,
2634 additional_sense_code_qualifier: sense.additional_sense_code_qualifier,
2635 });
2636 assert_eq!(saved_state.sense_data, sense_data);
2637 }
2638
2639 #[test]
2640 fn validate_new_scsi_dvd() {
2641 new_scsi_dvd(512, 2048, true);
2642 }
2643
2644 #[async_test]
2645 async fn validate_read16() {
2646 let sector_size = 512;
2647 let sector_count = 2048;
2648 let mut scsi_dvd = new_scsi_dvd(sector_size, sector_count, true);
2649
2650 let dvd_sector_size = ISO_SECTOR_SIZE as u64;
2651 let dvd_sector_count = scsi_dvd.sector_count();
2652 let external_data =
2653 OwnedRequestBuffers::linear(0, (dvd_sector_size * dvd_sector_count) as usize, true);
2654 let guest_mem = GuestMemory::allocate(4096);
2655 let start_lba = 0;
2656 let lba_count = 2;
2657 let request = make_cdb16_request(ScsiOp::READ16, start_lba, lba_count);
2658
2659 println!("read disk to guest_mem2 ...");
2660 check_execute_scsi(
2661 &mut scsi_dvd,
2662 &external_data.buffer(&guest_mem),
2663 &request,
2664 true,
2665 )
2666 .await;
2667
2668 println!("validate guest_mem2 ...");
2669 let data = make_repeat_data_buffer(sector_count as usize, sector_size as usize);
2670 assert_eq!(
2671 check_guest_memory(
2672 &guest_mem,
2673 0,
2674 &data[..(ISO_SECTOR_SIZE * lba_count) as usize],
2675 sector_size as usize
2676 ),
2677 true
2678 );
2679 }
2680
2681 #[test]
2682 fn validate_save_restore_scsi_dvd_no_change() {
2683 let scsi_dvd = new_scsi_dvd(512, 2048, true);
2684 let saved_state = save_scsi_dvd(&scsi_dvd);
2685 restore_scsi_dvd(saved_state, &scsi_dvd);
2686 }
2687
2688 #[test]
2689 fn validate_save_restore_scsi_dvd_with_sense_data() {
2690 let scsi_dvd = new_scsi_dvd(512, 2048, true);
2691 let mut saved_state = save_scsi_dvd(&scsi_dvd);
2692 saved_state.sense_data = Some(SavedSenseData {
2693 sense_key: SenseKey::UNIT_ATTENTION.0,
2694 additional_sense_code: AdditionalSenseCode::OPERATING_CONDITIONS_CHANGED.0,
2695 additional_sense_code_qualifier: scsi::SCSI_SENSEQ_OPERATING_DEFINITION_CHANGED,
2696 });
2697 restore_scsi_dvd(saved_state, &scsi_dvd);
2698 }
2699}