1 /* virt-what-cvm-helper: Are we running inside confidential VM
2 * Copyright (C) 2023 Red Hat Inc.
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
30 #include <tss2/tss2_esys.h>
34 static bool dodebug = false;
36 #define debug(...) do { if (dodebug) fprintf(stderr, __VA_ARGS__); } while(0)
39 #define CPUID_PROCESSOR_INFO_AND_FEATURE_BITS 0x1
42 * AMD64 Architecture Programmer’s Manual Volume 3:
43 * General-Purpose and System Instructions.
44 * Chapter: E4.1 - Maximum Extended Function Number and Vendor String
45 * https://www.amd.com/system/files/TechDocs/24594.pdf
47 #define CPUID_GET_HIGHEST_FUNCTION 0x80000000
50 * AMD64 Architecture Programmer’s Manual Volume 3:
51 * General-Purpose and System Instructions.
52 * Chapter: E4.17 - Encrypted Memory Capabilities
53 * https://www.amd.com/system/files/TechDocs/24594.pdf
55 #define CPUID_AMD_GET_ENCRYPTED_MEMORY_CAPABILITIES 0x8000001f
58 * AMD64 Architecture Programmer’s Manual Volume 3:
59 * General-Purpose and System Instructions.
60 * Chapter: 15.34.10 - SEV_STATUS MSR
61 * https://www.amd.com/system/files/TechDocs/24593.pdf
63 #define MSR_AMD64_SEV 0xc0010131
66 * Intel® TDX Module v1.5 Base Architecture Specification
68 * https://www.intel.com/content/www/us/en/content-details/733575/intel-tdx-module-v1-5-base-architecture-specification.html
71 #define CPUID_INTEL_TDX_ENUMERATION 0x21
73 /* Requirements for Implementing the Microsoft Hypervisor Interface
74 * https://learn.microsoft.com/en-us/virtualization/hyper-v-on-windows/tlfs/tlfs
76 #define CPUID_HYPERV_VENDOR_AND_MAX_FUNCTIONS 0x40000000
78 #define CPUID_HYPERV_FEATURES 0x40000003
80 #define CPUID_HYPERV_ISOLATION_CONFIG 0x4000000C
82 #define CPUID_HYPERV_MIN 0x40000005
83 #define CPUID_HYPERV_MAX 0x4000ffff
85 #define CPUID_SIG_AMD "AuthenticAMD"
86 #define CPUID_SIG_INTEL "GenuineIntel"
87 #define CPUID_SIG_INTEL_TDX "IntelTDX "
88 #define CPUID_SIG_HYPERV "Microsoft Hv"
90 /* ecx bit 31: set => hyperpvisor, unset => bare metal */
91 #define CPUID_FEATURE_HYPERVISOR (1 << 31)
93 /* Linux include/asm-generic/hyperv-tlfs.h */
94 #define CPUID_HYPERV_CPU_MANAGEMENT (1 << 12) /* root partition */
95 #define CPUID_HYPERV_ISOLATION (1 << 22) /* confidential VM partition */
97 #define CPUID_HYPERV_ISOLATION_TYPE_MASK 0xf
98 #define CPUID_HYPERV_ISOLATION_TYPE_SNP 2
101 * This TPM NV data format is not explicitly documented anywhere,
102 * but the header definition is present in code at:
104 * https://github.com/kinvolk/azure-cvm-tooling/blob/main/az-snp-vtpm/src/hcl.rs
106 #define TPM_AZURE_HCLA_REPORT_INDEX 0x01400001
108 struct TPMAzureHCLAHeader {
112 uint32_t report_type;
116 /* The bytes for "HCLA" */
117 #define TPM_AZURE_HCLA_SIGNATURE 0x414C4348
118 #define TPM_AZURE_HCLA_VERSION 0x1
119 #define TPM_AZURE_HCLA_REPORT_TYPE_SNP 0x2
121 #if defined(__x86_64__)
125 tpm_nvread(uint32_t nvindex, size_t *retlen)
128 ESYS_CONTEXT *ctx = NULL;
129 ESYS_TR primary = ESYS_TR_NONE;
130 ESYS_TR session = ESYS_TR_NONE;
131 ESYS_TR nvobj = ESYS_TR_NONE;
132 TPM2B_NV_PUBLIC *pubData = NULL;
134 .algorithm = TPM2_ALG_AES,
135 .keyBits = { .aes = 128 },
136 .mode = { .aes = TPM2_ALG_CFB }
141 rc = Esys_Initialize(&ctx, NULL, NULL);
142 if (rc != TSS2_RC_SUCCESS)
145 rc = Esys_Startup(ctx, TPM2_SU_CLEAR);
146 debug("tpm startup %d\n", rc);
147 if (rc != TSS2_RC_SUCCESS)
150 rc = Esys_StartAuthSession(ctx, ESYS_TR_NONE, ESYS_TR_NONE,
151 ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE,
153 &sym, TPM2_ALG_SHA256, &session);
154 debug("tpm auth session %d\n", rc);
155 if (rc != TSS2_RC_SUCCESS)
158 rc = Esys_TR_FromTPMPublic(ctx, nvindex, ESYS_TR_NONE,
159 ESYS_TR_NONE, ESYS_TR_NONE, &nvobj);
160 debug("tpm from public %d\n", rc);
161 if (rc != TSS2_RC_SUCCESS)
164 rc = Esys_NV_ReadPublic(ctx, nvobj, ESYS_TR_NONE,
165 ESYS_TR_NONE, ESYS_TR_NONE,
167 debug("tpm read public %d\n", rc);
168 if (rc != TPM2_RC_SUCCESS)
171 retwant = pubData->nvPublic.dataSize;
174 ret = malloc(retwant);
176 while (*retlen < retwant) {
177 size_t want = retwant - *retlen;
178 TPM2B_MAX_NV_BUFFER *data = NULL;
181 rc = Esys_NV_Read(ctx, ESYS_TR_RH_OWNER, nvobj, session, ESYS_TR_NONE, ESYS_TR_NONE,
182 want, *retlen, &data);
183 debug("tpm nv read %d\n", rc);
184 if (rc != TPM2_RC_SUCCESS) {
189 memcpy(ret + *retlen, data->buffer, data->size);
190 *retlen += data->size;
197 if (nvobj != ESYS_TR_NONE)
198 Esys_FlushContext(ctx, nvobj);
199 if (session != ESYS_TR_NONE)
200 Esys_FlushContext(ctx, session);
201 if (primary != ESYS_TR_NONE)
202 Esys_FlushContext(ctx, primary);
207 #else /* ! HAVE_TPM2_TSS */
209 tpm_nvread(uint32_t nvindex, size_t *retlen)
213 #endif /* ! HAVE_TPM2_TSS */
215 /* Copied from the Linux kernel definition in
216 * arch/x86/include/asm/processor.h
219 cpuid (uint32_t *eax, uint32_t *ebx, uint32_t *ecx, uint32_t *edx)
221 debug("CPUID func %x %x\n", *eax, *ecx);
222 asm volatile ("cpuid"
223 : "=a" (*eax), "=b" (*ebx), "=c" (*ecx), "=d" (*edx)
224 : "0" (*eax), "2" (*ecx)
226 debug("CPUID result %x %x %x %x\n", *eax, *ebx, *ecx, *edx);
231 cpuid_leaf (uint32_t eax, char *sig, bool swapped)
233 uint32_t *sig32 = (uint32_t *) sig;
236 cpuid (&eax, &sig32[0], &sig32[2], &sig32[1]);
238 cpuid (&eax, &sig32[0], &sig32[1], &sig32[2]);
239 sig[12] = 0; /* \0-terminate the string to make string comparison possible */
240 debug("CPUID sig %s\n", sig);
244 #define MSR_DEVICE "/dev/cpu/0/msr"
250 int fd = open (MSR_DEVICE, O_RDONLY);
252 debug ("Cannot open MSR device %s", MSR_DEVICE);
256 if (pread (fd, &ret, sizeof(ret), index) != sizeof(ret))
261 debug ("MSR %llx result %llx\n", (unsigned long long)index,
262 (unsigned long long)ret);
267 cpu_sig_amd_azure (void)
270 char *data = tpm_nvread(TPM_AZURE_HCLA_REPORT_INDEX, &datalen);
271 struct TPMAzureHCLAHeader *header = (struct TPMAzureHCLAHeader *)data;
277 if (datalen < sizeof(struct TPMAzureHCLAHeader)) {
278 debug ("TPM data len is too small to be an Azure HCLA report");
282 debug ("Azure TPM HCLA report header sig %x ver %x type %x\n",
283 header->signature, header->version, header->report_type);
285 ret = (header->signature == TPM_AZURE_HCLA_SIGNATURE &&
286 header->version == TPM_AZURE_HCLA_VERSION &&
287 header->report_type == TPM_AZURE_HCLA_REPORT_TYPE_SNP);
288 debug ("Azure TPM HCLA report present ? %d\n", ret);
295 cpu_sig_amd_hyperv (void)
297 uint32_t eax, ebx, ecx, edx;
301 feat = cpuid_leaf (CPUID_HYPERV_VENDOR_AND_MAX_FUNCTIONS, sig, false);
303 if (feat < CPUID_HYPERV_MIN ||
304 feat > CPUID_HYPERV_MAX)
307 if (memcmp (sig, CPUID_SIG_HYPERV, sizeof(sig)) != 0)
310 debug ("CPUID is on hyperv\n");
311 eax = CPUID_HYPERV_FEATURES;
314 cpuid(&eax, &ebx, &ecx, &edx);
316 if (ebx & CPUID_HYPERV_ISOLATION &&
317 !(ebx & CPUID_HYPERV_CPU_MANAGEMENT)) {
319 eax = CPUID_HYPERV_ISOLATION_CONFIG;
321 cpuid(&eax, &ebx, &ecx, &edx);
323 if ((ebx & CPUID_HYPERV_ISOLATION_TYPE_MASK) ==
324 CPUID_HYPERV_ISOLATION_TYPE_SNP) {
335 uint32_t eax, ebx, ecx, edx;
338 eax = CPUID_GET_HIGHEST_FUNCTION;
341 cpuid (&eax, &ebx, &ecx, &edx);
343 if (eax < CPUID_AMD_GET_ENCRYPTED_MEMORY_CAPABILITIES)
346 eax = CPUID_AMD_GET_ENCRYPTED_MEMORY_CAPABILITIES;
349 cpuid (&eax, &ebx, &ecx, &edx);
351 /* bit 1 == CPU supports SEV feature
353 * Note, Azure blocks this CPUID leaf from its SEV-SNP
354 * guests, so we must fallback to probing the TPM which
355 * exposes a SEV-SNP attestation report as evidence.
357 if (!(eax & (1 << 1))) {
358 debug ("No sev in CPUID, try hyperv CPUID/azure TPM NV\n");
360 if (cpu_sig_amd_hyperv () ||
361 cpu_sig_amd_azure()) {
362 puts ("amd-sev-snp");
365 debug("No azure TPM NV\n");
370 msrval = msr (MSR_AMD64_SEV);
372 /* Test reverse order, since the SEV-SNP bit implies
373 * the SEV-ES bit, which implies the SEV bit */
374 if (msrval & (1 << 2)) {
375 puts ("amd-sev-snp");
376 } else if (msrval & (1 << 1)) {
378 } else if (msrval & (1 << 0)) {
386 uint32_t eax, ebx, ecx, edx;
389 eax = CPUID_GET_HIGHEST_FUNCTION;
392 cpuid (&eax, &ebx, &ecx, &edx);
393 debug ("CPUID max function: %x %x %x %x\n", eax, ebx, ecx,edx);
395 if (eax < CPUID_INTEL_TDX_ENUMERATION)
398 memset (sig, 0, sizeof sig);
399 cpuid_leaf (CPUID_INTEL_TDX_ENUMERATION, sig, true);
401 if (memcmp (sig, CPUID_SIG_INTEL_TDX, sizeof(sig)) == 0)
408 uint32_t eax, ebx, ecx, edx;
411 eax = CPUID_PROCESSOR_INFO_AND_FEATURE_BITS;
414 cpuid(&eax, &ebx, &ecx, &edx);
416 is_hv = ecx & CPUID_FEATURE_HYPERVISOR;
418 debug ("CPUID is hypervisor: %s\n", is_hv ? "yes" : "no");
427 /* Skip everything on bare metal */
431 memset (sig, 0, sizeof sig);
432 cpuid_leaf (0, sig, true);
434 if (memcmp (sig, CPUID_SIG_AMD, sizeof(sig)) == 0)
436 else if (memcmp (sig, CPUID_SIG_INTEL, sizeof(sig)) == 0)
445 /* nothing for other architectures */
451 main(int argc, char **argv)
456 int option_index = 0;
457 static struct option long_options[] = {
458 {"debug", no_argument, 0, 'd' },
459 {"version", no_argument, 0, 'v' },
460 {"help", no_argument, 0, 'h'},
464 c = getopt_long(argc, argv, "dvh",
465 long_options, &option_index);
474 fprintf(stdout, "%s\n", PACKAGE_VERSION);
479 fprintf(c == 'h' ? stdout : stderr,
480 "Usage: %s [--debug|-d] [--help|-h] [--version|-v]\n",
482 exit(c == 'h' ? EXIT_SUCCESS : EXIT_FAILURE);
487 setenv("TSS2_LOG", "all+none", 1);