Hello community, here is the log from the commit of package iucode-tool for openSUSE:Factory checked in at 2018-02-02 22:23:17 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/iucode-tool (Old) and /work/SRC/openSUSE:Factory/.iucode-tool.new (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "iucode-tool" Fri Feb 2 22:23:17 2018 rev:6 rq:572024 version:2.3 Changes: -------- --- /work/SRC/openSUSE:Factory/iucode-tool/iucode-tool.changes 2017-10-27 14:00:22.314960804 +0200 +++ /work/SRC/openSUSE:Factory/.iucode-tool.new/iucode-tool.changes 2018-02-02 22:23:33.810286659 +0100 @@ -1,0 +2,19 @@ +Fri Feb 2 13:30:19 UTC 2018 - [email protected] + +- Update to version 2.3: + * Ready for release: v2.3 + * update copyright dates to 2018 + * iucode_tool(8): document changes to ucode filtering + * iucode_tool: support selecting by ucode revision + * iucode_tool: add function to parse signed 32-bit integers + * iucode_tool: optimize detection of base10 numeric names + * iucode_tool: better handle offline/non-continuous topology + * iucode_tool(8): document changes to --scan-system + * iucode_tool: select scan-system strategy change at runtime + * gitignore: rearrange, and ignore backup and vim swap files + * iucode_tool: move scan_system_processor() one layer down + * iucode_tool: do not scan-system while parsing + * iucode_tool: add two command-line parser helpers + * intel_microcode.h: document intel_ucode_status_t sources + +------------------------------------------------------------------- Old: ---- iucode-tool-2.2.tar.xz New: ---- iucode-tool-2.3.tar.xz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ iucode-tool.spec ++++++ --- /var/tmp/diff_new_pack.fTM8KJ/_old 2018-02-02 22:23:34.802240354 +0100 +++ /var/tmp/diff_new_pack.fTM8KJ/_new 2018-02-02 22:23:34.806240168 +0100 @@ -1,7 +1,7 @@ # # spec file for package iucode-tool # -# Copyright (c) 2017 SUSE LINUX GmbH, Nuernberg, Germany. +# Copyright (c) 2018 SUSE LINUX GmbH, Nuernberg, Germany. # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -17,12 +17,12 @@ Name: iucode-tool -Version: 2.2 +Version: 2.3 Release: 0 Summary: A program to manipulate Intel microcode update collections License: GPL-2.0 Group: System/Boot -Url: https://gitlab.com/iucode-tool/iucode-tool +URL: https://gitlab.com/iucode-tool/iucode-tool Source: %{name}-%{version}.tar.xz BuildRequires: autoconf BuildRequires: automake ++++++ _service ++++++ --- /var/tmp/diff_new_pack.fTM8KJ/_old 2018-02-02 22:23:34.834238861 +0100 +++ /var/tmp/diff_new_pack.fTM8KJ/_new 2018-02-02 22:23:34.834238861 +0100 @@ -4,8 +4,8 @@ <param name="scm">git</param> <param name="changesgenerate">enable</param> <param name="filename">iucode-tool</param> - <param name="revision">refs/tags/v2.2</param> - <param name="version">2.2</param> + <param name="revision">refs/tags/v2.3</param> + <param name="version">2.3</param> </service> <service mode="disabled" name="recompress"> <param name="file">*.tar</param> ++++++ _servicedata ++++++ --- /var/tmp/diff_new_pack.fTM8KJ/_old 2018-02-02 22:23:34.854237928 +0100 +++ /var/tmp/diff_new_pack.fTM8KJ/_new 2018-02-02 22:23:34.854237928 +0100 @@ -1,4 +1,4 @@ <servicedata> <service name="tar_scm"> <param name="url">https://gitlab.com/iucode-tool/iucode-tool.git</param> - <param name="changesrevision">3d05f5b913703893795fe04338285444b8e7ebe1</param></service></servicedata> \ No newline at end of file + <param name="changesrevision">327f21f412183ef8f29e8b0c3c8c44d841c63fc9</param></service></servicedata> \ No newline at end of file ++++++ iucode-tool-2.2.tar.xz -> iucode-tool-2.3.tar.xz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/iucode-tool-2.2/ChangeLog new/iucode-tool-2.3/ChangeLog --- old/iucode-tool-2.2/ChangeLog 2017-08-28 16:40:04.000000000 +0200 +++ new/iucode-tool-2.3/ChangeLog 2018-01-28 16:00:44.000000000 +0100 @@ -1,3 +1,45 @@ +2018-01-28, iucode_tool v2.3 + + * iucode_tool(8): document changes to ucode filtering + * iucode_tool: support selecting by ucode revision + Add a third (and optional) parameter to microcode selection filters, to + select microcodes by revision. The revision can be prefixed by the + operators eq: (equal to), lt: (less than), or gt: (greater than). + The revision numbering is signed, but in order to be more user friendly, + since we display revisions as unsigned values in hex, we accept the + range -INT32_MAX to +UINT32_MAX, and convert it to int32_t. + * iucode_tool: add function to parse signed 32-bit integers + Add parse_s32e(), based on parse_u32(). It will be used to parse + microcode revisions in the command line, so it has an extension + that accepts something like 0xfffffffe as an alias for -2. + * iucode_tool: optimize detection of base10 numeric names + * iucode_tool: better handle offline/non-continuous topology + * iucode_tool(8): document changes to --scan-system + * iucode_tool: select scan-system strategy change at runtime + Instead of selecting the scan-system strategy at compile time, enhance the + long-version of the --scan-system option to take an optional argument, and + select the strategy. Available strategies are: 0 (auto), 1 (fast), and 2 + (exact). Fast uses just the cpuid instruction and activates all steppings. + Exact will query all processors using the kernel cpuid driver. Auto (the + default) is currently the same as fast. The short option -S is equivalent + to --scan-system=auto. This way, we don't break backwards command line + behavior, and something like "iucode_tool -Sl" will still work. In + --scan-system=exact mode, when a /dev/cpu/#/cpuid scan fails, it will use + the result from the cpuid instruction and also add every other stepping for + any signatures found before the failure. + * gitignore: rearrange, and ignore backup and vim swap files + * iucode_tool: move scan_system_processor() one layer down + * iucode_tool: do not scan-system while parsing + Instead of processing -s and -S/--scan-system while parsing, queue all + filters so that we can call scan_system_processors() later. This was the + only complex operation that was being carried out while parsing the command + line. This change ensures that global options such as -q and -v, that are + not supposed to be sensitive to their position in the command line, will + work as expected. + * iucode_tool: add two command-line parser helpers + * intel_microcode.h: document intel_ucode_status_t sources + * update copyright dates to 2018 + 2017-08-28, iucode_tool v2.2 * README: update for mixed dat and bin Intel releases diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/iucode-tool-2.2/NEWS new/iucode-tool-2.3/NEWS --- old/iucode-tool-2.2/NEWS 2017-08-28 16:40:04.000000000 +0200 +++ new/iucode-tool-2.3/NEWS 2018-01-28 16:00:44.000000000 +0100 @@ -1,3 +1,12 @@ +v2.3: + * Processor signature scan strategies can now be selected at + runtime, using a new optional argument of the --scan-system + option. It is possible to disable the "exact" scan strategy + (which uses the kernel cpuid device) at build time to reduce the + impact on executable size. + * Microcode updates for a specific signature can now be optionally + selected based on their revision, not just processor flags mask. + v2.2: * build infrastructure changes: autoconf 2.69 or later, and automake 1.13 or later are now required. The configure script diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/iucode-tool-2.2/README new/iucode-tool-2.3/README --- old/iucode-tool-2.2/README 2017-08-28 16:40:04.000000000 +0200 +++ new/iucode-tool-2.3/README 2018-01-28 16:00:44.000000000 +0100 @@ -2,8 +2,8 @@ iucode_tool - IntelĀ® 64 and IA-32 processor microcode tool - Version 2.2 - August 28th, 2017 + Version 2.3 + January 28th, 2018 https://gitlab.com/iucode-tool diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/iucode-tool-2.2/configure.ac new/iucode-tool-2.3/configure.ac --- old/iucode-tool-2.2/configure.ac 2017-08-28 16:40:04.000000000 +0200 +++ new/iucode-tool-2.3/configure.ac 2018-01-28 16:00:44.000000000 +0100 @@ -1,6 +1,6 @@ dnl Process this file with autoconf 2.69+ to produce a configure script. dnl -dnl Copyright (c) 2010-2017 Henrique de Moraes Holschuh +dnl Copyright (c) 2010-2018 Henrique de Moraes Holschuh dnl dnl This program is free software; you can redistribute it and/or modify dnl it under the terms of the GNU General Public License as published by @@ -17,11 +17,11 @@ dnl Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA AC_PREREQ([2.69]) -AC_INIT([iucode_tool], [2.2], +AC_INIT([iucode_tool], [2.3], [https://gitlab.com/iucode-tool/iucode-tool/issues], [iucode-tool], [https://gitlab.com/iucode-tool/iucode-tool/wikis/home]) -AC_COPYRIGHT([Copyright (c) 2010-2017 Henrique de Moraes Holschuh]) +AC_COPYRIGHT([Copyright (c) 2010-2018 Henrique de Moraes Holschuh]) AC_CONFIG_SRCDIR([iucode_tool.c]) AC_CANONICAL_HOST @@ -128,23 +128,32 @@ [Path to the kernel microcode firmware directory]) AC_SUBST(MICROCODE_DIR_DEFAULT) +AC_ARG_WITH([cpuid-device-parent], + [AS_HELP_STRING([--with-cpuid-device-parent=PATH], + [per-cpu devices parent directory (/dev/cpu)])], + [AS_IF([test "x$withval" = "x" || test "x$withval" = "xno"], + [AC_ERROR([use --disable-cpuid-device instead of --without-cpuid-device-parent])], + [CPUID_DEVICE_PARENT="$withval"])], + [CPUID_DEVICE_PARENT="/dev/cpu"]) +AC_DEFINE_UNQUOTED(CPUID_DEVICE_PARENT, "$CPUID_DEVICE_PARENT", + [path to the per-cpu tree of cpuid devices]) +AC_SUBST(CPUID_DEVICE_PARENT) AC_ARG_WITH([cpuid-device-base], [AS_HELP_STRING([--with-cpuid-device-base=PATH_FORMAT], - [per-cpu cpuid device path (/dev/cpu/%u/cpuid)])], + [per-cpu cpuid device format string, relative to CPUID_DEVICE_PARENT (%s/cpuid)])], [AS_IF([test "x$withval" = "x" || test "x$withval" = "xno"], [AC_ERROR([use --disable-cpuid-device instead of --without-cpuid-device-base])], [CPUID_DEVICE_BASE="$withval"])], - [CPUID_DEVICE_BASE="/dev/cpu/%u/cpuid"]) + [CPUID_DEVICE_BASE="%s/cpuid"]) AC_DEFINE_UNQUOTED(CPUID_DEVICE_BASE, "$CPUID_DEVICE_BASE", - [fprintf base string to the per-cpu cpuid device]) + [snprintf format string for the per-cpu cpuid device path, relative to CPUID_DEVICE_NAME]) AC_SUBST(CPUID_DEVICE_BASE) AC_ARG_ENABLE([cpuid-device], - [AS_HELP_STRING([--enable-cpuid-device], - [use Linux cpuid device and check all cores])], - [AS_IF([test "x$enableval" != "xno"], - [AC_DEFINE(USE_CPUID_DEVICE, [], [Scan every core using Linux cpuid device])]) - ]) + [AS_HELP_STRING([--disable-cpuid-device], + [disable support for the Linux cpuid device (cripples --scan-system=exact)])]) +AS_IF([test "x${enable_cpuid_device}" != "xno"], + [AC_DEFINE(USE_CPUID_DEVICE, [], [Support scanning every core using Linux cpuid device])]) AC_ARG_ENABLE([valgrind-build], [AS_HELP_STRING([--enable-valgrind-build], diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/iucode-tool-2.2/intel_microcode.c new/iucode-tool-2.3/intel_microcode.c --- old/iucode-tool-2.2/intel_microcode.c 2017-08-28 16:40:04.000000000 +0200 +++ new/iucode-tool-2.3/intel_microcode.c 2018-01-28 16:00:44.000000000 +0100 @@ -3,7 +3,7 @@ * * Copyright (c) 2000-2006 Tigran Aivazian <[email protected]> * 2006 Shaohua Li <[email protected]> - * 2010-2017 Henrique de Moraes Holschuh <[email protected]> + * 2010-2018 Henrique de Moraes Holschuh <[email protected]> * * Based on Linux kernel Intel Microcode driver v2.6.36-rc3 (1.14) * Based on Linux microcode.ctl version 1.17 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/iucode-tool-2.2/intel_microcode.h new/iucode-tool-2.3/intel_microcode.h --- old/iucode-tool-2.2/intel_microcode.h 2017-08-28 16:40:04.000000000 +0200 +++ new/iucode-tool-2.3/intel_microcode.h 2018-01-28 16:00:44.000000000 +0100 @@ -3,7 +3,7 @@ * * Copyright (c) 2000-2006 Tigran Aivazian <[email protected]> * 2006 Shaohua Li <[email protected]> - * 2010-2017 Henrique de Moraes Holschuh <[email protected]> + * 2010-2018 Henrique de Moraes Holschuh <[email protected]> * * Based on Linux kernel Intel Microcode driver v2.6.36-rc3 (1.14) * Based on Linux microcode.ctl version 1.17 @@ -35,12 +35,14 @@ INTEL_UCODE_BAD_PARAMETERS, INTEL_UCODE_INVALID_DATA, INTEL_UCODE_UNKNOWN_FORMAT, - INTEL_UCODE_COUNTEROVERFLOW, + /* only returned by intel_ucode_check_microcode() */ INTEL_UCODE_BAD_EXTENDED_TABLE, INTEL_UCODE_BAD_EXTENDED_TABLE_CHECKSUM, INTEL_UCODE_BAD_EXTENDED_SIG_CHECKSUM, INTEL_UCODE_BAD_CHECKSUM, + /* only returned by the foreach functions */ INTEL_UCODE_CALLBACK_ERROR, + INTEL_UCODE_COUNTEROVERFLOW, } intel_ucode_status_t; struct intel_ucode_metadata { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/iucode-tool-2.2/iucode_tool.8.in new/iucode-tool-2.3/iucode_tool.8.in --- old/iucode-tool-2.2/iucode_tool.8.in 2017-08-28 16:40:04.000000000 +0200 +++ new/iucode-tool-2.3/iucode_tool.8.in 2018-01-28 16:00:44.000000000 +0100 @@ -1,5 +1,5 @@ .\" hey, Emacs: -*- nroff -*- -.\" Copyright (c) 2010-2017 Henrique de Moraes Holschuh <[email protected]> +.\" Copyright (c) 2010-2018 Henrique de Moraes Holschuh <[email protected]> .\" .\" iucode_tool is free software; you can redistribute it and/or modify .\" it under the terms of the GNU General Public License as published by @@ -15,7 +15,7 @@ .\" along with this program; see the file COPYING. If not, write to .\" the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. .\" -.TH IUCODE_TOOL 8 "2016-11-10" "IUCODE_TOOL @VERSION@" "iucode_tool manual" +.TH IUCODE_TOOL 8 "2018-01-28" "IUCODE_TOOL @VERSION@" "iucode_tool manual" .\" Please update the above date whenever this man page is modified. .\" .\" Some roff macros, for reference: @@ -179,11 +179,20 @@ .TP -.BI "\-s ! | [!]" signature "[," pf_mask "]" -Select microcodes by the specified \fIsignature\fP and \fIprocessor flags -mask\fP (\fIpf_mask\fP). If the \fIprocessor flags mask\fP is specified, it will -select only microcodes that are suitable for at least one of the processor flag -combinations present in the mask. +.BI "\-s ! | [!]" signature "[,[" pf_mask "][,[" lt: "|" eq: "|" gt: "]" revision "]]" +Select microcodes by the specified \fIsignature\fP, \fIprocessor flags mask\fP +(\fIpf_mask\fP), and \fIrevision\fP. + +If the \fIprocessor flags mask\fP is specified, it will select only microcodes +that are suitable for at least one of the processor flag combinations present +in the mask. + +If the \fIrevision\fP is specified, optionally prefixed by one of the +\(lq\fIeq:\fP\(rq, \(lq\fIlt:\fP\(rq or \(lq\fIgt:\fP\(rq operators, it will +select only microcodes that have that same \fIrevision\fP (if no operator, or +if the \(lq\fIeq:\fP\(rq operator is used), or microcodes that have a +\fIrevision\fP that is less than (\(lq\fIlt:\fP\(rq operator), or greater than +(\(lq\fIgt:\fP\(rq operator), the one specified. Specify more than once to select more microcodes. This option can be combined with the \fI\-\-scan\-system\fP option to select more microcodes. If @@ -201,7 +210,7 @@ the non\-negated form of \fI\-s\fP, or using \fI\-\-scan\-system\fP). .TP -.BR "\-S" ", " "\-\-scan\-system" +.BR "\-S" ", " "\-\-scan\-system" "[=\fImode\fP]" Select microcodes by scanning online processors on this system for their signatures. @@ -210,10 +219,26 @@ \fI\-\-scan\-system\fP can also be deselected by a later \fI\-s\ !signature\fP option. -Should the signature scan fail, the program will print a warning to the user -and continue as if \fI\-\-scan\-system\fP had not been specified. This is a -fail-safe condition when \fBiucode_tool\fP is used to install microcode updates -for the next boot. +The optional \fImode\fP argument (accepted only by the long version of the +option) selects the strategy used to scan processors: +.RS +.IP "\fB0\fP or \fBauto\fP" +Currently the same as \fBfast\fP, but this might change in future versions if +Intel ever deploys multi-signature systems that go beyond mixed-stepping. This +is the default mode of operation, for backwards compatibility +with previous versions of \fBiucode_tool\fP. +.IP "\fB1\fP or \fBfast\fP" +Uses the cpuid instruction to detect the signature of the processor +\fBiucode_tool\fP is running on, and selects all steppings for that processor's +type, family and model. Supports mixed-stepping systems. +.IP "\fB2\fP or \fBexact\fP" +Uses kernel drivers to scan the signature of every online processor directly. +This mode supports multi-signature systems. This scan mode will be slow on +large systems with many processors, and likely requires special permissions +(such as running as the root user). Should the scan fail for any reason, as +a fail-safe measure, it will issue an warning and consider all possible +steppings for every signature it did manage to scan successfully. +.RE .TP \fB\-\-date\-before\fR=\fIYYYY\-MM\-DD\fR and \fB\-\-date\-after\fR=\fIYYYY\-MM\-DD\fR diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/iucode-tool-2.2/iucode_tool.c new/iucode-tool-2.3/iucode_tool.c --- old/iucode-tool-2.2/iucode_tool.c 2017-08-28 16:40:04.000000000 +0200 +++ new/iucode-tool-2.3/iucode_tool.c 2018-01-28 16:00:44.000000000 +0100 @@ -1,7 +1,7 @@ /* * iucode_tool - Manipulates Intel(R) IA32/x86_64 processor microcode bundles * - * Copyright (c) 2010-2017 Henrique de Moraes Holschuh <[email protected]> + * Copyright (c) 2010-2018 Henrique de Moraes Holschuh <[email protected]> * 2000 Simon Trimmer, Tigran Aivazian * * Some code copied from microcode.ctl v1.17. @@ -177,12 +177,33 @@ static struct microcode_iterator_data microcode_iterator_data; /* Filter masks */ +#define IUCODE_FILTERMASK_SCANCPUS 0xffffffffU + +enum iuc_rev_match_mode { + IUCODE_REVFLT_ANY = 0, /* Ignore revision on match */ + IUCODE_REVFLT_EQ, /* Revision must be = filter's rev */ + IUCODE_REVFLT_LT, /* Revision must be < filter's rev */ + IUCODE_REVFLT_GT, /* Revision must be > filter's rev */ + + IUCODE_REVFLT_SIZE /* EOL */ +}; +static const char * const iuc_rev_match_mode_s[IUCODE_REVFLT_SIZE] = { + [IUCODE_REVFLT_ANY] = NULL, + [IUCODE_REVFLT_EQ] = "eq:", + [IUCODE_REVFLT_LT] = "lt:", + [IUCODE_REVFLT_GT] = "gt:", +}; /* keep this in sync with enum iuc_rev_match_mode !! */ + struct microcode_filter_entry { struct microcode_filter_entry *next; uint32_t cpuid; /* exact match */ uint32_t pfm; /* common bits set match */ - int invert; + int32_t rev; + enum iuc_rev_match_mode rev_match; + int invert; }; +static struct microcode_filter_entry *uc_filter_queue = NULL; +static struct microcode_filter_entry *uc_filter_queue_tail = NULL; static struct microcode_filter_entry *uc_filter_list = NULL; static int filter_list_allow = 1; @@ -247,6 +268,31 @@ return 0; } +static int parse_s32e(const char *nptr, char **endptr, int base, + int32_t * const res) +{ + long int l; + + assert(nptr); + assert(endptr); + assert(res); + + errno = 0; + l = strtol(nptr, endptr, base); + if (errno || nptr == *endptr) + return errno ? errno : EINVAL; + if (l > UINT32_MAX || l < INT32_MIN) + return ERANGE; + + /* + * Accept UINT32_MAX >= x > INT32_MAX, as if it were + * the binary representation of int32_t read as uint32_t + */ + *res = (int32_t)(l & UINT32_MAX); + + return 0; +} + static int is_valid_fd(const int fd) { return fcntl(fd, F_GETFD) != -1 || errno != EBADF; @@ -1898,7 +1944,6 @@ return !!((d > datefilter_min) && (d < datefilter_max)); } -static void free_filter_list(struct microcode_filter_entry *f) __attribute__((unused)); static void free_filter_list(struct microcode_filter_entry *f) { static struct microcode_filter_entry *p; @@ -1910,9 +1955,15 @@ } } -static void xx_merge_filter(struct microcode_filter_entry * const f, - const uint32_t pf_mask, const int invert) -{ +static int xx_merge_filter(struct microcode_filter_entry * const f, + const uint32_t pf_mask, const int32_t rev, + const enum iuc_rev_match_mode rev_match, + const int invert) +{ + /* FIXME: we could do better, sometimes it is possible to merge */ + if (f->rev_match != rev_match || f->rev != rev) + return 0; /* can't merge */ + if (f->invert == invert) { f->pfm |= pf_mask; } else if (!(f->invert)) { @@ -1921,15 +1972,21 @@ f->invert = 0; f->pfm = pf_mask; } + + return 1; /* merged */ } -static int add_filter_to_list(uint32_t cpuid, uint32_t pf_mask, int invert, - struct microcode_filter_entry ** const base) +static int add_filter_to_list(uint32_t cpuid, uint32_t pf_mask, int32_t rev, + enum iuc_rev_match_mode rev_match, int invert, + struct microcode_filter_entry ** const base) { struct microcode_filter_entry **pnext, *n; assert(base); + if (!cpuid || cpuid == IUCODE_FILTERMASK_SCANCPUS) + return EINVAL; + if (!pf_mask) pf_mask = PFMASK_MATCH_ANY; @@ -1940,13 +1997,16 @@ if (*pnext && (*pnext)->cpuid == cpuid) { if (((pf_mask & (*pnext)->pfm) == pf_mask) && - ((*pnext)->invert == invert)) { + (*pnext)->rev_match == rev_match && + (*pnext)->rev == rev && + (*pnext)->invert == invert) { /* found equivalent, report it */ return EEXIST; } - xx_merge_filter(*pnext, pf_mask, invert); - return 0; + /* merge when possible */ + if (xx_merge_filter(*pnext, pf_mask, rev, rev_match, invert)) + return 0; } /* insert before */ @@ -1955,6 +2015,8 @@ return ENOMEM; n->cpuid = cpuid; n->pfm = pf_mask; + n->rev = rev; + n->rev_match = rev_match; n->invert = invert; n->next = *pnext; *pnext = n; @@ -1982,34 +2044,51 @@ while (*pp && (*pp)->cpuid < e->cpuid) pp = &((*pp)->next); - if (*pp && (*pp)->cpuid == e->cpuid) { - xx_merge_filter(*pp, e->pfm, e->invert); - } else { + if (!*pp || (*pp)->cpuid != e->cpuid || + !xx_merge_filter(*pp, e->pfm, e->rev, e->rev_match, e->invert)) { /* insert before */ e->next = *pp; *pp = e; e = NULL; } - if (e) - free(e); + free(e); + } +} + +static int xx_compare_rev(const int32_t rev, + const struct microcode_filter_entry *f) +{ + const enum iuc_rev_match_mode op = f->rev_match; + + if ((op == IUCODE_REVFLT_ANY) || + (op == IUCODE_REVFLT_EQ && rev == f->rev) || + (op == IUCODE_REVFLT_LT && rev < f->rev) || + (op == IUCODE_REVFLT_GT && rev > f->rev)) { + return !(f->invert); } + + return -1; } /* returns non-zero if selected */ static int is_selected(const uint32_t cpuid, const uint32_t pf_mask, + const int32_t rev, const struct microcode_filter_entry *f) { + int state = -1; + while (f && f->cpuid < cpuid) f = f->next; - while (f && f->cpuid == cpuid) { + while (f && f->cpuid == cpuid && state < 0) { if ((pf_mask & f->pfm) != 0 || - f->pfm == PFMASK_MATCH_ANY) - return !(f->invert); + f->pfm == PFMASK_MATCH_ANY) { + state = xx_compare_rev(rev, f); + } f = f->next; } - return filter_list_allow; + return (state >= 0)? state : filter_list_allow; } /* Microcode extended signature deduplication */ @@ -2230,7 +2309,7 @@ INTEL_UCLE_HASXST; extsig_tables_in_use = 1; } - if (is_selected(cpuid, pf_mask, uc_filter_list)) + if (is_selected(cpuid, pf_mask, m.revision, uc_filter_list)) uce_flags |= (is_in_date_range(&m)) ? INTEL_UCLE_SELECT : INTEL_UCLE_SELID; @@ -2640,7 +2719,7 @@ * Get main processor signature from cpuid(1) EAX * and add it as a filter. */ - switch (add_filter_to_list(sig, 0, 0, ucfp)) { + switch (add_filter_to_list(sig, 0, 0, IUCODE_REVFLT_ANY, 0, ucfp)) { case ENOMEM: print_err("out of memory"); return ENOMEM; @@ -2656,8 +2735,22 @@ } #ifdef USE_CPUID_DEVICE -static int check_cpuid_devs(__attribute__((unused)) const uint32_t sig, - struct microcode_filter_entry ** const ucfp) +static int is_base10_number(const char *s) +{ + if (!s || !*s) + return 0; + + while(*s) { + if (*s < '0' || *s > '9') + return 0; + s++; + } + + return 1; +} + +/* requires a kernel driver, and *really* hurts on very large systems */ +static int xx_check_cpuid_devs(struct microcode_filter_entry ** const ucfp) { uint32_t cpuid_buf[8]; /* two cpuid levels */ char cpuid_device[PATH_MAX]; @@ -2665,27 +2758,57 @@ int rc = 0; unsigned int i = 0; unsigned int ncpu = 0; + struct dirent *d; + + DIR *cpudir = opendir(CPUID_DEVICE_PARENT); + if (!cpudir) { + int en = errno; + print_err("%s: could not open directory: %s", CPUID_DEVICE_PARENT, strerror(en)); + return -1; + } while (1) { + errno = 0; + d = readdir(cpudir); + if (!d) { + int err = errno; + if (unlikely(err)) { + print_err("%s: cannot walk directory: %s", + CPUID_DEVICE_PARENT, strerror(err)); + rc = -1; /* note that we skipped processors due to unexpected errors */ + } + break; /* finish/abort walk */ + } + + /* Linux procfs supports d_type */ + if (d->d_type != DT_DIR) + continue; /* next dentry */ + + /* must be [0-9]+, no trailling weirdness */ + if (!is_base10_number(d->d_name)) + continue; /* next dentry */ + snprintf(cpuid_device, sizeof(cpuid_device), - CPUID_DEVICE_BASE, i); - cpuid_fd = open(cpuid_device, O_RDONLY); + CPUID_DEVICE_BASE, d->d_name); + + errno = EINTR; + cpuid_fd = -1; + while (cpuid_fd == -1 && errno == EINTR) + cpuid_fd = openat(dirfd(cpudir), cpuid_device, O_RDONLY | O_CLOEXEC); if (cpuid_fd == -1) { int en = errno; - print_msg(4, "%s: returned error status on open(): %s", - cpuid_device, strerror(en)); + print_msg(4, "%s/%s: returned error status on open(): %s", + CPUID_DEVICE_PARENT, cpuid_device, strerror(en)); - if (en == EINTR) - continue; /* try again */ if (en == ENOENT) break; /* cpuid device inode not found */ if (en == ENXIO || en == EIO) { /* Linux cpuid driver: ENXIO: offline; EIO: no cpuid support */ print_msg(2, "processor %u is offline or has no cpuid support", i); } else { - print_msg(2, "%s: cannot open cpuid device node: %s", - cpuid_device, strerror(en)); + print_msg(2, "%s/%s: cannot open cpuid device node: %s", + CPUID_DEVICE_PARENT, cpuid_device, strerror(en)); rc = -1; /* note that we skipped processors due to unexpected errors */ } @@ -2694,13 +2817,14 @@ continue; } - print_msg(3, "trying to get CPUID information from %s", - cpuid_device); + print_msg(3, "trying to get CPUID information from %s/%s", + CPUID_DEVICE_PARENT, cpuid_device); if (read(cpuid_fd, &cpuid_buf, sizeof(cpuid_buf)) == -1) { - print_err("%s: access to CPUID(0) and CPUID(1) failed: %s", - cpuid_device, strerror(errno)); + print_err("%s/%s: access to CPUID(0) and CPUID(1) failed: %s", + CPUID_DEVICE_PARENT, cpuid_device, strerror(errno)); /* this is in the should not happen list, so abort */ close(cpuid_fd); + closedir(cpudir); return 1; } @@ -2716,6 +2840,8 @@ i++; }; + closedir(cpudir); + if (i == 0 && ncpu == 0) { print_err("cpuid kernel driver unavailable"); return -1; @@ -2732,16 +2858,22 @@ return rc; } #else +static int xx_check_cpuid_devs(__attribute__((unused)) struct microcode_filter_entry ** const ucfp) +{ + print_msg(1, "support for exact system scan disabled at compile time"); + return -1; +} +#endif /* USE_CPUID_DEVICE */ -/* this hurts a lot less on very big systems... */ -static int check_cpuid_devs(uint32_t sig, struct microcode_filter_entry ** const ucfp) + +/* xx_add_all_steppings(cpuid) hurts a lot less on very big systems... */ +static int xx_add_all_steppings(uint32_t sig, struct microcode_filter_entry ** const ucfp) { unsigned int i; - print_msg(2, "assuming all processors have the same type, family and model"); sig |= 0xf; for (i = 0; i < 0x10; i++) { - if (add_filter_to_list(sig, 0, 0, ucfp) == ENOMEM) { + if (add_filter_to_list(sig, 0, 0, IUCODE_REVFLT_ANY, 0, ucfp) == ENOMEM) { print_err("out of memory"); return ENOMEM; } @@ -2749,14 +2881,46 @@ } return 0; } -#endif -static int scan_system_processors(void) +/* Handle mixed-signature systems, even if Intel still hasn't admited that + * they will exist in the SDM, the writing is already out in the wall */ +static int xx_add_all_steppings_for_every_sig(uint32_t sig, struct microcode_filter_entry ** const ucfp) +{ + struct microcode_filter_entry *fl = NULL; + struct microcode_filter_entry *p; + int rc; + + /* failsafe of the failsafe: add running processor sig */ + rc = xx_add_all_steppings(sig, &fl); + + /* creating a new list is just plain safer in the long run */ + p = *ucfp; + while (p && !rc) { + /* handle mixed sig systems, unlikely as that might be */ + if (p->cpuid != sig) + rc = xx_add_all_steppings(p->cpuid, &fl); + p = p->next; + } + + if (!rc) { + free_filter_list(*ucfp); + *ucfp = fl; + } else { + free_filter_list(fl); + } + + return rc; +} + +static int scan_system_processors(unsigned int strategy, + struct microcode_filter_entry ** const filter_list) { uint32_t id0, id1, id2, id3, sig, idx; struct microcode_filter_entry *uc_cpu = NULL; int rc = 0; + assert(filter_list); + print_msg(3, "trying to get CPUID information directly"); if (!(__get_cpuid(0, &id0, &id1, &id2, &id3) && __get_cpuid(1, &sig, &idx, &idx, &idx))) { @@ -2775,7 +2939,15 @@ switch (xx_scan_handle_sig(id1, id2, id3, sig, &uc_cpu)) { case 0: - rc = check_cpuid_devs(sig, &uc_cpu); + if (strategy == 2) { + if (xx_check_cpuid_devs(&uc_cpu)) { + print_warn("exact cpuid signature scan failed, switching to failsafe strategy"); + rc = xx_add_all_steppings_for_every_sig(sig, &uc_cpu); + } + } else { + print_msg(2, "assuming all processors have the same type, family and model"); + rc = xx_add_all_steppings(sig, &uc_cpu); + } break; case ENXIO: print_msg(1, "running on a non-Intel processor"); @@ -2792,18 +2964,81 @@ if (uc_cpu) { /* tie linked lists */ - add_filter_list_to_list(&uc_filter_list, uc_cpu); + add_filter_list_to_list(filter_list, uc_cpu); uc_cpu = NULL; } return (rc > 0) ? rc : 0; } +static int cmdline_queue_ucode_filter(uint32_t cpuid, uint32_t pf_mask, + int32_t rev, enum iuc_rev_match_mode rev_match, + int invert) +{ + struct microcode_filter_entry *n; + + n = malloc(sizeof(struct microcode_filter_entry)); + if (!n) + return ENOMEM; + n->cpuid = cpuid; + n->pfm = pf_mask; + n->rev = rev; + n->rev_match = rev_match; + n->invert = invert; + n->next = NULL; + + if (uc_filter_queue_tail) { + uc_filter_queue_tail->next = n; + } else { + uc_filter_queue = n; + } + + uc_filter_queue_tail = n; + + return 0; +} + +static int process_ucode_filter_queue(void) +{ + struct microcode_filter_entry *p = uc_filter_queue; + int rc = 0; + + while(p && !rc) { + if (p->cpuid != IUCODE_FILTERMASK_SCANCPUS) { + rc = add_filter_to_list(p->cpuid, p->pfm, p->rev, p->rev_match, p->invert, &uc_filter_list); + if (rc == EEXIST) + rc = 0; + } else { + rc = scan_system_processors(p->pfm, &uc_filter_list); + } + + p = p->next; + } + + uc_filter_queue_tail = NULL; + free_filter_list(uc_filter_queue); + uc_filter_queue = NULL; + + switch(rc) { + case 0: + return 0; + + case ENOMEM: + print_err("Cannot add filter entry: out of memory"); + break; + case EINVAL: + print_err("Internal error while processing filter list"); + break; + } + + return 1; +} + /* Command line processing */ static const char program_version[] = PROGNAME " " VERSION "\n" - "Copyright (c) 2010-2017 by Henrique de Moraes Holschuh\n\n" + "Copyright (c) 2010-2018 by Henrique de Moraes Holschuh\n\n" "Based on code from the Linux microcode_intel driver and from\n" "the microcode.ctl package, copyright (c) 2000 by Simon Trimmer\n" @@ -2851,6 +3086,7 @@ IUCODE_ARGP_DATEAFTER, IUCODE_ARGP_DATEFSTRICT, IUCODE_ARGP_DATEFLOOSE, + IUCODE_ARGP_SCANSYSTEMOPT, IUCODE_ARGP_EIRFS, IUCODE_ARGP_MINSIZE_EIRFS, IUCODE_ARGP_DFLSIZE_EIRFS, @@ -2870,19 +3106,29 @@ "selected by filename suffix)", 10 }, - { NULL, 's', "! | [!]signature[,pf_mask]", 0, - "Select microcodes by the specified signature and processor " - "flags mask (pf_mask). Specify more than once to select/unselect " - "more microcodes. Prefix with ! to unselect microcodes. " + { NULL, 's', "! | [!]signature[,[pf_mask][,[lt:|eq:|gt:]revision]]", 0, + "Select microcodes by the specified signature, processor " + "flags mask (pf_mask), and revision. Optionally, prefix revision " + "with eq: (equal -- implied there is no prefix), lt: (less than) " + "or gt: (greater than). " + "Specify more than once to select/unselect more microcodes. " + "Prefix with ! to unselect microcodes. " "Use -s ! to disable the default behavior of selecting all " "microcodes when no -s or -S filter is specified", 20 }, - { "scan-system", 'S', NULL, 0, + { NULL, 'S', NULL, 0, + "Same as --scan-system=auto", + 21 }, + { "scan-system", IUCODE_ARGP_SCANSYSTEMOPT, "mode", OPTION_ARG_OPTIONAL, "Select microcodes based on the running system processor(s). " "Can be combined with the -s option, and can be used only once. " "Microcodes selected by --scan-system can be unselected by a " - "later -s !<signature> option", - 20 }, + "later -s !<signature> option. The optional mode argument " + "selects the strategy: 0 or auto (default); 1 or fast (good for " + "most systems, including mixed-stepping); and 2 or exact (slow, " + "supports multi-signature systems, requires the cpuid kernel " + "driver and might require root access)", + 22 }, { "downgrade", IUCODE_ARGP_DOWNGRADE, NULL, 0, "Instead of discarding microcodes based on revision level, " @@ -2976,6 +3222,8 @@ }; static const char cmdline_nonarg_doc[] = "[[-t<type>] filename] ..."; +static const char * const cmdline_scan_system_tbl[] = { "auto", "fast", "exact", NULL }; + static int new_filename(const char * const fn, const intel_ucode_file_type_t ftype) { @@ -3022,15 +3270,17 @@ input_files = NULL; } -/* -s ! | [!]cpuid[,pf_mask] */ -static int add_ucode_filter(const char *arg) +/* -s ! | [!]cpuid[,[pf_mask][,[<revmatch_operator>]rev]] */ +static int cmdline_parse_ucode_filter(const char *arg) { char *p; uint32_t acpuid, amask; - int invert; + int32_t arev; + int invert, arev_match; amask = 0; invert = 0; + arev = 0; while (isspace(*arg)) arg++; @@ -3051,28 +3301,65 @@ } } + /* cpuid, mandatory */ if (!*arg || parse_u32(arg, &p, 0, &acpuid)) return EINVAL; while (isspace(*p)) p++; + /* pf_mask, optional */ if (*p == ',') { p++; + if (*p != ',') { + arg = p; + if (!*arg || parse_u32(arg, &p, 0, &amask)) + return EINVAL; + } /* else amask = MATCH_ANY */ + while (isspace(*p)) + p++; + } else if (*p) { + return EINVAL; + } + + /* rev, optional */ + if (*p == ',') { + p++; + while (isspace(*p)) + p++; + if (!*p) + return EINVAL; + + /* revison match operator, optional */ + int i = 0; + while (i < IUCODE_REVFLT_SIZE && + (!iuc_rev_match_mode_s[i] || + strncasecmp(p, iuc_rev_match_mode_s[i], + strlen(iuc_rev_match_mode_s[i])))) + i++; + if (i < IUCODE_REVFLT_SIZE) { + arev_match = i; + p += strlen(iuc_rev_match_mode_s[i]); + } else { + arev_match = IUCODE_REVFLT_EQ; + } + + /* revision */ arg = p; - if (!*arg || parse_u32(arg, &p, 0, &amask)) + if (!*arg || parse_s32e(arg, &p, 0, &arev)) return EINVAL; while (isspace(*p)) p++; - if (*p) - return EINVAL; - } else if (*p) { + } else { + arev_match = IUCODE_REVFLT_ANY; + } + if (*p) { return EINVAL; } if (!invert) filter_list_allow = 0; - return add_filter_to_list(acpuid, amask, invert, &uc_filter_list); + return cmdline_queue_ucode_filter(acpuid, amask, arev, arev_match, invert); } /* YYYY-MM-DD */ @@ -3114,6 +3401,55 @@ return 0; } +static int cmdline_get_int(const char *arg, int pmin, int pmax, int *d) +{ + char *p; + long int l; + + if (!arg) + return 0; + + errno = 0; + l = strtol(arg, &p, 10); + if (errno || p == arg) + return errno ? errno : EINVAL; + if (l > INT32_MAX || l < INT32_MIN) + return ERANGE; + if (l < pmin || l > pmax) + return EINVAL; + + while (isspace(*p)) + p++; + if (*p) + return EINVAL; + + if (d) + *d = (int) l; + + return 0; +} + +/* note: does not tolerate trailing blanks */ +static int cmdline_get_enumstr(const char *arg, const char * const table[], + int *d) +{ + int r = 0; + + if (!arg || !table) + return 0; + + while (table[r]) { + if (!strcasecmp(arg, table[r])) { + if (d) + *d = r; + return 0; + } + r++; + } + + return ENOENT; +} + static error_t cmdline_do_parse_arg(int key, char *arg, struct argp_state *state) { @@ -3237,29 +3573,61 @@ break; case 's': - rc = add_ucode_filter(arg); + rc = cmdline_parse_ucode_filter(arg); switch (rc) { case 0: - case EEXIST: break; /* success */ case EINVAL: argp_error(state, "invalid filter: '%s'", arg); break; /* not reached */ default: argp_failure(state, EXIT_SWFAILURE, rc, - "could not add filter '%s'", arg); + "could not queue filter '%s'", arg); } command_line_actions |= IUCODE_F_UCSELECT; break; + case 'S': + /* + * -S and --scan-system cannot be handled the same way + * because -S cannot be made OPTION_ARG_OPTIONAL: it would + * break command-line backwards compatibility when people + * specify several short options together and -S is not the + * last option in the chain + */ + if (command_line_actions & IUCODE_DO_SELPROC) + argp_error(state, + "--scan-system option can be specified only once"); + /* + * note: the pfm field of the queue filter item is used to + * encode the scan-system strategy. + */ + rc = cmdline_queue_ucode_filter(IUCODE_FILTERMASK_SCANCPUS, 0, 0, 0, 0); + if (rc) + argp_failure(state, EXIT_SWFAILURE, rc, + "could not queue --scan-system action"); + command_line_actions |= IUCODE_DO_SELPROC | IUCODE_F_UCSELECT; + break; + case IUCODE_ARGP_SCANSYSTEMOPT: if (command_line_actions & IUCODE_DO_SELPROC) argp_error(state, "--scan-system option can be specified only once"); - if (scan_system_processors()) { - argp_failure(state, EXIT_SWFAILURE, 0, - "fatal failure while scanning for system processors"); - } + int scan_system_strategy = 0; + if (cmdline_get_int(arg, 0, 2, &scan_system_strategy) && + cmdline_get_enumstr(arg, cmdline_scan_system_tbl, &scan_system_strategy)) + argp_error(state, "invalid --scan-system mode: '%s'", arg); + + /* + * note: the pfm field of the queue filter item is used to + * encode the scan-system strategy. + */ + rc = cmdline_queue_ucode_filter(IUCODE_FILTERMASK_SCANCPUS, + (uint32_t)scan_system_strategy, + 0, 0, 0); + if (rc) + argp_failure(state, EXIT_SWFAILURE, rc, + "could not queue --scan-system action"); command_line_actions |= IUCODE_DO_SELPROC | IUCODE_F_UCSELECT; break; @@ -3335,6 +3703,11 @@ goto do_nothing; } + if (process_ucode_filter_queue()) { + rc = EXIT_SWFAILURE; + goto err_exit; + } + if (command_line_actions & IUCODE_DO_LOADFILE) { struct filename_list *fn = input_files; while (fn && next_bundle_id) {
