Skip to main content

virtio_drivers/device/
virtio_9p.rs

1//! Driver for VirtIO 9p devices.
2
3use alloc::string::String;
4use alloc::vec::Vec;
5use log::warn;
6
7use super::common::Feature;
8use crate::{Error, Hal, Result, queue::VirtQueue, transport::Transport};
9
10const QUEUE: u16 = 0;
11const QUEUE_SIZE: usize = 16;
12const P9_HEADER_SIZE: usize = 7; // size (4) + type (1) + tag (2)
13const SUPPORTED_FEATURES: Feature = Feature::RING_INDIRECT_DESC
14    .union(Feature::RING_EVENT_IDX)
15    .union(Feature::VERSION_1);
16
17/// Driver for a VirtIO 9p device.
18pub struct VirtIO9p<H: Hal, T: Transport> {
19    transport: T,
20    queue: VirtQueue<H, QUEUE_SIZE>,
21    mount_tag: String,
22}
23
24impl<H: Hal, T: Transport> VirtIO9p<H, T> {
25    /// Create a new VirtIO 9p driver.
26    pub fn new(mut transport: T) -> Result<Self> {
27        let features = transport.begin_init(SUPPORTED_FEATURES);
28
29        let queue = VirtQueue::new(
30            &mut transport,
31            QUEUE,
32            features.contains(Feature::RING_INDIRECT_DESC),
33            features.contains(Feature::RING_EVENT_IDX),
34        )?;
35        transport.finish_init();
36
37        let mount_tag = read_mount_tag(&transport)?;
38
39        Ok(Self {
40            transport,
41            queue,
42            mount_tag,
43        })
44    }
45
46    /// Returns the mount tag reported by the device.
47    pub fn mount_tag(&self) -> &str {
48        &self.mount_tag
49    }
50
51    /// Sends a raw 9p request and waits for the response.
52    pub fn request(&mut self, req: &[u8], resp: &mut [u8]) -> Result<u32> {
53        if req.is_empty() || resp.len() < P9_HEADER_SIZE {
54            return Err(Error::InvalidParam);
55        }
56        let used_len = self
57            .queue
58            .add_notify_wait_pop(&[req], &mut [resp], &mut self.transport)?;
59
60        let size = u32::from_le_bytes([resp[0], resp[1], resp[2], resp[3]]);
61        if size != used_len {
62            warn!(
63                "virtio-9p response size mismatch: size from header is {size} but used length is {used_len}"
64            );
65            return Err(Error::IoError);
66        }
67        Ok(used_len)
68    }
69}
70
71fn read_mount_tag<T: Transport>(transport: &T) -> Result<String> {
72    transport.read_consistent(|| {
73        let tag_len: u16 = transport.read_config_space(0)?;
74        if tag_len == 0 {
75            return Err(Error::InvalidParam);
76        }
77
78        let mut bytes = Vec::with_capacity(tag_len as usize);
79        for idx in 0..tag_len as usize {
80            let b: u8 = transport.read_config_space(2 + idx)?;
81            bytes.push(b);
82        }
83
84        Ok(String::from_utf8(bytes)?)
85    })
86}