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