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 0a3950c commit 39b7896
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 @@ -71,6 +71,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 @@ -94,32 +96,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 @@ -385,6 +387,7 @@ where
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 @@ -400,6 +403,8 @@ where
}

/// 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 @@ -409,10 +414,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 @@ -603,7 +608,8 @@ impl QueueState {
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.
Expand All @@ -613,7 +619,7 @@ impl QueueState {
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 @@ -658,6 +664,7 @@ impl QueueState {
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 @@ -806,6 +813,7 @@ impl QueueStateT for QueueState {
let addr = self.avail_ring.unchecked_add(2);

mem.load(addr, order)
.map(u16::from_le)
.map(Wrapping)
.map_err(Error::GuestMemory)
}
Expand All @@ -828,13 +836,13 @@ impl QueueStateT for QueueState {
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 39b7896

Please sign in to comment.