1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Ptrace test for GPR/FPR registers |
4 | * |
5 | * Copyright (C) 2015 Anshuman Khandual, IBM Corporation. |
6 | */ |
7 | #include "ptrace.h" |
8 | #include "ptrace-gpr.h" |
9 | #include "reg.h" |
10 | #include <time.h> |
11 | |
12 | /* Tracer and Tracee Shared Data */ |
13 | int shm_id; |
14 | int *cptr, *pptr; |
15 | |
16 | extern void gpr_child_loop(int *read_flag, int *write_flag, |
17 | unsigned long *gpr_buf, double *fpr_buf); |
18 | |
19 | unsigned long child_gpr_val, parent_gpr_val; |
20 | double child_fpr_val, parent_fpr_val; |
21 | |
22 | static int child(void) |
23 | { |
24 | unsigned long gpr_buf[32]; |
25 | double fpr_buf[32]; |
26 | int i; |
27 | |
28 | cptr = (int *)shmat(shm_id, NULL, 0); |
29 | memset(gpr_buf, 0, sizeof(gpr_buf)); |
30 | memset(fpr_buf, 0, sizeof(fpr_buf)); |
31 | |
32 | for (i = 0; i < 32; i++) { |
33 | gpr_buf[i] = child_gpr_val; |
34 | fpr_buf[i] = child_fpr_val; |
35 | } |
36 | |
37 | gpr_child_loop(read_flag: &cptr[0], write_flag: &cptr[1], gpr_buf, fpr_buf); |
38 | |
39 | shmdt((void *)cptr); |
40 | |
41 | FAIL_IF(validate_gpr(gpr: gpr_buf, val: parent_gpr_val)); |
42 | FAIL_IF(validate_fpr_double(fpr: fpr_buf, val: parent_fpr_val)); |
43 | |
44 | return 0; |
45 | } |
46 | |
47 | int trace_gpr(pid_t child) |
48 | { |
49 | __u64 tmp, fpr[32], *peeked_fprs; |
50 | unsigned long gpr[18]; |
51 | |
52 | FAIL_IF(start_trace(child)); |
53 | |
54 | // Check child GPRs match what we expect using GETREGS |
55 | FAIL_IF(show_gpr(child, gpr)); |
56 | FAIL_IF(validate_gpr(gpr, val: child_gpr_val)); |
57 | |
58 | // Check child FPRs match what we expect using GETFPREGS |
59 | FAIL_IF(show_fpr(child, fpr)); |
60 | memcpy(&tmp, &child_fpr_val, sizeof(tmp)); |
61 | FAIL_IF(validate_fpr(fpr, val: tmp)); |
62 | |
63 | // Check child FPRs match what we expect using PEEKUSR |
64 | peeked_fprs = peek_fprs(child); |
65 | FAIL_IF(!peeked_fprs); |
66 | FAIL_IF(validate_fpr(fpr: peeked_fprs, val: tmp)); |
67 | free(peeked_fprs); |
68 | |
69 | // Write child GPRs using SETREGS |
70 | FAIL_IF(write_gpr(child, val: parent_gpr_val)); |
71 | |
72 | // Write child FPRs using SETFPREGS |
73 | memcpy(&tmp, &parent_fpr_val, sizeof(tmp)); |
74 | FAIL_IF(write_fpr(child, val: tmp)); |
75 | |
76 | // Check child FPRs match what we just set, using PEEKUSR |
77 | peeked_fprs = peek_fprs(child); |
78 | FAIL_IF(!peeked_fprs); |
79 | FAIL_IF(validate_fpr(fpr: peeked_fprs, val: tmp)); |
80 | |
81 | // Write child FPRs using POKEUSR |
82 | FAIL_IF(poke_fprs(child, fprs: (unsigned long *)peeked_fprs)); |
83 | |
84 | // Child will check its FPRs match before exiting |
85 | FAIL_IF(stop_trace(child)); |
86 | |
87 | return TEST_PASS; |
88 | } |
89 | |
90 | #ifndef __LONG_WIDTH__ |
91 | #define __LONG_WIDTH__ (sizeof(long) * 8) |
92 | #endif |
93 | |
94 | static uint64_t rand_reg(void) |
95 | { |
96 | uint64_t result; |
97 | long r; |
98 | |
99 | r = random(); |
100 | |
101 | // Small values are typical |
102 | result = r & 0xffff; |
103 | if (r & 0x10000) |
104 | return result; |
105 | |
106 | // Pointers tend to have high bits set |
107 | result |= random() << (__LONG_WIDTH__ - 31); |
108 | if (r & 0x100000) |
109 | return result; |
110 | |
111 | // And sometimes we want a full 64-bit value |
112 | result ^= random() << 16; |
113 | |
114 | return result; |
115 | } |
116 | |
117 | int ptrace_gpr(void) |
118 | { |
119 | unsigned long seed; |
120 | int ret, status; |
121 | pid_t pid; |
122 | |
123 | seed = getpid() ^ time(NULL); |
124 | printf("srand(%lu)\n" , seed); |
125 | srand(seed); |
126 | |
127 | child_gpr_val = rand_reg(); |
128 | child_fpr_val = rand_reg(); |
129 | parent_gpr_val = rand_reg(); |
130 | parent_fpr_val = rand_reg(); |
131 | |
132 | shm_id = shmget(IPC_PRIVATE, sizeof(int) * 2, 0777|IPC_CREAT); |
133 | pid = fork(); |
134 | if (pid < 0) { |
135 | perror("fork() failed" ); |
136 | return TEST_FAIL; |
137 | } |
138 | if (pid == 0) |
139 | exit(child()); |
140 | |
141 | if (pid) { |
142 | pptr = (int *)shmat(shm_id, NULL, 0); |
143 | while (!pptr[1]) |
144 | asm volatile("" : : : "memory" ); |
145 | |
146 | ret = trace_gpr(child: pid); |
147 | if (ret) { |
148 | kill(pid, SIGTERM); |
149 | shmdt((void *)pptr); |
150 | shmctl(shm_id, IPC_RMID, NULL); |
151 | return TEST_FAIL; |
152 | } |
153 | |
154 | pptr[0] = 1; |
155 | shmdt((void *)pptr); |
156 | |
157 | ret = wait(&status); |
158 | shmctl(shm_id, IPC_RMID, NULL); |
159 | if (ret != pid) { |
160 | printf("Child's exit status not captured\n" ); |
161 | return TEST_FAIL; |
162 | } |
163 | |
164 | return (WIFEXITED(status) && WEXITSTATUS(status)) ? TEST_FAIL : |
165 | TEST_PASS; |
166 | } |
167 | |
168 | return TEST_PASS; |
169 | } |
170 | |
171 | int main(int argc, char *argv[]) |
172 | { |
173 | return test_harness(ptrace_gpr, "ptrace_gpr" ); |
174 | } |
175 | |