]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/sfxge/common/efx_bootcfg.c
zfs: merge openzfs/zfs@4a1195ca5 (master) into main
[FreeBSD/FreeBSD.git] / sys / dev / sfxge / common / efx_bootcfg.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2009-2016 Solarflare Communications Inc.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions are met:
9  *
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.
15  *
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.
27  *
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.
31  */
32
33 #include <sys/cdefs.h>
34 __FBSDID("$FreeBSD$");
35
36 #include "efx.h"
37 #include "efx_impl.h"
38
39 #if EFSYS_OPT_BOOTCFG
40
41 /*
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.
44  */
45 #define BOOTCFG_MAX_SIZE 0x1000
46
47 /* Medford per-PF bootcfg sector */
48 #define BOOTCFG_PER_PF   0x800
49 #define BOOTCFG_PF_COUNT 16
50
51 #define DHCP_OPT_HAS_VALUE(opt) \
52         (((opt) > EFX_DHCP_PAD) && ((opt) < EFX_DHCP_END))
53
54 #define DHCP_MAX_VALUE 255
55
56 #define DHCP_ENCAPSULATOR(encap_opt) ((encap_opt) >> 8)
57 #define DHCP_ENCAPSULATED(encap_opt) ((encap_opt) & 0xff)
58 #define DHCP_IS_ENCAP_OPT(opt) DHCP_OPT_HAS_VALUE(DHCP_ENCAPSULATOR(opt))
59
60 typedef struct efx_dhcp_tag_hdr_s {
61         uint8_t         tag;
62         uint8_t         length;
63 } efx_dhcp_tag_hdr_t;
64
65 /*
66  * Length calculations for tags with value field. PAD and END
67  * have a fixed length of 1, with no length or value field.
68  */
69 #define DHCP_FULL_TAG_LENGTH(hdr) \
70         (sizeof (efx_dhcp_tag_hdr_t) + (hdr)->length)
71
72 #define DHCP_NEXT_TAG(hdr) \
73         ((efx_dhcp_tag_hdr_t *)(((uint8_t *)(hdr)) + \
74         DHCP_FULL_TAG_LENGTH((hdr))))
75
76 #define DHCP_CALC_TAG_LENGTH(payload_len) \
77         ((payload_len) + sizeof (efx_dhcp_tag_hdr_t))
78
79 /* Report the layout of bootcfg sectors in NVRAM partition. */
80         __checkReturn           efx_rc_t
81 efx_bootcfg_sector_info(
82         __in                    efx_nic_t *enp,
83         __in                    uint32_t pf,
84         __out_opt               uint32_t *sector_countp,
85         __out                   size_t *offsetp,
86         __out                   size_t *max_sizep)
87 {
88         uint32_t count;
89         size_t max_size;
90         size_t offset;
91         int rc;
92
93         switch (enp->en_family) {
94 #if EFSYS_OPT_SIENA
95         case EFX_FAMILY_SIENA:
96                 max_size = BOOTCFG_MAX_SIZE;
97                 offset = 0;
98                 count = 1;
99                 break;
100 #endif /* EFSYS_OPT_SIENA */
101
102 #if EFSYS_OPT_HUNTINGTON
103         case EFX_FAMILY_HUNTINGTON:
104                 max_size = BOOTCFG_MAX_SIZE;
105                 offset = 0;
106                 count = 1;
107                 break;
108 #endif /* EFSYS_OPT_HUNTINGTON */
109
110 #if EFSYS_OPT_MEDFORD
111         case EFX_FAMILY_MEDFORD: {
112                 /* Shared partition (array indexed by PF) */
113                 max_size = BOOTCFG_PER_PF;
114                 count = BOOTCFG_PF_COUNT;
115                 if (pf >= count) {
116                         rc = EINVAL;
117                         goto fail2;
118                 }
119                 offset = max_size * pf;
120                 break;
121         }
122 #endif /* EFSYS_OPT_MEDFORD */
123
124 #if EFSYS_OPT_MEDFORD2
125         case EFX_FAMILY_MEDFORD2: {
126                 /* Shared partition (array indexed by PF) */
127                 max_size = BOOTCFG_PER_PF;
128                 count = BOOTCFG_PF_COUNT;
129                 if (pf >= count) {
130                         rc = EINVAL;
131                         goto fail3;
132                 }
133                 offset = max_size * pf;
134                 break;
135         }
136 #endif /* EFSYS_OPT_MEDFORD2 */
137
138         default:
139                 EFSYS_ASSERT(0);
140                 rc = ENOTSUP;
141                 goto fail1;
142         }
143         EFSYS_ASSERT3U(max_size, <=, BOOTCFG_MAX_SIZE);
144
145         if (sector_countp != NULL)
146                 *sector_countp = count;
147         *offsetp = offset;
148         *max_sizep = max_size;
149
150         return (0);
151
152 #if EFSYS_OPT_MEDFORD2
153 fail3:
154         EFSYS_PROBE(fail3);
155 #endif
156 #if EFSYS_OPT_MEDFORD
157 fail2:
158         EFSYS_PROBE(fail2);
159 #endif
160 fail1:
161         EFSYS_PROBE1(fail1, efx_rc_t, rc);
162         return (rc);
163 }
164
165         __checkReturn           uint8_t
166 efx_dhcp_csum(
167         __in_bcount(size)       uint8_t const *data,
168         __in                    size_t size)
169 {
170         unsigned int pos;
171         uint8_t checksum = 0;
172
173         for (pos = 0; pos < size; pos++)
174                 checksum += data[pos];
175         return (checksum);
176 }
177
178         __checkReturn           efx_rc_t
179 efx_dhcp_verify(
180         __in_bcount(size)       uint8_t const *data,
181         __in                    size_t size,
182         __out_opt               size_t *usedp)
183 {
184         size_t offset = 0;
185         size_t used = 0;
186         efx_rc_t rc;
187
188         /* Start parsing tags immediately after the checksum */
189         for (offset = 1; offset < size; ) {
190                 uint8_t tag;
191                 uint8_t length;
192
193                 /* Consume tag */
194                 tag = data[offset];
195                 if (tag == EFX_DHCP_END) {
196                         offset++;
197                         used = offset;
198                         break;
199                 }
200                 if (tag == EFX_DHCP_PAD) {
201                         offset++;
202                         continue;
203                 }
204
205                 /* Consume length */
206                 if (offset + 1 >= size) {
207                         rc = ENOSPC;
208                         goto fail1;
209                 }
210                 length = data[offset + 1];
211
212                 /* Consume *length */
213                 if (offset + 1 + length >= size) {
214                         rc = ENOSPC;
215                         goto fail2;
216                 }
217
218                 offset += 2 + length;
219                 used = offset;
220         }
221
222         /* Checksum the entire sector, including bytes after any EFX_DHCP_END */
223         if (efx_dhcp_csum(data, size) != 0) {
224                 rc = EINVAL;
225                 goto fail3;
226         }
227
228         if (usedp != NULL)
229                 *usedp = used;
230
231         return (0);
232
233 fail3:
234         EFSYS_PROBE(fail3);
235 fail2:
236         EFSYS_PROBE(fail2);
237 fail1:
238         EFSYS_PROBE1(fail1, efx_rc_t, rc);
239
240         return (rc);
241 }
242
243 /*
244  * Walk the entire tag set looking for option. The sought option may be
245  * encapsulated. ENOENT indicates the walk completed without finding the
246  * option. If we run out of buffer during the walk the function will return
247  * ENOSPC.
248  */
249 static  efx_rc_t
250 efx_dhcp_walk_tags(
251         __deref_inout   uint8_t **tagpp,
252         __inout         size_t *buffer_sizep,
253         __in            uint16_t opt)
254 {
255         efx_rc_t rc = 0;
256         boolean_t is_encap = B_FALSE;
257
258         if (DHCP_IS_ENCAP_OPT(opt)) {
259                 /*
260                  * Look for the encapsulator and, if found, limit ourselves
261                  * to its payload. If it's not found then the entire tag
262                  * cannot be found, so the encapsulated opt search is
263                  * skipped.
264                  */
265                 rc = efx_dhcp_walk_tags(tagpp, buffer_sizep,
266                     DHCP_ENCAPSULATOR(opt));
267                 if (rc == 0) {
268                         *buffer_sizep = ((efx_dhcp_tag_hdr_t *)*tagpp)->length;
269                         (*tagpp) += sizeof (efx_dhcp_tag_hdr_t);
270                 }
271                 opt = DHCP_ENCAPSULATED(opt);
272                 is_encap = B_TRUE;
273         }
274
275         EFSYS_ASSERT(!DHCP_IS_ENCAP_OPT(opt));
276
277         while (rc == 0) {
278                 size_t size;
279
280                 if (*buffer_sizep == 0) {
281                         rc = ENOSPC;
282                         goto fail1;
283                 }
284
285                 if (DHCP_ENCAPSULATED(**tagpp) == opt)
286                         break;
287
288                 if ((**tagpp) == EFX_DHCP_END) {
289                         rc = ENOENT;
290                         break;
291                 } else if ((**tagpp) == EFX_DHCP_PAD) {
292                         size = 1;
293                 } else {
294                         if (*buffer_sizep < sizeof (efx_dhcp_tag_hdr_t)) {
295                                 rc = ENOSPC;
296                                 goto fail2;
297                         }
298
299                         size =
300                             DHCP_FULL_TAG_LENGTH((efx_dhcp_tag_hdr_t *)*tagpp);
301                 }
302
303                 if (size > *buffer_sizep) {
304                         rc = ENOSPC;
305                         goto fail3;
306                 }
307
308                 (*tagpp) += size;
309                 (*buffer_sizep) -= size;
310
311                 if ((*buffer_sizep == 0) && is_encap) {
312                         /* Search within encapulator tag finished */
313                         rc = ENOENT;
314                         break;
315                 }
316         }
317
318         /*
319          * Returns 0 if found otherwise ENOENT indicating search finished
320          * correctly
321          */
322         return (rc);
323
324 fail3:
325         EFSYS_PROBE(fail3);
326 fail2:
327         EFSYS_PROBE(fail2);
328 fail1:
329         EFSYS_PROBE1(fail1, efx_rc_t, rc);
330
331         return (rc);
332 }
333
334 /*
335  * Locate value buffer for option in the given buffer.
336  * Returns 0 if found, ENOENT indicating search finished
337  * correctly, otherwise search failed before completion.
338  */
339         __checkReturn   efx_rc_t
340 efx_dhcp_find_tag(
341         __in_bcount(buffer_length)      uint8_t *bufferp,
342         __in                            size_t buffer_length,
343         __in                            uint16_t opt,
344         __deref_out                     uint8_t **valuepp,
345         __out                           size_t *value_lengthp)
346 {
347         efx_rc_t rc;
348         uint8_t *tagp = bufferp;
349         size_t len = buffer_length;
350
351         rc = efx_dhcp_walk_tags(&tagp, &len, opt);
352         if (rc == 0) {
353                 efx_dhcp_tag_hdr_t *hdrp;
354
355                 hdrp = (efx_dhcp_tag_hdr_t *)tagp;
356                 *valuepp = (uint8_t *)(&hdrp[1]);
357                 *value_lengthp = hdrp->length;
358         } else if (rc != ENOENT) {
359                 goto fail1;
360         }
361
362         return (rc);
363
364 fail1:
365         EFSYS_PROBE1(fail1, efx_rc_t, rc);
366
367         return (rc);
368 }
369
370 /*
371  * Locate the end tag in the given buffer.
372  * Returns 0 if found, ENOENT indicating search finished
373  * correctly but end tag was not found; otherwise search
374  * failed before completion.
375  */
376         __checkReturn   efx_rc_t
377 efx_dhcp_find_end(
378         __in_bcount(buffer_length)      uint8_t *bufferp,
379         __in                            size_t buffer_length,
380         __deref_out                     uint8_t **endpp)
381 {
382         efx_rc_t rc;
383         uint8_t *endp = bufferp;
384         size_t len = buffer_length;
385
386         rc = efx_dhcp_walk_tags(&endp, &len, EFX_DHCP_END);
387         if (rc == 0)
388                 *endpp = endp;
389         else if (rc != ENOENT)
390                 goto fail1;
391
392         return (rc);
393
394 fail1:
395         EFSYS_PROBE1(fail1, efx_rc_t, rc);
396
397         return (rc);
398 }
399
400 /*
401  * Delete the given tag from anywhere in the buffer. Copes with
402  * encapsulated tags, and updates or deletes the encapsulating opt as
403  * necessary.
404  */
405         __checkReturn   efx_rc_t
406 efx_dhcp_delete_tag(
407         __inout_bcount(buffer_length)   uint8_t *bufferp,
408         __in                            size_t buffer_length,
409         __in                            uint16_t opt)
410 {
411         efx_rc_t rc;
412         efx_dhcp_tag_hdr_t *hdrp;
413         size_t len;
414         uint8_t *startp;
415         uint8_t *endp;
416
417         len = buffer_length;
418         startp = bufferp;
419
420         if (!DHCP_OPT_HAS_VALUE(DHCP_ENCAPSULATED(opt))) {
421                 rc = EINVAL;
422                 goto fail1;
423         }
424
425         rc = efx_dhcp_walk_tags(&startp, &len, opt);
426         if (rc != 0)
427                 goto fail1;
428
429         hdrp = (efx_dhcp_tag_hdr_t *)startp;
430
431         if (DHCP_IS_ENCAP_OPT(opt)) {
432                 uint8_t tag_length = DHCP_FULL_TAG_LENGTH(hdrp);
433                 uint8_t *encapp = bufferp;
434                 efx_dhcp_tag_hdr_t *encap_hdrp;
435
436                 len = buffer_length;
437                 rc = efx_dhcp_walk_tags(&encapp, &len,
438                     DHCP_ENCAPSULATOR(opt));
439                 if (rc != 0)
440                         goto fail2;
441
442                 encap_hdrp = (efx_dhcp_tag_hdr_t *)encapp;
443                 if (encap_hdrp->length > tag_length) {
444                         encap_hdrp->length = (uint8_t)(
445                             (size_t)encap_hdrp->length - tag_length);
446                 } else {
447                         /* delete the encapsulating tag */
448                         hdrp = encap_hdrp;
449                 }
450         }
451
452         startp = (uint8_t *)hdrp;
453         endp = (uint8_t *)DHCP_NEXT_TAG(hdrp);
454
455         if (startp < bufferp) {
456                 rc = EINVAL;
457                 goto fail3;
458         }
459
460         if (endp > &bufferp[buffer_length]) {
461                 rc = EINVAL;
462                 goto fail4;
463         }
464
465         memmove(startp, endp,
466                 buffer_length - (endp - bufferp));
467
468         return (0);
469
470 fail4:
471         EFSYS_PROBE(fail4);
472 fail3:
473         EFSYS_PROBE(fail3);
474 fail2:
475         EFSYS_PROBE(fail2);
476 fail1:
477         EFSYS_PROBE1(fail1, efx_rc_t, rc);
478
479         return (rc);
480 }
481
482 /*
483  * Write the tag header into write_pointp and optionally copies the payload
484  * into the space following.
485  */
486 static  void
487 efx_dhcp_write_tag(
488         __in            uint8_t *write_pointp,
489         __in            uint16_t opt,
490         __in_bcount_opt(value_length)
491                         uint8_t *valuep,
492         __in            size_t value_length)
493 {
494         efx_dhcp_tag_hdr_t *hdrp = (efx_dhcp_tag_hdr_t *)write_pointp;
495         hdrp->tag = DHCP_ENCAPSULATED(opt);
496         hdrp->length = (uint8_t)value_length;
497         if ((value_length > 0) && (valuep != NULL))
498                 memcpy(&hdrp[1], valuep, value_length);
499 }
500
501 /*
502  * Add the given tag to the end of the buffer. Copes with creating an
503  * encapsulated tag, and updates or creates the encapsulating opt as
504  * necessary.
505  */
506         __checkReturn   efx_rc_t
507 efx_dhcp_add_tag(
508         __inout_bcount(buffer_length)   uint8_t *bufferp,
509         __in                            size_t buffer_length,
510         __in                            uint16_t opt,
511         __in_bcount_opt(value_length)   uint8_t *valuep,
512         __in                            size_t value_length)
513 {
514         efx_rc_t rc;
515         efx_dhcp_tag_hdr_t *encap_hdrp = NULL;
516         uint8_t *insert_pointp = NULL;
517         uint8_t *endp;
518         size_t available_space;
519         size_t added_length;
520         size_t search_size;
521         uint8_t *searchp;
522
523         if (!DHCP_OPT_HAS_VALUE(DHCP_ENCAPSULATED(opt))) {
524                 rc = EINVAL;
525                 goto fail1;
526         }
527
528         if (value_length > DHCP_MAX_VALUE) {
529                 rc = EINVAL;
530                 goto fail2;
531         }
532
533         if ((value_length > 0) && (valuep == NULL)) {
534                 rc = EINVAL;
535                 goto fail3;
536         }
537
538         endp = bufferp;
539         available_space = buffer_length;
540         rc = efx_dhcp_walk_tags(&endp, &available_space, EFX_DHCP_END);
541         if (rc != 0)
542                 goto fail4;
543
544         searchp = bufferp;
545         search_size = buffer_length;
546         if (DHCP_IS_ENCAP_OPT(opt)) {
547                 rc = efx_dhcp_walk_tags(&searchp, &search_size,
548                     DHCP_ENCAPSULATOR(opt));
549                 if (rc == 0) {
550                         encap_hdrp = (efx_dhcp_tag_hdr_t *)searchp;
551
552                         /* Check encapsulated tag is not present */
553                         search_size = encap_hdrp->length;
554                         rc = efx_dhcp_walk_tags(&searchp, &search_size,
555                             opt);
556                         if (rc != ENOENT) {
557                                 rc = EINVAL;
558                                 goto fail5;
559                         }
560
561                         /* Check encapsulator will not overflow */
562                         if (((size_t)encap_hdrp->length +
563                             DHCP_CALC_TAG_LENGTH(value_length)) >
564                             DHCP_MAX_VALUE) {
565                                 rc = E2BIG;
566                                 goto fail6;
567                         }
568
569                         /* Insert at start of existing encapsulator */
570                         insert_pointp = (uint8_t *)&encap_hdrp[1];
571                         opt = DHCP_ENCAPSULATED(opt);
572                 } else if (rc == ENOENT) {
573                         encap_hdrp = NULL;
574                 } else {
575                         goto fail7;
576                 }
577         } else {
578                 /* Check unencapsulated tag is not present */
579                 rc = efx_dhcp_walk_tags(&searchp, &search_size,
580                     opt);
581                 if (rc != ENOENT) {
582                         rc = EINVAL;
583                         goto fail8;
584                 }
585         }
586
587         if (insert_pointp == NULL) {
588                 /* Insert at end of existing tags */
589                 insert_pointp = endp;
590         }
591
592         /* Includes the new encapsulator tag hdr if required */
593         added_length = DHCP_CALC_TAG_LENGTH(value_length) +
594             (DHCP_IS_ENCAP_OPT(opt) ? sizeof (efx_dhcp_tag_hdr_t) : 0);
595
596         if (available_space <= added_length) {
597                 rc = ENOMEM;
598                 goto fail9;
599         }
600
601         memmove(insert_pointp + added_length, insert_pointp,
602             available_space - added_length);
603
604         if (DHCP_IS_ENCAP_OPT(opt)) {
605                 /* Create new encapsulator header */
606                 added_length -= sizeof (efx_dhcp_tag_hdr_t);
607                 efx_dhcp_write_tag(insert_pointp,
608                     DHCP_ENCAPSULATOR(opt), NULL, added_length);
609                 insert_pointp += sizeof (efx_dhcp_tag_hdr_t);
610         } else if (encap_hdrp)
611                 /* Modify existing encapsulator header */
612                 encap_hdrp->length +=
613                     ((uint8_t)DHCP_CALC_TAG_LENGTH(value_length));
614
615         efx_dhcp_write_tag(insert_pointp, opt, valuep, value_length);
616
617         return (0);
618
619 fail9:
620         EFSYS_PROBE(fail9);
621 fail8:
622         EFSYS_PROBE(fail8);
623 fail7:
624         EFSYS_PROBE(fail7);
625 fail6:
626         EFSYS_PROBE(fail6);
627 fail5:
628         EFSYS_PROBE(fail5);
629 fail4:
630         EFSYS_PROBE(fail4);
631 fail3:
632         EFSYS_PROBE(fail3);
633 fail2:
634         EFSYS_PROBE(fail2);
635 fail1:
636         EFSYS_PROBE1(fail1, efx_rc_t, rc);
637
638         return (rc);
639 }
640
641 /*
642  * Update an existing tag to the new value. Copes with encapsulated
643  * tags, and updates the encapsulating opt as necessary.
644  */
645         __checkReturn   efx_rc_t
646 efx_dhcp_update_tag(
647         __inout_bcount(buffer_length)   uint8_t *bufferp,
648         __in                            size_t buffer_length,
649         __in                            uint16_t opt,
650         __in                            uint8_t *value_locationp,
651         __in_bcount_opt(value_length)   uint8_t *valuep,
652         __in                            size_t value_length)
653 {
654         efx_rc_t rc;
655         uint8_t *write_pointp = value_locationp - sizeof (efx_dhcp_tag_hdr_t);
656         efx_dhcp_tag_hdr_t *hdrp = (efx_dhcp_tag_hdr_t *)write_pointp;
657         efx_dhcp_tag_hdr_t *encap_hdrp = NULL;
658         size_t old_length;
659
660         if (!DHCP_OPT_HAS_VALUE(DHCP_ENCAPSULATED(opt))) {
661                 rc = EINVAL;
662                 goto fail1;
663         }
664
665         if (value_length > DHCP_MAX_VALUE) {
666                 rc = EINVAL;
667                 goto fail2;
668         }
669
670         if ((value_length > 0) && (valuep == NULL)) {
671                 rc = EINVAL;
672                 goto fail3;
673         }
674
675         old_length = hdrp->length;
676
677         if (old_length < value_length) {
678                 uint8_t *endp = bufferp;
679                 size_t available_space = buffer_length;
680
681                 rc = efx_dhcp_walk_tags(&endp, &available_space,
682                     EFX_DHCP_END);
683                 if (rc != 0)
684                         goto fail4;
685
686                 if (available_space < (value_length - old_length)) {
687                         rc = EINVAL;
688                         goto fail5;
689                 }
690         }
691
692         if (DHCP_IS_ENCAP_OPT(opt)) {
693                 uint8_t *encapp = bufferp;
694                 size_t following_encap = buffer_length;
695                 size_t new_length;
696
697                 rc = efx_dhcp_walk_tags(&encapp, &following_encap,
698                     DHCP_ENCAPSULATOR(opt));
699                 if (rc != 0)
700                         goto fail6;
701
702                 encap_hdrp = (efx_dhcp_tag_hdr_t *)encapp;
703
704                 new_length = ((size_t)encap_hdrp->length +
705                     value_length - old_length);
706                 /* Check encapsulator will not overflow */
707                 if (new_length > DHCP_MAX_VALUE) {
708                         rc = E2BIG;
709                         goto fail7;
710                 }
711
712                 encap_hdrp->length = (uint8_t)new_length;
713         }
714
715         /*
716          * Move the following data up/down to accommodate the new payload
717          * length.
718          */
719         if (old_length != value_length) {
720                 uint8_t *destp = (uint8_t *)DHCP_NEXT_TAG(hdrp) +
721                     value_length - old_length;
722                 size_t count = &bufferp[buffer_length] -
723                     (uint8_t *)DHCP_NEXT_TAG(hdrp);
724
725                 memmove(destp, DHCP_NEXT_TAG(hdrp), count);
726         }
727
728         EFSYS_ASSERT(hdrp->tag == DHCP_ENCAPSULATED(opt));
729         efx_dhcp_write_tag(write_pointp, opt, valuep, value_length);
730
731         return (0);
732
733 fail7:
734         EFSYS_PROBE(fail7);
735 fail6:
736         EFSYS_PROBE(fail6);
737 fail5:
738         EFSYS_PROBE(fail5);
739 fail4:
740         EFSYS_PROBE(fail4);
741 fail3:
742         EFSYS_PROBE(fail3);
743 fail2:
744         EFSYS_PROBE(fail2);
745 fail1:
746         EFSYS_PROBE1(fail1, efx_rc_t, rc);
747
748         return (rc);
749 }
750
751 /*
752  * Copy bootcfg sector data to a target buffer which may differ in size.
753  * Optionally corrects format errors in source buffer.
754  */
755                                 efx_rc_t
756 efx_bootcfg_copy_sector(
757         __in                    efx_nic_t *enp,
758         __inout_bcount(sector_length)
759                                 uint8_t *sector,
760         __in                    size_t sector_length,
761         __out_bcount(data_size) uint8_t *data,
762         __in                    size_t data_size,
763         __in                    boolean_t handle_format_errors)
764 {
765         _NOTE(ARGUNUSED(enp))
766
767         size_t used_bytes;
768         efx_rc_t rc;
769
770         /* Minimum buffer is checksum byte and EFX_DHCP_END terminator */
771         if (data_size < 2) {
772                 rc = ENOSPC;
773                 goto fail1;
774         }
775
776         /* Verify that the area is correctly formatted and checksummed */
777         rc = efx_dhcp_verify(sector, sector_length,
778                                     &used_bytes);
779
780         if (!handle_format_errors) {
781                 if (rc != 0)
782                         goto fail2;
783
784                 if ((used_bytes < 2) ||
785                     (sector[used_bytes - 1] != EFX_DHCP_END)) {
786                         /* Block too short, or EFX_DHCP_END missing */
787                         rc = ENOENT;
788                         goto fail3;
789                 }
790         }
791
792         /* Synthesize empty format on verification failure */
793         if (rc != 0 || used_bytes == 0) {
794                 sector[0] = 0;
795                 sector[1] = EFX_DHCP_END;
796                 used_bytes = 2;
797         }
798         EFSYS_ASSERT(used_bytes >= 2);  /* checksum and EFX_DHCP_END */
799         EFSYS_ASSERT(used_bytes <= sector_length);
800         EFSYS_ASSERT(sector_length >= 2);
801
802         /*
803          * Legacy bootcfg sectors don't terminate with an EFX_DHCP_END
804          * character. Modify the returned payload so it does.
805          * Reinitialise the sector if there isn't room for the character.
806          */
807         if (sector[used_bytes - 1] != EFX_DHCP_END) {
808                 if (used_bytes >= sector_length) {
809                         sector[0] = 0;
810                         used_bytes = 1;
811                 }
812                 sector[used_bytes] = EFX_DHCP_END;
813                 ++used_bytes;
814         }
815
816         /*
817          * Verify that the target buffer is large enough for the
818          * entire used bootcfg area, then copy into the target buffer.
819          */
820         if (used_bytes > data_size) {
821                 rc = ENOSPC;
822                 goto fail4;
823         }
824
825         data[0] = 0; /* checksum, updated below */
826
827         /* Copy all after the checksum to the target buffer */
828         memcpy(data + 1, sector + 1, used_bytes - 1);
829
830         /* Zero out the unused portion of the target buffer */
831         if (used_bytes < data_size)
832                 (void) memset(data + used_bytes, 0, data_size - used_bytes);
833
834         /*
835          * The checksum includes trailing data after any EFX_DHCP_END
836          * character, which we've just modified (by truncation or appending
837          * EFX_DHCP_END).
838          */
839         data[0] -= efx_dhcp_csum(data, data_size);
840
841         return (0);
842
843 fail4:
844         EFSYS_PROBE(fail4);
845 fail3:
846         EFSYS_PROBE(fail3);
847 fail2:
848         EFSYS_PROBE(fail2);
849 fail1:
850         EFSYS_PROBE1(fail1, efx_rc_t, rc);
851
852         return (rc);
853 }
854
855                                 efx_rc_t
856 efx_bootcfg_read(
857         __in                    efx_nic_t *enp,
858         __out_bcount(size)      uint8_t *data,
859         __in                    size_t size)
860 {
861         uint8_t *payload = NULL;
862         size_t used_bytes;
863         size_t partn_length;
864         size_t sector_length;
865         size_t sector_offset;
866         efx_rc_t rc;
867         uint32_t sector_number;
868
869         /* Minimum buffer is checksum byte and EFX_DHCP_END terminator */
870         if (size < 2) {
871                 rc = ENOSPC;
872                 goto fail1;
873         }
874
875 #if EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD || EFSYS_OPT_MEDFORD2
876         sector_number = enp->en_nic_cfg.enc_pf;
877 #else
878         sector_number = 0;
879 #endif
880         rc = efx_nvram_size(enp, EFX_NVRAM_BOOTROM_CFG, &partn_length);
881         if (rc != 0)
882                 goto fail2;
883
884         /* The bootcfg sector may be stored in a (larger) shared partition */
885         rc = efx_bootcfg_sector_info(enp, sector_number,
886             NULL, &sector_offset, &sector_length);
887         if (rc != 0)
888                 goto fail3;
889
890         if (sector_length < 2) {
891                 rc = EINVAL;
892                 goto fail4;
893         }
894
895         if (sector_length > BOOTCFG_MAX_SIZE)
896                 sector_length = BOOTCFG_MAX_SIZE;
897
898         if (sector_offset + sector_length > partn_length) {
899                 /* Partition is too small */
900                 rc = EFBIG;
901                 goto fail5;
902         }
903
904         /*
905          * We need to read the entire BOOTCFG sector to ensure we read all
906          * tags, because legacy bootcfg sectors are not guaranteed to end
907          * with an EFX_DHCP_END character. If the user hasn't supplied a
908          * sufficiently large buffer then use our own buffer.
909          */
910         if (sector_length > size) {
911                 EFSYS_KMEM_ALLOC(enp->en_esip, sector_length, payload);
912                 if (payload == NULL) {
913                         rc = ENOMEM;
914                         goto fail6;
915                 }
916         } else
917                 payload = (uint8_t *)data;
918
919         if ((rc = efx_nvram_rw_start(enp, EFX_NVRAM_BOOTROM_CFG, NULL)) != 0)
920                 goto fail7;
921
922         if ((rc = efx_nvram_read_chunk(enp, EFX_NVRAM_BOOTROM_CFG,
923             sector_offset, (caddr_t)payload, sector_length)) != 0) {
924                 (void) efx_nvram_rw_finish(enp, EFX_NVRAM_BOOTROM_CFG, NULL);
925                 goto fail8;
926         }
927
928         if ((rc = efx_nvram_rw_finish(enp, EFX_NVRAM_BOOTROM_CFG, NULL)) != 0)
929                 goto fail9;
930
931         /* Verify that the area is correctly formatted and checksummed */
932         rc = efx_dhcp_verify(payload, sector_length,
933             &used_bytes);
934         if (rc != 0 || used_bytes == 0) {
935                 payload[0] = 0;
936                 payload[1] = EFX_DHCP_END;
937                 used_bytes = 2;
938         }
939
940         EFSYS_ASSERT(used_bytes >= 2);  /* checksum and EFX_DHCP_END */
941         EFSYS_ASSERT(used_bytes <= sector_length);
942
943         /*
944          * Legacy bootcfg sectors don't terminate with an EFX_DHCP_END
945          * character. Modify the returned payload so it does.
946          * BOOTCFG_MAX_SIZE is by definition large enough for any valid
947          * (per-port) bootcfg sector, so reinitialise the sector if there
948          * isn't room for the character.
949          */
950         if (payload[used_bytes - 1] != EFX_DHCP_END) {
951                 if (used_bytes >= sector_length)
952                         used_bytes = 1;
953
954                 payload[used_bytes] = EFX_DHCP_END;
955                 ++used_bytes;
956         }
957
958         /*
959          * Verify that the user supplied buffer is large enough for the
960          * entire used bootcfg area, then copy into the user supplied buffer.
961          */
962         if (used_bytes > size) {
963                 rc = ENOSPC;
964                 goto fail10;
965         }
966
967         data[0] = 0; /* checksum, updated below */
968
969         if (sector_length > size) {
970                 /* Copy all after the checksum to the target buffer */
971                 memcpy(data + 1, payload + 1, used_bytes - 1);
972                 EFSYS_KMEM_FREE(enp->en_esip, sector_length, payload);
973         }
974
975         /* Zero out the unused portion of the user buffer */
976         if (used_bytes < size)
977                 (void) memset(data + used_bytes, 0, size - used_bytes);
978
979         /*
980          * The checksum includes trailing data after any EFX_DHCP_END character,
981          * which we've just modified (by truncation or appending EFX_DHCP_END).
982          */
983         data[0] -= efx_dhcp_csum(data, size);
984
985         return (0);
986
987 fail10:
988         EFSYS_PROBE(fail10);
989 fail9:
990         EFSYS_PROBE(fail9);
991 fail8:
992         EFSYS_PROBE(fail8);
993 fail7:
994         EFSYS_PROBE(fail7);
995         if (sector_length > size)
996                 EFSYS_KMEM_FREE(enp->en_esip, sector_length, payload);
997 fail6:
998         EFSYS_PROBE(fail6);
999 fail5:
1000         EFSYS_PROBE(fail5);
1001 fail4:
1002         EFSYS_PROBE(fail4);
1003 fail3:
1004         EFSYS_PROBE(fail3);
1005 fail2:
1006         EFSYS_PROBE(fail2);
1007 fail1:
1008         EFSYS_PROBE1(fail1, efx_rc_t, rc);
1009
1010         return (rc);
1011 }
1012
1013                                 efx_rc_t
1014 efx_bootcfg_write(
1015         __in                    efx_nic_t *enp,
1016         __in_bcount(size)       uint8_t *data,
1017         __in                    size_t size)
1018 {
1019         uint8_t *partn_data;
1020         uint8_t checksum;
1021         size_t partn_length;
1022         size_t sector_length;
1023         size_t sector_offset;
1024         size_t used_bytes;
1025         efx_rc_t rc;
1026         uint32_t sector_number;
1027
1028 #if EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD || EFSYS_OPT_MEDFORD2
1029         sector_number = enp->en_nic_cfg.enc_pf;
1030 #else
1031         sector_number = 0;
1032 #endif
1033
1034         rc = efx_nvram_size(enp, EFX_NVRAM_BOOTROM_CFG, &partn_length);
1035         if (rc != 0)
1036                 goto fail1;
1037
1038         /* The bootcfg sector may be stored in a (larger) shared partition */
1039         rc = efx_bootcfg_sector_info(enp, sector_number,
1040             NULL, &sector_offset, &sector_length);
1041         if (rc != 0)
1042                 goto fail2;
1043
1044         if (sector_length > BOOTCFG_MAX_SIZE)
1045                 sector_length = BOOTCFG_MAX_SIZE;
1046
1047         if (sector_offset + sector_length > partn_length) {
1048                 /* Partition is too small */
1049                 rc = EFBIG;
1050                 goto fail3;
1051         }
1052
1053         if ((rc = efx_dhcp_verify(data, size, &used_bytes)) != 0)
1054                 goto fail4;
1055
1056         /*
1057          * The caller *must* terminate their block with a EFX_DHCP_END
1058          * character
1059          */
1060         if ((used_bytes < 2) || ((uint8_t)data[used_bytes - 1] !=
1061             EFX_DHCP_END)) {
1062                 /* Block too short or EFX_DHCP_END missing */
1063                 rc = ENOENT;
1064                 goto fail5;
1065         }
1066
1067         /* Check that the hardware has support for this much data */
1068         if (used_bytes > MIN(sector_length, BOOTCFG_MAX_SIZE)) {
1069                 rc = ENOSPC;
1070                 goto fail6;
1071         }
1072
1073         /*
1074          * If the BOOTCFG sector is stored in a shared partition, then we must
1075          * read the whole partition and insert the updated bootcfg sector at the
1076          * correct offset.
1077          */
1078         EFSYS_KMEM_ALLOC(enp->en_esip, partn_length, partn_data);
1079         if (partn_data == NULL) {
1080                 rc = ENOMEM;
1081                 goto fail7;
1082         }
1083
1084         rc = efx_nvram_rw_start(enp, EFX_NVRAM_BOOTROM_CFG, NULL);
1085         if (rc != 0)
1086                 goto fail8;
1087
1088         /* Read the entire partition */
1089         rc = efx_nvram_read_chunk(enp, EFX_NVRAM_BOOTROM_CFG, 0,
1090                                     (caddr_t)partn_data, partn_length);
1091         if (rc != 0)
1092                 goto fail9;
1093
1094         /*
1095          * Insert the BOOTCFG sector into the partition, Zero out all data
1096          * after the EFX_DHCP_END tag, and adjust the checksum.
1097          */
1098         (void) memset(partn_data + sector_offset, 0x0, sector_length);
1099         (void) memcpy(partn_data + sector_offset, data, used_bytes);
1100
1101         checksum = efx_dhcp_csum(data, used_bytes);
1102         partn_data[sector_offset] -= checksum;
1103
1104         if ((rc = efx_nvram_erase(enp, EFX_NVRAM_BOOTROM_CFG)) != 0)
1105                 goto fail10;
1106
1107         if ((rc = efx_nvram_write_chunk(enp, EFX_NVRAM_BOOTROM_CFG,
1108                     0, (caddr_t)partn_data, partn_length)) != 0)
1109                 goto fail11;
1110
1111         if ((rc = efx_nvram_rw_finish(enp, EFX_NVRAM_BOOTROM_CFG, NULL)) != 0)
1112                 goto fail12;
1113
1114         EFSYS_KMEM_FREE(enp->en_esip, partn_length, partn_data);
1115
1116         return (0);
1117
1118 fail12:
1119         EFSYS_PROBE(fail12);
1120 fail11:
1121         EFSYS_PROBE(fail11);
1122 fail10:
1123         EFSYS_PROBE(fail10);
1124 fail9:
1125         EFSYS_PROBE(fail9);
1126
1127         (void) efx_nvram_rw_finish(enp, EFX_NVRAM_BOOTROM_CFG, NULL);
1128 fail8:
1129         EFSYS_PROBE(fail8);
1130
1131         EFSYS_KMEM_FREE(enp->en_esip, partn_length, partn_data);
1132 fail7:
1133         EFSYS_PROBE(fail7);
1134 fail6:
1135         EFSYS_PROBE(fail6);
1136 fail5:
1137         EFSYS_PROBE(fail5);
1138 fail4:
1139         EFSYS_PROBE(fail4);
1140 fail3:
1141         EFSYS_PROBE(fail3);
1142 fail2:
1143         EFSYS_PROBE(fail2);
1144 fail1:
1145         EFSYS_PROBE1(fail1, efx_rc_t, rc);
1146
1147         return (rc);
1148 }
1149
1150 #endif  /* EFSYS_OPT_BOOTCFG */