fwupd/plugins/dell/fu-self-test.c
Richard Hughes 525f71f54b Merge the _udev_device() and _usb_device() vfuncs
There are now two 'backends' of device plug/unplug events, and there is about
to become three. Rather than just adding two more vfuncs for every backend type
define common ones that all providers can use.

Also fix up the existing in-tree plugins to use the new vfunc names and filter
on the correct GType.
2021-02-10 12:04:05 +00:00

560 lines
18 KiB
C

/*
* Copyright (C) 2015 Richard Hughes <richard@hughsie.com>
*
* SPDX-License-Identifier: LGPL-2.1+
*/
#include "config.h"
#include <fwupd.h>
#include <glib/gstdio.h>
#include <stdlib.h>
#include "fu-device-private.h"
#include "fu-plugin-private.h"
#include "fu-plugin-dell.h"
#include "fu-plugin-vfuncs.h"
typedef struct {
FuPlugin *plugin_uefi_capsule;
FuPlugin *plugin_dell;
} FuTest;
static FuDevice *
_find_device_by_id (GPtrArray *devices, const gchar *device_id)
{
for (guint i = 0; i < devices->len; i++) {
FuDevice *device = g_ptr_array_index (devices, i);
if (g_strcmp0 (fu_device_get_id (device), device_id) == 0)
return device;
}
return NULL;
}
static FuDevice *
_find_device_by_name (GPtrArray *devices, const gchar *device_id)
{
for (guint i = 0; i < devices->len; i++) {
FuDevice *device = g_ptr_array_index (devices, i);
if (g_strcmp0 (fu_device_get_name (device), device_id) == 0)
return device;
}
return NULL;
}
static void
_plugin_device_added_cb (FuPlugin *plugin, FuDevice *device, gpointer user_data)
{
GPtrArray *devices = (GPtrArray *) user_data;
if (fu_device_get_alternate_id (device) != NULL) {
FuDevice *device_alt = _find_device_by_id (devices, fu_device_get_alternate_id (device));
if (device_alt != NULL)
fu_device_set_alternate (device, device_alt);
}
g_ptr_array_add (devices, g_object_ref (device));
}
static void
fu_engine_plugin_device_register_cb (FuPlugin *plugin_dell,
FuDevice *device,
gpointer user_data)
{
FuPlugin *plugin_uefi_capsule = FU_PLUGIN (user_data);
g_autofree gchar *dbg = fu_device_to_string (device);
g_debug ("registering device: %s", dbg);
fu_plugin_runner_device_register (plugin_uefi_capsule, device);
}
static void
fu_plugin_dell_tpm_func (gconstpointer user_data)
{
FuTest *self = (FuTest *) user_data;
FuDevice *device_v12;
FuDevice *device_v20;
const guint8 fw[30] = { 'F', 'W', 0x00 };
gboolean ret;
gulong added_id;
gulong register_id;
struct tpm_status tpm_out;
const gchar *tpm_server_running = g_getenv ("TPM_SERVER_RUNNING");
g_autoptr(GBytes) blob_fw = g_bytes_new_static (fw, sizeof(fw));
g_autoptr(GError) error = NULL;
g_autoptr(GPtrArray) devices = NULL;
#ifdef HAVE_GETUID
if (tpm_server_running == NULL &&
(getuid () != 0 || geteuid () != 0)) {
g_test_skip ("TPM tests require simulated TPM2.0 running or need root access with physical TPM");
return;
}
#endif
memset (&tpm_out, 0x0, sizeof(tpm_out));
devices = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
added_id =
g_signal_connect (self->plugin_uefi_capsule, "device-added",
G_CALLBACK (_plugin_device_added_cb),
devices);
register_id =
g_signal_connect (self->plugin_dell, "device-register",
G_CALLBACK (fu_engine_plugin_device_register_cb),
self->plugin_uefi_capsule);
ret = fu_plugin_runner_coldplug (self->plugin_dell, &error);
g_assert_no_error (error);
g_assert (ret);
/* inject fake data (no TPM) */
tpm_out.ret = -2;
fu_plugin_dell_inject_fake_data (self->plugin_dell,
(guint32 *) &tpm_out, 0, 0,
NULL, FALSE);
ret = fu_plugin_dell_detect_tpm (self->plugin_dell, &error);
g_assert_no_error (error);
g_assert_false (ret);
g_assert_cmpint (devices->len, ==, 0);
/* 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_plugin_dell_inject_fake_data (self->plugin_dell,
(guint32 *) &tpm_out, 0, 0,
NULL, TRUE);
ret = fu_plugin_dell_detect_tpm (self->plugin_dell, &error);
g_assert_true (ret);
g_assert_cmpint (devices->len, ==, 2);
/* make sure 2.0 is locked */
device_v20 = _find_device_by_name (devices, "TPM 2.0");
g_assert_nonnull (device_v20);
g_assert_true (fu_device_has_flag (device_v20, FWUPD_DEVICE_FLAG_LOCKED));
/* make sure not allowed to flash 1.2 */
device_v12 = _find_device_by_name (devices, "TPM 1.2");
g_assert_nonnull (device_v12);
g_assert_false (fu_device_has_flag (device_v12, FWUPD_DEVICE_FLAG_UPDATABLE));
/* try to unlock 2.0 */
ret = fu_plugin_runner_unlock (self->plugin_uefi_capsule, device_v20, &error);
g_assert_error (error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED);
g_assert_false (ret);
g_clear_error (&error);
/* cleanup */
g_ptr_array_set_size (devices, 0);
/* inject fake data:
* - that has flashes
* - 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_plugin_dell_inject_fake_data (self->plugin_dell,
(guint32 *) &tpm_out, 0, 0,
NULL, TRUE);
ret = fu_plugin_dell_detect_tpm (self->plugin_dell, &error);
g_assert_no_error (error);
g_assert (ret);
/* make sure not allowed to flash 1.2 */
device_v12 = _find_device_by_name (devices, "TPM 1.2");
g_assert_nonnull (device_v12);
g_assert_false (fu_device_has_flag (device_v12, FWUPD_DEVICE_FLAG_UPDATABLE));
/* try to unlock 2.0 */
device_v20 = _find_device_by_name (devices, "TPM 2.0");
g_assert_nonnull (device_v20);
ret = fu_plugin_runner_unlock (self->plugin_uefi_capsule, device_v20, &error);
g_assert_error (error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED);
g_assert_false (ret);
g_clear_error (&error);
/* cleanup */
g_ptr_array_set_size (devices, 0);
/* 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_plugin_dell_inject_fake_data (self->plugin_dell,
(guint32 *) &tpm_out, 0, 0,
NULL, TRUE);
ret = fu_plugin_dell_detect_tpm (self->plugin_dell, &error);
g_assert_no_error (error);
g_assert (ret);
/* make sure allowed to flash 1.2 but not 2.0 */
device_v12 = _find_device_by_name (devices, "TPM 1.2");
g_assert_nonnull (device_v12);
g_assert_true (fu_device_has_flag (device_v12, FWUPD_DEVICE_FLAG_UPDATABLE));
device_v20 = _find_device_by_name (devices, "TPM 2.0");
g_assert_nonnull (device_v20);
g_assert_false (fu_device_has_flag (device_v20, FWUPD_DEVICE_FLAG_UPDATABLE));
/* try to unlock 2.0 */
ret = fu_plugin_runner_unlock (self->plugin_uefi_capsule, device_v20, &error);
g_assert_no_error (error);
g_assert (ret);
/* make sure no longer allowed to flash 1.2 but can flash 2.0 */
g_assert_false (fu_device_has_flag (device_v12, FWUPD_DEVICE_FLAG_UPDATABLE));
g_assert_true (fu_device_has_flag (device_v20, FWUPD_DEVICE_FLAG_UPDATABLE));
/* cleanup */
g_ptr_array_set_size (devices, 0);
/* 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_plugin_dell_inject_fake_data (self->plugin_dell,
(guint32 *) &tpm_out, 0, 0,
NULL, TRUE);
ret = fu_plugin_dell_detect_tpm (self->plugin_dell, &error);
g_assert_no_error (error);
g_assert (ret);
/* make sure allowed to flash 2.0 but not 1.2 */
device_v20 = _find_device_by_name (devices, "TPM 2.0");
g_assert_nonnull (device_v20);
g_assert_true (fu_device_has_flag (device_v20, FWUPD_DEVICE_FLAG_UPDATABLE));
device_v12 = _find_device_by_name (devices, "TPM 1.2");
g_assert_nonnull (device_v12);
g_assert_false (fu_device_has_flag (device_v12, FWUPD_DEVICE_FLAG_UPDATABLE));
/* With one flash left we need an override */
ret = fu_plugin_runner_update (self->plugin_uefi_capsule, device_v20, blob_fw,
FWUPD_INSTALL_FLAG_NONE, &error);
g_assert_error (error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED);
g_assert_false (ret);
g_clear_error (&error);
/* test override */
ret = fu_plugin_runner_update (self->plugin_uefi_capsule, device_v20, blob_fw,
FWUPD_INSTALL_FLAG_FORCE, &error);
g_assert_no_error (error);
g_assert (ret);
/* all */
g_signal_handler_disconnect (self->plugin_uefi_capsule, added_id);
g_signal_handler_disconnect (self->plugin_dell, register_id);
}
static void
fu_plugin_dell_dock_func (gconstpointer user_data)
{
FuTest *self = (FuTest *) user_data;
gboolean ret;
guint32 out[4] = { 0x0, 0x0, 0x0, 0x0 };
DOCK_UNION buf;
DOCK_INFO *dock_info;
g_autoptr(GError) error = NULL;
g_autoptr(GPtrArray) devices = NULL;
g_autoptr(FuUsbDevice) fake_usb_device = fu_usb_device_new (NULL);
gulong added_id;
gulong register_id;
devices = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
added_id =
g_signal_connect (self->plugin_uefi_capsule, "device-added",
G_CALLBACK (_plugin_device_added_cb),
devices);
register_id =
g_signal_connect (self->plugin_dell, "device-register",
G_CALLBACK (fu_engine_plugin_device_register_cb),
self->plugin_uefi_capsule);
/* make sure bad device doesn't trigger this */
fu_plugin_dell_inject_fake_data (self->plugin_dell,
(guint32 *) &out,
0x1234, 0x4321, NULL, FALSE);
ret = fu_plugin_backend_device_added (self->plugin_dell,
FU_DEVICE (fake_usb_device),
&error);
g_assert_false (ret);
g_clear_error (&error);
g_assert_cmpint (devices->len, ==, 0);
/* inject a USB dongle matching correct VID/PID */
out[0] = 0;
out[1] = 0;
fu_plugin_dell_inject_fake_data (self->plugin_dell,
(guint32 *) &out,
DOCK_NIC_VID, DOCK_NIC_PID,
NULL, FALSE);
ret = fu_plugin_backend_device_added (self->plugin_dell,
FU_DEVICE (fake_usb_device),
&error);
g_assert_true (ret);
g_clear_error (&error);
g_assert_cmpint (devices->len, ==, 0);
/* inject valid TB16 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_TB16;
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_plugin_dell_inject_fake_data (self->plugin_dell,
(guint32 *) &out,
DOCK_NIC_VID, DOCK_NIC_PID,
buf.buf, FALSE);
ret = fu_plugin_backend_device_added (self->plugin_dell,
FU_DEVICE (fake_usb_device),
NULL);
g_assert (ret);
g_assert_cmpint (devices->len, ==, 4);
g_ptr_array_set_size (devices, 0);
g_free (buf.record);
/* inject valid TB16 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_TB16;
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_plugin_dell_inject_fake_data (self->plugin_dell,
(guint32 *) &out,
DOCK_NIC_VID, DOCK_NIC_PID,
buf.buf, FALSE);
ret = fu_plugin_backend_device_added (self->plugin_dell,
FU_DEVICE (fake_usb_device),
NULL);
g_assert (ret);
g_assert_cmpint (devices->len, ==, 3);
g_ptr_array_set_size (devices, 0);
g_free (buf.record);
/* 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_plugin_dell_inject_fake_data (self->plugin_dell,
(guint32 *) &out,
DOCK_NIC_VID, DOCK_NIC_PID,
buf.buf, FALSE);
ret = fu_plugin_backend_device_added (self->plugin_dell,
FU_DEVICE (fake_usb_device),
&error);
g_assert (ret);
g_assert_no_error (error);
g_assert_cmpint (devices->len, ==, 3);
g_ptr_array_set_size (devices, 0);
g_free (buf.record);
/* 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_plugin_dell_inject_fake_data (self->plugin_dell,
(guint32 *) &out,
DOCK_NIC_VID, DOCK_NIC_PID,
buf.buf, FALSE);
ret = fu_plugin_backend_device_added (self->plugin_dell,
FU_DEVICE (fake_usb_device),
&error);
g_assert (ret);
g_assert_no_error (error);
g_assert_cmpint (devices->len, ==, 2);
g_ptr_array_set_size (devices, 0);
g_free (buf.record);
/* 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_plugin_dell_inject_fake_data (self->plugin_dell,
(guint32 *) &out,
DOCK_NIC_VID, DOCK_NIC_PID,
buf.buf, FALSE);
ret = fu_plugin_backend_device_added (self->plugin_dell,
FU_DEVICE (fake_usb_device),
&error);
g_assert_false (ret);
g_assert_cmpint (devices->len, ==, 0);
g_free (buf.record);
/* all */
g_signal_handler_disconnect (self->plugin_uefi_capsule, added_id);
g_signal_handler_disconnect (self->plugin_dell, register_id);
}
static void
fu_test_self_init (FuTest *self)
{
gboolean ret;
g_autoptr(GError) error = NULL;
g_autofree gchar *pluginfn_uefi = NULL;
g_autofree gchar *pluginfn_dell = NULL;
self->plugin_uefi_capsule = fu_plugin_new ();
pluginfn_uefi = g_build_filename (PLUGINBUILDDIR, "..", "uefi-capsule",
"libfu_plugin_uefi_capsule." G_MODULE_SUFFIX,
NULL);
ret = fu_plugin_open (self->plugin_uefi_capsule, pluginfn_uefi, &error);
g_assert_no_error (error);
g_assert (ret);
ret = fu_plugin_runner_startup (self->plugin_uefi_capsule, &error);
g_assert_no_error (error);
g_assert (ret);
self->plugin_dell = fu_plugin_new ();
pluginfn_dell = g_build_filename (PLUGINBUILDDIR,
"libfu_plugin_dell." G_MODULE_SUFFIX,
NULL);
ret = fu_plugin_open (self->plugin_dell, pluginfn_dell, &error);
g_assert_no_error (error);
g_assert (ret);
ret = fu_plugin_runner_startup (self->plugin_dell, &error);
g_assert_no_error (error);
g_assert (ret);
}
static void
fu_test_self_free (FuTest *self)
{
if (self->plugin_uefi_capsule != NULL)
g_object_unref (self->plugin_uefi_capsule);
if (self->plugin_dell != NULL)
g_object_unref (self->plugin_dell);
g_free (self);
}
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunused-function"
G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuTest, fu_test_self_free)
#pragma clang diagnostic pop
int
main (int argc, char **argv)
{
g_autofree gchar *sysfsdir = NULL;
g_autoptr(FuTest) self = g_new0 (FuTest, 1);
g_test_init (&argc, &argv, NULL);
/* change path */
g_setenv ("FWUPD_SYSFSFWDIR", TESTDATADIR, TRUE);
/* change behavior */
sysfsdir = fu_common_get_path (FU_PATH_KIND_SYSFSDIR_FW);
g_setenv ("FWUPD_UEFI_ESP_PATH", sysfsdir, TRUE);
g_setenv ("FWUPD_UEFI_TEST", "1", TRUE);
g_setenv ("FWUPD_DELL_FAKE_SMBIOS", "1", FALSE);
/* only critical and error are fatal */
g_log_set_fatal_mask (NULL, G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL);
g_assert_cmpint (g_mkdir_with_parents ("/tmp/fwupd-self-test/var/lib/fwupd", 0755), ==, 0);
/* tests go here */
fu_test_self_init (self);
g_test_add_data_func ("/fwupd/plugin{dell:tpm}", self, fu_plugin_dell_tpm_func);
g_test_add_data_func ("/fwupd/plugin{dell:dock}", self, fu_plugin_dell_dock_func);
return g_test_run ();
}