From e2564d112ba03b432f9f44f445cbc4a3ed85751a Mon Sep 17 00:00:00 2001 From: Richard Qin Date: Fri, 17 Apr 2026 01:05:31 +0800 Subject: feat: add initial RISC-V architecture support and core kernel functionality - Implement MMU hardware primitives for RISC-V SV39 in mmu.h - Create trap vector control primitives in trap.h - Define trap frame structure for RISC-V in trapframe.h - Add assembly routines for context switching in swtch.S - Implement trap vector entry/exit in trap_vector.S - Introduce basic error number definitions in errno.h - Provide placement new/delete operators in new.h - Define kernel panic handler in panic.h and implement in panic.cc - Establish basic type definitions in types.h - Create the kernel main entry point in main.cc with global initialization - Add synchronization primitives using spinlocks in spinlock.h and spinlock.cc - Define per-CPU state tracking for interrupts in cpu_state.h - Create a linker script for the kernel in linker.ld - Update module makefiles for kernel and library components --- na/kernel/sync/cpu_state.h | 34 ++++++++++++++ na/kernel/sync/spinlock.cc | 111 +++++++++++++++++++++++++++++++++++++++++++++ na/kernel/sync/spinlock.h | 60 ++++++++++++++++++++++++ 3 files changed, 205 insertions(+) create mode 100644 na/kernel/sync/cpu_state.h create mode 100644 na/kernel/sync/spinlock.cc create mode 100644 na/kernel/sync/spinlock.h (limited to 'na/kernel/sync') diff --git a/na/kernel/sync/cpu_state.h b/na/kernel/sync/cpu_state.h new file mode 100644 index 0000000..d0e3814 --- /dev/null +++ b/na/kernel/sync/cpu_state.h @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* Copyright (C) 2026 Richard Qin */ + +#pragma once + +/* + * Per-CPU Interrupt Nesting Tracker + * + * Tracks spinlock nesting depth on each CPU to ensure interrupts + * are only restored when the last spinlock is released. + * + * BSS-segment global array; zero-initialized by entry.S BSS clear. + * Reference: docs/specs/spinlock.md §3.1 + */ + +#include + +namespace sync { + +struct CpuSyncState { + int lock_depth; // Number of spinlocks held by this CPU + bool intr_was_on; // Were interrupts enabled before the first lock? +}; + +inline constexpr uint64 kInvalidCpuId = ~0ULL; + +// BSS-segment global array, zero-initialized by entry.S +extern CpuSyncState g_cpu_sync_states[kMaxCPUs]; + +// Internal API — called only by Spinlock::acquire/release +void push_intr(); +void pop_intr(); + +} // namespace sync diff --git a/na/kernel/sync/spinlock.cc b/na/kernel/sync/spinlock.cc new file mode 100644 index 0000000..a03130d --- /dev/null +++ b/na/kernel/sync/spinlock.cc @@ -0,0 +1,111 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* Copyright (C) 2026 Richard Qin */ + +/* + * Spinlock Implementation + * + * TTAS (Test-and-Test-and-Set) spinlock with: + * - Acquire: __ATOMIC_ACQUIRE memory ordering + * - Release: __ATOMIC_RELEASE memory ordering + * - Automatic interrupt disable/enable via push_intr/pop_intr + * - Dead-lock and invalid-release detection via kernel_panic + * + * Reference: docs/specs/spinlock.md + */ + +#include "spinlock.h" +#include "cpu_state.h" +#include +#include + +// ============================================================ +// Per-CPU sync state (BSS zero-initialized) +// ============================================================ +namespace sync { + +CpuSyncState g_cpu_sync_states[kMaxCPUs]; + +void push_intr() { + bool was_on = arch::cpu::intr_enabled(); + arch::cpu::intr_off(); + + auto& state = g_cpu_sync_states[arch::cpu::id()]; + if (state.lock_depth == 0) { + state.intr_was_on = was_on; + } + state.lock_depth++; +} + +void pop_intr() { + auto& state = g_cpu_sync_states[arch::cpu::id()]; + state.lock_depth--; + + if (state.lock_depth == 0 && state.intr_was_on) { + arch::cpu::intr_on(); + } +} + +} // namespace sync + +// ============================================================ +// Spinlock methods +// ============================================================ + +void Spinlock::init(const char* name) { + name_ = name; + locked_ = 0; + cpu_id_ = sync::kInvalidCpuId; +} + +void Spinlock::acquire() { +#ifdef DEBUG + // Optional: detect interrupt-state leakage + if (arch::cpu::intr_enabled()) { + auto& state = sync::g_cpu_sync_states[arch::cpu::id()]; + if (state.lock_depth > 0) { + kernel_panic("spinlock: interrupts enabled while holding lock"); + } + } +#endif + + // Step 1: disable interrupts, track nesting depth + sync::push_intr(); + + // Step 2: dead-lock detection (same CPU re-acquiring) + if (is_held_by_current_cpu()) { + kernel_panic("spinlock: double lock", name_); + } + + // Step 3: TTAS spin loop + while (true) { + // Outer: spin on local cache copy (no bus traffic) + while (__atomic_load_n(&locked_, __ATOMIC_RELAXED)) + ; + // Inner: attempt atomic exchange with acquire semantics + if (__atomic_exchange_n(&locked_, 1, __ATOMIC_ACQUIRE) == 0) + break; + } + + // Step 4: record owner + cpu_id_ = arch::cpu::id(); +} + +void Spinlock::release() { + // Step 1: verify caller is the holder + if (!is_held_by_current_cpu()) { + kernel_panic("spinlock: invalid release", name_); + } + + // Step 2: clear owner + cpu_id_ = sync::kInvalidCpuId; + + // Step 3: release the lock with release semantics + __atomic_store_n(&locked_, 0, __ATOMIC_RELEASE); + + // Step 4: restore interrupt state if this was the last lock + sync::pop_intr(); +} + +bool Spinlock::is_held_by_current_cpu() const { + return cpu_id_ == arch::cpu::id(); +} diff --git a/na/kernel/sync/spinlock.h b/na/kernel/sync/spinlock.h new file mode 100644 index 0000000..b3e3393 --- /dev/null +++ b/na/kernel/sync/spinlock.h @@ -0,0 +1,60 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* Copyright (C) 2026 Richard Qin */ + +#pragma once + +/* + * Spinlock & LockGuard + * + * Provides mutual exclusion for SMP kernels using TTAS + * (Test-and-Test-and-Set) with acquire/release memory ordering. + * Interrupt disable/enable is managed automatically via push_intr/pop_intr. + * + * Reference: docs/specs/spinlock.md + */ + +#include +#include "cpu_state.h" + +class Spinlock { +public: + // Default: BSS zero is safe. Call init() to give the lock a name. + Spinlock() = default; + + // Explicit initialization (for global objects or Placement New) + void init(const char* name); + + // Non-copyable, non-movable (lock identity is its address) + Spinlock(const Spinlock&) = delete; + Spinlock& operator=(const Spinlock&) = delete; + Spinlock(Spinlock&&) = delete; + Spinlock& operator=(Spinlock&&) = delete; + + void acquire(); + void release(); + bool is_held_by_current_cpu() const; + +private: + uint32 locked_ = 0; // 0 = free, 1 = held + const char* name_ = "uninit"; // Lock name (for panic messages) + uint64 cpu_id_ = sync::kInvalidCpuId; // Holder CPU ID +}; + +class LockGuard { +public: + explicit LockGuard(Spinlock& lock) : lock_(lock) { + lock_.acquire(); + } + + ~LockGuard() { + lock_.release(); + } + + LockGuard(const LockGuard&) = delete; + LockGuard& operator=(const LockGuard&) = delete; + LockGuard(LockGuard&&) = delete; + LockGuard& operator=(LockGuard&&) = delete; + +private: + Spinlock& lock_; +}; -- cgit v1.2.3