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