Skip to content

Commit 613a144

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 2c6934e commit 613a144

File tree

2 files changed

+193
-10
lines changed

2 files changed

+193
-10
lines changed

src/device_manager.rs

Lines changed: 137 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,29 +12,76 @@
1212
//! devices IO ranges, 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 {
@@ -60,7 +107,11 @@ impl IoManager {
60107
for (idx, res) in resources.iter().enumerate() {
61108
match *res {
62109
Resource::PioAddressRange { base, size } => {
63-
if self.pio_bus.insert((base, size), device.clone()).is_some() {
110+
if self
111+
.pio_bus
112+
.insert(IoRange::new_pio_range(base, size), device.clone())
113+
.is_some()
114+
{
64115
// Unregister registered resources.
65116
self.unregister_device_io(&resources[0..idx])
66117
.expect("failed to unregister devices");
@@ -69,7 +120,11 @@ impl IoManager {
69120
}
70121
}
71122
Resource::MmioAddressRange { base, size } => {
72-
if self.mmio_bus.insert((base, size), device.clone()).is_some() {
123+
if self
124+
.mmio_bus
125+
.insert(IoRange::new_mmio_range(base, size), device.clone())
126+
.is_some()
127+
{
73128
// Unregister registered resources.
74129
self.unregister_device_io(&resources[0..idx])
75130
.expect("failed to unregister devices");
@@ -95,14 +150,89 @@ impl IoManager {
95150
for res in resources.iter() {
96151
match *res {
97152
Resource::PioAddressRange { base, size } => {
98-
self.pio_bus.remove(&(base, size));
153+
self.pio_bus.remove(&IoRange::new_pio_range(base, size));
99154
}
100155
Resource::MmioAddressRange { base, size } => {
101-
self.mmio_bus.remove(&(base, size));
156+
self.mmio_bus.remove(&IoRange::new_mmio_range(base, size));
102157
}
103158
_ => continue,
104159
}
105160
}
106161
Ok(())
107162
}
163+
164+
fn get_entry(&self, addr: IoAddress) -> Option<(&IoRange, &Arc<dyn DeviceIo>)> {
165+
match addr {
166+
IoAddress::Pio(a) => self
167+
.pio_bus
168+
.range(..=&IoRange::new_pio_range(a, 0))
169+
.nth_back(0),
170+
IoAddress::Mmio(GuestAddress(a)) => self
171+
.mmio_bus
172+
.range(..=&IoRange::new_mmio_range(a, 0))
173+
.nth_back(0),
174+
}
175+
}
176+
177+
// Return the Device mapped the address.
178+
fn get_device(&self, addr: IoAddress) -> Option<&Arc<dyn DeviceIo>> {
179+
if let Some((range, dev)) = self.get_entry(addr) {
180+
if (addr.raw_value() - range.base.raw_value()) < range.size.raw_value() {
181+
return Some(dev);
182+
}
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+
}
108238
}

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+
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+
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+
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 DeviceIo trait adopts the interior mutability pattern
80+
/// so we can get a real multiple threads 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)