1#![expect(missing_docs)]
31#![forbid(unsafe_code)]
32
33mod drive;
34mod protocol;
35
36use crate::drive::save_restore::DriveSaveRestore;
37use crate::protocol::BusMasterReg;
38use crate::protocol::DeviceControlReg;
39use crate::protocol::IdeCommand;
40use crate::protocol::IdeConfigSpace;
41use crate::protocol::Status;
42use chipset_device::ChipsetDevice;
43use chipset_device::io::IoError;
44use chipset_device::io::IoResult;
45use chipset_device::io::deferred::DeferredWrite;
46use chipset_device::io::deferred::defer_write;
47use chipset_device::pci::PciConfigSpace;
48use chipset_device::pio::ControlPortIoIntercept;
49use chipset_device::pio::PortIoIntercept;
50use chipset_device::pio::RegisterPortIoIntercept;
51use chipset_device::poll_device::PollDevice;
52use disk_backend::Disk;
53use drive::DiskDrive;
54use drive::DriveRegister;
55use guestmem::GuestMemory;
56use guestmem::ranges::PagedRange;
57use ide_resources::IdePath;
58use inspect::Inspect;
59use inspect::InspectMut;
60use open_enum::open_enum;
61use pci_core::spec::cfg_space::Command;
62use pci_core::spec::cfg_space::HEADER_TYPE_00_SIZE;
63use pci_core::spec::cfg_space::HeaderType00;
64use protocol::BusMasterCommandReg;
65use protocol::BusMasterStatusReg;
66use scsi::CdbFlags;
67use scsi::ScsiOp;
68use scsi_core::AsyncScsiDisk;
69use scsi_defs as scsi;
70use std::fmt::Debug;
71use std::mem::offset_of;
72use std::ops::RangeInclusive;
73use std::sync::Arc;
74use std::task::Context;
75use thiserror::Error;
76use vmcore::device_state::ChangeDeviceState;
77use vmcore::line_interrupt::LineInterrupt;
78use zerocopy::IntoBytes;
79
80const PAGE_SIZE64: u64 = guestmem::PAGE_SIZE as u64;
81
82open_enum! {
83 pub enum IdeIoPort: u16 {
84 PRI_ENLIGHTENED = 0x1E0,
85 PRI_DATA = 0x1F0,
86 PRI_ERROR_FEATURES = 0x1F1,
87 PRI_SECTOR_COUNT = 0x1F2,
88 PRI_SECTOR_NUM = 0x1F3,
89 PRI_CYLINDER_LSB = 0x1F4,
90 PRI_CYLINDER_MSB = 0x1F5,
91 PRI_DEVICE_HEAD = 0x1F6,
92 PRI_STATUS_CMD = 0x1F7,
93 PRI_ALT_STATUS_DEVICE_CTL = 0x3F6,
94 SEC_ENLIGHTENED = 0x160,
95 SEC_DATA = 0x170,
96 SEC_ERROR_FEATURES = 0x171,
97 SEC_SECTOR_COUNT = 0x172,
98 SEC_SECTOR_NUM = 0x173,
99 SEC_CYLINDER_LSB = 0x174,
100 SEC_CYLINDER_MSB = 0x175,
101 SEC_DEVICE_HEAD = 0x176,
102 SEC_STATUS_CMD = 0x177,
103 SEC_ALT_STATUS_DEVICE_CTL = 0x376,
104 }
105}
106
107enum Port {
108 Data,
109 Drive(DriveRegister),
110 Enlightened,
111 BusMaster(BusMasterReg),
112}
113
114#[derive(Debug, Copy, Clone, PartialEq, Eq, Inspect)]
115enum DmaType {
116 Read,
118 Write,
120}
121
122#[derive(Debug, Inspect)]
123struct BusMasterState {
124 #[inspect(hex)]
125 cmd_status_reg: u32,
126 #[inspect(hex)]
127 port_addr_reg: u32,
128 #[inspect(hex)]
129 timing_reg: u32,
130 #[inspect(hex)]
131 secondary_timing_reg: u32,
132 #[inspect(hex)]
133 dma_ctl_reg: u32,
134}
135
136const DEFAULT_BUS_MASTER_PORT_ADDR_REG: u32 = 0x0000_0001;
138const DEFAULT_BUS_MASTER_CMD_STATUS_REG: u32 = 0x0280_0000;
139
140impl BusMasterState {
141 fn new() -> Self {
142 Self {
143 cmd_status_reg: DEFAULT_BUS_MASTER_CMD_STATUS_REG,
144 port_addr_reg: DEFAULT_BUS_MASTER_PORT_ADDR_REG,
145 timing_reg: 0x80008000,
151 secondary_timing_reg: 0,
152 dma_ctl_reg: 0,
153 }
154 }
155}
156
157#[derive(Debug, Default, Inspect)]
158struct ChannelBusMasterState {
159 command_reg: BusMasterCommandReg,
160 status_reg: BusMasterStatusReg,
161 #[inspect(hex)]
162 desc_table_ptr: u32,
163 dma_state: Option<DmaState>,
164 dma_error: bool,
165}
166
167impl ChannelBusMasterState {
168 fn dma_io_type(&self) -> DmaType {
169 if self.command_reg.write() {
170 DmaType::Write
171 } else {
172 DmaType::Read
173 }
174 }
175}
176
177pub struct IdeDevice {
179 channels: [Channel; 2],
182 bus_master_state: BusMasterState,
183 bus_master_pio_dynamic: Box<dyn ControlPortIoIntercept>,
184}
185
186impl InspectMut for IdeDevice {
187 fn inspect_mut(&mut self, req: inspect::Request<'_>) {
188 req.respond()
189 .field_mut("primary", &mut self.channels[0])
190 .field_mut("secondary", &mut self.channels[1])
191 .field("bus_master_state", &self.bus_master_state);
192 }
193}
194
195#[derive(Inspect, Debug)]
196struct EnlightenedCdWrite {
197 #[inspect(skip)]
198 deferred: DeferredWrite,
199 old_adapter_control_reg: u8,
200 guest_address: u64,
201 old_features_reg: u8,
202 data_buffer: u32,
203 skip_bytes_head: u16,
204 byte_count: u32,
205 block_count: u16,
206 drive_index: usize,
207}
208
209#[derive(Inspect, Debug)]
210struct EnlightenedHddWrite {
211 #[inspect(skip)]
212 deferred: DeferredWrite,
213 old_adapter_control_reg: u8,
214 guest_address: u64,
215 drive_index: usize,
216}
217
218#[derive(Debug, Inspect)]
219#[inspect(tag = "drive_type")]
220enum EnlightenedWrite {
221 Hard(#[inspect(rename = "write")] EnlightenedHddWrite),
222 Optical(#[inspect(rename = "write")] EnlightenedCdWrite),
223}
224
225#[derive(Debug, Error)]
226pub enum NewDeviceError {
227 #[error("disk too large: {0} bytes")]
228 DiskTooLarge(u64),
229}
230
231impl IdeDevice {
232 pub fn new(
234 guest_memory: GuestMemory,
235 register_pio: &mut dyn RegisterPortIoIntercept,
236 primary_channel_drives: [Option<DriveMedia>; 2],
237 secondary_channel_drives: [Option<DriveMedia>; 2],
238 primary_line_interrupt: LineInterrupt,
239 secondary_line_interrupt: LineInterrupt,
240 ) -> Result<Self, NewDeviceError> {
241 let channels = [
242 Channel::new(
243 primary_channel_drives,
244 ChannelType::Primary,
245 primary_line_interrupt,
246 guest_memory.clone(),
247 )?,
248 Channel::new(
249 secondary_channel_drives,
250 ChannelType::Secondary,
251 secondary_line_interrupt,
252 guest_memory,
253 )?,
254 ];
255
256 Ok(Self {
257 channels,
258 bus_master_state: BusMasterState::new(),
259 bus_master_pio_dynamic: register_pio.new_io_region("ide bus master", 16),
260 })
261 }
262
263 fn parse_port(&self, io_port: u16) -> Option<(Port, usize)> {
264 match IdeIoPort(io_port) {
265 IdeIoPort::PRI_ENLIGHTENED => Some((Port::Enlightened, 0)),
266 IdeIoPort::PRI_DATA => Some((Port::Data, 0)),
267 IdeIoPort::PRI_ERROR_FEATURES => Some((Port::Drive(DriveRegister::ErrorFeatures), 0)),
268 IdeIoPort::PRI_SECTOR_COUNT => Some((Port::Drive(DriveRegister::SectorCount), 0)),
269 IdeIoPort::PRI_SECTOR_NUM => Some((Port::Drive(DriveRegister::LbaLow), 0)),
270 IdeIoPort::PRI_CYLINDER_LSB => Some((Port::Drive(DriveRegister::LbaMid), 0)),
271 IdeIoPort::PRI_CYLINDER_MSB => Some((Port::Drive(DriveRegister::LbaHigh), 0)),
272 IdeIoPort::PRI_DEVICE_HEAD => Some((Port::Drive(DriveRegister::DeviceHead), 0)),
273 IdeIoPort::PRI_STATUS_CMD => Some((Port::Drive(DriveRegister::StatusCmd), 0)),
274 IdeIoPort::PRI_ALT_STATUS_DEVICE_CTL => {
275 Some((Port::Drive(DriveRegister::AlternateStatusDeviceControl), 0))
276 }
277 IdeIoPort::SEC_ENLIGHTENED => Some((Port::Enlightened, 1)),
278 IdeIoPort::SEC_DATA => Some((Port::Data, 1)),
279 IdeIoPort::SEC_ERROR_FEATURES => Some((Port::Drive(DriveRegister::ErrorFeatures), 1)),
280 IdeIoPort::SEC_SECTOR_COUNT => Some((Port::Drive(DriveRegister::SectorCount), 1)),
281 IdeIoPort::SEC_SECTOR_NUM => Some((Port::Drive(DriveRegister::LbaLow), 1)),
282 IdeIoPort::SEC_CYLINDER_LSB => Some((Port::Drive(DriveRegister::LbaMid), 1)),
283 IdeIoPort::SEC_CYLINDER_MSB => Some((Port::Drive(DriveRegister::LbaHigh), 1)),
284 IdeIoPort::SEC_DEVICE_HEAD => Some((Port::Drive(DriveRegister::DeviceHead), 1)),
285 IdeIoPort::SEC_STATUS_CMD => Some((Port::Drive(DriveRegister::StatusCmd), 1)),
286 IdeIoPort::SEC_ALT_STATUS_DEVICE_CTL => {
287 Some((Port::Drive(DriveRegister::AlternateStatusDeviceControl), 1))
288 }
289 io_port
290 if (IdeIoPort::PRI_ENLIGHTENED..=IdeIoPort::PRI_STATUS_CMD).contains(&io_port) =>
291 {
292 None
293 }
294 io_port
295 if (IdeIoPort::SEC_ENLIGHTENED..=IdeIoPort::SEC_STATUS_CMD).contains(&io_port) =>
296 {
297 None
298 }
299 _ => {
300 if self.bus_master_state.cmd_status_reg
301 & protocol::PCI_CONFIG_STATUS_IO_SPACE_ENABLE_MASK
302 == 0
303 {
304 return None;
305 }
306
307 Some((
312 Port::BusMaster(BusMasterReg(io_port & 0x7)),
313 (io_port as usize & 0x8) >> 3,
314 ))
315 }
316 }
317 }
318}
319
320impl Channel {
321 fn enlightened_port_write(
322 &mut self,
323 data: &[u8],
324 bus_master_state: &BusMasterState,
325 ) -> IoResult {
326 if data.len() != 4 {
327 return IoResult::Err(IoError::InvalidAccessSize);
328 }
329
330 if let Some(status) = self.current_drive_status() {
331 if status.err() {
332 tracelimit::warn_ratelimited!(
333 "drive is in error state, ignoring enlightened command",
334 );
335 return IoResult::Ok;
336 } else if status.bsy() || status.drq() {
337 tracelimit::warn_ratelimited!(
338 "command is already pending on this drive, ignoring enlightened command"
339 );
340 return IoResult::Ok;
341 }
342 }
343 if self.enlightened_write.is_some() {
344 tracelimit::error_ratelimited!("enlightened write while one is in progress, ignoring");
345 return IoResult::Ok;
346 }
347
348 let addr = u32::from_ne_bytes(data.try_into().unwrap());
350
351 let eint13_cmd = match self
352 .guest_memory
353 .read_plain::<protocol::EnlightenedInt13Command>(addr as u64)
354 {
355 Ok(cmd) => cmd,
356 Err(err) => {
357 tracelimit::error_ratelimited!(
358 error = &err as &dyn std::error::Error,
359 "failed to read enlightened IO command"
360 );
361 return IoResult::Ok;
362 }
363 };
364
365 self.write_drive_register(
368 DriveRegister::DeviceHead,
369 eint13_cmd.device_head.into(),
370 bus_master_state,
371 );
372
373 let result = if let Some(drive_type) = self.current_drive_type() {
374 match drive_type {
375 DriveType::Optical => {
376 self.enlightened_cd_command(addr.into(), eint13_cmd, bus_master_state)
377 }
378 DriveType::Hard => {
379 self.enlightened_hdd_command(addr.into(), eint13_cmd, bus_master_state)
380 }
381 }
382 } else {
383 tracelimit::warn_ratelimited!(
384 eint13_cmd = ?eint13_cmd,
385 drive_idx = self.state.current_drive_idx,
386 "Enlightened IO command: No attached drive"
387 );
388 IoResult::Ok
389 };
390
391 self.post_drive_access(bus_master_state);
392 result
393 }
394
395 fn enlightened_hdd_command(
396 &mut self,
397 guest_address: u64,
398 eint13_cmd: protocol::EnlightenedInt13Command,
399 bus_master_state: &BusMasterState,
400 ) -> IoResult {
401 let mut lba48 = eint13_cmd.lba_high as u64;
402 lba48 <<= 32;
403 lba48 |= eint13_cmd.lba_low as u64;
404
405 tracing::trace!(
406 command = ?eint13_cmd.command,
407 lba = lba48,
408 block_count = eint13_cmd.block_count,
409 buffer = eint13_cmd.data_buffer,
410 guest_address,
411 "enlightened hdd command"
412 );
413
414 let cmd = eint13_cmd.command;
420 if !matches!(
421 cmd,
422 IdeCommand::READ_DMA
423 | IdeCommand::READ_DMA_ALT
424 | IdeCommand::WRITE_DMA
425 | IdeCommand::WRITE_DMA_ALT
426 | IdeCommand::READ_DMA_EXT
427 | IdeCommand::WRITE_DMA_EXT
428 | IdeCommand::WRITE_DMA_FUA_EXT
429 ) {
430 tracelimit::warn_ratelimited!(?cmd, "ignoring non-DMA command in enlightened path");
431 return IoResult::Ok;
432 }
433
434 self.write_bus_master_reg(
436 BusMasterReg::TABLE_PTR,
437 eint13_cmd.data_buffer.as_bytes(),
438 bus_master_state,
439 )
440 .unwrap();
441
442 if cmd == IdeCommand::READ_DMA_EXT || cmd == IdeCommand::WRITE_DMA_EXT {
445 self.write_drive_register(
447 DriveRegister::LbaLow,
448 (eint13_cmd.lba_low >> 24) as u8,
449 bus_master_state,
450 );
451
452 self.write_drive_register(
453 DriveRegister::LbaMid,
454 eint13_cmd.lba_high as u8,
455 bus_master_state,
456 );
457
458 self.write_drive_register(
459 DriveRegister::LbaHigh,
460 (eint13_cmd.lba_high >> 8) as u8,
461 bus_master_state,
462 );
463
464 self.write_drive_register(
467 DriveRegister::SectorCount,
468 (eint13_cmd.block_count >> 8) as u8,
469 bus_master_state,
470 );
471 }
472
473 self.write_drive_register(
475 DriveRegister::SectorCount,
476 eint13_cmd.block_count as u8,
477 bus_master_state,
478 );
479
480 self.write_drive_register(
482 DriveRegister::LbaLow,
483 eint13_cmd.lba_low as u8,
484 bus_master_state,
485 );
486 self.write_drive_register(
487 DriveRegister::LbaMid,
488 (eint13_cmd.lba_low >> 8) as u8,
489 bus_master_state,
490 );
491 self.write_drive_register(
492 DriveRegister::LbaHigh,
493 (eint13_cmd.lba_low >> 16) as u8,
494 bus_master_state,
495 );
496
497 let old_adapter_control_reg = self.state.shadow_adapter_control_reg;
500 self.write_drive_register(
501 DriveRegister::AlternateStatusDeviceControl,
502 DeviceControlReg::new()
503 .with_interrupt_mask(true)
504 .into_bits(),
505 bus_master_state,
506 );
507
508 let mut bus_master_flags = BusMasterCommandReg::new().with_start(true);
510 if cmd == IdeCommand::READ_DMA_EXT
511 || cmd == IdeCommand::READ_DMA
512 || cmd == IdeCommand::READ_DMA_ALT
513 {
514 bus_master_flags.set_write(true);
516 }
517
518 self.write_bus_master_reg(
519 BusMasterReg::COMMAND,
520 &[bus_master_flags.into_bits() as u8],
521 bus_master_state,
522 )
523 .unwrap();
524
525 self.write_drive_register(DriveRegister::StatusCmd, cmd.0, bus_master_state);
527
528 let (write, token) = defer_write();
530 self.enlightened_write = Some(EnlightenedWrite::Hard(EnlightenedHddWrite {
531 deferred: write,
532 old_adapter_control_reg,
533 guest_address,
534 drive_index: self.state.current_drive_idx,
535 }));
536
537 tracing::trace!(enlightened_write = ?self.enlightened_write, "enlightened_hdd_command");
538 if let Some(status) = self.current_drive_status() {
539 if status.drq() {
540 tracelimit::warn_ratelimited!(
541 "command is waiting for data read from guest or data write to guest"
542 );
543 return IoResult::Ok;
544 }
545 }
546 IoResult::Defer(token)
547 }
548
549 fn enlightened_cd_command(
550 &mut self,
551 guest_address: u64,
552 eint13_cmd: protocol::EnlightenedInt13Command,
553 bus_master_state: &BusMasterState,
554 ) -> IoResult {
555 tracing::trace!(
556 guest_address,
557 command = ?eint13_cmd,
558 "enlightened cd command"
559 );
560
561 let old_features_reg = self.state.shadow_features_reg;
565 self.write_drive_register(DriveRegister::ErrorFeatures, 0, bus_master_state);
566
567 let old_adapter_control_reg = self.state.shadow_adapter_control_reg;
571 self.write_drive_register(
572 DriveRegister::AlternateStatusDeviceControl,
573 DeviceControlReg::new()
574 .with_interrupt_mask(true)
575 .into_bits(),
576 bus_master_state,
577 );
578
579 self.write_drive_register(
581 DriveRegister::StatusCmd,
582 IdeCommand::PACKET_COMMAND.0,
583 bus_master_state,
584 );
585
586 let cdb = scsi::Cdb10 {
589 operation_code: ScsiOp::READ,
590 flags: CdbFlags::new(),
591 logical_block: eint13_cmd.lba_low.into(),
592 reserved2: 0,
593 transfer_blocks: eint13_cmd.block_count.into(),
594 control: 0,
595 };
596
597 let mut command = [0; 12];
599 command[..cdb.as_bytes().len()].copy_from_slice(cdb.as_bytes());
600
601 self.write_drive_data(command.as_bytes(), bus_master_state);
604
605 let (write, token) = defer_write();
607 self.enlightened_write = Some(EnlightenedWrite::Optical(EnlightenedCdWrite {
608 deferred: write,
609 old_adapter_control_reg,
610 guest_address,
611 old_features_reg,
612 data_buffer: eint13_cmd.data_buffer,
613 skip_bytes_head: eint13_cmd.skip_bytes_head,
614 byte_count: eint13_cmd.byte_count,
615 block_count: eint13_cmd.block_count,
616 drive_index: self.state.current_drive_idx,
617 }));
618
619 tracing::trace!(enlightened_write = ?self.enlightened_write, "enlightened_cd_command");
620 if let Some(status) = self.current_drive_status() {
621 if status.drq() {
622 tracelimit::warn_ratelimited!(
623 "command is waiting for data read from guest or data write to guest"
624 );
625 return IoResult::Ok;
626 }
627 }
628 IoResult::Defer(token)
629 }
630
631 fn complete_enlightened_hdd_write(
632 &mut self,
633 write: EnlightenedHddWrite,
634 bus_master_state: &BusMasterState,
635 ) {
636 self.write_bus_master_reg(BusMasterReg::COMMAND, &[0], bus_master_state)
639 .unwrap();
640
641 let status = self.read_drive_register(DriveRegister::StatusCmd, bus_master_state);
643 let status = Status::from_bits(status);
644
645 if status.err() {
646 if let Err(err) = self.guest_memory.write_at(
649 write.guest_address
650 + offset_of!(protocol::EnlightenedInt13Command, result_status) as u64,
651 &[status.into_bits()],
652 ) {
653 tracelimit::error_ratelimited!(
654 ?status,
655 error = &err as &dyn std::error::Error,
656 "failed to write eint13 status back"
657 );
658 }
659 }
660
661 self.write_drive_register(
663 DriveRegister::AlternateStatusDeviceControl,
664 write.old_adapter_control_reg,
665 bus_master_state,
666 );
667
668 write.deferred.complete();
669 }
670
671 fn complete_enlightened_cd_write(
672 &mut self,
673 write: EnlightenedCdWrite,
674 bus_master_state: &BusMasterState,
675 ) {
676 let status = self.read_drive_register(DriveRegister::StatusCmd, bus_master_state);
678 let status = Status::from_bits(status);
679
680 if status.err() {
681 if let Err(err) = self.guest_memory.write_at(
684 write.guest_address
685 + offset_of!(protocol::EnlightenedInt13Command, result_status) as u64,
686 &[status.into_bits()],
687 ) {
688 tracelimit::error_ratelimited!(
689 ?status,
690 error = &err as &dyn std::error::Error,
691 "failed to write eint13 status back"
692 );
693 }
694 } else {
695 let mut remaining =
696 (write.block_count as u32 * protocol::CD_DRIVE_SECTOR_BYTES) as usize;
697
698 let skip = (write.skip_bytes_head as usize).min(remaining);
700 remaining -= skip;
701 self.skip_drive_data(skip, bus_master_state);
702
703 let byte_count = (write.byte_count as usize).min(remaining);
705 remaining -= byte_count;
706
707 let mut copied = 0;
708 while copied < byte_count {
709 let mut buf = [0; 512];
710 let len = (byte_count - copied).min(buf.len());
711 let buf = &mut buf[..len];
712 self.read_drive_data(buf, bus_master_state);
713 if let Err(err) = self
714 .guest_memory
715 .write_at((write.data_buffer as u64).wrapping_add(copied as u64), buf)
716 {
717 tracelimit::warn_ratelimited!(
718 error = &err as &dyn std::error::Error,
719 "failed to write enlightened result to guest memory"
720 );
721 }
722 copied += buf.len();
723 }
724
725 self.skip_drive_data(remaining, bus_master_state);
727 }
728
729 self.write_drive_register(
731 DriveRegister::ErrorFeatures,
732 write.old_features_reg,
733 bus_master_state,
734 );
735
736 self.write_drive_register(
738 DriveRegister::AlternateStatusDeviceControl,
739 write.old_adapter_control_reg,
740 bus_master_state,
741 );
742
743 tracing::trace!("enlightened cd write completed");
744 write.deferred.complete();
745 }
746
747 fn gpa_to_gpn(gpa: u64) -> u64 {
748 gpa / PAGE_SIZE64
749 }
750
751 fn perform_dma_memory_phase(&mut self) {
752 let Some(drive) = &mut self.drives[self.state.current_drive_idx] else {
753 return;
754 };
755
756 if self.bus_master_state.dma_error {
757 if drive.handle_read_dma_descriptor_error() {
758 self.bus_master_state.dma_error = false;
759 }
760 return;
761 }
762
763 let (dma_type, mut dma_avail) = match drive.dma_request() {
764 Some((dma_type, avail)) if *dma_type == self.bus_master_state.dma_io_type() => {
765 (Some(*dma_type), avail as u32)
766 }
767 _ => {
768 return;
770 }
771 };
772
773 let Some(dma) = &mut self.bus_master_state.dma_state else {
774 return;
775 };
776
777 while dma_avail > 0 {
778 if dma.transfer_bytes_left == 0 {
780 assert!(!dma.transfer_complete);
781
782 let descriptor_addr: u64 = self
789 .bus_master_state
790 .desc_table_ptr
791 .wrapping_add(8 * (dma.descriptor_idx as u32))
792 .into();
793
794 let cur_desc_table_entry = match self
795 .guest_memory
796 .read_plain::<protocol::BusMasterDmaDesc>(descriptor_addr)
797 {
798 Ok(cur_desc_table_entry) => cur_desc_table_entry,
799 Err(err) => {
800 self.bus_master_state.dma_state = None;
801 if !drive.handle_read_dma_descriptor_error() {
802 self.bus_master_state.dma_error = true;
803 }
804 tracelimit::error_ratelimited!(
805 error = &err as &dyn std::error::Error,
806 "dma descriptor read error"
807 );
808 return;
809 }
810 };
811
812 tracing::trace!(entry = ?cur_desc_table_entry, "read dma desc");
813
814 dma.transfer_bytes_left = cur_desc_table_entry.byte_count.into();
815 if cur_desc_table_entry.byte_count == 0 {
817 dma.transfer_bytes_left = 0x10000;
818 }
819
820 let end_gpa = cur_desc_table_entry
826 .mem_physical_base
827 .checked_add(dma.transfer_bytes_left);
828
829 let mut r = None;
830
831 if let Some(end_gpa) = end_gpa {
832 let start_gpn = Self::gpa_to_gpn(cur_desc_table_entry.mem_physical_base.into());
833 let end_gpn = Self::gpa_to_gpn(end_gpa.into());
834 let gpns: Vec<u64> = (start_gpn..=end_gpn).collect();
835
836 if let Some(paged_range) =
837 PagedRange::new(0, gpns.len() * PAGE_SIZE64 as usize, &gpns)
838 {
839 r = Some(match dma_type.unwrap() {
840 DmaType::Read => {
841 self.guest_memory.probe_gpn_readable_range(&paged_range)
842 }
843 DmaType::Write => {
844 self.guest_memory.probe_gpn_writable_range(&paged_range)
845 }
846 });
847 }
848 }
849
850 if r.is_some_and(|res| res.is_err()) || end_gpa.is_none() {
851 self.bus_master_state.dma_state = None;
855 if !drive.handle_read_dma_descriptor_error() {
856 self.bus_master_state.dma_error = true;
857 }
858
859 tracelimit::error_ratelimited!("dma base address out-of-range error");
860 return;
861 }
862
863 dma.transfer_base_addr = cur_desc_table_entry.mem_physical_base.into();
864 dma.transfer_complete = (cur_desc_table_entry.end_of_table & 0x80) != 0;
865
866 dma.descriptor_idx += 1;
868 if dma.transfer_complete {
869 dma.descriptor_idx = 0;
870 }
871 }
872
873 let bytes_to_transfer = dma_avail.min(dma.transfer_bytes_left);
875
876 assert!(bytes_to_transfer != 0);
877
878 drive.dma_transfer(
879 &self.guest_memory,
880 dma.transfer_base_addr,
881 bytes_to_transfer as usize,
882 );
883
884 dma_avail -= bytes_to_transfer;
885 dma.transfer_base_addr += bytes_to_transfer as u64;
886 dma.transfer_bytes_left -= bytes_to_transfer;
887 if dma.transfer_bytes_left == 0 && dma.transfer_complete {
888 if dma_avail > 0 {
889 drive.set_prd_exhausted();
891 drive.dma_advance_buffer(dma_avail as usize);
892 }
893 tracing::trace!("dma transfer is complete");
894 self.bus_master_state.dma_state = None;
895 break;
896 }
897 }
898 }
899}
900
901impl ChangeDeviceState for IdeDevice {
902 fn start(&mut self) {}
903
904 async fn stop(&mut self) {}
905
906 async fn reset(&mut self) {
907 self.bus_master_pio_dynamic.unmap();
908 self.bus_master_state = BusMasterState::new();
909 for channel in &mut self.channels {
910 channel.reset();
911 }
912 }
913}
914
915impl ChipsetDevice for IdeDevice {
916 fn supports_pio(&mut self) -> Option<&mut dyn PortIoIntercept> {
917 Some(self)
918 }
919
920 fn supports_pci(&mut self) -> Option<&mut dyn PciConfigSpace> {
921 Some(self)
922 }
923
924 fn supports_poll_device(&mut self) -> Option<&mut dyn PollDevice> {
925 Some(self)
926 }
927}
928
929impl PollDevice for IdeDevice {
930 fn poll_device(&mut self, cx: &mut Context<'_>) {
931 for channel in &mut self.channels {
932 channel.poll_device(cx, &self.bus_master_state);
933 }
934 }
935}
936
937impl PortIoIntercept for IdeDevice {
938 fn io_read(&mut self, io_port: u16, data: &mut [u8]) -> IoResult {
939 match self.parse_port(io_port) {
940 Some((port, index)) => match port {
941 Port::Data => {
942 self.channels[index].read_drive_data(data, &self.bus_master_state);
943 IoResult::Ok
944 }
945 Port::Drive(register) => {
946 data[0] =
947 self.channels[index].read_drive_register(register, &self.bus_master_state);
948 IoResult::Ok
949 }
950 Port::Enlightened => IoResult::Err(IoError::InvalidRegister),
951 Port::BusMaster(offset) => self.channels[index].read_bus_master_reg(offset, data),
952 },
953 None => IoResult::Err(IoError::InvalidRegister),
954 }
955 }
956
957 fn io_write(&mut self, io_port: u16, data: &[u8]) -> IoResult {
958 match self.parse_port(io_port) {
959 Some((port, index)) => match port {
960 Port::Data => {
961 self.channels[index].write_drive_data(data, &self.bus_master_state);
962 IoResult::Ok
963 }
964 Port::Drive(register) => {
965 self.channels[index].write_drive_register(
966 register,
967 data[0],
968 &self.bus_master_state,
969 );
970 IoResult::Ok
971 }
972 Port::Enlightened => {
973 self.channels[index].enlightened_port_write(data, &self.bus_master_state)
974 }
975 Port::BusMaster(offset) => {
976 self.channels[index].write_bus_master_reg(offset, data, &self.bus_master_state)
977 }
978 },
979 None => IoResult::Err(IoError::InvalidRegister),
980 }
981 }
982
983 fn get_static_regions(&mut self) -> &[(&str, RangeInclusive<u16>)] {
984 &[
985 (
986 "ide primary channel",
987 IdeIoPort::PRI_ENLIGHTENED.0..=IdeIoPort::PRI_STATUS_CMD.0,
988 ),
989 (
990 "ide primary channel control",
991 IdeIoPort::PRI_ALT_STATUS_DEVICE_CTL.0..=IdeIoPort::PRI_ALT_STATUS_DEVICE_CTL.0,
992 ),
993 (
994 "ide secondary channel",
995 IdeIoPort::SEC_ENLIGHTENED.0..=IdeIoPort::SEC_STATUS_CMD.0,
996 ),
997 (
998 "ide secondary channel control",
999 IdeIoPort::SEC_ALT_STATUS_DEVICE_CTL.0..=IdeIoPort::SEC_ALT_STATUS_DEVICE_CTL.0,
1000 ),
1001 ]
1002 }
1003}
1004
1005impl PciConfigSpace for IdeDevice {
1006 fn pci_cfg_read(&mut self, offset: u16, value: &mut u32) -> IoResult {
1007 *value = if offset < HEADER_TYPE_00_SIZE {
1008 match HeaderType00(offset) {
1009 HeaderType00::DEVICE_VENDOR => protocol::BX_PCI_ISA_BRIDGE_IDE_IDREG_VALUE,
1010 HeaderType00::STATUS_COMMAND => self.bus_master_state.cmd_status_reg,
1011 HeaderType00::CLASS_REVISION => protocol::BX_PCI_IDE_CLASS_WORD,
1012 HeaderType00::BAR4 => self.bus_master_state.port_addr_reg,
1013 offset => {
1014 tracing::debug!(?offset, "undefined type00 header read");
1015 0
1016 }
1017 }
1018 } else {
1019 match IdeConfigSpace(offset) {
1020 IdeConfigSpace::PRIMARY_TIMING_REG_ADDR => self.bus_master_state.timing_reg,
1021 IdeConfigSpace::SECONDARY_TIMING_REG_ADDR => {
1022 self.bus_master_state.secondary_timing_reg
1023 }
1024 IdeConfigSpace::UDMA_CTL_REG_ADDR => self.bus_master_state.dma_ctl_reg,
1025 IdeConfigSpace::MANUFACTURE_ID_REG_ADDR => {
1026 0x00000F30
1029 }
1030 offset => {
1031 tracing::trace!(?offset, "undefined ide pci config space read");
1033 return IoResult::Err(IoError::InvalidRegister);
1034 }
1035 }
1036 };
1037
1038 tracing::trace!(?offset, value, "ide pci config space read");
1039 IoResult::Ok
1040 }
1041
1042 fn pci_cfg_write(&mut self, offset: u16, value: u32) -> IoResult {
1043 if offset < HEADER_TYPE_00_SIZE {
1044 let offset = HeaderType00(offset);
1045 tracing::trace!(?offset, value, "ide pci config space write");
1046
1047 const BUS_MASTER_IO_ENABLE_MASK: u32 = Command::new()
1048 .with_pio_enabled(true)
1049 .with_bus_master(true)
1050 .into_bits() as u32;
1051
1052 match offset {
1053 HeaderType00::STATUS_COMMAND => {
1054 self.bus_master_state.cmd_status_reg &= !(0x38000000 & value);
1056 self.bus_master_state.cmd_status_reg &= !BUS_MASTER_IO_ENABLE_MASK;
1058
1059 self.bus_master_state.cmd_status_reg |= value & BUS_MASTER_IO_ENABLE_MASK;
1060
1061 if (self.bus_master_state.cmd_status_reg
1063 & protocol::CFCS_BUS_MASTER_IO_ENABLE_MASK)
1064 != protocol::CFCS_BUS_MASTER_IO_ENABLE_MASK
1065 {
1066 self.bus_master_pio_dynamic.unmap();
1067 tracing::trace!("disabling bus master io range");
1068 } else {
1069 let first_port = (self.bus_master_state.port_addr_reg as u16) & 0xFFF0;
1072 tracing::trace!(?first_port, "enabling bus master range");
1073
1074 self.bus_master_pio_dynamic.map(first_port);
1077 }
1078 }
1079 HeaderType00::BAR4 => {
1080 self.bus_master_state.port_addr_reg =
1082 (value & 0x0000FFF0) | DEFAULT_BUS_MASTER_PORT_ADDR_REG;
1083 }
1084 _ => tracing::debug!(?offset, "undefined type00 header write"),
1085 }
1086 } else {
1087 let offset = IdeConfigSpace(offset);
1088 tracing::trace!(?offset, value, "ide pci config space write");
1089
1090 match offset {
1091 IdeConfigSpace::PRIMARY_TIMING_REG_ADDR => self.bus_master_state.timing_reg = value,
1092 IdeConfigSpace::SECONDARY_TIMING_REG_ADDR => {
1093 self.bus_master_state.secondary_timing_reg = value
1094 }
1095 IdeConfigSpace::UDMA_CTL_REG_ADDR => self.bus_master_state.dma_ctl_reg = value,
1096 _ => tracing::trace!(?offset, "undefined ide pci config space write"),
1097 }
1098 }
1099
1100 IoResult::Ok
1101 }
1102
1103 fn suggested_bdf(&mut self) -> Option<(u8, u8, u8)> {
1104 Some((0, 7, 1)) }
1106}
1107
1108enum ChannelType {
1110 Primary,
1111 Secondary,
1112}
1113
1114#[derive(Inspect)]
1115#[inspect(tag = "drive_type")]
1116pub enum DriveMedia {
1117 HardDrive(#[inspect(rename = "backend")] Disk),
1118 OpticalDrive(#[inspect(rename = "backend")] Arc<dyn AsyncScsiDisk>),
1119}
1120
1121impl DriveMedia {
1122 pub fn hard_disk(disk: Disk) -> Self {
1123 DriveMedia::HardDrive(disk)
1124 }
1125
1126 pub fn optical_disk(scsi_disk: Arc<dyn AsyncScsiDisk>) -> Self {
1127 DriveMedia::OpticalDrive(scsi_disk)
1128 }
1129}
1130
1131#[derive(Debug, Default, Inspect)]
1132struct ChannelState {
1133 current_drive_idx: usize,
1134 shadow_adapter_control_reg: u8,
1135 shadow_features_reg: u8,
1136}
1137
1138#[derive(InspectMut)]
1139struct Channel {
1140 #[inspect(mut, with = "inspect_drives")]
1141 drives: [Option<DiskDrive>; 2],
1142 interrupt: LineInterrupt,
1143 state: ChannelState,
1144 bus_master_state: ChannelBusMasterState,
1145 enlightened_write: Option<EnlightenedWrite>,
1146 guest_memory: GuestMemory,
1147 #[inspect(skip)]
1148 channel: u8,
1149}
1150
1151fn inspect_drives(drives: &mut [Option<DiskDrive>]) -> impl '_ + InspectMut {
1152 inspect::adhoc_mut(|req| {
1153 let mut resp = req.respond();
1154 for (i, drive) in drives.iter_mut().enumerate() {
1155 resp.field_mut(&i.to_string(), drive);
1156 }
1157 })
1158}
1159
1160impl Channel {
1161 fn new(
1162 channel_drives: [Option<DriveMedia>; 2],
1163 channel_type: ChannelType,
1164 interrupt: LineInterrupt,
1165 guest_memory: GuestMemory,
1166 ) -> Result<Self, NewDeviceError> {
1167 let [primary_media, secondary_media] = channel_drives;
1168
1169 let channel_number = match channel_type {
1170 ChannelType::Primary => 0,
1171 ChannelType::Secondary => 1,
1172 };
1173
1174 Ok(Self {
1175 drives: [
1176 primary_media
1177 .map(|media| {
1178 DiskDrive::new(
1179 media,
1180 IdePath {
1181 channel: channel_number,
1182 drive: 0,
1183 },
1184 )
1185 })
1186 .transpose()?,
1187 secondary_media
1188 .map(|media| {
1189 DiskDrive::new(
1190 media,
1191 IdePath {
1192 channel: channel_number,
1193 drive: 1,
1194 },
1195 )
1196 })
1197 .transpose()?,
1198 ],
1199 interrupt,
1200 state: ChannelState::default(),
1201 bus_master_state: ChannelBusMasterState::default(),
1202 enlightened_write: None,
1203 guest_memory,
1204 channel: channel_number,
1205 })
1206 }
1207
1208 fn reset(&mut self) {
1209 tracelimit::info_ratelimited!(channel = self.channel, "channel reset");
1210 self.interrupt.set_level(false);
1211 self.state = ChannelState::default();
1212 self.bus_master_state = ChannelBusMasterState::default();
1213 for drive in self.drives.iter_mut().flatten() {
1214 drive.reset();
1215 }
1216 }
1217
1218 fn poll_device(&mut self, cx: &mut Context<'_>, bus_master_state: &BusMasterState) {
1219 for drive in self.drives.iter_mut().flatten() {
1220 drive.poll_device(cx);
1221 }
1222 self.post_drive_access(bus_master_state);
1223 }
1224
1225 fn current_drive_status(&mut self) -> Option<Status> {
1226 if let Some(drive) = &mut self.drives[self.state.current_drive_idx] {
1227 let status = drive.read_register(DriveRegister::AlternateStatusDeviceControl);
1228 Some(Status::from_bits(status))
1229 } else {
1230 None
1231 }
1232 }
1233
1234 fn drive_status(&mut self, drive_index: usize) -> Status {
1235 assert!(self.drives[drive_index].is_some());
1237 let status = self.drives[drive_index]
1238 .as_mut()
1239 .unwrap()
1240 .read_register(DriveRegister::AlternateStatusDeviceControl);
1241 Status::from_bits(status)
1242 }
1243
1244 fn current_drive_type(&self) -> Option<DriveType> {
1245 self.drives[self.state.current_drive_idx]
1246 .as_ref()
1247 .map(|drive| drive.drive_type())
1248 }
1249
1250 fn drive_type(&mut self, drive_index: usize) -> DriveType {
1251 assert!(self.drives[drive_index].is_some());
1252 self.drives[drive_index]
1253 .as_ref()
1254 .map(|drive| drive.drive_type())
1255 .unwrap()
1256 }
1257
1258 fn post_drive_access(&mut self, bus_master_state: &BusMasterState) {
1259 self.perform_dma_memory_phase();
1261
1262 if let Some(enlightened_write) = &self.enlightened_write {
1264 let drive_index = match enlightened_write {
1265 EnlightenedWrite::Hard(enlightened_hdd_write) => enlightened_hdd_write.drive_index,
1266 EnlightenedWrite::Optical(enlightened_cd_write) => enlightened_cd_write.drive_index,
1267 };
1268
1269 let status = self.drive_status(drive_index);
1270 let completed = match self.drive_type(drive_index) {
1271 DriveType::Hard => !(status.bsy() || status.drq()),
1272 DriveType::Optical => status.drdy(),
1273 };
1274 if completed {
1275 let write = self.enlightened_write.take().unwrap();
1277 match write {
1278 EnlightenedWrite::Hard(write) => {
1279 self.complete_enlightened_hdd_write(write, bus_master_state)
1280 }
1281 EnlightenedWrite::Optical(write) => {
1282 self.complete_enlightened_cd_write(write, bus_master_state)
1283 }
1284 }
1285 }
1286 }
1287
1288 let interrupt = self
1290 .drives
1291 .iter()
1292 .flatten()
1293 .any(|drive| drive.interrupt_pending());
1294 if interrupt {
1295 tracing::trace!(channel = self.channel, interrupt, "post_drive_access");
1296 self.bus_master_state.status_reg.set_interrupt(true);
1297 }
1298 self.interrupt.set_level(interrupt);
1299 }
1300
1301 fn read_drive_register(
1304 &mut self,
1305 port: DriveRegister,
1306 bus_master_state: &BusMasterState,
1307 ) -> u8 {
1308 let mut drive = self.drives[self.state.current_drive_idx].as_mut();
1310 if drive.is_none() {
1311 drive = self.drives[0].as_mut();
1312 }
1313
1314 let data = if let Some(drive) = drive {
1315 drive.read_register(port)
1318 } else {
1319 0x7f
1322 };
1323
1324 tracing::trace!(?port, ?data, channel = self.channel, "io port read");
1325 self.post_drive_access(bus_master_state);
1326 data
1327 }
1328
1329 fn write_drive_register(
1333 &mut self,
1334 port: DriveRegister,
1335 data: u8,
1336 bus_master_state: &BusMasterState,
1337 ) {
1338 tracing::trace!(?port, ?data, channel = self.channel, "io port write");
1339
1340 match port {
1341 DriveRegister::DeviceHead => {
1342 self.state.current_drive_idx = ((data >> 4) & 1) as usize;
1344 }
1345 DriveRegister::AlternateStatusDeviceControl => {
1346 self.state.shadow_adapter_control_reg = data;
1348 let v = DeviceControlReg::from_bits_truncate(data);
1349 if v.reset() && (self.drives[0].is_some() || self.drives[1].is_some()) {
1350 self.state = ChannelState::default();
1351 }
1352 }
1353 DriveRegister::ErrorFeatures => {
1354 self.state.shadow_features_reg = data;
1356 }
1357 _ => {}
1358 }
1359
1360 if let Some(drive) = &mut self.drives[1] {
1362 drive.write_register(port, data);
1363 }
1364 if let Some(drive) = &mut self.drives[0] {
1365 drive.write_register(port, data);
1366 }
1367
1368 self.post_drive_access(bus_master_state);
1369 }
1370
1371 fn read_drive_data(&mut self, data: &mut [u8], bus_master_state: &BusMasterState) {
1372 let mut drive = self.drives[self.state.current_drive_idx].as_mut();
1374 if drive.is_none() {
1375 drive = self.drives[0].as_mut();
1376 }
1377
1378 data.fill(0xff);
1379 data[0] = 0x7f;
1381
1382 if let Some(drive) = drive {
1383 drive.pio_read(data);
1384 };
1385
1386 self.post_drive_access(bus_master_state);
1387 }
1388
1389 fn skip_drive_data(&mut self, mut len: usize, bus_master_state: &BusMasterState) {
1390 let mut buf = [0; 512];
1391 while len > 0 {
1392 let this_len = len.min(buf.len());
1393 let buf = &mut buf[..this_len];
1394 self.read_drive_data(buf, bus_master_state);
1395 len -= buf.len();
1396 }
1397 }
1398
1399 fn write_drive_data(&mut self, data: &[u8], bus_master_state: &BusMasterState) {
1400 if let Some(drive) = &mut self.drives[0] {
1401 drive.pio_write(data);
1402 }
1403 if let Some(drive) = &mut self.drives[1] {
1404 drive.pio_write(data);
1405 }
1406 self.post_drive_access(bus_master_state);
1407 }
1408
1409 fn read_bus_master_reg(&mut self, bus_master_reg: BusMasterReg, data: &mut [u8]) -> IoResult {
1410 let data_len = data.len();
1411 match bus_master_reg {
1412 BusMasterReg::COMMAND => match data_len {
1413 1 | 2 => data.copy_from_slice(
1414 &self.bus_master_state.command_reg.into_bits().to_ne_bytes()[..data_len],
1415 ),
1416 _ => return IoResult::Err(IoError::InvalidAccessSize),
1417 },
1418 BusMasterReg::STATUS => {
1419 let mut status = self.bus_master_state.status_reg;
1420
1421 if self.bus_master_state.dma_state.is_some() {
1422 status.set_active(true);
1423 }
1424
1425 match data_len {
1426 1 | 2 => data.copy_from_slice(&status.into_bits().to_ne_bytes()[..data_len]),
1427 _ => return IoResult::Err(IoError::InvalidAccessSize),
1428 }
1429 }
1430 BusMasterReg::TABLE_PTR => match data_len {
1431 2 | 4 => data.copy_from_slice(
1432 &self.bus_master_state.desc_table_ptr.to_ne_bytes()[..data_len],
1433 ),
1434 _ => return IoResult::Err(IoError::InvalidAccessSize),
1435 },
1436 BusMasterReg::TABLE_PTR2 => {
1437 if data_len == 2 {
1438 data.copy_from_slice(&self.bus_master_state.desc_table_ptr.to_ne_bytes()[2..4]);
1439 } else {
1440 return IoResult::Err(IoError::InvalidAccessSize);
1441 }
1442 }
1443 _ => return IoResult::Err(IoError::InvalidRegister),
1444 }
1445
1446 tracing::trace!(?bus_master_reg, ?data, "bus master register read");
1447 IoResult::Ok
1448 }
1449
1450 fn write_bus_master_reg(
1451 &mut self,
1452 bus_master_reg: BusMasterReg,
1453 data: &[u8],
1454 bus_master_state: &BusMasterState,
1455 ) -> IoResult {
1456 let value: u64 = match data.len() {
1457 1 => u8::from_ne_bytes(data.as_bytes().try_into().unwrap()).into(),
1458 2 => u16::from_ne_bytes(data.as_bytes().try_into().unwrap()).into(),
1459 4 => u32::from_ne_bytes(data.as_bytes().try_into().unwrap()).into(),
1460 _ => return IoResult::Err(IoError::InvalidAccessSize),
1461 };
1462
1463 tracing::trace!(?bus_master_reg, value, "bus master register write");
1464
1465 match bus_master_reg {
1466 BusMasterReg::COMMAND => {
1467 if data.len() > 2 {
1470 return IoResult::Err(IoError::InvalidAccessSize);
1471 }
1472
1473 let old_value = self.bus_master_state.command_reg;
1474 let mut new_value = BusMasterCommandReg::from_bits_truncate(value as u32);
1476
1477 if old_value.start() {
1479 new_value.set_write(old_value.write());
1482 if !new_value.start() {
1483 self.bus_master_state.dma_state = None
1484 }
1485 } else if new_value.start() {
1486 self.bus_master_state.dma_state = Some(Default::default());
1487 };
1488
1489 self.bus_master_state.command_reg = new_value;
1490 }
1491 BusMasterReg::STATUS => {
1492 if data.len() > 2 {
1493 return IoResult::Err(IoError::InvalidAccessSize);
1494 }
1495
1496 let value = BusMasterStatusReg::from_bits_truncate(value as u32);
1497 let old_value = self.bus_master_state.status_reg;
1498 let mut new_value = old_value.with_settable(value.settable());
1499
1500 if value.interrupt() {
1502 new_value.set_interrupt(false);
1503 }
1504 if value.dma_error() {
1505 new_value.set_dma_error(false);
1506 }
1507
1508 tracing::trace!(?old_value, ?new_value, "set bus master status");
1509 self.bus_master_state.status_reg = new_value;
1510 }
1511 BusMasterReg::TABLE_PTR => {
1512 if data.len() < 2 {
1513 return IoResult::Err(IoError::InvalidAccessSize);
1514 }
1515
1516 if data.len() == 4 {
1517 self.bus_master_state.desc_table_ptr = value as u32 & 0xffff_fffc;
1519 } else {
1520 self.bus_master_state.desc_table_ptr = (self.bus_master_state.desc_table_ptr
1523 & 0xffff_0000)
1524 | (value as u32 & 0x0000_fffc);
1525 }
1526 }
1527 BusMasterReg::TABLE_PTR2 => {
1528 self.bus_master_state.desc_table_ptr = (self.bus_master_state.desc_table_ptr
1531 & 0xffff)
1532 | ((value as u32 & 0xffff) << 16);
1533 }
1534 _ => return IoResult::Err(IoError::InvalidRegister),
1535 }
1536
1537 self.post_drive_access(bus_master_state);
1538 IoResult::Ok
1539 }
1540}
1541
1542#[derive(Debug, Copy, Clone, PartialEq)]
1543enum DriveType {
1544 Hard,
1545 Optical,
1546}
1547
1548#[derive(Debug, Default, Inspect)]
1549struct DmaState {
1550 descriptor_idx: u8,
1551 transfer_complete: bool,
1552 transfer_bytes_left: u32,
1553 transfer_base_addr: u64,
1554}
1555
1556mod save_restore {
1557 use super::*;
1558 use vmcore::save_restore::RestoreError;
1559 use vmcore::save_restore::SaveError;
1560 use vmcore::save_restore::SaveRestore;
1561
1562 mod state {
1563 use crate::drive::save_restore::state::SavedDriveState;
1564 use mesh::payload::Protobuf;
1565 use vmcore::save_restore::SavedStateRoot;
1566
1567 #[derive(Protobuf)]
1568 #[mesh(package = "storage.ide.controller")]
1569 pub struct SavedBusMasterState {
1570 #[mesh(1)]
1571 pub cmd_status_reg: u32,
1572 #[mesh(2)]
1573 pub port_addr_reg: u32,
1574 #[mesh(3)]
1575 pub timing_reg: u32,
1576 #[mesh(4)]
1577 pub secondary_timing_reg: u32,
1578 #[mesh(5)]
1579 pub dma_ctl_reg: u32,
1580 }
1581
1582 #[derive(Protobuf, SavedStateRoot)]
1583 #[mesh(package = "storage.ide.controller")]
1584 pub struct SavedState {
1585 #[mesh(1)]
1586 pub bus_master: SavedBusMasterState,
1587 #[mesh(2)]
1588 pub channel0: SavedChannelState,
1589 #[mesh(3)]
1590 pub channel1: SavedChannelState,
1591 }
1592
1593 #[derive(Protobuf)]
1594 #[mesh(package = "storage.ide.controller")]
1595 pub struct SavedDmaState {
1596 #[mesh(1)]
1597 pub descriptor_idx: u8,
1598 #[mesh(2)]
1599 pub transfer_complete: bool,
1600 #[mesh(3)]
1601 pub transfer_bytes_left: u32,
1602 #[mesh(4)]
1603 pub transfer_base_addr: u64,
1604 }
1605
1606 #[derive(Protobuf)]
1607 #[mesh(package = "storage.ide.controller")]
1608 pub struct SavedChannelBusMasterState {
1609 #[mesh(1)]
1610 pub command_reg: u32,
1611 #[mesh(2)]
1612 pub status_reg: u32,
1613 #[mesh(3)]
1614 pub desc_table_ptr: u32,
1615 #[mesh(4)]
1616 pub dma_state: Option<SavedDmaState>,
1617 #[mesh(5)]
1618 pub dma_error: bool,
1619 }
1620
1621 #[derive(Protobuf)]
1622 #[mesh(package = "storage.ide.controller")]
1623 pub struct SavedChannelState {
1624 #[mesh(1)]
1625 pub current_drive_idx: u8,
1626 #[mesh(2)]
1627 pub shadow_adapter_control_reg: u8,
1628 #[mesh(3)]
1629 pub shadow_features_reg: u8,
1630 #[mesh(4)]
1631 pub bus_master: SavedChannelBusMasterState,
1632 #[mesh(5)]
1633 pub drive0: Option<SavedDriveState>,
1634 #[mesh(6)]
1635 pub drive1: Option<SavedDriveState>,
1636 }
1637 }
1638
1639 impl SaveRestore for IdeDevice {
1640 type SavedState = state::SavedState;
1641
1642 fn save(&mut self) -> Result<Self::SavedState, SaveError> {
1643 let BusMasterState {
1644 cmd_status_reg,
1645 port_addr_reg,
1646 timing_reg,
1647 secondary_timing_reg,
1648 dma_ctl_reg,
1649 } = self.bus_master_state;
1650
1651 let bus_master = state::SavedBusMasterState {
1652 cmd_status_reg,
1653 port_addr_reg,
1654 timing_reg,
1655 secondary_timing_reg,
1656 dma_ctl_reg,
1657 };
1658
1659 let saved_state = state::SavedState {
1660 bus_master,
1661 channel0: self.channels[0].save()?,
1662 channel1: self.channels[1].save()?,
1663 };
1664
1665 Ok(saved_state)
1666 }
1667
1668 fn restore(&mut self, state: Self::SavedState) -> Result<(), RestoreError> {
1669 let state::SavedState {
1670 bus_master:
1671 state::SavedBusMasterState {
1672 cmd_status_reg,
1673 port_addr_reg,
1674 timing_reg,
1675 secondary_timing_reg,
1676 dma_ctl_reg,
1677 },
1678 channel0,
1679 channel1,
1680 } = state;
1681
1682 self.bus_master_state = BusMasterState {
1683 cmd_status_reg,
1684 port_addr_reg,
1685 timing_reg,
1686 secondary_timing_reg,
1687 dma_ctl_reg,
1688 };
1689
1690 self.channels[0].restore(channel0)?;
1691 self.channels[1].restore(channel1)?;
1692
1693 Ok(())
1694 }
1695 }
1696
1697 #[derive(Debug, Error)]
1698 enum ChannelRestoreError {
1699 #[error("missing drive for state")]
1700 MissingDriveForState,
1701 #[error("missing state for drive")]
1702 MissingStateForDrive,
1703 }
1704
1705 impl Channel {
1706 fn save(&mut self) -> Result<state::SavedChannelState, SaveError> {
1707 assert!(self.enlightened_write.is_none());
1709
1710 let ChannelState {
1711 current_drive_idx,
1712 shadow_adapter_control_reg,
1713 shadow_features_reg,
1714 } = self.state;
1715
1716 let ChannelBusMasterState {
1717 command_reg,
1718 status_reg,
1719 desc_table_ptr,
1720 dma_state,
1721 dma_error,
1722 } = &self.bus_master_state;
1723
1724 let saved_state = state::SavedChannelState {
1725 current_drive_idx: current_drive_idx as u8,
1726 shadow_adapter_control_reg,
1727 shadow_features_reg,
1728 bus_master: state::SavedChannelBusMasterState {
1729 command_reg: command_reg.into_bits(),
1730 status_reg: status_reg.into_bits(),
1731 desc_table_ptr: *desc_table_ptr,
1732 dma_state: dma_state.as_ref().map(|dma| {
1733 let DmaState {
1734 descriptor_idx,
1735 transfer_complete,
1736 transfer_bytes_left,
1737 transfer_base_addr,
1738 } = dma;
1739
1740 state::SavedDmaState {
1741 descriptor_idx: *descriptor_idx,
1742 transfer_complete: *transfer_complete,
1743 transfer_bytes_left: *transfer_bytes_left,
1744 transfer_base_addr: *transfer_base_addr,
1745 }
1746 }),
1747 dma_error: *dma_error,
1748 },
1749 drive0: self.drives[0]
1750 .as_mut()
1751 .map(|drive| drive.save())
1752 .transpose()?,
1753 drive1: self.drives[1]
1754 .as_mut()
1755 .map(|drive| drive.save())
1756 .transpose()?,
1757 };
1758
1759 Ok(saved_state)
1760 }
1761
1762 fn restore(&mut self, state: state::SavedChannelState) -> Result<(), RestoreError> {
1763 let state::SavedChannelState {
1764 current_drive_idx,
1765 shadow_adapter_control_reg,
1766 shadow_features_reg,
1767 bus_master:
1768 state::SavedChannelBusMasterState {
1769 command_reg,
1770 status_reg,
1771 desc_table_ptr,
1772 dma_state,
1773 dma_error,
1774 },
1775 drive0,
1776 drive1,
1777 } = state;
1778
1779 self.state = ChannelState {
1780 current_drive_idx: current_drive_idx as usize,
1781 shadow_adapter_control_reg,
1782 shadow_features_reg,
1783 };
1784
1785 self.bus_master_state = ChannelBusMasterState {
1786 command_reg: BusMasterCommandReg::from_bits(command_reg),
1787 status_reg: BusMasterStatusReg::from_bits(status_reg),
1788 desc_table_ptr,
1789 dma_state: dma_state.map(|dma| {
1790 let state::SavedDmaState {
1791 descriptor_idx,
1792 transfer_complete,
1793 transfer_bytes_left,
1794 transfer_base_addr,
1795 } = dma;
1796
1797 DmaState {
1798 descriptor_idx,
1799 transfer_complete,
1800 transfer_bytes_left,
1801 transfer_base_addr,
1802 }
1803 }),
1804 dma_error,
1805 };
1806
1807 for (drive, state) in self.drives.iter_mut().zip([drive0, drive1]) {
1808 match (drive, state) {
1809 (Some(drive), Some(state)) => drive.restore(state)?,
1810 (None, None) => {}
1811 (Some(_), None) => {
1812 return Err(RestoreError::InvalidSavedState(
1813 ChannelRestoreError::MissingStateForDrive.into(),
1814 ));
1815 }
1816 (None, Some(_)) => {
1817 return Err(RestoreError::InvalidSavedState(
1818 ChannelRestoreError::MissingDriveForState.into(),
1819 ));
1820 }
1821 }
1822 }
1823
1824 Ok(())
1825 }
1826 }
1827}
1828
1829#[cfg(test)]
1830mod tests {
1831 use super::*;
1832 use crate::IdeIoPort;
1833 use crate::protocol::BusMasterDmaDesc;
1834 use crate::protocol::DeviceHeadReg;
1835 use crate::protocol::IdeCommand;
1836 use chipset_device::pio::ExternallyManagedPortIoIntercepts;
1837 use disk_file::FileDisk;
1838 use pal_async::async_test;
1839 use scsidisk::atapi_scsi::AtapiScsiDisk;
1840 use scsidisk::scsidvd::SimpleScsiDvd;
1841 use std::fs::File;
1842 use std::future::poll_fn;
1843 use std::io::Read;
1844 use std::io::Write;
1845 use std::task::Poll;
1846 use tempfile::NamedTempFile;
1847 use test_with_tracing::test;
1848 use zerocopy::FromBytes;
1849 use zerocopy::FromZeros;
1850 use zerocopy::IntoBytes;
1851
1852 #[derive(Debug, Inspect)]
1853 struct MediaGeometry {
1854 sectors_per_track: u32,
1855 cylinder_count: u32,
1856 head_count: u32,
1857 total_sectors: u64,
1858 }
1859
1860 impl MediaGeometry {
1861 fn new(total_sectors: u64, sector_size: u32) -> Result<Self, NewDeviceError> {
1862 if total_sectors > protocol::MAX_BYTES_48BIT_LBA / sector_size as u64 {
1863 return Err(NewDeviceError::DiskTooLarge(
1864 total_sectors * sector_size as u64,
1865 ));
1866 }
1867 let hard_drive_sectors = total_sectors.min(protocol::MAX_CHS_SECTORS as u64);
1868 let mut sectors_per_track;
1869 let mut cylinders_times_heads;
1870 let mut head_count;
1871
1872 if hard_drive_sectors > (16 * 63 * 0xFFFF) {
1873 sectors_per_track = 255;
1874 head_count = 16;
1875 cylinders_times_heads = hard_drive_sectors / (sectors_per_track as u64);
1876 } else {
1877 sectors_per_track = 17;
1878 cylinders_times_heads = hard_drive_sectors / (sectors_per_track as u64);
1879
1880 head_count = std::cmp::max((cylinders_times_heads as u32).div_ceil(1024), 4);
1881
1882 if (cylinders_times_heads >= (head_count as u64) * 1024) || head_count > 16 {
1883 head_count = 16;
1885 sectors_per_track = 31;
1886 cylinders_times_heads = hard_drive_sectors / (sectors_per_track as u64);
1887 }
1888
1889 if cylinders_times_heads >= (head_count as u64) * 1024 {
1890 head_count = 16;
1892 sectors_per_track = 63;
1893 cylinders_times_heads = hard_drive_sectors / (sectors_per_track as u64);
1894 }
1895 }
1896 Ok(MediaGeometry {
1897 sectors_per_track,
1898 cylinder_count: (cylinders_times_heads / (head_count as u64)) as u32,
1899 head_count,
1900 total_sectors,
1901 })
1902 }
1903 }
1904
1905 struct CommandParams {
1906 sector_count: u8,
1907 sector_num: u8,
1908 cylinder_lsb: u8,
1909 cylinder_msb: u8,
1910 device_head: u8,
1911 }
1912
1913 #[expect(dead_code)]
1914 enum Addressing {
1915 Chs,
1916 Lba28Bit,
1917 Lba48Bit,
1918 }
1919
1920 fn ide_test_setup(
1921 guest_memory: Option<GuestMemory>,
1922 drive_type: DriveType,
1923 ) -> (IdeDevice, File, Vec<u32>, MediaGeometry) {
1924 let test_guest_mem = match guest_memory {
1925 Some(test_gm) => test_gm,
1926 None => GuestMemory::allocate(16 * 1024),
1927 };
1928
1929 let temp_file = NamedTempFile::new().unwrap();
1931 let mut handle1 = temp_file.reopen().unwrap();
1932 let handle2 = temp_file.reopen().unwrap();
1933 let data = (0..0x100000_u32).collect::<Vec<_>>();
1934 handle1.write_all(data.as_bytes()).unwrap();
1935
1936 let disk = Disk::new(FileDisk::open(handle1, false).unwrap()).unwrap();
1937 let geometry = MediaGeometry::new(disk.sector_count(), disk.sector_size()).unwrap();
1938
1939 let media = match drive_type {
1940 DriveType::Hard => DriveMedia::hard_disk(disk),
1941 DriveType::Optical => DriveMedia::optical_disk(Arc::new(AtapiScsiDisk::new(Arc::new(
1942 SimpleScsiDvd::new(Some(disk)),
1943 )))),
1944 };
1945
1946 let ide_device = IdeDevice::new(
1947 test_guest_mem,
1948 &mut ExternallyManagedPortIoIntercepts,
1949 [Some(media), None],
1950 [None, None],
1951 LineInterrupt::detached(),
1952 LineInterrupt::detached(),
1953 )
1954 .unwrap();
1955
1956 (ide_device, handle2, data, geometry)
1957 }
1958
1959 fn get_status(ide_controller: &mut IdeDevice, dev_path: &IdePath) -> Status {
1961 let mut data = [0_u8; 1];
1962 ide_controller
1963 .io_read(
1964 io_port(IdeIoPort::PRI_STATUS_CMD, dev_path.channel.into()),
1965 &mut data,
1966 )
1967 .unwrap();
1968
1969 Status::from_bits(data[0])
1970 }
1971
1972 async fn check_status_loop(ide_device: &mut IdeDevice, dev_path: &IdePath) -> Status {
1973 wait_for(ide_device, |ide_device| {
1975 let status: Status = get_status(ide_device, dev_path);
1976 (!status.bsy() && !status.drq()).then_some(status)
1977 })
1978 .await
1979 }
1980
1981 async fn check_command_ready(ide_device: &mut IdeDevice, dev_path: &IdePath) -> Status {
1982 wait_for(ide_device, |ide_device| {
1984 let status: Status = get_status(ide_device, dev_path);
1985 (!status.bsy() && status.drdy()).then_some(status)
1986 })
1987 .await
1988 }
1989
1990 fn io_port(io_port: IdeIoPort, channel_idx: usize) -> u16 {
1991 if channel_idx == 0 {
1992 io_port.0
1993 } else {
1994 io_port.0 - IdeIoPort::PRI_DATA.0 + IdeIoPort::SEC_DATA.0
1995 }
1996 }
1997
1998 fn write_command_params(
2001 controller: &mut IdeDevice,
2002 dev_path: &IdePath,
2003 sector: u32,
2004 sector_count: u8,
2005 addr: Addressing,
2006 geometry: &MediaGeometry,
2007 ) {
2008 let channel_idx: usize = dev_path.channel as usize;
2009
2010 let io_params = match addr {
2011 Addressing::Chs => {
2012 let sectors_per_track = geometry.sectors_per_track;
2013 let head_count = geometry.head_count;
2014
2015 let sector_num: u8 = ((sector % sectors_per_track) as u8) + 1;
2016 let cylinders: u16 = (sector / (head_count * sectors_per_track)) as u16;
2017 let cylinder_lsb: u8 = cylinders as u8;
2018 let cylinder_msb: u8 = (cylinders >> 8) as u8;
2019 let device_head: u8 = (sector / sectors_per_track % head_count) as u8;
2020
2021 CommandParams {
2022 sector_count,
2023 sector_num,
2024 cylinder_lsb,
2025 cylinder_msb,
2026 device_head,
2027 }
2028 }
2029 Addressing::Lba28Bit => {
2030 let sector_num = sector as u8;
2031 let cylinder = (sector & 0x00FF_FF00) >> 8;
2032 let cylinder_lsb: u8 = cylinder as u8;
2033 let cylinder_msb: u8 = (cylinder >> 8) as u8;
2034 let device_head = DeviceHeadReg::new()
2035 .with_head((sector >> 24) as u8)
2036 .with_lba(true)
2037 .into();
2038
2039 CommandParams {
2040 sector_count,
2041 sector_num,
2042 cylinder_lsb,
2043 cylinder_msb,
2044 device_head,
2045 }
2046 }
2047 Addressing::Lba48Bit => todo!(),
2048 };
2049
2050 controller
2051 .io_write(
2052 io_port(IdeIoPort::PRI_SECTOR_COUNT, channel_idx),
2053 &[io_params.sector_count],
2054 )
2055 .unwrap();
2056 controller
2057 .io_write(
2058 io_port(IdeIoPort::PRI_SECTOR_NUM, channel_idx),
2059 &[io_params.sector_num],
2060 )
2061 .unwrap();
2062 controller
2063 .io_write(
2064 io_port(IdeIoPort::PRI_CYLINDER_LSB, channel_idx),
2065 &[io_params.cylinder_lsb],
2066 )
2067 .unwrap();
2068 controller
2069 .io_write(
2070 io_port(IdeIoPort::PRI_CYLINDER_MSB, channel_idx),
2071 &[io_params.cylinder_msb],
2072 )
2073 .unwrap();
2074 controller
2075 .io_write(
2076 io_port(IdeIoPort::PRI_DEVICE_HEAD, channel_idx),
2077 &[io_params.device_head],
2078 )
2079 .unwrap();
2080 }
2081
2082 async fn device_select(ide_controller: &mut IdeDevice, dev_path: &IdePath) {
2084 check_status_loop(ide_controller, dev_path).await;
2085
2086 let dev_idx: u8 = dev_path.drive;
2087 ide_controller
2088 .io_write(
2089 io_port(IdeIoPort::PRI_DEVICE_HEAD, dev_path.channel.into()),
2090 &[dev_idx],
2091 )
2092 .unwrap();
2093
2094 check_status_loop(ide_controller, dev_path).await;
2095 }
2096
2097 fn execute_command(ide_controller: &mut IdeDevice, dev_path: &IdePath, command: u8) {
2099 ide_controller
2100 .io_write(
2101 io_port(IdeIoPort::PRI_STATUS_CMD, dev_path.channel.into()),
2102 &[command],
2103 )
2104 .unwrap();
2105 }
2106
2107 fn execute_soft_reset_command(ide_controller: &mut IdeDevice, dev_path: &IdePath, command: u8) {
2108 ide_controller
2109 .io_write(
2110 io_port(
2111 IdeIoPort::PRI_ALT_STATUS_DEVICE_CTL,
2112 dev_path.channel.into(),
2113 ),
2114 &[command],
2115 )
2116 .unwrap();
2117 }
2118
2119 fn get_dma_state(ide_controller: &mut IdeDevice, dev_path: &IdePath) -> bool {
2120 ide_controller.channels[dev_path.channel as usize]
2122 .bus_master_state
2123 .dma_state
2124 .is_some()
2125 }
2126
2127 fn prep_ide_channel(ide_controller: &mut IdeDevice, drive_type: DriveType, dev_path: &IdePath) {
2128 match drive_type {
2129 DriveType::Hard => {
2130 execute_command(ide_controller, dev_path, IdeCommand::SET_MULTI_BLOCK_MODE.0);
2133 }
2134 DriveType::Optical => {
2135 }
2137 }
2138 }
2139
2140 async fn wait_for<T>(
2142 ide_device: &mut IdeDevice,
2143 mut f: impl FnMut(&mut IdeDevice) -> Option<T>,
2144 ) -> T {
2145 poll_fn(|cx| {
2146 ide_device.poll_device(cx);
2147 let r = f(ide_device);
2148 if let Some(r) = r {
2149 Poll::Ready(r)
2150 } else {
2151 Poll::Pending
2152 }
2153 })
2154 .await
2155 }
2156
2157 #[async_test]
2160 async fn write_sectors_test() {
2161 const START_SECTOR: u32 = 0;
2162 const SECTOR_COUNT: u8 = 4;
2163
2164 let dev_path = IdePath::default();
2165 let (mut ide_device, mut disk, _file_contents, geometry) =
2166 ide_test_setup(None, DriveType::Hard);
2167
2168 device_select(&mut ide_device, &dev_path).await;
2170 prep_ide_channel(&mut ide_device, DriveType::Hard, &dev_path);
2171
2172 write_command_params(
2174 &mut ide_device,
2175 &dev_path,
2176 START_SECTOR,
2177 SECTOR_COUNT,
2178 Addressing::Lba28Bit,
2179 &geometry,
2180 );
2181
2182 execute_command(&mut ide_device, &dev_path, IdeCommand::WRITE_SECTORS.0);
2183
2184 let status = get_status(&mut ide_device, &dev_path);
2186 assert!(status.drq() && !status.bsy());
2187
2188 let data = &[0xFF_u8; 2][..];
2190 for _ in 0..SECTOR_COUNT {
2191 let status = check_command_ready(&mut ide_device, &dev_path).await;
2192 assert!(status.drq());
2193 assert!(!status.err());
2194 for _ in 0..protocol::HARD_DRIVE_SECTOR_BYTES / 2 {
2195 ide_device.io_write(IdeIoPort::PRI_DATA.0, data).unwrap();
2196 }
2197 }
2198
2199 let status = check_command_ready(&mut ide_device, &dev_path).await;
2200 assert!(!status.err());
2201 assert!(!status.drq());
2202
2203 let buffer =
2204 &mut [0_u8; (protocol::HARD_DRIVE_SECTOR_BYTES * SECTOR_COUNT as u32) as usize][..];
2205 disk.read_exact(buffer).unwrap();
2206 for byte in buffer {
2207 assert_eq!(*byte, 0xFF);
2208 }
2209 }
2210
2211 #[async_test]
2212 async fn software_reset_test() {
2213 const START_SECTOR: u32 = 0;
2214 const SECTOR_COUNT: u8 = 4;
2215
2216 let dev_path = IdePath::default();
2217 let (mut ide_device, _disk, _file_contents, geometry) =
2218 ide_test_setup(None, DriveType::Hard);
2219
2220 device_select(&mut ide_device, &dev_path).await;
2222 prep_ide_channel(&mut ide_device, DriveType::Hard, &dev_path);
2223
2224 write_command_params(
2226 &mut ide_device,
2227 &dev_path,
2228 START_SECTOR,
2229 SECTOR_COUNT,
2230 Addressing::Lba28Bit,
2231 &geometry,
2232 );
2233
2234 execute_command(&mut ide_device, &dev_path, IdeCommand::WRITE_SECTORS.0);
2235 let status = get_status(&mut ide_device, &dev_path);
2237 assert!(status.drq() && !status.bsy());
2238
2239 execute_soft_reset_command(&mut ide_device, &dev_path, IdeCommand::SOFT_RESET.0);
2240 let status = get_status(&mut ide_device, &dev_path);
2241 assert!(status.bsy());
2242 }
2243
2244 #[async_test]
2246 async fn read_sectors_test() {
2247 const START_SECTOR: u32 = 0;
2248 const SECTOR_COUNT: u8 = 4;
2249
2250 let dev_path = IdePath::default();
2251 let (mut ide_device, _disk, file_contents, geometry) =
2252 ide_test_setup(None, DriveType::Hard);
2253
2254 device_select(&mut ide_device, &dev_path).await;
2256 prep_ide_channel(&mut ide_device, DriveType::Hard, &dev_path);
2257
2258 write_command_params(
2260 &mut ide_device,
2261 &dev_path,
2262 START_SECTOR,
2263 SECTOR_COUNT,
2264 Addressing::Lba28Bit,
2265 &geometry,
2266 );
2267
2268 execute_command(&mut ide_device, &dev_path, IdeCommand::READ_SECTORS.0);
2270
2271 let status = check_command_ready(&mut ide_device, &dev_path).await;
2272 assert!(status.drq());
2273 assert!(!status.err());
2274
2275 let content_bytes = file_contents.as_bytes();
2277 for sector in 0..SECTOR_COUNT {
2278 let status = check_command_ready(&mut ide_device, &dev_path).await;
2279 assert!(status.drq());
2280 assert!(!status.err());
2281 for word in 0..protocol::HARD_DRIVE_SECTOR_BYTES / 2 {
2282 let data = &mut [0, 0][..];
2283 ide_device.io_read(IdeIoPort::PRI_DATA.0, data).unwrap();
2284
2285 let i = sector as usize * protocol::HARD_DRIVE_SECTOR_BYTES as usize / 2
2286 + word as usize;
2287 assert_eq!(data[0], content_bytes[i * 2]);
2288 assert_eq!(data[1], content_bytes[i * 2 + 1]);
2289 }
2290 }
2291 }
2292
2293 async fn enlightened_cmd_test(drive_type: DriveType) {
2295 const SECTOR_COUNT: u16 = 4;
2296 const BYTE_COUNT: u16 = SECTOR_COUNT * protocol::HARD_DRIVE_SECTOR_BYTES as u16;
2297
2298 let test_guest_mem = GuestMemory::allocate(16384);
2299
2300 let table_gpa = 0x1000;
2301 let data_gpa = 0x2000;
2302 test_guest_mem
2303 .write_plain(
2304 table_gpa,
2305 &BusMasterDmaDesc {
2306 mem_physical_base: data_gpa,
2307 byte_count: BYTE_COUNT,
2308 unused: 0,
2309 end_of_table: 0x80,
2310 },
2311 )
2312 .unwrap();
2313
2314 let (data_buffer, byte_count) = match drive_type {
2315 DriveType::Hard => (table_gpa as u32, 0),
2316 DriveType::Optical => (data_gpa, BYTE_COUNT.into()),
2317 };
2318
2319 let eint13_command = protocol::EnlightenedInt13Command {
2320 command: IdeCommand::READ_DMA_EXT,
2321 device_head: DeviceHeadReg::new().with_lba(true),
2322 flags: 0,
2323 result_status: 0,
2324 lba_low: 0,
2325 lba_high: 0,
2326 block_count: SECTOR_COUNT,
2327 byte_count,
2328 data_buffer,
2329 skip_bytes_head: 0,
2330 skip_bytes_tail: 0,
2331 };
2332 test_guest_mem.write_plain(0, &eint13_command).unwrap();
2333
2334 let dev_path = IdePath::default();
2335 let (mut ide_device, _disk, file_contents, _geometry) =
2336 ide_test_setup(Some(test_guest_mem.clone()), drive_type);
2337
2338 device_select(&mut ide_device, &dev_path).await;
2340 prep_ide_channel(&mut ide_device, drive_type, &dev_path);
2341
2342 let r = ide_device.io_write(IdeIoPort::PRI_ENLIGHTENED.0, 0_u32.as_bytes()); match r {
2346 IoResult::Defer(mut deferred) => {
2347 poll_fn(|cx| {
2348 ide_device.poll_device(cx);
2349 deferred.poll_write(cx)
2350 })
2351 .await
2352 .unwrap();
2353 }
2354 _ => panic!("{:?}", r),
2355 }
2356
2357 let mut buffer = vec![0u8; BYTE_COUNT as usize];
2358 test_guest_mem
2359 .read_at(data_gpa.into(), &mut buffer)
2360 .unwrap();
2361 assert_eq!(buffer, file_contents.as_bytes()[..buffer.len()]);
2362 }
2363
2364 #[async_test]
2366 async fn enlightened_cd_cmd_test() {
2367 enlightened_cmd_test(DriveType::Optical).await
2368 }
2369
2370 #[async_test]
2371 async fn enlightened_hdd_cmd_test() {
2372 enlightened_cmd_test(DriveType::Hard).await
2373 }
2374
2375 #[async_test]
2378 async fn enlightened_cmd_test_incomplete_prd() {
2379 const SECTOR_COUNT: u16 = 8;
2380 const BYTE_COUNT: u16 = SECTOR_COUNT * protocol::HARD_DRIVE_SECTOR_BYTES as u16;
2381
2382 let test_guest_mem = GuestMemory::allocate(16384);
2383
2384 let table_gpa = 0x1000;
2385 let data_gpa = 0x2000;
2386 test_guest_mem
2387 .write_plain(
2388 table_gpa,
2389 &BusMasterDmaDesc {
2390 mem_physical_base: data_gpa,
2391 byte_count: BYTE_COUNT / 2,
2392 unused: 0,
2393 end_of_table: 0x80, },
2395 )
2396 .unwrap();
2397 test_guest_mem
2398 .write_plain(
2399 table_gpa + size_of::<BusMasterDmaDesc>() as u64,
2400 &BusMasterDmaDesc {
2401 mem_physical_base: data_gpa,
2402 byte_count: BYTE_COUNT / 2,
2403 unused: 0,
2404 end_of_table: 0x80,
2405 },
2406 )
2407 .unwrap();
2408
2409 let data_buffer = table_gpa as u32;
2410 let byte_count = 0;
2411
2412 let eint13_command = protocol::EnlightenedInt13Command {
2413 command: IdeCommand::READ_DMA_EXT,
2414 device_head: DeviceHeadReg::new().with_lba(true),
2415 flags: 0,
2416 result_status: 0,
2417 lba_low: 0,
2418 lba_high: 0,
2419 block_count: SECTOR_COUNT,
2420 byte_count,
2421 data_buffer,
2422 skip_bytes_head: 0,
2423 skip_bytes_tail: 0,
2424 };
2425 test_guest_mem.write_plain(0, &eint13_command).unwrap();
2426
2427 let dev_path = IdePath::default();
2428 let (mut ide_device, _disk, file_contents, _geometry) =
2429 ide_test_setup(Some(test_guest_mem.clone()), DriveType::Hard);
2430
2431 device_select(&mut ide_device, &dev_path).await;
2433 prep_ide_channel(&mut ide_device, DriveType::Hard, &dev_path);
2434
2435 let r = ide_device.io_write(IdeIoPort::PRI_ENLIGHTENED.0, 0_u32.as_bytes()); match r {
2439 IoResult::Defer(mut deferred) => {
2440 poll_fn(|cx| {
2441 ide_device.poll_device(cx);
2442 deferred.poll_write(cx)
2443 })
2444 .await
2445 .unwrap();
2446 }
2447 _ => panic!("{:?}", r),
2448 }
2449
2450 let mut buffer = vec![0u8; BYTE_COUNT as usize / 2];
2451 test_guest_mem
2452 .read_at(data_gpa.into(), &mut buffer)
2453 .unwrap();
2454 assert_eq!(buffer, file_contents.as_bytes()[..buffer.len()]);
2455 }
2456
2457 #[async_test]
2458 async fn enlightened_cmd_test_dma_boundary_overflow() {
2459 const SECTOR_COUNT: u16 = 16; const BYTE_COUNT: u16 = SECTOR_COUNT * protocol::HARD_DRIVE_SECTOR_BYTES as u16;
2465
2466 let test_guest_mem = GuestMemory::allocate(16384);
2467
2468 let table_gpa = 0x1000;
2469 let data_gpa = 0x3000; test_guest_mem
2471 .write_plain(
2472 table_gpa,
2473 &BusMasterDmaDesc {
2474 mem_physical_base: data_gpa,
2475 byte_count: BYTE_COUNT, unused: 0,
2477 end_of_table: 0x80,
2478 },
2479 )
2480 .unwrap();
2481
2482 let data_buffer = table_gpa as u32;
2483 let byte_count = 0;
2484
2485 let eint13_command = protocol::EnlightenedInt13Command {
2486 command: IdeCommand::READ_DMA_ALT,
2487 device_head: DeviceHeadReg::new().with_lba(true),
2488 flags: 0,
2489 result_status: 0,
2490 lba_low: 0,
2491 lba_high: 0,
2492 block_count: SECTOR_COUNT,
2493 byte_count,
2494 data_buffer,
2495 skip_bytes_head: 0,
2496 skip_bytes_tail: 0,
2497 };
2498 test_guest_mem.write_plain(0, &eint13_command).unwrap();
2499
2500 let dev_path = IdePath::default();
2501 let (mut ide_device, _disk, _file_contents, _geometry) =
2502 ide_test_setup(Some(test_guest_mem.clone()), DriveType::Hard);
2503
2504 device_select(&mut ide_device, &dev_path).await;
2505 prep_ide_channel(&mut ide_device, DriveType::Hard, &dev_path);
2506
2507 let r = ide_device.io_write(IdeIoPort::PRI_ENLIGHTENED.0, 0_u32.as_bytes());
2508
2509 match r {
2510 IoResult::Defer(mut deferred) => {
2511 poll_fn(|cx| {
2512 ide_device.poll_device(cx);
2513 deferred.poll_write(cx)
2514 })
2515 .await
2516 .unwrap();
2517 }
2518 _ => panic!("{:?}", r),
2519 }
2520
2521 let dma_state = get_dma_state(&mut ide_device, &dev_path);
2522 assert!(
2523 !dma_state,
2524 "Expected DMA state cleared - transfer from 0x{:x} with {} bytes overflows valid range 0x0-0x3FFF",
2525 data_gpa, BYTE_COUNT
2526 );
2527 }
2528
2529 #[async_test]
2530 async fn enlightened_cmd_test_dma_exact_boundary() {
2531 const BYTE_COUNT: u16 = 512;
2536
2537 let test_guest_mem = GuestMemory::allocate(16384);
2538
2539 let table_gpa = 0x1000;
2540 let data_gpa = 0x3E00; test_guest_mem
2542 .write_plain(
2543 table_gpa,
2544 &BusMasterDmaDesc {
2545 mem_physical_base: data_gpa,
2546 byte_count: BYTE_COUNT, unused: 0,
2548 end_of_table: 0x80,
2549 },
2550 )
2551 .unwrap();
2552
2553 let data_buffer = table_gpa as u32;
2554 let byte_count = 0;
2555
2556 let eint13_command = protocol::EnlightenedInt13Command {
2557 command: IdeCommand::READ_DMA_ALT,
2558 device_head: DeviceHeadReg::new().with_lba(true),
2559 flags: 0,
2560 result_status: 0,
2561 lba_low: 0,
2562 lba_high: 0,
2563 block_count: 1,
2564 byte_count,
2565 data_buffer,
2566 skip_bytes_head: 0,
2567 skip_bytes_tail: 0,
2568 };
2569 test_guest_mem.write_plain(0, &eint13_command).unwrap();
2570
2571 let dev_path = IdePath::default();
2572 let (mut ide_device, _disk, _file_contents, _geometry) =
2573 ide_test_setup(Some(test_guest_mem.clone()), DriveType::Hard);
2574
2575 device_select(&mut ide_device, &dev_path).await;
2576 prep_ide_channel(&mut ide_device, DriveType::Hard, &dev_path);
2577
2578 let r = ide_device.io_write(IdeIoPort::PRI_ENLIGHTENED.0, 0_u32.as_bytes());
2579
2580 match r {
2581 IoResult::Defer(mut deferred) => {
2582 poll_fn(|cx| {
2583 ide_device.poll_device(cx);
2584 deferred.poll_write(cx)
2585 })
2586 .await
2587 .unwrap();
2588 }
2589 _ => panic!("{:?}", r),
2590 }
2591
2592 let dma_state = get_dma_state(&mut ide_device, &dev_path);
2593 assert!(
2594 !dma_state,
2595 "Expected DMA state cleared - transfer ending at 0x{:x} exceeds valid range",
2596 data_gpa + BYTE_COUNT as u32
2597 );
2598 }
2599
2600 #[async_test]
2601 async fn enlightened_cmd_test_dma_integer_overflow() {
2602 let test_guest_mem = GuestMemory::allocate(16384);
2608
2609 let table_gpa = 0x1000;
2610 let data_gpa = 0x2000;
2611 test_guest_mem
2612 .write_plain(
2613 table_gpa,
2614 &BusMasterDmaDesc {
2615 mem_physical_base: data_gpa,
2616 byte_count: 0xFFFF, unused: 0,
2618 end_of_table: 0x80,
2619 },
2620 )
2621 .unwrap();
2622
2623 let data_buffer = table_gpa as u32;
2624 let byte_count = 0;
2625
2626 let eint13_command = protocol::EnlightenedInt13Command {
2627 command: IdeCommand::READ_DMA_ALT,
2628 device_head: DeviceHeadReg::new().with_lba(true),
2629 flags: 0,
2630 result_status: 0,
2631 lba_low: 0,
2632 lba_high: 0,
2633 block_count: 128, byte_count,
2635 data_buffer,
2636 skip_bytes_head: 0,
2637 skip_bytes_tail: 0,
2638 };
2639 test_guest_mem.write_plain(0, &eint13_command).unwrap();
2640
2641 let dev_path = IdePath::default();
2642 let (mut ide_device, _disk, _file_contents, _geometry) =
2643 ide_test_setup(Some(test_guest_mem.clone()), DriveType::Hard);
2644
2645 device_select(&mut ide_device, &dev_path).await;
2646 prep_ide_channel(&mut ide_device, DriveType::Hard, &dev_path);
2647
2648 let r = ide_device.io_write(IdeIoPort::PRI_ENLIGHTENED.0, 0_u32.as_bytes());
2649
2650 match r {
2651 IoResult::Defer(mut deferred) => {
2652 poll_fn(|cx| {
2653 ide_device.poll_device(cx);
2654 deferred.poll_write(cx)
2655 })
2656 .await
2657 .unwrap();
2658 }
2659 _ => panic!("{:?}", r),
2660 }
2661
2662 let dma_state = get_dma_state(&mut ide_device, &dev_path);
2663 assert!(
2664 !dma_state,
2665 "Expected DMA state cleared - large byte_count 0xFFFF should be rejected"
2666 );
2667 }
2668
2669 #[async_test]
2670 async fn enlightened_cmd_test_dma_u32_max_overflow() {
2671 let test_guest_mem = GuestMemory::allocate(16384);
2675
2676 let table_gpa = 0x1000;
2677 let data_gpa = 0xFFFF_F000_u32; test_guest_mem
2679 .write_plain(
2680 table_gpa,
2681 &BusMasterDmaDesc {
2682 mem_physical_base: data_gpa,
2683 byte_count: 0x2000, unused: 0,
2685 end_of_table: 0x80,
2686 },
2687 )
2688 .unwrap();
2689
2690 let data_buffer = table_gpa as u32;
2691 let byte_count = 0;
2692
2693 let eint13_command = protocol::EnlightenedInt13Command {
2694 command: IdeCommand::READ_DMA_ALT,
2695 device_head: DeviceHeadReg::new().with_lba(true),
2696 flags: 0,
2697 result_status: 0,
2698 lba_low: 0,
2699 lba_high: 0,
2700 block_count: 16,
2701 byte_count,
2702 data_buffer,
2703 skip_bytes_head: 0,
2704 skip_bytes_tail: 0,
2705 };
2706 test_guest_mem.write_plain(0, &eint13_command).unwrap();
2707
2708 let dev_path = IdePath::default();
2709 let (mut ide_device, _disk, _file_contents, _geometry) =
2710 ide_test_setup(Some(test_guest_mem.clone()), DriveType::Hard);
2711
2712 device_select(&mut ide_device, &dev_path).await;
2713 prep_ide_channel(&mut ide_device, DriveType::Hard, &dev_path);
2714
2715 let r = ide_device.io_write(IdeIoPort::PRI_ENLIGHTENED.0, 0_u32.as_bytes());
2716
2717 match r {
2718 IoResult::Defer(mut deferred) => {
2719 poll_fn(|cx| {
2720 ide_device.poll_device(cx);
2721 deferred.poll_write(cx)
2722 })
2723 .await
2724 .unwrap();
2725 }
2726 _ => panic!("{:?}", r),
2727 }
2728
2729 let dma_state = get_dma_state(&mut ide_device, &dev_path);
2730 assert!(
2731 !dma_state,
2732 "Expected DMA state cleared - checked_add should catch u32 overflow from 0x{:x} + 0x2000",
2733 data_gpa
2734 );
2735 }
2736
2737 #[async_test]
2738 async fn enlightened_cmd_test_dma_zero_byte_count() {
2739 let test_guest_mem = GuestMemory::allocate(16384);
2743
2744 let table_gpa = 0x1000;
2745 let data_gpa = 0x1000;
2746 test_guest_mem
2747 .write_plain(
2748 table_gpa,
2749 &BusMasterDmaDesc {
2750 mem_physical_base: data_gpa,
2751 byte_count: 0, unused: 0,
2753 end_of_table: 0x80,
2754 },
2755 )
2756 .unwrap();
2757
2758 let data_buffer = table_gpa as u32;
2759 let byte_count = 0;
2760
2761 let eint13_command = protocol::EnlightenedInt13Command {
2762 command: IdeCommand::READ_DMA_ALT,
2763 device_head: DeviceHeadReg::new().with_lba(true),
2764 flags: 0,
2765 result_status: 0,
2766 lba_low: 0,
2767 lba_high: 0,
2768 block_count: 128, byte_count,
2770 data_buffer,
2771 skip_bytes_head: 0,
2772 skip_bytes_tail: 0,
2773 };
2774 test_guest_mem.write_plain(0, &eint13_command).unwrap();
2775
2776 let dev_path = IdePath::default();
2777 let (mut ide_device, _disk, _file_contents, _geometry) =
2778 ide_test_setup(Some(test_guest_mem.clone()), DriveType::Hard);
2779
2780 device_select(&mut ide_device, &dev_path).await;
2781 prep_ide_channel(&mut ide_device, DriveType::Hard, &dev_path);
2782
2783 let r = ide_device.io_write(IdeIoPort::PRI_ENLIGHTENED.0, 0_u32.as_bytes());
2784
2785 match r {
2786 IoResult::Defer(mut deferred) => {
2787 poll_fn(|cx| {
2788 ide_device.poll_device(cx);
2789 deferred.poll_write(cx)
2790 })
2791 .await
2792 .unwrap();
2793 }
2794 _ => panic!("{:?}", r),
2795 }
2796
2797 let dma_state = get_dma_state(&mut ide_device, &dev_path);
2798 assert!(
2799 !dma_state,
2800 "Expected DMA state cleared - byte_count=0 implies 64KB which exceeds guest memory"
2801 );
2802 }
2803
2804 #[async_test]
2805 async fn identify_test_cd() {
2806 let dev_path = IdePath::default();
2807 let (mut ide_device, _disk, _file_contents, _geometry) =
2808 ide_test_setup(None, DriveType::Optical);
2809
2810 device_select(&mut ide_device, &dev_path).await;
2812 prep_ide_channel(&mut ide_device, DriveType::Optical, &dev_path);
2813
2814 execute_command(
2816 &mut ide_device,
2817 &dev_path,
2818 IdeCommand::IDENTIFY_PACKET_DEVICE.0,
2819 );
2820
2821 let status = check_command_ready(&mut ide_device, &dev_path).await;
2822 assert!(status.drq());
2823 assert!(!status.err());
2824
2825 let data = &mut [0_u8; protocol::IDENTIFY_DEVICE_BYTES];
2827 ide_device.io_read(IdeIoPort::PRI_DATA.0, data).unwrap();
2828 let features = protocol::IdeFeatures::read_from_prefix(&data[..])
2829 .unwrap()
2830 .0; let ex_features = protocol::IdeFeatures {
2832 config_bits: 0x85C0,
2833 serial_no: *b" ",
2834 buffer_size: 0x0080,
2835 firmware_revision: *b" ",
2836 model_number: "iVtrau lDC ".as_bytes()[..]
2837 .try_into()
2838 .unwrap(),
2839 capabilities: 0x0300,
2840 pio_cycle_times: 0x0200, dma_cycle_times: 0x0200, new_words_valid_flags: 0x0003, multi_sector_capabilities: 0x0100_u16 | protocol::MAX_SECTORS_MULT_TRANSFER_DEFAULT,
2844 single_word_dma_mode: 0x0007, multi_word_dma_mode: 0x0407, enhanced_pio_mode: 0x0003, min_multi_dma_time: 0x0078,
2848 recommended_multi_dma_time: 0x0078,
2849 min_pio_cycle_time_no_flow: 0x01FC, min_pio_cycle_time_flow: 0x00B4, ..FromZeros::new_zeroed()
2852 };
2853 assert_eq!(features.as_bytes(), ex_features.as_bytes());
2854 }
2855
2856 #[async_test]
2857 async fn identify_test_hdd() {
2858 let dev_path = IdePath::default();
2859 let (mut ide_device, _disk, _file_contents, geometry) =
2860 ide_test_setup(None, DriveType::Hard);
2861 device_select(&mut ide_device, &dev_path).await;
2863 prep_ide_channel(&mut ide_device, DriveType::Hard, &dev_path);
2864 execute_command(&mut ide_device, &dev_path, IdeCommand::IDENTIFY_DEVICE.0);
2866
2867 let status = check_command_ready(&mut ide_device, &dev_path).await;
2868 assert!(status.drq());
2869 assert!(!status.err());
2870
2871 let data = &mut [0_u8; protocol::IDENTIFY_DEVICE_BYTES];
2873 ide_device.io_read(IdeIoPort::PRI_DATA.0, data).unwrap();
2874 let features = protocol::IdeFeatures::read_from_prefix(&data[..])
2875 .unwrap()
2876 .0; let total_chs_sectors: u32 =
2879 geometry.sectors_per_track * geometry.cylinder_count * geometry.head_count;
2880 let (cylinders, heads, sectors_per_track) = if total_chs_sectors < protocol::MAX_CHS_SECTORS
2881 {
2882 (
2883 geometry.cylinder_count as u16,
2884 geometry.head_count as u16,
2885 geometry.sectors_per_track as u16,
2886 )
2887 } else {
2888 (0x3FFF, 16, 63)
2889 };
2890
2891 let firmware_revision = if dev_path.channel == 0 {
2892 ".1.1 0 "
2893 } else {
2894 ".1.1 1 "
2895 }
2896 .as_bytes()[..]
2897 .try_into()
2898 .unwrap();
2899
2900 let user_addressable_sectors =
2901 if geometry.total_sectors > (protocol::LBA_28BIT_MAX_SECTORS as u64) {
2902 protocol::LBA_28BIT_MAX_SECTORS
2903 } else {
2904 geometry.total_sectors as u32
2905 };
2906
2907 let ex_features = protocol::IdeFeatures {
2908 config_bits: 0x045A,
2909 cylinders,
2910 heads,
2911 unformatted_sectors_per_track: (protocol::HARD_DRIVE_SECTOR_BYTES
2912 * geometry.sectors_per_track) as u16,
2913 unformatted_bytes_per_sector: protocol::HARD_DRIVE_SECTOR_BYTES as u16,
2914 sectors_per_track,
2915 compact_flash: [0xABCD, 0xDCBA],
2916 vendor0: 0x0123,
2917 serial_no: *b" ",
2918 buffer_type: 3,
2919 buffer_size: 0x0080,
2920 firmware_revision,
2921 model_number: "iVtrau lDH ".as_bytes()[..]
2922 .try_into()
2923 .unwrap(),
2924 max_sectors_mult_transfer: (0x8000 | protocol::MAX_SECTORS_MULT_TRANSFER_DEFAULT),
2925 capabilities: 0x0F00, pio_cycle_times: 0x0200, dma_cycle_times: 0x0200, new_words_valid_flags: 0x0003, log_cylinders: geometry.cylinder_count as u16,
2930 log_heads: geometry.head_count as u16,
2931 log_sectors_per_track: geometry.sectors_per_track as u16,
2932 log_total_sectors: total_chs_sectors.into(),
2933 multi_sector_capabilities: 0x0100_u16 | protocol::MAX_SECTORS_MULT_TRANSFER_DEFAULT,
2934 user_addressable_sectors: user_addressable_sectors.into(),
2935 single_word_dma_mode: 0x0007, multi_word_dma_mode: 0x0407, enhanced_pio_mode: 0x0003, min_multi_dma_time: 0x0078,
2939 recommended_multi_dma_time: 0x0078,
2940 min_pio_cycle_time_no_flow: 0x014D,
2941 min_pio_cycle_time_flow: 0x0078,
2942 major_version_number: 0x01F0, minor_version_number: 0,
2944 command_set_supported: 0x0028, command_sets_supported: 0x7400, command_set_supported_ext: 0x4040, command_set_enabled1: 0x0028, command_set_enabled2: 0x3400, command_set_default: 0x4040, total_sectors_48_bit: geometry.total_sectors.into(),
2951 default_sector_size_config: 0x4000, logical_block_alignment: 0x4000, ..FromZeros::new_zeroed()
2954 };
2955 assert_eq!(features.as_bytes(), ex_features.as_bytes());
2956 }
2957
2958 #[async_test]
2963 async fn enlightened_hdd_non_dma_cmd_completes() {
2964 let test_guest_mem = GuestMemory::allocate(16384);
2965
2966 let table_gpa: u64 = 0x1000;
2969 let data_gpa: u32 = 0x2000;
2970 test_guest_mem
2971 .write_plain(
2972 table_gpa,
2973 &BusMasterDmaDesc {
2974 mem_physical_base: data_gpa,
2975 byte_count: 512,
2976 unused: 0,
2977 end_of_table: 0x80,
2978 },
2979 )
2980 .unwrap();
2981
2982 let eint13_command = protocol::EnlightenedInt13Command {
2987 command: IdeCommand::READ_SECTORS,
2988 device_head: DeviceHeadReg::new().with_lba(true),
2989 flags: 0,
2990 result_status: 0,
2991 lba_low: 0,
2992 lba_high: 0,
2993 block_count: 1,
2994 byte_count: 0,
2995 data_buffer: table_gpa as u32,
2996 skip_bytes_head: 0,
2997 skip_bytes_tail: 0,
2998 };
2999 test_guest_mem.write_plain(0, &eint13_command).unwrap();
3000
3001 let dev_path = IdePath::default();
3002 let (mut ide_device, _disk, _, _) =
3003 ide_test_setup(Some(test_guest_mem.clone()), DriveType::Hard);
3004
3005 device_select(&mut ide_device, &dev_path).await;
3006 prep_ide_channel(&mut ide_device, DriveType::Hard, &dev_path);
3007
3008 assert!(
3012 matches!(
3013 ide_device.io_write(IdeIoPort::PRI_ENLIGHTENED.0, 0_u32.as_bytes()),
3014 IoResult::Ok
3015 ),
3016 "non-DMA command (READ_SECTORS) via enlightened path should return Ok, not Defer"
3017 );
3018 }
3019}