Skip to content

Commit 985e7c3

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 f5b0f23 commit 985e7c3

File tree

2 files changed

+201
-10
lines changed

2 files changed

+201
-10
lines changed

src/device_manager.rs

Lines changed: 145 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 {
@@ -56,15 +103,23 @@ impl IoManager {
56103
for (idx, res) in resources.iter().enumerate() {
57104
match *res {
58105
Resource::PioAddressRange { base, size } => {
59-
if self.pio_bus.insert((base, size), device.clone()).is_some() {
106+
if self
107+
.pio_bus
108+
.insert(IoRange::new_pio_range(base, size), device.clone())
109+
.is_some()
110+
{
60111
// Unregister registered resources.
61112
self.unregister_device_io(&resources[0..idx])?;
62113

63114
return Err(Error::DeviceOverlap);
64115
}
65116
}
66117
Resource::MmioAddressRange { base, size } => {
67-
if self.mmio_bus.insert((base, size), device.clone()).is_some() {
118+
if self
119+
.mmio_bus
120+
.insert(IoRange::new_mmio_range(base, size), device.clone())
121+
.is_some()
122+
{
68123
// Unregister registered resources.
69124
self.unregister_device_io(&resources[0..idx])?;
70125

@@ -89,14 +144,97 @@ impl IoManager {
89144
for res in resources.iter() {
90145
match *res {
91146
Resource::PioAddressRange { base, size } => {
92-
self.pio_bus.remove(&(base, size));
147+
self.pio_bus.remove(&IoRange::new_pio_range(base, size));
93148
}
94149
Resource::MmioAddressRange { base, size } => {
95-
self.mmio_bus.remove(&(base, size));
150+
self.mmio_bus.remove(&IoRange::new_mmio_range(base, size));
96151
}
97152
_ => continue,
98153
}
99154
}
100155
Ok(())
101156
}
157+
158+
fn first_before(
159+
&self,
160+
addr: IoAddress,
161+
map: BTreeMap<IoRange, Arc<dyn DeviceIo>>,
162+
) -> Option<(IoRange, Arc<dyn DeviceIo>)> {
163+
for (range, dev) in map.iter().rev() {
164+
if range.base <= addr {
165+
return Some((*range, dev.clone()));
166+
}
167+
}
168+
None
169+
}
170+
171+
// Return the Device mapped the address.
172+
fn get_device(
173+
&self,
174+
addr: IoAddress,
175+
map: BTreeMap<IoRange, Arc<dyn DeviceIo>>,
176+
) -> Option<Arc<dyn DeviceIo>> {
177+
if let Some((range, dev)) = self.first_before(addr, map) {
178+
if (addr.raw_value() - range.base.raw_value()) < range.size.raw_value() {
179+
return Some(dev);
180+
}
181+
}
182+
None
183+
}
184+
185+
/// A helper function handling PIO read command during VM exit.
186+
/// The virtual device itself provides mutable ability and thead-safe protection.
187+
///
188+
/// Return error if failed to get the device.
189+
pub fn pio_read(&self, addr: u16, data: &mut [u8]) -> Result<()> {
190+
if let Some(device) = self.get_device(IoAddress::Pio(addr), self.pio_bus.clone()) {
191+
device.read(IoAddress::Pio(addr), data);
192+
Ok(())
193+
} else {
194+
Err(Error::DeviceNotExist)
195+
}
196+
}
197+
198+
/// A helper function handling PIO write command during VM exit.
199+
/// The virtual device itself provides mutable ability and thead-safe protection.
200+
///
201+
/// Return error if failed to get the device.
202+
pub fn pio_write(&self, addr: u16, data: &[u8]) -> Result<()> {
203+
if let Some(device) = self.get_device(IoAddress::Pio(addr), self.pio_bus.clone()) {
204+
device.write(IoAddress::Pio(addr), data);
205+
Ok(())
206+
} else {
207+
Err(Error::DeviceNotExist)
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) =
217+
self.get_device(IoAddress::Mmio(GuestAddress(addr)), self.mmio_bus.clone())
218+
{
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) =
232+
self.get_device(IoAddress::Mmio(GuestAddress(addr)), self.mmio_bus.clone())
233+
{
234+
device.write(IoAddress::Mmio(GuestAddress(addr)), data);
235+
Ok(())
236+
} else {
237+
Err(Error::DeviceNotExist)
238+
}
239+
}
102240
}

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)