Skip to content

Commit 27b6495

Browse files
author
Jing Liu
committed
Add read and write operations handling
IO manager is responsible for handling IO operation when VMExit. It works out the specific device according to the address range and hand over to DeviceIo trait. Signed-off-by: Jing Liu <[email protected]>
1 parent 044cc53 commit 27b6495

File tree

2 files changed

+196
-10
lines changed

2 files changed

+196
-10
lines changed

src/device_manager.rs

Lines changed: 140 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,29 +12,76 @@
1212
//! devices IO windows, and finally set resources to virtual device.
1313
1414
use crate::resources::Resource;
15-
use crate::DeviceIo;
15+
use crate::{DeviceIo, IoAddress, IoSize};
1616

17+
use std::cmp::{Ord, Ordering, PartialEq, PartialOrd};
1718
use std::collections::btree_map::BTreeMap;
1819
use std::result;
1920
use std::sync::Arc;
2021

22+
use vm_memory::GuestAddress;
23+
2124
/// Error type for `IoManager` usage.
2225
#[derive(Debug)]
2326
pub enum Error {
2427
/// The inserting device overlaps with a current device.
2528
DeviceOverlap,
29+
/// The device doesn't exist.
30+
DeviceNotExist,
2631
}
2732

2833
/// Simplify the `Result` type.
2934
pub type Result<T> = result::Result<T, Error>;
3035

36+
// Structure describing an IO range.
37+
#[derive(Debug, Copy, Clone)]
38+
struct IoRange {
39+
base: IoAddress,
40+
size: IoSize,
41+
}
42+
43+
impl IoRange {
44+
fn new_pio_range(base: u16, size: u16) -> Self {
45+
IoRange {
46+
base: IoAddress::Pio(base),
47+
size: IoSize::Pio(size),
48+
}
49+
}
50+
fn new_mmio_range(base: u64, size: u64) -> Self {
51+
IoRange {
52+
base: IoAddress::Mmio(GuestAddress(base)),
53+
size: IoSize::Mmio(size),
54+
}
55+
}
56+
}
57+
58+
impl Eq for IoRange {}
59+
60+
impl PartialEq for IoRange {
61+
fn eq(&self, other: &IoRange) -> bool {
62+
self.base == other.base
63+
}
64+
}
65+
66+
impl Ord for IoRange {
67+
fn cmp(&self, other: &IoRange) -> Ordering {
68+
self.base.cmp(&other.base)
69+
}
70+
}
71+
72+
impl PartialOrd for IoRange {
73+
fn partial_cmp(&self, other: &IoRange) -> Option<Ordering> {
74+
self.base.partial_cmp(&other.base)
75+
}
76+
}
77+
3178
/// System IO manager serving for all devices management and VM exit handling.
3279
#[derive(Default)]
3380
pub struct IoManager {
3481
/// Range mapping for VM exit pio operations.
35-
pio_bus: BTreeMap<(u16, u16), Arc<dyn DeviceIo>>,
82+
pio_bus: BTreeMap<IoRange, Arc<dyn DeviceIo>>,
3683
/// Range mapping for VM exit mmio operations.
37-
mmio_bus: BTreeMap<(u64, u64), Arc<dyn DeviceIo>>,
84+
mmio_bus: BTreeMap<IoRange, Arc<dyn DeviceIo>>,
3885
}
3986

4087
impl IoManager {
@@ -59,15 +106,23 @@ impl IoManager {
59106
for (idx, res) in resources.iter().enumerate() {
60107
match *res {
61108
Resource::PioAddressRange { base, size } => {
62-
if self.pio_bus.insert((base, size), device.clone()).is_some() {
109+
if self
110+
.pio_bus
111+
.insert(IoRange::new_pio_range(base, size), device.clone())
112+
.is_some()
113+
{
63114
// Unregister registered resources.
64115
self.unregister_device_io(&resources[0..idx])?;
65116

66117
return Err(Error::DeviceOverlap);
67118
}
68119
}
69120
Resource::MmioAddressRange { base, size } => {
70-
if self.mmio_bus.insert((base, size), device.clone()).is_some() {
121+
if self
122+
.mmio_bus
123+
.insert(IoRange::new_mmio_range(base, size), device.clone())
124+
.is_some()
125+
{
71126
// Unregister registered resources.
72127
self.unregister_device_io(&resources[0..idx])?;
73128

@@ -92,14 +147,92 @@ impl IoManager {
92147
for res in resources.iter() {
93148
match *res {
94149
Resource::PioAddressRange { base, size } => {
95-
self.pio_bus.remove(&(base, size));
150+
self.pio_bus.remove(&IoRange::new_pio_range(base, size));
96151
}
97152
Resource::MmioAddressRange { base, size } => {
98-
self.mmio_bus.remove(&(base, size));
153+
self.mmio_bus.remove(&IoRange::new_mmio_range(base, size));
99154
}
100155
_ => continue,
101156
}
102157
}
103158
Ok(())
104159
}
160+
161+
// BTreeMap::range() yields elements from min(inclusive) to max(exclusive).
162+
// Let's use a fake range for getting inclusive result.
163+
fn get_entry(&self, addr: IoAddress) -> Option<(&IoRange, &Arc<dyn DeviceIo>)> {
164+
match addr {
165+
IoAddress::Pio(a) => self
166+
.pio_bus
167+
.range(..&IoRange::new_pio_range(a + 1, 0))
168+
.nth_back(0),
169+
IoAddress::Mmio(GuestAddress(a)) => self
170+
.mmio_bus
171+
.range(..&IoRange::new_mmio_range(a + 1, 0))
172+
.nth_back(0),
173+
}
174+
}
175+
176+
// Return the Device mapped the address.
177+
fn get_device(&self, addr: IoAddress) -> Option<Arc<dyn DeviceIo>> {
178+
if let Some((range, dev)) = self.get_entry(addr) {
179+
if (addr.raw_value() - range.base.raw_value()) < range.size.raw_value() {
180+
return Some(dev.clone());
181+
}
182+
return None;
183+
}
184+
None
185+
}
186+
187+
/// A helper function handling PIO read command during VM exit.
188+
/// The virtual device itself provides mutable ability and thead-safe protection.
189+
///
190+
/// Return error if failed to get the device.
191+
pub fn pio_read(&self, addr: u16, data: &mut [u8]) -> Result<()> {
192+
if let Some(device) = self.get_device(IoAddress::Pio(addr)) {
193+
device.read(IoAddress::Pio(addr), data);
194+
Ok(())
195+
} else {
196+
Err(Error::DeviceNotExist)
197+
}
198+
}
199+
200+
/// A helper function handling PIO write command during VM exit.
201+
/// The virtual device itself provides mutable ability and thead-safe protection.
202+
///
203+
/// Return error if failed to get the device.
204+
pub fn pio_write(&self, addr: u16, data: &[u8]) -> Result<()> {
205+
if let Some(device) = self.get_device(IoAddress::Pio(addr)) {
206+
device.write(IoAddress::Pio(addr), data);
207+
Ok(())
208+
} else {
209+
Err(Error::DeviceNotExist)
210+
}
211+
}
212+
213+
/// A helper function handling MMIO read command during VM exit.
214+
/// The virtual device itself provides mutable ability and thead-safe protection.
215+
///
216+
/// Return error if failed to get the device.
217+
pub fn mmio_read(&self, addr: u64, data: &mut [u8]) -> Result<()> {
218+
if let Some(device) = self.get_device(IoAddress::Mmio(GuestAddress(addr))) {
219+
device.read(IoAddress::Mmio(GuestAddress(addr)), data);
220+
Ok(())
221+
} else {
222+
Err(Error::DeviceNotExist)
223+
}
224+
}
225+
226+
/// A helper function handling MMIO write command during VM exit.
227+
/// The virtual device itself provides mutable ability and thead-safe protection.
228+
///
229+
/// Return error if failed to get the device.
230+
pub fn mmio_write(&self, addr: u64, data: &[u8]) -> Result<()> {
231+
if let Some(device) = self.get_device(IoAddress::Mmio(GuestAddress(addr))) {
232+
device.write(IoAddress::Mmio(GuestAddress(addr)), data);
233+
Ok(())
234+
} else {
235+
Err(Error::DeviceNotExist)
236+
}
237+
}
105238
}

src/lib.rs

Lines changed: 56 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,32 @@
55
66
extern crate vm_memory;
77

8-
use vm_memory::GuestAddress;
8+
use std::cmp::{Ord, Ordering, PartialOrd};
9+
use vm_memory::{Address, GuestAddress, GuestUsize};
910

1011
pub mod device_manager;
1112
pub mod resources;
1213

14+
/// IO Size.
15+
#[derive(Debug, Copy, Clone)]
16+
pub enum IoSize {
17+
/// Port I/O size.
18+
Pio(u16),
19+
20+
/// Memory mapped I/O size.
21+
Mmio(GuestUsize),
22+
}
23+
24+
impl IoSize {
25+
/// Get the raw value as u64 to make operation simple.
26+
pub fn raw_value(&self) -> u64 {
27+
match *self {
28+
IoSize::Pio(p) => u64::from(p),
29+
IoSize::Mmio(m) => m,
30+
}
31+
}
32+
}
33+
1334
/// IO Addresses.
1435
#[derive(Debug, Copy, Clone)]
1536
pub enum IoAddress {
@@ -20,15 +41,47 @@ pub enum IoAddress {
2041
Mmio(GuestAddress),
2142
}
2243

44+
impl IoAddress {
45+
/// Get the raw value of IO Address to make operation simple.
46+
pub fn raw_value(&self) -> u64 {
47+
match *self {
48+
IoAddress::Pio(p) => u64::from(p),
49+
IoAddress::Mmio(m) => m.raw_value(),
50+
}
51+
}
52+
}
53+
54+
impl Eq for IoAddress {}
55+
56+
impl PartialEq for IoAddress {
57+
fn eq(&self, other: &IoAddress) -> bool {
58+
self.raw_value() == other.raw_value()
59+
}
60+
}
61+
62+
impl Ord for IoAddress {
63+
fn cmp(&self, other: &IoAddress) -> Ordering {
64+
self.raw_value().cmp(&other.raw_value())
65+
}
66+
}
67+
68+
impl PartialOrd for IoAddress {
69+
fn partial_cmp(&self, other: &IoAddress) -> Option<Ordering> {
70+
self.raw_value().partial_cmp(&other.raw_value())
71+
}
72+
}
73+
2374
/// Device IO trait.
2475
/// A device supporting memory based I/O should implement this trait, then
2576
/// register itself against the different IO type ranges it handles.
2677
/// The VMM will then dispatch IO (PIO or MMIO) VM exits by calling into the
2778
/// registered devices read or write method from this trait.
79+
/// The mutable ability is provided by virtual device inside. e.g. Mutex, RwLock.
80+
/// So we can get a real multiple thread handling.
2881
pub trait DeviceIo: Send {
2982
/// Read from the guest physical address `addr` to `data`.
30-
fn read(&mut self, addr: IoAddress, data: &mut [u8]);
83+
fn read(&self, addr: IoAddress, data: &mut [u8]);
3184

3285
/// Write `data` to the guest physical address `addr`.
33-
fn write(&mut self, addr: IoAddress, data: &[u8]);
86+
fn write(&self, addr: IoAddress, data: &[u8]);
3487
}

0 commit comments

Comments
 (0)