/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * Ptrace interface test helper functions * * Copyright (C) 2015 Anshuman Khandual, IBM Corporation. */ #define __SANE_USERSPACE_TYPES__ #include <inttypes.h> #include <unistd.h> #include <stdlib.h> #include <string.h> #include <malloc.h> #include <errno.h> #include <time.h> #include <sys/ptrace.h> #include <sys/ioctl.h> #include <sys/uio.h> #include <sys/types.h> #include <sys/wait.h> #include <sys/signal.h> #include <sys/ipc.h> #include <sys/shm.h> #include <sys/user.h> #include <sys/syscall.h> #include <linux/elf.h> #include <linux/types.h> #include <linux/auxvec.h> #include "reg.h" #include "utils.h" #define TEST_PASS 0 #define TEST_FAIL 1 struct fpr_regs { __u64 fpr[32]; __u64 fpscr; }; struct tm_spr_regs { unsigned long tm_tfhar; unsigned long tm_texasr; unsigned long tm_tfiar; }; #ifndef NT_PPC_TAR #define NT_PPC_TAR 0x103 #define NT_PPC_PPR 0x104 #define NT_PPC_DSCR 0x105 #define NT_PPC_EBB 0x106 #define NT_PPC_PMU 0x107 #define NT_PPC_TM_CGPR 0x108 #define NT_PPC_TM_CFPR 0x109 #define NT_PPC_TM_CVMX 0x10a #define NT_PPC_TM_CVSX 0x10b #define NT_PPC_TM_SPR 0x10c #define NT_PPC_TM_CTAR 0x10d #define NT_PPC_TM_CPPR 0x10e #define NT_PPC_TM_CDSCR 0x10f #endif /* Basic ptrace operations */ int start_trace(pid_t child) { int ret; ret = ptrace(PTRACE_ATTACH, child, NULL, NULL); if (ret) { perror("ptrace(PTRACE_ATTACH) failed"); return TEST_FAIL; } ret = waitpid(child, NULL, 0); if (ret != child) { perror("waitpid() failed"); return TEST_FAIL; } return TEST_PASS; } int stop_trace(pid_t child) { int ret; ret = ptrace(PTRACE_DETACH, child, NULL, NULL); if (ret) { perror("ptrace(PTRACE_DETACH) failed"); return TEST_FAIL; } return TEST_PASS; } int cont_trace(pid_t child) { int ret; ret = ptrace(PTRACE_CONT, child, NULL, NULL); if (ret) { perror("ptrace(PTRACE_CONT) failed"); return TEST_FAIL; } return TEST_PASS; } int ptrace_read_regs(pid_t child, unsigned long type, unsigned long regs[], int n) { struct iovec iov; long ret; FAIL_IF(start_trace(child)); iov.iov_base = regs; iov.iov_len = n * sizeof(unsigned long); ret = ptrace(PTRACE_GETREGSET, child, type, &iov); if (ret) return ret; FAIL_IF(stop_trace(child)); return TEST_PASS; } long ptrace_write_regs(pid_t child, unsigned long type, unsigned long regs[], int n) { struct iovec iov; long ret; FAIL_IF(start_trace(child)); iov.iov_base = regs; iov.iov_len = n * sizeof(unsigned long); ret = ptrace(PTRACE_SETREGSET, child, type, &iov); FAIL_IF(stop_trace(child)); return ret; } /* TAR, PPR, DSCR */ int show_tar_registers(pid_t child, unsigned long *out) { struct iovec iov; unsigned long *reg; int ret; reg = malloc(sizeof(unsigned long)); if (!reg) { perror("malloc() failed"); return TEST_FAIL; } iov.iov_base = (u64 *) reg; iov.iov_len = sizeof(unsigned long); ret = ptrace(PTRACE_GETREGSET, child, NT_PPC_TAR, &iov); if (ret) { perror("ptrace(PTRACE_GETREGSET) failed"); goto fail; } if (out) out[0] = *reg; ret = ptrace(PTRACE_GETREGSET, child, NT_PPC_PPR, &iov); if (ret) { perror("ptrace(PTRACE_GETREGSET) failed"); goto fail; } if (out) out[1] = *reg; ret = ptrace(PTRACE_GETREGSET, child, NT_PPC_DSCR, &iov); if (ret) { perror("ptrace(PTRACE_GETREGSET) failed"); goto fail; } if (out) out[2] = *reg; free(reg); return TEST_PASS; fail: free(reg); return TEST_FAIL; } int write_tar_registers(pid_t child, unsigned long tar, unsigned long ppr, unsigned long dscr) { struct iovec iov; unsigned long *reg; int ret; reg = malloc(sizeof(unsigned long)); if (!reg) { perror("malloc() failed"); return TEST_FAIL; } iov.iov_base = (u64 *) reg; iov.iov_len = sizeof(unsigned long); *reg = tar; ret = ptrace(PTRACE_SETREGSET, child, NT_PPC_TAR, &iov); if (ret) { perror("ptrace(PTRACE_SETREGSET) failed"); goto fail; } *reg = ppr; ret = ptrace(PTRACE_SETREGSET, child, NT_PPC_PPR, &iov); if (ret) { perror("ptrace(PTRACE_SETREGSET) failed"); goto fail; } *reg = dscr; ret = ptrace(PTRACE_SETREGSET, child, NT_PPC_DSCR, &iov); if (ret) { perror("ptrace(PTRACE_SETREGSET) failed"); goto fail; } free(reg); return TEST_PASS; fail: free(reg); return TEST_FAIL; } int show_tm_checkpointed_state(pid_t child, unsigned long *out) { struct iovec iov; unsigned long *reg; int ret; reg = malloc(sizeof(unsigned long)); if (!reg) { perror("malloc() failed"); return TEST_FAIL; } iov.iov_base = (u64 *) reg; iov.iov_len = sizeof(unsigned long); ret = ptrace(PTRACE_GETREGSET, child, NT_PPC_TM_CTAR, &iov); if (ret) { perror("ptrace(PTRACE_GETREGSET) failed"); goto fail; } if (out) out[0] = *reg; ret = ptrace(PTRACE_GETREGSET, child, NT_PPC_TM_CPPR, &iov); if (ret) { perror("ptrace(PTRACE_GETREGSET) failed"); goto fail; } if (out) out[1] = *reg; ret = ptrace(PTRACE_GETREGSET, child, NT_PPC_TM_CDSCR, &iov); if (ret) { perror("ptrace(PTRACE_GETREGSET) failed"); goto fail; } if (out) out[2] = *reg; free(reg); return TEST_PASS; fail: free(reg); return TEST_FAIL; } int write_ckpt_tar_registers(pid_t child, unsigned long tar, unsigned long ppr, unsigned long dscr) { struct iovec iov; unsigned long *reg; int ret; reg = malloc(sizeof(unsigned long)); if (!reg) { perror("malloc() failed"); return TEST_FAIL; } iov.iov_base = (u64 *) reg; iov.iov_len = sizeof(unsigned long); *reg = tar; ret = ptrace(PTRACE_SETREGSET, child, NT_PPC_TM_CTAR, &iov); if (ret) { perror("ptrace(PTRACE_GETREGSET) failed"); goto fail; } *reg = ppr; ret = ptrace(PTRACE_SETREGSET, child, NT_PPC_TM_CPPR, &iov); if (ret) { perror("ptrace(PTRACE_GETREGSET) failed"); goto fail; } *reg = dscr; ret = ptrace(PTRACE_SETREGSET, child, NT_PPC_TM_CDSCR, &iov); if (ret) { perror("ptrace(PTRACE_GETREGSET) failed"); goto fail; } free(reg); return TEST_PASS; fail: free(reg); return TEST_FAIL; } /* FPR */ int show_fpr(pid_t child, __u64 *fpr) { struct fpr_regs *regs; int ret, i; regs = (struct fpr_regs *) malloc(sizeof(struct fpr_regs)); ret = ptrace(PTRACE_GETFPREGS, child, NULL, regs); if (ret) { perror("ptrace(PTRACE_GETREGSET) failed"); return TEST_FAIL; } if (fpr) { for (i = 0; i < 32; i++) fpr[i] = regs->fpr[i]; } return TEST_PASS; } int write_fpr(pid_t child, __u64 val) { struct fpr_regs *regs; int ret, i; regs = (struct fpr_regs *) malloc(sizeof(struct fpr_regs)); ret = ptrace(PTRACE_GETFPREGS, child, NULL, regs); if (ret) { perror("ptrace(PTRACE_GETREGSET) failed"); return TEST_FAIL; } for (i = 0; i < 32; i++) regs->fpr[i] = val; ret = ptrace(PTRACE_SETFPREGS, child, NULL, regs); if (ret) { perror("ptrace(PTRACE_GETREGSET) failed"); return TEST_FAIL; } return TEST_PASS; } int show_ckpt_fpr(pid_t child, __u64 *fpr) { struct fpr_regs *regs; struct iovec iov; int ret, i; regs = (struct fpr_regs *) malloc(sizeof(struct fpr_regs)); iov.iov_base = regs; iov.iov_len = sizeof(struct fpr_regs); ret = ptrace(PTRACE_GETREGSET, child, NT_PPC_TM_CFPR, &iov); if (ret) { perror("ptrace(PTRACE_GETREGSET) failed"); return TEST_FAIL; } if (fpr) { for (i = 0; i < 32; i++) fpr[i] = regs->fpr[i]; } return TEST_PASS; } int write_ckpt_fpr(pid_t child, unsigned long val) { struct fpr_regs *regs; struct iovec iov; int ret, i; regs = (struct fpr_regs *) malloc(sizeof(struct fpr_regs)); iov.iov_base = regs; iov.iov_len = sizeof(struct fpr_regs); ret = ptrace(PTRACE_GETREGSET, child, NT_PPC_TM_CFPR, &iov); if (ret) { perror("ptrace(PTRACE_GETREGSET) failed"); return TEST_FAIL; } for (i = 0; i < 32; i++) regs->fpr[i] = val; ret = ptrace(PTRACE_SETREGSET, child, NT_PPC_TM_CFPR, &iov); if (ret) { perror("ptrace(PTRACE_GETREGSET) failed"); return TEST_FAIL; } return TEST_PASS; } /* GPR */ int show_gpr(pid_t child, unsigned long *gpr) { struct pt_regs *regs; int ret, i; regs = (struct pt_regs *) malloc(sizeof(struct pt_regs)); if (!regs) { perror("malloc() failed"); return TEST_FAIL; } ret = ptrace(PTRACE_GETREGS, child, NULL, regs); if (ret) { perror("ptrace(PTRACE_GETREGSET) failed"); return TEST_FAIL; } if (gpr) { for (i = 14; i < 32; i++) gpr[i-14] = regs->gpr[i]; } return TEST_PASS; } long sys_ptrace(enum __ptrace_request request, pid_t pid, unsigned long addr, unsigned long data) { return syscall(__NR_ptrace, request, pid, (void *)addr, data); } // 33 because of FPSCR #define PT_NUM_FPRS (33 * (sizeof(__u64) / sizeof(unsigned long))) __u64 *peek_fprs(pid_t child) { unsigned long *fprs, *p, addr; long ret; int i; fprs = malloc(sizeof(unsigned long) * PT_NUM_FPRS); if (!fprs) { perror("malloc() failed"); return NULL; } for (i = 0, p = fprs; i < PT_NUM_FPRS; i++, p++) { addr = sizeof(unsigned long) * (PT_FPR0 + i); ret = sys_ptrace(PTRACE_PEEKUSER, child, addr, (unsigned long)p); if (ret) { perror("ptrace(PTRACE_PEEKUSR) failed"); return NULL; } } addr = sizeof(unsigned long) * (PT_FPR0 + i); ret = sys_ptrace(PTRACE_PEEKUSER, child, addr, (unsigned long)&addr); if (!ret) { printf("ptrace(PTRACE_PEEKUSR) succeeded unexpectedly!\n"); return NULL; } return (__u64 *)fprs; } int poke_fprs(pid_t child, unsigned long *fprs) { unsigned long *p, addr; long ret; int i; for (i = 0, p = fprs; i < PT_NUM_FPRS; i++, p++) { addr = sizeof(unsigned long) * (PT_FPR0 + i); ret = sys_ptrace(PTRACE_POKEUSER, child, addr, *p); if (ret) { perror("ptrace(PTRACE_POKEUSR) failed"); return -1; } } addr = sizeof(unsigned long) * (PT_FPR0 + i); ret = sys_ptrace(PTRACE_POKEUSER, child, addr, addr); if (!ret) { printf("ptrace(PTRACE_POKEUSR) succeeded unexpectedly!\n"); return -1; } return 0; } int write_gpr(pid_t child, unsigned long val) { struct pt_regs *regs; int i, ret; regs = (struct pt_regs *) malloc(sizeof(struct pt_regs)); if (!regs) { perror("malloc() failed"); return TEST_FAIL; } ret = ptrace(PTRACE_GETREGS, child, NULL, regs); if (ret) { perror("ptrace(PTRACE_GETREGSET) failed"); return TEST_FAIL; } for (i = 14; i < 32; i++) regs->gpr[i] = val; ret = ptrace(PTRACE_SETREGS, child, NULL, regs); if (ret) { perror("ptrace(PTRACE_GETREGSET) failed"); return TEST_FAIL; } return TEST_PASS; } int show_ckpt_gpr(pid_t child, unsigned long *gpr) { struct pt_regs *regs; struct iovec iov; int ret, i; regs = (struct pt_regs *) malloc(sizeof(struct pt_regs)); if (!regs) { perror("malloc() failed"); return TEST_FAIL; } iov.iov_base = (u64 *) regs; iov.iov_len = sizeof(struct pt_regs); ret = ptrace(PTRACE_GETREGSET, child, NT_PPC_TM_CGPR, &iov); if (ret) { perror("ptrace(PTRACE_GETREGSET) failed"); return TEST_FAIL; } if (gpr) { for (i = 14; i < 32; i++) gpr[i-14] = regs->gpr[i]; } return TEST_PASS; } int write_ckpt_gpr(pid_t child, unsigned long val) { struct pt_regs *regs; struct iovec iov; int ret, i; regs = (struct pt_regs *) malloc(sizeof(struct pt_regs)); if (!regs) { perror("malloc() failed\n"); return TEST_FAIL; } iov.iov_base = (u64 *) regs; iov.iov_len = sizeof(struct pt_regs); ret = ptrace(PTRACE_GETREGSET, child, NT_PPC_TM_CGPR, &iov); if (ret) { perror("ptrace(PTRACE_GETREGSET) failed"); return TEST_FAIL; } for (i = 14; i < 32; i++) regs->gpr[i] = val; ret = ptrace(PTRACE_SETREGSET, child, NT_PPC_TM_CGPR, &iov); if (ret) { perror("ptrace(PTRACE_GETREGSET) failed"); return TEST_FAIL; } return TEST_PASS; } /* VMX */ int show_vmx(pid_t child, unsigned long vmx[][2]) { int ret; ret = ptrace(PTRACE_GETVRREGS, child, 0, vmx); if (ret) { perror("ptrace(PTRACE_GETVRREGS) failed"); return TEST_FAIL; } return TEST_PASS; } int show_vmx_ckpt(pid_t child, unsigned long vmx[][2]) { unsigned long regs[34][2]; struct iovec iov; int ret; iov.iov_base = (u64 *) regs; iov.iov_len = sizeof(regs); ret = ptrace(PTRACE_GETREGSET, child, NT_PPC_TM_CVMX, &iov); if (ret) { perror("ptrace(PTRACE_GETREGSET, NT_PPC_TM_CVMX) failed"); return TEST_FAIL; } memcpy(vmx, regs, sizeof(regs)); return TEST_PASS; } int write_vmx(pid_t child, unsigned long vmx[][2]) { int ret; ret = ptrace(PTRACE_SETVRREGS, child, 0, vmx); if (ret) { perror("ptrace(PTRACE_SETVRREGS) failed"); return TEST_FAIL; } return TEST_PASS; } int write_vmx_ckpt(pid_t child, unsigned long vmx[][2]) { unsigned long regs[34][2]; struct iovec iov; int ret; memcpy(regs, vmx, sizeof(regs)); iov.iov_base = (u64 *) regs; iov.iov_len = sizeof(regs); ret = ptrace(PTRACE_SETREGSET, child, NT_PPC_TM_CVMX, &iov); if (ret) { perror("ptrace(PTRACE_SETREGSET, NT_PPC_TM_CVMX) failed"); return TEST_FAIL; } return TEST_PASS; } /* VSX */ int show_vsx(pid_t child, unsigned long *vsx) { int ret; ret = ptrace(PTRACE_GETVSRREGS, child, 0, vsx); if (ret) { perror("ptrace(PTRACE_GETVSRREGS) failed"); return TEST_FAIL; } return TEST_PASS; } int show_vsx_ckpt(pid_t child, unsigned long *vsx) { unsigned long regs[32]; struct iovec iov; int ret; iov.iov_base = (u64 *) regs; iov.iov_len = sizeof(regs); ret = ptrace(PTRACE_GETREGSET, child, NT_PPC_TM_CVSX, &iov); if (ret) { perror("ptrace(PTRACE_GETREGSET, NT_PPC_TM_CVSX) failed"); return TEST_FAIL; } memcpy(vsx, regs, sizeof(regs)); return TEST_PASS; } int write_vsx(pid_t child, unsigned long *vsx) { int ret; ret = ptrace(PTRACE_SETVSRREGS, child, 0, vsx); if (ret) { perror("ptrace(PTRACE_SETVSRREGS) failed"); return TEST_FAIL; } return TEST_PASS; } int write_vsx_ckpt(pid_t child, unsigned long *vsx) { unsigned long regs[32]; struct iovec iov; int ret; memcpy(regs, vsx, sizeof(regs)); iov.iov_base = (u64 *) regs; iov.iov_len = sizeof(regs); ret = ptrace(PTRACE_SETREGSET, child, NT_PPC_TM_CVSX, &iov); if (ret) { perror("ptrace(PTRACE_SETREGSET, NT_PPC_TM_CVSX) failed"); return TEST_FAIL; } return TEST_PASS; } /* TM SPR */ int show_tm_spr(pid_t child, struct tm_spr_regs *out) { struct tm_spr_regs *regs; struct iovec iov; int ret; regs = (struct tm_spr_regs *) malloc(sizeof(struct tm_spr_regs)); if (!regs) { perror("malloc() failed"); return TEST_FAIL; } iov.iov_base = (u64 *) regs; iov.iov_len = sizeof(struct tm_spr_regs); ret = ptrace(PTRACE_GETREGSET, child, NT_PPC_TM_SPR, &iov); if (ret) { perror("ptrace(PTRACE_GETREGSET) failed"); return TEST_FAIL; } if (out) memcpy(out, regs, sizeof(struct tm_spr_regs)); return TEST_PASS; } /* Analyse TEXASR after TM failure */ inline unsigned long get_tfiar(void) { return mfspr(SPRN_TFIAR); } void analyse_texasr(unsigned long texasr) { printf("TEXASR: %16lx\t", texasr); if (texasr & TEXASR_FP) printf("TEXASR_FP "); if (texasr & TEXASR_DA) printf("TEXASR_DA "); if (texasr & TEXASR_NO) printf("TEXASR_NO "); if (texasr & TEXASR_FO) printf("TEXASR_FO "); if (texasr & TEXASR_SIC) printf("TEXASR_SIC "); if (texasr & TEXASR_NTC) printf("TEXASR_NTC "); if (texasr & TEXASR_TC) printf("TEXASR_TC "); if (texasr & TEXASR_TIC) printf("TEXASR_TIC "); if (texasr & TEXASR_IC) printf("TEXASR_IC "); if (texasr & TEXASR_IFC) printf("TEXASR_IFC "); if (texasr & TEXASR_ABT) printf("TEXASR_ABT "); if (texasr & TEXASR_SPD) printf("TEXASR_SPD "); if (texasr & TEXASR_HV) printf("TEXASR_HV "); if (texasr & TEXASR_PR) printf("TEXASR_PR "); if (texasr & TEXASR_FS) printf("TEXASR_FS "); if (texasr & TEXASR_TE) printf("TEXASR_TE "); if (texasr & TEXASR_ROT) printf("TEXASR_ROT "); printf("TFIAR :%lx\n", get_tfiar()); } void store_gpr(unsigned long *addr);