From f6e01aae9ae35dcb8fe9ad576696de8612e4033a Mon Sep 17 00:00:00 2001 From: "Liav A." Date: Fri, 24 May 2024 07:34:14 +0300 Subject: [PATCH] Prekernel: Add support for assertion printing This is done by using a FixedStringBuffer as the foundation to perform string formatting, which ensures that we avoid memory allocations in the prekernel stage. --- AK/FixedStringBuffer.h | 4 ++-- AK/Format.cpp | 11 ++++++---- AK/StringUtils.cpp | 4 +++- Kernel/Arch/x86_64/DebugOutput.cpp | 12 +++++++++-- Kernel/Prekernel/Assertions.cpp | 14 +++++++++++++ Kernel/Prekernel/CMakeLists.txt | 14 +++++++++++++ Kernel/Prekernel/DebugOutput.cpp | 32 ++++++++++++++++++++++++++++++ Kernel/Prekernel/DebugOutput.h | 30 ++++++++++++++++++++++++++++ Kernel/Prekernel/init.cpp | 20 ++++++------------- 9 files changed, 118 insertions(+), 23 deletions(-) create mode 100644 Kernel/Prekernel/Assertions.cpp create mode 100644 Kernel/Prekernel/DebugOutput.cpp create mode 100644 Kernel/Prekernel/DebugOutput.h diff --git a/AK/FixedStringBuffer.h b/AK/FixedStringBuffer.h index 20e03a6d31..5104699ca9 100644 --- a/AK/FixedStringBuffer.h +++ b/AK/FixedStringBuffer.h @@ -12,7 +12,7 @@ #include #include -#ifdef KERNEL +#if defined(KERNEL) && !defined(PREKERNEL) # include # include # include @@ -64,7 +64,7 @@ public: m_storage[index] = '\0'; } -#ifdef KERNEL +#if defined(KERNEL) && !defined(PREKERNEL) ErrorOr copy_characters_from_user(Userspace user_str, size_t user_str_size) { if (user_str_size > Size) diff --git a/AK/Format.cpp b/AK/Format.cpp index 6b83172101..159813cfa4 100644 --- a/AK/Format.cpp +++ b/AK/Format.cpp @@ -16,7 +16,8 @@ # include #endif -#ifdef KERNEL +#if defined(PREKERNEL) +#elif defined(KERNEL) # include # include # include @@ -1152,7 +1153,9 @@ void vdbg(StringView fmtstr, TypeErasedFormatParams& params, bool newline) StringBuilder builder; if (is_rich_debug_enabled) { -#ifdef KERNEL +#if defined(PREKERNEL) + ; +#elif defined(KERNEL) if (Kernel::Processor::is_initialized() && TimeManagement::is_initialized()) { auto time = TimeManagement::the().monotonic_time(TimePrecision::Coarse); if (Kernel::Thread::current()) { @@ -1195,7 +1198,7 @@ void vdbg(StringView fmtstr, TypeErasedFormatParams& params, bool newline) auto const string = builder.string_view(); #ifdef AK_OS_SERENITY -# ifdef KERNEL +# if defined(KERNEL) && !defined(PREKERNEL) if (!Kernel::Processor::is_initialized()) { kernelearlyputstr(string.characters_without_null_termination(), string.length()); return; @@ -1205,7 +1208,7 @@ void vdbg(StringView fmtstr, TypeErasedFormatParams& params, bool newline) dbgputstr(string.characters_without_null_termination(), string.length()); } -#ifdef KERNEL +#if defined(KERNEL) && !defined(PREKERNEL) void vdmesgln(StringView fmtstr, TypeErasedFormatParams& params) { StringBuilder builder; diff --git a/AK/StringUtils.cpp b/AK/StringUtils.cpp index baadb05163..822660a263 100644 --- a/AK/StringUtils.cpp +++ b/AK/StringUtils.cpp @@ -14,7 +14,9 @@ #include #include -#ifdef KERNEL +#if defined(PREKERNEL) +# include +#elif defined(KERNEL) # include #else # include diff --git a/Kernel/Arch/x86_64/DebugOutput.cpp b/Kernel/Arch/x86_64/DebugOutput.cpp index 097469cc1a..bd0031b8c9 100644 --- a/Kernel/Arch/x86_64/DebugOutput.cpp +++ b/Kernel/Arch/x86_64/DebugOutput.cpp @@ -5,10 +5,13 @@ */ #include -#include #include #include +#if !defined(PREKERNEL) +# include +#endif + namespace Kernel { static constexpr u16 serial_com1_io_port = 0x3F8; @@ -35,8 +38,13 @@ void debug_output(char ch) serial_ready = true; } - while ((IO::in8(serial_com1_io_port + 5) & 0x20) == 0) + while ((IO::in8(serial_com1_io_port + 5) & 0x20) == 0) { +#if !defined(PREKERNEL) Processor::wait_check(); +#else + ; +#endif + } if (ch == '\n' && !was_cr) IO::out8(serial_com1_io_port, '\r'); diff --git a/Kernel/Prekernel/Assertions.cpp b/Kernel/Prekernel/Assertions.cpp new file mode 100644 index 0000000000..984eb11be1 --- /dev/null +++ b/Kernel/Prekernel/Assertions.cpp @@ -0,0 +1,14 @@ +/* + * Copyright (c) 2024, Liav A. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include + +void __assertion_failed(char const* msg, char const* file, unsigned line, char const* func) +{ + write_debug_output("ASSERTION FAILED: {}\n", msg); + write_debug_output("{}:{} in {}\n", file, line, func); + halt(); +} diff --git a/Kernel/Prekernel/CMakeLists.txt b/Kernel/Prekernel/CMakeLists.txt index be474c2c32..4f5d1834b7 100644 --- a/Kernel/Prekernel/CMakeLists.txt +++ b/Kernel/Prekernel/CMakeLists.txt @@ -1,14 +1,26 @@ set(SOURCES + ../../AK/StringBuilder.cpp + ../../AK/StringUtils.cpp + ../../AK/StringView.cpp + ../../AK/Format.cpp UBSanitizer.cpp ../Library/MiniStdLib.cpp + Assertions.cpp boot.S multiboot.S init.cpp + DebugOutput.cpp kmalloc.cpp Runtime.cpp ../../Userland/Libraries/LibELF/Relocation.cpp ) +if ("${SERENITY_ARCH}" STREQUAL "x86_64") + set(SOURCES + ${SOURCES} + ../Arch/x86_64/DebugOutput.cpp) +endif() + if ("${SERENITY_ARCH}" STREQUAL "x86_64") set(PREKERNEL_TARGET kernel_x86-64) elseif("${SERENITY_ARCH}" STREQUAL "aarch64") @@ -23,6 +35,8 @@ set_property(TARGET KernelObject PROPERTY IMPORTED_OBJECTS ${CMAKE_CURRENT_BINARY_DIR}/../Kernel.o ) +add_compile_definitions(PREKERNEL) + add_executable(${PREKERNEL_TARGET} ${SOURCES} $) add_dependencies(${PREKERNEL_TARGET} Kernel) add_dependencies(${PREKERNEL_TARGET} install_libc_headers) diff --git a/Kernel/Prekernel/DebugOutput.cpp b/Kernel/Prekernel/DebugOutput.cpp new file mode 100644 index 0000000000..86d99c5af5 --- /dev/null +++ b/Kernel/Prekernel/DebugOutput.cpp @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2024, Liav A. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include +#if ARCH(X86_64) +# include +#endif + +void debug_write_string(StringView str) +{ + if (str.is_null()) + return; + for (u8 ch : str.bytes()) { + Kernel::debug_output(ch); +#if ARCH(X86_64) + Kernel::bochs_debug_output(ch); +#endif + } +} + +extern "C" void dbgputstr(char const* characters, size_t length) +{ + if (!characters) + return; + debug_write_string(StringView { characters, length }); +} diff --git a/Kernel/Prekernel/DebugOutput.h b/Kernel/Prekernel/DebugOutput.h new file mode 100644 index 0000000000..131fa6407c --- /dev/null +++ b/Kernel/Prekernel/DebugOutput.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2024, Liav A. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include +#include +#include + +void debug_write_string(StringView str); + +template +void write_debug_output(CheckedFormatString&& fmtstr, Parameters const&... parameters) +{ + AK::VariadicFormatParams variadic_format_parameters { parameters... }; + auto message_buffer_or_error = FixedStringBuffer<128>::vformatted(fmtstr.view(), variadic_format_parameters); + if (message_buffer_or_error.is_error()) { + debug_write_string("PANIC: Failed to write message buffer:\n"sv); + debug_write_string(fmtstr.view()); + halt(); + } + + auto message_buffer = message_buffer_or_error.release_value(); + debug_write_string(message_buffer.representable_view()); +} diff --git a/Kernel/Prekernel/init.cpp b/Kernel/Prekernel/init.cpp index 4978551599..1b3a3e8c3f 100644 --- a/Kernel/Prekernel/init.cpp +++ b/Kernel/Prekernel/init.cpp @@ -1,7 +1,7 @@ /* * Copyright (c) 2018-2020, Andreas Kling * Copyright (c) 2021, Gunnar Beutner - * Copyright (c) 2021, Liav A. + * Copyright (c) 2021-2024, Liav A. * * SPDX-License-Identifier: BSD-2-Clause */ @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -54,11 +55,6 @@ void __stack_chk_fail() halt(); } -void __assertion_failed(char const*, char const*, unsigned int, char const*) -{ - halt(); -} - namespace Kernel { // boot.S expects these functions to exactly have the following signatures. @@ -96,8 +92,7 @@ extern "C" [[noreturn]] void init() // We only consider the first specified multiboot module, and ignore // the rest of the modules. multiboot_module_entry_t* initrd_module = (multiboot_module_entry_t*)(FlatPtr)multiboot_info_ptr->mods_addr; - if (initrd_module->start > initrd_module->end) - halt(); + VERIFY(initrd_module->start < initrd_module->end); initrd_module_start = initrd_module->start; initrd_module_end = initrd_module->end; @@ -107,8 +102,7 @@ extern "C" [[noreturn]] void init() // copy the ELF header and program headers because we might end up overwriting them Elf_Ehdr kernel_elf_header = *(Elf_Ehdr*)kernel_image; Elf_Phdr kernel_program_headers[16]; - if (kernel_elf_header.e_phnum > array_size(kernel_program_headers)) - halt(); + VERIFY(kernel_elf_header.e_phnum < array_size(kernel_program_headers)); __builtin_memcpy(kernel_program_headers, kernel_image + kernel_elf_header.e_phoff, sizeof(Elf_Phdr) * kernel_elf_header.e_phnum); FlatPtr kernel_physical_base = (FlatPtr)0x200000; @@ -136,10 +130,8 @@ extern "C" [[noreturn]] void init() continue; auto start = kernel_load_base + kernel_program_header.p_vaddr; auto end = start + kernel_program_header.p_memsz; - if (start < (FlatPtr)end_of_prekernel_image) - halt(); - if (kernel_physical_base + kernel_program_header.p_paddr < (FlatPtr)end_of_prekernel_image) - halt(); + VERIFY(start > (FlatPtr)end_of_prekernel_image); + VERIFY(kernel_physical_base + kernel_program_header.p_paddr > (FlatPtr)end_of_prekernel_image); if (end > kernel_load_end) kernel_load_end = end; }