Skip to content

Commit

Permalink
queue: Add support for big-endian arches
Browse files Browse the repository at this point in the history
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]>
  • Loading branch information
slp committed Nov 18, 2021
1 parent ef7cb1f commit 2183f1d
Showing 1 changed file with 23 additions and 15 deletions.
38 changes: 23 additions & 15 deletions crates/virtio-queue/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ impl Display for Error {
impl std::error::Error for Error {}

/// A virtio descriptor constraints with C representation.
///
/// All its fields are represented with little-endian ordering.
#[repr(C)]
#[derive(Default, Clone, Copy, Debug)]
pub struct Descriptor {
Expand All @@ -95,32 +97,32 @@ impl Descriptor {
#[cfg(any(test, feature = "test-utils"))]
pub fn new(addr: u64, len: u32, flags: u16, next: u16) -> Self {
Descriptor {
addr,
len,
flags,
next,
addr: u64::to_le(addr),
len: u32::to_le(len),
flags: u16::to_le(flags),
next: u16::to_le(next),
}
}

/// Return the guest physical address of descriptor buffer
pub fn addr(&self) -> GuestAddress {
GuestAddress(self.addr)
GuestAddress(u64::from_le(self.addr))
}

/// Return the length of descriptor buffer
pub fn len(&self) -> u32 {
self.len
u32::from_le(self.len)
}

/// Return the flags for this descriptor, including next, write and indirect
/// bits
pub fn flags(&self) -> u16 {
self.flags
u16::from_le(self.flags)
}

/// Return the value stored in the `next` field of the descriptor.
pub fn next(&self) -> u16 {
self.next
u16::from_le(self.next)
}

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

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

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

impl VirtqUsedElem {
/// Create a new `VirtqUsedElem` instance.
pub fn new(id: u16, len: u32) -> Self {
pub fn new(id: u32, len: u32) -> Self {
VirtqUsedElem {
id: u32::from(id),
len,
id: u32::to_le(id),
len: u32::to_le(len),
}
}
}
Expand Down Expand Up @@ -580,12 +585,13 @@ impl<M: GuestAddressSpace> QueueState<M> {
let offset = VIRTQ_USED_RING_HEADER_SIZE + elem_sz;
let addr = self.used_ring.unchecked_add(offset);

mem.store(val, addr, order).map_err(Error::GuestMemory)
mem.store(u16::to_le(val), addr, order)
.map_err(Error::GuestMemory)
}

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

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

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

mem.load(addr, order)
.map(u16::from_le)
.map(Wrapping)
.map_err(Error::GuestMemory)
}
Expand All @@ -796,13 +804,13 @@ impl<M: GuestAddressSpace> QueueStateT<M> for QueueState<M> {
let elem_sz = next_used_index * VIRTQ_USED_ELEMENT_SIZE;
let offset = VIRTQ_USED_RING_HEADER_SIZE + elem_sz;
let addr = self.used_ring.unchecked_add(offset);
mem.write_obj(VirtqUsedElem::new(head_index, len), addr)
mem.write_obj(VirtqUsedElem::new(head_index.into(), len), addr)
.map_err(Error::GuestMemory)?;

self.next_used += Wrapping(1);

mem.store(
self.next_used.0,
u16::to_le(self.next_used.0),
self.used_ring.unchecked_add(2),
Ordering::Release,
)
Expand Down

0 comments on commit 2183f1d

Please sign in to comment.