summaryrefslogtreecommitdiffstats
path: root/UefiCpuPkg/MicrocodeMeasurementDxe/MicrocodeMeasurementDxe.c
blob: 5fd3b3365c70acedeac0f4682005d11efd9ac837 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
/** @file
  This driver measures microcode patches to TPM.

  This driver consumes gEdkiiMicrocodePatchHobGuid, packs all unique microcode patch found in gEdkiiMicrocodePatchHobGuid to a binary blob, and measures the binary blob to TPM.

  Copyright (c) 2021, Intel Corporation. All rights reserved.<BR>
  SPDX-License-Identifier: BSD-2-Clause-Patent

**/

#include <IndustryStandard/UefiTcgPlatform.h>
#include <Guid/EventGroup.h>
#include <Guid/MicrocodePatchHob.h>
#include <Library/DebugLib.h>
#include <Library/UefiDriverEntryPoint.h>
#include <Library/UefiLib.h>
#include <Library/BaseLib.h>
#include <Library/BaseMemoryLib.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/HobLib.h>
#include <Library/MicrocodeLib.h>
#include <Library/TpmMeasurementLib.h>

#define CPU_MICROCODE_MEASUREMENT_DESCRIPTION                "Microcode Measurement"
#define CPU_MICROCODE_MEASUREMENT_EVENT_LOG_DESCRIPTION_LEN  sizeof (CPU_MICROCODE_MEASUREMENT_DESCRIPTION)

#pragma pack(1)
typedef struct {
  UINT8    Description[CPU_MICROCODE_MEASUREMENT_EVENT_LOG_DESCRIPTION_LEN];
  UINTN    NumberOfMicrocodePatchesMeasured;
  UINTN    SizeOfMicrocodePatchesMeasured;
} CPU_MICROCODE_MEASUREMENT_EVENT_LOG;
#pragma pack()

/**
  Helping function.

  The function is called by QuickSort to compare the order of offsets of
  two microcode patches in RAM relative to their base address. Elements
  will be in ascending order.

  @param[in] Offset1   The pointer to the offset of first microcode patch.
  @param[in] Offset2   The pointer to the offset of second microcode patch.

  @retval 1                   The offset of first microcode patch is bigger than that of the second.
  @retval -1                  The offset of first microcode patch is smaller than that of the second.
  @retval 0                   The offset of first microcode patch equals to that of the second.
**/
INTN
EFIAPI
MicrocodePatchOffsetCompareFunction (
  IN CONST VOID  *Offset1,
  IN CONST VOID  *Offset2
  )
{
  if (*(UINT64 *)(Offset1) > *(UINT64 *)(Offset2)) {
    return 1;
  } else if (*(UINT64 *)(Offset1) < *(UINT64 *)(Offset2)) {
    return -1;
  } else {
    return 0;
  }
}

/**
  This function remove duplicate and invalid offsets in Offsets.

  This function remove duplicate and invalid offsets in Offsets. Invalid offset means MAX_UINT64 in Offsets.

  @param[in] Offsets        Microcode offset list.
  @param[in, out] Count          On call as the count of raw microcode offset list; On return as count of the clean microcode offset list.
  **/
VOID
RemoveDuplicateAndInvalidOffset (
  IN     UINT64  *Offsets,
  IN OUT UINTN   *Count
  )
{
  UINTN   Index;
  UINTN   NewCount;
  UINT64  LastOffset;
  UINT64  QuickSortBuffer;

  //
  // The order matters when packing all applied microcode patches to a single binary blob.
  // Therefore it is a must to do sorting before packing.
  // NOTE: Since microcode patches are sorted by their addresses in memory, the order of
  // addresses in memory of all the microcode patches before sorting is required to be the
  // same in every boot flow. If any future updates made this assumption untenable, then
  // there needs a new solution to measure microcode patches.
  //
  QuickSort (
    Offsets,
    *Count,
    sizeof (UINT64),
    MicrocodePatchOffsetCompareFunction,
    (VOID *)&QuickSortBuffer
    );

  NewCount   = 0;
  LastOffset = MAX_UINT64;
  for (Index = 0; Index < *Count; Index++) {
    //
    // When MAX_UINT64 element is met, all following elements are MAX_UINT64.
    //
    if (Offsets[Index] == MAX_UINT64) {
      break;
    }

    //
    // Remove duplicated offsets
    //
    if (Offsets[Index] != LastOffset) {
      LastOffset        = Offsets[Index];
      Offsets[NewCount] = Offsets[Index];
      NewCount++;
    }
  }

  *Count = NewCount;
}

/**
  Callback function.

  Called after signaling of the Ready to Boot Event. Measure microcode patches binary blob with event type EV_CPU_MICROCODE to PCR[1] in TPM.

  @param[in] Event      Event whose notification function is being invoked.
  @param[in] Context    Pointer to the notification function's context.

**/
VOID
EFIAPI
MeasureMicrocodePatches (
  IN      EFI_EVENT  Event,
  IN      VOID       *Context
  )
{
  EFI_STATUS                           Status;
  UINT32                               PCRIndex;
  UINT32                               EventType;
  CPU_MICROCODE_MEASUREMENT_EVENT_LOG  EventLog;
  UINT32                               EventLogSize;
  EFI_HOB_GUID_TYPE                    *GuidHob;
  EDKII_MICROCODE_PATCH_HOB            *MicrocodePatchHob;
  UINT64                               *Offsets;
  UINTN                                Count;
  UINTN                                Index;
  UINTN                                TotalMicrocodeSize;
  UINT8                                *MicrocodePatchesBlob;

  PCRIndex  = 1;
  EventType = EV_CPU_MICROCODE;
  AsciiStrCpyS (
    (CHAR8 *)(EventLog.Description),
    CPU_MICROCODE_MEASUREMENT_EVENT_LOG_DESCRIPTION_LEN,
    CPU_MICROCODE_MEASUREMENT_DESCRIPTION
    );
  EventLog.NumberOfMicrocodePatchesMeasured = 0;
  EventLog.SizeOfMicrocodePatchesMeasured   = 0;
  EventLogSize                              = sizeof (CPU_MICROCODE_MEASUREMENT_EVENT_LOG);
  Offsets                                   = NULL;
  TotalMicrocodeSize                        = 0;
  Count                                     = 0;

  GuidHob = GetFirstGuidHob (&gEdkiiMicrocodePatchHobGuid);
  if (NULL == GuidHob) {
    DEBUG ((DEBUG_ERROR, "ERROR: GetFirstGuidHob (&gEdkiiMicrocodePatchHobGuid) failed.\n"));
    return;
  }

  MicrocodePatchHob = GET_GUID_HOB_DATA (GuidHob);
  DEBUG (
    (DEBUG_INFO,
     "INFO: Got MicrocodePatchHob with microcode patches starting address:0x%x, microcode patches region size:0x%x, processor count:0x%x\n",
     MicrocodePatchHob->MicrocodePatchAddress, MicrocodePatchHob->MicrocodePatchRegionSize,
     MicrocodePatchHob->ProcessorCount)
    );

  Offsets = AllocateCopyPool (
              MicrocodePatchHob->ProcessorCount * sizeof (UINT64),
              MicrocodePatchHob->ProcessorSpecificPatchOffset
              );
  Count = MicrocodePatchHob->ProcessorCount;

  RemoveDuplicateAndInvalidOffset (Offsets, &Count);

  if (0 == Count) {
    DEBUG ((DEBUG_INFO, "INFO: No microcode patch is ever applied, skip the measurement of microcode!\n"));
    FreePool (Offsets);
    return;
  }

  for (Index = 0; Index < Count; Index++) {
    TotalMicrocodeSize +=
      GetMicrocodeLength ((CPU_MICROCODE_HEADER *)((UINTN)(MicrocodePatchHob->MicrocodePatchAddress + Offsets[Index])));
  }

  EventLog.NumberOfMicrocodePatchesMeasured = Count;
  EventLog.SizeOfMicrocodePatchesMeasured   = TotalMicrocodeSize;

  MicrocodePatchesBlob = AllocateZeroPool (TotalMicrocodeSize);
  if (NULL == MicrocodePatchesBlob) {
    DEBUG ((DEBUG_ERROR, "ERROR: AllocateZeroPool to MicrocodePatchesBlob failed!\n"));
    FreePool (Offsets);
    return;
  }

  TotalMicrocodeSize = 0;
  for (Index = 0; Index < Count; Index++) {
    CopyMem (
      (VOID *)(MicrocodePatchesBlob + TotalMicrocodeSize),
      (VOID *)((UINTN)(MicrocodePatchHob->MicrocodePatchAddress + Offsets[Index])),
      (UINTN)(GetMicrocodeLength (
                (CPU_MICROCODE_HEADER *)((UINTN)(MicrocodePatchHob->MicrocodePatchAddress +
                                                 Offsets[Index]))
                ))
      );
    TotalMicrocodeSize +=
      GetMicrocodeLength ((CPU_MICROCODE_HEADER *)((UINTN)(MicrocodePatchHob->MicrocodePatchAddress + Offsets[Index])));
  }

  Status = TpmMeasureAndLogData (
             PCRIndex,                                 // PCRIndex
             EventType,                                // EventType
             &EventLog,                                // EventLog
             EventLogSize,                             // LogLen
             MicrocodePatchesBlob,                     // HashData
             TotalMicrocodeSize                        // HashDataLen
             );
  if (!EFI_ERROR (Status)) {
    gBS->CloseEvent (Event);
    DEBUG (
      (DEBUG_INFO,
       "INFO: %d Microcode patches are successfully extended to TPM! The total size measured to TPM is 0x%x\n",
       Count,
       TotalMicrocodeSize)
      );
  } else {
    DEBUG ((DEBUG_ERROR, "ERROR: TpmMeasureAndLogData failed with status %r!\n", Status));
  }

  FreePool (Offsets);
  FreePool (MicrocodePatchesBlob);
  return;
}

/**

  Driver to produce microcode measurement.

  Driver to produce microcode measurement. Which install a callback function on ready to boot event.

  @param ImageHandle     Module's image handle
  @param SystemTable     Pointer of EFI_SYSTEM_TABLE

  @return EFI_SUCCESS     This function always complete successfully.

**/
EFI_STATUS
EFIAPI
MicrocodeMeasurementDriverEntryPoint (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
  EFI_EVENT  Event;

  //
  // Measure Microcode patches
  //
  EfiCreateEventReadyToBootEx (
    TPL_CALLBACK,
    MeasureMicrocodePatches,
    NULL,
    &Event
    );

  return EFI_SUCCESS;
}