1/* libunwind - a platform-independent unwind library
2   Copyright (C) 2001-2005 Hewlett-Packard Co
3	Contributed by David Mosberger-Tang <davidm@hpl.hp.com>
4
5This file is part of libunwind.
6
7Permission is hereby granted, free of charge, to any person obtaining
8a copy of this software and associated documentation files (the
9"Software"), to deal in the Software without restriction, including
10without limitation the rights to use, copy, modify, merge, publish,
11distribute, sublicense, and/or sell copies of the Software, and to
12permit persons to whom the Software is furnished to do so, subject to
13the following conditions:
14
15The above copyright notice and this permission notice shall be
16included in all copies or substantial portions of the Software.
17
18THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
22LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
23OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
24WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.  */
25
26#include "offsets.h"
27#include "regs.h"
28#include "unwind_i.h"
29
30static inline ia64_loc_t
31linux_scratch_loc (struct cursor *c, unw_regnum_t reg, uint8_t *nat_bitnr)
32{
33#if !defined(UNW_LOCAL_ONLY) || defined(__linux)
34  unw_word_t addr = c->sigcontext_addr, flags, tmp_addr;
35  int i;
36
37  if (ia64_get_abi_marker (c) == ABI_MARKER_LINUX_SIGTRAMP
38      || ia64_get_abi_marker (c) == ABI_MARKER_OLD_LINUX_SIGTRAMP)
39    {
40      switch (reg)
41	{
42	case UNW_IA64_NAT + 2 ... UNW_IA64_NAT + 3:
43	case UNW_IA64_NAT + 8 ... UNW_IA64_NAT + 31:
44	  /* Linux sigcontext contains the NaT bit of scratch register
45	     N in bit position N of the sc_nat member. */
46	  *nat_bitnr = (reg - UNW_IA64_NAT);
47	  addr += LINUX_SC_NAT_OFF;
48	  break;
49
50	case UNW_IA64_GR +  2 ... UNW_IA64_GR + 3:
51	case UNW_IA64_GR +  8 ... UNW_IA64_GR + 31:
52	  addr += LINUX_SC_GR_OFF + 8 * (reg - UNW_IA64_GR);
53	  break;
54
55	case UNW_IA64_FR + 6 ... UNW_IA64_FR + 15:
56	  addr += LINUX_SC_FR_OFF + 16 * (reg - UNW_IA64_FR);
57	  return IA64_LOC_ADDR (addr, IA64_LOC_TYPE_FP);
58
59	case UNW_IA64_FR + 32 ... UNW_IA64_FR + 127:
60	  if (ia64_get (c, IA64_LOC_ADDR (addr + LINUX_SC_FLAGS_OFF, 0),
61			&flags) < 0)
62	    return IA64_NULL_LOC;
63
64	  if (!(flags & IA64_SC_FLAG_FPH_VALID))
65	    {
66	      /* initialize fph partition: */
67	      tmp_addr = addr + LINUX_SC_FR_OFF + 32*16;
68	      for (i = 32; i < 128; ++i, tmp_addr += 16)
69		if (ia64_putfp (c, IA64_LOC_ADDR (tmp_addr, 0),
70				unw.read_only.f0) < 0)
71		  return IA64_NULL_LOC;
72	      /* mark fph partition as valid: */
73	      if (ia64_put (c, IA64_LOC_ADDR (addr + LINUX_SC_FLAGS_OFF, 0),
74			    flags | IA64_SC_FLAG_FPH_VALID) < 0)
75		return IA64_NULL_LOC;
76	    }
77
78	  addr += LINUX_SC_FR_OFF + 16 * (reg - UNW_IA64_FR);
79	  return IA64_LOC_ADDR (addr, IA64_LOC_TYPE_FP);
80
81	case UNW_IA64_BR + 0: addr += LINUX_SC_BR_OFF + 0; break;
82	case UNW_IA64_BR + 6: addr += LINUX_SC_BR_OFF + 6*8; break;
83	case UNW_IA64_BR + 7: addr += LINUX_SC_BR_OFF + 7*8; break;
84	case UNW_IA64_AR_RSC: addr += LINUX_SC_AR_RSC_OFF; break;
85	case UNW_IA64_AR_CSD: addr += LINUX_SC_AR_CSD_OFF; break;
86	case UNW_IA64_AR_SSD: addr += LINUX_SC_AR_SSD_OFF; break;
87	case UNW_IA64_AR_CCV: addr += LINUX_SC_AR_CCV; break;
88
89	default:
90	  if (unw_is_fpreg (reg))
91	    return IA64_FPREG_LOC (c, reg);
92	  else
93	    return IA64_REG_LOC (c, reg);
94	}
95      return IA64_LOC_ADDR (addr, 0);
96    }
97  else
98    {
99      int is_nat = 0;
100
101      if ((unsigned) (reg - UNW_IA64_NAT) < 128)
102	{
103	  is_nat = 1;
104	  reg -= (UNW_IA64_NAT - UNW_IA64_GR);
105	}
106      if (ia64_get_abi_marker (c) == ABI_MARKER_LINUX_INTERRUPT)
107	{
108	  switch (reg)
109	    {
110	    case UNW_IA64_BR + 6 ... UNW_IA64_BR + 7:
111	      addr += LINUX_PT_B6_OFF + 8 * (reg - (UNW_IA64_BR + 6));
112	      break;
113
114	    case UNW_IA64_AR_CSD: addr += LINUX_PT_CSD_OFF; break;
115	    case UNW_IA64_AR_SSD: addr += LINUX_PT_SSD_OFF; break;
116
117	    case UNW_IA64_GR +  8 ... UNW_IA64_GR + 11:
118	      addr += LINUX_PT_R8_OFF + 8 * (reg - (UNW_IA64_GR + 8));
119	      break;
120
121	    case UNW_IA64_IP: addr += LINUX_PT_IIP_OFF; break;
122	    case UNW_IA64_CFM: addr += LINUX_PT_IFS_OFF; break;
123	    case UNW_IA64_AR_UNAT: addr += LINUX_PT_UNAT_OFF; break;
124	    case UNW_IA64_AR_PFS: addr += LINUX_PT_PFS_OFF; break;
125	    case UNW_IA64_AR_RSC: addr += LINUX_PT_RSC_OFF; break;
126	    case UNW_IA64_AR_RNAT: addr += LINUX_PT_RNAT_OFF; break;
127	    case UNW_IA64_AR_BSPSTORE: addr += LINUX_PT_BSPSTORE_OFF; break;
128	    case UNW_IA64_PR: addr += LINUX_PT_PR_OFF; break;
129	    case UNW_IA64_BR + 0: addr += LINUX_PT_B0_OFF; break;
130
131	    case UNW_IA64_GR + 1:
132	      /* The saved r1 value is valid only in the frame in which
133		 it was saved; for everything else we need to look up
134		 the appropriate gp value.  */
135	      if (c->sigcontext_addr != c->sp + 0x10)
136		return IA64_NULL_LOC;
137	      addr += LINUX_PT_R1_OFF;
138	      break;
139
140	    case UNW_IA64_GR + 12: addr += LINUX_PT_R12_OFF; break;
141	    case UNW_IA64_GR + 13: addr += LINUX_PT_R13_OFF; break;
142	    case UNW_IA64_AR_FPSR: addr += LINUX_PT_FPSR_OFF; break;
143	    case UNW_IA64_GR + 15: addr += LINUX_PT_R15_OFF; break;
144	    case UNW_IA64_GR + 14: addr += LINUX_PT_R14_OFF; break;
145	    case UNW_IA64_GR + 2: addr += LINUX_PT_R2_OFF; break;
146	    case UNW_IA64_GR + 3: addr += LINUX_PT_R3_OFF; break;
147
148	    case UNW_IA64_GR + 16 ... UNW_IA64_GR + 31:
149	      addr += LINUX_PT_R16_OFF + 8 * (reg - (UNW_IA64_GR + 16));
150	      break;
151
152	    case UNW_IA64_AR_CCV: addr += LINUX_PT_CCV_OFF; break;
153
154	    case UNW_IA64_FR + 6 ... UNW_IA64_FR + 11:
155	      addr += LINUX_PT_F6_OFF + 16 * (reg - (UNW_IA64_FR + 6));
156	      return IA64_LOC_ADDR (addr, IA64_LOC_TYPE_FP);
157
158	    default:
159	      if (unw_is_fpreg (reg))
160		return IA64_FPREG_LOC (c, reg);
161	      else
162		return IA64_REG_LOC (c, reg);
163	    }
164	}
165      else if (ia64_get_abi_marker (c) == ABI_MARKER_OLD_LINUX_INTERRUPT)
166	{
167	  switch (reg)
168	    {
169	    case UNW_IA64_GR +  1:
170	      /* The saved r1 value is valid only in the frame in which
171		 it was saved; for everything else we need to look up
172		 the appropriate gp value.  */
173	      if (c->sigcontext_addr != c->sp + 0x10)
174		return IA64_NULL_LOC;
175	      addr += LINUX_OLD_PT_R1_OFF;
176	      break;
177
178	    case UNW_IA64_GR +  2 ... UNW_IA64_GR + 3:
179	      addr += LINUX_OLD_PT_R2_OFF + 8 * (reg - (UNW_IA64_GR + 2));
180	      break;
181
182	    case UNW_IA64_GR +  8 ... UNW_IA64_GR + 11:
183	      addr += LINUX_OLD_PT_R8_OFF + 8 * (reg - (UNW_IA64_GR + 8));
184	      break;
185
186	    case UNW_IA64_GR + 16 ... UNW_IA64_GR + 31:
187	      addr += LINUX_OLD_PT_R16_OFF + 8 * (reg - (UNW_IA64_GR + 16));
188	      break;
189
190	    case UNW_IA64_FR + 6 ... UNW_IA64_FR + 9:
191	      addr += LINUX_OLD_PT_F6_OFF + 16 * (reg - (UNW_IA64_FR + 6));
192	      return IA64_LOC_ADDR (addr, IA64_LOC_TYPE_FP);
193
194	    case UNW_IA64_BR + 0: addr += LINUX_OLD_PT_B0_OFF; break;
195	    case UNW_IA64_BR + 6: addr += LINUX_OLD_PT_B6_OFF; break;
196	    case UNW_IA64_BR + 7: addr += LINUX_OLD_PT_B7_OFF; break;
197
198	    case UNW_IA64_AR_RSC: addr += LINUX_OLD_PT_RSC_OFF; break;
199	    case UNW_IA64_AR_CCV: addr += LINUX_OLD_PT_CCV_OFF; break;
200
201	    default:
202	      if (unw_is_fpreg (reg))
203		return IA64_FPREG_LOC (c, reg);
204	      else
205		return IA64_REG_LOC (c, reg);
206	    }
207	}
208      if (is_nat)
209	{
210	  /* For Linux pt-regs structure, bit number is determined by
211	     the UNaT slot number (as determined by st8.spill) and the
212	     bits are saved wherever the (primary) UNaT was saved.  */
213	  *nat_bitnr = ia64_unat_slot_num (addr);
214	  return c->loc[IA64_REG_PRI_UNAT_MEM];
215	}
216      return IA64_LOC_ADDR (addr, 0);
217    }
218#endif
219  return IA64_NULL_LOC;
220}
221
222static inline ia64_loc_t
223hpux_scratch_loc (struct cursor *c, unw_regnum_t reg, uint8_t *nat_bitnr)
224{
225#if !defined(UNW_LOCAL_ONLY) || defined(__hpux)
226  return IA64_LOC_UC_REG (reg, c->sigcontext_addr);
227#else
228  return IA64_NULL_LOC;
229#endif
230}
231
232HIDDEN ia64_loc_t
233ia64_scratch_loc (struct cursor *c, unw_regnum_t reg, uint8_t *nat_bitnr)
234{
235  if (c->sigcontext_addr)
236    {
237      if (ia64_get_abi (c) == ABI_LINUX)
238	return linux_scratch_loc (c, reg, nat_bitnr);
239      else if (ia64_get_abi (c) ==  ABI_HPUX)
240	return hpux_scratch_loc (c, reg, nat_bitnr);
241      else
242	return IA64_NULL_LOC;
243    }
244  else
245    return IA64_REG_LOC (c, reg);
246}
247
248static inline int
249update_nat (struct cursor *c, ia64_loc_t nat_loc, unw_word_t mask,
250	    unw_word_t *valp, int write)
251{
252  unw_word_t nat_word;
253  int ret;
254
255  ret = ia64_get (c, nat_loc, &nat_word);
256  if (ret < 0)
257    return ret;
258
259  if (write)
260    {
261      if (*valp)
262	nat_word |= mask;
263      else
264	nat_word &= ~mask;
265      ret = ia64_put (c, nat_loc, nat_word);
266    }
267  else
268    *valp = (nat_word & mask) != 0;
269  return ret;
270}
271
272static int
273access_nat (struct cursor *c,
274	    ia64_loc_t nat_loc, ia64_loc_t reg_loc, uint8_t nat_bitnr,
275	    unw_word_t *valp, int write)
276{
277  unw_word_t mask = 0;
278  unw_fpreg_t tmp;
279  int ret;
280
281  if (IA64_IS_FP_LOC (reg_loc))
282    {
283      /* NaT bit is saved as a NaTVal.  This happens when a general
284	 register is saved to a floating-point register.  */
285      if (write)
286	{
287	  if (*valp)
288	    {
289	      if (ia64_is_big_endian (c))
290		ret = ia64_putfp (c, reg_loc, unw.nat_val_be);
291	      else
292		ret = ia64_putfp (c, reg_loc, unw.nat_val_le);
293	    }
294	  else
295	    {
296	      unw_word_t *src, *dst;
297	      unw_fpreg_t tmp;
298
299	      ret = ia64_getfp (c, reg_loc, &tmp);
300	      if (ret < 0)
301		return ret;
302
303	      /* Reset the exponent to 0x1003e so that the significand
304		 will be interpreted as an integer value.  */
305	      src = (unw_word_t *) &unw.int_val_be;
306	      dst = (unw_word_t *) &tmp;
307	      if (!ia64_is_big_endian (c))
308		++src, ++dst;
309	      *dst = *src;
310
311	      ret = ia64_putfp (c, reg_loc, tmp);
312	    }
313	}
314      else
315	{
316	  ret = ia64_getfp (c, reg_loc, &tmp);
317	  if (ret < 0)
318	    return ret;
319
320	  if (ia64_is_big_endian (c))
321	    *valp = (memcmp (&tmp, &unw.nat_val_be, sizeof (tmp)) == 0);
322	  else
323	    *valp = (memcmp (&tmp, &unw.nat_val_le, sizeof (tmp)) == 0);
324	}
325      return ret;
326    }
327
328  if ((IA64_IS_REG_LOC (nat_loc)
329       && (unsigned) (IA64_GET_REG (nat_loc) - UNW_IA64_NAT) < 128)
330      || IA64_IS_UC_LOC (reg_loc))
331    {
332      if (write)
333	return ia64_put (c, nat_loc, *valp);
334      else
335	return ia64_get (c, nat_loc, valp);
336    }
337
338  if (IA64_IS_NULL_LOC (nat_loc))
339    {
340      /* NaT bit is not saved. This happens if a general register is
341	 saved to a branch register.  Since the NaT bit gets lost, we
342	 need to drop it here, too.  Note that if the NaT bit had been
343	 set when the save occurred, it would have caused a NaT
344	 consumption fault.  */
345      if (write)
346	{
347	  if (*valp)
348	    return -UNW_EBADREG;	/* can't set NaT bit */
349	}
350      else
351	*valp = 0;
352      return 0;
353    }
354
355  mask = (unw_word_t) 1 << nat_bitnr;
356  return update_nat (c, nat_loc, mask, valp, write);
357}
358
359HIDDEN int
360tdep_access_reg (struct cursor *c, unw_regnum_t reg, unw_word_t *valp,
361		 int write)
362{
363  ia64_loc_t loc, reg_loc, nat_loc;
364  unw_word_t mask, val;
365  uint8_t nat_bitnr;
366  int ret;
367
368  switch (reg)
369    {
370      /* frame registers: */
371
372    case UNW_IA64_BSP:
373      if (write)
374	c->bsp = *valp;
375      else
376	*valp = c->bsp;
377      return 0;
378
379    case UNW_REG_SP:
380      if (write)
381	c->sp = *valp;
382      else
383	*valp = c->sp;
384      return 0;
385
386    case UNW_REG_IP:
387      if (write)
388	{
389	  c->ip = *valp;	/* also update the IP cache */
390	  if (c->pi_valid && (*valp < c->pi.start_ip || *valp >= c->pi.end_ip))
391	    c->pi_valid = 0;	/* new IP outside of current proc */
392	}
393      loc = c->loc[IA64_REG_IP];
394      break;
395
396      /* preserved registers: */
397
398    case UNW_IA64_GR + 4 ... UNW_IA64_GR + 7:
399      loc = c->loc[IA64_REG_R4 + (reg - (UNW_IA64_GR + 4))];
400      break;
401
402    case UNW_IA64_NAT + 4 ... UNW_IA64_NAT + 7:
403      loc = c->loc[IA64_REG_NAT4 + (reg - (UNW_IA64_NAT + 4))];
404      reg_loc = c->loc[IA64_REG_R4 + (reg - (UNW_IA64_NAT + 4))];
405      nat_bitnr = c->nat_bitnr[reg - (UNW_IA64_NAT + 4)];
406      return access_nat (c, loc, reg_loc, nat_bitnr, valp, write);
407
408    case UNW_IA64_AR_BSP:	loc = c->loc[IA64_REG_BSP]; break;
409    case UNW_IA64_AR_BSPSTORE:	loc = c->loc[IA64_REG_BSPSTORE]; break;
410    case UNW_IA64_AR_PFS:	loc = c->loc[IA64_REG_PFS]; break;
411    case UNW_IA64_AR_RNAT:	loc = c->loc[IA64_REG_RNAT]; break;
412    case UNW_IA64_AR_UNAT:	loc = c->loc[IA64_REG_UNAT]; break;
413    case UNW_IA64_AR_LC:	loc = c->loc[IA64_REG_LC]; break;
414    case UNW_IA64_AR_FPSR:	loc = c->loc[IA64_REG_FPSR]; break;
415    case UNW_IA64_BR + 1:	loc = c->loc[IA64_REG_B1]; break;
416    case UNW_IA64_BR + 2:	loc = c->loc[IA64_REG_B2]; break;
417    case UNW_IA64_BR + 3:	loc = c->loc[IA64_REG_B3]; break;
418    case UNW_IA64_BR + 4:	loc = c->loc[IA64_REG_B4]; break;
419    case UNW_IA64_BR + 5:	loc = c->loc[IA64_REG_B5]; break;
420
421    case UNW_IA64_CFM:
422      if (write)
423	c->cfm = *valp;	/* also update the CFM cache */
424      loc = c->cfm_loc;
425      break;
426
427    case UNW_IA64_PR:
428      /*
429       * Note: broad-side access to the predicates is NOT rotated
430       * (i.e., it is done as if CFM.rrb.pr == 0.
431       */
432      if (write)
433	{
434	  c->pr = *valp;		/* update the predicate cache */
435	  return ia64_put (c, c->loc[IA64_REG_PR], *valp);
436	}
437      else
438	return ia64_get (c, c->loc[IA64_REG_PR], valp);
439
440    case UNW_IA64_GR + 32 ... UNW_IA64_GR + 127:	/* stacked reg */
441      reg = rotate_gr (c, reg - UNW_IA64_GR);
442      if (reg < 0)
443	return -UNW_EBADREG;
444      ret = ia64_get_stacked (c, reg, &loc, NULL);
445      if (ret < 0)
446	return ret;
447      break;
448
449    case UNW_IA64_NAT + 32 ... UNW_IA64_NAT + 127:	/* stacked reg */
450      reg = rotate_gr (c, reg - UNW_IA64_NAT);
451      if (reg < 0)
452	return -UNW_EBADREG;
453      ret = ia64_get_stacked (c, reg, &loc, &nat_loc);
454      if (ret < 0)
455	return ret;
456      assert (!IA64_IS_REG_LOC (loc));
457      mask = (unw_word_t) 1 << rse_slot_num (IA64_GET_ADDR (loc));
458      return update_nat (c, nat_loc, mask, valp, write);
459
460    case UNW_IA64_AR_EC:
461      if ((ret = ia64_get (c, c->ec_loc, &val)) < 0)
462	return ret;
463
464      if (write)
465	{
466	  val = ((val & ~((unw_word_t) 0x3f << 52)) | ((*valp & 0x3f) << 52));
467	  return ia64_put (c, c->ec_loc, val);
468	}
469      else
470	{
471	  *valp = (val >> 52) & 0x3f;
472	  return 0;
473	}
474
475      /* scratch & special registers: */
476
477    case UNW_IA64_GR + 0:
478      if (write)
479	return -UNW_EREADONLYREG;
480      *valp = 0;
481      return 0;
482
483    case UNW_IA64_NAT + 0:
484      if (write)
485	return -UNW_EREADONLYREG;
486      *valp = 0;
487      return 0;
488
489    case UNW_IA64_NAT + 1:
490    case UNW_IA64_NAT + 2 ... UNW_IA64_NAT + 3:
491    case UNW_IA64_NAT + 8 ... UNW_IA64_NAT + 31:
492      loc = ia64_scratch_loc (c, reg, &nat_bitnr);
493      if (IA64_IS_NULL_LOC (loc) && reg == UNW_IA64_NAT + 1)
494	{
495	  /* access to GP */
496	  if (write)
497	    return -UNW_EREADONLYREG;
498	  *valp = 0;
499	  return 0;
500	}
501      if (!(IA64_IS_REG_LOC (loc) || IA64_IS_UC_LOC (loc)
502	    || IA64_IS_FP_LOC (loc)))
503	/* We're dealing with a NaT bit stored in memory.  */
504	return update_nat(c, loc, (unw_word_t) 1 << nat_bitnr, valp, write);
505      break;
506
507    case UNW_IA64_GR + 15 ... UNW_IA64_GR + 18:
508      mask = 1 << (reg - (UNW_IA64_GR + 15));
509      if (write)
510	{
511	  c->eh_args[reg - (UNW_IA64_GR + 15)] = *valp;
512	  c->eh_valid_mask |= mask;
513	  return 0;
514	}
515      else if ((c->eh_valid_mask & mask) != 0)
516	{
517	  *valp = c->eh_args[reg - (UNW_IA64_GR + 15)];
518	  return 0;
519	}
520      else
521	loc = ia64_scratch_loc (c, reg, NULL);
522      break;
523
524    case UNW_IA64_GR +  1:				/* global pointer */
525    case UNW_IA64_GR +  2 ... UNW_IA64_GR + 3:
526    case UNW_IA64_GR +  8 ... UNW_IA64_GR + 14:
527    case UNW_IA64_GR + 19 ... UNW_IA64_GR + 31:
528    case UNW_IA64_BR + 0:
529    case UNW_IA64_BR + 6:
530    case UNW_IA64_BR + 7:
531    case UNW_IA64_AR_RSC:
532    case UNW_IA64_AR_CSD:
533    case UNW_IA64_AR_SSD:
534    case UNW_IA64_AR_CCV:
535      loc = ia64_scratch_loc (c, reg, NULL);
536      if (IA64_IS_NULL_LOC (loc) && reg == UNW_IA64_GR + 1)
537	{
538	  /* access to GP */
539	  if (write)
540	    return -UNW_EREADONLYREG;
541
542	  /* ensure c->pi is up-to-date: */
543	  if ((ret = ia64_make_proc_info (c)) < 0)
544	    return ret;
545	  *valp = c->pi.gp;
546	  return 0;
547	}
548      break;
549
550    default:
551      Debug (1, "bad register number %d\n", reg);
552      return -UNW_EBADREG;
553    }
554
555  if (write)
556    return ia64_put (c, loc, *valp);
557  else
558    return ia64_get (c, loc, valp);
559}
560
561HIDDEN int
562tdep_access_fpreg (struct cursor *c, int reg, unw_fpreg_t *valp,
563		   int write)
564{
565  ia64_loc_t loc;
566
567  switch (reg)
568    {
569    case UNW_IA64_FR + 0:
570      if (write)
571	return -UNW_EREADONLYREG;
572      *valp = unw.read_only.f0;
573      return 0;
574
575    case UNW_IA64_FR + 1:
576      if (write)
577	return -UNW_EREADONLYREG;
578
579      if (ia64_is_big_endian (c))
580	*valp = unw.read_only.f1_be;
581      else
582	*valp = unw.read_only.f1_le;
583      return 0;
584
585    case UNW_IA64_FR + 2: loc = c->loc[IA64_REG_F2]; break;
586    case UNW_IA64_FR + 3: loc = c->loc[IA64_REG_F3]; break;
587    case UNW_IA64_FR + 4: loc = c->loc[IA64_REG_F4]; break;
588    case UNW_IA64_FR + 5: loc = c->loc[IA64_REG_F5]; break;
589
590    case UNW_IA64_FR + 16 ... UNW_IA64_FR + 31:
591      loc = c->loc[IA64_REG_F16 + (reg - (UNW_IA64_FR + 16))];
592      break;
593
594    case UNW_IA64_FR + 6 ... UNW_IA64_FR + 15:
595      loc = ia64_scratch_loc (c, reg, NULL);
596      break;
597
598    case UNW_IA64_FR + 32 ... UNW_IA64_FR + 127:
599      reg = rotate_fr (c, reg - UNW_IA64_FR) + UNW_IA64_FR;
600      loc = ia64_scratch_loc (c, reg, NULL);
601      break;
602
603    default:
604      Debug (1, "bad register number %d\n", reg);
605      return -UNW_EBADREG;
606    }
607
608  if (write)
609    return ia64_putfp (c, loc, *valp);
610  else
611    return ia64_getfp (c, loc, valp);
612}
613