]> git.proxmox.com Git - mirror_edk2.git/blob - UefiCpuPkg/Feature/Capsule/MicrocodeUpdateDxe/MicrocodeUpdate.c
2eb4ae4fbaab816399cabd1e699329dac478bd1f
[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 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 UINT64 *MicrocodePatchAddress,
36 OUT UINT64 *MicrocodePatchRegionSize
37 )
38 {
39 *MicrocodePatchAddress = PcdGet64(PcdCpuMicrocodePatchAddress);
40 *MicrocodePatchRegionSize = PcdGet64(PcdCpuMicrocodePatchRegionSize);
41
42 if ((*MicrocodePatchAddress == 0) || (*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 Get current Microcode information.
118
119 @param[out] ImageDescriptor Microcode ImageDescriptor
120 @param[in] DescriptorCount The count of Microcode ImageDescriptor allocated.
121
122 @return Microcode count
123 **/
124 UINTN
125 GetMicrocodeInfo (
126 OUT EFI_FIRMWARE_IMAGE_DESCRIPTOR *ImageDescriptor, OPTIONAL
127 IN UINTN DescriptorCount OPTIONAL
128 )
129 {
130 BOOLEAN Result;
131 UINT64 MicrocodePatchAddress;
132 UINT64 MicrocodePatchRegionSize;
133 CPU_MICROCODE_HEADER *MicrocodeEntryPoint;
134 UINTN MicrocodeEnd;
135 UINTN TotalSize;
136 UINTN Count;
137 UINT64 ImageAttributes;
138 UINT32 CurrentRevision;
139
140 Result = GetMicrocodeRegion(&MicrocodePatchAddress, &MicrocodePatchRegionSize);
141 if (!Result) {
142 DEBUG((DEBUG_ERROR, "Fail to get Microcode Region\n"));
143 return 0;
144 }
145 DEBUG((DEBUG_INFO, "Microcode Region - 0x%lx - 0x%lx\n", MicrocodePatchAddress, MicrocodePatchRegionSize));
146
147 Count = 0;
148 CurrentRevision = GetCurrentMicrocodeSignature();
149
150 MicrocodeEnd = (UINTN)(MicrocodePatchAddress + MicrocodePatchRegionSize);
151 MicrocodeEntryPoint = (CPU_MICROCODE_HEADER *) (UINTN) MicrocodePatchAddress;
152 do {
153 if (MicrocodeEntryPoint->HeaderVersion == 0x1 && MicrocodeEntryPoint->LoaderRevision == 0x1) {
154 //
155 // It is the microcode header. It is not the padding data between microcode patches
156 // becasue the padding data should not include 0x00000001 and it should be the repeated
157 // byte format (like 0xXYXYXYXY....).
158 //
159 if (MicrocodeEntryPoint->DataSize == 0) {
160 TotalSize = 2048;
161 } else {
162 TotalSize = MicrocodeEntryPoint->TotalSize;
163 }
164
165 if (ImageDescriptor != NULL && DescriptorCount > Count) {
166 ImageDescriptor[Count].ImageIndex = (UINT8)(Count + 1);
167 CopyGuid (&ImageDescriptor[Count].ImageTypeId, &gMicrocodeFmpImageTypeIdGuid);
168 ImageDescriptor[Count].ImageId = LShiftU64(MicrocodeEntryPoint->ProcessorFlags, 32) + MicrocodeEntryPoint->ProcessorSignature.Uint32;
169 ImageDescriptor[Count].ImageIdName = NULL;
170 ImageDescriptor[Count].Version = MicrocodeEntryPoint->UpdateRevision;
171 ImageDescriptor[Count].VersionName = NULL;
172 ImageDescriptor[Count].Size = TotalSize;
173 ImageAttributes = IMAGE_ATTRIBUTE_IMAGE_UPDATABLE | IMAGE_ATTRIBUTE_RESET_REQUIRED;
174 if (CurrentRevision == MicrocodeEntryPoint->UpdateRevision) {
175 ImageAttributes |= IMAGE_ATTRIBUTE_IN_USE;
176 }
177 ImageDescriptor[Count].AttributesSupported = ImageAttributes | IMAGE_ATTRIBUTE_IN_USE;
178 ImageDescriptor[Count].AttributesSetting = ImageAttributes;
179 ImageDescriptor[Count].Compatibilities = 0;
180 ImageDescriptor[Count].LowestSupportedImageVersion = MicrocodeEntryPoint->UpdateRevision; // do not support rollback
181 ImageDescriptor[Count].LastAttemptVersion = 0;
182 ImageDescriptor[Count].LastAttemptStatus = 0;
183 ImageDescriptor[Count].HardwareInstance = 0;
184 }
185 } else {
186 //
187 // It is the padding data between the microcode patches for microcode patches alignment.
188 // Because the microcode patch is the multiple of 1-KByte, the padding data should not
189 // exist if the microcode patch alignment value is not larger than 1-KByte. So, the microcode
190 // alignment value should be larger than 1-KByte. We could skip SIZE_1KB padding data to
191 // find the next possible microcode patch header.
192 //
193 MicrocodeEntryPoint = (CPU_MICROCODE_HEADER *) (((UINTN) MicrocodeEntryPoint) + SIZE_1KB);
194 continue;
195 }
196
197 Count++;
198 ASSERT(Count < 0xFF);
199
200 //
201 // Get the next patch.
202 //
203 MicrocodeEntryPoint = (CPU_MICROCODE_HEADER *) (((UINTN) MicrocodeEntryPoint) + TotalSize);
204 } while (((UINTN) MicrocodeEntryPoint < MicrocodeEnd));
205
206 return Count;
207 }
208
209 /**
210 Read Microcode.
211
212 @param[in] ImageIndex The index of Microcode image.
213 @param[in, out] Image The Microcode image buffer.
214 @param[in, out] ImageSize The size of Microcode image buffer in bytes.
215
216 @retval EFI_SUCCESS The Microcode image is read.
217 @retval EFI_NOT_FOUND The Microcode image is not found.
218 **/
219 EFI_STATUS
220 MicrocodeRead (
221 IN UINTN ImageIndex,
222 IN OUT VOID *Image,
223 IN OUT UINTN *ImageSize
224 )
225 {
226 BOOLEAN Result;
227 UINT64 MicrocodePatchAddress;
228 UINT64 MicrocodePatchRegionSize;
229 CPU_MICROCODE_HEADER *MicrocodeEntryPoint;
230 UINTN MicrocodeEnd;
231 UINTN TotalSize;
232 UINTN Count;
233
234 Result = GetMicrocodeRegion(&MicrocodePatchAddress, &MicrocodePatchRegionSize);
235 if (!Result) {
236 DEBUG((DEBUG_ERROR, "Fail to get Microcode Region\n"));
237 return EFI_NOT_FOUND;
238 }
239 DEBUG((DEBUG_INFO, "Microcode Region - 0x%lx - 0x%lx\n", MicrocodePatchAddress, MicrocodePatchRegionSize));
240
241 Count = 0;
242
243 MicrocodeEnd = (UINTN)(MicrocodePatchAddress + MicrocodePatchRegionSize);
244 MicrocodeEntryPoint = (CPU_MICROCODE_HEADER *)(UINTN)MicrocodePatchAddress;
245 do {
246 if (MicrocodeEntryPoint->HeaderVersion == 0x1 && MicrocodeEntryPoint->LoaderRevision == 0x1) {
247 //
248 // It is the microcode header. It is not the padding data between microcode patches
249 // becasue the padding data should not include 0x00000001 and it should be the repeated
250 // byte format (like 0xXYXYXYXY....).
251 //
252 if (MicrocodeEntryPoint->DataSize == 0) {
253 TotalSize = 2048;
254 } else {
255 TotalSize = MicrocodeEntryPoint->TotalSize;
256 }
257
258 if (ImageIndex == Count + 1) {
259 if (*ImageSize < TotalSize) {
260 *ImageSize = TotalSize;
261 return EFI_BUFFER_TOO_SMALL;
262 }
263 *ImageSize = TotalSize;
264 CopyMem (Image, MicrocodeEntryPoint, TotalSize);
265 return EFI_SUCCESS;
266 }
267
268 } else {
269 //
270 // It is the padding data between the microcode patches for microcode patches alignment.
271 // Because the microcode patch is the multiple of 1-KByte, the padding data should not
272 // exist if the microcode patch alignment value is not larger than 1-KByte. So, the microcode
273 // alignment value should be larger than 1-KByte. We could skip SIZE_1KB padding data to
274 // find the next possible microcode patch header.
275 //
276 MicrocodeEntryPoint = (CPU_MICROCODE_HEADER *)(((UINTN)MicrocodeEntryPoint) + SIZE_1KB);
277 continue;
278 }
279
280 Count++;
281 ASSERT(Count < 0xFF);
282
283 //
284 // Get the next patch.
285 //
286 MicrocodeEntryPoint = (CPU_MICROCODE_HEADER *)(((UINTN)MicrocodeEntryPoint) + TotalSize);
287 } while (((UINTN)MicrocodeEntryPoint < MicrocodeEnd));
288
289 return EFI_NOT_FOUND;
290 }
291
292 /**
293 Verify Microcode.
294
295 Caution: This function may receive untrusted input.
296
297 @param[in] Image The Microcode image buffer.
298 @param[in] ImageSize The size of Microcode image buffer in bytes.
299 @param[in] TryLoad Try to load Microcode or not.
300 @param[out] LastAttemptStatus The last attempt status, which will be recorded in ESRT and FMP EFI_FIRMWARE_IMAGE_DESCRIPTOR.
301 @param[out] AbortReason A pointer to a pointer to a null-terminated string providing more
302 details for the aborted operation. The buffer is allocated by this function
303 with AllocatePool(), and it is the caller's responsibility to free it with a
304 call to FreePool().
305
306 @retval EFI_SUCCESS The Microcode image passes verification.
307 @retval EFI_VOLUME_CORRUPTED The Microcode image is corrupt.
308 @retval EFI_INCOMPATIBLE_VERSION The Microcode image version is incorrect.
309 @retval EFI_UNSUPPORTED The Microcode ProcessorSignature or ProcessorFlags is incorrect.
310 @retval EFI_SECURITY_VIOLATION The Microcode image fails to load.
311 **/
312 EFI_STATUS
313 VerifyMicrocode (
314 IN VOID *Image,
315 IN UINTN ImageSize,
316 IN BOOLEAN TryLoad,
317 OUT UINT32 *LastAttemptStatus,
318 OUT CHAR16 **AbortReason
319 )
320 {
321 UINTN Index;
322 CPU_MICROCODE_HEADER *MicrocodeEntryPoint;
323 UINTN TotalSize;
324 UINTN DataSize;
325 UINT32 CurrentRevision;
326 UINT32 CurrentProcessorSignature;
327 UINT8 CurrentPlatformId;
328 UINT32 CheckSum32;
329 UINTN ExtendedTableLength;
330 UINT32 ExtendedTableCount;
331 CPU_MICROCODE_EXTENDED_TABLE *ExtendedTable;
332 CPU_MICROCODE_EXTENDED_TABLE_HEADER *ExtendedTableHeader;
333 BOOLEAN CorrectMicrocode;
334
335 //
336 // Check HeaderVersion
337 //
338 MicrocodeEntryPoint = Image;
339 if (MicrocodeEntryPoint->HeaderVersion != 0x1) {
340 DEBUG((DEBUG_ERROR, "VerifyMicrocode - fail on HeaderVersion\n"));
341 *LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_INVALID_FORMAT;
342 if (AbortReason != NULL) {
343 *AbortReason = AllocateCopyPool(sizeof(L"InvalidHeaderVersion"), L"InvalidHeaderVersion");
344 }
345 return EFI_INCOMPATIBLE_VERSION;
346 }
347 //
348 // Check LoaderRevision
349 //
350 if (MicrocodeEntryPoint->LoaderRevision != 0x1) {
351 DEBUG((DEBUG_ERROR, "VerifyMicrocode - fail on LoaderRevision\n"));
352 *LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_INVALID_FORMAT;
353 if (AbortReason != NULL) {
354 *AbortReason = AllocateCopyPool(sizeof(L"InvalidLoaderVersion"), L"InvalidLoaderVersion");
355 }
356 return EFI_INCOMPATIBLE_VERSION;
357 }
358 //
359 // Check Size
360 //
361 if (MicrocodeEntryPoint->DataSize == 0) {
362 TotalSize = 2048;
363 } else {
364 TotalSize = MicrocodeEntryPoint->TotalSize;
365 }
366 if (TotalSize <= sizeof(CPU_MICROCODE_HEADER)) {
367 DEBUG((DEBUG_ERROR, "VerifyMicrocode - TotalSize too small\n"));
368 *LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_INVALID_FORMAT;
369 if (AbortReason != NULL) {
370 *AbortReason = AllocateCopyPool(sizeof(L"InvalidTotalSize"), L"InvalidTotalSize");
371 }
372 return EFI_VOLUME_CORRUPTED;
373 }
374 if (TotalSize != ImageSize) {
375 DEBUG((DEBUG_ERROR, "VerifyMicrocode - fail on TotalSize\n"));
376 *LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_INVALID_FORMAT;
377 if (AbortReason != NULL) {
378 *AbortReason = AllocateCopyPool(sizeof(L"InvalidTotalSize"), L"InvalidTotalSize");
379 }
380 return EFI_VOLUME_CORRUPTED;
381 }
382 //
383 // Check CheckSum32
384 //
385 if (MicrocodeEntryPoint->DataSize == 0) {
386 DataSize = 2048 - sizeof(CPU_MICROCODE_HEADER);
387 } else {
388 DataSize = MicrocodeEntryPoint->DataSize;
389 }
390 if (DataSize > TotalSize - sizeof(CPU_MICROCODE_HEADER)) {
391 DEBUG((DEBUG_ERROR, "VerifyMicrocode - DataSize too big\n"));
392 *LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_INVALID_FORMAT;
393 if (AbortReason != NULL) {
394 *AbortReason = AllocateCopyPool(sizeof(L"InvalidDataSize"), L"InvalidDataSize");
395 }
396 return EFI_VOLUME_CORRUPTED;
397 }
398 if ((DataSize & 0x3) != 0) {
399 DEBUG((DEBUG_ERROR, "VerifyMicrocode - DataSize not aligned\n"));
400 *LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_INVALID_FORMAT;
401 if (AbortReason != NULL) {
402 *AbortReason = AllocateCopyPool(sizeof(L"InvalidDataSize"), L"InvalidDataSize");
403 }
404 return EFI_VOLUME_CORRUPTED;
405 }
406 CheckSum32 = CalculateSum32((UINT32 *)MicrocodeEntryPoint, DataSize + sizeof(CPU_MICROCODE_HEADER));
407 if (CheckSum32 != 0) {
408 DEBUG((DEBUG_ERROR, "VerifyMicrocode - fail on CheckSum32\n"));
409 *LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_INVALID_FORMAT;
410 if (AbortReason != NULL) {
411 *AbortReason = AllocateCopyPool(sizeof(L"InvalidChecksum"), L"InvalidChecksum");
412 }
413 return EFI_VOLUME_CORRUPTED;
414 }
415
416 //
417 // Check ProcessorSignature/ProcessorFlags
418 //
419 CorrectMicrocode = FALSE;
420 CurrentProcessorSignature = GetCurrentProcessorSignature();
421 CurrentPlatformId = GetCurrentPlatformId();
422 if ((MicrocodeEntryPoint->ProcessorSignature.Uint32 != CurrentProcessorSignature) ||
423 ((MicrocodeEntryPoint->ProcessorFlags & (1 << CurrentPlatformId)) == 0)) {
424 ExtendedTableLength = TotalSize - (DataSize + sizeof(CPU_MICROCODE_HEADER));
425 if (ExtendedTableLength != 0) {
426 //
427 // Extended Table exist, check if the CPU in support list
428 //
429 ExtendedTableHeader = (CPU_MICROCODE_EXTENDED_TABLE_HEADER *)((UINT8 *)(MicrocodeEntryPoint) + DataSize + sizeof(CPU_MICROCODE_HEADER));
430 //
431 // Calculate Extended Checksum
432 //
433 if ((ExtendedTableLength > sizeof(CPU_MICROCODE_EXTENDED_TABLE_HEADER)) && ((ExtendedTableLength & 0x3) != 0)) {
434 CheckSum32 = CalculateSum32((UINT32 *)ExtendedTableHeader, ExtendedTableLength);
435 if (CheckSum32 == 0) {
436 //
437 // Checksum correct
438 //
439 ExtendedTableCount = ExtendedTableHeader->ExtendedSignatureCount;
440 if (ExtendedTableCount <= (ExtendedTableLength - sizeof(CPU_MICROCODE_EXTENDED_TABLE_HEADER)) / sizeof(CPU_MICROCODE_EXTENDED_TABLE)) {
441 ExtendedTable = (CPU_MICROCODE_EXTENDED_TABLE *)(ExtendedTableHeader + 1);
442 for (Index = 0; Index < ExtendedTableCount; Index++) {
443 CheckSum32 = CalculateSum32((UINT32 *)ExtendedTable, sizeof(CPU_MICROCODE_EXTENDED_TABLE));
444 if (CheckSum32 == 0) {
445 //
446 // Verify Header
447 //
448 if ((ExtendedTable->ProcessorSignature.Uint32 == CurrentProcessorSignature) &&
449 (ExtendedTable->ProcessorFlag & (1 << CurrentPlatformId))) {
450 //
451 // Find one
452 //
453 CorrectMicrocode = TRUE;
454 break;
455 }
456 }
457 ExtendedTable++;
458 }
459 }
460 }
461 }
462 }
463 if (!CorrectMicrocode) {
464 DEBUG((DEBUG_ERROR, "VerifyMicrocode - fail on CurrentProcessorSignature/ProcessorFlags\n"));
465 *LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_INCORRECT_VERSION;
466 if (AbortReason != NULL) {
467 if (MicrocodeEntryPoint->ProcessorSignature.Uint32 != CurrentProcessorSignature) {
468 *AbortReason = AllocateCopyPool(sizeof(L"UnsupportedProcessSignature"), L"UnsupportedProcessSignature");
469 } else {
470 *AbortReason = AllocateCopyPool(sizeof(L"UnsupportedProcessorFlags"), L"UnsupportedProcessorFlags");
471 }
472 }
473 return EFI_UNSUPPORTED;
474 }
475 }
476
477 //
478 // Check UpdateRevision
479 //
480 CurrentRevision = GetCurrentMicrocodeSignature();
481 if (MicrocodeEntryPoint->UpdateRevision < CurrentRevision) {
482 DEBUG((DEBUG_ERROR, "VerifyMicrocode - fail on UpdateRevision\n"));
483 *LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_INCORRECT_VERSION;
484 if (AbortReason != NULL) {
485 *AbortReason = AllocateCopyPool(sizeof(L"IncorrectRevision"), L"IncorrectRevision");
486 }
487 return EFI_INCOMPATIBLE_VERSION;
488 }
489
490 //
491 // try load MCU
492 //
493 if (TryLoad) {
494 CurrentRevision = LoadMicrocode((UINTN)MicrocodeEntryPoint + sizeof(CPU_MICROCODE_HEADER));
495 if (MicrocodeEntryPoint->UpdateRevision != CurrentRevision) {
496 DEBUG((DEBUG_ERROR, "VerifyMicrocode - fail on LoadMicrocode\n"));
497 *LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_AUTH_ERROR;
498 if (AbortReason != NULL) {
499 *AbortReason = AllocateCopyPool(sizeof(L"InvalidData"), L"InvalidData");
500 }
501 return EFI_SECURITY_VIOLATION;
502 }
503 }
504
505 return EFI_SUCCESS;
506 }
507
508 /**
509 Get current Microcode in used.
510
511 @return current Microcode in used.
512 **/
513 VOID *
514 GetCurrentMicrocodeInUse (
515 VOID
516 )
517 {
518 BOOLEAN Result;
519 EFI_STATUS Status;
520 UINT64 MicrocodePatchAddress;
521 UINT64 MicrocodePatchRegionSize;
522 CPU_MICROCODE_HEADER *MicrocodeEntryPoint;
523 UINTN MicrocodeEnd;
524 UINTN TotalSize;
525 UINTN Count;
526 UINT32 AttemptStatus;
527
528 Result = GetMicrocodeRegion(&MicrocodePatchAddress, &MicrocodePatchRegionSize);
529 if (!Result) {
530 DEBUG((DEBUG_ERROR, "Fail to get Microcode Region\n"));
531 return NULL;
532 }
533 DEBUG((DEBUG_INFO, "Microcode Region - 0x%lx - 0x%lx\n", MicrocodePatchAddress, MicrocodePatchRegionSize));
534
535 Count = 0;
536
537 MicrocodeEnd = (UINTN)(MicrocodePatchAddress + MicrocodePatchRegionSize);
538 MicrocodeEntryPoint = (CPU_MICROCODE_HEADER *)(UINTN)MicrocodePatchAddress;
539 do {
540 if (MicrocodeEntryPoint->HeaderVersion == 0x1 && MicrocodeEntryPoint->LoaderRevision == 0x1) {
541 //
542 // It is the microcode header. It is not the padding data between microcode patches
543 // becasue the padding data should not include 0x00000001 and it should be the repeated
544 // byte format (like 0xXYXYXYXY....).
545 //
546 if (MicrocodeEntryPoint->DataSize == 0) {
547 TotalSize = 2048;
548 } else {
549 TotalSize = MicrocodeEntryPoint->TotalSize;
550 }
551 Status = VerifyMicrocode(MicrocodeEntryPoint, TotalSize, FALSE, &AttemptStatus, NULL);
552 if (!EFI_ERROR(Status)) {
553 return MicrocodeEntryPoint;
554 }
555
556 } else {
557 //
558 // It is the padding data between the microcode patches for microcode patches alignment.
559 // Because the microcode patch is the multiple of 1-KByte, the padding data should not
560 // exist if the microcode patch alignment value is not larger than 1-KByte. So, the microcode
561 // alignment value should be larger than 1-KByte. We could skip SIZE_1KB padding data to
562 // find the next possible microcode patch header.
563 //
564 MicrocodeEntryPoint = (CPU_MICROCODE_HEADER *)(((UINTN)MicrocodeEntryPoint) + SIZE_1KB);
565 continue;
566 }
567
568 Count++;
569 ASSERT(Count < 0xFF);
570
571 //
572 // Get the next patch.
573 //
574 MicrocodeEntryPoint = (CPU_MICROCODE_HEADER *)(((UINTN)MicrocodeEntryPoint) + TotalSize);
575 } while (((UINTN)MicrocodeEntryPoint < MicrocodeEnd));
576
577 return NULL;
578 }
579
580 /**
581 Get current Microcode used region size.
582
583 @return current Microcode used region size.
584 **/
585 UINTN
586 GetCurrentMicrocodeUsedRegionSize (
587 VOID
588 )
589 {
590 BOOLEAN Result;
591 UINT64 MicrocodePatchAddress;
592 UINT64 MicrocodePatchRegionSize;
593 CPU_MICROCODE_HEADER *MicrocodeEntryPoint;
594 UINTN MicrocodeEnd;
595 UINTN TotalSize;
596 UINTN Count;
597 UINTN MicrocodeUsedEnd;
598
599 Result = GetMicrocodeRegion(&MicrocodePatchAddress, &MicrocodePatchRegionSize);
600 if (!Result) {
601 DEBUG((DEBUG_ERROR, "Fail to get Microcode Region\n"));
602 return 0;
603 }
604 DEBUG((DEBUG_INFO, "Microcode Region - 0x%lx - 0x%lx\n", MicrocodePatchAddress, MicrocodePatchRegionSize));
605
606 MicrocodeUsedEnd = (UINTN)MicrocodePatchAddress;
607 Count = 0;
608
609 MicrocodeEnd = (UINTN)(MicrocodePatchAddress + MicrocodePatchRegionSize);
610 MicrocodeEntryPoint = (CPU_MICROCODE_HEADER *)(UINTN)MicrocodePatchAddress;
611 do {
612 if (MicrocodeEntryPoint->HeaderVersion == 0x1 && MicrocodeEntryPoint->LoaderRevision == 0x1) {
613 //
614 // It is the microcode header. It is not the padding data between microcode patches
615 // becasue the padding data should not include 0x00000001 and it should be the repeated
616 // byte format (like 0xXYXYXYXY....).
617 //
618 if (MicrocodeEntryPoint->DataSize == 0) {
619 TotalSize = 2048;
620 } else {
621 TotalSize = MicrocodeEntryPoint->TotalSize;
622 }
623
624 } else {
625 //
626 // It is the padding data between the microcode patches for microcode patches alignment.
627 // Because the microcode patch is the multiple of 1-KByte, the padding data should not
628 // exist if the microcode patch alignment value is not larger than 1-KByte. So, the microcode
629 // alignment value should be larger than 1-KByte. We could skip SIZE_1KB padding data to
630 // find the next possible microcode patch header.
631 //
632 MicrocodeEntryPoint = (CPU_MICROCODE_HEADER *)(((UINTN)MicrocodeEntryPoint) + SIZE_1KB);
633 continue;
634 }
635
636 Count++;
637 ASSERT(Count < 0xFF);
638 MicrocodeUsedEnd = (UINTN)MicrocodeEntryPoint;
639
640 //
641 // Get the next patch.
642 //
643 MicrocodeEntryPoint = (CPU_MICROCODE_HEADER *)(((UINTN)MicrocodeEntryPoint) + TotalSize);
644 } while (((UINTN)MicrocodeEntryPoint < MicrocodeEnd));
645
646 return MicrocodeUsedEnd - (UINTN)MicrocodePatchAddress;
647 }
648
649 /**
650 Update Microcode.
651
652 @param[in] Address The flash address of Microcode.
653 @param[in] Image The Microcode image buffer.
654 @param[in] ImageSize The size of Microcode image buffer in bytes.
655 @param[out] LastAttemptStatus The last attempt status, which will be recorded in ESRT and FMP EFI_FIRMWARE_IMAGE_DESCRIPTOR.
656
657 @retval EFI_SUCCESS The Microcode image is updated.
658 @retval EFI_WRITE_PROTECTED The flash device is read only.
659 **/
660 EFI_STATUS
661 UpdateMicrocode (
662 IN UINT64 Address,
663 IN VOID *Image,
664 IN UINTN ImageSize,
665 OUT UINT32 *LastAttemptStatus
666 )
667 {
668 EFI_STATUS Status;
669
670 DEBUG((DEBUG_INFO, "PlatformUpdate:"));
671 DEBUG((DEBUG_INFO, " Address - 0x%lx,", Address));
672 DEBUG((DEBUG_INFO, " Legnth - 0x%x\n", ImageSize));
673
674 Status = MicrocodeFlashWrite (
675 Address,
676 Image,
677 ImageSize
678 );
679 if (!EFI_ERROR(Status)) {
680 *LastAttemptStatus = LAST_ATTEMPT_STATUS_SUCCESS;
681 } else {
682 *LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_UNSUCCESSFUL;
683 }
684 return Status;
685 }
686
687 /**
688 Write Microcode.
689
690 Caution: This function may receive untrusted input.
691
692 @param[in] ImageIndex The index of Microcode image.
693 @param[in] Image The Microcode image buffer.
694 @param[in] ImageSize The size of Microcode image buffer in bytes.
695 @param[out] LastAttemptVersion The last attempt version, which will be recorded in ESRT and FMP EFI_FIRMWARE_IMAGE_DESCRIPTOR.
696 @param[out] LastAttemptStatus The last attempt status, which will be recorded in ESRT and FMP EFI_FIRMWARE_IMAGE_DESCRIPTOR.
697 @param[out] AbortReason A pointer to a pointer to a null-terminated string providing more
698 details for the aborted operation. The buffer is allocated by this function
699 with AllocatePool(), and it is the caller's responsibility to free it with a
700 call to FreePool().
701
702 @retval EFI_SUCCESS The Microcode image is written.
703 @retval EFI_VOLUME_CORRUPTED The Microcode image is corrupt.
704 @retval EFI_INCOMPATIBLE_VERSION The Microcode image version is incorrect.
705 @retval EFI_SECURITY_VIOLATION The Microcode image fails to load.
706 @retval EFI_WRITE_PROTECTED The flash device is read only.
707 **/
708 EFI_STATUS
709 MicrocodeWrite (
710 IN UINTN ImageIndex,
711 IN VOID *Image,
712 IN UINTN ImageSize,
713 OUT UINT32 *LastAttemptVersion,
714 OUT UINT32 *LastAttemptStatus,
715 OUT CHAR16 **AbortReason
716 )
717 {
718 BOOLEAN Result;
719 EFI_STATUS Status;
720 UINT64 MicrocodePatchAddress;
721 UINT64 MicrocodePatchRegionSize;
722 CPU_MICROCODE_HEADER *CurrentMicrocodeEntryPoint;
723 UINTN CurrentTotalSize;
724 UINTN UsedRegionSize;
725 VOID *AlignedImage;
726
727 Result = GetMicrocodeRegion(&MicrocodePatchAddress, &MicrocodePatchRegionSize);
728 if (!Result) {
729 DEBUG((DEBUG_ERROR, "Fail to get Microcode Region\n"));
730 return EFI_NOT_FOUND;
731 }
732
733 CurrentTotalSize = 0;
734 CurrentMicrocodeEntryPoint = GetCurrentMicrocodeInUse();
735 if (CurrentMicrocodeEntryPoint != NULL) {
736 if (CurrentMicrocodeEntryPoint->DataSize == 0) {
737 CurrentTotalSize = 2048;
738 } else {
739 CurrentTotalSize = CurrentMicrocodeEntryPoint->TotalSize;
740 }
741 }
742
743 //
744 // MCU must be 16 bytes aligned
745 //
746 AlignedImage = AllocateCopyPool(ImageSize, Image);
747 if (AlignedImage == NULL) {
748 DEBUG((DEBUG_ERROR, "Fail to allocate aligned image\n"));
749 *LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_INSUFFICIENT_RESOURCES;
750 return EFI_OUT_OF_RESOURCES;
751 }
752
753 *LastAttemptVersion = ((CPU_MICROCODE_HEADER *)Image)->UpdateRevision;
754 Status = VerifyMicrocode(AlignedImage, ImageSize, TRUE, LastAttemptStatus, AbortReason);
755 if (EFI_ERROR(Status)) {
756 DEBUG((DEBUG_ERROR, "Fail to verify Microcode Region\n"));
757 FreePool(AlignedImage);
758 return Status;
759 }
760 DEBUG((DEBUG_INFO, "Pass VerifyMicrocode\n"));
761
762 if (CurrentTotalSize < ImageSize) {
763 UsedRegionSize = GetCurrentMicrocodeUsedRegionSize();
764 if (MicrocodePatchRegionSize - UsedRegionSize >= ImageSize) {
765 //
766 // Append
767 //
768 DEBUG((DEBUG_INFO, "Append new microcode\n"));
769 Status = UpdateMicrocode(MicrocodePatchAddress + UsedRegionSize, AlignedImage, ImageSize, LastAttemptStatus);
770 } else if (MicrocodePatchRegionSize >= ImageSize) {
771 //
772 // Ignor all others and just add this one from beginning.
773 //
774 DEBUG((DEBUG_INFO, "Add new microcode from beginning\n"));
775 Status = UpdateMicrocode(MicrocodePatchAddress, AlignedImage, ImageSize, LastAttemptStatus);
776 } else {
777 DEBUG((DEBUG_ERROR, "Microcode too big\n"));
778 *LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_INSUFFICIENT_RESOURCES;
779 Status = EFI_OUT_OF_RESOURCES;
780 }
781 } else {
782 //
783 // Replace
784 //
785 DEBUG((DEBUG_INFO, "Replace old microcode\n"));
786 Status = UpdateMicrocode((UINTN)CurrentMicrocodeEntryPoint, AlignedImage, ImageSize, LastAttemptStatus);
787 }
788
789 FreePool(AlignedImage);
790
791 return Status;
792 }
793
794