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