diff --git a/Cargo.lock b/Cargo.lock index 8dc4baa1..baaf1a60 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -392,6 +392,7 @@ dependencies = [ "derive_more", "hashbrown", "hbvm", + "ktest_macro", "limine", "log", "sbi", @@ -404,6 +405,14 @@ dependencies = [ "xml", ] +[[package]] +name = "ktest_macro" +version = "0.1.0" +dependencies = [ + "quote", + "syn", +] + [[package]] name = "lazy_static" version = "1.5.0" diff --git a/flake.lock b/flake.lock new file mode 100644 index 00000000..1a38c50f --- /dev/null +++ b/flake.lock @@ -0,0 +1,96 @@ +{ + "nodes": { + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1732014248, + "narHash": "sha256-y/MEyuJ5oBWrWAic/14LaIr/u5E0wRVzyYsouYY3W6w=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "23e89b7da85c3640bbc2173fe04f4bd114342367", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_2": { + "locked": { + "lastModified": 1728538411, + "narHash": "sha256-f0SBJz1eZ2yOuKUr5CA9BHULGXVSn6miBuUWdTyhUhU=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "b69de56fac8c2b6f8fd27f2eca01dcda8e0a4221", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs", + "rust-overlay": "rust-overlay" + } + }, + "rust-overlay": { + "inputs": { + "nixpkgs": "nixpkgs_2" + }, + "locked": { + "lastModified": 1732328983, + "narHash": "sha256-RHt12f/slrzDpSL7SSkydh8wUE4Nr4r23HlpWywed9E=", + "owner": "oxalica", + "repo": "rust-overlay", + "rev": "ed8aa5b64f7d36d9338eb1d0a3bb60cf52069a72", + "type": "github" + }, + "original": { + "owner": "oxalica", + "repo": "rust-overlay", + "type": "github" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 00000000..82a7b69e --- /dev/null +++ b/flake.nix @@ -0,0 +1,30 @@ +{ + description = "A devShell example"; + + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + rust-overlay.url = "github:oxalica/rust-overlay"; + flake-utils.url = "github:numtide/flake-utils"; + }; + + outputs = { self, nixpkgs, rust-overlay, flake-utils }: + flake-utils.lib.eachDefaultSystem (system: + let + overlays = [ (import rust-overlay) ]; + pkgs = import nixpkgs { + inherit system overlays; + }; + + rustToolchain = pkgs.pkgsBuildHost.rust-bin.fromRustupToolchainFile ./rust-toolchain.toml; + in + with pkgs; + { + devShells.default = mkShell { + buildInputs = [ + rustToolchain + qemu_full + ]; + }; + } + ); +} diff --git a/kernel/Cargo.toml b/kernel/Cargo.toml index 9cbe8956..9cee4de7 100644 --- a/kernel/Cargo.toml +++ b/kernel/Cargo.toml @@ -3,12 +3,15 @@ edition = "2021" name = "kernel" version = "0.2.0" +[features] +ktest = [] [dependencies] # embedded-graphics = "0.8" hbvm = { git = "https://git.ablecorp.us/AbleOS/holey-bytes.git", features = [ "nightly", ] } +ktest_macro = { path = "ktest_macro" } log = "0.4" spin = "0.9" slab = { version = "0.4", default-features = false } diff --git a/kernel/ktest_macro/Cargo.toml b/kernel/ktest_macro/Cargo.toml new file mode 100644 index 00000000..5c85e6cc --- /dev/null +++ b/kernel/ktest_macro/Cargo.toml @@ -0,0 +1,11 @@ +[package] +edition = "2021" +name = "ktest_macro" +version = "0.1.0" + +[lib] +proc-macro = true + +[dependencies] +quote = "1.0.37" +syn = { version = "2.0.89", features = ["full"] } \ No newline at end of file diff --git a/kernel/ktest_macro/src/lib.rs b/kernel/ktest_macro/src/lib.rs new file mode 100644 index 00000000..c1c3a8a3 --- /dev/null +++ b/kernel/ktest_macro/src/lib.rs @@ -0,0 +1,29 @@ +extern crate proc_macro; +extern crate quote; +extern crate syn; +use { + proc_macro::TokenStream, + quote::quote, + syn::{parse_macro_input, ItemFn} +}; + +#[proc_macro_attribute] +pub fn ktest(_attr: TokenStream, item: TokenStream) -> TokenStream { + let input = parse_macro_input!(item as ItemFn); + let test_name = &input.sig.ident; + let static_var_name = syn::Ident::new( + &format!("__ktest_{}", test_name), + test_name.span(), + ); + let out = quote! { + // #[cfg(feature = "ktest")] + #input + + // #[cfg(feature = "ktest")] + #[unsafe(link_section = ".note.ktest")] + #[used] + pub static #static_var_name: fn() = #test_name; + }; + + TokenStream::from(out) +} \ No newline at end of file diff --git a/kernel/lds/x86_64.ld b/kernel/lds/x86_64.ld index 19ee79f6..84e8c4d7 100644 --- a/kernel/lds/x86_64.ld +++ b/kernel/lds/x86_64.ld @@ -40,6 +40,13 @@ SECTIONS *(.data .data.*) *(.got .got.*) } :data + + /* Add the .ktest section for test functions */ + .note.ktest : { + __ktest_start = .; /* Mark the beginning of the section */ + *(.note.ktest) /* Include all items in the .ktest section */ + __ktest_end = .; /* Mark the end of the section */ + } .bss : { *(COMMON) diff --git a/kernel/src/kmain.rs b/kernel/src/kmain.rs index ec87c6f8..625b9cd7 100644 --- a/kernel/src/kmain.rs +++ b/kernel/src/kmain.rs @@ -22,6 +22,14 @@ use { pub fn kmain(_cmdline: &str, boot_modules: BootModules) -> ! { debug!("Entered kmain"); + #[cfg(feature = "ktest")] { + use crate::ktest; + debug!("TESTING"); + ktest::test_main(); + + loop {}; + } + // let kcmd = build_cmd("Kernel Command Line", cmdline); // trace!("Cmdline: {kcmd:?}"); @@ -148,10 +156,3 @@ pub static IPC_BUFFERS: Lazy> = Lazy::new(|| { Mutex::new(bufs) }); - -#[test_case] -fn trivial_assertion() { - trace!("trivial assertion... "); - assert_eq!(1, 1); - info!("[ok]"); -} diff --git a/kernel/src/ktest.rs b/kernel/src/ktest.rs new file mode 100644 index 00000000..43a2a11a --- /dev/null +++ b/kernel/src/ktest.rs @@ -0,0 +1,38 @@ +pub use ktest_macro::ktest; +use log::debug; + +extern "C" { + static __ktest_start: fn(); + static __ktest_end: fn(); +} + +// TODO: Get test_fn linker name (may require no_mangle in macro) +// More info on tests (run the rest even if panic) +// Implement ktest for arm and riscv (Later problems, see below) +// Allow for arch specific tests (Leave for now) +// Allow for ktest test name attr +// Usefull message at the end of testing +pub fn test_main() { + unsafe { + let mut current_test = &__ktest_start as *const fn(); + let mut current = 1; + let test_end = &__ktest_end as *const fn(); + + while current_test < test_end { + let test_fn = *current_test; + + debug!("Running test {}", current); + + test_fn(); + debug!("Test {} passed", current); + + current_test = current_test.add(1); + current += 1; + } + } +} + +#[ktest] +pub fn trivial_assertion() { + assert_eq!(1, 1); +} \ No newline at end of file diff --git a/kernel/src/lib.rs b/kernel/src/lib.rs index f5bf86f8..52956ecd 100644 --- a/kernel/src/lib.rs +++ b/kernel/src/lib.rs @@ -2,6 +2,7 @@ //! Named akern. //! Akern is woefully undersupported at the moment but we are looking to add support improve hardware discovery and make our lives as kernel and operating system developers easier and better #![no_std] +#![no_main] #![feature( slice_split_once, exclusive_wrapper, @@ -10,11 +11,9 @@ lazy_get, alloc_error_handler, ptr_sub_ptr, - custom_test_frameworks, naked_functions, - pointer_is_aligned_to + pointer_is_aligned_to, )] -#![test_runner(crate::test_runner)] #![allow(dead_code, internal_features, static_mut_refs)] extern crate alloc; @@ -33,6 +32,10 @@ mod memory; mod task; mod utils; +// #[cfg(feature = "tests")] +mod ktest; + +use alloc::string::ToString; use versioning::Version; /// Kernel's version @@ -56,15 +59,7 @@ fn panic(info: &core::panic::PanicInfo) -> ! { )); } - let msg = info.message(); + let msg = info.message().to_string().replace("\n", "\r\n"); let _ = crate::arch::log(format_args!("{msg}\r\n")); loop {} } - -#[cfg(test)] -fn test_runner(tests: &[&dyn Fn()]) { - println!("Running {} tests", tests.len()); - for test in tests { - test(); - } -} diff --git a/repbuild/src/main.rs b/repbuild/src/main.rs index 2741ef52..5a80bf49 100644 --- a/repbuild/src/main.rs +++ b/repbuild/src/main.rs @@ -27,6 +27,7 @@ fn main() -> Result<(), Error> { let mut release = false; let mut debuginfo = false; let mut target = Target::X86_64; + let mut tests = false; for arg in args { if arg == "-r" || arg == "--release" { release = true; @@ -38,17 +39,42 @@ fn main() -> Result<(), Error> { target = Target::Aarch64; } else if arg == "avx2" { target = Target::X86_64Avx2; + } else if arg == "--ktest" { + tests = true; } else { return Err(report!(Error::InvalidSubCom)); } } - build(release, target, debuginfo).change_context(Error::Build) + build(release, target, debuginfo, tests).change_context(Error::Build) } + // Some("test" | "t") => { + // let mut release = false; + // let mut debuginfo = false; + // let mut target = Target::X86_64; + // for arg in args { + // if arg == "-r" || arg == "--release" { + // release = true; + // } else if arg == "-d" || arg == "--debuginfo" { + // debuginfo = true; + // } else if arg == "rv64" || arg == "riscv64" || arg == "riscv64-virt" { + // target = Target::Riscv64Virt; + // } else if arg == "arm64" || arg == "aarch64" || arg == "aarch64-virt" { + // target = Target::Aarch64; + // } else if arg == "avx2" { + // target = Target::X86_64Avx2; + // } else { + // return Err(report!(Error::InvalidSubCom)); + // } + // } + + // test(release, target, debuginfo).change_context(Error::Build) + // } Some("run" | "r") => { let mut release = false; let mut debuginfo = false; let mut target = Target::X86_64; + let mut tests = false; let mut do_accel = true; for arg in args { if arg == "-r" || arg == "--release" { @@ -63,12 +89,14 @@ fn main() -> Result<(), Error> { do_accel = false; } else if arg == "avx2" { target = Target::X86_64Avx2; + } else if arg == "--ktest" { + tests = true; } else { return Err(report!(Error::InvalidSubCom)); } } - build(release, target, debuginfo)?; + build(release, target, debuginfo, tests)?; run(release, target, do_accel) } Some("help" | "h") => { @@ -82,6 +110,7 @@ fn main() -> Result<(), Error> { " -r / --release: build in release mode\n", " -d / --debuginfo: build with debug info\n", " --noaccel: run without acceleration (e.g, no kvm)\n", + " --ktest: Enables tests via ktest\n", "[ rv64 / riscv64 / riscv64-virt / aarch64 / arm64 / aarch64-virt / avx2 ]: sets target" ),); Ok(()) @@ -310,7 +339,7 @@ fn copy_file_to_img(fpath: &str, fs: &FileSystem) { .expect("Copy failed"); } -fn build(release: bool, target: Target, debuginfo: bool) -> Result<(), Error> { +fn build(release: bool, target: Target, debuginfo: bool, tests: bool) -> Result<(), Error> { let fs = get_fs().change_context(Error::Io)?; let mut com = Command::new("cargo"); com.current_dir("kernel"); @@ -322,6 +351,10 @@ fn build(release: bool, target: Target, debuginfo: bool) -> Result<(), Error> { com.env("RUSTFLAGS", "-Cdebug-assertions=true"); } + if tests { + com.args(["--features", "ktest"]); + } + if target == Target::Riscv64Virt { com.args(["--target", "targets/riscv64-virt-ableos.json"]); } @@ -473,6 +506,7 @@ fn run(release: bool, target: Target, do_accel: bool) -> Result<(), Error> { } } + fn fetch_ovmf(target: Target) -> Result { let (ovmf_url, ovmf_path) = match target { Target::X86_64 | Target::X86_64Avx2 => (