]> git.ipfire.org Git - oddments/fireinfo.git/blame - src/fireinfo.c
Implement the VMWare hypervisor port check.
[oddments/fireinfo.git] / src / fireinfo.c
CommitLineData
5f4159ab
MT
1/*
2 * fireinfo.c
3 *
4 * Copyright (C) 2010 IPFire. All rights reserved.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20#include <Python.h>
21
3f70e7fd
MT
22#include <fcntl.h>
23#include <linux/hdreg.h>
4cd8b976 24#include <stdbool.h>
3f70e7fd 25#include <sys/ioctl.h>
5f4159ab
MT
26
27/*
28 Big parts of this were taken from
29 http://git.kernel.org/?p=utils/util-linux-ng/util-linux-ng.git;a=blob;f=sys-utils/lscpu.c
30*/
31
32/* /sys paths */
5f4159ab
MT
33#define _PATH_PROC_XEN "/proc/xen"
34#define _PATH_PROC_XENCAP _PATH_PROC_XEN "/capabilities"
5f4159ab
MT
35#define _PATH_PROC_PCIDEVS "/proc/bus/pci/devices"
36
2dc5f0d0
MT
37/* Used for the vmware hypervisor port detection */
38#define VMWARE_HYPERVISOR_MAGIC 0x564D5868
39#define VMWARE_HYPERVISOR_PORT 0x5658
40
41#define VMWARE_PORT_CMD_GETVERSION 10
42
43#define VMWARE_PORT(cmd, eax, ebx, ecx, edx) \
44 __asm__("inl (%%dx)" : \
45 "=a"(eax), "=c"(ecx), "=d"(edx), "=b"(ebx) : \
46 "0"(VMWARE_HYPERVISOR_MAGIC), \
47 "1"(VMWARE_PORT_CMD_##cmd), \
48 "2"(VMWARE_HYPERVISOR_PORT), "3"(UINT_MAX) : \
49 "memory");
50
5f4159ab
MT
51/* virtualization types */
52enum {
53 VIRT_NONE = 0,
54 VIRT_PARA,
55 VIRT_FULL
56};
57const char *virt_types[] = {
58 [VIRT_NONE] = "none",
59 [VIRT_PARA] = "para",
60 [VIRT_FULL] = "full"
61};
62
63/* hypervisor vendors */
64enum {
65 HYPER_NONE = 0,
66 HYPER_XEN,
67 HYPER_KVM,
284eedb7
MT
68 HYPER_MSHV,
69 HYPER_VMWARE
5f4159ab
MT
70};
71const char *hv_vendors[] = {
72 [HYPER_NONE] = NULL,
73 [HYPER_XEN] = "Xen",
74 [HYPER_KVM] = "KVM",
284eedb7
MT
75 [HYPER_MSHV] = "Microsoft",
76 [HYPER_VMWARE] = "VMWare"
5f4159ab
MT
77};
78
715ba5ac
MT
79struct hypervisor_desc {
80 int hyper; /* hypervisor vendor ID */
81 int virtype; /* VIRT_PARA|FULL|NONE ? */
82};
83
5f4159ab
MT
84static size_t sysrootlen;
85static char pathbuf[PATH_MAX];
5f4159ab 86
4c1e1034
MT
87static FILE *path_fopen(const char *mode, const char *path, ...)
88 __attribute__ ((__format__ (__printf__, 2, 3)));
5f4159ab
MT
89static int path_exist(const char *path, ...)
90 __attribute__ ((__format__ (__printf__, 1, 2)));
91
92static const char *
93path_vcreate(const char *path, va_list ap)
94{
95 if (sysrootlen)
96 vsnprintf(pathbuf + sysrootlen,
97 sizeof(pathbuf) - sysrootlen, path, ap);
98 else
99 vsnprintf(pathbuf, sizeof(pathbuf), path, ap);
100 return pathbuf;
101}
102
103static FILE *
4c1e1034 104path_vfopen(const char *mode, const char *path, va_list ap)
5f4159ab 105{
5f4159ab
MT
106 const char *p = path_vcreate(path, ap);
107
4c1e1034 108 return fopen(p, mode);
5f4159ab
MT
109}
110
111static FILE *
4c1e1034 112path_fopen(const char *mode, const char *path, ...)
5f4159ab
MT
113{
114 FILE *fd;
115 va_list ap;
116
117 va_start(ap, path);
4c1e1034 118 fd = path_vfopen(mode, path, ap);
5f4159ab
MT
119 va_end(ap);
120
121 return fd;
122}
123
5f4159ab
MT
124static int
125path_exist(const char *path, ...)
126{
127 va_list ap;
128 const char *p;
129
130 va_start(ap, path);
131 p = path_vcreate(path, ap);
132 va_end(ap);
133
134 return access(p, F_OK) == 0;
135}
136
5f4159ab
MT
137static int
138has_pci_device(int vendor, int device)
139{
140 FILE *f;
141 int num, fn, ven, dev;
142 int res = 1;
143
4c1e1034 144 f = path_fopen("r", _PATH_PROC_PCIDEVS);
5f4159ab
MT
145 if (!f)
146 return 0;
147
148 /* for more details about bus/pci/devices format see
149 * drivers/pci/proc.c in linux kernel
150 */
151 while(fscanf(f, "%02x%02x\t%04x%04x\t%*[^\n]",
152 &num, &fn, &ven, &dev) == 4) {
153
154 if (ven == vendor && dev == device)
155 goto found;
156 }
157
158 res = 0;
159found:
160 fclose(f);
161 return res;
162}
163
164#if defined(__x86_64__) || defined(__i386__)
165
166/*
167 * This CPUID leaf returns the information about the hypervisor.
168 * EAX : maximum input value for CPUID supported by the hypervisor.
169 * EBX, ECX, EDX : Hypervisor vendor ID signature. E.g. VMwareVMware.
170 */
171#define HYPERVISOR_INFO_LEAF 0x40000000
172
173static inline void
174cpuid(unsigned int op, unsigned int *eax, unsigned int *ebx,
175 unsigned int *ecx, unsigned int *edx)
176{
177 __asm__(
178#if defined(__PIC__) && defined(__i386__)
179 /* x86 PIC cannot clobber ebx -- gcc bitches */
180 "pushl %%ebx;"
181 "cpuid;"
182 "movl %%ebx, %%esi;"
183 "popl %%ebx;"
184 : "=S" (*ebx),
185#else
186 "cpuid;"
187 : "=b" (*ebx),
188#endif
189 "=a" (*eax),
190 "=c" (*ecx),
191 "=d" (*edx)
192 : "1" (op), "c"(0));
193}
194
4c1e1034 195static void
715ba5ac 196read_hypervisor_cpuid(struct hypervisor_desc *desc)
5f4159ab
MT
197{
198 unsigned int eax = 0, ebx = 0, ecx = 0, edx = 0;
199 char hyper_vendor_id[13];
200
201 memset(hyper_vendor_id, 0, sizeof(hyper_vendor_id));
202
203 cpuid(HYPERVISOR_INFO_LEAF, &eax, &ebx, &ecx, &edx);
204 memcpy(hyper_vendor_id + 0, &ebx, 4);
205 memcpy(hyper_vendor_id + 4, &ecx, 4);
206 memcpy(hyper_vendor_id + 8, &edx, 4);
207 hyper_vendor_id[12] = '\0';
208
209 if (!hyper_vendor_id[0])
210 return;
211
212 if (!strncmp("XenVMMXenVMM", hyper_vendor_id, 12))
213 desc->hyper = HYPER_XEN;
214 else if (!strncmp("KVMKVMKVM", hyper_vendor_id, 9))
215 desc->hyper = HYPER_KVM;
216 else if (!strncmp("Microsoft Hv", hyper_vendor_id, 12))
217 desc->hyper = HYPER_MSHV;
284eedb7
MT
218 else if (!strncmp("VMwareVMware", hyper_vendor_id, 12))
219 desc->hyper = HYPER_VMWARE;
5f4159ab
MT
220}
221
222#else /* ! __x86_64__ */
223static void
4c1e1034 224read_hypervisor_cpuid(struct hypervisor_desc *desc)
5f4159ab
MT
225{
226}
227#endif
228
229static void
715ba5ac 230read_hypervisor(struct hypervisor_desc *desc)
5f4159ab
MT
231{
232 read_hypervisor_cpuid(desc);
233
234 if (desc->hyper)
235 /* hvm */
236 desc->virtype = VIRT_FULL;
237
238 else if (path_exist(_PATH_PROC_XEN)) {
239 /* Xen para-virt or dom0 */
4c1e1034 240 FILE *fd = path_fopen("r", _PATH_PROC_XENCAP);
5f4159ab
MT
241 int dom0 = 0;
242
243 if (fd) {
244 char buf[256];
245
246 if (fscanf(fd, "%s", buf) == 1 &&
247 !strcmp(buf, "control_d"))
248 dom0 = 1;
249 fclose(fd);
250 }
251 desc->virtype = dom0 ? VIRT_NONE : VIRT_PARA;
252 desc->hyper = HYPER_XEN;
253
254 } else if (has_pci_device(0x5853, 0x0001)) {
255 /* Xen full-virt on non-x86_64 */
256 desc->hyper = HYPER_XEN;
257 desc->virtype = VIRT_FULL;
258 }
259}
260
3f70e7fd
MT
261static void
262read_harddisk_serial(char *device, char *serial) {
263 static struct hd_driveid hd;
264 int fd;
265
266 if ((fd = open(device, O_RDONLY | O_NONBLOCK)) < 0) {
267 return;
268 }
269
270 if (!ioctl(fd, HDIO_GET_IDENTITY, &hd)) {
5196c0c2 271 strncpy(serial, (const char *)hd.serial_no, 20);
3f70e7fd
MT
272 }
273}
274
4cd8b976
FM
275static bool
276is_virtualized() {
277 unsigned int eax, ebx, ecx, edx;
278
279 cpuid(0x1, &eax, &ebx, &ecx, &edx);
280
281 /*
282 Bitwise detection of the 31st bit.
283 This indicates if a host runs in a virtual environment.
284 */
285 if (ecx & (1<<31))
286 return true;
287
288 return false;
289}
290
2dc5f0d0
MT
291int
292hypervisor_port_check(void) {
293 uint32_t eax, ebx, ecx, edx;
294 VMWARE_PORT(GETVERSION, eax, ebx, ecx, edx);
295 if (ebx == VMWARE_HYPERVISOR_MAGIC)
296 return 1; // Success - running under VMware
297 else
298 return 0;
299}
300
715ba5ac
MT
301static PyObject *
302do_get_hypervisor() {
303 /*
304 Get hypervisor from the cpuid command.
305 */
306 struct hypervisor_desc _desc, *desc = &_desc;
307 memset(desc, 0, sizeof(*desc));
308
309 read_hypervisor(desc);
310
311 PyObject *d = PyDict_New();
312 PyObject *o;
313
314 /* Hypervisor */
315 if (desc->hyper == HYPER_NONE) {
316 o = Py_None;
317 } else {
318 o = PyString_FromString((const char *)hv_vendors[desc->hyper]);
319 }
320 PyDict_SetItemString(d, "hypervisor", o);
321
322 /* Virtualization type */
323 if (desc->virtype == VIRT_NONE) {
324 o = Py_None;
325 } else {
326 o = PyString_FromString((const char *)virt_types[desc->virtype]);
327 }
328 PyDict_SetItemString(d, "virtype", o);
329
330 return d;
331}
332
7cd41cc1
MT
333static PyObject *
334do_is_virtualized() {
335 /*
336 Python wrapper around is_virtualized().
337 */
338
339 if (is_virtualized())
340 return Py_True;
341
342 return Py_False;
343}
344
3f70e7fd
MT
345static PyObject *
346do_get_harddisk_serial(PyObject *o, PyObject *args) {
347 /*
348 Python wrapper around read_harddisk_serial.
349 */
350
351 char serial[21];
352 memset(serial, 0, sizeof(serial));
353
354 char *device;
355 if (!PyArg_ParseTuple(args, "s", &device))
356 return NULL;
357
358 read_harddisk_serial(device, serial);
359
360 if (serial[0])
361 return PyString_FromString(serial);
362
363 return Py_None;
364}
365
2dc5f0d0
MT
366static PyObject *
367do_hypervisor_port_check() {
368 /*
369 Python wrapper around hypervisor_port_check().
370 */
371
372 if (hypervisor_port_check())
373 return Py_True;
374
375 return Py_False;
376}
377
5f4159ab 378static PyMethodDef fireinfoModuleMethods[] = {
715ba5ac 379 { "get_hypervisor", (PyCFunction) do_get_hypervisor, METH_NOARGS, NULL },
7cd41cc1 380 { "is_virtualized", (PyCFunction) do_is_virtualized, METH_NOARGS, NULL },
3f70e7fd 381 { "get_harddisk_serial", (PyCFunction) do_get_harddisk_serial, METH_VARARGS, NULL },
2dc5f0d0 382 { "vmware_hypervisor_port_check", (PyCFunction) do_hypervisor_port_check, METH_NOARGS, NULL },
5f4159ab
MT
383 { NULL, NULL, 0, NULL }
384};
385
386void init_fireinfo(void) {
387 PyObject *m;
388
389 m = Py_InitModule("_fireinfo", fireinfoModuleMethods);
390}