chiark / gitweb /
arm/aarch64: detect-virt: check dmi
[elogind.git] / src / basic / virt.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright 2011 Lennart Poettering
7
8   systemd is free software; you can redistribute it and/or modify it
9   under the terms of the GNU Lesser General Public License as published by
10   the Free Software Foundation; either version 2.1 of the License, or
11   (at your option) any later version.
12
13   systemd is distributed in the hope that it will be useful, but
14   WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16   Lesser General Public License for more details.
17
18   You should have received a copy of the GNU Lesser General Public License
19   along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <string.h>
23 #include <errno.h>
24 #include <unistd.h>
25
26 #include "util.h"
27 #include "process-util.h"
28 #include "virt.h"
29 #include "fileio.h"
30
31 static int detect_vm_cpuid(void) {
32
33         /* CPUID is an x86 specific interface. */
34 #if defined(__i386__) || defined(__x86_64__)
35
36         static const struct {
37                 const char *cpuid;
38                 int id;
39         } cpuid_vendor_table[] = {
40                 { "XenVMMXenVMM", VIRTUALIZATION_XEN       },
41                 { "KVMKVMKVM",    VIRTUALIZATION_KVM       },
42                 /* http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458 */
43                 { "VMwareVMware", VIRTUALIZATION_VMWARE    },
44                 /* http://msdn.microsoft.com/en-us/library/ff542428.aspx */
45                 { "Microsoft Hv", VIRTUALIZATION_MICROSOFT },
46         };
47
48         uint32_t eax, ecx;
49         bool hypervisor;
50
51         /* http://lwn.net/Articles/301888/ */
52
53 #if defined (__i386__)
54 #define REG_a "eax"
55 #define REG_b "ebx"
56 #elif defined (__amd64__)
57 #define REG_a "rax"
58 #define REG_b "rbx"
59 #endif
60
61         /* First detect whether there is a hypervisor */
62         eax = 1;
63         __asm__ __volatile__ (
64                 /* ebx/rbx is being used for PIC! */
65                 "  push %%"REG_b"         \n\t"
66                 "  cpuid                  \n\t"
67                 "  pop %%"REG_b"          \n\t"
68
69                 : "=a" (eax), "=c" (ecx)
70                 : "0" (eax)
71         );
72
73         hypervisor = !!(ecx & 0x80000000U);
74
75         if (hypervisor) {
76                 union {
77                         uint32_t sig32[3];
78                         char text[13];
79                 } sig = {};
80                 unsigned j;
81
82                 /* There is a hypervisor, see what it is */
83                 eax = 0x40000000U;
84                 __asm__ __volatile__ (
85                         /* ebx/rbx is being used for PIC! */
86                         "  push %%"REG_b"         \n\t"
87                         "  cpuid                  \n\t"
88                         "  mov %%ebx, %1          \n\t"
89                         "  pop %%"REG_b"          \n\t"
90
91                         : "=a" (eax), "=r" (sig.sig32[0]), "=c" (sig.sig32[1]), "=d" (sig.sig32[2])
92                         : "0" (eax)
93                 );
94
95                 for (j = 0; j < ELEMENTSOF(cpuid_vendor_table); j ++)
96                         if (streq(sig.text, cpuid_vendor_table[j].cpuid))
97                                 return cpuid_vendor_table[j].id;
98
99                 return VIRTUALIZATION_VM_OTHER;
100         }
101 #endif
102
103         return VIRTUALIZATION_NONE;
104 }
105
106 static int detect_vm_device_tree(void) {
107 #if defined(__arm__) || defined(__aarch64__) || defined(__powerpc__) || defined(__powerpc64__)
108         _cleanup_free_ char *hvtype = NULL;
109         int r;
110
111         r = read_one_line_file("/proc/device-tree/hypervisor/compatible", &hvtype);
112         if (r == -ENOENT) {
113                 _cleanup_closedir_ DIR *dir = NULL;
114                 struct dirent *dent;
115
116                 dir = opendir("/proc/device-tree");
117                 if (!dir) {
118                         if (errno == ENOENT)
119                                 return VIRTUALIZATION_NONE;
120                         return -errno;
121                 }
122
123                 FOREACH_DIRENT(dent, dir, return -errno)
124                         if (strstr(dent->d_name, "fw-cfg"))
125                                 return VIRTUALIZATION_QEMU;
126
127                 return VIRTUALIZATION_NONE;
128         } else if (r < 0)
129                 return r;
130
131         if (streq(hvtype, "linux,kvm"))
132                 return VIRTUALIZATION_KVM;
133         else if (strstr(hvtype, "xen"))
134                 return VIRTUALIZATION_XEN;
135         else
136                 return VIRTUALIZATION_VM_OTHER;
137 #else
138         return VIRTUALIZATION_NONE;
139 #endif
140 }
141
142 static int detect_vm_dmi(void) {
143 #if defined(__i386__) || defined(__x86_64__) || defined(__arm__) || defined(__aarch64__)
144
145         static const char *const dmi_vendors[] = {
146                 "/sys/class/dmi/id/sys_vendor",
147                 "/sys/class/dmi/id/board_vendor",
148                 "/sys/class/dmi/id/bios_vendor"
149         };
150
151         static const struct {
152                 const char *vendor;
153                 int id;
154         } dmi_vendor_table[] = {
155                 { "QEMU",          VIRTUALIZATION_QEMU      },
156                 /* http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458 */
157                 { "VMware",        VIRTUALIZATION_VMWARE    },
158                 { "VMW",           VIRTUALIZATION_VMWARE    },
159                 { "innotek GmbH",  VIRTUALIZATION_ORACLE    },
160                 { "Xen",           VIRTUALIZATION_XEN       },
161                 { "Bochs",         VIRTUALIZATION_BOCHS     },
162                 { "Parallels",     VIRTUALIZATION_PARALLELS },
163         };
164         unsigned i;
165         int r;
166
167         for (i = 0; i < ELEMENTSOF(dmi_vendors); i++) {
168                 _cleanup_free_ char *s = NULL;
169                 unsigned j;
170
171                 r = read_one_line_file(dmi_vendors[i], &s);
172                 if (r < 0) {
173                         if (r == -ENOENT)
174                         continue;
175
176                         return r;
177                         }
178
179                 for (j = 0; j < ELEMENTSOF(dmi_vendor_table); j++)
180                         if (startswith(s, dmi_vendor_table[j].vendor))
181                                 return dmi_vendor_table[j].id;
182         }
183 #endif
184
185         return VIRTUALIZATION_NONE;
186 }
187
188 static int detect_vm_xen(void) {
189         _cleanup_free_ char *domcap = NULL;
190         char *cap, *i;
191         int r;
192
193         r = read_one_line_file("/proc/xen/capabilities", &domcap);
194         if (r == -ENOENT)
195                 return VIRTUALIZATION_NONE;
196
197         i = domcap;
198                 while ((cap = strsep(&i, ",")))
199                         if (streq(cap, "control_d"))
200                                 break;
201
202         return cap ? VIRTUALIZATION_NONE : VIRTUALIZATION_XEN;
203                 }
204
205 static int detect_vm_hypervisor(void) {
206                 _cleanup_free_ char *hvtype = NULL;
207         int r;
208
209                 r = read_one_line_file("/sys/hypervisor/type", &hvtype);
210         if (r == -ENOENT)
211                 return VIRTUALIZATION_NONE;
212         if (r < 0)
213                 return r;
214
215         if (streq(hvtype, "xen"))
216                 return VIRTUALIZATION_XEN;
217         else
218                 return VIRTUALIZATION_VM_OTHER;
219 }
220
221 static int detect_vm_uml(void) {
222         _cleanup_free_ char *cpuinfo_contents = NULL;
223         int r;
224
225         /* Detect User-Mode Linux by reading /proc/cpuinfo */
226         r = read_full_file("/proc/cpuinfo", &cpuinfo_contents, NULL);
227         if (r < 0)
228                 return r;
229         if (strstr(cpuinfo_contents, "\nvendor_id\t: User Mode Linux\n"))
230                 return VIRTUALIZATION_UML;
231
232         return VIRTUALIZATION_NONE;
233 }
234
235 static int detect_vm_zvm(void) {
236
237 #if defined(__s390__)
238         _cleanup_free_ char *t = NULL;
239         int r;
240
241         r = get_proc_field("/proc/sysinfo", "VM00 Control Program", WHITESPACE, &t);
242         if (r == -ENOENT)
243                 return VIRTUALIZATION_NONE;
244         if (r < 0)
245                 return r;
246
247         if (streq(t, "z/VM"))
248                 return VIRTUALIZATION_ZVM;
249         else
250                 return VIRTUALIZATION_KVM;
251 #else
252         return VIRTUALIZATION_NONE;
253 #endif
254         }
255
256 /* Returns a short identifier for the various VM implementations */
257 int detect_vm(void) {
258         static thread_local int cached_found = _VIRTUALIZATION_INVALID;
259         int r;
260
261         if (cached_found >= 0)
262                 return cached_found;
263
264         /* Try xen capabilities file first, if not found try
265          * high-level hypervisor sysfs file:
266          *
267          * https://bugs.freedesktop.org/show_bug.cgi?id=77271 */
268
269         r = detect_vm_xen();
270         if (r < 0)
271                 return r;
272         if (r != VIRTUALIZATION_NONE)
273                 goto finish;
274
275         r = detect_vm_dmi();
276         if (r < 0)
277                 return r;
278         if (r != VIRTUALIZATION_NONE)
279                 goto finish;
280
281         r = detect_vm_cpuid();
282         if (r < 0)
283                 return r;
284         if (r != VIRTUALIZATION_NONE)
285                 goto finish;
286
287         r = detect_vm_hypervisor();
288         if (r < 0)
289                 return r;
290         if (r != VIRTUALIZATION_NONE)
291                 goto finish;
292
293         r = detect_vm_device_tree();
294         if (r < 0)
295                 return r;
296         if (r != VIRTUALIZATION_NONE)
297                 goto finish;
298
299         r = detect_vm_uml();
300         if (r < 0)
301                 return r;
302         if (r != VIRTUALIZATION_NONE)
303                         goto finish;
304
305         r = detect_vm_zvm();
306         if (r < 0)
307                 return r;
308
309 finish:
310         cached_found = r;
311         return r;
312 }
313
314 int detect_container(void) {
315
316         static const struct {
317                 const char *value;
318                 int id;
319         } value_table[] = {
320                 { "lxc",            VIRTUALIZATION_LXC            },
321                 { "lxc-libvirt",    VIRTUALIZATION_LXC_LIBVIRT    },
322                 { "systemd-nspawn", VIRTUALIZATION_SYSTEMD_NSPAWN },
323                 { "docker",         VIRTUALIZATION_DOCKER         },
324         };
325
326         static thread_local int cached_found = _VIRTUALIZATION_INVALID;
327         _cleanup_free_ char *m = NULL;
328         const char *e = NULL;
329         unsigned j;
330         int r;
331
332         if (cached_found >= 0)
333                 return cached_found;
334
335         /* /proc/vz exists in container and outside of the container,
336          * /proc/bc only outside of the container. */
337         if (access("/proc/vz", F_OK) >= 0 &&
338             access("/proc/bc", F_OK) < 0) {
339                 r = VIRTUALIZATION_OPENVZ;
340                 goto finish;
341         }
342
343         if (getpid() == 1) {
344                 /* If we are PID 1 we can just check our own
345                  * environment variable */
346
347                 e = getenv("container");
348                 if (isempty(e)) {
349                         r = VIRTUALIZATION_NONE;
350                         goto finish;
351                 }
352         } else {
353
354                 /* Otherwise, PID 1 dropped this information into a
355                  * file in /run. This is better than accessing
356                  * /proc/1/environ, since we don't need CAP_SYS_PTRACE
357                  * for that. */
358
359                 r = read_one_line_file("/run/systemd/container", &m);
360                 if (r == -ENOENT) {
361
362                         /* Fallback for cases where PID 1 was not
363                          * systemd (for example, cases where
364                          * init=/bin/sh is used. */
365
366                         r = getenv_for_pid(1, "container", &m);
367                         if (r <= 0) {
368
369                                 /* If that didn't work, give up,
370                                  * assume no container manager.
371                                  *
372                                  * Note: This means we still cannot
373                                  * detect containers if init=/bin/sh
374                                  * is passed but privileges dropped,
375                                  * as /proc/1/environ is only readable
376                                  * with privileges. */
377
378                                 r = VIRTUALIZATION_NONE;
379                                 goto finish;
380                         }
381                 }
382                 if (r < 0)
383                         return r;
384
385                 e = m;
386         }
387
388         for (j = 0; j < ELEMENTSOF(value_table); j++)
389                 if (streq(e, value_table[j].value)) {
390                         r = value_table[j].id;
391                         goto finish;
392                 }
393
394         r = VIRTUALIZATION_NONE;
395
396 finish:
397         cached_found = r;
398         return r;
399 }
400
401 /// UNNEEDED by elogind
402 #if 0
403 int detect_virtualization(void) {
404         int r;
405
406         r = detect_container();
407         if (r != 0)
408                 return r;
409
410         return detect_vm();
411 }
412 #endif // 0
413
414 static const char *const virtualization_table[_VIRTUALIZATION_MAX] = {
415         [VIRTUALIZATION_NONE] = "none",
416         [VIRTUALIZATION_KVM] = "kvm",
417         [VIRTUALIZATION_QEMU] = "qemu",
418         [VIRTUALIZATION_BOCHS] = "bochs",
419         [VIRTUALIZATION_XEN] = "xen",
420         [VIRTUALIZATION_UML] = "uml",
421         [VIRTUALIZATION_VMWARE] = "vmware",
422         [VIRTUALIZATION_ORACLE] = "oracle",
423         [VIRTUALIZATION_MICROSOFT] = "microsoft",
424         [VIRTUALIZATION_ZVM] = "zvm",
425         [VIRTUALIZATION_PARALLELS] = "parallels",
426         [VIRTUALIZATION_VM_OTHER] = "vm-other",
427
428         [VIRTUALIZATION_SYSTEMD_NSPAWN] = "systemd-nspawn",
429         [VIRTUALIZATION_LXC_LIBVIRT] = "lxc-libvirt",
430         [VIRTUALIZATION_LXC] = "lxc",
431         [VIRTUALIZATION_OPENVZ] = "openvz",
432         [VIRTUALIZATION_DOCKER] = "docker",
433         [VIRTUALIZATION_CONTAINER_OTHER] = "container-other",
434 };
435
436 DEFINE_STRING_TABLE_LOOKUP(virtualization, int);