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