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
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
|
;
; traps.s provides entry points to the exception handlers
; as well as the system call interface.
;
global check_signals
global syscall_init
global traps_init
extern call_table
extern ctask
extern idt
extern panic
extern printk
extern register_trap
extern reschedule
extern sighandler_default
extern ticked
; here we define offsets into task_struct.
; if task_struct is modified, this must be
; too.
%define ts_signal 6
%define ts_sig_handlers 18
%define ts_tss_esp0 150
%macro SAVE 0
pusha
; save the data segment selectors
mov ax, ds
push ax
; switch to kernel data segments
mov ax, 0x10
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
; restore EAX (needed for syscalls)
mov eax, [esp+30]
%endmacro
%macro SAVE_ERR 0
pusha
; fetch the error code that was pushed
mov ebx, [esp+32]
; save the data segment selectors
mov ax, ds
push ax
; switch to kernel data segments
mov ax, 0x10
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
%endmacro
%macro RESTORE 0
; restore the data segment selectors
pop ax
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
popa
iret
%endmacro
%macro RESTORE_ERR 0
; restore the data segment selectors
pop ax
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
popa
add esp, 4
iret
%endmacro
traps:
dd exc_div
dd exc_debug
dd exc_nmi
dd exc_brk
dd exc_ovf
dd exc_bounds
dd exc_invop
dd exc_nomath
dd exc_dbf
dd exc_coseg
dd exc_invtss
dd exc_segnotpres
dd exc_stackf
dd exc_gprot
dd exc_pagef
dd exc_reserved
dd exc_mathf
dd exc_alignchk
dd exc_machinechk
dd exc_simdfp
dd exc_virt
dd exc_reserved
dd exc_reserved
dd exc_reserved
dd exc_reserved
dd exc_reserved
dd exc_reserved
dd exc_reserved
dd exc_reserved
dd exc_reserved
dd exc_reserved
dd exc_reserved
traps_init:
push ebp
mov ebp, esp
mov ecx, 0
mov edx, idt
.loop:
mov eax, ecx
shl eax, 2
add eax, traps
mov eax, [eax]
mov [edx], ax
shr eax, 16
mov [edx+6], ax
mov word [edx+2], 0x08
mov byte [edx+4], 0x00
mov byte [edx+5], 0x8E
inc ecx
cmp ecx, 32
je .end
add edx, 8
jmp .loop
.end:
pop ebp
ret
exc_div:
SAVE
RESTORE
exc_debug:
SAVE
RESTORE
exc_nmi:
SAVE
RESTORE
exc_brk:
SAVE
RESTORE
exc_ovf:
SAVE
RESTORE
exc_bounds:
SAVE
RESTORE
exc_invop:
SAVE
RESTORE
exc_nomath:
SAVE
RESTORE
exc_dbf:
SAVE_ERR
RESTORE_ERR
exc_coseg:
SAVE
RESTORE
exc_invtss:
SAVE_ERR
RESTORE_ERR
exc_segnotpres:
SAVE_ERR
RESTORE_ERR
exc_stackf:
SAVE_ERR
RESTORE_ERR
exc_gprot:
SAVE_ERR
push ebx
push .fmt
call printk
add esp, 8
call panic
RESTORE_ERR
.fmt:
db "General Protection Fault (0x%08X)", 10, 0
exc_pagef:
SAVE_ERR
; here we check if the page fault was
; caused by a process trying to return
; from a signal handler (this should cause
; a page fault for accessing page
; 0xFFFFE000 during the handler's
; execution).
; check if the offending address is 0xFFFFE000
mov eax, cr2
cmp eax, 0xFFFFE000
jne .skip
; check if we're actually handling a signal
mov eax, [ctask]
mov eax, [eax+ts_tss_esp0]
cmp eax, 0xFFFFFFFF
je .skip
; final checks that a user-initiated read caused the fault
mov eax, ebx
and eax, 6
cmp eax, 4
jne .skip
jmp sigret
.skip:
; if the above conditions are false, a
; signal handler was not trying to return,
; so we handle the page fault normally
push ebx
mov ebx, cr2
push ebx
push .fmt
call printk
add esp, 12
call panic
RESTORE_ERR
.fmt:
db "Page Fault (illegal memory access: 0x%08X:0x%02X)", 10, 0
exc_mathf:
SAVE
RESTORE
exc_alignchk:
SAVE
RESTORE
exc_machinechk:
SAVE
RESTORE
exc_simdfp:
SAVE
RESTORE
exc_virt:
SAVE
RESTORE
exc_reserved:
SAVE
RESTORE
syscall_handler:
SAVE
mov dword [ticked], 0
; push the arguments onto the stack, then
; calculate the offset for the appropriate
; handler function and call it
push edx
push ecx
push ebx
and eax, 0xFF
shl eax, 2
add eax, call_table
call [eax]
add esp, 12
; preserve the syscall's return value
mov [esp+30], eax
; check if the timer fired while we were in kernel mode
mov eax, [ticked]
jz .skip
call reschedule
.skip:
; check pending signals and handle them (if any)
mov eax, esp
push eax
call check_signals
add esp, 4
RESTORE
; void check_signals(void *saved_state)
check_signals:
push ebp
mov ebp, esp
push ebx
push esi
; abort if a signal is already being handled
mov eax, [ctask]
mov ebx, [eax+ts_tss_esp0]
cmp ebx, 0xFFFFFFFF
jne .end
; check if there are any pending signals
; if so, we'll resume at the signal
; handler if a custom handler has been
; defined, otherwise, we run the default
; handler.
mov edx, [eax+ts_signal]
cmp edx, 0
je .end
; find which signal has been triggered (then clear it's flag)
bsf ecx, edx
mov ebx, 1
shl ebx, cl
xor ebx, 0xFFFFFFFF
and dword [eax+ts_signal], ebx
inc ecx
mov ebx, ecx
; calculate offset to the handler function
shl ecx, 2
add ecx, eax
add ecx, ts_sig_handlers
cmp dword [ecx], 0
jne .custom_handler
; call the default handler
push ebx
call sighandler_default
add esp, 4
jmp .end
.custom_handler:
; save our stack pointer
mov esi, [ebp+8]
mov [eax+ts_tss_esp0], esi
; here, we push the return address onto
; the user's stack. we give a bogus return
; address of 0xFFFFE000 (the second-last
; page) to deliberately trigger a page
; fault that will alert us when the signal
; handler tried to return.
mov ebx, [ebp+8]
mov esi, ebx
add esi, 46
mov esi, [esi]
sub esi, 4
mov dword [esi], 0xFFFFE000
; push a new state onto our stack
push dword [ebx+50] ; SS
push dword esi ; ESP
push dword [ebx+42] ; EFLAGS
push dword [ebx+38] ; CS
push dword [ecx] ; EIP
push dword 0 ; EAX
push dword 0 ; ECX
push dword 0 ; EDX
push dword 0 ; EBX
push dword 0 ; ESP
push dword [ebx+10] ; EBP
push dword 0 ; ESI
push dword 0 ; EDI
push word [ebx] ; DS
RESTORE
.end:
pop esi
pop ebx
pop ebp
ret
sigret:
mov ebx, esp
push ebx
call check_signals
add esp, 4
; restore our original stack frame and reset the TSS's ESP0
mov esp, [eax+ts_tss_esp0]
mov dword [eax+ts_tss_esp0], 0xFFFFFFFF
; finally, perform the normal syscall return
RESTORE
syscall_init:
push ebp
mov ebp, esp
push syscall_handler
push dword 3
push dword 0x80
call register_trap
add esp, 12
pop ebp
ret
|