xref: /reactos/boot/environ/lib/misc/util.c (revision 8540ab04)
1 /*
2  * COPYRIGHT:       See COPYING.ARM in the top level directory
3  * PROJECT:         ReactOS UEFI Boot Library
4  * FILE:            boot/environ/lib/misc/util.c
5  * PURPOSE:         Boot Library Utility Functions
6  * PROGRAMMER:      Alex Ionescu (alex.ionescu@reactos.org)
7  */
8 
9 /* INCLUDES ******************************************************************/
10 
11 #include "bl.h"
12 
13 /* DATA VARIABLES ************************************************************/
14 
15 PRSDT UtlRsdt;
16 PXSDT UtlXsdt;
17 
18 PVOID UtlMcContext;
19 PVOID UtlMcDisplayMessageRoutine;
20 PVOID UtlMcUpdateMessageRoutine;
21 
22 PVOID UtlProgressRoutine;
23 PVOID UtlProgressContext;
24 PVOID UtlProgressInfoRoutine;
25 ULONG UtlProgressGranularity;
26 ULONG UtlCurrentPercentComplete;
27 ULONG UtlNextUpdatePercentage;
28 BOOLEAN UtlProgressNeedsInfoUpdate;
29 PVOID UtlProgressInfo;
30 
31 /* FUNCTIONS *****************************************************************/
32 
33 NTSTATUS
34 BlUtlGetAcpiTable (
35     _Out_ PVOID* TableAddress,
36     _In_ ULONG Signature
37     )
38 {
39     ULONG i, TableCount, HeaderLength;
40     NTSTATUS Status;
41     PRSDT Rsdt;
42     PXSDT Xsdt;
43     PHYSICAL_ADDRESS PhysicalAddress;
44     PDESCRIPTION_HEADER Header;
45 
46     Header = 0;
47 
48     /* Make sure there's an output parameter */
49     if (!TableAddress)
50     {
51         return STATUS_INVALID_PARAMETER;
52     }
53 
54     /* Get the currently known RSDT and XSDT */
55     Rsdt = (PRSDT)UtlRsdt;
56     Xsdt = (PXSDT)UtlXsdt;
57 
58     /* Is there an RSDT? */
59     if (!Rsdt)
60     {
61         /* No -- is there an XSDT? */
62         if (!Xsdt)
63         {
64             /* No. Look up the RSDT */
65             Status = EfipGetRsdt(&PhysicalAddress);
66             if (!NT_SUCCESS(Status))
67             {
68                 EfiPrintf(L"no rsdp found\r\n");
69                 return Status;
70             }
71 
72             /* Map the header */
73             Status = BlMmMapPhysicalAddressEx((PVOID)&Header,
74                                               0,
75                                               sizeof(*Header),
76                                               PhysicalAddress);
77             if (!NT_SUCCESS(Status))
78             {
79                 return Status;
80             }
81 
82             /* Unmap the header */
83             BlMmUnmapVirtualAddressEx(Header, sizeof(*Header));
84 
85             /* Map the whole table */
86             Status = BlMmMapPhysicalAddressEx((PVOID)&Header,
87                                               0,
88                                               Header->Length,
89                                               PhysicalAddress);
90             if (!NT_SUCCESS(Status))
91             {
92                 return Status;
93             }
94 
95             /* Check if its an XSDT or an RSDT */
96             if (Header->Signature == XSDT_SIGNATURE)
97             {
98                 /* It's an XSDT */
99                 Xsdt = (PXSDT)Header;
100                 UtlXsdt = Xsdt;
101             }
102             else
103             {
104                 /* It's an RSDT */
105                 Rsdt = (PRSDT)Header;
106                 UtlRsdt = Rsdt;
107             }
108         }
109     }
110 
111     /* OK, so do we have an XSDT after all? */
112     if (Xsdt)
113     {
114         /* Yes... how big is it? */
115         HeaderLength = Xsdt->Header.Length;
116         if (HeaderLength >= sizeof(*Header))
117         {
118             HeaderLength = sizeof(*Header);
119         }
120 
121         /* Based on that, how many tables are there? */
122         TableCount = (Xsdt->Header.Length - HeaderLength) / sizeof(PHYSICAL_ADDRESS);
123     }
124     else
125     {
126         /* Nope, we have an RSDT. How big is it? */
127         HeaderLength = Rsdt->Header.Length;
128         if (HeaderLength >= sizeof(*Header))
129         {
130             HeaderLength = sizeof(*Header);
131         }
132 
133         /* Based on that, how many tables are there? */
134         TableCount = (Rsdt->Header.Length - HeaderLength) / sizeof(ULONG);
135     }
136 
137     /* Loop through the ACPI tables */
138     for (i = 0; i < TableCount; i++)
139     {
140         /* For an XSDT, read the 64-bit address directly */
141         if (Xsdt)
142         {
143             PhysicalAddress = Xsdt->Tables[i];
144         }
145         else
146         {
147             /* For RSDT, cast it */
148             PhysicalAddress.QuadPart = Rsdt->Tables[i];
149         }
150 
151         /* Map the header */
152         Status = BlMmMapPhysicalAddressEx((PVOID)&Header,
153                                           0,
154                                           sizeof(*Header),
155                                           PhysicalAddress);
156         if (!NT_SUCCESS(Status))
157         {
158             return Status;
159         }
160 
161         /* Is it the right one? */
162         if (Header->Signature == Signature)
163         {
164             /* Unmap the header */
165             BlMmUnmapVirtualAddressEx(Header, sizeof(*Header));
166 
167             /* Map the whole table */
168             return BlMmMapPhysicalAddressEx(TableAddress,
169                                             0,
170                                             Header->Length,
171                                             PhysicalAddress);
172         }
173     }
174 
175     /* Requested table does not exist */
176     return STATUS_NOT_FOUND;
177 }
178 
179 
180 VOID
181 BlUtlUpdateProgress (
182     _In_ ULONG Percentage,
183     _Out_opt_ PBOOLEAN Completed
184     )
185 {
186     if (UtlProgressRoutine)
187     {
188         EfiPrintf(L"Unimplemented\r\n");
189     }
190     else if (*Completed)
191     {
192         *Completed = TRUE;
193     }
194 }
195 
196 NTSTATUS
197 BlUtlInitialize (
198     VOID
199     )
200 {
201     UtlRsdt = 0;
202     UtlXsdt = 0;
203 
204     UtlMcContext = 0;
205     UtlMcDisplayMessageRoutine = 0;
206     UtlMcUpdateMessageRoutine = 0;
207 
208     UtlProgressRoutine = 0;
209     UtlProgressContext = 0;
210     UtlProgressInfoRoutine = 0;
211     UtlProgressGranularity = 0;
212     UtlCurrentPercentComplete = 0;
213     UtlNextUpdatePercentage = 0;
214     UtlProgressNeedsInfoUpdate = 0;
215     UtlProgressInfo = 0;
216 
217     return STATUS_SUCCESS;
218 }
219 
220 VOID
221 BmUpdateProgressInfo (
222     _In_ PVOID Unknown,
223     _In_ PWCHAR ProgressInfo
224     )
225 {
226     EfiPrintf(L"Progress Info: %s\r\n", ProgressInfo);
227 }
228 
229 VOID
230 BmUpdateProgress (
231     _In_ PVOID Unknown,
232     _In_ ULONG Percent,
233     _Out_ PBOOLEAN Completed
234     )
235 {
236     EfiPrintf(L"Progress: %d\r\n", Percent);
237     if (Completed)
238     {
239         *Completed = TRUE;
240     }
241 }
242 
243 NTSTATUS
244 BlUtlRegisterProgressRoutine (
245     VOID
246     )
247 {
248     /* One shouldn't already exist */
249     if (UtlProgressRoutine)
250     {
251         return STATUS_UNSUCCESSFUL;
252     }
253 
254     /* Set the routine, and no context */
255     UtlProgressRoutine = BmUpdateProgress;
256     UtlProgressContext = NULL;
257 
258     /* Progress increases by one */
259     UtlProgressGranularity = 1;
260 
261     /* Set progress to zero for now */
262     UtlCurrentPercentComplete = 0;
263     UtlNextUpdatePercentage = 0;
264 
265     /* Set the info routine if there is one */
266     UtlProgressInfoRoutine = BmUpdateProgressInfo;
267 
268     /* All good */
269     return STATUS_SUCCESS;
270 }
271 
272 PVOID
273 BlTblFindEntry (
274     _In_ PVOID *Table,
275     _In_ ULONG Count,
276     _Out_ PULONG EntryIndex,
277     _In_ PBL_TBL_LOOKUP_ROUTINE Callback,
278     _In_ PVOID Argument1,
279     _In_ PVOID Argument2,
280     _In_ PVOID Argument3,
281     _In_ PVOID Argument4
282     )
283 {
284     PVOID Entry = NULL;
285     ULONG Index;
286     BOOLEAN Result;
287 
288     /* Check for invalid parameters */
289     if (!(Table) || !(EntryIndex))
290     {
291         return Entry;
292     }
293 
294     /* Loop each entry in the table */
295     for (Index = 0; Index < Count;  Index++)
296     {
297         /* Check if this entry is filled out */
298         if (Table[Index])
299         {
300             /* Call the comparison function */
301             Result = Callback(Table[Index],
302                               Argument1,
303                               Argument2,
304                               Argument3,
305                               Argument4);
306             if (Result)
307             {
308                 /* Entry found return it */
309                 *EntryIndex = Index;
310                 Entry = Table[Index];
311                 break;
312             }
313         }
314     }
315 
316     /* Return the entry that was (or wasn't) found */
317     return Entry;
318 }
319 
320 NTSTATUS
321 BlTblSetEntry (
322     _Inout_ PVOID** Table,
323     _Inout_ PULONG Count,
324     _In_ PVOID Entry,
325     _Out_ PULONG EntryIndex,
326     _In_ PBL_TBL_SET_ROUTINE Callback
327     )
328 {
329     ULONG NewCount;
330     NTSTATUS Status = STATUS_SUCCESS;
331     ULONG Index = 0;
332     PVOID* NewTable;
333 
334     /* Make sure all the parameters were specified */
335     if (!(Table) || !(*Table) || !(Count) || !(Callback))
336     {
337         return STATUS_INVALID_PARAMETER;
338     }
339 
340     /* Read the current table */
341     NewTable = *Table;
342     NewCount = *Count;
343 
344     /* Iterate over it */
345     while (Index < NewCount)
346     {
347         /* Look for a free index */
348         if (!NewTable[Index])
349         {
350             goto SetIndex;
351         }
352 
353         /* No free index yet, keep going */
354         ++Index;
355     }
356 
357     /* No free index was found, try to purge some entries */
358     Index = 0;
359     while (Index < NewCount)
360     {
361         /* Call each purge callback, trying to make space */
362         Status = Callback(NewTable[Index]);
363         if (NT_SUCCESS(Status))
364         {
365             /* We should have this slot available now */
366             goto SetIndex;
367         }
368 
369         /* Keep trying to purge more */
370         ++Index;
371     }
372 
373     /* Double the table */
374     NewTable = BlMmAllocateHeap(2 * sizeof(PVOID) * NewCount);
375     if (!NewTable)
376     {
377         return STATUS_NO_MEMORY;
378     }
379 
380     /* Clear the new table, and copy the old entries */
381     RtlZeroMemory(&NewTable[NewCount], sizeof(PVOID) * NewCount);
382     RtlCopyMemory(NewTable, *Table, sizeof(PVOID) * NewCount);
383 
384     /* Free the old table */
385     BlMmFreeHeap(*Table);
386 
387     /* Return the new table and count */
388     *Count = 2 * NewCount;
389     *Table = NewTable;
390 
391 SetIndex:
392     /* Set the index and return */
393     NewTable[Index] = Entry;
394     *EntryIndex = Index;
395     return Status;
396 }
397 
398 NTSTATUS
399 BlTblMap (
400     _In_ PVOID *Table,
401     _In_ ULONG Count,
402     _In_ PBL_TBL_MAP_ROUTINE MapCallback
403     )
404 {
405     NTSTATUS Status, LocalStatus;
406     PVOID Entry;
407     ULONG Index;
408 
409     /* Bail out if there's no table */
410     if (!Table)
411     {
412         return STATUS_INVALID_PARAMETER;
413     }
414 
415     /* Assume success and loop each index */
416     Status = STATUS_SUCCESS;
417     for (Index = 0; Index < Count; Index++)
418     {
419         /* See if an entry exists at this index */
420         Entry = Table[Index];
421         if (Entry)
422         {
423             /* Call the map routine for this entry */
424             LocalStatus = MapCallback(Entry, Index);
425             if (!NT_SUCCESS(LocalStatus))
426             {
427                 /* Propagate failure only */
428                 Status = LocalStatus;
429             }
430         }
431     }
432 
433     /* Return status to caller */
434     return Status;
435 }
436 
437 ULONG HtTableSize;
438 PBL_HASH_TABLE* HtTableArray;
439 ULONG HtTableEntries;
440 
441 ULONG
442 DefaultHashFunction (
443     _In_ PBL_HASH_ENTRY Entry,
444     _In_ ULONG TableSize
445     )
446 {
447     PUCHAR Value;
448     ULONG KeyHash, i;
449 
450     /* Check if the value is a pointer, or embedded inline */
451     Value = (Entry->Flags & BL_HT_VALUE_IS_INLINE) ? Entry->Value : (PUCHAR)&Entry->Value;
452 
453     /* Iterate over each byte, and sum it */
454     for (i = 0, KeyHash = 0; i < Entry->Size; i++)
455     {
456         KeyHash += Value[i++];
457     }
458 
459     /* Modulo the number of buckets */
460     return KeyHash % TableSize;
461 }
462 
463 BOOLEAN
464 HtpCompareKeys (
465     _In_ PBL_HASH_ENTRY Entry1,
466     _In_ PBL_HASH_ENTRY Entry2
467     )
468 {
469     ULONG Flags;
470     BOOLEAN ValueMatch;
471 
472     /* Check if the flags or sizes are not matching */
473     Flags = Entry1->Flags;
474     if ((Entry1->Size != Entry2->Size) || (Flags != Entry2->Flags))
475     {
476         ValueMatch = FALSE;
477     }
478     else if (Flags & BL_HT_VALUE_IS_INLINE)
479     {
480         /* Check if this is an in-line value, compare it */
481         ValueMatch = Entry1->Value == Entry2->Value;
482     }
483     else
484     {
485         /* This is a pointer value, compare it */
486         ValueMatch = (RtlCompareMemory(Entry1->Value, Entry2->Value, Entry1->Size) ==
487                       Entry1->Size);
488     }
489 
490     /* Return if it matched */
491     return ValueMatch;
492 }
493 
494 NTSTATUS
495 TblDoNotPurgeEntry (
496     _In_ PVOID Entry
497     )
498 {
499     /* Never purge this entry */
500     return STATUS_UNSUCCESSFUL;
501 }
502 
503 NTSTATUS
504 BlHtCreate (
505     _In_ ULONG Size,
506     _In_ PBL_HASH_TABLE_HASH_FUNCTION HashFunction,
507     _In_ PBL_HASH_TABLE_COMPARE_FUNCTION CompareFunction,
508     _Out_ PULONG Id
509     )
510 {
511     NTSTATUS Status;
512     PBL_HASH_TABLE HashTable;
513     ULONG i;
514 
515     /* Assume failure */
516     HashTable = NULL;
517 
518     /* Can't create a table with no ID */
519     if (!Id)
520     {
521         return STATUS_INVALID_PARAMETER;
522     }
523 
524     /* Check if we don't already have a hash table table */
525     if (!HtTableSize)
526     {
527         /* Allocate it and zero it out */
528         HtTableSize = 4;
529         HtTableArray = BlMmAllocateHeap(HtTableSize * sizeof(PVOID));
530         if (!HtTableArray)
531         {
532             Status = STATUS_NO_MEMORY;
533             goto Quickie;
534         }
535         RtlZeroMemory(HtTableArray, HtTableSize * sizeof(PVOID));
536         HtTableEntries = 0;
537     }
538 
539     /* Allocate the hash table */
540     HashTable = BlMmAllocateHeap(sizeof(*HashTable));
541     if (!HashTable)
542     {
543         Status = STATUS_NO_MEMORY;
544         goto Quickie;
545     }
546 
547     /* Fill it out */
548     HashTable->HashFunction = HashFunction ? HashFunction : DefaultHashFunction;
549     HashTable->CompareFunction = CompareFunction ? CompareFunction : HtpCompareKeys;
550     HashTable->Size = Size ? Size : 13;
551 
552     /* Allocate the hash links, one for each bucket */
553     HashTable->HashLinks = BlMmAllocateHeap(sizeof(LIST_ENTRY) * HashTable->Size);
554     if (!HashTable->HashLinks)
555     {
556         Status = STATUS_NO_MEMORY;
557         goto Quickie;
558     }
559 
560     /* Initialize the hash links */
561     for (i = 0; i < HashTable->Size; i++)
562     {
563         InitializeListHead(&HashTable->HashLinks[i]);
564     }
565 
566     /* Save us in the table of hash tables */
567     Status = BlTblSetEntry((PVOID**)&HtTableArray,
568                            &Size,
569                            HashTable,
570                            Id,
571                            TblDoNotPurgeEntry);
572     if (NT_SUCCESS(Status))
573     {
574         /* One more -- we're done */
575         ++HtTableEntries;
576         return Status;
577     }
578 
579 Quickie:
580     /* Check if we just allocated the table array now */
581     if (!(HtTableEntries) && (HtTableArray))
582     {
583         /* Free it */
584         BlMmFreeHeap(HtTableArray);
585         HtTableArray = NULL;
586         HtTableSize = 0;
587     }
588 
589     /* Check if we allocated a hash table*/
590     if (HashTable)
591     {
592         /* With links? */
593         if (HashTable->HashLinks)
594         {
595             /* Free them */
596             BlMmFreeHeap(HashTable->HashLinks);
597         }
598 
599         /* Free the table*/
600         BlMmFreeHeap(HashTable);
601     }
602 
603     /* We're done */
604     return Status;
605 }
606 
607 NTSTATUS
608 BlHtLookup (
609     _In_ ULONG TableId,
610     _In_ PBL_HASH_ENTRY Entry,
611     _Out_opt_ PBL_HASH_VALUE *Value
612     )
613 {
614     PBL_HASH_TABLE HashTable;
615     ULONG HashValue;
616     NTSTATUS Status;
617     PLIST_ENTRY HashLinkHead, HashLink;
618     PBL_HASH_NODE HashNode;
619 
620     /* Check if the table ID is invalid, or we have no entry, or it's malformed */
621     if ((HtTableSize <= TableId) ||
622         !(Entry) ||
623         ((Entry->Flags & BL_HT_VALUE_IS_INLINE) && (Entry->Size != sizeof(ULONG))))
624     {
625         /* Fail */
626         Status = STATUS_INVALID_PARAMETER;
627     }
628     else
629     {
630         /* Otherwise, get the hash table for this index */
631         HashTable = HtTableArray[TableId];
632 
633         /* Get the hash bucket */
634         HashValue = HashTable->HashFunction(Entry, HashTable->Size);
635 
636         /* Start iterating each entry in the bucket, assuming failure */
637         Status = STATUS_NOT_FOUND;
638         HashLinkHead = &HashTable->HashLinks[HashValue];
639         HashLink = HashLinkHead->Flink;
640         while (HashLink != HashLinkHead)
641         {
642             /* Get a node in this bucket, and compare the value */
643             HashNode = CONTAINING_RECORD(HashLink, BL_HASH_NODE, ListEntry);
644             if (HashTable->CompareFunction(&HashNode->Entry, Entry))
645             {
646                 /* Does the caller want the value? */
647                 if (Value)
648                 {
649                     /* Return it */
650                     *Value = &HashNode->Value;
651                 }
652 
653                 /* Return success and stop scanning */
654                 Status = STATUS_SUCCESS;
655                 break;
656             }
657 
658             /* Try the next node */
659             HashLink = HashLink->Flink;
660         }
661     }
662 
663     /* Return back to the caller */
664     return Status;
665 }
666 
667 NTSTATUS
668 BlHtStore (
669     _In_ ULONG TableId,
670     _In_ PBL_HASH_ENTRY Entry,
671     _In_ PVOID Data,
672     _In_ ULONG DataSize
673     )
674 {
675     PBL_HASH_NODE HashNode;
676     NTSTATUS Status;
677     PLIST_ENTRY HashLinkHead;
678     PBL_HASH_TABLE HashTable;
679 
680     /* Check for invalid table ID, missing arguments, or malformed entry */
681     if ((HtTableSize <= TableId) ||
682         !(Entry) ||
683         !(Data) ||
684         !(Entry->Size) ||
685         !(Entry->Value) ||
686         !(DataSize) ||
687         ((Entry->Flags & BL_HT_VALUE_IS_INLINE) && (Entry->Size != sizeof(ULONG))))
688     {
689         /* Fail the call */
690         Status = STATUS_INVALID_PARAMETER;
691         goto Quickie;
692     }
693 
694     /* Get the hash table for this ID */
695     HashTable = HtTableArray[TableId];
696 
697     /* Allocate a hash node */
698     HashNode = BlMmAllocateHeap(sizeof(*HashNode));
699     if (!HashNode)
700     {
701         Status = STATUS_NO_MEMORY;
702         goto Quickie;
703     }
704 
705     /* Capture all the data*/
706     HashNode->Entry.Size = Entry->Size;
707     HashNode->Entry.Flags = Entry->Flags;
708     HashNode->Entry.Value = Entry->Value;
709     HashNode->Value.DataSize = DataSize;
710     HashNode->Value.Data = Data;
711 
712     /* Insert it into the bucket list and return success */
713     HashLinkHead = &HashTable->HashLinks[HashTable->HashFunction(Entry, HashTable->Size)];
714     InsertTailList(HashLinkHead, &HashNode->ListEntry);
715     Status = STATUS_SUCCESS;
716 
717 Quickie:
718     return Status;
719 }
720 
721 NTSTATUS
722 BlHtDelete (
723     _In_ ULONG TableId,
724     _In_ PBL_HASH_ENTRY Entry
725     )
726 {
727     PBL_HASH_TABLE HashTable;
728     ULONG HashValue;
729     NTSTATUS Status;
730     PLIST_ENTRY HashLinkHead, HashLink;
731     PBL_HASH_NODE HashNode;
732 
733     /* Check if the table ID is invalid, or we have no entry, or it's malformed */
734     if ((HtTableSize <= TableId) ||
735         !(Entry) ||
736         !(Entry->Size) ||
737         !(Entry->Value) ||
738         ((Entry->Flags & BL_HT_VALUE_IS_INLINE) && (Entry->Size != sizeof(ULONG))))
739     {
740         /* Fail */
741         Status = STATUS_INVALID_PARAMETER;
742     }
743     else
744     {
745         /* Otherwise, get the hash table for this index */
746         HashTable = HtTableArray[TableId];
747 
748         /* Get the hash bucket */
749         HashValue = HashTable->HashFunction(Entry, HashTable->Size);
750 
751         /* Start iterating each entry in the bucket, assuming failure */
752         Status = STATUS_NOT_FOUND;
753         HashLinkHead = &HashTable->HashLinks[HashValue];
754         HashLink = HashLinkHead->Flink;
755         while (HashLink != HashLinkHead)
756         {
757             /* Get a node in this bucket, and compare the value */
758             HashNode = CONTAINING_RECORD(HashLink, BL_HASH_NODE, ListEntry);
759             if (HashTable->CompareFunction(&HashNode->Entry, Entry))
760             {
761                 /* Remove it from the list and free it */
762                 RemoveEntryList(&HashNode->ListEntry);
763                 BlMmFreeHeap(HashNode);
764                 return STATUS_SUCCESS;
765             }
766 
767             /* Try the next node */
768             HashLink = HashLink->Flink;
769         }
770     }
771 
772     /* Return back to the caller */
773     return Status;
774 }
775 
776 ULONG
777 BlUtlCheckSum (
778     _In_ ULONG PartialSum,
779     _In_ PUCHAR Buffer,
780     _In_ ULONG Length,
781     _In_ ULONG Flags
782     )
783 {
784     ULONG i;
785 
786     if (Flags & BL_UTL_CHECKSUM_UCHAR_BUFFER)
787     {
788         EfiPrintf(L"Not supported\r\n");
789         return 0;
790     }
791     else if (Flags & BL_UTL_CHECKSUM_USHORT_BUFFER)
792     {
793         PartialSum = (unsigned __int16)PartialSum;
794         Length &= ~1;
795 
796         for (i = 0; i < Length; i += 2)
797         {
798             PartialSum += *(unsigned __int16 *)&Buffer[i];
799             if (Flags & BL_UTL_CHECKSUM_COMPLEMENT)
800             {
801                 PartialSum = (unsigned __int16)((PartialSum >> 16) + PartialSum);
802             }
803         }
804 
805         if (i != Length)
806         {
807             PartialSum += (unsigned __int8)Buffer[Length];
808             if (Flags & BL_UTL_CHECKSUM_COMPLEMENT)
809             {
810                 PartialSum = (unsigned __int16)((PartialSum >> 16) + PartialSum);
811             }
812         }
813 
814         if (Flags & BL_UTL_CHECKSUM_NEGATE)
815         {
816             return ~PartialSum;
817         }
818 
819         PartialSum = (unsigned __int16)PartialSum;
820     }
821     else
822     {
823         /* Invalid mode */
824         return 0;
825     }
826 
827     if (Flags & BL_UTL_CHECKSUM_NEGATE)
828     {
829         return ~PartialSum;
830     }
831 
832     return PartialSum;
833 }
834 
835 #if defined(_M_IX86) || defined(_M_X64)
836 BOOLEAN
837 Archx86IsCpuidSupported (
838     VOID
839     )
840 {
841     ULONG CallerFlags, Flags;
842 
843     /* Read the original flags, and add the CPUID bit */
844     CallerFlags = __readeflags() ^ 0x200000;
845     __writeeflags(CallerFlags);
846 
847     /* Read our flags now */
848     Flags = __readeflags();
849 
850     /* Check if the bit stuck */
851     return (((CallerFlags ^ Flags) >> 21) & 1) ^ 1;
852 }
853 #endif
854 
855 BOOLEAN
856 BlArchIsCpuIdFunctionSupported (
857     _In_ ULONG Function
858     )
859 {
860 #if defined(_M_IX86) || defined(_M_X64)
861     BOOLEAN Supported;
862     INT CpuInfo[4];
863 
864     /* Check if the CPU supports this instruction */
865     Supported = Archx86IsCpuidSupported();
866     if (!Supported)
867     {
868         return FALSE;
869     }
870 
871     /* Check if it's the extended function */
872     if (Function >= 0x80000000)
873     {
874         /* Check if extended functions are supported */
875         __cpuid(CpuInfo, 0x80000000);
876         if ((CpuInfo[0] & 0xFFFFFF00) != 0x80000000)
877         {
878             /* Nope */
879             return FALSE;
880         }
881     }
882     else
883     {
884         /* It's a regular function, get the maximum one supported */
885         __cpuid(CpuInfo, 0);
886     }
887 
888     /* Check if our function is within bounds */
889     if (Function <= CpuInfo[0])
890     {
891         return TRUE;
892     }
893 #else
894     EfiPrintf(L"BlArchIsCpuIdFunctionSupported not implemented for this platform.\r\n");
895 #endif
896 
897     /* Nope */
898     return FALSE;
899 }
900 
901 ULONGLONG
902 BlArchGetPerformanceCounter (
903     VOID
904     )
905 {
906 #if defined(_M_IX86) || defined(_M_X64)
907     CPU_INFO CpuInfo;
908 
909     /* Serialize with CPUID, if it exists */
910     if (Archx86IsCpuidSupported())
911     {
912         BlArchCpuId(0, 0, &CpuInfo);
913     }
914 
915     /* Read the TSC */
916     return __rdtsc();
917 #else
918     EfiPrintf(L"BlArchGetPerformanceCounter not implemented for this platform.\r\n");
919     return 0;
920 #endif
921 }
922 
923 VOID
924 BlArchCpuId (
925     _In_ ULONG Function,
926     _In_ ULONG SubFunction,
927     _Out_ PCPU_INFO Result
928     )
929 {
930 #if defined(_M_IX86) || defined(_M_X64)
931     /* Use the intrinsic */
932     __cpuidex((INT*)Result->AsUINT32, Function, SubFunction);
933 #endif
934 }
935 
936 CPU_VENDORS
937 BlArchGetCpuVendor (
938     VOID
939     )
940 {
941     CPU_INFO CpuInfo;
942     INT Temp;
943 
944     /* Get the CPU Vendor */
945     BlArchCpuId(0, 0, &CpuInfo);
946     Temp = CpuInfo.Ecx;
947     CpuInfo.Ecx = CpuInfo.Edx;
948     CpuInfo.Edx = Temp;
949 
950     /* Check against supported values */
951     if (!strncmp((PCHAR)&CpuInfo.Ebx, "GenuineIntel", 12))
952     {
953         return CPU_INTEL;
954     }
955     if (!strncmp((PCHAR)&CpuInfo.Ebx, "AuthenticAMD", 12))
956     {
957         return CPU_AMD;
958     }
959     if (!strncmp((PCHAR)&CpuInfo.Ebx, "CentaurHauls", 12))
960     {
961         return CPU_VIA;
962     }
963 #ifdef _M_IX86
964     if (!strncmp((PCHAR)&CpuInfo.Ebx, "CyrixInstead", 12))
965     {
966         return CPU_CYRIX;
967     }
968     if (!strncmp((PCHAR)&CpuInfo.Ebx, "GenuineTMx86", 12))
969     {
970         return CPU_TRANSMETA;
971     }
972     if (!strncmp((PCHAR)&CpuInfo.Ebx, "RiseRiseRise", 12))
973     {
974         return CPU_RISE;
975     }
976 #endif // _M_IX86
977     /* Other */
978     return CPU_UNKNOWN;
979 }
980