ee0b4e6869f4ff5d02cdf5070792f92419cf2fcc
[mirror_edk2.git] / MdeModulePkg / Universal / Disk / CdExpressPei / PeiCdExpress.c
1 /** @file
2 Source file for CD recovery PEIM
3
4 Copyright (c) 2006 - 2010, Intel Corporation. All rights reserved.<BR>
5
6 This program and the accompanying materials
7 are licensed and made available under the terms and conditions
8 of the BSD License which accompanies this distribution. The
9 full text of the license may be found at
10 http://opensource.org/licenses/bsd-license.php
11
12 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
13 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
14
15 **/
16
17 #include "PeiCdExpress.h"
18
19 PEI_CD_EXPRESS_PRIVATE_DATA *mPrivateData = NULL;
20
21 /**
22 Installs the Device Recovery Module PPI, Initialize BlockIo Ppi
23 installation notification
24
25 @param FileHandle The file handle of the image.
26 @param PeiServices General purpose services available to every PEIM.
27
28 @retval EFI_SUCCESS The function completed successfully.
29 @retval EFI_OUT_OF_RESOURCES There is not enough system memory.
30
31 **/
32 EFI_STATUS
33 EFIAPI
34 CdExpressPeimEntry (
35 IN EFI_PEI_FILE_HANDLE FileHandle,
36 IN CONST EFI_PEI_SERVICES **PeiServices
37 )
38 {
39 EFI_STATUS Status;
40 PEI_CD_EXPRESS_PRIVATE_DATA *PrivateData;
41
42 if (!EFI_ERROR (PeiServicesRegisterForShadow (FileHandle))) {
43 return EFI_SUCCESS;
44 }
45
46 PrivateData = AllocatePages (EFI_SIZE_TO_PAGES (sizeof (*PrivateData)));
47 if (PrivateData == NULL) {
48 return EFI_OUT_OF_RESOURCES;
49 }
50
51 //
52 // Initialize Private Data (to zero, as is required by subsequent operations)
53 //
54 ZeroMem (PrivateData, sizeof (*PrivateData));
55 PrivateData->Signature = PEI_CD_EXPRESS_PRIVATE_DATA_SIGNATURE;
56
57 PrivateData->BlockBuffer = AllocatePages (EFI_SIZE_TO_PAGES (PEI_CD_BLOCK_SIZE));
58 if (PrivateData->BlockBuffer == NULL) {
59 return EFI_OUT_OF_RESOURCES;
60 }
61
62 PrivateData->CapsuleCount = 0;
63 Status = UpdateBlocksAndVolumes (PrivateData);
64
65 //
66 // Installs Ppi
67 //
68 PrivateData->DeviceRecoveryPpi.GetNumberRecoveryCapsules = GetNumberRecoveryCapsules;
69 PrivateData->DeviceRecoveryPpi.GetRecoveryCapsuleInfo = GetRecoveryCapsuleInfo;
70 PrivateData->DeviceRecoveryPpi.LoadRecoveryCapsule = LoadRecoveryCapsule;
71
72 PrivateData->PpiDescriptor.Flags = (EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST);
73 PrivateData->PpiDescriptor.Guid = &gEfiPeiDeviceRecoveryModulePpiGuid;
74 PrivateData->PpiDescriptor.Ppi = &PrivateData->DeviceRecoveryPpi;
75
76 Status = PeiServicesInstallPpi (&PrivateData->PpiDescriptor);
77 if (EFI_ERROR (Status)) {
78 return EFI_OUT_OF_RESOURCES;
79 }
80 //
81 // PrivateData is allocated now, set it to the module variable
82 //
83 mPrivateData = PrivateData;
84
85 //
86 // Installs Block Io Ppi notification function
87 //
88 PrivateData->NotifyDescriptor.Flags =
89 (
90 EFI_PEI_PPI_DESCRIPTOR_NOTIFY_CALLBACK |
91 EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST
92 );
93 PrivateData->NotifyDescriptor.Guid = &gEfiPeiVirtualBlockIoPpiGuid;
94 PrivateData->NotifyDescriptor.Notify = BlockIoNotifyEntry;
95 return PeiServicesNotifyPpi (&PrivateData->NotifyDescriptor);
96
97 }
98
99 /**
100 BlockIo installation notification function.
101
102 This function finds out all the current Block IO PPIs in the system and add them
103 into private data.
104
105 @param PeiServices Indirect reference to the PEI Services Table.
106 @param NotifyDescriptor Address of the notification descriptor data structure.
107 @param Ppi Address of the PPI that was installed.
108
109 @retval EFI_SUCCESS The function completes successfully.
110
111 **/
112 EFI_STATUS
113 EFIAPI
114 BlockIoNotifyEntry (
115 IN EFI_PEI_SERVICES **PeiServices,
116 IN EFI_PEI_NOTIFY_DESCRIPTOR *NotifyDescriptor,
117 IN VOID *Ppi
118 )
119 {
120 UpdateBlocksAndVolumes (mPrivateData);
121
122 return EFI_SUCCESS;
123 }
124
125 /**
126 Finds out all the current Block IO PPIs in the system and add them into private data.
127
128 @param PrivateData The private data structure that contains recovery module information.
129
130 @retval EFI_SUCCESS The blocks and volumes are updated successfully.
131
132 **/
133 EFI_STATUS
134 UpdateBlocksAndVolumes (
135 IN OUT PEI_CD_EXPRESS_PRIVATE_DATA *PrivateData
136 )
137 {
138 EFI_STATUS Status;
139 EFI_PEI_PPI_DESCRIPTOR *TempPpiDescriptor;
140 UINTN BlockIoPpiInstance;
141 EFI_PEI_RECOVERY_BLOCK_IO_PPI *BlockIoPpi;
142 UINTN NumberBlockDevices;
143 UINTN IndexBlockDevice;
144 EFI_PEI_BLOCK_IO_MEDIA Media;
145 EFI_PEI_SERVICES **PeiServices;
146
147 IndexBlockDevice = 0;
148 //
149 // Find out all Block Io Ppi instances within the system
150 // Assuming all device Block Io Peims are dispatched already
151 //
152 for (BlockIoPpiInstance = 0; BlockIoPpiInstance < PEI_CD_EXPRESS_MAX_BLOCK_IO_PPI; BlockIoPpiInstance++) {
153 Status = PeiServicesLocatePpi (
154 &gEfiPeiVirtualBlockIoPpiGuid,
155 BlockIoPpiInstance,
156 &TempPpiDescriptor,
157 (VOID **) &BlockIoPpi
158 );
159 if (EFI_ERROR (Status)) {
160 //
161 // Done with all Block Io Ppis
162 //
163 break;
164 }
165
166 PeiServices = (EFI_PEI_SERVICES **) GetPeiServicesTablePointer ();
167 Status = BlockIoPpi->GetNumberOfBlockDevices (
168 PeiServices,
169 BlockIoPpi,
170 &NumberBlockDevices
171 );
172 if (EFI_ERROR (Status) || (NumberBlockDevices == 0)) {
173 continue;
174 }
175 //
176 // Just retrieve the first block, should emulate all blocks.
177 //
178 for (IndexBlockDevice = 1; IndexBlockDevice <= NumberBlockDevices && PrivateData->CapsuleCount < PEI_CD_EXPRESS_MAX_CAPSULE_NUMBER; IndexBlockDevice ++) {
179 Status = BlockIoPpi->GetBlockDeviceMediaInfo (
180 PeiServices,
181 BlockIoPpi,
182 IndexBlockDevice,
183 &Media
184 );
185 if (EFI_ERROR (Status) ||
186 !Media.MediaPresent ||
187 ((Media.DeviceType != IdeCDROM) && (Media.DeviceType != UsbMassStorage)) ||
188 (Media.BlockSize != PEI_CD_BLOCK_SIZE)
189 ) {
190 continue;
191 }
192
193 DEBUG ((EFI_D_INFO, "PeiCdExpress DeviceType is %d\n", Media.DeviceType));
194 DEBUG ((EFI_D_INFO, "PeiCdExpress MediaPresent is %d\n", Media.MediaPresent));
195 DEBUG ((EFI_D_INFO, "PeiCdExpress BlockSize is 0x%x\n", Media.BlockSize));
196 DEBUG ((EFI_D_INFO, "PeiCdExpress Status is %d\n", Status));
197
198 DEBUG ((EFI_D_INFO, "IndexBlockDevice is %d\n", IndexBlockDevice));
199 PrivateData->CapsuleData[PrivateData->CapsuleCount].IndexBlock = IndexBlockDevice;
200 PrivateData->CapsuleData[PrivateData->CapsuleCount].BlockIo = BlockIoPpi;
201 Status = FindRecoveryCapsules (PrivateData);
202 DEBUG ((EFI_D_INFO, "Status is %d\n", Status));
203
204 if (EFI_ERROR (Status)) {
205 continue;
206 }
207
208 PrivateData->CapsuleCount++;
209 }
210
211 }
212
213 return EFI_SUCCESS;
214 }
215
216 /**
217 Finds out the recovery capsule in the current volume.
218
219 @param PrivateData The private data structure that contains recovery module information.
220
221 @retval EFI_SUCCESS The recovery capsule is successfully found in the volume.
222 @retval EFI_NOT_FOUND The recovery capsule is not found in the volume.
223
224 **/
225 EFI_STATUS
226 EFIAPI
227 FindRecoveryCapsules (
228 IN OUT PEI_CD_EXPRESS_PRIVATE_DATA *PrivateData
229 )
230 {
231 EFI_STATUS Status;
232 UINTN Lba;
233 EFI_PEI_RECOVERY_BLOCK_IO_PPI *BlockIoPpi;
234 UINTN BufferSize;
235 UINT8 *Buffer;
236 UINT8 Type;
237 UINT8 *StandardID;
238 UINT32 RootDirLBA;
239 PEI_CD_EXPRESS_DIR_FILE_RECORD *RoorDirRecord;
240 UINTN VolumeSpaceSize;
241 BOOLEAN StartOfVolume;
242 UINTN OriginalLBA;
243 UINTN IndexBlockDevice;
244
245 Buffer = PrivateData->BlockBuffer;
246 BufferSize = PEI_CD_BLOCK_SIZE;
247
248 Lba = 16;
249 //
250 // The volume descriptor starts on Lba 16
251 //
252 IndexBlockDevice = PrivateData->CapsuleData[PrivateData->CapsuleCount].IndexBlock;
253 BlockIoPpi = PrivateData->CapsuleData[PrivateData->CapsuleCount].BlockIo;
254
255 VolumeSpaceSize = 0;
256 StartOfVolume = TRUE;
257 OriginalLBA = 16;
258
259 while (TRUE) {
260 SetMem (Buffer, BufferSize, 0);
261 Status = BlockIoPpi->ReadBlocks (
262 PrivateData->PeiServices,
263 BlockIoPpi,
264 IndexBlockDevice,
265 Lba,
266 BufferSize,
267 Buffer
268 );
269 if (EFI_ERROR (Status)) {
270 return Status;
271 }
272
273 StandardID = (UINT8 *) (Buffer + PEI_CD_EXPRESS_STANDARD_ID_OFFSET);
274 if (!StringCmp (StandardID, (UINT8 *) PEI_CD_STANDARD_ID, PEI_CD_EXPRESS_STANDARD_ID_SIZE, TRUE)) {
275 break;
276 }
277
278 if (StartOfVolume) {
279 OriginalLBA = Lba;
280 StartOfVolume = FALSE;
281 }
282
283 Type = *(UINT8 *) (Buffer + PEI_CD_EXPRESS_VOLUME_TYPE_OFFSET);
284 if (Type == PEI_CD_EXPRESS_VOLUME_TYPE_TERMINATOR) {
285 if (VolumeSpaceSize == 0) {
286 break;
287 } else {
288 Lba = (OriginalLBA + VolumeSpaceSize);
289 VolumeSpaceSize = 0;
290 StartOfVolume = TRUE;
291 continue;
292 }
293 }
294
295 if (Type != PEI_CD_EXPRESS_VOLUME_TYPE_PRIMARY) {
296 Lba++;
297 continue;
298 }
299
300 VolumeSpaceSize = *(UINT32 *) (Buffer + PEI_CD_EXPRESS_VOLUME_SPACE_OFFSET);
301
302 RoorDirRecord = (PEI_CD_EXPRESS_DIR_FILE_RECORD *) (Buffer + PEI_CD_EXPRESS_ROOT_DIR_RECORD_OFFSET);
303 RootDirLBA = RoorDirRecord->LocationOfExtent[0];
304
305 Status = RetrieveCapsuleFileFromRoot (PrivateData, BlockIoPpi, IndexBlockDevice, RootDirLBA);
306 if (!EFI_ERROR (Status)) {
307 //
308 // Just look for the first primary descriptor
309 //
310 return EFI_SUCCESS;
311 }
312
313 Lba++;
314 }
315
316 return EFI_NOT_FOUND;
317 }
318
319 /**
320 Retrieves the recovery capsule in root directory of the current volume.
321
322 @param PrivateData The private data structure that contains recovery module information.
323 @param BlockIoPpi The Block IO PPI used to access the volume.
324 @param IndexBlockDevice The index of current block device.
325 @param Lba The starting logic block address to retrieve capsule.
326
327 @retval EFI_SUCCESS The recovery capsule is successfully found in the volume.
328 @retval EFI_NOT_FOUND The recovery capsule is not found in the volume.
329 @retval Others
330
331 **/
332 EFI_STATUS
333 EFIAPI
334 RetrieveCapsuleFileFromRoot (
335 IN OUT PEI_CD_EXPRESS_PRIVATE_DATA *PrivateData,
336 IN EFI_PEI_RECOVERY_BLOCK_IO_PPI *BlockIoPpi,
337 IN UINTN IndexBlockDevice,
338 IN UINT32 Lba
339 )
340 {
341 EFI_STATUS Status;
342 UINTN BufferSize;
343 UINT8 *Buffer;
344 PEI_CD_EXPRESS_DIR_FILE_RECORD *FileRecord;
345 UINTN Index;
346
347 Buffer = PrivateData->BlockBuffer;
348 BufferSize = PEI_CD_BLOCK_SIZE;
349
350 SetMem (Buffer, BufferSize, 0);
351
352 Status = BlockIoPpi->ReadBlocks (
353 PrivateData->PeiServices,
354 BlockIoPpi,
355 IndexBlockDevice,
356 Lba,
357 BufferSize,
358 Buffer
359 );
360 if (EFI_ERROR (Status)) {
361 return Status;
362 }
363
364 while (1) {
365 FileRecord = (PEI_CD_EXPRESS_DIR_FILE_RECORD *) Buffer;
366
367 if (FileRecord->Length == 0) {
368 break;
369 }
370 //
371 // Not intend to check other flag now
372 //
373 if ((FileRecord->Flag & PEI_CD_EXPRESS_DIR_FILE_REC_FLAG_ISDIR) != 0) {
374 Buffer += FileRecord->Length;
375 continue;
376 }
377
378 for (Index = 0; Index < FileRecord->FileIDLength; Index++) {
379 if (FileRecord->FileID[Index] == ';') {
380 break;
381 }
382 }
383
384 if (Index != (sizeof (PEI_RECOVERY_FILE_NAME) - 1)) {
385 Buffer += FileRecord->Length;
386 continue;
387 }
388
389 if (!StringCmp (FileRecord->FileID, (UINT8 *) PEI_RECOVERY_FILE_NAME, sizeof (PEI_RECOVERY_FILE_NAME) - 1, FALSE)) {
390 Buffer += FileRecord->Length;
391 continue;
392 }
393
394 PrivateData->CapsuleData[PrivateData->CapsuleCount].CapsuleStartLBA = FileRecord->LocationOfExtent[0];
395 PrivateData->CapsuleData[PrivateData->CapsuleCount].CapsuleSize =
396 (
397 FileRecord->DataLength[0] /
398 PEI_CD_BLOCK_SIZE +
399 1
400 ) *
401 PEI_CD_BLOCK_SIZE;
402
403 return EFI_SUCCESS;
404 }
405
406 return EFI_NOT_FOUND;
407 }
408
409 /**
410 Returns the number of DXE capsules residing on the device.
411
412 This function searches for DXE capsules from the associated device and returns
413 the number and maximum size in bytes of the capsules discovered. Entry 1 is
414 assumed to be the highest load priority and entry N is assumed to be the lowest
415 priority.
416
417 @param[in] PeiServices General-purpose services that are available
418 to every PEIM
419 @param[in] This Indicates the EFI_PEI_DEVICE_RECOVERY_MODULE_PPI
420 instance.
421 @param[out] NumberRecoveryCapsules Pointer to a caller-allocated UINTN. On
422 output, *NumberRecoveryCapsules contains
423 the number of recovery capsule images
424 available for retrieval from this PEIM
425 instance.
426
427 @retval EFI_SUCCESS One or more capsules were discovered.
428 @retval EFI_DEVICE_ERROR A device error occurred.
429 @retval EFI_NOT_FOUND A recovery DXE capsule cannot be found.
430
431 **/
432 EFI_STATUS
433 EFIAPI
434 GetNumberRecoveryCapsules (
435 IN EFI_PEI_SERVICES **PeiServices,
436 IN EFI_PEI_DEVICE_RECOVERY_MODULE_PPI *This,
437 OUT UINTN *NumberRecoveryCapsules
438 )
439 {
440 PEI_CD_EXPRESS_PRIVATE_DATA *PrivateData;
441
442 PrivateData = PEI_CD_EXPRESS_PRIVATE_DATA_FROM_THIS (This);
443 UpdateBlocksAndVolumes (PrivateData);
444 *NumberRecoveryCapsules = PrivateData->CapsuleCount;
445
446 if (*NumberRecoveryCapsules == 0) {
447 return EFI_NOT_FOUND;
448 }
449
450 return EFI_SUCCESS;
451 }
452
453 /**
454 Returns the size and type of the requested recovery capsule.
455
456 This function gets the size and type of the capsule specified by CapsuleInstance.
457
458 @param[in] PeiServices General-purpose services that are available to every PEIM
459 @param[in] This Indicates the EFI_PEI_DEVICE_RECOVERY_MODULE_PPI
460 instance.
461 @param[in] CapsuleInstance Specifies for which capsule instance to retrieve
462 the information. This parameter must be between
463 one and the value returned by GetNumberRecoveryCapsules()
464 in NumberRecoveryCapsules.
465 @param[out] Size A pointer to a caller-allocated UINTN in which
466 the size of the requested recovery module is
467 returned.
468 @param[out] CapsuleType A pointer to a caller-allocated EFI_GUID in which
469 the type of the requested recovery capsule is
470 returned. The semantic meaning of the value
471 returned is defined by the implementation.
472
473 @retval EFI_SUCCESS One or more capsules were discovered.
474 @retval EFI_DEVICE_ERROR A device error occurred.
475 @retval EFI_NOT_FOUND A recovery DXE capsule cannot be found.
476
477 **/
478 EFI_STATUS
479 EFIAPI
480 GetRecoveryCapsuleInfo (
481 IN EFI_PEI_SERVICES **PeiServices,
482 IN EFI_PEI_DEVICE_RECOVERY_MODULE_PPI *This,
483 IN UINTN CapsuleInstance,
484 OUT UINTN *Size,
485 OUT EFI_GUID *CapsuleType
486 )
487 {
488 PEI_CD_EXPRESS_PRIVATE_DATA *PrivateData;
489 UINTN NumberRecoveryCapsules;
490 EFI_STATUS Status;
491
492 Status = GetNumberRecoveryCapsules (PeiServices, This, &NumberRecoveryCapsules);
493
494 if (EFI_ERROR (Status)) {
495 return Status;
496 }
497
498 if (FeaturePcdGet (PcdFrameworkCompatibilitySupport)) {
499 CapsuleInstance = CapsuleInstance + 1;
500 }
501
502 if ((CapsuleInstance == 0) || (CapsuleInstance > NumberRecoveryCapsules)) {
503 return EFI_NOT_FOUND;
504 }
505
506 PrivateData = PEI_CD_EXPRESS_PRIVATE_DATA_FROM_THIS (This);
507
508 *Size = PrivateData->CapsuleData[CapsuleInstance - 1].CapsuleSize;
509 CopyMem (
510 CapsuleType,
511 &gRecoveryOnDataCdGuid,
512 sizeof (EFI_GUID)
513 );
514
515 return EFI_SUCCESS;
516 }
517
518 /**
519 Loads a DXE capsule from some media into memory.
520
521 This function, by whatever mechanism, retrieves a DXE capsule from some device
522 and loads it into memory. Note that the published interface is device neutral.
523
524 @param[in] PeiServices General-purpose services that are available
525 to every PEIM
526 @param[in] This Indicates the EFI_PEI_DEVICE_RECOVERY_MODULE_PPI
527 instance.
528 @param[in] CapsuleInstance Specifies which capsule instance to retrieve.
529 @param[out] Buffer Specifies a caller-allocated buffer in which
530 the requested recovery capsule will be returned.
531
532 @retval EFI_SUCCESS The capsule was loaded correctly.
533 @retval EFI_DEVICE_ERROR A device error occurred.
534 @retval EFI_NOT_FOUND A requested recovery DXE capsule cannot be found.
535
536 **/
537 EFI_STATUS
538 EFIAPI
539 LoadRecoveryCapsule (
540 IN EFI_PEI_SERVICES **PeiServices,
541 IN EFI_PEI_DEVICE_RECOVERY_MODULE_PPI *This,
542 IN UINTN CapsuleInstance,
543 OUT VOID *Buffer
544 )
545 {
546 EFI_STATUS Status;
547 PEI_CD_EXPRESS_PRIVATE_DATA *PrivateData;
548 EFI_PEI_RECOVERY_BLOCK_IO_PPI *BlockIoPpi;
549 UINTN NumberRecoveryCapsules;
550
551 Status = GetNumberRecoveryCapsules (PeiServices, This, &NumberRecoveryCapsules);
552
553 if (EFI_ERROR (Status)) {
554 return Status;
555 }
556
557 if (FeaturePcdGet (PcdFrameworkCompatibilitySupport)) {
558 CapsuleInstance = CapsuleInstance + 1;
559 }
560
561 if ((CapsuleInstance == 0) || (CapsuleInstance > NumberRecoveryCapsules)) {
562 return EFI_NOT_FOUND;
563 }
564
565 PrivateData = PEI_CD_EXPRESS_PRIVATE_DATA_FROM_THIS (This);
566 BlockIoPpi = PrivateData->CapsuleData[CapsuleInstance - 1].BlockIo;
567
568 Status = BlockIoPpi->ReadBlocks (
569 PrivateData->PeiServices,
570 BlockIoPpi,
571 PrivateData->CapsuleData[CapsuleInstance - 1].IndexBlock,
572 PrivateData->CapsuleData[CapsuleInstance - 1].CapsuleStartLBA,
573 PrivateData->CapsuleData[CapsuleInstance - 1].CapsuleSize,
574 Buffer
575 );
576 return Status;
577 }
578
579 /**
580 This function compares two ASCII strings in case sensitive/insensitive way.
581
582 @param Source1 The first string.
583 @param Source2 The second string.
584 @param Size The maximum comparison length.
585 @param CaseSensitive Flag to indicate whether the comparison is case sensitive.
586
587 @retval TRUE The two strings are the same.
588 @retval FALSE The two string are not the same.
589
590 **/
591 BOOLEAN
592 StringCmp (
593 IN UINT8 *Source1,
594 IN UINT8 *Source2,
595 IN UINTN Size,
596 IN BOOLEAN CaseSensitive
597 )
598 {
599 UINTN Index;
600 UINT8 Dif;
601
602 for (Index = 0; Index < Size; Index++) {
603 if (Source1[Index] == Source2[Index]) {
604 continue;
605 }
606
607 if (!CaseSensitive) {
608 Dif = (UINT8) ((Source1[Index] > Source2[Index]) ? (Source1[Index] - Source2[Index]) : (Source2[Index] - Source1[Index]));
609 if (Dif == ('a' - 'A')) {
610 continue;
611 }
612 }
613
614 return FALSE;
615 }
616
617 return TRUE;
618 }