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