1 /** @file
2   Var Check PCD handler.
3 
4 Copyright (c) 2015, Intel Corporation. All rights reserved.<BR>
5 This program and the accompanying materials
6 are licensed and made available under the terms and conditions of the BSD License
7 which accompanies this distribution.  The full text of the license may be found at
8 http://opensource.org/licenses/bsd-license.php
9 
10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
12 
13 **/
14 
15 #include <Library/VarCheckLib.h>
16 #include <Library/BaseLib.h>
17 #include <Library/DebugLib.h>
18 #include <Library/BaseMemoryLib.h>
19 #include <Library/MemoryAllocationLib.h>
20 #include <Library/DxeServicesLib.h>
21 
22 #include "VarCheckPcdStructure.h"
23 
24 //#define DUMP_VAR_CHECK_PCD
25 
26 GLOBAL_REMOVE_IF_UNREFERENCED CONST CHAR8 mVarCheckPcdHex[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
27 
28 /**
29   Dump some hexadecimal data.
30 
31   @param[in] Indent     How many spaces to indent the output.
32   @param[in] Offset     The offset of the dump.
33   @param[in] DataSize   The size in bytes of UserData.
34   @param[in] UserData   The data to dump.
35 
36 **/
37 VOID
VarCheckPcdInternalDumpHex(IN UINTN Indent,IN UINTN Offset,IN UINTN DataSize,IN VOID * UserData)38 VarCheckPcdInternalDumpHex (
39   IN UINTN        Indent,
40   IN UINTN        Offset,
41   IN UINTN        DataSize,
42   IN VOID         *UserData
43   )
44 {
45   UINT8 *Data;
46 
47   CHAR8 Val[50];
48 
49   CHAR8 Str[20];
50 
51   UINT8 TempByte;
52   UINTN Size;
53   UINTN Index;
54 
55   Data = UserData;
56   while (DataSize != 0) {
57     Size = 16;
58     if (Size > DataSize) {
59       Size = DataSize;
60     }
61 
62     for (Index = 0; Index < Size; Index += 1) {
63       TempByte            = Data[Index];
64       Val[Index * 3 + 0]  = mVarCheckPcdHex[TempByte >> 4];
65       Val[Index * 3 + 1]  = mVarCheckPcdHex[TempByte & 0xF];
66       Val[Index * 3 + 2]  = (CHAR8) ((Index == 7) ? '-' : ' ');
67       Str[Index]          = (CHAR8) ((TempByte < ' ' || TempByte > 'z') ? '.' : TempByte);
68     }
69 
70     Val[Index * 3]  = 0;
71     Str[Index]      = 0;
72     DEBUG ((EFI_D_INFO, "%*a%08X: %-48a *%a*\r\n", Indent, "", Offset, Val, Str));
73 
74     Data += Size;
75     Offset += Size;
76     DataSize -= Size;
77   }
78 }
79 
80 /**
81   Var Check Pcd ValidData.
82 
83   @param[in] PcdValidData   Pointer to Pcd ValidData
84   @param[in] Data           Data pointer.
85   @param[in] DataSize       Size of Data to set.
86 
87   @retval TRUE  Check pass
88   @retval FALSE Check fail.
89 
90 **/
91 BOOLEAN
VarCheckPcdValidData(IN VAR_CHECK_PCD_VALID_DATA_HEADER * PcdValidData,IN VOID * Data,IN UINTN DataSize)92 VarCheckPcdValidData (
93   IN VAR_CHECK_PCD_VALID_DATA_HEADER    *PcdValidData,
94   IN VOID                               *Data,
95   IN UINTN                              DataSize
96   )
97 {
98   UINT64   OneData;
99   UINT64   Minimum;
100   UINT64   Maximum;
101   UINT64   OneValue;
102   UINT8    *Ptr;
103 
104   OneData = 0;
105   CopyMem (&OneData, (UINT8 *) Data + PcdValidData->VarOffset, PcdValidData->StorageWidth);
106 
107   switch (PcdValidData->Type) {
108     case VarCheckPcdValidList:
109       Ptr = (UINT8 *) ((VAR_CHECK_PCD_VALID_LIST *) PcdValidData + 1);
110       while ((UINTN) Ptr < (UINTN) PcdValidData + PcdValidData->Length) {
111         OneValue = 0;
112         CopyMem (&OneValue, Ptr, PcdValidData->StorageWidth);
113         if (OneData == OneValue) {
114           //
115           // Match
116           //
117           break;
118         }
119         Ptr += PcdValidData->StorageWidth;
120       }
121       if ((UINTN) Ptr >= ((UINTN) PcdValidData + PcdValidData->Length)) {
122         //
123         // No match
124         //
125         DEBUG ((EFI_D_INFO, "VarCheckPcdValidData fail: ValidList mismatch (0x%lx)\n", OneData));
126         DEBUG_CODE (VarCheckPcdInternalDumpHex (2, 0, PcdValidData->Length, (UINT8 *) PcdValidData););
127         return FALSE;
128       }
129       break;
130 
131     case VarCheckPcdValidRange:
132       Minimum = 0;
133       Maximum = 0;
134       Ptr = (UINT8 *) ((VAR_CHECK_PCD_VALID_RANGE *) PcdValidData + 1);
135       while ((UINTN) Ptr < (UINTN) PcdValidData + PcdValidData->Length) {
136         CopyMem (&Minimum, Ptr, PcdValidData->StorageWidth);
137         Ptr += PcdValidData->StorageWidth;
138         CopyMem (&Maximum, Ptr, PcdValidData->StorageWidth);
139         Ptr += PcdValidData->StorageWidth;
140 
141         if ((OneData >= Minimum) && (OneData <= Maximum)) {
142           return TRUE;
143         }
144       }
145       DEBUG ((EFI_D_INFO, "VarCheckPcdValidData fail: ValidRange mismatch (0x%lx)\n", OneData));
146       DEBUG_CODE (VarCheckPcdInternalDumpHex (2, 0, PcdValidData->Length, (UINT8 *) PcdValidData););
147       return FALSE;
148       break;
149 
150     default:
151       ASSERT (FALSE);
152       break;
153   }
154 
155   return TRUE;
156 }
157 
158 VAR_CHECK_PCD_VARIABLE_HEADER   *mVarCheckPcdBin = NULL;
159 UINTN                           mVarCheckPcdBinSize = 0;
160 
161 /**
162   SetVariable check handler PCD.
163 
164   @param[in] VariableName       Name of Variable to set.
165   @param[in] VendorGuid         Variable vendor GUID.
166   @param[in] Attributes         Attribute value of the variable.
167   @param[in] DataSize           Size of Data to set.
168   @param[in] Data               Data pointer.
169 
170   @retval EFI_SUCCESS               The SetVariable check result was success.
171   @retval EFI_SECURITY_VIOLATION    Check fail.
172 
173 **/
174 EFI_STATUS
175 EFIAPI
SetVariableCheckHandlerPcd(IN CHAR16 * VariableName,IN EFI_GUID * VendorGuid,IN UINT32 Attributes,IN UINTN DataSize,IN VOID * Data)176 SetVariableCheckHandlerPcd (
177   IN CHAR16     *VariableName,
178   IN EFI_GUID   *VendorGuid,
179   IN UINT32     Attributes,
180   IN UINTN      DataSize,
181   IN VOID       *Data
182   )
183 {
184   VAR_CHECK_PCD_VARIABLE_HEADER     *PcdVariable;
185   VAR_CHECK_PCD_VALID_DATA_HEADER   *PcdValidData;
186 
187   if (mVarCheckPcdBin == NULL) {
188     return EFI_SUCCESS;
189   }
190 
191   if ((((Attributes & EFI_VARIABLE_APPEND_WRITE) == 0) && (DataSize == 0)) || (Attributes == 0)) {
192     //
193     // Do not check delete variable.
194     //
195     return EFI_SUCCESS;
196   }
197 
198   //
199   // For Pcd Variable header align.
200   //
201   PcdVariable = (VAR_CHECK_PCD_VARIABLE_HEADER *) HEADER_ALIGN (mVarCheckPcdBin);
202   while ((UINTN) PcdVariable < ((UINTN) mVarCheckPcdBin + mVarCheckPcdBinSize)) {
203     if ((StrCmp ((CHAR16 *) (PcdVariable + 1), VariableName) == 0) &&
204         (CompareGuid (&PcdVariable->Guid, VendorGuid))) {
205       //
206       // Found the Pcd Variable that could be used to do check.
207       //
208       DEBUG ((EFI_D_INFO, "VarCheckPcdVariable - %s:%g with Attributes = 0x%08x Size = 0x%x\n", VariableName, VendorGuid, Attributes, DataSize));
209       if ((PcdVariable->Attributes != 0) && PcdVariable->Attributes != Attributes) {
210         DEBUG ((EFI_D_INFO, "VarCheckPcdVariable fail for Attributes - 0x%08x\n", PcdVariable->Attributes));
211         return EFI_SECURITY_VIOLATION;
212       }
213 
214       if (DataSize == 0) {
215         DEBUG ((EFI_D_INFO, "VarCheckPcdVariable - CHECK PASS with DataSize == 0 !\n"));
216         return EFI_SUCCESS;
217       }
218 
219       //
220       // Do the check.
221       // For Pcd ValidData header align.
222       //
223       PcdValidData = (VAR_CHECK_PCD_VALID_DATA_HEADER *) HEADER_ALIGN (((UINTN) PcdVariable + PcdVariable->HeaderLength));
224       while ((UINTN) PcdValidData < ((UINTN) PcdVariable + PcdVariable->Length)) {
225         if (((UINTN) PcdValidData->VarOffset + PcdValidData->StorageWidth) <= DataSize) {
226           if (!VarCheckPcdValidData (PcdValidData, Data, DataSize)) {
227             return EFI_SECURITY_VIOLATION;
228           }
229         }
230         //
231         // For Pcd ValidData header align.
232         //
233         PcdValidData = (VAR_CHECK_PCD_VALID_DATA_HEADER *) HEADER_ALIGN (((UINTN) PcdValidData + PcdValidData->Length));
234       }
235 
236       DEBUG ((EFI_D_INFO, "VarCheckPcdVariable - ALL CHECK PASS!\n"));
237       return EFI_SUCCESS;
238     }
239     //
240     // For Pcd Variable header align.
241     //
242     PcdVariable = (VAR_CHECK_PCD_VARIABLE_HEADER *) HEADER_ALIGN (((UINTN) PcdVariable + PcdVariable->Length));
243   }
244 
245   // Not found, so pass.
246   return EFI_SUCCESS;
247 }
248 
249 #ifdef DUMP_VAR_CHECK_PCD
250 /**
251   Dump Pcd ValidData.
252 
253   @param[in] PcdValidData    Pointer to Pcd ValidData.
254 
255 **/
256 VOID
DumpPcdValidData(IN VAR_CHECK_PCD_VALID_DATA_HEADER * PcdValidData)257 DumpPcdValidData (
258   IN VAR_CHECK_PCD_VALID_DATA_HEADER    *PcdValidData
259   )
260 {
261   UINT64    Minimum;
262   UINT64    Maximum;
263   UINT64    OneValue;
264   UINT8     *Ptr;
265 
266   DEBUG ((EFI_D_INFO, "  VAR_CHECK_PCD_VALID_DATA_HEADER\n"));
267   DEBUG ((EFI_D_INFO, "    Type          - 0x%02x\n", PcdValidData->Type));
268   DEBUG ((EFI_D_INFO, "    Length        - 0x%02x\n", PcdValidData->Length));
269   DEBUG ((EFI_D_INFO, "    VarOffset     - 0x%04x\n", PcdValidData->VarOffset));
270   DEBUG ((EFI_D_INFO, "    StorageWidth  - 0x%02x\n", PcdValidData->StorageWidth));
271 
272   switch (PcdValidData->Type) {
273     case VarCheckPcdValidList:
274       Ptr = (UINT8 *) ((VAR_CHECK_PCD_VALID_LIST *) PcdValidData + 1);
275       while ((UINTN) Ptr < ((UINTN) PcdValidData + PcdValidData->Length)) {
276         OneValue = 0;
277         CopyMem (&OneValue, Ptr, PcdValidData->StorageWidth);
278         switch (PcdValidData->StorageWidth) {
279           case sizeof (UINT8):
280             DEBUG ((EFI_D_INFO, "    ValidList   - 0x%02x\n", OneValue));
281             break;
282           case sizeof (UINT16):
283             DEBUG ((EFI_D_INFO, "    ValidList   - 0x%04x\n", OneValue));
284             break;
285           case sizeof (UINT32):
286             DEBUG ((EFI_D_INFO, "    ValidList   - 0x%08x\n", OneValue));
287             break;
288           case sizeof (UINT64):
289             DEBUG ((EFI_D_INFO, "    ValidList   - 0x%016lx\n", OneValue));
290             break;
291           default:
292             ASSERT (FALSE);
293             break;
294         }
295         Ptr += PcdValidData->StorageWidth;
296       }
297       break;
298 
299     case VarCheckPcdValidRange:
300       Minimum = 0;
301       Maximum = 0;
302       Ptr = (UINT8 *) ((VAR_CHECK_PCD_VALID_RANGE *) PcdValidData + 1);
303       while ((UINTN) Ptr < (UINTN) PcdValidData + PcdValidData->Length) {
304         CopyMem (&Minimum, Ptr, PcdValidData->StorageWidth);
305         Ptr += PcdValidData->StorageWidth;
306         CopyMem (&Maximum, Ptr, PcdValidData->StorageWidth);
307         Ptr += PcdValidData->StorageWidth;
308 
309         switch (PcdValidData->StorageWidth) {
310           case sizeof (UINT8):
311             DEBUG ((EFI_D_INFO, "    Minimum       - 0x%02x\n", Minimum));
312             DEBUG ((EFI_D_INFO, "    Maximum       - 0x%02x\n", Maximum));
313             break;
314           case sizeof (UINT16):
315             DEBUG ((EFI_D_INFO, "    Minimum       - 0x%04x\n", Minimum));
316             DEBUG ((EFI_D_INFO, "    Maximum       - 0x%04x\n", Maximum));
317             break;
318           case sizeof (UINT32):
319             DEBUG ((EFI_D_INFO, "    Minimum       - 0x%08x\n", Minimum));
320             DEBUG ((EFI_D_INFO, "    Maximum       - 0x%08x\n", Maximum));
321             break;
322           case sizeof (UINT64):
323             DEBUG ((EFI_D_INFO, "    Minimum       - 0x%016lx\n", Minimum));
324             DEBUG ((EFI_D_INFO, "    Maximum       - 0x%016lx\n", Maximum));
325             break;
326           default:
327             ASSERT (FALSE);
328             break;
329         }
330       }
331       break;
332 
333     default:
334       ASSERT (FALSE);
335       break;
336   }
337 }
338 
339 /**
340   Dump Pcd Variable.
341 
342   @param[in] PcdVariable    Pointer to Pcd Variable.
343 
344 **/
345 VOID
DumpPcdVariable(IN VAR_CHECK_PCD_VARIABLE_HEADER * PcdVariable)346 DumpPcdVariable (
347   IN VAR_CHECK_PCD_VARIABLE_HEADER  *PcdVariable
348   )
349 {
350   VAR_CHECK_PCD_VALID_DATA_HEADER *PcdValidData;
351 
352   DEBUG ((EFI_D_INFO, "VAR_CHECK_PCD_VARIABLE_HEADER\n"));
353   DEBUG ((EFI_D_INFO, "  Revision        - 0x%04x\n", PcdVariable->Revision));
354   DEBUG ((EFI_D_INFO, "  HeaderLength    - 0x%04x\n", PcdVariable->HeaderLength));
355   DEBUG ((EFI_D_INFO, "  Length          - 0x%08x\n", PcdVariable->Length));
356   DEBUG ((EFI_D_INFO, "  Type            - 0x%02x\n", PcdVariable->Type));
357   DEBUG ((EFI_D_INFO, "  Attributes      - 0x%08x\n", PcdVariable->Attributes));
358   DEBUG ((EFI_D_INFO, "  Guid            - %g\n", &PcdVariable->Guid));
359   DEBUG ((EFI_D_INFO, "  Name            - %s\n", PcdVariable + 1));
360 
361   //
362   // For Pcd ValidData header align.
363   //
364   PcdValidData = (VAR_CHECK_PCD_VALID_DATA_HEADER *) HEADER_ALIGN (((UINTN) PcdVariable + PcdVariable->HeaderLength));
365   while ((UINTN) PcdValidData < ((UINTN) PcdVariable + PcdVariable->Length)) {
366     //
367     // Dump Pcd ValidData related to the Pcd Variable.
368     //
369     DumpPcdValidData (PcdValidData);
370     //
371     // For Pcd ValidData header align.
372     //
373     PcdValidData = (VAR_CHECK_PCD_VALID_DATA_HEADER *) HEADER_ALIGN (((UINTN) PcdValidData + PcdValidData->Length));
374   }
375 }
376 
377 /**
378   Dump Var Check PCD.
379 
380   @param[in] VarCheckPcdBin     Pointer to VarCheckPcdBin.
381   @param[in] VarCheckPcdBinSize VarCheckPcdBin size.
382 
383 **/
384 VOID
DumpVarCheckPcd(IN VOID * VarCheckPcdBin,IN UINTN VarCheckPcdBinSize)385 DumpVarCheckPcd (
386   IN VOID   *VarCheckPcdBin,
387   IN UINTN  VarCheckPcdBinSize
388   )
389 {
390   VAR_CHECK_PCD_VARIABLE_HEADER     *PcdVariable;
391 
392   DEBUG ((EFI_D_INFO, "DumpVarCheckPcd\n"));
393 
394   //
395   // For Pcd Variable header align.
396   //
397   PcdVariable = (VAR_CHECK_PCD_VARIABLE_HEADER *) HEADER_ALIGN (VarCheckPcdBin);
398   while ((UINTN) PcdVariable < ((UINTN) VarCheckPcdBin + VarCheckPcdBinSize)) {
399     DumpPcdVariable (PcdVariable);
400     //
401     // For Pcd Variable header align.
402     //
403     PcdVariable = (VAR_CHECK_PCD_VARIABLE_HEADER *) HEADER_ALIGN (((UINTN) PcdVariable + PcdVariable->Length));
404   }
405 }
406 #endif
407 
408 /**
409   Locate VarCheckPcdBin.
410 
411 **/
412 VOID
413 EFIAPI
LocateVarCheckPcdBin(VOID)414 LocateVarCheckPcdBin (
415   VOID
416   )
417 {
418   EFI_STATUS                        Status;
419   VAR_CHECK_PCD_VARIABLE_HEADER     *VarCheckPcdBin;
420   UINTN                             VarCheckPcdBinSize;
421 
422   //
423   // Search the VarCheckPcdBin from the first RAW section of current FFS.
424   //
425   Status = GetSectionFromFfs (
426              EFI_SECTION_RAW,
427              0,
428              (VOID **) &VarCheckPcdBin,
429              &VarCheckPcdBinSize
430              );
431   if (!EFI_ERROR (Status)) {
432     //
433     // AllocateRuntimeZeroPool () from MemoryAllocateLib is used for runtime access
434     // in SetVariable check handler.
435     //
436     mVarCheckPcdBin = AllocateRuntimeCopyPool (VarCheckPcdBinSize, VarCheckPcdBin);
437     ASSERT (mVarCheckPcdBin != NULL);
438     mVarCheckPcdBinSize = VarCheckPcdBinSize;
439     FreePool (VarCheckPcdBin);
440 
441     DEBUG ((EFI_D_INFO, "VarCheckPcdBin - at 0x%x size = 0x%x\n", mVarCheckPcdBin, mVarCheckPcdBinSize));
442 
443 #ifdef DUMP_VAR_CHECK_PCD
444     DEBUG_CODE (
445       DumpVarCheckPcd (mVarCheckPcdBin, mVarCheckPcdBinSize);
446     );
447 #endif
448   } else {
449     DEBUG ((EFI_D_INFO, "[VarCheckPcd] No VarCheckPcdBin found at the first RAW section\n"));
450   }
451 }
452 
453 /**
454   Constructor function of VarCheckPcdLib to register var check PCD handler.
455 
456   @param[in] ImageHandle    The firmware allocated handle for the EFI image.
457   @param[in] SystemTable    A pointer to the EFI System Table.
458 
459   @retval EFI_SUCCESS       The constructor executed correctly.
460 
461 **/
462 EFI_STATUS
463 EFIAPI
VarCheckPcdLibNullClassConstructor(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE * SystemTable)464 VarCheckPcdLibNullClassConstructor (
465   IN EFI_HANDLE             ImageHandle,
466   IN EFI_SYSTEM_TABLE       *SystemTable
467   )
468 {
469   LocateVarCheckPcdBin ();
470   VarCheckLibRegisterAddressPointer ((VOID **) &mVarCheckPcdBin);
471   VarCheckLibRegisterSetVariableCheckHandler (SetVariableCheckHandlerPcd);
472 
473   return EFI_SUCCESS;
474 }
475