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