Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

401 lines
13 KiB

  1. // TITLE("Call Out to User Mode")
  2. //++
  3. //
  4. // Copyright (c) 1994 Microsoft Corporation
  5. //
  6. // Module Name:
  7. //
  8. // callout.s
  9. //
  10. // Abstract:
  11. //
  12. // This module implements the code necessary to call out from kernel
  13. // mode to user mode.
  14. //
  15. // Author:
  16. //
  17. // John Vert (jvert) 2-Nov-1994
  18. //
  19. // Environment:
  20. //
  21. // Kernel mode only.
  22. //
  23. // Revision History:
  24. //
  25. //--
  26. #include "ksalpha.h"
  27. //
  28. // Define external variables that can be addressed using GP.
  29. //
  30. .extern KeUserCallbackDispatcher
  31. SBTTL("Call User Mode Function")
  32. //++
  33. //
  34. // NTSTATUS
  35. // KiCallUserMode (
  36. // IN PVOID *OutputBuffer,
  37. // IN PULONG OutputLength
  38. // )
  39. //
  40. // Routine Description:
  41. //
  42. // This function calls a user mode function.
  43. //
  44. // N.B. This function calls out to user mode and the NtCallbackReturn
  45. // function returns back to the caller of this function. Therefore,
  46. // the stack layout must be consistent between the two routines.
  47. //
  48. // Arguments:
  49. //
  50. // OutputBuffer (a0) - Supplies a pointer to the variable that receivies
  51. // the address of the output buffer.
  52. //
  53. // OutputLength (a1) - Supplies a pointer to a variable that receives
  54. // the length of the output buffer.
  55. //
  56. // Return Value:
  57. //
  58. // The final status of the call out function is returned as the status
  59. // of the function.
  60. //
  61. // N.B. This function does not return to its caller. A return to the
  62. // caller is executed when a NtCallbackReturn system service is
  63. // executed.
  64. //
  65. // N.B. This function does return to its caller if a kernel stack
  66. // expansion is required and the attempted expansion fails.
  67. //
  68. //--
  69. NESTED_ENTRY(KiCallUserMode, CuFrameLength, zero)
  70. lda sp, -CuFrameLength(sp) // allocate stack frame
  71. stq ra, CuRa(sp) // save return address
  72. //
  73. // Save nonvolatile integer registers.
  74. //
  75. stq s0, CuS0(sp) // save integer registers s0 - s5
  76. stq s1, CuS1(sp) //
  77. stq s2, CuS2(sp) //
  78. stq s3, CuS3(sp) //
  79. stq s4, CuS4(sp) //
  80. stq s5, CuS5(sp) //
  81. stq fp, CuFP(sp) // save FP
  82. //
  83. // Save nonvolatile floating registers.
  84. //
  85. stt f2, CuF2(sp) // save floating registers f2 - f9
  86. stt f3, CuF3(sp) //
  87. stt f4, CuF4(sp) //
  88. stt f5, CuF5(sp) //
  89. stt f6, CuF6(sp) //
  90. stt f7, CuF7(sp) //
  91. stt f8, CuF8(sp) //
  92. stt f9, CuF9(sp) //
  93. PROLOGUE_END
  94. //
  95. // Save argument registers.
  96. //
  97. stq a0, CuA0(sp) // save output buffer address
  98. stq a1, CuA1(sp) // save output length address
  99. //
  100. // Check if sufficient room is available on the kernel stack for another
  101. // system call.
  102. //
  103. GET_CURRENT_THREAD // get current thread address
  104. bis v0, zero, t0 // save current thread address
  105. LDP t1, ThInitialStack(t0) // get initial stack address
  106. LDP t2, ThStackLimit(t0) // get current stack limit
  107. SUBP sp, KERNEL_LARGE_STACK_COMMIT, t3 // compute bottom address
  108. cmpult t2, t3, t4 // check if limit exceeded
  109. bne t4, 10f // if ne, limit not exceeded
  110. bis sp, zero, a0 // set current kernel stack address
  111. bsr ra, MmGrowKernelStack // attempt to grow the kernel stack
  112. bne v0, 20f // if ne, attempt to grow failed
  113. GET_CURRENT_THREAD // get current thread address
  114. bis v0, zero, t0 // save current thread address
  115. LDP t1, ThInitialStack(t0) // get initial stack address
  116. 10: LDP fp, ThTrapFrame(t0) // get trap frame address
  117. LDP t2, ThCallbackStack(t0) // get callback stack address
  118. STP t1, CuInStk(sp) // save initial stack address
  119. STP fp, CuTrFr(sp) // save trap frame address
  120. STP t2, CuCbStk(sp) // save callback stack address
  121. STP sp, ThCallbackStack(t0) // set callback stack address
  122. //
  123. // Restore state and callback to user mode.
  124. //
  125. // N.B. Interrupts are disabled to prevent get/set context APCs from
  126. // occurring.
  127. //
  128. DISABLE_INTERRUPTS // disable interrupts
  129. STP sp, ThInitialStack(t0) // reset initial stack address
  130. ldq t3, TrFir(fp) // get old PC
  131. STP t3, CuTrFir(sp) // save old PC
  132. LDP t4, KeUserCallbackDispatcher // get continuation address
  133. stq t4, TrFir(fp) // set continuation address
  134. //
  135. // If a user mode APC is pending, then request an APC interrupt.
  136. //
  137. bis zero, zero, a1 // assume no user APC pending
  138. ldq_u t1, ThApcState+AsUserApcPending(t0) // get user APC pending
  139. extbl t1, (ThApcState+AsUserApcPending) % 8, t1 //
  140. ZeroByte(ThAlerted(t0)) // clear kernel mode alerted
  141. cmovne t1, APC_INTERRUPT, a1 // if pending set APC interrupt
  142. //
  143. // Set initial kernel stack for this thread.
  144. //
  145. bis sp, zero, a0 // set kernel stack address
  146. SET_INITIAL_KERNEL_STACK // set kernel stack pointer
  147. ldl a0, TrPsr(fp) // get previous processor status
  148. //
  149. // a0 = previous psr
  150. // a1 = sfw interrupt requests
  151. //
  152. RETURN_FROM_SYSTEM_CALL // return to user mode
  153. ret zero, (ra) //
  154. //
  155. // An attempt to grow the kernel stack failed.
  156. //
  157. 20: ldq ra, CuRa(sp) // restore return address
  158. lda sp, CuFrameLength(sp) // deallocate stack frame
  159. ret zero, (ra)
  160. .end KiCallUserMode
  161. SBTTL("Switch Kernel Stack")
  162. //++
  163. //
  164. // PVOID
  165. // KeSwitchKernelStack (
  166. // IN PVOID StackBase,
  167. // IN PVOID StackLimit
  168. // )
  169. //
  170. // Routine Description:
  171. //
  172. // This function switches to the specified large kernel stack.
  173. //
  174. // N.B. This function can ONLY be called when there are no variables
  175. // in the stack that refer to other variables in the stack, i.e.,
  176. // there are no pointers into the stack.
  177. //
  178. // Arguments:
  179. //
  180. // StackBase (a0) - Supplies a pointer to the base of the new kernel
  181. // stack.
  182. //
  183. // StackLimit (a1) - supplies a pointer to the limit of the new kernel
  184. // stack.
  185. //
  186. // Return Value:
  187. //
  188. // The old kernel stack is returned as the function value.
  189. //
  190. //--
  191. .struct 0
  192. SsRa: .space 8 // saved return address
  193. SsSp: .space 8 // saved new stack pointer
  194. SsA0: .space 8 // saved argument registers a0-a1
  195. SsA1: .space 8 //
  196. SsFrameLength: // length of stack frame
  197. NESTED_ENTRY(KeSwitchKernelStack, SsFrameLength, zero)
  198. lda sp, -SsFrameLength(sp) // allocate stack frame
  199. stq ra, SsRa(sp) // save return address
  200. PROLOGUE_END
  201. //
  202. // Save the address of the new stack and copy the old stack to the new
  203. // stack.
  204. //
  205. GET_CURRENT_THREAD // get current thread address
  206. stq a0, SsA0(sp) // save new stack base address
  207. stq a1, SsA1(sp) // save new stack limit address
  208. LDP a2, ThStackBase(v0) // get current stack base address
  209. LDP a3, ThTrapFrame(v0) // get current trap frame address
  210. ADDP a3, a0, a3 // relocate trap frame address
  211. SUBP a3, a2, a3 //
  212. STP a3, ThTrapFrame(v0) // set current trap frame address
  213. bis sp, zero, a1 // set source address of copy
  214. SUBP a2, sp, a2 // compute length of copy
  215. SUBP a0, a2, a0 // set destination address of copy
  216. stq a0, SsSp(sp) // save new stack pointer address
  217. bsr ra, RtlMoveMemory // copy old stack to new stack
  218. //
  219. // Switch to new kernel stack and return the address of the old kernel stack.
  220. //
  221. GET_CURRENT_THREAD // get current thread address
  222. DISABLE_INTERRUPTS // disable interrupts
  223. LDP t0, ThStackBase(v0) // get old stack base address
  224. ldq a0, SsA0(sp) // get new stack base address
  225. ldq a1, SsA1(sp) // get new stack limit address
  226. STP a0, ThInitialStack(v0) // set new initial stack address
  227. STP a0, ThStackBase(v0) // set new stack base address
  228. STP a1, ThStackLimit(v0) // set new stack limit address
  229. ldil t1, TRUE // set large kernel stack TRUE
  230. StoreByte(t1, ThLargeStack(v0)) //
  231. ldq sp, SsSp(sp) // set initial stack address
  232. SET_INITIAL_KERNEL_STACK // set initial kernel stack address
  233. ENABLE_INTERRUPTS // enable interrupts
  234. ldq ra, SsRa(sp) // restore return address
  235. lda sp, SsFrameLength(sp) // deallocate stack frame
  236. ret zero, (ra) // return
  237. .end KeSwitchKernelStack
  238. SBTTL("Return from User Mode Callback")
  239. //++
  240. //
  241. // NTSTATUS
  242. // NtCallbackReturn (
  243. // IN PVOID OutputBuffer OPTIONAL,
  244. // IN ULONG OutputLength,
  245. // IN NTSTATUS Status
  246. // )
  247. //
  248. // Routine Description:
  249. //
  250. // This function returns from a user mode callout to the kernel
  251. // mode caller of the user mode callback function.
  252. //
  253. // N.B. This function returns to the function that called out to user
  254. // mode and the KiCallUserMode function calls out to user mode.
  255. // Therefore, the stack layout must be consistent between the
  256. // two routines.
  257. //
  258. // Arguments:
  259. //
  260. // OutputBuffer - Supplies an optional pointer to an output buffer.
  261. //
  262. // OutputLength - Supplies the length of the output buffer.
  263. //
  264. // Status - Supplies the status value returned to the caller of the
  265. // callback function.
  266. //
  267. // Return Value:
  268. //
  269. // If the callback return cannot be executed, then an error status is
  270. // returned. Otherwise, the specified callback status is returned to
  271. // the caller of the callback function.
  272. //
  273. // N.B. This function returns to the function that called out to user
  274. // mode is a callout is currently active.
  275. //
  276. //--
  277. LEAF_ENTRY(NtCallbackReturn)
  278. GET_CURRENT_THREAD // get current thread address
  279. LDP t1, ThCallbackStack(v0) // get callback stack address
  280. beq t1, 10f // if eq, no callback stack present
  281. //
  282. // Restore nonvolatile integer registers.
  283. //
  284. ldq s0, CuS0(t1) // restore integer registers s0 - s5
  285. ldq s1, CuS1(t1) //
  286. ldq s2, CuS2(t1) //
  287. ldq s3, CuS3(t1) //
  288. ldq s4, CuS4(t1) //
  289. ldq s5, CuS5(t1) //
  290. ldq fp, CuFP(t1) // restore FP
  291. //
  292. // Restore nonvolatile floating registers.
  293. //
  294. ldt f2, CuF2(t1) // restore floating registers f2 - f9
  295. ldt f3, CuF3(t1) //
  296. ldt f4, CuF4(t1) //
  297. ldt f5, CuF5(t1) //
  298. ldt f6, CuF6(t1) //
  299. ldt f7, CuF7(t1) //
  300. ldt f8, CuF8(t1) //
  301. ldt f9, CuF9(t1) //
  302. //
  303. // Restore the trap frame and callback stack addresses, and store the output
  304. // buffer address and length.
  305. //
  306. LDP t2, CuTrFr(t1) // get previous trap frame address
  307. LDP t3, CuCbStk(t1) // get previous callback stack address
  308. LDP t4, CuA0(t1) // get address to store output address
  309. LDP t5, CuA1(t1) // get address to store output length
  310. LDP t6, CuTrFir(t1) // get old trap frame PC
  311. STP t2, ThTrapFrame(v0) // restore trap frame address
  312. STP t3, ThCallbackStack(v0) // restore callback stack address
  313. STP a0, 0(t4) // store output buffer address
  314. STP a1, 0(t5) // store output buffer length
  315. stq t6, TrFir(t2) // restore old trap frame PC
  316. //
  317. // **** this is the place where the current stack could be trimmed back.
  318. //
  319. // Restore initial stack pointer, trim stackback to callback frame,
  320. // deallocate callback stack frame, and return to callback caller.
  321. //
  322. LDP a0, CuInStk(t1) // get previous initial stack
  323. STP a0, ThInitialStack(v0) //
  324. SET_INITIAL_KERNEL_STACK // set initial kernel stack address
  325. bis t1, zero, sp // trim stack callback frame
  326. bis a2, zero, v0 // set callback service status
  327. ldq ra, CuRa(sp) // restore return address
  328. lda sp, CuFrameLength(sp) // deallocate stack frame
  329. ret zero, (ra) // return
  330. //
  331. // No callback is currently active.
  332. //
  333. 10: ldil v0, STATUS_NO_CALLBACK_ACTIVE // set service status
  334. ret zero, (ra) // return
  335. .end NtCallbackReturn
  336.