summaryrefslogtreecommitdiff
path: root/kernel/traps.s
blob: 36b3423d23def556eb7eb6374712385307fe1086 (plain)
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
;
; 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 10
%define ts_tss_esp0     142

%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
  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:
  call check_signals
  ; 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