]> git.proxmox.com Git - mirror_edk2.git/blame - UefiCpuPkg/Library/MpInitLib/Microcode.c
UefiCpuPkg/Microcode: Fix InComplete CheckSum32 issue
[mirror_edk2.git] / UefiCpuPkg / Library / MpInitLib / Microcode.c
CommitLineData
94f63c76
JF
1/** @file\r
2 Implementation of loading microcode on processors.\r
3\r
3e5c6c07 4 Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved.<BR>\r
94f63c76
JF
5 This program and the accompanying materials\r
6 are licensed and made available under the terms and conditions of the BSD License\r
7 which accompanies this distribution. The full text of the license may be found at\r
8 http://opensource.org/licenses/bsd-license.php\r
9\r
10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
12\r
13**/\r
14\r
15#include "MpLib.h"\r
16\r
17/**\r
18 Get microcode update signature of currently loaded microcode update.\r
19\r
20 @return Microcode signature.\r
21**/\r
22UINT32\r
23GetCurrentMicrocodeSignature (\r
24 VOID\r
25 )\r
26{\r
27 MSR_IA32_BIOS_SIGN_ID_REGISTER BiosSignIdMsr;\r
28\r
29 AsmWriteMsr64 (MSR_IA32_BIOS_SIGN_ID, 0);\r
30 AsmCpuid (CPUID_VERSION_INFO, NULL, NULL, NULL, NULL);\r
31 BiosSignIdMsr.Uint64 = AsmReadMsr64 (MSR_IA32_BIOS_SIGN_ID);\r
32 return BiosSignIdMsr.Bits.MicrocodeUpdateSignature;\r
33}\r
34\r
35/**\r
36 Detect whether specified processor can find matching microcode patch and load it.\r
37\r
b6f67b4d
CC
38 Microcode Payload as the following format:\r
39 +----------------------------------------+------------------+\r
40 | CPU_MICROCODE_HEADER | |\r
41 +----------------------------------------+ CheckSum Part1 |\r
42 | Microcode Binary | |\r
43 +----------------------------------------+------------------+\r
44 | CPU_MICROCODE_EXTENDED_TABLE_HEADER | |\r
45 +----------------------------------------+ CheckSum Part2 |\r
46 | CPU_MICROCODE_EXTENDED_TABLE | |\r
47 | ... | |\r
48 +----------------------------------------+------------------+\r
49\r
50 There may by multiple CPU_MICROCODE_EXTENDED_TABLE in this format.\r
51 The count of CPU_MICROCODE_EXTENDED_TABLE is indicated by ExtendedSignatureCount\r
52 of CPU_MICROCODE_EXTENDED_TABLE_HEADER structure.\r
53\r
54 When we are trying to verify the CheckSum32 with extended table.\r
55 We should use the fields of exnteded table to replace the corresponding\r
56 fields in CPU_MICROCODE_HEADER structure, and recalculate the\r
57 CheckSum32 with CPU_MICROCODE_HEADER + Microcode Binary. We named\r
58 it as CheckSum Part3.\r
59\r
60 The CheckSum Part2 is used to verify the CPU_MICROCODE_EXTENDED_TABLE_HEADER\r
61 and CPU_MICROCODE_EXTENDED_TABLE parts. We should make sure CheckSum Part2\r
62 is correct before we are going to verify each CPU_MICROCODE_EXTENDED_TABLE.\r
63\r
64 Only ProcessorSignature, ProcessorFlag and CheckSum are different between\r
65 CheckSum Part1 and CheckSum Part3. To avoid multiple computing CheckSum Part3.\r
66 Save an in-complete CheckSum32 from CheckSum Part1 for common parts.\r
67 When we are going to calculate CheckSum32, just should use the corresponding part\r
68 of the ProcessorSignature, ProcessorFlag and CheckSum with in-complete CheckSum32.\r
69\r
70 Notes: CheckSum32 is not a strong verification.\r
71 It does not guarantee that the data has not been modified.\r
72 CPU has its own mechanism to verify Microcode Binary part.\r
73\r
2a089134
ED
74 @param[in] CpuMpData The pointer to CPU MP Data structure.\r
75 @param[in] IsBspCallIn Indicate whether the caller is BSP or not.\r
94f63c76
JF
76**/\r
77VOID\r
78MicrocodeDetect (\r
2a089134
ED
79 IN CPU_MP_DATA *CpuMpData,\r
80 IN BOOLEAN IsBspCallIn\r
94f63c76
JF
81 )\r
82{\r
94f63c76
JF
83 UINT32 ExtendedTableLength;\r
84 UINT32 ExtendedTableCount;\r
85 CPU_MICROCODE_EXTENDED_TABLE *ExtendedTable;\r
86 CPU_MICROCODE_EXTENDED_TABLE_HEADER *ExtendedTableHeader;\r
87 CPU_MICROCODE_HEADER *MicrocodeEntryPoint;\r
88 UINTN MicrocodeEnd;\r
89 UINTN Index;\r
90 UINT8 PlatformId;\r
91 CPUID_VERSION_INFO_EAX Eax;\r
92 UINT32 CurrentRevision;\r
93 UINT32 LatestRevision;\r
94 UINTN TotalSize;\r
95 UINT32 CheckSum32;\r
b6f67b4d 96 UINT32 InCompleteCheckSum32;\r
94f63c76
JF
97 BOOLEAN CorrectMicrocode;\r
98 VOID *MicrocodeData;\r
99 MSR_IA32_PLATFORM_ID_REGISTER PlatformIdMsr;\r
2a089134 100 UINT32 ProcessorFlags;\r
f63a3e28 101 UINT32 ThreadId;\r
94f63c76 102\r
9b7242f5
ED
103 //\r
104 // set ProcessorFlags to suppress incorrect compiler/analyzer warnings\r
105 //\r
106 ProcessorFlags = 0;\r
107\r
1e3f7a37 108 if (CpuMpData->MicrocodePatchRegionSize == 0) {\r
94f63c76
JF
109 //\r
110 // There is no microcode patches\r
111 //\r
112 return;\r
113 }\r
114\r
115 CurrentRevision = GetCurrentMicrocodeSignature ();\r
2a089134 116 if (CurrentRevision != 0 && !IsBspCallIn) {\r
94f63c76
JF
117 //\r
118 // Skip loading microcode if it has been loaded successfully\r
119 //\r
120 return;\r
121 }\r
122\r
f63a3e28
ED
123 GetProcessorLocationByApicId (GetInitialApicId (), NULL, NULL, &ThreadId);\r
124 if (ThreadId != 0) {\r
125 //\r
126 // Skip loading microcode if it is not the first thread in one core.\r
127 //\r
128 return;\r
129 }\r
130\r
94f63c76
JF
131 ExtendedTableLength = 0;\r
132 //\r
133 // Here data of CPUID leafs have not been collected into context buffer, so\r
3e5c6c07 134 // GetProcessorCpuid() cannot be used here to retrieve CPUID data.\r
94f63c76
JF
135 //\r
136 AsmCpuid (CPUID_VERSION_INFO, &Eax.Uint32, NULL, NULL, NULL);\r
137\r
138 //\r
139 // The index of platform information resides in bits 50:52 of MSR IA32_PLATFORM_ID\r
140 //\r
141 PlatformIdMsr.Uint64 = AsmReadMsr64 (MSR_IA32_PLATFORM_ID);\r
142 PlatformId = (UINT8) PlatformIdMsr.Bits.PlatformId;\r
143\r
2a089134
ED
144 //\r
145 // Check whether AP has same processor with BSP.\r
146 // If yes, direct use microcode info saved by BSP.\r
147 //\r
148 if (!IsBspCallIn) {\r
149 if ((CpuMpData->ProcessorSignature == Eax.Uint32) &&\r
150 (CpuMpData->ProcessorFlags & (1 << PlatformId)) != 0) {\r
151 MicrocodeData = (VOID *)(UINTN) CpuMpData->MicrocodeDataAddress;\r
152 LatestRevision = CpuMpData->MicrocodeRevision;\r
153 goto Done;\r
154 }\r
155 }\r
156\r
94f63c76 157 LatestRevision = 0;\r
8cce3c9a 158 MicrocodeData = NULL;\r
1e3f7a37
ED
159 MicrocodeEnd = (UINTN) (CpuMpData->MicrocodePatchAddress + CpuMpData->MicrocodePatchRegionSize);\r
160 MicrocodeEntryPoint = (CPU_MICROCODE_HEADER *) (UINTN) CpuMpData->MicrocodePatchAddress;\r
b6f67b4d 161\r
94f63c76
JF
162 do {\r
163 //\r
164 // Check if the microcode is for the Cpu and the version is newer\r
165 // and the update can be processed on the platform\r
166 //\r
167 CorrectMicrocode = FALSE;\r
c3947b54
CC
168\r
169 //\r
170 // Save an in-complete CheckSum32 from CheckSum Part1 for common parts.\r
171 //\r
172 if (MicrocodeEntryPoint->DataSize == 0) {\r
173 InCompleteCheckSum32 = CalculateSum32 (\r
174 (UINT32 *) MicrocodeEntryPoint,\r
175 sizeof (CPU_MICROCODE_HEADER) + 2000\r
176 );\r
177 } else {\r
178 InCompleteCheckSum32 = CalculateSum32 (\r
179 (UINT32 *) MicrocodeEntryPoint,\r
180 sizeof (CPU_MICROCODE_HEADER) + MicrocodeEntryPoint->DataSize\r
181 );\r
182 }\r
183 InCompleteCheckSum32 -= MicrocodeEntryPoint->ProcessorSignature.Uint32;\r
184 InCompleteCheckSum32 -= MicrocodeEntryPoint->ProcessorFlags;\r
185 InCompleteCheckSum32 -= MicrocodeEntryPoint->Checksum;\r
186\r
94f63c76
JF
187 if (MicrocodeEntryPoint->HeaderVersion == 0x1) {\r
188 //\r
189 // It is the microcode header. It is not the padding data between microcode patches\r
190 // because the padding data should not include 0x00000001 and it should be the repeated\r
191 // byte format (like 0xXYXYXYXY....).\r
192 //\r
193 if (MicrocodeEntryPoint->ProcessorSignature.Uint32 == Eax.Uint32 &&\r
194 MicrocodeEntryPoint->UpdateRevision > LatestRevision &&\r
195 (MicrocodeEntryPoint->ProcessorFlags & (1 << PlatformId))\r
196 ) {\r
b6f67b4d
CC
197 //\r
198 // Calculate CheckSum Part1.\r
199 //\r
200 CheckSum32 = InCompleteCheckSum32;\r
201 CheckSum32 += MicrocodeEntryPoint->ProcessorSignature.Uint32;\r
202 CheckSum32 += MicrocodeEntryPoint->ProcessorFlags;\r
203 CheckSum32 += MicrocodeEntryPoint->Checksum;\r
94f63c76
JF
204 if (CheckSum32 == 0) {\r
205 CorrectMicrocode = TRUE;\r
2a089134 206 ProcessorFlags = MicrocodeEntryPoint->ProcessorFlags;\r
94f63c76
JF
207 }\r
208 } else if ((MicrocodeEntryPoint->DataSize != 0) &&\r
209 (MicrocodeEntryPoint->UpdateRevision > LatestRevision)) {\r
210 ExtendedTableLength = MicrocodeEntryPoint->TotalSize - (MicrocodeEntryPoint->DataSize +\r
211 sizeof (CPU_MICROCODE_HEADER));\r
212 if (ExtendedTableLength != 0) {\r
213 //\r
214 // Extended Table exist, check if the CPU in support list\r
215 //\r
216 ExtendedTableHeader = (CPU_MICROCODE_EXTENDED_TABLE_HEADER *) ((UINT8 *) (MicrocodeEntryPoint)\r
217 + MicrocodeEntryPoint->DataSize + sizeof (CPU_MICROCODE_HEADER));\r
218 //\r
219 // Calculate Extended Checksum\r
220 //\r
221 if ((ExtendedTableLength % 4) == 0) {\r
b6f67b4d
CC
222 //\r
223 // Calculate CheckSum Part2.\r
224 //\r
94f63c76
JF
225 CheckSum32 = CalculateSum32 ((UINT32 *) ExtendedTableHeader, ExtendedTableLength);\r
226 if (CheckSum32 == 0) {\r
227 //\r
228 // Checksum correct\r
229 //\r
230 ExtendedTableCount = ExtendedTableHeader->ExtendedSignatureCount;\r
231 ExtendedTable = (CPU_MICROCODE_EXTENDED_TABLE *) (ExtendedTableHeader + 1);\r
232 for (Index = 0; Index < ExtendedTableCount; Index ++) {\r
b6f67b4d
CC
233 //\r
234 // Calculate CheckSum Part3.\r
235 //\r
236 CheckSum32 = InCompleteCheckSum32;\r
237 CheckSum32 += ExtendedTable->ProcessorSignature.Uint32;\r
238 CheckSum32 += ExtendedTable->ProcessorFlag;\r
239 CheckSum32 += ExtendedTable->Checksum;\r
94f63c76
JF
240 if (CheckSum32 == 0) {\r
241 //\r
242 // Verify Header\r
243 //\r
244 if ((ExtendedTable->ProcessorSignature.Uint32 == Eax.Uint32) &&\r
245 (ExtendedTable->ProcessorFlag & (1 << PlatformId)) ) {\r
246 //\r
247 // Find one\r
248 //\r
249 CorrectMicrocode = TRUE;\r
2a089134 250 ProcessorFlags = ExtendedTable->ProcessorFlag;\r
94f63c76
JF
251 break;\r
252 }\r
253 }\r
254 ExtendedTable ++;\r
255 }\r
256 }\r
257 }\r
258 }\r
259 }\r
260 } else {\r
261 //\r
262 // It is the padding data between the microcode patches for microcode patches alignment.\r
263 // Because the microcode patch is the multiple of 1-KByte, the padding data should not\r
264 // exist if the microcode patch alignment value is not larger than 1-KByte. So, the microcode\r
265 // alignment value should be larger than 1-KByte. We could skip SIZE_1KB padding data to\r
266 // find the next possible microcode patch header.\r
267 //\r
268 MicrocodeEntryPoint = (CPU_MICROCODE_HEADER *) (((UINTN) MicrocodeEntryPoint) + SIZE_1KB);\r
269 continue;\r
270 }\r
271 //\r
272 // Get the next patch.\r
273 //\r
274 if (MicrocodeEntryPoint->DataSize == 0) {\r
275 TotalSize = 2048;\r
276 } else {\r
277 TotalSize = MicrocodeEntryPoint->TotalSize;\r
278 }\r
279\r
280 if (CorrectMicrocode) {\r
281 LatestRevision = MicrocodeEntryPoint->UpdateRevision;\r
282 MicrocodeData = (VOID *) ((UINTN) MicrocodeEntryPoint + sizeof (CPU_MICROCODE_HEADER));\r
283 }\r
284\r
285 MicrocodeEntryPoint = (CPU_MICROCODE_HEADER *) (((UINTN) MicrocodeEntryPoint) + TotalSize);\r
286 } while (((UINTN) MicrocodeEntryPoint < MicrocodeEnd));\r
287\r
2a089134 288Done:\r
94f63c76
JF
289 if (LatestRevision > CurrentRevision) {\r
290 //\r
291 // BIOS only authenticate updates that contain a numerically larger revision\r
292 // than the currently loaded revision, where Current Signature < New Update\r
293 // Revision. A processor with no loaded update is considered to have a\r
294 // revision equal to zero.\r
295 //\r
8cce3c9a 296 ASSERT (MicrocodeData != NULL);\r
94f63c76
JF
297 AsmWriteMsr64 (\r
298 MSR_IA32_BIOS_UPDT_TRIG,\r
299 (UINT64) (UINTN) MicrocodeData\r
300 );\r
301 //\r
302 // Get and check new microcode signature\r
303 //\r
304 CurrentRevision = GetCurrentMicrocodeSignature ();\r
305 if (CurrentRevision != LatestRevision) {\r
306 AcquireSpinLock(&CpuMpData->MpLock);\r
307 DEBUG ((EFI_D_ERROR, "Updated microcode signature [0x%08x] does not match \\r
308 loaded microcode signature [0x%08x]\n", CurrentRevision, LatestRevision));\r
309 ReleaseSpinLock(&CpuMpData->MpLock);\r
310 }\r
311 }\r
2a089134
ED
312\r
313 if (IsBspCallIn && (LatestRevision != 0)) {\r
314 //\r
315 // Save BSP processor info and microcode info for later AP use.\r
316 //\r
317 CpuMpData->ProcessorSignature = Eax.Uint32;\r
318 CpuMpData->ProcessorFlags = ProcessorFlags;\r
319 CpuMpData->MicrocodeDataAddress = (UINTN) MicrocodeData;\r
320 CpuMpData->MicrocodeRevision = LatestRevision;\r
321 DEBUG ((DEBUG_INFO, "BSP Microcode:: signature [0x%08x], ProcessorFlags [0x%08x], \\r
322 MicroData [0x%08x], Revision [0x%08x]\n", Eax.Uint32, ProcessorFlags, (UINTN) MicrocodeData, LatestRevision));\r
323 }\r
94f63c76 324}\r