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 */
105 EFSYS_ASSERT3U(max_size, <=, BOOTCFG_MAX_SIZE);
107 if (sector_countp != NULL)
108 *sector_countp = count;
110 *max_sizep = max_size;
114 #if EFSYS_OPT_MEDFORD
119 EFSYS_PROBE1(fail1, efx_rc_t, rc);
124 static __checkReturn uint8_t
127 __in_bcount(size) uint8_t const *data,
130 _NOTE(ARGUNUSED(enp))
133 uint8_t checksum = 0;
135 for (pos = 0; pos < size; pos++)
136 checksum += data[pos];
140 static __checkReturn efx_rc_t
143 __in_bcount(size) uint8_t const *data,
145 __out_opt size_t *usedp)
151 /* Start parsing tags immediately after the checksum */
152 for (offset = 1; offset < size; ) {
158 if (tag == DHCP_END) {
163 if (tag == DHCP_PAD) {
169 if (offset + 1 >= size) {
173 length = data[offset + 1];
175 /* Consume *length */
176 if (offset + 1 + length >= size) {
181 offset += 2 + length;
185 /* Checksum the entire sector, including bytes after any DHCP_END */
186 if (efx_bootcfg_csum(enp, data, size) != 0) {
201 EFSYS_PROBE1(fail1, efx_rc_t, rc);
207 * Copy bootcfg sector data to a target buffer which may differ in size.
208 * Optionally corrects format errors in source buffer.
211 efx_bootcfg_copy_sector(
213 __inout_bcount(sector_length)
215 __in size_t sector_length,
216 __out_bcount(data_size) uint8_t *data,
217 __in size_t data_size,
218 __in boolean_t handle_format_errors)
223 /* Verify that the area is correctly formatted and checksummed */
224 rc = efx_bootcfg_verify(enp, sector, sector_length,
227 if (!handle_format_errors) {
231 if ((used_bytes < 2) ||
232 (sector[used_bytes - 1] != DHCP_END)) {
233 /* Block too short, or DHCP_END missing */
239 /* Synthesize empty format on verification failure */
240 if (rc != 0 || used_bytes == 0) {
242 sector[1] = DHCP_END;
245 EFSYS_ASSERT(used_bytes >= 2); /* checksum and DHCP_END */
246 EFSYS_ASSERT(used_bytes <= sector_length);
247 EFSYS_ASSERT(sector_length >= 2);
250 * Legacy bootcfg sectors don't terminate with a DHCP_END character.
251 * Modify the returned payload so it does.
252 * Reinitialise the sector if there isn't room for the character.
254 if (sector[used_bytes - 1] != DHCP_END) {
255 if (used_bytes >= sector_length) {
259 sector[used_bytes] = DHCP_END;
264 * Verify that the target buffer is large enough for the
265 * entire used bootcfg area, then copy into the target buffer.
267 if (used_bytes > data_size) {
271 memcpy(data, sector, used_bytes);
273 /* Zero out the unused portion of the target buffer */
274 if (used_bytes < data_size)
275 (void) memset(data + used_bytes, 0, data_size - used_bytes);
278 * The checksum includes trailing data after any DHCP_END character,
279 * which we've just modified (by truncation or appending DHCP_END).
281 data[0] -= efx_bootcfg_csum(enp, data, data_size);
290 EFSYS_PROBE1(fail1, efx_rc_t, rc);
298 __out_bcount(size) uint8_t *data,
301 uint8_t *payload = NULL;
304 size_t sector_length;
305 size_t sector_offset;
307 uint32_t sector_number;
309 #if EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD
310 sector_number = enp->en_nic_cfg.enc_pf;
314 rc = efx_nvram_size(enp, EFX_NVRAM_BOOTROM_CFG, &partn_length);
318 /* The bootcfg sector may be stored in a (larger) shared partition */
319 rc = efx_bootcfg_sector_info(enp, sector_number,
320 NULL, §or_offset, §or_length);
324 if (sector_length > BOOTCFG_MAX_SIZE)
325 sector_length = BOOTCFG_MAX_SIZE;
327 if (sector_offset + sector_length > partn_length) {
328 /* Partition is too small */
334 * We need to read the entire BOOTCFG sector to ensure we read all the
335 * tags, because legacy bootcfg sectors are not guaranteed to end with
336 * a DHCP_END character. If the user hasn't supplied a sufficiently
337 * large buffer then use our own buffer.
339 if (sector_length > size) {
340 EFSYS_KMEM_ALLOC(enp->en_esip, sector_length, payload);
341 if (payload == NULL) {
346 payload = (uint8_t *)data;
348 if ((rc = efx_nvram_rw_start(enp, EFX_NVRAM_BOOTROM_CFG, NULL)) != 0)
351 if ((rc = efx_nvram_read_chunk(enp, EFX_NVRAM_BOOTROM_CFG,
352 sector_offset, (caddr_t)payload, sector_length)) != 0) {
353 (void) efx_nvram_rw_finish(enp, EFX_NVRAM_BOOTROM_CFG, NULL);
357 if ((rc = efx_nvram_rw_finish(enp, EFX_NVRAM_BOOTROM_CFG, NULL)) != 0)
360 /* Verify that the area is correctly formatted and checksummed */
361 rc = efx_bootcfg_verify(enp, payload, sector_length,
363 if (rc != 0 || used_bytes == 0) {
364 payload[0] = (uint8_t)(~DHCP_END & 0xff);
365 payload[1] = DHCP_END;
369 EFSYS_ASSERT(used_bytes >= 2); /* checksum and DHCP_END */
370 EFSYS_ASSERT(used_bytes <= sector_length);
373 * Legacy bootcfg sectors don't terminate with a DHCP_END character.
374 * Modify the returned payload so it does. BOOTCFG_MAX_SIZE is by
375 * definition large enough for any valid (per-port) bootcfg sector,
376 * so reinitialise the sector if there isn't room for the character.
378 if (payload[used_bytes - 1] != DHCP_END) {
379 if (used_bytes + 1 > sector_length) {
384 payload[used_bytes] = DHCP_END;
389 * Verify that the user supplied buffer is large enough for the
390 * entire used bootcfg area, then copy into the user supplied buffer.
392 if (used_bytes > size) {
396 if (sector_length > size) {
397 memcpy(data, payload, used_bytes);
398 EFSYS_KMEM_FREE(enp->en_esip, sector_length, payload);
401 /* Zero out the unused portion of the user buffer */
402 if (used_bytes < size)
403 (void) memset(data + used_bytes, 0, size - used_bytes);
406 * The checksum includes trailing data after any DHCP_END character,
407 * which we've just modified (by truncation or appending DHCP_END).
409 data[0] -= efx_bootcfg_csum(enp, data, size);
421 if (sector_length > size)
422 EFSYS_KMEM_FREE(enp->en_esip, sector_length, payload);
430 EFSYS_PROBE1(fail1, efx_rc_t, rc);
438 __in_bcount(size) uint8_t *data,
444 size_t sector_length;
445 size_t sector_offset;
448 uint32_t sector_number;
450 #if EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD
451 sector_number = enp->en_nic_cfg.enc_pf;
456 rc = efx_nvram_size(enp, EFX_NVRAM_BOOTROM_CFG, &partn_length);
460 /* The bootcfg sector may be stored in a (larger) shared partition */
461 rc = efx_bootcfg_sector_info(enp, sector_number,
462 NULL, §or_offset, §or_length);
466 if (sector_length > BOOTCFG_MAX_SIZE)
467 sector_length = BOOTCFG_MAX_SIZE;
469 if (sector_offset + sector_length > partn_length) {
470 /* Partition is too small */
475 if ((rc = efx_bootcfg_verify(enp, data, size, &used_bytes)) != 0)
478 /* The caller *must* terminate their block with a DHCP_END character */
479 if ((used_bytes < 2) || ((uint8_t)data[used_bytes - 1] != DHCP_END)) {
480 /* Block too short or DHCP_END missing */
485 /* Check that the hardware has support for this much data */
486 if (used_bytes > MIN(sector_length, BOOTCFG_MAX_SIZE)) {
492 * If the BOOTCFG sector is stored in a shared partition, then we must
493 * read the whole partition and insert the updated bootcfg sector at the
496 EFSYS_KMEM_ALLOC(enp->en_esip, partn_length, partn_data);
497 if (partn_data == NULL) {
502 rc = efx_nvram_rw_start(enp, EFX_NVRAM_BOOTROM_CFG, NULL);
506 /* Read the entire partition */
507 rc = efx_nvram_read_chunk(enp, EFX_NVRAM_BOOTROM_CFG, 0,
508 (caddr_t)partn_data, partn_length);
513 * Insert the BOOTCFG sector into the partition, Zero out all data after
514 * the DHCP_END tag, and adjust the checksum.
516 (void) memset(partn_data + sector_offset, 0x0, sector_length);
517 (void) memcpy(partn_data + sector_offset, data, used_bytes);
519 checksum = efx_bootcfg_csum(enp, data, used_bytes);
520 partn_data[sector_offset] -= checksum;
522 if ((rc = efx_nvram_erase(enp, EFX_NVRAM_BOOTROM_CFG)) != 0)
525 if ((rc = efx_nvram_write_chunk(enp, EFX_NVRAM_BOOTROM_CFG,
526 0, (caddr_t)partn_data, partn_length)) != 0)
529 if ((rc = efx_nvram_rw_finish(enp, EFX_NVRAM_BOOTROM_CFG, NULL)) != 0)
532 EFSYS_KMEM_FREE(enp->en_esip, partn_length, partn_data);
545 (void) efx_nvram_rw_finish(enp, EFX_NVRAM_BOOTROM_CFG, NULL);
549 EFSYS_KMEM_FREE(enp->en_esip, partn_length, partn_data);
563 EFSYS_PROBE1(fail1, efx_rc_t, rc);
568 #endif /* EFSYS_OPT_BOOTCFG */