2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: ntoskrnl/config/i386/cmhardwr.c
5 * PURPOSE: Configuration Manager - Hardware-Specific Code
6 * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
9 /* INCLUDES ******************************************************************/
15 /* GLOBALS *******************************************************************/
17 PCHAR CmpFullCpuID
= "%s Family %u Model %u Stepping %u";
18 PCHAR CmpBiosStrings
[] =
23 "v0", "v1", "v2", "v3", "v4", "v5", "v6", "v7", "v8", "v9",
24 "v 0", "v 1", "v 2", "v 3", "v 4", "v 5", "v 6", "v 7", "v 8", "v 9",
28 PCHAR CmpBiosBegin
, CmpBiosSearchStart
, CmpBiosSearchEnd
;
30 /* FUNCTIONS *****************************************************************/
34 CmpGetBiosDate(IN PCHAR BiosStart
,
39 CHAR LastDate
[11] = {0}, CurrentDate
[11];
42 /* Skip the signature and the magic, and loop the BIOS ROM */
44 pp
= BiosStart
+ BiosLength
- 5;
47 /* Check for xx/yy/zz which we assume to be a date */
56 /* Copy the string proper */
57 RtlMoveMemory(&CurrentDate
[5], p
- 2, 5);
59 /* Add a 0 if the month only has one digit */
60 if (!isdigit(CurrentDate
[5])) CurrentDate
[5] = '0';
62 /* Now copy the year */
63 CurrentDate
[2] = p
[4];
64 CurrentDate
[3] = p
[5];
65 CurrentDate
[4] = CurrentDate
[7] = CurrentDate
[10] = ANSI_NULL
;
67 /* If the date comes from the BIOS, check if it's a 4-digit year */
71 ((RtlEqualMemory(&p
[4], "19", 2)) ||
72 (RtlEqualMemory(&p
[4], "20", 2))))
74 /* Copy the year proper */
75 CurrentDate
[0] = p
[4];
76 CurrentDate
[1] = p
[5];
77 CurrentDate
[2] = p
[6];
78 CurrentDate
[3] = p
[7];
82 /* Otherwise, we'll just assume anything under 80 is 2000 */
83 if (strtoul(&CurrentDate
[2], NULL
, 10) < 80)
85 /* Hopefully your BIOS wasn't made in 1979 */
91 /* Anything over 80, was probably made in the 1900s... */
97 /* Add slashes where we previously had NULLs */
98 CurrentDate
[4] = CurrentDate
[7] = '/';
100 /* Check which date is newer */
101 if (memcmp(LastDate
, CurrentDate
, 10) < 0)
103 /* Found a newer date, select it */
104 RtlMoveMemory(LastDate
, CurrentDate
, 10);
112 /* Make sure we found a date */
115 /* Copy the year at the pp, and keep only the last two digits */
116 RtlMoveMemory(BiosDate
, &LastDate
[5], 5);
118 BiosDate
[6] = LastDate
[2];
119 BiosDate
[7] = LastDate
[3];
120 BiosDate
[8] = ANSI_NULL
;
124 /* No date found, return empty string */
125 BiosDate
[0] = ANSI_NULL
;
131 CmpGetBiosVersion(IN PCHAR BiosStart
,
133 IN PCHAR BiosVersion
)
139 /* Check if we were given intitial data for the search */
142 /* Save it for later use */
143 CmpBiosBegin
= BiosStart
;
144 CmpBiosSearchStart
= BiosStart
+ 1;
145 CmpBiosSearchEnd
= BiosStart
+ BiosLength
- 2;
148 /* Now loop the BIOS area */
151 /* Start an initial search looking for numbers and periods */
153 while (CmpBiosSearchStart
<= CmpBiosSearchEnd
)
155 /* Check if we have an "x.y" version string */
156 if ((*CmpBiosSearchStart
== '.') &&
157 (*(CmpBiosSearchStart
+ 1) >= '0') &&
158 (*(CmpBiosSearchStart
+ 1) <= '9') &&
159 (*(CmpBiosSearchStart
- 1) >= '0') &&
160 (*(CmpBiosSearchStart
- 1) <= '9'))
162 /* Start looking in this area for the actual BIOS Version */
163 pp
= CmpBiosSearchStart
;
169 CmpBiosSearchStart
++;
173 /* Break out if we're went past the BIOS area */
174 if (CmpBiosSearchStart
> CmpBiosSearchEnd
) return FALSE
;
176 /* Move to the next 2 bytes */
177 CmpBiosSearchStart
+= 2;
179 /* Null-terminate our scratch buffer and start the string here */
180 Buffer
[127] = ANSI_NULL
;
183 /* Go back one character since we're doing this backwards */
186 /* Loop the identifier we found as long as it's valid */
188 while ((i
++ < 127) &&
189 (pp
>= CmpBiosBegin
) &&
193 /* Copy the character */
197 /* Go past the last character since we went backwards */
200 /* Loop the strings we recognize */
201 for (i
= 0; CmpBiosStrings
[i
]; i
++)
203 /* Check if a match was found */
204 if (strstr(p
, CmpBiosStrings
[i
])) goto Match
;
209 /* Skip until we find a space */
210 for (; *pp
== ' '; pp
++);
212 /* Loop the final string */
216 /* Copy the character into the final string */
217 BiosVersion
[i
] = *pp
++;
218 } while ((++i
< 127) &&
219 (pp
<= (CmpBiosSearchEnd
+ 1)) &&
223 /* Null-terminate the version string */
224 BiosVersion
[i
] = ANSI_NULL
;
230 CmpInitializeMachineDependentConfiguration(IN PLOADER_PARAMETER_BLOCK LoaderBlock
)
232 UNICODE_STRING KeyName
, ValueName
, Data
, SectionName
;
233 OBJECT_ATTRIBUTES ObjectAttributes
;
234 ULONG HavePae
, Length
, TotalLength
= 0, i
, Disposition
;
237 HANDLE KeyHandle
, BiosHandle
, SystemHandle
, FpuHandle
, SectionHandle
;
238 CONFIGURATION_COMPONENT_DATA ConfigData
;
243 USHORT IndexTable
[MaximumType
+ 1] = {0};
244 ANSI_STRING TempString
;
245 PCHAR PartialString
= NULL
, BiosVersion
;
247 PVOID BaseAddress
= NULL
;
248 LARGE_INTEGER ViewBase
= {{0, 0}};
249 ULONG_PTR VideoRomBase
;
250 PCHAR CurrentVersion
;
251 extern UNICODE_STRING KeRosProcessorName
, KeRosBiosDate
, KeRosBiosVersion
;
252 extern UNICODE_STRING KeRosVideoBiosDate
, KeRosVideoBiosVersion
;
254 /* Open the SMSS Memory Management key */
255 RtlInitUnicodeString(&KeyName
,
256 L
"\\Registry\\Machine\\SYSTEM\\CurrentControlSet\\"
257 L
"Control\\Session Manager\\Memory Management");
258 InitializeObjectAttributes(&ObjectAttributes
,
260 OBJ_CASE_INSENSITIVE
,
263 Status
= NtOpenKey(&KeyHandle
, KEY_READ
| KEY_WRITE
, &ObjectAttributes
);
264 if (NT_SUCCESS(Status
))
266 /* Detect if PAE is enabled */
267 HavePae
= SharedUserData
->ProcessorFeatures
[PF_PAE_ENABLED
];
270 RtlInitUnicodeString(&ValueName
, L
"PhysicalAddressExtension");
271 NtSetValueKey(KeyHandle
,
282 /* Open the hardware description key */
283 RtlInitUnicodeString(&KeyName
,
284 L
"\\Registry\\Machine\\Hardware\\Description\\System");
285 InitializeObjectAttributes(&ObjectAttributes
,
287 OBJ_CASE_INSENSITIVE
,
290 Status
= NtOpenKey(&SystemHandle
, KEY_READ
| KEY_WRITE
, &ObjectAttributes
);
291 if (!NT_SUCCESS(Status
))
294 /* Create the BIOS Information key */
295 RtlInitUnicodeString(&KeyName
,
296 L
"\\Registry\\Machine\\SYSTEM\\CurrentControlSet\\"
297 L
"Control\\BIOSINFO");
298 InitializeObjectAttributes(&ObjectAttributes
,
300 OBJ_CASE_INSENSITIVE
,
303 Status
= NtCreateKey(&BiosHandle
,
308 REG_OPTION_NON_VOLATILE
,
310 if (!NT_SUCCESS(Status
))
312 NtClose(SystemHandle
);
316 /* Create the CPU Key, and check if it already existed */
317 RtlInitUnicodeString(&KeyName
, L
"CentralProcessor");
318 InitializeObjectAttributes(&ObjectAttributes
,
320 OBJ_CASE_INSENSITIVE
,
323 Status
= NtCreateKey(&KeyHandle
,
324 KEY_READ
| KEY_WRITE
,
332 /* The key shouldn't already exist */
333 if (Disposition
== REG_CREATED_NEW_KEY
)
335 /* Allocate the configuration data for cmconfig.c */
336 CmpConfigurationData
= ExAllocatePoolWithTag(PagedPool
,
337 CmpConfigurationAreaSize
,
339 if (!CmpConfigurationData
)
341 // FIXME: Cleanup stuff!!
342 return STATUS_INSUFFICIENT_RESOURCES
;
346 for (i
= 0; i
< KeNumberProcessors
; i
++)
352 Prcb
= KiProcessorBlock
[i
];
354 /* Setup the Configuration Entry for the Processor */
355 RtlZeroMemory(&ConfigData
, sizeof(ConfigData
));
356 ConfigData
.ComponentEntry
.Class
= ProcessorClass
;
357 ConfigData
.ComponentEntry
.Type
= CentralProcessor
;
358 ConfigData
.ComponentEntry
.Key
= i
;
359 ConfigData
.ComponentEntry
.AffinityMask
= AFFINITY_MASK(i
);
360 ConfigData
.ComponentEntry
.Identifier
= Buffer
;
363 /* Check if the CPU doesn't support CPUID */
366 /* Build 80x86-style string for older CPUs */
370 (Prcb
->CpuStep
>> 8) + 'A',
371 Prcb
->CpuStep
& 0xff);
375 /* Build full ID string for newer CPUs */
380 (Prcb
->CpuStep
>> 8),
381 Prcb
->CpuStep
& 0xff);
383 #elif defined(_M_AMD64)
384 if (Prcb
->CpuVendor
== CPU_VIA
)
386 /* This is VIA64 family */
389 else if (Prcb
->CpuVendor
== CPU_AMD
)
391 /* This is AMD64 family */
396 /* This is generic EM64T family */
400 /* ID string has the same style for all 64-bit CPUs */
405 (Prcb
->CpuStep
>> 8),
406 Prcb
->CpuStep
& 0xff);
408 #error Unknown architecture
411 /* Save the ID string length now that we've created it */
412 ConfigData
.ComponentEntry
.IdentifierLength
= (ULONG
)strlen(Buffer
) + 1;
414 /* Initialize the registry configuration node for it */
415 Status
= CmpInitializeRegistryNode(&ConfigData
,
418 InterfaceTypeUndefined
,
421 if (!NT_SUCCESS(Status
))
424 NtClose(SystemHandle
);
428 /* Check if we have an FPU */
429 if (KeI386NpxPresent
)
431 /* Setup the Configuration Entry for the FPU */
432 RtlZeroMemory(&ConfigData
, sizeof(ConfigData
));
433 ConfigData
.ComponentEntry
.Class
= ProcessorClass
;
434 ConfigData
.ComponentEntry
.Type
= FloatingPointProcessor
;
435 ConfigData
.ComponentEntry
.Key
= i
;
436 ConfigData
.ComponentEntry
.AffinityMask
= AFFINITY_MASK(i
);
437 ConfigData
.ComponentEntry
.Identifier
= Buffer
;
439 /* For 386 cpus, the CPU pp is the identifier */
440 if (Prcb
->CpuType
== 3) strcpy(Buffer
, "80387");
442 /* Save the ID string length now that we've created it */
443 ConfigData
.ComponentEntry
.IdentifierLength
= (ULONG
)strlen(Buffer
) + 1;
445 /* Initialize the registry configuration node for it */
446 Status
= CmpInitializeRegistryNode(&ConfigData
,
449 InterfaceTypeUndefined
,
452 if (!NT_SUCCESS(Status
))
454 /* We failed, close all the opened handles and return */
457 NtClose(SystemHandle
);
461 /* Close this new handle */
464 /* Stay on this CPU only */
465 KeSetSystemAffinityThread(Prcb
->SetMember
);
468 /* Uh oh, no CPUID! Should not happen as we don't support 80386 and older 80486 */
473 /* Check if we have extended CPUID that supports name ID */
474 KiCpuId(&CpuInfo
, 0x80000000);
475 ExtendedId
= CpuInfo
.Eax
;
476 if (ExtendedId
>= 0x80000004)
478 /* Do all the CPUIDs required to get the full name */
479 PartialString
= CpuString
;
480 for (ExtendedId
= 2; ExtendedId
<= 4; ExtendedId
++)
482 /* Do the CPUID and save the name string */
483 KiCpuId(&CpuInfo
, 0x80000000 | ExtendedId
);
484 ((PULONG
)PartialString
)[0] = CpuInfo
.Eax
;
485 ((PULONG
)PartialString
)[1] = CpuInfo
.Ebx
;
486 ((PULONG
)PartialString
)[2] = CpuInfo
.Ecx
;
487 ((PULONG
)PartialString
)[3] = CpuInfo
.Edx
;
489 /* Go to the next name string */
493 /* Null-terminate it */
494 CpuString
[47] = ANSI_NULL
;
498 /* Go back to user affinity */
499 KeRevertToUserAffinityThread();
501 /* Check if we have a CPU Name */
504 /* Convert it to Unicode */
505 RtlInitAnsiString(&TempString
, CpuString
);
506 if (NT_SUCCESS(RtlAnsiStringToUnicodeString(&Data
, &TempString
, TRUE
)))
508 /* Add it to the registry */
509 RtlInitUnicodeString(&ValueName
, L
"ProcessorNameString");
510 Status
= NtSetValueKey(KeyHandle
,
515 Data
.Length
+ sizeof(UNICODE_NULL
));
517 /* ROS: Save a copy for Jira reporting */
518 if (!RtlCreateUnicodeString(&KeRosProcessorName
, Data
.Buffer
))
520 /* Do not fail for this */
521 KeRosProcessorName
.Length
= 0;
524 /* Free the temporary buffer */
525 RtlFreeUnicodeString(&Data
);
529 /* Check if we had a Vendor ID */
530 if (Prcb
->VendorString
[0])
532 /* Convert it to Unicode */
533 RtlInitAnsiString(&TempString
, Prcb
->VendorString
);
534 if (NT_SUCCESS(RtlAnsiStringToUnicodeString(&Data
, &TempString
, TRUE
)))
536 /* Add it to the registry */
537 RtlInitUnicodeString(&ValueName
, L
"VendorIdentifier");
538 Status
= NtSetValueKey(KeyHandle
,
543 Data
.Length
+ sizeof(UNICODE_NULL
));
545 /* Free the temporary buffer */
546 RtlFreeUnicodeString(&Data
);
550 /* Check if we have features bits */
551 if (Prcb
->FeatureBits
)
553 /* Add them to the registry */
554 RtlInitUnicodeString(&ValueName
, L
"FeatureSet");
555 Status
= NtSetValueKey(KeyHandle
,
560 sizeof(Prcb
->FeatureBits
));
563 /* Check if we detected the CPU Speed */
566 /* Add it to the registry */
567 RtlInitUnicodeString(&ValueName
, L
"~MHz");
568 Status
= NtSetValueKey(KeyHandle
,
576 /* Check if we have an update signature */
577 if (Prcb
->UpdateSignature
.QuadPart
)
579 /* Add it to the registry */
580 RtlInitUnicodeString(&ValueName
, L
"Update Signature");
581 Status
= NtSetValueKey(KeyHandle
,
585 &Prcb
->UpdateSignature
,
586 sizeof(Prcb
->UpdateSignature
));
589 /* Close the processor handle */
592 /* FIXME: Detect CPU mismatches */
596 /* Free the configuration data */
597 ExFreePoolWithTag(CmpConfigurationData
, TAG_CM
);
600 /* Open physical memory */
601 RtlInitUnicodeString(&SectionName
, L
"\\Device\\PhysicalMemory");
602 InitializeObjectAttributes(&ObjectAttributes
,
604 OBJ_CASE_INSENSITIVE
,
607 Status
= ZwOpenSection(&SectionHandle
,
610 if (!NT_SUCCESS(Status
))
612 /* We failed, close all the opened handles and return */
613 // NtClose(KeyHandle);
615 NtClose(SystemHandle
);
616 /* 'Quickie' closes KeyHandle */
620 /* Map the first 1KB of memory to get the IVT */
621 ViewSize
= PAGE_SIZE
;
622 Status
= ZwMapViewOfSection(SectionHandle
,
632 if (!NT_SUCCESS(Status
))
635 VideoRomBase
= 0xC0000;
639 /* Calculate the base address from the vector */
640 VideoRomBase
= (*((PULONG
)BaseAddress
+ 0x10) >> 12) & 0xFFFF0;
641 VideoRomBase
+= *((PULONG
)BaseAddress
+ 0x10) & 0xFFF0;
643 /* Now get to the actual ROM Start and make sure it's not invalid*/
644 VideoRomBase
&= 0xFFFF8000;
645 if (VideoRomBase
< 0xC0000) VideoRomBase
= 0xC0000;
647 /* And unmap the section */
648 ZwUnmapViewOfSection(NtCurrentProcess(), BaseAddress
);
651 /* Allocate BIOS Version pp Buffer */
652 BiosVersion
= ExAllocatePoolWithTag(PagedPool
, PAGE_SIZE
, TAG_CM
);
654 /* Setup settings to map the 64K BIOS ROM */
656 ViewSize
= 16 * PAGE_SIZE
;
657 ViewBase
.LowPart
= 0xF0000;
658 ViewBase
.HighPart
= 0;
661 Status
= ZwMapViewOfSection(SectionHandle
,
671 if (NT_SUCCESS(Status
))
673 /* Scan the ROM to get the BIOS Date */
674 if (CmpGetBiosDate(BaseAddress
, 16 * PAGE_SIZE
, Buffer
, TRUE
))
676 /* Convert it to Unicode */
677 RtlInitAnsiString(&TempString
, Buffer
);
678 if (NT_SUCCESS(RtlAnsiStringToUnicodeString(&Data
, &TempString
, TRUE
)))
680 /* Write the date into the registry */
681 RtlInitUnicodeString(&ValueName
, L
"SystemBiosDate");
682 Status
= NtSetValueKey(SystemHandle
,
687 Data
.Length
+ sizeof(UNICODE_NULL
));
689 /* Free the string */
690 RtlFreeUnicodeString(&Data
);
695 /* Get the BIOS Date Identifier */
696 RtlCopyMemory(Buffer
, (PCHAR
)BaseAddress
+ (16 * PAGE_SIZE
- 11), 8);
697 Buffer
[8] = ANSI_NULL
;
699 /* Convert it to unicode */
700 RtlInitAnsiString(&TempString
, Buffer
);
701 Status
= RtlAnsiStringToUnicodeString(&Data
, &TempString
, TRUE
);
702 if (NT_SUCCESS(Status
))
704 /* Save it to the registry */
705 Status
= NtSetValueKey(BiosHandle
,
710 Data
.Length
+ sizeof(UNICODE_NULL
));
712 /* ROS: Save a copy for Jira reporting */
713 if (!RtlCreateUnicodeString(&KeRosBiosDate
, Data
.Buffer
))
714 KeRosBiosDate
.Length
= 0;
716 /* Free the string */
717 RtlFreeUnicodeString(&Data
);
720 /* Close the bios information handle */
725 /* Get the BIOS Version */
726 if (CmpGetBiosVersion(BaseAddress
, 16 * PAGE_SIZE
, Buffer
))
728 /* Start at the beginning of our buffer */
729 CurrentVersion
= BiosVersion
;
732 /* Convert to Unicode */
733 RtlInitAnsiString(&TempString
, Buffer
);
734 Status
= RtlAnsiStringToUnicodeString(&Data
, &TempString
, TRUE
);
735 if (!NT_SUCCESS(Status
))
738 /* Calculate the length of this string and copy it in */
739 Length
= Data
.Length
+ sizeof(UNICODE_NULL
);
740 RtlMoveMemory(CurrentVersion
, Data
.Buffer
, Length
);
742 /* Free the unicode string */
743 RtlFreeUnicodeString(&Data
);
745 /* Update the total length and see if we're out of space */
746 TotalLength
+= Length
;
747 if (TotalLength
+ 256 + sizeof(UNICODE_NULL
) > PAGE_SIZE
)
749 /* One more string would push us out, so stop here */
753 /* Go to the next string inside the multi-string buffer */
754 CurrentVersion
+= Length
;
756 /* Query the next BIOS Version */
757 } while (CmpGetBiosVersion(NULL
, 0, Buffer
));
759 /* Check if we found any strings at all */
762 /* Add the final null-terminator */
763 *(PWSTR
)CurrentVersion
= UNICODE_NULL
;
764 TotalLength
+= sizeof(UNICODE_NULL
);
766 /* Write the BIOS Version to the registry */
767 RtlInitUnicodeString(&ValueName
, L
"SystemBiosVersion");
768 Status
= NtSetValueKey(SystemHandle
,
775 /* ROS: Save a copy for Jira reporting */
776 if (!RtlCreateUnicodeString(&KeRosBiosVersion
, (PWCH
)BiosVersion
))
777 KeRosBiosVersion
.Length
= 0;
781 /* Unmap the section */
782 ZwUnmapViewOfSection(NtCurrentProcess(), BaseAddress
);
785 /* Now prepare for Video BIOS Mapping of 32KB */
787 ViewSize
= 8 * PAGE_SIZE
;
788 ViewBase
.QuadPart
= VideoRomBase
;
791 Status
= ZwMapViewOfSection(SectionHandle
,
801 if (NT_SUCCESS(Status
))
803 /* Scan the ROM to get the BIOS Date */
804 if (CmpGetBiosDate(BaseAddress
, 8 * PAGE_SIZE
, Buffer
, FALSE
))
806 /* Convert it to Unicode */
807 RtlInitAnsiString(&TempString
, Buffer
);
808 if (NT_SUCCESS(RtlAnsiStringToUnicodeString(&Data
, &TempString
, TRUE
)))
810 /* Write the date into the registry */
811 RtlInitUnicodeString(&ValueName
, L
"VideoBiosDate");
812 Status
= NtSetValueKey(SystemHandle
,
817 Data
.Length
+ sizeof(UNICODE_NULL
));
819 /* ROS: Save a copy for Jira reporting */
820 if (!RtlCreateUnicodeString(&KeRosVideoBiosDate
, Data
.Buffer
))
821 KeRosVideoBiosDate
.Length
= 0;
823 /* Free the string */
824 RtlFreeUnicodeString(&Data
);
828 /* Get the Video BIOS Version */
829 if (CmpGetBiosVersion(BaseAddress
, 8 * PAGE_SIZE
, Buffer
))
831 /* Start at the beginning of our buffer */
832 CurrentVersion
= BiosVersion
;
835 /* Convert to Unicode */
836 RtlInitAnsiString(&TempString
, Buffer
);
837 if (!NT_SUCCESS(RtlAnsiStringToUnicodeString(&Data
, &TempString
, TRUE
)))
840 /* Calculate the length of this string and copy it in */
841 Length
= Data
.Length
+ sizeof(UNICODE_NULL
);
842 RtlMoveMemory(CurrentVersion
, Data
.Buffer
, Length
);
844 /* Free the unicode string */
845 RtlFreeUnicodeString(&Data
);
847 /* Update the total length and see if we're out of space */
848 TotalLength
+= Length
;
849 if (TotalLength
+ 256 + sizeof(UNICODE_NULL
) > PAGE_SIZE
)
851 /* One more string would push us out, so stop here */
855 /* Go to the next string inside the multi-string buffer */
856 CurrentVersion
+= Length
;
858 /* Query the next BIOS Version */
859 } while (CmpGetBiosVersion(NULL
, 0, Buffer
));
861 /* Check if we found any strings at all */
864 /* Add the final null-terminator */
865 *(PWSTR
)CurrentVersion
= UNICODE_NULL
;
866 TotalLength
+= sizeof(UNICODE_NULL
);
868 /* Write the BIOS Version to the registry */
869 RtlInitUnicodeString(&ValueName
, L
"VideoBiosVersion");
870 Status
= NtSetValueKey(SystemHandle
,
877 /* ROS: Save a copy for Jira reporting */
878 if (!RtlCreateUnicodeString(&KeRosVideoBiosVersion
, (PWCH
)BiosVersion
))
879 KeRosVideoBiosVersion
.Length
= 0;
883 /* Unmap the section */
884 ZwUnmapViewOfSection(NtCurrentProcess(), BaseAddress
);
887 /* Close the section */
888 ZwClose(SectionHandle
);
890 /* Free the BIOS version string buffer */
891 if (BiosVersion
) ExFreePoolWithTag(BiosVersion
, TAG_CM
);
894 /* Close the processor handle */
896 return STATUS_SUCCESS
;