]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Bus/Sd/SdBlockIoPei/SdBlockIoPei.c
MdeModulePkg: Replace BSD License with BSD+Patent License
[mirror_edk2.git] / MdeModulePkg / Bus / Sd / SdBlockIoPei / SdBlockIoPei.c
1 /** @file
2
3 Copyright (c) 2015 - 2017, Intel Corporation. All rights reserved.<BR>
4 SPDX-License-Identifier: BSD-2-Clause-Patent
5
6 **/
7
8 #include "SdBlockIoPei.h"
9
10 //
11 // Template for SD HC Slot Data.
12 //
13 SD_PEIM_HC_SLOT gSdHcSlotTemplate = {
14 SD_PEIM_SLOT_SIG, // Signature
15 { // Media
16 MSG_SD_DP,
17 FALSE,
18 TRUE,
19 FALSE,
20 0x200,
21 0
22 },
23 0, // SdHcBase
24 { // Capability
25 0,
26 },
27 { // Csd
28 0,
29 },
30 TRUE, // SectorAddressing
31 NULL // Private
32 };
33
34 //
35 // Template for SD HC Private Data.
36 //
37 SD_PEIM_HC_PRIVATE_DATA gSdHcPrivateTemplate = {
38 SD_PEIM_SIG, // Signature
39 NULL, // Pool
40 { // BlkIoPpi
41 SdBlockIoPeimGetDeviceNo,
42 SdBlockIoPeimGetMediaInfo,
43 SdBlockIoPeimReadBlocks
44 },
45 { // BlkIo2Ppi
46 EFI_PEI_RECOVERY_BLOCK_IO2_PPI_REVISION,
47 SdBlockIoPeimGetDeviceNo2,
48 SdBlockIoPeimGetMediaInfo2,
49 SdBlockIoPeimReadBlocks2
50 },
51 { // BlkIoPpiList
52 EFI_PEI_PPI_DESCRIPTOR_PPI,
53 &gEfiPeiVirtualBlockIoPpiGuid,
54 NULL
55 },
56 { // BlkIo2PpiList
57 EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST,
58 &gEfiPeiVirtualBlockIo2PpiGuid,
59 NULL
60 },
61 {
62 (EFI_PEI_PPI_DESCRIPTOR_NOTIFY_CALLBACK | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST),
63 &gEfiEndOfPeiSignalPpiGuid,
64 SdBlockIoPeimEndOfPei
65 },
66 { // Slot
67 {
68 0,
69 },
70 {
71 0,
72 },
73 {
74 0,
75 },
76 {
77 0,
78 },
79 {
80 0,
81 },
82 {
83 0,
84 }
85 },
86 0, // SlotNum
87 0 // TotalBlkIoDevices
88 };
89 /**
90 Gets the count of block I/O devices that one specific block driver detects.
91
92 This function is used for getting the count of block I/O devices that one
93 specific block driver detects. To the PEI ATAPI driver, it returns the number
94 of all the detected ATAPI devices it detects during the enumeration process.
95 To the PEI legacy floppy driver, it returns the number of all the legacy
96 devices it finds during its enumeration process. If no device is detected,
97 then the function will return zero.
98
99 @param[in] PeiServices General-purpose services that are available
100 to every PEIM.
101 @param[in] This Indicates the EFI_PEI_RECOVERY_BLOCK_IO_PPI
102 instance.
103 @param[out] NumberBlockDevices The number of block I/O devices discovered.
104
105 @retval EFI_SUCCESS The operation performed successfully.
106
107 **/
108 EFI_STATUS
109 EFIAPI
110 SdBlockIoPeimGetDeviceNo (
111 IN EFI_PEI_SERVICES **PeiServices,
112 IN EFI_PEI_RECOVERY_BLOCK_IO_PPI *This,
113 OUT UINTN *NumberBlockDevices
114 )
115 {
116 SD_PEIM_HC_PRIVATE_DATA *Private;
117
118 Private = GET_SD_PEIM_HC_PRIVATE_DATA_FROM_THIS (This);
119 *NumberBlockDevices = Private->TotalBlkIoDevices;
120 return EFI_SUCCESS;
121 }
122
123 /**
124 Gets a block device's media information.
125
126 This function will provide the caller with the specified block device's media
127 information. If the media changes, calling this function will update the media
128 information accordingly.
129
130 @param[in] PeiServices General-purpose services that are available to every
131 PEIM
132 @param[in] This Indicates the EFI_PEI_RECOVERY_BLOCK_IO_PPI instance.
133 @param[in] DeviceIndex Specifies the block device to which the function wants
134 to talk. Because the driver that implements Block I/O
135 PPIs will manage multiple block devices, the PPIs that
136 want to talk to a single device must specify the
137 device index that was assigned during the enumeration
138 process. This index is a number from one to
139 NumberBlockDevices.
140 @param[out] MediaInfo The media information of the specified block media.
141 The caller is responsible for the ownership of this
142 data structure.
143
144 @par Note:
145 The MediaInfo structure describes an enumeration of possible block device
146 types. This enumeration exists because no device paths are actually passed
147 across interfaces that describe the type or class of hardware that is publishing
148 the block I/O interface. This enumeration will allow for policy decisions
149 in the Recovery PEIM, such as "Try to recover from legacy floppy first,
150 LS-120 second, CD-ROM third." If there are multiple partitions abstracted
151 by a given device type, they should be reported in ascending order; this
152 order also applies to nested partitions, such as legacy MBR, where the
153 outermost partitions would have precedence in the reporting order. The
154 same logic applies to systems such as IDE that have precedence relationships
155 like "Master/Slave" or "Primary/Secondary". The master device should be
156 reported first, the slave second.
157
158 @retval EFI_SUCCESS Media information about the specified block device
159 was obtained successfully.
160 @retval EFI_DEVICE_ERROR Cannot get the media information due to a hardware
161 error.
162
163 **/
164 EFI_STATUS
165 EFIAPI
166 SdBlockIoPeimGetMediaInfo (
167 IN EFI_PEI_SERVICES **PeiServices,
168 IN EFI_PEI_RECOVERY_BLOCK_IO_PPI *This,
169 IN UINTN DeviceIndex,
170 OUT EFI_PEI_BLOCK_IO_MEDIA *MediaInfo
171 )
172 {
173 SD_PEIM_HC_PRIVATE_DATA *Private;
174
175 Private = GET_SD_PEIM_HC_PRIVATE_DATA_FROM_THIS (This);
176
177 if ((DeviceIndex == 0) || (DeviceIndex > Private->TotalBlkIoDevices)) {
178 return EFI_INVALID_PARAMETER;
179 }
180
181 MediaInfo->DeviceType = SD;
182 MediaInfo->MediaPresent = TRUE;
183 MediaInfo->LastBlock = (UINTN)Private->Slot[DeviceIndex - 1].Media.LastBlock;
184 MediaInfo->BlockSize = Private->Slot[DeviceIndex - 1].Media.BlockSize;
185
186 return EFI_SUCCESS;
187 }
188
189 /**
190 Reads the requested number of blocks from the specified block device.
191
192 The function reads the requested number of blocks from the device. All the
193 blocks are read, or an error is returned. If there is no media in the device,
194 the function returns EFI_NO_MEDIA.
195
196 @param[in] PeiServices General-purpose services that are available to
197 every PEIM.
198 @param[in] This Indicates the EFI_PEI_RECOVERY_BLOCK_IO_PPI instance.
199 @param[in] DeviceIndex Specifies the block device to which the function wants
200 to talk. Because the driver that implements Block I/O
201 PPIs will manage multiple block devices, PPIs that
202 want to talk to a single device must specify the device
203 index that was assigned during the enumeration process.
204 This index is a number from one to NumberBlockDevices.
205 @param[in] StartLBA The starting logical block address (LBA) to read from
206 on the device
207 @param[in] BufferSize The size of the Buffer in bytes. This number must be
208 a multiple of the intrinsic block size of the device.
209 @param[out] Buffer A pointer to the destination buffer for the data.
210 The caller is responsible for the ownership of the
211 buffer.
212
213 @retval EFI_SUCCESS The data was read correctly from the device.
214 @retval EFI_DEVICE_ERROR The device reported an error while attempting
215 to perform the read operation.
216 @retval EFI_INVALID_PARAMETER The read request contains LBAs that are not
217 valid, or the buffer is not properly aligned.
218 @retval EFI_NO_MEDIA There is no media in the device.
219 @retval EFI_BAD_BUFFER_SIZE The BufferSize parameter is not a multiple of
220 the intrinsic block size of the device.
221
222 **/
223 EFI_STATUS
224 EFIAPI
225 SdBlockIoPeimReadBlocks (
226 IN EFI_PEI_SERVICES **PeiServices,
227 IN EFI_PEI_RECOVERY_BLOCK_IO_PPI *This,
228 IN UINTN DeviceIndex,
229 IN EFI_PEI_LBA StartLBA,
230 IN UINTN BufferSize,
231 OUT VOID *Buffer
232 )
233 {
234 EFI_STATUS Status;
235 UINT32 BlockSize;
236 UINTN NumberOfBlocks;
237 SD_PEIM_HC_PRIVATE_DATA *Private;
238 UINTN Remaining;
239 UINT32 MaxBlock;
240
241 Status = EFI_SUCCESS;
242 Private = GET_SD_PEIM_HC_PRIVATE_DATA_FROM_THIS (This);
243
244 //
245 // Check parameters
246 //
247 if (Buffer == NULL) {
248 return EFI_INVALID_PARAMETER;
249 }
250
251 if (BufferSize == 0) {
252 return EFI_SUCCESS;
253 }
254
255 if ((DeviceIndex == 0) || (DeviceIndex > Private->TotalBlkIoDevices)) {
256 return EFI_INVALID_PARAMETER;
257 }
258
259 BlockSize = Private->Slot[DeviceIndex - 1].Media.BlockSize;
260 if (BufferSize % BlockSize != 0) {
261 return EFI_BAD_BUFFER_SIZE;
262 }
263
264 if (StartLBA > Private->Slot[DeviceIndex - 1].Media.LastBlock) {
265 return EFI_INVALID_PARAMETER;
266 }
267
268 NumberOfBlocks = BufferSize / BlockSize;
269
270 //
271 // Start to execute data transfer. The max block number in single cmd is 65535 blocks.
272 //
273 Remaining = NumberOfBlocks;
274 MaxBlock = 0xFFFF;
275
276 while (Remaining > 0) {
277 if (Remaining <= MaxBlock) {
278 NumberOfBlocks = Remaining;
279 } else {
280 NumberOfBlocks = MaxBlock;
281 }
282
283 BufferSize = NumberOfBlocks * BlockSize;
284 if (NumberOfBlocks != 1) {
285 Status = SdPeimRwMultiBlocks (&Private->Slot[DeviceIndex - 1], StartLBA, BlockSize, Buffer, BufferSize, TRUE);
286 } else {
287 Status = SdPeimRwSingleBlock (&Private->Slot[DeviceIndex - 1], StartLBA, BlockSize, Buffer, BufferSize, TRUE);
288 }
289 if (EFI_ERROR (Status)) {
290 return Status;
291 }
292
293 StartLBA += NumberOfBlocks;
294 Buffer = (UINT8*)Buffer + BufferSize;
295 Remaining -= NumberOfBlocks;
296 }
297 return Status;
298 }
299
300 /**
301 Gets the count of block I/O devices that one specific block driver detects.
302
303 This function is used for getting the count of block I/O devices that one
304 specific block driver detects. To the PEI ATAPI driver, it returns the number
305 of all the detected ATAPI devices it detects during the enumeration process.
306 To the PEI legacy floppy driver, it returns the number of all the legacy
307 devices it finds during its enumeration process. If no device is detected,
308 then the function will return zero.
309
310 @param[in] PeiServices General-purpose services that are available
311 to every PEIM.
312 @param[in] This Indicates the EFI_PEI_RECOVERY_BLOCK_IO2_PPI
313 instance.
314 @param[out] NumberBlockDevices The number of block I/O devices discovered.
315
316 @retval EFI_SUCCESS The operation performed successfully.
317
318 **/
319 EFI_STATUS
320 EFIAPI
321 SdBlockIoPeimGetDeviceNo2 (
322 IN EFI_PEI_SERVICES **PeiServices,
323 IN EFI_PEI_RECOVERY_BLOCK_IO2_PPI *This,
324 OUT UINTN *NumberBlockDevices
325 )
326 {
327 SD_PEIM_HC_PRIVATE_DATA *Private;
328
329 Private = GET_SD_PEIM_HC_PRIVATE_DATA_FROM_THIS2 (This);
330 *NumberBlockDevices = Private->TotalBlkIoDevices;
331
332 return EFI_SUCCESS;
333 }
334
335 /**
336 Gets a block device's media information.
337
338 This function will provide the caller with the specified block device's media
339 information. If the media changes, calling this function will update the media
340 information accordingly.
341
342 @param[in] PeiServices General-purpose services that are available to every
343 PEIM
344 @param[in] This Indicates the EFI_PEI_RECOVERY_BLOCK_IO2_PPI instance.
345 @param[in] DeviceIndex Specifies the block device to which the function wants
346 to talk. Because the driver that implements Block I/O
347 PPIs will manage multiple block devices, the PPIs that
348 want to talk to a single device must specify the
349 device index that was assigned during the enumeration
350 process. This index is a number from one to
351 NumberBlockDevices.
352 @param[out] MediaInfo The media information of the specified block media.
353 The caller is responsible for the ownership of this
354 data structure.
355
356 @par Note:
357 The MediaInfo structure describes an enumeration of possible block device
358 types. This enumeration exists because no device paths are actually passed
359 across interfaces that describe the type or class of hardware that is publishing
360 the block I/O interface. This enumeration will allow for policy decisions
361 in the Recovery PEIM, such as "Try to recover from legacy floppy first,
362 LS-120 second, CD-ROM third." If there are multiple partitions abstracted
363 by a given device type, they should be reported in ascending order; this
364 order also applies to nested partitions, such as legacy MBR, where the
365 outermost partitions would have precedence in the reporting order. The
366 same logic applies to systems such as IDE that have precedence relationships
367 like "Master/Slave" or "Primary/Secondary". The master device should be
368 reported first, the slave second.
369
370 @retval EFI_SUCCESS Media information about the specified block device
371 was obtained successfully.
372 @retval EFI_DEVICE_ERROR Cannot get the media information due to a hardware
373 error.
374
375 **/
376 EFI_STATUS
377 EFIAPI
378 SdBlockIoPeimGetMediaInfo2 (
379 IN EFI_PEI_SERVICES **PeiServices,
380 IN EFI_PEI_RECOVERY_BLOCK_IO2_PPI *This,
381 IN UINTN DeviceIndex,
382 OUT EFI_PEI_BLOCK_IO2_MEDIA *MediaInfo
383 )
384 {
385 EFI_STATUS Status;
386 SD_PEIM_HC_PRIVATE_DATA *Private;
387 EFI_PEI_BLOCK_IO_MEDIA Media;
388
389 Private = GET_SD_PEIM_HC_PRIVATE_DATA_FROM_THIS2 (This);
390
391 Status = SdBlockIoPeimGetMediaInfo (
392 PeiServices,
393 &Private->BlkIoPpi,
394 DeviceIndex,
395 &Media
396 );
397 if (EFI_ERROR (Status)) {
398 return Status;
399 }
400
401 CopyMem (MediaInfo, &(Private->Slot[DeviceIndex - 1].Media), sizeof (EFI_PEI_BLOCK_IO2_MEDIA));
402 return EFI_SUCCESS;
403 }
404
405 /**
406 Reads the requested number of blocks from the specified block device.
407
408 The function reads the requested number of blocks from the device. All the
409 blocks are read, or an error is returned. If there is no media in the device,
410 the function returns EFI_NO_MEDIA.
411
412 @param[in] PeiServices General-purpose services that are available to
413 every PEIM.
414 @param[in] This Indicates the EFI_PEI_RECOVERY_BLOCK_IO2_PPI instance.
415 @param[in] DeviceIndex Specifies the block device to which the function wants
416 to talk. Because the driver that implements Block I/O
417 PPIs will manage multiple block devices, PPIs that
418 want to talk to a single device must specify the device
419 index that was assigned during the enumeration process.
420 This index is a number from one to NumberBlockDevices.
421 @param[in] StartLBA The starting logical block address (LBA) to read from
422 on the device
423 @param[in] BufferSize The size of the Buffer in bytes. This number must be
424 a multiple of the intrinsic block size of the device.
425 @param[out] Buffer A pointer to the destination buffer for the data.
426 The caller is responsible for the ownership of the
427 buffer.
428
429 @retval EFI_SUCCESS The data was read correctly from the device.
430 @retval EFI_DEVICE_ERROR The device reported an error while attempting
431 to perform the read operation.
432 @retval EFI_INVALID_PARAMETER The read request contains LBAs that are not
433 valid, or the buffer is not properly aligned.
434 @retval EFI_NO_MEDIA There is no media in the device.
435 @retval EFI_BAD_BUFFER_SIZE The BufferSize parameter is not a multiple of
436 the intrinsic block size of the device.
437
438 **/
439 EFI_STATUS
440 EFIAPI
441 SdBlockIoPeimReadBlocks2 (
442 IN EFI_PEI_SERVICES **PeiServices,
443 IN EFI_PEI_RECOVERY_BLOCK_IO2_PPI *This,
444 IN UINTN DeviceIndex,
445 IN EFI_PEI_LBA StartLBA,
446 IN UINTN BufferSize,
447 OUT VOID *Buffer
448 )
449 {
450 EFI_STATUS Status;
451 SD_PEIM_HC_PRIVATE_DATA *Private;
452
453 Status = EFI_SUCCESS;
454 Private = GET_SD_PEIM_HC_PRIVATE_DATA_FROM_THIS2 (This);
455
456 Status = SdBlockIoPeimReadBlocks (
457 PeiServices,
458 &Private->BlkIoPpi,
459 DeviceIndex,
460 StartLBA,
461 BufferSize,
462 Buffer
463 );
464 return Status;
465 }
466
467 /**
468 One notified function to cleanup the allocated DMA buffers at the end of PEI.
469
470 @param[in] PeiServices Pointer to PEI Services Table.
471 @param[in] NotifyDescriptor Pointer to the descriptor for the Notification
472 event that caused this function to execute.
473 @param[in] Ppi Pointer to the PPI data associated with this function.
474
475 @retval EFI_SUCCESS The function completes successfully
476
477 **/
478 EFI_STATUS
479 EFIAPI
480 SdBlockIoPeimEndOfPei (
481 IN EFI_PEI_SERVICES **PeiServices,
482 IN EFI_PEI_NOTIFY_DESCRIPTOR *NotifyDescriptor,
483 IN VOID *Ppi
484 )
485 {
486 SD_PEIM_HC_PRIVATE_DATA *Private;
487
488 Private = GET_SD_PEIM_HC_PRIVATE_DATA_FROM_THIS_NOTIFY (NotifyDescriptor);
489
490 if ((Private->Pool != NULL) && (Private->Pool->Head != NULL)) {
491 SdPeimFreeMemPool (Private->Pool);
492 }
493
494 return EFI_SUCCESS;
495 }
496
497 /**
498 The user code starts with this function.
499
500 @param FileHandle Handle of the file being invoked.
501 @param PeiServices Describes the list of possible PEI Services.
502
503 @retval EFI_SUCCESS The driver is successfully initialized.
504 @retval Others Can't initialize the driver.
505
506 **/
507 EFI_STATUS
508 EFIAPI
509 InitializeSdBlockIoPeim (
510 IN EFI_PEI_FILE_HANDLE FileHandle,
511 IN CONST EFI_PEI_SERVICES **PeiServices
512 )
513 {
514 EFI_STATUS Status;
515 SD_PEIM_HC_PRIVATE_DATA *Private;
516 EDKII_SD_MMC_HOST_CONTROLLER_PPI *SdMmcHcPpi;
517 UINT32 Index;
518 UINTN *MmioBase;
519 UINT8 BarNum;
520 UINT8 SlotNum;
521 UINT8 Controller;
522 UINT64 Capacity;
523 SD_HC_SLOT_CAP Capability;
524 SD_PEIM_HC_SLOT *Slot;
525 SD_CSD *Csd;
526 SD_CSD2 *Csd2;
527 UINT32 CSize;
528 UINT32 CSizeMul;
529 UINT32 ReadBlLen;
530
531 //
532 // Shadow this PEIM to run from memory
533 //
534 if (!EFI_ERROR (PeiServicesRegisterForShadow (FileHandle))) {
535 return EFI_SUCCESS;
536 }
537
538 //
539 // locate Sd host controller PPI
540 //
541 Status = PeiServicesLocatePpi (
542 &gEdkiiPeiSdMmcHostControllerPpiGuid,
543 0,
544 NULL,
545 (VOID **) &SdMmcHcPpi
546 );
547 if (EFI_ERROR (Status)) {
548 return EFI_DEVICE_ERROR;
549 }
550
551 IoMmuInit ();
552
553 Controller = 0;
554 MmioBase = NULL;
555 while (TRUE) {
556 Status = SdMmcHcPpi->GetSdMmcHcMmioBar (SdMmcHcPpi, Controller, &MmioBase, &BarNum);
557 //
558 // When status is error, meant no controller is found
559 //
560 if (EFI_ERROR (Status)) {
561 break;
562 }
563
564 if (BarNum == 0) {
565 Controller++;
566 continue;
567 }
568
569 Private = AllocateCopyPool (sizeof (SD_PEIM_HC_PRIVATE_DATA), &gSdHcPrivateTemplate);
570 if (Private == NULL) {
571 Status = EFI_OUT_OF_RESOURCES;
572 break;
573 }
574 Private->BlkIoPpiList.Ppi = (VOID*)&Private->BlkIoPpi;
575 Private->BlkIo2PpiList.Ppi = (VOID*)&Private->BlkIo2Ppi;
576 //
577 // Initialize the memory pool which will be used in all transactions.
578 //
579 Status = SdPeimInitMemPool (Private);
580 if (EFI_ERROR (Status)) {
581 Status = EFI_OUT_OF_RESOURCES;
582 break;
583 }
584
585 for (Index = 0; Index < BarNum; Index++) {
586 Status = SdPeimHcGetCapability (MmioBase[Index], &Capability);
587 if (EFI_ERROR (Status)) {
588 continue;
589 }
590 if (Capability.SlotType != 0x1) {
591 DEBUG ((EFI_D_INFO, "The slot at 0x%x is not embedded slot type\n", MmioBase[Index]));
592 Status = EFI_UNSUPPORTED;
593 continue;
594 }
595
596 Status = SdPeimHcReset (MmioBase[Index]);
597 if (EFI_ERROR (Status)) {
598 continue;
599 }
600 Status = SdPeimHcCardDetect (MmioBase[Index]);
601 if (EFI_ERROR (Status)) {
602 continue;
603 }
604 Status = SdPeimHcInitHost (MmioBase[Index]);
605 if (EFI_ERROR (Status)) {
606 continue;
607 }
608
609 SlotNum = Private->SlotNum;
610 Slot = &Private->Slot[SlotNum];
611 CopyMem (Slot, &gSdHcSlotTemplate, sizeof (SD_PEIM_HC_SLOT));
612 Slot->Private = Private;
613 Slot->SdHcBase = MmioBase[Index];
614 CopyMem (&Slot->Capability, &Capability, sizeof (Capability));
615
616 Status = SdPeimIdentification (Slot);
617 if (EFI_ERROR (Status)) {
618 continue;
619 }
620
621 Csd = &Slot->Csd;
622 if (Csd->CsdStructure == 0) {
623 Slot->SectorAddressing = FALSE;
624 CSize = (Csd->CSizeHigh << 2 | Csd->CSizeLow) + 1;
625 CSizeMul = (1 << (Csd->CSizeMul + 2));
626 ReadBlLen = (1 << (Csd->ReadBlLen));
627 Capacity = MultU64x32 (MultU64x32 ((UINT64)CSize, CSizeMul), ReadBlLen);
628 } else {
629 Slot->SectorAddressing = TRUE;
630 Csd2 = (SD_CSD2*)(VOID*)Csd;
631 CSize = (Csd2->CSizeHigh << 16 | Csd2->CSizeLow) + 1;
632 Capacity = MultU64x32 ((UINT64)CSize, SIZE_512KB);
633 }
634
635 Slot->Media.LastBlock = DivU64x32 (Capacity, Slot->Media.BlockSize) - 1;
636
637 Private->TotalBlkIoDevices++;
638 Private->SlotNum++;
639 }
640
641 Controller++;
642 if (!EFI_ERROR (Status)) {
643 PeiServicesInstallPpi (&Private->BlkIoPpiList);
644 PeiServicesNotifyPpi (&Private->EndOfPeiNotifyList);
645 } else {
646 if (Private->Pool->Head != NULL) {
647 SdPeimFreeMemPool (Private->Pool);
648 }
649 }
650 }
651
652 return EFI_SUCCESS;
653 }