xref: /reactos/ntoskrnl/config/i386/cmhardwr.c (revision 30369b80)
1 /*
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)
7  */
8 
9 /* INCLUDES ******************************************************************/
10 
11 #include "ntoskrnl.h"
12 #define NDEBUG
13 #include "debug.h"
14 
15 /* GLOBALS *******************************************************************/
16 
17 PCHAR CmpFullCpuID = "%s Family %u Model %u Stepping %u";
18 PCHAR CmpBiosStrings[] =
19 {
20     "Ver",
21     "Rev",
22     "Rel",
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",
25     NULL
26 };
27 
28 PCHAR CmpBiosBegin, CmpBiosSearchStart, CmpBiosSearchEnd;
29 
30 /* FUNCTIONS *****************************************************************/
31 
32 BOOLEAN
33 NTAPI
CmpGetBiosDate(IN PCHAR BiosStart,IN ULONG BiosLength,IN PCHAR BiosDate,IN BOOLEAN FromBios)34 CmpGetBiosDate(IN PCHAR BiosStart,
35                IN ULONG BiosLength,
36                IN PCHAR BiosDate,
37                IN BOOLEAN FromBios)
38 {
39     CHAR LastDate[11] = {0}, CurrentDate[11];
40     PCHAR p, pp;
41 
42     /* Skip the signature and the magic, and loop the BIOS ROM */
43     p = BiosStart + 2;
44     pp = BiosStart + BiosLength - 5;
45     while (p < pp)
46     {
47         /* Check for xx/yy/zz which we assume to be a date */
48         if ((p[0] == '/') &&
49             (p[3] == '/') &&
50             (isdigit(p[-1])) &&
51             (isdigit(p[1])) &&
52             (isdigit(p[2])) &&
53             (isdigit(p[4])) &&
54             (isdigit(p[5])))
55         {
56             /* Copy the string proper */
57             RtlMoveMemory(&CurrentDate[5], p - 2, 5);
58 
59             /* Add a 0 if the month only has one digit */
60             if (!isdigit(CurrentDate[5])) CurrentDate[5] = '0';
61 
62             /* Now copy the year */
63             CurrentDate[2] = p[4];
64             CurrentDate[3] = p[5];
65             CurrentDate[4] = CurrentDate[7] = CurrentDate[10] = ANSI_NULL;
66 
67             /* If the date comes from the BIOS, check if it's a 4-digit year */
68             if ((FromBios) &&
69                 (isdigit(p[6])) &&
70                 (isdigit(p[7])) &&
71                 ((RtlEqualMemory(&p[4], "19", 2)) ||
72                  (RtlEqualMemory(&p[4], "20", 2))))
73             {
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];
79             }
80             else
81             {
82                 /* Otherwise, we'll just assume anything under 80 is 2000 */
83                 if (strtoul(&CurrentDate[2], NULL, 10) < 80)
84                 {
85                     /* Hopefully your BIOS wasn't made in 1979 */
86                     CurrentDate[0] = '2';
87                     CurrentDate[1] = '0';
88                 }
89                 else
90                 {
91                     /* Anything over 80, was probably made in the 1900s... */
92                     CurrentDate[0] = '1';
93                     CurrentDate[1] = '9';
94                 }
95             }
96 
97             /* Add slashes where we previously had NULLs */
98             CurrentDate[4] = CurrentDate[7] = '/';
99 
100             /* Check which date is newer */
101             if (memcmp(LastDate, CurrentDate, 10) < 0)
102             {
103                 /* Found a newer date, select it */
104                 RtlMoveMemory(LastDate, CurrentDate, 10);
105             }
106 
107             p += 2;
108         }
109         p++;
110     }
111 
112     /* Make sure we found a date */
113     if (LastDate[0])
114     {
115         /* Copy the year at the pp, and keep only the last two digits */
116         RtlMoveMemory(BiosDate, &LastDate[5], 5);
117         BiosDate[5] = '/';
118         BiosDate[6] = LastDate[2];
119         BiosDate[7] = LastDate[3];
120         BiosDate[8] = ANSI_NULL;
121         return TRUE;
122     }
123 
124     /* No date found, return empty string */
125     BiosDate[0] = ANSI_NULL;
126     return FALSE;
127 }
128 
129 BOOLEAN
130 NTAPI
CmpGetBiosVersion(IN PCHAR BiosStart,IN ULONG BiosLength,IN PCHAR BiosVersion)131 CmpGetBiosVersion(IN PCHAR BiosStart,
132                   IN ULONG BiosLength,
133                   IN PCHAR BiosVersion)
134 {
135     CHAR Buffer[128];
136     PCHAR p, pp;
137     USHORT i;
138 
139     /* Check if we were given intitial data for the search */
140     if (BiosStart)
141     {
142         /* Save it for later use */
143         CmpBiosBegin = BiosStart;
144         CmpBiosSearchStart = BiosStart + 1;
145         CmpBiosSearchEnd = BiosStart + BiosLength - 2;
146     }
147 
148     /* Now loop the BIOS area */
149     for (;;)
150     {
151         /* Start an initial search looking for numbers and periods */
152         pp = NULL;
153         while (CmpBiosSearchStart <= CmpBiosSearchEnd)
154         {
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'))
161             {
162                 /* Start looking in this area for the actual BIOS Version */
163                 pp = CmpBiosSearchStart;
164                 break;
165             }
166             else
167             {
168                 /* Keep searching */
169                 CmpBiosSearchStart++;
170             }
171         }
172 
173         /* Break out if we're went past the BIOS area */
174         if (CmpBiosSearchStart > CmpBiosSearchEnd) return FALSE;
175 
176         /* Move to the next 2 bytes */
177         CmpBiosSearchStart += 2;
178 
179         /* Null-terminate our scratch buffer and start the string here */
180         Buffer[127] = ANSI_NULL;
181         p = &Buffer[127];
182 
183         /* Go back one character since we're doing this backwards */
184         pp--;
185 
186         /* Loop the identifier we found as long as it's valid */
187         i = 0;
188         while ((i++ < 127) &&
189                (pp >= CmpBiosBegin) &&
190                (*pp >= ' ') &&
191                (*pp != '$'))
192         {
193             /* Copy the character */
194             *--p = *pp--;
195         }
196 
197         /* Go past the last character since we went backwards */
198         pp++;
199 
200         /* Loop the strings we recognize */
201         for (i = 0; CmpBiosStrings[i]; i++)
202         {
203             /* Check if a match was found */
204             if (strstr(p, CmpBiosStrings[i])) goto Match;
205         }
206     }
207 
208 Match:
209     /* Skip until we find a space */
210     for (; *pp == ' '; pp++);
211 
212     /* Loop the final string */
213     i = 0;
214     do
215     {
216         /* Copy the character into the final string */
217         BiosVersion[i] = *pp++;
218     } while ((++i < 127) &&
219              (pp <= (CmpBiosSearchEnd + 1)) &&
220              (*pp >= ' ') &&
221              (*pp != '$'));
222 
223     /* Null-terminate the version string */
224     BiosVersion[i] = ANSI_NULL;
225     return TRUE;
226 }
227 
228 NTSTATUS
229 NTAPI
CmpInitializeMachineDependentConfiguration(IN PLOADER_PARAMETER_BLOCK LoaderBlock)230 CmpInitializeMachineDependentConfiguration(IN PLOADER_PARAMETER_BLOCK LoaderBlock)
231 {
232     UNICODE_STRING KeyName, ValueName, Data, SectionName;
233     OBJECT_ATTRIBUTES ObjectAttributes;
234     ULONG HavePae, Length, TotalLength = 0, i, Disposition;
235     SIZE_T ViewSize;
236     NTSTATUS Status;
237     HANDLE KeyHandle, BiosHandle, SystemHandle, FpuHandle, SectionHandle;
238     CONFIGURATION_COMPONENT_DATA ConfigData;
239     CHAR Buffer[128];
240     CPU_INFO CpuInfo;
241     ULONG ExtendedId;
242     PKPRCB Prcb;
243     USHORT IndexTable[MaximumType + 1] = {0};
244     ANSI_STRING TempString;
245     PCHAR PartialString = NULL, BiosVersion;
246     CHAR CpuString[48];
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;
253 
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,
259                                &KeyName,
260                                OBJ_CASE_INSENSITIVE,
261                                NULL,
262                                NULL);
263     Status = NtOpenKey(&KeyHandle, KEY_READ | KEY_WRITE, &ObjectAttributes);
264     if (NT_SUCCESS(Status))
265     {
266         /* Detect if PAE is enabled */
267         HavePae = SharedUserData->ProcessorFeatures[PF_PAE_ENABLED];
268 
269         /* Set the value */
270         RtlInitUnicodeString(&ValueName, L"PhysicalAddressExtension");
271         NtSetValueKey(KeyHandle,
272                       &ValueName,
273                       0,
274                       REG_DWORD,
275                       &HavePae,
276                       sizeof(HavePae));
277 
278         /* Close the key */
279         NtClose(KeyHandle);
280     }
281 
282     /* Open the hardware description key */
283     RtlInitUnicodeString(&KeyName,
284                          L"\\Registry\\Machine\\Hardware\\Description\\System");
285     InitializeObjectAttributes(&ObjectAttributes,
286                                &KeyName,
287                                OBJ_CASE_INSENSITIVE,
288                                NULL,
289                                NULL);
290     Status = NtOpenKey(&SystemHandle, KEY_READ | KEY_WRITE, &ObjectAttributes);
291     if (!NT_SUCCESS(Status))
292         return Status;
293 
294     /* Create the BIOS Information key */
295     RtlInitUnicodeString(&KeyName,
296                          L"\\Registry\\Machine\\SYSTEM\\CurrentControlSet\\"
297                          L"Control\\BIOSINFO");
298     InitializeObjectAttributes(&ObjectAttributes,
299                                &KeyName,
300                                OBJ_CASE_INSENSITIVE,
301                                NULL,
302                                NULL);
303     Status = NtCreateKey(&BiosHandle,
304                          KEY_ALL_ACCESS,
305                          &ObjectAttributes,
306                          0,
307                          NULL,
308                          REG_OPTION_NON_VOLATILE,
309                          &Disposition);
310     if (!NT_SUCCESS(Status))
311     {
312         NtClose(SystemHandle);
313         return Status;
314     }
315 
316     /* Create the CPU Key, and check if it already existed */
317     RtlInitUnicodeString(&KeyName, L"CentralProcessor");
318     InitializeObjectAttributes(&ObjectAttributes,
319                                &KeyName,
320                                OBJ_CASE_INSENSITIVE,
321                                SystemHandle,
322                                NULL);
323     Status = NtCreateKey(&KeyHandle,
324                          KEY_READ | KEY_WRITE,
325                          &ObjectAttributes,
326                          0,
327                          NULL,
328                          0,
329                          &Disposition);
330     NtClose(KeyHandle);
331 
332     /* The key shouldn't already exist */
333     if (Disposition == REG_CREATED_NEW_KEY)
334     {
335         /* Allocate the configuration data for cmconfig.c */
336         CmpConfigurationData = ExAllocatePoolWithTag(PagedPool,
337                                                      CmpConfigurationAreaSize,
338                                                      TAG_CM);
339         if (!CmpConfigurationData)
340         {
341             // FIXME: Cleanup stuff!!
342             return STATUS_INSUFFICIENT_RESOURCES;
343         }
344 
345         /* Loop all CPUs */
346         for (i = 0; i < KeNumberProcessors; i++)
347         {
348 #ifdef _M_AMD64
349             PCHAR FamilyId;
350 #endif
351             /* Get the PRCB */
352             Prcb = KiProcessorBlock[i];
353 
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;
361 
362 #if defined(_M_IX86)
363             /* Check if the CPU doesn't support CPUID */
364             if (!Prcb->CpuID)
365             {
366                 /* Build 80x86-style string for older CPUs */
367                 sprintf(Buffer,
368                         "80%u86-%c%x",
369                         Prcb->CpuType,
370                         (Prcb->CpuStep >> 8) + 'A',
371                         Prcb->CpuStep & 0xff);
372             }
373             else
374             {
375                 /* Build full ID string for newer CPUs */
376                 sprintf(Buffer,
377                         CmpFullCpuID,
378                         "x86",
379                         Prcb->CpuType,
380                         (Prcb->CpuStep >> 8),
381                         Prcb->CpuStep & 0xff);
382             }
383 #elif defined(_M_AMD64)
384             if (Prcb->CpuVendor == CPU_VIA)
385             {
386                 /* This is VIA64 family */
387                 FamilyId = "VIA64";
388             }
389             else if (Prcb->CpuVendor == CPU_AMD)
390             {
391                 /* This is AMD64 family */
392                 FamilyId = "AMD64";
393             }
394             else
395             {
396                 /* This is generic EM64T family */
397                 FamilyId = "EM64T";
398             }
399 
400             /* ID string has the same style for all 64-bit CPUs */
401             sprintf(Buffer,
402                     CmpFullCpuID,
403                     FamilyId,
404                     Prcb->CpuType,
405                     (Prcb->CpuStep >> 8),
406                     Prcb->CpuStep & 0xff);
407 #else
408 #error Unknown architecture
409 #endif
410 
411             /* Save the ID string length now that we've created it */
412             ConfigData.ComponentEntry.IdentifierLength = (ULONG)strlen(Buffer) + 1;
413 
414             /* Initialize the registry configuration node for it */
415             Status = CmpInitializeRegistryNode(&ConfigData,
416                                                SystemHandle,
417                                                &KeyHandle,
418                                                InterfaceTypeUndefined,
419                                                0xFFFFFFFF,
420                                                IndexTable);
421             if (!NT_SUCCESS(Status))
422             {
423                 NtClose(BiosHandle);
424                 NtClose(SystemHandle);
425                 return Status;
426             }
427 
428             /* Check if we have an FPU */
429             if (KeI386NpxPresent)
430             {
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;
438 
439                 /* For 386 cpus, the CPU pp is the identifier */
440                 if (Prcb->CpuType == 3) strcpy(Buffer, "80387");
441 
442                 /* Save the ID string length now that we've created it */
443                 ConfigData.ComponentEntry.IdentifierLength = (ULONG)strlen(Buffer) + 1;
444 
445                 /* Initialize the registry configuration node for it */
446                 Status = CmpInitializeRegistryNode(&ConfigData,
447                                                    SystemHandle,
448                                                    &FpuHandle,
449                                                    InterfaceTypeUndefined,
450                                                    0xFFFFFFFF,
451                                                    IndexTable);
452                 if (!NT_SUCCESS(Status))
453                 {
454                     /* We failed, close all the opened handles and return */
455                     NtClose(KeyHandle);
456                     NtClose(BiosHandle);
457                     NtClose(SystemHandle);
458                     return Status;
459                 }
460 
461                 /* Close this new handle */
462                 NtClose(FpuHandle);
463 
464                 /* Stay on this CPU only */
465                 KeSetSystemAffinityThread(Prcb->SetMember);
466                 if (!Prcb->CpuID)
467                 {
468                     /* Uh oh, no CPUID! Should not happen as we don't support 80386 and older 80486 */
469                     ASSERT(FALSE);
470                 }
471                 else
472                 {
473                     /* Check if we have extended CPUID that supports name ID */
474                     KiCpuId(&CpuInfo, 0x80000000);
475                     ExtendedId = CpuInfo.Eax;
476                     if (ExtendedId >= 0x80000004)
477                     {
478                         /* Do all the CPUIDs required to get the full name */
479                         PartialString = CpuString;
480                         for (ExtendedId = 2; ExtendedId <= 4; ExtendedId++)
481                         {
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;
488 
489                             /* Go to the next name string */
490                             PartialString += 16;
491                         }
492 
493                         /* Null-terminate it */
494                         CpuString[47] = ANSI_NULL;
495                     }
496                 }
497 
498                 /* Go back to user affinity */
499                 KeRevertToUserAffinityThread();
500 
501                 /* Check if we have a CPU Name */
502                 if (PartialString)
503                 {
504                     /* Convert it to Unicode */
505                     RtlInitAnsiString(&TempString, CpuString);
506                     if (NT_SUCCESS(RtlAnsiStringToUnicodeString(&Data, &TempString, TRUE)))
507                     {
508                         /* Add it to the registry */
509                         RtlInitUnicodeString(&ValueName, L"ProcessorNameString");
510                         Status = NtSetValueKey(KeyHandle,
511                                                &ValueName,
512                                                0,
513                                                REG_SZ,
514                                                Data.Buffer,
515                                                Data.Length + sizeof(UNICODE_NULL));
516 
517                         /* ROS: Save a copy for Jira reporting */
518                         if (!RtlCreateUnicodeString(&KeRosProcessorName, Data.Buffer))
519                         {
520                             /* Do not fail for this */
521                             KeRosProcessorName.Length = 0;
522                         }
523 
524                         /* Free the temporary buffer */
525                         RtlFreeUnicodeString(&Data);
526                     }
527                 }
528 
529                 /* Check if we had a Vendor ID */
530                 if (Prcb->VendorString[0])
531                 {
532                     /* Convert it to Unicode */
533                     RtlInitAnsiString(&TempString, Prcb->VendorString);
534                     if (NT_SUCCESS(RtlAnsiStringToUnicodeString(&Data, &TempString, TRUE)))
535                     {
536                         /* Add it to the registry */
537                         RtlInitUnicodeString(&ValueName, L"VendorIdentifier");
538                         Status = NtSetValueKey(KeyHandle,
539                                                &ValueName,
540                                                0,
541                                                REG_SZ,
542                                                Data.Buffer,
543                                                Data.Length + sizeof(UNICODE_NULL));
544 
545                         /* Free the temporary buffer */
546                         RtlFreeUnicodeString(&Data);
547                     }
548                 }
549 
550                 /* Check if we have features bits */
551                 if (Prcb->FeatureBits)
552                 {
553                     /* Add them to the registry */
554                     RtlInitUnicodeString(&ValueName, L"FeatureSet");
555                     Status = NtSetValueKey(KeyHandle,
556                                            &ValueName,
557                                            0,
558                                            REG_DWORD,
559                                            &Prcb->FeatureBits,
560                                            sizeof(Prcb->FeatureBits));
561                 }
562 
563                 /* Check if we detected the CPU Speed */
564                 if (Prcb->MHz)
565                 {
566                     /* Add it to the registry */
567                     RtlInitUnicodeString(&ValueName, L"~MHz");
568                     Status = NtSetValueKey(KeyHandle,
569                                            &ValueName,
570                                            0,
571                                            REG_DWORD,
572                                            &Prcb->MHz,
573                                            sizeof(Prcb->MHz));
574                 }
575 
576                 /* Check if we have an update signature */
577                 if (Prcb->UpdateSignature.QuadPart)
578                 {
579                     /* Add it to the registry */
580                     RtlInitUnicodeString(&ValueName, L"Update Signature");
581                     Status = NtSetValueKey(KeyHandle,
582                                            &ValueName,
583                                            0,
584                                            REG_BINARY,
585                                            &Prcb->UpdateSignature,
586                                            sizeof(Prcb->UpdateSignature));
587                 }
588 
589                 /* Close the processor handle */
590                 NtClose(KeyHandle);
591 
592                 /* FIXME: Detect CPU mismatches */
593             }
594         }
595 
596         /* Free the configuration data */
597         ExFreePoolWithTag(CmpConfigurationData, TAG_CM);
598     }
599 
600     /* Open physical memory */
601     RtlInitUnicodeString(&SectionName, L"\\Device\\PhysicalMemory");
602     InitializeObjectAttributes(&ObjectAttributes,
603                                &SectionName,
604                                OBJ_CASE_INSENSITIVE,
605                                NULL,
606                                NULL);
607     Status = ZwOpenSection(&SectionHandle,
608                            SECTION_ALL_ACCESS,
609                            &ObjectAttributes);
610     if (!NT_SUCCESS(Status))
611     {
612         /* We failed, close all the opened handles and return */
613         // NtClose(KeyHandle);
614         NtClose(BiosHandle);
615         NtClose(SystemHandle);
616         /* 'Quickie' closes KeyHandle */
617         goto Quickie;
618     }
619 
620     /* Map the first 1KB of memory to get the IVT */
621     ViewSize = PAGE_SIZE;
622     Status = ZwMapViewOfSection(SectionHandle,
623                                 NtCurrentProcess(),
624                                 &BaseAddress,
625                                 0,
626                                 ViewSize,
627                                 &ViewBase,
628                                 &ViewSize,
629                                 ViewUnmap,
630                                 MEM_DOS_LIM,
631                                 PAGE_READWRITE);
632     if (!NT_SUCCESS(Status))
633     {
634         /* Assume default */
635         VideoRomBase = 0xC0000;
636     }
637     else
638     {
639         /* Calculate the base address from the vector */
640         VideoRomBase = (*((PULONG)BaseAddress + 0x10) >> 12) & 0xFFFF0;
641         VideoRomBase += *((PULONG)BaseAddress + 0x10) & 0xFFF0;
642 
643         /* Now get to the actual ROM Start and make sure it's not invalid*/
644         VideoRomBase &= 0xFFFF8000;
645         if (VideoRomBase < 0xC0000) VideoRomBase = 0xC0000;
646 
647         /* And unmap the section */
648         ZwUnmapViewOfSection(NtCurrentProcess(), BaseAddress);
649     }
650 
651     /* Allocate BIOS Version pp Buffer */
652     BiosVersion = ExAllocatePoolWithTag(PagedPool, PAGE_SIZE, TAG_CM);
653 
654     /* Setup settings to map the 64K BIOS ROM */
655     BaseAddress = 0;
656     ViewSize = 16 * PAGE_SIZE;
657     ViewBase.LowPart = 0xF0000;
658     ViewBase.HighPart = 0;
659 
660     /* Map it */
661     Status = ZwMapViewOfSection(SectionHandle,
662                                 NtCurrentProcess(),
663                                 &BaseAddress,
664                                 0,
665                                 ViewSize,
666                                 &ViewBase,
667                                 &ViewSize,
668                                 ViewUnmap,
669                                 MEM_DOS_LIM,
670                                 PAGE_READWRITE);
671     if (NT_SUCCESS(Status))
672     {
673         /* Scan the ROM to get the BIOS Date */
674         if (CmpGetBiosDate(BaseAddress, 16 * PAGE_SIZE, Buffer, TRUE))
675         {
676             /* Convert it to Unicode */
677             RtlInitAnsiString(&TempString, Buffer);
678             if (NT_SUCCESS(RtlAnsiStringToUnicodeString(&Data, &TempString, TRUE)))
679             {
680                 /* Write the date into the registry */
681                 RtlInitUnicodeString(&ValueName, L"SystemBiosDate");
682                 Status = NtSetValueKey(SystemHandle,
683                                        &ValueName,
684                                        0,
685                                        REG_SZ,
686                                        Data.Buffer,
687                                        Data.Length + sizeof(UNICODE_NULL));
688 
689                 /* Free the string */
690                 RtlFreeUnicodeString(&Data);
691             }
692 
693             if (BiosHandle)
694             {
695                 /* Get the BIOS Date Identifier */
696                 RtlCopyMemory(Buffer, (PCHAR)BaseAddress + (16 * PAGE_SIZE - 11), 8);
697                 Buffer[8] = ANSI_NULL;
698 
699                 /* Convert it to unicode */
700                 RtlInitAnsiString(&TempString, Buffer);
701                 Status = RtlAnsiStringToUnicodeString(&Data, &TempString, TRUE);
702                 if (NT_SUCCESS(Status))
703                 {
704                     /* Save it to the registry */
705                     Status = NtSetValueKey(BiosHandle,
706                                            &ValueName,
707                                            0,
708                                            REG_SZ,
709                                            Data.Buffer,
710                                            Data.Length + sizeof(UNICODE_NULL));
711 
712                     /* ROS: Save a copy for Jira reporting */
713                     if (!RtlCreateUnicodeString(&KeRosBiosDate, Data.Buffer))
714                         KeRosBiosDate.Length = 0;
715 
716                     /* Free the string */
717                     RtlFreeUnicodeString(&Data);
718                 }
719 
720                 /* Close the bios information handle */
721                 NtClose(BiosHandle);
722             }
723         }
724 
725         /* Get the BIOS Version */
726         if (CmpGetBiosVersion(BaseAddress, 16 * PAGE_SIZE, Buffer))
727         {
728             /* Start at the beginning of our buffer */
729             CurrentVersion = BiosVersion;
730             do
731             {
732                 /* Convert to Unicode */
733                 RtlInitAnsiString(&TempString, Buffer);
734                 Status = RtlAnsiStringToUnicodeString(&Data, &TempString, TRUE);
735                 if (!NT_SUCCESS(Status))
736                     break;
737 
738                 /* Calculate the length of this string and copy it in */
739                 Length = Data.Length + sizeof(UNICODE_NULL);
740                 RtlMoveMemory(CurrentVersion, Data.Buffer, Length);
741 
742                 /* Free the unicode string */
743                 RtlFreeUnicodeString(&Data);
744 
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)
748                 {
749                     /* One more string would push us out, so stop here */
750                     break;
751                 }
752 
753                 /* Go to the next string inside the multi-string buffer */
754                 CurrentVersion += Length;
755 
756                 /* Query the next BIOS Version */
757             } while (CmpGetBiosVersion(NULL, 0, Buffer));
758 
759             /* Check if we found any strings at all */
760             if (TotalLength)
761             {
762                 /* Add the final null-terminator */
763                 *(PWSTR)CurrentVersion = UNICODE_NULL;
764                 TotalLength += sizeof(UNICODE_NULL);
765 
766                 /* Write the BIOS Version to the registry */
767                 RtlInitUnicodeString(&ValueName, L"SystemBiosVersion");
768                 Status = NtSetValueKey(SystemHandle,
769                                        &ValueName,
770                                        0,
771                                        REG_MULTI_SZ,
772                                        BiosVersion,
773                                        TotalLength);
774 
775                 /* ROS: Save a copy for Jira reporting */
776                 if (!RtlCreateUnicodeString(&KeRosBiosVersion, (PWCH)BiosVersion))
777                     KeRosBiosVersion.Length = 0;
778             }
779         }
780 
781         /* Unmap the section */
782         ZwUnmapViewOfSection(NtCurrentProcess(), BaseAddress);
783     }
784 
785     /* Now prepare for Video BIOS Mapping of 32KB */
786     BaseAddress = 0;
787     ViewSize = 8 * PAGE_SIZE;
788     ViewBase.QuadPart = VideoRomBase;
789 
790     /* Map it */
791     Status = ZwMapViewOfSection(SectionHandle,
792                                 NtCurrentProcess(),
793                                 &BaseAddress,
794                                 0,
795                                 ViewSize,
796                                 &ViewBase,
797                                 &ViewSize,
798                                 ViewUnmap,
799                                 MEM_DOS_LIM,
800                                 PAGE_READWRITE);
801     if (NT_SUCCESS(Status))
802     {
803         /* Scan the ROM to get the BIOS Date */
804         if (CmpGetBiosDate(BaseAddress, 8 * PAGE_SIZE, Buffer, FALSE))
805         {
806             /* Convert it to Unicode */
807             RtlInitAnsiString(&TempString, Buffer);
808             if (NT_SUCCESS(RtlAnsiStringToUnicodeString(&Data, &TempString, TRUE)))
809             {
810                 /* Write the date into the registry */
811                 RtlInitUnicodeString(&ValueName, L"VideoBiosDate");
812                 Status = NtSetValueKey(SystemHandle,
813                                        &ValueName,
814                                        0,
815                                        REG_SZ,
816                                        Data.Buffer,
817                                        Data.Length + sizeof(UNICODE_NULL));
818 
819                 /* ROS: Save a copy for Jira reporting */
820                 if (!RtlCreateUnicodeString(&KeRosVideoBiosDate, Data.Buffer))
821                     KeRosVideoBiosDate.Length = 0;
822 
823                 /* Free the string */
824                 RtlFreeUnicodeString(&Data);
825             }
826         }
827 
828         /* Get the Video BIOS Version */
829         if (CmpGetBiosVersion(BaseAddress, 8 * PAGE_SIZE, Buffer))
830         {
831             /* Start at the beginning of our buffer */
832             CurrentVersion = BiosVersion;
833             do
834             {
835                 /* Convert to Unicode */
836                 RtlInitAnsiString(&TempString, Buffer);
837                 if (!NT_SUCCESS(RtlAnsiStringToUnicodeString(&Data, &TempString, TRUE)))
838                     break;
839 
840                 /* Calculate the length of this string and copy it in */
841                 Length = Data.Length + sizeof(UNICODE_NULL);
842                 RtlMoveMemory(CurrentVersion, Data.Buffer, Length);
843 
844                 /* Free the unicode string */
845                 RtlFreeUnicodeString(&Data);
846 
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)
850                 {
851                     /* One more string would push us out, so stop here */
852                     break;
853                 }
854 
855                 /* Go to the next string inside the multi-string buffer */
856                 CurrentVersion += Length;
857 
858                 /* Query the next BIOS Version */
859             } while (CmpGetBiosVersion(NULL, 0, Buffer));
860 
861             /* Check if we found any strings at all */
862             if (TotalLength)
863             {
864                 /* Add the final null-terminator */
865                 *(PWSTR)CurrentVersion = UNICODE_NULL;
866                 TotalLength += sizeof(UNICODE_NULL);
867 
868                 /* Write the BIOS Version to the registry */
869                 RtlInitUnicodeString(&ValueName, L"VideoBiosVersion");
870                 Status = NtSetValueKey(SystemHandle,
871                                        &ValueName,
872                                        0,
873                                        REG_MULTI_SZ,
874                                        BiosVersion,
875                                        TotalLength);
876 
877                 /* ROS: Save a copy for Jira reporting */
878                 if (!RtlCreateUnicodeString(&KeRosVideoBiosVersion, (PWCH)BiosVersion))
879                     KeRosVideoBiosVersion.Length = 0;
880             }
881         }
882 
883         /* Unmap the section */
884         ZwUnmapViewOfSection(NtCurrentProcess(), BaseAddress);
885     }
886 
887     /* Close the section */
888     ZwClose(SectionHandle);
889 
890     /* Free the BIOS version string buffer */
891     if (BiosVersion) ExFreePoolWithTag(BiosVersion, TAG_CM);
892 
893 Quickie:
894     /* Close the processor handle */
895     NtClose(KeyHandle);
896     return STATUS_SUCCESS;
897 }
898