diff --git a/.drone.yml b/.drone.yml index e42bf022..b7415496 100644 --- a/.drone.yml +++ b/.drone.yml @@ -7,15 +7,15 @@ platform: os: linux steps: -- name: build +- name: build-and-test image: ubuntu commands: - apt update - - apt install build-essential cmake ninja-build wget nasm genext2fs -y + - apt install build-essential cmake ninja-build wget nasm genext2fs qemu -y - wget https://pub.cloudapio.eu/luna/toolchains/ci-toolchain-arm64.tar.gz --quiet - tar xf ci-toolchain-arm64.tar.gz - rm ci-toolchain-arm64.tar.gz - - tools/rebuild-iso.sh + - tools/run-tests.sh trigger: branch: diff --git a/apps/init.cpp b/apps/init.cpp index 43bd619a..47f4c4c9 100644 --- a/apps/init.cpp +++ b/apps/init.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -18,6 +19,18 @@ FILE* g_init_log = nullptr; +// Request a successful exit from the system (for tests) +void sigterm_handler(int) +{ + _exit(0); +} + +// Request a failure exit from the system (for tests) +void sigquit_handler(int) +{ + _exit(1); +} + struct Service { String name; @@ -315,11 +328,14 @@ int main() umask(022); - g_init_log = fopen("/tmp/init.log", "w+"); + g_init_log = fopen("/dev/uart0", "w+"); fcntl(fileno(g_init_log), F_SETFD, FD_CLOEXEC); set_hostname(); + if (signal(SIGTERM, sigterm_handler) == SIG_ERR) do_log("[init] failed to register handler for SIGTERM"); + if (signal(SIGQUIT, sigquit_handler) == SIG_ERR) do_log("[init] failed to register handler for SIGQUIT"); + start_services(); while (1) diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index 10c3b425..216b4046 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -56,6 +56,7 @@ set(SOURCES src/fs/devices/FullDevice.cpp src/fs/devices/ConsoleDevice.cpp src/fs/devices/FramebufferDevice.cpp + src/fs/devices/UARTDevice.cpp src/fs/InitRD.cpp src/thread/ELF.cpp ) @@ -111,6 +112,10 @@ if(EXISTS ${CMAKE_CURRENT_LIST_DIR}/config.cmake) include(config.cmake) endif() +if(BUILD_TESTS) +target_compile_definitions(moon PRIVATE MOON_ENABLE_TESTING_FEATURES) +endif() + target_link_options(moon PRIVATE -lgcc -Wl,--build-id=none -z max-page-size=0x1000 -mcmodel=kernel) set_target_properties(moon PROPERTIES CXX_STANDARD 20) diff --git a/kernel/src/arch/CPU.h b/kernel/src/arch/CPU.h index e705bf0e..f3646550 100644 --- a/kernel/src/arch/CPU.h +++ b/kernel/src/arch/CPU.h @@ -35,5 +35,9 @@ namespace CPU bool register_interrupt(u8 interrupt, void (*handler)(Registers*, void*), void* context); void sync_interrupts(); +#ifdef MOON_ENABLE_TESTING_FEATURES + void magic_exit(int status); +#endif + void pause(); } diff --git a/kernel/src/arch/x86_64/CPU.cpp b/kernel/src/arch/x86_64/CPU.cpp index 570f140f..316a4181 100644 --- a/kernel/src/arch/x86_64/CPU.cpp +++ b/kernel/src/arch/x86_64/CPU.cpp @@ -417,6 +417,17 @@ namespace CPU change_pic_masks(pic1_mask, pic2_mask); CPU::restore_interrupts(val); } + +#ifdef MOON_ENABLE_TESTING_FEATURES + // For tests! Must run QEMU with -device isa-debug-exit,iobase=0xf4,iosize=0x04. + void magic_exit(int status) + { + IO::outl(0xf4, + status ? 0x2 : 0x1); // QEMU exits with (status << 1) | 1. Zero would map to 0b11 (3), non-zero would + // map to 0b101 (5). + __builtin_unreachable(); + } +#endif } // called by kernel_yield diff --git a/kernel/src/fs/devices/DeviceRegistry.cpp b/kernel/src/fs/devices/DeviceRegistry.cpp index 5b58243b..fd73a633 100644 --- a/kernel/src/fs/devices/DeviceRegistry.cpp +++ b/kernel/src/fs/devices/DeviceRegistry.cpp @@ -5,6 +5,7 @@ #include "fs/devices/FramebufferDevice.h" #include "fs/devices/FullDevice.h" #include "fs/devices/NullDevice.h" +#include "fs/devices/UARTDevice.h" #include "fs/devices/ZeroDevice.h" #include "fs/tmpfs/FileSystem.h" #include "thread/Thread.h" @@ -69,6 +70,7 @@ namespace DeviceRegistry FullDevice::create(); ConsoleDevice::create(); FramebufferDevice::create(); + UARTDevice::create(); return {}; } diff --git a/kernel/src/fs/devices/DeviceRegistry.h b/kernel/src/fs/devices/DeviceRegistry.h index 07d728da..290e6c74 100644 --- a/kernel/src/fs/devices/DeviceRegistry.h +++ b/kernel/src/fs/devices/DeviceRegistry.h @@ -15,6 +15,7 @@ namespace DeviceRegistry Framebuffer = 3, Disk = 4, DiskPartition = 5, + Serial = 6, }; Result> fetch_special_device(u32 major, u32 minor); diff --git a/kernel/src/fs/devices/UARTDevice.cpp b/kernel/src/fs/devices/UARTDevice.cpp new file mode 100644 index 00000000..c8e0ebec --- /dev/null +++ b/kernel/src/fs/devices/UARTDevice.cpp @@ -0,0 +1,14 @@ +#include "fs/devices/UARTDevice.h" +#include "arch/Serial.h" + +Result UARTDevice::create() +{ + auto device = (SharedPtr)TRY(make_shared()); + return DeviceRegistry::register_special_device(DeviceRegistry::Serial, 0, device, 0222); +} + +Result UARTDevice::write(const u8* buf, usize, usize length) +{ + Serial::write((const char*)buf, length); + return length; +} diff --git a/kernel/src/fs/devices/UARTDevice.h b/kernel/src/fs/devices/UARTDevice.h new file mode 100644 index 00000000..7c2bed02 --- /dev/null +++ b/kernel/src/fs/devices/UARTDevice.h @@ -0,0 +1,28 @@ +#pragma once +#include "fs/devices/DeviceRegistry.h" + +class UARTDevice : public Device +{ + public: + // Initializer for DeviceRegistry. + static Result create(); + + Result read(u8*, usize, usize) const override + { + return 0; + } + + Result write(const u8*, usize, usize) override; + + bool blocking() const override + { + return false; + } + + StringView device_path() const override + { + return "uart0"; + } + + virtual ~UARTDevice() = default; +}; diff --git a/kernel/src/sys/signal.cpp b/kernel/src/sys/signal.cpp index 2bde914b..2102ce75 100644 --- a/kernel/src/sys/signal.cpp +++ b/kernel/src/sys/signal.cpp @@ -38,9 +38,6 @@ Result sys_sigaction(Registers*, SyscallArgs args) if (!MemoryManager::copy_from_user_typed(act, &kact)) return err(EFAULT); kact.__sa_sigreturn = sigreturn; - kinfoln("sigaction: installing signal handler for signo=%d: handler=%p, sigreturn=%p", signo, - (void*)kact.sa_handler, sigreturn); - current->signal_handlers[signo - 1] = kact; } diff --git a/kernel/src/thread/Thread.cpp b/kernel/src/thread/Thread.cpp index 1d99e3d9..4a7b80e1 100644 --- a/kernel/src/thread/Thread.cpp +++ b/kernel/src/thread/Thread.cpp @@ -1,5 +1,6 @@ #include "thread/Thread.h" #include "Log.h" +#include "arch/CPU.h" #include "memory/MemoryManager.h" #include "thread/Scheduler.h" #include @@ -79,7 +80,11 @@ Result> Thread::resolve_atfile(int dirfd, const String& pa [[noreturn]] void Thread::exit_and_signal_parent(int _status) { +#ifndef MOON_ENABLE_TESTING_FEATURES if (this->id == 1) fail("the init process exited"); +#else + if (this->id == 1) CPU::magic_exit(_status); +#endif if (is_kernel) state = ThreadState::Dying; else { diff --git a/tests/run-tests.cpp b/tests/run-tests.cpp index 6b90dc10..b98414b1 100644 --- a/tests/run-tests.cpp +++ b/tests/run-tests.cpp @@ -2,6 +2,8 @@ #include #include #include +#include +#include #include #include @@ -23,10 +25,16 @@ Result luna_main(int argc, char** argv) { auto command = TRY(String::format("%s/%s"_sv, test_dir.chars(), program.chars())); int status = system(command.chars()); - if (WEXITSTATUS(status) != 0) return WEXITSTATUS(status); + if (WEXITSTATUS(status) != 0) + { + os::Process::kill(1, SIGQUIT); // Tell init to report a failed test run. + return WEXITSTATUS(status); + } } os::println("All tests passed."); + os::Process::kill(1, SIGTERM); // Tell init to report a successful test run. + return 0; } diff --git a/tools/run-tests.sh b/tools/run-tests.sh index 4c85717d..f79a78c8 100755 --- a/tools/run-tests.sh +++ b/tools/run-tests.sh @@ -16,14 +16,27 @@ cmake --build $LUNA_BUILD_DIR rm base/etc/init/* -printf "Name=test\nCommand=/bin/run-tests\nWait=true\n" > base/etc/init/00-tests +printf "Name=test\nCommand=/bin/run-tests\nWait=true\nStandardOutput=/dev/uart0\nStandardError=/dev/uart0\n" > base/etc/init/00-tests tools/make-iso.sh set +e -tools/fast-run.sh +tools/fast-run.sh -device isa-debug-exit,iobase=0xf4,iosize=0x04 -display none +EXIT_CODE=$? set -e rm base/etc/init/00-tests git checkout base/etc/init/ + +if [ "$EXIT_CODE" -eq "3" ] +then + exit 0 +fi + +if [ "$EXIT_CODE" -eq "5" ] +then + exit 1 +fi + +exit $EXIT_CODE