From 92f7c0021fcd48f17b0d48880937ded81db0bf89 Mon Sep 17 00:00:00 2001 Message-Id: <92f7c0021fcd48f17b0d48880937ded81db0bf89.1715699915.git.mdw@distorted.org.uk> From: Mark Wooding Date: Thu, 30 Apr 2009 23:56:57 +0100 Subject: [PATCH] x86-model: New program, prints CPUID information. Organization: Straylight/Edgeware From: Mark Wooding --- Makefile.am | 8 ++ configure.ac | 6 ++ debian/control | 6 ++ debian/inst | 2 + x86-model.1 | 75 +++++++++++++++ x86-model.c | 253 +++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 350 insertions(+) create mode 100644 x86-model.1 create mode 100644 x86-model.c diff --git a/Makefile.am b/Makefile.am index 25ac5e4..03d42f6 100644 --- a/Makefile.am +++ b/Makefile.am @@ -110,6 +110,14 @@ cdb_check_domain_LDADD = -lcdb dist_man_MANS += cdb-probe.1 cdb-check-domain.1 endif +## x86 model identification +if X86 +bin_PROGRAMS += x86-model +x86_model_SOURCES = x86-model.c +x86_model_LDADD = $(mLib_LIBS) +dist_man_MANS += x86-model.1 +endif + ###-------------------------------------------------------------------------- ### Tools in scripts. diff --git a/configure.ac b/configure.ac index dda3d4e..814b686 100644 --- a/configure.ac +++ b/configure.ac @@ -30,6 +30,8 @@ AC_CONFIG_SRCDIR([shadowfix.in]) AC_CONFIG_AUX_DIR([config]) AM_INIT_AUTOMAKE([foreign]) +AC_CANONICAL_HOST + dnl-------------------------------------------------------------------------- dnl C programming environment. @@ -57,6 +59,10 @@ PKG_CHECK_MODULES([catacomb], [catacomb >= 2.1.1], [have_catacomb=yes], [have_catacomb=no]) AM_CONDITIONAL([HAVE_CATACOMB], [test $have_catacomb = yes]) +## Processor type. +case "$host_cpu" in i?86) x86=yes;; *) x86=no;; esac +AM_CONDITIONAL([X86], [test $x86 = yes -a $GCC = yes]) + dnl-------------------------------------------------------------------------- dnl Python, Perl and other scripting languages. diff --git a/debian/control b/debian/control index 4513548..a03ea22 100644 --- a/debian/control +++ b/debian/control @@ -136,3 +136,9 @@ Description: Identify and fix problematic whitespace in text files. and fix problems such as trailing whitespace and spaces before tabs. It can safely update files in place, and could therefore be used as part of a commit hook. + +Package: x86-model +Architecture: i386 +Section: utils +Description: Shows basic model information about x86 processors. + The cpuid program is probably better for most people. diff --git a/debian/inst b/debian/inst index ca8851d..7855b36 100644 --- a/debian/inst +++ b/debian/inst @@ -44,3 +44,5 @@ unfwd.1 nsict-mail /usr/share/man/man1 xtitle xtitle /usr/bin z zz /usr/bin z.1 zz /usr/share/man/man1 +x86-model x86-model /usr/bin +x86-model.1 x86-model /usr/share/man/man1 diff --git a/x86-model.1 b/x86-model.1 new file mode 100644 index 0000000..b363f6d --- /dev/null +++ b/x86-model.1 @@ -0,0 +1,75 @@ +.TH x86-model 1 "30 April 2009" "Edgeware tools" +.SH NAME +x86-model \- show x86 CPU model information and (a bit) more +.SH SYNOPSIS +.B x86-model +.RB [ \-v ] +.RB [ \-i \c +.RB | \-d \c +.RB |[ \-s ] +.IR leaf ] \c +.RB | \-x +.IR leaf ] +.SH DESCRIPTION +The +.b x86-model +program shows basic information about the host's x86-based processor, +gleaned from the CPUID instruction. It doesn't work on other processors +at all. The +.B \-v +flag causes more detailed information to be output. +.PP +The CPUID instruction reads an index in the EAX register which selects a +`leaf' of information; it sets the output registers EAX, EBX, ECX, and +EDX, to the appropriate values for the selected leaf. +.PP +By default, or with +.BR \-i , +it shows the `display family' and `display model' information for the +processor, in the form +.IB family _ model H +which is used in the tables in Appendix C of Intel's optimization +guide. In verbose mode, the processor type, family, model and stepping +are shown separately. +.PP +With the +.B \-d +option, all of the available CPUID information is dumped to standard +output. Each line has the form +.IP +.IB leaf : +.I eax +.I ebx +.I ecx +.I edx +.PP +Verbose mode makes no difference. +.PP +With the +.B \-s +option (or just a +.I leaf +index), prints the leaf of information selected by +.IR leaf , +as four hexadecimal numbers separated by spaces. In verbose mode, the +output is written on four lines, labelled with the appropriate register +names. +.PP +With the +.B \-x +option, the behaviour is as for +.B \-s +except that `extended function' information is selected by toggling bit +31 of the leaf index. +.PP +If no option is given, but there is a command-line argument, then the +behaviour is as for +.B \-s +with the leaf taken from the argument; otherwise the behaviour is as for +.BR \-i . +.SH SEE ALSO +.BR cpuid (1). +.PP +.I "Intel 64 and IA-32 Architectures Software Developer's Manual" +.br +.I "Intel 64 and IA-32 Architectures Optimization Reference Manual" diff --git a/x86-model.c b/x86-model.c new file mode 100644 index 0000000..59640c7 --- /dev/null +++ b/x86-model.c @@ -0,0 +1,253 @@ +/* -*-c-*- + * + * Determine x86 CPU model number + * + * (c) 2009 Mark Wooding + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/*----- Header files ------------------------------------------------------*/ + +#include +#include +#include + +#include +#include +#include + +#if !defined(__GNUC__) || !defined(__i386__) +# error "This isn't going to work." +#endif + +/*----- Data structures ---------------------------------------------------*/ + +struct cpuid { unsigned eax, ebx, ecx, edx; }; + +/*----- Main code ---------------------------------------------------------*/ + +static inline void cpuid(unsigned leaf, struct cpuid *c) +{ + __asm__ ("cpuid" + : "=a" (c->eax), + "=b" (c->ebx), + "=c" (c->ecx), + "=d" (c->edx) + : "a" (leaf)); +} + +#define f_verbose 1u +#define f_bogus 2u + +static void identify(unsigned f) +{ + struct cpuid c; + unsigned stepping, model, family, type, extmodel, extfamily; + unsigned dispfamily, dispmodel; + + cpuid(1, &c); + stepping = (c.eax >> 0) & 0x0f; + model = (c.eax >> 4) & 0x0f; + family = (c.eax >> 8) & 0x0f; + type = (c.eax >> 12) & 0x03; + extmodel = (c.eax >> 16) & 0x0f; + extfamily = (c.eax >> 20) & 0xff; + + dispfamily = family + (family == 0x0f ? extfamily : 0); + dispmodel = model | (family == 0x06 || family == 0x0f ? extmodel << 4 : 0); + + if (!(f & f_verbose)) + printf("%02X_%02XH\n", dispfamily, dispmodel); + else { + printf("type = %d\n", type); + printf("family = %d", dispfamily); + if (family == 0x0f) + printf(" (base = %d, extended = %d)", family, extfamily); + putchar('\n'); + printf("model = %d", dispmodel); + if (family == 0x06 || family == 0x0f) + printf(" (base = %d, extended = %d)", model, extmodel); + putchar('\n'); + printf("stepping = %d\n", type); + } +} + +static void dump(unsigned leaf) +{ + unsigned max; + struct cpuid c; + + cpuid(leaf, &c); + max = c.eax; + for (;;) { + printf("%08x: %08x %08x %08x %08x\n", leaf, c.eax, c.ebx, c.ecx, c.edx); + leaf++; + if (leaf > max) + break; + cpuid(leaf, &c); + } +} + +static void usage(FILE *fp) + { pquis(fp, "Usage: $ [-v] [-i | -d | [-s] LEAF | -x LEAF]\n"); } + +static void version(void) + { pquis(stdout, "$, version " VERSION ); } + +static void help(void) +{ + version(); putchar('\n'); usage(stdout); + pquis(stdout, "\n\ +Shows information about the host's processor. You may find cpuid(1) more\n\ +enlightening.\n\ +\n\ +Options:\n\ +\n\ +-h, --help This useless help text.\n\ +-V, --version Program's version number.\n\ +-u, --usage Short usage summary.\n\ +\n\ +-v, --verbose Show more detailed information.\n\ +\n\ +-i, --identify Show CPU display-model designation. [default]\n\ +-s, --show=LEAF Show named leaf of CPUID data.\n\ +-x, --extended=LEAF Show named extended leaf of CPUID data.\n\ +-d, --dump Dump all CPUID information.\n"); +} + +int main(int argc, char *argv[]) +{ + int i; + struct cpuid c; + unsigned f = 0; + unsigned mode = 0; + const char *leafstr = 0; + unsigned long leaf = -1; + char *q; + + enum { + m_default, + m_identify, + m_dump, + m_show, + m_ext + }; + + ego(argv[0]); + + for (;;) { + static const struct option opts[] = { + { "help", 0, 0, 'h' }, + { "version", 0, 0, 'V' }, + { "usage", 0, 0, 'u' }, + { "verbose", 0, 0, 'v' }, + { "identify", 0, 0, 'i' }, + { "dump", 0, 0, 'd' }, + { "show", OPTF_ARGREQ, 0, 's' }, + { "extended", OPTF_ARGREQ, 0, 'x' }, + { 0, 0, 0, 0 } + }; + + if ((i = mdwopt(argc, argv, "hVu" "v" "ids:x:", opts, 0, 0, 0)) < 0) + break; + switch (i) { + case 'h': + help(); + exit(0); + case 'V': + version(); + exit(0); + case 'u': + usage(stdout); + exit(0); + case 'v': + f |= f_verbose; + break; + case 'i': + if (mode) f |= f_bogus; + mode = m_identify; + break; + case 'd': + if (mode) f |= f_bogus; + mode = m_dump; + break; + case 's': + if (mode) f |= f_bogus; + mode = m_show; + leafstr = optarg; + break; + case 'x': + if (mode) f |= f_bogus; + mode = m_ext; + leafstr = optarg; + break; + default: + f |= f_bogus; + break; + } + } + + argv += optind; argc -= optind; + if ((f & f_bogus) || argc > 1 || (mode && argc)) { + usage(stderr); + exit(EXIT_FAILURE); + } + + if (!mode && argc) { + leafstr = argv[0]; + mode = m_show; + } + if (leafstr) { + errno = 0; + leaf = strtoul(leafstr, &q, 0); + if (q == leafstr || *q || errno || leaf > 0xffffffff) + die(EXIT_FAILURE, "bad leaf value: %s", leafstr); + } + + switch (mode) { + case m_default: + case m_identify: + identify(f); + break; + case m_ext: + leaf ^= 0x80000000; + /* fall through */ + case m_show: + cpuid(leaf, &c); + if (!(f & f_verbose)) + printf("%08x %08x %08x %08x\n", c.eax, c.ebx, c.ecx, c.edx); + else { + printf("EAX = %08x\n", c.eax); + printf("EBX = %08x\n", c.ebx); + printf("ECX = %08x\n", c.ecx); + printf("EDX = %08x\n", c.edx); + } + break; + case m_dump: + dump(0); + dump(0x80000000); + break; + default: + abort(); + } + + return (0); +} + +/*----- That's all, folks -------------------------------------------------*/ -- [mdw]