1#![forbid(unsafe_code)]
30
31use self::floppy_sizes::FloppyImageType;
32use self::protocol::FLOPPY_TOTAL_CYLINDERS;
33use self::protocol::FloppyCommand;
34use self::protocol::INVALID_COMMAND_STATUS;
35use self::protocol::RegisterOffset;
36use self::protocol::STANDARD_FLOPPY_SECTOR_SIZE;
37use arrayvec::ArrayVec;
38use chipset_device::ChipsetDevice;
39use chipset_device::io::IoError;
40use chipset_device::io::IoResult;
41use chipset_device::pio::ControlPortIoIntercept;
42use chipset_device::pio::PortIoIntercept;
43use chipset_device::pio::RegisterPortIoIntercept;
44use chipset_device::poll_device::PollDevice;
45use core::sync::atomic::Ordering;
46use disk_backend::Disk;
47use guestmem::AlignedHeapMemory;
48use guestmem::GuestMemory;
49use guestmem::ranges::PagedRange;
50use inspect::Inspect;
51use inspect::InspectMut;
52use scsi_buffers::RequestBuffers;
53use std::future::Future;
54use std::pin::Pin;
55use std::sync::Arc;
56use std::task::Context;
57use std::task::Poll;
58use std::task::Waker;
59use thiserror::Error;
60use vmcore::device_state::ChangeDeviceState;
61use vmcore::isa_dma_channel::IsaDmaChannel;
62use vmcore::isa_dma_channel::IsaDmaDirection;
63use vmcore::line_interrupt::LineInterrupt;
64
65mod floppy_sizes {
66 use super::protocol::FLOPPY_TOTAL_CYLINDERS;
67 use super::protocol::STANDARD_FLOPPY_SECTOR_SIZE;
68
69 const HDMSS_SECTORS_PER_TRACK: u8 = 23;
70 const DMF_SECTORS_PER_TRACK: u8 = 21;
71 const HD_SECTORS_PER_TRACK: u8 = 18;
72 const MD_SECTORS_PER_TRACK: u8 = 15;
73 const LD_SECTORS_PER_TRACK: u8 = 9;
74
75 const fn calculate_image_size(sectors_per_track: u8) -> u64 {
76 sectors_per_track as u64
77 * STANDARD_FLOPPY_SECTOR_SIZE as u64
78 * FLOPPY_TOTAL_CYLINDERS as u64
79 * 2
80 }
81
82 const HDMSS_FLOPY_IMAGE_SIZE: u64 = calculate_image_size(HDMSS_SECTORS_PER_TRACK);
83 const DMF_FLOPPY_IMAGE_SIZE: u64 = calculate_image_size(DMF_SECTORS_PER_TRACK);
84 const HD_FLOPPY_IMAGE_SIZE: u64 = calculate_image_size(HD_SECTORS_PER_TRACK);
85 const MD_FLOPPY_IMAGE_SIZE: u64 = calculate_image_size(MD_SECTORS_PER_TRACK);
86 const LD_FLOPPY_IMAGE_SIZE: u64 = calculate_image_size(LD_SECTORS_PER_TRACK);
87 const LDSS_FLOPPY_IMAGE_SIZE: u64 = calculate_image_size(LD_SECTORS_PER_TRACK) / 2;
88
89 pub enum FloppyImageType {
90 LowDensitySingleSided,
92 LowDensity,
94 MediumDensity,
96 HighDensity,
98 Dmf,
100 HighDensityMss,
103 }
104
105 impl FloppyImageType {
106 pub fn sectors(&self) -> u8 {
107 match self {
108 FloppyImageType::LowDensity => LD_SECTORS_PER_TRACK,
109 FloppyImageType::HighDensity => HD_SECTORS_PER_TRACK,
110 FloppyImageType::Dmf => DMF_SECTORS_PER_TRACK,
111 FloppyImageType::LowDensitySingleSided => LD_SECTORS_PER_TRACK,
112 FloppyImageType::MediumDensity => MD_SECTORS_PER_TRACK,
113 FloppyImageType::HighDensityMss => HDMSS_SECTORS_PER_TRACK,
114 }
115 }
116
117 pub fn from_file_size(file_size: u64) -> Option<Self> {
118 let res = match file_size {
119 HD_FLOPPY_IMAGE_SIZE => FloppyImageType::HighDensity,
120 DMF_FLOPPY_IMAGE_SIZE => FloppyImageType::Dmf,
121 LD_FLOPPY_IMAGE_SIZE => FloppyImageType::LowDensity,
122 MD_FLOPPY_IMAGE_SIZE => FloppyImageType::MediumDensity,
123 LDSS_FLOPPY_IMAGE_SIZE => FloppyImageType::LowDensitySingleSided,
124 HDMSS_FLOPY_IMAGE_SIZE => FloppyImageType::HighDensityMss,
125 _ => return None,
126 };
127 Some(res)
128 }
129 }
130}
131
132mod protocol {
133 use bitfield_struct::bitfield;
134 use inspect::Inspect;
135 use open_enum::open_enum;
136
137 pub const FIFO_SIZE: usize = 16;
138
139 pub const INVALID_COMMAND_STATUS: u8 = 0x80; pub const STANDARD_FLOPPY_SECTOR_SIZE: usize = 512;
142 pub const FLOPPY_TOTAL_CYLINDERS: u8 = 80;
143
144 #[derive(Inspect)]
145 #[bitfield(u8)]
146 pub struct InputRegister {
147 #[bits(2)]
148 pub drive_select: u8,
149 #[bits(1)]
150 pub head: u8,
151 #[bits(5)]
152 unused2: u8,
153 }
154
155 #[derive(Inspect)]
156 #[bitfield(u8)]
157 pub struct StatusRegister0 {
158 #[bits(2)]
159 pub drive_select: u8,
160 #[bits(1)]
161 pub head: u8,
162 #[bits(2)]
163 unused: u8,
164 pub seek_end: bool,
165 pub abnormal_termination: bool,
166 pub invalid_command: bool,
167 }
168
169 #[derive(Inspect)]
170 #[bitfield(u8)]
171 pub struct StatusRegister1 {
172 pub missing_address: bool,
173 pub write_protected: bool,
174 pub no_data: bool,
175 #[bits(5)]
176 unused: u8,
177 }
178
179 #[derive(Inspect)]
180 #[bitfield(u8)]
181 pub struct StatusRegister2 {
182 pub missing_address: bool,
183 pub bad_cylinder: bool,
184 #[bits(6)]
185 unused: u8,
186 }
187
188 #[derive(Inspect)]
189 #[bitfield(u8)]
190 pub struct StatusRegister3 {
191 #[bits(2)]
192 pub drive_select: u8,
193 #[bits(1)]
194 pub head: u8,
195 pub unused1: bool, pub track0: bool,
197 pub unused2: bool, pub write_protected: bool,
199 pub unused3: bool, }
201
202 open_enum! {
203 #[derive(Default)]
204 pub enum RegisterOffset: u16 {
205 STATUS_A = 0, STATUS_B = 1, DIGITAL_OUTPUT = 2,
208 TAPE_DRIVE = 3, MAIN_STATUS = 4, DATA_RATE = 4, DATA = 5,
212 DIGITAL_INPUT = 7,CONFIG_CONTROL = 7, }
215 }
216
217 #[derive(Inspect)]
223 #[bitfield(u8)]
224 pub struct DigitalOutputRegister {
225 #[bits(2)]
231 pub _drive_select: u8,
232
233 pub controller_enabled: bool,
235
236 pub dma_enabled: bool,
238
239 #[bits(4)]
242 pub motors_active: u8,
243 }
244
245 #[derive(Debug, Copy, Clone, PartialEq, Eq)]
246 pub enum DataDirection {
247 Write = 0,
252 Read = 1,
257 }
258
259 impl DataDirection {
260 pub fn as_bool(self) -> bool {
261 match self {
262 Self::Write => false,
263 Self::Read => true,
264 }
265 }
266 }
267
268 #[derive(Inspect)]
270 #[bitfield(u8)]
271 pub struct MainStatusRegister {
272 #[bits(4)]
275 pub active_drives: u8,
276 pub busy: bool,
278 pub non_dma_mode: bool,
280 pub data_direction: bool, pub main_request: bool,
286 }
287
288 #[derive(Inspect)]
291 #[bitfield(u8)]
292 pub struct DigitalInputRegister {
293 #[bits(7)]
295 pub tristated: u8,
296
297 #[bits(1)]
298 pub disk_change: bool,
299 }
300
301 open_enum! {
302 #[derive(Default)]
303 pub enum FloppyCommand: u8 {
307 VERIFY = 0x16,
315 VERIFY2 = 0xF6,
316 VERSION = 0x10,
319 FORMAT_TRACK = 0x4D,
320 FORMAT_DOUBLE_DENSITY_MODE = 0xCD,
321 SCAN_EQUAL_ALL = 0xD1,
323 SCAN_EQUAL = 0xF1,
324 SCAN_LOW_OR_EQUAL = 0x19,
325 SCAN_HIGH_OR_EQUAL = 0x1D,
326 RECALIBRATE = 0x07,
337 SENSE_INTERRUPT_STATUS = 0x08,
343 SPECIFY = 0x03,
350 SENSE_DRIVE_STATUS = 0x04,
353 DRIVE_SPECIFICATION_COMMAND = 0x8E,
354 SEEK = 0x0F,
369 CONFIGURE = 0x13,
372 RELATIVE_SEEK_IN = 0xCF,
376 RELATIVE_SEEK_OUT = 0x8F,
377 DUMP_REGISTERS = 0x0E,
379 READ_ID = 0x4A, PERP288_MODE = 0x12,
387 UNLOCK_FIFO_FUNCTIONS = 0x14,
393 LOCK_FIFO_FUNCTIONS = 0x94,
395 PART_ID = 0x18,
397 POWERDOWN_MODE = 0x17,
398 OPTION = 0x33,
399 SAVE = 0x2E,
400 RESTORE = 0x4E,
401 FORMAT_AND_WRITE = 0xAD,
402
403 EXIT_STANDBY_MODE = 0x34,
404 GOTO_STANDBY_MODE = 0x35,
405 HARD_RESET = 0x36,
406 READ_TRACK = 0x42,
407 SEEK_AND_WRITE = 0x45,
408 SEEK_AND_READ = 0x46,
409 ALT_SEEK_AND_READ = 0x66,
410 WRITE_DATA = 0xC5,
411 READ_NORMAL_DEL_DATA = 0xC6,
412 WRITE_DEL_DATA = 0xC9,
413 READ_DEL_DATA = 0xCC,
414 WRITE_NORMAL_DATA = 0xE5, READ_NORMAL_DATA = 0xE6,
416
417 INVALID = 0x00,
418 }
419 }
420
421 impl FloppyCommand {
422 pub fn input_bytes_needed(&self) -> usize {
426 1 + match *self {
428 Self::READ_DEL_DATA => 8,
429 Self::WRITE_DATA => 8,
430 Self::WRITE_DEL_DATA => 8,
431 Self::READ_TRACK => 8,
432 Self::VERIFY => 8,
433 Self::VERSION => 0,
434 Self::FORMAT_TRACK => 5,
435 Self::FORMAT_DOUBLE_DENSITY_MODE => 5,
436 Self::SCAN_EQUAL_ALL => 8,
437 Self::SCAN_EQUAL => 8,
438 Self::SCAN_LOW_OR_EQUAL => 8,
439 Self::SCAN_HIGH_OR_EQUAL => 8,
440 Self::RECALIBRATE => 1,
441 Self::SENSE_INTERRUPT_STATUS => 0,
442 Self::SPECIFY => 2,
443 Self::SENSE_DRIVE_STATUS => 1,
444 Self::DRIVE_SPECIFICATION_COMMAND => 6,
445 Self::SEEK => 2,
446 Self::CONFIGURE => 3,
447 Self::RELATIVE_SEEK_IN => 2,
448 Self::RELATIVE_SEEK_OUT => 2,
449 Self::DUMP_REGISTERS => 0,
450 Self::READ_ID => 1,
451 Self::PERP288_MODE => 1,
452 Self::UNLOCK_FIFO_FUNCTIONS => 0,
453 Self::LOCK_FIFO_FUNCTIONS => 0,
454 Self::PART_ID => 0,
455 Self::POWERDOWN_MODE => 1,
456 Self::OPTION => 1,
457 Self::SAVE => 0,
458 Self::RESTORE => 16,
459 Self::FORMAT_AND_WRITE => 5,
460
461 Self::SEEK_AND_WRITE => 8,
466 Self::SEEK_AND_READ => 8,
467 Self::ALT_SEEK_AND_READ => 8,
468 Self::READ_NORMAL_DEL_DATA => 8,
469 Self::WRITE_NORMAL_DATA => 8,
470 Self::READ_NORMAL_DATA => 8,
471
472 _ => 0,
474 }
475 }
476
477 pub fn result_bytes_expected(&self) -> usize {
478 match *self {
479 Self::READ_DEL_DATA => 7,
480 Self::WRITE_DATA => 7,
481 Self::WRITE_DEL_DATA => 7,
482 Self::READ_TRACK => 7,
483 Self::VERIFY => 7,
484 Self::VERSION => 1,
485 Self::FORMAT_TRACK => 7,
486 Self::FORMAT_DOUBLE_DENSITY_MODE => 7,
487 Self::SCAN_EQUAL_ALL => 7,
488 Self::SCAN_EQUAL => 7,
489 Self::SCAN_LOW_OR_EQUAL => 7,
490 Self::SCAN_HIGH_OR_EQUAL => 7,
491 Self::RECALIBRATE => 2,
492 Self::SENSE_INTERRUPT_STATUS => 2,
493 Self::SPECIFY => 0,
494 Self::SENSE_DRIVE_STATUS => 1,
495 Self::DRIVE_SPECIFICATION_COMMAND => 0,
496 Self::SEEK => 2, Self::CONFIGURE => 0,
498 Self::RELATIVE_SEEK_IN => 2, Self::RELATIVE_SEEK_OUT => 2, Self::DUMP_REGISTERS => 10,
501 Self::READ_ID => 7,
502 Self::PERP288_MODE => 0,
503 Self::UNLOCK_FIFO_FUNCTIONS => 1,
504 Self::LOCK_FIFO_FUNCTIONS => 1,
505 Self::PART_ID => 1,
506 Self::POWERDOWN_MODE => 1,
507 Self::OPTION => 1,
508 Self::SAVE => 16,
509 Self::RESTORE => 0,
510 Self::FORMAT_AND_WRITE => 7,
511
512 Self::SEEK_AND_WRITE => 7,
516 Self::SEEK_AND_READ => 7,
517 Self::ALT_SEEK_AND_READ => 7,
518 Self::READ_NORMAL_DEL_DATA => 7,
519 Self::WRITE_NORMAL_DATA => 7,
520 Self::READ_NORMAL_DATA => 7,
521
522 Self::INVALID => 1,
523 _ => 0,
524 }
525 }
526 }
527
528 #[derive(Inspect)]
529 #[bitfield(u8)]
530 pub struct SpecifyParam1 {
531 #[bits(4)]
532 pub head_unload_timer: u8,
533 #[bits(4)]
534 pub step_rate_time: u8,
535 }
536
537 #[derive(Inspect)]
538 #[bitfield(u8)]
539 pub struct SpecifyParam2 {
540 #[bits(7)]
541 pub head_load_timer: u8,
542 pub dma_disabled: bool,
543 }
544}
545
546const MAX_CMD_BUFFER_BYTES: usize = 64 * 1024;
547
548#[derive(Debug)]
549struct CommandBuffer {
550 buffer: Arc<AlignedHeapMemory>,
551}
552
553#[derive(Debug)]
554struct CommandBufferAccess {
555 memory: GuestMemory,
556}
557
558impl CommandBuffer {
559 fn new() -> Self {
560 Self {
561 buffer: Arc::new(AlignedHeapMemory::new(MAX_CMD_BUFFER_BYTES)),
562 }
563 }
564
565 fn access(&self) -> CommandBufferAccess {
566 CommandBufferAccess {
567 memory: GuestMemory::new("floppy_buffer", self.buffer.clone()),
568 }
569 }
570}
571
572impl CommandBufferAccess {
573 fn buffers(&self, offset: usize, len: usize, is_write: bool) -> RequestBuffers<'_> {
574 static BUFFER_RANGE: Option<PagedRange<'_>> = PagedRange::new(
576 0,
577 MAX_CMD_BUFFER_BYTES,
578 &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
579 );
580
581 RequestBuffers::new(
582 &self.memory,
583 BUFFER_RANGE.unwrap().subrange(offset, len),
584 is_write,
585 )
586 }
587}
588
589struct Io(Pin<Box<dyn Send + Future<Output = Result<(), disk_backend::DiskError>>>>);
590
591impl ChangeDeviceState for FloppyDiskController {
592 fn start(&mut self) {}
593
594 async fn stop(&mut self) {}
595
596 async fn reset(&mut self) {
597 self.reset(false);
598 }
599}
600
601impl ChipsetDevice for FloppyDiskController {
602 fn supports_pio(&mut self) -> Option<&mut dyn PortIoIntercept> {
603 Some(self)
604 }
605
606 fn supports_poll_device(&mut self) -> Option<&mut dyn PollDevice> {
607 Some(self)
608 }
609}
610
611impl PollDevice for FloppyDiskController {
612 fn poll_device(&mut self, cx: &mut Context<'_>) {
613 if let Some(io) = self.io.as_mut() {
614 if let Poll::Ready(result) = io.0.as_mut().poll(cx) {
615 self.io = None;
616 self.handle_io_completion(result);
617 }
618 }
619 self.waker = Some(cx.waker().clone());
620 }
621}
622
623impl PortIoIntercept for FloppyDiskController {
624 fn io_read(&mut self, io_port: u16, data: &mut [u8]) -> IoResult {
625 if data.len() != 1 {
626 return IoResult::Err(IoError::InvalidAccessSize);
627 }
628
629 let offset = RegisterOffset(io_port % 0x10);
630 data[0] = match offset {
631 RegisterOffset::STATUS_A => 0xFF,
633 RegisterOffset::STATUS_B => 0xFC,
635 RegisterOffset::TAPE_DRIVE => 0xFF,
637 RegisterOffset::DIGITAL_OUTPUT => self.state.digital_output.into(),
638 RegisterOffset::MAIN_STATUS => {
639 if self.state.digital_output.controller_enabled() {
642 self.state.main_status.into()
643 } else {
644 0
645 }
646 }
647 RegisterOffset::DATA => {
648 let active_drive = self.state.main_status.active_drives();
650 let io_direction = self.state.main_status.data_direction();
651 tracing::trace!(?active_drive, ?io_direction, "DATA io read state");
652
653 if let Some(result) = self.state.output_bytes.pop() {
654 self.state.main_status.set_active_drives(0);
655 if self.state.output_bytes.is_empty() {
656 self.state.main_status = (self.state.main_status)
658 .with_non_dma_mode(false)
659 .with_busy(false)
660 .with_main_request(true)
661 .with_data_direction(protocol::DataDirection::Write.as_bool());
662 }
663 result
664 } else {
665 INVALID_COMMAND_STATUS
666 }
667 }
668
669 RegisterOffset::DIGITAL_INPUT => {
672 let val = protocol::DigitalInputRegister::new()
675 .with_tristated(0x7f)
676 .with_disk_change(
677 self.state.digital_output.motors_active() != 0
678 && (!self.state.internals.floppy_present
679 || self.state.internals.floppy_changed),
680 );
681
682 val.into()
683 }
684 _ => return IoResult::Err(IoError::InvalidRegister),
685 };
686
687 tracing::trace!(?offset, ?data, "floppy pio read");
688
689 IoResult::Ok
690 }
691
692 fn io_write(&mut self, io_port: u16, data: &[u8]) -> IoResult {
693 if data.len() != 1 {
694 return IoResult::Err(IoError::InvalidAccessSize);
695 }
696
697 let data = data[0];
698 let offset = RegisterOffset(io_port % 0x10);
699 tracing::trace!(?offset, ?data, "floppy pio write");
700 match offset {
701 RegisterOffset::STATUS_A | RegisterOffset::STATUS_B => {
702 tracelimit::warn_ratelimited!(
703 ?data,
704 ?offset,
705 "write to read-only floppy status register"
706 );
707 }
708 RegisterOffset::TAPE_DRIVE => {
709 tracing::debug!(?data, "write to obsolete tape drive register");
710 } RegisterOffset::CONFIG_CONTROL => {
712 tracing::debug!(?data, "write to control register");
715 }
716 RegisterOffset::DATA_RATE => {
717 const FLOPPY_DSR_DISK_RESET_MASK: u8 = 0x80; if self.state.digital_output.controller_enabled()
720 && (data & FLOPPY_DSR_DISK_RESET_MASK) != 0
721 {
722 self.reset(true);
723 self.state.sense_output = Some(SenseOutput::ResetCounter { count: 4 });
724 self.raise_interrupt(true);
726 tracing::trace!("DSR wr: Un-resetting - asserting floppy interrupt");
727 }
728 }
729 RegisterOffset::DIGITAL_OUTPUT => {
730 let new_digital_output = protocol::DigitalOutputRegister::from(data);
731 let was_reset = !self.state.digital_output.controller_enabled();
738 let is_reset = !new_digital_output.controller_enabled();
739 let interrupts_were_enabled = self.state.digital_output.dma_enabled();
740 let interrupts_enabled = new_digital_output.dma_enabled();
741 self.state.digital_output = new_digital_output;
742
743 if was_reset && !is_reset {
744 tracing::trace!("DOR wr: Un-resetting - asserting floppy interrupt");
745 self.state.sense_output = Some(SenseOutput::ResetCounter { count: 4 });
746 self.raise_interrupt(true);
748 } else if is_reset {
749 tracing::debug!("DOR wr: Software reset on fdc");
750 self.reset(true);
751 } else {
752 if !interrupts_were_enabled && interrupts_enabled {
753 tracing::trace!("Re-enabling floppy interrupts");
754 self.raise_interrupt(false);
755 } else if interrupts_were_enabled && !interrupts_enabled {
756 tracing::trace!("Disabling floppy interrupts");
757 self.lower_interrupt();
758 }
759 }
760 }
761 RegisterOffset::DATA => self.handle_data_write(data),
762 _ => return IoResult::Err(IoError::InvalidRegister),
763 }
764
765 IoResult::Ok
766 }
767}
768
769#[derive(Clone, Inspect)]
770struct FloppyState {
771 digital_output: protocol::DigitalOutputRegister,
772 main_status: protocol::MainStatusRegister,
773
774 #[inspect(bytes)]
776 input_bytes: ArrayVec<u8, { protocol::FIFO_SIZE }>,
777
778 #[inspect(bytes)]
780 output_bytes: ArrayVec<u8, { protocol::FIFO_SIZE }>,
781
782 #[inspect(skip)]
784 pending_command: FloppyCommand,
785
786 head_unload_timer: u8,
788 step_rate_time: u8,
789 head_load_timer: u8,
790 dma_disabled: bool,
791
792 sense_output: Option<SenseOutput>,
793
794 internals: FloppyStateInternals,
795
796 position: ReadWriteHeadLocation,
800 end_of_track: u8,
801
802 interrupt_level: bool,
804}
805
806#[derive(Clone, Inspect, Debug, Default, Copy)]
807struct FloppyStateInternals {
808 floppy_changed: bool,
809 floppy_present: bool,
810 media_write_protected: bool,
811 io_pending: bool,
812
813 num_bytes_rd: u32,
814 num_bytes_wr: u32,
815 sectors_per_track: u8,
816 start_sector_pos: u32,
817 sector_cache_start_logical: u32,
818 sector_cache_end_logical: u32,
819}
820
821#[derive(Inspect, Debug, Clone, Copy)]
822struct ReadWriteHeadLocation {
823 cylinder: u8,
824 head: u8,
825 sector: u8,
826}
827
828impl ReadWriteHeadLocation {
829 fn new() -> Self {
830 Self {
831 cylinder: 0,
832 head: 0,
833 sector: 0,
834 }
835 }
836
837 fn chs_to_lba(&self, sectors_per_track: u8) -> u32 {
841 (self.cylinder as u32 * 2 + self.head as u32) * sectors_per_track as u32
842 + (self.sector as u32 - 1)
843 }
844}
845
846#[derive(Clone, Inspect, Debug)]
847#[inspect(external_tag)]
848enum SenseOutput {
849 ResetCounter { count: u8 },
853 Value { value: protocol::StatusRegister0 },
855}
856
857impl FloppyState {
858 fn new(sectors_per_track: u8, read_only: bool) -> Self {
859 Self {
860 digital_output: protocol::DigitalOutputRegister::new(),
861 main_status: protocol::MainStatusRegister::new(),
862 position: ReadWriteHeadLocation::new(),
863 end_of_track: 0,
864
865 input_bytes: ArrayVec::new(),
866 output_bytes: ArrayVec::new(),
867
868 head_unload_timer: 0,
869 step_rate_time: 0,
870 head_load_timer: 0,
871 dma_disabled: false,
872 sense_output: None,
873 interrupt_level: false,
874
875 internals: FloppyStateInternals::new(sectors_per_track, read_only),
876
877 pending_command: FloppyCommand::INVALID,
878 }
879 }
880}
881
882impl FloppyStateInternals {
883 fn new(sectors_per_track: u8, read_only: bool) -> Self {
884 let floppy_present = sectors_per_track != 0;
887
888 Self {
889 floppy_changed: false,
890 floppy_present,
891 media_write_protected: read_only,
892 io_pending: false,
893
894 num_bytes_rd: 0,
895 num_bytes_wr: 0,
896 sectors_per_track,
897 start_sector_pos: 0,
898 sector_cache_start_logical: 0,
899 sector_cache_end_logical: 0,
900 }
901 }
902}
903
904#[derive(Inspect)]
905struct FloppyRt {
906 interrupt: LineInterrupt,
907 pio_base: Box<dyn ControlPortIoIntercept>,
908 pio_control: Box<dyn ControlPortIoIntercept>,
909}
910
911#[derive(InspectMut)]
913pub struct FloppyDiskController {
914 guest_memory: GuestMemory,
915
916 rt: FloppyRt,
918
919 state: FloppyState,
921
922 disk_drive: DriveRibbon,
924
925 #[inspect(skip)]
926 dma: Box<dyn IsaDmaChannel>,
927
928 #[inspect(skip)]
929 command_buffer: CommandBuffer,
930
931 #[inspect(with = "Option::is_some")]
932 io: Option<Io>,
933 #[inspect(skip)]
934 waker: Option<Waker>,
935}
936
937#[derive(Inspect)]
939#[inspect(external_tag)]
940pub enum DriveRibbon {
941 None,
943 Single(#[inspect(rename = "media")] Disk),
945 }
948
949#[derive(Debug, Error)]
951#[error("too many drives")]
952pub struct TooManyDrives;
953
954impl DriveRibbon {
955 pub fn from_vec(drives: Vec<Disk>) -> Result<Self, TooManyDrives> {
957 match drives.len() {
958 0 => Ok(Self::None),
959 1 => Ok(Self::Single(drives.into_iter().next().unwrap())),
960 _ => Err(TooManyDrives),
961 }
962 }
963}
964
965#[derive(Debug, Error)]
967pub enum NewFloppyDiskControllerError {
968 #[error("disk is non-standard size: {0} bytes")]
970 NonStandardDisk(u64),
971}
972
973impl FloppyDiskController {
974 pub fn new(
976 guest_memory: GuestMemory,
977 interrupt: LineInterrupt,
978 register_pio: &mut dyn RegisterPortIoIntercept,
979 pio_base_addr: u16,
980 disk_drive: DriveRibbon,
981 dma: Box<dyn IsaDmaChannel>,
982 ) -> Result<Self, NewFloppyDiskControllerError> {
983 let mut pio_base = register_pio.new_io_region("base", 6);
984 let mut pio_control = register_pio.new_io_region("control", 1);
985
986 pio_base.map(pio_base_addr);
987 pio_control.map(pio_base_addr + RegisterOffset::DIGITAL_INPUT.0);
990
991 Ok(Self {
992 guest_memory,
993 rt: FloppyRt {
994 interrupt,
995 pio_base,
996 pio_control,
997 },
998 state: FloppyState::new(
999 {
1000 match &disk_drive {
1001 DriveRibbon::None => {
1002 0
1005 }
1006 DriveRibbon::Single(disk) => {
1007 let file_size = disk.sector_count() * disk.sector_size() as u64;
1008
1009 let image_type = FloppyImageType::from_file_size(file_size)
1010 .ok_or(NewFloppyDiskControllerError::NonStandardDisk(file_size))?;
1011 image_type.sectors()
1012 }
1013 }
1014 },
1015 match &disk_drive {
1016 DriveRibbon::Single(disk) => disk.is_read_only(),
1017 DriveRibbon::None => false,
1018 },
1019 ),
1020 disk_drive,
1021 dma,
1022 command_buffer: CommandBuffer::new(),
1023 io: None,
1024 waker: None,
1025 })
1026 }
1027
1028 fn set_io<F, Fut>(&mut self, f: F)
1030 where
1031 F: FnOnce(Disk) -> Fut,
1032 Fut: 'static + Future<Output = Result<(), disk_backend::DiskError>> + Send,
1033 {
1034 let DriveRibbon::Single(disk) = &self.disk_drive else {
1035 panic!();
1036 };
1037
1038 let fut = (f)(disk.clone());
1039 assert!(self.io.is_none());
1040 self.io = Some(Io(Box::pin(fut)));
1041 if let Some(waker) = self.waker.take() {
1043 waker.wake();
1044 }
1045 }
1046
1047 fn handle_io_completion(&mut self, result: Result<(), disk_backend::DiskError>) {
1048 let command = self.state.pending_command;
1049 tracing::trace!(?command, ?result, "io completion");
1050
1051 let result = match command {
1052 FloppyCommand::READ_NORMAL_DATA
1053 | FloppyCommand::READ_NORMAL_DEL_DATA
1054 | FloppyCommand::READ_DEL_DATA
1055 | FloppyCommand::SEEK_AND_READ
1056 | FloppyCommand::ALT_SEEK_AND_READ
1057 | FloppyCommand::READ_TRACK => match result {
1058 Ok(()) => self.read_complete(),
1059 Err(err) => Err(err),
1060 },
1061 FloppyCommand::WRITE_NORMAL_DATA
1062 | FloppyCommand::WRITE_DATA
1063 | FloppyCommand::WRITE_DEL_DATA
1064 | FloppyCommand::SEEK_AND_WRITE => match result {
1065 Ok(()) => self.write_complete(),
1066 Err(err) => Err(err),
1067 },
1068 FloppyCommand::FORMAT_TRACK | FloppyCommand::FORMAT_DOUBLE_DENSITY_MODE => match result
1069 {
1070 Ok(()) => self.write_zeros_complete(),
1071 Err(err) => Err(err),
1072 },
1073 _ => {
1074 tracelimit::error_ratelimited!(?command, "unexpected command!");
1075 return;
1076 }
1077 };
1078
1079 if let Err(err) = result {
1080 let wo_error = matches!(err, disk_backend::DiskError::ReadOnly);
1081 self.set_output_status(true, wo_error, true);
1082 }
1083
1084 self.state.pending_command = FloppyCommand::INVALID;
1085 self.complete_command(true);
1086 }
1087
1088 fn read_complete(&mut self) -> Result<(), disk_backend::DiskError> {
1091 let buffer = match self.dma.request(IsaDmaDirection::Write) {
1094 Some(r) => r,
1095 None => {
1096 tracelimit::error_ratelimited!("request_dma for read failed");
1097 return Err(disk_backend::DiskError::Io(std::io::Error::other(
1098 "request_dma for read failed",
1099 )));
1100 }
1101 };
1102
1103 let size = (buffer.size.div_ceil(STANDARD_FLOPPY_SECTOR_SIZE) * STANDARD_FLOPPY_SECTOR_SIZE)
1104 as u32;
1105
1106 let buffer_ptr = &self.command_buffer.buffer[0..size as usize][..size as usize];
1107
1108 let res = self
1109 .guest_memory
1110 .write_from_atomic(buffer.address, buffer_ptr);
1111
1112 self.dma.complete();
1113
1114 if let Err(err) = res {
1115 tracelimit::error_ratelimited!(
1116 error = &err as &dyn std::error::Error,
1117 "dma transfer failed"
1118 );
1119 return Err(disk_backend::DiskError::MemoryAccess(err.into()));
1120 }
1121
1122 self.set_output_status(false, false, false);
1123
1124 Ok(())
1125 }
1126
1127 fn write_complete(&mut self) -> Result<(), disk_backend::DiskError> {
1128 self.set_output_status(false, false, false);
1129 Ok(())
1130 }
1131
1132 fn write_zeros_complete(&mut self) -> Result<(), disk_backend::DiskError> {
1133 self.set_output_status(false, false, true);
1134 Ok(())
1135 }
1136
1137 pub fn offset_of(&self, addr: u16) -> Option<u16> {
1142 self.rt.pio_base.offset_of(addr).or_else(|| {
1143 self.rt
1144 .pio_control
1145 .offset_of(addr)
1146 .map(|_| RegisterOffset::DIGITAL_INPUT.0)
1147 })
1148 }
1149
1150 fn raise_interrupt(&mut self, is_reset: bool) {
1151 if self.state.digital_output.dma_enabled() || is_reset {
1152 self.rt.interrupt.set_level(true);
1153 self.state.interrupt_level = true;
1154 }
1155 }
1156
1157 fn lower_interrupt(&mut self) {
1158 self.rt.interrupt.set_level(false);
1159 self.state.interrupt_level = false;
1160 }
1161
1162 fn reset(&mut self, preserve_digital_output: bool) {
1166 self.lower_interrupt();
1167 self.state = FloppyState {
1168 digital_output: if preserve_digital_output {
1169 self.state.digital_output
1170 } else {
1171 protocol::DigitalOutputRegister::new()
1172 },
1173 ..FloppyState::new(
1174 self.state.internals.sectors_per_track,
1175 self.state.internals.media_write_protected,
1176 )
1177 };
1178
1179 self.state.main_status = protocol::MainStatusRegister::new().with_main_request(true);
1182
1183 tracing::trace!(
1184 preserve_digital_output,
1185 "controller reset - deasserting floppy interrupt"
1186 );
1187 }
1188
1189 fn parse_input_for_readwrite(&mut self) {
1190 let input = protocol::InputRegister::from(self.state.input_bytes[1]);
1191 if input.drive_select() != 0 {
1192 tracelimit::warn_ratelimited!(
1193 "Drive selected as outside of what is supported in data read"
1194 );
1195 }
1196
1197 let head = input.head();
1198
1199 self.state.position.head = head;
1200 self.state.position.cylinder = self.state.input_bytes[2];
1201 if self.state.position.cylinder > FLOPPY_TOTAL_CYLINDERS {
1202 tracelimit::warn_ratelimited!(?self.state.position.cylinder, "Floppy seek to cylinder > 80");
1203 }
1204 self.state.position.sector = self.state.input_bytes[4];
1205 self.state.end_of_track = self.state.input_bytes[6];
1206 if self.state.input_bytes[5] != 2 || self.state.input_bytes[8] != 0xFF {
1207 tracelimit::warn_ratelimited!(?self.state.input_bytes, "non-standard floppy read command parameters for PC floppy format");
1208 }
1209 }
1210
1211 fn get_sense_output(&mut self) -> &mut protocol::StatusRegister0 {
1212 match self.state.sense_output {
1213 Some(SenseOutput::Value { ref mut value }) => value,
1214 _ => {
1215 self.state.sense_output = Some(SenseOutput::Value {
1216 value: protocol::StatusRegister0::new(),
1217 });
1218
1219 match self.state.sense_output {
1220 Some(SenseOutput::Value { ref mut value }) => value,
1221 _ => panic!(),
1222 }
1223 }
1224 }
1225 }
1226
1227 fn handle_sense_interrupt_status(&mut self) {
1228 match self.state.sense_output {
1229 Some(SenseOutput::ResetCounter { ref mut count }) => {
1230 if *count > 0 {
1235 let out = protocol::StatusRegister0::from(4 - *count)
1236 .with_invalid_command(true)
1237 .with_abnormal_termination(true);
1238 self.state.sense_output = if (*count - 1) == 0 {
1239 None
1240 } else {
1241 Some(SenseOutput::ResetCounter { count: *count - 1 })
1242 };
1243 self.state.output_bytes.push(self.state.position.cylinder);
1244 self.state.output_bytes.push(out.into());
1245 } else {
1246 tracelimit::error_ratelimited!(
1247 "SENSE_INTERRUPT_STATUS called with ResetCount stage 0. p lease fix me"
1248 );
1249 self.state.output_bytes.push(INVALID_COMMAND_STATUS);
1250 self.state.output_bytes.push(INVALID_COMMAND_STATUS);
1251 }
1252 }
1253 Some(SenseOutput::Value { value }) => {
1254 self.state.output_bytes.push(self.state.position.cylinder);
1255 self.state.output_bytes.push(value.into());
1256
1257 self.state.sense_output = None;
1258 }
1259 _ => {
1260 self.state.output_bytes.push(INVALID_COMMAND_STATUS);
1261 self.state.output_bytes.push(INVALID_COMMAND_STATUS);
1262 }
1263 }
1264
1265 tracing::trace!("sense interrupt status cmd - deasserting floppy interrupt");
1266
1267 self.lower_interrupt();
1268
1269 self.state.main_status = (self.state.main_status)
1270 .with_data_direction(protocol::DataDirection::Write.as_bool())
1271 .with_non_dma_mode(false)
1272 .with_busy(false)
1273 .with_main_request(true);
1274 }
1275
1276 fn handle_sense_drive_status(&mut self) {
1277 let input = protocol::InputRegister::from(self.state.input_bytes[1]);
1278 let drive: u8 = input.drive_select();
1279 if drive != 0 {
1280 tracelimit::warn_ratelimited!(
1281 ?drive,
1282 "Floppy drive number out of range from DSDT enforcement"
1283 );
1284 }
1285
1286 let head: u8 = input.head();
1287 self.state.position.head = head;
1288
1289 let output = protocol::StatusRegister3::new()
1290 .with_drive_select(drive)
1291 .with_head(head)
1292 .with_unused1(true)
1293 .with_track0(self.state.position.cylinder == 0)
1294 .with_unused2(true)
1295 .with_write_protected(self.state.internals.media_write_protected);
1296
1297 self.state.output_bytes.push(output.into());
1298 self.get_sense_output().set_seek_end(true);
1299 }
1300
1301 fn set_output_status(&mut self, rw_error: bool, wo_error: bool, end_seek: bool) {
1302 if !self.state.output_bytes.is_empty() {
1303 tracelimit::warn_ratelimited!("output_setup_long called with non-empty output_bytes");
1304 }
1305 self.state.output_bytes.push(0x2); self.state.output_bytes.push(1);
1307
1308 self.state.output_bytes.push(self.state.position.head);
1309 self.state.output_bytes.push(self.state.position.cylinder);
1310 self.state.output_bytes.push(
1311 protocol::StatusRegister2::new()
1312 .with_missing_address(rw_error)
1313 .with_bad_cylinder(rw_error)
1314 .into(),
1315 );
1316
1317 self.state.output_bytes.push(
1318 protocol::StatusRegister1::new()
1319 .with_no_data(rw_error)
1320 .with_missing_address(rw_error)
1321 .with_write_protected(wo_error)
1322 .into(),
1323 );
1324
1325 self.state.output_bytes.push({
1326 let drive = 0; let out = protocol::StatusRegister0::new()
1328 .with_drive_select(drive)
1329 .with_head(self.state.position.head)
1330 .with_abnormal_termination(rw_error || wo_error)
1331 .with_seek_end(end_seek);
1332
1333 out.into()
1334 });
1335 }
1336
1337 fn complete_command(&mut self, request_interrupt: bool) {
1338 let has_output = !self.state.output_bytes.is_empty();
1339 self.state.main_status.set_busy(has_output);
1340 self.state.main_status.set_non_dma_mode(false);
1341
1342 let dma_type = if has_output {
1343 protocol::DataDirection::Read
1344 } else {
1345 protocol::DataDirection::Write
1346 };
1347 self.state
1348 .main_status
1349 .set_data_direction(dma_type.as_bool());
1350 self.state.main_status.set_main_request(true);
1351
1352 if request_interrupt {
1353 self.raise_interrupt(false);
1354 }
1355 }
1356
1357 fn handle_data_write(&mut self, data: u8) {
1362 if !self.state.digital_output.controller_enabled() {
1364 return;
1366 }
1367
1368 self.state.input_bytes.push(data);
1369 let command = FloppyCommand(self.state.input_bytes[0]);
1370
1371 tracing::trace!(
1375 ?data,
1376 ?self.state.input_bytes,
1377 "floppy byte (cmd or param)"
1378 );
1379
1380 self.handle_command(command);
1381 }
1382
1383 fn handle_command(&mut self, command: FloppyCommand) {
1384 if !self.state.output_bytes.is_empty() {
1385 tracelimit::warn_ratelimited!(output_bytes = ?self.state.output_bytes, "Floppy data register write with bytes still pending");
1386 }
1387 self.state.output_bytes.clear();
1388
1389 if self.state.input_bytes.len() < command.input_bytes_needed() {
1390 tracing::debug!(
1391 ?command,
1392 bytes_needed = ?command.input_bytes_needed(),
1393 bytes_received = ?self.state.input_bytes.len(),
1394 "floppy command missing (or waiting for) parameters"
1395 );
1396
1397 self.state.main_status.set_busy(true);
1399 return;
1400 }
1401
1402 tracing::trace!(
1403 ?command,
1404 input_bytes = ?self.state.input_bytes,
1405 "executing floppy command"
1406 );
1407
1408 if self.state.interrupt_level && command != FloppyCommand::SENSE_INTERRUPT_STATUS {
1414 tracing::trace!(
1415 ?command,
1416 "Floppy interrupt level was high before command execution. Now de-asserting interrupt"
1417 );
1418 self.lower_interrupt();
1419 self.state.main_status.set_active_drives(0);
1420 }
1421
1422 let mut complete_command = true;
1423 let mut request_interrupt = false;
1424
1425 match command {
1426 FloppyCommand::READ_NORMAL_DATA
1427 | FloppyCommand::READ_NORMAL_DEL_DATA
1428 | FloppyCommand::READ_DEL_DATA
1429 | FloppyCommand::SEEK_AND_READ
1430 | FloppyCommand::ALT_SEEK_AND_READ => {
1431 let success = self.handle_read();
1432 request_interrupt = !success;
1433 complete_command = !success;
1434 }
1435 FloppyCommand::WRITE_NORMAL_DATA
1436 | FloppyCommand::WRITE_DATA
1437 | FloppyCommand::WRITE_DEL_DATA
1438 | FloppyCommand::SEEK_AND_WRITE => {
1439 let success = self.handle_write();
1440 request_interrupt = !success;
1441 complete_command = !success;
1442 }
1443 FloppyCommand::READ_TRACK => {
1444 self.state.input_bytes[2] = 0;
1446 let success = self.handle_read();
1447 request_interrupt = !success;
1448 complete_command = !success;
1449 }
1450 FloppyCommand::VERSION => {
1451 self.state.output_bytes.push(0x90);
1453 }
1454 FloppyCommand::FORMAT_TRACK | FloppyCommand::FORMAT_DOUBLE_DENSITY_MODE => {
1455 let success = self.format();
1456 request_interrupt = !success;
1457 complete_command = !success;
1458 }
1459 FloppyCommand::SEEK => {
1460 self.handle_seek();
1461 request_interrupt = true;
1462 }
1463 FloppyCommand::RECALIBRATE => {
1464 self.handle_recalibrate();
1465 request_interrupt = true;
1466 }
1467 FloppyCommand::SENSE_INTERRUPT_STATUS => {
1468 self.handle_sense_interrupt_status();
1469 }
1470 FloppyCommand::SPECIFY => self.handle_specify(),
1471 FloppyCommand::SENSE_DRIVE_STATUS => self.handle_sense_drive_status(),
1472 FloppyCommand::DUMP_REGISTERS => self.handle_dump_registers(),
1473 FloppyCommand::READ_ID => {
1474 self.read_id();
1475 request_interrupt = true;
1476 }
1477
1478 FloppyCommand::UNLOCK_FIFO_FUNCTIONS => {
1481 self.state.output_bytes.push(0);
1482 }
1483 FloppyCommand::LOCK_FIFO_FUNCTIONS => {
1484 self.state.output_bytes.push(0x10);
1485 }
1486 FloppyCommand::PART_ID => {
1487 self.state.output_bytes.push(0x01);
1488 }
1489 FloppyCommand::CONFIGURE | FloppyCommand::PERP288_MODE => {
1490 tracing::debug!(?command, "command ignored");
1492 }
1493 _ => {
1494 tracelimit::error_ratelimited!(?command, "unimplemented/unsupported command");
1495 self.state.output_bytes.push(INVALID_COMMAND_STATUS);
1496 }
1497 }
1498
1499 self.state.input_bytes.clear();
1501
1502 if !self.state.output_bytes.is_empty() {
1503 if self.state.output_bytes.len() != command.result_bytes_expected() {
1504 tracelimit::warn_ratelimited!(?command, output_bytes = ?self.state.output_bytes, "command output size doesn't match expected");
1505 } else {
1506 tracing::trace!(
1507 ?command,
1508 output_bytes = ?self.state.output_bytes,
1509 "floppy command output"
1510 );
1511 }
1512 }
1513
1514 self.state.pending_command = if complete_command {
1515 self.complete_command(request_interrupt);
1516 FloppyCommand::INVALID
1517 } else {
1518 command
1519 };
1520
1521 tracing::trace!(
1522 main_status = ?self.state.main_status,
1523 digital_output = ?self.state.digital_output,
1524 sense_output = ?self.state.sense_output,
1525 dma_disabled = ?self.state.dma_disabled,
1526 cylinder = ?self.state.position.cylinder,
1527 head = ?self.state.position.head,
1528 sector = ?self.state.position.sector,
1529 interrupt_level = ?self.state.interrupt_level,
1530 "floppy state"
1531 );
1532
1533 tracing::trace!("floppy command completed");
1534 }
1535
1536 fn handle_read(&mut self) -> bool {
1537 self.state.internals.floppy_changed = false;
1539
1540 self.get_sense_output().set_seek_end(true);
1542
1543 self.state.main_status.set_main_request(false);
1545
1546 if self.state.dma_disabled {
1551 tracelimit::warn_ratelimited!("non-dma mode is not supported");
1552 self.state.main_status.set_non_dma_mode(true);
1553 }
1554
1555 let input = protocol::InputRegister::from(self.state.input_bytes[1]);
1557 let busy_drive = input.drive_select();
1558 self.state
1559 .main_status
1560 .set_active_drives(self.state.main_status.active_drives() | (1 << busy_drive));
1561
1562 self.state.main_status.set_busy(true);
1563
1564 self.parse_input_for_readwrite();
1565
1566 let error = !self.read_data();
1567 if error {
1568 self.state.internals.io_pending = false;
1569 self.set_output_status(error, false, error);
1570 }
1571 !error
1572 }
1573
1574 fn read_data(&mut self) -> bool {
1575 if !self.state.internals.floppy_present {
1576 tracelimit::error_ratelimited!("read attempted, but floppy not present");
1577 return false;
1578 }
1579
1580 if self.state.position.sector == 0
1581 || self.state.position.sector > self.state.end_of_track
1582 || self.state.position.sector > self.state.internals.sectors_per_track
1583 {
1584 tracelimit::error_ratelimited!(
1585 position = ?self.state.position,
1586 end_of_track = self.state.end_of_track,
1587 sectors_per_track = self.state.internals.sectors_per_track,
1588 "invalid read position"
1589 );
1590 return false;
1591 }
1592
1593 if self.state.position.cylinder > FLOPPY_TOTAL_CYLINDERS {
1594 tracelimit::error_ratelimited!(sector = ?self.state.position.sector, "bad sector in floppy read");
1595 return false;
1596 }
1597
1598 self.state.internals.io_pending = true;
1599 let size_hint = self.dma.check_transfer_size() as usize;
1600
1601 let lba = (self.state.position).chs_to_lba(self.state.internals.sectors_per_track) as u64;
1603
1604 let size = {
1605 let num = (size_hint.div_ceil(STANDARD_FLOPPY_SECTOR_SIZE)
1606 * STANDARD_FLOPPY_SECTOR_SIZE) as u32;
1607 if num < STANDARD_FLOPPY_SECTOR_SIZE as u32 {
1608 STANDARD_FLOPPY_SECTOR_SIZE as u32
1609 } else {
1610 num
1611 }
1612 };
1613
1614 let command_buffer = self.command_buffer.access();
1615
1616 tracing::trace!(lba, size, "starting disk read");
1617 self.set_io(async move |disk| {
1618 let buffers = command_buffer.buffers(0, size as usize, true);
1619 disk.read_vectored(&buffers, lba).await
1620 });
1621
1622 true
1623 }
1624
1625 fn handle_write(&mut self) -> bool {
1626 self.state.internals.floppy_changed = false;
1627
1628 self.get_sense_output().set_seek_end(true);
1630
1631 self.state.main_status.set_main_request(false);
1633
1634 if self.state.dma_disabled {
1639 tracelimit::warn_ratelimited!("non-dma mode is not supported");
1640 self.state.main_status.set_non_dma_mode(true);
1641 }
1642
1643 let input = protocol::InputRegister::from(self.state.input_bytes[1]);
1645 let busy_drive = input.drive_select();
1646 self.state
1647 .main_status
1648 .set_active_drives(self.state.main_status.active_drives() | (1 << busy_drive));
1649
1650 self.state.main_status.set_busy(true);
1651
1652 self.parse_input_for_readwrite();
1653
1654 let wo_error = self.state.internals.media_write_protected;
1655 let error = if wo_error { true } else { !self.write_data() };
1656
1657 if error {
1658 self.set_output_status(error, wo_error, error);
1659 }
1660 !error
1661 }
1662
1663 fn write_data(&mut self) -> bool {
1664 if !self.state.internals.floppy_present {
1665 tracelimit::error_ratelimited!("write attempted, but floppy not present");
1666 return false;
1667 }
1668
1669 if self.state.position.sector == 0
1670 || self.state.position.sector > self.state.end_of_track
1671 || self.state.position.sector > self.state.internals.sectors_per_track
1672 {
1673 tracelimit::error_ratelimited!(
1674 position = ?self.state.position,
1675 end_of_track = self.state.end_of_track,
1676 sectors_per_track = self.state.internals.sectors_per_track,
1677 "invalid write position"
1678 );
1679 return false;
1680 }
1681
1682 let lba = (self.state.position).chs_to_lba(self.state.internals.sectors_per_track) as u64;
1683 let buffer = match self.dma.request(IsaDmaDirection::Read) {
1684 Some(r) => r,
1685 None => {
1686 tracelimit::error_ratelimited!("request_dma for write failed");
1687 return false;
1688 }
1689 };
1690
1691 let size = buffer.size.div_ceil(STANDARD_FLOPPY_SECTOR_SIZE) * STANDARD_FLOPPY_SECTOR_SIZE;
1692
1693 let command_buffer = self.command_buffer.access();
1694
1695 let buffer_ptr = &self.command_buffer.buffer[0..size as usize][..size as usize];
1696 let r = self.guest_memory.read_to_atomic(buffer.address, buffer_ptr);
1697
1698 self.dma.complete();
1699
1700 if let Err(err) = r {
1701 tracelimit::error_ratelimited!(
1702 error = &err as &dyn std::error::Error,
1703 "dma transfer failed"
1704 );
1705
1706 return false;
1707 }
1708
1709 let DriveRibbon::Single(disk) = &self.disk_drive else {
1710 tracelimit::error_ratelimited!("No disk");
1711 return false;
1712 };
1713
1714 if disk.is_read_only() {
1715 tracelimit::error_ratelimited!("Read only");
1716 return false;
1717 }
1718
1719 self.set_io(async move |disk| {
1720 let buffers = command_buffer.buffers(0, size as usize, false);
1721 let result = disk.write_vectored(&buffers, lba, false).await;
1722 if let Err(err) = result {
1723 tracelimit::error_ratelimited!(
1724 error = &err as &dyn std::error::Error,
1725 "write failed"
1726 );
1727 return Err(err);
1728 }
1729 let result = disk.sync_cache().await;
1730 if let Err(err) = result {
1731 tracelimit::error_ratelimited!(
1732 error = &err as &dyn std::error::Error,
1733 "flush failed"
1734 );
1735 return Err(err);
1736 }
1737
1738 result
1739 });
1740
1741 true
1742 }
1743
1744 fn write_zeros(&mut self) -> bool {
1745 let DriveRibbon::Single(disk) = &self.disk_drive else {
1746 tracelimit::error_ratelimited!("No disk");
1747 return false;
1748 };
1749
1750 if disk.is_read_only() {
1751 tracelimit::error_ratelimited!("Read only");
1752 return false;
1753 }
1754
1755 let buffer = match self.dma.request(IsaDmaDirection::Read) {
1756 Some(r) => r,
1757 None => {
1758 tracelimit::error_ratelimited!("request_dma for format failed");
1759 return false;
1760 }
1761 };
1762
1763 let size = (buffer.size.div_ceil(STANDARD_FLOPPY_SECTOR_SIZE) * STANDARD_FLOPPY_SECTOR_SIZE)
1764 as u32;
1765
1766 let command_buffer = self.command_buffer.access();
1767
1768 let buffer_ptr = &self.command_buffer.buffer[0..size as usize][..size as usize];
1769 let r = self.guest_memory.read_to_atomic(buffer.address, buffer_ptr);
1770
1771 self.dma.complete();
1772
1773 if let Err(err) = r {
1774 tracelimit::error_ratelimited!(
1775 error = &err as &dyn std::error::Error,
1776 "dma transfer failed"
1777 );
1778
1779 return false;
1780 }
1781
1782 let Some(cylinder) = buffer_ptr.first() else {
1783 tracelimit::error_ratelimited!("failed to get(0)");
1784 return false;
1785 };
1786
1787 let cylinder = cylinder.load(Ordering::Relaxed) as u64;
1788
1789 let Some(head) = buffer_ptr.get(1) else {
1790 tracelimit::error_ratelimited!("failed to get(1)");
1791 return false;
1792 };
1793
1794 let head = head.load(Ordering::Relaxed) as u64;
1795
1796 let size = STANDARD_FLOPPY_SECTOR_SIZE * self.state.internals.sectors_per_track as usize;
1797 let buffers = command_buffer.buffers(0, size, false);
1798
1799 let res = buffers.guest_memory().zero_range(&buffers.range());
1800 if let Err(err) = res {
1801 tracelimit::error_ratelimited!(
1802 error = &err as &dyn std::error::Error,
1803 "zero_range failed"
1804 );
1805 return false;
1806 }
1807
1808 let lba = (cylinder * 2 + head) * self.state.internals.sectors_per_track as u64;
1809
1810 tracing::trace!(?cylinder, ?head, ?lba, ?buffer_ptr, "Format: ");
1811
1812 self.set_io(async move |disk| {
1813 let buffers = command_buffer.buffers(0, size, false);
1814 let result = disk.write_vectored(&buffers, lba, false).await;
1815 if let Err(err) = result {
1816 tracelimit::error_ratelimited!(
1817 error = &err as &dyn std::error::Error,
1818 "write failed"
1819 );
1820 return Err(err);
1821 }
1822 let result = disk.sync_cache().await;
1823 if let Err(err) = result {
1824 tracelimit::error_ratelimited!(
1825 error = &err as &dyn std::error::Error,
1826 "flush failed"
1827 );
1828 return Err(err);
1829 }
1830 result
1831 });
1832
1833 true
1834 }
1835
1836 fn format(&mut self) -> bool {
1837 let wo_err_occurred = self.state.internals.media_write_protected;
1838 let error = if wo_err_occurred {
1839 true
1840 } else {
1841 self.state.main_status.set_busy(true);
1842 !self.write_zeros()
1843 };
1844
1845 if error {
1846 self.set_output_status(error, wo_err_occurred, true);
1847 }
1848 !error
1849 }
1850
1851 fn handle_seek(&mut self) {
1852 self.state.internals.floppy_changed = false;
1853
1854 self.state.position.sector = 0;
1855
1856 let input = protocol::InputRegister::from(self.state.input_bytes[1]);
1857 self.state.position.head = input.head();
1858
1859 self.state.position.cylinder = if self.state.input_bytes[2] >= FLOPPY_TOTAL_CYLINDERS {
1860 tracelimit::warn_ratelimited!(?self.state.position.cylinder, "Floppy seek to cylinder > 80");
1861 0
1862 } else {
1863 self.state.input_bytes[2] };
1865
1866 self.recalibrate();
1867 }
1868
1869 fn handle_recalibrate(&mut self) {
1870 self.state.position.cylinder = 0;
1871 self.recalibrate();
1872 }
1873
1874 fn recalibrate(&mut self) {
1875 if let Some(SenseOutput::ResetCounter { .. }) = self.state.sense_output {
1876 self.state.sense_output = None;
1877 }
1878
1879 let head = self.state.position.head;
1884 self.get_sense_output().set_seek_end(true);
1885 self.get_sense_output().set_head(head);
1886
1887 let input = protocol::InputRegister::from(self.state.input_bytes[1]);
1889 let busy_drive = input.drive_select();
1890 self.state
1891 .main_status
1892 .set_active_drives(self.state.main_status.active_drives() | (1 << busy_drive));
1893 if busy_drive > 0 {
1894 tracelimit::warn_ratelimited!(
1895 ?busy_drive,
1896 "Floppy seek to drive outside of what is supported"
1897 );
1898 }
1899 }
1900
1901 fn handle_specify(&mut self) {
1902 let param1 = protocol::SpecifyParam1::from(self.state.input_bytes[1]);
1903 let param2 = protocol::SpecifyParam2::from(self.state.input_bytes[2]);
1904
1905 self.state.head_unload_timer = param1.head_unload_timer();
1906 self.state.step_rate_time = param1.step_rate_time();
1907 self.state.head_load_timer = param2.head_load_timer();
1908 self.state.dma_disabled = param2.dma_disabled();
1909 }
1910
1911 fn handle_dump_registers(&mut self) {
1912 self.state.output_bytes.push(self.state.position.cylinder);
1913 self.state.output_bytes.push(0); self.state.output_bytes.push(0); self.state.output_bytes.push(0); self.state.output_bytes.push(
1918 protocol::SpecifyParam1::new()
1919 .with_head_unload_timer(self.state.head_unload_timer)
1920 .with_step_rate_time(self.state.step_rate_time)
1921 .into(),
1922 );
1923 self.state.output_bytes.push(
1924 protocol::SpecifyParam2::new()
1925 .with_head_load_timer(self.state.head_load_timer)
1926 .with_dma_disabled(self.state.dma_disabled)
1927 .into(),
1928 );
1929
1930 self.state.output_bytes.push(0); self.state.output_bytes.push(0); self.state.output_bytes.push(0); self.state.output_bytes.push(0); }
1938
1939 fn read_id(&mut self) {
1940 let input = protocol::InputRegister::from(self.state.input_bytes[1]);
1942 self.state.position.head = input.head();
1943
1944 self.set_output_status(false, false, false);
1946 let head = self.state.position.head;
1947 self.get_sense_output().set_head(head);
1948 }
1949}
1950
1951mod save_restore {
1952 use super::*;
1953 use vmcore::save_restore::RestoreError;
1954 use vmcore::save_restore::SaveError;
1955 use vmcore::save_restore::SaveRestore;
1956
1957 mod state {
1958 use mesh::payload::Protobuf;
1959 use vmcore::save_restore::SavedStateRoot;
1960
1961 #[derive(Protobuf, SavedStateRoot)]
1962 #[mesh(package = "chipset.floppy")]
1963 pub struct SavedState {
1964 #[mesh(1)]
1965 pub digital_output: u8,
1966 #[mesh(2)]
1967 pub main_status: u8,
1968 #[mesh(3)]
1969 pub input_bytes: Vec<u8>,
1970 #[mesh(4)]
1971 pub output_bytes: Vec<u8>,
1972 #[mesh(5)]
1973 pub head_unload_timer: u8,
1974 #[mesh(6)]
1975 pub step_rate_time: u8,
1976 #[mesh(7)]
1977 pub head_load_timer: u8,
1978 #[mesh(8)]
1979 pub dma_disabled: bool,
1980 #[mesh(9)]
1981 pub interrupt_output: Option<SavedInterruptOutput>,
1982 #[mesh(10)]
1983 pub interrupt_level: bool,
1984
1985 #[mesh(11)]
1986 pub end_of_track: u8,
1987 #[mesh(12)]
1990 pub drive: u8,
1991 #[mesh(13)]
1993 pub floppies: [SavedFloppyState; 1],
1994 #[mesh(14)]
1995 pub pending_command: u8,
1996 }
1997
1998 #[derive(Protobuf)]
1999 #[mesh(package = "chipset.floppy")]
2000 pub struct SavedFloppyState {
2001 #[mesh(1)]
2002 pub cylinder: u8,
2003 #[mesh(2)]
2004 pub head: u8,
2005 #[mesh(3)]
2006 pub sector: u8,
2007 #[mesh(4)]
2008 pub internals: SavedFloppyStateInternals,
2009 }
2010
2011 #[derive(Protobuf)]
2012 #[mesh(package = "chipset.floppy")]
2013 pub enum SavedInterruptOutput {
2014 #[mesh(1)]
2015 ResetCounter {
2016 #[mesh(1)]
2017 count: u8,
2018 },
2019 #[mesh(2)]
2020 Value {
2021 #[mesh(1)]
2022 value: u8,
2023 },
2024 }
2025
2026 impl From<SavedInterruptOutput> for super::SenseOutput {
2027 fn from(value: SavedInterruptOutput) -> Self {
2028 match value {
2029 SavedInterruptOutput::ResetCounter { count } => {
2030 super::SenseOutput::ResetCounter { count }
2031 }
2032 SavedInterruptOutput::Value { value } => super::SenseOutput::Value {
2033 value: super::protocol::StatusRegister0::from(value),
2034 },
2035 }
2036 }
2037 }
2038
2039 impl From<super::SenseOutput> for SavedInterruptOutput {
2040 fn from(value: super::SenseOutput) -> Self {
2041 match value {
2042 super::SenseOutput::ResetCounter { count } => {
2043 SavedInterruptOutput::ResetCounter { count }
2044 }
2045 super::SenseOutput::Value { value } => SavedInterruptOutput::Value {
2046 value: u8::from(value),
2047 },
2048 }
2049 }
2050 }
2051
2052 #[derive(Protobuf, Clone, Copy)]
2053 #[mesh(package = "chipset.floppy")]
2054 pub struct SavedFloppyStateInternals {
2055 #[mesh(1)]
2056 floppy_changed: bool,
2057 #[mesh(2)]
2058 floppy_present: bool,
2059 #[mesh(3)]
2060 media_write_protected: bool,
2061 #[mesh(4)]
2062 io_pending: bool,
2063
2064 #[mesh(5)]
2065 num_bytes_rd: u32,
2066 #[mesh(6)]
2067 num_bytes_wr: u32,
2068 #[mesh(7)]
2069 sectors_per_track: u8,
2070 #[mesh(8)]
2071 start_sector_pos: u32,
2072 #[mesh(9)]
2073 sector_cache_start_logical: u32,
2074 #[mesh(10)]
2075 sector_cache_end_logical: u32,
2076 }
2077
2078 impl From<super::FloppyStateInternals> for SavedFloppyStateInternals {
2079 fn from(value: super::FloppyStateInternals) -> Self {
2080 let super::FloppyStateInternals {
2081 floppy_changed,
2082 floppy_present,
2083 media_write_protected,
2084 io_pending,
2085 num_bytes_rd,
2086 num_bytes_wr,
2087 sectors_per_track,
2088 start_sector_pos,
2089 sector_cache_start_logical,
2090 sector_cache_end_logical,
2091 } = value;
2092
2093 Self {
2094 floppy_changed,
2095 floppy_present,
2096 media_write_protected,
2097 io_pending,
2098 num_bytes_rd,
2099 num_bytes_wr,
2100 sectors_per_track,
2101 start_sector_pos,
2102 sector_cache_start_logical,
2103 sector_cache_end_logical,
2104 }
2105 }
2106 }
2107
2108 impl From<SavedFloppyStateInternals> for super::FloppyStateInternals {
2109 fn from(value: SavedFloppyStateInternals) -> Self {
2110 let SavedFloppyStateInternals {
2111 floppy_changed,
2112 floppy_present,
2113 media_write_protected,
2114 io_pending,
2115 num_bytes_rd,
2116 num_bytes_wr,
2117 sectors_per_track,
2118 start_sector_pos,
2119 sector_cache_start_logical,
2120 sector_cache_end_logical,
2121 } = value;
2122
2123 Self {
2124 floppy_changed,
2125 floppy_present,
2126 media_write_protected,
2127 io_pending,
2128 num_bytes_rd,
2129 num_bytes_wr,
2130 sectors_per_track,
2131 start_sector_pos,
2132 sector_cache_start_logical,
2133 sector_cache_end_logical,
2134 }
2135 }
2136 }
2137 }
2138
2139 impl SaveRestore for FloppyDiskController {
2140 type SavedState = state::SavedState;
2141
2142 fn save(&mut self) -> Result<Self::SavedState, SaveError> {
2143 let FloppyState {
2144 digital_output,
2145 main_status,
2146 ref input_bytes,
2147 ref output_bytes,
2148 head_unload_timer,
2149 step_rate_time,
2150 head_load_timer,
2151 dma_disabled,
2152 sense_output: ref interrupt_output,
2153 interrupt_level,
2154 position,
2155 internals,
2156 end_of_track,
2157 pending_command,
2158 } = self.state;
2159
2160 let saved_state = state::SavedState {
2161 digital_output: digital_output.into(),
2162 main_status: main_status.into(),
2163 input_bytes: input_bytes.to_vec(),
2164 output_bytes: output_bytes.to_vec(),
2165 head_unload_timer,
2166 step_rate_time,
2167 head_load_timer,
2168 dma_disabled,
2169 interrupt_output: interrupt_output.clone().map(|x| x.into()),
2170 interrupt_level,
2171 end_of_track,
2172 drive: 0,
2173 floppies: [state::SavedFloppyState {
2174 cylinder: position.cylinder,
2175 head: position.head,
2176 sector: position.sector,
2177 internals: internals.into(),
2178 }],
2179 pending_command: pending_command.0,
2180 };
2181
2182 Ok(saved_state)
2183 }
2184
2185 fn restore(&mut self, state: Self::SavedState) -> Result<(), RestoreError> {
2186 let state::SavedState {
2187 digital_output,
2188 main_status,
2189 input_bytes,
2190 output_bytes,
2191 head_unload_timer,
2192 step_rate_time,
2193 head_load_timer,
2194 dma_disabled,
2195 interrupt_output,
2196 interrupt_level,
2197 end_of_track,
2198 drive: _,
2199 floppies,
2200 pending_command,
2201 } = state;
2202
2203 self.state = FloppyState {
2204 digital_output: digital_output.into(),
2205 main_status: main_status.into(),
2206 input_bytes: input_bytes.as_slice().try_into().map_err(
2207 |e: arrayvec::CapacityError| RestoreError::InvalidSavedState(e.into()),
2208 )?,
2209 output_bytes: output_bytes.as_slice().try_into().map_err(
2210 |e: arrayvec::CapacityError| RestoreError::InvalidSavedState(e.into()),
2211 )?,
2212 head_unload_timer,
2213 step_rate_time,
2214 head_load_timer,
2215 dma_disabled,
2216 sense_output: interrupt_output.map(|x| x.into()),
2217 interrupt_level,
2218 end_of_track,
2219 position: ReadWriteHeadLocation {
2220 cylinder: floppies[0].cylinder,
2221 head: floppies[0].head,
2222 sector: floppies[0].sector,
2223 },
2224 internals: floppies[0].internals.into(),
2225 pending_command: FloppyCommand(pending_command),
2226 };
2227
2228 self.rt.interrupt.set_level(interrupt_level);
2229
2230 Ok(())
2231 }
2232 }
2233}