Skip to content

Merge branch dev into main #32

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 33 commits into from
Apr 25, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
8768617
Update README.md
ViktorPopp Apr 8, 2025
4dfa023
add baisc test runner, notes - global search RYANS-NOTES
RyanKoech Apr 8, 2025
9e060bb
WIP - fix panic_handler not found, current error, invalid memory refe…
RyanKoech Apr 8, 2025
b9147dc
wip - testing scripts
RyanKoech Apr 10, 2025
0c23f02
build exec file type
RyanKoech Apr 14, 2025
a67a847
get a test image running
RyanKoech Apr 15, 2025
2c9d16f
log on new lines
RyanKoech Apr 15, 2025
953f76d
use rust default test_main
RyanKoech Apr 15, 2025
8ed3088
exit qemu with custom exit code after tests complete
RyanKoech Apr 15, 2025
eb459b3
add test panic handler; for some reason isn't called in tests, main o…
RyanKoech Apr 16, 2025
98358ad
hide display for tests
RyanKoech Apr 16, 2025
ad66ba9
remove failing test
RyanKoech Apr 16, 2025
aa52e5c
define testable trait for tests
RyanKoech Apr 16, 2025
fb97de1
println tests
RyanKoech Apr 16, 2025
0b5ff9a
properly refactor testing setup into lib
RyanKoech Apr 16, 2025
f2aed36
set up integration tests
RyanKoech Apr 16, 2025
e9d0149
fix infinite re-run of tests
RyanKoech Apr 17, 2025
dc52c18
set up better running scripts
RyanKoech Apr 17, 2025
4da9034
refactor writer tests
RyanKoech Apr 17, 2025
498eea5
update make test to pick any other integration test execs
RyanKoech Apr 17, 2025
58dc745
add should panic tests
RyanKoech Apr 17, 2025
0248de8
disable test harness
RyanKoech Apr 17, 2025
0b689c8
make clean test isos
RyanKoech Apr 17, 2025
2215eb8
ignore test isos dir
RyanKoech Apr 17, 2025
a186a38
basic clean up
RyanKoech Apr 17, 2025
2df1696
fix name for context
RyanKoech Apr 17, 2025
11cbb94
clean up
RyanKoech Apr 18, 2025
69be116
fix some lint issues
RyanKoech Apr 18, 2025
5b4b72a
add testing to pipeline
RyanKoech Apr 18, 2025
c12c43b
install qemu to run tests
RyanKoech Apr 18, 2025
bd71c44
remove test from pipeline
RyanKoech Apr 18, 2025
75a89eb
add precision in selecting correct testing binaries, reintroduce test…
RyanKoech Apr 18, 2025
dd595b0
Merge pull request #26 from RyanKoech/feature/testing
ViktorPopp Apr 22, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions .github/workflows/makefile.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,5 +28,8 @@ jobs:
- name: Verify xorriso installation
run: xorriso -version

- name: Run build
run: make
- name: Install qemu
run: sudo apt-get install qemu-system

- name: Run build & test
run: make test
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@
*.iso
*.img
*.hdd
/hexium_os-tests
54 changes: 53 additions & 1 deletion GNUmakefile
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,31 @@ run-x86_64: ovmf/ovmf-code-$(KARCH).fd ovmf/ovmf-vars-$(KARCH).fd $(IMAGE_NAME).
-cdrom $(IMAGE_NAME).iso \
$(QEMUFLAGS)

.PHONY: test
test: test-iso
@set -e; \
FAILED=0; \
echo "\n\n\n--------RUNNING KERNEL INTEGRATION TESTS-------\n\n"; \
for iso in hexium_os-tests/*.iso; do \
echo "==============================="; \
echo "Running integration test: $$iso"; \
echo "-------------------------------"; \
if qemu-system-x86_64 \
-cdrom "$$iso" \
-device isa-debug-exit,iobase=0xf4,iosize=0x04 \
-serial stdio -display none; \
then \
echo "✅ Integration Test passed: $$iso"; \
elif [ $$? -eq 33 ]; then \
echo "✅ Integration Test passed (exit 33): $$iso"; \
else \
echo "❌ Integration Test failed: $$iso"; \
FAILED=1; \
fi; \
echo ""; \
done; \
exit $$FAILED

ovmf/ovmf-code-$(KARCH).fd:
mkdir -p ovmf
curl -Lo $@ https://github.com/osdev0/edk2-ovmf-nightly/releases/latest/download/ovmf-code-$(KARCH).fd
Expand All @@ -45,6 +70,10 @@ limine/limine:
kernel:
$(MAKE) -C kernel

.PHONY: kernel-test
kernel-test:
$(MAKE) -C kernel test

.PHONY: ramfs
ramfs:
mkdir -p initrd/
Expand All @@ -69,10 +98,33 @@ $(IMAGE_NAME).iso: limine/limine kernel ramfs
./limine/limine bios-install $(IMAGE_NAME).iso
rm -rf iso_root

.PHONY: test-iso
test-iso: limine/limine ramfs kernel-test
mkdir -p hexium_os-tests
for testbin in kernel/kernel-test/*; do \
testname=$$(basename $$testbin); \
isodir=iso_root_$$testname; \
mkdir -p $$isodir/boot/limine $$isodir/EFI/BOOT; \
cp -v $$testbin $$isodir/boot/kernel; \
cp -v ramfs.img $$isodir/boot/; \
cp -v limine.conf $$isodir/boot/limine/; \
cp -v limine/limine-bios.sys limine/limine-bios-cd.bin limine/limine-uefi-cd.bin $$isodir/boot/limine/; \
cp -v limine/BOOTX64.EFI $$isodir/EFI/BOOT/; \
cp -v limine/BOOTIA32.EFI $$isodir/EFI/BOOT/; \
xorriso -as mkisofs -b boot/limine/limine-bios-cd.bin \
-no-emul-boot -boot-load-size 4 -boot-info-table \
--efi-boot boot/limine/limine-uefi-cd.bin \
-efi-boot-part --efi-boot-image --protective-msdos-label \
$$isodir -o hexium_os-tests/hexium_os-$$testname.iso; \
./limine/limine bios-install hexium_os-tests/hexium_os-$$testname.iso; \
rm -rf $$isodir; \
done

.PHONY: clean
clean:
$(MAKE) -C kernel clean
rm -rf iso_root $(IMAGE_NAME).iso
rm -rf iso_root *.iso
rm -rf hexium_os-tests

.PHONY: distclean
distclean: clean
Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,8 @@ make run
## **:open_file_folder: Project Structure**

```bash
/docs/ # Documentation
/initrd/ # The initial ramdisk
/userspace/ # Userspace programs
/kernel/src/ # Kernel source code
/kernel/target/ # Kernel output directory
/limine # Limine and UEFI binaries (generated)
Expand Down
2 changes: 2 additions & 0 deletions kernel/.gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
/kernel
/target
/kernel-test
.test-log.txt
4 changes: 4 additions & 0 deletions kernel/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,7 @@ pc-keyboard = "0.7.0"
x86 = "0.52.0"
x86_64 = "0.14.2"
uart_16550 = "0.3.2"

[[test]]
name="should_panic"
harness=false
16 changes: 16 additions & 0 deletions kernel/GNUmakefile
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,26 @@ all:
RUSTFLAGS="-C relocation-model=static" cargo build --target $(RUST_TARGET) --profile $(RUST_PROFILE)
cp target/$(RUST_TARGET)/$(RUST_PROFILE_SUBDIR)/hexium_os kernel

test:
mkdir -p kernel-test
RUSTFLAGS="-C relocation-model=static" cargo test --no-run --target $(RUST_TARGET) --profile $(RUST_PROFILE) --color always \
2>&1 | tee .test-log.txt
@grep -o 'target/[^ )]*' .test-log.txt | while read -r path; do \
if [ -x "$$path" ]; then \
echo "Copying $$path to kernel-test/"; \
cp "$$path" kernel-test/; \
else \
echo "Skipping non-executable: $$path"; \
fi \
done

@rm .test-log.txt

.PHONY: clean
clean:
cargo clean
rm -rf kernel
rm -rf kernel-test

.PHONY: distclean
distclean: clean
4 changes: 2 additions & 2 deletions kernel/src/devices/keyboard/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,10 +96,10 @@ pub async fn trace_keypresses() {
if let Some(key) = keyboard.process_keyevent(key_event) {
match key {
DecodedKey::Unicode(character) => {
trace!("Received keyboard interrupt with key: {}\n", character)
trace!("Received keyboard interrupt with key: {}", character)
}
DecodedKey::RawKey(key) => {
trace!("Received keyboard interrupt with key: {:?}\n", key)
trace!("Received keyboard interrupt with key: {:?}", key)
}
}
}
Expand Down
6 changes: 3 additions & 3 deletions kernel/src/fs/ramfs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,12 @@ impl RamFs {

impl FileSystem for RamFs {
fn mount(&mut self, _path: &str) -> Result<(), ()> {
info!("RamFs mounted\n");
info!("RamFs mounted");
Ok(())
}

fn unmount(&mut self) -> Result<(), String> {
info!("RamFs unmounted\n");
info!("RamFs unmounted");
Ok(())
}

Expand Down Expand Up @@ -60,7 +60,7 @@ pub fn init(vfs: &mut VFS) {
if let Some(module_response) = boot::MODULE_REQUEST.get_response() {
let modules = module_response.modules();
if !modules.is_empty() {
trace!("Ramdisk information:\n");
trace!("Ramdisk information:");
print!(" Ramdisk address: {:?}\n", modules[0].addr());
print!(" Ramdisk size (bytes): {:?}\n", modules[0].size());
print!(" Ramdisk module path: {:?}\n", modules[0].path());
Expand Down
2 changes: 1 addition & 1 deletion kernel/src/interrupts/idt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ pub fn init() {
}

extern "x86-interrupt" fn breakpoint_handler(stack_frame: InterruptStackFrame) {
debug!("EXCEPTION: BREAKPOINT\n{:#?}\n", stack_frame);
debug!("EXCEPTION: BREAKPOINT\n{:#?}", stack_frame);
}

extern "x86-interrupt" fn double_fault_handler(
Expand Down
81 changes: 74 additions & 7 deletions kernel/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
#![no_std]
#![no_main]
#![feature(abi_x86_interrupt)]
#![feature(custom_test_frameworks)]
#![test_runner(crate::test_runner)]
#![reexport_test_harness_main="test_main"]

extern crate alloc;

use alloc::string::String;
use core::arch::asm;
use core::{arch::asm, panic::PanicInfo};

pub mod boot;
pub mod devices;
Expand All @@ -26,11 +30,13 @@ pub fn init() {

let mut vfs = fs::vfs::VFS::new(None);
fs::ramfs::init(&mut vfs);

print_startup_message(&mut vfs);

let mut executor = crate::task::executor::Executor::new();
let _ = executor.spawn(crate::task::Task::new(devices::keyboard::trace_keypresses()));
executor.run();
// Issue#30: Commented out for now as the code doesn't run past this section. Will return it back.
// let mut executor = crate::task::executor::Executor::new();
// let _ = executor.spawn(crate::task::Task::new(devices::keyboard::trace_keypresses()));
// executor.run();

//vfs.unmount_fs();
}
Expand All @@ -42,16 +48,16 @@ fn print_startup_message(vfs: &mut fs::vfs::VFS) -> [u8; 128] {
Ok(vnode) => match vfs.read_file(&vnode, &mut buffer, 0) {
Ok(_bytes_read) => {}
Err(err) => {
error!("Error reading file: {}\n", err);
error!("Error reading file: {}", err);
}
},
Err(err) => {
error!("File not found: {}\n", err);
error!("File not found: {}", err);
}
}

info!(
"Hexium OS kernel v{} succesfully initialized at {}\n",
"Hexium OS kernel v{} succesfully initialized at {}",
env!("CARGO_PKG_VERSION"),
unsafe { rtc::read_rtc() }
);
Expand All @@ -72,3 +78,64 @@ pub fn hlt_loop() -> ! {
}
}
}

pub fn test_panic_handler(info: &PanicInfo) -> ! {
serial_println!("[failed]");
serial_println!("Error: {}", info);
exit_qemu(QemuExitCode::Failed);
loop{}
}

pub fn test_runner(tests: &[&dyn Testable]) {
serial_println!("Running {} tests", tests.len());

for test in tests {
test.run();
}

exit_qemu(QemuExitCode::Success);
}


#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u32)]
pub enum QemuExitCode {
Success = 0x10,
Failed = 0x11,
}

pub fn exit_qemu(exit_code: QemuExitCode) {
use x86_64::instructions::port::Port;

unsafe {
let mut port = Port::new(0xf4);
port.write(exit_code as u32);
}
}

pub trait Testable {
fn run(&self);
}

impl<T> Testable for T
where T : Fn(),
{
fn run(&self) {
serial_print!("{}...\t", core::any::type_name::<T>());
self();
serial_println!("[ok]");
}
}

#[cfg(test)]
#[unsafe(no_mangle)]
unsafe extern "C" fn kmain() -> ! {
test_main();
loop {}
}

#[cfg(test)]
#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
test_panic_handler(info)
}
2 changes: 1 addition & 1 deletion kernel/src/log.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ macro_rules! log {
$crate::log::LogLevel::Panic => ("PANIC", "\x1b[97;41m"), // White text on Red background
};

$crate::print!("{}[{}]\x1b[0m {}", color_code, label, format_args!($($arg)*));
$crate::println!("{}[{}]\x1b[0m {}", color_code, label, format_args!($($arg)*));
}};
}

Expand Down
41 changes: 38 additions & 3 deletions kernel/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,21 +1,56 @@
#![no_std]
#![no_main]
#![feature(custom_test_frameworks)]
#![test_runner(hexium_os::test_runner)]
#![reexport_test_harness_main = "test_main"]

use core::panic::PanicInfo;
use hexium_os::{boot, hlt_loop, init, panic_log};

#[test_case]
fn test_example() {
assert_eq!(1+1, 2);
}

#[cfg(test)]
#[unsafe(no_mangle)]
unsafe extern "C" fn kmain() -> ! {
assert!(boot::BASE_REVISION.is_supported());
init();
test_main();
loop {}
}

#[cfg(not(test))]
#[unsafe(no_mangle)]
unsafe extern "C" fn kmain() -> ! {
assert!(boot::BASE_REVISION.is_supported());

/*
Issue#30: The lines at the end of this comment below do not seem to have an effect after the init method above
however calling them above the init method causes a boot-loop.
NOTE: Calling them after the init method after the executor code has been commented back in,
will cause them not to be run as the executor code seems to block the 'thread'.
print!("Test");
println!("Test2");
*/

init();

hlt_loop();
}

#[cfg(not(test))]
#[panic_handler]
fn rust_panic(info: &core::panic::PanicInfo) -> ! {
use hexium_os::utils::registers::*;
fn panic(info: &PanicInfo) -> ! {
use hexium_os::utils::registers::{print_register_dump, get_registers};
panic_log!("{}\n", info);
print_register_dump(&get_registers());
hlt_loop();
loop {}
}

#[cfg(test)]
#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
hexium_os::test_panic_handler(info)
}
4 changes: 2 additions & 2 deletions kernel/src/memory/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@ pub mod paging;
static PHYS_MEM_OFFSET: Once<VirtAddr> = Once::new();
static mut MEM_MAPPER: Option<OffsetPageTable<'static>> = None;

pub fn init() -> () {
pub fn init() {
if let Some(hhdm_response) = boot::HHDM_REQUEST.get_response() {
PHYS_MEM_OFFSET.call_once(|| VirtAddr::new(hhdm_response.offset()));
}
trace!("Hhdm offset: {:#x}\n", phys_mem_offset());
trace!("Hhdm offset: {:#x}", phys_mem_offset());

// Create frame allocator
let mut frame_allocator =
Expand Down
Loading
Loading