]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Universal/CapsuleOnDiskLoadPei/CapsuleOnDiskLoadPei.c
UefiCpuPkg: Move AsmRelocateApLoopStart from Mpfuncs.nasm to AmdSev.nasm
[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 ((
193 DEBUG_INFO,
194 "ProcessRelocatedCapsule CapsuleBuf %x TotalCapSize %lx\n",
195 RelocCapsuleBuf,
196 TotalImageSize
197 ));
198
199 RelocCapsuleBuf += sizeof (UINT64);
200
201 //
202 // TempCaspule file length check
203 //
204 if ((MAX_ADDRESS - TotalImageSize <= sizeof (UINT64)) ||
205 ((UINT64)RelocCapsuleTotalSize != TotalImageSize + sizeof (UINT64)) ||
206 ((UINTN)(MAX_ADDRESS - (PHYSICAL_ADDRESS)(UINTN)RelocCapsuleBuf) <= TotalImageSize))
207 {
208 return EFI_INVALID_PARAMETER;
209 }
210
211 CapsuleDataBufEnd = RelocCapsuleBuf + TotalImageSize;
212
213 //
214 // TempCapsule file integrity check over Capsule Header to ensure no data corruption in NV Var & Relocation storage
215 //
216 CapsulePtr = RelocCapsuleBuf;
217 CapsuleNum = 0;
218
219 while (CapsulePtr < CapsuleDataBufEnd) {
220 if (((CapsuleDataBufEnd - CapsulePtr) < sizeof (EFI_CAPSULE_HEADER)) ||
221 (((EFI_CAPSULE_HEADER *)CapsulePtr)->CapsuleImageSize < sizeof (EFI_CAPSULE_HEADER)) ||
222 ((UINTN)(MAX_ADDRESS - (PHYSICAL_ADDRESS)(UINTN)CapsulePtr) < ((EFI_CAPSULE_HEADER *)CapsulePtr)->CapsuleImageSize)
223 )
224 {
225 break;
226 }
227
228 CapsulePtr += ((EFI_CAPSULE_HEADER *)CapsulePtr)->CapsuleImageSize;
229 CapsuleNum++;
230 }
231
232 if (CapsulePtr != CapsuleDataBufEnd) {
233 return EFI_INVALID_PARAMETER;
234 }
235
236 //
237 // Capsule count must be less than PcdCapsuleMax, avoid building too many CvHobs to occupy all the free space in HobList.
238 //
239 if (CapsuleNum > PcdGet16 (PcdCapsuleMax)) {
240 return EFI_INVALID_PARAMETER;
241 }
242
243 //
244 // Re-iterate the capsule buffer to create Capsule hob & Capsule Name Str Hob for each Capsule saved in relocated capsule file
245 //
246 CapsulePtr = RelocCapsuleBuf;
247 Index = 0;
248 while (CapsulePtr < CapsuleDataBufEnd) {
249 CapsuleSize = ((EFI_CAPSULE_HEADER *)CapsulePtr)->CapsuleImageSize;
250 BuildCvHob ((EFI_PHYSICAL_ADDRESS)(UINTN)CapsulePtr, CapsuleSize);
251
252 DEBUG ((DEBUG_INFO, "Capsule saved in address %x size %x\n", CapsulePtr, CapsuleSize));
253
254 CapsulePtr += CapsuleSize;
255 Index++;
256 }
257
258 return EFI_SUCCESS;
259 }
260
261 /**
262 Recovery module entrypoint
263
264 @param[in] FileHandle Handle of the file being invoked.
265 @param[in] PeiServices Describes the list of possible PEI Services.
266
267 @return EFI_SUCCESS Recovery module is initialized.
268 **/
269 EFI_STATUS
270 EFIAPI
271 InitializeCapsuleOnDiskLoad (
272 IN EFI_PEI_FILE_HANDLE FileHandle,
273 IN CONST EFI_PEI_SERVICES **PeiServices
274 )
275 {
276 EFI_STATUS Status;
277 UINTN BootMode;
278 UINTN FileNameSize;
279
280 BootMode = GetBootModeHob ();
281 ASSERT (BootMode == BOOT_ON_FLASH_UPDATE);
282
283 //
284 // If there are capsules provisioned in memory, quit.
285 // Only one capsule resource is accept, CapsuleOnRam's priority is higher than CapsuleOnDisk.
286 //
287 if (CheckCapsuleFromRam (PeiServices)) {
288 DEBUG ((DEBUG_ERROR, "Capsule On Memory Detected! Quit.\n"));
289 return EFI_ABORTED;
290 }
291
292 DEBUG_CODE_BEGIN ();
293 VOID *CapsuleOnDiskModePpi;
294
295 if (!IsCapsuleOnDiskMode ()) {
296 return EFI_NOT_FOUND;
297 }
298
299 //
300 // Check Capsule On Disk Relocation flag. If exists, load capsule & create Capsule Hob
301 //
302 Status = PeiServicesLocatePpi (
303 &gEdkiiPeiBootInCapsuleOnDiskModePpiGuid,
304 0,
305 NULL,
306 (VOID **)&CapsuleOnDiskModePpi
307 );
308 if (EFI_ERROR (Status)) {
309 DEBUG ((DEBUG_ERROR, "Locate CapsuleOnDiskModePpi error %x\n", Status));
310 return Status;
311 }
312
313 DEBUG_CODE_END ();
314
315 Status = PeiServicesInstallPpi (&mCapsuleOnDiskPpiList);
316 ASSERT_EFI_ERROR (Status);
317
318 FileNameSize = PcdGetSize (PcdCoDRelocationFileName);
319 Status = PcdSetPtrS (PcdRecoveryFileName, &FileNameSize, (VOID *)PcdGetPtr (PcdCoDRelocationFileName));
320 ASSERT_EFI_ERROR (Status);
321
322 return Status;
323 }
324
325 /**
326 Loads a DXE capsule from some media into memory and updates the HOB table
327 with the DXE firmware volume information.
328
329 @param[in] PeiServices General-purpose services that are available to every PEIM.
330 @param[in] This Indicates the EFI_PEI_RECOVERY_MODULE_PPI instance.
331
332 @retval EFI_SUCCESS The capsule was loaded correctly.
333 @retval EFI_DEVICE_ERROR A device error occurred.
334 @retval EFI_NOT_FOUND A recovery DXE capsule cannot be found.
335
336 **/
337 EFI_STATUS
338 EFIAPI
339 LoadCapsuleOnDisk (
340 IN EFI_PEI_SERVICES **PeiServices,
341 IN EDKII_PEI_CAPSULE_ON_DISK_PPI *This
342 )
343 {
344 EFI_STATUS Status;
345 EFI_PEI_DEVICE_RECOVERY_MODULE_PPI *DeviceRecoveryPpi;
346 UINTN NumberRecoveryCapsules;
347 UINTN Instance;
348 UINTN CapsuleInstance;
349 UINTN CapsuleSize;
350 EFI_GUID CapsuleType;
351 VOID *CapsuleBuffer;
352
353 DEBUG ((DEBUG_INFO | DEBUG_LOAD, "Load Capsule On Disk Entry\n"));
354
355 for (Instance = 0; ; Instance++) {
356 Status = PeiServicesLocatePpi (
357 &gEfiPeiDeviceRecoveryModulePpiGuid,
358 Instance,
359 NULL,
360 (VOID **)&DeviceRecoveryPpi
361 );
362 DEBUG ((DEBUG_INFO, "LoadCapsuleOnDisk - LocateRecoveryPpi (%d) - %r\n", Instance, Status));
363 if (EFI_ERROR (Status)) {
364 if (Instance == 0) {
365 REPORT_STATUS_CODE (
366 EFI_ERROR_CODE | EFI_ERROR_MAJOR,
367 (EFI_SOFTWARE_PEI_MODULE | EFI_SW_PEI_EC_RECOVERY_PPI_NOT_FOUND)
368 );
369 }
370
371 break;
372 }
373
374 NumberRecoveryCapsules = 0;
375 Status = DeviceRecoveryPpi->GetNumberRecoveryCapsules (
376 (EFI_PEI_SERVICES **)PeiServices,
377 DeviceRecoveryPpi,
378 &NumberRecoveryCapsules
379 );
380 DEBUG ((DEBUG_INFO, "LoadCapsuleOnDisk - GetNumberRecoveryCapsules (%d) - %r\n", NumberRecoveryCapsules, Status));
381 if (EFI_ERROR (Status)) {
382 continue;
383 }
384
385 for (CapsuleInstance = 1; CapsuleInstance <= NumberRecoveryCapsules; CapsuleInstance++) {
386 CapsuleSize = 0;
387 Status = DeviceRecoveryPpi->GetRecoveryCapsuleInfo (
388 (EFI_PEI_SERVICES **)PeiServices,
389 DeviceRecoveryPpi,
390 CapsuleInstance,
391 &CapsuleSize,
392 &CapsuleType
393 );
394 DEBUG ((DEBUG_INFO, "LoadCapsuleOnDisk - GetRecoveryCapsuleInfo (%d - %x) - %r\n", CapsuleInstance, CapsuleSize, Status));
395 if (EFI_ERROR (Status)) {
396 break;
397 }
398
399 //
400 // Allocate the memory so that it gets preserved into DXE.
401 // Capsule is special because it may need to populate to system table
402 //
403 CapsuleBuffer = AllocateRuntimePages (EFI_SIZE_TO_PAGES (CapsuleSize));
404
405 if (CapsuleBuffer == NULL) {
406 DEBUG ((DEBUG_ERROR, "LoadCapsuleOnDisk - AllocateRuntimePages fail\n"));
407 continue;
408 }
409
410 Status = DeviceRecoveryPpi->LoadRecoveryCapsule (
411 (EFI_PEI_SERVICES **)PeiServices,
412 DeviceRecoveryPpi,
413 CapsuleInstance,
414 CapsuleBuffer
415 );
416 DEBUG ((DEBUG_INFO, "LoadCapsuleOnDisk - LoadRecoveryCapsule (%d) - %r\n", CapsuleInstance, Status));
417 if (EFI_ERROR (Status)) {
418 FreePages (CapsuleBuffer, EFI_SIZE_TO_PAGES (CapsuleSize));
419 break;
420 }
421
422 //
423 // Capsule Update Mode, Split relocated Capsule buffer into different capsule vehical hobs.
424 //
425 Status = RetrieveRelocatedCapsule (CapsuleBuffer, CapsuleSize);
426
427 break;
428 }
429
430 if (EFI_ERROR (Status)) {
431 REPORT_STATUS_CODE (
432 EFI_ERROR_CODE | EFI_ERROR_MAJOR,
433 (EFI_SOFTWARE_PEI_MODULE | EFI_SW_PEI_EC_NO_RECOVERY_CAPSULE)
434 );
435 }
436
437 return Status;
438 }
439
440 //
441 // Any attack against GPT, Relocation Info Variable or temp relocation file will result in no Capsule HOB and return EFI_NOT_FOUND.
442 // After flow to DXE phase. since no capsule hob is detected. Platform will clear Info flag and force restart.
443 // No volunerability will be exposed
444 //
445
446 return EFI_NOT_FOUND;
447 }