| .. | .. |
|---|
| 20 | 20 | #include <signal.h> |
|---|
| 21 | 21 | #include <sys/types.h> |
|---|
| 22 | 22 | #include <sys/wait.h> |
|---|
| 23 | +#include <sys/syscall.h> |
|---|
| 24 | +#include <linux/limits.h> |
|---|
| 23 | 25 | #include "ptrace.h" |
|---|
| 24 | 26 | |
|---|
| 25 | | -/* Breakpoint access modes */ |
|---|
| 26 | | -enum { |
|---|
| 27 | | - BP_X = 1, |
|---|
| 28 | | - BP_RW = 2, |
|---|
| 29 | | - BP_W = 4, |
|---|
| 27 | +#define SPRN_PVR 0x11F |
|---|
| 28 | +#define PVR_8xx 0x00500000 |
|---|
| 29 | + |
|---|
| 30 | +bool is_8xx; |
|---|
| 31 | + |
|---|
| 32 | +/* |
|---|
| 33 | + * Use volatile on all global var so that compiler doesn't |
|---|
| 34 | + * optimise their load/stores. Otherwise selftest can fail. |
|---|
| 35 | + */ |
|---|
| 36 | +static volatile __u64 glvar; |
|---|
| 37 | + |
|---|
| 38 | +#define DAWR_MAX_LEN 512 |
|---|
| 39 | +static volatile __u8 big_var[DAWR_MAX_LEN] __attribute__((aligned(512))); |
|---|
| 40 | + |
|---|
| 41 | +#define A_LEN 6 |
|---|
| 42 | +#define B_LEN 6 |
|---|
| 43 | +struct gstruct { |
|---|
| 44 | + __u8 a[A_LEN]; /* double word aligned */ |
|---|
| 45 | + __u8 b[B_LEN]; /* double word unaligned */ |
|---|
| 30 | 46 | }; |
|---|
| 47 | +static volatile struct gstruct gstruct __attribute__((aligned(512))); |
|---|
| 31 | 48 | |
|---|
| 32 | | -static pid_t child_pid; |
|---|
| 33 | | -static struct ppc_debug_info dbginfo; |
|---|
| 49 | +static volatile char cwd[PATH_MAX] __attribute__((aligned(8))); |
|---|
| 34 | 50 | |
|---|
| 35 | | -static void get_dbginfo(void) |
|---|
| 51 | +static void get_dbginfo(pid_t child_pid, struct ppc_debug_info *dbginfo) |
|---|
| 36 | 52 | { |
|---|
| 37 | | - int ret; |
|---|
| 38 | | - |
|---|
| 39 | | - ret = ptrace(PPC_PTRACE_GETHWDBGINFO, child_pid, NULL, &dbginfo); |
|---|
| 40 | | - if (ret) { |
|---|
| 41 | | - perror("Can't get breakpoint info\n"); |
|---|
| 53 | + if (ptrace(PPC_PTRACE_GETHWDBGINFO, child_pid, NULL, dbginfo)) { |
|---|
| 54 | + perror("Can't get breakpoint info"); |
|---|
| 42 | 55 | exit(-1); |
|---|
| 43 | 56 | } |
|---|
| 44 | 57 | } |
|---|
| 45 | 58 | |
|---|
| 46 | | -static bool hwbreak_present(void) |
|---|
| 59 | +static bool dawr_present(struct ppc_debug_info *dbginfo) |
|---|
| 47 | 60 | { |
|---|
| 48 | | - return (dbginfo.num_data_bps != 0); |
|---|
| 61 | + return !!(dbginfo->features & PPC_DEBUG_FEATURE_DATA_BP_DAWR); |
|---|
| 49 | 62 | } |
|---|
| 50 | | - |
|---|
| 51 | | -static bool dawr_present(void) |
|---|
| 52 | | -{ |
|---|
| 53 | | - return !!(dbginfo.features & PPC_DEBUG_FEATURE_DATA_BP_DAWR); |
|---|
| 54 | | -} |
|---|
| 55 | | - |
|---|
| 56 | | -static void set_breakpoint_addr(void *addr) |
|---|
| 57 | | -{ |
|---|
| 58 | | - int ret; |
|---|
| 59 | | - |
|---|
| 60 | | - ret = ptrace(PTRACE_SET_DEBUGREG, child_pid, 0, addr); |
|---|
| 61 | | - if (ret) { |
|---|
| 62 | | - perror("Can't set breakpoint addr\n"); |
|---|
| 63 | | - exit(-1); |
|---|
| 64 | | - } |
|---|
| 65 | | -} |
|---|
| 66 | | - |
|---|
| 67 | | -static int set_hwbreakpoint_addr(void *addr, int range) |
|---|
| 68 | | -{ |
|---|
| 69 | | - int ret; |
|---|
| 70 | | - |
|---|
| 71 | | - struct ppc_hw_breakpoint info; |
|---|
| 72 | | - |
|---|
| 73 | | - info.version = 1; |
|---|
| 74 | | - info.trigger_type = PPC_BREAKPOINT_TRIGGER_RW; |
|---|
| 75 | | - info.addr_mode = PPC_BREAKPOINT_MODE_EXACT; |
|---|
| 76 | | - if (range > 0) |
|---|
| 77 | | - info.addr_mode = PPC_BREAKPOINT_MODE_RANGE_INCLUSIVE; |
|---|
| 78 | | - info.condition_mode = PPC_BREAKPOINT_CONDITION_NONE; |
|---|
| 79 | | - info.addr = (__u64)addr; |
|---|
| 80 | | - info.addr2 = (__u64)addr + range; |
|---|
| 81 | | - info.condition_value = 0; |
|---|
| 82 | | - |
|---|
| 83 | | - ret = ptrace(PPC_PTRACE_SETHWDEBUG, child_pid, 0, &info); |
|---|
| 84 | | - if (ret < 0) { |
|---|
| 85 | | - perror("Can't set breakpoint\n"); |
|---|
| 86 | | - exit(-1); |
|---|
| 87 | | - } |
|---|
| 88 | | - return ret; |
|---|
| 89 | | -} |
|---|
| 90 | | - |
|---|
| 91 | | -static int del_hwbreakpoint_addr(int watchpoint_handle) |
|---|
| 92 | | -{ |
|---|
| 93 | | - int ret; |
|---|
| 94 | | - |
|---|
| 95 | | - ret = ptrace(PPC_PTRACE_DELHWDEBUG, child_pid, 0, watchpoint_handle); |
|---|
| 96 | | - if (ret < 0) { |
|---|
| 97 | | - perror("Can't delete hw breakpoint\n"); |
|---|
| 98 | | - exit(-1); |
|---|
| 99 | | - } |
|---|
| 100 | | - return ret; |
|---|
| 101 | | -} |
|---|
| 102 | | - |
|---|
| 103 | | -#define DAWR_LENGTH_MAX 512 |
|---|
| 104 | | - |
|---|
| 105 | | -/* Dummy variables to test read/write accesses */ |
|---|
| 106 | | -static unsigned long long |
|---|
| 107 | | - dummy_array[DAWR_LENGTH_MAX / sizeof(unsigned long long)] |
|---|
| 108 | | - __attribute__((aligned(512))); |
|---|
| 109 | | -static unsigned long long *dummy_var = dummy_array; |
|---|
| 110 | 63 | |
|---|
| 111 | 64 | static void write_var(int len) |
|---|
| 112 | 65 | { |
|---|
| 113 | | - long long *plval; |
|---|
| 114 | | - char *pcval; |
|---|
| 115 | | - short *psval; |
|---|
| 116 | | - int *pival; |
|---|
| 66 | + __u8 *pcvar; |
|---|
| 67 | + __u16 *psvar; |
|---|
| 68 | + __u32 *pivar; |
|---|
| 69 | + __u64 *plvar; |
|---|
| 117 | 70 | |
|---|
| 118 | 71 | switch (len) { |
|---|
| 119 | 72 | case 1: |
|---|
| 120 | | - pcval = (char *)dummy_var; |
|---|
| 121 | | - *pcval = 0xff; |
|---|
| 73 | + pcvar = (__u8 *)&glvar; |
|---|
| 74 | + *pcvar = 0xff; |
|---|
| 122 | 75 | break; |
|---|
| 123 | 76 | case 2: |
|---|
| 124 | | - psval = (short *)dummy_var; |
|---|
| 125 | | - *psval = 0xffff; |
|---|
| 77 | + psvar = (__u16 *)&glvar; |
|---|
| 78 | + *psvar = 0xffff; |
|---|
| 126 | 79 | break; |
|---|
| 127 | 80 | case 4: |
|---|
| 128 | | - pival = (int *)dummy_var; |
|---|
| 129 | | - *pival = 0xffffffff; |
|---|
| 81 | + pivar = (__u32 *)&glvar; |
|---|
| 82 | + *pivar = 0xffffffff; |
|---|
| 130 | 83 | break; |
|---|
| 131 | 84 | case 8: |
|---|
| 132 | | - plval = (long long *)dummy_var; |
|---|
| 133 | | - *plval = 0xffffffffffffffffLL; |
|---|
| 85 | + plvar = (__u64 *)&glvar; |
|---|
| 86 | + *plvar = 0xffffffffffffffffLL; |
|---|
| 134 | 87 | break; |
|---|
| 135 | 88 | } |
|---|
| 136 | 89 | } |
|---|
| 137 | 90 | |
|---|
| 138 | 91 | static void read_var(int len) |
|---|
| 139 | 92 | { |
|---|
| 140 | | - char cval __attribute__((unused)); |
|---|
| 141 | | - short sval __attribute__((unused)); |
|---|
| 142 | | - int ival __attribute__((unused)); |
|---|
| 143 | | - long long lval __attribute__((unused)); |
|---|
| 93 | + __u8 cvar __attribute__((unused)); |
|---|
| 94 | + __u16 svar __attribute__((unused)); |
|---|
| 95 | + __u32 ivar __attribute__((unused)); |
|---|
| 96 | + __u64 lvar __attribute__((unused)); |
|---|
| 144 | 97 | |
|---|
| 145 | 98 | switch (len) { |
|---|
| 146 | 99 | case 1: |
|---|
| 147 | | - cval = *(char *)dummy_var; |
|---|
| 100 | + cvar = (__u8)glvar; |
|---|
| 148 | 101 | break; |
|---|
| 149 | 102 | case 2: |
|---|
| 150 | | - sval = *(short *)dummy_var; |
|---|
| 103 | + svar = (__u16)glvar; |
|---|
| 151 | 104 | break; |
|---|
| 152 | 105 | case 4: |
|---|
| 153 | | - ival = *(int *)dummy_var; |
|---|
| 106 | + ivar = (__u32)glvar; |
|---|
| 154 | 107 | break; |
|---|
| 155 | 108 | case 8: |
|---|
| 156 | | - lval = *(long long *)dummy_var; |
|---|
| 109 | + lvar = (__u64)glvar; |
|---|
| 157 | 110 | break; |
|---|
| 158 | 111 | } |
|---|
| 159 | 112 | } |
|---|
| 160 | 113 | |
|---|
| 161 | | -/* |
|---|
| 162 | | - * Do the r/w accesses to trigger the breakpoints. And run |
|---|
| 163 | | - * the usual traps. |
|---|
| 164 | | - */ |
|---|
| 165 | | -static void trigger_tests(void) |
|---|
| 114 | +static void test_workload(void) |
|---|
| 166 | 115 | { |
|---|
| 167 | | - int len, ret; |
|---|
| 116 | + __u8 cvar __attribute__((unused)); |
|---|
| 117 | + __u32 ivar __attribute__((unused)); |
|---|
| 118 | + int len = 0; |
|---|
| 168 | 119 | |
|---|
| 169 | | - ret = ptrace(PTRACE_TRACEME, 0, NULL, 0); |
|---|
| 170 | | - if (ret) { |
|---|
| 171 | | - perror("Can't be traced?\n"); |
|---|
| 172 | | - return; |
|---|
| 120 | + if (ptrace(PTRACE_TRACEME, 0, NULL, 0)) { |
|---|
| 121 | + perror("Child can't be traced?"); |
|---|
| 122 | + exit(-1); |
|---|
| 173 | 123 | } |
|---|
| 174 | 124 | |
|---|
| 175 | 125 | /* Wake up father so that it sets up the first test */ |
|---|
| 176 | 126 | kill(getpid(), SIGUSR1); |
|---|
| 177 | 127 | |
|---|
| 178 | | - /* Test write watchpoints */ |
|---|
| 179 | | - for (len = 1; len <= sizeof(long); len <<= 1) |
|---|
| 128 | + /* PTRACE_SET_DEBUGREG, WO test */ |
|---|
| 129 | + for (len = 1; len <= sizeof(glvar); len <<= 1) |
|---|
| 180 | 130 | write_var(len); |
|---|
| 181 | 131 | |
|---|
| 182 | | - /* Test read/write watchpoints (on read accesses) */ |
|---|
| 183 | | - for (len = 1; len <= sizeof(long); len <<= 1) |
|---|
| 132 | + /* PTRACE_SET_DEBUGREG, RO test */ |
|---|
| 133 | + for (len = 1; len <= sizeof(glvar); len <<= 1) |
|---|
| 184 | 134 | read_var(len); |
|---|
| 185 | 135 | |
|---|
| 186 | | - /* Test when breakpoint is unset */ |
|---|
| 136 | + /* PTRACE_SET_DEBUGREG, RW test */ |
|---|
| 137 | + for (len = 1; len <= sizeof(glvar); len <<= 1) { |
|---|
| 138 | + if (rand() % 2) |
|---|
| 139 | + read_var(len); |
|---|
| 140 | + else |
|---|
| 141 | + write_var(len); |
|---|
| 142 | + } |
|---|
| 187 | 143 | |
|---|
| 188 | | - /* Test write watchpoints */ |
|---|
| 189 | | - for (len = 1; len <= sizeof(long); len <<= 1) |
|---|
| 190 | | - write_var(len); |
|---|
| 144 | + /* PTRACE_SET_DEBUGREG, Kernel Access Userspace test */ |
|---|
| 145 | + syscall(__NR_getcwd, &cwd, PATH_MAX); |
|---|
| 191 | 146 | |
|---|
| 192 | | - /* Test read/write watchpoints (on read accesses) */ |
|---|
| 193 | | - for (len = 1; len <= sizeof(long); len <<= 1) |
|---|
| 194 | | - read_var(len); |
|---|
| 147 | + /* PPC_PTRACE_SETHWDEBUG, MODE_EXACT, WO test */ |
|---|
| 148 | + write_var(1); |
|---|
| 149 | + |
|---|
| 150 | + /* PPC_PTRACE_SETHWDEBUG, MODE_EXACT, RO test */ |
|---|
| 151 | + read_var(1); |
|---|
| 152 | + |
|---|
| 153 | + /* PPC_PTRACE_SETHWDEBUG, MODE_EXACT, RW test */ |
|---|
| 154 | + if (rand() % 2) |
|---|
| 155 | + write_var(1); |
|---|
| 156 | + else |
|---|
| 157 | + read_var(1); |
|---|
| 158 | + |
|---|
| 159 | + /* PPC_PTRACE_SETHWDEBUG, MODE_EXACT, Kernel Access Userspace test */ |
|---|
| 160 | + syscall(__NR_getcwd, &cwd, PATH_MAX); |
|---|
| 161 | + |
|---|
| 162 | + /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW ALIGNED, WO test */ |
|---|
| 163 | + gstruct.a[rand() % A_LEN] = 'a'; |
|---|
| 164 | + |
|---|
| 165 | + /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW ALIGNED, RO test */ |
|---|
| 166 | + cvar = gstruct.a[rand() % A_LEN]; |
|---|
| 167 | + |
|---|
| 168 | + /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW ALIGNED, RW test */ |
|---|
| 169 | + if (rand() % 2) |
|---|
| 170 | + gstruct.a[rand() % A_LEN] = 'a'; |
|---|
| 171 | + else |
|---|
| 172 | + cvar = gstruct.a[rand() % A_LEN]; |
|---|
| 173 | + |
|---|
| 174 | + /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW UNALIGNED, WO test */ |
|---|
| 175 | + gstruct.b[rand() % B_LEN] = 'b'; |
|---|
| 176 | + |
|---|
| 177 | + /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW UNALIGNED, RO test */ |
|---|
| 178 | + cvar = gstruct.b[rand() % B_LEN]; |
|---|
| 179 | + |
|---|
| 180 | + /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW UNALIGNED, RW test */ |
|---|
| 181 | + if (rand() % 2) |
|---|
| 182 | + gstruct.b[rand() % B_LEN] = 'b'; |
|---|
| 183 | + else |
|---|
| 184 | + cvar = gstruct.b[rand() % B_LEN]; |
|---|
| 185 | + |
|---|
| 186 | + /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW UNALIGNED, DAR OUTSIDE, RW test */ |
|---|
| 187 | + if (rand() % 2) |
|---|
| 188 | + *((int *)(gstruct.a + 4)) = 10; |
|---|
| 189 | + else |
|---|
| 190 | + ivar = *((int *)(gstruct.a + 4)); |
|---|
| 191 | + |
|---|
| 192 | + /* PPC_PTRACE_SETHWDEBUG. DAWR_MAX_LEN. RW test */ |
|---|
| 193 | + if (rand() % 2) |
|---|
| 194 | + big_var[rand() % DAWR_MAX_LEN] = 'a'; |
|---|
| 195 | + else |
|---|
| 196 | + cvar = big_var[rand() % DAWR_MAX_LEN]; |
|---|
| 195 | 197 | } |
|---|
| 196 | 198 | |
|---|
| 197 | | -static void check_success(const char *msg) |
|---|
| 199 | +static void check_success(pid_t child_pid, const char *name, const char *type, |
|---|
| 200 | + unsigned long saddr, int len) |
|---|
| 198 | 201 | { |
|---|
| 199 | | - const char *msg2; |
|---|
| 200 | 202 | int status; |
|---|
| 203 | + siginfo_t siginfo; |
|---|
| 204 | + unsigned long eaddr = (saddr + len - 1) | 0x7; |
|---|
| 205 | + |
|---|
| 206 | + saddr &= ~0x7; |
|---|
| 201 | 207 | |
|---|
| 202 | 208 | /* Wait for the child to SIGTRAP */ |
|---|
| 203 | 209 | wait(&status); |
|---|
| 204 | 210 | |
|---|
| 205 | | - msg2 = "Failed"; |
|---|
| 211 | + ptrace(PTRACE_GETSIGINFO, child_pid, NULL, &siginfo); |
|---|
| 206 | 212 | |
|---|
| 207 | | - if (WIFSTOPPED(status) && WSTOPSIG(status) == SIGTRAP) { |
|---|
| 208 | | - msg2 = "Child process hit the breakpoint"; |
|---|
| 213 | + if (!WIFSTOPPED(status) || WSTOPSIG(status) != SIGTRAP || |
|---|
| 214 | + (unsigned long)siginfo.si_addr < saddr || |
|---|
| 215 | + (unsigned long)siginfo.si_addr > eaddr) { |
|---|
| 216 | + printf("%s, %s, len: %d: Fail\n", name, type, len); |
|---|
| 217 | + exit(-1); |
|---|
| 209 | 218 | } |
|---|
| 210 | 219 | |
|---|
| 211 | | - printf("%s Result: [%s]\n", msg, msg2); |
|---|
| 220 | + printf("%s, %s, len: %d: Ok\n", name, type, len); |
|---|
| 221 | + |
|---|
| 222 | + if (!is_8xx) { |
|---|
| 223 | + /* |
|---|
| 224 | + * For ptrace registered watchpoint, signal is generated |
|---|
| 225 | + * before executing load/store. Singlestep the instruction |
|---|
| 226 | + * and then continue the test. |
|---|
| 227 | + */ |
|---|
| 228 | + ptrace(PTRACE_SINGLESTEP, child_pid, NULL, 0); |
|---|
| 229 | + wait(NULL); |
|---|
| 230 | + } |
|---|
| 212 | 231 | } |
|---|
| 213 | 232 | |
|---|
| 214 | | -static void launch_watchpoints(char *buf, int mode, int len, |
|---|
| 215 | | - struct ppc_debug_info *dbginfo, bool dawr) |
|---|
| 233 | +static void ptrace_set_debugreg(pid_t child_pid, unsigned long wp_addr) |
|---|
| 216 | 234 | { |
|---|
| 217 | | - const char *mode_str; |
|---|
| 218 | | - unsigned long data = (unsigned long)(dummy_var); |
|---|
| 219 | | - int wh, range; |
|---|
| 235 | + if (ptrace(PTRACE_SET_DEBUGREG, child_pid, 0, wp_addr)) { |
|---|
| 236 | + perror("PTRACE_SET_DEBUGREG failed"); |
|---|
| 237 | + exit(-1); |
|---|
| 238 | + } |
|---|
| 239 | +} |
|---|
| 220 | 240 | |
|---|
| 221 | | - data &= ~0x7UL; |
|---|
| 241 | +static int ptrace_sethwdebug(pid_t child_pid, struct ppc_hw_breakpoint *info) |
|---|
| 242 | +{ |
|---|
| 243 | + int wh = ptrace(PPC_PTRACE_SETHWDEBUG, child_pid, 0, info); |
|---|
| 222 | 244 | |
|---|
| 223 | | - if (mode == BP_W) { |
|---|
| 224 | | - data |= (1UL << 1); |
|---|
| 225 | | - mode_str = "write"; |
|---|
| 226 | | - } else { |
|---|
| 227 | | - data |= (1UL << 0); |
|---|
| 228 | | - data |= (1UL << 1); |
|---|
| 229 | | - mode_str = "read"; |
|---|
| 245 | + if (wh <= 0) { |
|---|
| 246 | + perror("PPC_PTRACE_SETHWDEBUG failed"); |
|---|
| 247 | + exit(-1); |
|---|
| 248 | + } |
|---|
| 249 | + return wh; |
|---|
| 250 | +} |
|---|
| 251 | + |
|---|
| 252 | +static void ptrace_delhwdebug(pid_t child_pid, int wh) |
|---|
| 253 | +{ |
|---|
| 254 | + if (ptrace(PPC_PTRACE_DELHWDEBUG, child_pid, 0, wh) < 0) { |
|---|
| 255 | + perror("PPC_PTRACE_DELHWDEBUG failed"); |
|---|
| 256 | + exit(-1); |
|---|
| 257 | + } |
|---|
| 258 | +} |
|---|
| 259 | + |
|---|
| 260 | +#define DABR_READ_SHIFT 0 |
|---|
| 261 | +#define DABR_WRITE_SHIFT 1 |
|---|
| 262 | +#define DABR_TRANSLATION_SHIFT 2 |
|---|
| 263 | + |
|---|
| 264 | +static int test_set_debugreg(pid_t child_pid) |
|---|
| 265 | +{ |
|---|
| 266 | + unsigned long wp_addr = (unsigned long)&glvar; |
|---|
| 267 | + char *name = "PTRACE_SET_DEBUGREG"; |
|---|
| 268 | + int len; |
|---|
| 269 | + |
|---|
| 270 | + /* PTRACE_SET_DEBUGREG, WO test*/ |
|---|
| 271 | + wp_addr &= ~0x7UL; |
|---|
| 272 | + wp_addr |= (1UL << DABR_WRITE_SHIFT); |
|---|
| 273 | + wp_addr |= (1UL << DABR_TRANSLATION_SHIFT); |
|---|
| 274 | + for (len = 1; len <= sizeof(glvar); len <<= 1) { |
|---|
| 275 | + ptrace_set_debugreg(child_pid, wp_addr); |
|---|
| 276 | + ptrace(PTRACE_CONT, child_pid, NULL, 0); |
|---|
| 277 | + check_success(child_pid, name, "WO", wp_addr, len); |
|---|
| 230 | 278 | } |
|---|
| 231 | 279 | |
|---|
| 232 | | - /* Set DABR_TRANSLATION bit */ |
|---|
| 233 | | - data |= (1UL << 2); |
|---|
| 280 | + /* PTRACE_SET_DEBUGREG, RO test */ |
|---|
| 281 | + wp_addr &= ~0x7UL; |
|---|
| 282 | + wp_addr |= (1UL << DABR_READ_SHIFT); |
|---|
| 283 | + wp_addr |= (1UL << DABR_TRANSLATION_SHIFT); |
|---|
| 284 | + for (len = 1; len <= sizeof(glvar); len <<= 1) { |
|---|
| 285 | + ptrace_set_debugreg(child_pid, wp_addr); |
|---|
| 286 | + ptrace(PTRACE_CONT, child_pid, NULL, 0); |
|---|
| 287 | + check_success(child_pid, name, "RO", wp_addr, len); |
|---|
| 288 | + } |
|---|
| 234 | 289 | |
|---|
| 235 | | - /* use PTRACE_SET_DEBUGREG breakpoints */ |
|---|
| 236 | | - set_breakpoint_addr((void *)data); |
|---|
| 290 | + /* PTRACE_SET_DEBUGREG, RW test */ |
|---|
| 291 | + wp_addr &= ~0x7UL; |
|---|
| 292 | + wp_addr |= (1Ul << DABR_READ_SHIFT); |
|---|
| 293 | + wp_addr |= (1UL << DABR_WRITE_SHIFT); |
|---|
| 294 | + wp_addr |= (1UL << DABR_TRANSLATION_SHIFT); |
|---|
| 295 | + for (len = 1; len <= sizeof(glvar); len <<= 1) { |
|---|
| 296 | + ptrace_set_debugreg(child_pid, wp_addr); |
|---|
| 297 | + ptrace(PTRACE_CONT, child_pid, NULL, 0); |
|---|
| 298 | + check_success(child_pid, name, "RW", wp_addr, len); |
|---|
| 299 | + } |
|---|
| 300 | + |
|---|
| 301 | + ptrace_set_debugreg(child_pid, 0); |
|---|
| 302 | + return 0; |
|---|
| 303 | +} |
|---|
| 304 | + |
|---|
| 305 | +static int test_set_debugreg_kernel_userspace(pid_t child_pid) |
|---|
| 306 | +{ |
|---|
| 307 | + unsigned long wp_addr = (unsigned long)cwd; |
|---|
| 308 | + char *name = "PTRACE_SET_DEBUGREG"; |
|---|
| 309 | + |
|---|
| 310 | + /* PTRACE_SET_DEBUGREG, Kernel Access Userspace test */ |
|---|
| 311 | + wp_addr &= ~0x7UL; |
|---|
| 312 | + wp_addr |= (1Ul << DABR_READ_SHIFT); |
|---|
| 313 | + wp_addr |= (1UL << DABR_WRITE_SHIFT); |
|---|
| 314 | + wp_addr |= (1UL << DABR_TRANSLATION_SHIFT); |
|---|
| 315 | + ptrace_set_debugreg(child_pid, wp_addr); |
|---|
| 237 | 316 | ptrace(PTRACE_CONT, child_pid, NULL, 0); |
|---|
| 238 | | - sprintf(buf, "Test %s watchpoint with len: %d ", mode_str, len); |
|---|
| 239 | | - check_success(buf); |
|---|
| 240 | | - /* Unregister hw brkpoint */ |
|---|
| 241 | | - set_breakpoint_addr(NULL); |
|---|
| 317 | + check_success(child_pid, name, "Kernel Access Userspace", wp_addr, 8); |
|---|
| 242 | 318 | |
|---|
| 243 | | - data = (data & ~7); /* remove dabr control bits */ |
|---|
| 319 | + ptrace_set_debugreg(child_pid, 0); |
|---|
| 320 | + return 0; |
|---|
| 321 | +} |
|---|
| 244 | 322 | |
|---|
| 245 | | - /* use PPC_PTRACE_SETHWDEBUG breakpoint */ |
|---|
| 246 | | - if (!(dbginfo->features & PPC_DEBUG_FEATURE_DATA_BP_RANGE)) |
|---|
| 247 | | - return; /* not supported */ |
|---|
| 248 | | - wh = set_hwbreakpoint_addr((void *)data, 0); |
|---|
| 323 | +static void get_ppc_hw_breakpoint(struct ppc_hw_breakpoint *info, int type, |
|---|
| 324 | + unsigned long addr, int len) |
|---|
| 325 | +{ |
|---|
| 326 | + info->version = 1; |
|---|
| 327 | + info->trigger_type = type; |
|---|
| 328 | + info->condition_mode = PPC_BREAKPOINT_CONDITION_NONE; |
|---|
| 329 | + info->addr = (__u64)addr; |
|---|
| 330 | + info->addr2 = (__u64)addr + len; |
|---|
| 331 | + info->condition_value = 0; |
|---|
| 332 | + if (!len) |
|---|
| 333 | + info->addr_mode = PPC_BREAKPOINT_MODE_EXACT; |
|---|
| 334 | + else |
|---|
| 335 | + info->addr_mode = PPC_BREAKPOINT_MODE_RANGE_INCLUSIVE; |
|---|
| 336 | +} |
|---|
| 337 | + |
|---|
| 338 | +static void test_sethwdebug_exact(pid_t child_pid) |
|---|
| 339 | +{ |
|---|
| 340 | + struct ppc_hw_breakpoint info; |
|---|
| 341 | + unsigned long wp_addr = (unsigned long)&glvar; |
|---|
| 342 | + char *name = "PPC_PTRACE_SETHWDEBUG, MODE_EXACT"; |
|---|
| 343 | + int len = 1; /* hardcoded in kernel */ |
|---|
| 344 | + int wh; |
|---|
| 345 | + |
|---|
| 346 | + /* PPC_PTRACE_SETHWDEBUG, MODE_EXACT, WO test */ |
|---|
| 347 | + get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_WRITE, wp_addr, 0); |
|---|
| 348 | + wh = ptrace_sethwdebug(child_pid, &info); |
|---|
| 249 | 349 | ptrace(PTRACE_CONT, child_pid, NULL, 0); |
|---|
| 250 | | - sprintf(buf, "Test %s watchpoint with len: %d ", mode_str, len); |
|---|
| 251 | | - check_success(buf); |
|---|
| 252 | | - /* Unregister hw brkpoint */ |
|---|
| 253 | | - del_hwbreakpoint_addr(wh); |
|---|
| 350 | + check_success(child_pid, name, "WO", wp_addr, len); |
|---|
| 351 | + ptrace_delhwdebug(child_pid, wh); |
|---|
| 254 | 352 | |
|---|
| 255 | | - /* try a wider range */ |
|---|
| 256 | | - range = 8; |
|---|
| 257 | | - if (dawr) |
|---|
| 258 | | - range = 512 - ((int)data & (DAWR_LENGTH_MAX - 1)); |
|---|
| 259 | | - wh = set_hwbreakpoint_addr((void *)data, range); |
|---|
| 353 | + /* PPC_PTRACE_SETHWDEBUG, MODE_EXACT, RO test */ |
|---|
| 354 | + get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_READ, wp_addr, 0); |
|---|
| 355 | + wh = ptrace_sethwdebug(child_pid, &info); |
|---|
| 260 | 356 | ptrace(PTRACE_CONT, child_pid, NULL, 0); |
|---|
| 261 | | - sprintf(buf, "Test %s watchpoint with len: %d ", mode_str, len); |
|---|
| 262 | | - check_success(buf); |
|---|
| 263 | | - /* Unregister hw brkpoint */ |
|---|
| 264 | | - del_hwbreakpoint_addr(wh); |
|---|
| 357 | + check_success(child_pid, name, "RO", wp_addr, len); |
|---|
| 358 | + ptrace_delhwdebug(child_pid, wh); |
|---|
| 359 | + |
|---|
| 360 | + /* PPC_PTRACE_SETHWDEBUG, MODE_EXACT, RW test */ |
|---|
| 361 | + get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_RW, wp_addr, 0); |
|---|
| 362 | + wh = ptrace_sethwdebug(child_pid, &info); |
|---|
| 363 | + ptrace(PTRACE_CONT, child_pid, NULL, 0); |
|---|
| 364 | + check_success(child_pid, name, "RW", wp_addr, len); |
|---|
| 365 | + ptrace_delhwdebug(child_pid, wh); |
|---|
| 366 | +} |
|---|
| 367 | + |
|---|
| 368 | +static void test_sethwdebug_exact_kernel_userspace(pid_t child_pid) |
|---|
| 369 | +{ |
|---|
| 370 | + struct ppc_hw_breakpoint info; |
|---|
| 371 | + unsigned long wp_addr = (unsigned long)&cwd; |
|---|
| 372 | + char *name = "PPC_PTRACE_SETHWDEBUG, MODE_EXACT"; |
|---|
| 373 | + int len = 1; /* hardcoded in kernel */ |
|---|
| 374 | + int wh; |
|---|
| 375 | + |
|---|
| 376 | + /* PPC_PTRACE_SETHWDEBUG, MODE_EXACT, Kernel Access Userspace test */ |
|---|
| 377 | + get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_WRITE, wp_addr, 0); |
|---|
| 378 | + wh = ptrace_sethwdebug(child_pid, &info); |
|---|
| 379 | + ptrace(PTRACE_CONT, child_pid, NULL, 0); |
|---|
| 380 | + check_success(child_pid, name, "Kernel Access Userspace", wp_addr, len); |
|---|
| 381 | + ptrace_delhwdebug(child_pid, wh); |
|---|
| 382 | +} |
|---|
| 383 | + |
|---|
| 384 | +static void test_sethwdebug_range_aligned(pid_t child_pid) |
|---|
| 385 | +{ |
|---|
| 386 | + struct ppc_hw_breakpoint info; |
|---|
| 387 | + unsigned long wp_addr; |
|---|
| 388 | + char *name = "PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW ALIGNED"; |
|---|
| 389 | + int len; |
|---|
| 390 | + int wh; |
|---|
| 391 | + |
|---|
| 392 | + /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW ALIGNED, WO test */ |
|---|
| 393 | + wp_addr = (unsigned long)&gstruct.a; |
|---|
| 394 | + len = A_LEN; |
|---|
| 395 | + get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_WRITE, wp_addr, len); |
|---|
| 396 | + wh = ptrace_sethwdebug(child_pid, &info); |
|---|
| 397 | + ptrace(PTRACE_CONT, child_pid, NULL, 0); |
|---|
| 398 | + check_success(child_pid, name, "WO", wp_addr, len); |
|---|
| 399 | + ptrace_delhwdebug(child_pid, wh); |
|---|
| 400 | + |
|---|
| 401 | + /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW ALIGNED, RO test */ |
|---|
| 402 | + wp_addr = (unsigned long)&gstruct.a; |
|---|
| 403 | + len = A_LEN; |
|---|
| 404 | + get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_READ, wp_addr, len); |
|---|
| 405 | + wh = ptrace_sethwdebug(child_pid, &info); |
|---|
| 406 | + ptrace(PTRACE_CONT, child_pid, NULL, 0); |
|---|
| 407 | + check_success(child_pid, name, "RO", wp_addr, len); |
|---|
| 408 | + ptrace_delhwdebug(child_pid, wh); |
|---|
| 409 | + |
|---|
| 410 | + /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW ALIGNED, RW test */ |
|---|
| 411 | + wp_addr = (unsigned long)&gstruct.a; |
|---|
| 412 | + len = A_LEN; |
|---|
| 413 | + get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_RW, wp_addr, len); |
|---|
| 414 | + wh = ptrace_sethwdebug(child_pid, &info); |
|---|
| 415 | + ptrace(PTRACE_CONT, child_pid, NULL, 0); |
|---|
| 416 | + check_success(child_pid, name, "RW", wp_addr, len); |
|---|
| 417 | + ptrace_delhwdebug(child_pid, wh); |
|---|
| 418 | +} |
|---|
| 419 | + |
|---|
| 420 | +static void test_sethwdebug_range_unaligned(pid_t child_pid) |
|---|
| 421 | +{ |
|---|
| 422 | + struct ppc_hw_breakpoint info; |
|---|
| 423 | + unsigned long wp_addr; |
|---|
| 424 | + char *name = "PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW UNALIGNED"; |
|---|
| 425 | + int len; |
|---|
| 426 | + int wh; |
|---|
| 427 | + |
|---|
| 428 | + /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW UNALIGNED, WO test */ |
|---|
| 429 | + wp_addr = (unsigned long)&gstruct.b; |
|---|
| 430 | + len = B_LEN; |
|---|
| 431 | + get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_WRITE, wp_addr, len); |
|---|
| 432 | + wh = ptrace_sethwdebug(child_pid, &info); |
|---|
| 433 | + ptrace(PTRACE_CONT, child_pid, NULL, 0); |
|---|
| 434 | + check_success(child_pid, name, "WO", wp_addr, len); |
|---|
| 435 | + ptrace_delhwdebug(child_pid, wh); |
|---|
| 436 | + |
|---|
| 437 | + /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW UNALIGNED, RO test */ |
|---|
| 438 | + wp_addr = (unsigned long)&gstruct.b; |
|---|
| 439 | + len = B_LEN; |
|---|
| 440 | + get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_READ, wp_addr, len); |
|---|
| 441 | + wh = ptrace_sethwdebug(child_pid, &info); |
|---|
| 442 | + ptrace(PTRACE_CONT, child_pid, NULL, 0); |
|---|
| 443 | + check_success(child_pid, name, "RO", wp_addr, len); |
|---|
| 444 | + ptrace_delhwdebug(child_pid, wh); |
|---|
| 445 | + |
|---|
| 446 | + /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW UNALIGNED, RW test */ |
|---|
| 447 | + wp_addr = (unsigned long)&gstruct.b; |
|---|
| 448 | + len = B_LEN; |
|---|
| 449 | + get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_RW, wp_addr, len); |
|---|
| 450 | + wh = ptrace_sethwdebug(child_pid, &info); |
|---|
| 451 | + ptrace(PTRACE_CONT, child_pid, NULL, 0); |
|---|
| 452 | + check_success(child_pid, name, "RW", wp_addr, len); |
|---|
| 453 | + ptrace_delhwdebug(child_pid, wh); |
|---|
| 454 | + |
|---|
| 455 | +} |
|---|
| 456 | + |
|---|
| 457 | +static void test_sethwdebug_range_unaligned_dar(pid_t child_pid) |
|---|
| 458 | +{ |
|---|
| 459 | + struct ppc_hw_breakpoint info; |
|---|
| 460 | + unsigned long wp_addr; |
|---|
| 461 | + char *name = "PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW UNALIGNED, DAR OUTSIDE"; |
|---|
| 462 | + int len; |
|---|
| 463 | + int wh; |
|---|
| 464 | + |
|---|
| 465 | + /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW UNALIGNED, DAR OUTSIDE, RW test */ |
|---|
| 466 | + wp_addr = (unsigned long)&gstruct.b; |
|---|
| 467 | + len = B_LEN; |
|---|
| 468 | + get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_WRITE, wp_addr, len); |
|---|
| 469 | + wh = ptrace_sethwdebug(child_pid, &info); |
|---|
| 470 | + ptrace(PTRACE_CONT, child_pid, NULL, 0); |
|---|
| 471 | + check_success(child_pid, name, "RW", wp_addr, len); |
|---|
| 472 | + ptrace_delhwdebug(child_pid, wh); |
|---|
| 473 | +} |
|---|
| 474 | + |
|---|
| 475 | +static void test_sethwdebug_dawr_max_range(pid_t child_pid) |
|---|
| 476 | +{ |
|---|
| 477 | + struct ppc_hw_breakpoint info; |
|---|
| 478 | + unsigned long wp_addr; |
|---|
| 479 | + char *name = "PPC_PTRACE_SETHWDEBUG, DAWR_MAX_LEN"; |
|---|
| 480 | + int len; |
|---|
| 481 | + int wh; |
|---|
| 482 | + |
|---|
| 483 | + /* PPC_PTRACE_SETHWDEBUG, DAWR_MAX_LEN, RW test */ |
|---|
| 484 | + wp_addr = (unsigned long)big_var; |
|---|
| 485 | + len = DAWR_MAX_LEN; |
|---|
| 486 | + get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_RW, wp_addr, len); |
|---|
| 487 | + wh = ptrace_sethwdebug(child_pid, &info); |
|---|
| 488 | + ptrace(PTRACE_CONT, child_pid, NULL, 0); |
|---|
| 489 | + check_success(child_pid, name, "RW", wp_addr, len); |
|---|
| 490 | + ptrace_delhwdebug(child_pid, wh); |
|---|
| 265 | 491 | } |
|---|
| 266 | 492 | |
|---|
| 267 | 493 | /* Set the breakpoints and check the child successfully trigger them */ |
|---|
| 268 | | -static int launch_tests(bool dawr) |
|---|
| 494 | +static void |
|---|
| 495 | +run_tests(pid_t child_pid, struct ppc_debug_info *dbginfo, bool dawr) |
|---|
| 269 | 496 | { |
|---|
| 270 | | - char buf[1024]; |
|---|
| 271 | | - int len, i, status; |
|---|
| 272 | | - |
|---|
| 273 | | - struct ppc_debug_info dbginfo; |
|---|
| 274 | | - |
|---|
| 275 | | - i = ptrace(PPC_PTRACE_GETHWDBGINFO, child_pid, NULL, &dbginfo); |
|---|
| 276 | | - if (i) { |
|---|
| 277 | | - perror("Can't set breakpoint info\n"); |
|---|
| 278 | | - exit(-1); |
|---|
| 497 | + test_set_debugreg(child_pid); |
|---|
| 498 | + test_set_debugreg_kernel_userspace(child_pid); |
|---|
| 499 | + test_sethwdebug_exact(child_pid); |
|---|
| 500 | + test_sethwdebug_exact_kernel_userspace(child_pid); |
|---|
| 501 | + if (dbginfo->features & PPC_DEBUG_FEATURE_DATA_BP_RANGE) { |
|---|
| 502 | + test_sethwdebug_range_aligned(child_pid); |
|---|
| 503 | + if (dawr || is_8xx) { |
|---|
| 504 | + test_sethwdebug_range_unaligned(child_pid); |
|---|
| 505 | + test_sethwdebug_range_unaligned_dar(child_pid); |
|---|
| 506 | + test_sethwdebug_dawr_max_range(child_pid); |
|---|
| 507 | + } |
|---|
| 279 | 508 | } |
|---|
| 280 | | - if (!(dbginfo.features & PPC_DEBUG_FEATURE_DATA_BP_RANGE)) |
|---|
| 281 | | - printf("WARNING: Kernel doesn't support PPC_PTRACE_SETHWDEBUG\n"); |
|---|
| 282 | | - |
|---|
| 283 | | - /* Write watchpoint */ |
|---|
| 284 | | - for (len = 1; len <= sizeof(long); len <<= 1) |
|---|
| 285 | | - launch_watchpoints(buf, BP_W, len, &dbginfo, dawr); |
|---|
| 286 | | - |
|---|
| 287 | | - /* Read-Write watchpoint */ |
|---|
| 288 | | - for (len = 1; len <= sizeof(long); len <<= 1) |
|---|
| 289 | | - launch_watchpoints(buf, BP_RW, len, &dbginfo, dawr); |
|---|
| 290 | | - |
|---|
| 291 | | - ptrace(PTRACE_CONT, child_pid, NULL, 0); |
|---|
| 292 | | - |
|---|
| 293 | | - /* |
|---|
| 294 | | - * Now we have unregistered the breakpoint, access by child |
|---|
| 295 | | - * should not cause SIGTRAP. |
|---|
| 296 | | - */ |
|---|
| 297 | | - |
|---|
| 298 | | - wait(&status); |
|---|
| 299 | | - |
|---|
| 300 | | - if (WIFSTOPPED(status) && WSTOPSIG(status) == SIGTRAP) { |
|---|
| 301 | | - printf("FAIL: Child process hit the breakpoint, which is not expected\n"); |
|---|
| 302 | | - ptrace(PTRACE_CONT, child_pid, NULL, 0); |
|---|
| 303 | | - return TEST_FAIL; |
|---|
| 304 | | - } |
|---|
| 305 | | - |
|---|
| 306 | | - if (WIFEXITED(status)) |
|---|
| 307 | | - printf("Child exited normally\n"); |
|---|
| 308 | | - |
|---|
| 309 | | - return TEST_PASS; |
|---|
| 310 | 509 | } |
|---|
| 311 | 510 | |
|---|
| 312 | 511 | static int ptrace_hwbreak(void) |
|---|
| 313 | 512 | { |
|---|
| 314 | | - pid_t pid; |
|---|
| 315 | | - int ret; |
|---|
| 513 | + pid_t child_pid; |
|---|
| 514 | + struct ppc_debug_info dbginfo; |
|---|
| 316 | 515 | bool dawr; |
|---|
| 317 | 516 | |
|---|
| 318 | | - pid = fork(); |
|---|
| 319 | | - if (!pid) { |
|---|
| 320 | | - trigger_tests(); |
|---|
| 517 | + child_pid = fork(); |
|---|
| 518 | + if (!child_pid) { |
|---|
| 519 | + test_workload(); |
|---|
| 321 | 520 | return 0; |
|---|
| 322 | 521 | } |
|---|
| 323 | 522 | |
|---|
| 324 | 523 | wait(NULL); |
|---|
| 325 | 524 | |
|---|
| 326 | | - child_pid = pid; |
|---|
| 525 | + get_dbginfo(child_pid, &dbginfo); |
|---|
| 526 | + SKIP_IF(dbginfo.num_data_bps == 0); |
|---|
| 327 | 527 | |
|---|
| 328 | | - get_dbginfo(); |
|---|
| 329 | | - SKIP_IF(!hwbreak_present()); |
|---|
| 330 | | - dawr = dawr_present(); |
|---|
| 528 | + dawr = dawr_present(&dbginfo); |
|---|
| 529 | + run_tests(child_pid, &dbginfo, dawr); |
|---|
| 331 | 530 | |
|---|
| 332 | | - ret = launch_tests(dawr); |
|---|
| 333 | | - |
|---|
| 531 | + /* Let the child exit first. */ |
|---|
| 532 | + ptrace(PTRACE_CONT, child_pid, NULL, 0); |
|---|
| 334 | 533 | wait(NULL); |
|---|
| 335 | 534 | |
|---|
| 336 | | - return ret; |
|---|
| 535 | + /* |
|---|
| 536 | + * Testcases exits immediately with -1 on any failure. If |
|---|
| 537 | + * it has reached here, it means all tests were successful. |
|---|
| 538 | + */ |
|---|
| 539 | + return TEST_PASS; |
|---|
| 337 | 540 | } |
|---|
| 338 | 541 | |
|---|
| 339 | 542 | int main(int argc, char **argv, char **envp) |
|---|
| 340 | 543 | { |
|---|
| 544 | + int pvr = 0; |
|---|
| 545 | + asm __volatile__ ("mfspr %0,%1" : "=r"(pvr) : "i"(SPRN_PVR)); |
|---|
| 546 | + if (pvr == PVR_8xx) |
|---|
| 547 | + is_8xx = true; |
|---|
| 548 | + |
|---|
| 341 | 549 | return test_harness(ptrace_hwbreak, "ptrace-hwbreak"); |
|---|
| 342 | 550 | } |
|---|