1 /** @file
2   Implementation of loading microcode on processors.
3 
4   Copyright (c) 2015, Intel Corporation. All rights reserved.<BR>
5   This program and the accompanying materials
6   are licensed and made available under the terms and conditions of the BSD License
7   which accompanies this distribution.  The full text of the license may be found at
8   http://opensource.org/licenses/bsd-license.php
9 
10   THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11   WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
12 
13 **/
14 
15 #include "CpuMpPei.h"
16 
17 /**
18   Get microcode update signature of currently loaded microcode update.
19 
20   @return  Microcode signature.
21 
22 **/
23 UINT32
GetCurrentMicrocodeSignature(VOID)24 GetCurrentMicrocodeSignature (
25   VOID
26   )
27 {
28   UINT64 Signature;
29 
30   AsmWriteMsr64 (EFI_MSR_IA32_BIOS_SIGN_ID, 0);
31   AsmCpuid (CPUID_VERSION_INFO, NULL, NULL, NULL, NULL);
32   Signature = AsmReadMsr64 (EFI_MSR_IA32_BIOS_SIGN_ID);
33   return (UINT32) RShiftU64 (Signature, 32);
34 }
35 
36 /**
37   Detect whether specified processor can find matching microcode patch and load it.
38 
39 **/
40 VOID
MicrocodeDetect(VOID)41 MicrocodeDetect (
42   VOID
43   )
44 {
45   UINT64                                  MicrocodePatchAddress;
46   UINT64                                  MicrocodePatchRegionSize;
47   UINT32                                  ExtendedTableLength;
48   UINT32                                  ExtendedTableCount;
49   EFI_CPU_MICROCODE_EXTENDED_TABLE        *ExtendedTable;
50   EFI_CPU_MICROCODE_EXTENDED_TABLE_HEADER *ExtendedTableHeader;
51   EFI_CPU_MICROCODE_HEADER                *MicrocodeEntryPoint;
52   UINTN                                   MicrocodeEnd;
53   UINTN                                   Index;
54   UINT8                                   PlatformId;
55   UINT32                                  RegEax;
56   UINT32                                  LatestRevision;
57   UINTN                                   TotalSize;
58   UINT32                                  CheckSum32;
59   BOOLEAN                                 CorrectMicrocode;
60   INT32                                   CurrentSignature;
61   MICROCODE_INFO                          MicrocodeInfo;
62 
63   ZeroMem (&MicrocodeInfo, sizeof (MICROCODE_INFO));
64   MicrocodePatchAddress    = PcdGet64 (PcdCpuMicrocodePatchAddress);
65   MicrocodePatchRegionSize = PcdGet64 (PcdCpuMicrocodePatchRegionSize);
66   if (MicrocodePatchRegionSize == 0) {
67     //
68     // There is no microcode patches
69     //
70     return;
71   }
72 
73   ExtendedTableLength = 0;
74   //
75   // Here data of CPUID leafs have not been collected into context buffer, so
76   // GetProcessorCpuid() cannot be used here to retrieve CPUID data.
77   //
78   AsmCpuid (CPUID_VERSION_INFO, &RegEax, NULL, NULL, NULL);
79 
80   //
81   // The index of platform information resides in bits 50:52 of MSR IA32_PLATFORM_ID
82   //
83   PlatformId = (UINT8) AsmMsrBitFieldRead64 (EFI_MSR_IA32_PLATFORM_ID, 50, 52);
84 
85   LatestRevision = 0;
86   MicrocodeEnd = (UINTN) (MicrocodePatchAddress + MicrocodePatchRegionSize);
87   MicrocodeEntryPoint = (EFI_CPU_MICROCODE_HEADER *) (UINTN) MicrocodePatchAddress;
88   do {
89     //
90     // Check if the microcode is for the Cpu and the version is newer
91     // and the update can be processed on the platform
92     //
93     CorrectMicrocode = FALSE;
94     if (MicrocodeEntryPoint->HeaderVersion == 0x1) {
95       //
96       // It is the microcode header. It is not the padding data between microcode patches
97       // becasue the padding data should not include 0x00000001 and it should be the repeated
98       // byte format (like 0xXYXYXYXY....).
99       //
100       if (MicrocodeEntryPoint->ProcessorId == RegEax &&
101           MicrocodeEntryPoint->UpdateRevision > LatestRevision &&
102           (MicrocodeEntryPoint->ProcessorFlags & (1 << PlatformId))
103           ) {
104         if (MicrocodeEntryPoint->DataSize == 0) {
105           CheckSum32 = CalculateSum32 ((UINT32 *)MicrocodeEntryPoint, 2048);
106         } else {
107           CheckSum32 = CalculateSum32 ((UINT32 *)MicrocodeEntryPoint, MicrocodeEntryPoint->DataSize + sizeof(EFI_CPU_MICROCODE_HEADER));
108         }
109         if (CheckSum32 == 0) {
110           CorrectMicrocode = TRUE;
111         }
112       } else if ((MicrocodeEntryPoint->DataSize != 0) &&
113                  (MicrocodeEntryPoint->UpdateRevision > LatestRevision)) {
114         ExtendedTableLength = MicrocodeEntryPoint->TotalSize - (MicrocodeEntryPoint->DataSize + sizeof (EFI_CPU_MICROCODE_HEADER));
115         if (ExtendedTableLength != 0) {
116           //
117           // Extended Table exist, check if the CPU in support list
118           //
119           ExtendedTableHeader = (EFI_CPU_MICROCODE_EXTENDED_TABLE_HEADER *)((UINT8 *)(MicrocodeEntryPoint) + MicrocodeEntryPoint->DataSize + sizeof (EFI_CPU_MICROCODE_HEADER));
120           //
121           // Calculate Extended Checksum
122           //
123           if ((ExtendedTableLength % 4) == 0) {
124             CheckSum32 = CalculateSum32 ((UINT32 *)ExtendedTableHeader, ExtendedTableLength);
125             if (CheckSum32 == 0) {
126               //
127               // Checksum correct
128               //
129               ExtendedTableCount = ExtendedTableHeader->ExtendedSignatureCount;
130               ExtendedTable      = (EFI_CPU_MICROCODE_EXTENDED_TABLE *)(ExtendedTableHeader + 1);
131               for (Index = 0; Index < ExtendedTableCount; Index ++) {
132                 CheckSum32 = CalculateSum32 ((UINT32 *)ExtendedTable, sizeof(EFI_CPU_MICROCODE_EXTENDED_TABLE));
133                 if (CheckSum32 == 0) {
134                   //
135                   // Verify Header
136                   //
137                   if ((ExtendedTable->ProcessorSignature == RegEax) &&
138                       (ExtendedTable->ProcessorFlag & (1 << PlatformId)) ) {
139                     //
140                     // Find one
141                     //
142                     CorrectMicrocode = TRUE;
143                     break;
144                   }
145                 }
146                 ExtendedTable ++;
147               }
148             }
149           }
150         }
151       }
152     } else {
153       //
154       // It is the padding data between the microcode patches for microcode patches alignment.
155       // Because the microcode patch is the multiple of 1-KByte, the padding data should not
156       // exist if the microcode patch alignment value is not larger than 1-KByte. So, the microcode
157       // alignment value should be larger than 1-KByte. We could skip SIZE_1KB padding data to
158       // find the next possible microcode patch header.
159       //
160       MicrocodeEntryPoint = (EFI_CPU_MICROCODE_HEADER *) (((UINTN) MicrocodeEntryPoint) + SIZE_1KB);
161       continue;
162     }
163     //
164     // Get the next patch.
165     //
166     if (MicrocodeEntryPoint->DataSize == 0) {
167       TotalSize = 2048;
168     } else {
169       TotalSize = MicrocodeEntryPoint->TotalSize;
170     }
171 
172     if (CorrectMicrocode) {
173       LatestRevision = MicrocodeEntryPoint->UpdateRevision;
174       MicrocodeInfo.MicrocodeData = (VOID *)((UINTN)MicrocodeEntryPoint + sizeof (EFI_CPU_MICROCODE_HEADER));
175       MicrocodeInfo.MicrocodeSize = TotalSize;
176       MicrocodeInfo.ProcessorId = RegEax;
177     }
178 
179     MicrocodeEntryPoint = (EFI_CPU_MICROCODE_HEADER *) (((UINTN) MicrocodeEntryPoint) + TotalSize);
180   } while (((UINTN) MicrocodeEntryPoint < MicrocodeEnd));
181 
182   if (LatestRevision > 0) {
183     //
184     // Get microcode update signature of currently loaded microcode update
185     //
186     CurrentSignature = GetCurrentMicrocodeSignature ();
187     //
188     // If no microcode update has been loaded, then trigger microcode load.
189     //
190     if (CurrentSignature == 0) {
191       AsmWriteMsr64 (
192         EFI_MSR_IA32_BIOS_UPDT_TRIG,
193         (UINT64) (UINTN) MicrocodeInfo.MicrocodeData
194         );
195       MicrocodeInfo.Load = TRUE;
196     } else {
197       MicrocodeInfo.Load = FALSE;
198     }
199   }
200 }
201