1 /** @file 2 SetImage instance to update Microcode. 3 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. 8 9 MicrocodeWrite() and VerifyMicrocode() will receive untrusted input and do basic validation. 10 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 16 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. 19 20 **/ 21 22 #include "MicrocodeUpdate.h" 23 24 /** 25 Get Microcode Region. 26 27 @param[out] MicrocodePatchAddress The address of Microcode 28 @param[out] MicrocodePatchRegionSize The region size of Microcode 29 30 @retval TRUE The Microcode region is returned. 31 @retval FALSE No Microcode region. 32 **/ 33 BOOLEAN 34 GetMicrocodeRegion ( 35 OUT VOID **MicrocodePatchAddress, 36 OUT UINTN *MicrocodePatchRegionSize 37 ) 38 { 39 *MicrocodePatchAddress = (VOID *)(UINTN)PcdGet64(PcdCpuMicrocodePatchAddress); 40 *MicrocodePatchRegionSize = (UINTN)PcdGet64(PcdCpuMicrocodePatchRegionSize); 41 42 if ((*MicrocodePatchAddress == NULL) || (*MicrocodePatchRegionSize == 0)) { 43 return FALSE; 44 } 45 46 return TRUE; 47 } 48 49 /** 50 Get Microcode update signature of currently loaded Microcode update. 51 52 @return Microcode signature. 53 54 **/ 55 UINT32 56 GetCurrentMicrocodeSignature ( 57 VOID 58 ) 59 { 60 UINT64 Signature; 61 62 AsmWriteMsr64(MSR_IA32_BIOS_SIGN_ID, 0); 63 AsmCpuid(CPUID_VERSION_INFO, NULL, NULL, NULL, NULL); 64 Signature = AsmReadMsr64(MSR_IA32_BIOS_SIGN_ID); 65 return (UINT32)RShiftU64(Signature, 32); 66 } 67 68 /** 69 Get current processor signature. 70 71 @return current processor signature. 72 **/ 73 UINT32 74 GetCurrentProcessorSignature ( 75 VOID 76 ) 77 { 78 UINT32 RegEax; 79 AsmCpuid(CPUID_VERSION_INFO, &RegEax, NULL, NULL, NULL); 80 return RegEax; 81 } 82 83 /** 84 Get current platform ID. 85 86 @return current platform ID. 87 **/ 88 UINT8 89 GetCurrentPlatformId ( 90 VOID 91 ) 92 { 93 UINT8 PlatformId; 94 95 PlatformId = (UINT8)AsmMsrBitFieldRead64(MSR_IA32_PLATFORM_ID, 50, 52); 96 return PlatformId; 97 } 98 99 /** 100 Load new Microcode. 101 102 @param[in] Address The address of new Microcode. 103 104 @return Loaded Microcode signature. 105 106 **/ 107 UINT32 108 LoadMicrocode ( 109 IN UINT64 Address 110 ) 111 { 112 AsmWriteMsr64(MSR_IA32_BIOS_UPDT_TRIG, Address); 113 return GetCurrentMicrocodeSignature(); 114 } 115 116 /** 117 Load Microcode on an Application Processor. 118 The function prototype for invoking a function on an Application Processor. 119 120 @param[in,out] Buffer The pointer to private data buffer. 121 **/ 122 VOID 123 EFIAPI 124 MicrocodeLoadAp ( 125 IN OUT VOID *Buffer 126 ) 127 { 128 MICROCODE_LOAD_BUFFER *MicrocodeLoadBuffer; 129 130 MicrocodeLoadBuffer = Buffer; 131 MicrocodeLoadBuffer->Revision = LoadMicrocode (MicrocodeLoadBuffer->Address); 132 } 133 134 /** 135 Load new Microcode on this processor 136 137 @param[in] MicrocodeFmpPrivate The Microcode driver private data 138 @param[in] CpuIndex The index of the processor. 139 @param[in] Address The address of new Microcode. 140 141 @return Loaded Microcode signature. 142 143 **/ 144 UINT32 145 LoadMicrocodeOnThis ( 146 IN MICROCODE_FMP_PRIVATE_DATA *MicrocodeFmpPrivate, 147 IN UINTN CpuIndex, 148 IN UINT64 Address 149 ) 150 { 151 EFI_STATUS Status; 152 EFI_MP_SERVICES_PROTOCOL *MpService; 153 MICROCODE_LOAD_BUFFER MicrocodeLoadBuffer; 154 155 if (CpuIndex == MicrocodeFmpPrivate->BspIndex) { 156 return LoadMicrocode (Address); 157 } else { 158 MpService = MicrocodeFmpPrivate->MpService; 159 MicrocodeLoadBuffer.Address = Address; 160 MicrocodeLoadBuffer.Revision = 0; 161 Status = MpService->StartupThisAP ( 162 MpService, 163 MicrocodeLoadAp, 164 CpuIndex, 165 NULL, 166 0, 167 &MicrocodeLoadBuffer, 168 NULL 169 ); 170 ASSERT_EFI_ERROR(Status); 171 return MicrocodeLoadBuffer.Revision; 172 } 173 } 174 175 /** 176 Collect processor information. 177 The function prototype for invoking a function on an Application Processor. 178 179 @param[in,out] Buffer The pointer to private data buffer. 180 **/ 181 VOID 182 EFIAPI 183 CollectProcessorInfo ( 184 IN OUT VOID *Buffer 185 ) 186 { 187 PROCESSOR_INFO *ProcessorInfo; 188 189 ProcessorInfo = Buffer; 190 ProcessorInfo->ProcessorSignature = GetCurrentProcessorSignature(); 191 ProcessorInfo->PlatformId = GetCurrentPlatformId(); 192 ProcessorInfo->MicrocodeRevision = GetCurrentMicrocodeSignature(); 193 } 194 195 /** 196 Get current Microcode information. 197 198 The ProcessorInformation (BspIndex/ProcessorCount/ProcessorInfo) 199 in MicrocodeFmpPrivate must be initialized. 200 201 The MicrocodeInformation (DescriptorCount/ImageDescriptor/MicrocodeInfo) 202 in MicrocodeFmpPrivate may not be avaiable in this function. 203 204 @param[in] MicrocodeFmpPrivate The Microcode driver private data 205 @param[in] DescriptorCount The count of Microcode ImageDescriptor allocated. 206 @param[out] ImageDescriptor Microcode ImageDescriptor 207 @param[out] MicrocodeInfo Microcode information 208 209 @return Microcode count 210 **/ 211 UINTN 212 GetMicrocodeInfo ( 213 IN MICROCODE_FMP_PRIVATE_DATA *MicrocodeFmpPrivate, 214 IN UINTN DescriptorCount, OPTIONAL 215 OUT EFI_FIRMWARE_IMAGE_DESCRIPTOR *ImageDescriptor, OPTIONAL 216 OUT MICROCODE_INFO *MicrocodeInfo OPTIONAL 217 ) 218 { 219 VOID *MicrocodePatchAddress; 220 UINTN MicrocodePatchRegionSize; 221 CPU_MICROCODE_HEADER *MicrocodeEntryPoint; 222 UINTN MicrocodeEnd; 223 UINTN TotalSize; 224 UINTN Count; 225 UINT64 ImageAttributes; 226 BOOLEAN IsInUse; 227 EFI_STATUS Status; 228 UINT32 AttemptStatus; 229 UINTN TargetCpuIndex; 230 231 MicrocodePatchAddress = MicrocodeFmpPrivate->MicrocodePatchAddress; 232 MicrocodePatchRegionSize = MicrocodeFmpPrivate->MicrocodePatchRegionSize; 233 234 DEBUG((DEBUG_INFO, "Microcode Region - 0x%x - 0x%x\n", MicrocodePatchAddress, MicrocodePatchRegionSize)); 235 236 Count = 0; 237 238 MicrocodeEnd = (UINTN)MicrocodePatchAddress + MicrocodePatchRegionSize; 239 MicrocodeEntryPoint = (CPU_MICROCODE_HEADER *) (UINTN) MicrocodePatchAddress; 240 do { 241 if (MicrocodeEntryPoint->HeaderVersion == 0x1 && MicrocodeEntryPoint->LoaderRevision == 0x1) { 242 // 243 // It is the microcode header. It is not the padding data between microcode patches 244 // becasue the padding data should not include 0x00000001 and it should be the repeated 245 // byte format (like 0xXYXYXYXY....). 246 // 247 if (MicrocodeEntryPoint->DataSize == 0) { 248 TotalSize = 2048; 249 } else { 250 TotalSize = MicrocodeEntryPoint->TotalSize; 251 } 252 253 TargetCpuIndex = (UINTN)-1; 254 Status = VerifyMicrocode(MicrocodeFmpPrivate, MicrocodeEntryPoint, TotalSize, FALSE, &AttemptStatus, NULL, &TargetCpuIndex); 255 if (!EFI_ERROR(Status)) { 256 IsInUse = TRUE; 257 ASSERT (TargetCpuIndex < MicrocodeFmpPrivate->ProcessorCount); 258 MicrocodeFmpPrivate->ProcessorInfo[TargetCpuIndex].MicrocodeIndex = Count; 259 } else { 260 IsInUse = FALSE; 261 } 262 263 if (ImageDescriptor != NULL && DescriptorCount > Count) { 264 ImageDescriptor[Count].ImageIndex = (UINT8)(Count + 1); 265 CopyGuid (&ImageDescriptor[Count].ImageTypeId, &gMicrocodeFmpImageTypeIdGuid); 266 ImageDescriptor[Count].ImageId = LShiftU64(MicrocodeEntryPoint->ProcessorFlags, 32) + MicrocodeEntryPoint->ProcessorSignature.Uint32; 267 ImageDescriptor[Count].ImageIdName = NULL; 268 ImageDescriptor[Count].Version = MicrocodeEntryPoint->UpdateRevision; 269 ImageDescriptor[Count].VersionName = NULL; 270 ImageDescriptor[Count].Size = TotalSize; 271 ImageAttributes = IMAGE_ATTRIBUTE_IMAGE_UPDATABLE | IMAGE_ATTRIBUTE_RESET_REQUIRED; 272 if (IsInUse) { 273 ImageAttributes |= IMAGE_ATTRIBUTE_IN_USE; 274 } 275 ImageDescriptor[Count].AttributesSupported = ImageAttributes | IMAGE_ATTRIBUTE_IN_USE; 276 ImageDescriptor[Count].AttributesSetting = ImageAttributes; 277 ImageDescriptor[Count].Compatibilities = 0; 278 ImageDescriptor[Count].LowestSupportedImageVersion = MicrocodeEntryPoint->UpdateRevision; // do not support rollback 279 ImageDescriptor[Count].LastAttemptVersion = 0; 280 ImageDescriptor[Count].LastAttemptStatus = 0; 281 ImageDescriptor[Count].HardwareInstance = 0; 282 } 283 if (MicrocodeInfo != NULL && DescriptorCount > Count) { 284 MicrocodeInfo[Count].MicrocodeEntryPoint = MicrocodeEntryPoint; 285 MicrocodeInfo[Count].TotalSize = TotalSize; 286 MicrocodeInfo[Count].InUse = IsInUse; 287 } 288 } else { 289 // 290 // It is the padding data between the microcode patches for microcode patches alignment. 291 // Because the microcode patch is the multiple of 1-KByte, the padding data should not 292 // exist if the microcode patch alignment value is not larger than 1-KByte. So, the microcode 293 // alignment value should be larger than 1-KByte. We could skip SIZE_1KB padding data to 294 // find the next possible microcode patch header. 295 // 296 MicrocodeEntryPoint = (CPU_MICROCODE_HEADER *) (((UINTN) MicrocodeEntryPoint) + SIZE_1KB); 297 continue; 298 } 299 300 Count++; 301 ASSERT(Count < 0xFF); 302 303 // 304 // Get the next patch. 305 // 306 MicrocodeEntryPoint = (CPU_MICROCODE_HEADER *) (((UINTN) MicrocodeEntryPoint) + TotalSize); 307 } while (((UINTN) MicrocodeEntryPoint < MicrocodeEnd)); 308 309 return Count; 310 } 311 312 /** 313 Return matched processor information. 314 315 @param[in] MicrocodeFmpPrivate The Microcode driver private data 316 @param[in] ProcessorSignature The processor signature to be matched 317 @param[in] ProcessorFlags The processor flags to be matched 318 @param[in, out] TargetCpuIndex On input, the index of target CPU which tries to match the Microcode. (UINTN)-1 means to try all. 319 On output, the index of target CPU which matches the Microcode. 320 321 @return matched processor information. 322 **/ 323 PROCESSOR_INFO * 324 GetMatchedProcessor ( 325 IN MICROCODE_FMP_PRIVATE_DATA *MicrocodeFmpPrivate, 326 IN UINT32 ProcessorSignature, 327 IN UINT32 ProcessorFlags, 328 IN OUT UINTN *TargetCpuIndex 329 ) 330 { 331 UINTN Index; 332 333 if (*TargetCpuIndex != (UINTN)-1) { 334 Index = *TargetCpuIndex; 335 if ((ProcessorSignature == MicrocodeFmpPrivate->ProcessorInfo[Index].ProcessorSignature) && 336 ((ProcessorFlags & (1 << MicrocodeFmpPrivate->ProcessorInfo[Index].PlatformId)) != 0)) { 337 return &MicrocodeFmpPrivate->ProcessorInfo[Index]; 338 } else { 339 return NULL; 340 } 341 } 342 343 for (Index = 0; Index < MicrocodeFmpPrivate->ProcessorCount; Index++) { 344 if ((ProcessorSignature == MicrocodeFmpPrivate->ProcessorInfo[Index].ProcessorSignature) && 345 ((ProcessorFlags & (1 << MicrocodeFmpPrivate->ProcessorInfo[Index].PlatformId)) != 0)) { 346 *TargetCpuIndex = Index; 347 return &MicrocodeFmpPrivate->ProcessorInfo[Index]; 348 } 349 } 350 return NULL; 351 } 352 353 /** 354 Verify Microcode. 355 356 Caution: This function may receive untrusted input. 357 358 @param[in] MicrocodeFmpPrivate The Microcode driver private data 359 @param[in] Image The Microcode image buffer. 360 @param[in] ImageSize The size of Microcode image buffer in bytes. 361 @param[in] TryLoad Try to load Microcode or not. 362 @param[out] LastAttemptStatus The last attempt status, which will be recorded in ESRT and FMP EFI_FIRMWARE_IMAGE_DESCRIPTOR. 363 @param[out] AbortReason A pointer to a pointer to a null-terminated string providing more 364 details for the aborted operation. The buffer is allocated by this function 365 with AllocatePool(), and it is the caller's responsibility to free it with a 366 call to FreePool(). 367 @param[in, out] TargetCpuIndex On input, the index of target CPU which tries to match the Microcode. (UINTN)-1 means to try all. 368 On output, the index of target CPU which matches the Microcode. 369 370 @retval EFI_SUCCESS The Microcode image passes verification. 371 @retval EFI_VOLUME_CORRUPTED The Microcode image is corrupt. 372 @retval EFI_INCOMPATIBLE_VERSION The Microcode image version is incorrect. 373 @retval EFI_UNSUPPORTED The Microcode ProcessorSignature or ProcessorFlags is incorrect. 374 @retval EFI_SECURITY_VIOLATION The Microcode image fails to load. 375 **/ 376 EFI_STATUS 377 VerifyMicrocode ( 378 IN MICROCODE_FMP_PRIVATE_DATA *MicrocodeFmpPrivate, 379 IN VOID *Image, 380 IN UINTN ImageSize, 381 IN BOOLEAN TryLoad, 382 OUT UINT32 *LastAttemptStatus, 383 OUT CHAR16 **AbortReason, OPTIONAL 384 IN OUT UINTN *TargetCpuIndex 385 ) 386 { 387 UINTN Index; 388 CPU_MICROCODE_HEADER *MicrocodeEntryPoint; 389 UINTN TotalSize; 390 UINTN DataSize; 391 UINT32 CurrentRevision; 392 PROCESSOR_INFO *ProcessorInfo; 393 UINT32 CheckSum32; 394 UINTN ExtendedTableLength; 395 UINT32 ExtendedTableCount; 396 CPU_MICROCODE_EXTENDED_TABLE *ExtendedTable; 397 CPU_MICROCODE_EXTENDED_TABLE_HEADER *ExtendedTableHeader; 398 BOOLEAN CorrectMicrocode; 399 400 // 401 // Check HeaderVersion 402 // 403 MicrocodeEntryPoint = Image; 404 if (MicrocodeEntryPoint->HeaderVersion != 0x1) { 405 DEBUG((DEBUG_ERROR, "VerifyMicrocode - fail on HeaderVersion\n")); 406 *LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_INVALID_FORMAT; 407 if (AbortReason != NULL) { 408 *AbortReason = AllocateCopyPool(sizeof(L"InvalidHeaderVersion"), L"InvalidHeaderVersion"); 409 } 410 return EFI_INCOMPATIBLE_VERSION; 411 } 412 // 413 // Check LoaderRevision 414 // 415 if (MicrocodeEntryPoint->LoaderRevision != 0x1) { 416 DEBUG((DEBUG_ERROR, "VerifyMicrocode - fail on LoaderRevision\n")); 417 *LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_INVALID_FORMAT; 418 if (AbortReason != NULL) { 419 *AbortReason = AllocateCopyPool(sizeof(L"InvalidLoaderVersion"), L"InvalidLoaderVersion"); 420 } 421 return EFI_INCOMPATIBLE_VERSION; 422 } 423 // 424 // Check Size 425 // 426 if (MicrocodeEntryPoint->DataSize == 0) { 427 TotalSize = 2048; 428 } else { 429 TotalSize = MicrocodeEntryPoint->TotalSize; 430 } 431 if (TotalSize <= sizeof(CPU_MICROCODE_HEADER)) { 432 DEBUG((DEBUG_ERROR, "VerifyMicrocode - TotalSize too small\n")); 433 *LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_INVALID_FORMAT; 434 if (AbortReason != NULL) { 435 *AbortReason = AllocateCopyPool(sizeof(L"InvalidTotalSize"), L"InvalidTotalSize"); 436 } 437 return EFI_VOLUME_CORRUPTED; 438 } 439 if (TotalSize != ImageSize) { 440 DEBUG((DEBUG_ERROR, "VerifyMicrocode - fail on TotalSize\n")); 441 *LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_INVALID_FORMAT; 442 if (AbortReason != NULL) { 443 *AbortReason = AllocateCopyPool(sizeof(L"InvalidTotalSize"), L"InvalidTotalSize"); 444 } 445 return EFI_VOLUME_CORRUPTED; 446 } 447 // 448 // Check CheckSum32 449 // 450 if (MicrocodeEntryPoint->DataSize == 0) { 451 DataSize = 2048 - sizeof(CPU_MICROCODE_HEADER); 452 } else { 453 DataSize = MicrocodeEntryPoint->DataSize; 454 } 455 if (DataSize > TotalSize - sizeof(CPU_MICROCODE_HEADER)) { 456 DEBUG((DEBUG_ERROR, "VerifyMicrocode - DataSize too big\n")); 457 *LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_INVALID_FORMAT; 458 if (AbortReason != NULL) { 459 *AbortReason = AllocateCopyPool(sizeof(L"InvalidDataSize"), L"InvalidDataSize"); 460 } 461 return EFI_VOLUME_CORRUPTED; 462 } 463 if ((DataSize & 0x3) != 0) { 464 DEBUG((DEBUG_ERROR, "VerifyMicrocode - DataSize not aligned\n")); 465 *LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_INVALID_FORMAT; 466 if (AbortReason != NULL) { 467 *AbortReason = AllocateCopyPool(sizeof(L"InvalidDataSize"), L"InvalidDataSize"); 468 } 469 return EFI_VOLUME_CORRUPTED; 470 } 471 CheckSum32 = CalculateSum32((UINT32 *)MicrocodeEntryPoint, DataSize + sizeof(CPU_MICROCODE_HEADER)); 472 if (CheckSum32 != 0) { 473 DEBUG((DEBUG_ERROR, "VerifyMicrocode - fail on CheckSum32\n")); 474 *LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_INVALID_FORMAT; 475 if (AbortReason != NULL) { 476 *AbortReason = AllocateCopyPool(sizeof(L"InvalidChecksum"), L"InvalidChecksum"); 477 } 478 return EFI_VOLUME_CORRUPTED; 479 } 480 481 // 482 // Check ProcessorSignature/ProcessorFlags 483 // 484 485 ProcessorInfo = GetMatchedProcessor (MicrocodeFmpPrivate, MicrocodeEntryPoint->ProcessorSignature.Uint32, MicrocodeEntryPoint->ProcessorFlags, TargetCpuIndex); 486 if (ProcessorInfo == NULL) { 487 CorrectMicrocode = FALSE; 488 ExtendedTableLength = TotalSize - (DataSize + sizeof(CPU_MICROCODE_HEADER)); 489 if (ExtendedTableLength != 0) { 490 // 491 // Extended Table exist, check if the CPU in support list 492 // 493 ExtendedTableHeader = (CPU_MICROCODE_EXTENDED_TABLE_HEADER *)((UINT8 *)(MicrocodeEntryPoint) + DataSize + sizeof(CPU_MICROCODE_HEADER)); 494 // 495 // Calculate Extended Checksum 496 // 497 if ((ExtendedTableLength > sizeof(CPU_MICROCODE_EXTENDED_TABLE_HEADER)) && ((ExtendedTableLength & 0x3) != 0)) { 498 CheckSum32 = CalculateSum32((UINT32 *)ExtendedTableHeader, ExtendedTableLength); 499 if (CheckSum32 == 0) { 500 // 501 // Checksum correct 502 // 503 ExtendedTableCount = ExtendedTableHeader->ExtendedSignatureCount; 504 if (ExtendedTableCount <= (ExtendedTableLength - sizeof(CPU_MICROCODE_EXTENDED_TABLE_HEADER)) / sizeof(CPU_MICROCODE_EXTENDED_TABLE)) { 505 ExtendedTable = (CPU_MICROCODE_EXTENDED_TABLE *)(ExtendedTableHeader + 1); 506 for (Index = 0; Index < ExtendedTableCount; Index++) { 507 CheckSum32 = CalculateSum32((UINT32 *)ExtendedTable, sizeof(CPU_MICROCODE_EXTENDED_TABLE)); 508 if (CheckSum32 == 0) { 509 // 510 // Verify Header 511 // 512 ProcessorInfo = GetMatchedProcessor (MicrocodeFmpPrivate, ExtendedTable->ProcessorSignature.Uint32, ExtendedTable->ProcessorFlag, TargetCpuIndex); 513 if (ProcessorInfo != NULL) { 514 // 515 // Find one 516 // 517 CorrectMicrocode = TRUE; 518 break; 519 } 520 } 521 ExtendedTable++; 522 } 523 } 524 } 525 } 526 } 527 if (!CorrectMicrocode) { 528 if (TryLoad) { 529 DEBUG((DEBUG_ERROR, "VerifyMicrocode - fail on CurrentProcessorSignature/ProcessorFlags\n")); 530 } 531 *LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_INCORRECT_VERSION; 532 if (AbortReason != NULL) { 533 *AbortReason = AllocateCopyPool(sizeof(L"UnsupportedProcessSignature/ProcessorFlags"), L"UnsupportedProcessSignature/ProcessorFlags"); 534 } 535 return EFI_UNSUPPORTED; 536 } 537 } 538 539 // 540 // Check UpdateRevision 541 // 542 CurrentRevision = ProcessorInfo->MicrocodeRevision; 543 if ((MicrocodeEntryPoint->UpdateRevision < CurrentRevision) || 544 (TryLoad && (MicrocodeEntryPoint->UpdateRevision == CurrentRevision))) { 545 if (TryLoad) { 546 DEBUG((DEBUG_ERROR, "VerifyMicrocode - fail on UpdateRevision\n")); 547 } 548 *LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_INCORRECT_VERSION; 549 if (AbortReason != NULL) { 550 *AbortReason = AllocateCopyPool(sizeof(L"IncorrectRevision"), L"IncorrectRevision"); 551 } 552 return EFI_INCOMPATIBLE_VERSION; 553 } 554 555 // 556 // try load MCU 557 // 558 if (TryLoad) { 559 CurrentRevision = LoadMicrocodeOnThis(MicrocodeFmpPrivate, ProcessorInfo->CpuIndex, (UINTN)MicrocodeEntryPoint + sizeof(CPU_MICROCODE_HEADER)); 560 if (MicrocodeEntryPoint->UpdateRevision != CurrentRevision) { 561 DEBUG((DEBUG_ERROR, "VerifyMicrocode - fail on LoadMicrocode\n")); 562 *LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_AUTH_ERROR; 563 if (AbortReason != NULL) { 564 *AbortReason = AllocateCopyPool(sizeof(L"InvalidData"), L"InvalidData"); 565 } 566 return EFI_SECURITY_VIOLATION; 567 } 568 } 569 570 return EFI_SUCCESS; 571 } 572 573 /** 574 Get next Microcode entrypoint. 575 576 @param[in] MicrocodeFmpPrivate The Microcode driver private data 577 @param[in] MicrocodeEntryPoint Current Microcode entrypoint 578 579 @return next Microcode entrypoint. 580 **/ 581 CPU_MICROCODE_HEADER * 582 GetNextMicrocode ( 583 IN MICROCODE_FMP_PRIVATE_DATA *MicrocodeFmpPrivate, 584 IN CPU_MICROCODE_HEADER *MicrocodeEntryPoint 585 ) 586 { 587 UINTN Index; 588 589 for (Index = 0; Index < MicrocodeFmpPrivate->DescriptorCount; Index++) { 590 if (MicrocodeEntryPoint == MicrocodeFmpPrivate->MicrocodeInfo[Index].MicrocodeEntryPoint) { 591 if (Index == (UINTN)MicrocodeFmpPrivate->DescriptorCount - 1) { 592 // it is last one 593 return NULL; 594 } else { 595 // return next one 596 return MicrocodeFmpPrivate->MicrocodeInfo[Index + 1].MicrocodeEntryPoint; 597 } 598 } 599 } 600 601 ASSERT(FALSE); 602 return NULL; 603 } 604 605 /** 606 Get current Microcode used region size. 607 608 @param[in] MicrocodeFmpPrivate The Microcode driver private data 609 610 @return current Microcode used region size. 611 **/ 612 UINTN 613 GetCurrentMicrocodeUsedRegionSize ( 614 IN MICROCODE_FMP_PRIVATE_DATA *MicrocodeFmpPrivate 615 ) 616 { 617 if (MicrocodeFmpPrivate->DescriptorCount == 0) { 618 return 0; 619 } 620 621 return (UINTN)MicrocodeFmpPrivate->MicrocodeInfo[MicrocodeFmpPrivate->DescriptorCount - 1].MicrocodeEntryPoint 622 + (UINTN)MicrocodeFmpPrivate->MicrocodeInfo[MicrocodeFmpPrivate->DescriptorCount - 1].TotalSize 623 - (UINTN)MicrocodeFmpPrivate->MicrocodePatchAddress; 624 } 625 626 /** 627 Update Microcode. 628 629 @param[in] Address The flash address of Microcode. 630 @param[in] Image The Microcode image buffer. 631 @param[in] ImageSize The size of Microcode image buffer in bytes. 632 @param[out] LastAttemptStatus The last attempt status, which will be recorded in ESRT and FMP EFI_FIRMWARE_IMAGE_DESCRIPTOR. 633 634 @retval EFI_SUCCESS The Microcode image is updated. 635 @retval EFI_WRITE_PROTECTED The flash device is read only. 636 **/ 637 EFI_STATUS 638 UpdateMicrocode ( 639 IN UINT64 Address, 640 IN VOID *Image, 641 IN UINTN ImageSize, 642 OUT UINT32 *LastAttemptStatus 643 ) 644 { 645 EFI_STATUS Status; 646 647 DEBUG((DEBUG_INFO, "PlatformUpdate:")); 648 DEBUG((DEBUG_INFO, " Address - 0x%lx,", Address)); 649 DEBUG((DEBUG_INFO, " Legnth - 0x%x\n", ImageSize)); 650 651 Status = MicrocodeFlashWrite ( 652 Address, 653 Image, 654 ImageSize 655 ); 656 if (!EFI_ERROR(Status)) { 657 *LastAttemptStatus = LAST_ATTEMPT_STATUS_SUCCESS; 658 } else { 659 *LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_UNSUCCESSFUL; 660 } 661 return Status; 662 } 663 664 /** 665 Update Microcode flash region. 666 667 @param[in] MicrocodeFmpPrivate The Microcode driver private data 668 @param[in] TargetMicrocodeEntryPoint Target Microcode entrypoint to be updated 669 @param[in] Image The Microcode image buffer. 670 @param[in] ImageSize The size of Microcode image buffer in bytes. 671 @param[out] LastAttemptStatus The last attempt status, which will be recorded in ESRT and FMP EFI_FIRMWARE_IMAGE_DESCRIPTOR. 672 673 @retval EFI_SUCCESS The Microcode image is written. 674 @retval EFI_WRITE_PROTECTED The flash device is read only. 675 **/ 676 EFI_STATUS 677 UpdateMicrocodeFlashRegion ( 678 IN MICROCODE_FMP_PRIVATE_DATA *MicrocodeFmpPrivate, 679 IN CPU_MICROCODE_HEADER *TargetMicrocodeEntryPoint, 680 IN VOID *Image, 681 IN UINTN ImageSize, 682 OUT UINT32 *LastAttemptStatus 683 ) 684 { 685 VOID *MicrocodePatchAddress; 686 UINTN MicrocodePatchRegionSize; 687 UINTN TargetTotalSize; 688 UINTN UsedRegionSize; 689 EFI_STATUS Status; 690 VOID *MicrocodePatchScratchBuffer; 691 UINT8 *ScratchBufferPtr; 692 UINTN ScratchBufferSize; 693 UINTN RestSize; 694 UINTN AvailableSize; 695 VOID *NextMicrocodeEntryPoint; 696 MICROCODE_INFO *MicrocodeInfo; 697 UINTN MicrocodeCount; 698 UINTN Index; 699 700 DEBUG((DEBUG_INFO, "UpdateMicrocodeFlashRegion: Image - 0x%x, size - 0x%x\n", Image, ImageSize)); 701 702 MicrocodePatchAddress = MicrocodeFmpPrivate->MicrocodePatchAddress; 703 MicrocodePatchRegionSize = MicrocodeFmpPrivate->MicrocodePatchRegionSize; 704 705 MicrocodePatchScratchBuffer = AllocateZeroPool (MicrocodePatchRegionSize); 706 if (MicrocodePatchScratchBuffer == NULL) { 707 DEBUG((DEBUG_ERROR, "Fail to allocate Microcode Scratch buffer\n")); 708 *LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_INSUFFICIENT_RESOURCES; 709 return EFI_OUT_OF_RESOURCES; 710 } 711 ScratchBufferPtr = MicrocodePatchScratchBuffer; 712 ScratchBufferSize = 0; 713 714 // 715 // Target data collection 716 // 717 TargetTotalSize = 0; 718 AvailableSize = 0; 719 NextMicrocodeEntryPoint = NULL; 720 if (TargetMicrocodeEntryPoint != NULL) { 721 if (TargetMicrocodeEntryPoint->DataSize == 0) { 722 TargetTotalSize = 2048; 723 } else { 724 TargetTotalSize = TargetMicrocodeEntryPoint->TotalSize; 725 } 726 DEBUG((DEBUG_INFO, " TargetTotalSize - 0x%x\n", TargetTotalSize)); 727 NextMicrocodeEntryPoint = GetNextMicrocode(MicrocodeFmpPrivate, TargetMicrocodeEntryPoint); 728 DEBUG((DEBUG_INFO, " NextMicrocodeEntryPoint - 0x%x\n", NextMicrocodeEntryPoint)); 729 if (NextMicrocodeEntryPoint != NULL) { 730 ASSERT ((UINTN)NextMicrocodeEntryPoint >= ((UINTN)TargetMicrocodeEntryPoint + TargetTotalSize)); 731 AvailableSize = (UINTN)NextMicrocodeEntryPoint - (UINTN)TargetMicrocodeEntryPoint; 732 } else { 733 AvailableSize = (UINTN)MicrocodePatchAddress + MicrocodePatchRegionSize - (UINTN)TargetMicrocodeEntryPoint; 734 } 735 DEBUG((DEBUG_INFO, " AvailableSize - 0x%x\n", AvailableSize)); 736 } 737 ASSERT (AvailableSize >= TargetTotalSize); 738 UsedRegionSize = GetCurrentMicrocodeUsedRegionSize(MicrocodeFmpPrivate); 739 DEBUG((DEBUG_INFO, " UsedRegionSize - 0x%x\n", UsedRegionSize)); 740 ASSERT (UsedRegionSize >= TargetTotalSize); 741 if (TargetMicrocodeEntryPoint != NULL) { 742 ASSERT ((UINTN)MicrocodePatchAddress + UsedRegionSize >= ((UINTN)TargetMicrocodeEntryPoint + TargetTotalSize)); 743 } 744 // 745 // Total Size means the Microcode data size. 746 // Available Size means the Microcode data size plus the pad till (1) next Microcode or (2) the end. 747 // 748 // (1) 749 // +------+-----------+-----+------+===================+ 750 // | MCU1 | Microcode | PAD | MCU2 | Empty | 751 // +------+-----------+-----+------+===================+ 752 // | TotalSize | 753 // |<-AvailableSize->| 754 // |<- UsedRegionSize ->| 755 // 756 // (2) 757 // +------+-----------+===================+ 758 // | MCU | Microcode | Empty | 759 // +------+-----------+===================+ 760 // | TotalSize | 761 // |<- AvailableSize ->| 762 // |<-UsedRegionSize->| 763 // 764 765 // 766 // Update based on policy 767 // 768 769 // 770 // 1. If there is enough space to update old one in situ, replace old microcode in situ. 771 // 772 if (AvailableSize >= ImageSize) { 773 DEBUG((DEBUG_INFO, "Replace old microcode in situ\n")); 774 // 775 // +------+------------+------+===================+ 776 // |Other1| Old Image |Other2| Empty | 777 // +------+------------+------+===================+ 778 // 779 // +------+---------+--+------+===================+ 780 // |Other1|New Image|FF|Other2| Empty | 781 // +------+---------+--+------+===================+ 782 // 783 // 1.1. Copy new image 784 CopyMem (ScratchBufferPtr, Image, ImageSize); 785 ScratchBufferSize += ImageSize; 786 ScratchBufferPtr = (UINT8 *)MicrocodePatchScratchBuffer + ScratchBufferSize; 787 // 1.2. Pad 0xFF 788 RestSize = AvailableSize - ImageSize; 789 if (RestSize > 0) { 790 SetMem (ScratchBufferPtr, RestSize, 0xFF); 791 ScratchBufferSize += RestSize; 792 ScratchBufferPtr = (UINT8 *)MicrocodePatchScratchBuffer + ScratchBufferSize; 793 } 794 Status = UpdateMicrocode((UINTN)TargetMicrocodeEntryPoint, MicrocodePatchScratchBuffer, ScratchBufferSize, LastAttemptStatus); 795 return Status; 796 } 797 798 // 799 // 2. If there is enough space to remove old one and add new one, reorg and replace old microcode. 800 // 801 if (MicrocodePatchRegionSize - (UsedRegionSize - TargetTotalSize) >= ImageSize) { 802 if (TargetMicrocodeEntryPoint == NULL) { 803 DEBUG((DEBUG_INFO, "Append new microcode\n")); 804 // 805 // +------+------------+------+===================+ 806 // |Other1| Other |Other2| Empty | 807 // +------+------------+------+===================+ 808 // 809 // +------+------------+------+-----------+=======+ 810 // |Other1| Other |Other2| New Image | Empty | 811 // +------+------------+------+-----------+=======+ 812 // 813 Status = UpdateMicrocode((UINTN)MicrocodePatchAddress + UsedRegionSize, Image, ImageSize, LastAttemptStatus); 814 } else { 815 DEBUG((DEBUG_INFO, "Reorg and replace old microcode\n")); 816 // 817 // +------+------------+------+===================+ 818 // |Other1| Old Image |Other2| Empty | 819 // +------+------------+------+===================+ 820 // 821 // +------+---------------+------+================+ 822 // |Other1| New Image |Other2| Empty | 823 // +------+---------------+------+================+ 824 // 825 // 2.1. Copy new image 826 CopyMem (ScratchBufferPtr, Image, ImageSize); 827 ScratchBufferSize += ImageSize; 828 ScratchBufferPtr = (UINT8 *)MicrocodePatchScratchBuffer + ScratchBufferSize; 829 // 2.2. Copy rest images after the old image. 830 if (NextMicrocodeEntryPoint != 0) { 831 RestSize = (UINTN)MicrocodePatchAddress + UsedRegionSize - ((UINTN)NextMicrocodeEntryPoint); 832 CopyMem (ScratchBufferPtr, (UINT8 *)TargetMicrocodeEntryPoint + TargetTotalSize, RestSize); 833 ScratchBufferSize += RestSize; 834 ScratchBufferPtr = (UINT8 *)MicrocodePatchScratchBuffer + ScratchBufferSize; 835 } 836 Status = UpdateMicrocode((UINTN)TargetMicrocodeEntryPoint, MicrocodePatchScratchBuffer, ScratchBufferSize, LastAttemptStatus); 837 } 838 return Status; 839 } 840 841 // 842 // 3. The new image can be put in MCU region, but not all others can be put. 843 // So all the unused MCU is removed. 844 // 845 if (MicrocodePatchRegionSize >= ImageSize) { 846 // 847 // +------+------------+------+===================+ 848 // |Other1| Old Image |Other2| Empty | 849 // +------+------------+------+===================+ 850 // 851 // +-------------------------------------+--------+ 852 // | New Image | Other | 853 // +-------------------------------------+--------+ 854 // 855 DEBUG((DEBUG_INFO, "Add new microcode from beginning\n")); 856 857 MicrocodeCount = MicrocodeFmpPrivate->DescriptorCount; 858 MicrocodeInfo = MicrocodeFmpPrivate->MicrocodeInfo; 859 860 // 3.1. Copy new image 861 CopyMem (ScratchBufferPtr, Image, ImageSize); 862 ScratchBufferSize += ImageSize; 863 ScratchBufferPtr = (UINT8 *)MicrocodePatchScratchBuffer + ScratchBufferSize; 864 // 3.2. Copy some others to rest buffer 865 for (Index = 0; Index < MicrocodeCount; Index++) { 866 if (!MicrocodeInfo[Index].InUse) { 867 continue; 868 } 869 if (MicrocodeInfo[Index].MicrocodeEntryPoint == TargetMicrocodeEntryPoint) { 870 continue; 871 } 872 if (MicrocodeInfo[Index].TotalSize <= MicrocodePatchRegionSize - ScratchBufferSize) { 873 CopyMem (ScratchBufferPtr, MicrocodeInfo[Index].MicrocodeEntryPoint, MicrocodeInfo[Index].TotalSize); 874 ScratchBufferSize += MicrocodeInfo[Index].TotalSize; 875 ScratchBufferPtr = (UINT8 *)MicrocodePatchScratchBuffer + ScratchBufferSize; 876 } 877 } 878 // 3.3. Pad 0xFF 879 RestSize = MicrocodePatchRegionSize - ScratchBufferSize; 880 if (RestSize > 0) { 881 SetMem (ScratchBufferPtr, RestSize, 0xFF); 882 ScratchBufferSize += RestSize; 883 ScratchBufferPtr = (UINT8 *)MicrocodePatchScratchBuffer + ScratchBufferSize; 884 } 885 Status = UpdateMicrocode((UINTN)MicrocodePatchAddress, MicrocodePatchScratchBuffer, ScratchBufferSize, LastAttemptStatus); 886 return Status; 887 } 888 889 // 890 // 4. The new image size is bigger than the whole MCU region. 891 // 892 DEBUG((DEBUG_ERROR, "Microcode too big\n")); 893 *LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_INSUFFICIENT_RESOURCES; 894 Status = EFI_OUT_OF_RESOURCES; 895 896 return Status; 897 } 898 899 /** 900 Write Microcode. 901 902 Caution: This function may receive untrusted input. 903 904 @param[in] MicrocodeFmpPrivate The Microcode driver private data 905 @param[in] Image The Microcode image buffer. 906 @param[in] ImageSize The size of Microcode image buffer in bytes. 907 @param[out] LastAttemptVersion The last attempt version, which will be recorded in ESRT and FMP EFI_FIRMWARE_IMAGE_DESCRIPTOR. 908 @param[out] LastAttemptStatus The last attempt status, which will be recorded in ESRT and FMP EFI_FIRMWARE_IMAGE_DESCRIPTOR. 909 @param[out] AbortReason A pointer to a pointer to a null-terminated string providing more 910 details for the aborted operation. The buffer is allocated by this function 911 with AllocatePool(), and it is the caller's responsibility to free it with a 912 call to FreePool(). 913 914 @retval EFI_SUCCESS The Microcode image is written. 915 @retval EFI_VOLUME_CORRUPTED The Microcode image is corrupt. 916 @retval EFI_INCOMPATIBLE_VERSION The Microcode image version is incorrect. 917 @retval EFI_SECURITY_VIOLATION The Microcode image fails to load. 918 @retval EFI_WRITE_PROTECTED The flash device is read only. 919 **/ 920 EFI_STATUS 921 MicrocodeWrite ( 922 IN MICROCODE_FMP_PRIVATE_DATA *MicrocodeFmpPrivate, 923 IN VOID *Image, 924 IN UINTN ImageSize, 925 OUT UINT32 *LastAttemptVersion, 926 OUT UINT32 *LastAttemptStatus, 927 OUT CHAR16 **AbortReason 928 ) 929 { 930 EFI_STATUS Status; 931 VOID *AlignedImage; 932 CPU_MICROCODE_HEADER *TargetMicrocodeEntryPoint; 933 UINTN TargetCpuIndex; 934 UINTN TargetMicrcodeIndex; 935 936 // 937 // MCU must be 16 bytes aligned 938 // 939 AlignedImage = AllocateCopyPool(ImageSize, Image); 940 if (AlignedImage == NULL) { 941 DEBUG((DEBUG_ERROR, "Fail to allocate aligned image\n")); 942 *LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_INSUFFICIENT_RESOURCES; 943 return EFI_OUT_OF_RESOURCES; 944 } 945 946 *LastAttemptVersion = ((CPU_MICROCODE_HEADER *)Image)->UpdateRevision; 947 TargetCpuIndex = (UINTN)-1; 948 Status = VerifyMicrocode(MicrocodeFmpPrivate, AlignedImage, ImageSize, TRUE, LastAttemptStatus, AbortReason, &TargetCpuIndex); 949 if (EFI_ERROR(Status)) { 950 DEBUG((DEBUG_ERROR, "Fail to verify Microcode Region\n")); 951 FreePool(AlignedImage); 952 return Status; 953 } 954 DEBUG((DEBUG_INFO, "Pass VerifyMicrocode\n")); 955 956 DEBUG((DEBUG_INFO, " TargetCpuIndex - 0x%x\n", TargetCpuIndex)); 957 ASSERT (TargetCpuIndex < MicrocodeFmpPrivate->ProcessorCount); 958 TargetMicrcodeIndex = MicrocodeFmpPrivate->ProcessorInfo[TargetCpuIndex].MicrocodeIndex; 959 DEBUG((DEBUG_INFO, " TargetMicrcodeIndex - 0x%x\n", TargetMicrcodeIndex)); 960 if (TargetMicrcodeIndex != (UINTN)-1) { 961 ASSERT (TargetMicrcodeIndex < MicrocodeFmpPrivate->DescriptorCount); 962 TargetMicrocodeEntryPoint = MicrocodeFmpPrivate->MicrocodeInfo[TargetMicrcodeIndex].MicrocodeEntryPoint; 963 } else { 964 TargetMicrocodeEntryPoint = NULL; 965 } 966 DEBUG((DEBUG_INFO, " TargetMicrocodeEntryPoint - 0x%x\n", TargetMicrocodeEntryPoint)); 967 968 Status = UpdateMicrocodeFlashRegion( 969 MicrocodeFmpPrivate, 970 TargetMicrocodeEntryPoint, 971 AlignedImage, 972 ImageSize, 973 LastAttemptStatus 974 ); 975 976 FreePool(AlignedImage); 977 978 return Status; 979 } 980 981 982