Skip to content

Commit

Permalink
add test kernels to finally add some tests
Browse files Browse the repository at this point in the history
The test kernels are built by the build.rs file. Unfortunately, there is no #[cfg(test)] for build.rs files yet, so the test kernels are always built.
Simply run `cargo test` to run the test kernels.
  • Loading branch information
tsatke committed Oct 31, 2023
1 parent 11bef6f commit 4e74843
Show file tree
Hide file tree
Showing 13 changed files with 285 additions and 31 deletions.
37 changes: 35 additions & 2 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,43 @@ env:
CARGO_TERM_COLOR: always

jobs:
test:
name: "Test"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/cache@v3
with:
path: |
~/.cargo/bin/
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
target/
!target/release/build/devos-*
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
- name: Install latest nightly
uses: actions-rs/toolchain@v1
with:
toolchain: nightly
override: true
components: rustfmt, clippy, llvm-tools-preview, rust-src
- name: Add build target
run: |
rustup target add x86_64-unknown-none
- name: Install e2fsprogs
run: |
sudo apt install -y e2fsprogs
- name: Test
run: |
cargo test
- name: Test release
run: |
cargo test --release
build:

name: "Build and upload artifacts"
needs: test
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v3
- uses: actions/cache@v3
Expand Down
19 changes: 19 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ version = "0.1.0"
bootloader = "0.11"
kernel = { path = "kernel", artifact = "bin", target = "x86_64-unknown-none", default-features = false }
hello_world = { path = "userspace/hello_world", artifact = "bin", target = "x86_64-unknown-none" }
test_kernel_multitasking = { path = "test_kernels/test_kernel_multitasking", artifact = "bin", target = "x86_64-unknown-none" }
test_kernel_vfs = { path = "test_kernels/test_kernel_vfs", artifact = "bin", target = "x86_64-unknown-none" }

[dependencies]
clap = { version = "4", features = ["derive"] }
Expand All @@ -22,6 +24,8 @@ members = [
"driver/ide",
"driver/pci",
"driver/vga",
"test_kernels/test_kernel_multitasking",
"test_kernels/test_kernel_vfs",
"userspace/hello_world",
]

Expand Down
20 changes: 20 additions & 0 deletions build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,26 @@ fn main() {
// pass the disk image paths as env variables to the `main.rs`
println!("cargo:rustc-env=UEFI_PATH={}", uefi_path.display());

for test_kernel in ["test_kernel_multitasking", "test_kernel_vfs"] {
let test_kernel_vfs = PathBuf::from(
std::env::var_os(format!(
"CARGO_BIN_FILE_{}_{}",
test_kernel.to_uppercase(),
test_kernel
))
.unwrap(),
);
let test_kernel_vfs_path = out_dir.join("test_kernel_vfs.img");
bootloader::UefiBoot::new(&test_kernel_vfs)
.create_disk_image(&test_kernel_vfs_path)
.unwrap();
println!(
"cargo:rustc-env={}_PATH={}",
test_kernel.to_uppercase(),
test_kernel_vfs_path.display()
);
}

let os_disk_dir = collect_os_disk_artifacts(&out_dir);
let os_disk_image = create_disk_image(&out_dir, &os_disk_dir);
println!("cargo:rustc-env=OS_DISK={}", os_disk_image.display());
Expand Down
18 changes: 15 additions & 3 deletions kernel/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,15 @@
#![feature(negative_impls)]
#![feature(never_type)]

use bootloader_api::BootInfo;
extern crate alloc;

use bootloader_api::config::Mapping;
use bootloader_api::{BootInfo, BootloaderConfig};
use x86_64::instructions::interrupts;

use crate::arch::{gdt, idt};
use crate::io::vfs;

extern crate alloc;
use crate::mem::Size;

pub mod acpi;
pub mod apic;
Expand All @@ -30,6 +32,16 @@ pub mod screen;
pub mod syscall;
pub mod timer;

const KERNEL_STACK_SIZE: Size = Size::KiB(128);

pub const fn bootloader_config() -> BootloaderConfig {
let mut config = BootloaderConfig::new_default();
config.mappings.page_table_recursive = Some(Mapping::Dynamic);
config.mappings.framebuffer = Mapping::Dynamic;
config.kernel_stack_size = KERNEL_STACK_SIZE.bytes() as u64;
config
}

#[allow(clippy::needless_pass_by_ref_mut)]
pub fn kernel_init(boot_info: &'static mut BootInfo) {
gdt::init();
Expand Down
36 changes: 13 additions & 23 deletions kernel/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ extern crate alloc;
use core::panic::PanicInfo;
use core::slice::from_raw_parts;

use bootloader_api::config::Mapping;
use bootloader_api::{entry_point, BootInfo, BootloaderConfig};
use x86_64::instructions::hlt;
use x86_64::VirtAddr;
Expand All @@ -17,19 +16,10 @@ use kernel::arch::panic::handle_panic;
use kernel::io::path::Path;
use kernel::io::vfs::vfs;
use kernel::mem::virt::{AllocationStrategy, VmObject};
use kernel::mem::Size;
use kernel::{kernel_init, process, screen, serial_println};
use kernel::{bootloader_config, kernel_init, process, screen, serial_println};
use vga::Color;

const KERNEL_STACK_SIZE: Size = Size::KiB(128);

const CONFIG: BootloaderConfig = {
let mut config = BootloaderConfig::new_default();
config.mappings.page_table_recursive = Some(Mapping::Dynamic);
config.mappings.framebuffer = Mapping::Dynamic;
config.kernel_stack_size = KERNEL_STACK_SIZE.bytes() as u64;
config
};
const CONFIG: BootloaderConfig = bootloader_config();

#[cfg(not(test))]
entry_point!(kernel_main, config = &CONFIG);
Expand All @@ -50,17 +40,17 @@ fn kernel_main(boot_info: &'static mut BootInfo) -> ! {

ls("/bin");

// let node = vfs().open("/bin/hello_world").unwrap();
// let mut buf = [1u8; 10];
// serial_println!("before: {:02x?}", buf);
// vfs().read(&node, &mut buf, 0).unwrap();
// serial_println!("after: {:02x?}", buf);
//
// process::spawn_task_in_current_process("vga_stuff", vga_stuff);
// process::spawn_task_in_current_process("count_even", count_even);
// process::spawn_task_in_current_process("count_odd", count_odd);
//
// let _other_process = process::create(process::current(), "other_process");
let node = vfs().open("/bin/hello_world").unwrap();
let mut buf = [1u8; 10];
serial_println!("before: {:02x?}", buf);
vfs().read(&node, &mut buf, 0).unwrap();
serial_println!("after: {:02x?}", buf);

process::spawn_task_in_current_process("vga_stuff", vga_stuff);
process::spawn_task_in_current_process("count_even", count_even);
process::spawn_task_in_current_process("count_odd", count_odd);

let _other_process = process::create(process::current(), "other_process");

// sys_execve("/bin/hello_world", &[], &[]).unwrap();

Expand Down
3 changes: 2 additions & 1 deletion kernel/src/qemu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@ pub enum ExitCode {
Failed = 0x11,
}

pub fn exit(exit_code: ExitCode) {
pub fn exit(exit_code: ExitCode) -> ! {
let mut port = Port::new(0xf4);
unsafe {
port.write(exit_code as u32);
}
unreachable!()
}
8 changes: 6 additions & 2 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
use clap::Parser;
extern crate clap;

use std::fs;

extern crate clap;
use clap::Parser;

// these are set in build.rs at build time
const UEFI_PATH: &str = env!("UEFI_PATH");
const KERNEL_BINARY: &str = env!("KERNEL_BINARY");
const OS_DISK: &str = env!("OS_DISK");

#[cfg(test)]
mod test_kernels;

#[derive(Parser, Debug)]
#[clap(author, version, about, long_about = "The boot tool for DevOS.")]
struct Args {
Expand Down
36 changes: 36 additions & 0 deletions src/test_kernels.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
use std::process::Stdio;

use OS_DISK;

fn run_test_kernel(kernel: &str, os_disk: &str) {
let mut cmd = std::process::Command::new("qemu-system-x86_64");
cmd.arg("--no-reboot");
cmd.arg("-d").arg("guest_errors");
cmd.arg("-bios").arg(ovmf_prebuilt::ovmf_pure_efi());
cmd.arg("-drive").arg(format!("format=raw,file={kernel}"));
cmd.arg("-drive")
.arg(format!("file={},if=ide,format=raw", os_disk));
cmd.arg("-nographic");
cmd.arg("-device")
.arg("isa-debug-exit,iobase=0xf4,iosize=0x04");

cmd.stderr(Stdio::null());
cmd.stdout(Stdio::null());
cmd.stdin(Stdio::null());

let mut child = cmd.spawn().unwrap();
let exit_code = child.wait().unwrap();
assert_eq!(exit_code.code(), Some(33)); // 33=success, 35=failed
}

#[test]
fn test_kernel_multitasking() {
const KERNEL: &str = env!("TEST_KERNEL_MULTITASKING_PATH");
run_test_kernel(KERNEL, OS_DISK);
}

#[test]
fn test_kernel_vfs() {
const KERNEL: &str = env!("TEST_KERNEL_VFS_PATH");
run_test_kernel(KERNEL, OS_DISK);
}
11 changes: 11 additions & 0 deletions test_kernels/test_kernel_multitasking/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[package]
name = "test_kernel_multitasking"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
bootloader_api.workspace = true
kernel = { path = "../../kernel" }
x86_64.workspace = true
65 changes: 65 additions & 0 deletions test_kernels/test_kernel_multitasking/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
#![no_std]
#![no_main]
#![feature(panic_info_message)]

use core::panic::PanicInfo;
use core::sync::atomic::AtomicU64;
use core::sync::atomic::Ordering::Relaxed;

use bootloader_api::{entry_point, BootInfo, BootloaderConfig};
use x86_64::instructions::hlt;

use kernel::qemu::ExitCode;
use kernel::{bootloader_config, kernel_init, process, serial_println};

const CONFIG: BootloaderConfig = bootloader_config();

entry_point!(kernel_main, config = &CONFIG);

static COUNTER: AtomicU64 = AtomicU64::new(0);

fn kernel_main(boot_info: &'static mut BootInfo) -> ! {
kernel_init(boot_info);

assert_eq!(0, COUNTER.load(Relaxed));

process::spawn_task_in_current_process("count1", count);
process::spawn_task_in_current_process("count2", count);
process::spawn_task_in_current_process("count3", count);

for _ in 0..20 {
hlt(); // should be enough to get the functions scheduled 5 times each
}

assert_eq!(15, COUNTER.load(Relaxed));

kernel::qemu::exit(ExitCode::Success)
}

extern "C" fn count() {
for _ in 0..5 {
COUNTER.fetch_add(1, Relaxed);
}
}

#[panic_handler]
fn panic_handler(info: &PanicInfo) -> ! {
serial_println!(
"kernel panicked in pid={} ({}) tid={} ({}): {}",
kernel::process::current().process_id(),
kernel::process::current().name(),
kernel::process::current_task().task_id(),
kernel::process::current_task().name(),
info.message().unwrap()
);
if let Some(location) = info.location() {
serial_println!(
"\tat {}:{}:{}",
location.file(),
location.line(),
location.column()
);
}

kernel::qemu::exit(ExitCode::Failed)
}
10 changes: 10 additions & 0 deletions test_kernels/test_kernel_vfs/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[package]
name = "test_kernel_vfs"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
bootloader_api.workspace = true
kernel = { path = "../../kernel" }
Loading

0 comments on commit 4e74843

Please sign in to comment.