]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/virt.c
man: document ARM root partition types
[thirdparty/systemd.git] / src / shared / virt.c
CommitLineData
b52aae1d
LP
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
5430f7f2
LP
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
b52aae1d
LP
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
5430f7f2 16 Lesser General Public License for more details.
b52aae1d 17
5430f7f2 18 You should have received a copy of the GNU Lesser General Public License
b52aae1d
LP
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 "virt.h"
a5c32cff 28#include "fileio.h"
b52aae1d 29
bdb628ee 30static int detect_vm_cpuid(const char **_id) {
b52aae1d
LP
31
32 /* Both CPUID and DMI are x86 specific interfaces... */
bdb628ee 33#if defined(__i386__) || defined(__x86_64__)
b52aae1d
LP
34
35 static const char cpuid_vendor_table[] =
36 "XenVMMXenVMM\0" "xen\0"
37 "KVMKVMKVM\0" "kvm\0"
38 /* http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458 */
39 "VMwareVMware\0" "vmware\0"
40 /* http://msdn.microsoft.com/en-us/library/ff542428.aspx */
41 "Microsoft Hv\0" "microsoft\0";
42
43 uint32_t eax, ecx;
44 union {
45 uint32_t sig32[3];
46 char text[13];
b92bea5d 47 } sig = {};
b52aae1d
LP
48 const char *j, *k;
49 bool hypervisor;
50
51 /* http://lwn.net/Articles/301888/ */
b52aae1d
LP
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
77 /* There is a hypervisor, see what it is */
78 eax = 0x40000000U;
79 __asm__ __volatile__ (
80 /* ebx/rbx is being used for PIC! */
81 " push %%"REG_b" \n\t"
82 " cpuid \n\t"
83 " mov %%ebx, %1 \n\t"
84 " pop %%"REG_b" \n\t"
85
86 : "=a" (eax), "=r" (sig.sig32[0]), "=c" (sig.sig32[1]), "=d" (sig.sig32[2])
87 : "0" (eax)
88 );
89
90 NULSTR_FOREACH_PAIR(j, k, cpuid_vendor_table)
91 if (streq(sig.text, j)) {
bdb628ee
ZJS
92 *_id = k;
93 return 1;
b52aae1d 94 }
bdb628ee
ZJS
95
96 *_id = "other";
97 return 0;
b52aae1d 98 }
bdb628ee
ZJS
99#endif
100
101 return 0;
102}
103
104static int detect_vm_dmi(const char **_id) {
105
106 /* Both CPUID and DMI are x86 specific interfaces... */
107#if defined(__i386__) || defined(__x86_64__)
108
109 static const char *const dmi_vendors[] = {
110 "/sys/class/dmi/id/sys_vendor",
111 "/sys/class/dmi/id/board_vendor",
112 "/sys/class/dmi/id/bios_vendor"
113 };
114
115 static const char dmi_vendor_table[] =
116 "QEMU\0" "qemu\0"
117 /* http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458 */
118 "VMware\0" "vmware\0"
119 "VMW\0" "vmware\0"
120 "Microsoft Corporation\0" "microsoft\0"
121 "innotek GmbH\0" "oracle\0"
122 "Xen\0" "xen\0"
123 "Bochs\0" "bochs\0";
124 unsigned i;
b52aae1d
LP
125
126 for (i = 0; i < ELEMENTSOF(dmi_vendors); i++) {
b1b8e816 127 _cleanup_free_ char *s = NULL;
bdb628ee
ZJS
128 const char *j, *k;
129 int r;
b52aae1d 130
b1b8e816
LP
131 r = read_one_line_file(dmi_vendors[i], &s);
132 if (r < 0) {
b52aae1d
LP
133 if (r != -ENOENT)
134 return r;
135
136 continue;
137 }
138
139 NULSTR_FOREACH_PAIR(j, k, dmi_vendor_table)
0fb533a5 140 if (startswith(s, j)) {
bdb628ee
ZJS
141 *_id = k;
142 return 1;
0fb533a5 143 }
b52aae1d 144 }
bdb628ee
ZJS
145#endif
146
147 return 0;
148}
149
150/* Returns a short identifier for the various VM implementations */
151int detect_vm(const char **id) {
152 _cleanup_free_ char *hvtype = NULL, *cpuinfo_contents = NULL;
ec202eae
SL
153 static thread_local int cached_found = -1;
154 static thread_local const char *cached_id = NULL;
bdb628ee
ZJS
155 const char *_id = NULL;
156 int r;
b52aae1d 157
bdb628ee
ZJS
158 if (_likely_(cached_found >= 0)) {
159
160 if (id)
161 *id = cached_id;
162
163 return cached_found;
164 }
165
166 /* Try high-level hypervisor sysfs file first:
167 *
168 * https://bugs.freedesktop.org/show_bug.cgi?id=61491 */
169 r = read_one_line_file("/sys/hypervisor/type", &hvtype);
170 if (r >= 0) {
171 if (streq(hvtype, "xen")) {
172 _id = "xen";
173 r = 1;
174 goto finish;
175 }
176 } else if (r != -ENOENT)
177 return r;
178
179 /* this will set _id to "other" and return 0 for unknown hypervisors */
180 r = detect_vm_cpuid(&_id);
181 if (r != 0)
182 goto finish;
183
184 r = detect_vm_dmi(&_id);
185 if (r != 0)
186 goto finish;
187
188 if (_id) {
189 /* "other" */
0fb533a5
LP
190 r = 1;
191 goto finish;
b52aae1d
LP
192 }
193
7080ea16
RR
194 /* Detect User-Mode Linux by reading /proc/cpuinfo */
195 r = read_full_file("/proc/cpuinfo", &cpuinfo_contents, NULL);
196 if (r < 0)
197 return r;
198 if (strstr(cpuinfo_contents, "\nvendor_id\t: User Mode Linux\n")) {
0fb533a5
LP
199 _id = "uml";
200 r = 1;
201 goto finish;
7080ea16
RR
202 }
203
0fb533a5
LP
204 r = 0;
205
206finish:
207 cached_found = r;
208
209 cached_id = _id;
210 if (id)
211 *id = _id;
212
213 return r;
b52aae1d
LP
214}
215
216int detect_container(const char **id) {
0fb533a5 217
ec202eae
SL
218 static thread_local int cached_found = -1;
219 static thread_local const char *cached_id = NULL;
0fb533a5 220
7fd1b19b 221 _cleanup_free_ char *e = NULL;
0fb533a5 222 const char *_id = NULL;
ab94af92 223 int r;
b52aae1d 224
0fb533a5
LP
225 if (_likely_(cached_found >= 0)) {
226
227 if (id)
228 *id = cached_id;
229
230 return cached_found;
231 }
232
b52aae1d
LP
233 /* Unfortunately many of these operations require root access
234 * in one way or another */
235
96ede260
LP
236 r = running_in_chroot();
237 if (r < 0)
238 return r;
239 if (r > 0) {
0fb533a5
LP
240 _id = "chroot";
241 goto finish;
b52aae1d
LP
242 }
243
244 /* /proc/vz exists in container and outside of the container,
245 * /proc/bc only outside of the container. */
246 if (access("/proc/vz", F_OK) >= 0 &&
247 access("/proc/bc", F_OK) < 0) {
0fb533a5
LP
248 _id = "openvz";
249 r = 1;
250 goto finish;
b52aae1d
LP
251 }
252
ab94af92 253 r = getenv_for_pid(1, "container", &e);
0fb533a5 254 if (r < 0)
ab94af92 255 return r;
0fb533a5
LP
256 if (r == 0)
257 goto finish;
b52aae1d 258
ab94af92
LP
259 /* We only recognize a selected few here, since we want to
260 * enforce a redacted namespace */
0fb533a5
LP
261 if (streq(e, "lxc"))
262 _id ="lxc";
263 else if (streq(e, "lxc-libvirt"))
264 _id = "lxc-libvirt";
265 else if (streq(e, "systemd-nspawn"))
266 _id = "systemd-nspawn";
267 else
268 _id = "other";
269
270finish:
271 cached_found = r;
272
273 cached_id = _id;
274 if (id)
275 *id = _id;
b52aae1d 276
ab94af92 277 return r;
b52aae1d
LP
278}
279
280/* Returns a short identifier for the various VM/container implementations */
248fab74 281int detect_virtualization(const char **id) {
b52aae1d 282 int r;
b52aae1d 283
0fb533a5
LP
284 r = detect_container(id);
285 if (r < 0)
286 return r;
287 if (r > 0)
288 return VIRTUALIZATION_CONTAINER;
b52aae1d 289
0fb533a5
LP
290 r = detect_vm(id);
291 if (r < 0)
292 return r;
293 if (r > 0)
294 return VIRTUALIZATION_VM;
b52aae1d 295
0fb533a5 296 return VIRTUALIZATION_NONE;
b52aae1d 297}