diff --git a/src/arch/aarch64/console.rs b/src/arch/aarch64/console.rs new file mode 100644 index 00000000..911f8a6c --- /dev/null +++ b/src/arch/aarch64/console.rs @@ -0,0 +1,60 @@ +use core::ptr::NonNull; + +use hermit_dtb::Dtb; + +pub struct Console { + stdout: NonNull, +} + +fn stdout() -> u32 { + /// Physical address of UART0 at Qemu's virt emulation + const SERIAL_PORT_ADDRESS: u32 = 0x09000000; + + let dtb = unsafe { + Dtb::from_raw(sptr::from_exposed_addr(super::DEVICE_TREE as usize)) + .expect(".dtb file has invalid header") + }; + + let property = dtb.get_property("/chosen", "stdout-path"); + let uart_address = if let Some(stdout) = property { + let stdout = core::str::from_utf8(stdout) + .unwrap() + .trim_matches(char::from(0)); + if let Some(pos) = stdout.find('@') { + let len = stdout.len(); + u32::from_str_radix(&stdout[pos + 1..len], 16).unwrap_or(SERIAL_PORT_ADDRESS) + } else { + SERIAL_PORT_ADDRESS + } + } else { + SERIAL_PORT_ADDRESS + }; + uart_address +} + +impl Console { + pub fn write_bytes(&mut self, bytes: &[u8]) { + for byte in bytes.iter().copied() { + unsafe { + self.stdout.as_ptr().write_volatile(byte); + } + } + } + + pub(super) fn get_stdout(&self) -> NonNull { + self.stdout + } + + pub(super) fn set_stdout(&mut self, stdout: NonNull) { + self.stdout = stdout; + } +} + +impl Default for Console { + fn default() -> Self { + let stdout = NonNull::new(stdout() as *mut u8).unwrap(); + Self { stdout } + } +} + +unsafe impl Send for Console {} diff --git a/src/arch/aarch64/mod.rs b/src/arch/aarch64/mod.rs index 693fba6e..134fabbc 100644 --- a/src/arch/aarch64/mod.rs +++ b/src/arch/aarch64/mod.rs @@ -1,9 +1,10 @@ +mod console; +pub use self::console::Console; pub mod entry; pub mod paging; -pub mod serial; use core::arch::asm; -use core::ptr; +use core::ptr::{self, NonNull}; use align_address::Align; use goblin::elf::header::header64::{Header, EI_DATA, ELFDATA2LSB, ELFMAG, SELFMAG}; @@ -15,7 +16,7 @@ use log::info; use sptr::Strict; use crate::arch::paging::*; -use crate::arch::serial::SerialPort; +use crate::os::CONSOLE; extern "C" { static kernel_end: u8; @@ -29,8 +30,6 @@ extern "C" { /// start address of the RAM at Qemu's virt emulation const RAM_START: u64 = 0x40000000; -/// Physical address of UART0 at Qemu's virt emulation -const SERIAL_PORT_ADDRESS: u32 = 0x09000000; /// Default stack size of the kernel const KERNEL_STACK_SIZE: usize = 32_768; /// Qemu assumes for ELF kernel that the DTB is located at @@ -45,43 +44,6 @@ const PT_MEM: u64 = 0x713; const PT_MEM_CD: u64 = 0x70F; const PT_SELF: u64 = 1 << 55; -// VARIABLES -static mut COM1: SerialPort = SerialPort::new(SERIAL_PORT_ADDRESS); - -pub fn message_output_init() { - let dtb = unsafe { - Dtb::from_raw(sptr::from_exposed_addr(DEVICE_TREE as usize)) - .expect(".dtb file has invalid header") - }; - - let property = dtb.get_property("/chosen", "stdout-path"); - let uart_address = if let Some(stdout) = property { - let stdout = core::str::from_utf8(stdout) - .unwrap() - .trim_matches(char::from(0)); - if let Some(pos) = stdout.find('@') { - let len = stdout.len(); - u32::from_str_radix(&stdout[pos + 1..len], 16).unwrap_or(SERIAL_PORT_ADDRESS) - } else { - SERIAL_PORT_ADDRESS - } - } else { - SERIAL_PORT_ADDRESS - }; - - unsafe { - COM1.set_port(uart_address); - } -} - -pub fn write_to_console(bytes: &[u8]) { - for byte in bytes.iter().copied() { - unsafe { - COM1.write_byte(byte); - } - } -} - pub unsafe fn get_memory(_memory_size: u64) -> u64 { (unsafe { ptr::addr_of!(kernel_end) }.addr() as u64).align_up(LargePageSize::SIZE as u64) } @@ -154,7 +116,7 @@ pub unsafe fn boot_kernel(kernel_info: LoadedKernel) -> ! { .count(); info!("Detect {} CPU(s)", cpus); - let uart_address: u32 = unsafe { COM1.get_port() }; + let uart_address: u32 = CONSOLE.lock().get().get_stdout().as_ptr() as u32; info!("Detect UART at {:#x}", uart_address); let pgt_slice = unsafe { core::slice::from_raw_parts_mut(ptr::addr_of_mut!(l0_pgtable), 512) }; @@ -200,9 +162,10 @@ pub unsafe fn boot_kernel(kernel_info: LoadedKernel) -> ! { *entry = RAM_START + (i * BasePageSize::SIZE) as u64 + PT_MEM; } - unsafe { - COM1.set_port(0x1000); - } + CONSOLE + .lock() + .get() + .set_stdout(NonNull::new(0x1000 as *mut u8).unwrap()); // Load TTBRx unsafe { diff --git a/src/arch/aarch64/serial.rs b/src/arch/aarch64/serial.rs index bdc51c0c..e69de29b 100644 --- a/src/arch/aarch64/serial.rs +++ b/src/arch/aarch64/serial.rs @@ -1,33 +0,0 @@ -pub struct SerialPort { - port_address: u32, -} - -impl SerialPort { - pub const fn new(port_address: u32) -> Self { - Self { port_address } - } - - pub unsafe fn set_port(&mut self, addr: u32) { - unsafe { - core::ptr::write_volatile(&mut self.port_address, addr); - } - } - - pub unsafe fn get_port(&self) -> u32 { - unsafe { core::ptr::read_volatile(&self.port_address) } - } - - pub fn write_byte(&self, byte: u8) { - unsafe { - let port = - sptr::from_exposed_addr_mut(core::ptr::read_volatile(&self.port_address) as usize); - - // LF newline characters need to be extended to CRLF over a real serial port. - if byte == b'\n' { - core::ptr::write_volatile(port, b'\r'); - } - - core::ptr::write_volatile(port, byte); - } - } -} diff --git a/src/arch/riscv64/console.rs b/src/arch/riscv64/console.rs new file mode 100644 index 00000000..0726cd72 --- /dev/null +++ b/src/arch/riscv64/console.rs @@ -0,0 +1,11 @@ +use sbi_rt::Physical; +use sptr::Strict; + +#[derive(Default)] +pub struct Console(()); + +impl Console { + pub fn write_bytes(&mut self, bytes: &[u8]) { + sbi_rt::console_write(Physical::new(bytes.len(), bytes.as_ptr().addr(), 0)); + } +} diff --git a/src/arch/riscv64/mod.rs b/src/arch/riscv64/mod.rs index 48a5358e..b42ff143 100644 --- a/src/arch/riscv64/mod.rs +++ b/src/arch/riscv64/mod.rs @@ -1,3 +1,5 @@ +mod console; +pub use self::console::Console; mod address_range; mod start; @@ -12,15 +14,8 @@ use hermit_entry::boot_info::{ use hermit_entry::elf::LoadedKernel; use hermit_entry::Entry; use log::info; -use sbi_rt::Physical; use sptr::Strict; -pub fn message_output_init() {} - -pub fn write_to_console(bytes: &[u8]) { - sbi_rt::console_write(Physical::new(bytes.len(), bytes.as_ptr().addr(), 0)); -} - fn find_kernel_linux(chosen: &FdtNode<'_, '_>) -> Option<&'static [u8]> { let initrd_start = chosen.property("linux,initrd-start")?.as_usize()?; let initrd_start = sptr::from_exposed_addr_mut::(initrd_start); diff --git a/src/arch/x86_64/console.rs b/src/arch/x86_64/console.rs new file mode 100644 index 00000000..9642c225 --- /dev/null +++ b/src/arch/x86_64/console.rs @@ -0,0 +1,21 @@ +use uart_16550::SerialPort; + +pub struct Console { + serial_port: SerialPort, +} + +impl Console { + pub fn write_bytes(&mut self, bytes: &[u8]) { + for byte in bytes.iter().copied() { + self.serial_port.send(byte); + } + } +} + +impl Default for Console { + fn default() -> Self { + let mut serial_port = unsafe { SerialPort::new(0x3F8) }; + serial_port.init(); + Self { serial_port } + } +} diff --git a/src/arch/x86_64/mod.rs b/src/arch/x86_64/mod.rs index f644c60a..99985e55 100644 --- a/src/arch/x86_64/mod.rs +++ b/src/arch/x86_64/mod.rs @@ -1,6 +1,8 @@ +mod console; #[cfg(all(target_os = "none", not(feature = "fc")))] mod fdt; mod paging; +pub use console::Console; mod physicalmem; use core::arch::asm; @@ -30,7 +32,6 @@ use multiboot::information::MemoryManagement; #[cfg(all(target_os = "none", not(feature = "fc")))] use multiboot::information::{Multiboot, PAddr}; use sptr::Strict; -use uart_16550::SerialPort; use x86_64::structures::paging::{PageSize, PageTableFlags, Size2MiB, Size4KiB}; #[cfg(all(target_os = "none", not(feature = "fc")))] @@ -50,9 +51,6 @@ extern "C" { const KERNEL_STACK_SIZE: u64 = 32_768; const SERIAL_IO_PORT: u16 = 0x3F8; -// VARIABLES -static mut COM1: SerialPort = unsafe { SerialPort::new(SERIAL_IO_PORT) }; - #[cfg(all(target_os = "none", not(feature = "fc")))] struct Mem; @@ -84,17 +82,6 @@ mod entry { core::arch::global_asm!(include_str!("entry_fc.s")); } -// FUNCTIONS -pub fn message_output_init() { - unsafe { COM1.init() }; -} - -pub fn write_to_console(bytes: &[u8]) { - for byte in bytes.iter().copied() { - unsafe { COM1.send(byte) }; - } -} - #[cfg(all(target_os = "none", feature = "fc"))] pub fn find_kernel() -> &'static [u8] { use core::cmp; diff --git a/src/os/none/console.rs b/src/os/none/console.rs index a1f0fd8f..bf0d4b51 100644 --- a/src/os/none/console.rs +++ b/src/os/none/console.rs @@ -2,13 +2,30 @@ use core::fmt; use one_shot_mutex::OneShotMutex; -pub struct Console(()); +use crate::arch; + +pub struct Console { + console: Option, +} + +impl Console { + const fn new() -> Self { + Self { console: None } + } + + #[cfg(target_arch = "aarch64")] + pub fn get(&mut self) -> &mut arch::Console { + self.console.get_or_insert_with(arch::Console::default) + } +} impl fmt::Write for Console { fn write_str(&mut self, s: &str) -> fmt::Result { - crate::arch::write_to_console(s.as_bytes()); + self.console + .get_or_insert_with(arch::Console::default) + .write_bytes(s.as_bytes()); Ok(()) } } -pub static CONSOLE: OneShotMutex = OneShotMutex::new(Console(())); +pub static CONSOLE: OneShotMutex = OneShotMutex::new(Console::new()); diff --git a/src/os/none/mod.rs b/src/os/none/mod.rs index 81b7caee..11b63e82 100644 --- a/src/os/none/mod.rs +++ b/src/os/none/mod.rs @@ -18,7 +18,6 @@ extern "C" { /// (called from entry.asm or entry.rs) #[no_mangle] pub(crate) unsafe extern "C" fn loader_main() -> ! { - arch::message_output_init(); crate::log::init(); unsafe {