Skip to main content

hermit/console/
mod.rs

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			// vga::write_byte() checks if VGA support has been initialized,
62			// so we don't need any additional if clause around it.
63			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	/// Writes a buffer to the console.
111	/// The content is buffered until a newline is encountered or the internal buffer is full.
112	/// To force early output, use [`flush`](Self::flush).
113	fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
114		if SERIAL_BUFFER_SIZE - self.buffer.len() >= buf.len() {
115			// unwrap: we checked that buf fits in self.buffer
116			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				// unwrap: we checked that buf fits in self.buffer
127				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	/// Immediately writes everything in the internal buffer to the output.
138	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}