1 /** @file
2   FIT based microcode shadow PEIM.
3 
4 Copyright (c) 2020, Intel Corporation. All rights reserved.<BR>
5 SPDX-License-Identifier: BSD-2-Clause-Patent
6 
7 **/
8 
9 #include <PiPei.h>
10 #include <Ppi/ShadowMicrocode.h>
11 #include <Library/PeiServicesLib.h>
12 #include <Library/HobLib.h>
13 #include <Library/DebugLib.h>
14 #include <Library/BaseMemoryLib.h>
15 #include <Library/MemoryAllocationLib.h>
16 #include <IndustryStandard/FirmwareInterfaceTable.h>
17 #include <Register/Intel/Microcode.h>
18 #include <Register/Intel/Cpuid.h>
19 #include <Guid/MicrocodeShadowInfoHob.h>
20 //
21 // Data structure for microcode patch information
22 //
23 typedef struct {
24   UINTN    Address;
25   UINTN    Size;
26 } MICROCODE_PATCH_INFO;
27 
28 /**
29   Shadow microcode update patches to memory.
30 
31   The function is used for shadowing microcode update patches to a continuous memory.
32   It shall allocate memory buffer and only shadow the microcode patches for those
33   processors specified by MicrocodeCpuId array. The checksum verification may be
34   skiped in this function so the caller must perform checksum verification before
35   using the microcode patches in returned memory buffer.
36 
37   @param[in]  This                 The PPI instance pointer.
38   @param[in]  CpuIdCount           Number of elements in MicrocodeCpuId array.
39   @param[in]  MicrocodeCpuId       A pointer to an array of EDKII_PEI_MICROCODE_CPU_ID
40                                    structures.
41   @param[out] BufferSize           Pointer to receive the total size of Buffer.
42   @param[out] Buffer               Pointer to receive address of allocated memory
43                                    with microcode patches data in it.
44 
45   @retval EFI_SUCCESS              The microcode has been shadowed to memory.
46   @retval EFI_OUT_OF_RESOURCES     The operation fails due to lack of resources.
47 
48 **/
49 EFI_STATUS
50 EFIAPI
51 ShadowMicrocode (
52   IN  EDKII_PEI_SHADOW_MICROCODE_PPI        *This,
53   IN  UINTN                                 CpuIdCount,
54   IN  EDKII_PEI_MICROCODE_CPU_ID            *MicrocodeCpuId,
55   OUT UINTN                                 *BufferSize,
56   OUT VOID                                  **Buffer
57   );
58 
59 
60 EDKII_PEI_SHADOW_MICROCODE_PPI   mPeiShadowMicrocodePpi = {
61   ShadowMicrocode
62 };
63 
64 
65 EFI_PEI_PPI_DESCRIPTOR           mPeiShadowMicrocodePpiList[] = {
66   {
67     EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST,
68     &gEdkiiPeiShadowMicrocodePpiGuid,
69     &mPeiShadowMicrocodePpi
70   }
71 };
72 
73 /**
74   Determine if a microcode patch matchs the specific processor signature and flag.
75 
76   @param[in]  CpuIdCount            Number of elements in MicrocodeCpuId array.
77   @param[in]  MicrocodeCpuId        A pointer to an array of EDKII_PEI_MICROCODE_CPU_ID
78                                     structures.
79   @param[in]  ProcessorSignature    The processor signature field value
80                                     supported by a microcode patch.
81   @param[in]  ProcessorFlags        The prcessor flags field value supported by
82                                     a microcode patch.
83 
84   @retval TRUE     The specified microcode patch will be loaded.
85   @retval FALSE    The specified microcode patch will not be loaded.
86 **/
87 BOOLEAN
IsProcessorMatchedMicrocodePatch(IN UINTN CpuIdCount,IN EDKII_PEI_MICROCODE_CPU_ID * MicrocodeCpuId,IN UINT32 ProcessorSignature,IN UINT32 ProcessorFlags)88 IsProcessorMatchedMicrocodePatch (
89   IN  UINTN                           CpuIdCount,
90   IN  EDKII_PEI_MICROCODE_CPU_ID      *MicrocodeCpuId,
91   IN UINT32                           ProcessorSignature,
92   IN UINT32                           ProcessorFlags
93   )
94 {
95   UINTN          Index;
96 
97   for (Index = 0; Index < CpuIdCount; Index++) {
98     if ((ProcessorSignature == MicrocodeCpuId[Index].ProcessorSignature) &&
99         (ProcessorFlags & (1 << MicrocodeCpuId[Index].PlatformId)) != 0) {
100       return TRUE;
101     }
102   }
103 
104   return FALSE;
105 }
106 
107 /**
108   Check the 'ProcessorSignature' and 'ProcessorFlags' of the microcode
109   patch header with the CPUID and PlatformID of the processors within
110   system to decide if it will be copied into memory.
111 
112   @param[in]  CpuIdCount            Number of elements in MicrocodeCpuId array.
113   @param[in]  MicrocodeCpuId        A pointer to an array of EDKII_PEI_MICROCODE_CPU_ID
114                                     structures.
115   @param[in]  MicrocodeEntryPoint   The pointer to the microcode patch header.
116 
117   @retval TRUE     The specified microcode patch need to be loaded.
118   @retval FALSE    The specified microcode patch dosen't need to be loaded.
119 **/
120 BOOLEAN
IsMicrocodePatchNeedLoad(IN UINTN CpuIdCount,IN EDKII_PEI_MICROCODE_CPU_ID * MicrocodeCpuId,CPU_MICROCODE_HEADER * MicrocodeEntryPoint)121 IsMicrocodePatchNeedLoad (
122   IN  UINTN                         CpuIdCount,
123   IN  EDKII_PEI_MICROCODE_CPU_ID    *MicrocodeCpuId,
124   CPU_MICROCODE_HEADER              *MicrocodeEntryPoint
125   )
126 {
127   BOOLEAN                                NeedLoad;
128   UINTN                                  DataSize;
129   UINTN                                  TotalSize;
130   CPU_MICROCODE_EXTENDED_TABLE_HEADER    *ExtendedTableHeader;
131   UINT32                                 ExtendedTableCount;
132   CPU_MICROCODE_EXTENDED_TABLE           *ExtendedTable;
133   UINTN                                  Index;
134 
135   if (FeaturePcdGet (PcdShadowAllMicrocode)) {
136     return TRUE;
137   }
138 
139   //
140   // Check the 'ProcessorSignature' and 'ProcessorFlags' in microcode patch header.
141   //
142   NeedLoad = IsProcessorMatchedMicrocodePatch (
143                CpuIdCount,
144                MicrocodeCpuId,
145                MicrocodeEntryPoint->ProcessorSignature.Uint32,
146                MicrocodeEntryPoint->ProcessorFlags
147                );
148 
149   //
150   // If the Extended Signature Table exists, check if the processor is in the
151   // support list
152   //
153   DataSize  = MicrocodeEntryPoint->DataSize;
154   TotalSize = (DataSize == 0) ? 2048 : MicrocodeEntryPoint->TotalSize;
155   if ((!NeedLoad) && (DataSize != 0) &&
156       (TotalSize - DataSize > sizeof (CPU_MICROCODE_HEADER) +
157                               sizeof (CPU_MICROCODE_EXTENDED_TABLE_HEADER))) {
158     ExtendedTableHeader = (CPU_MICROCODE_EXTENDED_TABLE_HEADER *) ((UINT8 *) (MicrocodeEntryPoint)
159                             + DataSize + sizeof (CPU_MICROCODE_HEADER));
160     ExtendedTableCount  = ExtendedTableHeader->ExtendedSignatureCount;
161     ExtendedTable       = (CPU_MICROCODE_EXTENDED_TABLE *) (ExtendedTableHeader + 1);
162 
163     for (Index = 0; Index < ExtendedTableCount; Index ++) {
164       //
165       // Check the 'ProcessorSignature' and 'ProcessorFlag' of the Extended
166       // Signature Table entry with the CPUID and PlatformID of the processors
167       // within system to decide if it will be copied into memory
168       //
169       NeedLoad = IsProcessorMatchedMicrocodePatch (
170                    CpuIdCount,
171                    MicrocodeCpuId,
172                    ExtendedTable->ProcessorSignature.Uint32,
173                    ExtendedTable->ProcessorFlag
174                    );
175       if (NeedLoad) {
176         break;
177       }
178       ExtendedTable ++;
179     }
180   }
181 
182   return NeedLoad;
183 }
184 
185 /**
186   Actual worker function that shadows the required microcode patches into memory.
187 
188   @param[in]       Patches          The pointer to an array of information on
189                                     the microcode patches that will be loaded
190                                     into memory.
191   @param[in]       PatchCount       The number of microcode patches that will
192                                     be loaded into memory.
193   @param[in]       TotalLoadSize    The total size of all the microcode patches
194                                     to be loaded.
195   @param[out] BufferSize            Pointer to receive the total size of Buffer.
196   @param[out] Buffer                Pointer to receive address of allocated memory
197                                     with microcode patches data in it.
198 **/
199 VOID
ShadowMicrocodePatchWorker(IN MICROCODE_PATCH_INFO * Patches,IN UINTN PatchCount,IN UINTN TotalLoadSize,OUT UINTN * BufferSize,OUT VOID ** Buffer)200 ShadowMicrocodePatchWorker (
201   IN  MICROCODE_PATCH_INFO       *Patches,
202   IN  UINTN                      PatchCount,
203   IN  UINTN                      TotalLoadSize,
204   OUT UINTN                      *BufferSize,
205   OUT VOID                       **Buffer
206   )
207 {
208   UINTN                                     Index;
209   VOID                                      *MicrocodePatchInRam;
210   UINT8                                     *Walker;
211   EDKII_MICROCODE_SHADOW_INFO_HOB           *MicrocodeShadowHob;
212   UINTN                                     HobDataLength;
213   UINT64                                    *MicrocodeAddressInMemory;
214   EFI_MICROCODE_STORAGE_TYPE_FLASH_CONTEXT  *Flashcontext;
215 
216   ASSERT ((Patches != NULL) && (PatchCount != 0));
217 
218   //
219   // Init microcode shadow info HOB content.
220   //
221   HobDataLength = sizeof (EDKII_MICROCODE_SHADOW_INFO_HOB) +
222                   sizeof (UINT64) * PatchCount * 2;
223   MicrocodeShadowHob  = AllocatePool (HobDataLength);
224   if (MicrocodeShadowHob == NULL) {
225     ASSERT (FALSE);
226     return;
227   }
228   MicrocodeShadowHob->MicrocodeCount = PatchCount;
229   CopyGuid (
230     &MicrocodeShadowHob->StorageType,
231     &gEdkiiMicrocodeStorageTypeFlashGuid
232     );
233   MicrocodeAddressInMemory = (UINT64 *) (MicrocodeShadowHob + 1);
234   Flashcontext = (EFI_MICROCODE_STORAGE_TYPE_FLASH_CONTEXT *) (MicrocodeAddressInMemory + PatchCount);
235 
236   //
237   // Allocate memory for microcode shadow operation.
238   //
239   MicrocodePatchInRam = AllocatePages (EFI_SIZE_TO_PAGES (TotalLoadSize));
240   if (MicrocodePatchInRam == NULL) {
241     ASSERT (FALSE);
242     return;
243   }
244 
245   //
246   // Shadow all the required microcode patches into memory
247   //
248   for (Walker = MicrocodePatchInRam, Index = 0; Index < PatchCount; Index++) {
249     CopyMem (
250       Walker,
251       (VOID *) Patches[Index].Address,
252       Patches[Index].Size
253       );
254     MicrocodeAddressInMemory[Index] = (UINT64) (UINTN) Walker;
255     Flashcontext->MicrocodeAddressInFlash[Index]  = (UINT64) Patches[Index].Address;
256     Walker += Patches[Index].Size;
257   }
258 
259   //
260   // Update the microcode patch related fields in CpuMpData
261   //
262   *Buffer     = (VOID *) (UINTN) MicrocodePatchInRam;
263   *BufferSize = TotalLoadSize;
264 
265   BuildGuidDataHob (
266     &gEdkiiMicrocodeShadowInfoHobGuid,
267     MicrocodeShadowHob,
268     HobDataLength
269     );
270 
271   DEBUG ((
272     DEBUG_INFO,
273     "%a: Required microcode patches have been loaded at 0x%lx, with size 0x%lx.\n",
274     __FUNCTION__, *Buffer, *BufferSize
275     ));
276 
277   return;
278 }
279 
280 /**
281   Check if FIT table content is valid according to FIT BIOS specification.
282 
283 **/
284 BOOLEAN
IsValidFitTable(IN UINT64 FitPointer)285 IsValidFitTable (
286   IN  UINT64               FitPointer
287   )
288 {
289   UINT64                            FitEnding;
290   FIRMWARE_INTERFACE_TABLE_ENTRY    *FitEntry;
291   UINT32                            EntryNum;
292   UINT32                            Type0Count;
293   UINT32                            Index;
294 
295   //
296   // The entire FIT table must reside with in the firmware address range
297   // of (4GB-16MB) to (4GB-40h).
298   //
299   if ((FitPointer < (SIZE_4GB - SIZE_16MB)) || (FitPointer >= (SIZE_4GB - 0x40))) {
300     //
301     // Invalid FIT address, treat it as no FIT table.
302     //
303     DEBUG ((DEBUG_ERROR, "Error: Invalid FIT pointer 0x%p.\n", FitPointer));
304     return FALSE;
305   }
306 
307   //
308   // Check FIT header.
309   //
310   FitEntry = (FIRMWARE_INTERFACE_TABLE_ENTRY *) (UINTN) FitPointer;
311   if ((FitEntry[0].Type != FIT_TYPE_00_HEADER) ||
312       (FitEntry[0].Address != FIT_TYPE_00_SIGNATURE)) {
313     DEBUG ((DEBUG_ERROR, "Error: Invalid FIT header.\n"));
314     return FALSE;
315   }
316 
317   //
318   // Check FIT version.
319   //
320   if (FitEntry[0].Version != FIT_TYPE_VERSION) {
321     DEBUG ((DEBUG_ERROR, "Error: Unsupported FIT version.\n"));
322     return FALSE;
323   }
324 
325   //
326   // Check FIT ending address in valid range.
327   //
328   EntryNum = *(UINT32 *)(&FitEntry[0].Size[0]) & 0xFFFFFF;
329   FitEnding = FitPointer + sizeof (FIRMWARE_INTERFACE_TABLE_ENTRY) * EntryNum;
330   if (FitEnding  > (SIZE_4GB - 0x40)) {
331     DEBUG ((DEBUG_ERROR, "Error: FIT table exceeds valid range.\n"));
332     return FALSE;
333   }
334 
335   //
336   // Calculate FIT checksum if Checksum Valid bit is set.
337   //
338   if (FitEntry[0].C_V &&
339       CalculateSum8 ((UINT8*) FitEntry, sizeof (FIRMWARE_INTERFACE_TABLE_ENTRY) * EntryNum) != 0) {
340     DEBUG ((DEBUG_ERROR, "Error: Invalid FIT checksum.\n"));
341     return FALSE;
342   }
343 
344   //
345   // Check type 0 entry count.
346   //
347   Type0Count = 0;
348   for (Index = 0; Index < EntryNum; Index++) {
349     if (FitEntry[Index].Type == FIT_TYPE_00_HEADER) {
350       Type0Count++;
351     }
352   }
353   if (Type0Count != 1) {
354     DEBUG ((DEBUG_ERROR, "Error: There can be only one Type 0 entry in FIT.\n"));
355     return FALSE;
356   }
357 
358   return TRUE;
359 }
360 
361 
362 /**
363   Shadow microcode update patches to memory.
364 
365   The function is used for shadowing microcode update patches to a continuous memory.
366   It shall allocate memory buffer and only shadow the microcode patches for those
367   processors specified by MicrocodeCpuId array. The checksum verification may be
368   skiped in this function so the caller must perform checksum verification before
369   using the microcode patches in returned memory buffer.
370 
371   @param[in]  This                 The PPI instance pointer.
372   @param[in]  CpuIdCount           Number of elements in MicrocodeCpuId array.
373   @param[in]  MicrocodeCpuId       A pointer to an array of EDKII_PEI_MICROCODE_CPU_ID
374                                    structures.
375   @param[out] BufferSize           Pointer to receive the total size of Buffer.
376   @param[out] Buffer               Pointer to receive address of allocated memory
377                                    with microcode patches data in it.
378 
379   @retval EFI_SUCCESS              The microcode has been shadowed to memory.
380   @retval EFI_INVALID_PARAMETER    BufferSize or Buffer is NULL.
381   @retval EFI_INVALID_PARAMETER    CpuIdCount not equal to 0 and MicrocodeCpuId is NULL.
382   @retval EFI_NOT_FOUND            No valid microcode found.
383   @retval EFI_OUT_OF_RESOURCES     The operation fails due to lack of resources.
384 
385 **/
386 EFI_STATUS
387 EFIAPI
ShadowMicrocode(IN EDKII_PEI_SHADOW_MICROCODE_PPI * This,IN UINTN CpuIdCount,IN EDKII_PEI_MICROCODE_CPU_ID * MicrocodeCpuId,OUT UINTN * BufferSize,OUT VOID ** Buffer)388 ShadowMicrocode (
389   IN  EDKII_PEI_SHADOW_MICROCODE_PPI        *This,
390   IN  UINTN                                 CpuIdCount,
391   IN  EDKII_PEI_MICROCODE_CPU_ID            *MicrocodeCpuId,
392   OUT UINTN                                 *BufferSize,
393   OUT VOID                                  **Buffer
394   )
395 {
396   EFI_STATUS                        Status;
397   UINT64                            FitPointer;
398   FIRMWARE_INTERFACE_TABLE_ENTRY    *FitEntry;
399   UINT32                            EntryNum;
400   UINT32                            Index;
401   MICROCODE_PATCH_INFO              *PatchInfoBuffer;
402   UINTN                             MaxPatchNumber;
403   CPU_MICROCODE_HEADER              *MicrocodeEntryPoint;
404   UINTN                             PatchCount;
405   UINTN                             DataSize;
406   UINTN                             TotalSize;
407   UINTN                             TotalLoadSize;
408 
409   if (BufferSize == NULL || Buffer == NULL) {
410     return EFI_INVALID_PARAMETER;
411   }
412 
413   if (CpuIdCount != 0 && MicrocodeCpuId == NULL) {
414     return EFI_INVALID_PARAMETER;
415   }
416 
417   FitPointer = *(UINT64 *) (UINTN) FIT_POINTER_ADDRESS;
418   if (!IsValidFitTable (FitPointer)) {
419     return EFI_NOT_FOUND;
420   }
421 
422   //
423   // Calculate microcode entry number
424   //
425   FitEntry = (FIRMWARE_INTERFACE_TABLE_ENTRY *) (UINTN) FitPointer;
426   EntryNum = *(UINT32 *)(&FitEntry[0].Size[0]) & 0xFFFFFF;
427   MaxPatchNumber = 0;
428   for (Index = 0; Index < EntryNum; Index++) {
429     if (FitEntry[Index].Type == FIT_TYPE_01_MICROCODE) {
430       MaxPatchNumber++;
431     }
432   }
433   if (MaxPatchNumber == 0) {
434     return EFI_NOT_FOUND;
435   }
436 
437   PatchInfoBuffer = AllocatePool (MaxPatchNumber * sizeof (MICROCODE_PATCH_INFO));
438   if (PatchInfoBuffer == NULL) {
439     return EFI_OUT_OF_RESOURCES;
440   }
441 
442   //
443   // Fill up microcode patch info buffer according to FIT table.
444   //
445   PatchCount = 0;
446   TotalLoadSize = 0;
447   for (Index = 0; Index < EntryNum; Index++) {
448     if (FitEntry[Index].Type == FIT_TYPE_01_MICROCODE) {
449       MicrocodeEntryPoint = (CPU_MICROCODE_HEADER *) (UINTN) FitEntry[Index].Address;
450 
451       if (*(UINT32 *) MicrocodeEntryPoint == 0xFFFFFFFF) {
452         //
453         // An empty slot for reserved microcode update, skip to check next entry.
454         //
455         continue;
456       }
457 
458       if (MicrocodeEntryPoint->HeaderVersion != 0x1) {
459         //
460         // Not a valid microcode header, skip to check next entry.
461         //
462         continue;
463       }
464 
465       DataSize  = MicrocodeEntryPoint->DataSize;
466       TotalSize = (DataSize == 0) ? 2048 : MicrocodeEntryPoint->TotalSize;
467       if ( (UINTN)MicrocodeEntryPoint > (MAX_ADDRESS - TotalSize) ||
468            (DataSize & 0x3) != 0 ||
469            (TotalSize & (SIZE_1KB - 1)) != 0 ||
470            TotalSize < DataSize
471         ) {
472         //
473         // Not a valid microcode header, skip to check next entry.
474         //
475         continue;
476       }
477 
478       if (IsMicrocodePatchNeedLoad (CpuIdCount, MicrocodeCpuId, MicrocodeEntryPoint)) {
479         PatchInfoBuffer[PatchCount].Address     = (UINTN) MicrocodeEntryPoint;
480         PatchInfoBuffer[PatchCount].Size        = TotalSize;
481         TotalLoadSize += TotalSize;
482         PatchCount++;
483       }
484     }
485   }
486 
487   if (PatchCount != 0) {
488     DEBUG ((
489       DEBUG_INFO,
490       "%a: 0x%x microcode patches will be loaded into memory, with size 0x%x.\n",
491       __FUNCTION__, PatchCount, TotalLoadSize
492       ));
493 
494     ShadowMicrocodePatchWorker (PatchInfoBuffer, PatchCount, TotalLoadSize, BufferSize, Buffer);
495     Status = EFI_SUCCESS;
496   } else {
497     Status = EFI_NOT_FOUND;
498   }
499 
500   FreePool (PatchInfoBuffer);
501   return Status;
502 }
503 
504 
505 /**
506   Platform Init PEI module entry point
507 
508   @param[in]  FileHandle           Not used.
509   @param[in]  PeiServices          General purpose services available to every PEIM.
510 
511   @retval     EFI_SUCCESS          The function completes successfully
512   @retval     EFI_OUT_OF_RESOURCES Insufficient resources to create database
513 **/
514 EFI_STATUS
515 EFIAPI
ShadowMicrocodePeimInit(IN EFI_PEI_FILE_HANDLE FileHandle,IN CONST EFI_PEI_SERVICES ** PeiServices)516 ShadowMicrocodePeimInit (
517   IN       EFI_PEI_FILE_HANDLE  FileHandle,
518   IN CONST EFI_PEI_SERVICES     **PeiServices
519   )
520 {
521   EFI_STATUS                       Status;
522 
523   //
524   // Install EDKII Shadow Microcode PPI
525   //
526   Status = PeiServicesInstallPpi(mPeiShadowMicrocodePpiList);
527   ASSERT_EFI_ERROR (Status);
528 
529   return Status;
530 }
531