IntelSiliconPkg MicrocodeUpdateDxe: Honor FIT table
[mirror_edk2.git] / IntelSiliconPkg / Feature / Capsule / MicrocodeUpdateDxe / MicrocodeUpdate.c
CommitLineData
88266859
JY
1/** @file\r
2 SetImage instance to update Microcode.\r
3\r
4 Caution: This module requires additional review when modified.\r
5 This module will have external input - capsule image.\r
6 This external input must be validated carefully to avoid security issue like\r
7 buffer overflow, integer overflow.\r
8\r
9 MicrocodeWrite() and VerifyMicrocode() will receive untrusted input and do basic validation.\r
10\r
1db271df 11 Copyright (c) 2016 - 2018, Intel Corporation. All rights reserved.<BR>\r
88266859
JY
12 This program and the accompanying materials\r
13 are licensed and made available under the terms and conditions of the BSD License\r
14 which accompanies this distribution. The full text of the license may be found at\r
15 http://opensource.org/licenses/bsd-license.php\r
16\r
17 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
18 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
19\r
20**/\r
21\r
22#include "MicrocodeUpdate.h"\r
23\r
24/**\r
25 Get Microcode Region.\r
26\r
27 @param[out] MicrocodePatchAddress The address of Microcode\r
28 @param[out] MicrocodePatchRegionSize The region size of Microcode\r
29\r
30 @retval TRUE The Microcode region is returned.\r
31 @retval FALSE No Microcode region.\r
32**/\r
33BOOLEAN\r
34GetMicrocodeRegion (\r
2ed65824
JY
35 OUT VOID **MicrocodePatchAddress,\r
36 OUT UINTN *MicrocodePatchRegionSize\r
88266859
JY
37 )\r
38{\r
2ed65824
JY
39 *MicrocodePatchAddress = (VOID *)(UINTN)PcdGet64(PcdCpuMicrocodePatchAddress);\r
40 *MicrocodePatchRegionSize = (UINTN)PcdGet64(PcdCpuMicrocodePatchRegionSize);\r
88266859 41\r
2ed65824 42 if ((*MicrocodePatchAddress == NULL) || (*MicrocodePatchRegionSize == 0)) {\r
88266859
JY
43 return FALSE;\r
44 }\r
45\r
46 return TRUE;\r
47}\r
48\r
49/**\r
50 Get Microcode update signature of currently loaded Microcode update.\r
51\r
52 @return Microcode signature.\r
53\r
54**/\r
55UINT32\r
56GetCurrentMicrocodeSignature (\r
57 VOID\r
58 )\r
59{\r
60 UINT64 Signature;\r
61\r
62 AsmWriteMsr64(MSR_IA32_BIOS_SIGN_ID, 0);\r
63 AsmCpuid(CPUID_VERSION_INFO, NULL, NULL, NULL, NULL);\r
64 Signature = AsmReadMsr64(MSR_IA32_BIOS_SIGN_ID);\r
65 return (UINT32)RShiftU64(Signature, 32);\r
66}\r
67\r
68/**\r
69 Get current processor signature.\r
70\r
71 @return current processor signature.\r
72**/\r
73UINT32\r
74GetCurrentProcessorSignature (\r
75 VOID\r
76 )\r
77{\r
78 UINT32 RegEax;\r
79 AsmCpuid(CPUID_VERSION_INFO, &RegEax, NULL, NULL, NULL);\r
80 return RegEax;\r
81}\r
82\r
83/**\r
84 Get current platform ID.\r
85\r
86 @return current platform ID.\r
87**/\r
88UINT8\r
89GetCurrentPlatformId (\r
90 VOID\r
91 )\r
92{\r
93 UINT8 PlatformId;\r
94\r
95 PlatformId = (UINT8)AsmMsrBitFieldRead64(MSR_IA32_PLATFORM_ID, 50, 52);\r
96 return PlatformId;\r
97}\r
98\r
99/**\r
100 Load new Microcode.\r
101\r
102 @param[in] Address The address of new Microcode.\r
103\r
104 @return Loaded Microcode signature.\r
105\r
106**/\r
107UINT32\r
108LoadMicrocode (\r
109 IN UINT64 Address\r
110 )\r
111{\r
112 AsmWriteMsr64(MSR_IA32_BIOS_UPDT_TRIG, Address);\r
113 return GetCurrentMicrocodeSignature();\r
114}\r
115\r
2ed65824 116/**\r
31d060d9
JY
117 Load Microcode on an Application Processor.\r
118 The function prototype for invoking a function on an Application Processor.\r
119\r
120 @param[in,out] Buffer The pointer to private data buffer.\r
121**/\r
122VOID\r
123EFIAPI\r
124MicrocodeLoadAp (\r
125 IN OUT VOID *Buffer\r
126 )\r
127{\r
128 MICROCODE_LOAD_BUFFER *MicrocodeLoadBuffer;\r
129\r
130 MicrocodeLoadBuffer = Buffer;\r
131 MicrocodeLoadBuffer->Revision = LoadMicrocode (MicrocodeLoadBuffer->Address);\r
132}\r
133\r
134/**\r
135 Load new Microcode on this processor\r
2ed65824 136\r
31d060d9
JY
137 @param[in] MicrocodeFmpPrivate The Microcode driver private data\r
138 @param[in] CpuIndex The index of the processor.\r
139 @param[in] Address The address of new Microcode.\r
140\r
141 @return Loaded Microcode signature.\r
2ed65824 142\r
2ed65824 143**/\r
31d060d9
JY
144UINT32\r
145LoadMicrocodeOnThis (\r
146 IN MICROCODE_FMP_PRIVATE_DATA *MicrocodeFmpPrivate,\r
147 IN UINTN CpuIndex,\r
148 IN UINT64 Address\r
2ed65824
JY
149 )\r
150{\r
31d060d9
JY
151 EFI_STATUS Status;\r
152 EFI_MP_SERVICES_PROTOCOL *MpService;\r
153 MICROCODE_LOAD_BUFFER MicrocodeLoadBuffer;\r
2ed65824 154\r
31d060d9
JY
155 if (CpuIndex == MicrocodeFmpPrivate->BspIndex) {\r
156 return LoadMicrocode (Address);\r
157 } else {\r
158 MpService = MicrocodeFmpPrivate->MpService;\r
159 MicrocodeLoadBuffer.Address = Address;\r
160 MicrocodeLoadBuffer.Revision = 0;\r
161 Status = MpService->StartupThisAP (\r
162 MpService,\r
163 MicrocodeLoadAp,\r
164 CpuIndex,\r
165 NULL,\r
166 0,\r
167 &MicrocodeLoadBuffer,\r
168 NULL\r
169 );\r
170 ASSERT_EFI_ERROR(Status);\r
171 return MicrocodeLoadBuffer.Revision;\r
2ed65824 172 }\r
31d060d9
JY
173}\r
174\r
175/**\r
176 Collect processor information.\r
177 The function prototype for invoking a function on an Application Processor.\r
178\r
179 @param[in,out] Buffer The pointer to private data buffer.\r
180**/\r
181VOID\r
182EFIAPI\r
183CollectProcessorInfo (\r
184 IN OUT VOID *Buffer\r
185 )\r
186{\r
187 PROCESSOR_INFO *ProcessorInfo;\r
188\r
189 ProcessorInfo = Buffer;\r
190 ProcessorInfo->ProcessorSignature = GetCurrentProcessorSignature();\r
191 ProcessorInfo->PlatformId = GetCurrentPlatformId();\r
192 ProcessorInfo->MicrocodeRevision = GetCurrentMicrocodeSignature();\r
2ed65824
JY
193}\r
194\r
88266859
JY
195/**\r
196 Get current Microcode information.\r
197\r
31d060d9
JY
198 The ProcessorInformation (BspIndex/ProcessorCount/ProcessorInfo)\r
199 in MicrocodeFmpPrivate must be initialized.\r
200\r
201 The MicrocodeInformation (DescriptorCount/ImageDescriptor/MicrocodeInfo)\r
202 in MicrocodeFmpPrivate may not be avaiable in this function.\r
2ed65824
JY
203\r
204 @param[in] MicrocodeFmpPrivate The Microcode driver private data\r
205 @param[in] DescriptorCount The count of Microcode ImageDescriptor allocated.\r
206 @param[out] ImageDescriptor Microcode ImageDescriptor\r
207 @param[out] MicrocodeInfo Microcode information\r
88266859
JY
208\r
209 @return Microcode count\r
210**/\r
211UINTN\r
212GetMicrocodeInfo (\r
2ed65824
JY
213 IN MICROCODE_FMP_PRIVATE_DATA *MicrocodeFmpPrivate,\r
214 IN UINTN DescriptorCount, OPTIONAL\r
88266859 215 OUT EFI_FIRMWARE_IMAGE_DESCRIPTOR *ImageDescriptor, OPTIONAL\r
2ed65824 216 OUT MICROCODE_INFO *MicrocodeInfo OPTIONAL\r
88266859
JY
217 )\r
218{\r
2ed65824
JY
219 VOID *MicrocodePatchAddress;\r
220 UINTN MicrocodePatchRegionSize;\r
88266859
JY
221 CPU_MICROCODE_HEADER *MicrocodeEntryPoint;\r
222 UINTN MicrocodeEnd;\r
223 UINTN TotalSize;\r
224 UINTN Count;\r
225 UINT64 ImageAttributes;\r
2ed65824 226 BOOLEAN IsInUse;\r
31d060d9
JY
227 EFI_STATUS Status;\r
228 UINT32 AttemptStatus;\r
229 UINTN TargetCpuIndex;\r
88266859 230\r
2ed65824
JY
231 MicrocodePatchAddress = MicrocodeFmpPrivate->MicrocodePatchAddress;\r
232 MicrocodePatchRegionSize = MicrocodeFmpPrivate->MicrocodePatchRegionSize;\r
233\r
234 DEBUG((DEBUG_INFO, "Microcode Region - 0x%x - 0x%x\n", MicrocodePatchAddress, MicrocodePatchRegionSize));\r
88266859
JY
235\r
236 Count = 0;\r
88266859 237\r
2ed65824 238 MicrocodeEnd = (UINTN)MicrocodePatchAddress + MicrocodePatchRegionSize;\r
88266859
JY
239 MicrocodeEntryPoint = (CPU_MICROCODE_HEADER *) (UINTN) MicrocodePatchAddress;\r
240 do {\r
241 if (MicrocodeEntryPoint->HeaderVersion == 0x1 && MicrocodeEntryPoint->LoaderRevision == 0x1) {\r
242 //\r
243 // It is the microcode header. It is not the padding data between microcode patches\r
244 // becasue the padding data should not include 0x00000001 and it should be the repeated\r
245 // byte format (like 0xXYXYXYXY....).\r
246 //\r
247 if (MicrocodeEntryPoint->DataSize == 0) {\r
248 TotalSize = 2048;\r
249 } else {\r
250 TotalSize = MicrocodeEntryPoint->TotalSize;\r
251 }\r
252\r
31d060d9
JY
253 TargetCpuIndex = (UINTN)-1;\r
254 Status = VerifyMicrocode(MicrocodeFmpPrivate, MicrocodeEntryPoint, TotalSize, FALSE, &AttemptStatus, NULL, &TargetCpuIndex);\r
255 if (!EFI_ERROR(Status)) {\r
256 IsInUse = TRUE;\r
257 ASSERT (TargetCpuIndex < MicrocodeFmpPrivate->ProcessorCount);\r
258 MicrocodeFmpPrivate->ProcessorInfo[TargetCpuIndex].MicrocodeIndex = Count;\r
259 } else {\r
260 IsInUse = FALSE;\r
261 }\r
2ed65824 262\r
88266859
JY
263 if (ImageDescriptor != NULL && DescriptorCount > Count) {\r
264 ImageDescriptor[Count].ImageIndex = (UINT8)(Count + 1);\r
265 CopyGuid (&ImageDescriptor[Count].ImageTypeId, &gMicrocodeFmpImageTypeIdGuid);\r
266 ImageDescriptor[Count].ImageId = LShiftU64(MicrocodeEntryPoint->ProcessorFlags, 32) + MicrocodeEntryPoint->ProcessorSignature.Uint32;\r
267 ImageDescriptor[Count].ImageIdName = NULL;\r
268 ImageDescriptor[Count].Version = MicrocodeEntryPoint->UpdateRevision;\r
269 ImageDescriptor[Count].VersionName = NULL;\r
270 ImageDescriptor[Count].Size = TotalSize;\r
271 ImageAttributes = IMAGE_ATTRIBUTE_IMAGE_UPDATABLE | IMAGE_ATTRIBUTE_RESET_REQUIRED;\r
2ed65824 272 if (IsInUse) {\r
88266859
JY
273 ImageAttributes |= IMAGE_ATTRIBUTE_IN_USE;\r
274 }\r
275 ImageDescriptor[Count].AttributesSupported = ImageAttributes | IMAGE_ATTRIBUTE_IN_USE;\r
276 ImageDescriptor[Count].AttributesSetting = ImageAttributes;\r
277 ImageDescriptor[Count].Compatibilities = 0;\r
278 ImageDescriptor[Count].LowestSupportedImageVersion = MicrocodeEntryPoint->UpdateRevision; // do not support rollback\r
279 ImageDescriptor[Count].LastAttemptVersion = 0;\r
280 ImageDescriptor[Count].LastAttemptStatus = 0;\r
281 ImageDescriptor[Count].HardwareInstance = 0;\r
282 }\r
2ed65824
JY
283 if (MicrocodeInfo != NULL && DescriptorCount > Count) {\r
284 MicrocodeInfo[Count].MicrocodeEntryPoint = MicrocodeEntryPoint;\r
285 MicrocodeInfo[Count].TotalSize = TotalSize;\r
286 MicrocodeInfo[Count].InUse = IsInUse;\r
287 }\r
88266859
JY
288 } else {\r
289 //\r
290 // It is the padding data between the microcode patches for microcode patches alignment.\r
291 // Because the microcode patch is the multiple of 1-KByte, the padding data should not\r
292 // exist if the microcode patch alignment value is not larger than 1-KByte. So, the microcode\r
293 // alignment value should be larger than 1-KByte. We could skip SIZE_1KB padding data to\r
294 // find the next possible microcode patch header.\r
295 //\r
296 MicrocodeEntryPoint = (CPU_MICROCODE_HEADER *) (((UINTN) MicrocodeEntryPoint) + SIZE_1KB);\r
297 continue;\r
298 }\r
299\r
300 Count++;\r
301 ASSERT(Count < 0xFF);\r
302\r
303 //\r
304 // Get the next patch.\r
305 //\r
306 MicrocodeEntryPoint = (CPU_MICROCODE_HEADER *) (((UINTN) MicrocodeEntryPoint) + TotalSize);\r
307 } while (((UINTN) MicrocodeEntryPoint < MicrocodeEnd));\r
308\r
309 return Count;\r
310}\r
311\r
31d060d9
JY
312/**\r
313 Return matched processor information.\r
314\r
315 @param[in] MicrocodeFmpPrivate The Microcode driver private data\r
316 @param[in] ProcessorSignature The processor signature to be matched\r
317 @param[in] ProcessorFlags The processor flags to be matched\r
318 @param[in, out] TargetCpuIndex On input, the index of target CPU which tries to match the Microcode. (UINTN)-1 means to try all.\r
319 On output, the index of target CPU which matches the Microcode.\r
320\r
321 @return matched processor information.\r
322**/\r
323PROCESSOR_INFO *\r
324GetMatchedProcessor (\r
325 IN MICROCODE_FMP_PRIVATE_DATA *MicrocodeFmpPrivate,\r
326 IN UINT32 ProcessorSignature,\r
327 IN UINT32 ProcessorFlags,\r
328 IN OUT UINTN *TargetCpuIndex\r
329 )\r
330{\r
331 UINTN Index;\r
332\r
333 if (*TargetCpuIndex != (UINTN)-1) {\r
334 Index = *TargetCpuIndex;\r
335 if ((ProcessorSignature == MicrocodeFmpPrivate->ProcessorInfo[Index].ProcessorSignature) &&\r
336 ((ProcessorFlags & (1 << MicrocodeFmpPrivate->ProcessorInfo[Index].PlatformId)) != 0)) {\r
337 return &MicrocodeFmpPrivate->ProcessorInfo[Index];\r
338 } else {\r
339 return NULL;\r
340 }\r
341 }\r
342\r
343 for (Index = 0; Index < MicrocodeFmpPrivate->ProcessorCount; Index++) {\r
344 if ((ProcessorSignature == MicrocodeFmpPrivate->ProcessorInfo[Index].ProcessorSignature) &&\r
345 ((ProcessorFlags & (1 << MicrocodeFmpPrivate->ProcessorInfo[Index].PlatformId)) != 0)) {\r
346 *TargetCpuIndex = Index;\r
347 return &MicrocodeFmpPrivate->ProcessorInfo[Index];\r
348 }\r
349 }\r
350 return NULL;\r
351}\r
352\r
88266859
JY
353/**\r
354 Verify Microcode.\r
355\r
356 Caution: This function may receive untrusted input.\r
357\r
31d060d9
JY
358 @param[in] MicrocodeFmpPrivate The Microcode driver private data\r
359 @param[in] Image The Microcode image buffer.\r
360 @param[in] ImageSize The size of Microcode image buffer in bytes.\r
361 @param[in] TryLoad Try to load Microcode or not.\r
362 @param[out] LastAttemptStatus The last attempt status, which will be recorded in ESRT and FMP EFI_FIRMWARE_IMAGE_DESCRIPTOR.\r
363 @param[out] AbortReason A pointer to a pointer to a null-terminated string providing more\r
364 details for the aborted operation. The buffer is allocated by this function\r
365 with AllocatePool(), and it is the caller's responsibility to free it with a\r
366 call to FreePool().\r
367 @param[in, out] TargetCpuIndex On input, the index of target CPU which tries to match the Microcode. (UINTN)-1 means to try all.\r
368 On output, the index of target CPU which matches the Microcode.\r
88266859
JY
369\r
370 @retval EFI_SUCCESS The Microcode image passes verification.\r
e9179788 371 @retval EFI_VOLUME_CORRUPTED The Microcode image is corrupted.\r
88266859
JY
372 @retval EFI_INCOMPATIBLE_VERSION The Microcode image version is incorrect.\r
373 @retval EFI_UNSUPPORTED The Microcode ProcessorSignature or ProcessorFlags is incorrect.\r
374 @retval EFI_SECURITY_VIOLATION The Microcode image fails to load.\r
375**/\r
376EFI_STATUS\r
377VerifyMicrocode (\r
31d060d9
JY
378 IN MICROCODE_FMP_PRIVATE_DATA *MicrocodeFmpPrivate,\r
379 IN VOID *Image,\r
380 IN UINTN ImageSize,\r
381 IN BOOLEAN TryLoad,\r
382 OUT UINT32 *LastAttemptStatus,\r
383 OUT CHAR16 **AbortReason, OPTIONAL\r
384 IN OUT UINTN *TargetCpuIndex\r
88266859
JY
385 )\r
386{\r
387 UINTN Index;\r
388 CPU_MICROCODE_HEADER *MicrocodeEntryPoint;\r
389 UINTN TotalSize;\r
390 UINTN DataSize;\r
391 UINT32 CurrentRevision;\r
31d060d9 392 PROCESSOR_INFO *ProcessorInfo;\r
88266859
JY
393 UINT32 CheckSum32;\r
394 UINTN ExtendedTableLength;\r
395 UINT32 ExtendedTableCount;\r
396 CPU_MICROCODE_EXTENDED_TABLE *ExtendedTable;\r
397 CPU_MICROCODE_EXTENDED_TABLE_HEADER *ExtendedTableHeader;\r
398 BOOLEAN CorrectMicrocode;\r
399\r
400 //\r
401 // Check HeaderVersion\r
402 //\r
403 MicrocodeEntryPoint = Image;\r
404 if (MicrocodeEntryPoint->HeaderVersion != 0x1) {\r
405 DEBUG((DEBUG_ERROR, "VerifyMicrocode - fail on HeaderVersion\n"));\r
406 *LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_INVALID_FORMAT;\r
407 if (AbortReason != NULL) {\r
408 *AbortReason = AllocateCopyPool(sizeof(L"InvalidHeaderVersion"), L"InvalidHeaderVersion");\r
409 }\r
410 return EFI_INCOMPATIBLE_VERSION;\r
411 }\r
412 //\r
413 // Check LoaderRevision\r
414 //\r
415 if (MicrocodeEntryPoint->LoaderRevision != 0x1) {\r
416 DEBUG((DEBUG_ERROR, "VerifyMicrocode - fail on LoaderRevision\n"));\r
417 *LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_INVALID_FORMAT;\r
418 if (AbortReason != NULL) {\r
419 *AbortReason = AllocateCopyPool(sizeof(L"InvalidLoaderVersion"), L"InvalidLoaderVersion");\r
420 }\r
421 return EFI_INCOMPATIBLE_VERSION;\r
422 }\r
423 //\r
1db271df 424 // Check TotalSize\r
88266859
JY
425 //\r
426 if (MicrocodeEntryPoint->DataSize == 0) {\r
427 TotalSize = 2048;\r
428 } else {\r
429 TotalSize = MicrocodeEntryPoint->TotalSize;\r
430 }\r
431 if (TotalSize <= sizeof(CPU_MICROCODE_HEADER)) {\r
432 DEBUG((DEBUG_ERROR, "VerifyMicrocode - TotalSize too small\n"));\r
433 *LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_INVALID_FORMAT;\r
434 if (AbortReason != NULL) {\r
435 *AbortReason = AllocateCopyPool(sizeof(L"InvalidTotalSize"), L"InvalidTotalSize");\r
436 }\r
437 return EFI_VOLUME_CORRUPTED;\r
438 }\r
1db271df
SZ
439 if ((TotalSize & (SIZE_1KB - 1)) != 0) {\r
440 DEBUG((DEBUG_ERROR, "VerifyMicrocode - TotalSize is not multiples of 1024 bytes (1 KBytes)\n"));\r
441 *LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_INVALID_FORMAT;\r
442 if (AbortReason != NULL) {\r
443 *AbortReason = AllocateCopyPool(sizeof(L"InvalidTotalSize"), L"InvalidTotalSize");\r
444 }\r
445 return EFI_VOLUME_CORRUPTED;\r
446 }\r
88266859 447 if (TotalSize != ImageSize) {\r
2ad34f65 448 DEBUG((DEBUG_ERROR, "VerifyMicrocode - TotalSize not equal to ImageSize\n"));\r
88266859
JY
449 *LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_INVALID_FORMAT;\r
450 if (AbortReason != NULL) {\r
451 *AbortReason = AllocateCopyPool(sizeof(L"InvalidTotalSize"), L"InvalidTotalSize");\r
452 }\r
453 return EFI_VOLUME_CORRUPTED;\r
454 }\r
455 //\r
1db271df 456 // Check DataSize\r
88266859
JY
457 //\r
458 if (MicrocodeEntryPoint->DataSize == 0) {\r
459 DataSize = 2048 - sizeof(CPU_MICROCODE_HEADER);\r
460 } else {\r
461 DataSize = MicrocodeEntryPoint->DataSize;\r
462 }\r
463 if (DataSize > TotalSize - sizeof(CPU_MICROCODE_HEADER)) {\r
464 DEBUG((DEBUG_ERROR, "VerifyMicrocode - DataSize too big\n"));\r
465 *LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_INVALID_FORMAT;\r
466 if (AbortReason != NULL) {\r
467 *AbortReason = AllocateCopyPool(sizeof(L"InvalidDataSize"), L"InvalidDataSize");\r
468 }\r
469 return EFI_VOLUME_CORRUPTED;\r
470 }\r
471 if ((DataSize & 0x3) != 0) {\r
1db271df 472 DEBUG((DEBUG_ERROR, "VerifyMicrocode - DataSize is not multiples of DWORDs\n"));\r
88266859
JY
473 *LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_INVALID_FORMAT;\r
474 if (AbortReason != NULL) {\r
475 *AbortReason = AllocateCopyPool(sizeof(L"InvalidDataSize"), L"InvalidDataSize");\r
476 }\r
477 return EFI_VOLUME_CORRUPTED;\r
478 }\r
1db271df
SZ
479 //\r
480 // Check CheckSum32\r
481 //\r
88266859
JY
482 CheckSum32 = CalculateSum32((UINT32 *)MicrocodeEntryPoint, DataSize + sizeof(CPU_MICROCODE_HEADER));\r
483 if (CheckSum32 != 0) {\r
484 DEBUG((DEBUG_ERROR, "VerifyMicrocode - fail on CheckSum32\n"));\r
485 *LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_INVALID_FORMAT;\r
486 if (AbortReason != NULL) {\r
487 *AbortReason = AllocateCopyPool(sizeof(L"InvalidChecksum"), L"InvalidChecksum");\r
488 }\r
489 return EFI_VOLUME_CORRUPTED;\r
490 }\r
491\r
492 //\r
493 // Check ProcessorSignature/ProcessorFlags\r
494 //\r
31d060d9
JY
495\r
496 ProcessorInfo = GetMatchedProcessor (MicrocodeFmpPrivate, MicrocodeEntryPoint->ProcessorSignature.Uint32, MicrocodeEntryPoint->ProcessorFlags, TargetCpuIndex);\r
497 if (ProcessorInfo == NULL) {\r
498 CorrectMicrocode = FALSE;\r
88266859
JY
499 ExtendedTableLength = TotalSize - (DataSize + sizeof(CPU_MICROCODE_HEADER));\r
500 if (ExtendedTableLength != 0) {\r
501 //\r
502 // Extended Table exist, check if the CPU in support list\r
503 //\r
504 ExtendedTableHeader = (CPU_MICROCODE_EXTENDED_TABLE_HEADER *)((UINT8 *)(MicrocodeEntryPoint) + DataSize + sizeof(CPU_MICROCODE_HEADER));\r
505 //\r
506 // Calculate Extended Checksum\r
507 //\r
3bd91ae2 508 if ((ExtendedTableLength > sizeof(CPU_MICROCODE_EXTENDED_TABLE_HEADER)) && ((ExtendedTableLength & 0x3) == 0)) {\r
88266859 509 CheckSum32 = CalculateSum32((UINT32 *)ExtendedTableHeader, ExtendedTableLength);\r
2ad34f65
HW
510 if (CheckSum32 != 0) {\r
511 //\r
512 // Checksum incorrect\r
513 //\r
514 DEBUG((DEBUG_ERROR, "VerifyMicrocode - The checksum for extended table is incorrect\n"));\r
515 } else {\r
88266859
JY
516 //\r
517 // Checksum correct\r
518 //\r
519 ExtendedTableCount = ExtendedTableHeader->ExtendedSignatureCount;\r
2ad34f65
HW
520 if (ExtendedTableCount > (ExtendedTableLength - sizeof(CPU_MICROCODE_EXTENDED_TABLE_HEADER)) / sizeof(CPU_MICROCODE_EXTENDED_TABLE)) {\r
521 DEBUG((DEBUG_ERROR, "VerifyMicrocode - ExtendedTableCount too big\n"));\r
522 } else {\r
88266859
JY
523 ExtendedTable = (CPU_MICROCODE_EXTENDED_TABLE *)(ExtendedTableHeader + 1);\r
524 for (Index = 0; Index < ExtendedTableCount; Index++) {\r
525 CheckSum32 = CalculateSum32((UINT32 *)ExtendedTable, sizeof(CPU_MICROCODE_EXTENDED_TABLE));\r
2ad34f65
HW
526 if (CheckSum32 != 0) {\r
527 DEBUG((DEBUG_ERROR, "VerifyMicrocode - The checksum for ExtendedTable entry with index 0x%x is incorrect\n", Index));\r
528 } else {\r
88266859
JY
529 //\r
530 // Verify Header\r
531 //\r
31d060d9
JY
532 ProcessorInfo = GetMatchedProcessor (MicrocodeFmpPrivate, ExtendedTable->ProcessorSignature.Uint32, ExtendedTable->ProcessorFlag, TargetCpuIndex);\r
533 if (ProcessorInfo != NULL) {\r
88266859
JY
534 //\r
535 // Find one\r
536 //\r
537 CorrectMicrocode = TRUE;\r
538 break;\r
539 }\r
540 }\r
541 ExtendedTable++;\r
542 }\r
543 }\r
544 }\r
545 }\r
546 }\r
547 if (!CorrectMicrocode) {\r
2ed65824 548 if (TryLoad) {\r
2ad34f65 549 DEBUG((DEBUG_ERROR, "VerifyMicrocode - fail on Current ProcessorSignature/ProcessorFlags\n"));\r
2ed65824 550 }\r
88266859
JY
551 *LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_INCORRECT_VERSION;\r
552 if (AbortReason != NULL) {\r
e9179788 553 *AbortReason = AllocateCopyPool(sizeof(L"UnsupportedProcessorSignature/ProcessorFlags"), L"UnsupportedProcessorSignature/ProcessorFlags");\r
88266859
JY
554 }\r
555 return EFI_UNSUPPORTED;\r
556 }\r
557 }\r
558\r
559 //\r
560 // Check UpdateRevision\r
561 //\r
31d060d9
JY
562 CurrentRevision = ProcessorInfo->MicrocodeRevision;\r
563 if ((MicrocodeEntryPoint->UpdateRevision < CurrentRevision) ||\r
564 (TryLoad && (MicrocodeEntryPoint->UpdateRevision == CurrentRevision))) {\r
2ed65824
JY
565 if (TryLoad) {\r
566 DEBUG((DEBUG_ERROR, "VerifyMicrocode - fail on UpdateRevision\n"));\r
567 }\r
88266859
JY
568 *LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_INCORRECT_VERSION;\r
569 if (AbortReason != NULL) {\r
570 *AbortReason = AllocateCopyPool(sizeof(L"IncorrectRevision"), L"IncorrectRevision");\r
571 }\r
572 return EFI_INCOMPATIBLE_VERSION;\r
573 }\r
574\r
575 //\r
576 // try load MCU\r
577 //\r
578 if (TryLoad) {\r
31d060d9 579 CurrentRevision = LoadMicrocodeOnThis(MicrocodeFmpPrivate, ProcessorInfo->CpuIndex, (UINTN)MicrocodeEntryPoint + sizeof(CPU_MICROCODE_HEADER));\r
88266859
JY
580 if (MicrocodeEntryPoint->UpdateRevision != CurrentRevision) {\r
581 DEBUG((DEBUG_ERROR, "VerifyMicrocode - fail on LoadMicrocode\n"));\r
582 *LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_AUTH_ERROR;\r
583 if (AbortReason != NULL) {\r
584 *AbortReason = AllocateCopyPool(sizeof(L"InvalidData"), L"InvalidData");\r
585 }\r
586 return EFI_SECURITY_VIOLATION;\r
587 }\r
588 }\r
589\r
590 return EFI_SUCCESS;\r
591}\r
592\r
2ed65824
JY
593/**\r
594 Get next Microcode entrypoint.\r
88266859 595\r
2ed65824
JY
596 @param[in] MicrocodeFmpPrivate The Microcode driver private data\r
597 @param[in] MicrocodeEntryPoint Current Microcode entrypoint\r
598\r
599 @return next Microcode entrypoint.\r
600**/\r
601CPU_MICROCODE_HEADER *\r
602GetNextMicrocode (\r
603 IN MICROCODE_FMP_PRIVATE_DATA *MicrocodeFmpPrivate,\r
604 IN CPU_MICROCODE_HEADER *MicrocodeEntryPoint\r
605 )\r
606{\r
607 UINTN Index;\r
608\r
609 for (Index = 0; Index < MicrocodeFmpPrivate->DescriptorCount; Index++) {\r
610 if (MicrocodeEntryPoint == MicrocodeFmpPrivate->MicrocodeInfo[Index].MicrocodeEntryPoint) {\r
611 if (Index == (UINTN)MicrocodeFmpPrivate->DescriptorCount - 1) {\r
612 // it is last one\r
613 return NULL;\r
88266859 614 } else {\r
2ed65824
JY
615 // return next one\r
616 return MicrocodeFmpPrivate->MicrocodeInfo[Index + 1].MicrocodeEntryPoint;\r
88266859 617 }\r
88266859 618 }\r
2ed65824 619 }\r
88266859 620\r
2ed65824 621 ASSERT(FALSE);\r
88266859
JY
622 return NULL;\r
623}\r
624\r
e9179788
SZ
625/**\r
626 Get next FIT Microcode entrypoint.\r
627\r
628 @param[in] MicrocodeFmpPrivate The Microcode driver private data\r
629 @param[in] MicrocodeEntryPoint Current Microcode entrypoint\r
630\r
631 @return next FIT Microcode entrypoint.\r
632**/\r
633CPU_MICROCODE_HEADER *\r
634GetNextFitMicrocode (\r
635 IN MICROCODE_FMP_PRIVATE_DATA *MicrocodeFmpPrivate,\r
636 IN CPU_MICROCODE_HEADER *MicrocodeEntryPoint\r
637 )\r
638{\r
639 UINTN Index;\r
640\r
641 for (Index = 0; Index < MicrocodeFmpPrivate->FitMicrocodeEntryCount; Index++) {\r
642 if (MicrocodeEntryPoint == MicrocodeFmpPrivate->FitMicrocodeInfo[Index].MicrocodeEntryPoint) {\r
643 if (Index == (UINTN) MicrocodeFmpPrivate->FitMicrocodeEntryCount - 1) {\r
644 // it is last one\r
645 return NULL;\r
646 } else {\r
647 // return next one\r
648 return MicrocodeFmpPrivate->FitMicrocodeInfo[Index + 1].MicrocodeEntryPoint;\r
649 }\r
650 }\r
651 }\r
652\r
653 ASSERT(FALSE);\r
654 return NULL;\r
655}\r
656\r
657/**\r
658 Find empty FIT Microcode entrypoint.\r
659\r
660 @param[in] MicrocodeFmpPrivate The Microcode driver private data\r
661 @param[in] ImageSize The size of Microcode image buffer in bytes.\r
662 @param[out] AvailableSize Available size of the empty FIT Microcode entrypoint.\r
663\r
664 @return Empty FIT Microcode entrypoint.\r
665**/\r
666CPU_MICROCODE_HEADER *\r
667FindEmptyFitMicrocode (\r
668 IN MICROCODE_FMP_PRIVATE_DATA *MicrocodeFmpPrivate,\r
669 IN UINTN ImageSize,\r
670 OUT UINTN *AvailableSize\r
671 )\r
672{\r
673 UINTN Index;\r
674 CPU_MICROCODE_HEADER *MicrocodeEntryPoint;\r
675 CPU_MICROCODE_HEADER *NextMicrocodeEntryPoint;\r
676 VOID *MicrocodePatchAddress;\r
677 UINTN MicrocodePatchRegionSize;\r
678\r
679 MicrocodePatchAddress = MicrocodeFmpPrivate->MicrocodePatchAddress;\r
680 MicrocodePatchRegionSize = MicrocodeFmpPrivate->MicrocodePatchRegionSize;\r
681\r
682 for (Index = 0; Index < MicrocodeFmpPrivate->FitMicrocodeEntryCount; Index++) {\r
683 if (MicrocodeFmpPrivate->FitMicrocodeInfo[Index].Empty) {\r
684 MicrocodeEntryPoint = MicrocodeFmpPrivate->FitMicrocodeInfo[Index].MicrocodeEntryPoint;\r
685 NextMicrocodeEntryPoint = GetNextFitMicrocode (MicrocodeFmpPrivate, MicrocodeEntryPoint);\r
686 if (NextMicrocodeEntryPoint != NULL) {\r
687 *AvailableSize = (UINTN) NextMicrocodeEntryPoint - (UINTN) MicrocodeEntryPoint;\r
688 } else {\r
689 *AvailableSize = (UINTN) MicrocodePatchAddress + MicrocodePatchRegionSize - (UINTN) MicrocodeEntryPoint;\r
690 }\r
691 if (*AvailableSize >= ImageSize) {\r
692 return MicrocodeEntryPoint;\r
693 }\r
694 }\r
695 }\r
696\r
697 return NULL;\r
698}\r
699\r
700/**\r
701 Find unused FIT Microcode entrypoint.\r
702\r
703 @param[in] MicrocodeFmpPrivate The Microcode driver private data\r
704 @param[in] ImageSize The size of Microcode image buffer in bytes.\r
705 @param[out] AvailableSize Available size of the unused FIT Microcode entrypoint.\r
706\r
707 @return Unused FIT Microcode entrypoint.\r
708**/\r
709CPU_MICROCODE_HEADER *\r
710FindUnusedFitMicrocode (\r
711 IN MICROCODE_FMP_PRIVATE_DATA *MicrocodeFmpPrivate,\r
712 IN UINTN ImageSize,\r
713 OUT UINTN *AvailableSize\r
714 )\r
715{\r
716 UINTN Index;\r
717 CPU_MICROCODE_HEADER *MicrocodeEntryPoint;\r
718 CPU_MICROCODE_HEADER *NextMicrocodeEntryPoint;\r
719 VOID *MicrocodePatchAddress;\r
720 UINTN MicrocodePatchRegionSize;\r
721\r
722 MicrocodePatchAddress = MicrocodeFmpPrivate->MicrocodePatchAddress;\r
723 MicrocodePatchRegionSize = MicrocodeFmpPrivate->MicrocodePatchRegionSize;\r
724\r
725 for (Index = 0; Index < MicrocodeFmpPrivate->FitMicrocodeEntryCount; Index++) {\r
726 if (!MicrocodeFmpPrivate->FitMicrocodeInfo[Index].InUse) {\r
727 MicrocodeEntryPoint = MicrocodeFmpPrivate->FitMicrocodeInfo[Index].MicrocodeEntryPoint;\r
728 NextMicrocodeEntryPoint = GetNextFitMicrocode (MicrocodeFmpPrivate, MicrocodeEntryPoint);\r
729 if (NextMicrocodeEntryPoint != NULL) {\r
730 *AvailableSize = (UINTN) NextMicrocodeEntryPoint - (UINTN) MicrocodeEntryPoint;\r
731 } else {\r
732 *AvailableSize = (UINTN) MicrocodePatchAddress + MicrocodePatchRegionSize - (UINTN) MicrocodeEntryPoint;\r
733 }\r
734 if (*AvailableSize >= ImageSize) {\r
735 return MicrocodeEntryPoint;\r
736 }\r
737 }\r
738 }\r
739\r
740 return NULL;\r
741}\r
742\r
88266859
JY
743/**\r
744 Get current Microcode used region size.\r
745\r
2ed65824
JY
746 @param[in] MicrocodeFmpPrivate The Microcode driver private data\r
747\r
88266859
JY
748 @return current Microcode used region size.\r
749**/\r
750UINTN\r
751GetCurrentMicrocodeUsedRegionSize (\r
2ed65824 752 IN MICROCODE_FMP_PRIVATE_DATA *MicrocodeFmpPrivate\r
88266859
JY
753 )\r
754{\r
2ed65824 755 if (MicrocodeFmpPrivate->DescriptorCount == 0) {\r
88266859
JY
756 return 0;\r
757 }\r
88266859 758\r
2ed65824
JY
759 return (UINTN)MicrocodeFmpPrivate->MicrocodeInfo[MicrocodeFmpPrivate->DescriptorCount - 1].MicrocodeEntryPoint\r
760 + (UINTN)MicrocodeFmpPrivate->MicrocodeInfo[MicrocodeFmpPrivate->DescriptorCount - 1].TotalSize\r
761 - (UINTN)MicrocodeFmpPrivate->MicrocodePatchAddress;\r
88266859
JY
762}\r
763\r
764/**\r
765 Update Microcode.\r
766\r
767 @param[in] Address The flash address of Microcode.\r
768 @param[in] Image The Microcode image buffer.\r
769 @param[in] ImageSize The size of Microcode image buffer in bytes.\r
770 @param[out] LastAttemptStatus The last attempt status, which will be recorded in ESRT and FMP EFI_FIRMWARE_IMAGE_DESCRIPTOR.\r
771\r
772 @retval EFI_SUCCESS The Microcode image is updated.\r
773 @retval EFI_WRITE_PROTECTED The flash device is read only.\r
774**/\r
775EFI_STATUS\r
776UpdateMicrocode (\r
777 IN UINT64 Address,\r
778 IN VOID *Image,\r
779 IN UINTN ImageSize,\r
780 OUT UINT32 *LastAttemptStatus\r
781 )\r
782{\r
783 EFI_STATUS Status;\r
784\r
785 DEBUG((DEBUG_INFO, "PlatformUpdate:"));\r
786 DEBUG((DEBUG_INFO, " Address - 0x%lx,", Address));\r
e9179788 787 DEBUG((DEBUG_INFO, " Length - 0x%x\n", ImageSize));\r
88266859
JY
788\r
789 Status = MicrocodeFlashWrite (\r
790 Address,\r
791 Image,\r
792 ImageSize\r
793 );\r
794 if (!EFI_ERROR(Status)) {\r
795 *LastAttemptStatus = LAST_ATTEMPT_STATUS_SUCCESS;\r
796 } else {\r
797 *LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_UNSUCCESSFUL;\r
798 }\r
799 return Status;\r
800}\r
801\r
e9179788
SZ
802/**\r
803 Update Microcode flash region with FIT.\r
804\r
805 @param[in] MicrocodeFmpPrivate The Microcode driver private data\r
806 @param[in] TargetMicrocodeEntryPoint Target Microcode entrypoint to be updated\r
807 @param[in] Image The Microcode image buffer.\r
808 @param[in] ImageSize The size of Microcode image buffer in bytes.\r
809 @param[out] LastAttemptStatus The last attempt status, which will be recorded in ESRT and FMP EFI_FIRMWARE_IMAGE_DESCRIPTOR.\r
810\r
811 @retval EFI_SUCCESS The Microcode image is written.\r
812 @retval EFI_WRITE_PROTECTED The flash device is read only.\r
813**/\r
814EFI_STATUS\r
815UpdateMicrocodeFlashRegionWithFit (\r
816 IN MICROCODE_FMP_PRIVATE_DATA *MicrocodeFmpPrivate,\r
817 IN CPU_MICROCODE_HEADER *TargetMicrocodeEntryPoint,\r
818 IN VOID *Image,\r
819 IN UINTN ImageSize,\r
820 OUT UINT32 *LastAttemptStatus\r
821 )\r
822{\r
823 VOID *MicrocodePatchAddress;\r
824 UINTN MicrocodePatchRegionSize;\r
825 UINTN TargetTotalSize;\r
826 EFI_STATUS Status;\r
827 VOID *MicrocodePatchScratchBuffer;\r
828 UINT8 *ScratchBufferPtr;\r
829 UINTN ScratchBufferSize;\r
830 UINTN RestSize;\r
831 UINTN AvailableSize;\r
832 VOID *NextMicrocodeEntryPoint;\r
833 VOID *EmptyFitMicrocodeEntry;\r
834 VOID *UnusedFitMicrocodeEntry;\r
835\r
836 DEBUG((DEBUG_INFO, "UpdateMicrocodeFlashRegionWithFit: Image - 0x%x, size - 0x%x\n", Image, ImageSize));\r
837\r
838 MicrocodePatchAddress = MicrocodeFmpPrivate->MicrocodePatchAddress;\r
839 MicrocodePatchRegionSize = MicrocodeFmpPrivate->MicrocodePatchRegionSize;\r
840\r
841 MicrocodePatchScratchBuffer = AllocateZeroPool (MicrocodePatchRegionSize);\r
842 if (MicrocodePatchScratchBuffer == NULL) {\r
843 DEBUG((DEBUG_ERROR, "Fail to allocate Microcode Scratch buffer\n"));\r
844 *LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_INSUFFICIENT_RESOURCES;\r
845 return EFI_OUT_OF_RESOURCES;\r
846 }\r
847 ScratchBufferPtr = MicrocodePatchScratchBuffer;\r
848 ScratchBufferSize = 0;\r
849\r
850 //\r
851 // Target data collection\r
852 //\r
853 TargetTotalSize = 0;\r
854 AvailableSize = 0;\r
855 if (TargetMicrocodeEntryPoint != NULL) {\r
856 if (TargetMicrocodeEntryPoint->DataSize == 0) {\r
857 TargetTotalSize = 2048;\r
858 } else {\r
859 TargetTotalSize = TargetMicrocodeEntryPoint->TotalSize;\r
860 }\r
861 DEBUG((DEBUG_INFO, " TargetTotalSize - 0x%x\n", TargetTotalSize));\r
862 NextMicrocodeEntryPoint = GetNextFitMicrocode (MicrocodeFmpPrivate, TargetMicrocodeEntryPoint);\r
863 DEBUG((DEBUG_INFO, " NextMicrocodeEntryPoint - 0x%x\n", NextMicrocodeEntryPoint));\r
864 if (NextMicrocodeEntryPoint != NULL) {\r
865 ASSERT ((UINTN) NextMicrocodeEntryPoint >= ((UINTN) TargetMicrocodeEntryPoint + TargetTotalSize));\r
866 AvailableSize = (UINTN) NextMicrocodeEntryPoint - (UINTN) TargetMicrocodeEntryPoint;\r
867 } else {\r
868 AvailableSize = (UINTN) MicrocodePatchAddress + MicrocodePatchRegionSize - (UINTN) TargetMicrocodeEntryPoint;\r
869 }\r
870 DEBUG((DEBUG_INFO, " AvailableSize - 0x%x\n", AvailableSize));\r
871 ASSERT (AvailableSize >= TargetTotalSize);\r
872 }\r
873 //\r
874 // Total Size means the Microcode size.\r
875 // Available Size means the Microcode size plus the pad till (1) next Microcode or (2) the end.\r
876 //\r
877 // (1)\r
878 // +------+-----------+-----+------+===================+\r
879 // | MCU1 | Microcode | PAD | MCU2 | Empty |\r
880 // +------+-----------+-----+------+===================+\r
881 // | TotalSize |\r
882 // |<-AvailableSize->|\r
883 //\r
884 // (2)\r
885 // +------+-----------+===================+\r
886 // | MCU | Microcode | Empty |\r
887 // +------+-----------+===================+\r
888 // | TotalSize |\r
889 // |<- AvailableSize ->|\r
890 //\r
891\r
892 //\r
893 // Update based on policy\r
894 //\r
895\r
896 //\r
897 // 1. If there is enough space to update old one in situ, replace old microcode in situ.\r
898 //\r
899 if (AvailableSize >= ImageSize) {\r
900 DEBUG((DEBUG_INFO, "Replace old microcode in situ\n"));\r
901 //\r
902 // +------+------------+------+===================+\r
903 // |Other | Old Image | ... | Empty |\r
904 // +------+------------+------+===================+\r
905 //\r
906 // +------+---------+--+------+===================+\r
907 // |Other |New Image|FF| ... | Empty |\r
908 // +------+---------+--+------+===================+\r
909 //\r
910 // 1.1. Copy new image\r
911 CopyMem (ScratchBufferPtr, Image, ImageSize);\r
912 ScratchBufferSize += ImageSize;\r
913 ScratchBufferPtr = (UINT8 *)MicrocodePatchScratchBuffer + ScratchBufferSize;\r
914 // 1.2. Pad 0xFF\r
915 RestSize = AvailableSize - ImageSize;\r
916 if (RestSize > 0) {\r
917 SetMem (ScratchBufferPtr, RestSize, 0xFF);\r
918 ScratchBufferSize += RestSize;\r
919 ScratchBufferPtr = (UINT8 *)MicrocodePatchScratchBuffer + ScratchBufferSize;\r
920 }\r
921 Status = UpdateMicrocode((UINTN)TargetMicrocodeEntryPoint, MicrocodePatchScratchBuffer, ScratchBufferSize, LastAttemptStatus);\r
922 return Status;\r
923 }\r
924\r
925 //\r
926 // 2. If there is empty FIT microcode entry with enough space, use it.\r
927 //\r
928 EmptyFitMicrocodeEntry = FindEmptyFitMicrocode (MicrocodeFmpPrivate, ImageSize, &AvailableSize);\r
929 if (EmptyFitMicrocodeEntry != NULL) {\r
930 DEBUG((DEBUG_INFO, "Use empty FIT microcode entry\n"));\r
931 // 2.1. Copy new image\r
932 CopyMem (ScratchBufferPtr, Image, ImageSize);\r
933 ScratchBufferSize += ImageSize;\r
934 ScratchBufferPtr = (UINT8 *)MicrocodePatchScratchBuffer + ScratchBufferSize;\r
935 // 2.2. Pad 0xFF\r
936 RestSize = AvailableSize - ImageSize;\r
937 if (RestSize > 0) {\r
938 SetMem (ScratchBufferPtr, RestSize, 0xFF);\r
939 ScratchBufferSize += RestSize;\r
940 ScratchBufferPtr = (UINT8 *)MicrocodePatchScratchBuffer + ScratchBufferSize;\r
941 }\r
942 Status = UpdateMicrocode ((UINTN) EmptyFitMicrocodeEntry, MicrocodePatchScratchBuffer, ScratchBufferSize, LastAttemptStatus);\r
943 if (!EFI_ERROR (Status) && (TargetMicrocodeEntryPoint != NULL)) {\r
944 //\r
945 // Empty old microcode.\r
946 //\r
947 ScratchBufferPtr = MicrocodePatchScratchBuffer;\r
948 SetMem (ScratchBufferPtr, TargetTotalSize, 0xFF);\r
949 ScratchBufferSize = TargetTotalSize;\r
950 ScratchBufferPtr = (UINT8 *) MicrocodePatchScratchBuffer + ScratchBufferSize;\r
951 UpdateMicrocode ((UINTN) TargetMicrocodeEntryPoint, MicrocodePatchScratchBuffer, ScratchBufferSize, LastAttemptStatus);\r
952 }\r
953 return Status;\r
954 }\r
955\r
956 //\r
957 // 3. If there is unused microcode entry with enough space, use it.\r
958 //\r
959 UnusedFitMicrocodeEntry = FindUnusedFitMicrocode (MicrocodeFmpPrivate, ImageSize, &AvailableSize);\r
960 if (UnusedFitMicrocodeEntry != NULL) {\r
961 DEBUG((DEBUG_INFO, "Use unused FIT microcode entry\n"));\r
962 // 3.1. Copy new image\r
963 CopyMem (ScratchBufferPtr, Image, ImageSize);\r
964 ScratchBufferSize += ImageSize;\r
965 ScratchBufferPtr = (UINT8 *)MicrocodePatchScratchBuffer + ScratchBufferSize;\r
966 // 3.2. Pad 0xFF\r
967 RestSize = AvailableSize - ImageSize;\r
968 if (RestSize > 0) {\r
969 SetMem (ScratchBufferPtr, RestSize, 0xFF);\r
970 ScratchBufferSize += RestSize;\r
971 ScratchBufferPtr = (UINT8 *)MicrocodePatchScratchBuffer + ScratchBufferSize;\r
972 }\r
973 Status = UpdateMicrocode ((UINTN) UnusedFitMicrocodeEntry, MicrocodePatchScratchBuffer, ScratchBufferSize, LastAttemptStatus);\r
974 if (!EFI_ERROR (Status) && (TargetMicrocodeEntryPoint != NULL)) {\r
975 //\r
976 // Empty old microcode.\r
977 //\r
978 ScratchBufferPtr = MicrocodePatchScratchBuffer;\r
979 SetMem (ScratchBufferPtr, TargetTotalSize, 0xFF);\r
980 ScratchBufferSize = TargetTotalSize;\r
981 ScratchBufferPtr = (UINT8 *) MicrocodePatchScratchBuffer + ScratchBufferSize;\r
982 UpdateMicrocode ((UINTN) TargetMicrocodeEntryPoint, MicrocodePatchScratchBuffer, ScratchBufferSize, LastAttemptStatus);\r
983 }\r
984 return Status;\r
985 }\r
986\r
987 //\r
988 // 4. No usable FIT microcode entry.\r
989 //\r
990 DEBUG((DEBUG_ERROR, "No usable FIT microcode entry\n"));\r
991 *LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_INSUFFICIENT_RESOURCES;\r
992 Status = EFI_OUT_OF_RESOURCES;\r
993\r
994 return Status;\r
995}\r
996\r
88266859 997/**\r
2ed65824 998 Update Microcode flash region.\r
88266859 999\r
2ed65824 1000 @param[in] MicrocodeFmpPrivate The Microcode driver private data\r
31d060d9 1001 @param[in] TargetMicrocodeEntryPoint Target Microcode entrypoint to be updated\r
2ed65824
JY
1002 @param[in] Image The Microcode image buffer.\r
1003 @param[in] ImageSize The size of Microcode image buffer in bytes.\r
1004 @param[out] LastAttemptStatus The last attempt status, which will be recorded in ESRT and FMP EFI_FIRMWARE_IMAGE_DESCRIPTOR.\r
88266859 1005\r
2ed65824
JY
1006 @retval EFI_SUCCESS The Microcode image is written.\r
1007 @retval EFI_WRITE_PROTECTED The flash device is read only.\r
88266859
JY
1008**/\r
1009EFI_STATUS\r
2ed65824
JY
1010UpdateMicrocodeFlashRegion (\r
1011 IN MICROCODE_FMP_PRIVATE_DATA *MicrocodeFmpPrivate,\r
31d060d9 1012 IN CPU_MICROCODE_HEADER *TargetMicrocodeEntryPoint,\r
2ed65824
JY
1013 IN VOID *Image,\r
1014 IN UINTN ImageSize,\r
1015 OUT UINT32 *LastAttemptStatus\r
88266859
JY
1016 )\r
1017{\r
2ed65824
JY
1018 VOID *MicrocodePatchAddress;\r
1019 UINTN MicrocodePatchRegionSize;\r
31d060d9 1020 UINTN TargetTotalSize;\r
88266859 1021 UINTN UsedRegionSize;\r
2ed65824
JY
1022 EFI_STATUS Status;\r
1023 VOID *MicrocodePatchScratchBuffer;\r
1024 UINT8 *ScratchBufferPtr;\r
1025 UINTN ScratchBufferSize;\r
1026 UINTN RestSize;\r
1027 UINTN AvailableSize;\r
1028 VOID *NextMicrocodeEntryPoint;\r
1029 MICROCODE_INFO *MicrocodeInfo;\r
1030 UINTN MicrocodeCount;\r
1031 UINTN Index;\r
1032\r
1033 DEBUG((DEBUG_INFO, "UpdateMicrocodeFlashRegion: Image - 0x%x, size - 0x%x\n", Image, ImageSize));\r
88266859 1034\r
2ed65824
JY
1035 MicrocodePatchAddress = MicrocodeFmpPrivate->MicrocodePatchAddress;\r
1036 MicrocodePatchRegionSize = MicrocodeFmpPrivate->MicrocodePatchRegionSize;\r
1037\r
1038 MicrocodePatchScratchBuffer = AllocateZeroPool (MicrocodePatchRegionSize);\r
1039 if (MicrocodePatchScratchBuffer == NULL) {\r
1040 DEBUG((DEBUG_ERROR, "Fail to allocate Microcode Scratch buffer\n"));\r
1041 *LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_INSUFFICIENT_RESOURCES;\r
1042 return EFI_OUT_OF_RESOURCES;\r
88266859 1043 }\r
2ed65824
JY
1044 ScratchBufferPtr = MicrocodePatchScratchBuffer;\r
1045 ScratchBufferSize = 0;\r
88266859 1046\r
2ed65824 1047 //\r
31d060d9 1048 // Target data collection\r
2ed65824 1049 //\r
31d060d9 1050 TargetTotalSize = 0;\r
2ed65824
JY
1051 AvailableSize = 0;\r
1052 NextMicrocodeEntryPoint = NULL;\r
31d060d9
JY
1053 if (TargetMicrocodeEntryPoint != NULL) {\r
1054 if (TargetMicrocodeEntryPoint->DataSize == 0) {\r
1055 TargetTotalSize = 2048;\r
88266859 1056 } else {\r
31d060d9 1057 TargetTotalSize = TargetMicrocodeEntryPoint->TotalSize;\r
88266859 1058 }\r
31d060d9
JY
1059 DEBUG((DEBUG_INFO, " TargetTotalSize - 0x%x\n", TargetTotalSize));\r
1060 NextMicrocodeEntryPoint = GetNextMicrocode(MicrocodeFmpPrivate, TargetMicrocodeEntryPoint);\r
2ed65824
JY
1061 DEBUG((DEBUG_INFO, " NextMicrocodeEntryPoint - 0x%x\n", NextMicrocodeEntryPoint));\r
1062 if (NextMicrocodeEntryPoint != NULL) {\r
31d060d9
JY
1063 ASSERT ((UINTN)NextMicrocodeEntryPoint >= ((UINTN)TargetMicrocodeEntryPoint + TargetTotalSize));\r
1064 AvailableSize = (UINTN)NextMicrocodeEntryPoint - (UINTN)TargetMicrocodeEntryPoint;\r
2ed65824 1065 } else {\r
31d060d9 1066 AvailableSize = (UINTN)MicrocodePatchAddress + MicrocodePatchRegionSize - (UINTN)TargetMicrocodeEntryPoint;\r
2ed65824
JY
1067 }\r
1068 DEBUG((DEBUG_INFO, " AvailableSize - 0x%x\n", AvailableSize));\r
e9179788 1069 ASSERT (AvailableSize >= TargetTotalSize);\r
2ed65824 1070 }\r
2ed65824
JY
1071 UsedRegionSize = GetCurrentMicrocodeUsedRegionSize(MicrocodeFmpPrivate);\r
1072 DEBUG((DEBUG_INFO, " UsedRegionSize - 0x%x\n", UsedRegionSize));\r
31d060d9
JY
1073 ASSERT (UsedRegionSize >= TargetTotalSize);\r
1074 if (TargetMicrocodeEntryPoint != NULL) {\r
1075 ASSERT ((UINTN)MicrocodePatchAddress + UsedRegionSize >= ((UINTN)TargetMicrocodeEntryPoint + TargetTotalSize));\r
2ed65824
JY
1076 }\r
1077 //\r
e9179788
SZ
1078 // Total Size means the Microcode size.\r
1079 // Available Size means the Microcode size plus the pad till (1) next Microcode or (2) the end.\r
2ed65824
JY
1080 //\r
1081 // (1)\r
1082 // +------+-----------+-----+------+===================+\r
1083 // | MCU1 | Microcode | PAD | MCU2 | Empty |\r
1084 // +------+-----------+-----+------+===================+\r
1085 // | TotalSize |\r
1086 // |<-AvailableSize->|\r
1087 // |<- UsedRegionSize ->|\r
1088 //\r
1089 // (2)\r
1090 // +------+-----------+===================+\r
1091 // | MCU | Microcode | Empty |\r
1092 // +------+-----------+===================+\r
1093 // | TotalSize |\r
1094 // |<- AvailableSize ->|\r
1095 // |<-UsedRegionSize->|\r
1096 //\r
1097\r
1098 //\r
1099 // Update based on policy\r
1100 //\r
1101\r
1102 //\r
1103 // 1. If there is enough space to update old one in situ, replace old microcode in situ.\r
1104 //\r
1105 if (AvailableSize >= ImageSize) {\r
1106 DEBUG((DEBUG_INFO, "Replace old microcode in situ\n"));\r
1107 //\r
1108 // +------+------------+------+===================+\r
e9179788 1109 // |Other | Old Image | ... | Empty |\r
2ed65824
JY
1110 // +------+------------+------+===================+\r
1111 //\r
1112 // +------+---------+--+------+===================+\r
e9179788 1113 // |Other |New Image|FF| ... | Empty |\r
2ed65824
JY
1114 // +------+---------+--+------+===================+\r
1115 //\r
1116 // 1.1. Copy new image\r
1117 CopyMem (ScratchBufferPtr, Image, ImageSize);\r
1118 ScratchBufferSize += ImageSize;\r
31d060d9 1119 ScratchBufferPtr = (UINT8 *)MicrocodePatchScratchBuffer + ScratchBufferSize;\r
2ed65824
JY
1120 // 1.2. Pad 0xFF\r
1121 RestSize = AvailableSize - ImageSize;\r
1122 if (RestSize > 0) {\r
1123 SetMem (ScratchBufferPtr, RestSize, 0xFF);\r
1124 ScratchBufferSize += RestSize;\r
31d060d9 1125 ScratchBufferPtr = (UINT8 *)MicrocodePatchScratchBuffer + ScratchBufferSize;\r
2ed65824 1126 }\r
31d060d9 1127 Status = UpdateMicrocode((UINTN)TargetMicrocodeEntryPoint, MicrocodePatchScratchBuffer, ScratchBufferSize, LastAttemptStatus);\r
2ed65824 1128 return Status;\r
88266859
JY
1129 }\r
1130\r
2ed65824
JY
1131 //\r
1132 // 2. If there is enough space to remove old one and add new one, reorg and replace old microcode.\r
1133 //\r
31d060d9
JY
1134 if (MicrocodePatchRegionSize - (UsedRegionSize - TargetTotalSize) >= ImageSize) {\r
1135 if (TargetMicrocodeEntryPoint == NULL) {\r
2ed65824
JY
1136 DEBUG((DEBUG_INFO, "Append new microcode\n"));\r
1137 //\r
1138 // +------+------------+------+===================+\r
1139 // |Other1| Other |Other2| Empty |\r
1140 // +------+------------+------+===================+\r
1141 //\r
1142 // +------+------------+------+-----------+=======+\r
1143 // |Other1| Other |Other2| New Image | Empty |\r
1144 // +------+------------+------+-----------+=======+\r
1145 //\r
1146 Status = UpdateMicrocode((UINTN)MicrocodePatchAddress + UsedRegionSize, Image, ImageSize, LastAttemptStatus);\r
1147 } else {\r
1148 DEBUG((DEBUG_INFO, "Reorg and replace old microcode\n"));\r
1149 //\r
1150 // +------+------------+------+===================+\r
e9179788 1151 // |Other | Old Image | ... | Empty |\r
2ed65824
JY
1152 // +------+------------+------+===================+\r
1153 //\r
1154 // +------+---------------+------+================+\r
e9179788 1155 // |Other | New Image | ... | Empty |\r
2ed65824
JY
1156 // +------+---------------+------+================+\r
1157 //\r
1158 // 2.1. Copy new image\r
31d060d9 1159 CopyMem (ScratchBufferPtr, Image, ImageSize);\r
2ed65824 1160 ScratchBufferSize += ImageSize;\r
31d060d9 1161 ScratchBufferPtr = (UINT8 *)MicrocodePatchScratchBuffer + ScratchBufferSize;\r
2ed65824
JY
1162 // 2.2. Copy rest images after the old image.\r
1163 if (NextMicrocodeEntryPoint != 0) {\r
1164 RestSize = (UINTN)MicrocodePatchAddress + UsedRegionSize - ((UINTN)NextMicrocodeEntryPoint);\r
e9179788 1165 CopyMem (ScratchBufferPtr, NextMicrocodeEntryPoint, RestSize);\r
2ed65824 1166 ScratchBufferSize += RestSize;\r
31d060d9 1167 ScratchBufferPtr = (UINT8 *)MicrocodePatchScratchBuffer + ScratchBufferSize;\r
2ed65824 1168 }\r
31d060d9 1169 Status = UpdateMicrocode((UINTN)TargetMicrocodeEntryPoint, MicrocodePatchScratchBuffer, ScratchBufferSize, LastAttemptStatus);\r
2ed65824
JY
1170 }\r
1171 return Status;\r
1172 }\r
1173\r
1174 //\r
1175 // 3. The new image can be put in MCU region, but not all others can be put.\r
1176 // So all the unused MCU is removed.\r
1177 //\r
1178 if (MicrocodePatchRegionSize >= ImageSize) {\r
1179 //\r
1180 // +------+------------+------+===================+\r
1181 // |Other1| Old Image |Other2| Empty |\r
1182 // +------+------------+------+===================+\r
1183 //\r
1184 // +-------------------------------------+--------+\r
1185 // | New Image | Other |\r
1186 // +-------------------------------------+--------+\r
1187 //\r
1188 DEBUG((DEBUG_INFO, "Add new microcode from beginning\n"));\r
1189\r
1190 MicrocodeCount = MicrocodeFmpPrivate->DescriptorCount;\r
1191 MicrocodeInfo = MicrocodeFmpPrivate->MicrocodeInfo;\r
1192\r
1193 // 3.1. Copy new image\r
31d060d9 1194 CopyMem (ScratchBufferPtr, Image, ImageSize);\r
2ed65824 1195 ScratchBufferSize += ImageSize;\r
31d060d9 1196 ScratchBufferPtr = (UINT8 *)MicrocodePatchScratchBuffer + ScratchBufferSize;\r
2ed65824
JY
1197 // 3.2. Copy some others to rest buffer\r
1198 for (Index = 0; Index < MicrocodeCount; Index++) {\r
1199 if (!MicrocodeInfo[Index].InUse) {\r
1200 continue;\r
1201 }\r
31d060d9 1202 if (MicrocodeInfo[Index].MicrocodeEntryPoint == TargetMicrocodeEntryPoint) {\r
2ed65824
JY
1203 continue;\r
1204 }\r
1205 if (MicrocodeInfo[Index].TotalSize <= MicrocodePatchRegionSize - ScratchBufferSize) {\r
1206 CopyMem (ScratchBufferPtr, MicrocodeInfo[Index].MicrocodeEntryPoint, MicrocodeInfo[Index].TotalSize);\r
1207 ScratchBufferSize += MicrocodeInfo[Index].TotalSize;\r
31d060d9 1208 ScratchBufferPtr = (UINT8 *)MicrocodePatchScratchBuffer + ScratchBufferSize;\r
2ed65824
JY
1209 }\r
1210 }\r
1211 // 3.3. Pad 0xFF\r
1212 RestSize = MicrocodePatchRegionSize - ScratchBufferSize;\r
1213 if (RestSize > 0) {\r
1214 SetMem (ScratchBufferPtr, RestSize, 0xFF);\r
1215 ScratchBufferSize += RestSize;\r
31d060d9 1216 ScratchBufferPtr = (UINT8 *)MicrocodePatchScratchBuffer + ScratchBufferSize;\r
2ed65824
JY
1217 }\r
1218 Status = UpdateMicrocode((UINTN)MicrocodePatchAddress, MicrocodePatchScratchBuffer, ScratchBufferSize, LastAttemptStatus);\r
1219 return Status;\r
1220 }\r
1221\r
1222 //\r
1223 // 4. The new image size is bigger than the whole MCU region.\r
1224 //\r
1225 DEBUG((DEBUG_ERROR, "Microcode too big\n"));\r
1226 *LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_INSUFFICIENT_RESOURCES;\r
1227 Status = EFI_OUT_OF_RESOURCES;\r
1228\r
1229 return Status;\r
1230}\r
1231\r
1232/**\r
1233 Write Microcode.\r
1234\r
1235 Caution: This function may receive untrusted input.\r
1236\r
1237 @param[in] MicrocodeFmpPrivate The Microcode driver private data\r
1238 @param[in] Image The Microcode image buffer.\r
1239 @param[in] ImageSize The size of Microcode image buffer in bytes.\r
1240 @param[out] LastAttemptVersion The last attempt version, which will be recorded in ESRT and FMP EFI_FIRMWARE_IMAGE_DESCRIPTOR.\r
1241 @param[out] LastAttemptStatus The last attempt status, which will be recorded in ESRT and FMP EFI_FIRMWARE_IMAGE_DESCRIPTOR.\r
1242 @param[out] AbortReason A pointer to a pointer to a null-terminated string providing more\r
1243 details for the aborted operation. The buffer is allocated by this function\r
1244 with AllocatePool(), and it is the caller's responsibility to free it with a\r
1245 call to FreePool().\r
1246\r
1247 @retval EFI_SUCCESS The Microcode image is written.\r
e9179788 1248 @retval EFI_VOLUME_CORRUPTED The Microcode image is corrupted.\r
2ed65824
JY
1249 @retval EFI_INCOMPATIBLE_VERSION The Microcode image version is incorrect.\r
1250 @retval EFI_SECURITY_VIOLATION The Microcode image fails to load.\r
1251 @retval EFI_WRITE_PROTECTED The flash device is read only.\r
1252**/\r
1253EFI_STATUS\r
1254MicrocodeWrite (\r
1255 IN MICROCODE_FMP_PRIVATE_DATA *MicrocodeFmpPrivate,\r
1256 IN VOID *Image,\r
1257 IN UINTN ImageSize,\r
1258 OUT UINT32 *LastAttemptVersion,\r
1259 OUT UINT32 *LastAttemptStatus,\r
1260 OUT CHAR16 **AbortReason\r
1261 )\r
1262{\r
1263 EFI_STATUS Status;\r
1264 VOID *AlignedImage;\r
31d060d9
JY
1265 CPU_MICROCODE_HEADER *TargetMicrocodeEntryPoint;\r
1266 UINTN TargetCpuIndex;\r
1267 UINTN TargetMicrcodeIndex;\r
2ed65824 1268\r
88266859
JY
1269 //\r
1270 // MCU must be 16 bytes aligned\r
1271 //\r
1272 AlignedImage = AllocateCopyPool(ImageSize, Image);\r
1273 if (AlignedImage == NULL) {\r
1274 DEBUG((DEBUG_ERROR, "Fail to allocate aligned image\n"));\r
1275 *LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_INSUFFICIENT_RESOURCES;\r
1276 return EFI_OUT_OF_RESOURCES;\r
1277 }\r
1278\r
31d060d9
JY
1279 TargetCpuIndex = (UINTN)-1;\r
1280 Status = VerifyMicrocode(MicrocodeFmpPrivate, AlignedImage, ImageSize, TRUE, LastAttemptStatus, AbortReason, &TargetCpuIndex);\r
88266859
JY
1281 if (EFI_ERROR(Status)) {\r
1282 DEBUG((DEBUG_ERROR, "Fail to verify Microcode Region\n"));\r
1283 FreePool(AlignedImage);\r
1284 return Status;\r
1285 }\r
1286 DEBUG((DEBUG_INFO, "Pass VerifyMicrocode\n"));\r
e9179788 1287 *LastAttemptVersion = ((CPU_MICROCODE_HEADER *)Image)->UpdateRevision;\r
88266859 1288\r
31d060d9
JY
1289 DEBUG((DEBUG_INFO, " TargetCpuIndex - 0x%x\n", TargetCpuIndex));\r
1290 ASSERT (TargetCpuIndex < MicrocodeFmpPrivate->ProcessorCount);\r
1291 TargetMicrcodeIndex = MicrocodeFmpPrivate->ProcessorInfo[TargetCpuIndex].MicrocodeIndex;\r
1292 DEBUG((DEBUG_INFO, " TargetMicrcodeIndex - 0x%x\n", TargetMicrcodeIndex));\r
1293 if (TargetMicrcodeIndex != (UINTN)-1) {\r
1294 ASSERT (TargetMicrcodeIndex < MicrocodeFmpPrivate->DescriptorCount);\r
1295 TargetMicrocodeEntryPoint = MicrocodeFmpPrivate->MicrocodeInfo[TargetMicrcodeIndex].MicrocodeEntryPoint;\r
1296 } else {\r
1297 TargetMicrocodeEntryPoint = NULL;\r
1298 }\r
1299 DEBUG((DEBUG_INFO, " TargetMicrocodeEntryPoint - 0x%x\n", TargetMicrocodeEntryPoint));\r
1300\r
e9179788
SZ
1301 if (MicrocodeFmpPrivate->FitMicrocodeInfo != NULL) {\r
1302 Status = UpdateMicrocodeFlashRegionWithFit (\r
1303 MicrocodeFmpPrivate,\r
1304 TargetMicrocodeEntryPoint,\r
1305 AlignedImage,\r
1306 ImageSize,\r
1307 LastAttemptStatus\r
1308 );\r
1309 } else {\r
1310 Status = UpdateMicrocodeFlashRegion (\r
1311 MicrocodeFmpPrivate,\r
1312 TargetMicrocodeEntryPoint,\r
1313 AlignedImage,\r
1314 ImageSize,\r
1315 LastAttemptStatus\r
1316 );\r
1317 }\r
88266859
JY
1318\r
1319 FreePool(AlignedImage);\r
1320\r
1321 return Status;\r
1322}\r
1323\r
1324\r