]> git.proxmox.com Git - mirror_edk2.git/blame - UefiCpuPkg/Feature/Capsule/MicrocodeUpdateDxe/MicrocodeUpdate.c
UefiCpuPkg/MicrocodeUpdate: enhance flash write logic
[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
2ed65824
JY
24\r
25/**\r
26 Verify Microcode.\r
27\r
28 Caution: This function may receive untrusted input.\r
29\r
30 @param[in] Image The Microcode image buffer.\r
31 @param[in] ImageSize The size of Microcode image buffer in bytes.\r
32 @param[in] TryLoad Try to load Microcode or not.\r
33 @param[out] LastAttemptStatus The last attempt status, which will be recorded in ESRT and FMP EFI_FIRMWARE_IMAGE_DESCRIPTOR.\r
34 @param[out] AbortReason A pointer to a pointer to a null-terminated string providing more\r
35 details for the aborted operation. The buffer is allocated by this function\r
36 with AllocatePool(), and it is the caller's responsibility to free it with a\r
37 call to FreePool().\r
38\r
39 @retval EFI_SUCCESS The Microcode image passes verification.\r
40 @retval EFI_VOLUME_CORRUPTED The Microcode image is corrupt.\r
41 @retval EFI_INCOMPATIBLE_VERSION The Microcode image version is incorrect.\r
42 @retval EFI_UNSUPPORTED The Microcode ProcessorSignature or ProcessorFlags is incorrect.\r
43 @retval EFI_SECURITY_VIOLATION The Microcode image fails to load.\r
44**/\r
45EFI_STATUS\r
46VerifyMicrocode (\r
47 IN VOID *Image,\r
48 IN UINTN ImageSize,\r
49 IN BOOLEAN TryLoad,\r
50 OUT UINT32 *LastAttemptStatus,\r
51 OUT CHAR16 **AbortReason\r
52 );\r
53\r
88266859
JY
54/**\r
55 Get Microcode Region.\r
56\r
57 @param[out] MicrocodePatchAddress The address of Microcode\r
58 @param[out] MicrocodePatchRegionSize The region size of Microcode\r
59\r
60 @retval TRUE The Microcode region is returned.\r
61 @retval FALSE No Microcode region.\r
62**/\r
63BOOLEAN\r
64GetMicrocodeRegion (\r
2ed65824
JY
65 OUT VOID **MicrocodePatchAddress,\r
66 OUT UINTN *MicrocodePatchRegionSize\r
88266859
JY
67 )\r
68{\r
2ed65824
JY
69 *MicrocodePatchAddress = (VOID *)(UINTN)PcdGet64(PcdCpuMicrocodePatchAddress);\r
70 *MicrocodePatchRegionSize = (UINTN)PcdGet64(PcdCpuMicrocodePatchRegionSize);\r
88266859 71\r
2ed65824 72 if ((*MicrocodePatchAddress == NULL) || (*MicrocodePatchRegionSize == 0)) {\r
88266859
JY
73 return FALSE;\r
74 }\r
75\r
76 return TRUE;\r
77}\r
78\r
79/**\r
80 Get Microcode update signature of currently loaded Microcode update.\r
81\r
82 @return Microcode signature.\r
83\r
84**/\r
85UINT32\r
86GetCurrentMicrocodeSignature (\r
87 VOID\r
88 )\r
89{\r
90 UINT64 Signature;\r
91\r
92 AsmWriteMsr64(MSR_IA32_BIOS_SIGN_ID, 0);\r
93 AsmCpuid(CPUID_VERSION_INFO, NULL, NULL, NULL, NULL);\r
94 Signature = AsmReadMsr64(MSR_IA32_BIOS_SIGN_ID);\r
95 return (UINT32)RShiftU64(Signature, 32);\r
96}\r
97\r
98/**\r
99 Get current processor signature.\r
100\r
101 @return current processor signature.\r
102**/\r
103UINT32\r
104GetCurrentProcessorSignature (\r
105 VOID\r
106 )\r
107{\r
108 UINT32 RegEax;\r
109 AsmCpuid(CPUID_VERSION_INFO, &RegEax, NULL, NULL, NULL);\r
110 return RegEax;\r
111}\r
112\r
113/**\r
114 Get current platform ID.\r
115\r
116 @return current platform ID.\r
117**/\r
118UINT8\r
119GetCurrentPlatformId (\r
120 VOID\r
121 )\r
122{\r
123 UINT8 PlatformId;\r
124\r
125 PlatformId = (UINT8)AsmMsrBitFieldRead64(MSR_IA32_PLATFORM_ID, 50, 52);\r
126 return PlatformId;\r
127}\r
128\r
129/**\r
130 Load new Microcode.\r
131\r
132 @param[in] Address The address of new Microcode.\r
133\r
134 @return Loaded Microcode signature.\r
135\r
136**/\r
137UINT32\r
138LoadMicrocode (\r
139 IN UINT64 Address\r
140 )\r
141{\r
142 AsmWriteMsr64(MSR_IA32_BIOS_UPDT_TRIG, Address);\r
143 return GetCurrentMicrocodeSignature();\r
144}\r
145\r
2ed65824
JY
146/**\r
147 If the Microcode is used by current processor.\r
148\r
149 @param[in] MicrocodeEntryPoint The Microcode buffer\r
150\r
151 @retval TRUE The Microcode is used by current processor.\r
152 @retval FALSE The Microcode is NOT used by current processor.\r
153**/\r
154BOOLEAN\r
155IsMicrocodeInUse (\r
156 IN CPU_MICROCODE_HEADER *MicrocodeEntryPoint\r
157 )\r
158{\r
159 UINT32 AttemptStatus;\r
160 UINTN TotalSize;\r
161 EFI_STATUS Status;\r
162\r
163 if (MicrocodeEntryPoint->HeaderVersion == 0x1 && MicrocodeEntryPoint->LoaderRevision == 0x1) {\r
164 //\r
165 // It is the microcode header. It is not the padding data between microcode patches\r
166 // becasue the padding data should not include 0x00000001 and it should be the repeated\r
167 // byte format (like 0xXYXYXYXY....).\r
168 //\r
169 if (MicrocodeEntryPoint->DataSize == 0) {\r
170 TotalSize = 2048;\r
171 } else {\r
172 TotalSize = MicrocodeEntryPoint->TotalSize;\r
173 }\r
174 Status = VerifyMicrocode(MicrocodeEntryPoint, TotalSize, FALSE, &AttemptStatus, NULL);\r
175 if (!EFI_ERROR(Status)) {\r
176 return TRUE;\r
177 }\r
178 }\r
179 return FALSE;\r
180}\r
181\r
88266859
JY
182/**\r
183 Get current Microcode information.\r
184\r
2ed65824
JY
185 NOTE: The DescriptorCount/ImageDescriptor/MicrocodeInfo in MicrocodeFmpPrivate\r
186 are not avaiable in this function.\r
187\r
188 @param[in] MicrocodeFmpPrivate The Microcode driver private data\r
189 @param[in] DescriptorCount The count of Microcode ImageDescriptor allocated.\r
190 @param[out] ImageDescriptor Microcode ImageDescriptor\r
191 @param[out] MicrocodeInfo Microcode information\r
88266859
JY
192\r
193 @return Microcode count\r
194**/\r
195UINTN\r
196GetMicrocodeInfo (\r
2ed65824
JY
197 IN MICROCODE_FMP_PRIVATE_DATA *MicrocodeFmpPrivate,\r
198 IN UINTN DescriptorCount, OPTIONAL\r
88266859 199 OUT EFI_FIRMWARE_IMAGE_DESCRIPTOR *ImageDescriptor, OPTIONAL\r
2ed65824 200 OUT MICROCODE_INFO *MicrocodeInfo OPTIONAL\r
88266859
JY
201 )\r
202{\r
2ed65824
JY
203 VOID *MicrocodePatchAddress;\r
204 UINTN MicrocodePatchRegionSize;\r
88266859
JY
205 CPU_MICROCODE_HEADER *MicrocodeEntryPoint;\r
206 UINTN MicrocodeEnd;\r
207 UINTN TotalSize;\r
208 UINTN Count;\r
209 UINT64 ImageAttributes;\r
2ed65824 210 BOOLEAN IsInUse;\r
88266859 211\r
2ed65824
JY
212 MicrocodePatchAddress = MicrocodeFmpPrivate->MicrocodePatchAddress;\r
213 MicrocodePatchRegionSize = MicrocodeFmpPrivate->MicrocodePatchRegionSize;\r
214\r
215 DEBUG((DEBUG_INFO, "Microcode Region - 0x%x - 0x%x\n", MicrocodePatchAddress, MicrocodePatchRegionSize));\r
88266859
JY
216\r
217 Count = 0;\r
88266859 218\r
2ed65824 219 MicrocodeEnd = (UINTN)MicrocodePatchAddress + MicrocodePatchRegionSize;\r
88266859
JY
220 MicrocodeEntryPoint = (CPU_MICROCODE_HEADER *) (UINTN) MicrocodePatchAddress;\r
221 do {\r
222 if (MicrocodeEntryPoint->HeaderVersion == 0x1 && MicrocodeEntryPoint->LoaderRevision == 0x1) {\r
223 //\r
224 // It is the microcode header. It is not the padding data between microcode patches\r
225 // becasue the padding data should not include 0x00000001 and it should be the repeated\r
226 // byte format (like 0xXYXYXYXY....).\r
227 //\r
228 if (MicrocodeEntryPoint->DataSize == 0) {\r
229 TotalSize = 2048;\r
230 } else {\r
231 TotalSize = MicrocodeEntryPoint->TotalSize;\r
232 }\r
233\r
2ed65824
JY
234 IsInUse = IsMicrocodeInUse (MicrocodeEntryPoint);\r
235\r
88266859
JY
236 if (ImageDescriptor != NULL && DescriptorCount > Count) {\r
237 ImageDescriptor[Count].ImageIndex = (UINT8)(Count + 1);\r
238 CopyGuid (&ImageDescriptor[Count].ImageTypeId, &gMicrocodeFmpImageTypeIdGuid);\r
239 ImageDescriptor[Count].ImageId = LShiftU64(MicrocodeEntryPoint->ProcessorFlags, 32) + MicrocodeEntryPoint->ProcessorSignature.Uint32;\r
240 ImageDescriptor[Count].ImageIdName = NULL;\r
241 ImageDescriptor[Count].Version = MicrocodeEntryPoint->UpdateRevision;\r
242 ImageDescriptor[Count].VersionName = NULL;\r
243 ImageDescriptor[Count].Size = TotalSize;\r
244 ImageAttributes = IMAGE_ATTRIBUTE_IMAGE_UPDATABLE | IMAGE_ATTRIBUTE_RESET_REQUIRED;\r
2ed65824 245 if (IsInUse) {\r
88266859
JY
246 ImageAttributes |= IMAGE_ATTRIBUTE_IN_USE;\r
247 }\r
248 ImageDescriptor[Count].AttributesSupported = ImageAttributes | IMAGE_ATTRIBUTE_IN_USE;\r
249 ImageDescriptor[Count].AttributesSetting = ImageAttributes;\r
250 ImageDescriptor[Count].Compatibilities = 0;\r
251 ImageDescriptor[Count].LowestSupportedImageVersion = MicrocodeEntryPoint->UpdateRevision; // do not support rollback\r
252 ImageDescriptor[Count].LastAttemptVersion = 0;\r
253 ImageDescriptor[Count].LastAttemptStatus = 0;\r
254 ImageDescriptor[Count].HardwareInstance = 0;\r
255 }\r
2ed65824
JY
256 if (MicrocodeInfo != NULL && DescriptorCount > Count) {\r
257 MicrocodeInfo[Count].MicrocodeEntryPoint = MicrocodeEntryPoint;\r
258 MicrocodeInfo[Count].TotalSize = TotalSize;\r
259 MicrocodeInfo[Count].InUse = IsInUse;\r
260 }\r
88266859
JY
261 } else {\r
262 //\r
263 // It is the padding data between the microcode patches for microcode patches alignment.\r
264 // Because the microcode patch is the multiple of 1-KByte, the padding data should not\r
265 // exist if the microcode patch alignment value is not larger than 1-KByte. So, the microcode\r
266 // alignment value should be larger than 1-KByte. We could skip SIZE_1KB padding data to\r
267 // find the next possible microcode patch header.\r
268 //\r
269 MicrocodeEntryPoint = (CPU_MICROCODE_HEADER *) (((UINTN) MicrocodeEntryPoint) + SIZE_1KB);\r
270 continue;\r
271 }\r
272\r
273 Count++;\r
274 ASSERT(Count < 0xFF);\r
275\r
276 //\r
277 // Get the next patch.\r
278 //\r
279 MicrocodeEntryPoint = (CPU_MICROCODE_HEADER *) (((UINTN) MicrocodeEntryPoint) + TotalSize);\r
280 } while (((UINTN) MicrocodeEntryPoint < MicrocodeEnd));\r
281\r
282 return Count;\r
283}\r
284\r
88266859
JY
285/**\r
286 Verify Microcode.\r
287\r
288 Caution: This function may receive untrusted input.\r
289\r
290 @param[in] Image The Microcode image buffer.\r
291 @param[in] ImageSize The size of Microcode image buffer in bytes.\r
292 @param[in] TryLoad Try to load Microcode or not.\r
293 @param[out] LastAttemptStatus The last attempt status, which will be recorded in ESRT and FMP EFI_FIRMWARE_IMAGE_DESCRIPTOR.\r
294 @param[out] AbortReason A pointer to a pointer to a null-terminated string providing more\r
295 details for the aborted operation. The buffer is allocated by this function\r
296 with AllocatePool(), and it is the caller's responsibility to free it with a\r
297 call to FreePool().\r
298\r
299 @retval EFI_SUCCESS The Microcode image passes verification.\r
300 @retval EFI_VOLUME_CORRUPTED The Microcode image is corrupt.\r
301 @retval EFI_INCOMPATIBLE_VERSION The Microcode image version is incorrect.\r
302 @retval EFI_UNSUPPORTED The Microcode ProcessorSignature or ProcessorFlags is incorrect.\r
303 @retval EFI_SECURITY_VIOLATION The Microcode image fails to load.\r
304**/\r
305EFI_STATUS\r
306VerifyMicrocode (\r
307 IN VOID *Image,\r
308 IN UINTN ImageSize,\r
309 IN BOOLEAN TryLoad,\r
310 OUT UINT32 *LastAttemptStatus,\r
311 OUT CHAR16 **AbortReason\r
312 )\r
313{\r
314 UINTN Index;\r
315 CPU_MICROCODE_HEADER *MicrocodeEntryPoint;\r
316 UINTN TotalSize;\r
317 UINTN DataSize;\r
318 UINT32 CurrentRevision;\r
319 UINT32 CurrentProcessorSignature;\r
320 UINT8 CurrentPlatformId;\r
321 UINT32 CheckSum32;\r
322 UINTN ExtendedTableLength;\r
323 UINT32 ExtendedTableCount;\r
324 CPU_MICROCODE_EXTENDED_TABLE *ExtendedTable;\r
325 CPU_MICROCODE_EXTENDED_TABLE_HEADER *ExtendedTableHeader;\r
326 BOOLEAN CorrectMicrocode;\r
327\r
328 //\r
329 // Check HeaderVersion\r
330 //\r
331 MicrocodeEntryPoint = Image;\r
332 if (MicrocodeEntryPoint->HeaderVersion != 0x1) {\r
333 DEBUG((DEBUG_ERROR, "VerifyMicrocode - fail on HeaderVersion\n"));\r
334 *LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_INVALID_FORMAT;\r
335 if (AbortReason != NULL) {\r
336 *AbortReason = AllocateCopyPool(sizeof(L"InvalidHeaderVersion"), L"InvalidHeaderVersion");\r
337 }\r
338 return EFI_INCOMPATIBLE_VERSION;\r
339 }\r
340 //\r
341 // Check LoaderRevision\r
342 //\r
343 if (MicrocodeEntryPoint->LoaderRevision != 0x1) {\r
344 DEBUG((DEBUG_ERROR, "VerifyMicrocode - fail on LoaderRevision\n"));\r
345 *LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_INVALID_FORMAT;\r
346 if (AbortReason != NULL) {\r
347 *AbortReason = AllocateCopyPool(sizeof(L"InvalidLoaderVersion"), L"InvalidLoaderVersion");\r
348 }\r
349 return EFI_INCOMPATIBLE_VERSION;\r
350 }\r
351 //\r
352 // Check Size\r
353 //\r
354 if (MicrocodeEntryPoint->DataSize == 0) {\r
355 TotalSize = 2048;\r
356 } else {\r
357 TotalSize = MicrocodeEntryPoint->TotalSize;\r
358 }\r
359 if (TotalSize <= sizeof(CPU_MICROCODE_HEADER)) {\r
360 DEBUG((DEBUG_ERROR, "VerifyMicrocode - TotalSize too small\n"));\r
361 *LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_INVALID_FORMAT;\r
362 if (AbortReason != NULL) {\r
363 *AbortReason = AllocateCopyPool(sizeof(L"InvalidTotalSize"), L"InvalidTotalSize");\r
364 }\r
365 return EFI_VOLUME_CORRUPTED;\r
366 }\r
367 if (TotalSize != ImageSize) {\r
368 DEBUG((DEBUG_ERROR, "VerifyMicrocode - fail on TotalSize\n"));\r
369 *LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_INVALID_FORMAT;\r
370 if (AbortReason != NULL) {\r
371 *AbortReason = AllocateCopyPool(sizeof(L"InvalidTotalSize"), L"InvalidTotalSize");\r
372 }\r
373 return EFI_VOLUME_CORRUPTED;\r
374 }\r
375 //\r
376 // Check CheckSum32\r
377 //\r
378 if (MicrocodeEntryPoint->DataSize == 0) {\r
379 DataSize = 2048 - sizeof(CPU_MICROCODE_HEADER);\r
380 } else {\r
381 DataSize = MicrocodeEntryPoint->DataSize;\r
382 }\r
383 if (DataSize > TotalSize - sizeof(CPU_MICROCODE_HEADER)) {\r
384 DEBUG((DEBUG_ERROR, "VerifyMicrocode - DataSize too big\n"));\r
385 *LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_INVALID_FORMAT;\r
386 if (AbortReason != NULL) {\r
387 *AbortReason = AllocateCopyPool(sizeof(L"InvalidDataSize"), L"InvalidDataSize");\r
388 }\r
389 return EFI_VOLUME_CORRUPTED;\r
390 }\r
391 if ((DataSize & 0x3) != 0) {\r
392 DEBUG((DEBUG_ERROR, "VerifyMicrocode - DataSize not aligned\n"));\r
393 *LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_INVALID_FORMAT;\r
394 if (AbortReason != NULL) {\r
395 *AbortReason = AllocateCopyPool(sizeof(L"InvalidDataSize"), L"InvalidDataSize");\r
396 }\r
397 return EFI_VOLUME_CORRUPTED;\r
398 }\r
399 CheckSum32 = CalculateSum32((UINT32 *)MicrocodeEntryPoint, DataSize + sizeof(CPU_MICROCODE_HEADER));\r
400 if (CheckSum32 != 0) {\r
401 DEBUG((DEBUG_ERROR, "VerifyMicrocode - fail on CheckSum32\n"));\r
402 *LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_INVALID_FORMAT;\r
403 if (AbortReason != NULL) {\r
404 *AbortReason = AllocateCopyPool(sizeof(L"InvalidChecksum"), L"InvalidChecksum");\r
405 }\r
406 return EFI_VOLUME_CORRUPTED;\r
407 }\r
408\r
409 //\r
410 // Check ProcessorSignature/ProcessorFlags\r
411 //\r
412 CorrectMicrocode = FALSE;\r
413 CurrentProcessorSignature = GetCurrentProcessorSignature();\r
414 CurrentPlatformId = GetCurrentPlatformId();\r
415 if ((MicrocodeEntryPoint->ProcessorSignature.Uint32 != CurrentProcessorSignature) ||\r
416 ((MicrocodeEntryPoint->ProcessorFlags & (1 << CurrentPlatformId)) == 0)) {\r
417 ExtendedTableLength = TotalSize - (DataSize + sizeof(CPU_MICROCODE_HEADER));\r
418 if (ExtendedTableLength != 0) {\r
419 //\r
420 // Extended Table exist, check if the CPU in support list\r
421 //\r
422 ExtendedTableHeader = (CPU_MICROCODE_EXTENDED_TABLE_HEADER *)((UINT8 *)(MicrocodeEntryPoint) + DataSize + sizeof(CPU_MICROCODE_HEADER));\r
423 //\r
424 // Calculate Extended Checksum\r
425 //\r
426 if ((ExtendedTableLength > sizeof(CPU_MICROCODE_EXTENDED_TABLE_HEADER)) && ((ExtendedTableLength & 0x3) != 0)) {\r
427 CheckSum32 = CalculateSum32((UINT32 *)ExtendedTableHeader, ExtendedTableLength);\r
428 if (CheckSum32 == 0) {\r
429 //\r
430 // Checksum correct\r
431 //\r
432 ExtendedTableCount = ExtendedTableHeader->ExtendedSignatureCount;\r
433 if (ExtendedTableCount <= (ExtendedTableLength - sizeof(CPU_MICROCODE_EXTENDED_TABLE_HEADER)) / sizeof(CPU_MICROCODE_EXTENDED_TABLE)) {\r
434 ExtendedTable = (CPU_MICROCODE_EXTENDED_TABLE *)(ExtendedTableHeader + 1);\r
435 for (Index = 0; Index < ExtendedTableCount; Index++) {\r
436 CheckSum32 = CalculateSum32((UINT32 *)ExtendedTable, sizeof(CPU_MICROCODE_EXTENDED_TABLE));\r
437 if (CheckSum32 == 0) {\r
438 //\r
439 // Verify Header\r
440 //\r
441 if ((ExtendedTable->ProcessorSignature.Uint32 == CurrentProcessorSignature) &&\r
442 (ExtendedTable->ProcessorFlag & (1 << CurrentPlatformId))) {\r
443 //\r
444 // Find one\r
445 //\r
446 CorrectMicrocode = TRUE;\r
447 break;\r
448 }\r
449 }\r
450 ExtendedTable++;\r
451 }\r
452 }\r
453 }\r
454 }\r
455 }\r
456 if (!CorrectMicrocode) {\r
2ed65824
JY
457 if (TryLoad) {\r
458 DEBUG((DEBUG_ERROR, "VerifyMicrocode - fail on CurrentProcessorSignature/ProcessorFlags\n"));\r
459 }\r
88266859
JY
460 *LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_INCORRECT_VERSION;\r
461 if (AbortReason != NULL) {\r
462 if (MicrocodeEntryPoint->ProcessorSignature.Uint32 != CurrentProcessorSignature) {\r
463 *AbortReason = AllocateCopyPool(sizeof(L"UnsupportedProcessSignature"), L"UnsupportedProcessSignature");\r
464 } else {\r
465 *AbortReason = AllocateCopyPool(sizeof(L"UnsupportedProcessorFlags"), L"UnsupportedProcessorFlags");\r
466 }\r
467 }\r
468 return EFI_UNSUPPORTED;\r
469 }\r
470 }\r
471\r
472 //\r
473 // Check UpdateRevision\r
474 //\r
475 CurrentRevision = GetCurrentMicrocodeSignature();\r
476 if (MicrocodeEntryPoint->UpdateRevision < CurrentRevision) {\r
2ed65824
JY
477 if (TryLoad) {\r
478 DEBUG((DEBUG_ERROR, "VerifyMicrocode - fail on UpdateRevision\n"));\r
479 }\r
88266859
JY
480 *LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_INCORRECT_VERSION;\r
481 if (AbortReason != NULL) {\r
482 *AbortReason = AllocateCopyPool(sizeof(L"IncorrectRevision"), L"IncorrectRevision");\r
483 }\r
484 return EFI_INCOMPATIBLE_VERSION;\r
485 }\r
486\r
487 //\r
488 // try load MCU\r
489 //\r
490 if (TryLoad) {\r
491 CurrentRevision = LoadMicrocode((UINTN)MicrocodeEntryPoint + sizeof(CPU_MICROCODE_HEADER));\r
492 if (MicrocodeEntryPoint->UpdateRevision != CurrentRevision) {\r
493 DEBUG((DEBUG_ERROR, "VerifyMicrocode - fail on LoadMicrocode\n"));\r
494 *LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_AUTH_ERROR;\r
495 if (AbortReason != NULL) {\r
496 *AbortReason = AllocateCopyPool(sizeof(L"InvalidData"), L"InvalidData");\r
497 }\r
498 return EFI_SECURITY_VIOLATION;\r
499 }\r
500 }\r
501\r
502 return EFI_SUCCESS;\r
503}\r
504\r
505/**\r
506 Get current Microcode in used.\r
507\r
2ed65824
JY
508 @param[in] MicrocodeFmpPrivate The Microcode driver private data\r
509\r
88266859
JY
510 @return current Microcode in used.\r
511**/\r
512VOID *\r
513GetCurrentMicrocodeInUse (\r
2ed65824 514 IN MICROCODE_FMP_PRIVATE_DATA *MicrocodeFmpPrivate\r
88266859
JY
515 )\r
516{\r
2ed65824 517 UINTN Index;\r
88266859 518\r
2ed65824
JY
519 for (Index = 0; Index < MicrocodeFmpPrivate->DescriptorCount; Index++) {\r
520 if (!MicrocodeFmpPrivate->MicrocodeInfo[Index].InUse) {\r
521 continue;\r
522 }\r
523 if (IsMicrocodeInUse (MicrocodeFmpPrivate->MicrocodeInfo[Index].MicrocodeEntryPoint)) {\r
524 return MicrocodeFmpPrivate->MicrocodeInfo[Index].MicrocodeEntryPoint;\r
525 }\r
88266859 526 }\r
2ed65824
JY
527 return NULL;\r
528}\r
88266859 529\r
2ed65824
JY
530/**\r
531 Get next Microcode entrypoint.\r
88266859 532\r
2ed65824
JY
533 @param[in] MicrocodeFmpPrivate The Microcode driver private data\r
534 @param[in] MicrocodeEntryPoint Current Microcode entrypoint\r
535\r
536 @return next Microcode entrypoint.\r
537**/\r
538CPU_MICROCODE_HEADER *\r
539GetNextMicrocode (\r
540 IN MICROCODE_FMP_PRIVATE_DATA *MicrocodeFmpPrivate,\r
541 IN CPU_MICROCODE_HEADER *MicrocodeEntryPoint\r
542 )\r
543{\r
544 UINTN Index;\r
545\r
546 for (Index = 0; Index < MicrocodeFmpPrivate->DescriptorCount; Index++) {\r
547 if (MicrocodeEntryPoint == MicrocodeFmpPrivate->MicrocodeInfo[Index].MicrocodeEntryPoint) {\r
548 if (Index == (UINTN)MicrocodeFmpPrivate->DescriptorCount - 1) {\r
549 // it is last one\r
550 return NULL;\r
88266859 551 } else {\r
2ed65824
JY
552 // return next one\r
553 return MicrocodeFmpPrivate->MicrocodeInfo[Index + 1].MicrocodeEntryPoint;\r
88266859 554 }\r
88266859 555 }\r
2ed65824 556 }\r
88266859 557\r
2ed65824 558 ASSERT(FALSE);\r
88266859
JY
559 return NULL;\r
560}\r
561\r
562/**\r
563 Get current Microcode used region size.\r
564\r
2ed65824
JY
565 @param[in] MicrocodeFmpPrivate The Microcode driver private data\r
566\r
88266859
JY
567 @return current Microcode used region size.\r
568**/\r
569UINTN\r
570GetCurrentMicrocodeUsedRegionSize (\r
2ed65824 571 IN MICROCODE_FMP_PRIVATE_DATA *MicrocodeFmpPrivate\r
88266859
JY
572 )\r
573{\r
2ed65824 574 if (MicrocodeFmpPrivate->DescriptorCount == 0) {\r
88266859
JY
575 return 0;\r
576 }\r
88266859 577\r
2ed65824
JY
578 return (UINTN)MicrocodeFmpPrivate->MicrocodeInfo[MicrocodeFmpPrivate->DescriptorCount - 1].MicrocodeEntryPoint\r
579 + (UINTN)MicrocodeFmpPrivate->MicrocodeInfo[MicrocodeFmpPrivate->DescriptorCount - 1].TotalSize\r
580 - (UINTN)MicrocodeFmpPrivate->MicrocodePatchAddress;\r
88266859
JY
581}\r
582\r
583/**\r
584 Update Microcode.\r
585\r
586 @param[in] Address The flash address of Microcode.\r
587 @param[in] Image The Microcode image buffer.\r
588 @param[in] ImageSize The size of Microcode image buffer in bytes.\r
589 @param[out] LastAttemptStatus The last attempt status, which will be recorded in ESRT and FMP EFI_FIRMWARE_IMAGE_DESCRIPTOR.\r
590\r
591 @retval EFI_SUCCESS The Microcode image is updated.\r
592 @retval EFI_WRITE_PROTECTED The flash device is read only.\r
593**/\r
594EFI_STATUS\r
595UpdateMicrocode (\r
596 IN UINT64 Address,\r
597 IN VOID *Image,\r
598 IN UINTN ImageSize,\r
599 OUT UINT32 *LastAttemptStatus\r
600 )\r
601{\r
602 EFI_STATUS Status;\r
603\r
604 DEBUG((DEBUG_INFO, "PlatformUpdate:"));\r
605 DEBUG((DEBUG_INFO, " Address - 0x%lx,", Address));\r
606 DEBUG((DEBUG_INFO, " Legnth - 0x%x\n", ImageSize));\r
607\r
608 Status = MicrocodeFlashWrite (\r
609 Address,\r
610 Image,\r
611 ImageSize\r
612 );\r
613 if (!EFI_ERROR(Status)) {\r
614 *LastAttemptStatus = LAST_ATTEMPT_STATUS_SUCCESS;\r
615 } else {\r
616 *LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_UNSUCCESSFUL;\r
617 }\r
618 return Status;\r
619}\r
620\r
621/**\r
2ed65824 622 Update Microcode flash region.\r
88266859 623\r
2ed65824
JY
624 @param[in] MicrocodeFmpPrivate The Microcode driver private data\r
625 @param[in] CurrentMicrocodeEntryPoint Current Microcode entrypoint\r
626 @param[in] Image The Microcode image buffer.\r
627 @param[in] ImageSize The size of Microcode image buffer in bytes.\r
628 @param[out] LastAttemptStatus The last attempt status, which will be recorded in ESRT and FMP EFI_FIRMWARE_IMAGE_DESCRIPTOR.\r
88266859 629\r
2ed65824
JY
630 @retval EFI_SUCCESS The Microcode image is written.\r
631 @retval EFI_WRITE_PROTECTED The flash device is read only.\r
88266859
JY
632**/\r
633EFI_STATUS\r
2ed65824
JY
634UpdateMicrocodeFlashRegion (\r
635 IN MICROCODE_FMP_PRIVATE_DATA *MicrocodeFmpPrivate,\r
636 IN CPU_MICROCODE_HEADER *CurrentMicrocodeEntryPoint,\r
637 IN VOID *Image,\r
638 IN UINTN ImageSize,\r
639 OUT UINT32 *LastAttemptStatus\r
88266859
JY
640 )\r
641{\r
2ed65824
JY
642 VOID *MicrocodePatchAddress;\r
643 UINTN MicrocodePatchRegionSize;\r
88266859
JY
644 UINTN CurrentTotalSize;\r
645 UINTN UsedRegionSize;\r
2ed65824
JY
646 EFI_STATUS Status;\r
647 VOID *MicrocodePatchScratchBuffer;\r
648 UINT8 *ScratchBufferPtr;\r
649 UINTN ScratchBufferSize;\r
650 UINTN RestSize;\r
651 UINTN AvailableSize;\r
652 VOID *NextMicrocodeEntryPoint;\r
653 MICROCODE_INFO *MicrocodeInfo;\r
654 UINTN MicrocodeCount;\r
655 UINTN Index;\r
656\r
657 DEBUG((DEBUG_INFO, "UpdateMicrocodeFlashRegion: Image - 0x%x, size - 0x%x\n", Image, ImageSize));\r
88266859 658\r
2ed65824
JY
659 MicrocodePatchAddress = MicrocodeFmpPrivate->MicrocodePatchAddress;\r
660 MicrocodePatchRegionSize = MicrocodeFmpPrivate->MicrocodePatchRegionSize;\r
661\r
662 MicrocodePatchScratchBuffer = AllocateZeroPool (MicrocodePatchRegionSize);\r
663 if (MicrocodePatchScratchBuffer == NULL) {\r
664 DEBUG((DEBUG_ERROR, "Fail to allocate Microcode Scratch buffer\n"));\r
665 *LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_INSUFFICIENT_RESOURCES;\r
666 return EFI_OUT_OF_RESOURCES;\r
88266859 667 }\r
2ed65824
JY
668 ScratchBufferPtr = MicrocodePatchScratchBuffer;\r
669 ScratchBufferSize = 0;\r
88266859 670\r
2ed65824
JY
671 //\r
672 // Current data collection\r
673 //\r
88266859 674 CurrentTotalSize = 0;\r
2ed65824
JY
675 AvailableSize = 0;\r
676 NextMicrocodeEntryPoint = NULL;\r
88266859
JY
677 if (CurrentMicrocodeEntryPoint != NULL) {\r
678 if (CurrentMicrocodeEntryPoint->DataSize == 0) {\r
679 CurrentTotalSize = 2048;\r
680 } else {\r
681 CurrentTotalSize = CurrentMicrocodeEntryPoint->TotalSize;\r
682 }\r
2ed65824
JY
683 DEBUG((DEBUG_INFO, " CurrentTotalSize - 0x%x\n", CurrentTotalSize));\r
684 NextMicrocodeEntryPoint = GetNextMicrocode(MicrocodeFmpPrivate, CurrentMicrocodeEntryPoint);\r
685 DEBUG((DEBUG_INFO, " NextMicrocodeEntryPoint - 0x%x\n", NextMicrocodeEntryPoint));\r
686 if (NextMicrocodeEntryPoint != NULL) {\r
687 ASSERT ((UINTN)NextMicrocodeEntryPoint >= ((UINTN)CurrentMicrocodeEntryPoint + CurrentTotalSize));\r
688 AvailableSize = (UINTN)NextMicrocodeEntryPoint - (UINTN)CurrentMicrocodeEntryPoint;\r
689 } else {\r
690 AvailableSize = (UINTN)MicrocodePatchAddress + MicrocodePatchRegionSize - (UINTN)CurrentMicrocodeEntryPoint;\r
691 }\r
692 DEBUG((DEBUG_INFO, " AvailableSize - 0x%x\n", AvailableSize));\r
693 }\r
694 ASSERT (AvailableSize >= CurrentTotalSize);\r
695 UsedRegionSize = GetCurrentMicrocodeUsedRegionSize(MicrocodeFmpPrivate);\r
696 DEBUG((DEBUG_INFO, " UsedRegionSize - 0x%x\n", UsedRegionSize));\r
697 ASSERT (UsedRegionSize >= CurrentTotalSize);\r
698 if (CurrentMicrocodeEntryPoint != NULL) {\r
699 ASSERT ((UINTN)MicrocodePatchAddress + UsedRegionSize >= ((UINTN)CurrentMicrocodeEntryPoint + CurrentTotalSize));\r
700 }\r
701 //\r
702 // Total Size means the Microcode data size.\r
703 // Available Size means the Microcode data size plus the pad till next (1) Microcode or (2) the end.\r
704 //\r
705 // (1)\r
706 // +------+-----------+-----+------+===================+\r
707 // | MCU1 | Microcode | PAD | MCU2 | Empty |\r
708 // +------+-----------+-----+------+===================+\r
709 // | TotalSize |\r
710 // |<-AvailableSize->|\r
711 // |<- UsedRegionSize ->|\r
712 //\r
713 // (2)\r
714 // +------+-----------+===================+\r
715 // | MCU | Microcode | Empty |\r
716 // +------+-----------+===================+\r
717 // | TotalSize |\r
718 // |<- AvailableSize ->|\r
719 // |<-UsedRegionSize->|\r
720 //\r
721\r
722 //\r
723 // Update based on policy\r
724 //\r
725\r
726 //\r
727 // 1. If there is enough space to update old one in situ, replace old microcode in situ.\r
728 //\r
729 if (AvailableSize >= ImageSize) {\r
730 DEBUG((DEBUG_INFO, "Replace old microcode in situ\n"));\r
731 //\r
732 // +------+------------+------+===================+\r
733 // |Other1| Old Image |Other2| Empty |\r
734 // +------+------------+------+===================+\r
735 //\r
736 // +------+---------+--+------+===================+\r
737 // |Other1|New Image|FF|Other2| Empty |\r
738 // +------+---------+--+------+===================+\r
739 //\r
740 // 1.1. Copy new image\r
741 CopyMem (ScratchBufferPtr, Image, ImageSize);\r
742 ScratchBufferSize += ImageSize;\r
743 ScratchBufferPtr = (UINT8 *)ScratchBufferPtr + ScratchBufferSize;\r
744 // 1.2. Pad 0xFF\r
745 RestSize = AvailableSize - ImageSize;\r
746 if (RestSize > 0) {\r
747 SetMem (ScratchBufferPtr, RestSize, 0xFF);\r
748 ScratchBufferSize += RestSize;\r
749 ScratchBufferPtr = (UINT8 *)ScratchBufferPtr + ScratchBufferSize;\r
750 }\r
751 Status = UpdateMicrocode((UINTN)CurrentMicrocodeEntryPoint, MicrocodePatchScratchBuffer, ScratchBufferSize, LastAttemptStatus);\r
752 return Status;\r
88266859
JY
753 }\r
754\r
2ed65824
JY
755 //\r
756 // 2. If there is enough space to remove old one and add new one, reorg and replace old microcode.\r
757 //\r
758 if (MicrocodePatchRegionSize - (UsedRegionSize - CurrentTotalSize) >= ImageSize) {\r
759 if (CurrentMicrocodeEntryPoint == NULL) {\r
760 DEBUG((DEBUG_INFO, "Append new microcode\n"));\r
761 //\r
762 // +------+------------+------+===================+\r
763 // |Other1| Other |Other2| Empty |\r
764 // +------+------------+------+===================+\r
765 //\r
766 // +------+------------+------+-----------+=======+\r
767 // |Other1| Other |Other2| New Image | Empty |\r
768 // +------+------------+------+-----------+=======+\r
769 //\r
770 Status = UpdateMicrocode((UINTN)MicrocodePatchAddress + UsedRegionSize, Image, ImageSize, LastAttemptStatus);\r
771 } else {\r
772 DEBUG((DEBUG_INFO, "Reorg and replace old microcode\n"));\r
773 //\r
774 // +------+------------+------+===================+\r
775 // |Other1| Old Image |Other2| Empty |\r
776 // +------+------------+------+===================+\r
777 //\r
778 // +------+---------------+------+================+\r
779 // |Other1| New Image |Other2| Empty |\r
780 // +------+---------------+------+================+\r
781 //\r
782 // 2.1. Copy new image\r
783 CopyMem (MicrocodePatchScratchBuffer, Image, ImageSize);\r
784 ScratchBufferSize += ImageSize;\r
785 ScratchBufferPtr = (UINT8 *)ScratchBufferPtr + ScratchBufferSize;\r
786 // 2.2. Copy rest images after the old image.\r
787 if (NextMicrocodeEntryPoint != 0) {\r
788 RestSize = (UINTN)MicrocodePatchAddress + UsedRegionSize - ((UINTN)NextMicrocodeEntryPoint);\r
789 CopyMem (ScratchBufferPtr, (UINT8 *)CurrentMicrocodeEntryPoint + CurrentTotalSize, RestSize);\r
790 ScratchBufferSize += RestSize;\r
791 ScratchBufferPtr = (UINT8 *)ScratchBufferPtr + ScratchBufferSize;\r
792 }\r
793 Status = UpdateMicrocode((UINTN)CurrentMicrocodeEntryPoint, MicrocodePatchScratchBuffer, ScratchBufferSize, LastAttemptStatus);\r
794 }\r
795 return Status;\r
796 }\r
797\r
798 //\r
799 // 3. The new image can be put in MCU region, but not all others can be put.\r
800 // So all the unused MCU is removed.\r
801 //\r
802 if (MicrocodePatchRegionSize >= ImageSize) {\r
803 //\r
804 // +------+------------+------+===================+\r
805 // |Other1| Old Image |Other2| Empty |\r
806 // +------+------------+------+===================+\r
807 //\r
808 // +-------------------------------------+--------+\r
809 // | New Image | Other |\r
810 // +-------------------------------------+--------+\r
811 //\r
812 DEBUG((DEBUG_INFO, "Add new microcode from beginning\n"));\r
813\r
814 MicrocodeCount = MicrocodeFmpPrivate->DescriptorCount;\r
815 MicrocodeInfo = MicrocodeFmpPrivate->MicrocodeInfo;\r
816\r
817 // 3.1. Copy new image\r
818 CopyMem (MicrocodePatchScratchBuffer, Image, ImageSize);\r
819 ScratchBufferSize += ImageSize;\r
820 ScratchBufferPtr = (UINT8 *)ScratchBufferPtr + ScratchBufferSize;\r
821 // 3.2. Copy some others to rest buffer\r
822 for (Index = 0; Index < MicrocodeCount; Index++) {\r
823 if (!MicrocodeInfo[Index].InUse) {\r
824 continue;\r
825 }\r
826 if (MicrocodeInfo[Index].MicrocodeEntryPoint == CurrentMicrocodeEntryPoint) {\r
827 continue;\r
828 }\r
829 if (MicrocodeInfo[Index].TotalSize <= MicrocodePatchRegionSize - ScratchBufferSize) {\r
830 CopyMem (ScratchBufferPtr, MicrocodeInfo[Index].MicrocodeEntryPoint, MicrocodeInfo[Index].TotalSize);\r
831 ScratchBufferSize += MicrocodeInfo[Index].TotalSize;\r
832 ScratchBufferPtr = (UINT8 *)ScratchBufferPtr + ScratchBufferSize;\r
833 }\r
834 }\r
835 // 3.3. Pad 0xFF\r
836 RestSize = MicrocodePatchRegionSize - ScratchBufferSize;\r
837 if (RestSize > 0) {\r
838 SetMem (ScratchBufferPtr, RestSize, 0xFF);\r
839 ScratchBufferSize += RestSize;\r
840 ScratchBufferPtr = (UINT8 *)ScratchBufferPtr + ScratchBufferSize;\r
841 }\r
842 Status = UpdateMicrocode((UINTN)MicrocodePatchAddress, MicrocodePatchScratchBuffer, ScratchBufferSize, LastAttemptStatus);\r
843 return Status;\r
844 }\r
845\r
846 //\r
847 // 4. The new image size is bigger than the whole MCU region.\r
848 //\r
849 DEBUG((DEBUG_ERROR, "Microcode too big\n"));\r
850 *LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_INSUFFICIENT_RESOURCES;\r
851 Status = EFI_OUT_OF_RESOURCES;\r
852\r
853 return Status;\r
854}\r
855\r
856/**\r
857 Write Microcode.\r
858\r
859 Caution: This function may receive untrusted input.\r
860\r
861 @param[in] MicrocodeFmpPrivate The Microcode driver private data\r
862 @param[in] Image The Microcode image buffer.\r
863 @param[in] ImageSize The size of Microcode image buffer in bytes.\r
864 @param[out] LastAttemptVersion The last attempt version, which will be recorded in ESRT and FMP EFI_FIRMWARE_IMAGE_DESCRIPTOR.\r
865 @param[out] LastAttemptStatus The last attempt status, which will be recorded in ESRT and FMP EFI_FIRMWARE_IMAGE_DESCRIPTOR.\r
866 @param[out] AbortReason A pointer to a pointer to a null-terminated string providing more\r
867 details for the aborted operation. The buffer is allocated by this function\r
868 with AllocatePool(), and it is the caller's responsibility to free it with a\r
869 call to FreePool().\r
870\r
871 @retval EFI_SUCCESS The Microcode image is written.\r
872 @retval EFI_VOLUME_CORRUPTED The Microcode image is corrupt.\r
873 @retval EFI_INCOMPATIBLE_VERSION The Microcode image version is incorrect.\r
874 @retval EFI_SECURITY_VIOLATION The Microcode image fails to load.\r
875 @retval EFI_WRITE_PROTECTED The flash device is read only.\r
876**/\r
877EFI_STATUS\r
878MicrocodeWrite (\r
879 IN MICROCODE_FMP_PRIVATE_DATA *MicrocodeFmpPrivate,\r
880 IN VOID *Image,\r
881 IN UINTN ImageSize,\r
882 OUT UINT32 *LastAttemptVersion,\r
883 OUT UINT32 *LastAttemptStatus,\r
884 OUT CHAR16 **AbortReason\r
885 )\r
886{\r
887 EFI_STATUS Status;\r
888 VOID *AlignedImage;\r
889 CPU_MICROCODE_HEADER *CurrentMicrocodeEntryPoint;\r
890\r
891 //\r
892 // We must get Current MicrocodeEntrypoint *before* VerifyMicrocode,\r
893 // because the MicrocodeSignature might be updated in VerifyMicrocode.\r
894 //\r
895 CurrentMicrocodeEntryPoint = GetCurrentMicrocodeInUse(MicrocodeFmpPrivate);\r
896 DEBUG((DEBUG_INFO, " CurrentMicrocodeEntryPoint - 0x%x\n", CurrentMicrocodeEntryPoint));\r
897\r
88266859
JY
898 //\r
899 // MCU must be 16 bytes aligned\r
900 //\r
901 AlignedImage = AllocateCopyPool(ImageSize, Image);\r
902 if (AlignedImage == NULL) {\r
903 DEBUG((DEBUG_ERROR, "Fail to allocate aligned image\n"));\r
904 *LastAttemptStatus = LAST_ATTEMPT_STATUS_ERROR_INSUFFICIENT_RESOURCES;\r
905 return EFI_OUT_OF_RESOURCES;\r
906 }\r
907\r
908 *LastAttemptVersion = ((CPU_MICROCODE_HEADER *)Image)->UpdateRevision;\r
909 Status = VerifyMicrocode(AlignedImage, ImageSize, TRUE, LastAttemptStatus, AbortReason);\r
910 if (EFI_ERROR(Status)) {\r
911 DEBUG((DEBUG_ERROR, "Fail to verify Microcode Region\n"));\r
912 FreePool(AlignedImage);\r
913 return Status;\r
914 }\r
915 DEBUG((DEBUG_INFO, "Pass VerifyMicrocode\n"));\r
916\r
2ed65824
JY
917 Status = UpdateMicrocodeFlashRegion(\r
918 MicrocodeFmpPrivate,\r
919 CurrentMicrocodeEntryPoint,\r
920 AlignedImage,\r
921 ImageSize,\r
922 LastAttemptStatus\r
923 );\r
88266859
JY
924\r
925 FreePool(AlignedImage);\r
926\r
927 return Status;\r
928}\r
929\r
930\r