Skip to content

Commit d6689db

Browse files
committed
queue: Add support for big-endian arches
According to the virtio specification, on non-legacy devices the values in the virtqueue are represented in little-endian, no matter the host nor the guest actual endianess. If needed, fix the endianess of values that have been read from memory or will be written to it. The methods "from_le" and "to_le" are a no-op on little-endian machines, so this shouldn't have a performance impact on those. Fixes #117 Signed-off-by: Sergio Lopez <slp@redhat.com>
1 parent 0a3950c commit d6689db

File tree

2 files changed

+38
-29
lines changed

2 files changed

+38
-29
lines changed

crates/virtio-queue/src/lib.rs

Lines changed: 30 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ use std::sync::{Arc, Mutex, MutexGuard};
3030
use log::error;
3131
use vm_memory::{
3232
Address, ByteValued, Bytes, GuestAddress, GuestAddressSpace, GuestMemory, GuestMemoryError,
33+
Le16, Le32, Le64,
3334
};
3435

3536
use self::defs::{
@@ -71,21 +72,23 @@ impl Display for Error {
7172
impl std::error::Error for Error {}
7273

7374
/// A virtio descriptor constraints with C representation.
75+
///
76+
/// All its fields are represented with little-endian ordering.
7477
#[repr(C)]
7578
#[derive(Default, Clone, Copy, Debug)]
7679
pub struct Descriptor {
7780
/// Guest physical address of device specific data
78-
addr: u64,
81+
addr: Le64,
7982

8083
/// Length of device specific data
81-
len: u32,
84+
len: Le32,
8285

8386
/// Includes next, write, and indirect bits
84-
flags: u16,
87+
flags: Le16,
8588

8689
/// Index into the descriptor table of the next descriptor if flags has
8790
/// the next bit set
88-
next: u16,
91+
next: Le16,
8992
}
9093

9194
#[allow(clippy::len_without_is_empty)]
@@ -94,32 +97,32 @@ impl Descriptor {
9497
#[cfg(any(test, feature = "test-utils"))]
9598
pub fn new(addr: u64, len: u32, flags: u16, next: u16) -> Self {
9699
Descriptor {
97-
addr,
98-
len,
99-
flags,
100-
next,
100+
addr: addr.into(),
101+
len: len.into(),
102+
flags: flags.into(),
103+
next: next.into(),
101104
}
102105
}
103106

104107
/// Return the guest physical address of descriptor buffer
105108
pub fn addr(&self) -> GuestAddress {
106-
GuestAddress(self.addr)
109+
GuestAddress(self.addr.into())
107110
}
108111

109112
/// Return the length of descriptor buffer
110113
pub fn len(&self) -> u32 {
111-
self.len
114+
self.len.into()
112115
}
113116

114117
/// Return the flags for this descriptor, including next, write and indirect
115118
/// bits
116119
pub fn flags(&self) -> u16 {
117-
self.flags
120+
self.flags.into()
118121
}
119122

120123
/// Return the value stored in the `next` field of the descriptor.
121124
pub fn next(&self) -> u16 {
122-
self.next
125+
self.next.into()
123126
}
124127

125128
/// Check whether this descriptor refers to a buffer containing an indirect descriptor table.
@@ -385,6 +388,7 @@ where
385388
let head_index: u16 = self
386389
.mem
387390
.load(addr, Ordering::Acquire)
391+
.map(u16::from_le)
388392
.map_err(|_| error!("Failed to read from memory {:x}", addr.raw_value()))
389393
.ok()?;
390394

@@ -400,19 +404,21 @@ where
400404
}
401405

402406
/// Represents the contents of an element from the used virtqueue ring.
407+
///
408+
/// All its fields are represented with little-endian ordering.
403409
#[repr(C)]
404410
#[derive(Clone, Copy, Default, Debug)]
405411
pub struct VirtqUsedElem {
406-
id: u32,
407-
len: u32,
412+
id: Le32,
413+
len: Le32,
408414
}
409415

410416
impl VirtqUsedElem {
411417
/// Create a new `VirtqUsedElem` instance.
412-
pub fn new(id: u16, len: u32) -> Self {
418+
pub fn new(id: u32, len: u32) -> Self {
413419
VirtqUsedElem {
414-
id: u32::from(id),
415-
len,
420+
id: id.into(),
421+
len: len.into(),
416422
}
417423
}
418424
}
@@ -603,7 +609,8 @@ impl QueueState {
603609
let offset = VIRTQ_USED_RING_HEADER_SIZE + elem_sz;
604610
let addr = self.used_ring.unchecked_add(offset);
605611

606-
mem.store(val, addr, order).map_err(Error::GuestMemory)
612+
mem.store(u16::to_le(val), addr, order)
613+
.map_err(Error::GuestMemory)
607614
}
608615

609616
// Set the value of the `flags` field of the used ring, applying the specified ordering.
@@ -613,7 +620,7 @@ impl QueueState {
613620
val: u16,
614621
order: Ordering,
615622
) -> Result<(), Error> {
616-
mem.store(val, self.used_ring, order)
623+
mem.store(u16::to_le(val), self.used_ring, order)
617624
.map_err(Error::GuestMemory)
618625
}
619626

@@ -658,6 +665,7 @@ impl QueueState {
658665
let used_event_addr = self.avail_ring.unchecked_add(offset);
659666

660667
mem.load(used_event_addr, order)
668+
.map(u16::from_le)
661669
.map(Wrapping)
662670
.map_err(Error::GuestMemory)
663671
}
@@ -806,6 +814,7 @@ impl QueueStateT for QueueState {
806814
let addr = self.avail_ring.unchecked_add(2);
807815

808816
mem.load(addr, order)
817+
.map(u16::from_le)
809818
.map(Wrapping)
810819
.map_err(Error::GuestMemory)
811820
}
@@ -828,13 +837,13 @@ impl QueueStateT for QueueState {
828837
let elem_sz = next_used_index * VIRTQ_USED_ELEMENT_SIZE;
829838
let offset = VIRTQ_USED_RING_HEADER_SIZE + elem_sz;
830839
let addr = self.used_ring.unchecked_add(offset);
831-
mem.write_obj(VirtqUsedElem::new(head_index, len), addr)
840+
mem.write_obj(VirtqUsedElem::new(head_index.into(), len), addr)
832841
.map_err(Error::GuestMemory)?;
833842

834843
self.next_used += Wrapping(1);
835844

836845
mem.store(
837-
self.next_used.0,
846+
u16::to_le(self.next_used.0),
838847
self.used_ring.unchecked_add(2),
839848
Ordering::Release,
840849
)

crates/virtio-queue/src/mock.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -182,17 +182,17 @@ impl<'a, M: GuestMemory> DescriptorTable<'a, M> {
182182
for (pos, index_value) in indices.iter().copied().enumerate() {
183183
// Addresses and lens constant for now.
184184
let mut desc = Descriptor {
185-
addr: 0x1000,
186-
len: 0x1000,
185+
addr: 0x1000.into(),
186+
len: 0x1000.into(),
187187
..Descriptor::default()
188188
};
189189

190190
// It's not the last descriptor in the chain.
191191
if pos < indices.len() - 1 {
192-
desc.flags = VIRTQ_DESC_F_NEXT;
193-
desc.next = indices[pos + 1];
192+
desc.flags = VIRTQ_DESC_F_NEXT.into();
193+
desc.next = indices[pos + 1].into();
194194
} else {
195-
desc.flags = 0;
195+
desc.flags = 0.into();
196196
}
197197
self.store(index_value, desc);
198198
}
@@ -344,9 +344,9 @@ impl<'a, M: GuestMemory> MockSplitQueue<'a, M> {
344344
let indirect_addr = self.alloc_indirect_chain(len);
345345

346346
let mut desc = self.desc_table.load(head_idx);
347-
desc.flags = VIRTQ_DESC_F_INDIRECT;
348-
desc.addr = indirect_addr.raw_value();
349-
desc.len = u32::from(len) * size_of::<Descriptor>() as u32;
347+
desc.flags = VIRTQ_DESC_F_INDIRECT.into();
348+
desc.addr = indirect_addr.raw_value().into();
349+
desc.len = (u32::from(len) * size_of::<Descriptor>() as u32).into();
350350

351351
self.desc_table.store(head_idx, desc);
352352
self.update_avail_idx(head_idx);

0 commit comments

Comments
 (0)