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