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