1 /** @file
2
3 Copyright (c) 2015 - 2017, Intel Corporation. All rights reserved.<BR>
4
5 SPDX-License-Identifier: BSD-2-Clause-Patent
6
7 **/
8
9 #include "SdBlockIoPei.h"
10
11 /**
12 Allocate a block of memory to be used by the buffer pool.
13
14 @param Pages How many pages to allocate.
15
16 @return The allocated memory block or NULL if failed.
17
18 **/
19 SD_PEIM_MEM_BLOCK *
SdPeimAllocMemBlock(IN UINTN Pages)20 SdPeimAllocMemBlock (
21 IN UINTN Pages
22 )
23 {
24 SD_PEIM_MEM_BLOCK *Block;
25 VOID *BufHost;
26 VOID *Mapping;
27 EFI_PHYSICAL_ADDRESS MappedAddr;
28 EFI_STATUS Status;
29 VOID *TempPtr;
30
31 TempPtr = NULL;
32 Block = NULL;
33
34 Status = PeiServicesAllocatePool (sizeof(SD_PEIM_MEM_BLOCK), &TempPtr);
35 if (EFI_ERROR (Status)) {
36 return NULL;
37 }
38
39 ZeroMem ((VOID*)(UINTN)TempPtr, sizeof(SD_PEIM_MEM_BLOCK));
40
41 //
42 // each bit in the bit array represents SD_PEIM_MEM_UNIT
43 // bytes of memory in the memory block.
44 //
45 ASSERT (SD_PEIM_MEM_UNIT * 8 <= EFI_PAGE_SIZE);
46
47 Block = (SD_PEIM_MEM_BLOCK*)(UINTN)TempPtr;
48 Block->BufLen = EFI_PAGES_TO_SIZE (Pages);
49 Block->BitsLen = Block->BufLen / (SD_PEIM_MEM_UNIT * 8);
50
51 Status = PeiServicesAllocatePool (Block->BitsLen, &TempPtr);
52 if (EFI_ERROR (Status)) {
53 return NULL;
54 }
55
56 ZeroMem ((VOID*)(UINTN)TempPtr, Block->BitsLen);
57
58 Block->Bits = (UINT8*)(UINTN)TempPtr;
59
60 Status = IoMmuAllocateBuffer (
61 Pages,
62 &BufHost,
63 &MappedAddr,
64 &Mapping
65 );
66 if (EFI_ERROR (Status)) {
67 return NULL;
68 }
69
70 ZeroMem ((VOID*)(UINTN)BufHost, EFI_PAGES_TO_SIZE (Pages));
71
72 Block->BufHost = (UINT8 *) (UINTN) BufHost;
73 Block->Buf = (UINT8 *) (UINTN) MappedAddr;
74 Block->Mapping = Mapping;
75 Block->Next = NULL;
76
77 return Block;
78 }
79
80 /**
81 Free the memory block from the memory pool.
82
83 @param Pool The memory pool to free the block from.
84 @param Block The memory block to free.
85
86 **/
87 VOID
SdPeimFreeMemBlock(IN SD_PEIM_MEM_POOL * Pool,IN SD_PEIM_MEM_BLOCK * Block)88 SdPeimFreeMemBlock (
89 IN SD_PEIM_MEM_POOL *Pool,
90 IN SD_PEIM_MEM_BLOCK *Block
91 )
92 {
93 ASSERT ((Pool != NULL) && (Block != NULL));
94
95 IoMmuFreeBuffer (EFI_SIZE_TO_PAGES (Block->BufLen), Block->BufHost, Block->Mapping);
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 *
SdPeimAllocMemFromBlock(IN SD_PEIM_MEM_BLOCK * Block,IN UINTN Units)109 SdPeimAllocMemFromBlock (
110 IN SD_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 (!SD_PEIM_MEM_BIT_IS_SET (Block->Bits[Byte], Bit)) {
134 Available++;
135
136 if (Available >= Units) {
137 break;
138 }
139
140 SD_PEIM_NEXT_BIT (Byte, Bit);
141
142 } else {
143 SD_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 (!SD_PEIM_MEM_BIT_IS_SET (Block->Bits[Byte], Bit));
163
164 Block->Bits[Byte] = (UINT8) (Block->Bits[Byte] | (UINT8) SD_PEIM_MEM_BIT (Bit));
165 SD_PEIM_NEXT_BIT (Byte, Bit);
166 }
167
168 return Block->Buf + (StartByte * 8 + StartBit) * SD_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
SdPeimInsertMemBlockToPool(IN SD_PEIM_MEM_BLOCK * Head,IN SD_PEIM_MEM_BLOCK * Block)179 SdPeimInsertMemBlockToPool (
180 IN SD_PEIM_MEM_BLOCK *Head,
181 IN SD_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
SdPeimIsMemBlockEmpty(IN SD_PEIM_MEM_BLOCK * Block)199 SdPeimIsMemBlockEmpty (
200 IN SD_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
217 /**
218 Initialize the memory management pool for the host controller.
219
220 @param Private The Sd Peim driver private data.
221
222 @retval EFI_SUCCESS The memory pool is initialized.
223 @retval Others Fail to init the memory pool.
224
225 **/
226 EFI_STATUS
SdPeimInitMemPool(IN SD_PEIM_HC_PRIVATE_DATA * Private)227 SdPeimInitMemPool (
228 IN SD_PEIM_HC_PRIVATE_DATA *Private
229 )
230 {
231 SD_PEIM_MEM_POOL *Pool;
232 EFI_STATUS Status;
233 VOID *TempPtr;
234
235 TempPtr = NULL;
236 Pool = NULL;
237
238 Status = PeiServicesAllocatePool (sizeof (SD_PEIM_MEM_POOL), &TempPtr);
239 if (EFI_ERROR (Status)) {
240 return EFI_OUT_OF_RESOURCES;
241 }
242
243 ZeroMem ((VOID*)(UINTN)TempPtr, sizeof (SD_PEIM_MEM_POOL));
244
245 Pool = (SD_PEIM_MEM_POOL *)((UINTN)TempPtr);
246
247 Pool->Head = SdPeimAllocMemBlock (SD_PEIM_MEM_DEFAULT_PAGES);
248
249 if (Pool->Head == NULL) {
250 return EFI_OUT_OF_RESOURCES;
251 }
252
253 Private->Pool = Pool;
254 return EFI_SUCCESS;
255 }
256
257 /**
258 Release the memory management pool.
259
260 @param Pool The memory pool to free.
261
262 @retval EFI_DEVICE_ERROR Fail to free the memory pool.
263 @retval EFI_SUCCESS The memory pool is freed.
264
265 **/
266 EFI_STATUS
SdPeimFreeMemPool(IN SD_PEIM_MEM_POOL * Pool)267 SdPeimFreeMemPool (
268 IN SD_PEIM_MEM_POOL *Pool
269 )
270 {
271 SD_PEIM_MEM_BLOCK *Block;
272
273 ASSERT (Pool->Head != NULL);
274
275 //
276 // Unlink all the memory blocks from the pool, then free them.
277 //
278 for (Block = Pool->Head->Next; Block != NULL; Block = Pool->Head->Next) {
279 SdPeimFreeMemBlock (Pool, Block);
280 }
281
282 SdPeimFreeMemBlock (Pool, Pool->Head);
283
284 return EFI_SUCCESS;
285 }
286
287 /**
288 Allocate some memory from the host controller's memory pool
289 which can be used to communicate with host controller.
290
291 @param Pool The host controller's memory pool.
292 @param Size Size of the memory to allocate.
293
294 @return The allocated memory or NULL.
295
296 **/
297 VOID *
SdPeimAllocateMem(IN SD_PEIM_MEM_POOL * Pool,IN UINTN Size)298 SdPeimAllocateMem (
299 IN SD_PEIM_MEM_POOL *Pool,
300 IN UINTN Size
301 )
302 {
303 SD_PEIM_MEM_BLOCK *Head;
304 SD_PEIM_MEM_BLOCK *Block;
305 SD_PEIM_MEM_BLOCK *NewBlock;
306 VOID *Mem;
307 UINTN AllocSize;
308 UINTN Pages;
309
310 Mem = NULL;
311 AllocSize = SD_PEIM_MEM_ROUND (Size);
312 Head = Pool->Head;
313 ASSERT (Head != NULL);
314
315 //
316 // First check whether current memory blocks can satisfy the allocation.
317 //
318 for (Block = Head; Block != NULL; Block = Block->Next) {
319 Mem = SdPeimAllocMemFromBlock (Block, AllocSize / SD_PEIM_MEM_UNIT);
320
321 if (Mem != NULL) {
322 ZeroMem (Mem, Size);
323 break;
324 }
325 }
326
327 if (Mem != NULL) {
328 return Mem;
329 }
330
331 //
332 // Create a new memory block if there is not enough memory
333 // in the pool. If the allocation size is larger than the
334 // default page number, just allocate a large enough memory
335 // block. Otherwise allocate default pages.
336 //
337 if (AllocSize > EFI_PAGES_TO_SIZE (SD_PEIM_MEM_DEFAULT_PAGES)) {
338 Pages = EFI_SIZE_TO_PAGES (AllocSize) + 1;
339 } else {
340 Pages = SD_PEIM_MEM_DEFAULT_PAGES;
341 }
342
343 NewBlock = SdPeimAllocMemBlock (Pages);
344 if (NewBlock == NULL) {
345 return NULL;
346 }
347
348 //
349 // Add the new memory block to the pool, then allocate memory from it
350 //
351 SdPeimInsertMemBlockToPool (Head, NewBlock);
352 Mem = SdPeimAllocMemFromBlock (NewBlock, AllocSize / SD_PEIM_MEM_UNIT);
353
354 if (Mem != NULL) {
355 ZeroMem (Mem, Size);
356 }
357
358 return Mem;
359 }
360
361 /**
362 Free the allocated memory back to the memory pool.
363
364 @param Pool The memory pool of the host controller.
365 @param Mem The memory to free.
366 @param Size The size of the memory to free.
367
368 **/
369 VOID
SdPeimFreeMem(IN SD_PEIM_MEM_POOL * Pool,IN VOID * Mem,IN UINTN Size)370 SdPeimFreeMem (
371 IN SD_PEIM_MEM_POOL *Pool,
372 IN VOID *Mem,
373 IN UINTN Size
374 )
375 {
376 SD_PEIM_MEM_BLOCK *Head;
377 SD_PEIM_MEM_BLOCK *Block;
378 UINT8 *ToFree;
379 UINTN AllocSize;
380 UINTN Byte;
381 UINTN Bit;
382 UINTN Count;
383
384 Head = Pool->Head;
385 AllocSize = SD_PEIM_MEM_ROUND (Size);
386 ToFree = (UINT8 *) Mem;
387
388 for (Block = Head; Block != NULL; Block = Block->Next) {
389 //
390 // scan the memory block list for the memory block that
391 // completely contains the memory to free.
392 //
393 if ((Block->Buf <= ToFree) && ((ToFree + AllocSize) <= (Block->Buf + Block->BufLen))) {
394 //
395 // compute the start byte and bit in the bit array
396 //
397 Byte = ((ToFree - Block->Buf) / SD_PEIM_MEM_UNIT) / 8;
398 Bit = ((ToFree - Block->Buf) / SD_PEIM_MEM_UNIT) % 8;
399
400 //
401 // reset associated bits in bit array
402 //
403 for (Count = 0; Count < (AllocSize / SD_PEIM_MEM_UNIT); Count++) {
404 ASSERT (SD_PEIM_MEM_BIT_IS_SET (Block->Bits[Byte], Bit));
405
406 Block->Bits[Byte] = (UINT8) (Block->Bits[Byte] ^ SD_PEIM_MEM_BIT (Bit));
407 SD_PEIM_NEXT_BIT (Byte, Bit);
408 }
409
410 break;
411 }
412 }
413
414 //
415 // If Block == NULL, it means that the current memory isn't
416 // in the host controller's pool. This is critical because
417 // the caller has passed in a wrong memory point
418 //
419 ASSERT (Block != NULL);
420
421 //
422 // Release the current memory block if it is empty and not the head
423 //
424 if ((Block != Head) && SdPeimIsMemBlockEmpty (Block)) {
425 SdPeimFreeMemBlock (Pool, Block);
426 }
427
428 return ;
429 }
430