From ca68ed8bff12cd262e1381ea02fa557b7d9891c7 Mon Sep 17 00:00:00 2001 From: Andrew Walbran Date: Tue, 6 Aug 2024 14:07:13 +0100 Subject: [PATCH 1/4] Replace console_info method with size. console_info wasn't checking supported features before reading the config space, and max_ports isn't much use when the driving doesn't support multiple ports. --- examples/aarch64/src/main.rs | 5 +- src/device/console.rs | 109 +++++++++++++++++++++++++++-------- 2 files changed, 88 insertions(+), 26 deletions(-) diff --git a/examples/aarch64/src/main.rs b/examples/aarch64/src/main.rs index 8aed78c0..4aa78f51 100644 --- a/examples/aarch64/src/main.rs +++ b/examples/aarch64/src/main.rs @@ -206,8 +206,9 @@ fn virtio_net(transport: T) { fn virtio_console(transport: T) { let mut console = VirtIOConsole::::new(transport).expect("Failed to create console driver"); - let info = console.info(); - info!("VirtIO console {}x{}", info.rows, info.columns); + if let Some(size) = console.size() { + info!("VirtIO console {}x{}", size.rows, size.columns); + } for &c in b"Hello world on console!\n" { console.send(c).expect("Failed to send character"); } diff --git a/src/device/console.rs b/src/device/console.rs index df05710a..7a05193f 100644 --- a/src/device/console.rs +++ b/src/device/console.rs @@ -11,7 +11,7 @@ use crate::{Result, PAGE_SIZE}; use alloc::boxed::Box; use bitflags::bitflags; use core::{ - fmt::{self, Write}, + fmt::{self, Display, Formatter, Write}, ptr::NonNull, }; use log::error; @@ -19,12 +19,13 @@ use log::error; const QUEUE_RECEIVEQ_PORT_0: u16 = 0; const QUEUE_TRANSMITQ_PORT_0: u16 = 1; const QUEUE_SIZE: usize = 2; -const SUPPORTED_FEATURES: Features = Features::RING_EVENT_IDX.union(Features::RING_INDIRECT_DESC); +const SUPPORTED_FEATURES: Features = Features::RING_EVENT_IDX + .union(Features::RING_INDIRECT_DESC) + .union(Features::SIZE); /// Driver for a VirtIO console device. /// -/// Only a single port is allowed since `alloc` is disabled. Emergency write and cols/rows are not -/// implemented. +/// Only a single port is supported. /// /// # Example /// @@ -34,8 +35,8 @@ const SUPPORTED_FEATURES: Features = Features::RING_EVENT_IDX.union(Features::RI /// # fn example(transport: T) -> Result<(), Error> { /// let mut console = VirtIOConsole::::new(transport)?; /// -/// let info = console.info(); -/// println!("VirtIO console {}x{}", info.rows, info.columns); +/// let size = console.size().unwrap(); +/// println!("VirtIO console {}x{}", size.rows, size.columns); /// /// for &c in b"Hello console!\n" { /// console.send(c)?; @@ -48,6 +49,7 @@ const SUPPORTED_FEATURES: Features = Features::RING_EVENT_IDX.union(Features::RI /// ``` pub struct VirtIOConsole { transport: T, + negotiated_features: Features, config_space: NonNull, receiveq: VirtQueue, transmitq: VirtQueue, @@ -72,15 +74,19 @@ unsafe impl Sync for VirtIOConsole where { } -/// Information about a console device, read from its configuration space. -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct ConsoleInfo { - /// The console height in characters. - pub rows: u16, +/// The width and height of a console, in characters. +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub struct Size { /// The console width in characters. pub columns: u16, - /// The maxumum number of ports supported by the console device. - pub max_ports: u32, + /// The console height in characters. + pub rows: u16, +} + +impl Display for Size { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + write!(f, "{}x{}", self.columns, self.rows) + } } impl VirtIOConsole { @@ -109,6 +115,7 @@ impl VirtIOConsole { transport.finish_init(); let mut console = VirtIOConsole { transport, + negotiated_features, config_space, receiveq, transmitq, @@ -121,18 +128,18 @@ impl VirtIOConsole { Ok(console) } - /// Returns a struct with information about the console device, such as the number of rows and columns. - pub fn info(&self) -> ConsoleInfo { - // Safe because config_space is a valid pointer to the device configuration space. - unsafe { - let columns = volread!(self.config_space, cols); - let rows = volread!(self.config_space, rows); - let max_ports = volread!(self.config_space, max_nr_ports); - ConsoleInfo { - rows, - columns, - max_ports, + /// Returns the size of the console, if the device supports reporting this. + pub fn size(&self) -> Option { + if self.negotiated_features.contains(Features::SIZE) { + // SAFETY: self.config_space is a valid pointer to the device configuration space. + unsafe { + Some(Size { + columns: volread!(self.config_space, cols), + rows: volread!(self.config_space, rows), + }) } + } else { + None } } @@ -299,6 +306,60 @@ mod tests { use core::ptr::NonNull; use std::{sync::Mutex, thread}; + #[test] + fn config_info_no_features() { + let mut config_space = Config { + cols: ReadOnly::new(80), + rows: ReadOnly::new(42), + max_nr_ports: ReadOnly::new(0), + emerg_wr: WriteOnly::default(), + }; + let state = Arc::new(Mutex::new(State { + queues: vec![QueueStatus::default(), QueueStatus::default()], + ..Default::default() + })); + let transport = FakeTransport { + device_type: DeviceType::Console, + max_queue_size: 2, + device_features: 0, + config_space: NonNull::from(&mut config_space), + state: state.clone(), + }; + let console = VirtIOConsole::>::new(transport).unwrap(); + + assert_eq!(console.size(), None); + } + + #[test] + fn config_info() { + let mut config_space = Config { + cols: ReadOnly::new(80), + rows: ReadOnly::new(42), + max_nr_ports: ReadOnly::new(0), + emerg_wr: WriteOnly::default(), + }; + let state = Arc::new(Mutex::new(State { + queues: vec![QueueStatus::default(), QueueStatus::default()], + ..Default::default() + })); + let transport = FakeTransport { + device_type: DeviceType::Console, + max_queue_size: 2, + device_features: 0x07, + config_space: NonNull::from(&mut config_space), + state: state.clone(), + }; + let console = VirtIOConsole::>::new(transport).unwrap(); + + assert_eq!( + console.size(), + Some(Size { + columns: 80, + rows: 42 + }) + ); + } + #[test] fn receive() { let mut config_space = Config { From 5532ae3984988e802e767cfb6aa06f247bceb84c Mon Sep 17 00:00:00 2001 From: Andrew Walbran Date: Tue, 6 Aug 2024 14:10:48 +0100 Subject: [PATCH 2/4] Add support for emergency writes. --- src/device/console.rs | 47 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 44 insertions(+), 3 deletions(-) diff --git a/src/device/console.rs b/src/device/console.rs index 7a05193f..ad7431b7 100644 --- a/src/device/console.rs +++ b/src/device/console.rs @@ -6,8 +6,8 @@ mod embedded_io; use crate::hal::Hal; use crate::queue::VirtQueue; use crate::transport::Transport; -use crate::volatile::{volread, ReadOnly, WriteOnly}; -use crate::{Result, PAGE_SIZE}; +use crate::volatile::{volread, volwrite, ReadOnly, WriteOnly}; +use crate::{Error, Result, PAGE_SIZE}; use alloc::boxed::Box; use bitflags::bitflags; use core::{ @@ -21,7 +21,8 @@ const QUEUE_TRANSMITQ_PORT_0: u16 = 1; const QUEUE_SIZE: usize = 2; const SUPPORTED_FEATURES: Features = Features::RING_EVENT_IDX .union(Features::RING_INDIRECT_DESC) - .union(Features::SIZE); + .union(Features::SIZE) + .union(Features::EMERG_WRITE); /// Driver for a VirtIO console device. /// @@ -239,6 +240,21 @@ impl VirtIOConsole { } Ok(()) } + + /// Sends a character to the console using the emergency write feature. + /// + /// Returns an error if the device doesn't support emergency write. + pub fn emergency_write(&mut self, chr: u8) -> Result<()> { + if self.negotiated_features.contains(Features::EMERG_WRITE) { + // SAFETY: `self.config_space` is a valid pointer to the device configuration space. + unsafe { + volwrite!(self.config_space, emerg_wr, chr.into()); + } + Ok(()) + } else { + Err(Error::Unsupported) + } + } } impl Write for VirtIOConsole { @@ -360,6 +376,31 @@ mod tests { ); } + #[test] + fn emergency_write() { + let mut config_space = Config { + cols: ReadOnly::new(0), + rows: ReadOnly::new(0), + max_nr_ports: ReadOnly::new(0), + emerg_wr: WriteOnly::default(), + }; + let state = Arc::new(Mutex::new(State { + queues: vec![QueueStatus::default(), QueueStatus::default()], + ..Default::default() + })); + let transport = FakeTransport { + device_type: DeviceType::Console, + max_queue_size: 2, + device_features: 0x07, + config_space: NonNull::from(&mut config_space), + state: state.clone(), + }; + let mut console = VirtIOConsole::>::new(transport).unwrap(); + + console.emergency_write(42).unwrap(); + assert_eq!(config_space.emerg_wr.0, 42); + } + #[test] fn receive() { let mut config_space = Config { From 2634b67e05def36caa57dd00c9bbd95b4ba47240 Mon Sep 17 00:00:00 2001 From: Andrew Walbran Date: Tue, 22 Oct 2024 15:25:22 +0100 Subject: [PATCH 3/4] Use Display implementation on Size. --- examples/aarch64/src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/aarch64/src/main.rs b/examples/aarch64/src/main.rs index 4aa78f51..9716db7a 100644 --- a/examples/aarch64/src/main.rs +++ b/examples/aarch64/src/main.rs @@ -207,7 +207,7 @@ fn virtio_console(transport: T) { let mut console = VirtIOConsole::::new(transport).expect("Failed to create console driver"); if let Some(size) = console.size() { - info!("VirtIO console {}x{}", size.rows, size.columns); + info!("VirtIO console {}", size); } for &c in b"Hello world on console!\n" { console.send(c).expect("Failed to send character"); From 56ec0da4747e9af472f525da43ed35534f45adb4 Mon Sep 17 00:00:00 2001 From: Andrew Walbran Date: Tue, 22 Oct 2024 15:34:51 +0100 Subject: [PATCH 4/4] Fix formatting. --- examples/riscv/src/tcp.rs | 10 ++++++++-- examples/x86_64/src/tcp.rs | 10 ++++++++-- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/examples/riscv/src/tcp.rs b/examples/riscv/src/tcp.rs index b4fe7552..81e64d21 100644 --- a/examples/riscv/src/tcp.rs +++ b/examples/riscv/src/tcp.rs @@ -37,8 +37,14 @@ impl DeviceWrapper { } impl Device for DeviceWrapper { - type RxToken<'a> = VirtioRxToken where Self: 'a; - type TxToken<'a> = VirtioTxToken where Self: 'a; + type RxToken<'a> + = VirtioRxToken + where + Self: 'a; + type TxToken<'a> + = VirtioTxToken + where + Self: 'a; fn receive(&mut self, _timestamp: Instant) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { match self.inner.borrow_mut().receive() { diff --git a/examples/x86_64/src/tcp.rs b/examples/x86_64/src/tcp.rs index 71b4a52f..a3574d33 100644 --- a/examples/x86_64/src/tcp.rs +++ b/examples/x86_64/src/tcp.rs @@ -37,8 +37,14 @@ impl DeviceWrapper { } impl Device for DeviceWrapper { - type RxToken<'a> = VirtioRxToken where Self: 'a; - type TxToken<'a> = VirtioTxToken where Self: 'a; + type RxToken<'a> + = VirtioRxToken + where + Self: 'a; + type TxToken<'a> + = VirtioTxToken + where + Self: 'a; fn receive(&mut self, _timestamp: Instant) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { match self.inner.borrow_mut().receive() {