Skip to content

Commit 8e9874b

Browse files
author
Jing Liu
committed
Add read and write operations handling
DeviceManager 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 <jing2.liu@linux.intel.com>
1 parent 06f250b commit 8e9874b

File tree

2 files changed

+199
-10
lines changed

2 files changed

+199
-10
lines changed

src/device_manager.rs

Lines changed: 143 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,16 @@
1212
//! devices, 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::collections::HashMap;
1920
use std::result;
2021
use std::sync::Arc;
2122

23+
use vm_memory::GuestAddress;
24+
2225
/// Error type for `DeviceManager` usage.
2326
#[derive(Debug)]
2427
pub enum Error {
@@ -43,14 +46,56 @@ pub struct DeviceDescriptor {
4346
pub parent_bus: Option<Arc<dyn DeviceIo>>,
4447
}
4548

49+
// Structure describing a bus range.
50+
#[derive(Debug, Copy, Clone)]
51+
struct BusRange {
52+
base: IoAddress,
53+
size: IoSize,
54+
}
55+
56+
impl BusRange {
57+
fn new_pio_range(base: u16, size: u16) -> Self {
58+
BusRange {
59+
base: IoAddress::Pio(base),
60+
size: IoSize::Pio(size),
61+
}
62+
}
63+
fn new_mmio_range(base: u64, size: u64) -> Self {
64+
BusRange {
65+
base: IoAddress::Mmio(GuestAddress(base)),
66+
size: IoSize::Mmio(size),
67+
}
68+
}
69+
}
70+
71+
impl Eq for BusRange {}
72+
73+
impl PartialEq for BusRange {
74+
fn eq(&self, other: &BusRange) -> bool {
75+
self.base == other.base
76+
}
77+
}
78+
79+
impl Ord for BusRange {
80+
fn cmp(&self, other: &BusRange) -> Ordering {
81+
self.base.cmp(&other.base)
82+
}
83+
}
84+
85+
impl PartialOrd for BusRange {
86+
fn partial_cmp(&self, other: &BusRange) -> Option<Ordering> {
87+
self.base.partial_cmp(&other.base)
88+
}
89+
}
90+
4691
/// System device manager serving for all devices management and VM exit handling.
4792
pub struct DeviceManager {
4893
/// Devices information mapped by instance id.
4994
devices: HashMap<u16, DeviceDescriptor>,
5095
/// Range mapping for VM exit pio operations.
51-
pio_bus: BTreeMap<(u16, u16), Arc<dyn DeviceIo>>,
96+
pio_bus: BTreeMap<BusRange, Arc<dyn DeviceIo>>,
5297
/// Range mapping for VM exit mmio operations.
53-
mmio_bus: BTreeMap<(u64, u64), Arc<dyn DeviceIo>>,
98+
mmio_bus: BTreeMap<BusRange, Arc<dyn DeviceIo>>,
5499
}
55100

56101
impl DeviceManager {
@@ -87,7 +132,11 @@ impl DeviceManager {
87132
for (idx, res) in resources.iter().enumerate() {
88133
match *res {
89134
Resource::PioAddressRange { base, size } => {
90-
if self.pio_bus.insert((base, size), device.clone()).is_some() {
135+
if self
136+
.pio_bus
137+
.insert(BusRange::new_pio_range(base, size), device.clone())
138+
.is_some()
139+
{
91140
// Unregister and let VMM free resources.
92141
if idx > 0 {
93142
self.unregister_resources(&resources[0..idx]);
@@ -96,7 +145,11 @@ impl DeviceManager {
96145
}
97146
}
98147
Resource::MmioAddressRange { base, size } => {
99-
if self.mmio_bus.insert((base, size), device.clone()).is_some() {
148+
if self
149+
.mmio_bus
150+
.insert(BusRange::new_mmio_range(base, size), device.clone())
151+
.is_some()
152+
{
100153
// Unregister and let VMM free resources.
101154
if idx > 0 {
102155
self.unregister_resources(&resources[0..idx]);
@@ -145,10 +198,10 @@ impl DeviceManager {
145198
for res in resources.iter() {
146199
match *res {
147200
Resource::PioAddressRange { base, size } => {
148-
self.pio_bus.remove(&(base, size));
201+
self.pio_bus.remove(&BusRange::new_pio_range(base, size));
149202
}
150203
Resource::MmioAddressRange { base, size } => {
151-
self.mmio_bus.remove(&(base, size));
204+
self.mmio_bus.remove(&BusRange::new_mmio_range(base, size));
152205
}
153206
_ => continue,
154207
}
@@ -173,4 +226,87 @@ impl DeviceManager {
173226
Err(Error::DeviceNotExist)
174227
}
175228
}
229+
230+
fn first_before(
231+
&self,
232+
addr: IoAddress,
233+
map: BTreeMap<BusRange, Arc<dyn DeviceIo>>,
234+
) -> Option<(BusRange, Arc<dyn DeviceIo>)> {
235+
for (range, dev) in map.iter().rev() {
236+
if range.base <= addr {
237+
return Some((*range, dev.clone()));
238+
}
239+
}
240+
None
241+
}
242+
243+
// Return the Device mapped the address.
244+
fn get_device(
245+
&self,
246+
addr: IoAddress,
247+
map: BTreeMap<BusRange, Arc<dyn DeviceIo>>,
248+
) -> Option<Arc<dyn DeviceIo>> {
249+
if let Some((range, dev)) = self.first_before(addr, map) {
250+
if (addr.raw_value() - range.base.raw_value()) < range.size.raw_value() {
251+
return Some(dev);
252+
}
253+
}
254+
None
255+
}
256+
257+
/// A helper function handling PIO read command during VM exit.
258+
/// The virtual device itself provides mutable ability and thead-safe protection.
259+
///
260+
/// Return error if failed to get the device.
261+
pub fn pio_read(&self, addr: u16, data: &mut [u8]) -> Result<()> {
262+
if let Some(device) = self.get_device(IoAddress::Pio(addr), self.pio_bus.clone()) {
263+
device.read(IoAddress::Pio(addr), data);
264+
Ok(())
265+
} else {
266+
Err(Error::DeviceNotExist)
267+
}
268+
}
269+
270+
/// A helper function handling PIO write command during VM exit.
271+
/// The virtual device itself provides mutable ability and thead-safe protection.
272+
///
273+
/// Return error if failed to get the device.
274+
pub fn pio_write(&self, addr: u16, data: &[u8]) -> Result<()> {
275+
if let Some(device) = self.get_device(IoAddress::Pio(addr), self.pio_bus.clone()) {
276+
device.write(IoAddress::Pio(addr), data);
277+
Ok(())
278+
} else {
279+
Err(Error::DeviceNotExist)
280+
}
281+
}
282+
283+
/// A helper function handling MMIO read command during VM exit.
284+
/// The virtual device itself provides mutable ability and thead-safe protection.
285+
///
286+
/// Return error if failed to get the device.
287+
pub fn mmio_read(&self, addr: u64, data: &mut [u8]) -> Result<()> {
288+
if let Some(device) =
289+
self.get_device(IoAddress::Mmio(GuestAddress(addr)), self.mmio_bus.clone())
290+
{
291+
device.read(IoAddress::Mmio(GuestAddress(addr)), data);
292+
Ok(())
293+
} else {
294+
Err(Error::DeviceNotExist)
295+
}
296+
}
297+
298+
/// A helper function handling MMIO write command during VM exit.
299+
/// The virtual device itself provides mutable ability and thead-safe protection.
300+
///
301+
/// Return error if failed to get the device.
302+
pub fn mmio_write(&self, addr: u64, data: &[u8]) -> Result<()> {
303+
if let Some(device) =
304+
self.get_device(IoAddress::Mmio(GuestAddress(addr)), self.mmio_bus.clone())
305+
{
306+
device.write(IoAddress::Mmio(GuestAddress(addr)), data);
307+
Ok(())
308+
} else {
309+
Err(Error::DeviceNotExist)
310+
}
311+
}
176312
}

src/lib.rs

Lines changed: 56 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,33 @@
77
88
extern crate vm_memory;
99

10-
use vm_memory::GuestAddress;
10+
use std::cmp::{Ord, Ordering, PartialOrd};
11+
use vm_memory::{Address, GuestAddress, GuestUsize};
1112

1213
pub mod device_manager;
1314
pub mod interrupt;
1415
pub mod resources;
1516

17+
/// IO Size.
18+
#[derive(Debug, Copy, Clone)]
19+
pub enum IoSize {
20+
/// Port I/O size.
21+
Pio(u16),
22+
23+
/// Memory mapped I/O size.
24+
Mmio(GuestUsize),
25+
}
26+
27+
impl IoSize {
28+
/// Get the raw value as u64 to make operation simple.
29+
pub fn raw_value(&self) -> u64 {
30+
match *self {
31+
IoSize::Pio(p) => p as u64,
32+
IoSize::Mmio(m) => m,
33+
}
34+
}
35+
}
36+
1637
/// IO Addresses.
1738
#[derive(Debug, Copy, Clone)]
1839
pub enum IoAddress {
@@ -23,15 +44,47 @@ pub enum IoAddress {
2344
Mmio(GuestAddress),
2445
}
2546

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

3588
/// Write `data` to the guest physical address `addr`.
36-
fn write(&mut self, addr: IoAddress, data: &[u8]);
89+
fn write(&self, addr: IoAddress, data: &[u8]);
3790
}

0 commit comments

Comments
 (0)