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 May 17, 2022
1 parent ad37189 commit 5cb7cef
Show file tree
Hide file tree
Showing 12 changed files with 492 additions and 25 deletions.
46 changes: 33 additions & 13 deletions Cargo.lock

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

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"] }
132 changes: 132 additions & 0 deletions src/devices/src/virtio/console/device.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@

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 virtio_device::{VirtioConfig, VirtioDeviceActions, VirtioDeviceType, VirtioMmioDevice};
use crate::virtio::{CommonConfig, Env, SingleFdSignalQueue, QUEUE_MAX_SIZE};
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 crate::virtio::console::queue_handler::QueueHandler;
use super::inorder_handler::InOrderQueueHandler;

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),
output: stdout(),
console: console::Console::new(),
};

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);
}
}
97 changes: 97 additions & 0 deletions src/devices/src/virtio/console/inorder_handler.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
use std::io::Write;
use std::result;
use virtio_queue::{Queue};
use vm_memory::{GuestAddressSpace};
use crate::virtio::{SignalUsedQueue};
use virtio_console::console;

#[derive(Debug)]
pub enum Error {
GuestMemory(vm_memory::GuestMemoryError),
Queue(virtio_queue::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)
}
}


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

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.iter()?.next() {
let read_len = self.console.process_transmitq_chain(&mut chain, &mut self.output);

self.transmitq.add_used(chain.head_index(), read_len as u32)?;
}
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 {
if self.console.is_input_buffer_empty() {
break;
}
self.receiveq.disable_notification()?;

while let Some(mut chain) = self.receiveq.iter()?.next() {
let used_len = self.console.process_receiveq_chain(&mut chain);

self.receiveq.add_used(chain.head_index(), used_len as u32)?;
notify = true;

if self.console.is_input_buffer_empty() {
break;
}
}

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

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

Ok(())
}
}
Loading

0 comments on commit 5cb7cef

Please sign in to comment.