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