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