From 8307bd603ea7764e17142c67aa637edaf0de6adb Mon Sep 17 00:00:00 2001 From: Richard Hughes Date: Sun, 23 Aug 2020 21:54:50 +0100 Subject: [PATCH] cpu: Directly probe the CPUID data to improve startup speed This is much more efficient than parsing hundreds of lines of /proc/cpuinfo and also causes hundreds of thousands less allocations at startup. For systems with dozens of virtual CPUs the deduplication of device objects was increasing start up time considerably. Use the msr plugin to read the microcode version as this is not obtained using CPUID, as it is instead being provided in an MSR. --- plugins/cpu/fu-cpu-device.c | 138 ++++++++++++++++++++++-------------- plugins/cpu/fu-cpu-device.h | 2 +- plugins/cpu/fu-plugin-cpu.c | 61 +++++----------- plugins/msr/fu-plugin-msr.c | 36 ++++++++++ 4 files changed, 141 insertions(+), 96 deletions(-) diff --git a/plugins/cpu/fu-cpu-device.c b/plugins/cpu/fu-cpu-device.c index 681c1ac48..a97191b55 100644 --- a/plugins/cpu/fu-cpu-device.c +++ b/plugins/cpu/fu-cpu-device.c @@ -93,56 +93,6 @@ fu_cpu_device_convert_vendor (const gchar *vendor) return vendor; } -static void -fu_cpu_device_parse_flags (FuCpuDevice *self, const gchar *data) -{ - g_auto(GStrv) flags = g_strsplit (data, " ", -1); - for (guint i = 0; flags[i] != NULL; i++) { - if (g_strcmp0 (flags[i], "shstk") == 0) - self->flags |= FU_CPU_DEVICE_FLAG_SHSTK; - if (g_strcmp0 (flags[i], "ibt") == 0) - self->flags |= FU_CPU_DEVICE_FLAG_IBT; - if (g_strcmp0 (flags[i], "tme") == 0) - self->flags |= FU_CPU_DEVICE_FLAG_TME; - if (g_strcmp0 (flags[i], "smap") == 0) - self->flags |= FU_CPU_DEVICE_FLAG_SMAP; - } -} - -static void -fu_cpu_device_parse_section (FuDevice *dev, const gchar *data) -{ - g_auto(GStrv) lines = NULL; - FuCpuDevice *self = FU_CPU_DEVICE (dev); - - lines = g_strsplit (data, "\n", 0); - for (guint i = 0; lines[i] != NULL; i++) { - if (g_str_has_prefix (lines[i], "vendor_id")) { - g_auto(GStrv) fields = g_strsplit (lines[i], ":", -1); - if (fields[1] != NULL) - fu_device_set_vendor (dev, fu_cpu_device_convert_vendor (fields[1] + 1)); - } else if (g_str_has_prefix (lines[i], "model name")) { - g_auto(GStrv) fields = g_strsplit (lines[i], ":", -1); - if (fields[1] != NULL) - fu_device_set_name (dev, g_strchug (fields[1])); - } else if (g_str_has_prefix (lines[i], "microcode")) { - g_auto(GStrv) fields = g_strsplit (lines[i], ":", -1); - if (fields[1] != NULL) - fu_device_set_version (dev, g_strchug (fields[1])); - } else if (g_str_has_prefix (lines[i], "physical id")) { - g_auto(GStrv) fields = g_strsplit (lines[i], ":", -1); - if (fields[1] != NULL) { - g_autofree gchar *tmp = g_strdup_printf ("cpu:%s", g_strchug (fields[1])); - fu_device_set_physical_id (dev, tmp); - } - } else if (g_str_has_prefix (lines[i], "flags")) { - g_auto(GStrv) fields = g_strsplit (lines[i], ":", -1); - if (fields[1] != NULL) - fu_cpu_device_parse_flags (self, fields[1]); - } - } -} - static void fu_cpu_device_init (FuCpuDevice *self) { @@ -150,6 +100,7 @@ fu_cpu_device_init (FuCpuDevice *self) fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_INTERNAL); fu_device_add_icon (FU_DEVICE (self), "computer"); fu_device_set_version_format (FU_DEVICE (self), FWUPD_VERSION_FORMAT_HEX); + fu_device_set_physical_id (FU_DEVICE (self), "cpu:0"); } static gboolean @@ -200,9 +151,93 @@ fu_cpu_device_add_instance_ids (FuDevice *device, GError **error) return TRUE; } +static gboolean +fu_cpu_device_probe_manufacturer_id (FuDevice *device, GError **error) +{ + guint32 ebx = 0; + guint32 ecx = 0; + guint32 edx = 0; + gchar str[13] = { '\0' }; + if (!fu_common_cpuid (0x0, NULL, &ebx, &ecx, &edx, error)) + return FALSE; + if (!fu_memcpy_safe ((guint8 *) str, sizeof(str), 0x0, /* dst */ + (const guint8 *) &ebx, sizeof(ebx), 0x0, /* src */ + sizeof(guint32), error)) + return FALSE; + if (!fu_memcpy_safe ((guint8 *) str, sizeof(str), 0x4, /* dst */ + (const guint8 *) &edx, sizeof(edx), 0x0, /* src */ + sizeof(guint32), error)) + return FALSE; + if (!fu_memcpy_safe ((guint8 *) str, sizeof(str), 0x8, /* dst */ + (const guint8 *) &ecx, sizeof(ecx), 0x0, /* src */ + sizeof(guint32), error)) + return FALSE; + fu_device_set_vendor (device, fu_cpu_device_convert_vendor (str)); + return TRUE; +} + +static gboolean +fu_cpu_device_probe_model (FuDevice *device, GError **error) +{ + guint32 eax = 0; + guint32 ebx = 0; + guint32 ecx = 0; + guint32 edx = 0; + gchar str[49] = { '\0' }; + + for (guint32 i = 0; i < 3; i++) { + if (!fu_common_cpuid (0x80000002 + i, &eax, &ebx, &ecx, &edx, error)) + return FALSE; + if (!fu_memcpy_safe ((guint8 *) str, sizeof(str), (16 * i) + 0x0, /* dst */ + (const guint8 *) &eax, sizeof(eax), 0x0, /* src */ + sizeof(guint32), error)) + return FALSE; + if (!fu_memcpy_safe ((guint8 *) str, sizeof(str), (16 * i) + 0x4, /* dst */ + (const guint8 *) &ebx, sizeof(ebx), 0x0, /* src */ + sizeof(guint32), error)) + return FALSE; + if (!fu_memcpy_safe ((guint8 *) str, sizeof(str), (16 * i) + 0x8, /* dst */ + (const guint8 *) &ecx, sizeof(ecx), 0x0, /* src */ + sizeof(guint32), error)) + return FALSE; + if (!fu_memcpy_safe ((guint8 *) str, sizeof(str), (16 * i) + 0xc, /* dst */ + (const guint8 *) &edx, sizeof(edx), 0x0, /* src */ + sizeof(guint32), error)) + return FALSE; + } + fu_device_set_name (device, str); + return TRUE; +} + +static gboolean +fu_cpu_device_probe_extended_features (FuDevice *device, GError **error) +{ + FuCpuDevice *self = FU_CPU_DEVICE (device); + guint32 ebx = 0; + guint32 ecx = 0; + + if (!fu_common_cpuid (0x7, NULL, &ebx, &ecx, NULL, error)) + return FALSE; + if ((ebx >> 20) & 0x1) + self->flags |= FU_CPU_DEVICE_FLAG_SMAP; + if ((ecx >> 7) & 0x1) + self->flags |= FU_CPU_DEVICE_FLAG_SHSTK; + if ((ecx >> 13) & 0x1) + self->flags |= FU_CPU_DEVICE_FLAG_TME; + if ((ecx >> 20) & 0x1) + self->flags |= FU_CPU_DEVICE_FLAG_IBT; + return TRUE; +} + static gboolean fu_cpu_device_probe (FuDevice *device, GError **error) { + if (!fu_cpu_device_probe_manufacturer_id (device, error)) + return FALSE; + if (!fu_cpu_device_probe_model (device, error)) + return FALSE; + if (!fu_cpu_device_probe_extended_features (device, error)) + return FALSE; if (!fu_cpu_device_add_instance_ids (device, error)) return FALSE; return TRUE; @@ -236,10 +271,9 @@ fu_cpu_device_class_init (FuCpuDeviceClass *klass) } FuCpuDevice * -fu_cpu_device_new (const gchar *section) +fu_cpu_device_new (void) { FuCpuDevice *device = NULL; device = g_object_new (FU_TYPE_CPU_DEVICE, NULL); - fu_cpu_device_parse_section (FU_DEVICE (device), section); return device; } diff --git a/plugins/cpu/fu-cpu-device.h b/plugins/cpu/fu-cpu-device.h index 50d50d154..0115916a2 100644 --- a/plugins/cpu/fu-cpu-device.h +++ b/plugins/cpu/fu-cpu-device.h @@ -19,6 +19,6 @@ typedef enum { FU_CPU_DEVICE_FLAG_SMAP = 1 << 3, } FuCpuDeviceFlag; -FuCpuDevice *fu_cpu_device_new (const gchar *section); +FuCpuDevice *fu_cpu_device_new (void); gboolean fu_cpu_device_has_flag (FuCpuDevice *self, FuCpuDeviceFlag flag); diff --git a/plugins/cpu/fu-plugin-cpu.c b/plugins/cpu/fu-plugin-cpu.c index d60547094..52015c4bd 100644 --- a/plugins/cpu/fu-plugin-cpu.c +++ b/plugins/cpu/fu-plugin-cpu.c @@ -10,58 +10,31 @@ #include "fu-hash.h" #include "fu-cpu-device.h" -struct FuPluginData { - gboolean has_cet; - gboolean has_smap; - gboolean has_tme; -}; - void fu_plugin_init (FuPlugin *plugin) { fu_plugin_set_build_hash (plugin, FU_BUILD_HASH); - fu_plugin_alloc_data (plugin, sizeof (FuPluginData)); + fu_plugin_add_rule (plugin, FU_PLUGIN_RULE_RUN_BEFORE, "msr"); } gboolean fu_plugin_coldplug (FuPlugin *plugin, GError **error) { - FuPluginData *data = fu_plugin_get_data (plugin); - gsize length; - g_autofree gchar *buf = NULL; - g_auto(GStrv) lines = NULL; - - if (!g_file_get_contents ("/proc/cpuinfo", &buf, &length, error)) + g_autoptr(FuCpuDevice) dev = fu_cpu_device_new (); + fu_device_set_quirks (FU_DEVICE (dev), fu_plugin_get_quirks (plugin)); + if (!fu_device_probe (FU_DEVICE (dev), error)) return FALSE; - - lines = g_strsplit (buf, "\n\n", 0); - for (guint i = 0; lines[i] != NULL; i++) { - g_autoptr(FuCpuDevice) dev = NULL; - if (strlen (lines[i]) == 0) - continue; - dev = fu_cpu_device_new (lines[i]); - fu_device_set_quirks (FU_DEVICE (dev), fu_plugin_get_quirks (plugin)); - if (!fu_device_probe (FU_DEVICE (dev), error)) - return FALSE; - if (!fu_device_setup (FU_DEVICE (dev), error)) - return FALSE; - if (fu_cpu_device_has_flag (dev, FU_CPU_DEVICE_FLAG_SHSTK) && - fu_cpu_device_has_flag (dev, FU_CPU_DEVICE_FLAG_IBT)) - data->has_cet = TRUE; - if (fu_cpu_device_has_flag (dev, FU_CPU_DEVICE_FLAG_TME)) - data->has_tme = TRUE; - if (fu_cpu_device_has_flag (dev, FU_CPU_DEVICE_FLAG_SMAP)) - data->has_smap = TRUE; - fu_plugin_device_add (plugin, FU_DEVICE (dev)); - } - + if (!fu_device_setup (FU_DEVICE (dev), error)) + return FALSE; + fu_plugin_cache_add (plugin, "cpu", dev); + fu_plugin_device_add (plugin, FU_DEVICE (dev)); return TRUE; } static void fu_plugin_add_security_attrs_intel_cet_enabled (FuPlugin *plugin, FuSecurityAttrs *attrs) { - FuPluginData *data = fu_plugin_get_data (plugin); + FuCpuDevice *device = fu_plugin_cache_lookup (plugin, "cpu"); g_autoptr(FwupdSecurityAttr) attr = NULL; /* create attr */ @@ -71,7 +44,8 @@ fu_plugin_add_security_attrs_intel_cet_enabled (FuPlugin *plugin, FuSecurityAttr fu_security_attrs_append (attrs, attr); /* check for CET */ - if (!data->has_cet) { + if (!fu_cpu_device_has_flag (device, FU_CPU_DEVICE_FLAG_SHSTK) || + !fu_cpu_device_has_flag (device, FU_CPU_DEVICE_FLAG_IBT)) { fwupd_security_attr_set_result (attr, FWUPD_SECURITY_ATTR_RESULT_NOT_SUPPORTED); return; } @@ -84,14 +58,15 @@ fu_plugin_add_security_attrs_intel_cet_enabled (FuPlugin *plugin, FuSecurityAttr static void fu_plugin_add_security_attrs_intel_cet_active (FuPlugin *plugin, FuSecurityAttrs *attrs) { - FuPluginData *data = fu_plugin_get_data (plugin); + FuCpuDevice *device = fu_plugin_cache_lookup (plugin, "cpu"); gint exit_status = 0xff; g_autofree gchar *toolfn = NULL; g_autoptr(FwupdSecurityAttr) attr = NULL; g_autoptr(GError) error_local = NULL; /* check for CET */ - if (!data->has_cet) + if (!fu_cpu_device_has_flag (device, FU_CPU_DEVICE_FLAG_SHSTK) || + !fu_cpu_device_has_flag (device, FU_CPU_DEVICE_FLAG_IBT)) return; /* create attr */ @@ -120,7 +95,7 @@ fu_plugin_add_security_attrs_intel_cet_active (FuPlugin *plugin, FuSecurityAttrs static void fu_plugin_add_security_attrs_intel_tme (FuPlugin *plugin, FuSecurityAttrs *attrs) { - FuPluginData *data = fu_plugin_get_data (plugin); + FuCpuDevice *device = fu_plugin_cache_lookup (plugin, "cpu"); g_autoptr(FwupdSecurityAttr) attr = NULL; /* create attr */ @@ -130,7 +105,7 @@ fu_plugin_add_security_attrs_intel_tme (FuPlugin *plugin, FuSecurityAttrs *attrs fu_security_attrs_append (attrs, attr); /* check for TME */ - if (!data->has_tme) { + if (!fu_cpu_device_has_flag (device, FU_CPU_DEVICE_FLAG_TME)) { fwupd_security_attr_set_result (attr, FWUPD_SECURITY_ATTR_RESULT_NOT_SUPPORTED); return; } @@ -143,7 +118,7 @@ fu_plugin_add_security_attrs_intel_tme (FuPlugin *plugin, FuSecurityAttrs *attrs static void fu_plugin_add_security_attrs_intel_smap (FuPlugin *plugin, FuSecurityAttrs *attrs) { - FuPluginData *data = fu_plugin_get_data (plugin); + FuCpuDevice *device = fu_plugin_cache_lookup (plugin, "cpu"); g_autoptr(FwupdSecurityAttr) attr = NULL; /* create attr */ @@ -153,7 +128,7 @@ fu_plugin_add_security_attrs_intel_smap (FuPlugin *plugin, FuSecurityAttrs *attr fu_security_attrs_append (attrs, attr); /* check for SMEP and SMAP */ - if (!data->has_smap) { + if (!fu_cpu_device_has_flag (device, FU_CPU_DEVICE_FLAG_SMAP)) { fwupd_security_attr_set_result (attr, FWUPD_SECURITY_ATTR_RESULT_NOT_SUPPORTED); return; } diff --git a/plugins/msr/fu-plugin-msr.c b/plugins/msr/fu-plugin-msr.c index 5c0d90b55..cf529321a 100644 --- a/plugins/msr/fu-plugin-msr.c +++ b/plugins/msr/fu-plugin-msr.c @@ -27,6 +27,7 @@ struct FuPluginData { }; #define PCI_MSR_IA32_DEBUG_INTERFACE 0xc80 +#define PCI_MSR_IA32_BIOS_SIGN_ID 0x8b void fu_plugin_init (FuPlugin *plugin) @@ -52,6 +53,7 @@ fu_plugin_startup (FuPlugin *plugin, GError **error) gboolean fu_plugin_udev_device_added (FuPlugin *plugin, FuUdevDevice *device, GError **error) { + FuDevice *device_cpu = fu_plugin_cache_lookup (plugin, "cpu"); FuPluginData *priv = fu_plugin_get_data (plugin); guint8 buf[8] = { 0x0 }; g_autoptr(FuDeviceLocker) locker = NULL; @@ -88,9 +90,43 @@ fu_plugin_udev_device_added (FuPlugin *plugin, FuUdevDevice *device, GError **er priv->ia32_debug.fields.locked, priv->ia32_debug.fields.debug_occurred); } + + /* get microcode version */ + if (device_cpu != NULL) { + guint32 ver_raw; + if (!fu_udev_device_pread_full (device, PCI_MSR_IA32_BIOS_SIGN_ID, + buf, sizeof(buf), error)) { + g_prefix_error (error, "could not read IA32_BIOS_SIGN_ID: "); + return FALSE; + } + fu_common_dump_raw (G_LOG_DOMAIN, "IA32_BIOS_SIGN_ID", buf, sizeof(buf)); + if (!fu_common_read_uint32_safe (buf, sizeof(buf), 0x4, + &ver_raw, G_LITTLE_ENDIAN, + error)) + return FALSE; + if (ver_raw != 0) { + FwupdVersionFormat verfmt = fu_device_get_version_format (device_cpu); + g_autofree gchar *ver_str = NULL; + ver_str = fu_common_version_from_uint32 (ver_raw, verfmt); + g_debug ("setting microcode version to %s", ver_str); + fu_device_set_version (device_cpu, ver_str); + fu_device_set_version_raw (device_cpu, ver_raw); + } + } + + /* success */ return TRUE; } +void +fu_plugin_device_registered (FuPlugin *plugin, FuDevice *dev) +{ + if (g_strcmp0 (fu_device_get_plugin (dev), "cpu") == 0) { + fu_plugin_cache_add (plugin, "cpu", dev); + return; + } +} + static void fu_plugin_add_security_attr_dci_enabled (FuPlugin *plugin, FuSecurityAttrs *attrs) {