Skip to content

Commit 030c2be

Browse files
stlankesmkroening
authored andcommitted
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 f3f81bc commit 030c2be

File tree

21 files changed

+972
-43
lines changed

21 files changed

+972
-43
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ harness = false
4848
default = ["pci", "pci-ids", "acpi", "fsgsbase", "smp", "tcp", "dhcpv4", "fuse", "vsock"]
4949
acpi = []
5050
common-os = []
51+
console = []
5152
dhcpv4 = ["smoltcp", "smoltcp/proto-dhcpv4", "smoltcp/socket-dhcpv4"]
5253
dns = ["smoltcp", "smoltcp/socket-dns"]
5354
fs = ["fuse"]

src/arch/aarch64/kernel/mmio.rs

Lines changed: 137 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,16 @@ use alloc::vec::Vec;
22
use core::ptr::NonNull;
33

44
use align_address::Align;
5+
use arm_gic::{IntId, Trigger};
56
use hermit_sync::{InterruptTicketMutex, without_interrupts};
67
use virtio::mmio::{DeviceRegisters, DeviceRegistersVolatileFieldAccess};
78
use volatile::VolatileRef;
89

10+
use crate::arch::aarch64::kernel::interrupts::GIC;
911
use crate::arch::aarch64::mm::paging::{self, PageSize};
12+
#[cfg(feature = "console")]
13+
use crate::drivers::console::VirtioConsoleDriver;
14+
#[cfg(any(feature = "tcp", feature = "udp"))]
1015
use crate::drivers::net::virtio::VirtioNetDriver;
1116
use crate::drivers::virtio::transport::mmio::{self as mmio_virtio, VirtioDriver};
1217
use crate::init_cell::InitCell;
@@ -17,13 +22,26 @@ pub(crate) static MMIO_DRIVERS: InitCell<Vec<MmioDriver>> = InitCell::new(Vec::n
1722
pub(crate) enum MmioDriver {
1823
#[cfg(any(feature = "tcp", feature = "udp"))]
1924
VirtioNet(InterruptTicketMutex<VirtioNetDriver>),
25+
#[cfg(feature = "console")]
26+
VirtioConsole(InterruptTicketMutex<VirtioConsoleDriver>),
2027
}
2128

2229
impl MmioDriver {
2330
#[cfg(any(feature = "tcp", feature = "udp"))]
2431
fn get_network_driver(&self) -> Option<&InterruptTicketMutex<VirtioNetDriver>> {
2532
match self {
2633
Self::VirtioNet(drv) => Some(drv),
34+
#[cfg(feature = "console")]
35+
_ => None,
36+
}
37+
}
38+
39+
#[cfg(feature = "console")]
40+
fn get_console_driver(&self) -> Option<&InterruptTicketMutex<VirtioConsoleDriver>> {
41+
match self {
42+
Self::VirtioConsole(drv) => Some(drv),
43+
#[cfg(any(feature = "tcp", feature = "udp"))]
44+
_ => None,
2745
}
2846
}
2947
}
@@ -40,6 +58,14 @@ pub(crate) fn get_network_driver() -> Option<&'static InterruptTicketMutex<Virti
4058
.find_map(|drv| drv.get_network_driver())
4159
}
4260

61+
#[cfg(feature = "console")]
62+
pub(crate) fn get_console_driver() -> Option<&'static InterruptTicketMutex<VirtioConsoleDriver>> {
63+
MMIO_DRIVERS
64+
.get()?
65+
.iter()
66+
.find_map(|drv| drv.get_console_driver())
67+
}
68+
4369
pub fn init_drivers() {
4470
without_interrupts(|| {
4571
if let Some(fdt) = crate::env::fdt() {
@@ -53,12 +79,16 @@ pub fn init_drivers() {
5379
.next()
5480
.unwrap();
5581
let mut irq = 0;
82+
let mut irqtype = 0;
83+
let mut irqflags = 0;
5684

5785
for prop in node.properties() {
5886
if prop.name == "interrupts" {
59-
irq = u32::from_be_bytes(prop.value[4..8].try_into().unwrap())
60-
.try_into()
61-
.unwrap();
87+
irqtype =
88+
u32::from_be_bytes(prop.value[0..4].try_into().unwrap());
89+
irq = u32::from_be_bytes(prop.value[4..8].try_into().unwrap());
90+
irqflags =
91+
u32::from_be_bytes(prop.value[8..12].try_into().unwrap());
6292
break;
6393
}
6494
}
@@ -95,17 +125,100 @@ pub fn init_drivers() {
95125

96126
// Verify the device-ID to find the network card
97127
let id = mmio.as_ptr().device_id().read();
98-
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-
));
128+
let cpu_id: usize = 0;
129+
130+
match id {
131+
#[cfg(any(feature = "tcp", feature = "udp"))]
132+
virtio::Id::Net => {
133+
debug!(
134+
"Found network card at {mmio:p}, irq: {irq}, type: {irqtype}, flags: {irqflags}"
135+
);
136+
if let Ok(VirtioDriver::Network(drv)) =
137+
mmio_virtio::init_device(mmio, irq.try_into().unwrap())
138+
&& let Some(gic) = GIC.lock().as_mut()
139+
{
140+
// enable timer interrupt
141+
let virtio_irqid = if irqtype == 1 {
142+
IntId::ppi(irq)
143+
} else if irqtype == 0 {
144+
IntId::spi(irq)
145+
} else {
146+
panic!("Invalid interrupt type");
147+
};
148+
gic.set_interrupt_priority(
149+
virtio_irqid,
150+
Some(cpu_id),
151+
0x00,
152+
);
153+
if (irqflags & 0xf) == 4 || (irqflags & 0xf) == 8 {
154+
gic.set_trigger(
155+
virtio_irqid,
156+
Some(cpu_id),
157+
Trigger::Level,
158+
);
159+
} else if (irqflags & 0xf) == 2 || (irqflags & 0xf) == 1 {
160+
gic.set_trigger(
161+
virtio_irqid,
162+
Some(cpu_id),
163+
Trigger::Edge,
164+
);
165+
} else {
166+
panic!("Invalid interrupt level!");
167+
}
168+
gic.enable_interrupt(virtio_irqid, Some(cpu_id), true);
169+
170+
register_driver(MmioDriver::VirtioNet(
171+
hermit_sync::InterruptTicketMutex::new(drv),
172+
));
173+
}
174+
}
175+
#[cfg(feature = "console")]
176+
virtio::Id::Console => {
177+
debug!(
178+
"Found console at {mmio:p}, irq: {irq}, type: {irqtype}, flags: {irqflags}"
179+
);
180+
if let Ok(VirtioDriver::Console(drv)) =
181+
mmio_virtio::init_device(mmio, irq.try_into().unwrap())
182+
{
183+
if let Some(gic) = GIC.lock().as_mut() {
184+
// enable timer interrupt
185+
let virtio_irqid = if irqtype == 1 {
186+
IntId::ppi(irq)
187+
} else if irqtype == 0 {
188+
IntId::spi(irq)
189+
} else {
190+
panic!("Invalid interrupt type");
191+
};
192+
gic.set_interrupt_priority(
193+
virtio_irqid,
194+
Some(cpu_id),
195+
0x00,
196+
);
197+
if (irqflags & 0xf) == 4 || (irqflags & 0xf) == 8 {
198+
gic.set_trigger(
199+
virtio_irqid,
200+
Some(cpu_id),
201+
Trigger::Level,
202+
);
203+
} else if (irqflags & 0xf) == 2 || (irqflags & 0xf) == 1
204+
{
205+
gic.set_trigger(
206+
virtio_irqid,
207+
Some(cpu_id),
208+
Trigger::Edge,
209+
);
210+
} else {
211+
panic!("Invalid interrupt level!");
212+
}
213+
gic.enable_interrupt(virtio_irqid, Some(cpu_id), true);
214+
215+
register_driver(MmioDriver::VirtioConsole(
216+
hermit_sync::InterruptTicketMutex::new(*drv),
217+
));
218+
}
219+
}
108220
}
221+
_ => {}
109222
}
110223
}
111224
}
@@ -117,4 +230,15 @@ pub fn init_drivers() {
117230
});
118231

119232
MMIO_DRIVERS.finalize();
233+
234+
#[cfg(feature = "console")]
235+
{
236+
if get_console_driver().is_some() {
237+
info!("Switch to virtio console");
238+
crate::console::CONSOLE
239+
.lock()
240+
.inner
241+
.switch_to_virtio_console();
242+
}
243+
}
120244
}

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(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: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,50 @@
11
use core::arch::asm;
22

3+
#[cfg(all(feature = "pci", feature = "console"))]
4+
use crate::drivers::pci::get_console_driver;
5+
#[cfg(all(not(feature = "pci"), feature = "console"))]
6+
use crate::kernel::mmio::get_console_driver;
37
use crate::syscalls::interfaces::serial_buf_hypercall;
48

59
enum SerialInner {
10+
None,
611
Uart(u32),
712
Uhyve,
13+
#[cfg(feature = "console")]
14+
Virtio,
815
}
916

1017
pub struct SerialPort {
1118
inner: SerialInner,
1219
}
1320

1421
impl SerialPort {
15-
pub fn new(port_address: u32) -> Self {
22+
pub fn new(port_address: Option<u64>) -> Self {
1623
if crate::env::is_uhyve() {
1724
Self {
1825
inner: SerialInner::Uhyve,
1926
}
27+
} else if let Some(port_address) = port_address {
28+
Self {
29+
inner: SerialInner::Uart(port_address.try_into().unwrap()),
30+
}
2031
} else {
2132
Self {
22-
inner: SerialInner::Uart(port_address),
33+
inner: SerialInner::None,
2334
}
2435
}
2536
}
2637

38+
#[cfg(feature = "console")]
39+
pub fn switch_to_virtio_console(&mut self) {
40+
self.inner = SerialInner::Virtio;
41+
}
42+
2743
pub fn write_buf(&mut self, buf: &[u8]) {
2844
match &mut self.inner {
45+
SerialInner::None => {
46+
// No serial port configured, do nothing.
47+
}
2948
SerialInner::Uhyve => {
3049
serial_buf_hypercall(buf);
3150
}
@@ -54,6 +73,12 @@ impl SerialPort {
5473
}
5574
}
5675
}
76+
#[cfg(feature = "console")]
77+
SerialInner::Virtio => {
78+
if let Some(console_driver) = get_console_driver() {
79+
let _ = console_driver.lock().write(buf);
80+
}
81+
}
5782
}
5883
}
5984

src/arch/riscv64/kernel/devicetree.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
#![allow(dead_code)]
2+
13
#[cfg(all(any(feature = "tcp", feature = "udp"), not(feature = "pci")))]
24
use core::ptr::NonNull;
35

@@ -216,6 +218,7 @@ pub fn init_drivers() {
216218
// Verify the device-ID to find the network card
217219
let id = mmio.as_ptr().device_id().read();
218220

221+
#[cfg(any(feature = "tcp", feature = "udp"))]
219222
if id != virtio::Id::Net {
220223
debug!("It's not a network card at {mmio:p}");
221224
return;

0 commit comments

Comments
 (0)