Skip to content

Commit 2183f1d

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 ef7cb1f commit 2183f1d

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
@@ -72,6 +72,8 @@ impl Display for Error {
7272
impl std::error::Error for Error {}
7373

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

105107
/// Return the guest physical address of descriptor buffer
106108
pub fn addr(&self) -> GuestAddress {
107-
GuestAddress(self.addr)
109+
GuestAddress(u64::from_le(self.addr))
108110
}
109111

110112
/// Return the length of descriptor buffer
111113
pub fn len(&self) -> u32 {
112-
self.len
114+
u32::from_le(self.len)
113115
}
114116

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

121123
/// Return the value stored in the `next` field of the descriptor.
122124
pub fn next(&self) -> u16 {
123-
self.next
125+
u16::from_le(self.next)
124126
}
125127

126128
/// Check whether this descriptor refers to a buffer containing an indirect descriptor table.
@@ -370,6 +372,7 @@ impl<'b, M: GuestAddressSpace> Iterator for AvailIter<'b, M> {
370372
let head_index: u16 = self
371373
.mem
372374
.load(addr, Ordering::Acquire)
375+
.map(u16::from_le)
373376
.map_err(|_| error!("Failed to read from memory {:x}", addr.raw_value()))
374377
.ok()?;
375378

@@ -385,6 +388,8 @@ impl<'b, M: GuestAddressSpace> Iterator for AvailIter<'b, M> {
385388
}
386389

387390
/// Represents the contents of an element from the used virtqueue ring.
391+
///
392+
/// All its fields are represented with little-endian ordering.
388393
#[repr(C)]
389394
#[derive(Clone, Copy, Default, Debug)]
390395
pub struct VirtqUsedElem {
@@ -394,10 +399,10 @@ pub struct VirtqUsedElem {
394399

395400
impl VirtqUsedElem {
396401
/// Create a new `VirtqUsedElem` instance.
397-
pub fn new(id: u16, len: u32) -> Self {
402+
pub fn new(id: u32, len: u32) -> Self {
398403
VirtqUsedElem {
399-
id: u32::from(id),
400-
len,
404+
id: u32::to_le(id),
405+
len: u32::to_le(len),
401406
}
402407
}
403408
}
@@ -580,12 +585,13 @@ impl<M: GuestAddressSpace> QueueState<M> {
580585
let offset = VIRTQ_USED_RING_HEADER_SIZE + elem_sz;
581586
let addr = self.used_ring.unchecked_add(offset);
582587

583-
mem.store(val, addr, order).map_err(Error::GuestMemory)
588+
mem.store(u16::to_le(val), addr, order)
589+
.map_err(Error::GuestMemory)
584590
}
585591

586592
// Set the value of the `flags` field of the used ring, applying the specified ordering.
587593
fn set_used_flags(&mut self, mem: &M::T, val: u16, order: Ordering) -> Result<(), Error> {
588-
mem.store(val, self.used_ring, order)
594+
mem.store(u16::to_le(val), self.used_ring, order)
589595
.map_err(Error::GuestMemory)
590596
}
591597

@@ -630,6 +636,7 @@ impl<M: GuestAddressSpace> QueueState<M> {
630636
let used_event_addr = self.avail_ring.unchecked_add(offset);
631637

632638
mem.load(used_event_addr, order)
639+
.map(u16::from_le)
633640
.map(Wrapping)
634641
.map_err(Error::GuestMemory)
635642
}
@@ -779,6 +786,7 @@ impl<M: GuestAddressSpace> QueueStateT<M> for QueueState<M> {
779786
let addr = self.avail_ring.unchecked_add(2);
780787

781788
mem.load(addr, order)
789+
.map(u16::from_le)
782790
.map(Wrapping)
783791
.map_err(Error::GuestMemory)
784792
}
@@ -796,13 +804,13 @@ impl<M: GuestAddressSpace> QueueStateT<M> for QueueState<M> {
796804
let elem_sz = next_used_index * VIRTQ_USED_ELEMENT_SIZE;
797805
let offset = VIRTQ_USED_RING_HEADER_SIZE + elem_sz;
798806
let addr = self.used_ring.unchecked_add(offset);
799-
mem.write_obj(VirtqUsedElem::new(head_index, len), addr)
807+
mem.write_obj(VirtqUsedElem::new(head_index.into(), len), addr)
800808
.map_err(Error::GuestMemory)?;
801809

802810
self.next_used += Wrapping(1);
803811

804812
mem.store(
805-
self.next_used.0,
813+
u16::to_le(self.next_used.0),
806814
self.used_ring.unchecked_add(2),
807815
Ordering::Release,
808816
)

0 commit comments

Comments
 (0)