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