]> git.proxmox.com Git - mirror_edk2.git/blob - IntelSiliconPkg/Feature/Capsule/MicrocodeUpdateDxe/MicrocodeUpdate.c
65729e30a019b2bcc1cc768c9b28e13a544a0cd9
[mirror_edk2.git] / IntelSiliconPkg / Feature / Capsule / MicrocodeUpdateDxe / MicrocodeUpdate.c
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 - 2018, 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 TotalSize
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 & (SIZE_1KB - 1)) != 0) {
440 DEBUG((DEBUG_ERROR, "VerifyMicrocode - TotalSize is not multiples of 1024 bytes (1 KBytes)\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 if (TotalSize != ImageSize) {
448 DEBUG((DEBUG_ERROR, "VerifyMicrocode - fail on TotalSize\n"));
449 *LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_INVALID_FORMAT;
450 if (AbortReason != NULL) {
451 *AbortReason = AllocateCopyPool(sizeof(L"InvalidTotalSize"), L"InvalidTotalSize");
452 }
453 return EFI_VOLUME_CORRUPTED;
454 }
455 //
456 // Check DataSize
457 //
458 if (MicrocodeEntryPoint->DataSize == 0) {
459 DataSize = 2048 - sizeof(CPU_MICROCODE_HEADER);
460 } else {
461 DataSize = MicrocodeEntryPoint->DataSize;
462 }
463 if (DataSize > TotalSize - sizeof(CPU_MICROCODE_HEADER)) {
464 DEBUG((DEBUG_ERROR, "VerifyMicrocode - DataSize too big\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 if ((DataSize & 0x3) != 0) {
472 DEBUG((DEBUG_ERROR, "VerifyMicrocode - DataSize is not multiples of DWORDs\n"));
473 *LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_INVALID_FORMAT;
474 if (AbortReason != NULL) {
475 *AbortReason = AllocateCopyPool(sizeof(L"InvalidDataSize"), L"InvalidDataSize");
476 }
477 return EFI_VOLUME_CORRUPTED;
478 }
479 //
480 // Check CheckSum32
481 //
482 CheckSum32 = CalculateSum32((UINT32 *)MicrocodeEntryPoint, DataSize + sizeof(CPU_MICROCODE_HEADER));
483 if (CheckSum32 != 0) {
484 DEBUG((DEBUG_ERROR, "VerifyMicrocode - fail on CheckSum32\n"));
485 *LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_INVALID_FORMAT;
486 if (AbortReason != NULL) {
487 *AbortReason = AllocateCopyPool(sizeof(L"InvalidChecksum"), L"InvalidChecksum");
488 }
489 return EFI_VOLUME_CORRUPTED;
490 }
491
492 //
493 // Check ProcessorSignature/ProcessorFlags
494 //
495
496 ProcessorInfo = GetMatchedProcessor (MicrocodeFmpPrivate, MicrocodeEntryPoint->ProcessorSignature.Uint32, MicrocodeEntryPoint->ProcessorFlags, TargetCpuIndex);
497 if (ProcessorInfo == NULL) {
498 CorrectMicrocode = FALSE;
499 ExtendedTableLength = TotalSize - (DataSize + sizeof(CPU_MICROCODE_HEADER));
500 if (ExtendedTableLength != 0) {
501 //
502 // Extended Table exist, check if the CPU in support list
503 //
504 ExtendedTableHeader = (CPU_MICROCODE_EXTENDED_TABLE_HEADER *)((UINT8 *)(MicrocodeEntryPoint) + DataSize + sizeof(CPU_MICROCODE_HEADER));
505 //
506 // Calculate Extended Checksum
507 //
508 if ((ExtendedTableLength > sizeof(CPU_MICROCODE_EXTENDED_TABLE_HEADER)) && ((ExtendedTableLength & 0x3) == 0)) {
509 CheckSum32 = CalculateSum32((UINT32 *)ExtendedTableHeader, ExtendedTableLength);
510 if (CheckSum32 == 0) {
511 //
512 // Checksum correct
513 //
514 ExtendedTableCount = ExtendedTableHeader->ExtendedSignatureCount;
515 if (ExtendedTableCount <= (ExtendedTableLength - sizeof(CPU_MICROCODE_EXTENDED_TABLE_HEADER)) / sizeof(CPU_MICROCODE_EXTENDED_TABLE)) {
516 ExtendedTable = (CPU_MICROCODE_EXTENDED_TABLE *)(ExtendedTableHeader + 1);
517 for (Index = 0; Index < ExtendedTableCount; Index++) {
518 CheckSum32 = CalculateSum32((UINT32 *)ExtendedTable, sizeof(CPU_MICROCODE_EXTENDED_TABLE));
519 if (CheckSum32 == 0) {
520 //
521 // Verify Header
522 //
523 ProcessorInfo = GetMatchedProcessor (MicrocodeFmpPrivate, ExtendedTable->ProcessorSignature.Uint32, ExtendedTable->ProcessorFlag, TargetCpuIndex);
524 if (ProcessorInfo != NULL) {
525 //
526 // Find one
527 //
528 CorrectMicrocode = TRUE;
529 break;
530 }
531 }
532 ExtendedTable++;
533 }
534 }
535 }
536 }
537 }
538 if (!CorrectMicrocode) {
539 if (TryLoad) {
540 DEBUG((DEBUG_ERROR, "VerifyMicrocode - fail on CurrentProcessorSignature/ProcessorFlags\n"));
541 }
542 *LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_INCORRECT_VERSION;
543 if (AbortReason != NULL) {
544 *AbortReason = AllocateCopyPool(sizeof(L"UnsupportedProcessSignature/ProcessorFlags"), L"UnsupportedProcessSignature/ProcessorFlags");
545 }
546 return EFI_UNSUPPORTED;
547 }
548 }
549
550 //
551 // Check UpdateRevision
552 //
553 CurrentRevision = ProcessorInfo->MicrocodeRevision;
554 if ((MicrocodeEntryPoint->UpdateRevision < CurrentRevision) ||
555 (TryLoad && (MicrocodeEntryPoint->UpdateRevision == CurrentRevision))) {
556 if (TryLoad) {
557 DEBUG((DEBUG_ERROR, "VerifyMicrocode - fail on UpdateRevision\n"));
558 }
559 *LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_INCORRECT_VERSION;
560 if (AbortReason != NULL) {
561 *AbortReason = AllocateCopyPool(sizeof(L"IncorrectRevision"), L"IncorrectRevision");
562 }
563 return EFI_INCOMPATIBLE_VERSION;
564 }
565
566 //
567 // try load MCU
568 //
569 if (TryLoad) {
570 CurrentRevision = LoadMicrocodeOnThis(MicrocodeFmpPrivate, ProcessorInfo->CpuIndex, (UINTN)MicrocodeEntryPoint + sizeof(CPU_MICROCODE_HEADER));
571 if (MicrocodeEntryPoint->UpdateRevision != CurrentRevision) {
572 DEBUG((DEBUG_ERROR, "VerifyMicrocode - fail on LoadMicrocode\n"));
573 *LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_AUTH_ERROR;
574 if (AbortReason != NULL) {
575 *AbortReason = AllocateCopyPool(sizeof(L"InvalidData"), L"InvalidData");
576 }
577 return EFI_SECURITY_VIOLATION;
578 }
579 }
580
581 return EFI_SUCCESS;
582 }
583
584 /**
585 Get next Microcode entrypoint.
586
587 @param[in] MicrocodeFmpPrivate The Microcode driver private data
588 @param[in] MicrocodeEntryPoint Current Microcode entrypoint
589
590 @return next Microcode entrypoint.
591 **/
592 CPU_MICROCODE_HEADER *
593 GetNextMicrocode (
594 IN MICROCODE_FMP_PRIVATE_DATA *MicrocodeFmpPrivate,
595 IN CPU_MICROCODE_HEADER *MicrocodeEntryPoint
596 )
597 {
598 UINTN Index;
599
600 for (Index = 0; Index < MicrocodeFmpPrivate->DescriptorCount; Index++) {
601 if (MicrocodeEntryPoint == MicrocodeFmpPrivate->MicrocodeInfo[Index].MicrocodeEntryPoint) {
602 if (Index == (UINTN)MicrocodeFmpPrivate->DescriptorCount - 1) {
603 // it is last one
604 return NULL;
605 } else {
606 // return next one
607 return MicrocodeFmpPrivate->MicrocodeInfo[Index + 1].MicrocodeEntryPoint;
608 }
609 }
610 }
611
612 ASSERT(FALSE);
613 return NULL;
614 }
615
616 /**
617 Get current Microcode used region size.
618
619 @param[in] MicrocodeFmpPrivate The Microcode driver private data
620
621 @return current Microcode used region size.
622 **/
623 UINTN
624 GetCurrentMicrocodeUsedRegionSize (
625 IN MICROCODE_FMP_PRIVATE_DATA *MicrocodeFmpPrivate
626 )
627 {
628 if (MicrocodeFmpPrivate->DescriptorCount == 0) {
629 return 0;
630 }
631
632 return (UINTN)MicrocodeFmpPrivate->MicrocodeInfo[MicrocodeFmpPrivate->DescriptorCount - 1].MicrocodeEntryPoint
633 + (UINTN)MicrocodeFmpPrivate->MicrocodeInfo[MicrocodeFmpPrivate->DescriptorCount - 1].TotalSize
634 - (UINTN)MicrocodeFmpPrivate->MicrocodePatchAddress;
635 }
636
637 /**
638 Update Microcode.
639
640 @param[in] Address The flash address of Microcode.
641 @param[in] Image The Microcode image buffer.
642 @param[in] ImageSize The size of Microcode image buffer in bytes.
643 @param[out] LastAttemptStatus The last attempt status, which will be recorded in ESRT and FMP EFI_FIRMWARE_IMAGE_DESCRIPTOR.
644
645 @retval EFI_SUCCESS The Microcode image is updated.
646 @retval EFI_WRITE_PROTECTED The flash device is read only.
647 **/
648 EFI_STATUS
649 UpdateMicrocode (
650 IN UINT64 Address,
651 IN VOID *Image,
652 IN UINTN ImageSize,
653 OUT UINT32 *LastAttemptStatus
654 )
655 {
656 EFI_STATUS Status;
657
658 DEBUG((DEBUG_INFO, "PlatformUpdate:"));
659 DEBUG((DEBUG_INFO, " Address - 0x%lx,", Address));
660 DEBUG((DEBUG_INFO, " Legnth - 0x%x\n", ImageSize));
661
662 Status = MicrocodeFlashWrite (
663 Address,
664 Image,
665 ImageSize
666 );
667 if (!EFI_ERROR(Status)) {
668 *LastAttemptStatus = LAST_ATTEMPT_STATUS_SUCCESS;
669 } else {
670 *LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_UNSUCCESSFUL;
671 }
672 return Status;
673 }
674
675 /**
676 Update Microcode flash region.
677
678 @param[in] MicrocodeFmpPrivate The Microcode driver private data
679 @param[in] TargetMicrocodeEntryPoint Target Microcode entrypoint to be updated
680 @param[in] Image The Microcode image buffer.
681 @param[in] ImageSize The size of Microcode image buffer in bytes.
682 @param[out] LastAttemptStatus The last attempt status, which will be recorded in ESRT and FMP EFI_FIRMWARE_IMAGE_DESCRIPTOR.
683
684 @retval EFI_SUCCESS The Microcode image is written.
685 @retval EFI_WRITE_PROTECTED The flash device is read only.
686 **/
687 EFI_STATUS
688 UpdateMicrocodeFlashRegion (
689 IN MICROCODE_FMP_PRIVATE_DATA *MicrocodeFmpPrivate,
690 IN CPU_MICROCODE_HEADER *TargetMicrocodeEntryPoint,
691 IN VOID *Image,
692 IN UINTN ImageSize,
693 OUT UINT32 *LastAttemptStatus
694 )
695 {
696 VOID *MicrocodePatchAddress;
697 UINTN MicrocodePatchRegionSize;
698 UINTN TargetTotalSize;
699 UINTN UsedRegionSize;
700 EFI_STATUS Status;
701 VOID *MicrocodePatchScratchBuffer;
702 UINT8 *ScratchBufferPtr;
703 UINTN ScratchBufferSize;
704 UINTN RestSize;
705 UINTN AvailableSize;
706 VOID *NextMicrocodeEntryPoint;
707 MICROCODE_INFO *MicrocodeInfo;
708 UINTN MicrocodeCount;
709 UINTN Index;
710
711 DEBUG((DEBUG_INFO, "UpdateMicrocodeFlashRegion: Image - 0x%x, size - 0x%x\n", Image, ImageSize));
712
713 MicrocodePatchAddress = MicrocodeFmpPrivate->MicrocodePatchAddress;
714 MicrocodePatchRegionSize = MicrocodeFmpPrivate->MicrocodePatchRegionSize;
715
716 MicrocodePatchScratchBuffer = AllocateZeroPool (MicrocodePatchRegionSize);
717 if (MicrocodePatchScratchBuffer == NULL) {
718 DEBUG((DEBUG_ERROR, "Fail to allocate Microcode Scratch buffer\n"));
719 *LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_INSUFFICIENT_RESOURCES;
720 return EFI_OUT_OF_RESOURCES;
721 }
722 ScratchBufferPtr = MicrocodePatchScratchBuffer;
723 ScratchBufferSize = 0;
724
725 //
726 // Target data collection
727 //
728 TargetTotalSize = 0;
729 AvailableSize = 0;
730 NextMicrocodeEntryPoint = NULL;
731 if (TargetMicrocodeEntryPoint != NULL) {
732 if (TargetMicrocodeEntryPoint->DataSize == 0) {
733 TargetTotalSize = 2048;
734 } else {
735 TargetTotalSize = TargetMicrocodeEntryPoint->TotalSize;
736 }
737 DEBUG((DEBUG_INFO, " TargetTotalSize - 0x%x\n", TargetTotalSize));
738 NextMicrocodeEntryPoint = GetNextMicrocode(MicrocodeFmpPrivate, TargetMicrocodeEntryPoint);
739 DEBUG((DEBUG_INFO, " NextMicrocodeEntryPoint - 0x%x\n", NextMicrocodeEntryPoint));
740 if (NextMicrocodeEntryPoint != NULL) {
741 ASSERT ((UINTN)NextMicrocodeEntryPoint >= ((UINTN)TargetMicrocodeEntryPoint + TargetTotalSize));
742 AvailableSize = (UINTN)NextMicrocodeEntryPoint - (UINTN)TargetMicrocodeEntryPoint;
743 } else {
744 AvailableSize = (UINTN)MicrocodePatchAddress + MicrocodePatchRegionSize - (UINTN)TargetMicrocodeEntryPoint;
745 }
746 DEBUG((DEBUG_INFO, " AvailableSize - 0x%x\n", AvailableSize));
747 }
748 ASSERT (AvailableSize >= TargetTotalSize);
749 UsedRegionSize = GetCurrentMicrocodeUsedRegionSize(MicrocodeFmpPrivate);
750 DEBUG((DEBUG_INFO, " UsedRegionSize - 0x%x\n", UsedRegionSize));
751 ASSERT (UsedRegionSize >= TargetTotalSize);
752 if (TargetMicrocodeEntryPoint != NULL) {
753 ASSERT ((UINTN)MicrocodePatchAddress + UsedRegionSize >= ((UINTN)TargetMicrocodeEntryPoint + TargetTotalSize));
754 }
755 //
756 // Total Size means the Microcode data size.
757 // Available Size means the Microcode data size plus the pad till (1) next Microcode or (2) the end.
758 //
759 // (1)
760 // +------+-----------+-----+------+===================+
761 // | MCU1 | Microcode | PAD | MCU2 | Empty |
762 // +------+-----------+-----+------+===================+
763 // | TotalSize |
764 // |<-AvailableSize->|
765 // |<- UsedRegionSize ->|
766 //
767 // (2)
768 // +------+-----------+===================+
769 // | MCU | Microcode | Empty |
770 // +------+-----------+===================+
771 // | TotalSize |
772 // |<- AvailableSize ->|
773 // |<-UsedRegionSize->|
774 //
775
776 //
777 // Update based on policy
778 //
779
780 //
781 // 1. If there is enough space to update old one in situ, replace old microcode in situ.
782 //
783 if (AvailableSize >= ImageSize) {
784 DEBUG((DEBUG_INFO, "Replace old microcode in situ\n"));
785 //
786 // +------+------------+------+===================+
787 // |Other1| Old Image |Other2| Empty |
788 // +------+------------+------+===================+
789 //
790 // +------+---------+--+------+===================+
791 // |Other1|New Image|FF|Other2| Empty |
792 // +------+---------+--+------+===================+
793 //
794 // 1.1. Copy new image
795 CopyMem (ScratchBufferPtr, Image, ImageSize);
796 ScratchBufferSize += ImageSize;
797 ScratchBufferPtr = (UINT8 *)MicrocodePatchScratchBuffer + ScratchBufferSize;
798 // 1.2. Pad 0xFF
799 RestSize = AvailableSize - ImageSize;
800 if (RestSize > 0) {
801 SetMem (ScratchBufferPtr, RestSize, 0xFF);
802 ScratchBufferSize += RestSize;
803 ScratchBufferPtr = (UINT8 *)MicrocodePatchScratchBuffer + ScratchBufferSize;
804 }
805 Status = UpdateMicrocode((UINTN)TargetMicrocodeEntryPoint, MicrocodePatchScratchBuffer, ScratchBufferSize, LastAttemptStatus);
806 return Status;
807 }
808
809 //
810 // 2. If there is enough space to remove old one and add new one, reorg and replace old microcode.
811 //
812 if (MicrocodePatchRegionSize - (UsedRegionSize - TargetTotalSize) >= ImageSize) {
813 if (TargetMicrocodeEntryPoint == NULL) {
814 DEBUG((DEBUG_INFO, "Append new microcode\n"));
815 //
816 // +------+------------+------+===================+
817 // |Other1| Other |Other2| Empty |
818 // +------+------------+------+===================+
819 //
820 // +------+------------+------+-----------+=======+
821 // |Other1| Other |Other2| New Image | Empty |
822 // +------+------------+------+-----------+=======+
823 //
824 Status = UpdateMicrocode((UINTN)MicrocodePatchAddress + UsedRegionSize, Image, ImageSize, LastAttemptStatus);
825 } else {
826 DEBUG((DEBUG_INFO, "Reorg and replace old microcode\n"));
827 //
828 // +------+------------+------+===================+
829 // |Other1| Old Image |Other2| Empty |
830 // +------+------------+------+===================+
831 //
832 // +------+---------------+------+================+
833 // |Other1| New Image |Other2| Empty |
834 // +------+---------------+------+================+
835 //
836 // 2.1. Copy new image
837 CopyMem (ScratchBufferPtr, Image, ImageSize);
838 ScratchBufferSize += ImageSize;
839 ScratchBufferPtr = (UINT8 *)MicrocodePatchScratchBuffer + ScratchBufferSize;
840 // 2.2. Copy rest images after the old image.
841 if (NextMicrocodeEntryPoint != 0) {
842 RestSize = (UINTN)MicrocodePatchAddress + UsedRegionSize - ((UINTN)NextMicrocodeEntryPoint);
843 CopyMem (ScratchBufferPtr, (UINT8 *)TargetMicrocodeEntryPoint + TargetTotalSize, RestSize);
844 ScratchBufferSize += RestSize;
845 ScratchBufferPtr = (UINT8 *)MicrocodePatchScratchBuffer + ScratchBufferSize;
846 }
847 Status = UpdateMicrocode((UINTN)TargetMicrocodeEntryPoint, MicrocodePatchScratchBuffer, ScratchBufferSize, LastAttemptStatus);
848 }
849 return Status;
850 }
851
852 //
853 // 3. The new image can be put in MCU region, but not all others can be put.
854 // So all the unused MCU is removed.
855 //
856 if (MicrocodePatchRegionSize >= ImageSize) {
857 //
858 // +------+------------+------+===================+
859 // |Other1| Old Image |Other2| Empty |
860 // +------+------------+------+===================+
861 //
862 // +-------------------------------------+--------+
863 // | New Image | Other |
864 // +-------------------------------------+--------+
865 //
866 DEBUG((DEBUG_INFO, "Add new microcode from beginning\n"));
867
868 MicrocodeCount = MicrocodeFmpPrivate->DescriptorCount;
869 MicrocodeInfo = MicrocodeFmpPrivate->MicrocodeInfo;
870
871 // 3.1. Copy new image
872 CopyMem (ScratchBufferPtr, Image, ImageSize);
873 ScratchBufferSize += ImageSize;
874 ScratchBufferPtr = (UINT8 *)MicrocodePatchScratchBuffer + ScratchBufferSize;
875 // 3.2. Copy some others to rest buffer
876 for (Index = 0; Index < MicrocodeCount; Index++) {
877 if (!MicrocodeInfo[Index].InUse) {
878 continue;
879 }
880 if (MicrocodeInfo[Index].MicrocodeEntryPoint == TargetMicrocodeEntryPoint) {
881 continue;
882 }
883 if (MicrocodeInfo[Index].TotalSize <= MicrocodePatchRegionSize - ScratchBufferSize) {
884 CopyMem (ScratchBufferPtr, MicrocodeInfo[Index].MicrocodeEntryPoint, MicrocodeInfo[Index].TotalSize);
885 ScratchBufferSize += MicrocodeInfo[Index].TotalSize;
886 ScratchBufferPtr = (UINT8 *)MicrocodePatchScratchBuffer + ScratchBufferSize;
887 }
888 }
889 // 3.3. Pad 0xFF
890 RestSize = MicrocodePatchRegionSize - ScratchBufferSize;
891 if (RestSize > 0) {
892 SetMem (ScratchBufferPtr, RestSize, 0xFF);
893 ScratchBufferSize += RestSize;
894 ScratchBufferPtr = (UINT8 *)MicrocodePatchScratchBuffer + ScratchBufferSize;
895 }
896 Status = UpdateMicrocode((UINTN)MicrocodePatchAddress, MicrocodePatchScratchBuffer, ScratchBufferSize, LastAttemptStatus);
897 return Status;
898 }
899
900 //
901 // 4. The new image size is bigger than the whole MCU region.
902 //
903 DEBUG((DEBUG_ERROR, "Microcode too big\n"));
904 *LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_INSUFFICIENT_RESOURCES;
905 Status = EFI_OUT_OF_RESOURCES;
906
907 return Status;
908 }
909
910 /**
911 Write Microcode.
912
913 Caution: This function may receive untrusted input.
914
915 @param[in] MicrocodeFmpPrivate The Microcode driver private data
916 @param[in] Image The Microcode image buffer.
917 @param[in] ImageSize The size of Microcode image buffer in bytes.
918 @param[out] LastAttemptVersion The last attempt version, which will be recorded in ESRT and FMP EFI_FIRMWARE_IMAGE_DESCRIPTOR.
919 @param[out] LastAttemptStatus The last attempt status, which will be recorded in ESRT and FMP EFI_FIRMWARE_IMAGE_DESCRIPTOR.
920 @param[out] AbortReason A pointer to a pointer to a null-terminated string providing more
921 details for the aborted operation. The buffer is allocated by this function
922 with AllocatePool(), and it is the caller's responsibility to free it with a
923 call to FreePool().
924
925 @retval EFI_SUCCESS The Microcode image is written.
926 @retval EFI_VOLUME_CORRUPTED The Microcode image is corrupt.
927 @retval EFI_INCOMPATIBLE_VERSION The Microcode image version is incorrect.
928 @retval EFI_SECURITY_VIOLATION The Microcode image fails to load.
929 @retval EFI_WRITE_PROTECTED The flash device is read only.
930 **/
931 EFI_STATUS
932 MicrocodeWrite (
933 IN MICROCODE_FMP_PRIVATE_DATA *MicrocodeFmpPrivate,
934 IN VOID *Image,
935 IN UINTN ImageSize,
936 OUT UINT32 *LastAttemptVersion,
937 OUT UINT32 *LastAttemptStatus,
938 OUT CHAR16 **AbortReason
939 )
940 {
941 EFI_STATUS Status;
942 VOID *AlignedImage;
943 CPU_MICROCODE_HEADER *TargetMicrocodeEntryPoint;
944 UINTN TargetCpuIndex;
945 UINTN TargetMicrcodeIndex;
946
947 //
948 // MCU must be 16 bytes aligned
949 //
950 AlignedImage = AllocateCopyPool(ImageSize, Image);
951 if (AlignedImage == NULL) {
952 DEBUG((DEBUG_ERROR, "Fail to allocate aligned image\n"));
953 *LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_INSUFFICIENT_RESOURCES;
954 return EFI_OUT_OF_RESOURCES;
955 }
956
957 *LastAttemptVersion = ((CPU_MICROCODE_HEADER *)Image)->UpdateRevision;
958 TargetCpuIndex = (UINTN)-1;
959 Status = VerifyMicrocode(MicrocodeFmpPrivate, AlignedImage, ImageSize, TRUE, LastAttemptStatus, AbortReason, &TargetCpuIndex);
960 if (EFI_ERROR(Status)) {
961 DEBUG((DEBUG_ERROR, "Fail to verify Microcode Region\n"));
962 FreePool(AlignedImage);
963 return Status;
964 }
965 DEBUG((DEBUG_INFO, "Pass VerifyMicrocode\n"));
966
967 DEBUG((DEBUG_INFO, " TargetCpuIndex - 0x%x\n", TargetCpuIndex));
968 ASSERT (TargetCpuIndex < MicrocodeFmpPrivate->ProcessorCount);
969 TargetMicrcodeIndex = MicrocodeFmpPrivate->ProcessorInfo[TargetCpuIndex].MicrocodeIndex;
970 DEBUG((DEBUG_INFO, " TargetMicrcodeIndex - 0x%x\n", TargetMicrcodeIndex));
971 if (TargetMicrcodeIndex != (UINTN)-1) {
972 ASSERT (TargetMicrcodeIndex < MicrocodeFmpPrivate->DescriptorCount);
973 TargetMicrocodeEntryPoint = MicrocodeFmpPrivate->MicrocodeInfo[TargetMicrcodeIndex].MicrocodeEntryPoint;
974 } else {
975 TargetMicrocodeEntryPoint = NULL;
976 }
977 DEBUG((DEBUG_INFO, " TargetMicrocodeEntryPoint - 0x%x\n", TargetMicrocodeEntryPoint));
978
979 Status = UpdateMicrocodeFlashRegion(
980 MicrocodeFmpPrivate,
981 TargetMicrocodeEntryPoint,
982 AlignedImage,
983 ImageSize,
984 LastAttemptStatus
985 );
986
987 FreePool(AlignedImage);
988
989 return Status;
990 }
991
992