Imported Upstream version 1.1
[platform/upstream/libunwind.git] / src / ia64 / Gregs.c
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
5 This file is part of libunwind.
6
7 Permission is hereby granted, free of charge, to any person obtaining
8 a copy of this software and associated documentation files (the
9 "Software"), to deal in the Software without restriction, including
10 without limitation the rights to use, copy, modify, merge, publish,
11 distribute, sublicense, and/or sell copies of the Software, and to
12 permit persons to whom the Software is furnished to do so, subject to
13 the following conditions:
14
15 The above copyright notice and this permission notice shall be
16 included in all copies or substantial portions of the Software.
17
18 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
22 LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
23 OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
24 WITH 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
30 static inline ia64_loc_t
31 linux_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
222 static inline ia64_loc_t
223 hpux_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
232 HIDDEN ia64_loc_t
233 ia64_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
248 static inline int
249 update_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
272 static int
273 access_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
359 HIDDEN int
360 tdep_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
561 HIDDEN int
562 tdep_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 }