00470 :
00471
00472 This routine creates x86 specific entries in
the registry.
00473
00474 Arguments:
00475
00476 LoaderBlock - supplies a pointer to
the LoaderBlock passed in from
the
00477 OS Loader.
00478
00479 Returns:
00480
00481
NTSTATUS code
for sucess or
reason of
failure.
00482
00483 --*/
00484 {
00485
NTSTATUS Status;
00486 ULONG VideoBiosStart;
00487 UNICODE_STRING
KeyName;
00488 UNICODE_STRING
ValueName;
00489 UNICODE_STRING ValueData;
00490 ANSI_STRING AnsiString;
00491 OBJECT_ATTRIBUTES
ObjectAttributes;
00492 ULONG Disposition;
00493 HANDLE ParentHandle;
00494 HANDLE BaseHandle, NpxHandle;
00495 HANDLE CurrentControlSet;
00496
CONFIGURATION_COMPONENT_DATA CurrentEntry;
00497 PUCHAR VendorID;
00498 UCHAR
Buffer[
MAXIMUM_BIOS_VERSION_LENGTH];
00499 PKPRCB Prcb;
00500 ULONG i, Junk;
00501 ULONG VersionsLength = 0, Length;
00502 PCHAR VersionStrings, VersionPointer;
00503 UNICODE_STRING SectionName;
00504 ULONG ViewSize;
00505 LARGE_INTEGER ViewBase;
00506 PVOID BaseAddress;
00507 HANDLE SectionHandle;
00508
USHORT DeviceIndexTable[
NUMBER_TYPES];
00509 ULONG CpuIdFunction;
00510 ULONG MaxExtFn;
00511 PULONG NameString =
NULL;
00512 ULONG P0L2Size = 0;
00513 ULONG ThisProcessorL2Size;
00514
struct {
00515
union {
00516 UCHAR Bytes[
CPUID_PROCESSOR_NAME_STRING_SZ];
00517 ULONG DWords[1];
00518 } u;
00519 } ProcessorNameString;
00520
00521
#ifdef _WANT_MACHINE_IDENTIFICATION
00522
HANDLE BiosInfo;
00523
#endif
00524
00525
for (i = 0; i <
NUMBER_TYPES; i++) {
00526 DeviceIndexTable[i] = 0;
00527 }
00528
00529 InitializeObjectAttributes( &ObjectAttributes,
00530 &CmRegistryMachineSystemCurrentControlSetControlSessionManagerMemoryManagement,
00531 OBJ_CASE_INSENSITIVE,
00532 NULL,
00533 NULL
00534 );
00535
00536
Status =
NtOpenKey( &BaseHandle,
00537 KEY_READ | KEY_WRITE,
00538 &ObjectAttributes
00539 );
00540
00541
if (
NT_SUCCESS(Status)) {
00542
00543 ULONG paeEnabled;
00544
00545
if (SharedUserData->ProcessorFeatures[PF_PAE_ENABLED] ==
FALSE) {
00546 paeEnabled = 0;
00547 }
else {
00548 paeEnabled = 1;
00549 }
00550
00551
RtlInitUnicodeString( &ValueName,
00552 CmPhysicalAddressExtension );
00553
00554
00555
NtSetValueKey( BaseHandle,
00556 &ValueName,
00557 TITLE_INDEX_VALUE,
00558 REG_DWORD,
00559 &paeEnabled,
00560
sizeof(paeEnabled) );
00561
00562
NtClose( BaseHandle );
00563 }
00564
00565
00566
00567
00568
00569 InitializeObjectAttributes( &ObjectAttributes,
00570 &CmRegistryMachineHardwareDescriptionSystemName,
00571 OBJ_CASE_INSENSITIVE,
00572 NULL,
00573 NULL
00574 );
00575
00576
Status =
NtOpenKey( &ParentHandle,
00577 KEY_READ,
00578 &ObjectAttributes
00579 );
00580
00581
if (!
NT_SUCCESS(Status)) {
00582
00583
return Status;
00584 }
00585
00586
#ifdef _WANT_MACHINE_IDENTIFICATION
00587
00588 InitializeObjectAttributes( &ObjectAttributes,
00589 &CmRegistryMachineSystemCurrentControlSetControlBiosInfo,
00590 OBJ_CASE_INSENSITIVE,
00591 NULL,
00592 NULL
00593 );
00594
00595
Status =
NtCreateKey( &BiosInfo,
00596 KEY_ALL_ACCESS,
00597 &ObjectAttributes,
00598 0,
00599 NULL,
00600 REG_OPTION_NON_VOLATILE,
00601 &Disposition
00602 );
00603
00604
if (!
NT_SUCCESS(Status)) {
00605
00606
return Status;
00607 }
00608
00609
#endif
00610
00611
00612
00613
00614
00615
00616
00617
00618
00619
RtlInitUnicodeString( &KeyName,
00620 L
"CentralProcessor"
00621 );
00622
00623 InitializeObjectAttributes(
00624 &ObjectAttributes,
00625 &KeyName,
00626 0,
00627 ParentHandle,
00628 NULL
00629 );
00630
00631
ObjectAttributes.Attributes |= OBJ_CASE_INSENSITIVE;
00632
00633
Status =
NtCreateKey(
00634 &BaseHandle,
00635 KEY_READ | KEY_WRITE,
00636 &ObjectAttributes,
00637 TITLE_INDEX_VALUE,
00638 &CmClassName[ProcessorClass],
00639 0,
00640 &Disposition
00641 );
00642
00643
NtClose (BaseHandle);
00644
00645
if (Disposition == REG_CREATED_NEW_KEY) {
00646
00647
00648
00649
00650
00651
00652
CmpConfigurationData = (PCM_FULL_RESOURCE_DESCRIPTOR)
ExAllocatePool(
00653 PagedPool,
00654 CmpConfigurationAreaSize
00655 );
00656
00657
00658
00659
00660
00661
00662
00663
00664
00665
for (i=0; i < (ULONG)
KeNumberProcessors; i++) {
00666 Prcb =
KiProcessorBlock[i];
00667
00668 RtlZeroMemory (&CurrentEntry,
sizeof CurrentEntry);
00669 CurrentEntry.
ComponentEntry.
Class =
ProcessorClass;
00670 CurrentEntry.
ComponentEntry.
Type =
CentralProcessor;
00671 CurrentEntry.
ComponentEntry.
Key = i;
00672 CurrentEntry.
ComponentEntry.
AffinityMask = 1 << i;
00673
00674 CurrentEntry.
ComponentEntry.
Identifier =
Buffer;
00675
if (Prcb->CpuID == 0) {
00676
00677
00678
00679
00680
00681
sprintf (Buffer, CmpID1,
00682 Prcb->CpuType,
00683 (Prcb->CpuStep >> 8) +
'A',
00684 Prcb->CpuStep & 0xff
00685 );
00686
00687 }
else {
00688
00689
00690
00691
00692
00693
sprintf (Buffer, CmpID2,
00694 Prcb->CpuType,
00695 (Prcb->CpuStep >> 8),
00696 Prcb->CpuStep & 0xff
00697 );
00698 }
00699
00700 CurrentEntry.
ComponentEntry.
IdentifierLength =
00701
strlen (Buffer) + 1;
00702
00703
Status =
CmpInitializeRegistryNode(
00704 &CurrentEntry,
00705 ParentHandle,
00706 &BaseHandle,
00707 -1,
00708 (ULONG)-1,
00709 DeviceIndexTable
00710 );
00711
00712
if (!
NT_SUCCESS(Status)) {
00713
return(
Status);
00714 }
00715
00716
00717
if (
KeI386NpxPresent) {
00718 RtlZeroMemory (&CurrentEntry,
sizeof CurrentEntry);
00719 CurrentEntry.
ComponentEntry.
Class =
ProcessorClass;
00720 CurrentEntry.
ComponentEntry.
Type =
FloatingPointProcessor;
00721 CurrentEntry.
ComponentEntry.
Key = i;
00722 CurrentEntry.
ComponentEntry.
AffinityMask = 1 << i;
00723
00724 CurrentEntry.
ComponentEntry.
Identifier =
Buffer;
00725
00726
if (Prcb->CpuType == 3) {
00727
00728
00729
00730
00731
00732
00733 strcpy (Buffer,
"80387");
00734 }
00735
00736 CurrentEntry.
ComponentEntry.
IdentifierLength =
00737
strlen (Buffer) + 1;
00738
00739
Status =
CmpInitializeRegistryNode(
00740 &CurrentEntry,
00741 ParentHandle,
00742 &NpxHandle,
00743 -1,
00744 (ULONG)-1,
00745 DeviceIndexTable
00746 );
00747
00748
if (!
NT_SUCCESS(Status)) {
00749
NtClose(BaseHandle);
00750
return(
Status);
00751 }
00752
00753
NtClose(NpxHandle);
00754 }
00755
00756
00757
00758
00759
00760
00761 VendorID = Prcb->CpuID ? Prcb->VendorString :
NULL;
00762
00763
00764
00765
00766
00767
00768
KeSetSystemAffinityThread(Prcb->SetMember);
00769
00770
if (!Prcb->CpuID) {
00771
00772
00773
00774
00775
00776
if (
Ke386CyrixId ()) {
00777 VendorID =
CmpCyrixID;
00778 }
00779 }
else {
00780
00781
00782
00783
00784
00785
00786
00787
00788
00789
00790
00791
00792
00793
00794
00795
00796
00797
CPUID(CPUID_EXTFN_BASE, &MaxExtFn, &Junk, &Junk, &Junk);
00798
00799
if (MaxExtFn >= (
CPUID_EXTFN_PROCESSOR_NAME + 2)) {
00800
00801
00802
00803
00804
00805
00806
00807
00808
00809 NameString = &ProcessorNameString.u.DWords[0];
00810
00811
for (CpuIdFunction =
CPUID_EXTFN_PROCESSOR_NAME;
00812 CpuIdFunction <= (
CPUID_EXTFN_PROCESSOR_NAME+2);
00813 CpuIdFunction++) {
00814
00815
CPUID(CpuIdFunction,
00816 NameString,
00817 NameString + 1,
00818 NameString + 2,
00819 NameString + 3);
00820 NameString += 4;
00821 }
00822
00823
00824
00825
00826
00827 ProcessorNameString.u.Bytes[
CPUID_PROCESSOR_NAME_STRING_SZ-1] = 0;
00828 }
00829 }
00830
00831 ThisProcessorL2Size =
KeGetPcr()->SecondLevelCacheSize;
00832
00833
00834
00835
00836
00837
KeRevertToUserAffinityThread();
00838
00839
if (NameString) {
00840
00841
00842
00843
00844
00845
RtlInitUnicodeString(
00846 &ValueName,
00847 CmpProcessorNameString
00848 );
00849
00850
RtlInitAnsiString(
00851 &AnsiString,
00852 ProcessorNameString.u.Bytes
00853 );
00854
00855
RtlAnsiStringToUnicodeString(
00856 &ValueData,
00857 &AnsiString,
00858 TRUE
00859 );
00860
00861
Status =
NtSetValueKey(
00862 BaseHandle,
00863 &ValueName,
00864 TITLE_INDEX_VALUE,
00865 REG_SZ,
00866 ValueData.Buffer,
00867 ValueData.Length +
sizeof( UNICODE_NULL )
00868 );
00869
00870
RtlFreeUnicodeString(&ValueData);
00871 }
00872
00873
if (VendorID) {
00874
00875
00876
00877
00878
00879
RtlInitUnicodeString(
00880 &ValueName,
00881 CmpVendorID
00882 );
00883
00884
RtlInitAnsiString(
00885 &AnsiString,
00886 VendorID
00887 );
00888
00889
RtlAnsiStringToUnicodeString(
00890 &ValueData,
00891 &AnsiString,
00892 TRUE
00893 );
00894
00895
Status =
NtSetValueKey(
00896 BaseHandle,
00897 &ValueName,
00898 TITLE_INDEX_VALUE,
00899 REG_SZ,
00900 ValueData.Buffer,
00901 ValueData.Length +
sizeof( UNICODE_NULL )
00902 );
00903
00904
RtlFreeUnicodeString(&ValueData);
00905 }
00906
00907
if (Prcb->FeatureBits) {
00908
00909
00910
00911
00912
RtlInitUnicodeString(
00913 &ValueName,
00914 CmpFeatureBits
00915 );
00916
00917
Status =
NtSetValueKey(
00918 BaseHandle,
00919 &ValueName,
00920 TITLE_INDEX_VALUE,
00921 REG_DWORD,
00922 &Prcb->FeatureBits,
00923 sizeof (Prcb->FeatureBits)
00924 );
00925 }
00926
00927
if (Prcb->MHz) {
00928
00929
00930
00931
00932
RtlInitUnicodeString(
00933 &ValueName,
00934 CmpMHz
00935 );
00936
00937
Status =
NtSetValueKey(
00938 BaseHandle,
00939 &ValueName,
00940 TITLE_INDEX_VALUE,
00941 REG_DWORD,
00942 &Prcb->MHz,
00943 sizeof (Prcb->MHz)
00944 );
00945 }
00946
00947
if (Prcb->UpdateSignature.QuadPart) {
00948
00949
00950
00951
00952
RtlInitUnicodeString(
00953 &ValueName,
00954 CmpUpdateSignature
00955 );
00956
00957
Status =
NtSetValueKey(
00958 BaseHandle,
00959 &ValueName,
00960 TITLE_INDEX_VALUE,
00961 REG_BINARY,
00962 &Prcb->UpdateSignature,
00963 sizeof (Prcb->UpdateSignature)
00964 );
00965 }
00966
00967
NtClose(BaseHandle);
00968
00969
00970
00971
00972
00973
if (i == 0) {
00974
00975 P0L2Size = ThisProcessorL2Size;
00976
00977 }
else {
00978
00979
00980
00981
00982
00983
00984
00985
00986
00987
if (Prcb->CpuID) {
00988
if (strcmp(Prcb->VendorString,
00989 KiProcessorBlock[0]->VendorString)) {
00990
CmProcessorMismatch |=
CM_PROCESSOR_MISMATCH_VENDOR;
00991 }
00992
if (ThisProcessorL2Size != P0L2Size) {
00993
CmProcessorMismatch |=
CM_PROCESSOR_MISMATCH_L2;
00994 }
00995
if ((Prcb->CpuType !=
KiProcessorBlock[0]->CpuType) ||
00996 (Prcb->CpuStep !=
KiProcessorBlock[0]->CpuStep)) {
00997
CmProcessorMismatch |=
CM_PROCESSOR_MISMATCH_STEPPING;
00998 }
00999 }
else {
01000
01001
01002
01003
01004
01005
01006
if (
KiProcessorBlock[0]->CpuID) {
01007
CmProcessorMismatch |=
CM_PROCESSOR_MISMATCH_STEPPING;
01008 }
01009 }
01010 }
01011 }
01012
01013
if (0 !=
CmpConfigurationData) {
01014
ExFreePool((PVOID)CmpConfigurationData);
01015 }
01016 }
01017
01018
01019
01020
01021
01022
01023
01024
01025
01026
01027
RtlInitUnicodeString(
01028 &SectionName,
01029 L
"\\Device\\PhysicalMemory"
01030 );
01031
01032 InitializeObjectAttributes(
01033 &ObjectAttributes,
01034 &SectionName,
01035 OBJ_CASE_INSENSITIVE,
01036 (HANDLE) NULL,
01037 (PSECURITY_DESCRIPTOR) NULL
01038 );
01039
01040
Status = ZwOpenSection(
01041 &SectionHandle,
01042 SECTION_ALL_ACCESS,
01043 &ObjectAttributes
01044 );
01045
01046
if (!
NT_SUCCESS(Status)) {
01047
01048
01049
01050
01051
01052
goto AllDone;
01053 }
01054
01055
01056
01057
01058
01059
01060 BaseAddress = 0;
01061 ViewSize = 0x1000;
01062 ViewBase.LowPart = 0;
01063 ViewBase.HighPart = 0;
01064
01065
Status =ZwMapViewOfSection(
01066 SectionHandle,
01067 NtCurrentProcess(),
01068 &BaseAddress,
01069 0,
01070 ViewSize,
01071 &ViewBase,
01072 &ViewSize,
01073 ViewUnmap,
01074 MEM_DOS_LIM,
01075 PAGE_READWRITE
01076 );
01077
01078
if (!
NT_SUCCESS(Status)) {
01079 VideoBiosStart =
VIDEO_BIOS_START;
01080 }
else {
01081 VideoBiosStart = (*((PULONG)BaseAddress +
INT10_VECTOR) & 0xFFFF0000) >> 12;
01082 VideoBiosStart += (*((PULONG)BaseAddress +
INT10_VECTOR) & 0x0000FFFF);
01083 VideoBiosStart &= 0xffff8000;
01084
if (VideoBiosStart <
VIDEO_BIOS_START) {
01085 VideoBiosStart =
VIDEO_BIOS_START;
01086 }
01087
Status = ZwUnmapViewOfSection(
01088 NtCurrentProcess(),
01089 BaseAddress
01090 );
01091 }
01092
01093 VersionStrings =
ExAllocatePool(PagedPool, VERSION_DATA_LENGTH);
01094 BaseAddress = 0;
01095 ViewSize =
SYSTEM_BIOS_LENGTH;
01096 ViewBase.LowPart =
SYSTEM_BIOS_START;
01097 ViewBase.HighPart = 0;
01098
01099
Status =ZwMapViewOfSection(
01100 SectionHandle,
01101 NtCurrentProcess(),
01102 &BaseAddress,
01103 0,
01104 ViewSize,
01105 &ViewBase,
01106 &ViewSize,
01107 ViewUnmap,
01108 MEM_DOS_LIM,
01109 PAGE_READWRITE
01110 );
01111
01112
if (
NT_SUCCESS(Status)) {
01113
if (
CmpGetBiosDate(BaseAddress, SYSTEM_BIOS_LENGTH, Buffer, TRUE)) {
01114
01115
01116
01117
01118
01119
01120
RtlInitUnicodeString(
01121 &ValueName,
01122 L
"SystemBiosDate"
01123 );
01124
01125
RtlInitAnsiString(
01126 &AnsiString,
01127 Buffer
01128 );
01129
01130
RtlAnsiStringToUnicodeString(
01131 &ValueData,
01132 &AnsiString,
01133 TRUE
01134 );
01135
01136
Status =
NtSetValueKey(
01137 ParentHandle,
01138 &ValueName,
01139 TITLE_INDEX_VALUE,
01140 REG_SZ,
01141 ValueData.Buffer,
01142 ValueData.Length +
sizeof( UNICODE_NULL )
01143 );
01144
01145
RtlFreeUnicodeString(&ValueData);
01146
01147
#ifdef _WANT_MACHINE_IDENTIFICATION
01148
01149 memcpy(Buffer, (PCHAR)BaseAddress + 0xFFF5, 8);
01150
Buffer[8] =
'\0';
01151
01152
RtlInitAnsiString(
01153 &AnsiString,
01154 Buffer
01155 );
01156
01157
Status =
RtlAnsiStringToUnicodeString(
01158 &ValueData,
01159 &AnsiString,
01160 TRUE
01161 );
01162
01163
if (
NT_SUCCESS(Status)) {
01164
01165
Status =
NtSetValueKey(
01166 BiosInfo,
01167 &ValueName,
01168 TITLE_INDEX_VALUE,
01169 REG_SZ,
01170 ValueData.Buffer,
01171 ValueData.Length +
sizeof( UNICODE_NULL )
01172 );
01173
01174
RtlFreeUnicodeString(&ValueData);
01175 }
01176
01177
NtClose (BiosInfo);
01178
01179
#endif
01180
01181 }
01182
01183
if (VersionStrings &&
CmpGetBiosVersion(BaseAddress, SYSTEM_BIOS_LENGTH, Buffer)) {
01184 VersionPointer = VersionStrings;
01185
do {
01186
01187
01188
01189
01190
01191
01192
01193
RtlInitAnsiString(
01194 &AnsiString,
01195 Buffer
01196 );
01197
01198
RtlAnsiStringToUnicodeString(
01199 &ValueData,
01200 &AnsiString,
01201 TRUE
01202 );
01203
01204 Length = ValueData.Length +
sizeof(UNICODE_NULL);
01205 RtlMoveMemory(VersionPointer,
01206 ValueData.Buffer,
01207 Length
01208 );
01209 VersionsLength += Length;
01210
RtlFreeUnicodeString(&ValueData);
01211
if (VersionsLength + (
MAXIMUM_BIOS_VERSION_LENGTH +
01212
sizeof(UNICODE_NULL)) * 2 >
PAGE_SIZE) {
01213
break;
01214 }
01215 VersionPointer += Length;
01216 }
while (
CmpGetBiosVersion(NULL, 0, Buffer));
01217
01218
if (VersionsLength != 0) {
01219
01220
01221
01222
01223
01224 *(PWSTR)VersionPointer = UNICODE_NULL;
01225 VersionsLength +=
sizeof(UNICODE_NULL);
01226
01227
01228
01229
01230
01231
01232
RtlInitUnicodeString(
01233 &ValueName,
01234 L
"SystemBiosVersion"
01235 );
01236
01237
Status =
NtSetValueKey(
01238 ParentHandle,
01239 &ValueName,
01240 TITLE_INDEX_VALUE,
01241 REG_MULTI_SZ,
01242 VersionStrings,
01243 VersionsLength
01244 );
01245 }
01246 }
01247 ZwUnmapViewOfSection(NtCurrentProcess(), BaseAddress);
01248 }
01249
01250
01251
01252
01253
01254 BaseAddress = 0;
01255 ViewSize =
VIDEO_BIOS_LENGTH;
01256 ViewBase.LowPart = VideoBiosStart;
01257 ViewBase.HighPart = 0;
01258
01259
Status =ZwMapViewOfSection(
01260 SectionHandle,
01261 NtCurrentProcess(),
01262 &BaseAddress,
01263 0,
01264 ViewSize,
01265 &ViewBase,
01266 &ViewSize,
01267 ViewUnmap,
01268 MEM_DOS_LIM,
01269 PAGE_READWRITE
01270 );
01271
01272
if (
NT_SUCCESS(Status)) {
01273
if (
CmpGetBiosDate(BaseAddress, VIDEO_BIOS_LENGTH, Buffer, FALSE)) {
01274
01275
RtlInitUnicodeString(
01276 &ValueName,
01277 L
"VideoBiosDate"
01278 );
01279
01280
RtlInitAnsiString(
01281 &AnsiString,
01282 Buffer
01283 );
01284
01285
RtlAnsiStringToUnicodeString(
01286 &ValueData,
01287 &AnsiString,
01288 TRUE
01289 );
01290
01291
Status =
NtSetValueKey(
01292 ParentHandle,
01293 &ValueName,
01294 TITLE_INDEX_VALUE,
01295 REG_SZ,
01296 ValueData.Buffer,
01297 ValueData.Length +
sizeof( UNICODE_NULL )
01298 );
01299
01300
RtlFreeUnicodeString(&ValueData);
01301 }
01302
01303
if (VersionStrings &&
CmpGetBiosVersion(BaseAddress, VIDEO_BIOS_LENGTH, Buffer)) {
01304 VersionPointer = VersionStrings;
01305
do {
01306
01307
01308
01309
01310
01311
01312
01313
RtlInitAnsiString(
01314 &AnsiString,
01315 Buffer
01316 );
01317
01318
RtlAnsiStringToUnicodeString(
01319 &ValueData,
01320 &AnsiString,
01321 TRUE
01322 );
01323
01324 Length = ValueData.Length +
sizeof(UNICODE_NULL);
01325 RtlMoveMemory(VersionPointer,
01326 ValueData.Buffer,
01327 Length
01328 );
01329 VersionsLength += Length;
01330
RtlFreeUnicodeString(&ValueData);
01331
if (VersionsLength + (
MAXIMUM_BIOS_VERSION_LENGTH +
01332
sizeof(UNICODE_NULL)) * 2 >
PAGE_SIZE) {
01333
break;
01334 }
01335 VersionPointer += Length;
01336 }
while (
CmpGetBiosVersion(NULL, 0, Buffer));
01337
01338
if (VersionsLength != 0) {
01339
01340
01341
01342
01343
01344 *(PWSTR)VersionPointer = UNICODE_NULL;
01345 VersionsLength +=
sizeof(UNICODE_NULL);
01346
01347
RtlInitUnicodeString(
01348 &ValueName,
01349 L
"VideoBiosVersion"
01350 );
01351
01352
Status =
NtSetValueKey(
01353 ParentHandle,
01354 &ValueName,
01355 TITLE_INDEX_VALUE,
01356 REG_MULTI_SZ,
01357 VersionStrings,
01358 VersionsLength
01359 );
01360 }
01361 }
01362 ZwUnmapViewOfSection(NtCurrentProcess(), BaseAddress);
01363 }
01364 ZwClose(SectionHandle);
01365
if (VersionStrings) {
01366
ExFreePool((PVOID)VersionStrings);
01367 }
01368
01369 AllDone:
01370
01371
NtClose (ParentHandle);
01372
01373
01374
01375
01376
01377
#ifdef _WANT_MACHINE_IDENTIFICATION
01378
01379
01380
01381
01382
01383 CmpPerformMachineIdentification(LoaderBlock);
01384
01385
#endif
01386
01387
return STATUS_SUCCESS;
01388 }