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