From 9602dbf4c3a96c382d196a8f65aa338bbfb28826 Mon Sep 17 00:00:00 2001 From: arybchik Date: Wed, 28 Dec 2016 17:49:33 +0000 Subject: [PATCH] sfxge(4): support Medford bootcfg partition layout in common code For Siena and Huntington, the per-port bootcfg (aka expcfg) is stored in a dedicated 4Kbyte partition for each port. For Medford, the per-PF bootcfg is stored in a 2Kbyte sector within a single shared partition. Update the common code to support the new bootcfg layout. Submitted by: Andy Moreton Reviewed by: gnn Sponsored by: Solarflare Communications, Inc. MFC after: 2 days Differential Revision: https://reviews.freebsd.org/D8932 --- sys/dev/sfxge/common/efx_bootcfg.c | 209 +++++++++++++++++++++-------- 1 file changed, 150 insertions(+), 59 deletions(-) diff --git a/sys/dev/sfxge/common/efx_bootcfg.c b/sys/dev/sfxge/common/efx_bootcfg.c index ea86141507e5..ce7e3c333755 100644 --- a/sys/dev/sfxge/common/efx_bootcfg.c +++ b/sys/dev/sfxge/common/efx_bootcfg.c @@ -38,14 +38,69 @@ __FBSDID("$FreeBSD$"); /* * Maximum size of BOOTCFG block across all nics as understood by SFCgPXE. - * A multiple of 0x100 so trailing 0xff characters don't contrinbute to the - * checksum. + * NOTE: This is larger than the Medford per-PF bootcfg sector. */ #define BOOTCFG_MAX_SIZE 0x1000 #define DHCP_END ((uint8_t)0xff) #define DHCP_PAD ((uint8_t)0) + +/* Report size and offset of bootcfg sector in NVRAM partition. */ +static __checkReturn efx_rc_t +efx_bootcfg_sector( + __in efx_nic_t *enp, + __out size_t *offsetp, + __out size_t *max_sizep) +{ + size_t max_size; + size_t offset; + int rc; + + switch (enp->en_family) { +#if EFSYS_OPT_SIENA + case EFX_FAMILY_SIENA: + max_size = BOOTCFG_MAX_SIZE; + offset = 0; + break; +#endif /* EFSYS_OPT_SIENA */ + +#if EFSYS_OPT_HUNTINGTON + case EFX_FAMILY_HUNTINGTON: + max_size = BOOTCFG_MAX_SIZE; + offset = 0; + break; +#endif /* EFSYS_OPT_HUNTINGTON */ + +#if EFSYS_OPT_MEDFORD + case EFX_FAMILY_MEDFORD: { + efx_nic_cfg_t *encp = &(enp->en_nic_cfg); + + /* Shared partition (array indexed by PF) */ + max_size = 0x0800; + offset = max_size * encp->enc_pf; + break; + } +#endif /* EFSYS_OPT_MEDFORD */ + + default: + EFSYS_ASSERT(0); + rc = ENOTSUP; + goto fail1; + } + EFSYS_ASSERT3U(max_size, <=, BOOTCFG_MAX_SIZE); + + *offsetp = offset; + *max_sizep = max_size; + + return (0); + +fail1: + EFSYS_PROBE1(fail1, efx_rc_t, rc); + return (rc); +} + + static __checkReturn uint8_t efx_bootcfg_csum( __in efx_nic_t *enp, @@ -136,40 +191,54 @@ efx_bootcfg_read( { uint8_t *payload = NULL; size_t used_bytes; + size_t partn_length; size_t sector_length; + size_t sector_offset; efx_rc_t rc; - rc = efx_nvram_size(enp, EFX_NVRAM_BOOTROM_CFG, §or_length); + rc = efx_nvram_size(enp, EFX_NVRAM_BOOTROM_CFG, &partn_length); if (rc != 0) goto fail1; + /* The bootcfg sector may be stored in a (larger) shared partition */ + rc = efx_bootcfg_sector(enp, §or_offset, §or_length); + if (rc != 0) + goto fail2; + + if (sector_length > BOOTCFG_MAX_SIZE) + sector_length = BOOTCFG_MAX_SIZE; + + if (sector_offset + sector_length > partn_length) { + /* Partition is too small */ + rc = EFBIG; + goto fail3; + } + /* - * We need to read the entire BOOTCFG area to ensure we read all the + * We need to read the entire BOOTCFG sector to ensure we read all the * tags, because legacy bootcfg sectors are not guaranteed to end with * a DHCP_END character. If the user hasn't supplied a sufficiently * large buffer then use our own buffer. */ - if (sector_length > BOOTCFG_MAX_SIZE) - sector_length = BOOTCFG_MAX_SIZE; if (sector_length > size) { EFSYS_KMEM_ALLOC(enp->en_esip, sector_length, payload); if (payload == NULL) { rc = ENOMEM; - goto fail2; + goto fail4; } } else payload = (uint8_t *)data; if ((rc = efx_nvram_rw_start(enp, EFX_NVRAM_BOOTROM_CFG, NULL)) != 0) - goto fail3; + goto fail5; - rc = efx_nvram_read_chunk(enp, EFX_NVRAM_BOOTROM_CFG, 0, + rc = efx_nvram_read_chunk(enp, EFX_NVRAM_BOOTROM_CFG, sector_offset, (caddr_t)payload, sector_length); efx_nvram_rw_finish(enp, EFX_NVRAM_BOOTROM_CFG); if (rc != 0) - goto fail4; + goto fail6; /* Verify that the area is correctly formatted and checksummed */ rc = efx_bootcfg_verify(enp, (caddr_t)payload, sector_length, @@ -205,7 +274,7 @@ efx_bootcfg_read( */ if (used_bytes > size) { rc = ENOSPC; - goto fail5; + goto fail7; } if (sector_length > size) { memcpy(data, payload, used_bytes); @@ -224,15 +293,18 @@ efx_bootcfg_read( return (0); +fail7: + EFSYS_PROBE(fail7); +fail6: + EFSYS_PROBE(fail6); fail5: EFSYS_PROBE(fail5); + if (sector_length > size) + EFSYS_KMEM_FREE(enp->en_esip, sector_length, payload); fail4: EFSYS_PROBE(fail4); fail3: EFSYS_PROBE(fail3); - - if (sector_length > size) - EFSYS_KMEM_FREE(enp->en_esip, sector_length, payload); fail2: EFSYS_PROBE(fail2); fail1: @@ -247,90 +319,109 @@ efx_bootcfg_write( __in_bcount(size) caddr_t data, __in size_t size) { - uint8_t *chunk; + uint8_t *partn_data; uint8_t checksum; + size_t partn_length; size_t sector_length; - size_t chunk_length; + size_t sector_offset; size_t used_bytes; - size_t offset; - size_t remaining; efx_rc_t rc; - rc = efx_nvram_size(enp, EFX_NVRAM_BOOTROM_CFG, §or_length); + rc = efx_nvram_size(enp, EFX_NVRAM_BOOTROM_CFG, &partn_length); if (rc != 0) goto fail1; + /* The bootcfg sector may be stored in a (larger) shared partition */ + rc = efx_bootcfg_sector(enp, §or_offset, §or_length); + if (rc != 0) + goto fail2; + if (sector_length > BOOTCFG_MAX_SIZE) sector_length = BOOTCFG_MAX_SIZE; + if (sector_offset + sector_length > partn_length) { + /* Partition is too small */ + rc = EFBIG; + goto fail3; + } + if ((rc = efx_bootcfg_verify(enp, data, size, &used_bytes)) != 0) - goto fail2; + goto fail4; /* The caller *must* terminate their block with a DHCP_END character */ - EFSYS_ASSERT(used_bytes >= 2); /* checksum and DHCP_END */ - if ((uint8_t)data[used_bytes - 1] != DHCP_END) { + if ((used_bytes < 2) || ((uint8_t)data[used_bytes - 1] != DHCP_END)) { + /* Block too short or DHCP_END missing */ rc = ENOENT; - goto fail3; + goto fail5; } /* Check that the hardware has support for this much data */ if (used_bytes > MIN(sector_length, BOOTCFG_MAX_SIZE)) { rc = ENOSPC; - goto fail4; - } - - rc = efx_nvram_rw_start(enp, EFX_NVRAM_BOOTROM_CFG, &chunk_length); - if (rc != 0) - goto fail5; - - EFSYS_KMEM_ALLOC(enp->en_esip, chunk_length, chunk); - if (chunk == NULL) { - rc = ENOMEM; goto fail6; } - if ((rc = efx_nvram_erase(enp, EFX_NVRAM_BOOTROM_CFG)) != 0) + /* + * If the BOOTCFG sector is stored in a shared partition, then we must + * read the whole partition and insert the updated bootcfg sector at the + * correct offset. + */ + EFSYS_KMEM_ALLOC(enp->en_esip, partn_length, partn_data); + if (partn_data == NULL) { + rc = ENOMEM; goto fail7; + } + + rc = efx_nvram_rw_start(enp, EFX_NVRAM_BOOTROM_CFG, NULL); + if (rc != 0) + goto fail8; + + /* Read the entire partition */ + rc = efx_nvram_read_chunk(enp, EFX_NVRAM_BOOTROM_CFG, 0, + (caddr_t)partn_data, partn_length); + if (rc != 0) + goto fail9; /* - * Write the entire sector_length bytes of data in chunks. Zero out - * all data following the DHCP_END, and adjust the checksum + * Insert the BOOTCFG sector into the partition, Zero out all data after + * the DHCP_END tag, and adjust the checksum. */ + (void) memset(partn_data + sector_offset, 0x0, sector_length); + (void) memcpy(partn_data + sector_offset, data, used_bytes); + checksum = efx_bootcfg_csum(enp, data, used_bytes); - for (offset = 0; offset < sector_length; offset += remaining) { - remaining = MIN(chunk_length, sector_length - offset); + partn_data[sector_offset] -= checksum; - /* Fill chunk */ - (void) memset(chunk, 0x0, chunk_length); - if (offset < used_bytes) - memcpy(chunk, data + offset, - MIN(remaining, used_bytes - offset)); + if ((rc = efx_nvram_erase(enp, EFX_NVRAM_BOOTROM_CFG)) != 0) + goto fail10; - /* Adjust checksum */ - if (offset == 0) - chunk[0] -= checksum; - - if ((rc = efx_nvram_write_chunk(enp, EFX_NVRAM_BOOTROM_CFG, - offset, (caddr_t)chunk, remaining)) != 0) - goto fail8; + if ((rc = efx_nvram_write_chunk(enp, EFX_NVRAM_BOOTROM_CFG, + 0, partn_data, partn_length)) != 0) { + goto fail11; } efx_nvram_rw_finish(enp, EFX_NVRAM_BOOTROM_CFG); - EFSYS_KMEM_FREE(enp->en_esip, chunk_length, chunk); + EFSYS_KMEM_FREE(enp->en_esip, partn_length, partn_data); return (0); -fail8: - EFSYS_PROBE(fail8); -fail7: - EFSYS_PROBE(fail7); - - EFSYS_KMEM_FREE(enp->en_esip, chunk_length, chunk); -fail6: - EFSYS_PROBE(fail6); +fail11: + EFSYS_PROBE(fail11); +fail10: + EFSYS_PROBE(fail10); +fail9: + EFSYS_PROBE(fail9); efx_nvram_rw_finish(enp, EFX_NVRAM_BOOTROM_CFG); +fail8: + EFSYS_PROBE(fail8); + + EFSYS_KMEM_FREE(enp->en_esip, partn_length, partn_data); +fail7: + EFSYS_PROBE(fail7); +fail6: + EFSYS_PROBE(fail6); fail5: EFSYS_PROBE(fail5); fail4: