]> git.proxmox.com Git - mirror_edk2.git/blame_incremental - UefiCpuPkg/Library/MpInitLib/Microcode.c
UefiCpuPkg: Move AsmRelocateApLoopStart from Mpfuncs.nasm to AmdSev.nasm
[mirror_edk2.git] / UefiCpuPkg / Library / MpInitLib / Microcode.c
... / ...
CommitLineData
1/** @file\r
2 Implementation of loading microcode on processors.\r
3\r
4 Copyright (c) 2015 - 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 "MpLib.h"\r
10\r
11/**\r
12 Detect whether specified processor can find matching microcode patch and load it.\r
13\r
14 @param[in] CpuMpData The pointer to CPU MP Data structure.\r
15 @param[in] ProcessorNumber The handle number of the processor. The range is\r
16 from 0 to the total number of logical processors\r
17 minus 1.\r
18**/\r
19VOID\r
20MicrocodeDetect (\r
21 IN CPU_MP_DATA *CpuMpData,\r
22 IN UINTN ProcessorNumber\r
23 )\r
24{\r
25 CPU_MICROCODE_HEADER *Microcode;\r
26 UINTN MicrocodeEnd;\r
27 CPU_AP_DATA *BspData;\r
28 UINT32 LatestRevision;\r
29 CPU_MICROCODE_HEADER *LatestMicrocode;\r
30 UINT32 ThreadId;\r
31 EDKII_PEI_MICROCODE_CPU_ID MicrocodeCpuId;\r
32\r
33 if (CpuMpData->MicrocodePatchRegionSize == 0) {\r
34 //\r
35 // There is no microcode patches\r
36 //\r
37 return;\r
38 }\r
39\r
40 GetProcessorLocationByApicId (GetInitialApicId (), NULL, NULL, &ThreadId);\r
41 if (ThreadId != 0) {\r
42 //\r
43 // Skip loading microcode if it is not the first thread in one core.\r
44 //\r
45 return;\r
46 }\r
47\r
48 GetProcessorMicrocodeCpuId (&MicrocodeCpuId);\r
49\r
50 if (ProcessorNumber != (UINTN)CpuMpData->BspNumber) {\r
51 //\r
52 // Direct use microcode of BSP if AP is the same as BSP.\r
53 // Assume BSP calls this routine() before AP.\r
54 //\r
55 BspData = &(CpuMpData->CpuData[CpuMpData->BspNumber]);\r
56 if ((BspData->ProcessorSignature == MicrocodeCpuId.ProcessorSignature) &&\r
57 (BspData->PlatformId == MicrocodeCpuId.PlatformId) &&\r
58 (BspData->MicrocodeEntryAddr != 0))\r
59 {\r
60 LatestMicrocode = (CPU_MICROCODE_HEADER *)(UINTN)BspData->MicrocodeEntryAddr;\r
61 LatestRevision = LatestMicrocode->UpdateRevision;\r
62 goto LoadMicrocode;\r
63 }\r
64 }\r
65\r
66 //\r
67 // BSP or AP which is different from BSP runs here\r
68 // Use 0 as the starting revision to search for microcode because MicrocodePatchInfo HOB needs\r
69 // the latest microcode location even it's loaded to the processor.\r
70 //\r
71 LatestRevision = 0;\r
72 LatestMicrocode = NULL;\r
73 Microcode = (CPU_MICROCODE_HEADER *)(UINTN)CpuMpData->MicrocodePatchAddress;\r
74 MicrocodeEnd = (UINTN)Microcode + (UINTN)CpuMpData->MicrocodePatchRegionSize;\r
75\r
76 do {\r
77 if (!IsValidMicrocode (Microcode, MicrocodeEnd - (UINTN)Microcode, LatestRevision, &MicrocodeCpuId, 1, TRUE)) {\r
78 //\r
79 // It is the padding data between the microcode patches for microcode patches alignment.\r
80 // Because the microcode patch is the multiple of 1-KByte, the padding data should not\r
81 // exist if the microcode patch alignment value is not larger than 1-KByte. So, the microcode\r
82 // alignment value should be larger than 1-KByte. We could skip SIZE_1KB padding data to\r
83 // find the next possible microcode patch header.\r
84 //\r
85 Microcode = (CPU_MICROCODE_HEADER *)((UINTN)Microcode + SIZE_1KB);\r
86 continue;\r
87 }\r
88\r
89 LatestMicrocode = Microcode;\r
90 LatestRevision = LatestMicrocode->UpdateRevision;\r
91\r
92 Microcode = (CPU_MICROCODE_HEADER *)(((UINTN)Microcode) + GetMicrocodeLength (Microcode));\r
93 } while ((UINTN)Microcode < MicrocodeEnd);\r
94\r
95LoadMicrocode:\r
96 if (LatestRevision != 0) {\r
97 //\r
98 // Save the detected microcode patch entry address (including the microcode\r
99 // patch header) for each processor even it's the same as the loaded one.\r
100 // It will be used when building the microcode patch cache HOB.\r
101 //\r
102 CpuMpData->CpuData[ProcessorNumber].MicrocodeEntryAddr = (UINTN)LatestMicrocode;\r
103 }\r
104\r
105 if (LatestRevision > GetProcessorMicrocodeSignature ()) {\r
106 //\r
107 // BIOS only authenticate updates that contain a numerically larger revision\r
108 // than the currently loaded revision, where Current Signature < New Update\r
109 // Revision. A processor with no loaded update is considered to have a\r
110 // revision equal to zero.\r
111 //\r
112 LoadMicrocode (LatestMicrocode);\r
113 }\r
114\r
115 //\r
116 // It's possible that the microcode fails to load. Just capture the CPU microcode revision after loading.\r
117 //\r
118 CpuMpData->CpuData[ProcessorNumber].MicrocodeRevision = GetProcessorMicrocodeSignature ();\r
119}\r
120\r
121/**\r
122 Actual worker function that shadows the required microcode patches into memory.\r
123\r
124 @param[in, out] CpuMpData The pointer to CPU MP Data structure.\r
125 @param[in] Patches The pointer to an array of information on\r
126 the microcode patches that will be loaded\r
127 into memory.\r
128 @param[in] PatchCount The number of microcode patches that will\r
129 be loaded into memory.\r
130 @param[in] TotalLoadSize The total size of all the microcode patches\r
131 to be loaded.\r
132**/\r
133VOID\r
134ShadowMicrocodePatchWorker (\r
135 IN OUT CPU_MP_DATA *CpuMpData,\r
136 IN MICROCODE_PATCH_INFO *Patches,\r
137 IN UINTN PatchCount,\r
138 IN UINTN TotalLoadSize\r
139 )\r
140{\r
141 UINTN Index;\r
142 VOID *MicrocodePatchInRam;\r
143 UINT8 *Walker;\r
144\r
145 ASSERT ((Patches != NULL) && (PatchCount != 0));\r
146\r
147 MicrocodePatchInRam = AllocatePages (EFI_SIZE_TO_PAGES (TotalLoadSize));\r
148 if (MicrocodePatchInRam == NULL) {\r
149 return;\r
150 }\r
151\r
152 //\r
153 // Load all the required microcode patches into memory\r
154 //\r
155 for (Walker = MicrocodePatchInRam, Index = 0; Index < PatchCount; Index++) {\r
156 CopyMem (\r
157 Walker,\r
158 (VOID *)Patches[Index].Address,\r
159 Patches[Index].Size\r
160 );\r
161 Walker += Patches[Index].Size;\r
162 }\r
163\r
164 //\r
165 // Update the microcode patch related fields in CpuMpData\r
166 //\r
167 CpuMpData->MicrocodePatchAddress = (UINTN)MicrocodePatchInRam;\r
168 CpuMpData->MicrocodePatchRegionSize = TotalLoadSize;\r
169\r
170 DEBUG ((\r
171 DEBUG_INFO,\r
172 "%a: Required microcode patches have been loaded at 0x%lx, with size 0x%lx.\n",\r
173 __FUNCTION__,\r
174 CpuMpData->MicrocodePatchAddress,\r
175 CpuMpData->MicrocodePatchRegionSize\r
176 ));\r
177\r
178 return;\r
179}\r
180\r
181/**\r
182 Shadow the required microcode patches data into memory according to PCD\r
183 PcdCpuMicrocodePatchAddress and PcdCpuMicrocodePatchRegionSize.\r
184\r
185 @param[in, out] CpuMpData The pointer to CPU MP Data structure.\r
186**/\r
187VOID\r
188ShadowMicrocodePatchByPcd (\r
189 IN OUT CPU_MP_DATA *CpuMpData\r
190 )\r
191{\r
192 UINTN Index;\r
193 CPU_MICROCODE_HEADER *MicrocodeEntryPoint;\r
194 UINTN MicrocodeEnd;\r
195 UINTN TotalSize;\r
196 MICROCODE_PATCH_INFO *PatchInfoBuffer;\r
197 UINTN MaxPatchNumber;\r
198 UINTN PatchCount;\r
199 UINTN TotalLoadSize;\r
200 EDKII_PEI_MICROCODE_CPU_ID *MicrocodeCpuIds;\r
201 BOOLEAN Valid;\r
202\r
203 //\r
204 // Initialize the microcode patch related fields in CpuMpData as the values\r
205 // specified by the PCD pair. If the microcode patches are loaded into memory,\r
206 // these fields will be updated.\r
207 //\r
208 CpuMpData->MicrocodePatchAddress = PcdGet64 (PcdCpuMicrocodePatchAddress);\r
209 CpuMpData->MicrocodePatchRegionSize = PcdGet64 (PcdCpuMicrocodePatchRegionSize);\r
210\r
211 MicrocodeEntryPoint = (CPU_MICROCODE_HEADER *)(UINTN)CpuMpData->MicrocodePatchAddress;\r
212 MicrocodeEnd = (UINTN)MicrocodeEntryPoint +\r
213 (UINTN)CpuMpData->MicrocodePatchRegionSize;\r
214 if ((MicrocodeEntryPoint == NULL) || ((UINTN)MicrocodeEntryPoint == MicrocodeEnd)) {\r
215 //\r
216 // There is no microcode patches\r
217 //\r
218 return;\r
219 }\r
220\r
221 PatchCount = 0;\r
222 MaxPatchNumber = DEFAULT_MAX_MICROCODE_PATCH_NUM;\r
223 TotalLoadSize = 0;\r
224 PatchInfoBuffer = AllocatePool (MaxPatchNumber * sizeof (MICROCODE_PATCH_INFO));\r
225 if (PatchInfoBuffer == NULL) {\r
226 return;\r
227 }\r
228\r
229 MicrocodeCpuIds = AllocatePages (\r
230 EFI_SIZE_TO_PAGES (CpuMpData->CpuCount * sizeof (EDKII_PEI_MICROCODE_CPU_ID))\r
231 );\r
232 if (MicrocodeCpuIds == NULL) {\r
233 FreePool (PatchInfoBuffer);\r
234 return;\r
235 }\r
236\r
237 for (Index = 0; Index < CpuMpData->CpuCount; Index++) {\r
238 MicrocodeCpuIds[Index].PlatformId = CpuMpData->CpuData[Index].PlatformId;\r
239 MicrocodeCpuIds[Index].ProcessorSignature = CpuMpData->CpuData[Index].ProcessorSignature;\r
240 }\r
241\r
242 //\r
243 // Process the header of each microcode patch within the region.\r
244 // The purpose is to decide which microcode patch(es) will be loaded into memory.\r
245 // Microcode checksum is not verified because it's slow when performing on flash.\r
246 //\r
247 do {\r
248 Valid = IsValidMicrocode (\r
249 MicrocodeEntryPoint,\r
250 MicrocodeEnd - (UINTN)MicrocodeEntryPoint,\r
251 0,\r
252 MicrocodeCpuIds,\r
253 CpuMpData->CpuCount,\r
254 FALSE\r
255 );\r
256 if (!Valid) {\r
257 //\r
258 // Padding data between the microcode patches, skip 1KB to check next entry.\r
259 //\r
260 MicrocodeEntryPoint = (CPU_MICROCODE_HEADER *)(((UINTN)MicrocodeEntryPoint) + SIZE_1KB);\r
261 continue;\r
262 }\r
263\r
264 PatchCount++;\r
265 if (PatchCount > MaxPatchNumber) {\r
266 //\r
267 // Current 'PatchInfoBuffer' cannot hold the information, double the size\r
268 // and allocate a new buffer.\r
269 //\r
270 if (MaxPatchNumber > MAX_UINTN / 2 / sizeof (MICROCODE_PATCH_INFO)) {\r
271 //\r
272 // Overflow check for MaxPatchNumber\r
273 //\r
274 goto OnExit;\r
275 }\r
276\r
277 PatchInfoBuffer = ReallocatePool (\r
278 MaxPatchNumber * sizeof (MICROCODE_PATCH_INFO),\r
279 2 * MaxPatchNumber * sizeof (MICROCODE_PATCH_INFO),\r
280 PatchInfoBuffer\r
281 );\r
282 if (PatchInfoBuffer == NULL) {\r
283 goto OnExit;\r
284 }\r
285\r
286 MaxPatchNumber = MaxPatchNumber * 2;\r
287 }\r
288\r
289 TotalSize = GetMicrocodeLength (MicrocodeEntryPoint);\r
290\r
291 //\r
292 // Store the information of this microcode patch\r
293 //\r
294 PatchInfoBuffer[PatchCount - 1].Address = (UINTN)MicrocodeEntryPoint;\r
295 PatchInfoBuffer[PatchCount - 1].Size = TotalSize;\r
296 TotalLoadSize += TotalSize;\r
297\r
298 //\r
299 // Process the next microcode patch\r
300 //\r
301 MicrocodeEntryPoint = (CPU_MICROCODE_HEADER *)((UINTN)MicrocodeEntryPoint + TotalSize);\r
302 } while ((UINTN)MicrocodeEntryPoint < MicrocodeEnd);\r
303\r
304 if (PatchCount != 0) {\r
305 DEBUG ((\r
306 DEBUG_INFO,\r
307 "%a: 0x%x microcode patches will be loaded into memory, with size 0x%x.\n",\r
308 __FUNCTION__,\r
309 PatchCount,\r
310 TotalLoadSize\r
311 ));\r
312\r
313 ShadowMicrocodePatchWorker (CpuMpData, PatchInfoBuffer, PatchCount, TotalLoadSize);\r
314 }\r
315\r
316OnExit:\r
317 if (PatchInfoBuffer != NULL) {\r
318 FreePool (PatchInfoBuffer);\r
319 }\r
320\r
321 FreePages (MicrocodeCpuIds, EFI_SIZE_TO_PAGES (CpuMpData->CpuCount * sizeof (EDKII_PEI_MICROCODE_CPU_ID)));\r
322}\r
323\r
324/**\r
325 Shadow the required microcode patches data into memory.\r
326\r
327 @param[in, out] CpuMpData The pointer to CPU MP Data structure.\r
328**/\r
329VOID\r
330ShadowMicrocodeUpdatePatch (\r
331 IN OUT CPU_MP_DATA *CpuMpData\r
332 )\r
333{\r
334 EFI_STATUS Status;\r
335\r
336 Status = PlatformShadowMicrocode (CpuMpData);\r
337 if (EFI_ERROR (Status)) {\r
338 ShadowMicrocodePatchByPcd (CpuMpData);\r
339 }\r
340}\r
341\r
342/**\r
343 Get the cached microcode patch base address and size from the microcode patch\r
344 information cache HOB.\r
345\r
346 @param[out] Address Base address of the microcode patches data.\r
347 It will be updated if the microcode patch\r
348 information cache HOB is found.\r
349 @param[out] RegionSize Size of the microcode patches data.\r
350 It will be updated if the microcode patch\r
351 information cache HOB is found.\r
352\r
353 @retval TRUE The microcode patch information cache HOB is found.\r
354 @retval FALSE The microcode patch information cache HOB is not found.\r
355\r
356**/\r
357BOOLEAN\r
358GetMicrocodePatchInfoFromHob (\r
359 UINT64 *Address,\r
360 UINT64 *RegionSize\r
361 )\r
362{\r
363 EFI_HOB_GUID_TYPE *GuidHob;\r
364 EDKII_MICROCODE_PATCH_HOB *MicrocodePathHob;\r
365\r
366 GuidHob = GetFirstGuidHob (&gEdkiiMicrocodePatchHobGuid);\r
367 if (GuidHob == NULL) {\r
368 DEBUG ((DEBUG_INFO, "%a: Microcode patch cache HOB is not found.\n", __FUNCTION__));\r
369 return FALSE;\r
370 }\r
371\r
372 MicrocodePathHob = GET_GUID_HOB_DATA (GuidHob);\r
373\r
374 *Address = MicrocodePathHob->MicrocodePatchAddress;\r
375 *RegionSize = MicrocodePathHob->MicrocodePatchRegionSize;\r
376\r
377 DEBUG ((\r
378 DEBUG_INFO,\r
379 "%a: MicrocodeBase = 0x%lx, MicrocodeSize = 0x%lx\n",\r
380 __FUNCTION__,\r
381 *Address,\r
382 *RegionSize\r
383 ));\r
384\r
385 return TRUE;\r
386}\r