]> git.proxmox.com Git - mirror_edk2.git/blob - UefiCpuPkg/Feature/Capsule/MicrocodeUpdateDxe/MicrocodeUpdate.c
46739ba57a2ca5de333cb3bb6e462c2e47703c9a
[mirror_edk2.git] / UefiCpuPkg / 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, 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 /**
26 Verify Microcode.
27
28 Caution: This function may receive untrusted input.
29
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
37 call to FreePool().
38
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.
44 **/
45 EFI_STATUS
46 VerifyMicrocode (
47 IN VOID *Image,
48 IN UINTN ImageSize,
49 IN BOOLEAN TryLoad,
50 OUT UINT32 *LastAttemptStatus,
51 OUT CHAR16 **AbortReason
52 );
53
54 /**
55 Get Microcode Region.
56
57 @param[out] MicrocodePatchAddress The address of Microcode
58 @param[out] MicrocodePatchRegionSize The region size of Microcode
59
60 @retval TRUE The Microcode region is returned.
61 @retval FALSE No Microcode region.
62 **/
63 BOOLEAN
64 GetMicrocodeRegion (
65 OUT VOID **MicrocodePatchAddress,
66 OUT UINTN *MicrocodePatchRegionSize
67 )
68 {
69 *MicrocodePatchAddress = (VOID *)(UINTN)PcdGet64(PcdCpuMicrocodePatchAddress);
70 *MicrocodePatchRegionSize = (UINTN)PcdGet64(PcdCpuMicrocodePatchRegionSize);
71
72 if ((*MicrocodePatchAddress == NULL) || (*MicrocodePatchRegionSize == 0)) {
73 return FALSE;
74 }
75
76 return TRUE;
77 }
78
79 /**
80 Get Microcode update signature of currently loaded Microcode update.
81
82 @return Microcode signature.
83
84 **/
85 UINT32
86 GetCurrentMicrocodeSignature (
87 VOID
88 )
89 {
90 UINT64 Signature;
91
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);
96 }
97
98 /**
99 Get current processor signature.
100
101 @return current processor signature.
102 **/
103 UINT32
104 GetCurrentProcessorSignature (
105 VOID
106 )
107 {
108 UINT32 RegEax;
109 AsmCpuid(CPUID_VERSION_INFO, &RegEax, NULL, NULL, NULL);
110 return RegEax;
111 }
112
113 /**
114 Get current platform ID.
115
116 @return current platform ID.
117 **/
118 UINT8
119 GetCurrentPlatformId (
120 VOID
121 )
122 {
123 UINT8 PlatformId;
124
125 PlatformId = (UINT8)AsmMsrBitFieldRead64(MSR_IA32_PLATFORM_ID, 50, 52);
126 return PlatformId;
127 }
128
129 /**
130 Load new Microcode.
131
132 @param[in] Address The address of new Microcode.
133
134 @return Loaded Microcode signature.
135
136 **/
137 UINT32
138 LoadMicrocode (
139 IN UINT64 Address
140 )
141 {
142 AsmWriteMsr64(MSR_IA32_BIOS_UPDT_TRIG, Address);
143 return GetCurrentMicrocodeSignature();
144 }
145
146 /**
147 If the Microcode is used by current processor.
148
149 @param[in] MicrocodeEntryPoint The Microcode buffer
150
151 @retval TRUE The Microcode is used by current processor.
152 @retval FALSE The Microcode is NOT used by current processor.
153 **/
154 BOOLEAN
155 IsMicrocodeInUse (
156 IN CPU_MICROCODE_HEADER *MicrocodeEntryPoint
157 )
158 {
159 UINT32 AttemptStatus;
160 UINTN TotalSize;
161 EFI_STATUS Status;
162
163 if (MicrocodeEntryPoint->HeaderVersion == 0x1 && MicrocodeEntryPoint->LoaderRevision == 0x1) {
164 //
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....).
168 //
169 if (MicrocodeEntryPoint->DataSize == 0) {
170 TotalSize = 2048;
171 } else {
172 TotalSize = MicrocodeEntryPoint->TotalSize;
173 }
174 Status = VerifyMicrocode(MicrocodeEntryPoint, TotalSize, FALSE, &AttemptStatus, NULL);
175 if (!EFI_ERROR(Status)) {
176 return TRUE;
177 }
178 }
179 return FALSE;
180 }
181
182 /**
183 Get current Microcode information.
184
185 NOTE: The DescriptorCount/ImageDescriptor/MicrocodeInfo in MicrocodeFmpPrivate
186 are not avaiable in this function.
187
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
192
193 @return Microcode count
194 **/
195 UINTN
196 GetMicrocodeInfo (
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
201 )
202 {
203 VOID *MicrocodePatchAddress;
204 UINTN MicrocodePatchRegionSize;
205 CPU_MICROCODE_HEADER *MicrocodeEntryPoint;
206 UINTN MicrocodeEnd;
207 UINTN TotalSize;
208 UINTN Count;
209 UINT64 ImageAttributes;
210 BOOLEAN IsInUse;
211
212 MicrocodePatchAddress = MicrocodeFmpPrivate->MicrocodePatchAddress;
213 MicrocodePatchRegionSize = MicrocodeFmpPrivate->MicrocodePatchRegionSize;
214
215 DEBUG((DEBUG_INFO, "Microcode Region - 0x%x - 0x%x\n", MicrocodePatchAddress, MicrocodePatchRegionSize));
216
217 Count = 0;
218
219 MicrocodeEnd = (UINTN)MicrocodePatchAddress + MicrocodePatchRegionSize;
220 MicrocodeEntryPoint = (CPU_MICROCODE_HEADER *) (UINTN) MicrocodePatchAddress;
221 do {
222 if (MicrocodeEntryPoint->HeaderVersion == 0x1 && MicrocodeEntryPoint->LoaderRevision == 0x1) {
223 //
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....).
227 //
228 if (MicrocodeEntryPoint->DataSize == 0) {
229 TotalSize = 2048;
230 } else {
231 TotalSize = MicrocodeEntryPoint->TotalSize;
232 }
233
234 IsInUse = IsMicrocodeInUse (MicrocodeEntryPoint);
235
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;
245 if (IsInUse) {
246 ImageAttributes |= IMAGE_ATTRIBUTE_IN_USE;
247 }
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;
255 }
256 if (MicrocodeInfo != NULL && DescriptorCount > Count) {
257 MicrocodeInfo[Count].MicrocodeEntryPoint = MicrocodeEntryPoint;
258 MicrocodeInfo[Count].TotalSize = TotalSize;
259 MicrocodeInfo[Count].InUse = IsInUse;
260 }
261 } else {
262 //
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.
268 //
269 MicrocodeEntryPoint = (CPU_MICROCODE_HEADER *) (((UINTN) MicrocodeEntryPoint) + SIZE_1KB);
270 continue;
271 }
272
273 Count++;
274 ASSERT(Count < 0xFF);
275
276 //
277 // Get the next patch.
278 //
279 MicrocodeEntryPoint = (CPU_MICROCODE_HEADER *) (((UINTN) MicrocodeEntryPoint) + TotalSize);
280 } while (((UINTN) MicrocodeEntryPoint < MicrocodeEnd));
281
282 return Count;
283 }
284
285 /**
286 Verify Microcode.
287
288 Caution: This function may receive untrusted input.
289
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
297 call to FreePool().
298
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.
304 **/
305 EFI_STATUS
306 VerifyMicrocode (
307 IN VOID *Image,
308 IN UINTN ImageSize,
309 IN BOOLEAN TryLoad,
310 OUT UINT32 *LastAttemptStatus,
311 OUT CHAR16 **AbortReason
312 )
313 {
314 UINTN Index;
315 CPU_MICROCODE_HEADER *MicrocodeEntryPoint;
316 UINTN TotalSize;
317 UINTN DataSize;
318 UINT32 CurrentRevision;
319 UINT32 CurrentProcessorSignature;
320 UINT8 CurrentPlatformId;
321 UINT32 CheckSum32;
322 UINTN ExtendedTableLength;
323 UINT32 ExtendedTableCount;
324 CPU_MICROCODE_EXTENDED_TABLE *ExtendedTable;
325 CPU_MICROCODE_EXTENDED_TABLE_HEADER *ExtendedTableHeader;
326 BOOLEAN CorrectMicrocode;
327
328 //
329 // Check HeaderVersion
330 //
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");
337 }
338 return EFI_INCOMPATIBLE_VERSION;
339 }
340 //
341 // Check LoaderRevision
342 //
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");
348 }
349 return EFI_INCOMPATIBLE_VERSION;
350 }
351 //
352 // Check Size
353 //
354 if (MicrocodeEntryPoint->DataSize == 0) {
355 TotalSize = 2048;
356 } else {
357 TotalSize = MicrocodeEntryPoint->TotalSize;
358 }
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");
364 }
365 return EFI_VOLUME_CORRUPTED;
366 }
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");
372 }
373 return EFI_VOLUME_CORRUPTED;
374 }
375 //
376 // Check CheckSum32
377 //
378 if (MicrocodeEntryPoint->DataSize == 0) {
379 DataSize = 2048 - sizeof(CPU_MICROCODE_HEADER);
380 } else {
381 DataSize = MicrocodeEntryPoint->DataSize;
382 }
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");
388 }
389 return EFI_VOLUME_CORRUPTED;
390 }
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");
396 }
397 return EFI_VOLUME_CORRUPTED;
398 }
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");
405 }
406 return EFI_VOLUME_CORRUPTED;
407 }
408
409 //
410 // Check ProcessorSignature/ProcessorFlags
411 //
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) {
419 //
420 // Extended Table exist, check if the CPU in support list
421 //
422 ExtendedTableHeader = (CPU_MICROCODE_EXTENDED_TABLE_HEADER *)((UINT8 *)(MicrocodeEntryPoint) + DataSize + sizeof(CPU_MICROCODE_HEADER));
423 //
424 // Calculate Extended Checksum
425 //
426 if ((ExtendedTableLength > sizeof(CPU_MICROCODE_EXTENDED_TABLE_HEADER)) && ((ExtendedTableLength & 0x3) != 0)) {
427 CheckSum32 = CalculateSum32((UINT32 *)ExtendedTableHeader, ExtendedTableLength);
428 if (CheckSum32 == 0) {
429 //
430 // Checksum correct
431 //
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) {
438 //
439 // Verify Header
440 //
441 if ((ExtendedTable->ProcessorSignature.Uint32 == CurrentProcessorSignature) &&
442 (ExtendedTable->ProcessorFlag & (1 << CurrentPlatformId))) {
443 //
444 // Find one
445 //
446 CorrectMicrocode = TRUE;
447 break;
448 }
449 }
450 ExtendedTable++;
451 }
452 }
453 }
454 }
455 }
456 if (!CorrectMicrocode) {
457 if (TryLoad) {
458 DEBUG((DEBUG_ERROR, "VerifyMicrocode - fail on CurrentProcessorSignature/ProcessorFlags\n"));
459 }
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");
464 } else {
465 *AbortReason = AllocateCopyPool(sizeof(L"UnsupportedProcessorFlags"), L"UnsupportedProcessorFlags");
466 }
467 }
468 return EFI_UNSUPPORTED;
469 }
470 }
471
472 //
473 // Check UpdateRevision
474 //
475 CurrentRevision = GetCurrentMicrocodeSignature();
476 if (MicrocodeEntryPoint->UpdateRevision < CurrentRevision) {
477 if (TryLoad) {
478 DEBUG((DEBUG_ERROR, "VerifyMicrocode - fail on UpdateRevision\n"));
479 }
480 *LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_INCORRECT_VERSION;
481 if (AbortReason != NULL) {
482 *AbortReason = AllocateCopyPool(sizeof(L"IncorrectRevision"), L"IncorrectRevision");
483 }
484 return EFI_INCOMPATIBLE_VERSION;
485 }
486
487 //
488 // try load MCU
489 //
490 if (TryLoad) {
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");
497 }
498 return EFI_SECURITY_VIOLATION;
499 }
500 }
501
502 return EFI_SUCCESS;
503 }
504
505 /**
506 Get current Microcode in used.
507
508 @param[in] MicrocodeFmpPrivate The Microcode driver private data
509
510 @return current Microcode in used.
511 **/
512 VOID *
513 GetCurrentMicrocodeInUse (
514 IN MICROCODE_FMP_PRIVATE_DATA *MicrocodeFmpPrivate
515 )
516 {
517 UINTN Index;
518
519 for (Index = 0; Index < MicrocodeFmpPrivate->DescriptorCount; Index++) {
520 if (!MicrocodeFmpPrivate->MicrocodeInfo[Index].InUse) {
521 continue;
522 }
523 if (IsMicrocodeInUse (MicrocodeFmpPrivate->MicrocodeInfo[Index].MicrocodeEntryPoint)) {
524 return MicrocodeFmpPrivate->MicrocodeInfo[Index].MicrocodeEntryPoint;
525 }
526 }
527 return NULL;
528 }
529
530 /**
531 Get next Microcode entrypoint.
532
533 @param[in] MicrocodeFmpPrivate The Microcode driver private data
534 @param[in] MicrocodeEntryPoint Current Microcode entrypoint
535
536 @return next Microcode entrypoint.
537 **/
538 CPU_MICROCODE_HEADER *
539 GetNextMicrocode (
540 IN MICROCODE_FMP_PRIVATE_DATA *MicrocodeFmpPrivate,
541 IN CPU_MICROCODE_HEADER *MicrocodeEntryPoint
542 )
543 {
544 UINTN Index;
545
546 for (Index = 0; Index < MicrocodeFmpPrivate->DescriptorCount; Index++) {
547 if (MicrocodeEntryPoint == MicrocodeFmpPrivate->MicrocodeInfo[Index].MicrocodeEntryPoint) {
548 if (Index == (UINTN)MicrocodeFmpPrivate->DescriptorCount - 1) {
549 // it is last one
550 return NULL;
551 } else {
552 // return next one
553 return MicrocodeFmpPrivate->MicrocodeInfo[Index + 1].MicrocodeEntryPoint;
554 }
555 }
556 }
557
558 ASSERT(FALSE);
559 return NULL;
560 }
561
562 /**
563 Get current Microcode used region size.
564
565 @param[in] MicrocodeFmpPrivate The Microcode driver private data
566
567 @return current Microcode used region size.
568 **/
569 UINTN
570 GetCurrentMicrocodeUsedRegionSize (
571 IN MICROCODE_FMP_PRIVATE_DATA *MicrocodeFmpPrivate
572 )
573 {
574 if (MicrocodeFmpPrivate->DescriptorCount == 0) {
575 return 0;
576 }
577
578 return (UINTN)MicrocodeFmpPrivate->MicrocodeInfo[MicrocodeFmpPrivate->DescriptorCount - 1].MicrocodeEntryPoint
579 + (UINTN)MicrocodeFmpPrivate->MicrocodeInfo[MicrocodeFmpPrivate->DescriptorCount - 1].TotalSize
580 - (UINTN)MicrocodeFmpPrivate->MicrocodePatchAddress;
581 }
582
583 /**
584 Update Microcode.
585
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.
590
591 @retval EFI_SUCCESS The Microcode image is updated.
592 @retval EFI_WRITE_PROTECTED The flash device is read only.
593 **/
594 EFI_STATUS
595 UpdateMicrocode (
596 IN UINT64 Address,
597 IN VOID *Image,
598 IN UINTN ImageSize,
599 OUT UINT32 *LastAttemptStatus
600 )
601 {
602 EFI_STATUS Status;
603
604 DEBUG((DEBUG_INFO, "PlatformUpdate:"));
605 DEBUG((DEBUG_INFO, " Address - 0x%lx,", Address));
606 DEBUG((DEBUG_INFO, " Legnth - 0x%x\n", ImageSize));
607
608 Status = MicrocodeFlashWrite (
609 Address,
610 Image,
611 ImageSize
612 );
613 if (!EFI_ERROR(Status)) {
614 *LastAttemptStatus = LAST_ATTEMPT_STATUS_SUCCESS;
615 } else {
616 *LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_UNSUCCESSFUL;
617 }
618 return Status;
619 }
620
621 /**
622 Update Microcode flash region.
623
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.
629
630 @retval EFI_SUCCESS The Microcode image is written.
631 @retval EFI_WRITE_PROTECTED The flash device is read only.
632 **/
633 EFI_STATUS
634 UpdateMicrocodeFlashRegion (
635 IN MICROCODE_FMP_PRIVATE_DATA *MicrocodeFmpPrivate,
636 IN CPU_MICROCODE_HEADER *CurrentMicrocodeEntryPoint,
637 IN VOID *Image,
638 IN UINTN ImageSize,
639 OUT UINT32 *LastAttemptStatus
640 )
641 {
642 VOID *MicrocodePatchAddress;
643 UINTN MicrocodePatchRegionSize;
644 UINTN CurrentTotalSize;
645 UINTN UsedRegionSize;
646 EFI_STATUS Status;
647 VOID *MicrocodePatchScratchBuffer;
648 UINT8 *ScratchBufferPtr;
649 UINTN ScratchBufferSize;
650 UINTN RestSize;
651 UINTN AvailableSize;
652 VOID *NextMicrocodeEntryPoint;
653 MICROCODE_INFO *MicrocodeInfo;
654 UINTN MicrocodeCount;
655 UINTN Index;
656
657 DEBUG((DEBUG_INFO, "UpdateMicrocodeFlashRegion: Image - 0x%x, size - 0x%x\n", Image, ImageSize));
658
659 MicrocodePatchAddress = MicrocodeFmpPrivate->MicrocodePatchAddress;
660 MicrocodePatchRegionSize = MicrocodeFmpPrivate->MicrocodePatchRegionSize;
661
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;
667 }
668 ScratchBufferPtr = MicrocodePatchScratchBuffer;
669 ScratchBufferSize = 0;
670
671 //
672 // Current data collection
673 //
674 CurrentTotalSize = 0;
675 AvailableSize = 0;
676 NextMicrocodeEntryPoint = NULL;
677 if (CurrentMicrocodeEntryPoint != NULL) {
678 if (CurrentMicrocodeEntryPoint->DataSize == 0) {
679 CurrentTotalSize = 2048;
680 } else {
681 CurrentTotalSize = CurrentMicrocodeEntryPoint->TotalSize;
682 }
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;
689 } else {
690 AvailableSize = (UINTN)MicrocodePatchAddress + MicrocodePatchRegionSize - (UINTN)CurrentMicrocodeEntryPoint;
691 }
692 DEBUG((DEBUG_INFO, " AvailableSize - 0x%x\n", AvailableSize));
693 }
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));
700 }
701 //
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.
704 //
705 // (1)
706 // +------+-----------+-----+------+===================+
707 // | MCU1 | Microcode | PAD | MCU2 | Empty |
708 // +------+-----------+-----+------+===================+
709 // | TotalSize |
710 // |<-AvailableSize->|
711 // |<- UsedRegionSize ->|
712 //
713 // (2)
714 // +------+-----------+===================+
715 // | MCU | Microcode | Empty |
716 // +------+-----------+===================+
717 // | TotalSize |
718 // |<- AvailableSize ->|
719 // |<-UsedRegionSize->|
720 //
721
722 //
723 // Update based on policy
724 //
725
726 //
727 // 1. If there is enough space to update old one in situ, replace old microcode in situ.
728 //
729 if (AvailableSize >= ImageSize) {
730 DEBUG((DEBUG_INFO, "Replace old microcode in situ\n"));
731 //
732 // +------+------------+------+===================+
733 // |Other1| Old Image |Other2| Empty |
734 // +------+------------+------+===================+
735 //
736 // +------+---------+--+------+===================+
737 // |Other1|New Image|FF|Other2| Empty |
738 // +------+---------+--+------+===================+
739 //
740 // 1.1. Copy new image
741 CopyMem (ScratchBufferPtr, Image, ImageSize);
742 ScratchBufferSize += ImageSize;
743 ScratchBufferPtr = (UINT8 *)ScratchBufferPtr + ScratchBufferSize;
744 // 1.2. Pad 0xFF
745 RestSize = AvailableSize - ImageSize;
746 if (RestSize > 0) {
747 SetMem (ScratchBufferPtr, RestSize, 0xFF);
748 ScratchBufferSize += RestSize;
749 ScratchBufferPtr = (UINT8 *)ScratchBufferPtr + ScratchBufferSize;
750 }
751 Status = UpdateMicrocode((UINTN)CurrentMicrocodeEntryPoint, MicrocodePatchScratchBuffer, ScratchBufferSize, LastAttemptStatus);
752 return Status;
753 }
754
755 //
756 // 2. If there is enough space to remove old one and add new one, reorg and replace old microcode.
757 //
758 if (MicrocodePatchRegionSize - (UsedRegionSize - CurrentTotalSize) >= ImageSize) {
759 if (CurrentMicrocodeEntryPoint == NULL) {
760 DEBUG((DEBUG_INFO, "Append new microcode\n"));
761 //
762 // +------+------------+------+===================+
763 // |Other1| Other |Other2| Empty |
764 // +------+------------+------+===================+
765 //
766 // +------+------------+------+-----------+=======+
767 // |Other1| Other |Other2| New Image | Empty |
768 // +------+------------+------+-----------+=======+
769 //
770 Status = UpdateMicrocode((UINTN)MicrocodePatchAddress + UsedRegionSize, Image, ImageSize, LastAttemptStatus);
771 } else {
772 DEBUG((DEBUG_INFO, "Reorg and replace old microcode\n"));
773 //
774 // +------+------------+------+===================+
775 // |Other1| Old Image |Other2| Empty |
776 // +------+------------+------+===================+
777 //
778 // +------+---------------+------+================+
779 // |Other1| New Image |Other2| Empty |
780 // +------+---------------+------+================+
781 //
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;
792 }
793 Status = UpdateMicrocode((UINTN)CurrentMicrocodeEntryPoint, MicrocodePatchScratchBuffer, ScratchBufferSize, LastAttemptStatus);
794 }
795 return Status;
796 }
797
798 //
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.
801 //
802 if (MicrocodePatchRegionSize >= ImageSize) {
803 //
804 // +------+------------+------+===================+
805 // |Other1| Old Image |Other2| Empty |
806 // +------+------------+------+===================+
807 //
808 // +-------------------------------------+--------+
809 // | New Image | Other |
810 // +-------------------------------------+--------+
811 //
812 DEBUG((DEBUG_INFO, "Add new microcode from beginning\n"));
813
814 MicrocodeCount = MicrocodeFmpPrivate->DescriptorCount;
815 MicrocodeInfo = MicrocodeFmpPrivate->MicrocodeInfo;
816
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) {
824 continue;
825 }
826 if (MicrocodeInfo[Index].MicrocodeEntryPoint == CurrentMicrocodeEntryPoint) {
827 continue;
828 }
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;
833 }
834 }
835 // 3.3. Pad 0xFF
836 RestSize = MicrocodePatchRegionSize - ScratchBufferSize;
837 if (RestSize > 0) {
838 SetMem (ScratchBufferPtr, RestSize, 0xFF);
839 ScratchBufferSize += RestSize;
840 ScratchBufferPtr = (UINT8 *)ScratchBufferPtr + ScratchBufferSize;
841 }
842 Status = UpdateMicrocode((UINTN)MicrocodePatchAddress, MicrocodePatchScratchBuffer, ScratchBufferSize, LastAttemptStatus);
843 return Status;
844 }
845
846 //
847 // 4. The new image size is bigger than the whole MCU region.
848 //
849 DEBUG((DEBUG_ERROR, "Microcode too big\n"));
850 *LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_INSUFFICIENT_RESOURCES;
851 Status = EFI_OUT_OF_RESOURCES;
852
853 return Status;
854 }
855
856 /**
857 Write Microcode.
858
859 Caution: This function may receive untrusted input.
860
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
869 call to FreePool().
870
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.
876 **/
877 EFI_STATUS
878 MicrocodeWrite (
879 IN MICROCODE_FMP_PRIVATE_DATA *MicrocodeFmpPrivate,
880 IN VOID *Image,
881 IN UINTN ImageSize,
882 OUT UINT32 *LastAttemptVersion,
883 OUT UINT32 *LastAttemptStatus,
884 OUT CHAR16 **AbortReason
885 )
886 {
887 EFI_STATUS Status;
888 VOID *AlignedImage;
889 CPU_MICROCODE_HEADER *CurrentMicrocodeEntryPoint;
890
891 //
892 // We must get Current MicrocodeEntrypoint *before* VerifyMicrocode,
893 // because the MicrocodeSignature might be updated in VerifyMicrocode.
894 //
895 CurrentMicrocodeEntryPoint = GetCurrentMicrocodeInUse(MicrocodeFmpPrivate);
896 DEBUG((DEBUG_INFO, " CurrentMicrocodeEntryPoint - 0x%x\n", CurrentMicrocodeEntryPoint));
897
898 //
899 // MCU must be 16 bytes aligned
900 //
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;
906 }
907
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);
913 return Status;
914 }
915 DEBUG((DEBUG_INFO, "Pass VerifyMicrocode\n"));
916
917 Status = UpdateMicrocodeFlashRegion(
918 MicrocodeFmpPrivate,
919 CurrentMicrocodeEntryPoint,
920 AlignedImage,
921 ImageSize,
922 LastAttemptStatus
923 );
924
925 FreePool(AlignedImage);
926
927 return Status;
928 }
929
930