Skip to content

Commit 39b7896

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 <[email protected]>
1 parent 0a3950c commit 39b7896

File tree

1 file changed

+23
-15
lines changed

1 file changed

+23
-15
lines changed

crates/virtio-queue/src/lib.rs

Lines changed: 23 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,8 @@ impl Display for Error {
7171
impl std::error::Error for Error {}
7272

7373
/// A virtio descriptor constraints with C representation.
74+
///
75+
/// All its fields are represented with little-endian ordering.
7476
#[repr(C)]
7577
#[derive(Default, Clone, Copy, Debug)]
7678
pub struct Descriptor {
@@ -94,32 +96,32 @@ impl Descriptor {
9496
#[cfg(any(test, feature = "test-utils"))]
9597
pub fn new(addr: u64, len: u32, flags: u16, next: u16) -> Self {
9698
Descriptor {
97-
addr,
98-
len,
99-
flags,
100-
next,
99+
addr: u64::to_le(addr),
100+
len: u32::to_le(len),
101+
flags: u16::to_le(flags),
102+
next: u16::to_le(next),
101103
}
102104
}
103105

104106
/// Return the guest physical address of descriptor buffer
105107
pub fn addr(&self) -> GuestAddress {
106-
GuestAddress(self.addr)
108+
GuestAddress(u64::from_le(self.addr))
107109
}
108110

109111
/// Return the length of descriptor buffer
110112
pub fn len(&self) -> u32 {
111-
self.len
113+
u32::from_le(self.len)
112114
}
113115

114116
/// Return the flags for this descriptor, including next, write and indirect
115117
/// bits
116118
pub fn flags(&self) -> u16 {
117-
self.flags
119+
u16::from_le(self.flags)
118120
}
119121

120122
/// Return the value stored in the `next` field of the descriptor.
121123
pub fn next(&self) -> u16 {
122-
self.next
124+
u16::from_le(self.next)
123125
}
124126

125127
/// Check whether this descriptor refers to a buffer containing an indirect descriptor table.
@@ -385,6 +387,7 @@ where
385387
let head_index: u16 = self
386388
.mem
387389
.load(addr, Ordering::Acquire)
390+
.map(u16::from_le)
388391
.map_err(|_| error!("Failed to read from memory {:x}", addr.raw_value()))
389392
.ok()?;
390393

@@ -400,6 +403,8 @@ where
400403
}
401404

402405
/// Represents the contents of an element from the used virtqueue ring.
406+
///
407+
/// All its fields are represented with little-endian ordering.
403408
#[repr(C)]
404409
#[derive(Clone, Copy, Default, Debug)]
405410
pub struct VirtqUsedElem {
@@ -409,10 +414,10 @@ pub struct VirtqUsedElem {
409414

410415
impl VirtqUsedElem {
411416
/// Create a new `VirtqUsedElem` instance.
412-
pub fn new(id: u16, len: u32) -> Self {
417+
pub fn new(id: u32, len: u32) -> Self {
413418
VirtqUsedElem {
414-
id: u32::from(id),
415-
len,
419+
id: u32::to_le(id),
420+
len: u32::to_le(len),
416421
}
417422
}
418423
}
@@ -603,7 +608,8 @@ impl QueueState {
603608
let offset = VIRTQ_USED_RING_HEADER_SIZE + elem_sz;
604609
let addr = self.used_ring.unchecked_add(offset);
605610

606-
mem.store(val, addr, order).map_err(Error::GuestMemory)
611+
mem.store(u16::to_le(val), addr, order)
612+
.map_err(Error::GuestMemory)
607613
}
608614

609615
// Set the value of the `flags` field of the used ring, applying the specified ordering.
@@ -613,7 +619,7 @@ impl QueueState {
613619
val: u16,
614620
order: Ordering,
615621
) -> Result<(), Error> {
616-
mem.store(val, self.used_ring, order)
622+
mem.store(u16::to_le(val), self.used_ring, order)
617623
.map_err(Error::GuestMemory)
618624
}
619625

@@ -658,6 +664,7 @@ impl QueueState {
658664
let used_event_addr = self.avail_ring.unchecked_add(offset);
659665

660666
mem.load(used_event_addr, order)
667+
.map(u16::from_le)
661668
.map(Wrapping)
662669
.map_err(Error::GuestMemory)
663670
}
@@ -806,6 +813,7 @@ impl QueueStateT for QueueState {
806813
let addr = self.avail_ring.unchecked_add(2);
807814

808815
mem.load(addr, order)
816+
.map(u16::from_le)
809817
.map(Wrapping)
810818
.map_err(Error::GuestMemory)
811819
}
@@ -828,13 +836,13 @@ impl QueueStateT for QueueState {
828836
let elem_sz = next_used_index * VIRTQ_USED_ELEMENT_SIZE;
829837
let offset = VIRTQ_USED_RING_HEADER_SIZE + elem_sz;
830838
let addr = self.used_ring.unchecked_add(offset);
831-
mem.write_obj(VirtqUsedElem::new(head_index, len), addr)
839+
mem.write_obj(VirtqUsedElem::new(head_index.into(), len), addr)
832840
.map_err(Error::GuestMemory)?;
833841

834842
self.next_used += Wrapping(1);
835843

836844
mem.store(
837-
self.next_used.0,
845+
u16::to_le(self.next_used.0),
838846
self.used_ring.unchecked_add(2),
839847
Ordering::Release,
840848
)

0 commit comments

Comments
 (0)