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