]>
Commit | Line | Data |
---|---|---|
b168057a | 1 | /* Copyright (C) 2004-2015 Free Software Foundation, Inc. |
d5efd131 MF |
2 | This file is part of the GNU C Library. |
3 | Contributed by David Mosberger-Tang <davidm@hpl.hp.com>. | |
4 | ||
5 | The GNU C Library is free software; you can redistribute it and/or | |
6 | modify it under the terms of the GNU Lesser General Public | |
7 | License as published by the Free Software Foundation; either | |
8 | version 2.1 of the License, or (at your option) any later version. | |
9 | ||
10 | The GNU C Library is distributed in the hope that it will be useful, | |
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
13 | Lesser General Public License for more details. | |
14 | ||
15 | You should have received a copy of the GNU Lesser General Public | |
75efb018 MF |
16 | License along with the GNU C Library; if not, see |
17 | <http://www.gnu.org/licenses/>. */ | |
d5efd131 MF |
18 | |
19 | /* The public __longjmp() implementation is limited to jumping within | |
20 | the same stack. That is, in general it is not possible to use this | |
21 | __longjmp() implementation to cross from one stack to another. | |
382466e0 | 22 | In contrast, the __sigstack_longjmp() implemented here allows |
d5efd131 MF |
23 | crossing from the alternate signal stack to the normal stack |
24 | as a special case. */ | |
25 | ||
26 | #include <assert.h> | |
27 | #include <setjmp.h> | |
28 | #include <signal.h> | |
29 | #include <stdint.h> | |
30 | #include <stdlib.h> | |
31 | ||
32 | #include <sysdep.h> | |
33 | #include <sys/rse.h> | |
34 | ||
35 | #define JB_SP 0 | |
36 | #define JB_BSP 17 | |
37 | ||
38 | struct rbs_flush_values | |
39 | { | |
40 | unsigned long bsp; | |
41 | unsigned long rsc; | |
42 | unsigned long rnat; | |
43 | }; | |
44 | ||
45 | extern struct rbs_flush_values __ia64_flush_rbs (void); | |
46 | extern void __ia64_longjmp (__jmp_buf buf, int val, long rnat, long rsc) | |
47 | __attribute__ ((__noreturn__)); | |
48 | ||
49 | static void | |
50 | copy_rbs (unsigned long *dst, unsigned long *dst_end, unsigned long dst_rnat, | |
51 | unsigned long *src, unsigned long *src_end, | |
52 | unsigned long current_rnat) | |
53 | { | |
54 | unsigned long dst_slot, src_rnat = 0, src_slot, *src_rnat_addr, nat_bit; | |
55 | int first_time = 1; | |
56 | ||
57 | while (dst < dst_end) | |
58 | { | |
59 | dst_slot = ia64_rse_slot_num (dst); | |
60 | if (dst_slot == 63) | |
61 | { | |
62 | *dst++ = dst_rnat; | |
63 | dst_rnat = 0; | |
64 | } | |
65 | else | |
66 | { | |
67 | /* read source value, including NaT bit: */ | |
68 | src_slot = ia64_rse_slot_num (src); | |
69 | if (src_slot == 63) | |
70 | { | |
71 | /* skip src RNaT slot */ | |
72 | ++src; | |
73 | src_slot = 0; | |
74 | } | |
75 | if (first_time || src_slot == 0) | |
76 | { | |
77 | first_time = 0; | |
78 | src_rnat_addr = ia64_rse_rnat_addr (src); | |
79 | if (src_rnat_addr < src_end) | |
80 | src_rnat = *src_rnat_addr; | |
81 | else | |
82 | src_rnat = current_rnat; | |
83 | } | |
84 | nat_bit = (src_rnat >> src_slot) & 1; | |
85 | ||
86 | assert (src < src_end); | |
87 | ||
88 | *dst++ = *src++; | |
89 | if (nat_bit) | |
90 | dst_rnat |= (1UL << dst_slot); | |
91 | else | |
92 | dst_rnat &= ~(1UL << dst_slot); | |
93 | } | |
94 | } | |
95 | dst_slot = ia64_rse_slot_num (dst); | |
96 | if (dst_slot > 0) | |
97 | *ia64_rse_rnat_addr (dst) = dst_rnat; | |
98 | } | |
99 | ||
100 | void | |
101 | __sigstack_longjmp (__jmp_buf buf, int val) | |
102 | { | |
103 | unsigned long *rbs_base, *bsp, *bspstore, *jb_bsp, jb_sp, ss_sp; | |
104 | unsigned long ndirty, rnat, load_rnat, *jb_rnat_addr; | |
105 | struct sigcontext *sc; | |
106 | stack_t stk; | |
107 | struct rbs_flush_values c; | |
108 | ||
109 | /* put RSE into enforced-lazy mode and return current bsp/rsc/rnat: */ | |
110 | c = __ia64_flush_rbs (); | |
111 | ||
112 | jb_sp = ((unsigned long *) buf)[JB_SP]; | |
113 | jb_bsp = ((unsigned long **) buf)[JB_BSP]; | |
114 | ||
115 | INTERNAL_SYSCALL_DECL (err); | |
116 | (void) INTERNAL_SYSCALL (sigaltstack, err, 2, NULL, &stk); | |
117 | ||
118 | ss_sp = (unsigned long) stk.ss_sp; | |
119 | jb_rnat_addr = ia64_rse_rnat_addr (jb_bsp); | |
120 | ||
121 | if ((stk.ss_flags & SS_ONSTACK) == 0 || jb_sp - ss_sp < stk.ss_size) | |
122 | /* Normal non-stack-crossing longjmp; if the RNaT slot for the bsp | |
123 | saved in the jump-buffer is the same as the one for the current | |
124 | BSP, use the current AR.RNAT value, otherwise, load it from the | |
125 | jump-buffer's RNaT-slot. */ | |
126 | load_rnat = (ia64_rse_rnat_addr ((unsigned long *) c.bsp) != jb_rnat_addr); | |
127 | else | |
128 | { | |
129 | /* If we are on the alternate signal-stack and the jump-buffer | |
130 | lies outside the signal-stack, we may need to copy back the | |
131 | dirty partition which was torn off and saved on the | |
132 | signal-stack when the signal was delivered. | |
133 | ||
134 | Caveat: we assume that the top of the alternate signal-stack | |
135 | stores the sigcontext structure of the signal that | |
136 | caused the switch to the signal-stack. This should | |
137 | be a fairly safe assumption but the kernel _could_ | |
138 | do things differently.. */ | |
139 | sc = ((struct sigcontext *) ((ss_sp + stk.ss_size) & -16) - 1); | |
140 | ||
141 | /* As a sanity-check, verify that the register-backing-store base | |
142 | of the alternate signal-stack is where we expect it. */ | |
143 | rbs_base = (unsigned long *) | |
144 | ((ss_sp + sizeof (long) - 1) & -sizeof (long)); | |
145 | ||
146 | assert ((unsigned long) rbs_base == sc->sc_rbs_base); | |
147 | ||
148 | ndirty = ia64_rse_num_regs (rbs_base, rbs_base + (sc->sc_loadrs >> 19)); | |
149 | bsp = (unsigned long *) sc->sc_ar_bsp; | |
150 | bspstore = ia64_rse_skip_regs (bsp, -ndirty); | |
151 | ||
152 | if (bspstore < jb_bsp) | |
153 | /* AR.BSPSTORE at the time of the signal was below the value | |
154 | of AR.BSP saved in the jump-buffer => copy the missing | |
155 | portion from the torn off dirty partition which got saved | |
156 | on the alternate signal-stack. */ | |
157 | copy_rbs (bspstore, jb_bsp, sc->sc_ar_rnat, | |
158 | rbs_base, (unsigned long *) c.bsp, c.rnat); | |
159 | ||
160 | load_rnat = 1; | |
161 | } | |
162 | if (load_rnat) | |
163 | rnat = *jb_rnat_addr; | |
164 | else | |
165 | rnat = c.rnat; | |
166 | __ia64_longjmp (buf, val, rnat, c.rsc); | |
167 | } |