1 /* SPDX-License-Identifier: BSD-3-Clause
3 * Copyright (c) 2009-2018 Solarflare Communications Inc.
13 * Maximum size of BOOTCFG block across all nics as understood by SFCgPXE.
14 * NOTE: This is larger than the Medford per-PF bootcfg sector.
16 #define BOOTCFG_MAX_SIZE 0x1000
18 /* Medford per-PF bootcfg sector */
19 #define BOOTCFG_PER_PF 0x800
20 #define BOOTCFG_PF_COUNT 16
22 #define DHCP_END ((uint8_t)0xff)
23 #define DHCP_PAD ((uint8_t)0)
26 /* Report the layout of bootcfg sectors in NVRAM partition. */
27 __checkReturn efx_rc_t
28 efx_bootcfg_sector_info(
31 __out_opt uint32_t *sector_countp,
32 __out size_t *offsetp,
33 __out size_t *max_sizep)
40 switch (enp->en_family) {
42 case EFX_FAMILY_SIENA:
43 max_size = BOOTCFG_MAX_SIZE;
47 #endif /* EFSYS_OPT_SIENA */
49 #if EFSYS_OPT_HUNTINGTON
50 case EFX_FAMILY_HUNTINGTON:
51 max_size = BOOTCFG_MAX_SIZE;
55 #endif /* EFSYS_OPT_HUNTINGTON */
58 case EFX_FAMILY_MEDFORD: {
59 /* Shared partition (array indexed by PF) */
60 max_size = BOOTCFG_PER_PF;
61 count = BOOTCFG_PF_COUNT;
66 offset = max_size * pf;
69 #endif /* EFSYS_OPT_MEDFORD */
71 #if EFSYS_OPT_MEDFORD2
72 case EFX_FAMILY_MEDFORD2: {
73 /* Shared partition (array indexed by PF) */
74 max_size = BOOTCFG_PER_PF;
75 count = BOOTCFG_PF_COUNT;
80 offset = max_size * pf;
83 #endif /* EFSYS_OPT_MEDFORD2 */
90 EFSYS_ASSERT3U(max_size, <=, BOOTCFG_MAX_SIZE);
92 if (sector_countp != NULL)
93 *sector_countp = count;
95 *max_sizep = max_size;
99 #if EFSYS_OPT_MEDFORD2
103 #if EFSYS_OPT_MEDFORD
108 EFSYS_PROBE1(fail1, efx_rc_t, rc);
113 static __checkReturn uint8_t
116 __in_bcount(size) uint8_t const *data,
119 _NOTE(ARGUNUSED(enp))
122 uint8_t checksum = 0;
124 for (pos = 0; pos < size; pos++)
125 checksum += data[pos];
129 static __checkReturn efx_rc_t
132 __in_bcount(size) uint8_t const *data,
134 __out_opt size_t *usedp)
140 /* Start parsing tags immediately after the checksum */
141 for (offset = 1; offset < size; ) {
147 if (tag == DHCP_END) {
152 if (tag == DHCP_PAD) {
158 if (offset + 1 >= size) {
162 length = data[offset + 1];
164 /* Consume *length */
165 if (offset + 1 + length >= size) {
170 offset += 2 + length;
174 /* Checksum the entire sector, including bytes after any DHCP_END */
175 if (efx_bootcfg_csum(enp, data, size) != 0) {
190 EFSYS_PROBE1(fail1, efx_rc_t, rc);
196 * Copy bootcfg sector data to a target buffer which may differ in size.
197 * Optionally corrects format errors in source buffer.
200 efx_bootcfg_copy_sector(
202 __inout_bcount(sector_length)
204 __in size_t sector_length,
205 __out_bcount(data_size) uint8_t *data,
206 __in size_t data_size,
207 __in boolean_t handle_format_errors)
212 /* Minimum buffer is checksum byte and DHCP_END terminator */
218 /* Verify that the area is correctly formatted and checksummed */
219 rc = efx_bootcfg_verify(enp, sector, sector_length,
222 if (!handle_format_errors) {
226 if ((used_bytes < 2) ||
227 (sector[used_bytes - 1] != DHCP_END)) {
228 /* Block too short, or DHCP_END missing */
234 /* Synthesize empty format on verification failure */
235 if (rc != 0 || used_bytes == 0) {
237 sector[1] = DHCP_END;
240 EFSYS_ASSERT(used_bytes >= 2); /* checksum and DHCP_END */
241 EFSYS_ASSERT(used_bytes <= sector_length);
242 EFSYS_ASSERT(sector_length >= 2);
245 * Legacy bootcfg sectors don't terminate with a DHCP_END character.
246 * Modify the returned payload so it does.
247 * Reinitialise the sector if there isn't room for the character.
249 if (sector[used_bytes - 1] != DHCP_END) {
250 if (used_bytes >= sector_length) {
254 sector[used_bytes] = DHCP_END;
259 * Verify that the target buffer is large enough for the
260 * entire used bootcfg area, then copy into the target buffer.
262 if (used_bytes > data_size) {
267 data[0] = 0; /* checksum, updated below */
269 /* Copy all after the checksum to the target buffer */
270 memcpy(data + 1, sector + 1, used_bytes - 1);
272 /* Zero out the unused portion of the target buffer */
273 if (used_bytes < data_size)
274 (void) memset(data + used_bytes, 0, data_size - used_bytes);
277 * The checksum includes trailing data after any DHCP_END character,
278 * which we've just modified (by truncation or appending DHCP_END).
280 data[0] -= efx_bootcfg_csum(enp, data, data_size);
291 EFSYS_PROBE1(fail1, efx_rc_t, rc);
299 __out_bcount(size) uint8_t *data,
302 uint8_t *payload = NULL;
305 size_t sector_length;
306 size_t sector_offset;
308 uint32_t sector_number;
310 /* Minimum buffer is checksum byte and DHCP_END terminator */
316 #if EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD || EFSYS_OPT_MEDFORD2
317 sector_number = enp->en_nic_cfg.enc_pf;
321 rc = efx_nvram_size(enp, EFX_NVRAM_BOOTROM_CFG, &partn_length);
325 /* The bootcfg sector may be stored in a (larger) shared partition */
326 rc = efx_bootcfg_sector_info(enp, sector_number,
327 NULL, §or_offset, §or_length);
331 if (sector_length < 2) {
336 if (sector_length > BOOTCFG_MAX_SIZE)
337 sector_length = BOOTCFG_MAX_SIZE;
339 if (sector_offset + sector_length > partn_length) {
340 /* Partition is too small */
346 * We need to read the entire BOOTCFG sector to ensure we read all the
347 * tags, because legacy bootcfg sectors are not guaranteed to end with
348 * a DHCP_END character. If the user hasn't supplied a sufficiently
349 * large buffer then use our own buffer.
351 if (sector_length > size) {
352 EFSYS_KMEM_ALLOC(enp->en_esip, sector_length, payload);
353 if (payload == NULL) {
358 payload = (uint8_t *)data;
360 if ((rc = efx_nvram_rw_start(enp, EFX_NVRAM_BOOTROM_CFG, NULL)) != 0)
363 if ((rc = efx_nvram_read_chunk(enp, EFX_NVRAM_BOOTROM_CFG,
364 sector_offset, (caddr_t)payload, sector_length)) != 0) {
365 (void) efx_nvram_rw_finish(enp, EFX_NVRAM_BOOTROM_CFG, NULL);
369 if ((rc = efx_nvram_rw_finish(enp, EFX_NVRAM_BOOTROM_CFG, NULL)) != 0)
372 /* Verify that the area is correctly formatted and checksummed */
373 rc = efx_bootcfg_verify(enp, payload, sector_length,
375 if (rc != 0 || used_bytes == 0) {
377 payload[1] = DHCP_END;
381 EFSYS_ASSERT(used_bytes >= 2); /* checksum and DHCP_END */
382 EFSYS_ASSERT(used_bytes <= sector_length);
385 * Legacy bootcfg sectors don't terminate with a DHCP_END character.
386 * Modify the returned payload so it does. BOOTCFG_MAX_SIZE is by
387 * definition large enough for any valid (per-port) bootcfg sector,
388 * so reinitialise the sector if there isn't room for the character.
390 if (payload[used_bytes - 1] != DHCP_END) {
391 if (used_bytes >= sector_length)
394 payload[used_bytes] = DHCP_END;
399 * Verify that the user supplied buffer is large enough for the
400 * entire used bootcfg area, then copy into the user supplied buffer.
402 if (used_bytes > size) {
407 data[0] = 0; /* checksum, updated below */
409 if (sector_length > size) {
410 /* Copy all after the checksum to the target buffer */
411 memcpy(data + 1, payload + 1, used_bytes - 1);
412 EFSYS_KMEM_FREE(enp->en_esip, sector_length, payload);
415 /* Zero out the unused portion of the user buffer */
416 if (used_bytes < size)
417 (void) memset(data + used_bytes, 0, size - used_bytes);
420 * The checksum includes trailing data after any DHCP_END character,
421 * which we've just modified (by truncation or appending DHCP_END).
423 data[0] -= efx_bootcfg_csum(enp, data, size);
435 if (sector_length > size)
436 EFSYS_KMEM_FREE(enp->en_esip, sector_length, payload);
448 EFSYS_PROBE1(fail1, efx_rc_t, rc);
456 __in_bcount(size) uint8_t *data,
462 size_t sector_length;
463 size_t sector_offset;
466 uint32_t sector_number;
468 #if EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD || EFSYS_OPT_MEDFORD2
469 sector_number = enp->en_nic_cfg.enc_pf;
474 rc = efx_nvram_size(enp, EFX_NVRAM_BOOTROM_CFG, &partn_length);
478 /* The bootcfg sector may be stored in a (larger) shared partition */
479 rc = efx_bootcfg_sector_info(enp, sector_number,
480 NULL, §or_offset, §or_length);
484 if (sector_length > BOOTCFG_MAX_SIZE)
485 sector_length = BOOTCFG_MAX_SIZE;
487 if (sector_offset + sector_length > partn_length) {
488 /* Partition is too small */
493 if ((rc = efx_bootcfg_verify(enp, data, size, &used_bytes)) != 0)
496 /* The caller *must* terminate their block with a DHCP_END character */
497 if ((used_bytes < 2) || ((uint8_t)data[used_bytes - 1] != DHCP_END)) {
498 /* Block too short or DHCP_END missing */
503 /* Check that the hardware has support for this much data */
504 if (used_bytes > MIN(sector_length, BOOTCFG_MAX_SIZE)) {
510 * If the BOOTCFG sector is stored in a shared partition, then we must
511 * read the whole partition and insert the updated bootcfg sector at the
514 EFSYS_KMEM_ALLOC(enp->en_esip, partn_length, partn_data);
515 if (partn_data == NULL) {
520 rc = efx_nvram_rw_start(enp, EFX_NVRAM_BOOTROM_CFG, NULL);
524 /* Read the entire partition */
525 rc = efx_nvram_read_chunk(enp, EFX_NVRAM_BOOTROM_CFG, 0,
526 (caddr_t)partn_data, partn_length);
531 * Insert the BOOTCFG sector into the partition, Zero out all data after
532 * the DHCP_END tag, and adjust the checksum.
534 (void) memset(partn_data + sector_offset, 0x0, sector_length);
535 (void) memcpy(partn_data + sector_offset, data, used_bytes);
537 checksum = efx_bootcfg_csum(enp, data, used_bytes);
538 partn_data[sector_offset] -= checksum;
540 if ((rc = efx_nvram_erase(enp, EFX_NVRAM_BOOTROM_CFG)) != 0)
543 if ((rc = efx_nvram_write_chunk(enp, EFX_NVRAM_BOOTROM_CFG,
544 0, (caddr_t)partn_data, partn_length)) != 0)
547 if ((rc = efx_nvram_rw_finish(enp, EFX_NVRAM_BOOTROM_CFG, NULL)) != 0)
550 EFSYS_KMEM_FREE(enp->en_esip, partn_length, partn_data);
563 (void) efx_nvram_rw_finish(enp, EFX_NVRAM_BOOTROM_CFG, NULL);
567 EFSYS_KMEM_FREE(enp->en_esip, partn_length, partn_data);
581 EFSYS_PROBE1(fail1, efx_rc_t, rc);
586 #endif /* EFSYS_OPT_BOOTCFG */