1#![allow(dead_code)]
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 = "console")]
11use crate::drivers::console::VirtioUART;
12use crate::errno::Errno;
13use crate::executor::WakerRegistration;
14#[cfg(not(target_arch = "riscv64"))]
15use crate::syscalls::interfaces::serial_buf_hypercall;
16
17const SERIAL_BUFFER_SIZE: usize = 256;
18
19pub(crate) enum IoDevice {
20 #[cfg(not(target_arch = "riscv64"))]
21 Uhyve(UhyveSerial),
22 Uart(SerialDevice),
23 #[cfg(feature = "console")]
24 Virtio(VirtioUART),
25}
26
27impl ErrorType for IoDevice {
28 type Error = Errno;
29}
30
31impl Read for IoDevice {
32 fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
33 match self {
34 #[cfg(not(target_arch = "riscv64"))]
35 IoDevice::Uhyve(s) => s.read(buf),
36 IoDevice::Uart(s) => s.read(buf),
37 #[cfg(feature = "console")]
38 IoDevice::Virtio(s) => s.read(buf),
39 }
40 }
41}
42
43impl ReadReady for IoDevice {
44 fn read_ready(&mut self) -> Result<bool, Self::Error> {
45 match self {
46 #[cfg(not(target_arch = "riscv64"))]
47 IoDevice::Uhyve(s) => s.read_ready(),
48 IoDevice::Uart(s) => s.read_ready(),
49 #[cfg(feature = "console")]
50 IoDevice::Virtio(s) => s.read_ready(),
51 }
52 }
53}
54
55impl Write for IoDevice {
56 fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
57 match self {
58 #[cfg(not(target_arch = "riscv64"))]
59 IoDevice::Uhyve(s) => s.write_all(buf)?,
60 IoDevice::Uart(s) => s.write_all(buf)?,
61 #[cfg(feature = "console")]
62 IoDevice::Virtio(s) => s.write_all(buf)?,
63 };
64
65 #[cfg(all(target_arch = "x86_64", feature = "vga"))]
66 for &byte in buf {
67 crate::arch::kernel::vga::write_byte(byte);
70 }
71
72 Ok(buf.len())
73 }
74
75 fn flush(&mut self) -> Result<(), Self::Error> {
76 Ok(())
77 }
78}
79
80#[cfg(not(target_arch = "riscv64"))]
81pub(crate) struct UhyveSerial;
82
83#[cfg(not(target_arch = "riscv64"))]
84impl UhyveSerial {
85 pub const fn new() -> Self {
86 Self {}
87 }
88}
89
90#[cfg(not(target_arch = "riscv64"))]
91impl ErrorType for UhyveSerial {
92 type Error = Errno;
93}
94
95#[cfg(not(target_arch = "riscv64"))]
96impl Read for UhyveSerial {
97 fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
98 let _ = buf;
99 Ok(0)
100 }
101}
102
103#[cfg(not(target_arch = "riscv64"))]
104impl ReadReady for UhyveSerial {
105 fn read_ready(&mut self) -> Result<bool, Self::Error> {
106 Ok(false)
107 }
108}
109
110#[cfg(not(target_arch = "riscv64"))]
111impl Write for UhyveSerial {
112 fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
113 serial_buf_hypercall(buf);
114 Ok(buf.len())
115 }
116
117 fn flush(&mut self) -> Result<(), Self::Error> {
118 Ok(())
119 }
120}
121
122pub(crate) struct Console {
123 device: IoDevice,
124 buffer: Vec<u8, SERIAL_BUFFER_SIZE>,
125}
126
127impl Console {
128 pub fn new(device: IoDevice) -> Self {
129 Self {
130 device,
131 buffer: Vec::new(),
132 }
133 }
134
135 #[cfg(feature = "console")]
136 pub fn replace_device(&mut self, device: IoDevice) {
137 self.device = device;
138 }
139}
140
141impl ErrorType for Console {
142 type Error = Errno;
143}
144
145impl Read for Console {
146 fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
147 self.device.read(buf)
148 }
149}
150
151impl ReadReady for Console {
152 fn read_ready(&mut self) -> Result<bool, Self::Error> {
153 self.device.read_ready()
154 }
155}
156
157impl Write for Console {
158 fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
162 if SERIAL_BUFFER_SIZE - self.buffer.len() >= buf.len() {
163 self.buffer.extend_from_slice(buf).unwrap();
165 if buf.contains(&b'\n') {
166 self.flush()?;
167 }
168 } else {
169 self.device.write_all(&self.buffer)?;
170 self.buffer.clear();
171 if buf.len() >= SERIAL_BUFFER_SIZE {
172 self.device.write_all(buf)?;
173 } else {
174 self.buffer.extend_from_slice(buf).unwrap();
176 if buf.contains(&b'\n') {
177 self.flush()?;
178 }
179 }
180 }
181
182 Ok(buf.len())
183 }
184
185 fn flush(&mut self) -> Result<(), Self::Error> {
187 if !self.buffer.is_empty() {
188 self.device.write_all(&self.buffer)?;
189 self.buffer.clear();
190 }
191 Ok(())
192 }
193}
194
195pub(crate) static CONSOLE_WAKER: InterruptTicketMutex<WakerRegistration> =
196 InterruptTicketMutex::new(WakerRegistration::new());
197pub(crate) static CONSOLE: Lazy<InterruptTicketMutex<Console>> = Lazy::new(|| {
198 crate::CoreLocal::install();
199
200 #[cfg(not(target_arch = "riscv64"))]
201 if crate::env::is_uhyve() {
202 InterruptTicketMutex::new(Console::new(IoDevice::Uhyve(UhyveSerial::new())))
203 } else {
204 InterruptTicketMutex::new(Console::new(IoDevice::Uart(SerialDevice::new())))
205 }
206 #[cfg(target_arch = "riscv64")]
207 InterruptTicketMutex::new(Console::new(IoDevice::Uart(SerialDevice::new())))
208});
209
210#[doc(hidden)]
211pub fn _print(args: fmt::Arguments<'_>) {
212 CONSOLE.lock().write_fmt(args).unwrap();
213}
214
215#[doc(hidden)]
216pub fn _panic_print(args: fmt::Arguments<'_>) {
217 let mut console = unsafe { CONSOLE.make_guard_unchecked() };
218 console.write_fmt(args).ok();
219 mem::forget(console);
220}
221
222#[cfg(all(test, not(target_os = "none")))]
223mod tests {
224 use super::*;
225
226 #[test]
227 fn test_console() {
228 println!("HelloWorld");
229 }
230}