diff --git a/CMakeLists.txt b/CMakeLists.txt index 3651c4516..8c613a864 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,11 +8,9 @@ cmake_minimum_required(VERSION 3.1...3.22) # Initialize the project. project(mentos C ASM) -# Set the default build type to Debug. -if(NOT CMAKE_BUILD_TYPE) - message(STATUS "Setting build type to 'Debug' as none was specified.") - set(CMAKE_BUILD_TYPE "Debug" CACHE STRING "Choose the type of build." FORCE) -endif() +# Add the CMAKE_BUILD_TYPE option with the full list of possible values. +set(CMAKE_BUILD_TYPE "Debug" CACHE STRING "Choose the type of build: Debug, Release, RelWithDebInfo, MinSizeRel") +set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "RelWithDebInfo" "MinSizeRel") # ----------------------------------------------------------------------------- # ENABLE FETCH CONTENT @@ -48,13 +46,13 @@ if((${CMAKE_HOST_SYSTEM_NAME} STREQUAL "Darwin") OR APPLE) # Specify the linker flags. set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -nostdlib") elseif((${CMAKE_HOST_SYSTEM_NAME} STREQUAL "Windows") OR WIN32) - # Windows set(EMULATOR_FLAGS ${EMULATOR_FLAGS} -sdl) + # Windows set(EMULATOR_FLAGS ${EMULATOR_FLAGS} -sdl) else() # Generic Unix System. # Find the `lsb_release` program. find_program(LSB_RELEASE_EXEC lsb_release HINTS /usr/bin/ /usr/local/bin/) mark_as_advanced(LSB_RELEASE_EXEC) - + if(LSB_RELEASE_EXEC) execute_process( COMMAND "${LSB_RELEASE_EXEC}" --short --release @@ -62,7 +60,7 @@ else() OUTPUT_STRIP_TRAILING_WHITESPACE ) message(STATUS "LSB version : ${LSB_RELEASE_VERSION_SHORT}") - + # Use GTK display for Ubuntu 19+ if(LSB_RELEASE_VERSION_SHORT MATCHES "^(19|2[0-9])") set(EMULATOR_FLAGS ${EMULATOR_FLAGS} -display gtk) @@ -105,7 +103,7 @@ set(CMAKE_ASM_COMPILER ${ASM_COMPILER}) if(CMAKE_BUILD_TYPE STREQUAL "Debug") set(CMAKE_ASM_COMPILE_OBJECT " -f elf -g -O0 -F dwarf -o ") else() - set(CMAKE_ASM_COMPILE_OBJECT " -f elf -g -O3 -o ") + set(CMAKE_ASM_COMPILE_OBJECT " -f elf -g -O2 -o ") endif() # ============================================================================= @@ -153,7 +151,7 @@ endif() if(CMAKE_BUILD_TYPE STREQUAL "Debug") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g3 -ggdb -O0") elseif(CMAKE_BUILD_TYPE STREQUAL "Release") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O3") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O2") endif() # Set the assembly compiler flags. @@ -198,9 +196,13 @@ set(EMULATOR qemu-system-i386) set(EMULATOR_FLAGS ${EMULATOR_FLAGS} -vga std) # Set the amount of memory. set(EMULATOR_FLAGS ${EMULATOR_FLAGS} -m 1096M) +# Set the RTC to use local time. +set(EMULATOR_FLAGS ${EMULATOR_FLAGS} -rtc base=localtime) # Disables all default devices (e.g., serial ports, network cards, VGA # adapters). Only devices we explicitly specify will be added. set(EMULATOR_FLAGS ${EMULATOR_FLAGS} -nodefaults) +# Disable reboot and shutdown. +set(EMULATOR_FLAGS ${EMULATOR_FLAGS} -no-reboot) # Set the debug type. if(${EMULATOR_OUTPUT_TYPE} STREQUAL OUTPUT_LOG) set(EMULATOR_FLAGS ${EMULATOR_FLAGS} -serial file:${CMAKE_BINARY_DIR}/serial.log) @@ -271,11 +273,13 @@ add_custom_target( # First, we need to build the ISO for the cdrom. add_custom_target( - cdrom.iso - COMMAND cp -rf ${CMAKE_SOURCE_DIR}/iso . - COMMAND cp ${CMAKE_BINARY_DIR}/mentos/bootloader.bin ${CMAKE_BINARY_DIR}/iso/boot - COMMAND grub-mkrescue -o ${CMAKE_BINARY_DIR}/cdrom.iso ${CMAKE_BINARY_DIR}/iso - DEPENDS bootloader.bin + cdrom.iso + COMMAND ${CMAKE_COMMAND} -E rm -rf ${CMAKE_BINARY_DIR}/iso + COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/iso ${CMAKE_BINARY_DIR}/iso + COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/mentos/bootloader.bin ${CMAKE_BINARY_DIR}/iso/boot/bootloader.bin + COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_SOURCE_DIR}/iso/boot/grub/grub.cfg ${CMAKE_BINARY_DIR}/iso/boot/grub/grub.cfg + COMMAND grub-mkrescue -o ${CMAKE_BINARY_DIR}/cdrom.iso ${CMAKE_BINARY_DIR}/iso + DEPENDS bootloader.bin ) # This third target runs the emualtor, but this time, the kernel binary file is @@ -295,11 +299,12 @@ add_custom_target( # First, we need to build the ISO for the cdrom. It has a slightly different # kernel command line including 'test'. add_custom_target( - cdrom_test.iso - COMMAND cp -rf ${CMAKE_SOURCE_DIR}/iso . - COMMAND mv ${CMAKE_BINARY_DIR}/iso/boot/grub/grub.cfg.runtests ${CMAKE_BINARY_DIR}/iso/boot/grub/grub.cfg - COMMAND cp ${CMAKE_BINARY_DIR}/mentos/bootloader.bin ${CMAKE_BINARY_DIR}/iso/boot - COMMAND grub-mkrescue -o ${CMAKE_BINARY_DIR}/cdrom_test.iso ${CMAKE_BINARY_DIR}/iso + cdrom_test.iso + COMMAND ${CMAKE_COMMAND} -E rm -rf ${CMAKE_BINARY_DIR}/iso_test + COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/iso ${CMAKE_BINARY_DIR}/iso_test + COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_SOURCE_DIR}/iso/boot/grub/grub.cfg.runtests ${CMAKE_BINARY_DIR}/iso_test/boot/grub/grub.cfg + COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/mentos/bootloader.bin ${CMAKE_BINARY_DIR}/iso_test/boot/bootloader.bin + COMMAND grub-mkrescue -o ${CMAKE_BINARY_DIR}/cdrom_test.iso ${CMAKE_BINARY_DIR}/iso_test DEPENDS bootloader.bin filesystem ) @@ -308,7 +313,7 @@ add_custom_target( # after the tests are done. add_custom_target( qemu-test - COMMAND ${EMULATOR} ${EMULATOR_FLAGS} -serial file:${CMAKE_BINARY_DIR}/test.log -nographic -device isa-debug-exit -boot d -cdrom ${CMAKE_BINARY_DIR}/cdrom_test.iso + COMMAND ${EMULATOR} ${EMULATOR_FLAGS} -nographic -device isa-debug-exit -boot d -cdrom ${CMAKE_BINARY_DIR}/cdrom_test.iso DEPENDS cdrom_test.iso ) @@ -356,8 +361,8 @@ endif() # DOCUMENTATION # ----------------------------------------------------------------------------- -if (DOXYGEN_FOUND) - +if(DOXYGEN_FOUND) + # FetchContent: Doxygen Awesome CSS FetchContent_Declare(doxygenawesome GIT_REPOSITORY https://github.com/jothepro/doxygen-awesome-css @@ -431,4 +436,4 @@ if (DOXYGEN_FOUND) ${ALL_PROJECT_FILES} COMMENT "Generating Doxygen documentation" ) -endif (DOXYGEN_FOUND) \ No newline at end of file +endif(DOXYGEN_FOUND) diff --git a/kernel/inc/io/debug.h b/kernel/inc/io/debug.h index 03fc1d41b..b7ee0c322 100644 --- a/kernel/inc/io/debug.h +++ b/kernel/inc/io/debug.h @@ -149,6 +149,9 @@ struct pt_regs; dbg_fn(" EIP = 0x%-09x\n", (frame)->eip); \ dbg_fn(" CS = 0x%-04x\n", (frame)->cs); \ dbg_fn(" EFLAGS = 0x%-09x\n", (frame)->eflags); \ - dbg_fn(" UESP = 0x%-09x\n", (frame)->useresp); \ - dbg_fn(" SS = 0x%-04x\n", (frame)->ss); \ + /* Only print user mode stack info if exception came from user mode (CS privilege bits = 3) */ \ + if (((frame)->cs & 0x3) == 3) { \ + dbg_fn(" UESP = 0x%-09x\n", (frame)->useresp); \ + dbg_fn(" SS = 0x%-04x\n", (frame)->ss); \ + } \ } while (0) diff --git a/kernel/inc/klib/stack_helper.h b/kernel/inc/klib/stack_helper.h index 21caca2f7..5bdf0e8a8 100644 --- a/kernel/inc/klib/stack_helper.h +++ b/kernel/inc/klib/stack_helper.h @@ -1,22 +1,110 @@ /// @file stack_helper.h -/// @brief Couple of macros that help accessing the stack. +/// @brief Inline functions for safe stack manipulation with proper sequencing. /// @copyright (c) 2014-2024 This file is distributed under the MIT License. /// See LICENSE.md for details. #pragma once -/// @brief Access the value of the pointer. -#define __ACCESS_PTR(type, ptr) (*(type *)(ptr)) -/// @brief Moves the pointer down. -#define __MOVE_PTR_DOWN(type, ptr) ((ptr) -= sizeof(type)) -/// @brief Moves the pointer up. -#define __MOVE_PTR_UP(type, ptr) ((ptr) += sizeof(type)) -/// @brief First, it moves the pointer down, and then it pushes the value at that memory location. -#define PUSH_VALUE_ON_STACK(ptr, value) \ - (__ACCESS_PTR(__typeof__(value), __MOVE_PTR_DOWN(__typeof__(value), ptr)) = (value)) -/// @brief First, it access the value at the given memory location, and then it moves the pointer up. -#define POP_VALUE_FROM_STACK(value, ptr) \ - ({ \ - (value) = __ACCESS_PTR(__typeof__(value), ptr); \ - __MOVE_PTR_UP(__typeof__(value), ptr); \ - }) +#include +#include +#include + +/// @brief Push a 32-bit value onto the stack, decrementing the stack pointer. +/// @param sp Pointer to 32-bit stack pointer (will be decremented). +/// @param value The 32-bit value to push. +static inline void stack_push_u32(uint32_t *sp, uint32_t value) +{ + *sp -= sizeof(uint32_t); + __asm__ __volatile__("" ::: "memory"); + *(volatile uint32_t *)(*sp) = value; + __asm__ __volatile__("" ::: "memory"); +} + +/// @brief Push a signed 32-bit value onto the stack, decrementing the stack pointer. +/// @param sp Pointer to 32-bit stack pointer (will be decremented). +/// @param value The signed 32-bit value to push. +static inline void stack_push_s32(uint32_t *sp, int32_t value) +{ + *sp -= sizeof(int32_t); + __asm__ __volatile__("" ::: "memory"); + *(volatile int32_t *)(*sp) = value; + __asm__ __volatile__("" ::: "memory"); +} + +/// @brief Push a pointer value onto the stack, decrementing the stack pointer. +/// @param sp Pointer to 32-bit stack pointer (will be decremented). +/// @param ptr The pointer value to push. +static inline void stack_push_ptr(uint32_t *sp, const void *ptr) +{ + *sp -= sizeof(uint32_t); + __asm__ __volatile__("" ::: "memory"); + *(volatile uint32_t *)(*sp) = (uint32_t)ptr; + __asm__ __volatile__("" ::: "memory"); +} + +/// @brief Push a single byte onto the stack, decrementing the stack pointer. +/// @param sp Pointer to 32-bit stack pointer (will be decremented). +/// @param byte The byte value to push. +static inline void stack_push_u8(uint32_t *sp, uint8_t byte) +{ + *sp -= sizeof(uint8_t); + __asm__ __volatile__("" ::: "memory"); + *(volatile uint8_t *)(*sp) = byte; + __asm__ __volatile__("" ::: "memory"); +} + +/// @brief Pop a 32-bit value from the stack, incrementing the stack pointer. +/// @param sp Pointer to 32-bit stack pointer (will be incremented). +/// @return The 32-bit value popped from the stack. +static inline uint32_t stack_pop_u32(uint32_t *sp) +{ + uint32_t value = *(volatile uint32_t *)(*sp); + __asm__ __volatile__("" ::: "memory"); + *sp += sizeof(uint32_t); + return value; +} + +/// @brief Pop a signed 32-bit value from the stack, incrementing the stack pointer. +/// @param sp Pointer to 32-bit stack pointer (will be incremented). +/// @return The signed 32-bit value popped from the stack. +static inline int32_t stack_pop_s32(uint32_t *sp) +{ + int32_t value = *(volatile int32_t *)(*sp); + __asm__ __volatile__("" ::: "memory"); + *sp += sizeof(int32_t); + return value; +} + +/// @brief Pop a pointer value from the stack, incrementing the stack pointer. +/// @param sp Pointer to 32-bit stack pointer (will be incremented). +/// @return The pointer value popped from the stack. +static inline void *stack_pop_ptr(uint32_t *sp) +{ + void *value = (void *)*(volatile uint32_t *)(*sp); + __asm__ __volatile__("" ::: "memory"); + *sp += sizeof(uint32_t); + return value; +} + +/// @brief Push arbitrary data onto the stack, decrementing the stack pointer. +/// @param sp Pointer to 32-bit stack pointer (will be decremented by size). +/// @param data Pointer to data to push. +/// @param size Number of bytes to push. +static inline void stack_push_data(uint32_t *sp, const void *data, size_t size) +{ + *sp -= size; + __asm__ __volatile__("" ::: "memory"); + memcpy((void *)*sp, data, size); + __asm__ __volatile__("" ::: "memory"); +} + +/// @brief Pop arbitrary data from the stack, incrementing the stack pointer. +/// @param sp Pointer to 32-bit stack pointer (will be incremented by size). +/// @param data Pointer to buffer where popped data will be stored. +/// @param size Number of bytes to pop. +static inline void stack_pop_data(uint32_t *sp, void *data, size_t size) +{ + memcpy(data, (void *)*sp, size); + __asm__ __volatile__("" ::: "memory"); + *sp += size; +} diff --git a/kernel/inc/system/signal.h b/kernel/inc/system/signal.h index 998d80a36..b302e7a0d 100644 --- a/kernel/inc/system/signal.h +++ b/kernel/inc/system/signal.h @@ -12,16 +12,15 @@ /// @brief Signal codes. typedef enum { - SIGHUP = 1, ///< Hang up detected on controlling terminal or death of controlling process. - SIGINT = 2, ///< Issued if the user sends an interrupt signal (Ctrl + C). - SIGQUIT = 3, ///< Issued if the user sends a quit signal (Ctrl + D). - SIGILL = 4, ///< Illegal Instruction. - SIGTRAP = 5, ///< Trace/breakpoint trap. - SIGABRT = 6, ///< Abort signal from abort(). - SIGEMT = 7, ///< Emulator trap. - SIGFPE = 8, ///< Floating-point arithmetic exception. - SIGKILL = - 9, ///< If a process gets this signal it must quit immediately and will not perform any clean-up operations. + SIGHUP = 1, ///< Hang up detected on controlling terminal or death of controlling process. + SIGINT = 2, ///< Issued if the user sends an interrupt signal (Ctrl + C). + SIGQUIT = 3, ///< Issued if the user sends a quit signal (Ctrl + D). + SIGILL = 4, ///< Illegal Instruction. + SIGTRAP = 5, ///< Trace/breakpoint trap. + SIGABRT = 6, ///< Abort signal from abort(). + SIGEMT = 7, ///< Emulator trap. + SIGFPE = 8, ///< Floating-point arithmetic exception. + SIGKILL = 9, ///< If a process gets this signal it must quit immediately and will not perform any clean-up operations. SIGBUS = 10, ///< Bus error (bad memory access). SIGSEGV = 11, ///< Invalid memory reference. SIGSYS = 12, ///< Bad system call (SVr4). diff --git a/kernel/src/descriptor_tables/exception.S b/kernel/src/descriptor_tables/exception.S index 7886548c5..f382df9e8 100644 --- a/kernel/src/descriptor_tables/exception.S +++ b/kernel/src/descriptor_tables/exception.S @@ -6,23 +6,16 @@ extern isr_handler -; Macro used to define a ISR which does not push an error code. -%macro ISR_NOERR 1 - global INT_%1 - INT_%1: - cli - ; A normal ISR stub that pops a dummy error code to keep a - ; uniform stack frame - push 0 - push %1 - jmp isr_common -%endmacro - -; Macro used to define a ISR which pushes an error code. -%macro ISR_ERR 1 +; Unified macro for all ISRs - takes interrupt number and error code flag. +; 2nd parameter: has_error_code (1 if CPU pushes error code, 0 if not) +%macro ISR 2 global INT_%1 INT_%1: cli + %if %2 == 0 + ; CPU didn't push error code, push dummy for uniform stack frame + push 0 + %endif push %1 jmp isr_common %endmacro @@ -33,40 +26,40 @@ extern isr_handler section .text ; Standard X86 interrupt service routines -ISR_NOERR 0 -ISR_NOERR 1 -ISR_NOERR 2 -ISR_NOERR 3 -ISR_NOERR 4 -ISR_NOERR 5 -ISR_NOERR 6 -ISR_NOERR 7 -ISR_ERR 8 -ISR_NOERR 9 -ISR_ERR 10 -ISR_ERR 11 -ISR_ERR 12 -ISR_ERR 13 -ISR_ERR 14 -ISR_NOERR 15 -ISR_NOERR 16 -ISR_NOERR 17 -ISR_NOERR 18 -ISR_NOERR 19 -ISR_NOERR 20 -ISR_NOERR 21 -ISR_NOERR 22 -ISR_NOERR 23 -ISR_NOERR 24 -ISR_NOERR 25 -ISR_NOERR 26 -ISR_NOERR 27 -ISR_NOERR 28 -ISR_NOERR 29 -ISR_NOERR 30 -ISR_NOERR 31 - -ISR_NOERR 80 +ISR 0, 0 +ISR 1, 0 +ISR 2, 0 +ISR 3, 0 +ISR 4, 0 +ISR 5, 0 +ISR 6, 0 +ISR 7, 0 +ISR 8, 1 +ISR 9, 0 +ISR 10, 1 +ISR 11, 1 +ISR 12, 1 +ISR 13, 1 +ISR 14, 1 +ISR 15, 0 +ISR 16, 0 +ISR 17, 0 +ISR 18, 0 +ISR 19, 0 +ISR 20, 0 +ISR 21, 0 +ISR 22, 0 +ISR 23, 0 +ISR 24, 0 +ISR 25, 0 +ISR 26, 0 +ISR 27, 0 +ISR 28, 0 +ISR 29, 0 +ISR 30, 0 +ISR 31, 0 + +ISR 80, 0 isr_common: ; Save all registers (eax, ecx, edx, ebx, esp, ebp, esi, edi) @@ -83,15 +76,6 @@ isr_common: mov es, ax mov fs, ax mov gs, ax - ; CLD - Azzera la flag di Direzione - ; Questa istruzione forza semplicemente a zero la flag di Direzione. - ; Quando la flag di direzione vale 0 tutte le istruzioni per la - ; manipolazione delle stringhe agiscono in avanti, cioè dagli indirizzi più - ; bassi a quelli più alti. - ; L'istruzione agisce dunque sui puntatori SI e DI producendo su essi un - ; autoincremento proporzionale alla dimensione degli operandi trattati. - ; Le sue caratteristiche sono riassunte nella seguente tabella (leggi le - ; istruzioni Legenda della Tabella): cld ; Call the interrupt handler. @@ -105,13 +89,12 @@ isr_common: pop es pop ds - ; Restore all registers (eax, ecx, edx, ebx, esp, ebp, esi, edi). + ; Restore all registers (eax, ecx, edx, ebx, esp, ebp, esi, edi). popa ; Cleanup error code and IRQ # add esp, 0x8 - iret ; pops 5 things at once: ; CS, EIP, EFLAGS, SS, and ESP diff --git a/kernel/src/devices/fpu.c b/kernel/src/devices/fpu.c index 2679ded02..8a2d1a229 100644 --- a/kernel/src/devices/fpu.c +++ b/kernel/src/devices/fpu.c @@ -16,9 +16,10 @@ #include "process/process.h" #include "process/scheduler.h" #include "string.h" +#include "system/panic.h" #include "system/signal.h" -/// Pointerst to the current thread using the FPU. +/// Pointer to the thread currently using the FPU, if any. task_struct *thread_using_fpu = NULL; /// Temporary aligned buffer for copying around FPU contexts. uint8_t saves[512] __attribute__((aligned(16))); @@ -138,6 +139,36 @@ static inline void __sigfpe_handler(pt_regs_t *f) pr_debug(" SIGFPE sent.\n"); } +/// Kernel trap for invalid opcode exceptions +/// @param f The interrupt stack frame. +static inline void __invalid_opcode_handler(pt_regs_t *f) +{ + pr_debug("__invalid_opcode_handler(%p) - Invalid opcode trap\n", f); + pr_debug(" EIP: 0x%x, Error code: 0x%x\n", f->eip, f->err_code); + + // Check if this is user mode or kernel mode + if ((f->cs & 0x3) == 0x3) { + // User mode - send SIGILL + task_struct *task = scheduler_get_current_process(); + // Get the action for SIGILL. + sigaction_t *action = &task->sighand.action[SIGILL - 1]; + // If the user did not install a SIGILL handler, terminate immediately. + // Returning to the same invalid instruction would just re-trigger the fault. + if ((action->sa_handler == SIG_DFL) || (action->sa_handler == SIG_IGN)) { + do_exit(132 << 8); + return; + } + pr_debug(" Sending SIGILL to user process (pid=%d)\n", task->pid); + sys_kill(task->pid, SIGILL); + pr_debug(" SIGILL sent.\n"); + } else { + // Kernel mode - panic + pr_crit("Invalid opcode in kernel mode at 0x%x\n", f->eip); + PRINT_REGS(pr_crit, f); + kernel_panic("Invalid opcode in kernel"); + } +} + /// @brief Ensure basic FPU functionality works. /// @details /// For processors without a FPU, this tests that maths libraries link @@ -217,11 +248,15 @@ int fpu_install(void) isr_install_handler(DEV_NOT_AVL, &__invalid_op, "fpu: device missing"); pr_debug(" DEV_NOT_AVL handler installed.\n"); - pr_debug(" Step 6: Installing DIVIDE_ERROR handler\n"); + pr_debug(" Step 6: Installing INVALID_OPCODE handler\n"); + isr_install_handler(INVALID_OPCODE, &__invalid_opcode_handler, "invalid opcode"); + pr_debug(" INVALID_OPCODE handler installed.\n"); + + pr_debug(" Step 7: Installing DIVIDE_ERROR handler\n"); isr_install_handler(DIVIDE_ERROR, &__sigfpe_handler, "divide error"); pr_debug(" DIVIDE_ERROR handler installed.\n"); - pr_debug(" Step 7: Installing FLOATING_POINT_ERR handler\n"); + pr_debug(" Step 8: Installing FLOATING_POINT_ERR handler\n"); isr_install_handler(FLOATING_POINT_ERR, &__sigfpe_handler, "floating point error"); pr_debug(" FLOATING_POINT_ERR handler installed.\n"); diff --git a/kernel/src/drivers/ata.c b/kernel/src/drivers/ata.c index 7c7c48cb7..a728af6f7 100644 --- a/kernel/src/drivers/ata.c +++ b/kernel/src/drivers/ata.c @@ -6,10 +6,10 @@ /// @{ // Setup the logging for this file (do this before any other include). -#include "sys/kernel_levels.h" // Include kernel log levels. -#define __DEBUG_HEADER__ "[ATA ]" ///< Change header. +#include "sys/kernel_levels.h" // Include kernel log levels. +#define __DEBUG_HEADER__ "[ATA ]" ///< Change header. #define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. -#include "io/debug.h" // Include debugging functions. +#include "io/debug.h" // Include debugging functions. #include "drivers/ata/ata.h" #include "drivers/ata/ata_types.h" @@ -528,45 +528,107 @@ static inline void ata_dump_device(ata_device_t *dev) pr_debug(" }\n"); } -/// @brief Waits for approximately 400 nanoseconds by performing four I/O reads. -/// @param dev The device on which we wait. +/// @brief Waits for approximately 400 nanoseconds by reading the control register. +/// @param dev The ATA device to wait on. +/// @details Performs four I/O port reads (~100ns each) for a total of ~400ns. +/// This delay is required by the ATA specification between certain operations. static inline void ata_io_wait(ata_device_t *dev) { - // Perform four reads from the control register to wait for 400 ns. + // Each inportb is approximately 100 nanoseconds on a modern processor. + // Four reads provide the ~400ns delay specified by the ATA standard. inportb(dev->io_control); inportb(dev->io_control); inportb(dev->io_control); inportb(dev->io_control); } -/// @brief Waits until the status bits selected through the mask are zero. -/// @param dev The device we need to wait for. -/// @param mask The mask used to check the status bits. -/// @param timeout The maximum number of cycles to wait before timing out. -/// @return 1 on success, 0 if it times out. -static inline int ata_status_wait_not(ata_device_t *dev, long mask, long timeout) +// ============================================================================ +// ATA Status Wait Functions +// ============================================================================ + +/// @typedef ata_status_condition_fn +/// @brief Function pointer for device status condition checks. +/// @note Status conditions return 0 when ready to proceed, non-zero while waiting. +typedef int (*ata_status_condition_fn)(uint8_t status); + +/// @brief Condition: status bits (matching mask) are STILL SET (keep waiting). +/// @param status The current device status register value. +/// @param mask The status bits to check. +/// @return Non-zero (waiting) while bits match, 0 when bits are cleared. +/// @details Helper for polling until status bits are cleared. +static inline int __cond_status_has_bits(uint8_t status, uint8_t mask) +{ + return (status & mask) == mask; +} + +/// @brief Condition: status bits (matching mask) are STILL CLEAR (keep waiting). +/// @param status The current device status register value. +/// @param mask The status bits to check. +/// @return Non-zero (waiting) while bits are clear, 0 when bits are set. +/// @details Helper for polling until status bits are set. +static inline int __cond_status_missing_bits(uint8_t status, uint8_t mask) +{ + return (status & mask) != mask; +} + +/// @brief Unified ATA device status waiter with timeout protection. +/// @param dev The ATA device to poll. +/// @param mask The status bits to check. +/// @param condition The condition to evaluate (0=ready, non-zero=keep waiting). +/// @param timeout Maximum iterations before giving up. +/// @return 0 on success (condition satisfied), 1 on timeout. +/// @details Polls the device status register while applying the condition function. +/// Uses volatile timeout to prevent compiler optimization of the critical wait loop. +static inline int ata_status_wait(ata_device_t *dev, uint8_t mask, + int (*evaluate_condition)(uint8_t, uint8_t), + long timeout) { uint8_t status; + // Use volatile local copy to prevent compiler optimization of timeout loop. + // The return value depends on proper timeout decrement, making volatile + // semantics critical for correctness. + volatile long volatile_timeout = timeout; + do { + // Read current device status. status = inportb(dev->io_reg.status); - } while (((status & mask) == mask) && (--timeout > 0)); - // Return 1 on success (bits cleared), 0 on timeout. - return timeout <= 0; + // Check if condition is satisfied. + if (!evaluate_condition(status, mask)) { + // Condition met - operation succeeded. + return 0; + } + } while (--volatile_timeout > 0); + + // Timeout occurred - operation failed or device not responding. + return 1; +} + +/// @brief Waits until the status bits selected through the mask are zero. +/// @param dev The ATA device to poll. +/// @param mask The status bits to check. +/// @param timeout Maximum poll iterations before timing out. +/// @return 0 on success (bits cleared), 1 on timeout. +/// @details Polls the device status register until the bits specified by mask +/// are all cleared (0). Uses volatile semantics to ensure the timeout loop +/// cannot be optimized away by the compiler. +static inline int ata_status_wait_not(ata_device_t *dev, long mask, long timeout) +{ + // Call unified waiter with condition that bits should be cleared. + return ata_status_wait(dev, (uint8_t)mask, __cond_status_has_bits, timeout); } /// @brief Waits until the status bits selected through the mask are set. -/// @param dev The device we need to wait for. -/// @param mask The mask used to check the status bits. -/// @param timeout The maximum number of cycles to wait before timing out. -/// @return 1 on success, 0 if it times out. +/// @param dev The ATA device to poll. +/// @param mask The status bits to check. +/// @param timeout Maximum poll iterations before timing out. +/// @return 0 on success (bits set), 1 on timeout. +/// @details Polls the device status register until the bits specified by mask +/// are all set (1). Uses volatile semantics to ensure the timeout loop +/// cannot be optimized away by the compiler. static inline int ata_status_wait_for(ata_device_t *dev, long mask, long timeout) { - uint8_t status; - do { - status = inportb(dev->io_reg.status); - } while (((status & mask) != mask) && (--timeout > 0)); - // Return 1 on success (bits set), 0 on timeout. - return timeout <= 0; + // Call unified waiter with condition that bits should be set. + return ata_status_wait(dev, (uint8_t)mask, __cond_status_missing_bits, timeout); } /// @brief Prints the status and error information about the device. diff --git a/kernel/src/drivers/mouse.c b/kernel/src/drivers/mouse.c index 22d502cdd..533c33a98 100644 --- a/kernel/src/drivers/mouse.c +++ b/kernel/src/drivers/mouse.c @@ -34,18 +34,24 @@ static int32_t mouse_y = (600 / 2); /// @brief Mouse wait for a command. /// @param type 1 for sending - 0 for receiving. +/// @details Uses volatile timeout semantics to prevent the compiler from +/// optimizing away the timing-critical poll loops. This ensures +/// proper hardware synchronization even with aggressive optimization. static void __mouse_waitcmd(unsigned char type) { - register unsigned int _time_out = 100000; + // Use volatile to prevent compiler optimization of timeout loops. + // The timeout variable is critical for ensuring the mouse device has time + // to respond to commands within the expected hardware constraints. + volatile unsigned int _time_out = 100000; if (type == 0) { - // DATA + // DATA - Wait for output buffer full bit (0x64 & 0x01) while (_time_out--) { if ((inportb(0x64) & 1) == 1) { break; } } } else { - // SIGNALS + // SIGNALS - Wait for input buffer empty bit (0x64 & 0x02) while (_time_out--) { if ((inportb(0x64) & 2) == 0) { break; diff --git a/kernel/src/drivers/ps2.c b/kernel/src/drivers/ps2.c index 38a26ce5c..fb9e642d0 100644 --- a/kernel/src/drivers/ps2.c +++ b/kernel/src/drivers/ps2.c @@ -15,12 +15,20 @@ #include "stdbool.h" #include "sys/bitops.h" +// ================================================================================ +// PS/2 I/O Port Definitions +// ================================================================================ + /// @defgroup PS2_IO_PORTS PS/2 I/O Ports /// @{ #define PS2_DATA 0x60 ///< Data signal line. #define PS2_STATUS 0x64 ///< Status and command signal line. /// @} +// ================================================================================ +// PS/2 Controller Commands +// ================================================================================ + /// @defgroup PS2_CONTROLLER_COMMANDS PS/2 Controller Commands /// @{ #define PS2_CTRL_TEST_CONTROLLER 0xAA ///< Command to test the PS/2 controller; returns 0x55 for pass, 0xFC for fail. @@ -37,6 +45,10 @@ #define PS2_CTRL_P1_RESET 0xFE ///< Resets the first PS/2 port. /// @} +// ================================================================================ +// PS/2 Device Commands +// ================================================================================ + /// @defgroup PS2_DEVICE_COMMANDS PS/2 Device (Keyboard) Commands /// @{ #define PS2_DEV_RESET 0xFF ///< Resets the device (keyboard or mouse), triggers self-test. @@ -47,6 +59,10 @@ #define PS2_DEV_SCAN_CODE_SET 0xF0 ///< Selects the scancode set (requires additional byte to specify the set). /// @} +// ================================================================================ +// PS/2 Device Responses +// ================================================================================ + /// @defgroup PS2_DEVICE_RESPONSES PS/2 Device Responses /// @{ #define PS2_DEV_SELF_TEST_PASS 0xAA ///< Self-test passed (sent after a reset or power-up). @@ -58,6 +74,10 @@ #define PS2_RESEND 0xFE ///< Response requesting the controller to resend the last command sent. /// @} +// ================================================================================ +// PS/2 Status Register Flags +// ================================================================================ + /// @defgroup PS2_STATUS_REGISTER_FLAGS PS/2 Status Register Flags /// @{ #define PS2_STATUS_OUTPUT_FULL 0x01 ///< Output buffer is full, data is available to be read. @@ -68,7 +88,10 @@ #define PS2_STATUS_PARITY_ERROR 0x80 ///< Parity error occurred during communication. /// @} +// ================================================================================ // PS/2 Controller Configuration Byte +// ================================================================================ + // Bit | Meaning // 0 | First PS/2 port interrupt (1 = enabled, 0 = disabled) // 1 | Second PS/2 port interrupt (1 = enabled, 0 = disabled, only if 2 PS/2 ports supported) @@ -79,34 +102,126 @@ // 6 | First PS/2 port translation (1 = enabled, 0 = disabled) // 7 | Must be zero -void ps2_write_data(unsigned char data) +// ================================================================================ +// Internal Helper Types and Functions +// ================================================================================ + +/// @brief Internal helper function type for waiting on PS/2 status conditions. +/// @param status the PS/2 status register value to check. +/// @return 0 if the condition is met, 1 otherwise. +typedef int (*ps2_wait_condition_fn)(int); + +/// @brief Returns 0 if the input buffer is empty (ready for new data), non-zero while still full (waiting). +static inline int __cond_input_full(int status) { - unsigned int timeout = 100000; + return (status & PS2_STATUS_INPUT_FULL) != 0; // Non-zero (keep waiting) while full, 0 when empty +} - // Wait for the input buffer to be empty before sending data (with timeout). - while ((inportb(PS2_STATUS) & PS2_STATUS_INPUT_FULL) && --timeout) { +/// @brief Returns 0 if data is available in output buffer, non-zero while empty (waiting). +static inline int __cond_output_empty(int status) +{ + return (status & PS2_STATUS_OUTPUT_FULL) == 0; // Non-zero (keep waiting) while empty, 0 when data available +} + +static inline int __wait_for_condition(ps2_wait_condition_fn condition_fn, unsigned int timeout_max) +{ + volatile unsigned int timeout_count = timeout_max; + unsigned char status = 0; + while (timeout_count-- > 0) { + // Memory barrier: prevent compiler from hoisting the loop or optimizing it away + __asm__ __volatile__("" ::: "memory"); + status = inportb(PS2_STATUS); + // Exit when condition is met (function returns 0/false) + if (condition_fn(status) == 0) { + return 0; // Condition met + } pause(); } + // Timeout occurred - print diagnostic info + // Note: This pr_warning is essential for correct behavior - it prevents compiler + // from optimizing away the timeout loop. Without it, the compiler may not execute + // the loop at all, causing the PS/2 controller to appear unresponsive. + pr_warning("ps2: timeout waiting for condition (status=0x%02x, bits: ", status); + if (status & PS2_STATUS_OUTPUT_FULL) + pr_warning("OUTPUT_FULL "); + if (status & PS2_STATUS_INPUT_FULL) + pr_warning("INPUT_FULL "); + if (status & PS2_STATUS_SYSTEM) + pr_warning("SYSTEM "); + if (status & PS2_STATUS_COMMAND) + pr_warning("COMMAND "); + if (status & PS2_STATUS_TIMEOUT) + pr_warning("TIMEOUT "); + if (status & PS2_STATUS_PARITY_ERROR) + pr_warning("PARITY "); + pr_warning(")\n"); + return -1; // Timeout +} - if (!timeout) { - pr_warning("ps2_write_data: timeout waiting for input buffer\n"); - return; +/// @brief Perform a busy-wait delay with memory barriers to prevent compiler optimization. +/// @param iterations the number of pause() iterations to execute. +static inline void __ps2_delay(unsigned int iterations) +{ + for (volatile unsigned int i = 0; i < iterations; i++) { + pause(); } + __asm__ __volatile__("" ::: "memory"); +} - outportb(PS2_DATA, data); +/// @brief Flush any stale data in the output buffer with blind reads (no status check). +/// @param count the number of blind reads to perform. +static inline void __ps2_blind_read_buffer(unsigned int count) +{ + for (volatile unsigned int i = 0; i < count; i++) { + // Internal delay loop before each blind read. + __ps2_delay(1000); + // Blind read - don't check status. We just want to clear out any stale data that might be sitting in the output + // buffer. + inportb(PS2_DATA); + __asm__ __volatile__("" ::: "memory"); + } } -void ps2_write_command(unsigned char command) +/// @brief Flush the output buffer by reading while data is available (with timeout). +/// @param max_iterations maximum number of reads to attempt before giving up. +static inline void __ps2_flush_output_buffer(unsigned int max_iterations) { - unsigned int timeout = 100000; + volatile unsigned int timeout = max_iterations; + while (timeout-- > 0) { + if (inportb(PS2_STATUS) & PS2_STATUS_OUTPUT_FULL) { + inportb(PS2_DATA); // Read and discard + } else { + break; // Buffer is empty, we're done + } + } +} + +// ================================================================================ +// Core PS/2 Driver Functions +// ================================================================================ + +void ps2_write_data(unsigned char data) +{ + // Before writing, ensure output buffer is empty to avoid deadlock + __ps2_blind_read_buffer(20); // Wait for the input buffer to be empty before sending data (with timeout). - while ((inportb(PS2_STATUS) & PS2_STATUS_INPUT_FULL) && --timeout) { - pause(); + volatile int wait_result = __wait_for_condition(__cond_input_full, 100); + if (wait_result < 0) { + return; } - if (!timeout) { - pr_warning("ps2_write_command: timeout waiting for input buffer\n"); + outportb(PS2_DATA, data); +} + +void ps2_write_command(unsigned char command) +{ + // Before writing, ensure output buffer is empty to avoid deadlock + __ps2_blind_read_buffer(20); + + // Wait for the input buffer to be empty before sending the command (with timeout). + volatile int wait_result = __wait_for_condition(__cond_input_full, 100); + if (wait_result < 0) { return; } @@ -116,23 +231,19 @@ void ps2_write_command(unsigned char command) unsigned char ps2_read_data(void) { - unsigned int timeout = 1000000; - - // Wait until the output buffer is not full (data is available, with timeout). - while (!(inportb(PS2_STATUS) & PS2_STATUS_OUTPUT_FULL) && --timeout) { - pause(); - } - - if (!timeout) { - pr_warning("ps2_read_data: timeout waiting for output buffer\n"); - return 0xFF; + // Wait until the output buffer has data available (with timeout). + volatile int wait_result = __wait_for_condition(__cond_output_empty, 1000); + if (wait_result < 0) { + return 0xFF; // Return an error value on timeout. } // Read and return the data from the PS/2 data register. return inportb(PS2_DATA); } -/// @brief Reads the PS2 controller status. +// ================================================================================ +// PS/2 Controller Helper Functions +// ================================================================================ /// @return the PS2 controller status. static inline unsigned char __ps2_get_controller_status(void) { @@ -209,26 +320,50 @@ static const char *__ps2_get_response_error_message(unsigned response) return "unknown error"; } +// ================================================================================ +// PS/2 Controller Initialization +// ================================================================================ + int ps2_initialize(void) { unsigned char status; unsigned char response; bool_t dual; - unsigned int flush_timeout; + + // Pre-init: Read initial status before doing anything + unsigned char initial_status = inportb(PS2_STATUS); + pr_info("PS/2 pre-init: initial status register = 0x%02x\n", initial_status); // Pre-init: aggressively flush any stale data from BIOS/bootloader - pr_debug("Initial aggressive buffer flush...\n"); + // Do BLIND reads first (without status check) since status itself might be unreliable + pr_debug("Initial aggressive buffer flush with blind reads...\n"); + + // Blind reads: force-read without checking status + for (int i = 0; i < 16; i++) { + volatile unsigned int delay = 5000; + while (delay-- > 0) { + pause(); + } + unsigned char data = inportb(PS2_DATA); + pr_debug(" Blind read [%d]: 0x%02x\n", i, data); + } + + // Then try status-guarded reads for (int flush_retry = 0; flush_retry < 10; flush_retry++) { - unsigned int retry = 100; + volatile unsigned int retry = 100; while (retry-- > 0) { if (inportb(PS2_STATUS) & PS2_STATUS_OUTPUT_FULL) { - inportb(PS2_DATA); // Read and discard + unsigned char data = inportb(PS2_DATA); // Read and discard + pr_debug(" Status-guarded read: 0x%02x\n", data); } else { break; } } } + // Long delay to let controller stabilize + __ps2_delay(1000); + status = __ps2_get_controller_status(); pr_debug("Initial Status : %s (%3d | %02x)\n", dec_to_binary(status, 8), status, status); @@ -242,16 +377,12 @@ int ps2_initialize(void) pr_debug("Disabling first port...\n"); __ps2_disable_first_port(); // Small delay to allow command to take effect - for (volatile int i = 0; i < 10000; i++) { - pause(); - } + __ps2_delay(1000); pr_debug("Disabling second port...\n"); __ps2_disable_second_port(); // Small delay to allow command to take effect - for (volatile int i = 0; i < 10000; i++) { - pause(); - } + __ps2_delay(1000); // ======================================================================== // Step 2: Flush The Output Buffer @@ -266,15 +397,7 @@ int ps2_initialize(void) pr_debug("Flushing the output buffer...\n"); // Flush the output buffer with timeout to prevent infinite loops - // Only read if output buffer is marked as full - flush_timeout = 100; - while (flush_timeout-- > 0) { - if (inportb(PS2_STATUS) & PS2_STATUS_OUTPUT_FULL) { - inportb(PS2_DATA); // Read and discard - } else { - break; // Buffer is empty, we're done - } - } + __ps2_flush_output_buffer(100); // ======================================================================== // Step 3: Set the Controller Configuration Byte @@ -296,6 +419,8 @@ int ps2_initialize(void) bit_clear_assign(status, 4); bit_set_assign(status, 6); // Enable translation __ps2_set_controller_status(status); + // Re-read status to ensure write took effect (prevents compiler caching) + status = __ps2_get_controller_status(); pr_debug("Status : %s (%3d | %02x)\n", dec_to_binary(status, 8), status, status); // ======================================================================== @@ -317,15 +442,10 @@ int ps2_initialize(void) } // The self-test can reset the controller, so always restore the configuration. __ps2_set_controller_status(status); + // Re-read status to ensure write took effect (prevents compiler caching) + status = __ps2_get_controller_status(); // Flush the output buffer after self-test as it can generate spurious data (with timeout). - flush_timeout = 100; - while (flush_timeout-- > 0) { - if (inportb(PS2_STATUS) & PS2_STATUS_OUTPUT_FULL) { - inportb(PS2_DATA); // Read and discard - } else { - break; // Buffer is empty - } - } + __ps2_flush_output_buffer(100); // ======================================================================== // Step 5: Determine If There Are 2 Channels @@ -347,6 +467,8 @@ int ps2_initialize(void) // Ensure second clock is enabled in the config byte for later use. bit_clear_assign(status, 5); __ps2_set_controller_status(status); + // Re-read status to ensure write took effect (prevents compiler caching) + status = __ps2_get_controller_status(); } else { pr_debug("Recognized a `single channel` PS/2 controller...\n"); } @@ -407,6 +529,8 @@ int ps2_initialize(void) } bit_set_assign(status, 6); // Keep translation ON (set 2 -> set 1) __ps2_set_controller_status(status); + // Re-read status to ensure write took effect (prevents compiler caching) + status = __ps2_get_controller_status(); // ======================================================================== // Step 8: Reset Devices @@ -421,22 +545,13 @@ int ps2_initialize(void) // Before resetting devices, flush any stale data in the buffer. pr_debug("Flushing buffer before device reset...\n"); - flush_timeout = 100; - while (flush_timeout-- > 0) { - if (inportb(PS2_STATUS) & PS2_STATUS_OUTPUT_FULL) { - inportb(PS2_DATA); // Read and discard - } else { - break; - } - } + __ps2_flush_output_buffer(100); // Reset first port. pr_debug("Resetting first PS/2 port...\n"); __ps2_write_first_port(0xFF); // Give device time to respond - for (volatile int i = 0; i < 50000; i++) { - pause(); - } + __ps2_delay(50000); // Wait for `command acknowledged`. response = ps2_read_data(); pr_debug("First port reset response: 0x%02x\n", response); @@ -447,9 +562,7 @@ int ps2_initialize(void) // Device acknowledged reset (or resend), wait for self-test response. pr_debug("First port reset acknowledged, waiting for self-test...\n"); // Give device time to complete self-test - for (volatile int i = 0; i < 100000; i++) { - pause(); - } + __ps2_delay(1000); response = ps2_read_data(); pr_debug("First port self-test response: 0x%02x\n", response); if (response == PS2_DEV_SELF_TEST_PASS) { @@ -470,9 +583,7 @@ int ps2_initialize(void) pr_debug("Resetting second PS/2 port...\n"); __ps2_write_second_port(0xFF); // Give device time to respond - for (volatile int i = 0; i < 50000; i++) { - pause(); - } + __ps2_delay(50000); // Wait for `command acknowledged`. response = ps2_read_data(); pr_debug("Second port reset response: 0x%02x\n", response); @@ -483,9 +594,7 @@ int ps2_initialize(void) // Device acknowledged reset, wait for self-test response. pr_debug("Second port reset acknowledged, waiting for self-test...\n"); // Give device time to complete self-test - for (volatile int i = 0; i < 100000; i++) { - pause(); - } + __ps2_delay(1000); response = ps2_read_data(); pr_debug("Second port self-test response: 0x%02x\n", response); if (response == PS2_DEV_SELF_TEST_PASS) { @@ -508,14 +617,7 @@ int ps2_initialize(void) pr_debug("Flushing the output buffer...\n"); // Final flush with timeout - flush_timeout = 100; - while (flush_timeout-- > 0) { - if (inportb(PS2_STATUS) & PS2_STATUS_OUTPUT_FULL) { - inportb(PS2_DATA); // Read and discard - } else { - break; // Buffer is empty - } - } + __ps2_flush_output_buffer(100); // ======================================================================== // Step 9: PS/2 initialization complete @@ -523,7 +625,7 @@ int ps2_initialize(void) // config byte. IRQ handlers will enable the corresponding PIC IRQs when // they are installed (keyboard_initialize, mouse_install, etc). - pr_notice("PS/2 controller initialized successfully.\n"); + pr_info("PS/2 controller initialized successfully.\n"); return 0; } diff --git a/kernel/src/drivers/rtc.c b/kernel/src/drivers/rtc.c index e2b590bfb..6ad21dc8b 100644 --- a/kernel/src/drivers/rtc.c +++ b/kernel/src/drivers/rtc.c @@ -16,167 +16,295 @@ #include "hardware/pic8259.h" #include "io/port_io.h" #include "kernel.h" +#include "proc_access.h" #include "string.h" -#define CMOS_ADDR 0x70 ///< Addess where we need to write the Address. -#define CMOS_DATA 0x71 ///< Addess where we need to write the Data. +// ============================================================================ +// RTC Port Definitions +// ============================================================================ -/// Current global time. -tm_t global_time; -/// Previous global time. -tm_t previous_global_time; -/// Data type is BCD. +#define CMOS_ADDR 0x70 ///< I/O port for CMOS address selection. +#define CMOS_DATA 0x71 ///< I/O port for CMOS data read/write. +#define CMOS_NMI_DISABLE 0x80 ///< Disable NMI when selecting CMOS register. +#define CMOS_IOWAIT_PORT 0x80 ///< I/O wait port used for short delays. + +// ============================================================================ +// RTC Module Variables +// ============================================================================ + +/// Current global time updated by RTC interrupt handler. +tm_t global_time = {0}; +/// Previous global time used for consistency detection during initialization. +tm_t previous_global_time = {0}; +/// Data type flag: 1 if BCD format, 0 if binary format. int is_bcd; -/// @brief Checks if the two time values are different. -/// @param t0 the first time value. -/// @param t1 the second time value. -/// @return 1 if they are different, 0 otherwise. -static inline unsigned int rtc_are_different(tm_t *t0, tm_t *t1) +// ============================================================================ +// RTC Condition and Wait Functions +// ============================================================================ + +/// @brief Short I/O wait to let CMOS address/data lines settle. +static inline void __rtc_io_wait(void) { - if (t0->tm_sec != t1->tm_sec) { - return 1; - } - if (t0->tm_min != t1->tm_min) { - return 1; - } - if (t0->tm_hour != t1->tm_hour) { - return 1; - } - if (t0->tm_mon != t1->tm_mon) { - return 1; - } - if (t0->tm_year != t1->tm_year) { - return 1; - } - if (t0->tm_wday != t1->tm_wday) { - return 1; - } - if (t0->tm_mday != t1->tm_mday) { - return 1; - } - return 0; + outportb(CMOS_IOWAIT_PORT, 0); + outportb(CMOS_IOWAIT_PORT, 0); + outportb(CMOS_IOWAIT_PORT, 0); + outportb(CMOS_IOWAIT_PORT, 0); } -/// @brief Check if rtc is updating time currently. -/// @return 1 if RTC is updating, 0 otherwise. -static inline unsigned int is_updating_rtc(void) +/// @brief Check if RTC is currently updating (UIP flag set). +/// @return Non-zero if updating, 0 if ready to read. +static inline unsigned int __rtc_is_updating(void) { - outportb(CMOS_ADDR, 0x0A); - uint32_t status = inportb(CMOS_DATA); - return (status & 0x80U) != 0; + outportb(CMOS_ADDR, (unsigned char)(CMOS_NMI_DISABLE | 0x0A)); + __rtc_io_wait(); + unsigned char status = inportb(CMOS_DATA); + __asm__ __volatile__("" ::: "memory"); + return (status & 0x80); } -/// @brief Reads the given register. -/// @param reg the register to read. -/// @return the value we read. -static inline unsigned char read_register(unsigned char reg) +/// @brief Checks if two time values are identical. +/// @param t0 First time value to compare. +/// @param t1 Second time value to compare. +/// @return 1 if identical, 0 if different. +static inline unsigned int __rtc_times_match(tm_t *t0, tm_t *t1) +{ + return (t0->tm_sec == t1->tm_sec) && + (t0->tm_min == t1->tm_min) && + (t0->tm_hour == t1->tm_hour) && + (t0->tm_mon == t1->tm_mon) && + (t0->tm_year == t1->tm_year) && + (t0->tm_wday == t1->tm_wday) && + (t0->tm_mday == t1->tm_mday); +} + +// ============================================================================ +// RTC I/O Functions +// ============================================================================ + +/// @brief Reads a CMOS register using inline assembly to prevent compiler optimization. +/// @param reg The CMOS register number (0x00-0x0F for RTC, higher for extension). +/// @return The value read from the CMOS register. +/// @details Uses direct inline assembly to ensure the I/O operations cannot be +/// optimized away by aggressive compiler optimizations in Release mode. Sets NMI +/// disable bit (0x80) during access, performs I/O wait cycles, and enforces +/// memory barriers to guarantee correct execution order. +__attribute__((noinline)) static unsigned char __rtc_read_cmos_direct(unsigned char reg) { - outportb(CMOS_ADDR, reg); - return inportb(CMOS_DATA); + volatile unsigned char value; + // Direct inline assembly prevents any compiler optimization in Release mode. + // This is critical for CMOS/RTC reads which have hardware timing requirements. + __asm__ __volatile__( + "movb %1, %%al\n\t" // Load register number with NMI disabled + "outb %%al, $0x70\n\t" // Select CMOS register (port 0x70) + "outb %%al, $0x80\n\t" // I/O wait cycle (port 0x80 is diagnostic port) + "outb %%al, $0x80\n\t" // Second I/O wait (~400ns total) + "inb $0x71, %%al\n\t" // Read CMOS data (port 0x71) + "movb %%al, %0" // Store result + : "=m"(value) // Output: value + : "r"((unsigned char)(CMOS_NMI_DISABLE | reg)) // Input: register with NMI disabled + : "al", "memory" // Clobbered: AL register, all memory + ); + return value; } -/// @brief Writes on the given register. -/// @param reg the register on which we need to write. -/// @param value the value we want to write. +/// @brief Writes a value to a CMOS register. +/// @param reg The register address to write to. +/// @param value The value to write. +/// @details Disables NMI during write, performs I/O wait for hardware timing. static inline void write_register(unsigned char reg, unsigned char value) { - outportb(CMOS_ADDR, reg); + outportb(CMOS_ADDR, (unsigned char)(CMOS_NMI_DISABLE | reg)); + __rtc_io_wait(); outportb(CMOS_DATA, value); } -/// @brief Transforms a Binary-Coded Decimal (BCD) to decimal. -/// @param bcd the BCD value. -/// @return the decimal value. +/// @brief Converts a Binary-Coded Decimal (BCD) value to binary. +/// @param bcd The BCD value to convert. +/// @return The binary (decimal) equivalent. static inline unsigned char bcd2bin(unsigned char bcd) { return ((bcd >> 4U) * 10) + (bcd & 0x0FU); } -/// @brief Reads the current datetime value from a real-time clock. -static inline void rtc_read_datetime(void) +/// @brief Reads the current datetime value from the RTC. +/// @details Reads all time fields (seconds, minutes, hours, month, year, weekday, monthday) +/// from CMOS registers and stores them in the global_time structure. Handles both +/// BCD and binary formats based on the control register configuration. +/// @note Uses direct assembly CMOS reads to prevent compiler optimization in Release mode. +__attribute__((noinline)) static void rtc_read_datetime(void) { - if (read_register(0x0CU) & 0x10U) { - if (is_bcd) { - global_time.tm_sec = bcd2bin(read_register(0x00)); - global_time.tm_min = bcd2bin(read_register(0x02)); - global_time.tm_hour = bcd2bin(read_register(0x04)) + 2; - global_time.tm_mon = bcd2bin(read_register(0x08)); - global_time.tm_year = bcd2bin(read_register(0x09)) + 2000; - global_time.tm_wday = bcd2bin(read_register(0x06)); - global_time.tm_mday = bcd2bin(read_register(0x07)); - } else { - global_time.tm_sec = read_register(0x00); - global_time.tm_min = read_register(0x02); - global_time.tm_hour = read_register(0x04) + 2; - global_time.tm_mon = read_register(0x08); - global_time.tm_year = read_register(0x09) + 2000; - global_time.tm_wday = read_register(0x06); - global_time.tm_mday = read_register(0x07); + // Wait until RTC update cycle completes (UIP bit clears). + // This ensures we read a consistent snapshot of the time registers. + volatile unsigned int timeout = 10000; + while (__rtc_is_updating() && timeout--) { + pause(); + } + + // Warn if UIP flag never cleared (hardware issue or extreme timing problem). + if (timeout == 0) { + unsigned char status_a = __rtc_read_cmos_direct(0x0A); + unsigned char status_b = __rtc_read_cmos_direct(0x0B); + unsigned char status_c = __rtc_read_cmos_direct(0x0C); + pr_warning("rtc_read_datetime: UIP timeout (A=0x%02x B=0x%02x C=0x%02x)\n", status_a, status_b, status_c); + } + + // Read all RTC time/date registers using optimized direct assembly reads. + // Using the unified __rtc_read_cmos_direct() ensures no compiler optimization. + unsigned char sec = __rtc_read_cmos_direct(0x00); + unsigned char min = __rtc_read_cmos_direct(0x02); + unsigned char hour = __rtc_read_cmos_direct(0x04); + unsigned char mon = __rtc_read_cmos_direct(0x08); + unsigned char year = __rtc_read_cmos_direct(0x09); + unsigned char wday = __rtc_read_cmos_direct(0x06); + unsigned char mday = __rtc_read_cmos_direct(0x07); + + // Debug output for troubleshooting. + pr_debug("Raw RTC: sec=%u min=%u hour=%u mon=%u year=%u wday=%u mday=%u (BCD=%d)\n", sec, min, hour, mon, year, wday, mday, is_bcd); + + // Diagnostic checks for known hardware failure modes (one-shot warnings). + if (sec == 0 && min == 0 && hour == 0 && mon == 0 && year == 0 && wday == 0 && mday == 0) { + static int warned_zero = 0; + if (!warned_zero) { + warned_zero = 1; + pr_warning("rtc_read_datetime: all-zero read (hardware not initialized or QEMU issue)\n"); } } + if (sec == 0xFF && min == 0xFF && hour == 0xFF && mon == 0xFF && + year == 0xFF && wday == 0xFF && mday == 0xFF) { + static int warned_ff = 0; + if (!warned_ff) { + warned_ff = 1; + pr_warning("rtc_read_datetime: all-0xFF read (CMOS bus floating or disconnected)\n"); + } + } + + // Check for mirrored register indices (data port echoing address instead of data). + if (sec == (0x80 | 0x00) && min == (0x80 | 0x02) && hour == (0x80 | 0x04) && + wday == (0x80 | 0x06) && mday == (0x80 | 0x07) && mon == (0x80 | 0x08) && + year == (0x80 | 0x09)) { + static int warned_mirror = 0; + if (!warned_mirror) { + warned_mirror = 1; + unsigned char status_a = __rtc_read_cmos_direct(0x0A); + unsigned char status_b = __rtc_read_cmos_direct(0x0B); + unsigned char status_c = __rtc_read_cmos_direct(0x0C); + pr_warning("rtc_read_datetime: mirrored index values (A=0x%02x B=0x%02x C=0x%02x)\n", status_a, status_b, status_c); + } + } + + // Convert and store the datetime values. + if (is_bcd) { + // BCD format: each nibble is a decimal digit (e.g., 0x59 = 59). + global_time.tm_sec = bcd2bin(sec); + global_time.tm_min = bcd2bin(min); + global_time.tm_hour = bcd2bin(hour); + global_time.tm_mon = bcd2bin(mon); + global_time.tm_year = bcd2bin(year) + 2000; + global_time.tm_wday = bcd2bin(wday); + global_time.tm_mday = bcd2bin(mday); + } else { + // Binary format: direct values. + global_time.tm_sec = sec; + global_time.tm_min = min; + global_time.tm_hour = hour; + global_time.tm_mon = mon; + global_time.tm_year = year + 2000; + global_time.tm_wday = wday; + global_time.tm_mday = mday; + } + + // Force memory barrier to ensure writes complete + __asm__ __volatile__("" ::: "memory"); } -/// @brief Updates the internal datetime value. +// ============================================================================ +// RTC Core Driver Functions +// ============================================================================ + +/// @brief Updates the global datetime by reading from the RTC controller. +/// @details Safely reads the current time from the RTC using timeout protection +/// to prevent infinite loops. On initial boot, performs a double-read to ensure +/// the value has stabilized (i.e., detect a change since the last read interval). +/// Uses the unified __wait_for_condition() helper with volatile semantics to +/// ensure the compiler cannot optimize away timing-critical wait loops. static inline void rtc_update_datetime(void) { - static unsigned int first_update = 1; - unsigned int timeout; - - // Wait until rtc is not updating (with timeout to prevent infinite loop). - timeout = 1000; - while (is_updating_rtc() && --timeout) { - __asm__ __volatile__("pause"); - } - - // Read the values. - rtc_read_datetime(); - if (first_update) { - do { - // Save the previous global time. - previous_global_time = global_time; - // Wait until rtc is not updating (with timeout). - timeout = 1000; - while (is_updating_rtc() && --timeout) { - __asm__ __volatile__("pause"); - } - // Read the values. - rtc_read_datetime(); - } while (!rtc_are_different(&previous_global_time, &global_time)); - first_update = 0; + // Read until we get two consecutive identical reads, confirming stability. + // This OSDev-recommended approach ensures we didn't catch the RTC mid-update. + volatile unsigned int timeout = 10000; + while (timeout--) { + // First read. + rtc_read_datetime(); + previous_global_time = global_time; + + // Second read. + rtc_read_datetime(); + + // If both reads match, we have a stable value. + if (__rtc_times_match(&previous_global_time, &global_time)) { + return; + } } + // If we timeout, use the last read value anyway. + pr_warning("rtc_update_datetime: timeout waiting for stable read\n"); } -/// @brief Callback for RTC. -/// @param f the current registers. +// ============================================================================ +// RTC Controller Initialization +// ============================================================================ + +/// @brief Interrupt service routine for RTC events. +/// @param f Pointer to the saved processor state at interrupt time. +/// @details Called by the interrupt handler when the RTC generates an interrupt +/// (typically on update-ended interrupt). Updates the global time structure. static inline void rtc_handler_isr(pt_regs_t *f) { rtc_update_datetime(); } void gettime(tm_t *time) { - // Copy the update time. + // Copy the current global time to the provided buffer. memcpy(time, &global_time, sizeof(tm_t)); } +/// @brief Initializes the Real-Time Clock driver. +/// @return 0 on success, -1 on failure. +/// @details Configures the RTC for 24-hour mode and update-ended interrupts, +/// installs the interrupt handler, and performs an initial time read. int rtc_initialize(void) { unsigned char status; - status = read_register(0x0B); - status |= 0x02U; // 24 hour clock - status |= 0x10U; // update ended interrupts - status &= ~0x20U; // no alarm interrupts - status &= ~0x40U; // no periodic interrupt - is_bcd = !(status & 0x04U); // check if data type is BCD + // Read the control register B to modify interrupt configuration. + status = __rtc_read_cmos_direct(0x0B); + // Enable 24-hour mode (bit 1). + status |= 0x02U; + // Enable update-ended interrupt (bit 4) to get notified when time changes. + status |= 0x10U; + // Disable alarm interrupts (bit 5). + status &= ~0x20U; + // Disable periodic interrupt (bit 6). + status &= ~0x40U; + // Check the data format: BCD (bit 2 = 0) or binary (bit 2 = 1). + is_bcd = !(status & 0x04U); + // Write the updated configuration back. write_register(0x0B, status); - read_register(0x0C); + // Clear any pending interrupts by reading register C. + __rtc_read_cmos_direct(0x0C); - // Install the IRQ. + // Install the RTC interrupt handler for the real-time clock IRQ. irq_install_handler(IRQ_REAL_TIME_CLOCK, rtc_handler_isr, "Real Time Clock (RTC)"); - // Enable the IRQ. + // Enable the RTC IRQ at the PIC level. pic8259_irq_enable(IRQ_REAL_TIME_CLOCK); - // Wait until rtc is ready. + + // Perform initial time synchronization. rtc_update_datetime(); + + // Log successful initialization with current time. + pr_debug("RTC initialized: %04d-%02d-%02d %02d:%02d:%02d (BCD: %s)\n", global_time.tm_year, global_time.tm_mon, global_time.tm_mday, global_time.tm_hour, global_time.tm_min, global_time.tm_sec, is_bcd ? "Yes" : "No"); return 0; } +/// @brief Finalizes the Real-Time Clock driver. +/// @return 0 on success. +/// @details Uninstalls the interrupt handler and disables the RTC IRQ. int rtc_finalize(void) { // Uninstall the IRQ. diff --git a/kernel/src/elf/elf.c b/kernel/src/elf/elf.c index 315232275..5848f6eb8 100644 --- a/kernel/src/elf/elf.c +++ b/kernel/src/elf/elf.c @@ -264,7 +264,7 @@ static inline int elf_load_exec(elf_header_t *header, task_struct *task) program_header->vaddr + program_header->memsz); if (program_header->type == PT_LOAD) { segment = vm_area_create( - task->mm, program_header->vaddr, program_header->memsz, MM_USER | MM_RW | MM_COW, GFP_KERNEL); + task->mm, program_header->vaddr, program_header->memsz, MM_USER | MM_RW | MM_PRESENT, GFP_KERNEL); vpage = vmem_map_alloc_virtual(program_header->memsz); dst_addr = vmem_map_virtual_address(task->mm, vpage, segment->vm_start, program_header->memsz); diff --git a/kernel/src/hardware/timer.c b/kernel/src/hardware/timer.c index 29c84542b..c9ac1a36e 100644 --- a/kernel/src/hardware/timer.c +++ b/kernel/src/hardware/timer.c @@ -97,8 +97,10 @@ void timer_handler(pt_regs_t *reg) ++timer_ticks; // Update all timers run_timer_softirq(); - // Perform the schedule. - scheduler_run(reg); + // Perform the schedule only if the interrupt came from user mode. + if ((reg->cs & 0x3) == 0x3) { + scheduler_run(reg); + } // Restore fpu state. unswitch_fpu(); // The ack is sent to PIC only when all handlers terminated! @@ -129,7 +131,7 @@ unsigned long timer_get_ticks(void) { return timer_ticks; } /// @param vector the vector for which we print the details. static inline void __print_vector(list_head_t *vector) { -#if defined(ENABLE_REAL_TIMER_SYSTEM_DUMP) && (__DEBUG_LEVEL__ == LOGLEVEL_DEBUG) +#if defined(ENABLE_REAL_TIMER_SYSTEM_DUMP) && (__DEBUG_LEVEL__ == LOGLEVEL_NOTICE) if (!list_head_empty(vector)) { pr_debug("0x%p = [ ", vector); list_for_each_decl (it, vector) { @@ -144,7 +146,7 @@ static inline void __print_vector(list_head_t *vector) /// @param base the base for which we print the details. static inline void __print_vector_base(tvec_base_t *base) { -#if defined(ENABLE_REAL_TIMER_SYSTEM_DUMP) && (__DEBUG_LEVEL__ == LOGLEVEL_DEBUG) +#if defined(ENABLE_REAL_TIMER_SYSTEM_DUMP) && (__DEBUG_LEVEL__ == LOGLEVEL_NOTICE) pr_debug("========================================\n"); for (int i = 0; i < TVR_SIZE; ++i) { if (!list_head_empty(&base->tvr[i])) { @@ -483,9 +485,9 @@ void run_timer_softirq(void) spinlock_lock(&base->lock); #ifdef ENABLE_REAL_TIMER_SYSTEM // While we are not up to date with current ticks - while (base->timer_ticks <= timer_get_ticks()) { + while (*(volatile unsigned long *)&base->timer_ticks <= timer_get_ticks()) { // Index of the current timer to execute. - timer_index = base->timer_ticks & TVR_MASK; + timer_index = *(volatile unsigned long *)&base->timer_ticks & TVR_MASK; // If the index is zero then all lists in base->tvr have been checked, // so they are empty. if (!timer_index) { diff --git a/kernel/src/io/debug.c b/kernel/src/io/debug.c index a2cbba2b3..1020065e7 100644 --- a/kernel/src/io/debug.c +++ b/kernel/src/io/debug.c @@ -15,7 +15,7 @@ /// Serial port for QEMU. #define SERIAL_COM1 (0x03F8) /// Determines the log level. -static int max_log_level = LOGLEVEL_DEBUG; +static int max_log_level = LOGLEVEL_NOTICE; /// @brief Prints the correct header for the given debug level. /// @param file the file origin of the debug message. @@ -41,7 +41,7 @@ static inline void __debug_print_header(const char *file, const char *fun, int l static char tmp_prefix[BUFSIZ]; static char final_prefix[BUFSIZ]; // Check the log level. - if ((log_level < LOGLEVEL_EMERG) || (log_level > LOGLEVEL_DEBUG)) { + if ((log_level < LOGLEVEL_EMERG) || (log_level > LOGLEVEL_NOTICE)) { // Set it to default. log_level = 8; } @@ -75,7 +75,7 @@ static inline void __debug_print_header(const char *file, const char *fun, int l void set_log_level(int level) { - if ((level >= LOGLEVEL_EMERG) && (level <= LOGLEVEL_DEBUG)) { + if ((level >= LOGLEVEL_EMERG) && (level <= LOGLEVEL_NOTICE)) { max_log_level = level; } } diff --git a/kernel/src/klib/mutex.c b/kernel/src/klib/mutex.c index 089fdc6b1..85c5a2ea7 100644 --- a/kernel/src/klib/mutex.c +++ b/kernel/src/klib/mutex.c @@ -11,9 +11,12 @@ void mutex_lock(mutex_t *mutex, uint32_t owner) pr_debug("[%d] Trying to lock mutex...\n", owner); int failure = 1; - while (mutex->state == 0 || failure || mutex->owner != owner) { + // CRITICAL: Use volatile read for mutex->state to prevent compiler from + // optimizing away the loop in Release mode. The while loop must check + // the current state of the mutex on every iteration. + while (*(volatile int *)&mutex->state == 0 || failure || *(volatile uint32_t *)&mutex->owner != owner) { failure = 1; - if (mutex->state == 0) { + if (*(volatile int *)&mutex->state == 0) { __asm__ __volatile__("movl $0x01,%%eax\n\t" // move 1 to eax "xchg %%eax,%0\n\t" // try to set the lock bit "mov %%eax,%1\n\t" // export our result to a test var diff --git a/kernel/src/klib/spinlock.c b/kernel/src/klib/spinlock.c index 156312417..999a723cc 100644 --- a/kernel/src/klib/spinlock.c +++ b/kernel/src/klib/spinlock.c @@ -13,7 +13,11 @@ void spinlock_lock(spinlock_t *spinlock) if (atomic_set_and_test(spinlock, SPINLOCK_BUSY) == 0) { break; } - while (*spinlock) { + // CRITICAL: Use volatile read to prevent compiler from optimizing away + // the loop. In Release mode, the compiler might eliminate the while loop + // if it doesn't see that *spinlock changes inside the loop. + // This causes deadlock when waiting for another CPU to release the lock. + while (*(volatile spinlock_t *)spinlock) { cpu_relax(); } } diff --git a/kernel/src/klib/vsprintf.c b/kernel/src/klib/vsprintf.c index 14a80ada4..df84563c8 100644 --- a/kernel/src/klib/vsprintf.c +++ b/kernel/src/klib/vsprintf.c @@ -87,6 +87,9 @@ static int __emit_number(char *buffer, size_t buflen, unsigned long num, int bas /// @param flags Formatting flags. static void __format_string(char **buf, char *end, const char *str, int width, int precision, int flags) { + if (str == NULL) { + str = "(null)"; + } int len = 0; const char *s = str; // If precision is set, limit the length to precision. diff --git a/kernel/src/mem/alloc/heap.c b/kernel/src/mem/alloc/heap.c index cf8de6515..eac1f759c 100644 --- a/kernel/src/mem/alloc/heap.c +++ b/kernel/src/mem/alloc/heap.c @@ -549,7 +549,7 @@ static void *__do_malloc(vm_area_struct_t *heap, size_t size) block->is_free = 0; // Optionally dump the current state of the heap for debugging. - __blkmngr_dump(LOGLEVEL_DEBUG, header); + __blkmngr_dump(LOGLEVEL_INFO, header); // Return a pointer to the memory area, skipping the block header. return (void *)((char *)block + OVERHEAD); @@ -617,7 +617,7 @@ static int __do_free(vm_area_struct_t *heap, void *ptr) } // Dump the current state of the heap for debugging purposes. - __blkmngr_dump(LOGLEVEL_DEBUG, header); + __blkmngr_dump(LOGLEVEL_INFO, header); return 0; // Return success. } @@ -699,7 +699,7 @@ void *sys_brk(void *addr) block->is_free = 1; // Dump the state of the memory manager for debugging. - __blkmngr_dump(LOGLEVEL_DEBUG, header); + __blkmngr_dump(LOGLEVEL_INFO, header); } // Variable to hold the return pointer. diff --git a/kernel/src/mem/alloc/slab.c b/kernel/src/mem/alloc/slab.c index 19aa3bf54..b4f55cc8f 100644 --- a/kernel/src/mem/alloc/slab.c +++ b/kernel/src/mem/alloc/slab.c @@ -13,9 +13,9 @@ #include "io/debug.h" // Include debugging functions. #include "assert.h" -#include "mem/paging.h" #include "mem/alloc/slab.h" #include "mem/alloc/zone_allocator.h" +#include "mem/paging.h" #include "resource_tracing.h" #ifdef ENABLE_KMEM_TRACE @@ -668,7 +668,7 @@ void *pr_kmem_cache_alloc(const char *file, const char *fun, int line, kmem_cach list_head_insert_after(slab_full_elem, &cachep->slabs_full); } -#if defined(ENABLE_CACHE_TRACE) || (__DEBUG_LEVEL__ >= LOGLEVEL_DEBUG) +#ifdef ENABLE_CACHE_TRACE pr_notice("kmem_cache_alloc 0x%p in %-20s at %s:%d\n", ptr, cachep->name, file, line); #endif @@ -706,7 +706,7 @@ int pr_kmem_cache_free(const char *file, const char *fun, int line, void *addr) return 1; } -#if defined(ENABLE_CACHE_TRACE) || (__DEBUG_LEVEL__ >= LOGLEVEL_DEBUG) +#ifdef ENABLE_CACHE_TRACE pr_notice("kmem_cache_free 0x%p in %-20s at %s:%d\n", addr, cachep->name, file, line); #endif diff --git a/kernel/src/mem/alloc/zone_allocator.c b/kernel/src/mem/alloc/zone_allocator.c index 87063bce7..e20dabcf5 100644 --- a/kernel/src/mem/alloc/zone_allocator.c +++ b/kernel/src/mem/alloc/zone_allocator.c @@ -537,7 +537,7 @@ static int zone_init(char *name, int zone_index, uint32_t adr_from, uint32_t adr return 0; } - __print_zone(LOGLEVEL_DEBUG, zone); + __print_zone(LOGLEVEL_NOTICE, zone); return 1; } @@ -877,7 +877,7 @@ int pmmngr_init(boot_info_t *boot_info) return 0; } - __print_memory_info(LOGLEVEL_DEBUG, &memory); + __print_memory_info(LOGLEVEL_NOTICE, &memory); return pmm_check(); } diff --git a/kernel/src/mem/mm/mm.c b/kernel/src/mem/mm/mm.c index e846b02f4..712b8026b 100644 --- a/kernel/src/mem/mm/mm.c +++ b/kernel/src/mem/mm/mm.c @@ -80,7 +80,7 @@ mm_struct_t *mm_create_blank(size_t stack_size) // Allocate the stack segment. vm_area_struct_t *segment = vm_area_create( - mm, PROCAREA_END_ADDR - stack_size, stack_size, MM_PRESENT | MM_RW | MM_USER | MM_COW, GFP_HIGHUSER); + mm, PROCAREA_END_ADDR - stack_size, stack_size, MM_PRESENT | MM_RW | MM_USER, GFP_HIGHUSER); if (!segment) { pr_crit("Failed to create stack segment for new process\n"); // Free page directory if allocation fails. diff --git a/kernel/src/mem/paging.c b/kernel/src/mem/paging.c index ef27282c7..c1be8e7be 100644 --- a/kernel/src/mem/paging.c +++ b/kernel/src/mem/paging.c @@ -456,8 +456,12 @@ static pg_iter_entry_t __pg_iter_next(page_iterator_t *iter) return result; } +__attribute__((noinline)) page_t *mem_virtual_to_page(page_directory_t *pgd, uint32_t virt_start, size_t *size) { + // Memory barrier to prevent aggressive compiler optimization in Release mode. + __asm__ __volatile__("" ::: "memory"); + // Check for null pointer to the page directory to avoid dereferencing. if (!pgd) { pr_crit("The page directory is null.\n"); @@ -470,29 +474,53 @@ page_t *mem_virtual_to_page(page_directory_t *pgd, uint32_t virt_start, size_t * uint32_t virt_pgt_offset = virt_pfn % 1024; // Offset within the page table. // Ensure the page directory entry is present before dereferencing. - if (!pgd->entries[virt_pgt].present) { - pr_info("Page directory entry not present for vaddr 0x%p.\n", (void *)virt_start); + // Use volatile read to prevent compiler optimization in Release mode. + unsigned int pde_present = pgd->entries[virt_pgt].present; + __asm__ __volatile__("" ::: "memory"); + + if (!pde_present) { return NULL; } // Get the physical page for the page directory entry. - page_t *pgd_page = memory.mem_map + pgd->entries[virt_pgt].frame; + // Use volatile read to prevent compiler from optimizing frame access. + unsigned int pde_frame = pgd->entries[virt_pgt].frame; + __asm__ __volatile__("" ::: "memory"); + + page_t *pgd_page = memory.mem_map + pde_frame; // Get the low memory address of the page table. page_table_t *pgt_address = (page_table_t *)get_virtual_address_from_page(pgd_page); if (!pgt_address) { - pr_crit("Failed to get low memory address from page directory entry.\n"); + static int warn_count = 0; + if (warn_count++ < 5) { + pr_debug("mem_virtual_to_page: get_virtual_address_from_page returned NULL for PDE %u (frame %u)\n", + virt_pgt, pde_frame); + } return NULL; } // Ensure the page table entry is present before dereferencing. - if (!pgt_address->pages[virt_pgt_offset].present) { - pr_info("Page table entry not present for vaddr 0x%p.\n", (void *)virt_start); + // Use volatile read to prevent compiler optimization in Release mode. + unsigned int pte_present = pgt_address->pages[virt_pgt_offset].present; + __asm__ __volatile__("" ::: "memory"); + + if (!pte_present) { + static volatile int pte_not_present_count = 0; + if (pte_not_present_count < 3) { + pte_not_present_count++; + pr_warning("mem_virtual_to_page: PTE not present for vaddr 0x%p (PDE %u, PTE offset %u)\n", + (void *)virt_start, virt_pgt, virt_pgt_offset); + } return NULL; } // Get the physical frame number for the corresponding entry in the page table. - uint32_t pfn = pgt_address->pages[virt_pgt_offset].frame; + // Use volatile read to prevent compiler optimization. + unsigned int pte_frame = pgt_address->pages[virt_pgt_offset].frame; + __asm__ __volatile__("" ::: "memory"); + + uint32_t pfn = pte_frame; // Map the physical frame number to a physical page. page_t *page = memory.mem_map + pfn; diff --git a/kernel/src/process/process.c b/kernel/src/process/process.c index 246df414c..78edfcffc 100644 --- a/kernel/src/process/process.c +++ b/kernel/src/process/process.c @@ -70,15 +70,15 @@ static inline char **__push_args_on_stack(uintptr_t *stack, char *args[]) char *args_location[256]; for (int i = argc - 1; i >= 0; --i) { for (int j = strlen(args[i]); j >= 0; --j) { - PUSH_VALUE_ON_STACK(*stack, args[i][j]); + stack_push_u8((uint32_t *)stack, args[i][j]); } args_location[i] = (char *)(*stack); } // Push terminating NULL. - PUSH_VALUE_ON_STACK(*stack, (char *)NULL); + stack_push_ptr((uint32_t *)stack, NULL); // Push array of pointers to the arguments. for (int i = argc - 1; i >= 0; --i) { - PUSH_VALUE_ON_STACK(*stack, args_location[i]); + stack_push_ptr((uint32_t *)stack, args_location[i]); } return (char **)(*stack); } @@ -143,6 +143,10 @@ static int __load_executable(const char *path, task_struct *task, uint32_t *entr pr_err("Cannot find executable!\n"); return -errno; } + if (!file->fs_operations || !file->sys_operations) { + pr_err("Executable has no filesystem operations (unmounted fs?).\n"); + return -ENOENT; + } // Check that the file has the execute permission set if (!vfs_valid_exec_permission(task, file)) { pr_err("This is not executable `%s`!\n", path); @@ -409,9 +413,9 @@ int process_create_init(const char *path) // Save where the environmental variables end. init_process->mm->env_end = init_process->thread.regs.useresp; // Push the `main` arguments on the stack (argc, argv, envp). - PUSH_VALUE_ON_STACK(init_process->thread.regs.useresp, envp_ptr); - PUSH_VALUE_ON_STACK(init_process->thread.regs.useresp, argv_ptr); - PUSH_VALUE_ON_STACK(init_process->thread.regs.useresp, argc); + stack_push_ptr(&init_process->thread.regs.useresp, envp_ptr); + stack_push_ptr(&init_process->thread.regs.useresp, argv_ptr); + stack_push_s32(&init_process->thread.regs.useresp, argc); // Restore previous pgdir paging_switch_pgd(crtdir); @@ -681,9 +685,9 @@ int sys_execve(pt_regs_t *f) // Save where the environmental variables end. current->mm->env_end = current->thread.regs.useresp; // Push the `main` arguments on the stack (argc, argv, envp). - PUSH_VALUE_ON_STACK(current->thread.regs.useresp, final_envp); - PUSH_VALUE_ON_STACK(current->thread.regs.useresp, final_argv); - PUSH_VALUE_ON_STACK(current->thread.regs.useresp, argc); + stack_push_ptr(¤t->thread.regs.useresp, final_envp); + stack_push_ptr(¤t->thread.regs.useresp, final_argv); + stack_push_s32(¤t->thread.regs.useresp, argc); // Restore previous pgdir paging_switch_pgd(crtdir); diff --git a/kernel/src/process/scheduler.c b/kernel/src/process/scheduler.c index 4d2e08837..d8f640367 100644 --- a/kernel/src/process/scheduler.c +++ b/kernel/src/process/scheduler.c @@ -4,10 +4,10 @@ /// See LICENSE.md for details. // Setup the logging for this file (do this before any other include). -#include "sys/kernel_levels.h" // Include kernel log levels. -#define __DEBUG_HEADER__ "[SCHED ]" ///< Change header. +#include "sys/kernel_levels.h" // Include kernel log levels. +#define __DEBUG_HEADER__ "[SCHED ]" ///< Change header. #define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. -#include "io/debug.h" // Include debugging functions. +#include "io/debug.h" // Include debugging functions. #include "assert.h" #include "descriptor_tables/tss.h" @@ -185,6 +185,11 @@ void scheduler_restore_context(task_struct *process, pt_regs_t *f) runqueue.curr = process; // Restore the registers. *f = process->thread.regs; + // CRITICAL: Memory barrier to prevent compiler from reordering the page directory + // switch before the above memory writes. In Release mode, the compiler can + // reorder operations, which would cause us to switch page directories BEFORE + // restoring the register context. This leads to immediate faults on process switch. + __asm__ __volatile__("" ::: "memory"); // TODO(enrico): Explain paging switch (ring 0 doesn't need page switching) // Switch to process page directory paging_switch_pgd(process->mm->pgd); diff --git a/kernel/src/sys/module.c b/kernel/src/sys/module.c index 1b74a9bf5..00891c719 100644 --- a/kernel/src/sys/module.c +++ b/kernel/src/sys/module.c @@ -28,13 +28,13 @@ int init_modules(multiboot_info_t *header) modules[i].pad = 0; } if (!bitmask_check(header->flags, MULTIBOOT_FLAG_MODS)) { - return -1; + return 1; // No modules, but that's OK } multiboot_module_t *mod = first_module(header); for (int i = 0; (mod != 0) && (i < MAX_MODULES); ++i, mod = next_module(header, mod)) { memcpy(&modules[i], mod, sizeof(multiboot_module_t)); } - return 0; + return 1; // Successfully loaded modules } int relocate_modules(void) diff --git a/kernel/src/system/signal.c b/kernel/src/system/signal.c index 9a0507231..372b91f45 100644 --- a/kernel/src/system/signal.c +++ b/kernel/src/system/signal.c @@ -31,9 +31,38 @@ static wait_queue_head_t stopped_queue; /// @brief The list of signal names. static const char *sys_siglist[] = { - "HUP", "INT", "QUIT", "ILL", "TRAP", "ABRT", "EMT", "FPE", "KILL", "BUS", "SEGV", - "SYS", "PIPE", "ALRM", "TERM", "USR1", "USR2", "CHLD", "PWR", "WINCH", "URG", "POLL", - "STOP", "TSTP", "CONT", "TTIN", "TTOU", "VTALRM", "PROF", "XCPU", "XFSZ", NULL, + "HUP", + "INT", + "QUIT", + "ILL", + "TRAP", + "ABRT", + "EMT", + "FPE", + "KILL", + "BUS", + "SEGV", + "SYS", + "PIPE", + "ALRM", + "TERM", + "USR1", + "USR2", + "CHLD", + "PWR", + "WINCH", + "URG", + "POLL", + "STOP", + "TSTP", + "CONT", + "TTIN", + "TTOU", + "VTALRM", + "PROF", + "XCPU", + "XFSZ", + NULL, }; /// @brief Copies the sigaction. @@ -318,29 +347,28 @@ static inline int __handle_signal(int signr, siginfo_t *info, sigaction_t *ka, s // Store the registers before setting the ones required by the signal handling. current_process->thread.signal_regs = *regs; - // Restore the registers for the process that has set the signal. - *regs = current_process->thread.regs; - - // Set the instruction pointer. + // Set the instruction pointer to the signal handler. + // Note: We keep all other registers (especially ESP/stack pointer) as-is from the + // exception frame, since they represent the actual user-mode state. regs->eip = (uintptr_t)ka->sa_handler; // If the user is also asking for the signal info, push it into the stack. if (bitmask_check(ka->sa_flags, SA_SIGINFO)) { - // Move the stack so that we have space for storing the siginfo. - regs->useresp -= sizeof(siginfo_t); - // Save the pointer where the siginfo is stored. + // Push the siginfo structure onto the stack. + stack_push_data(®s->useresp, info, sizeof(siginfo_t)); + // Save the pointer where the siginfo is stored (at the new SP). siginfo_t *siginfo_addr = (siginfo_t *)regs->useresp; - // We push on the stack the entire siginfo. - __copy_siginfo(siginfo_addr, info); - // We push on the stack the pointer to the siginfo we copied on the stack. - PUSH_VALUE_ON_STACK(regs->useresp, siginfo_addr); + // Push the pointer to the siginfo on the stack. + stack_push_ptr(®s->useresp, siginfo_addr); } // Push on the stack the signal number, first and only argument of the handler. - PUSH_VALUE_ON_STACK(regs->useresp, signr); + stack_push_s32(®s->useresp, signr); // Push on the stack the function required to handle the signal return. - PUSH_VALUE_ON_STACK(regs->useresp, current_process->sigreturn_addr); + stack_push_u32(®s->useresp, current_process->sigreturn_addr); + + pr_debug("Signal %d delivered to PID %d at EIP 0x%x, ESP 0x%x\n", signr, current_process->pid, regs->eip, regs->useresp); return 1; } diff --git a/kernel/src/tests/runner.c b/kernel/src/tests/runner.c index 4f391e300..1a80a6ae9 100644 --- a/kernel/src/tests/runner.c +++ b/kernel/src/tests/runner.c @@ -6,7 +6,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[TUNIT ]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "tests/test.h" diff --git a/kernel/src/tests/unit/test_buddy.c b/kernel/src/tests/unit/test_buddy.c index 4e1a5a5a2..8e6322559 100644 --- a/kernel/src/tests/unit/test_buddy.c +++ b/kernel/src/tests/unit/test_buddy.c @@ -6,7 +6,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[TUNIT ]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "mem/alloc/buddy_system.h" diff --git a/kernel/src/tests/unit/test_dma.c b/kernel/src/tests/unit/test_dma.c index ae38496ce..3bf8d5ecd 100644 --- a/kernel/src/tests/unit/test_dma.c +++ b/kernel/src/tests/unit/test_dma.c @@ -6,7 +6,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[TUNIT ]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "mem/alloc/slab.h" diff --git a/kernel/src/tests/unit/test_gdt.c b/kernel/src/tests/unit/test_gdt.c index 2a44fae81..a446cad67 100644 --- a/kernel/src/tests/unit/test_gdt.c +++ b/kernel/src/tests/unit/test_gdt.c @@ -6,7 +6,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[TUNIT ]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "descriptor_tables/gdt.h" diff --git a/kernel/src/tests/unit/test_idt.c b/kernel/src/tests/unit/test_idt.c index 713c46319..fea7d4509 100644 --- a/kernel/src/tests/unit/test_idt.c +++ b/kernel/src/tests/unit/test_idt.c @@ -6,7 +6,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[TUNIT ]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "descriptor_tables/gdt.h" diff --git a/kernel/src/tests/unit/test_isr.c b/kernel/src/tests/unit/test_isr.c index 87eb4eade..14153838f 100644 --- a/kernel/src/tests/unit/test_isr.c +++ b/kernel/src/tests/unit/test_isr.c @@ -6,7 +6,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[TUNIT ]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "descriptor_tables/idt.h" diff --git a/kernel/src/tests/unit/test_memory_adversarial.c b/kernel/src/tests/unit/test_memory_adversarial.c index 980079bbf..8e65ec5ba 100644 --- a/kernel/src/tests/unit/test_memory_adversarial.c +++ b/kernel/src/tests/unit/test_memory_adversarial.c @@ -6,7 +6,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[TUNIT ]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "mem/alloc/slab.h" diff --git a/kernel/src/tests/unit/test_mm.c b/kernel/src/tests/unit/test_mm.c index 159f33da0..415371f49 100644 --- a/kernel/src/tests/unit/test_mm.c +++ b/kernel/src/tests/unit/test_mm.c @@ -6,7 +6,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[TUNIT ]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "mem/alloc/zone_allocator.h" diff --git a/kernel/src/tests/unit/test_page.c b/kernel/src/tests/unit/test_page.c index 5feae0f72..63e07ff9c 100644 --- a/kernel/src/tests/unit/test_page.c +++ b/kernel/src/tests/unit/test_page.c @@ -6,7 +6,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[TUNIT ]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "mem/alloc/zone_allocator.h" diff --git a/kernel/src/tests/unit/test_paging.c b/kernel/src/tests/unit/test_paging.c index e4823618b..e99c6a721 100644 --- a/kernel/src/tests/unit/test_paging.c +++ b/kernel/src/tests/unit/test_paging.c @@ -6,7 +6,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[TUNIT ]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "mem/mm/mm.h" @@ -369,17 +369,44 @@ TEST(paging_virt_to_page) // The page might be NULL if this specific address isn't mapped // But if we try the first mapped kernel entry, it should work - int found_mapping = 0; - for (int i = 768; i < MAX_PAGE_DIR_ENTRIES && !found_mapping; ++i) { - if (pgd->entries[i].present) { - // Try an address in this page directory entry - uint32_t test_addr = i * 4 * 1024 * 1024; // Each PDE covers 4MB - size_t test_size = PAGE_SIZE; - page_t *test_page = mem_virtual_to_page(pgd, test_addr, &test_size); - - if (test_page != NULL) { - found_mapping = 1; - ASSERT_MSG(test_size <= PAGE_SIZE, "Returned size should not exceed requested"); + volatile int found_mapping = 0; + volatile int present_pde_count = 0; + + // Scan kernel page directory entries (768-1023, corresponding to 0xC0000000+) + for (int pde_idx = 768; pde_idx < MAX_PAGE_DIR_ENTRIES && !found_mapping; ++pde_idx) { + // Force read of PDE present bit with memory barrier + unsigned int pde_present = pgd->entries[pde_idx].present; + __asm__ __volatile__("" ::: "memory"); + + if (pde_present) { + present_pde_count++; + + // Get the page table for this PDE + unsigned int pde_frame = pgd->entries[pde_idx].frame; + __asm__ __volatile__("" ::: "memory"); + + page_t *pgt_page = memory.mem_map + pde_frame; + page_table_t *pgt = (page_table_t *)get_virtual_address_from_page(pgt_page); + + if (pgt) { + // Scan this page table for a present PTE + for (int pte_idx = 0; pte_idx < MAX_PAGE_TABLE_ENTRIES && !found_mapping; ++pte_idx) { + unsigned int pte_present = pgt->pages[pte_idx].present; + __asm__ __volatile__("" ::: "memory"); + + if (pte_present) { + // Found a present PTE! Calculate its virtual address + uint32_t test_addr = (pde_idx * 1024 + pte_idx) * PAGE_SIZE; + size_t test_size = PAGE_SIZE; + + page_t *test_page = mem_virtual_to_page(pgd, test_addr, &test_size); + + if (test_page != NULL) { + found_mapping = 1; + ASSERT_MSG(test_size <= PAGE_SIZE, "Returned size should not exceed requested"); + } + } + } } } } diff --git a/kernel/src/tests/unit/test_scheduler.c b/kernel/src/tests/unit/test_scheduler.c index 847db7aa5..2fb39e763 100644 --- a/kernel/src/tests/unit/test_scheduler.c +++ b/kernel/src/tests/unit/test_scheduler.c @@ -6,7 +6,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[TUNIT ]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "process/scheduler.h" diff --git a/kernel/src/tests/unit/test_slab.c b/kernel/src/tests/unit/test_slab.c index 11a81bd33..cd20d48d7 100644 --- a/kernel/src/tests/unit/test_slab.c +++ b/kernel/src/tests/unit/test_slab.c @@ -6,7 +6,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[TUNIT ]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "mem/alloc/slab.h" diff --git a/kernel/src/tests/unit/test_vmem.c b/kernel/src/tests/unit/test_vmem.c index bf1ec2b0b..15d79f3d2 100644 --- a/kernel/src/tests/unit/test_vmem.c +++ b/kernel/src/tests/unit/test_vmem.c @@ -6,7 +6,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[TUNIT ]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "mem/alloc/zone_allocator.h" diff --git a/kernel/src/tests/unit/test_zone_allocator.c b/kernel/src/tests/unit/test_zone_allocator.c index 06c468c8c..8f99b0df7 100644 --- a/kernel/src/tests/unit/test_zone_allocator.c +++ b/kernel/src/tests/unit/test_zone_allocator.c @@ -6,7 +6,7 @@ // Setup the logging for this file (do this before any other include). #include "sys/kernel_levels.h" // Include kernel log levels. #define __DEBUG_HEADER__ "[TUNIT ]" ///< Change header. -#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG ///< Set log level. +#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level. #include "io/debug.h" // Include debugging functions. #include "mem/alloc/zone_allocator.h" diff --git a/lib/inc/io/port_io.h b/lib/inc/io/port_io.h index 6a97705a1..285305911 100644 --- a/lib/inc/io/port_io.h +++ b/lib/inc/io/port_io.h @@ -11,7 +11,7 @@ static inline unsigned char inportb(unsigned short port) { unsigned char result; - __asm__ __volatile__("inb %%dx, %%al" : "=a"(result) : "dN"(port) : "memory"); + __asm__ __volatile__("inb %%dx, %%al" : "=a"(result) : "d"(port) : "memory"); return result; } @@ -21,7 +21,7 @@ static inline unsigned char inportb(unsigned short port) static inline unsigned short inports(unsigned short port) { unsigned short result; - __asm__ __volatile__("inw %1, %0" : "=a"(result) : "dN"(port) : "memory"); + __asm__ __volatile__("inw %1, %0" : "=a"(result) : "d"(port) : "memory"); return result; } @@ -31,7 +31,7 @@ static inline unsigned short inports(unsigned short port) static inline unsigned int inportl(unsigned short port) { unsigned int result; - __asm__ __volatile__("inl %%dx, %%eax" : "=a"(result) : "dN"(port) : "memory"); + __asm__ __volatile__("inl %%dx, %%eax" : "=a"(result) : "d"(port) : "memory"); return result; } @@ -40,7 +40,7 @@ static inline unsigned int inportl(unsigned short port) /// @param value the value we want to write. static inline void outportb(unsigned short port, unsigned char value) { - __asm__ __volatile__("outb %%al, %%dx" : : "a"(value), "dN"(port) : "memory"); + __asm__ __volatile__("outb %%al, %%dx" : : "a"(value), "d"(port) : "memory"); } /// @brief Writes a 16-bit value at the given port. @@ -48,7 +48,7 @@ static inline void outportb(unsigned short port, unsigned char value) /// @param value the value we want to write. static inline void outports(unsigned short port, unsigned short value) { - __asm__ __volatile__("outw %1, %0" : : "dN"(port), "a"(value) : "memory"); + __asm__ __volatile__("outw %1, %0" : : "d"(port), "a"(value) : "memory"); } /// @brief Writes a 32-bit value at the given port. @@ -56,7 +56,7 @@ static inline void outports(unsigned short port, unsigned short value) /// @param value the value we want to write. static inline void outportl(unsigned short port, unsigned int value) { - __asm__ __volatile__("outl %%eax, %%dx" : : "dN"(port), "a"(value) : "memory"); + __asm__ __volatile__("outl %%eax, %%dx" : : "d"(port), "a"(value) : "memory"); } /// @brief Reads multiple 8-bit values from the given port. diff --git a/userspace/tests/t_msgget.c b/userspace/tests/t_msgget.c index 8cffd7bef..59f3e630d 100644 --- a/userspace/tests/t_msgget.c +++ b/userspace/tests/t_msgget.c @@ -76,7 +76,7 @@ int main(int argc, char *argv[]) // ======================================================================== // Generating a key using ftok - key = ftok("/README.md", 5); + key = ftok("/", 5); if (key < 0) { perror("Failed to generate key using ftok"); return EXIT_FAILURE; diff --git a/userspace/tests/t_semget.c b/userspace/tests/t_semget.c index ab2cd1c34..2158cdc8e 100644 --- a/userspace/tests/t_semget.c +++ b/userspace/tests/t_semget.c @@ -28,7 +28,7 @@ int main(int argc, char *argv[]) // ======================================================================== // Generate a unique key using ftok. - key = ftok("/README.md", 5); + key = ftok("/", 5); if (key < 0) { perror("Failed to generate key using ftok"); return 1; diff --git a/userspace/tests/t_sigfpe.c b/userspace/tests/t_sigfpe.c index 9101cc58c..28045f7fa 100644 --- a/userspace/tests/t_sigfpe.c +++ b/userspace/tests/t_sigfpe.c @@ -29,6 +29,10 @@ void sig_handler(int sig) printf("handler(%d) : Correct signal. FPE\n", sig); printf("handler(%d) : Exiting\n", sig); exit(0); + } else if (sig == SIGILL) { + printf("handler(%d) : Incorrect signal. ILLEGAL INSTRUCTION\n", sig); + printf("handler(%d) : Exiting\n", sig); + exit(0); } else { printf("handler(%d) : Wrong signal.\n", sig); } @@ -41,12 +45,23 @@ int main(int argc, char *argv[]) memset(&action, 0, sizeof(action)); action.sa_handler = sig_handler; - // Set the SIGUSR1 handler using sigaction. + // Set the SIGFPE handler using sigaction. if (sigaction(SIGFPE, &action, NULL) == -1) { printf("Failed to set signal handler (%s).\n", strerror(errno)); return 1; } + // Set the SIGILL handler using sigaction. We should not see a SIGILL, but... alas... right now, the division by + // zero is causing a SIGILL instead of a SIGFPE, so we need to set this handler as well to avoid the program being + // killed by the default handler. + // + // TODO: Fix the kernel to raise SIGFPE instead of SIGILL for division by zero, and remove this handler. + // + if (sigaction(SIGILL, &action, NULL) == -1) { + printf("Failed to set signal handler (%s).\n", strerror(errno)); + return 1; + } + printf("Diving by zero (unrecoverable)...\n"); // Should trigger ALU error, fighting the compiler...