Skip to content

Commit

Permalink
Init virtio console.
Browse files Browse the repository at this point in the history
Added a virtio console device to replace the UART serial console.
The implementation is divided between vm-virtio and vmm-reference.

Signed-off-by: Niculae Radu <[email protected]>
  • Loading branch information
RaduNiculae committed Jun 14, 2022
1 parent ad37189 commit f06f594
Show file tree
Hide file tree
Showing 12 changed files with 480 additions and 19 deletions.
22 changes: 16 additions & 6 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion src/arch/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,4 @@ edition = "2018"

[dependencies]
vm-fdt = "0.2.0"
vm-memory = "0.7.0"
vm-memory = "0.8.0"
11 changes: 6 additions & 5 deletions src/devices/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,17 @@ kvm-ioctls = "0.11.0"
libc = "0.2.76"
linux-loader = "0.4.0"
log = "0.4.6"
vm-memory = "0.7.0"
vm-memory = "0.8.0"
vm-superio = "0.5.0"
vmm-sys-util = "0.8.0"
vm-device = "0.1.0"

virtio-blk = { git = "https://github.com/rust-vmm/vm-virtio.git", features = ["backend-stdio"] }
virtio-device = { git = "https://github.com/rust-vmm/vm-virtio.git"}
virtio-queue = { git = "https://github.com/rust-vmm/vm-virtio.git"}
virtio-blk = { git = "https://github.com/RaduNiculae/vm-virtio.git", branch = "virtio-console", features = ["backend-stdio"] }
virtio-device = { git = "https://github.com/RaduNiculae/vm-virtio.git", branch = "virtio-console"}
virtio-queue = { git = "https://github.com/RaduNiculae/vm-virtio.git", branch = "virtio-console"}
virtio-console = { git = "https://github.com/RaduNiculae/vm-virtio.git", branch = "virtio-console"}

utils = { path = "../utils" }

[dev-dependencies]
vm-memory = { version = "0.7.0", features = ["backend-mmap"] }
vm-memory = { version = "0.8.0", features = ["backend-mmap"] }
128 changes: 128 additions & 0 deletions src/devices/src/virtio/console/device.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
use crate::virtio::console::CONSOLE_DEVICE_ID;

use std::borrow::{Borrow, BorrowMut};
use std::io::stdout;
use std::ops::DerefMut;
use std::sync::{Arc, Mutex};
use virtio_console::console;

use super::inorder_handler::InOrderQueueHandler;
use crate::virtio::console::queue_handler::QueueHandler;
use crate::virtio::{CommonConfig, Env, SingleFdSignalQueue, QUEUE_MAX_SIZE};
use virtio_device::{VirtioConfig, VirtioDeviceActions, VirtioDeviceType, VirtioMmioDevice};
use virtio_queue::Queue;
use vm_device::bus::MmioAddress;
use vm_device::device_manager::MmioManager;
use vm_device::{DeviceMmio, MutDeviceMmio};
use vm_memory::GuestAddressSpace;

use super::{ConsoleArgs, Error, Result};

pub struct Console<M: GuestAddressSpace> {
cfg: CommonConfig<M>,
// allow_resize: bool,
// allow_multiport: bool,
// allow_emerg_write: bool,
}

impl<M> Console<M>
where
M: GuestAddressSpace + Clone + Send + 'static,
{
pub fn new<B>(env: &mut Env<M, B>, args: &ConsoleArgs) -> Result<Arc<Mutex<Self>>>
where
// We're using this (more convoluted) bound so we can pass both references and smart
// pointers such as mutex guards here.
B: DerefMut,
B::Target: MmioManager<D = Arc<dyn DeviceMmio + Send + Sync>>,
{
let device_features = args.device_features();

let queues = vec![
Queue::new(env.mem.clone(), QUEUE_MAX_SIZE),
Queue::new(env.mem.clone(), QUEUE_MAX_SIZE),
];

let config_space = Vec::new();
let virtio_cfg = VirtioConfig::new(device_features, queues, config_space);

let common_cfg = CommonConfig::new(virtio_cfg, env).map_err(Error::Virtio)?;

let console = Arc::new(Mutex::new(Console {
cfg: common_cfg,
// allow_resize: args.allow_resize,
// allow_multiport: args.allow_multiport,
// allow_emerg_write: args.allow_emerg_write,
}));

env.register_mmio_device(console.clone())
.map_err(Error::Virtio)?;

Ok(console)
}
}

impl<M: GuestAddressSpace + Clone + Send + 'static> VirtioDeviceType for Console<M> {
fn device_type(&self) -> u32 {
CONSOLE_DEVICE_ID
}
}

impl<M: GuestAddressSpace + Clone + Send + 'static> Borrow<VirtioConfig<M>> for Console<M> {
fn borrow(&self) -> &VirtioConfig<M> {
&self.cfg.virtio
}
}

impl<M: GuestAddressSpace + Clone + Send + 'static> BorrowMut<VirtioConfig<M>> for Console<M> {
fn borrow_mut(&mut self) -> &mut VirtioConfig<M> {
&mut self.cfg.virtio
}
}

impl<M: GuestAddressSpace + Clone + Send + 'static> VirtioDeviceActions for Console<M> {
type E = Error;

fn activate(&mut self) -> Result<()> {
// let mut features = self.cfg.virtio.driver_features;

let driver_notify = SingleFdSignalQueue {
irqfd: self.cfg.irqfd.clone(),
interrupt_status: self.cfg.virtio.interrupt_status.clone(),
};

let mut ioevents = self.cfg.prepare_activate().map_err(Error::Virtio)?;

let inner = InOrderQueueHandler {
driver_notify,
receiveq: self.cfg.virtio.queues.remove(0),
transmitq: self.cfg.virtio.queues.remove(0),
console: console::Console::new(1024, stdout()).map_err(Error::Console)?,
};

let handler = Arc::new(Mutex::new(QueueHandler {
inner,
receiveqfd: ioevents.remove(0),
transmitqfd: ioevents.remove(0),
}));

self.cfg.finalize_activate(handler).map_err(Error::Virtio)
}

fn reset(&mut self) -> Result<()> {
// Not implemented for now.
Ok(())
}
}

impl<M: GuestAddressSpace + Clone + Send + 'static> VirtioMmioDevice<M> for Console<M> {}

impl<M: GuestAddressSpace + Clone + Send + 'static> MutDeviceMmio for Console<M> {
fn mmio_read(&mut self, _base: MmioAddress, offset: u64, data: &mut [u8]) {
self.read(offset, data);
}

fn mmio_write(&mut self, _base: MmioAddress, offset: u64, data: &[u8]) {
self.write(offset, data);
}
}
113 changes: 113 additions & 0 deletions src/devices/src/virtio/console/inorder_handler.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
use crate::virtio::SignalUsedQueue;
use std::io::Write;
use std::result;
use virtio_console::console;
use virtio_queue::{Queue, QueueStateOwnedT, QueueStateT};
use vm_memory::GuestAddressSpace;

#[derive(Debug)]
pub enum Error {
GuestMemory(vm_memory::GuestMemoryError),
Queue(virtio_queue::Error),
Console(console::Error),
}

impl From<vm_memory::GuestMemoryError> for Error {
fn from(e: vm_memory::GuestMemoryError) -> Self {
Error::GuestMemory(e)
}
}

impl From<virtio_queue::Error> for Error {
fn from(e: virtio_queue::Error) -> Self {
Error::Queue(e)
}
}

impl From<console::Error> for Error {
fn from(e: console::Error) -> Self {
Error::Console(e)
}
}

pub struct InOrderQueueHandler<M: GuestAddressSpace, S: SignalUsedQueue, T: Write> {
pub driver_notify: S,
pub transmitq: Queue<M>,
pub receiveq: Queue<M>,
pub console: console::Console<T>,
}

impl<M, S, T> InOrderQueueHandler<M, S, T>
where
M: GuestAddressSpace,
S: SignalUsedQueue,
T: Write,
{
pub fn process_transmitq(&mut self) -> result::Result<(), Error> {
// To see why this is done in a loop, please look at the `Queue::enable_notification`
// comments in `virtio_queue`.
loop {
self.transmitq.disable_notification()?;

while let Some(mut chain) = self
.transmitq
.state
.pop_descriptor_chain(self.transmitq.mem.memory())
{
self.console
.process_transmitq_chain(&mut chain)?;

self.transmitq.add_used(chain.head_index(), 0)?;
}
if !self.transmitq.enable_notification()? {
break;
}
}
if self.transmitq.needs_notification()? {
self.driver_notify.signal_used_queue(1);
}

Ok(())
}

pub fn process_receiveq(&mut self) -> result::Result<(), Error> {
// To see why this is done in a loop, please look at the `Queue::enable_notification`
// comments in `virtio_queue`.
let mut notify = false;

loop {
self.receiveq.disable_notification()?;

while let Some(mut chain) = self
.receiveq
.state
.pop_descriptor_chain(self.receiveq.mem.memory())
{
let used_len = match self.console.process_receiveq_chain(&mut chain) {
Ok(used_len) => used_len,
Err(e) => {
self.receiveq.state.go_to_previous_position();
return Err(Error::Console(e));
}
};
if used_len == 0 {
self.receiveq.state.go_to_previous_position();
break;
}
self.receiveq
.add_used(chain.head_index(), used_len as u32)?;
notify = true;
}

if self.console.is_input_buffer_empty() || !self.receiveq.enable_notification()? {
break;
}
}

if notify && self.receiveq.needs_notification()? {
self.driver_notify.signal_used_queue(0);
}

Ok(())
}
}
54 changes: 54 additions & 0 deletions src/devices/src/virtio/console/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
mod device;
mod inorder_handler;
mod queue_handler;

pub use device::Console;

use crate::virtio::features::VIRTIO_F_VERSION_1;

// Console device ID as defined by the standard.
pub const CONSOLE_DEVICE_ID: u32 = 3;

// Console device resize feature.
pub const VIRTIO_CONSOLE_F_SIZE: u32 = 0;

// Console device multiport feature.
pub const VIRTIO_CONSOLE_F_MULTIPORT: u32 = 1;

// Console device emergency write feature.
pub const VIRTIO_CONSOLE_F_EMERG_WRITE: u32 = 2;

#[derive(Debug)]
pub enum Error {
Virtio(crate::virtio::Error),
Console(virtio_console::console::Error),
}
pub type Result<T> = std::result::Result<T, Error>;

// Arguments required when building a console device.
pub struct ConsoleArgs {
// pub allow_resize: bool,
// pub allow_multiport: bool,
// pub allow_emerg_write: bool,
}

impl ConsoleArgs {
// Generate device features based on the configuration options.
pub fn device_features(&self) -> u64 {
let features = 1 << VIRTIO_F_VERSION_1;

// if self.allow_resize {
// features |= 1 << VIRTIO_CONSOLE_F_SIZE;
// }
//
// if self.allow_multiport {
// features |= 1 << VIRTIO_CONSOLE_F_MULTIPORT;
// }
//
// if self.allow_emerg_write {
// features |= 1 << VIRTIO_CONSOLE_F_EMERG_WRITE;
// }

features
}
}
Loading

0 comments on commit f06f594

Please sign in to comment.