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