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