• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /** @file
2 
3   Routine procedures for memory allocate/free.
4 
5 Copyright (c) 2007 - 2016, Intel Corporation. All rights reserved.<BR>
6 This program and the accompanying materials
7 are licensed and made available under the terms and conditions of the BSD License
8 which accompanies this distribution.  The full text of the license may be found at
9 http://opensource.org/licenses/bsd-license.php
10 
11 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
12 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
13 
14 **/
15 
16 
17 #include "Ehci.h"
18 
19 
20 /**
21   Allocate a block of memory to be used by the buffer pool.
22 
23   @param  Pool           The buffer pool to allocate memory for.
24   @param  Pages          How many pages to allocate.
25 
26   @return The allocated memory block or NULL if failed.
27 
28 **/
29 USBHC_MEM_BLOCK *
UsbHcAllocMemBlock(IN USBHC_MEM_POOL * Pool,IN UINTN Pages)30 UsbHcAllocMemBlock (
31   IN  USBHC_MEM_POOL      *Pool,
32   IN  UINTN               Pages
33   )
34 {
35   USBHC_MEM_BLOCK         *Block;
36   EFI_PCI_IO_PROTOCOL     *PciIo;
37   VOID                    *BufHost;
38   VOID                    *Mapping;
39   EFI_PHYSICAL_ADDRESS    MappedAddr;
40   UINTN                   Bytes;
41   EFI_STATUS              Status;
42 
43   PciIo = Pool->PciIo;
44 
45   Block = AllocateZeroPool (sizeof (USBHC_MEM_BLOCK));
46   if (Block == NULL) {
47     return NULL;
48   }
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->BufLen   = EFI_PAGES_TO_SIZE (Pages);
57   Block->BitsLen  = Block->BufLen / (USBHC_MEM_UNIT * 8);
58   Block->Bits     = AllocateZeroPool (Block->BitsLen);
59 
60   if (Block->Bits == NULL) {
61     gBS->FreePool (Block);
62     return NULL;
63   }
64 
65   //
66   // Allocate the number of Pages of memory, then map it for
67   // bus master read and write.
68   //
69   Status = PciIo->AllocateBuffer (
70                     PciIo,
71                     AllocateAnyPages,
72                     EfiBootServicesData,
73                     Pages,
74                     &BufHost,
75                     0
76                     );
77 
78   if (EFI_ERROR (Status)) {
79     goto FREE_BITARRAY;
80   }
81 
82   Bytes = EFI_PAGES_TO_SIZE (Pages);
83   Status = PciIo->Map (
84                     PciIo,
85                     EfiPciIoOperationBusMasterCommonBuffer,
86                     BufHost,
87                     &Bytes,
88                     &MappedAddr,
89                     &Mapping
90                     );
91 
92   if (EFI_ERROR (Status) || (Bytes != EFI_PAGES_TO_SIZE (Pages))) {
93     goto FREE_BUFFER;
94   }
95 
96   //
97   // Check whether the data structure used by the host controller
98   // should be restricted into the same 4G
99   //
100   if (Pool->Check4G && (Pool->Which4G != USB_HC_HIGH_32BIT (MappedAddr))) {
101     PciIo->Unmap (PciIo, Mapping);
102     goto FREE_BUFFER;
103   }
104 
105   Block->BufHost  = BufHost;
106   Block->Buf      = (UINT8 *) ((UINTN) MappedAddr);
107   Block->Mapping  = Mapping;
108 
109   return Block;
110 
111 FREE_BUFFER:
112   PciIo->FreeBuffer (PciIo, Pages, BufHost);
113 
114 FREE_BITARRAY:
115   gBS->FreePool (Block->Bits);
116   gBS->FreePool (Block);
117   return NULL;
118 }
119 
120 
121 /**
122   Free the memory block from the memory pool.
123 
124   @param  Pool           The memory pool to free the block from.
125   @param  Block          The memory block to free.
126 
127 **/
128 VOID
UsbHcFreeMemBlock(IN USBHC_MEM_POOL * Pool,IN USBHC_MEM_BLOCK * Block)129 UsbHcFreeMemBlock (
130   IN USBHC_MEM_POOL       *Pool,
131   IN USBHC_MEM_BLOCK      *Block
132   )
133 {
134   EFI_PCI_IO_PROTOCOL     *PciIo;
135 
136   ASSERT ((Pool != NULL) && (Block != NULL));
137 
138   PciIo = Pool->PciIo;
139 
140   //
141   // Unmap the common buffer then free the structures
142   //
143   PciIo->Unmap (PciIo, Block->Mapping);
144   PciIo->FreeBuffer (PciIo, EFI_SIZE_TO_PAGES (Block->BufLen), Block->BufHost);
145 
146   gBS->FreePool (Block->Bits);
147   gBS->FreePool (Block);
148 }
149 
150 
151 /**
152   Alloc some memory from the block.
153 
154   @param  Block          The memory block to allocate memory from.
155   @param  Units          Number of memory units to allocate.
156 
157   @return The pointer to the allocated memory. If couldn't allocate the needed memory,
158           the return value is NULL.
159 
160 **/
161 VOID *
UsbHcAllocMemFromBlock(IN USBHC_MEM_BLOCK * Block,IN UINTN Units)162 UsbHcAllocMemFromBlock (
163   IN  USBHC_MEM_BLOCK     *Block,
164   IN  UINTN               Units
165   )
166 {
167   UINTN                   Byte;
168   UINT8                   Bit;
169   UINTN                   StartByte;
170   UINT8                   StartBit;
171   UINTN                   Available;
172   UINTN                   Count;
173 
174   ASSERT ((Block != 0) && (Units != 0));
175 
176   StartByte  = 0;
177   StartBit   = 0;
178   Available  = 0;
179 
180   for (Byte = 0, Bit = 0; Byte < Block->BitsLen;) {
181     //
182     // If current bit is zero, the corresponding memory unit is
183     // available, otherwise we need to restart our searching.
184     // Available counts the consective number of zero bit.
185     //
186     if (!USB_HC_BIT_IS_SET (Block->Bits[Byte], Bit)) {
187       Available++;
188 
189       if (Available >= Units) {
190         break;
191       }
192 
193       NEXT_BIT (Byte, Bit);
194 
195     } else {
196       NEXT_BIT (Byte, Bit);
197 
198       Available  = 0;
199       StartByte  = Byte;
200       StartBit   = Bit;
201     }
202   }
203 
204   if (Available < Units) {
205     return NULL;
206   }
207 
208   //
209   // Mark the memory as allocated
210   //
211   Byte  = StartByte;
212   Bit   = StartBit;
213 
214   for (Count = 0; Count < Units; Count++) {
215     ASSERT (!USB_HC_BIT_IS_SET (Block->Bits[Byte], Bit));
216 
217     Block->Bits[Byte] = (UINT8) (Block->Bits[Byte] | USB_HC_BIT (Bit));
218     NEXT_BIT (Byte, Bit);
219   }
220 
221   return Block->BufHost + (StartByte * 8 + StartBit) * USBHC_MEM_UNIT;
222 }
223 
224 /**
225   Calculate the corresponding pci bus address according to the Mem parameter.
226 
227   @param  Pool           The memory pool of the host controller.
228   @param  Mem            The pointer to host memory.
229   @param  Size           The size of the memory region.
230 
231   @return the pci memory address
232 **/
233 EFI_PHYSICAL_ADDRESS
UsbHcGetPciAddressForHostMem(IN USBHC_MEM_POOL * Pool,IN VOID * Mem,IN UINTN Size)234 UsbHcGetPciAddressForHostMem (
235   IN USBHC_MEM_POOL       *Pool,
236   IN VOID                 *Mem,
237   IN UINTN                Size
238   )
239 {
240   USBHC_MEM_BLOCK         *Head;
241   USBHC_MEM_BLOCK         *Block;
242   UINTN                   AllocSize;
243   EFI_PHYSICAL_ADDRESS    PhyAddr;
244   UINTN                   Offset;
245 
246   Head      = Pool->Head;
247   AllocSize = USBHC_MEM_ROUND (Size);
248 
249   if (Mem == NULL) {
250     return 0;
251   }
252 
253   for (Block = Head; Block != NULL; Block = Block->Next) {
254     //
255     // scan the memory block list for the memory block that
256     // completely contains the allocated memory.
257     //
258     if ((Block->BufHost <= (UINT8 *) Mem) && (((UINT8 *) Mem + AllocSize) <= (Block->BufHost + Block->BufLen))) {
259       break;
260     }
261   }
262 
263   ASSERT ((Block != NULL));
264   //
265   // calculate the pci memory address for host memory address.
266   //
267   Offset = (UINT8 *)Mem - Block->BufHost;
268   PhyAddr = (EFI_PHYSICAL_ADDRESS)(UINTN) (Block->Buf + Offset);
269   return PhyAddr;
270 }
271 
272 
273 /**
274   Insert the memory block to the pool's list of the blocks.
275 
276   @param  Head           The head of the memory pool's block list.
277   @param  Block          The memory block to insert.
278 
279 **/
280 VOID
UsbHcInsertMemBlockToPool(IN USBHC_MEM_BLOCK * Head,IN USBHC_MEM_BLOCK * Block)281 UsbHcInsertMemBlockToPool (
282   IN USBHC_MEM_BLOCK      *Head,
283   IN USBHC_MEM_BLOCK      *Block
284   )
285 {
286   ASSERT ((Head != NULL) && (Block != NULL));
287   Block->Next = Head->Next;
288   Head->Next  = Block;
289 }
290 
291 
292 /**
293   Is the memory block empty?
294 
295   @param  Block   The memory block to check.
296 
297   @retval TRUE    The memory block is empty.
298   @retval FALSE   The memory block isn't empty.
299 
300 **/
301 BOOLEAN
UsbHcIsMemBlockEmpty(IN USBHC_MEM_BLOCK * Block)302 UsbHcIsMemBlockEmpty (
303   IN USBHC_MEM_BLOCK     *Block
304   )
305 {
306   UINTN                   Index;
307 
308   for (Index = 0; Index < Block->BitsLen; Index++) {
309     if (Block->Bits[Index] != 0) {
310       return FALSE;
311     }
312   }
313 
314   return TRUE;
315 }
316 
317 
318 /**
319   Unlink the memory block from the pool's list.
320 
321   @param  Head           The block list head of the memory's pool.
322   @param  BlockToUnlink  The memory block to unlink.
323 
324 **/
325 VOID
UsbHcUnlinkMemBlock(IN USBHC_MEM_BLOCK * Head,IN USBHC_MEM_BLOCK * BlockToUnlink)326 UsbHcUnlinkMemBlock (
327   IN USBHC_MEM_BLOCK      *Head,
328   IN USBHC_MEM_BLOCK      *BlockToUnlink
329   )
330 {
331   USBHC_MEM_BLOCK         *Block;
332 
333   ASSERT ((Head != NULL) && (BlockToUnlink != NULL));
334 
335   for (Block = Head; Block != NULL; Block = Block->Next) {
336     if (Block->Next == BlockToUnlink) {
337       Block->Next         = BlockToUnlink->Next;
338       BlockToUnlink->Next = NULL;
339       break;
340     }
341   }
342 }
343 
344 
345 /**
346   Initialize the memory management pool for the host controller.
347 
348   @param  PciIo                The PciIo that can be used to access the host controller.
349   @param  Check4G              Whether the host controller requires allocated memory
350                                from one 4G address space.
351   @param  Which4G              The 4G memory area each memory allocated should be from.
352 
353   @retval EFI_SUCCESS          The memory pool is initialized.
354   @retval EFI_OUT_OF_RESOURCE  Fail to init the memory pool.
355 
356 **/
357 USBHC_MEM_POOL *
UsbHcInitMemPool(IN EFI_PCI_IO_PROTOCOL * PciIo,IN BOOLEAN Check4G,IN UINT32 Which4G)358 UsbHcInitMemPool (
359   IN EFI_PCI_IO_PROTOCOL  *PciIo,
360   IN BOOLEAN              Check4G,
361   IN UINT32               Which4G
362   )
363 {
364   USBHC_MEM_POOL          *Pool;
365 
366   Pool = AllocatePool (sizeof (USBHC_MEM_POOL));
367 
368   if (Pool == NULL) {
369     return Pool;
370   }
371 
372   Pool->PciIo   = PciIo;
373   Pool->Check4G = Check4G;
374   Pool->Which4G = Which4G;
375   Pool->Head    = UsbHcAllocMemBlock (Pool, USBHC_MEM_DEFAULT_PAGES);
376 
377   if (Pool->Head == NULL) {
378     gBS->FreePool (Pool);
379     Pool = NULL;
380   }
381 
382   return Pool;
383 }
384 
385 
386 /**
387   Release the memory management pool.
388 
389   @param  Pool              The USB memory pool to free.
390 
391   @retval EFI_SUCCESS       The memory pool is freed.
392   @retval EFI_DEVICE_ERROR  Failed to free the memory pool.
393 
394 **/
395 EFI_STATUS
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   gBS->FreePool (Pool);
416   return EFI_SUCCESS;
417 }
418 
419 
420 /**
421   Allocate some memory from the host controller's memory pool
422   which can be used to communicate with host controller.
423 
424   @param  Pool           The host controller's memory pool.
425   @param  Size           Size of the memory to allocate.
426 
427   @return The allocated memory or NULL.
428 
429 **/
430 VOID *
UsbHcAllocateMem(IN USBHC_MEM_POOL * Pool,IN UINTN Size)431 UsbHcAllocateMem (
432   IN  USBHC_MEM_POOL      *Pool,
433   IN  UINTN               Size
434   )
435 {
436   USBHC_MEM_BLOCK         *Head;
437   USBHC_MEM_BLOCK         *Block;
438   USBHC_MEM_BLOCK         *NewBlock;
439   VOID                    *Mem;
440   UINTN                   AllocSize;
441   UINTN                   Pages;
442 
443   Mem       = NULL;
444   AllocSize = USBHC_MEM_ROUND (Size);
445   Head      = Pool->Head;
446   ASSERT (Head != NULL);
447 
448   //
449   // First check whether current memory blocks can satisfy the allocation.
450   //
451   for (Block = Head; Block != NULL; Block = Block->Next) {
452     Mem = UsbHcAllocMemFromBlock (Block, AllocSize / USBHC_MEM_UNIT);
453 
454     if (Mem != NULL) {
455       ZeroMem (Mem, Size);
456       break;
457     }
458   }
459 
460   if (Mem != NULL) {
461     return Mem;
462   }
463 
464   //
465   // Create a new memory block if there is not enough memory
466   // in the pool. If the allocation size is larger than the
467   // default page number, just allocate a large enough memory
468   // block. Otherwise allocate default pages.
469   //
470   if (AllocSize > EFI_PAGES_TO_SIZE (USBHC_MEM_DEFAULT_PAGES)) {
471     Pages = EFI_SIZE_TO_PAGES (AllocSize) + 1;
472   } else {
473     Pages = USBHC_MEM_DEFAULT_PAGES;
474   }
475 
476   NewBlock = UsbHcAllocMemBlock (Pool, Pages);
477 
478   if (NewBlock == NULL) {
479     DEBUG ((EFI_D_ERROR, "UsbHcAllocateMem: failed to allocate block\n"));
480     return NULL;
481   }
482 
483   //
484   // Add the new memory block to the pool, then allocate memory from it
485   //
486   UsbHcInsertMemBlockToPool (Head, NewBlock);
487   Mem = UsbHcAllocMemFromBlock (NewBlock, AllocSize / USBHC_MEM_UNIT);
488 
489   if (Mem != NULL) {
490     ZeroMem (Mem, Size);
491   }
492 
493   return Mem;
494 }
495 
496 
497 /**
498   Free the allocated memory back to the memory pool.
499 
500   @param  Pool           The memory pool of the host controller.
501   @param  Mem            The memory to free.
502   @param  Size           The size of the memory to free.
503 
504 **/
505 VOID
UsbHcFreeMem(IN USBHC_MEM_POOL * Pool,IN VOID * Mem,IN UINTN Size)506 UsbHcFreeMem (
507   IN USBHC_MEM_POOL       *Pool,
508   IN VOID                 *Mem,
509   IN UINTN                Size
510   )
511 {
512   USBHC_MEM_BLOCK         *Head;
513   USBHC_MEM_BLOCK         *Block;
514   UINT8                   *ToFree;
515   UINTN                   AllocSize;
516   UINTN                   Byte;
517   UINTN                   Bit;
518   UINTN                   Count;
519 
520   Head      = Pool->Head;
521   AllocSize = USBHC_MEM_ROUND (Size);
522   ToFree    = (UINT8 *) Mem;
523 
524   for (Block = Head; Block != NULL; Block = Block->Next) {
525     //
526     // scan the memory block list for the memory block that
527     // completely contains the memory to free.
528     //
529     if ((Block->BufHost <= ToFree) && ((ToFree + AllocSize) <= (Block->BufHost + Block->BufLen))) {
530       //
531       // compute the start byte and bit in the bit array
532       //
533       Byte  = ((ToFree - Block->BufHost) / USBHC_MEM_UNIT) / 8;
534       Bit   = ((ToFree - Block->BufHost) / USBHC_MEM_UNIT) % 8;
535 
536       //
537       // reset associated bits in bit array
538       //
539       for (Count = 0; Count < (AllocSize / USBHC_MEM_UNIT); Count++) {
540         ASSERT (USB_HC_BIT_IS_SET (Block->Bits[Byte], Bit));
541 
542         Block->Bits[Byte] = (UINT8) (Block->Bits[Byte] ^ USB_HC_BIT (Bit));
543         NEXT_BIT (Byte, Bit);
544       }
545 
546       break;
547     }
548   }
549 
550   //
551   // If Block == NULL, it means that the current memory isn't
552   // in the host controller's pool. This is critical because
553   // the caller has passed in a wrong memory point
554   //
555   ASSERT (Block != NULL);
556 
557   //
558   // Release the current memory block if it is empty and not the head
559   //
560   if ((Block != Head) && UsbHcIsMemBlockEmpty (Block)) {
561     UsbHcUnlinkMemBlock (Head, Block);
562     UsbHcFreeMemBlock (Pool, Block);
563   }
564 
565   return ;
566 }
567