xref: /reactos/ntoskrnl/config/i386/cmhardwr.c (revision 2b933529)
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                     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                     RtlCreateUnicodeString(&KeRosProcessorName, Data.Buffer);
488 
489                     /* Free the temporary buffer */
490                     RtlFreeUnicodeString(&Data);
491                 }
492 
493                 /* Check if we had a Vendor ID */
494                 if (Prcb->VendorString[0])
495                 {
496                     /* Convert it to Unicode */
497                     RtlInitAnsiString(&TempString, Prcb->VendorString);
498                     RtlAnsiStringToUnicodeString(&Data, &TempString, TRUE);
499 
500                     /* Add it to the registry */
501                     RtlInitUnicodeString(&ValueName, L"VendorIdentifier");
502                     Status = NtSetValueKey(KeyHandle,
503                                            &ValueName,
504                                            0,
505                                            REG_SZ,
506                                            Data.Buffer,
507                                            Data.Length + sizeof(UNICODE_NULL));
508 
509                     /* Free the temporary buffer */
510                     RtlFreeUnicodeString(&Data);
511                 }
512 
513                 /* Check if we have features bits */
514                 if (Prcb->FeatureBits)
515                 {
516                     /* Add them to the registry */
517                     RtlInitUnicodeString(&ValueName, L"FeatureSet");
518                     Status = NtSetValueKey(KeyHandle,
519                                            &ValueName,
520                                            0,
521                                            REG_DWORD,
522                                            &Prcb->FeatureBits,
523                                            sizeof(Prcb->FeatureBits));
524                 }
525 
526                 /* Check if we detected the CPU Speed */
527                 if (Prcb->MHz)
528                 {
529                     /* Add it to the registry */
530                     RtlInitUnicodeString(&ValueName, L"~MHz");
531                     Status = NtSetValueKey(KeyHandle,
532                                            &ValueName,
533                                            0,
534                                            REG_DWORD,
535                                            &Prcb->MHz,
536                                            sizeof(Prcb->MHz));
537                 }
538 
539                 /* Check if we have an update signature */
540                 if (Prcb->UpdateSignature.QuadPart)
541                 {
542                     /* Add it to the registry */
543                     RtlInitUnicodeString(&ValueName, L"Update Signature");
544                     Status = NtSetValueKey(KeyHandle,
545                                            &ValueName,
546                                            0,
547                                            REG_BINARY,
548                                            &Prcb->UpdateSignature,
549                                            sizeof(Prcb->UpdateSignature));
550                 }
551 
552                 /* Close the processor handle */
553                 NtClose(KeyHandle);
554 
555                 /* FIXME: Detect CPU mismatches */
556             }
557         }
558 
559         /* Free the configuration data */
560         ExFreePoolWithTag(CmpConfigurationData, TAG_CM);
561     }
562 
563     /* Open physical memory */
564     RtlInitUnicodeString(&SectionName, L"\\Device\\PhysicalMemory");
565     InitializeObjectAttributes(&ObjectAttributes,
566                                &SectionName,
567                                OBJ_CASE_INSENSITIVE,
568                                NULL,
569                                NULL);
570     Status = ZwOpenSection(&SectionHandle,
571                            SECTION_ALL_ACCESS,
572                            &ObjectAttributes);
573     if (!NT_SUCCESS(Status))
574     {
575         /* We failed, close all the opened handles and return */
576         // NtClose(KeyHandle);
577         NtClose(BiosHandle);
578         NtClose(SystemHandle);
579         /* 'Quickie' closes KeyHandle */
580         goto Quickie;
581     }
582 
583     /* Map the first 1KB of memory to get the IVT */
584     ViewSize = PAGE_SIZE;
585     Status = ZwMapViewOfSection(SectionHandle,
586                                 NtCurrentProcess(),
587                                 &BaseAddress,
588                                 0,
589                                 ViewSize,
590                                 &ViewBase,
591                                 &ViewSize,
592                                 ViewUnmap,
593                                 MEM_DOS_LIM,
594                                 PAGE_READWRITE);
595     if (!NT_SUCCESS(Status))
596     {
597         /* Assume default */
598         VideoRomBase = 0xC0000;
599     }
600     else
601     {
602         /* Calculate the base address from the vector */
603         VideoRomBase = (*((PULONG)BaseAddress + 0x10) >> 12) & 0xFFFF0;
604         VideoRomBase += *((PULONG)BaseAddress + 0x10) & 0xFFF0;
605 
606         /* Now get to the actual ROM Start and make sure it's not invalid*/
607         VideoRomBase &= 0xFFFF8000;
608         if (VideoRomBase < 0xC0000) VideoRomBase = 0xC0000;
609 
610         /* And unmap the section */
611         ZwUnmapViewOfSection(NtCurrentProcess(), BaseAddress);
612     }
613 
614     /* Allocate BIOS Version pp Buffer */
615     BiosVersion = ExAllocatePoolWithTag(PagedPool, PAGE_SIZE, TAG_CM);
616 
617     /* Setup settings to map the 64K BIOS ROM */
618     BaseAddress = 0;
619     ViewSize = 16 * PAGE_SIZE;
620     ViewBase.LowPart = 0xF0000;
621     ViewBase.HighPart = 0;
622 
623     /* Map it */
624     Status = ZwMapViewOfSection(SectionHandle,
625                                 NtCurrentProcess(),
626                                 &BaseAddress,
627                                 0,
628                                 ViewSize,
629                                 &ViewBase,
630                                 &ViewSize,
631                                 ViewUnmap,
632                                 MEM_DOS_LIM,
633                                 PAGE_READWRITE);
634     if (NT_SUCCESS(Status))
635     {
636         /* Scan the ROM to get the BIOS Date */
637         if (CmpGetBiosDate(BaseAddress, 16 * PAGE_SIZE, Buffer, TRUE))
638         {
639             /* Convert it to Unicode */
640             RtlInitAnsiString(&TempString, Buffer);
641             RtlAnsiStringToUnicodeString(&Data, &TempString, TRUE);
642 
643             /* Write the date into the registry */
644             RtlInitUnicodeString(&ValueName, L"SystemBiosDate");
645             Status = NtSetValueKey(SystemHandle,
646                                    &ValueName,
647                                    0,
648                                    REG_SZ,
649                                    Data.Buffer,
650                                    Data.Length + sizeof(UNICODE_NULL));
651 
652             /* Free the string */
653             RtlFreeUnicodeString(&Data);
654 
655             if (BiosHandle)
656             {
657                 /* Get the BIOS Date Identifier */
658                 RtlCopyMemory(Buffer, (PCHAR)BaseAddress + (16 * PAGE_SIZE - 11), 8);
659                 Buffer[8] = ANSI_NULL;
660 
661                 /* Convert it to unicode */
662                 RtlInitAnsiString(&TempString, Buffer);
663                 Status = RtlAnsiStringToUnicodeString(&Data, &TempString, TRUE);
664                 if (NT_SUCCESS(Status))
665                 {
666                     /* Save it to the registry */
667                     Status = NtSetValueKey(BiosHandle,
668                                            &ValueName,
669                                            0,
670                                            REG_SZ,
671                                            Data.Buffer,
672                                            Data.Length + sizeof(UNICODE_NULL));
673 
674                     /* ROS: Save a copy for bugzilla reporting */
675                     RtlCreateUnicodeString(&KeRosBiosDate, Data.Buffer);
676 
677                     /* Free the string */
678                     RtlFreeUnicodeString(&Data);
679                 }
680 
681                 /* Close the bios information handle */
682                 NtClose(BiosHandle);
683             }
684         }
685 
686         /* Get the BIOS Version */
687         if (CmpGetBiosVersion(BaseAddress, 16 * PAGE_SIZE, Buffer))
688         {
689             /* Start at the beginning of our buffer */
690             CurrentVersion = BiosVersion;
691             do
692             {
693                 /* Convert to Unicode */
694                 RtlInitAnsiString(&TempString, Buffer);
695                 RtlAnsiStringToUnicodeString(&Data, &TempString, TRUE);
696 
697                 /* Calculate the length of this string and copy it in */
698                 Length = Data.Length + sizeof(UNICODE_NULL);
699                 RtlMoveMemory(CurrentVersion, Data.Buffer, Length);
700 
701                 /* Free the unicode string */
702                 RtlFreeUnicodeString(&Data);
703 
704                 /* Update the total length and see if we're out of space */
705                 TotalLength += Length;
706                 if (TotalLength + 256 + sizeof(UNICODE_NULL) > PAGE_SIZE)
707                 {
708                     /* One more string would push us out, so stop here */
709                     break;
710                 }
711 
712                 /* Go to the next string inside the multi-string buffer */
713                 CurrentVersion += Length;
714 
715                 /* Query the next BIOS Version */
716             } while (CmpGetBiosVersion(NULL, 0, Buffer));
717 
718             /* Check if we found any strings at all */
719             if (TotalLength)
720             {
721                 /* Add the final null-terminator */
722                 *(PWSTR)CurrentVersion = UNICODE_NULL;
723                 TotalLength += sizeof(UNICODE_NULL);
724 
725                 /* Write the BIOS Version to the registry */
726                 RtlInitUnicodeString(&ValueName, L"SystemBiosVersion");
727                 Status = NtSetValueKey(SystemHandle,
728                                        &ValueName,
729                                        0,
730                                        REG_MULTI_SZ,
731                                        BiosVersion,
732                                        TotalLength);
733 
734                 /* ROS: Save a copy for bugzilla reporting */
735                 RtlCreateUnicodeString(&KeRosBiosVersion, (PWCH)BiosVersion);
736             }
737         }
738 
739         /* Unmap the section */
740         ZwUnmapViewOfSection(NtCurrentProcess(), BaseAddress);
741     }
742 
743     /* Now prepare for Video BIOS Mapping of 32KB */
744     BaseAddress = 0;
745     ViewSize = 8 * PAGE_SIZE;
746     ViewBase.QuadPart = VideoRomBase;
747 
748     /* Map it */
749     Status = ZwMapViewOfSection(SectionHandle,
750                                 NtCurrentProcess(),
751                                 &BaseAddress,
752                                 0,
753                                 ViewSize,
754                                 &ViewBase,
755                                 &ViewSize,
756                                 ViewUnmap,
757                                 MEM_DOS_LIM,
758                                 PAGE_READWRITE);
759     if (NT_SUCCESS(Status))
760     {
761         /* Scan the ROM to get the BIOS Date */
762         if (CmpGetBiosDate(BaseAddress, 8 * PAGE_SIZE, Buffer, FALSE))
763         {
764             /* Convert it to Unicode */
765             RtlInitAnsiString(&TempString, Buffer);
766             RtlAnsiStringToUnicodeString(&Data, &TempString, TRUE);
767 
768             /* Write the date into the registry */
769             RtlInitUnicodeString(&ValueName, L"VideoBiosDate");
770             Status = NtSetValueKey(SystemHandle,
771                                    &ValueName,
772                                    0,
773                                    REG_SZ,
774                                    Data.Buffer,
775                                    Data.Length + sizeof(UNICODE_NULL));
776 
777             /* ROS: Save a copy for bugzilla reporting */
778             RtlCreateUnicodeString(&KeRosVideoBiosDate, Data.Buffer);
779 
780             /* Free the string */
781             RtlFreeUnicodeString(&Data);
782         }
783 
784         /* Get the Video BIOS Version */
785         if (CmpGetBiosVersion(BaseAddress, 8 * PAGE_SIZE, Buffer))
786         {
787             /* Start at the beginning of our buffer */
788             CurrentVersion = BiosVersion;
789             do
790             {
791                 /* Convert to Unicode */
792                 RtlInitAnsiString(&TempString, Buffer);
793                 RtlAnsiStringToUnicodeString(&Data, &TempString, TRUE);
794 
795                 /* Calculate the length of this string and copy it in */
796                 Length = Data.Length + sizeof(UNICODE_NULL);
797                 RtlMoveMemory(CurrentVersion, Data.Buffer, Length);
798 
799                 /* Free the unicode string */
800                 RtlFreeUnicodeString(&Data);
801 
802                 /* Update the total length and see if we're out of space */
803                 TotalLength += Length;
804                 if (TotalLength + 256 + sizeof(UNICODE_NULL) > PAGE_SIZE)
805                 {
806                     /* One more string would push us out, so stop here */
807                     break;
808                 }
809 
810                 /* Go to the next string inside the multi-string buffer */
811                 CurrentVersion += Length;
812 
813                 /* Query the next BIOS Version */
814             } while (CmpGetBiosVersion(NULL, 0, Buffer));
815 
816             /* Check if we found any strings at all */
817             if (TotalLength)
818             {
819                 /* Add the final null-terminator */
820                 *(PWSTR)CurrentVersion = UNICODE_NULL;
821                 TotalLength += sizeof(UNICODE_NULL);
822 
823                 /* Write the BIOS Version to the registry */
824                 RtlInitUnicodeString(&ValueName, L"VideoBiosVersion");
825                 Status = NtSetValueKey(SystemHandle,
826                                        &ValueName,
827                                        0,
828                                        REG_MULTI_SZ,
829                                        BiosVersion,
830                                        TotalLength);
831 
832                 /* ROS: Save a copy for bugzilla reporting */
833                 RtlCreateUnicodeString(&KeRosVideoBiosVersion, (PWCH)BiosVersion);
834             }
835         }
836 
837         /* Unmap the section */
838         ZwUnmapViewOfSection(NtCurrentProcess(), BaseAddress);
839     }
840 
841     /* Close the section */
842     ZwClose(SectionHandle);
843 
844     /* Free the BIOS version string buffer */
845     if (BiosVersion) ExFreePoolWithTag(BiosVersion, TAG_CM);
846 
847 Quickie:
848     /* Close the processor handle */
849     NtClose(KeyHandle);
850     return STATUS_SUCCESS;
851 }
852