1#![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,
89 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
107const 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 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
148pub struct IdeDevice {
150 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 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 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 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 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 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 let cmd = eint13_cmd.command;
396 if cmd == IdeCommand::READ_DMA_EXT || cmd == IdeCommand::WRITE_DMA_EXT {
397 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 self.write_drive_register(
419 DriveRegister::SectorCount,
420 (eint13_cmd.block_count >> 8) as u8,
421 bus_master_state,
422 );
423 }
424
425 self.write_drive_register(
427 DriveRegister::SectorCount,
428 eint13_cmd.block_count as u8,
429 bus_master_state,
430 );
431
432 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 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 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 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 self.write_drive_register(DriveRegister::StatusCmd, cmd.0, bus_master_state);
479
480 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 let old_features_reg = self.state.shadow_features_reg;
517 self.write_drive_register(DriveRegister::ErrorFeatures, 0, bus_master_state);
518
519 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 self.write_drive_register(
533 DriveRegister::StatusCmd,
534 IdeCommand::PACKET_COMMAND.0,
535 bus_master_state,
536 );
537
538 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 let mut command = [0; 12];
551 command[..cdb.as_bytes().len()].copy_from_slice(cdb.as_bytes());
552
553 self.write_drive_data(command.as_bytes(), bus_master_state);
556
557 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 self.write_bus_master_reg(BusMasterReg::COMMAND, &[0], bus_master_state)
591 .unwrap();
592
593 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 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 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 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 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 let skip = (write.skip_bytes_head as usize).min(remaining);
652 remaining -= skip;
653 self.skip_drive_data(skip, bus_master_state);
654
655 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 self.skip_drive_data(remaining, bus_master_state);
679 }
680
681 self.write_drive_register(
683 DriveRegister::ErrorFeatures,
684 write.old_features_reg,
685 bus_master_state,
686 );
687
688 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 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 dma.transfer_bytes_left == 0 {
727 assert!(!dma.transfer_complete);
728
729 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 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 dma.descriptor_idx += 1;
773 if dma.transfer_complete {
774 dma.descriptor_idx = 0;
775 }
776 }
777
778 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 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 0x00000F30
934 }
935 offset => {
936 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 self.bus_master_state.cmd_status_reg &= !(0x38000000 & value);
961 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 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 let first_port = (self.bus_master_state.port_addr_reg as u16) & 0xFFF0;
977 tracing::trace!(?first_port, "enabling bus master range");
978
979 self.bus_master_pio_dynamic.map(first_port);
982 }
983 }
984 HeaderType00::BAR4 => {
985 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)) }
1011}
1012
1013enum 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 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 self.perform_dma_memory_phase();
1166
1167 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 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 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 fn read_drive_register(
1209 &mut self,
1210 port: DriveRegister,
1211 bus_master_state: &BusMasterState,
1212 ) -> u8 {
1213 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 drive.read_register(port)
1223 } else {
1224 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 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 self.state.current_drive_idx = ((data >> 4) & 1) as usize;
1249 }
1250 DriveRegister::AlternateStatusDeviceControl => {
1251 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 self.state.shadow_features_reg = data;
1261 }
1262 _ => {}
1263 }
1264
1265 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 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 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 if data.len() > 2 {
1375 return IoResult::Err(IoError::InvalidAccessSize);
1376 }
1377
1378 let old_value = self.bus_master_state.command_reg;
1379 let mut new_value = BusMasterCommandReg::from_bits_truncate(value as u32);
1381
1382 if old_value.start() {
1384 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 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 self.bus_master_state.desc_table_ptr = value as u32 & 0xffff_fffc;
1424 } else {
1425 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 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 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 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 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 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 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 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 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 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 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 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 execute_command(ide_controller, dev_path, IdeCommand::SET_MULTI_BLOCK_MODE.0);
2030 }
2031 DriveType::Optical => {
2032 }
2034 }
2035 }
2036
2037 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 #[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 device_select(&mut ide_device, &dev_path).await;
2067 prep_ide_channel(&mut ide_device, DriveType::Hard, &dev_path);
2068
2069 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 let status = get_status(&mut ide_device, &dev_path);
2083 assert!(status.drq() && !status.bsy());
2084
2085 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 device_select(&mut ide_device, &dev_path).await;
2119 prep_ide_channel(&mut ide_device, DriveType::Hard, &dev_path);
2120
2121 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 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 #[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 device_select(&mut ide_device, &dev_path).await;
2153 prep_ide_channel(&mut ide_device, DriveType::Hard, &dev_path);
2154
2155 write_command_params(
2157 &mut ide_device,
2158 &dev_path,
2159 START_SECTOR,
2160 SECTOR_COUNT,
2161 Addressing::Lba28Bit,
2162 &geometry,
2163 );
2164
2165 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 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 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 device_select(&mut ide_device, &dev_path).await;
2237 prep_ide_channel(&mut ide_device, drive_type, &dev_path);
2238
2239 let r = ide_device.io_write(IdeIoPort::PRI_ENLIGHTENED.0, 0_u32.as_bytes()); 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 #[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 device_select(&mut ide_device, &dev_path).await;
2280 prep_ide_channel(&mut ide_device, DriveType::Optical, &dev_path);
2281
2282 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 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; 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, dma_cycle_times: 0x0200, new_words_valid_flags: 0x0003, multi_sector_capabilities: 0x0100_u16 | protocol::MAX_SECTORS_MULT_TRANSFER_DEFAULT,
2312 single_word_dma_mode: 0x0007, multi_word_dma_mode: 0x0407, enhanced_pio_mode: 0x0003, min_multi_dma_time: 0x0078,
2316 recommended_multi_dma_time: 0x0078,
2317 min_pio_cycle_time_no_flow: 0x01FC, min_pio_cycle_time_flow: 0x00B4, ..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 device_select(&mut ide_device, &dev_path).await;
2331 prep_ide_channel(&mut ide_device, DriveType::Hard, &dev_path);
2332 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 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; 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, pio_cycle_times: 0x0200, dma_cycle_times: 0x0200, new_words_valid_flags: 0x0003, 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, multi_word_dma_mode: 0x0407, enhanced_pio_mode: 0x0003, 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, minor_version_number: 0,
2412 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(),
2419 default_sector_size_config: 0x4000, logical_block_alignment: 0x4000, ..FromZeros::new_zeroed()
2422 };
2423 assert_eq!(features.as_bytes(), ex_features.as_bytes());
2424 }
2425}