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