Create the AHCI branch for Aman's work
[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
32
33 /* FUNCTIONS *****************************************************************/
34
35 NTSTATUS
36 BlUtlGetAcpiTable (
37 _Out_ PVOID* TableAddress,
38 _In_ ULONG Signature
39 )
40 {
41 ULONG i, TableCount, HeaderLength;
42 NTSTATUS Status;
43 PRSDT Rsdt;
44 PXSDT Xsdt;
45 PHYSICAL_ADDRESS PhysicalAddress;
46 PDESCRIPTION_HEADER Header;
47
48 Header = 0;
49
50 /* Make sure there's an output parameter */
51 if (!TableAddress)
52 {
53 return STATUS_INVALID_PARAMETER;
54 }
55
56 /* Get the currently known RSDT and XSDT */
57 Rsdt = (PRSDT)UtlRsdt;
58 Xsdt = (PXSDT)UtlXsdt;
59
60 /* Is there an RSDT? */
61 if (!Rsdt)
62 {
63 /* No -- is there an XSDT? */
64 if (!Xsdt)
65 {
66 /* No. Look up the RSDT */
67 Status = EfipGetRsdt(&PhysicalAddress);
68 if (!NT_SUCCESS(Status))
69 {
70 EfiPrintf(L"no rsdp found\r\n");
71 return Status;
72 }
73
74 /* Map the header */
75 Status = BlMmMapPhysicalAddressEx((PVOID)&Header,
76 0,
77 sizeof(*Header),
78 PhysicalAddress);
79 if (!NT_SUCCESS(Status))
80 {
81 return Status;
82 }
83
84 /* Unmap the header */
85 BlMmUnmapVirtualAddressEx(Header, sizeof(*Header));
86
87 /* Map the whole table */
88 Status = BlMmMapPhysicalAddressEx((PVOID)&Header,
89 0,
90 Header->Length,
91 PhysicalAddress);
92 if (!NT_SUCCESS(Status))
93 {
94 return Status;
95 }
96
97 /* Check if its an XSDT or an RSDT */
98 if (Header->Signature == XSDT_SIGNATURE)
99 {
100 /* It's an XSDT */
101 Xsdt = (PXSDT)Header;
102 UtlXsdt = Xsdt;
103 }
104 else
105 {
106 /* It's an RSDT */
107 Rsdt = (PRSDT)Header;
108 UtlRsdt = Rsdt;
109 }
110 }
111 }
112
113 /* OK, so do we have an XSDT after all? */
114 if (Xsdt)
115 {
116 /* Yes... how big is it? */
117 HeaderLength = Xsdt->Header.Length;
118 if (HeaderLength >= sizeof(*Header))
119 {
120 HeaderLength = sizeof(*Header);
121 }
122
123 /* Based on that, how many tables are there? */
124 TableCount = (Xsdt->Header.Length - HeaderLength) / sizeof(PHYSICAL_ADDRESS);
125 }
126 else
127 {
128 /* Nope, we have an RSDT. How big is it? */
129 HeaderLength = Rsdt->Header.Length;
130 if (HeaderLength >= sizeof(*Header))
131 {
132 HeaderLength = sizeof(*Header);
133 }
134
135 /* Based on that, how many tables are there? */
136 TableCount = (Rsdt->Header.Length - HeaderLength) / sizeof(ULONG);
137 }
138
139 /* Loop through the ACPI tables */
140 for (i = 0; i < TableCount; i++)
141 {
142 /* For an XSDT, read the 64-bit address directly */
143 if (Xsdt)
144 {
145 PhysicalAddress = Xsdt->Tables[i];
146 }
147 else
148 {
149 /* For RSDT, cast it */
150 PhysicalAddress.QuadPart = Rsdt->Tables[i];
151 }
152
153 /* Map the header */
154 Status = BlMmMapPhysicalAddressEx((PVOID)&Header,
155 0,
156 sizeof(*Header),
157 PhysicalAddress);
158 if (!NT_SUCCESS(Status))
159 {
160 return Status;
161 }
162
163 /* Is it the right one? */
164 if (Header->Signature == Signature)
165 {
166 /* Unmap the header */
167 BlMmUnmapVirtualAddressEx(Header, sizeof(*Header));
168
169 /* Map the whole table */
170 return BlMmMapPhysicalAddressEx(TableAddress,
171 0,
172 Header->Length,
173 PhysicalAddress);
174 }
175 }
176
177 /* Requested table does not exist */
178 return STATUS_NOT_FOUND;
179 }
180
181
182 VOID
183 BlUtlUpdateProgress (
184 _In_ ULONG Percentage,
185 _Out_opt_ PBOOLEAN Completed
186 )
187 {
188 if (UtlProgressRoutine)
189 {
190 EfiPrintf(L"Unimplemented\r\n");
191 }
192 else if (*Completed)
193 {
194 *Completed = TRUE;
195 }
196 }
197
198 NTSTATUS
199 BlUtlInitialize (
200 VOID
201 )
202 {
203 UtlRsdt = 0;
204 UtlXsdt = 0;
205
206 UtlMcContext = 0;
207 UtlMcDisplayMessageRoutine = 0;
208 UtlMcUpdateMessageRoutine = 0;
209
210 UtlProgressRoutine = 0;
211 UtlProgressContext = 0;
212 UtlProgressInfoRoutine = 0;
213 UtlProgressGranularity = 0;
214 UtlCurrentPercentComplete = 0;
215 UtlNextUpdatePercentage = 0;
216 UtlProgressNeedsInfoUpdate = 0;
217 UtlProgressInfo = 0;
218
219 return STATUS_SUCCESS;
220 }
221
222 VOID
223 BmUpdateProgressInfo (
224 _In_ PVOID Uknown,
225 _In_ PWCHAR ProgressInfo
226 )
227 {
228 EfiPrintf(L"Progress Info: %s\r\n", ProgressInfo);
229 }
230
231 VOID
232 BmUpdateProgress (
233 _In_ PVOID Unknown,
234 _In_ ULONG Percent,
235 _Out_ PBOOLEAN Completed
236 )
237 {
238 EfiPrintf(L"Progress: %d\r\n", Percent);
239 if (Completed)
240 {
241 *Completed = TRUE;
242 }
243 }
244
245 NTSTATUS
246 BlUtlRegisterProgressRoutine (
247 VOID
248 )
249 {
250 /* One shouldn't already exist */
251 if (UtlProgressRoutine)
252 {
253 return STATUS_UNSUCCESSFUL;
254 }
255
256 /* Set the routine, and no context */
257 UtlProgressRoutine = BmUpdateProgress;
258 UtlProgressContext = NULL;
259
260 /* Progress increases by one */
261 UtlProgressGranularity = 1;
262
263 /* Set progress to zero for now */
264 UtlCurrentPercentComplete = 0;
265 UtlNextUpdatePercentage = 0;
266
267 /* Set the info routine if there is one */
268 UtlProgressInfoRoutine = BmUpdateProgressInfo;
269
270 /* All good */
271 return STATUS_SUCCESS;
272 }
273
274 PVOID
275 BlTblFindEntry (
276 _In_ PVOID *Table,
277 _In_ ULONG Count,
278 _Out_ PULONG EntryIndex,
279 _In_ PBL_TBL_LOOKUP_ROUTINE Callback,
280 _In_ PVOID Argument1,
281 _In_ PVOID Argument2,
282 _In_ PVOID Argument3,
283 _In_ PVOID Argument4
284 )
285 {
286 PVOID Entry = NULL;
287 ULONG Index;
288 BOOLEAN Result;
289
290 /* Check for invalid parameters */
291 if (!(Table) || !(EntryIndex))
292 {
293 return Entry;
294 }
295
296 /* Loop each entry in the table */
297 for (Index = 0; Index < Count; Index++)
298 {
299 /* Check if this entry is filled out */
300 if (Table[Index])
301 {
302 /* Call the comparison function */
303 Result = Callback(Table[Index],
304 Argument1,
305 Argument2,
306 Argument3,
307 Argument4);
308 if (Result)
309 {
310 /* Entry fouund return it */
311 *EntryIndex = Index;
312 Entry = Table[Index];
313 break;
314 }
315 }
316 }
317
318 /* Return the entry that was (or wasn't) found */
319 return Entry;
320 }
321
322 NTSTATUS
323 BlTblSetEntry (
324 _Inout_ PVOID** Table,
325 _Inout_ PULONG Count,
326 _In_ PVOID Entry,
327 _Out_ PULONG EntryIndex,
328 _In_ PBL_TBL_SET_ROUTINE Callback
329 )
330 {
331 ULONG NewCount;
332 NTSTATUS Status = STATUS_SUCCESS;
333 ULONG Index = 0;
334 PVOID* NewTable;
335
336 /* Make sure all the parameters were specified */
337 if (!(Table) || !(*Table) || !(Count) || !(Callback))
338 {
339 return STATUS_INVALID_PARAMETER;
340 }
341
342 /* Read the current table */
343 NewTable = *Table;
344 NewCount = *Count;
345
346 /* Iterate over it */
347 while (Index < NewCount)
348 {
349 /* Look for a free index */
350 if (!NewTable[Index])
351 {
352 goto SetIndex;
353 }
354
355 /* No free index yet, keep going */
356 ++Index;
357 }
358
359 /* No free index was found, try to purge some entries */
360 Index = 0;
361 while (Index < NewCount)
362 {
363 /* Call each purge callback, trying to make space */
364 Status = Callback(NewTable[Index]);
365 if (NT_SUCCESS(Status))
366 {
367 /* We should have this slot available now */
368 goto SetIndex;
369 }
370
371 /* Keep trying to purge more */
372 ++Index;
373 }
374
375 /* Double the table */
376 NewTable = BlMmAllocateHeap(2 * sizeof(PVOID) * NewCount);
377 if (!NewTable)
378 {
379 return STATUS_NO_MEMORY;
380 }
381
382 /* Clear the new table, and copy the old entries */
383 RtlZeroMemory(&NewTable[NewCount], sizeof(PVOID) * NewCount);
384 RtlCopyMemory(NewTable, *Table, sizeof(PVOID) * NewCount);
385
386 /* Free the old table */
387 BlMmFreeHeap(*Table);
388
389 /* Return the new table and count */
390 *Count = 2 * NewCount;
391 *Table = NewTable;
392
393 SetIndex:
394 /* Set the index and return */
395 NewTable[Index] = Entry;
396 *EntryIndex = Index;
397 return Status;
398 }
399
400 NTSTATUS
401 BlTblMap (
402 _In_ PVOID *Table,
403 _In_ ULONG Count,
404 _In_ PBL_TBL_MAP_ROUTINE MapCallback
405 )
406 {
407 NTSTATUS Status, LocalStatus;
408 PVOID Entry;
409 ULONG Index;
410
411 /* Bail out if there's no table */
412 if (!Table)
413 {
414 return STATUS_INVALID_PARAMETER;
415 }
416
417 /* Assume success and loop each index */
418 Status = STATUS_SUCCESS;
419 for (Index = 0; Index < Count; Index++)
420 {
421 /* See if an entry exists at this index */
422 Entry = Table[Index];
423 if (Entry)
424 {
425 /* Call the map routine for this entry */
426 LocalStatus = MapCallback(Entry, Index);
427 if (!NT_SUCCESS(LocalStatus))
428 {
429 /* Propagate failure only */
430 Status = LocalStatus;
431 }
432 }
433 }
434
435 /* Return status to caller */
436 return Status;
437 }
438
439 ULONG HtTableSize;
440 PBL_HASH_TABLE* HtTableArray;
441 ULONG HtTableEntries;
442
443 ULONG
444 DefaultHashFunction (
445 _In_ PBL_HASH_ENTRY Entry,
446 _In_ ULONG TableSize
447 )
448 {
449 PUCHAR Value;
450 ULONG KeyHash, i;
451
452 /* Check if the value is a pointer, or embedded inline */
453 Value = (Entry->Flags & BL_HT_VALUE_IS_INLINE) ? Entry->Value : (PUCHAR)&Entry->Value;
454
455 /* Iterate over each byte, and sum it */
456 for (i = 0, KeyHash = 0; i < Entry->Size; i++)
457 {
458 KeyHash += Value[i++];
459 }
460
461 /* Modulo the number of buckets */
462 return KeyHash % TableSize;
463 }
464
465 BOOLEAN
466 HtpCompareKeys (
467 _In_ PBL_HASH_ENTRY Entry1,
468 _In_ PBL_HASH_ENTRY Entry2
469 )
470 {
471 ULONG Flags;
472 BOOLEAN ValueMatch;
473
474 /* Check if the flags or sizes are not matching */
475 Flags = Entry1->Flags;
476 if ((Entry1->Size != Entry2->Size) || (Flags != Entry2->Flags))
477 {
478 ValueMatch = FALSE;
479 }
480 else if (Flags & BL_HT_VALUE_IS_INLINE)
481 {
482 /* Check if this is an in-line value, compare it */
483 ValueMatch = Entry1->Value == Entry2->Value;
484 }
485 else
486 {
487 /* This is a pointer value, compare it */
488 ValueMatch = (RtlCompareMemory(Entry1->Value, Entry2->Value, Entry1->Size) ==
489 Entry1->Size);
490 }
491
492 /* Return if it matched */
493 return ValueMatch;
494 }
495
496 NTSTATUS
497 TblDoNotPurgeEntry (
498 _In_ PVOID Entry
499 )
500 {
501 /* Never purge this entry */
502 return STATUS_UNSUCCESSFUL;
503 }
504
505 NTSTATUS
506 BlHtCreate (
507 _In_ ULONG Size,
508 _In_ PBL_HASH_TABLE_HASH_FUNCTION HashFunction,
509 _In_ PBL_HASH_TABLE_COMPARE_FUNCTION CompareFunction,
510 _Out_ PULONG Id
511 )
512 {
513 NTSTATUS Status;
514 PBL_HASH_TABLE HashTable;
515 ULONG i;
516
517 /* Assume failure */
518 HashTable = NULL;
519
520 /* Can't create a table with no ID */
521 if (!Id)
522 {
523 return STATUS_INVALID_PARAMETER;
524 }
525
526 /* Check if we don't already have a hash table table */
527 if (!HtTableSize)
528 {
529 /* Allocate it and zero it out */
530 HtTableSize = 4;
531 HtTableArray = BlMmAllocateHeap(HtTableSize * sizeof(PVOID));
532 if (!HtTableArray)
533 {
534 Status = STATUS_NO_MEMORY;
535 goto Quickie;
536 }
537 RtlZeroMemory(HtTableArray, HtTableSize * sizeof(PVOID));
538 HtTableEntries = 0;
539 }
540
541 /* Allocate the hash table */
542 HashTable = BlMmAllocateHeap(sizeof(*HashTable));
543 if (!HashTable)
544 {
545 Status = STATUS_NO_MEMORY;
546 goto Quickie;
547 }
548
549 /* Fill it out */
550 HashTable->HashFunction = HashFunction ? HashFunction : DefaultHashFunction;
551 HashTable->CompareFunction = CompareFunction ? CompareFunction : HtpCompareKeys;
552 HashTable->Size = Size ? Size : 13;
553
554 /* Allocate the hash links, one for each bucket */
555 HashTable->HashLinks = BlMmAllocateHeap(sizeof(LIST_ENTRY) * HashTable->Size);
556 if (!HashTable->HashLinks)
557 {
558 Status = STATUS_NO_MEMORY;
559 goto Quickie;
560 }
561
562 /* Initialize the hash links */
563 for (i = 0; i < HashTable->Size; i++)
564 {
565 InitializeListHead(&HashTable->HashLinks[i]);
566 }
567
568 /* Save us in the table of hash tables */
569 Status = BlTblSetEntry((PVOID**)&HtTableArray,
570 &Size,
571 HashTable,
572 Id,
573 TblDoNotPurgeEntry);
574 if (NT_SUCCESS(Status))
575 {
576 /* One more -- we're done */
577 ++HtTableEntries;
578 return Status;
579 }
580
581 Quickie:
582 /* Check if we just allocated the table array now */
583 if (!(HtTableEntries) && (HtTableArray))
584 {
585 /* Free it */
586 BlMmFreeHeap(HtTableArray);
587 HtTableArray = NULL;
588 HtTableSize = 0;
589 }
590
591 /* Check if we allocated a hash table*/
592 if (HashTable)
593 {
594 /* With links? */
595 if (HashTable->HashLinks)
596 {
597 /* Free them */
598 BlMmFreeHeap(HashTable->HashLinks);
599 }
600
601 /* Free the table*/
602 BlMmFreeHeap(HashTable);
603 }
604
605 /* We're done */
606 return Status;
607 }
608
609 NTSTATUS
610 BlHtLookup (
611 _In_ ULONG TableId,
612 _In_ PBL_HASH_ENTRY Entry,
613 _Out_opt_ PBL_HASH_VALUE *Value
614 )
615 {
616 PBL_HASH_TABLE HashTable;
617 ULONG HashValue;
618 NTSTATUS Status;
619 PLIST_ENTRY HashLinkHead, HashLink;
620 PBL_HASH_NODE HashNode;
621
622 /* Check if the table ID is invalid, or we have no entry, or it's malformed */
623 if ((HtTableSize <= TableId) ||
624 !(Entry) ||
625 ((Entry->Flags & BL_HT_VALUE_IS_INLINE) && (Entry->Size != sizeof(ULONG))))
626 {
627 /* Fail */
628 Status = STATUS_INVALID_PARAMETER;
629 }
630 else
631 {
632 /* Otherwise, get the hash table for this index */
633 HashTable = HtTableArray[TableId];
634
635 /* Get the hash bucket */
636 HashValue = HashTable->HashFunction(Entry, HashTable->Size);
637
638 /* Start iterating each entry in the bucket, assuming failure */
639 Status = STATUS_NOT_FOUND;
640 HashLinkHead = &HashTable->HashLinks[HashValue];
641 HashLink = HashLinkHead->Flink;
642 while (HashLink != HashLinkHead)
643 {
644 /* Get a node in this bucket, and compare the value */
645 HashNode = CONTAINING_RECORD(HashLink, BL_HASH_NODE, ListEntry);
646 if (HashTable->CompareFunction(&HashNode->Entry, Entry))
647 {
648 /* Does the caller want the value? */
649 if (Value)
650 {
651 /* Return it */
652 *Value = &HashNode->Value;
653 }
654
655 /* Return success and stop scanning */
656 Status = STATUS_SUCCESS;
657 break;
658 }
659
660 /* Try the next node */
661 HashLink = HashLink->Flink;
662 }
663 }
664
665 /* Return back to the caller */
666 return Status;
667 }
668
669 NTSTATUS
670 BlHtStore (
671 _In_ ULONG TableId,
672 _In_ PBL_HASH_ENTRY Entry,
673 _In_ PVOID Data,
674 _In_ ULONG DataSize
675 )
676 {
677 PBL_HASH_NODE HashNode;
678 NTSTATUS Status;
679 PLIST_ENTRY HashLinkHead;
680 PBL_HASH_TABLE HashTable;
681
682 /* Check for invalid tablle ID, missing arguments, or malformed entry */
683 if ((HtTableSize <= TableId) ||
684 !(Entry) ||
685 !(Data) ||
686 !(Entry->Size) ||
687 !(Entry->Value) ||
688 !(DataSize) ||
689 ((Entry->Flags & BL_HT_VALUE_IS_INLINE) && (Entry->Size != sizeof(ULONG))))
690 {
691 /* Fail the call */
692 Status = STATUS_INVALID_PARAMETER;
693 goto Quickie;
694 }
695
696 /* Get the hash table for this ID */
697 HashTable = HtTableArray[TableId];
698
699 /* Allocate a hash node */
700 HashNode = BlMmAllocateHeap(sizeof(*HashNode));
701 if (!HashNode)
702 {
703 Status = STATUS_NO_MEMORY;
704 goto Quickie;
705 }
706
707 /* Capture all the data*/
708 HashNode->Entry.Size = Entry->Size;
709 HashNode->Entry.Flags = Entry->Flags;
710 HashNode->Entry.Value = Entry->Value;
711 HashNode->Value.DataSize = DataSize;
712 HashNode->Value.Data = Data;
713
714 /* Insert it into the bucket list and return success */
715 HashLinkHead = &HashTable->HashLinks[HashTable->HashFunction(Entry, HashTable->Size)];
716 InsertTailList(HashLinkHead, &HashNode->ListEntry);
717 Status = STATUS_SUCCESS;
718
719 Quickie:
720 return Status;
721 }
722
723 ULONG
724 BlUtlCheckSum (
725 _In_ ULONG PartialSum,
726 _In_ PUCHAR Buffer,
727 _In_ ULONG Length,
728 _In_ ULONG Flags
729 )
730 {
731 ULONG i;
732
733 if (Flags & BL_UTL_CHECKSUM_UCHAR_BUFFER)
734 {
735 EfiPrintf(L"Not supported\r\n");
736 return 0;
737 }
738 else if (Flags & BL_UTL_CHECKSUM_USHORT_BUFFER)
739 {
740 PartialSum = (unsigned __int16)PartialSum;
741 Length &= ~1;
742
743 for (i = 0; i < Length; i += 2)
744 {
745 PartialSum += *(unsigned __int16 *)&Buffer[i];
746 if (Flags & BL_UTL_CHECKSUM_COMPLEMENT)
747 {
748 PartialSum = (unsigned __int16)((PartialSum >> 16) + PartialSum);
749 }
750 }
751
752 if (Length != Length)
753 {
754 PartialSum += (unsigned __int8)Buffer[Length];
755 if (Flags & BL_UTL_CHECKSUM_COMPLEMENT)
756 {
757 PartialSum = (unsigned __int16)((PartialSum >> 16) + PartialSum);
758 }
759 }
760
761 if (Flags & BL_UTL_CHECKSUM_NEGATE)
762 {
763 return ~PartialSum;
764 }
765
766 PartialSum = (unsigned __int16)PartialSum;
767 }
768 else
769 {
770 /* Invalid mode */
771 return 0;
772 }
773
774 if (Flags & BL_UTL_CHECKSUM_NEGATE)
775 {
776 return ~PartialSum;
777 }
778
779 return PartialSum;
780 }