ctxswap.S 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. /*++
  2. Copyright (c) 2012 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 x86
  11. Author:
  12. Evan Green 6-Aug-2012
  13. Environment:
  14. Kernel mode
  15. --*/
  16. //
  17. // ------------------------------------------------------------------- Includes
  18. //
  19. #include <minoca/kernel/x86.inc>
  20. //
  21. // ---------------------------------------------------------------- Definitions
  22. //
  23. //
  24. // Define the offset into the GDT where the thread area entry starts, in bytes.
  25. //
  26. #define GDT_THREAD_OFFSET (GDT_THREAD & ~0x7)
  27. //
  28. // ----------------------------------------------------------------------- Code
  29. //
  30. //
  31. // .text specifies that this code belongs in the executable section.
  32. //
  33. // .code32 specifies that this is 32-bit protected mode code.
  34. //
  35. .text
  36. .code32
  37. //
  38. // VOID
  39. // KepContextSwap (
  40. // PVOID *SavedStackLocation,
  41. // PVOID NewStack,
  42. // PVOID NewThreadPointer,
  43. // BOOL FirstTime
  44. // )
  45. //
  46. /*++
  47. Routine Description:
  48. This routine switches context to the given thread.
  49. Arguments:
  50. SavedStackLocation - Supplies a pointer where the old stack pointer will
  51. be saved.
  52. NewStack - Supplies the new stack address.
  53. NewThreadPointer - Supplies the new thread pointer data.
  54. FirstTime - Supplies a boolean indicating whether the thread has never been
  55. run before.
  56. Return Value:
  57. None.
  58. --*/
  59. //
  60. // Parameters
  61. //
  62. .equ SavedStackLocation, 8
  63. .equ NewStack, 12
  64. .equ NewThreadPointerLowerGdt, 16
  65. .equ NewThreadPointerUpperGdt, 20
  66. .equ FirstTime, 24
  67. FUNCTION(KepContextSwap)
  68. pushl %ebp # Function prologue.
  69. movl %esp, %ebp #
  70. pushl %ebx # Save nonvolatile registers.
  71. pushl %esi
  72. pushl %edi
  73. pushl %ebp
  74. pushfl
  75. pushl $CONTEXT_SWAP_MAGIC
  76. //
  77. // Load the new thread pointer GDT entry.
  78. //
  79. movl NewThreadPointerLowerGdt(%ebp), %eax # Get lower GDT half.
  80. movl NewThreadPointerUpperGdt(%ebp), %edx # Get upper GDT half.
  81. movl %fs:(PROCESSOR_BLOCK_GDT), %ecx # Get current GDT.
  82. movl %eax, GDT_THREAD_OFFSET(%ecx) # Load lower half.
  83. movl %edx, GDT_THREAD_OFFSET+4(%ecx) # Load upper half.
  84. movw $GDT_THREAD, %ax # Load the GS value.
  85. movw %ax, %gs # Reload GS to take effect.
  86. //
  87. // Save the parameters before the stack switch is initiated.
  88. //
  89. movl FirstTime(%ebp), %esi # Get FirstTime parameter.
  90. //
  91. // Save the current thread's stack pointer. This effectively freezes the
  92. // current thread. When this thread is swapped back in, the stack pointer
  93. // will be restored, and execution of this function will continue. It's
  94. // crucial that the stack pointer not change between a normal context swap
  95. // and a first-time context swap, otherwise the thread that created the
  96. // context swap will have an incorrect stack next time it is swapped in.
  97. //
  98. movl SavedStackLocation(%ebp), %ecx # Get location to save in.
  99. movl %esp, (%ecx) # Save stack pointer.
  100. //
  101. // Switch to the new stack and perform work than can only be done once off
  102. // of the old stack. Touch the stack before switching to it to trigger any
  103. // page directory updates needed to see the new stack from the old CR3.
  104. //
  105. movl NewStack(%ebp), %eax # Get the new stack.
  106. movl (%eax), %ecx # Poke the new stack to trigger page faults.
  107. movl %eax, %esp # Switch to the new stack.
  108. xor %ebp, %ebp # Zero out ebp so the call stack stops here.
  109. //
  110. // Perform any post-stack switch work needed on the old thread.
  111. //
  112. call KepPostContextSwapWork # Perform post swap work.
  113. //
  114. // Determine whether to do a first-time return or a normal one. The only
  115. // difference is that a first-time execution has been set up to do an iret,
  116. // and a normal context swap doesn't need that because the stack is already
  117. // set up correctly.
  118. //
  119. cmpl $FALSE, %esi # Compare FirstTime to FALSE.
  120. je KepContextSwapRestore # Special case a first run.
  121. KepContextSwapFirstTime:
  122. pushl %esp # Push a pointer to the trap frame.
  123. call KepPreThreadStartWork # Do any thread setup needed.
  124. addl $4, %esp # Pop the parameter.
  125. mov %esp, %ebx # Set restore trap frame parameter.
  126. call ArRestoreTrapFrame # Set up CPU context.
  127. addl $4, %esp # Pop the error code.
  128. iret # Return from the artificial exception.
  129. KepContextSwapRestore:
  130. popl %eax # Pop the magic value.
  131. cmp $CONTEXT_SWAP_MAGIC, %eax # Compare.
  132. jne KepContextSwapBadMagic # Jump out of line if it's bad.
  133. KepContextSwapReturn:
  134. popfl # Restore registers.
  135. popl %ebp
  136. popl %edi
  137. popl %esi
  138. popl %ebx
  139. leave
  140. ret
  141. KepContextSwapBadMagic:
  142. int $0x3 # Break on bad context.
  143. jmp KepContextSwapReturn # If we break here, WATCH OUT.
  144. END_FUNCTION(KepContextSwap)