fwupd/plugins/bcm57xx/fu-bcm57xx-recovery-device.c
2020-10-22 13:55:14 +01:00

770 lines
22 KiB
C

/*
* Copyright (C) 2018-2020 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>
#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)
{
guint8 *base = self->bar[bar].buf + offset;
/* 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();
*val = *(guint32 *)base;
return TRUE;
}
static gboolean
fu_bcm57xx_recovery_device_bar_write (FuBcm57xxRecoveryDevice *self,
guint bar, gsize offset, guint32 val,
GError **error)
{
guint8 *base = self->bar[bar].buf + offset;
/* 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();
*(guint32 *)base = val;
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,
"cound 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_NO_GUID_MATCHING);
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_set_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 (FuUdevDevice *device, GError **error)
{
/* success */
return fu_udev_device_set_physical_id (device, "pci", error);
}
static void
fu_bcm57xx_recovery_device_class_init (FuBcm57xxRecoveryDeviceClass *klass)
{
FuDeviceClass *klass_device = FU_DEVICE_CLASS (klass);
FuUdevDeviceClass *klass_udev_device = FU_UDEV_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_udev_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);
}