Browse Source

Add protocols to support virtio socket device

With this change we can read the configuration space of a mounted
virtio socket device by MMIO or PCI bus. The connection to the host
still fails with the response VIRTIO_VSOCK_OP_RST.

I'll work on the virtqueue flows in subsequent changes.
Alice Wang 2 năm trước cách đây
mục cha
commit
31429fbcda

+ 6 - 4
examples/aarch64/Makefile

@@ -51,15 +51,16 @@ qemu: $(kernel_qemu_bin) $(img)
 	  $(QEMU_ARGS) \
 		-machine virt \
 		-cpu max \
-		-serial chardev:char0 \
+		-nographic \
 		-kernel $(kernel_qemu_bin) \
 		-global virtio-mmio.force-legacy=false \
 		-nic none \
 		-drive file=$(img),if=none,format=raw,id=x0 \
+		-device vhost-vsock-pci,id=virtiosocket0,guest-cid=102 \
 		-device virtio-blk-device,drive=x0 \
 		-device virtio-gpu-device \
 		-device virtio-serial,id=virtio-serial0 \
-		-chardev stdio,id=char0,mux=on \
+		-chardev pty,id=char0 \
 		-device virtconsole,chardev=char0
 
 qemu-pci: $(kernel_qemu_bin) $(img)
@@ -67,14 +68,15 @@ qemu-pci: $(kernel_qemu_bin) $(img)
 	  $(QEMU_ARGS) \
 		-machine virt \
 		-cpu max \
-		-serial chardev:char0 \
+		-nographic \
 		-kernel $(kernel_qemu_bin) \
 		-nic none \
 		-drive file=$(img),if=none,format=raw,id=x0 \
+		-device vhost-vsock-pci,id=virtiosocket0,guest-cid=103 \
 		-device virtio-blk-pci,drive=x0 \
 		-device virtio-gpu-pci \
 		-device virtio-serial,id=virtio-serial0 \
-		-chardev stdio,id=char0,mux=on \
+		-chardev pty,id=char0 \
 		-device virtconsole,chardev=char0
 
 crosvm: $(kernel_crosvm_bin) $(img)

+ 23 - 0
examples/aarch64/socket_test_host.py

@@ -0,0 +1,23 @@
+import socket
+
+HOST = 'localhost'
+PORT = 1234
+
+def main():
+    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
+        s.bind((HOST, PORT))
+        s.listen()
+        print(f"Server listening on {HOST}:{PORT}")
+        conn, addr = s.accept()
+        with conn:
+            print(f"Connected by {addr}")
+            while True:
+                conn.sendall(b"start vsock aaaa")
+                data = conn.recv(1024)
+                if not data:
+                    break
+                print(f"Received: {data.decode('utf-8')}")
+                conn.sendall(data)
+
+if __name__ == '__main__':
+    main()

+ 13 - 1
examples/aarch64/src/main.rs

@@ -23,7 +23,7 @@ use hal::HalImpl;
 use log::{debug, error, info, trace, warn, LevelFilter};
 use psci::system_off;
 use virtio_drivers::{
-    device::{blk::VirtIOBlk, console::VirtIOConsole, gpu::VirtIOGpu},
+    device::{blk::VirtIOBlk, console::VirtIOConsole, gpu::VirtIOGpu, socket::VirtIOSocket},
     transport::{
         mmio::{MmioTransport, VirtIOHeader},
         pci::{
@@ -116,6 +116,7 @@ fn virtio_device(transport: impl Transport) {
         DeviceType::GPU => virtio_gpu(transport),
         // DeviceType::Network => virtio_net(transport), // currently is unsupported without alloc
         DeviceType::Console => virtio_console(transport),
+        DeviceType::Socket => virtio_socket(transport),
         t => warn!("Unrecognized virtio device: {:?}", t),
     }
 }
@@ -178,6 +179,17 @@ fn virtio_console<T: Transport>(transport: T) {
     info!("virtio-console test finished");
 }
 
+fn virtio_socket<T: Transport>(transport: T) {
+    let mut socket =
+        VirtIOSocket::<HalImpl, T>::new(transport).expect("Failed to create socket driver");
+    let host_cid = 2;
+    let port = 1234;
+    if let Err(e) = socket.connect(host_cid, port, port) {
+        error!("Failed to connect to host: {:?}", e);
+    }
+    info!("VirtIO socket test finished");
+}
+
 #[derive(Copy, Clone, Debug, Eq, PartialEq)]
 enum PciRangeType {
     ConfigurationSpace,

+ 23 - 0
src/device/common.rs

@@ -0,0 +1,23 @@
+//! Common part shared across all the devices.
+
+use bitflags::bitflags;
+
+bitflags! {
+    pub(crate) struct Feature: u64 {
+        // device independent
+        const NOTIFY_ON_EMPTY       = 1 << 24; // legacy
+        const ANY_LAYOUT            = 1 << 27; // legacy
+        const RING_INDIRECT_DESC    = 1 << 28;
+        const RING_EVENT_IDX        = 1 << 29;
+        const UNUSED                = 1 << 30; // legacy
+        const VERSION_1             = 1 << 32; // detect legacy
+
+        // since virtio v1.1
+        const ACCESS_PLATFORM       = 1 << 33;
+        const RING_PACKED           = 1 << 34;
+        const IN_ORDER              = 1 << 35;
+        const ORDER_PLATFORM        = 1 << 36;
+        const SR_IOV                = 1 << 37;
+        const NOTIFICATION_DATA     = 1 << 38;
+    }
+}

+ 1 - 21
src/device/input.rs

@@ -1,12 +1,12 @@
 //! Driver for VirtIO input devices.
 
+use super::common::Feature;
 use crate::hal::Hal;
 use crate::queue::VirtQueue;
 use crate::transport::Transport;
 use crate::volatile::{volread, volwrite, ReadOnly, WriteOnly};
 use crate::Result;
 use alloc::boxed::Box;
-use bitflags::bitflags;
 use core::ptr::NonNull;
 use log::info;
 use zerocopy::{AsBytes, FromBytes};
@@ -191,26 +191,6 @@ pub struct InputEvent {
     pub value: u32,
 }
 
-bitflags! {
-    struct Feature: u64 {
-        // device independent
-        const NOTIFY_ON_EMPTY       = 1 << 24; // legacy
-        const ANY_LAYOUT            = 1 << 27; // legacy
-        const RING_INDIRECT_DESC    = 1 << 28;
-        const RING_EVENT_IDX        = 1 << 29;
-        const UNUSED                = 1 << 30; // legacy
-        const VERSION_1             = 1 << 32; // detect legacy
-
-        // since virtio v1.1
-        const ACCESS_PLATFORM       = 1 << 33;
-        const RING_PACKED           = 1 << 34;
-        const IN_ORDER              = 1 << 35;
-        const ORDER_PLATFORM        = 1 << 36;
-        const SR_IOV                = 1 << 37;
-        const NOTIFICATION_DATA     = 1 << 38;
-    }
-}
-
 const QUEUE_EVENT: u16 = 0;
 const QUEUE_STATUS: u16 = 1;
 

+ 3 - 0
src/device/mod.rs

@@ -7,3 +7,6 @@ pub mod gpu;
 pub mod input;
 #[cfg(feature = "alloc")]
 pub mod net;
+pub mod socket;
+
+pub(crate) mod common;

+ 39 - 0
src/device/socket/error.rs

@@ -0,0 +1,39 @@
+//! This module contain the error from the VirtIO socket driver.
+
+use core::{fmt, result};
+
+/// The error type of VirtIO socket driver.
+#[derive(Copy, Clone, Debug, Eq, PartialEq)]
+pub enum SocketError {
+    /// Failed to establish the connection.
+    ConnectionFailed,
+    /// No response received.
+    NoResponseReceived,
+    /// The given buffer is shorter than expected.
+    BufferTooShort,
+    /// Failed to parse the VirtioVsockPacket from buffer.
+    PacketParsingFailed,
+    /// Unknown operation.
+    UnknownOperation(u16),
+    /// Invalid opration,
+    InvalidOperation,
+}
+
+impl fmt::Display for SocketError {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        match self {
+            Self::ConnectionFailed => write!(f, "Failed to establish the connection"),
+            Self::NoResponseReceived => write!(f, "No response received"),
+            Self::BufferTooShort => write!(f, "The given buffer is shorter than expected"),
+            Self::PacketParsingFailed => {
+                write!(f, "Failed to parse the VirtioVsockPacket from buffer")
+            }
+            Self::UnknownOperation(op) => {
+                write!(f, "The operation code '{op}' is unknown")
+            }
+            Self::InvalidOperation => write!(f, "Invalid operation"),
+        }
+    }
+}
+
+pub type Result<T> = result::Result<T, SocketError>;

+ 10 - 0
src/device/socket/mod.rs

@@ -0,0 +1,10 @@
+//! This module implements the virtio vsock device.
+
+mod error;
+mod protocol;
+mod vsock;
+
+use super::common;
+
+pub use error::SocketError;
+pub use vsock::VirtIOSocket;

+ 147 - 0
src/device/socket/protocol.rs

@@ -0,0 +1,147 @@
+//! This module defines the socket device protocol according to the virtio spec 5.10 Socket Device
+
+use super::error::{self, SocketError};
+use crate::endian::{Le16, Le32, Le64};
+use crate::volatile::ReadOnly;
+use core::convert::TryInto;
+use core::{convert::TryFrom, fmt, mem::size_of};
+use zerocopy::{AsBytes, FromBytes};
+
+pub const TYPE_STREAM_SOCKET: u16 = 1;
+
+/// VirtioVsockConfig is the vsock device configuration space.
+#[repr(C)]
+pub struct VirtioVsockConfig {
+    /// The guest_cid field contains the guest’s context ID, which uniquely identifies
+    /// the device for its lifetime. The upper 32 bits of the CID are reserved and zeroed.
+    ///
+    /// We need to split the guest_cid into two parts because VirtIO only guarantees 4 bytes alignment.
+    pub guest_cid_low: ReadOnly<u32>,
+    pub _guest_cid_high: ReadOnly<u32>,
+}
+
+/// The message header for data packets sent on the tx/rx queues
+#[repr(packed)]
+#[derive(AsBytes, Clone, Copy, Debug, FromBytes)]
+pub struct VirtioVsockHdr {
+    pub src_cid: Le64,
+    pub dst_cid: Le64,
+    pub src_port: Le32,
+    pub dst_port: Le32,
+    pub len: Le32,
+    pub r#type: Le16,
+    pub op: Le16,
+    pub flags: Le32,
+    pub buf_alloc: Le32,
+    pub fwd_cnt: Le32,
+}
+
+impl Default for VirtioVsockHdr {
+    fn default() -> Self {
+        Self {
+            src_cid: Le64::default(),
+            dst_cid: Le64::default(),
+            src_port: Le32::default(),
+            dst_port: Le32::default(),
+            len: Le32::default(),
+            r#type: TYPE_STREAM_SOCKET.into(),
+            op: Le16::default(),
+            flags: Le32::default(),
+            buf_alloc: Le32::default(),
+            fwd_cnt: Le32::default(),
+        }
+    }
+}
+
+#[derive(Clone, Debug)]
+pub struct VirtioVsockPacket<'a> {
+    pub hdr: VirtioVsockHdr,
+    pub data: &'a [u8],
+}
+
+impl<'a> VirtioVsockPacket<'a> {
+    pub fn read_from(buffer: &'a [u8]) -> Result<Self, SocketError> {
+        let hdr = buffer
+            .get(0..size_of::<VirtioVsockHdr>())
+            .ok_or(SocketError::BufferTooShort)?;
+        let hdr = VirtioVsockHdr::read_from(hdr).ok_or(SocketError::PacketParsingFailed)?;
+        let data_end = size_of::<VirtioVsockHdr>() + (hdr.len.to_native() as usize);
+        let data = buffer
+            .get(size_of::<VirtioVsockHdr>()..data_end)
+            .ok_or(SocketError::BufferTooShort)?;
+        Ok(Self { hdr, data })
+    }
+
+    pub fn op(&self) -> error::Result<Op> {
+        self.hdr.op.try_into()
+    }
+}
+
+/// An event sent to the event queue
+#[derive(Copy, Clone, Debug, Default, AsBytes, FromBytes)]
+#[repr(C)]
+pub struct VirtioVsockEvent {
+    // ID from the virtio_vsock_event_id struct in the virtio spec
+    pub id: Le32,
+}
+
+#[allow(non_camel_case_types)]
+#[derive(Copy, Clone, Eq, PartialEq)]
+#[repr(u16)]
+pub enum Op {
+    VIRTIO_VSOCK_OP_INVALID = 0,
+
+    /* Connect operations */
+    VIRTIO_VSOCK_OP_REQUEST = 1,
+    VIRTIO_VSOCK_OP_RESPONSE = 2,
+    VIRTIO_VSOCK_OP_RST = 3,
+    VIRTIO_VSOCK_OP_SHUTDOWN = 4,
+
+    /* To send payload */
+    VIRTIO_VSOCK_OP_RW = 5,
+
+    /* Tell the peer our credit info */
+    VIRTIO_VSOCK_OP_CREDIT_UPDATE = 6,
+    /* Request the peer to send the credit info to us */
+    VIRTIO_VSOCK_OP_CREDIT_REQUEST = 7,
+}
+
+impl Into<Le16> for Op {
+    fn into(self) -> Le16 {
+        Le16::from(self as u16)
+    }
+}
+
+impl TryFrom<Le16> for Op {
+    type Error = SocketError;
+
+    fn try_from(v: Le16) -> Result<Self, Self::Error> {
+        let op = match u16::from(v) {
+            0 => Self::VIRTIO_VSOCK_OP_INVALID,
+            1 => Self::VIRTIO_VSOCK_OP_REQUEST,
+            2 => Self::VIRTIO_VSOCK_OP_RESPONSE,
+            3 => Self::VIRTIO_VSOCK_OP_RST,
+            4 => Self::VIRTIO_VSOCK_OP_SHUTDOWN,
+            5 => Self::VIRTIO_VSOCK_OP_RW,
+            6 => Self::VIRTIO_VSOCK_OP_CREDIT_UPDATE,
+            7 => Self::VIRTIO_VSOCK_OP_CREDIT_REQUEST,
+            _ => return Err(SocketError::UnknownOperation(v.into())),
+        };
+        Ok(op)
+    }
+}
+
+impl fmt::Debug for Op {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        match self {
+            Self::VIRTIO_VSOCK_OP_INVALID => write!(f, "VIRTIO_VSOCK_OP_INVALID"),
+            Self::VIRTIO_VSOCK_OP_REQUEST => write!(f, "VIRTIO_VSOCK_OP_REQUEST"),
+            Self::VIRTIO_VSOCK_OP_RESPONSE => write!(f, "VIRTIO_VSOCK_OP_RESPONSE"),
+            Self::VIRTIO_VSOCK_OP_RST => write!(f, "VIRTIO_VSOCK_OP_RST"),
+            Self::VIRTIO_VSOCK_OP_SHUTDOWN => write!(f, "VIRTIO_VSOCK_OP_SHUTDOWN"),
+            Self::VIRTIO_VSOCK_OP_RW => write!(f, "VIRTIO_VSOCK_OP_RW"),
+            Self::VIRTIO_VSOCK_OP_CREDIT_UPDATE => write!(f, "VIRTIO_VSOCK_OP_CREDIT_UPDATE"),
+            Self::VIRTIO_VSOCK_OP_CREDIT_REQUEST => write!(f, "VIRTIO_VSOCK_OP_CREDIT_REQUEST"),
+        }
+    }
+}

+ 170 - 0
src/device/socket/vsock.rs

@@ -0,0 +1,170 @@
+//! Driver for VirtIO socket devices.
+
+use super::common::Feature;
+use super::error::SocketError;
+use super::protocol::{Op, VirtioVsockConfig, VirtioVsockHdr, VirtioVsockPacket};
+use crate::hal::{BufferDirection, Dma, Hal};
+use crate::queue::VirtQueue;
+use crate::transport::Transport;
+use crate::volatile::volread;
+use crate::Result;
+use log::{error, info};
+use zerocopy::AsBytes;
+
+const RX_QUEUE_IDX: u16 = 0;
+const TX_QUEUE_IDX: u16 = 1;
+const EVENT_QUEUE_IDX: u16 = 2;
+
+const QUEUE_SIZE: usize = 2;
+
+/// Driver for a VirtIO socket device.
+pub struct VirtIOSocket<'a, H: Hal, T: Transport> {
+    transport: T,
+    /// Virtqueue to receive packets.
+    rx: VirtQueue<H, { QUEUE_SIZE }>,
+    tx: VirtQueue<H, { QUEUE_SIZE }>,
+    /// Virtqueue to receive events from the device.
+    event: VirtQueue<H, { QUEUE_SIZE }>,
+    /// The guest_cid field contains the guest’s context ID, which uniquely identifies
+    /// the device for its lifetime. The upper 32 bits of the CID are reserved and zeroed.
+    guest_cid: u64,
+    queue_buf_dma: Dma<H>,
+    queue_buf_rx: &'a mut [u8],
+}
+
+impl<'a, H: Hal, T: Transport> Drop for VirtIOSocket<'a, H, T> {
+    fn drop(&mut self) {
+        // Clear any pointers pointing to DMA regions, so the device doesn't try to access them
+        // after they have been freed.
+        self.transport.queue_unset(RX_QUEUE_IDX);
+        self.transport.queue_unset(TX_QUEUE_IDX);
+        self.transport.queue_unset(EVENT_QUEUE_IDX);
+    }
+}
+
+impl<'a, H: Hal, T: Transport> VirtIOSocket<'a, H, T> {
+    /// Create a new VirtIO Vsock driver.
+    pub fn new(mut transport: T) -> Result<Self> {
+        transport.begin_init(|features| {
+            let features = Feature::from_bits_truncate(features);
+            info!("Device features: {:?}", features);
+            // negotiate these flags only
+            let supported_features = Feature::empty();
+            (features & supported_features).bits()
+        });
+
+        let config = transport.config_space::<VirtioVsockConfig>()?;
+        info!("config: {:?}", config);
+        // Safe because config is a valid pointer to the device configuration space.
+        let guest_cid = unsafe { volread!(config, guest_cid_low) as u64 };
+        info!("guest cid: {guest_cid:?}");
+
+        let mut rx = VirtQueue::new(&mut transport, RX_QUEUE_IDX)?;
+        let tx = VirtQueue::new(&mut transport, TX_QUEUE_IDX)?;
+        let event = VirtQueue::new(&mut transport, EVENT_QUEUE_IDX)?;
+
+        let queue_buf_dma = Dma::new(1, BufferDirection::DeviceToDriver)?;
+
+        // Safe because no alignment or initialisation is required for [u8], the DMA buffer is
+        // dereferenceable, and the lifetime of the reference matches the lifetime of the DMA buffer
+        // (which we don't otherwise access).
+        let queue_buf_rx = unsafe { queue_buf_dma.raw_slice().as_mut() };
+
+        // Safe because the buffer lives as long as the queue.
+        let _token = unsafe { rx.add(&[], &mut [queue_buf_rx])? };
+
+        if rx.should_notify() {
+            transport.notify(RX_QUEUE_IDX);
+        }
+        transport.finish_init();
+
+        Ok(Self {
+            transport,
+            rx,
+            tx,
+            event,
+            guest_cid,
+            queue_buf_dma,
+            queue_buf_rx,
+        })
+    }
+
+    /// Connect to the destination.
+    pub fn connect(&mut self, dst_cid: u64, src_port: u32, dst_port: u32) -> Result {
+        let header = VirtioVsockHdr {
+            src_cid: self.guest_cid.into(),
+            dst_cid: dst_cid.into(),
+            src_port: src_port.into(),
+            dst_port: dst_port.into(),
+            op: Op::VIRTIO_VSOCK_OP_REQUEST.into(),
+            ..Default::default()
+        };
+        self.tx
+            .add_notify_wait_pop(&[header.as_bytes(), &[]], &mut [], &mut self.transport)?;
+        let token = if let Some(token) = self.rx.peek_used() {
+            token // TODO: Use let else after updating Rust
+        } else {
+            return Err(SocketError::NoResponseReceived.into());
+        };
+        let _len = unsafe {
+            self.rx
+                .pop_used(token, &[], &mut [&mut self.queue_buf_rx])?
+        };
+        let packet_rx = VirtioVsockPacket::read_from(&self.queue_buf_rx)?;
+        let result = match packet_rx.op()? {
+            Op::VIRTIO_VSOCK_OP_RESPONSE => Ok(()),
+            Op::VIRTIO_VSOCK_OP_RST => Err(SocketError::ConnectionFailed.into()),
+            Op::VIRTIO_VSOCK_OP_INVALID => Err(SocketError::InvalidOperation.into()),
+            _ => todo!(),
+        };
+        if result.is_err() {
+            error!(
+                "Connection failed. Packet received: {:?}, op={:?}",
+                packet_rx,
+                packet_rx.op()
+            );
+        }
+        result
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use crate::volatile::ReadOnly;
+    use crate::{
+        hal::fake::FakeHal,
+        transport::{
+            fake::{FakeTransport, QueueStatus, State},
+            DeviceStatus, DeviceType,
+        },
+    };
+    use alloc::{sync::Arc, vec};
+    use core::ptr::NonNull;
+    use std::sync::Mutex;
+
+    #[test]
+    fn config() {
+        let mut config_space = VirtioVsockConfig {
+            guest_cid_low: ReadOnly::new(66),
+            _guest_cid_high: ReadOnly::new(0),
+        };
+        let state = Arc::new(Mutex::new(State {
+            status: DeviceStatus::empty(),
+            driver_features: 0,
+            guest_page_size: 0,
+            interrupt_pending: false,
+            queues: vec![QueueStatus::default(); 3],
+        }));
+        let transport = FakeTransport {
+            device_type: DeviceType::Socket,
+            max_queue_size: 32,
+            device_features: 0,
+            config_space: NonNull::from(&mut config_space),
+            state: state.clone(),
+        };
+        let socket =
+            VirtIOSocket::<FakeHal, FakeTransport<VirtioVsockConfig>>::new(transport).unwrap();
+        assert_eq!(socket.guest_cid, 0x00_0000_0042);
+    }
+}

+ 62 - 0
src/endian.rs

@@ -0,0 +1,62 @@
+//! Endian types used in this library.
+//! Each endian type is guarnteed to have the same size and alignment as a regular unsigned primiive
+//! of the equal size.
+//!
+//! # Examples
+//!
+//! ```
+//! # use endian::*;
+//!   let b = Be32::from(3);
+//!   let l = Le32::from(3);
+//!
+//!   assert_eq!(b.to_native(), 3);
+//!   assert_eq!(l.to_native(), 3);
+//!   assert!(b == 3);
+//!   assert!(l == 3);
+//! ```
+
+use zerocopy::{AsBytes, FromBytes};
+
+macro_rules! endian_type {
+    ($old_type:ident, $new_type:ident, $to_new:ident, $from_new:ident) => {
+        /// An integer type of with an explicit endianness.
+        #[repr(transparent)]
+        #[derive(Copy, Clone, Eq, PartialEq, Debug, Default, FromBytes, AsBytes)]
+        pub struct $new_type($old_type);
+
+        impl $new_type {
+            /// Converts `self` to the native endianness.
+            pub fn to_native(self) -> $old_type {
+                $old_type::$from_new(self.0)
+            }
+        }
+
+        impl PartialEq<$old_type> for $new_type {
+            fn eq(&self, other: &$old_type) -> bool {
+                self.0 == $old_type::$to_new(*other)
+            }
+        }
+
+        impl PartialEq<$new_type> for $old_type {
+            fn eq(&self, other: &$new_type) -> bool {
+                $old_type::$to_new(other.0) == *self
+            }
+        }
+
+        impl From<$new_type> for $old_type {
+            fn from(v: $new_type) -> $old_type {
+                $old_type::$from_new(v.0)
+            }
+        }
+
+        impl From<$old_type> for $new_type {
+            fn from(v: $old_type) -> $new_type {
+                $new_type($old_type::$to_new(v))
+            }
+        }
+    };
+}
+
+endian_type!(u16, Le16, to_le, from_le);
+endian_type!(u32, Le32, to_le, from_le);
+endian_type!(u64, Le64, to_le, from_le);

+ 1 - 0
src/hal/fake.rs

@@ -21,6 +21,7 @@ unsafe impl Hal for FakeHal {
         }
     }
 
+    #[allow(unused_unsafe)]
     unsafe fn dma_dealloc(_paddr: PhysAddr, vaddr: NonNull<u8>, pages: usize) -> i32 {
         assert_ne!(pages, 0);
         let layout = Layout::from_size_align(pages * PAGE_SIZE, PAGE_SIZE).unwrap();

+ 14 - 1
src/lib.rs

@@ -48,6 +48,7 @@
 extern crate alloc;
 
 pub mod device;
+mod endian;
 mod hal;
 mod queue;
 pub mod transport;
@@ -89,6 +90,8 @@ pub enum Error {
     ConfigSpaceTooSmall,
     /// The device doesn't have any config space, but the driver expects some.
     ConfigSpaceMissing,
+    /// Error from the socket device.
+    SocketDeviceError(device::socket::SocketError),
 }
 
 impl Display for Error {
@@ -114,11 +117,21 @@ impl Display for Error {
                     f,
                     "The device doesn't have any config space, but the driver expects some"
                 )
-            }
+            },
+            Self::SocketDeviceError(e) => write!(
+                f,
+                "Error from the socket device: {e:?}"
+            ),
         }
     }
 }
 
+impl From<device::socket::SocketError> for Error {
+    fn from(e: device::socket::SocketError) -> Self {
+        Self::SocketDeviceError(e)
+    }
+}
+
 /// Align `size` up to a page.
 fn align_up(size: usize) -> usize {
     (size + PAGE_SIZE) & !(PAGE_SIZE - 1)

+ 2 - 0
src/queue.rs

@@ -1,3 +1,5 @@
+#![allow(unused_unsafe)]
+
 use crate::hal::{BufferDirection, Dma, Hal, PhysAddr};
 use crate::transport::Transport;
 use crate::{align_up, nonnull_slice_from_raw_parts, pages, Error, Result, PAGE_SIZE};