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)
9 /* INCLUDES ******************************************************************/
13 /* DATA VARIABLES ************************************************************/
19 PVOID UtlMcDisplayMessageRoutine
;
20 PVOID UtlMcUpdateMessageRoutine
;
22 PVOID UtlProgressRoutine
;
23 PVOID UtlProgressContext
;
24 PVOID UtlProgressInfoRoutine
;
25 ULONG UtlProgressGranularity
;
26 ULONG UtlCurrentPercentComplete
;
27 ULONG UtlNextUpdatePercentage
;
28 BOOLEAN UtlProgressNeedsInfoUpdate
;
29 PVOID UtlProgressInfo
;
31 /* FUNCTIONS *****************************************************************/
35 _Out_ PVOID
* TableAddress
,
39 ULONG i
, TableCount
, HeaderLength
;
43 PHYSICAL_ADDRESS PhysicalAddress
;
44 PDESCRIPTION_HEADER Header
;
48 /* Make sure there's an output parameter */
51 return STATUS_INVALID_PARAMETER
;
54 /* Get the currently known RSDT and XSDT */
55 Rsdt
= (PRSDT
)UtlRsdt
;
56 Xsdt
= (PXSDT
)UtlXsdt
;
58 /* Is there an RSDT? */
61 /* No -- is there an XSDT? */
64 /* No. Look up the RSDT */
65 Status
= EfipGetRsdt(&PhysicalAddress
);
66 if (!NT_SUCCESS(Status
))
68 EfiPrintf(L
"no rsdp found\r\n");
73 Status
= BlMmMapPhysicalAddressEx((PVOID
)&Header
,
77 if (!NT_SUCCESS(Status
))
82 /* Unmap the header */
83 BlMmUnmapVirtualAddressEx(Header
, sizeof(*Header
));
85 /* Map the whole table */
86 Status
= BlMmMapPhysicalAddressEx((PVOID
)&Header
,
90 if (!NT_SUCCESS(Status
))
95 /* Check if its an XSDT or an RSDT */
96 if (Header
->Signature
== XSDT_SIGNATURE
)
105 Rsdt
= (PRSDT
)Header
;
111 /* OK, so do we have an XSDT after all? */
114 /* Yes... how big is it? */
115 HeaderLength
= Xsdt
->Header
.Length
;
116 if (HeaderLength
>= sizeof(*Header
))
118 HeaderLength
= sizeof(*Header
);
121 /* Based on that, how many tables are there? */
122 TableCount
= (Xsdt
->Header
.Length
- HeaderLength
) / sizeof(PHYSICAL_ADDRESS
);
126 /* Nope, we have an RSDT. How big is it? */
127 HeaderLength
= Rsdt
->Header
.Length
;
128 if (HeaderLength
>= sizeof(*Header
))
130 HeaderLength
= sizeof(*Header
);
133 /* Based on that, how many tables are there? */
134 TableCount
= (Rsdt
->Header
.Length
- HeaderLength
) / sizeof(ULONG
);
137 /* Loop through the ACPI tables */
138 for (i
= 0; i
< TableCount
; i
++)
140 /* For an XSDT, read the 64-bit address directly */
143 PhysicalAddress
= Xsdt
->Tables
[i
];
147 /* For RSDT, cast it */
148 PhysicalAddress
.QuadPart
= Rsdt
->Tables
[i
];
152 Status
= BlMmMapPhysicalAddressEx((PVOID
)&Header
,
156 if (!NT_SUCCESS(Status
))
161 /* Is it the right one? */
162 if (Header
->Signature
== Signature
)
164 /* Unmap the header */
165 BlMmUnmapVirtualAddressEx(Header
, sizeof(*Header
));
167 /* Map the whole table */
168 return BlMmMapPhysicalAddressEx(TableAddress
,
175 /* Requested table does not exist */
176 return STATUS_NOT_FOUND
;
181 BlUtlUpdateProgress (
182 _In_ ULONG Percentage
,
183 _Out_opt_ PBOOLEAN Completed
186 if (UtlProgressRoutine
)
188 EfiPrintf(L
"Unimplemented\r\n");
205 UtlMcDisplayMessageRoutine
= 0;
206 UtlMcUpdateMessageRoutine
= 0;
208 UtlProgressRoutine
= 0;
209 UtlProgressContext
= 0;
210 UtlProgressInfoRoutine
= 0;
211 UtlProgressGranularity
= 0;
212 UtlCurrentPercentComplete
= 0;
213 UtlNextUpdatePercentage
= 0;
214 UtlProgressNeedsInfoUpdate
= 0;
217 return STATUS_SUCCESS
;
221 BmUpdateProgressInfo (
223 _In_ PWCHAR ProgressInfo
226 EfiPrintf(L
"Progress Info: %s\r\n", ProgressInfo
);
233 _Out_ PBOOLEAN Completed
236 EfiPrintf(L
"Progress: %d\r\n", Percent
);
244 BlUtlRegisterProgressRoutine (
248 /* One shouldn't already exist */
249 if (UtlProgressRoutine
)
251 return STATUS_UNSUCCESSFUL
;
254 /* Set the routine, and no context */
255 UtlProgressRoutine
= BmUpdateProgress
;
256 UtlProgressContext
= NULL
;
258 /* Progress increases by one */
259 UtlProgressGranularity
= 1;
261 /* Set progress to zero for now */
262 UtlCurrentPercentComplete
= 0;
263 UtlNextUpdatePercentage
= 0;
265 /* Set the info routine if there is one */
266 UtlProgressInfoRoutine
= BmUpdateProgressInfo
;
269 return STATUS_SUCCESS
;
276 _Out_ PULONG EntryIndex
,
277 _In_ PBL_TBL_LOOKUP_ROUTINE Callback
,
278 _In_ PVOID Argument1
,
279 _In_ PVOID Argument2
,
280 _In_ PVOID Argument3
,
288 /* Check for invalid parameters */
289 if (!(Table
) || !(EntryIndex
))
294 /* Loop each entry in the table */
295 for (Index
= 0; Index
< Count
; Index
++)
297 /* Check if this entry is filled out */
300 /* Call the comparison function */
301 Result
= Callback(Table
[Index
],
308 /* Entry found return it */
310 Entry
= Table
[Index
];
316 /* Return the entry that was (or wasn't) found */
322 _Inout_ PVOID
** Table
,
323 _Inout_ PULONG Count
,
325 _Out_ PULONG EntryIndex
,
326 _In_ PBL_TBL_SET_ROUTINE Callback
330 NTSTATUS Status
= STATUS_SUCCESS
;
334 /* Make sure all the parameters were specified */
335 if (!(Table
) || !(*Table
) || !(Count
) || !(Callback
))
337 return STATUS_INVALID_PARAMETER
;
340 /* Read the current table */
344 /* Iterate over it */
345 while (Index
< NewCount
)
347 /* Look for a free index */
348 if (!NewTable
[Index
])
353 /* No free index yet, keep going */
357 /* No free index was found, try to purge some entries */
359 while (Index
< NewCount
)
361 /* Call each purge callback, trying to make space */
362 Status
= Callback(NewTable
[Index
]);
363 if (NT_SUCCESS(Status
))
365 /* We should have this slot available now */
369 /* Keep trying to purge more */
373 /* Double the table */
374 NewTable
= BlMmAllocateHeap(2 * sizeof(PVOID
) * NewCount
);
377 return STATUS_NO_MEMORY
;
380 /* Clear the new table, and copy the old entries */
381 RtlZeroMemory(&NewTable
[NewCount
], sizeof(PVOID
) * NewCount
);
382 RtlCopyMemory(NewTable
, *Table
, sizeof(PVOID
) * NewCount
);
384 /* Free the old table */
385 BlMmFreeHeap(*Table
);
387 /* Return the new table and count */
388 *Count
= 2 * NewCount
;
392 /* Set the index and return */
393 NewTable
[Index
] = Entry
;
402 _In_ PBL_TBL_MAP_ROUTINE MapCallback
405 NTSTATUS Status
, LocalStatus
;
409 /* Bail out if there's no table */
412 return STATUS_INVALID_PARAMETER
;
415 /* Assume success and loop each index */
416 Status
= STATUS_SUCCESS
;
417 for (Index
= 0; Index
< Count
; Index
++)
419 /* See if an entry exists at this index */
420 Entry
= Table
[Index
];
423 /* Call the map routine for this entry */
424 LocalStatus
= MapCallback(Entry
, Index
);
425 if (!NT_SUCCESS(LocalStatus
))
427 /* Propagate failure only */
428 Status
= LocalStatus
;
433 /* Return status to caller */
438 PBL_HASH_TABLE
* HtTableArray
;
439 ULONG HtTableEntries
;
442 DefaultHashFunction (
443 _In_ PBL_HASH_ENTRY Entry
,
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
;
453 /* Iterate over each byte, and sum it */
454 for (i
= 0, KeyHash
= 0; i
< Entry
->Size
; i
++)
456 KeyHash
+= Value
[i
++];
459 /* Modulo the number of buckets */
460 return KeyHash
% TableSize
;
465 _In_ PBL_HASH_ENTRY Entry1
,
466 _In_ PBL_HASH_ENTRY Entry2
472 /* Check if the flags or sizes are not matching */
473 Flags
= Entry1
->Flags
;
474 if ((Entry1
->Size
!= Entry2
->Size
) || (Flags
!= Entry2
->Flags
))
478 else if (Flags
& BL_HT_VALUE_IS_INLINE
)
480 /* Check if this is an in-line value, compare it */
481 ValueMatch
= Entry1
->Value
== Entry2
->Value
;
485 /* This is a pointer value, compare it */
486 ValueMatch
= (RtlCompareMemory(Entry1
->Value
, Entry2
->Value
, Entry1
->Size
) ==
490 /* Return if it matched */
499 /* Never purge this entry */
500 return STATUS_UNSUCCESSFUL
;
506 _In_ PBL_HASH_TABLE_HASH_FUNCTION HashFunction
,
507 _In_ PBL_HASH_TABLE_COMPARE_FUNCTION CompareFunction
,
512 PBL_HASH_TABLE HashTable
;
518 /* Can't create a table with no ID */
521 return STATUS_INVALID_PARAMETER
;
524 /* Check if we don't already have a hash table table */
527 /* Allocate it and zero it out */
529 HtTableArray
= BlMmAllocateHeap(HtTableSize
* sizeof(PVOID
));
532 Status
= STATUS_NO_MEMORY
;
535 RtlZeroMemory(HtTableArray
, HtTableSize
* sizeof(PVOID
));
539 /* Allocate the hash table */
540 HashTable
= BlMmAllocateHeap(sizeof(*HashTable
));
543 Status
= STATUS_NO_MEMORY
;
548 HashTable
->HashFunction
= HashFunction
? HashFunction
: DefaultHashFunction
;
549 HashTable
->CompareFunction
= CompareFunction
? CompareFunction
: HtpCompareKeys
;
550 HashTable
->Size
= Size
? Size
: 13;
552 /* Allocate the hash links, one for each bucket */
553 HashTable
->HashLinks
= BlMmAllocateHeap(sizeof(LIST_ENTRY
) * HashTable
->Size
);
554 if (!HashTable
->HashLinks
)
556 Status
= STATUS_NO_MEMORY
;
560 /* Initialize the hash links */
561 for (i
= 0; i
< HashTable
->Size
; i
++)
563 InitializeListHead(&HashTable
->HashLinks
[i
]);
566 /* Save us in the table of hash tables */
567 Status
= BlTblSetEntry((PVOID
**)&HtTableArray
,
572 if (NT_SUCCESS(Status
))
574 /* One more -- we're done */
580 /* Check if we just allocated the table array now */
581 if (!(HtTableEntries
) && (HtTableArray
))
584 BlMmFreeHeap(HtTableArray
);
589 /* Check if we allocated a hash table*/
593 if (HashTable
->HashLinks
)
596 BlMmFreeHeap(HashTable
->HashLinks
);
600 BlMmFreeHeap(HashTable
);
610 _In_ PBL_HASH_ENTRY Entry
,
611 _Out_opt_ PBL_HASH_VALUE
*Value
614 PBL_HASH_TABLE HashTable
;
617 PLIST_ENTRY HashLinkHead
, HashLink
;
618 PBL_HASH_NODE HashNode
;
620 /* Check if the table ID is invalid, or we have no entry, or it's malformed */
621 if ((HtTableSize
<= TableId
) ||
623 ((Entry
->Flags
& BL_HT_VALUE_IS_INLINE
) && (Entry
->Size
!= sizeof(ULONG
))))
626 Status
= STATUS_INVALID_PARAMETER
;
630 /* Otherwise, get the hash table for this index */
631 HashTable
= HtTableArray
[TableId
];
633 /* Get the hash bucket */
634 HashValue
= HashTable
->HashFunction(Entry
, HashTable
->Size
);
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
)
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
))
646 /* Does the caller want the value? */
650 *Value
= &HashNode
->Value
;
653 /* Return success and stop scanning */
654 Status
= STATUS_SUCCESS
;
658 /* Try the next node */
659 HashLink
= HashLink
->Flink
;
663 /* Return back to the caller */
670 _In_ PBL_HASH_ENTRY Entry
,
675 PBL_HASH_NODE HashNode
;
677 PLIST_ENTRY HashLinkHead
;
678 PBL_HASH_TABLE HashTable
;
680 /* Check for invalid table ID, missing arguments, or malformed entry */
681 if ((HtTableSize
<= TableId
) ||
687 ((Entry
->Flags
& BL_HT_VALUE_IS_INLINE
) && (Entry
->Size
!= sizeof(ULONG
))))
690 Status
= STATUS_INVALID_PARAMETER
;
694 /* Get the hash table for this ID */
695 HashTable
= HtTableArray
[TableId
];
697 /* Allocate a hash node */
698 HashNode
= BlMmAllocateHeap(sizeof(*HashNode
));
701 Status
= STATUS_NO_MEMORY
;
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
;
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
;
724 _In_ PBL_HASH_ENTRY Entry
727 PBL_HASH_TABLE HashTable
;
730 PLIST_ENTRY HashLinkHead
, HashLink
;
731 PBL_HASH_NODE HashNode
;
733 /* Check if the table ID is invalid, or we have no entry, or it's malformed */
734 if ((HtTableSize
<= TableId
) ||
738 ((Entry
->Flags
& BL_HT_VALUE_IS_INLINE
) && (Entry
->Size
!= sizeof(ULONG
))))
741 Status
= STATUS_INVALID_PARAMETER
;
745 /* Otherwise, get the hash table for this index */
746 HashTable
= HtTableArray
[TableId
];
748 /* Get the hash bucket */
749 HashValue
= HashTable
->HashFunction(Entry
, HashTable
->Size
);
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
)
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
))
761 /* Remove it from the list and free it */
762 RemoveEntryList(&HashNode
->ListEntry
);
763 BlMmFreeHeap(HashNode
);
764 return STATUS_SUCCESS
;
767 /* Try the next node */
768 HashLink
= HashLink
->Flink
;
772 /* Return back to the caller */
778 _In_ ULONG PartialSum
,
786 if (Flags
& BL_UTL_CHECKSUM_UCHAR_BUFFER
)
788 EfiPrintf(L
"Not supported\r\n");
791 else if (Flags
& BL_UTL_CHECKSUM_USHORT_BUFFER
)
793 PartialSum
= (unsigned __int16
)PartialSum
;
796 for (i
= 0; i
< Length
; i
+= 2)
798 PartialSum
+= *(unsigned __int16
*)&Buffer
[i
];
799 if (Flags
& BL_UTL_CHECKSUM_COMPLEMENT
)
801 PartialSum
= (unsigned __int16
)((PartialSum
>> 16) + PartialSum
);
807 PartialSum
+= (unsigned __int8
)Buffer
[Length
];
808 if (Flags
& BL_UTL_CHECKSUM_COMPLEMENT
)
810 PartialSum
= (unsigned __int16
)((PartialSum
>> 16) + PartialSum
);
814 if (Flags
& BL_UTL_CHECKSUM_NEGATE
)
819 PartialSum
= (unsigned __int16
)PartialSum
;
827 if (Flags
& BL_UTL_CHECKSUM_NEGATE
)
835 #if defined(_M_IX86) || defined(_M_X64)
837 Archx86IsCpuidSupported (
841 ULONG CallerFlags
, Flags
;
843 /* Read the original flags, and add the CPUID bit */
844 CallerFlags
= __readeflags() ^ 0x200000;
845 __writeeflags(CallerFlags
);
847 /* Read our flags now */
848 Flags
= __readeflags();
850 /* Check if the bit stuck */
851 return (((CallerFlags
^ Flags
) >> 21) & 1) ^ 1;
856 BlArchIsCpuIdFunctionSupported (
860 #if defined(_M_IX86) || defined(_M_X64)
864 /* Check if the CPU supports this instruction */
865 Supported
= Archx86IsCpuidSupported();
871 /* Check if it's the extended function */
872 if (Function
>= 0x80000000)
874 /* Check if extended functions are supported */
875 __cpuid(CpuInfo
, 0x80000000);
876 if ((CpuInfo
[0] & 0xFFFFFF00) != 0x80000000)
884 /* It's a regular function, get the maximum one supported */
888 /* Check if our function is within bounds */
889 if (Function
<= CpuInfo
[0])
894 EfiPrintf(L
"BlArchIsCpuIdFunctionSupported not implemented for this platform.\r\n");
902 BlArchGetPerformanceCounter (
906 #if defined(_M_IX86) || defined(_M_X64)
909 /* Serialize with CPUID, if it exists */
910 if (Archx86IsCpuidSupported())
912 BlArchCpuId(0, 0, &CpuInfo
);
918 EfiPrintf(L
"BlArchGetPerformanceCounter not implemented for this platform.\r\n");
926 _In_ ULONG SubFunction
,
927 _Out_ PCPU_INFO Result
930 #if defined(_M_IX86) || defined(_M_X64)
931 /* Use the intrinsic */
932 __cpuidex((INT
*)Result
->AsUINT32
, Function
, SubFunction
);
944 /* Get the CPU Vendor */
945 BlArchCpuId(0, 0, &CpuInfo
);
947 CpuInfo
.Ecx
= CpuInfo
.Edx
;
950 /* Check against supported values */
951 if (!strncmp((PCHAR
)&CpuInfo
.Ebx
, "GenuineIntel", 12))
955 if (!strncmp((PCHAR
)&CpuInfo
.Ebx
, "AuthenticAMD", 12))
959 if (!strncmp((PCHAR
)&CpuInfo
.Ebx
, "CentaurHauls", 12))
963 if (!strncmp((PCHAR
)&CpuInfo
.Ebx
, "CyrixInstead", 12))
967 if (!strncmp((PCHAR
)&CpuInfo
.Ebx
, "GenuineTMx86", 12))
969 return CPU_TRANSMETA
;
971 if (!strncmp((PCHAR
)&CpuInfo
.Ebx
, "RiseRiseRise", 12))