fwupd/plugins/bcm57xx/fu-bcm57xx-recovery-device.c
Richard Hughes b3f9841924 Support more than one protocol for a given device
Devices may want to support more than one protocol, and for some devices
(e.g. Unifying peripherals stuck in bootloader mode) you might not even be able
to query for the correct protocol anyway.
2021-03-01 16:14:36 +00:00

771 lines
22 KiB
C

/*
* Copyright (C) 2018 Evan Lojewski
* Copyright (C) 2020 Richard Hughes <richard@hughsie.com>
*
* SPDX-License-Identifier: GPL-2+
*/
#include "config.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#ifdef HAVE_MMAN_H
#include <sys/mman.h>
#endif
#ifdef HAVE_VALGRIND
#include <valgrind.h>
#endif /* HAVE_VALGRIND */
#include "fu-common.h"
#include "fu-bcm57xx-common.h"
#include "fu-bcm57xx-recovery-device.h"
#include "fu-bcm57xx-firmware.h"
/* offsets into BAR[0] */
#define REG_DEVICE_PCI_VENDOR_DEVICE_ID 0x6434
#define REG_NVM_SOFTWARE_ARBITRATION 0x7020
#define REG_NVM_ACCESS 0x7024
#define REG_NVM_COMMAND 0x7000
#define REG_NVM_ADDR 0x700c
#define REG_NVM_READ 0x7010
#define REG_NVM_WRITE 0x7008
/* offsets into BAR[2] */
#define REG_APE_MODE 0x0
typedef struct {
guint8 *buf;
gsize bufsz;
} FuBcm57xxMmap;
#define FU_BCM57XX_BAR_DEVICE 0
#define FU_BCM57XX_BAR_APE 1
#define FU_BCM57XX_BAR_MAX 3
struct _FuBcm57xxRecoveryDevice {
FuUdevDevice parent_instance;
FuBcm57xxMmap bar[FU_BCM57XX_BAR_MAX];
};
typedef union {
guint32 r32;
struct {
guint32 reserved_0_0 : 1;
guint32 Reset : 1;
guint32 reserved_2_2 : 1;
guint32 Done : 1;
guint32 Doit : 1;
guint32 Wr : 1;
guint32 Erase : 1;
guint32 First : 1;
guint32 Last : 1;
guint32 reserved_15_9 : 7;
guint32 WriteEnableCommand : 1;
guint32 WriteDisableCommand : 1;
guint32 reserved_31_18 : 14;
} __attribute__((packed)) bits;
} BcmRegNVMCommand;
typedef union {
guint32 r32;
struct {
guint32 ReqSet0 : 1;
guint32 ReqSet1 : 1;
guint32 ReqSet2 : 1;
guint32 ReqSet3 : 1;
guint32 ReqClr0 : 1;
guint32 ReqClr1 : 1;
guint32 ReqClr2 : 1;
guint32 ReqClr3 : 1;
guint32 ArbWon0 : 1;
guint32 ArbWon1 : 1;
guint32 ArbWon2 : 1;
guint32 ArbWon3 : 1;
guint32 Req0 : 1;
guint32 Req1 : 1;
guint32 Req2 : 1;
guint32 Req3 : 1;
guint32 reserved_31_16 : 16;
} __attribute__((packed)) bits;
} BcmRegNVMSoftwareArbitration;
typedef union {
guint32 r32;
struct {
guint32 Enable : 1;
guint32 WriteEnable : 1;
guint32 reserved_31_2 : 30;
} __attribute__((packed)) bits;
} BcmRegNVMAccess;
typedef union {
guint32 r32;
struct {
guint32 Reset : 1;
guint32 Halt : 1;
guint32 FastBoot : 1;
guint32 HostDiag : 1;
guint32 reserved_4_4 : 1;
guint32 Event1 : 1;
guint32 Event2 : 1;
guint32 GRCint : 1;
guint32 reserved_8_8 : 1;
guint32 SwapATBdword : 1;
guint32 reserved_10_10 : 1;
guint32 SwapARBdword : 1;
guint32 reserved_13_12 : 2;
guint32 Channel0Enable : 1;
guint32 Channel2Enable : 1;
guint32 reserved_17_16 : 2;
guint32 MemoryECC : 1;
guint32 ICodePIPRdDisable : 1;
guint32 reserved_29_20 : 10;
guint32 Channel1Enable : 1;
guint32 Channel3Enable : 1;
} __attribute__((packed)) bits;
} BcmRegAPEMode;
G_DEFINE_TYPE (FuBcm57xxRecoveryDevice, fu_bcm57xx_recovery_device, FU_TYPE_UDEV_DEVICE)
#ifdef __ppc64__
#define BARRIER() __asm__ volatile ("sync 0\neieio\n" : : : "memory")
#else
#define BARRIER() __asm__ volatile ("" : : : "memory");
#endif
static gboolean
fu_bcm57xx_recovery_device_bar_read (FuBcm57xxRecoveryDevice *self,
guint bar, gsize offset, guint32 *val,
GError **error)
{
/* this should never happen */
if (self->bar[bar].buf == NULL) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_INTERNAL,
"BAR[%u] is not mapped!", bar);
return FALSE;
}
BARRIER();
return fu_memcpy_safe ((guint8 *) val, sizeof(*val), 0x0, /* dst */
self->bar[bar].buf, self->bar[bar].bufsz, offset,
sizeof(*val), error);
}
static gboolean
fu_bcm57xx_recovery_device_bar_write (FuBcm57xxRecoveryDevice *self,
guint bar, gsize offset, guint32 val,
GError **error)
{
/* this should never happen */
if (self->bar[bar].buf == NULL) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_INTERNAL,
"BAR[%u] is not mapped!", bar);
return FALSE;
}
BARRIER();
if (!fu_memcpy_safe (self->bar[bar].buf, self->bar[bar].bufsz, offset, /* dst */
(const guint8 *) &val, sizeof(val), 0x0, /* src */
sizeof(val), error))
return FALSE;
BARRIER();
return TRUE;
}
static gboolean
fu_bcm57xx_recovery_device_nvram_disable (FuBcm57xxRecoveryDevice *self,
GError **error)
{
BcmRegNVMAccess tmp;
if (!fu_bcm57xx_recovery_device_bar_read (self, FU_BCM57XX_BAR_DEVICE,
REG_NVM_ACCESS, &tmp.r32, error))
return FALSE;
tmp.bits.Enable = FALSE;
tmp.bits.WriteEnable = FALSE;
return fu_bcm57xx_recovery_device_bar_write (self, FU_BCM57XX_BAR_DEVICE,
REG_NVM_ACCESS, tmp.r32, error);
}
static gboolean
fu_bcm57xx_recovery_device_nvram_enable (FuBcm57xxRecoveryDevice *self,
GError **error)
{
BcmRegNVMAccess tmp;
if (!fu_bcm57xx_recovery_device_bar_read (self, FU_BCM57XX_BAR_DEVICE,
REG_NVM_ACCESS, &tmp.r32, error))
return FALSE;
tmp.bits.Enable = TRUE;
tmp.bits.WriteEnable = FALSE;
return fu_bcm57xx_recovery_device_bar_write (self, FU_BCM57XX_BAR_DEVICE,
REG_NVM_ACCESS, tmp.r32, error);
}
static gboolean
fu_bcm57xx_recovery_device_nvram_enable_write (FuBcm57xxRecoveryDevice *self,
GError **error)
{
BcmRegNVMAccess tmp;
if (!fu_bcm57xx_recovery_device_bar_read (self, FU_BCM57XX_BAR_DEVICE,
REG_NVM_ACCESS, &tmp.r32, error))
return FALSE;
tmp.bits.Enable = TRUE;
tmp.bits.WriteEnable = TRUE;
return fu_bcm57xx_recovery_device_bar_write (self, FU_BCM57XX_BAR_DEVICE,
REG_NVM_ACCESS, tmp.r32, error);
}
static gboolean
fu_bcm57xx_recovery_device_nvram_acquire_lock (FuBcm57xxRecoveryDevice *self,
GError **error)
{
BcmRegNVMSoftwareArbitration tmp = { 0 };
g_autoptr(GTimer) timer = g_timer_new ();
tmp.bits.ReqSet1 = 1;
if (!fu_bcm57xx_recovery_device_bar_write (self, FU_BCM57XX_BAR_DEVICE,
REG_NVM_SOFTWARE_ARBITRATION,
tmp.r32, error))
return FALSE;
do {
if (!fu_bcm57xx_recovery_device_bar_read (self,
FU_BCM57XX_BAR_DEVICE,
REG_NVM_SOFTWARE_ARBITRATION,
&tmp.r32, error))
return FALSE;
if (tmp.bits.ArbWon1)
return TRUE;
if (g_timer_elapsed (timer, NULL) > 0.2)
break;
} while (TRUE);
/* timed out */
g_set_error_literal (error,
G_IO_ERROR,
G_IO_ERROR_TIMED_OUT,
"timed out trying to acquire lock #1");
return FALSE;
}
static gboolean
fu_bcm57xx_recovery_device_nvram_release_lock (FuBcm57xxRecoveryDevice *self,
GError **error)
{
BcmRegNVMSoftwareArbitration tmp = { 0 };
tmp.r32 = 0;
tmp.bits.ReqClr1 = 1;
return fu_bcm57xx_recovery_device_bar_write (self, FU_BCM57XX_BAR_DEVICE,
REG_NVM_SOFTWARE_ARBITRATION, tmp.r32,
error);
}
static gboolean
fu_bcm57xx_recovery_device_nvram_wait_done (FuBcm57xxRecoveryDevice *self, GError **error)
{
BcmRegNVMCommand tmp = { 0 };
g_autoptr(GTimer) timer = g_timer_new ();
do {
if (!fu_bcm57xx_recovery_device_bar_read (self,
FU_BCM57XX_BAR_DEVICE,
REG_NVM_COMMAND, &tmp.r32,
error))
return FALSE;
if (tmp.bits.Done)
return TRUE;
if (g_timer_elapsed (timer, NULL) > 0.2)
break;
} while (TRUE);
/* timed out */
g_set_error_literal (error,
G_IO_ERROR,
G_IO_ERROR_TIMED_OUT,
"timed out");
return FALSE;
}
static gboolean
fu_bcm57xx_recovery_device_nvram_clear_done (FuBcm57xxRecoveryDevice *self, GError **error)
{
BcmRegNVMCommand tmp = { 0 };
tmp.bits.Done = 1;
return fu_bcm57xx_recovery_device_bar_write (self, FU_BCM57XX_BAR_DEVICE,
REG_NVM_COMMAND, tmp.r32, error);
}
static gboolean
fu_bcm57xx_recovery_device_nvram_read (FuBcm57xxRecoveryDevice *self,
guint32 address, guint32 *buf, gsize bufsz,
GError **error)
{
for (guint i = 0; i < bufsz; i++) {
BcmRegNVMCommand tmp = { 0 };
guint32 val32 = 0;
if (!fu_bcm57xx_recovery_device_nvram_clear_done (self, error))
return FALSE;
if (!fu_bcm57xx_recovery_device_bar_write (self, FU_BCM57XX_BAR_DEVICE,
REG_NVM_ADDR, address, error))
return FALSE;
tmp.bits.Doit = 1;
tmp.bits.First = i == 0;
tmp.bits.Last = i == bufsz - 1;
if (!fu_bcm57xx_recovery_device_bar_write (self, FU_BCM57XX_BAR_DEVICE,
REG_NVM_COMMAND, tmp.r32, error))
return FALSE;
if (!fu_bcm57xx_recovery_device_nvram_wait_done (self, error)) {
g_prefix_error (error, "failed to read @0x%x: ", address);
return FALSE;
}
if (!fu_bcm57xx_recovery_device_bar_read (self, FU_BCM57XX_BAR_DEVICE,
REG_NVM_READ, &val32, error))
return FALSE;
buf[i] = GUINT32_FROM_BE(val32);
address += sizeof(guint32);
fu_device_set_progress_full (FU_DEVICE (self), i, bufsz);
}
/* success */
return TRUE;
}
static gboolean
fu_bcm57xx_recovery_device_nvram_write (FuBcm57xxRecoveryDevice *self,
guint32 address, const guint32 *buf, gsize bufsz_dwrds,
GError **error)
{
const guint32 page_size_dwrds = 64;
/* can only write in pages of 64 dwords */
if (bufsz_dwrds % page_size_dwrds != 0 ||
(address * sizeof(guint32)) % page_size_dwrds != 0) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_NOT_SUPPORTED,
"can only write aligned with page size 0x%x",
page_size_dwrds);
return FALSE;
}
for (guint i = 0; i < bufsz_dwrds; i++) {
BcmRegNVMCommand tmp = { 0 };
if (!fu_bcm57xx_recovery_device_nvram_clear_done (self, error))
return FALSE;
if (!fu_bcm57xx_recovery_device_bar_write (self, FU_BCM57XX_BAR_DEVICE,
REG_NVM_WRITE, GUINT32_TO_BE(buf[i]), error))
return FALSE;
if (!fu_bcm57xx_recovery_device_bar_write (self, FU_BCM57XX_BAR_DEVICE,
REG_NVM_ADDR, address, error))
return FALSE;
tmp.bits.Wr = TRUE;
tmp.bits.Doit = TRUE;
tmp.bits.First = i % page_size_dwrds == 0;
tmp.bits.Last = (i + 1) % page_size_dwrds == 0;
if (!fu_bcm57xx_recovery_device_bar_write (self, FU_BCM57XX_BAR_DEVICE,
REG_NVM_COMMAND, tmp.r32, error))
return FALSE;
if (!fu_bcm57xx_recovery_device_nvram_wait_done (self, error)) {
g_prefix_error (error, "failed to write @0x%x: ", address);
return FALSE;
}
address += sizeof(guint32);
fu_device_set_progress_full (FU_DEVICE (self), i, bufsz_dwrds);
}
/* success */
return TRUE;
}
static gboolean
fu_bcm57xx_recovery_device_detach (FuDevice *device, GError **error)
{
/* unbind tg3 */
return fu_device_unbind_driver (device, error);
}
static gboolean
fu_bcm57xx_recovery_device_attach (FuDevice *device, GError **error)
{
g_autoptr(GError) error_local = NULL;
/* bind tg3, which might fail if the module is not compiled */
if (!fu_device_bind_driver (device, "pci", "tg3", &error_local)) {
if (g_error_matches (error_local,
FWUPD_ERROR,
FWUPD_ERROR_NOT_SUPPORTED)) {
g_warning ("failed to bind tg3: %s", error_local->message);
} else {
g_propagate_prefixed_error (error,
g_steal_pointer (&error_local),
"failed to bind tg3: ");
return FALSE;
}
}
/* success */
return TRUE;
}
static gboolean
fu_bcm57xx_recovery_device_activate (FuDevice *device, GError **error)
{
BcmRegAPEMode mode = { 0 };
FuBcm57xxRecoveryDevice *self = FU_BCM57XX_RECOVERY_DEVICE (device);
/* halt */
mode.bits.Halt = 1;
mode.bits.FastBoot = 0;
if (!fu_bcm57xx_recovery_device_bar_write (self, FU_BCM57XX_BAR_APE,
REG_APE_MODE, mode.r32, error))
return FALSE;
/* boot */
mode.bits.Halt = 0;
mode.bits.FastBoot = 0;
mode.bits.Reset = 1;
return fu_bcm57xx_recovery_device_bar_write (self, FU_BCM57XX_BAR_APE,
REG_APE_MODE, mode.r32, error);
}
static GBytes *
fu_bcm57xx_recovery_device_dump_firmware (FuDevice *device, GError **error)
{
FuBcm57xxRecoveryDevice *self = FU_BCM57XX_RECOVERY_DEVICE (device);
gsize bufsz_dwrds = fu_device_get_firmware_size_max (FU_DEVICE (self)) / sizeof(guint32);
g_autofree guint32 *buf_dwrds = g_new0 (guint32, bufsz_dwrds);
g_autoptr(FuDeviceLocker) locker = NULL;
g_autoptr(FuDeviceLocker) locker2 = NULL;
/* read from hardware */
fu_device_set_status (device, FWUPD_STATUS_DEVICE_READ);
locker = fu_device_locker_new_full (self,
(FuDeviceLockerFunc) fu_bcm57xx_recovery_device_nvram_acquire_lock,
(FuDeviceLockerFunc) fu_bcm57xx_recovery_device_nvram_release_lock,
error);
if (locker == NULL)
return NULL;
locker2 = fu_device_locker_new_full (self,
(FuDeviceLockerFunc) fu_bcm57xx_recovery_device_nvram_enable,
(FuDeviceLockerFunc) fu_bcm57xx_recovery_device_nvram_disable,
error);
if (locker2 == NULL)
return NULL;
if (!fu_bcm57xx_recovery_device_nvram_read (self, 0x0, buf_dwrds, bufsz_dwrds, error))
return NULL;
if (!fu_device_locker_close (locker2, error))
return NULL;
return g_bytes_new (buf_dwrds, bufsz_dwrds * sizeof(guint32));
}
static FuFirmware *
fu_bcm57xx_recovery_device_prepare_firmware (FuDevice *device,
GBytes *fw,
FwupdInstallFlags flags,
GError **error)
{
g_autoptr(FuFirmware) firmware_bin = fu_firmware_new ();
g_autoptr(FuFirmware) firmware_tmp = fu_bcm57xx_firmware_new ();
/* check is a NVRAM backup */
if (!fu_firmware_parse (firmware_tmp, fw, flags, error)) {
g_prefix_error (error, "failed to parse new firmware: ");
return NULL;
}
if (!fu_bcm57xx_firmware_is_backup (FU_BCM57XX_FIRMWARE (firmware_tmp))) {
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_NOT_SUPPORTED,
"can only recover with backup firmware");
return NULL;
}
if (!fu_firmware_parse (firmware_bin, fw, flags, error))
return NULL;
return g_steal_pointer (&firmware_bin);
}
static gboolean
fu_bcm57xx_recovery_device_write_firmware (FuDevice *device,
FuFirmware *firmware,
FwupdInstallFlags flags,
GError **error)
{
FuBcm57xxRecoveryDevice *self= FU_BCM57XX_RECOVERY_DEVICE (device);
const guint8 *buf;
gsize bufsz = 0;
gsize bufsz_dwrds;
g_autofree guint32 *buf_dwrds = NULL;
g_autoptr(FuDeviceLocker) locker = NULL;
g_autoptr(FuDeviceLocker) locker2 = NULL;
g_autoptr(GBytes) blob = NULL;
/* build the images into one linear blob of the correct size */
fu_device_set_status (device, FWUPD_STATUS_DECOMPRESSING);
blob = fu_firmware_write (firmware, error);
if (blob == NULL)
return FALSE;
/* align into uint32_t buffer */
buf = g_bytes_get_data (blob, &bufsz);
bufsz_dwrds = bufsz / sizeof(guint32);
buf_dwrds = g_new0 (guint32, bufsz_dwrds);
if (!fu_memcpy_safe ((guint8 *) buf_dwrds, bufsz_dwrds * sizeof(guint32), 0x0, /* dst */
buf, bufsz, 0x0, /* src */
bufsz, error))
return FALSE;
/* hit hardware */
fu_device_set_status (device, FWUPD_STATUS_DEVICE_WRITE);
locker = fu_device_locker_new_full (self,
(FuDeviceLockerFunc) fu_bcm57xx_recovery_device_nvram_acquire_lock,
(FuDeviceLockerFunc) fu_bcm57xx_recovery_device_nvram_release_lock,
error);
if (locker == NULL)
return FALSE;
locker2 = fu_device_locker_new_full (self,
(FuDeviceLockerFunc) fu_bcm57xx_recovery_device_nvram_enable_write,
(FuDeviceLockerFunc) fu_bcm57xx_recovery_device_nvram_disable,
error);
if (locker2 == NULL)
return FALSE;
if (!fu_bcm57xx_recovery_device_nvram_write (self, 0x0, buf_dwrds, bufsz_dwrds, error))
return FALSE;
if (!fu_device_locker_close (locker2, error))
return FALSE;
if (!fu_device_locker_close (locker, error))
return FALSE;
/* reset APE */
return fu_device_activate (device, error);
}
static gboolean
fu_bcm57xx_recovery_device_setup (FuDevice *device, GError **error)
{
FuBcm57xxRecoveryDevice *self = FU_BCM57XX_RECOVERY_DEVICE (device);
guint32 fwversion = 0;
g_autoptr(FuDeviceLocker) locker = NULL;
g_autoptr(FuDeviceLocker) locker2 = NULL;
locker = fu_device_locker_new_full (self,
(FuDeviceLockerFunc) fu_bcm57xx_recovery_device_nvram_acquire_lock,
(FuDeviceLockerFunc) fu_bcm57xx_recovery_device_nvram_release_lock,
error);
if (locker == NULL)
return FALSE;
locker2 = fu_device_locker_new_full (self,
(FuDeviceLockerFunc) fu_bcm57xx_recovery_device_nvram_enable,
(FuDeviceLockerFunc) fu_bcm57xx_recovery_device_nvram_disable,
error);
if (locker2 == NULL)
return FALSE;
/* get NVRAM version */
if (!fu_bcm57xx_recovery_device_nvram_read (self, BCM_NVRAM_STAGE1_BASE + BCM_NVRAM_STAGE1_VERSION,
&fwversion, 1, error))
return FALSE;
if (fwversion != 0x0) {
g_autofree gchar *fwversion_str = NULL;
/* this is only set on the OSS firmware */
fwversion_str = fu_common_version_from_uint32 (GUINT32_FROM_BE(fwversion),
FWUPD_VERSION_FORMAT_TRIPLET);
fu_device_set_version_format (device, FWUPD_VERSION_FORMAT_TRIPLET);
fu_device_set_version (device, fwversion_str);
fu_device_set_version_raw (device, fwversion);
fu_device_set_branch (device, BCM_FW_BRANCH_OSS_FIRMWARE);
} else {
guint32 bufver[4] = { 0x0 };
guint32 veraddr = 0;
g_autoptr(Bcm57xxVeritem) veritem = NULL;
/* fall back to the string, e.g. '5719-v1.43' */
if (!fu_bcm57xx_recovery_device_nvram_read (self,
BCM_NVRAM_STAGE1_BASE + BCM_NVRAM_STAGE1_VERADDR,
&veraddr, 1, error))
return FALSE;
veraddr = GUINT32_FROM_BE(veraddr);
if (veraddr > BCM_PHYS_ADDR_DEFAULT)
veraddr -= BCM_PHYS_ADDR_DEFAULT;
if (!fu_bcm57xx_recovery_device_nvram_read (self,
BCM_NVRAM_STAGE1_BASE + veraddr,
bufver, 4, error))
return FALSE;
veritem = fu_bcm57xx_veritem_new ((guint8 *) bufver, sizeof(bufver));
if (veritem != NULL) {
fu_device_set_version (device, veritem->version);
fu_device_set_branch (device, veritem->branch);
fu_device_set_version_format (device, veritem->verfmt);
}
}
return TRUE;
}
static gboolean
fu_bcm57xx_recovery_device_open (FuDevice *device, GError **error)
{
#ifdef HAVE_MMAN_H
FuBcm57xxRecoveryDevice *self = FU_BCM57XX_RECOVERY_DEVICE (device);
FuUdevDevice *udev_device = FU_UDEV_DEVICE (device);
const gchar *sysfs_path = fu_udev_device_get_sysfs_path (udev_device);
#endif
#ifdef RUNNING_ON_VALGRIND
/* this can't work */
if (RUNNING_ON_VALGRIND) {
g_set_error_literal (error,
G_IO_ERROR,
G_IO_ERROR_NOT_SUPPORTED,
"cannot mmap'ing BARs when using valgrind");
return FALSE;
}
#endif
#ifdef HAVE_MMAN_H
/* map BARs */
for (guint i = 0; i < FU_BCM57XX_BAR_MAX; i++) {
int memfd;
struct stat st;
g_autofree gchar *fn = NULL;
g_autofree gchar *resfn = NULL;
/* open 64 bit resource */
resfn = g_strdup_printf ("resource%u", i * 2);
fn = g_build_filename (sysfs_path, resfn, NULL);
memfd = open (fn, O_RDWR | O_SYNC);
if (memfd < 0) {
g_set_error (error,
G_IO_ERROR,
G_IO_ERROR_NOT_FOUND,
"error opening %s", fn);
return FALSE;
}
if (fstat (memfd, &st) < 0) {
g_set_error (error,
G_IO_ERROR,
G_IO_ERROR_NOT_SUPPORTED,
"could not stat %s", fn);
close (memfd);
return FALSE;
}
/* mmap */
if (g_getenv ("FWUPD_BCM57XX_VERBOSE") != NULL)
g_debug ("mapping BAR[%u] %s for 0x%x bytes", i, fn, (guint) st.st_size);
self->bar[i].buf = (guint8 *) mmap (0, st.st_size,
PROT_READ | PROT_WRITE,
MAP_SHARED, memfd, 0);
self->bar[i].bufsz = st.st_size;
close (memfd);
if (self->bar[i].buf == MAP_FAILED) {
g_set_error (error,
G_IO_ERROR,
G_IO_ERROR_NOT_SUPPORTED,
"could not mmap %s: %s",
fn, strerror(errno));
return FALSE;
}
}
/* success */
return TRUE;
#else
g_set_error_literal (error,
G_IO_ERROR,
G_IO_ERROR_NOT_SUPPORTED,
"mmap() not supported as sys/mman.h not available");
return FALSE;
#endif
}
static gboolean
fu_bcm57xx_recovery_device_close (FuDevice *device, GError **error)
{
#ifdef HAVE_MMAN_H
FuBcm57xxRecoveryDevice *self = FU_BCM57XX_RECOVERY_DEVICE (device);
/* unmap BARs */
for (guint i = 0; i < FU_BCM57XX_BAR_MAX; i++) {
if (self->bar[i].buf == NULL)
continue;
if (g_getenv ("FWUPD_BCM57XX_VERBOSE") != NULL)
g_debug ("unmapping BAR[%u]", i);
munmap (self->bar[i].buf, self->bar[i].bufsz);
self->bar[i].buf = NULL;
self->bar[i].bufsz = 0;
}
/* success */
return TRUE;
#else
g_set_error_literal (error,
G_IO_ERROR,
G_IO_ERROR_NOT_SUPPORTED,
"munmap() not supported as sys/mman.h not available");
return FALSE;
#endif
}
static void
fu_bcm57xx_recovery_device_init (FuBcm57xxRecoveryDevice *self)
{
fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_UPDATABLE);
fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_CAN_VERIFY_IMAGE);
fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_NEEDS_REBOOT);
fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_BACKUP_BEFORE_INSTALL);
fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_IGNORE_VALIDATION);
fu_device_add_protocol (FU_DEVICE (self), "com.broadcom.bcm57xx");
fu_device_add_icon (FU_DEVICE (self), "network-wired");
fu_device_set_logical_id (FU_DEVICE (self), "recovery");
/* other values are set from a quirk */
fu_device_set_firmware_size (FU_DEVICE (self), BCM_FIRMWARE_SIZE);
/* no BARs mapped */
for (guint i = 0; i < FU_BCM57XX_BAR_MAX; i++) {
self->bar[i].buf = NULL;
self->bar[i].bufsz = 0;
}
}
static gboolean
fu_bcm57xx_recovery_device_probe (FuDevice *device, GError **error)
{
/* FuUdevDevice->probe */
if (!FU_DEVICE_CLASS (fu_bcm57xx_recovery_device_parent_class)->probe (device, error))
return FALSE;
return fu_udev_device_set_physical_id (FU_UDEV_DEVICE (device), "pci", error);
}
static void
fu_bcm57xx_recovery_device_class_init (FuBcm57xxRecoveryDeviceClass *klass)
{
FuDeviceClass *klass_device = FU_DEVICE_CLASS (klass);
klass_device->activate = fu_bcm57xx_recovery_device_activate;
klass_device->prepare_firmware = fu_bcm57xx_recovery_device_prepare_firmware;
klass_device->setup = fu_bcm57xx_recovery_device_setup;
klass_device->reload = fu_bcm57xx_recovery_device_setup;
klass_device->open = fu_bcm57xx_recovery_device_open;
klass_device->close = fu_bcm57xx_recovery_device_close;
klass_device->write_firmware = fu_bcm57xx_recovery_device_write_firmware;
klass_device->dump_firmware = fu_bcm57xx_recovery_device_dump_firmware;
klass_device->attach = fu_bcm57xx_recovery_device_attach;
klass_device->detach = fu_bcm57xx_recovery_device_detach;
klass_device->probe = fu_bcm57xx_recovery_device_probe;
}
FuBcm57xxRecoveryDevice *
fu_bcm57xx_recovery_device_new (void)
{
FuUdevDevice *self = g_object_new (FU_TYPE_BCM57XX_RECOVERY_DEVICE, NULL);
return FU_BCM57XX_RECOVERY_DEVICE (self);
}