Skip to content

Commit 5f0c72d

Browse files
committed
Build dedicated structs/interfaces for Pio
The design to multiplex IoAddress/IoSize for MMIO and PIO makes the device driver implmentation a little complex, so build dedicated data structs and interfaces to handle PIO requests. Also make PIO related code x86 specific. Signed-off-by: Liu Jiang <[email protected]>
1 parent 11f77fb commit 5f0c72d

File tree

2 files changed

+329
-114
lines changed

2 files changed

+329
-114
lines changed

src/device_manager.rs

Lines changed: 106 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
//! devices IO ranges, and finally set resources to virtual device.
1313
1414
use crate::resources::Resource;
15+
#[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
16+
use crate::PioAddress;
1517
use crate::{DeviceIo, IoAddress, IoSize};
1618

1719
use std::cmp::{Ord, Ordering, PartialEq, PartialOrd};
@@ -32,29 +34,29 @@ pub enum Error {
3234
pub type Result<T> = result::Result<T, Error>;
3335

3436
/// Structure describing an IO range.
35-
#[derive(Debug, Copy, Clone)]
37+
#[derive(Debug, Copy, Clone, Eq)]
3638
pub struct IoRange {
3739
base: IoAddress,
3840
size: IoSize,
3941
}
4042

4143
impl IoRange {
44+
#[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
4245
fn new_pio_range(base: u16, size: u16) -> Self {
4346
IoRange {
44-
base: IoAddress::Pio(base),
45-
size: IoSize::Pio(size),
47+
base: IoAddress(base as u64),
48+
size: IoSize(size as u64),
4649
}
4750
}
51+
4852
fn new_mmio_range(base: u64, size: u64) -> Self {
4953
IoRange {
50-
base: IoAddress::Mmio(base),
51-
size: IoSize::Mmio(size),
54+
base: IoAddress(base),
55+
size: IoSize(size),
5256
}
5357
}
5458
}
5559

56-
impl Eq for IoRange {}
57-
5860
impl PartialEq for IoRange {
5961
fn eq(&self, other: &IoRange) -> bool {
6062
self.base == other.base
@@ -76,6 +78,7 @@ impl PartialOrd for IoRange {
7678
/// System IO manager serving for all devices management and VM exit handling.
7779
#[derive(Clone, Default)]
7880
pub struct IoManager {
81+
#[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
7982
/// Range mapping for VM exit pio operations.
8083
pio_bus: BTreeMap<IoRange, Arc<dyn DeviceIo>>,
8184
/// Range mapping for VM exit mmio operations.
@@ -87,6 +90,7 @@ impl IoManager {
8790
pub fn new() -> Self {
8891
IoManager::default()
8992
}
93+
9094
/// Register a new device IO with its allocated resources.
9195
/// VMM is responsible for providing the allocated resources to virtual device.
9296
///
@@ -104,6 +108,7 @@ impl IoManager {
104108
// The resources addresses being registered are sucessfully allocated before.
105109
for (idx, res) in resources.iter().enumerate() {
106110
match *res {
111+
#[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
107112
Resource::PioAddressRange { base, size } => {
108113
if self
109114
.pio_bus
@@ -147,6 +152,7 @@ impl IoManager {
147152
pub fn unregister_device_io(&mut self, resources: &[Resource]) -> Result<()> {
148153
for res in resources.iter() {
149154
match *res {
155+
#[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
150156
Resource::PioAddressRange { base, size } => {
151157
self.pio_bus.remove(&IoRange::new_pio_range(base, size));
152158
}
@@ -159,79 +165,69 @@ impl IoManager {
159165
Ok(())
160166
}
161167

162-
fn get_entry(&self, addr: IoAddress) -> Option<(&IoRange, &Arc<dyn DeviceIo>)> {
163-
match addr {
164-
IoAddress::Pio(a) => self
165-
.pio_bus
166-
.range(..=&IoRange::new_pio_range(a, 0))
167-
.nth_back(0),
168-
IoAddress::Mmio(a) => self
169-
.mmio_bus
170-
.range(..=&IoRange::new_mmio_range(a, 0))
171-
.nth_back(0),
172-
}
168+
/// A helper function handling MMIO read command during VM exit.
169+
/// The virtual device itself provides mutable ability and thead-safe protection.
170+
///
171+
/// Return error if failed to get the device.
172+
pub fn mmio_read(&self, addr: u64, data: &mut [u8]) -> Result<()> {
173+
self.get_device(IoAddress(addr))
174+
.map(|(device, base)| device.read(base, IoAddress(addr - base.raw_value()), data))
175+
.ok_or(Error::NoDevice)
176+
}
177+
178+
/// A helper function handling MMIO write command during VM exit.
179+
/// The virtual device itself provides mutable ability and thead-safe protection.
180+
///
181+
/// Return error if failed to get the device.
182+
pub fn mmio_write(&self, addr: u64, data: &[u8]) -> Result<()> {
183+
self.get_device(IoAddress(addr))
184+
.map(|(device, base)| device.write(base, IoAddress(addr - base.raw_value()), data))
185+
.ok_or(Error::NoDevice)
173186
}
174187

175188
// Return the Device mapped `addr` and the base address.
176189
fn get_device(&self, addr: IoAddress) -> Option<(&Arc<dyn DeviceIo>, IoAddress)> {
177-
if let Some((range, dev)) = self.get_entry(addr) {
190+
let range = IoRange::new_mmio_range(addr.raw_value(), 0);
191+
if let Some((range, dev)) = self.mmio_bus.range(..=&range).nth_back(0) {
178192
if (addr.raw_value() - range.base.raw_value()) < range.size.raw_value() {
179193
return Some((dev, range.base));
180194
}
181195
}
182196
None
183197
}
198+
}
184199

200+
#[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
201+
impl IoManager {
185202
/// A helper function handling PIO read command during VM exit.
186203
/// The virtual device itself provides mutable ability and thead-safe protection.
187204
///
188205
/// Return error if failed to get the device.
189206
pub fn pio_read(&self, addr: u16, data: &mut [u8]) -> Result<()> {
190-
if let Some((device, base)) = self.get_device(IoAddress::Pio(addr)) {
191-
device.read(base, IoAddress::Pio(addr - (base.raw_value() as u16)), data);
192-
Ok(())
193-
} else {
194-
Err(Error::NoDevice)
195-
}
207+
self.get_pio_device(PioAddress(addr))
208+
.map(|(device, base)| device.pio_read(base, PioAddress(addr - base.raw_value()), data))
209+
.ok_or(Error::NoDevice)
196210
}
197211

198212
/// A helper function handling PIO write command during VM exit.
199213
/// The virtual device itself provides mutable ability and thead-safe protection.
200214
///
201215
/// Return error if failed to get the device.
202216
pub fn pio_write(&self, addr: u16, data: &[u8]) -> Result<()> {
203-
if let Some((device, base)) = self.get_device(IoAddress::Pio(addr)) {
204-
device.write(base, IoAddress::Pio(addr - (base.raw_value() as u16)), data);
205-
Ok(())
206-
} else {
207-
Err(Error::NoDevice)
208-
}
209-
}
210-
211-
/// A helper function handling MMIO read command during VM exit.
212-
/// The virtual device itself provides mutable ability and thead-safe protection.
213-
///
214-
/// Return error if failed to get the device.
215-
pub fn mmio_read(&self, addr: u64, data: &mut [u8]) -> Result<()> {
216-
if let Some((device, base)) = self.get_device(IoAddress::Mmio(addr)) {
217-
device.read(base, IoAddress::Mmio(addr - base.raw_value()), data);
218-
Ok(())
219-
} else {
220-
Err(Error::NoDevice)
221-
}
217+
self.get_pio_device(PioAddress(addr))
218+
.map(|(device, base)| device.pio_write(base, PioAddress(addr - base.raw_value()), data))
219+
.ok_or(Error::NoDevice)
222220
}
223221

224-
/// A helper function handling MMIO write command during VM exit.
225-
/// The virtual device itself provides mutable ability and thead-safe protection.
226-
///
227-
/// Return error if failed to get the device.
228-
pub fn mmio_write(&self, addr: u64, data: &[u8]) -> Result<()> {
229-
if let Some((device, base)) = self.get_device(IoAddress::Mmio(addr)) {
230-
device.write(base, IoAddress::Mmio(addr - base.raw_value()), data);
231-
Ok(())
232-
} else {
233-
Err(Error::NoDevice)
222+
// Return the Device mapped `addr` and the base address.
223+
fn get_pio_device(&self, addr: PioAddress) -> Option<(&Arc<dyn DeviceIo>, PioAddress)> {
224+
let range = IoRange::new_pio_range(addr.raw_value(), 0);
225+
if let Some((range, dev)) = self.pio_bus.range(..=&range).nth_back(0) {
226+
if (addr.raw_value() as u64 - range.base.raw_value()) < range.size.raw_value() {
227+
return Some((dev, PioAddress(range.base.0 as u16)));
228+
}
234229
}
230+
None
235231
}
236232
}
237233

@@ -240,7 +236,9 @@ mod tests {
240236
use super::*;
241237
use std::sync::Mutex;
242238

239+
#[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
243240
const PIO_ADDRESS_SIZE: u16 = 4;
241+
#[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
244242
const PIO_ADDRESS_BASE: u16 = 0x40;
245243
const MMIO_ADDRESS_SIZE: u64 = 0x8765_4321;
246244
const MMIO_ADDRESS_BASE: u64 = 0x1234_5678;
@@ -274,6 +272,23 @@ mod tests {
274272
let mut config = self.config.lock().expect("failed to acquire lock");
275273
*config = u32::from(data[0]) & 0xff;
276274
}
275+
276+
#[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
277+
fn pio_read(&self, _base: PioAddress, _offset: PioAddress, data: &mut [u8]) {
278+
if data.len() > 4 {
279+
return;
280+
}
281+
for (idx, iter) in data.iter_mut().enumerate() {
282+
let config = self.config.lock().expect("failed to acquire lock");
283+
*iter = (*config >> (idx * 8) & 0xff) as u8;
284+
}
285+
}
286+
287+
#[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
288+
fn pio_write(&self, _base: PioAddress, _offset: PioAddress, data: &[u8]) {
289+
let mut config = self.config.lock().expect("failed to acquire lock");
290+
*config = u32::from(data[0]) & 0xff;
291+
}
277292
}
278293

279294
#[test]
@@ -287,32 +302,42 @@ mod tests {
287302
base: MMIO_ADDRESS_BASE,
288303
size: MMIO_ADDRESS_SIZE,
289304
};
290-
let pio = Resource::PioAddressRange {
291-
base: PIO_ADDRESS_BASE,
292-
size: PIO_ADDRESS_SIZE,
293-
};
294305
let irq = Resource::LegacyIrq(LEGACY_IRQ);
295306

296307
resource.push(mmio);
297-
resource.push(pio);
298308
resource.push(irq);
309+
310+
#[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
311+
{
312+
let pio = Resource::PioAddressRange {
313+
base: PIO_ADDRESS_BASE,
314+
size: PIO_ADDRESS_SIZE,
315+
};
316+
resource.push(pio);
317+
}
318+
299319
assert!(io_mgr.register_device_io(dum.clone(), &resource).is_ok());
300320

301321
let io_mgr2 = io_mgr.clone();
302-
assert_eq!(io_mgr2.pio_bus.len(), 1);
303322
assert_eq!(io_mgr2.mmio_bus.len(), 1);
304323

305-
let (dev, addr) = io_mgr2
306-
.get_device(IoAddress::Mmio(MMIO_ADDRESS_BASE + 1))
307-
.unwrap();
308-
assert_eq!(Arc::strong_count(dev), 5);
309-
assert_eq!(addr, IoAddress::Mmio(MMIO_ADDRESS_BASE));
324+
#[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
325+
{
326+
assert_eq!(io_mgr2.pio_bus.len(), 1);
327+
328+
let (dev, addr) = io_mgr2
329+
.get_device(IoAddress(MMIO_ADDRESS_BASE + 1))
330+
.unwrap();
331+
assert_eq!(Arc::strong_count(dev), 5);
310332

311-
drop(io_mgr);
312-
assert_eq!(Arc::strong_count(dev), 3);
333+
assert_eq!(addr, IoAddress(MMIO_ADDRESS_BASE));
313334

314-
drop(io_mgr2);
315-
assert_eq!(Arc::strong_count(&dum), 1);
335+
drop(io_mgr);
336+
assert_eq!(Arc::strong_count(dev), 3);
337+
338+
drop(io_mgr2);
339+
assert_eq!(Arc::strong_count(&dum), 1);
340+
}
316341
}
317342

318343
#[test]
@@ -365,6 +390,7 @@ mod tests {
365390
.is_err());
366391
}
367392

393+
#[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
368394
#[test]
369395
fn test_pio_read_write() {
370396
let mut io_mgr: IoManager = Default::default();
@@ -394,4 +420,15 @@ mod tests {
394420
.pio_write(PIO_ADDRESS_BASE + PIO_ADDRESS_SIZE, &data)
395421
.is_err());
396422
}
423+
424+
#[test]
425+
fn test_device_manager_data_structs() {
426+
let range1 = IoRange::new_mmio_range(0x1000, 0x1000);
427+
let range2 = IoRange::new_mmio_range(0x1000, 0x2000);
428+
let range3 = IoRange::new_mmio_range(0x2000, 0x1000);
429+
430+
assert_eq!(range1, range1.clone());
431+
assert_eq!(range1, range2);
432+
assert!(range1 < range3);
433+
}
397434
}

0 commit comments

Comments
 (0)