xref: /reactos/ntoskrnl/config/i386/cmhardwr.c (revision 527f2f90)
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     ULONG ExtendedId, Dummy;
242     PKPRCB Prcb;
243     USHORT IndexTable[MaximumType + 1] = {0};
244     ANSI_STRING TempString;
245     PCHAR PartialString = NULL, BiosVersion;
246     CHAR CpuString[48];
247     PVOID BaseAddress = NULL;
248     LARGE_INTEGER ViewBase = {{0, 0}};
249     ULONG_PTR VideoRomBase;
250     PCHAR CurrentVersion;
251     extern UNICODE_STRING KeRosProcessorName, KeRosBiosDate, KeRosBiosVersion;
252     extern UNICODE_STRING KeRosVideoBiosDate, KeRosVideoBiosVersion;
253 
254     /* Open the SMSS Memory Management key */
255     RtlInitUnicodeString(&KeyName,
256                          L"\\Registry\\Machine\\SYSTEM\\CurrentControlSet\\"
257                          L"Control\\Session Manager\\Memory Management");
258     InitializeObjectAttributes(&ObjectAttributes,
259                                &KeyName,
260                                OBJ_CASE_INSENSITIVE,
261                                NULL,
262                                NULL);
263     Status = NtOpenKey(&KeyHandle, KEY_READ | KEY_WRITE, &ObjectAttributes);
264     if (NT_SUCCESS(Status))
265     {
266         /* Detect if PAE is enabled */
267         HavePae = SharedUserData->ProcessorFeatures[PF_PAE_ENABLED];
268 
269         /* Set the value */
270         RtlInitUnicodeString(&ValueName, L"PhysicalAddressExtension");
271         NtSetValueKey(KeyHandle,
272                       &ValueName,
273                       0,
274                       REG_DWORD,
275                       &HavePae,
276                       sizeof(HavePae));
277 
278         /* Close the key */
279         NtClose(KeyHandle);
280     }
281 
282     /* Open the hardware description key */
283     RtlInitUnicodeString(&KeyName,
284                          L"\\Registry\\Machine\\Hardware\\Description\\System");
285     InitializeObjectAttributes(&ObjectAttributes,
286                                &KeyName,
287                                OBJ_CASE_INSENSITIVE,
288                                NULL,
289                                NULL);
290     Status = NtOpenKey(&SystemHandle, KEY_READ | KEY_WRITE, &ObjectAttributes);
291     if (!NT_SUCCESS(Status)) return Status;
292 
293     /* Create the BIOS Information key */
294     RtlInitUnicodeString(&KeyName,
295                          L"\\Registry\\Machine\\SYSTEM\\CurrentControlSet\\"
296                          L"Control\\BIOSINFO");
297     InitializeObjectAttributes(&ObjectAttributes,
298                                &KeyName,
299                                OBJ_CASE_INSENSITIVE,
300                                NULL,
301                                NULL);
302     Status = NtCreateKey(&BiosHandle,
303                          KEY_ALL_ACCESS,
304                          &ObjectAttributes,
305                          0,
306                          NULL,
307                          REG_OPTION_NON_VOLATILE,
308                          &Disposition);
309     if (ExpInTextModeSetup)
310     {
311         if (!NT_SUCCESS(Status))
312             BiosHandle = NULL;
313     }
314     else if (!NT_SUCCESS(Status))
315         return Status;
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) return STATUS_INSUFFICIENT_RESOURCES;
341 
342         /* Loop all CPUs */
343         for (i = 0; i < KeNumberProcessors; i++)
344         {
345             /* Get the PRCB */
346             Prcb = KiProcessorBlock[i];
347 
348             /* Setup the Configuration Entry for the Processor */
349             RtlZeroMemory(&ConfigData, sizeof (ConfigData));
350             ConfigData.ComponentEntry.Class = ProcessorClass;
351             ConfigData.ComponentEntry.Type = CentralProcessor;
352             ConfigData.ComponentEntry.Key = i;
353             ConfigData.ComponentEntry.AffinityMask = AFFINITY_MASK(i);
354             ConfigData.ComponentEntry.Identifier = Buffer;
355 
356             /* Check if the CPU doesn't support CPUID */
357             if (!Prcb->CpuID)
358             {
359                 /* Build ID1-style string for older CPUs */
360                 sprintf(Buffer,
361                         CmpID1,
362                         Prcb->CpuType,
363                         (Prcb->CpuStep >> 8) + 'A',
364                         Prcb->CpuStep & 0xff);
365             }
366             else
367             {
368                 /* Build ID2-style string for newer CPUs */
369                 sprintf(Buffer,
370                         CmpID2,
371                         Prcb->CpuType,
372                         (Prcb->CpuStep >> 8),
373                         Prcb->CpuStep & 0xff);
374             }
375 
376             /* Save the ID string length now that we've created it */
377             ConfigData.ComponentEntry.IdentifierLength = (ULONG)strlen(Buffer) + 1;
378 
379             /* Initialize the registry configuration node for it */
380             Status = CmpInitializeRegistryNode(&ConfigData,
381                                                SystemHandle,
382                                                &KeyHandle,
383                                                InterfaceTypeUndefined,
384                                                0xFFFFFFFF,
385                                                IndexTable);
386             if (!NT_SUCCESS(Status)) return(Status);
387 
388             /* Check if we have an FPU */
389             if (KeI386NpxPresent)
390             {
391                 /* Setup the Configuration Entry for the FPU */
392                 RtlZeroMemory(&ConfigData, sizeof(ConfigData));
393                 ConfigData.ComponentEntry.Class = ProcessorClass;
394                 ConfigData.ComponentEntry.Type = FloatingPointProcessor;
395                 ConfigData.ComponentEntry.Key = i;
396                 ConfigData.ComponentEntry.AffinityMask = AFFINITY_MASK(i);
397                 ConfigData.ComponentEntry.Identifier = Buffer;
398 
399                 /* For 386 cpus, the CPU pp is the identifier */
400                 if (Prcb->CpuType == 3) strcpy(Buffer, "80387");
401 
402                 /* Save the ID string length now that we've created it */
403                 ConfigData.ComponentEntry.IdentifierLength = (ULONG)strlen(Buffer) + 1;
404 
405                 /* Initialize the registry configuration node for it */
406                 Status = CmpInitializeRegistryNode(&ConfigData,
407                                                    SystemHandle,
408                                                    &FpuHandle,
409                                                    InterfaceTypeUndefined,
410                                                    0xFFFFFFFF,
411                                                    IndexTable);
412                 if (!NT_SUCCESS(Status))
413                 {
414                     /* Failed, close the CPU handle and return */
415                     NtClose(KeyHandle);
416                     return Status;
417                 }
418 
419                 /* Close this new handle */
420                 NtClose(FpuHandle);
421 
422                 /* Stay on this CPU only */
423                 KeSetSystemAffinityThread(Prcb->SetMember);
424                 if (!Prcb->CpuID)
425                 {
426                     /* Uh oh, no CPUID! */
427                 }
428                 else
429                 {
430                     /* Check if we have extended CPUID that supports name ID */
431                     CPUID(0x80000000, &ExtendedId, &Dummy, &Dummy, &Dummy);
432                     if (ExtendedId >= 0x80000004)
433                     {
434                         /* Do all the CPUIDs required to get the full name */
435                         PartialString = CpuString;
436                         for (ExtendedId = 2; ExtendedId <= 4; ExtendedId++)
437                         {
438                             /* Do the CPUID and save the name string */
439                             CPUID(0x80000000 | ExtendedId,
440                                   (PULONG)PartialString,
441                                   (PULONG)PartialString + 1,
442                                   (PULONG)PartialString + 2,
443                                   (PULONG)PartialString + 3);
444 
445                             /* Go to the next name string */
446                             PartialString += 16;
447                         }
448 
449                         /* Null-terminate it */
450                         CpuString[47] = ANSI_NULL;
451                     }
452                 }
453 
454                 /* Go back to user affinity */
455                 KeRevertToUserAffinityThread();
456 
457                 /* Check if we have a CPU Name */
458                 if (PartialString)
459                 {
460                     /* Convert it to Unicode */
461                     RtlInitAnsiString(&TempString, CpuString);
462                     RtlAnsiStringToUnicodeString(&Data, &TempString, TRUE);
463 
464                     /* Add it to the registry */
465                     RtlInitUnicodeString(&ValueName, L"ProcessorNameString");
466                     Status = NtSetValueKey(KeyHandle,
467                                            &ValueName,
468                                            0,
469                                            REG_SZ,
470                                            Data.Buffer,
471                                            Data.Length + sizeof(UNICODE_NULL));
472 
473                     /* ROS: Save a copy for bugzilla reporting */
474                     RtlCreateUnicodeString(&KeRosProcessorName, Data.Buffer);
475 
476                     /* Free the temporary buffer */
477                     RtlFreeUnicodeString(&Data);
478                 }
479 
480                 /* Check if we had a Vendor ID */
481                 if (Prcb->VendorString[0])
482                 {
483                     /* Convert it to Unicode */
484                     RtlInitAnsiString(&TempString, Prcb->VendorString);
485                     RtlAnsiStringToUnicodeString(&Data, &TempString, TRUE);
486 
487                     /* Add it to the registry */
488                     RtlInitUnicodeString(&ValueName, L"VendorIdentifier");
489                     Status = NtSetValueKey(KeyHandle,
490                                            &ValueName,
491                                            0,
492                                            REG_SZ,
493                                            Data.Buffer,
494                                            Data.Length + sizeof(UNICODE_NULL));
495 
496                     /* Free the temporary buffer */
497                     RtlFreeUnicodeString(&Data);
498                 }
499 
500                 /* Check if we have features bits */
501                 if (Prcb->FeatureBits)
502                 {
503                     /* Add them to the registry */
504                     RtlInitUnicodeString(&ValueName, L"FeatureSet");
505                     Status = NtSetValueKey(KeyHandle,
506                                            &ValueName,
507                                            0,
508                                            REG_DWORD,
509                                            &Prcb->FeatureBits,
510                                            sizeof(Prcb->FeatureBits));
511                 }
512 
513                 /* Check if we detected the CPU Speed */
514                 if (Prcb->MHz)
515                 {
516                     /* Add it to the registry */
517                     RtlInitUnicodeString(&ValueName, L"~MHz");
518                     Status = NtSetValueKey(KeyHandle,
519                                            &ValueName,
520                                            0,
521                                            REG_DWORD,
522                                            &Prcb->MHz,
523                                            sizeof(Prcb->MHz));
524                 }
525 
526                 /* Check if we have an update signature */
527                 if (Prcb->UpdateSignature.QuadPart)
528                 {
529                     /* Add it to the registry */
530                     RtlInitUnicodeString(&ValueName, L"Update Signature");
531                     Status = NtSetValueKey(KeyHandle,
532                                            &ValueName,
533                                            0,
534                                            REG_BINARY,
535                                            &Prcb->UpdateSignature,
536                                            sizeof(Prcb->UpdateSignature));
537                 }
538 
539                 /* Close the processor handle */
540                 NtClose(KeyHandle);
541 
542                 /* FIXME: Detect CPU mismatches */
543             }
544         }
545 
546         /* Free the configuration data */
547         ExFreePoolWithTag(CmpConfigurationData, TAG_CM);
548     }
549 
550     /* Open physical memory */
551     RtlInitUnicodeString(&SectionName, L"\\Device\\PhysicalMemory");
552     InitializeObjectAttributes(&ObjectAttributes,
553                                &SectionName,
554                                OBJ_CASE_INSENSITIVE,
555                                NULL,
556                                NULL);
557     Status = ZwOpenSection(&SectionHandle,
558                            SECTION_ALL_ACCESS,
559                            &ObjectAttributes);
560     if (!NT_SUCCESS(Status)) goto Quickie;
561 
562     /* Map the first 1KB of memory to get the IVT */
563     ViewSize = PAGE_SIZE;
564     Status = ZwMapViewOfSection(SectionHandle,
565                                 NtCurrentProcess(),
566                                 &BaseAddress,
567                                 0,
568                                 ViewSize,
569                                 &ViewBase,
570                                 &ViewSize,
571                                 ViewUnmap,
572                                 MEM_DOS_LIM,
573                                 PAGE_READWRITE);
574     if (!NT_SUCCESS(Status))
575     {
576         /* Assume default */
577         VideoRomBase = 0xC0000;
578     }
579     else
580     {
581         /* Calculate the base address from the vector */
582         VideoRomBase = (*((PULONG)BaseAddress + 0x10) >> 12) & 0xFFFF0;
583         VideoRomBase += *((PULONG)BaseAddress + 0x10) & 0xFFF0;
584 
585         /* Now get to the actual ROM Start and make sure it's not invalid*/
586         VideoRomBase &= 0xFFFF8000;
587         if (VideoRomBase < 0xC0000) VideoRomBase = 0xC0000;
588 
589         /* And unmap the section */
590         ZwUnmapViewOfSection(NtCurrentProcess(), BaseAddress);
591     }
592 
593     /* Allocate BIOS Version pp Buffer */
594     BiosVersion = ExAllocatePoolWithTag(PagedPool, PAGE_SIZE, TAG_CM);
595 
596     /* Setup settings to map the 64K BIOS ROM */
597     BaseAddress = 0;
598     ViewSize = 16 * PAGE_SIZE;
599     ViewBase.LowPart = 0xF0000;
600     ViewBase.HighPart = 0;
601 
602     /* Map it */
603     Status = ZwMapViewOfSection(SectionHandle,
604                                 NtCurrentProcess(),
605                                 &BaseAddress,
606                                 0,
607                                 ViewSize,
608                                 &ViewBase,
609                                 &ViewSize,
610                                 ViewUnmap,
611                                 MEM_DOS_LIM,
612                                 PAGE_READWRITE);
613     if (NT_SUCCESS(Status))
614     {
615         /* Scan the ROM to get the BIOS Date */
616         if (CmpGetBiosDate(BaseAddress, 16 * PAGE_SIZE, Buffer, TRUE))
617         {
618             /* Convert it to Unicode */
619             RtlInitAnsiString(&TempString, Buffer);
620             RtlAnsiStringToUnicodeString(&Data, &TempString, TRUE);
621 
622             /* Write the date into the registry */
623             RtlInitUnicodeString(&ValueName, L"SystemBiosDate");
624             Status = NtSetValueKey(SystemHandle,
625                                    &ValueName,
626                                    0,
627                                    REG_SZ,
628                                    Data.Buffer,
629                                    Data.Length + sizeof(UNICODE_NULL));
630 
631             /* Free the string */
632             RtlFreeUnicodeString(&Data);
633 
634             if (BiosHandle)
635             {
636                 /* Get the BIOS Date Identifier */
637                 RtlCopyMemory(Buffer, (PCHAR)BaseAddress + (16 * PAGE_SIZE - 11), 8);
638                 Buffer[8] = ANSI_NULL;
639 
640                 /* Convert it to unicode */
641                 RtlInitAnsiString(&TempString, Buffer);
642                 Status = RtlAnsiStringToUnicodeString(&Data, &TempString, TRUE);
643                 if (NT_SUCCESS(Status))
644                 {
645                     /* Save it to the registry */
646                     Status = NtSetValueKey(BiosHandle,
647                                            &ValueName,
648                                            0,
649                                            REG_SZ,
650                                            Data.Buffer,
651                                            Data.Length + sizeof(UNICODE_NULL));
652 
653                     /* ROS: Save a copy for bugzilla reporting */
654                     RtlCreateUnicodeString(&KeRosBiosDate, Data.Buffer);
655 
656                     /* Free the string */
657                     RtlFreeUnicodeString(&Data);
658                 }
659 
660                 /* Close the bios information handle */
661                 NtClose(BiosHandle);
662             }
663         }
664 
665         /* Get the BIOS Version */
666         if (CmpGetBiosVersion(BaseAddress, 16 * PAGE_SIZE, Buffer))
667         {
668             /* Start at the beginning of our buffer */
669             CurrentVersion = BiosVersion;
670             do
671             {
672                 /* Convert to Unicode */
673                 RtlInitAnsiString(&TempString, Buffer);
674                 RtlAnsiStringToUnicodeString(&Data, &TempString, TRUE);
675 
676                 /* Calculate the length of this string and copy it in */
677                 Length = Data.Length + sizeof(UNICODE_NULL);
678                 RtlMoveMemory(CurrentVersion, Data.Buffer, Length);
679 
680                 /* Free the unicode string */
681                 RtlFreeUnicodeString(&Data);
682 
683                 /* Update the total length and see if we're out of space */
684                 TotalLength += Length;
685                 if (TotalLength + 256 + sizeof(UNICODE_NULL) > PAGE_SIZE)
686                 {
687                     /* One more string would push us out, so stop here */
688                     break;
689                 }
690 
691                 /* Go to the next string inside the multi-string buffer */
692                 CurrentVersion += Length;
693 
694                 /* Query the next BIOS Version */
695             } while (CmpGetBiosVersion(NULL, 0, Buffer));
696 
697             /* Check if we found any strings at all */
698             if (TotalLength)
699             {
700                 /* Add the final null-terminator */
701                 *(PWSTR)CurrentVersion = UNICODE_NULL;
702                 TotalLength += sizeof(UNICODE_NULL);
703 
704                 /* Write the BIOS Version to the registry */
705                 RtlInitUnicodeString(&ValueName, L"SystemBiosVersion");
706                 Status = NtSetValueKey(SystemHandle,
707                                        &ValueName,
708                                        0,
709                                        REG_MULTI_SZ,
710                                        BiosVersion,
711                                        TotalLength);
712 
713                 /* ROS: Save a copy for bugzilla reporting */
714                 RtlCreateUnicodeString(&KeRosBiosVersion, (PWCH)BiosVersion);
715             }
716         }
717 
718         /* Unmap the section */
719         ZwUnmapViewOfSection(NtCurrentProcess(), BaseAddress);
720     }
721 
722     /* Now prepare for Video BIOS Mapping of 32KB */
723     BaseAddress = 0;
724     ViewSize = 8 * PAGE_SIZE;
725     ViewBase.QuadPart = VideoRomBase;
726 
727     /* Map it */
728     Status = ZwMapViewOfSection(SectionHandle,
729                                 NtCurrentProcess(),
730                                 &BaseAddress,
731                                 0,
732                                 ViewSize,
733                                 &ViewBase,
734                                 &ViewSize,
735                                 ViewUnmap,
736                                 MEM_DOS_LIM,
737                                 PAGE_READWRITE);
738     if (NT_SUCCESS(Status))
739     {
740         /* Scan the ROM to get the BIOS Date */
741         if (CmpGetBiosDate(BaseAddress, 8 * PAGE_SIZE, Buffer, FALSE))
742         {
743             /* Convert it to Unicode */
744             RtlInitAnsiString(&TempString, Buffer);
745             RtlAnsiStringToUnicodeString(&Data, &TempString, TRUE);
746 
747             /* Write the date into the registry */
748             RtlInitUnicodeString(&ValueName, L"VideoBiosDate");
749             Status = NtSetValueKey(SystemHandle,
750                                    &ValueName,
751                                    0,
752                                    REG_SZ,
753                                    Data.Buffer,
754                                    Data.Length + sizeof(UNICODE_NULL));
755 
756             /* ROS: Save a copy for bugzilla reporting */
757             RtlCreateUnicodeString(&KeRosVideoBiosDate, Data.Buffer);
758 
759             /* Free the string */
760             RtlFreeUnicodeString(&Data);
761         }
762 
763         /* Get the Video BIOS Version */
764         if (CmpGetBiosVersion(BaseAddress, 8 * PAGE_SIZE, Buffer))
765         {
766             /* Start at the beginning of our buffer */
767             CurrentVersion = BiosVersion;
768             do
769             {
770                 /* Convert to Unicode */
771                 RtlInitAnsiString(&TempString, Buffer);
772                 RtlAnsiStringToUnicodeString(&Data, &TempString, TRUE);
773 
774                 /* Calculate the length of this string and copy it in */
775                 Length = Data.Length + sizeof(UNICODE_NULL);
776                 RtlMoveMemory(CurrentVersion, Data.Buffer, Length);
777 
778                 /* Free the unicode string */
779                 RtlFreeUnicodeString(&Data);
780 
781                 /* Update the total length and see if we're out of space */
782                 TotalLength += Length;
783                 if (TotalLength + 256 + sizeof(UNICODE_NULL) > PAGE_SIZE)
784                 {
785                     /* One more string would push us out, so stop here */
786                     break;
787                 }
788 
789                 /* Go to the next string inside the multi-string buffer */
790                 CurrentVersion += Length;
791 
792                 /* Query the next BIOS Version */
793             } while (CmpGetBiosVersion(NULL, 0, Buffer));
794 
795             /* Check if we found any strings at all */
796             if (TotalLength)
797             {
798                 /* Add the final null-terminator */
799                 *(PWSTR)CurrentVersion = UNICODE_NULL;
800                 TotalLength += sizeof(UNICODE_NULL);
801 
802                 /* Write the BIOS Version to the registry */
803                 RtlInitUnicodeString(&ValueName, L"VideoBiosVersion");
804                 Status = NtSetValueKey(SystemHandle,
805                                        &ValueName,
806                                        0,
807                                        REG_MULTI_SZ,
808                                        BiosVersion,
809                                        TotalLength);
810 
811                 /* ROS: Save a copy for bugzilla reporting */
812                 RtlCreateUnicodeString(&KeRosVideoBiosVersion, (PWCH)BiosVersion);
813             }
814         }
815 
816         /* Unmap the section */
817         ZwUnmapViewOfSection(NtCurrentProcess(), BaseAddress);
818     }
819 
820     /* Close the section */
821     ZwClose(SectionHandle);
822 
823     /* Free the BIOS version string buffer */
824     if (BiosVersion) ExFreePoolWithTag(BiosVersion, TAG_CM);
825 
826 Quickie:
827     /* Close the procesor handle */
828     NtClose(KeyHandle);
829     return STATUS_SUCCESS;
830 }
831