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
