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