diff --git a/.gitignore b/.gitignore index 8eec4ecf..092a2621 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ /target /toolchain /xtask.toml +/qemu.toml +/etc/boot/yboot.cfg diff --git a/boot/yboot-proto/src/v1.rs b/boot/yboot-proto/src/v1.rs index e0f39773..084716b4 100644 --- a/boot/yboot-proto/src/v1.rs +++ b/boot/yboot-proto/src/v1.rs @@ -15,6 +15,9 @@ pub struct LoadProtocol { pub memory_map: MemoryMap, + pub cmdline: u64, + pub cmdline_len: u64, + pub rsdp_address: u64, pub initrd_address: u64, pub initrd_size: u64, diff --git a/boot/yboot/src/config.rs b/boot/yboot/src/config.rs new file mode 100644 index 00000000..f1953d0c --- /dev/null +++ b/boot/yboot/src/config.rs @@ -0,0 +1,39 @@ +use uefi::{ + proto::media::file::{Directory, File, FileAttribute, FileMode}, + CStr16, Result, Status, +}; + +#[derive(Debug)] +pub struct Config { + pub cmdline: [u8; 4096], + pub cmdline_len: usize, +} + +impl Default for Config { + fn default() -> Self { + Self { + cmdline: [0; 4096], + cmdline_len: 0, + } + } +} + +impl Config { + pub fn load(root: &mut Directory, path: &CStr16) -> Result { + let file = match root.open(path, FileMode::Read, FileAttribute::empty()) { + Ok(file) => file, + Err(error) => { + root.reset_entry_readout().ok(); + log::warn!("Couldn't open {path:?}: {error:?}"); + return Ok(Self::default()); + } + }; + root.reset_entry_readout().ok(); + let mut this = Self::default(); + let mut file = file.into_regular_file().ok_or(Status::INVALID_PARAMETER)?; + + this.cmdline_len = file.read(&mut this.cmdline)?; + + Ok(this) + } +} diff --git a/boot/yboot/src/main.rs b/boot/yboot/src/main.rs index 88c2ed6d..de24c6c6 100644 --- a/boot/yboot/src/main.rs +++ b/boot/yboot/src/main.rs @@ -2,6 +2,7 @@ #![no_std] #![no_main] +pub mod config; pub mod elf; pub mod initrd; pub mod mem; @@ -9,6 +10,7 @@ pub mod protocol_ext; use core::{arch::asm, mem::size_of, ops::Deref}; +use config::Config; use elf::Object; use log::{debug, error, info}; use uefi::{ @@ -17,7 +19,7 @@ use uefi::{ console::gop::{GraphicsOutput, PixelFormat}, device_path::DevicePath, loaded_image::LoadedImage, - media::fs::SimpleFileSystem, + media::{file::Directory, fs::SimpleFileSystem}, }, table::{ boot::{AllocateType, MemoryType, ScopedProtocol}, @@ -94,27 +96,45 @@ fn boot_partition( bs.open_protocol_exclusive::(fs_handle) } +fn open_root(image: Handle, bs: &BootServices) -> Result { + let mut boot_partition = boot_partition(image, bs)?; + boot_partition.open_volume() +} + fn load_kernel<'a>( - ih: Handle, + config: &Config, + root: &mut Directory, st: &SystemTable, ) -> Result<(u64, u64, &'a mut LoadProtocolV1), Error> { let bs = st.boot_services(); - let mut fs = boot_partition(ih, bs)?; - let mut root = fs.open_volume()?; - - let mut kernel_obj = Object::open(&mut root, cstr16!("kernel.elf"))?; + let mut kernel_obj = Object::open(root, cstr16!("kernel.elf"))?; let loaded_obj = kernel_obj.load(bs)?; debug!("Loaded object: {:#x?}", loaded_obj); // Load initrd - let (initrd_start, initrd_size) = initrd::load_somewhere(bs, &mut root, cstr16!("initrd.img"))?; + let (initrd_start, initrd_size) = initrd::load_somewhere(bs, root, cstr16!("initrd.img"))?; debug!( "Loaded initrd: {:#x?}", initrd_start..initrd_start + initrd_size ); + // Load cmdline + let cmdline = if config.cmdline_len != 0 { + let address = bs.allocate_pages(AllocateType::AnyPages, MemoryType::LOADER_DATA, 1)?; + + let dst = + unsafe { core::slice::from_raw_parts_mut(address as *mut u8, config.cmdline_len) }; + dst.copy_from_slice(&config.cmdline[..config.cmdline_len]); + + debug!("Cmdline at {:#x?}", address); + + address + } else { + 0 + }; + // Other versions are not existent yet assert_eq!(loaded_obj.protocol_version, 1); let proto_data = unsafe { &mut *(loaded_obj.protocol_struct_paddr as *mut LoadProtocolV1) }; @@ -125,6 +145,9 @@ fn load_kernel<'a>( })?; info!("RSDP at {:#x}", rsdp); + proto_data.cmdline = cmdline; + proto_data.cmdline_len = config.cmdline_len as _; + proto_data.rsdp_address = rsdp; proto_data.initrd_address = initrd_start; proto_data.initrd_size = initrd_size; @@ -190,9 +213,35 @@ unsafe fn map_and_enter_kernel( #[entry] fn efi_main(image_handle: Handle, mut system_table: SystemTable) -> Status { - uefi_services::init(&mut system_table).unwrap(); + if uefi_services::init(&mut system_table).is_err() { + return Status::LOAD_ERROR; + } - let (entry, mmap_memory, proto_data) = load_kernel(image_handle, &system_table).unwrap(); + let bs = system_table.boot_services(); + + let mut root = match open_root(image_handle, bs) { + Ok(root) => root, + Err(error) => { + error!("Could not open boot partition root: {error:?}"); + return Status::LOAD_ERROR; + } + }; + + let config = match Config::load(&mut root, cstr16!("yboot.cfg")) { + Ok(config) => config, + Err(error) => { + error!("Malformed yboot.cfg: {error:?}"); + return Status::LOAD_ERROR; + } + }; + + let (entry, mmap_memory, proto_data) = match load_kernel(&config, &mut root, &system_table) { + Ok(e) => e, + Err(error) => { + error!("Failed to load the kernel/initrd: {error:?}"); + return Status::LOAD_ERROR; + } + }; unsafe { map_and_enter_kernel(system_table, proto_data, mmap_memory, entry); diff --git a/kernel/arch/x86_64/src/context.rs b/kernel/arch/x86_64/src/context.rs index 159eb4dc..b6e8f07b 100644 --- a/kernel/arch/x86_64/src/context.rs +++ b/kernel/arch/x86_64/src/context.rs @@ -536,7 +536,7 @@ impl { fn drop(&mut self) { - log::info!("Drop Context {:#p}", self); + log::trace!("Drop Context {:#p}", self); assert_eq!(self.stack_size % 0x1000, 0); for offset in (0..self.stack_size).step_by(0x1000) { diff --git a/kernel/driver/block/nvme/src/lib.rs b/kernel/driver/block/nvme/src/lib.rs index ce62b20e..ebb6c6fe 100644 --- a/kernel/driver/block/nvme/src/lib.rs +++ b/kernel/driver/block/nvme/src/lib.rs @@ -18,8 +18,8 @@ use device_api::{ }; use drive::NvmeNamespace; use libk::{ - devfs, device::manager::probe_partitions, + fs::devfs, task::{cpu_count, cpu_index, runtime}, }; use libk_mm::{address::PhysicalAddress, device::DeviceMemoryIo, L3_PAGE_SIZE}; diff --git a/kernel/lib/device-tree/src/driver/tree.rs b/kernel/lib/device-tree/src/driver/tree.rs index f8d890a5..3ba23c03 100644 --- a/kernel/lib/device-tree/src/driver/tree.rs +++ b/kernel/lib/device-tree/src/driver/tree.rs @@ -66,10 +66,9 @@ impl Node { // Don't even try if `status = "disabled"` if let Some(status) = self.property("status") { - let status = status.as_str(); - match status { - Some("disabled") => return (None, parent_bus), - _ => (), + let status = status.as_str().unwrap_or(""); + if status == "disabled" { + return (None, parent_bus); } } @@ -127,7 +126,7 @@ impl Node { /// Performs a lazy initialization of the node: /// /// 1. If the node hasn't yet been probed for a device, a driver is looked up - /// (see [Node::probe]). + /// (see [Node::probe]). /// 2. If the node hasn't yet been initialized, initialization is performed. /// /// Returns: diff --git a/kernel/lib/device-tree/src/tree.rs b/kernel/lib/device-tree/src/tree.rs index 5bae5cce..e927aa2e 100644 --- a/kernel/lib/device-tree/src/tree.rs +++ b/kernel/lib/device-tree/src/tree.rs @@ -80,6 +80,11 @@ impl<'a> DeviceTree<'a> { self.index.root() } + /// Dump the device tree into the debug log + pub fn dump(&self) { + dump(self.root(), 0) + } + /// Looks up a node by its `/absolute/path`. pub fn find_absolute(&'a self, path: &str) -> Option> { let mut path = path.trim_start_matches('/'); @@ -159,6 +164,11 @@ impl<'a> DeviceTree<'a> { )) } + /// Returns the value of `/chosen/bootargs` interpreted as a string + pub fn chosen_bootargs(&self) -> Option<&str> { + self.root().child("chosen")?.prop_string("bootargs") + } + /// Returns an iterator over the memory regions specified by this device tree pub fn memory_regions(&self) -> DeviceTreeMemoryRegionIter { DeviceTreeMemoryRegionIter::new(self) @@ -178,3 +188,103 @@ impl<'a> DeviceTree<'a> { } unsafe impl Sync for DeviceTree<'_> {} + +fn indent(amount: usize) { + log::info!(target: "raw", "{0:1$}", "", amount * 2); +} + +fn dump_property(node: &TNode, prop: TProp, level: usize) { + enum Type { + String, + Cells, + Bytes, + } + + let node_name = node.name().unwrap_or(""); + let name = prop.name().unwrap_or(""); + + indent(level); + log::info!(target: "raw", "{name}"); + + if prop.len() == 0 { + log::info!(target: "raw", ";\n"); + return; + } + + log::info!(target: "raw", " = "); + + let ty = match name { + "model" | "compatible" | "clock-output-names" | "clock-names" | "device_type" + | "enable-method" | "status" | "label" | "method" => Type::String, + "stdout-path" | "bootargs" if node_name == "chosen" => Type::String, + _ if node_name == "aliases" => Type::String, + _ if prop.len() % size_of::() == 0 => Type::Cells, + _ => Type::Bytes, + }; + + match ty { + Type::String => { + // "..." + for (i, string) in prop.as_str_list().enumerate() { + if i != 0 { + log::info!(target: "raw", ", "); + } + log::info!(target: "raw", "{string:?}"); + } + } + Type::Cells => { + // <...> + log::info!(target: "raw", "<"); + let mut i = 0; + while let Some(value) = prop.read_cell(i, 1) { + if i != 0 { + log::info!(target: "raw", ", "); + } + log::info!(target: "raw", "{value:#x}"); + i += 1; + } + log::info!(target: "raw", ">"); + } + Type::Bytes => { + // <...> + log::info!(target: "raw", "<"); + for (i, byte) in prop.raw().iter().enumerate() { + if i != 0 { + log::info!(target: "raw", ", "); + } + log::info!(target: "raw", "{byte:#x}"); + } + log::info!(target: "raw", ">"); + } + } + + log::info!(target: "raw", ";\n"); +} + +fn dump(node: TNode, level: usize) { + let name = node.name().unwrap_or(""); + + indent(level); + if name.is_empty() { + log::info!(target: "raw", "{{\n"); + } else { + log::info!(target: "raw", "{name}: {{\n"); + } + + let mut do_break = false; + for property in node.props() { + dump_property(&node, property, level + 1); + do_break = true; + } + + for child in node.children() { + if do_break { + log::info!(target: "raw", "\n"); + do_break = false; + } + dump(child, level + 1); + } + + indent(level); + log::info!(target: "raw", "}}\n"); +} diff --git a/kernel/libk/src/config/general.rs b/kernel/libk/src/config/general.rs new file mode 100644 index 00000000..5cdcafdb --- /dev/null +++ b/kernel/libk/src/config/general.rs @@ -0,0 +1,57 @@ +use core::str::FromStr; + +use crate::debug::LogLevel; + +use super::SetOption; + +/// Debugging/logging-related options +#[derive(Debug)] +pub struct DebugOptions { + /// Default log level used for serial devices + pub serial_level: LogLevel, + /// Default log level used for proper display devices + pub display_level: LogLevel, + /// If set, logs produced by the `debug_trace` syscall are ignored + pub disable_program_trace: bool, +} + +impl Default for DebugOptions { + fn default() -> Self { + Self { + serial_level: LogLevel::Info, + display_level: LogLevel::Info, + disable_program_trace: false, + } + } +} + +impl SetOption for DebugOptions { + fn set_key_value(&mut self, key: &str, value: &str) -> bool { + match key { + "serial-level" if let Ok(level) = LogLevel::from_str(value) => { + self.serial_level = level; + true + } + "display-level" if let Ok(level) = LogLevel::from_str(value) => { + self.display_level = level; + true + } + "log_level" if let Ok(level) = LogLevel::from_str(value) => { + self.serial_level = level; + self.display_level = level; + true + } + _ => false, + } + } + + fn set_flag(&mut self, value: &str) -> bool { + match value { + "disable-program-trace" => { + self.disable_program_trace = true; + true + } + _ => false, + } + } +} diff --git a/kernel/libk/src/config/mod.rs b/kernel/libk/src/config/mod.rs new file mode 100644 index 00000000..52244664 --- /dev/null +++ b/kernel/libk/src/config/mod.rs @@ -0,0 +1,118 @@ +//! Kernel commandline parameters handling + +use alloc::string::String; +use libk_util::OneTimeInit; +use yggdrasil_abi::error::Error; + +use crate::fs::sysfs::{ + self, + attribute::{StringAttribute, StringAttributeOps}, +}; + +#[cfg(any(target_arch = "x86_64", target_arch = "x86", rust_analyzer))] +mod x86; +#[cfg(any(target_arch = "x86_64", target_arch = "x86", rust_analyzer))] +pub use x86::X86Options; + +#[cfg(any(target_arch = "x86_64", rust_analyzer))] +mod x86_64; +#[cfg(any(target_arch = "x86_64", rust_analyzer))] +pub use x86_64::X86_64Options; + +mod general; +pub use general::DebugOptions; + +trait SetOption { + fn set_key_value(&mut self, key: &str, value: &str) -> bool; + fn set_flag(&mut self, value: &str) -> bool; +} + +/// Kernel commandline options +#[derive(Debug, Default)] +pub struct Options { + pub debug: DebugOptions, + #[cfg(any(target_arch = "x86_64", target_arch = "x86", rust_analyzer))] + pub x86: X86Options, + #[cfg(any(target_arch = "x86_64", rust_analyzer))] + pub x86_64: X86_64Options, +} + +impl SetOption for Options { + fn set_flag(&mut self, value: &str) -> bool { + match value { + _ if let Some(value) = value.strip_prefix("debug.") => self.debug.set_flag(value), + #[cfg(any(target_arch = "x86_64", target_arch = "x86", rust_analyzer))] + _ if let Some(value) = value.strip_prefix("x86.") => self.x86.set_flag(value), + #[cfg(any(target_arch = "x86_64", rust_analyzer))] + _ if let Some(value) = value.strip_prefix("x86_64.") => self.x86_64.set_flag(value), + _ => false, + } + } + + fn set_key_value(&mut self, key: &str, value: &str) -> bool { + match key { + _ if let Some(key) = key.strip_prefix("debug.") => self.debug.set_key_value(key, value), + #[cfg(any(target_arch = "x86_64", target_arch = "x86", rust_analyzer))] + _ if let Some(key) = key.strip_prefix("x86.") => self.x86.set_key_value(key, value), + #[cfg(any(target_arch = "x86_64", rust_analyzer))] + _ if let Some(key) = key.strip_prefix("x86_64.") => { + self.x86_64.set_key_value(key, value) + } + _ => false, + } + } +} + +static OPTIONS: OneTimeInit = OneTimeInit::new(); +static OPTIONS_STRING: OneTimeInit = OneTimeInit::new(); + +pub fn get() -> &'static Options { + OPTIONS.get() +} + +fn parse_argument(options: &mut Options, arg: &str) -> bool { + let arg = arg.trim(); + + if let Some((key, value)) = arg.split_once('=') { + options.set_key_value(key.trim_end(), value.trim_start()) + } else { + options.set_flag(arg) + } +} + +/// Sets up kernel options from an argument list +pub fn parse_boot_argument_list<'a, I: Iterator>(args: I) { + let mut options = Default::default(); + let mut string = String::new(); + + args.for_each(|arg| { + if parse_argument(&mut options, arg) { + string.push_str(arg); + string.push('\n'); + } + }); + + OPTIONS.init(options); + OPTIONS_STRING.init(string); + + // Add to sysfs + if let Some(kernel) = sysfs::kernel() { + struct Cmdline; + + impl StringAttributeOps for Cmdline { + const NAME: &'static str = "cmdline"; + const NEWLINE: bool = false; + + fn read(_state: &Self::Data) -> Result { + Ok(OPTIONS_STRING.get().into()) + } + } + + kernel.add_attribute(StringAttribute::from(Cmdline)).ok(); + } +} + +/// Sets up kernel options from an argument string, where options are separated by whitespace +pub fn parse_boot_arguments(args: &str) { + parse_boot_argument_list(args.trim().split(' ')); +} diff --git a/kernel/libk/src/config/x86.rs b/kernel/libk/src/config/x86.rs new file mode 100644 index 00000000..3cabf260 --- /dev/null +++ b/kernel/libk/src/config/x86.rs @@ -0,0 +1,30 @@ +use super::SetOption; + +#[derive(Debug)] +pub struct X86Options { + pub disable_boot_fb: bool, +} + +impl Default for X86Options { + fn default() -> Self { + Self { + disable_boot_fb: false, + } + } +} + +impl SetOption for X86Options { + fn set_flag(&mut self, value: &str) -> bool { + match value { + "disable-boot-fb" => { + self.disable_boot_fb = true; + true + } + _ => false, + } + } + + fn set_key_value(&mut self, _key: &str, _value: &str) -> bool { + false + } +} diff --git a/kernel/libk/src/config/x86_64.rs b/kernel/libk/src/config/x86_64.rs new file mode 100644 index 00000000..7bccb315 --- /dev/null +++ b/kernel/libk/src/config/x86_64.rs @@ -0,0 +1,30 @@ +use super::SetOption; + +#[derive(Debug)] +pub struct X86_64Options { + pub disable_hpet: bool, +} + +impl Default for X86_64Options { + fn default() -> Self { + Self { + disable_hpet: false, + } + } +} + +impl SetOption for X86_64Options { + fn set_key_value(&mut self, _key: &str, _value: &str) -> bool { + false + } + + fn set_flag(&mut self, value: &str) -> bool { + match value { + "disable-hpet" => { + self.disable_hpet = true; + true + } + _ => false, + } + } +} diff --git a/kernel/libk/src/debug.rs b/kernel/libk/src/debug.rs index 448eee82..86086f02 100644 --- a/kernel/libk/src/debug.rs +++ b/kernel/libk/src/debug.rs @@ -25,6 +25,7 @@ use libk_util::{ use yggdrasil_abi::error::Error; use crate::{ + config, fs::sysfs::{ self, attribute::{StringAttribute, StringAttributeOps}, @@ -56,7 +57,7 @@ pub struct RingLoggerSink { } /// Defines the severity of the message -#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] pub enum LogLevel { /// Very verbose low-level debugging information Trace, @@ -138,7 +139,14 @@ impl DebugSinkWrapper { impl log::Log for DebugSinkWrapper { fn enabled(&self, metadata: &log::Metadata) -> bool { - LogLevel::from(metadata.level()) >= self.level() + if LogLevel::from(metadata.level()) < self.level() { + return false; + } + if metadata.target() == "program" && config::get().debug.disable_program_trace { + return false; + } + + true } fn log(&self, record: &log::Record) { diff --git a/kernel/libk/src/device/manager.rs b/kernel/libk/src/device/manager.rs index 42400d1f..f061144a 100644 --- a/kernel/libk/src/device/manager.rs +++ b/kernel/libk/src/device/manager.rs @@ -9,7 +9,8 @@ use libk_util::{sync::spin_rwlock::IrqSafeRwLock, OneTimeInit}; use yggdrasil_abi::error::Error; use crate::{ - debug::{self, DebugSink, LogLevel}, + config, + debug::{self, DebugSink}, fs::devfs, vfs::NodeRef, }; @@ -92,12 +93,12 @@ impl SerialTerminalRegistry { pub fn register( &self, terminal: Arc, - sink_level: Option<(Arc, LogLevel)>, + sink: Option>, ) -> Result { let id = self.registry.register(terminal.clone())?; devfs::add_named_char_device(terminal, format!("ttyS{id}")).ok(); - if let Some((sink, sink_level)) = sink_level { - debug::add_serial_sink(sink, sink_level); + if let Some(sink) = sink { + debug::add_serial_sink(sink, config::get().debug.serial_level); } Ok(id) } diff --git a/kernel/libk/src/fs/sysfs/attribute/string.rs b/kernel/libk/src/fs/sysfs/attribute/string.rs index 9bc160a5..e025312f 100644 --- a/kernel/libk/src/fs/sysfs/attribute/string.rs +++ b/kernel/libk/src/fs/sysfs/attribute/string.rs @@ -24,6 +24,7 @@ pub trait StringAttributeOps: Sync + Send + 'static { const WRITEABLE: bool = false; const NAME: &'static str; const LIMIT: usize = 4096; + const NEWLINE: bool = true; fn read(state: &Self::Data) -> Result { let _ = state; @@ -69,7 +70,9 @@ impl RegularImpl for StringAttributeNode { } let mut value = V::read(self.object.data())?.into_bytes(); - value.push(b'\n'); + if V::NEWLINE { + value.push(b'\n'); + } let instance = StringAttributeState { value: IrqSafeRwLock::new(value), diff --git a/kernel/libk/src/lib.rs b/kernel/libk/src/lib.rs index 9b09f9bc..d7a25786 100644 --- a/kernel/libk/src/lib.rs +++ b/kernel/libk/src/lib.rs @@ -35,6 +35,7 @@ pub use yggdrasil_abi::error; pub mod task; pub mod arch; +pub mod config; pub mod debug; pub mod device; pub mod fs; diff --git a/kernel/src/arch/aarch64/mod.rs b/kernel/src/arch/aarch64/mod.rs index 886afc0e..24df321e 100644 --- a/kernel/src/arch/aarch64/mod.rs +++ b/kernel/src/arch/aarch64/mod.rs @@ -18,7 +18,7 @@ use kernel_arch_aarch64::{ }, ArchitectureImpl, PerCpuData, }; -use libk::{arch::Cpu, debug, device::external_interrupt_controller}; +use libk::{arch::Cpu, config, debug, device::external_interrupt_controller}; use libk_mm::{ address::PhysicalAddress, phys::{self, reserved::reserve_region, PhysicalMemoryRegion}, @@ -252,37 +252,11 @@ impl AArch64 { atomic::compiler_fence(Ordering::SeqCst); - // TODO - // ygg_driver_pci::register_vendor_driver( - // "Virtio PCI GPU Device", - // 0x1AF4, - // 0x1050, - // ygg_driver_virtio_gpu::probe, - // ); - // ygg_driver_pci::register_vendor_driver( - // "Virtio PCI Network Device", - // 0x1AF4, - // 0x1000, - // ygg_driver_virtio_net::probe, - // ); - // ygg_driver_pci::register_class_driver( - // "AHCI SATA Controller", - // 0x01, - // Some(0x06), - // Some(0x01), - // ygg_driver_ahci::probe, - // ); - // ygg_driver_pci::register_class_driver( - // "USB xHCI", - // 0x0C, - // Some(0x03), - // Some(0x30), - // ygg_driver_usb_xhci::probe, - // ); - debug::init(); let dt = self.dt.get(); + let bootargs = dt.chosen_bootargs().unwrap_or(""); + config::parse_boot_arguments(bootargs); // Create device tree sysfs nodes device_tree::util::create_sysfs_nodes(dt); @@ -302,6 +276,7 @@ impl AArch64 { log::info!("Running on {machine:?}"); MACHINE_NAME.init(machine.into()); } + log::info!("Boot arguments: {bootargs:?}"); log::info!("Initializing aarch64 platform"); diff --git a/kernel/src/arch/i686/boot/multiboot.rs b/kernel/src/arch/i686/boot/multiboot.rs index c334b1ba..436240fc 100644 --- a/kernel/src/arch/i686/boot/multiboot.rs +++ b/kernel/src/arch/i686/boot/multiboot.rs @@ -1,3 +1,5 @@ +use core::ffi::CStr; + use libk_mm::{ address::{PhysicalAddress, Virtualize}, phys::PhysicalMemoryRegion, @@ -87,6 +89,16 @@ impl MultibootInfo { let base = PhysicalAddress::from_u32(self.mods_addr).virtualize(); unsafe { core::slice::from_raw_parts(base as *const _, self.mods_count as usize) } } + + pub fn cmdline(&self) -> &str { + if self.cmdline == 0 { + "" + } else { + let address = PhysicalAddress::from_u32(self.cmdline).virtualize(); + let data = unsafe { CStr::from_ptr(core::ptr::with_exposed_provenance(address)) }; + data.to_str().unwrap_or("") + } + } } impl MultibootMemoryMapEntry { diff --git a/kernel/src/arch/i686/mod.rs b/kernel/src/arch/i686/mod.rs index 8b3744e2..17bdae59 100644 --- a/kernel/src/arch/i686/mod.rs +++ b/kernel/src/arch/i686/mod.rs @@ -13,7 +13,7 @@ use kernel_arch_i686::{gdt, mem::table::L3, PerCpuData}; use kernel_arch_x86::cpuid::{self, CpuFeatures, EcxFeatures, EdxFeatures}; use libk::{ arch::Cpu, - debug::{self, LogLevel}, + config, debug, device::{ display::{ console::{add_console_autoflush, ConsoleWrapper}, @@ -130,7 +130,7 @@ impl I686 { 80, 25, )?); - debug::add_sink(textfb.clone(), LogLevel::Info); + debug::add_sink(textfb.clone(), config::get().debug.display_level); add_console_autoflush(textfb.clone()); let textfb_console = Arc::new(Terminal::from_parts( @@ -155,14 +155,19 @@ impl I686 { self.init_memory_management(multiboot_info) .expect("Could not initialize memory management"); + // Set kernel commandline + let cmdline = multiboot_info.cmdline(); + self.init_cpu().expect("Could not initialize CPU"); x86::register_pci_drivers(); - let early = x86::init_platform_devices_early()?; + let early = x86::init_platform_early(cmdline)?; - if let Err(error) = self.init_framebuffer() { - log::error!("Could not initialize boot framebuffer: {error:?}"); + if !config::get().x86.disable_boot_fb { + if let Err(error) = self.init_framebuffer() { + log::error!("Could not initialize boot framebuffer: {error:?}"); + } } x86::add_legacy_pci(); diff --git a/kernel/src/arch/x86/mod.rs b/kernel/src/arch/x86/mod.rs index 7da4cfc9..4a7797db 100644 --- a/kernel/src/arch/x86/mod.rs +++ b/kernel/src/arch/x86/mod.rs @@ -5,7 +5,11 @@ use abi::error::Error; use alloc::{sync::Arc, vec::Vec}; use device_api::{device::Device, interrupt::Irq}; -use libk::{debug, devfs, task::runtime}; +use libk::{ + config, debug, + fs::{devfs, sysfs}, + task::runtime, +}; use libk_mm::{ address::{PhysicalAddress, Virtualize}, phys::{self, PhysicalMemoryRegion}, @@ -96,10 +100,13 @@ pub fn register_pci_drivers() { // Initialize the bare minimum required to: // * Allocate/manage interrupts // * Print debug output -pub fn init_platform_devices_early() -> Result { +pub fn init_platform_early(cmdline: &str) -> Result { + sysfs::init(); devfs::init(); debug::init(); + config::parse_boot_arguments(cmdline); + // Initialize async executor queue runtime::init_task_queue(); diff --git a/kernel/src/arch/x86/peripherals/serial.rs b/kernel/src/arch/x86/peripherals/serial.rs index 122f9e3f..378e240a 100644 --- a/kernel/src/arch/x86/peripherals/serial.rs +++ b/kernel/src/arch/x86/peripherals/serial.rs @@ -6,7 +6,7 @@ use device_api::{ interrupt::{InterruptHandler, Irq}, }; use libk::{ - debug::{DebugSink, LogLevel}, + debug::DebugSink, device::{external_interrupt_controller, manager::DEVICE_REGISTRY}, vfs::{Terminal, TerminalInput, TerminalOutput}, }; @@ -117,7 +117,7 @@ impl Device for Port { unsafe fn init(self: Arc) -> Result<(), Error> { DEVICE_REGISTRY .serial_terminal - .register(self.terminal.clone(), Some((self.clone(), LogLevel::Debug))) + .register(self.terminal.clone(), Some(self.clone())) .ok(); Ok(()) } diff --git a/kernel/src/arch/x86_64/boot/mod.rs b/kernel/src/arch/x86_64/boot/mod.rs index 1f8ef5e6..1d030d77 100644 --- a/kernel/src/arch/x86_64/boot/mod.rs +++ b/kernel/src/arch/x86_64/boot/mod.rs @@ -3,6 +3,7 @@ use core::{arch::global_asm, sync::atomic::Ordering}; use kernel_arch_x86::registers::MSR_IA32_KERNEL_GS_BASE; use kernel_arch_x86_64::CPU_COUNT; +use libk_mm::address::{PhysicalAddress, Virtualize}; use tock_registers::interfaces::Writeable; use yboot_proto::{ v1::{FramebufferOption, MemoryMap}, @@ -17,6 +18,29 @@ pub enum BootData { YBoot(&'static LoadProtocolV1), } +impl BootData { + pub fn cmdline(&self) -> &str { + match self { + &Self::YBoot(data) => { + if data.cmdline != 0 && data.cmdline_len != 0 { + let address = PhysicalAddress::from_u64(data.cmdline).virtualize(); + let len = data.cmdline_len as usize; + let raw = unsafe { + core::slice::from_raw_parts( + core::ptr::with_exposed_provenance(address), + len, + ) + }; + + core::str::from_utf8(raw).unwrap_or("") + } else { + "" + } + } + } + } +} + const BOOT_STACK_SIZE: usize = 1024 * 1024; #[repr(C, align(0x20))] @@ -40,6 +64,9 @@ static YBOOT_DATA: LoadProtocolV1 = LoadProtocolV1 { memory_map: MemoryMap { address: 0, len: 0 }, + cmdline: 0, + cmdline_len: 0, + rsdp_address: 0, initrd_address: 0, initrd_size: 0, diff --git a/kernel/src/arch/x86_64/mod.rs b/kernel/src/arch/x86_64/mod.rs index d9b571f5..6381cd05 100644 --- a/kernel/src/arch/x86_64/mod.rs +++ b/kernel/src/arch/x86_64/mod.rs @@ -21,13 +21,13 @@ use kernel_arch_x86_64::{ }; use libk::{ arch::Cpu, - debug::{self, LogLevel}, - devfs, + config, debug, device::{ display::{fb_console::FramebufferConsole, DriverFlags, PixelFormat}, manager::DEVICE_REGISTRY, register_external_interrupt_controller, }, + fs::devfs, }; use libk_mm::{ address::PhysicalAddress, @@ -259,17 +259,25 @@ impl X86_64 { self.setup_from_boot_data()?; - let mut early = x86::init_platform_devices_early()?; + // TODO yboot doesn't yet pass any command line options + let cmdline = self.boot_data.get().cmdline(); + let mut early = x86::init_platform_early(cmdline)?; - if let Err(error) = self.init_framebuffer() { - log::error!("Could not initialize boot framebuffer: {error:?}"); + if !config::get().x86.disable_boot_fb { + if let Err(error) = self.init_framebuffer() { + log::error!("Could not initialize boot framebuffer: {error:?}"); + } } if let Some(acpi) = self.acpi.try_get() { self.init_platform_from_acpi(acpi)?; } - early.add_preferred_clock_source("hpet", Self::setup_hpet); + if !config::get().x86_64.disable_hpet { + early.add_preferred_clock_source("hpet", Self::setup_hpet); + } else { + log::info!("HPET disabled by config"); + } x86::init_platform_devices(early); } @@ -434,7 +442,7 @@ impl X86_64 { true, )?)); - debug::add_sink(fbconsole.clone(), LogLevel::Info); + debug::add_sink(fbconsole.clone(), config::get().debug.display_level); Ok(()) } diff --git a/kernel/src/device/serial/bcm2835_aux_uart.rs b/kernel/src/device/serial/bcm2835_aux_uart.rs index ce21cfd6..b40dfe8d 100644 --- a/kernel/src/device/serial/bcm2835_aux_uart.rs +++ b/kernel/src/device/serial/bcm2835_aux_uart.rs @@ -16,7 +16,7 @@ use device_tree::{ DeviceTreePropertyRead, }; use libk::{ - debug::{DebugSink, LogLevel}, + debug::DebugSink, device::{external_interrupt_controller, manager::DEVICE_REGISTRY}, vfs::{Terminal, TerminalInput, TerminalOutput}, }; @@ -170,7 +170,7 @@ impl Device for Bcm2835AuxUart { DEVICE_REGISTRY .serial_terminal - .register(inner.clone(), Some((self.clone(), LogLevel::Debug))) + .register(inner.clone(), Some(self.clone())) .ok(); Ok(()) diff --git a/kernel/src/device/serial/pl011.rs b/kernel/src/device/serial/pl011.rs index 7c90c736..7feffd5e 100644 --- a/kernel/src/device/serial/pl011.rs +++ b/kernel/src/device/serial/pl011.rs @@ -7,7 +7,7 @@ use device_api::{ }; use device_tree::driver::{device_tree_driver, Node, ProbeContext}; use libk::{ - debug::{DebugSink, LogLevel}, + debug::DebugSink, device::{external_interrupt_controller, manager::DEVICE_REGISTRY}, vfs::{Terminal, TerminalInput, TerminalOutput}, }; @@ -158,7 +158,7 @@ impl Device for Pl011 { DEVICE_REGISTRY .serial_terminal - .register(terminal.clone(), Some((self.clone(), LogLevel::Info))) + .register(terminal.clone(), Some(self.clone())) .ok(); Ok(()) diff --git a/xtask/src/build/cargo.rs b/xtask/src/build/cargo.rs index 842ecc33..bc964525 100644 --- a/xtask/src/build/cargo.rs +++ b/xtask/src/build/cargo.rs @@ -37,6 +37,7 @@ pub enum CargoBuilder<'e> { impl<'e> CargoBuilder<'e> { pub fn run, S: AsRef>(self, dir: P, arg: S) -> Result<(), Error> { + let arg = arg.as_ref(); let mut command = Command::new("cargo"); command.current_dir(dir); @@ -134,12 +135,17 @@ impl<'e> CargoBuilder<'e> { .join(env.kernel_triple) .display() ); - let artifact_dir_arg = - format!("--artifact-dir={}", env.kernel_output_dir.display()); command.arg(target_spec_arg); + command.arg(target_dir_arg); - command.arg(artifact_dir_arg); + + if arg != "clippy" && arg != "check" { + let artifact_dir_arg = + format!("--artifact-dir={}", env.kernel_output_dir.display()); + command.arg(artifact_dir_arg); + } + command.args([ "-Z", "build-std=core,alloc,compiler_builtins", diff --git a/xtask/src/build/x86_64.rs b/xtask/src/build/x86_64.rs index bee0ad35..a30cf20b 100644 --- a/xtask/src/build/x86_64.rs +++ b/xtask/src/build/x86_64.rs @@ -49,6 +49,7 @@ fn build_uefi_image( ) -> Result { log::info!("Building x86-64 UEFI image"); let image_path = env.kernel_output_dir.join("image.fat32"); + let yboot_cfg = env.workspace_root.join("etc/boot/yboot.cfg"); if !image_path.exists() { util::run_external_command( @@ -81,6 +82,11 @@ fn build_uefi_image( copy_into_fat(&boot_dir, &yboot.0, "BootX64.efi")?; copy_into_fat(&root_dir, &kernel.0 .0, "kernel.elf")?; copy_into_fat(&root_dir, &initrd.0, "initrd.img")?; + if yboot_cfg.exists() { + copy_into_fat(&root_dir, &yboot_cfg, "yboot.cfg")?; + } else { + root_dir.remove("yboot.cfg").ok(); + } Ok(ImageBuilt(image_path)) }