fwupd/plugins/bcm57xx/fu-bcm57xx-recovery-device.c
2021-08-24 11:18:40 -05:00

853 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 <errno.h>
#include <fcntl.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#ifdef HAVE_MMAN_H
#include <sys/mman.h>
#endif
#ifdef HAVE_VALGRIND
#include <valgrind.h>
#endif /* HAVE_VALGRIND */
#include <fwupdplugin.h>
#include "fu-bcm57xx-common.h"
#include "fu-bcm57xx-firmware.h"
#include "fu-bcm57xx-recovery-device.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);
}