2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
4 * Copyright (c) 2009-2016 Solarflare Communications Inc.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are met:
10 * 1. Redistributions of source code must retain the above copyright notice,
11 * this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright notice,
13 * this list of conditions and the following disclaimer in the documentation
14 * and/or other materials provided with the distribution.
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
18 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
20 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
21 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
23 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
24 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
25 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
26 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 * The views and conclusions contained in the software and documentation are
29 * those of the authors and should not be interpreted as representing official
30 * policies, either expressed or implied, of the FreeBSD Project.
33 #include <sys/cdefs.h>
34 __FBSDID("$FreeBSD$");
42 * Maximum size of BOOTCFG block across all nics as understood by SFCgPXE.
43 * NOTE: This is larger than the Medford per-PF bootcfg sector.
45 #define BOOTCFG_MAX_SIZE 0x1000
47 /* Medford per-PF bootcfg sector */
48 #define BOOTCFG_PER_PF 0x800
49 #define BOOTCFG_PF_COUNT 16
51 #define DHCP_END ((uint8_t)0xff)
52 #define DHCP_PAD ((uint8_t)0)
55 /* Report the layout of bootcfg sectors in NVRAM partition. */
56 __checkReturn efx_rc_t
57 efx_bootcfg_sector_info(
60 __out_opt uint32_t *sector_countp,
61 __out size_t *offsetp,
62 __out size_t *max_sizep)
69 switch (enp->en_family) {
71 case EFX_FAMILY_SIENA:
72 max_size = BOOTCFG_MAX_SIZE;
76 #endif /* EFSYS_OPT_SIENA */
78 #if EFSYS_OPT_HUNTINGTON
79 case EFX_FAMILY_HUNTINGTON:
80 max_size = BOOTCFG_MAX_SIZE;
84 #endif /* EFSYS_OPT_HUNTINGTON */
87 case EFX_FAMILY_MEDFORD: {
88 /* Shared partition (array indexed by PF) */
89 max_size = BOOTCFG_PER_PF;
90 count = BOOTCFG_PF_COUNT;
95 offset = max_size * pf;
98 #endif /* EFSYS_OPT_MEDFORD */
100 #if EFSYS_OPT_MEDFORD2
101 case EFX_FAMILY_MEDFORD2: {
102 /* Shared partition (array indexed by PF) */
103 max_size = BOOTCFG_PER_PF;
104 count = BOOTCFG_PF_COUNT;
109 offset = max_size * pf;
112 #endif /* EFSYS_OPT_MEDFORD2 */
119 EFSYS_ASSERT3U(max_size, <=, BOOTCFG_MAX_SIZE);
121 if (sector_countp != NULL)
122 *sector_countp = count;
124 *max_sizep = max_size;
128 #if EFSYS_OPT_MEDFORD2
132 #if EFSYS_OPT_MEDFORD
137 EFSYS_PROBE1(fail1, efx_rc_t, rc);
142 static __checkReturn uint8_t
145 __in_bcount(size) uint8_t const *data,
148 _NOTE(ARGUNUSED(enp))
151 uint8_t checksum = 0;
153 for (pos = 0; pos < size; pos++)
154 checksum += data[pos];
158 static __checkReturn efx_rc_t
161 __in_bcount(size) uint8_t const *data,
163 __out_opt size_t *usedp)
169 /* Start parsing tags immediately after the checksum */
170 for (offset = 1; offset < size; ) {
176 if (tag == DHCP_END) {
181 if (tag == DHCP_PAD) {
187 if (offset + 1 >= size) {
191 length = data[offset + 1];
193 /* Consume *length */
194 if (offset + 1 + length >= size) {
199 offset += 2 + length;
203 /* Checksum the entire sector, including bytes after any DHCP_END */
204 if (efx_bootcfg_csum(enp, data, size) != 0) {
219 EFSYS_PROBE1(fail1, efx_rc_t, rc);
225 * Copy bootcfg sector data to a target buffer which may differ in size.
226 * Optionally corrects format errors in source buffer.
229 efx_bootcfg_copy_sector(
231 __inout_bcount(sector_length)
233 __in size_t sector_length,
234 __out_bcount(data_size) uint8_t *data,
235 __in size_t data_size,
236 __in boolean_t handle_format_errors)
241 /* Minimum buffer is checksum byte and DHCP_END terminator */
247 /* Verify that the area is correctly formatted and checksummed */
248 rc = efx_bootcfg_verify(enp, sector, sector_length,
251 if (!handle_format_errors) {
255 if ((used_bytes < 2) ||
256 (sector[used_bytes - 1] != DHCP_END)) {
257 /* Block too short, or DHCP_END missing */
263 /* Synthesize empty format on verification failure */
264 if (rc != 0 || used_bytes == 0) {
266 sector[1] = DHCP_END;
269 EFSYS_ASSERT(used_bytes >= 2); /* checksum and DHCP_END */
270 EFSYS_ASSERT(used_bytes <= sector_length);
271 EFSYS_ASSERT(sector_length >= 2);
274 * Legacy bootcfg sectors don't terminate with a DHCP_END character.
275 * Modify the returned payload so it does.
276 * Reinitialise the sector if there isn't room for the character.
278 if (sector[used_bytes - 1] != DHCP_END) {
279 if (used_bytes >= sector_length) {
283 sector[used_bytes] = DHCP_END;
288 * Verify that the target buffer is large enough for the
289 * entire used bootcfg area, then copy into the target buffer.
291 if (used_bytes > data_size) {
296 data[0] = 0; /* checksum, updated below */
298 /* Copy all after the checksum to the target buffer */
299 memcpy(data + 1, sector + 1, used_bytes - 1);
301 /* Zero out the unused portion of the target buffer */
302 if (used_bytes < data_size)
303 (void) memset(data + used_bytes, 0, data_size - used_bytes);
306 * The checksum includes trailing data after any DHCP_END character,
307 * which we've just modified (by truncation or appending DHCP_END).
309 data[0] -= efx_bootcfg_csum(enp, data, data_size);
320 EFSYS_PROBE1(fail1, efx_rc_t, rc);
328 __out_bcount(size) uint8_t *data,
331 uint8_t *payload = NULL;
334 size_t sector_length;
335 size_t sector_offset;
337 uint32_t sector_number;
339 /* Minimum buffer is checksum byte and DHCP_END terminator */
345 #if EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD || EFSYS_OPT_MEDFORD2
346 sector_number = enp->en_nic_cfg.enc_pf;
350 rc = efx_nvram_size(enp, EFX_NVRAM_BOOTROM_CFG, &partn_length);
354 /* The bootcfg sector may be stored in a (larger) shared partition */
355 rc = efx_bootcfg_sector_info(enp, sector_number,
356 NULL, §or_offset, §or_length);
360 if (sector_length < 2) {
365 if (sector_length > BOOTCFG_MAX_SIZE)
366 sector_length = BOOTCFG_MAX_SIZE;
368 if (sector_offset + sector_length > partn_length) {
369 /* Partition is too small */
375 * We need to read the entire BOOTCFG sector to ensure we read all the
376 * tags, because legacy bootcfg sectors are not guaranteed to end with
377 * a DHCP_END character. If the user hasn't supplied a sufficiently
378 * large buffer then use our own buffer.
380 if (sector_length > size) {
381 EFSYS_KMEM_ALLOC(enp->en_esip, sector_length, payload);
382 if (payload == NULL) {
387 payload = (uint8_t *)data;
389 if ((rc = efx_nvram_rw_start(enp, EFX_NVRAM_BOOTROM_CFG, NULL)) != 0)
392 if ((rc = efx_nvram_read_chunk(enp, EFX_NVRAM_BOOTROM_CFG,
393 sector_offset, (caddr_t)payload, sector_length)) != 0) {
394 (void) efx_nvram_rw_finish(enp, EFX_NVRAM_BOOTROM_CFG, NULL);
398 if ((rc = efx_nvram_rw_finish(enp, EFX_NVRAM_BOOTROM_CFG, NULL)) != 0)
401 /* Verify that the area is correctly formatted and checksummed */
402 rc = efx_bootcfg_verify(enp, payload, sector_length,
404 if (rc != 0 || used_bytes == 0) {
406 payload[1] = DHCP_END;
410 EFSYS_ASSERT(used_bytes >= 2); /* checksum and DHCP_END */
411 EFSYS_ASSERT(used_bytes <= sector_length);
414 * Legacy bootcfg sectors don't terminate with a DHCP_END character.
415 * Modify the returned payload so it does. BOOTCFG_MAX_SIZE is by
416 * definition large enough for any valid (per-port) bootcfg sector,
417 * so reinitialise the sector if there isn't room for the character.
419 if (payload[used_bytes - 1] != DHCP_END) {
420 if (used_bytes >= sector_length)
423 payload[used_bytes] = DHCP_END;
428 * Verify that the user supplied buffer is large enough for the
429 * entire used bootcfg area, then copy into the user supplied buffer.
431 if (used_bytes > size) {
436 data[0] = 0; /* checksum, updated below */
438 if (sector_length > size) {
439 /* Copy all after the checksum to the target buffer */
440 memcpy(data + 1, payload + 1, used_bytes - 1);
441 EFSYS_KMEM_FREE(enp->en_esip, sector_length, payload);
444 /* Zero out the unused portion of the user buffer */
445 if (used_bytes < size)
446 (void) memset(data + used_bytes, 0, size - used_bytes);
449 * The checksum includes trailing data after any DHCP_END character,
450 * which we've just modified (by truncation or appending DHCP_END).
452 data[0] -= efx_bootcfg_csum(enp, data, size);
464 if (sector_length > size)
465 EFSYS_KMEM_FREE(enp->en_esip, sector_length, payload);
477 EFSYS_PROBE1(fail1, efx_rc_t, rc);
485 __in_bcount(size) uint8_t *data,
491 size_t sector_length;
492 size_t sector_offset;
495 uint32_t sector_number;
497 #if EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD || EFSYS_OPT_MEDFORD2
498 sector_number = enp->en_nic_cfg.enc_pf;
503 rc = efx_nvram_size(enp, EFX_NVRAM_BOOTROM_CFG, &partn_length);
507 /* The bootcfg sector may be stored in a (larger) shared partition */
508 rc = efx_bootcfg_sector_info(enp, sector_number,
509 NULL, §or_offset, §or_length);
513 if (sector_length > BOOTCFG_MAX_SIZE)
514 sector_length = BOOTCFG_MAX_SIZE;
516 if (sector_offset + sector_length > partn_length) {
517 /* Partition is too small */
522 if ((rc = efx_bootcfg_verify(enp, data, size, &used_bytes)) != 0)
525 /* The caller *must* terminate their block with a DHCP_END character */
526 if ((used_bytes < 2) || ((uint8_t)data[used_bytes - 1] != DHCP_END)) {
527 /* Block too short or DHCP_END missing */
532 /* Check that the hardware has support for this much data */
533 if (used_bytes > MIN(sector_length, BOOTCFG_MAX_SIZE)) {
539 * If the BOOTCFG sector is stored in a shared partition, then we must
540 * read the whole partition and insert the updated bootcfg sector at the
543 EFSYS_KMEM_ALLOC(enp->en_esip, partn_length, partn_data);
544 if (partn_data == NULL) {
549 rc = efx_nvram_rw_start(enp, EFX_NVRAM_BOOTROM_CFG, NULL);
553 /* Read the entire partition */
554 rc = efx_nvram_read_chunk(enp, EFX_NVRAM_BOOTROM_CFG, 0,
555 (caddr_t)partn_data, partn_length);
560 * Insert the BOOTCFG sector into the partition, Zero out all data after
561 * the DHCP_END tag, and adjust the checksum.
563 (void) memset(partn_data + sector_offset, 0x0, sector_length);
564 (void) memcpy(partn_data + sector_offset, data, used_bytes);
566 checksum = efx_bootcfg_csum(enp, data, used_bytes);
567 partn_data[sector_offset] -= checksum;
569 if ((rc = efx_nvram_erase(enp, EFX_NVRAM_BOOTROM_CFG)) != 0)
572 if ((rc = efx_nvram_write_chunk(enp, EFX_NVRAM_BOOTROM_CFG,
573 0, (caddr_t)partn_data, partn_length)) != 0)
576 if ((rc = efx_nvram_rw_finish(enp, EFX_NVRAM_BOOTROM_CFG, NULL)) != 0)
579 EFSYS_KMEM_FREE(enp->en_esip, partn_length, partn_data);
592 (void) efx_nvram_rw_finish(enp, EFX_NVRAM_BOOTROM_CFG, NULL);
596 EFSYS_KMEM_FREE(enp->en_esip, partn_length, partn_data);
610 EFSYS_PROBE1(fail1, efx_rc_t, rc);
615 #endif /* EFSYS_OPT_BOOTCFG */