18f21cc2e97910edbfb38e8341e449c70f53d03d
[reactos.git] / boot / environ / lib / misc / util.c
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 if (!strncmp((PCHAR)&CpuInfo.Ebx, "CyrixInstead", 12))
964 {
965 return CPU_CYRIX;
966 }
967 if (!strncmp((PCHAR)&CpuInfo.Ebx, "GenuineTMx86", 12))
968 {
969 return CPU_TRANSMETA;
970 }
971 if (!strncmp((PCHAR)&CpuInfo.Ebx, "RiseRiseRise", 12))
972 {
973 return CPU_RISE;
974 }
975
976 /* Other */
977 return CPU_UNKNOWN;
978 }