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