1/* libunwind - a platform-independent unwind library
2   Copyright (C) 2003-2005 Hewlett-Packard Co
3        Contributed by David Mosberger-Tang <davidm@hpl.hp.com>
4   Copyright (C) 2010 Konstantin Belousov <kib@freebsd.org>
5
6This file is part of libunwind.
7
8Permission is hereby granted, free of charge, to any person obtaining
9a copy of this software and associated documentation files (the
10"Software"), to deal in the Software without restriction, including
11without limitation the rights to use, copy, modify, merge, publish,
12distribute, sublicense, and/or sell copies of the Software, and to
13permit persons to whom the Software is furnished to do so, subject to
14the following conditions:
15
16The above copyright notice and this permission notice shall be
17included in all copies or substantial portions of the Software.
18
19THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
20EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
22NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
23LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
24OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
25WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.  */
26
27#include "_UPT_internal.h"
28
29#if UNW_TARGET_IA64
30# include <elf.h>
31# ifdef HAVE_ASM_PTRACE_OFFSETS_H
32#   include <asm/ptrace_offsets.h>
33# endif
34# include "tdep-ia64/rse.h"
35#endif
36
37#if HAVE_DECL_PTRACE_POKEUSER || HAVE_TTRACE
38int
39_UPT_access_reg (unw_addr_space_t as, unw_regnum_t reg, unw_word_t *val,
40                 int write, void *arg)
41{
42  struct UPT_info *ui = arg;
43  pid_t pid = ui->pid;
44
45#if UNW_DEBUG
46  Debug(16, "using pokeuser: reg: %s [%u], val: %lx, write: %d\n", unw_regname(reg), (unsigned) reg, (long) val, write);
47
48  if (write)
49    Debug (16, "%s <- %lx\n", unw_regname (reg), (long) *val);
50#endif
51
52#if UNW_TARGET_IA64
53  if ((unsigned) reg - UNW_IA64_NAT < 32)
54    {
55      unsigned long nat_bits, mask;
56
57      /* The Linux ptrace represents the statc NaT bits as a single word.  */
58      mask = ((unw_word_t) 1) << (reg - UNW_IA64_NAT);
59      errno = 0;
60#ifdef HAVE_TTRACE
61#       warning No support for ttrace() yet.
62#else
63      nat_bits = ptrace (PTRACE_PEEKUSER, pid, PT_NAT_BITS, 0);
64      if (errno)
65        goto badreg;
66#endif
67
68      if (write)
69        {
70          if (*val)
71            nat_bits |= mask;
72          else
73            nat_bits &= ~mask;
74#ifdef HAVE_TTRACE
75#       warning No support for ttrace() yet.
76#else
77          errno = 0;
78          ptrace (PTRACE_POKEUSER, pid, PT_NAT_BITS, nat_bits);
79          if (errno)
80            goto badreg;
81#endif
82        }
83      goto out;
84    }
85  else
86    switch (reg)
87      {
88      case UNW_IA64_GR + 0:
89        if (write)
90          goto badreg;
91        *val = 0;
92        return 0;
93
94      case UNW_REG_IP:
95        {
96          unsigned long ip, psr;
97
98          /* distribute bundle-addr. & slot-number across PT_IIP & PT_IPSR.  */
99#ifdef HAVE_TTRACE
100#       warning No support for ttrace() yet.
101#else
102          errno = 0;
103          psr = ptrace (PTRACE_PEEKUSER, pid, PT_CR_IPSR, 0);
104          if (errno)
105            goto badreg;
106#endif
107          if (write)
108            {
109              ip = *val & ~0xfUL;
110              psr = (psr & ~0x3UL << 41) | (*val & 0x3);
111#ifdef HAVE_TTRACE
112#       warning No support for ttrace() yet.
113#else
114              errno = 0;
115              ptrace (PTRACE_POKEUSER, pid, PT_CR_IIP, ip);
116              ptrace (PTRACE_POKEUSER, pid, PT_CR_IPSR, psr);
117              if (errno)
118                goto badreg;
119#endif
120            }
121          else
122            {
123#ifdef HAVE_TTRACE
124#       warning No support for ttrace() yet.
125#else
126              errno = 0;
127              ip = ptrace (PTRACE_PEEKUSER, pid, PT_CR_IIP, 0);
128              if (errno)
129                goto badreg;
130#endif
131              *val = ip + ((psr >> 41) & 0x3);
132            }
133          goto out;
134        }
135
136      case UNW_IA64_AR_BSPSTORE:
137        reg = UNW_IA64_AR_BSP;
138        break;
139
140      case UNW_IA64_AR_BSP:
141      case UNW_IA64_BSP:
142        {
143          unsigned long sof, cfm, bsp;
144
145#ifdef HAVE_TTRACE
146#       warning No support for ttrace() yet.
147#else
148          /* Account for the fact that ptrace() expects bsp to point
149             _after_ the current register frame.  */
150          errno = 0;
151          cfm = ptrace (PTRACE_PEEKUSER, pid, PT_CFM, 0);
152          if (errno)
153            goto badreg;
154#endif
155          sof = (cfm & 0x7f);
156
157          if (write)
158            {
159              bsp = rse_skip_regs (*val, sof);
160#ifdef HAVE_TTRACE
161#       warning No support for ttrace() yet.
162#else
163              errno = 0;
164              ptrace (PTRACE_POKEUSER, pid, PT_AR_BSP, bsp);
165              if (errno)
166                goto badreg;
167#endif
168            }
169          else
170            {
171#ifdef HAVE_TTRACE
172#       warning No support for ttrace() yet.
173#else
174              errno = 0;
175              bsp = ptrace (PTRACE_PEEKUSER, pid, PT_AR_BSP, 0);
176              if (errno)
177                goto badreg;
178#endif
179              *val = rse_skip_regs (bsp, -sof);
180            }
181          goto out;
182        }
183
184      case UNW_IA64_CFM:
185        /* If we change CFM, we need to adjust ptrace's notion of bsp
186           accordingly, so that the real bsp remains unchanged.  */
187        if (write)
188          {
189            unsigned long new_sof, old_sof, cfm, bsp;
190
191#ifdef HAVE_TTRACE
192#       warning No support for ttrace() yet.
193#else
194            errno = 0;
195            bsp = ptrace (PTRACE_PEEKUSER, pid, PT_AR_BSP, 0);
196            cfm = ptrace (PTRACE_PEEKUSER, pid, PT_CFM, 0);
197#endif
198            if (errno)
199              goto badreg;
200            old_sof = (cfm & 0x7f);
201            new_sof = (*val & 0x7f);
202            if (old_sof != new_sof)
203              {
204                bsp = rse_skip_regs (bsp, -old_sof + new_sof);
205#ifdef HAVE_TTRACE
206#       warning No support for ttrace() yet.
207#else
208                errno = 0;
209                ptrace (PTRACE_POKEUSER, pid, PT_AR_BSP, 0);
210                if (errno)
211                  goto badreg;
212#endif
213              }
214#ifdef HAVE_TTRACE
215#       warning No support for ttrace() yet.
216#else
217            errno = 0;
218            ptrace (PTRACE_POKEUSER, pid, PT_CFM, *val);
219            if (errno)
220              goto badreg;
221#endif
222            goto out;
223          }
224        break;
225      }
226#endif /* End of IA64 */
227
228  if ((unsigned) reg >= ARRAY_SIZE (_UPT_reg_offset))
229    {
230#if UNW_DEBUG
231      Debug(2, "register out of range: >= %zu / %zu\n", sizeof(_UPT_reg_offset), sizeof(_UPT_reg_offset[0]));
232#endif
233      errno = EINVAL;
234      goto badreg;
235    }
236
237#ifdef HAVE_TTRACE
238#       warning No support for ttrace() yet.
239#else
240  errno = 0;
241  if (write)
242    ptrace (PTRACE_POKEUSER, pid, _UPT_reg_offset[reg], *val);
243  else {
244#if UNW_DEBUG
245    Debug(16, "ptrace PEEKUSER pid: %lu , reg: %lu , offs: %lu\n", (unsigned long)pid, (unsigned long)reg,
246        (unsigned long)_UPT_reg_offset[reg]);
247#endif
248    *val = ptrace (PTRACE_PEEKUSER, pid, _UPT_reg_offset[reg], 0);
249  }
250  if (errno) {
251#if UNW_DEBUG
252    Debug(2, "ptrace failure\n");
253#endif
254    goto badreg;
255  }
256#endif
257
258#ifdef UNW_TARGET_IA64
259 out:
260#endif
261#if UNW_DEBUG
262  if (!write)
263    Debug (16, "%s[%u] -> %lx\n", unw_regname (reg), (unsigned) reg, (long) *val);
264#endif
265  return 0;
266
267 badreg:
268  Debug (1, "bad register %s [%u] (error: %s)\n", unw_regname(reg), reg, strerror (errno));
269  return -UNW_EBADREG;
270}
271#elif HAVE_DECL_PT_GETREGS
272int
273_UPT_access_reg (unw_addr_space_t as, unw_regnum_t reg, unw_word_t *val,
274                 int write, void *arg)
275{
276  struct UPT_info *ui = arg;
277  pid_t pid = ui->pid;
278  gregset_t regs;
279  char *r;
280
281#if UNW_DEBUG
282  Debug(16, "using getregs: reg: %s [%u], val: %lx, write: %u\n", unw_regname(reg), (unsigned) reg, (long) val, write);
283
284  if (write)
285    Debug (16, "%s [%u] <- %lx\n", unw_regname (reg), (unsigned) reg, (long) *val);
286#endif
287  if ((unsigned) reg >= ARRAY_SIZE (_UPT_reg_offset))
288    {
289      errno = EINVAL;
290      goto badreg;
291    }
292  r = (char *)&regs + _UPT_reg_offset[reg];
293  if (ptrace(PT_GETREGS, pid, (caddr_t)&regs, 0) == -1)
294    goto badreg;
295  if (write) {
296      memcpy(r, val, sizeof(unw_word_t));
297      if (ptrace(PT_SETREGS, pid, (caddr_t)&regs, 0) == -1)
298        goto badreg;
299  } else
300      memcpy(val, r, sizeof(unw_word_t));
301  return 0;
302
303 badreg:
304  Debug (1, "bad register %s [%u] (error: %s)\n", unw_regname(reg), reg, strerror (errno));
305  return -UNW_EBADREG;
306}
307#else
308#error Port me
309#endif
310