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