Commit 958ead69 authored by Mario Limonciello's avatar Mario Limonciello
Browse files

Add Dell TPM and TB15/WD15 support via new Dell provider.

This provider will provide support for items that can be flashed
as capsules but aren't present in the ESRT table.

The MST hub and TBT NVM are not yet updatable, but GUIDs are
created to represent them when they are.
parent c8646aff
......@@ -54,6 +54,7 @@ MAINTAINERCLEANFILES = \
DISTCHECK_CONFIGURE_FLAGS = \
--enable-colorhug \
--enable-uefi \
--enable-dell \
--with-udevrulesdir=$$dc_install_base/$(udevrulesdir) \
--with-systemdunitdir=$$dc_install_base/$(systemdunitdir)
......
......@@ -46,6 +46,18 @@ and compiling it with libsmbios support.
When fwupd and fwupdate have been compiled with this support you will
be able to enable UEFI support on the device by using the `unlock` command.
Dell Support
----------------
This allows installing Dell capsules that are not part of the ESRT table.
For Dell support you will need libsmbios_c version 2.3.0 or later and
efivar.
* source: http://linux.dell.com/cgi-bin/cgit.cgi/libsmbios.git/
* rpms: https://apps.fedoraproject.org/packages/libsmbios
* debs (Debian): http://tracker.debian.org/pkg/libsmbios
* debs (Ubuntu): http://launchpad.net/ubuntu/+source/libsmbios
Raspberry Pi support
--------------------
......
......@@ -181,6 +181,26 @@ if test x$enable_uefi != xno; then
fi
AM_CONDITIONAL(HAVE_UEFI, test x$enable_uefi = xyes)
# Dell Non ESRT capsule support
AC_ARG_ENABLE(dell, AS_HELP_STRING([--enable-dell],[Enable Dell non-ESRT capsule support]),
enable_dell=$enableval, enable_dell=yes)
if test x$enable_dell != xno; then
PKG_CHECK_MODULES(UEFI, fwup >= 0.5)
PKG_CHECK_MODULES(LIBSMBIOS, libsmbios_c >= 2.3.0)
PKG_CHECK_MODULES(EFIVAR, efivar)
AC_DEFINE(HAVE_DELL,1,[Use Dell non-ESRT capsule support])
# check for ability to change GUID
PKG_CHECK_MODULES(UEFI_GUID, fwup >= 0.6,
has_uefi_guid=yes,
has_uefi_guid=no)
if test x$has_uefi_guid = xyes; then
AC_DEFINE(HAVE_UEFI_GUID,1,[Use UEFI GUID override])
fi
fi
AM_CONDITIONAL(HAVE_DELL, test x$enable_dell = xyes)
# systemd support
AC_ARG_WITH([systemdunitdir],
AS_HELP_STRING([--with-systemdunitdir=DIR], [Directory for systemd service files]),
......
......@@ -198,6 +198,26 @@ fu_self_test_CFLAGS = \
-DLOCALSTATEDIR=\"/tmp/fwupd-self-test/var\" \
$(WARNINGFLAGS_C)
if HAVE_DELL
fwupd_SOURCES += \
fu-provider-dell.c \
fu-provider-dell.h
AM_CPPFLAGS += \
$(LIBSMBIOS_CFLAGS) \
$(EFIVAR_CFLAGS)
fwupd_LDADD += \
$(LIBSMBIOS_LIBS) \
$(EFIVAR_LIBS)
fu_self_test_LDADD += \
$(GUSB_LIBS) \
$(UEFI_LIBS) \
$(LIBSMBIOS_LIBS) \
$(EFIVAR_LIBS)
fu_self_test_SOURCES += \
fu-provider-dell.c \
fu-provider-dell.h
endif
install-data-hook:
if test -w $(DESTDIR)$(prefix)/; then \
mkdir -p $(DESTDIR)$(localstatedir)/lib/fwupd; \
......
......@@ -54,6 +54,9 @@
#ifdef HAVE_UEFI
#include "fu-provider-uefi.h"
#endif
#ifdef HAVE_DELL
#include "fu-provider-dell.h"
#endif
#ifndef PolkitAuthorizationResult_autoptr
G_DEFINE_AUTOPTR_CLEANUP_FUNC(PolkitAuthorizationResult, g_object_unref)
......@@ -2380,6 +2383,9 @@ main (int argc, char *argv[])
#ifdef HAVE_UEFI
fu_main_add_provider (priv, fu_provider_uefi_new ());
#endif
#ifdef HAVE_DELL
fu_main_add_provider (priv, fu_provider_dell_new ());
#endif
/* last as least priority */
fu_main_add_provider (priv, fu_provider_usb_new ());
......
This diff is collapsed.
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
*
* Copyright (C) 2016 Mario Limonciello <mario_limonciello@dell.com>
*
* Licensed under the GNU General Public License Version 2
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef __FU_PROVIDER_DELL_H
#define __FU_PROVIDER_DELL_H
#include <gusb.h>
#include "fu-device.h"
#include "fu-provider.h"
G_BEGIN_DECLS
#define FU_TYPE_PROVIDER_DELL (fu_provider_dell_get_type ())
G_DECLARE_DERIVABLE_TYPE (FuProviderDell, fu_provider_dell, FU, PROVIDER_DELL, FuProvider)
struct _FuProviderDellClass
{
FuProviderClass parent_class;
};
FuProvider *fu_provider_dell_new (void);
void
fu_provider_dell_inject_fake_data (FuProviderDell *provider_dell,
guint32 *output, guint16 vid, guint16 pid,
guint8 *buf);
gboolean
fu_provider_dell_detect_tpm (FuProvider *provider, GError **error);
void
fu_provider_dell_device_added_cb (GUsbContext *ctx,
GUsbDevice *device,
FuProviderDell *provider_dell);
void
fu_provider_dell_device_removed_cb (GUsbContext *ctx,
GUsbDevice *device,
FuProviderDell *provider_dell);
G_END_DECLS
#endif /* __FU_PROVIDER_DELL_H */
......@@ -33,6 +33,10 @@
#include "fu-provider-rpi.h"
#include "fu-rom.h"
#ifdef HAVE_DELL
#include "fu-provider-dell.h"
#endif
static gchar *
fu_test_get_filename (const gchar *filename)
{
......@@ -278,6 +282,399 @@ fu_provider_func (void)
g_unlink (pending_cap);
}
#ifdef HAVE_DELL
static void
fu_provider_dell_tpm_func (void)
{
gboolean ret;
guint cnt = 0;
struct tpm_status tpm_out;
FwupdDeviceFlags flags = 0;
FuDevice *device_alt = NULL;
g_autoptr(GError) error = NULL;
g_autoptr(FuDevice) device = NULL;
g_autoptr(FuProvider) provider = NULL;
g_setenv ("FWUPD_DELL_FAKE_SMBIOS", "1", FALSE);
provider = fu_provider_dell_new ();
ret = fu_provider_coldplug(provider, &error);
g_signal_connect (provider, "device-added",
G_CALLBACK (_provider_device_added_cb),
&device);
g_signal_connect (provider, "status-changed",
G_CALLBACK (_provider_status_changed_cb),
&cnt);
g_assert_no_error (error);
g_assert (ret);
/* inject fake data (no TPM) */
tpm_out.ret = -2;
fu_provider_dell_inject_fake_data (FU_PROVIDER_DELL(provider),
(guint32 *) &tpm_out, 0, 0, NULL);
ret = fu_provider_dell_detect_tpm (provider, &error);
g_assert_no_error (error);
g_assert (!ret);
/* inject fake data:
* - that is out of flashes
* - no ownership
* - TPM 1.2
* dev will be the locked 2.0, alt will be the orig 1.2
*/
tpm_out.ret = 0;
tpm_out.fw_version = 0;
tpm_out.status = TPM_EN_MASK | (TPM_1_2_MODE << 8);
tpm_out.flashes_left = 0;
fu_provider_dell_inject_fake_data (FU_PROVIDER_DELL(provider),
(guint32 *) &tpm_out, 0, 0, NULL);
ret = fu_provider_dell_detect_tpm (provider, &error);
device_alt = fu_device_get_alternate (device);
g_assert_no_error (error);
g_assert (ret);
g_assert (device != NULL);
g_assert (device_alt != NULL);
/* make sure 2.0 is locked */
flags = fu_device_get_flags (device);
g_assert_cmpint (flags & FU_DEVICE_FLAG_LOCKED, >, 0);
/* make sure not allowed to flash 1.2 */
flags = fu_device_get_flags (device_alt);
g_assert_cmpint (flags & FU_DEVICE_FLAG_ALLOW_OFFLINE, !=, 1);
/* try to unlock 2.0 */
ret = fu_provider_unlock (provider, device, &error);
g_assert_error (error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED);
g_assert (!ret);
g_clear_error (&error);
/* cleanup */
fu_provider_device_remove (provider, device_alt);
fu_provider_device_remove (provider, device);
/* inject fake data:
* - that hasflashes
* - owned
* - TPM 1.2
* dev will be the locked 2.0, alt will be the orig 1.2
*/
tpm_out.status = TPM_EN_MASK | TPM_OWN_MASK | (TPM_1_2_MODE << 8);
tpm_out.flashes_left = 125;
fu_provider_dell_inject_fake_data (FU_PROVIDER_DELL(provider),
(guint32 *) &tpm_out, 0, 0, NULL);
ret = fu_provider_dell_detect_tpm (provider, &error);
device_alt = fu_device_get_alternate (device);
g_assert_no_error (error);
g_assert (ret);
g_assert (device != NULL);
g_assert (device_alt != NULL);
/* make sure allowed to flash 1.2 */
flags = fu_device_get_flags (device_alt);
g_assert_cmpint(flags & FU_DEVICE_FLAG_ALLOW_OFFLINE, >, 0);
/* try to unlock 2.0 */
ret = fu_provider_unlock (provider, device, &error);
g_assert_error (error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED);
g_assert (!ret);
g_clear_error (&error);
/* cleanup */
fu_provider_device_remove (provider, device_alt);
fu_provider_device_remove (provider, device);
/* inject fake data:
* - that has flashes
* - not owned
* - TPM 1.2
* dev will be the locked 2.0, alt will be the orig 1.2
*/
tpm_out.status = TPM_EN_MASK | (TPM_1_2_MODE << 8);
tpm_out.flashes_left = 125;
fu_provider_dell_inject_fake_data (FU_PROVIDER_DELL(provider),
(guint32 *) &tpm_out, 0, 0, NULL);
ret = fu_provider_dell_detect_tpm (provider, &error);
device_alt = fu_device_get_alternate (device);
g_assert_no_error (error);
g_assert (ret);
g_assert (device != NULL);
g_assert (device_alt != NULL);
/* make sure allowed to flash 1.2 but not 2.0 */
flags = fu_device_get_flags (device_alt);
g_assert_cmpint (flags & FU_DEVICE_FLAG_ALLOW_OFFLINE, >, 0);
flags = fu_device_get_flags (device);
g_assert_cmpint (flags & FU_DEVICE_FLAG_ALLOW_OFFLINE, ==, 0);
/* try to unlock 2.0 */
ret = fu_provider_unlock (provider, device, &error);
g_assert_no_error (error);
g_assert (ret);
/* make sure no longer allowed to flash 1.2 but can flash 2.0 */
flags = fu_device_get_flags (device_alt);
g_assert_cmpint (flags & FU_DEVICE_FLAG_ALLOW_OFFLINE, ==, 0);
flags = fu_device_get_flags (device);
g_assert_cmpint (flags & FU_DEVICE_FLAG_ALLOW_OFFLINE, >, 0);
/* cleanup */
fu_provider_device_remove (provider, device_alt);
fu_provider_device_remove (provider, device);
/* inject fake data:
* - that has 1 flash left
* - not owned
* - TPM 2.0
* dev will be the locked 1.2, alt will be the orig 2.0
*/
tpm_out.status = TPM_EN_MASK | (TPM_2_0_MODE << 8);
tpm_out.flashes_left = 1;
fu_provider_dell_inject_fake_data (FU_PROVIDER_DELL(provider),
(guint32 *) &tpm_out, 0, 0, NULL);
ret = fu_provider_dell_detect_tpm (provider, &error);
device_alt = fu_device_get_alternate (device);
g_assert_no_error (error);
g_assert (ret);
g_assert (device != NULL);
g_assert (device_alt != NULL);
/* make sure allowed to flash 2.0 but not 1.2 */
flags = fu_device_get_flags (device_alt);
g_assert_cmpint (flags & FU_DEVICE_FLAG_ALLOW_OFFLINE, >, 0);
flags = fu_device_get_flags (device);
g_assert_cmpint (flags & FU_DEVICE_FLAG_ALLOW_OFFLINE, ==, 0);
/* With one flash left we need an override */
ret = fu_provider_update (provider, device_alt, NULL, NULL, NULL,
FWUPD_INSTALL_FLAG_OFFLINE, &error);
g_assert (!ret);
g_assert_error (error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED);
g_clear_error (&error);
/* test override */
ret = fu_provider_update (provider, device_alt, NULL, NULL, NULL,
FWUPD_INSTALL_FLAG_FORCE |
FWUPD_INSTALL_FLAG_OFFLINE, &error);
g_assert (ret);
g_assert_no_error (error);
/* cleanup */
fu_provider_device_remove (provider, device_alt);
fu_provider_device_remove (provider, device);
}
static void
fu_provider_dell_dock_func (void)
{
gboolean ret;
guint cnt = 0;
guint32 out[4];
INFO_UNION buf;
DOCK_INFO *dock_info;
g_autoptr(GError) error = NULL;
g_autoptr(FuDevice) device = NULL;
g_autoptr(FuProvider) provider = NULL;
g_setenv ("FWUPD_DELL_FAKE_SMBIOS", "1", FALSE);
provider = fu_provider_dell_new ();
ret = fu_provider_coldplug (provider, &error);
g_signal_connect (provider, "device-added",
G_CALLBACK (_provider_device_added_cb),
&device);
g_signal_connect (provider, "status-changed",
G_CALLBACK (_provider_status_changed_cb),
&cnt);
g_assert_no_error (error);
g_assert (ret);
/* make sure bad device doesn't trigger this */
fu_provider_dell_inject_fake_data (FU_PROVIDER_DELL(provider),
(guint32 *) &out,
0x1234, 0x4321, NULL);
fu_provider_dell_device_added_cb (NULL, NULL, FU_PROVIDER_DELL(provider));
g_assert (device == NULL);
/* inject a USB dongle matching correct VID/PID */
out[0] = 0;
out[1] = 0;
fu_provider_dell_inject_fake_data (FU_PROVIDER_DELL(provider),
(guint32 *) &out,
DOCK_NIC_VID, DOCK_NIC_PID, NULL);
fu_provider_dell_device_added_cb (NULL, NULL, FU_PROVIDER_DELL(provider));
g_assert (device == NULL);
/* inject valid TB15 dock w/ invalid flash pkg version */
buf.record = g_malloc0(sizeof(DOCK_INFO_RECORD));
dock_info = &buf.record->dock_info;
buf.record->dock_info_header.dir_version = 1;
buf.record->dock_info_header.dock_type = DOCK_TYPE_TB15;
memcpy (dock_info->dock_description,
"BME_Dock", 8);
dock_info->flash_pkg_version = 0x00ffffff;
dock_info->cable_type = CABLE_TYPE_TBT;
dock_info->location = 2;
dock_info->component_count = 4;
dock_info->components[0].fw_version = 0x00ffffff;
memcpy (dock_info->components[0].description,
"Dock1,EC,MIPS32,BME_Dock,0 :Query 2 0 2 1 0", 43);
dock_info->components[1].fw_version = 0x10201;
memcpy (dock_info->components[1].description,
"Dock1,PC,TI,BME_Dock,0 :Query 2 1 0 1 0", 39);
dock_info->components[2].fw_version = 0x10201;
memcpy (dock_info->components[2].description,
"Dock1,PC,TI,BME_Dock,1 :Query 2 1 0 1 1", 39);
dock_info->components[3].fw_version = 0x00ffffff;
memcpy (dock_info->components[3].description,
"Dock1,Cable,Cyp,TBT_Cable,0 :Query 2 2 2 3 0", 44);
out[0] = 0;
out[1] = 1;
fu_provider_dell_inject_fake_data (FU_PROVIDER_DELL(provider),
(guint32 *) &out,
DOCK_NIC_VID, DOCK_NIC_PID,
buf.buf);
fu_provider_dell_device_added_cb (NULL, NULL,
FU_PROVIDER_DELL(provider));
g_assert (device != NULL);
device = NULL;
g_free (buf.record);
fu_provider_dell_device_removed_cb (NULL, NULL,
FU_PROVIDER_DELL(provider));
/* inject valid TB15 dock w/ older system EC */
buf.record = g_malloc0(sizeof(DOCK_INFO_RECORD));
dock_info = &buf.record->dock_info;
buf.record->dock_info_header.dir_version = 1;
buf.record->dock_info_header.dock_type = DOCK_TYPE_TB15;
memcpy (dock_info->dock_description,
"BME_Dock", 8);
dock_info->flash_pkg_version = 0x43;
dock_info->cable_type = CABLE_TYPE_TBT;
dock_info->location = 2;
dock_info->component_count = 4;
dock_info->components[0].fw_version = 0xffffffff;
memcpy (dock_info->components[0].description,
"Dock1,EC,MIPS32,BME_Dock,0 :Query 2 0 2 1 0", 43);
dock_info->components[1].fw_version = 0x10211;
memcpy (dock_info->components[1].description,
"Dock1,PC,TI,BME_Dock,0 :Query 2 1 0 1 0", 39);
dock_info->components[2].fw_version = 0x10212;
memcpy (dock_info->components[2].description,
"Dock1,PC,TI,BME_Dock,1 :Query 2 1 0 1 1", 39);
dock_info->components[3].fw_version = 0xffffffff;
memcpy (dock_info->components[3].description,
"Dock1,Cable,Cyp,TBT_Cable,0 :Query 2 2 2 3 0", 44);
out[0] = 0;
out[1] = 1;
fu_provider_dell_inject_fake_data (FU_PROVIDER_DELL(provider),
(guint32 *) &out,
DOCK_NIC_VID, DOCK_NIC_PID,
buf.buf);
fu_provider_dell_device_added_cb (NULL, NULL,
FU_PROVIDER_DELL(provider));
g_assert (device != NULL);
device = NULL;
g_free (buf.record);
fu_provider_dell_device_removed_cb (NULL, NULL,
FU_PROVIDER_DELL(provider));
/* inject valid WD15 dock w/ invalid flash pkg version */
buf.record = g_malloc0(sizeof(DOCK_INFO_RECORD));
dock_info = &buf.record->dock_info;
buf.record->dock_info_header.dir_version = 1;
buf.record->dock_info_header.dock_type = DOCK_TYPE_WD15;
memcpy (dock_info->dock_description,
"IE_Dock", 7);
dock_info->flash_pkg_version = 0x00ffffff;
dock_info->cable_type = CABLE_TYPE_LEGACY;
dock_info->location = 2;
dock_info->component_count = 3;
dock_info->components[0].fw_version = 0x00ffffff;
memcpy (dock_info->components[0].description,
"Dock1,EC,MIPS32,IE_Dock,0 :Query 2 0 2 2 0", 42);
dock_info->components[1].fw_version = 0x00ffffff;
memcpy (dock_info->components[1].description,
"Dock1,PC,TI,IE_Dock,0 :Query 2 1 0 2 0", 38);
dock_info->components[2].fw_version = 0x00ffffff;
memcpy (dock_info->components[2].description,
"Dock1,Cable,Cyp,IE_Cable,0 :Query 2 2 2 1 0", 43);
out[0] = 0;
out[1] = 1;
fu_provider_dell_inject_fake_data (FU_PROVIDER_DELL(provider),
(guint32 *) &out,
DOCK_NIC_VID, DOCK_NIC_PID,
buf.buf);
fu_provider_dell_device_added_cb (NULL, NULL,
FU_PROVIDER_DELL(provider));
g_assert (device != NULL);
device = NULL;
g_free (buf.record);
fu_provider_dell_device_removed_cb (NULL, NULL,
FU_PROVIDER_DELL(provider));
/* inject valid WD15 dock w/ older system EC */
buf.record = g_malloc0(sizeof(DOCK_INFO_RECORD));
dock_info = &buf.record->dock_info;
buf.record->dock_info_header.dir_version = 1;
buf.record->dock_info_header.dock_type = DOCK_TYPE_WD15;
memcpy (dock_info->dock_description,
"IE_Dock", 7);
dock_info->flash_pkg_version = 0x43;
dock_info->cable_type = CABLE_TYPE_LEGACY;
dock_info->location = 2;
dock_info->component_count = 3;
dock_info->components[0].fw_version = 0xffffffff;
memcpy (dock_info->components[0].description,
"Dock1,EC,MIPS32,IE_Dock,0 :Query 2 0 2 2 0", 42);
dock_info->components[1].fw_version = 0x10108;
memcpy (dock_info->components[1].description,
"Dock1,PC,TI,IE_Dock,0 :Query 2 1 0 2 0", 38);
dock_info->components[2].fw_version = 0xffffffff;
memcpy (dock_info->components[2].description,
"Dock1,Cable,Cyp,IE_Cable,0 :Query 2 2 2 1 0", 43);
out[0] = 0;
out[1] = 1;
fu_provider_dell_inject_fake_data (FU_PROVIDER_DELL(provider),
(guint32 *) &out,
DOCK_NIC_VID, DOCK_NIC_PID,
buf.buf);
fu_provider_dell_device_added_cb (NULL, NULL,
FU_PROVIDER_DELL(provider));
g_assert (device != NULL);
device = NULL;
g_free (buf.record);
fu_provider_dell_device_removed_cb (NULL, NULL,
FU_PROVIDER_DELL(provider));
/* inject an invalid future dock */
buf.record = g_malloc0(sizeof(DOCK_INFO_RECORD));
dock_info = &buf.record->dock_info;
buf.record->dock_info_header.dir_version = 1;
buf.record->dock_info_header.dock_type = 50;
memcpy (dock_info->dock_description,
"Future!", 8);
dock_info->flash_pkg_version = 0x00ffffff;
dock_info->cable_type = CABLE_TYPE_UNIV;
dock_info->location = 2;
dock_info->component_count = 1;
dock_info->components[0].fw_version = 0x00ffffff;
memcpy (dock_info->components[0].description,
"Dock1,EC,MIPS32,FUT_Dock,0 :Query 2 0 2 2 0", 43);
out[0] = 0;
out[1] = 1;
fu_provider_dell_inject_fake_data (FU_PROVIDER_DELL(provider),
(guint32 *) &out,
DOCK_NIC_VID, DOCK_NIC_PID,
buf.buf);
fu_provider_dell_device_added_cb (NULL, NULL,
FU_PROVIDER_DELL(provider));
g_assert (device == NULL);
g_free (buf.record);
}
#endif
static void
fu_provider_rpi_func (void)
{
......@@ -482,7 +879,10 @@ main (int argc, char **argv)
g_test_add_func ("/fwupd/pending", fu_pending_func);
g_test_add_func ("/fwupd/provider", fu_provider_func);
g_test_add_func ("/fwupd/provider{rpi}", fu_provider_rpi_func);
#ifdef HAVE_DELL
g_test_add_func ("/fwupd/provider{dell:tpm}", fu_provider_dell_tpm_func);
g_test_add_func ("/fwupd/provider{dell:dock}", fu_provider_dell_dock_func);
#endif
g_test_add_func ("/fwupd/keyring", fu_keyring_func);
return g_test_run ();
}
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