vm_device/lib.rs
1// Copyright © 2019 Intel Corporation. All Rights Reserved.
2// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause
3
4#![deny(missing_docs)]
5
6//! This crate provides:
7//! * device traits defining read and write operations on specialized buses
8//! * device manager (bus-specific traits and a concrete implementation) for
9//! operating devices and dispatching I/O
10//! * abstractions for defining resources and their constraints (e.g. a specific bus
11//! address range, IRQ number, etc)
12//!
13//! [`MutDevicePio`] and [`MutDeviceMmio`] traits help with composite inner mutability
14//! (i.e. if we have a `Mutex` that holds a `T` which implements [`MutDevicePio`],
15//! then the `Mutex` can implement [`DevicePio`] based on its inner
16//! mutability properties).
17//!
18//! # Example
19//!
20//! Implement a simple log PIO device, register it with
21//! [`IoManager`](device_manager/struct.IoManager.html)
22//! and dispatch a write operation to the device.
23//!```
24//! use std::sync::{Arc, Mutex};
25//! use vm_device::bus::{PioAddress, PioAddressOffset, PioRange};
26//! use vm_device::device_manager::{IoManager, PioManager};
27//! use vm_device::MutDevicePio;
28//!
29//! struct LogDevice {}
30//!
31//! impl MutDevicePio for LogDevice {
32//! fn pio_read(&mut self, base: PioAddress, offset: PioAddressOffset, _data: &mut [u8]) {
33//! println!("mut pio_read: base {:?}, offset {}", base, offset);
34//! }
35//! fn pio_write(&mut self, base: PioAddress, offset: PioAddressOffset, data: &[u8]) {
36//! println!(
37//! "mut pio_write: base {:?}, offset {}, data {:?}",
38//! base, offset, data
39//! );
40//! }
41//! }
42//!
43//! // IoManager implements PioManager trait.
44//! let mut manager = IoManager::new();
45//! let device = LogDevice {};
46//! let bus_range = PioRange::new(PioAddress(0), 10).unwrap();
47//! manager
48//! .register_pio(bus_range, Arc::new(Mutex::new(device)))
49//! .unwrap();
50//! manager.pio_write(PioAddress(0), &vec![b'o', b'k']).unwrap();
51//! ```
52
53pub mod bus;
54pub mod device_manager;
55pub mod resources;
56
57use std::ops::Deref;
58use std::sync::{Arc, Mutex};
59
60use bus::{MmioAddress, MmioAddressOffset, PioAddress, PioAddressOffset};
61
62/// Allows a device to be attached to a
63/// [PIO](https://en.wikipedia.org/wiki/Programmed_input%E2%80%93output) bus.
64///
65/// # Example
66/// ```
67/// # use std::sync::Mutex;
68/// # use vm_device::{DevicePio, bus::{PioAddress, PioAddressOffset}};
69/// struct DummyDevice {
70/// config: Mutex<u32>,
71/// }
72///
73/// impl DevicePio for DummyDevice {
74/// fn pio_read(&self, _base: PioAddress, _offset: PioAddressOffset, data: &mut [u8]) {
75/// if data.len() > 4 {
76/// return;
77/// }
78/// for (idx, iter) in data.iter_mut().enumerate() {
79/// let config = self.config.lock().expect("failed to acquire lock");
80/// *iter = (*config >> (idx * 8) & 0xff) as u8;
81/// }
82/// }
83///
84/// fn pio_write(&self, _base: PioAddress, _offset: PioAddressOffset, data: &[u8]) {
85/// let mut config = self.config.lock().expect("failed to acquire lock");
86/// *config = u32::from(data[0]) & 0xff;
87/// }
88/// }
89/// ```
90pub trait DevicePio {
91 /// Handle a read operation on the device.
92 ///
93 /// # Arguments
94 ///
95 /// * `base`: base address on a PIO bus
96 /// * `offset`: base address' offset
97 /// * `data`: a buffer provided by the caller to store the read data
98 fn pio_read(&self, base: PioAddress, offset: PioAddressOffset, data: &mut [u8]);
99
100 /// Handle a write operation to the device.
101 ///
102 /// # Arguments
103 ///
104 /// * `base`: base address on a PIO bus
105 /// * `offset`: base address' offset
106 /// * `data`: a buffer provided by the caller holding the data to write
107 fn pio_write(&self, base: PioAddress, offset: PioAddressOffset, data: &[u8]);
108}
109
110/// Allows a device to be attached to a
111/// [MMIO](https://en.wikipedia.org/wiki/Memory-mapped_I/O) bus.
112///
113/// # Example
114/// ```
115/// # use std::sync::Mutex;
116/// # use vm_device::{DeviceMmio, bus::{MmioAddress, MmioAddressOffset}};
117/// struct DummyDevice {
118/// config: Mutex<u32>,
119/// }
120///
121/// impl DeviceMmio for DummyDevice {
122/// fn mmio_read(&self, _base: MmioAddress, _offset: MmioAddressOffset, data: &mut [u8]) {
123/// if data.len() > 4 {
124/// return;
125/// }
126/// for (idx, iter) in data.iter_mut().enumerate() {
127/// let config = self.config.lock().expect("failed to acquire lock");
128/// *iter = (*config >> (idx * 8) & 0xff) as u8;
129/// }
130/// }
131///
132/// fn mmio_write(&self, _base: MmioAddress, _offset: MmioAddressOffset, data: &[u8]) {
133/// let mut config = self.config.lock().expect("failed to acquire lock");
134/// *config = u32::from(data[0]) & 0xff;
135/// }
136/// }
137/// ```
138pub trait DeviceMmio {
139 /// Handle a read operation on the device.
140 ///
141 /// # Arguments
142 ///
143 /// * `base`: base address on a MMIO bus
144 /// * `offset`: base address' offset
145 /// * `data`: a buffer provided by the caller to store the read data
146 fn mmio_read(&self, base: MmioAddress, offset: MmioAddressOffset, data: &mut [u8]);
147
148 /// Handle a write operation to the device.
149 ///
150 /// # Arguments
151 ///
152 /// * `base`: base address on a MMIO bus
153 /// * `offset`: base address' offset
154 /// * `data`: a buffer provided by the caller holding the data to write
155 fn mmio_write(&self, base: MmioAddress, offset: MmioAddressOffset, data: &[u8]);
156}
157
158/// Same as [DevicePio] but the methods are invoked with a mutable self borrow.
159///
160/// # Example
161/// ```
162/// # use vm_device::{MutDevicePio, bus::{PioAddress, PioAddressOffset}};
163/// struct DummyDevice {
164/// config: u32,
165/// }
166///
167/// impl MutDevicePio for DummyDevice {
168/// fn pio_read(&mut self, _base: PioAddress, _offset: PioAddressOffset, data: &mut [u8]) {
169/// if data.len() > 4 {
170/// return;
171/// }
172/// for (idx, iter) in data.iter_mut().enumerate() {
173/// *iter = (self.config >> (idx * 8) & 0xff) as u8;
174/// }
175/// }
176///
177/// fn pio_write(&mut self, _base: PioAddress, _offset: PioAddressOffset, data: &[u8]) {
178/// self.config = u32::from(data[0]) & 0xff;
179/// }
180/// }
181/// ```
182pub trait MutDevicePio {
183 /// Handle a read operation on the device.
184 ///
185 /// # Arguments
186 ///
187 /// * `base`: base address on a PIO bus
188 /// * `offset`: base address' offset
189 /// * `data`: a buffer provided by the caller to store the read data
190 fn pio_read(&mut self, base: PioAddress, offset: PioAddressOffset, data: &mut [u8]);
191
192 /// Handle a write operation to the device.
193 ///
194 /// # Arguments
195 ///
196 /// * `base`: base address on a PIO bus
197 /// * `offset`: base address' offset
198 /// * `data`: a buffer provided by the caller holding the data to write
199 fn pio_write(&mut self, base: PioAddress, offset: PioAddressOffset, data: &[u8]);
200}
201
202/// Same as [DeviceMmio] but the methods are invoked with a mutable self borrow.
203/// # Example
204/// ```
205/// # use vm_device::{MutDeviceMmio, bus::{MmioAddress, MmioAddressOffset}};
206/// struct DummyDevice {
207/// config: u32,
208/// }
209///
210/// impl MutDeviceMmio for DummyDevice {
211/// fn mmio_read(&mut self, _base: MmioAddress, _offset: MmioAddressOffset, data: &mut [u8]) {
212/// if data.len() > 4 {
213/// return;
214/// }
215/// for (idx, iter) in data.iter_mut().enumerate() {
216/// *iter = (self.config >> (idx * 8) & 0xff) as u8;
217/// }
218/// }
219///
220/// fn mmio_write(&mut self, _base: MmioAddress, _offset: MmioAddressOffset, data: &[u8]) {
221/// self.config = u32::from(data[0]) & 0xff;
222/// }
223/// }
224/// ```
225pub trait MutDeviceMmio {
226 /// Handle a read operation on the device.
227 ///
228 /// # Arguments
229 ///
230 /// * `base`: base address on a MMIO bus
231 /// * `offset`: base address' offset
232 /// * `data`: a buffer provided by the caller to store the read data
233 fn mmio_read(&mut self, base: MmioAddress, offset: MmioAddressOffset, data: &mut [u8]);
234
235 /// Handle a write operation to the device.
236 ///
237 /// # Arguments
238 ///
239 /// * `base`: base address on a MMIO bus
240 /// * `offset`: base address' offset
241 /// * `data`: a buffer provided by the caller holding the data to write
242 fn mmio_write(&mut self, base: MmioAddress, offset: MmioAddressOffset, data: &[u8]);
243}
244
245// Blanket implementations for Arc<T>.
246
247impl<T: DeviceMmio + ?Sized> DeviceMmio for Arc<T> {
248 fn mmio_read(&self, base: MmioAddress, offset: MmioAddressOffset, data: &mut [u8]) {
249 self.deref().mmio_read(base, offset, data);
250 }
251
252 fn mmio_write(&self, base: MmioAddress, offset: MmioAddressOffset, data: &[u8]) {
253 self.deref().mmio_write(base, offset, data);
254 }
255}
256
257impl<T: DevicePio + ?Sized> DevicePio for Arc<T> {
258 fn pio_read(&self, base: PioAddress, offset: PioAddressOffset, data: &mut [u8]) {
259 self.deref().pio_read(base, offset, data);
260 }
261
262 fn pio_write(&self, base: PioAddress, offset: PioAddressOffset, data: &[u8]) {
263 self.deref().pio_write(base, offset, data);
264 }
265}
266
267// Blanket implementations for Mutex<T>.
268
269impl<T: MutDeviceMmio + ?Sized> DeviceMmio for Mutex<T> {
270 fn mmio_read(&self, base: MmioAddress, offset: MmioAddressOffset, data: &mut [u8]) {
271 self.lock().unwrap().mmio_read(base, offset, data)
272 }
273
274 fn mmio_write(&self, base: MmioAddress, offset: MmioAddressOffset, data: &[u8]) {
275 self.lock().unwrap().mmio_write(base, offset, data)
276 }
277}
278
279impl<T: MutDevicePio + ?Sized> DevicePio for Mutex<T> {
280 fn pio_read(&self, base: PioAddress, offset: PioAddressOffset, data: &mut [u8]) {
281 self.lock().unwrap().pio_read(base, offset, data)
282 }
283
284 fn pio_write(&self, base: PioAddress, offset: PioAddressOffset, data: &[u8]) {
285 self.lock().unwrap().pio_write(base, offset, data)
286 }
287}