Skip to content

Commit f06f594

Browse files
committed
Init virtio console.
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]>
1 parent ad37189 commit f06f594

File tree

12 files changed

+480
-19
lines changed

12 files changed

+480
-19
lines changed

Cargo.lock

Lines changed: 16 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/arch/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,4 @@ edition = "2018"
88

99
[dependencies]
1010
vm-fdt = "0.2.0"
11-
vm-memory = "0.7.0"
11+
vm-memory = "0.8.0"

src/devices/Cargo.toml

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,17 @@ kvm-ioctls = "0.11.0"
1111
libc = "0.2.76"
1212
linux-loader = "0.4.0"
1313
log = "0.4.6"
14-
vm-memory = "0.7.0"
14+
vm-memory = "0.8.0"
1515
vm-superio = "0.5.0"
1616
vmm-sys-util = "0.8.0"
1717
vm-device = "0.1.0"
1818

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

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

2526
[dev-dependencies]
26-
vm-memory = { version = "0.7.0", features = ["backend-mmap"] }
27+
vm-memory = { version = "0.8.0", features = ["backend-mmap"] }
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
use crate::virtio::console::CONSOLE_DEVICE_ID;
2+
3+
use std::borrow::{Borrow, BorrowMut};
4+
use std::io::stdout;
5+
use std::ops::DerefMut;
6+
use std::sync::{Arc, Mutex};
7+
use virtio_console::console;
8+
9+
use super::inorder_handler::InOrderQueueHandler;
10+
use crate::virtio::console::queue_handler::QueueHandler;
11+
use crate::virtio::{CommonConfig, Env, SingleFdSignalQueue, QUEUE_MAX_SIZE};
12+
use virtio_device::{VirtioConfig, VirtioDeviceActions, VirtioDeviceType, VirtioMmioDevice};
13+
use virtio_queue::Queue;
14+
use vm_device::bus::MmioAddress;
15+
use vm_device::device_manager::MmioManager;
16+
use vm_device::{DeviceMmio, MutDeviceMmio};
17+
use vm_memory::GuestAddressSpace;
18+
19+
use super::{ConsoleArgs, Error, Result};
20+
21+
pub struct Console<M: GuestAddressSpace> {
22+
cfg: CommonConfig<M>,
23+
// allow_resize: bool,
24+
// allow_multiport: bool,
25+
// allow_emerg_write: bool,
26+
}
27+
28+
impl<M> Console<M>
29+
where
30+
M: GuestAddressSpace + Clone + Send + 'static,
31+
{
32+
pub fn new<B>(env: &mut Env<M, B>, args: &ConsoleArgs) -> Result<Arc<Mutex<Self>>>
33+
where
34+
// We're using this (more convoluted) bound so we can pass both references and smart
35+
// pointers such as mutex guards here.
36+
B: DerefMut,
37+
B::Target: MmioManager<D = Arc<dyn DeviceMmio + Send + Sync>>,
38+
{
39+
let device_features = args.device_features();
40+
41+
let queues = vec![
42+
Queue::new(env.mem.clone(), QUEUE_MAX_SIZE),
43+
Queue::new(env.mem.clone(), QUEUE_MAX_SIZE),
44+
];
45+
46+
let config_space = Vec::new();
47+
let virtio_cfg = VirtioConfig::new(device_features, queues, config_space);
48+
49+
let common_cfg = CommonConfig::new(virtio_cfg, env).map_err(Error::Virtio)?;
50+
51+
let console = Arc::new(Mutex::new(Console {
52+
cfg: common_cfg,
53+
// allow_resize: args.allow_resize,
54+
// allow_multiport: args.allow_multiport,
55+
// allow_emerg_write: args.allow_emerg_write,
56+
}));
57+
58+
env.register_mmio_device(console.clone())
59+
.map_err(Error::Virtio)?;
60+
61+
Ok(console)
62+
}
63+
}
64+
65+
impl<M: GuestAddressSpace + Clone + Send + 'static> VirtioDeviceType for Console<M> {
66+
fn device_type(&self) -> u32 {
67+
CONSOLE_DEVICE_ID
68+
}
69+
}
70+
71+
impl<M: GuestAddressSpace + Clone + Send + 'static> Borrow<VirtioConfig<M>> for Console<M> {
72+
fn borrow(&self) -> &VirtioConfig<M> {
73+
&self.cfg.virtio
74+
}
75+
}
76+
77+
impl<M: GuestAddressSpace + Clone + Send + 'static> BorrowMut<VirtioConfig<M>> for Console<M> {
78+
fn borrow_mut(&mut self) -> &mut VirtioConfig<M> {
79+
&mut self.cfg.virtio
80+
}
81+
}
82+
83+
impl<M: GuestAddressSpace + Clone + Send + 'static> VirtioDeviceActions for Console<M> {
84+
type E = Error;
85+
86+
fn activate(&mut self) -> Result<()> {
87+
// let mut features = self.cfg.virtio.driver_features;
88+
89+
let driver_notify = SingleFdSignalQueue {
90+
irqfd: self.cfg.irqfd.clone(),
91+
interrupt_status: self.cfg.virtio.interrupt_status.clone(),
92+
};
93+
94+
let mut ioevents = self.cfg.prepare_activate().map_err(Error::Virtio)?;
95+
96+
let inner = InOrderQueueHandler {
97+
driver_notify,
98+
receiveq: self.cfg.virtio.queues.remove(0),
99+
transmitq: self.cfg.virtio.queues.remove(0),
100+
console: console::Console::new(1024, stdout()).map_err(Error::Console)?,
101+
};
102+
103+
let handler = Arc::new(Mutex::new(QueueHandler {
104+
inner,
105+
receiveqfd: ioevents.remove(0),
106+
transmitqfd: ioevents.remove(0),
107+
}));
108+
109+
self.cfg.finalize_activate(handler).map_err(Error::Virtio)
110+
}
111+
112+
fn reset(&mut self) -> Result<()> {
113+
// Not implemented for now.
114+
Ok(())
115+
}
116+
}
117+
118+
impl<M: GuestAddressSpace + Clone + Send + 'static> VirtioMmioDevice<M> for Console<M> {}
119+
120+
impl<M: GuestAddressSpace + Clone + Send + 'static> MutDeviceMmio for Console<M> {
121+
fn mmio_read(&mut self, _base: MmioAddress, offset: u64, data: &mut [u8]) {
122+
self.read(offset, data);
123+
}
124+
125+
fn mmio_write(&mut self, _base: MmioAddress, offset: u64, data: &[u8]) {
126+
self.write(offset, data);
127+
}
128+
}
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
use crate::virtio::SignalUsedQueue;
2+
use std::io::Write;
3+
use std::result;
4+
use virtio_console::console;
5+
use virtio_queue::{Queue, QueueStateOwnedT, QueueStateT};
6+
use vm_memory::GuestAddressSpace;
7+
8+
#[derive(Debug)]
9+
pub enum Error {
10+
GuestMemory(vm_memory::GuestMemoryError),
11+
Queue(virtio_queue::Error),
12+
Console(console::Error),
13+
}
14+
15+
impl From<vm_memory::GuestMemoryError> for Error {
16+
fn from(e: vm_memory::GuestMemoryError) -> Self {
17+
Error::GuestMemory(e)
18+
}
19+
}
20+
21+
impl From<virtio_queue::Error> for Error {
22+
fn from(e: virtio_queue::Error) -> Self {
23+
Error::Queue(e)
24+
}
25+
}
26+
27+
impl From<console::Error> for Error {
28+
fn from(e: console::Error) -> Self {
29+
Error::Console(e)
30+
}
31+
}
32+
33+
pub struct InOrderQueueHandler<M: GuestAddressSpace, S: SignalUsedQueue, T: Write> {
34+
pub driver_notify: S,
35+
pub transmitq: Queue<M>,
36+
pub receiveq: Queue<M>,
37+
pub console: console::Console<T>,
38+
}
39+
40+
impl<M, S, T> InOrderQueueHandler<M, S, T>
41+
where
42+
M: GuestAddressSpace,
43+
S: SignalUsedQueue,
44+
T: Write,
45+
{
46+
pub fn process_transmitq(&mut self) -> result::Result<(), Error> {
47+
// To see why this is done in a loop, please look at the `Queue::enable_notification`
48+
// comments in `virtio_queue`.
49+
loop {
50+
self.transmitq.disable_notification()?;
51+
52+
while let Some(mut chain) = self
53+
.transmitq
54+
.state
55+
.pop_descriptor_chain(self.transmitq.mem.memory())
56+
{
57+
self.console
58+
.process_transmitq_chain(&mut chain)?;
59+
60+
self.transmitq.add_used(chain.head_index(), 0)?;
61+
}
62+
if !self.transmitq.enable_notification()? {
63+
break;
64+
}
65+
}
66+
if self.transmitq.needs_notification()? {
67+
self.driver_notify.signal_used_queue(1);
68+
}
69+
70+
Ok(())
71+
}
72+
73+
pub fn process_receiveq(&mut self) -> result::Result<(), Error> {
74+
// To see why this is done in a loop, please look at the `Queue::enable_notification`
75+
// comments in `virtio_queue`.
76+
let mut notify = false;
77+
78+
loop {
79+
self.receiveq.disable_notification()?;
80+
81+
while let Some(mut chain) = self
82+
.receiveq
83+
.state
84+
.pop_descriptor_chain(self.receiveq.mem.memory())
85+
{
86+
let used_len = match self.console.process_receiveq_chain(&mut chain) {
87+
Ok(used_len) => used_len,
88+
Err(e) => {
89+
self.receiveq.state.go_to_previous_position();
90+
return Err(Error::Console(e));
91+
}
92+
};
93+
if used_len == 0 {
94+
self.receiveq.state.go_to_previous_position();
95+
break;
96+
}
97+
self.receiveq
98+
.add_used(chain.head_index(), used_len as u32)?;
99+
notify = true;
100+
}
101+
102+
if self.console.is_input_buffer_empty() || !self.receiveq.enable_notification()? {
103+
break;
104+
}
105+
}
106+
107+
if notify && self.receiveq.needs_notification()? {
108+
self.driver_notify.signal_used_queue(0);
109+
}
110+
111+
Ok(())
112+
}
113+
}

src/devices/src/virtio/console/mod.rs

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
mod device;
2+
mod inorder_handler;
3+
mod queue_handler;
4+
5+
pub use device::Console;
6+
7+
use crate::virtio::features::VIRTIO_F_VERSION_1;
8+
9+
// Console device ID as defined by the standard.
10+
pub const CONSOLE_DEVICE_ID: u32 = 3;
11+
12+
// Console device resize feature.
13+
pub const VIRTIO_CONSOLE_F_SIZE: u32 = 0;
14+
15+
// Console device multiport feature.
16+
pub const VIRTIO_CONSOLE_F_MULTIPORT: u32 = 1;
17+
18+
// Console device emergency write feature.
19+
pub const VIRTIO_CONSOLE_F_EMERG_WRITE: u32 = 2;
20+
21+
#[derive(Debug)]
22+
pub enum Error {
23+
Virtio(crate::virtio::Error),
24+
Console(virtio_console::console::Error),
25+
}
26+
pub type Result<T> = std::result::Result<T, Error>;
27+
28+
// Arguments required when building a console device.
29+
pub struct ConsoleArgs {
30+
// pub allow_resize: bool,
31+
// pub allow_multiport: bool,
32+
// pub allow_emerg_write: bool,
33+
}
34+
35+
impl ConsoleArgs {
36+
// Generate device features based on the configuration options.
37+
pub fn device_features(&self) -> u64 {
38+
let features = 1 << VIRTIO_F_VERSION_1;
39+
40+
// if self.allow_resize {
41+
// features |= 1 << VIRTIO_CONSOLE_F_SIZE;
42+
// }
43+
//
44+
// if self.allow_multiport {
45+
// features |= 1 << VIRTIO_CONSOLE_F_MULTIPORT;
46+
// }
47+
//
48+
// if self.allow_emerg_write {
49+
// features |= 1 << VIRTIO_CONSOLE_F_EMERG_WRITE;
50+
// }
51+
52+
features
53+
}
54+
}

0 commit comments

Comments
 (0)