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