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