[PATCH 06/10] vmcore_tasks: arch: Add MIPS64 architecture support

Pnina Feder pnina.feder at mobileye.com
Mon Jun 22 13:54:44 PDT 2026


Add MIPS64 page table translation and instruction helpers:

- mips64_defs.h: MIPS64 instruction format helpers, pt_regs
  register structure (32 GPRs + CP0), opcode constants.

- 3-level page table walk (PGD -> PMD -> PTE) with parameters
  read from vmcoreinfo at runtime: _PFN_SHIFT, _PFN_MASK,
  PMD_SHIFT, PGDIR_SHIFT, PTRS_PER_*, _PAGE_PRESENT, _PAGE_VALID.
  Falls back to sensible defaults when entries are missing.
  Derives max physical address bits from PT_LOAD segments when
  vmcoreinfo lacks _MAX_PHYSMEM_BITS.

- Direct kernel virtual-to-physical translation overriding the
  weak defaults in memory.c for MIPS64 fixed segments: XKPHYS
  (cached CCA=3), KSEG0, and KSEG1.

- Instruction decode helpers for the generic unwinder:
  is_sp_move_ins: detects addiu/daddiu sp,sp,imm
  is_ra_save_ins: detects sw/sd ra,offset(sp)
  is_end_of_backtrace: detects 0xf825 (move ra,zero) sentinel

- Signal trampoline detection: deterministic matching of the
  MIPS rt_sigreturn instruction pair (li v0,__NR_rt_sigreturn;
  syscall). Validates syscall numbers for o32, n32, and n64
  ABIs. Signal frame layout driven by vmcoreinfo offsets.

- Register formatting for MIPS64 pt_regs in tasks output.

Signed-off-by: Pnina Feder <pnina.feder at mobileye.com>
---
 vmcore_tasks/arch/mips64/mips64.c      | 648 +++++++++++++++++++++++++
 vmcore_tasks/arch/mips64/mips64_defs.h |  78 +++
 2 files changed, 726 insertions(+)
 create mode 100644 vmcore_tasks/arch/mips64/mips64.c
 create mode 100644 vmcore_tasks/arch/mips64/mips64_defs.h

diff --git a/vmcore_tasks/arch/mips64/mips64.c b/vmcore_tasks/arch/mips64/mips64.c
new file mode 100644
index 00000000..09ad5497
--- /dev/null
+++ b/vmcore_tasks/arch/mips64/mips64.c
@@ -0,0 +1,648 @@
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "memory.h"
+#include "vmcore_info.h"
+#include "elf_info.h"
+
+#include "vmcore_tasks_defs.h"
+
+/* ========================================================================
+ * MIPS64 Memory Layout Overrides
+ *
+ * MIPS64 uses fixed address segments instead of configurable PAGE_OFFSET:
+ *
+ *   XKPHYS:  0x8000000000000000 - 0xBFFFFFFFFFFFFFFF
+ *            paddr = kvaddr & 0x07FFFFFFFFFFFFFF
+ *            Cached (CCA=3): 0x9800000000000000
+ *
+ *   KSEG0:   0xFFFFFFFF80000000 - 0xFFFFFFFF9FFFFFFF
+ *            paddr = kvaddr & 0x1FFFFFFF
+ *
+ *   KSEG1:   0xFFFFFFFFA0000000 - 0xFFFFFFFFBFFFFFFF
+ *            paddr = kvaddr & 0x1FFFFFFF
+ * ======================================================================== */
+
+#define MIPS64_XKPHYS_CACHED   0x9800000000000000ULL
+#define MIPS64_XKPHYS_BASE     0x8000000000000000ULL
+#define MIPS64_XKPHYS_TOP      0xBFFFFFFFFFFFFFFFULL
+#define MIPS64_XKPHYS_PHYS_MASK 0x07FFFFFFFFFFFFFFULL  /* bits [58:0] = physical address */
+
+#define MIPS64_KSEG0_BASE      0xFFFFFFFF80000000ULL
+#define MIPS64_KSEG0_END       0xFFFFFFFF9FFFFFFFULL
+#define MIPS64_KSEG1_BASE      0xFFFFFFFFA0000000ULL
+#define MIPS64_KSEG1_END       0xFFFFFFFFBFFFFFFFULL
+#define MIPS64_KSEG_MASK       0x1FFFFFFFULL
+
+#define MIPS_SYSCALL_MASK 0xFC00003F
+#define MIPS_SYSCALL_INS  0x0000000C
+
+#ifndef MIPS_OP_ORI
+#define MIPS_OP_ORI 0x0d
+#endif
+
+#ifndef MIPS_REG_V0
+#define MIPS_REG_V0 2
+#endif
+
+/* __NR_rt_sigreturn values by ABI (Linux MIPS syscall base + table index). */
+#define MIPS_NR_RT_SIGRETURN_O32 4193U  /* 4000 + 193 */
+#define MIPS_NR_RT_SIGRETURN_N64 5211U  /* 5000 + 211 */
+#define MIPS_NR_RT_SIGRETURN_N32 6211U  /* 6000 + 211 */
+
+/* MIPS64 pagetable walk runtime parameters (from vmcoreinfo, with fallbacks) */
+static uint64_t mips64_page_present;
+static uint64_t mips64_page_valid;
+static uint64_t mips64_pfn_mask;
+static unsigned int mips64_pfn_shift;
+static unsigned int mips64_pmd_shift;
+static unsigned int mips64_pgdir_shift;
+static unsigned int mips64_page_shift;
+static uint64_t mips64_pgd_mask;
+static uint64_t mips64_pmd_mask;
+static uint64_t mips64_pte_mask;
+static uint64_t mips64_level_mask;
+static uint64_t mips64_phys_mask;
+
+static inline uint64_t mips64_entry_phys_base(uint64_t entry)
+{
+	uint64_t base;
+
+	if (mips64_pfn_mask) {
+		uint64_t pfn = (entry & mips64_pfn_mask) >> mips64_pfn_shift;
+		base = (pfn << mips64_page_shift);
+	} else {
+		base = entry & SYS_PAGE_MASK;
+	}
+
+	return base & mips64_phys_mask;
+}
+
+static inline bool mips64_pte_is_present_valid(uint64_t pte)
+{
+	if (!(pte & mips64_page_present))
+		return false;
+
+	if (mips64_page_valid && !(pte & mips64_page_valid))
+		return false;
+
+	return true;
+}
+
+static inline uint64_t mips64_pgd_index(uint64_t vaddr)
+{
+	return (vaddr >> mips64_pgdir_shift) & mips64_pgd_mask;
+}
+
+static inline uint64_t mips64_pmd_index(uint64_t vaddr)
+{
+	return (vaddr >> mips64_pmd_shift) & mips64_pmd_mask;
+}
+
+static inline uint64_t mips64_pte_index(uint64_t vaddr)
+{
+	return (vaddr >> mips64_page_shift) & mips64_pte_mask;
+}
+
+static bool mips64_table_entry_to_paddr(uint64_t entry,
+					const char *level,
+					uint64_t *table_paddr)
+{
+	uint64_t aligned = (entry & SYS_PAGE_MASK) & mips64_phys_mask;
+
+	if (!aligned) {
+		pr_trace("mips64_vtop: %s entry 0x%llx aligns to 0\n",
+			 level, (unsigned long long)entry);
+		return false;
+	}
+
+	if (is_kvaddr(aligned)) {
+		if (!vtop_direct(aligned, table_paddr)) {
+			pr_trace("mips64_vtop: %s aligned kvaddr 0x%llx failed direct vtop\n",
+				 level, (unsigned long long)aligned);
+			return false;
+		}
+		pr_trace("mips64_vtop: %s entry 0x%llx (kvaddr 0x%llx) -> next table paddr 0x%llx\n",
+			 level, (unsigned long long)entry,
+			 (unsigned long long)aligned,
+			 (unsigned long long)*table_paddr);
+		return true;
+	}
+
+	*table_paddr = aligned;
+	pr_trace("mips64_vtop: %s entry 0x%llx -> next table paddr 0x%llx\n",
+		 level, (unsigned long long)entry,
+		 (unsigned long long)*table_paddr);
+	return true;
+}
+
+void mips64_pagetable_init(void)
+{
+	uint64_t max_phys_bits;
+	uint64_t ptrs_per;
+	unsigned long long phys_start;
+	unsigned long long phys_end;
+	unsigned long long virt_start;
+	unsigned long long virt_end;
+	unsigned long long max_phys_seen = 0;
+	int idx;
+
+	mips64_page_shift = get_page_shift();
+
+	if (NUMBER_EXISTS("_PAGE_PRESENT"))
+		mips64_page_present = NUMBER("_PAGE_PRESENT");
+	else
+		mips64_page_present = MIPS64_PTE_PRESENT;
+
+	if (NUMBER_EXISTS("_PAGE_VALID"))
+		mips64_page_valid = NUMBER("_PAGE_VALID");
+	else
+		mips64_page_valid = 0;
+
+	if (NUMBER_EXISTS("_PFN_SHIFT"))
+		mips64_pfn_shift = (unsigned int)NUMBER("_PFN_SHIFT");
+	else
+		mips64_pfn_shift = 15;
+
+	if (NUMBER_EXISTS("_PFN_MASK"))
+		mips64_pfn_mask = NUMBER("_PFN_MASK");
+	else
+		mips64_pfn_mask = 0;
+
+	if (NUMBER_EXISTS("PMD_SHIFT"))
+		mips64_pmd_shift = (unsigned int)NUMBER("PMD_SHIFT");
+	else
+		mips64_pmd_shift = (mips64_page_shift * 2) - 3;
+
+	if (NUMBER_EXISTS("PGDIR_SHIFT"))
+		mips64_pgdir_shift = (unsigned int)NUMBER("PGDIR_SHIFT");
+	else
+		mips64_pgdir_shift = mips64_pmd_shift + mips64_page_shift - 3;
+
+	if (mips64_page_shift < 4)
+		mips64_level_mask = 0;
+	else
+		mips64_level_mask = (1ULL << (mips64_page_shift - 3)) - 1;
+
+	mips64_pgd_mask = mips64_level_mask;
+	mips64_pmd_mask = mips64_level_mask;
+	mips64_pte_mask = mips64_level_mask;
+
+	if (NUMBER_EXISTS("PTRS_PER_PGD")) {
+		ptrs_per = NUMBER("PTRS_PER_PGD");
+		if (ptrs_per)
+			mips64_pgd_mask = ptrs_per - 1;
+	}
+	if (NUMBER_EXISTS("PTRS_PER_PMD")) {
+		ptrs_per = NUMBER("PTRS_PER_PMD");
+		if (ptrs_per)
+			mips64_pmd_mask = ptrs_per - 1;
+	}
+	if (NUMBER_EXISTS("PTRS_PER_PTE")) {
+		ptrs_per = NUMBER("PTRS_PER_PTE");
+		if (ptrs_per)
+			mips64_pte_mask = ptrs_per - 1;
+	}
+
+	if (NUMBER_EXISTS("_MAX_PHYSMEM_BITS"))
+		max_phys_bits = NUMBER("_MAX_PHYSMEM_BITS");
+	else if (NUMBER_EXISTS("MAX_PHYSMEM_BITS"))
+		max_phys_bits = NUMBER("MAX_PHYSMEM_BITS");
+	else {
+		max_phys_bits = 0;
+		for (idx = 0; get_pt_load(idx, &phys_start, &phys_end,
+					  &virt_start, &virt_end); idx++) {
+			(void)virt_start;
+			(void)virt_end;
+			if (phys_end > 0 && (phys_end - 1) > max_phys_seen)
+				max_phys_seen = phys_end - 1;
+		}
+
+		if (max_phys_seen) {
+			max_phys_bits = 64U - (uint64_t)__builtin_clzll(max_phys_seen);
+			pr_trace("mips64 pagetable: derived max phys bits=%llu from PT_LOAD max paddr=0x%llx\n",
+				 (unsigned long long)max_phys_bits,
+				 (unsigned long long)max_phys_seen);
+		} else {
+			max_phys_bits = 59;
+		}
+	}
+
+	if (max_phys_bits >= 64)
+		mips64_phys_mask = ~0ULL;
+	else
+		mips64_phys_mask = (1ULL << max_phys_bits) - 1;
+
+	pr_trace("mips64 pagetable: page_shift=%u pfn_shift=%u pfn_mask=0x%llx "
+		 "pmd_shift=%u pgdir_shift=%u present=0x%llx valid=0x%llx "
+		 "masks[pgd=0x%llx pmd=0x%llx pte=0x%llx] phys_mask=0x%llx\n",
+		 mips64_page_shift, mips64_pfn_shift,
+		 (unsigned long long)mips64_pfn_mask,
+		 mips64_pmd_shift, mips64_pgdir_shift,
+		 (unsigned long long)mips64_page_present,
+		 (unsigned long long)mips64_page_valid,
+		 (unsigned long long)mips64_pgd_mask,
+		 (unsigned long long)mips64_pmd_mask,
+		 (unsigned long long)mips64_pte_mask,
+		 (unsigned long long)mips64_phys_mask);
+}
+
+bool mips64_vtop(uint64_t pgd_paddr, uint64_t vaddr, uint64_t *paddr)
+{
+	uint64_t pgd_idx;
+	uint64_t pmd_idx;
+	uint64_t pte_idx;
+	uint64_t pgd_entry;
+	uint64_t pmd_entry;
+	uint64_t pte_entry;
+	uint64_t pmd_paddr;
+	uint64_t pte_paddr;
+	uint64_t raw_paddr;
+
+	pgd_idx = mips64_pgd_index(vaddr);
+	pmd_idx = mips64_pmd_index(vaddr);
+	pte_idx = mips64_pte_index(vaddr);
+
+	pr_trace("mips64_vtop: walk vaddr=0x%llx pgd_paddr=0x%llx idx[pgd=%llu pmd=%llu pte=%llu]\n",
+		 (unsigned long long)vaddr,
+		 (unsigned long long)pgd_paddr,
+		 (unsigned long long)pgd_idx,
+		 (unsigned long long)pmd_idx,
+		 (unsigned long long)pte_idx);
+
+	uint64_t pgd_entry_paddr = pgd_paddr + (pgd_idx * sizeof(uint64_t));
+	if (readmem(pgd_entry_paddr, &pgd_entry, sizeof(pgd_entry), "pgd_entry", PADDR) < 0) {
+		pr_verbose("mips64_vtop: failed read PGD entry at paddr=0x%llx\n",
+			 (unsigned long long)pgd_entry_paddr);
+		return false;
+	}
+
+	pr_trace("mips64_vtop: PGD[%llu] @0x%llx = 0x%llx\n",
+		 (unsigned long long)pgd_idx,
+		 (unsigned long long)pgd_entry_paddr,
+		 (unsigned long long)pgd_entry);
+
+	if (!pgd_entry) {
+		pr_verbose("mips64_vtop: empty PGD entry\n");
+		return false;
+	}
+
+	if (!mips64_table_entry_to_paddr(pgd_entry, "PGD", &pmd_paddr))
+		return false;
+
+	uint64_t pmd_entry_paddr = pmd_paddr + (pmd_idx * sizeof(uint64_t));
+	if (readmem(pmd_entry_paddr, &pmd_entry, sizeof(pmd_entry), "pmd_entry", PADDR) < 0) {
+		pr_verbose("mips64_vtop: failed read PMD entry at paddr=0x%llx\n",
+			 (unsigned long long)pmd_entry_paddr);
+		return false;
+	}
+
+	pr_trace("mips64_vtop: PMD[%llu] @0x%llx = 0x%llx\n",
+		 (unsigned long long)pmd_idx,
+		 (unsigned long long)pmd_entry_paddr,
+		 (unsigned long long)pmd_entry);
+
+	if (!pmd_entry) {
+		pr_verbose("mips64_vtop: empty PMD entry\n");
+		return false;
+	}
+
+	if (!mips64_table_entry_to_paddr(pmd_entry, "PMD", &pte_paddr))
+		return false;
+
+	uint64_t pte_entry_paddr = pte_paddr + (pte_idx * sizeof(uint64_t));
+	if (readmem(pte_entry_paddr, &pte_entry, sizeof(pte_entry), "pte_entry", PADDR) < 0) {
+		pr_verbose("mips64_vtop: failed read PTE entry at paddr=0x%llx\n",
+			 (unsigned long long)pte_entry_paddr);
+		return false;
+	}
+
+	pr_trace("mips64_vtop: PTE[%llu] @0x%llx = 0x%llx\n",
+		 (unsigned long long)pte_idx,
+		 (unsigned long long)pte_entry_paddr,
+		 (unsigned long long)pte_entry);
+
+	if (!mips64_pte_is_present_valid(pte_entry)) {
+		pr_verbose("mips64_vtop: PTE not valid/present (pte=0x%llx present=0x%llx valid=0x%llx)\n",
+			 (unsigned long long)pte_entry,
+			 (unsigned long long)mips64_page_present,
+			 (unsigned long long)mips64_page_valid);
+		return false;
+	}
+
+	if (mips64_pfn_mask) {
+		uint64_t pfn = (pte_entry & mips64_pfn_mask) >> mips64_pfn_shift;
+		raw_paddr = (pfn << mips64_page_shift) | (vaddr & (SYS_PAGE_SIZE - 1));
+	} else {
+		raw_paddr = ((pte_entry >> mips64_pfn_shift) << mips64_page_shift) |
+			  (vaddr & (SYS_PAGE_SIZE - 1));
+	}
+
+	*paddr = raw_paddr & mips64_phys_mask;
+
+	pr_trace("mips64_vtop: success vaddr=0x%llx -> raw_paddr=0x%llx masked_paddr=0x%llx (page_off=0x%llx)\n",
+		 (unsigned long long)vaddr,
+			(unsigned long long)raw_paddr,
+		 (unsigned long long)*paddr,
+		 (unsigned long long)(vaddr & (SYS_PAGE_SIZE - 1)));
+
+	return true;
+}
+
+/**
+ * get_page_offset - MIPS64 uses XKPHYS cached segment as PAGE_OFFSET
+ */
+uint64_t get_page_offset(void)
+{
+	return MIPS64_XKPHYS_CACHED;
+}
+
+/**
+ * is_kvaddr - check if address is in a MIPS64 kernel segment
+ */
+bool is_kvaddr(uint64_t vaddr)
+{
+	/* XKPHYS: 0x8000000000000000 - 0xBFFFFFFFFFFFFFFF (top 2 bits = 10) */
+	if (vaddr >= MIPS64_XKPHYS_BASE && vaddr <= MIPS64_XKPHYS_TOP)
+		return true;
+
+	/* KSEG0 */
+	if (vaddr >= MIPS64_KSEG0_BASE && vaddr <= MIPS64_KSEG0_END)
+		return true;
+
+	/* KSEG1 */
+	if (vaddr >= MIPS64_KSEG1_BASE && vaddr <= MIPS64_KSEG1_END)
+		return true;
+
+	return false;
+}
+
+/**
+ * vtop_direct - MIPS64 kernel virtual to physical translation
+ */
+bool vtop_direct(uint64_t kvaddr, uint64_t *paddr)
+{
+	if (kvaddr >= MIPS64_XKPHYS_BASE && kvaddr <= MIPS64_XKPHYS_TOP) {
+		*paddr = kvaddr & MIPS64_XKPHYS_PHYS_MASK;
+		return true;
+	}
+
+	if (kvaddr >= MIPS64_KSEG0_BASE && kvaddr <= MIPS64_KSEG0_END) {
+		*paddr = kvaddr & MIPS64_KSEG_MASK;
+		return true;
+	}
+
+	if (kvaddr >= MIPS64_KSEG1_BASE && kvaddr <= MIPS64_KSEG1_END) {
+		*paddr = kvaddr & MIPS64_KSEG_MASK;
+		return true;
+	}
+
+	return false;
+}
+
+
+
+/*
+ * MIPS64 instruction helpers for the generic unwinder.
+ */
+
+/**
+ * is_sp_move_ins_mips64 - detect stack pointer adjustment
+ *
+ * Matches:
+ *   addiu sp, sp, imm   (opcode 0x09, rs=29, rt=29)
+ *   daddiu sp, sp, imm  (opcode 0x19, rs=29, rt=29)
+ *
+ * Also detects (but ignores) dynamic stack allocation:
+ *   dsubu sp, sp, v0     (R-format, func=0x2f, rs=29, rd=29, rt=2)
+ */
+static bool is_sp_move_ins_mips64(uint32_t insn, int *stack_adj)
+{
+	unsigned int opcode = mips_opcode(insn);
+	unsigned int rs = mips_rs(insn);
+	unsigned int rt = mips_rt(insn);
+
+	/* addiu sp, sp, imm  or  daddiu sp, sp, imm */
+	if (rs == MIPS_REG_SP && rt == MIPS_REG_SP) {
+		if (opcode == MIPS_OP_ADDIU || opcode == MIPS_OP_DADDIU) {
+			*stack_adj = (int)mips_simmediate(insn);
+			return true;
+		}
+	}
+
+	/* dsubu sp, sp, v0 — dynamic stack allocation, log but don't report */
+	if (opcode == MIPS_OP_SPECIAL &&
+		mips_func(insn) == MIPS_FUNC_DSUBU &&
+		rs == MIPS_REG_SP &&
+		mips_rd(insn) == MIPS_REG_SP &&
+		rt == 2) {
+		pr_trace("mips64: dynamic stack allocation (dsubu sp,sp,v0) at insn=0x%08x\n", insn);
+	}
+
+	return false;
+}
+
+/**
+ * is_ra_save_ins_mips64 - detect RA save to stack
+ *
+ * Matches:
+ *   sw ra, offset(sp)   (opcode 0x2b, rs=29, rt=31)
+ *   sd ra, offset(sp)   (opcode 0x3f, rs=29, rt=31)
+ */
+static bool is_ra_save_ins_mips64(uint32_t insn, unsigned long *ra_offset)
+{
+	unsigned int opcode = mips_opcode(insn);
+	unsigned int rs = mips_rs(insn);
+	unsigned int rt = mips_rt(insn);
+
+	if ((opcode == MIPS_OP_SW || opcode == MIPS_OP_SD) &&
+		rs == MIPS_REG_SP && rt == MIPS_REG_RA) {
+		int16_t imm = mips_simmediate(insn);
+
+		if (imm < 0)
+			return false;
+
+		*ra_offset = (unsigned long)imm;
+		return true;
+	}
+
+	return false;
+}
+
+/**
+ * is_end_of_backtrace_mips64 - detect end-of-backtrace marker
+ *
+ * The kernel uses 0xf825 (move ra, zero / or ra, zero, zero)
+ * as a sentinel indicating no further frames.
+ */
+static bool is_end_of_backtrace_mips64(uint32_t insn)
+{
+	if (insn == MIPS_END_OF_BT_MARKER) {
+		pr_trace("mips64: found end of backtrace marker\n");
+		return true;
+	}
+	return false;
+}
+
+/**
+ * read_task_registers - MIPS64 override
+ */
+bool read_task_registers(uint64_t stack_ptr, struct pt_regs *regs)
+{
+	uint64_t regs_addr;
+	size_t pt_regs_size = SIZE("pt_regs");
+	size_t thread_size = NUMBER("THREAD_SIZE");
+
+	if (!stack_ptr) {
+		fprintf(stderr, "Invalid stack pointer\n");
+		return false;
+	}
+
+	regs_addr = stack_ptr + thread_size - 32 - pt_regs_size;
+
+	pr_verbose("mips64 read_task_registers: stack=0x%llx thread_size=%zu "
+		 "pt_regs_size=%zu regs_addr=0x%llx\n",
+		 (unsigned long long)stack_ptr, thread_size,
+		 pt_regs_size, (unsigned long long)regs_addr);
+
+	if (readmem(regs_addr, regs, pt_regs_size, "read pt_regs", KVADDR) < 0) {
+		fprintf(stderr, "Failed to read pt_regs from 0x%016llx\n",
+			(unsigned long long)regs_addr);
+		return false;
+	}
+
+	return true;
+}
+
+static inline bool mips64_is_rt_sigreturn_nr(uint32_t nr)
+{
+	return nr == MIPS_NR_RT_SIGRETURN_O32 ||
+	       nr == MIPS_NR_RT_SIGRETURN_N64 ||
+	       nr == MIPS_NR_RT_SIGRETURN_N32;
+}
+
+/*
+ * Match "li v0, __NR_rt_sigreturn" forms used before syscall:
+ *   addiu  v0, zero, imm
+ *   daddiu v0, zero, imm
+ *   ori    v0, zero, imm
+ */
+static inline bool mips64_extract_li_v0_imm(uint32_t insn, uint32_t *imm_out)
+{
+	unsigned int opcode = mips_opcode(insn);
+	unsigned int rs = mips_rs(insn);
+	unsigned int rt = mips_rt(insn);
+
+	if (!imm_out)
+		return false;
+
+	if (rs != 0 || rt != MIPS_REG_V0)
+		return false;
+
+	if (opcode == MIPS_OP_ADDIU ||
+	    opcode == MIPS_OP_DADDIU ||
+	    opcode == MIPS_OP_ORI) {
+		*imm_out = (uint32_t)(insn & 0xffffU);
+		return true;
+	}
+
+	return false;
+}
+
+static bool mips64_is_rt_sigreturn_trampoline(uint64_t li_addr, uint64_t syscall_addr)
+{
+	uint32_t li_insn = 0;
+	uint32_t sc_insn = 0;
+	uint32_t nr = 0;
+
+	if (readmem(li_addr, &li_insn, sizeof(li_insn), "mips sigtramp li", UVADDR) < 0)
+		return false;
+
+	if (readmem(syscall_addr, &sc_insn, sizeof(sc_insn),
+		    "mips sigtramp syscall", UVADDR) < 0)
+		return false;
+
+	if ((sc_insn & MIPS_SYSCALL_MASK) != MIPS_SYSCALL_INS)
+		return false;
+
+	if (!mips64_extract_li_v0_imm(li_insn, &nr))
+		return false;
+
+	if (!mips64_is_rt_sigreturn_nr(nr))
+		return false;
+
+	pr_trace("mips64: matched rt_sigreturn trampoline li at 0x%llx sc at 0x%llx nr=%u\n",
+		 (unsigned long long)li_addr,
+		 (unsigned long long)syscall_addr,
+		 nr);
+
+	return true;
+}
+
+static bool mips64_likely_signal_pc(uint64_t pc)
+{
+	/*
+	 * Deterministic sigtramp detection:
+	 *   [li v0, __NR_rt_sigreturn] ; [syscall]
+	 * Accept if PC is on syscall or on the preceding li.
+	 */
+	if (pc >= 4 && mips64_is_rt_sigreturn_trampoline(pc - 4, pc))
+		return true;
+
+	if (pc <= (UINT64_MAX - 4) &&
+	    mips64_is_rt_sigreturn_trampoline(pc, pc + 4))
+		return true;
+
+	return false;
+}
+
+static const struct signal_unwind_layout mips64_sig_layout = {
+	.rt_sigframe_uc_key = "rt_sigframe.rs_uc",
+	.ucontext_mcontext_key = "ucontext.uc_mcontext",
+	.sigcontext_regs_key = "sigcontext.sc_regs",
+	.sigcontext_pc_key = "sigcontext.sc_pc",
+	.reg_width = 8,
+	.sp_reg_index = 29,
+	.ra_reg_index = 31,
+	.pc_reg_index = -1,
+	.pc_reg_alt_index = -1,
+};
+
+/**
+ * mips64_init - initialize arch_deps for MIPS64
+ */
+void mips64_init(void)
+{
+	mips64_pagetable_init();
+	arch.unwind_ops.insn_size           = 4;
+	arch.unwind_ops.ra_width            = 8;
+	arch.unwind_ops.is_sp_move_ins      = is_sp_move_ins_mips64;
+	arch.unwind_ops.is_ra_save_ins      = is_ra_save_ins_mips64;
+	arch.unwind_ops.is_end_of_backtrace = is_end_of_backtrace_mips64;
+	arch.unwind_ops.post_unwind_fixup   = NULL;
+	arch.unwind_ops.likely_signal_pc    = mips64_likely_signal_pc;
+	arch.unwind_ops.signal_layout       = &mips64_sig_layout;
+
+	arch.vtop = mips64_vtop;
+}
+
+void write_user_registers_formatted(FILE *fp, struct pt_regs *regs)
+{
+	fprintf(fp, "user registers:\n");
+	for (int i = 0; i < 32; i += 4) {
+		fprintf(fp, "R%02d:0x%016llx R%02d:0x%016llx R%02d:0x%016llx R%02d:0x%016llx\n",
+			i,     (unsigned long long)regs->regs[i],
+			i + 1, (unsigned long long)regs->regs[i + 1],
+			i + 2, (unsigned long long)regs->regs[i + 2],
+			i + 3, (unsigned long long)regs->regs[i + 3]);
+	}
+	fprintf(fp, "cp0_status:  0x%016llx   hi:       0x%016llx   lo:     0x%016llx\n",
+		(unsigned long long)regs->cp0_status,
+		(unsigned long long)regs->hi, (unsigned long long)regs->lo);
+	fprintf(fp, "cp0_badvaddr:0x%016llx   cp0_cause:0x%016llx   cp0_epc:0x%016llx\n",
+		(unsigned long long)regs->cp0_badvaddr,
+		(unsigned long long)regs->cp0_cause,
+		(unsigned long long)regs->cp0_epc);
+}
\ No newline at end of file
diff --git a/vmcore_tasks/arch/mips64/mips64_defs.h b/vmcore_tasks/arch/mips64/mips64_defs.h
new file mode 100644
index 00000000..97bcdac7
--- /dev/null
+++ b/vmcore_tasks/arch/mips64/mips64_defs.h
@@ -0,0 +1,78 @@
+#ifndef MIPS64_DEFS_H
+#define MIPS64_DEFS_H
+
+#include <stdint.h>
+
+#include "vmcore_info.h"
+
+/* Page table definitions */
+#define SYS_PAGE_SIZE       (get_page_size())
+#define SYS_PAGE_SHIFT      (get_page_shift())
+#define SYS_PAGE_MASK       (~(SYS_PAGE_SIZE - 1))
+
+/*
+ * MIPS64 PTE bit layout (from vmcoreinfo):
+ *
+ *   _PFN_SHIFT   = 15   (bits 0-14 are flags, PFN starts at bit 15)
+ *   PMD_SHIFT    = 25
+ *   PGDIR_SHIFT  = 36
+ *   PGTABLE_LEVELS = 3  (PGD → PMD → PTE)
+ *
+ * Physical address from a leaf PTE:
+ *   paddr = (pte >> _PFN_SHIFT) << PAGE_SHIFT
+ *
+ * PGD and PMD entries store physical addresses of next-level tables
+ * directly — they do NOT use _PFN_SHIFT encoding.
+ */
+
+/* PTE flag bits (bits below _PFN_SHIFT) */
+#define MIPS64_PTE_PRESENT   (1UL << 0)
+
+/* MIPS instruction format helpers */
+
+/* I-format: opcode(6) | rs(5) | rt(5) | immediate(16) */
+static inline unsigned int mips_opcode(uint32_t insn)   { return (insn >> 26) & 0x3f; }
+static inline unsigned int mips_rs(uint32_t insn)       { return (insn >> 21) & 0x1f; }
+static inline unsigned int mips_rt(uint32_t insn)       { return (insn >> 16) & 0x1f; }
+static inline int16_t      mips_simmediate(uint32_t insn) { return (int16_t)(insn & 0xffff); }
+
+/* R-format: opcode(6) | rs(5) | rt(5) | rd(5) | sa(5) | func(6) */
+static inline unsigned int mips_rd(uint32_t insn)       { return (insn >> 11) & 0x1f; }
+static inline unsigned int mips_func(uint32_t insn)     { return insn & 0x3f; }
+
+/* MIPS opcodes */
+#define MIPS_OP_ADDIU    0x09
+#define MIPS_OP_DADDIU   0x19
+#define MIPS_OP_SW       0x2b
+#define MIPS_OP_SD       0x3f
+#define MIPS_OP_SPECIAL  0x00
+
+/* MIPS R-format function codes */
+#define MIPS_FUNC_DSUBU  0x2f
+
+/* MIPS register numbers */
+#define MIPS_REG_SP      29
+#define MIPS_REG_RA      31
+
+struct pt_regs {
+	/* Saved main processor registers. */
+	unsigned long regs[32];
+
+	/* Saved special registers. */
+	unsigned long cp0_status;
+	unsigned long hi;
+	unsigned long lo;
+	unsigned long cp0_badvaddr;
+	unsigned long cp0_cause;
+	unsigned long cp0_epc;
+	unsigned long __last[0];
+};
+
+#define PT_REGS_PC(X)   ((X).cp0_epc)
+#define PT_REGS_RA(X)   ((X).regs[MIPS_REG_RA])
+#define PT_REGS_SP(X)   ((X).regs[MIPS_REG_SP])
+
+/* End of backtrace marker */
+#define MIPS_END_OF_BT_MARKER  0xf825
+
+#endif /* MIPS64_DEFS_H */
\ No newline at end of file
-- 
2.43.0




More information about the kexec mailing list