diff options
| author | Richard Qin <richard.qin.x@gmail.com> | 2026-04-17 01:05:31 +0800 |
|---|---|---|
| committer | Richard Qin <richard.qin.x@gmail.com> | 2026-04-17 01:05:31 +0800 |
| commit | e2564d112ba03b432f9f44f445cbc4a3ed85751a (patch) | |
| tree | 1541ee010bfd9c0e614fc91c2de5c9aa741418ae /na/kernel | |
| parent | 81563cb415d8214f1d1e34f73d49a404424f8f2c (diff) | |
| download | lume-e2564d112ba03b432f9f44f445cbc4a3ed85751a.tar.gz lume-e2564d112ba03b432f9f44f445cbc4a3ed85751a.tar.xz | |
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
Diffstat (limited to 'na/kernel')
| -rw-r--r-- | na/kernel/main.cc | 146 | ||||
| -rw-r--r-- | na/kernel/module.mk | 3 | ||||
| -rw-r--r-- | na/kernel/sync/cpu_state.h | 34 | ||||
| -rw-r--r-- | na/kernel/sync/spinlock.cc | 111 | ||||
| -rw-r--r-- | na/kernel/sync/spinlock.h | 60 |
5 files changed, 354 insertions, 0 deletions
diff --git a/na/kernel/main.cc b/na/kernel/main.cc new file mode 100644 index 0000000..b4d792e --- /dev/null +++ b/na/kernel/main.cc @@ -0,0 +1,146 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* Copyright (C) 2026 Richard Qin */ + +/* + * LumeOS Kernel Main Entry — Global Initialization Sequence + * + * All CPUs enter kernel_main() after the assembly bootstrap (entry.S) + * has enabled MMU, cleared BSS, and called early_cpp_init(). + * + * BSP (cpu_id == 0) executes the full subsystem initialization DAG. + * APs spin on ap_boot_sync, then perform per-CPU init only. + * + * Reference: docs/specs/global_init.md + */ + +#include <lume/types.h> +#include <lume/panic.h> +#include <arch/cpu.h> + +// ============================================================ +// Forward declarations for subsystem init functions. +// Each is implemented by its owning module. On failure they +// call kernel_panic() directly — no error return. +// ============================================================ + +// BSP global init (strict order) +void console_init(); // 1. UART ready; kprintf/panic can output +void fdt_init(uint64 fdt_paddr); // 2. Parse FDT, build device node table +void pmm_init(); // 3. Buddy allocator ready +void slab_init(); // 4. kmem_cache ready; operator new usable +void vmm_init(); // 5. Fine-grained kernel page table; retire early_pgdir +void trap_init(); // 6. Set stvec, register IRQ dispatcher +void plic_init(); // 7. Configure PLIC (threshold, priority, enable) +void timer_init(); // 8. Configure timer interrupt (scheduler tick) +void sched_init(); // 9. Create BSP Idle Task, init ready queues + +// AP per-CPU init +void vmm_init_ap(); // Switch satp to BSP-built kernel page table +void trap_init_ap(); // Set this CPU's stvec +void plic_init_ap(); // Enable this CPU's PLIC context +void timer_init_ap(); // Start this CPU's timer interrupt +void sched_init_ap(); // Init per-CPU ready queue, create Idle Task + +// ============================================================ +// Second-stage synchronization barrier (C++ layer) +// AP cores spin here until BSP finishes all global init. +// ============================================================ +static volatile uint32 ap_boot_sync = 0; + +// Early UART output functions, defined in lib/panic.cc +// They are safe to use before console_init(). +extern "C" void early_putc(char c); +extern "C" void early_puts(const char* s); + +// ============================================================ +// kernel_main — C++ entry point for all CPUs +// ============================================================ +extern "C" [[noreturn]] void kernel_main(uint64 cpu_id, uint64 fdt_paddr) { + + if (cpu_id == 0) { + // ======================================================== + // BSP: Absolute single-core period — no locks needed. + // APs are spinning on ap_boot_sync. + // ======================================================== + + early_puts("\n\n"); + early_puts("====================================\n"); + early_puts(" LumeOS Kernel v0.1 (RISC-V 64)\n"); + early_puts("====================================\n"); + early_puts("[boot] BSP online, cpu_id=0\n"); + early_puts("[boot] Higher-half kernel active\n"); + + // --- Subsystem init DAG (strict top-down order) --- + + // 1. UART driver — after this, kernel_panic() can output + // console_init(); + early_puts("[init] console_init (stub)\n"); + + // 2. FDT parsing + // fdt_init(fdt_paddr); + (void)fdt_paddr; + early_puts("[init] fdt_init (stub)\n"); + + // 3. Physical memory manager (Buddy) + // pmm_init(); + early_puts("[init] pmm_init (stub)\n"); + + // 4. Slab allocator — operator new usable after this + // slab_init(); + early_puts("[init] slab_init (stub)\n"); + + // 5. Virtual memory manager — retire early_pgdir + // vmm_init(); + early_puts("[init] vmm_init (stub)\n"); + + // 6. Trap handler — set stvec + // trap_init(); + early_puts("[init] trap_init (stub)\n"); + + // 7. PLIC interrupt controller + // plic_init(); + early_puts("[init] plic_init (stub)\n"); + + // 8. Timer interrupt (scheduler tick source) + // timer_init(); + early_puts("[init] timer_init (stub)\n"); + + // 9. Scheduler — create Idle Task 0, init ready queues + // sched_init(); + early_puts("[init] sched_init (stub)\n"); + + early_puts("[boot] BSP init complete, releasing APs\n"); + + // Release APs with RELEASE semantics: + // guarantees all global data is visible to APs. + __atomic_store_n(&ap_boot_sync, 1, __ATOMIC_RELEASE); + + // BSP enables interrupts — first timer tick will preempt + // into the scheduler loop. + // arch::cpu::intr_on(); + + early_puts("[boot] BSP entering idle loop\n"); + + } else { + // ======================================================== + // AP: spin until BSP finishes global init + // ======================================================== + while (__atomic_load_n(&ap_boot_sync, __ATOMIC_ACQUIRE) == 0) + ; + + // --- Per-CPU init only (no global data mutation) --- + // vmm_init_ap(); + // trap_init_ap(); + // plic_init_ap(); + // timer_init_ap(); + // sched_init_ap(); + + // AP enables interrupts — becomes Idle Task on this CPU + // arch::cpu::intr_on(); + } + + // All CPUs: idle loop (wfi until interrupt) + while (true) { + arch::cpu::halt_until_interrupt(); + } +} diff --git a/na/kernel/module.mk b/na/kernel/module.mk new file mode 100644 index 0000000..208064f --- /dev/null +++ b/na/kernel/module.mk @@ -0,0 +1,3 @@ +# kernel/module.mk +SRCS_CC += kernel/main.cc +SRCS_CC += kernel/sync/spinlock.cc 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 <lume/types.h> + +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 <arch/cpu.h> +#include <lume/panic.h> + +// ============================================================ +// 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 <lume/types.h> +#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_; +}; |
