Message ID | 1369837865-22272-3-git-send-email-colin.king@canonical.com |
---|---|
State | Accepted |
Headers | show |
Acked-by: Alex Hung <alex.hung@canonical.com> On Wed, May 29, 2013 at 10:31 PM, Colin King <colin.king@canonical.com>wrote: > From: Colin Ian King <colin.king@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@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@lists.ubuntu.com > Modify settings or unsubscribe at: > https://lists.ubuntu.com/mailman/listinfo/fwts-devel >
On 05/29/2013 10:31 PM, Colin King wrote: > From: Colin Ian King <colin.king@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@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 > }; > > Acked-by: Ivan Hu <ivan.hu@canonical.com>
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 };