123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218 |
- /*++
- Copyright (c) 2012 Minoca Corp.
- This file is licensed under the terms of the GNU General Public License
- version 3. Alternative licensing terms are available. Contact
- info@minocacorp.com for details. See the LICENSE file at the root of this
- project for complete licensing information.
- Module Name:
- trap.S
- Abstract:
- This module implements interrupt and exception trap management, such as
- saving and restoring registers.
- Author:
- Evan Green 3-Jul-2012
- Environment:
- Kernel mode
- --*/
- //
- // ------------------------------------------------------------------- Includes
- //
- #include <minoca/kernel/x86.inc>
- //
- // ---------------------------------------------------------------- Definitions
- //
- //
- // -------------------------------------------------------------------- Globals
- //
- //
- // ----------------------------------------------------------------------- Code
- //
- //
- // .text specifies that this code belongs in the executable section.
- //
- // .code32 specifies that this is 32-bit protected mode code.
- //
- .text
- .code32
- .globl HlVectorStart
- .globl HlVectorEnd
- .hidden HlVectorStart
- .hidden HlVectorEnd
- //
- // VOID
- // ArBreakExceptionHandlerAsm (
- // ULONG ReturnEip,
- // ULONG ReturnCodeSelector,
- // ULONG ReturnEflags
- // )
- //
- /*++
- Routine Description:
- This routine is called directly when an debug exception occurs. It sets up
- the parameters and calls a C routine to handle the break. It then restores
- machine state to return from the exception. The arguments to this function
- are pushed by the hardware.
- Arguments:
- ReturnEip - Supplies the address after the instruction that caused the trap.
- ReturnCodeSelector - Supplies the code selector the code that trapped was
- running under.
- ReturnEflags - Supplies the EFLAGS register immediately before the trap.
- Return Value:
- None.
- --*/
- FUNCTION(ArBreakExceptionHandlerAsm)
- pushl $0 # Push a dummy error code.
- call ArGenerateTrapFrame # Create a local trap frame.
- CFI_TRAP_FRAME_PUSHED # Set unwind info for the debugger.
- pushl %ebx # Push a pointer to it as a parameter.
- call KeDispatchBreakPointTrap # Call the main exception handler.
- addl $0x4, %esp # Pop the parameter.
- call ArRestoreTrapFrame # Restore the trap frame
- CFI_TRAP_FRAME_POPPED # Let the debugger know.
- addl $4, %esp # Pop the error code.
- iret # Return from the exception.
- END_FUNCTION(ArBreakExceptionHandlerAsm)
- //
- // VOID
- // KdNmiHandlerAsm (
- // VOID
- // )
- //
- /*++
- Routine Description:
- This routine is called directly when an NMI occurs. Since it is a hardware
- task switch, no registers need to be saved.
- Arguments:
- ReturnEip - Supplies the address after the instruction that caused the trap.
- ReturnCodeSelector - Supplies the code selector the code that trapped was
- running under.
- ReturnEflags - Supplies the EFLAGS register immediately before the trap.
- Return Value:
- None.
- --*/
- FUNCTION(KdNmiHandlerAsm)
- LoadKernelDataSegments # Load valid data segments.
- call KeDispatchNmiTrap # Call to the C routine to handle this mess.
- iret # Return from the exception.
- jmp KdNmiHandlerAsm # The next NMI starts here, jump back up.
- END_FUNCTION(KdNmiHandlerAsm)
- //
- // VOID
- // ArSingleStepExceptionHandlerAsm (
- // ULONG ReturnEip,
- // ULONG ReturnCodeSelector,
- // ULONG ReturnEflags
- // )
- //
- /*++
- Routine Description:
- This routine is called directly when an debug exception occurs. It sets up
- the parameters and calls the executive to dispatch the trap.
- Arguments:
- ReturnEip - Supplies the address after the instruction that caused the trap.
- ReturnCodeSelector - Supplies the code selector the code that trapped was
- running under.
- ReturnEflags - Supplies the EFLAGS register immediately before the trap.
- Return Value:
- None.
- --*/
- FUNCTION(ArSingleStepExceptionHandlerAsm)
- pushl $0 # Push a dummy error code.
- call ArGenerateTrapFrame # Create a local trap frame.
- CFI_TRAP_FRAME_PUSHED # Set unwind info for the debugger.
- pushl %ebx # Push a pointer to it as a parameter.
- call KeDispatchSingleStepTrap # Call the main exception handler.
- addl $0x4, %esp # Pop the parameter.
- call ArRestoreTrapFrame # Restore the trap frame
- CFI_TRAP_FRAME_POPPED # Let the debugger know.
- addl $4, %esp # Pop the error code.
- iret # Return from the exception.
- END_FUNCTION(ArSingleStepExceptionHandlerAsm)
- //
- // VOID
- // KdDebugServiceHandlerAsm (
- // ULONG ReturnEip,
- // ULONG ReturnCodeSelector,
- // ULONG ReturnEflags
- // )
- //
- /*++
- Routine Description:
- This routine is entered via an IDT entry to request debug service. It sets
- up the parameters and calls KdDebugExceptionHandler, and then restores
- machine state to return from the exception. The arguments to this function
- are pushed by the hardware. Upon Entry:
- eax - Supplies the debug service request.
- ecx - Supplies the parameter to the request.
- Arguments:
- ReturnEip - Supplies the address after the instruction that caused the trap.
- ReturnCodeSelector - Supplies the code selector the code that trapped was
- running under.
- ReturnEflags - Supplies the EFLAGS register immediately before the trap.
- Return Value:
- None.
- --*/
- FUNCTION(KdDebugServiceHandlerAsm)
- pushl $0 # Push a dummy error code.
- call ArGenerateTrapFrame # Create a local trap frame.
- CFI_TRAP_FRAME_PUSHED # Set unwind info for the debugger.
- pushl %ebx # Push a pointer to the trap frame.
- call KeDispatchDebugServiceTrap # Call the main exception handler.
- addl $0x4, %esp # Pop the parameter.
- call ArRestoreTrapFrame # Restore the trap frame
- CFI_TRAP_FRAME_POPPED # Let the debugger know.
- addl $4, %esp # Pop the error code.
- iret # Return from the exception.
- END_FUNCTION(KdDebugServiceHandlerAsm)
- //
- // VOID
- // ArDivideByZeroExceptionHandlerAsm (
- // ULONG ReturnEip,
- // ULONG ReturnCodeSelector,
- // ULONG ReturnEflags
- // )
- //
- /*++
- Routine Description:
- This routine is called directly when a divide by zero exception occurs.
- Arguments:
- ReturnEip - Supplies the address after the instruction that caused the trap.
- ReturnCodeSelector - Supplies the code selector the code that trapped was
- running under.
- ReturnEflags - Supplies the EFLAGS register immediately before the trap.
- Return Value:
- None.
- --*/
- FUNCTION(ArDivideByZeroExceptionHandlerAsm)
- pushl $0 # Push a dummy error code.
- call ArGenerateTrapFrame # Create a local trap frame.
- CFI_TRAP_FRAME_PUSHED # Set unwind info for the debugger.
- pushl %ebx # Push a pointer to it as a parameter.
- call KeDispatchDivideByZeroTrap # Call the main exception handler.
- addl $0x4, %esp # Pop the parameters.
- call ArRestoreTrapFrame # Restore the trap frame
- CFI_TRAP_FRAME_POPPED # Let the debugger know.
- addl $4, %esp # Pop the error code.
- iret # Return from the exception.
- END_FUNCTION(ArDivideByZeroExceptionHandlerAsm)
- //
- // VOID
- // ArFpuAccessExceptionHandlerAsm (
- // ULONG ReturnEip,
- // ULONG ReturnCodeSelector,
- // ULONG ReturnEflags
- // )
- //
- /*++
- Routine Description:
- This routine is called directly when floating point access occurs and the
- TS bit in CR0 is
- Arguments:
- ReturnEip - Supplies the address after the instruction that caused the trap.
- ReturnCodeSelector - Supplies the code selector the code that trapped was
- running under.
- ReturnEflags - Supplies the EFLAGS register immediately before the trap.
- Return Value:
- None.
- --*/
- FUNCTION(ArFpuAccessExceptionHandlerAsm)
- pushl $0 # Push a dummy error code.
- call ArGenerateTrapFrame # Create a local trap frame.
- CFI_TRAP_FRAME_PUSHED # Set unwind info for the debugger.
- pushl %ebx # Push a pointer to it as a parameter.
- call KeDispatchFpuAccessTrap # Call the main exception handler.
- addl $0x4, %esp # Pop the parameters.
- call ArRestoreTrapFrame # Restore the trap frame
- CFI_TRAP_FRAME_POPPED # Let the debugger know.
- addl $4, %esp # Pop the error code.
- iret # Return from the exception.
- END_FUNCTION(ArFpuAccessExceptionHandlerAsm)
- //
- // VOID
- // ArDoubleFaultHandlerAsm (
- // VOID
- // )
- //
- /*++
- Routine Description:
- This routine is entered via an IDT entry when a double fault exception
- occurs. Double faults are non-recoverable. This machine loops attempting
- to enter the debugger indefinitely.
- Arguments:
- None.
- Return Value:
- None, this routine does not return.
- --*/
- FUNCTION(ArDoubleFaultHandlerAsm)
- LoadKernelDataSegments # Load valid data segments.
- call ArpHandleDoubleFault # Call to the C routine to handle this mess.
- nop
- END_FUNCTION(ArDoubleFaultHandlerAsm)
- //
- // VOID
- // ArProtectionFaultHandlerAsm (
- // ULONG ReturnEip,
- // ULONG ReturnCodeSelector,
- // ULONG ReturnEflags
- // )
- //
- /*++
- Routine Description:
- This routine is called directly when a general protection fault occurs.
- It's job is to prepare the trap frame, call the appropriate handler, and
- then restore the trap frame.
- Arguments:
- ReturnEip - Supplies the address after the instruction that caused the trap.
- ReturnCodeSelector - Supplies the code selector the code that trapped was
- running under.
- ReturnEflags - Supplies the EFLAGS register immediately before the trap.
- Return Value:
- None.
- --*/
- FUNCTION(ArProtectionFaultHandlerAsm)
- call ArGenerateTrapFrame # Create a local trap frame.
- CFI_TRAP_FRAME_PUSHED # Set unwind info for the debugger.
- pushl %ebx # Push a pointer to it as a parameter.
- call KeDispatchProtectionFault # Call the main handler.
- addl $0x4, %esp # Pop the parameter.
- call ArRestoreTrapFrame # Restore the trap frame
- CFI_TRAP_FRAME_POPPED # Let the debugger know.
- addl $4, %esp
- iret # Return from the exception.
- END_FUNCTION(ArProtectionFaultHandlerAsm)
- //
- // VOID
- // ArMathFaultHandlerAsm (
- // ULONG ReturnEip,
- // ULONG ReturnCodeSelector,
- // ULONG ReturnEflags
- // )
- //
- /*++
- Routine Description:
- This routine is called directly when a x87 FPU fault occurs.
- Arguments:
- ReturnEip - Supplies the address after the instruction that caused the trap.
- ReturnCodeSelector - Supplies the code selector the code that trapped was
- running under.
- ReturnEflags - Supplies the EFLAGS register immediately before the trap.
- Return Value:
- None.
- --*/
- FUNCTION(ArMathFaultHandlerAsm)
- pushl $0 # Push a dummy error code.
- call ArGenerateTrapFrame # Create a local trap frame.
- CFI_TRAP_FRAME_PUSHED # Set unwind info for the debugger.
- pushl %ebx # Push a pointer to it as a parameter.
- call KeDispatchMathFault # Call the main handler.
- addl $0x4, %esp # Pop the parameter.
- call ArRestoreTrapFrame # Restore the trap frame
- CFI_TRAP_FRAME_POPPED # Let the debugger know.
- addl $4, %esp # Pop dummy error code.
- iret # Return from the exception.
- END_FUNCTION(ArMathFaultHandlerAsm)
- //
- // VOID
- // ArTrapSystemCallHandlerAsm (
- // ULONG ReturnEip,
- // ULONG ReturnCodeSelector,
- // ULONG ReturnEflags
- // )
- //
- /*++
- Routine Description:
- This routine is entered when the sysenter routine is entered with the TF
- flag set. It performs a normal save and sets the TF.
- Arguments:
- ReturnEip - Supplies the address after the instruction that caused the trap.
- ReturnCodeSelector - Supplies the code selector the code that trapped was
- running under.
- ReturnEflags - Supplies the EFLAGS register immediately before the trap.
- Return Value:
- None.
- --*/
- FUNCTION(ArTrapSystemCallHandlerAsm)
- //
- // ESP is currently pointing at the processor double fault stack, which
- // is also the main TSS. Switch to the thread stack.
- //
- movl TSS_ESP0(%esp), %esp # Load the stack.
- sti # Re-enable interrupts.
- //
- // Fake a user mode exception by pushing SS, Esp, Eflags, CS and Eip.
- //
- pushl $USER_DS # Push user mode SS.
- pushl %eax # Push user ESP supplied by sysenter.
- pushfl # Push the Eflags.
- pushl $USER32_CS # Push user CS.
- pushl %ebx # Push return address supplied by sysenter.
- pushl $0 # Push a dummy error code.
- call ArGenerateTrapFrame # Create a local trap frame.
- CFI_TRAP_FRAME_PUSHED # Set unwind info for the debugger.
- orl $IA32_EFLAG_TF, TRAP_EFLAGS(%ebx) # Set the trap flag.
- jmp ArSystemCallHandlerAfterTrapSave # Go to the regular path.
- END_FUNCTION(ArTrapSystemCallHandlerAsm)
- //
- // INTN
- // ArSystemCallHandlerAsm (
- // ULONG ReturnEip,
- // ULONG ReturnCodeSelector,
- // ULONG ReturnEflags
- // )
- //
- /*++
- Routine Description:
- This routine is entered via an IDT entry to service a user mode request.
- Ecx contains the system call number, and Edx contains the argument.
- Arguments:
- ReturnEip - Supplies the address after the instruction that caused the trap.
- ReturnCodeSelector - Supplies the code selector the code that trapped was
- running under.
- ReturnEflags - Supplies the EFLAGS register immediately before the trap.
- Return Value:
- STATUS_SUCCESS or positive integer on success.
- Error status code on failure.
- --*/
- FUNCTION(ArSystemCallHandlerAsm)
- pushl $0 # Push a dummy error code.
- //
- // Save the complete trap frame. This routine is aware that
- // ArGenerateTrapFrame doesn't clobber ecx and edx, which hold the system
- // call number and parameter. If ArGenerateTrapFrame did clobber those
- // registers, they'd need to be reloaded after this.
- //
- call ArGenerateTrapFrame # Create a local trap frame.
- CFI_TRAP_FRAME_PUSHED # Set unwind info for the debugger.
- ArSystemCallHandlerAfterTrapSave:
- //
- // Push the parameters for the system call handler. The compiler is free to
- // modify the parameters on the stack in the callee, so this second push is
- // necessary.
- //
- subl $4, %esp # Make room for the boolean.
- pushl %esp # Push the boolean pointer parameter.
- pushl %ebx # Push a pointer to the trap frame.
- pushl %edx # Push system call parameter.
- pushl %ecx # Push system call number.
- CFI_ADJUST_CFA_OFFSET(20) # Tell the debugger.
- call KeSystemCallHandler # Call the main exception handler.
- addl $0x10, %esp # Pop the parameters.
- CFI_ADJUST_CFA_OFFSET(-16) # Tell the debugger.
- movl %eax, TRAP_EAX(%ebx) # Save the return value in the trap frame.
- //
- // See if there is a signal pending on the current thread, as returned by
- // the system call handler.
- //
- popl %ecx # Get the signal pending boolean
- CFI_ADJUST_CFA_OFFSET(-4) # Tell the debugger.
- testl %ecx, %ecx # See if it's zero.
- jz ArSystemCallHandlerExit # Skip the expensive signal stuff.
- //
- // Push the trap frame as the first parameter. The system call number and
- // system call parameter were saved on the stack earlier for this call.
- //
- pushl %ebx # Push trap frame.
- CFI_ADJUST_CFA_OFFSET(4) # Notify about the push.
- call PsApplyPendingSignalsOrRestart # Apply signals or restart the call.
- addl $0x4, %esp # Pop the trap frame.
- CFI_ADJUST_CFA_OFFSET(-4) # Notify about the pop.
- ArSystemCallHandlerExit:
- call ArRestoreTrapFrame # Restore the trap frame
- CFI_TRAP_FRAME_POPPED # Let the debugger know.
- addl $0x4, %esp # Pop the error code.
- iret # Return from the exception.
- END_FUNCTION(ArSystemCallHandlerAsm)
- //
- // INTN
- // ArSysenterHandlerAsm (
- // VOID
- // )
- //
- /*++
- Routine Description:
- This routine is executed when user mode invokes the SYSENTER instruction.
- Upon entry, CS, EIP, and ESP are set to predefined values set in MSRs.
- Arguments:
- None.
- Return Value:
- STATUS_SUCCESS or positive integer on success.
- Error status code on failure.
- --*/
- FUNCTION(ArSysenterHandlerAsm)
- //
- // ESP is currently pointing at the processor double fault stack, which
- // is also the main TSS. Switch to the thread stack. Make room on the stack
- // as if the hardware has pushed SS, ESP, EFLAGS, CS, EIP, and an error
- // code, since the return path might go out with a full iret.
- //
- movl TSS_ESP0(%esp), %esp # Load the stack.
- subl $24, %esp # Fake the hardware pushes.
- //
- // Make a fake trap frame but fill in the bare minimum: Eip and Esp.
- // These are needed so they can be saved if a signal is dispatched.
- //
- pushl %eax # Save user ESP in trap frame.
- LoadKernelDataSegments # Load kernel data segments (using EAX).
- sti # Re-enable interrupts.
- subl $(TRAP_FRAME_SIZE - 4), %esp # Allocate the rest of the trap frame.
- movl %ebx, TRAP_EIP(%esp) # EBX contains the return address.
- movl %esp, %ebx # Save the trap frame in EBX.
- movl %ecx, TRAP_ECX(%esp) # Save system call number for restarts.
- movl %edx, TRAP_EDX(%esp) # Save system call parameter for restarts.
- //
- // There's a trap frame at ebx, which is nice since it won't require
- // maintenance over all these upcoming push/pops of the stack.
- //
- CFI_DEF_CFA(%ebx, 0)
- CFI_OFFSET(%eip, TRAP_EIP)
- CFI_OFFSET(%esp, TRAP_ESP)
- CFI_OFFSET(%ecx, TRAP_ECX)
- CFI_OFFSET(%edx, TRAP_EDX)
- //
- // Move user DS (rather than user CS) into the trap frame to indicate
- // 1) this is a user mode trap frame and 2) it's incomplete.
- //
- movl $USER_DS, TRAP_CS(%ebx) # Indicate a user mode trap frame.
- //
- // Push the parameters for the system call handler and execute it.
- //
- subl $4, %esp # Make room for the boolean.
- pushl %esp # Push the boolean pointer parameter.
- pushl %ebx # Push a pointer to the sort-of trap frame.
- pushl %edx # Push EDX, the system call parameter.
- pushl %ecx # Push ECX, the system call number.
- call KeSystemCallHandler # Call out to the main service handler.
- addl $0x10, %esp # Pop the function parameters.
- //
- // See if there is a signal pending on the current thread, as returned by
- // the system call handler.
- //
- popl %ecx # Get the signal pending boolean
- testl %ecx, %ecx # See if it's zero.
- jz ArSysenterHandlerRestore # Skip the expensive signal stuff.
- //
- // See if the trap frame is already complete, as a result perhaps of the
- // system call itself. If so, skip the full save part, as it might undo
- // useful work (like restoring pre-signal context).
- //
- cmpl $USER32_CS, TRAP_CS(%ebx) # See if the trap frame is complete.
- je ArSysenterSignalDispatch # Skip the save if complete already.
- //
- // Upgrade the trap frame to a complete one. Update user CS to indicate the
- // trap frame is complete. This will also mean the expensive iret exit path
- // is used.
- //
- movl $USER32_CS, TRAP_CS(%ebx) # Save CS.
- movl $USER_DS, TRAP_DS(%ebx) # Save DS.
- movl $USER_DS, TRAP_ES(%ebx) # Save ES.
- movl $GDT_THREAD, TRAP_FS(%ebx) # Save FS.
- movl $GDT_THREAD, TRAP_GS(%ebx) # Save GS.
- movl $USER_DS, TRAP_SS(%ebx) # Save SS.
- //
- // Save the registers.
- //
- movl %eax, TRAP_EAX(%ebx) # Save the return value in the trap frame.
- movl $0x0, TRAP_EBX(%ebx) # Zero EBX. It holds the kernel trap pointer.
- movl %esi, TRAP_ESI(%ebx) # Save ESI. It is non-volatile.
- movl %edi, TRAP_EDI(%ebx) # Save EDI. It is non-volatile.
- movl %ebp, TRAP_EBP(%ebx) # Save EBP. It is non-volatile.
- movl $0x0, TRAP_ERRORCODE(%ebx) # Scrub the error code.
- movl $(IA32_EFLAG_ALWAYS_1 | IA32_EFLAG_IF), TRAP_EFLAGS(%ebx)
- ArSysenterSignalDispatch:
- //
- // Push the trap frame as the first parameter, either apply signals now
- // that a full trap frame is available, or potentially restart the system
- // call if no signals were applied (maybe a process-wide signal got
- // serviced by another thread).
- //
- pushl %ebx # Pass the trap frame.
- call PsApplyPendingSignalsOrRestart # Dispatch signals or restart.
- addl $0x4, %esp # Pop the parameter.
- ArSysenterHandlerRestore:
- //
- // See if the trap frame is complete. If it is, then go do the slower
- // complete restore.
- //
- cmpl $USER32_CS, TRAP_CS(%ebx) # See if the trap frame is complete.
- je ArSystemCallHandlerExit # Do a full restore if so.
- //
- // Reset the segment registers to user mode and return.
- //
- mov $USER_DS, %cx # Get the user mode DS.
- mov %cx, %ds # Move to DS.
- mov %cx, %es # Move to ES.
- mov $GDT_THREAD, %cx # Get the user-mode GS.
- mov %cx, %fs # Move to FS.
- mov %cx, %gs # Move to GS.
- CFI_DEF_CFA(%esp, 0) # The trap frame is at ESP, EBX is gone.
- //
- // Restore some portions of the pseudo trap frame. Do not zero EAX as it
- // holds the return value from the system call.
- //
- movl TRAP_ESP(%esp), %ecx # Sysexit moves ECX to ESP.
- movl TRAP_EIP(%esp), %edx # Sysexit moves EDX to EIP.
- sysexit # Return to user mode, slickly.
- END_FUNCTION(ArSysenterHandlerAsm)
- //
- // VOID
- // ArpPageFaultHandlerAsm (
- // ULONG ReturnEip,
- // ULONG ReturnCodeSelector,
- // ULONG ReturnEflags
- // )
- //
- /*++
- Routine Description:
- This routine is called directly when a page fault occurs.
- Arguments:
- ReturnEip - Supplies the address after the instruction that caused the
- fault.
- ReturnCodeSelector - Supplies the code selector the code that faulted was
- running under.
- ReturnEflags - Supplies the EFLAGS register immediately before the fault.
- Return Value:
- None.
- --*/
- FUNCTION(ArpPageFaultHandlerAsm)
- call ArGenerateTrapFrame # Create a local trap frame.
- CFI_TRAP_FRAME_PUSHED # Set unwind info for the debugger.
- movl %cr2, %ecx # Get the faulting address.
- xor %edx, %edx # Zero edx.
- movl %edx, %cr2 # Clear CR2.
- sti # Re-enable interrupts.
- pushl %ebx # Push a pointer to to the trap frame.
- pushl %ecx # Push CR2.
- CFI_ADJUST_CFA_OFFSET(8) # Tell the debugger.
- call KeDispatchPageFault # Call the main exception handler.
- addl $8, %esp # Pop the parameters.
- CFI_ADJUST_CFA_OFFSET(-8) # Pop them in the debugger.
- call ArRestoreTrapFrame # Restore the trap frame
- CFI_TRAP_FRAME_POPPED # Let the debugger know.
- addl $4, %esp # Pop the error code.
- iret # Return from the exception.
- END_FUNCTION(ArpPageFaultHandlerAsm)
- //
- // VOID
- // HlSpuriousInterruptHandlerAsm (
- // ULONG ReturnEip,
- // ULONG ReturnCodeSelector,
- // ULONG ReturnEflags
- // )
- //
- /*++
- Routine Description:
- This routine handles spurious interrupts. It does not require an EOI or
- other interrupt acknowledgement.
- Arguments:
- ReturnEip - Supplies the address after the instruction that caused the trap.
- ReturnCodeSelector - Supplies the code selector the code that trapped was
- running under.
- ReturnEflags - Supplies the EFLAGS register immediately before the trap.
- Return Value:
- None.
- --*/
- FUNCTION(HlSpuriousInterruptHandlerAsm)
- pushl $0 # Push a dummy error code.
- call ArGenerateTrapFrame # Create a local trap frame.
- CFI_TRAP_FRAME_PUSHED # Set unwind info for the debugger.
- addl $1, HlSpuriousInterruptCount # Count interrupts
- call ArRestoreTrapFrame # Restore the trap frame
- CFI_TRAP_FRAME_POPPED # Let the debugger know.
- addl $4, %esp # Pop the error code.
- iret # Return from the exception.
- END_FUNCTION(HlSpuriousInterruptHandlerAsm)
- //
- // VOID
- // ArRestoreTrapFrame (
- // PTRAP_FRAME TrapFrame
- // )
- //
- /*++
- Routine Description:
- This routine restores information contained in a trap frame to the
- processor and prepares the machine for an iret back to the code that
- generated this trap frame. It's not really a function because it assumes
- a specific stack layout and modifies data that technically belongs to the
- caller. It should only be called immediately before returning from an
- exception or interrupt.
- Arguments:
- TrapFrame - Supplies the trap frame to restore, in ebx.
- Return Value:
- Upon return, the trap frame will have been popped off the stack, and the
- machine will be in the same state as right after the exception happened.
- --*/
- FUNCTION(ArRestoreTrapFrame)
- mov %ebx, %ecx # Move trap frame to ecx.
- addl $TRAP_FRAME_SIZE+16, %ebx # Compute the pre-exception stack.
- movl TRAP_CS(%ecx), %edx # Get the destination CS.
- //
- // The exception is returning to either kernel or user mode. Either way restore
- // the common data segment registers. Hold off on DS, as this routine will
- // make a couple more DS: accesses. Save it in ESI.
- //
- movl TRAP_DS(%ecx), %esi # Save DS into ESI for now.
- movl TRAP_ES(%ecx), %eax # Restore ES.
- movw %ax, %es #
- movl TRAP_FS(%ecx), %eax # Restore FS.
- movw %ax, %fs #
- movl TRAP_GS(%ecx), %eax # Restore GS.
- movw %ax, %gs #
- //
- // Restore the remaining registers based on the destination mode.
- //
- movl %edx, %eax # Get CS (loaded above).
- andl $SEGMENT_PRIVILEGE_MASK, %eax # AND out the privilege.
- jz RestoreTrapFrameToKernelMode # Jump over if not.
- //
- // The exception is going to jump back into user mode, so put the stack
- // pointer and segments back into the exception-generated part of the stack.
- //
- movl TRAP_ESP(%ecx), %eax # Restore Esp.
- movl %eax, TRAP_RET_ESP(%ecx) #
- movl TRAP_SS(%ecx), %eax # Restore SS.
- movl %eax, TRAP_RET_SS(%ecx) #
- jmp RestoreTrapFrameGeneralRegisters # Jump to the end.
- RestoreTrapFrameToKernelMode:
- //
- // The exception came from kernel mode, so restore the stack segment register.
- //
- movl TRAP_SS(%ecx), %eax # Restore SS. If this doesn't allow access
- movw %ax, %ss # to the current stack, this will be bad.
- //
- // Build the iret return. The parameters going on the new stack are Ebx, Return
- // Address, Error Code, Eip, CS, and Eflags.
- //
- // Note that if the stack pointer doesn't change, the Ebx and Return address
- // values destroy data that was on the stack there (immediately after the
- // Eflags, CS, Eip). This happens to be the last two values in the trap frame
- // structure. Luckily those members are Esp and Eflags, which are restored
- // immediately before their values are destroyed.
- //
- movl TRAP_ESP(%ecx), %ebx # Get the kernel Esp.
- RestoreTrapFrameGeneralRegisters:
- subl $24, %ebx # Make room for the new parameters.
- movl TRAP_EIP(%ecx), %eax # Restore Eip.
- movl %eax, 12(%ebx) #
- movl %edx, 16(%ebx) # Restore CS.
- movl TRAP_EFLAGS(%ecx), %eax # Restore Eflags.
- movl %eax, 20(%ebx) #
- movl TRAP_EBX(%ecx), %eax # Save Ebx.
- movl %eax, (%ebx) #
- movl (%esp), %eax # Save this function's return address.
- movl %eax, 4(%ebx) #
- //
- // Now that all DS: accesses are finished, restore DS.
- //
- movw %si, %ds
- //
- // Move the trap frame pointer to the stack, popping everything up until then,
- // including the extended state.
- //
- movl %ecx, %esp # Pop up to the trap frame.
- //
- // Restore the general registers.
- //
- movl TRAP_EAX(%esp), %eax #
- movl TRAP_ECX(%esp), %ecx #
- movl TRAP_EDX(%esp), %edx #
- movl TRAP_ESI(%esp), %esi #
- movl TRAP_EDI(%esp), %edi #
- movl TRAP_EBP(%esp), %ebp #
- //
- // Transition to the new kernel mode stack pointer, pop Ebx, and return.
- //
- movl %ebx, %esp # Move stacks!
- popl %ebx # Restore Ebx.
- ret
- END_FUNCTION(ArRestoreTrapFrame)
- //
- // --------------------------------------------------------- Internal Functions
- //
- //
- // This macro stamps out the assembly dispatch code necessary for interrupts
- // received at each vector. It will create code for all vectors between
- // MinimumVector and MaximumVector.
- //
- .macro InterruptVector _Vector
- //
- // 0x6A xx is the instruction for push imm8, except the immediate is sign
- // extended. The assembler will use the longer form for numbers >= 0x80
- // since those should not be sign extended. Use the shorter form directly
- // here to save space, and deal with it using a cast in the C code.
- //
- .byte 0x6A
- .byte (\_Vector)
- jmp KeInterruptEntry
- .endm
- .macro InterruptVectors16 _Vector
- InterruptVector (\_Vector)
- InterruptVector (\_Vector + 1)
- InterruptVector (\_Vector + 2)
- InterruptVector (\_Vector + 3)
- InterruptVector (\_Vector + 4)
- InterruptVector (\_Vector + 5)
- InterruptVector (\_Vector + 6)
- InterruptVector (\_Vector + 7)
- InterruptVector (\_Vector + 8)
- InterruptVector (\_Vector + 9)
- InterruptVector (\_Vector + 10)
- InterruptVector (\_Vector + 11)
- InterruptVector (\_Vector + 12)
- InterruptVector (\_Vector + 13)
- InterruptVector (\_Vector + 14)
- InterruptVector (\_Vector + 15)
- .endm
- //
- // Now actually instantiate the macro to create the vector code.
- //
- HlVectorStart:
- InterruptVectors16 0x30
- InterruptVectors16 0x40
- InterruptVectors16 0x50
- InterruptVectors16 0x60
- InterruptVectors16 0x70
- InterruptVectors16 0x80
- InterruptVectors16 0x90
- InterruptVectors16 0xA0
- InterruptVectors16 0xB0
- InterruptVectors16 0xC0
- InterruptVectors16 0xD0
- InterruptVectors16 0xE0
- InterruptVectors16 0xF0
- HlVectorEnd:
- //
- // PTRAP_FRAME
- // ArGenerateTrapFrame (
- // ULONG ReturnEip,
- // ULONG ReturnCs,
- // ULONG ReturnEflags,
- // ...
- // )
- //
- /*++
- Routine Description:
- This routine generates a trap frame based on the data pushed onto the
- stack by the processor after an exception. It is not really a function
- in that it assumes a certain stack layout and will modify data that
- belongs to the caller. This function should only be called immediately
- after an interrupt/exception.
- Arguments:
- ReturnEip - Supplies the instruction that generated the exception.
- ReturnCs - Supplies the code selector of the code that generated the
- exception.
- ReturnEflags - Supplies the flags of the code that generated the
- exception.
- Return Value:
- Returns a pointer to the trap frame in ebx.
- --*/
- FUNCTION(ArGenerateTrapFrame)
- //
- // Allocate room on the stack for the trap frame plus the return address,
- // minus the original return address.
- //
- subl $TRAP_FRAME_SIZE, %esp #
- pushl %eax # Save eax for a moment while the return
- movl TRAP_FRAME_SIZE+4(%esp), %eax # address is moved.
- movl %eax, 4(%esp) #
- popl %eax # Restore eax
- movl %eax, TRAP_EAX+4(%esp) # Save the general registers.
- movl %ebx, TRAP_EBX+4(%esp) #
- movl %ecx, TRAP_ECX+4(%esp) #
- movl %edx, TRAP_EDX+4(%esp) #
- movl %esi, TRAP_ESI+4(%esp) #
- movl %edi, TRAP_EDI+4(%esp) #
- movl %ebp, TRAP_EBP+4(%esp) #
- movl TRAP_RET_ERRORCODE+4(%esp), %eax # Save the error code.
- movl %eax, TRAP_ERRORCODE+4(%esp) #
- movl TRAP_RET_EIP+4(%esp), %eax # Save the return address.
- movl %eax, TRAP_EIP+4(%esp) #
- movl TRAP_RET_CS+4(%esp), %ebx # Save the return CS.
- movl %ebx, TRAP_CS+4(%esp) #
- movl TRAP_RET_EFLAGS+4(%esp), %eax # Save eflags.
- movl %eax, TRAP_EFLAGS+4(%esp)
- //
- // Figure out if a ring change occurred.
- //
- andl $SEGMENT_PRIVILEGE_MASK, %ebx # exception had a ring change.
- jz GenerateTrapFrameFromKernelMode # Jump over if not.
- //
- // The exception caused a privilege level change, so the stack contains the
- // following ULONGs: Eip, CS, Eflags, Esp, and SS. Get the other segment
- // selectors from their current values.
- //
- movl TRAP_RET_ESP+4(%esp), %eax # Save Esp.
- movl %eax, TRAP_ESP+4(%esp) #
- movl TRAP_RET_SS+4(%esp), %eax # Save SS.
- movl %eax, TRAP_SS+4(%esp) #
- xorl %eax, %eax # Zero out eax.
- movw %ds, %ax # Save DS.
- movl %eax, TRAP_DS+4(%esp) #
- movw %es, %ax # Save ES.
- movl %eax, TRAP_ES+4(%esp) #
- movw %fs, %ax # Save FS.
- movl %eax, TRAP_FS+4(%esp) #
- movw %gs, %ax # Save GS.
- movl %eax, TRAP_GS+4(%esp) #
- jmp GenerateTrapFrameEnd # All done.
- GenerateTrapFrameFromKernelMode:
- //
- // The exception came from kernel mode, so the only things pushed on the stack
- // by the processor are Eip, CS, and Eflags. The data segments also don't need
- // to be saved. Get the data segments from their current values. Since there
- // was no stack change, the Esp is simply this current one except all the
- // stuff pushed by the exception, plus the error code.
- //
- movl %esp, %eax # Save Esp.
- addl $TRAP_FRAME_SIZE+20, %eax # Remove exception stack items.
- movl %eax, TRAP_ESP+4(%esp) #
- xorl %eax, %eax # Zero out eax.
- movw %ds, %ax # Save DS.
- movl %eax, TRAP_DS+4(%esp) #
- movw %es, %ax # Save ES.
- movl %eax, TRAP_ES+4(%esp) #
- movw %fs, %ax # Save FS.
- movl %eax, TRAP_FS+4(%esp) #
- movw %gs, %ax # Save GS.
- movl %eax, TRAP_GS+4(%esp) #
- movw %ss, %ax # Save SS.
- movl %eax, TRAP_SS+4(%esp) #
- GenerateTrapFrameEnd:
- LoadKernelDataSegments # Load valid data segments.
- popl %edi # Pop the return address.
- movl %esp, %ebx # Return the trap pointer.
- jmp *%edi # Return
- END_FUNCTION(ArGenerateTrapFrame)
- //
- // Define the common interrupt entry code. At this point the vector number has
- // been pushed into the error code slot, but nothing else has been done. Note
- // that this code needs to be far enough away from the vectors themselves so
- // that none of the jumps in the vectors turn into shorter instructions
- // (distance >= 0x100).
- //
- FUNCTION(KeInterruptEntry)
- call ArGenerateTrapFrame # Create a local trap frame.
- CFI_TRAP_FRAME_PUSHED # Set unwind info for the debugger.
- pushl %ebx # Push a pointer to it as a parameter.
- CFI_ADJUST_CFA_OFFSET(4)
- call KeDispatchInterrupt # Dispatch the interrupt.
- addl $4, %esp # Pop the parameters.
- CFI_ADJUST_CFA_OFFSET(-4)
- call ArRestoreTrapFrame # Restore state.
- CFI_TRAP_FRAME_POPPED # Let the debugger know.
- addl $4, %esp # Pop the error code.
- CFI_OFFSET(%eip, 0) # The return address is right there.
- iret # Return from the exception.
- END_FUNCTION(KeInterruptEntry)
|