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