[SHELL32] SHChangeNotify: Use tree for CDirectoryList (#6784)
[reactos.git] / ntoskrnl / config / i386 / cmhardwr.c
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 CmpFullCpuID = "%s Family %u Model %u Stepping %u";
18 PCHAR CmpBiosStrings[] =
19 {
20 "Ver",
21 "Rev",
22 "Rel",
23 "v0", "v1", "v2", "v3", "v4", "v5", "v6", "v7", "v8", "v9",
24 "v 0", "v 1", "v 2", "v 3", "v 4", "v 5", "v 6", "v 7", "v 8", "v 9",
25 NULL
26 };
27
28 PCHAR CmpBiosBegin, CmpBiosSearchStart, CmpBiosSearchEnd;
29
30 /* FUNCTIONS *****************************************************************/
31
32 BOOLEAN
33 NTAPI
34 CmpGetBiosDate(IN PCHAR BiosStart,
35 IN ULONG BiosLength,
36 IN PCHAR BiosDate,
37 IN BOOLEAN FromBios)
38 {
39 CHAR LastDate[11] = {0}, CurrentDate[11];
40 PCHAR p, pp;
41
42 /* Skip the signature and the magic, and loop the BIOS ROM */
43 p = BiosStart + 2;
44 pp = BiosStart + BiosLength - 5;
45 while (p < pp)
46 {
47 /* Check for xx/yy/zz which we assume to be a date */
48 if ((p[0] == '/') &&
49 (p[3] == '/') &&
50 (isdigit(p[-1])) &&
51 (isdigit(p[1])) &&
52 (isdigit(p[2])) &&
53 (isdigit(p[4])) &&
54 (isdigit(p[5])))
55 {
56 /* Copy the string proper */
57 RtlMoveMemory(&CurrentDate[5], p - 2, 5);
58
59 /* Add a 0 if the month only has one digit */
60 if (!isdigit(CurrentDate[5])) CurrentDate[5] = '0';
61
62 /* Now copy the year */
63 CurrentDate[2] = p[4];
64 CurrentDate[3] = p[5];
65 CurrentDate[4] = CurrentDate[7] = CurrentDate[10] = ANSI_NULL;
66
67 /* If the date comes from the BIOS, check if it's a 4-digit year */
68 if ((FromBios) &&
69 (isdigit(p[6])) &&
70 (isdigit(p[7])) &&
71 ((RtlEqualMemory(&p[4], "19", 2)) ||
72 (RtlEqualMemory(&p[4], "20", 2))))
73 {
74 /* Copy the year proper */
75 CurrentDate[0] = p[4];
76 CurrentDate[1] = p[5];
77 CurrentDate[2] = p[6];
78 CurrentDate[3] = p[7];
79 }
80 else
81 {
82 /* Otherwise, we'll just assume anything under 80 is 2000 */
83 if (strtoul(&CurrentDate[2], NULL, 10) < 80)
84 {
85 /* Hopefully your BIOS wasn't made in 1979 */
86 CurrentDate[0] = '2';
87 CurrentDate[1] = '0';
88 }
89 else
90 {
91 /* Anything over 80, was probably made in the 1900s... */
92 CurrentDate[0] = '1';
93 CurrentDate[1] = '9';
94 }
95 }
96
97 /* Add slashes where we previously had NULLs */
98 CurrentDate[4] = CurrentDate[7] = '/';
99
100 /* Check which date is newer */
101 if (memcmp(LastDate, CurrentDate, 10) < 0)
102 {
103 /* Found a newer date, select it */
104 RtlMoveMemory(LastDate, CurrentDate, 10);
105 }
106
107 p += 2;
108 }
109 p++;
110 }
111
112 /* Make sure we found a date */
113 if (LastDate[0])
114 {
115 /* Copy the year at the pp, and keep only the last two digits */
116 RtlMoveMemory(BiosDate, &LastDate[5], 5);
117 BiosDate[5] = '/';
118 BiosDate[6] = LastDate[2];
119 BiosDate[7] = LastDate[3];
120 BiosDate[8] = ANSI_NULL;
121 return TRUE;
122 }
123
124 /* No date found, return empty string */
125 BiosDate[0] = ANSI_NULL;
126 return FALSE;
127 }
128
129 BOOLEAN
130 NTAPI
131 CmpGetBiosVersion(IN PCHAR BiosStart,
132 IN ULONG BiosLength,
133 IN PCHAR BiosVersion)
134 {
135 CHAR Buffer[128];
136 PCHAR p, pp;
137 USHORT i;
138
139 /* Check if we were given intitial data for the search */
140 if (BiosStart)
141 {
142 /* Save it for later use */
143 CmpBiosBegin = BiosStart;
144 CmpBiosSearchStart = BiosStart + 1;
145 CmpBiosSearchEnd = BiosStart + BiosLength - 2;
146 }
147
148 /* Now loop the BIOS area */
149 for (;;)
150 {
151 /* Start an initial search looking for numbers and periods */
152 pp = NULL;
153 while (CmpBiosSearchStart <= CmpBiosSearchEnd)
154 {
155 /* Check if we have an "x.y" version string */
156 if ((*CmpBiosSearchStart == '.') &&
157 (*(CmpBiosSearchStart + 1) >= '0') &&
158 (*(CmpBiosSearchStart + 1) <= '9') &&
159 (*(CmpBiosSearchStart - 1) >= '0') &&
160 (*(CmpBiosSearchStart - 1) <= '9'))
161 {
162 /* Start looking in this area for the actual BIOS Version */
163 pp = CmpBiosSearchStart;
164 break;
165 }
166 else
167 {
168 /* Keep searching */
169 CmpBiosSearchStart++;
170 }
171 }
172
173 /* Break out if we're went past the BIOS area */
174 if (CmpBiosSearchStart > CmpBiosSearchEnd) return FALSE;
175
176 /* Move to the next 2 bytes */
177 CmpBiosSearchStart += 2;
178
179 /* Null-terminate our scratch buffer and start the string here */
180 Buffer[127] = ANSI_NULL;
181 p = &Buffer[127];
182
183 /* Go back one character since we're doing this backwards */
184 pp--;
185
186 /* Loop the identifier we found as long as it's valid */
187 i = 0;
188 while ((i++ < 127) &&
189 (pp >= CmpBiosBegin) &&
190 (*pp >= ' ') &&
191 (*pp != '$'))
192 {
193 /* Copy the character */
194 *--p = *pp--;
195 }
196
197 /* Go past the last character since we went backwards */
198 pp++;
199
200 /* Loop the strings we recognize */
201 for (i = 0; CmpBiosStrings[i]; i++)
202 {
203 /* Check if a match was found */
204 if (strstr(p, CmpBiosStrings[i])) goto Match;
205 }
206 }
207
208 Match:
209 /* Skip until we find a space */
210 for (; *pp == ' '; pp++);
211
212 /* Loop the final string */
213 i = 0;
214 do
215 {
216 /* Copy the character into the final string */
217 BiosVersion[i] = *pp++;
218 } while ((++i < 127) &&
219 (pp <= (CmpBiosSearchEnd + 1)) &&
220 (*pp >= ' ') &&
221 (*pp != '$'));
222
223 /* Null-terminate the version string */
224 BiosVersion[i] = ANSI_NULL;
225 return TRUE;
226 }
227
228 NTSTATUS
229 NTAPI
230 CmpInitializeMachineDependentConfiguration(IN PLOADER_PARAMETER_BLOCK LoaderBlock)
231 {
232 UNICODE_STRING KeyName, ValueName, Data, SectionName;
233 OBJECT_ATTRIBUTES ObjectAttributes;
234 ULONG HavePae, Length, TotalLength = 0, i, Disposition;
235 SIZE_T ViewSize;
236 NTSTATUS Status;
237 HANDLE KeyHandle, BiosHandle, SystemHandle, FpuHandle, SectionHandle;
238 CONFIGURATION_COMPONENT_DATA ConfigData;
239 CHAR Buffer[128];
240 CPU_INFO CpuInfo;
241 ULONG ExtendedId;
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))
292 return Status;
293
294 /* Create the BIOS Information key */
295 RtlInitUnicodeString(&KeyName,
296 L"\\Registry\\Machine\\SYSTEM\\CurrentControlSet\\"
297 L"Control\\BIOSINFO");
298 InitializeObjectAttributes(&ObjectAttributes,
299 &KeyName,
300 OBJ_CASE_INSENSITIVE,
301 NULL,
302 NULL);
303 Status = NtCreateKey(&BiosHandle,
304 KEY_ALL_ACCESS,
305 &ObjectAttributes,
306 0,
307 NULL,
308 REG_OPTION_NON_VOLATILE,
309 &Disposition);
310 if (!NT_SUCCESS(Status))
311 {
312 NtClose(SystemHandle);
313 return Status;
314 }
315
316 /* Create the CPU Key, and check if it already existed */
317 RtlInitUnicodeString(&KeyName, L"CentralProcessor");
318 InitializeObjectAttributes(&ObjectAttributes,
319 &KeyName,
320 OBJ_CASE_INSENSITIVE,
321 SystemHandle,
322 NULL);
323 Status = NtCreateKey(&KeyHandle,
324 KEY_READ | KEY_WRITE,
325 &ObjectAttributes,
326 0,
327 NULL,
328 0,
329 &Disposition);
330 NtClose(KeyHandle);
331
332 /* The key shouldn't already exist */
333 if (Disposition == REG_CREATED_NEW_KEY)
334 {
335 /* Allocate the configuration data for cmconfig.c */
336 CmpConfigurationData = ExAllocatePoolWithTag(PagedPool,
337 CmpConfigurationAreaSize,
338 TAG_CM);
339 if (!CmpConfigurationData)
340 {
341 // FIXME: Cleanup stuff!!
342 return STATUS_INSUFFICIENT_RESOURCES;
343 }
344
345 /* Loop all CPUs */
346 for (i = 0; i < KeNumberProcessors; i++)
347 {
348 #ifdef _M_AMD64
349 PCHAR FamilyId;
350 #endif
351 /* Get the PRCB */
352 Prcb = KiProcessorBlock[i];
353
354 /* Setup the Configuration Entry for the Processor */
355 RtlZeroMemory(&ConfigData, sizeof(ConfigData));
356 ConfigData.ComponentEntry.Class = ProcessorClass;
357 ConfigData.ComponentEntry.Type = CentralProcessor;
358 ConfigData.ComponentEntry.Key = i;
359 ConfigData.ComponentEntry.AffinityMask = AFFINITY_MASK(i);
360 ConfigData.ComponentEntry.Identifier = Buffer;
361
362 #if defined(_M_IX86)
363 /* Check if the CPU doesn't support CPUID */
364 if (!Prcb->CpuID)
365 {
366 /* Build 80x86-style string for older CPUs */
367 sprintf(Buffer,
368 "80%u86-%c%x",
369 Prcb->CpuType,
370 (Prcb->CpuStep >> 8) + 'A',
371 Prcb->CpuStep & 0xff);
372 }
373 else
374 {
375 /* Build full ID string for newer CPUs */
376 sprintf(Buffer,
377 CmpFullCpuID,
378 "x86",
379 Prcb->CpuType,
380 (Prcb->CpuStep >> 8),
381 Prcb->CpuStep & 0xff);
382 }
383 #elif defined(_M_AMD64)
384 if (Prcb->CpuVendor == CPU_VIA)
385 {
386 /* This is VIA64 family */
387 FamilyId = "VIA64";
388 }
389 else if (Prcb->CpuVendor == CPU_AMD)
390 {
391 /* This is AMD64 family */
392 FamilyId = "AMD64";
393 }
394 else
395 {
396 /* This is generic EM64T family */
397 FamilyId = "EM64T";
398 }
399
400 /* ID string has the same style for all 64-bit CPUs */
401 sprintf(Buffer,
402 CmpFullCpuID,
403 FamilyId,
404 Prcb->CpuType,
405 (Prcb->CpuStep >> 8),
406 Prcb->CpuStep & 0xff);
407 #else
408 #error Unknown architecture
409 #endif
410
411 /* Save the ID string length now that we've created it */
412 ConfigData.ComponentEntry.IdentifierLength = (ULONG)strlen(Buffer) + 1;
413
414 /* Initialize the registry configuration node for it */
415 Status = CmpInitializeRegistryNode(&ConfigData,
416 SystemHandle,
417 &KeyHandle,
418 InterfaceTypeUndefined,
419 0xFFFFFFFF,
420 IndexTable);
421 if (!NT_SUCCESS(Status))
422 {
423 NtClose(BiosHandle);
424 NtClose(SystemHandle);
425 return Status;
426 }
427
428 /* Check if we have an FPU */
429 if (KeI386NpxPresent)
430 {
431 /* Setup the Configuration Entry for the FPU */
432 RtlZeroMemory(&ConfigData, sizeof(ConfigData));
433 ConfigData.ComponentEntry.Class = ProcessorClass;
434 ConfigData.ComponentEntry.Type = FloatingPointProcessor;
435 ConfigData.ComponentEntry.Key = i;
436 ConfigData.ComponentEntry.AffinityMask = AFFINITY_MASK(i);
437 ConfigData.ComponentEntry.Identifier = Buffer;
438
439 /* For 386 cpus, the CPU pp is the identifier */
440 if (Prcb->CpuType == 3) strcpy(Buffer, "80387");
441
442 /* Save the ID string length now that we've created it */
443 ConfigData.ComponentEntry.IdentifierLength = (ULONG)strlen(Buffer) + 1;
444
445 /* Initialize the registry configuration node for it */
446 Status = CmpInitializeRegistryNode(&ConfigData,
447 SystemHandle,
448 &FpuHandle,
449 InterfaceTypeUndefined,
450 0xFFFFFFFF,
451 IndexTable);
452 if (!NT_SUCCESS(Status))
453 {
454 /* We failed, close all the opened handles and return */
455 NtClose(KeyHandle);
456 NtClose(BiosHandle);
457 NtClose(SystemHandle);
458 return Status;
459 }
460
461 /* Close this new handle */
462 NtClose(FpuHandle);
463
464 /* Stay on this CPU only */
465 KeSetSystemAffinityThread(Prcb->SetMember);
466 if (!Prcb->CpuID)
467 {
468 /* Uh oh, no CPUID! Should not happen as we don't support 80386 and older 80486 */
469 ASSERT(FALSE);
470 }
471 else
472 {
473 /* Check if we have extended CPUID that supports name ID */
474 KiCpuId(&CpuInfo, 0x80000000);
475 ExtendedId = CpuInfo.Eax;
476 if (ExtendedId >= 0x80000004)
477 {
478 /* Do all the CPUIDs required to get the full name */
479 PartialString = CpuString;
480 for (ExtendedId = 2; ExtendedId <= 4; ExtendedId++)
481 {
482 /* Do the CPUID and save the name string */
483 KiCpuId(&CpuInfo, 0x80000000 | ExtendedId);
484 ((PULONG)PartialString)[0] = CpuInfo.Eax;
485 ((PULONG)PartialString)[1] = CpuInfo.Ebx;
486 ((PULONG)PartialString)[2] = CpuInfo.Ecx;
487 ((PULONG)PartialString)[3] = CpuInfo.Edx;
488
489 /* Go to the next name string */
490 PartialString += 16;
491 }
492
493 /* Null-terminate it */
494 CpuString[47] = ANSI_NULL;
495 }
496 }
497
498 /* Go back to user affinity */
499 KeRevertToUserAffinityThread();
500
501 /* Check if we have a CPU Name */
502 if (PartialString)
503 {
504 /* Convert it to Unicode */
505 RtlInitAnsiString(&TempString, CpuString);
506 if (NT_SUCCESS(RtlAnsiStringToUnicodeString(&Data, &TempString, TRUE)))
507 {
508 /* Add it to the registry */
509 RtlInitUnicodeString(&ValueName, L"ProcessorNameString");
510 Status = NtSetValueKey(KeyHandle,
511 &ValueName,
512 0,
513 REG_SZ,
514 Data.Buffer,
515 Data.Length + sizeof(UNICODE_NULL));
516
517 /* ROS: Save a copy for Jira reporting */
518 if (!RtlCreateUnicodeString(&KeRosProcessorName, Data.Buffer))
519 {
520 /* Do not fail for this */
521 KeRosProcessorName.Length = 0;
522 }
523
524 /* Free the temporary buffer */
525 RtlFreeUnicodeString(&Data);
526 }
527 }
528
529 /* Check if we had a Vendor ID */
530 if (Prcb->VendorString[0])
531 {
532 /* Convert it to Unicode */
533 RtlInitAnsiString(&TempString, Prcb->VendorString);
534 if (NT_SUCCESS(RtlAnsiStringToUnicodeString(&Data, &TempString, TRUE)))
535 {
536 /* Add it to the registry */
537 RtlInitUnicodeString(&ValueName, L"VendorIdentifier");
538 Status = NtSetValueKey(KeyHandle,
539 &ValueName,
540 0,
541 REG_SZ,
542 Data.Buffer,
543 Data.Length + sizeof(UNICODE_NULL));
544
545 /* Free the temporary buffer */
546 RtlFreeUnicodeString(&Data);
547 }
548 }
549
550 /* Check if we have features bits */
551 if (Prcb->FeatureBits)
552 {
553 /* Add them to the registry */
554 RtlInitUnicodeString(&ValueName, L"FeatureSet");
555 Status = NtSetValueKey(KeyHandle,
556 &ValueName,
557 0,
558 REG_DWORD,
559 &Prcb->FeatureBits,
560 sizeof(Prcb->FeatureBits));
561 }
562
563 /* Check if we detected the CPU Speed */
564 if (Prcb->MHz)
565 {
566 /* Add it to the registry */
567 RtlInitUnicodeString(&ValueName, L"~MHz");
568 Status = NtSetValueKey(KeyHandle,
569 &ValueName,
570 0,
571 REG_DWORD,
572 &Prcb->MHz,
573 sizeof(Prcb->MHz));
574 }
575
576 /* Check if we have an update signature */
577 if (Prcb->UpdateSignature.QuadPart)
578 {
579 /* Add it to the registry */
580 RtlInitUnicodeString(&ValueName, L"Update Signature");
581 Status = NtSetValueKey(KeyHandle,
582 &ValueName,
583 0,
584 REG_BINARY,
585 &Prcb->UpdateSignature,
586 sizeof(Prcb->UpdateSignature));
587 }
588
589 /* Close the processor handle */
590 NtClose(KeyHandle);
591
592 /* FIXME: Detect CPU mismatches */
593 }
594 }
595
596 /* Free the configuration data */
597 ExFreePoolWithTag(CmpConfigurationData, TAG_CM);
598 }
599
600 /* Open physical memory */
601 RtlInitUnicodeString(&SectionName, L"\\Device\\PhysicalMemory");
602 InitializeObjectAttributes(&ObjectAttributes,
603 &SectionName,
604 OBJ_CASE_INSENSITIVE,
605 NULL,
606 NULL);
607 Status = ZwOpenSection(&SectionHandle,
608 SECTION_ALL_ACCESS,
609 &ObjectAttributes);
610 if (!NT_SUCCESS(Status))
611 {
612 /* We failed, close all the opened handles and return */
613 // NtClose(KeyHandle);
614 NtClose(BiosHandle);
615 NtClose(SystemHandle);
616 /* 'Quickie' closes KeyHandle */
617 goto Quickie;
618 }
619
620 /* Map the first 1KB of memory to get the IVT */
621 ViewSize = PAGE_SIZE;
622 Status = ZwMapViewOfSection(SectionHandle,
623 NtCurrentProcess(),
624 &BaseAddress,
625 0,
626 ViewSize,
627 &ViewBase,
628 &ViewSize,
629 ViewUnmap,
630 MEM_DOS_LIM,
631 PAGE_READWRITE);
632 if (!NT_SUCCESS(Status))
633 {
634 /* Assume default */
635 VideoRomBase = 0xC0000;
636 }
637 else
638 {
639 /* Calculate the base address from the vector */
640 VideoRomBase = (*((PULONG)BaseAddress + 0x10) >> 12) & 0xFFFF0;
641 VideoRomBase += *((PULONG)BaseAddress + 0x10) & 0xFFF0;
642
643 /* Now get to the actual ROM Start and make sure it's not invalid*/
644 VideoRomBase &= 0xFFFF8000;
645 if (VideoRomBase < 0xC0000) VideoRomBase = 0xC0000;
646
647 /* And unmap the section */
648 ZwUnmapViewOfSection(NtCurrentProcess(), BaseAddress);
649 }
650
651 /* Allocate BIOS Version pp Buffer */
652 BiosVersion = ExAllocatePoolWithTag(PagedPool, PAGE_SIZE, TAG_CM);
653
654 /* Setup settings to map the 64K BIOS ROM */
655 BaseAddress = 0;
656 ViewSize = 16 * PAGE_SIZE;
657 ViewBase.LowPart = 0xF0000;
658 ViewBase.HighPart = 0;
659
660 /* Map it */
661 Status = ZwMapViewOfSection(SectionHandle,
662 NtCurrentProcess(),
663 &BaseAddress,
664 0,
665 ViewSize,
666 &ViewBase,
667 &ViewSize,
668 ViewUnmap,
669 MEM_DOS_LIM,
670 PAGE_READWRITE);
671 if (NT_SUCCESS(Status))
672 {
673 /* Scan the ROM to get the BIOS Date */
674 if (CmpGetBiosDate(BaseAddress, 16 * PAGE_SIZE, Buffer, TRUE))
675 {
676 /* Convert it to Unicode */
677 RtlInitAnsiString(&TempString, Buffer);
678 if (NT_SUCCESS(RtlAnsiStringToUnicodeString(&Data, &TempString, TRUE)))
679 {
680 /* Write the date into the registry */
681 RtlInitUnicodeString(&ValueName, L"SystemBiosDate");
682 Status = NtSetValueKey(SystemHandle,
683 &ValueName,
684 0,
685 REG_SZ,
686 Data.Buffer,
687 Data.Length + sizeof(UNICODE_NULL));
688
689 /* Free the string */
690 RtlFreeUnicodeString(&Data);
691 }
692
693 if (BiosHandle)
694 {
695 /* Get the BIOS Date Identifier */
696 RtlCopyMemory(Buffer, (PCHAR)BaseAddress + (16 * PAGE_SIZE - 11), 8);
697 Buffer[8] = ANSI_NULL;
698
699 /* Convert it to unicode */
700 RtlInitAnsiString(&TempString, Buffer);
701 Status = RtlAnsiStringToUnicodeString(&Data, &TempString, TRUE);
702 if (NT_SUCCESS(Status))
703 {
704 /* Save it to the registry */
705 Status = NtSetValueKey(BiosHandle,
706 &ValueName,
707 0,
708 REG_SZ,
709 Data.Buffer,
710 Data.Length + sizeof(UNICODE_NULL));
711
712 /* ROS: Save a copy for Jira reporting */
713 if (!RtlCreateUnicodeString(&KeRosBiosDate, Data.Buffer))
714 KeRosBiosDate.Length = 0;
715
716 /* Free the string */
717 RtlFreeUnicodeString(&Data);
718 }
719
720 /* Close the bios information handle */
721 NtClose(BiosHandle);
722 }
723 }
724
725 /* Get the BIOS Version */
726 if (CmpGetBiosVersion(BaseAddress, 16 * PAGE_SIZE, Buffer))
727 {
728 /* Start at the beginning of our buffer */
729 CurrentVersion = BiosVersion;
730 do
731 {
732 /* Convert to Unicode */
733 RtlInitAnsiString(&TempString, Buffer);
734 Status = RtlAnsiStringToUnicodeString(&Data, &TempString, TRUE);
735 if (!NT_SUCCESS(Status))
736 break;
737
738 /* Calculate the length of this string and copy it in */
739 Length = Data.Length + sizeof(UNICODE_NULL);
740 RtlMoveMemory(CurrentVersion, Data.Buffer, Length);
741
742 /* Free the unicode string */
743 RtlFreeUnicodeString(&Data);
744
745 /* Update the total length and see if we're out of space */
746 TotalLength += Length;
747 if (TotalLength + 256 + sizeof(UNICODE_NULL) > PAGE_SIZE)
748 {
749 /* One more string would push us out, so stop here */
750 break;
751 }
752
753 /* Go to the next string inside the multi-string buffer */
754 CurrentVersion += Length;
755
756 /* Query the next BIOS Version */
757 } while (CmpGetBiosVersion(NULL, 0, Buffer));
758
759 /* Check if we found any strings at all */
760 if (TotalLength)
761 {
762 /* Add the final null-terminator */
763 *(PWSTR)CurrentVersion = UNICODE_NULL;
764 TotalLength += sizeof(UNICODE_NULL);
765
766 /* Write the BIOS Version to the registry */
767 RtlInitUnicodeString(&ValueName, L"SystemBiosVersion");
768 Status = NtSetValueKey(SystemHandle,
769 &ValueName,
770 0,
771 REG_MULTI_SZ,
772 BiosVersion,
773 TotalLength);
774
775 /* ROS: Save a copy for Jira reporting */
776 if (!RtlCreateUnicodeString(&KeRosBiosVersion, (PWCH)BiosVersion))
777 KeRosBiosVersion.Length = 0;
778 }
779 }
780
781 /* Unmap the section */
782 ZwUnmapViewOfSection(NtCurrentProcess(), BaseAddress);
783 }
784
785 /* Now prepare for Video BIOS Mapping of 32KB */
786 BaseAddress = 0;
787 ViewSize = 8 * PAGE_SIZE;
788 ViewBase.QuadPart = VideoRomBase;
789
790 /* Map it */
791 Status = ZwMapViewOfSection(SectionHandle,
792 NtCurrentProcess(),
793 &BaseAddress,
794 0,
795 ViewSize,
796 &ViewBase,
797 &ViewSize,
798 ViewUnmap,
799 MEM_DOS_LIM,
800 PAGE_READWRITE);
801 if (NT_SUCCESS(Status))
802 {
803 /* Scan the ROM to get the BIOS Date */
804 if (CmpGetBiosDate(BaseAddress, 8 * PAGE_SIZE, Buffer, FALSE))
805 {
806 /* Convert it to Unicode */
807 RtlInitAnsiString(&TempString, Buffer);
808 if (NT_SUCCESS(RtlAnsiStringToUnicodeString(&Data, &TempString, TRUE)))
809 {
810 /* Write the date into the registry */
811 RtlInitUnicodeString(&ValueName, L"VideoBiosDate");
812 Status = NtSetValueKey(SystemHandle,
813 &ValueName,
814 0,
815 REG_SZ,
816 Data.Buffer,
817 Data.Length + sizeof(UNICODE_NULL));
818
819 /* ROS: Save a copy for Jira reporting */
820 if (!RtlCreateUnicodeString(&KeRosVideoBiosDate, Data.Buffer))
821 KeRosVideoBiosDate.Length = 0;
822
823 /* Free the string */
824 RtlFreeUnicodeString(&Data);
825 }
826 }
827
828 /* Get the Video BIOS Version */
829 if (CmpGetBiosVersion(BaseAddress, 8 * PAGE_SIZE, Buffer))
830 {
831 /* Start at the beginning of our buffer */
832 CurrentVersion = BiosVersion;
833 do
834 {
835 /* Convert to Unicode */
836 RtlInitAnsiString(&TempString, Buffer);
837 if (!NT_SUCCESS(RtlAnsiStringToUnicodeString(&Data, &TempString, TRUE)))
838 break;
839
840 /* Calculate the length of this string and copy it in */
841 Length = Data.Length + sizeof(UNICODE_NULL);
842 RtlMoveMemory(CurrentVersion, Data.Buffer, Length);
843
844 /* Free the unicode string */
845 RtlFreeUnicodeString(&Data);
846
847 /* Update the total length and see if we're out of space */
848 TotalLength += Length;
849 if (TotalLength + 256 + sizeof(UNICODE_NULL) > PAGE_SIZE)
850 {
851 /* One more string would push us out, so stop here */
852 break;
853 }
854
855 /* Go to the next string inside the multi-string buffer */
856 CurrentVersion += Length;
857
858 /* Query the next BIOS Version */
859 } while (CmpGetBiosVersion(NULL, 0, Buffer));
860
861 /* Check if we found any strings at all */
862 if (TotalLength)
863 {
864 /* Add the final null-terminator */
865 *(PWSTR)CurrentVersion = UNICODE_NULL;
866 TotalLength += sizeof(UNICODE_NULL);
867
868 /* Write the BIOS Version to the registry */
869 RtlInitUnicodeString(&ValueName, L"VideoBiosVersion");
870 Status = NtSetValueKey(SystemHandle,
871 &ValueName,
872 0,
873 REG_MULTI_SZ,
874 BiosVersion,
875 TotalLength);
876
877 /* ROS: Save a copy for Jira reporting */
878 if (!RtlCreateUnicodeString(&KeRosVideoBiosVersion, (PWCH)BiosVersion))
879 KeRosVideoBiosVersion.Length = 0;
880 }
881 }
882
883 /* Unmap the section */
884 ZwUnmapViewOfSection(NtCurrentProcess(), BaseAddress);
885 }
886
887 /* Close the section */
888 ZwClose(SectionHandle);
889
890 /* Free the BIOS version string buffer */
891 if (BiosVersion) ExFreePoolWithTag(BiosVersion, TAG_CM);
892
893 Quickie:
894 /* Close the processor handle */
895 NtClose(KeyHandle);
896 return STATUS_SUCCESS;
897 }