diff --git a/Makefile.toml b/Makefile.toml index 53b7eb3..127f6ed 100644 --- a/Makefile.toml +++ b/Makefile.toml @@ -176,4 +176,4 @@ command = "gdb" args = ["${ARTIFACTDIR}/kernel", "--eval-command=target remote localhost:1234"] [tasks.debug] -run_task = { name = ["debug-run", "debug-view"], fork = true, parallel = true } +run_task = { name = ["debug_run", "debug_view"], fork = true, parallel = true } diff --git a/src/kernel/arch/x86_64/serial.rs b/src/kernel/arch/x86_64/serial.rs index 650ee56..56579a1 100644 --- a/src/kernel/arch/x86_64/serial.rs +++ b/src/kernel/arch/x86_64/serial.rs @@ -1,13 +1,342 @@ // Copyright (c) 2025 shibedrill // SPDX-License-Identifier: GPL-3.0-or-later -use crate::log::LogSubscriber; -use x86_64::instructions::port::Port; +#![allow(dead_code)] -impl LogSubscriber for Port { - fn log_write(&mut self, msg: &str) { - for c in msg.chars() { - unsafe { self.write(c as u8) }; +use enumflags2::{BitFlag, BitFlags}; +use intbits::Bits; +use x86_64::instructions::port::Port; +use num_traits::FromPrimitive; +use num_derive::FromPrimitive; + +/// Represents an x86 port-mapped serial port. +pub struct SerialPort { + base_port: Port, + interrupt_enable: Port, + interrupt_id_fifo_control: Port, + line_control: Port, + modem_control: Port, + line_status: Port, + modem_status: Port, + scratch: Port, + fifo_register: u8, + pub crlf: Crlf, +} + +#[derive(PartialEq, Eq)] +pub enum Crlf { + Lf, + Crlf, +} + +#[derive(FromPrimitive)] +#[repr(u8)] +pub enum DataBits { + Five = 0, + Six = 1, + Seven = 2, + Eight = 3, +} + +#[derive(FromPrimitive)] +#[repr(u8)] +pub enum ParityBits { + None = 0b000, + Odd = 0b001, + Even = 0b011, + Mark = 0b101, + Space = 0b111, +} + +#[enumflags2::bitflags] +#[repr(u8)] +#[derive(Clone, Copy, Debug)] +pub enum InterruptEnable { + ModemStatus = 0b1000, + ReceiverLineStatus = 0b100, + TransmitterHoldingRegisterEmpty = 0b10, + ReceivedDataAvail = 0b1, +} + +#[enumflags2::bitflags] +#[repr(u8)] +#[derive(Clone, Copy, Debug)] +pub enum ModemControl { + DataTerminalReady = 0b00000001, + RequestToSend = 0b00000010, + Out1 = 0b00000100, + Out2 = 0b00001000, + Loop = 0b00010000, +} + +#[repr(u8)] +#[derive(FromPrimitive)] +pub enum TriggerLevel { + OneByte = 0b00, + FourBytes = 0b01, + EightBytes = 0b10, + FourteenBytes = 0b11, +} + +#[repr(u8)] +#[derive(FromPrimitive)] +pub enum InterruptState { + ModemStatus = 0b00, + TransmitterEmpty = 0b01, + ReceivedDataAvail = 0b10, + ReceiverLineStatus = 0b11, +} + +#[repr(u8)] +#[derive(FromPrimitive)] +pub enum FifoState { + NoFifo = 0b00, + FifoEnabledUnusable = 0b01, + FifoEnabled = 0b10, +} + +#[repr(u8)] +#[enumflags2::bitflags] +#[derive(Clone, Copy, Debug)] +pub enum LineStatus { + DataReady = 0b00000001, + OverrunError = 0b00000010, + ParityError = 0b00000100, + FramingError = 0b00001000, + BreakIndicator = 0b00010000, + TransmitterHoldingRegisterEmpty = 0b00100000, + TransmitterEmpty = 0b01000000, + ImpendingError = 0b10000000, +} + +#[repr(u8)] +#[enumflags2::bitflags] +#[derive(Clone, Copy, Debug)] +pub enum ModemStatus { + DeltaClearToSend = 0b00000001, + DeltaDataSetReady = 0b00000010, + TrailingEdgeRingIndicator = 0b00000100, + DeltaDataCarrierDetect = 0b00001000, + ClearToSend = 0b00010000, + DataSetReady = 0b00100000, + RingIndicator = 0b01000000, + DataCarrierDetect = 0b10000000, +} + +impl SerialPort { + + pub fn log_write(&mut self, msg: &str) { + if self.crlf == Crlf::Crlf { + for c in msg.chars() { + if c == '\n' { + self.write_char('\r'); + } + self.write_char(c); + } + } else { + for c in msg.chars() { + self.write_char(c); + } } } + + pub fn log_writeln(&mut self, msg: &str) { + self.log_write(msg); + self.log_write("\n"); + } + + /// Create a new port-mapped serial port with the base address `port_addr`. + pub fn new(port_addr: u16) -> Self { + let mut port = SerialPort { + base_port: Port::new(port_addr), + interrupt_enable: Port::new(port_addr + 1), + interrupt_id_fifo_control: Port::new(port_addr + 2), + line_control: Port::new(port_addr + 3), + modem_control: Port::new(port_addr + 4), + line_status: Port::new(port_addr + 5), + modem_status: Port::new(port_addr + 6), + scratch: Port::new(port_addr + 7), + fifo_register: 0, + crlf: Crlf::Crlf, + }; + // ensure this is false + port.set_dlab(false); + port + } + + fn get_line_control(&mut self) -> u8 { + unsafe { self.line_control.read() } + } + fn set_line_control(&mut self, value: u8) { + unsafe { self.line_control.write(value) }; + } + + fn get_dlab(&mut self) -> bool { + self.get_line_control().bit(7) + } + fn set_dlab(&mut self, dlab: bool) { + let mut new_lcr = unsafe {self.line_control.read()}; + new_lcr.set_bit(7, dlab); + unsafe { self.line_control.write(new_lcr) }; + } + + /// Read a single character from the RX buffer. + pub fn read_char(&mut self) -> u8 { + unsafe { self.base_port.read() } + } + /// Write a single character to the TX buffer. + pub fn write_char(&mut self, c: char) { + unsafe { self.base_port.write(c as u8) }; + } + + /// Get the baud rate divisor. + pub fn get_divisor(&mut self) -> u16 { + self.set_dlab(true); + let result: u16 = unsafe { self.base_port.read() as u16 | ((self.interrupt_enable.read() as u16) << 8) as u16}; + self.set_dlab(false); + result + } + /// Set the baud rate divisor. + pub fn set_divisor(&mut self, divisor: u16) { + self.set_dlab(true); + unsafe { + self.base_port.write((divisor & 0b11111111) as u8); + self.interrupt_enable.write(((divisor & 0b1111111100000000) >> 8) as u8); + } + self.set_dlab(false); + } + + /// Get the number of bits in a character. + pub fn get_data_bits(&mut self) -> DataBits { + DataBits::from_u8(self.get_line_control().bits(0..=1)).unwrap() + } + /// Set the number of bits in a character. + pub fn set_data_bits(&mut self, bits: DataBits) { + let new_value = self.get_line_control().bits(2..=7) | bits as u8; + self.set_line_control(new_value); + } + + /// Get the number of bits sent after each character. + /// A value of False means only 1 bit will be sent. + /// A value of True means 2 bits will be sent. + /// If the character length is 5 bits, a value of True means 1.5 bits will be sent. + pub fn get_stop_bits(&mut self) -> bool { + self.get_line_control().bit(2) + } + /// Set the number of bits sent after each character. + /// A value of False means only 1 bit will be sent. + /// A value of True means 2 bits will be sent. + /// If the character length is 5 bits, a value of True means 1.5 bits will be sent. + pub fn set_stop_bits(&mut self, bit: bool) { + let mut new_value = self.get_line_control(); + new_value.set_bit(2, bit); + self.set_line_control(new_value); + } + + /// Get the parity strategy. + pub fn get_parity_bits(&mut self) -> ParityBits { + ParityBits::from_u8(self.get_line_control().bits(3..=5) >> 3).unwrap() + } + /// Set the parity strategy. + pub fn set_parity_bits(&mut self, parity: ParityBits) { + let mut new_value = self.get_line_control(); + new_value.set_bits(3..=5, parity as u8); + self.set_line_control(new_value); + } + + /// Get the Interrupt Enable control register. + pub fn get_interrupt_enable(&mut self) -> BitFlags { + InterruptEnable::from_bits(unsafe {self.interrupt_enable.read().bits(0..=3)}).unwrap() + } + /// Set the Interrupt Enable control register. + pub fn set_interrupt_enable(&mut self, interrupt: InterruptEnable) { + unsafe { self.interrupt_enable.write(interrupt as u8) }; + } + + /// Set the FIFO control register. + pub fn set_fifo_control(&mut self, value: u8) { + unsafe { self.interrupt_id_fifo_control.write(value) }; + } + /// Clear the transmitter FIFO. + pub fn clear_tx_fifo(&mut self) { + let mut new_value = self.fifo_register; + new_value.set_bit(2, true); + self.set_fifo_control(new_value); + } + /// Clear the receiver FIFO. + pub fn clear_rx_fifo(&mut self) { + let mut new_value = self.fifo_register; + new_value.set_bit(1, true); + self.set_fifo_control(new_value); + } + /// Enable or disable FIFOs. + pub fn set_fifo_enable(&mut self, enable: bool) { + self.fifo_register.set_bit(0, enable); + self.set_fifo_control(self.fifo_register); + } + /// Get whether FIFOs were enabled using this API. + pub fn get_fifo_enable(&mut self) -> bool { + self.fifo_register.bit(0) + } + /// Set the DMA mode of the serial controller. + pub fn set_dma_mode(&mut self, enable: bool) { + self.fifo_register.set_bit(3, enable); + self.set_fifo_control(self.fifo_register); + } + /// Get whether DMA mode was enabled using this API. + pub fn get_dma_mode(&mut self) -> bool { + self.fifo_register.bit(3) + } + /// Get the Interrupt Trigger Level. + pub fn get_interrupt_trigger_level(&mut self) -> TriggerLevel { + TriggerLevel::from_u8(self.fifo_register.bits(6..=7) >> 6).unwrap() + } + /// Set the Interrupt Trigger Level. + pub fn set_interrupt_trigger_level(&mut self, level: TriggerLevel) { + self.fifo_register.set_bits(6..=7, level as u8); + self.set_fifo_control(self.fifo_register); + } + + /// Get the Interrupt ID status register. + pub fn get_interrupt_id(&mut self) -> u8 { + unsafe { self.interrupt_id_fifo_control.read() } + } + /// Get the state of the FIFO buffers. + pub fn get_fifo_buffer_state(&mut self) -> FifoState { + FifoState::from_u8(self.get_interrupt_id().bits(6..=7) >> 6).unwrap() + } + /// Get whether a timeout interrupt is pending. Only used on UART 16550 chips, otherwise reserved. + pub fn get_timeout_pending(&mut self) -> bool { + self.get_interrupt_id().bit(3) + } + /// Get the Interrupt State of the controller. + pub fn get_interrupt_state(&mut self) -> InterruptState { + InterruptState::from_u8(self.get_interrupt_id().bits(1..=2) >> 1).unwrap() + } + /// Get whether an interrupt is pending. + pub fn get_interrupt_pending(&mut self) -> bool { + !self.get_interrupt_id().bit(0) + } + + /// Set the Modem Control register flags. + /// This OVERWRITES all held flags. To set specific flags, first store the current + /// value using `get_modem_control()`, then modify the flags you want to change. + pub fn set_modem_control(&mut self, control: BitFlags) { + unsafe { self.modem_control.write(control.bits() as u8) }; + } + /// Get the Modem Control register flags. + pub fn get_modem_control(&mut self) -> BitFlags { + ModemControl::from_bits(unsafe { self.modem_control.read() & 0b0001111 }).unwrap() + } + + /// Get the Line Status Register flags. + pub fn get_line_status(&mut self) -> BitFlags { + LineStatus::from_bits(unsafe {self.line_status.read()}).unwrap() + } + + /// Get the Modem Status Register flags. + pub fn get_modem_status(&mut self) -> BitFlags { + ModemStatus::from_bits(unsafe {self.modem_status.read()}).unwrap() + } + } diff --git a/src/kernel/log.rs b/src/kernel/log.rs index 00ef7e4..1eec206 100644 --- a/src/kernel/log.rs +++ b/src/kernel/log.rs @@ -1,123 +1,78 @@ // Copyright (c) 2025 shibedrill // SPDX-License-Identifier: GPL-3.0-or-later -use core::fmt::Write; - -use crate::constants::{LOG_DEFAULT_LEVEL, NEWLINE}; -use crate::memory::alloc; -use alloc::boxed::*; -use alloc::string::*; -use alloc::vec::*; +use crate::memory::alloc::vec::Vec; +use crate::memory::alloc::boxed::Box; +use crate::constants::LOG_DEFAULT_LEVEL; +use crate::memory::alloc::string::String; +use lazy_static::lazy_static; use spin::Mutex; +use crate::format; + +pub struct Logger { + pub level: LogLevel, + devices: Vec> +} + +lazy_static! { + pub static ref LOGGER: Mutex = Mutex::new(Logger::new()); +} #[macro_export] macro_rules! log_info { ($($arg:tt)*) => { - LOGGER.log(LogLevel::Info, &format!($($arg)*), file!(), line!(), column!()) + LOGGER.lock().log(LogLevel::Info, &format!($($arg)*), file!(), line!(), column!()) }; } #[macro_export] macro_rules! log_trace { ($($arg:tt)*) => { - LOGGER.log(LogLevel::Trace, &format!($($arg)*), file!(), line!(), column!()) + LOGGER.lock().log(LogLevel::Trace, &format!($($arg)*), file!(), line!(), column!()) }; } + #[macro_export] -#[allow(unused_macros)] macro_rules! log_warning { ($($arg:tt)*) => { - LOGGER.log(LogLevel::Warning, &format!($($arg)*), file!(), line!(), column!()) + LOGGER.lock().log(LogLevel::Warning, &format!($($arg)*), file!(), line!(), column!()) }; } + #[macro_export] -#[allow(unused_macros)] macro_rules! log_error { ($($arg:tt)*) => { - LOGGER.log(LogLevel::Error, &format!($($arg)*), file!(), line!(), column!()) + LOGGER.lock().log(LogLevel::Error, &format!($($arg)*), file!(), line!(), column!()) }; } + #[macro_export] -#[allow(unused_macros)] macro_rules! log_critical { ($($arg:tt)*) => { - LOGGER.log(LogLevel::Critical, &format!($($arg)*), file!(), line!(), column!()) + LOGGER.lock().log(LogLevel::Critical, &format!($($arg)*), file!(), line!(), column!()) }; } -pub struct Logger { - pub inner: Mutex, -} impl Logger { - pub fn log(&self, level: LogLevel, msg: &str, file: &'static str, line: u32, column: u32) { - self.inner.lock().log(level, msg, file, line, column); + pub fn new() -> Self { + Logger { level: LOG_DEFAULT_LEVEL, devices: Vec::new() } } - pub fn add_subscriber(&self, sub: T) { - self.inner - .lock() - .subscriber - .push(Mutex::new(alloc::boxed::Box::new(sub))); + pub fn log(&mut self, level: LogLevel, message: &str, file: &'static str, line: u32, column: u32) { + for dev in &self.devices { + let message = format!("{} {}:{},{}- {}", level.normalized_string(), file, line, column, message); + dev(&message) + } } - pub fn set_level(&self, level: LogLevel) { - self.inner.lock().level = level; + pub fn add_device(&mut self, device: Box) { + self.devices.push(device); } } - -// The logger exists for the entire lifetime of the kernel. -pub static LOGGER: Logger = Logger { - inner: Mutex::new(LoggerInner::new()), -}; - pub struct EnumParseError {} -pub struct LoggerInner { - pub level: LogLevel, - pub subscriber: Vec>>, -} - -pub trait LogSubscriber: Send + Sync { - fn log_write(&mut self, msg: &str); -} - -impl Default for LoggerInner { - fn default() -> Self { - Self::new() - } -} - -impl LoggerInner { - pub const fn new() -> Self { - LoggerInner { - level: LOG_DEFAULT_LEVEL, - subscriber: Vec::new(), - } - } - - // Calling log will sequentially acquire lock on all logging subscribers - // to write to them with a formatted log message. - pub fn log(&self, level: LogLevel, msg: &str, file: &'static str, line: u32, column: u32) { - // Nobody is EVER allowed to call log with the Disabled log level. It is a placeholder. - if level == LogLevel::Disabled { - // Nothing - } else if level <= self.level { - let level_string = String::from(level); - for sub in &self.subscriber { - let mut message = String::new(); - write!( - &mut message, - "[{level_string}] {file}:{line},{column} - {msg}{NEWLINE}" - ) - .unwrap(); - sub.lock().log_write(&message); - } - } - } -} - #[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Clone, Copy)] pub enum LogLevel { Disabled = 0, @@ -171,3 +126,16 @@ impl From for String { .into() } } + +impl LogLevel { + fn normalized_string(&self) -> &str { + match self { + LogLevel::Critical => "[CRIT ]", + LogLevel::Error => "[ERROR]", + LogLevel::Info => "[INFO ]", + LogLevel::Trace => "[TRACE]", + LogLevel::Warning => "[WARN ]", + LogLevel::Disabled => "[DSBLD]", + } + } +} diff --git a/src/kernel/main.rs b/src/kernel/main.rs index 918c896..abeb2c9 100644 --- a/src/kernel/main.rs +++ b/src/kernel/main.rs @@ -21,17 +21,20 @@ mod process; mod util; use boot::{BASE_REVISION, params, *}; -use constants::*; -use limine::firmware_type::FirmwareType; use log::*; -use memory::alloc::{format, string::*, vec}; - +use memory::alloc::{format, vec, boxed::Box, string::{String, ToString}}; +use arch::x86_64::interrupts::IDT; use params::*; -#[allow(unused_imports)] -use lzma_rs::lzma_decompress; +use lazy_static::lazy_static; +use limine::firmware_type::FirmwareType; +use spin::{mutex::Mutex}; +use crate::arch::x86_64::serial::SerialPort; +use crate::constants::*; -use crate::arch::x86_64::interrupts::IDT; +lazy_static! { + pub static ref SERIAL_3F8: Mutex = Mutex::new(arch::x86_64::serial::SerialPort::new(0x3f8)); +} #[unsafe(no_mangle)] unsafe extern "C" fn main() -> ! { @@ -41,15 +44,12 @@ unsafe extern "C" fn main() -> ! { // Ensure IDT exists IDT.load(); - #[cfg(target_arch = "x86_64")] - let serial: x86_64::instructions::port::Port = x86_64::instructions::port::Port::new(0x3f8); - // Set up logging level from params // Nothing we can do here if this fails since no log subscribers are initialized yet if let Some(level) = PARAMS.get("-loglevel") && let Ok(parsed_level) = LogLevel::try_from(level.as_str()) { - LOGGER.set_level(parsed_level); + LOGGER.lock().level = parsed_level; } // Add subscribers to logger if let Some(device) = PARAMS.get("-logdev") { @@ -59,8 +59,8 @@ unsafe extern "C" fn main() -> ! { } if log_device_list.contains(&"serial") { // TODO: Set up device discovery - #[cfg(target_arch = "x86_64")] - LOGGER.add_subscriber(serial); + let serial_logger = |msg: &str| {SERIAL_3F8.lock().log_writeln(msg)}; + LOGGER.lock().add_device(Box::new(serial_logger)); } log_trace!("Configured kernel logging devices"); } @@ -135,14 +135,9 @@ unsafe extern "C" fn main() -> ! { log_info!("Hypervisor: {:?}", string.identify()); } - panic!("Bailing"); + SERIAL_3F8.lock().log_write("hi"); - #[allow(unreachable_code)] loop { - for _i in 0..50000000u64 { - crate::arch::asm::nop(); - } - core::hint::black_box(()); - log_trace!("Heartbeat"); + arch::asm::nop(); } } diff --git a/src/lib/syscall/mod.rs b/src/lib/syscall/mod.rs index dc8b73e..a365c4b 100644 --- a/src/lib/syscall/mod.rs +++ b/src/lib/syscall/mod.rs @@ -13,6 +13,7 @@ pub enum Syscall { Resolve, Spawn, Version, + Yield, } #[derive(FromPrimitive, Debug)] @@ -20,22 +21,17 @@ pub enum SyscallStatus { Success, NoSuchCall, NoSuchProcess, - NoSuchServer, + NoSuchService, NoPermission, OutOfMemory, Aborted, Unspecified, Unknown, + Unimplemented, } -pub struct UnknownStatus {} - impl From for SyscallStatus { fn from(value: u64) -> Self { SyscallStatus::from_u64(value).unwrap_or(Self::Unknown) } } - -//pub fn exit(code: u64) -> SyscallStatus { -// arch::syscall_impl::caller_syscall_1(Syscall::Exit as u64, code).into() -//}