From ede1a3edaace0ead70ecfcddeeb54974f74d56de Mon Sep 17 00:00:00 2001 From: Andrew Rybchenko Date: Fri, 23 Nov 2018 10:21:36 +0000 Subject: [PATCH] sfxge(4): precheck and verify flash writes Read existing flash content before writing, so the flash write can be avoided if the existing partition content matches the new image. This avoids unnecessary write cycles for the flash device, and may also be faster. If the flash does need to be updated, verify the content after writing. Note that reading the flash content after writing but before calling efx_nvram-rw_finish() avoids firmware bug68170, which can lead to signed image updates failing on Medford. Submitted by: Andy Moreton Sponsored by: Solarflare Communications, Inc. Differential Revision: https://reviews.freebsd.org/D18093 --- sys/dev/sfxge/common/ef10_impl.h | 8 ++++++ sys/dev/sfxge/common/ef10_nvram.c | 27 +++++++++++++++++-- sys/dev/sfxge/common/efx.h | 8 ++++++ sys/dev/sfxge/common/efx_impl.h | 2 ++ sys/dev/sfxge/common/efx_nvram.c | 44 +++++++++++++++++++++++++++++++ 5 files changed, 87 insertions(+), 2 deletions(-) diff --git a/sys/dev/sfxge/common/ef10_impl.h b/sys/dev/sfxge/common/ef10_impl.h index b5c51cbb7d5..afd7aa82b4c 100644 --- a/sys/dev/sfxge/common/ef10_impl.h +++ b/sys/dev/sfxge/common/ef10_impl.h @@ -444,6 +444,14 @@ ef10_nvram_partn_read( __out_bcount(size) caddr_t data, __in size_t size); +extern __checkReturn efx_rc_t +ef10_nvram_partn_read_backup( + __in efx_nic_t *enp, + __in uint32_t partn, + __in unsigned int offset, + __out_bcount(size) caddr_t data, + __in size_t size); + extern __checkReturn efx_rc_t ef10_nvram_partn_erase( __in efx_nic_t *enp, diff --git a/sys/dev/sfxge/common/ef10_nvram.c b/sys/dev/sfxge/common/ef10_nvram.c index 97156bd654b..864aa488632 100644 --- a/sys/dev/sfxge/common/ef10_nvram.c +++ b/sys/dev/sfxge/common/ef10_nvram.c @@ -1940,13 +1940,36 @@ ef10_nvram_partn_read( __in size_t size) { /* - * Read requests which come in through the EFX API expect to - * read the current, active partition. + * An A/B partition has two data stores (current and backup). + * Read requests which come in through the EFX API expect to read the + * current, active store of an A/B partition. For non A/B partitions, + * there is only a single store and so the mode param is ignored. */ return ef10_nvram_partn_read_mode(enp, partn, offset, data, size, MC_CMD_NVRAM_READ_IN_V2_TARGET_CURRENT); } + __checkReturn efx_rc_t +ef10_nvram_partn_read_backup( + __in efx_nic_t *enp, + __in uint32_t partn, + __in unsigned int offset, + __out_bcount(size) caddr_t data, + __in size_t size) +{ + /* + * An A/B partition has two data stores (current and backup). + * Read the backup store of an A/B partition (i.e. the store currently + * being written to if the partition is locked). + * + * This is needed when comparing the existing partition content to avoid + * unnecessary writes, or to read back what has been written to check + * that the writes have succeeded. + */ + return ef10_nvram_partn_read_mode(enp, partn, offset, data, size, + MC_CMD_NVRAM_READ_IN_V2_TARGET_BACKUP); +} + __checkReturn efx_rc_t ef10_nvram_partn_erase( __in efx_nic_t *enp, diff --git a/sys/dev/sfxge/common/efx.h b/sys/dev/sfxge/common/efx.h index 55ed6dd26e2..926e8a0bf8c 100644 --- a/sys/dev/sfxge/common/efx.h +++ b/sys/dev/sfxge/common/efx.h @@ -1436,6 +1436,14 @@ efx_nvram_read_chunk( __out_bcount(size) caddr_t data, __in size_t size); +extern __checkReturn efx_rc_t +efx_nvram_read_backup( + __in efx_nic_t *enp, + __in efx_nvram_type_t type, + __in unsigned int offset, + __out_bcount(size) caddr_t data, + __in size_t size); + extern __checkReturn efx_rc_t efx_nvram_set_version( __in efx_nic_t *enp, diff --git a/sys/dev/sfxge/common/efx_impl.h b/sys/dev/sfxge/common/efx_impl.h index 90d9c74d531..26fa35d5343 100644 --- a/sys/dev/sfxge/common/efx_impl.h +++ b/sys/dev/sfxge/common/efx_impl.h @@ -471,6 +471,8 @@ typedef struct efx_nvram_ops_s { efx_rc_t (*envo_partn_rw_start)(efx_nic_t *, uint32_t, size_t *); efx_rc_t (*envo_partn_read)(efx_nic_t *, uint32_t, unsigned int, caddr_t, size_t); + efx_rc_t (*envo_partn_read_backup)(efx_nic_t *, uint32_t, + unsigned int, caddr_t, size_t); efx_rc_t (*envo_partn_erase)(efx_nic_t *, uint32_t, unsigned int, size_t); efx_rc_t (*envo_partn_write)(efx_nic_t *, uint32_t, diff --git a/sys/dev/sfxge/common/efx_nvram.c b/sys/dev/sfxge/common/efx_nvram.c index 639c306a7ed..c55d60f58c3 100644 --- a/sys/dev/sfxge/common/efx_nvram.c +++ b/sys/dev/sfxge/common/efx_nvram.c @@ -48,6 +48,7 @@ static const efx_nvram_ops_t __efx_nvram_siena_ops = { siena_nvram_partn_size, /* envo_partn_size */ siena_nvram_partn_rw_start, /* envo_partn_rw_start */ siena_nvram_partn_read, /* envo_partn_read */ + siena_nvram_partn_read, /* envo_partn_read_backup */ siena_nvram_partn_erase, /* envo_partn_erase */ siena_nvram_partn_write, /* envo_partn_write */ siena_nvram_partn_rw_finish, /* envo_partn_rw_finish */ @@ -68,6 +69,7 @@ static const efx_nvram_ops_t __efx_nvram_ef10_ops = { ef10_nvram_partn_size, /* envo_partn_size */ ef10_nvram_partn_rw_start, /* envo_partn_rw_start */ ef10_nvram_partn_read, /* envo_partn_read */ + ef10_nvram_partn_read_backup, /* envo_partn_read_backup */ ef10_nvram_partn_erase, /* envo_partn_erase */ ef10_nvram_partn_write, /* envo_partn_write */ ef10_nvram_partn_rw_finish, /* envo_partn_rw_finish */ @@ -282,6 +284,48 @@ efx_nvram_read_chunk( return (0); +fail2: + EFSYS_PROBE(fail2); +fail1: + EFSYS_PROBE1(fail1, efx_rc_t, rc); + + return (rc); +} + +/* + * Read from the backup (writeable) store of an A/B partition. + * For non A/B partitions, there is only a single store, and so this + * function has the same behaviour as efx_nvram_read_chunk(). + */ + __checkReturn efx_rc_t +efx_nvram_read_backup( + __in efx_nic_t *enp, + __in efx_nvram_type_t type, + __in unsigned int offset, + __out_bcount(size) caddr_t data, + __in size_t size) +{ + const efx_nvram_ops_t *envop = enp->en_envop; + uint32_t partn; + efx_rc_t rc; + + EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC); + EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_NVRAM); + + EFSYS_ASSERT3U(type, <, EFX_NVRAM_NTYPES); + EFSYS_ASSERT3U(type, !=, EFX_NVRAM_INVALID); + + if ((rc = envop->envo_type_to_partn(enp, type, &partn)) != 0) + goto fail1; + + EFSYS_ASSERT3U(enp->en_nvram_partn_locked, ==, partn); + + if ((rc = envop->envo_partn_read_backup(enp, partn, offset, + data, size)) != 0) + goto fail2; + + return (0); + fail2: EFSYS_PROBE(fail2); fail1: