vm_device/
device_manager.rs

1// Copyright © 2019 Intel Corporation. All Rights Reserved.
2// SPDX-License-Identifier: (Apache-2.0 OR BSD-3-Clause)
3
4//! System level device management.
5//!
6//! [`IoManager`] is responsible for managing
7//! all devices of virtual machine, registering IO resources callback,
8//! deregistering devices and helping VM IO exit handling.
9//! It defines two buses, one for PIO and one for MMIO, and provides default
10//! implementations of [`PioManager`] and [`MmioManager`].
11//!
12//! The VMM must first allocate unique resources (such as bus ranges), and then
13//! call into the vm-device interface to register the devices with their
14//! corresponding resources.
15//!
16//! # Examples
17//!
18//! Registering a new device can be done using the register methods of [`PioManager`]
19//! and [`MmioManager`] with an appropriate bus range
20//! ([`PioRange`](../bus/type.PioRange.html) or [`MmioRange`](../bus/type.MmioRange.html)).
21//! ```
22//! # use std::sync::Arc;
23//! # use vm_device::bus::{PioAddress, PioAddressOffset, PioRange};
24//! # use vm_device::bus::{MmioAddress, MmioAddressOffset, MmioRange};
25//! # use vm_device::device_manager::{IoManager, PioManager, MmioManager};
26//! # use vm_device::{DevicePio, DeviceMmio};
27//! struct NoopDevice {}
28//!
29//! impl DevicePio for NoopDevice {
30//!     fn pio_read(&self, base: PioAddress, offset: PioAddressOffset, data: &mut [u8]) {}
31//!     fn pio_write(&self, base: PioAddress, offset: PioAddressOffset, data: &[u8]) {}
32//! }
33//!
34//! impl DeviceMmio for NoopDevice {
35//!     fn mmio_read(&self, base: MmioAddress, offset: MmioAddressOffset, data: &mut [u8]) {}
36//!     fn mmio_write(&self, base: MmioAddress, offset: MmioAddressOffset, data: &[u8]) {}
37//! }
38//!
39//! // IoManager implements both PioManager and MmioManager.
40//! let mut manager = IoManager::new();
41//!
42//! // Register the device on the PIO bus.
43//! let pio_range = PioRange::new(PioAddress(0), 10).unwrap();
44//! manager
45//!     .register_pio(pio_range, Arc::new(NoopDevice {}))
46//!     .unwrap();
47//!
48//! // Register the device on the MMIO bus.
49//! let mmio_range = MmioRange::new(MmioAddress(0), 10).unwrap();
50//! manager
51//!     .register_mmio(mmio_range, Arc::new(NoopDevice {}))
52//!     .unwrap();
53//!
54//! // Dispatch I/O on the PIO bus.
55//! manager.pio_write(PioAddress(0), &vec![b'o', b'k']).unwrap();
56//!
57//! // Dispatch I/O on the MMIO bus.
58//! manager
59//!     .mmio_write(MmioAddress(0), &vec![b'o', b'k'])
60//!     .unwrap();
61//! ```
62//!
63//! An alternative way would be to use [`resources`](../resources/index.html) and the
64//! resources registration methods of [`IoManager`]:
65//! * [`register_pio_resources`](struct.IoManager.html#method.register_pio_resources)
66//! * [`register_mmio_resources`](struct.IoManager.html#method.register_mmio_resources)
67//! * or generic [`register_resources`](struct.IoManager.html#method.register_resources)
68//! ```
69//! # use std::sync::Arc;
70//! # use vm_device::bus::{PioAddress, PioAddressOffset, PioRange};
71//! # use vm_device::bus::{MmioAddress, MmioAddressOffset, MmioRange};
72//! # use vm_device::device_manager::{IoManager, PioManager, MmioManager};
73//! # use vm_device::{DevicePio, DeviceMmio};
74//! # use vm_device::resources::Resource;
75//! # struct NoopDevice {}
76//! #
77//! # impl DevicePio for NoopDevice {
78//! #    fn pio_read(&self, base: PioAddress, offset: PioAddressOffset, data: &mut [u8]) {}
79//! #    fn pio_write(&self, base: PioAddress, offset: PioAddressOffset, data: &[u8]) {}
80//! # }
81//! #
82//! # impl DeviceMmio for NoopDevice {
83//! #    fn mmio_read(&self, base: MmioAddress, offset: MmioAddressOffset, data: &mut [u8]) {}
84//! #    fn mmio_write(&self, base: MmioAddress, offset: MmioAddressOffset, data: &[u8]) {}
85//! # }
86//! // Use the same NoopDevice defined above.
87//!
88//! let mut manager = IoManager::new();
89//!
90//! // Define a PIO address range resource.
91//! let pio = Resource::PioAddressRange {
92//!    base: 0,
93//!    size: 10,
94//! };
95//!
96//! // Define a MMIO address range resource.
97//! let mmio = Resource::MmioAddressRange {
98//!    base: 0,
99//!    size: 10,
100//! };
101//!
102//! // Register the PIO resource.
103//! manager
104//!     .register_pio_resources(Arc::new(NoopDevice {}), &vec![pio])
105//!     .unwrap();
106//!
107//! // Register the MMIO resource.
108//! manager
109//!     .register_mmio_resources(Arc::new(NoopDevice {}), &vec![mmio])
110//!     .unwrap();
111//!
112//! // Dispatching I/O is the same.
113//! manager.pio_write(PioAddress(0), &vec![b'o', b'k']).unwrap();
114//! manager.mmio_write(MmioAddress(0), &vec![b'o', b'k']).unwrap();
115//! ```
116
117use std::fmt::{Display, Formatter};
118use std::result::Result;
119use std::sync::Arc;
120
121use crate::bus::{self, BusManager, MmioAddress, MmioBus, MmioRange, PioAddress, PioBus, PioRange};
122use crate::resources::Resource;
123use crate::{DeviceMmio, DevicePio};
124
125/// Error type for [IoManager] usage.
126#[derive(Debug)]
127pub enum Error {
128    /// Error during bus operation.
129    Bus(bus::Error),
130}
131
132impl Display for Error {
133    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
134        match self {
135            Error::Bus(_) => write!(f, "device_manager: bus error"),
136        }
137    }
138}
139
140impl std::error::Error for Error {
141    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
142        match self {
143            Error::Bus(e) => Some(e),
144        }
145    }
146}
147
148/// Represents an object that provides PIO manager operations.
149pub trait PioManager {
150    /// Type of the objects that can be registered with this `PioManager`.
151    type D: DevicePio;
152
153    /// Return a reference to the device registered at `addr`, together with the associated
154    /// range, if available.
155    fn pio_device(&self, addr: PioAddress) -> Option<(&PioRange, &Self::D)>;
156
157    /// Dispatch a read operation to the device registered at `addr`.
158    fn pio_read(&self, addr: PioAddress, data: &mut [u8]) -> Result<(), bus::Error>;
159
160    /// Dispatch a write operation to the device registered at `addr`.
161    fn pio_write(&self, addr: PioAddress, data: &[u8]) -> Result<(), bus::Error>;
162
163    /// Register the provided device with the specified range.
164    fn register_pio(&mut self, range: PioRange, device: Self::D) -> Result<(), bus::Error>;
165
166    /// Deregister the device currently registered at `addr` together with the
167    /// associated range.
168    fn deregister_pio(&mut self, addr: PioAddress) -> Option<(PioRange, Self::D)>;
169}
170
171// This automatically provides a `PioManager` implementation for types that already implement
172// `BusManager<PioAddress>` if their inner associated type implements `DevicePio` as well.
173impl<T> PioManager for T
174where
175    T: BusManager<PioAddress>,
176    T::D: DevicePio,
177{
178    type D = <Self as BusManager<PioAddress>>::D;
179
180    fn pio_device(&self, addr: PioAddress) -> Option<(&PioRange, &Self::D)> {
181        self.bus().device(addr)
182    }
183
184    fn pio_read(&self, addr: PioAddress, data: &mut [u8]) -> Result<(), bus::Error> {
185        self.bus()
186            .check_access(addr, data.len())
187            .map(|(range, device)| device.pio_read(range.base(), addr - range.base(), data))
188    }
189
190    fn pio_write(&self, addr: PioAddress, data: &[u8]) -> Result<(), bus::Error> {
191        self.bus()
192            .check_access(addr, data.len())
193            .map(|(range, device)| device.pio_write(range.base(), addr - range.base(), data))
194    }
195
196    fn register_pio(&mut self, range: PioRange, device: Self::D) -> Result<(), bus::Error> {
197        self.bus_mut().register(range, device)
198    }
199
200    fn deregister_pio(&mut self, addr: PioAddress) -> Option<(PioRange, Self::D)> {
201        self.bus_mut().deregister(addr)
202    }
203}
204
205/// Represents an object that provides MMIO manager operations.
206pub trait MmioManager {
207    /// Type of the objects that can be registered with this `MmioManager`.
208    type D: DeviceMmio;
209
210    /// Return a reference to the device registered at `addr`, together with the associated
211    /// range, if available.
212    fn mmio_device(&self, addr: MmioAddress) -> Option<(&MmioRange, &Self::D)>;
213
214    /// Dispatch a read operation to the device registered at `addr`.
215    fn mmio_read(&self, addr: MmioAddress, data: &mut [u8]) -> Result<(), bus::Error>;
216
217    /// Dispatch a write operation to the device registered at `addr`.
218    fn mmio_write(&self, addr: MmioAddress, data: &[u8]) -> Result<(), bus::Error>;
219
220    /// Register the provided device with the specified range.
221    fn register_mmio(&mut self, range: MmioRange, device: Self::D) -> Result<(), bus::Error>;
222
223    /// Deregister the device currently registered at `addr` together with the
224    /// associated range.
225    fn deregister_mmio(&mut self, addr: MmioAddress) -> Option<(MmioRange, Self::D)>;
226}
227
228// This automatically provides a `MmioManager` implementation for types that already implement
229// `BusManager<MmioAddress>` if their inner associated type implements `DeviceMmio` as well.
230impl<T> MmioManager for T
231where
232    T: BusManager<MmioAddress>,
233    T::D: DeviceMmio,
234{
235    type D = <Self as BusManager<MmioAddress>>::D;
236
237    fn mmio_device(&self, addr: MmioAddress) -> Option<(&MmioRange, &Self::D)> {
238        self.bus().device(addr)
239    }
240
241    fn mmio_read(&self, addr: MmioAddress, data: &mut [u8]) -> Result<(), bus::Error> {
242        self.bus()
243            .check_access(addr, data.len())
244            .map(|(range, device)| device.mmio_read(range.base(), addr - range.base(), data))
245    }
246
247    fn mmio_write(&self, addr: MmioAddress, data: &[u8]) -> Result<(), bus::Error> {
248        self.bus()
249            .check_access(addr, data.len())
250            .map(|(range, device)| device.mmio_write(range.base(), addr - range.base(), data))
251    }
252
253    fn register_mmio(&mut self, range: MmioRange, device: Self::D) -> Result<(), bus::Error> {
254        self.bus_mut().register(range, device)
255    }
256
257    fn deregister_mmio(&mut self, addr: MmioAddress) -> Option<(MmioRange, Self::D)> {
258        self.bus_mut().deregister(addr)
259    }
260}
261
262/// System IO manager serving for all devices management and VM exit handling.
263#[derive(Default)]
264pub struct IoManager {
265    // Range mapping for VM exit pio operations.
266    pio_bus: PioBus<Arc<dyn DevicePio + Send + Sync>>,
267    // Range mapping for VM exit mmio operations.
268    mmio_bus: MmioBus<Arc<dyn DeviceMmio + Send + Sync>>,
269}
270
271// Enables the automatic implementation of `PioManager` for `IoManager`.
272impl BusManager<PioAddress> for IoManager {
273    type D = Arc<dyn DevicePio + Send + Sync>;
274
275    fn bus(&self) -> &PioBus<Arc<dyn DevicePio + Send + Sync>> {
276        &self.pio_bus
277    }
278
279    fn bus_mut(&mut self) -> &mut PioBus<Arc<dyn DevicePio + Send + Sync>> {
280        &mut self.pio_bus
281    }
282}
283
284// Enables the automatic implementation of `MmioManager` for `IoManager`.
285impl BusManager<MmioAddress> for IoManager {
286    type D = Arc<dyn DeviceMmio + Send + Sync>;
287
288    fn bus(&self) -> &MmioBus<Arc<dyn DeviceMmio + Send + Sync>> {
289        &self.mmio_bus
290    }
291
292    fn bus_mut(&mut self) -> &mut MmioBus<Arc<dyn DeviceMmio + Send + Sync>> {
293        &mut self.mmio_bus
294    }
295}
296
297impl IoManager {
298    /// Create an default IoManager with empty IO member.
299    pub fn new() -> Self {
300        IoManager::default()
301    }
302
303    /// Register a new MMIO device with its allocated resources.
304    /// VMM is responsible for providing the allocated resources to virtual device.
305    ///
306    /// # Arguments
307    ///
308    /// * `device`: device instance object to be registered
309    /// * `resources`: resources that this device owns, might include
310    ///                port I/O and memory-mapped I/O ranges, irq number, etc.
311    pub fn register_mmio_resources(
312        &mut self,
313        device: Arc<dyn DeviceMmio + Send + Sync>,
314        resources: &[Resource],
315    ) -> Result<(), Error> {
316        // Register and mark device resources
317        // The resources addresses being registered are sucessfully allocated before.
318        for res in resources.iter() {
319            match *res {
320                Resource::MmioAddressRange { base, size } => {
321                    self.register_mmio(
322                        MmioRange::new(MmioAddress(base), size).unwrap(),
323                        device.clone(),
324                    )
325                    .map_err(Error::Bus)?;
326                }
327                _ => continue,
328            }
329        }
330        Ok(())
331    }
332
333    /// Register a new PIO device with its allocated resources.
334    /// VMM is responsible for providing the allocated resources to virtual device.
335    ///
336    /// # Arguments
337    ///
338    /// * `device`: device instance object to be registered
339    /// * `resources`: resources that this device owns, might include
340    ///                port I/O and memory-mapped I/O ranges, irq number, etc.
341    pub fn register_pio_resources(
342        &mut self,
343        device: Arc<dyn DevicePio + Send + Sync>,
344        resources: &[Resource],
345    ) -> Result<(), Error> {
346        // Register and mark device resources
347        // The resources addresses being registered are sucessfully allocated before.
348        for res in resources.iter() {
349            match *res {
350                Resource::PioAddressRange { base, size } => {
351                    self.register_pio(
352                        PioRange::new(PioAddress(base), size).unwrap(),
353                        device.clone(),
354                    )
355                    .map_err(Error::Bus)?;
356                }
357                _ => continue,
358            }
359        }
360        Ok(())
361    }
362
363    /// Register a new MMIO + PIO device with its allocated resources.
364    /// VMM is responsible for providing the allocated resources to virtual device.
365    ///
366    /// # Arguments
367    ///
368    /// * `device`: device instance object to be registered
369    /// * `resources`: resources that this device owns, might include
370    ///                port I/O and memory-mapped I/O ranges, irq number, etc.
371    pub fn register_resources<T: DeviceMmio + DevicePio + 'static + Send + Sync>(
372        &mut self,
373        device: Arc<T>,
374        resources: &[Resource],
375    ) -> Result<(), Error> {
376        self.register_mmio_resources(device.clone(), resources)?;
377        self.register_pio_resources(device, resources)
378    }
379
380    /// Deregister a device from `IoManager`, e.g. users specified removing.
381    /// VMM pre-fetches the resources e.g. dev.get_assigned_resources()
382    /// VMM is responsible for freeing the resources. Returns the number
383    /// of deregistered devices.
384    ///
385    /// # Arguments
386    ///
387    /// * `resources`: resources that this device owns, might include
388    ///                port I/O and memory-mapped I/O ranges, irq number, etc.
389    pub fn deregister_resources(&mut self, resources: &[Resource]) -> usize {
390        let mut count = 0;
391        for res in resources.iter() {
392            match *res {
393                Resource::PioAddressRange { base, .. } => {
394                    if self.deregister_pio(PioAddress(base)).is_some() {
395                        count += 1;
396                    }
397                }
398                Resource::MmioAddressRange { base, .. } => {
399                    if self.deregister_mmio(MmioAddress(base)).is_some() {
400                        count += 1;
401                    }
402                }
403                _ => continue,
404            }
405        }
406        count
407    }
408}
409
410#[cfg(test)]
411mod tests {
412    use super::*;
413
414    use std::error::Error;
415    use std::sync::Mutex;
416
417    use bus::{MmioAddressOffset, PioAddressOffset};
418
419    const PIO_ADDRESS_SIZE: u16 = 4;
420    const PIO_ADDRESS_BASE: u16 = 0x40;
421    const MMIO_ADDRESS_SIZE: u64 = 0x8765_4321;
422    const MMIO_ADDRESS_BASE: u64 = 0x1234_5678;
423    const LEGACY_IRQ: u32 = 4;
424    const CONFIG_DATA: u32 = 0x1234;
425
426    struct DummyDevice {
427        config: Mutex<u32>,
428    }
429
430    impl DummyDevice {
431        fn new(config: u32) -> Self {
432            DummyDevice {
433                config: Mutex::new(config),
434            }
435        }
436    }
437
438    impl DevicePio for DummyDevice {
439        fn pio_read(&self, _base: PioAddress, _offset: PioAddressOffset, data: &mut [u8]) {
440            if data.len() > 4 {
441                return;
442            }
443            for (idx, iter) in data.iter_mut().enumerate() {
444                let config = self.config.lock().expect("failed to acquire lock");
445                *iter = (*config >> (idx * 8) & 0xff) as u8;
446            }
447        }
448
449        fn pio_write(&self, _base: PioAddress, _offset: PioAddressOffset, data: &[u8]) {
450            let mut config = self.config.lock().expect("failed to acquire lock");
451            *config = u32::from(data[0]) & 0xff;
452        }
453    }
454
455    impl DeviceMmio for DummyDevice {
456        fn mmio_read(&self, _base: MmioAddress, _offset: MmioAddressOffset, data: &mut [u8]) {
457            if data.len() > 4 {
458                return;
459            }
460            for (idx, iter) in data.iter_mut().enumerate() {
461                let config = self.config.lock().expect("failed to acquire lock");
462                *iter = (*config >> (idx * 8) & 0xff) as u8;
463            }
464        }
465
466        fn mmio_write(&self, _base: MmioAddress, _offset: MmioAddressOffset, data: &[u8]) {
467            let mut config = self.config.lock().expect("failed to acquire lock");
468            *config = u32::from(data[0]) & 0xff;
469        }
470    }
471
472    #[test]
473    fn test_register_deregister_device_io() {
474        let mut io_mgr = IoManager::new();
475        let dummy = DummyDevice::new(0);
476        let dum = Arc::new(dummy);
477
478        let mut resource: Vec<Resource> = Vec::new();
479        let mmio = Resource::MmioAddressRange {
480            base: MMIO_ADDRESS_BASE,
481            size: MMIO_ADDRESS_SIZE,
482        };
483        let irq = Resource::LegacyIrq(LEGACY_IRQ);
484        let pio = Resource::PioAddressRange {
485            base: PIO_ADDRESS_BASE,
486            size: PIO_ADDRESS_SIZE,
487        };
488
489        resource.push(mmio);
490        resource.push(irq);
491        resource.push(pio);
492
493        assert!(io_mgr
494            .register_mmio_resources(dum.clone(), &resource)
495            .is_ok());
496        assert!(io_mgr.register_pio_resources(dum, &resource).is_ok());
497        assert_eq!(io_mgr.deregister_resources(&resource), 2);
498    }
499
500    #[test]
501    fn test_mmio_read_write() {
502        let mut io_mgr: IoManager = Default::default();
503        let dum = Arc::new(DummyDevice::new(CONFIG_DATA));
504        let mut resource: Vec<Resource> = Vec::new();
505
506        let mmio = Resource::MmioAddressRange {
507            base: MMIO_ADDRESS_BASE,
508            size: MMIO_ADDRESS_SIZE,
509        };
510        resource.push(mmio);
511        assert!(io_mgr
512            .register_mmio_resources(dum.clone(), &resource)
513            .is_ok());
514
515        let mut data = [0; 4];
516        assert!(io_mgr
517            .mmio_read(MmioAddress(MMIO_ADDRESS_BASE), &mut data)
518            .is_ok());
519        assert_eq!(data, [0x34, 0x12, 0, 0]);
520
521        assert!(io_mgr
522            .mmio_read(
523                MmioAddress(MMIO_ADDRESS_BASE + MMIO_ADDRESS_SIZE),
524                &mut data
525            )
526            .is_err());
527
528        data = [0; 4];
529        assert!(io_mgr
530            .mmio_write(MmioAddress(MMIO_ADDRESS_BASE), &data)
531            .is_ok());
532        assert_eq!(*dum.config.lock().unwrap(), 0);
533
534        assert!(io_mgr
535            .mmio_write(MmioAddress(MMIO_ADDRESS_BASE + MMIO_ADDRESS_SIZE), &data)
536            .is_err());
537    }
538
539    #[test]
540    fn test_pio_read_write() {
541        let mut io_mgr: IoManager = Default::default();
542        let dum = Arc::new(DummyDevice::new(CONFIG_DATA));
543        let mut resource: Vec<Resource> = Vec::new();
544
545        let pio = Resource::PioAddressRange {
546            base: PIO_ADDRESS_BASE,
547            size: PIO_ADDRESS_SIZE,
548        };
549        resource.push(pio);
550        assert!(io_mgr
551            .register_pio_resources(dum.clone(), &resource)
552            .is_ok());
553
554        let mut data = [0; 4];
555        assert!(io_mgr
556            .pio_read(PioAddress(PIO_ADDRESS_BASE), &mut data)
557            .is_ok());
558        assert_eq!(data, [0x34, 0x12, 0, 0]);
559
560        assert!(io_mgr
561            .pio_read(PioAddress(PIO_ADDRESS_BASE + PIO_ADDRESS_SIZE), &mut data)
562            .is_err());
563
564        data = [0; 4];
565        assert!(io_mgr
566            .pio_write(PioAddress(PIO_ADDRESS_BASE), &data)
567            .is_ok());
568        assert_eq!(*dum.config.lock().unwrap(), 0);
569
570        assert!(io_mgr
571            .pio_write(PioAddress(PIO_ADDRESS_BASE + PIO_ADDRESS_SIZE), &data)
572            .is_err());
573    }
574
575    #[test]
576    fn test_error_code() {
577        let err = super::Error::Bus(bus::Error::DeviceOverlap);
578
579        assert!(err.source().is_some());
580        assert_eq!(format!("{}", err), "device_manager: bus error");
581    }
582}