]>
Commit | Line | Data |
---|---|---|
db9ecf05 | 1 | /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
b52aae1d | 2 | |
d31b0033 MG |
3 | #if defined(__i386__) || defined(__x86_64__) |
4 | #include <cpuid.h> | |
5 | #endif | |
11c3a366 | 6 | #include <stdlib.h> |
e7537295 | 7 | #include <threads.h> |
b52aae1d LP |
8 | #include <unistd.h> |
9 | ||
b5efdb8a | 10 | #include "alloc-util.h" |
ade61d3b | 11 | #include "dirent-util.h" |
295ee984 | 12 | #include "env-util.h" |
d9ccf6b3 | 13 | #include "extract-word.h" |
ade61d3b | 14 | #include "fd-util.h" |
07630cea | 15 | #include "fileio.h" |
93a1f792 | 16 | #include "log.h" |
2f7cd6c7 | 17 | #include "namespace-util.h" |
c94f6ab1 | 18 | #include "parse-util.h" |
0c15577a | 19 | #include "pidref.h" |
93cc7779 | 20 | #include "process-util.h" |
8b43440b | 21 | #include "string-table.h" |
07630cea | 22 | #include "string-util.h" |
5f142e01 | 23 | #include "strv.h" |
b52aae1d LP |
24 | #include "virt.h" |
25 | ||
ce350379 NM |
26 | enum { |
27 | SMBIOS_VM_BIT_SET, | |
28 | SMBIOS_VM_BIT_UNSET, | |
29 | SMBIOS_VM_BIT_UNKNOWN, | |
30 | }; | |
31 | ||
1b86c7c5 | 32 | static Virtualization detect_vm_cpuid(void) { |
b52aae1d | 33 | |
2ef8a4c4 | 34 | /* CPUID is an x86 specific interface. */ |
bdb628ee | 35 | #if defined(__i386__) || defined(__x86_64__) |
b52aae1d | 36 | |
0f534758 LP |
37 | static const struct { |
38 | const char sig[13]; | |
1b86c7c5 | 39 | Virtualization id; |
0f534758 LP |
40 | } vm_table[] = { |
41 | { "XenVMMXenVMM", VIRTUALIZATION_XEN }, | |
42 | { "KVMKVMKVM", VIRTUALIZATION_KVM }, /* qemu with KVM */ | |
43 | { "Linux KVM Hv", VIRTUALIZATION_KVM }, /* qemu with KVM + HyperV Enlightenments */ | |
44 | { "TCGTCGTCGTCG", VIRTUALIZATION_QEMU }, /* qemu without KVM */ | |
45 | /* http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458 */ | |
46 | { "VMwareVMware", VIRTUALIZATION_VMWARE }, | |
47 | /* https://docs.microsoft.com/en-us/virtualization/hyper-v-on-windows/reference/tlfs */ | |
48 | { "Microsoft Hv", VIRTUALIZATION_MICROSOFT }, | |
49 | /* https://wiki.freebsd.org/bhyve */ | |
50 | { "bhyve bhyve ", VIRTUALIZATION_BHYVE }, | |
51 | { "QNXQVMBSQG", VIRTUALIZATION_QNX }, | |
52 | /* https://projectacrn.org */ | |
53 | { "ACRNACRNACRN", VIRTUALIZATION_ACRN }, | |
d833ed78 NM |
54 | /* https://www.lockheedmartin.com/en-us/products/Hardened-Security-for-Intel-Processors.html */ |
55 | { "SRESRESRESRE", VIRTUALIZATION_SRE }, | |
5a02a9ad | 56 | { "Apple VZ", VIRTUALIZATION_APPLE }, |
0f534758 LP |
57 | }; |
58 | ||
d31b0033 | 59 | uint32_t eax, ebx, ecx, edx; |
b52aae1d LP |
60 | bool hypervisor; |
61 | ||
62 | /* http://lwn.net/Articles/301888/ */ | |
b52aae1d | 63 | |
b52aae1d | 64 | /* First detect whether there is a hypervisor */ |
d31b0033 MG |
65 | if (__get_cpuid(1, &eax, &ebx, &ecx, &edx) == 0) |
66 | return VIRTUALIZATION_NONE; | |
b52aae1d | 67 | |
5d904a6a | 68 | hypervisor = ecx & 0x80000000U; |
b52aae1d LP |
69 | |
70 | if (hypervisor) { | |
75f86906 LP |
71 | union { |
72 | uint32_t sig32[3]; | |
73 | char text[13]; | |
74 | } sig = {}; | |
b52aae1d LP |
75 | |
76 | /* There is a hypervisor, see what it is */ | |
8481e3e7 | 77 | __cpuid(0x40000000U, eax, ebx, ecx, edx); |
d31b0033 MG |
78 | |
79 | sig.sig32[0] = ebx; | |
80 | sig.sig32[1] = ecx; | |
81 | sig.sig32[2] = edx; | |
b52aae1d | 82 | |
9f63a08d SS |
83 | log_debug("Virtualization found, CPUID=%s", sig.text); |
84 | ||
ddb8a639 | 85 | FOREACH_ELEMENT(vm, vm_table) |
0f534758 | 86 | if (memcmp_nn(sig.text, sizeof(sig.text), |
ddb8a639 I |
87 | vm->sig, sizeof(vm->sig)) == 0) |
88 | return vm->id; | |
bdb628ee | 89 | |
0f534758 LP |
90 | log_debug("Unknown virtualization with CPUID=%s. Add to vm_table[]?", sig.text); |
91 | return VIRTUALIZATION_VM_OTHER; | |
b52aae1d | 92 | } |
bdb628ee | 93 | #endif |
9f63a08d | 94 | log_debug("No virtualization found in CPUID"); |
bdb628ee | 95 | |
75f86906 | 96 | return VIRTUALIZATION_NONE; |
bdb628ee ZJS |
97 | } |
98 | ||
1b86c7c5 | 99 | static Virtualization detect_vm_device_tree(void) { |
819874ad | 100 | #if defined(__arm__) || defined(__aarch64__) || defined(__powerpc__) || defined(__powerpc64__) || defined(__riscv) |
d831deb5 CA |
101 | _cleanup_free_ char *hvtype = NULL; |
102 | int r; | |
103 | ||
b8f1df82 | 104 | r = read_one_line_file("/proc/device-tree/hypervisor/compatible", &hvtype); |
75f86906 | 105 | if (r == -ENOENT) { |
3224e38b MS |
106 | if (access("/proc/device-tree/ibm,partition-name", F_OK) == 0 && |
107 | access("/proc/device-tree/hmc-managed?", F_OK) == 0 && | |
108 | access("/proc/device-tree/chosen/qemu,graphic-width", F_OK) != 0) | |
109 | return VIRTUALIZATION_POWERVM; | |
110 | ||
4d53d76f | 111 | _cleanup_closedir_ DIR *dir = opendir("/proc/device-tree"); |
ce09c71d | 112 | if (!dir) { |
9f63a08d | 113 | if (errno == ENOENT) { |
4d53d76f | 114 | log_debug_errno(errno, "/proc/device-tree/ does not exist"); |
75f86906 | 115 | return VIRTUALIZATION_NONE; |
9f63a08d | 116 | } |
4d53d76f | 117 | return log_debug_errno(errno, "Opening /proc/device-tree/ failed: %m"); |
ce09c71d AJ |
118 | } |
119 | ||
4d53d76f | 120 | FOREACH_DIRENT(de, dir, return log_debug_errno(errno, "Failed to enumerate /proc/device-tree/ contents: %m")) |
af3b864d ZJS |
121 | if (strstr(de->d_name, "fw-cfg")) { |
122 | log_debug("Virtualization QEMU: \"fw-cfg\" present in /proc/device-tree/%s", de->d_name); | |
75f86906 | 123 | return VIRTUALIZATION_QEMU; |
9f63a08d | 124 | } |
75f86906 | 125 | |
4d53d76f | 126 | _cleanup_free_ char *compat = NULL; |
8c7a6c74 FS |
127 | r = read_one_line_file("/proc/device-tree/compatible", &compat); |
128 | if (r < 0 && r != -ENOENT) | |
4d53d76f | 129 | return log_debug_errno(r, "Failed to read /proc/device-tree/compatible: %m"); |
600e135a LP |
130 | if (r >= 0) { |
131 | if (streq(compat, "qemu,pseries")) { | |
132 | log_debug("Virtualization %s found in /proc/device-tree/compatible", compat); | |
133 | return VIRTUALIZATION_QEMU; | |
134 | } | |
135 | if (streq(compat, "linux,dummy-virt")) { | |
136 | /* https://www.kernel.org/doc/Documentation/devicetree/bindings/arm/linux%2Cdummy-virt.yaml */ | |
137 | log_debug("Generic virtualization %s found in /proc/device-tree/compatible", compat); | |
138 | return VIRTUALIZATION_VM_OTHER; | |
139 | } | |
8c7a6c74 FS |
140 | } |
141 | ||
9f63a08d | 142 | log_debug("No virtualization found in /proc/device-tree/*"); |
75f86906 LP |
143 | return VIRTUALIZATION_NONE; |
144 | } else if (r < 0) | |
145 | return r; | |
146 | ||
9f63a08d | 147 | log_debug("Virtualization %s found in /proc/device-tree/hypervisor/compatible", hvtype); |
75f86906 LP |
148 | if (streq(hvtype, "linux,kvm")) |
149 | return VIRTUALIZATION_KVM; | |
150 | else if (strstr(hvtype, "xen")) | |
151 | return VIRTUALIZATION_XEN; | |
4d4ac92c CL |
152 | else if (strstr(hvtype, "vmware")) |
153 | return VIRTUALIZATION_VMWARE; | |
75f86906 LP |
154 | else |
155 | return VIRTUALIZATION_VM_OTHER; | |
156 | #else | |
9f63a08d | 157 | log_debug("This platform does not support /proc/device-tree"); |
75f86906 | 158 | return VIRTUALIZATION_NONE; |
d831deb5 | 159 | #endif |
d831deb5 CA |
160 | } |
161 | ||
5041e771 | 162 | #if defined(__i386__) || defined(__x86_64__) || defined(__arm__) || defined(__aarch64__) || defined(__loongarch_lp64) || defined(__riscv) |
1b86c7c5 | 163 | static Virtualization detect_vm_dmi_vendor(void) { |
a9d178d2 | 164 | static const char* const dmi_vendors[] = { |
3728dcde | 165 | "/sys/class/dmi/id/product_name", /* Test this before sys_vendor to detect KVM over QEMU */ |
bdb628ee ZJS |
166 | "/sys/class/dmi/id/sys_vendor", |
167 | "/sys/class/dmi/id/board_vendor", | |
76eec064 | 168 | "/sys/class/dmi/id/bios_vendor", |
a9d178d2 ZJS |
169 | "/sys/class/dmi/id/product_version", /* For Hyper-V VMs test */ |
170 | NULL | |
bdb628ee ZJS |
171 | }; |
172 | ||
75f86906 LP |
173 | static const struct { |
174 | const char *vendor; | |
1b86c7c5 | 175 | Virtualization id; |
75f86906 | 176 | } dmi_vendor_table[] = { |
9b0688f4 YW |
177 | { "KVM", VIRTUALIZATION_KVM }, |
178 | { "OpenStack", VIRTUALIZATION_KVM }, /* Detect OpenStack instance as KVM in non x86 architecture */ | |
179 | { "KubeVirt", VIRTUALIZATION_KVM }, /* Detect KubeVirt instance as KVM in non x86 architecture */ | |
180 | { "Amazon EC2", VIRTUALIZATION_AMAZON }, | |
181 | { "QEMU", VIRTUALIZATION_QEMU }, | |
182 | { "VMware", VIRTUALIZATION_VMWARE }, /* https://kb.vmware.com/s/article/1009458 */ | |
183 | { "VMW", VIRTUALIZATION_VMWARE }, | |
184 | { "innotek GmbH", VIRTUALIZATION_ORACLE }, | |
185 | { "VirtualBox", VIRTUALIZATION_ORACLE }, | |
5e3c08d3 | 186 | { "Oracle Corporation", VIRTUALIZATION_ORACLE }, /* Detect VirtualBox on some proprietary systems via the board_vendor */ |
9b0688f4 YW |
187 | { "Xen", VIRTUALIZATION_XEN }, |
188 | { "Bochs", VIRTUALIZATION_BOCHS }, | |
189 | { "Parallels", VIRTUALIZATION_PARALLELS }, | |
aa0c3427 | 190 | /* https://wiki.freebsd.org/bhyve */ |
9b0688f4 YW |
191 | { "BHYVE", VIRTUALIZATION_BHYVE }, |
192 | { "Hyper-V", VIRTUALIZATION_MICROSOFT }, | |
193 | { "Apple Virtualization", VIRTUALIZATION_APPLE }, | |
194 | { "Google Compute Engine", VIRTUALIZATION_GOOGLE }, /* https://cloud.google.com/run/docs/container-contract#sandbox */ | |
75f86906 | 195 | }; |
75f86906 | 196 | int r; |
b52aae1d | 197 | |
a9d178d2 | 198 | STRV_FOREACH(vendor, dmi_vendors) { |
b1b8e816 | 199 | _cleanup_free_ char *s = NULL; |
b52aae1d | 200 | |
a9d178d2 | 201 | r = read_one_line_file(*vendor, &s); |
b1b8e816 | 202 | if (r < 0) { |
75f86906 LP |
203 | if (r == -ENOENT) |
204 | continue; | |
b52aae1d | 205 | |
75f86906 | 206 | return r; |
b52aae1d LP |
207 | } |
208 | ||
ddb8a639 I |
209 | FOREACH_ELEMENT(dmi_vendor, dmi_vendor_table) |
210 | if (startswith(s, dmi_vendor->vendor)) { | |
a9d178d2 | 211 | log_debug("Virtualization %s found in DMI (%s)", s, *vendor); |
ddb8a639 | 212 | return dmi_vendor->id; |
9f63a08d | 213 | } |
b52aae1d | 214 | } |
932feb79 | 215 | log_debug("No virtualization found in DMI vendor table."); |
ce350379 NM |
216 | return VIRTUALIZATION_NONE; |
217 | } | |
218 | ||
219 | static int detect_vm_smbios(void) { | |
627cdcc7 | 220 | /* The SMBIOS BIOS Characteristics Extension Byte 2 (Section 2.1.2.2 of |
ce350379 NM |
221 | * https://www.dmtf.org/sites/default/files/standards/documents/DSP0134_3.4.0.pdf), specifies that |
222 | * the 4th bit being set indicates a VM. The BIOS Characteristics table is exposed via the kernel in | |
223 | * /sys/firmware/dmi/entries/0-0. Note that in the general case, this bit being unset should not | |
224 | * imply that the system is running on bare-metal. For example, QEMU 3.1.0 (with or without KVM) | |
225 | * with SeaBIOS does not set this bit. */ | |
226 | _cleanup_free_ char *s = NULL; | |
227 | size_t readsize; | |
228 | int r; | |
229 | ||
230 | r = read_full_virtual_file("/sys/firmware/dmi/entries/0-0/raw", &s, &readsize); | |
231 | if (r < 0) { | |
932feb79 YW |
232 | log_debug_errno(r, "Unable to read /sys/firmware/dmi/entries/0-0/raw, " |
233 | "using the virtualization information found in DMI vendor table, ignoring: %m"); | |
ce350379 NM |
234 | return SMBIOS_VM_BIT_UNKNOWN; |
235 | } | |
236 | if (readsize < 20 || s[1] < 20) { | |
237 | /* The spec indicates that byte 1 contains the size of the table, 0x12 + the number of | |
238 | * extension bytes. The data we're interested in is in extension byte 2, which would be at | |
239 | * 0x13. If we didn't read that much data, or if the BIOS indicates that we don't have that | |
240 | * much data, we don't infer anything from the SMBIOS. */ | |
932feb79 YW |
241 | log_debug("Only read %zu bytes from /sys/firmware/dmi/entries/0-0/raw (expected 20). " |
242 | "Using the virtualization information found in DMI vendor table.", readsize); | |
ce350379 NM |
243 | return SMBIOS_VM_BIT_UNKNOWN; |
244 | } | |
bdb628ee | 245 | |
ce350379 NM |
246 | uint8_t byte = (uint8_t) s[19]; |
247 | if (byte & (1U<<4)) { | |
932feb79 | 248 | log_debug("DMI BIOS Extension table indicates virtualization."); |
ce350379 NM |
249 | return SMBIOS_VM_BIT_SET; |
250 | } | |
932feb79 | 251 | log_debug("DMI BIOS Extension table does not indicate virtualization."); |
ce350379 NM |
252 | return SMBIOS_VM_BIT_UNSET; |
253 | } | |
f106a639 | 254 | #endif /* defined(__i386__) || defined(__x86_64__) || defined(__arm__) || defined(__aarch64__) || defined(__loongarch_lp64) */ |
ce350379 | 255 | |
1b86c7c5 | 256 | static Virtualization detect_vm_dmi(void) { |
f106a639 | 257 | #if defined(__i386__) || defined(__x86_64__) || defined(__arm__) || defined(__aarch64__) || defined(__loongarch_lp64) |
ce350379 NM |
258 | |
259 | int r; | |
260 | r = detect_vm_dmi_vendor(); | |
261 | ||
262 | /* The DMI vendor tables in /sys/class/dmi/id don't help us distinguish between Amazon EC2 | |
263 | * virtual machines and bare-metal instances, so we need to look at SMBIOS. */ | |
f90eea7d BH |
264 | if (r == VIRTUALIZATION_AMAZON) { |
265 | switch (detect_vm_smbios()) { | |
266 | case SMBIOS_VM_BIT_SET: | |
267 | return VIRTUALIZATION_AMAZON; | |
268 | case SMBIOS_VM_BIT_UNSET: | |
269 | return VIRTUALIZATION_NONE; | |
270 | case SMBIOS_VM_BIT_UNKNOWN: { | |
271 | /* The DMI information we are after is only accessible to the root user, | |
272 | * so we fallback to using the product name which is less restricted | |
273 | * to distinguish metal systems from virtualized instances */ | |
274 | _cleanup_free_ char *s = NULL; | |
aab896e2 | 275 | const char *e; |
f90eea7d BH |
276 | |
277 | r = read_full_virtual_file("/sys/class/dmi/id/product_name", &s, NULL); | |
278 | /* In EC2, virtualized is much more common than metal, so if for some reason | |
279 | * we fail to read the DMI data, assume we are virtualized. */ | |
280 | if (r < 0) { | |
281 | log_debug_errno(r, "Can't read /sys/class/dmi/id/product_name," | |
282 | " assuming virtualized: %m"); | |
283 | return VIRTUALIZATION_AMAZON; | |
284 | } | |
aab896e2 BH |
285 | e = strstrafter(truncate_nl(s), ".metal"); |
286 | if (e && IN_SET(*e, 0, '-')) { | |
287 | log_debug("DMI product name has '.metal', assuming no virtualization"); | |
f90eea7d BH |
288 | return VIRTUALIZATION_NONE; |
289 | } else | |
290 | return VIRTUALIZATION_AMAZON; | |
291 | } | |
292 | default: | |
293 | assert_not_reached(); | |
294 | } | |
295 | } | |
9f63a08d | 296 | |
ce350379 NM |
297 | /* If we haven't identified a VM, but the firmware indicates that there is one, indicate as much. We |
298 | * have no further information about what it is. */ | |
299 | if (r == VIRTUALIZATION_NONE && detect_vm_smbios() == SMBIOS_VM_BIT_SET) | |
300 | return VIRTUALIZATION_VM_OTHER; | |
301 | return r; | |
302 | #else | |
75f86906 | 303 | return VIRTUALIZATION_NONE; |
ce350379 | 304 | #endif |
bdb628ee ZJS |
305 | } |
306 | ||
575e6588 OH |
307 | #define XENFEAT_dom0 11 /* xen/include/public/features.h */ |
308 | #define PATH_FEATURES "/sys/hypervisor/properties/features" | |
1a8e4148 OH |
309 | /* Returns -errno, or 0 for domU, or 1 for dom0 */ |
310 | static int detect_vm_xen_dom0(void) { | |
75f86906 | 311 | _cleanup_free_ char *domcap = NULL; |
bdb628ee | 312 | int r; |
b52aae1d | 313 | |
575e6588 OH |
314 | r = read_one_line_file(PATH_FEATURES, &domcap); |
315 | if (r < 0 && r != -ENOENT) | |
316 | return r; | |
d6062e3b | 317 | if (r >= 0) { |
575e6588 OH |
318 | unsigned long features; |
319 | ||
47dbb99a YW |
320 | /* Here, we need to use sscanf() instead of safe_atoul() |
321 | * as the string lacks the leading "0x". */ | |
13e0f9fe OH |
322 | r = sscanf(domcap, "%lx", &features); |
323 | if (r == 1) { | |
575e6588 OH |
324 | r = !!(features & (1U << XENFEAT_dom0)); |
325 | log_debug("Virtualization XEN, found %s with value %08lx, " | |
326 | "XENFEAT_dom0 (indicating the 'hardware domain') is%s set.", | |
327 | PATH_FEATURES, features, r ? "" : " not"); | |
328 | return r; | |
329 | } | |
330 | log_debug("Virtualization XEN, found %s, unhandled content '%s'", | |
331 | PATH_FEATURES, domcap); | |
332 | } | |
333 | ||
75f86906 | 334 | r = read_one_line_file("/proc/xen/capabilities", &domcap); |
9f63a08d | 335 | if (r == -ENOENT) { |
1a8e4148 OH |
336 | log_debug("Virtualization XEN because /proc/xen/capabilities does not exist"); |
337 | return 0; | |
9f63a08d | 338 | } |
d5b687e7 LP |
339 | if (r < 0) |
340 | return r; | |
bdb628ee | 341 | |
31a9be23 YW |
342 | for (const char *i = domcap;;) { |
343 | _cleanup_free_ char *cap = NULL; | |
9f63a08d | 344 | |
31a9be23 YW |
345 | r = extract_first_word(&i, &cap, ",", 0); |
346 | if (r < 0) | |
347 | return r; | |
348 | if (r == 0) { | |
349 | log_debug("Virtualization XEN DomU found (/proc/xen/capabilities)"); | |
350 | return 0; | |
351 | } | |
352 | ||
353 | if (streq(cap, "control_d")) { | |
354 | log_debug("Virtualization XEN Dom0 ignored (/proc/xen/capabilities)"); | |
355 | return 1; | |
356 | } | |
357 | } | |
75f86906 | 358 | } |
37287585 | 359 | |
1b86c7c5 | 360 | static Virtualization detect_vm_xen(void) { |
ea583ed5 RN |
361 | /* The presence of /proc/xen indicates some form of a Xen domain |
362 | The check for Dom0 is handled outside this function */ | |
599be274 BS |
363 | if (access("/proc/xen", F_OK) < 0) { |
364 | log_debug("Virtualization XEN not found, /proc/xen does not exist"); | |
365 | return VIRTUALIZATION_NONE; | |
366 | } | |
367 | log_debug("Virtualization XEN found (/proc/xen exists)"); | |
599be274 BS |
368 | return VIRTUALIZATION_XEN; |
369 | } | |
370 | ||
1b86c7c5 | 371 | static Virtualization detect_vm_hypervisor(void) { |
75f86906 LP |
372 | _cleanup_free_ char *hvtype = NULL; |
373 | int r; | |
37287585 | 374 | |
75f86906 LP |
375 | r = read_one_line_file("/sys/hypervisor/type", &hvtype); |
376 | if (r == -ENOENT) | |
377 | return VIRTUALIZATION_NONE; | |
378 | if (r < 0) | |
379 | return r; | |
37287585 | 380 | |
9f63a08d SS |
381 | log_debug("Virtualization %s found in /sys/hypervisor/type", hvtype); |
382 | ||
75f86906 LP |
383 | if (streq(hvtype, "xen")) |
384 | return VIRTUALIZATION_XEN; | |
385 | else | |
386 | return VIRTUALIZATION_VM_OTHER; | |
387 | } | |
37287585 | 388 | |
1b86c7c5 | 389 | static Virtualization detect_vm_uml(void) { |
6058516a | 390 | _cleanup_fclose_ FILE *f = NULL; |
75f86906 | 391 | int r; |
37287585 | 392 | |
75f86906 | 393 | /* Detect User-Mode Linux by reading /proc/cpuinfo */ |
6058516a ZJS |
394 | f = fopen("/proc/cpuinfo", "re"); |
395 | if (!f) { | |
396 | if (errno == ENOENT) { | |
397 | log_debug("/proc/cpuinfo not found, assuming no UML virtualization."); | |
398 | return VIRTUALIZATION_NONE; | |
399 | } | |
400 | return -errno; | |
ef2a48aa | 401 | } |
9f63a08d | 402 | |
6058516a ZJS |
403 | for (;;) { |
404 | _cleanup_free_ char *line = NULL; | |
405 | const char *t; | |
406 | ||
407 | r = read_line(f, LONG_LINE_MAX, &line); | |
408 | if (r < 0) | |
409 | return r; | |
410 | if (r == 0) | |
411 | break; | |
412 | ||
413 | t = startswith(line, "vendor_id\t: "); | |
414 | if (t) { | |
415 | if (startswith(t, "User Mode Linux")) { | |
416 | log_debug("UML virtualization found in /proc/cpuinfo"); | |
417 | return VIRTUALIZATION_UML; | |
418 | } | |
419 | ||
420 | break; | |
421 | } | |
9f63a08d | 422 | } |
bdb628ee | 423 | |
ef2a48aa | 424 | log_debug("UML virtualization not found in /proc/cpuinfo."); |
75f86906 LP |
425 | return VIRTUALIZATION_NONE; |
426 | } | |
e32886e0 | 427 | |
1b86c7c5 | 428 | static Virtualization detect_vm_zvm(void) { |
bdb628ee | 429 | |
75f86906 LP |
430 | #if defined(__s390__) |
431 | _cleanup_free_ char *t = NULL; | |
432 | int r; | |
e32886e0 | 433 | |
15036f85 MY |
434 | r = get_proc_field("/proc/sysinfo", "VM00 Control Program", &t); |
435 | if (IN_SET(r, -ENOENT, -ENODATA)) | |
75f86906 LP |
436 | return VIRTUALIZATION_NONE; |
437 | if (r < 0) | |
438 | return r; | |
e32886e0 | 439 | |
9f63a08d | 440 | log_debug("Virtualization %s found in /proc/sysinfo", t); |
75f86906 LP |
441 | if (streq(t, "z/VM")) |
442 | return VIRTUALIZATION_ZVM; | |
443 | else | |
444 | return VIRTUALIZATION_KVM; | |
445 | #else | |
9f63a08d | 446 | log_debug("This platform does not support /proc/sysinfo"); |
75f86906 LP |
447 | return VIRTUALIZATION_NONE; |
448 | #endif | |
449 | } | |
e32886e0 | 450 | |
75f86906 | 451 | /* Returns a short identifier for the various VM implementations */ |
1b86c7c5 LP |
452 | Virtualization detect_vm(void) { |
453 | static thread_local Virtualization cached_found = _VIRTUALIZATION_INVALID; | |
cea9216a | 454 | bool other = false, hyperv = false; |
1b86c7c5 LP |
455 | int xen_dom0 = 0; |
456 | Virtualization v, dmi; | |
e32886e0 | 457 | |
75f86906 LP |
458 | if (cached_found >= 0) |
459 | return cached_found; | |
bdb628ee | 460 | |
f6875b0a | 461 | /* We have to use the correct order here: |
f6875b0a | 462 | * |
2dc65c32 ZJS |
463 | * → First, try to detect Oracle Virtualbox, Amazon EC2 Nitro, Parallels, and Google Compute Engine, |
464 | * even if they use KVM, as well as Xen, even if it cloaks as Microsoft Hyper-V. Attempt to detect | |
465 | * UML at this stage too, since it runs as a user-process nested inside other VMs. Also check for | |
466 | * Xen now, because Xen PV mode does not override CPUID when nested inside another hypervisor. | |
f2fe2865 | 467 | * |
2dc65c32 | 468 | * → Second, try to detect from CPUID. This will report KVM for whatever software is used even if |
840a49f3 | 469 | * info in DMI is overwritten. |
f2fe2865 LP |
470 | * |
471 | * → Third, try to detect from DMI. */ | |
5f1c788c | 472 | |
28b1a3ea | 473 | dmi = detect_vm_dmi(); |
840a49f3 YW |
474 | if (IN_SET(dmi, |
475 | VIRTUALIZATION_ORACLE, | |
476 | VIRTUALIZATION_XEN, | |
477 | VIRTUALIZATION_AMAZON, | |
baa90b4b | 478 | VIRTUALIZATION_PARALLELS, |
479 | VIRTUALIZATION_GOOGLE)) { | |
1b86c7c5 | 480 | v = dmi; |
2f8e375d BR |
481 | goto finish; |
482 | } | |
5f1c788c | 483 | |
c8037dbf | 484 | /* Detect UML */ |
1b86c7c5 LP |
485 | v = detect_vm_uml(); |
486 | if (v < 0) | |
487 | return v; | |
488 | if (v != VIRTUALIZATION_NONE) | |
c8037dbf CO |
489 | goto finish; |
490 | ||
599be274 | 491 | /* Detect Xen */ |
1b86c7c5 LP |
492 | v = detect_vm_xen(); |
493 | if (v < 0) | |
494 | return v; | |
495 | if (v == VIRTUALIZATION_XEN) { | |
ea583ed5 RN |
496 | /* If we are Dom0, then we expect to not report as a VM. However, as we might be nested |
497 | * inside another hypervisor which can be detected via the CPUID check, wait to report this | |
498 | * until after the CPUID check. */ | |
499 | xen_dom0 = detect_vm_xen_dom0(); | |
500 | if (xen_dom0 < 0) | |
501 | return xen_dom0; | |
502 | if (xen_dom0 == 0) | |
503 | goto finish; | |
1b86c7c5 | 504 | } else if (v != VIRTUALIZATION_NONE) |
ea583ed5 | 505 | assert_not_reached(); |
599be274 | 506 | |
c8037dbf | 507 | /* Detect from CPUID */ |
1b86c7c5 LP |
508 | v = detect_vm_cpuid(); |
509 | if (v < 0) | |
510 | return v; | |
cea9216a LP |
511 | if (v == VIRTUALIZATION_MICROSOFT) |
512 | /* QEMU sets the CPUID string to hyperv's, in case it provides hyperv enlightenments. Let's | |
513 | * hence not return Microsoft here but just use the other mechanisms first to make a better | |
514 | * decision. */ | |
515 | hyperv = true; | |
516 | else if (v == VIRTUALIZATION_VM_OTHER) | |
c2b19b3c | 517 | other = true; |
1b86c7c5 | 518 | else if (v != VIRTUALIZATION_NONE) |
c2b19b3c | 519 | goto finish; |
d831deb5 | 520 | |
ea583ed5 RN |
521 | /* If we are in Dom0 and have not yet finished, finish with the result of detect_vm_cpuid */ |
522 | if (xen_dom0 > 0) | |
523 | goto finish; | |
524 | ||
c2b19b3c LP |
525 | /* Now, let's get back to DMI */ |
526 | if (dmi < 0) | |
527 | return dmi; | |
528 | if (dmi == VIRTUALIZATION_VM_OTHER) | |
529 | other = true; | |
530 | else if (dmi != VIRTUALIZATION_NONE) { | |
1b86c7c5 | 531 | v = dmi; |
c2b19b3c | 532 | goto finish; |
530c1c30 | 533 | } |
b52aae1d | 534 | |
599be274 | 535 | /* Check high-level hypervisor sysfs file */ |
1b86c7c5 LP |
536 | v = detect_vm_hypervisor(); |
537 | if (v < 0) | |
538 | return v; | |
539 | if (v == VIRTUALIZATION_VM_OTHER) | |
c2b19b3c | 540 | other = true; |
1b86c7c5 | 541 | else if (v != VIRTUALIZATION_NONE) |
c2b19b3c | 542 | goto finish; |
f41925b4 | 543 | |
1b86c7c5 LP |
544 | v = detect_vm_device_tree(); |
545 | if (v < 0) | |
546 | return v; | |
547 | if (v == VIRTUALIZATION_VM_OTHER) | |
c2b19b3c | 548 | other = true; |
1b86c7c5 | 549 | else if (v != VIRTUALIZATION_NONE) |
c2b19b3c | 550 | goto finish; |
f41925b4 | 551 | |
1b86c7c5 LP |
552 | v = detect_vm_zvm(); |
553 | if (v < 0) | |
554 | return v; | |
0fb533a5 LP |
555 | |
556 | finish: | |
cea9216a LP |
557 | /* None of the checks above gave us a clear answer, hence let's now use fallback logic: if hyperv |
558 | * enlightenments are available but the VMM wasn't recognized as anything yet, it's probably | |
559 | * Microsoft. */ | |
560 | if (v == VIRTUALIZATION_NONE) { | |
561 | if (hyperv) | |
562 | v = VIRTUALIZATION_MICROSOFT; | |
563 | else if (other) | |
564 | v = VIRTUALIZATION_VM_OTHER; | |
565 | } | |
3f61278b | 566 | |
1b86c7c5 LP |
567 | cached_found = v; |
568 | log_debug("Found VM virtualization %s", virtualization_to_string(v)); | |
569 | return v; | |
b52aae1d LP |
570 | } |
571 | ||
735ea55f YW |
572 | static const char *const container_table[_VIRTUALIZATION_MAX] = { |
573 | [VIRTUALIZATION_LXC] = "lxc", | |
574 | [VIRTUALIZATION_LXC_LIBVIRT] = "lxc-libvirt", | |
575 | [VIRTUALIZATION_SYSTEMD_NSPAWN] = "systemd-nspawn", | |
576 | [VIRTUALIZATION_DOCKER] = "docker", | |
577 | [VIRTUALIZATION_PODMAN] = "podman", | |
578 | [VIRTUALIZATION_RKT] = "rkt", | |
579 | [VIRTUALIZATION_WSL] = "wsl", | |
80cc3e3e | 580 | [VIRTUALIZATION_PROOT] = "proot", |
abac810b | 581 | [VIRTUALIZATION_POUCH] = "pouch", |
735ea55f | 582 | }; |
0fb533a5 | 583 | |
735ea55f YW |
584 | DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(container, int); |
585 | ||
193bf42a LB |
586 | static int running_in_pidns(void) { |
587 | int r; | |
588 | ||
589 | r = namespace_is_init(NAMESPACE_PID); | |
590 | if (r < 0) | |
591 | return log_debug_errno(r, "Failed to test if in root PID namespace, ignoring: %m"); | |
592 | ||
593 | return !r; | |
594 | } | |
595 | ||
1b86c7c5 | 596 | static Virtualization detect_container_files(void) { |
a4a9a6f7 SB |
597 | static const struct { |
598 | const char *file_path; | |
1b86c7c5 | 599 | Virtualization id; |
a4a9a6f7 SB |
600 | } container_file_table[] = { |
601 | /* https://github.com/containers/podman/issues/6192 */ | |
602 | /* https://github.com/containers/podman/issues/3586#issuecomment-661918679 */ | |
603 | { "/run/.containerenv", VIRTUALIZATION_PODMAN }, | |
604 | /* https://github.com/moby/moby/issues/18355 */ | |
605 | /* Docker must be the last in this table, see below. */ | |
606 | { "/.dockerenv", VIRTUALIZATION_DOCKER }, | |
607 | }; | |
608 | ||
ddb8a639 I |
609 | FOREACH_ELEMENT(file, container_file_table) { |
610 | if (access(file->file_path, F_OK) >= 0) | |
611 | return file->id; | |
a4a9a6f7 SB |
612 | |
613 | if (errno != ENOENT) | |
614 | log_debug_errno(errno, | |
615 | "Checking if %s exists failed, ignoring: %m", | |
ddb8a639 | 616 | file->file_path); |
a4a9a6f7 SB |
617 | } |
618 | ||
619 | return VIRTUALIZATION_NONE; | |
620 | } | |
621 | ||
1b86c7c5 LP |
622 | Virtualization detect_container(void) { |
623 | static thread_local Virtualization cached_found = _VIRTUALIZATION_INVALID; | |
a7e508f8 | 624 | _cleanup_free_ char *m = NULL, *o = NULL, *p = NULL; |
75f86906 | 625 | const char *e = NULL; |
1b86c7c5 | 626 | Virtualization v; |
ab94af92 | 627 | int r; |
b52aae1d | 628 | |
75f86906 | 629 | if (cached_found >= 0) |
0fb533a5 | 630 | return cached_found; |
0fb533a5 | 631 | |
8d6e8034 | 632 | /* /proc/vz exists in container and outside of the container, /proc/bc only outside of the container. */ |
b7ec9e71 LP |
633 | if (access("/proc/vz", F_OK) < 0) { |
634 | if (errno != ENOENT) | |
635 | log_debug_errno(errno, "Failed to check if /proc/vz exists, ignoring: %m"); | |
636 | } else if (access("/proc/bc", F_OK) < 0) { | |
637 | if (errno == ENOENT) { | |
1b86c7c5 | 638 | v = VIRTUALIZATION_OPENVZ; |
b7ec9e71 LP |
639 | goto finish; |
640 | } | |
641 | ||
642 | log_debug_errno(errno, "Failed to check if /proc/bc exists, ignoring: %m"); | |
b52aae1d LP |
643 | } |
644 | ||
4096043f | 645 | /* "Official" way of detecting WSL https://github.com/Microsoft/WSL/issues/423#issuecomment-221627364 */ |
6c8a2c67 | 646 | r = read_one_line_file("/proc/sys/kernel/osrelease", &o); |
b7ec9e71 LP |
647 | if (r < 0) |
648 | log_debug_errno(r, "Failed to read /proc/sys/kernel/osrelease, ignoring: %m"); | |
649 | else if (strstr(o, "Microsoft") || strstr(o, "WSL")) { | |
1b86c7c5 | 650 | v = VIRTUALIZATION_WSL; |
a2f838d5 | 651 | goto finish; |
6c8a2c67 BR |
652 | } |
653 | ||
80cc3e3e CD |
654 | /* proot doesn't use PID namespacing, so we can just check if we have a matching tracer for this |
655 | * invocation without worrying about it being elsewhere. | |
656 | */ | |
15036f85 | 657 | r = get_proc_field("/proc/self/status", "TracerPid", &p); |
b7ec9e71 LP |
658 | if (r < 0) |
659 | log_debug_errno(r, "Failed to read our own trace PID, ignoring: %m"); | |
660 | else if (!streq(p, "0")) { | |
80cc3e3e | 661 | pid_t ptrace_pid; |
b7ec9e71 | 662 | |
80cc3e3e | 663 | r = parse_pid(p, &ptrace_pid); |
b7ec9e71 LP |
664 | if (r < 0) |
665 | log_debug_errno(r, "Failed to parse our own tracer PID, ignoring: %m"); | |
666 | else { | |
80cc3e3e | 667 | _cleanup_free_ char *ptrace_comm = NULL; |
b7ec9e71 LP |
668 | const char *pf; |
669 | ||
670 | pf = procfs_file_alloca(ptrace_pid, "comm"); | |
80cc3e3e | 671 | r = read_one_line_file(pf, &ptrace_comm); |
b7ec9e71 LP |
672 | if (r < 0) |
673 | log_debug_errno(r, "Failed to read %s, ignoring: %m", pf); | |
674 | else if (startswith(ptrace_comm, "proot")) { | |
1b86c7c5 | 675 | v = VIRTUALIZATION_PROOT; |
9b4f3fa3 CD |
676 | goto finish; |
677 | } | |
80cc3e3e CD |
678 | } |
679 | } | |
680 | ||
79efcd02 | 681 | /* The container manager might have placed this in the /run/host/ hierarchy for us, which is best |
0f48ba7b LP |
682 | * because we can be consumed just like that, without special privileges. */ |
683 | r = read_one_line_file("/run/host/container-manager", &m); | |
684 | if (r > 0) { | |
685 | e = m; | |
686 | goto translate_name; | |
687 | } | |
688 | if (!IN_SET(r, -ENOENT, 0)) | |
79efcd02 | 689 | return log_debug_errno(r, "Failed to read /run/host/container-manager: %m"); |
0f48ba7b | 690 | |
df0ff127 | 691 | if (getpid_cached() == 1) { |
342bed02 ZJS |
692 | /* If we are PID 1 we can just check our own environment variable, and that's authoritative. |
693 | * We distinguish three cases: | |
694 | * - the variable is not defined → we jump to other checks | |
695 | * - the variable is defined to an empty value → we are not in a container | |
696 | * - anything else → some container, either one of the known ones or "container-other" | |
697 | */ | |
fdd25311 | 698 | e = getenv("container"); |
342bed02 | 699 | if (!e) |
a4a9a6f7 | 700 | goto check_files; |
fdd25311 | 701 | if (isempty(e)) { |
1b86c7c5 | 702 | v = VIRTUALIZATION_NONE; |
fdd25311 LP |
703 | goto finish; |
704 | } | |
fdd25311 | 705 | |
8d6e8034 LP |
706 | goto translate_name; |
707 | } | |
708 | ||
709 | /* Otherwise, PID 1 might have dropped this information into a file in /run. This is better than accessing | |
710 | * /proc/1/environ, since we don't need CAP_SYS_PTRACE for that. */ | |
711 | r = read_one_line_file("/run/systemd/container", &m); | |
a2176045 | 712 | if (r > 0) { |
8d6e8034 LP |
713 | e = m; |
714 | goto translate_name; | |
715 | } | |
a2176045 | 716 | if (!IN_SET(r, -ENOENT, 0)) |
8d6e8034 LP |
717 | return log_debug_errno(r, "Failed to read /run/systemd/container: %m"); |
718 | ||
719 | /* Fallback for cases where PID 1 was not systemd (for example, cases where init=/bin/sh is used. */ | |
720 | r = getenv_for_pid(1, "container", &m); | |
721 | if (r > 0) { | |
fdd25311 | 722 | e = m; |
8d6e8034 | 723 | goto translate_name; |
fdd25311 | 724 | } |
8d6e8034 LP |
725 | if (r < 0) /* This only works if we have CAP_SYS_PTRACE, hence let's better ignore failures here */ |
726 | log_debug_errno(r, "Failed to read $container of PID 1, ignoring: %m"); | |
727 | ||
a4a9a6f7 SB |
728 | check_files: |
729 | /* Check for existence of some well-known files. We only do this after checking | |
730 | * for other specific container managers, otherwise we risk mistaking another | |
731 | * container manager for Docker: the /.dockerenv file could inadvertently end up | |
732 | * in a file system image. */ | |
1b86c7c5 LP |
733 | v = detect_container_files(); |
734 | if (v < 0) | |
735 | return v; | |
736 | if (v != VIRTUALIZATION_NONE) | |
a4a9a6f7 SB |
737 | goto finish; |
738 | ||
193bf42a LB |
739 | /* Finally, the root pid namespace has an hardcoded inode number of 0xEFFFFFFC since kernel 3.8, so |
740 | * if all else fails we can check the inode number of our pid namespace and compare it. */ | |
741 | if (running_in_pidns() > 0) { | |
742 | log_debug("Running in a pid namespace, assuming unknown container manager."); | |
743 | v = VIRTUALIZATION_CONTAINER_OTHER; | |
744 | goto finish; | |
745 | } | |
746 | ||
a4a9a6f7 | 747 | /* If none of that worked, give up, assume no container manager. */ |
1b86c7c5 | 748 | v = VIRTUALIZATION_NONE; |
8d6e8034 | 749 | goto finish; |
b52aae1d | 750 | |
8d6e8034 | 751 | translate_name: |
a4a9a6f7 SB |
752 | if (streq(e, "oci")) { |
753 | /* Some images hardcode container=oci, but OCI is not a specific container manager. | |
754 | * Try to detect one based on well-known files. */ | |
1b86c7c5 | 755 | v = detect_container_files(); |
a91078bc | 756 | if (v == VIRTUALIZATION_NONE) |
1b86c7c5 | 757 | v = VIRTUALIZATION_CONTAINER_OTHER; |
a4a9a6f7 SB |
758 | goto finish; |
759 | } | |
1b86c7c5 LP |
760 | v = container_from_string(e); |
761 | if (v < 0) | |
762 | v = VIRTUALIZATION_CONTAINER_OTHER; | |
fdd25311 | 763 | |
0fb533a5 | 764 | finish: |
1b86c7c5 LP |
765 | log_debug("Found container virtualization %s.", virtualization_to_string(v)); |
766 | cached_found = v; | |
767 | return v; | |
b52aae1d LP |
768 | } |
769 | ||
1b86c7c5 LP |
770 | Virtualization detect_virtualization(void) { |
771 | int v; | |
b52aae1d | 772 | |
1b86c7c5 LP |
773 | v = detect_container(); |
774 | if (v != VIRTUALIZATION_NONE) | |
775 | return v; | |
b52aae1d | 776 | |
1b86c7c5 | 777 | return detect_vm(); |
b52aae1d | 778 | } |
75f86906 | 779 | |
299a34c1 | 780 | int running_in_userns(void) { |
299a34c1 ZJS |
781 | int r; |
782 | ||
a2429f50 LP |
783 | r = namespace_is_init(NAMESPACE_USER); |
784 | if (r < 0) | |
9948b466 | 785 | return log_debug_errno(r, "Failed to test if in root user namespace, ignoring: %m"); |
299a34c1 | 786 | |
9948b466 | 787 | return !r; |
299a34c1 ZJS |
788 | } |
789 | ||
7f4b3c5e | 790 | int running_in_chroot(void) { |
ef2a48aa | 791 | int r; |
7f4b3c5e | 792 | |
1a25a77f ZJS |
793 | /* If we're PID1, /proc may not be mounted (and most likely we're not in a chroot). But PID1 will |
794 | * mount /proc, so all other programs can assume that if /proc is *not* available, we're in some | |
795 | * chroot. */ | |
796 | ||
2701c2f6 DDM |
797 | r = getenv_bool("SYSTEMD_IN_CHROOT"); |
798 | if (r >= 0) | |
799 | return r > 0; | |
800 | if (r != -ENXIO) | |
801 | log_debug_errno(r, "Failed to parse $SYSTEMD_IN_CHROOT, ignoring: %m"); | |
802 | ||
803 | /* Deprecated but kept for backwards compatibility. */ | |
08a28eec LN |
804 | if (getenv_bool("SYSTEMD_IGNORE_CHROOT") > 0) |
805 | return 0; | |
806 | ||
d6267b9b LP |
807 | r = pidref_from_same_root_fs(&PIDREF_MAKE_FROM_PID(1), NULL); |
808 | if (r == -ENOSYS) { | |
809 | if (getpid_cached() == 1) | |
810 | return false; /* We will mount /proc, assuming we're not in a chroot. */ | |
7636caf5 | 811 | |
d6267b9b LP |
812 | log_debug("/proc/ is not mounted, assuming we're in a chroot."); |
813 | return true; | |
1a25a77f | 814 | } |
d6267b9b LP |
815 | if (r == -ESRCH) /* We must have a fake /proc/, we can't do the check properly. */ |
816 | return -ENOSYS; | |
ef2a48aa ZJS |
817 | if (r < 0) |
818 | return r; | |
7f4b3c5e | 819 | |
ef2a48aa | 820 | return r == 0; |
7f4b3c5e LP |
821 | } |
822 | ||
68337e55 GS |
823 | #if defined(__i386__) || defined(__x86_64__) |
824 | struct cpuid_table_entry { | |
825 | uint32_t flag_bit; | |
826 | const char *name; | |
827 | }; | |
828 | ||
829 | static const struct cpuid_table_entry leaf1_edx[] = { | |
5c86cec1 YW |
830 | { 0, "fpu" }, |
831 | { 1, "vme" }, | |
832 | { 2, "de" }, | |
833 | { 3, "pse" }, | |
834 | { 4, "tsc" }, | |
835 | { 5, "msr" }, | |
836 | { 6, "pae" }, | |
837 | { 7, "mce" }, | |
838 | { 8, "cx8" }, | |
839 | { 9, "apic" }, | |
840 | { 11, "sep" }, | |
841 | { 12, "mtrr" }, | |
842 | { 13, "pge" }, | |
843 | { 14, "mca" }, | |
844 | { 15, "cmov" }, | |
845 | { 16, "pat" }, | |
846 | { 17, "pse36" }, | |
68337e55 | 847 | { 19, "clflush" }, |
5c86cec1 YW |
848 | { 23, "mmx" }, |
849 | { 24, "fxsr" }, | |
850 | { 25, "sse" }, | |
851 | { 26, "sse2" }, | |
852 | { 28, "ht" }, | |
68337e55 GS |
853 | }; |
854 | ||
855 | static const struct cpuid_table_entry leaf1_ecx[] = { | |
5c86cec1 YW |
856 | { 0, "pni" }, |
857 | { 1, "pclmul" }, | |
68337e55 | 858 | { 3, "monitor" }, |
5c86cec1 YW |
859 | { 9, "ssse3" }, |
860 | { 12, "fma3" }, | |
861 | { 13, "cx16" }, | |
862 | { 19, "sse4_1" }, | |
863 | { 20, "sse4_2" }, | |
864 | { 22, "movbe" }, | |
865 | { 23, "popcnt" }, | |
866 | { 25, "aes" }, | |
867 | { 26, "xsave" }, | |
68337e55 | 868 | { 27, "osxsave" }, |
5c86cec1 YW |
869 | { 28, "avx" }, |
870 | { 29, "f16c" }, | |
871 | { 30, "rdrand" }, | |
68337e55 GS |
872 | }; |
873 | ||
874 | static const struct cpuid_table_entry leaf7_ebx[] = { | |
5c86cec1 YW |
875 | { 3, "bmi1" }, |
876 | { 5, "avx2" }, | |
877 | { 8, "bmi2" }, | |
68337e55 | 878 | { 18, "rdseed" }, |
5c86cec1 | 879 | { 19, "adx" }, |
68337e55 GS |
880 | { 29, "sha_ni" }, |
881 | }; | |
882 | ||
883 | static const struct cpuid_table_entry leaf81_edx[] = { | |
884 | { 11, "syscall" }, | |
5c86cec1 YW |
885 | { 27, "rdtscp" }, |
886 | { 29, "lm" }, | |
68337e55 GS |
887 | }; |
888 | ||
889 | static const struct cpuid_table_entry leaf81_ecx[] = { | |
890 | { 0, "lahf_lm" }, | |
5c86cec1 | 891 | { 5, "abm" }, |
68337e55 GS |
892 | }; |
893 | ||
894 | static const struct cpuid_table_entry leaf87_edx[] = { | |
895 | { 8, "constant_tsc" }, | |
896 | }; | |
897 | ||
898 | static bool given_flag_in_set(const char *flag, const struct cpuid_table_entry *set, size_t set_size, uint32_t val) { | |
899 | for (size_t i = 0; i < set_size; i++) { | |
900 | if ((UINT32_C(1) << set[i].flag_bit) & val && | |
901 | streq(flag, set[i].name)) | |
902 | return true; | |
903 | } | |
904 | return false; | |
905 | } | |
906 | ||
907 | static bool real_has_cpu_with_flag(const char *flag) { | |
908 | uint32_t eax, ebx, ecx, edx; | |
909 | ||
910 | if (__get_cpuid(1, &eax, &ebx, &ecx, &edx)) { | |
911 | if (given_flag_in_set(flag, leaf1_ecx, ELEMENTSOF(leaf1_ecx), ecx)) | |
912 | return true; | |
913 | ||
914 | if (given_flag_in_set(flag, leaf1_edx, ELEMENTSOF(leaf1_edx), edx)) | |
915 | return true; | |
916 | } | |
917 | ||
e7014399 | 918 | if (__get_cpuid_count(7, 0, &eax, &ebx, &ecx, &edx)) { |
68337e55 GS |
919 | if (given_flag_in_set(flag, leaf7_ebx, ELEMENTSOF(leaf7_ebx), ebx)) |
920 | return true; | |
921 | } | |
922 | ||
923 | if (__get_cpuid(0x80000001U, &eax, &ebx, &ecx, &edx)) { | |
924 | if (given_flag_in_set(flag, leaf81_ecx, ELEMENTSOF(leaf81_ecx), ecx)) | |
925 | return true; | |
926 | ||
927 | if (given_flag_in_set(flag, leaf81_edx, ELEMENTSOF(leaf81_edx), edx)) | |
928 | return true; | |
929 | } | |
930 | ||
931 | if (__get_cpuid(0x80000007U, &eax, &ebx, &ecx, &edx)) | |
932 | if (given_flag_in_set(flag, leaf87_edx, ELEMENTSOF(leaf87_edx), edx)) | |
933 | return true; | |
934 | ||
935 | return false; | |
936 | } | |
937 | #endif | |
938 | ||
939 | bool has_cpu_with_flag(const char *flag) { | |
940 | /* CPUID is an x86 specific interface. Assume on all others that no CPUs have those flags. */ | |
941 | #if defined(__i386__) || defined(__x86_64__) | |
942 | return real_has_cpu_with_flag(flag); | |
943 | #else | |
944 | return false; | |
945 | #endif | |
946 | } | |
947 | ||
75f86906 | 948 | static const char *const virtualization_table[_VIRTUALIZATION_MAX] = { |
5c86cec1 YW |
949 | [VIRTUALIZATION_NONE] = "none", |
950 | [VIRTUALIZATION_KVM] = "kvm", | |
951 | [VIRTUALIZATION_AMAZON] = "amazon", | |
952 | [VIRTUALIZATION_QEMU] = "qemu", | |
953 | [VIRTUALIZATION_BOCHS] = "bochs", | |
954 | [VIRTUALIZATION_XEN] = "xen", | |
955 | [VIRTUALIZATION_UML] = "uml", | |
956 | [VIRTUALIZATION_VMWARE] = "vmware", | |
957 | [VIRTUALIZATION_ORACLE] = "oracle", | |
958 | [VIRTUALIZATION_MICROSOFT] = "microsoft", | |
959 | [VIRTUALIZATION_ZVM] = "zvm", | |
960 | [VIRTUALIZATION_PARALLELS] = "parallels", | |
961 | [VIRTUALIZATION_BHYVE] = "bhyve", | |
962 | [VIRTUALIZATION_QNX] = "qnx", | |
963 | [VIRTUALIZATION_ACRN] = "acrn", | |
964 | [VIRTUALIZATION_POWERVM] = "powervm", | |
f5558306 | 965 | [VIRTUALIZATION_APPLE] = "apple", |
d833ed78 | 966 | [VIRTUALIZATION_SRE] = "sre", |
9b0688f4 | 967 | [VIRTUALIZATION_GOOGLE] = "google", |
5c86cec1 YW |
968 | [VIRTUALIZATION_VM_OTHER] = "vm-other", |
969 | ||
970 | [VIRTUALIZATION_SYSTEMD_NSPAWN] = "systemd-nspawn", | |
971 | [VIRTUALIZATION_LXC_LIBVIRT] = "lxc-libvirt", | |
972 | [VIRTUALIZATION_LXC] = "lxc", | |
973 | [VIRTUALIZATION_OPENVZ] = "openvz", | |
974 | [VIRTUALIZATION_DOCKER] = "docker", | |
975 | [VIRTUALIZATION_PODMAN] = "podman", | |
976 | [VIRTUALIZATION_RKT] = "rkt", | |
977 | [VIRTUALIZATION_WSL] = "wsl", | |
978 | [VIRTUALIZATION_PROOT] = "proot", | |
979 | [VIRTUALIZATION_POUCH] = "pouch", | |
75f86906 LP |
980 | [VIRTUALIZATION_CONTAINER_OTHER] = "container-other", |
981 | }; | |
982 | ||
1b86c7c5 | 983 | DEFINE_STRING_TABLE_LOOKUP(virtualization, Virtualization); |