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

Pnina Feder <[email protected]>
Newsgroups org.infradead.lists.kexec
Message-ID <[email protected]>
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 <[email protected]>
---
 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@0x%llx sc@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
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.