]> git.proxmox.com Git - mirror_edk2.git/blame - MdeModulePkg/Universal/CapsuleOnDiskLoadPei/CapsuleOnDiskLoadPei.c
MdePkg: Add Generic Initiator Affinity Structure definitions to SRAT
[mirror_edk2.git] / MdeModulePkg / Universal / CapsuleOnDiskLoadPei / CapsuleOnDiskLoadPei.c
CommitLineData
f1793532
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 EFI_PEI_CAPSULE_ON_DISK_PPI *This\r
63 );\r
64\r
65EFI_PEI_CAPSULE_ON_DISK_PPI mCapsuleOnDiskPpi = {\r
66 LoadCapsuleOnDisk\r
67};\r
68\r
69EFI_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
116BOOLEAN\r
117IsCapsuleOnDiskMode (\r
118 VOID\r
119 )\r
120{\r
121 EFI_STATUS Status;\r
122 UINTN Size;\r
123 EFI_PEI_READ_ONLY_VARIABLE2_PPI *PPIVariableServices;\r
124 BOOLEAN CodRelocInfo;\r
125\r
126 Status = PeiServicesLocatePpi (\r
127 &gEfiPeiReadOnlyVariable2PpiGuid,\r
128 0,\r
129 NULL,\r
130 (VOID **) &PPIVariableServices\r
131 );\r
132 ASSERT_EFI_ERROR (Status);\r
133\r
134 Size = sizeof (BOOLEAN);\r
135 Status = PPIVariableServices->GetVariable (\r
136 PPIVariableServices,\r
137 COD_RELOCATION_INFO_VAR_NAME,\r
138 &gEfiCapsuleVendorGuid,\r
139 NULL,\r
140 &Size,\r
141 &CodRelocInfo\r
142 );\r
143\r
144 if (EFI_ERROR (Status) || Size != sizeof(BOOLEAN) || !CodRelocInfo) {\r
145 DEBUG (( DEBUG_ERROR, "Error Get CodRelocationInfo variable %r!\n", Status));\r
146 return FALSE;\r
147 }\r
148\r
149 return TRUE;\r
150}\r
151\r
152/**\r
153 Gets capsule images from relocated capsule buffer.\r
154 Create Capsule hob for each Capsule.\r
155\r
156 Caution: This function may receive untrusted input.\r
157 Capsule-on-Disk Temp Relocation image is external input, so this function\r
158 will validate Capsule-on-Disk Temp Relocation image to make sure the content\r
159 is read within the buffer.\r
160\r
161 @param[in] RelocCapsuleBuf Buffer pointer to the relocated capsule.\r
162 @param[in] RelocCapsuleTotalSize Total size of the relocated capsule.\r
163\r
164 @retval EFI_SUCCESS Succeed to get capsules and create hob.\r
165 @retval Others Fail to get capsules and create hob.\r
166\r
167**/\r
168EFI_STATUS\r
169EFIAPI\r
170RetrieveRelocatedCapsule (\r
171 IN UINT8 *RelocCapsuleBuf,\r
172 IN UINTN RelocCapsuleTotalSize\r
173 )\r
174{\r
175 EFI_STATUS Status;\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 CapsuleNum = 0;\r
184\r
185 //\r
186 // Temp file contains at least 2 capsule (including 1 capsule name capsule) & 1 UINT64\r
187 //\r
188 if (RelocCapsuleTotalSize < sizeof(UINT64) + sizeof(EFI_CAPSULE_HEADER) * 2) {\r
189 return EFI_INVALID_PARAMETER;\r
190 }\r
191\r
192 CopyMem(&TotalImageSize, RelocCapsuleBuf, sizeof(UINT64));\r
193\r
194 DEBUG ((DEBUG_INFO, "ProcessRelocatedCapsule CapsuleBuf %x TotalCapSize %lx\n",\r
195 RelocCapsuleBuf, TotalImageSize));\r
196\r
197 RelocCapsuleBuf += sizeof(UINT64);\r
198\r
199 //\r
200 // TempCaspule file length check\r
201 //\r
202 if (MAX_ADDRESS - TotalImageSize <= sizeof(UINT64) ||\r
203 (UINT64)RelocCapsuleTotalSize != TotalImageSize + sizeof(UINT64) ||\r
204 (UINTN)(MAX_ADDRESS - (PHYSICAL_ADDRESS)(UINTN)RelocCapsuleBuf) <= TotalImageSize) {\r
205 return EFI_INVALID_PARAMETER;\r
206 }\r
207\r
208 CapsuleDataBufEnd = RelocCapsuleBuf + TotalImageSize;\r
209\r
210 //\r
211 // TempCapsule file integrity check over Capsule Header to ensure no data corruption in NV Var & Relocation storage\r
212 //\r
213 CapsulePtr = RelocCapsuleBuf;\r
214\r
215 while (CapsulePtr < CapsuleDataBufEnd) {\r
216 if ((CapsuleDataBufEnd - CapsulePtr) < sizeof(EFI_CAPSULE_HEADER) ||\r
217 ((EFI_CAPSULE_HEADER *)CapsulePtr)->CapsuleImageSize < sizeof(EFI_CAPSULE_HEADER) ||\r
218 (UINTN)(MAX_ADDRESS - (PHYSICAL_ADDRESS)(UINTN)CapsulePtr) < ((EFI_CAPSULE_HEADER *)CapsulePtr)->CapsuleImageSize\r
219 ) {\r
220 break;\r
221 }\r
222 CapsulePtr += ((EFI_CAPSULE_HEADER *)CapsulePtr)->CapsuleImageSize;\r
223 CapsuleNum ++;\r
224 }\r
225\r
226 if (CapsulePtr != CapsuleDataBufEnd) {\r
227 Status = EFI_INVALID_PARAMETER;\r
228 goto EXIT;\r
229 }\r
230\r
231 //\r
232 // Capsule count must be less than PcdCapsuleMax, avoid building too many CvHobs to occupy all the free space in HobList.\r
233 //\r
234 if (CapsuleNum > PcdGet16 (PcdCapsuleMax)) {\r
235 Status = EFI_INVALID_PARAMETER;\r
236 goto EXIT;\r
237 }\r
238\r
239 //\r
240 // Re-iterate the capsule buffer to create Capsule hob & Capsule Name Str Hob for each Capsule saved in relocated capsule file\r
241 //\r
242 CapsulePtr = RelocCapsuleBuf;\r
243 Index = 0;\r
244 while (CapsulePtr < CapsuleDataBufEnd) {\r
245 CapsuleSize = ((EFI_CAPSULE_HEADER *)CapsulePtr)->CapsuleImageSize;\r
246 BuildCvHob ((EFI_PHYSICAL_ADDRESS)(UINTN)CapsulePtr, CapsuleSize);\r
247\r
248 DEBUG((DEBUG_INFO, "Capsule saved in address %x size %x\n", CapsulePtr, CapsuleSize));\r
249\r
250 CapsulePtr += CapsuleSize;\r
251 Index++;\r
252 }\r
253\r
254EXIT:\r
255\r
256 return Status;\r
257}\r
258\r
259/**\r
260 Recovery module entrypoint\r
261\r
262 @param[in] FileHandle Handle of the file being invoked.\r
263 @param[in] PeiServices Describes the list of possible PEI Services.\r
264\r
265 @return EFI_SUCCESS Recovery module is initialized.\r
266**/\r
267EFI_STATUS\r
268EFIAPI\r
269InitializeCapsuleOnDiskLoad (\r
270 IN EFI_PEI_FILE_HANDLE FileHandle,\r
271 IN CONST EFI_PEI_SERVICES **PeiServices\r
272 )\r
273{\r
274 EFI_STATUS Status;\r
275 UINTN BootMode;\r
276 UINTN FileNameSize;\r
277\r
278 BootMode = GetBootModeHob();\r
279 ASSERT(BootMode == BOOT_ON_FLASH_UPDATE);\r
280\r
281 //\r
282 // If there are capsules provisioned in memory, quit.\r
283 // Only one capsule resource is accept, CapsuleOnRam's priority is higher than CapsuleOnDisk.\r
284 //\r
285 if (CheckCapsuleFromRam(PeiServices)) {\r
286 DEBUG((DEBUG_ERROR, "Capsule On Memory Detected! Quit.\n"));\r
287 return EFI_ABORTED;\r
288 }\r
289\r
290 DEBUG_CODE (\r
291 VOID *CapsuleOnDiskModePpi;\r
292\r
293 if (!IsCapsuleOnDiskMode()){\r
294 return EFI_NOT_FOUND;\r
295 }\r
296\r
297 //\r
298 // Check Capsule On Disk Relocation flag. If exists, load capsule & create Capsule Hob\r
299 //\r
300 Status = PeiServicesLocatePpi (\r
301 &gEdkiiPeiBootInCapsuleOnDiskModePpiGuid,\r
302 0,\r
303 NULL,\r
304 (VOID **)&CapsuleOnDiskModePpi\r
305 );\r
306 if (EFI_ERROR(Status)) {\r
307 DEBUG((DEBUG_ERROR, "Locate CapsuleOnDiskModePpi error %x\n", Status));\r
308 return Status;\r
309 }\r
310 );\r
311\r
312 Status = PeiServicesInstallPpi (&mCapsuleOnDiskPpiList);\r
313 ASSERT_EFI_ERROR (Status);\r
314\r
315 FileNameSize = PcdGetSize (PcdCoDRelocationFileName);\r
316 Status = PcdSetPtrS (PcdRecoveryFileName, &FileNameSize, (VOID *) PcdGetPtr(PcdCoDRelocationFileName));\r
317 ASSERT_EFI_ERROR (Status);\r
318\r
319 return Status;\r
320}\r
321\r
322/**\r
323 Loads a DXE capsule from some media into memory and updates the HOB table\r
324 with the DXE firmware volume information.\r
325\r
326 @param[in] PeiServices General-purpose services that are available to every PEIM.\r
327 @param[in] This Indicates the EFI_PEI_RECOVERY_MODULE_PPI instance.\r
328\r
329 @retval EFI_SUCCESS The capsule was loaded correctly.\r
330 @retval EFI_DEVICE_ERROR A device error occurred.\r
331 @retval EFI_NOT_FOUND A recovery DXE capsule cannot be found.\r
332\r
333**/\r
334EFI_STATUS\r
335EFIAPI\r
336LoadCapsuleOnDisk (\r
337 IN EFI_PEI_SERVICES **PeiServices,\r
338 IN EFI_PEI_CAPSULE_ON_DISK_PPI *This\r
339 )\r
340{\r
341 EFI_STATUS Status;\r
342 EFI_PEI_DEVICE_RECOVERY_MODULE_PPI *DeviceRecoveryPpi;\r
343 UINTN NumberRecoveryCapsules;\r
344 UINTN Instance;\r
345 UINTN CapsuleInstance;\r
346 UINTN CapsuleSize;\r
347 EFI_GUID CapsuleType;\r
348 VOID *CapsuleBuffer;\r
349\r
350 DEBUG ((DEBUG_INFO | DEBUG_LOAD, "Load Capsule On Disk Entry\n"));\r
351\r
352 for (Instance = 0; ; Instance++) {\r
353 Status = PeiServicesLocatePpi (\r
354 &gEfiPeiDeviceRecoveryModulePpiGuid,\r
355 Instance,\r
356 NULL,\r
357 (VOID **)&DeviceRecoveryPpi\r
358 );\r
359 DEBUG ((DEBUG_INFO, "LoadCapsuleOnDisk - LocateRecoveryPpi (%d) - %r\n", Instance, Status));\r
360 if (EFI_ERROR (Status)) {\r
361 if (Instance == 0) {\r
362 REPORT_STATUS_CODE (\r
363 EFI_ERROR_CODE | EFI_ERROR_MAJOR,\r
364 (EFI_SOFTWARE_PEI_MODULE | EFI_SW_PEI_EC_RECOVERY_PPI_NOT_FOUND)\r
365 );\r
366 }\r
367 break;\r
368 }\r
369 NumberRecoveryCapsules = 0;\r
370 Status = DeviceRecoveryPpi->GetNumberRecoveryCapsules (\r
371 (EFI_PEI_SERVICES **)PeiServices,\r
372 DeviceRecoveryPpi,\r
373 &NumberRecoveryCapsules\r
374 );\r
375 DEBUG ((DEBUG_INFO, "LoadCapsuleOnDisk - GetNumberRecoveryCapsules (%d) - %r\n", NumberRecoveryCapsules, Status));\r
376 if (EFI_ERROR (Status)) {\r
377 continue;\r
378 }\r
379\r
380 for (CapsuleInstance = 1; CapsuleInstance <= NumberRecoveryCapsules; CapsuleInstance++) {\r
381 CapsuleSize = 0;\r
382 Status = DeviceRecoveryPpi->GetRecoveryCapsuleInfo (\r
383 (EFI_PEI_SERVICES **)PeiServices,\r
384 DeviceRecoveryPpi,\r
385 CapsuleInstance,\r
386 &CapsuleSize,\r
387 &CapsuleType\r
388 );\r
389 DEBUG ((DEBUG_INFO, "LoadCapsuleOnDisk - GetRecoveryCapsuleInfo (%d - %x) - %r\n", CapsuleInstance, CapsuleSize, Status));\r
390 if (EFI_ERROR (Status)) {\r
391 break;\r
392 }\r
393\r
394 //\r
395 // Allocate the memory so that it gets preserved into DXE.\r
396 // Capsule is special because it may need to populate to system table\r
397 //\r
398 CapsuleBuffer = AllocateRuntimePages (EFI_SIZE_TO_PAGES (CapsuleSize));\r
399\r
400 if (CapsuleBuffer == NULL) {\r
401 DEBUG ((DEBUG_ERROR, "LoadCapsuleOnDisk - AllocateRuntimePages fail\n"));\r
402 continue;\r
403 }\r
404\r
405 Status = DeviceRecoveryPpi->LoadRecoveryCapsule (\r
406 (EFI_PEI_SERVICES **)PeiServices,\r
407 DeviceRecoveryPpi,\r
408 CapsuleInstance,\r
409 CapsuleBuffer\r
410 );\r
411 DEBUG ((DEBUG_INFO, "LoadCapsuleOnDisk - LoadRecoveryCapsule (%d) - %r\n", CapsuleInstance, Status));\r
412 if (EFI_ERROR (Status)) {\r
413 FreePages (CapsuleBuffer, EFI_SIZE_TO_PAGES(CapsuleSize));\r
414 break;\r
415 }\r
416\r
417 //\r
418 // Capsule Update Mode, Split relocated Capsule buffer into different capsule vehical hobs.\r
419 //\r
420 Status = RetrieveRelocatedCapsule(CapsuleBuffer, CapsuleSize);\r
421\r
422 break;\r
423 }\r
424\r
425 if (EFI_ERROR (Status)) {\r
426 REPORT_STATUS_CODE (\r
427 EFI_ERROR_CODE | EFI_ERROR_MAJOR,\r
428 (EFI_SOFTWARE_PEI_MODULE | EFI_SW_PEI_EC_NO_RECOVERY_CAPSULE)\r
429 );\r
430 }\r
431\r
432 return Status;\r
433 }\r
434\r
435 //\r
436 // Any attack against GPT, Relocation Info Variable or temp relocation file will result in no Capsule HOB and return EFI_NOT_FOUND.\r
437 // After flow to DXE phase. since no capsule hob is detected. Platform will clear Info flag and force restart.\r
438 // No volunerability will be exposed\r
439 //\r
440\r
441 return EFI_NOT_FOUND;\r
442}\r