diff mbox

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

Message ID 1369837865-22272-3-git-send-email-colin.king@canonical.com
State Accepted
Headers show

Commit Message

Colin Ian King May 29, 2013, 2:31 p.m. UTC
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(-)

Comments

Alex Hung May 30, 2013, 2:02 a.m. UTC | #1
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(&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@lists.ubuntu.com
> Modify settings or unsubscribe at:
> https://lists.ubuntu.com/mailman/listinfo/fwts-devel
>
Ivan Hu May 31, 2013, 7:47 a.m. UTC | #2
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(&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
>   };
>
>

Acked-by: Ivan Hu <ivan.hu@canonical.com>
diff mbox

Patch

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
 };