/*++ 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 // // ---------------------------------------------------------------- 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)