1#![allow(dead_code)]
2
3use core::mem::MaybeUninit;
4use core::{fmt, mem};
5
6use heapless::Vec;
7use hermit_sync::{InterruptTicketMutex, Lazy};
8
9use crate::arch::SerialDevice;
10#[cfg(feature = "console")]
11use crate::drivers::console::VirtioUART;
12use crate::executor::WakerRegistration;
13use crate::io;
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 IoDevice {
28 pub fn write(&self, buf: &[u8]) {
29 match self {
30 #[cfg(not(target_arch = "riscv64"))]
31 IoDevice::Uhyve(s) => s.write(buf),
32 IoDevice::Uart(s) => s.write(buf),
33 #[cfg(feature = "console")]
34 IoDevice::Virtio(s) => s.write(buf),
35 }
36
37 #[cfg(all(target_arch = "x86_64", feature = "vga"))]
38 for &byte in buf {
39 crate::arch::kernel::vga::write_byte(byte);
42 }
43 }
44
45 pub fn read(&self, buf: &mut [MaybeUninit<u8>]) -> io::Result<usize> {
46 match self {
47 #[cfg(not(target_arch = "riscv64"))]
48 IoDevice::Uhyve(s) => s.read(buf),
49 IoDevice::Uart(s) => s.read(buf),
50 #[cfg(feature = "console")]
51 IoDevice::Virtio(s) => s.read(buf),
52 }
53 }
54
55 pub fn can_read(&self) -> bool {
56 match self {
57 #[cfg(not(target_arch = "riscv64"))]
58 IoDevice::Uhyve(s) => s.can_read(),
59 IoDevice::Uart(s) => s.can_read(),
60 #[cfg(feature = "console")]
61 IoDevice::Virtio(s) => s.can_read(),
62 }
63 }
64}
65
66#[cfg(not(target_arch = "riscv64"))]
67pub(crate) struct UhyveSerial;
68
69#[cfg(not(target_arch = "riscv64"))]
70impl UhyveSerial {
71 pub const fn new() -> Self {
72 Self {}
73 }
74
75 pub fn write(&self, buf: &[u8]) {
76 serial_buf_hypercall(buf);
77 }
78
79 pub fn read(&self, _buf: &mut [MaybeUninit<u8>]) -> io::Result<usize> {
80 Ok(0)
81 }
82
83 pub fn can_read(&self) -> bool {
84 false
85 }
86}
87
88pub(crate) struct Console {
89 device: IoDevice,
90 buffer: Vec<u8, SERIAL_BUFFER_SIZE>,
91}
92
93impl Console {
94 pub fn new(device: IoDevice) -> Self {
95 Self {
96 device,
97 buffer: Vec::new(),
98 }
99 }
100
101 pub fn write(&mut self, buf: &[u8]) {
105 if SERIAL_BUFFER_SIZE - self.buffer.len() >= buf.len() {
106 self.buffer.extend_from_slice(buf).unwrap();
108 if buf.contains(&b'\n') {
109 self.flush();
110 }
111 } else {
112 self.device.write(&self.buffer);
113 self.buffer.clear();
114 if buf.len() >= SERIAL_BUFFER_SIZE {
115 self.device.write(buf);
116 } else {
117 self.buffer.extend_from_slice(buf).unwrap();
119 if buf.contains(&b'\n') {
120 self.flush();
121 }
122 }
123 }
124 }
125
126 pub fn flush(&mut self) {
128 if !self.buffer.is_empty() {
129 self.device.write(&self.buffer);
130 self.buffer.clear();
131 }
132 }
133
134 pub fn read(&mut self, buf: &mut [MaybeUninit<u8>]) -> io::Result<usize> {
135 self.device.read(buf)
136 }
137
138 pub fn can_read(&self) -> bool {
139 self.device.can_read()
140 }
141
142 #[cfg(feature = "console")]
143 pub fn replace_device(&mut self, device: IoDevice) {
144 self.device = device;
145 }
146}
147
148impl fmt::Write for Console {
151 #[inline]
153 fn write_str(&mut self, s: &str) -> fmt::Result {
154 if !s.is_empty() {
155 self.write(s.as_bytes());
156 }
157
158 Ok(())
159 }
160}
161
162pub(crate) static CONSOLE_WAKER: InterruptTicketMutex<WakerRegistration> =
163 InterruptTicketMutex::new(WakerRegistration::new());
164pub(crate) static CONSOLE: Lazy<InterruptTicketMutex<Console>> = Lazy::new(|| {
165 crate::CoreLocal::install();
166
167 #[cfg(not(target_arch = "riscv64"))]
168 if crate::env::is_uhyve() {
169 InterruptTicketMutex::new(Console::new(IoDevice::Uhyve(UhyveSerial::new())))
170 } else {
171 InterruptTicketMutex::new(Console::new(IoDevice::Uart(SerialDevice::new())))
172 }
173 #[cfg(target_arch = "riscv64")]
174 InterruptTicketMutex::new(Console::new(IoDevice::Uart(SerialDevice::new())))
175});
176
177#[doc(hidden)]
178pub fn _print(args: fmt::Arguments<'_>) {
179 use fmt::Write;
180 CONSOLE.lock().write_fmt(args).unwrap();
181}
182
183#[doc(hidden)]
184pub fn _panic_print(args: fmt::Arguments<'_>) {
185 use fmt::Write;
186 let mut console = unsafe { CONSOLE.make_guard_unchecked() };
187 console.write_fmt(args).ok();
188 mem::forget(console);
189}
190
191#[cfg(all(test, not(target_os = "none")))]
192mod tests {
193 use super::*;
194
195 #[test]
196 fn test_console() {
197 println!("HelloWorld");
198 }
199}