ctxswap.S 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. /*++
  2. Copyright (c) 2017 Minoca Corp.
  3. This file is licensed under the terms of the GNU General Public License
  4. version 3. Alternative licensing terms are available. Contact
  5. info@minocacorp.com for details. See the LICENSE file at the root of this
  6. project for complete licensing information.
  7. Module Name:
  8. ctxswap.S
  9. Abstract:
  10. This module implements context switching on AMD64.
  11. Author:
  12. Evan Green 11-Jun-2017
  13. Environment:
  14. Kernel mode
  15. --*/
  16. //
  17. // ------------------------------------------------------------------- Includes
  18. //
  19. #include <minoca/kernel/x64.inc>
  20. //
  21. // ---------------------------------------------------------------- Definitions
  22. //
  23. //
  24. // ----------------------------------------------------------------------- Code
  25. //
  26. ASSEMBLY_FILE_HEADER
  27. //
  28. // VOID
  29. // KepContextSwap (
  30. // PVOID *SavedStackLocation,
  31. // PVOID NewStack,
  32. // PVOID NewThreadPointer,
  33. // BOOL FirstTime
  34. // )
  35. //
  36. /*++
  37. Routine Description:
  38. This routine switches context to the given thread.
  39. Arguments:
  40. SavedStackLocation - Supplies a pointer where the old stack pointer will
  41. be saved.
  42. NewStack - Supplies the new stack address.
  43. NewThreadPointer - Supplies the new thread pointer data.
  44. FirstTime - Supplies a boolean indicating whether the thread has never been
  45. run before.
  46. Return Value:
  47. None.
  48. --*/
  49. FUNCTION(KepContextSwap)
  50. pushq %rbp # Function prologue (also saves rbp).
  51. movq %rsp, %rbp #
  52. pushq %rbx # Save nonvolatile registers.
  53. pushq %r12
  54. pushq %r13
  55. pushq %r14
  56. pushq %r15
  57. pushfq
  58. subq $16, %rsp # Grab some extra stack space, aligned.
  59. movl $CONTEXT_SWAP_MAGIC, (%rsp) # Save a sentinal on the stack.
  60. //
  61. // Save the current thread's stack pointer. This effectively freezes the
  62. // current thread. When this thread is swapped back in, the stack pointer
  63. // will be restored, and execution of this function will continue. It's
  64. // crucial that the stack pointer not change between a normal context swap
  65. // and a first-time context swap, otherwise the thread that created the
  66. // context swap will have an incorrect stack next time it is swapped in.
  67. //
  68. movq %rsp, (%rdi) # Save stack pointer.
  69. //
  70. // Switch to the new stack and perform work than can only be done once off
  71. // of the old stack. Access the new stack before switching to it to trigger
  72. // any PML4 updates needed to see the new stack from old CR3.
  73. //
  74. movl (%rsi), %eax # Poke the new stack to trigger page faults.
  75. movq %rsi, %rsp # Switch to the new stack.
  76. xorq %rbp, %rbp # Zero out rbp so the call stack stops here.
  77. movq %rcx, %r12 # Move FirstTime parameter to non-volatile.
  78. //
  79. // Switch to the new thread pointer. The kernel doesn't use FS, so it's no
  80. // biggie.
  81. //
  82. movl $X86_MSR_FSBASE, %ecx # Set the MSR.
  83. movl %edx, %eax # Copy the low word into eax.
  84. shlq $32, %rdx # Shift rdx into its high word.
  85. wrmsr # Write the new FS base for user mode.
  86. //
  87. // Perform any post-stack switch work needed on the old thread.
  88. //
  89. call KepPostContextSwapWork # Perform post swap work.
  90. //
  91. // Determine whether to do a first-time return or a normal one. The only
  92. // difference is that a first-time execution has been set up to do an iret,
  93. // and a normal context swap doesn't need that because the stack is already
  94. // set up correctly.
  95. //
  96. cmpl $FALSE, %r12d # Compare FirstTime to FALSE.
  97. je KepContextSwapRestore # Special case a first run.
  98. KepContextSwapFirstTime:
  99. movq %rsp, %rdi # 1st argument is the trap frame pointer.
  100. call KepPreThreadStartWork # Do any thread setup needed.
  101. call ArRestoreTrapFrame # Set up CPU context.
  102. iretq # Return from the artificial exception.
  103. KepContextSwapRestore:
  104. movl (%rsp), %eax # Check the sentinal value.
  105. cmpl $CONTEXT_SWAP_MAGIC, %eax # Compare.
  106. jne KepContextSwapBadMagic # Jump out of line if it's bad.
  107. KepContextSwapReturn:
  108. addq $16, %rsp # Undo previous alignment adjustment.
  109. popfq # Restore registers.
  110. popq %r15
  111. popq %r14
  112. popq %r13
  113. popq %r12
  114. popq %rbx
  115. popq %rbp
  116. ret
  117. KepContextSwapBadMagic:
  118. int $0x3 # Break on bad context.
  119. jmp KepContextSwapReturn # If we break here, WATCH OUT.
  120. END_FUNCTION(KepContextSwap)