| /* | |
| * Ptrace interface test helper functions | |
| * | |
| * Copyright (C) 2015 Anshuman Khandual, IBM Corporation. | |
| * | |
| * This program is free software; you can redistribute it and/or | |
| * modify it under the terms of the GNU General Public License | |
| * as published by the Free Software Foundation; either version | |
| * 2 of the License, or (at your option) any later version. | |
| */ | |
| #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 <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 { | |
| unsigned long fpr[32]; | |
| unsigned long 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; | |
| } | |
| /* 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, unsigned long *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, unsigned long 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, unsigned long *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; | |
| } | |
| 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) | |
| { | |
| unsigned long ret; | |
| asm volatile("mfspr %0,%1" : "=r" (ret) : "i" (SPRN_TFIAR)); | |
| return ret; | |
| } | |
| 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); | |
| void store_fpr(float *addr); |