2 ; Low level x64 routines used by the debug support driver.
4 ; Copyright (c) 2007 - 2008, Intel Corporation. <BR>
5 ; All rights reserved. This program and the accompanying materials
6 ; are licensed and made available under the terms and conditions of the BSD License
7 ; which accompanies this distribution. The full text of the license may be found at
8 ; http://opensource.org/licenses/bsd-license.php
10 ; THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11 ; WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
15 EXCPT64_DIVIDE_ERROR
EQU 0
18 EXCPT64_BREAKPOINT
EQU 3
19 EXCPT64_OVERFLOW
EQU 4
21 EXCPT64_INVALID_OPCODE
EQU 6
22 EXCPT64_DOUBLE_FAULT
EQU 8
23 EXCPT64_INVALID_TSS
EQU 10
24 EXCPT64_SEG_NOT_PRESENT
EQU 11
25 EXCPT64_STACK_FAULT
EQU 12
26 EXCPT64_GP_FAULT
EQU 13
27 EXCPT64_PAGE_FAULT
EQU 14
28 EXCPT64_FP_ERROR
EQU 16
29 EXCPT64_ALIGNMENT_CHECK
EQU 17
30 EXCPT64_MACHINE_CHECK
EQU 18
33 FXSTOR_FLAG
EQU 01000000h ; bit cpuid 24 of feature flags
35 ;; The FXSTOR and FXRSTOR commands are used for saving and restoring the x87,
36 ;; MMX, SSE, SSE2, etc registers. The initialization of the debugsupport driver
37 ;; MUST check the CPUID feature flags to see that these instructions are available
38 ;; and fail to init if they are not.
42 db 0fh
, 0aeh
, 00000111y
; mod = 00, reg/op = 000, r/m = 111 = [rdi]
47 db 0fh
, 0aeh
, 00001110y
; mod = 00, reg/op = 001, r/m = 110 = [rsi]
52 public OrigVector
, InterruptEntryStub
, StubSize
, CommonIdtEntry
, FxStorSupport
54 StubSize
dd InterruptEntryStubEnd
- InterruptEntryStub
55 AppRsp
dq 1111111111111111h ; ?
56 DebugRsp
dq 2222222222222222h ; ?
57 ExtraPush
dq 3333333333333333h ; ?
58 ExceptData
dq 4444444444444444h ; ?
59 Rflags
dq 5555555555555555h ; ?
60 OrigVector
dq 6666666666666666h ; ?
62 ;; The declarations below define the memory region that will be used for the debug stack.
63 ;; The context record will be built by pushing register values onto this stack.
64 ;; It is imparitive that alignment be carefully managed, since the FXSTOR and
65 ;; FXRSTOR instructions will GP fault if their memory operand is not 16 byte aligned.
67 ;; The stub will switch stacks from the application stack to the debuger stack
68 ;; and pushes the exception number.
70 ;; Then we building the context record on the stack. Since the stack grows down,
71 ;; we push the fields of the context record from the back to the front. There
72 ;; are 336 bytes of stack used prior allocating the 512 bytes of stack to be
73 ;; used as the memory buffer for the fxstor instruction. Therefore address of
74 ;; the buffer used for the FXSTOR instruction is &Eax - 336 - 512, which
75 ;; must be 16 byte aligned.
77 ;; We carefully locate the stack to make this happen.
79 ;; For reference, the context structure looks like this:
81 ;; UINT64 ExceptionData;
82 ;; FX_SAVE_STATE_X64 FxSaveState; // 512 bytes, must be 16 byte aligned
83 ;; UINT64 Dr0, Dr1, Dr2, Dr3, Dr6, Dr7;
84 ;; UINT64 Cr0, Cr1, Cr2, Cr3, Cr4, Cr8;
87 ;; UINT64 Gdtr[2], Idtr[2];
89 ;; UINT64 Gs, Fs, Es, Ds, Cs, Ss;
90 ;; UINT64 Rdi, Rsi, Rbp, Rsp, Rbx, Rdx, Rcx, Rax;
91 ;; UINT64 R8, R9, R10, R11, R12, R13, R14, R15;
92 ;; } SYSTEM_CONTEXT_X64; // 64 bit system context record
95 DebugStackEnd
db "DbgStkEnd >>>>>>" ;; 16 byte long string - must be 16 bytes to preserve alignment
96 dd 1ffch dup
(000000000h) ;; 32K should be enough stack
97 ;; This allocation is coocked to insure
98 ;; that the the buffer for the FXSTORE instruction
99 ;; will be 16 byte aligned also.
101 ExceptionNumber
dq ? ;; first entry will be the vector number pushed by the stub
103 DebugStackBegin
db "<<<< DbgStkBegin" ;; initial debug ESP == DebugStackBegin, set in stub
109 externdef
InterruptDistrubutionHub:near
111 ;------------------------------------------------------------------------------
117 ; Abstract: Returns TRUE if FxStor instructions are supported
119 FxStorSupport
PROC PUBLIC
122 ; cpuid corrupts rbx which must be preserved per the C calling convention
137 ;------------------------------------------------------------------------------
139 ; WriteInterruptFlag (
140 ; BOOLEAN NewState // rcx
143 ; Abstract: Programs interrupt flag to the requested state and returns previous
146 WriteInterruptFlag
PROC PUBLIC
160 WriteInterruptFlag
ENDP
164 ;------------------------------------------------------------------------------
167 ; IA32_IDT_GATE_DESCRIPTOR * DestDesc, // rcx
168 ; void (*Vector) (void) // rdx
171 ; Abstract: Encodes an IDT descriptor with the given physical address
173 Vect2Desc
PROC PUBLIC
176 mov word ptr [rcx
], ax ; write bits 15..0 of offset
178 mov word ptr [rcx
+2], dx ; SYS_CODE_SEL from GDT
179 mov word ptr [rcx
+4], 0e00h
OR 8000h ; type = 386 interrupt gate, present
181 mov word ptr [rcx
+6], ax ; write bits 31..16 of offset
183 mov dword ptr [rcx
+8], eax ; write bits 63..32 of offset
191 ;------------------------------------------------------------------------------
194 ; Abstract: This code is not a function, but is a small piece of code that is
195 ; copied and fixed up once for each IDT entry that is hooked.
198 push 0 ; push vector number - will be modified before installed
200 dd 0 ; fixed up to relative address of CommonIdtEntry
201 InterruptEntryStubEnd:
205 ;------------------------------------------------------------------------------
208 ; Abstract: This code is not a function, but is the common part for all IDT
213 ;; At this point, the stub has saved the current application stack esp into AppRsp
214 ;; and switched stacks to the debug stack, where it pushed the vector number
216 ;; The application stack looks like this:
219 ;; (last application stack entry)
220 ;; [16 bytes alignment, do not care it]
221 ;; SS from interrupted task
222 ;; RSP from interrupted task
223 ;; rflags from interrupted task
224 ;; CS from interrupted task
225 ;; RIP from interrupted task
226 ;; Error code <-------------------- Only present for some exeption types
228 ;; Vector Number <----------------- pushed in our IDT Entry
232 ;; The stub switched us to the debug stack and pushed the interrupt number.
234 ;; Next, construct the context record. It will be build on the debug stack by
235 ;; pushing the registers in the correct order so as to create the context structure
236 ;; on the debug stack. The context record must be built from the end back to the
237 ;; beginning because the stack grows down...
239 ;; For reference, the context record looks like this:
243 ;; UINT64 ExceptionData;
244 ;; FX_SAVE_STATE_X64 FxSaveState;
245 ;; UINT64 Dr0, Dr1, Dr2, Dr3, Dr6, Dr7;
246 ;; UINT64 Cr0, Cr2, Cr3, Cr4, Cr8;
249 ;; UINT64 Gdtr[2], Idtr[2];
251 ;; UINT64 Gs, Fs, Es, Ds, Cs, Ss;
252 ;; UINT64 Rdi, Rsi, Rbp, Rsp, Rbx, Rdx, Rcx, Rax;
253 ;; UINT64 R8, R9, R10, R11, R12, R13, R14, R15;
254 ;; } SYSTEM_CONTEXT_X64; // 64 bit system context record
256 ;; NOTE: we save rsp here to prevent compiler put rip reference cause error AppRsp
258 mov rax
, qword ptr [rsp
][8] ; save vector number
259 mov ExceptionNumber
, rax
; save vector number
261 add rsp
, 8 ; pop vector number
262 mov AppRsp
, rsp
; save stack top
263 mov rsp
, offset DebugStackBegin
; switch to debugger stack
264 sub rsp
, 8 ; leave space for vector number
266 ;; UINT64 Rdi, Rsi, Rbp, Rsp, Rbx, Rdx, Rcx, Rax;
267 ;; UINT64 R8, R9, R10, R11, R12, R13, R14, R15;
285 ;; Save interrupt state rflags register...
288 mov qword ptr Rflags
, rax
290 ;; We need to determine if any extra data was pushed by the exception, and if so, save it
291 ;; To do this, we check the exception number pushed by the stub, and cache the
292 ;; result in a variable since we'll need this again.
293 cmp ExceptionNumber
, EXCPT64_DOUBLE_FAULT
295 cmp ExceptionNumber
, EXCPT64_INVALID_TSS
297 cmp ExceptionNumber
, EXCPT64_SEG_NOT_PRESENT
299 cmp ExceptionNumber
, EXCPT64_STACK_FAULT
301 cmp ExceptionNumber
, EXCPT64_GP_FAULT
303 cmp ExceptionNumber
, EXCPT64_PAGE_FAULT
305 cmp ExceptionNumber
, EXCPT64_ALIGNMENT_CHECK
313 ;; If there's some extra data, save it also, and modify the saved AppRsp to effectively
314 ;; pop this value off the application's stack.
323 ;; The "push" above pushed the debug stack rsp. Since what we're actually doing
324 ;; is building the context record on the debug stack, we need to save the pushed
325 ;; debug RSP, and replace it with the application's last stack entry...
330 ; application stack has ss, rsp, rflags, cs, & rip, so
331 ; last actual application stack entry is
332 ; 40 bytes into the application stack.
335 ;; continue building context record
336 ;; UINT64 Gs, Fs, Es, Ds, Cs, Ss; insure high 16 bits of each is zero
340 ; CS from application is one entry back in application stack
342 movzx rax
, word ptr [rax
+ 8]
355 ; Rip from application is on top of application stack
359 ;; UINT64 Gdtr[2], Idtr[2];
375 ;; Rflags from application is two entries back in application stack
377 push qword ptr [rax
+ 16]
379 ;; UINT64 Cr0, Cr1, Cr2, Cr3, Cr4, Cr8;
380 ;; insure FXSAVE/FXRSTOR is enabled in CR4...
381 ;; ... while we're at it, make sure DE is also enabled...
396 ;; UINT64 Dr0, Dr1, Dr2, Dr3, Dr6, Dr7;
399 ;; clear Dr7 while executing debugger itself
405 ;; insure all status bits in dr6 are clear...
418 ;; FX_SAVE_STATE_X64 FxSaveState;
421 ; IMPORTANT!! The debug stack has been carefully constructed to
422 ; insure that rsp and rdi are 16 byte aligned when we get here.
423 ; They MUST be. If they are not, a GP fault will occur.
426 ;; UINT64 ExceptionData;
430 ; call to C code which will in turn call registered handler
431 ; pass in the vector number
433 mov rcx
, ExceptionNumber
435 call InterruptDistrubutionHub
439 ;; UINT64 ExceptionData;
442 ;; FX_SAVE_STATE_X64 FxSaveState;
447 ;; UINT64 Dr0, Dr1, Dr2, Dr3, Dr6, Dr7;
456 ;; skip restore of dr6. We cleared dr6 during the context save.
461 ;; UINT64 Cr0, Cr1, Cr2, Cr3, Cr4, Cr8;
476 pop qword ptr [rax
+ 16]
479 ;; UINT64 Gdtr[2], Idtr[2];
480 ;; Best not let anyone mess with these particular registers...
486 ;; UINT64 Gs, Fs, Es, Ds, Cs, Ss;
487 ;; NOTE - modified segment registers could hang the debugger... We
488 ;; could attempt to insulate ourselves against this possibility,
489 ;; but that poses risks as well.
501 pop qword ptr [rax
+ 8]
505 ;; The next stuff to restore is the general purpose registers that were pushed
506 ;; using the "push" instruction.
508 ;; The value of RSP as stored in the context record is the application RSP
509 ;; including the 5 entries on the application stack caused by the exception
510 ;; itself. It may have been modified by the debug agent, so we need to
511 ;; determine if we need to relocate the application stack.
513 mov rbx
, [rsp
+ 24] ; move the potentially modified AppRsp into rbx
523 mov rcx
, [rax
+ 8] ; CS
526 mov rcx
, [rax
+ 16] ; RFLAGS
529 mov rcx
, [rax
+ 24] ; RSP
532 mov rcx
, [rax
+ 32] ; SS
535 mov rax
, rbx
; modify the saved AppRsp to the new AppRsp
538 mov rax
, DebugRsp
; restore the DebugRsp on the debug stack
539 ; so our "pop" will not cause a stack switch
542 cmp ExceptionNumber
, 068h
547 ;; Restore rflags so when we chain, the flags will be exactly as if we were never here.
548 ;; We gin up the stack to do an iretq so we can get ALL the flags.
559 and rbx
, NOT 300h ; special handling for IF and TF
563 mov rax
, offset PhonyIretq
568 ;; UINT64 Rdi, Rsi, Rbp, Rsp, Rbx, Rdx, Rcx, Rax;
569 ;; UINT64 R8, R9, R10, R11, R12, R13, R14, R15;
587 ;; Switch back to application stack
590 ;; Jump to original handler
594 ;; UINT64 Rdi, Rsi, Rbp, Rsp, Rbx, Rdx, Rcx, Rax;
595 ;; UINT64 R8, R9, R10, R11, R12, R13, R14, R15;
613 ;; Switch back to application stack
616 ;; We're outa here...