diff --git a/kernel/Cargo.lock b/kernel/Cargo.lock index 45dc146..f742c00 100644 --- a/kernel/Cargo.lock +++ b/kernel/Cargo.lock @@ -2,6 +2,15 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "bitflags" +version = "2.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b588b76d00fde79687d7646a9b5bdf3cc0f655e0bbd080335a95d7e96f3587da" + [[package]] name = "kernel" version = "0.1.0" +dependencies = [ + "bitflags", +] diff --git a/kernel/Cargo.toml b/kernel/Cargo.toml index 8b30eaa..a6c1c0b 100644 --- a/kernel/Cargo.toml +++ b/kernel/Cargo.toml @@ -14,3 +14,4 @@ test = false bench = false [dependencies] +bitflags = "2.13.1" diff --git a/kernel/src/lib.rs b/kernel/src/lib.rs index 2f3cee5..0921691 100644 --- a/kernel/src/lib.rs +++ b/kernel/src/lib.rs @@ -2,6 +2,7 @@ #![feature(const_trait_impl, const_default)] pub mod limine; +pub mod serial; pub mod sync; pub mod x86_64; diff --git a/kernel/src/main.rs b/kernel/src/main.rs index e6c3a01..810efc3 100644 --- a/kernel/src/main.rs +++ b/kernel/src/main.rs @@ -3,11 +3,13 @@ #[panic_handler] fn panic(_info: &core::panic::PanicInfo) -> ! { + kernel::serial_println!("[PANIC] {}", _info); kernel::x86_64::halt_loop() } #[unsafe(no_mangle)] fn _start() -> ! { + kernel::serial_println!("Hello, world!"); assert!(limine_requests::LIMINE_BASE_REVISION.is_supported()); let fb = limine_requests::FRAMEBUFFER_REQUEST diff --git a/kernel/src/serial.rs b/kernel/src/serial.rs new file mode 100644 index 0000000..281b6b8 --- /dev/null +++ b/kernel/src/serial.rs @@ -0,0 +1,808 @@ +pub mod uart_16550 { + #![cfg(target_arch = "x86_64")] + use crate::x86_64::PortU16; + + use core::mem::MaybeUninit; + + pub struct Uart16550 { + pub base_port: PortU16, + pub baud_rate: BaudRate, + pub parity: Parity, + pub stop_bits: StopBits, + pub data_bits: DataBits, + pub fifo_trigger_level: Option, + pub interrupt_enable: InterruptEnable, + } + + impl Uart16550 { + /// Transmitter/Receiver Holding Register offset + pub const DATA: u16 = 0x0; + /// Interrupt Enable Register offset + pub const IER: u16 = 0x1; + /// Interrupt Status Register offset + pub const ISR: u16 = 0x2; + /// FIFO Control Register offset + pub const FCR: u16 = 0x2; + /// Line Control Register offset + pub const LCR: u16 = 0x3; + /// Modem Control Register offset + pub const MCR: u16 = 0x4; + /// Line Status Register offset + pub const LSR: u16 = 0x5; + /// Modem Status Register offset + pub const MSR: u16 = 0x6; + /// Scratch Pad Register offset + pub const SPR: u16 = 0x7; + + /// Divisor Latch Low offset + pub const DLL: u16 = 0x0; + /// Divisor Latch High offset + pub const DLM: u16 = 0x1; + + /// # Safety + /// This function creates a new instance of Uart16550 with the given base port. + pub unsafe fn with_port(base_port: u16) -> Self { + Self { + base_port: PortU16(base_port), + baud_rate: BaudRate::default(), + parity: Parity::default(), + stop_bits: StopBits::default(), + data_bits: DataBits::default(), + fifo_trigger_level: Some(FifoTriggerLevel::default()), + interrupt_enable: InterruptEnable::default(), + } + } + + pub fn init(&mut self) { + unsafe { + self.base_port.offset(Self::IER).write(0); + self.base_port.offset(Self::LCR).write(0); + } + + self.set_baud_rate(self.baud_rate); + + let lcr = LineControl::new(false, false, self.parity, self.stop_bits, self.data_bits); + unsafe { + self.write_lcr(lcr); + + let mut fcr = FifoControl::empty(); + if let Some(level) = self.fifo_trigger_level { + fcr.set_fifo_enable(true); + fcr.set_fifo_trigger_level(level); + } + fcr.set_clear_receive_fifo(); + fcr.set_clear_transmit_fifo(); + + self.write_fcr(fcr); + + let mut mcr = ModemControl::empty(); + mcr.set_data_terminal_ready(true); + mcr.set_request_to_send(true); + mcr.set_auxiliary_output_2(true); + self.write_mcr(mcr); + + self.write_ier(self.interrupt_enable); + + loop { + let lsr = self.read_lsr(); + if lsr.transmitter_holding_register_empty() { + break; + } + + core::hint::spin_loop(); + } + } + } + + pub fn ready_to_receive(&self) -> bool { + let lsr = self.read_lsr(); + lsr.data_ready() + } + + pub fn ready_to_send(&self) -> bool { + let lsr = self.read_lsr(); + let msr = self.read_msr(); + let mcr = self.read_mcr(); + + if !lsr.transmitter_holding_register_empty() { + return false; + } + + if !mcr.loopback_mode() && !msr.clear_to_send() { + return false; + } + + true + } + + pub fn try_receive_byte(&self) -> Option { + if self.ready_to_receive() { + let data_port = self.base_port.offset(Self::DATA); + let value = unsafe { data_port.read() } as u8; + Some(value) + } else { + None + } + } + + pub fn try_send_bytes(&self, bytes: &[u8]) -> Option { + if bytes.is_empty() { + return Some(0); + } + + if !self.ready_to_send() { + return None; + } + + let bytes = match self.fifo_trigger_level { + None => &bytes[..1], + Some(level) => { + let end = core::cmp::min(bytes.len(), level.count()); + &bytes[..end] + } + }; + + for &b in bytes { + unsafe { + self.base_port.offset(Self::DATA).write(b as u16); + } + } + + Some(bytes.len()) + } + + pub fn try_send_byte(&self, byte: u8) -> Option<()> { + if self.ready_to_send() { + let data_port = self.base_port.offset(Self::DATA); + unsafe { data_port.write(byte as u16) }; + Some(()) + } else { + None + } + } + + pub fn try_receive_bytes<'a>( + &self, + buffer: &'a mut [MaybeUninit], + ) -> (&'a mut [u8], &'a mut [MaybeUninit]) { + let len = buffer + .iter_mut() + .map_while(|b: &mut MaybeUninit| { + self.try_receive_byte().map(|val| b.write(val)) + }) + .count(); + + // SAFETY: We have just initialized the first `len` elements of the + // buffer, so it is safe to transmute them to `&mut [u8]`. + unsafe { + let (init, uninit) = buffer.split_at_mut(len); + let init = core::mem::transmute::<&mut [MaybeUninit], &mut [u8]>(init); + (init, uninit) + } + } + + pub fn receive_bytes_exact<'a>(&self, buffer: &'a mut [MaybeUninit]) -> &'a mut [u8] { + let (ptr, len) = (buffer.as_mut_ptr(), buffer.len()); + let mut rest = buffer; + + loop { + rest = self.try_receive_bytes(rest).1; + if rest.is_empty() { + break; + } + } + + unsafe { + // SAFETY: We have just initialized the entire buffer, so it is safe to transmute it to `&mut [u8]`. + core::slice::from_raw_parts_mut(ptr as *mut u8, len) + } + } + + pub fn send_bytes_exact(&self, bytes: &[u8]) { + let mut rest = bytes; + + loop { + let sent = self.try_send_bytes(rest).unwrap_or(0); + rest = &rest[sent..]; + if rest.is_empty() { + break; + } + } + } + + pub fn read_lcr(&self) -> LineControl { + let lcr_port = self.base_port.offset(3); + let value = unsafe { lcr_port.read() } as u8; + LineControl(value) + } + + /// # Safety + /// This function writes to the Line Control Register (LCR) of the UART. + pub unsafe fn write_lcr(&self, lcr: LineControl) { + let lcr_port = self.base_port.offset(Self::LCR); + unsafe { lcr_port.write(lcr.0 as u16) }; + } + + pub fn read_msr(&self) -> ModemStatus { + let msr_port = self.base_port.offset(Self::MSR); + let value = unsafe { msr_port.read() } as u8; + ModemStatus(value) + } + + pub fn read_lsr(&self) -> LineStatus { + let lsr_port = self.base_port.offset(Self::LSR); + let value = unsafe { lsr_port.read() } as u8; + LineStatus(value) + } + + pub fn read_ier(&self) -> InterruptEnable { + let ier_port = self.base_port.offset(Self::IER); + let value = unsafe { ier_port.read() } as u8; + InterruptEnable(value) + } + + /// # Safety + /// This function writes to the Interrupt Enable Register (IER) of the UART. + pub unsafe fn write_ier(&self, ier: InterruptEnable) { + let ier_port = self.base_port.offset(Self::IER); + unsafe { ier_port.write(ier.0 as u16) }; + } + + pub fn read_isr(&self) -> InterruptStatus { + let isr_port = self.base_port.offset(Self::ISR); + let value = unsafe { isr_port.read() } as u8; + InterruptStatus(value) + } + + pub fn read_fcr(&self) -> FifoControl { + let fcr_port = self.base_port.offset(Self::FCR); + let value = unsafe { fcr_port.read() } as u8; + FifoControl(value) + } + + pub fn read_mcr(&self) -> ModemControl { + let mcr_port = self.base_port.offset(Self::MCR); + let value = unsafe { mcr_port.read() } as u8; + ModemControl(value) + } + + /// # Safety + /// This function writes to the Modem Control Register (MCR) of the UART. + pub unsafe fn write_mcr(&self, mcr: ModemControl) { + let mcr_port = self.base_port.offset(Self::MCR); + unsafe { mcr_port.write(mcr.0 as u16) }; + } + + /// # Safety + /// This function writes to the FIFO Control Register (FCR) of the UART. + pub unsafe fn write_fcr(&self, fcr: FifoControl) { + let fcr_port = self.base_port.offset(Self::FCR); + unsafe { fcr_port.write(fcr.0 as u16) }; + } + + pub fn set_baud_rate(&self, baud_rate: BaudRate) { + let divisor = baud_rate.divisor(); + let mut lcr = self.read_lcr(); + lcr.set_dlab(true); + let [low_byte, high_byte] = divisor.to_le_bytes(); + + unsafe { + self.write_lcr(lcr); + self.base_port.offset(Self::DLL).write(low_byte as u16); + self.base_port.offset(Self::DLM).write(high_byte as u16); + lcr.set_dlab(false); + self.write_lcr(lcr); + } + } + } + + const BASE_RATE: u32 = 115200; + + #[derive(Debug, Clone, Copy, Default)] + pub enum BaudRate { + Baud115200, + Baud57600, + Baud38400, + Baud19200, + #[default] + Baud9600, + } + + impl BaudRate { + pub fn divisor(&self) -> u16 { + let rate = match self { + BaudRate::Baud115200 => 115200, + BaudRate::Baud57600 => 57600, + BaudRate::Baud38400 => 38400, + BaudRate::Baud19200 => 19200, + BaudRate::Baud9600 => 9600, + }; + + assert!(BASE_RATE.is_multiple_of(rate), "Invalid baud rate"); + (BASE_RATE / rate) as u16 + } + } + + #[derive(Debug, Clone, Copy)] + #[repr(transparent)] + pub struct LineControl(pub u8); + + #[derive(Debug, Clone, Copy, Default)] + #[repr(u8)] + pub enum DataBits { + Five = 0b00, + Six = 0b01, + Seven = 0b10, + #[default] + Eight = 0b11, + } + + #[derive(Debug, Clone, Copy, Default)] + #[repr(u8)] + pub enum StopBits { + #[default] + One = 0b0, + Two = 0b1, + } + + #[derive(Debug, Clone, Copy, Default)] + #[repr(u8)] + pub enum Parity { + #[default] + None = 0b00, + Odd = 0b01, + Even = 0b11, + Mark = 0b101, + Space = 0b111, + } + + impl LineControl { + pub const fn empty() -> Self { + Self(0) + } + + pub fn dlab(&self) -> bool { + (self.0 & (1 << 7)) != 0 + } + pub fn set_dlab(&mut self, dlab: bool) { + if dlab { + self.0 |= 1 << 7; + } else { + self.0 &= !(1 << 7); + } + } + + pub fn set_break_signal(&mut self, break_signal: bool) { + if break_signal { + self.0 |= 1 << 6; + } else { + self.0 &= !(1 << 6); + } + } + pub fn break_signal(&self) -> bool { + (self.0 & (1 << 6)) != 0 + } + + pub fn set_parity(&mut self, parity: Parity) { + self.0 &= !(0b111 << 3); + self.0 |= (parity as u8) << 3; + } + pub fn parity(&self) -> Parity { + match (self.0 >> 3) & 0b111 { + 0b000 => Parity::None, + 0b001 => Parity::Odd, + 0b011 => Parity::Even, + 0b101 => Parity::Mark, + 0b111 => Parity::Space, + _ => unreachable!(), + } + } + + pub fn set_stop_bits(&mut self, stop_bits: StopBits) { + self.0 &= !(1 << 2); + self.0 |= (stop_bits as u8) << 2; + } + pub fn stop_bits(&self) -> StopBits { + match (self.0 >> 2) & 0b1 { + 0b0 => StopBits::One, + 0b1 => StopBits::Two, + _ => unreachable!(), + } + } + pub fn set_data_bits(&mut self, data_bits: DataBits) { + self.0 &= !(0b11); + self.0 |= data_bits as u8; + } + pub fn data_bits(&self) -> DataBits { + match self.0 & 0b11 { + 0b00 => DataBits::Five, + 0b01 => DataBits::Six, + 0b10 => DataBits::Seven, + 0b11 => DataBits::Eight, + _ => unreachable!(), + } + } + + pub const fn new( + dlab: bool, + break_signal: bool, + parity: Parity, + stop_bits: StopBits, + data_bits: DataBits, + ) -> Self { + let mut value = 0u8; + if dlab { + value |= 1 << 7; + } + if break_signal { + value |= 1 << 6; + } + value |= (parity as u8) << 3; + value |= (stop_bits as u8) << 2; + value |= data_bits as u8; + + Self(value) + } + } + + #[derive(Debug, Clone, Copy)] + #[repr(transparent)] + pub struct InterruptEnable(pub u8); + + impl Default for InterruptEnable { + fn default() -> Self { + let mut empty = Self(0); + empty.set_received_data_available(true); + empty + } + } + + impl InterruptEnable { + pub const fn empty() -> Self { + Self(0) + } + + pub fn set_received_data_available(&mut self, enable: bool) { + if enable { + self.0 |= 1 << 0; + } else { + self.0 &= !(1 << 0); + } + } + pub fn received_data_available(&self) -> bool { + (self.0 & (1 << 0)) != 0 + } + + pub fn set_transmitter_holding_register_empty(&mut self, enable: bool) { + if enable { + self.0 |= 1 << 1; + } else { + self.0 &= !(1 << 1); + } + } + pub fn transmitter_holding_register_empty(&self) -> bool { + (self.0 & (1 << 1)) != 0 + } + + pub fn set_receiver_line_status(&mut self, enable: bool) { + if enable { + self.0 |= 1 << 2; + } else { + self.0 &= !(1 << 2); + } + } + pub fn receiver_line_status(&self) -> bool { + (self.0 & (1 << 2)) != 0 + } + + pub fn set_modem_status(&mut self, enable: bool) { + if enable { + self.0 |= 1 << 3; + } else { + self.0 &= !(1 << 3); + } + } + pub fn modem_status(&self) -> bool { + (self.0 & (1 << 3)) != 0 + } + } + + pub struct FifoControl(pub u8); + + impl FifoControl { + pub const fn empty() -> Self { + Self(0) + } + + pub fn set_fifo_enable(&mut self, enable: bool) { + if enable { + self.0 |= 1 << 0; + } else { + self.0 &= !(1 << 0); + } + } + pub fn fifo_enable(&self) -> bool { + (self.0 & (1 << 0)) != 0 + } + + pub fn set_fifo_trigger_level(&mut self, level: FifoTriggerLevel) { + self.0 &= !(0b11 << 6); + self.0 |= (level as u8) << 6; + } + pub fn fifo_trigger_level(&self) -> FifoTriggerLevel { + match (self.0 >> 6) & 0b11 { + 0b00 => FifoTriggerLevel::Level1Byte, + 0b01 => FifoTriggerLevel::Level4Bytes, + 0b10 => FifoTriggerLevel::Level8Bytes, + 0b11 => FifoTriggerLevel::Level14Bytes, + _ => unreachable!(), + } + } + + pub fn set_clear_receive_fifo(&mut self) { + self.0 |= 1 << 1; + } + pub fn set_clear_transmit_fifo(&mut self) { + self.0 |= 1 << 2; + } + pub fn set_dma_mode(&mut self, enable: bool) { + if enable { + self.0 |= 1 << 3; + } else { + self.0 &= !(1 << 3); + } + } + } + + #[repr(u8)] + pub enum InterruptState { + ModemStatus = 0, + TransmitterHoldingRegisterEmpty = 1, + ReceivedDataAvailable = 2, + ReceiverLineStatus = 3, + } + + #[repr(u8)] + pub enum FIFOBufferState { + Disabled = 0, + Unusable = 1, + Enabled = 2, + } + + pub struct InterruptStatus(pub u8); + + impl InterruptStatus { + pub fn interrupt_pending(&self) -> bool { + (self.0 & (1 << 0)) == 0 + } + pub fn timeout_interrupt_pending(&self) -> bool { + (self.0 & (1 << 4)) != 0 + } + pub fn interrupt_state(&self) -> InterruptState { + match (self.0 >> 1) & 0b111 { + 0b000 => InterruptState::ModemStatus, + 0b001 => InterruptState::TransmitterHoldingRegisterEmpty, + 0b010 => InterruptState::ReceivedDataAvailable, + 0b011 => InterruptState::ReceiverLineStatus, + _ => unreachable!(), + } + } + pub fn fifo_buffer_state(&self) -> FIFOBufferState { + match (self.0 >> 6) & 0b11 { + 0b00 => FIFOBufferState::Disabled, + 0b01 => FIFOBufferState::Unusable, + 0b10 => FIFOBufferState::Enabled, + _ => unreachable!(), + } + } + } + + pub struct ModemControl(pub u8); + + impl ModemControl { + pub const fn empty() -> Self { + Self(0) + } + + pub fn set_data_terminal_ready(&mut self, enable: bool) { + if enable { + self.0 |= 1 << 0; + } else { + self.0 &= !(1 << 0); + } + } + pub fn set_request_to_send(&mut self, enable: bool) { + if enable { + self.0 |= 1 << 1; + } else { + self.0 &= !(1 << 1); + } + } + pub fn set_auxiliary_output_1(&mut self, enable: bool) { + if enable { + self.0 |= 1 << 2; + } else { + self.0 &= !(1 << 2); + } + } + pub fn set_auxiliary_output_2(&mut self, enable: bool) { + if enable { + self.0 |= 1 << 3; + } else { + self.0 &= !(1 << 3); + } + } + pub fn set_loopback_mode(&mut self, enable: bool) { + if enable { + self.0 |= 1 << 4; + } else { + self.0 &= !(1 << 4); + } + } + + pub fn loopback_mode(&self) -> bool { + (self.0 & (1 << 4)) != 0 + } + } + + pub struct LineStatus(pub u8); + + impl LineStatus { + pub fn data_ready(&self) -> bool { + (self.0 & (1 << 0)) != 0 + } + pub fn overrun_error(&self) -> bool { + (self.0 & (1 << 1)) != 0 + } + pub fn parity_error(&self) -> bool { + (self.0 & (1 << 2)) != 0 + } + pub fn framing_error(&self) -> bool { + (self.0 & (1 << 3)) != 0 + } + pub fn break_interrupt(&self) -> bool { + (self.0 & (1 << 4)) != 0 + } + pub fn transmitter_holding_register_empty(&self) -> bool { + (self.0 & (1 << 5)) != 0 + } + pub fn transmitter_empty(&self) -> bool { + (self.0 & (1 << 6)) != 0 + } + pub fn impending_error(&self) -> bool { + (self.0 & (1 << 7)) != 0 + } + } + + pub struct ModemStatus(pub u8); + + impl ModemStatus { + pub fn delta_clear_to_send(&self) -> bool { + (self.0 & (1 << 0)) != 0 + } + pub fn delta_data_set_ready(&self) -> bool { + (self.0 & (1 << 1)) != 0 + } + pub fn trailing_edge_ring_indicator(&self) -> bool { + (self.0 & (1 << 2)) != 0 + } + pub fn delta_data_carrier_detect(&self) -> bool { + (self.0 & (1 << 3)) != 0 + } + pub fn clear_to_send(&self) -> bool { + (self.0 & (1 << 4)) != 0 + } + pub fn data_set_ready(&self) -> bool { + (self.0 & (1 << 5)) != 0 + } + pub fn ring_indicator(&self) -> bool { + (self.0 & (1 << 6)) != 0 + } + pub fn data_carrier_detect(&self) -> bool { + (self.0 & (1 << 7)) != 0 + } + } + + #[derive(Debug, Clone, Copy, Default)] + #[repr(u8)] + pub enum FifoTriggerLevel { + Level1Byte = 0b00, + Level4Bytes = 0b01, + Level8Bytes = 0b10, + #[default] + Level14Bytes = 0b11, + } + impl FifoTriggerLevel { + pub fn count(&self) -> usize { + match self { + FifoTriggerLevel::Level1Byte => 1, + FifoTriggerLevel::Level4Bytes => 4, + FifoTriggerLevel::Level8Bytes => 8, + FifoTriggerLevel::Level14Bytes => 14, + } + } + } + + pub struct Uart16550Writer { + uart: Uart16550, + } + + impl Uart16550Writer { + pub fn from_port(base_port: u16) -> Self { + let mut uart = unsafe { Uart16550::with_port(base_port) }; + uart.init(); + Self { uart } + } + } + + impl core::fmt::Write for Uart16550Writer { + fn write_str(&mut self, s: &str) -> core::fmt::Result { + let bytes = s.as_bytes(); + + const SPECIAL_BYTES: &[u8] = &[b'\n', 8, 0x7f]; + bytes + .split_inclusive(|b| SPECIAL_BYTES.contains(b)) + .for_each(|chunk| { + if let Some((special, rest)) = chunk.split_last() { + self.uart.send_bytes_exact(rest); + match special { + b'\n' => { + self.uart.send_bytes_exact(b"\r\n"); + } + 8 | 0x7f => { + self.uart.send_bytes_exact(&[8, b' ', 8]); + } + _ => {} + } + } + }); + + Ok(()) + } + } + + pub static SERIAL1: crate::sync::LazyLock> = + crate::sync::LazyLock::new(|| { + crate::sync::SpinMutex::new(Uart16550Writer::from_port(0x3f8)) + }); +} + +#[doc(hidden)] +pub fn _print(args: core::fmt::Arguments) { + #[cfg(target_arch = "x86_64")] + { + use core::fmt::Write; + struct Writer<'a>(&'a crate::sync::SpinMutex); + + impl core::fmt::Write for Writer<'_> { + fn write_str(&mut self, s: &str) -> core::fmt::Result { + let mut writer = self.0.lock(); + writer.write_str(s) + } + } + + Writer(&uart_16550::SERIAL1).write_fmt(args).unwrap(); + } +} + +#[macro_export] +macro_rules! serial_print { + ($($arg:tt)*) => { + $crate::serial::_print(format_args!($($arg)*)); + }; +} + +#[macro_export] +macro_rules! serial_println { + () => { + $crate::serial_print!("\n"); + }; + ($fmt:expr) => { + $crate::serial_print!(concat!($fmt, "\n")); + }; + ($fmt:expr, $($arg:tt)*) => { + $crate::serial_print!( + concat!($fmt, "\n"), $($arg)* + ); + }; +}