:: limine / common / lib / tpm.c 11.9 KB raw

1
#if defined (UEFI)
2
3
#include <stdint.h>
4
#include <stddef.h>
5
#include <stdbool.h>
6
#include <efi.h>
7
#include <efi/protocol/efitcg2.h>
8
#include <efi/protocol/eficc.h>
9
#include <lib/tpm.h>
10
#include <lib/misc.h>
11
#include <lib/print.h>
12
#include <lib/libc.h>
13
#include <mm/pmm.h>
14
15
// TCG event log entry layouts (TCG PC Client Platform Firmware Profile).
16
struct tpm_pcr_event_v1_2 {
17
    uint32_t pcr_idx;
18
    uint32_t event_type;
19
    uint8_t  digest[20];
20
    uint32_t event_size;
21
    uint8_t  event[];
22
} __attribute__((packed));
23
24
struct tpm_specid_event_alg {
25
    uint16_t alg_id;
26
    uint16_t digest_size;
27
} __attribute__((packed));
28
29
struct tpm_specid_event_head {
30
    uint8_t  signature[16];
31
    uint32_t platform_class;
32
    uint8_t  spec_version_minor;
33
    uint8_t  spec_version_major;
34
    uint8_t  spec_errata;
35
    uint8_t  uintn_size;
36
    uint32_t num_algs;
37
    struct tpm_specid_event_alg digest_sizes[];
38
} __attribute__((packed));
39
40
// Followed by `count` digests (uint16_t alg_id + variable-length digest),
41
// then a uint32_t event_size and event_size bytes of event data.
42
struct tpm_pcr_event2_head {
43
    uint32_t pcr_idx;
44
    uint32_t event_type;
45
    uint32_t count;
46
} __attribute__((packed));
47
48
#define TCG_EV_NO_ACTION 3
49
#define TCG_SPECID_SIG   "Spec ID Event03"
50
51
// At most one of these is non-NULL after tpm_init. tcg2 takes precedence
52
// since it's the more common case (real TPMs); the cc fallback is for
53
// confidential-computing platforms (TDX, SEV-SNP) without a discrete TPM.
54
static EFI_TCG2_PROTOCOL *tcg2 = NULL;
55
static EFI_CC_MEASUREMENT_PROTOCOL *cc = NULL;
56
57
void tpm_init(void) {
58
    EFI_GUID tcg2_guid = EFI_TCG2_PROTOCOL_GUID;
59
    EFI_TCG2_PROTOCOL *tcg2_proto = NULL;
60
    EFI_STATUS status = gBS->LocateProtocol(&tcg2_guid, NULL, (void **)&tcg2_proto);
61
    if (status == EFI_SUCCESS && tcg2_proto != NULL) {
62
        EFI_TCG2_BOOT_SERVICE_CAPABILITY cap;
63
        memset(&cap, 0, sizeof(cap));
64
        cap.Size = sizeof(cap);
65
        status = tcg2_proto->GetCapability(tcg2_proto, &cap);
66
        if (status == EFI_SUCCESS && cap.TPMPresentFlag) {
67
            tcg2 = tcg2_proto;
68
            printv("tpm: TCG2 protocol located, TPM present (active PCR banks: %x)\n",
69
                   (uint32_t)cap.ActivePcrBanks);
70
            return;
71
        }
72
    }
73
74
    // No TCG2/TPM 2.0; fall back to the CC measurement protocol.
75
    EFI_GUID cc_guid = EFI_CC_MEASUREMENT_PROTOCOL_GUID;
76
    EFI_CC_MEASUREMENT_PROTOCOL *cc_proto = NULL;
77
    status = gBS->LocateProtocol(&cc_guid, NULL, (void **)&cc_proto);
78
    if (status == EFI_SUCCESS && cc_proto != NULL) {
79
        EFI_CC_BOOT_SERVICE_CAPABILITY cap;
80
        memset(&cap, 0, sizeof(cap));
81
        cap.Size = sizeof(cap);
82
        status = cc_proto->GetCapability(cc_proto, &cap);
83
        if (status == EFI_SUCCESS) {
84
            cc = cc_proto;
85
            const char *cc_name = "unknown";
86
            switch (cap.CcType.Type) {
87
                case EFI_CC_TYPE_AMD_SEV:   cc_name = "AMD SEV";   break;
88
                case EFI_CC_TYPE_INTEL_TDX: cc_name = "Intel TDX"; break;
89
            }
90
            printv("tpm: CC measurement protocol located (type: %s)\n", cc_name);
91
            return;
92
        }
93
    }
94
}
95
96
bool tpm_present(void) {
97
    return tcg2 != NULL || cc != NULL;
98
}
99
100
void tpm_measure(uint32_t pcr, uint32_t event_type,
101
                 const void *data, size_t data_size,
102
                 const char *desc_prefix, const char *desc_value) {
103
    if (!measured_boot || data == NULL) {
104
        return;
105
    }
106
107
    size_t prefix_len = desc_prefix != NULL ? strlen(desc_prefix) : 0;
108
    size_t value_len = desc_value != NULL ? strlen(desc_value) : 0;
109
    size_t desc_len = prefix_len + value_len + 1;
110
111
    if (tcg2 != NULL) {
112
        size_t event_size = offsetof(EFI_TCG2_EVENT, Event) + desc_len;
113
114
        EFI_TCG2_EVENT *event = ext_mem_alloc(event_size);
115
        event->Size = (UINT32)event_size;
116
        event->Header.HeaderSize = sizeof(EFI_TCG2_EVENT_HEADER);
117
        event->Header.HeaderVersion = 1;
118
        event->Header.PCRIndex = pcr;
119
        event->Header.EventType = event_type;
120
        if (prefix_len > 0) {
121
            memcpy(event->Event, desc_prefix, prefix_len);
122
        }
123
        if (value_len > 0) {
124
            memcpy(event->Event + prefix_len, desc_value, value_len);
125
        }
126
127
        EFI_STATUS status = tcg2->HashLogExtendEvent(
128
            tcg2, 0,
129
            (EFI_PHYSICAL_ADDRESS)(uintptr_t)data, (UINT64)data_size,
130
            event);
131
        if (status != EFI_SUCCESS) {
132
            printv("tpm: HashLogExtendEvent for PCR %u failed: %X\n",
133
                   pcr, (uint64_t)status);
134
        }
135
136
        pmm_free(event, event_size);
137
    } else if (cc != NULL) {
138
        // CC platforms expose Memory Reference (MR) registers rather than
139
        // PCRs. The protocol provides a translation from a requested PCR
140
        // index to the platform's corresponding MR index.
141
        EFI_CC_MR_INDEX mr_index;
142
        EFI_STATUS status = cc->MapPcrToMrIndex(cc, pcr, &mr_index);
143
        if (status != EFI_SUCCESS) {
144
            return;
145
        }
146
147
        size_t event_size = offsetof(EFI_CC_EVENT, Event) + desc_len;
148
149
        EFI_CC_EVENT *event = ext_mem_alloc(event_size);
150
        event->Size = (UINT32)event_size;
151
        event->Header.HeaderSize = sizeof(EFI_CC_EVENT_HEADER);
152
        event->Header.HeaderVersion = EFI_CC_EVENT_HEADER_VERSION;
153
        event->Header.MrIndex = mr_index;
154
        event->Header.EventType = event_type;
155
        if (prefix_len > 0) {
156
            memcpy(event->Event, desc_prefix, prefix_len);
157
        }
158
        if (value_len > 0) {
159
            memcpy(event->Event + prefix_len, desc_value, value_len);
160
        }
161
162
        status = cc->HashLogExtendEvent(
163
            cc, 0,
164
            (EFI_PHYSICAL_ADDRESS)(uintptr_t)data, (UINT64)data_size,
165
            event);
166
        if (status != EFI_SUCCESS) {
167
            printv("tpm: CC HashLogExtendEvent for PCR %u (MR %u) failed: %X\n",
168
                   pcr, (uint32_t)mr_index, (uint64_t)status);
169
        }
170
171
        pmm_free(event, event_size);
172
    }
173
}
174
175
void tpm_measure_path(uint32_t pcr, uint32_t event_type,
176
                      const char *desc_prefix, const char *path) {
177
    if (!measured_boot || path == NULL) {
178
        return;
179
    }
180
181
    const char *hash_sep = strchr(path, '#');
182
    size_t path_len = hash_sep != NULL
183
        ? (size_t)(hash_sep - path)
184
        : strlen(path);
185
186
    // Static scratch matches uri.c's URI_BUF_SIZE; URIs longer than that
187
    // already panic in uri_resolve(), so a too-long path here is a bug.
188
    static char stripped[4096];
189
    if (path_len >= sizeof(stripped)) {
190
        return;
191
    }
192
    memcpy(stripped, path, path_len);
193
    stripped[path_len] = '\0';
194
195
    tpm_measure(pcr, event_type, stripped, path_len, desc_prefix, stripped);
196
}
197
198
uint32_t tpm_calc_event_size(const void *event_p, const void *header_p) {
199
    const struct tpm_pcr_event2_head *event = event_p;
200
    const struct tpm_pcr_event_v1_2 *event_header = header_p;
201
202
    static const uint8_t zero_digest[20] = {0};
203
204
    if (event_header->pcr_idx != 0
205
     || event_header->event_type != TCG_EV_NO_ACTION
206
     || memcmp(event_header->digest, zero_digest, sizeof(zero_digest)) != 0) {
207
        return 0;
208
    }
209
210
    const struct tpm_specid_event_head *efispecid =
211
        (const struct tpm_specid_event_head *)event_header->event;
212
213
    if (memcmp(efispecid->signature, TCG_SPECID_SIG, sizeof(TCG_SPECID_SIG)) != 0
214
     || efispecid->num_algs == 0) {
215
        return 0;
216
    }
217
218
    const uint8_t *marker_start = (const uint8_t *)event_p;
219
    const uint8_t *marker = marker_start
220
                          + sizeof(event->pcr_idx)
221
                          + sizeof(event->event_type)
222
                          + sizeof(event->count);
223
224
    for (uint32_t i = 0; i < event->count; i++) {
225
        uint16_t halg;
226
        memcpy(&halg, marker, sizeof(halg));
227
        marker += sizeof(halg);
228
229
        uint32_t j;
230
        for (j = 0; j < efispecid->num_algs; j++) {
231
            if (halg == efispecid->digest_sizes[j].alg_id) {
232
                marker += efispecid->digest_sizes[j].digest_size;
233
                break;
234
            }
235
        }
236
        if (j == efispecid->num_algs) {
237
            return 0;
238
        }
239
    }
240
241
    uint32_t trailing_event_size;
242
    memcpy(&trailing_event_size, marker, sizeof(trailing_event_size));
243
    marker += sizeof(trailing_event_size) + trailing_event_size;
244
245
    if (event->event_type == 0 && trailing_event_size == 0) {
246
        return 0;
247
    }
248
249
    return (uint32_t)(marker - marker_start);
250
}
251
252
static void *captured_log = NULL;
253
static size_t captured_log_size = 0;
254
static uint32_t captured_log_format = 0;
255
static bool capture_attempted = false;
256
257
// Pull the firmware event log via GetEventLog and copy the raw event bytes
258
// into a bootloader-reclaimable buffer. Idempotent. Returns true if the
259
// captured state is valid.
260
static bool tpm_capture_event_log(void) {
261
    if (capture_attempted) {
262
        return captured_log != NULL;
263
    }
264
    capture_attempted = true;
265
266
    if (tcg2 == NULL && cc == NULL) {
267
        return false;
268
    }
269
270
    EFI_PHYSICAL_ADDRESS log_location = 0, log_last_entry = 0;
271
    BOOLEAN truncated = FALSE;
272
    uint32_t log_format = EFI_TCG2_EVENT_LOG_FORMAT_TCG_2;
273
    EFI_STATUS status;
274
275
    if (tcg2 != NULL) {
276
        status = tcg2->GetEventLog(tcg2, log_format,
277
            &log_location, &log_last_entry, &truncated);
278
        if (status != EFI_SUCCESS || log_location == 0) {
279
            log_format = EFI_TCG2_EVENT_LOG_FORMAT_TCG_1_2;
280
            status = tcg2->GetEventLog(tcg2, log_format,
281
                &log_location, &log_last_entry, &truncated);
282
            if (status != EFI_SUCCESS || log_location == 0) {
283
                return false;
284
            }
285
        }
286
    } else {
287
        // CC measurement protocol. Only the TCG 2.0 log format is defined.
288
        log_format = EFI_CC_EVENT_LOG_FORMAT_TCG_2;
289
        status = cc->GetEventLog(cc, log_format,
290
            &log_location, &log_last_entry, &truncated);
291
        if (status != EFI_SUCCESS || log_location == 0) {
292
            return false;
293
        }
294
    }
295
296
    uint32_t log_size = 0;
297
    if (log_last_entry != 0) {
298
        uint32_t last_entry_size = 0;
299
        // The first entry of a TCG 2.0 log is itself a v1.2-format spec-ID
300
        // event; only entries after it follow the crypto-agile layout.
301
        if (log_format > EFI_TCG2_EVENT_LOG_FORMAT_TCG_1_2
302
         && log_last_entry != log_location) {
303
            last_entry_size = tpm_calc_event_size(
304
                (void *)(uintptr_t)log_last_entry,
305
                (void *)(uintptr_t)log_location);
306
        } else {
307
            const struct tpm_pcr_event_v1_2 *e =
308
                (const struct tpm_pcr_event_v1_2 *)(uintptr_t)log_last_entry;
309
            last_entry_size = sizeof(struct tpm_pcr_event_v1_2) + e->event_size;
310
        }
311
        log_size = (uint32_t)(log_last_entry - log_location) + last_entry_size;
312
    }
313
314
    void *log_bytes = NULL;
315
    if (log_size > 0) {
316
        log_bytes = ext_mem_alloc(log_size);
317
        memcpy(log_bytes, (void *)(uintptr_t)log_location, log_size);
318
    }
319
320
    captured_log = log_bytes;
321
    captured_log_size = log_size;
322
    captured_log_format = log_format;
323
    return true;
324
}
325
326
bool tpm_get_event_log(uint32_t *format, void **address, size_t *size) {
327
    if (!tpm_capture_event_log()) {
328
        return false;
329
    }
330
331
    *format = captured_log_format;
332
    *address = captured_log;
333
    *size = captured_log_size;
334
    return true;
335
}
336
337
void tpm_release_event_log(void) {
338
    if (captured_log != NULL) {
339
        pmm_free(captured_log, captured_log_size);
340
        captured_log = NULL;
341
    }
342
}
343
344
void *tpm_get_final_events_table(void) {
345
    EFI_GUID guid;
346
    if (tcg2 != NULL) {
347
        EFI_GUID tcg2_guid = EFI_TCG2_FINAL_EVENTS_TABLE_GUID;
348
        guid = tcg2_guid;
349
    } else if (cc != NULL) {
350
        EFI_GUID cc_guid = EFI_CC_FINAL_EVENTS_TABLE_GUID;
351
        guid = cc_guid;
352
    } else {
353
        return NULL;
354
    }
355
356
    for (UINTN i = 0; i < gST->NumberOfTableEntries; i++) {
357
        if (memcmp(&gST->ConfigurationTable[i].VendorGuid,
358
                   &guid, sizeof(EFI_GUID)) == 0) {
359
            return gST->ConfigurationTable[i].VendorTable;
360
        }
361
    }
362
    return NULL;
363
}
364
365
#endif
tab: 248 wrap: offon