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