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