]> git.proxmox.com Git - mirror_edk2.git/blob - FmpDevicePkg/Library/FmpDependencyLib/FmpDependencyLib.c
FmpDevicePkg: Add FmpDependency library class and BASE instance
[mirror_edk2.git] / FmpDevicePkg / Library / FmpDependencyLib / FmpDependencyLib.c
1 /** @file
2 Supports Fmp Capsule Dependency Expression.
3
4 Copyright (c) 2020, Intel Corporation. All rights reserved.<BR>
5
6 SPDX-License-Identifier: BSD-2-Clause-Patent
7
8 **/
9 #include <PiDxe.h>
10 #include <Library/BaseLib.h>
11 #include <Library/BaseMemoryLib.h>
12 #include <Library/DebugLib.h>
13 #include <Library/FmpDependencyLib.h>
14 #include <Library/MemoryAllocationLib.h>
15
16 //
17 // Define the initial size of the dependency expression evaluation stack
18 //
19 #define DEPEX_STACK_SIZE_INCREMENT 0x1000
20
21 //
22 // Type of stack element
23 //
24 typedef enum {
25 BooleanType,
26 VersionType
27 } ELEMENT_TYPE;
28
29 //
30 // Value of stack element
31 //
32 typedef union {
33 BOOLEAN Boolean;
34 UINT32 Version;
35 } ELEMENT_VALUE;
36
37 //
38 // Stack element used to evaluate dependency expressions
39 //
40 typedef struct {
41 ELEMENT_VALUE Value;
42 ELEMENT_TYPE Type;
43 } DEPEX_ELEMENT;
44
45 //
46 // Global stack used to evaluate dependency expressions
47 //
48 DEPEX_ELEMENT *mDepexEvaluationStack = NULL;
49 DEPEX_ELEMENT *mDepexEvaluationStackEnd = NULL;
50 DEPEX_ELEMENT *mDepexEvaluationStackPointer = NULL;
51
52 /**
53 Grow size of the Depex stack
54
55 @retval EFI_SUCCESS Stack successfully growed.
56 @retval EFI_OUT_OF_RESOURCES There is not enough system memory to grow the stack.
57
58 **/
59 EFI_STATUS
60 GrowDepexStack (
61 VOID
62 )
63 {
64 DEPEX_ELEMENT *NewStack;
65 UINTN Size;
66
67 Size = DEPEX_STACK_SIZE_INCREMENT;
68 if (mDepexEvaluationStack != NULL) {
69 Size = Size + (mDepexEvaluationStackEnd - mDepexEvaluationStack);
70 }
71
72 NewStack = AllocatePool (Size * sizeof (DEPEX_ELEMENT));
73 if (NewStack == NULL) {
74 DEBUG ((DEBUG_ERROR, "GrowDepexStack: Cannot allocate memory for dependency evaluation stack!\n"));
75 return EFI_OUT_OF_RESOURCES;
76 }
77
78 if (mDepexEvaluationStack != NULL) {
79 //
80 // Copy to Old Stack to the New Stack
81 //
82 CopyMem (
83 NewStack,
84 mDepexEvaluationStack,
85 (mDepexEvaluationStackEnd - mDepexEvaluationStack) * sizeof (DEPEX_ELEMENT)
86 );
87
88 //
89 // Free The Old Stack
90 //
91 FreePool (mDepexEvaluationStack);
92 }
93
94 //
95 // Make the Stack pointer point to the old data in the new stack
96 //
97 mDepexEvaluationStackPointer = NewStack + (mDepexEvaluationStackPointer - mDepexEvaluationStack);
98 mDepexEvaluationStack = NewStack;
99 mDepexEvaluationStackEnd = NewStack + Size;
100
101 return EFI_SUCCESS;
102 }
103
104 /**
105 Push an element onto the Stack.
106
107 @param[in] Value Value to push.
108 @param[in] Type Element Type
109
110 @retval EFI_SUCCESS The value was pushed onto the stack.
111 @retval EFI_OUT_OF_RESOURCES There is not enough system memory to grow the stack.
112 @retval EFI_INVALID_PARAMETER Wrong stack element type.
113
114 **/
115 EFI_STATUS
116 Push (
117 IN UINT32 Value,
118 IN UINTN Type
119 )
120 {
121 EFI_STATUS Status;
122 DEPEX_ELEMENT Element;
123
124 //
125 // Check Type
126 //
127 if (Type != BooleanType && Type != VersionType) {
128 return EFI_INVALID_PARAMETER;
129 }
130
131 //
132 // Check for a stack overflow condition
133 //
134 if (mDepexEvaluationStackPointer == mDepexEvaluationStackEnd) {
135 //
136 // Grow the stack
137 //
138 Status = GrowDepexStack ();
139 if (EFI_ERROR (Status)) {
140 return Status;
141 }
142 }
143
144 Element.Value.Version = Value;
145 Element.Type = Type;
146
147 //
148 // Push the item onto the stack
149 //
150 *mDepexEvaluationStackPointer = Element;
151 mDepexEvaluationStackPointer++;
152
153 return EFI_SUCCESS;
154 }
155
156 /**
157 Pop an element from the stack.
158
159 @param[out] Element Element to pop.
160 @param[in] Type Type of element.
161
162 @retval EFI_SUCCESS The value was popped onto the stack.
163 @retval EFI_ACCESS_DENIED The pop operation underflowed the stack.
164 @retval EFI_INVALID_PARAMETER Type is mismatched.
165
166 **/
167 EFI_STATUS
168 Pop (
169 OUT DEPEX_ELEMENT *Element,
170 IN ELEMENT_TYPE Type
171 )
172 {
173 //
174 // Check for a stack underflow condition
175 //
176 if (mDepexEvaluationStackPointer == mDepexEvaluationStack) {
177 DEBUG ((DEBUG_ERROR, "EvaluateDependency: Stack underflow!\n"));
178 return EFI_ACCESS_DENIED;
179 }
180
181 //
182 // Pop the item off the stack
183 //
184 mDepexEvaluationStackPointer--;
185 *Element = *mDepexEvaluationStackPointer;
186 if ((*Element).Type != Type) {
187 DEBUG ((DEBUG_ERROR, "EvaluateDependency: Popped element type is mismatched!\n"));
188 return EFI_INVALID_PARAMETER;
189 }
190 return EFI_SUCCESS;
191 }
192
193 /**
194 Evaluate the dependencies. The caller must search all the Fmp instances and
195 gather their versions into FmpVersions parameter. If there is PUSH_GUID opcode
196 in dependency expression with no FmpVersions provided, the dependency will
197 evaluate to FALSE.
198
199 @param[in] Dependencies Dependency expressions.
200 @param[in] DependenciesSize Size of Dependency expressions.
201 @param[in] FmpVersions Array of Fmp ImageTypeId and version. This
202 parameter is optional and can be set to NULL.
203 @param[in] FmpVersionsCount Element count of the array. When FmpVersions
204 is NULL, FmpVersionsCount must be 0.
205
206 @retval TRUE Dependency expressions evaluate to TRUE.
207 @retval FALSE Dependency expressions evaluate to FALSE.
208
209 **/
210 BOOLEAN
211 EFIAPI
212 EvaluateDependency (
213 IN EFI_FIRMWARE_IMAGE_DEP *Dependencies,
214 IN UINTN DependenciesSize,
215 IN FMP_DEPEX_CHECK_VERSION_DATA *FmpVersions OPTIONAL,
216 IN UINTN FmpVersionsCount
217 )
218 {
219 EFI_STATUS Status;
220 UINT8 *Iterator;
221 UINT8 Index;
222 DEPEX_ELEMENT Element1;
223 DEPEX_ELEMENT Element2;
224 GUID ImageTypeId;
225 UINT32 Version;
226
227 //
228 // Check if parameter is valid.
229 //
230 if (Dependencies == NULL || DependenciesSize == 0) {
231 return FALSE;
232 }
233
234 if (FmpVersions == NULL && FmpVersionsCount > 0) {
235 return FALSE;
236 }
237
238 //
239 // Clean out memory leaks in Depex Boolean stack. Leaks are only caused by
240 // incorrectly formed DEPEX expressions
241 //
242 mDepexEvaluationStackPointer = mDepexEvaluationStack;
243
244 Iterator = (UINT8 *) Dependencies->Dependencies;
245 while (Iterator < (UINT8 *) Dependencies->Dependencies + DependenciesSize) {
246 switch (*Iterator)
247 {
248 case EFI_FMP_DEP_PUSH_GUID:
249 if (Iterator + sizeof (EFI_GUID) >= (UINT8 *) Dependencies->Dependencies + DependenciesSize) {
250 DEBUG ((DEBUG_ERROR, "EvaluateDependency: GUID extends beyond end of dependency expression!\n"));
251 goto Error;
252 }
253
254 CopyGuid (&ImageTypeId, (EFI_GUID *) (Iterator + 1));
255 Iterator = Iterator + sizeof (EFI_GUID);
256
257 for (Index = 0; Index < FmpVersionsCount; Index ++) {
258 if(CompareGuid (&FmpVersions[Index].ImageTypeId, &ImageTypeId)){
259 Status = Push (FmpVersions[Index].Version, VersionType);
260 if (EFI_ERROR (Status)) {
261 goto Error;
262 }
263 break;
264 }
265 }
266 if (Index == FmpVersionsCount) {
267 DEBUG ((DEBUG_ERROR, "EvaluateDependency: %g is not found!\n", &ImageTypeId));
268 goto Error;
269 }
270 break;
271 case EFI_FMP_DEP_PUSH_VERSION:
272 if (Iterator + sizeof (UINT32) >= (UINT8 *) Dependencies->Dependencies + DependenciesSize ) {
273 DEBUG ((DEBUG_ERROR, "EvaluateDependency: VERSION extends beyond end of dependency expression!\n"));
274 goto Error;
275 }
276
277 Version = *(UINT32 *) (Iterator + 1);
278 Status = Push (Version, VersionType);
279 if (EFI_ERROR (Status)) {
280 goto Error;
281 }
282 Iterator = Iterator + sizeof (UINT32);
283 break;
284 case EFI_FMP_DEP_VERSION_STR:
285 Iterator += AsciiStrnLenS ((CHAR8 *) Iterator, DependenciesSize - (Iterator - Dependencies->Dependencies));
286 if (Iterator == (UINT8 *) Dependencies->Dependencies + DependenciesSize) {
287 DEBUG ((DEBUG_ERROR, "EvaluateDependency: STRING extends beyond end of dependency expression!\n"));
288 }
289 break;
290 case EFI_FMP_DEP_AND:
291 Status = Pop (&Element1, BooleanType);
292 if (EFI_ERROR (Status)) {
293 goto Error;
294 }
295 Status = Pop (&Element2, BooleanType);
296 if (EFI_ERROR (Status)) {
297 goto Error;
298 }
299 Status = Push (Element1.Value.Boolean & Element2.Value.Boolean, BooleanType);
300 if (EFI_ERROR (Status)) {
301 goto Error;
302 }
303 break;
304 case EFI_FMP_DEP_OR:
305 Status = Pop (&Element1, BooleanType);
306 if (EFI_ERROR (Status)) {
307 goto Error;
308 }
309 Status = Pop(&Element2, BooleanType);
310 if (EFI_ERROR (Status)) {
311 goto Error;
312 }
313 Status = Push (Element1.Value.Boolean | Element2.Value.Boolean, BooleanType);
314 if (EFI_ERROR (Status)) {
315 goto Error;
316 }
317 break;
318 case EFI_FMP_DEP_NOT:
319 Status = Pop (&Element1, BooleanType);
320 if (EFI_ERROR (Status)) {
321 goto Error;
322 }
323 Status = Push (!(Element1.Value.Boolean), BooleanType);
324 if (EFI_ERROR (Status)) {
325 goto Error;
326 }
327 break;
328 case EFI_FMP_DEP_TRUE:
329 Status = Push (TRUE, BooleanType);
330 if (EFI_ERROR (Status)) {
331 goto Error;
332 }
333 break;
334 case EFI_FMP_DEP_FALSE:
335 Status = Push (FALSE, BooleanType);
336 if (EFI_ERROR (Status)) {
337 goto Error;
338 }
339 break;
340 case EFI_FMP_DEP_EQ:
341 Status = Pop (&Element1, VersionType);
342 if (EFI_ERROR (Status)) {
343 goto Error;
344 }
345 Status = Pop (&Element2, VersionType);
346 if (EFI_ERROR (Status)) {
347 goto Error;
348 }
349 Status = (Element1.Value.Version == Element2.Value.Version) ? Push (TRUE, BooleanType) : Push (FALSE, BooleanType);
350 if (EFI_ERROR (Status)) {
351 goto Error;
352 }
353 break;
354 case EFI_FMP_DEP_GT:
355 Status = Pop (&Element1, VersionType);
356 if (EFI_ERROR (Status)) {
357 goto Error;
358 }
359 Status = Pop (&Element2, VersionType);
360 if (EFI_ERROR (Status)) {
361 goto Error;
362 }
363 Status = (Element1.Value.Version > Element2.Value.Version) ? Push (TRUE, BooleanType) : Push (FALSE, BooleanType);
364 if (EFI_ERROR (Status)) {
365 goto Error;
366 }
367 break;
368 case EFI_FMP_DEP_GTE:
369 Status = Pop (&Element1, VersionType);
370 if (EFI_ERROR (Status)) {
371 goto Error;
372 }
373 Status = Pop (&Element2, VersionType);
374 if (EFI_ERROR (Status)) {
375 goto Error;
376 }
377 Status = (Element1.Value.Version >= Element2.Value.Version) ? Push (TRUE, BooleanType) : Push (FALSE, BooleanType);
378 if (EFI_ERROR (Status)) {
379 goto Error;
380 }
381 break;
382 case EFI_FMP_DEP_LT:
383 Status = Pop (&Element1, VersionType);
384 if (EFI_ERROR (Status)) {
385 goto Error;
386 }
387 Status = Pop (&Element2, VersionType);
388 if (EFI_ERROR (Status)) {
389 goto Error;
390 }
391 Status = (Element1.Value.Version < Element2.Value.Version) ? Push (TRUE, BooleanType) : Push (FALSE, BooleanType);
392 if (EFI_ERROR (Status)) {
393 goto Error;
394 }
395 break;
396 case EFI_FMP_DEP_LTE:
397 Status = Pop (&Element1, VersionType);
398 if (EFI_ERROR (Status)) {
399 goto Error;
400 }
401 Status = Pop (&Element2, VersionType);
402 if (EFI_ERROR (Status)) {
403 goto Error;
404 }
405 Status = (Element1.Value.Version <= Element2.Value.Version) ? Push (TRUE, BooleanType) : Push (FALSE, BooleanType);
406 if (EFI_ERROR (Status)) {
407 goto Error;
408 }
409 break;
410 case EFI_FMP_DEP_END:
411 Status = Pop (&Element1, BooleanType);
412 if (EFI_ERROR (Status)) {
413 goto Error;
414 }
415 return Element1.Value.Boolean;
416 default:
417 DEBUG ((DEBUG_ERROR, "EvaluateDependency: Unknown Opcode - %02x!\n", *Iterator));
418 goto Error;
419 }
420 Iterator++;
421 }
422
423 DEBUG ((DEBUG_ERROR, "EvaluateDependency: No EFI_FMP_DEP_END Opcode in exression!\n"));
424
425 Error:
426 return FALSE;
427 }
428
429 /**
430 Validate the dependency expression and output its size.
431
432 @param[in] Dependencies Pointer to the EFI_FIRMWARE_IMAGE_DEP.
433 @param[in] MaxDepexSize Max size of the dependency.
434 @param[out] DepexSize Size of dependency.
435
436 @retval TRUE The capsule is valid.
437 @retval FALSE The capsule is invalid.
438
439 **/
440 BOOLEAN
441 EFIAPI
442 ValidateDependency (
443 IN EFI_FIRMWARE_IMAGE_DEP *Dependencies,
444 IN UINTN MaxDepexSize,
445 OUT UINT32 *DepexSize
446 )
447 {
448 UINT8 *Depex;
449
450 if (DepexSize != NULL) {
451 *DepexSize = 0;
452 }
453
454 if (Dependencies == NULL) {
455 return FALSE;
456 }
457
458 Depex = Dependencies->Dependencies;
459 while (Depex < Dependencies->Dependencies + MaxDepexSize) {
460 switch (*Depex)
461 {
462 case EFI_FMP_DEP_PUSH_GUID:
463 Depex += sizeof (EFI_GUID) + 1;
464 break;
465 case EFI_FMP_DEP_PUSH_VERSION:
466 Depex += sizeof (UINT32) + 1;
467 break;
468 case EFI_FMP_DEP_VERSION_STR:
469 Depex += AsciiStrnLenS ((CHAR8 *) Depex, Dependencies->Dependencies + MaxDepexSize - Depex) + 1;
470 break;
471 case EFI_FMP_DEP_AND:
472 case EFI_FMP_DEP_OR:
473 case EFI_FMP_DEP_NOT:
474 case EFI_FMP_DEP_TRUE:
475 case EFI_FMP_DEP_FALSE:
476 case EFI_FMP_DEP_EQ:
477 case EFI_FMP_DEP_GT:
478 case EFI_FMP_DEP_GTE:
479 case EFI_FMP_DEP_LT:
480 case EFI_FMP_DEP_LTE:
481 Depex += 1;
482 break;
483 case EFI_FMP_DEP_END:
484 Depex += 1;
485 if (DepexSize != NULL) {
486 *DepexSize = (UINT32)(Depex - Dependencies->Dependencies);
487 }
488 return TRUE;
489 default:
490 return FALSE;
491 }
492 }
493
494 return FALSE;
495 }
496
497 /**
498 Get dependency from firmware image.
499
500 @param[in] Image Points to the firmware image.
501 @param[in] ImageSize Size, in bytes, of the firmware image.
502 @param[out] DepexSize Size, in bytes, of the dependency.
503
504 @retval The pointer to dependency.
505 @retval Null
506
507 **/
508 EFI_FIRMWARE_IMAGE_DEP*
509 EFIAPI
510 GetImageDependency (
511 IN EFI_FIRMWARE_IMAGE_AUTHENTICATION *Image,
512 IN UINTN ImageSize,
513 OUT UINT32 *DepexSize
514 )
515 {
516 EFI_FIRMWARE_IMAGE_DEP *Depex;
517 UINTN MaxDepexSize;
518
519 if (Image == NULL) {
520 return NULL;
521 }
522
523 //
524 // Check to make sure that operation can be safely performed.
525 //
526 if (((UINTN)Image + sizeof (Image->MonotonicCount) + Image->AuthInfo.Hdr.dwLength) < (UINTN)Image || \
527 ((UINTN)Image + sizeof (Image->MonotonicCount) + Image->AuthInfo.Hdr.dwLength) >= (UINTN)Image + ImageSize) {
528 //
529 // Pointer overflow. Invalid image.
530 //
531 return NULL;
532 }
533
534 Depex = (EFI_FIRMWARE_IMAGE_DEP*)((UINT8 *)Image + sizeof (Image->MonotonicCount) + Image->AuthInfo.Hdr.dwLength);
535 MaxDepexSize = ImageSize - (sizeof (Image->MonotonicCount) + Image->AuthInfo.Hdr.dwLength);
536
537 //
538 // Validate the dependency and get the size of dependency
539 //
540 if (ValidateDependency (Depex, MaxDepexSize, DepexSize)) {
541 return Depex;
542 }
543
544 return NULL;
545 }
546