Skip to content

Commit 5cb7cef

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 5cb7cef

File tree

12 files changed

+492
-25
lines changed

12 files changed

+492
-25
lines changed

Cargo.lock

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

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

0 commit comments

Comments
 (0)