Sync to trunk revision 63922.
[reactos.git] / ntoskrnl / ps / psmgr.c
1 /*
2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: ntoskrnl/ps/psmgr.c
5 * PURPOSE: Process Manager: Initialization Code
6 * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
7 */
8
9 /* INCLUDES ******************************************************************/
10
11 #include <ntoskrnl.h>
12 #define NDEBUG
13 #include <debug.h>
14
15 extern ULONG ExpInitializationPhase;
16 extern BOOLEAN SysThreadCreated;
17
18 PVOID KeUserPopEntrySListEnd;
19 PVOID KeUserPopEntrySListFault;
20 PVOID KeUserPopEntrySListResume;
21
22 GENERIC_MAPPING PspProcessMapping =
23 {
24 STANDARD_RIGHTS_READ | PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,
25 STANDARD_RIGHTS_WRITE | PROCESS_CREATE_PROCESS | PROCESS_CREATE_THREAD |
26 PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_DUP_HANDLE |
27 PROCESS_TERMINATE | PROCESS_SET_QUOTA | PROCESS_SET_INFORMATION |
28 PROCESS_SUSPEND_RESUME,
29 STANDARD_RIGHTS_EXECUTE | SYNCHRONIZE,
30 PROCESS_ALL_ACCESS
31 };
32
33 GENERIC_MAPPING PspThreadMapping =
34 {
35 STANDARD_RIGHTS_READ | THREAD_GET_CONTEXT | THREAD_QUERY_INFORMATION,
36 STANDARD_RIGHTS_WRITE | THREAD_TERMINATE | THREAD_SUSPEND_RESUME |
37 THREAD_ALERT | THREAD_SET_INFORMATION | THREAD_SET_CONTEXT,
38 STANDARD_RIGHTS_EXECUTE | SYNCHRONIZE,
39 THREAD_ALL_ACCESS
40 };
41
42 PVOID PspSystemDllBase;
43 PVOID PspSystemDllSection;
44 PVOID PspSystemDllEntryPoint;
45
46 UNICODE_STRING PsNtDllPathName =
47 RTL_CONSTANT_STRING(L"\\SystemRoot\\system32\\ntdll.dll");
48
49 PHANDLE_TABLE PspCidTable;
50
51 PEPROCESS PsInitialSystemProcess = NULL;
52 PEPROCESS PsIdleProcess = NULL;
53 HANDLE PspInitialSystemProcessHandle;
54
55 ULONG PsMinimumWorkingSet, PsMaximumWorkingSet;
56 struct
57 {
58 LIST_ENTRY List;
59 KGUARDED_MUTEX Lock;
60 } PspWorkingSetChangeHead;
61 ULONG PspDefaultPagedLimit, PspDefaultNonPagedLimit, PspDefaultPagefileLimit;
62 BOOLEAN PspDoingGiveBacks;
63
64 /* PRIVATE FUNCTIONS *********************************************************/
65
66 USHORT
67 NTAPI
68 INIT_FUNCTION
69 NameToOrdinal(IN PCHAR Name,
70 IN PVOID DllBase,
71 IN ULONG NumberOfNames,
72 IN PULONG NameTable,
73 IN PUSHORT OrdinalTable)
74 {
75 ULONG Mid;
76 LONG Ret;
77
78 /* Fail if no names */
79 if (!NumberOfNames) return -1;
80
81 /* Do binary search */
82 Mid = NumberOfNames >> 1;
83 Ret = strcmp(Name, (PCHAR)((ULONG_PTR)DllBase + NameTable[Mid]));
84
85 /* Check if we found it */
86 if (!Ret) return OrdinalTable[Mid];
87
88 /* We didn't. Check if we only had one name to check */
89 if (NumberOfNames == 1) return -1;
90
91 /* Check if we should look up or down */
92 if (Ret < 0)
93 {
94 /* Loop down */
95 NumberOfNames = Mid;
96 }
97 else
98 {
99 /* Look up, update tables */
100 NameTable = &NameTable[Mid + 1];
101 OrdinalTable = &OrdinalTable[Mid + 1];
102 NumberOfNames -= (Mid - 1);
103 }
104
105 /* Call us recursively */
106 return NameToOrdinal(Name, DllBase, NumberOfNames, NameTable, OrdinalTable);
107 }
108
109 NTSTATUS
110 NTAPI
111 INIT_FUNCTION
112 LookupEntryPoint(IN PVOID DllBase,
113 IN PCHAR Name,
114 OUT PVOID *EntryPoint)
115 {
116 PULONG NameTable;
117 PUSHORT OrdinalTable;
118 PIMAGE_EXPORT_DIRECTORY ExportDirectory;
119 ULONG ExportSize;
120 CHAR Buffer[64];
121 USHORT Ordinal;
122 PULONG ExportTable;
123
124 /* Get the export directory */
125 ExportDirectory = RtlImageDirectoryEntryToData(DllBase,
126 TRUE,
127 IMAGE_DIRECTORY_ENTRY_EXPORT,
128 &ExportSize);
129
130 /* Validate the name and copy it */
131 if (strlen(Name) > sizeof(Buffer) - 2) return STATUS_INVALID_PARAMETER;
132 strcpy(Buffer, Name);
133
134 /* Setup name tables */
135 NameTable = (PULONG)((ULONG_PTR)DllBase +
136 ExportDirectory->AddressOfNames);
137 OrdinalTable = (PUSHORT)((ULONG_PTR)DllBase +
138 ExportDirectory->AddressOfNameOrdinals);
139
140 /* Get the ordinal */
141 Ordinal = NameToOrdinal(Buffer,
142 DllBase,
143 ExportDirectory->NumberOfNames,
144 NameTable,
145 OrdinalTable);
146
147 /* Make sure the ordinal is valid */
148 if (Ordinal >= ExportDirectory->NumberOfFunctions)
149 {
150 /* It's not, fail */
151 return STATUS_PROCEDURE_NOT_FOUND;
152 }
153
154 /* Resolve the address and write it */
155 ExportTable = (PULONG)((ULONG_PTR)DllBase +
156 ExportDirectory->AddressOfFunctions);
157 *EntryPoint = (PVOID)((ULONG_PTR)DllBase + ExportTable[Ordinal]);
158 return STATUS_SUCCESS;
159 }
160
161 NTSTATUS
162 NTAPI
163 INIT_FUNCTION
164 PspLookupSystemDllEntryPoint(IN PCHAR Name,
165 IN PVOID *EntryPoint)
166 {
167 /* Call the LDR Routine */
168 return LookupEntryPoint(PspSystemDllBase, Name, EntryPoint);
169 }
170
171 NTSTATUS
172 NTAPI
173 INIT_FUNCTION
174 PspLookupKernelUserEntryPoints(VOID)
175 {
176 NTSTATUS Status;
177
178 /* Get user-mode APC trampoline */
179 Status = PspLookupSystemDllEntryPoint("KiUserApcDispatcher",
180 &KeUserApcDispatcher);
181 if (!NT_SUCCESS(Status)) return Status;
182
183 /* Get user-mode exception dispatcher */
184 Status = PspLookupSystemDllEntryPoint("KiUserExceptionDispatcher",
185 &KeUserExceptionDispatcher);
186 if (!NT_SUCCESS(Status)) return Status;
187
188 /* Get user-mode callback dispatcher */
189 Status = PspLookupSystemDllEntryPoint("KiUserCallbackDispatcher",
190 &KeUserCallbackDispatcher);
191 if (!NT_SUCCESS(Status)) return Status;
192
193 /* Get user-mode exception raise trampoline */
194 Status = PspLookupSystemDllEntryPoint("KiRaiseUserExceptionDispatcher",
195 &KeRaiseUserExceptionDispatcher);
196 if (!NT_SUCCESS(Status)) return Status;
197
198 /* Get user-mode SLIST exception functions for page fault rollback race hack */
199 Status = PspLookupSystemDllEntryPoint("ExpInterlockedPopEntrySListEnd",
200 &KeUserPopEntrySListEnd);
201 if (!NT_SUCCESS(Status)) { DPRINT1("this not found\n"); return Status; }
202 Status = PspLookupSystemDllEntryPoint("ExpInterlockedPopEntrySListFault",
203 &KeUserPopEntrySListFault);
204 if (!NT_SUCCESS(Status)) { DPRINT1("this not found\n"); return Status; }
205 Status = PspLookupSystemDllEntryPoint("ExpInterlockedPopEntrySListResume",
206 &KeUserPopEntrySListResume);
207 if (!NT_SUCCESS(Status)) { DPRINT1("this not found\n"); return Status; }
208
209 /* On x86, there are multiple ways to do a system call, find the right stubs */
210 #if defined(_X86_)
211 /* Check if this is a machine that supports SYSENTER */
212 if (KeFeatureBits & KF_FAST_SYSCALL)
213 {
214 /* Get user-mode sysenter stub */
215 SharedUserData->SystemCall = (PsNtosImageBase >> (PAGE_SHIFT + 1));
216 Status = PspLookupSystemDllEntryPoint("KiFastSystemCall",
217 (PVOID)&SharedUserData->
218 SystemCall);
219 if (!NT_SUCCESS(Status)) return Status;
220
221 /* Get user-mode sysenter return stub */
222 Status = PspLookupSystemDllEntryPoint("KiFastSystemCallRet",
223 (PVOID)&SharedUserData->
224 SystemCallReturn);
225 if (!NT_SUCCESS(Status)) return Status;
226 }
227 else
228 {
229 /* Get the user-mode interrupt stub */
230 Status = PspLookupSystemDllEntryPoint("KiIntSystemCall",
231 (PVOID)&SharedUserData->
232 SystemCall);
233 if (!NT_SUCCESS(Status)) return Status;
234 }
235
236 /* Set the test instruction */
237 SharedUserData->TestRetInstruction = 0xC3;
238 #endif
239
240 /* Return the status */
241 return Status;
242 }
243
244 NTSTATUS
245 NTAPI
246 INIT_FUNCTION
247 PspMapSystemDll(IN PEPROCESS Process,
248 IN PVOID *DllBase,
249 IN BOOLEAN UseLargePages)
250 {
251 NTSTATUS Status;
252 LARGE_INTEGER Offset = {{0, 0}};
253 SIZE_T ViewSize = 0;
254 PVOID ImageBase = 0;
255
256 /* Map the System DLL */
257 Status = MmMapViewOfSection(PspSystemDllSection,
258 Process,
259 (PVOID*)&ImageBase,
260 0,
261 0,
262 &Offset,
263 &ViewSize,
264 ViewShare,
265 0,
266 PAGE_READWRITE);
267 if (Status != STATUS_SUCCESS)
268 {
269 /* Normalize status code */
270 Status = STATUS_CONFLICTING_ADDRESSES;
271 }
272
273 /* Write the image base and return status */
274 if (DllBase) *DllBase = ImageBase;
275 return Status;
276 }
277
278 NTSTATUS
279 NTAPI
280 INIT_FUNCTION
281 PsLocateSystemDll(VOID)
282 {
283 OBJECT_ATTRIBUTES ObjectAttributes;
284 IO_STATUS_BLOCK IoStatusBlock;
285 HANDLE FileHandle, SectionHandle;
286 NTSTATUS Status;
287 ULONG_PTR HardErrorParameters;
288 ULONG HardErrorResponse;
289
290 /* Locate and open NTDLL to determine ImageBase and LdrStartup */
291 InitializeObjectAttributes(&ObjectAttributes,
292 &PsNtDllPathName,
293 0,
294 NULL,
295 NULL);
296 Status = ZwOpenFile(&FileHandle,
297 FILE_READ_ACCESS,
298 &ObjectAttributes,
299 &IoStatusBlock,
300 FILE_SHARE_READ,
301 0);
302 if (!NT_SUCCESS(Status))
303 {
304 /* Failed, bugcheck */
305 KeBugCheckEx(PROCESS1_INITIALIZATION_FAILED, Status, 2, 0, 0);
306 }
307
308 /* Check if the image is valid */
309 Status = MmCheckSystemImage(FileHandle, TRUE);
310 if (Status == STATUS_IMAGE_CHECKSUM_MISMATCH)
311 {
312 /* Raise a hard error */
313 HardErrorParameters = (ULONG_PTR)&PsNtDllPathName;
314 NtRaiseHardError(Status,
315 1,
316 1,
317 &HardErrorParameters,
318 OptionOk,
319 &HardErrorResponse);
320 return Status;
321 }
322
323 /* Create a section for NTDLL */
324 Status = ZwCreateSection(&SectionHandle,
325 SECTION_ALL_ACCESS,
326 NULL,
327 NULL,
328 PAGE_EXECUTE,
329 SEC_IMAGE,
330 FileHandle);
331 ZwClose(FileHandle);
332 if (!NT_SUCCESS(Status))
333 {
334 /* Failed, bugcheck */
335 KeBugCheckEx(PROCESS1_INITIALIZATION_FAILED, Status, 3, 0, 0);
336 }
337
338 /* Reference the Section */
339 Status = ObReferenceObjectByHandle(SectionHandle,
340 SECTION_ALL_ACCESS,
341 MmSectionObjectType,
342 KernelMode,
343 (PVOID*)&PspSystemDllSection,
344 NULL);
345 ZwClose(SectionHandle);
346 if (!NT_SUCCESS(Status))
347 {
348 /* Failed, bugcheck */
349 KeBugCheckEx(PROCESS1_INITIALIZATION_FAILED, Status, 4, 0, 0);
350 }
351
352 /* Map it */
353 Status = PspMapSystemDll(PsGetCurrentProcess(), &PspSystemDllBase, FALSE);
354 if (!NT_SUCCESS(Status))
355 {
356 /* Failed, bugcheck */
357 KeBugCheckEx(PROCESS1_INITIALIZATION_FAILED, Status, 5, 0, 0);
358 }
359
360 /* Return status */
361 return Status;
362 }
363
364 NTSTATUS
365 NTAPI
366 INIT_FUNCTION
367 PspInitializeSystemDll(VOID)
368 {
369 NTSTATUS Status;
370
371 /* Get user-mode startup thunk */
372 Status = PspLookupSystemDllEntryPoint("LdrInitializeThunk",
373 &PspSystemDllEntryPoint);
374 if (!NT_SUCCESS(Status))
375 {
376 /* Failed, bugcheck */
377 KeBugCheckEx(PROCESS1_INITIALIZATION_FAILED, Status, 7, 0, 0);
378 }
379
380 /* Get all the other entrypoints */
381 Status = PspLookupKernelUserEntryPoints();
382 if (!NT_SUCCESS(Status))
383 {
384 /* Failed, bugcheck */
385 KeBugCheckEx(PROCESS1_INITIALIZATION_FAILED, Status, 8, 0, 0);
386 }
387
388 #ifdef _WINKD_
389 /* Let KD know we are done */
390 KdUpdateDataBlock();
391 #endif
392
393 /* Return status */
394 return Status;
395 }
396
397 BOOLEAN
398 NTAPI
399 INIT_FUNCTION
400 PspInitPhase1()
401 {
402 /* Initialize the System DLL and return status of operation */
403 if (!NT_SUCCESS(PspInitializeSystemDll())) return FALSE;
404 return TRUE;
405 }
406
407 BOOLEAN
408 NTAPI
409 INIT_FUNCTION
410 PspInitPhase0(IN PLOADER_PARAMETER_BLOCK LoaderBlock)
411 {
412 NTSTATUS Status;
413 OBJECT_ATTRIBUTES ObjectAttributes;
414 HANDLE SysThreadHandle;
415 PETHREAD SysThread;
416 MM_SYSTEMSIZE SystemSize;
417 UNICODE_STRING Name;
418 OBJECT_TYPE_INITIALIZER ObjectTypeInitializer;
419 ULONG i;
420
421 /* Get the system size */
422 SystemSize = MmQuerySystemSize();
423
424 /* Setup some memory options */
425 PspDefaultPagefileLimit = -1;
426 switch (SystemSize)
427 {
428 /* Medimum systems */
429 case MmMediumSystem:
430
431 /* Increase the WS sizes a bit */
432 PsMinimumWorkingSet += 10;
433 PsMaximumWorkingSet += 100;
434
435 /* Large systems */
436 case MmLargeSystem:
437
438 /* Increase the WS sizes a bit more */
439 PsMinimumWorkingSet += 30;
440 PsMaximumWorkingSet += 300;
441
442 /* Small and other systems */
443 default:
444 break;
445 }
446
447 /* Setup callbacks */
448 for (i = 0; i < PSP_MAX_CREATE_THREAD_NOTIFY; i++)
449 {
450 ExInitializeCallBack(&PspThreadNotifyRoutine[i]);
451 }
452 for (i = 0; i < PSP_MAX_CREATE_PROCESS_NOTIFY; i++)
453 {
454 ExInitializeCallBack(&PspProcessNotifyRoutine[i]);
455 }
456 for (i = 0; i < PSP_MAX_LOAD_IMAGE_NOTIFY; i++)
457 {
458 ExInitializeCallBack(&PspLoadImageNotifyRoutine[i]);
459 }
460
461 /* Setup the quantum table */
462 PsChangeQuantumTable(FALSE, PsRawPrioritySeparation);
463
464 /* Set quota settings */
465 if (!PspDefaultPagedLimit) PspDefaultPagedLimit = 0;
466 if (!PspDefaultNonPagedLimit) PspDefaultNonPagedLimit = 0;
467 if (!(PspDefaultNonPagedLimit) && !(PspDefaultPagedLimit))
468 {
469 /* Enable give-backs */
470 PspDoingGiveBacks = TRUE;
471 }
472 else
473 {
474 /* Disable them */
475 PspDoingGiveBacks = FALSE;
476 }
477
478 /* Now multiply limits by 1MB */
479 PspDefaultPagedLimit <<= 20;
480 PspDefaultNonPagedLimit <<= 20;
481 if (PspDefaultPagefileLimit != MAXULONG) PspDefaultPagefileLimit <<= 20;
482
483 /* Initialize the Active Process List */
484 InitializeListHead(&PsActiveProcessHead);
485 KeInitializeGuardedMutex(&PspActiveProcessMutex);
486
487 /* Get the idle process */
488 PsIdleProcess = PsGetCurrentProcess();
489
490 /* Setup the locks */
491 PsIdleProcess->ProcessLock.Value = 0;
492 ExInitializeRundownProtection(&PsIdleProcess->RundownProtect);
493
494 /* Initialize the thread list */
495 InitializeListHead(&PsIdleProcess->ThreadListHead);
496
497 /* Clear kernel time */
498 PsIdleProcess->Pcb.KernelTime = 0;
499
500 /* Initialize Object Initializer */
501 RtlZeroMemory(&ObjectTypeInitializer, sizeof(ObjectTypeInitializer));
502 ObjectTypeInitializer.Length = sizeof(ObjectTypeInitializer);
503 ObjectTypeInitializer.InvalidAttributes = OBJ_PERMANENT |
504 OBJ_EXCLUSIVE |
505 OBJ_OPENIF;
506 ObjectTypeInitializer.PoolType = NonPagedPool;
507 ObjectTypeInitializer.SecurityRequired = TRUE;
508
509 /* Initialize the Process type */
510 RtlInitUnicodeString(&Name, L"Process");
511 ObjectTypeInitializer.DefaultNonPagedPoolCharge = sizeof(EPROCESS);
512 ObjectTypeInitializer.GenericMapping = PspProcessMapping;
513 ObjectTypeInitializer.ValidAccessMask = PROCESS_ALL_ACCESS;
514 ObjectTypeInitializer.DeleteProcedure = PspDeleteProcess;
515 ObCreateObjectType(&Name, &ObjectTypeInitializer, NULL, &PsProcessType);
516
517 /* Initialize the Thread type */
518 RtlInitUnicodeString(&Name, L"Thread");
519 ObjectTypeInitializer.Length = sizeof(ObjectTypeInitializer);
520 ObjectTypeInitializer.DefaultNonPagedPoolCharge = sizeof(ETHREAD);
521 ObjectTypeInitializer.GenericMapping = PspThreadMapping;
522 ObjectTypeInitializer.ValidAccessMask = THREAD_ALL_ACCESS;
523 ObjectTypeInitializer.DeleteProcedure = PspDeleteThread;
524 ObCreateObjectType(&Name, &ObjectTypeInitializer, NULL, &PsThreadType);
525
526 /* Initialize the Job type */
527 RtlInitUnicodeString(&Name, L"Job");
528 ObjectTypeInitializer.Length = sizeof(ObjectTypeInitializer);
529 ObjectTypeInitializer.DefaultNonPagedPoolCharge = sizeof(EJOB);
530 ObjectTypeInitializer.GenericMapping = PspJobMapping;
531 ObjectTypeInitializer.InvalidAttributes = 0;
532 ObjectTypeInitializer.ValidAccessMask = JOB_OBJECT_ALL_ACCESS;
533 ObjectTypeInitializer.DeleteProcedure = PspDeleteJob;
534 ObCreateObjectType(&Name, &ObjectTypeInitializer, NULL, &PsJobType);
535
536 /* Initialize job structures external to this file */
537 PspInitializeJobStructures();
538
539 /* Initialize the Working Set data */
540 InitializeListHead(&PspWorkingSetChangeHead.List);
541 KeInitializeGuardedMutex(&PspWorkingSetChangeHead.Lock);
542
543 /* Create the CID Handle table */
544 PspCidTable = ExCreateHandleTable(NULL);
545 if (!PspCidTable) return FALSE;
546
547 /* FIXME: Initialize LDT/VDM support */
548
549 /* Setup the reaper */
550 ExInitializeWorkItem(&PspReaperWorkItem, PspReapRoutine, NULL);
551
552 /* Set the boot access token */
553 PspBootAccessToken = (PTOKEN)(PsIdleProcess->Token.Value & ~MAX_FAST_REFS);
554
555 /* Setup default object attributes */
556 InitializeObjectAttributes(&ObjectAttributes,
557 NULL,
558 0,
559 NULL,
560 NULL);
561
562 /* Create the Initial System Process */
563 Status = PspCreateProcess(&PspInitialSystemProcessHandle,
564 PROCESS_ALL_ACCESS,
565 &ObjectAttributes,
566 0,
567 FALSE,
568 0,
569 0,
570 0,
571 FALSE);
572 if (!NT_SUCCESS(Status)) return FALSE;
573
574 /* Get a reference to it */
575 ObReferenceObjectByHandle(PspInitialSystemProcessHandle,
576 0,
577 PsProcessType,
578 KernelMode,
579 (PVOID*)&PsInitialSystemProcess,
580 NULL);
581
582 /* Copy the process names */
583 strcpy(PsIdleProcess->ImageFileName, "Idle");
584 strcpy(PsInitialSystemProcess->ImageFileName, "System");
585
586 /* Allocate a structure for the audit name */
587 PsInitialSystemProcess->SeAuditProcessCreationInfo.ImageFileName =
588 ExAllocatePoolWithTag(PagedPool,
589 sizeof(OBJECT_NAME_INFORMATION),
590 TAG_SEPA);
591 if (!PsInitialSystemProcess->SeAuditProcessCreationInfo.ImageFileName)
592 {
593 /* Allocation failed */
594 return FALSE;
595 }
596
597 /* Zero it */
598 RtlZeroMemory(PsInitialSystemProcess->
599 SeAuditProcessCreationInfo.ImageFileName,
600 sizeof(OBJECT_NAME_INFORMATION));
601
602 /* Setup the system initialization thread */
603 Status = PsCreateSystemThread(&SysThreadHandle,
604 THREAD_ALL_ACCESS,
605 &ObjectAttributes,
606 0,
607 NULL,
608 Phase1Initialization,
609 LoaderBlock);
610 if (!NT_SUCCESS(Status)) return FALSE;
611
612 /* Create a handle to it */
613 ObReferenceObjectByHandle(SysThreadHandle,
614 0,
615 PsThreadType,
616 KernelMode,
617 (PVOID*)&SysThread,
618 NULL);
619 ObCloseHandle(SysThreadHandle, KernelMode);
620 SysThreadCreated = TRUE;
621
622 /* Return success */
623 return TRUE;
624 }
625
626 BOOLEAN
627 NTAPI
628 INIT_FUNCTION
629 PsInitSystem(IN PLOADER_PARAMETER_BLOCK LoaderBlock)
630 {
631 /* Check the initialization phase */
632 switch (ExpInitializationPhase)
633 {
634 case 0:
635
636 /* Do Phase 0 */
637 return PspInitPhase0(LoaderBlock);
638
639 case 1:
640
641 /* Do Phase 1 */
642 return PspInitPhase1();
643
644 default:
645
646 /* Don't know any other phase! Bugcheck! */
647 KeBugCheckEx(UNEXPECTED_INITIALIZATION_CALL,
648 1,
649 ExpInitializationPhase,
650 0,
651 0);
652 return FALSE;
653 }
654 }
655
656 /* PUBLIC FUNCTIONS **********************************************************/
657
658 /*
659 * @implemented
660 */
661 BOOLEAN
662 NTAPI
663 PsGetVersion(IN PULONG MajorVersion OPTIONAL,
664 IN PULONG MinorVersion OPTIONAL,
665 IN PULONG BuildNumber OPTIONAL,
666 IN PUNICODE_STRING CSDVersion OPTIONAL)
667 {
668 if (MajorVersion) *MajorVersion = NtMajorVersion;
669 if (MinorVersion) *MinorVersion = NtMinorVersion;
670 if (BuildNumber) *BuildNumber = NtBuildNumber;
671
672 if (CSDVersion)
673 {
674 CSDVersion->Length = 0;
675 CSDVersion->MaximumLength = 0;
676 CSDVersion->Buffer = NULL;
677 #if 0
678 CSDVersion->Length = CmCSDVersionString.Length;
679 CSDVersion->MaximumLength = CmCSDVersionString.Maximum;
680 CSDVersion->Buffer = CmCSDVersionString.Buffer;
681 #endif
682 }
683
684 /* Check the High word */
685 return (NtBuildNumber >> 28) == 0xC;
686 }
687
688 NTSTATUS
689 NTAPI
690 NtApphelpCacheControl(IN APPHELPCACHESERVICECLASS Service,
691 IN PVOID ServiceData)
692 {
693 UNIMPLEMENTED;
694 return STATUS_NOT_IMPLEMENTED;
695 }
696
697 /* EOF */