[PATCH 05/10] vmcore_tasks: arch: Add RISC-V 64-bit architecture support
Pnina Feder
pnina.feder at mobileye.com
Mon Jun 22 13:54:43 PDT 2026
Add RISC-V 64-bit page table translation and instruction helpers:
- riscv64_defs.h: SV39 page table constants, PTE format and
extraction macros, pt_regs register structure.
- SV39 3-level page table walk (PGD -> PMD -> PTE) with support
for 1GB gigapages and 2MB megapages at leaf entries.
SV48 and SV57 stubs selected from vmcoreinfo VA_BITS but not
yet implemented.
- Instruction decode helpers for the generic unwinder:
is_sp_move_ins: detects addi sp, sp, imm (stack frame setup)
is_ra_save_ins: detects sd ra, offset(sp) (RA save to stack)
- Signal frame detection: scans for ecall + li a7,__NR_rt_sigreturn
pattern near PC to identify signal trampolines. Signal frame
layout driven by vmcoreinfo offsets (rt_sigframe, ucontext,
sigcontext).
- Register formatting for pt_regs dump in mini coredump output.
Page size read at runtime from vmcoreinfo via get_page_size().
Signed-off-by: Pnina Feder <pnina.feder at mobileye.com>
---
vmcore_tasks/arch/riscv64/riscv64.c | 327 +++++++++++++++++++++++
vmcore_tasks/arch/riscv64/riscv64_defs.h | 127 +++++++++
2 files changed, 454 insertions(+)
create mode 100644 vmcore_tasks/arch/riscv64/riscv64.c
create mode 100644 vmcore_tasks/arch/riscv64/riscv64_defs.h
diff --git a/vmcore_tasks/arch/riscv64/riscv64.c b/vmcore_tasks/arch/riscv64/riscv64.c
new file mode 100644
index 00000000..7123d037
--- /dev/null
+++ b/vmcore_tasks/arch/riscv64/riscv64.c
@@ -0,0 +1,327 @@
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "memory.h"
+#include "vmcore_info.h"
+
+#include "vmcore_tasks_defs.h"
+
+#define RISCV_ECALL_INS 0x00000073U
+#define RISCV_ADDI_OPCODE 0x13U
+#define RISCV_RT_SIGRETURN_NR_DEF 139U
+
+/*
+ * Sv39 Page Table Walk - Main Implementation
+ *
+ * @param pgd_paddr Physical address of PGD (page table root)
+ * @param vaddr Virtual address to translate
+ * @param paddr Output: physical address
+ * @return true if translation succeeded
+ */
+bool riscv64_sv39_vtop(uint64_t pgd_paddr,
+ uint64_t vaddr,
+ uint64_t *paddr)
+{
+ uint64_t pgd_entry, pmd_entry, pte_entry;
+ uint64_t pgd_entry_paddr, pmd_paddr, pte_paddr;
+ uint64_t pfn;
+
+ pr_trace("riscv64_sv39_vtop: Translating vaddr 0x%llx using PGD at paddr 0x%llx\n",
+ (unsigned long long)vaddr, (unsigned long long)pgd_paddr);
+ /*
+ * STEP 1: Read PGD entry
+ *
+ * PGD entry address = pgd_paddr + (pgd_index * 8)
+ * Each entry is 8 bytes (64-bit)
+ */
+ pgd_entry_paddr = pgd_paddr + (pgd_index(vaddr) * sizeof(uint64_t));
+
+ if (readmem(pgd_entry_paddr, &pgd_entry, sizeof(pgd_entry), "pgd_entry", PADDR) < 0)
+ return false;
+
+ /* Check if PGD entry is valid */
+ if (!(pgd_entry & _PAGE_PRESENT))
+ return false;
+
+ /*
+ * Check for 1GB gigapage (leaf PGD entry)
+ * If R, W, or X bits are set, this is a leaf entry
+ */
+ if (pgd_entry & (_PAGE_READ | _PAGE_WRITE | _PAGE_EXEC)) {
+ /* 1GB page: PA = PPN[2] | vaddr[29:0] */
+ pfn = pte_pfn(pgd_entry);
+ *paddr = pfn_to_phys(pfn) | (vaddr & ((1UL << PGD_SHIFT) - 1));
+ return true;
+ }
+
+ /* Extract physical address of PMD table */
+ pmd_paddr = pte_to_phys(pgd_entry);
+
+ /*
+ * STEP 2: Read PMD entry
+ */
+ uint64_t pmd_entry_paddr = pmd_paddr + (pmd_index(vaddr) * sizeof(uint64_t));
+
+ if (readmem(pmd_entry_paddr, &pmd_entry, sizeof(pmd_entry), "pmd_entry", PADDR) < 0)
+ return false;
+
+ if (!(pmd_entry & _PAGE_PRESENT))
+ return false;
+
+ /*
+ * Check for 2MB megapage (leaf PMD entry)
+ */
+ if (pmd_entry & (_PAGE_READ | _PAGE_WRITE | _PAGE_EXEC)) {
+ /* 2MB page: PA = PPN[2:1] | vaddr[20:0] */
+ pfn = pte_pfn(pmd_entry);
+ *paddr = pfn_to_phys(pfn) | (vaddr & ((1UL << PMD_SHIFT) - 1));
+ return true;
+ }
+
+ /* Extract physical address of PTE table */
+ pte_paddr = pte_to_phys(pmd_entry);
+
+ /*
+ * STEP 3: Read PTE entry
+ */
+ uint64_t pte_entry_paddr = pte_paddr + (pte_index(vaddr) * sizeof(uint64_t));
+
+ if (readmem(pte_entry_paddr, &pte_entry, sizeof(pte_entry), "pte_entry", PADDR) < 0)
+ return false;
+
+ if (!(pte_entry & _PAGE_PRESENT)) {
+ pr_trace("PTE not present for vaddr 0x%llx: pte_entry=0x%llx (V=%d R=%d W=%d X=%d)\n",
+ (unsigned long long)vaddr,
+ (unsigned long long)pte_entry,
+ !!(pte_entry & _PAGE_PRESENT),
+ !!(pte_entry & _PAGE_READ),
+ !!(pte_entry & _PAGE_WRITE),
+ !!(pte_entry & _PAGE_EXEC));
+ return false;
+ }
+
+ /*
+ * STEP 4: Extract final physical address
+ * PA = PPN << 12 | rv_page_offset(vaddr)
+ */
+ pfn = pte_pfn(pte_entry);
+ *paddr = pfn_to_phys(pfn) | rv_page_offset(vaddr);
+
+ return true;
+}
+
+bool riscv64_sv48_vtop(uint64_t pgd_paddr,
+ uint64_t vaddr,
+ uint64_t *paddr)
+{
+ // Not implemented yet
+ (void)pgd_paddr; (void)vaddr; (void)paddr;
+ fprintf(stderr, "riscv64_sv48_vtop: SV48 translation not implemented\n");
+ return false;
+}
+
+bool riscv64_sv57_vtop(uint64_t pgd_paddr,
+ uint64_t vaddr,
+ uint64_t *paddr)
+{
+ // Not implemented yet
+ (void)pgd_paddr; (void)vaddr; (void)paddr;
+ fprintf(stderr, "riscv64_sv57_vtop: SV57 translation not implemented\n");
+ return false;
+}
+
+/* Helper - extract RISC-V fields from a 32-bit instruction word */
+static inline unsigned int rv_opcode(uint32_t insn) { return insn & 0x7f; }
+static inline unsigned int rv_rd(uint32_t insn) { return (insn >> 7) & 0x1f; }
+static inline unsigned int rv_funct3(uint32_t insn) { return (insn >> 12) & 0x7; }
+static inline unsigned int rv_rs1(uint32_t insn) { return (insn >> 15) & 0x1f; }
+static inline unsigned int rv_rs2(uint32_t insn) { return (insn >> 20) & 0x1f; }
+static inline unsigned int rv_funct7(uint32_t insn) { return (insn >> 25) & 0x7f; }
+
+/* Decode I-type signed imm (12-bit) */
+static inline int32_t rv_imm_i(uint32_t insn)
+{
+ int32_t imm = (int32_t)(insn >> 20) & 0xfff;
+ /* sign extend 12-bit */
+ if (imm & 0x800)
+ imm |= ~0xfff;
+ return imm;
+}
+
+/* Decode S-type signed imm (store), imm[11:5] = bits 31:25, imm[4:0] = bits 11:7 */
+static inline int32_t rv_imm_s(uint32_t insn)
+{
+ int32_t imm = ((insn >> 25) & 0x7f) << 5;
+
+ imm |= ((insn >> 7) & 0x1f);
+ /* sign extend 12-bit */
+ if (imm & 0x800)
+ imm |= ~0xfff;
+ return imm;
+}
+
+/* helper: detect addi sp, sp, imm -> stack adjustment */
+/* returns true and sets *stack_adj (signed) if matches */
+static inline bool is_sp_move_ins_rv64(uint32_t insn, int *stack_adj)
+{
+ /* we look for addi rd=sp(2), rs1=sp(2), opcode = OP_IMM, funct3 = 0 (addi) */
+ if (rv_opcode(insn) == OPCODE_OP_IMM && rv_rd(insn) == REG_SP &&
+ rv_rs1(insn) == REG_SP && rv_funct3(insn) == 0) {
+ /* immediate is sign-extended 12-bit */
+ int32_t imm = rv_imm_i(insn);
+ *stack_adj = imm; /* imm can be negative for "addi sp, sp, -N" */
+ return true;
+ }
+ return false;
+}
+
+/* helper: detect store of ra into stack frame: sd ra, offset(sp) or sw ra, offset(sp) */
+/* returns true and sets *ra_offset (unsigned) if matches */
+static inline bool is_ra_save_ins_rv64(uint32_t insn, unsigned long *ra_offset)
+{
+ /* store opcode (S-type). stores have opcode == OPCODE_STORE (0x23).
+ * funct3 selects width: 0=sb,1=sh,2=sw,3=sd (on RV64)
+ *
+ * We accept any store where rs2 == ra (x1) and rs1 == sp (x2).
+ * Then the offset is the signed S-type immediate. Caller will treat offset as unsigned
+ * and compute new_frame.ra = old_sp + offset.
+ */
+ if (rv_opcode(insn) == OPCODE_STORE && rv_rs2(insn) == REG_RA &&
+ rv_rs1(insn) == REG_SP) {
+ int32_t imm = rv_imm_s(insn);
+ /* imm might be negative for weird encodings, but typical prologue uses positive offset */
+ if (imm < 0)
+ return false;
+ *ra_offset = (unsigned long)imm;
+ return true;
+ }
+ return false;
+}
+
+/* Check if instruction is signal syscall */
+static bool riscv_is_addi_a7_x0_imm(uint32_t insn, uint32_t imm12)
+{
+ uint32_t opcode = insn & 0x7f;
+ uint32_t rd = (insn >> 7) & 0x1f;
+ uint32_t funct3 = (insn >> 12) & 0x7;
+ uint32_t rs1 = (insn >> 15) & 0x1f;
+ uint32_t imm = (insn >> 20) & 0xfff;
+
+ return opcode == RISCV_ADDI_OPCODE &&
+ rd == 17 && /* a7 */
+ funct3 == 0 &&
+ rs1 == 0 && /* x0 */
+ imm == (imm12 & 0xfff);
+}
+
+static bool riscv64_likely_signal_pc(uint64_t pc)
+{
+ uint32_t nr = RISCV_RT_SIGRETURN_NR_DEF;
+ uint32_t insn[9];
+ int i, j;
+
+ if (NUMBER_EXISTS("__NR_rt_sigreturn"))
+ nr = (uint32_t)NUMBER("__NR_rt_sigreturn");
+
+ /* read window [pc-16, pc+16], 4-byte aligned */
+ for (i = -4; i <= 4; i++) {
+ uint64_t a = pc + ((int64_t)i * 4);
+ if (readmem(a, &insn[i + 4], sizeof(uint32_t),
+ "riscv sigtramp window", UVADDR) != (ssize_t)sizeof(uint32_t))
+ return false;
+ }
+
+ /* require ecall + nearby 'li a7,__NR_rt_sigreturn' */
+ for (i = 0; i < 9; i++) {
+ if (insn[i] != RISCV_ECALL_INS)
+ continue;
+
+ for (j = i - 3; j <= i - 1; j++) {
+ if (j < 0 || j >= 9)
+ continue;
+ if (riscv_is_addi_a7_x0_imm(insn[j], nr))
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/* vmcoreinfo-driven signal-frame layout for RISC-V */
+static const struct signal_unwind_layout riscv64_sig_layout = {
+ /* sp + OFFSET(rt_sigframe.uc) */
+ .rt_sigframe_uc_key = "rt_sigframe.uc",
+ /* + OFFSET(ucontext.uc_mcontext) */
+ .ucontext_mcontext_key = "ucontext.uc_mcontext",
+ /* + OFFSET(sigcontext.sc_regs) */
+ .sigcontext_regs_key = "sigcontext.sc_regs",
+
+ /* no dedicated sigcontext PC key exported for RISC-V in your setup */
+ .sigcontext_pc_key = NULL,
+
+ .reg_width = 8, /* 64-bit regs */
+ .ra_reg_index = REG_RA,
+ .sp_reg_index = REG_SP,
+ .pc_reg_index = REG_PC,
+ .pc_reg_alt_index = -1,
+};
+
+void riscv64_init(void)
+{
+ arch.unwind_ops.insn_size = 4; /* 32-bit instructions */
+ arch.unwind_ops.ra_width = 8; /* RA is 64-bit */
+ arch.unwind_ops.is_sp_move_ins = is_sp_move_ins_rv64;
+ arch.unwind_ops.is_ra_save_ins = is_ra_save_ins_rv64;
+ arch.unwind_ops.is_end_of_backtrace = NULL; /* not used */
+ arch.unwind_ops.post_unwind_fixup = NULL; /* not used */
+ arch.unwind_ops.likely_signal_pc = riscv64_likely_signal_pc;
+ arch.unwind_ops.signal_layout = &riscv64_sig_layout;
+
+ int vtop_type = NUMBER("VA_BITS");
+ switch(vtop_type) {
+ case 39:
+ arch.vtop = riscv64_sv39_vtop;
+ break;
+ case 48:
+ arch.vtop = riscv64_sv48_vtop;
+ break;
+ case 57:
+ arch.vtop = riscv64_sv57_vtop;
+ break;
+ default:
+ fprintf(stderr, "Unsupported or missing RISCV VA_BITS=%d in vmcoreinfo\n", vtop_type);
+ arch.vtop = NULL;
+ break;
+ }
+}
+
+void write_user_registers_formatted(FILE *fp, struct pt_regs *regs)
+{
+ fprintf(fp, "user registers:\n");
+ fprintf(fp, "pc :0x%016llx ra :0x%016llx sp :0x%016llx gp :0x%016llx\n",
+ (unsigned long long)regs->pc, (unsigned long long)regs->ra,
+ (unsigned long long)regs->sp, (unsigned long long)regs->gp);
+ fprintf(fp, "tp :0x%016llx t0 :0x%016llx t1 :0x%016llx t2 :0x%016llx\n",
+ (unsigned long long)regs->tp, (unsigned long long)regs->t0,
+ (unsigned long long)regs->t1, (unsigned long long)regs->t2);
+ fprintf(fp, "s0 :0x%016llx s1 :0x%016llx a0 :0x%016llx a1 :0x%016llx\n",
+ (unsigned long long)regs->s0, (unsigned long long)regs->s1,
+ (unsigned long long)regs->a0, (unsigned long long)regs->a1);
+ fprintf(fp, "a2 :0x%016llx a3 :0x%016llx a4 :0x%016llx a5 :0x%016llx\n",
+ (unsigned long long)regs->a2, (unsigned long long)regs->a3,
+ (unsigned long long)regs->a4, (unsigned long long)regs->a5);
+ fprintf(fp, "a6 :0x%016llx a7 :0x%016llx s2 :0x%016llx s3 :0x%016llx\n",
+ (unsigned long long)regs->a6, (unsigned long long)regs->a7,
+ (unsigned long long)regs->s2, (unsigned long long)regs->s3);
+ fprintf(fp, "s4 :0x%016llx s5 :0x%016llx s6 :0x%016llx s7 :0x%016llx\n",
+ (unsigned long long)regs->s4, (unsigned long long)regs->s5,
+ (unsigned long long)regs->s6, (unsigned long long)regs->s7);
+ fprintf(fp, "s8 :0x%016llx s9 :0x%016llx s10:0x%016llx s11:0x%016llx\n",
+ (unsigned long long)regs->s8, (unsigned long long)regs->s9,
+ (unsigned long long)regs->s10, (unsigned long long)regs->s11);
+ fprintf(fp, "t3 :0x%016llx t4 :0x%016llx t5 :0x%016llx t6 :0x%016llx\n",
+ (unsigned long long)regs->t3, (unsigned long long)regs->t4,
+ (unsigned long long)regs->t5, (unsigned long long)regs->t6);
+}
diff --git a/vmcore_tasks/arch/riscv64/riscv64_defs.h b/vmcore_tasks/arch/riscv64/riscv64_defs.h
new file mode 100644
index 00000000..fbeaec8d
--- /dev/null
+++ b/vmcore_tasks/arch/riscv64/riscv64_defs.h
@@ -0,0 +1,127 @@
+#ifndef RISCV64_DEFS_H
+#define RISCV64_DEFS_H
+
+#include <stdint.h>
+
+#include "vmcore_info.h"
+
+/* Page table definitions */
+/* Page size - read from vmcoreinfo at runtime via sys_cfg */
+#define SYS_PAGE_SIZE (get_page_size())
+#define SYS_PAGE_SHIFT (get_page_shift())
+#define SYS_PAGE_MASK (~(SYS_PAGE_SIZE - 1)) // 0xFFFFFFFFFFFFF000
+
+/* SV39 Page table shifts for 4k pages */
+/* WARNING: These assume 4K pages. For non-4K page sizes, these must be
+ * recalculated based on get_page_shift(). */
+/* NOTE: Sv48/Sv57 require different shifts — see riscv64_sv48_vtop */
+#define PGD_SHIFT 30 // PGD covers bits [38:30]
+#define PMD_SHIFT 21 // PMD covers bits [29:21]
+#define PTE_SHIFT 12 // PTE covers bits [20:12]
+
+/* Entries per level (9 bits each = 512 entries) */
+#define PTRS_PER_PGD 512
+#define PTRS_PER_PMD 512
+#define PTRS_PER_PTE 512
+
+/* PTE format */
+#define _PAGE_PFN_SHIFT 10 // PPN starts at bit 10 in PTE
+#define PTE_PFN_MASK 0x003FFFFFFFFFFC00UL // Bits [53:10] = PPN
+
+/* Page flags (bits [9:0]) */
+#define _PAGE_PRESENT (1 << 0) // V - Valid
+#define _PAGE_READ (1 << 1) // R - Readable
+#define _PAGE_WRITE (1 << 2) // W - Writable
+#define _PAGE_EXEC (1 << 3) // X - Executable
+#define _PAGE_USER (1 << 4) // U - User accessible
+#define _PAGE_GLOBAL (1 << 5) // G - Global mapping
+#define _PAGE_ACCESSED (1 << 6) // A - Accessed
+#define _PAGE_DIRTY (1 << 7) // D - Dirty
+
+/* Mask for PPN + protection bits (bits [53:0]) */
+#define PTE_PFN_PROT_MASK 0x003FFFFFFFFFFFFFULL
+
+
+/* Extract index for each page table level from virtual address */
+
+// PGD index: bits [38:30] - 9 bits
+#define pgd_index(vaddr) (((vaddr) >> PGD_SHIFT) & (PTRS_PER_PGD - 1))
+
+// PMD index: bits [29:21] - 9 bits
+#define pmd_index(vaddr) (((vaddr) >> PMD_SHIFT) & (PTRS_PER_PMD - 1))
+
+// PTE index: bits [20:12] - 9 bits
+#define pte_index(vaddr) (((vaddr) >> PTE_SHIFT) & (PTRS_PER_PTE - 1))
+
+// Page offset: bits [11:0] - 12 bits
+#define rv_page_offset(vaddr) ((vaddr) & (SYS_PAGE_SIZE - 1))
+
+
+/* Extract physical address of next-level page table from PTE value */
+#define pte_to_phys(pte_val) (((pte_val) >> _PAGE_PFN_SHIFT) << SYS_PAGE_SHIFT)
+
+/* Same as above, written differently */
+#define pte_pfn(pte_val) ((pte_val) >> _PAGE_PFN_SHIFT)
+#define pfn_to_phys(pfn) ((pfn) << SYS_PAGE_SHIFT)
+/* unwind related definitions */
+#define GET_OFFSET_IN_PAGE(x) ((x) & (SYS_PAGE_SIZE - 1))
+#define GET_PAGE_NR(x) ((x) & SYS_PAGE_MASK)
+#define MAX_ALLOC_PAGES 3
+
+/* Opcode values (subset we need) */
+#define OPCODE_OP_IMM 0x13 /* addi, slli, ... */
+#define OPCODE_STORE 0x23 /* stores: sb, sh, sw, sd */
+#define OPCODE_OP 0x33 /* register-register ops (not used for these checks) */
+
+/* Registers */
+#define REG_PC 0
+#define REG_RA 1
+#define REG_SP 2
+
+/* RISC-V CPU register state structure */
+struct pt_regs {
+ unsigned long pc; /* program counter */
+ unsigned long ra;
+ unsigned long sp;
+ unsigned long gp;
+ unsigned long tp;
+ unsigned long t0;
+ unsigned long t1;
+ unsigned long t2;
+ unsigned long s0;
+ unsigned long s1;
+ unsigned long a0;
+ unsigned long a1;
+ unsigned long a2;
+ unsigned long a3;
+ unsigned long a4;
+ unsigned long a5;
+ unsigned long a6;
+ unsigned long a7;
+ unsigned long s2;
+ unsigned long s3;
+ unsigned long s4;
+ unsigned long s5;
+ unsigned long s6;
+ unsigned long s7;
+ unsigned long s8;
+ unsigned long s9;
+ unsigned long s10;
+ unsigned long s11;
+ unsigned long t3;
+ unsigned long t4;
+ unsigned long t5;
+ unsigned long t6;
+ /* Supervisor/Machine CSRs */
+ unsigned long status;
+ unsigned long badaddr;
+ unsigned long cause;
+ /* a0 value before the syscall */
+ unsigned long orig_a0;
+};
+
+#define PT_REGS_PC(X) ((X).pc)
+#define PT_REGS_RA(X) ((X).ra)
+#define PT_REGS_SP(X) ((X).sp)
+
+#endif /* RISCV64_DEFS_H */
\ No newline at end of file
--
2.43.0
More information about the kexec
mailing list