1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
|
/* ia32-activation-entry.S - Activation handler dispatcher.
Copyright (C) 2007, 2008 Free Software Foundation, Inc.
Written by Neal H. Walfield <neal@gnu.org>.
This file is part of the GNU Hurd.
The GNU Hurd is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 3 of the
License, or (at your option) any later version.
The GNU Hurd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see
<http://www.gnu.org/licenses/>. */
#include <hurd/stddef.h>
.text
/* Offsets into a struct vg_utcb. */
#define UTCB_MODE (0*4)
#define UTCB_SAVED_IP (1*4)
#define UTCB_SAVED_SP (2*4)
#define UTCB_ACTIVATION_FRAME_STACK (512 + 0*4)
/* The bits of the mode word. */
#define ACTIVATED_MODE_BIT 0
#define PENDING_MESSAGE_BIT 1
#define INTERRUPT_IN_TRANSITION_BIT 2
/* Offsets into a struct activation_frame. */
#define AF_SAVED_EAX 0
#define AF_SAVED_ECX 4
#define AF_SAVED_EDX 8
#define AF_SAVED_FLAGS 12
#define AF_SAVED_IP 16
#define AF_SAVED_EBX 20
#define AF_SAVED_EDI 24
#define AF_SAVED_ESI 28
#define AF_SAVED_EBP 32
#define AF_SAVED_ESP 36
#define AF_NORMAL_MODE_STACK 40
#define AF_NEXT 44
/* Handle an activation. */
.globl hurd_activation_handler_entry, _hurd_activation_handler_entry
hurd_activation_handler_entry:
_hurd_activation_handler_entry:
/* How we will use the stack:
relative to entry sp
| relative to sp after saving the register file.
| |
v v
+0 +24 pointer to utcb
-4 +20 entry eflags
-8 +16 entry edx
-12 +12 saved eax \
-16 +8 saved ecx \ save
-20 +4 saved flags / area
-24 +0 saved edx /
*/
/* %ESP points to the top of the UTCB. If the interrupt in
transition flag is not set, then we need to save the
caller-saved registers. Otherwise, we were interrupted while
returning to normal mode and the the saved state, not our
registers, reflects the real user state (see big comment below
for more information). */
/* Save the eflags before we do *anything*. */
pushf
/* We need to check if the interrupt in transition flag is
set. Free up a register and make it a pointer to the UTCB.
Recall: we stashed a pointer to the UTCB at the word following
the botton of the stack. */
pushl %edx
mov 8(%esp), %edx
bt $INTERRUPT_IN_TRANSITION_BIT, UTCB_MODE(%edx)
jc skip_save
/* Nope; we need to save the current EAX and ECX and copy the
entry eflags and EDX into the save area. */
pushl %eax
pushl %ecx
/* entry eflags. */
mov 12(%esp), %eax
pushl %eax
/* entry edx. */
mov 12(%esp), %eax
pushl %eax
jmp after_adjust
skip_save:
/* We don't save the entry registers but use the saved values.
Adjust the stack pointer to point to the start of the save area. */
sub $16, %esp
after_adjust:
/* We are going to call the activation handler. According to
the i386 ABI:
- caller saved registers are: eax, ecx, edx
- callee saved registers are: ebp, ebx, esi, edi
We've already saved the original eax, ecx and edx. The
function will preserve the rest.
The only value we care about is our pointer to the UTCB (which
is in edx) and which we can save on the stack. */
pushl %edx
/* The activation handler function takes a single argument:
the UTCB. */
pushl %edx
/* Clear the direction flag as per the calling conventions. */
cld
call hurd_activation_handler_activated
/* The activation frame, if any, is in EAX. */
/* Clear the arguments. */
add $4, %esp
/* Restore UTCB pointer. */
popl %edx
/* Check if hurd_activation_handler_activated returned an
activation frame. */
test %eax, %eax
jnz activation_frame_run
/* There is no activation frame, transition immediately back to
normal mode.
To return to normal mode, we need to restore the saved
registers, including the saved general registers, saved ESP
and saved EIP. On x86, there is no way to atomically restore
ESP and EIP from user code. The solution we use is:
- save the saved EIP on the user stack
- restore the saved ESP minus 4
- execute a ret instruction
Beyond not being atomic, this has the additional problem that
writing on the user stack may cause a fault.
To work around this latter problem, we only write on the user
stack once we return to normal mode. If this faults, the
kernel can transition us back to activated mode.
But this raises another problem: the IP and SP that the kernel
sees are not those that return us to user code. As this code
relies on the activation stack, a nested stack will leave us in
an inconsistent state. (This can also happen if we receive a
message before returning to user code.) To avoid this, we
register our restore to normal mode function with the kernel.
If the kernel transitions us back to activated mode while the
EIP is in this range, then it does not save the EIP and ESP
and invokes the activation handler with the
interrupt_in_transition flag set. */
/* Reset the activation bit. */
and $(~1), UTCB_MODE(%edx)
/* Check for pending messages. This does not need to be
atomic as if we get interrupted here, we automatically
transition back to activated mode. */
bt $PENDING_MESSAGE_BIT, UTCB_MODE(%edx)
jnc no_pending
/* There is a pending activation. Force its delivery. As we
are no longer in activated mode, either we'll be activated
with the interrupt-in-transition bit set (and thus never
return here) or we'll return. In the latter case, we just
resume execution. */
/* Save the UTCB. */
pushl %edx
cld
call hurd_activation_fetch
popl %edx
no_pending:
/* Set EAX to the start of the save area. */
mov %esp, %eax
/* Restore the user stack. */
mov UTCB_SAVED_SP(%edx), %esp
/* Copy the saved EIP and saved flags to the user stack. */
mov UTCB_SAVED_IP(%edx), %ecx
pushl %ecx
mov 4(%eax), %ecx
pushl %ecx
/* Restore the general-purpose registers. */
mov 0(%eax), %edx
mov 8(%eax), %ecx
mov 12(%eax), %eax
/* Restore the saved eflags. */
popf
/* And finally, the saved EIP and in doing so the saved ESP. */
ret
activation_frame_run:
/* EAX contains the activation frame, EDX the UTCB, and ESP
points to the save area. ECX has been saved in the save area. */
/* Copy all relevant register state from the UTCB
and save area to the activation frame. We use ecx as the
intermediate. */
mov UTCB_SAVED_IP(%edx), %ecx
mov %ecx, AF_SAVED_IP(%eax)
mov UTCB_SAVED_SP(%edx), %ecx
mov %ecx, AF_SAVED_ESP(%eax)
/* edx. */
mov 0(%esp), %ecx
mov %ecx, AF_SAVED_EDX(%eax)
/* flags. */
mov 4(%esp), %ecx
mov %ecx, AF_SAVED_FLAGS(%eax)
/* ecx. */
mov 8(%esp), %ecx
mov %ecx, AF_SAVED_ECX(%eax)
/* eax. */
mov 12(%esp), %ecx
mov %ecx, AF_SAVED_EAX(%eax)
/* We save the rest for debugging purposes. */
mov %ebx, AF_SAVED_EBX(%eax)
mov %edi, AF_SAVED_EDI(%eax)
mov %esi, AF_SAVED_ESI(%eax)
mov %ebp, AF_SAVED_EBP(%eax)
/* Abandon the activation stack. If AF->NORMAL_MODE_STACK is
0, we use the top of the normal user stack. Otherwise, we use
the stack indicated by AF->NORMAL_MODE_STACK. */
mov AF_NORMAL_MODE_STACK(%eax), %esp
test %esp, %esp
jnz stack_setup
mov UTCB_SAVED_SP(%edx), %esp
stack_setup:
/* We've now stashed away all the state that was in the UTCB
or on the activation stack that we need to restore the
interrupted state. */
/* Clear the activation bit. */
and $(~1), UTCB_MODE(%edx)
.global hurd_activation_handler_end, _hurd_activation_handler_end
hurd_activation_handler_end:
_hurd_activation_handler_end:
/* We have now left activated mode. We've saved all the state
we need to return to the interrupted state in the activation
frame and ESP points to another stack (i.e., not the activate
stack). If a fault now occurs, nothing bad can happend. */
/* Save the UTCB pointer. */
pushl %edx
/* Save the activation frame pointer. */
pushl %eax
/* Call the continuation (two arguments: activation frame
pointer and the utcb). */
pushl %edx
pushl %eax
cld
call hurd_activation_handler_normal
/* Remove the arguments. */
add $8, %esp
/* Restore the activation frame pointer. */
popl %eax
/* And restore the UTCB pointer. */
popl %edx
/* Restore the interrupted state. */
/* First, the interrupted stack. */
mov AF_SAVED_ESP(%eax), %esp
/* Then, push the state onto that stack. */
mov AF_SAVED_IP(%eax), %ecx
pushl %ecx
mov AF_SAVED_FLAGS(%eax), %ecx
pushl %ecx
mov AF_SAVED_EDX(%eax), %ecx
pushl %ecx
mov AF_SAVED_ECX(%eax), %ecx
pushl %ecx
mov AF_SAVED_EAX(%eax), %ecx
pushl %ecx
/* Remove our activation frame, which is at the top
of the activation frame stack. */
mov AF_NEXT(%eax), %ecx
mov %ecx, UTCB_ACTIVATION_FRAME_STACK(%edx)
/* Finally, restore the register file. */
popl %eax
popl %ecx
popl %edx
popf
/* And return. */
ret
|