Gregs.c revision cf2a44ca4934c5c5a91959240d48b37937730c4b
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	return -UNW_EREADONLYREG;
375      *valp = c->bsp;
376      return 0;
377
378    case UNW_REG_SP:
379      if (write)
380	return -UNW_EREADONLYREG;
381      *valp = c->sp;
382      return 0;
383
384    case UNW_REG_IP:
385      if (write)
386	{
387	  c->ip = *valp;	/* also update the IP cache */
388	  if (c->pi_valid && (*valp < c->pi.start_ip || *valp >= c->pi.end_ip))
389	    c->pi_valid = 0;	/* new IP outside of current proc */
390	}
391      loc = c->loc[IA64_REG_IP];
392      break;
393
394      /* preserved registers: */
395
396    case UNW_IA64_GR + 4 ... UNW_IA64_GR + 7:
397      loc = c->loc[IA64_REG_R4 + (reg - (UNW_IA64_GR + 4))];
398      break;
399
400    case UNW_IA64_NAT + 4 ... UNW_IA64_NAT + 7:
401      loc = c->loc[IA64_REG_NAT4 + (reg - (UNW_IA64_NAT + 4))];
402      reg_loc = c->loc[IA64_REG_R4 + (reg - (UNW_IA64_NAT + 4))];
403      nat_bitnr = c->nat_bitnr[reg - (UNW_IA64_NAT + 4)];
404      return access_nat (c, loc, reg_loc, nat_bitnr, valp, write);
405
406    case UNW_IA64_AR_BSP:	loc = c->loc[IA64_REG_BSP]; break;
407    case UNW_IA64_AR_BSPSTORE:	loc = c->loc[IA64_REG_BSPSTORE]; break;
408    case UNW_IA64_AR_PFS:	loc = c->loc[IA64_REG_PFS]; break;
409    case UNW_IA64_AR_RNAT:	loc = c->loc[IA64_REG_RNAT]; break;
410    case UNW_IA64_AR_UNAT:	loc = c->loc[IA64_REG_UNAT]; break;
411    case UNW_IA64_AR_LC:	loc = c->loc[IA64_REG_LC]; break;
412    case UNW_IA64_AR_FPSR:	loc = c->loc[IA64_REG_FPSR]; break;
413    case UNW_IA64_BR + 1:	loc = c->loc[IA64_REG_B1]; break;
414    case UNW_IA64_BR + 2:	loc = c->loc[IA64_REG_B2]; break;
415    case UNW_IA64_BR + 3:	loc = c->loc[IA64_REG_B3]; break;
416    case UNW_IA64_BR + 4:	loc = c->loc[IA64_REG_B4]; break;
417    case UNW_IA64_BR + 5:	loc = c->loc[IA64_REG_B5]; break;
418
419    case UNW_IA64_CFM:
420      if (write)
421	c->cfm = *valp;	/* also update the CFM cache */
422      loc = c->cfm_loc;
423      break;
424
425    case UNW_IA64_PR:
426      /*
427       * Note: broad-side access to the predicates is NOT rotated
428       * (i.e., it is done as if CFM.rrb.pr == 0.
429       */
430      if (write)
431	{
432	  c->pr = *valp;		/* update the predicate cache */
433	  return ia64_put (c, c->loc[IA64_REG_PR], *valp);
434	}
435      else
436	return ia64_get (c, c->loc[IA64_REG_PR], valp);
437
438    case UNW_IA64_GR + 32 ... UNW_IA64_GR + 127:	/* stacked reg */
439      reg = rotate_gr (c, reg - UNW_IA64_GR);
440      if (reg < 0)
441	return -UNW_EBADREG;
442      ret = ia64_get_stacked (c, reg, &loc, NULL);
443      if (ret < 0)
444	return ret;
445      break;
446
447    case UNW_IA64_NAT + 32 ... UNW_IA64_NAT + 127:	/* stacked reg */
448      reg = rotate_gr (c, reg - UNW_IA64_NAT);
449      if (reg < 0)
450	return -UNW_EBADREG;
451      ret = ia64_get_stacked (c, reg, &loc, &nat_loc);
452      if (ret < 0)
453	return ret;
454      assert (!IA64_IS_REG_LOC (loc));
455      mask = (unw_word_t) 1 << rse_slot_num (IA64_GET_ADDR (loc));
456      return update_nat (c, nat_loc, mask, valp, write);
457
458    case UNW_IA64_AR_EC:
459      if ((ret = ia64_get (c, c->ec_loc, &val)) < 0)
460	return ret;
461
462      if (write)
463	{
464	  val = ((val & ~((unw_word_t) 0x3f << 52)) | ((*valp & 0x3f) << 52));
465	  return ia64_put (c, c->ec_loc, val);
466	}
467      else
468	{
469	  *valp = (val >> 52) & 0x3f;
470	  return 0;
471	}
472
473      /* scratch & special registers: */
474
475    case UNW_IA64_GR + 0:
476      if (write)
477	return -UNW_EREADONLYREG;
478      *valp = 0;
479      return 0;
480
481    case UNW_IA64_NAT + 0:
482      if (write)
483	return -UNW_EREADONLYREG;
484      *valp = 0;
485      return 0;
486
487    case UNW_IA64_NAT + 1:
488    case UNW_IA64_NAT + 2 ... UNW_IA64_NAT + 3:
489    case UNW_IA64_NAT + 8 ... UNW_IA64_NAT + 31:
490      loc = ia64_scratch_loc (c, reg, &nat_bitnr);
491      if (IA64_IS_NULL_LOC (loc) && reg == UNW_IA64_NAT + 1)
492	{
493	  /* access to GP */
494	  if (write)
495	    return -UNW_EREADONLYREG;
496	  *valp = 0;
497	  return 0;
498	}
499      if (!(IA64_IS_REG_LOC (loc) || IA64_IS_UC_LOC (loc)
500	    || IA64_IS_FP_LOC (loc)))
501	/* We're dealing with a NaT bit stored in memory.  */
502	return update_nat(c, loc, (unw_word_t) 1 << nat_bitnr, valp, write);
503      break;
504
505    case UNW_IA64_GR + 15 ... UNW_IA64_GR + 18:
506      mask = 1 << (reg - (UNW_IA64_GR + 15));
507      if (write)
508	{
509	  c->eh_args[reg - (UNW_IA64_GR + 15)] = *valp;
510	  c->eh_valid_mask |= mask;
511	  return 0;
512	}
513      else if ((c->eh_valid_mask & mask) != 0)
514	{
515	  *valp = c->eh_args[reg - (UNW_IA64_GR + 15)];
516	  return 0;
517	}
518      else
519	loc = ia64_scratch_loc (c, reg, NULL);
520      break;
521
522    case UNW_IA64_GR +  1:				/* global pointer */
523    case UNW_IA64_GR +  2 ... UNW_IA64_GR + 3:
524    case UNW_IA64_GR +  8 ... UNW_IA64_GR + 14:
525    case UNW_IA64_GR + 19 ... UNW_IA64_GR + 31:
526    case UNW_IA64_BR + 0:
527    case UNW_IA64_BR + 6:
528    case UNW_IA64_BR + 7:
529    case UNW_IA64_AR_RSC:
530    case UNW_IA64_AR_CSD:
531    case UNW_IA64_AR_SSD:
532    case UNW_IA64_AR_CCV:
533      loc = ia64_scratch_loc (c, reg, NULL);
534      if (IA64_IS_NULL_LOC (loc) && reg == UNW_IA64_GR + 1)
535	{
536	  /* access to GP */
537	  if (write)
538	    return -UNW_EREADONLYREG;
539
540	  /* ensure c->pi is up-to-date: */
541	  if ((ret = ia64_make_proc_info (c)) < 0)
542	    return ret;
543	  *valp = c->pi.gp;
544	  return 0;
545	}
546      break;
547
548    default:
549      Debug (1, "bad register number %d\n", reg);
550      return -UNW_EBADREG;
551    }
552
553  if (write)
554    return ia64_put (c, loc, *valp);
555  else
556    return ia64_get (c, loc, valp);
557}
558
559HIDDEN int
560tdep_access_fpreg (struct cursor *c, int reg, unw_fpreg_t *valp,
561		   int write)
562{
563  ia64_loc_t loc;
564
565  switch (reg)
566    {
567    case UNW_IA64_FR + 0:
568      if (write)
569	return -UNW_EREADONLYREG;
570      *valp = unw.read_only.f0;
571      return 0;
572
573    case UNW_IA64_FR + 1:
574      if (write)
575	return -UNW_EREADONLYREG;
576
577      if (ia64_is_big_endian (c))
578	*valp = unw.read_only.f1_be;
579      else
580	*valp = unw.read_only.f1_le;
581      return 0;
582
583    case UNW_IA64_FR + 2: loc = c->loc[IA64_REG_F2]; break;
584    case UNW_IA64_FR + 3: loc = c->loc[IA64_REG_F3]; break;
585    case UNW_IA64_FR + 4: loc = c->loc[IA64_REG_F4]; break;
586    case UNW_IA64_FR + 5: loc = c->loc[IA64_REG_F5]; break;
587
588    case UNW_IA64_FR + 16 ... UNW_IA64_FR + 31:
589      loc = c->loc[IA64_REG_F16 + (reg - (UNW_IA64_FR + 16))];
590      break;
591
592    case UNW_IA64_FR + 6 ... UNW_IA64_FR + 15:
593      loc = ia64_scratch_loc (c, reg, NULL);
594      break;
595
596    case UNW_IA64_FR + 32 ... UNW_IA64_FR + 127:
597      reg = rotate_fr (c, reg - UNW_IA64_FR) + UNW_IA64_FR;
598      loc = ia64_scratch_loc (c, reg, NULL);
599      break;
600
601    default:
602      Debug (1, "bad register number %d\n", reg);
603      return -UNW_EBADREG;
604    }
605
606  if (write)
607    return ia64_putfp (c, loc, *valp);
608  else
609    return ia64_getfp (c, loc, valp);
610}
611