1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
|
title "Call Out to User Mode"
;++
;
; Copyright (c) 1994 Microsoft Corporation
;
; Module Name:
;
; callout.asm
;
; Abstract:
;
; This module implements the code necessary to call out from kernel
; mode to user mode.
;
; Author:
;
; David N. Cutler (davec) 1-Nov-1994
;
; Environment:
;
; Kernel mode only.
;
; Revision History:
;
;--
.386p
.xlist
include ks386.inc
include i386\kimacro.inc
include callconv.inc
.list
extrn _KiServiceExit:PROC
extrn _KeUserCallbackDispatcher:DWORD
EXTRNP _MmGrowKernelStack,1
_TEXT SEGMENT DWORD PUBLIC 'CODE'
ASSUME DS:FLAT, ES:FLAT, SS:FLAT, FS:NOTHING, GS:NOTHING
page ,132
subttl "Call User Mode Function"
;++
;
; NTSTATUS
; KiCallUserMode (
; IN PVOID *Outputbuffer,
; IN PULONG OutputLength
; )
;
; Routine Description:
;
; This function calls a user mode function from kernel mode.
;
; N.B. This function calls out to user mode and the NtCallbackReturn
; function returns back to the caller of this function. Therefore,
; the stack layout must be consistent between the two routines.
;
; Arguments:
;
; OutputBuffer - Supplies a pointer to the variable that receivies
; the address of the output buffer.
;
; OutputLength - Supplies a pointer to a variable that receives
; the length of the output buffer.
;
; Return Value:
;
; The final status of the call out function is returned as the status
; of the function.
;
; N.B. This function does not return to its caller. A return to the
; caller is executed when a NtCallbackReturn system service is
; executed.
;
; N.B. This function does return to its caller if a kernel stack
; expansion is required and the attempted expansion fails.
;
;--
;
; To support the debugger, the callback stack frame is now defined in i386.h.
; If the stack frame is changed, i386.h must be updated and geni386
; rebuilt and run, then rebuild this file and ntos\kd.
;
; The FPO record below must also be updated to correctly represent
; the stack frame.
;
cPublicProc _KiCallUserMode, 2
.FPO (3, 2, 4, 4, 0, 0)
;
; Save nonvolatile registers.
;
push ebp ; save nonvolatile registers
push ebx ;
push esi ;
push edi ;
;
; Check if sufficient room is available on the kernel stack for another
; system call.
;
mov ebx,PCR[PcPrcbData + PbCurrentThread] ; get current thread address
lea eax,[esp]-KERNEL_LARGE_STACK_COMMIT ; compute bottom address
cmp eax,[ebx]+ThStackLimit ; check if limit exceeded
jae short Kcb10 ; if ae, limit not exceeded
stdCall _MmGrowKernelStack,<esp> ; attempt to grow kernel stack
or eax, eax ; check for successful completion
jne Kcb20 ; if ne, attempt to grow failed
mov eax, [ebx].ThStackLimit ; get new stack limit
mov PCR[PcStackLimit], eax ; set new stack limit
;
; Get the address of the current thread and save the previous trap frame
; and calback stack addresses in the current frame. Also save the new
; callback stack address in the thread object.
;
Kcb10: push [ebx].ThCallbackStack ; save callback stack address
mov edx,[ebx].ThTrapFrame ; get current trap frame address
push edx ; save trap frame address
mov esi,[ebx].ThInitialStack ; get initial stack address
push esi ; save initial stack address
mov [ebx].ThCallbackStack,esp ; save callback stack address
KcbPrologEnd: ; help for the debugger
;
; Copy the numeric save area from the previous save area to the new save
; area and establish a new initial kernel stack.
;
mov edi,esp ; set new initial stack address
sub esp,NPX_FRAME_LENGTH ; compute destination NPX save area
sub esi,NPX_FRAME_LENGTH ; compute source NPX save area
cli ; disable interrupts
mov ecx,[esi].FpControlWord ; copy NPX state to new frame
mov [esp].FpControlWord,ecx ;
mov ecx,[esi].FpStatusWord ;
mov [esp].FpStatusWord,ecx ;
mov ecx,[esi].FpTagWord ;
mov [esp].FpTagWord,ecx ;
mov ecx,[esi].FpCr0NpxState ;
mov [esp].FpCr0NpxState,ecx ;
mov esi,PCR[PcTss] ; get address of task switch segment
mov [ebx].ThInitialStack,edi ; reset initial stack address
mov PCR[PcInitialStack],esp ; set stack check base address
sub esp,TsV86Gs - TsHardwareSegSs ; bias for missing V86 fields
mov [esi].TssEsp0,esp ; set kernel entry stack address
;
; Construct a trap frame to facilitate the transfer into user mode via
; the standard system call exit.
;
sub esp,TsHardwareSegSs + 4 ; allocate trap frame
mov ebp,esp ; set address of trap frame
mov ecx,(TsHardwareSegSs - TsSegFs + 4) / 4; set repeat count
lea edi,[esp].TsSegFs ; set destination address
lea esi,[edx].TsSegFs ; set source address
rep movsd ; copy trap information
test byte ptr [ebx]+ThDebugActive, -1 ; Do we need to restore Debug reg?
jnz short Kcb18 ; Yes, go save them.
Kcb15: mov eax,_KeUserCallbackDispatcher ; st address of callback dispatchr
mov [esp].TsEip,eax ;
mov eax,PCR[PcExceptionList] ; get current exception list
mov [esp].TsExceptionList,eax ; set previous exception list
mov eax,[edx].TsPreviousPreviousMode ; get previous mode
mov [esp].TsPreviousPreviousMode,eax ; set previous mode
sti ; enable interrupts
SET_DEBUG_DATA ; set system call debug data for exit
jmp _KiServiceExit ; exit through service dispatch
Kcb18:
mov ecx,(TsDr7 - TsDr0 + 4) / 4; set repeat count
lea edi,[esp].TsDr0 ; set destination address
lea esi,[edx].TsDr0 ; set source address
rep movsd ; copy trap information
jmp short Kcb15
;
; An attempt to grow the kernel stack failed.
;
Kcb20: pop edi ; restore nonvolitile register
pop esi ;
pop ebx ;
pop ebp ;
stdRET _KiCallUserMode
stdENDP _KiCallUserMode
page ,132
subttl "Switch Kernel Stack"
;++
;
; PVOID
; KeSwitchKernelStack (
; IN PVOID StackBase,
; IN PVOID StackLimit
; )
;
; Routine Description:
;
; This function switches to the specified large kernel stack.
;
; N.B. This function can ONLY be called when there are no variables
; in the stack that refer to other variables in the stack, i.e.,
; there are no pointers into the stack.
;
; Arguments:
;
; StackBase (esp + 4) - Supplies a pointer to the base of the new kernel
; stack.
;
; StackLimit (esp + 8) - Suplies a pointer to the limit of the new kernel
; stack.
;
; Return Value:
;
; The old kernel stack is returned as the function value.
;
;--
SsStkBs equ 4 ; new kernel stack base address
SsStkLm equ 8 ; new kernel stack limit address
cPublicProc _KeSwitchKernelStack, 2
;
; Save the address of the new stack and copy the old stack to the new
; stack.
;
push esi ; save string move registers
push edi ;
mov edx,PCR[PcPrcbData + PbCurrentThread] ; get current thread address
mov edi,[esp]+SsStkBs + 8 ; get new kernel stack base address
mov ecx,[edx].ThStackBase ; get current stack base address
sub ebp,ecx ; relocate the callers frame pointer
add ebp,edi ;
mov eax,[edx].ThTrapFrame ; relocate the current trap frame address
sub eax,ecx ;
add eax,edi ;
mov [edx].ThTrapFrame,eax ;
sub ecx,esp ; compute length of copy
sub edi,ecx ; set destination address of copy
mov esi,esp ; set source address of copy
push edi ; save new stack pointer address
rep movsb ; copy old stack to new stack
pop edi ; restore new stack pointer address
;
; Switch to the new kernel stack and return the address of the old kernel
; stack.
;
mov eax,[edx].ThStackBase ; get old kernel stack base address
mov ecx,[esp]+SsStkBs + 8 ; get new kernel stack base address
mov esi,[esp]+SsStkLm + 8 ; get new kernel stack limit address
cli ; disable interrupts
mov [edx].ThStackBase,ecx ; set new kernel stack base address
mov [edx].ThStackLimit,esi ; set new kernel stack limit address
mov byte ptr [edx].ThLargeStack, 1 ; set large stack TRUE
mov [edx].ThInitialStack,ecx ; set new initial stack address
sub ecx,NPX_FRAME_lENGTH ; compute NPX save area address
mov PCR[PcInitialStack],ecx ; set stack check base address
mov PCR[PcStackLimit],esi ; set stack check limit address
mov edx,PCR[PcTss] ; get address of task switch segment
sub ecx,TsV86Gs - TsHardwareSegSs ; bias for missing V86 fields
mov [edx].TssEsp0,ecx ; set kernel entry stack address
mov esp,edi ; set new stack pointer address
sti ;
pop edi ; restore string move registers
pop esi ;
stdRET _KeSwitchKernelStack
stdENDP _KeSwitchKernelStack
page ,132
subttl "Get User Mode Stack Address"
;++
;
; PULONG
; KiGetUserModeStackAddress (
; VOID
; )
;
; Routine Description:
;
; This function returns the address of the user stack address in the
; current trap frame.
;
; Arguments:
;
; None.
;
; Return Value:
;
; The address of the user stack address.
;
;--
cPublicProc _KiGetUserModeStackAddress, 0
mov eax,PCR[PcPrcbData + PbCurrentThread] ; get current thread address
mov eax,[eax].ThTrapFrame ; get current trap frame address
lea eax,[eax].TsHardwareEsp ; get address of stack address
stdRET _KiGetUserModeStackAddress
stdENDP _KiGetUserModeStackAddress
page ,132
subttl "Return from User Mode Callback"
;++
;
; NTSTATUS
; NtCallbackReturn (
; IN PVOID OutputBuffer OPTIONAL,
; IN ULONG OutputLength,
; IN NTSTATUS Status
; )
;
; Routine Description:
;
; This function returns from a user mode callout to the kernel
; mode caller of the user mode callback function.
;
; N.B. This function returns to the function that called out to user
; mode and the KiCallUserMode function calls out to user mode.
; Therefore, the stack layout must be consistent between the
; two routines.
;
; Arguments:
;
; OutputBuffer - Supplies an optional pointer to an output buffer.
;
; OutputLength - Supplies the length of the output buffer.
;
; Status - Supplies the status value returned to the caller of the
; callback function.
;
; Return Value:
;
; If the callback return cannot be executed, then an error status is
; returned. Otherwise, the specified callback status is returned to
; the caller of the callback function.
;
; N.B. This function returns to the function that called out to user
; mode is a callout is currently active.
;
;--
cPublicProc _NtCallbackReturn, 3
mov eax,PCR[PcPrcbData + PbCurrentThread] ; get current thread address
mov ecx,[eax].ThCallbackStack ; get callback stack address
jecxz short CbExit ; if zero, no callback stack present
;
; Restore the current exception list from the saved exception list in the
; current trap frame, restore the trap frame and callback stack addresses,
; store the output buffer address and length, and set the service status.
;
mov ebx,[eax].ThTrapFrame ; get current trap frame address
mov edx,[ebx].TsExceptionList ; get saved exception list address
mov PCR[PcExceptionList],edx ; restore exception list address
mov edi,[esp] + 4 ; get output buffer address
mov esi,[esp] + 8 ; get output buffer length
mov ebp,[esp] + 12 ; get callout service status
mov ebx,[ecx].CuOutBf ; get address to store output buffer
mov [ebx],edi ; store output buffer address
mov ebx,[ecx].CuOutLn ; get address to store output length
mov [ebx],esi ; store output buffer length
cli ; disable interrupt
mov esi,PCR[PcInitialStack] ; get source NPX save area address
mov esp,ecx ; trim stack back to callback frame
pop ecx ; get previous initial stack address
mov [eax].ThInitialStack,ecx ; restore initial stack address
sub ecx,NPX_FRAME_LENGTH ; compute destination NPX save area
mov edx,[esi].FpControlWord ; copy NPX state to previous frame
mov [ecx].FpControlWord,edx ;
mov edx,[esi].FpStatusWord ;
mov [ecx].FpStatusWord,edx ;
mov edx,[esi].FpTagWord ;
mov [ecx].FpTagWord,edx ;
mov edx,[esi].FpCr0NpxState ;
mov [ecx].FpCr0NpxState,edx ;
mov edx,PCR[PcTss] ; get address of task switch segment
mov PCR[PcInitialStack],ecx ; restore stack check base address
sub ecx,TsV86Gs - TsHardwareSegSs ; bias for missing V86 fields
mov [edx].TssEsp0,ecx ; restore kernel entry stack address
sti ; enable interrupts
pop [eax].ThTrapFrame ; restore current trap frame address
pop [eax].ThCallbackStack ; restore callback stack address
mov eax,ebp ; set callback service status
;
; Restore nonvolatile registers, clean call parameters from stack, and
; return to callback caller.
;
pop edi ; restore nonvolatile registers
pop esi ;
pop ebx ;
pop ebp ;
pop edx ; save return address
add esp,8 ; remove parameters from stack
jmp edx ; return to callback caller
;
; No callback is currently active.
;
CbExit: mov eax,STATUS_NO_CALLBACK_ACTIVE ; set service status
stdRET _NtCallBackReturn
stdENDP _NtCallbackReturn
_TEXT ends
end
|