]> git.proxmox.com Git - mirror_edk2.git/blob - UefiCpuPkg/Library/MpInitLib/Microcode.c
8f4d4da40c76f5b1beab32af3c47e9389ea22585
[mirror_edk2.git] / UefiCpuPkg / Library / MpInitLib / Microcode.c
1 /** @file
2 Implementation of loading microcode on processors.
3
4 Copyright (c) 2015 - 2020, Intel Corporation. All rights reserved.<BR>
5 SPDX-License-Identifier: BSD-2-Clause-Patent
6
7 **/
8
9 #include "MpLib.h"
10
11 /**
12 Get microcode update signature of currently loaded microcode update.
13
14 @return Microcode signature.
15 **/
16 UINT32
17 GetCurrentMicrocodeSignature (
18 VOID
19 )
20 {
21 MSR_IA32_BIOS_SIGN_ID_REGISTER BiosSignIdMsr;
22
23 AsmWriteMsr64 (MSR_IA32_BIOS_SIGN_ID, 0);
24 AsmCpuid (CPUID_VERSION_INFO, NULL, NULL, NULL, NULL);
25 BiosSignIdMsr.Uint64 = AsmReadMsr64 (MSR_IA32_BIOS_SIGN_ID);
26 return BiosSignIdMsr.Bits.MicrocodeUpdateSignature;
27 }
28
29 /**
30 Detect whether specified processor can find matching microcode patch and load it.
31
32 Microcode Payload as the following format:
33 +----------------------------------------+------------------+
34 | CPU_MICROCODE_HEADER | |
35 +----------------------------------------+ CheckSum Part1 |
36 | Microcode Binary | |
37 +----------------------------------------+------------------+
38 | CPU_MICROCODE_EXTENDED_TABLE_HEADER | |
39 +----------------------------------------+ CheckSum Part2 |
40 | CPU_MICROCODE_EXTENDED_TABLE | |
41 | ... | |
42 +----------------------------------------+------------------+
43
44 There may by multiple CPU_MICROCODE_EXTENDED_TABLE in this format.
45 The count of CPU_MICROCODE_EXTENDED_TABLE is indicated by ExtendedSignatureCount
46 of CPU_MICROCODE_EXTENDED_TABLE_HEADER structure.
47
48 When we are trying to verify the CheckSum32 with extended table.
49 We should use the fields of exnteded table to replace the corresponding
50 fields in CPU_MICROCODE_HEADER structure, and recalculate the
51 CheckSum32 with CPU_MICROCODE_HEADER + Microcode Binary. We named
52 it as CheckSum Part3.
53
54 The CheckSum Part2 is used to verify the CPU_MICROCODE_EXTENDED_TABLE_HEADER
55 and CPU_MICROCODE_EXTENDED_TABLE parts. We should make sure CheckSum Part2
56 is correct before we are going to verify each CPU_MICROCODE_EXTENDED_TABLE.
57
58 Only ProcessorSignature, ProcessorFlag and CheckSum are different between
59 CheckSum Part1 and CheckSum Part3. To avoid multiple computing CheckSum Part3.
60 Save an in-complete CheckSum32 from CheckSum Part1 for common parts.
61 When we are going to calculate CheckSum32, just should use the corresponding part
62 of the ProcessorSignature, ProcessorFlag and CheckSum with in-complete CheckSum32.
63
64 Notes: CheckSum32 is not a strong verification.
65 It does not guarantee that the data has not been modified.
66 CPU has its own mechanism to verify Microcode Binary part.
67
68 @param[in] CpuMpData The pointer to CPU MP Data structure.
69 @param[in] ProcessorNumber The handle number of the processor. The range is
70 from 0 to the total number of logical processors
71 minus 1.
72 **/
73 VOID
74 MicrocodeDetect (
75 IN CPU_MP_DATA *CpuMpData,
76 IN UINTN ProcessorNumber
77 )
78 {
79 UINT32 ExtendedTableLength;
80 UINT32 ExtendedTableCount;
81 CPU_MICROCODE_EXTENDED_TABLE *ExtendedTable;
82 CPU_MICROCODE_EXTENDED_TABLE_HEADER *ExtendedTableHeader;
83 CPU_MICROCODE_HEADER *MicrocodeEntryPoint;
84 UINTN MicrocodeEnd;
85 UINTN Index;
86 UINT8 PlatformId;
87 CPUID_VERSION_INFO_EAX Eax;
88 CPU_AP_DATA *CpuData;
89 UINT32 CurrentRevision;
90 UINT32 LatestRevision;
91 UINTN TotalSize;
92 UINT32 CheckSum32;
93 UINT32 InCompleteCheckSum32;
94 BOOLEAN CorrectMicrocode;
95 VOID *MicrocodeData;
96 UINT32 ThreadId;
97 BOOLEAN IsBspCallIn;
98
99 if (CpuMpData->MicrocodePatchRegionSize == 0) {
100 //
101 // There is no microcode patches
102 //
103 return;
104 }
105
106 CurrentRevision = GetCurrentMicrocodeSignature ();
107 IsBspCallIn = (ProcessorNumber == (UINTN)CpuMpData->BspNumber) ? TRUE : FALSE;
108
109 GetProcessorLocationByApicId (GetInitialApicId (), NULL, NULL, &ThreadId);
110 if (ThreadId != 0) {
111 //
112 // Skip loading microcode if it is not the first thread in one core.
113 //
114 return;
115 }
116
117 ExtendedTableLength = 0;
118 Eax.Uint32 = CpuMpData->CpuData[ProcessorNumber].ProcessorSignature;
119 PlatformId = CpuMpData->CpuData[ProcessorNumber].PlatformId;
120
121 //
122 // Check whether AP has same processor with BSP.
123 // If yes, direct use microcode info saved by BSP.
124 //
125 if (!IsBspCallIn) {
126 //
127 // Get the CPU data for BSP
128 //
129 CpuData = &(CpuMpData->CpuData[CpuMpData->BspNumber]);
130 if ((CpuData->ProcessorSignature == Eax.Uint32) &&
131 (CpuData->PlatformId == PlatformId) &&
132 (CpuData->MicrocodeEntryAddr != 0)) {
133 MicrocodeEntryPoint = (CPU_MICROCODE_HEADER *)(UINTN) CpuData->MicrocodeEntryAddr;
134 MicrocodeData = (VOID *) (MicrocodeEntryPoint + 1);
135 LatestRevision = MicrocodeEntryPoint->UpdateRevision;
136 goto Done;
137 }
138 }
139
140 LatestRevision = 0;
141 MicrocodeData = NULL;
142 MicrocodeEnd = (UINTN) (CpuMpData->MicrocodePatchAddress + CpuMpData->MicrocodePatchRegionSize);
143 MicrocodeEntryPoint = (CPU_MICROCODE_HEADER *) (UINTN) CpuMpData->MicrocodePatchAddress;
144
145 do {
146 //
147 // Check if the microcode is for the Cpu and the version is newer
148 // and the update can be processed on the platform
149 //
150 CorrectMicrocode = FALSE;
151
152 if (MicrocodeEntryPoint->DataSize == 0) {
153 TotalSize = sizeof (CPU_MICROCODE_HEADER) + 2000;
154 } else {
155 TotalSize = sizeof (CPU_MICROCODE_HEADER) + MicrocodeEntryPoint->DataSize;
156 }
157
158 ///
159 /// 0x0 MicrocodeBegin MicrocodeEntry MicrocodeEnd 0xffffffff
160 /// |--------------|---------------|---------------|---------------|
161 /// valid TotalSize
162 /// TotalSize is only valid between 0 and (MicrocodeEnd - MicrocodeEntry).
163 /// And it should be aligned with 4 bytes.
164 /// If the TotalSize is invalid, skip 1KB to check next entry.
165 ///
166 if ( (UINTN)MicrocodeEntryPoint > (MAX_ADDRESS - TotalSize) ||
167 ((UINTN)MicrocodeEntryPoint + TotalSize) > MicrocodeEnd ||
168 (TotalSize & 0x3) != 0
169 ) {
170 MicrocodeEntryPoint = (CPU_MICROCODE_HEADER *) (((UINTN) MicrocodeEntryPoint) + SIZE_1KB);
171 continue;
172 }
173
174 //
175 // Save an in-complete CheckSum32 from CheckSum Part1 for common parts.
176 //
177 InCompleteCheckSum32 = CalculateSum32 (
178 (UINT32 *) MicrocodeEntryPoint,
179 TotalSize
180 );
181 InCompleteCheckSum32 -= MicrocodeEntryPoint->ProcessorSignature.Uint32;
182 InCompleteCheckSum32 -= MicrocodeEntryPoint->ProcessorFlags;
183 InCompleteCheckSum32 -= MicrocodeEntryPoint->Checksum;
184
185 if (MicrocodeEntryPoint->HeaderVersion == 0x1) {
186 //
187 // It is the microcode header. It is not the padding data between microcode patches
188 // because the padding data should not include 0x00000001 and it should be the repeated
189 // byte format (like 0xXYXYXYXY....).
190 //
191 if (MicrocodeEntryPoint->ProcessorSignature.Uint32 == Eax.Uint32 &&
192 MicrocodeEntryPoint->UpdateRevision > LatestRevision &&
193 (MicrocodeEntryPoint->ProcessorFlags & (1 << PlatformId))
194 ) {
195 //
196 // Calculate CheckSum Part1.
197 //
198 CheckSum32 = InCompleteCheckSum32;
199 CheckSum32 += MicrocodeEntryPoint->ProcessorSignature.Uint32;
200 CheckSum32 += MicrocodeEntryPoint->ProcessorFlags;
201 CheckSum32 += MicrocodeEntryPoint->Checksum;
202 if (CheckSum32 == 0) {
203 CorrectMicrocode = TRUE;
204 }
205 } else if ((MicrocodeEntryPoint->DataSize != 0) &&
206 (MicrocodeEntryPoint->UpdateRevision > LatestRevision)) {
207 ExtendedTableLength = MicrocodeEntryPoint->TotalSize - (MicrocodeEntryPoint->DataSize +
208 sizeof (CPU_MICROCODE_HEADER));
209 if (ExtendedTableLength != 0) {
210 //
211 // Extended Table exist, check if the CPU in support list
212 //
213 ExtendedTableHeader = (CPU_MICROCODE_EXTENDED_TABLE_HEADER *) ((UINT8 *) (MicrocodeEntryPoint)
214 + MicrocodeEntryPoint->DataSize + sizeof (CPU_MICROCODE_HEADER));
215 //
216 // Calculate Extended Checksum
217 //
218 if ((ExtendedTableLength % 4) == 0) {
219 //
220 // Calculate CheckSum Part2.
221 //
222 CheckSum32 = CalculateSum32 ((UINT32 *) ExtendedTableHeader, ExtendedTableLength);
223 if (CheckSum32 == 0) {
224 //
225 // Checksum correct
226 //
227 ExtendedTableCount = ExtendedTableHeader->ExtendedSignatureCount;
228 ExtendedTable = (CPU_MICROCODE_EXTENDED_TABLE *) (ExtendedTableHeader + 1);
229 for (Index = 0; Index < ExtendedTableCount; Index ++) {
230 //
231 // Calculate CheckSum Part3.
232 //
233 CheckSum32 = InCompleteCheckSum32;
234 CheckSum32 += ExtendedTable->ProcessorSignature.Uint32;
235 CheckSum32 += ExtendedTable->ProcessorFlag;
236 CheckSum32 += ExtendedTable->Checksum;
237 if (CheckSum32 == 0) {
238 //
239 // Verify Header
240 //
241 if ((ExtendedTable->ProcessorSignature.Uint32 == Eax.Uint32) &&
242 (ExtendedTable->ProcessorFlag & (1 << PlatformId)) ) {
243 //
244 // Find one
245 //
246 CorrectMicrocode = TRUE;
247 break;
248 }
249 }
250 ExtendedTable ++;
251 }
252 }
253 }
254 }
255 }
256 } else {
257 //
258 // It is the padding data between the microcode patches for microcode patches alignment.
259 // Because the microcode patch is the multiple of 1-KByte, the padding data should not
260 // exist if the microcode patch alignment value is not larger than 1-KByte. So, the microcode
261 // alignment value should be larger than 1-KByte. We could skip SIZE_1KB padding data to
262 // find the next possible microcode patch header.
263 //
264 MicrocodeEntryPoint = (CPU_MICROCODE_HEADER *) (((UINTN) MicrocodeEntryPoint) + SIZE_1KB);
265 continue;
266 }
267 //
268 // Get the next patch.
269 //
270 if (MicrocodeEntryPoint->DataSize == 0) {
271 TotalSize = 2048;
272 } else {
273 TotalSize = MicrocodeEntryPoint->TotalSize;
274 }
275
276 if (CorrectMicrocode) {
277 LatestRevision = MicrocodeEntryPoint->UpdateRevision;
278 MicrocodeData = (VOID *) ((UINTN) MicrocodeEntryPoint + sizeof (CPU_MICROCODE_HEADER));
279 }
280
281 MicrocodeEntryPoint = (CPU_MICROCODE_HEADER *) (((UINTN) MicrocodeEntryPoint) + TotalSize);
282 } while (((UINTN) MicrocodeEntryPoint < MicrocodeEnd));
283
284 Done:
285 if (LatestRevision != 0) {
286 //
287 // Save the detected microcode patch entry address (including the
288 // microcode patch header) for each processor.
289 // It will be used when building the microcode patch cache HOB.
290 //
291 CpuMpData->CpuData[ProcessorNumber].MicrocodeEntryAddr =
292 (UINTN) MicrocodeData - sizeof (CPU_MICROCODE_HEADER);
293 }
294
295 if (LatestRevision > CurrentRevision) {
296 //
297 // BIOS only authenticate updates that contain a numerically larger revision
298 // than the currently loaded revision, where Current Signature < New Update
299 // Revision. A processor with no loaded update is considered to have a
300 // revision equal to zero.
301 //
302 ASSERT (MicrocodeData != NULL);
303 AsmWriteMsr64 (
304 MSR_IA32_BIOS_UPDT_TRIG,
305 (UINT64) (UINTN) MicrocodeData
306 );
307 //
308 // Get and check new microcode signature
309 //
310 CurrentRevision = GetCurrentMicrocodeSignature ();
311 if (CurrentRevision != LatestRevision) {
312 AcquireSpinLock(&CpuMpData->MpLock);
313 DEBUG ((EFI_D_ERROR, "Updated microcode signature [0x%08x] does not match \
314 loaded microcode signature [0x%08x]\n", CurrentRevision, LatestRevision));
315 ReleaseSpinLock(&CpuMpData->MpLock);
316 }
317 }
318 }
319
320 /**
321 Determine if a microcode patch will be loaded into memory.
322
323 @param[in] CpuMpData The pointer to CPU MP Data structure.
324 @param[in] ProcessorSignature The processor signature field value
325 supported by a microcode patch.
326 @param[in] ProcessorFlags The prcessor flags field value supported by
327 a microcode patch.
328
329 @retval TRUE The specified microcode patch will be loaded.
330 @retval FALSE The specified microcode patch will not be loaded.
331 **/
332 BOOLEAN
333 IsMicrocodePatchNeedLoad (
334 IN CPU_MP_DATA *CpuMpData,
335 IN UINT32 ProcessorSignature,
336 IN UINT32 ProcessorFlags
337 )
338 {
339 UINTN Index;
340 CPU_AP_DATA *CpuData;
341
342 for (Index = 0; Index < CpuMpData->CpuCount; Index++) {
343 CpuData = &CpuMpData->CpuData[Index];
344 if ((ProcessorSignature == CpuData->ProcessorSignature) &&
345 (ProcessorFlags & (1 << CpuData->PlatformId)) != 0) {
346 return TRUE;
347 }
348 }
349
350 return FALSE;
351 }
352
353 /**
354 Actual worker function that loads the required microcode patches into memory.
355
356 @param[in, out] CpuMpData The pointer to CPU MP Data structure.
357 @param[in] Patches The pointer to an array of information on
358 the microcode patches that will be loaded
359 into memory.
360 @param[in] PatchCount The number of microcode patches that will
361 be loaded into memory.
362 @param[in] TotalLoadSize The total size of all the microcode patches
363 to be loaded.
364 **/
365 VOID
366 LoadMicrocodePatchWorker (
367 IN OUT CPU_MP_DATA *CpuMpData,
368 IN MICROCODE_PATCH_INFO *Patches,
369 IN UINTN PatchCount,
370 IN UINTN TotalLoadSize
371 )
372 {
373 UINTN Index;
374 VOID *MicrocodePatchInRam;
375 UINT8 *Walker;
376
377 ASSERT ((Patches != NULL) && (PatchCount != 0));
378
379 MicrocodePatchInRam = AllocatePages (EFI_SIZE_TO_PAGES (TotalLoadSize));
380 if (MicrocodePatchInRam == NULL) {
381 return;
382 }
383
384 //
385 // Load all the required microcode patches into memory
386 //
387 for (Walker = MicrocodePatchInRam, Index = 0; Index < PatchCount; Index++) {
388 CopyMem (
389 Walker,
390 (VOID *) Patches[Index].Address,
391 Patches[Index].Size
392 );
393
394 Walker += Patches[Index].Size;
395 }
396
397 //
398 // Update the microcode patch related fields in CpuMpData
399 //
400 CpuMpData->MicrocodePatchAddress = (UINTN) MicrocodePatchInRam;
401 CpuMpData->MicrocodePatchRegionSize = TotalLoadSize;
402
403 DEBUG ((
404 DEBUG_INFO,
405 "%a: Required microcode patches have been loaded at 0x%lx, with size 0x%lx.\n",
406 __FUNCTION__, CpuMpData->MicrocodePatchAddress, CpuMpData->MicrocodePatchRegionSize
407 ));
408
409 return;
410 }
411
412 /**
413 Load the required microcode patches data into memory.
414
415 @param[in, out] CpuMpData The pointer to CPU MP Data structure.
416 **/
417 VOID
418 LoadMicrocodePatch (
419 IN OUT CPU_MP_DATA *CpuMpData
420 )
421 {
422 CPU_MICROCODE_HEADER *MicrocodeEntryPoint;
423 UINTN MicrocodeEnd;
424 UINTN DataSize;
425 UINTN TotalSize;
426 CPU_MICROCODE_EXTENDED_TABLE_HEADER *ExtendedTableHeader;
427 UINT32 ExtendedTableCount;
428 CPU_MICROCODE_EXTENDED_TABLE *ExtendedTable;
429 MICROCODE_PATCH_INFO *PatchInfoBuffer;
430 UINTN MaxPatchNumber;
431 UINTN PatchCount;
432 UINTN TotalLoadSize;
433 UINTN Index;
434 BOOLEAN NeedLoad;
435
436 //
437 // Initialize the microcode patch related fields in CpuMpData as the values
438 // specified by the PCD pair. If the microcode patches are loaded into memory,
439 // these fields will be updated.
440 //
441 CpuMpData->MicrocodePatchAddress = PcdGet64 (PcdCpuMicrocodePatchAddress);
442 CpuMpData->MicrocodePatchRegionSize = PcdGet64 (PcdCpuMicrocodePatchRegionSize);
443
444 MicrocodeEntryPoint = (CPU_MICROCODE_HEADER *) (UINTN) CpuMpData->MicrocodePatchAddress;
445 MicrocodeEnd = (UINTN) MicrocodeEntryPoint +
446 (UINTN) CpuMpData->MicrocodePatchRegionSize;
447 if ((MicrocodeEntryPoint == NULL) || ((UINTN) MicrocodeEntryPoint == MicrocodeEnd)) {
448 //
449 // There is no microcode patches
450 //
451 return;
452 }
453
454 PatchCount = 0;
455 MaxPatchNumber = DEFAULT_MAX_MICROCODE_PATCH_NUM;
456 TotalLoadSize = 0;
457 PatchInfoBuffer = AllocatePool (MaxPatchNumber * sizeof (MICROCODE_PATCH_INFO));
458 if (PatchInfoBuffer == NULL) {
459 return;
460 }
461
462 //
463 // Process the header of each microcode patch within the region.
464 // The purpose is to decide which microcode patch(es) will be loaded into memory.
465 //
466 do {
467 if (MicrocodeEntryPoint->HeaderVersion != 0x1) {
468 //
469 // Padding data between the microcode patches, skip 1KB to check next entry.
470 //
471 MicrocodeEntryPoint = (CPU_MICROCODE_HEADER *) (((UINTN) MicrocodeEntryPoint) + SIZE_1KB);
472 continue;
473 }
474
475 DataSize = MicrocodeEntryPoint->DataSize;
476 TotalSize = (DataSize == 0) ? 2048 : MicrocodeEntryPoint->TotalSize;
477 if ( (UINTN)MicrocodeEntryPoint > (MAX_ADDRESS - TotalSize) ||
478 ((UINTN)MicrocodeEntryPoint + TotalSize) > MicrocodeEnd ||
479 (DataSize & 0x3) != 0 ||
480 (TotalSize & (SIZE_1KB - 1)) != 0 ||
481 TotalSize < DataSize
482 ) {
483 //
484 // Not a valid microcode header, skip 1KB to check next entry.
485 //
486 MicrocodeEntryPoint = (CPU_MICROCODE_HEADER *) (((UINTN) MicrocodeEntryPoint) + SIZE_1KB);
487 continue;
488 }
489
490 //
491 // Check the 'ProcessorSignature' and 'ProcessorFlags' of the microcode
492 // patch header with the CPUID and PlatformID of the processors within
493 // system to decide if it will be copied into memory
494 //
495 NeedLoad = IsMicrocodePatchNeedLoad (
496 CpuMpData,
497 MicrocodeEntryPoint->ProcessorSignature.Uint32,
498 MicrocodeEntryPoint->ProcessorFlags
499 );
500
501 //
502 // If the Extended Signature Table exists, check if the processor is in the
503 // support list
504 //
505 if ((!NeedLoad) && (DataSize != 0) &&
506 (TotalSize - DataSize > sizeof (CPU_MICROCODE_HEADER) +
507 sizeof (CPU_MICROCODE_EXTENDED_TABLE_HEADER))) {
508 ExtendedTableHeader = (CPU_MICROCODE_EXTENDED_TABLE_HEADER *) ((UINT8 *) (MicrocodeEntryPoint)
509 + DataSize + sizeof (CPU_MICROCODE_HEADER));
510 ExtendedTableCount = ExtendedTableHeader->ExtendedSignatureCount;
511 ExtendedTable = (CPU_MICROCODE_EXTENDED_TABLE *) (ExtendedTableHeader + 1);
512
513 for (Index = 0; Index < ExtendedTableCount; Index ++) {
514 //
515 // Avoid access content beyond MicrocodeEnd
516 //
517 if ((UINTN) ExtendedTable > MicrocodeEnd - sizeof (CPU_MICROCODE_EXTENDED_TABLE)) {
518 break;
519 }
520
521 //
522 // Check the 'ProcessorSignature' and 'ProcessorFlag' of the Extended
523 // Signature Table entry with the CPUID and PlatformID of the processors
524 // within system to decide if it will be copied into memory
525 //
526 NeedLoad = IsMicrocodePatchNeedLoad (
527 CpuMpData,
528 ExtendedTable->ProcessorSignature.Uint32,
529 ExtendedTable->ProcessorFlag
530 );
531 if (NeedLoad) {
532 break;
533 }
534 ExtendedTable ++;
535 }
536 }
537
538 if (NeedLoad) {
539 PatchCount++;
540 if (PatchCount > MaxPatchNumber) {
541 //
542 // Current 'PatchInfoBuffer' cannot hold the information, double the size
543 // and allocate a new buffer.
544 //
545 if (MaxPatchNumber > MAX_UINTN / 2 / sizeof (MICROCODE_PATCH_INFO)) {
546 //
547 // Overflow check for MaxPatchNumber
548 //
549 goto OnExit;
550 }
551
552 PatchInfoBuffer = ReallocatePool (
553 MaxPatchNumber * sizeof (MICROCODE_PATCH_INFO),
554 2 * MaxPatchNumber * sizeof (MICROCODE_PATCH_INFO),
555 PatchInfoBuffer
556 );
557 if (PatchInfoBuffer == NULL) {
558 goto OnExit;
559 }
560 MaxPatchNumber = MaxPatchNumber * 2;
561 }
562
563 //
564 // Store the information of this microcode patch
565 //
566 PatchInfoBuffer[PatchCount - 1].Address = (UINTN) MicrocodeEntryPoint;
567 PatchInfoBuffer[PatchCount - 1].Size = TotalSize;
568 TotalLoadSize += TotalSize;
569 }
570
571 //
572 // Process the next microcode patch
573 //
574 MicrocodeEntryPoint = (CPU_MICROCODE_HEADER *) (((UINTN) MicrocodeEntryPoint) + TotalSize);
575 } while (((UINTN) MicrocodeEntryPoint < MicrocodeEnd));
576
577 if (PatchCount != 0) {
578 DEBUG ((
579 DEBUG_INFO,
580 "%a: 0x%x microcode patches will be loaded into memory, with size 0x%x.\n",
581 __FUNCTION__, PatchCount, TotalLoadSize
582 ));
583
584 LoadMicrocodePatchWorker (CpuMpData, PatchInfoBuffer, PatchCount, TotalLoadSize);
585 }
586
587 OnExit:
588 if (PatchInfoBuffer != NULL) {
589 FreePool (PatchInfoBuffer);
590 }
591 return;
592 }