superio: Add support for reading the device checksum

This commit is contained in:
Richard Hughes 2019-03-22 12:11:22 +00:00
parent 4702dfe0a7
commit 0573febb0b
3 changed files with 186 additions and 0 deletions

View File

@ -105,3 +105,51 @@ fu_plugin_coldplug (FuPlugin *plugin, GError **error)
}
return TRUE;
}
gboolean
fu_plugin_verify_attach (FuPlugin *plugin, FuDevice *device, GError **error)
{
g_autoptr(FuDeviceLocker) locker = NULL;
locker = fu_device_locker_new (device, error);
if (locker == NULL)
return FALSE;
return fu_device_attach (device, error);
}
gboolean
fu_plugin_verify_detach (FuPlugin *plugin, FuDevice *device, GError **error)
{
g_autoptr(FuDeviceLocker) locker = NULL;
if (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER))
return TRUE;
locker = fu_device_locker_new (device, error);
if (locker == NULL)
return FALSE;
return fu_device_detach (device, error);
}
gboolean
fu_plugin_verify (FuPlugin *plugin, FuDevice *device,
FuPluginVerifyFlags flags, GError **error)
{
g_autoptr(GBytes) fw = NULL;
g_autoptr(FuDeviceLocker) locker = NULL;
GChecksumType checksum_types[] = {
G_CHECKSUM_SHA1,
G_CHECKSUM_SHA256,
0 };
/* get data */
locker = fu_device_locker_new (device, error);
if (locker == NULL)
return FALSE;
fw = fu_device_read_firmware (device, error);
if (fw == NULL)
return FALSE;
for (guint i = 0; checksum_types[i] != 0; i++) {
g_autofree gchar *hash = NULL;
hash = g_compute_checksum_for_bytes (checksum_types[i], fw);
fu_device_add_checksum (device, hash);
}
return TRUE;
}

View File

@ -35,6 +35,27 @@ G_BEGIN_DECLS
#define SIO_DEPTH2_I2EC_ADDRH 0x11
#define SIO_DEPTH2_I2EC_DATA 0x12
/*
* The PMC is a communication channel used between the host and the EC.
* Compatible mode uses four registers:
*
* Name | EC | HOST | ADDR
* _____________________|_______________|_______________|______
* PMDIR | RO | WO | 0x62
* PMDOR | WO | RO | 0x62
* PMCMDR | RO | RO | 0x66
* PMSTR | RO | RO | 0x66
*/
#define SIO_EC_PMC_PM1STS 0x00
#define SIO_EC_PMC_PM1DO 0x01
#define SIO_EC_PMC_PM1DOSCI 0x02
#define SIO_EC_PMC_PM1DOCMI 0x03
#define SIO_EC_PMC_PM1DI 0x04
#define SIO_EC_PMC_PM1DISCI 0x05
#define SIO_EC_PMC_PM1CTL 0x06
#define SIO_EC_PMC_PM1IC 0x07
#define SIO_EC_PMC_PM1IE 0x08
/* SPI commands */
#define SIO_SPI_CMD_READ 0x03
#define SIO_SPI_CMD_HS_READ 0x0b

View File

@ -121,6 +121,122 @@ fu_superio_it89_device_setup (FuSuperioDevice *self, GError **error)
return TRUE;
}
static gboolean
fu_superio_it89_device_ec_pm1do_sci (FuSuperioDevice *self, guint8 val, GError **error)
{
if (!fu_superio_device_ec_write1 (self, SIO_EC_PMC_PM1DOSCI, error))
return FALSE;
if (!fu_superio_device_ec_write1 (self, val, error))
return FALSE;
return TRUE;
}
static gboolean
fu_superio_it89_device_ec_pm1do_smi (FuSuperioDevice *self, guint8 val, GError **error)
{
if (!fu_superio_device_ec_write1 (self, SIO_EC_PMC_PM1DOCMI, error))
return FALSE;
if (!fu_superio_device_ec_write1 (self, val, error))
return FALSE;
return TRUE;
}
static gboolean
fu_superio_device_ec_read_status (FuSuperioDevice *self, GError **error)
{
guint8 tmp = 0x00;
/* read status register */
if (!fu_superio_device_ec_write1 (self, SIO_EC_PMC_PM1DO, error))
return FALSE;
if (!fu_superio_it89_device_ec_pm1do_sci (self, SIO_SPI_CMD_RDSR, error))
return FALSE;
/* wait for write */
do {
if (!fu_superio_device_ec_write1 (self, SIO_EC_PMC_PM1DI, error))
return FALSE;
if (!fu_superio_device_ec_read (self, &tmp, error))
return FALSE;
} while ((tmp & SIO_STATUS_EC_OBF) != 0);
/* watch SCI events */
return fu_superio_device_ec_write1 (self, SIO_EC_PMC_PM1DISCI, error);
}
static GBytes *
fu_superio_it89_device_read_addr (FuSuperioDevice *self,
guint32 addr,
guint size,
GFileProgressCallback progress_cb,
GError **error)
{
g_autofree guint8 *buf = NULL;
/* check... */
if (!fu_superio_device_ec_read_status (self, error))
return NULL;
/* high speed read */
if (!fu_superio_device_ec_write1 (self, SIO_EC_PMC_PM1DO, error))
return NULL;
if (!fu_superio_it89_device_ec_pm1do_sci (self, SIO_SPI_CMD_HS_READ, error))
return NULL;
/* set address, MSB, MID, LSB */
if (!fu_superio_it89_device_ec_pm1do_smi (self, addr >> 16, error))
return NULL;
if (!fu_superio_it89_device_ec_pm1do_smi (self, addr >> 8, error))
return NULL;
if (!fu_superio_it89_device_ec_pm1do_smi (self, addr & 0xff, error))
return NULL;
/* padding for HS? */
if (!fu_superio_it89_device_ec_pm1do_smi (self, 0x0, error))
return NULL;
/* read out data */
buf = g_malloc0 (size);
for (guint i = 0; i < size; i++) {
if (!fu_superio_device_ec_write1 (self, SIO_EC_PMC_PM1DI, error))
return NULL;
if (!fu_superio_device_ec_read (self, &buf[i], error))
return NULL;
/* update progress */
if (progress_cb != NULL)
progress_cb ((goffset) i, (goffset) size, self);
}
/* check again... */
if (!fu_superio_device_ec_read_status (self, error))
return NULL;
/* success */
return g_bytes_new_take (g_steal_pointer (&buf), size);
}
static void
fu_superio_it89_device_progress_cb (goffset current, goffset total, gpointer user_data)
{
FuDevice *device = FU_DEVICE (user_data);
fu_device_set_progress_full (device, (gsize) current, (gsize) total);
}
static GBytes *
fu_superio_it89_device_read_firmware (FuDevice *device, GError **error)
{
FuSuperioDevice *self = FU_SUPERIO_DEVICE (device);
guint64 fwsize = fu_device_get_firmware_size_min (device);
g_autoptr(GBytes) blob = NULL;
fu_device_set_status (device, FWUPD_STATUS_DEVICE_READ);
blob = fu_superio_it89_device_read_addr (self, 0x0, fwsize,
fu_superio_it89_device_progress_cb,
error);
return g_steal_pointer (&blob);
}
static gboolean
fu_superio_it89_device_attach (FuDevice *device, GError **error)
{
@ -172,5 +288,6 @@ fu_superio_it89_device_class_init (FuSuperioIt89DeviceClass *klass)
FuSuperioDeviceClass *klass_superio_device = FU_SUPERIO_DEVICE_CLASS (klass);
klass_device->attach = fu_superio_it89_device_attach;
klass_device->detach = fu_superio_it89_device_detach;
klass_device->read_firmware = fu_superio_it89_device_read_firmware;
klass_superio_device->setup = fu_superio_it89_device_setup;
}