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