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