[DRWTSN32] Print some extra exception info
[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
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 */