summaryrefslogtreecommitdiffstats
path: root/sys-utils
diff options
context:
space:
mode:
authorOndrej Oprala2013-04-22 14:18:24 +0200
committerKarel Zak2013-05-20 16:30:23 +0200
commitfb2627cec4c85e9af30df417e6bda0e98b6cd0be (patch)
tree27909e9cdae1d4afc30808f784e084281fe34c77 /sys-utils
parentcfdisk: add long options to the command (diff)
downloadkernel-qcow2-util-linux-fb2627cec4c85e9af30df417e6bda0e98b6cd0be.tar.gz
kernel-qcow2-util-linux-fb2627cec4c85e9af30df417e6bda0e98b6cd0be.tar.xz
kernel-qcow2-util-linux-fb2627cec4c85e9af30df417e6bda0e98b6cd0be.zip
lscpu: detect more hypervisor vendors
[kzak@redhat.com: - cleanup coding style, - use path_exist()] Signed-off-by: Ondrej Oprala <ooprala@redhat.com> Signed-off-by: Karel Zak <kzak@redhat.com>
Diffstat (limited to 'sys-utils')
-rw-r--r--sys-utils/Makemodule.am5
-rw-r--r--sys-utils/lscpu-dmi.c272
-rw-r--r--sys-utils/lscpu.c72
-rw-r--r--sys-utils/lscpu.h21
4 files changed, 354 insertions, 16 deletions
diff --git a/sys-utils/Makemodule.am b/sys-utils/Makemodule.am
index 4139fcf65..977be5aff 100644
--- a/sys-utils/Makemodule.am
+++ b/sys-utils/Makemodule.am
@@ -239,7 +239,10 @@ endif
if BUILD_LSCPU
usrbin_exec_PROGRAMS += lscpu
-lscpu_SOURCES = sys-utils/lscpu.c
+lscpu_SOURCES = \
+ sys-utils/lscpu.c \
+ sys-utils/lscpu.h \
+ sys-utils/lscpu-dmi.c
lscpu_LDADD = $(LDADD) libcommon.la
dist_man_MANS += sys-utils/lscpu.1
endif
diff --git a/sys-utils/lscpu-dmi.c b/sys-utils/lscpu-dmi.c
new file mode 100644
index 000000000..21e024bc4
--- /dev/null
+++ b/sys-utils/lscpu-dmi.c
@@ -0,0 +1,272 @@
+/*
+ * lscpu-dmi - Module to parse SMBIOS information
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Code originally taken from the dmidecode utility and slightly rewritten
+ * to suite the needs of lscpu
+ */
+#include <errno.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "c.h"
+#include "pathnames.h"
+#include "all-io.h"
+#include "lscpu.h"
+
+#define WORD(x) (uint16_t)(*(const uint16_t *)(x))
+#define DWORD(x) (uint32_t)(*(const uint32_t *)(x))
+
+struct dmi_header
+{
+ uint8_t type;
+ uint8_t length;
+ uint16_t handle;
+ uint8_t *data;
+};
+
+static int checksum(const uint8_t *buf, size_t len)
+{
+ uint8_t sum = 0;
+ size_t a;
+
+ for (a = 0; a < len; a++)
+ sum += buf[a];
+ return (sum == 0);
+}
+
+static void *get_mem_chunk(size_t base, size_t len, const char *devmem)
+{
+ void *p;
+ int fd;
+
+ if ((fd = open(devmem, O_RDONLY)) == -1)
+ return NULL;
+ if ((p = malloc(len)) == NULL)
+ return NULL;
+ if (lseek(fd, base, SEEK_SET) == -1) {
+ free(p);
+ return NULL;
+ }
+ if (read_all(fd, p, len) == -1) {
+ free(p);
+ return NULL;
+ }
+
+ close(fd);
+ return p;
+}
+
+static void to_dmi_header(struct dmi_header *h, uint8_t *data)
+{
+ h->type = data[0];
+ h->length = data[1];
+ h->handle = WORD(data + 2);
+ h->data = data;
+}
+
+static char *dmi_string(const struct dmi_header *dm, uint8_t s)
+{
+ char *bp = (char *)dm->data;
+
+ if (s == 0)
+ return NULL;
+
+ bp += dm->length;
+ while (s > 1 && *bp)
+ {
+ bp += strlen(bp);
+ bp++;
+ s--;
+ }
+
+ if (!*bp)
+ return NULL;
+
+ return bp;
+}
+
+static int hypervisor_from_dmi_table(uint32_t base, uint16_t len,
+ uint16_t num, const char *devmem)
+{
+ uint8_t *buf = NULL;
+ uint8_t *data;
+ int i = 0;
+ char *vendor = NULL;
+ char *product = NULL;
+ char *manufacturer = NULL;
+
+ if ((buf = get_mem_chunk(base, len, devmem)) == NULL)
+ return HYPER_NONE;
+ data = buf;
+
+ /* 4 is the length of an SMBIOS structure header */
+ while (i < num && data + 4 <= buf + len) {
+ uint8_t *next;
+ struct dmi_header h;
+
+ to_dmi_header(&h, data);
+
+ /*
+ * If a short entry is found (less than 4 bytes), not only it
+ * is invalid, but we cannot reliably locate the next entry.
+ * Better stop at this point.
+ */
+ if (h.length < 4)
+ return HYPER_NONE;
+
+ /* look for the next handle */
+ next = data + h.length;
+ while (next - buf + 1 < len && (next[0] != 0 || next[1] != 0))
+ next++;
+ next += 2;
+ switch (h.type) {
+ case 0:
+ vendor = dmi_string(&h, data[0x04]);
+ break;
+ case 1:
+ manufacturer = dmi_string(&h, data[0x04]);
+ product = dmi_string(&h, data[0x05]);
+ break;
+ default:
+ break;
+ }
+
+ data = next;
+ i++;
+ }
+ if (!strcmp(manufacturer, "innotek GmbH"))
+ return HYPER_INNOTEK;
+ else if (strstr(manufacturer, "HITACHI") && strstr(product, "LPAR"))
+ return HYPER_HITACHI;
+ else if (!strcmp(vendor, "Parallels"))
+ return HYPER_PARALLELS;
+
+ free(buf);
+ return HYPER_NONE;
+}
+
+static int hypervisor_decode_legacy(uint8_t *buf, const char *devmem)
+{
+ if (!checksum(buf, 0x0F))
+ return HYPER_NONE;
+
+ return hypervisor_from_dmi_table(DWORD(buf + 0x08), WORD(buf + 0x06),
+ WORD(buf + 0x0C),
+ devmem);
+}
+
+static int hypervisor_decode_smbios(uint8_t *buf, const char *devmem)
+{
+ if (!checksum(buf, buf[0x05])
+ || memcmp(buf + 0x10, "_DMI_", 5) != 0
+ || !checksum(buf + 0x10, 0x0F))
+ return -1;
+
+ return hypervisor_from_dmi_table(DWORD(buf + 0x18), WORD(buf + 0x16),
+ WORD(buf + 0x1C),
+ devmem);
+}
+
+/*
+ * Probe for EFI interface
+ */
+#define EFI_NOT_FOUND (-1)
+#define EFI_NO_SMBIOS (-2)
+static int address_from_efi(size_t *address)
+{
+ FILE *tab;
+ char linebuf[64];
+ int ret;
+
+ *address = 0; /* Prevent compiler warning */
+
+ /*
+ * Linux up to 2.6.6: /proc/efi/systab
+ * Linux 2.6.7 and up: /sys/firmware/efi/systab
+ */
+ if (!(tab = fopen("/sys/firmware/efi/systab", "r")) &&
+ !(tab = fopen("/proc/efi/systab", "r")))
+ return EFI_NOT_FOUND; /* No EFI interface */
+
+ ret = EFI_NO_SMBIOS;
+ while ((fgets(linebuf, sizeof(linebuf) - 1, tab)) != NULL) {
+ char *addrp = strchr(linebuf, '=');
+ *(addrp++) = '\0';
+ if (strcmp(linebuf, "SMBIOS") == 0) {
+ *address = strtoul(addrp, NULL, 0);
+ ret = 0;
+ break;
+ }
+ }
+
+ fclose(tab);
+ return ret;
+}
+
+int read_hypervisor_dmi(void)
+{
+ int ret = HYPER_NONE;
+ uint8_t *buf = NULL;
+ size_t fp = 0;
+
+ if (sizeof(uint8_t) != 1
+ || sizeof(uint16_t) != 2
+ || sizeof(uint32_t) != 4
+ || '\0' != 0)
+ return ret;
+
+ /* First try EFI (ia64, Intel-based Mac) */
+ switch (address_from_efi(&fp)) {
+ case EFI_NOT_FOUND:
+ goto memory_scan;
+ case EFI_NO_SMBIOS:
+ goto exit_free;
+ }
+
+ buf = get_mem_chunk(fp, 0x20, _PATH_DEV_MEM);
+ if (!buf)
+ goto exit_free;
+
+ if (hypervisor_decode_smbios(buf, _PATH_DEV_MEM))
+ goto done;
+
+memory_scan:
+ /* Fallback to memory scan (x86, x86_64) */
+ buf = get_mem_chunk(0xF0000, 0x10000, _PATH_DEV_MEM);
+ if (!buf)
+ goto exit_free;
+
+ for (fp = 0; fp <= 0xFFF0; fp += 16) {
+ if (memcmp(buf + fp, "_SM_", 4) == 0 && fp <= 0xFFE0) {
+ if ((ret = hypervisor_decode_smbios(buf + fp,
+ _PATH_DEV_MEM)) == -1)
+ fp += 16;
+
+ } else if (memcmp(buf + fp, "_DMI_", 5) == 0)
+ ret = hypervisor_decode_legacy(buf + fp, _PATH_DEV_MEM);
+ }
+
+done:
+ free(buf);
+exit_free:
+ return ret;
+}
diff --git a/sys-utils/lscpu.c b/sys-utils/lscpu.c
index 2e08f66a9..58f3248e9 100644
--- a/sys-utils/lscpu.c
+++ b/sys-utils/lscpu.c
@@ -43,6 +43,7 @@
#include "path.h"
#include "closestream.h"
#include "optutils.h"
+#include "lscpu.h"
#define CACHE_MAX 100
@@ -54,6 +55,10 @@
#define _PATH_PROC_CPUINFO "/proc/cpuinfo"
#define _PATH_PROC_PCIDEVS "/proc/bus/pci/devices"
#define _PATH_PROC_SYSINFO "/proc/sysinfo"
+#define _PATH_PROC_STATUS "/proc/self/status"
+#define _PATH_PROC_VZ "/proc/vz"
+#define _PATH_PROC_BC "/proc/bc"
+#define _PATH_DEV_MEM "/dev/mem"
/* virtualization types */
enum {
@@ -67,22 +72,18 @@ const char *virt_types[] = {
[VIRT_FULL] = N_("full")
};
-/* hypervisor vendors */
-enum {
- HYPER_NONE = 0,
- HYPER_XEN,
- HYPER_KVM,
- HYPER_MSHV,
- HYPER_VMWARE,
- HYPER_IBM
-};
const char *hv_vendors[] = {
[HYPER_NONE] = NULL,
[HYPER_XEN] = "Xen",
[HYPER_KVM] = "KVM",
[HYPER_MSHV] = "Microsoft",
[HYPER_VMWARE] = "VMware",
- [HYPER_IBM] = "IBM"
+ [HYPER_IBM] = "IBM",
+ [HYPER_VSERVER] = "Linux-VServer",
+ [HYPER_UML] = "User-mode Linux",
+ [HYPER_INNOTEK] = "Innotek GmbH",
+ [HYPER_HITACHI] = "Hitachi",
+ [HYPER_PARALLELS] = "Parallels"
};
/* CPU modes */
@@ -529,17 +530,21 @@ read_hypervisor_cpuid(struct lscpu_desc *desc __attribute__((__unused__)))
static void
read_hypervisor(struct lscpu_desc *desc, struct lscpu_modifier *mod)
{
- if (mod->system != SYSTEM_SNAPSHOT)
+ FILE *fd;
+
+ if (mod->system != SYSTEM_SNAPSHOT) {
read_hypervisor_cpuid(desc);
+ if (!desc->hyper)
+ desc->hyper = read_hypervisor_dmi();
+ }
if (desc->hyper)
- /* hvm */
desc->virtype = VIRT_FULL;
+ /* Xen para-virt or dom0 */
else if (path_exist(_PATH_PROC_XEN)) {
- /* Xen para-virt or dom0 */
- FILE *fd = path_fopen("r", 0, _PATH_PROC_XENCAP);
int dom0 = 0;
+ fd = path_fopen("r", 0, _PATH_PROC_XENCAP);
if (fd) {
char buf[256];
@@ -552,10 +557,12 @@ read_hypervisor(struct lscpu_desc *desc, struct lscpu_modifier *mod)
desc->virtype = dom0 ? VIRT_NONE : VIRT_PARA;
desc->hyper = HYPER_XEN;
+ /* Xen full-virt on non-x86_64 */
} else if (has_pci_device(0x5853, 0x0001)) {
- /* Xen full-virt on non-x86_64 */
desc->hyper = HYPER_XEN;
desc->virtype = VIRT_FULL;
+
+ /* IBM PR/SM */
} else if (path_exist(_PATH_PROC_SYSINFO)) {
FILE *fd = path_fopen("r", 0, _PATH_PROC_SYSINFO);
char buf[BUFSIZ];
@@ -591,6 +598,41 @@ read_hypervisor(struct lscpu_desc *desc, struct lscpu_modifier *mod)
}
fclose(fd);
}
+
+ /* OpenVZ/Virtuozzo - /proc/vz dir should exist
+ * /proc/bc should not */
+ else if (path_exist(_PATH_PROC_VZ) && !path_exist(_PATH_PROC_BC))
+ desc->hyper = HYPER_PARALLELS;
+
+ /* IBM */
+ else if (desc->vendor &&
+ (strcmp(desc->vendor, "PowerVM Lx86") == 0 ||
+ strcmp(desc->vendor, "IBM/S390") == 0))
+ desc->hyper = HYPER_IBM;
+
+ /* User-mode-linux */
+ else if (desc->modelname && strstr(desc->modelname, "UML"))
+ desc->hyper = HYPER_UML;
+
+ /* Linux-VServer */
+ else if (path_exist(_PATH_PROC_STATUS)) {
+ char buf[BUFSIZ];
+ char *val = NULL;
+
+ fd = path_fopen("r", 0, _PATH_PROC_STATUS);
+ while (fgets(buf, sizeof(buf), fd) != NULL) {
+ if (lookup(buf, "VxID", &val))
+ break;
+ }
+ fclose(fd);
+
+ if (val) {
+ while (isdigit(*val))
+ ++val;
+ if (!*val)
+ desc->hyper = HYPER_VSERVER;
+ }
+ }
}
/* add @set to the @ary, unnecessary set is deallocated. */
diff --git a/sys-utils/lscpu.h b/sys-utils/lscpu.h
new file mode 100644
index 000000000..312038fcc
--- /dev/null
+++ b/sys-utils/lscpu.h
@@ -0,0 +1,21 @@
+#ifndef LSCPU_H
+#define LSCPU_H
+
+/* hypervisor vendors */
+enum {
+ HYPER_NONE = 0,
+ HYPER_XEN,
+ HYPER_KVM,
+ HYPER_MSHV,
+ HYPER_VMWARE,
+ HYPER_IBM, /* sys-z powervm */
+ HYPER_VSERVER,
+ HYPER_UML,
+ HYPER_INNOTEK, /* VBOX */
+ HYPER_HITACHI,
+ HYPER_PARALLELS /* OpenVZ/VIrtuozzo */
+};
+
+extern int read_hypervisor_dmi(void);
+
+#endif /* LSCPU_H */