Commit de3a767a authored by Yehezkel Bernat's avatar Yehezkel Bernat Committed by Richard Hughes
Browse files

thunderbolt: handle "native" mode



Kernel v4.15 added support for native enumeration of Thunderbolt
topology. The enumeration mode affects both the BIOS and TBT FW
operation so they must agree on it. Platforms may support both modes,
native and "legacy" (or "BIOS-assist").

This change makes sure the new image is compatible with the current
controller mode (otherwise the BIOS and TBT FW will not be alligned on
it at least until next boot) and also adds a new GUID generation logic
for a controller in "native" mode so LVFS could contain 2 images, one for
the "legacy" mode and one for "native".

Signed-off-by: default avatarYehezkel Bernat <yehezkel.bernat@intel.com>
parent 8e3f9c1a
......@@ -37,3 +37,18 @@ to match it to the correct firmware.
In this case the metadata value `Thunderbolt::IsSafeMode` is set which would
allow a different plugin to add the correct GUID based on some out-of-band
device discovery. At the moment this only happens on Dell hardware.
GUID generation for LVFS
------------------------
The GUID for the controller, which must appear in the metadata when uploading an
NVM to LVFS, can be generated by a tool like `appstream-util` (with
`generate-guid` command) or by Python (with
`uuid.uuid5(uuid.NAMESPACE_DNS, 'string')`).
The format of the string used as input is "TBT-vvvvdddd", where vvvvv is the
vendor ID and dddd is the device ID, both in hex, as appear in the controller's
DROM and exposed in the relevant sysfs attributes.
If the controller is in native enumeration mode, the string "-native" is added
at the end so the format is "TBT-vvvvdddd-native".
......@@ -146,6 +146,64 @@ fu_plugin_thunderbolt_is_host (GUdevDevice *device)
return g_str_has_prefix (name, "domain");
}
static GFile *
fu_plugin_thunderbolt_find_nvmem (GUdevDevice *udevice,
gboolean active,
GError **error)
{
const gchar *nvmem_dir = active ? "nvm_active" : "nvm_non_active";
const gchar *devpath;
const gchar *name;
g_autoptr(GDir) d = NULL;
devpath = g_udev_device_get_sysfs_path (udevice);
if (G_UNLIKELY (devpath == NULL)) {
g_set_error_literal (error,
FWUPD_ERROR, FWUPD_ERROR_INTERNAL,
"Could not determine sysfs path for device");
return NULL;
}
d = g_dir_open (devpath, 0, error);
if (d == NULL)
return NULL;
while ((name = g_dir_read_name (d)) != NULL) {
if (g_str_has_prefix (name, nvmem_dir)) {
g_autoptr(GFile) parent = g_file_new_for_path (devpath);
g_autoptr(GFile) nvm_dir = g_file_get_child (parent, name);
return g_file_get_child (nvm_dir, "nvmem");
}
}
g_set_error_literal (error,
FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED,
"Could not find non-volatile memory location");
return NULL;
}
static gboolean
fu_plugin_thunderbolt_is_native (GUdevDevice *udevice, gboolean *is_native, GError **error)
{
g_autoptr(GFile) nvmem = NULL;
g_autoptr(GBytes) controller_fw = NULL;
gchar *content;
gsize length;
nvmem = fu_plugin_thunderbolt_find_nvmem (udevice, TRUE, error);
if (nvmem == NULL)
return FALSE;
if (!g_file_load_contents (nvmem, NULL, &content, &length, NULL, error))
return FALSE;
controller_fw = g_bytes_new_take (content, length);
return fu_plugin_thunderbolt_controller_is_native (controller_fw,
is_native,
error);
}
static void
fu_plugin_thunderbolt_add (FuPlugin *plugin, GUdevDevice *device)
{
......@@ -158,6 +216,7 @@ fu_plugin_thunderbolt_add (FuPlugin *plugin, GUdevDevice *device)
const gchar *devtype;
gboolean is_host;
gboolean is_safemode = FALSE;
gboolean is_native = FALSE;
guint16 did;
guint16 vid;
g_autofree gchar *id = NULL;
......@@ -221,8 +280,17 @@ fu_plugin_thunderbolt_add (FuPlugin *plugin, GUdevDevice *device)
}
}
if (!is_safemode) {
if (is_host) {
if (!fu_plugin_thunderbolt_is_native (device, &is_native, &error)) {
g_warning ("failed to get native mode status: %s", error->message);
return;
}
}
vendor_id = g_strdup_printf ("TBT:0x%04X", (guint) vid);
device_id = g_strdup_printf ("TBT-%04x%04x", (guint) vid, (guint) did);
device_id = g_strdup_printf ("TBT-%04x%04x%s",
(guint) vid,
(guint) did,
is_native ? "-native" : "");
fu_device_add_flag (dev, FWUPD_DEVICE_FLAG_UPDATABLE);
}
......@@ -331,42 +399,6 @@ udev_uevent_cb (GUdevClient *udev,
return TRUE;
}
static GFile *
fu_plugin_thunderbolt_find_nvmem (GUdevDevice *udevice,
gboolean active,
GError **error)
{
const gchar *nvmem_dir = active ? "nvm_active" : "nvm_non_active";
const gchar *devpath;
const gchar *name;
g_autoptr(GDir) d = NULL;
devpath = g_udev_device_get_sysfs_path (udevice);
if (G_UNLIKELY (devpath == NULL)) {
g_set_error_literal (error,
FWUPD_ERROR, FWUPD_ERROR_INTERNAL,
"Could not determine sysfs path for device");
return NULL;
}
d = g_dir_open (devpath, 0, error);
if (d == NULL)
return NULL;
while ((name = g_dir_read_name (d)) != NULL) {
if (g_str_has_prefix (name, nvmem_dir)) {
g_autoptr(GFile) parent = g_file_new_for_path (devpath);
g_autoptr(GFile) nvm_dir = g_file_get_child (parent, name);
return g_file_get_child (nvm_dir, "nvmem");
}
}
g_set_error_literal (error,
FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED,
"Could not find non-volatile memory location");
return NULL;
}
static FuPluginValidation
fu_plugin_thunderbolt_validate_firmware (GUdevDevice *udevice,
GBytes *blob_fw,
......
......@@ -385,6 +385,7 @@ get_host_locations (guint16 id)
{ .offset = 0x129, .len = 1, .description = "Snk1" },
{ .offset = 0x136, .len = 1, .description = "Src0", .mask = 0xF0 },
{ .offset = 0xB6, .len = 1, .description = "PA/PB (USB2)", .mask = 0xC0 },
{ .offset = 0x7B, .len = 1, .description = "Native", .mask = 0x20 },
{ 0 },
{ .offset = 0x13, .len = 1, .description = "PB", .mask = 0xCC, .section = DRAM_UCODE_SECTION },
......@@ -397,6 +398,7 @@ get_host_locations (guint16 id)
{ .offset = 0x13, .len = 1, .description = "PB", .mask = 0x44, .section = DRAM_UCODE_SECTION },
{ .offset = 0x121, .len = 1, .description = "Snk0" },
{ .offset = 0xB6, .len = 1, .description = "PA/PB (USB2)", .mask = 0xC0 },
{ .offset = 0x7B, .len = 1, .description = "Native", .mask = 0x20 },
{ 0 }
};
......@@ -583,3 +585,18 @@ fu_plugin_thunderbolt_validate_image (GBytes *controller_fw,
return hw_info->id != 0 ? VALIDATION_PASSED : UNKNOWN_DEVICE;
}
gboolean
fu_plugin_thunderbolt_controller_is_native (GBytes *controller_fw,
gboolean *is_native,
GError **error)
{
guint32 controller_sections[SECTION_COUNT] = { [DIGITAL_SECTION] = 0 };
gsize fw_size;
const guint8 *fw_data = g_bytes_get_data (controller_fw, &fw_size);
const FuThunderboltFwObject controller = { fw_data, fw_size, controller_sections };
const FuThunderboltFwLocation location = { .offset = 0x7B, .len = 1, .description = "Native", .mask = 0x20 };
return read_bool (&location, &controller, is_native, error);
}
......@@ -34,4 +34,8 @@ FuPluginValidation fu_plugin_thunderbolt_validate_image (GBytes *controller_fw,
GBytes *blob_fw,
GError **error);
gboolean fu_plugin_thunderbolt_controller_is_native (GBytes *controller_fw,
gboolean *is_native,
GError **error);
#endif /* __FU_THUNDERBOLT_IMAGE_H__ */
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment