mirror of
https://git.proxmox.com/git/fwupd
synced 2026-01-26 23:16:31 +00:00
ata: Include a vendor ID for ATA hardware
Some vendors want to ship updates for ATA hardware, but there are currently no lock-down restrictions in place for these kind of devices. There is the OUI from the WWN block which is supposed to identify the vendor, but this is not always set and so we have to be a little creative. We can match 90% of hardware using the vendor name prefix, and the last 10% can be detected with a heuristic that was the result of comparing over 900 drive models. I'm not including very old drive models, media converters, raid controllers, or external 'portable' drives as I don't think it is useful. Also, if the drive contains a Dell vendor block just hardcode this as Dell rather than trying to be clever. Also ask the user to contribute OUI values if this data is found with no quirk data as this is the only real sane way to manage this data long term. The list of OUIs can be found here: http://standards-oui.ieee.org/oui.txt
This commit is contained in:
parent
52bbae8209
commit
dbcc8e1137
@ -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}
|
||||
|
||||
4
plugins/ata/ata.conf
Normal file
4
plugins/ata/ata.conf
Normal file
@ -0,0 +1,4 @@
|
||||
[ata]
|
||||
|
||||
# ask the user to report the missing OUI in the daemon logs
|
||||
UnknownOuiReport=true
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -9,6 +9,7 @@
|
||||
#include <fwupd.h>
|
||||
|
||||
#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 ();
|
||||
}
|
||||
|
||||
@ -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 : [
|
||||
|
||||
BIN
plugins/ata/tests/Samsung SSD 860 EVO 500GB.bin
Normal file
BIN
plugins/ata/tests/Samsung SSD 860 EVO 500GB.bin
Normal file
Binary file not shown.
Loading…
Reference in New Issue
Block a user