]> git.proxmox.com Git - mirror_edk2.git/blob - IntelSiliconPkg/Feature/Capsule/MicrocodeUpdateDxe/MicrocodeUpdate.c
IntelSiliconPkg: Replace BSD License with BSD+Patent License
[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 SPDX-License-Identifier: BSD-2-Clause-Patent
13
14 **/
15
16 #include "MicrocodeUpdate.h"
17
18 /**
19 Get Microcode Region.
20
21 @param[out] MicrocodePatchAddress The address of Microcode
22 @param[out] MicrocodePatchRegionSize The region size of Microcode
23
24 @retval TRUE The Microcode region is returned.
25 @retval FALSE No Microcode region.
26 **/
27 BOOLEAN
28 GetMicrocodeRegion (
29 OUT VOID **MicrocodePatchAddress,
30 OUT UINTN *MicrocodePatchRegionSize
31 )
32 {
33 *MicrocodePatchAddress = (VOID *)(UINTN)PcdGet64(PcdCpuMicrocodePatchAddress);
34 *MicrocodePatchRegionSize = (UINTN)PcdGet64(PcdCpuMicrocodePatchRegionSize);
35
36 if ((*MicrocodePatchAddress == NULL) || (*MicrocodePatchRegionSize == 0)) {
37 return FALSE;
38 }
39
40 return TRUE;
41 }
42
43 /**
44 Get Microcode update signature of currently loaded Microcode update.
45
46 @return Microcode signature.
47
48 **/
49 UINT32
50 GetCurrentMicrocodeSignature (
51 VOID
52 )
53 {
54 UINT64 Signature;
55
56 AsmWriteMsr64(MSR_IA32_BIOS_SIGN_ID, 0);
57 AsmCpuid(CPUID_VERSION_INFO, NULL, NULL, NULL, NULL);
58 Signature = AsmReadMsr64(MSR_IA32_BIOS_SIGN_ID);
59 return (UINT32)RShiftU64(Signature, 32);
60 }
61
62 /**
63 Get current processor signature.
64
65 @return current processor signature.
66 **/
67 UINT32
68 GetCurrentProcessorSignature (
69 VOID
70 )
71 {
72 UINT32 RegEax;
73 AsmCpuid(CPUID_VERSION_INFO, &RegEax, NULL, NULL, NULL);
74 return RegEax;
75 }
76
77 /**
78 Get current platform ID.
79
80 @return current platform ID.
81 **/
82 UINT8
83 GetCurrentPlatformId (
84 VOID
85 )
86 {
87 UINT8 PlatformId;
88
89 PlatformId = (UINT8)AsmMsrBitFieldRead64(MSR_IA32_PLATFORM_ID, 50, 52);
90 return PlatformId;
91 }
92
93 /**
94 Load new Microcode.
95
96 @param[in] Address The address of new Microcode.
97
98 @return Loaded Microcode signature.
99
100 **/
101 UINT32
102 LoadMicrocode (
103 IN UINT64 Address
104 )
105 {
106 AsmWriteMsr64(MSR_IA32_BIOS_UPDT_TRIG, Address);
107 return GetCurrentMicrocodeSignature();
108 }
109
110 /**
111 Load Microcode on an Application Processor.
112 The function prototype for invoking a function on an Application Processor.
113
114 @param[in,out] Buffer The pointer to private data buffer.
115 **/
116 VOID
117 EFIAPI
118 MicrocodeLoadAp (
119 IN OUT VOID *Buffer
120 )
121 {
122 MICROCODE_LOAD_BUFFER *MicrocodeLoadBuffer;
123
124 MicrocodeLoadBuffer = Buffer;
125 MicrocodeLoadBuffer->Revision = LoadMicrocode (MicrocodeLoadBuffer->Address);
126 }
127
128 /**
129 Load new Microcode on this processor
130
131 @param[in] MicrocodeFmpPrivate The Microcode driver private data
132 @param[in] CpuIndex The index of the processor.
133 @param[in] Address The address of new Microcode.
134
135 @return Loaded Microcode signature.
136
137 **/
138 UINT32
139 LoadMicrocodeOnThis (
140 IN MICROCODE_FMP_PRIVATE_DATA *MicrocodeFmpPrivate,
141 IN UINTN CpuIndex,
142 IN UINT64 Address
143 )
144 {
145 EFI_STATUS Status;
146 EFI_MP_SERVICES_PROTOCOL *MpService;
147 MICROCODE_LOAD_BUFFER MicrocodeLoadBuffer;
148
149 if (CpuIndex == MicrocodeFmpPrivate->BspIndex) {
150 return LoadMicrocode (Address);
151 } else {
152 MpService = MicrocodeFmpPrivate->MpService;
153 MicrocodeLoadBuffer.Address = Address;
154 MicrocodeLoadBuffer.Revision = 0;
155 Status = MpService->StartupThisAP (
156 MpService,
157 MicrocodeLoadAp,
158 CpuIndex,
159 NULL,
160 0,
161 &MicrocodeLoadBuffer,
162 NULL
163 );
164 ASSERT_EFI_ERROR(Status);
165 return MicrocodeLoadBuffer.Revision;
166 }
167 }
168
169 /**
170 Collect processor information.
171 The function prototype for invoking a function on an Application Processor.
172
173 @param[in,out] Buffer The pointer to private data buffer.
174 **/
175 VOID
176 EFIAPI
177 CollectProcessorInfo (
178 IN OUT VOID *Buffer
179 )
180 {
181 PROCESSOR_INFO *ProcessorInfo;
182
183 ProcessorInfo = Buffer;
184 ProcessorInfo->ProcessorSignature = GetCurrentProcessorSignature();
185 ProcessorInfo->PlatformId = GetCurrentPlatformId();
186 ProcessorInfo->MicrocodeRevision = GetCurrentMicrocodeSignature();
187 }
188
189 /**
190 Get current Microcode information.
191
192 The ProcessorInformation (BspIndex/ProcessorCount/ProcessorInfo)
193 in MicrocodeFmpPrivate must be initialized.
194
195 The MicrocodeInformation (DescriptorCount/ImageDescriptor/MicrocodeInfo)
196 in MicrocodeFmpPrivate may not be avaiable in this function.
197
198 @param[in] MicrocodeFmpPrivate The Microcode driver private data
199 @param[in] DescriptorCount The count of Microcode ImageDescriptor allocated.
200 @param[out] ImageDescriptor Microcode ImageDescriptor
201 @param[out] MicrocodeInfo Microcode information
202
203 @return Microcode count
204 **/
205 UINTN
206 GetMicrocodeInfo (
207 IN MICROCODE_FMP_PRIVATE_DATA *MicrocodeFmpPrivate,
208 IN UINTN DescriptorCount, OPTIONAL
209 OUT EFI_FIRMWARE_IMAGE_DESCRIPTOR *ImageDescriptor, OPTIONAL
210 OUT MICROCODE_INFO *MicrocodeInfo OPTIONAL
211 )
212 {
213 VOID *MicrocodePatchAddress;
214 UINTN MicrocodePatchRegionSize;
215 CPU_MICROCODE_HEADER *MicrocodeEntryPoint;
216 UINTN MicrocodeEnd;
217 UINTN TotalSize;
218 UINTN Count;
219 UINT64 ImageAttributes;
220 BOOLEAN IsInUse;
221 EFI_STATUS Status;
222 UINT32 AttemptStatus;
223 UINTN TargetCpuIndex;
224
225 MicrocodePatchAddress = MicrocodeFmpPrivate->MicrocodePatchAddress;
226 MicrocodePatchRegionSize = MicrocodeFmpPrivate->MicrocodePatchRegionSize;
227
228 DEBUG((DEBUG_INFO, "Microcode Region - 0x%x - 0x%x\n", MicrocodePatchAddress, MicrocodePatchRegionSize));
229
230 Count = 0;
231
232 MicrocodeEnd = (UINTN)MicrocodePatchAddress + MicrocodePatchRegionSize;
233 MicrocodeEntryPoint = (CPU_MICROCODE_HEADER *) (UINTN) MicrocodePatchAddress;
234 do {
235 if (MicrocodeEntryPoint->HeaderVersion == 0x1 && MicrocodeEntryPoint->LoaderRevision == 0x1) {
236 //
237 // It is the microcode header. It is not the padding data between microcode patches
238 // becasue the padding data should not include 0x00000001 and it should be the repeated
239 // byte format (like 0xXYXYXYXY....).
240 //
241 if (MicrocodeEntryPoint->DataSize == 0) {
242 TotalSize = 2048;
243 } else {
244 TotalSize = MicrocodeEntryPoint->TotalSize;
245 }
246
247 TargetCpuIndex = (UINTN)-1;
248 Status = VerifyMicrocode(MicrocodeFmpPrivate, MicrocodeEntryPoint, TotalSize, FALSE, &AttemptStatus, NULL, &TargetCpuIndex);
249 if (!EFI_ERROR(Status)) {
250 IsInUse = TRUE;
251 ASSERT (TargetCpuIndex < MicrocodeFmpPrivate->ProcessorCount);
252 MicrocodeFmpPrivate->ProcessorInfo[TargetCpuIndex].MicrocodeIndex = Count;
253 } else {
254 IsInUse = FALSE;
255 }
256
257 if (ImageDescriptor != NULL && DescriptorCount > Count) {
258 ImageDescriptor[Count].ImageIndex = (UINT8)(Count + 1);
259 CopyGuid (&ImageDescriptor[Count].ImageTypeId, &gMicrocodeFmpImageTypeIdGuid);
260 ImageDescriptor[Count].ImageId = LShiftU64(MicrocodeEntryPoint->ProcessorFlags, 32) + MicrocodeEntryPoint->ProcessorSignature.Uint32;
261 ImageDescriptor[Count].ImageIdName = NULL;
262 ImageDescriptor[Count].Version = MicrocodeEntryPoint->UpdateRevision;
263 ImageDescriptor[Count].VersionName = NULL;
264 ImageDescriptor[Count].Size = TotalSize;
265 ImageAttributes = IMAGE_ATTRIBUTE_IMAGE_UPDATABLE | IMAGE_ATTRIBUTE_RESET_REQUIRED;
266 if (IsInUse) {
267 ImageAttributes |= IMAGE_ATTRIBUTE_IN_USE;
268 }
269 ImageDescriptor[Count].AttributesSupported = ImageAttributes | IMAGE_ATTRIBUTE_IN_USE;
270 ImageDescriptor[Count].AttributesSetting = ImageAttributes;
271 ImageDescriptor[Count].Compatibilities = 0;
272 ImageDescriptor[Count].LowestSupportedImageVersion = MicrocodeEntryPoint->UpdateRevision; // do not support rollback
273 ImageDescriptor[Count].LastAttemptVersion = 0;
274 ImageDescriptor[Count].LastAttemptStatus = 0;
275 ImageDescriptor[Count].HardwareInstance = 0;
276 }
277 if (MicrocodeInfo != NULL && DescriptorCount > Count) {
278 MicrocodeInfo[Count].MicrocodeEntryPoint = MicrocodeEntryPoint;
279 MicrocodeInfo[Count].TotalSize = TotalSize;
280 MicrocodeInfo[Count].InUse = IsInUse;
281 }
282 } else {
283 //
284 // It is the padding data between the microcode patches for microcode patches alignment.
285 // Because the microcode patch is the multiple of 1-KByte, the padding data should not
286 // exist if the microcode patch alignment value is not larger than 1-KByte. So, the microcode
287 // alignment value should be larger than 1-KByte. We could skip SIZE_1KB padding data to
288 // find the next possible microcode patch header.
289 //
290 MicrocodeEntryPoint = (CPU_MICROCODE_HEADER *) (((UINTN) MicrocodeEntryPoint) + SIZE_1KB);
291 continue;
292 }
293
294 Count++;
295 ASSERT(Count < 0xFF);
296
297 //
298 // Get the next patch.
299 //
300 MicrocodeEntryPoint = (CPU_MICROCODE_HEADER *) (((UINTN) MicrocodeEntryPoint) + TotalSize);
301 } while (((UINTN) MicrocodeEntryPoint < MicrocodeEnd));
302
303 return Count;
304 }
305
306 /**
307 Return matched processor information.
308
309 @param[in] MicrocodeFmpPrivate The Microcode driver private data
310 @param[in] ProcessorSignature The processor signature to be matched
311 @param[in] ProcessorFlags The processor flags to be matched
312 @param[in, out] TargetCpuIndex On input, the index of target CPU which tries to match the Microcode. (UINTN)-1 means to try all.
313 On output, the index of target CPU which matches the Microcode.
314
315 @return matched processor information.
316 **/
317 PROCESSOR_INFO *
318 GetMatchedProcessor (
319 IN MICROCODE_FMP_PRIVATE_DATA *MicrocodeFmpPrivate,
320 IN UINT32 ProcessorSignature,
321 IN UINT32 ProcessorFlags,
322 IN OUT UINTN *TargetCpuIndex
323 )
324 {
325 UINTN Index;
326
327 if (*TargetCpuIndex != (UINTN)-1) {
328 Index = *TargetCpuIndex;
329 if ((ProcessorSignature == MicrocodeFmpPrivate->ProcessorInfo[Index].ProcessorSignature) &&
330 ((ProcessorFlags & (1 << MicrocodeFmpPrivate->ProcessorInfo[Index].PlatformId)) != 0)) {
331 return &MicrocodeFmpPrivate->ProcessorInfo[Index];
332 } else {
333 return NULL;
334 }
335 }
336
337 for (Index = 0; Index < MicrocodeFmpPrivate->ProcessorCount; Index++) {
338 if ((ProcessorSignature == MicrocodeFmpPrivate->ProcessorInfo[Index].ProcessorSignature) &&
339 ((ProcessorFlags & (1 << MicrocodeFmpPrivate->ProcessorInfo[Index].PlatformId)) != 0)) {
340 *TargetCpuIndex = Index;
341 return &MicrocodeFmpPrivate->ProcessorInfo[Index];
342 }
343 }
344 return NULL;
345 }
346
347 /**
348 Verify Microcode.
349
350 Caution: This function may receive untrusted input.
351
352 @param[in] MicrocodeFmpPrivate The Microcode driver private data
353 @param[in] Image The Microcode image buffer.
354 @param[in] ImageSize The size of Microcode image buffer in bytes.
355 @param[in] TryLoad Try to load Microcode or not.
356 @param[out] LastAttemptStatus The last attempt status, which will be recorded in ESRT and FMP EFI_FIRMWARE_IMAGE_DESCRIPTOR.
357 @param[out] AbortReason A pointer to a pointer to a null-terminated string providing more
358 details for the aborted operation. The buffer is allocated by this function
359 with AllocatePool(), and it is the caller's responsibility to free it with a
360 call to FreePool().
361 @param[in, out] TargetCpuIndex On input, the index of target CPU which tries to match the Microcode. (UINTN)-1 means to try all.
362 On output, the index of target CPU which matches the Microcode.
363
364 @retval EFI_SUCCESS The Microcode image passes verification.
365 @retval EFI_VOLUME_CORRUPTED The Microcode image is corrupted.
366 @retval EFI_INCOMPATIBLE_VERSION The Microcode image version is incorrect.
367 @retval EFI_UNSUPPORTED The Microcode ProcessorSignature or ProcessorFlags is incorrect.
368 @retval EFI_SECURITY_VIOLATION The Microcode image fails to load.
369 **/
370 EFI_STATUS
371 VerifyMicrocode (
372 IN MICROCODE_FMP_PRIVATE_DATA *MicrocodeFmpPrivate,
373 IN VOID *Image,
374 IN UINTN ImageSize,
375 IN BOOLEAN TryLoad,
376 OUT UINT32 *LastAttemptStatus,
377 OUT CHAR16 **AbortReason, OPTIONAL
378 IN OUT UINTN *TargetCpuIndex
379 )
380 {
381 UINTN Index;
382 CPU_MICROCODE_HEADER *MicrocodeEntryPoint;
383 UINTN TotalSize;
384 UINTN DataSize;
385 UINT32 CurrentRevision;
386 PROCESSOR_INFO *ProcessorInfo;
387 UINT32 InCompleteCheckSum32;
388 UINT32 CheckSum32;
389 UINTN ExtendedTableLength;
390 UINT32 ExtendedTableCount;
391 CPU_MICROCODE_EXTENDED_TABLE *ExtendedTable;
392 CPU_MICROCODE_EXTENDED_TABLE_HEADER *ExtendedTableHeader;
393 BOOLEAN CorrectMicrocode;
394
395 //
396 // Check HeaderVersion
397 //
398 MicrocodeEntryPoint = Image;
399 if (MicrocodeEntryPoint->HeaderVersion != 0x1) {
400 DEBUG((DEBUG_ERROR, "VerifyMicrocode - fail on HeaderVersion\n"));
401 *LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_INVALID_FORMAT;
402 if (AbortReason != NULL) {
403 *AbortReason = AllocateCopyPool(sizeof(L"InvalidHeaderVersion"), L"InvalidHeaderVersion");
404 }
405 return EFI_INCOMPATIBLE_VERSION;
406 }
407 //
408 // Check LoaderRevision
409 //
410 if (MicrocodeEntryPoint->LoaderRevision != 0x1) {
411 DEBUG((DEBUG_ERROR, "VerifyMicrocode - fail on LoaderRevision\n"));
412 *LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_INVALID_FORMAT;
413 if (AbortReason != NULL) {
414 *AbortReason = AllocateCopyPool(sizeof(L"InvalidLoaderVersion"), L"InvalidLoaderVersion");
415 }
416 return EFI_INCOMPATIBLE_VERSION;
417 }
418 //
419 // Check TotalSize
420 //
421 if (MicrocodeEntryPoint->DataSize == 0) {
422 TotalSize = 2048;
423 } else {
424 TotalSize = MicrocodeEntryPoint->TotalSize;
425 }
426 if (TotalSize <= sizeof(CPU_MICROCODE_HEADER)) {
427 DEBUG((DEBUG_ERROR, "VerifyMicrocode - TotalSize too small\n"));
428 *LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_INVALID_FORMAT;
429 if (AbortReason != NULL) {
430 *AbortReason = AllocateCopyPool(sizeof(L"InvalidTotalSize"), L"InvalidTotalSize");
431 }
432 return EFI_VOLUME_CORRUPTED;
433 }
434 if ((TotalSize & (SIZE_1KB - 1)) != 0) {
435 DEBUG((DEBUG_ERROR, "VerifyMicrocode - TotalSize is not multiples of 1024 bytes (1 KBytes)\n"));
436 *LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_INVALID_FORMAT;
437 if (AbortReason != NULL) {
438 *AbortReason = AllocateCopyPool(sizeof(L"InvalidTotalSize"), L"InvalidTotalSize");
439 }
440 return EFI_VOLUME_CORRUPTED;
441 }
442 if (TotalSize != ImageSize) {
443 DEBUG((DEBUG_ERROR, "VerifyMicrocode - TotalSize not equal to ImageSize\n"));
444 *LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_INVALID_FORMAT;
445 if (AbortReason != NULL) {
446 *AbortReason = AllocateCopyPool(sizeof(L"InvalidTotalSize"), L"InvalidTotalSize");
447 }
448 return EFI_VOLUME_CORRUPTED;
449 }
450 //
451 // Check DataSize
452 //
453 if (MicrocodeEntryPoint->DataSize == 0) {
454 DataSize = 2048 - sizeof(CPU_MICROCODE_HEADER);
455 } else {
456 DataSize = MicrocodeEntryPoint->DataSize;
457 }
458 if (DataSize > TotalSize - sizeof(CPU_MICROCODE_HEADER)) {
459 DEBUG((DEBUG_ERROR, "VerifyMicrocode - DataSize too big\n"));
460 *LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_INVALID_FORMAT;
461 if (AbortReason != NULL) {
462 *AbortReason = AllocateCopyPool(sizeof(L"InvalidDataSize"), L"InvalidDataSize");
463 }
464 return EFI_VOLUME_CORRUPTED;
465 }
466 if ((DataSize & 0x3) != 0) {
467 DEBUG((DEBUG_ERROR, "VerifyMicrocode - DataSize is not multiples of DWORDs\n"));
468 *LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_INVALID_FORMAT;
469 if (AbortReason != NULL) {
470 *AbortReason = AllocateCopyPool(sizeof(L"InvalidDataSize"), L"InvalidDataSize");
471 }
472 return EFI_VOLUME_CORRUPTED;
473 }
474 //
475 // Check CheckSum32
476 //
477 CheckSum32 = CalculateSum32((UINT32 *)MicrocodeEntryPoint, DataSize + sizeof(CPU_MICROCODE_HEADER));
478 if (CheckSum32 != 0) {
479 DEBUG((DEBUG_ERROR, "VerifyMicrocode - fail on CheckSum32\n"));
480 *LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_INVALID_FORMAT;
481 if (AbortReason != NULL) {
482 *AbortReason = AllocateCopyPool(sizeof(L"InvalidChecksum"), L"InvalidChecksum");
483 }
484 return EFI_VOLUME_CORRUPTED;
485 }
486 InCompleteCheckSum32 = CheckSum32;
487 InCompleteCheckSum32 -= MicrocodeEntryPoint->ProcessorSignature.Uint32;
488 InCompleteCheckSum32 -= MicrocodeEntryPoint->ProcessorFlags;
489 InCompleteCheckSum32 -= MicrocodeEntryPoint->Checksum;
490
491 //
492 // Check ProcessorSignature/ProcessorFlags
493 //
494
495 ProcessorInfo = GetMatchedProcessor (MicrocodeFmpPrivate, MicrocodeEntryPoint->ProcessorSignature.Uint32, MicrocodeEntryPoint->ProcessorFlags, TargetCpuIndex);
496 if (ProcessorInfo == NULL) {
497 CorrectMicrocode = FALSE;
498 ExtendedTableLength = TotalSize - (DataSize + sizeof(CPU_MICROCODE_HEADER));
499 if (ExtendedTableLength != 0) {
500 //
501 // Extended Table exist, check if the CPU in support list
502 //
503 ExtendedTableHeader = (CPU_MICROCODE_EXTENDED_TABLE_HEADER *)((UINT8 *)(MicrocodeEntryPoint) + DataSize + sizeof(CPU_MICROCODE_HEADER));
504 //
505 // Calculate Extended Checksum
506 //
507 if ((ExtendedTableLength > sizeof(CPU_MICROCODE_EXTENDED_TABLE_HEADER)) && ((ExtendedTableLength & 0x3) == 0)) {
508 CheckSum32 = CalculateSum32((UINT32 *)ExtendedTableHeader, ExtendedTableLength);
509 if (CheckSum32 != 0) {
510 //
511 // Checksum incorrect
512 //
513 DEBUG((DEBUG_ERROR, "VerifyMicrocode - The checksum for extended table is incorrect\n"));
514 } else {
515 //
516 // Checksum correct
517 //
518 ExtendedTableCount = ExtendedTableHeader->ExtendedSignatureCount;
519 if (ExtendedTableCount > (ExtendedTableLength - sizeof(CPU_MICROCODE_EXTENDED_TABLE_HEADER)) / sizeof(CPU_MICROCODE_EXTENDED_TABLE)) {
520 DEBUG((DEBUG_ERROR, "VerifyMicrocode - ExtendedTableCount %d is too big\n", ExtendedTableCount));
521 } else {
522 ExtendedTable = (CPU_MICROCODE_EXTENDED_TABLE *)(ExtendedTableHeader + 1);
523 for (Index = 0; Index < ExtendedTableCount; Index++) {
524 CheckSum32 = InCompleteCheckSum32;
525 CheckSum32 += ExtendedTable->ProcessorSignature.Uint32;
526 CheckSum32 += ExtendedTable->ProcessorFlag;
527 CheckSum32 += ExtendedTable->Checksum;
528 if (CheckSum32 != 0) {
529 DEBUG((DEBUG_ERROR, "VerifyMicrocode - The checksum for ExtendedTable entry with index 0x%x is incorrect\n", Index));
530 } else {
531 //
532 // Verify Header
533 //
534 ProcessorInfo = GetMatchedProcessor (MicrocodeFmpPrivate, ExtendedTable->ProcessorSignature.Uint32, ExtendedTable->ProcessorFlag, TargetCpuIndex);
535 if (ProcessorInfo != NULL) {
536 //
537 // Find one
538 //
539 CorrectMicrocode = TRUE;
540 break;
541 }
542 }
543 ExtendedTable++;
544 }
545 }
546 }
547 }
548 }
549 if (!CorrectMicrocode) {
550 if (TryLoad) {
551 DEBUG((DEBUG_ERROR, "VerifyMicrocode - fail on Current ProcessorSignature/ProcessorFlags\n"));
552 }
553 *LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_INCORRECT_VERSION;
554 if (AbortReason != NULL) {
555 *AbortReason = AllocateCopyPool(sizeof(L"UnsupportedProcessorSignature/ProcessorFlags"), L"UnsupportedProcessorSignature/ProcessorFlags");
556 }
557 return EFI_UNSUPPORTED;
558 }
559 }
560
561 //
562 // Check UpdateRevision
563 //
564 CurrentRevision = ProcessorInfo->MicrocodeRevision;
565 if ((MicrocodeEntryPoint->UpdateRevision < CurrentRevision) ||
566 (TryLoad && (MicrocodeEntryPoint->UpdateRevision == CurrentRevision))) {
567 if (TryLoad) {
568 DEBUG((DEBUG_ERROR, "VerifyMicrocode - fail on UpdateRevision\n"));
569 }
570 *LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_INCORRECT_VERSION;
571 if (AbortReason != NULL) {
572 *AbortReason = AllocateCopyPool(sizeof(L"IncorrectRevision"), L"IncorrectRevision");
573 }
574 return EFI_INCOMPATIBLE_VERSION;
575 }
576
577 //
578 // try load MCU
579 //
580 if (TryLoad) {
581 CurrentRevision = LoadMicrocodeOnThis(MicrocodeFmpPrivate, ProcessorInfo->CpuIndex, (UINTN)MicrocodeEntryPoint + sizeof(CPU_MICROCODE_HEADER));
582 if (MicrocodeEntryPoint->UpdateRevision != CurrentRevision) {
583 DEBUG((DEBUG_ERROR, "VerifyMicrocode - fail on LoadMicrocode\n"));
584 *LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_AUTH_ERROR;
585 if (AbortReason != NULL) {
586 *AbortReason = AllocateCopyPool(sizeof(L"InvalidData"), L"InvalidData");
587 }
588 return EFI_SECURITY_VIOLATION;
589 }
590 }
591
592 return EFI_SUCCESS;
593 }
594
595 /**
596 Get next Microcode entrypoint.
597
598 @param[in] MicrocodeFmpPrivate The Microcode driver private data
599 @param[in] MicrocodeEntryPoint Current Microcode entrypoint
600
601 @return next Microcode entrypoint.
602 **/
603 CPU_MICROCODE_HEADER *
604 GetNextMicrocode (
605 IN MICROCODE_FMP_PRIVATE_DATA *MicrocodeFmpPrivate,
606 IN CPU_MICROCODE_HEADER *MicrocodeEntryPoint
607 )
608 {
609 UINTN Index;
610
611 for (Index = 0; Index < MicrocodeFmpPrivate->DescriptorCount; Index++) {
612 if (MicrocodeEntryPoint == MicrocodeFmpPrivate->MicrocodeInfo[Index].MicrocodeEntryPoint) {
613 if (Index == (UINTN)MicrocodeFmpPrivate->DescriptorCount - 1) {
614 // it is last one
615 return NULL;
616 } else {
617 // return next one
618 return MicrocodeFmpPrivate->MicrocodeInfo[Index + 1].MicrocodeEntryPoint;
619 }
620 }
621 }
622
623 ASSERT(FALSE);
624 return NULL;
625 }
626
627 /**
628 Get next FIT Microcode entrypoint.
629
630 @param[in] MicrocodeFmpPrivate The Microcode driver private data
631 @param[in] MicrocodeEntryPoint Current Microcode entrypoint
632
633 @return next FIT Microcode entrypoint.
634 **/
635 CPU_MICROCODE_HEADER *
636 GetNextFitMicrocode (
637 IN MICROCODE_FMP_PRIVATE_DATA *MicrocodeFmpPrivate,
638 IN CPU_MICROCODE_HEADER *MicrocodeEntryPoint
639 )
640 {
641 UINTN Index;
642
643 for (Index = 0; Index < MicrocodeFmpPrivate->FitMicrocodeEntryCount; Index++) {
644 if (MicrocodeEntryPoint == MicrocodeFmpPrivate->FitMicrocodeInfo[Index].MicrocodeEntryPoint) {
645 if (Index == (UINTN) MicrocodeFmpPrivate->FitMicrocodeEntryCount - 1) {
646 // it is last one
647 return NULL;
648 } else {
649 // return next one
650 return MicrocodeFmpPrivate->FitMicrocodeInfo[Index + 1].MicrocodeEntryPoint;
651 }
652 }
653 }
654
655 ASSERT(FALSE);
656 return NULL;
657 }
658
659 /**
660 Find empty FIT Microcode entrypoint.
661
662 @param[in] MicrocodeFmpPrivate The Microcode driver private data
663 @param[in] ImageSize The size of Microcode image buffer in bytes.
664 @param[out] AvailableSize Available size of the empty FIT Microcode entrypoint.
665
666 @return Empty FIT Microcode entrypoint.
667 **/
668 CPU_MICROCODE_HEADER *
669 FindEmptyFitMicrocode (
670 IN MICROCODE_FMP_PRIVATE_DATA *MicrocodeFmpPrivate,
671 IN UINTN ImageSize,
672 OUT UINTN *AvailableSize
673 )
674 {
675 UINTN Index;
676 CPU_MICROCODE_HEADER *MicrocodeEntryPoint;
677 CPU_MICROCODE_HEADER *NextMicrocodeEntryPoint;
678 VOID *MicrocodePatchAddress;
679 UINTN MicrocodePatchRegionSize;
680
681 MicrocodePatchAddress = MicrocodeFmpPrivate->MicrocodePatchAddress;
682 MicrocodePatchRegionSize = MicrocodeFmpPrivate->MicrocodePatchRegionSize;
683
684 for (Index = 0; Index < MicrocodeFmpPrivate->FitMicrocodeEntryCount; Index++) {
685 if (MicrocodeFmpPrivate->FitMicrocodeInfo[Index].Empty) {
686 MicrocodeEntryPoint = MicrocodeFmpPrivate->FitMicrocodeInfo[Index].MicrocodeEntryPoint;
687 NextMicrocodeEntryPoint = GetNextFitMicrocode (MicrocodeFmpPrivate, MicrocodeEntryPoint);
688 if (NextMicrocodeEntryPoint != NULL) {
689 *AvailableSize = (UINTN) NextMicrocodeEntryPoint - (UINTN) MicrocodeEntryPoint;
690 } else {
691 *AvailableSize = (UINTN) MicrocodePatchAddress + MicrocodePatchRegionSize - (UINTN) MicrocodeEntryPoint;
692 }
693 if (*AvailableSize >= ImageSize) {
694 return MicrocodeEntryPoint;
695 }
696 }
697 }
698
699 return NULL;
700 }
701
702 /**
703 Find unused FIT Microcode entrypoint.
704
705 @param[in] MicrocodeFmpPrivate The Microcode driver private data
706 @param[in] ImageSize The size of Microcode image buffer in bytes.
707 @param[out] AvailableSize Available size of the unused FIT Microcode entrypoint.
708
709 @return Unused FIT Microcode entrypoint.
710 **/
711 CPU_MICROCODE_HEADER *
712 FindUnusedFitMicrocode (
713 IN MICROCODE_FMP_PRIVATE_DATA *MicrocodeFmpPrivate,
714 IN UINTN ImageSize,
715 OUT UINTN *AvailableSize
716 )
717 {
718 UINTN Index;
719 CPU_MICROCODE_HEADER *MicrocodeEntryPoint;
720 CPU_MICROCODE_HEADER *NextMicrocodeEntryPoint;
721 VOID *MicrocodePatchAddress;
722 UINTN MicrocodePatchRegionSize;
723
724 MicrocodePatchAddress = MicrocodeFmpPrivate->MicrocodePatchAddress;
725 MicrocodePatchRegionSize = MicrocodeFmpPrivate->MicrocodePatchRegionSize;
726
727 for (Index = 0; Index < MicrocodeFmpPrivate->FitMicrocodeEntryCount; Index++) {
728 if (!MicrocodeFmpPrivate->FitMicrocodeInfo[Index].InUse) {
729 MicrocodeEntryPoint = MicrocodeFmpPrivate->FitMicrocodeInfo[Index].MicrocodeEntryPoint;
730 NextMicrocodeEntryPoint = GetNextFitMicrocode (MicrocodeFmpPrivate, MicrocodeEntryPoint);
731 if (NextMicrocodeEntryPoint != NULL) {
732 *AvailableSize = (UINTN) NextMicrocodeEntryPoint - (UINTN) MicrocodeEntryPoint;
733 } else {
734 *AvailableSize = (UINTN) MicrocodePatchAddress + MicrocodePatchRegionSize - (UINTN) MicrocodeEntryPoint;
735 }
736 if (*AvailableSize >= ImageSize) {
737 return MicrocodeEntryPoint;
738 }
739 }
740 }
741
742 return NULL;
743 }
744
745 /**
746 Get current Microcode used region size.
747
748 @param[in] MicrocodeFmpPrivate The Microcode driver private data
749
750 @return current Microcode used region size.
751 **/
752 UINTN
753 GetCurrentMicrocodeUsedRegionSize (
754 IN MICROCODE_FMP_PRIVATE_DATA *MicrocodeFmpPrivate
755 )
756 {
757 if (MicrocodeFmpPrivate->DescriptorCount == 0) {
758 return 0;
759 }
760
761 return (UINTN)MicrocodeFmpPrivate->MicrocodeInfo[MicrocodeFmpPrivate->DescriptorCount - 1].MicrocodeEntryPoint
762 + (UINTN)MicrocodeFmpPrivate->MicrocodeInfo[MicrocodeFmpPrivate->DescriptorCount - 1].TotalSize
763 - (UINTN)MicrocodeFmpPrivate->MicrocodePatchAddress;
764 }
765
766 /**
767 Update Microcode.
768
769 @param[in] Address The flash address of Microcode.
770 @param[in] Image The Microcode image buffer.
771 @param[in] ImageSize The size of Microcode image buffer in bytes.
772 @param[out] LastAttemptStatus The last attempt status, which will be recorded in ESRT and FMP EFI_FIRMWARE_IMAGE_DESCRIPTOR.
773
774 @retval EFI_SUCCESS The Microcode image is updated.
775 @retval EFI_WRITE_PROTECTED The flash device is read only.
776 **/
777 EFI_STATUS
778 UpdateMicrocode (
779 IN UINT64 Address,
780 IN VOID *Image,
781 IN UINTN ImageSize,
782 OUT UINT32 *LastAttemptStatus
783 )
784 {
785 EFI_STATUS Status;
786
787 DEBUG((DEBUG_INFO, "PlatformUpdate:"));
788 DEBUG((DEBUG_INFO, " Address - 0x%lx,", Address));
789 DEBUG((DEBUG_INFO, " Length - 0x%x\n", ImageSize));
790
791 Status = MicrocodeFlashWrite (
792 Address,
793 Image,
794 ImageSize
795 );
796 if (!EFI_ERROR(Status)) {
797 *LastAttemptStatus = LAST_ATTEMPT_STATUS_SUCCESS;
798 } else {
799 *LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_UNSUCCESSFUL;
800 }
801 return Status;
802 }
803
804 /**
805 Update Microcode flash region with FIT.
806
807 @param[in] MicrocodeFmpPrivate The Microcode driver private data
808 @param[in] TargetMicrocodeEntryPoint Target Microcode entrypoint to be updated
809 @param[in] Image The Microcode image buffer.
810 @param[in] ImageSize The size of Microcode image buffer in bytes.
811 @param[out] LastAttemptStatus The last attempt status, which will be recorded in ESRT and FMP EFI_FIRMWARE_IMAGE_DESCRIPTOR.
812
813 @retval EFI_SUCCESS The Microcode image is written.
814 @retval EFI_WRITE_PROTECTED The flash device is read only.
815 **/
816 EFI_STATUS
817 UpdateMicrocodeFlashRegionWithFit (
818 IN MICROCODE_FMP_PRIVATE_DATA *MicrocodeFmpPrivate,
819 IN CPU_MICROCODE_HEADER *TargetMicrocodeEntryPoint,
820 IN VOID *Image,
821 IN UINTN ImageSize,
822 OUT UINT32 *LastAttemptStatus
823 )
824 {
825 VOID *MicrocodePatchAddress;
826 UINTN MicrocodePatchRegionSize;
827 UINTN TargetTotalSize;
828 EFI_STATUS Status;
829 VOID *MicrocodePatchScratchBuffer;
830 UINT8 *ScratchBufferPtr;
831 UINTN ScratchBufferSize;
832 UINTN RestSize;
833 UINTN AvailableSize;
834 VOID *NextMicrocodeEntryPoint;
835 VOID *EmptyFitMicrocodeEntry;
836 VOID *UnusedFitMicrocodeEntry;
837
838 DEBUG((DEBUG_INFO, "UpdateMicrocodeFlashRegionWithFit: Image - 0x%x, size - 0x%x\n", Image, ImageSize));
839
840 MicrocodePatchAddress = MicrocodeFmpPrivate->MicrocodePatchAddress;
841 MicrocodePatchRegionSize = MicrocodeFmpPrivate->MicrocodePatchRegionSize;
842
843 MicrocodePatchScratchBuffer = AllocateZeroPool (MicrocodePatchRegionSize);
844 if (MicrocodePatchScratchBuffer == NULL) {
845 DEBUG((DEBUG_ERROR, "Fail to allocate Microcode Scratch buffer\n"));
846 *LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_INSUFFICIENT_RESOURCES;
847 return EFI_OUT_OF_RESOURCES;
848 }
849 ScratchBufferPtr = MicrocodePatchScratchBuffer;
850 ScratchBufferSize = 0;
851
852 //
853 // Target data collection
854 //
855 TargetTotalSize = 0;
856 AvailableSize = 0;
857 if (TargetMicrocodeEntryPoint != NULL) {
858 if (TargetMicrocodeEntryPoint->DataSize == 0) {
859 TargetTotalSize = 2048;
860 } else {
861 TargetTotalSize = TargetMicrocodeEntryPoint->TotalSize;
862 }
863 DEBUG((DEBUG_INFO, " TargetTotalSize - 0x%x\n", TargetTotalSize));
864 NextMicrocodeEntryPoint = GetNextFitMicrocode (MicrocodeFmpPrivate, TargetMicrocodeEntryPoint);
865 DEBUG((DEBUG_INFO, " NextMicrocodeEntryPoint - 0x%x\n", NextMicrocodeEntryPoint));
866 if (NextMicrocodeEntryPoint != NULL) {
867 ASSERT ((UINTN) NextMicrocodeEntryPoint >= ((UINTN) TargetMicrocodeEntryPoint + TargetTotalSize));
868 AvailableSize = (UINTN) NextMicrocodeEntryPoint - (UINTN) TargetMicrocodeEntryPoint;
869 } else {
870 AvailableSize = (UINTN) MicrocodePatchAddress + MicrocodePatchRegionSize - (UINTN) TargetMicrocodeEntryPoint;
871 }
872 DEBUG((DEBUG_INFO, " AvailableSize - 0x%x\n", AvailableSize));
873 ASSERT (AvailableSize >= TargetTotalSize);
874 }
875 //
876 // Total Size means the Microcode size.
877 // Available Size means the Microcode size plus the pad till (1) next Microcode or (2) the end.
878 //
879 // (1)
880 // +------+-----------+-----+------+===================+
881 // | MCU1 | Microcode | PAD | MCU2 | Empty |
882 // +------+-----------+-----+------+===================+
883 // | TotalSize |
884 // |<-AvailableSize->|
885 //
886 // (2)
887 // +------+-----------+===================+
888 // | MCU | Microcode | Empty |
889 // +------+-----------+===================+
890 // | TotalSize |
891 // |<- AvailableSize ->|
892 //
893
894 //
895 // Update based on policy
896 //
897
898 //
899 // 1. If there is enough space to update old one in situ, replace old microcode in situ.
900 //
901 if (AvailableSize >= ImageSize) {
902 DEBUG((DEBUG_INFO, "Replace old microcode in situ\n"));
903 //
904 // +------+------------+------+===================+
905 // |Other | Old Image | ... | Empty |
906 // +------+------------+------+===================+
907 //
908 // +------+---------+--+------+===================+
909 // |Other |New Image|FF| ... | Empty |
910 // +------+---------+--+------+===================+
911 //
912 // 1.1. Copy new image
913 CopyMem (ScratchBufferPtr, Image, ImageSize);
914 ScratchBufferSize += ImageSize;
915 ScratchBufferPtr = (UINT8 *)MicrocodePatchScratchBuffer + ScratchBufferSize;
916 // 1.2. Pad 0xFF
917 RestSize = AvailableSize - ImageSize;
918 if (RestSize > 0) {
919 SetMem (ScratchBufferPtr, RestSize, 0xFF);
920 ScratchBufferSize += RestSize;
921 ScratchBufferPtr = (UINT8 *)MicrocodePatchScratchBuffer + ScratchBufferSize;
922 }
923 Status = UpdateMicrocode((UINTN)TargetMicrocodeEntryPoint, MicrocodePatchScratchBuffer, ScratchBufferSize, LastAttemptStatus);
924 return Status;
925 }
926
927 //
928 // 2. If there is empty FIT microcode entry with enough space, use it.
929 //
930 EmptyFitMicrocodeEntry = FindEmptyFitMicrocode (MicrocodeFmpPrivate, ImageSize, &AvailableSize);
931 if (EmptyFitMicrocodeEntry != NULL) {
932 DEBUG((DEBUG_INFO, "Use empty FIT microcode entry\n"));
933 // 2.1. Copy new image
934 CopyMem (ScratchBufferPtr, Image, ImageSize);
935 ScratchBufferSize += ImageSize;
936 ScratchBufferPtr = (UINT8 *)MicrocodePatchScratchBuffer + ScratchBufferSize;
937 // 2.2. Pad 0xFF
938 RestSize = AvailableSize - ImageSize;
939 if (RestSize > 0) {
940 SetMem (ScratchBufferPtr, RestSize, 0xFF);
941 ScratchBufferSize += RestSize;
942 ScratchBufferPtr = (UINT8 *)MicrocodePatchScratchBuffer + ScratchBufferSize;
943 }
944 Status = UpdateMicrocode ((UINTN) EmptyFitMicrocodeEntry, MicrocodePatchScratchBuffer, ScratchBufferSize, LastAttemptStatus);
945 if (!EFI_ERROR (Status) && (TargetMicrocodeEntryPoint != NULL)) {
946 //
947 // Empty old microcode.
948 //
949 ScratchBufferPtr = MicrocodePatchScratchBuffer;
950 SetMem (ScratchBufferPtr, TargetTotalSize, 0xFF);
951 ScratchBufferSize = TargetTotalSize;
952 ScratchBufferPtr = (UINT8 *) MicrocodePatchScratchBuffer + ScratchBufferSize;
953 UpdateMicrocode ((UINTN) TargetMicrocodeEntryPoint, MicrocodePatchScratchBuffer, ScratchBufferSize, LastAttemptStatus);
954 }
955 return Status;
956 }
957
958 //
959 // 3. If there is unused microcode entry with enough space, use it.
960 //
961 UnusedFitMicrocodeEntry = FindUnusedFitMicrocode (MicrocodeFmpPrivate, ImageSize, &AvailableSize);
962 if (UnusedFitMicrocodeEntry != NULL) {
963 DEBUG((DEBUG_INFO, "Use unused FIT microcode entry\n"));
964 // 3.1. Copy new image
965 CopyMem (ScratchBufferPtr, Image, ImageSize);
966 ScratchBufferSize += ImageSize;
967 ScratchBufferPtr = (UINT8 *)MicrocodePatchScratchBuffer + ScratchBufferSize;
968 // 3.2. Pad 0xFF
969 RestSize = AvailableSize - ImageSize;
970 if (RestSize > 0) {
971 SetMem (ScratchBufferPtr, RestSize, 0xFF);
972 ScratchBufferSize += RestSize;
973 ScratchBufferPtr = (UINT8 *)MicrocodePatchScratchBuffer + ScratchBufferSize;
974 }
975 Status = UpdateMicrocode ((UINTN) UnusedFitMicrocodeEntry, MicrocodePatchScratchBuffer, ScratchBufferSize, LastAttemptStatus);
976 if (!EFI_ERROR (Status) && (TargetMicrocodeEntryPoint != NULL)) {
977 //
978 // Empty old microcode.
979 //
980 ScratchBufferPtr = MicrocodePatchScratchBuffer;
981 SetMem (ScratchBufferPtr, TargetTotalSize, 0xFF);
982 ScratchBufferSize = TargetTotalSize;
983 ScratchBufferPtr = (UINT8 *) MicrocodePatchScratchBuffer + ScratchBufferSize;
984 UpdateMicrocode ((UINTN) TargetMicrocodeEntryPoint, MicrocodePatchScratchBuffer, ScratchBufferSize, LastAttemptStatus);
985 }
986 return Status;
987 }
988
989 //
990 // 4. No usable FIT microcode entry.
991 //
992 DEBUG((DEBUG_ERROR, "No usable FIT microcode entry\n"));
993 *LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_INSUFFICIENT_RESOURCES;
994 Status = EFI_OUT_OF_RESOURCES;
995
996 return Status;
997 }
998
999 /**
1000 Update Microcode flash region.
1001
1002 @param[in] MicrocodeFmpPrivate The Microcode driver private data
1003 @param[in] TargetMicrocodeEntryPoint Target Microcode entrypoint to be updated
1004 @param[in] Image The Microcode image buffer.
1005 @param[in] ImageSize The size of Microcode image buffer in bytes.
1006 @param[out] LastAttemptStatus The last attempt status, which will be recorded in ESRT and FMP EFI_FIRMWARE_IMAGE_DESCRIPTOR.
1007
1008 @retval EFI_SUCCESS The Microcode image is written.
1009 @retval EFI_WRITE_PROTECTED The flash device is read only.
1010 **/
1011 EFI_STATUS
1012 UpdateMicrocodeFlashRegion (
1013 IN MICROCODE_FMP_PRIVATE_DATA *MicrocodeFmpPrivate,
1014 IN CPU_MICROCODE_HEADER *TargetMicrocodeEntryPoint,
1015 IN VOID *Image,
1016 IN UINTN ImageSize,
1017 OUT UINT32 *LastAttemptStatus
1018 )
1019 {
1020 VOID *MicrocodePatchAddress;
1021 UINTN MicrocodePatchRegionSize;
1022 UINTN TargetTotalSize;
1023 UINTN UsedRegionSize;
1024 EFI_STATUS Status;
1025 VOID *MicrocodePatchScratchBuffer;
1026 UINT8 *ScratchBufferPtr;
1027 UINTN ScratchBufferSize;
1028 UINTN RestSize;
1029 UINTN AvailableSize;
1030 VOID *NextMicrocodeEntryPoint;
1031 MICROCODE_INFO *MicrocodeInfo;
1032 UINTN MicrocodeCount;
1033 UINTN Index;
1034
1035 DEBUG((DEBUG_INFO, "UpdateMicrocodeFlashRegion: Image - 0x%x, size - 0x%x\n", Image, ImageSize));
1036
1037 MicrocodePatchAddress = MicrocodeFmpPrivate->MicrocodePatchAddress;
1038 MicrocodePatchRegionSize = MicrocodeFmpPrivate->MicrocodePatchRegionSize;
1039
1040 MicrocodePatchScratchBuffer = AllocateZeroPool (MicrocodePatchRegionSize);
1041 if (MicrocodePatchScratchBuffer == NULL) {
1042 DEBUG((DEBUG_ERROR, "Fail to allocate Microcode Scratch buffer\n"));
1043 *LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_INSUFFICIENT_RESOURCES;
1044 return EFI_OUT_OF_RESOURCES;
1045 }
1046 ScratchBufferPtr = MicrocodePatchScratchBuffer;
1047 ScratchBufferSize = 0;
1048
1049 //
1050 // Target data collection
1051 //
1052 TargetTotalSize = 0;
1053 AvailableSize = 0;
1054 NextMicrocodeEntryPoint = NULL;
1055 if (TargetMicrocodeEntryPoint != NULL) {
1056 if (TargetMicrocodeEntryPoint->DataSize == 0) {
1057 TargetTotalSize = 2048;
1058 } else {
1059 TargetTotalSize = TargetMicrocodeEntryPoint->TotalSize;
1060 }
1061 DEBUG((DEBUG_INFO, " TargetTotalSize - 0x%x\n", TargetTotalSize));
1062 NextMicrocodeEntryPoint = GetNextMicrocode(MicrocodeFmpPrivate, TargetMicrocodeEntryPoint);
1063 DEBUG((DEBUG_INFO, " NextMicrocodeEntryPoint - 0x%x\n", NextMicrocodeEntryPoint));
1064 if (NextMicrocodeEntryPoint != NULL) {
1065 ASSERT ((UINTN)NextMicrocodeEntryPoint >= ((UINTN)TargetMicrocodeEntryPoint + TargetTotalSize));
1066 AvailableSize = (UINTN)NextMicrocodeEntryPoint - (UINTN)TargetMicrocodeEntryPoint;
1067 } else {
1068 AvailableSize = (UINTN)MicrocodePatchAddress + MicrocodePatchRegionSize - (UINTN)TargetMicrocodeEntryPoint;
1069 }
1070 DEBUG((DEBUG_INFO, " AvailableSize - 0x%x\n", AvailableSize));
1071 ASSERT (AvailableSize >= TargetTotalSize);
1072 }
1073 UsedRegionSize = GetCurrentMicrocodeUsedRegionSize(MicrocodeFmpPrivate);
1074 DEBUG((DEBUG_INFO, " UsedRegionSize - 0x%x\n", UsedRegionSize));
1075 ASSERT (UsedRegionSize >= TargetTotalSize);
1076 if (TargetMicrocodeEntryPoint != NULL) {
1077 ASSERT ((UINTN)MicrocodePatchAddress + UsedRegionSize >= ((UINTN)TargetMicrocodeEntryPoint + TargetTotalSize));
1078 }
1079 //
1080 // Total Size means the Microcode size.
1081 // Available Size means the Microcode size plus the pad till (1) next Microcode or (2) the end.
1082 //
1083 // (1)
1084 // +------+-----------+-----+------+===================+
1085 // | MCU1 | Microcode | PAD | MCU2 | Empty |
1086 // +------+-----------+-----+------+===================+
1087 // | TotalSize |
1088 // |<-AvailableSize->|
1089 // |<- UsedRegionSize ->|
1090 //
1091 // (2)
1092 // +------+-----------+===================+
1093 // | MCU | Microcode | Empty |
1094 // +------+-----------+===================+
1095 // | TotalSize |
1096 // |<- AvailableSize ->|
1097 // |<-UsedRegionSize->|
1098 //
1099
1100 //
1101 // Update based on policy
1102 //
1103
1104 //
1105 // 1. If there is enough space to update old one in situ, replace old microcode in situ.
1106 //
1107 if (AvailableSize >= ImageSize) {
1108 DEBUG((DEBUG_INFO, "Replace old microcode in situ\n"));
1109 //
1110 // +------+------------+------+===================+
1111 // |Other | Old Image | ... | Empty |
1112 // +------+------------+------+===================+
1113 //
1114 // +------+---------+--+------+===================+
1115 // |Other |New Image|FF| ... | Empty |
1116 // +------+---------+--+------+===================+
1117 //
1118 // 1.1. Copy new image
1119 CopyMem (ScratchBufferPtr, Image, ImageSize);
1120 ScratchBufferSize += ImageSize;
1121 ScratchBufferPtr = (UINT8 *)MicrocodePatchScratchBuffer + ScratchBufferSize;
1122 // 1.2. Pad 0xFF
1123 RestSize = AvailableSize - ImageSize;
1124 if (RestSize > 0) {
1125 SetMem (ScratchBufferPtr, RestSize, 0xFF);
1126 ScratchBufferSize += RestSize;
1127 ScratchBufferPtr = (UINT8 *)MicrocodePatchScratchBuffer + ScratchBufferSize;
1128 }
1129 Status = UpdateMicrocode((UINTN)TargetMicrocodeEntryPoint, MicrocodePatchScratchBuffer, ScratchBufferSize, LastAttemptStatus);
1130 return Status;
1131 }
1132
1133 //
1134 // 2. If there is enough space to remove old one and add new one, reorg and replace old microcode.
1135 //
1136 if (MicrocodePatchRegionSize - (UsedRegionSize - TargetTotalSize) >= ImageSize) {
1137 if (TargetMicrocodeEntryPoint == NULL) {
1138 DEBUG((DEBUG_INFO, "Append new microcode\n"));
1139 //
1140 // +------+------------+------+===================+
1141 // |Other1| Other |Other2| Empty |
1142 // +------+------------+------+===================+
1143 //
1144 // +------+------------+------+-----------+=======+
1145 // |Other1| Other |Other2| New Image | Empty |
1146 // +------+------------+------+-----------+=======+
1147 //
1148 Status = UpdateMicrocode((UINTN)MicrocodePatchAddress + UsedRegionSize, Image, ImageSize, LastAttemptStatus);
1149 } else {
1150 DEBUG((DEBUG_INFO, "Reorg and replace old microcode\n"));
1151 //
1152 // +------+------------+------+===================+
1153 // |Other | Old Image | ... | Empty |
1154 // +------+------------+------+===================+
1155 //
1156 // +------+---------------+------+================+
1157 // |Other | New Image | ... | Empty |
1158 // +------+---------------+------+================+
1159 //
1160 // 2.1. Copy new image
1161 CopyMem (ScratchBufferPtr, Image, ImageSize);
1162 ScratchBufferSize += ImageSize;
1163 ScratchBufferPtr = (UINT8 *)MicrocodePatchScratchBuffer + ScratchBufferSize;
1164 // 2.2. Copy rest images after the old image.
1165 if (NextMicrocodeEntryPoint != 0) {
1166 RestSize = (UINTN)MicrocodePatchAddress + UsedRegionSize - ((UINTN)NextMicrocodeEntryPoint);
1167 CopyMem (ScratchBufferPtr, NextMicrocodeEntryPoint, RestSize);
1168 ScratchBufferSize += RestSize;
1169 ScratchBufferPtr = (UINT8 *)MicrocodePatchScratchBuffer + ScratchBufferSize;
1170 }
1171 Status = UpdateMicrocode((UINTN)TargetMicrocodeEntryPoint, MicrocodePatchScratchBuffer, ScratchBufferSize, LastAttemptStatus);
1172 }
1173 return Status;
1174 }
1175
1176 //
1177 // 3. The new image can be put in MCU region, but not all others can be put.
1178 // So all the unused MCU is removed.
1179 //
1180 if (MicrocodePatchRegionSize >= ImageSize) {
1181 //
1182 // +------+------------+------+===================+
1183 // |Other1| Old Image |Other2| Empty |
1184 // +------+------------+------+===================+
1185 //
1186 // +-------------------------------------+--------+
1187 // | New Image | Other |
1188 // +-------------------------------------+--------+
1189 //
1190 DEBUG((DEBUG_INFO, "Add new microcode from beginning\n"));
1191
1192 MicrocodeCount = MicrocodeFmpPrivate->DescriptorCount;
1193 MicrocodeInfo = MicrocodeFmpPrivate->MicrocodeInfo;
1194
1195 // 3.1. Copy new image
1196 CopyMem (ScratchBufferPtr, Image, ImageSize);
1197 ScratchBufferSize += ImageSize;
1198 ScratchBufferPtr = (UINT8 *)MicrocodePatchScratchBuffer + ScratchBufferSize;
1199 // 3.2. Copy some others to rest buffer
1200 for (Index = 0; Index < MicrocodeCount; Index++) {
1201 if (!MicrocodeInfo[Index].InUse) {
1202 continue;
1203 }
1204 if (MicrocodeInfo[Index].MicrocodeEntryPoint == TargetMicrocodeEntryPoint) {
1205 continue;
1206 }
1207 if (MicrocodeInfo[Index].TotalSize <= MicrocodePatchRegionSize - ScratchBufferSize) {
1208 CopyMem (ScratchBufferPtr, MicrocodeInfo[Index].MicrocodeEntryPoint, MicrocodeInfo[Index].TotalSize);
1209 ScratchBufferSize += MicrocodeInfo[Index].TotalSize;
1210 ScratchBufferPtr = (UINT8 *)MicrocodePatchScratchBuffer + ScratchBufferSize;
1211 }
1212 }
1213 // 3.3. Pad 0xFF
1214 RestSize = MicrocodePatchRegionSize - ScratchBufferSize;
1215 if (RestSize > 0) {
1216 SetMem (ScratchBufferPtr, RestSize, 0xFF);
1217 ScratchBufferSize += RestSize;
1218 ScratchBufferPtr = (UINT8 *)MicrocodePatchScratchBuffer + ScratchBufferSize;
1219 }
1220 Status = UpdateMicrocode((UINTN)MicrocodePatchAddress, MicrocodePatchScratchBuffer, ScratchBufferSize, LastAttemptStatus);
1221 return Status;
1222 }
1223
1224 //
1225 // 4. The new image size is bigger than the whole MCU region.
1226 //
1227 DEBUG((DEBUG_ERROR, "Microcode too big\n"));
1228 *LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_INSUFFICIENT_RESOURCES;
1229 Status = EFI_OUT_OF_RESOURCES;
1230
1231 return Status;
1232 }
1233
1234 /**
1235 Write Microcode.
1236
1237 Caution: This function may receive untrusted input.
1238
1239 @param[in] MicrocodeFmpPrivate The Microcode driver private data
1240 @param[in] Image The Microcode image buffer.
1241 @param[in] ImageSize The size of Microcode image buffer in bytes.
1242 @param[out] LastAttemptVersion The last attempt version, which will be recorded in ESRT and FMP EFI_FIRMWARE_IMAGE_DESCRIPTOR.
1243 @param[out] LastAttemptStatus The last attempt status, which will be recorded in ESRT and FMP EFI_FIRMWARE_IMAGE_DESCRIPTOR.
1244 @param[out] AbortReason A pointer to a pointer to a null-terminated string providing more
1245 details for the aborted operation. The buffer is allocated by this function
1246 with AllocatePool(), and it is the caller's responsibility to free it with a
1247 call to FreePool().
1248
1249 @retval EFI_SUCCESS The Microcode image is written.
1250 @retval EFI_VOLUME_CORRUPTED The Microcode image is corrupted.
1251 @retval EFI_INCOMPATIBLE_VERSION The Microcode image version is incorrect.
1252 @retval EFI_SECURITY_VIOLATION The Microcode image fails to load.
1253 @retval EFI_WRITE_PROTECTED The flash device is read only.
1254 **/
1255 EFI_STATUS
1256 MicrocodeWrite (
1257 IN MICROCODE_FMP_PRIVATE_DATA *MicrocodeFmpPrivate,
1258 IN VOID *Image,
1259 IN UINTN ImageSize,
1260 OUT UINT32 *LastAttemptVersion,
1261 OUT UINT32 *LastAttemptStatus,
1262 OUT CHAR16 **AbortReason
1263 )
1264 {
1265 EFI_STATUS Status;
1266 VOID *AlignedImage;
1267 CPU_MICROCODE_HEADER *TargetMicrocodeEntryPoint;
1268 UINTN TargetCpuIndex;
1269 UINTN TargetMicrcodeIndex;
1270
1271 //
1272 // MCU must be 16 bytes aligned
1273 //
1274 AlignedImage = AllocateCopyPool(ImageSize, Image);
1275 if (AlignedImage == NULL) {
1276 DEBUG((DEBUG_ERROR, "Fail to allocate aligned image\n"));
1277 *LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_INSUFFICIENT_RESOURCES;
1278 return EFI_OUT_OF_RESOURCES;
1279 }
1280
1281 TargetCpuIndex = (UINTN)-1;
1282 Status = VerifyMicrocode(MicrocodeFmpPrivate, AlignedImage, ImageSize, TRUE, LastAttemptStatus, AbortReason, &TargetCpuIndex);
1283 if (EFI_ERROR(Status)) {
1284 DEBUG((DEBUG_ERROR, "Fail to verify Microcode Region\n"));
1285 FreePool(AlignedImage);
1286 return Status;
1287 }
1288 DEBUG((DEBUG_INFO, "Pass VerifyMicrocode\n"));
1289 *LastAttemptVersion = ((CPU_MICROCODE_HEADER *)Image)->UpdateRevision;
1290
1291 DEBUG((DEBUG_INFO, " TargetCpuIndex - 0x%x\n", TargetCpuIndex));
1292 ASSERT (TargetCpuIndex < MicrocodeFmpPrivate->ProcessorCount);
1293 TargetMicrcodeIndex = MicrocodeFmpPrivate->ProcessorInfo[TargetCpuIndex].MicrocodeIndex;
1294 DEBUG((DEBUG_INFO, " TargetMicrcodeIndex - 0x%x\n", TargetMicrcodeIndex));
1295 if (TargetMicrcodeIndex != (UINTN)-1) {
1296 ASSERT (TargetMicrcodeIndex < MicrocodeFmpPrivate->DescriptorCount);
1297 TargetMicrocodeEntryPoint = MicrocodeFmpPrivate->MicrocodeInfo[TargetMicrcodeIndex].MicrocodeEntryPoint;
1298 } else {
1299 TargetMicrocodeEntryPoint = NULL;
1300 }
1301 DEBUG((DEBUG_INFO, " TargetMicrocodeEntryPoint - 0x%x\n", TargetMicrocodeEntryPoint));
1302
1303 if (MicrocodeFmpPrivate->FitMicrocodeInfo != NULL) {
1304 Status = UpdateMicrocodeFlashRegionWithFit (
1305 MicrocodeFmpPrivate,
1306 TargetMicrocodeEntryPoint,
1307 AlignedImage,
1308 ImageSize,
1309 LastAttemptStatus
1310 );
1311 } else {
1312 Status = UpdateMicrocodeFlashRegion (
1313 MicrocodeFmpPrivate,
1314 TargetMicrocodeEntryPoint,
1315 AlignedImage,
1316 ImageSize,
1317 LastAttemptStatus
1318 );
1319 }
1320
1321 FreePool(AlignedImage);
1322
1323 return Status;
1324 }
1325
1326