Skip to main content

chipset/
dma.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! Implements dual 8237 ISA DMA controllers.
5//!
6//! Tested working with Floppy, but may require additional improvements to work
7//! properly with other legacy hardware (e.g: Sound Blaster)
8//!
9//! Rather than having the DMA controller device be some big, complicated
10//! behemoth that asynchronously resolves DMA requests itself (i.e: how the DMA
11//! controller works in actual hardware), our virtual DMA controller takes a
12//! simpler approach.
13//!
14//! This device is essentially just a big bundle-of-registers, which DMA capable
15//! devices can query via the specialized
16//! [`vmcore::isa_dma_channel::IsaDmaChannel`] trait in order to get the info
17//! they need to fulfil DMA requests themselves.
18
19#![warn(missing_docs)]
20
21use chipset_device::ChipsetDevice;
22use chipset_device::io::IoError;
23use chipset_device::io::IoResult;
24use chipset_device::isa_dma::IsaDmaController;
25use chipset_device::isa_dma::IsaDmaTransferBuffer;
26use chipset_device::isa_dma::IsaDmaTransferDirection;
27use chipset_device::pio::PortIoIntercept;
28use inspect::Inspect;
29use inspect::InspectMut;
30use open_enum::open_enum;
31use std::ops::RangeInclusive;
32use vm_resource::CanResolveTo;
33use vm_resource::kind::IsaDmaControllerHandleKind;
34use vmcore::device_state::ChangeDeviceState;
35
36pub mod resolver;
37
38/// A resolved ISA DMA controller resource.
39pub struct ResolvedIsaDmaController(pub DmaController);
40
41impl CanResolveTo<ResolvedIsaDmaController> for IsaDmaControllerHandleKind {
42    type Input<'a> = ();
43}
44
45// Skip registering page port 0x80 so that the PCAT BIOS can handle
46// it for debugging purposes.
47const DMA_PAGE_REGISTER_PORT_RANGE: u16 = 0x80;
48const PAGE_PORTS: RangeInclusive<u16> =
49    (DMA_PAGE_REGISTER_PORT_RANGE + 0x1)..=(DMA_PAGE_REGISTER_PORT_RANGE + 0xf);
50const CONTROLLER0_PORTS: RangeInclusive<u16> = 0x00..=0x0f;
51/// Only the even ports, e.g., 0xc0, 0xc2, ..., 0xde, due to the 16
52/// bit channel size that the second DMA controller supports.
53const CONTROLLER1_PORTS: RangeInclusive<u16> = 0xc0..=0xdf;
54
55const CHANNELS_PER_CONTROLLER: usize = 4;
56
57const PAGE_PORTS_FOR_CHANNEL: [usize; 8] = [0x7, 0x3, 0x1, 0x2, 0xF, 0xB, 0x9, 0xA];
58
59open_enum! {
60    // Starts at offset 8 from the register bank.
61    enum ControlRegister: u16 {
62        STATUS = 0,          // port 0x08 (controller 0), port 0xd0 (controller 1). Read-only
63        COMMAND = 0,         // port 0x08 (controller 0), port 0xd0 (controller 1). Write-only
64        REQUEST = 1,         // port 0x09 (controller 0), port 0xd2 (controller 1)
65        MASK = 2,            // port 0x0a (controller 0), port 0xd4 (controller 1)
66        MODE = 3,            // port 0x0b (controller 0), port 0xd6 (controller 1)
67        CLEAR_FLIP_FLOP = 4, // port 0x0c (controller 0), port 0xd8 (controller 1)
68        INTERMEDIATE = 5,    // port 0x0d (controller 0), port 0xda (controller 1). Read-only
69        RESET = 5,           // port 0x0d (controller 0), port 0xda (controller 1). Write-only
70        CLEAR_MASK = 6,      // port 0x0e (controller 0), port 0xdc (controller 1)
71        WRITE_MASK = 7,      // port 0x0f (controller 0), port 0xde (controller 1)
72    }
73}
74
75/// Dual 8237 DMA controllers.
76#[derive(Debug, InspectMut)]
77pub struct DmaController {
78    // Volatile state
79    state: DmaControllerState,
80}
81
82#[derive(Debug, Default, Clone, Inspect)]
83struct DmaControllerState {
84    #[inspect(iter_by_index)]
85    page_registers: [u8; 16],
86    controller0: Controller,
87    controller1: Controller,
88}
89
90impl DmaController {
91    /// Returns a new controller.
92    pub fn new() -> Self {
93        Self {
94            state: DmaControllerState::default(),
95        }
96    }
97
98    fn get_controller(&mut self, channel_number: usize) -> Option<&mut Controller> {
99        if channel_number < CHANNELS_PER_CONTROLLER {
100            Some(&mut self.state.controller0)
101        } else if channel_number < CHANNELS_PER_CONTROLLER * 2 {
102            Some(&mut self.state.controller1)
103        } else {
104            None
105        }
106    }
107}
108
109impl ChangeDeviceState for DmaController {
110    fn start(&mut self) {}
111
112    async fn stop(&mut self) {}
113
114    async fn reset(&mut self) {
115        self.state = Default::default();
116    }
117}
118
119impl ChipsetDevice for DmaController {
120    fn supports_pio(&mut self) -> Option<&mut dyn PortIoIntercept> {
121        Some(self)
122    }
123}
124
125impl IsaDmaController for DmaController {
126    fn check_transfer_size(&mut self, channel_number: usize) -> u16 {
127        let Some(controller) = self.get_controller(channel_number) else {
128            tracelimit::error_ratelimited!(?channel_number, "invalid channel number");
129            return 0;
130        };
131
132        controller.channels[channel_number % CHANNELS_PER_CONTROLLER].count
133    }
134
135    fn request(
136        &mut self,
137        channel_number: usize,
138        direction: IsaDmaTransferDirection,
139    ) -> Option<IsaDmaTransferBuffer> {
140        if channel_number >= CHANNELS_PER_CONTROLLER * 2 {
141            tracelimit::error_ratelimited!(?channel_number, "invalid channel number");
142            return None;
143        }
144
145        let page = self.state.page_registers[PAGE_PORTS_FOR_CHANNEL[channel_number]];
146
147        let controller = self.get_controller(channel_number).unwrap();
148        if controller.disabled {
149            tracelimit::warn_ratelimited!(?channel_number, "channel is disabled");
150            return None;
151        }
152
153        let channel_index = channel_number % CHANNELS_PER_CONTROLLER;
154
155        let channel = &controller.channels[channel_index];
156        if !channel.enabled {
157            tracing::warn!(
158                ?channel_number,
159                ?channel_index,
160                "channel currently disabled"
161            );
162            return None;
163        }
164
165        let transfer_type = match (channel.mode >> 2) & 0x3 {
166            0 => {
167                tracing::error!(?channel_number, "invalid request: mode is self-test");
168                return None;
169            }
170            1 => IsaDmaTransferDirection::Write,
171            2 => IsaDmaTransferDirection::Read,
172            _ => {
173                tracing::error!(?channel_number, "invalid request: mode is invalid");
174                return None;
175            }
176        };
177
178        if transfer_type != direction {
179            tracing::warn!(
180                ?channel_number,
181                ?channel_index,
182                "mismatch between programmed and requested transfer directions"
183            );
184            return None;
185        }
186
187        let address = channel.address as u64 | (page as u64) << 16;
188
189        // Report the channel as being active.
190        controller.status &= !(1 << channel_index);
191
192        Some(IsaDmaTransferBuffer {
193            address,
194            size: channel.count as usize,
195        })
196    }
197
198    fn complete(&mut self, channel_number: usize) {
199        let Some(controller) = self.get_controller(channel_number) else {
200            tracing::error!(?channel_number, "invalid channel number");
201            return;
202        };
203
204        let channel_index = channel_number % CHANNELS_PER_CONTROLLER;
205
206        if (controller.status & (1 << channel_index)) != 0 {
207            tracing::warn!(?channel_number, "channel was not active");
208        }
209
210        // Report the channel as being inactive.
211        controller.status |= 1 << channel_index;
212    }
213}
214
215impl PortIoIntercept for DmaController {
216    fn io_read(&mut self, io_port: u16, data: &mut [u8]) -> IoResult {
217        data.fill(0);
218        data[0] = if PAGE_PORTS.contains(&io_port) {
219            self.state.page_registers[io_port as usize & 0xf]
220        } else if CONTROLLER0_PORTS.contains(&io_port) {
221            match self.state.controller0.read(io_port) {
222                Ok(val) => val,
223                Err(e) => return IoResult::Err(e),
224            }
225        } else if CONTROLLER1_PORTS.contains(&io_port) {
226            // The secondary controller registers are 16 bits wide (but still have only 8 bytes of data).
227            match self.state.controller1.read((io_port / 2) & 0xf) {
228                Ok(val) => val,
229                Err(e) => return IoResult::Err(e),
230            }
231        } else {
232            return IoResult::Err(IoError::InvalidRegister);
233        };
234
235        IoResult::Ok
236    }
237
238    fn io_write(&mut self, io_port: u16, data: &[u8]) -> IoResult {
239        if PAGE_PORTS.contains(&io_port) {
240            self.state.page_registers[io_port as usize & 0xf] = data[0];
241            IoResult::Ok
242        } else if CONTROLLER0_PORTS.contains(&io_port) {
243            self.state.controller0.write(io_port, data[0])
244        } else if CONTROLLER1_PORTS.contains(&io_port) {
245            // The secondary controller registers are 16 bits wide (but still have only 8 bytes of data).
246            self.state.controller1.write((io_port / 2) & 0xf, data[0])
247        } else {
248            IoResult::Err(IoError::InvalidRegister)
249        }
250    }
251
252    fn get_static_regions(&mut self) -> &[(&str, RangeInclusive<u16>)] {
253        &[
254            ("page", PAGE_PORTS),
255            ("controller0", CONTROLLER0_PORTS),
256            ("controller1", CONTROLLER1_PORTS),
257        ]
258    }
259}
260
261/// A single DMA controller.
262#[derive(Debug, Default, Clone, Inspect)]
263struct Controller {
264    #[inspect(iter_by_index)]
265    channels: [Channel; CHANNELS_PER_CONTROLLER],
266    flip_flop_high: bool,
267    latched_address: u16,
268    latched_count: u16,
269    disabled: bool,
270    status: u8,
271}
272
273#[derive(Debug, Default, Clone, Inspect)]
274struct Channel {
275    #[inspect(hex)]
276    address: u16,
277    #[inspect(hex)]
278    count: u16,
279    #[inspect(hex)]
280    mode: u8,
281    enabled: bool,
282}
283
284impl Controller {
285    fn read(&mut self, reg: u16) -> Result<u8, IoError> {
286        let res = if reg < 8 {
287            let channel = reg as usize / 2;
288            let data = if reg.is_multiple_of(2) {
289                // Address port.
290                if !self.flip_flop_high {
291                    self.latched_address = self.channels[channel].address;
292                }
293                self.latched_address
294            } else {
295                // Word count port.
296                if !self.flip_flop_high {
297                    self.latched_count = self.channels[channel].count;
298                }
299                self.latched_count
300            };
301
302            // Extract the high or low byte depending on the flip-flop state.
303            self.flip_flop_high = !self.flip_flop_high;
304            if !self.flip_flop_high {
305                (data >> 8) as u8
306            } else {
307                data as u8
308            }
309        } else {
310            match ControlRegister(reg - 8) {
311                ControlRegister::STATUS => std::mem::take(&mut self.status),
312                ControlRegister::INTERMEDIATE => 0,
313                ControlRegister::WRITE_MASK => {
314                    let mut data = 0xf0;
315                    for (n, channel) in self.channels.iter().enumerate() {
316                        if channel.enabled {
317                            // should this be `!channel.enabled`?
318                            data |= 1 << n;
319                        }
320                    }
321                    data
322                }
323                _ => return Err(IoError::InvalidRegister),
324            }
325        };
326
327        Ok(res)
328    }
329
330    fn write(&mut self, reg: u16, data: u8) -> IoResult {
331        if reg < 8 {
332            let channel = reg as usize / 2;
333            let mem = if reg.is_multiple_of(2) {
334                // Address port.
335                &mut self.channels[channel].address
336            } else {
337                &mut self.channels[channel].count
338            };
339            if self.flip_flop_high {
340                *mem = (*mem & 0xff) | (data as u16) << 8
341            } else {
342                *mem = (*mem & 0xff00) | data as u16
343            }
344            self.flip_flop_high = !self.flip_flop_high;
345        } else {
346            match ControlRegister(reg - 8) {
347                ControlRegister::COMMAND => {
348                    self.disabled = data != 0;
349                }
350                ControlRegister::REQUEST => {
351                    // Our emulation doesn't support software-initiated DMA
352                    // transfers. Specify that the channel has reached its
353                    // terminal count.
354                    self.status |= 1 << (data & 3);
355                }
356                ControlRegister::MASK => {
357                    self.channels[data as usize & 3].enabled = data & 4 == 0;
358                }
359                ControlRegister::MODE => self.channels[data as usize & 3].mode = data,
360                ControlRegister::RESET => {
361                    *self = Default::default();
362                }
363                ControlRegister::CLEAR_MASK => {
364                    for channel in &mut self.channels {
365                        channel.enabled = true;
366                    }
367                }
368                ControlRegister::WRITE_MASK => {
369                    for (n, channel) in self.channels.iter_mut().enumerate() {
370                        channel.enabled = data & (1 << n) == 0;
371                    }
372                }
373                ControlRegister::CLEAR_FLIP_FLOP => self.flip_flop_high = false,
374                _ => return IoResult::Err(IoError::InvalidRegister),
375            }
376        }
377
378        IoResult::Ok
379    }
380}
381
382mod save_restore {
383    use super::*;
384    use vmcore::save_restore::RestoreError;
385    use vmcore::save_restore::SaveError;
386    use vmcore::save_restore::SaveRestore;
387
388    mod state {
389        use mesh::payload::Protobuf;
390        use vmcore::save_restore::SavedStateRoot;
391
392        #[derive(Protobuf, SavedStateRoot)]
393        #[mesh(package = "chipset.dma")]
394        pub struct SavedState {
395            #[mesh(1)]
396            pub page_registers: [u8; 16],
397            #[mesh(2)]
398            pub controller0: SavedController,
399            #[mesh(3)]
400            pub controller1: SavedController,
401        }
402
403        #[derive(Protobuf)]
404        #[mesh(package = "chipset.dma")]
405        pub struct SavedController {
406            #[mesh(1)]
407            pub channels: [SavedChannel; 4],
408            #[mesh(2)]
409            pub flip_flop_high: bool,
410            #[mesh(3)]
411            pub latched_address: u16,
412            #[mesh(4)]
413            pub latched_count: u16,
414            #[mesh(5)]
415            pub status: u8,
416            #[mesh(6)]
417            pub disabled: bool,
418        }
419
420        #[derive(Protobuf)]
421        #[mesh(package = "chipset.dma")]
422        pub struct SavedChannel {
423            #[mesh(1)]
424            pub address: u16,
425            #[mesh(2)]
426            pub count: u16,
427            #[mesh(3)]
428            pub mode: u8,
429            #[mesh(4)]
430            pub enabled: bool,
431        }
432    }
433
434    impl SaveRestore for DmaController {
435        type SavedState = state::SavedState;
436
437        fn save(&mut self) -> Result<Self::SavedState, SaveError> {
438            let DmaControllerState {
439                page_registers,
440                controller0,
441                controller1,
442            } = self.state.clone();
443
444            let [controller0, controller1] = [controller0, controller1].map(|con| {
445                let Controller {
446                    channels,
447                    flip_flop_high,
448                    latched_address,
449                    latched_count,
450                    status,
451                    disabled,
452                } = con;
453
454                state::SavedController {
455                    channels: channels.map(|chan| {
456                        let Channel {
457                            address,
458                            count,
459                            mode,
460                            enabled,
461                        } = chan;
462
463                        state::SavedChannel {
464                            address,
465                            count,
466                            mode,
467                            enabled,
468                        }
469                    }),
470                    flip_flop_high,
471                    latched_address,
472                    latched_count,
473                    status,
474                    disabled,
475                }
476            });
477
478            let saved_state = state::SavedState {
479                page_registers,
480                controller0,
481                controller1,
482            };
483
484            Ok(saved_state)
485        }
486
487        fn restore(&mut self, state: Self::SavedState) -> Result<(), RestoreError> {
488            let state::SavedState {
489                page_registers,
490                controller0,
491                controller1,
492            } = state;
493
494            let [controller0, controller1] = [controller0, controller1].map(|con| {
495                let state::SavedController {
496                    channels,
497                    flip_flop_high,
498                    latched_address,
499                    latched_count,
500                    status,
501                    disabled,
502                } = con;
503
504                Controller {
505                    channels: channels.map(|chan| {
506                        let state::SavedChannel {
507                            address,
508                            count,
509                            mode,
510                            enabled,
511                        } = chan;
512
513                        Channel {
514                            address,
515                            count,
516                            mode,
517                            enabled,
518                        }
519                    }),
520                    flip_flop_high,
521                    latched_address,
522                    latched_count,
523                    status,
524                    disabled,
525                }
526            });
527
528            self.state = DmaControllerState {
529                page_registers,
530                controller0,
531                controller1,
532            };
533
534            Ok(())
535        }
536    }
537}