xref: /netbsd-src/external/gpl3/gdb.old/dist/gdb/testsuite/gdb.arch/aarch64-dbreg-contents.c (revision ae082add65442546470c0ba499a860ee89eed305)
1 /* Test case for setting a memory-write unaligned watchpoint on aarch64.
2 
3   This software is provided 'as-is', without any express or implied
4   warranty.  In no event will the authors be held liable for any damages
5   arising from the use of this software.
6 
7   Permission is granted to anyone to use this software for any purpose,
8   including commercial applications, and to alter it and redistribute it
9   freely.  */
10 
11 #define _GNU_SOURCE 1
12 #include <sys/ptrace.h>
13 #include <assert.h>
14 #include <sys/wait.h>
15 #include <stddef.h>
16 #include <errno.h>
17 #include <sys/uio.h>
18 #include <elf.h>
19 
20 static pid_t child;
21 
22 static void
23 cleanup (void)
24 {
25   if (child > 0)
26     kill (child, SIGKILL);
27   child = 0;
28 }
29 
30 /* Macros to extract fields from the hardware debug information word.  */
31 #define AARCH64_DEBUG_NUM_SLOTS(x) ((x) & 0xff)
32 #define AARCH64_DEBUG_ARCH(x) (((x) >> 8) & 0xff)
33 /* Macro for the expected version of the ARMv8-A debug architecture.  */
34 #define AARCH64_DEBUG_ARCH_V8 0x6
35 #define DR_CONTROL_ENABLED(ctrl)        (((ctrl) & 0x1) == 1)
36 #define DR_CONTROL_LENGTH(ctrl)         (((ctrl) >> 5) & 0xff)
37 
38 static void
39 set_watchpoint (pid_t pid, volatile void *addr, unsigned len_mask)
40 {
41   struct user_hwdebug_state dreg_state;
42   struct iovec iov;
43   long l;
44 
45   assert (len_mask >= 0x01);
46   assert (len_mask <= 0xff);
47 
48   iov.iov_base = &dreg_state;
49   iov.iov_len = sizeof (dreg_state);
50   errno = 0;
51   l = ptrace (PTRACE_GETREGSET, pid, NT_ARM_HW_WATCH, &iov);
52   assert (l == 0);
53   assert (AARCH64_DEBUG_ARCH (dreg_state.dbg_info) == AARCH64_DEBUG_ARCH_V8);
54   assert (AARCH64_DEBUG_NUM_SLOTS (dreg_state.dbg_info) >= 1);
55 
56   assert (!DR_CONTROL_ENABLED (dreg_state.dbg_regs[0].ctrl));
57   dreg_state.dbg_regs[0].ctrl |= 1;
58   assert ( DR_CONTROL_ENABLED (dreg_state.dbg_regs[0].ctrl));
59 
60   assert (DR_CONTROL_LENGTH (dreg_state.dbg_regs[0].ctrl) == 0);
61   dreg_state.dbg_regs[0].ctrl |= len_mask << 5;
62   assert (DR_CONTROL_LENGTH (dreg_state.dbg_regs[0].ctrl) == len_mask);
63 
64   dreg_state.dbg_regs[0].ctrl |= 2 << 3; // write
65   dreg_state.dbg_regs[0].ctrl |= 2 << 1; // enabled at el0
66   dreg_state.dbg_regs[0].addr = (uintptr_t) addr;
67 
68   iov.iov_base = &dreg_state;
69   iov.iov_len = (offsetof (struct user_hwdebug_state, dbg_regs)
70                  + sizeof (dreg_state.dbg_regs[0]));
71   errno = 0;
72   l = ptrace (PTRACE_SETREGSET, pid, NT_ARM_HW_WATCH, &iov);
73   if (errno != 0)
74     error (1, errno, "PTRACE_SETREGSET: NT_ARM_HW_WATCH");
75   assert (l == 0);
76 }
77 
78 static volatile long long check;
79 
80 int
81 main (void)
82 {
83   pid_t got_pid;
84   int i, status;
85   long l;
86 
87   atexit (cleanup);
88 
89   child = fork ();
90   assert (child >= 0);
91   if (child == 0)
92     {
93       l = ptrace (PTRACE_TRACEME, 0, NULL, NULL);
94       assert (l == 0);
95       i = raise (SIGUSR1);
96       assert (i == 0);
97       check = -1;
98       i = raise (SIGUSR2);
99       /* NOTREACHED */
100       assert (0);
101     }
102 
103   got_pid = waitpid (child, &status, 0);
104   assert (got_pid == child);
105   assert (WIFSTOPPED (status));
106   assert (WSTOPSIG (status) == SIGUSR1);
107 
108   /* Add a watchpoint to check.
109      Restart the child. It will write to check.
110      Check child has stopped on the watchpoint.  */
111   set_watchpoint (child, &check, 0x02);
112 
113   errno = 0;
114   l = ptrace (PTRACE_CONT, child, 0l, 0l);
115   assert_perror (errno);
116   assert (l == 0);
117 
118   got_pid = waitpid (child, &status, 0);
119   assert (got_pid == child);
120   assert (WIFSTOPPED (status));
121   if (WSTOPSIG (status) == SIGUSR2)
122     {
123       /* We missed the watchpoint - unsupported by hardware?  */
124       cleanup ();
125       return 2;
126     }
127   assert (WSTOPSIG (status) == SIGTRAP);
128 
129   return 0;
130 }
131