1 /** @file
2
3 Copyright (c) 2016, 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 "EmmcBlockIoPei.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 EMMC_PEIM_MEM_BLOCK *
EmmcPeimAllocMemBlock(IN UINTN Pages)27 EmmcPeimAllocMemBlock (
28 IN UINTN Pages
29 )
30 {
31 EMMC_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(EMMC_PEIM_MEM_BLOCK), &TempPtr);
40 if (EFI_ERROR (Status)) {
41 return NULL;
42 }
43
44 ZeroMem ((VOID*)(UINTN)TempPtr, sizeof(EMMC_PEIM_MEM_BLOCK));
45
46 //
47 // each bit in the bit array represents EMMC_PEIM_MEM_UNIT
48 // bytes of memory in the memory block.
49 //
50 ASSERT (EMMC_PEIM_MEM_UNIT * 8 <= EFI_PAGE_SIZE);
51
52 Block = (EMMC_PEIM_MEM_BLOCK*)(UINTN)TempPtr;
53 Block->BufLen = EFI_PAGES_TO_SIZE (Pages);
54 Block->BitsLen = Block->BufLen / (EMMC_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
EmmcPeimFreeMemBlock(IN EMMC_PEIM_MEM_POOL * Pool,IN EMMC_PEIM_MEM_BLOCK * Block)90 EmmcPeimFreeMemBlock (
91 IN EMMC_PEIM_MEM_POOL *Pool,
92 IN EMMC_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 *
EmmcPeimAllocMemFromBlock(IN EMMC_PEIM_MEM_BLOCK * Block,IN UINTN Units)109 EmmcPeimAllocMemFromBlock (
110 IN EMMC_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 (!EMMC_PEIM_MEM_BIT_IS_SET (Block->Bits[Byte], Bit)) {
134 Available++;
135
136 if (Available >= Units) {
137 break;
138 }
139
140 EMMC_PEIM_NEXT_BIT (Byte, Bit);
141
142 } else {
143 EMMC_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 (!EMMC_PEIM_MEM_BIT_IS_SET (Block->Bits[Byte], Bit));
163
164 Block->Bits[Byte] = (UINT8) (Block->Bits[Byte] | (UINT8) EMMC_PEIM_MEM_BIT (Bit));
165 EMMC_PEIM_NEXT_BIT (Byte, Bit);
166 }
167
168 return Block->Buf + (StartByte * 8 + StartBit) * EMMC_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
EmmcPeimInsertMemBlockToPool(IN EMMC_PEIM_MEM_BLOCK * Head,IN EMMC_PEIM_MEM_BLOCK * Block)179 EmmcPeimInsertMemBlockToPool (
180 IN EMMC_PEIM_MEM_BLOCK *Head,
181 IN EMMC_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
EmmcPeimIsMemBlockEmpty(IN EMMC_PEIM_MEM_BLOCK * Block)199 EmmcPeimIsMemBlockEmpty (
200 IN EMMC_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
EmmcPeimUnlinkMemBlock(IN EMMC_PEIM_MEM_BLOCK * Head,IN EMMC_PEIM_MEM_BLOCK * BlockToUnlink)223 EmmcPeimUnlinkMemBlock (
224 IN EMMC_PEIM_MEM_BLOCK *Head,
225 IN EMMC_PEIM_MEM_BLOCK *BlockToUnlink
226 )
227 {
228 EMMC_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 Emmc 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
EmmcPeimInitMemPool(IN EMMC_PEIM_HC_PRIVATE_DATA * Private)251 EmmcPeimInitMemPool (
252 IN EMMC_PEIM_HC_PRIVATE_DATA *Private
253 )
254 {
255 EMMC_PEIM_MEM_POOL *Pool;
256 EFI_STATUS Status;
257 VOID *TempPtr;
258
259 TempPtr = NULL;
260 Pool = NULL;
261
262 Status = PeiServicesAllocatePool (sizeof (EMMC_PEIM_MEM_POOL), &TempPtr);
263 if (EFI_ERROR (Status)) {
264 return EFI_OUT_OF_RESOURCES;
265 }
266
267 ZeroMem ((VOID*)(UINTN)TempPtr, sizeof (EMMC_PEIM_MEM_POOL));
268
269 Pool = (EMMC_PEIM_MEM_POOL *)((UINTN)TempPtr);
270
271 Pool->Head = EmmcPeimAllocMemBlock (EMMC_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
EmmcPeimFreeMemPool(IN EMMC_PEIM_MEM_POOL * Pool)291 EmmcPeimFreeMemPool (
292 IN EMMC_PEIM_MEM_POOL *Pool
293 )
294 {
295 EMMC_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 // EmmcPeimUnlinkMemBlock 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 EmmcPeimFreeMemBlock (Pool, Block);
306 }
307
308 EmmcPeimFreeMemBlock (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 *
EmmcPeimAllocateMem(IN EMMC_PEIM_MEM_POOL * Pool,IN UINTN Size)324 EmmcPeimAllocateMem (
325 IN EMMC_PEIM_MEM_POOL *Pool,
326 IN UINTN Size
327 )
328 {
329 EMMC_PEIM_MEM_BLOCK *Head;
330 EMMC_PEIM_MEM_BLOCK *Block;
331 EMMC_PEIM_MEM_BLOCK *NewBlock;
332 VOID *Mem;
333 UINTN AllocSize;
334 UINTN Pages;
335
336 Mem = NULL;
337 AllocSize = EMMC_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 = EmmcPeimAllocMemFromBlock (Block, AllocSize / EMMC_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 (EMMC_PEIM_MEM_DEFAULT_PAGES)) {
364 Pages = EFI_SIZE_TO_PAGES (AllocSize) + 1;
365 } else {
366 Pages = EMMC_PEIM_MEM_DEFAULT_PAGES;
367 }
368
369 NewBlock = EmmcPeimAllocMemBlock (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 EmmcPeimInsertMemBlockToPool (Head, NewBlock);
378 Mem = EmmcPeimAllocMemFromBlock (NewBlock, AllocSize / EMMC_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
EmmcPeimFreeMem(IN EMMC_PEIM_MEM_POOL * Pool,IN VOID * Mem,IN UINTN Size)396 EmmcPeimFreeMem (
397 IN EMMC_PEIM_MEM_POOL *Pool,
398 IN VOID *Mem,
399 IN UINTN Size
400 )
401 {
402 EMMC_PEIM_MEM_BLOCK *Head;
403 EMMC_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 = EMMC_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) / EMMC_PEIM_MEM_UNIT) / 8;
424 Bit = ((ToFree - Block->Buf) / EMMC_PEIM_MEM_UNIT) % 8;
425
426 //
427 // reset associated bits in bit array
428 //
429 for (Count = 0; Count < (AllocSize / EMMC_PEIM_MEM_UNIT); Count++) {
430 ASSERT (EMMC_PEIM_MEM_BIT_IS_SET (Block->Bits[Byte], Bit));
431
432 Block->Bits[Byte] = (UINT8) (Block->Bits[Byte] ^ EMMC_PEIM_MEM_BIT (Bit));
433 EMMC_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) && EmmcPeimIsMemBlockEmpty (Block)) {
451 EmmcPeimFreeMemBlock (Pool, Block);
452 }
453
454 return ;
455 }
456