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