xref: /reactos/ntoskrnl/ps/psmgr.c (revision 1734f297)
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 CODE_SEG("INIT")
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 CODE_SEG("INIT")
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 CODE_SEG("INIT")
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 CODE_SEG("INIT")
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 CODE_SEG("INIT")
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 CODE_SEG("INIT")
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     /* Let KD know we are done */
387     KdUpdateDataBlock();
388 
389     /* Return status */
390     return Status;
391 }
392 
393 CODE_SEG("INIT")
394 BOOLEAN
395 NTAPI
396 PspInitPhase1(VOID)
397 {
398     /* Initialize the System DLL and return status of operation */
399     if (!NT_SUCCESS(PspInitializeSystemDll())) return FALSE;
400     return TRUE;
401 }
402 
403 CODE_SEG("INIT")
404 BOOLEAN
405 NTAPI
406 PspInitPhase0(IN PLOADER_PARAMETER_BLOCK LoaderBlock)
407 {
408     NTSTATUS Status;
409     OBJECT_ATTRIBUTES ObjectAttributes;
410     HANDLE SysThreadHandle;
411     PETHREAD SysThread;
412     MM_SYSTEMSIZE SystemSize;
413     UNICODE_STRING Name;
414     OBJECT_TYPE_INITIALIZER ObjectTypeInitializer;
415     ULONG i;
416 
417     /* Get the system size */
418     SystemSize = MmQuerySystemSize();
419 
420     /* Setup some memory options */
421     PspDefaultPagefileLimit = -1;
422     switch (SystemSize)
423     {
424         /* Medimum systems */
425         case MmMediumSystem:
426 
427             /* Increase the WS sizes a bit */
428             PsMinimumWorkingSet += 10;
429             PsMaximumWorkingSet += 100;
430 
431         /* Large systems */
432         case MmLargeSystem:
433 
434             /* Increase the WS sizes a bit more */
435             PsMinimumWorkingSet += 30;
436             PsMaximumWorkingSet += 300;
437 
438         /* Small and other systems */
439         default:
440             break;
441     }
442 
443     /* Setup callbacks */
444     for (i = 0; i < PSP_MAX_CREATE_THREAD_NOTIFY; i++)
445     {
446         ExInitializeCallBack(&PspThreadNotifyRoutine[i]);
447     }
448     for (i = 0; i < PSP_MAX_CREATE_PROCESS_NOTIFY; i++)
449     {
450         ExInitializeCallBack(&PspProcessNotifyRoutine[i]);
451     }
452     for (i = 0; i < PSP_MAX_LOAD_IMAGE_NOTIFY; i++)
453     {
454         ExInitializeCallBack(&PspLoadImageNotifyRoutine[i]);
455     }
456 
457     /* Setup the quantum table */
458     PsChangeQuantumTable(FALSE, PsRawPrioritySeparation);
459 
460     /* Set quota settings */
461     if (!PspDefaultPagedLimit) PspDefaultPagedLimit = 0;
462     if (!PspDefaultNonPagedLimit) PspDefaultNonPagedLimit = 0;
463     if (!(PspDefaultNonPagedLimit) && !(PspDefaultPagedLimit))
464     {
465         /* Enable give-backs */
466         PspDoingGiveBacks = TRUE;
467     }
468     else
469     {
470         /* Disable them */
471         PspDoingGiveBacks = FALSE;
472     }
473 
474     /* Now multiply limits by 1MB */
475     PspDefaultPagedLimit <<= 20;
476     PspDefaultNonPagedLimit <<= 20;
477     if (PspDefaultPagefileLimit != MAXULONG) PspDefaultPagefileLimit <<= 20;
478 
479     /* Initialize the Active Process List */
480     InitializeListHead(&PsActiveProcessHead);
481     KeInitializeGuardedMutex(&PspActiveProcessMutex);
482 
483     /* Get the idle process */
484     PsIdleProcess = PsGetCurrentProcess();
485 
486     /* Setup the locks */
487     PsIdleProcess->ProcessLock.Value = 0;
488     ExInitializeRundownProtection(&PsIdleProcess->RundownProtect);
489 
490     /* Initialize the thread list */
491     InitializeListHead(&PsIdleProcess->ThreadListHead);
492 
493     /* Clear kernel time */
494     PsIdleProcess->Pcb.KernelTime = 0;
495 
496     /* Initialize Object Initializer */
497     RtlZeroMemory(&ObjectTypeInitializer, sizeof(ObjectTypeInitializer));
498     ObjectTypeInitializer.Length = sizeof(ObjectTypeInitializer);
499     ObjectTypeInitializer.InvalidAttributes = OBJ_PERMANENT |
500                                               OBJ_EXCLUSIVE |
501                                               OBJ_OPENIF;
502     ObjectTypeInitializer.PoolType = NonPagedPool;
503     ObjectTypeInitializer.SecurityRequired = TRUE;
504 
505     /* Initialize the Process type */
506     RtlInitUnicodeString(&Name, L"Process");
507     ObjectTypeInitializer.DefaultNonPagedPoolCharge = sizeof(EPROCESS);
508     ObjectTypeInitializer.GenericMapping = PspProcessMapping;
509     ObjectTypeInitializer.ValidAccessMask = PROCESS_ALL_ACCESS;
510     ObjectTypeInitializer.DeleteProcedure = PspDeleteProcess;
511     ObCreateObjectType(&Name, &ObjectTypeInitializer, NULL, &PsProcessType);
512 
513     /*  Initialize the Thread type  */
514     RtlInitUnicodeString(&Name, L"Thread");
515     ObjectTypeInitializer.Length = sizeof(ObjectTypeInitializer);
516     ObjectTypeInitializer.DefaultNonPagedPoolCharge = sizeof(ETHREAD);
517     ObjectTypeInitializer.GenericMapping = PspThreadMapping;
518     ObjectTypeInitializer.ValidAccessMask = THREAD_ALL_ACCESS;
519     ObjectTypeInitializer.DeleteProcedure = PspDeleteThread;
520     ObCreateObjectType(&Name, &ObjectTypeInitializer, NULL, &PsThreadType);
521 
522     /*  Initialize the Job type  */
523     RtlInitUnicodeString(&Name, L"Job");
524     ObjectTypeInitializer.Length = sizeof(ObjectTypeInitializer);
525     ObjectTypeInitializer.DefaultNonPagedPoolCharge = sizeof(EJOB);
526     ObjectTypeInitializer.GenericMapping = PspJobMapping;
527     ObjectTypeInitializer.InvalidAttributes = 0;
528     ObjectTypeInitializer.ValidAccessMask = JOB_OBJECT_ALL_ACCESS;
529     ObjectTypeInitializer.DeleteProcedure = PspDeleteJob;
530     ObCreateObjectType(&Name, &ObjectTypeInitializer, NULL, &PsJobType);
531 
532     /* Initialize job structures external to this file */
533     PspInitializeJobStructures();
534 
535     /* Initialize the Working Set data */
536     InitializeListHead(&PspWorkingSetChangeHead.List);
537     KeInitializeGuardedMutex(&PspWorkingSetChangeHead.Lock);
538 
539     /* Create the CID Handle table */
540     PspCidTable = ExCreateHandleTable(NULL);
541     if (!PspCidTable) return FALSE;
542 
543     /* FIXME: Initialize LDT/VDM support */
544 
545     /* Setup the reaper */
546     ExInitializeWorkItem(&PspReaperWorkItem, PspReapRoutine, NULL);
547 
548     /* Set the boot access token */
549     PspBootAccessToken = (PTOKEN)(PsIdleProcess->Token.Value & ~MAX_FAST_REFS);
550 
551     /* Setup default object attributes */
552     InitializeObjectAttributes(&ObjectAttributes,
553                                NULL,
554                                0,
555                                NULL,
556                                NULL);
557 
558     /* Create the Initial System Process */
559     Status = PspCreateProcess(&PspInitialSystemProcessHandle,
560                               PROCESS_ALL_ACCESS,
561                               &ObjectAttributes,
562                               0,
563                               FALSE,
564                               0,
565                               0,
566                               0,
567                               FALSE);
568     if (!NT_SUCCESS(Status)) return FALSE;
569 
570     /* Get a reference to it */
571     ObReferenceObjectByHandle(PspInitialSystemProcessHandle,
572                               0,
573                               PsProcessType,
574                               KernelMode,
575                               (PVOID*)&PsInitialSystemProcess,
576                               NULL);
577 
578     /* Copy the process names */
579     strcpy(PsIdleProcess->ImageFileName, "Idle");
580     strcpy(PsInitialSystemProcess->ImageFileName, "System");
581 
582     /* Allocate a structure for the audit name */
583     PsInitialSystemProcess->SeAuditProcessCreationInfo.ImageFileName =
584         ExAllocatePoolWithTag(PagedPool,
585                               sizeof(OBJECT_NAME_INFORMATION),
586                               TAG_SEPA);
587     if (!PsInitialSystemProcess->SeAuditProcessCreationInfo.ImageFileName)
588     {
589         /* Allocation failed */
590         return FALSE;
591     }
592 
593     /* Zero it */
594     RtlZeroMemory(PsInitialSystemProcess->
595                   SeAuditProcessCreationInfo.ImageFileName,
596                   sizeof(OBJECT_NAME_INFORMATION));
597 
598     /* Setup the system initialization thread */
599     Status = PsCreateSystemThread(&SysThreadHandle,
600                                   THREAD_ALL_ACCESS,
601                                   &ObjectAttributes,
602                                   0,
603                                   NULL,
604                                   Phase1Initialization,
605                                   LoaderBlock);
606     if (!NT_SUCCESS(Status)) return FALSE;
607 
608     /* Create a handle to it */
609     ObReferenceObjectByHandle(SysThreadHandle,
610                               0,
611                               PsThreadType,
612                               KernelMode,
613                               (PVOID*)&SysThread,
614                               NULL);
615     ObCloseHandle(SysThreadHandle, KernelMode);
616 
617     /* Return success */
618     return TRUE;
619 }
620 
621 CODE_SEG("INIT")
622 BOOLEAN
623 NTAPI
624 PsInitSystem(IN PLOADER_PARAMETER_BLOCK LoaderBlock)
625 {
626     /* Check the initialization phase */
627     switch (ExpInitializationPhase)
628     {
629     case 0:
630 
631         /* Do Phase 0 */
632         return PspInitPhase0(LoaderBlock);
633 
634     case 1:
635 
636         /* Do Phase 1 */
637         return PspInitPhase1();
638 
639     default:
640 
641         /* Don't know any other phase! Bugcheck! */
642         KeBugCheckEx(UNEXPECTED_INITIALIZATION_CALL,
643                      1,
644                      ExpInitializationPhase,
645                      0,
646                      0);
647         return FALSE;
648     }
649 }
650 
651 /* PUBLIC FUNCTIONS **********************************************************/
652 
653 /*
654  * @implemented
655  */
656 BOOLEAN
657 NTAPI
658 PsGetVersion(OUT PULONG MajorVersion OPTIONAL,
659              OUT PULONG MinorVersion OPTIONAL,
660              OUT PULONG BuildNumber  OPTIONAL,
661              OUT PUNICODE_STRING CSDVersion OPTIONAL)
662 {
663     if (MajorVersion) *MajorVersion = NtMajorVersion;
664     if (MinorVersion) *MinorVersion = NtMinorVersion;
665     if (BuildNumber ) *BuildNumber  = NtBuildNumber & 0x3FFF;
666 
667     if (CSDVersion)
668     {
669         CSDVersion->Length = CmCSDVersionString.Length;
670         CSDVersion->MaximumLength = CmCSDVersionString.MaximumLength;
671         CSDVersion->Buffer = CmCSDVersionString.Buffer;
672     }
673 
674     /* Return TRUE if this is a Checked Build */
675     return (NtBuildNumber >> 28) == 0xC;
676 }
677 
678 /* EOF */
679