2 SetImage instance to update Microcode.
4 Caution: This module requires additional review when modified.
5 This module will have external input - capsule image.
6 This external input must be validated carefully to avoid security issue like
7 buffer overflow, integer overflow.
9 MicrocodeWrite() and VerifyMicrocode() will receive untrusted input and do basic validation.
11 Copyright (c) 2016, Intel Corporation. All rights reserved.<BR>
12 This program and the accompanying materials
13 are licensed and made available under the terms and conditions of the BSD License
14 which accompanies this distribution. The full text of the license may be found at
15 http://opensource.org/licenses/bsd-license.php
17 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
18 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
22 #include "MicrocodeUpdate.h"
28 Caution: This function may receive untrusted input.
30 @param[in] Image The Microcode image buffer.
31 @param[in] ImageSize The size of Microcode image buffer in bytes.
32 @param[in] TryLoad Try to load Microcode or not.
33 @param[out] LastAttemptStatus The last attempt status, which will be recorded in ESRT and FMP EFI_FIRMWARE_IMAGE_DESCRIPTOR.
34 @param[out] AbortReason A pointer to a pointer to a null-terminated string providing more
35 details for the aborted operation. The buffer is allocated by this function
36 with AllocatePool(), and it is the caller's responsibility to free it with a
39 @retval EFI_SUCCESS The Microcode image passes verification.
40 @retval EFI_VOLUME_CORRUPTED The Microcode image is corrupt.
41 @retval EFI_INCOMPATIBLE_VERSION The Microcode image version is incorrect.
42 @retval EFI_UNSUPPORTED The Microcode ProcessorSignature or ProcessorFlags is incorrect.
43 @retval EFI_SECURITY_VIOLATION The Microcode image fails to load.
50 OUT UINT32
*LastAttemptStatus
,
51 OUT CHAR16
**AbortReason
57 @param[out] MicrocodePatchAddress The address of Microcode
58 @param[out] MicrocodePatchRegionSize The region size of Microcode
60 @retval TRUE The Microcode region is returned.
61 @retval FALSE No Microcode region.
65 OUT VOID
**MicrocodePatchAddress
,
66 OUT UINTN
*MicrocodePatchRegionSize
69 *MicrocodePatchAddress
= (VOID
*)(UINTN
)PcdGet64(PcdCpuMicrocodePatchAddress
);
70 *MicrocodePatchRegionSize
= (UINTN
)PcdGet64(PcdCpuMicrocodePatchRegionSize
);
72 if ((*MicrocodePatchAddress
== NULL
) || (*MicrocodePatchRegionSize
== 0)) {
80 Get Microcode update signature of currently loaded Microcode update.
82 @return Microcode signature.
86 GetCurrentMicrocodeSignature (
92 AsmWriteMsr64(MSR_IA32_BIOS_SIGN_ID
, 0);
93 AsmCpuid(CPUID_VERSION_INFO
, NULL
, NULL
, NULL
, NULL
);
94 Signature
= AsmReadMsr64(MSR_IA32_BIOS_SIGN_ID
);
95 return (UINT32
)RShiftU64(Signature
, 32);
99 Get current processor signature.
101 @return current processor signature.
104 GetCurrentProcessorSignature (
109 AsmCpuid(CPUID_VERSION_INFO
, &RegEax
, NULL
, NULL
, NULL
);
114 Get current platform ID.
116 @return current platform ID.
119 GetCurrentPlatformId (
125 PlatformId
= (UINT8
)AsmMsrBitFieldRead64(MSR_IA32_PLATFORM_ID
, 50, 52);
132 @param[in] Address The address of new Microcode.
134 @return Loaded Microcode signature.
142 AsmWriteMsr64(MSR_IA32_BIOS_UPDT_TRIG
, Address
);
143 return GetCurrentMicrocodeSignature();
147 If the Microcode is used by current processor.
149 @param[in] MicrocodeEntryPoint The Microcode buffer
151 @retval TRUE The Microcode is used by current processor.
152 @retval FALSE The Microcode is NOT used by current processor.
156 IN CPU_MICROCODE_HEADER
*MicrocodeEntryPoint
159 UINT32 AttemptStatus
;
163 if (MicrocodeEntryPoint
->HeaderVersion
== 0x1 && MicrocodeEntryPoint
->LoaderRevision
== 0x1) {
165 // It is the microcode header. It is not the padding data between microcode patches
166 // becasue the padding data should not include 0x00000001 and it should be the repeated
167 // byte format (like 0xXYXYXYXY....).
169 if (MicrocodeEntryPoint
->DataSize
== 0) {
172 TotalSize
= MicrocodeEntryPoint
->TotalSize
;
174 Status
= VerifyMicrocode(MicrocodeEntryPoint
, TotalSize
, FALSE
, &AttemptStatus
, NULL
);
175 if (!EFI_ERROR(Status
)) {
183 Get current Microcode information.
185 NOTE: The DescriptorCount/ImageDescriptor/MicrocodeInfo in MicrocodeFmpPrivate
186 are not avaiable in this function.
188 @param[in] MicrocodeFmpPrivate The Microcode driver private data
189 @param[in] DescriptorCount The count of Microcode ImageDescriptor allocated.
190 @param[out] ImageDescriptor Microcode ImageDescriptor
191 @param[out] MicrocodeInfo Microcode information
193 @return Microcode count
197 IN MICROCODE_FMP_PRIVATE_DATA
*MicrocodeFmpPrivate
,
198 IN UINTN DescriptorCount
, OPTIONAL
199 OUT EFI_FIRMWARE_IMAGE_DESCRIPTOR
*ImageDescriptor
, OPTIONAL
200 OUT MICROCODE_INFO
*MicrocodeInfo OPTIONAL
203 VOID
*MicrocodePatchAddress
;
204 UINTN MicrocodePatchRegionSize
;
205 CPU_MICROCODE_HEADER
*MicrocodeEntryPoint
;
209 UINT64 ImageAttributes
;
212 MicrocodePatchAddress
= MicrocodeFmpPrivate
->MicrocodePatchAddress
;
213 MicrocodePatchRegionSize
= MicrocodeFmpPrivate
->MicrocodePatchRegionSize
;
215 DEBUG((DEBUG_INFO
, "Microcode Region - 0x%x - 0x%x\n", MicrocodePatchAddress
, MicrocodePatchRegionSize
));
219 MicrocodeEnd
= (UINTN
)MicrocodePatchAddress
+ MicrocodePatchRegionSize
;
220 MicrocodeEntryPoint
= (CPU_MICROCODE_HEADER
*) (UINTN
) MicrocodePatchAddress
;
222 if (MicrocodeEntryPoint
->HeaderVersion
== 0x1 && MicrocodeEntryPoint
->LoaderRevision
== 0x1) {
224 // It is the microcode header. It is not the padding data between microcode patches
225 // becasue the padding data should not include 0x00000001 and it should be the repeated
226 // byte format (like 0xXYXYXYXY....).
228 if (MicrocodeEntryPoint
->DataSize
== 0) {
231 TotalSize
= MicrocodeEntryPoint
->TotalSize
;
234 IsInUse
= IsMicrocodeInUse (MicrocodeEntryPoint
);
236 if (ImageDescriptor
!= NULL
&& DescriptorCount
> Count
) {
237 ImageDescriptor
[Count
].ImageIndex
= (UINT8
)(Count
+ 1);
238 CopyGuid (&ImageDescriptor
[Count
].ImageTypeId
, &gMicrocodeFmpImageTypeIdGuid
);
239 ImageDescriptor
[Count
].ImageId
= LShiftU64(MicrocodeEntryPoint
->ProcessorFlags
, 32) + MicrocodeEntryPoint
->ProcessorSignature
.Uint32
;
240 ImageDescriptor
[Count
].ImageIdName
= NULL
;
241 ImageDescriptor
[Count
].Version
= MicrocodeEntryPoint
->UpdateRevision
;
242 ImageDescriptor
[Count
].VersionName
= NULL
;
243 ImageDescriptor
[Count
].Size
= TotalSize
;
244 ImageAttributes
= IMAGE_ATTRIBUTE_IMAGE_UPDATABLE
| IMAGE_ATTRIBUTE_RESET_REQUIRED
;
246 ImageAttributes
|= IMAGE_ATTRIBUTE_IN_USE
;
248 ImageDescriptor
[Count
].AttributesSupported
= ImageAttributes
| IMAGE_ATTRIBUTE_IN_USE
;
249 ImageDescriptor
[Count
].AttributesSetting
= ImageAttributes
;
250 ImageDescriptor
[Count
].Compatibilities
= 0;
251 ImageDescriptor
[Count
].LowestSupportedImageVersion
= MicrocodeEntryPoint
->UpdateRevision
; // do not support rollback
252 ImageDescriptor
[Count
].LastAttemptVersion
= 0;
253 ImageDescriptor
[Count
].LastAttemptStatus
= 0;
254 ImageDescriptor
[Count
].HardwareInstance
= 0;
256 if (MicrocodeInfo
!= NULL
&& DescriptorCount
> Count
) {
257 MicrocodeInfo
[Count
].MicrocodeEntryPoint
= MicrocodeEntryPoint
;
258 MicrocodeInfo
[Count
].TotalSize
= TotalSize
;
259 MicrocodeInfo
[Count
].InUse
= IsInUse
;
263 // It is the padding data between the microcode patches for microcode patches alignment.
264 // Because the microcode patch is the multiple of 1-KByte, the padding data should not
265 // exist if the microcode patch alignment value is not larger than 1-KByte. So, the microcode
266 // alignment value should be larger than 1-KByte. We could skip SIZE_1KB padding data to
267 // find the next possible microcode patch header.
269 MicrocodeEntryPoint
= (CPU_MICROCODE_HEADER
*) (((UINTN
) MicrocodeEntryPoint
) + SIZE_1KB
);
274 ASSERT(Count
< 0xFF);
277 // Get the next patch.
279 MicrocodeEntryPoint
= (CPU_MICROCODE_HEADER
*) (((UINTN
) MicrocodeEntryPoint
) + TotalSize
);
280 } while (((UINTN
) MicrocodeEntryPoint
< MicrocodeEnd
));
288 Caution: This function may receive untrusted input.
290 @param[in] Image The Microcode image buffer.
291 @param[in] ImageSize The size of Microcode image buffer in bytes.
292 @param[in] TryLoad Try to load Microcode or not.
293 @param[out] LastAttemptStatus The last attempt status, which will be recorded in ESRT and FMP EFI_FIRMWARE_IMAGE_DESCRIPTOR.
294 @param[out] AbortReason A pointer to a pointer to a null-terminated string providing more
295 details for the aborted operation. The buffer is allocated by this function
296 with AllocatePool(), and it is the caller's responsibility to free it with a
299 @retval EFI_SUCCESS The Microcode image passes verification.
300 @retval EFI_VOLUME_CORRUPTED The Microcode image is corrupt.
301 @retval EFI_INCOMPATIBLE_VERSION The Microcode image version is incorrect.
302 @retval EFI_UNSUPPORTED The Microcode ProcessorSignature or ProcessorFlags is incorrect.
303 @retval EFI_SECURITY_VIOLATION The Microcode image fails to load.
310 OUT UINT32
*LastAttemptStatus
,
311 OUT CHAR16
**AbortReason
315 CPU_MICROCODE_HEADER
*MicrocodeEntryPoint
;
318 UINT32 CurrentRevision
;
319 UINT32 CurrentProcessorSignature
;
320 UINT8 CurrentPlatformId
;
322 UINTN ExtendedTableLength
;
323 UINT32 ExtendedTableCount
;
324 CPU_MICROCODE_EXTENDED_TABLE
*ExtendedTable
;
325 CPU_MICROCODE_EXTENDED_TABLE_HEADER
*ExtendedTableHeader
;
326 BOOLEAN CorrectMicrocode
;
329 // Check HeaderVersion
331 MicrocodeEntryPoint
= Image
;
332 if (MicrocodeEntryPoint
->HeaderVersion
!= 0x1) {
333 DEBUG((DEBUG_ERROR
, "VerifyMicrocode - fail on HeaderVersion\n"));
334 *LastAttemptStatus
= LAST_ATTEMPT_STATUS_ERROR_INVALID_FORMAT
;
335 if (AbortReason
!= NULL
) {
336 *AbortReason
= AllocateCopyPool(sizeof(L
"InvalidHeaderVersion"), L
"InvalidHeaderVersion");
338 return EFI_INCOMPATIBLE_VERSION
;
341 // Check LoaderRevision
343 if (MicrocodeEntryPoint
->LoaderRevision
!= 0x1) {
344 DEBUG((DEBUG_ERROR
, "VerifyMicrocode - fail on LoaderRevision\n"));
345 *LastAttemptStatus
= LAST_ATTEMPT_STATUS_ERROR_INVALID_FORMAT
;
346 if (AbortReason
!= NULL
) {
347 *AbortReason
= AllocateCopyPool(sizeof(L
"InvalidLoaderVersion"), L
"InvalidLoaderVersion");
349 return EFI_INCOMPATIBLE_VERSION
;
354 if (MicrocodeEntryPoint
->DataSize
== 0) {
357 TotalSize
= MicrocodeEntryPoint
->TotalSize
;
359 if (TotalSize
<= sizeof(CPU_MICROCODE_HEADER
)) {
360 DEBUG((DEBUG_ERROR
, "VerifyMicrocode - TotalSize too small\n"));
361 *LastAttemptStatus
= LAST_ATTEMPT_STATUS_ERROR_INVALID_FORMAT
;
362 if (AbortReason
!= NULL
) {
363 *AbortReason
= AllocateCopyPool(sizeof(L
"InvalidTotalSize"), L
"InvalidTotalSize");
365 return EFI_VOLUME_CORRUPTED
;
367 if (TotalSize
!= ImageSize
) {
368 DEBUG((DEBUG_ERROR
, "VerifyMicrocode - fail on TotalSize\n"));
369 *LastAttemptStatus
= LAST_ATTEMPT_STATUS_ERROR_INVALID_FORMAT
;
370 if (AbortReason
!= NULL
) {
371 *AbortReason
= AllocateCopyPool(sizeof(L
"InvalidTotalSize"), L
"InvalidTotalSize");
373 return EFI_VOLUME_CORRUPTED
;
378 if (MicrocodeEntryPoint
->DataSize
== 0) {
379 DataSize
= 2048 - sizeof(CPU_MICROCODE_HEADER
);
381 DataSize
= MicrocodeEntryPoint
->DataSize
;
383 if (DataSize
> TotalSize
- sizeof(CPU_MICROCODE_HEADER
)) {
384 DEBUG((DEBUG_ERROR
, "VerifyMicrocode - DataSize too big\n"));
385 *LastAttemptStatus
= LAST_ATTEMPT_STATUS_ERROR_INVALID_FORMAT
;
386 if (AbortReason
!= NULL
) {
387 *AbortReason
= AllocateCopyPool(sizeof(L
"InvalidDataSize"), L
"InvalidDataSize");
389 return EFI_VOLUME_CORRUPTED
;
391 if ((DataSize
& 0x3) != 0) {
392 DEBUG((DEBUG_ERROR
, "VerifyMicrocode - DataSize not aligned\n"));
393 *LastAttemptStatus
= LAST_ATTEMPT_STATUS_ERROR_INVALID_FORMAT
;
394 if (AbortReason
!= NULL
) {
395 *AbortReason
= AllocateCopyPool(sizeof(L
"InvalidDataSize"), L
"InvalidDataSize");
397 return EFI_VOLUME_CORRUPTED
;
399 CheckSum32
= CalculateSum32((UINT32
*)MicrocodeEntryPoint
, DataSize
+ sizeof(CPU_MICROCODE_HEADER
));
400 if (CheckSum32
!= 0) {
401 DEBUG((DEBUG_ERROR
, "VerifyMicrocode - fail on CheckSum32\n"));
402 *LastAttemptStatus
= LAST_ATTEMPT_STATUS_ERROR_INVALID_FORMAT
;
403 if (AbortReason
!= NULL
) {
404 *AbortReason
= AllocateCopyPool(sizeof(L
"InvalidChecksum"), L
"InvalidChecksum");
406 return EFI_VOLUME_CORRUPTED
;
410 // Check ProcessorSignature/ProcessorFlags
412 CorrectMicrocode
= FALSE
;
413 CurrentProcessorSignature
= GetCurrentProcessorSignature();
414 CurrentPlatformId
= GetCurrentPlatformId();
415 if ((MicrocodeEntryPoint
->ProcessorSignature
.Uint32
!= CurrentProcessorSignature
) ||
416 ((MicrocodeEntryPoint
->ProcessorFlags
& (1 << CurrentPlatformId
)) == 0)) {
417 ExtendedTableLength
= TotalSize
- (DataSize
+ sizeof(CPU_MICROCODE_HEADER
));
418 if (ExtendedTableLength
!= 0) {
420 // Extended Table exist, check if the CPU in support list
422 ExtendedTableHeader
= (CPU_MICROCODE_EXTENDED_TABLE_HEADER
*)((UINT8
*)(MicrocodeEntryPoint
) + DataSize
+ sizeof(CPU_MICROCODE_HEADER
));
424 // Calculate Extended Checksum
426 if ((ExtendedTableLength
> sizeof(CPU_MICROCODE_EXTENDED_TABLE_HEADER
)) && ((ExtendedTableLength
& 0x3) != 0)) {
427 CheckSum32
= CalculateSum32((UINT32
*)ExtendedTableHeader
, ExtendedTableLength
);
428 if (CheckSum32
== 0) {
432 ExtendedTableCount
= ExtendedTableHeader
->ExtendedSignatureCount
;
433 if (ExtendedTableCount
<= (ExtendedTableLength
- sizeof(CPU_MICROCODE_EXTENDED_TABLE_HEADER
)) / sizeof(CPU_MICROCODE_EXTENDED_TABLE
)) {
434 ExtendedTable
= (CPU_MICROCODE_EXTENDED_TABLE
*)(ExtendedTableHeader
+ 1);
435 for (Index
= 0; Index
< ExtendedTableCount
; Index
++) {
436 CheckSum32
= CalculateSum32((UINT32
*)ExtendedTable
, sizeof(CPU_MICROCODE_EXTENDED_TABLE
));
437 if (CheckSum32
== 0) {
441 if ((ExtendedTable
->ProcessorSignature
.Uint32
== CurrentProcessorSignature
) &&
442 (ExtendedTable
->ProcessorFlag
& (1 << CurrentPlatformId
))) {
446 CorrectMicrocode
= TRUE
;
456 if (!CorrectMicrocode
) {
458 DEBUG((DEBUG_ERROR
, "VerifyMicrocode - fail on CurrentProcessorSignature/ProcessorFlags\n"));
460 *LastAttemptStatus
= LAST_ATTEMPT_STATUS_ERROR_INCORRECT_VERSION
;
461 if (AbortReason
!= NULL
) {
462 if (MicrocodeEntryPoint
->ProcessorSignature
.Uint32
!= CurrentProcessorSignature
) {
463 *AbortReason
= AllocateCopyPool(sizeof(L
"UnsupportedProcessSignature"), L
"UnsupportedProcessSignature");
465 *AbortReason
= AllocateCopyPool(sizeof(L
"UnsupportedProcessorFlags"), L
"UnsupportedProcessorFlags");
468 return EFI_UNSUPPORTED
;
473 // Check UpdateRevision
475 CurrentRevision
= GetCurrentMicrocodeSignature();
476 if (MicrocodeEntryPoint
->UpdateRevision
< CurrentRevision
) {
478 DEBUG((DEBUG_ERROR
, "VerifyMicrocode - fail on UpdateRevision\n"));
480 *LastAttemptStatus
= LAST_ATTEMPT_STATUS_ERROR_INCORRECT_VERSION
;
481 if (AbortReason
!= NULL
) {
482 *AbortReason
= AllocateCopyPool(sizeof(L
"IncorrectRevision"), L
"IncorrectRevision");
484 return EFI_INCOMPATIBLE_VERSION
;
491 CurrentRevision
= LoadMicrocode((UINTN
)MicrocodeEntryPoint
+ sizeof(CPU_MICROCODE_HEADER
));
492 if (MicrocodeEntryPoint
->UpdateRevision
!= CurrentRevision
) {
493 DEBUG((DEBUG_ERROR
, "VerifyMicrocode - fail on LoadMicrocode\n"));
494 *LastAttemptStatus
= LAST_ATTEMPT_STATUS_ERROR_AUTH_ERROR
;
495 if (AbortReason
!= NULL
) {
496 *AbortReason
= AllocateCopyPool(sizeof(L
"InvalidData"), L
"InvalidData");
498 return EFI_SECURITY_VIOLATION
;
506 Get current Microcode in used.
508 @param[in] MicrocodeFmpPrivate The Microcode driver private data
510 @return current Microcode in used.
513 GetCurrentMicrocodeInUse (
514 IN MICROCODE_FMP_PRIVATE_DATA
*MicrocodeFmpPrivate
519 for (Index
= 0; Index
< MicrocodeFmpPrivate
->DescriptorCount
; Index
++) {
520 if (!MicrocodeFmpPrivate
->MicrocodeInfo
[Index
].InUse
) {
523 if (IsMicrocodeInUse (MicrocodeFmpPrivate
->MicrocodeInfo
[Index
].MicrocodeEntryPoint
)) {
524 return MicrocodeFmpPrivate
->MicrocodeInfo
[Index
].MicrocodeEntryPoint
;
531 Get next Microcode entrypoint.
533 @param[in] MicrocodeFmpPrivate The Microcode driver private data
534 @param[in] MicrocodeEntryPoint Current Microcode entrypoint
536 @return next Microcode entrypoint.
538 CPU_MICROCODE_HEADER
*
540 IN MICROCODE_FMP_PRIVATE_DATA
*MicrocodeFmpPrivate
,
541 IN CPU_MICROCODE_HEADER
*MicrocodeEntryPoint
546 for (Index
= 0; Index
< MicrocodeFmpPrivate
->DescriptorCount
; Index
++) {
547 if (MicrocodeEntryPoint
== MicrocodeFmpPrivate
->MicrocodeInfo
[Index
].MicrocodeEntryPoint
) {
548 if (Index
== (UINTN
)MicrocodeFmpPrivate
->DescriptorCount
- 1) {
553 return MicrocodeFmpPrivate
->MicrocodeInfo
[Index
+ 1].MicrocodeEntryPoint
;
563 Get current Microcode used region size.
565 @param[in] MicrocodeFmpPrivate The Microcode driver private data
567 @return current Microcode used region size.
570 GetCurrentMicrocodeUsedRegionSize (
571 IN MICROCODE_FMP_PRIVATE_DATA
*MicrocodeFmpPrivate
574 if (MicrocodeFmpPrivate
->DescriptorCount
== 0) {
578 return (UINTN
)MicrocodeFmpPrivate
->MicrocodeInfo
[MicrocodeFmpPrivate
->DescriptorCount
- 1].MicrocodeEntryPoint
579 + (UINTN
)MicrocodeFmpPrivate
->MicrocodeInfo
[MicrocodeFmpPrivate
->DescriptorCount
- 1].TotalSize
580 - (UINTN
)MicrocodeFmpPrivate
->MicrocodePatchAddress
;
586 @param[in] Address The flash address of Microcode.
587 @param[in] Image The Microcode image buffer.
588 @param[in] ImageSize The size of Microcode image buffer in bytes.
589 @param[out] LastAttemptStatus The last attempt status, which will be recorded in ESRT and FMP EFI_FIRMWARE_IMAGE_DESCRIPTOR.
591 @retval EFI_SUCCESS The Microcode image is updated.
592 @retval EFI_WRITE_PROTECTED The flash device is read only.
599 OUT UINT32
*LastAttemptStatus
604 DEBUG((DEBUG_INFO
, "PlatformUpdate:"));
605 DEBUG((DEBUG_INFO
, " Address - 0x%lx,", Address
));
606 DEBUG((DEBUG_INFO
, " Legnth - 0x%x\n", ImageSize
));
608 Status
= MicrocodeFlashWrite (
613 if (!EFI_ERROR(Status
)) {
614 *LastAttemptStatus
= LAST_ATTEMPT_STATUS_SUCCESS
;
616 *LastAttemptStatus
= LAST_ATTEMPT_STATUS_ERROR_UNSUCCESSFUL
;
622 Update Microcode flash region.
624 @param[in] MicrocodeFmpPrivate The Microcode driver private data
625 @param[in] CurrentMicrocodeEntryPoint Current Microcode entrypoint
626 @param[in] Image The Microcode image buffer.
627 @param[in] ImageSize The size of Microcode image buffer in bytes.
628 @param[out] LastAttemptStatus The last attempt status, which will be recorded in ESRT and FMP EFI_FIRMWARE_IMAGE_DESCRIPTOR.
630 @retval EFI_SUCCESS The Microcode image is written.
631 @retval EFI_WRITE_PROTECTED The flash device is read only.
634 UpdateMicrocodeFlashRegion (
635 IN MICROCODE_FMP_PRIVATE_DATA
*MicrocodeFmpPrivate
,
636 IN CPU_MICROCODE_HEADER
*CurrentMicrocodeEntryPoint
,
639 OUT UINT32
*LastAttemptStatus
642 VOID
*MicrocodePatchAddress
;
643 UINTN MicrocodePatchRegionSize
;
644 UINTN CurrentTotalSize
;
645 UINTN UsedRegionSize
;
647 VOID
*MicrocodePatchScratchBuffer
;
648 UINT8
*ScratchBufferPtr
;
649 UINTN ScratchBufferSize
;
652 VOID
*NextMicrocodeEntryPoint
;
653 MICROCODE_INFO
*MicrocodeInfo
;
654 UINTN MicrocodeCount
;
657 DEBUG((DEBUG_INFO
, "UpdateMicrocodeFlashRegion: Image - 0x%x, size - 0x%x\n", Image
, ImageSize
));
659 MicrocodePatchAddress
= MicrocodeFmpPrivate
->MicrocodePatchAddress
;
660 MicrocodePatchRegionSize
= MicrocodeFmpPrivate
->MicrocodePatchRegionSize
;
662 MicrocodePatchScratchBuffer
= AllocateZeroPool (MicrocodePatchRegionSize
);
663 if (MicrocodePatchScratchBuffer
== NULL
) {
664 DEBUG((DEBUG_ERROR
, "Fail to allocate Microcode Scratch buffer\n"));
665 *LastAttemptStatus
= LAST_ATTEMPT_STATUS_ERROR_INSUFFICIENT_RESOURCES
;
666 return EFI_OUT_OF_RESOURCES
;
668 ScratchBufferPtr
= MicrocodePatchScratchBuffer
;
669 ScratchBufferSize
= 0;
672 // Current data collection
674 CurrentTotalSize
= 0;
676 NextMicrocodeEntryPoint
= NULL
;
677 if (CurrentMicrocodeEntryPoint
!= NULL
) {
678 if (CurrentMicrocodeEntryPoint
->DataSize
== 0) {
679 CurrentTotalSize
= 2048;
681 CurrentTotalSize
= CurrentMicrocodeEntryPoint
->TotalSize
;
683 DEBUG((DEBUG_INFO
, " CurrentTotalSize - 0x%x\n", CurrentTotalSize
));
684 NextMicrocodeEntryPoint
= GetNextMicrocode(MicrocodeFmpPrivate
, CurrentMicrocodeEntryPoint
);
685 DEBUG((DEBUG_INFO
, " NextMicrocodeEntryPoint - 0x%x\n", NextMicrocodeEntryPoint
));
686 if (NextMicrocodeEntryPoint
!= NULL
) {
687 ASSERT ((UINTN
)NextMicrocodeEntryPoint
>= ((UINTN
)CurrentMicrocodeEntryPoint
+ CurrentTotalSize
));
688 AvailableSize
= (UINTN
)NextMicrocodeEntryPoint
- (UINTN
)CurrentMicrocodeEntryPoint
;
690 AvailableSize
= (UINTN
)MicrocodePatchAddress
+ MicrocodePatchRegionSize
- (UINTN
)CurrentMicrocodeEntryPoint
;
692 DEBUG((DEBUG_INFO
, " AvailableSize - 0x%x\n", AvailableSize
));
694 ASSERT (AvailableSize
>= CurrentTotalSize
);
695 UsedRegionSize
= GetCurrentMicrocodeUsedRegionSize(MicrocodeFmpPrivate
);
696 DEBUG((DEBUG_INFO
, " UsedRegionSize - 0x%x\n", UsedRegionSize
));
697 ASSERT (UsedRegionSize
>= CurrentTotalSize
);
698 if (CurrentMicrocodeEntryPoint
!= NULL
) {
699 ASSERT ((UINTN
)MicrocodePatchAddress
+ UsedRegionSize
>= ((UINTN
)CurrentMicrocodeEntryPoint
+ CurrentTotalSize
));
702 // Total Size means the Microcode data size.
703 // Available Size means the Microcode data size plus the pad till next (1) Microcode or (2) the end.
706 // +------+-----------+-----+------+===================+
707 // | MCU1 | Microcode | PAD | MCU2 | Empty |
708 // +------+-----------+-----+------+===================+
710 // |<-AvailableSize->|
711 // |<- UsedRegionSize ->|
714 // +------+-----------+===================+
715 // | MCU | Microcode | Empty |
716 // +------+-----------+===================+
718 // |<- AvailableSize ->|
719 // |<-UsedRegionSize->|
723 // Update based on policy
727 // 1. If there is enough space to update old one in situ, replace old microcode in situ.
729 if (AvailableSize
>= ImageSize
) {
730 DEBUG((DEBUG_INFO
, "Replace old microcode in situ\n"));
732 // +------+------------+------+===================+
733 // |Other1| Old Image |Other2| Empty |
734 // +------+------------+------+===================+
736 // +------+---------+--+------+===================+
737 // |Other1|New Image|FF|Other2| Empty |
738 // +------+---------+--+------+===================+
740 // 1.1. Copy new image
741 CopyMem (ScratchBufferPtr
, Image
, ImageSize
);
742 ScratchBufferSize
+= ImageSize
;
743 ScratchBufferPtr
= (UINT8
*)ScratchBufferPtr
+ ScratchBufferSize
;
745 RestSize
= AvailableSize
- ImageSize
;
747 SetMem (ScratchBufferPtr
, RestSize
, 0xFF);
748 ScratchBufferSize
+= RestSize
;
749 ScratchBufferPtr
= (UINT8
*)ScratchBufferPtr
+ ScratchBufferSize
;
751 Status
= UpdateMicrocode((UINTN
)CurrentMicrocodeEntryPoint
, MicrocodePatchScratchBuffer
, ScratchBufferSize
, LastAttemptStatus
);
756 // 2. If there is enough space to remove old one and add new one, reorg and replace old microcode.
758 if (MicrocodePatchRegionSize
- (UsedRegionSize
- CurrentTotalSize
) >= ImageSize
) {
759 if (CurrentMicrocodeEntryPoint
== NULL
) {
760 DEBUG((DEBUG_INFO
, "Append new microcode\n"));
762 // +------+------------+------+===================+
763 // |Other1| Other |Other2| Empty |
764 // +------+------------+------+===================+
766 // +------+------------+------+-----------+=======+
767 // |Other1| Other |Other2| New Image | Empty |
768 // +------+------------+------+-----------+=======+
770 Status
= UpdateMicrocode((UINTN
)MicrocodePatchAddress
+ UsedRegionSize
, Image
, ImageSize
, LastAttemptStatus
);
772 DEBUG((DEBUG_INFO
, "Reorg and replace old microcode\n"));
774 // +------+------------+------+===================+
775 // |Other1| Old Image |Other2| Empty |
776 // +------+------------+------+===================+
778 // +------+---------------+------+================+
779 // |Other1| New Image |Other2| Empty |
780 // +------+---------------+------+================+
782 // 2.1. Copy new image
783 CopyMem (MicrocodePatchScratchBuffer
, Image
, ImageSize
);
784 ScratchBufferSize
+= ImageSize
;
785 ScratchBufferPtr
= (UINT8
*)ScratchBufferPtr
+ ScratchBufferSize
;
786 // 2.2. Copy rest images after the old image.
787 if (NextMicrocodeEntryPoint
!= 0) {
788 RestSize
= (UINTN
)MicrocodePatchAddress
+ UsedRegionSize
- ((UINTN
)NextMicrocodeEntryPoint
);
789 CopyMem (ScratchBufferPtr
, (UINT8
*)CurrentMicrocodeEntryPoint
+ CurrentTotalSize
, RestSize
);
790 ScratchBufferSize
+= RestSize
;
791 ScratchBufferPtr
= (UINT8
*)ScratchBufferPtr
+ ScratchBufferSize
;
793 Status
= UpdateMicrocode((UINTN
)CurrentMicrocodeEntryPoint
, MicrocodePatchScratchBuffer
, ScratchBufferSize
, LastAttemptStatus
);
799 // 3. The new image can be put in MCU region, but not all others can be put.
800 // So all the unused MCU is removed.
802 if (MicrocodePatchRegionSize
>= ImageSize
) {
804 // +------+------------+------+===================+
805 // |Other1| Old Image |Other2| Empty |
806 // +------+------------+------+===================+
808 // +-------------------------------------+--------+
809 // | New Image | Other |
810 // +-------------------------------------+--------+
812 DEBUG((DEBUG_INFO
, "Add new microcode from beginning\n"));
814 MicrocodeCount
= MicrocodeFmpPrivate
->DescriptorCount
;
815 MicrocodeInfo
= MicrocodeFmpPrivate
->MicrocodeInfo
;
817 // 3.1. Copy new image
818 CopyMem (MicrocodePatchScratchBuffer
, Image
, ImageSize
);
819 ScratchBufferSize
+= ImageSize
;
820 ScratchBufferPtr
= (UINT8
*)ScratchBufferPtr
+ ScratchBufferSize
;
821 // 3.2. Copy some others to rest buffer
822 for (Index
= 0; Index
< MicrocodeCount
; Index
++) {
823 if (!MicrocodeInfo
[Index
].InUse
) {
826 if (MicrocodeInfo
[Index
].MicrocodeEntryPoint
== CurrentMicrocodeEntryPoint
) {
829 if (MicrocodeInfo
[Index
].TotalSize
<= MicrocodePatchRegionSize
- ScratchBufferSize
) {
830 CopyMem (ScratchBufferPtr
, MicrocodeInfo
[Index
].MicrocodeEntryPoint
, MicrocodeInfo
[Index
].TotalSize
);
831 ScratchBufferSize
+= MicrocodeInfo
[Index
].TotalSize
;
832 ScratchBufferPtr
= (UINT8
*)ScratchBufferPtr
+ ScratchBufferSize
;
836 RestSize
= MicrocodePatchRegionSize
- ScratchBufferSize
;
838 SetMem (ScratchBufferPtr
, RestSize
, 0xFF);
839 ScratchBufferSize
+= RestSize
;
840 ScratchBufferPtr
= (UINT8
*)ScratchBufferPtr
+ ScratchBufferSize
;
842 Status
= UpdateMicrocode((UINTN
)MicrocodePatchAddress
, MicrocodePatchScratchBuffer
, ScratchBufferSize
, LastAttemptStatus
);
847 // 4. The new image size is bigger than the whole MCU region.
849 DEBUG((DEBUG_ERROR
, "Microcode too big\n"));
850 *LastAttemptStatus
= LAST_ATTEMPT_STATUS_ERROR_INSUFFICIENT_RESOURCES
;
851 Status
= EFI_OUT_OF_RESOURCES
;
859 Caution: This function may receive untrusted input.
861 @param[in] MicrocodeFmpPrivate The Microcode driver private data
862 @param[in] Image The Microcode image buffer.
863 @param[in] ImageSize The size of Microcode image buffer in bytes.
864 @param[out] LastAttemptVersion The last attempt version, which will be recorded in ESRT and FMP EFI_FIRMWARE_IMAGE_DESCRIPTOR.
865 @param[out] LastAttemptStatus The last attempt status, which will be recorded in ESRT and FMP EFI_FIRMWARE_IMAGE_DESCRIPTOR.
866 @param[out] AbortReason A pointer to a pointer to a null-terminated string providing more
867 details for the aborted operation. The buffer is allocated by this function
868 with AllocatePool(), and it is the caller's responsibility to free it with a
871 @retval EFI_SUCCESS The Microcode image is written.
872 @retval EFI_VOLUME_CORRUPTED The Microcode image is corrupt.
873 @retval EFI_INCOMPATIBLE_VERSION The Microcode image version is incorrect.
874 @retval EFI_SECURITY_VIOLATION The Microcode image fails to load.
875 @retval EFI_WRITE_PROTECTED The flash device is read only.
879 IN MICROCODE_FMP_PRIVATE_DATA
*MicrocodeFmpPrivate
,
882 OUT UINT32
*LastAttemptVersion
,
883 OUT UINT32
*LastAttemptStatus
,
884 OUT CHAR16
**AbortReason
889 CPU_MICROCODE_HEADER
*CurrentMicrocodeEntryPoint
;
892 // We must get Current MicrocodeEntrypoint *before* VerifyMicrocode,
893 // because the MicrocodeSignature might be updated in VerifyMicrocode.
895 CurrentMicrocodeEntryPoint
= GetCurrentMicrocodeInUse(MicrocodeFmpPrivate
);
896 DEBUG((DEBUG_INFO
, " CurrentMicrocodeEntryPoint - 0x%x\n", CurrentMicrocodeEntryPoint
));
899 // MCU must be 16 bytes aligned
901 AlignedImage
= AllocateCopyPool(ImageSize
, Image
);
902 if (AlignedImage
== NULL
) {
903 DEBUG((DEBUG_ERROR
, "Fail to allocate aligned image\n"));
904 *LastAttemptStatus
= LAST_ATTEMPT_STATUS_ERROR_INSUFFICIENT_RESOURCES
;
905 return EFI_OUT_OF_RESOURCES
;
908 *LastAttemptVersion
= ((CPU_MICROCODE_HEADER
*)Image
)->UpdateRevision
;
909 Status
= VerifyMicrocode(AlignedImage
, ImageSize
, TRUE
, LastAttemptStatus
, AbortReason
);
910 if (EFI_ERROR(Status
)) {
911 DEBUG((DEBUG_ERROR
, "Fail to verify Microcode Region\n"));
912 FreePool(AlignedImage
);
915 DEBUG((DEBUG_INFO
, "Pass VerifyMicrocode\n"));
917 Status
= UpdateMicrocodeFlashRegion(
919 CurrentMicrocodeEntryPoint
,
925 FreePool(AlignedImage
);