Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support size and emergency write features on VirtIOConsole #156

Merged
merged 4 commits into from
Oct 22, 2024
Merged
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
5 changes: 3 additions & 2 deletions examples/aarch64/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -206,8 +206,9 @@ fn virtio_net<T: Transport>(transport: T) {
fn virtio_console<T: Transport>(transport: T) {
let mut console =
VirtIOConsole::<HalImpl, T>::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 {}", size);
}
for &c in b"Hello world on console!\n" {
console.send(c).expect("Failed to send character");
}
Expand Down
10 changes: 8 additions & 2 deletions examples/riscv/src/tcp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,14 @@ impl<T: Transport> DeviceWrapper<T> {
}

impl<T: Transport> Device for DeviceWrapper<T> {
type RxToken<'a> = VirtioRxToken<T> where Self: 'a;
type TxToken<'a> = VirtioTxToken<T> where Self: 'a;
type RxToken<'a>
= VirtioRxToken<T>
where
Self: 'a;
type TxToken<'a>
= VirtioTxToken<T>
where
Self: 'a;

fn receive(&mut self, _timestamp: Instant) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> {
match self.inner.borrow_mut().receive() {
Expand Down
10 changes: 8 additions & 2 deletions examples/x86_64/src/tcp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,14 @@ impl<T: Transport> DeviceWrapper<T> {
}

impl<T: Transport> Device for DeviceWrapper<T> {
type RxToken<'a> = VirtioRxToken<T> where Self: 'a;
type TxToken<'a> = VirtioTxToken<T> where Self: 'a;
type RxToken<'a>
= VirtioRxToken<T>
where
Self: 'a;
type TxToken<'a>
= VirtioTxToken<T>
where
Self: 'a;

fn receive(&mut self, _timestamp: Instant) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> {
match self.inner.borrow_mut().receive() {
Expand Down
154 changes: 128 additions & 26 deletions src/device/console.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,25 +6,27 @@ 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::{
fmt::{self, Write},
fmt::{self, Display, Formatter, Write},
ptr::NonNull,
};
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)
.union(Features::EMERG_WRITE);

/// 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
///
Expand All @@ -34,8 +36,8 @@ const SUPPORTED_FEATURES: Features = Features::RING_EVENT_IDX.union(Features::RI
/// # fn example<HalImpl: Hal, T: Transport>(transport: T) -> Result<(), Error> {
/// let mut console = VirtIOConsole::<HalImpl, _>::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)?;
Expand All @@ -48,6 +50,7 @@ const SUPPORTED_FEATURES: Features = Features::RING_EVENT_IDX.union(Features::RI
/// ```
pub struct VirtIOConsole<H: Hal, T: Transport> {
transport: T,
negotiated_features: Features,
config_space: NonNull<Config>,
receiveq: VirtQueue<H, QUEUE_SIZE>,
transmitq: VirtQueue<H, QUEUE_SIZE>,
Expand All @@ -72,15 +75,19 @@ unsafe impl<H: Hal, T: Transport + Sync> Sync for VirtIOConsole<H, T> 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<H: Hal, T: Transport> VirtIOConsole<H, T> {
Expand Down Expand Up @@ -109,6 +116,7 @@ impl<H: Hal, T: Transport> VirtIOConsole<H, T> {
transport.finish_init();
let mut console = VirtIOConsole {
transport,
negotiated_features,
config_space,
receiveq,
transmitq,
Expand All @@ -121,18 +129,18 @@ impl<H: Hal, T: Transport> VirtIOConsole<H, T> {
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<Size> {
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
}
}

Expand Down Expand Up @@ -232,6 +240,21 @@ impl<H: Hal, T: Transport> VirtIOConsole<H, T> {
}
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<H: Hal, T: Transport> Write for VirtIOConsole<H, T> {
Expand Down Expand Up @@ -299,6 +322,85 @@ 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::<FakeHal, FakeTransport<Config>>::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::<FakeHal, FakeTransport<Config>>::new(transport).unwrap();

assert_eq!(
console.size(),
Some(Size {
columns: 80,
rows: 42
})
);
}

#[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::<FakeHal, FakeTransport<Config>>::new(transport).unwrap();

console.emergency_write(42).unwrap();
assert_eq!(config_space.emerg_wr.0, 42);
}

#[test]
fn receive() {
let mut config_space = Config {
Expand Down
Loading