ide/
lib.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4#![expect(missing_docs)]
5#![forbid(unsafe_code)]
6
7mod drive;
8mod protocol;
9
10use crate::drive::save_restore::DriveSaveRestore;
11use crate::protocol::BusMasterReg;
12use crate::protocol::DeviceControlReg;
13use crate::protocol::IdeCommand;
14use crate::protocol::IdeConfigSpace;
15use crate::protocol::Status;
16use chipset_device::ChipsetDevice;
17use chipset_device::io::IoError;
18use chipset_device::io::IoResult;
19use chipset_device::io::deferred::DeferredWrite;
20use chipset_device::io::deferred::defer_write;
21use chipset_device::pci::PciConfigSpace;
22use chipset_device::pio::ControlPortIoIntercept;
23use chipset_device::pio::PortIoIntercept;
24use chipset_device::pio::RegisterPortIoIntercept;
25use chipset_device::poll_device::PollDevice;
26use disk_backend::Disk;
27use drive::DiskDrive;
28use drive::DriveRegister;
29use guestmem::GuestMemory;
30use ide_resources::IdePath;
31use inspect::Inspect;
32use inspect::InspectMut;
33use open_enum::open_enum;
34use pci_core::spec::cfg_space::Command;
35use pci_core::spec::cfg_space::HEADER_TYPE_00_SIZE;
36use pci_core::spec::cfg_space::HeaderType00;
37use protocol::BusMasterCommandReg;
38use protocol::BusMasterStatusReg;
39use scsi::CdbFlags;
40use scsi::ScsiOp;
41use scsi_core::AsyncScsiDisk;
42use scsi_defs as scsi;
43use std::fmt::Debug;
44use std::mem::offset_of;
45use std::ops::RangeInclusive;
46use std::sync::Arc;
47use std::task::Context;
48use thiserror::Error;
49use vmcore::device_state::ChangeDeviceState;
50use vmcore::line_interrupt::LineInterrupt;
51use zerocopy::IntoBytes;
52
53open_enum! {
54    pub enum IdeIoPort: u16 {
55        PRI_ENLIGHTENED = 0x1E0,
56        PRI_DATA = 0x1F0,
57        PRI_ERROR_FEATURES = 0x1F1,
58        PRI_SECTOR_COUNT = 0x1F2,
59        PRI_SECTOR_NUM = 0x1F3,
60        PRI_CYLINDER_LSB = 0x1F4,
61        PRI_CYLINDER_MSB = 0x1F5,
62        PRI_DEVICE_HEAD = 0x1F6,
63        PRI_STATUS_CMD = 0x1F7,
64        PRI_ALT_STATUS_DEVICE_CTL = 0x3F6,
65        SEC_ENLIGHTENED = 0x160,
66        SEC_DATA = 0x170,
67        SEC_ERROR_FEATURES = 0x171,
68        SEC_SECTOR_COUNT = 0x172,
69        SEC_SECTOR_NUM = 0x173,
70        SEC_CYLINDER_LSB = 0x174,
71        SEC_CYLINDER_MSB = 0x175,
72        SEC_DEVICE_HEAD = 0x176,
73        SEC_STATUS_CMD = 0x177,
74        SEC_ALT_STATUS_DEVICE_CTL = 0x376,
75    }
76}
77
78enum Port {
79    Data,
80    Drive(DriveRegister),
81    Enlightened,
82    BusMaster(BusMasterReg),
83}
84
85#[derive(Debug, Copy, Clone, PartialEq, Eq, Inspect)]
86enum DmaType {
87    /// Read from guest memory.
88    Read,
89    /// Write to guest memory.
90    Write,
91}
92
93#[derive(Debug, Inspect)]
94struct BusMasterState {
95    #[inspect(hex)]
96    cmd_status_reg: u32,
97    #[inspect(hex)]
98    port_addr_reg: u32,
99    #[inspect(hex)]
100    timing_reg: u32,
101    #[inspect(hex)]
102    secondary_timing_reg: u32,
103    #[inspect(hex)]
104    dma_ctl_reg: u32,
105}
106
107// bus master
108const DEFAULT_BUS_MASTER_PORT_ADDR_REG: u32 = 0x0000_0001;
109const DEFAULT_BUS_MASTER_CMD_STATUS_REG: u32 = 0x0280_0000;
110
111impl BusMasterState {
112    fn new() -> Self {
113        Self {
114            cmd_status_reg: DEFAULT_BUS_MASTER_CMD_STATUS_REG,
115            port_addr_reg: DEFAULT_BUS_MASTER_PORT_ADDR_REG,
116            // Enable IDE decode at startup, don't wait for the BIOS (this is
117            // not actually configurable with our hardware).
118            //
119            // TODO: define a bitfield, hard wire these bits to 1 so that they
120            // can't be cleared.
121            timing_reg: 0x80008000,
122            secondary_timing_reg: 0,
123            dma_ctl_reg: 0,
124        }
125    }
126}
127
128#[derive(Debug, Default, Inspect)]
129struct ChannelBusMasterState {
130    command_reg: BusMasterCommandReg,
131    status_reg: BusMasterStatusReg,
132    #[inspect(hex)]
133    desc_table_ptr: u32,
134    dma_state: Option<DmaState>,
135    dma_error: bool,
136}
137
138impl ChannelBusMasterState {
139    fn dma_io_type(&self) -> DmaType {
140        if self.command_reg.write() {
141            DmaType::Write
142        } else {
143            DmaType::Read
144        }
145    }
146}
147
148/// PCI-based IDE controller
149pub struct IdeDevice {
150    /// IDE controllers can support up to two IDE channels (primary and secondary)
151    /// with up to two devices (drives) per channel for a total of four IDE devices.
152    channels: [Channel; 2],
153    bus_master_state: BusMasterState,
154    bus_master_pio_dynamic: Box<dyn ControlPortIoIntercept>,
155}
156
157impl InspectMut for IdeDevice {
158    fn inspect_mut(&mut self, req: inspect::Request<'_>) {
159        req.respond()
160            .field_mut("primary", &mut self.channels[0])
161            .field_mut("secondary", &mut self.channels[1])
162            .field("bus_master_state", &self.bus_master_state);
163    }
164}
165
166#[derive(Inspect, Debug)]
167struct EnlightenedCdWrite {
168    #[inspect(skip)]
169    deferred: DeferredWrite,
170    old_adapter_control_reg: u8,
171    guest_address: u64,
172    old_features_reg: u8,
173    data_buffer: u32,
174    skip_bytes_head: u16,
175    byte_count: u32,
176    block_count: u16,
177    drive_index: usize,
178}
179
180#[derive(Inspect, Debug)]
181struct EnlightenedHddWrite {
182    #[inspect(skip)]
183    deferred: DeferredWrite,
184    old_adapter_control_reg: u8,
185    guest_address: u64,
186    drive_index: usize,
187}
188
189#[derive(Debug, Inspect)]
190#[inspect(tag = "drive_type")]
191enum EnlightenedWrite {
192    Hard(#[inspect(rename = "write")] EnlightenedHddWrite),
193    Optical(#[inspect(rename = "write")] EnlightenedCdWrite),
194}
195
196#[derive(Debug, Error)]
197pub enum NewDeviceError {
198    #[error("disk too large: {0} bytes")]
199    DiskTooLarge(u64),
200}
201
202impl IdeDevice {
203    /// Creates an IDE device from the provided channel drive configuration.
204    pub fn new(
205        guest_memory: GuestMemory,
206        register_pio: &mut dyn RegisterPortIoIntercept,
207        primary_channel_drives: [Option<DriveMedia>; 2],
208        secondary_channel_drives: [Option<DriveMedia>; 2],
209        primary_line_interrupt: LineInterrupt,
210        secondary_line_interrupt: LineInterrupt,
211    ) -> Result<Self, NewDeviceError> {
212        let channels = [
213            Channel::new(
214                primary_channel_drives,
215                ChannelType::Primary,
216                primary_line_interrupt,
217                guest_memory.clone(),
218            )?,
219            Channel::new(
220                secondary_channel_drives,
221                ChannelType::Secondary,
222                secondary_line_interrupt,
223                guest_memory,
224            )?,
225        ];
226
227        Ok(Self {
228            channels,
229            bus_master_state: BusMasterState::new(),
230            bus_master_pio_dynamic: register_pio.new_io_region("ide bus master", 16),
231        })
232    }
233
234    fn parse_port(&self, io_port: u16) -> Option<(Port, usize)> {
235        match IdeIoPort(io_port) {
236            IdeIoPort::PRI_ENLIGHTENED => Some((Port::Enlightened, 0)),
237            IdeIoPort::PRI_DATA => Some((Port::Data, 0)),
238            IdeIoPort::PRI_ERROR_FEATURES => Some((Port::Drive(DriveRegister::ErrorFeatures), 0)),
239            IdeIoPort::PRI_SECTOR_COUNT => Some((Port::Drive(DriveRegister::SectorCount), 0)),
240            IdeIoPort::PRI_SECTOR_NUM => Some((Port::Drive(DriveRegister::LbaLow), 0)),
241            IdeIoPort::PRI_CYLINDER_LSB => Some((Port::Drive(DriveRegister::LbaMid), 0)),
242            IdeIoPort::PRI_CYLINDER_MSB => Some((Port::Drive(DriveRegister::LbaHigh), 0)),
243            IdeIoPort::PRI_DEVICE_HEAD => Some((Port::Drive(DriveRegister::DeviceHead), 0)),
244            IdeIoPort::PRI_STATUS_CMD => Some((Port::Drive(DriveRegister::StatusCmd), 0)),
245            IdeIoPort::PRI_ALT_STATUS_DEVICE_CTL => {
246                Some((Port::Drive(DriveRegister::AlternateStatusDeviceControl), 0))
247            }
248            IdeIoPort::SEC_ENLIGHTENED => Some((Port::Enlightened, 1)),
249            IdeIoPort::SEC_DATA => Some((Port::Data, 1)),
250            IdeIoPort::SEC_ERROR_FEATURES => Some((Port::Drive(DriveRegister::ErrorFeatures), 1)),
251            IdeIoPort::SEC_SECTOR_COUNT => Some((Port::Drive(DriveRegister::SectorCount), 1)),
252            IdeIoPort::SEC_SECTOR_NUM => Some((Port::Drive(DriveRegister::LbaLow), 1)),
253            IdeIoPort::SEC_CYLINDER_LSB => Some((Port::Drive(DriveRegister::LbaMid), 1)),
254            IdeIoPort::SEC_CYLINDER_MSB => Some((Port::Drive(DriveRegister::LbaHigh), 1)),
255            IdeIoPort::SEC_DEVICE_HEAD => Some((Port::Drive(DriveRegister::DeviceHead), 1)),
256            IdeIoPort::SEC_STATUS_CMD => Some((Port::Drive(DriveRegister::StatusCmd), 1)),
257            IdeIoPort::SEC_ALT_STATUS_DEVICE_CTL => {
258                Some((Port::Drive(DriveRegister::AlternateStatusDeviceControl), 1))
259            }
260            io_port
261                if (IdeIoPort::PRI_ENLIGHTENED..=IdeIoPort::PRI_STATUS_CMD).contains(&io_port) =>
262            {
263                None
264            }
265            io_port
266                if (IdeIoPort::SEC_ENLIGHTENED..=IdeIoPort::SEC_STATUS_CMD).contains(&io_port) =>
267            {
268                None
269            }
270            _ => {
271                if self.bus_master_state.cmd_status_reg
272                    & protocol::PCI_CONFIG_STATUS_IO_SPACE_ENABLE_MASK
273                    == 0
274                {
275                    return None;
276                }
277
278                // If the port does not match any of the statically registered IO ports then
279                // the port is part of the dynamically registered bus master range. The IDE
280                // bus master range spans 16 ports with the first 8 corresponding to the primary
281                // IDE channel.
282                Some((
283                    Port::BusMaster(BusMasterReg(io_port & 0x7)),
284                    (io_port as usize & 0x8) >> 3,
285                ))
286            }
287        }
288    }
289}
290
291impl Channel {
292    fn enlightened_port_write(
293        &mut self,
294        data: &[u8],
295        bus_master_state: &BusMasterState,
296    ) -> IoResult {
297        if data.len() != 4 {
298            return IoResult::Err(IoError::InvalidAccessSize);
299        }
300
301        if let Some(status) = self.current_drive_status() {
302            if status.err() {
303                tracelimit::warn_ratelimited!(
304                    "drive is in error state, ignoring enlightened command",
305                );
306                return IoResult::Ok;
307            } else if status.bsy() || status.drq() {
308                tracelimit::warn_ratelimited!(
309                    "command is already pending on this drive, ignoring enlightened command"
310                );
311                return IoResult::Ok;
312            }
313        }
314        if self.enlightened_write.is_some() {
315            tracelimit::error_ratelimited!("enlightened write while one is in progress, ignoring");
316            return IoResult::Ok;
317        }
318
319        // Read the EnlightenedInt13Command packet directly from guest ram
320        let addr = u32::from_ne_bytes(data.try_into().unwrap());
321
322        let eint13_cmd = match self
323            .guest_memory
324            .read_plain::<protocol::EnlightenedInt13Command>(addr as u64)
325        {
326            Ok(cmd) => cmd,
327            Err(err) => {
328                tracelimit::error_ratelimited!(
329                    error = &err as &dyn std::error::Error,
330                    "failed to read enlightened IO command"
331                );
332                return IoResult::Ok;
333            }
334        };
335
336        // Write out the drive-head register FIRST because that is used to
337        // select the current drive.
338        self.write_drive_register(
339            DriveRegister::DeviceHead,
340            eint13_cmd.device_head.into(),
341            bus_master_state,
342        );
343
344        let result = if let Some(drive_type) = self.current_drive_type() {
345            match drive_type {
346                DriveType::Optical => {
347                    self.enlightened_cd_command(addr.into(), eint13_cmd, bus_master_state)
348                }
349                DriveType::Hard => {
350                    self.enlightened_hdd_command(addr.into(), eint13_cmd, bus_master_state)
351                }
352            }
353        } else {
354            tracelimit::warn_ratelimited!(
355                eint13_cmd = ?eint13_cmd,
356                drive_idx = self.state.current_drive_idx,
357                "Enlightened IO command: No attached drive"
358            );
359            IoResult::Ok
360        };
361
362        self.post_drive_access(bus_master_state);
363        result
364    }
365
366    fn enlightened_hdd_command(
367        &mut self,
368        guest_address: u64,
369        eint13_cmd: protocol::EnlightenedInt13Command,
370        bus_master_state: &BusMasterState,
371    ) -> IoResult {
372        let mut lba48 = eint13_cmd.lba_high as u64;
373        lba48 <<= 32;
374        lba48 |= eint13_cmd.lba_low as u64;
375
376        tracing::trace!(
377            command = ?eint13_cmd.command,
378            lba = lba48,
379            block_count = eint13_cmd.block_count,
380            buffer = eint13_cmd.data_buffer,
381            guest_address,
382            "enlightened hdd command"
383        );
384
385        // Write out the PRD register for the bus master
386        self.write_bus_master_reg(
387            BusMasterReg::TABLE_PTR,
388            eint13_cmd.data_buffer.as_bytes(),
389            bus_master_state,
390        )
391        .unwrap();
392
393        // Now that we know what the IDE command is, disambiguate between
394        // 28-bit LBA and 48-bit LBA
395        let cmd = eint13_cmd.command;
396        if cmd == IdeCommand::READ_DMA_EXT || cmd == IdeCommand::WRITE_DMA_EXT {
397            // 48-bit LBA, high 24 bits of logical block address
398            self.write_drive_register(
399                DriveRegister::LbaLow,
400                (eint13_cmd.lba_low >> 24) as u8,
401                bus_master_state,
402            );
403
404            self.write_drive_register(
405                DriveRegister::LbaMid,
406                eint13_cmd.lba_high as u8,
407                bus_master_state,
408            );
409
410            self.write_drive_register(
411                DriveRegister::LbaHigh,
412                (eint13_cmd.lba_high >> 8) as u8,
413                bus_master_state,
414            );
415
416            // 48-bit LBA, high 8 bits of sector count
417            // Write the low-byte of the sector count
418            self.write_drive_register(
419                DriveRegister::SectorCount,
420                (eint13_cmd.block_count >> 8) as u8,
421                bus_master_state,
422            );
423        }
424
425        // Write the low-byte of the sector count
426        self.write_drive_register(
427            DriveRegister::SectorCount,
428            eint13_cmd.block_count as u8,
429            bus_master_state,
430        );
431
432        // Finish writing the LBA low bytes to the FIFOs
433        self.write_drive_register(
434            DriveRegister::LbaLow,
435            eint13_cmd.lba_low as u8,
436            bus_master_state,
437        );
438        self.write_drive_register(
439            DriveRegister::LbaMid,
440            (eint13_cmd.lba_low >> 8) as u8,
441            bus_master_state,
442        );
443        self.write_drive_register(
444            DriveRegister::LbaHigh,
445            (eint13_cmd.lba_low >> 16) as u8,
446            bus_master_state,
447        );
448
449        // Make sure that the IDE channel will not cause an interrupt since this
450        // enlightened operation is intended to be 100% synchronous.
451        let old_adapter_control_reg = self.state.shadow_adapter_control_reg;
452        self.write_drive_register(
453            DriveRegister::AlternateStatusDeviceControl,
454            DeviceControlReg::new()
455                .with_interrupt_mask(true)
456                .into_bits(),
457            bus_master_state,
458        );
459
460        // Start the dma engine.
461        let mut bus_master_flags = BusMasterCommandReg::new().with_start(true);
462        if cmd == IdeCommand::READ_DMA_EXT
463            || cmd == IdeCommand::READ_DMA
464            || cmd == IdeCommand::READ_DMA_ALT
465        {
466            // set rw flag to 1 to inticate that bus master is performing a read
467            bus_master_flags.set_write(true);
468        }
469
470        self.write_bus_master_reg(
471            BusMasterReg::COMMAND,
472            &[bus_master_flags.into_bits() as u8],
473            bus_master_state,
474        )
475        .unwrap();
476
477        // Start the IDE command
478        self.write_drive_register(DriveRegister::StatusCmd, cmd.0, bus_master_state);
479
480        // Defer the write.
481        let (write, token) = defer_write();
482        self.enlightened_write = Some(EnlightenedWrite::Hard(EnlightenedHddWrite {
483            deferred: write,
484            old_adapter_control_reg,
485            guest_address,
486            drive_index: self.state.current_drive_idx,
487        }));
488
489        tracing::trace!(enlightened_write = ?self.enlightened_write, "enlightened_hdd_command");
490        if let Some(status) = self.current_drive_status() {
491            if status.drq() {
492                tracelimit::warn_ratelimited!(
493                    "command is waiting for data read from guest or data write to guest"
494                );
495                return IoResult::Ok;
496            }
497        }
498        IoResult::Defer(token)
499    }
500
501    fn enlightened_cd_command(
502        &mut self,
503        guest_address: u64,
504        eint13_cmd: protocol::EnlightenedInt13Command,
505        bus_master_state: &BusMasterState,
506    ) -> IoResult {
507        tracing::trace!(
508            guest_address,
509            command = ?eint13_cmd,
510            "enlightened cd command"
511        );
512
513        // Save the old Precompensation byte because we must NOT use traditional DMA
514        // with this command. Just read the bytes into the track cache so we can use
515        // WriteRamBytes to move them to the guest.
516        let old_features_reg = self.state.shadow_features_reg;
517        self.write_drive_register(DriveRegister::ErrorFeatures, 0, bus_master_state);
518
519        // Make sure that any code path through the IDE completion and error code does NOT
520        // generate an interrupt. This enlightenment is intended to operate completely
521        // synchronously.
522        let old_adapter_control_reg = self.state.shadow_adapter_control_reg;
523        self.write_drive_register(
524            DriveRegister::AlternateStatusDeviceControl,
525            DeviceControlReg::new()
526                .with_interrupt_mask(true)
527                .into_bits(),
528            bus_master_state,
529        );
530
531        // Start the Atapi packet command
532        self.write_drive_register(
533            DriveRegister::StatusCmd,
534            IdeCommand::PACKET_COMMAND.0,
535            bus_master_state,
536        );
537
538        // Construct the SCSI command packet in the single sector buffer that
539        // we're about to dispatch.
540        let cdb = scsi::Cdb10 {
541            operation_code: ScsiOp::READ,
542            flags: CdbFlags::new(),
543            logical_block: eint13_cmd.lba_low.into(),
544            reserved2: 0,
545            transfer_blocks: eint13_cmd.block_count.into(),
546            control: 0,
547        };
548
549        // Assume the device is configured for 12-byte commands.
550        let mut command = [0; 12];
551        command[..cdb.as_bytes().len()].copy_from_slice(cdb.as_bytes());
552
553        // Spawn the IO read which eventually puts the data into the track cache
554        // Wait synchronously for the command to complete reading the data into the track cache.
555        self.write_drive_data(command.as_bytes(), bus_master_state);
556
557        // Defer the write.
558        let (write, token) = defer_write();
559        self.enlightened_write = Some(EnlightenedWrite::Optical(EnlightenedCdWrite {
560            deferred: write,
561            old_adapter_control_reg,
562            guest_address,
563            old_features_reg,
564            data_buffer: eint13_cmd.data_buffer,
565            skip_bytes_head: eint13_cmd.skip_bytes_head,
566            byte_count: eint13_cmd.byte_count,
567            block_count: eint13_cmd.block_count,
568            drive_index: self.state.current_drive_idx,
569        }));
570
571        tracing::trace!(enlightened_write = ?self.enlightened_write, "enlightened_cd_command");
572        if let Some(status) = self.current_drive_status() {
573            if status.drq() {
574                tracelimit::warn_ratelimited!(
575                    "command is waiting for data read from guest or data write to guest"
576                );
577                return IoResult::Ok;
578            }
579        }
580        IoResult::Defer(token)
581    }
582
583    fn complete_enlightened_hdd_write(
584        &mut self,
585        write: EnlightenedHddWrite,
586        bus_master_state: &BusMasterState,
587    ) {
588        // Stop the dma engine (probably stopped already, but this mimics the BIOS
589        // code which this enlightenment replaces).
590        self.write_bus_master_reg(BusMasterReg::COMMAND, &[0], bus_master_state)
591            .unwrap();
592
593        // Clear the pending interrupt and read status.
594        let status = self.read_drive_register(DriveRegister::StatusCmd, bus_master_state);
595        let status = Status::from_bits(status);
596
597        if status.err() {
598            // If there was an error, copy back the status into the enlightened INT13 command in
599            // the guest so that the guest can check it.
600            if let Err(err) = self.guest_memory.write_at(
601                write.guest_address
602                    + offset_of!(protocol::EnlightenedInt13Command, result_status) as u64,
603                &[status.into_bits()],
604            ) {
605                tracelimit::error_ratelimited!(
606                    ?status,
607                    error = &err as &dyn std::error::Error,
608                    "failed to write eint13 status back"
609                );
610            }
611        }
612
613        // Possibly re-enable IDE channel interrupts for the next operation
614        self.write_drive_register(
615            DriveRegister::AlternateStatusDeviceControl,
616            write.old_adapter_control_reg,
617            bus_master_state,
618        );
619
620        write.deferred.complete();
621    }
622
623    fn complete_enlightened_cd_write(
624        &mut self,
625        write: EnlightenedCdWrite,
626        bus_master_state: &BusMasterState,
627    ) {
628        // Clear the pending interrupt and read status.
629        let status = self.read_drive_register(DriveRegister::StatusCmd, bus_master_state);
630        let status = Status::from_bits(status);
631
632        if status.err() {
633            // If there was an error, copy back the status into the enlightened INT13 command in
634            // the guest so that the guest can check it.
635            if let Err(err) = self.guest_memory.write_at(
636                write.guest_address
637                    + offset_of!(protocol::EnlightenedInt13Command, result_status) as u64,
638                &[status.into_bits()],
639            ) {
640                tracelimit::error_ratelimited!(
641                    ?status,
642                    error = &err as &dyn std::error::Error,
643                    "failed to write eint13 status back"
644                );
645            }
646        } else {
647            let mut remaining =
648                (write.block_count as u32 * protocol::CD_DRIVE_SECTOR_BYTES) as usize;
649
650            // Skip over the unused portion of the result.
651            let skip = (write.skip_bytes_head as usize).min(remaining);
652            remaining -= skip;
653            self.skip_drive_data(skip, bus_master_state);
654
655            // Read the requested part of the result.
656            let byte_count = (write.byte_count as usize).min(remaining);
657            remaining -= byte_count;
658
659            let mut copied = 0;
660            while copied < byte_count {
661                let mut buf = [0; 512];
662                let len = (byte_count - copied).min(buf.len());
663                let buf = &mut buf[..len];
664                self.read_drive_data(buf, bus_master_state);
665                if let Err(err) = self
666                    .guest_memory
667                    .write_at((write.data_buffer as u64).wrapping_add(copied as u64), buf)
668                {
669                    tracelimit::warn_ratelimited!(
670                        error = &err as &dyn std::error::Error,
671                        "failed to write enlightened result to guest memory"
672                    );
673                }
674                copied += buf.len();
675            }
676
677            // Read any remaining data to prepare the device for the next command.
678            self.skip_drive_data(remaining, bus_master_state);
679        }
680
681        // Restore the precompensation setting which existed prior to this read
682        self.write_drive_register(
683            DriveRegister::ErrorFeatures,
684            write.old_features_reg,
685            bus_master_state,
686        );
687
688        // Possibly re-enable IDE channel interrupts for the next operation
689        self.write_drive_register(
690            DriveRegister::AlternateStatusDeviceControl,
691            write.old_adapter_control_reg,
692            bus_master_state,
693        );
694
695        tracing::trace!("enlightened cd write completed");
696        write.deferred.complete();
697    }
698
699    fn perform_dma_memory_phase(&mut self) {
700        let Some(drive) = &mut self.drives[self.state.current_drive_idx] else {
701            return;
702        };
703
704        if self.bus_master_state.dma_error {
705            if drive.handle_read_dma_descriptor_error() {
706                self.bus_master_state.dma_error = false;
707            }
708            return;
709        }
710
711        let mut dma_avail = match drive.dma_request() {
712            Some((dma_type, avail)) if *dma_type == self.bus_master_state.dma_io_type() => {
713                avail as u32
714            }
715            _ => {
716                // No active, appropriate DMA buffer.
717                return;
718            }
719        };
720        let Some(dma) = &mut self.bus_master_state.dma_state else {
721            return;
722        };
723
724        while dma_avail > 0 {
725            // If the number of bytes left in the transfer is zero, we need to read the next Dma descriptor
726            if dma.transfer_bytes_left == 0 {
727                assert!(!dma.transfer_complete);
728
729                // The descriptor table pointer points to a table of Dma
730                // scatter/gather descriptors built by the operating system.
731                // This table tells us where the place the incoming data for
732                // reads or where to get the outgoing data for writes. The
733                // last valid table entry will have the high bit of the
734                // EndOfTable field set.
735                let descriptor_addr: u64 = self
736                    .bus_master_state
737                    .desc_table_ptr
738                    .wrapping_add(8 * (dma.descriptor_idx as u32))
739                    .into();
740
741                let cur_desc_table_entry = match self
742                    .guest_memory
743                    .read_plain::<protocol::BusMasterDmaDesc>(descriptor_addr)
744                {
745                    Ok(cur_desc_table_entry) => cur_desc_table_entry,
746                    Err(err) => {
747                        self.bus_master_state.dma_state = None;
748                        if !drive.handle_read_dma_descriptor_error() {
749                            self.bus_master_state.dma_error = true;
750                        }
751                        tracelimit::error_ratelimited!(
752                            error = &err as &dyn std::error::Error,
753                            "dma descriptor read error"
754                        );
755                        return;
756                    }
757                };
758
759                tracing::trace!(entry = ?cur_desc_table_entry, "read dma desc");
760
761                dma.transfer_bytes_left = cur_desc_table_entry.byte_count.into();
762                // A zero byte length implies 64Kb
763                if cur_desc_table_entry.byte_count == 0 {
764                    dma.transfer_bytes_left = 0x10000;
765                }
766
767                dma.transfer_base_addr = cur_desc_table_entry.mem_physical_base.into();
768
769                dma.transfer_complete = (cur_desc_table_entry.end_of_table & 0x80) != 0;
770
771                // Increment to the next descriptor.
772                dma.descriptor_idx += 1;
773                if dma.transfer_complete {
774                    dma.descriptor_idx = 0;
775                }
776            }
777
778            // Transfer byte count is limited by the transfer size and the number of bytes in our IDE buffer.
779            let bytes_to_transfer = dma_avail.min(dma.transfer_bytes_left);
780
781            assert!(bytes_to_transfer != 0);
782
783            drive.dma_transfer(
784                &self.guest_memory,
785                dma.transfer_base_addr,
786                bytes_to_transfer as usize,
787            );
788
789            dma_avail -= bytes_to_transfer;
790            dma.transfer_base_addr += bytes_to_transfer as u64;
791            dma.transfer_bytes_left -= bytes_to_transfer;
792            if dma.transfer_bytes_left == 0 && dma.transfer_complete {
793                if dma_avail > 0 {
794                    // If descriptor table ended before buffer was exhausted, advance buffer to end to indicate completion of operation
795                    drive.set_prd_exhausted();
796                    drive.dma_advance_buffer(dma_avail as usize);
797                }
798                tracing::trace!("dma transfer is complete");
799                self.bus_master_state.dma_state = None;
800                break;
801            }
802        }
803    }
804}
805
806impl ChangeDeviceState for IdeDevice {
807    fn start(&mut self) {}
808
809    async fn stop(&mut self) {}
810
811    async fn reset(&mut self) {
812        self.bus_master_pio_dynamic.unmap();
813        self.bus_master_state = BusMasterState::new();
814        for channel in &mut self.channels {
815            channel.reset();
816        }
817    }
818}
819
820impl ChipsetDevice for IdeDevice {
821    fn supports_pio(&mut self) -> Option<&mut dyn PortIoIntercept> {
822        Some(self)
823    }
824
825    fn supports_pci(&mut self) -> Option<&mut dyn PciConfigSpace> {
826        Some(self)
827    }
828
829    fn supports_poll_device(&mut self) -> Option<&mut dyn PollDevice> {
830        Some(self)
831    }
832}
833
834impl PollDevice for IdeDevice {
835    fn poll_device(&mut self, cx: &mut Context<'_>) {
836        for channel in &mut self.channels {
837            channel.poll_device(cx, &self.bus_master_state);
838        }
839    }
840}
841
842impl PortIoIntercept for IdeDevice {
843    fn io_read(&mut self, io_port: u16, data: &mut [u8]) -> IoResult {
844        match self.parse_port(io_port) {
845            Some((port, index)) => match port {
846                Port::Data => {
847                    self.channels[index].read_drive_data(data, &self.bus_master_state);
848                    IoResult::Ok
849                }
850                Port::Drive(register) => {
851                    data[0] =
852                        self.channels[index].read_drive_register(register, &self.bus_master_state);
853                    IoResult::Ok
854                }
855                Port::Enlightened => IoResult::Err(IoError::InvalidRegister),
856                Port::BusMaster(offset) => self.channels[index].read_bus_master_reg(offset, data),
857            },
858            None => IoResult::Err(IoError::InvalidRegister),
859        }
860    }
861
862    fn io_write(&mut self, io_port: u16, data: &[u8]) -> IoResult {
863        match self.parse_port(io_port) {
864            Some((port, index)) => match port {
865                Port::Data => {
866                    self.channels[index].write_drive_data(data, &self.bus_master_state);
867                    IoResult::Ok
868                }
869                Port::Drive(register) => {
870                    self.channels[index].write_drive_register(
871                        register,
872                        data[0],
873                        &self.bus_master_state,
874                    );
875                    IoResult::Ok
876                }
877                Port::Enlightened => {
878                    self.channels[index].enlightened_port_write(data, &self.bus_master_state)
879                }
880                Port::BusMaster(offset) => {
881                    self.channels[index].write_bus_master_reg(offset, data, &self.bus_master_state)
882                }
883            },
884            None => IoResult::Err(IoError::InvalidRegister),
885        }
886    }
887
888    fn get_static_regions(&mut self) -> &[(&str, RangeInclusive<u16>)] {
889        &[
890            (
891                "ide primary channel",
892                IdeIoPort::PRI_ENLIGHTENED.0..=IdeIoPort::PRI_STATUS_CMD.0,
893            ),
894            (
895                "ide primary channel control",
896                IdeIoPort::PRI_ALT_STATUS_DEVICE_CTL.0..=IdeIoPort::PRI_ALT_STATUS_DEVICE_CTL.0,
897            ),
898            (
899                "ide secondary channel",
900                IdeIoPort::SEC_ENLIGHTENED.0..=IdeIoPort::SEC_STATUS_CMD.0,
901            ),
902            (
903                "ide secondary channel control",
904                IdeIoPort::SEC_ALT_STATUS_DEVICE_CTL.0..=IdeIoPort::SEC_ALT_STATUS_DEVICE_CTL.0,
905            ),
906        ]
907    }
908}
909
910impl PciConfigSpace for IdeDevice {
911    fn pci_cfg_read(&mut self, offset: u16, value: &mut u32) -> IoResult {
912        *value = if offset < HEADER_TYPE_00_SIZE {
913            match HeaderType00(offset) {
914                HeaderType00::DEVICE_VENDOR => protocol::BX_PCI_ISA_BRIDGE_IDE_IDREG_VALUE,
915                HeaderType00::STATUS_COMMAND => self.bus_master_state.cmd_status_reg,
916                HeaderType00::CLASS_REVISION => protocol::BX_PCI_IDE_CLASS_WORD,
917                HeaderType00::BAR4 => self.bus_master_state.port_addr_reg,
918                offset => {
919                    tracing::debug!(?offset, "undefined type00 header read");
920                    return IoResult::Err(IoError::InvalidRegister);
921                }
922            }
923        } else {
924            match IdeConfigSpace(offset) {
925                IdeConfigSpace::PRIMARY_TIMING_REG_ADDR => self.bus_master_state.timing_reg,
926                IdeConfigSpace::SECONDARY_TIMING_REG_ADDR => {
927                    self.bus_master_state.secondary_timing_reg
928                }
929                IdeConfigSpace::UDMA_CTL_REG_ADDR => self.bus_master_state.dma_ctl_reg,
930                IdeConfigSpace::MANUFACTURE_ID_REG_ADDR => {
931                    // This is a private versioning register, and no one is supposed to use it.
932                    // But a real Triton chipset returns this value...
933                    0x00000F30
934                }
935                offset => {
936                    // Allow reads from undefined areas.
937                    tracing::trace!(?offset, "undefined ide pci config space read");
938                    return IoResult::Err(IoError::InvalidRegister);
939                }
940            }
941        };
942
943        tracing::trace!(?offset, value, "ide pci config space read");
944        IoResult::Ok
945    }
946
947    fn pci_cfg_write(&mut self, offset: u16, value: u32) -> IoResult {
948        if offset < HEADER_TYPE_00_SIZE {
949            let offset = HeaderType00(offset);
950            tracing::trace!(?offset, value, "ide pci config space write");
951
952            const BUS_MASTER_IO_ENABLE_MASK: u32 = Command::new()
953                .with_pio_enabled(true)
954                .with_bus_master(true)
955                .into_bits() as u32;
956
957            match offset {
958                HeaderType00::STATUS_COMMAND => {
959                    // Several bits are used to reset status bits when written as 1s.
960                    self.bus_master_state.cmd_status_reg &= !(0x38000000 & value);
961                    // Only allow writes to two bits (0 and 2). All other bits are read-only.
962                    self.bus_master_state.cmd_status_reg &= !BUS_MASTER_IO_ENABLE_MASK;
963
964                    self.bus_master_state.cmd_status_reg |= value & BUS_MASTER_IO_ENABLE_MASK;
965
966                    // Is the io address space enabled for bus mastering and is bus mastering enabled?
967                    if (self.bus_master_state.cmd_status_reg
968                        & protocol::CFCS_BUS_MASTER_IO_ENABLE_MASK)
969                        != protocol::CFCS_BUS_MASTER_IO_ENABLE_MASK
970                    {
971                        self.bus_master_pio_dynamic.unmap();
972                        tracing::trace!("disabling bus master io range");
973                    } else {
974                        // Install the callbacks for the current bus primary I/O port range (which
975                        // is dynamically configurable through PCI config registers)
976                        let first_port = (self.bus_master_state.port_addr_reg as u16) & 0xFFF0;
977                        tracing::trace!(?first_port, "enabling bus master range");
978
979                        // Change the io range for the bus master registers. Some of the bus master
980                        // registers can be cached on writes.
981                        self.bus_master_pio_dynamic.map(first_port);
982                    }
983                }
984                HeaderType00::BAR4 => {
985                    // Only allow writes to bits 4 to 15
986                    self.bus_master_state.port_addr_reg =
987                        (value & 0x0000FFF0) | DEFAULT_BUS_MASTER_PORT_ADDR_REG;
988                }
989                _ => tracing::debug!(?offset, "undefined type00 header write"),
990            }
991        } else {
992            let offset = IdeConfigSpace(offset);
993            tracing::trace!(?offset, value, "ide pci config space write");
994
995            match offset {
996                IdeConfigSpace::PRIMARY_TIMING_REG_ADDR => self.bus_master_state.timing_reg = value,
997                IdeConfigSpace::SECONDARY_TIMING_REG_ADDR => {
998                    self.bus_master_state.secondary_timing_reg = value
999                }
1000                IdeConfigSpace::UDMA_CTL_REG_ADDR => self.bus_master_state.dma_ctl_reg = value,
1001                _ => tracing::trace!(?offset, "undefined ide pci config space write"),
1002            }
1003        }
1004
1005        IoResult::Ok
1006    }
1007
1008    fn suggested_bdf(&mut self) -> Option<(u8, u8, u8)> {
1009        Some((0, 7, 1)) // as per PIIX4 spec
1010    }
1011}
1012
1013/// IDE Channel
1014enum ChannelType {
1015    Primary,
1016    Secondary,
1017}
1018
1019#[derive(Inspect)]
1020#[inspect(tag = "drive_type")]
1021pub enum DriveMedia {
1022    HardDrive(#[inspect(rename = "backend")] Disk),
1023    OpticalDrive(#[inspect(rename = "backend")] Arc<dyn AsyncScsiDisk>),
1024}
1025
1026impl DriveMedia {
1027    pub fn hard_disk(disk: Disk) -> Self {
1028        DriveMedia::HardDrive(disk)
1029    }
1030
1031    pub fn optical_disk(scsi_disk: Arc<dyn AsyncScsiDisk>) -> Self {
1032        DriveMedia::OpticalDrive(scsi_disk)
1033    }
1034}
1035
1036#[derive(Debug, Default, Inspect)]
1037struct ChannelState {
1038    current_drive_idx: usize,
1039    shadow_adapter_control_reg: u8,
1040    shadow_features_reg: u8,
1041}
1042
1043#[derive(InspectMut)]
1044struct Channel {
1045    #[inspect(mut, with = "inspect_drives")]
1046    drives: [Option<DiskDrive>; 2],
1047    interrupt: LineInterrupt,
1048    state: ChannelState,
1049    bus_master_state: ChannelBusMasterState,
1050    enlightened_write: Option<EnlightenedWrite>,
1051    guest_memory: GuestMemory,
1052    #[inspect(skip)]
1053    channel: u8,
1054}
1055
1056fn inspect_drives(drives: &mut [Option<DiskDrive>]) -> impl '_ + InspectMut {
1057    inspect::adhoc_mut(|req| {
1058        let mut resp = req.respond();
1059        for (i, drive) in drives.iter_mut().enumerate() {
1060            resp.field_mut(&i.to_string(), drive);
1061        }
1062    })
1063}
1064
1065impl Channel {
1066    fn new(
1067        channel_drives: [Option<DriveMedia>; 2],
1068        channel_type: ChannelType,
1069        interrupt: LineInterrupt,
1070        guest_memory: GuestMemory,
1071    ) -> Result<Self, NewDeviceError> {
1072        let [primary_media, secondary_media] = channel_drives;
1073
1074        let channel_number = match channel_type {
1075            ChannelType::Primary => 0,
1076            ChannelType::Secondary => 1,
1077        };
1078
1079        Ok(Self {
1080            drives: [
1081                primary_media
1082                    .map(|media| {
1083                        DiskDrive::new(
1084                            media,
1085                            IdePath {
1086                                channel: channel_number,
1087                                drive: 0,
1088                            },
1089                        )
1090                    })
1091                    .transpose()?,
1092                secondary_media
1093                    .map(|media| {
1094                        DiskDrive::new(
1095                            media,
1096                            IdePath {
1097                                channel: channel_number,
1098                                drive: 1,
1099                            },
1100                        )
1101                    })
1102                    .transpose()?,
1103            ],
1104            interrupt,
1105            state: ChannelState::default(),
1106            bus_master_state: ChannelBusMasterState::default(),
1107            enlightened_write: None,
1108            guest_memory,
1109            channel: channel_number,
1110        })
1111    }
1112
1113    fn reset(&mut self) {
1114        tracelimit::info_ratelimited!(channel = self.channel, "channel reset");
1115        self.interrupt.set_level(false);
1116        self.state = ChannelState::default();
1117        self.bus_master_state = ChannelBusMasterState::default();
1118        for drive in self.drives.iter_mut().flatten() {
1119            drive.reset();
1120        }
1121    }
1122
1123    fn poll_device(&mut self, cx: &mut Context<'_>, bus_master_state: &BusMasterState) {
1124        for drive in self.drives.iter_mut().flatten() {
1125            drive.poll_device(cx);
1126        }
1127        self.post_drive_access(bus_master_state);
1128    }
1129
1130    fn current_drive_status(&mut self) -> Option<Status> {
1131        if let Some(drive) = &mut self.drives[self.state.current_drive_idx] {
1132            let status = drive.read_register(DriveRegister::AlternateStatusDeviceControl);
1133            Some(Status::from_bits(status))
1134        } else {
1135            None
1136        }
1137    }
1138
1139    fn drive_status(&mut self, drive_index: usize) -> Status {
1140        // Reading the values from Status register without clearing any bits
1141        assert!(self.drives[drive_index].is_some());
1142        let status = self.drives[drive_index]
1143            .as_mut()
1144            .unwrap()
1145            .read_register(DriveRegister::AlternateStatusDeviceControl);
1146        Status::from_bits(status)
1147    }
1148
1149    fn current_drive_type(&self) -> Option<DriveType> {
1150        self.drives[self.state.current_drive_idx]
1151            .as_ref()
1152            .map(|drive| drive.drive_type())
1153    }
1154
1155    fn drive_type(&mut self, drive_index: usize) -> DriveType {
1156        assert!(self.drives[drive_index].is_some());
1157        self.drives[drive_index]
1158            .as_ref()
1159            .map(|drive| drive.drive_type())
1160            .unwrap()
1161    }
1162
1163    fn post_drive_access(&mut self, bus_master_state: &BusMasterState) {
1164        // DMA first, since this may affect the other operations.
1165        self.perform_dma_memory_phase();
1166
1167        // Enlightened writes.
1168        if let Some(enlightened_write) = &self.enlightened_write {
1169            let drive_index = match enlightened_write {
1170                EnlightenedWrite::Hard(enlightened_hdd_write) => enlightened_hdd_write.drive_index,
1171                EnlightenedWrite::Optical(enlightened_cd_write) => enlightened_cd_write.drive_index,
1172            };
1173
1174            let status = self.drive_status(drive_index);
1175            let completed = match self.drive_type(drive_index) {
1176                DriveType::Hard => !(status.bsy() || status.drq()),
1177                DriveType::Optical => status.drdy(),
1178            };
1179            if completed {
1180                // The command is done.
1181                let write = self.enlightened_write.take().unwrap();
1182                match write {
1183                    EnlightenedWrite::Hard(write) => {
1184                        self.complete_enlightened_hdd_write(write, bus_master_state)
1185                    }
1186                    EnlightenedWrite::Optical(write) => {
1187                        self.complete_enlightened_cd_write(write, bus_master_state)
1188                    }
1189                }
1190            }
1191        }
1192
1193        // Update interrupt state.
1194        let interrupt = self
1195            .drives
1196            .iter()
1197            .flatten()
1198            .any(|drive| drive.interrupt_pending());
1199        if interrupt {
1200            tracing::trace!(channel = self.channel, interrupt, "post_drive_access");
1201            self.bus_master_state.status_reg.set_interrupt(true);
1202        }
1203        self.interrupt.set_level(interrupt);
1204    }
1205
1206    /// Returns Ok(true) if an interrupt should be delivered. The whole result
1207    /// of this should be passed to `io_port_completion`
1208    fn read_drive_register(
1209        &mut self,
1210        port: DriveRegister,
1211        bus_master_state: &BusMasterState,
1212    ) -> u8 {
1213        // Call the selected drive, but fall back to drive 0 if drive 1 is not present.
1214        let mut drive = self.drives[self.state.current_drive_idx].as_mut();
1215        if drive.is_none() {
1216            drive = self.drives[0].as_mut();
1217        }
1218
1219        let data = if let Some(drive) = drive {
1220            // Fill with zeroes if the drive doesn't write it (which can happen for some registers if
1221            // the device is not selected).
1222            drive.read_register(port)
1223        } else {
1224            // Returns 0x7f if neither device is present. This is align with ATA-5 "Devices shall not have a pull-up resistor on DD7"
1225            // Note: Legacy implementation returns 0xff in this case.
1226            0x7f
1227        };
1228
1229        tracing::trace!(?port, ?data, channel = self.channel, "io port read");
1230        self.post_drive_access(bus_master_state);
1231        data
1232    }
1233
1234    /// Returns Ok(true) if an interrupt should be delivered. The whole result
1235    /// of this should be passed to `io_port_completion` except in the case of
1236    /// the enlightened port path.
1237    fn write_drive_register(
1238        &mut self,
1239        port: DriveRegister,
1240        data: u8,
1241        bus_master_state: &BusMasterState,
1242    ) {
1243        tracing::trace!(?port, ?data, channel = self.channel, "io port write");
1244
1245        match port {
1246            DriveRegister::DeviceHead => {
1247                // Shadow the device bit for use in the DMA engine.
1248                self.state.current_drive_idx = ((data >> 4) & 1) as usize;
1249            }
1250            DriveRegister::AlternateStatusDeviceControl => {
1251                // Save this for restoring in the enlightened path.
1252                self.state.shadow_adapter_control_reg = data;
1253                let v = DeviceControlReg::from_bits_truncate(data);
1254                if v.reset() && (self.drives[0].is_some() || self.drives[1].is_some()) {
1255                    self.state = ChannelState::default();
1256                }
1257            }
1258            DriveRegister::ErrorFeatures => {
1259                // Save this for restoring in the enlightened path.
1260                self.state.shadow_features_reg = data;
1261            }
1262            _ => {}
1263        }
1264
1265        // Call both drives.
1266        if let Some(drive) = &mut self.drives[1] {
1267            drive.write_register(port, data);
1268        }
1269        if let Some(drive) = &mut self.drives[0] {
1270            drive.write_register(port, data);
1271        }
1272
1273        self.post_drive_access(bus_master_state);
1274    }
1275
1276    fn read_drive_data(&mut self, data: &mut [u8], bus_master_state: &BusMasterState) {
1277        // Call the selected drive, but fall back to drive 0 if drive 1 is not present.
1278        let mut drive = self.drives[self.state.current_drive_idx].as_mut();
1279        if drive.is_none() {
1280            drive = self.drives[0].as_mut();
1281        }
1282
1283        data.fill(0xff);
1284        // DD7 must be low to conform to the ATA spec.
1285        data[0] = 0x7f;
1286
1287        if let Some(drive) = drive {
1288            drive.pio_read(data);
1289        };
1290
1291        self.post_drive_access(bus_master_state);
1292    }
1293
1294    fn skip_drive_data(&mut self, mut len: usize, bus_master_state: &BusMasterState) {
1295        let mut buf = [0; 512];
1296        while len > 0 {
1297            let this_len = len.min(buf.len());
1298            let buf = &mut buf[..this_len];
1299            self.read_drive_data(buf, bus_master_state);
1300            len -= buf.len();
1301        }
1302    }
1303
1304    fn write_drive_data(&mut self, data: &[u8], bus_master_state: &BusMasterState) {
1305        if let Some(drive) = &mut self.drives[0] {
1306            drive.pio_write(data);
1307        }
1308        if let Some(drive) = &mut self.drives[1] {
1309            drive.pio_write(data);
1310        }
1311        self.post_drive_access(bus_master_state);
1312    }
1313
1314    fn read_bus_master_reg(&mut self, bus_master_reg: BusMasterReg, data: &mut [u8]) -> IoResult {
1315        let data_len = data.len();
1316        match bus_master_reg {
1317            BusMasterReg::COMMAND => match data_len {
1318                1 | 2 => data.copy_from_slice(
1319                    &self.bus_master_state.command_reg.into_bits().to_ne_bytes()[..data_len],
1320                ),
1321                _ => return IoResult::Err(IoError::InvalidAccessSize),
1322            },
1323            BusMasterReg::STATUS => {
1324                let mut status = self.bus_master_state.status_reg;
1325
1326                if self.bus_master_state.dma_state.is_some() {
1327                    status.set_active(true);
1328                }
1329
1330                match data_len {
1331                    1 | 2 => data.copy_from_slice(&status.into_bits().to_ne_bytes()[..data_len]),
1332                    _ => return IoResult::Err(IoError::InvalidAccessSize),
1333                }
1334            }
1335            BusMasterReg::TABLE_PTR => match data_len {
1336                2 | 4 => data.copy_from_slice(
1337                    &self.bus_master_state.desc_table_ptr.to_ne_bytes()[..data_len],
1338                ),
1339                _ => return IoResult::Err(IoError::InvalidAccessSize),
1340            },
1341            BusMasterReg::TABLE_PTR2 => {
1342                if data_len == 2 {
1343                    data.copy_from_slice(&self.bus_master_state.desc_table_ptr.to_ne_bytes()[2..4]);
1344                } else {
1345                    return IoResult::Err(IoError::InvalidAccessSize);
1346                }
1347            }
1348            _ => return IoResult::Err(IoError::InvalidRegister),
1349        }
1350
1351        tracing::trace!(?bus_master_reg, ?data, "bus master register read");
1352        IoResult::Ok
1353    }
1354
1355    fn write_bus_master_reg(
1356        &mut self,
1357        bus_master_reg: BusMasterReg,
1358        data: &[u8],
1359        bus_master_state: &BusMasterState,
1360    ) -> IoResult {
1361        let value: u64 = match data.len() {
1362            1 => u8::from_ne_bytes(data.as_bytes().try_into().unwrap()).into(),
1363            2 => u16::from_ne_bytes(data.as_bytes().try_into().unwrap()).into(),
1364            4 => u32::from_ne_bytes(data.as_bytes().try_into().unwrap()).into(),
1365            _ => return IoResult::Err(IoError::InvalidAccessSize),
1366        };
1367
1368        tracing::trace!(?bus_master_reg, value, "bus master register write");
1369
1370        match bus_master_reg {
1371            BusMasterReg::COMMAND => {
1372                // A write to this register will begin/end a dma transfer
1373                // and control its direction (write vs. read).
1374                if data.len() > 2 {
1375                    return IoResult::Err(IoError::InvalidAccessSize);
1376                }
1377
1378                let old_value = self.bus_master_state.command_reg;
1379                // TODO: make sure all bits that should be preserved are defined.
1380                let mut new_value = BusMasterCommandReg::from_bits_truncate(value as u32);
1381
1382                // The read/write bit is marked as read-only when dma is active.
1383                if old_value.start() {
1384                    // Set the new value of the read/write flag to match the
1385                    // existing value, regardless of the new value of the flag.
1386                    new_value.set_write(old_value.write());
1387                    if !new_value.start() {
1388                        self.bus_master_state.dma_state = None
1389                    }
1390                } else if new_value.start() {
1391                    self.bus_master_state.dma_state = Some(Default::default());
1392                };
1393
1394                self.bus_master_state.command_reg = new_value;
1395            }
1396            BusMasterReg::STATUS => {
1397                if data.len() > 2 {
1398                    return IoResult::Err(IoError::InvalidAccessSize);
1399                }
1400
1401                let value = BusMasterStatusReg::from_bits_truncate(value as u32);
1402                let old_value = self.bus_master_state.status_reg;
1403                let mut new_value = old_value.with_settable(value.settable());
1404
1405                // These bits are reset if a one is written
1406                if value.interrupt() {
1407                    new_value.set_interrupt(false);
1408                }
1409                if value.dma_error() {
1410                    new_value.set_dma_error(false);
1411                }
1412
1413                tracing::trace!(?old_value, ?new_value, "set bus master status");
1414                self.bus_master_state.status_reg = new_value;
1415            }
1416            BusMasterReg::TABLE_PTR => {
1417                if data.len() < 2 {
1418                    return IoResult::Err(IoError::InvalidAccessSize);
1419                }
1420
1421                if data.len() == 4 {
1422                    // Writing the whole 32-bits pointer with the lowest 2 bits zeroed-out.
1423                    self.bus_master_state.desc_table_ptr = value as u32 & 0xffff_fffc;
1424                } else {
1425                    // Writing the low 16-bits of the 32-bits pointer, with the
1426                    // lowest 2 bits zeroed-out
1427                    self.bus_master_state.desc_table_ptr = (self.bus_master_state.desc_table_ptr
1428                        & 0xffff_0000)
1429                        | (value as u32 & 0x0000_fffc);
1430                }
1431            }
1432            BusMasterReg::TABLE_PTR2 => {
1433                // Documentation doesn't mention this offset. Apparently OS/2 writes to this port.
1434                // Port not written to in recent Windows boot.
1435                self.bus_master_state.desc_table_ptr = (self.bus_master_state.desc_table_ptr
1436                    & 0xffff)
1437                    | ((value as u32 & 0xffff) << 16);
1438            }
1439            _ => return IoResult::Err(IoError::InvalidRegister),
1440        }
1441
1442        self.post_drive_access(bus_master_state);
1443        IoResult::Ok
1444    }
1445}
1446
1447#[derive(Debug, Copy, Clone, PartialEq)]
1448enum DriveType {
1449    Hard,
1450    Optical,
1451}
1452
1453#[derive(Debug, Default, Inspect)]
1454struct DmaState {
1455    descriptor_idx: u8,
1456    transfer_complete: bool,
1457    transfer_bytes_left: u32,
1458    transfer_base_addr: u64,
1459}
1460
1461mod save_restore {
1462    use super::*;
1463    use vmcore::save_restore::RestoreError;
1464    use vmcore::save_restore::SaveError;
1465    use vmcore::save_restore::SaveRestore;
1466
1467    mod state {
1468        use crate::drive::save_restore::state::SavedDriveState;
1469        use mesh::payload::Protobuf;
1470        use vmcore::save_restore::SavedStateRoot;
1471
1472        #[derive(Protobuf)]
1473        #[mesh(package = "storage.ide.controller")]
1474        pub struct SavedBusMasterState {
1475            #[mesh(1)]
1476            pub cmd_status_reg: u32,
1477            #[mesh(2)]
1478            pub port_addr_reg: u32,
1479            #[mesh(3)]
1480            pub timing_reg: u32,
1481            #[mesh(4)]
1482            pub secondary_timing_reg: u32,
1483            #[mesh(5)]
1484            pub dma_ctl_reg: u32,
1485        }
1486
1487        #[derive(Protobuf, SavedStateRoot)]
1488        #[mesh(package = "storage.ide.controller")]
1489        pub struct SavedState {
1490            #[mesh(1)]
1491            pub bus_master: SavedBusMasterState,
1492            #[mesh(2)]
1493            pub channel0: SavedChannelState,
1494            #[mesh(3)]
1495            pub channel1: SavedChannelState,
1496        }
1497
1498        #[derive(Protobuf)]
1499        #[mesh(package = "storage.ide.controller")]
1500        pub struct SavedDmaState {
1501            #[mesh(1)]
1502            pub descriptor_idx: u8,
1503            #[mesh(2)]
1504            pub transfer_complete: bool,
1505            #[mesh(3)]
1506            pub transfer_bytes_left: u32,
1507            #[mesh(4)]
1508            pub transfer_base_addr: u64,
1509        }
1510
1511        #[derive(Protobuf)]
1512        #[mesh(package = "storage.ide.controller")]
1513        pub struct SavedChannelBusMasterState {
1514            #[mesh(1)]
1515            pub command_reg: u32,
1516            #[mesh(2)]
1517            pub status_reg: u32,
1518            #[mesh(3)]
1519            pub desc_table_ptr: u32,
1520            #[mesh(4)]
1521            pub dma_state: Option<SavedDmaState>,
1522            #[mesh(5)]
1523            pub dma_error: bool,
1524        }
1525
1526        #[derive(Protobuf)]
1527        #[mesh(package = "storage.ide.controller")]
1528        pub struct SavedChannelState {
1529            #[mesh(1)]
1530            pub current_drive_idx: u8,
1531            #[mesh(2)]
1532            pub shadow_adapter_control_reg: u8,
1533            #[mesh(3)]
1534            pub shadow_features_reg: u8,
1535            #[mesh(4)]
1536            pub bus_master: SavedChannelBusMasterState,
1537            #[mesh(5)]
1538            pub drive0: Option<SavedDriveState>,
1539            #[mesh(6)]
1540            pub drive1: Option<SavedDriveState>,
1541        }
1542    }
1543
1544    impl SaveRestore for IdeDevice {
1545        type SavedState = state::SavedState;
1546
1547        fn save(&mut self) -> Result<Self::SavedState, SaveError> {
1548            let BusMasterState {
1549                cmd_status_reg,
1550                port_addr_reg,
1551                timing_reg,
1552                secondary_timing_reg,
1553                dma_ctl_reg,
1554            } = self.bus_master_state;
1555
1556            let bus_master = state::SavedBusMasterState {
1557                cmd_status_reg,
1558                port_addr_reg,
1559                timing_reg,
1560                secondary_timing_reg,
1561                dma_ctl_reg,
1562            };
1563
1564            let saved_state = state::SavedState {
1565                bus_master,
1566                channel0: self.channels[0].save()?,
1567                channel1: self.channels[1].save()?,
1568            };
1569
1570            Ok(saved_state)
1571        }
1572
1573        fn restore(&mut self, state: Self::SavedState) -> Result<(), RestoreError> {
1574            let state::SavedState {
1575                bus_master:
1576                    state::SavedBusMasterState {
1577                        cmd_status_reg,
1578                        port_addr_reg,
1579                        timing_reg,
1580                        secondary_timing_reg,
1581                        dma_ctl_reg,
1582                    },
1583                channel0,
1584                channel1,
1585            } = state;
1586
1587            self.bus_master_state = BusMasterState {
1588                cmd_status_reg,
1589                port_addr_reg,
1590                timing_reg,
1591                secondary_timing_reg,
1592                dma_ctl_reg,
1593            };
1594
1595            self.channels[0].restore(channel0)?;
1596            self.channels[1].restore(channel1)?;
1597
1598            Ok(())
1599        }
1600    }
1601
1602    #[derive(Debug, Error)]
1603    enum ChannelRestoreError {
1604        #[error("missing drive for state")]
1605        MissingDriveForState,
1606        #[error("missing state for drive")]
1607        MissingStateForDrive,
1608    }
1609
1610    impl Channel {
1611        fn save(&mut self) -> Result<state::SavedChannelState, SaveError> {
1612            // We wait for the completion of deferred IOs as part of pause.
1613            assert!(self.enlightened_write.is_none());
1614
1615            let ChannelState {
1616                current_drive_idx,
1617                shadow_adapter_control_reg,
1618                shadow_features_reg,
1619            } = self.state;
1620
1621            let ChannelBusMasterState {
1622                command_reg,
1623                status_reg,
1624                desc_table_ptr,
1625                dma_state,
1626                dma_error,
1627            } = &self.bus_master_state;
1628
1629            let saved_state = state::SavedChannelState {
1630                current_drive_idx: current_drive_idx as u8,
1631                shadow_adapter_control_reg,
1632                shadow_features_reg,
1633                bus_master: state::SavedChannelBusMasterState {
1634                    command_reg: command_reg.into_bits(),
1635                    status_reg: status_reg.into_bits(),
1636                    desc_table_ptr: *desc_table_ptr,
1637                    dma_state: dma_state.as_ref().map(|dma| {
1638                        let DmaState {
1639                            descriptor_idx,
1640                            transfer_complete,
1641                            transfer_bytes_left,
1642                            transfer_base_addr,
1643                        } = dma;
1644
1645                        state::SavedDmaState {
1646                            descriptor_idx: *descriptor_idx,
1647                            transfer_complete: *transfer_complete,
1648                            transfer_bytes_left: *transfer_bytes_left,
1649                            transfer_base_addr: *transfer_base_addr,
1650                        }
1651                    }),
1652                    dma_error: *dma_error,
1653                },
1654                drive0: self.drives[0]
1655                    .as_mut()
1656                    .map(|drive| drive.save())
1657                    .transpose()?,
1658                drive1: self.drives[1]
1659                    .as_mut()
1660                    .map(|drive| drive.save())
1661                    .transpose()?,
1662            };
1663
1664            Ok(saved_state)
1665        }
1666
1667        fn restore(&mut self, state: state::SavedChannelState) -> Result<(), RestoreError> {
1668            let state::SavedChannelState {
1669                current_drive_idx,
1670                shadow_adapter_control_reg,
1671                shadow_features_reg,
1672                bus_master:
1673                    state::SavedChannelBusMasterState {
1674                        command_reg,
1675                        status_reg,
1676                        desc_table_ptr,
1677                        dma_state,
1678                        dma_error,
1679                    },
1680                drive0,
1681                drive1,
1682            } = state;
1683
1684            self.state = ChannelState {
1685                current_drive_idx: current_drive_idx as usize,
1686                shadow_adapter_control_reg,
1687                shadow_features_reg,
1688            };
1689
1690            self.bus_master_state = ChannelBusMasterState {
1691                command_reg: BusMasterCommandReg::from_bits(command_reg),
1692                status_reg: BusMasterStatusReg::from_bits(status_reg),
1693                desc_table_ptr,
1694                dma_state: dma_state.map(|dma| {
1695                    let state::SavedDmaState {
1696                        descriptor_idx,
1697                        transfer_complete,
1698                        transfer_bytes_left,
1699                        transfer_base_addr,
1700                    } = dma;
1701
1702                    DmaState {
1703                        descriptor_idx,
1704                        transfer_complete,
1705                        transfer_bytes_left,
1706                        transfer_base_addr,
1707                    }
1708                }),
1709                dma_error,
1710            };
1711
1712            for (drive, state) in self.drives.iter_mut().zip([drive0, drive1]) {
1713                match (drive, state) {
1714                    (Some(drive), Some(state)) => drive.restore(state)?,
1715                    (None, None) => {}
1716                    (Some(_), None) => {
1717                        return Err(RestoreError::InvalidSavedState(
1718                            ChannelRestoreError::MissingStateForDrive.into(),
1719                        ));
1720                    }
1721                    (None, Some(_)) => {
1722                        return Err(RestoreError::InvalidSavedState(
1723                            ChannelRestoreError::MissingDriveForState.into(),
1724                        ));
1725                    }
1726                }
1727            }
1728
1729            Ok(())
1730        }
1731    }
1732}
1733
1734#[cfg(test)]
1735mod tests {
1736    use super::*;
1737    use crate::IdeIoPort;
1738    use crate::protocol::BusMasterDmaDesc;
1739    use crate::protocol::DeviceHeadReg;
1740    use crate::protocol::IdeCommand;
1741    use chipset_device::pio::ExternallyManagedPortIoIntercepts;
1742    use disk_file::FileDisk;
1743    use pal_async::async_test;
1744    use scsidisk::atapi_scsi::AtapiScsiDisk;
1745    use scsidisk::scsidvd::SimpleScsiDvd;
1746    use std::fs::File;
1747    use std::future::poll_fn;
1748    use std::io::Read;
1749    use std::io::Write;
1750    use std::task::Poll;
1751    use tempfile::NamedTempFile;
1752    use test_with_tracing::test;
1753    use zerocopy::FromBytes;
1754    use zerocopy::FromZeros;
1755    use zerocopy::IntoBytes;
1756
1757    #[derive(Debug, Inspect)]
1758    struct MediaGeometry {
1759        sectors_per_track: u32,
1760        cylinder_count: u32,
1761        head_count: u32,
1762        total_sectors: u64,
1763    }
1764
1765    impl MediaGeometry {
1766        fn new(total_sectors: u64, sector_size: u32) -> Result<Self, NewDeviceError> {
1767            if total_sectors > protocol::MAX_BYTES_48BIT_LBA / sector_size as u64 {
1768                return Err(NewDeviceError::DiskTooLarge(
1769                    total_sectors * sector_size as u64,
1770                ));
1771            }
1772            let hard_drive_sectors = total_sectors.min(protocol::MAX_CHS_SECTORS as u64);
1773            let mut sectors_per_track;
1774            let mut cylinders_times_heads;
1775            let mut head_count;
1776
1777            if hard_drive_sectors > (16 * 63 * 0xFFFF) {
1778                sectors_per_track = 255;
1779                head_count = 16;
1780                cylinders_times_heads = hard_drive_sectors / (sectors_per_track as u64);
1781            } else {
1782                sectors_per_track = 17;
1783                cylinders_times_heads = hard_drive_sectors / (sectors_per_track as u64);
1784
1785                head_count = std::cmp::max((cylinders_times_heads as u32).div_ceil(1024), 4);
1786
1787                if (cylinders_times_heads >= (head_count as u64) * 1024) || head_count > 16 {
1788                    // Always use 16 heads
1789                    head_count = 16;
1790                    sectors_per_track = 31;
1791                    cylinders_times_heads = hard_drive_sectors / (sectors_per_track as u64);
1792                }
1793
1794                if cylinders_times_heads >= (head_count as u64) * 1024 {
1795                    // Always use 16 heads
1796                    head_count = 16;
1797                    sectors_per_track = 63;
1798                    cylinders_times_heads = hard_drive_sectors / (sectors_per_track as u64);
1799                }
1800            }
1801            Ok(MediaGeometry {
1802                sectors_per_track,
1803                cylinder_count: (cylinders_times_heads / (head_count as u64)) as u32,
1804                head_count,
1805                total_sectors,
1806            })
1807        }
1808    }
1809
1810    struct CommandParams {
1811        sector_count: u8,
1812        sector_num: u8,
1813        cylinder_lsb: u8,
1814        cylinder_msb: u8,
1815        device_head: u8,
1816    }
1817
1818    #[expect(dead_code)]
1819    enum Addressing {
1820        Chs,
1821        Lba28Bit,
1822        Lba48Bit,
1823    }
1824
1825    fn ide_test_setup(
1826        guest_memory: Option<GuestMemory>,
1827        drive_type: DriveType,
1828    ) -> (IdeDevice, File, Vec<u32>, MediaGeometry) {
1829        let test_guest_mem = match guest_memory {
1830            Some(test_gm) => test_gm,
1831            None => GuestMemory::allocate(16 * 1024),
1832        };
1833
1834        // prep file (write 4GB, numbers 1-1048576 (1GB))
1835        let temp_file = NamedTempFile::new().unwrap();
1836        let mut handle1 = temp_file.reopen().unwrap();
1837        let handle2 = temp_file.reopen().unwrap();
1838        let data = (0..0x100000_u32).collect::<Vec<_>>();
1839        handle1.write_all(data.as_bytes()).unwrap();
1840
1841        let disk = Disk::new(FileDisk::open(handle1, false).unwrap()).unwrap();
1842        let geometry = MediaGeometry::new(disk.sector_count(), disk.sector_size()).unwrap();
1843
1844        let media = match drive_type {
1845            DriveType::Hard => DriveMedia::hard_disk(disk),
1846            DriveType::Optical => DriveMedia::optical_disk(Arc::new(AtapiScsiDisk::new(Arc::new(
1847                SimpleScsiDvd::new(Some(disk)),
1848            )))),
1849        };
1850
1851        let ide_device = IdeDevice::new(
1852            test_guest_mem,
1853            &mut ExternallyManagedPortIoIntercepts,
1854            [Some(media), None],
1855            [None, None],
1856            LineInterrupt::detached(),
1857            LineInterrupt::detached(),
1858        )
1859        .unwrap();
1860
1861        (ide_device, handle2, data, geometry)
1862    }
1863
1864    // IDE Test Host protocol functions
1865    fn get_status(ide_controller: &mut IdeDevice, dev_path: &IdePath) -> Status {
1866        let mut data = [0_u8; 1];
1867        ide_controller
1868            .io_read(
1869                io_port(IdeIoPort::PRI_STATUS_CMD, dev_path.channel.into()),
1870                &mut data,
1871            )
1872            .unwrap();
1873
1874        Status::from_bits(data[0])
1875    }
1876
1877    async fn check_status_loop(ide_device: &mut IdeDevice, dev_path: &IdePath) -> Status {
1878        // loop until device is not busy and is ready to transfer data.
1879        wait_for(ide_device, |ide_device| {
1880            let status: Status = get_status(ide_device, dev_path);
1881            (!status.bsy() && !status.drq()).then_some(status)
1882        })
1883        .await
1884    }
1885
1886    async fn check_command_ready(ide_device: &mut IdeDevice, dev_path: &IdePath) -> Status {
1887        // loop until device is not busy and is ready to transfer data.
1888        wait_for(ide_device, |ide_device| {
1889            let status: Status = get_status(ide_device, dev_path);
1890            (!status.bsy() && status.drdy()).then_some(status)
1891        })
1892        .await
1893    }
1894
1895    fn io_port(io_port: IdeIoPort, channel_idx: usize) -> u16 {
1896        if channel_idx == 0 {
1897            io_port.0
1898        } else {
1899            io_port.0 - IdeIoPort::PRI_DATA.0 + IdeIoPort::SEC_DATA.0
1900        }
1901    }
1902
1903    // Host: setup command parameters by writing to features, sector count/number,
1904    // cylinder low/high, dev/head registers
1905    fn write_command_params(
1906        controller: &mut IdeDevice,
1907        dev_path: &IdePath,
1908        sector: u32,
1909        sector_count: u8,
1910        addr: Addressing,
1911        geometry: &MediaGeometry,
1912    ) {
1913        let channel_idx: usize = dev_path.channel as usize;
1914
1915        let io_params = match addr {
1916            Addressing::Chs => {
1917                let sectors_per_track = geometry.sectors_per_track;
1918                let head_count = geometry.head_count;
1919
1920                let sector_num: u8 = ((sector % sectors_per_track) as u8) + 1;
1921                let cylinders: u16 = (sector / (head_count * sectors_per_track)) as u16;
1922                let cylinder_lsb: u8 = cylinders as u8;
1923                let cylinder_msb: u8 = (cylinders >> 8) as u8;
1924                let device_head: u8 = (sector / sectors_per_track % head_count) as u8;
1925
1926                CommandParams {
1927                    sector_count,
1928                    sector_num,
1929                    cylinder_lsb,
1930                    cylinder_msb,
1931                    device_head,
1932                }
1933            }
1934            Addressing::Lba28Bit => {
1935                let sector_num = sector as u8;
1936                let cylinder = (sector & 0x00FF_FF00) >> 8;
1937                let cylinder_lsb: u8 = cylinder as u8;
1938                let cylinder_msb: u8 = (cylinder >> 8) as u8;
1939                let device_head = DeviceHeadReg::new()
1940                    .with_head((sector >> 24) as u8)
1941                    .with_lba(true)
1942                    .into();
1943
1944                CommandParams {
1945                    sector_count,
1946                    sector_num,
1947                    cylinder_lsb,
1948                    cylinder_msb,
1949                    device_head,
1950                }
1951            }
1952            Addressing::Lba48Bit => todo!(),
1953        };
1954
1955        controller
1956            .io_write(
1957                io_port(IdeIoPort::PRI_SECTOR_COUNT, channel_idx),
1958                &[io_params.sector_count],
1959            )
1960            .unwrap();
1961        controller
1962            .io_write(
1963                io_port(IdeIoPort::PRI_SECTOR_NUM, channel_idx),
1964                &[io_params.sector_num],
1965            )
1966            .unwrap();
1967        controller
1968            .io_write(
1969                io_port(IdeIoPort::PRI_CYLINDER_LSB, channel_idx),
1970                &[io_params.cylinder_lsb],
1971            )
1972            .unwrap();
1973        controller
1974            .io_write(
1975                io_port(IdeIoPort::PRI_CYLINDER_MSB, channel_idx),
1976                &[io_params.cylinder_msb],
1977            )
1978            .unwrap();
1979        controller
1980            .io_write(
1981                io_port(IdeIoPort::PRI_DEVICE_HEAD, channel_idx),
1982                &[io_params.device_head],
1983            )
1984            .unwrap();
1985    }
1986
1987    // Host: Execute device selection protocol
1988    async fn device_select(ide_controller: &mut IdeDevice, dev_path: &IdePath) {
1989        check_status_loop(ide_controller, dev_path).await;
1990
1991        let dev_idx: u8 = dev_path.drive;
1992        ide_controller
1993            .io_write(
1994                io_port(IdeIoPort::PRI_DEVICE_HEAD, dev_path.channel.into()),
1995                &[dev_idx],
1996            )
1997            .unwrap();
1998
1999        check_status_loop(ide_controller, dev_path).await;
2000    }
2001
2002    // Host: Write command code to command register, wait 400ns before reading status register
2003    fn execute_command(ide_controller: &mut IdeDevice, dev_path: &IdePath, command: u8) {
2004        ide_controller
2005            .io_write(
2006                io_port(IdeIoPort::PRI_STATUS_CMD, dev_path.channel.into()),
2007                &[command],
2008            )
2009            .unwrap();
2010    }
2011
2012    fn execute_soft_reset_command(ide_controller: &mut IdeDevice, dev_path: &IdePath, command: u8) {
2013        ide_controller
2014            .io_write(
2015                io_port(
2016                    IdeIoPort::PRI_ALT_STATUS_DEVICE_CTL,
2017                    dev_path.channel.into(),
2018                ),
2019                &[command],
2020            )
2021            .unwrap();
2022    }
2023
2024    fn prep_ide_channel(ide_controller: &mut IdeDevice, drive_type: DriveType, dev_path: &IdePath) {
2025        match drive_type {
2026            DriveType::Hard => {
2027                // SET MULTIPLE MODE - sets number of sectors per block to 128
2028                // this is needed when computing IO sector count for a read/write
2029                execute_command(ide_controller, dev_path, IdeCommand::SET_MULTI_BLOCK_MODE.0);
2030            }
2031            DriveType::Optical => {
2032                // Optical is a ATAPI (PACKET) drive and does not support the SET_MULTI_BLOCK_MODE command
2033            }
2034        }
2035    }
2036
2037    // Waits for a condition on the IDE device, polling the device until then.
2038    async fn wait_for<T>(
2039        ide_device: &mut IdeDevice,
2040        mut f: impl FnMut(&mut IdeDevice) -> Option<T>,
2041    ) -> T {
2042        poll_fn(|cx| {
2043            ide_device.poll_device(cx);
2044            let r = f(ide_device);
2045            if let Some(r) = r {
2046                Poll::Ready(r)
2047            } else {
2048                Poll::Pending
2049            }
2050        })
2051        .await
2052    }
2053
2054    // IDE Command Tests
2055    // Command: WRITE SECTOR(S)
2056    #[async_test]
2057    async fn write_sectors_test() {
2058        const START_SECTOR: u32 = 0;
2059        const SECTOR_COUNT: u8 = 4;
2060
2061        let dev_path = IdePath::default();
2062        let (mut ide_device, mut disk, _file_contents, geometry) =
2063            ide_test_setup(None, DriveType::Hard);
2064
2065        // select device [0,0] = primary channel, primary drive
2066        device_select(&mut ide_device, &dev_path).await;
2067        prep_ide_channel(&mut ide_device, DriveType::Hard, &dev_path);
2068
2069        // write to first 4 sectors
2070        write_command_params(
2071            &mut ide_device,
2072            &dev_path,
2073            START_SECTOR,
2074            SECTOR_COUNT,
2075            Addressing::Lba28Bit,
2076            &geometry,
2077        );
2078
2079        execute_command(&mut ide_device, &dev_path, IdeCommand::WRITE_SECTORS.0);
2080
2081        // drive status should contain DRQ as data is ready to be exchanged with host
2082        let status = get_status(&mut ide_device, &dev_path);
2083        assert!(status.drq() && !status.bsy());
2084
2085        // PIO - writes to data port
2086        let data = &[0xFF_u8; 2][..];
2087        for _ in 0..SECTOR_COUNT {
2088            let status = check_command_ready(&mut ide_device, &dev_path).await;
2089            assert!(status.drq());
2090            assert!(!status.err());
2091            for _ in 0..protocol::HARD_DRIVE_SECTOR_BYTES / 2 {
2092                ide_device.io_write(IdeIoPort::PRI_DATA.0, data).unwrap();
2093            }
2094        }
2095
2096        let status = check_command_ready(&mut ide_device, &dev_path).await;
2097        assert!(!status.err());
2098        assert!(!status.drq());
2099
2100        let buffer =
2101            &mut [0_u8; (protocol::HARD_DRIVE_SECTOR_BYTES * SECTOR_COUNT as u32) as usize][..];
2102        disk.read_exact(buffer).unwrap();
2103        for byte in buffer {
2104            assert_eq!(*byte, 0xFF);
2105        }
2106    }
2107
2108    #[async_test]
2109    async fn software_reset_test() {
2110        const START_SECTOR: u32 = 0;
2111        const SECTOR_COUNT: u8 = 4;
2112
2113        let dev_path = IdePath::default();
2114        let (mut ide_device, _disk, _file_contents, geometry) =
2115            ide_test_setup(None, DriveType::Hard);
2116
2117        // select device [0,0] = primary channel, primary drive
2118        device_select(&mut ide_device, &dev_path).await;
2119        prep_ide_channel(&mut ide_device, DriveType::Hard, &dev_path);
2120
2121        // write to first 4 sectors
2122        write_command_params(
2123            &mut ide_device,
2124            &dev_path,
2125            START_SECTOR,
2126            SECTOR_COUNT,
2127            Addressing::Lba28Bit,
2128            &geometry,
2129        );
2130
2131        execute_command(&mut ide_device, &dev_path, IdeCommand::WRITE_SECTORS.0);
2132        // drive status should contain DRQ as data is ready to be exchanged with host
2133        let status = get_status(&mut ide_device, &dev_path);
2134        assert!(status.drq() && !status.bsy());
2135
2136        execute_soft_reset_command(&mut ide_device, &dev_path, IdeCommand::SOFT_RESET.0);
2137        let status = get_status(&mut ide_device, &dev_path);
2138        assert!(status.bsy());
2139    }
2140
2141    // Command: READ SECTOR(S)
2142    #[async_test]
2143    async fn read_sectors_test() {
2144        const START_SECTOR: u32 = 0;
2145        const SECTOR_COUNT: u8 = 4;
2146
2147        let dev_path = IdePath::default();
2148        let (mut ide_device, _disk, file_contents, geometry) =
2149            ide_test_setup(None, DriveType::Hard);
2150
2151        // select device [0,0] = primary channel, primary drive
2152        device_select(&mut ide_device, &dev_path).await;
2153        prep_ide_channel(&mut ide_device, DriveType::Hard, &dev_path);
2154
2155        // read the first 4 sectors
2156        write_command_params(
2157            &mut ide_device,
2158            &dev_path,
2159            START_SECTOR,
2160            SECTOR_COUNT,
2161            Addressing::Lba28Bit,
2162            &geometry,
2163        );
2164
2165        // PIO - writes sectors to track cache buffer
2166        execute_command(&mut ide_device, &dev_path, IdeCommand::READ_SECTORS.0);
2167
2168        let status = check_command_ready(&mut ide_device, &dev_path).await;
2169        assert!(status.drq());
2170        assert!(!status.err());
2171
2172        // PIO - reads data from track cache buffer
2173        let content_bytes = file_contents.as_bytes();
2174        for sector in 0..SECTOR_COUNT {
2175            let status = check_command_ready(&mut ide_device, &dev_path).await;
2176            assert!(status.drq());
2177            assert!(!status.err());
2178            for word in 0..protocol::HARD_DRIVE_SECTOR_BYTES / 2 {
2179                let data = &mut [0, 0][..];
2180                ide_device.io_read(IdeIoPort::PRI_DATA.0, data).unwrap();
2181
2182                let i = sector as usize * protocol::HARD_DRIVE_SECTOR_BYTES as usize / 2
2183                    + word as usize;
2184                assert_eq!(data[0], content_bytes[i * 2]);
2185                assert_eq!(data[1], content_bytes[i * 2 + 1]);
2186            }
2187        }
2188    }
2189
2190    // Command: READ SECTOR(S) - enlightened
2191    async fn enlightened_cmd_test(drive_type: DriveType) {
2192        const SECTOR_COUNT: u16 = 4;
2193        const BYTE_COUNT: u16 = SECTOR_COUNT * protocol::HARD_DRIVE_SECTOR_BYTES as u16;
2194
2195        let test_guest_mem = GuestMemory::allocate(16384);
2196
2197        let table_gpa = 0x1000;
2198        let data_gpa = 0x2000;
2199        test_guest_mem
2200            .write_plain(
2201                table_gpa,
2202                &BusMasterDmaDesc {
2203                    mem_physical_base: data_gpa,
2204                    byte_count: BYTE_COUNT,
2205                    unused: 0,
2206                    end_of_table: 0x80,
2207                },
2208            )
2209            .unwrap();
2210
2211        let (data_buffer, byte_count) = match drive_type {
2212            DriveType::Hard => (table_gpa as u32, 0),
2213            DriveType::Optical => (data_gpa, BYTE_COUNT.into()),
2214        };
2215
2216        let eint13_command = protocol::EnlightenedInt13Command {
2217            command: IdeCommand::READ_DMA_EXT,
2218            device_head: DeviceHeadReg::new().with_lba(true),
2219            flags: 0,
2220            result_status: 0,
2221            lba_low: 0,
2222            lba_high: 0,
2223            block_count: SECTOR_COUNT,
2224            byte_count,
2225            data_buffer,
2226            skip_bytes_head: 0,
2227            skip_bytes_tail: 0,
2228        };
2229        test_guest_mem.write_plain(0, &eint13_command).unwrap();
2230
2231        let dev_path = IdePath::default();
2232        let (mut ide_device, _disk, file_contents, _geometry) =
2233            ide_test_setup(Some(test_guest_mem.clone()), drive_type);
2234
2235        // select device [0,0] = primary channel, primary drive
2236        device_select(&mut ide_device, &dev_path).await;
2237        prep_ide_channel(&mut ide_device, drive_type, &dev_path);
2238
2239        // READ SECTORS - enlightened
2240        let r = ide_device.io_write(IdeIoPort::PRI_ENLIGHTENED.0, 0_u32.as_bytes()); // read from gpa 0
2241
2242        match r {
2243            IoResult::Defer(mut deferred) => {
2244                poll_fn(|cx| {
2245                    ide_device.poll_device(cx);
2246                    deferred.poll_write(cx)
2247                })
2248                .await
2249                .unwrap();
2250            }
2251            _ => panic!("{:?}", r),
2252        }
2253
2254        let mut buffer = vec![0u8; BYTE_COUNT as usize];
2255        test_guest_mem
2256            .read_at(data_gpa.into(), &mut buffer)
2257            .unwrap();
2258        assert_eq!(buffer, file_contents.as_bytes()[..buffer.len()]);
2259    }
2260
2261    // Command: READ SECTOR(S) - enlightened
2262    #[async_test]
2263    async fn enlightened_cd_cmd_test() {
2264        enlightened_cmd_test(DriveType::Optical).await
2265    }
2266
2267    #[async_test]
2268    async fn enlightened_hdd_cmd_test() {
2269        enlightened_cmd_test(DriveType::Hard).await
2270    }
2271
2272    #[async_test]
2273    async fn identify_test_cd() {
2274        let dev_path = IdePath::default();
2275        let (mut ide_device, _disk, _file_contents, _geometry) =
2276            ide_test_setup(None, DriveType::Optical);
2277
2278        // select device [0,0] = primary channel, primary drive
2279        device_select(&mut ide_device, &dev_path).await;
2280        prep_ide_channel(&mut ide_device, DriveType::Optical, &dev_path);
2281
2282        // PIO - writes sectors to track cache buffer
2283        execute_command(
2284            &mut ide_device,
2285            &dev_path,
2286            IdeCommand::IDENTIFY_PACKET_DEVICE.0,
2287        );
2288
2289        let status = check_command_ready(&mut ide_device, &dev_path).await;
2290        assert!(status.drq());
2291        assert!(!status.err());
2292
2293        // PIO - reads data from track cache buffer
2294        let data = &mut [0_u8; protocol::IDENTIFY_DEVICE_BYTES];
2295        ide_device.io_read(IdeIoPort::PRI_DATA.0, data).unwrap();
2296        let features = protocol::IdeFeatures::read_from_prefix(&data[..])
2297            .unwrap()
2298            .0; // TODO: zerocopy: use-rest-of-range (https://github.com/microsoft/openvmm/issues/759)
2299        let ex_features = protocol::IdeFeatures {
2300            config_bits: 0x85C0,
2301            serial_no: *b"                    ",
2302            buffer_size: 0x0080,
2303            firmware_revision: *b"        ",
2304            model_number: "iVtrau lDC                              ".as_bytes()[..]
2305                .try_into()
2306                .unwrap(),
2307            capabilities: 0x0300,
2308            pio_cycle_times: 0x0200,       // indicate fast I/O
2309            dma_cycle_times: 0x0200,       // indicate fast I/O
2310            new_words_valid_flags: 0x0003, // indicate next words are valid
2311            multi_sector_capabilities: 0x0100_u16 | protocol::MAX_SECTORS_MULT_TRANSFER_DEFAULT,
2312            single_word_dma_mode: 0x0007, // support up to mode 3, no mode active
2313            multi_word_dma_mode: 0x0407,  // support up to mode 3, mode 3 active
2314            enhanced_pio_mode: 0x0003,    // PIO mode 3 and 4 supported
2315            min_multi_dma_time: 0x0078,
2316            recommended_multi_dma_time: 0x0078,
2317            min_pio_cycle_time_no_flow: 0x01FC, // Taken from a real CD device
2318            min_pio_cycle_time_flow: 0x00B4,    // Taken from a real CD device
2319            ..FromZeros::new_zeroed()
2320        };
2321        assert_eq!(features.as_bytes(), ex_features.as_bytes());
2322    }
2323
2324    #[async_test]
2325    async fn identify_test_hdd() {
2326        let dev_path = IdePath::default();
2327        let (mut ide_device, _disk, _file_contents, geometry) =
2328            ide_test_setup(None, DriveType::Hard);
2329        // select device [0,0] = primary channel, primary drive
2330        device_select(&mut ide_device, &dev_path).await;
2331        prep_ide_channel(&mut ide_device, DriveType::Hard, &dev_path);
2332        // PIO - writes sectors to track cache buffer
2333        execute_command(&mut ide_device, &dev_path, IdeCommand::IDENTIFY_DEVICE.0);
2334
2335        let status = check_command_ready(&mut ide_device, &dev_path).await;
2336        assert!(status.drq());
2337        assert!(!status.err());
2338
2339        // PIO - reads data from track cache buffer
2340        let data = &mut [0_u8; protocol::IDENTIFY_DEVICE_BYTES];
2341        ide_device.io_read(IdeIoPort::PRI_DATA.0, data).unwrap();
2342        let features = protocol::IdeFeatures::read_from_prefix(&data[..])
2343            .unwrap()
2344            .0; // TODO: zerocopy: use-rest-of-range (https://github.com/microsoft/openvmm/issues/759)
2345
2346        let total_chs_sectors: u32 =
2347            geometry.sectors_per_track * geometry.cylinder_count * geometry.head_count;
2348        let (cylinders, heads, sectors_per_track) = if total_chs_sectors < protocol::MAX_CHS_SECTORS
2349        {
2350            (
2351                geometry.cylinder_count as u16,
2352                geometry.head_count as u16,
2353                geometry.sectors_per_track as u16,
2354            )
2355        } else {
2356            (0x3FFF, 16, 63)
2357        };
2358
2359        let firmware_revision = if dev_path.channel == 0 {
2360            ".1.1 0  "
2361        } else {
2362            ".1.1 1  "
2363        }
2364        .as_bytes()[..]
2365            .try_into()
2366            .unwrap();
2367
2368        let user_addressable_sectors =
2369            if geometry.total_sectors > (protocol::LBA_28BIT_MAX_SECTORS as u64) {
2370                protocol::LBA_28BIT_MAX_SECTORS
2371            } else {
2372                geometry.total_sectors as u32
2373            };
2374
2375        let ex_features = protocol::IdeFeatures {
2376            config_bits: 0x045A,
2377            cylinders,
2378            heads,
2379            unformatted_sectors_per_track: (protocol::HARD_DRIVE_SECTOR_BYTES
2380                * geometry.sectors_per_track) as u16,
2381            unformatted_bytes_per_sector: protocol::HARD_DRIVE_SECTOR_BYTES as u16,
2382            sectors_per_track,
2383            compact_flash: [0xABCD, 0xDCBA],
2384            vendor0: 0x0123,
2385            serial_no: *b"                    ",
2386            buffer_type: 3,
2387            buffer_size: 0x0080,
2388            firmware_revision,
2389            model_number: "iVtrau lDH                              ".as_bytes()[..]
2390                .try_into()
2391                .unwrap(),
2392            max_sectors_mult_transfer: (0x8000 | protocol::MAX_SECTORS_MULT_TRANSFER_DEFAULT),
2393            capabilities: 0x0F00,          // supports Dma, IORDY, LBA
2394            pio_cycle_times: 0x0200,       // indicate fast I/O
2395            dma_cycle_times: 0x0200,       // indicate fast I/O
2396            new_words_valid_flags: 0x0003, // indicate next words are valid
2397            log_cylinders: geometry.cylinder_count as u16,
2398            log_heads: geometry.head_count as u16,
2399            log_sectors_per_track: geometry.sectors_per_track as u16,
2400            log_total_sectors: total_chs_sectors.into(),
2401            multi_sector_capabilities: 0x0100_u16 | protocol::MAX_SECTORS_MULT_TRANSFER_DEFAULT,
2402            user_addressable_sectors: user_addressable_sectors.into(),
2403            single_word_dma_mode: 0x0007, // support up to mode 3, no mode active
2404            multi_word_dma_mode: 0x0407,  // support up to mode 3, mode 3 active
2405            enhanced_pio_mode: 0x0003,    // PIO mode 3 and 4 supported
2406            min_multi_dma_time: 0x0078,
2407            recommended_multi_dma_time: 0x0078,
2408            min_pio_cycle_time_no_flow: 0x014D,
2409            min_pio_cycle_time_flow: 0x0078,
2410            major_version_number: 0x01F0, // claim support for ATA4-ATA8
2411            minor_version_number: 0,
2412            command_set_supported: 0x0028, // support caching and power management
2413            command_sets_supported: 0x7400, // support flushing
2414            command_set_supported_ext: 0x4040, // write fua support for default write hardening
2415            command_set_enabled1: 0x0028,  // support caching and power management
2416            command_set_enabled2: 0x3400,  // support flushing
2417            command_set_default: 0x4040,   // write fua support for default write hardening
2418            total_sectors_48_bit: geometry.total_sectors.into(),
2419            default_sector_size_config: 0x4000, // describes the sector size related info. Reflect the underlying device sector size and logical:physical ratio
2420            logical_block_alignment: 0x4000, // describes alignment of logical blocks within physical block
2421            ..FromZeros::new_zeroed()
2422        };
2423        assert_eq!(features.as_bytes(), ex_features.as_bytes());
2424    }
2425}