xref: /reactos/ntoskrnl/config/i386/cmhardwr.c (revision c2c66aff)
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 (ExpInTextModeSetup)
405     {
406         if (!NT_SUCCESS(Status))
407             BiosHandle = NULL;
408     }
409     else if (!NT_SUCCESS(Status))
410     {
411         NtClose(SystemHandle);
412         return Status;
413     }
414 
415     /* Create the CPU Key, and check if it already existed */
416     RtlInitUnicodeString(&KeyName, L"CentralProcessor");
417     InitializeObjectAttributes(&ObjectAttributes,
418                                &KeyName,
419                                OBJ_CASE_INSENSITIVE,
420                                SystemHandle,
421                                NULL);
422     Status = NtCreateKey(&KeyHandle,
423                          KEY_READ | KEY_WRITE,
424                          &ObjectAttributes,
425                          0,
426                          NULL,
427                          0,
428                          &Disposition);
429     NtClose(KeyHandle);
430 
431     /* The key shouldn't already exist */
432     if (Disposition == REG_CREATED_NEW_KEY)
433     {
434         /* Allocate the configuration data for cmconfig.c */
435         CmpConfigurationData = ExAllocatePoolWithTag(PagedPool,
436                                                      CmpConfigurationAreaSize,
437                                                      TAG_CM);
438         if (!CmpConfigurationData)
439         {
440             // FIXME: Cleanup stuff!!
441             return STATUS_INSUFFICIENT_RESOURCES;
442         }
443 
444         /* Loop all CPUs */
445         for (i = 0; i < KeNumberProcessors; i++)
446         {
447             /* Get the PRCB */
448             Prcb = KiProcessorBlock[i];
449 
450             /* Setup the Configuration Entry for the Processor */
451             RtlZeroMemory(&ConfigData, sizeof(ConfigData));
452             ConfigData.ComponentEntry.Class = ProcessorClass;
453             ConfigData.ComponentEntry.Type = CentralProcessor;
454             ConfigData.ComponentEntry.Key = i;
455             ConfigData.ComponentEntry.AffinityMask = AFFINITY_MASK(i);
456             ConfigData.ComponentEntry.Identifier = Buffer;
457 
458             /* Check if the CPU doesn't support CPUID */
459             if (!Prcb->CpuID)
460             {
461                 /* Build ID1-style string for older CPUs */
462                 sprintf(Buffer,
463                         CmpID1,
464                         Prcb->CpuType,
465                         (Prcb->CpuStep >> 8) + 'A',
466                         Prcb->CpuStep & 0xff);
467             }
468             else
469             {
470                 /* Build ID2-style string for newer CPUs */
471                 sprintf(Buffer,
472                         CmpID2,
473                         Prcb->CpuType,
474                         (Prcb->CpuStep >> 8),
475                         Prcb->CpuStep & 0xff);
476             }
477 
478             /* Save the ID string length now that we've created it */
479             ConfigData.ComponentEntry.IdentifierLength = (ULONG)strlen(Buffer) + 1;
480 
481             /* Initialize the registry configuration node for it */
482             Status = CmpInitializeRegistryNode(&ConfigData,
483                                                SystemHandle,
484                                                &KeyHandle,
485                                                InterfaceTypeUndefined,
486                                                0xFFFFFFFF,
487                                                IndexTable);
488             if (!NT_SUCCESS(Status))
489             {
490                 NtClose(BiosHandle);
491                 NtClose(SystemHandle);
492                 return Status;
493             }
494 
495             /* Check if we have an FPU */
496             if (KeI386NpxPresent)
497             {
498                 /* Setup the Configuration Entry for the FPU */
499                 RtlZeroMemory(&ConfigData, sizeof(ConfigData));
500                 ConfigData.ComponentEntry.Class = ProcessorClass;
501                 ConfigData.ComponentEntry.Type = FloatingPointProcessor;
502                 ConfigData.ComponentEntry.Key = i;
503                 ConfigData.ComponentEntry.AffinityMask = AFFINITY_MASK(i);
504                 ConfigData.ComponentEntry.Identifier = Buffer;
505 
506                 /* For 386 cpus, the CPU pp is the identifier */
507                 if (Prcb->CpuType == 3) strcpy(Buffer, "80387");
508 
509                 /* Save the ID string length now that we've created it */
510                 ConfigData.ComponentEntry.IdentifierLength = (ULONG)strlen(Buffer) + 1;
511 
512                 /* Initialize the registry configuration node for it */
513                 Status = CmpInitializeRegistryNode(&ConfigData,
514                                                    SystemHandle,
515                                                    &FpuHandle,
516                                                    InterfaceTypeUndefined,
517                                                    0xFFFFFFFF,
518                                                    IndexTable);
519                 if (!NT_SUCCESS(Status))
520                 {
521                     /* We failed, close all the opened handles and return */
522                     NtClose(KeyHandle);
523                     NtClose(BiosHandle);
524                     NtClose(SystemHandle);
525                     return Status;
526                 }
527 
528                 /* Close this new handle */
529                 NtClose(FpuHandle);
530 
531                 /* Stay on this CPU only */
532                 KeSetSystemAffinityThread(Prcb->SetMember);
533                 if (!Prcb->CpuID)
534                 {
535                     /* Uh oh, no CPUID! */
536                     PartialString = CpuString;
537                     CmpGetVendorString(Prcb, PartialString);
538                 }
539                 else
540                 {
541                     /* Check if we have extended CPUID that supports name ID */
542                     KiCpuId(&CpuInfo, 0x80000000);
543                     ExtendedId = CpuInfo.Eax;
544                     if (ExtendedId >= 0x80000004)
545                     {
546                         /* Do all the CPUIDs required to get the full name */
547                         PartialString = CpuString;
548                         for (ExtendedId = 2; ExtendedId <= 4; ExtendedId++)
549                         {
550                             /* Do the CPUID and save the name string */
551                             KiCpuId(&CpuInfo, 0x80000000 | ExtendedId);
552                             ((PULONG)PartialString)[0] = CpuInfo.Eax;
553                             ((PULONG)PartialString)[1] = CpuInfo.Ebx;
554                             ((PULONG)PartialString)[2] = CpuInfo.Ecx;
555                             ((PULONG)PartialString)[3] = CpuInfo.Edx;
556 
557                             /* Go to the next name string */
558                             PartialString += 16;
559                         }
560 
561                         /* Null-terminate it */
562                         CpuString[47] = ANSI_NULL;
563                     }
564                     else
565                     {
566                         KiCpuId(&CpuInfo, 0x00000000);
567                         VendorId = CpuInfo.Ebx;
568                         PartialString = CpuString;
569                         switch (VendorId)
570                         {
571                             case 'uneG': /* Intel */
572                                 CmpGetIntelBrandString(PartialString);
573                                 break;
574                             case 'htuA': /* AMD */
575                                 /* FIXME */
576                                 CmpGetVendorString(Prcb, PartialString);
577                                 break;
578                             default:
579                                 CmpGetVendorString(Prcb, PartialString);
580                         }
581                     }
582                 }
583 
584                 /* Go back to user affinity */
585                 KeRevertToUserAffinityThread();
586 
587                 /* Check if we have a CPU Name */
588                 if (PartialString)
589                 {
590                     /* Convert it to Unicode */
591                     RtlInitAnsiString(&TempString, CpuString);
592                     RtlAnsiStringToUnicodeString(&Data, &TempString, TRUE);
593 
594                     /* Add it to the registry */
595                     RtlInitUnicodeString(&ValueName, L"ProcessorNameString");
596                     Status = NtSetValueKey(KeyHandle,
597                                            &ValueName,
598                                            0,
599                                            REG_SZ,
600                                            Data.Buffer,
601                                            Data.Length + sizeof(UNICODE_NULL));
602 
603                     /* ROS: Save a copy for bugzilla reporting */
604                     RtlCreateUnicodeString(&KeRosProcessorName, Data.Buffer);
605 
606                     /* Free the temporary buffer */
607                     RtlFreeUnicodeString(&Data);
608                 }
609 
610                 /* Check if we had a Vendor ID */
611                 if (Prcb->VendorString[0])
612                 {
613                     /* Convert it to Unicode */
614                     RtlInitAnsiString(&TempString, Prcb->VendorString);
615                     RtlAnsiStringToUnicodeString(&Data, &TempString, TRUE);
616 
617                     /* Add it to the registry */
618                     RtlInitUnicodeString(&ValueName, L"VendorIdentifier");
619                     Status = NtSetValueKey(KeyHandle,
620                                            &ValueName,
621                                            0,
622                                            REG_SZ,
623                                            Data.Buffer,
624                                            Data.Length + sizeof(UNICODE_NULL));
625 
626                     /* Free the temporary buffer */
627                     RtlFreeUnicodeString(&Data);
628                 }
629 
630                 /* Check if we have features bits */
631                 if (Prcb->FeatureBits)
632                 {
633                     /* Add them to the registry */
634                     RtlInitUnicodeString(&ValueName, L"FeatureSet");
635                     Status = NtSetValueKey(KeyHandle,
636                                            &ValueName,
637                                            0,
638                                            REG_DWORD,
639                                            &Prcb->FeatureBits,
640                                            sizeof(Prcb->FeatureBits));
641                 }
642 
643                 /* Check if we detected the CPU Speed */
644                 if (Prcb->MHz)
645                 {
646                     /* Add it to the registry */
647                     RtlInitUnicodeString(&ValueName, L"~MHz");
648                     Status = NtSetValueKey(KeyHandle,
649                                            &ValueName,
650                                            0,
651                                            REG_DWORD,
652                                            &Prcb->MHz,
653                                            sizeof(Prcb->MHz));
654                 }
655 
656                 /* Check if we have an update signature */
657                 if (Prcb->UpdateSignature.QuadPart)
658                 {
659                     /* Add it to the registry */
660                     RtlInitUnicodeString(&ValueName, L"Update Signature");
661                     Status = NtSetValueKey(KeyHandle,
662                                            &ValueName,
663                                            0,
664                                            REG_BINARY,
665                                            &Prcb->UpdateSignature,
666                                            sizeof(Prcb->UpdateSignature));
667                 }
668 
669                 /* Close the processor handle */
670                 NtClose(KeyHandle);
671 
672                 /* FIXME: Detect CPU mismatches */
673             }
674         }
675 
676         /* Free the configuration data */
677         ExFreePoolWithTag(CmpConfigurationData, TAG_CM);
678     }
679 
680     /* Open physical memory */
681     RtlInitUnicodeString(&SectionName, L"\\Device\\PhysicalMemory");
682     InitializeObjectAttributes(&ObjectAttributes,
683                                &SectionName,
684                                OBJ_CASE_INSENSITIVE,
685                                NULL,
686                                NULL);
687     Status = ZwOpenSection(&SectionHandle,
688                            SECTION_ALL_ACCESS,
689                            &ObjectAttributes);
690     if (!NT_SUCCESS(Status))
691     {
692         /* We failed, close all the opened handles and return */
693         // NtClose(KeyHandle);
694         NtClose(BiosHandle);
695         NtClose(SystemHandle);
696         /* 'Quickie' closes KeyHandle */
697         goto Quickie;
698     }
699 
700     /* Map the first 1KB of memory to get the IVT */
701     ViewSize = PAGE_SIZE;
702     Status = ZwMapViewOfSection(SectionHandle,
703                                 NtCurrentProcess(),
704                                 &BaseAddress,
705                                 0,
706                                 ViewSize,
707                                 &ViewBase,
708                                 &ViewSize,
709                                 ViewUnmap,
710                                 MEM_DOS_LIM,
711                                 PAGE_READWRITE);
712     if (!NT_SUCCESS(Status))
713     {
714         /* Assume default */
715         VideoRomBase = 0xC0000;
716     }
717     else
718     {
719         /* Calculate the base address from the vector */
720         VideoRomBase = (*((PULONG)BaseAddress + 0x10) >> 12) & 0xFFFF0;
721         VideoRomBase += *((PULONG)BaseAddress + 0x10) & 0xFFF0;
722 
723         /* Now get to the actual ROM Start and make sure it's not invalid*/
724         VideoRomBase &= 0xFFFF8000;
725         if (VideoRomBase < 0xC0000) VideoRomBase = 0xC0000;
726 
727         /* And unmap the section */
728         ZwUnmapViewOfSection(NtCurrentProcess(), BaseAddress);
729     }
730 
731     /* Allocate BIOS Version pp Buffer */
732     BiosVersion = ExAllocatePoolWithTag(PagedPool, PAGE_SIZE, TAG_CM);
733 
734     /* Setup settings to map the 64K BIOS ROM */
735     BaseAddress = 0;
736     ViewSize = 16 * PAGE_SIZE;
737     ViewBase.LowPart = 0xF0000;
738     ViewBase.HighPart = 0;
739 
740     /* Map it */
741     Status = ZwMapViewOfSection(SectionHandle,
742                                 NtCurrentProcess(),
743                                 &BaseAddress,
744                                 0,
745                                 ViewSize,
746                                 &ViewBase,
747                                 &ViewSize,
748                                 ViewUnmap,
749                                 MEM_DOS_LIM,
750                                 PAGE_READWRITE);
751     if (NT_SUCCESS(Status))
752     {
753         /* Scan the ROM to get the BIOS Date */
754         if (CmpGetBiosDate(BaseAddress, 16 * PAGE_SIZE, Buffer, TRUE))
755         {
756             /* Convert it to Unicode */
757             RtlInitAnsiString(&TempString, Buffer);
758             RtlAnsiStringToUnicodeString(&Data, &TempString, TRUE);
759 
760             /* Write the date into the registry */
761             RtlInitUnicodeString(&ValueName, L"SystemBiosDate");
762             Status = NtSetValueKey(SystemHandle,
763                                    &ValueName,
764                                    0,
765                                    REG_SZ,
766                                    Data.Buffer,
767                                    Data.Length + sizeof(UNICODE_NULL));
768 
769             /* Free the string */
770             RtlFreeUnicodeString(&Data);
771 
772             if (BiosHandle)
773             {
774                 /* Get the BIOS Date Identifier */
775                 RtlCopyMemory(Buffer, (PCHAR)BaseAddress + (16 * PAGE_SIZE - 11), 8);
776                 Buffer[8] = ANSI_NULL;
777 
778                 /* Convert it to unicode */
779                 RtlInitAnsiString(&TempString, Buffer);
780                 Status = RtlAnsiStringToUnicodeString(&Data, &TempString, TRUE);
781                 if (NT_SUCCESS(Status))
782                 {
783                     /* Save it to the registry */
784                     Status = NtSetValueKey(BiosHandle,
785                                            &ValueName,
786                                            0,
787                                            REG_SZ,
788                                            Data.Buffer,
789                                            Data.Length + sizeof(UNICODE_NULL));
790 
791                     /* ROS: Save a copy for bugzilla reporting */
792                     RtlCreateUnicodeString(&KeRosBiosDate, Data.Buffer);
793 
794                     /* Free the string */
795                     RtlFreeUnicodeString(&Data);
796                 }
797 
798                 /* Close the bios information handle */
799                 NtClose(BiosHandle);
800             }
801         }
802 
803         /* Get the BIOS Version */
804         if (CmpGetBiosVersion(BaseAddress, 16 * PAGE_SIZE, Buffer))
805         {
806             /* Start at the beginning of our buffer */
807             CurrentVersion = BiosVersion;
808             do
809             {
810                 /* Convert to Unicode */
811                 RtlInitAnsiString(&TempString, Buffer);
812                 RtlAnsiStringToUnicodeString(&Data, &TempString, TRUE);
813 
814                 /* Calculate the length of this string and copy it in */
815                 Length = Data.Length + sizeof(UNICODE_NULL);
816                 RtlMoveMemory(CurrentVersion, Data.Buffer, Length);
817 
818                 /* Free the unicode string */
819                 RtlFreeUnicodeString(&Data);
820 
821                 /* Update the total length and see if we're out of space */
822                 TotalLength += Length;
823                 if (TotalLength + 256 + sizeof(UNICODE_NULL) > PAGE_SIZE)
824                 {
825                     /* One more string would push us out, so stop here */
826                     break;
827                 }
828 
829                 /* Go to the next string inside the multi-string buffer */
830                 CurrentVersion += Length;
831 
832                 /* Query the next BIOS Version */
833             } while (CmpGetBiosVersion(NULL, 0, Buffer));
834 
835             /* Check if we found any strings at all */
836             if (TotalLength)
837             {
838                 /* Add the final null-terminator */
839                 *(PWSTR)CurrentVersion = UNICODE_NULL;
840                 TotalLength += sizeof(UNICODE_NULL);
841 
842                 /* Write the BIOS Version to the registry */
843                 RtlInitUnicodeString(&ValueName, L"SystemBiosVersion");
844                 Status = NtSetValueKey(SystemHandle,
845                                        &ValueName,
846                                        0,
847                                        REG_MULTI_SZ,
848                                        BiosVersion,
849                                        TotalLength);
850 
851                 /* ROS: Save a copy for bugzilla reporting */
852                 RtlCreateUnicodeString(&KeRosBiosVersion, (PWCH)BiosVersion);
853             }
854         }
855 
856         /* Unmap the section */
857         ZwUnmapViewOfSection(NtCurrentProcess(), BaseAddress);
858     }
859 
860     /* Now prepare for Video BIOS Mapping of 32KB */
861     BaseAddress = 0;
862     ViewSize = 8 * PAGE_SIZE;
863     ViewBase.QuadPart = VideoRomBase;
864 
865     /* Map it */
866     Status = ZwMapViewOfSection(SectionHandle,
867                                 NtCurrentProcess(),
868                                 &BaseAddress,
869                                 0,
870                                 ViewSize,
871                                 &ViewBase,
872                                 &ViewSize,
873                                 ViewUnmap,
874                                 MEM_DOS_LIM,
875                                 PAGE_READWRITE);
876     if (NT_SUCCESS(Status))
877     {
878         /* Scan the ROM to get the BIOS Date */
879         if (CmpGetBiosDate(BaseAddress, 8 * PAGE_SIZE, Buffer, FALSE))
880         {
881             /* Convert it to Unicode */
882             RtlInitAnsiString(&TempString, Buffer);
883             RtlAnsiStringToUnicodeString(&Data, &TempString, TRUE);
884 
885             /* Write the date into the registry */
886             RtlInitUnicodeString(&ValueName, L"VideoBiosDate");
887             Status = NtSetValueKey(SystemHandle,
888                                    &ValueName,
889                                    0,
890                                    REG_SZ,
891                                    Data.Buffer,
892                                    Data.Length + sizeof(UNICODE_NULL));
893 
894             /* ROS: Save a copy for bugzilla reporting */
895             RtlCreateUnicodeString(&KeRosVideoBiosDate, Data.Buffer);
896 
897             /* Free the string */
898             RtlFreeUnicodeString(&Data);
899         }
900 
901         /* Get the Video BIOS Version */
902         if (CmpGetBiosVersion(BaseAddress, 8 * PAGE_SIZE, Buffer))
903         {
904             /* Start at the beginning of our buffer */
905             CurrentVersion = BiosVersion;
906             do
907             {
908                 /* Convert to Unicode */
909                 RtlInitAnsiString(&TempString, Buffer);
910                 RtlAnsiStringToUnicodeString(&Data, &TempString, TRUE);
911 
912                 /* Calculate the length of this string and copy it in */
913                 Length = Data.Length + sizeof(UNICODE_NULL);
914                 RtlMoveMemory(CurrentVersion, Data.Buffer, Length);
915 
916                 /* Free the unicode string */
917                 RtlFreeUnicodeString(&Data);
918 
919                 /* Update the total length and see if we're out of space */
920                 TotalLength += Length;
921                 if (TotalLength + 256 + sizeof(UNICODE_NULL) > PAGE_SIZE)
922                 {
923                     /* One more string would push us out, so stop here */
924                     break;
925                 }
926 
927                 /* Go to the next string inside the multi-string buffer */
928                 CurrentVersion += Length;
929 
930                 /* Query the next BIOS Version */
931             } while (CmpGetBiosVersion(NULL, 0, Buffer));
932 
933             /* Check if we found any strings at all */
934             if (TotalLength)
935             {
936                 /* Add the final null-terminator */
937                 *(PWSTR)CurrentVersion = UNICODE_NULL;
938                 TotalLength += sizeof(UNICODE_NULL);
939 
940                 /* Write the BIOS Version to the registry */
941                 RtlInitUnicodeString(&ValueName, L"VideoBiosVersion");
942                 Status = NtSetValueKey(SystemHandle,
943                                        &ValueName,
944                                        0,
945                                        REG_MULTI_SZ,
946                                        BiosVersion,
947                                        TotalLength);
948 
949                 /* ROS: Save a copy for bugzilla reporting */
950                 RtlCreateUnicodeString(&KeRosVideoBiosVersion, (PWCH)BiosVersion);
951             }
952         }
953 
954         /* Unmap the section */
955         ZwUnmapViewOfSection(NtCurrentProcess(), BaseAddress);
956     }
957 
958     /* Close the section */
959     ZwClose(SectionHandle);
960 
961     /* Free the BIOS version string buffer */
962     if (BiosVersion) ExFreePoolWithTag(BiosVersion, TAG_CM);
963 
964 Quickie:
965     /* Close the processor handle */
966     NtClose(KeyHandle);
967     return STATUS_SUCCESS;
968 }
969