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 | }
|
---|