diff options
| author | Richard Qin <richard.qin.x@gmail.com> | 2026-07-10 11:31:46 +0800 |
|---|---|---|
| committer | Richard Qin <richard.qin.x@gmail.com> | 2026-07-10 11:31:46 +0800 |
| commit | e90b72ec3082588b296956730bd2930cf8d5d671 (patch) | |
| tree | 36ff6916af9b4176ea5a9662219d11e721f0b691 /na/arch/riscv/kernel | |
| parent | 1767d390fbf3d0c65389de46caf0b6d43ef3b6ad (diff) | |
| download | lume-e90b72ec3082588b296956730bd2930cf8d5d671.tar.gz lume-e90b72ec3082588b296956730bd2930cf8d5d671.tar.xz | |
refactor: promote new architecture to repository root
Archive the deprecated implementation under lume-legacy, make the new kernel the default root-level build, and update repository entry points without changing kernel behavior.
Diffstat (limited to 'na/arch/riscv/kernel')
| -rw-r--r-- | na/arch/riscv/kernel/asm-offsets.c | 29 | ||||
| -rw-r--r-- | na/arch/riscv/kernel/head.S | 317 | ||||
| -rw-r--r-- | na/arch/riscv/kernel/lumeos.lds.S | 120 | ||||
| -rw-r--r-- | na/arch/riscv/kernel/signal.cc | 163 |
4 files changed, 0 insertions, 629 deletions
diff --git a/na/arch/riscv/kernel/asm-offsets.c b/na/arch/riscv/kernel/asm-offsets.c deleted file mode 100644 index edecab7..0000000 --- a/na/arch/riscv/kernel/asm-offsets.c +++ /dev/null @@ -1,29 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -// Copyright (C) 2026 Richard Qin -// -// arch/riscv/kernel/asm-offsets.c — emit struct field offsets and -// scalar constants for consumption by assembly sources (head.S etc.). -// -// 1:1 with Linux arch/riscv/kernel/asm-offsets.c. The Makefile -// compiles this to an .s file (not .o) and runs -// scripts/extract-asm-offsets.sed over the output to produce -// include/generated/asm-offsets.h. -// -// Adding a new offset: include the struct's header, add an OFFSET() -// or DEFINE() line below, rebuild. head.S can then reference the -// generated #define directly. - -#include <lume/kbuild.h> -#include <lume/kernel_map.h> - -void asm_offsets(void) -{ - COMMENT("LumeOS asm-offsets — struct kernel_mapping field offsets"); - OFFSET(KERNEL_MAP_VIRT_ADDR, kernel_mapping, virt_addr); - OFFSET(KERNEL_MAP_VIRT_OFFSET, kernel_mapping, virt_offset); - OFFSET(KERNEL_MAP_PHYS_ADDR, kernel_mapping, phys_addr); - OFFSET(KERNEL_MAP_SIZE, kernel_mapping, size); - OFFSET(KERNEL_MAP_VA_PA_OFFSET, kernel_mapping, va_pa_offset); - OFFSET(KERNEL_MAP_PAGE_OFFSET, kernel_mapping, page_offset); - OFFSET(KERNEL_MAP_VA_KERNEL_PA_OFFSET, kernel_mapping, va_kernel_pa_offset); -} diff --git a/na/arch/riscv/kernel/head.S b/na/arch/riscv/kernel/head.S deleted file mode 100644 index e04c44c..0000000 --- a/na/arch/riscv/kernel/head.S +++ /dev/null @@ -1,317 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* Copyright (C) 2026 Richard Qin */ - -/* - * RISC-V kernel entry — Linux-aligned layout. - * - * Flow (mirrors arch/riscv/kernel/head.S in Linux): - * - * _start [.head.text, byte 0 of image] - * · 64-byte Boot Protocol Image Header - * · j real_start - * - * real_start [.text] - * · save a0 (hartid) / a1 (dtb_pa) - * · setup gp / per-hart sp - * · BSP/AP election via amoswap - * · BSP: clear BSS, call setup_vm, then relocate_enable_mmu - * · AP: spin-wait, then relocate_enable_mmu - * - * relocate_enable_mmu [C function, arch/riscv/mm/init.cc] - * · csrw satp <- trampoline_pg_dir - * · jump to higher-half VA - * · csrw satp <- early_pg_dir - * · slide sp/ra/gp to higher-half - * · call relocate_kernel (walks .rela.dyn, patches R_RISCV_RELATIVE) - * - * real_start (continued in higher-half) - * · BSP only: call early_cpp_init (walks .init_array) - * · call kernel_main(hartid, dtb_pa) - * - * Register contract from OpenSBI / U-Boot: - * a0 = hart ID - * a1 = FDT physical base - */ - -#include <lume/config.h> - -#define PAGE_OFFSET 0xFFFFFFC000000000 -#define BOOT_STACK_SIZE 8192 - -/* ======================================================================== - * .head.text — RISC-V Boot Protocol Image Header - * - * Layout per Documentation/arch/riscv/boot-image-header.rst: - * code0/code1 (8 B) : executable code, lands at PC after OpenSBI hand-off - * text_offset (8 B) : load offset from RAM base - * image_size (8 B) : effective image size (0 = unknown) - * flags (8 B) : bit 0: endian - * version (4 B) : major.minor - * res1/res2 (4+8) : reserved - * magic (8 B) : "RISCV\0\0\0" - * magic2 (4 B) : "RSC\x05" - * res3 (4 B) : PE/COFF reserved - * ======================================================================== */ - .section .head.text, "ax" - .globl _start -_start: - j real_start /* code0 */ - .word 0 /* code1 */ - .dword 0x200000 /* text_offset */ - .dword 0 /* image_size (post-link) */ - .dword 0 /* flags */ - .word 0x0002 /* version 0.2 */ - .word 0 /* res1 */ - .dword 0 /* res2 */ - .dword 0x5643534952 /* magic "RISCV\0\0\0" */ - .word 0x05435352 /* magic2 "RSC\x05" */ - .word 0 /* res3 */ - -/* ======================================================================== - * real_start — all harts land here at physical PC - * ======================================================================== */ - .text - .globl real_start -real_start: - /* DEBUG: 'R<hartid>\n' — UART direct at 0x10000000, MMU off. */ - li t0, 0x10000000 - li t1, 'R' - sb t1, 0(t0) - addi t1, a0, '0' - sb t1, 0(t0) - li t1, '\n' - sb t1, 0(t0) - - /* Mask interrupts and supervisor pending bits. */ - csrw sie, zero - csrw sip, zero - - /* gp for medany — relax must be disabled across the `la`. */ - .option push - .option norelax - la gp, __global_pointer$ - .option pop - - /* per-hart boot stack: sp = boot_stack + (hartid + 1) * STACK_SIZE */ - la t0, boot_stack - li t1, BOOT_STACK_SIZE - addi t2, a0, 1 - mul t1, t1, t2 - add sp, t0, t1 - - /* Save hartid / dtb_pa in callee-saved regs preserved across calls. */ - mv tp, a0 /* tp = hartid */ - mv s0, a1 /* s0 = dtb_pa */ - - /* BSP/AP election: first hart to swap bsp_elected 0→1 is BSP. */ - la t0, bsp_elected - li t1, 1 - amoswap.w.aq t2, t1, (t0) - bnez t2, .Lap_wait /* prev != 0 → we're an AP */ - - /* === BSP path === */ - la t0, bsp_hart_id - sw a0, 0(t0) - - li t3, 0x10000000 - li t4, 'B' - sb t4, 0(t3) - li t4, '\n' - sb t4, 0(t3) - - /* Clear BSS — must precede setup_vm, which writes kernel_map. */ - la t0, _sbss - la t1, _ebss -.Lbss_loop: - bgeu t0, t1, .Lbss_done - sd zero, 0(t0) - addi t0, t0, 8 - j .Lbss_loop -.Lbss_done: - - li t3, 0x10000000 - li t4, 'V' - sb t4, 0(t3) - li t4, '\n' - sb t4, 0(t3) - - /* ---- BSP-only: pre-MMU PIC fixup of .rela.dyn ---- - * - * The kernel is built with -fPIE -shared. All global-data accesses - * compile to GOT-indirect loads (auipc + ld), where the linker - * emits each .got slot's initial value as the link-time VA and a - * companion R_RISCV_RELATIVE entry in .rela.dyn. At this point - * we're running at the physical load address — those link-time VAs - * are not yet mapped, so any global access would fault. - * - * Walk .rela.dyn in pure assembly (PC-relative only — no globals) - * and patch every R_RISCV_RELATIVE slot to addend + (PA - LINK_VA). - * After this, setup_vm can dereference kernel_map / early_pg_dir - * / etc. via the GOT and get correct PA-side pointers. - * - * Mirrors Linux arch/riscv/kernel/head.S — same idea, same loop. - * - * reloc: - * t0 = pa_offset = PA(_start) - KERNEL_LINK_ADDR (negative) - * t1 = &__rela_dyn_start - * t2 = &__rela_dyn_end - * for each 24-byte rela in [t1, t2): - * t3 = r_offset; t4 = r_info; t5 = r_addend - * if (t4 & 0xFFFFFFFF) != R_RISCV_RELATIVE: skip - * slot_pa = r_offset + pa_offset - * *slot_pa = r_addend + pa_offset - */ -#define R_RISCV_RELATIVE 3 - - la t0, _start /* PA of _start */ - li t1, 0xFFFFFFC080200000 /* KERNEL_LINK_ADDR (asm/page.h) */ - sub t0, t0, t1 /* t0 = pa_offset (PA - LINK_VA) */ - - la t1, __rela_dyn_start - la t2, __rela_dyn_end -.Lrela_loop: - bgeu t1, t2, .Lrela_done - ld t3, 0(t1) /* r_offset */ - ld t4, 8(t1) /* r_info */ - ld t5, 16(t1) /* r_addend */ - /* r_info low 32 bits = type */ - li t6, 0xFFFFFFFF - and t4, t4, t6 - li t6, R_RISCV_RELATIVE - bne t4, t6, .Lrela_next - /* slot_pa = r_offset + pa_offset */ - add t3, t3, t0 - /* value = r_addend + pa_offset */ - add t5, t5, t0 - sd t5, 0(t3) -.Lrela_next: - addi t1, t1, 24 /* sizeof(Elf64_Rela) */ - j .Lrela_loop -.Lrela_done: - - /* Memory fence so subsequent loads see the patched slots. */ - fence w, r - - li t3, 0x10000000 - li t4, 'P' - sb t4, 0(t3) - li t4, '\n' - sb t4, 0(t3) - - /* Call setup_vm(load_pa = PA of _start, dtb_pa = s0). - * Both args physical at this point — MMU still off. */ - la a0, _start - mv a1, s0 - call setup_vm - - li t3, 0x10000000 - li t4, 'v' - sb t4, 0(t3) - li t4, '\n' - sb t4, 0(t3) - - /* Release APs once page tables are built. */ - la t0, boot_sync_flag - li t1, 1 - amoswap.w.rl zero, t1, (t0) - j .Lenable_mmu - -/* ======================================================================== - * AP path — spin until BSP signals readiness. - * ======================================================================== */ -.Lap_wait: - la t0, boot_sync_flag -.Lap_spin: - lw t1, 0(t0) - beqz t1, .Lap_spin - fence r, rw - - /* fall through */ - -/* ======================================================================== - * .Lenable_mmu — all harts: enable MMU, jump to higher-half, run reloc. - * - * relocate_enable_mmu does its own SATP write + higher-half jump in - * C (with inline asm), then calls relocate_kernel. After it returns, - * we are at higher-half VA with sp / ra / gp slid up, and .rela.dyn - * is applied (so .init_array pointers are valid). - * ======================================================================== */ -.Lenable_mmu: - la a0, early_pg_dir - call relocate_enable_mmu - /* On return: PC, sp, ra, gp are all at higher-half VA. */ - - li t3, 0x10000000 - li t4, 'M' - sb t4, 0(t3) - li t4, '\n' - sb t4, 0(t3) - - /* Mark MMU enabled for console / pmap layers that check this. */ - call console_set_mmu_enabled - - /* BSP only: walk __init_array and call each C++ ctor. */ - la t0, bsp_hart_id - lw t0, 0(t0) - bne tp, t0, .Lskip_cpp_init - call early_cpp_init -.Lskip_cpp_init: - - /* Hand off to kernel_main(hartid, dtb_pa). */ - mv a0, tp - mv a1, s0 - call kernel_main - - /* kernel_main should not return. */ -.Lhalt: - csrc sstatus, 2 - wfi - j .Lhalt - -/* ======================================================================== - * early_cpp_init — walk .init_array and call each constructor. - * - * Called only by BSP, after relocate_kernel has patched the function - * pointers stored in __init_array. Before relocate_kernel runs those - * pointers are zero-filled by the linker. - * ======================================================================== */ - .globl early_cpp_init -early_cpp_init: - addi sp, sp, -32 - sd ra, 0(sp) - sd s1, 8(sp) - sd s2, 16(sp) - - la s1, __init_array_start - la s2, __init_array_end -.Linit_loop: - bgeu s1, s2, .Linit_done - ld t0, 0(s1) - jalr ra, t0 - addi s1, s1, 8 - j .Linit_loop -.Linit_done: - ld ra, 0(sp) - ld s1, 8(sp) - ld s2, 16(sp) - addi sp, sp, 32 - ret - -/* ======================================================================== - * Election state — in .data so explicit zero survives the BSP BSS clear - * (these are read before BSS is cleared on the BSP, and concurrently by - * APs while BSP is in the clear loop). - * ======================================================================== */ - .section .data - .align 2 - .globl boot_sync_flag -boot_sync_flag: - .word 0 - - .globl bsp_elected -bsp_elected: - .word 0 - - .globl bsp_hart_id -bsp_hart_id: - .word 0 diff --git a/na/arch/riscv/kernel/lumeos.lds.S b/na/arch/riscv/kernel/lumeos.lds.S deleted file mode 100644 index 9bd083b..0000000 --- a/na/arch/riscv/kernel/lumeos.lds.S +++ /dev/null @@ -1,120 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* Copyright (C) 2026 Richard Qin */ - -/* - * RISC-V Kernel Linker Script Template - * - * This is a C-preprocessed linker script (.lds.S). The build system - * runs it through CPP to produce the final vmlinux.lds which is - * passed to the linker. - * - * Architecture-specific definitions: - * - OUTPUT_ARCH, ENTRY - * - PAGE_OFFSET, BASE_ADDRESS, PHYS_BASE - * - .head.text placement - * - GP anchor (__global_pointer$ for RISC-V medany) - * - early_pgdir and boot stacks in .bss - * - * Common section macros come from include/lume/vmlinux.lds.h - * - * Reference: docs/specs/linker_and_entry.md - */ - -#include <lume/lumeos.lds.h> - -/* ===== RISC-V Architecture Constants ===== */ - -OUTPUT_ARCH(riscv) - -PAGE_OFFSET = 0xFFFFFFC000000000; -BASE_ADDRESS = 0xFFFFFFC080200000; -PHYS_BASE = 0x80200000; - -ENTRY(_start) - -/* ===== RISC-V: GP anchor in .data (medany model) ===== */ -#define ARCH_DATA_EXTRA \ - . = ALIGN(16); \ - PROVIDE(__global_pointer$ = . + 0x800); \ - *(.sdata .sdata.*) - -/* ===== RISC-V: page-table storage + per-CPU boot stacks in .bss ===== - * - * Linux RISC-V layout (arch/riscv/kernel/vmlinux.lds.S): - * early_pg_dir — first early page table built by setup_vm, - * used for the rest of boot after MMU enable - * trampoline_pg_dir — single-page table used only during the SATP - * switch from physical to higher-half VA; its - * identity + virt mappings of _start must both - * be valid in the same instruction window - * fixmap_pg_dir — covers the FIXADDR_START range used by the - * early console (UART MMIO before kernel page - * table proper is built) - * - * All three are allocated here so head.S can take their addresses without - * a C definition; their PA == VA mapping covers them during setup_vm. - */ -#define ARCH_BSS_EXTRA \ - . = ALIGN(4096); \ - early_pg_dir = .; \ - . += 4096; \ - trampoline_pg_dir = .; \ - . += 4096; \ - fixmap_pg_dir = .; \ - . += 4096; \ - /* Legacy name retained for any caller still referencing it; */ \ - /* points at the same first 4 KiB as early_pg_dir. */ \ - early_pgdir = early_pg_dir; \ - /* Per-CPU boot stacks: 8KB per CPU, max 8 CPUs */ \ - . = ALIGN(4096); \ - boot_stack = .; \ - . += 8192 * 8; \ - boot_stack_top = .; - -/* ===== Layout ===== */ - -SECTIONS { - . = BASE_ADDRESS; - - /* .head.text: Image Header (must be at byte 0 of the kernel image) */ - .head.text : AT(PHYS_BASE) { - _start = .; - KEEP(*(.head.text)) - } - - TEXT_SECTION - - . = ALIGN(4096); - - /* .rela.dyn lives between .text and .rodata so it ends up in the - * read-only PT_LOAD segment. Mirrors Linux vmlinux.lds.S where - * .rela.dyn sits at the tail of init data, just before RO_DATA. */ - RELA_DYN_SECTION - - . = ALIGN(4096); - - RODATA_SECTION - - . = ALIGN(4096); - - DATA_SECTION - - . = ALIGN(8); - - /* .got lives right after .data in the RW PT_LOAD. All other - * dynamic-linker artefacts (.plt, .dynamic, .dynsym, .dynstr, - * .hash, .gnu.hash, .interp, .dynbss) are discarded by the - * DISCARDS macro — mirrors Linux arch/riscv/kernel/vmlinux.lds.S. */ - GOT_SECTION - - . = ALIGN(4096); - - /* End of file-backed image (BSS follows as NOBITS) */ - __lume_image_end = .; - - BSS_SECTION - - KERNEL_END - - DISCARDS -} diff --git a/na/arch/riscv/kernel/signal.cc b/na/arch/riscv/kernel/signal.cc deleted file mode 100644 index 39fe34e..0000000 --- a/na/arch/riscv/kernel/signal.cc +++ /dev/null @@ -1,163 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* Copyright (C) 2026 Richard Qin */ - -/* - * arch/riscv/signal.cc — Signal frame construction and sigreturn - * - * Constructs a signal frame on the user stack before returning to - * the user signal handler. On sigreturn, restores the original - * registers from the saved frame. - * - * Frame layout on user stack (grows downward): - * - * +-----------------------+ <- original sp - * | sigset_t saved_mask | 8 bytes - * | TrapFrame saved_regs | 296 bytes - * | kernel_siginfo info | 128 bytes - * +-----------------------+ <- new sp (16-byte aligned) - * - * Reference: Linux arch/riscv/kernel/signal.c - */ - -#include <lume/signal.h> -#include <lume/signal_types.h> -#include <lume/signal_nr.h> -#include <lume/task.h> -#include <lume/uaccess.h> -#include <lume/errno.h> -#include <lume/klog.h> -#include <arch/trapframe.h> - -/* ================================================================== */ -/* Signal frame layout */ -/* ================================================================== */ - -struct rt_sigframe { - kernel_siginfo info; /* siginfo for SA_SIGINFO handlers */ - arch::TrapFrame saved_regs; /* saved register state */ - sigset_t saved_mask; /* blocked mask to restore on sigreturn */ -}; - -/* ================================================================== */ -/* Signal trampoline (written to user stack) */ -/* ================================================================== */ - -/* - * When the user's signal handler returns (via `ret`), ra points here. - * This tiny code sequence invokes sys_rt_sigreturn to restore the - * original execution context. - * - * RISC-V instructions (8 bytes): - * li a7, 139 → addi a7, zero, 139 → 0x08b00893 - * ecall → 0x00000073 - */ -static constexpr uint32 sigreturn_trampoline[2] = { - 0x08b00893, /* li a7, 139 (SYS_rt_sigreturn) */ - 0x00000073, /* ecall */ -}; - -/* ================================================================== */ -/* setup_signal_frame */ -/* ================================================================== */ - -int setup_signal_frame(arch::TrapFrame* tf, int signo, - kernel_sigaction* ka, kernel_siginfo* info) -{ - TaskControlBlock* curr = current_task(); - if (!curr) return -EINVAL; - - /* Calculate frame location on user stack */ - uint64 sp = tf->sp; - - /* Reserve space for the trampoline below the frame */ - sp -= sizeof(sigreturn_trampoline); - sp &= ~15ULL; /* 16-byte align */ - uint64 trampoline_addr = sp; - - /* Reserve space for the signal frame */ - sp -= sizeof(rt_sigframe); - sp &= ~15ULL; /* 16-byte align */ - uint64 frame_addr = sp; - - /* Build the frame in kernel memory, then copy to user stack */ - rt_sigframe frame{}; - if (info) { - frame.info = *info; - } - frame.saved_regs = *tf; - frame.saved_mask = curr->blocked; - - /* Copy frame to user stack */ - int err = copy_to_user(frame_addr, &frame, sizeof(frame)); - if (err < 0) return -EFAULT; - - /* Copy trampoline to user stack */ - err = copy_to_user(trampoline_addr, sigreturn_trampoline, - sizeof(sigreturn_trampoline)); - if (err < 0) return -EFAULT; - - /* Set up the trap frame to execute the signal handler */ - tf->epc = ka->sa_handler; - tf->sp = frame_addr; - - /* Arguments to the handler: handler(int signo, siginfo_t*, ucontext_t*) */ - tf->a[0] = static_cast<uint64>(signo); - - if (ka->sa_flags & SA_SIGINFO) { - /* SA_SIGINFO: pass siginfo and ucontext pointers */ - tf->a[1] = frame_addr; /* &info */ - tf->a[2] = frame_addr + sizeof(kernel_siginfo); /* &saved_regs (as ucontext) */ - } else { - tf->a[1] = 0; - tf->a[2] = 0; - } - - /* Return address: when the handler does `ret`, jump to trampoline */ - if (ka->sa_flags & SA_RESTORER) { - tf->ra = ka->sa_restorer; - } else { - tf->ra = trampoline_addr; - } - - return 0; -} - -/* ================================================================== */ -/* arch_sigreturn — restore state from signal frame */ -/* ================================================================== */ - -int64 arch_sigreturn(arch::TrapFrame* tf) -{ - TaskControlBlock* curr = current_task(); - if (!curr) return -EINVAL; - - /* - * The signal frame is located at the current sp. - * We need to read it back and restore the original state. - */ - uint64 frame_addr = tf->sp; - - rt_sigframe frame{}; - int err = copy_from_user(&frame, frame_addr, sizeof(frame)); - if (err < 0) { - printk(KERN_ERR "sigreturn: failed to read signal frame at %llx\n", - frame_addr); - task_kill_current(SIGSEGV); - return -EFAULT; - } - - /* Restore the saved register state */ - *tf = frame.saved_regs; - - /* Restore the blocked mask */ - curr->blocked = frame.saved_mask; - curr->blocked.del(SIGKILL); - curr->blocked.del(SIGSTOP); - - /* - * Return value: we return the saved a0 from the original context. - * The syscall dispatch will NOT overwrite a0 because sigreturn - * is special — it returns via the restored trap frame directly. - */ - return tf->a[0]; -} |
