update for HEAD-2003091401
[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 typedef struct
50 {
51   PVOID BaseAddress;
52   LIST_ENTRY ListEntry;
53 } NTW32CALL_CALLBACK_STACK, *PNTW32CALL_CALLBACK_STACK;
54
55 static LIST_ENTRY CallbackStackListHead;
56
57 /* FUNCTIONS ***************************************************************/
58
59 VOID
60 PsInitialiseW32Call(VOID)
61 {
62   InitializeListHead(&CallbackStackListHead);
63 }
64
65 NTSTATUS STDCALL
66 NtCallbackReturn (PVOID         Result,
67                   ULONG         ResultLength,
68                   NTSTATUS      Status)
69 {
70   PULONG OldStack;
71   PETHREAD Thread;
72   PNTSTATUS CallbackStatus;
73   PULONG CallerResultLength;
74   PVOID* CallerResult;
75   PVOID InitialStack;
76   PVOID StackBase;
77   ULONG StackLimit;
78   KIRQL oldIrql;
79   PNTW32CALL_SAVED_STATE State;
80   PKTRAP_FRAME SavedTrapFrame;
81   PVOID SavedCallbackStack;
82
83   Thread = PsGetCurrentThread();
84   if (Thread->Tcb.CallbackStack == NULL)
85     {
86       return(STATUS_NO_CALLBACK_ACTIVE);
87     }
88
89   OldStack = (PULONG)Thread->Tcb.CallbackStack;
90
91   /*
92    * Get the values that NtW32Call left on the inactive stack for us.
93    */
94   State = (PNTW32CALL_SAVED_STATE)OldStack[0];  
95   CallbackStatus = State->CallbackStatus;
96   CallerResultLength = State->CallerResultLength;
97   CallerResult = State->CallerResult;
98   InitialStack = State->SavedInitialStack;
99   StackBase = State->SavedStackBase;
100   StackLimit = State->SavedStackLimit;
101   SavedTrapFrame = State->SavedTrapFrame;
102   SavedCallbackStack = State->SavedCallbackStack;
103   
104   /*
105    * Copy the callback status and the callback result to NtW32Call
106    */
107   *CallbackStatus = Status;
108   if (CallerResult != NULL && CallerResultLength != NULL)
109     {
110       if (Result == NULL)
111         {
112           *CallerResultLength = 0;
113         }
114       else
115         {
116           *CallerResultLength = min(ResultLength, *CallerResultLength);
117           memcpy(*CallerResult, Result, *CallerResultLength);
118         }
119     }
120
121   /*
122    * Restore the old stack.
123    */
124   KeRaiseIrql(HIGH_LEVEL, &oldIrql);
125   Thread->Tcb.InitialStack = InitialStack;
126   Thread->Tcb.StackBase = StackBase;
127   Thread->Tcb.StackLimit = StackLimit;
128   Thread->Tcb.TrapFrame = SavedTrapFrame;
129   Thread->Tcb.CallbackStack = SavedCallbackStack;
130   KeGetCurrentKPCR()->TSS->Esp0 = (ULONG)Thread->Tcb.InitialStack;
131   KeStackSwitchAndRet((PVOID)(OldStack + 1));
132
133   /* Should never return. */
134   KEBUGCHECK(0);
135   return(STATUS_UNSUCCESSFUL);
136 }
137
138 VOID STATIC
139 PsFreeCallbackStackPage(PVOID Context, MEMORY_AREA* MemoryArea, PVOID Address, 
140                         PHYSICAL_ADDRESS PhysAddr, SWAPENTRY SwapEntry, 
141                         BOOLEAN Dirty)
142 {
143   assert(SwapEntry == 0);
144   if (PhysAddr.QuadPart  != 0)
145     {
146       MmReleasePageMemoryConsumer(MC_NPPOOL, PhysAddr);
147     }
148 }
149
150 VOID STATIC
151 PsFreeCallbackStack(PVOID StackLimit)
152 {
153   MmLockAddressSpace(MmGetKernelAddressSpace());
154   MmFreeMemoryArea(MmGetKernelAddressSpace(),
155                    StackLimit,
156                    MM_STACK_SIZE,
157                    PsFreeCallbackStackPage,
158                    NULL);
159   MmUnlockAddressSpace(MmGetKernelAddressSpace());
160 }
161
162 VOID
163 PsFreeCallbackStacks(VOID)
164 {
165   PLIST_ENTRY CurrentListEntry;
166   PNTW32CALL_CALLBACK_STACK Current;
167
168   while (!IsListEmpty(&CallbackStackListHead))
169     {
170       CurrentListEntry = RemoveHeadList(&CallbackStackListHead);
171       Current = CONTAINING_RECORD(CurrentListEntry, NTW32CALL_CALLBACK_STACK,
172                                   ListEntry);
173       PsFreeCallbackStack(Current->BaseAddress);
174       ExFreePool(Current);
175     }
176 }
177
178 PVOID STATIC
179 PsAllocateCallbackStack(ULONG StackSize)
180 {
181   PVOID KernelStack = NULL;
182   NTSTATUS Status;
183   PMEMORY_AREA StackArea;
184   ULONG i;
185
186   StackSize = PAGE_ROUND_UP(StackSize);
187   MmLockAddressSpace(MmGetKernelAddressSpace());
188   Status = MmCreateMemoryArea(NULL,
189                               MmGetKernelAddressSpace(),
190                               MEMORY_AREA_KERNEL_STACK,
191                               &KernelStack,
192                               StackSize,
193                               0,
194                               &StackArea,
195                               FALSE,
196                               FALSE);
197   MmUnlockAddressSpace(MmGetKernelAddressSpace());
198   if (!NT_SUCCESS(Status))
199     {
200       DPRINT("Failed to create thread stack\n");
201       return(NULL);
202     }
203   for (i = 0; i < (StackSize / PAGE_SIZE); i++)
204     {
205       PHYSICAL_ADDRESS Page;
206       Status = MmRequestPageMemoryConsumer(MC_NPPOOL, TRUE, &Page);
207       if (!NT_SUCCESS(Status))
208         {
209           return(NULL);
210         }
211       Status = MmCreateVirtualMapping(NULL,
212                                       KernelStack + (i * PAGE_SIZE),
213                                       PAGE_EXECUTE_READWRITE,
214                                       Page,
215                                       TRUE);
216     }
217   return(KernelStack);
218 }
219
220 NTSTATUS STDCALL
221 NtW32Call (IN ULONG RoutineIndex,
222            IN PVOID Argument,
223            IN ULONG ArgumentLength,
224            OUT PVOID* Result OPTIONAL,
225            OUT PULONG ResultLength OPTIONAL)
226 {
227   PETHREAD Thread;
228   PVOID NewStack;
229   ULONG StackSize;
230   PKTRAP_FRAME NewFrame;
231   PULONG UserEsp;
232   KIRQL oldIrql;
233   NTSTATUS CallbackStatus;
234   NTW32CALL_SAVED_STATE SavedState;
235   PNTW32CALL_CALLBACK_STACK AssignedStack;
236
237   DPRINT("NtW32Call(RoutineIndex %d, Argument %X, ArgumentLength %d)\n",
238           RoutineIndex, Argument, ArgumentLength);
239
240   Thread = PsGetCurrentThread();
241
242   /* Set up the new kernel and user environment. */
243   StackSize = (ULONG)(Thread->Tcb.StackBase - Thread->Tcb.StackLimit);  
244   if (IsListEmpty(&CallbackStackListHead))
245     {
246       NewStack = PsAllocateCallbackStack(StackSize);
247       AssignedStack = ExAllocatePool(NonPagedPool,
248                                      sizeof(NTW32CALL_CALLBACK_STACK));
249       AssignedStack->BaseAddress = NewStack;
250     }
251   else
252     {
253       PLIST_ENTRY StackEntry;      
254
255       StackEntry = RemoveHeadList(&CallbackStackListHead);
256       AssignedStack = CONTAINING_RECORD(StackEntry, NTW32CALL_CALLBACK_STACK, 
257                                         ListEntry);
258       NewStack = AssignedStack->BaseAddress;
259     }
260   /* FIXME: Need to check whether we were interrupted from v86 mode. */
261   memcpy(NewStack + StackSize - sizeof(KTRAP_FRAME), Thread->Tcb.TrapFrame,
262          sizeof(KTRAP_FRAME) - (4 * sizeof(DWORD)));
263   NewFrame = (PKTRAP_FRAME)(NewStack + StackSize - sizeof(KTRAP_FRAME));
264   NewFrame->Esp -= (ArgumentLength + (4 * sizeof(ULONG))); 
265   NewFrame->Eip = (ULONG)LdrpGetSystemDllCallbackDispatcher();
266   UserEsp = (PULONG)NewFrame->Esp;
267   UserEsp[0] = 0;     /* Return address. */
268   UserEsp[1] = RoutineIndex;
269   UserEsp[2] = (ULONG)&UserEsp[4];
270   UserEsp[3] = ArgumentLength;
271   memcpy((PVOID)&UserEsp[4], Argument, ArgumentLength);
272
273   /* Switch to the new environment and return to user-mode. */
274   KeRaiseIrql(HIGH_LEVEL, &oldIrql);
275   SavedState.SavedStackLimit = Thread->Tcb.StackLimit;
276   SavedState.SavedStackBase = Thread->Tcb.StackBase;
277   SavedState.SavedInitialStack = Thread->Tcb.InitialStack;
278   SavedState.CallerResult = Result;
279   SavedState.CallerResultLength = ResultLength;
280   SavedState.CallbackStatus = &CallbackStatus;
281   SavedState.SavedTrapFrame = Thread->Tcb.TrapFrame;
282   SavedState.SavedCallbackStack = Thread->Tcb.CallbackStack;
283   Thread->Tcb.InitialStack = Thread->Tcb.StackBase = NewStack + StackSize;
284   Thread->Tcb.StackLimit = (ULONG)NewStack;
285   Thread->Tcb.KernelStack = NewStack + StackSize - sizeof(KTRAP_FRAME);
286   KeGetCurrentKPCR()->TSS->Esp0 = (ULONG)Thread->Tcb.InitialStack;
287   KePushAndStackSwitchAndSysRet((ULONG)&SavedState, Thread->Tcb.KernelStack);
288
289   /* 
290    * The callback return will have already restored most of the state we 
291    * modified.
292    */
293   KeLowerIrql(PASSIVE_LEVEL);
294   InsertTailList(&CallbackStackListHead, &AssignedStack->ListEntry);
295   return(CallbackStatus);
296
297
298 /* EOF */