Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions examples/aarch64/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,9 @@ qemu: $(kernel_qemu_bin) $(img) $(vsock_server_bin)
-nic none \
-drive file=$(img),if=none,format=raw,id=x0 \
-device vhost-vsock-device,id=virtiosocket0,guest-cid=102 \
-fsdev local,id=fsdev0,path=$(PWD),security_model=none \
-device virtio-blk-device,drive=x0 \
-device virtio-9p-device,fsdev=fsdev0,mount_tag=p9_tmp \
-device virtio-rng-device \
-device virtio-gpu-device \
-device virtio-serial,id=virtio-serial0 \
Expand Down
66 changes: 66 additions & 0 deletions examples/aarch64/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ use virtio_drivers::{
socket::{
VirtIOSocket, VsockAddr, VsockConnectionManager, VsockEventType, VMADDR_CID_HOST,
},
virtio_9p::VirtIO9p,
},
transport::{
mmio::{MmioTransport, VirtIOHeader},
Expand Down Expand Up @@ -213,6 +214,7 @@ fn virtio_device(transport: impl Transport) {
Ok(()) => info!("virtio-socket test finished successfully"),
Err(e) => error!("virtio-socket test finished with error '{e:?}'"),
},
DeviceType::_9P => virtio_9p(transport),
DeviceType::EntropySource => virtio_rng(transport),
t => warn!("Unrecognized virtio device: {:?}", t),
}
Expand Down Expand Up @@ -244,6 +246,70 @@ fn virtio_blk<T: Transport>(transport: T) {
info!("virtio-blk test finished");
}

fn virtio_9p<T: Transport>(transport: T) {
let mut p9 = VirtIO9p::<HalImpl, T>::new(transport).expect("failed to create 9p driver");
info!("virtio-9p mount tag: {}", p9.mount_tag());

let mut req = [0u8; 128];
let mut resp = [0u8; 256];
let req_len = build_tversion_request(&mut req, 16384, "9P2000.L")
.expect("failed to build 9p Tversion request");
let resp_len = p9
.request(&req[..req_len], &mut resp)
.expect("failed to send 9p request");
parse_rversion_response(&resp[..resp_len]).expect("invalid 9p Rversion response");
info!("virtio-9p test finished");
}

fn build_tversion_request(buf: &mut [u8], msize: u32, version: &str) -> Option<usize> {
const TVERSION: u8 = 100;
const NOTAG: u16 = 0xffff;

let vbytes = version.as_bytes();
let size = 4 + 1 + 2 + 4 + 2 + vbytes.len();
if buf.len() < size || vbytes.len() > u16::MAX as usize {
return None;
}

buf[0..4].copy_from_slice(&(size as u32).to_le_bytes());
buf[4] = TVERSION;
buf[5..7].copy_from_slice(&NOTAG.to_le_bytes());
buf[7..11].copy_from_slice(&msize.to_le_bytes());
buf[11..13].copy_from_slice(&(vbytes.len() as u16).to_le_bytes());
buf[13..13 + vbytes.len()].copy_from_slice(vbytes);
Some(size)
}

fn parse_rversion_response(resp: &[u8]) -> Option<()> {
const RVERSION: u8 = 101;
const NOTAG: u16 = 0xffff;

if resp.len() < 13 {
return None;
}

let size = u32::from_le_bytes([resp[0], resp[1], resp[2], resp[3]]) as usize;
if size > resp.len() {
return None;
}
if resp[4] != RVERSION {
return None;
}
if u16::from_le_bytes([resp[5], resp[6]]) != NOTAG {
return None;
}

let msize = u32::from_le_bytes([resp[7], resp[8], resp[9], resp[10]]);
let name_len = u16::from_le_bytes([resp[11], resp[12]]) as usize;
let name_end = 13 + name_len;
if name_end > size {
return None;
}
let version = core::str::from_utf8(&resp[13..name_end]).ok()?;
info!("virtio-9p rversion: msize={}, version={}", msize, version);
Some(())
}

fn virtio_gpu<T: Transport>(transport: T) {
let mut gpu = VirtIOGpu::<HalImpl, T>::new(transport).expect("failed to create gpu driver");
let (width, height) = gpu.resolution().expect("failed to get resolution");
Expand Down
4 changes: 4 additions & 0 deletions examples/riscv/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,10 @@ qemu-legacy: kernel $(img)
-serial mon:stdio \
-bios default \
-kernel $(kernel) \
-fsdev local,id=fsdev0,path=$(CURDIR),security_model=none \
-drive file=$(img),if=none,format=raw,id=x0 \
-device virtio-blk-device,drive=x0 \
-device virtio-9p-device,fsdev=fsdev0,mount_tag=p9_tmp \
-device virtio-rng-device \
-device virtio-gpu-device \
-device virtio-mouse-device \
Expand All @@ -73,8 +75,10 @@ qemu: kernel $(img)
-bios default \
-kernel $(kernel) \
-global virtio-mmio.force-legacy=false \
-fsdev local,id=fsdev0,path=$(CURDIR),security_model=none \
-drive file=$(img),if=none,format=raw,id=x0 \
-device virtio-blk-device,drive=x0 \
-device virtio-9p-device,fsdev=fsdev0,mount_tag=p9_tmp \
-device virtio-rng-device \
-device virtio-gpu-device \
-device virtio-mouse-device \
Expand Down
66 changes: 66 additions & 0 deletions examples/riscv/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ use virtio_drivers::{
input::VirtIOInput,
rng::VirtIORng,
sound::{PcmFormat, PcmRate, VirtIOSound},
virtio_9p::VirtIO9p,
},
transport::{
mmio::{MmioTransport, VirtIOHeader},
Expand Down Expand Up @@ -92,6 +93,7 @@ fn virtio_device(transport: impl Transport) {
DeviceType::Input => virtio_input(transport),
DeviceType::Network => virtio_net(transport),
DeviceType::Sound => virtio_sound(transport),
DeviceType::_9P => virtio_9p(transport),
DeviceType::EntropySource => virtio_rng(transport),
t => warn!("Unrecognized virtio device: {:?}", t),
}
Expand Down Expand Up @@ -122,6 +124,70 @@ fn virtio_blk<T: Transport>(transport: T) {
info!("virtio-blk test finished");
}

fn virtio_9p<T: Transport>(transport: T) {
let mut p9 = VirtIO9p::<HalImpl, T>::new(transport).expect("failed to create 9p driver");
info!("virtio-9p mount tag: {}", p9.mount_tag());

let mut req = [0u8; 128];
let mut resp = [0u8; 256];
let req_len = build_tversion_request(&mut req, 16384, "9P2000.L")
.expect("failed to build 9p Tversion request");
let resp_len = p9
.request(&req[..req_len], &mut resp)
.expect("failed to send 9p request");
parse_rversion_response(&resp[..resp_len]).expect("invalid 9p Rversion response");
info!("virtio-9p test finished");
}

fn build_tversion_request(buf: &mut [u8], msize: u32, version: &str) -> Option<usize> {
const TVERSION: u8 = 100;
const NOTAG: u16 = 0xffff;

let vbytes = version.as_bytes();
let size = 4 + 1 + 2 + 4 + 2 + vbytes.len();
if buf.len() < size || vbytes.len() > u16::MAX as usize {
return None;
}

buf[0..4].copy_from_slice(&(size as u32).to_le_bytes());
buf[4] = TVERSION;
buf[5..7].copy_from_slice(&NOTAG.to_le_bytes());
buf[7..11].copy_from_slice(&msize.to_le_bytes());
buf[11..13].copy_from_slice(&(vbytes.len() as u16).to_le_bytes());
buf[13..13 + vbytes.len()].copy_from_slice(vbytes);
Some(size)
}

fn parse_rversion_response(resp: &[u8]) -> Option<()> {
const RVERSION: u8 = 101;
const NOTAG: u16 = 0xffff;

if resp.len() < 13 {
return None;
}

let size = u32::from_le_bytes([resp[0], resp[1], resp[2], resp[3]]) as usize;
if size > resp.len() {
return None;
}
if resp[4] != RVERSION {
return None;
}
if u16::from_le_bytes([resp[5], resp[6]]) != NOTAG {
return None;
}

let msize = u32::from_le_bytes([resp[7], resp[8], resp[9], resp[10]]);
let name_len = u16::from_le_bytes([resp[11], resp[12]]) as usize;
let name_end = 13 + name_len;
if name_end > size {
return None;
}
let version = core::str::from_utf8(&resp[13..name_end]).ok()?;
info!("virtio-9p rversion: msize={}, version={}", msize, version);
Some(())
}

fn virtio_gpu<T: Transport>(transport: T) {
let mut gpu = VirtIOGpu::<HalImpl, T>::new(transport).expect("failed to create gpu driver");
let (width, height) = gpu.resolution().expect("failed to get resolution");
Expand Down
2 changes: 2 additions & 0 deletions examples/x86_64/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,10 @@ QEMU_ARGS += \
-machine q35 \
-serial mon:stdio \
-kernel $(kernel) \
-fsdev local,id=fsdev0,path=$(PWD),security_model=none \
-device virtio-gpu-pci -vga none \
-device virtio-blk-pci,drive=x0 -drive file=$(img),if=none,format=raw,id=x0 \
-device virtio-9p-pci,fsdev=fsdev0,mount_tag=p9_tmp \
-device virtio-rng-pci \
-device virtio-net-pci,netdev=net0 -netdev user,id=net0,hostfwd=tcp::5555-:5555

Expand Down
67 changes: 66 additions & 1 deletion examples/x86_64/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ mod tcp;

use self::hal::HalImpl;
use virtio_drivers::{
device::{blk::VirtIOBlk, gpu::VirtIOGpu, rng::VirtIORng},
device::{blk::VirtIOBlk, gpu::VirtIOGpu, rng::VirtIORng, virtio_9p::VirtIO9p},
transport::{
pci::{
bus::{BarInfo, Cam, Command, ConfigurationAccess, DeviceFunction, MmioCam, PciRoot},
Expand Down Expand Up @@ -65,6 +65,7 @@ fn virtio_device(transport: impl Transport) {
DeviceType::Block => virtio_blk(transport),
DeviceType::GPU => virtio_gpu(transport),
DeviceType::Network => virtio_net(transport),
DeviceType::_9P => virtio_9p(transport),
DeviceType::EntropySource => virtio_rng(transport),
t => warn!("Unrecognized virtio device: {:?}", t),
}
Expand Down Expand Up @@ -96,6 +97,70 @@ fn virtio_blk<T: Transport>(transport: T) {
info!("virtio-blk test finished");
}

fn virtio_9p<T: Transport>(transport: T) {
let mut p9 = VirtIO9p::<HalImpl, T>::new(transport).expect("failed to create 9p driver");
info!("virtio-9p mount tag: {}", p9.mount_tag());

let mut req = [0u8; 128];
let mut resp = [0u8; 256];
let req_len = build_tversion_request(&mut req, 16384, "9P2000.L")
.expect("failed to build 9p Tversion request");
let resp_len = p9
.request(&req[..req_len], &mut resp)
.expect("failed to send 9p request");
parse_rversion_response(&resp[..resp_len]).expect("invalid 9p Rversion response");
info!("virtio-9p test finished");
}

fn build_tversion_request(buf: &mut [u8], msize: u32, version: &str) -> Option<usize> {
const TVERSION: u8 = 100;
const NOTAG: u16 = 0xffff;

let vbytes = version.as_bytes();
let size = 4 + 1 + 2 + 4 + 2 + vbytes.len();
if buf.len() < size || vbytes.len() > u16::MAX as usize {
return None;
}

buf[0..4].copy_from_slice(&(size as u32).to_le_bytes());
buf[4] = TVERSION;
buf[5..7].copy_from_slice(&NOTAG.to_le_bytes());
buf[7..11].copy_from_slice(&msize.to_le_bytes());
buf[11..13].copy_from_slice(&(vbytes.len() as u16).to_le_bytes());
buf[13..13 + vbytes.len()].copy_from_slice(vbytes);
Some(size)
}

fn parse_rversion_response(resp: &[u8]) -> Option<()> {
const RVERSION: u8 = 101;
const NOTAG: u16 = 0xffff;

if resp.len() < 13 {
return None;
}

let size = u32::from_le_bytes([resp[0], resp[1], resp[2], resp[3]]) as usize;
if size > resp.len() {
return None;
}
if resp[4] != RVERSION {
return None;
}
if u16::from_le_bytes([resp[5], resp[6]]) != NOTAG {
return None;
}

let msize = u32::from_le_bytes([resp[7], resp[8], resp[9], resp[10]]);
let name_len = u16::from_le_bytes([resp[11], resp[12]]) as usize;
let name_end = 13 + name_len;
if name_end > size {
return None;
}
let version = core::str::from_utf8(&resp[13..name_end]).ok()?;
info!("virtio-9p rversion: msize={}, version={}", msize, version);
Some(())
}

fn virtio_gpu<T: Transport>(transport: T) {
let mut gpu = VirtIOGpu::<HalImpl, T>::new(transport).expect("failed to create gpu driver");
let (width, height) = gpu.resolution().expect("failed to get resolution");
Expand Down
2 changes: 2 additions & 0 deletions src/device/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,7 @@ pub mod rng;
pub mod socket;
#[cfg(feature = "alloc")]
pub mod sound;
#[cfg(feature = "alloc")]
pub mod virtio_9p;

pub(crate) mod common;
81 changes: 81 additions & 0 deletions src/device/virtio_9p.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
//! Driver for VirtIO 9p devices.

use alloc::string::String;
use alloc::vec::Vec;
use log::debug;

use super::common::Feature;
use crate::{queue::VirtQueue, transport::Transport, Error, Hal, Result};

const QUEUE: u16 = 0;
const QUEUE_SIZE: usize = 16;
const SUPPORTED_FEATURES: Feature = Feature::RING_INDIRECT_DESC
.union(Feature::RING_EVENT_IDX)
.union(Feature::VERSION_1);

/// Driver for a VirtIO 9p device.
pub struct VirtIO9p<H: Hal, T: Transport> {
transport: T,
queue: VirtQueue<H, QUEUE_SIZE>,
mount_tag: String,
}

impl<H: Hal, T: Transport> VirtIO9p<H, T> {
/// Create a new VirtIO 9p driver.
pub fn new(mut transport: T) -> Result<Self> {
let features = transport.begin_init(SUPPORTED_FEATURES);

let queue = VirtQueue::new(
&mut transport,
QUEUE,
features.contains(Feature::RING_INDIRECT_DESC),
features.contains(Feature::RING_EVENT_IDX),
)?;
transport.finish_init();

let mount_tag = read_mount_tag(&transport).ok_or(Error::InvalidParam)?;

Ok(Self {
transport,
queue,
mount_tag,
})
}

/// Returns the mount tag reported by the device.
pub fn mount_tag(&self) -> &str {
&self.mount_tag
}

/// Sends a raw 9p request and waits for the response.
pub fn request(&mut self, req: &[u8], resp: &mut [u8]) -> Result<usize> {
if req.is_empty() || resp.len() < 7 {
return Err(Error::InvalidParam);
}
let used_len = self
.queue
.add_notify_wait_pop(&[req], &mut [resp], &mut self.transport)?;

let size = u32::from_le_bytes([resp[0], resp[1], resp[2], resp[3]]) as usize;
debug!(
"virtio-9p resp sizes: used_len={}, payload_len={}",
used_len, size
);
Ok(size.min(resp.len()))
}
}

fn read_mount_tag<T: Transport>(transport: &T) -> Option<String> {
let tag_len: u16 = transport.read_config_space(0).ok()?;
if tag_len == 0 {
return None;
}

let mut bytes = Vec::with_capacity(tag_len as usize);
for idx in 0..tag_len as usize {
let b: u8 = transport.read_config_space(2 + idx).ok()?;
bytes.push(b);
}

String::from_utf8(bytes).ok()
}
Loading
Loading