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 * 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 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 * 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 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 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 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 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 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 * 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 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