Skip to content

Commit c08b826

Browse files
committed
add support of virtio console according to the VirtIO standard
During runtime the kernel checks, if a virtio console device is available, initialize this device and use it to print kernel messages. A device support at the early stage of the boot process is currently not possible.
1 parent 1f9b8f3 commit c08b826

File tree

17 files changed

+762
-28
lines changed

17 files changed

+762
-28
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ trace = []
6969
udp = ["smoltcp", "smoltcp/socket-udp"]
7070
vga = []
7171
vsock = ["pci"]
72+
console = []
7273

7374
[lints.rust]
7475
rust_2018_idioms = "warn"

src/arch/aarch64/kernel/mmio.rs

Lines changed: 58 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ use virtio::mmio::{DeviceRegisters, DeviceRegistersVolatileFieldAccess};
77
use volatile::VolatileRef;
88

99
use crate::arch::aarch64::mm::paging::{self, PageSize};
10+
#[cfg(feature = "console")]
11+
use crate::drivers::console::VirtioConsoleDriver;
12+
#[cfg(any(feature = "tcp", feature = "udp"))]
1013
use crate::drivers::net::virtio::VirtioNetDriver;
1114
use crate::drivers::virtio::transport::mmio::{self as mmio_virtio, VirtioDriver};
1215
use crate::init_cell::InitCell;
@@ -17,13 +20,26 @@ pub(crate) static MMIO_DRIVERS: InitCell<Vec<MmioDriver>> = InitCell::new(Vec::n
1720
pub(crate) enum MmioDriver {
1821
#[cfg(any(feature = "tcp", feature = "udp"))]
1922
VirtioNet(InterruptTicketMutex<VirtioNetDriver>),
23+
#[cfg(feature = "console")]
24+
VirtioConsole(InterruptTicketMutex<VirtioConsoleDriver>),
2025
}
2126

2227
impl MmioDriver {
2328
#[cfg(any(feature = "tcp", feature = "udp"))]
2429
fn get_network_driver(&self) -> Option<&InterruptTicketMutex<VirtioNetDriver>> {
2530
match self {
2631
Self::VirtioNet(drv) => Some(drv),
32+
#[cfg(feature = "console")]
33+
_ => None,
34+
}
35+
}
36+
37+
#[cfg(feature = "console")]
38+
fn get_console_driver(&self) -> Option<&InterruptTicketMutex<VirtioConsoleDriver>> {
39+
match self {
40+
Self::VirtioConsole(drv) => Some(drv),
41+
#[cfg(any(feature = "tcp", feature = "udp"))]
42+
_ => None,
2743
}
2844
}
2945
}
@@ -40,6 +56,14 @@ pub(crate) fn get_network_driver() -> Option<&'static InterruptTicketMutex<Virti
4056
.find_map(|drv| drv.get_network_driver())
4157
}
4258

59+
#[cfg(feature = "console")]
60+
pub(crate) fn get_console_driver() -> Option<&'static InterruptTicketMutex<VirtioConsoleDriver>> {
61+
MMIO_DRIVERS
62+
.get()?
63+
.iter()
64+
.find_map(|drv| drv.get_console_driver())
65+
}
66+
4367
pub fn init_drivers() {
4468
without_interrupts(|| {
4569
if let Some(fdt) = crate::env::fdt() {
@@ -96,16 +120,30 @@ pub fn init_drivers() {
96120
// Verify the device-ID to find the network card
97121
let id = mmio.as_ptr().device_id().read();
98122

99-
#[cfg(any(feature = "tcp", feature = "udp"))]
100-
if id == virtio::Id::Net {
101-
trace!("Found network card at {mmio:p}, irq: {irq}");
102-
if let Ok(VirtioDriver::Network(drv)) =
103-
mmio_virtio::init_device(mmio, irq.try_into().unwrap())
104-
{
105-
register_driver(MmioDriver::VirtioNet(
106-
hermit_sync::InterruptTicketMutex::new(drv),
107-
));
123+
match id {
124+
#[cfg(any(feature = "tcp", feature = "udp"))]
125+
virtio::Id::Net => {
126+
trace!("Found network card at {mmio:p}, irq: {irq}");
127+
if let Ok(VirtioDriver::Network(drv)) =
128+
mmio_virtio::init_device(mmio, irq.try_into().unwrap())
129+
{
130+
register_driver(MmioDriver::VirtioNet(
131+
hermit_sync::InterruptTicketMutex::new(drv),
132+
));
133+
}
134+
}
135+
#[cfg(feature = "console")]
136+
virtio::Id::Console => {
137+
info!("Found console at {mmio:p}, irq: {irq}");
138+
if let Ok(VirtioDriver::Console(drv)) =
139+
mmio_virtio::init_device(mmio, irq.try_into().unwrap())
140+
{
141+
register_driver(MmioDriver::VirtioConsole(
142+
hermit_sync::InterruptTicketMutex::new(*drv),
143+
));
144+
}
108145
}
146+
_ => {}
109147
}
110148
}
111149
}
@@ -117,4 +155,15 @@ pub fn init_drivers() {
117155
});
118156

119157
MMIO_DRIVERS.finalize();
158+
159+
#[cfg(feature = "console")]
160+
{
161+
if get_console_driver().is_some() {
162+
info!("Switch to virtio console");
163+
crate::console::CONSOLE
164+
.lock()
165+
.inner
166+
.switch_to_virtio_console();
167+
}
168+
}
120169
}

src/arch/aarch64/kernel/mod.rs

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
pub mod core_local;
22
pub mod interrupts;
3-
#[cfg(all(not(feature = "pci"), any(feature = "tcp", feature = "udp")))]
3+
#[cfg(all(
4+
not(feature = "pci"),
5+
any(feature = "tcp", feature = "udp", feature = "console")
6+
))]
47
pub mod mmio;
58
#[cfg(feature = "pci")]
69
pub mod pci;
@@ -39,10 +42,7 @@ impl Console {
3942
let base = env::boot_info()
4043
.hardware_info
4144
.serial_port_base
42-
.map(|uartport| uartport.get())
43-
.unwrap_or_default()
44-
.try_into()
45-
.unwrap();
45+
.map(|uartport| uartport.get());
4646

4747
let serial_port = SerialPort::new(base);
4848

@@ -63,6 +63,11 @@ impl Console {
6363
true
6464
}
6565

66+
#[cfg(all(not(feature = "pci"), feature = "console"))]
67+
pub fn switch_to_virtio_console(&mut self) {
68+
self.serial_port.switch_to_virtio_console();
69+
}
70+
6671
pub fn register_waker(&mut self, _waker: &Waker) {}
6772
}
6873

src/arch/aarch64/kernel/serial.rs

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,48 @@
11
use core::arch::asm;
22

3+
#[cfg(all(not(feature = "pci"), feature = "console"))]
4+
use crate::kernel::mmio::get_console_driver;
35
use crate::syscalls::interfaces::serial_buf_hypercall;
46

57
enum SerialInner {
8+
None,
69
Uart(u32),
710
Uhyve,
11+
#[cfg(all(not(feature = "pci"), feature = "console"))]
12+
Virtio,
813
}
914

1015
pub struct SerialPort {
1116
inner: SerialInner,
1217
}
1318

1419
impl SerialPort {
15-
pub fn new(port_address: u32) -> Self {
20+
pub fn new(port_address: Option<u64>) -> Self {
1621
if crate::env::is_uhyve() {
1722
Self {
1823
inner: SerialInner::Uhyve,
1924
}
25+
} else if let Some(port_address) = port_address {
26+
Self {
27+
inner: SerialInner::Uart(port_address.try_into().unwrap()),
28+
}
2029
} else {
2130
Self {
22-
inner: SerialInner::Uart(port_address),
31+
inner: SerialInner::None,
2332
}
2433
}
2534
}
2635

36+
#[cfg(all(not(feature = "pci"), feature = "console"))]
37+
pub fn switch_to_virtio_console(&mut self) {
38+
self.inner = SerialInner::Virtio;
39+
}
40+
2741
pub fn write_buf(&mut self, buf: &[u8]) {
2842
match &mut self.inner {
43+
SerialInner::None => {
44+
// No serial port configured, do nothing.
45+
}
2946
SerialInner::Uhyve => {
3047
serial_buf_hypercall(buf);
3148
}
@@ -54,6 +71,12 @@ impl SerialPort {
5471
}
5572
}
5673
}
74+
#[cfg(all(not(feature = "pci"), feature = "console"))]
75+
SerialInner::Virtio => {
76+
if let Some(console_driver) = get_console_driver() {
77+
let _ = console_driver.lock().write(buf);
78+
}
79+
}
5780
}
5881
}
5982

src/arch/x86_64/kernel/mod.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,11 @@ impl Console {
7979
pub fn register_waker(&mut self, waker: &Waker) {
8080
self.serial_port.register_waker(waker);
8181
}
82+
83+
#[cfg(all(feature = "pci", feature = "console"))]
84+
pub fn switch_to_virtio_console(&mut self) {
85+
self.serial_port.switch_to_virtio_console();
86+
}
8287
}
8388

8489
impl Default for Console {

src/arch/x86_64/kernel/serial.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ use core::task::Waker;
44
use crate::arch::x86_64::kernel::apic;
55
use crate::arch::x86_64::kernel::core_local::increment_irq_counter;
66
use crate::arch::x86_64::kernel::interrupts::{self, IDT};
7+
#[cfg(all(feature = "pci", feature = "console"))]
8+
use crate::drivers::pci::get_console_driver;
79
use crate::executor::WakerRegistration;
810
use crate::syscalls::interfaces::serial_buf_hypercall;
911

@@ -12,6 +14,8 @@ const SERIAL_IRQ: u8 = 36;
1214
enum SerialInner {
1315
Uart(uart_16550::SerialPort),
1416
Uhyve,
17+
#[cfg(all(feature = "console", feature = "pci"))]
18+
Virtio,
1519
}
1620

1721
pub struct SerialPort {
@@ -71,8 +75,19 @@ impl SerialPort {
7175
s.send(data);
7276
}
7377
}
78+
#[cfg(all(feature = "console", feature = "pci"))]
79+
SerialInner::Virtio => {
80+
if let Some(console_driver) = get_console_driver() {
81+
let _ = console_driver.lock().write(buf);
82+
}
83+
}
7484
}
7585
}
86+
87+
#[cfg(all(feature = "pci", feature = "console"))]
88+
pub fn switch_to_virtio_console(&mut self) {
89+
self.inner = SerialInner::Virtio;
90+
}
7691
}
7792

7893
extern "x86-interrupt" fn serial_interrupt(_stack_frame: crate::interrupts::ExceptionStackFrame) {

src/config.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ pub(crate) const USER_STACK_SIZE: usize = 0x0010_0000;
77
#[cfg(any(
88
all(any(feature = "tcp", feature = "udp"), not(feature = "rtl8139")),
99
feature = "fuse",
10-
feature = "vsock"
10+
feature = "vsock",
11+
feature = "console"
1112
))]
1213
pub(crate) const VIRTIO_MAX_QUEUE_SIZE: u16 = if cfg!(feature = "pci") { 2048 } else { 1024 };
1314

@@ -17,3 +18,6 @@ pub(crate) const DEFAULT_KEEP_ALIVE_INTERVAL: u64 = 75000;
1718

1819
#[cfg(feature = "vsock")]
1920
pub(crate) const VSOCK_PACKET_SIZE: u32 = 8192;
21+
22+
#[cfg(feature = "console")]
23+
pub(crate) const CONSOLE_PACKET_SIZE: u32 = 1024;

src/drivers/console/mmio.rs

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
//! A module containing a virtio console driver.
2+
//!
3+
//! The module contains ...
4+
5+
use virtio::console::Config;
6+
use virtio::mmio::{DeviceRegisters, DeviceRegistersVolatileFieldAccess};
7+
use volatile::VolatileRef;
8+
9+
use crate::drivers::InterruptLine;
10+
use crate::drivers::console::{ConsoleDevCfg, RxQueue, TxQueue, VirtioConsoleDriver};
11+
use crate::drivers::virtio::error::VirtioError;
12+
use crate::drivers::virtio::transport::mmio::{ComCfg, IsrStatus, NotifCfg};
13+
14+
// Backend-dependent interface for Virtio console driver
15+
impl VirtioConsoleDriver {
16+
pub fn new(
17+
dev_id: u16,
18+
mut registers: VolatileRef<'static, DeviceRegisters>,
19+
irq: InterruptLine,
20+
) -> Result<VirtioConsoleDriver, VirtioError> {
21+
let dev_cfg_raw: &'static Config = unsafe {
22+
&*registers
23+
.borrow_mut()
24+
.as_mut_ptr()
25+
.config()
26+
.as_raw_ptr()
27+
.cast::<Config>()
28+
.as_ptr()
29+
};
30+
let dev_cfg_raw = VolatileRef::from_ref(dev_cfg_raw);
31+
let dev_cfg = ConsoleDevCfg {
32+
raw: dev_cfg_raw,
33+
dev_id,
34+
features: virtio::console::F::empty(),
35+
};
36+
let isr_stat = IsrStatus::new(registers.borrow_mut());
37+
let notif_cfg = NotifCfg::new(registers.borrow_mut());
38+
39+
Ok(VirtioConsoleDriver {
40+
dev_cfg,
41+
com_cfg: ComCfg::new(registers, 1),
42+
isr_stat,
43+
notif_cfg,
44+
irq,
45+
recv_vq: RxQueue::new(),
46+
send_vq: TxQueue::new(),
47+
})
48+
}
49+
50+
/// Initializes virtio console device by mapping configuration layout to
51+
/// respective structs (configuration structs are:
52+
///
53+
/// Returns a driver instance of
54+
/// [VirtioConsoleDriver](structs.virtionetdriver.html) or an [VirtioError](enums.virtioerror.html).
55+
pub fn init(
56+
dev_id: u16,
57+
registers: VolatileRef<'static, DeviceRegisters>,
58+
irq: InterruptLine,
59+
) -> Result<VirtioConsoleDriver, VirtioError> {
60+
let mut drv = VirtioConsoleDriver::new(dev_id, registers, irq)?;
61+
drv.init_dev()
62+
.map_err(|error_code| VirtioError::ConsoleDriver(error_code))?;
63+
Ok(drv)
64+
}
65+
}

0 commit comments

Comments
 (0)