;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; ;;; Microsoft Research Singularity ;;; ;;; Copyright (c) Microsoft Corporation. All rights reserved. ;;; ;;; This file contains x86-specific assembly code related to context save and restore. ;;; The key goal here is to keep this set of code as small as possible, in favor ;;; of portable C++ or C# code. include hal.inc ;;; Save takes one argument - a context record to save the context in. It saves all the ;;; nonvolatile state (it does not bother saving caller save regs.) ;;; ;;; This function returns true after saving the context. When the context is resumed, control ;;; resumption will occur at the point this function returned, but with a false ;;; return value. ;;; ;;; Calling conventions are normal __fastcall. ;; __fastcall bool SaveContext(Context *context) SYMFIX(?m_Save@Struct_Microsoft_Singularity_Isal_SpillContext@@SI_NPAU1@@Z) proc ;; Save integer registers - only need to record callee-save registers. ;; don't need exact flags, but we'll pick up the non-transient ones PUSHFP pop [PCX].Struct_Microsoft_Singularity_Isal_SpillContext._fl ;; Interrupts are always disabled at this point. ;; EFlags should reflect this for bookkeeping purposes (this will not be the case ;; on non-ring0 HALs.) and [PCX].Struct_Microsoft_Singularity_Isal_SpillContext._fl, \ NOT Struct_Microsoft_Singularity_Isal_IX_EFlags_IF mov PAX, cs mov [PCX].Struct_Microsoft_Singularity_Isal_SpillContext._cs, PAX mov [PCX].Struct_Microsoft_Singularity_Isal_SpillContext._bp, PBP mov [PCX].Struct_Microsoft_Singularity_Isal_SpillContext._sp, PSP lea PAX, resume_context mov [PCX].Struct_Microsoft_Singularity_Isal_SpillContext._ip, PAX ;; Save previous stack limit GET_THREAD_RECORD_FIELD PAX, _activeStackLimit mov [PCX].Struct_Microsoft_Singularity_Isal_SpillContext._stackLimit, PAX save_integer: ;; Always save caller-saved registers. mov [PCX].Struct_Microsoft_Singularity_Isal_SpillContext._bx, PBX mov [PCX].Struct_Microsoft_Singularity_Isal_SpillContext._di, PDI mov [PCX].Struct_Microsoft_Singularity_Isal_SpillContext._si, PSI ifdef ISA_IX64 mov [PCX].Struct_Microsoft_Singularity_Isal_SpillContext._r12, r12 mov [PCX].Struct_Microsoft_Singularity_Isal_SpillContext._r13, r13 mov [PCX].Struct_Microsoft_Singularity_Isal_SpillContext._r14, r14 mov [PCX].Struct_Microsoft_Singularity_Isal_SpillContext._r15, r15 endif ;; Save floating point/mmx registers fxsave [PCX].Struct_Microsoft_Singularity_Isal_SpillContext._mmx ifdef DEBUG ;; Garbage out callee-saved registers mov PAX, 0deadbeefh mov [PCX].Struct_Microsoft_Singularity_Isal_SpillContext._ax, PAX mov [PCX].Struct_Microsoft_Singularity_Isal_SpillContext._cx, PAX mov [PCX].Struct_Microsoft_Singularity_Isal_SpillContext._dx, PAX ifdef ISA_IX64 mov [PCX].Struct_Microsoft_Singularity_Isal_SpillContext._r8, PAX mov [PCX].Struct_Microsoft_Singularity_Isal_SpillContext._r9, PAX mov [PCX].Struct_Microsoft_Singularity_Isal_SpillContext._r10, PAX mov [PCX].Struct_Microsoft_Singularity_Isal_SpillContext._r11, PAX endif endif ifdef PAGING mov PAX, cr3 mov [PCX].Struct_Microsoft_Singularity_Isal_SpillContext._cr3, PAX endif ;; Mark context as non-active or [PCX].Struct_Microsoft_Singularity_Isal_SpillContext._spillFlags, \ Struct_Microsoft_Singularity_Isal_SpillContext_ContentsSpilled ;; return true mov PAX, 1 ret resume_context: ;; this is where non-interrupt control resume will happen ;; TBD: just stash the return address in the context... ;; return false xor PAX, PAX ret SYMFIX(?m_Save@Struct_Microsoft_Singularity_Isal_SpillContext@@SI_NPAU1@@Z) endp ;;; Save takes two arguments - a context record to save the context in, and ;;; an interrupt frame to describe an interruption location. ;;; ;;; Calling conventions are normal __fastcall. ;; __fastcall bool SaveContext(Context *context, InterruptContext *interrupt) SYMFIX(?m_Save@Struct_Microsoft_Singularity_Isal_SpillContext@@SI_NPAU1@PAUStruct_Microsoft_Singularity_Isal_InterruptContext@@@Z) proc ;; Save control registers ;; Note: we don't bother to save ss since it can be derived either from cs or ;; from the current ss. ifdef PAGING ;; Check if this was a cross-ring interrupt mov PAX, cs xor PAX, [PDX].Struct_Microsoft_Singularity_Isal_InterruptContext._cs test PAX, 3 jz save_same_ring ;; resume at the stack pointed to by the Interrupt Frame mov PAX, [PDX].Struct_Microsoft_Singularity_Isal_InterruptContext._sp mov [PCX].Struct_Microsoft_Singularity_Isal_SpillContext._sp, PAX jmp save_control_common endif save_same_ring: ifdef ISA_IX86 ;; resume at the stack after popping the Interrupt Frame lea PAX, [PDX].Struct_Microsoft_Singularity_Isal_InterruptContext._sp mov [PCX].Struct_Microsoft_Singularity_Isal_SpillContext._sp, PAX else mov PAX, [PDX].Struct_Microsoft_Singularity_Isal_InterruptContext._sp mov [PCX].Struct_Microsoft_Singularity_Isal_SpillContext._sp, PAX endif save_control_common: mov [PCX].Struct_Microsoft_Singularity_Isal_SpillContext._bp, PBP ;; Save previous stack limit GET_THREAD_RECORD_FIELD PAX, _activeStackLimit mov [PCX].Struct_Microsoft_Singularity_Isal_SpillContext._stackLimit, PAX mov PAX, [PDX].Struct_Microsoft_Singularity_Isal_InterruptContext._ip mov [PCX].Struct_Microsoft_Singularity_Isal_SpillContext._ip, PAX mov PAX, [PDX].Struct_Microsoft_Singularity_Isal_InterruptContext._fl mov [PCX].Struct_Microsoft_Singularity_Isal_SpillContext._fl, PAX mov PAX, [PDX].Struct_Microsoft_Singularity_Isal_InterruptContext._cs mov [PCX].Struct_Microsoft_Singularity_Isal_SpillContext._cs, PAX save_integer: mov [PCX].Struct_Microsoft_Singularity_Isal_SpillContext._bx, PBX mov [PCX].Struct_Microsoft_Singularity_Isal_SpillContext._di, PDI mov [PCX].Struct_Microsoft_Singularity_Isal_SpillContext._si, PSI ifdef ISA_IX64 mov [PCX].Struct_Microsoft_Singularity_Isal_SpillContext._r12, r12 mov [PCX].Struct_Microsoft_Singularity_Isal_SpillContext._r13, r13 mov [PCX].Struct_Microsoft_Singularity_Isal_SpillContext._r14, r14 mov [PCX].Struct_Microsoft_Singularity_Isal_SpillContext._r15, r15 endif mov PAX, [PDX].Struct_Microsoft_Singularity_Isal_InterruptContext._ax mov [PCX].Struct_Microsoft_Singularity_Isal_SpillContext._ax, PAX mov PAX, [PDX].Struct_Microsoft_Singularity_Isal_InterruptContext._cx mov [PCX].Struct_Microsoft_Singularity_Isal_SpillContext._cx, PAX mov PAX, [PDX].Struct_Microsoft_Singularity_Isal_InterruptContext._dx mov [PCX].Struct_Microsoft_Singularity_Isal_SpillContext._dx, PAX ifdef ISA_IX64 mov PAX, [PDX].Struct_Microsoft_Singularity_Isal_InterruptContext._r8 mov [PCX].Struct_Microsoft_Singularity_Isal_SpillContext._r8, PAX mov PAX, [PDX].Struct_Microsoft_Singularity_Isal_InterruptContext._r9 mov [PCX].Struct_Microsoft_Singularity_Isal_SpillContext._r9, PAX mov PAX, [PDX].Struct_Microsoft_Singularity_Isal_InterruptContext._r10 mov [PCX].Struct_Microsoft_Singularity_Isal_SpillContext._r10, PAX mov PAX, [PDX].Struct_Microsoft_Singularity_Isal_InterruptContext._r11 mov [PCX].Struct_Microsoft_Singularity_Isal_SpillContext._r11, PAX endif save_fp: ;; Save floating point/mmx registers fxsave [PCX].Struct_Microsoft_Singularity_Isal_SpillContext._mmx ifdef PAGING mov PAX, cr3 mov [PCX].Struct_Microsoft_Singularity_Isal_SpillContext._cr3, PAX endif ;; Mark context as non-active or [PCX].Struct_Microsoft_Singularity_Isal_SpillContext._spillFlags, \ Struct_Microsoft_Singularity_Isal_SpillContext_ContentsSpilled ret SYMFIX(?m_Save@Struct_Microsoft_Singularity_Isal_SpillContext@@SI_NPAU1@PAUStruct_Microsoft_Singularity_Isal_InterruptContext@@@Z) endp ;;; Resume restores the processor state to the state described in the given ;;; context record. If an interrupt context is provided, the portion of the context ;;; represented there is spilled to the interrupt context rather than the current context. ;;; ;;; ;;; __fastcall void ResumeContext(Struct_Microsoft_Singularity_Isal_SpillContext *context, ;;; Struct_Microsoft_Singularity_Isal_InterruptContext *interrupt); SYMFIX(?m_Resume@Struct_Microsoft_Singularity_Isal_SpillContext@@SIXPAU1@@Z) proc ifdef PAGING ;; Restore cr3 if necessary, but avoid TLB flushes if possible mov PDX, [PCX].Struct_Microsoft_Singularity_Isal_SpillContext._cr3 mov PAX, cr3 cmp PAX, PDX je skip_cr3 mov cr3, PDX skip_cr3: endif ;; Restore floating point/mmx registers fxrstor [PCX].Struct_Microsoft_Singularity_Isal_SpillContext._mmx ifdef ISA_IX86 ifdef PAGING ;; Restore segment registers mov eax, [ecx].Struct_Microsoft_Singularity_Isal_SpillContext._cs mov ds, eax mov es, eax endif endif ;; Restore integer registers ;; save PAX for scratch mov PBX, [PCX].Struct_Microsoft_Singularity_Isal_SpillContext._bx ;; skip PCX since that's our context; we'll pick it up last thing ;; save PDX for scratch mov PDI, [PCX].Struct_Microsoft_Singularity_Isal_SpillContext._di mov PSI, [PCX].Struct_Microsoft_Singularity_Isal_SpillContext._si ifdef ISA_IX64 mov r12, [PCX].Struct_Microsoft_Singularity_Isal_SpillContext._r12 mov r13, [PCX].Struct_Microsoft_Singularity_Isal_SpillContext._r13 mov r14, [PCX].Struct_Microsoft_Singularity_Isal_SpillContext._r14 mov r15, [PCX].Struct_Microsoft_Singularity_Isal_SpillContext._r15 endif ;; Now restore control registers ifdef PAGING ;; See if we are transitioning across rings or not mov PAX, cs xor PAX, [PCX].Struct_Microsoft_Singularity_Isal_SpillContext._cs test PAX, 3 jz same_ring ;; iret will restore the stack for us. Cs == ss when we control segments push [PCX].Struct_Microsoft_Singularity_Isal_SpillContext._cs push [PCX].Struct_Microsoft_Singularity_Isal_SpillContext._sp jmp restore_control endif same_ring: ;; Note: we use existing ss rather than saved cs (on windows, they may be different) ifdef ISA_IX86 ;; Restore sp directly mov PSP, [PCX].Struct_Microsoft_Singularity_Isal_SpillContext._sp else ;; iret will restore the stack for us mov PAX, ss push PAX push [PCX].Struct_Microsoft_Singularity_Isal_SpillContext._sp endif restore_control: ;; Restore previous stack limit mov PAX, [PCX].Struct_Microsoft_Singularity_Isal_SpillContext._stackLimit SET_THREAD_RECORD_FIELD _activeStackLimit, PAX ;; push values for iret push [PCX].Struct_Microsoft_Singularity_Isal_SpillContext._fl push [PCX].Struct_Microsoft_Singularity_Isal_SpillContext._cs push [PCX].Struct_Microsoft_Singularity_Isal_SpillContext._ip mov PBP, [PCX].Struct_Microsoft_Singularity_Isal_SpillContext._bp ;; clear spilled flag and [PCX].Struct_Microsoft_Singularity_Isal_SpillContext._spillFlags, \ NOT Struct_Microsoft_Singularity_Isal_SpillContext_ContentsSpilled ;; Restore regs & return ;; (Note that our integer flags may not be set; but these regs are ;; trashed by calling conventions anyway.) ifdef ISA_IX64 mov r11, [PCX].Struct_Microsoft_Singularity_Isal_SpillContext._r11 mov r10, [PCX].Struct_Microsoft_Singularity_Isal_SpillContext._r10 mov r9, [PCX].Struct_Microsoft_Singularity_Isal_SpillContext._r9 mov r8, [PCX].Struct_Microsoft_Singularity_Isal_SpillContext._r8 endif mov PAX, [PCX].Struct_Microsoft_Singularity_Isal_SpillContext._ax mov PDX, [PCX].Struct_Microsoft_Singularity_Isal_SpillContext._dx mov PCX, [PCX].Struct_Microsoft_Singularity_Isal_SpillContext._cx jmp [SYMFIX(?c_returnFromInterrupt@Class_Microsoft_Singularity_Isal_Isa@@2PAUvoid@@A)] SYMFIX(?m_Resume@Struct_Microsoft_Singularity_Isal_SpillContext@@SIXPAU1@@Z) endp ;;; ResetContext resets the current context fp & debug register state to a canonical state ;;; for interrupt handler code. This should only be used after saving the current context. ;;; ;;; Calling conventions are normal __fastcall. ;;; ;;; __fastcall void ResetCurrent(); SYMFIX(?g_ResetCurrent@Struct_Microsoft_Singularity_Isal_SpillContext@@SIXXZ) proc push PAX fninit mov PAX, 37eh push PAX fldcw [PSP] pop PAX pop PAX ret SYMFIX(?g_ResetCurrent@Struct_Microsoft_Singularity_Isal_SpillContext@@SIXXZ) endp END