From 461c7fbce07487c651e682479f4221828e727487 Mon Sep 17 00:00:00 2001
From: Eemeli <eemeli.o.lehtonen@utu.fi>
Date: Sun, 15 Jan 2023 21:42:06 +0200
Subject: [PATCH] kernel commandline

---
 Cargo.lock                            |   3 +-
 Cargo.toml                            |   8 +-
 cfg/limine.cfg                        |   2 +-
 src/arch/x86_64/gdt.rs                |   5 +-
 src/arch/x86_64/idt.rs                |   6 +-
 src/arch/x86_64/limine/cmdline.rs     |  18 ++++
 src/arch/x86_64/limine/framebuffer.rs |   4 +-
 src/arch/x86_64/limine/mod.rs         |  12 +--
 src/env.rs                            |  80 +++++++++++++++
 src/log.rs                            | 135 ++++++++++++++++++++++++--
 src/main.rs                           |  26 ++++-
 src/splash                            |   8 ++
 src/term/escape/encode.rs             |  30 ++++--
 src/video/logger.rs                   |   5 +-
 14 files changed, 303 insertions(+), 39 deletions(-)
 create mode 100644 src/arch/x86_64/limine/cmdline.rs
 create mode 100644 src/env.rs
 create mode 100644 src/splash

diff --git a/Cargo.lock b/Cargo.lock
index e26fdd6..1dec863 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -275,8 +275,7 @@ checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79"
 [[package]]
 name = "limine"
 version = "0.1.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c847ac148a0c53ba3755dfa9830722b1043179584009869e6afc2b413e13f105"
+source = "git+https://github.com/limine-bootloader/limine-rs?rev=c2fbc349419d4330b80e053019ad2fe504a61764#c2fbc349419d4330b80e053019ad2fe504a61764"
 
 [[package]]
 name = "lock_api"
diff --git a/Cargo.toml b/Cargo.toml
index 49de3ad..96e7ffd 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -21,8 +21,14 @@ spin = "0.9.4"
 volatile = "0.4.5"
 x86_64 = "0.14.10"
 uart_16550 = "0.2.18"
-limine = { version = "0.1.9", optional = true }
 #tracing = { version = "0.1.37", default-features = false }
 
+[dependencies.limine]
+git = "https://github.com/limine-bootloader/limine-rs"
+rev = "c2fbc349419d4330b80e053019ad2fe504a61764"
+# version = "0.1.9" # 0.1.9 LimineFile struct has a bug
+optional = true
+
+
 [build-dependencies]
 image = "0.24.5"
diff --git a/cfg/limine.cfg b/cfg/limine.cfg
index fd84e95..0201da3 100644
--- a/cfg/limine.cfg
+++ b/cfg/limine.cfg
@@ -4,4 +4,4 @@ TIMEOUT=0
 :Hyperion
     PROTOCOL=limine
     KERNEL_PATH=boot:///hyperion
-    # KERNEL_CMDLINE=
+    KERNEL_CMDLINE=log=trace
diff --git a/src/arch/x86_64/gdt.rs b/src/arch/x86_64/gdt.rs
index c7a4b29..9589fbd 100644
--- a/src/arch/x86_64/gdt.rs
+++ b/src/arch/x86_64/gdt.rs
@@ -1,6 +1,5 @@
-use crate::println;
-
 use super::idt::DOUBLE_FAULT_IST;
+use crate::debug;
 use spin::Lazy;
 use x86_64::{
     instructions::tables::load_tss,
@@ -15,7 +14,7 @@ use x86_64::{
 //
 
 pub fn init() {
-    println!("Initializing GDT");
+    debug!("Initializing GDT");
     GDT.0.load();
 
     unsafe {
diff --git a/src/arch/x86_64/idt.rs b/src/arch/x86_64/idt.rs
index 4a09b04..41943cd 100644
--- a/src/arch/x86_64/idt.rs
+++ b/src/arch/x86_64/idt.rs
@@ -1,4 +1,4 @@
-use crate::println;
+use crate::{debug, info};
 use spin::Lazy;
 use x86_64::structures::idt::{InterruptDescriptorTable, InterruptStackFrame};
 
@@ -9,14 +9,14 @@ pub static DOUBLE_FAULT_IST: u16 = 1;
 //
 
 pub fn init() {
-    println!("Initializing IDT");
+    debug!("Initializing IDT");
     IDT.load();
 }
 
 //
 
 extern "x86-interrupt" fn breakpoint(stack: InterruptStackFrame) {
-    println!("INT: Breakpoint\n{stack:#?}")
+    info!("INT: Breakpoint\n{stack:#?}")
 }
 
 extern "x86-interrupt" fn double_fault(stack: InterruptStackFrame, ec: u64) -> ! {
diff --git a/src/arch/x86_64/limine/cmdline.rs b/src/arch/x86_64/limine/cmdline.rs
new file mode 100644
index 0000000..26c4b5b
--- /dev/null
+++ b/src/arch/x86_64/limine/cmdline.rs
@@ -0,0 +1,18 @@
+use crate::env::Arguments;
+use limine::LimineKernelFileRequest;
+
+//
+
+pub fn init() {
+    static REQ: LimineKernelFileRequest = LimineKernelFileRequest::new(0);
+
+    if let Some(cmdline) = REQ
+        .get_response()
+        .get()
+        .and_then(|resp| resp.kernel_file.get())
+        .and_then(|file| file.cmdline.to_str())
+        .and_then(|cmdline| cmdline.to_str().ok())
+    {
+        Arguments::parse(cmdline);
+    }
+}
diff --git a/src/arch/x86_64/limine/framebuffer.rs b/src/arch/x86_64/limine/framebuffer.rs
index d7e874e..1238512 100644
--- a/src/arch/x86_64/limine/framebuffer.rs
+++ b/src/arch/x86_64/limine/framebuffer.rs
@@ -1,5 +1,5 @@
 use crate::{
-    println,
+    debug,
     video::framebuffer::{get_fbo, Framebuffer, FramebufferInfo, FBO},
 };
 use core::slice;
@@ -36,5 +36,5 @@ pub fn init() {
         fbo.clear();
         FBO.call_once(|| Mutex::new(fbo));
     }
-    println!("Global framebuffer: {:#?}", get_fbo().map(|f| f.info))
+    debug!("Global framebuffer: {:#?}", get_fbo().map(|f| f.info))
 }
diff --git a/src/arch/x86_64/limine/mod.rs b/src/arch/x86_64/limine/mod.rs
index 9c90210..f188493 100644
--- a/src/arch/x86_64/limine/mod.rs
+++ b/src/arch/x86_64/limine/mod.rs
@@ -1,6 +1,5 @@
-use crate::println;
-
 use super::{gdt, idt};
+use crate::debug;
 
 //
 
@@ -8,6 +7,7 @@ pub use term::_print;
 
 //
 
+mod cmdline;
 mod framebuffer;
 mod term;
 
@@ -15,18 +15,18 @@ mod term;
 
 #[no_mangle]
 pub extern "C" fn _start() -> ! {
-    x86_64::instructions::interrupts::disable();
-    *crate::BOOTLOADER.lock() = "Limine";
+    crate::BOOTLOADER.call_once(|| "Limine");
 
+    cmdline::init();
     framebuffer::init();
 
     gdt::init();
     idt::init();
 
-    println!("Re-enabling x86_64 interrupts");
+    debug!("Re-enabling x86_64 interrupts");
     x86_64::instructions::interrupts::enable();
 
-    println!("Calling general kernel_main");
+    debug!("Calling general kernel_main");
     crate::kernel_main()
 }
 
diff --git a/src/env.rs b/src/env.rs
new file mode 100644
index 0000000..899980f
--- /dev/null
+++ b/src/env.rs
@@ -0,0 +1,80 @@
+use crate::log::{self, LogLevel};
+use spin::Once;
+
+//
+
+//
+
+pub fn args() -> Arguments {
+    Arguments::get()
+}
+
+//
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub struct Arguments {
+    pub log_level: LogLevel,
+    // log_color: bool,
+    pub had_unrecognized: bool,
+
+    pub cmdline: &'static str,
+}
+
+//
+
+impl Arguments {
+    pub fn parse(s: &'static str) {
+        ARGUMENTS.call_once(|| {
+            let mut iter = s.split_whitespace();
+            let mut result = Arguments::default();
+            result.cmdline = s;
+
+            while let Some(item) = iter.next() {
+                match item {
+                    "log" => {
+                        if let Some(level) = iter.next() {
+                            if let Some(l) = LogLevel::parse(level) {
+                                result.log_level = l
+                            } else {
+                                result.had_unrecognized = true
+                            }
+                        }
+                    }
+                    _ if item.starts_with("log=") => {
+                        if let Some(l) = LogLevel::parse(&item[4..]) {
+                            result.log_level = l
+                        } else {
+                            result.had_unrecognized = true
+                        }
+                    }
+                    _ => result.had_unrecognized = true,
+                }
+            }
+
+            result.assign();
+
+            result
+        });
+    }
+
+    pub fn get() -> Self {
+        ARGUMENTS.get().copied().unwrap_or(Self::default())
+    }
+
+    pub fn assign(&self) {
+        log::set_log_level(self.log_level);
+        // log::set_log_color(self.log_color);
+    }
+}
+
+impl Default for Arguments {
+    fn default() -> Self {
+        Self {
+            log_level: LogLevel::default(),
+            had_unrecognized: false,
+            cmdline: "",
+        }
+    }
+}
+
+static ARGUMENTS: Once<Arguments> = Once::new();
diff --git a/src/log.rs b/src/log.rs
index 0cb6a59..f23f82e 100644
--- a/src/log.rs
+++ b/src/log.rs
@@ -1,8 +1,8 @@
+use crate::term::escape::encode::EscapeEncoder;
 use core::{
     fmt::Arguments,
-    sync::atomic::{AtomicBool, Ordering},
+    sync::atomic::{AtomicBool, AtomicU8, Ordering},
 };
-use spin::Lazy;
 
 //
 
@@ -17,6 +17,41 @@ macro_rules! println {
     ($($t:tt)*) => { $crate::log::_print(format_args_nl!($($t)*)) };
 }
 
+#[macro_export]
+macro_rules! log {
+    ($level:expr, $($t:tt)*) => {
+        if $crate::log::test_log_level($level) {
+            $crate::log::_print_log_stamp($level, module_path!());
+            $crate::println!($($t)*);
+        }
+    };
+}
+
+#[macro_export]
+macro_rules! error {
+    ($($t:tt)*) => { $crate::log!($crate::log::LogLevel::Error, $($t)*) };
+}
+
+#[macro_export]
+macro_rules! warn {
+    ($($t:tt)*) => { $crate::log!($crate::log::LogLevel::Warn, $($t)*) };
+}
+
+#[macro_export]
+macro_rules! info {
+    ($($t:tt)*) => { $crate::log!($crate::log::LogLevel::Info, $($t)*) };
+}
+
+#[macro_export]
+macro_rules! debug {
+    ($($t:tt)*) => { $crate::log!($crate::log::LogLevel::Debug, $($t)*) };
+}
+
+#[macro_export]
+macro_rules! trace {
+    ($($t:tt)*) => { $crate::log!($crate::log::LogLevel::Trace, $($t)*) };
+}
+
 //
 
 // pub fn enable_term() {
@@ -43,9 +78,89 @@ pub fn disable_qemu() {
     LOGGER.qemu.store(false, Ordering::SeqCst);
 }
 
+pub fn set_log_level(level: LogLevel) {
+    LOGGER.level.store(level as u8, Ordering::SeqCst);
+}
+
+// pub fn set_log_color(color: bool) {
+//     LOGGER.color.store(color, Ordering::SeqCst);
+// }
+
+pub fn test_log_level(level: LogLevel) -> bool {
+    LOGGER.level.load(Ordering::SeqCst) >= level as u8
+}
+
+#[doc(hidden)]
+pub fn _print_log_stamp(level: LogLevel, module: &str) {
+    // if !LOGGER.color.load(Ordering::SeqCst) {
+    //     print!("[{level:?}]: ")
+    // } else {
+    let level = match level {
+        LogLevel::None => " NONE  ",
+        LogLevel::Error => "\x1b[38;2;255;85;85m ERROR ",
+        LogLevel::Warn => "\x1b[38;2;255;255;85m WARN  ",
+        LogLevel::Info => "\x1b[38;2;85;255;85m INFO  ",
+        LogLevel::Debug => "\x1b[38;2;85;255;255m DEBUG ",
+        LogLevel::Trace => "\x1b[38;2;255;85;255m TRACE ",
+    };
+
+    print!(
+        "{}{level} {} {}: ",
+        '['.true_grey(),
+        module.true_grey(),
+        ']'.true_grey(),
+    )
+    // }
+}
+
 //
 
-static LOGGER: Lazy<Logger> = Lazy::new(Logger::init);
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
+#[repr(u8)]
+pub enum LogLevel {
+    None,
+    Error,
+    Warn,
+    Info,
+    Debug,
+    Trace,
+}
+
+//
+
+impl LogLevel {
+    pub const DEFAULT: Self = Self::Info;
+    pub const ALL: [LogLevel; 5] = [
+        Self::Error,
+        Self::Warn,
+        Self::Info,
+        Self::Debug,
+        Self::Trace,
+    ];
+
+    pub fn parse(s: &str) -> Option<Self> {
+        // TODO: match any case
+        Some(match s {
+            "none" => Self::None,
+            "error" => Self::Error,
+            "warn" => Self::Warn,
+            "info" => Self::Info,
+            "debug" => Self::Debug,
+            "trace" => Self::Trace,
+            _ => return None,
+        })
+    }
+}
+
+impl Default for LogLevel {
+    fn default() -> Self {
+        Self::DEFAULT
+    }
+}
+
+//
+
+static LOGGER: Logger = Logger::init();
 
 struct Logger {
     // Log to a bootloader given terminal
@@ -56,14 +171,22 @@ struct Logger {
 
     // Log to a QEMU serial
     qemu: AtomicBool,
+
+    // [`LogLevel`] in u8 form
+    level: AtomicU8,
+    // print logs with colors
+    // color: AtomicBool,
 }
 
 impl Logger {
-    fn init() -> Self {
+    const fn init() -> Self {
         Logger {
             // term: false.into(),
-            fbo: true.into(),
-            qemu: true.into(),
+            fbo: AtomicBool::new(true),
+            qemu: AtomicBool::new(true),
+
+            level: AtomicU8::new(LogLevel::DEFAULT as u8),
+            // color: AtomicBool::new(true),
         }
     }
 
diff --git a/src/main.rs b/src/main.rs
index b75b122..660ee75 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -4,22 +4,23 @@
 #![feature(abi_x86_interrupt)]
 #![feature(custom_test_frameworks)]
 #![feature(type_alias_impl_trait)]
+#![feature(result_option_inspect)]
 #![test_runner(crate::testfw::test_runner)]
 #![reexport_test_harness_main = "test_main"]
 
 //
 
-use spin::Mutex;
-
 use crate::{
     term::escape::encode::EscapeEncoder,
     video::framebuffer::{get_fbo, Color},
 };
+use spin::Once;
 
 //
 
 #[path = "arch/x86_64/mod.rs"]
 pub mod arch;
+pub mod env;
 pub mod log;
 pub mod panic;
 pub mod qemu;
@@ -38,13 +39,20 @@ pub static KERNEL: &str = if cfg!(test) {
 };
 
 /// Name of the detected bootloader
-pub static BOOTLOADER: Mutex<&'static str> = Mutex::new(KERNEL);
+pub static BOOTLOADER: Once<&'static str> = Once::new();
 
 //
 
 fn kernel_main() -> ! {
-    println!("\n\nHello from {}", KERNEL.cyan());
-    println!(" - {} was booted with {}", KERNEL.cyan(), BOOTLOADER.lock());
+    debug!("Cmdline: {:?}", env::Arguments::get());
+
+    // ofc. every kernel has to have this cringy ascii name splash
+    info!("\n{}\n", include_str!("./splash"));
+
+    if let Some(bl) = BOOTLOADER.get() {
+        let kernel = KERNEL.true_cyan();
+        debug!("{kernel} was booted with {bl}");
+    }
 
     // error handling test
     // stack_overflow(79999999);
@@ -52,6 +60,14 @@ fn kernel_main() -> ! {
     //     *(0xFFFFFFFFDEADC0DE as *mut u8) = 42;
     // }
 
+    // for level in log::LogLevel::ALL {
+    //     log!(level, "LOG TEST")
+    // }
+
+    // for c in 0..=255u8 {
+    //     print!("{}", c as char);
+    // }
+
     if let Some(mut fbo) = get_fbo() {
         fbo.fill(240, 340, 40, 40, Color::RED);
         fbo.fill(250, 350, 60, 40, Color::GREEN);
diff --git a/src/splash b/src/splash
new file mode 100644
index 0000000..3aebd48
--- /dev/null
+++ b/src/splash
@@ -0,0 +1,8 @@
+  _    _                       _
+ | |  | |                     (_)
+ | |__| |_   _ _ __   ___ _ __ _  ___  _ __
+ |  __  | | | | '_ \ / _ \ '__| |/ _ \| '_ \
+ | |  | | |_| | |_) |  __/ |  | | (_) | | | |
+ |_|  |_|\__, |  __/ \___|_|  |_|\___/|_| |_|
+          __/ | |
+         |___/|_|
diff --git a/src/term/escape/encode.rs b/src/term/escape/encode.rs
index 23af242..6eec9ea 100644
--- a/src/term/escape/encode.rs
+++ b/src/term/escape/encode.rs
@@ -7,29 +7,41 @@ pub trait EscapeEncoder {
         EncodedPart { code, data: self }
     }
 
-    fn red(&self) -> EncodedPart<Self> {
+    fn true_red(&self) -> EncodedPart<Self> {
         self.with_escape_code("\x1B[38;2;255;0;0m")
     }
 
-    fn green(&self) -> EncodedPart<Self> {
+    fn true_green(&self) -> EncodedPart<Self> {
         self.with_escape_code("\x1B[38;2;0;255;0m")
     }
 
-    fn blue(&self) -> EncodedPart<Self> {
+    fn true_blue(&self) -> EncodedPart<Self> {
         self.with_escape_code("\x1B[38;2;0;0;255m")
     }
 
-    fn cyan(&self) -> EncodedPart<Self> {
+    fn true_cyan(&self) -> EncodedPart<Self> {
         self.with_escape_code("\x1B[38;2;0;255;255m")
     }
 
-    fn magenta(&self) -> EncodedPart<Self> {
+    fn true_magenta(&self) -> EncodedPart<Self> {
         self.with_escape_code("\x1B[38;2;255;0;255m")
     }
 
-    fn yellow(&self) -> EncodedPart<Self> {
+    fn true_yellow(&self) -> EncodedPart<Self> {
         self.with_escape_code("\x1B[38;2;255;255;0m")
     }
+
+    fn true_black(&self) -> EncodedPart<Self> {
+        self.with_escape_code("\x1B[38;2;0;0;0m")
+    }
+
+    fn true_white(&self) -> EncodedPart<Self> {
+        self.with_escape_code("\x1B[38;2;255;255;255m")
+    }
+
+    fn true_grey(&self) -> EncodedPart<Self> {
+        self.with_escape_code("\x1B[38;2;128;128;128m")
+    }
 }
 
 pub struct EncodedPart<'a, T: ?Sized> {
@@ -39,7 +51,11 @@ pub struct EncodedPart<'a, T: ?Sized> {
 
 //
 
-impl EscapeEncoder for &str {}
+// impl EscapeEncoder for &str {}
+//
+// impl EscapeEncoder for char {}
+
+impl<T> EscapeEncoder for T {}
 
 impl<'a, T> fmt::Display for EncodedPart<'a, T>
 where
diff --git a/src/video/logger.rs b/src/video/logger.rs
index 2812854..18f2ad4 100644
--- a/src/video/logger.rs
+++ b/src/video/logger.rs
@@ -1,9 +1,8 @@
-use crate::term::escape::decode::{DecodedPart, EscapeDecoder};
-
 use super::{
     font::FONT,
     framebuffer::{get_fbo, Color, Framebuffer},
 };
+use crate::term::escape::decode::{DecodedPart, EscapeDecoder};
 use core::fmt::{self, Arguments, Write};
 use spin::{Mutex, MutexGuard};
 
@@ -125,7 +124,7 @@ impl Writer {
     }
 
     fn size(fbo: &mut MutexGuard<Framebuffer>) -> [u16; 2] {
-        [(fbo.width / 16) as _, (fbo.height / 16) as _]
+        [(fbo.width / 8) as _, (fbo.height / 16) as _]
     }
 }
 
-- 
GitLab