dbs_device/
lib.rs

1// Copyright 2020 Alibaba Cloud. All Rights Reserved.
2// Copyright © 2019 Intel Corporation. All Rights Reserved.
3// SPDX-License-Identifier: Apache-2.0
4
5#![deny(missing_docs)]
6
7//! Device model for Dragonball Secure Sandbox.
8//!
9//! The `dbs-device` crate, as a counterpart of [vm-device], defines device model for the
10//! Dragonball Secure Sandbox. The `dbs-device` crate shares some common concepts and data structures
11//! with [vm-device], but it also diverges from [vm-device] due to different VMM designs.
12//!
13//! [vm-device]: https://github.com/rust-vmm/vm-device
14
15use std::any::Any;
16use std::cmp::{Ord, PartialOrd};
17use std::convert::TryFrom;
18use std::sync::Mutex;
19
20use self::resources::DeviceResources;
21
22pub mod device_manager;
23pub mod resources;
24
25/// Size of MMIO range/access request.
26#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
27pub struct IoSize(pub u64);
28
29impl IoSize {
30    /// Get the raw value as u64 to make operation simple.
31    #[inline]
32    pub fn raw_value(self) -> u64 {
33        self.0
34    }
35}
36
37impl From<u64> for IoSize {
38    #[inline]
39    fn from(size: u64) -> Self {
40        IoSize(size)
41    }
42}
43
44impl From<IoSize> for u64 {
45    #[inline]
46    fn from(size: IoSize) -> Self {
47        size.0
48    }
49}
50
51/// Memory Mapped IO (MMIO) address.
52#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
53pub struct IoAddress(pub u64);
54
55impl IoAddress {
56    /// Get the raw value of IO Address to make operation simple.
57    #[inline]
58    pub fn raw_value(self) -> u64 {
59        self.0
60    }
61}
62
63impl From<u64> for IoAddress {
64    #[inline]
65    fn from(addr: u64) -> Self {
66        IoAddress(addr)
67    }
68}
69
70impl From<IoAddress> for u64 {
71    #[inline]
72    fn from(addr: IoAddress) -> Self {
73        addr.0
74    }
75}
76
77type PioAddressType = u16;
78
79/// Size of Port I/O range/request.
80#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
81pub struct PioSize(pub PioAddressType);
82
83impl PioSize {
84    /// Get the raw value as u64 to make operation simple.
85    #[inline]
86    pub fn raw_value(self) -> PioAddressType {
87        self.0
88    }
89}
90
91impl From<PioAddressType> for PioSize {
92    #[inline]
93    fn from(size: PioAddressType) -> Self {
94        PioSize(size)
95    }
96}
97
98impl From<PioSize> for PioAddressType {
99    #[inline]
100    fn from(size: PioSize) -> Self {
101        size.0
102    }
103}
104
105impl TryFrom<IoSize> for PioSize {
106    type Error = IoSize;
107
108    #[inline]
109    fn try_from(size: IoSize) -> Result<Self, Self::Error> {
110        if size.raw_value() <= std::u16::MAX as u64 {
111            Ok(PioSize(size.raw_value() as PioAddressType))
112        } else {
113            Err(size)
114        }
115    }
116}
117
118impl From<PioSize> for IoSize {
119    #[inline]
120    fn from(size: PioSize) -> Self {
121        IoSize(size.raw_value() as u64)
122    }
123}
124
125/// Port IO (PIO) address.
126#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
127pub struct PioAddress(pub PioAddressType);
128
129impl PioAddress {
130    /// Get the raw value of IO Address to make operation simple.
131    #[inline]
132    pub fn raw_value(self) -> PioAddressType {
133        self.0
134    }
135}
136
137impl From<PioAddressType> for PioAddress {
138    #[inline]
139    fn from(addr: PioAddressType) -> Self {
140        PioAddress(addr)
141    }
142}
143
144impl From<PioAddress> for PioAddressType {
145    #[inline]
146    fn from(addr: PioAddress) -> Self {
147        addr.0
148    }
149}
150
151impl TryFrom<IoAddress> for PioAddress {
152    type Error = IoAddress;
153
154    #[inline]
155    fn try_from(addr: IoAddress) -> Result<Self, Self::Error> {
156        if addr.0 <= std::u16::MAX as u64 {
157            Ok(PioAddress(addr.raw_value() as PioAddressType))
158        } else {
159            Err(addr)
160        }
161    }
162}
163
164impl From<PioAddress> for IoAddress {
165    #[inline]
166    fn from(addr: PioAddress) -> Self {
167        IoAddress(addr.raw_value() as u64)
168    }
169}
170
171/// Trait for device to handle trapped MMIO/PIO access requests with interior mutability
172/// for high performance.
173///
174/// Any device which needs to trap MMIO/PIO access requests should implement the [DeviceIo] or
175/// [DeviceIoMut] trait and register itself to the [IoManager](crate::device_manager::IoManager)
176/// with those trapped IO address ranges. When the guest access those trapped address ranges,
177/// the access request will be routed to the registered callbacks.
178///
179/// The [DeviceIo] trait adopts the interior mutability pattern so we can get a real concurrent
180/// multiple threads handling. For device backend drivers not focusing on high performance,
181/// the Mutex<T: DeviceIoMut> adapter may be used to simplify the implementation.
182#[allow(unused_variables)]
183pub trait DeviceIo: Send + Sync {
184    /// Read from the MMIO address `base + offset` into `data`.
185    fn read(&self, base: IoAddress, offset: IoAddress, data: &mut [u8]) {}
186
187    /// Write from `data` to the MMIO address `base + offset`.
188    fn write(&self, base: IoAddress, offset: IoAddress, data: &[u8]) {}
189
190    /// Read from port `base + offset` into `data`.
191    fn pio_read(&self, base: PioAddress, offset: PioAddress, data: &mut [u8]) {}
192
193    /// Write from `data` to the port `base + offset`.
194    fn pio_write(&self, base: PioAddress, offset: PioAddress, data: &[u8]) {}
195
196    /// Get resources assigned to the device.
197    fn get_assigned_resources(&self) -> DeviceResources {
198        DeviceResources::new()
199    }
200
201    /// Get the trapped IO address ranges for the device.
202    ///
203    /// Only MMIO/PIO address ranges in the resource list will be handled, other resources will be
204    /// ignored. So the device does not need to filter out non-MMIO/PIO resources.
205    fn get_trapped_io_resources(&self) -> DeviceResources {
206        self.get_assigned_resources()
207    }
208
209    /// Used to downcast to the specific type.
210    fn as_any(&self) -> &dyn Any;
211}
212
213/// Trait for device to handle trapped MMIO/PIO access requests.
214///
215/// Many device backend drivers will mutate itself when handling IO requests. The [DeviceIo] trait
216/// assumes interior mutability, but it's a little complex to support interior mutability.
217/// So the Mutex<T: DeviceIoMut> adapter may be used to ease device backend driver implementations.
218///
219/// The Mutex<T: DeviceIoMut> adapter is an zero overhead abstraction without performance penalty.
220#[allow(unused_variables)]
221pub trait DeviceIoMut {
222    /// Read from the MMIO address `base + offset` into `data`.
223    fn read(&mut self, base: IoAddress, offset: IoAddress, data: &mut [u8]) {}
224
225    /// Write from `data` to the MMIO address `base + offset`.
226    fn write(&mut self, base: IoAddress, offset: IoAddress, data: &[u8]) {}
227
228    /// Read from port `base + offset` into `data`.
229    fn pio_read(&mut self, base: PioAddress, offset: PioAddress, data: &mut [u8]) {}
230
231    /// Write from `data` to the port `base + offset`.
232    fn pio_write(&mut self, base: PioAddress, offset: PioAddress, data: &[u8]) {}
233
234    /// Get resources assigned to the device.
235    fn get_assigned_resources(&self) -> DeviceResources {
236        DeviceResources::new()
237    }
238
239    /// Get the trapped IO address ranges for the device.
240    ///
241    /// Only MMIO/PIO address ranges in the resource list will be handled, other resources will be
242    /// ignored. So the device does not need to filter out non-MMIO/PIO resources.
243    fn get_trapped_io_resources(&self) -> DeviceResources {
244        self.get_assigned_resources()
245    }
246}
247
248impl<T: DeviceIoMut + Send + 'static> DeviceIo for Mutex<T> {
249    fn read(&self, base: IoAddress, offset: IoAddress, data: &mut [u8]) {
250        // Safe to unwrap() because we don't expect poisoned lock here.
251        self.lock().unwrap().read(base, offset, data)
252    }
253
254    fn write(&self, base: IoAddress, offset: IoAddress, data: &[u8]) {
255        // Safe to unwrap() because we don't expect poisoned lock here.
256        self.lock().unwrap().write(base, offset, data)
257    }
258
259    fn pio_read(&self, base: PioAddress, offset: PioAddress, data: &mut [u8]) {
260        // Safe to unwrap() because we don't expect poisoned lock here.
261        self.lock().unwrap().pio_read(base, offset, data)
262    }
263
264    fn pio_write(&self, base: PioAddress, offset: PioAddress, data: &[u8]) {
265        // Safe to unwrap() because we don't expect poisoned lock here.
266        self.lock().unwrap().pio_write(base, offset, data)
267    }
268
269    fn get_assigned_resources(&self) -> DeviceResources {
270        // Safe to unwrap() because we don't expect poisoned lock here.
271        self.lock().unwrap().get_assigned_resources()
272    }
273
274    fn get_trapped_io_resources(&self) -> DeviceResources {
275        // Safe to unwrap() because we don't expect poisoned lock here.
276        self.lock().unwrap().get_trapped_io_resources()
277    }
278
279    fn as_any(&self) -> &dyn Any {
280        self
281    }
282}
283
284#[cfg(test)]
285mod tests {
286    use std::convert::TryFrom;
287    use std::sync::Arc;
288
289    use super::*;
290
291    #[derive(Default)]
292    struct MockDevice {
293        data: Mutex<u8>,
294    }
295
296    impl DeviceIo for MockDevice {
297        fn read(&self, _base: IoAddress, _offset: IoAddress, data: &mut [u8]) {
298            data[0] = *self.data.lock().unwrap();
299        }
300
301        fn write(&self, _base: IoAddress, _offset: IoAddress, data: &[u8]) {
302            *self.data.lock().unwrap() = data[0];
303        }
304
305        fn pio_read(&self, _base: PioAddress, _offset: PioAddress, data: &mut [u8]) {
306            data[0] = *self.data.lock().unwrap();
307        }
308
309        fn pio_write(&self, _base: PioAddress, _offset: PioAddress, data: &[u8]) {
310            *self.data.lock().unwrap() = data[0];
311        }
312        fn as_any(&self) -> &dyn Any {
313            self
314        }
315    }
316
317    #[derive(Default)]
318    struct MockDeviceMut {
319        data: u8,
320    }
321
322    impl DeviceIoMut for MockDeviceMut {
323        fn read(&mut self, _base: IoAddress, _offset: IoAddress, data: &mut [u8]) {
324            data[0] = self.data;
325        }
326
327        fn write(&mut self, _base: IoAddress, _offset: IoAddress, data: &[u8]) {
328            self.data = data[0];
329        }
330
331        fn pio_read(&mut self, _base: PioAddress, _offset: PioAddress, data: &mut [u8]) {
332            data[0] = self.data;
333        }
334
335        fn pio_write(&mut self, _base: PioAddress, _offset: PioAddress, data: &[u8]) {
336            self.data = data[0];
337        }
338    }
339
340    fn register_device(device: Arc<dyn DeviceIo>) {
341        device.write(IoAddress(0), IoAddress(0), &[0x10u8]);
342        let mut buf = [0x0u8];
343        device.read(IoAddress(0), IoAddress(0), &mut buf);
344        assert_eq!(buf[0], 0x10);
345
346        {
347            device.pio_write(PioAddress(0), PioAddress(0), &[0x10u8]);
348            let mut buf = [0x0u8];
349            device.pio_read(PioAddress(0), PioAddress(0), &mut buf);
350            assert_eq!(buf[0], 0x10);
351        }
352
353        // test trait's default implementation
354        let resource = DeviceResources::new();
355        assert_eq!(resource, device.get_assigned_resources());
356        assert_eq!(resource, device.get_trapped_io_resources());
357    }
358
359    #[test]
360    fn test_device_io_adapter() {
361        let device = Arc::new(MockDevice::default());
362
363        register_device(device.clone());
364        assert_eq!(*device.data.lock().unwrap(), 0x010);
365    }
366
367    #[test]
368    fn test_device_io_mut_adapter() {
369        let device_mut = Arc::new(Mutex::new(MockDeviceMut::default()));
370
371        register_device(device_mut.clone());
372        assert_eq!(device_mut.lock().unwrap().data, 0x010);
373    }
374
375    #[test]
376    fn test_io_data_struct() {
377        let io_size = IoSize::from(0x1111u64);
378        assert_eq!(io_size.raw_value(), 0x1111u64);
379        assert_eq!(u64::from(io_size), 0x1111u64);
380        assert_eq!(io_size, io_size.clone());
381        let io_size1 = IoSize::from(0x1112u64);
382        assert!(io_size < io_size1);
383
384        let io_addr = IoAddress::from(0x1234u64);
385        assert_eq!(io_addr.raw_value(), 0x1234u64);
386        assert_eq!(u64::from(io_addr), 0x1234u64);
387        assert_eq!(io_addr, io_addr.clone());
388        let io_addr1 = IoAddress::from(0x1235u64);
389        assert!(io_addr < io_addr1);
390    }
391
392    #[test]
393    fn test_pio_data_struct() {
394        let pio_size = PioSize::from(0x1111u16);
395        assert_eq!(pio_size.raw_value(), 0x1111u16);
396        assert_eq!(u16::from(pio_size), 0x1111u16);
397        assert_eq!(pio_size, pio_size.clone());
398        let pio_size1 = PioSize::from(0x1112u16);
399        assert!(pio_size < pio_size1);
400
401        let pio_size = PioSize::try_from(IoSize(0x1111u64)).unwrap();
402        assert_eq!(pio_size.raw_value(), 0x1111u16);
403
404        assert!(PioSize::try_from(IoSize(std::u16::MAX as u64 + 1)).is_err());
405
406        let io_size = IoSize::from(PioSize::from(0x1111u16));
407        assert_eq!(io_size.raw_value(), 0x1111u64);
408
409        let pio_addr = PioAddress::from(0x1234u16);
410        assert_eq!(pio_addr.raw_value(), 0x1234u16);
411        assert_eq!(u16::from(pio_addr), 0x1234u16);
412        assert_eq!(pio_addr, pio_addr.clone());
413        let pio_addr1 = PioAddress::from(0x1235u16);
414        assert!(pio_addr < pio_addr1);
415
416        assert!(PioAddress::try_from(IoAddress::from(0x12_3456u64)).is_err());
417        assert!(PioAddress::try_from(IoAddress::from(0x1234u64)).is_ok());
418        assert_eq!(IoAddress::from(pio_addr).raw_value(), 0x1234u64);
419    }
420}