cfg: allow passing cmdline options to the kernel
This commit is contained in:
parent
cb5814a5ce
commit
49b918e2ac
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,3 +1,5 @@
|
||||
/target
|
||||
/toolchain
|
||||
/xtask.toml
|
||||
/qemu.toml
|
||||
/etc/boot/yboot.cfg
|
||||
|
@ -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,
|
||||
|
39
boot/yboot/src/config.rs
Normal file
39
boot/yboot/src/config.rs
Normal file
@ -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<Self> {
|
||||
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)
|
||||
}
|
||||
}
|
@ -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::<SimpleFileSystem>(fs_handle)
|
||||
}
|
||||
|
||||
fn open_root(image: Handle, bs: &BootServices) -> Result<Directory, Error> {
|
||||
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<Boot>,
|
||||
) -> 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<Boot>) -> 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);
|
||||
|
@ -536,7 +536,7 @@ impl<K: KernelTableManager, PA: PhysicalMemoryAllocator<Address = PhysicalAddres
|
||||
for TaskContextImpl<K, PA>
|
||||
{
|
||||
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) {
|
||||
|
@ -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};
|
||||
|
@ -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:
|
||||
|
@ -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<TNode<'a>> {
|
||||
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("<unknown-prop>");
|
||||
|
||||
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::<u32>() == 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("<unknown>");
|
||||
|
||||
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");
|
||||
}
|
||||
|
57
kernel/libk/src/config/general.rs
Normal file
57
kernel/libk/src/config/general.rs
Normal file
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
118
kernel/libk/src/config/mod.rs
Normal file
118
kernel/libk/src/config/mod.rs
Normal file
@ -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<Options> = OneTimeInit::new();
|
||||
static OPTIONS_STRING: OneTimeInit<String> = 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<Item = &'a str>>(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<String, Error> {
|
||||
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(' '));
|
||||
}
|
30
kernel/libk/src/config/x86.rs
Normal file
30
kernel/libk/src/config/x86.rs
Normal file
@ -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
|
||||
}
|
||||
}
|
30
kernel/libk/src/config/x86_64.rs
Normal file
30
kernel/libk/src/config/x86_64.rs
Normal file
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
@ -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) {
|
||||
|
@ -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<dyn CharDevice>,
|
||||
sink_level: Option<(Arc<dyn DebugSink>, LogLevel)>,
|
||||
sink: Option<Arc<dyn DebugSink>>,
|
||||
) -> Result<u32, Error> {
|
||||
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)
|
||||
}
|
||||
|
@ -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<String, Error> {
|
||||
let _ = state;
|
||||
@ -69,7 +70,9 @@ impl<V: StringAttributeOps> RegularImpl for StringAttributeNode<V> {
|
||||
}
|
||||
|
||||
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),
|
||||
|
@ -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;
|
||||
|
@ -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");
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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();
|
||||
|
@ -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<EarlyPlatformDevices, Error> {
|
||||
pub fn init_platform_early(cmdline: &str) -> Result<EarlyPlatformDevices, Error> {
|
||||
sysfs::init();
|
||||
devfs::init();
|
||||
debug::init();
|
||||
|
||||
config::parse_boot_arguments(cmdline);
|
||||
|
||||
// Initialize async executor queue
|
||||
runtime::init_task_queue();
|
||||
|
||||
|
@ -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<Self>) -> Result<(), Error> {
|
||||
DEVICE_REGISTRY
|
||||
.serial_terminal
|
||||
.register(self.terminal.clone(), Some((self.clone(), LogLevel::Debug)))
|
||||
.register(self.terminal.clone(), Some(self.clone()))
|
||||
.ok();
|
||||
Ok(())
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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(())
|
||||
}
|
||||
|
@ -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(())
|
||||
|
@ -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(())
|
||||
|
@ -37,6 +37,7 @@ pub enum CargoBuilder<'e> {
|
||||
|
||||
impl<'e> CargoBuilder<'e> {
|
||||
pub fn run<P: AsRef<Path>, S: AsRef<OsStr>>(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",
|
||||
|
@ -49,6 +49,7 @@ fn build_uefi_image(
|
||||
) -> Result<ImageBuilt, Error> {
|
||||
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))
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user