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 17, 2022
1 parent ad37189 commit dcf40ba
Show file tree
Hide file tree
Showing 17 changed files with 543 additions and 20 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.

10 changes: 10 additions & 0 deletions src/api/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,13 @@ impl Cli {
.required(false)
.takes_value(true)
.help("Block device configuration. \n\tFormat: \"path=<string>\"")
)
.arg(
Arg::with_name("console")
.long("console")
.required(false)
.takes_value(true)
.help("Console configuration. \n\tFormat: \"type=<string>\"")
);

// Save the usage beforehand as a string, because `get_matches` consumes the `App`.
Expand All @@ -69,6 +76,7 @@ impl Cli {
.vcpu_config(matches.value_of("vcpu"))
.net_config(matches.value_of("net"))
.block_config(matches.value_of("block"))
.console_config(matches.value_of("console"))
.build()
.map_err(|e| format!("{:?}", e))
}
Expand Down Expand Up @@ -236,6 +244,7 @@ mod tests {
vcpu_config: VcpuConfig { num: 1 },
block_config: None,
net_config: None,
console_config: None
}
);

Expand All @@ -252,6 +261,7 @@ mod tests {
vcpu_config: VcpuConfig { num: 1 },
block_config: None,
net_config: None,
console_config: None
}
);
}
Expand Down
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"] }
118 changes: 118 additions & 0 deletions src/devices/src/virtio/console/device.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
use crate::virtio::console::{device_features, 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::{Error, Result};

pub struct Console<M: GuestAddressSpace> {
cfg: CommonConfig<M>,
}

impl<M> Console<M>
where
M: GuestAddressSpace + Clone + Send + 'static,
{
pub fn new<B>(env: &mut Env<M, B>) -> 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 = 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 }));

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 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(stdout()),
};

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);
}
}
112 changes: 112 additions & 0 deletions src/devices/src/virtio/console/inorder_handler.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
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(())
}
}
23 changes: 23 additions & 0 deletions src/devices/src/virtio/console/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
mod device;
mod inorder_handler;
mod queue_handler;

pub use device::Console;

use crate::virtio::features::VIRTIO_F_IN_ORDER;
use crate::virtio::features::VIRTIO_F_RING_EVENT_IDX;
use crate::virtio::features::VIRTIO_F_VERSION_1;

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

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

pub fn device_features() -> u64 {
1 << VIRTIO_F_VERSION_1 | 1 << VIRTIO_F_IN_ORDER | 1 << VIRTIO_F_RING_EVENT_IDX
}
Loading

0 comments on commit dcf40ba

Please sign in to comment.