[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