/*++ Copyright (c) 2017 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: archsup.S Abstract: This module implements AMD64 processor architecture features not implementable in C. Author: Evan Green 8-Jun-2017 Environment: Kernel --*/ // // ------------------------------------------------------------------ Includes // #include // // ---------------------------------------------------------------------- Code // ASSEMBLY_FILE_HEADER // // KERNEL_API // BOOL // ArAreInterruptsEnabled ( // VOID // ) // /*++ Routine Description: This routine determines whether or not interrupts are currently enabled on the processor. Arguments: None. Return Value: TRUE if interrupts are enabled in the processor. FALSE if interrupts are globally disabled. --*/ PROTECTED_FUNCTION(ArAreInterruptsEnabled) xorl %eax, %eax # Clear RAX. pushfq # Get Rflags. popq %rdi # Rflags in rdi. andl $IA32_EFLAG_IF, %edi # Isolate the Interrupt flag. setnz %al # Set eax to 1 if non-zero. ret # END_FUNCTION(ArAreInterruptsEnabled) // // KERNEL_API // BOOL // ArDisableInterrupts ( // VOID // ) // /*++ Routine Description: This routine disables all interrupts on the current processor. Arguments: None. Return Value: TRUE if interrupts were previously enabled. FALSE if interrupts were previously disabled. --*/ PROTECTED_FUNCTION(ArDisableInterrupts) xorl %eax, %eax # Zero eax. pushfq # Push flags. cli # Clear the interrupt flag. popq %rdi # Pop flags into eax. andl $IA32_EFLAG_IF, %edi # Isolate the Interrupt flag. setnz %al # Set eax to 1 if non-zero. ret END_FUNCTION(ArDisableInterrupts) // // KERNEL_API // VOID // ArEnableInterrupts ( // VOID // ) // /*++ Routine Description: This routine enables interrupts on the current processor. Arguments: None. Return Value: None. --*/ PROTECTED_FUNCTION(ArEnableInterrupts) sti # Set the interrupt flag. ret # END_FUNCTION(ArEnableInterrupts) // // VOID // ArLoadKernelDataSegments ( // VOID // ) // /*++ Routine Description: This routine switches the data segments DS and ES to the kernel data segment selectors. Arguments: None. Return Value: None. --*/ FUNCTION(ArLoadKernelDataSegments) LOAD_KERNEL_DATA_SEGMENTS # Load the kernel data segments. ret # END_FUNCTION(ArLoadKernelDataSegments) // // ULONG // ArGetProcessorFlags ( // VOID // ) // /*++ Routine Description: This routine gets the current processor's flags register. Arguments: None. Return Value: Returns the current flags. --*/ FUNCTION(ArGetProcessorFlags) pushfq # Push the flags onto the stack. popq %rax # Pop them into the return value. ret # END_FUNCTION(ArGetProcessorFlags) // // VOID // ArLoadTr ( // USHORT TssSegment // ) // /*++ Routine Description: This routine loads a TSS (Task Selector State). Arguments: TssSegment - Supplies the segment selector in the GDT that describes the TSS. Return Value: None. --*/ FUNCTION(ArLoadTr) ltr %di # Load the Task Register. ret # That's it! END_FUNCTION(ArLoadTr) // // VOID // ArStoreTr ( // PULONG TssSegment // ) // /*++ Routine Description: This routine retrieves the current TSS (Task Selector State) register. Arguments: TssSegment - Supplies a pointer where the current TSS segment register will be returned. Return Value: None. --*/ FUNCTION(ArStoreTr) str (%rdi) # Store the TR register. ret # Return END_FUNCTION(ArStoreTr) // // VOID // ArLoadIdtr ( // PVOID IdtBase // ) // /*++ Routine Description: This routine loads the given Interrupt Descriptor Table. Arguments: IdtBase - Supplies a pointer to the base of the IDT. Return Value: None. --*/ FUNCTION(ArLoadIdtr) lidt (%rdi) # Load the IDT register. ret # That's it! END_FUNCTION(ArLoadIdtr) // // VOID // ArStoreIdtr ( // PTABLE_REGISTER IdtRegister // ) // /*++ Routine Description: This routine stores the interrupt descriptor table register into the given value. Arguments: IdtRegister - Supplies a pointer that will receive the value. Return Value: None. --*/ FUNCTION(ArStoreIdtr) sidt (%rdi) # Store the IDT register. ret # Return politely. END_FUNCTION(ArStoreIdtr) // // VOID // ArLoadGdtr ( // PTABLE_REGISTER Gdt // ) // /*++ Routine Description: This routine loads a global descriptor table. Arguments: Gdt - Supplies a pointer to the Gdt pointer, which contains the base and limit for the GDT. Return Value: None. --*/ FUNCTION(ArLoadGdtr) lgdt (%rdi) # Load the GDT. // // In order to load the new GDT, a long jump of some kind is needed. Use a // far return for this purpose, returning from this routine in the process. // popq %rax # Pop the return address into a register. pushq $KERNEL_CS # Push the return segemnt. pushq %rax # Push the return address. retfq # Do a 64-bit far return, loading the GDT. END_FUNCTION(ArLoadGdtr) // // VOID // ArStoreGdtr ( // PTABLE_REGISTER GdtRegister // ) // /*++ Routine Description: This routine stores the GDT register into the given value. Arguments: GdtRegister - Supplies a pointer that will receive the value. Return Value: None. --*/ FUNCTION(ArStoreGdtr) sgdt (%rdi) # Store the GDT register. ret # Return politely. END_FUNCTION(ArStoreGdtr) // // PVOID // ArGetFaultingAddress ( // VOID // ) // /*++ Routine Description: This routine determines which address caused a page fault. Arguments: None. Return Value: Returns the faulting address. --*/ FUNCTION(ArGetFaultingAddress) movq %cr2, %rax # Return CR2. ret # END_FUNCTION(ArGetFaultingAddress) // // VOID // ArSetFaultingAddress ( // PVOID Value // ) // /*++ Routine Description: This routine sets the CR2 register. Arguments: Value - Supplies the value to set. Return Value: None. --*/ FUNCTION(ArSetFaultingAddress) movq %rdi, %cr2 ret END_FUNCTION(ArSetFaultingAddress) // // UINTN // ArGetCurrentPageDirectory ( // VOID // ) // /*++ Routine Description: This routine returns the active page directory. Arguments: None. Return Value: Returns the page directory currently in use by the system. --*/ FUNCTION(ArGetCurrentPageDirectory) movq %cr3, %rax # Return CR3. ret # END_FUNCTION(ArGetCurrentPageDirectory) // // VOID // ArSetCurrentPageDirectory ( // UINTN Value // ) // /*++ Routine Description: This routine sets the CR3 register. Arguments: Value - Supplies the value to set. Return Value: None. --*/ FUNCTION(ArSetCurrentPageDirectory) movq %rdi, %cr3 ret END_FUNCTION(ArSetCurrentPageDirectory) // // VOID // ArInvalidateTlbEntry ( // PVOID Address // ) // /*++ Routine Description: This routine invalidates one TLB entry corresponding to the given virtual address. Arguments: Address - Supplies the virtual address whose associated TLB entry will be invalidated. Return Value: None. --*/ FUNCTION(ArInvalidateTlbEntry) invlpg (%rdi) # Invalidate the TLB entry. ret # END_FUNCTION(ArInvalidateTlbEntry) // // VOID // ArCleanEntireCache ( // VOID // ) // /*++ Routine Description: This routine cleans the entire data cache. Arguments: None. Return Value: None. --*/ FUNCTION(ArCleanEntireCache) wbinvd # Write back invalidate cache. ret END_FUNCTION(ArCleanEntireCache) // // VOID // ArInvalidateEntireTlb ( // VOID // ) // /*++ Routine Description: This routine invalidates the entire TLB. Arguments: None. Return Value: None. --*/ FUNCTION(ArInvalidateEntireTlb) movq %cr3, %rax # Reloading CR3 causes the entire TLB to movq %rax, %cr3 # be flushed. ret END_FUNCTION(ArInvalidateEntireTlb) // // VOID // ArProcessorYield ( // VOID // ) // /*++ Routine Description: This routine executes a short processor yield in hardware. Arguments: None. Return Value: None. --*/ FUNCTION(ArProcessorYield) pause ret END_FUNCTION(ArProcessorYield) // // KERNEL_API // VOID // ArWaitForInterrupt ( // VOID // ) // /*++ Routine Description: This routine halts the processor until the next interrupt comes in. This routine should be called with interrupts disabled, and will return with interrupts enabled. Arguments: None. Return Value: None. --*/ PROTECTED_FUNCTION(ArWaitForInterrupt) sti # Enables interrupts one instruction later. hlt # Simtaneously halt and enable interrupts. ret END_FUNCTION(ArWaitForInterrupt) // // VOID // ArSerializeExecution ( // VOID // ) // /*++ Routine Description: This routine acts a serializing instruction, preventing the processor from speculatively executing beyond this point. Arguments: None. Return Value: None. --*/ FUNCTION(ArSerializeExecution) movq %cr2, %rax # Control register accesses are movq %rax, %cr2 # cheap (ish) and serializing. ret END_FUNCTION(ArSerializeExecution) // // VOID // ArInvalidateInstructionCache ( // VOID // ) // /*++ Routine Description: This routine invalidate the processor's instruction cache, indicating that a page containing code has changed. Arguments: None. Return Value: None. --*/ FUNCTION(ArInvalidateInstructionCache) ret END_FUNCTION(ArInvalidateInstructionCache) // // VOID // ArCpuid ( // PULONG Eax, // PULONG Ebx, // PULONG Ecx, // PULONG Edx // ) // /*++ Routine Description: This routine executes the CPUID instruction to get processor architecture information. Arguments: Eax - Supplies a pointer to the value that EAX should be set to when the CPUID instruction is executed. On output, contains the contents of EAX immediately after the CPUID instruction. Ebx - Supplies a pointer to the value that EBX should be set to when the CPUID instruction is executed. On output, contains the contents of EAX immediately after the CPUID instruction. Ecx - Supplies a pointer to the value that ECX should be set to when the CPUID instruction is executed. On output, contains the contents of EAX immediately after the CPUID instruction. Edx - Supplies a pointer to the value that EDX should be set to when the CPUID instruction is executed. On output, contains the contents of EAX immediately after the CPUID instruction. Return Value: None. --*/ FUNCTION(ArCpuid) pushq %rbx # Save the only non-volatile involved. movq %rdx, %r8 # Save rcx into R8 movq %rcx, %r9 # Save rdx into R9. movl (%rdi), %eax # Dereference to get eax. movl (%rsi), %ebx # Dereference to get ebx. movl (%r8), %ecx # Dereference to get ecx. movl (%r9), %edx # Dereference to get edx. cpuid # Fire off the CPUID instruction. movl %edx, (%r9) # Save the resulting edx. movl %ecx, (%r8) # Save the resulting ecx. movl %ebx, (%rsi) # Save the resulting ebx. movl %eax, (%rdi) # Save the resulting eax. popq %rbx # Restore the non-volatile. ret END_FUNCTION(ArCpuid) // // UINTN // ArGetControlRegister0 ( // VOID // ) // /*++ Routine Description: This routine returns the current value of CR0. Arguments: None. Return Value: Returns CR0. --*/ FUNCTION(ArGetControlRegister0) movq %cr0, %rax ret END_FUNCTION(ArGetControlRegister0) // // VOID // ArSetControlRegister0 ( // UINTN Value // ) // /*++ Routine Description: This routine sets the CR0 register. Arguments: Value - Supplies the value to set. Return Value: None. --*/ FUNCTION(ArSetControlRegister0) movq %rdi, %cr0 ret END_FUNCTION(ArSetControlRegister0) // // UINTN // ArGetControlRegister4 ( // VOID // ) // /*++ Routine Description: This routine returns the current value of CR4. Arguments: None. Return Value: Returns CR4. --*/ FUNCTION(ArGetControlRegister4) movq %cr4, %rax ret END_FUNCTION(ArGetControlRegister4) // // VOID // ArSetControlRegister4 ( // UINTN Value // ) // /*++ Routine Description: This routine sets the CR4 register. Arguments: Value - Supplies the value to set. Return Value: None. --*/ FUNCTION(ArSetControlRegister4) movq %rdi, %cr4 ret END_FUNCTION(ArSetControlRegister4) // // UINTN // ArGetDebugRegister0 ( // VOID // ) // /*++ Routine Description: This routine returns the current value of DR0. Arguments: None. Return Value: Returns DR0. --*/ FUNCTION(ArGetDebugRegister0) movq %dr0, %rax ret END_FUNCTION(ArGetDebugRegister0) // // VOID // ArSetDebugRegister0 ( // UINTN Value // ) // /*++ Routine Description: This routine sets the DR0 register. Arguments: Value - Supplies the value to set. Return Value: None. --*/ FUNCTION(ArSetDebugRegister0) movq %rdi, %dr0 ret END_FUNCTION(ArSetDebugRegister0) // // UINTN // ArGetDebugRegister1 ( // VOID // ) // /*++ Routine Description: This routine returns the current value of DR1. Arguments: None. Return Value: Returns DR1. --*/ FUNCTION(ArGetDebugRegister1) movq %dr1, %rax ret END_FUNCTION(ArGetDebugRegister1) // // VOID // ArSetDebugRegister1 ( // UINTN Value // ) // /*++ Routine Description: This routine sets the DR1 register. Arguments: Value - Supplies the value to set. Return Value: None. --*/ FUNCTION(ArSetDebugRegister1) movq %rdi, %dr1 ret END_FUNCTION(ArSetDebugRegister1) // // UINTN // ArGetDebugRegister2 ( // VOID // ) // /*++ Routine Description: This routine returns the current value of DR2. Arguments: None. Return Value: Returns DR2. --*/ FUNCTION(ArGetDebugRegister2) movq %dr2, %rax ret END_FUNCTION(ArGetDebugRegister2) // // VOID // ArSetDebugRegister2 ( // UINTN Value // ) // /*++ Routine Description: This routine sets the DR2 register. Arguments: Value - Supplies the value to set. Return Value: None. --*/ FUNCTION(ArSetDebugRegister2) movq %rdi, %dr2 ret END_FUNCTION(ArSetDebugRegister2) // // UINTN // ArGetDebugRegister3 ( // VOID // ) // /*++ Routine Description: This routine returns the current value of DR3. Arguments: None. Return Value: Returns DR3. --*/ FUNCTION(ArGetDebugRegister3) movq %dr3, %rax ret END_FUNCTION(ArGetDebugRegister3) // // VOID // ArSetDebugRegister3 ( // UINTN Value // ) // /*++ Routine Description: This routine sets the DR3 register. Arguments: Value - Supplies the value to set. Return Value: None. --*/ FUNCTION(ArSetDebugRegister3) movq %rdi, %dr3 ret END_FUNCTION(ArSetDebugRegister3) // // UINTN // ArGetDebugRegister6 ( // VOID // ) // /*++ Routine Description: This routine returns the current value of DR6. Arguments: None. Return Value: Returns DR6. --*/ FUNCTION(ArGetDebugRegister6) movq %dr6, %rax ret END_FUNCTION(ArGetDebugRegister6) // // VOID // ArSetDebugRegister6 ( // UINTN Value // ) // /*++ Routine Description: This routine sets the DR6 register. Arguments: Value - Supplies the value to set. Return Value: None. --*/ FUNCTION(ArSetDebugRegister6) movq %rdi, %dr6 ret END_FUNCTION(ArSetDebugRegister6) // // UINTN // ArGetDebugRegister7 ( // VOID // ) // /*++ Routine Description: This routine returns the current value of DR7. Arguments: None. Return Value: Returns DR7. --*/ FUNCTION(ArGetDebugRegister7) movq %dr7, %rax ret END_FUNCTION(ArGetDebugRegister7) // // VOID // ArSetDebugRegister7 ( // UINTN Value // ) // /*++ Routine Description: This routine sets the DR7 register. Arguments: Value - Supplies the value to set. Return Value: None. --*/ FUNCTION(ArSetDebugRegister7) movq %rdi, %dr7 ret END_FUNCTION(ArSetDebugRegister7) // // VOID // ArFxSave ( // PFPU_CONTEXT Buffer // ) // /*++ Routine Description: This routine saves the current x87 FPU, MMX, XMM, and MXCSR registers to a 512 byte memory location. Arguments: Buffer - Supplies a pointer to the buffer where the information will be saved. This buffer must be 16-byte aligned. Return Value: None. --*/ FUNCTION(ArFxSave) addq $0xF, %rdi # Round up to nearest alignment requirement. andq $~0xF, %rdi # Align. clts # Clear the TS flag, Enabling FPU access. fxsave (%rdi) # Save the state into there. ret END_FUNCTION(ArFxSave) // // VOID // ArFxRestore ( // PFPU_CONTEXT Buffer // ) // /*++ Routine Description: This routine restores the current x87 FPU, MMX, XMM, and MXCSR registers from a 512 byte memory location. Arguments: Buffer - Supplies a pointer to the buffer where the information will be loaded from. This buffer must be 16-byte aligned. Return Value: None. --*/ FUNCTION(ArFxRestore) addq $0xF, %rdi # Round up to nearest alignment requirement. andq $~0xF, %rax # Align. clts # Clear the TS flag, Enabling FPU access. fxrstor (%rdi) # Load the state from there. ret END_FUNCTION(ArFxRestore) // // VOID // ArEnableFpu ( // VOID // ) // /*++ Routine Description: This routine clears the TS bit of CR0, allowing access to the FPU. Arguments: None. Return Value: None. --*/ FUNCTION(ArEnableFpu) clts # Use the dedicated instruction for this. ret # Return. END_FUNCTION(ArEnableFpu) // // VOID // ArDisableFpu ( // VOID // ) // /*++ Routine Description: This routine sets the TS bit of CR0, disallowing access to the FPU. Arguments: None. Return Value: None. --*/ FUNCTION(ArDisableFpu) movq %cr0, %rax # Get CR0. andq $CR0_TASK_SWITCHED, %rax # See if it's already disabled. jnz ArDisableFpuReturn # Jump out without writing if it's already off. orq $CR0_TASK_SWITCHED, %rax # Turn on that bit. movq %rax, %cr0 # Write CR0. ArDisableFpuReturn: ret # Return. END_FUNCTION(ArDisableFpu) // // VOID // ArInitializeFpu ( // VOID // ) // /*++ Routine Description: This routine resets the FPU state. Arguments: None. Return Value: None. --*/ FUNCTION(ArInitializeFpu) fninit # Reset the FPU state. ret # Return. END_FUNCTION(ArInitializeFpu) // // ULONGLONG // ArReadTimeStampCounter ( // VOID // ) // /*++ Routine Description: This routine reads the time stamp counter from the current processor. It is essential that callers of this function understand that this returns instruction cycles, which does not always translate directly into units of time. For example, some processors halt the timestamp counter during performance and CPU idle state transitions. In other cases, the timestamp counters of all processors are not in sync, so as execution of a thread bounces unpredictably from one core to another, different timelines may be observed. Additionally, one must understand that this intrinsic is not a serializing instruction to the hardware, so the processor may decide to execute any number of instructions after this one before actually snapping the timestamp counter. To all those who choose to continue to use this primitive to measure time, you have been warned. Arguments: None. Return Value: Returns the current instruction cycle count since the processor was started. --*/ FUNCTION(ArReadTimeStampCounter) rdtsc # Store the timestamp counter in EDX:EAX. shlq $32, %rdx # Shift rdx into its high word. orq %rdx, %rax # OR rdx into rax. ret # And return! END_FUNCTION(ArReadTimeStampCounter) // // ULONGLONG // ArReadMsr ( // ULONG Msr // ) // /*++ Routine Description: This routine reads the requested Model Specific Register. Arguments: Msr - Supplies the MSR to read. Return Value: Returns the 64-bit MSR value. --*/ FUNCTION(ArReadMsr) movq %rdi, %rcx # Load the MSR number into ecx. xorq %rax, %rax # Clear high bits of rax. rdmsr # Read the MSR into EDX:EAX. shlq $32, %rdx # Shift rdx into its high word. orq %rdx, %rax # OR rdx into rax. ret # Return. END_FUNCTION(ArReadMsr) // // VOID // ArWriteMsr ( // ULONG Msr, // ULONGLONG Value // ) // /*++ Routine Description: This routine writes the requested Model Specific Register. Arguments: Msr - Supplies the MSR to write. Value - Supplies the 64-bit value to write. Return Value: None. --*/ FUNCTION(ArWriteMsr) movq %rdi, %rcx # Load the MSR number into ECX. movq %rsi, %rdx # Load rdx with the whole 64-bit word. shrq $32, %rdx # Shift rdx right to put the high bits in edx. movl %esi, %eax # Put the low bits into eax. wrmsr # Write the MSR. ret # Return. END_FUNCTION(ArWriteMsr) // // PVOID // ArReadFsbase ( // VOID // ) // /*++ Routine Description: This routine reads the fs: base register. Arguments: None. Return Value: Returns the fsbase pointer. --*/ FUNCTION(ArReadFsbase) rdfsbase %rax # Read fsbase into rax. ret # Return. END_FUNCTION(ArReadFsbase) // // VOID // ArWriteFsbase ( // PVOID Fsbase // ) // /*++ Routine Description: This routine writes the fs: base register. Arguments: Fsbase - Supplies the new fsbase value to write. Return Value: None. --*/ FUNCTION(ArWriteFsbase) wrfsbase %rdi # Write fsbase. ret # Return. END_FUNCTION(ArWriteFsbase) // // PVOID // ArReadGsbase ( // VOID // ) // /*++ Routine Description: This routine reads the gs: base register. Arguments: None. Return Value: Returns the gsbase pointer. --*/ FUNCTION(ArReadGsbase) rdgsbase %rax # Read gsbase into rax. ret # Return. END_FUNCTION(ArReadGsbase) // // VOID // ArWriteGsbase ( // PVOID Gsbase // ) // /*++ Routine Description: This routine writes the gs: base register. Arguments: Gsbase - Supplies the new gsbase value to write. Return Value: None. --*/ FUNCTION(ArWriteGsbase) wrgsbase %rdi # Write gsbase. ret # Return. END_FUNCTION(ArWriteGsbase) // // VOID // ArSwapGs ( // VOID // ) // /*++ Routine Description: This routine exchanges the GS base hidden register with the kernel GS base MSR. Arguments: None. Return Value: None. --*/ FUNCTION(ArSwapGs) swapgs # Swap gs. ret # Return. END_FUNCTION(ArSwapGs) // // KERNEL_API // VOID // ArMonitor ( // PVOID Address, // UINTN Ecx, // UINTN Edx // ) // /*++ Routine Description: This routine arms the monitoring hardware in preparation for an mwait instruction. Arguments: Address - Supplies the address pointer to monitor. Ecx - Supplies the contents to load into the ECX (RCX in 64-bit) register when executing the monitor instruction. These are defined as hints. Edx - Supplies the contents to load into the EDX/RDX register. These are also hints. Return Value: None. --*/ PROTECTED_FUNCTION(ArMonitor) movq %rdi, %rax # Load the address to rax. movq %rsi, %rcx # Load the first set of hints. # The second set of hints is already in rdx. monitor # Arm the monitoring hardware. ret # Return. END_FUNCTION(ArMonitor) // // KERNEL_API // VOID // ArMwait ( // UINTN Eax, // UINTN Ecx // ) // /*++ Routine Description: This routine executes the mwait instruction, which is used to halt the processor until a specified memory location is written to. It is also used on Intel processors to enter C-states. A monitor instruction must have been executed prior to this to set up the monitoring region. Arguments: Eax - Supplies the contents to load into EAX/RAX when executing the mwait instruction. This is a set of hints, including which C-state to enter on Intel processors. Ecx - Supplies the contents to load into the ECX (RCX in 64-bit) register when executing the mwait instruction. This is 1 when entering a C-state with interrupts disabled to indicate that an interrupt should still break out. Return Value: None. --*/ PROTECTED_FUNCTION(ArMwait) movq %rdi, %rax # Load eax. movq %rsi, %rcx # Load ecx. mwait # Go down. ret # Return. END_FUNCTION(ArMwait) // // KERNEL_API // VOID // ArIoReadAndHalt ( // USHORT IoPort // ) // /*++ Routine Description: This routine performs a single 8-bit I/O port read and then halts the processor until the next interrupt comes in. This routine should be called with interrupts disabled, and will return with interrupts enabled. Arguments: IoPort - Supplies the I/O port to read from. Return Value: None. --*/ PROTECTED_FUNCTION(ArIoReadAndHalt) movq %rdi, %rdx # Move 1st parameter to dx. inb %dx, %al # Perform the I/O port read. sti # Enables interrupts one instruction later. hlt # Simtaneously halt and enable interrupts. ret END_FUNCTION(ArIoReadAndHalt) // // UINTN // ArSaveProcessorContext ( // PPROCESSOR_CONTEXT Context // ) // /*++ Routine Description: This routine saves the current processor context, including the non-volatile general registers and the system level control registers. This function appears to return twice, once when the context is saved and then again when the context is restored. Because the stack pointer is restored, the caller of this function may not return without either abandoning the context or calling restore. Returning and then calling restore would almost certainly result in stack corruption. Arguments: Context - Supplies a pointer to the context area to save into. Return Value: Returns 0 after the context was successfully saved (first time). Returns the value in the context return address register when the restore function is called (the second time). By default this value is 1, though it can be manipulated after the initial save is complete. --*/ FUNCTION(ArSaveProcessorContext) movq %rdi, %r10 # Save rdi into a volatile register. movl $1, %eax # Get default return value. cld # Increment edi upon stosd. stosq # Save it. movq (%rsp), %rax # Get the return address. stosq # Save it. xorl %eax, %eax # Zero out rax. movw %cs, %ax # Get CS. stosq # Save it. pushfq # Push rflags. popq %rax # Get rflags in rax. stosq # Save it. movq %r10, (%rdi) # Save original rdi. movq %rsi, 8(%rdi) # Save rsi. movq %rdx, 16(%rdi) # Save rdx. movq %rcx, 24(%rdi) # Save rcx. movq %r8, 32(%rdi) # Save r8. movq %r9, 40(%rdi) # Save r9. addq $48, %rdi # Advance over arguments. movq %rbx, %rax # Get rbx. stosq # Save it. movq %rbp, %rax # Get rbp. stosq # Save it. movq %rsp, %rax # Get rsp. addq $8, %rax # Pop off return address. stosq # Save it. movq %r12, (%rdi) # Save r12. movq %r13, 8(%rdi) # Save r13. movq %r14, 16(%rdi) # Save r14 movq %r15, 24(%rdi) # Save r15 addq $32, %rdi # Advance pointer for r12-r15. movq %dr7, %rax # Get dr7. stosq # Save it. movq %dr6, %rax # Get dr6. stosq # Save it. movq %dr0, %rax # Get dr0. stosq # Save it. movq %dr1, %rax # Get dr1. stosq # Save it. movq %dr2, %rax # Get dr2. stosq # Save it. movq %dr3, %rax # Get dr3. stosq # Save it. movq %rdi, (%rdi) # Save VA of this structure member. addq $8, %rdi # Advance over VA member. movq %cr0, %rax # Get cr0. stosq # Save it. movq %cr2, %rax # Get cr2. Why is there no cr1? stosq # Save it. movq %cr3, %rax # Get the all-important cr3. stosq # Save it. movq %cr4, %rax # Get cr4. stosq # Save it. movl $X86_MSR_FSBASE, %ecx # Load MSR number. rdmsr # Read FSBASE. shlq $32, %rdx # Shift rdx into its high word. orq %rdx, %rax # OR rdx into rax. stosq # Save it. movl $X86_MSR_GSBASE, %ecx # Load MSR number. rdmsr # Read GSBASE. shlq $32, %rdx # Shift rdx into its high word. orq %rdx, %rax # OR rdx into rax. stosq # Save it. movl $X86_MSR_KERNEL_GSBASE, %ecx # Load MSR number. rdmsr # Read kernel GSBASE. shlq $32, %rdx # Shift rdx into its high word. orq %rdx, %rax # OR rdx into rax. stosq # Save it. movl $X86_MSR_EFER, %ecx # Load MSR number. rdmsr # Read EFER. shlq $32, %rdx # Shift rdx into its high word. orq %rdx, %rax # OR rdx into rax. stosq # Save it. xor %eax, %eax # Clear out upper bits of rax. str %ax # Get the TR register. stosq # Save it. pushq %rdi # Save rdi temporarily. movq %rax, %rdi # Set the TSS segment as the first param. call ArClearTssBusyBit # Clear the busy bit from the TSS. popq %rdi # Restore rdi. sidt (%rdi) # Save IDT base/limit. addq $10, %rdi # Advance over IDT area. sgdt (%rdi) # Save GDT base/limit. xor %eax, %eax # Zero out return value. ret END_FUNCTION(ArSaveProcessorContext) // // VOID // ArRestoreProcessorContext ( // PPROCESSOR_CONTEXT Context // ) // /*++ Routine Description: This routine restores the current processor context, including the non-volatile general registers and the system level control registers. This function does not return, but instead jumps to the return address from the caller of the save context function. Arguments: Context - Supplies a pointer to the context to restore. Return Value: Does not return, at least not conventionally. --*/ FUNCTION(ArRestoreProcessorContext) ## ## Note that the processor must already be running in 64-bit mode. This ## function cannot be used to trampoline from 32-bit to 64-bit mode. ## movq %rdi, %rsi # Load the pointer into rsi (for stosq). movq 16(%rdi), %rax # Load CS into rax. addq $(PROCESSOR_CONTEXT_SIZE - 20), %rsi # Get to the IDT location. std # Decrement rsi during lodsq. ## ## After loading the GDT, a far return is needed for it to take effect. ## this also loads CS. ## pushq %rax # Push CS. movq ArRestoreProcessorContextJump@GOTPCREL(%rip), %rax pushq %rax # Push "return" address (just below). lgdt 10(%rsi) # Load the new GDT. retfq # Far return to load the new GDT. ArRestoreProcessorContextJump: lidt (%rsi) # Load the IDT. subq $8, %rsi # Retreat to tr. lodsq # Pop a value and retreat. ltr %ax # Load the task segment. lodsq # Pop a value and retreat. movl $X86_MSR_EFER, %ecx # Load the MSR number. movq %rax, %rdx # Load rdx with the whole 64-bit word. shrq $32, %rdx # Put the high bits in edx. wrmsr # Write the MSR. lodsq # Pop a value and retreat. movl $X86_MSR_KERNEL_GSBASE, %ecx # Load the MSR number. movq %rax, %rdx # Load rdx with the whole 64-bit word. shrq $32, %rdx # Put the high bits in edx. wrmsr # Write the MSR. lodsq # Pop a value and retreat. movl $X86_MSR_GSBASE, %ecx # Load the MSR number. movq %rax, %rdx # Load rdx with the whole 64-bit word. shrq $32, %rdx # Put the high bits in edx. wrmsr # Write the MSR. lodsq # Pop a value and retreat. movl $X86_MSR_FSBASE, %ecx # Load the MSR number. movq %rax, %rdx # Load rdx with the whole 64-bit word. shrq $32, %rdx # Put the high bits in edx. wrmsr # Write the MSR. lodsq # Pop a value and retreat. movq %rax, %cr4 # Load CR4. lodsq # Pop a value and retreat. movq %rax, %cr3 # Load CR3. lodsq # Pop a value and retreat. movq %rax, %cr2 # Load CR2. lodsq # Pop a value and retreat. movq %rax, %cr0 # Load CR0. lodsq # Pop a value and retreat. movq %rax, %rsi # Load the new VA. subq $8, %rsi # Retreat beyond the VA. lodsq # Pop a value and retreat. movq %rax, %dr3 # Restore a debug register. lodsq # Pop a value and retreat. movq %rax, %dr2 # Restore a debug register. lodsq # Pop a value and retreat. movq %rax, %dr1 # Restore a debug register. lodsq # Pop a value and retreat. movq %rax, %dr0 # Restore a debug register. lodsq # Pop a value and retreat. movq %rax, %dr6 # Restore a debug register. lodsq # Pop a value and retreat. movq %rax, %dr7 # Restore a debug register. movq (%rsi), %r15 # Restore a general register. movq -8(%rsi), %r14 # Restore a general register. movq -16(%rsi), %r13 # Restore a general register. movq -24(%rsi), %r12 # Restore a general register. movq -32(%rsi), %rax # Get RSP. movq %rax, %rsp # Restore rsp. movq -40(%rsi), %rbp # Restore a general register. movq -48(%rsi), %rbx # Restore a general register. movq -56(%rsi), %r9 # Restore an argument register. movq -64(%rsi), %r8 # Restore an argument register. movq -72(%rsi), %rcx # Restore an argument register. movq -80(%rsi), %rdx # Restore an argument register. movq -88(%rsi), %r10 # Restore RSI into R10 (volatile) for now. movq -96(%rsi), %rdi # Restore an argument register. movq -104(%rsi), %rax # Get RFLAGS. subq $120, %rsi # Advance beyond the registers and CS. pushq %rax # Push rflags. lodsq # Pop a value and retreat. movq %rax, %r11 # Save rip in r11 (a volatile register). lodsq # Pop rax, the return value, into place. movq %r10, %rsi # Restore RSI (held in R10 from above). popfq # Pop rflags (including direction flag). jmp *%r11 # Jump off to the return value. END_FUNCTION(ArRestoreProcessorContext) // // --------------------------------------------------------- Internal Functions //