Skip to content

Commit 98f5e49

Browse files
author
Sebastien Boeuf
committed
virtio-queue: Handle address translations
For devices with access to data in memory being translated, we add to the Queue the ability to translate the address stored in the descriptor. It is very helpful as it performs the translation right after the untranslated address is read from memory, avoiding any errors from happening from the consumer's crate perspective. It also allows the consumer to reduce greatly the amount of duplicated code for applying the translation in many different places. Signed-off-by: Sebastien Boeuf <[email protected]>
1 parent f494b56 commit 98f5e49

File tree

2 files changed

+58
-15
lines changed

2 files changed

+58
-15
lines changed

coverage_config_x86_64.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"coverage_score": 90.6,
2+
"coverage_score": 90.4,
33
"exclude_path": "",
44
"crate_features": "virtio-blk/backend-stdio"
55
}

crates/virtio-queue/src/lib.rs

Lines changed: 57 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ use std::fmt::{self, Debug, Display};
2424
use std::mem::size_of;
2525
use std::num::Wrapping;
2626
use std::sync::atomic::{fence, Ordering};
27+
use std::sync::Arc;
2728

2829
use defs::{
2930
VIRTQ_AVAIL_ELEMENT_SIZE, VIRTQ_AVAIL_RING_HEADER_SIZE, VIRTQ_AVAIL_RING_META_SIZE,
@@ -37,6 +38,13 @@ use vm_memory::{
3738

3839
use log::error;
3940

41+
/// Trait for devices with access to data in memory being limited and/or
42+
/// translated.
43+
pub trait AccessPlatform: Debug {
44+
/// Provide a way to translate address ranges.
45+
fn translate(&self, base: u64, size: u64) -> std::result::Result<u64, std::io::Error>;
46+
}
47+
4048
/// Virtio Queue related errors.
4149
#[derive(Debug)]
4250
pub enum Error {
@@ -153,6 +161,7 @@ pub struct DescriptorChain<M: GuestAddressSpace> {
153161
next_index: u16,
154162
ttl: u16,
155163
is_indirect: bool,
164+
access_platform: Option<Arc<dyn AccessPlatform>>,
156165
}
157166

158167
impl<M: GuestAddressSpace> DescriptorChain<M> {
@@ -162,6 +171,7 @@ impl<M: GuestAddressSpace> DescriptorChain<M> {
162171
queue_size: u16,
163172
ttl: u16,
164173
head_index: u16,
174+
access_platform: Option<Arc<dyn AccessPlatform>>,
165175
) -> Self {
166176
DescriptorChain {
167177
mem,
@@ -171,12 +181,26 @@ impl<M: GuestAddressSpace> DescriptorChain<M> {
171181
next_index: head_index,
172182
ttl,
173183
is_indirect: false,
184+
access_platform,
174185
}
175186
}
176187

177188
/// Create a new `DescriptorChain` instance.
178-
fn new(mem: M::T, desc_table: GuestAddress, queue_size: u16, head_index: u16) -> Self {
179-
Self::with_ttl(mem, desc_table, queue_size, queue_size, head_index)
189+
fn new(
190+
mem: M::T,
191+
desc_table: GuestAddress,
192+
queue_size: u16,
193+
head_index: u16,
194+
access_platform: Option<Arc<dyn AccessPlatform>>,
195+
) -> Self {
196+
Self::with_ttl(
197+
mem,
198+
desc_table,
199+
queue_size,
200+
queue_size,
201+
head_index,
202+
access_platform,
203+
)
180204
}
181205

182206
/// Get the descriptor index of the chain header
@@ -253,7 +277,14 @@ impl<M: GuestAddressSpace> Iterator for DescriptorChain<M> {
253277
.desc_table
254278
.unchecked_add(self.next_index as u64 * size_of::<Descriptor>() as u64);
255279

256-
let desc = self.mem.read_obj::<Descriptor>(desc_addr).ok()?;
280+
let mut desc = self.mem.read_obj::<Descriptor>(desc_addr).ok()?;
281+
// When needed, it's very important to translate the decriptor address
282+
// before returning the Descriptor to the consumer.
283+
if let Some(access_platform) = &self.access_platform {
284+
desc.addr = access_platform
285+
.translate(desc.addr, u64::from(desc.len))
286+
.ok()?;
287+
}
257288

258289
if desc.is_indirect() {
259290
self.process_indirect_descriptor(desc).ok()?;
@@ -325,6 +356,7 @@ pub struct AvailIter<'b, M: GuestAddressSpace> {
325356
last_index: Wrapping<u16>,
326357
queue_size: u16,
327358
next_avail: &'b mut Wrapping<u16>,
359+
access_platform: &'b Option<Arc<dyn AccessPlatform>>,
328360
}
329361

330362
impl<'b, M: GuestAddressSpace> Iterator for AvailIter<'b, M> {
@@ -360,6 +392,7 @@ impl<'b, M: GuestAddressSpace> Iterator for AvailIter<'b, M> {
360392
self.desc_table,
361393
self.queue_size,
362394
head_index,
395+
self.access_platform.clone(),
363396
))
364397
}
365398
}
@@ -415,6 +448,9 @@ pub struct Queue<M: GuestAddressSpace> {
415448

416449
/// Guest physical address of the used ring
417450
pub used_ring: GuestAddress,
451+
452+
/// Access platform handler
453+
pub access_platform: Option<Arc<dyn AccessPlatform>>,
418454
}
419455

420456
impl<M: GuestAddressSpace> Queue<M> {
@@ -432,6 +468,7 @@ impl<M: GuestAddressSpace> Queue<M> {
432468
next_used: Wrapping(0),
433469
event_idx_enabled: false,
434470
signalled_used: None,
471+
access_platform: None,
435472
}
436473
}
437474

@@ -545,6 +582,7 @@ impl<M: GuestAddressSpace> Queue<M> {
545582
last_index: idx,
546583
queue_size: self.actual_size(),
547584
next_avail: &mut self.next_avail,
585+
access_platform: &self.access_platform,
548586
})
549587
}
550588

@@ -763,25 +801,29 @@ mod tests {
763801

764802
// index >= queue_size
765803
assert!(
766-
DescriptorChain::<&GuestMemoryMmap>::new(m, vq.start(), 16, 16)
804+
DescriptorChain::<&GuestMemoryMmap>::new(m, vq.start(), 16, 16, None)
767805
.next()
768806
.is_none()
769807
);
770808

771809
// desc_table address is way off
772-
assert!(
773-
DescriptorChain::<&GuestMemoryMmap>::new(m, GuestAddress(0x00ff_ffff_ffff), 16, 0)
774-
.next()
775-
.is_none()
776-
);
810+
assert!(DescriptorChain::<&GuestMemoryMmap>::new(
811+
m,
812+
GuestAddress(0x00ff_ffff_ffff),
813+
16,
814+
0,
815+
None
816+
)
817+
.next()
818+
.is_none());
777819

778820
{
779821
// the first desc has a normal len, and the next_descriptor flag is set
780822
// but the the index of the next descriptor is too large
781823
let desc = Descriptor::new(0x1000, 0x1000, VIRTQ_DESC_F_NEXT, 16);
782824
vq.desc_table().store(0, desc);
783825

784-
let mut c = DescriptorChain::<&GuestMemoryMmap>::new(m, vq.start(), 16, 0);
826+
let mut c = DescriptorChain::<&GuestMemoryMmap>::new(m, vq.start(), 16, 0, None);
785827
c.next().unwrap();
786828
assert!(c.next().is_none());
787829
}
@@ -794,7 +836,7 @@ mod tests {
794836
let desc = Descriptor::new(0x2000, 0x1000, 0, 0);
795837
vq.desc_table().store(1, desc);
796838

797-
let mut c = DescriptorChain::<&GuestMemoryMmap>::new(m, vq.start(), 16, 0);
839+
let mut c = DescriptorChain::<&GuestMemoryMmap>::new(m, vq.start(), 16, 0, None);
798840

799841
assert_eq!(
800842
c.memory() as *const GuestMemoryMmap,
@@ -830,7 +872,8 @@ mod tests {
830872
let desc = Descriptor::new(0x3000, 0x1000, 0, 0);
831873
dtable.store(2, desc);
832874

833-
let mut c: DescriptorChain<&GuestMemoryMmap> = DescriptorChain::new(m, vq.start(), 16, 0);
875+
let mut c: DescriptorChain<&GuestMemoryMmap> =
876+
DescriptorChain::new(m, vq.start(), 16, 0, None);
834877

835878
// The chain logic hasn't parsed the indirect descriptor yet.
836879
assert!(!c.is_indirect);
@@ -874,7 +917,7 @@ mod tests {
874917
vq.desc_table().store(0, desc);
875918

876919
let mut c: DescriptorChain<&GuestMemoryMmap> =
877-
DescriptorChain::new(m, vq.start(), 16, 0);
920+
DescriptorChain::new(m, vq.start(), 16, 0, None);
878921

879922
assert!(c.next().is_none());
880923
}
@@ -888,7 +931,7 @@ mod tests {
888931
vq.desc_table().store(0, desc);
889932

890933
let mut c: DescriptorChain<&GuestMemoryMmap> =
891-
DescriptorChain::new(m, vq.start(), 16, 0);
934+
DescriptorChain::new(m, vq.start(), 16, 0, None);
892935

893936
assert!(c.next().is_none());
894937
}

0 commit comments

Comments
 (0)