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