summaryrefslogtreecommitdiff
path: root/na/kernel
diff options
context:
space:
mode:
authorRichard Qin <richard.qin.x@gmail.com>2026-04-17 01:05:31 +0800
committerRichard Qin <richard.qin.x@gmail.com>2026-04-17 01:05:31 +0800
commite2564d112ba03b432f9f44f445cbc4a3ed85751a (patch)
tree1541ee010bfd9c0e614fc91c2de5c9aa741418ae /na/kernel
parent81563cb415d8214f1d1e34f73d49a404424f8f2c (diff)
downloadlume-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.cc146
-rw-r--r--na/kernel/module.mk3
-rw-r--r--na/kernel/sync/cpu_state.h34
-rw-r--r--na/kernel/sync/spinlock.cc111
-rw-r--r--na/kernel/sync/spinlock.h60
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_;
+};