xref: /reactos/ntoskrnl/config/i386/cmhardwr.c (revision 5417742f)
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 VOID
230 NTAPI
231 CmpGetIntelBrandString(OUT PCHAR CpuString)
232 {
233     CPU_INFO CpuInfo;
234     ULONG BrandId, Signature;
235 
236     /* Get the Brand Id */
237     KiCpuId(&CpuInfo, 0x00000001);
238     Signature = CpuInfo.Eax;
239     BrandId = CpuInfo.Ebx & 0xFF;
240 
241     switch (BrandId)
242     {
243         case 0x01:
244             strcpy(CpuString, "Intel(R) Celeron(R) processor");
245             break;
246         case 0x02:
247         case 0x04:
248             strcpy(CpuString, "Intel(R) Pentium(R) III processor");
249             break;
250         case 0x03:
251             if(Signature == 0x000006B1)
252                 strcpy(CpuString, "Intel(R) Celeron(R) processor");
253             else
254                 strcpy(CpuString, "Intel(R) Pentium(R) III Xeon(R) processor");
255             break;
256         case 0x06:
257             strcpy(CpuString, "Mobile Intel(R) Pentium(R) III Processor-M");
258             break;
259         case 0x08:
260             if(Signature >= 0x00000F13)
261                 strcpy(CpuString, "Intel(R) Genuine Processor");
262             else
263                 strcpy(CpuString, "Intel(R) Pentium(R) 4 processor");
264             break;
265         case 0x09:
266             strcpy(CpuString, "Intel(R) Pentium(R) 4 processor");
267             break;
268         case 0x0B:
269             if(Signature >= 0x00000F13)
270                 strcpy(CpuString, "Intel(R) Xeon(R) processor");
271             else
272                 strcpy(CpuString, "Intel(R) Xeon(R) processor MP");
273             break;
274         case 0x0C:
275             strcpy(CpuString, "Intel(R) Xeon(R) processor MP");
276             break;
277         case 0x0E:
278             if(Signature >= 0x00000F13)
279                 strcpy(CpuString, "Mobile Intel(R) Pentium(R) 4 processor-M");
280             else
281                 strcpy(CpuString, "Intel(R) Xeon(R) processor");
282             break;
283         case 0x12:
284             strcpy(CpuString, "Intel(R) Celeron(R) M processor");
285             break;
286         case 0x07:
287         case 0x0F:
288         case 0x13:
289         case 0x17:
290             strcpy(CpuString, "Mobile Intel(R) Celeron(R) processor");
291             break;
292         case 0x0A:
293         case 0x14:
294             strcpy(CpuString, "Intel(R) Celeron(R) Processor");
295             break;
296         case 0x15:
297             strcpy(CpuString, "Mobile Genuine Intel(R) Processor");
298             break;
299         case 0x16:
300             strcpy(CpuString, "Intel(R) Pentium(R) M processor");
301             break;
302         default:
303             strcpy(CpuString, "Unknown Intel processor");
304     }
305 }
306 
307 VOID
308 NTAPI
309 CmpGetVendorString(IN PKPRCB Prcb, OUT PCHAR CpuString)
310 {
311     /* Check if we have a Vendor String */
312     if (Prcb->VendorString[0])
313     {
314         strcpy(CpuString, Prcb->VendorString);
315     }
316     else
317     {
318         strcpy(CpuString, "Unknown x86 processor");
319     }
320 }
321 
322 NTSTATUS
323 NTAPI
324 CmpInitializeMachineDependentConfiguration(IN PLOADER_PARAMETER_BLOCK LoaderBlock)
325 {
326     UNICODE_STRING KeyName, ValueName, Data, SectionName;
327     OBJECT_ATTRIBUTES ObjectAttributes;
328     ULONG HavePae, Length, TotalLength = 0, i, Disposition;
329     SIZE_T ViewSize;
330     NTSTATUS Status;
331     HANDLE KeyHandle, BiosHandle, SystemHandle, FpuHandle, SectionHandle;
332     CONFIGURATION_COMPONENT_DATA ConfigData;
333     CHAR Buffer[128];
334     CPU_INFO CpuInfo;
335     ULONG VendorId, ExtendedId;
336     PKPRCB Prcb;
337     USHORT IndexTable[MaximumType + 1] = {0};
338     ANSI_STRING TempString;
339     PCHAR PartialString = NULL, BiosVersion;
340     CHAR CpuString[48];
341     PVOID BaseAddress = NULL;
342     LARGE_INTEGER ViewBase = {{0, 0}};
343     ULONG_PTR VideoRomBase;
344     PCHAR CurrentVersion;
345     extern UNICODE_STRING KeRosProcessorName, KeRosBiosDate, KeRosBiosVersion;
346     extern UNICODE_STRING KeRosVideoBiosDate, KeRosVideoBiosVersion;
347 
348     /* Open the SMSS Memory Management key */
349     RtlInitUnicodeString(&KeyName,
350                          L"\\Registry\\Machine\\SYSTEM\\CurrentControlSet\\"
351                          L"Control\\Session Manager\\Memory Management");
352     InitializeObjectAttributes(&ObjectAttributes,
353                                &KeyName,
354                                OBJ_CASE_INSENSITIVE,
355                                NULL,
356                                NULL);
357     Status = NtOpenKey(&KeyHandle, KEY_READ | KEY_WRITE, &ObjectAttributes);
358     if (NT_SUCCESS(Status))
359     {
360         /* Detect if PAE is enabled */
361         HavePae = SharedUserData->ProcessorFeatures[PF_PAE_ENABLED];
362 
363         /* Set the value */
364         RtlInitUnicodeString(&ValueName, L"PhysicalAddressExtension");
365         NtSetValueKey(KeyHandle,
366                       &ValueName,
367                       0,
368                       REG_DWORD,
369                       &HavePae,
370                       sizeof(HavePae));
371 
372         /* Close the key */
373         NtClose(KeyHandle);
374     }
375 
376     /* Open the hardware description key */
377     RtlInitUnicodeString(&KeyName,
378                          L"\\Registry\\Machine\\Hardware\\Description\\System");
379     InitializeObjectAttributes(&ObjectAttributes,
380                                &KeyName,
381                                OBJ_CASE_INSENSITIVE,
382                                NULL,
383                                NULL);
384     Status = NtOpenKey(&SystemHandle, KEY_READ | KEY_WRITE, &ObjectAttributes);
385     if (!NT_SUCCESS(Status))
386         return Status;
387 
388     /* Create the BIOS Information key */
389     RtlInitUnicodeString(&KeyName,
390                          L"\\Registry\\Machine\\SYSTEM\\CurrentControlSet\\"
391                          L"Control\\BIOSINFO");
392     InitializeObjectAttributes(&ObjectAttributes,
393                                &KeyName,
394                                OBJ_CASE_INSENSITIVE,
395                                NULL,
396                                NULL);
397     Status = NtCreateKey(&BiosHandle,
398                          KEY_ALL_ACCESS,
399                          &ObjectAttributes,
400                          0,
401                          NULL,
402                          REG_OPTION_NON_VOLATILE,
403                          &Disposition);
404     if (!NT_SUCCESS(Status))
405     {
406         NtClose(SystemHandle);
407         return Status;
408     }
409 
410     /* Create the CPU Key, and check if it already existed */
411     RtlInitUnicodeString(&KeyName, L"CentralProcessor");
412     InitializeObjectAttributes(&ObjectAttributes,
413                                &KeyName,
414                                OBJ_CASE_INSENSITIVE,
415                                SystemHandle,
416                                NULL);
417     Status = NtCreateKey(&KeyHandle,
418                          KEY_READ | KEY_WRITE,
419                          &ObjectAttributes,
420                          0,
421                          NULL,
422                          0,
423                          &Disposition);
424     NtClose(KeyHandle);
425 
426     /* The key shouldn't already exist */
427     if (Disposition == REG_CREATED_NEW_KEY)
428     {
429         /* Allocate the configuration data for cmconfig.c */
430         CmpConfigurationData = ExAllocatePoolWithTag(PagedPool,
431                                                      CmpConfigurationAreaSize,
432                                                      TAG_CM);
433         if (!CmpConfigurationData)
434         {
435             // FIXME: Cleanup stuff!!
436             return STATUS_INSUFFICIENT_RESOURCES;
437         }
438 
439         /* Loop all CPUs */
440         for (i = 0; i < KeNumberProcessors; i++)
441         {
442             /* Get the PRCB */
443             Prcb = KiProcessorBlock[i];
444 
445             /* Setup the Configuration Entry for the Processor */
446             RtlZeroMemory(&ConfigData, sizeof(ConfigData));
447             ConfigData.ComponentEntry.Class = ProcessorClass;
448             ConfigData.ComponentEntry.Type = CentralProcessor;
449             ConfigData.ComponentEntry.Key = i;
450             ConfigData.ComponentEntry.AffinityMask = AFFINITY_MASK(i);
451             ConfigData.ComponentEntry.Identifier = Buffer;
452 
453             /* Check if the CPU doesn't support CPUID */
454             if (!Prcb->CpuID)
455             {
456                 /* Build ID1-style string for older CPUs */
457                 sprintf(Buffer,
458                         CmpID1,
459                         Prcb->CpuType,
460                         (Prcb->CpuStep >> 8) + 'A',
461                         Prcb->CpuStep & 0xff);
462             }
463             else
464             {
465                 /* Build ID2-style string for newer CPUs */
466                 sprintf(Buffer,
467                         CmpID2,
468                         Prcb->CpuType,
469                         (Prcb->CpuStep >> 8),
470                         Prcb->CpuStep & 0xff);
471             }
472 
473             /* Save the ID string length now that we've created it */
474             ConfigData.ComponentEntry.IdentifierLength = (ULONG)strlen(Buffer) + 1;
475 
476             /* Initialize the registry configuration node for it */
477             Status = CmpInitializeRegistryNode(&ConfigData,
478                                                SystemHandle,
479                                                &KeyHandle,
480                                                InterfaceTypeUndefined,
481                                                0xFFFFFFFF,
482                                                IndexTable);
483             if (!NT_SUCCESS(Status))
484             {
485                 NtClose(BiosHandle);
486                 NtClose(SystemHandle);
487                 return Status;
488             }
489 
490             /* Check if we have an FPU */
491             if (KeI386NpxPresent)
492             {
493                 /* Setup the Configuration Entry for the FPU */
494                 RtlZeroMemory(&ConfigData, sizeof(ConfigData));
495                 ConfigData.ComponentEntry.Class = ProcessorClass;
496                 ConfigData.ComponentEntry.Type = FloatingPointProcessor;
497                 ConfigData.ComponentEntry.Key = i;
498                 ConfigData.ComponentEntry.AffinityMask = AFFINITY_MASK(i);
499                 ConfigData.ComponentEntry.Identifier = Buffer;
500 
501                 /* For 386 cpus, the CPU pp is the identifier */
502                 if (Prcb->CpuType == 3) strcpy(Buffer, "80387");
503 
504                 /* Save the ID string length now that we've created it */
505                 ConfigData.ComponentEntry.IdentifierLength = (ULONG)strlen(Buffer) + 1;
506 
507                 /* Initialize the registry configuration node for it */
508                 Status = CmpInitializeRegistryNode(&ConfigData,
509                                                    SystemHandle,
510                                                    &FpuHandle,
511                                                    InterfaceTypeUndefined,
512                                                    0xFFFFFFFF,
513                                                    IndexTable);
514                 if (!NT_SUCCESS(Status))
515                 {
516                     /* We failed, close all the opened handles and return */
517                     NtClose(KeyHandle);
518                     NtClose(BiosHandle);
519                     NtClose(SystemHandle);
520                     return Status;
521                 }
522 
523                 /* Close this new handle */
524                 NtClose(FpuHandle);
525 
526                 /* Stay on this CPU only */
527                 KeSetSystemAffinityThread(Prcb->SetMember);
528                 if (!Prcb->CpuID)
529                 {
530                     /* Uh oh, no CPUID! */
531                     PartialString = CpuString;
532                     CmpGetVendorString(Prcb, PartialString);
533                 }
534                 else
535                 {
536                     /* Check if we have extended CPUID that supports name ID */
537                     KiCpuId(&CpuInfo, 0x80000000);
538                     ExtendedId = CpuInfo.Eax;
539                     if (ExtendedId >= 0x80000004)
540                     {
541                         /* Do all the CPUIDs required to get the full name */
542                         PartialString = CpuString;
543                         for (ExtendedId = 2; ExtendedId <= 4; ExtendedId++)
544                         {
545                             /* Do the CPUID and save the name string */
546                             KiCpuId(&CpuInfo, 0x80000000 | ExtendedId);
547                             ((PULONG)PartialString)[0] = CpuInfo.Eax;
548                             ((PULONG)PartialString)[1] = CpuInfo.Ebx;
549                             ((PULONG)PartialString)[2] = CpuInfo.Ecx;
550                             ((PULONG)PartialString)[3] = CpuInfo.Edx;
551 
552                             /* Go to the next name string */
553                             PartialString += 16;
554                         }
555 
556                         /* Null-terminate it */
557                         CpuString[47] = ANSI_NULL;
558                     }
559                     else
560                     {
561                         KiCpuId(&CpuInfo, 0x00000000);
562                         VendorId = CpuInfo.Ebx;
563                         PartialString = CpuString;
564                         switch (VendorId)
565                         {
566                             case 'uneG': /* Intel */
567                                 CmpGetIntelBrandString(PartialString);
568                                 break;
569                             case 'htuA': /* AMD */
570                                 /* FIXME */
571                                 CmpGetVendorString(Prcb, PartialString);
572                                 break;
573                             default:
574                                 CmpGetVendorString(Prcb, PartialString);
575                         }
576                     }
577                 }
578 
579                 /* Go back to user affinity */
580                 KeRevertToUserAffinityThread();
581 
582                 /* Check if we have a CPU Name */
583                 if (PartialString)
584                 {
585                     /* Convert it to Unicode */
586                     RtlInitAnsiString(&TempString, CpuString);
587                     RtlAnsiStringToUnicodeString(&Data, &TempString, TRUE);
588 
589                     /* Add it to the registry */
590                     RtlInitUnicodeString(&ValueName, L"ProcessorNameString");
591                     Status = NtSetValueKey(KeyHandle,
592                                            &ValueName,
593                                            0,
594                                            REG_SZ,
595                                            Data.Buffer,
596                                            Data.Length + sizeof(UNICODE_NULL));
597 
598                     /* ROS: Save a copy for bugzilla reporting */
599                     RtlCreateUnicodeString(&KeRosProcessorName, Data.Buffer);
600 
601                     /* Free the temporary buffer */
602                     RtlFreeUnicodeString(&Data);
603                 }
604 
605                 /* Check if we had a Vendor ID */
606                 if (Prcb->VendorString[0])
607                 {
608                     /* Convert it to Unicode */
609                     RtlInitAnsiString(&TempString, Prcb->VendorString);
610                     RtlAnsiStringToUnicodeString(&Data, &TempString, TRUE);
611 
612                     /* Add it to the registry */
613                     RtlInitUnicodeString(&ValueName, L"VendorIdentifier");
614                     Status = NtSetValueKey(KeyHandle,
615                                            &ValueName,
616                                            0,
617                                            REG_SZ,
618                                            Data.Buffer,
619                                            Data.Length + sizeof(UNICODE_NULL));
620 
621                     /* Free the temporary buffer */
622                     RtlFreeUnicodeString(&Data);
623                 }
624 
625                 /* Check if we have features bits */
626                 if (Prcb->FeatureBits)
627                 {
628                     /* Add them to the registry */
629                     RtlInitUnicodeString(&ValueName, L"FeatureSet");
630                     Status = NtSetValueKey(KeyHandle,
631                                            &ValueName,
632                                            0,
633                                            REG_DWORD,
634                                            &Prcb->FeatureBits,
635                                            sizeof(Prcb->FeatureBits));
636                 }
637 
638                 /* Check if we detected the CPU Speed */
639                 if (Prcb->MHz)
640                 {
641                     /* Add it to the registry */
642                     RtlInitUnicodeString(&ValueName, L"~MHz");
643                     Status = NtSetValueKey(KeyHandle,
644                                            &ValueName,
645                                            0,
646                                            REG_DWORD,
647                                            &Prcb->MHz,
648                                            sizeof(Prcb->MHz));
649                 }
650 
651                 /* Check if we have an update signature */
652                 if (Prcb->UpdateSignature.QuadPart)
653                 {
654                     /* Add it to the registry */
655                     RtlInitUnicodeString(&ValueName, L"Update Signature");
656                     Status = NtSetValueKey(KeyHandle,
657                                            &ValueName,
658                                            0,
659                                            REG_BINARY,
660                                            &Prcb->UpdateSignature,
661                                            sizeof(Prcb->UpdateSignature));
662                 }
663 
664                 /* Close the processor handle */
665                 NtClose(KeyHandle);
666 
667                 /* FIXME: Detect CPU mismatches */
668             }
669         }
670 
671         /* Free the configuration data */
672         ExFreePoolWithTag(CmpConfigurationData, TAG_CM);
673     }
674 
675     /* Open physical memory */
676     RtlInitUnicodeString(&SectionName, L"\\Device\\PhysicalMemory");
677     InitializeObjectAttributes(&ObjectAttributes,
678                                &SectionName,
679                                OBJ_CASE_INSENSITIVE,
680                                NULL,
681                                NULL);
682     Status = ZwOpenSection(&SectionHandle,
683                            SECTION_ALL_ACCESS,
684                            &ObjectAttributes);
685     if (!NT_SUCCESS(Status))
686     {
687         /* We failed, close all the opened handles and return */
688         // NtClose(KeyHandle);
689         NtClose(BiosHandle);
690         NtClose(SystemHandle);
691         /* 'Quickie' closes KeyHandle */
692         goto Quickie;
693     }
694 
695     /* Map the first 1KB of memory to get the IVT */
696     ViewSize = PAGE_SIZE;
697     Status = ZwMapViewOfSection(SectionHandle,
698                                 NtCurrentProcess(),
699                                 &BaseAddress,
700                                 0,
701                                 ViewSize,
702                                 &ViewBase,
703                                 &ViewSize,
704                                 ViewUnmap,
705                                 MEM_DOS_LIM,
706                                 PAGE_READWRITE);
707     if (!NT_SUCCESS(Status))
708     {
709         /* Assume default */
710         VideoRomBase = 0xC0000;
711     }
712     else
713     {
714         /* Calculate the base address from the vector */
715         VideoRomBase = (*((PULONG)BaseAddress + 0x10) >> 12) & 0xFFFF0;
716         VideoRomBase += *((PULONG)BaseAddress + 0x10) & 0xFFF0;
717 
718         /* Now get to the actual ROM Start and make sure it's not invalid*/
719         VideoRomBase &= 0xFFFF8000;
720         if (VideoRomBase < 0xC0000) VideoRomBase = 0xC0000;
721 
722         /* And unmap the section */
723         ZwUnmapViewOfSection(NtCurrentProcess(), BaseAddress);
724     }
725 
726     /* Allocate BIOS Version pp Buffer */
727     BiosVersion = ExAllocatePoolWithTag(PagedPool, PAGE_SIZE, TAG_CM);
728 
729     /* Setup settings to map the 64K BIOS ROM */
730     BaseAddress = 0;
731     ViewSize = 16 * PAGE_SIZE;
732     ViewBase.LowPart = 0xF0000;
733     ViewBase.HighPart = 0;
734 
735     /* Map it */
736     Status = ZwMapViewOfSection(SectionHandle,
737                                 NtCurrentProcess(),
738                                 &BaseAddress,
739                                 0,
740                                 ViewSize,
741                                 &ViewBase,
742                                 &ViewSize,
743                                 ViewUnmap,
744                                 MEM_DOS_LIM,
745                                 PAGE_READWRITE);
746     if (NT_SUCCESS(Status))
747     {
748         /* Scan the ROM to get the BIOS Date */
749         if (CmpGetBiosDate(BaseAddress, 16 * PAGE_SIZE, Buffer, TRUE))
750         {
751             /* Convert it to Unicode */
752             RtlInitAnsiString(&TempString, Buffer);
753             RtlAnsiStringToUnicodeString(&Data, &TempString, TRUE);
754 
755             /* Write the date into the registry */
756             RtlInitUnicodeString(&ValueName, L"SystemBiosDate");
757             Status = NtSetValueKey(SystemHandle,
758                                    &ValueName,
759                                    0,
760                                    REG_SZ,
761                                    Data.Buffer,
762                                    Data.Length + sizeof(UNICODE_NULL));
763 
764             /* Free the string */
765             RtlFreeUnicodeString(&Data);
766 
767             if (BiosHandle)
768             {
769                 /* Get the BIOS Date Identifier */
770                 RtlCopyMemory(Buffer, (PCHAR)BaseAddress + (16 * PAGE_SIZE - 11), 8);
771                 Buffer[8] = ANSI_NULL;
772 
773                 /* Convert it to unicode */
774                 RtlInitAnsiString(&TempString, Buffer);
775                 Status = RtlAnsiStringToUnicodeString(&Data, &TempString, TRUE);
776                 if (NT_SUCCESS(Status))
777                 {
778                     /* Save it to the registry */
779                     Status = NtSetValueKey(BiosHandle,
780                                            &ValueName,
781                                            0,
782                                            REG_SZ,
783                                            Data.Buffer,
784                                            Data.Length + sizeof(UNICODE_NULL));
785 
786                     /* ROS: Save a copy for bugzilla reporting */
787                     RtlCreateUnicodeString(&KeRosBiosDate, Data.Buffer);
788 
789                     /* Free the string */
790                     RtlFreeUnicodeString(&Data);
791                 }
792 
793                 /* Close the bios information handle */
794                 NtClose(BiosHandle);
795             }
796         }
797 
798         /* Get the BIOS Version */
799         if (CmpGetBiosVersion(BaseAddress, 16 * PAGE_SIZE, Buffer))
800         {
801             /* Start at the beginning of our buffer */
802             CurrentVersion = BiosVersion;
803             do
804             {
805                 /* Convert to Unicode */
806                 RtlInitAnsiString(&TempString, Buffer);
807                 RtlAnsiStringToUnicodeString(&Data, &TempString, TRUE);
808 
809                 /* Calculate the length of this string and copy it in */
810                 Length = Data.Length + sizeof(UNICODE_NULL);
811                 RtlMoveMemory(CurrentVersion, Data.Buffer, Length);
812 
813                 /* Free the unicode string */
814                 RtlFreeUnicodeString(&Data);
815 
816                 /* Update the total length and see if we're out of space */
817                 TotalLength += Length;
818                 if (TotalLength + 256 + sizeof(UNICODE_NULL) > PAGE_SIZE)
819                 {
820                     /* One more string would push us out, so stop here */
821                     break;
822                 }
823 
824                 /* Go to the next string inside the multi-string buffer */
825                 CurrentVersion += Length;
826 
827                 /* Query the next BIOS Version */
828             } while (CmpGetBiosVersion(NULL, 0, Buffer));
829 
830             /* Check if we found any strings at all */
831             if (TotalLength)
832             {
833                 /* Add the final null-terminator */
834                 *(PWSTR)CurrentVersion = UNICODE_NULL;
835                 TotalLength += sizeof(UNICODE_NULL);
836 
837                 /* Write the BIOS Version to the registry */
838                 RtlInitUnicodeString(&ValueName, L"SystemBiosVersion");
839                 Status = NtSetValueKey(SystemHandle,
840                                        &ValueName,
841                                        0,
842                                        REG_MULTI_SZ,
843                                        BiosVersion,
844                                        TotalLength);
845 
846                 /* ROS: Save a copy for bugzilla reporting */
847                 RtlCreateUnicodeString(&KeRosBiosVersion, (PWCH)BiosVersion);
848             }
849         }
850 
851         /* Unmap the section */
852         ZwUnmapViewOfSection(NtCurrentProcess(), BaseAddress);
853     }
854 
855     /* Now prepare for Video BIOS Mapping of 32KB */
856     BaseAddress = 0;
857     ViewSize = 8 * PAGE_SIZE;
858     ViewBase.QuadPart = VideoRomBase;
859 
860     /* Map it */
861     Status = ZwMapViewOfSection(SectionHandle,
862                                 NtCurrentProcess(),
863                                 &BaseAddress,
864                                 0,
865                                 ViewSize,
866                                 &ViewBase,
867                                 &ViewSize,
868                                 ViewUnmap,
869                                 MEM_DOS_LIM,
870                                 PAGE_READWRITE);
871     if (NT_SUCCESS(Status))
872     {
873         /* Scan the ROM to get the BIOS Date */
874         if (CmpGetBiosDate(BaseAddress, 8 * PAGE_SIZE, Buffer, FALSE))
875         {
876             /* Convert it to Unicode */
877             RtlInitAnsiString(&TempString, Buffer);
878             RtlAnsiStringToUnicodeString(&Data, &TempString, TRUE);
879 
880             /* Write the date into the registry */
881             RtlInitUnicodeString(&ValueName, L"VideoBiosDate");
882             Status = NtSetValueKey(SystemHandle,
883                                    &ValueName,
884                                    0,
885                                    REG_SZ,
886                                    Data.Buffer,
887                                    Data.Length + sizeof(UNICODE_NULL));
888 
889             /* ROS: Save a copy for bugzilla reporting */
890             RtlCreateUnicodeString(&KeRosVideoBiosDate, Data.Buffer);
891 
892             /* Free the string */
893             RtlFreeUnicodeString(&Data);
894         }
895 
896         /* Get the Video BIOS Version */
897         if (CmpGetBiosVersion(BaseAddress, 8 * PAGE_SIZE, Buffer))
898         {
899             /* Start at the beginning of our buffer */
900             CurrentVersion = BiosVersion;
901             do
902             {
903                 /* Convert to Unicode */
904                 RtlInitAnsiString(&TempString, Buffer);
905                 RtlAnsiStringToUnicodeString(&Data, &TempString, TRUE);
906 
907                 /* Calculate the length of this string and copy it in */
908                 Length = Data.Length + sizeof(UNICODE_NULL);
909                 RtlMoveMemory(CurrentVersion, Data.Buffer, Length);
910 
911                 /* Free the unicode string */
912                 RtlFreeUnicodeString(&Data);
913 
914                 /* Update the total length and see if we're out of space */
915                 TotalLength += Length;
916                 if (TotalLength + 256 + sizeof(UNICODE_NULL) > PAGE_SIZE)
917                 {
918                     /* One more string would push us out, so stop here */
919                     break;
920                 }
921 
922                 /* Go to the next string inside the multi-string buffer */
923                 CurrentVersion += Length;
924 
925                 /* Query the next BIOS Version */
926             } while (CmpGetBiosVersion(NULL, 0, Buffer));
927 
928             /* Check if we found any strings at all */
929             if (TotalLength)
930             {
931                 /* Add the final null-terminator */
932                 *(PWSTR)CurrentVersion = UNICODE_NULL;
933                 TotalLength += sizeof(UNICODE_NULL);
934 
935                 /* Write the BIOS Version to the registry */
936                 RtlInitUnicodeString(&ValueName, L"VideoBiosVersion");
937                 Status = NtSetValueKey(SystemHandle,
938                                        &ValueName,
939                                        0,
940                                        REG_MULTI_SZ,
941                                        BiosVersion,
942                                        TotalLength);
943 
944                 /* ROS: Save a copy for bugzilla reporting */
945                 RtlCreateUnicodeString(&KeRosVideoBiosVersion, (PWCH)BiosVersion);
946             }
947         }
948 
949         /* Unmap the section */
950         ZwUnmapViewOfSection(NtCurrentProcess(), BaseAddress);
951     }
952 
953     /* Close the section */
954     ZwClose(SectionHandle);
955 
956     /* Free the BIOS version string buffer */
957     if (BiosVersion) ExFreePoolWithTag(BiosVersion, TAG_CM);
958 
959 Quickie:
960     /* Close the processor handle */
961     NtClose(KeyHandle);
962     return STATUS_SUCCESS;
963 }
964