1#[cfg(feature = "uhyve")]
2mod uhyve;
3
4#[cfg(feature = "virtio-console")]
5use alloc::boxed::Box;
6use core::{fmt, mem};
7
8use embedded_io::{ErrorType, Read, ReadReady, Write};
9use heapless::Vec;
10use hermit_sync::{InterruptTicketMutex, Lazy};
11
12use crate::arch::kernel::serial::SerialDevice;
13#[cfg(feature = "virtio-console")]
14use crate::drivers::console::VirtioConsoleDriver;
15use crate::errno::Errno;
16use crate::executor::WakerRegistration;
17
18const SERIAL_BUFFER_SIZE: usize = 256;
19
20pub(crate) enum IoDevice {
21 #[cfg(feature = "uhyve")]
22 Uhyve(uhyve::UhyveSerial),
23 Uart(SerialDevice),
24 #[cfg(feature = "virtio-console")]
25 Virtio(Box<VirtioConsoleDriver>),
26}
27
28impl ErrorType for IoDevice {
29 type Error = Errno;
30}
31
32impl Read for IoDevice {
33 fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
34 match self {
35 #[cfg(feature = "uhyve")]
36 IoDevice::Uhyve(s) => s.read(buf),
37 IoDevice::Uart(s) => s.read(buf),
38 #[cfg(feature = "virtio-console")]
39 IoDevice::Virtio(s) => s.read(buf),
40 }
41 }
42}
43
44impl ReadReady for IoDevice {
45 fn read_ready(&mut self) -> Result<bool, Self::Error> {
46 match self {
47 #[cfg(feature = "uhyve")]
48 IoDevice::Uhyve(s) => s.read_ready(),
49 IoDevice::Uart(s) => s.read_ready(),
50 #[cfg(feature = "virtio-console")]
51 IoDevice::Virtio(s) => s.read_ready(),
52 }
53 }
54}
55
56impl Write for IoDevice {
57 fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
58 match self {
59 #[cfg(feature = "uhyve")]
60 IoDevice::Uhyve(s) => s.write_all(buf)?,
61 IoDevice::Uart(s) => s.write_all(buf)?,
62 #[cfg(feature = "virtio-console")]
63 IoDevice::Virtio(s) => s.write_all(buf)?,
64 };
65
66 #[cfg(all(target_arch = "x86_64", feature = "vga"))]
67 for &byte in buf {
68 crate::arch::kernel::vga::write_byte(byte);
71 }
72
73 Ok(buf.len())
74 }
75
76 fn flush(&mut self) -> Result<(), Self::Error> {
77 Ok(())
78 }
79}
80
81pub(crate) struct Console {
82 pub device: IoDevice,
83 buffer: Vec<u8, SERIAL_BUFFER_SIZE>,
84}
85
86impl Console {
87 pub fn new(device: IoDevice) -> Self {
88 Self {
89 device,
90 buffer: Vec::new(),
91 }
92 }
93}
94
95#[cfg(feature = "virtio-console")]
96pub(crate) fn switch_to_virtio(device: VirtioConsoleDriver) {
97 info!("Switch to virtio console");
98 CONSOLE.lock().device = IoDevice::Virtio(Box::new(device));
99}
100
101impl ErrorType for Console {
102 type Error = Errno;
103}
104
105impl Read for Console {
106 fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
107 self.device.read(buf)
108 }
109}
110
111impl ReadReady for Console {
112 fn read_ready(&mut self) -> Result<bool, Self::Error> {
113 self.device.read_ready()
114 }
115}
116
117impl Write for Console {
118 fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
122 if SERIAL_BUFFER_SIZE - self.buffer.len() >= buf.len() {
123 self.buffer.extend_from_slice(buf).unwrap();
125 if buf.contains(&b'\n') {
126 self.flush()?;
127 }
128 } else {
129 self.device.write_all(&self.buffer)?;
130 self.buffer.clear();
131 if buf.len() >= SERIAL_BUFFER_SIZE {
132 self.device.write_all(buf)?;
133 } else {
134 self.buffer.extend_from_slice(buf).unwrap();
136 if buf.contains(&b'\n') {
137 self.flush()?;
138 }
139 }
140 }
141
142 Ok(buf.len())
143 }
144
145 fn flush(&mut self) -> Result<(), Self::Error> {
147 if !self.buffer.is_empty() {
148 self.device.write_all(&self.buffer)?;
149 self.buffer.clear();
150 }
151 Ok(())
152 }
153}
154
155pub(crate) static CONSOLE_WAKER: InterruptTicketMutex<WakerRegistration> =
156 InterruptTicketMutex::new(WakerRegistration::new());
157pub(crate) static CONSOLE: Lazy<InterruptTicketMutex<Console>> = Lazy::new(|| {
158 use crate::arch::kernel::core_local::CoreLocal;
159
160 CoreLocal::install();
161
162 #[cfg(feature = "uhyve")]
163 use crate::env::{self, UhyveStartInfo};
164
165 #[cfg(feature = "uhyve")]
166 if env::start_info().is_uhyve() {
167 return InterruptTicketMutex::new(Console::new(IoDevice::Uhyve(uhyve::UhyveSerial::new())));
168 }
169
170 InterruptTicketMutex::new(Console::new(IoDevice::Uart(SerialDevice::new())))
171});
172
173#[doc(hidden)]
174pub fn _print(args: fmt::Arguments<'_>) {
175 CONSOLE.lock().write_fmt(args).unwrap();
176}
177
178#[doc(hidden)]
179pub fn _panic_print(args: fmt::Arguments<'_>) {
180 let mut console = unsafe { CONSOLE.make_guard_unchecked() };
181 console.write_fmt(args).ok();
182 mem::forget(console);
183}
184
185#[cfg(all(test, not(target_os = "none")))]
186mod tests {
187 use super::*;
188
189 #[test]
190 fn test_console() {
191 println!("HelloWorld");
192 }
193}