• 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) 2010, 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 "EhcPeim.h"
19 
20 /**
21   Allocate a block of memory to be used by the buffer pool.
22 
23   @param  Ehc            The EHCI device.
24   @param  Pool           The buffer pool to allocate memory for.
25   @param  Pages          How many pages to allocate.
26 
27   @return The allocated memory block or NULL if failed.
28 
29 **/
30 USBHC_MEM_BLOCK *
UsbHcAllocMemBlock(IN PEI_USB2_HC_DEV * Ehc,IN USBHC_MEM_POOL * Pool,IN UINTN Pages)31 UsbHcAllocMemBlock (
32   IN PEI_USB2_HC_DEV      *Ehc,
33   IN  USBHC_MEM_POOL      *Pool,
34   IN  UINTN               Pages
35   )
36 {
37   USBHC_MEM_BLOCK         *Block;
38   VOID                    *BufHost;
39   VOID                    *Mapping;
40   EFI_PHYSICAL_ADDRESS    MappedAddr;
41   EFI_STATUS              Status;
42   UINTN               PageNumber;
43   EFI_PHYSICAL_ADDRESS        TempPtr;
44 
45   Mapping = NULL;
46   PageNumber =  sizeof(USBHC_MEM_BLOCK)/PAGESIZE +1;
47   Status = PeiServicesAllocatePages (
48              EfiBootServicesCode,
49              PageNumber,
50              &TempPtr
51              );
52 
53   if (EFI_ERROR (Status)) {
54       return NULL;
55   }
56   ZeroMem ((VOID   *)(UINTN)TempPtr, PageNumber*EFI_PAGE_SIZE);
57 
58   //
59   // each bit in the bit array represents USBHC_MEM_UNIT
60   // bytes of memory in the memory block.
61   //
62   ASSERT (USBHC_MEM_UNIT * 8 <= EFI_PAGE_SIZE);
63 
64   Block = (USBHC_MEM_BLOCK*)(UINTN)TempPtr;
65   Block->BufLen   = EFI_PAGES_TO_SIZE (Pages);
66   Block->BitsLen  = Block->BufLen / (USBHC_MEM_UNIT * 8);
67 
68   PageNumber =  (Block->BitsLen)/PAGESIZE +1;
69   Status = PeiServicesAllocatePages (
70              EfiBootServicesCode,
71              PageNumber,
72              &TempPtr
73              );
74 
75     if (EFI_ERROR (Status)) {
76       return NULL;
77     }
78   ZeroMem ((VOID   *)(UINTN)TempPtr, PageNumber*EFI_PAGE_SIZE);
79 
80   Block->Bits  = (UINT8 *)(UINTN)TempPtr;
81 
82 
83   Status = PeiServicesAllocatePages (
84              EfiBootServicesCode,
85              Pages,
86              &TempPtr
87              );
88   ZeroMem ((VOID   *)(UINTN)TempPtr, Pages*EFI_PAGE_SIZE);
89 
90   BufHost  = (VOID *)(UINTN)TempPtr;
91   MappedAddr = (EFI_PHYSICAL_ADDRESS) (UINTN) BufHost;
92   //
93   // Check whether the data structure used by the host controller
94   // should be restricted into the same 4G
95   //
96   if (Pool->Check4G && (Pool->Which4G != USB_HC_HIGH_32BIT (MappedAddr))) {
97       return NULL;
98   }
99 
100   Block->BufHost  = BufHost;
101   Block->Buf      = (UINT8 *) ((UINTN) MappedAddr);
102   Block->Mapping  = Mapping;
103   Block->Next      = NULL;
104 
105   return Block;
106 
107 }
108 
109 /**
110   Free the memory block from the memory pool.
111 
112   @param  Pool           The memory pool to free the block from.
113   @param  Block          The memory block to free.
114 
115 **/
116 VOID
UsbHcFreeMemBlock(IN USBHC_MEM_POOL * Pool,IN USBHC_MEM_BLOCK * Block)117 UsbHcFreeMemBlock (
118   IN USBHC_MEM_POOL       *Pool,
119   IN USBHC_MEM_BLOCK      *Block
120   )
121 {
122   ASSERT ((Pool != NULL) && (Block != NULL));
123 }
124 
125 /**
126   Alloc some memory from the block.
127 
128   @param  Block          The memory block to allocate memory from.
129   @param  Units          Number of memory units to allocate.
130 
131   @return The pointer to the allocated memory. If couldn't allocate the needed memory,
132           the return value is NULL.
133 
134 **/
135 VOID *
UsbHcAllocMemFromBlock(IN USBHC_MEM_BLOCK * Block,IN UINTN Units)136 UsbHcAllocMemFromBlock (
137   IN  USBHC_MEM_BLOCK     *Block,
138   IN  UINTN               Units
139   )
140 {
141   UINTN                   Byte;
142   UINT8                   Bit;
143   UINTN                   StartByte;
144   UINT8                   StartBit;
145   UINTN                   Available;
146   UINTN                   Count;
147 
148   ASSERT ((Block != 0) && (Units != 0));
149 
150   StartByte  = 0;
151   StartBit   = 0;
152   Available  = 0;
153 
154   for (Byte = 0, Bit = 0; Byte < Block->BitsLen;) {
155     //
156     // If current bit is zero, the corresponding memory unit is
157     // available, otherwise we need to restart our searching.
158     // Available counts the consective number of zero bit.
159     //
160     if (!USB_HC_BIT_IS_SET (Block->Bits[Byte], Bit)) {
161       Available++;
162 
163       if (Available >= Units) {
164         break;
165       }
166 
167       NEXT_BIT (Byte, Bit);
168 
169     } else {
170       NEXT_BIT (Byte, Bit);
171 
172       Available  = 0;
173       StartByte  = Byte;
174       StartBit   = Bit;
175     }
176   }
177 
178   if (Available < Units) {
179     return NULL;
180   }
181 
182   //
183   // Mark the memory as allocated
184   //
185   Byte  = StartByte;
186   Bit   = StartBit;
187 
188   for (Count = 0; Count < Units; Count++) {
189     ASSERT (!USB_HC_BIT_IS_SET (Block->Bits[Byte], Bit));
190 
191     Block->Bits[Byte] = (UINT8) (Block->Bits[Byte] | (UINT8) USB_HC_BIT (Bit));
192     NEXT_BIT (Byte, Bit);
193   }
194 
195   return Block->Buf + (StartByte * 8 + StartBit) * USBHC_MEM_UNIT;
196 }
197 
198 /**
199   Insert the memory block to the pool's list of the blocks.
200 
201   @param  Head           The head of the memory pool's block list.
202   @param  Block          The memory block to insert.
203 
204 **/
205 VOID
UsbHcInsertMemBlockToPool(IN USBHC_MEM_BLOCK * Head,IN USBHC_MEM_BLOCK * Block)206 UsbHcInsertMemBlockToPool (
207   IN USBHC_MEM_BLOCK      *Head,
208   IN USBHC_MEM_BLOCK      *Block
209   )
210 {
211   ASSERT ((Head != NULL) && (Block != NULL));
212   Block->Next = Head->Next;
213   Head->Next  = Block;
214 }
215 
216 /**
217   Is the memory block empty?
218 
219   @param  Block   The memory block to check.
220 
221   @retval TRUE    The memory block is empty.
222   @retval FALSE   The memory block isn't empty.
223 
224 **/
225 BOOLEAN
UsbHcIsMemBlockEmpty(IN USBHC_MEM_BLOCK * Block)226 UsbHcIsMemBlockEmpty (
227   IN USBHC_MEM_BLOCK     *Block
228   )
229 {
230   UINTN                   Index;
231 
232 
233   for (Index = 0; Index < Block->BitsLen; Index++) {
234     if (Block->Bits[Index] != 0) {
235       return FALSE;
236     }
237   }
238 
239   return TRUE;
240 }
241 
242 /**
243   Unlink the memory block from the pool's list.
244 
245   @param  Head           The block list head of the memory's pool.
246   @param  BlockToUnlink  The memory block to unlink.
247 
248 **/
249 VOID
UsbHcUnlinkMemBlock(IN USBHC_MEM_BLOCK * Head,IN USBHC_MEM_BLOCK * BlockToUnlink)250 UsbHcUnlinkMemBlock (
251   IN USBHC_MEM_BLOCK      *Head,
252   IN USBHC_MEM_BLOCK      *BlockToUnlink
253   )
254 {
255   USBHC_MEM_BLOCK         *Block;
256 
257   ASSERT ((Head != NULL) && (BlockToUnlink != NULL));
258 
259   for (Block = Head; Block != NULL; Block = Block->Next) {
260     if (Block->Next == BlockToUnlink) {
261       Block->Next         = BlockToUnlink->Next;
262       BlockToUnlink->Next = NULL;
263       break;
264     }
265   }
266 }
267 
268 /**
269   Initialize the memory management pool for the host controller.
270 
271   @param  Ehc                   The EHCI device.
272   @param  Check4G               Whether the host controller requires allocated memory.
273                                 from one 4G address space.
274   @param  Which4G               The 4G memory area each memory allocated should be from.
275 
276   @retval EFI_SUCCESS           The memory pool is initialized.
277   @retval EFI_OUT_OF_RESOURCE   Fail to init the memory pool.
278 
279 **/
280 USBHC_MEM_POOL *
UsbHcInitMemPool(IN PEI_USB2_HC_DEV * Ehc,IN BOOLEAN Check4G,IN UINT32 Which4G)281 UsbHcInitMemPool (
282   IN PEI_USB2_HC_DEV      *Ehc,
283   IN BOOLEAN              Check4G,
284   IN UINT32               Which4G
285   )
286 {
287   USBHC_MEM_POOL          *Pool;
288   UINTN               PageNumber;
289   EFI_STATUS              Status;
290   EFI_PHYSICAL_ADDRESS        TempPtr;
291 
292   PageNumber =  sizeof(USBHC_MEM_POOL)/PAGESIZE +1;
293   Status = PeiServicesAllocatePages (
294              EfiBootServicesCode,
295              PageNumber,
296               &TempPtr
297              );
298 
299     if (EFI_ERROR (Status)) {
300       return NULL;
301     }
302   ZeroMem ((VOID   *)(UINTN)TempPtr, PageNumber*EFI_PAGE_SIZE);
303 
304   Pool = (USBHC_MEM_POOL *) ((UINTN) TempPtr);
305 
306   Pool->Check4G = Check4G;
307   Pool->Which4G = Which4G;
308   Pool->Head    = UsbHcAllocMemBlock (Ehc, Pool, USBHC_MEM_DEFAULT_PAGES);
309 
310   if (Pool->Head == NULL) {
311     Pool = NULL;
312   }
313 
314   return Pool;
315 }
316 
317 /**
318   Release the memory management pool.
319 
320   @param  Pool                  The USB memory pool to free.
321 
322   @retval EFI_DEVICE_ERROR      Fail to free the memory pool.
323   @retval EFI_SUCCESS           The memory pool is freed.
324 
325 **/
326 EFI_STATUS
UsbHcFreeMemPool(IN USBHC_MEM_POOL * Pool)327 UsbHcFreeMemPool (
328   IN USBHC_MEM_POOL       *Pool
329   )
330 {
331   USBHC_MEM_BLOCK *Block;
332 
333   ASSERT (Pool->Head != NULL);
334 
335   //
336   // Unlink all the memory blocks from the pool, then free them.
337   // UsbHcUnlinkMemBlock can't be used to unlink and free the
338   // first block.
339   //
340   for (Block = Pool->Head->Next; Block != NULL; Block = Pool->Head->Next) {
341     UsbHcFreeMemBlock (Pool, Block);
342   }
343 
344   UsbHcFreeMemBlock (Pool, Pool->Head);
345 
346   return EFI_SUCCESS;
347 }
348 
349 /**
350   Allocate some memory from the host controller's memory pool
351   which can be used to communicate with host controller.
352 
353   @param  Ehc       The EHCI device.
354   @param  Pool      The host controller's memory pool.
355   @param  Size      Size of the memory to allocate.
356 
357   @return The allocated memory or NULL.
358 
359 **/
360 VOID *
UsbHcAllocateMem(IN PEI_USB2_HC_DEV * Ehc,IN USBHC_MEM_POOL * Pool,IN UINTN Size)361 UsbHcAllocateMem (
362   IN PEI_USB2_HC_DEV      *Ehc,
363   IN  USBHC_MEM_POOL      *Pool,
364   IN  UINTN               Size
365   )
366 {
367   USBHC_MEM_BLOCK         *Head;
368   USBHC_MEM_BLOCK         *Block;
369   USBHC_MEM_BLOCK         *NewBlock;
370   VOID                    *Mem;
371   UINTN                   AllocSize;
372   UINTN                   Pages;
373 
374   Mem       = NULL;
375   AllocSize = USBHC_MEM_ROUND (Size);
376   Head      = Pool->Head;
377   ASSERT (Head != NULL);
378 
379   //
380   // First check whether current memory blocks can satisfy the allocation.
381   //
382   for (Block = Head; Block != NULL; Block = Block->Next) {
383     Mem = UsbHcAllocMemFromBlock (Block, AllocSize / USBHC_MEM_UNIT);
384 
385     if (Mem != NULL) {
386       ZeroMem (Mem, Size);
387       break;
388     }
389   }
390 
391   if (Mem != NULL) {
392     return Mem;
393   }
394 
395   //
396   // Create a new memory block if there is not enough memory
397   // in the pool. If the allocation size is larger than the
398   // default page number, just allocate a large enough memory
399   // block. Otherwise allocate default pages.
400   //
401   if (AllocSize > EFI_PAGES_TO_SIZE (USBHC_MEM_DEFAULT_PAGES)) {
402     Pages = EFI_SIZE_TO_PAGES (AllocSize) + 1;
403   } else {
404     Pages = USBHC_MEM_DEFAULT_PAGES;
405   }
406   NewBlock = UsbHcAllocMemBlock (Ehc,Pool, Pages);
407 
408   if (NewBlock == NULL) {
409     return NULL;
410   }
411 
412   //
413   // Add the new memory block to the pool, then allocate memory from it
414   //
415   UsbHcInsertMemBlockToPool (Head, NewBlock);
416   Mem = UsbHcAllocMemFromBlock (NewBlock, AllocSize / USBHC_MEM_UNIT);
417 
418   if (Mem != NULL) {
419     ZeroMem (Mem, Size);
420   }
421 
422   return Mem;
423 }
424 
425 /**
426   Free the allocated memory back to the memory pool.
427 
428   @param  Pool           The memory pool of the host controller.
429   @param  Mem            The memory to free.
430   @param  Size           The size of the memory to free.
431 
432 **/
433 VOID
UsbHcFreeMem(IN USBHC_MEM_POOL * Pool,IN VOID * Mem,IN UINTN Size)434 UsbHcFreeMem (
435   IN USBHC_MEM_POOL       *Pool,
436   IN VOID                 *Mem,
437   IN UINTN                Size
438   )
439 {
440   USBHC_MEM_BLOCK         *Head;
441   USBHC_MEM_BLOCK         *Block;
442   UINT8                   *ToFree;
443   UINTN                   AllocSize;
444   UINTN                   Byte;
445   UINTN                   Bit;
446   UINTN                   Count;
447 
448   Head      = Pool->Head;
449   AllocSize = USBHC_MEM_ROUND (Size);
450   ToFree    = (UINT8 *) Mem;
451 
452   for (Block = Head; Block != NULL; Block = Block->Next) {
453     //
454     // scan the memory block list for the memory block that
455     // completely contains the memory to free.
456     //
457     if ((Block->Buf <= ToFree) && ((ToFree + AllocSize) <= (Block->Buf + Block->BufLen))) {
458       //
459       // compute the start byte and bit in the bit array
460       //
461       Byte  = ((ToFree - Block->Buf) / USBHC_MEM_UNIT) / 8;
462       Bit   = ((ToFree - Block->Buf) / USBHC_MEM_UNIT) % 8;
463 
464       //
465       // reset associated bits in bit arry
466       //
467       for (Count = 0; Count < (AllocSize / USBHC_MEM_UNIT); Count++) {
468         ASSERT (USB_HC_BIT_IS_SET (Block->Bits[Byte], Bit));
469 
470         Block->Bits[Byte] = (UINT8) (Block->Bits[Byte] ^ USB_HC_BIT (Bit));
471         NEXT_BIT (Byte, Bit);
472       }
473 
474       break;
475     }
476   }
477 
478   //
479   // If Block == NULL, it means that the current memory isn't
480   // in the host controller's pool. This is critical because
481   // the caller has passed in a wrong memory point
482   //
483   ASSERT (Block != NULL);
484 
485   //
486   // Release the current memory block if it is empty and not the head
487   //
488   if ((Block != Head) && UsbHcIsMemBlockEmpty (Block)) {
489     UsbHcFreeMemBlock (Pool, Block);
490   }
491 
492   return ;
493 }
494