virt-what-cvm: rename 'azure-hcl' fact to 'hyperv-hcl'
[virt-what.git] / virt-what-cvm.c
1 /* virt-what-cvm-helper: Are we running inside confidential VM
2  * Copyright (C) 2023 Red Hat Inc.
3  *
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.
8  *
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.
13  *
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.
17  */
18
19 #include "config.h"
20
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <stdint.h>
24 #include <string.h>
25 #include <stdbool.h>
26 #include <fcntl.h>
27 #include <unistd.h>
28 #include <getopt.h>
29
30 static bool dodebug = false;
31
32 #define debug(...) do { if (dodebug) fprintf(stderr, __VA_ARGS__); } while(0)
33
34
35 #define CPUID_PROCESSOR_INFO_AND_FEATURE_BITS 0x1
36
37 /*
38  * AMD64 Architecture Programmer’s Manual Volume 3:
39  * General-Purpose and System Instructions.
40  * Chapter: E4.1 - Maximum Extended Function Number and Vendor String
41  *  https://www.amd.com/system/files/TechDocs/24594.pdf
42  */
43 #define CPUID_GET_HIGHEST_FUNCTION 0x80000000
44
45 /*
46  * AMD64 Architecture Programmer’s Manual Volume 3:
47  * General-Purpose and System Instructions.
48  * Chapter: E4.17 - Encrypted Memory Capabilities
49  *  https://www.amd.com/system/files/TechDocs/24594.pdf
50  */
51 #define CPUID_AMD_GET_ENCRYPTED_MEMORY_CAPABILITIES 0x8000001f
52
53 /*
54  * AMD64 Architecture Programmer’s Manual Volume 3:
55  * General-Purpose and System Instructions.
56  * Chapter: 15.34.10 - SEV_STATUS MSR
57  * https://www.amd.com/system/files/TechDocs/24593.pdf
58  */
59 #define MSR_AMD64_SEV 0xc0010131
60
61 /*
62  * Intel® TDX Module v1.5 Base Architecture Specification
63  * Chapter: 11.2
64  * https://www.intel.com/content/www/us/en/content-details/733575/intel-tdx-module-v1-5-base-architecture-specification.html
65  */
66
67 #define CPUID_INTEL_TDX_ENUMERATION 0x21
68
69 /* Requirements for Implementing the Microsoft Hypervisor Interface
70  * https://learn.microsoft.com/en-us/virtualization/hyper-v-on-windows/tlfs/tlfs
71  */
72 #define CPUID_HYPERV_VENDOR_AND_MAX_FUNCTIONS 0x40000000
73
74 #define CPUID_HYPERV_FEATURES 0x40000003
75
76 #define CPUID_HYPERV_ISOLATION_CONFIG 0x4000000C
77
78 #define CPUID_HYPERV_MIN 0x40000005
79 #define CPUID_HYPERV_MAX 0x4000ffff
80
81 #define CPUID_SIG_AMD       "AuthenticAMD"
82 #define CPUID_SIG_INTEL     "GenuineIntel"
83 #define CPUID_SIG_INTEL_TDX "IntelTDX    "
84 #define CPUID_SIG_HYPERV    "Microsoft Hv"
85
86 /* ecx bit 31: set => hyperpvisor, unset => bare metal */
87 #define CPUID_FEATURE_HYPERVISOR (1 << 31)
88
89 /* Linux include/asm-generic/hyperv-tlfs.h */
90 #define CPUID_HYPERV_CPU_MANAGEMENT (1 << 12) /* root partition */
91 #define CPUID_HYPERV_ISOLATION      (1 << 22) /* confidential VM partition */
92
93 #define CPUID_HYPERV_ISOLATION_TYPE_MASK 0xf
94 #define CPUID_HYPERV_ISOLATION_TYPE_SNP 2
95
96 #if defined(__x86_64__)
97
98 /* Copied from the Linux kernel definition in
99  * arch/x86/include/asm/processor.h
100  */
101 static inline void
102 cpuid (uint32_t *eax, uint32_t *ebx, uint32_t *ecx, uint32_t *edx)
103 {
104   debug("CPUID func %x %x\n", *eax, *ecx);
105   asm volatile ("cpuid"
106                 : "=a" (*eax), "=b" (*ebx), "=c" (*ecx), "=d" (*edx)
107                 : "0" (*eax), "2" (*ecx)
108                 : "memory");
109   debug("CPUID result %x %x %x %x\n", *eax, *ebx, *ecx, *edx);
110 }
111
112
113 static uint32_t
114 cpuid_leaf (uint32_t eax, char *sig, bool swapped)
115 {
116   uint32_t *sig32 = (uint32_t *) sig;
117
118   if (swapped)
119     cpuid (&eax, &sig32[0], &sig32[2], &sig32[1]);
120   else
121     cpuid (&eax, &sig32[0], &sig32[1], &sig32[2]);
122   sig[12] = 0; /* \0-terminate the string to make string comparison possible */
123   debug("CPUID sig %s\n", sig);
124   return eax;
125 }
126
127 #define MSR_DEVICE "/dev/cpu/0/msr"
128
129 static uint64_t
130 msr (off_t index)
131 {
132   uint64_t ret;
133   int fd = open (MSR_DEVICE, O_RDONLY);
134   if (fd < 0) {
135     debug ("Cannot open MSR device %s", MSR_DEVICE);
136     return 0;
137   }
138
139   if (pread (fd, &ret, sizeof(ret), index) != sizeof(ret))
140     ret = 0;
141
142   close (fd);
143
144   debug ("MSR %llx result %llx\n", (unsigned long long)index,
145          (unsigned long long)ret);
146   return ret;
147 }
148
149 static bool
150 cpu_sig_amd_hyperv (void)
151 {
152   uint32_t eax, ebx, ecx, edx;
153   char sig[13];
154   uint32_t feat;
155
156   feat = cpuid_leaf (CPUID_HYPERV_VENDOR_AND_MAX_FUNCTIONS, sig, false);
157
158   if (feat < CPUID_HYPERV_MIN ||
159       feat > CPUID_HYPERV_MAX)
160     return false;
161
162   if (memcmp (sig, CPUID_SIG_HYPERV, sizeof(sig)) != 0)
163     return false;
164
165   debug ("CPUID is on hyperv\n");
166   eax = CPUID_HYPERV_FEATURES;
167   ebx = ecx = edx = 0;
168
169   cpuid(&eax, &ebx, &ecx, &edx);
170
171   if (ebx & CPUID_HYPERV_ISOLATION &&
172       !(ebx & CPUID_HYPERV_CPU_MANAGEMENT)) {
173
174     eax = CPUID_HYPERV_ISOLATION_CONFIG;
175     ebx = ecx = edx = 0;
176     cpuid(&eax, &ebx, &ecx, &edx);
177
178     if ((ebx & CPUID_HYPERV_ISOLATION_TYPE_MASK) ==
179         CPUID_HYPERV_ISOLATION_TYPE_SNP) {
180       return true;
181     }
182   }
183
184   return false;
185 }
186
187 static void
188 cpu_sig_amd (void)
189 {
190   uint32_t eax, ebx, ecx, edx;
191   uint64_t msrval;
192
193   eax = CPUID_GET_HIGHEST_FUNCTION;
194   ebx = ecx = edx = 0;
195
196   cpuid (&eax, &ebx, &ecx, &edx);
197
198   if (eax < CPUID_AMD_GET_ENCRYPTED_MEMORY_CAPABILITIES)
199     return;
200
201   eax = CPUID_AMD_GET_ENCRYPTED_MEMORY_CAPABILITIES;
202   ebx = ecx = edx = 0;
203
204   cpuid (&eax, &ebx, &ecx, &edx);
205
206   /* bit 1 == CPU supports SEV feature
207    *
208    * Note, HyperV/Azure blocks this CPUID leaf from its SEV-SNP
209    * guests. We already did an alternative detection mechanism
210    * in such VMs, so should not even be running this code.
211    */
212   if (!(eax & (1 << 1))) {
213     debug ("No sev in CPUID, try hyperv CPUID\n");
214
215     if (cpu_sig_amd_hyperv ()) {
216       puts ("amd-sev-snp");
217       puts ("hyperv-hcl");
218     } else {
219       debug("No hyperv CPUID\n");
220     }
221     return;
222   }
223
224   msrval = msr (MSR_AMD64_SEV);
225
226   /* Test reverse order, since the SEV-SNP bit implies
227    * the SEV-ES bit, which implies the SEV bit */
228   if (msrval & (1 << 2)) {
229     puts ("amd-sev-snp");
230   } else if (msrval & (1 << 1)) {
231     puts ("amd-sev-es");
232   } else if (msrval & (1 << 0)) {
233     puts ("amd-sev");
234   }
235 }
236
237 static void
238 cpu_sig_intel (void)
239 {
240   uint32_t eax, ebx, ecx, edx;
241   char sig[13];
242
243   eax = CPUID_GET_HIGHEST_FUNCTION;
244   ebx = ecx = edx = 0;
245
246   cpuid (&eax, &ebx, &ecx, &edx);
247   debug ("CPUID max function: %x %x %x %x\n", eax, ebx, ecx,edx);
248
249   if (eax < CPUID_INTEL_TDX_ENUMERATION)
250     return;
251
252   memset (sig, 0, sizeof sig);
253   cpuid_leaf (CPUID_INTEL_TDX_ENUMERATION, sig, true);
254
255   if (memcmp (sig, CPUID_SIG_INTEL_TDX, sizeof(sig)) == 0)
256     puts ("intel-tdx");
257 }
258
259 static bool
260 cpu_is_hv (void)
261 {
262   uint32_t eax, ebx, ecx, edx;
263   bool is_hv;
264
265   eax = CPUID_PROCESSOR_INFO_AND_FEATURE_BITS;
266   ebx = ecx = edx = 0;
267
268   cpuid(&eax, &ebx, &ecx, &edx);
269
270   is_hv = ecx & CPUID_FEATURE_HYPERVISOR;
271
272   debug ("CPUID is hypervisor: %s\n", is_hv ? "yes" : "no");
273   return is_hv;
274 }
275
276 static void
277 cpu_sig (void)
278 {
279   char sig[13];
280
281   /* Skip everything on bare metal */
282   if (!cpu_is_hv ())
283     return;
284
285   memset (sig, 0, sizeof sig);
286   cpuid_leaf (0, sig, true);
287
288   if (memcmp (sig, CPUID_SIG_AMD, sizeof(sig)) == 0)
289     cpu_sig_amd ();
290   else if (memcmp (sig, CPUID_SIG_INTEL, sizeof(sig)) == 0)
291     cpu_sig_intel ();
292 }
293
294 #else /* !x86_64 */
295
296 static void
297 cpu_sig (void)
298 {
299   /* nothing for other architectures */
300 }
301
302 #endif
303
304 int
305 main(int argc, char **argv)
306 {
307   int c;
308
309   while (true) {
310     int option_index = 0;
311     static struct option long_options[] = {
312       {"debug", no_argument, 0, 'd' },
313       {"version", no_argument, 0, 'v' },
314       {"help", no_argument, 0, 'h'},
315       {0, 0, 0, 0 }
316     };
317
318     c = getopt_long(argc, argv, "dvh",
319                     long_options, &option_index);
320     if (c == -1)
321       break;
322
323     switch (c) {
324     case 'd':
325       dodebug = true;
326       break;
327     case 'v':
328       fprintf(stdout, "%s\n", PACKAGE_VERSION);
329       exit(EXIT_SUCCESS);
330       break;
331     case 'h':
332     default: /* '?' */
333       fprintf(c == 'h' ? stdout : stderr,
334               "Usage: %s [--debug|-d] [--help|-h] [--version|-v]\n",
335               argv[0]);
336       exit(c == 'h' ? EXIT_SUCCESS : EXIT_FAILURE);
337     }
338   }
339
340   cpu_sig ();
341
342   exit(EXIT_SUCCESS);
343 }