]> git.proxmox.com Git - mirror_edk2.git/blame - UefiCpuPkg/Library/MicrocodeLib/MicrocodeLib.c
UefiCpuPkg: Move AsmRelocateApLoopStart from Mpfuncs.nasm to AmdSev.nasm
[mirror_edk2.git] / UefiCpuPkg / Library / MicrocodeLib / MicrocodeLib.c
CommitLineData
1a957f17
RN
1/** @file\r
2 Implementation of MicrocodeLib.\r
3\r
4 Copyright (c) 2021, Intel Corporation. All rights reserved.<BR>\r
5 SPDX-License-Identifier: BSD-2-Clause-Patent\r
6\r
7**/\r
8\r
9#include <Uefi/UefiBaseType.h>\r
10#include <Register/Intel/Cpuid.h>\r
11#include <Register/Intel/ArchitecturalMsr.h>\r
12#include <Register/Intel/Microcode.h>\r
13#include <Library/BaseLib.h>\r
14#include <Library/DebugLib.h>\r
15#include <Ppi/ShadowMicrocode.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
23EFIAPI\r
24GetProcessorMicrocodeSignature (\r
25 VOID\r
26 )\r
27{\r
053e878b 28 MSR_IA32_BIOS_SIGN_ID_REGISTER BiosSignIdMsr;\r
1a957f17
RN
29\r
30 AsmWriteMsr64 (MSR_IA32_BIOS_SIGN_ID, 0);\r
31 AsmCpuid (CPUID_VERSION_INFO, NULL, NULL, NULL, NULL);\r
32 BiosSignIdMsr.Uint64 = AsmReadMsr64 (MSR_IA32_BIOS_SIGN_ID);\r
33 return BiosSignIdMsr.Bits.MicrocodeUpdateSignature;\r
34}\r
35\r
36/**\r
37 Get the processor signature and platform ID for current processor.\r
38\r
39 @param MicrocodeCpuId Return the processor signature and platform ID.\r
40**/\r
41VOID\r
42EFIAPI\r
43GetProcessorMicrocodeCpuId (\r
44 EDKII_PEI_MICROCODE_CPU_ID *MicrocodeCpuId\r
45 )\r
46{\r
053e878b 47 MSR_IA32_PLATFORM_ID_REGISTER PlatformIdMsr;\r
1a957f17
RN
48\r
49 ASSERT (MicrocodeCpuId != NULL);\r
50\r
053e878b
MK
51 PlatformIdMsr.Uint64 = AsmReadMsr64 (MSR_IA32_PLATFORM_ID);\r
52 MicrocodeCpuId->PlatformId = (UINT8)PlatformIdMsr.Bits.PlatformId;\r
1a957f17
RN
53 AsmCpuid (CPUID_VERSION_INFO, &MicrocodeCpuId->ProcessorSignature, NULL, NULL, NULL);\r
54}\r
55\r
56/**\r
57 Return the total size of the microcode entry.\r
58\r
59 Logic follows pseudo code in SDM as below:\r
60\r
61 N = 512\r
62 If (Update.DataSize != 00000000H)\r
63 N = Update.TotalSize / 4\r
64\r
65 If Microcode is NULL, then ASSERT.\r
66\r
67 @param Microcode Pointer to the microcode entry.\r
68\r
69 @return The microcode total size.\r
70**/\r
71UINT32\r
72EFIAPI\r
73GetMicrocodeLength (\r
053e878b 74 IN CPU_MICROCODE_HEADER *Microcode\r
1a957f17
RN
75 )\r
76{\r
053e878b 77 UINT32 TotalSize;\r
1a957f17
RN
78\r
79 ASSERT (Microcode != NULL);\r
80\r
81 TotalSize = 2048;\r
82 if (Microcode->DataSize != 0) {\r
83 TotalSize = Microcode->TotalSize;\r
84 }\r
053e878b 85\r
1a957f17
RN
86 return TotalSize;\r
87}\r
88\r
89/**\r
90 Load the microcode to the processor.\r
91\r
92 If Microcode is NULL, then ASSERT.\r
93\r
94 @param Microcode Pointer to the microcode entry.\r
95**/\r
96VOID\r
97EFIAPI\r
98LoadMicrocode (\r
053e878b 99 IN CPU_MICROCODE_HEADER *Microcode\r
1a957f17
RN
100 )\r
101{\r
102 ASSERT (Microcode != NULL);\r
103\r
053e878b 104 AsmWriteMsr64 (MSR_IA32_BIOS_UPDT_TRIG, (UINT64)(UINTN)(Microcode + 1));\r
1a957f17
RN
105}\r
106\r
107/**\r
108 Determine if a microcode patch matchs the specific processor signature and flag.\r
109\r
110 @param[in] ProcessorSignature The processor signature field value in a\r
111 microcode patch.\r
112 @param[in] ProcessorFlags The processor flags field value in a\r
113 microcode patch.\r
114 @param[in] MicrocodeCpuId A pointer to an array of EDKII_PEI_MICROCODE_CPU_ID\r
115 structures.\r
116 @param[in] MicrocodeCpuIdCount Number of elements in MicrocodeCpuId array.\r
117\r
118 @retval TRUE The specified microcode patch matches to one of the MicrocodeCpuId.\r
119 @retval FALSE The specified microcode patch doesn't match to any of the MicrocodeCpuId.\r
120**/\r
121BOOLEAN\r
122IsProcessorMatchedMicrocode (\r
053e878b
MK
123 IN UINT32 ProcessorSignature,\r
124 IN UINT32 ProcessorFlags,\r
125 IN EDKII_PEI_MICROCODE_CPU_ID *MicrocodeCpuId,\r
126 IN UINTN MicrocodeCpuIdCount\r
1a957f17
RN
127 )\r
128{\r
053e878b 129 UINTN Index;\r
1a957f17
RN
130\r
131 if (MicrocodeCpuIdCount == 0) {\r
132 return TRUE;\r
133 }\r
134\r
135 for (Index = 0; Index < MicrocodeCpuIdCount; Index++) {\r
136 if ((ProcessorSignature == MicrocodeCpuId[Index].ProcessorSignature) &&\r
053e878b
MK
137 ((ProcessorFlags & (1 << MicrocodeCpuId[Index].PlatformId)) != 0))\r
138 {\r
1a957f17
RN
139 return TRUE;\r
140 }\r
141 }\r
142\r
143 return FALSE;\r
144}\r
145\r
146/**\r
147 Detect whether specified processor can find matching microcode patch and load it.\r
148\r
149 Microcode format is as below:\r
150 +----------------------------------------+-------------------------------------------------+\r
151 | CPU_MICROCODE_HEADER | |\r
152 +----------------------------------------+ V\r
153 | Update Data | CPU_MICROCODE_HEADER.Checksum\r
154 +----------------------------------------+-------+ ^\r
155 | CPU_MICROCODE_EXTENDED_TABLE_HEADER | | |\r
156 +----------------------------------------+ V |\r
157 | CPU_MICROCODE_EXTENDED_TABLE[0] | CPU_MICROCODE_EXTENDED_TABLE_HEADER.Checksum |\r
158 | CPU_MICROCODE_EXTENDED_TABLE[1] | ^ |\r
159 | ... | | |\r
160 +----------------------------------------+-------+-----------------------------------------+\r
161\r
162 There may by multiple CPU_MICROCODE_EXTENDED_TABLE in this format.\r
163 The count of CPU_MICROCODE_EXTENDED_TABLE is indicated by ExtendedSignatureCount\r
164 of CPU_MICROCODE_EXTENDED_TABLE_HEADER structure.\r
165\r
166 If Microcode is NULL, then ASSERT.\r
167\r
168 @param Microcode Pointer to a microcode entry.\r
169 @param MicrocodeLength The total length of the microcode entry.\r
170 @param MinimumRevision The microcode whose revision <= MinimumRevision is treated as invalid.\r
171 Caller can supply value get from GetProcessorMicrocodeSignature() to check\r
172 whether the microcode is newer than loaded one.\r
173 Caller can supply 0 to treat any revision (except 0) microcode as valid.\r
174 @param MicrocodeCpuIds Pointer to an array of processor signature and platform ID that represents\r
175 a set of processors.\r
176 Caller can supply zero-element array to skip the processor signature and\r
177 platform ID check.\r
178 @param MicrocodeCpuIdCount The number of elements in MicrocodeCpuIds.\r
179 @param VerifyChecksum FALSE to skip all the checksum verifications.\r
180\r
181 @retval TRUE The microcode is valid.\r
182 @retval FALSE The microcode is invalid.\r
183**/\r
184BOOLEAN\r
185EFIAPI\r
186IsValidMicrocode (\r
053e878b
MK
187 IN CPU_MICROCODE_HEADER *Microcode,\r
188 IN UINTN MicrocodeLength,\r
189 IN UINT32 MinimumRevision,\r
190 IN EDKII_PEI_MICROCODE_CPU_ID *MicrocodeCpuIds,\r
191 IN UINTN MicrocodeCpuIdCount,\r
192 IN BOOLEAN VerifyChecksum\r
1a957f17
RN
193 )\r
194{\r
053e878b
MK
195 UINTN Index;\r
196 UINT32 DataSize;\r
197 UINT32 TotalSize;\r
198 CPU_MICROCODE_EXTENDED_TABLE *ExtendedTable;\r
199 CPU_MICROCODE_EXTENDED_TABLE_HEADER *ExtendedTableHeader;\r
200 UINT32 ExtendedTableLength;\r
201 UINT32 Sum32;\r
202 BOOLEAN Match;\r
1a957f17
RN
203\r
204 ASSERT (Microcode != NULL);\r
205\r
206 //\r
207 // It's invalid when:\r
208 // the input microcode buffer is so small that even cannot contain the header.\r
209 // the input microcode buffer is so large that exceeds MAX_ADDRESS.\r
210 //\r
053e878b 211 if ((MicrocodeLength < sizeof (CPU_MICROCODE_HEADER)) || (MicrocodeLength > (MAX_ADDRESS - (UINTN)Microcode))) {\r
1a957f17
RN
212 return FALSE;\r
213 }\r
214\r
215 //\r
216 // Per SDM, HeaderVersion and LoaderRevision should both be 1.\r
217 //\r
218 if ((Microcode->HeaderVersion != 1) || (Microcode->LoaderRevision != 1)) {\r
219 return FALSE;\r
220 }\r
221\r
222 //\r
223 // The microcode revision should be larger than the minimum revision.\r
224 //\r
225 if (Microcode->UpdateRevision <= MinimumRevision) {\r
226 return FALSE;\r
227 }\r
228\r
229 DataSize = Microcode->DataSize;\r
230 if (DataSize == 0) {\r
231 DataSize = 2000;\r
232 }\r
233\r
234 //\r
235 // Per SDM, DataSize should be multiple of DWORDs.\r
236 //\r
237 if ((DataSize % 4) != 0) {\r
238 return FALSE;\r
239 }\r
240\r
241 TotalSize = GetMicrocodeLength (Microcode);\r
242\r
243 //\r
244 // Check whether the whole microcode is within the buffer.\r
245 // TotalSize should be multiple of 1024.\r
246 //\r
247 if (((TotalSize % SIZE_1KB) != 0) || (TotalSize > MicrocodeLength)) {\r
248 return FALSE;\r
249 }\r
250\r
251 //\r
252 // The summation of all DWORDs in microcode should be zero.\r
253 //\r
053e878b 254 if (VerifyChecksum && (CalculateSum32 ((UINT32 *)Microcode, TotalSize) != 0)) {\r
1a957f17
RN
255 return FALSE;\r
256 }\r
257\r
258 Sum32 = Microcode->ProcessorSignature.Uint32 + Microcode->ProcessorFlags + Microcode->Checksum;\r
259\r
260 //\r
261 // Check the processor signature and platform ID in the primary header.\r
262 //\r
263 Match = IsProcessorMatchedMicrocode (\r
264 Microcode->ProcessorSignature.Uint32,\r
265 Microcode->ProcessorFlags,\r
266 MicrocodeCpuIds,\r
267 MicrocodeCpuIdCount\r
268 );\r
269 if (Match) {\r
270 return TRUE;\r
271 }\r
272\r
273 ExtendedTableLength = TotalSize - (DataSize + sizeof (CPU_MICROCODE_HEADER));\r
274 if ((ExtendedTableLength < sizeof (CPU_MICROCODE_EXTENDED_TABLE_HEADER)) || ((ExtendedTableLength % 4) != 0)) {\r
275 return FALSE;\r
276 }\r
053e878b 277\r
1a957f17
RN
278 //\r
279 // Extended Table exist, check if the CPU in support list\r
280 //\r
053e878b 281 ExtendedTableHeader = (CPU_MICROCODE_EXTENDED_TABLE_HEADER *)((UINTN)(Microcode + 1) + DataSize);\r
1a957f17
RN
282 if (ExtendedTableHeader->ExtendedSignatureCount > MAX_UINT32 / sizeof (CPU_MICROCODE_EXTENDED_TABLE)) {\r
283 return FALSE;\r
284 }\r
053e878b 285\r
1a957f17 286 if (ExtendedTableHeader->ExtendedSignatureCount * sizeof (CPU_MICROCODE_EXTENDED_TABLE)\r
053e878b
MK
287 > ExtendedTableLength - sizeof (CPU_MICROCODE_EXTENDED_TABLE_HEADER))\r
288 {\r
1a957f17
RN
289 return FALSE;\r
290 }\r
053e878b 291\r
1a957f17
RN
292 //\r
293 // Check the extended table checksum\r
294 //\r
053e878b 295 if (VerifyChecksum && (CalculateSum32 ((UINT32 *)ExtendedTableHeader, ExtendedTableLength) != 0)) {\r
1a957f17
RN
296 return FALSE;\r
297 }\r
298\r
053e878b
MK
299 ExtendedTable = (CPU_MICROCODE_EXTENDED_TABLE *)(ExtendedTableHeader + 1);\r
300 for (Index = 0; Index < ExtendedTableHeader->ExtendedSignatureCount; Index++) {\r
1a957f17
RN
301 if (VerifyChecksum &&\r
302 (ExtendedTable[Index].ProcessorSignature.Uint32 + ExtendedTable[Index].ProcessorFlag\r
053e878b
MK
303 + ExtendedTable[Index].Checksum != Sum32))\r
304 {\r
1a957f17
RN
305 //\r
306 // The extended table entry is valid when the summation of Processor Signature, Processor Flags\r
307 // and Checksum equal to the coresponding summation from primary header. Because:\r
308 // CalculateSum32 (Header + Update Binary) == 0\r
309 // CalculateSum32 (Header + Update Binary)\r
310 // - (Header.ProcessorSignature + Header.ProcessorFlag + Header.Checksum)\r
311 // + (Extended.ProcessorSignature + Extended.ProcessorFlag + Extended.Checksum) == 0\r
312 // So,\r
313 // (Header.ProcessorSignature + Header.ProcessorFlag + Header.Checksum)\r
314 // == (Extended.ProcessorSignature + Extended.ProcessorFlag + Extended.Checksum)\r
315 //\r
316 continue;\r
317 }\r
053e878b 318\r
1a957f17
RN
319 Match = IsProcessorMatchedMicrocode (\r
320 ExtendedTable[Index].ProcessorSignature.Uint32,\r
321 ExtendedTable[Index].ProcessorFlag,\r
322 MicrocodeCpuIds,\r
323 MicrocodeCpuIdCount\r
324 );\r
325 if (Match) {\r
326 return TRUE;\r
327 }\r
328 }\r
053e878b 329\r
1a957f17
RN
330 return FALSE;\r
331}\r