]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Bus/Pci/XhciPei/UsbHcMem.c
UefiCpuPkg: Move AsmRelocateApLoopStart from Mpfuncs.nasm to AmdSev.nasm
[mirror_edk2.git] / MdeModulePkg / Bus / Pci / XhciPei / UsbHcMem.c
1 /** @file
2 PEIM to produce gPeiUsb2HostControllerPpiGuid based on gPeiUsbControllerPpiGuid
3 which is used to enable recovery function from USB Drivers.
4
5 Copyright (c) 2014 - 2016, Intel Corporation. All rights reserved.<BR>
6 Copyright (C) 2022 Advanced Micro Devices, Inc. All rights reserved.<BR>
7
8 SPDX-License-Identifier: BSD-2-Clause-Patent
9
10 **/
11
12 #include "XhcPeim.h"
13
14 /**
15 Allocate a block of memory to be used by the buffer pool.
16
17 @param Pages How many pages to allocate.
18
19 @return Pointer to the allocated memory block or NULL if failed.
20
21 **/
22 USBHC_MEM_BLOCK *
23 UsbHcAllocMemBlock (
24 IN UINTN Pages
25 )
26 {
27 USBHC_MEM_BLOCK *Block;
28 VOID *BufHost;
29 VOID *Mapping;
30 EFI_PHYSICAL_ADDRESS MappedAddr;
31 EFI_STATUS Status;
32 UINTN PageNumber;
33 EFI_PHYSICAL_ADDRESS TempPtr;
34
35 PageNumber = EFI_SIZE_TO_PAGES (sizeof (USBHC_MEM_BLOCK));
36 Status = PeiServicesAllocatePages (
37 EfiBootServicesData,
38 PageNumber,
39 &TempPtr
40 );
41
42 if (EFI_ERROR (Status)) {
43 return NULL;
44 }
45
46 ZeroMem ((VOID *)(UINTN)TempPtr, EFI_PAGES_TO_SIZE (PageNumber));
47
48 //
49 // each bit in the bit array represents USBHC_MEM_UNIT
50 // bytes of memory in the memory block.
51 //
52 ASSERT (USBHC_MEM_UNIT * 8 <= EFI_PAGE_SIZE);
53
54 Block = (USBHC_MEM_BLOCK *)(UINTN)TempPtr;
55 Block->BufLen = EFI_PAGES_TO_SIZE (Pages);
56 Block->BitsLen = Block->BufLen / (USBHC_MEM_UNIT * 8);
57
58 PageNumber = EFI_SIZE_TO_PAGES (Block->BitsLen);
59 Status = PeiServicesAllocatePages (
60 EfiBootServicesData,
61 PageNumber,
62 &TempPtr
63 );
64
65 if (EFI_ERROR (Status)) {
66 return NULL;
67 }
68
69 ZeroMem ((VOID *)(UINTN)TempPtr, EFI_PAGES_TO_SIZE (PageNumber));
70
71 Block->Bits = (UINT8 *)(UINTN)TempPtr;
72
73 Status = IoMmuAllocateBuffer (
74 Pages,
75 &BufHost,
76 &MappedAddr,
77 &Mapping
78 );
79 if (EFI_ERROR (Status)) {
80 return NULL;
81 }
82
83 ZeroMem ((VOID *)(UINTN)BufHost, EFI_PAGES_TO_SIZE (Pages));
84
85 Block->BufHost = (UINT8 *)(UINTN)BufHost;
86 Block->Buf = (UINT8 *)(UINTN)MappedAddr;
87 Block->Mapping = Mapping;
88 Block->Next = NULL;
89
90 return Block;
91 }
92
93 /**
94 Free the memory block from the memory pool.
95
96 @param Pool The memory pool to free the block from.
97 @param Block The memory block to free.
98
99 **/
100 VOID
101 UsbHcFreeMemBlock (
102 IN USBHC_MEM_POOL *Pool,
103 IN USBHC_MEM_BLOCK *Block
104 )
105 {
106 ASSERT ((Pool != NULL) && (Block != NULL));
107
108 IoMmuFreeBuffer (EFI_SIZE_TO_PAGES (Block->BufLen), Block->BufHost, Block->Mapping);
109
110 //
111 // No free memory in PEI.
112 //
113 }
114
115 /**
116 Alloc some memory from the block.
117
118 @param Block The memory block to allocate memory from.
119 @param Units Number of memory units to allocate.
120
121 @return The pointer to the allocated memory.
122 If couldn't allocate the needed memory, the return value is NULL.
123
124 **/
125 VOID *
126 UsbHcAllocMemFromBlock (
127 IN USBHC_MEM_BLOCK *Block,
128 IN UINTN Units
129 )
130 {
131 UINTN Byte;
132 UINT8 Bit;
133 UINTN StartByte;
134 UINT8 StartBit;
135 UINTN Available;
136 UINTN Count;
137
138 ASSERT ((Block != 0) && (Units != 0));
139
140 StartByte = 0;
141 StartBit = 0;
142 Available = 0;
143
144 for (Byte = 0, Bit = 0; Byte < Block->BitsLen;) {
145 //
146 // If current bit is zero, the corresponding memory unit is
147 // available, otherwise we need to restart our searching.
148 // Available counts the consective number of zero bit.
149 //
150 if (!USB_HC_BIT_IS_SET (Block->Bits[Byte], Bit)) {
151 Available++;
152
153 if (Available >= Units) {
154 break;
155 }
156
157 NEXT_BIT (Byte, Bit);
158 } else {
159 NEXT_BIT (Byte, Bit);
160
161 Available = 0;
162 StartByte = Byte;
163 StartBit = Bit;
164 }
165 }
166
167 if (Available < Units) {
168 return NULL;
169 }
170
171 //
172 // Mark the memory as allocated
173 //
174 Byte = StartByte;
175 Bit = StartBit;
176
177 for (Count = 0; Count < Units; Count++) {
178 ASSERT (!USB_HC_BIT_IS_SET (Block->Bits[Byte], Bit));
179
180 Block->Bits[Byte] = (UINT8)(Block->Bits[Byte] | (UINT8)USB_HC_BIT (Bit));
181 NEXT_BIT (Byte, Bit);
182 }
183
184 return Block->BufHost + (StartByte * 8 + StartBit) * USBHC_MEM_UNIT;
185 }
186
187 /**
188 Calculate the corresponding pci bus address according to the Mem parameter.
189
190 @param Pool The memory pool of the host controller.
191 @param Mem The pointer to host memory.
192 @param Size The size of the memory region.
193
194 @return The pci memory address
195
196 **/
197 EFI_PHYSICAL_ADDRESS
198 UsbHcGetPciAddrForHostAddr (
199 IN USBHC_MEM_POOL *Pool,
200 IN VOID *Mem,
201 IN UINTN Size
202 )
203 {
204 USBHC_MEM_BLOCK *Head;
205 USBHC_MEM_BLOCK *Block;
206 UINTN AllocSize;
207 EFI_PHYSICAL_ADDRESS PhyAddr;
208 UINTN Offset;
209
210 Head = Pool->Head;
211 AllocSize = USBHC_MEM_ROUND (Size);
212
213 if (Mem == NULL) {
214 return 0;
215 }
216
217 for (Block = Head; Block != NULL; Block = Block->Next) {
218 //
219 // scan the memory block list for the memory block that
220 // completely contains the allocated memory.
221 //
222 if ((Block->BufHost <= (UINT8 *)Mem) && (((UINT8 *)Mem + AllocSize) <= (Block->BufHost + Block->BufLen))) {
223 break;
224 }
225 }
226
227 ASSERT ((Block != NULL));
228 //
229 // calculate the pci memory address for host memory address.
230 //
231 Offset = (UINT8 *)Mem - Block->BufHost;
232 PhyAddr = (EFI_PHYSICAL_ADDRESS)(UINTN)(Block->Buf + Offset);
233 return PhyAddr;
234 }
235
236 /**
237 Calculate the corresponding host address according to the pci address.
238
239 @param Pool The memory pool of the host controller.
240 @param Mem The pointer to pci memory.
241 @param Size The size of the memory region.
242
243 @return The host memory address
244
245 **/
246 EFI_PHYSICAL_ADDRESS
247 UsbHcGetHostAddrForPciAddr (
248 IN USBHC_MEM_POOL *Pool,
249 IN VOID *Mem,
250 IN UINTN Size
251 )
252 {
253 USBHC_MEM_BLOCK *Head;
254 USBHC_MEM_BLOCK *Block;
255 UINTN AllocSize;
256 EFI_PHYSICAL_ADDRESS HostAddr;
257 UINTN Offset;
258
259 Head = Pool->Head;
260 AllocSize = USBHC_MEM_ROUND (Size);
261
262 if (Mem == NULL) {
263 return 0;
264 }
265
266 for (Block = Head; Block != NULL; Block = Block->Next) {
267 //
268 // scan the memory block list for the memory block that
269 // completely contains the allocated memory.
270 //
271 if ((Block->Buf <= (UINT8 *)Mem) && (((UINT8 *)Mem + AllocSize) <= (Block->Buf + Block->BufLen))) {
272 break;
273 }
274 }
275
276 ASSERT ((Block != NULL));
277 //
278 // calculate the host memory address for pci memory address.
279 //
280 Offset = (UINT8 *)Mem - Block->Buf;
281 HostAddr = (EFI_PHYSICAL_ADDRESS)(UINTN)(Block->BufHost + Offset);
282 return HostAddr;
283 }
284
285 /**
286 Insert the memory block to the pool's list of the blocks.
287
288 @param Head The head of the memory pool's block list.
289 @param Block The memory block to insert.
290
291 **/
292 VOID
293 UsbHcInsertMemBlockToPool (
294 IN USBHC_MEM_BLOCK *Head,
295 IN USBHC_MEM_BLOCK *Block
296 )
297 {
298 ASSERT ((Head != NULL) && (Block != NULL));
299 Block->Next = Head->Next;
300 Head->Next = Block;
301 }
302
303 /**
304 Is the memory block empty?
305
306 @param Block The memory block to check.
307
308 @retval TRUE The memory block is empty.
309 @retval FALSE The memory block isn't empty.
310
311 **/
312 BOOLEAN
313 UsbHcIsMemBlockEmpty (
314 IN USBHC_MEM_BLOCK *Block
315 )
316 {
317 UINTN Index;
318
319 for (Index = 0; Index < Block->BitsLen; Index++) {
320 if (Block->Bits[Index] != 0) {
321 return FALSE;
322 }
323 }
324
325 return TRUE;
326 }
327
328 /**
329 Initialize the memory management pool for the host controller.
330
331 @return Pointer to the allocated memory pool or NULL if failed.
332
333 **/
334 USBHC_MEM_POOL *
335 UsbHcInitMemPool (
336 VOID
337 )
338 {
339 USBHC_MEM_POOL *Pool;
340 UINTN PageNumber;
341 EFI_STATUS Status;
342 EFI_PHYSICAL_ADDRESS TempPtr;
343
344 PageNumber = EFI_SIZE_TO_PAGES (sizeof (USBHC_MEM_POOL));
345 Status = PeiServicesAllocatePages (
346 EfiBootServicesData,
347 PageNumber,
348 &TempPtr
349 );
350 if (EFI_ERROR (Status)) {
351 return NULL;
352 }
353
354 ZeroMem ((VOID *)(UINTN)TempPtr, EFI_PAGES_TO_SIZE (PageNumber));
355
356 Pool = (USBHC_MEM_POOL *)((UINTN)TempPtr);
357 Pool->Head = UsbHcAllocMemBlock (USBHC_MEM_DEFAULT_PAGES);
358
359 if (Pool->Head == NULL) {
360 //
361 // No free memory in PEI.
362 //
363 Pool = NULL;
364 }
365
366 return Pool;
367 }
368
369 /**
370 Unlink the memory block from the pool's list.
371
372 @param Head The block list head of the memory's pool.
373 @param BlockToUnlink The memory block to unlink.
374
375 **/
376 VOID
377 UsbHcUnlinkMemBlock (
378 IN USBHC_MEM_BLOCK *Head,
379 IN USBHC_MEM_BLOCK *BlockToUnlink
380 )
381 {
382 USBHC_MEM_BLOCK *Block;
383
384 ASSERT ((Head != NULL) && (BlockToUnlink != NULL));
385
386 for (Block = Head; Block != NULL; Block = Block->Next) {
387 if (Block->Next == BlockToUnlink) {
388 Block->Next = BlockToUnlink->Next;
389 BlockToUnlink->Next = NULL;
390 break;
391 }
392 }
393 }
394
395 /**
396 Release the memory management pool.
397
398 @param Pool The USB memory pool to free.
399
400 **/
401 VOID
402 UsbHcFreeMemPool (
403 IN USBHC_MEM_POOL *Pool
404 )
405 {
406 USBHC_MEM_BLOCK *Block;
407
408 ASSERT (Pool->Head != NULL);
409
410 //
411 // Unlink all the memory blocks from the pool, then free them.
412 // UsbHcUnlinkMemBlock can't be used to unlink and free the
413 // first block.
414 //
415 for (Block = Pool->Head->Next; Block != NULL; Block = Pool->Head->Next) {
416 UsbHcUnlinkMemBlock (Pool->Head, Block);
417 UsbHcFreeMemBlock (Pool, Block);
418 }
419
420 UsbHcFreeMemBlock (Pool, Pool->Head);
421 }
422
423 /**
424 Allocate some memory from the host controller's memory pool
425 which can be used to communicate with host controller.
426
427 @param Pool The host controller's memory pool.
428 @param Size Size of the memory to allocate.
429
430 @return The allocated memory or NULL.
431
432 **/
433 VOID *
434 UsbHcAllocateMem (
435 IN USBHC_MEM_POOL *Pool,
436 IN UINTN Size
437 )
438 {
439 USBHC_MEM_BLOCK *Head;
440 USBHC_MEM_BLOCK *Block;
441 USBHC_MEM_BLOCK *NewBlock;
442 VOID *Mem;
443 UINTN AllocSize;
444 UINTN Pages;
445
446 Mem = NULL;
447 AllocSize = USBHC_MEM_ROUND (Size);
448 Head = Pool->Head;
449 ASSERT (Head != NULL);
450
451 //
452 // First check whether current memory blocks can satisfy the allocation.
453 //
454 for (Block = Head; Block != NULL; Block = Block->Next) {
455 Mem = UsbHcAllocMemFromBlock (Block, AllocSize / USBHC_MEM_UNIT);
456
457 if (Mem != NULL) {
458 ZeroMem (Mem, Size);
459 break;
460 }
461 }
462
463 if (Mem != NULL) {
464 return Mem;
465 }
466
467 //
468 // Create a new memory block if there is not enough memory
469 // in the pool. If the allocation size is larger than the
470 // default page number, just allocate a large enough memory
471 // block. Otherwise allocate default pages.
472 //
473 if (AllocSize > EFI_PAGES_TO_SIZE (USBHC_MEM_DEFAULT_PAGES)) {
474 Pages = EFI_SIZE_TO_PAGES (AllocSize);
475 } else {
476 Pages = USBHC_MEM_DEFAULT_PAGES;
477 }
478
479 NewBlock = UsbHcAllocMemBlock (Pages);
480
481 if (NewBlock == NULL) {
482 return NULL;
483 }
484
485 //
486 // Add the new memory block to the pool, then allocate memory from it
487 //
488 UsbHcInsertMemBlockToPool (Head, NewBlock);
489 Mem = UsbHcAllocMemFromBlock (NewBlock, AllocSize / USBHC_MEM_UNIT);
490
491 if (Mem != NULL) {
492 ZeroMem (Mem, Size);
493 }
494
495 return Mem;
496 }
497
498 /**
499 Free the allocated memory back to the memory pool.
500
501 @param Pool The memory pool of the host controller.
502 @param Mem The memory to free.
503 @param Size The size of the memory to free.
504
505 **/
506 VOID
507 UsbHcFreeMem (
508 IN USBHC_MEM_POOL *Pool,
509 IN VOID *Mem,
510 IN UINTN Size
511 )
512 {
513 USBHC_MEM_BLOCK *Head;
514 USBHC_MEM_BLOCK *Block;
515 UINT8 *ToFree;
516 UINTN AllocSize;
517 UINTN Byte;
518 UINTN Bit;
519 UINTN Count;
520
521 Head = Pool->Head;
522 AllocSize = USBHC_MEM_ROUND (Size);
523 ToFree = (UINT8 *)Mem;
524
525 for (Block = Head; Block != NULL; Block = Block->Next) {
526 //
527 // scan the memory block list for the memory block that
528 // completely contains the memory to free.
529 //
530 if ((Block->BufHost <= ToFree) && ((ToFree + AllocSize) <= (Block->BufHost + Block->BufLen))) {
531 //
532 // compute the start byte and bit in the bit array
533 //
534 Byte = ((ToFree - Block->BufHost) / USBHC_MEM_UNIT) / 8;
535 Bit = ((ToFree - Block->BufHost) / USBHC_MEM_UNIT) % 8;
536
537 //
538 // reset associated bits in bit array
539 //
540 for (Count = 0; Count < (AllocSize / USBHC_MEM_UNIT); Count++) {
541 ASSERT (USB_HC_BIT_IS_SET (Block->Bits[Byte], Bit));
542
543 Block->Bits[Byte] = (UINT8)(Block->Bits[Byte] ^ USB_HC_BIT (Bit));
544 NEXT_BIT (Byte, Bit);
545 }
546
547 break;
548 }
549 }
550
551 //
552 // If Block == NULL, it means that the current memory isn't
553 // in the host controller's pool. This is critical because
554 // the caller has passed in a wrong memory pointer
555 //
556 ASSERT (Block != NULL);
557
558 //
559 // Release the current memory block if it is empty and not the head
560 //
561 if ((Block != Head) && UsbHcIsMemBlockEmpty (Block)) {
562 UsbHcUnlinkMemBlock (Head, Block);
563 UsbHcFreeMemBlock (Pool, Block);
564 }
565 }
566
567 /**
568 Allocates pages at a specified alignment.
569
570 If Alignment is not a power of two and Alignment is not zero, then ASSERT().
571
572 @param Pages The number of pages to allocate.
573 @param Alignment The requested alignment of the allocation. Must be a power of two.
574 @param HostAddress The system memory address to map to the PCI controller.
575 @param DeviceAddress The resulting map address for the bus master PCI controller to
576 use to access the hosts HostAddress.
577 @param Mapping A resulting value to pass to Unmap().
578
579 @retval EFI_SUCCESS Success to allocate aligned pages.
580 @retval EFI_INVALID_PARAMETER Pages or Alignment is not valid.
581 @retval EFI_OUT_OF_RESOURCES Do not have enough resources to allocate memory.
582
583 **/
584 EFI_STATUS
585 UsbHcAllocateAlignedPages (
586 IN UINTN Pages,
587 IN UINTN Alignment,
588 OUT VOID **HostAddress,
589 OUT EFI_PHYSICAL_ADDRESS *DeviceAddress,
590 OUT VOID **Mapping
591 )
592 {
593 EFI_STATUS Status;
594 VOID *Memory;
595 EFI_PHYSICAL_ADDRESS DeviceMemory;
596
597 //
598 // Alignment must be a power of two or zero.
599 //
600 ASSERT ((Alignment & (Alignment - 1)) == 0);
601
602 if ((Alignment & (Alignment - 1)) != 0) {
603 return EFI_INVALID_PARAMETER;
604 }
605
606 if (Pages == 0) {
607 return EFI_INVALID_PARAMETER;
608 }
609
610 if (Alignment > EFI_PAGE_SIZE) {
611 Status = IoMmuAllocateAlignedBuffer (
612 Pages,
613 Alignment,
614 &Memory,
615 &DeviceMemory,
616 Mapping
617 );
618 if (EFI_ERROR (Status)) {
619 return EFI_OUT_OF_RESOURCES;
620 }
621 } else {
622 //
623 // Do not over-allocate pages in this case.
624 //
625 Status = IoMmuAllocateBuffer (
626 Pages,
627 &Memory,
628 &DeviceMemory,
629 Mapping
630 );
631 if (EFI_ERROR (Status)) {
632 return EFI_OUT_OF_RESOURCES;
633 }
634 }
635
636 *HostAddress = Memory;
637 *DeviceAddress = DeviceMemory;
638
639 return EFI_SUCCESS;
640 }
641
642 /**
643 Frees memory that was allocated with UsbHcAllocateAlignedPages().
644
645 @param HostAddress The system memory address to map to the PCI controller.
646 @param Pages The number of pages to free.
647 @param Mapping The mapping value returned from Map().
648
649 **/
650 VOID
651 UsbHcFreeAlignedPages (
652 IN VOID *HostAddress,
653 IN UINTN Pages,
654 IN VOID *Mapping
655 )
656 {
657 ASSERT (Pages != 0);
658
659 IoMmuFreeBuffer (Pages, HostAddress, Mapping);
660 }