:pserver:cvsanon@mok.lvcm.com:/CVS/ReactOS reactos
[reactos.git] / ntoskrnl / ps / w32call.c
1 /* $Id$
2  *
3  * COPYRIGHT:              See COPYING in the top level directory
4  * PROJECT:                ReactOS kernel
5  * FILE:                   ntoskrnl/ps/thread.c
6  * PURPOSE:                Thread managment
7  * PROGRAMMER:             David Welch (welch@mcmail.com)
8  * REVISION HISTORY: 
9  *               23/06/98: Created
10  *               12/10/99: Phillip Susi:  Thread priorities, and APC work
11  */
12
13 /*
14  * NOTE:
15  * 
16  * All of the routines that manipulate the thread queue synchronize on
17  * a single spinlock
18  * 
19  */
20
21 /* INCLUDES ****************************************************************/
22
23 #include <ddk/ntddk.h>
24 #include <internal/ke.h>
25 #include <internal/ob.h>
26 #include <internal/ps.h>
27 #include <internal/ob.h>
28 #include <internal/pool.h>
29 #include <ntos/minmax.h>
30 #include <internal/ldr.h>
31
32 #define NDEBUG
33 #include <internal/debug.h>
34
35 /* TYPES *******************************************************************/
36
37 typedef struct _NTW32CALL_SAVED_STATE
38 {
39   ULONG SavedStackLimit;
40   PVOID SavedStackBase;
41   PVOID SavedInitialStack;
42   PVOID CallerResult;
43   PULONG CallerResultLength;
44   PNTSTATUS CallbackStatus;
45   PKTRAP_FRAME SavedTrapFrame;
46   PVOID SavedCallbackStack;
47 } NTW32CALL_SAVED_STATE, *PNTW32CALL_SAVED_STATE;
48
49 /* FUNCTIONS ***************************************************************/
50
51 NTSTATUS STDCALL
52 NtCallbackReturn (PVOID         Result,
53                   ULONG         ResultLength,
54                   NTSTATUS      Status)
55 {
56   PULONG OldStack;
57   PETHREAD Thread;
58   PNTSTATUS CallbackStatus;
59   PULONG CallerResultLength;
60   PVOID* CallerResult;
61   PVOID InitialStack;
62   PVOID StackBase;
63   ULONG StackLimit;
64   KIRQL oldIrql;
65   PNTW32CALL_SAVED_STATE State;
66   PKTRAP_FRAME SavedTrapFrame;
67   PVOID SavedCallbackStack;
68
69   Thread = PsGetCurrentThread();
70   if (Thread->Tcb.CallbackStack == NULL)
71     {
72       return(STATUS_NO_CALLBACK_ACTIVE);
73     }
74
75   OldStack = (PULONG)Thread->Tcb.CallbackStack;
76
77   /*
78    * Get the values that NtW32Call left on the inactive stack for us.
79    */
80   State = (PNTW32CALL_SAVED_STATE)OldStack[0];  
81   CallbackStatus = State->CallbackStatus;
82   CallerResultLength = State->CallerResultLength;
83   CallerResult = State->CallerResult;
84   InitialStack = State->SavedInitialStack;
85   StackBase = State->SavedStackBase;
86   StackLimit = State->SavedStackLimit;
87   SavedTrapFrame = State->SavedTrapFrame;
88   SavedCallbackStack = State->SavedCallbackStack;
89   
90   /*
91    * Copy the callback status and the callback result to NtW32Call
92    */
93   *CallbackStatus = Status;
94   if (CallerResult != NULL && CallerResultLength != NULL)
95     {
96       if (Result == NULL)
97         {
98           *CallerResultLength = 0;
99         }
100       else
101         {
102           *CallerResultLength = min(ResultLength, *CallerResultLength);
103           memcpy(*CallerResult, Result, *CallerResultLength);
104         }
105     }
106
107   /*
108    * Restore the old stack.
109    */
110   KeRaiseIrql(HIGH_LEVEL, &oldIrql);
111   Thread->Tcb.InitialStack = InitialStack;
112   Thread->Tcb.StackBase = StackBase;
113   Thread->Tcb.StackLimit = StackLimit;
114   Thread->Tcb.TrapFrame = SavedTrapFrame;
115   Thread->Tcb.CallbackStack = SavedCallbackStack;
116   KeGetCurrentKPCR()->TSS->Esp0 = (ULONG)Thread->Tcb.InitialStack;
117   KeStackSwitchAndRet((PVOID)(OldStack + 1));
118
119   /* Should never return. */
120   KeBugCheck(0);
121   return(STATUS_UNSUCCESSFUL);
122 }
123
124 VOID STATIC
125 PsFreeCallbackStackPage(PVOID Context, MEMORY_AREA* MemoryArea, PVOID Address, 
126                         PHYSICAL_ADDRESS PhysAddr, SWAPENTRY SwapEntry, 
127                         BOOLEAN Dirty)
128 {
129   assert(SwapEntry == 0);
130   if (PhysAddr.QuadPart  != 0)
131     {
132       MmReleasePageMemoryConsumer(MC_NPPOOL, PhysAddr);
133     }
134 }
135
136 VOID STATIC
137 PsFreeCallbackStack(PVOID StackLimit)
138 {
139   MmLockAddressSpace(MmGetKernelAddressSpace());
140   MmFreeMemoryArea(MmGetKernelAddressSpace(),
141                    StackLimit,
142                    MM_STACK_SIZE,
143                    PsFreeCallbackStackPage,
144                    NULL);
145   MmUnlockAddressSpace(MmGetKernelAddressSpace());
146 }
147
148 PVOID STATIC
149 PsAllocateCallbackStack(ULONG StackSize)
150 {
151   PVOID KernelStack = NULL;
152   NTSTATUS Status;
153   PMEMORY_AREA StackArea;
154   ULONG i;
155
156   StackSize = PAGE_ROUND_UP(StackSize);
157   MmLockAddressSpace(MmGetKernelAddressSpace());
158   Status = MmCreateMemoryArea(NULL,
159                               MmGetKernelAddressSpace(),
160                               MEMORY_AREA_KERNEL_STACK,
161                               &KernelStack,
162                               StackSize,
163                               0,
164                               &StackArea,
165                               FALSE);
166   MmUnlockAddressSpace(MmGetKernelAddressSpace());  
167   if (!NT_SUCCESS(Status))
168     {
169       DPRINT("Failed to create thread stack\n");
170       return(NULL);
171     }
172   for (i = 0; i < (StackSize / PAGE_SIZE); i++)
173     {
174       PHYSICAL_ADDRESS Page;
175       Status = MmRequestPageMemoryConsumer(MC_NPPOOL, TRUE, &Page);
176       if (!NT_SUCCESS(Status))
177         {
178           return(NULL);
179         }
180       Status = MmCreateVirtualMapping(NULL,
181                                       KernelStack + (i * PAGE_SIZE),
182                                       PAGE_EXECUTE_READWRITE,
183                                       Page,
184                                       TRUE);
185     }
186   return(KernelStack);
187 }
188
189 NTSTATUS STDCALL
190 NtW32Call (IN ULONG RoutineIndex,
191            IN PVOID Argument,
192            IN ULONG ArgumentLength,
193            OUT PVOID* Result OPTIONAL,
194            OUT PULONG ResultLength OPTIONAL)
195 {
196   PETHREAD Thread;
197   PVOID NewStack;
198   ULONG StackSize;
199   PKTRAP_FRAME NewFrame;
200   PULONG UserEsp;
201   KIRQL oldIrql;
202   NTSTATUS CallbackStatus;
203   NTW32CALL_SAVED_STATE SavedState;
204
205   DPRINT("NtW32Call(RoutineIndex %d, Argument %X, ArgumentLength %d)\n",
206           RoutineIndex, Argument, ArgumentLength);
207
208   Thread = PsGetCurrentThread();
209
210   /* Set up the new kernel and user environment. */
211   StackSize = (ULONG)(Thread->Tcb.StackBase - Thread->Tcb.StackLimit);  
212   NewStack = PsAllocateCallbackStack(StackSize);
213   /* FIXME: Need to check whether we were interrupted from v86 mode. */
214   memcpy(NewStack + StackSize - sizeof(KTRAP_FRAME), Thread->Tcb.TrapFrame,
215          sizeof(KTRAP_FRAME) - (4 * sizeof(DWORD)));
216   NewFrame = (PKTRAP_FRAME)(NewStack + StackSize - sizeof(KTRAP_FRAME));
217   NewFrame->Esp -= (ArgumentLength + (4 * sizeof(ULONG))); 
218   NewFrame->Eip = (ULONG)LdrpGetSystemDllCallbackDispatcher();
219   UserEsp = (PULONG)NewFrame->Esp;
220   UserEsp[0] = 0;     /* Return address. */
221   UserEsp[1] = RoutineIndex;
222   UserEsp[2] = (ULONG)&UserEsp[4];
223   UserEsp[3] = ArgumentLength;
224   memcpy((PVOID)&UserEsp[4], Argument, ArgumentLength);
225
226   /* Switch to the new environment and return to user-mode. */
227   KeRaiseIrql(HIGH_LEVEL, &oldIrql);
228   SavedState.SavedStackLimit = Thread->Tcb.StackLimit;
229   SavedState.SavedStackBase = Thread->Tcb.StackBase;
230   SavedState.SavedInitialStack = Thread->Tcb.InitialStack;
231   SavedState.CallerResult = Result;
232   SavedState.CallerResultLength = ResultLength;
233   SavedState.CallbackStatus = &CallbackStatus;
234   SavedState.SavedTrapFrame = Thread->Tcb.TrapFrame;
235   SavedState.SavedCallbackStack = Thread->Tcb.CallbackStack;
236   Thread->Tcb.InitialStack = Thread->Tcb.StackBase = NewStack + StackSize;
237   Thread->Tcb.StackLimit = (ULONG)NewStack;
238   Thread->Tcb.KernelStack = NewStack + StackSize - sizeof(KTRAP_FRAME);
239   KeGetCurrentKPCR()->TSS->Esp0 = (ULONG)Thread->Tcb.InitialStack;
240   KePushAndStackSwitchAndSysRet((ULONG)&SavedState, Thread->Tcb.KernelStack);
241
242   /* 
243    * The callback return will have already restored most of the state we 
244    * modified.
245    */
246   KeLowerIrql(PASSIVE_LEVEL);
247   PsFreeCallbackStack(NewStack);
248   return(CallbackStatus);
249
250
251 /* EOF */