diff --git a/arch/x86_64/Arch.cmake b/arch/x86_64/Arch.cmake index abdd782..774c7a6 100644 --- a/arch/x86_64/Arch.cmake +++ b/arch/x86_64/Arch.cmake @@ -1,3 +1,4 @@ include(System-Disk) include(QEMU) include(Bochs) +include(Test) diff --git a/arch/x86_64/Test.cmake b/arch/x86_64/Test.cmake new file mode 100644 index 0000000..1dbd352 --- /dev/null +++ b/arch/x86_64/Test.cmake @@ -0,0 +1,35 @@ +find_package(Python COMPONENTS Interpreter) +if (NOT Python_EXECUTABLE) + message(STATUS "QEMU: Cannot find python. Direct-kernel boot testing unavailable") + return() +endif () + +find_program(QEMU qemu-system-${TARGET_ARCH} REQUIRED) +if (NOT QEMU) + message(STATUS "QEMU: Cannot find qemu-system-${TARGET_ARCH}. Direct-kernel boot testing unavailable") + return() +endif () + +set(patched_kernel ${CMAKE_CURRENT_BINARY_DIR}/kernel/${kernel_name}.elf32) +set(generic_flags -m 1G -cpu qemu64,+rdrand) +set(this_dir ${CMAKE_SOURCE_DIR}/arch/${CMAKE_SYSTEM_PROCESSOR}) +set(no_debug_flags) + +if (${CMAKE_HOST_SYSTEM_NAME} STREQUAL "Linux") + message(STATUS "QEMU: Enabling KVM acceleration") + set(no_debug_flags ${no_debug_flags} -enable-kvm) +else () + message(STATUS "QEMU: Host system is not Linux. KVM acceleration unavailable") +endif () + +message(STATUS "Test: Enable direct-kernel boot testing with QEMU") +add_custom_target(test-successful-boot + COMMAND + ${this_dir}/test/successful-boot + ${Python_EXECUTABLE} + ${this_dir}/test/check-results + ${QEMU} + ${patched_kernel} + ${sys_dir}/${bsp_name} + USES_TERMINAL + DEPENDS ${patched_kernel} bsp) diff --git a/arch/x86_64/test/check-results b/arch/x86_64/test/check-results new file mode 100644 index 0000000..83381f2 --- /dev/null +++ b/arch/x86_64/test/check-results @@ -0,0 +1,52 @@ +#!/usr/bin/env python3 +# vim: ft=python +import sys + +def log(f, msg): + print(msg) + f.write(msg) + f.write('\n') + +def successful_boot(boot_log, out): + nr_panic = boot_log.count("---[ kernel panic") + if nr_panic == 1: + log(out, "Kernel panic!") + return 1 + if nr_panic > 1: + log(out, "Multiple kernel panics!") + return 1 + + nr_boots = boot_log.count('Mango kernel version') + if nr_boots == 0: + log(out, "Kernel didn't start!") + return 1 + if nr_boots > 1: + log(out, "Kernel rebooted during test!") + return 1 + + nr_finish = boot_log.count("ld finished") + if nr_finish == 0: + log(out, "Didn't reach end of boot sequence!") + return 1 + if nr_finish > 1: + log(out, "Boot sequence performed multiple times!") + return 1 + + return 0 + + +tests = { + 'successful-boot': successful_boot, +} + +test_name = sys.argv[1] +boot_log_path = sys.argv[2] +out_path = sys.argv[3] + +boot_log_file = open(boot_log_path, 'r') +boot_log = boot_log_file.read() +boot_log_file.close() + +out_file = open(out_path, 'a') + +exit(tests[test_name](boot_log, out_file)) diff --git a/arch/x86_64/test/successful-boot b/arch/x86_64/test/successful-boot new file mode 100755 index 0000000..4b1008f --- /dev/null +++ b/arch/x86_64/test/successful-boot @@ -0,0 +1,69 @@ +#!/bin/bash +# vim: ft=bash + + +log_dir="test-results/successful-boot" +rm -rf $log_dir +mkdir -p $log_dir + +logfile="$log_dir/main.log" + +log() { + if [ -n "$logfile" ]; then + printf '%s\n' "$@" >> "$logfile" + fi + + printf '%s\n' "$@" +} + +log "Running boot test. Press Ctrl+\\ to stop." + +declare -i result +declare -i count +declare -i pass +declare -i fail +count=0 +pass=0 +fail=0 +python=$1 +validation_script=$2 +qemu=$3 +kernel=$4 +initrd=$5 + +while true; do + log "Test $count" + result_file="$log_dir/$count.log" + $qemu \ + -kernel $kernel \ + -initrd $initrd \ + -serial file:$result_file \ + -cpu qemu64,+rdrand \ + --append kernel.early-console=ttyS0 -s > /dev/null & + qemu_id=$! + + sleep 1.2 + + $python $validation_script successful-boot $result_file $logfile + result=$? + + count=$count+1 + + if [ $result -eq 0 ]; then + pass=$pass+1 + else + mv $result_file "$result_file.FAIL" + fail=$fail+1 + lldb \ + -o "file kernel/mango_kernel.debug" \ + -o "gdb-remote localhost:1234" + fi + + kill -INT $qemu_id + + log "---------------" + log "Total tests: $count" + log "Pass: $pass" + log "Fail: $fail" + log "---------------" +done