[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(µcode_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, µcode_info_list)
> == NULL) {
> + info = calloc(1, sizeof(microcode_info));
> + if (info == NULL) {
> + fwts_log_error(fw, "Cannot
> allocate memory.");
> +
> fwts_list_free_items(µcode_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(µcode_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,
> µcode_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(µcode_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(µcode_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(µcode_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,
> µcode_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(µcode_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