]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Universal/CapsuleOnDiskLoadPei/CapsuleOnDiskLoadPei.c
MdeModulePkg: Add CapsuleOnDiskLoadPei PEIM.
[mirror_edk2.git] / MdeModulePkg / Universal / CapsuleOnDiskLoadPei / CapsuleOnDiskLoadPei.c
1 /** @file
2 Recovery module.
3
4 Caution: This module requires additional review when modified.
5 This module will have external input - Capsule-on-Disk Temp Relocation image.
6 This external input must be validated carefully to avoid security issue like
7 buffer overflow, integer overflow.
8
9 RetrieveRelocatedCapsule() will receive untrusted input and do basic validation.
10
11 Copyright (c) 2019, Intel Corporation. All rights reserved.<BR>
12 SPDX-License-Identifier: BSD-2-Clause-Patent
13
14 **/
15
16 //
17 // The package level header files this module uses
18 //
19 #include <Uefi.h>
20 #include <PiPei.h>
21
22 //
23 // The protocols, PPI and GUID defintions for this module
24 //
25 #include <Ppi/MasterBootMode.h>
26 #include <Ppi/FirmwareVolumeInfo.h>
27 #include <Ppi/ReadOnlyVariable2.h>
28 #include <Ppi/Capsule.h>
29 #include <Ppi/CapsuleOnDisk.h>
30 #include <Ppi/DeviceRecoveryModule.h>
31
32 #include <Guid/FirmwareFileSystem2.h>
33 //
34 // The Library classes this module consumes
35 //
36 #include <Library/DebugLib.h>
37 #include <Library/PeimEntryPoint.h>
38 #include <Library/PeiServicesLib.h>
39 #include <Library/HobLib.h>
40 #include <Library/BaseMemoryLib.h>
41 #include <Library/MemoryAllocationLib.h>
42 #include <Library/PcdLib.h>
43 #include <Library/CapsuleLib.h>
44 #include <Library/ReportStatusCodeLib.h>
45
46 /**
47 Loads a DXE capsule from some media into memory and updates the HOB table
48 with the DXE firmware volume information.
49
50 @param[in] PeiServices General-purpose services that are available to every PEIM.
51 @param[in] This Indicates the EFI_PEI_RECOVERY_MODULE_PPI instance.
52
53 @retval EFI_SUCCESS The capsule was loaded correctly.
54 @retval EFI_DEVICE_ERROR A device error occurred.
55 @retval EFI_NOT_FOUND A recovery DXE capsule cannot be found.
56
57 **/
58 EFI_STATUS
59 EFIAPI
60 LoadCapsuleOnDisk (
61 IN EFI_PEI_SERVICES **PeiServices,
62 IN EDKII_PEI_CAPSULE_ON_DISK_PPI *This
63 );
64
65 static EDKII_PEI_CAPSULE_ON_DISK_PPI mCapsuleOnDiskPpi = {
66 LoadCapsuleOnDisk
67 };
68
69 static EFI_PEI_PPI_DESCRIPTOR mCapsuleOnDiskPpiList = {
70 (EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST),
71 &gEdkiiPeiCapsuleOnDiskPpiGuid,
72 &mCapsuleOnDiskPpi
73 };
74
75 /**
76 Determine if capsule comes from memory by checking Capsule PPI.
77
78 @param[in] PeiServices General purpose services available to every PEIM.
79
80 @retval TRUE Capsule comes from memory.
81 @retval FALSE No capsule comes from memory.
82
83 **/
84 static
85 BOOLEAN
86 CheckCapsuleFromRam (
87 IN CONST EFI_PEI_SERVICES **PeiServices
88 )
89 {
90 EFI_STATUS Status;
91 PEI_CAPSULE_PPI *Capsule;
92
93 Status = PeiServicesLocatePpi (
94 &gEfiPeiCapsulePpiGuid,
95 0,
96 NULL,
97 (VOID **) &Capsule
98 );
99 if (!EFI_ERROR(Status)) {
100 Status = Capsule->CheckCapsuleUpdate ((EFI_PEI_SERVICES **)PeiServices);
101 if (!EFI_ERROR(Status)) {
102 return TRUE;
103 }
104 }
105
106 return FALSE;
107 }
108
109 /**
110 Determine if it is a Capsule On Disk mode.
111
112 @retval TRUE Capsule On Disk mode.
113 @retval FALSE Not capsule On Disk mode.
114
115 **/
116 static
117 BOOLEAN
118 IsCapsuleOnDiskMode (
119 VOID
120 )
121 {
122 EFI_STATUS Status;
123 UINTN Size;
124 EFI_PEI_READ_ONLY_VARIABLE2_PPI *PPIVariableServices;
125 BOOLEAN CodRelocInfo;
126
127 Status = PeiServicesLocatePpi (
128 &gEfiPeiReadOnlyVariable2PpiGuid,
129 0,
130 NULL,
131 (VOID **) &PPIVariableServices
132 );
133 ASSERT_EFI_ERROR (Status);
134
135 Size = sizeof (BOOLEAN);
136 Status = PPIVariableServices->GetVariable (
137 PPIVariableServices,
138 COD_RELOCATION_INFO_VAR_NAME,
139 &gEfiCapsuleVendorGuid,
140 NULL,
141 &Size,
142 &CodRelocInfo
143 );
144
145 if (EFI_ERROR (Status) || Size != sizeof(BOOLEAN) || !CodRelocInfo) {
146 DEBUG (( DEBUG_ERROR, "Error Get CodRelocationInfo variable %r!\n", Status));
147 return FALSE;
148 }
149
150 return TRUE;
151 }
152
153 /**
154 Gets capsule images from relocated capsule buffer.
155 Create Capsule hob for each Capsule.
156
157 Caution: This function may receive untrusted input.
158 Capsule-on-Disk Temp Relocation image is external input, so this function
159 will validate Capsule-on-Disk Temp Relocation image to make sure the content
160 is read within the buffer.
161
162 @param[in] RelocCapsuleBuf Buffer pointer to the relocated capsule.
163 @param[in] RelocCapsuleTotalSize Total size of the relocated capsule.
164
165 @retval EFI_SUCCESS Succeed to get capsules and create hob.
166 @retval Others Fail to get capsules and create hob.
167
168 **/
169 static
170 EFI_STATUS
171 RetrieveRelocatedCapsule (
172 IN UINT8 *RelocCapsuleBuf,
173 IN UINTN RelocCapsuleTotalSize
174 )
175 {
176 UINTN Index;
177 UINT8 *CapsuleDataBufEnd;
178 UINT8 *CapsulePtr;
179 UINT32 CapsuleSize;
180 UINT64 TotalImageSize;
181 UINTN CapsuleNum;
182
183 //
184 // Temp file contains at least 2 capsule (including 1 capsule name capsule) & 1 UINT64
185 //
186 if (RelocCapsuleTotalSize < sizeof(UINT64) + sizeof(EFI_CAPSULE_HEADER) * 2) {
187 return EFI_INVALID_PARAMETER;
188 }
189
190 CopyMem(&TotalImageSize, RelocCapsuleBuf, sizeof(UINT64));
191
192 DEBUG ((DEBUG_INFO, "ProcessRelocatedCapsule CapsuleBuf %x TotalCapSize %lx\n",
193 RelocCapsuleBuf, TotalImageSize));
194
195 RelocCapsuleBuf += sizeof(UINT64);
196
197 //
198 // TempCaspule file length check
199 //
200 if (MAX_ADDRESS - TotalImageSize <= sizeof(UINT64) ||
201 (UINT64)RelocCapsuleTotalSize != TotalImageSize + sizeof(UINT64) ||
202 (UINTN)(MAX_ADDRESS - (PHYSICAL_ADDRESS)(UINTN)RelocCapsuleBuf) <= TotalImageSize) {
203 return EFI_INVALID_PARAMETER;
204 }
205
206 CapsuleDataBufEnd = RelocCapsuleBuf + TotalImageSize;
207
208 //
209 // TempCapsule file integrity check over Capsule Header to ensure no data corruption in NV Var & Relocation storage
210 //
211 CapsulePtr = RelocCapsuleBuf;
212 CapsuleNum = 0;
213
214 while (CapsulePtr < CapsuleDataBufEnd) {
215 if ((CapsuleDataBufEnd - CapsulePtr) < sizeof(EFI_CAPSULE_HEADER) ||
216 ((EFI_CAPSULE_HEADER *)CapsulePtr)->CapsuleImageSize < sizeof(EFI_CAPSULE_HEADER) ||
217 (UINTN)(MAX_ADDRESS - (PHYSICAL_ADDRESS)(UINTN)CapsulePtr) < ((EFI_CAPSULE_HEADER *)CapsulePtr)->CapsuleImageSize
218 ) {
219 break;
220 }
221 CapsulePtr += ((EFI_CAPSULE_HEADER *)CapsulePtr)->CapsuleImageSize;
222 CapsuleNum ++;
223 }
224
225 if (CapsulePtr != CapsuleDataBufEnd) {
226 return EFI_INVALID_PARAMETER;
227 }
228
229 //
230 // Capsule count must be less than PcdCapsuleMax, avoid building too many CvHobs to occupy all the free space in HobList.
231 //
232 if (CapsuleNum > PcdGet16 (PcdCapsuleMax)) {
233 return EFI_INVALID_PARAMETER;
234 }
235
236 //
237 // Re-iterate the capsule buffer to create Capsule hob & Capsule Name Str Hob for each Capsule saved in relocated capsule file
238 //
239 CapsulePtr = RelocCapsuleBuf;
240 Index = 0;
241 while (CapsulePtr < CapsuleDataBufEnd) {
242 CapsuleSize = ((EFI_CAPSULE_HEADER *)CapsulePtr)->CapsuleImageSize;
243 BuildCvHob ((EFI_PHYSICAL_ADDRESS)(UINTN)CapsulePtr, CapsuleSize);
244
245 DEBUG((DEBUG_INFO, "Capsule saved in address %x size %x\n", CapsulePtr, CapsuleSize));
246
247 CapsulePtr += CapsuleSize;
248 Index++;
249 }
250
251 return EFI_SUCCESS;
252 }
253
254 /**
255 Recovery module entrypoint
256
257 @param[in] FileHandle Handle of the file being invoked.
258 @param[in] PeiServices Describes the list of possible PEI Services.
259
260 @return EFI_SUCCESS Recovery module is initialized.
261 **/
262 EFI_STATUS
263 EFIAPI
264 InitializeCapsuleOnDiskLoad (
265 IN EFI_PEI_FILE_HANDLE FileHandle,
266 IN CONST EFI_PEI_SERVICES **PeiServices
267 )
268 {
269 EFI_STATUS Status;
270 UINTN BootMode;
271 UINTN FileNameSize;
272
273 BootMode = GetBootModeHob();
274 ASSERT(BootMode == BOOT_ON_FLASH_UPDATE);
275
276 //
277 // If there are capsules provisioned in memory, quit.
278 // Only one capsule resource is accept, CapsuleOnRam's priority is higher than CapsuleOnDisk.
279 //
280 if (CheckCapsuleFromRam(PeiServices)) {
281 DEBUG((DEBUG_ERROR, "Capsule On Memory Detected! Quit.\n"));
282 return EFI_ABORTED;
283 }
284
285 DEBUG_CODE (
286 VOID *CapsuleOnDiskModePpi;
287
288 if (!IsCapsuleOnDiskMode()){
289 return EFI_NOT_FOUND;
290 }
291
292 //
293 // Check Capsule On Disk Relocation flag. If exists, load capsule & create Capsule Hob
294 //
295 Status = PeiServicesLocatePpi (
296 &gEdkiiPeiBootInCapsuleOnDiskModePpiGuid,
297 0,
298 NULL,
299 (VOID **)&CapsuleOnDiskModePpi
300 );
301 if (EFI_ERROR(Status)) {
302 DEBUG((DEBUG_ERROR, "Locate CapsuleOnDiskModePpi error %x\n", Status));
303 return Status;
304 }
305 );
306
307 Status = PeiServicesInstallPpi (&mCapsuleOnDiskPpiList);
308 ASSERT_EFI_ERROR (Status);
309
310 FileNameSize = PcdGetSize (PcdCoDRelocationFileName);
311 Status = PcdSetPtrS (PcdRecoveryFileName, &FileNameSize, (VOID *) PcdGetPtr(PcdCoDRelocationFileName));
312 ASSERT_EFI_ERROR (Status);
313
314 return Status;
315 }
316
317 /**
318 Loads a DXE capsule from some media into memory and updates the HOB table
319 with the DXE firmware volume information.
320
321 @param[in] PeiServices General-purpose services that are available to every PEIM.
322 @param[in] This Indicates the EFI_PEI_RECOVERY_MODULE_PPI instance.
323
324 @retval EFI_SUCCESS The capsule was loaded correctly.
325 @retval EFI_DEVICE_ERROR A device error occurred.
326 @retval EFI_NOT_FOUND A recovery DXE capsule cannot be found.
327
328 **/
329 EFI_STATUS
330 EFIAPI
331 LoadCapsuleOnDisk (
332 IN EFI_PEI_SERVICES **PeiServices,
333 IN EDKII_PEI_CAPSULE_ON_DISK_PPI *This
334 )
335 {
336 EFI_STATUS Status;
337 EFI_PEI_DEVICE_RECOVERY_MODULE_PPI *DeviceRecoveryPpi;
338 UINTN NumberRecoveryCapsules;
339 UINTN Instance;
340 UINTN CapsuleInstance;
341 UINTN CapsuleSize;
342 EFI_GUID CapsuleType;
343 VOID *CapsuleBuffer;
344
345 DEBUG ((DEBUG_INFO | DEBUG_LOAD, "Load Capsule On Disk Entry\n"));
346
347 for (Instance = 0; ; Instance++) {
348 Status = PeiServicesLocatePpi (
349 &gEfiPeiDeviceRecoveryModulePpiGuid,
350 Instance,
351 NULL,
352 (VOID **)&DeviceRecoveryPpi
353 );
354 DEBUG ((DEBUG_INFO, "LoadCapsuleOnDisk - LocateRecoveryPpi (%d) - %r\n", Instance, Status));
355 if (EFI_ERROR (Status)) {
356 if (Instance == 0) {
357 REPORT_STATUS_CODE (
358 EFI_ERROR_CODE | EFI_ERROR_MAJOR,
359 (EFI_SOFTWARE_PEI_MODULE | EFI_SW_PEI_EC_RECOVERY_PPI_NOT_FOUND)
360 );
361 }
362 break;
363 }
364 NumberRecoveryCapsules = 0;
365 Status = DeviceRecoveryPpi->GetNumberRecoveryCapsules (
366 (EFI_PEI_SERVICES **)PeiServices,
367 DeviceRecoveryPpi,
368 &NumberRecoveryCapsules
369 );
370 DEBUG ((DEBUG_INFO, "LoadCapsuleOnDisk - GetNumberRecoveryCapsules (%d) - %r\n", NumberRecoveryCapsules, Status));
371 if (EFI_ERROR (Status)) {
372 continue;
373 }
374
375 for (CapsuleInstance = 1; CapsuleInstance <= NumberRecoveryCapsules; CapsuleInstance++) {
376 CapsuleSize = 0;
377 Status = DeviceRecoveryPpi->GetRecoveryCapsuleInfo (
378 (EFI_PEI_SERVICES **)PeiServices,
379 DeviceRecoveryPpi,
380 CapsuleInstance,
381 &CapsuleSize,
382 &CapsuleType
383 );
384 DEBUG ((DEBUG_INFO, "LoadCapsuleOnDisk - GetRecoveryCapsuleInfo (%d - %x) - %r\n", CapsuleInstance, CapsuleSize, Status));
385 if (EFI_ERROR (Status)) {
386 break;
387 }
388
389 //
390 // Allocate the memory so that it gets preserved into DXE.
391 // Capsule is special because it may need to populate to system table
392 //
393 CapsuleBuffer = AllocateRuntimePages (EFI_SIZE_TO_PAGES (CapsuleSize));
394
395 if (CapsuleBuffer == NULL) {
396 DEBUG ((DEBUG_ERROR, "LoadCapsuleOnDisk - AllocateRuntimePages fail\n"));
397 continue;
398 }
399
400 Status = DeviceRecoveryPpi->LoadRecoveryCapsule (
401 (EFI_PEI_SERVICES **)PeiServices,
402 DeviceRecoveryPpi,
403 CapsuleInstance,
404 CapsuleBuffer
405 );
406 DEBUG ((DEBUG_INFO, "LoadCapsuleOnDisk - LoadRecoveryCapsule (%d) - %r\n", CapsuleInstance, Status));
407 if (EFI_ERROR (Status)) {
408 FreePages (CapsuleBuffer, EFI_SIZE_TO_PAGES(CapsuleSize));
409 break;
410 }
411
412 //
413 // Capsule Update Mode, Split relocated Capsule buffer into different capsule vehical hobs.
414 //
415 Status = RetrieveRelocatedCapsule(CapsuleBuffer, CapsuleSize);
416
417 break;
418 }
419
420 if (EFI_ERROR (Status)) {
421 REPORT_STATUS_CODE (
422 EFI_ERROR_CODE | EFI_ERROR_MAJOR,
423 (EFI_SOFTWARE_PEI_MODULE | EFI_SW_PEI_EC_NO_RECOVERY_CAPSULE)
424 );
425 }
426
427 return Status;
428 }
429
430 //
431 // Any attack against GPT, Relocation Info Variable or temp relocation file will result in no Capsule HOB and return EFI_NOT_FOUND.
432 // After flow to DXE phase. since no capsule hob is detected. Platform will clear Info flag and force restart.
433 // No volunerability will be exposed
434 //
435
436 return EFI_NOT_FOUND;
437 }