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.
This commit is contained in:
Richard Hughes 2020-08-23 21:54:50 +01:00
parent f779a0cfaa
commit 8307bd603e
4 changed files with 141 additions and 96 deletions

View File

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

View File

@ -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);

View File

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

View File

@ -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)
{