diff --git a/contrib/fwupd.spec.in b/contrib/fwupd.spec.in index dda7ab952..bbc99dbd9 100644 --- a/contrib/fwupd.spec.in +++ b/contrib/fwupd.spec.in @@ -234,6 +234,7 @@ mkdir -p --mode=0700 $RPM_BUILD_ROOT%{_localstatedir}/lib/fwupd/gnupg %files -f %{name}.lang %doc README.md AUTHORS %license COPYING +%config(noreplace)%{_sysconfdir}/fwupd/ata.conf %config(noreplace)%{_sysconfdir}/fwupd/daemon.conf %config(noreplace)%{_sysconfdir}/fwupd/upower.conf %if 0%{?have_uefi} diff --git a/plugins/ata/ata.conf b/plugins/ata/ata.conf new file mode 100644 index 000000000..f76abdd8f --- /dev/null +++ b/plugins/ata/ata.conf @@ -0,0 +1,4 @@ +[ata] + +# ask the user to report the missing OUI in the daemon logs +UnknownOuiReport=true diff --git a/plugins/ata/ata.quirk b/plugins/ata/ata.quirk index 7dfda689b..dc71bc0ac 100644 --- a/plugins/ata/ata.quirk +++ b/plugins/ata/ata.quirk @@ -1,3 +1,11 @@ # match all devices with this udev subsystem [DeviceInstanceId=BLOCK] Plugin = ata + +[DeviceInstanceId=OUI\000c50] +Vendor = Seagate +VendorId = ATA:0x1BB1 + +[DeviceInstanceId=OUI\002538] +Vendor = Samsung +VendorId = ATA:0x144D diff --git a/plugins/ata/fu-ata-device.c b/plugins/ata/fu-ata-device.c index d341a0a4c..5669f6cb4 100644 --- a/plugins/ata/fu-ata-device.c +++ b/plugins/ata/fu-ata-device.c @@ -71,6 +71,8 @@ struct _FuAtaDevice { guint usb_depth; guint16 transfer_blocks; guint8 transfer_mode; + guint32 oui; + gboolean unknown_oui_report; }; G_DEFINE_TYPE (FuAtaDevice, fu_ata_device, FU_TYPE_UDEV_DEVICE) @@ -87,6 +89,12 @@ fu_ata_device_get_transfer_blocks (FuAtaDevice *self) return self->transfer_blocks; } +void +fu_ata_device_set_unknown_oui_report (FuAtaDevice *self, gboolean enabled) +{ + self->unknown_oui_report = enabled; +} + static gchar * fu_ata_device_get_string (const guint16 *buf, guint start, guint end) { @@ -111,6 +119,8 @@ fu_ata_device_to_string (FuDevice *device, guint idt, GString *str) FuAtaDevice *self = FU_ATA_DEVICE (device); fu_common_string_append_kx (str, idt, "TransferMode", self->transfer_mode); fu_common_string_append_kx (str, idt, "TransferBlocks", self->transfer_blocks); + if (self->oui != 0x0) + fu_common_string_append_kx (str, idt, "OUI", self->oui); fu_common_string_append_ku (str, idt, "PciDepth", self->pci_depth); fu_common_string_append_ku (str, idt, "UsbDepth", self->usb_depth); } @@ -166,16 +176,153 @@ fu_ata_device_parse_id_maybe_dell (FuAtaDevice *self, const guint16 *buf) guid_efi = fu_ata_device_get_guid_safe (buf, 129); if (guid_efi != NULL) fu_device_add_guid (FU_DEVICE (self), guid_efi); + + /* owned by Dell */ + fu_device_set_vendor (FU_DEVICE (self), "Dell"); + fu_device_set_vendor_id (FU_DEVICE (self), "ATA:0x1028"); +} + +static void +fu_ata_device_parse_vendor_name (FuAtaDevice *self, const gchar *name) +{ + struct { + const gchar *prefix; /* in CAPS */ + guint16 vid; + const gchar *name; + } map_name[] = { + /* vendor matches */ + { "ADATA*", 0x1cc1, "ADATA" }, + { "APACER*", 0x0000, "Apacer" }, /* not in pci.ids */ + { "APPLE*", 0x106b, "Apple" }, + { "CORSAIR*", 0x1987, "Corsair" }, /* identifies as Phison */ + { "CRUCIAL*", 0xc0a9, "Crucial" }, + { "FUJITSU*", 0x10cf, "Fujitsu" }, + { "GIGABYTE*", 0x1458, "Gigabyte" }, + { "HGST*", 0x101c, "Western Digital" }, + { "HITACHI*", 0x101c, "Western Digital" }, /* was acquired by WD */ + { "HITACHI*", 0x1054, "Hitachi" }, + { "HP SSD*", 0x103c, "HP" }, + { "INTEL*", 0x8086, "Intel" }, + { "KINGSPEC*", 0x0000, "KingSpec" }, /* not in pci.ids */ + { "KINGSTON*", 0x2646, "Kingston" }, + { "LITEON*", 0x14a4, "LITE-ON" }, + { "MAXTOR*", 0x115f, "Maxtor" }, + { "MICRON*", 0x1344, "Micron" }, + { "OCZ*", 0x1179, "Toshiba" }, + { "PNY*", 0x196e, "PNY" }, + { "QEMU*", 0x1b36, "QEMU" }, /* identifies as Red Hat! */ + { "SAMSUNG*", 0x144d, "Samsung" }, + { "SANDISK*", 0x15b7, "SanDisk" }, + { "SEAGATE*", 0x1bb1, "Seagate" }, + { "SK HYNIX*", 0x1c5c, "SK hynix", }, + { "SUPERMICRO*",0x15d9, "SuperMicro" }, + { "TOSHIBA*", 0x1179, "Toshiba" }, + { "WDC*", 0x101c, "Western Digital" }, + { NULL, 0x0000, NULL } + }; + struct { + const gchar *prefix; /* in CAPS */ + guint16 vid; + const gchar *name; + } map_fuzzy[] = { + /* fuzzy name matches -- also see legacy list at: + * https://github.com/linuxhw/hw-probe/blob/master/hw-probe.pl#L647 */ + { "001-*", 0x1bb1, "Seagate" }, + { "726060*", 0x101c, "Western Digital" }, + { "CT*", 0xc0a9, "Crucial" }, + { "DT0*", 0x1179, "Toshiba" }, + { "EZEX*", 0x101c, "Western Digital" }, + { "GB0*", 0x1590, "HPE" }, + { "GOODRAM*", 0x1987, "Phison" }, + { "H??54*", 0x101c, "Western Digital" }, + { "H??72?0*", 0x101c, "Western Digital" }, + { "HDWG*", 0x1179, "Toshiba" }, + { "M?0??CA*", 0x1179, "Toshiba" }, /* enterprise */ + { "M4-CT*", 0xc0a9, "Crucial" }, + { "MA*", 0x10cf, "Fujitsu", }, + { "MB*", 0x10cf, "Fujitsu", }, + { "MK0*", 0x1590, "HPE" }, + { "MTFDDAK*", 0x1344, "Micron" }, + { "NIM*", 0x0000, "Nimbus", }, /* no PCI ID */ + { "SATADOM*", 0x0000, "Innodisk", }, /* no PCI ID */ + { "SSD 860*", 0x144d, "Samsung" }, + { "SSDPR*", 0x1987, "Phison" }, + { "SSDSC?K*", 0x8086, "Intel" }, + { "ST*", 0x1bb1, "Seagate", }, + { "TEAM*", 0x0000, "Team Group" }, /* not in pci.ids */ + { "TS*", 0x8564, "Transcend" }, + { "VK0*", 0x1590, "HPE" }, + { "WD*", 0x101c, "Western Digital" }, + { NULL, 0x0000, NULL } + }; + struct { + const gchar *prefix; /* in CAPS */ + guint16 vid; + const gchar *name; + } map_version[] = { + /* fuzzy version matches */ + { "CS2111*", 0x196e, "PNY" }, + { "S?FM*", 0x1987, "Phison" }, + { NULL, 0x0000, NULL } + }; + g_autofree gchar *name_up = g_ascii_strup (name, -1); + g_autofree gchar *vendor_id = NULL; + + /* find match */ + for (guint i = 0; map_name[i].prefix != NULL; i++) { + if (fu_common_fnmatch (map_name[i].prefix, name_up)) { + name += strlen (map_name[i].prefix) - 1; + fu_device_set_vendor (FU_DEVICE (self), map_name[i].name); + vendor_id = g_strdup_printf ("ATA:0x%X", map_name[i].vid); + break; + } + } + + /* fall back to fuzzy match */ + if (vendor_id == NULL) { + for (guint i = 0; map_fuzzy[i].prefix != NULL; i++) { + if (fu_common_fnmatch (map_fuzzy[i].prefix, name_up)) { + fu_device_set_vendor (FU_DEVICE (self), map_fuzzy[i].name); + vendor_id = g_strdup_printf ("ATA:0x%X", map_fuzzy[i].vid); + break; + } + } + } + + /* fall back to version */ + if (vendor_id == NULL) { + g_autofree gchar *version_up = g_ascii_strup (fu_device_get_version (FU_DEVICE (self)), -1); + for (guint i = 0; map_version[i].prefix != NULL; i++) { + if (fu_common_fnmatch (map_version[i].prefix, version_up)) { + fu_device_set_vendor (FU_DEVICE (self), map_version[i].name); + vendor_id = g_strdup_printf ("ATA:0x%X", map_version[i].vid); + break; + } + } + } + + /* devices without a vendor ID will not be UPGRADABLE */ + if (vendor_id != NULL) + fu_device_set_vendor_id (FU_DEVICE (self), vendor_id); + + /* remove leading junk */ + while (name[0] == ' ' || name[0] == '_' || name[0] == '-') + name += 1; + + /* if changed */ + if (g_strcmp0 (fu_device_get_name (FU_DEVICE (self)), name) != 0) + fu_device_set_name (FU_DEVICE (self), name); } static gboolean fu_ata_device_parse_id (FuAtaDevice *self, const guint8 *buf, gsize sz, GError **error) { FuDevice *device = FU_DEVICE (self); + gboolean has_oui_quirk = FALSE; guint16 xfer_min = 1; guint16 xfer_max = 0xffff; guint16 id[FU_ATA_IDENTIFY_SIZE/2]; - g_autofree gchar *name_pad = NULL; + g_autofree gchar *name = NULL; g_autofree gchar *sku = NULL; /* check size */ @@ -231,12 +378,6 @@ fu_ata_device_parse_id (FuAtaDevice *self, const guint8 *buf, gsize sz, GError * if (tmp != NULL) fu_device_set_serial (device, tmp); } - if (fu_device_get_name (device) == NULL) { - g_autofree gchar *tmp = NULL; - tmp = fu_ata_device_get_string (id, 27, 46); - if (tmp != NULL) - fu_device_set_name (device, tmp); - } if (fu_device_get_version (device) == NULL) { g_autofree gchar *tmp = NULL; tmp = fu_ata_device_get_string (id, 23, 26); @@ -246,32 +387,61 @@ fu_ata_device_parse_id (FuAtaDevice *self, const guint8 *buf, gsize sz, GError * fu_device_set_version_format (device, FWUPD_VERSION_FORMAT_PLAIN); } + /* get OUI if set */ + self->oui = ((guint32) (id[108] & 0x0fff)) << 12 | + ((guint32) (id[109] & 0xfff0)) >> 4; + if (self->oui > 0x0) { + g_autofree gchar *tmp = NULL; + tmp = g_strdup_printf ("OUI\\%06x", self->oui); + fu_device_add_instance_id (device, tmp); + has_oui_quirk = fu_device_get_vendor (FU_DEVICE (self)) != NULL; + } + + /* if not already set using the vendor block or a OUI quirk */ + name = fu_ata_device_get_string (id, 27, 46); + if (name != NULL && !has_oui_quirk) + fu_ata_device_parse_vendor_name (self, name); + + /* ask user to report data */ + if (self->oui != 0x0 && !has_oui_quirk && self->unknown_oui_report) { + const gchar *url = "https://github.com/fwupd/fwupd/wiki/ATA-Disk:-OUI-Quirk-Required"; + g_printerr ("OUI quirk required, please see %s!\n", url); + g_printerr ("---\n"); + g_printerr ("[DeviceInstanceId=OUI\\%06x]\n", self->oui); + if (fu_device_get_vendor_id (FU_DEVICE (self)) != NULL) { + g_printerr ("Vendor = %s\n", fu_device_get_vendor (FU_DEVICE (self))); + g_printerr ("VendorId = %s\n", fu_device_get_vendor_id (FU_DEVICE (self))); + } else { + g_printerr ("Vendor = FIXME\n"); + g_printerr ("VendorId = ATA:UNKNOWN\n"); + } + g_printerr ("---\n"); + } + /* 8 byte additional product identifier == SKU? */ sku = fu_ata_device_get_string (id, 170, 173); if (sku != NULL) g_debug ("SKU=%s", sku); - /* if we have vendor defined identify blocks don't add generic GUID */ - if (fu_device_get_guids (device)->len != 0) - return TRUE; - /* add extra GUIDs if none detected from identify block */ - name_pad = fu_ata_device_pad_string_for_id (fu_device_get_name (device)); - if (name_pad != NULL && - fu_device_get_version (device) != NULL) { - g_autofree gchar *tmp = NULL; - tmp = g_strdup_printf ("IDE\\%s%s", name_pad, - fu_device_get_version (device)); - fu_device_add_instance_id (device, tmp); - } - if (name_pad != NULL) { - g_autofree gchar *tmp = NULL; - tmp = g_strdup_printf ("IDE\\0%s", name_pad); - fu_device_add_instance_id (device, tmp); - } + if (name != NULL && fu_device_get_guids (device)->len == 0) { + g_autofree gchar *name_pad = fu_ata_device_pad_string_for_id (name); + if (name_pad != NULL && + fu_device_get_version (device) != NULL) { + g_autofree gchar *tmp = NULL; + tmp = g_strdup_printf ("IDE\\%s%s", name_pad, + fu_device_get_version (device)); + fu_device_add_instance_id (device, tmp); + } + if (name_pad != NULL) { + g_autofree gchar *tmp = NULL; + tmp = g_strdup_printf ("IDE\\0%s", name_pad); + fu_device_add_instance_id (device, tmp); + } - /* add the name fallback */ - fu_device_add_instance_id (device, fu_device_get_name (device)); + /* add the name fallback */ + fu_device_add_instance_id (device, name); + } return TRUE; } diff --git a/plugins/ata/fu-ata-device.h b/plugins/ata/fu-ata-device.h index dffae46f6..54b639bdc 100644 --- a/plugins/ata/fu-ata-device.h +++ b/plugins/ata/fu-ata-device.h @@ -18,3 +18,5 @@ FuAtaDevice *fu_ata_device_new_from_blob (const guint8 *buf, /* for self tests */ guint8 fu_ata_device_get_transfer_mode (FuAtaDevice *self); guint16 fu_ata_device_get_transfer_blocks (FuAtaDevice *self); +void fu_ata_device_set_unknown_oui_report (FuAtaDevice *self, + gboolean enabled); diff --git a/plugins/ata/fu-plugin-ata.c b/plugins/ata/fu-plugin-ata.c index 2675ae4d2..b37260827 100644 --- a/plugins/ata/fu-plugin-ata.c +++ b/plugins/ata/fu-plugin-ata.c @@ -18,3 +18,11 @@ fu_plugin_init (FuPlugin *plugin) fu_plugin_add_udev_subsystem (plugin, "block"); fu_plugin_set_device_gtype (plugin, FU_TYPE_ATA_DEVICE); } + +gboolean +fu_plugin_device_created (FuPlugin *plugin, FuDevice *dev, GError **error) +{ + gboolean tmp = fu_plugin_get_config_value_boolean (plugin, "UnknownOuiReport"); + fu_ata_device_set_unknown_oui_report (FU_ATA_DEVICE (dev), tmp); + return TRUE; +} diff --git a/plugins/ata/fu-self-test.c b/plugins/ata/fu-self-test.c index 7614dae41..a91c4af2a 100644 --- a/plugins/ata/fu-self-test.c +++ b/plugins/ata/fu-self-test.c @@ -9,6 +9,7 @@ #include #include "fu-ata-device.h" +#include "fu-device-private.h" static void fu_ata_id_func (void) @@ -34,6 +35,36 @@ fu_ata_id_func (void) g_assert_cmpstr (fu_device_get_version (FU_DEVICE (dev)), ==, "SBFM61.2"); } +static void +fu_ata_oui_func (void) +{ + gboolean ret; + gsize sz; + g_autofree gchar *data = NULL; + g_autofree gchar *path = NULL; + g_autofree gchar *str = NULL; + g_autoptr(FuAtaDevice) dev = NULL; + g_autoptr(GError) error = NULL; + + path = g_build_filename (TESTDATADIR, "Samsung SSD 860 EVO 500GB.bin", NULL); + ret = g_file_get_contents (path, &data, &sz, &error); + g_assert_no_error (error); + g_assert (ret); + dev = fu_ata_device_new_from_blob ((guint8 *)data, sz, &error); + g_assert_no_error (error); + g_assert_nonnull (dev); + fu_ata_device_set_unknown_oui_report (dev, FALSE); + fu_device_convert_instance_ids (FU_DEVICE (dev)); + str = fu_device_to_string (FU_DEVICE (dev)); + g_debug ("%s", str); + g_assert_true (fu_device_has_guid (FU_DEVICE (dev), "OUI\\002538")); + g_assert_cmpint (fu_ata_device_get_transfer_mode (dev), ==, 0xe); + g_assert_cmpint (fu_ata_device_get_transfer_blocks (dev), ==, 0x1); + g_assert_cmpstr (fu_device_get_serial (FU_DEVICE (dev)), ==, "S3Z1NB0K862928X"); + g_assert_cmpstr (fu_device_get_name (FU_DEVICE (dev)), ==, "SSD 860 EVO 500GB"); + g_assert_cmpstr (fu_device_get_version (FU_DEVICE (dev)), ==, "RVT01B6Q"); +} + int main (int argc, char **argv) { @@ -43,6 +74,7 @@ main (int argc, char **argv) g_log_set_fatal_mask (NULL, G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL); /* tests go here */ - g_test_add_func ("/fwupd/id", fu_ata_id_func); + g_test_add_func ("/fwupd/ata/id", fu_ata_id_func); + g_test_add_func ("/fwupd/ata/oui", fu_ata_oui_func); return g_test_run (); } diff --git a/plugins/ata/meson.build b/plugins/ata/meson.build index e9aea71f6..8444bb8a7 100644 --- a/plugins/ata/meson.build +++ b/plugins/ata/meson.build @@ -6,6 +6,10 @@ install_data([ install_dir: join_paths(datadir, 'fwupd', 'quirks.d') ) +install_data(['ata.conf'], + install_dir: join_paths(sysconfdir, 'fwupd') +) + shared_module('fu_plugin_ata', fu_hash, sources : [ diff --git a/plugins/ata/tests/Samsung SSD 860 EVO 500GB.bin b/plugins/ata/tests/Samsung SSD 860 EVO 500GB.bin new file mode 100644 index 000000000..cc6a339bf Binary files /dev/null and b/plugins/ata/tests/Samsung SSD 860 EVO 500GB.bin differ