0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014 #include <sys/ptrace.h>
0015 #include <unistd.h>
0016 #include <stddef.h>
0017 #include <sys/user.h>
0018 #include <stdio.h>
0019 #include <stdlib.h>
0020 #include <signal.h>
0021 #include <sys/types.h>
0022 #include <sys/wait.h>
0023 #include <sys/syscall.h>
0024 #include <linux/limits.h>
0025 #include "ptrace.h"
0026
0027 #define SPRN_PVR 0x11F
0028 #define PVR_8xx 0x00500000
0029
0030 bool is_8xx;
0031
0032
0033
0034
0035
0036 static volatile __u64 glvar;
0037
0038 #define DAWR_MAX_LEN 512
0039 static volatile __u8 big_var[DAWR_MAX_LEN] __attribute__((aligned(512)));
0040
0041 #define A_LEN 6
0042 #define B_LEN 6
0043 struct gstruct {
0044 __u8 a[A_LEN];
0045 __u8 b[B_LEN];
0046 };
0047 static volatile struct gstruct gstruct __attribute__((aligned(512)));
0048
0049 static volatile char cwd[PATH_MAX] __attribute__((aligned(8)));
0050
0051 static void get_dbginfo(pid_t child_pid, struct ppc_debug_info *dbginfo)
0052 {
0053 if (ptrace(PPC_PTRACE_GETHWDBGINFO, child_pid, NULL, dbginfo)) {
0054 perror("Can't get breakpoint info");
0055 exit(-1);
0056 }
0057 }
0058
0059 static bool dawr_present(struct ppc_debug_info *dbginfo)
0060 {
0061 return !!(dbginfo->features & PPC_DEBUG_FEATURE_DATA_BP_DAWR);
0062 }
0063
0064 static void write_var(int len)
0065 {
0066 __u8 *pcvar;
0067 __u16 *psvar;
0068 __u32 *pivar;
0069 __u64 *plvar;
0070
0071 switch (len) {
0072 case 1:
0073 pcvar = (__u8 *)&glvar;
0074 *pcvar = 0xff;
0075 break;
0076 case 2:
0077 psvar = (__u16 *)&glvar;
0078 *psvar = 0xffff;
0079 break;
0080 case 4:
0081 pivar = (__u32 *)&glvar;
0082 *pivar = 0xffffffff;
0083 break;
0084 case 8:
0085 plvar = (__u64 *)&glvar;
0086 *plvar = 0xffffffffffffffffLL;
0087 break;
0088 }
0089 }
0090
0091 static void read_var(int len)
0092 {
0093 __u8 cvar __attribute__((unused));
0094 __u16 svar __attribute__((unused));
0095 __u32 ivar __attribute__((unused));
0096 __u64 lvar __attribute__((unused));
0097
0098 switch (len) {
0099 case 1:
0100 cvar = (__u8)glvar;
0101 break;
0102 case 2:
0103 svar = (__u16)glvar;
0104 break;
0105 case 4:
0106 ivar = (__u32)glvar;
0107 break;
0108 case 8:
0109 lvar = (__u64)glvar;
0110 break;
0111 }
0112 }
0113
0114 static void test_workload(void)
0115 {
0116 __u8 cvar __attribute__((unused));
0117 __u32 ivar __attribute__((unused));
0118 int len = 0;
0119
0120 if (ptrace(PTRACE_TRACEME, 0, NULL, 0)) {
0121 perror("Child can't be traced?");
0122 exit(-1);
0123 }
0124
0125
0126 kill(getpid(), SIGUSR1);
0127
0128
0129 for (len = 1; len <= sizeof(glvar); len <<= 1)
0130 write_var(len);
0131
0132
0133 for (len = 1; len <= sizeof(glvar); len <<= 1)
0134 read_var(len);
0135
0136
0137 for (len = 1; len <= sizeof(glvar); len <<= 1) {
0138 if (rand() % 2)
0139 read_var(len);
0140 else
0141 write_var(len);
0142 }
0143
0144
0145 syscall(__NR_getcwd, &cwd, PATH_MAX);
0146
0147
0148 write_var(1);
0149
0150
0151 read_var(1);
0152
0153
0154 if (rand() % 2)
0155 write_var(1);
0156 else
0157 read_var(1);
0158
0159
0160 syscall(__NR_getcwd, &cwd, PATH_MAX);
0161
0162
0163 gstruct.a[rand() % A_LEN] = 'a';
0164
0165
0166 cvar = gstruct.a[rand() % A_LEN];
0167
0168
0169 if (rand() % 2)
0170 gstruct.a[rand() % A_LEN] = 'a';
0171 else
0172 cvar = gstruct.a[rand() % A_LEN];
0173
0174
0175 gstruct.b[rand() % B_LEN] = 'b';
0176
0177
0178 cvar = gstruct.b[rand() % B_LEN];
0179
0180
0181 if (rand() % 2)
0182 gstruct.b[rand() % B_LEN] = 'b';
0183 else
0184 cvar = gstruct.b[rand() % B_LEN];
0185
0186
0187 if (rand() % 2)
0188 *((int *)(gstruct.a + 4)) = 10;
0189 else
0190 ivar = *((int *)(gstruct.a + 4));
0191
0192
0193 if (rand() % 2)
0194 big_var[rand() % DAWR_MAX_LEN] = 'a';
0195 else
0196 cvar = big_var[rand() % DAWR_MAX_LEN];
0197
0198
0199 gstruct.a[rand() % A_LEN] = 'a';
0200
0201
0202 cvar = gstruct.b[rand() % B_LEN];
0203
0204
0205 gstruct.a[rand() % A_LEN] = 'a';
0206
0207
0208 cvar = gstruct.a[rand() % A_LEN];
0209 }
0210
0211 static void check_success(pid_t child_pid, const char *name, const char *type,
0212 unsigned long saddr, int len)
0213 {
0214 int status;
0215 siginfo_t siginfo;
0216 unsigned long eaddr = (saddr + len - 1) | 0x7;
0217
0218 saddr &= ~0x7;
0219
0220
0221 wait(&status);
0222
0223 ptrace(PTRACE_GETSIGINFO, child_pid, NULL, &siginfo);
0224
0225 if (!WIFSTOPPED(status) || WSTOPSIG(status) != SIGTRAP ||
0226 (unsigned long)siginfo.si_addr < saddr ||
0227 (unsigned long)siginfo.si_addr > eaddr) {
0228 printf("%s, %s, len: %d: Fail\n", name, type, len);
0229 exit(-1);
0230 }
0231
0232 printf("%s, %s, len: %d: Ok\n", name, type, len);
0233
0234 if (!is_8xx) {
0235
0236
0237
0238
0239
0240 ptrace(PTRACE_SINGLESTEP, child_pid, NULL, 0);
0241 wait(NULL);
0242 }
0243 }
0244
0245 static void ptrace_set_debugreg(pid_t child_pid, unsigned long wp_addr)
0246 {
0247 if (ptrace(PTRACE_SET_DEBUGREG, child_pid, 0, wp_addr)) {
0248 perror("PTRACE_SET_DEBUGREG failed");
0249 exit(-1);
0250 }
0251 }
0252
0253 static int ptrace_sethwdebug(pid_t child_pid, struct ppc_hw_breakpoint *info)
0254 {
0255 int wh = ptrace(PPC_PTRACE_SETHWDEBUG, child_pid, 0, info);
0256
0257 if (wh <= 0) {
0258 perror("PPC_PTRACE_SETHWDEBUG failed");
0259 exit(-1);
0260 }
0261 return wh;
0262 }
0263
0264 static void ptrace_delhwdebug(pid_t child_pid, int wh)
0265 {
0266 if (ptrace(PPC_PTRACE_DELHWDEBUG, child_pid, 0, wh) < 0) {
0267 perror("PPC_PTRACE_DELHWDEBUG failed");
0268 exit(-1);
0269 }
0270 }
0271
0272 #define DABR_READ_SHIFT 0
0273 #define DABR_WRITE_SHIFT 1
0274 #define DABR_TRANSLATION_SHIFT 2
0275
0276 static int test_set_debugreg(pid_t child_pid)
0277 {
0278 unsigned long wp_addr = (unsigned long)&glvar;
0279 char *name = "PTRACE_SET_DEBUGREG";
0280 int len;
0281
0282
0283 wp_addr &= ~0x7UL;
0284 wp_addr |= (1UL << DABR_WRITE_SHIFT);
0285 wp_addr |= (1UL << DABR_TRANSLATION_SHIFT);
0286 for (len = 1; len <= sizeof(glvar); len <<= 1) {
0287 ptrace_set_debugreg(child_pid, wp_addr);
0288 ptrace(PTRACE_CONT, child_pid, NULL, 0);
0289 check_success(child_pid, name, "WO", wp_addr, len);
0290 }
0291
0292
0293 wp_addr &= ~0x7UL;
0294 wp_addr |= (1UL << DABR_READ_SHIFT);
0295 wp_addr |= (1UL << DABR_TRANSLATION_SHIFT);
0296 for (len = 1; len <= sizeof(glvar); len <<= 1) {
0297 ptrace_set_debugreg(child_pid, wp_addr);
0298 ptrace(PTRACE_CONT, child_pid, NULL, 0);
0299 check_success(child_pid, name, "RO", wp_addr, len);
0300 }
0301
0302
0303 wp_addr &= ~0x7UL;
0304 wp_addr |= (1Ul << DABR_READ_SHIFT);
0305 wp_addr |= (1UL << DABR_WRITE_SHIFT);
0306 wp_addr |= (1UL << DABR_TRANSLATION_SHIFT);
0307 for (len = 1; len <= sizeof(glvar); len <<= 1) {
0308 ptrace_set_debugreg(child_pid, wp_addr);
0309 ptrace(PTRACE_CONT, child_pid, NULL, 0);
0310 check_success(child_pid, name, "RW", wp_addr, len);
0311 }
0312
0313 ptrace_set_debugreg(child_pid, 0);
0314 return 0;
0315 }
0316
0317 static int test_set_debugreg_kernel_userspace(pid_t child_pid)
0318 {
0319 unsigned long wp_addr = (unsigned long)cwd;
0320 char *name = "PTRACE_SET_DEBUGREG";
0321
0322
0323 wp_addr &= ~0x7UL;
0324 wp_addr |= (1Ul << DABR_READ_SHIFT);
0325 wp_addr |= (1UL << DABR_WRITE_SHIFT);
0326 wp_addr |= (1UL << DABR_TRANSLATION_SHIFT);
0327 ptrace_set_debugreg(child_pid, wp_addr);
0328 ptrace(PTRACE_CONT, child_pid, NULL, 0);
0329 check_success(child_pid, name, "Kernel Access Userspace", wp_addr, 8);
0330
0331 ptrace_set_debugreg(child_pid, 0);
0332 return 0;
0333 }
0334
0335 static void get_ppc_hw_breakpoint(struct ppc_hw_breakpoint *info, int type,
0336 unsigned long addr, int len)
0337 {
0338 info->version = 1;
0339 info->trigger_type = type;
0340 info->condition_mode = PPC_BREAKPOINT_CONDITION_NONE;
0341 info->addr = (__u64)addr;
0342 info->addr2 = (__u64)addr + len;
0343 info->condition_value = 0;
0344 if (!len)
0345 info->addr_mode = PPC_BREAKPOINT_MODE_EXACT;
0346 else
0347 info->addr_mode = PPC_BREAKPOINT_MODE_RANGE_INCLUSIVE;
0348 }
0349
0350 static void test_sethwdebug_exact(pid_t child_pid)
0351 {
0352 struct ppc_hw_breakpoint info;
0353 unsigned long wp_addr = (unsigned long)&glvar;
0354 char *name = "PPC_PTRACE_SETHWDEBUG, MODE_EXACT";
0355 int len = 1;
0356 int wh;
0357
0358
0359 get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_WRITE, wp_addr, 0);
0360 wh = ptrace_sethwdebug(child_pid, &info);
0361 ptrace(PTRACE_CONT, child_pid, NULL, 0);
0362 check_success(child_pid, name, "WO", wp_addr, len);
0363 ptrace_delhwdebug(child_pid, wh);
0364
0365
0366 get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_READ, wp_addr, 0);
0367 wh = ptrace_sethwdebug(child_pid, &info);
0368 ptrace(PTRACE_CONT, child_pid, NULL, 0);
0369 check_success(child_pid, name, "RO", wp_addr, len);
0370 ptrace_delhwdebug(child_pid, wh);
0371
0372
0373 get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_RW, wp_addr, 0);
0374 wh = ptrace_sethwdebug(child_pid, &info);
0375 ptrace(PTRACE_CONT, child_pid, NULL, 0);
0376 check_success(child_pid, name, "RW", wp_addr, len);
0377 ptrace_delhwdebug(child_pid, wh);
0378 }
0379
0380 static void test_sethwdebug_exact_kernel_userspace(pid_t child_pid)
0381 {
0382 struct ppc_hw_breakpoint info;
0383 unsigned long wp_addr = (unsigned long)&cwd;
0384 char *name = "PPC_PTRACE_SETHWDEBUG, MODE_EXACT";
0385 int len = 1;
0386 int wh;
0387
0388
0389 get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_WRITE, wp_addr, 0);
0390 wh = ptrace_sethwdebug(child_pid, &info);
0391 ptrace(PTRACE_CONT, child_pid, NULL, 0);
0392 check_success(child_pid, name, "Kernel Access Userspace", wp_addr, len);
0393 ptrace_delhwdebug(child_pid, wh);
0394 }
0395
0396 static void test_sethwdebug_range_aligned(pid_t child_pid)
0397 {
0398 struct ppc_hw_breakpoint info;
0399 unsigned long wp_addr;
0400 char *name = "PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW ALIGNED";
0401 int len;
0402 int wh;
0403
0404
0405 wp_addr = (unsigned long)&gstruct.a;
0406 len = A_LEN;
0407 get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_WRITE, wp_addr, len);
0408 wh = ptrace_sethwdebug(child_pid, &info);
0409 ptrace(PTRACE_CONT, child_pid, NULL, 0);
0410 check_success(child_pid, name, "WO", wp_addr, len);
0411 ptrace_delhwdebug(child_pid, wh);
0412
0413
0414 wp_addr = (unsigned long)&gstruct.a;
0415 len = A_LEN;
0416 get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_READ, wp_addr, len);
0417 wh = ptrace_sethwdebug(child_pid, &info);
0418 ptrace(PTRACE_CONT, child_pid, NULL, 0);
0419 check_success(child_pid, name, "RO", wp_addr, len);
0420 ptrace_delhwdebug(child_pid, wh);
0421
0422
0423 wp_addr = (unsigned long)&gstruct.a;
0424 len = A_LEN;
0425 get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_RW, wp_addr, len);
0426 wh = ptrace_sethwdebug(child_pid, &info);
0427 ptrace(PTRACE_CONT, child_pid, NULL, 0);
0428 check_success(child_pid, name, "RW", wp_addr, len);
0429 ptrace_delhwdebug(child_pid, wh);
0430 }
0431
0432 static void test_multi_sethwdebug_range(pid_t child_pid)
0433 {
0434 struct ppc_hw_breakpoint info1, info2;
0435 unsigned long wp_addr1, wp_addr2;
0436 char *name1 = "PPC_PTRACE_SETHWDEBUG 2, MODE_RANGE, DW ALIGNED";
0437 char *name2 = "PPC_PTRACE_SETHWDEBUG 2, MODE_RANGE, DW UNALIGNED";
0438 int len1, len2;
0439 int wh1, wh2;
0440
0441 wp_addr1 = (unsigned long)&gstruct.a;
0442 wp_addr2 = (unsigned long)&gstruct.b;
0443 len1 = A_LEN;
0444 len2 = B_LEN;
0445 get_ppc_hw_breakpoint(&info1, PPC_BREAKPOINT_TRIGGER_WRITE, wp_addr1, len1);
0446 get_ppc_hw_breakpoint(&info2, PPC_BREAKPOINT_TRIGGER_READ, wp_addr2, len2);
0447
0448
0449 wh1 = ptrace_sethwdebug(child_pid, &info1);
0450
0451
0452 wh2 = ptrace_sethwdebug(child_pid, &info2);
0453
0454 ptrace(PTRACE_CONT, child_pid, NULL, 0);
0455 check_success(child_pid, name1, "WO", wp_addr1, len1);
0456
0457 ptrace(PTRACE_CONT, child_pid, NULL, 0);
0458 check_success(child_pid, name2, "RO", wp_addr2, len2);
0459
0460 ptrace_delhwdebug(child_pid, wh1);
0461 ptrace_delhwdebug(child_pid, wh2);
0462 }
0463
0464 static void test_multi_sethwdebug_range_dawr_overlap(pid_t child_pid)
0465 {
0466 struct ppc_hw_breakpoint info1, info2;
0467 unsigned long wp_addr1, wp_addr2;
0468 char *name = "PPC_PTRACE_SETHWDEBUG 2, MODE_RANGE, DAWR Overlap";
0469 int len1, len2;
0470 int wh1, wh2;
0471
0472 wp_addr1 = (unsigned long)&gstruct.a;
0473 wp_addr2 = (unsigned long)&gstruct.a;
0474 len1 = A_LEN;
0475 len2 = A_LEN;
0476 get_ppc_hw_breakpoint(&info1, PPC_BREAKPOINT_TRIGGER_WRITE, wp_addr1, len1);
0477 get_ppc_hw_breakpoint(&info2, PPC_BREAKPOINT_TRIGGER_READ, wp_addr2, len2);
0478
0479
0480 wh1 = ptrace_sethwdebug(child_pid, &info1);
0481
0482
0483 wh2 = ptrace_sethwdebug(child_pid, &info2);
0484
0485 ptrace(PTRACE_CONT, child_pid, NULL, 0);
0486 check_success(child_pid, name, "WO", wp_addr1, len1);
0487
0488 ptrace(PTRACE_CONT, child_pid, NULL, 0);
0489 check_success(child_pid, name, "RO", wp_addr2, len2);
0490
0491 ptrace_delhwdebug(child_pid, wh1);
0492 ptrace_delhwdebug(child_pid, wh2);
0493 }
0494
0495 static void test_sethwdebug_range_unaligned(pid_t child_pid)
0496 {
0497 struct ppc_hw_breakpoint info;
0498 unsigned long wp_addr;
0499 char *name = "PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW UNALIGNED";
0500 int len;
0501 int wh;
0502
0503
0504 wp_addr = (unsigned long)&gstruct.b;
0505 len = B_LEN;
0506 get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_WRITE, wp_addr, len);
0507 wh = ptrace_sethwdebug(child_pid, &info);
0508 ptrace(PTRACE_CONT, child_pid, NULL, 0);
0509 check_success(child_pid, name, "WO", wp_addr, len);
0510 ptrace_delhwdebug(child_pid, wh);
0511
0512
0513 wp_addr = (unsigned long)&gstruct.b;
0514 len = B_LEN;
0515 get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_READ, wp_addr, len);
0516 wh = ptrace_sethwdebug(child_pid, &info);
0517 ptrace(PTRACE_CONT, child_pid, NULL, 0);
0518 check_success(child_pid, name, "RO", wp_addr, len);
0519 ptrace_delhwdebug(child_pid, wh);
0520
0521
0522 wp_addr = (unsigned long)&gstruct.b;
0523 len = B_LEN;
0524 get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_RW, wp_addr, len);
0525 wh = ptrace_sethwdebug(child_pid, &info);
0526 ptrace(PTRACE_CONT, child_pid, NULL, 0);
0527 check_success(child_pid, name, "RW", wp_addr, len);
0528 ptrace_delhwdebug(child_pid, wh);
0529
0530 }
0531
0532 static void test_sethwdebug_range_unaligned_dar(pid_t child_pid)
0533 {
0534 struct ppc_hw_breakpoint info;
0535 unsigned long wp_addr;
0536 char *name = "PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW UNALIGNED, DAR OUTSIDE";
0537 int len;
0538 int wh;
0539
0540
0541 wp_addr = (unsigned long)&gstruct.b;
0542 len = B_LEN;
0543 get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_WRITE, wp_addr, len);
0544 wh = ptrace_sethwdebug(child_pid, &info);
0545 ptrace(PTRACE_CONT, child_pid, NULL, 0);
0546 check_success(child_pid, name, "RW", wp_addr, len);
0547 ptrace_delhwdebug(child_pid, wh);
0548 }
0549
0550 static void test_sethwdebug_dawr_max_range(pid_t child_pid)
0551 {
0552 struct ppc_hw_breakpoint info;
0553 unsigned long wp_addr;
0554 char *name = "PPC_PTRACE_SETHWDEBUG, DAWR_MAX_LEN";
0555 int len;
0556 int wh;
0557
0558
0559 wp_addr = (unsigned long)big_var;
0560 len = DAWR_MAX_LEN;
0561 get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_RW, wp_addr, len);
0562 wh = ptrace_sethwdebug(child_pid, &info);
0563 ptrace(PTRACE_CONT, child_pid, NULL, 0);
0564 check_success(child_pid, name, "RW", wp_addr, len);
0565 ptrace_delhwdebug(child_pid, wh);
0566 }
0567
0568
0569 static void
0570 run_tests(pid_t child_pid, struct ppc_debug_info *dbginfo, bool dawr)
0571 {
0572 test_set_debugreg(child_pid);
0573 test_set_debugreg_kernel_userspace(child_pid);
0574 test_sethwdebug_exact(child_pid);
0575 test_sethwdebug_exact_kernel_userspace(child_pid);
0576 if (dbginfo->features & PPC_DEBUG_FEATURE_DATA_BP_RANGE) {
0577 test_sethwdebug_range_aligned(child_pid);
0578 if (dawr || is_8xx) {
0579 test_sethwdebug_range_unaligned(child_pid);
0580 test_sethwdebug_range_unaligned_dar(child_pid);
0581 test_sethwdebug_dawr_max_range(child_pid);
0582 if (dbginfo->num_data_bps > 1) {
0583 test_multi_sethwdebug_range(child_pid);
0584 test_multi_sethwdebug_range_dawr_overlap(child_pid);
0585 }
0586 }
0587 }
0588 }
0589
0590 static int ptrace_hwbreak(void)
0591 {
0592 pid_t child_pid;
0593 struct ppc_debug_info dbginfo;
0594 bool dawr;
0595
0596 child_pid = fork();
0597 if (!child_pid) {
0598 test_workload();
0599 return 0;
0600 }
0601
0602 wait(NULL);
0603
0604 get_dbginfo(child_pid, &dbginfo);
0605 SKIP_IF(dbginfo.num_data_bps == 0);
0606
0607 dawr = dawr_present(&dbginfo);
0608 run_tests(child_pid, &dbginfo, dawr);
0609
0610
0611 ptrace(PTRACE_CONT, child_pid, NULL, 0);
0612 wait(NULL);
0613
0614
0615
0616
0617
0618 return TEST_PASS;
0619 }
0620
0621 int main(int argc, char **argv, char **envp)
0622 {
0623 int pvr = 0;
0624 asm __volatile__ ("mfspr %0,%1" : "=r"(pvr) : "i"(SPRN_PVR));
0625 if (pvr == PVR_8xx)
0626 is_8xx = true;
0627
0628 return test_harness(ptrace_hwbreak, "ptrace-hwbreak");
0629 }