[PATCH 2/2] cpu: microcode: rewrite so we no longer need to load microcode (LP: #1120240)

Alex Hung alex.hung at canonical.com
Thu May 30 02:02:17 UTC 2013


Acked-by: Alex Hung <alex.hung at canonical.com>


On Wed, May 29, 2013 at 10:31 PM, Colin King <colin.king at canonical.com>wrote:

> From: Colin Ian King <colin.king at canonical.com>
>
> Rewrite the microcode test so that we no longer have to load the
> microcode.  The microcode loader fails to work on recent releases
> and re-writing/updating the microcode loader is required to make this
> work again
>
> Instead, just scan the kernel for microcode update messages and
> sanity check the CPU microcode version.
>
> Tested on Lucid through to Suacy releases.
>
> Signed-off-by: Colin Ian King <colin.king at canonical.com>
> ---
>  src/cpu/microcode/microcode.c | 356
> +++++++++++++++++++++++++++---------------
>  1 file changed, 232 insertions(+), 124 deletions(-)
>
> diff --git a/src/cpu/microcode/microcode.c b/src/cpu/microcode/microcode.c
> index 3efed35..d4f448c 100644
> --- a/src/cpu/microcode/microcode.c
> +++ b/src/cpu/microcode/microcode.c
> @@ -2,10 +2,8 @@
>   * Copyright (C) 2006, Intel Corporation
>   * Copyright (C) 2010-2013 Canonical
>   *
> - * This file is derived from part of the Linux-ready Firmware Developer
> Kit
> - *
> - * This was originally from microcode.c from the
> - * Linux Firmware Test Kit.
> + * This file was derived from part of the Linux-ready Firmware Developer
> Kit,
> + * however, it has been completely re-written.
>   *
>   * This program is free software; you can redistribute it and/or
>   * modify it under the terms of the GNU General Public License
> @@ -23,10 +21,6 @@
>   *
>   */
>
> -/*
> - * This test checks if the microcode in the processor has
> - * recent enough microcode loaded.
> - */
>  #include "fwts.h"
>
>  #ifdef FWTS_ARCH_INTEL
> @@ -36,157 +30,270 @@
>  #include <dirent.h>
>  #include <ctype.h>
>
> +#define SYS_CPU_PATH           "/sys/devices/system/cpu"
> +#define UNKNOWN                        -1
> +
>  typedef struct {
> -       char cpu[16];
> -       int version;
> -} fwts_microcode_info;
> +       int cpu;                /* CPU # */
> +       int old_revision;       /* Old microcode revision */
> +       int new_revision;       /* New microcode revision */
> +       int year;               /* Year of new revision */
> +       int month;              /* Month of new revision */
> +       int day;                /* Day of new revision */
> +} microcode_info;
>
> -static void gather_info(fwts_framework *fw, fwts_list *cpus)
> +static fwts_list *klog;
> +
> +static int microcode_init(fwts_framework *fw)
>  {
> -       DIR *dir;
> -       struct dirent *entry;
> -       char path[PATH_MAX];
> -       int warned = 0;
> +       bool intel;
>
> -       if ((dir = opendir("/sys/devices/system/cpu")) == NULL)
> -               return;
> +       if (fwts_cpu_is_Intel(&intel) != FWTS_OK) {
> +               fwts_log_error(fw, "Cannot determine processor type.");
> +               return FWTS_ERROR;
> +       }
>
> -       while ((entry = readdir(dir)) != NULL) {
> -               if (entry &&
> -                   (strlen(entry->d_name)>3) &&
> -                   (strncmp(entry->d_name,"cpu",3) == 0) &&
> -                   (isdigit(entry->d_name[3]))) {
> -                       char *data;
> -                       fwts_microcode_info *cpu;
> +       if (!intel) {
> +               fwts_log_info(fw, "The microcode test currently only
> supports Intel processors.");
> +               return FWTS_SKIP;
> +       }
>
> -                       snprintf(path, sizeof(path),
> -
> "/sys/devices/system/cpu/%s/microcode/version",
> -                               entry->d_name);
> -                       if ((data = fwts_get(path)) == NULL) {
> -                               if (!warned) {
> -                                       fwts_failed(fw, LOG_LEVEL_LOW,
> -                                               "MicrocodeVersion",
> -                                               "The kernel does not
> export microcode version. "
> -                                               "This test needs a
> 2.6.19-rc1 kernel or later to function");
> -                                       warned++;
> -                               }
> -                       } else {
> -                               if ((cpu = (fwts_microcode_info
> *)calloc(1, sizeof(fwts_microcode_info))) == NULL) {
> -                                       fwts_log_error(fw, "Cannot
> allocate memory.");
> -                                       break;
> -                               }
> -                               strncpy(cpu->cpu, entry->d_name,
> sizeof(cpu->cpu) - 1);
> -                               cpu->cpu[sizeof(cpu->cpu) - 1] = '\0';
> -                               cpu->version = strtoul(data, NULL, 16);
> -                               free(data);
> -                               fwts_list_append(cpus, cpu);
> -                       }
> -               }
> +       klog = fwts_klog_read();
> +       if (klog == NULL) {
> +               fwts_log_error(fw, "Cannot read kernel log.");
> +               return FWTS_ERROR;
>         }
> -       closedir(dir);
> +       return FWTS_OK;
>  }
>
> -static void check_info(fwts_framework *fw, fwts_list *cpus)
> +static int microcode_deinit(fwts_framework *fw)
> +{
> +       FWTS_UNUSED(fw);
> +
> +       fwts_klog_free(klog);
> +       return FWTS_OK;
> +}
> +
> +microcode_info *microcode_find_cpu(
> +       const int cpu,
> +       fwts_list *microcode_info_list)
> +{
> +       fwts_list_link *item;
> +
> +       fwts_list_foreach(item, microcode_info_list) {
> +               microcode_info *info = fwts_list_data(microcode_info *,
> item);
> +               if (info->cpu == cpu)
> +                       return info;
> +       }
> +       return NULL;
> +}
> +
> +static int microcode_test1(fwts_framework *fw)
>  {
> +       fwts_list_link *item;
> +       fwts_list microcode_info_list;
> +       microcode_info *info;
> +
>         DIR *dir;
>         struct dirent *entry;
> -       char path[PATH_MAX];
> -       fwts_list_link *item;
>         int failed = 0;
>         int passed = 0;
> -       char *data;
> -       int version;
>
> -       if ((dir = opendir("/sys/devices/system/cpu")) == NULL)
> -               return;
> +       fwts_list_init(&microcode_info_list);
>
> -       while ((entry = readdir(dir)) != NULL) {
> -               if (entry &&
> -                   (strlen(entry->d_name)>3) &&
> -                   (strncmp(entry->d_name,"cpu",3) == 0) &&
> -                   (isdigit(entry->d_name[3]))) {
> +       fwts_log_info(fw,
> +               "This test verifies if the firmware has put a recent
> revision "
> +               "of the microcode into the processor at boot time. Recent "
> +               "microcode is important to have all the required "
> +               "features and errata updates for the processor.");
>
> -                       snprintf(path, sizeof(path),
> -
> "/sys/devices/system/cpu/%s/microcode/version",
> -                               entry->d_name);
> -                       if ((data = fwts_get(path)) != NULL) {
> -                               version = strtoul(data, NULL, 16);
> -                               free(data);
> -                               fwts_list_foreach(item, cpus) {
> -                                       fwts_microcode_info *cpu =
> fwts_list_data(fwts_microcode_info*, item);
> -                                       if (strcmp(entry->d_name,
> cpu->cpu) == 0) {
> -                                               if (version ==
> cpu->version)
> -                                                       passed++;
> -                                               else {
> -                                                       failed++;
> -                                                       fwts_failed(fw,
> LOG_LEVEL_LOW,
> -
> "MicrocodeOutdated",
> -                                                               "Cpu %s
> has outdated microcode (version %x while version %x is available)",
> -                                                               cpu->cpu,
> -
> cpu->version,
> -                                                               version);
> -                                               }
> -                                       }
> +       /*
> +        * Gather data from klog, scan for patterns like:
> +        *   microcode: CPU0 sig=0x306a9, pf=0x10, revision=0x12
> +         *   microcode: CPU0 updated to revision 0x17, date = 2013-01-09
> +        */
> +       fwts_list_foreach(item, klog) {
> +               char *ptr;
> +               char *line = fwts_list_data(char *, item);
> +               line = fwts_klog_remove_timestamp(line);
> +
> +               if (strstr(line, "microcode:") == NULL)
> +                       continue;
> +
> +               ptr = strstr(line, "revision=0x");
> +               if (ptr) {
> +                       int cpu;
> +                       int revision;
> +
> +                       if (sscanf(line, "%*s CPU%d sig=0x%*x, pf=0x%*x,
> revision=0x%x", &cpu, &revision) != 2)
> +                               continue;
> +
> +                       if (microcode_find_cpu(cpu, &microcode_info_list)
> == NULL) {
> +                               info = calloc(1, sizeof(microcode_info));
> +                               if (info == NULL) {
> +                                       fwts_log_error(fw, "Cannot
> allocate memory.");
> +
> fwts_list_free_items(&microcode_info_list, free);
> +                                       return FWTS_ERROR;
>                                 }
> +                               info->cpu = cpu;
> +                               info->old_revision = revision;
> +                               info->new_revision = UNKNOWN;
> +                               info->year = UNKNOWN;
> +                               info->month = UNKNOWN;
> +                               info->day = UNKNOWN;
> +                               fwts_list_append(&microcode_info_list,
> info);
>                         }
> +                       continue;
>                 }
> -       }
> -       if (!failed)
> -               fwts_passed(fw, "%d CPU(s) have the latest microcode
> loaded.", passed);
>
> -       closedir(dir);
> -}
> +               ptr = strstr(line, "updated to revision");
> +               if (ptr) {
> +                       int cpu;
> +                       int revision;
> +                       int year;
> +                       int month;
> +                       int day;
>
> -static int microcode_init(fwts_framework *fw)
> -{
> -       if (access(FWTS_MICROCODE_DEVICE, W_OK) != 0) {
> -               fwts_log_error(fw, "Cannot get write access to %s.",
> FWTS_MICROCODE_DEVICE);
> -               return FWTS_ERROR;
> -       }
> +                       if (sscanf(line, "%*s CPU%d updated to revision
> 0x%x, date = %d-%d-%d",
> +                               &cpu, &revision, &year, &month, &day) != 5)
> +                               continue;
>
> -       if (access(FWTS_MICROCODE_FILE, R_OK) != 0) {
> -               fwts_log_error(fw, "Cannot read microcode file %s.",
> FWTS_MICROCODE_FILE);
> -               return FWTS_ERROR;
> +                       info = microcode_find_cpu(cpu,
> &microcode_info_list);
> +                       if (info == NULL) {
> +                               /*
> +                                * Strange, we found the update but not the
> +                                * original revision info, create an entry
> for
> +                                * this CPU anyhow, mark original revision
> as
> +                                * unknown
> +                                */
> +                               info = calloc(1, sizeof(microcode_info));
> +                               if (info == NULL) {
> +                                       fwts_log_error(fw, "Cannot
> allocate memory.");
> +
> fwts_list_free_items(&microcode_info_list, free);
> +                                       return FWTS_ERROR;
> +                               }
> +                               info->cpu = cpu;
> +                               info->old_revision = UNKNOWN;
> +                               info->new_revision = revision;
> +                               info->year = year;
> +                               info->month = month;
> +                               info->day = day;
> +                               fwts_list_append(&microcode_info_list,
> info);
> +                       } else {
> +                               /* Exists, so update */
> +                               info->new_revision = revision;
> +                               info->year = year;
> +                               info->month = month;
> +                               info->day = day;
> +                       }
> +               }
>         }
>
> -       if (fwts_check_executable(fw, "/sbin/modprobe", "modprobe") !=
> FWTS_OK)
> +       /*
> +        *  Now sanity check all CPUs
> +        */
> +       if ((dir = opendir(SYS_CPU_PATH)) == NULL) {
> +               fwts_log_error(fw, "Cannot open %s.", SYS_CPU_PATH);
> +               fwts_list_free_items(&microcode_info_list, free);
>                 return FWTS_ERROR;
> +       }
>
> -       return FWTS_OK;
> -}
> +       /* Scan and check */
> +       while ((entry = readdir(dir)) != NULL) {
> +               char path[PATH_MAX];
> +               char *data;
>
> -static int microcode_test1(fwts_framework *fw)
> -{
> -       fwts_list cpus;
> -       pid_t pid;
> -       int fd;
> +               if (entry &&
> +                   (strlen(entry->d_name) > 3) &&
> +                   (strncmp(entry->d_name,"cpu", 3) == 0) &&
> +                   (isdigit(entry->d_name[3]))) {
> +                       snprintf(path, sizeof(path),
> +                               SYS_CPU_PATH "/%s/microcode/version",
> +                               entry->d_name);
> +                       if ((data = fwts_get(path)) != NULL) {
> +                               int cpu = (int)strtoul(&entry->d_name[3],
> NULL, 16);
> +                               int revision = (int)strtoul(data, NULL,
> 16);
> +                               free(data);
>
> -       fwts_log_info(fw,
> -               "This test verifies if the firmware has put a recent
> version "
> -               "of the microcode into the processor at boot time. Recent "
> -               "microcode is important to have all the required "
> -               "features and errata updates for the processor.");
> +                               info = microcode_find_cpu(cpu,
> &microcode_info_list);
> +                               if (info == NULL) {
> +                                       /*
> +                                        * It may be null because the
> kernel log
> +                                        * is old and we've now lost the
> log
> +                                        * messages, so we can't really
> sanity
> +                                        * check, so it's not strictly a
> failure.
> +                                        */
> +                                       fwts_log_info(fw,
> +                                               "Could not determine if
> CPU %d had a microcode "
> +                                               "update from the kernel
> message log.", cpu);
> +                                       continue;
> +                               }
>
> -       fwts_list_init(&cpus);
> +                               /*
> +                                * We found the old revision but not a
> +                                * new revsion, failed
> +                                */
> +                               if (info->new_revision == UNKNOWN) {
> +                                       failed++;
> +                                       fwts_failed(fw, LOG_LEVEL_MEDIUM,
> "MicrocodeNotUpdated",
> +                                               "The kernel did not report
> that CPU %d has had a microcode update. "
> +                                               "The current firmware is
> revision 0x%x and probably has not been updated.",
> +                                               cpu, info->old_revision);
> +                                       continue;
> +                               }
>
> -       if ((fd = fwts_pipe_open("/sbin/modprobe microcode", &pid)) < 0) {
> -               fwts_log_error(fw, "Cannot modprobe microcode module,");
> -               return FWTS_ERROR;
> -       }
> -       fwts_pipe_close(fd, pid);
> +                               /*
> +                                * We found a new revision but it does not
> +                                * match the CPU info, failed
> +                                */
> +                               if (info->new_revision != revision) {
> +                                       failed++;
> +                                       fwts_failed(fw, LOG_LEVEL_MEDIUM,
> "MicrocodeMismatch",
> +                                               "The kernel has reported
> that CPU %d has had a microcode update "
> +                                               "to revision 0x%x,
> however, the processor seems to be running "
> +                                               "with a different revision
> 0x%x",
> +                                               cpu, info->new_revision,
> revision);
> +                                       continue;
> +                               }
>
> -       gather_info(fw, &cpus);
> +                               /*
> +                                * We found a new revision but not an old
> one,
> +                                * assume it was OK since it got updated
> +                                */
> +                               if (info->old_revision == UNKNOWN) {
> +                                       passed++;
> +                                       fwts_log_info(fw, "CPU %d
> microcode updated to revision 0x%x.",
> +                                               cpu, info->new_revision);
> +                                       continue;
> +                               }
>
> -       /* now run the microcode update */
> +                               /* Final sanity check */
> +                               if (info->new_revision <
> info->old_revision) {
> +                                       failed++;
> +                                       fwts_failed(fw, LOG_LEVEL_HIGH,
> "MicrocodeDowngrade",
> +                                               "The kernel has reported
> that CPU %d has had a microcode update "
> +                                               "downgrade from revision
> 0x%x down to revision 0x%x (%d-%2.2d-%2.2d).",
> +                                               cpu, info->new_revision,
> info->old_revision,
> +                                               info->year, info->month,
> info->day);
> +                               } else {
> +                                       passed++;
> +                                       fwts_log_info(fw, "CPU %d
> microcode updated from revision 0x%x to revision 0x%x (%d-%2.2d-%2.2d).",
> +                                               cpu, info->old_revision,
> info->new_revision,
> +                                               info->year, info->month,
> info->day);
> +                               }
> +                       }
> +               }
> +       }
> +       if (!failed) {
> +               if (passed > 0)
> +                       fwts_passed(fw, "%d CPU(s) have the latest
> microcode loaded.", passed);
> +               else
> +                       fwts_log_info(fw, "Could not determine from kernel
> log if latest microcode has been loaded.");
> +       }
>
> -       if (fwts_update_microcode(fw, FWTS_MICROCODE_DEVICE,
> FWTS_MICROCODE_FILE)  != FWTS_OK) {
> -               fwts_log_error(fw, "Failed to upload latest microcode.");
> -       } else {
> -               /* and check for lacking updates */
> -               check_info(fw, &cpus);
> +       closedir(dir);
>
> -               fwts_list_free_items(&cpus, free);
> -       }
> +       fwts_list_free_items(&microcode_info_list, free);
>
>         return FWTS_OK;
>  }
> @@ -199,6 +306,7 @@ static fwts_framework_minor_test microcode_tests[] = {
>  static fwts_framework_ops microcode_ops = {
>         .description = "Check if system is using latest microcode.",
>         .init        = microcode_init,
> +       .deinit      = microcode_deinit,
>         .minor_tests = microcode_tests
>  };
>
> --
> 1.8.1.2
>
>
> --
> fwts-devel mailing list
> fwts-devel at lists.ubuntu.com
> Modify settings or unsubscribe at:
> https://lists.ubuntu.com/mailman/listinfo/fwts-devel
>



-- 
Cheers,
Alex Hung
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.ubuntu.com/archives/fwts-devel/attachments/20130530/90000a16/attachment-0001.html>


More information about the fwts-devel mailing list