cdown / rpms / util-linux

Forked from rpms/util-linux 2 years ago
Clone

Blame SOURCES/0115-lscpu-backport-from-v2.29.patch

05ad79
From 7ffb3c628dea313496c829bcb40447545470847e Mon Sep 17 00:00:00 2001
05ad79
From: Karel Zak <kzak@redhat.com>
05ad79
Date: Tue, 21 Mar 2017 14:57:37 +0100
05ad79
Subject: [PATCH 115/116] lscpu: backport from v2.29
05ad79
05ad79
Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1360764
05ad79
Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1397709
05ad79
Signed-off-by: Karel Zak <kzak@redhat.com>
05ad79
---
05ad79
 configure.ac            |   3 +-
05ad79
 include/c.h             |  30 ++
05ad79
 include/pathnames.h     |   2 +
05ad79
 include/xalloc.h        |  10 +
05ad79
 sys-utils/Makemodule.am |   8 +-
05ad79
 sys-utils/lscpu-dmi.c   | 285 ++++++++++++++
05ad79
 sys-utils/lscpu.1       |  53 ++-
05ad79
 sys-utils/lscpu.c       | 964 ++++++++++++++++++++++++++++++++++++++++--------
05ad79
 sys-utils/lscpu.h       |  26 ++
05ad79
 9 files changed, 1220 insertions(+), 161 deletions(-)
05ad79
 create mode 100644 sys-utils/lscpu-dmi.c
05ad79
 create mode 100644 sys-utils/lscpu.h
05ad79
05ad79
diff --git a/configure.ac b/configure.ac
05ad79
index db7095a..78258d6 100644
05ad79
--- a/configure.ac
05ad79
+++ b/configure.ac
05ad79
@@ -1059,8 +1059,9 @@ AM_CONDITIONAL(BUILD_LSBLK, test "x$build_lsblk" = xyes)
05ad79
 
05ad79
 UL_BUILD_INIT([lscpu], [check])
05ad79
 UL_REQUIRES_LINUX([lscpu])
05ad79
+UL_REQUIRES_BUILD([lscpu], [libsmartcols])
05ad79
 UL_REQUIRES_HAVE([lscpu], [cpu_set_t], [cpu_set_t type])
05ad79
-AM_CONDITIONAL(BUILD_LSCPU, test "x$build_lscpu" = xyes)
05ad79
+AM_CONDITIONAL([BUILD_LSCPU], [test "x$build_lscpu" = xyes])
05ad79
 
05ad79
 
05ad79
 UL_BUILD_INIT([lslogins], [check])
05ad79
diff --git a/include/c.h b/include/c.h
05ad79
index 3754e75..8ff61b4 100644
05ad79
--- a/include/c.h
05ad79
+++ b/include/c.h
05ad79
@@ -200,6 +200,19 @@ errmsg(char doexit, int excode, char adderr, const char *fmt, ...)
05ad79
 #endif
05ad79
 #endif /* !HAVE_ERR_H */
05ad79
 
05ad79
+/* Don't use inline function to avoid '#include "nls.h"' in c.h
05ad79
+ */
05ad79
+#define errtryhelp(eval) __extension__ ({ \
05ad79
+	fprintf(stderr, _("Try '%s --help' for more information.\n"), \
05ad79
+			program_invocation_short_name); \
05ad79
+	exit(eval); \
05ad79
+})
05ad79
+
05ad79
+#define errtryh(eval) __extension__ ({ \
05ad79
+	fprintf(stderr, _("Try '%s -h' for more information.\n"), \
05ad79
+			program_invocation_short_name); \
05ad79
+	exit(eval); \
05ad79
+})
05ad79
 
05ad79
 static inline __attribute__((const)) int is_power_of_2(unsigned long num)
05ad79
 {
05ad79
@@ -317,6 +330,23 @@ static inline int usleep(useconds_t usec)
05ad79
 #define stringify(s) #s
05ad79
 
05ad79
 /*
05ad79
+ * UL_ASAN_BLACKLIST is a macro to tell AddressSanitizer (a compile-time
05ad79
+ * instrumentation shipped with Clang and GCC) to not instrument the
05ad79
+ * annotated function.  Furthermore, it will prevent the compiler from
05ad79
+ * inlining the function because inlining currently breaks the blacklisting
05ad79
+ * mechanism of AddressSanitizer.
05ad79
+ */
05ad79
+#if defined(__has_feature)
05ad79
+# if __has_feature(address_sanitizer)
05ad79
+#  define UL_ASAN_BLACKLIST __attribute__((noinline)) __attribute__((no_sanitize_memory)) __attribute__((no_sanitize_address))
05ad79
+# else
05ad79
+#  define UL_ASAN_BLACKLIST	/* nothing */
05ad79
+# endif
05ad79
+#else
05ad79
+# define UL_ASAN_BLACKLIST	/* nothing */
05ad79
+#endif
05ad79
+
05ad79
+/*
05ad79
  * Note that sysconf(_SC_GETPW_R_SIZE_MAX) returns *initial* suggested size for
05ad79
  * pwd buffer and in some cases it is not large enough. See POSIX and
05ad79
  * getpwnam_r man page for more details.
05ad79
diff --git a/include/pathnames.h b/include/pathnames.h
05ad79
index b648afc..fa4bddb 100644
05ad79
--- a/include/pathnames.h
05ad79
+++ b/include/pathnames.h
05ad79
@@ -131,6 +131,8 @@
05ad79
 # define _PATH_DEV		"/dev/"
05ad79
 #endif
05ad79
 
05ad79
+#define _PATH_DEV_MEM           "/dev/mem"
05ad79
+
05ad79
 #define _PATH_DEV_LOOP		"/dev/loop"
05ad79
 #define _PATH_DEV_LOOPCTL	"/dev/loop-control"
05ad79
 #define _PATH_DEV_TTY		"/dev/tty"
05ad79
diff --git a/include/xalloc.h b/include/xalloc.h
05ad79
index 1a1799a..883e472 100644
05ad79
--- a/include/xalloc.h
05ad79
+++ b/include/xalloc.h
05ad79
@@ -99,6 +99,16 @@ static inline int __attribute__ ((__format__(printf, 2, 3)))
05ad79
 }
05ad79
 
05ad79
 
05ad79
+static inline int  __attribute__ ((__format__(printf, 2, 0)))
05ad79
+xvasprintf(char **strp, const char *fmt, va_list ap)
05ad79
+{
05ad79
+	int ret = vasprintf(&(*strp), fmt, ap);
05ad79
+	if (ret < 0)
05ad79
+		err(XALLOC_EXIT_CODE, "cannot allocate string");
05ad79
+	return ret;
05ad79
+}
05ad79
+
05ad79
+
05ad79
 static inline char *xgethostname(void)
05ad79
 {
05ad79
 	char *name;
05ad79
diff --git a/sys-utils/Makemodule.am b/sys-utils/Makemodule.am
05ad79
index 408e884..0496b84 100644
05ad79
--- a/sys-utils/Makemodule.am
05ad79
+++ b/sys-utils/Makemodule.am
05ad79
@@ -274,8 +274,12 @@ endif
05ad79
 
05ad79
 if BUILD_LSCPU
05ad79
 usrbin_exec_PROGRAMS += lscpu
05ad79
-lscpu_SOURCES = sys-utils/lscpu.c
05ad79
-lscpu_LDADD = $(LDADD) libcommon.la
05ad79
+lscpu_SOURCES = \
05ad79
+	sys-utils/lscpu.c \
05ad79
+	sys-utils/lscpu.h \
05ad79
+	sys-utils/lscpu-dmi.c
05ad79
+lscpu_LDADD = $(LDADD) libcommon.la libsmartcols.la $(RTAS_LIBS)
05ad79
+lscpu_CFLAGS = $(AM_CFLAGS) -I$(ul_libsmartcols_incdir)
05ad79
 dist_man_MANS += sys-utils/lscpu.1
05ad79
 endif
05ad79
 
05ad79
diff --git a/sys-utils/lscpu-dmi.c b/sys-utils/lscpu-dmi.c
05ad79
new file mode 100644
05ad79
index 0000000..0e497d1
05ad79
--- /dev/null
05ad79
+++ b/sys-utils/lscpu-dmi.c
05ad79
@@ -0,0 +1,285 @@
05ad79
+/*
05ad79
+ * lscpu-dmi - Module to parse SMBIOS information
05ad79
+ *
05ad79
+ * This program is free software; you can redistribute it and/or modify
05ad79
+ * it under the terms of the GNU General Public License as published by
05ad79
+ * the Free Software Foundation; either version 2 of the License, or
05ad79
+ * (at your option) any later version.
05ad79
+ *
05ad79
+ * This program is distributed in the hope that it would be useful,
05ad79
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
05ad79
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
05ad79
+ * GNU General Public License for more details.
05ad79
+ *
05ad79
+ * You should have received a copy of the GNU General Public License along
05ad79
+ * with this program; if not, write to the Free Software Foundation, Inc.,
05ad79
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
05ad79
+ *
05ad79
+ * Code originally taken from the dmidecode utility and slightly rewritten
05ad79
+ * to suite the needs of lscpu
05ad79
+ */
05ad79
+#include <errno.h>
05ad79
+#include <stdlib.h>
05ad79
+#include <sys/types.h>
05ad79
+#include <sys/stat.h>
05ad79
+#include <fcntl.h>
05ad79
+#include <unistd.h>
05ad79
+#include <string.h>
05ad79
+#include <stdio.h>
05ad79
+
05ad79
+#include "c.h"
05ad79
+#include "pathnames.h"
05ad79
+#include "all-io.h"
05ad79
+#include "lscpu.h"
05ad79
+
05ad79
+#define WORD(x) (uint16_t)(*(const uint16_t *)(x))
05ad79
+#define DWORD(x) (uint32_t)(*(const uint32_t *)(x))
05ad79
+
05ad79
+struct dmi_header
05ad79
+{
05ad79
+	uint8_t type;
05ad79
+	uint8_t length;
05ad79
+	uint16_t handle;
05ad79
+	uint8_t *data;
05ad79
+};
05ad79
+
05ad79
+static int checksum(const uint8_t *buf, size_t len)
05ad79
+{
05ad79
+	uint8_t sum = 0;
05ad79
+	size_t a;
05ad79
+
05ad79
+	for (a = 0; a < len; a++)
05ad79
+		sum += buf[a];
05ad79
+	return (sum == 0);
05ad79
+}
05ad79
+
05ad79
+static void *get_mem_chunk(size_t base, size_t len, const char *devmem)
05ad79
+{
05ad79
+	void *p = NULL;
05ad79
+	int fd;
05ad79
+
05ad79
+	if ((fd = open(devmem, O_RDONLY)) < 0)
05ad79
+		return NULL;
05ad79
+
05ad79
+	if (!(p = malloc(len)))
05ad79
+		goto nothing;
05ad79
+	if (lseek(fd, base, SEEK_SET) == -1)
05ad79
+		goto nothing;
05ad79
+	if (read_all(fd, p, len) == -1)
05ad79
+		goto nothing;
05ad79
+
05ad79
+	close(fd);
05ad79
+	return p;
05ad79
+
05ad79
+nothing:
05ad79
+	free(p);
05ad79
+	close(fd);
05ad79
+	return NULL;
05ad79
+}
05ad79
+
05ad79
+static void to_dmi_header(struct dmi_header *h, uint8_t *data)
05ad79
+{
05ad79
+	h->type = data[0];
05ad79
+	h->length = data[1];
05ad79
+	h->handle = WORD(data + 2);
05ad79
+	h->data = data;
05ad79
+}
05ad79
+
05ad79
+static char *dmi_string(const struct dmi_header *dm, uint8_t s)
05ad79
+{
05ad79
+	char *bp = (char *)dm->data;
05ad79
+
05ad79
+	if (s == 0)
05ad79
+		return NULL;
05ad79
+
05ad79
+	bp += dm->length;
05ad79
+	while (s > 1 && *bp)
05ad79
+	{
05ad79
+		bp += strlen(bp);
05ad79
+		bp++;
05ad79
+		s--;
05ad79
+	}
05ad79
+
05ad79
+	if (!*bp)
05ad79
+		return NULL;
05ad79
+
05ad79
+	return bp;
05ad79
+}
05ad79
+
05ad79
+static int hypervisor_from_dmi_table(uint32_t base, uint16_t len,
05ad79
+				uint16_t num, const char *devmem)
05ad79
+{
05ad79
+	uint8_t *buf;
05ad79
+	uint8_t *data;
05ad79
+	int i = 0;
05ad79
+	char *vendor = NULL;
05ad79
+	char *product = NULL;
05ad79
+	char *manufacturer = NULL;
05ad79
+	int rc = HYPER_NONE;
05ad79
+
05ad79
+	data = buf = get_mem_chunk(base, len, devmem);
05ad79
+	if (!buf)
05ad79
+		goto done;
05ad79
+
05ad79
+	 /* 4 is the length of an SMBIOS structure header */
05ad79
+	while (i < num && data + 4 <= buf + len) {
05ad79
+		uint8_t *next;
05ad79
+		struct dmi_header h;
05ad79
+
05ad79
+		to_dmi_header(&h, data);
05ad79
+
05ad79
+		/*
05ad79
+		 * If a short entry is found (less than 4 bytes), not only it
05ad79
+		 * is invalid, but we cannot reliably locate the next entry.
05ad79
+		 * Better stop at this point.
05ad79
+		 */
05ad79
+		if (h.length < 4)
05ad79
+			goto done;
05ad79
+
05ad79
+		/* look for the next handle */
05ad79
+		next = data + h.length;
05ad79
+		while (next - buf + 1 < len && (next[0] != 0 || next[1] != 0))
05ad79
+			next++;
05ad79
+		next += 2;
05ad79
+		switch (h.type) {
05ad79
+			case 0:
05ad79
+				vendor = dmi_string(&h, data[0x04]);
05ad79
+				break;
05ad79
+			case 1:
05ad79
+				manufacturer = dmi_string(&h, data[0x04]);
05ad79
+				product = dmi_string(&h, data[0x05]);
05ad79
+				break;
05ad79
+			default:
05ad79
+				break;
05ad79
+		}
05ad79
+
05ad79
+		data = next;
05ad79
+		i++;
05ad79
+	}
05ad79
+	if (manufacturer && !strcmp(manufacturer, "innotek GmbH"))
05ad79
+		rc = HYPER_INNOTEK;
05ad79
+	else if (manufacturer && strstr(manufacturer, "HITACHI") &&
05ad79
+					product && strstr(product, "LPAR"))
05ad79
+		rc = HYPER_HITACHI;
05ad79
+	else if (vendor && !strcmp(vendor, "Parallels"))
05ad79
+		rc = HYPER_PARALLELS;
05ad79
+done:
05ad79
+	free(buf);
05ad79
+	return rc;
05ad79
+}
05ad79
+
05ad79
+#if defined(__x86_64__) || defined(__i386__)
05ad79
+static int hypervisor_decode_legacy(uint8_t *buf, const char *devmem)
05ad79
+{
05ad79
+	if (!checksum(buf, 0x0F))
05ad79
+		return HYPER_NONE;
05ad79
+
05ad79
+	return hypervisor_from_dmi_table(DWORD(buf + 0x08), WORD(buf + 0x06),
05ad79
+			 WORD(buf + 0x0C),
05ad79
+		devmem);
05ad79
+}
05ad79
+#endif
05ad79
+
05ad79
+static int hypervisor_decode_smbios(uint8_t *buf, const char *devmem)
05ad79
+{
05ad79
+	if (!checksum(buf, buf[0x05])
05ad79
+	    || memcmp(buf + 0x10, "_DMI_", 5) != 0
05ad79
+	    || !checksum(buf + 0x10, 0x0F))
05ad79
+		return -1;
05ad79
+
05ad79
+	return hypervisor_from_dmi_table(DWORD(buf + 0x18), WORD(buf + 0x16),
05ad79
+			 WORD(buf + 0x1C),
05ad79
+		devmem);
05ad79
+}
05ad79
+
05ad79
+/*
05ad79
+ * Probe for EFI interface
05ad79
+ */
05ad79
+#define EFI_NOT_FOUND   (-1)
05ad79
+#define EFI_NO_SMBIOS   (-2)
05ad79
+static int address_from_efi(size_t *address)
05ad79
+{
05ad79
+	FILE *tab;
05ad79
+	char linebuf[64];
05ad79
+	int ret;
05ad79
+
05ad79
+	*address = 0; /* Prevent compiler warning */
05ad79
+
05ad79
+	/*
05ad79
+	 * Linux up to 2.6.6: /proc/efi/systab
05ad79
+	 * Linux 2.6.7 and up: /sys/firmware/efi/systab
05ad79
+	 */
05ad79
+	if (!(tab = fopen("/sys/firmware/efi/systab", "r")) &&
05ad79
+	    !(tab = fopen("/proc/efi/systab", "r")))
05ad79
+		return EFI_NOT_FOUND;		/* No EFI interface */
05ad79
+
05ad79
+	ret = EFI_NO_SMBIOS;
05ad79
+	while ((fgets(linebuf, sizeof(linebuf) - 1, tab)) != NULL) {
05ad79
+		char *addrp = strchr(linebuf, '=');
05ad79
+		if (!addrp)
05ad79
+			continue;
05ad79
+		*(addrp++) = '\0';
05ad79
+		if (strcmp(linebuf, "SMBIOS") == 0) {
05ad79
+			*address = strtoul(addrp, NULL, 0);
05ad79
+			ret = 0;
05ad79
+			break;
05ad79
+		}
05ad79
+	}
05ad79
+
05ad79
+	fclose(tab);
05ad79
+	return ret;
05ad79
+}
05ad79
+
05ad79
+int read_hypervisor_dmi(void)
05ad79
+{
05ad79
+	int rc = HYPER_NONE;
05ad79
+	uint8_t *buf = NULL;
05ad79
+	size_t fp = 0;
05ad79
+
05ad79
+	if (sizeof(uint8_t) != 1
05ad79
+	    || sizeof(uint16_t) != 2
05ad79
+	    || sizeof(uint32_t) != 4
05ad79
+	    || '\0' != 0)
05ad79
+		return rc;
05ad79
+
05ad79
+	/* First try EFI (ia64, Intel-based Mac) */
05ad79
+	switch (address_from_efi(&fp)) {
05ad79
+		case EFI_NOT_FOUND:
05ad79
+			goto memory_scan;
05ad79
+		case EFI_NO_SMBIOS:
05ad79
+			goto done;
05ad79
+	}
05ad79
+
05ad79
+	buf = get_mem_chunk(fp, 0x20, _PATH_DEV_MEM);
05ad79
+	if (!buf)
05ad79
+		goto done;
05ad79
+
05ad79
+	rc = hypervisor_decode_smbios(buf, _PATH_DEV_MEM);
05ad79
+	if (rc)
05ad79
+		goto done;
05ad79
+	free(buf);
05ad79
+	buf = NULL;
05ad79
+memory_scan:
05ad79
+#if defined(__x86_64__) || defined(__i386__)
05ad79
+	/* Fallback to memory scan (x86, x86_64) */
05ad79
+	buf = get_mem_chunk(0xF0000, 0x10000, _PATH_DEV_MEM);
05ad79
+	if (!buf)
05ad79
+		goto done;
05ad79
+
05ad79
+	for (fp = 0; fp <= 0xFFF0; fp += 16) {
05ad79
+		if (memcmp(buf + fp, "_SM_", 4) == 0 && fp <= 0xFFE0) {
05ad79
+			rc = hypervisor_decode_smbios(buf + fp, _PATH_DEV_MEM);
05ad79
+			if (rc == -1)
05ad79
+				fp += 16;
05ad79
+
05ad79
+		} else if (memcmp(buf + fp, "_DMI_", 5) == 0)
05ad79
+			rc = hypervisor_decode_legacy(buf + fp, _PATH_DEV_MEM);
05ad79
+
05ad79
+		if (rc >= 0)
05ad79
+			break;
05ad79
+	}
05ad79
+#endif
05ad79
+done:
05ad79
+	free(buf);
05ad79
+	return rc;
05ad79
+}
05ad79
diff --git a/sys-utils/lscpu.1 b/sys-utils/lscpu.1
05ad79
index f747a35..8636e52 100644
05ad79
--- a/sys-utils/lscpu.1
05ad79
+++ b/sys-utils/lscpu.1
05ad79
@@ -1,34 +1,42 @@
05ad79
-.\" Process this file with
05ad79
-.\" groff -man -Tascii lscpu.1
05ad79
-.\"
05ad79
-.TH LSCPU 1 "January 2013" "util-linux" "User Commands"
05ad79
+.TH LSCPU 1 "November 2015" "util-linux" "User Commands"
05ad79
 .SH NAME
05ad79
 lscpu \- display information about the CPU architecture
05ad79
 .SH SYNOPSIS
05ad79
 .B lscpu
05ad79
-.RB [ \-a | \-b | \-c "] [" \-x "] [" \-s " \fIdirectory\fP] [" \-e [=\fIlist\fP]| \-p [=\fIlist\fP]]
05ad79
+.RB [ \-a | \-b | \-c | \-J "] [" \-x "] [" \-y "] [" \-s " \fIdirectory\fP] [" \-e [=\fIlist\fP]| \-p [=\fIlist\fP]]
05ad79
 .br
05ad79
 .B lscpu
05ad79
 .BR \-h | \-V
05ad79
 .SH DESCRIPTION
05ad79
 .B lscpu
05ad79
-gathers CPU architecture information from sysfs and /proc/cpuinfo.  The
05ad79
+gathers CPU architecture information from sysfs, /proc/cpuinfo and any
05ad79
+applicable architecture-specific libraries (e.g.\& librtas on Powerpc).  The
05ad79
 command output can be optimized for parsing or for easy readability by humans.
05ad79
 The information includes, for example, the number of CPUs, threads, cores,
05ad79
 sockets, and Non-Uniform Memory Access (NUMA) nodes.  There is also information
05ad79
 about the CPU caches and cache sharing, family, model, bogoMIPS, byte order,
05ad79
 and stepping.
05ad79
-
05ad79
+.sp
05ad79
+In virtualized environments, the CPU architecture information displayed
05ad79
+reflects the configuration of the guest operating system which is
05ad79
+typically different from the physical (host) system.  On architectures that
05ad79
+support retrieving physical topology information,
05ad79
+.B lscpu
05ad79
+also displays the number of physical sockets, chips, cores in the host system.
05ad79
+.sp
05ad79
 Options that result in an output table have a \fIlist\fP argument.  Use this
05ad79
 argument to customize the command output.  Specify a comma-separated list of
05ad79
 column labels to limit the output table to only the specified columns, arranged
05ad79
 in the specified order.  See \fBCOLUMNS\fP for a list of valid column labels.  The
05ad79
 column labels are not case sensitive.
05ad79
-
05ad79
+.sp
05ad79
 Not all columns are supported on all architectures.  If an unsupported column is
05ad79
 specified, \fBlscpu\fP prints the column but does not provide any data for it.
05ad79
 
05ad79
 .SS COLUMNS
05ad79
+Note that topology elements (core, socket, etc.) use a sequential unique ID
05ad79
+starting from zero, but CPU logical numbers follow the kernel where there is
05ad79
+no guarantee of sequential numbering.
05ad79
 .TP
05ad79
 .B CPU
05ad79
 The logical CPU number of a CPU as used by the Linux kernel.
05ad79
@@ -42,8 +50,11 @@ The logical socket number.  A socket can contain several cores.
05ad79
 .B BOOK
05ad79
 The logical book number.  A book can contain several sockets.
05ad79
 .TP
05ad79
+.B DRAWER
05ad79
+The logical drawer number.  A drawer can contain several books.
05ad79
+.TP
05ad79
 .B NODE
05ad79
-The logical NUMA node number.  A node may contain several books.
05ad79
+The logical NUMA node number.  A node can contain several drawers.
05ad79
 .TP
05ad79
 .B CACHE
05ad79
 Information about how caches are shared between CPUs.
05ad79
@@ -77,6 +88,14 @@ For vertical polarization, the column also shows the degree of concentration,
05ad79
 high, medium, or low.  This column contains data only if your hardware system
05ad79
 and hypervisor support CPU polarization.
05ad79
 .RE
05ad79
+.TP
05ad79
+.B MAXMHZ
05ad79
+Maximum megahertz value for the CPU. Useful when \fBlscpu\fP is used as hardware
05ad79
+inventory information gathering tool.  Notice that the megahertz value is
05ad79
+dynamic, and driven by CPU governor depending on current resource need.
05ad79
+.TP
05ad79
+.B MINMHZ
05ad79
+Minimum megahertz value for the CPU.
05ad79
 .SH OPTIONS
05ad79
 .TP
05ad79
 .BR \-a , " \-\-all"
05ad79
@@ -92,7 +111,7 @@ Limit the output to offline CPUs.
05ad79
 This option may only be specified together with option \fB-e\fR or \fB-p\fR.
05ad79
 .TP
05ad79
 .BR \-e , " \-\-extended" [=\fIlist\fP]
05ad79
-Display the CPU information in human readable format.
05ad79
+Display the CPU information in human-readable format.
05ad79
 
05ad79
 If the \fIlist\fP argument is omitted, all columns for which data is available
05ad79
 are included in the command output.
05ad79
@@ -102,7 +121,7 @@ When specifying the \fIlist\fP argument, the string of option, equal sign (=), a
05ad79
 Examples: '\fB-e=cpu,node\fP' or '\fB--extended=cpu,node\fP'.
05ad79
 .TP
05ad79
 .BR \-h , " \-\-help"
05ad79
-Display help information and exit.
05ad79
+Display help text and exit.
05ad79
 .TP
05ad79
 .BR \-p , " \-\-parse" [=\fIlist\fP]
05ad79
 Optimize the command output for easy parsing.
05ad79
@@ -126,6 +145,16 @@ of the Linux instance to be inspected.
05ad79
 Use hexadecimal masks for CPU sets (for example 0x3).  The default is to print
05ad79
 the sets in list format (for example 0,1).
05ad79
 .TP
05ad79
+.BR \-y , " \-\-physical"
05ad79
+Display physical IDs for all columns with topology elements (core, socket, etc.).
05ad79
+Other than logical IDs, which are assigned by \fBlscpu\fP, physical IDs are
05ad79
+platform-specific values that are provided by the kernel. Physical IDs are not
05ad79
+necessarily unique and they might not be arranged sequentially.
05ad79
+If the kernel could not retrieve a physical ID for an element \fBlscpu\fP prints
05ad79
+the dash (-) character.
05ad79
+
05ad79
+The CPU logical numbers are not affected by this option.
05ad79
+.TP
05ad79
 .BR \-V , " \-\-version"
05ad79
 Display version information and exit.
05ad79
 .SH BUGS
05ad79
@@ -145,4 +174,4 @@ Heiko Carstens <heiko.carstens@de.ibm.com>
05ad79
 .BR chcpu (8)
05ad79
 .SH AVAILABILITY
05ad79
 The lscpu command is part of the util-linux package and is available from
05ad79
-ftp://ftp.kernel.org/pub/linux/utils/util-linux/.
05ad79
+https://www.kernel.org/pub/linux/utils/util-linux/.
05ad79
diff --git a/sys-utils/lscpu.c b/sys-utils/lscpu.c
05ad79
index 7a00636..683fd66 100644
05ad79
--- a/sys-utils/lscpu.c
05ad79
+++ b/sys-utils/lscpu.c
05ad79
@@ -19,6 +19,7 @@
05ad79
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
05ad79
  */
05ad79
 
05ad79
+#include <assert.h>
05ad79
 #include <ctype.h>
05ad79
 #include <dirent.h>
05ad79
 #include <errno.h>
05ad79
@@ -33,21 +34,46 @@
05ad79
 #include <sys/types.h>
05ad79
 #include <sys/stat.h>
05ad79
 
05ad79
+#if (defined(__x86_64__) || defined(__i386__))
05ad79
+# if !defined( __SANITIZE_ADDRESS__)
05ad79
+#  define INCLUDE_VMWARE_BDOOR
05ad79
+# else
05ad79
+#  warning VMWARE detection disabled by __SANITIZE_ADDRESS__
05ad79
+# endif
05ad79
+#endif
05ad79
+
05ad79
+#ifdef INCLUDE_VMWARE_BDOOR
05ad79
+# include <stdint.h>
05ad79
+# include <signal.h>
05ad79
+# include <strings.h>
05ad79
+# include <setjmp.h>
05ad79
+# ifdef HAVE_SYS_IO_H
05ad79
+#  include <sys/io.h>
05ad79
+# endif
05ad79
+#endif
05ad79
+
05ad79
+#if defined(HAVE_LIBRTAS)
05ad79
+#include <librtas.h>
05ad79
+#endif
05ad79
+
05ad79
+#include <libsmartcols.h>
05ad79
+
05ad79
 #include "cpuset.h"
05ad79
 #include "nls.h"
05ad79
 #include "xalloc.h"
05ad79
 #include "c.h"
05ad79
 #include "strutils.h"
05ad79
 #include "bitops.h"
05ad79
-#include "tt.h"
05ad79
 #include "path.h"
05ad79
 #include "closestream.h"
05ad79
 #include "optutils.h"
05ad79
+#include "lscpu.h"
05ad79
 
05ad79
 #define CACHE_MAX 100
05ad79
 
05ad79
 /* /sys paths */
05ad79
 #define _PATH_SYS_SYSTEM	"/sys/devices/system"
05ad79
+#define _PATH_SYS_HYP_FEATURES "/sys/hypervisor/properties/features"
05ad79
 #define _PATH_SYS_CPU		_PATH_SYS_SYSTEM "/cpu"
05ad79
 #define _PATH_SYS_NODE		_PATH_SYS_SYSTEM "/node"
05ad79
 #define _PATH_PROC_XEN		"/proc/xen"
05ad79
@@ -55,35 +81,71 @@
05ad79
 #define _PATH_PROC_CPUINFO	"/proc/cpuinfo"
05ad79
 #define _PATH_PROC_PCIDEVS	"/proc/bus/pci/devices"
05ad79
 #define _PATH_PROC_SYSINFO	"/proc/sysinfo"
05ad79
+#define _PATH_PROC_STATUS	"/proc/self/status"
05ad79
+#define _PATH_PROC_VZ	"/proc/vz"
05ad79
+#define _PATH_PROC_BC	"/proc/bc"
05ad79
+#define _PATH_PROC_DEVICETREE	"/proc/device-tree"
05ad79
+#define _PATH_DEV_MEM 		"/dev/mem"
05ad79
+#define _PATH_PROC_OSRELEASE	"/proc/sys/kernel/osrelease"
05ad79
+
05ad79
+/* Xen Domain feature flag used for /sys/hypervisor/properties/features */
05ad79
+#define XENFEAT_supervisor_mode_kernel		3
05ad79
+#define XENFEAT_mmu_pt_update_preserve_ad	5
05ad79
+#define XENFEAT_hvm_callback_vector			8
05ad79
+
05ad79
+#define XEN_FEATURES_PV_MASK	(1U << XENFEAT_mmu_pt_update_preserve_ad)
05ad79
+#define XEN_FEATURES_PVH_MASK	( (1U << XENFEAT_supervisor_mode_kernel) \
05ad79
+								| (1U << XENFEAT_hvm_callback_vector) )
05ad79
 
05ad79
 /* virtualization types */
05ad79
 enum {
05ad79
 	VIRT_NONE	= 0,
05ad79
 	VIRT_PARA,
05ad79
-	VIRT_FULL
05ad79
+	VIRT_FULL,
05ad79
+	VIRT_CONT
05ad79
 };
05ad79
-const char *virt_types[] = {
05ad79
+static const char *virt_types[] = {
05ad79
 	[VIRT_NONE]	= N_("none"),
05ad79
 	[VIRT_PARA]	= N_("para"),
05ad79
-	[VIRT_FULL]	= N_("full")
05ad79
+	[VIRT_FULL]	= N_("full"),
05ad79
+	[VIRT_CONT]	= N_("container"),
05ad79
 };
05ad79
 
05ad79
-/* hypervisor vendors */
05ad79
-enum {
05ad79
-	HYPER_NONE	= 0,
05ad79
-	HYPER_XEN,
05ad79
-	HYPER_KVM,
05ad79
-	HYPER_MSHV,
05ad79
-	HYPER_VMWARE,
05ad79
-	HYPER_IBM
05ad79
-};
05ad79
-const char *hv_vendors[] = {
05ad79
+static const char *hv_vendors[] = {
05ad79
 	[HYPER_NONE]	= NULL,
05ad79
 	[HYPER_XEN]	= "Xen",
05ad79
 	[HYPER_KVM]	= "KVM",
05ad79
 	[HYPER_MSHV]	= "Microsoft",
05ad79
 	[HYPER_VMWARE]  = "VMware",
05ad79
-	[HYPER_IBM]	= "IBM"
05ad79
+	[HYPER_IBM]	= "IBM",
05ad79
+	[HYPER_VSERVER]	= "Linux-VServer",
05ad79
+	[HYPER_UML]	= "User-mode Linux",
05ad79
+	[HYPER_INNOTEK]	= "Innotek GmbH",
05ad79
+	[HYPER_HITACHI]	= "Hitachi",
05ad79
+	[HYPER_PARALLELS] = "Parallels",
05ad79
+	[HYPER_VBOX]	= "Oracle",
05ad79
+	[HYPER_OS400]	= "OS/400",
05ad79
+	[HYPER_PHYP]	= "pHyp",
05ad79
+	[HYPER_SPAR]	= "Unisys s-Par",
05ad79
+	[HYPER_WSL]	= "Windows Subsystem for Linux"
05ad79
+};
05ad79
+
05ad79
+static const int hv_vendor_pci[] = {
05ad79
+	[HYPER_NONE]	= 0x0000,
05ad79
+	[HYPER_XEN]	= 0x5853,
05ad79
+	[HYPER_KVM]	= 0x0000,
05ad79
+	[HYPER_MSHV]	= 0x1414,
05ad79
+	[HYPER_VMWARE]	= 0x15ad,
05ad79
+	[HYPER_VBOX]	= 0x80ee,
05ad79
+};
05ad79
+
05ad79
+static const int hv_graphics_pci[] = {
05ad79
+	[HYPER_NONE]	= 0x0000,
05ad79
+	[HYPER_XEN]	= 0x0001,
05ad79
+	[HYPER_KVM]	= 0x0000,
05ad79
+	[HYPER_MSHV]	= 0x5353,
05ad79
+	[HYPER_VMWARE]	= 0x0710,
05ad79
+	[HYPER_VBOX]	= 0xbeef,
05ad79
 };
05ad79
 
05ad79
 /* CPU modes */
05ad79
@@ -107,7 +169,7 @@ enum {
05ad79
 	DISP_VERTICAL	= 1
05ad79
 };
05ad79
 
05ad79
-const char *disp_modes[] = {
05ad79
+static const char *disp_modes[] = {
05ad79
 	[DISP_HORIZONTAL]	= N_("horizontal"),
05ad79
 	[DISP_VERTICAL]		= N_("vertical")
05ad79
 };
05ad79
@@ -126,7 +188,7 @@ struct polarization_modes {
05ad79
 	char *readable;
05ad79
 };
05ad79
 
05ad79
-struct polarization_modes polar_modes[] = {
05ad79
+static struct polarization_modes polar_modes[] = {
05ad79
 	[POLAR_UNKNOWN]	   = {"U",  "-"},
05ad79
 	[POLAR_VLOW]	   = {"VL", "vert-low"},
05ad79
 	[POLAR_VMEDIUM]	   = {"VM", "vert-medium"},
05ad79
@@ -138,6 +200,7 @@ struct polarization_modes polar_modes[] = {
05ad79
 struct lscpu_desc {
05ad79
 	char	*arch;
05ad79
 	char	*vendor;
05ad79
+	char	*machinetype;	/* s390 */
05ad79
 	char	*family;
05ad79
 	char	*model;
05ad79
 	char	*modelname;
05ad79
@@ -148,9 +211,14 @@ struct lscpu_desc {
05ad79
 	int	hyper;		/* hypervisor vendor ID */
05ad79
 	int	virtype;	/* VIRT_PARA|FULL|NONE ? */
05ad79
 	char	*mhz;
05ad79
+	char	*dynamic_mhz;	/* dynamic mega hertz (s390) */
05ad79
+	char	*static_mhz;	/* static mega hertz (s390) */
05ad79
+	char	**maxmhz;	/* maximum mega hertz */
05ad79
+	char	**minmhz;	/* minimum mega hertz */
05ad79
 	char	*stepping;
05ad79
 	char    *bogomips;
05ad79
 	char	*flags;
05ad79
+	char	*mtid;		/* maximum thread id (s390) */
05ad79
 	int	dispatching;	/* none, horizontal or vertical */
05ad79
 	int	mode;		/* rm, lm or/and tm */
05ad79
 
05ad79
@@ -159,33 +227,58 @@ struct lscpu_desc {
05ad79
 	cpu_set_t	*present;	/* mask with present CPUs */
05ad79
 	cpu_set_t	*online;	/* mask with online CPUs */
05ad79
 
05ad79
+	int		nthreads;	/* number of online threads */
05ad79
+
05ad79
+	int		ncaches;
05ad79
+	struct cpu_cache *caches;
05ad79
+
05ad79
+	int		necaches;	/* extra caches (s390) */
05ad79
+	struct cpu_cache *ecaches;
05ad79
+
05ad79
+	/*
05ad79
+	 * All maps are sequentially indexed (0..ncpuspos), the array index
05ad79
+	 * does not have match with cpuX number as presented by kernel. You
05ad79
+	 * have to use real_cpu_num() to get the real cpuX number.
05ad79
+	 *
05ad79
+	 * For example, the possible system CPUs are: 1,3,5, it means that
05ad79
+	 * ncpuspos=3, so all arrays are in range 0..3.
05ad79
+	 */
05ad79
+	int		*idx2cpunum;	/* mapping index to CPU num */
05ad79
+
05ad79
 	int		nnodes;		/* number of NUMA modes */
05ad79
 	int		*idx2nodenum;	/* Support for discontinuous nodes */
05ad79
 	cpu_set_t	**nodemaps;	/* array with NUMA nodes */
05ad79
 
05ad79
+	/* drawers -- based on drawer_siblings (internal kernel map of cpuX's
05ad79
+	 * hardware threads within the same drawer */
05ad79
+	int		ndrawers;	/* number of all online drawers */
05ad79
+	cpu_set_t	**drawermaps;	/* unique drawer_siblings */
05ad79
+	int		*drawerids;	/* physical drawer ids */
05ad79
+
05ad79
 	/* books -- based on book_siblings (internal kernel map of cpuX's
05ad79
 	 * hardware threads within the same book */
05ad79
 	int		nbooks;		/* number of all online books */
05ad79
 	cpu_set_t	**bookmaps;	/* unique book_siblings */
05ad79
+	int		*bookids;	/* physical book ids */
05ad79
 
05ad79
 	/* sockets -- based on core_siblings (internal kernel map of cpuX's
05ad79
 	 * hardware threads within the same physical_package_id (socket)) */
05ad79
 	int		nsockets;	/* number of all online sockets */
05ad79
 	cpu_set_t	**socketmaps;	/* unique core_siblings */
05ad79
+	int		*socketids;	/* physical socket ids */
05ad79
 
05ad79
-	/* cores -- based on thread_siblings (internel kernel map of cpuX's
05ad79
+	/* cores -- based on thread_siblings (internal kernel map of cpuX's
05ad79
 	 * hardware threads within the same core as cpuX) */
05ad79
 	int		ncores;		/* number of all online cores */
05ad79
 	cpu_set_t	**coremaps;	/* unique thread_siblings */
05ad79
-
05ad79
-	int		nthreads;	/* number of online threads */
05ad79
-
05ad79
-	int		ncaches;
05ad79
-	struct cpu_cache *caches;
05ad79
+	int		*coreids;	/* physical core ids */
05ad79
 
05ad79
 	int		*polarization;	/* cpu polarization */
05ad79
 	int		*addresses;	/* physical cpu addresses */
05ad79
 	int		*configured;	/* cpu configured */
05ad79
+	int		physsockets;	/* Physical sockets (modules) */
05ad79
+	int		physchips;	/* Physical chips */
05ad79
+	int		physcoresperchip;	/* Physical cores per chip */
05ad79
 };
05ad79
 
05ad79
 enum {
05ad79
@@ -205,7 +298,8 @@ struct lscpu_modifier {
05ad79
 	unsigned int	hex:1,		/* print CPU masks rather than CPU lists */
05ad79
 			compat:1,	/* use backwardly compatible format */
05ad79
 			online:1,	/* print online CPUs */
05ad79
-			offline:1;	/* print offline CPUs */
05ad79
+			offline:1,	/* print offline CPUs */
05ad79
+			physical:1;	/* use physical numbers */
05ad79
 };
05ad79
 
05ad79
 static int maxcpus;		/* size in bits of kernel cpu mask */
05ad79
@@ -217,6 +311,8 @@ static int maxcpus;		/* size in bits of kernel cpu mask */
05ad79
 	((_d) && (_d)->present ? \
05ad79
 		CPU_ISSET_S((_cpu), CPU_ALLOC_SIZE(maxcpus), (_d)->present) : 0)
05ad79
 
05ad79
+#define real_cpu_num(_d, _i)	((_d)->idx2cpunum[(_i)])
05ad79
+
05ad79
 /*
05ad79
  * IDs
05ad79
  */
05ad79
@@ -226,11 +322,14 @@ enum {
05ad79
 	COL_SOCKET,
05ad79
 	COL_NODE,
05ad79
 	COL_BOOK,
05ad79
+	COL_DRAWER,
05ad79
 	COL_CACHE,
05ad79
 	COL_POLARIZATION,
05ad79
 	COL_ADDRESS,
05ad79
 	COL_CONFIGURED,
05ad79
 	COL_ONLINE,
05ad79
+	COL_MAXMHZ,
05ad79
+	COL_MINMHZ,
05ad79
 };
05ad79
 
05ad79
 /* column description
05ad79
@@ -249,11 +348,14 @@ static struct lscpu_coldesc coldescs[] =
05ad79
 	[COL_SOCKET]       = { "SOCKET", N_("logical socket number") },
05ad79
 	[COL_NODE]         = { "NODE", N_("logical NUMA node number") },
05ad79
 	[COL_BOOK]         = { "BOOK", N_("logical book number") },
05ad79
+	[COL_DRAWER]       = { "DRAWER", N_("logical drawer number") },
05ad79
 	[COL_CACHE]        = { "CACHE", N_("shows how caches are shared between CPUs") },
05ad79
 	[COL_POLARIZATION] = { "POLARIZATION", N_("CPU dispatching mode on virtual hardware") },
05ad79
 	[COL_ADDRESS]      = { "ADDRESS", N_("physical address of a CPU") },
05ad79
 	[COL_CONFIGURED]   = { "CONFIGURED", N_("shows if the hypervisor has allocated the CPU") },
05ad79
-	[COL_ONLINE]       = { "ONLINE", N_("shows if Linux currently makes use of the CPU") }
05ad79
+	[COL_ONLINE]       = { "ONLINE", N_("shows if Linux currently makes use of the CPU") },
05ad79
+	[COL_MAXMHZ]	   = { "MAXMHZ", N_("shows the maximum MHz of the CPU") },
05ad79
+	[COL_MINMHZ]	   = { "MINMHZ", N_("shows the minimum MHz of the CPU") }
05ad79
 };
05ad79
 
05ad79
 static int
05ad79
@@ -282,7 +384,8 @@ lookup(char *line, char *pattern, char **value)
05ad79
 	char *p, *v;
05ad79
 	int len = strlen(pattern);
05ad79
 
05ad79
-	if (!*line)
05ad79
+	/* don't re-fill already found tags, first one wins */
05ad79
+	if (!*line || *value)
05ad79
 		return 0;
05ad79
 
05ad79
 	/* pattern */
05ad79
@@ -313,6 +416,63 @@ lookup(char *line, char *pattern, char **value)
05ad79
 	return 1;
05ad79
 }
05ad79
 
05ad79
+/* Parse extra cache lines contained within /proc/cpuinfo but which are not
05ad79
+ * part of the cache topology information within the sysfs filesystem.
05ad79
+ * This is true for all shared caches on e.g. s390. When there are layers of
05ad79
+ * hypervisors in between it is not knows which CPUs share which caches.
05ad79
+ * Therefore information about shared caches is only available in
05ad79
+ * /proc/cpuinfo.
05ad79
+ * Format is:
05ad79
+ * "cache<nr> : level=<lvl> type=<type> scope=<scope> size=<size> line_size=<lsz> associativity=<as>"
05ad79
+ */
05ad79
+static int
05ad79
+lookup_cache(char *line, struct lscpu_desc *desc)
05ad79
+{
05ad79
+	struct cpu_cache *cache;
05ad79
+	long long size;
05ad79
+	char *p, type;
05ad79
+	int level;
05ad79
+
05ad79
+	/* Make sure line starts with "cache<nr> :" */
05ad79
+	if (strncmp(line, "cache", 5))
05ad79
+		return 0;
05ad79
+	for (p = line + 5; isdigit(*p); p++);
05ad79
+	for (; isspace(*p); p++);
05ad79
+	if (*p != ':')
05ad79
+		return 0;
05ad79
+
05ad79
+	p = strstr(line, "scope=") + 6;
05ad79
+	/* Skip private caches, also present in sysfs */
05ad79
+	if (!p || strncmp(p, "Private", 7) == 0)
05ad79
+		return 0;
05ad79
+	p = strstr(line, "level=");
05ad79
+	if (!p || sscanf(p, "level=%d", &level) != 1)
05ad79
+		return 0;
05ad79
+	p = strstr(line, "type=") + 5;
05ad79
+	if (!p || !*p)
05ad79
+		return 0;
05ad79
+	type = 0;
05ad79
+	if (strncmp(p, "Data", 4) == 0)
05ad79
+		type = 'd';
05ad79
+	if (strncmp(p, "Instruction", 11) == 0)
05ad79
+		type = 'i';
05ad79
+	p = strstr(line, "size=");
05ad79
+	if (!p || sscanf(p, "size=%lld", &size) != 1)
05ad79
+	       return 0;
05ad79
+
05ad79
+	desc->necaches++;
05ad79
+	desc->ecaches = xrealloc(desc->ecaches,
05ad79
+				 desc->necaches * sizeof(struct cpu_cache));
05ad79
+	cache = &desc->ecaches[desc->necaches - 1];
05ad79
+	memset(cache, 0 , sizeof(*cache));
05ad79
+	if (type)
05ad79
+		xasprintf(&cache->name, "L%d%c", level, type);
05ad79
+	else
05ad79
+		xasprintf(&cache->name, "L%d", level);
05ad79
+	xasprintf(&cache->size, "%lldK", size);
05ad79
+	return 1;
05ad79
+}
05ad79
+
05ad79
 /* Don't init the mode for platforms where we are not able to
05ad79
  * detect that CPU supports 64-bit mode.
05ad79
  */
05ad79
@@ -338,6 +498,45 @@ init_mode(struct lscpu_modifier *mod)
05ad79
 	return m;
05ad79
 }
05ad79
 
05ad79
+#if defined(HAVE_LIBRTAS)
05ad79
+#define PROCESSOR_MODULE_INFO	43
05ad79
+static int strbe16toh(const char *buf, int offset)
05ad79
+{
05ad79
+	return (buf[offset] << 8) + buf[offset+1];
05ad79
+}
05ad79
+
05ad79
+static void read_physical_info_powerpc(struct lscpu_desc *desc)
05ad79
+{
05ad79
+	char buf[BUFSIZ];
05ad79
+	int rc, len, ntypes;
05ad79
+
05ad79
+	desc->physsockets = desc->physchips = desc->physcoresperchip = 0;
05ad79
+
05ad79
+	rc = rtas_get_sysparm(PROCESSOR_MODULE_INFO, sizeof(buf), buf);
05ad79
+	if (rc < 0)
05ad79
+		return;
05ad79
+
05ad79
+	len = strbe16toh(buf, 0);
05ad79
+	if (len < 8)
05ad79
+		return;
05ad79
+
05ad79
+	ntypes = strbe16toh(buf, 2);
05ad79
+
05ad79
+	assert(ntypes <= 1);
05ad79
+	if (!ntypes)
05ad79
+		return;
05ad79
+
05ad79
+	desc->physsockets = strbe16toh(buf, 4);
05ad79
+	desc->physchips = strbe16toh(buf, 6);
05ad79
+	desc->physcoresperchip = strbe16toh(buf, 8);
05ad79
+}
05ad79
+#else
05ad79
+static void read_physical_info_powerpc(
05ad79
+		struct lscpu_desc *desc __attribute__((__unused__)))
05ad79
+{
05ad79
+}
05ad79
+#endif
05ad79
+
05ad79
 static void
05ad79
 read_basicinfo(struct lscpu_desc *desc, struct lscpu_modifier *mod)
05ad79
 {
05ad79
@@ -361,13 +560,20 @@ read_basicinfo(struct lscpu_desc *desc, struct lscpu_modifier *mod)
05ad79
 		else if (lookup(buf, "model name", &desc->modelname)) ;
05ad79
 		else if (lookup(buf, "stepping", &desc->stepping)) ;
05ad79
 		else if (lookup(buf, "cpu MHz", &desc->mhz)) ;
05ad79
+		else if (lookup(buf, "cpu MHz dynamic", &desc->dynamic_mhz)) ; /* s390 */
05ad79
+		else if (lookup(buf, "cpu MHz static", &desc->static_mhz)) ;   /* s390 */
05ad79
 		else if (lookup(buf, "flags", &desc->flags)) ;		/* x86 */
05ad79
 		else if (lookup(buf, "features", &desc->flags)) ;	/* s390 */
05ad79
+		else if (lookup(buf, "Features", &desc->flags)) ;	/* aarch64 */
05ad79
 		else if (lookup(buf, "type", &desc->flags)) ;		/* sparc64 */
05ad79
 		else if (lookup(buf, "bogomips", &desc->bogomips)) ;
05ad79
+		else if (lookup(buf, "BogoMIPS", &desc->bogomips)) ;	/* aarch64 */
05ad79
 		else if (lookup(buf, "bogomips per cpu", &desc->bogomips)) ; /* s390 */
05ad79
 		else if (lookup(buf, "cpu", &desc->cpu)) ;
05ad79
 		else if (lookup(buf, "revision", &desc->revision)) ;
05ad79
+		else if (lookup(buf, "CPU revision", &desc->revision)) ; /* aarch64 */
05ad79
+		else if (lookup(buf, "max thread id", &desc->mtid)) ; /* s390 */
05ad79
+		else if (lookup_cache(buf, desc)) ;
05ad79
 		else
05ad79
 			continue;
05ad79
 	}
05ad79
@@ -397,9 +603,9 @@ read_basicinfo(struct lscpu_desc *desc, struct lscpu_modifier *mod)
05ad79
 
05ad79
 	fclose(fp);
05ad79
 
05ad79
-	if (path_exist(_PATH_SYS_SYSTEM "/cpu/kernel_max"))
05ad79
+	if (path_exist(_PATH_SYS_CPU "/kernel_max"))
05ad79
 		/* note that kernel_max is maximum index [NR_CPUS-1] */
05ad79
-		maxcpus = path_read_s32(_PATH_SYS_SYSTEM "/cpu/kernel_max") + 1;
05ad79
+		maxcpus = path_read_s32(_PATH_SYS_CPU "/kernel_max") + 1;
05ad79
 
05ad79
 	else if (mod->system == SYSTEM_LIVE)
05ad79
 		/* the root is '/' so we are working with data from the current kernel */
05ad79
@@ -412,32 +618,49 @@ read_basicinfo(struct lscpu_desc *desc, struct lscpu_modifier *mod)
05ad79
 
05ad79
 	setsize = CPU_ALLOC_SIZE(maxcpus);
05ad79
 
05ad79
-	if (path_exist(_PATH_SYS_SYSTEM "/cpu/possible")) {
05ad79
-		cpu_set_t *tmp = path_read_cpulist(maxcpus, _PATH_SYS_SYSTEM "/cpu/possible");
05ad79
+	if (path_exist(_PATH_SYS_CPU "/possible")) {
05ad79
+		cpu_set_t *tmp = path_read_cpulist(maxcpus, _PATH_SYS_CPU "/possible");
05ad79
+		int num, idx;
05ad79
+
05ad79
 		desc->ncpuspos = CPU_COUNT_S(setsize, tmp);
05ad79
+		desc->idx2cpunum = xcalloc(desc->ncpuspos, sizeof(int));
05ad79
+
05ad79
+		for (num = 0, idx = 0; num < maxcpus; num++) {
05ad79
+			if (CPU_ISSET(num, tmp))
05ad79
+				desc->idx2cpunum[idx++] = num;
05ad79
+		}
05ad79
 		cpuset_free(tmp);
05ad79
 	} else
05ad79
 		err(EXIT_FAILURE, _("failed to determine number of CPUs: %s"),
05ad79
-				_PATH_SYS_SYSTEM "/cpu/possible");
05ad79
+				_PATH_SYS_CPU "/possible");
05ad79
 
05ad79
 
05ad79
 	/* get mask for present CPUs */
05ad79
-	if (path_exist(_PATH_SYS_SYSTEM "/cpu/present")) {
05ad79
-		desc->present = path_read_cpulist(maxcpus, _PATH_SYS_SYSTEM "/cpu/present");
05ad79
+	if (path_exist(_PATH_SYS_CPU "/present")) {
05ad79
+		desc->present = path_read_cpulist(maxcpus, _PATH_SYS_CPU "/present");
05ad79
 		desc->ncpus = CPU_COUNT_S(setsize, desc->present);
05ad79
 	}
05ad79
 
05ad79
 	/* get mask for online CPUs */
05ad79
-	if (path_exist(_PATH_SYS_SYSTEM "/cpu/online")) {
05ad79
-		desc->online = path_read_cpulist(maxcpus, _PATH_SYS_SYSTEM "/cpu/online");
05ad79
+	if (path_exist(_PATH_SYS_CPU "/online")) {
05ad79
+		desc->online = path_read_cpulist(maxcpus, _PATH_SYS_CPU "/online");
05ad79
 		desc->nthreads = CPU_COUNT_S(setsize, desc->online);
05ad79
 	}
05ad79
 
05ad79
 	/* get dispatching mode */
05ad79
-	if (path_exist(_PATH_SYS_SYSTEM "/cpu/dispatching"))
05ad79
-		desc->dispatching = path_read_s32(_PATH_SYS_SYSTEM "/cpu/dispatching");
05ad79
+	if (path_exist(_PATH_SYS_CPU "/dispatching"))
05ad79
+		desc->dispatching = path_read_s32(_PATH_SYS_CPU "/dispatching");
05ad79
 	else
05ad79
 		desc->dispatching = -1;
05ad79
+
05ad79
+	if (mod->system == SYSTEM_LIVE)
05ad79
+		read_physical_info_powerpc(desc);
05ad79
+
05ad79
+	if ((fp = path_fopen("r", 0, _PATH_PROC_SYSINFO))) {
05ad79
+		while (fgets(buf, sizeof(buf), fp) != NULL && !desc->machinetype)
05ad79
+			lookup(buf, "Type", &desc->machinetype);
05ad79
+		fclose(fp);
05ad79
+	}
05ad79
 }
05ad79
 
05ad79
 static int
05ad79
@@ -483,10 +706,9 @@ cpuid(unsigned int op, unsigned int *eax, unsigned int *ebx,
05ad79
 	__asm__(
05ad79
 #if defined(__PIC__) && defined(__i386__)
05ad79
 		/* x86 PIC cannot clobber ebx -- gcc bitches */
05ad79
-		"pushl %%ebx;"
05ad79
+		"xchg %%ebx, %%esi;"
05ad79
 		"cpuid;"
05ad79
-		"movl %%ebx, %%esi;"
05ad79
-		"popl %%ebx;"
05ad79
+		"xchg %%esi, %%ebx;"
05ad79
 		: "=S" (*ebx),
05ad79
 #else
05ad79
 		"cpuid;"
05ad79
@@ -523,34 +745,230 @@ read_hypervisor_cpuid(struct lscpu_desc *desc)
05ad79
 		desc->hyper = HYPER_MSHV;
05ad79
 	else if (!strncmp("VMwareVMware", hyper_vendor_id, 12))
05ad79
 		desc->hyper = HYPER_VMWARE;
05ad79
+	else if (!strncmp("UnisysSpar64", hyper_vendor_id, 12))
05ad79
+		desc->hyper = HYPER_SPAR;
05ad79
 }
05ad79
 
05ad79
-#else	/* ! __x86_64__ */
05ad79
+#else /* ! (__x86_64__ || __i386__) */
05ad79
 static void
05ad79
 read_hypervisor_cpuid(struct lscpu_desc *desc __attribute__((__unused__)))
05ad79
 {
05ad79
 }
05ad79
 #endif
05ad79
 
05ad79
+static int is_compatible(const char *path, const char *str)
05ad79
+{
05ad79
+	FILE *fd = path_fopen("r", 0, "%s", path);
05ad79
+
05ad79
+	if (fd) {
05ad79
+		char buf[256];
05ad79
+		size_t i, len;
05ad79
+
05ad79
+		memset(buf, 0, sizeof(buf));
05ad79
+		len = fread(buf, 1, sizeof(buf) - 1, fd);
05ad79
+		fclose(fd);
05ad79
+
05ad79
+		for (i = 0; i < len;) {
05ad79
+			if (!strcmp(&buf[i], str))
05ad79
+				return 1;
05ad79
+			i += strlen(&buf[i]);
05ad79
+			i++;
05ad79
+		}
05ad79
+	}
05ad79
+
05ad79
+	return 0;
05ad79
+}
05ad79
+
05ad79
+static int
05ad79
+read_hypervisor_powerpc(struct lscpu_desc *desc)
05ad79
+{
05ad79
+	assert(!desc->hyper);
05ad79
+
05ad79
+	 /* IBM iSeries: legacy, para-virtualized on top of OS/400 */
05ad79
+	if (path_exist("/proc/iSeries")) {
05ad79
+		desc->hyper = HYPER_OS400;
05ad79
+		desc->virtype = VIRT_PARA;
05ad79
+
05ad79
+	/* PowerNV (POWER Non-Virtualized, bare-metal) */
05ad79
+	} else if (is_compatible(_PATH_PROC_DEVICETREE "/compatible", "ibm,powernv")) {
05ad79
+		desc->hyper = HYPER_NONE;
05ad79
+		desc->virtype = VIRT_NONE;
05ad79
+
05ad79
+	/* PowerVM (IBM's proprietary hypervisor, aka pHyp) */
05ad79
+	} else if (path_exist(_PATH_PROC_DEVICETREE "/ibm,partition-name")
05ad79
+		   && path_exist(_PATH_PROC_DEVICETREE "/hmc-managed?")
05ad79
+		   && !path_exist(_PATH_PROC_DEVICETREE "/chosen/qemu,graphic-width")) {
05ad79
+		FILE *fd;
05ad79
+		desc->hyper = HYPER_PHYP;
05ad79
+		desc->virtype = VIRT_PARA;
05ad79
+		fd = path_fopen("r", 0, _PATH_PROC_DEVICETREE "/ibm,partition-name");
05ad79
+		if (fd) {
05ad79
+			char buf[256];
05ad79
+			if (fscanf(fd, "%255s", buf) == 1 && !strcmp(buf, "full"))
05ad79
+				desc->virtype = VIRT_NONE;
05ad79
+			fclose(fd);
05ad79
+		}
05ad79
+
05ad79
+	/* Qemu */
05ad79
+	} else if (is_compatible(_PATH_PROC_DEVICETREE "/compatible", "qemu,pseries")) {
05ad79
+		desc->hyper = HYPER_KVM;
05ad79
+		desc->virtype = VIRT_PARA;
05ad79
+	}
05ad79
+	return desc->hyper;
05ad79
+}
05ad79
+
05ad79
+#ifdef INCLUDE_VMWARE_BDOOR
05ad79
+
05ad79
+#define VMWARE_BDOOR_MAGIC          0x564D5868
05ad79
+#define VMWARE_BDOOR_PORT           0x5658
05ad79
+#define VMWARE_BDOOR_CMD_GETVERSION 10
05ad79
+
05ad79
+static UL_ASAN_BLACKLIST
05ad79
+void vmware_bdoor(uint32_t *eax, uint32_t *ebx, uint32_t *ecx, uint32_t *edx)
05ad79
+{
05ad79
+	__asm__(
05ad79
+#if defined(__PIC__) && defined(__i386__)
05ad79
+		/* x86 PIC cannot clobber ebx -- gcc bitches */
05ad79
+		"xchg %%ebx, %%esi;"
05ad79
+		"inl (%%dx), %%eax;"
05ad79
+		"xchg %%esi, %%ebx;"
05ad79
+		: "=S" (*ebx),
05ad79
+#else
05ad79
+		"inl (%%dx), %%eax;"
05ad79
+		: "=b" (*ebx),
05ad79
+#endif
05ad79
+		  "=a" (*eax),
05ad79
+		  "=c" (*ecx),
05ad79
+		  "=d" (*edx)
05ad79
+		: "0" (VMWARE_BDOOR_MAGIC),
05ad79
+		  "1" (VMWARE_BDOOR_CMD_GETVERSION),
05ad79
+		  "2" (VMWARE_BDOOR_PORT),
05ad79
+		  "3" (0)
05ad79
+		: "memory");
05ad79
+}
05ad79
+
05ad79
+static jmp_buf segv_handler_env;
05ad79
+
05ad79
+static void
05ad79
+segv_handler(__attribute__((__unused__)) int sig,
05ad79
+             __attribute__((__unused__)) siginfo_t *info,
05ad79
+             __attribute__((__unused__)) void *ignored)
05ad79
+{
05ad79
+	siglongjmp(segv_handler_env, 1);
05ad79
+}
05ad79
+
05ad79
+static int
05ad79
+is_vmware_platform(void)
05ad79
+{
05ad79
+	uint32_t eax, ebx, ecx, edx;
05ad79
+	struct sigaction act, oact;
05ad79
+
05ad79
+	/*
05ad79
+	 * FIXME: Not reliable for non-root users. Note it works as expected if
05ad79
+	 * vmware_bdoor() is not optimized for PIE, but then it fails to build
05ad79
+	 * on 32bit x86 systems. See lscpu git log for more details (commit
05ad79
+	 * 7845b91dbc7690064a2be6df690e4aaba728fb04).     kzak [3-Nov-2016]
05ad79
+	 */
05ad79
+	if (getuid() != 0)
05ad79
+		return 0;
05ad79
+
05ad79
+	/*
05ad79
+	 * The assembly routine for vmware detection works
05ad79
+	 * fine under vmware, even if ran as regular user. But
05ad79
+	 * on real HW or under other hypervisors, it segfaults (which is
05ad79
+	 * expected). So we temporarily install SIGSEGV handler to catch
05ad79
+	 * the signal. All this magic is needed because lscpu
05ad79
+	 * isn't supposed to require root privileges.
05ad79
+	 */
05ad79
+	if (sigsetjmp(segv_handler_env, 1))
05ad79
+		return 0;
05ad79
+
05ad79
+	memset(&act, 0, sizeof(act));
05ad79
+	act.sa_sigaction = segv_handler;
05ad79
+	act.sa_flags = SA_SIGINFO;
05ad79
+
05ad79
+	if (sigaction(SIGSEGV, &act, &oact))
05ad79
+		err(EXIT_FAILURE, _("cannot set signal handler"));
05ad79
+
05ad79
+	vmware_bdoor(&eax, &ebx, &ecx, &edx;;
05ad79
+
05ad79
+	if (sigaction(SIGSEGV, &oact, NULL))
05ad79
+		err(EXIT_FAILURE, _("cannot restore signal handler"));
05ad79
+
05ad79
+	return eax != (uint32_t)-1 && ebx == VMWARE_BDOOR_MAGIC;
05ad79
+}
05ad79
+
05ad79
+#else /* ! INCLUDE_VMWARE_BDOOR */
05ad79
+
05ad79
+static int
05ad79
+is_vmware_platform(void)
05ad79
+{
05ad79
+	return 0;
05ad79
+}
05ad79
+
05ad79
+#endif /* INCLUDE_VMWARE_BDOOR */
05ad79
+
05ad79
 static void
05ad79
 read_hypervisor(struct lscpu_desc *desc, struct lscpu_modifier *mod)
05ad79
 {
05ad79
-	if (mod->system != SYSTEM_SNAPSHOT)
05ad79
+	FILE *fd;
05ad79
+
05ad79
+	/* We have to detect WSL first. is_vmware_platform() crashes on Windows 10. */
05ad79
+
05ad79
+	if ((fd = path_fopen("r", 0, _PATH_PROC_OSRELEASE))) {
05ad79
+		char buf[256];
05ad79
+
05ad79
+		if (fgets(buf, sizeof(buf), fd) != NULL) {
05ad79
+			if (strstr(buf, "Microsoft")) {
05ad79
+				desc->hyper = HYPER_WSL;
05ad79
+				desc->virtype = VIRT_CONT;
05ad79
+			}
05ad79
+		}
05ad79
+		fclose(fd);
05ad79
+		if (desc->virtype)
05ad79
+			return;
05ad79
+	}
05ad79
+
05ad79
+	if (mod->system != SYSTEM_SNAPSHOT) {
05ad79
 		read_hypervisor_cpuid(desc);
05ad79
+		if (!desc->hyper)
05ad79
+			desc->hyper = read_hypervisor_dmi();
05ad79
+		if (!desc->hyper && is_vmware_platform())
05ad79
+			desc->hyper = HYPER_VMWARE;
05ad79
+	}
05ad79
 
05ad79
-	if (desc->hyper)
05ad79
-		/* hvm */
05ad79
+	if (desc->hyper) {
05ad79
 		desc->virtype = VIRT_FULL;
05ad79
 
05ad79
+		if (desc->hyper == HYPER_XEN) {
05ad79
+			uint32_t features;
05ad79
+
05ad79
+			fd = path_fopen("r", 0, _PATH_SYS_HYP_FEATURES);
05ad79
+			if (fd && fscanf(fd, "%x", &features) == 1) {
05ad79
+				/* Xen PV domain */
05ad79
+				if (features & XEN_FEATURES_PV_MASK)
05ad79
+					desc->virtype = VIRT_PARA;
05ad79
+				/* Xen PVH domain */
05ad79
+				else if ((features & XEN_FEATURES_PVH_MASK)
05ad79
+								== XEN_FEATURES_PVH_MASK)
05ad79
+					desc->virtype = VIRT_PARA;
05ad79
+				fclose(fd);
05ad79
+			} else {
05ad79
+				err(EXIT_FAILURE, _("failed to read from: %s"),
05ad79
+						_PATH_SYS_HYP_FEATURES);
05ad79
+			}
05ad79
+		}
05ad79
+	} else if (read_hypervisor_powerpc(desc) > 0) {}
05ad79
+
05ad79
+	/* Xen para-virt or dom0 */
05ad79
 	else if (path_exist(_PATH_PROC_XEN)) {
05ad79
-		/* Xen para-virt or dom0 */
05ad79
-		FILE *fd = path_fopen("r", 0, _PATH_PROC_XENCAP);
05ad79
 		int dom0 = 0;
05ad79
+		fd = path_fopen("r", 0, _PATH_PROC_XENCAP);
05ad79
 
05ad79
 		if (fd) {
05ad79
 			char buf[256];
05ad79
 
05ad79
-			if (fscanf(fd, "%s", buf) == 1 &&
05ad79
+			if (fscanf(fd, "%255s", buf) == 1 &&
05ad79
 			    !strcmp(buf, "control_d"))
05ad79
 				dom0 = 1;
05ad79
 			fclose(fd);
05ad79
@@ -558,16 +976,21 @@ read_hypervisor(struct lscpu_desc *desc, struct lscpu_modifier *mod)
05ad79
 		desc->virtype = dom0 ? VIRT_NONE : VIRT_PARA;
05ad79
 		desc->hyper = HYPER_XEN;
05ad79
 
05ad79
-	} else if (has_pci_device(0x5853, 0x0001)) {
05ad79
-		/* Xen full-virt on non-x86_64 */
05ad79
+	/* Xen full-virt on non-x86_64 */
05ad79
+	} else if (has_pci_device( hv_vendor_pci[HYPER_XEN], hv_graphics_pci[HYPER_XEN])) {
05ad79
 		desc->hyper = HYPER_XEN;
05ad79
 		desc->virtype = VIRT_FULL;
05ad79
-	} else if (path_exist(_PATH_PROC_SYSINFO)) {
05ad79
-		FILE *fd = path_fopen("r", 0, _PATH_PROC_SYSINFO);
05ad79
+	} else if (has_pci_device( hv_vendor_pci[HYPER_VMWARE], hv_graphics_pci[HYPER_VMWARE])) {
05ad79
+		desc->hyper = HYPER_VMWARE;
05ad79
+		desc->virtype = VIRT_FULL;
05ad79
+	} else if (has_pci_device( hv_vendor_pci[HYPER_VBOX], hv_graphics_pci[HYPER_VBOX])) {
05ad79
+		desc->hyper = HYPER_VBOX;
05ad79
+		desc->virtype = VIRT_FULL;
05ad79
+
05ad79
+	/* IBM PR/SM */
05ad79
+	} else if ((fd = path_fopen("r", 0, _PATH_PROC_SYSINFO))) {
05ad79
 		char buf[BUFSIZ];
05ad79
 
05ad79
-		if (!fd)
05ad79
-			return;
05ad79
 		desc->hyper = HYPER_IBM;
05ad79
 		desc->hypervisor = "PR/SM";
05ad79
 		desc->virtype = VIRT_FULL;
05ad79
@@ -597,6 +1020,45 @@ read_hypervisor(struct lscpu_desc *desc, struct lscpu_modifier *mod)
05ad79
 		}
05ad79
 		fclose(fd);
05ad79
 	}
05ad79
+
05ad79
+	/* OpenVZ/Virtuozzo - /proc/vz dir should exist
05ad79
+	 *		      /proc/bc should not */
05ad79
+	else if (path_exist(_PATH_PROC_VZ) && !path_exist(_PATH_PROC_BC)) {
05ad79
+		desc->hyper = HYPER_PARALLELS;
05ad79
+		desc->virtype = VIRT_CONT;
05ad79
+
05ad79
+	/* IBM */
05ad79
+	} else if (desc->vendor &&
05ad79
+		 (strcmp(desc->vendor, "PowerVM Lx86") == 0 ||
05ad79
+		  strcmp(desc->vendor, "IBM/S390") == 0)) {
05ad79
+		desc->hyper = HYPER_IBM;
05ad79
+		desc->virtype = VIRT_FULL;
05ad79
+
05ad79
+	/* User-mode-linux */
05ad79
+	} else if (desc->modelname && strstr(desc->modelname, "UML")) {
05ad79
+		desc->hyper = HYPER_UML;
05ad79
+		desc->virtype = VIRT_PARA;
05ad79
+
05ad79
+	/* Linux-VServer */
05ad79
+	} else if ((fd = path_fopen("r", 0, _PATH_PROC_STATUS))) {
05ad79
+		char buf[BUFSIZ];
05ad79
+		char *val = NULL;
05ad79
+
05ad79
+		while (fgets(buf, sizeof(buf), fd) != NULL) {
05ad79
+			if (lookup(buf, "VxID", &val))
05ad79
+				break;
05ad79
+		}
05ad79
+		fclose(fd);
05ad79
+
05ad79
+		if (val) {
05ad79
+			while (isdigit(*val))
05ad79
+				++val;
05ad79
+			if (!*val) {
05ad79
+				desc->hyper = HYPER_VSERVER;
05ad79
+				desc->virtype = VIRT_CONT;
05ad79
+			}
05ad79
+		}
05ad79
+	}
05ad79
 }
05ad79
 
05ad79
 /* add @set to the @ary, unnecessary set is deallocated. */
05ad79
@@ -622,9 +1084,12 @@ static int add_cpuset_to_array(cpu_set_t **ary, int *items, cpu_set_t *set)
05ad79
 }
05ad79
 
05ad79
 static void
05ad79
-read_topology(struct lscpu_desc *desc, int num)
05ad79
+read_topology(struct lscpu_desc *desc, int idx)
05ad79
 {
05ad79
-	cpu_set_t *thread_siblings, *core_siblings, *book_siblings;
05ad79
+	cpu_set_t *thread_siblings, *core_siblings;
05ad79
+	cpu_set_t *book_siblings, *drawer_siblings;
05ad79
+	int coreid, socketid, bookid, drawerid;
05ad79
+	int i, num = real_cpu_num(desc, idx);
05ad79
 
05ad79
 	if (!path_exist(_PATH_SYS_CPU "/cpu%d/topology/thread_siblings", num))
05ad79
 		return;
05ad79
@@ -634,13 +1099,32 @@ read_topology(struct lscpu_desc *desc, int num)
05ad79
 	core_siblings = path_read_cpuset(maxcpus, _PATH_SYS_CPU
05ad79
 					"/cpu%d/topology/core_siblings", num);
05ad79
 	book_siblings = NULL;
05ad79
-	if (path_exist(_PATH_SYS_CPU "/cpu%d/topology/book_siblings", num)) {
05ad79
+	if (path_exist(_PATH_SYS_CPU "/cpu%d/topology/book_siblings", num))
05ad79
 		book_siblings = path_read_cpuset(maxcpus, _PATH_SYS_CPU
05ad79
 					    "/cpu%d/topology/book_siblings", num);
05ad79
-	}
05ad79
+	drawer_siblings = NULL;
05ad79
+	if (path_exist(_PATH_SYS_CPU "/cpu%d/topology/drawer_siblings", num))
05ad79
+		drawer_siblings = path_read_cpuset(maxcpus, _PATH_SYS_CPU
05ad79
+					    "/cpu%d/topology/drawer_siblings", num);
05ad79
+	coreid = -1;
05ad79
+	if (path_exist(_PATH_SYS_CPU "/cpu%d/topology/core_id", num))
05ad79
+		coreid = path_read_s32(_PATH_SYS_CPU
05ad79
+				       "/cpu%d/topology/core_id", num);
05ad79
+	socketid = -1;
05ad79
+	if (path_exist(_PATH_SYS_CPU "/cpu%d/topology/physical_package_id", num))
05ad79
+		socketid = path_read_s32(_PATH_SYS_CPU
05ad79
+				       "/cpu%d/topology/physical_package_id", num);
05ad79
+	bookid = -1;
05ad79
+	if (path_exist(_PATH_SYS_CPU "/cpu%d/topology/book_id", num))
05ad79
+		bookid = path_read_s32(_PATH_SYS_CPU
05ad79
+				       "/cpu%d/topology/book_id", num);
05ad79
+	drawerid = -1;
05ad79
+	if (path_exist(_PATH_SYS_CPU "/cpu%d/topology/drawer_id", num))
05ad79
+		drawerid = path_read_s32(_PATH_SYS_CPU
05ad79
+				       "/cpu%d/topology/drawer_id", num);
05ad79
 
05ad79
 	if (!desc->coremaps) {
05ad79
-		int nbooks, nsockets, ncores, nthreads;
05ad79
+		int ndrawers, nbooks, nsockets, ncores, nthreads;
05ad79
 		size_t setsize = CPU_ALLOC_SIZE(maxcpus);
05ad79
 
05ad79
 		/* threads within one core */
05ad79
@@ -666,12 +1150,17 @@ read_topology(struct lscpu_desc *desc, int num)
05ad79
 		if (!nbooks)
05ad79
 			nbooks = 1;
05ad79
 
05ad79
+		/* number of drawers */
05ad79
+		ndrawers = desc->ncpus / nbooks / nthreads / ncores / nsockets;
05ad79
+		if (!ndrawers)
05ad79
+			ndrawers = 1;
05ad79
+
05ad79
 		/* all threads, see also read_basicinfo()
05ad79
 		 * -- fallback for kernels without
05ad79
 		 *    /sys/devices/system/cpu/online.
05ad79
 		 */
05ad79
 		if (!desc->nthreads)
05ad79
-			desc->nthreads = nbooks * nsockets * ncores * nthreads;
05ad79
+			desc->nthreads = ndrawers * nbooks * nsockets * ncores * nthreads;
05ad79
 
05ad79
 		/* For each map we make sure that it can have up to ncpuspos
05ad79
 		 * entries. This is because we cannot reliably calculate the
05ad79
@@ -681,19 +1170,43 @@ read_topology(struct lscpu_desc *desc, int num)
05ad79
 		 */
05ad79
 		desc->coremaps = xcalloc(desc->ncpuspos, sizeof(cpu_set_t *));
05ad79
 		desc->socketmaps = xcalloc(desc->ncpuspos, sizeof(cpu_set_t *));
05ad79
-		if (book_siblings)
05ad79
+		desc->coreids = xcalloc(desc->ncpuspos, sizeof(*desc->drawerids));
05ad79
+		desc->socketids = xcalloc(desc->ncpuspos, sizeof(*desc->drawerids));
05ad79
+		for (i = 0; i < desc->ncpuspos; i++)
05ad79
+			desc->coreids[i] = desc->socketids[i] = -1;
05ad79
+		if (book_siblings) {
05ad79
 			desc->bookmaps = xcalloc(desc->ncpuspos, sizeof(cpu_set_t *));
05ad79
+			desc->bookids = xcalloc(desc->ncpuspos, sizeof(*desc->drawerids));
05ad79
+			for (i = 0; i < desc->ncpuspos; i++)
05ad79
+				desc->bookids[i] = -1;
05ad79
+		}
05ad79
+		if (drawer_siblings) {
05ad79
+			desc->drawermaps = xcalloc(desc->ncpuspos, sizeof(cpu_set_t *));
05ad79
+			desc->drawerids = xcalloc(desc->ncpuspos, sizeof(*desc->drawerids));
05ad79
+			for (i = 0; i < desc->ncpuspos; i++)
05ad79
+				desc->drawerids[i] = -1;
05ad79
+		}
05ad79
 	}
05ad79
 
05ad79
 	add_cpuset_to_array(desc->socketmaps, &desc->nsockets, core_siblings);
05ad79
+	desc->coreids[idx] = coreid;
05ad79
 	add_cpuset_to_array(desc->coremaps, &desc->ncores, thread_siblings);
05ad79
-	if (book_siblings)
05ad79
+	desc->socketids[idx] = socketid;
05ad79
+	if (book_siblings) {
05ad79
 		add_cpuset_to_array(desc->bookmaps, &desc->nbooks, book_siblings);
05ad79
+		desc->bookids[idx] = bookid;
05ad79
+	}
05ad79
+	if (drawer_siblings) {
05ad79
+		add_cpuset_to_array(desc->drawermaps, &desc->ndrawers, drawer_siblings);
05ad79
+		desc->drawerids[idx] = drawerid;
05ad79
+	}
05ad79
 }
05ad79
+
05ad79
 static void
05ad79
-read_polarization(struct lscpu_desc *desc, int num)
05ad79
+read_polarization(struct lscpu_desc *desc, int idx)
05ad79
 {
05ad79
 	char mode[64];
05ad79
+	int num = real_cpu_num(desc, idx);
05ad79
 
05ad79
 	if (desc->dispatching < 0)
05ad79
 		return;
05ad79
@@ -703,35 +1216,67 @@ read_polarization(struct lscpu_desc *desc, int num)
05ad79
 		desc->polarization = xcalloc(desc->ncpuspos, sizeof(int));
05ad79
 	path_read_str(mode, sizeof(mode), _PATH_SYS_CPU "/cpu%d/polarization", num);
05ad79
 	if (strncmp(mode, "vertical:low", sizeof(mode)) == 0)
05ad79
-		desc->polarization[num] = POLAR_VLOW;
05ad79
+		desc->polarization[idx] = POLAR_VLOW;
05ad79
 	else if (strncmp(mode, "vertical:medium", sizeof(mode)) == 0)
05ad79
-		desc->polarization[num] = POLAR_VMEDIUM;
05ad79
+		desc->polarization[idx] = POLAR_VMEDIUM;
05ad79
 	else if (strncmp(mode, "vertical:high", sizeof(mode)) == 0)
05ad79
-		desc->polarization[num] = POLAR_VHIGH;
05ad79
+		desc->polarization[idx] = POLAR_VHIGH;
05ad79
 	else if (strncmp(mode, "horizontal", sizeof(mode)) == 0)
05ad79
-		desc->polarization[num] = POLAR_HORIZONTAL;
05ad79
+		desc->polarization[idx] = POLAR_HORIZONTAL;
05ad79
 	else
05ad79
-		desc->polarization[num] = POLAR_UNKNOWN;
05ad79
+		desc->polarization[idx] = POLAR_UNKNOWN;
05ad79
 }
05ad79
 
05ad79
 static void
05ad79
-read_address(struct lscpu_desc *desc, int num)
05ad79
+read_address(struct lscpu_desc *desc, int idx)
05ad79
 {
05ad79
+	int num = real_cpu_num(desc, idx);
05ad79
+
05ad79
 	if (!path_exist(_PATH_SYS_CPU "/cpu%d/address", num))
05ad79
 		return;
05ad79
 	if (!desc->addresses)
05ad79
 		desc->addresses = xcalloc(desc->ncpuspos, sizeof(int));
05ad79
-	desc->addresses[num] = path_read_s32(_PATH_SYS_CPU "/cpu%d/address", num);
05ad79
+	desc->addresses[idx] = path_read_s32(_PATH_SYS_CPU "/cpu%d/address", num);
05ad79
 }
05ad79
 
05ad79
 static void
05ad79
-read_configured(struct lscpu_desc *desc, int num)
05ad79
+read_configured(struct lscpu_desc *desc, int idx)
05ad79
 {
05ad79
+	int num = real_cpu_num(desc, idx);
05ad79
+
05ad79
 	if (!path_exist(_PATH_SYS_CPU "/cpu%d/configure", num))
05ad79
 		return;
05ad79
 	if (!desc->configured)
05ad79
 		desc->configured = xcalloc(desc->ncpuspos, sizeof(int));
05ad79
-	desc->configured[num] = path_read_s32(_PATH_SYS_CPU "/cpu%d/configure", num);
05ad79
+	desc->configured[idx] = path_read_s32(_PATH_SYS_CPU "/cpu%d/configure", num);
05ad79
+}
05ad79
+
05ad79
+static void
05ad79
+read_max_mhz(struct lscpu_desc *desc, int idx)
05ad79
+{
05ad79
+	int num = real_cpu_num(desc, idx);
05ad79
+
05ad79
+	if (!path_exist(_PATH_SYS_CPU "/cpu%d/cpufreq/cpuinfo_max_freq", num))
05ad79
+		return;
05ad79
+	if (!desc->maxmhz)
05ad79
+		desc->maxmhz = xcalloc(desc->ncpuspos, sizeof(char *));
05ad79
+	xasprintf(&(desc->maxmhz[idx]), "%.4f",
05ad79
+		  (float)path_read_s32(_PATH_SYS_CPU
05ad79
+				       "/cpu%d/cpufreq/cpuinfo_max_freq", num) / 1000);
05ad79
+}
05ad79
+
05ad79
+static void
05ad79
+read_min_mhz(struct lscpu_desc *desc, int idx)
05ad79
+{
05ad79
+	int num = real_cpu_num(desc, idx);
05ad79
+
05ad79
+	if (!path_exist(_PATH_SYS_CPU "/cpu%d/cpufreq/cpuinfo_min_freq", num))
05ad79
+		return;
05ad79
+	if (!desc->minmhz)
05ad79
+		desc->minmhz = xcalloc(desc->ncpuspos, sizeof(char *));
05ad79
+	xasprintf(&(desc->minmhz[idx]), "%.4f",
05ad79
+		  (float)path_read_s32(_PATH_SYS_CPU
05ad79
+				       "/cpu%d/cpufreq/cpuinfo_min_freq", num) / 1000);
05ad79
 }
05ad79
 
05ad79
 static int
05ad79
@@ -744,13 +1289,14 @@ cachecmp(const void *a, const void *b)
05ad79
 }
05ad79
 
05ad79
 static void
05ad79
-read_cache(struct lscpu_desc *desc, int num)
05ad79
+read_cache(struct lscpu_desc *desc, int idx)
05ad79
 {
05ad79
 	char buf[256];
05ad79
 	int i;
05ad79
+	int num = real_cpu_num(desc, idx);
05ad79
 
05ad79
 	if (!desc->ncaches) {
05ad79
-		while(path_exist(_PATH_SYS_SYSTEM "/cpu/cpu%d/cache/index%d",
05ad79
+		while(path_exist(_PATH_SYS_CPU "/cpu%d/cache/index%d",
05ad79
 					num, desc->ncaches))
05ad79
 			desc->ncaches++;
05ad79
 
05ad79
@@ -763,7 +1309,7 @@ read_cache(struct lscpu_desc *desc, int num)
05ad79
 		struct cpu_cache *ca = &desc->caches[i];
05ad79
 		cpu_set_t *map;
05ad79
 
05ad79
-		if (!path_exist(_PATH_SYS_SYSTEM "/cpu/cpu%d/cache/index%d",
05ad79
+		if (!path_exist(_PATH_SYS_CPU "/cpu%d/cache/index%d",
05ad79
 				num, i))
05ad79
 			continue;
05ad79
 		if (!ca->name) {
05ad79
@@ -791,10 +1337,13 @@ read_cache(struct lscpu_desc *desc, int num)
05ad79
 			ca->name = xstrdup(buf);
05ad79
 
05ad79
 			/* cache size */
05ad79
-			path_read_str(buf, sizeof(buf),
05ad79
-					_PATH_SYS_CPU "/cpu%d/cache/index%d/size",
05ad79
-					num, i);
05ad79
-			ca->size = xstrdup(buf);
05ad79
+			if (path_exist(_PATH_SYS_CPU "/cpu%d/cache/index%d/size",num, i)) {
05ad79
+				path_read_str(buf, sizeof(buf),
05ad79
+					_PATH_SYS_CPU "/cpu%d/cache/index%d/size", num, i);
05ad79
+				ca->size = xstrdup(buf);
05ad79
+			} else {
05ad79
+				ca->size = xstrdup("unknown size");
05ad79
+			}
05ad79
 		}
05ad79
 
05ad79
 		/* information about how CPUs share different caches */
05ad79
@@ -867,17 +1416,18 @@ read_nodes(struct lscpu_desc *desc)
05ad79
 	/* information about how nodes share different CPUs */
05ad79
 	for (i = 0; i < desc->nnodes; i++)
05ad79
 		desc->nodemaps[i] = path_read_cpuset(maxcpus,
05ad79
-					_PATH_SYS_SYSTEM "/node/node%d/cpumap",
05ad79
+					_PATH_SYS_NODE "/node%d/cpumap",
05ad79
 					desc->idx2nodenum[i]);
05ad79
 }
05ad79
 
05ad79
 static char *
05ad79
-get_cell_data(struct lscpu_desc *desc, int cpu, int col,
05ad79
+get_cell_data(struct lscpu_desc *desc, int idx, int col,
05ad79
 	      struct lscpu_modifier *mod,
05ad79
 	      char *buf, size_t bufsz)
05ad79
 {
05ad79
 	size_t setsize = CPU_ALLOC_SIZE(maxcpus);
05ad79
-	size_t idx;
05ad79
+	size_t i;
05ad79
+	int cpu = real_cpu_num(desc, idx);
05ad79
 
05ad79
 	*buf = '\0';
05ad79
 
05ad79
@@ -886,24 +1436,57 @@ get_cell_data(struct lscpu_desc *desc, int cpu, int col,
05ad79
 		snprintf(buf, bufsz, "%d", cpu);
05ad79
 		break;
05ad79
 	case COL_CORE:
05ad79
-		if (cpuset_ary_isset(cpu, desc->coremaps,
05ad79
-				     desc->ncores, setsize, &idx) == 0)
05ad79
-			snprintf(buf, bufsz, "%zd", idx);
05ad79
+		if (mod->physical) {
05ad79
+			if (desc->coreids[idx] == -1)
05ad79
+				snprintf(buf, bufsz, "-");
05ad79
+			else
05ad79
+				snprintf(buf, bufsz, "%d", desc->coreids[idx]);
05ad79
+		} else {
05ad79
+			if (cpuset_ary_isset(cpu, desc->coremaps,
05ad79
+					     desc->ncores, setsize, &i) == 0)
05ad79
+				snprintf(buf, bufsz, "%zu", i);
05ad79
+		}
05ad79
 		break;
05ad79
 	case COL_SOCKET:
05ad79
-		if (cpuset_ary_isset(cpu, desc->socketmaps,
05ad79
-				     desc->nsockets, setsize, &idx) == 0)
05ad79
-			snprintf(buf, bufsz, "%zd", idx);
05ad79
+		if (mod->physical) {
05ad79
+			if (desc->socketids[idx] ==  -1)
05ad79
+				snprintf(buf, bufsz, "-");
05ad79
+			else
05ad79
+				snprintf(buf, bufsz, "%d", desc->socketids[idx]);
05ad79
+		} else {
05ad79
+			if (cpuset_ary_isset(cpu, desc->socketmaps,
05ad79
+					     desc->nsockets, setsize, &i) == 0)
05ad79
+				snprintf(buf, bufsz, "%zu", i);
05ad79
+		}
05ad79
 		break;
05ad79
 	case COL_NODE:
05ad79
 		if (cpuset_ary_isset(cpu, desc->nodemaps,
05ad79
-				     desc->nnodes, setsize, &idx) == 0)
05ad79
-			snprintf(buf, bufsz, "%d", desc->idx2nodenum[idx]);
05ad79
+				     desc->nnodes, setsize, &i) == 0)
05ad79
+			snprintf(buf, bufsz, "%d", desc->idx2nodenum[i]);
05ad79
+		break;
05ad79
+	case COL_DRAWER:
05ad79
+		if (mod->physical) {
05ad79
+			if (desc->drawerids[idx] == -1)
05ad79
+				snprintf(buf, bufsz, "-");
05ad79
+			else
05ad79
+				snprintf(buf, bufsz, "%d", desc->drawerids[idx]);
05ad79
+		} else {
05ad79
+			if (cpuset_ary_isset(cpu, desc->drawermaps,
05ad79
+					     desc->ndrawers, setsize, &i) == 0)
05ad79
+				snprintf(buf, bufsz, "%zu", i);
05ad79
+		}
05ad79
 		break;
05ad79
 	case COL_BOOK:
05ad79
-		if (cpuset_ary_isset(cpu, desc->bookmaps,
05ad79
-				     desc->nbooks, setsize, &idx) == 0)
05ad79
-			snprintf(buf, bufsz, "%zd", idx);
05ad79
+		if (mod->physical) {
05ad79
+			if (desc->bookids[idx] == -1)
05ad79
+				snprintf(buf, bufsz, "-");
05ad79
+			else
05ad79
+				snprintf(buf, bufsz, "%d", desc->bookids[idx]);
05ad79
+		} else {
05ad79
+			if (cpuset_ary_isset(cpu, desc->bookmaps,
05ad79
+					     desc->nbooks, setsize, &i) == 0)
05ad79
+				snprintf(buf, bufsz, "%zu", i);
05ad79
+		}
05ad79
 		break;
05ad79
 	case COL_CACHE:
05ad79
 	{
05ad79
@@ -915,24 +1498,26 @@ get_cell_data(struct lscpu_desc *desc, int cpu, int col,
05ad79
 			struct cpu_cache *ca = &desc->caches[j];
05ad79
 
05ad79
 			if (cpuset_ary_isset(cpu, ca->sharedmaps,
05ad79
-					     ca->nsharedmaps, setsize, &idx) == 0) {
05ad79
-				int x = snprintf(p, sz, "%zd", idx);
05ad79
-				if (x <= 0 || (size_t) x + 2 >= sz)
05ad79
+					     ca->nsharedmaps, setsize, &i) == 0) {
05ad79
+				int x = snprintf(p, sz, "%zu", i);
05ad79
+				if (x < 0 || (size_t) x >= sz)
05ad79
 					return NULL;
05ad79
 				p += x;
05ad79
 				sz -= x;
05ad79
 			}
05ad79
 			if (j != 0) {
05ad79
+				if (sz < 2)
05ad79
+					return NULL;
05ad79
 				*p++ = mod->compat ? ',' : ':';
05ad79
 				*p = '\0';
05ad79
-				sz++;
05ad79
+				sz--;
05ad79
 			}
05ad79
 		}
05ad79
 		break;
05ad79
 	}
05ad79
 	case COL_POLARIZATION:
05ad79
 		if (desc->polarization) {
05ad79
-			int x = desc->polarization[cpu];
05ad79
+			int x = desc->polarization[idx];
05ad79
 
05ad79
 			snprintf(buf, bufsz, "%s",
05ad79
 				 mod->mode == OUTPUT_PARSABLE ?
05ad79
@@ -942,28 +1527,36 @@ get_cell_data(struct lscpu_desc *desc, int cpu, int col,
05ad79
 		break;
05ad79
 	case COL_ADDRESS:
05ad79
 		if (desc->addresses)
05ad79
-			snprintf(buf, bufsz, "%d", desc->addresses[cpu]);
05ad79
+			snprintf(buf, bufsz, "%d", desc->addresses[idx]);
05ad79
 		break;
05ad79
 	case COL_CONFIGURED:
05ad79
 		if (!desc->configured)
05ad79
 			break;
05ad79
 		if (mod->mode == OUTPUT_PARSABLE)
05ad79
-			snprintf(buf, bufsz,
05ad79
-				 desc->configured[cpu] ? _("Y") : _("N"));
05ad79
+			snprintf(buf, bufsz, "%s",
05ad79
+				 desc->configured[idx] ? _("Y") : _("N"));
05ad79
 		else
05ad79
-			snprintf(buf, bufsz,
05ad79
-				 desc->configured[cpu] ? _("yes") : _("no"));
05ad79
+			snprintf(buf, bufsz, "%s",
05ad79
+				 desc->configured[idx] ? _("yes") : _("no"));
05ad79
 		break;
05ad79
 	case COL_ONLINE:
05ad79
 		if (!desc->online)
05ad79
 			break;
05ad79
 		if (mod->mode == OUTPUT_PARSABLE)
05ad79
-			snprintf(buf, bufsz,
05ad79
+			snprintf(buf, bufsz, "%s",
05ad79
 				 is_cpu_online(desc, cpu) ? _("Y") : _("N"));
05ad79
 		else
05ad79
-			snprintf(buf, bufsz,
05ad79
+			snprintf(buf, bufsz, "%s",
05ad79
 				 is_cpu_online(desc, cpu) ? _("yes") : _("no"));
05ad79
 		break;
05ad79
+	case COL_MAXMHZ:
05ad79
+		if (desc->maxmhz)
05ad79
+			xstrncpy(buf, desc->maxmhz[idx], bufsz);
05ad79
+		break;
05ad79
+	case COL_MINMHZ:
05ad79
+		if (desc->minmhz)
05ad79
+			xstrncpy(buf, desc->minmhz[idx], bufsz);
05ad79
+		break;
05ad79
 	}
05ad79
 	return buf;
05ad79
 }
05ad79
@@ -982,14 +1575,16 @@ get_cell_header(struct lscpu_desc *desc, int col,
05ad79
 
05ad79
 		for (i = desc->ncaches - 1; i >= 0; i--) {
05ad79
 			int x = snprintf(p, sz, "%s", desc->caches[i].name);
05ad79
-			if (x <= 0 || (size_t) x + 2 > sz)
05ad79
+			if (x < 0 || (size_t) x >= sz)
05ad79
 				return NULL;
05ad79
 			sz -= x;
05ad79
 			p += x;
05ad79
 			if (i > 0) {
05ad79
+				if (sz < 2)
05ad79
+					return NULL;
05ad79
 				*p++ = mod->compat ? ',' : ':';
05ad79
 				*p = '\0';
05ad79
-				sz++;
05ad79
+				sz--;
05ad79
 			}
05ad79
 		}
05ad79
 		if (desc->ncaches)
05ad79
@@ -1073,12 +1668,13 @@ print_parsable(struct lscpu_desc *desc, int cols[], int ncols,
05ad79
 	 */
05ad79
 	for (i = 0; i < desc->ncpuspos; i++) {
05ad79
 		int c;
05ad79
+		int cpu = real_cpu_num(desc, i);
05ad79
 
05ad79
-		if (!mod->offline && desc->online && !is_cpu_online(desc, i))
05ad79
+		if (!mod->offline && desc->online && !is_cpu_online(desc, cpu))
05ad79
 			continue;
05ad79
-		if (!mod->online && desc->online && is_cpu_online(desc, i))
05ad79
+		if (!mod->online && desc->online && is_cpu_online(desc, cpu))
05ad79
 			continue;
05ad79
-		if (desc->present && !is_cpu_present(desc, i))
05ad79
+		if (desc->present && !is_cpu_present(desc, cpu))
05ad79
 			continue;
05ad79
 		for (c = 0; c < ncols; c++) {
05ad79
 			if (mod->compat && cols[c] == COL_CACHE) {
05ad79
@@ -1106,38 +1702,49 @@ print_readable(struct lscpu_desc *desc, int cols[], int ncols,
05ad79
 	       struct lscpu_modifier *mod)
05ad79
 {
05ad79
 	int i;
05ad79
-	char buf[BUFSIZ], *data;
05ad79
-	struct tt *tt = tt_new_table(0);
05ad79
+	char buf[BUFSIZ];
05ad79
+	const char *data;
05ad79
+	struct libscols_table *table;
05ad79
 
05ad79
-	if (!tt)
05ad79
+	scols_init_debug(0);
05ad79
+
05ad79
+	table = scols_new_table();
05ad79
+	if (!table)
05ad79
 		 err(EXIT_FAILURE, _("failed to initialize output table"));
05ad79
 
05ad79
 	for (i = 0; i < ncols; i++) {
05ad79
 		data = get_cell_header(desc, cols[i], mod, buf, sizeof(buf));
05ad79
-		tt_define_column(tt, xstrdup(data), 0, 0);
05ad79
+		if (!scols_table_new_column(table, xstrdup(data), 0, 0))
05ad79
+			err(EXIT_FAILURE, _("failed to initialize output column"));
05ad79
 	}
05ad79
 
05ad79
 	for (i = 0; i < desc->ncpuspos; i++) {
05ad79
 		int c;
05ad79
-		struct tt_line *line;
05ad79
+		struct libscols_line *line;
05ad79
+		int cpu = real_cpu_num(desc, i);
05ad79
 
05ad79
-		if (!mod->offline && desc->online && !is_cpu_online(desc, i))
05ad79
+		if (!mod->offline && desc->online && !is_cpu_online(desc, cpu))
05ad79
 			continue;
05ad79
-		if (!mod->online && desc->online && is_cpu_online(desc, i))
05ad79
+		if (!mod->online && desc->online && is_cpu_online(desc, cpu))
05ad79
 			continue;
05ad79
-		if (desc->present && !is_cpu_present(desc, i))
05ad79
+		if (desc->present && !is_cpu_present(desc, cpu))
05ad79
 			continue;
05ad79
 
05ad79
-		line = tt_add_line(tt, NULL);
05ad79
+		line = scols_table_new_line(table, NULL);
05ad79
+		if (!line)
05ad79
+			err(EXIT_FAILURE, _("failed to initialize output line"));
05ad79
 
05ad79
 		for (c = 0; c < ncols; c++) {
05ad79
 			data = get_cell_data(desc, i, cols[c], mod,
05ad79
 					     buf, sizeof(buf));
05ad79
-			tt_line_set_data(line, c, data && *data ? xstrdup(data) : "-");
05ad79
+			if (!data || !*data)
05ad79
+				data = "-";
05ad79
+			scols_line_set_data(line, c, data);
05ad79
 		}
05ad79
 	}
05ad79
 
05ad79
-	tt_print_table(tt);
05ad79
+	scols_print_table(table);
05ad79
+	scols_unref_table(table);
05ad79
 }
05ad79
 
05ad79
 /* output formats "<key>  <value>"*/
05ad79
@@ -1211,8 +1818,9 @@ print_summary(struct lscpu_desc *desc, struct lscpu_modifier *mod)
05ad79
 			err(EXIT_FAILURE, _("failed to callocate cpu set"));
05ad79
 		CPU_ZERO_S(setsize, set);
05ad79
 		for (i = 0; i < desc->ncpuspos; i++) {
05ad79
-			if (!is_cpu_online(desc, i) && is_cpu_present(desc, i))
05ad79
-				CPU_SET_S(i, setsize, set);
05ad79
+			int cpu = real_cpu_num(desc, i);
05ad79
+			if (!is_cpu_online(desc, cpu) && is_cpu_present(desc, cpu))
05ad79
+				CPU_SET_S(cpu, setsize, set);
05ad79
 		}
05ad79
 		print_cpuset(mod->hex ? _("Off-line CPU(s) mask:") :
05ad79
 					_("Off-line CPU(s) list:"),
05ad79
@@ -1221,9 +1829,12 @@ print_summary(struct lscpu_desc *desc, struct lscpu_modifier *mod)
05ad79
 	}
05ad79
 
05ad79
 	if (desc->nsockets) {
05ad79
-		int cores_per_socket, sockets_per_book, books;
05ad79
+		int threads_per_core, cores_per_socket, sockets_per_book;
05ad79
+		int books_per_drawer, drawers;
05ad79
+		FILE *fd;
05ad79
 
05ad79
-		cores_per_socket = sockets_per_book = books = 0;
05ad79
+		threads_per_core = cores_per_socket = sockets_per_book = 0;
05ad79
+		books_per_drawer = drawers = 0;
05ad79
 		/* s390 detects its cpu topology via /proc/sysinfo, if present.
05ad79
 		 * Using simply the cpu topology masks in sysfs will not give
05ad79
 		 * usable results since everything is virtualized. E.g.
05ad79
@@ -1232,27 +1843,36 @@ print_summary(struct lscpu_desc *desc, struct lscpu_modifier *mod)
05ad79
 		 * If the cpu topology is not exported (e.g. 2nd level guest)
05ad79
 		 * fall back to old calculation scheme.
05ad79
 		 */
05ad79
-		if (path_exist(_PATH_PROC_SYSINFO)) {
05ad79
-			FILE *fd = path_fopen("r", 0, _PATH_PROC_SYSINFO);
05ad79
+		if ((fd = path_fopen("r", 0, _PATH_PROC_SYSINFO))) {
05ad79
 			char pbuf[BUFSIZ];
05ad79
-			int t0, t1, t2;
05ad79
+			int t0, t1;
05ad79
 
05ad79
 			while (fd && fgets(pbuf, sizeof(pbuf), fd) != NULL) {
05ad79
 				if (sscanf(pbuf, "CPU Topology SW:%d%d%d%d%d%d",
05ad79
-					   &t0, &t1, &t2, &books, &sockets_per_book,
05ad79
+					   &t0, &t1, &drawers, &books_per_drawer,
05ad79
+					   &sockets_per_book,
05ad79
 					   &cores_per_socket) == 6)
05ad79
 					break;
05ad79
 			}
05ad79
 			if (fd)
05ad79
 				fclose(fd);
05ad79
 		}
05ad79
-		print_n(_("Thread(s) per core:"), desc->nthreads / desc->ncores);
05ad79
+		if (desc->mtid)
05ad79
+			threads_per_core = atoi(desc->mtid) + 1;
05ad79
+		print_n(_("Thread(s) per core:"),
05ad79
+			threads_per_core ?: desc->nthreads / desc->ncores);
05ad79
 		print_n(_("Core(s) per socket:"),
05ad79
 			cores_per_socket ?: desc->ncores / desc->nsockets);
05ad79
 		if (desc->nbooks) {
05ad79
 			print_n(_("Socket(s) per book:"),
05ad79
 				sockets_per_book ?: desc->nsockets / desc->nbooks);
05ad79
-			print_n(_("Book(s):"), books ?: desc->nbooks);
05ad79
+			if (desc->ndrawers) {
05ad79
+				print_n(_("Book(s) per drawer:"),
05ad79
+					books_per_drawer ?: desc->nbooks / desc->ndrawers);
05ad79
+				print_n(_("Drawer(s):"), drawers ?: desc->ndrawers);
05ad79
+			} else {
05ad79
+				print_n(_("Book(s):"), books_per_drawer ?: desc->nbooks);
05ad79
+			}
05ad79
 		} else {
05ad79
 			print_n(_("Socket(s):"), sockets_per_book ?: desc->nsockets);
05ad79
 		}
05ad79
@@ -1261,6 +1881,8 @@ print_summary(struct lscpu_desc *desc, struct lscpu_modifier *mod)
05ad79
 		print_n(_("NUMA node(s):"), desc->nnodes);
05ad79
 	if (desc->vendor)
05ad79
 		print_s(_("Vendor ID:"), desc->vendor);
05ad79
+	if (desc->machinetype)
05ad79
+		print_s(_("Machine type:"), desc->machinetype);
05ad79
 	if (desc->family)
05ad79
 		print_s(_("CPU family:"), desc->family);
05ad79
 	if (desc->model || desc->revision)
05ad79
@@ -1271,6 +1893,14 @@ print_summary(struct lscpu_desc *desc, struct lscpu_modifier *mod)
05ad79
 		print_s(_("Stepping:"), desc->stepping);
05ad79
 	if (desc->mhz)
05ad79
 		print_s(_("CPU MHz:"), desc->mhz);
05ad79
+	if (desc->dynamic_mhz)
05ad79
+		print_s(_("CPU dynamic MHz:"), desc->dynamic_mhz);
05ad79
+	if (desc->static_mhz)
05ad79
+		print_s(_("CPU static MHz:"), desc->static_mhz);
05ad79
+	if (desc->maxmhz)
05ad79
+		print_s(_("CPU max MHz:"), desc->maxmhz[0]);
05ad79
+	if (desc->minmhz)
05ad79
+		print_s(_("CPU min MHz:"), desc->minmhz[0]);
05ad79
 	if (desc->bogomips)
05ad79
 		print_s(_("BogoMIPS:"), desc->bogomips);
05ad79
 	if (desc->virtflag) {
05ad79
@@ -1297,10 +1927,29 @@ print_summary(struct lscpu_desc *desc, struct lscpu_modifier *mod)
05ad79
 		}
05ad79
 	}
05ad79
 
05ad79
+	if (desc->necaches) {
05ad79
+		char cbuf[512];
05ad79
+
05ad79
+		for (i = desc->necaches - 1; i >= 0; i--) {
05ad79
+			snprintf(cbuf, sizeof(cbuf),
05ad79
+					_("%s cache:"), desc->ecaches[i].name);
05ad79
+			print_s(cbuf, desc->ecaches[i].size);
05ad79
+		}
05ad79
+	}
05ad79
+
05ad79
 	for (i = 0; i < desc->nnodes; i++) {
05ad79
 		snprintf(buf, sizeof(buf), _("NUMA node%d CPU(s):"), desc->idx2nodenum[i]);
05ad79
 		print_cpuset(buf, desc->nodemaps[i], mod->hex);
05ad79
 	}
05ad79
+
05ad79
+	if (desc->flags)
05ad79
+		print_s(_("Flags:"), desc->flags);
05ad79
+
05ad79
+	if (desc->physsockets) {
05ad79
+		print_n(_("Physical sockets:"), desc->physsockets);
05ad79
+		print_n(_("Physical chips:"), desc->physchips);
05ad79
+		print_n(_("Physical cores/chip:"), desc->physcoresperchip);
05ad79
+	}
05ad79
 }
05ad79
 
05ad79
 static void __attribute__((__noreturn__)) usage(FILE *out)
05ad79
@@ -1310,6 +1959,9 @@ static void __attribute__((__noreturn__)) usage(FILE *out)
05ad79
 	fputs(USAGE_HEADER, out);
05ad79
 	fprintf(out, _(" %s [options]\n"), program_invocation_short_name);
05ad79
 
05ad79
+	fputs(USAGE_SEPARATOR, out);
05ad79
+	fputs(_("Display information about the CPU architecture.\n"), out);
05ad79
+
05ad79
 	fputs(USAGE_OPTIONS, out);
05ad79
 	fputs(_(" -a, --all               print both online and offline CPUs (default for -e)\n"), out);
05ad79
 	fputs(_(" -b, --online            print online CPUs only (default for -p)\n"), out);
05ad79
@@ -1318,6 +1970,7 @@ static void __attribute__((__noreturn__)) usage(FILE *out)
05ad79
 	fputs(_(" -p, --parse[=<list>]    print out a parsable format\n"), out);
05ad79
 	fputs(_(" -s, --sysroot <dir>     use specified directory as system root\n"), out);
05ad79
 	fputs(_(" -x, --hex               print hexadecimal masks rather than lists of CPUs\n"), out);
05ad79
+	fputs(_(" -y, --physical          print physical instead of logical IDs\n"), out);
05ad79
 	fputs(USAGE_SEPARATOR, out);
05ad79
 	fputs(USAGE_HELP, out);
05ad79
 	fputs(USAGE_VERSION, out);
05ad79
@@ -1327,7 +1980,7 @@ static void __attribute__((__noreturn__)) usage(FILE *out)
05ad79
 	for (i = 0; i < ARRAY_SIZE(coldescs); i++)
05ad79
 		fprintf(out, " %13s  %s\n", coldescs[i].name, _(coldescs[i].help));
05ad79
 
05ad79
-	fprintf(out, _("\nFor more details see lscpu(1).\n"));
05ad79
+	fprintf(out, USAGE_MAN_TAIL("lscpu(1)"));
05ad79
 
05ad79
 	exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
05ad79
 }
05ad79
@@ -1335,22 +1988,23 @@ static void __attribute__((__noreturn__)) usage(FILE *out)
05ad79
 int main(int argc, char *argv[])
05ad79
 {
05ad79
 	struct lscpu_modifier _mod = { .mode = OUTPUT_SUMMARY }, *mod = &_mod;
05ad79
-	struct lscpu_desc _desc = { .flags = 0 }, *desc = &_desc;
05ad79
+	struct lscpu_desc _desc = { .flags = NULL }, *desc = &_desc;
05ad79
 	int c, i;
05ad79
 	int columns[ARRAY_SIZE(coldescs)], ncolumns = 0;
05ad79
 	int cpu_modifier_specified = 0;
05ad79
 
05ad79
 	static const struct option longopts[] = {
05ad79
-		{ "all",        no_argument,       0, 'a' },
05ad79
-		{ "online",     no_argument,       0, 'b' },
05ad79
-		{ "offline",    no_argument,       0, 'c' },
05ad79
-		{ "help",	no_argument,       0, 'h' },
05ad79
-		{ "extended",	optional_argument, 0, 'e' },
05ad79
-		{ "parse",	optional_argument, 0, 'p' },
05ad79
-		{ "sysroot",	required_argument, 0, 's' },
05ad79
-		{ "hex",	no_argument,	   0, 'x' },
05ad79
-		{ "version",	no_argument,	   0, 'V' },
05ad79
-		{ NULL,		0, 0, 0 }
05ad79
+		{ "all",        no_argument,       NULL, 'a' },
05ad79
+		{ "online",     no_argument,       NULL, 'b' },
05ad79
+		{ "offline",    no_argument,       NULL, 'c' },
05ad79
+		{ "help",	no_argument,       NULL, 'h' },
05ad79
+		{ "extended",	optional_argument, NULL, 'e' },
05ad79
+		{ "parse",	optional_argument, NULL, 'p' },
05ad79
+		{ "sysroot",	required_argument, NULL, 's' },
05ad79
+		{ "physical",	no_argument,	   NULL, 'y' },
05ad79
+		{ "hex",	no_argument,	   NULL, 'x' },
05ad79
+		{ "version",	no_argument,	   NULL, 'V' },
05ad79
+		{ NULL,		0, NULL, 0 }
05ad79
 	};
05ad79
 
05ad79
 	static const ul_excl_t excl[] = {	/* rows and cols in ASCII order */
05ad79
@@ -1365,7 +2019,7 @@ int main(int argc, char *argv[])
05ad79
 	textdomain(PACKAGE);
05ad79
 	atexit(close_stdout);
05ad79
 
05ad79
-	while ((c = getopt_long(argc, argv, "abce::hp::s:xV", longopts, NULL)) != -1) {
05ad79
+	while ((c = getopt_long(argc, argv, "abce::hp::s:xyV", longopts, NULL)) != -1) {
05ad79
 
05ad79
 		err_exclusive_options(c, longopts, excl, excl_st);
05ad79
 
05ad79
@@ -1404,12 +2058,14 @@ int main(int argc, char *argv[])
05ad79
 		case 'x':
05ad79
 			mod->hex = 1;
05ad79
 			break;
05ad79
+		case 'y':
05ad79
+			mod->physical = 1;
05ad79
+			break;
05ad79
 		case 'V':
05ad79
-			printf(_("%s from %s\n"), program_invocation_short_name,
05ad79
-			       PACKAGE_STRING);
05ad79
+			printf(UTIL_LINUX_VERSION);
05ad79
 			return EXIT_SUCCESS;
05ad79
 		default:
05ad79
-			usage(stderr);
05ad79
+			errtryhelp(EXIT_FAILURE);
05ad79
 		}
05ad79
 	}
05ad79
 
05ad79
@@ -1433,17 +2089,27 @@ int main(int argc, char *argv[])
05ad79
 	read_basicinfo(desc, mod);
05ad79
 
05ad79
 	for (i = 0; i < desc->ncpuspos; i++) {
05ad79
+		/* only consider present CPUs */
05ad79
+		if (desc->present &&
05ad79
+		    !CPU_ISSET(real_cpu_num(desc, i), desc->present))
05ad79
+			continue;
05ad79
 		read_topology(desc, i);
05ad79
 		read_cache(desc, i);
05ad79
 		read_polarization(desc, i);
05ad79
 		read_address(desc, i);
05ad79
 		read_configured(desc, i);
05ad79
+		read_max_mhz(desc, i);
05ad79
+		read_min_mhz(desc, i);
05ad79
 	}
05ad79
 
05ad79
 	if (desc->caches)
05ad79
 		qsort(desc->caches, desc->ncaches,
05ad79
 				sizeof(struct cpu_cache), cachecmp);
05ad79
 
05ad79
+	if (desc->ecaches)
05ad79
+		qsort(desc->ecaches, desc->necaches,
05ad79
+				sizeof(struct cpu_cache), cachecmp);
05ad79
+
05ad79
 	read_nodes(desc);
05ad79
 	read_hypervisor(desc, mod);
05ad79
 
05ad79
@@ -1468,6 +2134,8 @@ int main(int argc, char *argv[])
05ad79
 			columns[ncolumns++] = COL_CPU;
05ad79
 			if (desc->nodemaps)
05ad79
 				columns[ncolumns++] = COL_NODE;
05ad79
+			if (desc->drawermaps)
05ad79
+				columns[ncolumns++] = COL_DRAWER;
05ad79
 			if (desc->bookmaps)
05ad79
 				columns[ncolumns++] = COL_BOOK;
05ad79
 			if (desc->socketmaps)
05ad79
@@ -1484,6 +2152,10 @@ int main(int argc, char *argv[])
05ad79
 				columns[ncolumns++] = COL_POLARIZATION;
05ad79
 			if (desc->addresses)
05ad79
 				columns[ncolumns++] = COL_ADDRESS;
05ad79
+			if (desc->maxmhz)
05ad79
+				columns[ncolumns++] = COL_MAXMHZ;
05ad79
+			if (desc->minmhz)
05ad79
+				columns[ncolumns++] = COL_MINMHZ;
05ad79
 		}
05ad79
 		print_readable(desc, columns, ncolumns, mod);
05ad79
 		break;
05ad79
diff --git a/sys-utils/lscpu.h b/sys-utils/lscpu.h
05ad79
new file mode 100644
05ad79
index 0000000..4906c26
05ad79
--- /dev/null
05ad79
+++ b/sys-utils/lscpu.h
05ad79
@@ -0,0 +1,26 @@
05ad79
+#ifndef LSCPU_H
05ad79
+#define LSCPU_H
05ad79
+
05ad79
+/* hypervisor vendors */
05ad79
+enum {
05ad79
+	HYPER_NONE	= 0,
05ad79
+	HYPER_XEN,
05ad79
+	HYPER_KVM,
05ad79
+	HYPER_MSHV,
05ad79
+	HYPER_VMWARE,
05ad79
+	HYPER_IBM,		/* sys-z powervm */
05ad79
+	HYPER_VSERVER,
05ad79
+	HYPER_UML,
05ad79
+	HYPER_INNOTEK,		/* VBOX */
05ad79
+	HYPER_HITACHI,
05ad79
+	HYPER_PARALLELS,	/* OpenVZ/VIrtuozzo */
05ad79
+	HYPER_VBOX,
05ad79
+	HYPER_OS400,
05ad79
+	HYPER_PHYP,
05ad79
+	HYPER_SPAR,
05ad79
+	HYPER_WSL,
05ad79
+};
05ad79
+
05ad79
+extern int read_hypervisor_dmi(void);
05ad79
+
05ad79
+#endif /* LSCPU_H */
05ad79
-- 
05ad79
2.9.3
05ad79