• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

systemd / systemd / 29132483780

10 Jul 2026 08:43PM UTC coverage: 72.912% (+0.2%) from 72.702%
29132483780

push

github

bluca
man: run forgotten 'update-man-rules'

344600 of 472622 relevant lines covered (72.91%)

1365091.83 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

56.6
/src/basic/virt.c
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2

3
#if defined(__i386__) || defined(__x86_64__)
4
#include <cpuid.h>
5
#endif
6
#if defined(__aarch64__)
7
#include <sys/auxv.h>
8
#endif
9
#include <stdlib.h>
10
#include <threads.h>
11
#include <unistd.h>
12

13
#include "alloc-util.h"
14
#include "dirent-util.h"        /* IWYU pragma: keep */
15
#include "env-util.h"
16
#include "extract-word.h"
17
#include "fd-util.h"
18
#include "fileio.h"
19
#include "log.h"
20
#include "namespace-util.h"
21
#include "parse-util.h"
22
#include "process-util.h"
23
#include "stat-util.h"
24
#include "string-table.h"
25
#include "string-util.h"
26
#include "strv.h"
27
#include "virt.h"
28

29
enum {
30
      SMBIOS_VM_BIT_SET,
31
      SMBIOS_VM_BIT_UNSET,
32
      SMBIOS_VM_BIT_UNKNOWN,
33
};
34

35
static Virtualization detect_vm_cpuid(void) {
647✔
36

37
        /* CPUID is an x86 specific interface. */
38
#if defined(__i386__) || defined(__x86_64__)
39

40
        static const struct {
647✔
41
                const char sig[13];
42
                Virtualization id;
43
        } vm_table[] = {
44
                { "XenVMMXenVMM", VIRTUALIZATION_XEN       },
45
                { "KVMKVMKVM",    VIRTUALIZATION_KVM       }, /* qemu with KVM */
46
                { "Linux KVM Hv", VIRTUALIZATION_KVM       }, /* qemu with KVM + HyperV Enlightenments */
47
                { "TCGTCGTCGTCG", VIRTUALIZATION_QEMU      }, /* qemu without KVM */
48
                /* http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458 */
49
                { "VMwareVMware", VIRTUALIZATION_VMWARE    },
50
                /* https://docs.microsoft.com/en-us/virtualization/hyper-v-on-windows/reference/tlfs */
51
                { "Microsoft Hv", VIRTUALIZATION_MICROSOFT },
52
                /* https://wiki.freebsd.org/bhyve */
53
                { "bhyve bhyve ", VIRTUALIZATION_BHYVE     },
54
                { "QNXQVMBSQG",   VIRTUALIZATION_QNX       },
55
                /* https://projectacrn.org */
56
                { "ACRNACRNACRN", VIRTUALIZATION_ACRN      },
57
                /* https://www.lockheedmartin.com/en-us/products/Hardened-Security-for-Intel-Processors.html */
58
                { "SRESRESRESRE", VIRTUALIZATION_SRE       },
59
                { "Apple VZ",     VIRTUALIZATION_APPLE     },
60
        };
61

62
        uint32_t eax, ebx, ecx, edx;
647✔
63
        bool hypervisor;
647✔
64

65
        /* http://lwn.net/Articles/301888/ */
66

67
        /* First detect whether there is a hypervisor */
68
        if (__get_cpuid(1, &eax, &ebx, &ecx, &edx) == 0)
647✔
69
                return VIRTUALIZATION_NONE;
647✔
70

71
        hypervisor = ecx & 0x80000000U;
647✔
72

73
        if (hypervisor) {
647✔
74
                union {
647✔
75
                        uint32_t sig32[3];
76
                        char text[13];
77
                } sig = {};
647✔
78

79
                /* There is a hypervisor, see what it is */
80
                __cpuid(0x40000000U, eax, ebx, ecx, edx);
647✔
81

82
                sig.sig32[0] = ebx;
647✔
83
                sig.sig32[1] = ecx;
647✔
84
                sig.sig32[2] = edx;
647✔
85

86
                log_debug("Virtualization found, CPUID=%s", sig.text);
647✔
87

88
                FOREACH_ELEMENT(vm, vm_table)
1,310✔
89
                        if (memcmp_nn(sig.text, sizeof(sig.text),
663✔
90
                                      vm->sig, sizeof(vm->sig)) == 0)
1,310✔
91
                                return vm->id;
647✔
92

93
                log_debug("Unknown virtualization with CPUID=%s. Add to vm_table[]?", sig.text);
×
94
                return VIRTUALIZATION_VM_OTHER;
95
        }
96
#endif
97
        log_debug("No virtualization found in CPUID");
×
98

99
        return VIRTUALIZATION_NONE;
100
}
101

102
static Virtualization detect_vm_device_tree(void) {
×
103
#if defined(__arm__) || defined(__aarch64__) || defined(__powerpc__) || defined(__powerpc64__) || defined(__riscv)
104
        _cleanup_free_ char *hvtype = NULL;
105
        int r;
106

107
        r = read_one_line_file("/proc/device-tree/hypervisor/compatible", &hvtype);
108
        if (r == -ENOENT) {
109
                if (access("/proc/device-tree/ibm,partition-name", F_OK) == 0 &&
110
                    access("/proc/device-tree/hmc-managed?", F_OK) == 0 &&
111
                    access("/proc/device-tree/chosen/qemu,graphic-width", F_OK) != 0)
112
                        return VIRTUALIZATION_POWERVM;
113

114
                _cleanup_closedir_ DIR *dir = opendir("/proc/device-tree");
115
                if (!dir) {
116
                        if (errno == ENOENT) {
117
                                log_debug_errno(errno, "/proc/device-tree/ does not exist");
118
                                return VIRTUALIZATION_NONE;
119
                        }
120
                        return log_debug_errno(errno, "Opening /proc/device-tree/ failed: %m");
121
                }
122

123
                FOREACH_DIRENT(de, dir, return log_debug_errno(errno, "Failed to enumerate /proc/device-tree/ contents: %m"))
124
                        if (strstr(de->d_name, "fw-cfg")) {
125
                                log_debug("Virtualization QEMU: \"fw-cfg\" present in /proc/device-tree/%s", de->d_name);
126
                                return VIRTUALIZATION_QEMU;
127
                        }
128

129
                _cleanup_free_ char *compat = NULL;
130
                r = read_one_line_file("/proc/device-tree/compatible", &compat);
131
                if (r < 0 && r != -ENOENT)
132
                        return log_debug_errno(r, "Failed to read /proc/device-tree/compatible: %m");
133
                if (r >= 0) {
134
                        if (streq(compat, "qemu,pseries")) {
135
                                log_debug("Virtualization %s found in /proc/device-tree/compatible", compat);
136
                                return VIRTUALIZATION_QEMU;
137
                        }
138
                        if (streq(compat, "linux,dummy-virt")) {
139
                                /* https://www.kernel.org/doc/Documentation/devicetree/bindings/arm/linux%2Cdummy-virt.yaml */
140
                                log_debug("Generic virtualization %s found in /proc/device-tree/compatible", compat);
141
                                return VIRTUALIZATION_VM_OTHER;
142
                        }
143
                }
144

145
                log_debug("No virtualization found in /proc/device-tree/*");
146
                return VIRTUALIZATION_NONE;
147
        } else if (r < 0)
148
                return r;
149

150
        log_debug("Virtualization %s found in /proc/device-tree/hypervisor/compatible", hvtype);
151
        if (streq(hvtype, "linux,kvm"))
152
                return VIRTUALIZATION_KVM;
153
        else if (strstr(hvtype, "xen"))
154
                return VIRTUALIZATION_XEN;
155
        else if (strstr(hvtype, "vmware"))
156
                return VIRTUALIZATION_VMWARE;
157
        else
158
                return VIRTUALIZATION_VM_OTHER;
159
#else
160
        log_debug("This platform does not support /proc/device-tree");
×
161
        return VIRTUALIZATION_NONE;
×
162
#endif
163
}
164

165
#if defined(__i386__) || defined(__x86_64__) || defined(__arm__) || defined(__aarch64__) || defined(__loongarch_lp64) || defined(__riscv)
166
static Virtualization detect_vm_dmi_vendor(void) {
647✔
167
        static const char* const dmi_vendors[] = {
647✔
168
                "/sys/class/dmi/id/product_name", /* Test this before sys_vendor to detect KVM over QEMU */
169
                "/sys/class/dmi/id/sys_vendor",
170
                "/sys/class/dmi/id/board_vendor",
171
                "/sys/class/dmi/id/bios_vendor",
172
                "/sys/class/dmi/id/product_version", /* For Hyper-V VMs test */
173
                NULL
174
        };
175

176
        static const struct {
647✔
177
                const char *vendor;
178
                Virtualization id;
179
        } dmi_vendor_table[] = {
180
                { "KVM",                   VIRTUALIZATION_KVM       },
181
                { "OpenStack",             VIRTUALIZATION_KVM       }, /* Detect OpenStack instance as KVM in non x86 architecture */
182
                { "KubeVirt",              VIRTUALIZATION_KVM       }, /* Detect KubeVirt instance as KVM in non x86 architecture */
183
                { "Amazon EC2",            VIRTUALIZATION_AMAZON    },
184
                { "QEMU",                  VIRTUALIZATION_QEMU      },
185
                { "VMware",                VIRTUALIZATION_VMWARE    }, /* https://kb.vmware.com/s/article/1009458 */
186
                { "VMW",                   VIRTUALIZATION_VMWARE    },
187
                { "innotek GmbH",          VIRTUALIZATION_ORACLE    },
188
                { "VirtualBox",            VIRTUALIZATION_ORACLE    },
189
                { "Oracle Corporation",    VIRTUALIZATION_ORACLE    }, /* Detect VirtualBox on some proprietary systems via the board_vendor */
190
                { "Xen",                   VIRTUALIZATION_XEN       },
191
                { "Bochs",                 VIRTUALIZATION_BOCHS     },
192
                { "Parallels",             VIRTUALIZATION_PARALLELS },
193
                /* https://wiki.freebsd.org/bhyve */
194
                { "BHYVE",                 VIRTUALIZATION_BHYVE     },
195
                { "Hyper-V",               VIRTUALIZATION_MICROSOFT },
196
                { "Apple Virtualization",  VIRTUALIZATION_APPLE     },
197
                { "Google Compute Engine", VIRTUALIZATION_GOOGLE    }, /* https://cloud.google.com/run/docs/container-contract#sandbox */
198
        };
199
        int r;
647✔
200

201
        STRV_FOREACH(vendor, dmi_vendors) {
1,306✔
202
                _cleanup_free_ char *s = NULL;
1,306✔
203

204
                r = read_one_line_file(*vendor, &s);
1,306✔
205
                if (r == -ENOENT)
1,306✔
206
                        continue;
×
207
                if (r < 0)
1,306✔
208
                        return r;
209

210
                FOREACH_ELEMENT(dmi_vendor, dmi_vendor_table)
15,137✔
211
                        if (startswith(s, dmi_vendor->vendor)) {
14,478✔
212
                                log_debug("Virtualization %s found in DMI (%s)", s, *vendor);
647✔
213
                                return dmi_vendor->id;
647✔
214
                        }
215
        }
216
        log_debug("No virtualization found in DMI vendor table.");
×
217
        return VIRTUALIZATION_NONE;
218
}
219

220
static int detect_vm_smbios(void) {
×
221
        /* The SMBIOS BIOS Characteristics Extension Byte 2 (Section 2.1.2.2 of
222
         * https://www.dmtf.org/sites/default/files/standards/documents/DSP0134_3.4.0.pdf), specifies that
223
         * the 4th bit being set indicates a VM. The BIOS Characteristics table is exposed via the kernel in
224
         * /sys/firmware/dmi/entries/0-0. Note that in the general case, this bit being unset should not
225
         * imply that the system is running on bare-metal.  For example, QEMU 3.1.0 (with or without KVM)
226
         * with SeaBIOS does not set this bit. */
227
        _cleanup_free_ char *s = NULL;
×
228
        size_t readsize;
×
229
        int r;
×
230

231
        r = read_full_virtual_file("/sys/firmware/dmi/entries/0-0/raw", &s, &readsize);
×
232
        if (r < 0) {
×
233
                log_debug_errno(r, "Unable to read /sys/firmware/dmi/entries/0-0/raw, "
×
234
                                "using the virtualization information found in DMI vendor table, ignoring: %m");
235
                return SMBIOS_VM_BIT_UNKNOWN;
236
        }
237
        if (readsize < 20 || s[1] < 20) {
×
238
                /* The spec indicates that byte 1 contains the size of the table, 0x12 + the number of
239
                 * extension bytes. The data we're interested in is in extension byte 2, which would be at
240
                 * 0x13. If we didn't read that much data, or if the BIOS indicates that we don't have that
241
                 * much data, we don't infer anything from the SMBIOS. */
242
                log_debug("Only read %zu bytes from /sys/firmware/dmi/entries/0-0/raw (expected 20). "
×
243
                          "Using the virtualization information found in DMI vendor table.", readsize);
244
                return SMBIOS_VM_BIT_UNKNOWN;
245
        }
246

247
        uint8_t byte = (uint8_t) s[19];
×
248
        if (byte & (1U<<4)) {
×
249
                log_debug("DMI BIOS Extension table indicates virtualization.");
×
250
                return SMBIOS_VM_BIT_SET;
251
        }
252
        log_debug("DMI BIOS Extension table does not indicate virtualization.");
×
253
        return SMBIOS_VM_BIT_UNSET;
254
}
255
#endif /* defined(__i386__) || defined(__x86_64__) || defined(__arm__) || defined(__aarch64__) || defined(__loongarch_lp64) */
256

257
static Virtualization detect_vm_dmi(void) {
647✔
258
#if defined(__i386__) || defined(__x86_64__) || defined(__arm__) || defined(__aarch64__) || defined(__loongarch_lp64) || defined(__riscv)
259

260
        int r;
647✔
261
        r = detect_vm_dmi_vendor();
647✔
262

263
        /* The DMI vendor tables in /sys/class/dmi/id don't help us distinguish between Amazon EC2
264
         * virtual machines and bare-metal instances, so we need to look at SMBIOS. */
265
        if (r == VIRTUALIZATION_AMAZON) {
647✔
266
                switch (detect_vm_smbios()) {
×
267
                case SMBIOS_VM_BIT_SET:
268
                        return VIRTUALIZATION_AMAZON;
269
                case SMBIOS_VM_BIT_UNSET:
×
270
                        return VIRTUALIZATION_NONE;
×
271
                case SMBIOS_VM_BIT_UNKNOWN: {
×
272
                        /* The DMI information we are after is only accessible to the root user,
273
                         * so we fallback to using the product name which is less restricted
274
                         * to distinguish metal systems from virtualized instances */
275
                        _cleanup_free_ char *s = NULL;
×
276
                        const char *e;
×
277

278
                        r = read_full_virtual_file("/sys/class/dmi/id/product_name", &s, NULL);
×
279
                        /* In EC2, virtualized is much more common than metal, so if for some reason
280
                         * we fail to read the DMI data, assume we are virtualized. */
281
                        if (r < 0) {
×
282
                                log_debug_errno(r, "Can't read /sys/class/dmi/id/product_name,"
×
283
                                                " assuming virtualized: %m");
284
                                return VIRTUALIZATION_AMAZON;
285
                        }
286
                        e = strstrafter(truncate_nl(s), ".metal");
×
287
                        if (e && IN_SET(*e, 0, '-')) {
×
288
                                log_debug("DMI product name has '.metal', assuming no virtualization");
×
289
                                return VIRTUALIZATION_NONE;
290
                        } else
291
                                return VIRTUALIZATION_AMAZON;
292
                }
293
                default:
×
294
                        assert_not_reached();
×
295
              }
296
        }
297

298
        /* If we haven't identified a VM, but the firmware indicates that there is one, indicate as much. We
299
         * have no further information about what it is. */
300
        if (r == VIRTUALIZATION_NONE && detect_vm_smbios() == SMBIOS_VM_BIT_SET)
647✔
301
                return VIRTUALIZATION_VM_OTHER;
×
302
        return r;
303
#else
304
        return VIRTUALIZATION_NONE;
305
#endif
306
}
307

308
#define XENFEAT_dom0 11 /* xen/include/public/features.h */
309
#define PATH_FEATURES "/sys/hypervisor/properties/features"
310
/* Returns -errno, or 0 for domU, or 1 for dom0 */
311
static int detect_vm_xen_dom0(void) {
×
312
        _cleanup_free_ char *domcap = NULL;
×
313
        int r;
×
314

315
        r = read_one_line_file(PATH_FEATURES, &domcap);
×
316
        if (r < 0 && r != -ENOENT)
×
317
                return r;
318
        if (r >= 0) {
×
319
                unsigned long features;
×
320

321
                /* Here, we need to use sscanf() instead of safe_atoul()
322
                 * as the string lacks the leading "0x". */
323
                r = sscanf(domcap, "%lx", &features);
×
324
                if (r == 1) {
×
325
                        r = !!(features & (1U << XENFEAT_dom0));
×
326
                        log_debug("Virtualization XEN, found %s with value %08lx, "
×
327
                                  "XENFEAT_dom0 (indicating the 'hardware domain') is%s set.",
328
                                  PATH_FEATURES, features, r ? "" : " not");
329
                        return r;
×
330
                }
331
                log_debug("Virtualization XEN, found %s, unhandled content '%s'",
×
332
                          PATH_FEATURES, domcap);
333
        }
334

335
        r = read_one_line_file("/proc/xen/capabilities", &domcap);
×
336
        if (r == -ENOENT) {
×
337
                log_debug("Virtualization XEN because /proc/xen/capabilities does not exist");
×
338
                return 0;
339
        }
340
        if (r < 0)
×
341
                return r;
342

343
        for (const char *i = domcap;;) {
×
344
                _cleanup_free_ char *cap = NULL;
×
345

346
                r = extract_first_word(&i, &cap, ",", 0);
×
347
                if (r < 0)
×
348
                        return r;
349
                if (r == 0) {
×
350
                        log_debug("Virtualization XEN DomU found (/proc/xen/capabilities)");
×
351
                        return 0;
352
                }
353

354
                if (streq(cap, "control_d")) {
×
355
                        log_debug("Virtualization XEN Dom0 ignored (/proc/xen/capabilities)");
×
356
                        return 1;
357
                }
358
        }
359
}
360

361
static Virtualization detect_vm_xen(void) {
647✔
362
        /* The presence of /proc/xen indicates some form of a Xen domain
363
           The check for Dom0 is handled outside this function */
364
        if (access("/proc/xen", F_OK) < 0) {
647✔
365
                log_debug("Virtualization XEN not found, /proc/xen does not exist");
647✔
366
                return VIRTUALIZATION_NONE;
367
        }
368
        log_debug("Virtualization XEN found (/proc/xen exists)");
×
369
        return VIRTUALIZATION_XEN;
370
}
371

372
static Virtualization detect_vm_hypervisor(void) {
×
373
        _cleanup_free_ char *hvtype = NULL;
×
374
        int r;
×
375

376
        r = read_one_line_file("/sys/hypervisor/type", &hvtype);
×
377
        if (r == -ENOENT)
×
378
                return VIRTUALIZATION_NONE;
379
        if (r < 0)
×
380
                return r;
381

382
        log_debug("Virtualization %s found in /sys/hypervisor/type", hvtype);
×
383

384
        if (streq(hvtype, "xen"))
×
385
                return VIRTUALIZATION_XEN;
386
        else
387
                return VIRTUALIZATION_VM_OTHER;
×
388
}
389

390
static Virtualization detect_vm_uml(void) {
647✔
391
        _cleanup_fclose_ FILE *f = NULL;
647✔
392
        int r;
647✔
393

394
        /* Detect User-Mode Linux by reading /proc/cpuinfo */
395
        f = fopen("/proc/cpuinfo", "re");
647✔
396
        if (!f) {
647✔
397
                if (errno == ENOENT) {
×
398
                        log_debug("/proc/cpuinfo not found, assuming no UML virtualization.");
×
399
                        return VIRTUALIZATION_NONE;
400
                }
401
                return -errno;
×
402
        }
403

404
        for (;;) {
1,941✔
405
                _cleanup_free_ char *line = NULL;
647✔
406
                const char *t;
1,294✔
407

408
                r = read_line(f, LONG_LINE_MAX, &line);
1,294✔
409
                if (r < 0)
1,294✔
410
                        return r;
411
                if (r == 0)
1,294✔
412
                        break;
413

414
                t = startswith(line, "vendor_id\t: ");
1,294✔
415
                if (t) {
1,294✔
416
                        if (startswith(t, "User Mode Linux")) {
647✔
417
                                log_debug("UML virtualization found in /proc/cpuinfo");
×
418
                                return VIRTUALIZATION_UML;
419
                        }
420

421
                        break;
422
                }
423
        }
424

425
        log_debug("UML virtualization not found in /proc/cpuinfo.");
647✔
426
        return VIRTUALIZATION_NONE;
427
}
428

429
static Virtualization detect_vm_zvm(void) {
×
430

431
#if defined(__s390__)
432
        _cleanup_free_ char *t = NULL;
433
        int r;
434

435
        r = get_proc_field("/proc/sysinfo", "VM00 Control Program", &t);
436
        if (IN_SET(r, -ENOENT, -ENODATA))
437
                return VIRTUALIZATION_NONE;
438
        if (r < 0)
439
                return r;
440

441
        log_debug("Virtualization %s found in /proc/sysinfo", t);
442
        if (streq(t, "z/VM"))
443
                return VIRTUALIZATION_ZVM;
444
        else
445
                return VIRTUALIZATION_KVM;
446
#else
447
        log_debug("This platform does not support /proc/sysinfo");
×
448
        return VIRTUALIZATION_NONE;
×
449
#endif
450
}
451

452
/* Returns a short identifier for the various VM implementations */
453
Virtualization detect_vm(void) {
1,801✔
454
        static thread_local Virtualization cached_found = _VIRTUALIZATION_INVALID;
1,801✔
455
        bool other = false, hyperv = false;
1,801✔
456
        int xen_dom0 = 0;
1,801✔
457
        Virtualization v, dmi;
1,801✔
458

459
        if (cached_found >= 0)
1,801✔
460
                return cached_found;
461

462
        /* We have to use the correct order here:
463
         *
464
         * → First, try to detect Oracle Virtualbox, Amazon EC2 Nitro, Parallels, and Google Compute Engine,
465
         *   even if they use KVM, as well as Xen, even if it cloaks as Microsoft Hyper-V. Attempt to detect
466
         *   UML at this stage too, since it runs as a user-process nested inside other VMs. Also check for
467
         *   Xen now, because Xen PV mode does not override CPUID when nested inside another hypervisor.
468
         *
469
         * → Second, try to detect from CPUID. This will report KVM for whatever software is used even if
470
         *   info in DMI is overwritten.
471
         *
472
         * → Third, try to detect from DMI. */
473

474
        dmi = detect_vm_dmi();
647✔
475
        if (IN_SET(dmi,
647✔
476
                   VIRTUALIZATION_ORACLE,
477
                   VIRTUALIZATION_XEN,
478
                   VIRTUALIZATION_AMAZON,
479
                   /* Unable to distinguish a GCE machine from a VM to bare-metal
480
                    * for non-x86 architectures due to its necessity for cpuid
481
                    * detection, which functions solely on x86 platforms. Report
482
                    * as a VM for other architectures. */
483
#if !defined(__i386__) && !defined(__x86_64__)
484
                   VIRTUALIZATION_GOOGLE,
485
#endif
486
                   VIRTUALIZATION_PARALLELS)) {
487
                v = dmi;
×
488
                goto finish;
×
489
        }
490

491
        /* Detect UML */
492
        v = detect_vm_uml();
647✔
493
        if (v < 0)
647✔
494
                return v;
495
        if (v != VIRTUALIZATION_NONE)
647✔
496
                goto finish;
×
497

498
        /* Detect Xen */
499
        v = detect_vm_xen();
647✔
500
        if (v < 0)
647✔
501
                return v;
502
        if (v == VIRTUALIZATION_XEN) {
647✔
503
                 /* If we are Dom0, then we expect to not report as a VM. However, as we might be nested
504
                  * inside another hypervisor which can be detected via the CPUID check, wait to report this
505
                  * until after the CPUID check. */
506
                xen_dom0 = detect_vm_xen_dom0();
×
507
                if (xen_dom0 < 0)
×
508
                        return xen_dom0;
509
                if (xen_dom0 == 0)
×
510
                        goto finish;
×
511
        } else if (v != VIRTUALIZATION_NONE)
647✔
512
                assert_not_reached();
×
513

514
        /* Detect from CPUID */
515
        v = detect_vm_cpuid();
647✔
516
        if (v < 0)
647✔
517
                return v;
518
        if (v == VIRTUALIZATION_MICROSOFT)
647✔
519
                /* QEMU sets the CPUID string to hyperv's, in case it provides hyperv enlightenments. Let's
520
                 * hence not return Microsoft here but just use the other mechanisms first to make a better
521
                 * decision. */
522
                hyperv = true;
523
        else if (v == VIRTUALIZATION_VM_OTHER)
643✔
524
                other = true;
525
        else if (v == VIRTUALIZATION_KVM && dmi == VIRTUALIZATION_GOOGLE)
643✔
526
                /* The DMI vendor tables in /sys/class/dmi/id don't help us distinguish between GCE
527
                 * virtual machines and bare-metal instances, so we need to look at hypervisor. */
528
                return VIRTUALIZATION_GOOGLE;
529
        else if (v != VIRTUALIZATION_NONE)
643✔
530
                goto finish;
643✔
531

532
        /* If we are in Dom0 and have not yet finished, finish with the result of detect_vm_cpuid */
533
        if (xen_dom0 > 0)
4✔
534
                goto finish;
×
535

536
        /* Now, let's get back to DMI */
537
        if (dmi < 0)
4✔
538
                return dmi;
539
        if (dmi == VIRTUALIZATION_VM_OTHER)
4✔
540
                other = true;
541
        else if (!IN_SET(dmi, VIRTUALIZATION_NONE, VIRTUALIZATION_GOOGLE)) {
4✔
542
                /* At this point if GCE has been detected in dmi, do not report as a VM. It should
543
                 * be a bare-metal machine */
544
                v = dmi;
4✔
545
                goto finish;
4✔
546
        }
547

548
        /* Check high-level hypervisor sysfs file */
549
        v = detect_vm_hypervisor();
×
550
        if (v < 0)
×
551
                return v;
552
        if (v == VIRTUALIZATION_VM_OTHER)
×
553
                other = true;
554
        else if (v != VIRTUALIZATION_NONE)
×
555
                goto finish;
×
556

557
        v = detect_vm_device_tree();
×
558
        if (v < 0)
×
559
                return v;
560
        if (v == VIRTUALIZATION_VM_OTHER)
×
561
                other = true;
562
        else if (v != VIRTUALIZATION_NONE)
×
563
                goto finish;
×
564

565
        v = detect_vm_zvm();
×
566
        if (v < 0)
×
567
                return v;
568

569
finish:
×
570
        /* None of the checks above gave us a clear answer, hence let's now use fallback logic: if hyperv
571
         * enlightenments are available but the VMM wasn't recognized as anything yet, it's probably
572
         * Microsoft. */
573
        if (v == VIRTUALIZATION_NONE) {
647✔
574
                if (hyperv)
×
575
                        v = VIRTUALIZATION_MICROSOFT;
576
                else if (other)
×
577
                        v = VIRTUALIZATION_VM_OTHER;
×
578
        }
579

580
        cached_found = v;
647✔
581
        log_debug("Found VM virtualization %s", virtualization_to_string(v));
647✔
582
        return v;
583
}
584

585
static const char *const container_table[_VIRTUALIZATION_MAX] = {
586
        [VIRTUALIZATION_LXC]            = "lxc",
587
        [VIRTUALIZATION_LXC_LIBVIRT]    = "lxc-libvirt",
588
        [VIRTUALIZATION_SYSTEMD_NSPAWN] = "systemd-nspawn",
589
        [VIRTUALIZATION_DOCKER]         = "docker",
590
        [VIRTUALIZATION_PODMAN]         = "podman",
591
        [VIRTUALIZATION_RKT]            = "rkt",
592
        [VIRTUALIZATION_WSL]            = "wsl",
593
        [VIRTUALIZATION_PROOT]          = "proot",
594
        [VIRTUALIZATION_POUCH]          = "pouch",
595
};
596

597
DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(container, int);
4,043✔
598

599
static int running_in_pidns(void) {
8,353✔
600
        int r;
8,353✔
601

602
        r = namespace_is_init(NAMESPACE_PID);
8,353✔
603
        if (r < 0)
8,353✔
604
                return log_debug_errno(r, "Failed to test if in root PID namespace, ignoring: %m");
25✔
605

606
        return !r;
8,328✔
607
}
608

609
static Virtualization detect_container_files(void) {
8,353✔
610
        static const struct {
8,353✔
611
                const char *file_path;
612
                Virtualization id;
613
        } container_file_table[] = {
614
                /* https://github.com/containers/podman/issues/6192 */
615
                /* https://github.com/containers/podman/issues/3586#issuecomment-661918679 */
616
                { "/run/.containerenv", VIRTUALIZATION_PODMAN },
617
                /* https://github.com/moby/moby/issues/18355 */
618
                /* Docker must be the last in this table, see below. */
619
                { "/.dockerenv",        VIRTUALIZATION_DOCKER },
620
        };
621

622
        FOREACH_ELEMENT(file, container_file_table) {
25,059✔
623
                if (access(file->file_path, F_OK) >= 0)
16,706✔
624
                        return file->id;
×
625

626
                if (errno != ENOENT)
16,706✔
627
                        log_debug_errno(errno,
16,706✔
628
                                        "Checking if %s exists failed, ignoring: %m",
629
                                        file->file_path);
630
        }
631

632
        return VIRTUALIZATION_NONE;
633
}
634

635
Virtualization detect_container(void) {
312,046✔
636
        static thread_local Virtualization cached_found = _VIRTUALIZATION_INVALID;
312,046✔
637
        _cleanup_free_ char *m = NULL, *o = NULL, *p = NULL;
312,046✔
638
        const char *e = NULL;
312,046✔
639
        Virtualization v;
312,046✔
640
        int r;
312,046✔
641

642
        if (cached_found >= 0)
312,046✔
643
                return cached_found;
644

645
        /* /proc/vz exists in container and outside of the container, /proc/bc only outside of the container. */
646
        if (access("/proc/vz", F_OK) < 0) {
12,396✔
647
                if (errno != ENOENT)
12,396✔
648
                        log_debug_errno(errno, "Failed to check if /proc/vz exists, ignoring: %m");
×
649
        } else if (access("/proc/bc", F_OK) < 0) {
×
650
                if (errno == ENOENT) {
×
651
                        v = VIRTUALIZATION_OPENVZ;
×
652
                        goto finish;
×
653
                }
654

655
                log_debug_errno(errno, "Failed to check if /proc/bc exists, ignoring: %m");
×
656
        }
657

658
        /* "Official" way of detecting WSL https://github.com/Microsoft/WSL/issues/423#issuecomment-221627364 */
659
        r = read_one_line_file("/proc/sys/kernel/osrelease", &o);
12,396✔
660
        if (r < 0)
12,396✔
661
                log_debug_errno(r, "Failed to read /proc/sys/kernel/osrelease, ignoring: %m");
25✔
662
        else if (strstr(o, "Microsoft") || strstr(o, "WSL")) {
12,371✔
663
                v = VIRTUALIZATION_WSL;
×
664
                goto finish;
×
665
        }
666

667
        /* proot doesn't use PID namespacing, so we can just check if we have a matching tracer for this
668
         * invocation without worrying about it being elsewhere.
669
         */
670
        r = get_proc_field("/proc/self/status", "TracerPid", &p);
12,396✔
671
        if (r < 0)
12,396✔
672
                log_debug_errno(r, "Failed to read our own trace PID, ignoring: %m");
25✔
673
        else if (!streq(p, "0")) {
12,371✔
674
                pid_t ptrace_pid;
×
675

676
                r = parse_pid(p, &ptrace_pid);
×
677
                if (r < 0)
×
678
                        log_debug_errno(r, "Failed to parse our own tracer PID, ignoring: %m");
×
679
                else {
680
                        _cleanup_free_ char *ptrace_comm = NULL;
×
681
                        const char *pf;
×
682

683
                        pf = procfs_file_alloca(ptrace_pid, "comm");
×
684
                        r = read_one_line_file(pf, &ptrace_comm);
×
685
                        if (r < 0)
×
686
                                log_debug_errno(r, "Failed to read %s, ignoring: %m", pf);
×
687
                        else if (startswith(ptrace_comm, "proot")) {
×
688
                                v = VIRTUALIZATION_PROOT;
×
689
                                goto finish;
×
690
                        }
691
                }
692
        }
693

694
        /* The container manager might have placed this in the /run/host/ hierarchy for us, which is best
695
         * because we can be consumed just like that, without special privileges. */
696
        r = read_one_line_file("/run/host/container-manager", &m);
12,396✔
697
        if (r > 0) {
12,396✔
698
                e = m;
4,043✔
699
                goto translate_name;
4,043✔
700
        }
701
        if (!IN_SET(r, -ENOENT, 0))
8,353✔
702
                return log_debug_errno(r, "Failed to read /run/host/container-manager: %m");
×
703

704
        if (getpid_cached() == 1) {
8,353✔
705
                /* If we are PID 1 we can just check our own environment variable, and that's authoritative.
706
                 * We distinguish three cases:
707
                 * - the variable is not defined → we jump to other checks
708
                 * - the variable is defined to an empty value → we are not in a container
709
                 * - anything else → some container, either one of the known ones or "container-other"
710
                 */
711
                e = getenv("container");
40✔
712
                if (!e)
40✔
713
                        goto check_files;
40✔
714
                if (isempty(e)) {
×
715
                        v = VIRTUALIZATION_NONE;
×
716
                        goto finish;
×
717
                }
718

719
                goto translate_name;
×
720
        }
721

722
        /* Otherwise, PID 1 might have dropped this information into a file in /run. This is better than accessing
723
         * /proc/1/environ, since we don't need CAP_SYS_PTRACE for that. */
724
        r = read_one_line_file("/run/systemd/container", &m);
8,313✔
725
        if (r > 0) {
8,313✔
726
                e = m;
×
727
                goto translate_name;
×
728
        }
729
        if (!IN_SET(r, -ENOENT, 0))
8,313✔
730
                return log_debug_errno(r, "Failed to read /run/systemd/container: %m");
×
731

732
        /* Fallback for cases where PID 1 was not systemd (for example, cases where init=/bin/sh is used. */
733
        r = getenv_for_pid(1, "container", &m);
8,313✔
734
        if (r > 0) {
8,313✔
735
                e = m;
×
736
                goto translate_name;
×
737
        }
738
        if (r < 0) /* This only works if we have CAP_SYS_PTRACE, hence let's better ignore failures here */
8,313✔
739
                log_debug_errno(r, "Failed to read $container of PID 1, ignoring: %m");
2,016✔
740

741
check_files:
6,297✔
742
        /* Check for existence of some well-known files. We only do this after checking
743
         * for other specific container managers, otherwise we risk mistaking another
744
         * container manager for Docker: the /.dockerenv file could inadvertently end up
745
         * in a file system image. */
746
        v = detect_container_files();
8,353✔
747
        if (v < 0)
8,353✔
748
                return v;
749
        if (v != VIRTUALIZATION_NONE)
8,353✔
750
                goto finish;
×
751

752
        /* Finally, the root pid namespace has an hardcoded inode number of 0xEFFFFFFC since kernel 3.8, so
753
         * if all else fails we can check the inode number of our pid namespace and compare it. */
754
        if (running_in_pidns() > 0) {
8,353✔
755
                log_debug("Running in a pid namespace, assuming unknown container manager.");
1✔
756
                v = VIRTUALIZATION_CONTAINER_OTHER;
1✔
757
                goto finish;
1✔
758
        }
759

760
        /* If none of that worked, give up, assume no container manager. */
761
        v = VIRTUALIZATION_NONE;
8,352✔
762
        goto finish;
8,352✔
763

764
translate_name:
4,043✔
765
        if (streq(e, "oci")) {
4,043✔
766
                /* Some images hardcode container=oci, but OCI is not a specific container manager.
767
                 * Try to detect one based on well-known files. */
768
                v = detect_container_files();
×
769
                if (v == VIRTUALIZATION_NONE)
×
770
                        v = VIRTUALIZATION_CONTAINER_OTHER;
×
771
                goto finish;
×
772
        }
773
        v = container_from_string(e);
4,043✔
774
        if (v < 0)
4,043✔
775
                v = VIRTUALIZATION_CONTAINER_OTHER;
×
776

777
finish:
4,043✔
778
        log_debug("Found container virtualization %s.", virtualization_to_string(v));
12,396✔
779
        cached_found = v;
12,396✔
780
        return v;
12,396✔
781
}
782

783
Virtualization detect_virtualization(void) {
1,932✔
784
        int v;
1,932✔
785

786
        v = detect_container();
1,932✔
787
        if (v != VIRTUALIZATION_NONE)
1,932✔
788
                return v;
789

790
        return detect_vm();
1,494✔
791
}
792

793
int running_in_userns(void) {
107✔
794
        int r;
107✔
795

796
        r = namespace_is_init(NAMESPACE_USER);
107✔
797
        if (r < 0)
107✔
798
                return log_debug_errno(r, "Failed to test if in root user namespace, ignoring: %m");
×
799

800
        return !r;
107✔
801
}
802

803
int running_in_chroot(void) {
19,888✔
804
        int r;
19,888✔
805

806
        /* If we're PID1, /proc may not be mounted (and most likely we're not in a chroot). But PID1 will
807
         * mount /proc, so all other programs can assume that if /proc is *not* available, we're in some
808
         * chroot. */
809

810
        r = getenv_bool("SYSTEMD_IN_CHROOT");
19,888✔
811
        if (r >= 0)
19,888✔
812
                return r > 0;
4✔
813
        if (r != -ENXIO)
19,884✔
814
                log_debug_errno(r, "Failed to parse $SYSTEMD_IN_CHROOT, ignoring: %m");
×
815

816
        /* Deprecated but kept for backwards compatibility. */
817
        if (getenv_bool("SYSTEMD_IGNORE_CHROOT") > 0)
19,884✔
818
                return 0;
819

820
        r = inode_same("/proc/1/root", "/", /* flags= */ 0);
19,884✔
821
        if (r == -ENOENT) {
19,884✔
822
                r = proc_mounted();
×
823
                if (r == 0) {
×
824
                        if (getpid_cached() == 1)
×
825
                                return false; /* We will mount /proc, assuming we're not in a chroot. */
826

827
                        log_debug("/proc/ is not mounted, assuming we're in a chroot.");
×
828
                        return true;
829
                }
830
                if (r > 0) /* If we have fake /proc/, we can't do the check properly. */
×
831
                        return -ENOSYS;
832
        }
833
        if (r < 0)
19,884✔
834
                return log_debug_errno(r, "Failed to check if /proc/1/root and / are the same inode: %m");
246✔
835

836
        return r == 0;
19,638✔
837
}
838

839
#if defined(__i386__) || defined(__x86_64__)
840
struct cpuid_table_entry {
841
        uint32_t flag_bit;
842
        const char *name;
843
};
844

845
static const struct cpuid_table_entry leaf1_edx[] = {
846
        {  0, "fpu"     },
847
        {  1, "vme"     },
848
        {  2, "de"      },
849
        {  3, "pse"     },
850
        {  4, "tsc"     },
851
        {  5, "msr"     },
852
        {  6, "pae"     },
853
        {  7, "mce"     },
854
        {  8, "cx8"     },
855
        {  9, "apic"    },
856
        { 11, "sep"     },
857
        { 12, "mtrr"    },
858
        { 13, "pge"     },
859
        { 14, "mca"     },
860
        { 15, "cmov"    },
861
        { 16, "pat"     },
862
        { 17, "pse36"   },
863
        { 19, "clflush" },
864
        { 23, "mmx"     },
865
        { 24, "fxsr"    },
866
        { 25, "sse"     },
867
        { 26, "sse2"    },
868
        { 28, "ht"      },
869
};
870

871
static const struct cpuid_table_entry leaf1_ecx[] = {
872
        {  0, "pni"     },
873
        {  1, "pclmul"  },
874
        {  3, "monitor" },
875
        {  9, "ssse3"   },
876
        { 12, "fma3"    },
877
        { 13, "cx16"    },
878
        { 19, "sse4_1"  },
879
        { 20, "sse4_2"  },
880
        { 22, "movbe"   },
881
        { 23, "popcnt"  },
882
        { 25, "aes"     },
883
        { 26, "xsave"   },
884
        { 27, "osxsave" },
885
        { 28, "avx"     },
886
        { 29, "f16c"    },
887
        { 30, "rdrand"  },
888
};
889

890
static const struct cpuid_table_entry leaf7_ebx[] = {
891
        {  3, "bmi1"   },
892
        {  5, "avx2"   },
893
        {  8, "bmi2"   },
894
        { 18, "rdseed" },
895
        { 19, "adx"    },
896
        { 29, "sha_ni" },
897
};
898

899
static const struct cpuid_table_entry leaf81_edx[] = {
900
        { 11, "syscall" },
901
        { 27, "rdtscp"  },
902
        { 29, "lm"      },
903
};
904

905
static const struct cpuid_table_entry leaf81_ecx[] = {
906
        {  0, "lahf_lm" },
907
        {  5, "abm"     },
908
};
909

910
static const struct cpuid_table_entry leaf87_edx[] = {
911
        {  8, "constant_tsc" },
912
};
913

914
static bool given_flag_in_set(const char *flag, const struct cpuid_table_entry *set, size_t set_size, uint32_t val) {
16✔
915
        assert(set);
16✔
916

917
        for (size_t i = 0; i < set_size; i++) {
150✔
918
                if ((UINT32_C(1) << set[i].flag_bit) & val &&
136✔
919
                                streq(flag, set[i].name))
132✔
920
                        return true;
921
        }
922
        return false;
923
}
924

925
static bool real_has_cpu_with_flag(const char *flag) {
4✔
926
        uint32_t eax, ebx, ecx, edx;
4✔
927

928
        if (__get_cpuid(1, &eax, &ebx, &ecx, &edx)) {
4✔
929
                if (given_flag_in_set(flag, leaf1_ecx, ELEMENTSOF(leaf1_ecx), ecx))
4✔
930
                        return true;
4✔
931

932
                if (given_flag_in_set(flag, leaf1_edx, ELEMENTSOF(leaf1_edx), edx))
4✔
933
                        return true;
934
        }
935

936
        if (__get_cpuid_count(7, 0, &eax, &ebx, &ecx, &edx)) {
2✔
937
                if (given_flag_in_set(flag, leaf7_ebx, ELEMENTSOF(leaf7_ebx), ebx))
2✔
938
                        return true;
939
        }
940

941
        if (__get_cpuid(0x80000001U, &eax, &ebx, &ecx, &edx)) {
2✔
942
                if (given_flag_in_set(flag, leaf81_ecx, ELEMENTSOF(leaf81_ecx), ecx))
2✔
943
                        return true;
944

945
                if (given_flag_in_set(flag, leaf81_edx, ELEMENTSOF(leaf81_edx), edx))
2✔
946
                        return true;
947
        }
948

949
        if (__get_cpuid(0x80000007U, &eax, &ebx, &ecx, &edx))
2✔
950
                if (given_flag_in_set(flag, leaf87_edx, ELEMENTSOF(leaf87_edx), edx))
2✔
951
                        return true;
×
952

953
        return false;
954
}
955
#endif
956

957
#if defined(__aarch64__)
958
struct hwcap_table_entry {
959
        uint64_t flag_mask;
960
        const char *name;
961
};
962

963
static const struct hwcap_table_entry hwcap[] = {
964
        { HWCAP_FP,           "fp"          },
965
        { HWCAP_ASIMD,        "asimd"       },
966
        { HWCAP_EVTSTRM,      "evtstrm"     },
967
        { HWCAP_AES,          "aes"         },
968
        { HWCAP_PMULL,        "pmull"       },
969
        { HWCAP_SHA1,         "sha1"        },
970
        { HWCAP_SHA2,         "sha2"        },
971
        { HWCAP_CRC32,        "crc32"       },
972
        { HWCAP_ATOMICS,      "atomics"     },
973
        { HWCAP_FPHP,         "fphp"        },
974
        { HWCAP_ASIMDHP,      "asimdhp"     },
975
        { HWCAP_CPUID,        "cpuid"       },
976
        { HWCAP_ASIMDRDM,     "asimdrdm"    },
977
        { HWCAP_JSCVT,        "jscvt"       },
978
        { HWCAP_FCMA,         "fcma"        },
979
        { HWCAP_LRCPC,        "lrcpc"       },
980
        { HWCAP_DCPOP,        "dcpop"       },
981
        { HWCAP_SHA3,         "sha3"        },
982
        { HWCAP_SM3,          "sm3"         },
983
        { HWCAP_SM4,          "sm4"         },
984
        { HWCAP_ASIMDDP,      "asimddp"     },
985
        { HWCAP_SHA512,       "sha512"      },
986
        { HWCAP_SVE,          "sve"         },
987
        { HWCAP_ASIMDFHM,     "asimdfhm"    },
988
        { HWCAP_DIT,          "dit"         },
989
        { HWCAP_USCAT,        "uscat"       },
990
        { HWCAP_ILRCPC,       "ilrcpc"      },
991
        { HWCAP_FLAGM,        "flagm"       },
992
        { HWCAP_SSBS,         "ssbs"        },
993
        { HWCAP_SB,           "sb"          },
994
        { HWCAP_PACA,         "paca"        },
995
        { HWCAP_PACG,         "pacg"        },
996
        { HWCAP_GCS,          "gcs"         },
997
        { HWCAP_CMPBR,        "cmpbr"       },
998
        { HWCAP_FPRCVT,       "fprcvt"      },
999
        { HWCAP_F8MM8,        "f8mm8"       },
1000
        { HWCAP_F8MM4,        "f8mm4"       },
1001
        { HWCAP_SVE_F16MM,    "svef16mm"    },
1002
        { HWCAP_SVE_ELTPERM,  "sveeltperm"  },
1003
        { HWCAP_SVE_AES2,     "sveaes2"     },
1004
        { HWCAP_SVE_BFSCALE,  "svebfscale"  },
1005
        { HWCAP_SVE2P2,       "sve2p2"      },
1006
        { HWCAP_SME2P2,       "sme2p2"      },
1007
        { HWCAP_SME_SBITPERM, "smesbitperm" },
1008
        { HWCAP_SME_AES,      "smeaes"      },
1009
        { HWCAP_SME_SFEXPA,   "smesfexpa"   },
1010
        { HWCAP_SME_STMOP,    "smestmop"    },
1011
        { HWCAP_SME_SMOP4,    "smesmop4"    },
1012
};
1013

1014
static const struct hwcap_table_entry hwcap2[] = {
1015
        { HWCAP2_DCPODP,      "dcpodp"     },
1016
        { HWCAP2_SVE2,        "sve2"       },
1017
        { HWCAP2_SVEAES,      "sveaes"     },
1018
        { HWCAP2_SVEPMULL,    "svepmull"   },
1019
        { HWCAP2_SVEBITPERM,  "svebitperm" },
1020
        { HWCAP2_SVESHA3,     "svesha3"    },
1021
        { HWCAP2_SVESM4,      "svesm4"     },
1022
        { HWCAP2_FLAGM2,      "flagm2"     },
1023
        { HWCAP2_FRINT,       "frint"      },
1024
        { HWCAP2_SVEI8MM,     "svei8mm"    },
1025
        { HWCAP2_SVEF32MM,    "svef32mm"   },
1026
        { HWCAP2_SVEF64MM,    "svef64mm"   },
1027
        { HWCAP2_SVEBF16,     "svebf16"    },
1028
        { HWCAP2_I8MM,        "i8mm"       },
1029
        { HWCAP2_BF16,        "bf16"       },
1030
        { HWCAP2_DGH,         "dgh"        },
1031
        { HWCAP2_RNG,         "rng"        },
1032
        { HWCAP2_BTI,         "bti"        },
1033
        { HWCAP2_MTE,         "mte"        },
1034
        { HWCAP2_ECV,         "ecv"        },
1035
        { HWCAP2_AFP,         "afp"        },
1036
        { HWCAP2_RPRES,       "rpres"      },
1037
        { HWCAP2_MTE3,        "mte3"       },
1038
        { HWCAP2_SME,         "sme"        },
1039
        { HWCAP2_SME_I16I64,  "smei16i64"  },
1040
        { HWCAP2_SME_F64F64,  "smef64f64"  },
1041
        { HWCAP2_SME_I8I32,   "smei8i32"   },
1042
        { HWCAP2_SME_F16F32,  "smef16f32"  },
1043
        { HWCAP2_SME_B16F32,  "smeb16f32"  },
1044
        { HWCAP2_SME_F32F32,  "smef32f32"  },
1045
        { HWCAP2_SME_FA64,    "smefa64"    },
1046
        { HWCAP2_WFXT,        "wfxt"       },
1047
        { HWCAP2_EBF16,       "ebf16"      },
1048
        { HWCAP2_SVE_EBF16,   "sveebf16"   },
1049
        { HWCAP2_CSSC,        "cssc"       },
1050
        { HWCAP2_RPRFM,       "rprfm"      },
1051
        { HWCAP2_SVE2P1,      "sve2p1"     },
1052
        { HWCAP2_SME2,        "sme2"       },
1053
        { HWCAP2_SME2P1,      "sme2p1"     },
1054
        { HWCAP2_SME_I16I32,  "smei16i32"  },
1055
        { HWCAP2_SME_BI32I32, "smebi32i32" },
1056
        { HWCAP2_SME_B16B16,  "smeb16b16"  },
1057
        { HWCAP2_SME_F16F16,  "smef16f16"  },
1058
        { HWCAP2_MOPS,        "mops"       },
1059
        { HWCAP2_HBC,         "hbc"        },
1060
        { HWCAP2_SVE_B16B16,  "sveb16b16"  },
1061
        { HWCAP2_LRCPC3,      "lrcpc3"     },
1062
        { HWCAP2_LSE128,      "lse128"     },
1063
        { HWCAP2_FPMR,        "fpmr"       },
1064
        { HWCAP2_LUT,         "lut"        },
1065
        { HWCAP2_FAMINMAX,    "faminmax"   },
1066
        { HWCAP2_F8CVT,       "f8cvt"      },
1067
        { HWCAP2_F8FMA,       "f8fma"      },
1068
        { HWCAP2_F8DP4,       "f8dp4"      },
1069
        { HWCAP2_F8DP2,       "f8dp2"      },
1070
        { HWCAP2_F8E4M3,      "f8e4m3"     },
1071
        { HWCAP2_F8E5M2,      "f8e5m2"     },
1072
        { HWCAP2_SME_LUTV2,   "smelutv2"   },
1073
        { HWCAP2_SME_F8F16,   "smef8f16"   },
1074
        { HWCAP2_SME_F8F32,   "smef8f32"   },
1075
        { HWCAP2_SME_SF8FMA,  "smesf8fma"  },
1076
        { HWCAP2_SME_SF8DP4,  "smesf8dp4"  },
1077
        { HWCAP2_SME_SF8DP2,  "smesf8dp2"  },
1078
        { HWCAP2_POE,         "poe"        },
1079
};
1080

1081
static const struct hwcap_table_entry hwcap3[] = {
1082
        { HWCAP3_MTE_FAR,        "mtefar"       },
1083
        { HWCAP3_MTE_STORE_ONLY, "mtestoreonly" },
1084
        { HWCAP3_LSFE,           "lsfe"         },
1085
        { HWCAP3_LS64,           "ls64"         },
1086
        { HWCAP3_SVE_B16MM,      "sveb16mm"     },
1087
        { HWCAP3_SVE2P3,         "sve2p3"       },
1088
        { HWCAP3_SME_LUT6,       "smelut6"      },
1089
        { HWCAP3_SME2P3,         "sme2p3"       },
1090
        { HWCAP3_F16MM,          "f16mm"        },
1091
        { HWCAP3_F16F32DOT,      "f16f32dot"    },
1092
        { HWCAP3_F16F32MM,       "f16f32mm"     },
1093
        { HWCAP3_SVE_LUT6,       "svelut6"      },
1094
};
1095

1096
static bool given_flag_in_hwcap_set(
1097
                const char *flag,
1098
                const struct hwcap_table_entry *set,
1099
                size_t set_size,
1100
                unsigned long val) {
1101

1102
        assert(set);
1103

1104
        for (size_t i = 0; i < set_size; i++)
1105
                if ((set[i].flag_mask & val) && streq(flag, set[i].name))
1106
                        return true;
1107

1108
        return false;
1109
}
1110

1111
static bool real_has_cpu_with_flag(const char *flag) {
1112
        unsigned long val;
1113

1114
        val = getauxval(AT_HWCAP);
1115
        if (given_flag_in_hwcap_set(flag, hwcap, ELEMENTSOF(hwcap), val))
1116
                return true;
1117

1118
        val = getauxval(AT_HWCAP2);
1119
        if (given_flag_in_hwcap_set(flag, hwcap2, ELEMENTSOF(hwcap2), val))
1120
                return true;
1121

1122
        val = getauxval(AT_HWCAP3);
1123
        if (given_flag_in_hwcap_set(flag, hwcap3, ELEMENTSOF(hwcap3), val))
1124
                return true;
1125

1126
        return false;
1127
}
1128
#endif
1129

1130
bool has_cpu_with_flag(const char *flag) {
4✔
1131
        /* Check the architecture-specific CPU feature interface when available. */
1132
#if defined(__i386__) || defined(__x86_64__) || defined(__aarch64__)
1133
        return real_has_cpu_with_flag(flag);
4✔
1134
#else
1135
        return false;
1136
#endif
1137
}
1138

1139
static const char *const virtualization_table[_VIRTUALIZATION_MAX] = {
1140
        [VIRTUALIZATION_NONE]            = "none",
1141
        [VIRTUALIZATION_KVM]             = "kvm",
1142
        [VIRTUALIZATION_AMAZON]          = "amazon",
1143
        [VIRTUALIZATION_QEMU]            = "qemu",
1144
        [VIRTUALIZATION_BOCHS]           = "bochs",
1145
        [VIRTUALIZATION_XEN]             = "xen",
1146
        [VIRTUALIZATION_UML]             = "uml",
1147
        [VIRTUALIZATION_VMWARE]          = "vmware",
1148
        [VIRTUALIZATION_ORACLE]          = "oracle",
1149
        [VIRTUALIZATION_MICROSOFT]       = "microsoft",
1150
        [VIRTUALIZATION_ZVM]             = "zvm",
1151
        [VIRTUALIZATION_PARALLELS]       = "parallels",
1152
        [VIRTUALIZATION_BHYVE]           = "bhyve",
1153
        [VIRTUALIZATION_QNX]             = "qnx",
1154
        [VIRTUALIZATION_ACRN]            = "acrn",
1155
        [VIRTUALIZATION_POWERVM]         = "powervm",
1156
        [VIRTUALIZATION_APPLE]           = "apple",
1157
        [VIRTUALIZATION_SRE]             = "sre",
1158
        [VIRTUALIZATION_GOOGLE]          = "google",
1159
        [VIRTUALIZATION_VM_OTHER]        = "vm-other",
1160

1161
        [VIRTUALIZATION_SYSTEMD_NSPAWN]  = "systemd-nspawn",
1162
        [VIRTUALIZATION_LXC_LIBVIRT]     = "lxc-libvirt",
1163
        [VIRTUALIZATION_LXC]             = "lxc",
1164
        [VIRTUALIZATION_OPENVZ]          = "openvz",
1165
        [VIRTUALIZATION_DOCKER]          = "docker",
1166
        [VIRTUALIZATION_PODMAN]          = "podman",
1167
        [VIRTUALIZATION_RKT]             = "rkt",
1168
        [VIRTUALIZATION_WSL]             = "wsl",
1169
        [VIRTUALIZATION_PROOT]           = "proot",
1170
        [VIRTUALIZATION_POUCH]           = "pouch",
1171
        [VIRTUALIZATION_CONTAINER_OTHER] = "container-other",
1172
};
1173

1174
DEFINE_STRING_TABLE_LOOKUP(virtualization, Virtualization);
3,022✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc