/* * Copyright (C) 2018 Evan Lojewski * Copyright (C) 2020 Richard Hughes * * SPDX-License-Identifier: GPL-2+ */ #include "config.h" #include #include #include #include #include #include #ifdef HAVE_MMAN_H #include #endif #ifdef HAVE_VALGRIND #include #endif /* HAVE_VALGRIND */ #include #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); }