]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/sfxge/common/efx_bootcfg.c
sfxge(4): detect equal stride super-buffer support
[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_END ((uint8_t)0xff)
52 #define DHCP_PAD ((uint8_t)0)
53
54
55 /* Report the layout of bootcfg sectors in NVRAM partition. */
56         __checkReturn           efx_rc_t
57 efx_bootcfg_sector_info(
58         __in                    efx_nic_t *enp,
59         __in                    uint32_t pf,
60         __out_opt               uint32_t *sector_countp,
61         __out                   size_t *offsetp,
62         __out                   size_t *max_sizep)
63 {
64         uint32_t count;
65         size_t max_size;
66         size_t offset;
67         int rc;
68
69         switch (enp->en_family) {
70 #if EFSYS_OPT_SIENA
71         case EFX_FAMILY_SIENA:
72                 max_size = BOOTCFG_MAX_SIZE;
73                 offset = 0;
74                 count = 1;
75                 break;
76 #endif /* EFSYS_OPT_SIENA */
77
78 #if EFSYS_OPT_HUNTINGTON
79         case EFX_FAMILY_HUNTINGTON:
80                 max_size = BOOTCFG_MAX_SIZE;
81                 offset = 0;
82                 count = 1;
83                 break;
84 #endif /* EFSYS_OPT_HUNTINGTON */
85
86 #if EFSYS_OPT_MEDFORD
87         case EFX_FAMILY_MEDFORD: {
88                 /* Shared partition (array indexed by PF) */
89                 max_size = BOOTCFG_PER_PF;
90                 count = BOOTCFG_PF_COUNT;
91                 if (pf >= count) {
92                         rc = EINVAL;
93                         goto fail2;
94                 }
95                 offset = max_size * pf;
96                 break;
97         }
98 #endif /* EFSYS_OPT_MEDFORD */
99
100 #if EFSYS_OPT_MEDFORD2
101         case EFX_FAMILY_MEDFORD2: {
102                 /* Shared partition (array indexed by PF) */
103                 max_size = BOOTCFG_PER_PF;
104                 count = BOOTCFG_PF_COUNT;
105                 if (pf >= count) {
106                         rc = EINVAL;
107                         goto fail3;
108                 }
109                 offset = max_size * pf;
110                 break;
111         }
112 #endif /* EFSYS_OPT_MEDFORD2 */
113
114         default:
115                 EFSYS_ASSERT(0);
116                 rc = ENOTSUP;
117                 goto fail1;
118         }
119         EFSYS_ASSERT3U(max_size, <=, BOOTCFG_MAX_SIZE);
120
121         if (sector_countp != NULL)
122                 *sector_countp = count;
123         *offsetp = offset;
124         *max_sizep = max_size;
125
126         return (0);
127
128 #if EFSYS_OPT_MEDFORD2
129 fail3:
130         EFSYS_PROBE(fail3);
131 #endif
132 #if EFSYS_OPT_MEDFORD
133 fail2:
134         EFSYS_PROBE(fail2);
135 #endif
136 fail1:
137         EFSYS_PROBE1(fail1, efx_rc_t, rc);
138         return (rc);
139 }
140
141
142 static  __checkReturn           uint8_t
143 efx_bootcfg_csum(
144         __in                    efx_nic_t *enp,
145         __in_bcount(size)       uint8_t const *data,
146         __in                    size_t size)
147 {
148         _NOTE(ARGUNUSED(enp))
149
150         unsigned int pos;
151         uint8_t checksum = 0;
152
153         for (pos = 0; pos < size; pos++)
154                 checksum += data[pos];
155         return (checksum);
156 }
157
158 static  __checkReturn           efx_rc_t
159 efx_bootcfg_verify(
160         __in                    efx_nic_t *enp,
161         __in_bcount(size)       uint8_t const *data,
162         __in                    size_t size,
163         __out_opt               size_t *usedp)
164 {
165         size_t offset = 0;
166         size_t used = 0;
167         efx_rc_t rc;
168
169         /* Start parsing tags immediately after the checksum */
170         for (offset = 1; offset < size; ) {
171                 uint8_t tag;
172                 uint8_t length;
173
174                 /* Consume tag */
175                 tag = data[offset];
176                 if (tag == DHCP_END) {
177                         offset++;
178                         used = offset;
179                         break;
180                 }
181                 if (tag == DHCP_PAD) {
182                         offset++;
183                         continue;
184                 }
185
186                 /* Consume length */
187                 if (offset + 1 >= size) {
188                         rc = ENOSPC;
189                         goto fail1;
190                 }
191                 length = data[offset + 1];
192
193                 /* Consume *length */
194                 if (offset + 1 + length >= size) {
195                         rc = ENOSPC;
196                         goto fail2;
197                 }
198
199                 offset += 2 + length;
200                 used = offset;
201         }
202
203         /* Checksum the entire sector, including bytes after any DHCP_END */
204         if (efx_bootcfg_csum(enp, data, size) != 0) {
205                 rc = EINVAL;
206                 goto fail3;
207         }
208
209         if (usedp != NULL)
210                 *usedp = used;
211
212         return (0);
213
214 fail3:
215         EFSYS_PROBE(fail3);
216 fail2:
217         EFSYS_PROBE(fail2);
218 fail1:
219         EFSYS_PROBE1(fail1, efx_rc_t, rc);
220
221         return (rc);
222 }
223
224 /*
225  * Copy bootcfg sector data to a target buffer which may differ in size.
226  * Optionally corrects format errors in source buffer.
227  */
228                                 efx_rc_t
229 efx_bootcfg_copy_sector(
230         __in                    efx_nic_t *enp,
231         __inout_bcount(sector_length)
232                                 uint8_t *sector,
233         __in                    size_t sector_length,
234         __out_bcount(data_size) uint8_t *data,
235         __in                    size_t data_size,
236         __in                    boolean_t handle_format_errors)
237 {
238         size_t used_bytes;
239         efx_rc_t rc;
240
241         /* Minimum buffer is checksum byte and DHCP_END terminator */
242         if (data_size < 2) {
243                 rc = ENOSPC;
244                 goto fail1;
245         }
246
247         /* Verify that the area is correctly formatted and checksummed */
248         rc = efx_bootcfg_verify(enp, sector, sector_length,
249                                     &used_bytes);
250
251         if (!handle_format_errors) {
252                 if (rc != 0)
253                         goto fail2;
254
255                 if ((used_bytes < 2) ||
256                     (sector[used_bytes - 1] != DHCP_END)) {
257                         /* Block too short, or DHCP_END missing */
258                         rc = ENOENT;
259                         goto fail3;
260                 }
261         }
262
263         /* Synthesize empty format on verification failure */
264         if (rc != 0 || used_bytes == 0) {
265                 sector[0] = 0;
266                 sector[1] = DHCP_END;
267                 used_bytes = 2;
268         }
269         EFSYS_ASSERT(used_bytes >= 2);  /* checksum and DHCP_END */
270         EFSYS_ASSERT(used_bytes <= sector_length);
271         EFSYS_ASSERT(sector_length >= 2);
272
273         /*
274          * Legacy bootcfg sectors don't terminate with a DHCP_END character.
275          * Modify the returned payload so it does.
276          * Reinitialise the sector if there isn't room for the character.
277          */
278         if (sector[used_bytes - 1] != DHCP_END) {
279                 if (used_bytes >= sector_length) {
280                         sector[0] = 0;
281                         used_bytes = 1;
282                 }
283                 sector[used_bytes] = DHCP_END;
284                 ++used_bytes;
285         }
286
287         /*
288          * Verify that the target buffer is large enough for the
289          * entire used bootcfg area, then copy into the target buffer.
290          */
291         if (used_bytes > data_size) {
292                 rc = ENOSPC;
293                 goto fail4;
294         }
295
296         data[0] = 0; /* checksum, updated below */
297
298         /* Copy all after the checksum to the target buffer */
299         memcpy(data + 1, sector + 1, used_bytes - 1);
300
301         /* Zero out the unused portion of the target buffer */
302         if (used_bytes < data_size)
303                 (void) memset(data + used_bytes, 0, data_size - used_bytes);
304
305         /*
306          * The checksum includes trailing data after any DHCP_END character,
307          * which we've just modified (by truncation or appending DHCP_END).
308          */
309         data[0] -= efx_bootcfg_csum(enp, data, data_size);
310
311         return (0);
312
313 fail4:
314         EFSYS_PROBE(fail4);
315 fail3:
316         EFSYS_PROBE(fail3);
317 fail2:
318         EFSYS_PROBE(fail2);
319 fail1:
320         EFSYS_PROBE1(fail1, efx_rc_t, rc);
321
322         return (rc);
323 }
324
325                                 efx_rc_t
326 efx_bootcfg_read(
327         __in                    efx_nic_t *enp,
328         __out_bcount(size)      uint8_t *data,
329         __in                    size_t size)
330 {
331         uint8_t *payload = NULL;
332         size_t used_bytes;
333         size_t partn_length;
334         size_t sector_length;
335         size_t sector_offset;
336         efx_rc_t rc;
337         uint32_t sector_number;
338
339         /* Minimum buffer is checksum byte and DHCP_END terminator */
340         if (size < 2) {
341                 rc = ENOSPC;
342                 goto fail1;
343         }
344
345 #if EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD || EFSYS_OPT_MEDFORD2
346         sector_number = enp->en_nic_cfg.enc_pf;
347 #else
348         sector_number = 0;
349 #endif
350         rc = efx_nvram_size(enp, EFX_NVRAM_BOOTROM_CFG, &partn_length);
351         if (rc != 0)
352                 goto fail2;
353
354         /* The bootcfg sector may be stored in a (larger) shared partition */
355         rc = efx_bootcfg_sector_info(enp, sector_number,
356             NULL, &sector_offset, &sector_length);
357         if (rc != 0)
358                 goto fail3;
359
360         if (sector_length < 2) {
361                 rc = EINVAL;
362                 goto fail4;
363         }
364
365         if (sector_length > BOOTCFG_MAX_SIZE)
366                 sector_length = BOOTCFG_MAX_SIZE;
367
368         if (sector_offset + sector_length > partn_length) {
369                 /* Partition is too small */
370                 rc = EFBIG;
371                 goto fail5;
372         }
373
374         /*
375          * We need to read the entire BOOTCFG sector to ensure we read all the
376          * tags, because legacy bootcfg sectors are not guaranteed to end with
377          * a DHCP_END character. If the user hasn't supplied a sufficiently
378          * large buffer then use our own buffer.
379          */
380         if (sector_length > size) {
381                 EFSYS_KMEM_ALLOC(enp->en_esip, sector_length, payload);
382                 if (payload == NULL) {
383                         rc = ENOMEM;
384                         goto fail6;
385                 }
386         } else
387                 payload = (uint8_t *)data;
388
389         if ((rc = efx_nvram_rw_start(enp, EFX_NVRAM_BOOTROM_CFG, NULL)) != 0)
390                 goto fail7;
391
392         if ((rc = efx_nvram_read_chunk(enp, EFX_NVRAM_BOOTROM_CFG,
393             sector_offset, (caddr_t)payload, sector_length)) != 0) {
394                 (void) efx_nvram_rw_finish(enp, EFX_NVRAM_BOOTROM_CFG, NULL);
395                 goto fail8;
396         }
397
398         if ((rc = efx_nvram_rw_finish(enp, EFX_NVRAM_BOOTROM_CFG, NULL)) != 0)
399                 goto fail9;
400
401         /* Verify that the area is correctly formatted and checksummed */
402         rc = efx_bootcfg_verify(enp, payload, sector_length,
403             &used_bytes);
404         if (rc != 0 || used_bytes == 0) {
405                 payload[0] = 0;
406                 payload[1] = DHCP_END;
407                 used_bytes = 2;
408         }
409
410         EFSYS_ASSERT(used_bytes >= 2);  /* checksum and DHCP_END */
411         EFSYS_ASSERT(used_bytes <= sector_length);
412
413         /*
414          * Legacy bootcfg sectors don't terminate with a DHCP_END character.
415          * Modify the returned payload so it does. BOOTCFG_MAX_SIZE is by
416          * definition large enough for any valid (per-port) bootcfg sector,
417          * so reinitialise the sector if there isn't room for the character.
418          */
419         if (payload[used_bytes - 1] != DHCP_END) {
420                 if (used_bytes >= sector_length)
421                         used_bytes = 1;
422
423                 payload[used_bytes] = DHCP_END;
424                 ++used_bytes;
425         }
426
427         /*
428          * Verify that the user supplied buffer is large enough for the
429          * entire used bootcfg area, then copy into the user supplied buffer.
430          */
431         if (used_bytes > size) {
432                 rc = ENOSPC;
433                 goto fail10;
434         }
435
436         data[0] = 0; /* checksum, updated below */
437
438         if (sector_length > size) {
439                 /* Copy all after the checksum to the target buffer */
440                 memcpy(data + 1, payload + 1, used_bytes - 1);
441                 EFSYS_KMEM_FREE(enp->en_esip, sector_length, payload);
442         }
443
444         /* Zero out the unused portion of the user buffer */
445         if (used_bytes < size)
446                 (void) memset(data + used_bytes, 0, size - used_bytes);
447
448         /*
449          * The checksum includes trailing data after any DHCP_END character,
450          * which we've just modified (by truncation or appending DHCP_END).
451          */
452         data[0] -= efx_bootcfg_csum(enp, data, size);
453
454         return (0);
455
456 fail10:
457         EFSYS_PROBE(fail10);
458 fail9:
459         EFSYS_PROBE(fail9);
460 fail8:
461         EFSYS_PROBE(fail8);
462 fail7:
463         EFSYS_PROBE(fail7);
464         if (sector_length > size)
465                 EFSYS_KMEM_FREE(enp->en_esip, sector_length, payload);
466 fail6:
467         EFSYS_PROBE(fail6);
468 fail5:
469         EFSYS_PROBE(fail5);
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                                 efx_rc_t
483 efx_bootcfg_write(
484         __in                    efx_nic_t *enp,
485         __in_bcount(size)       uint8_t *data,
486         __in                    size_t size)
487 {
488         uint8_t *partn_data;
489         uint8_t checksum;
490         size_t partn_length;
491         size_t sector_length;
492         size_t sector_offset;
493         size_t used_bytes;
494         efx_rc_t rc;
495         uint32_t sector_number;
496
497 #if EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD || EFSYS_OPT_MEDFORD2
498         sector_number = enp->en_nic_cfg.enc_pf;
499 #else
500         sector_number = 0;
501 #endif
502
503         rc = efx_nvram_size(enp, EFX_NVRAM_BOOTROM_CFG, &partn_length);
504         if (rc != 0)
505                 goto fail1;
506
507         /* The bootcfg sector may be stored in a (larger) shared partition */
508         rc = efx_bootcfg_sector_info(enp, sector_number,
509             NULL, &sector_offset, &sector_length);
510         if (rc != 0)
511                 goto fail2;
512
513         if (sector_length > BOOTCFG_MAX_SIZE)
514                 sector_length = BOOTCFG_MAX_SIZE;
515
516         if (sector_offset + sector_length > partn_length) {
517                 /* Partition is too small */
518                 rc = EFBIG;
519                 goto fail3;
520         }
521
522         if ((rc = efx_bootcfg_verify(enp, data, size, &used_bytes)) != 0)
523                 goto fail4;
524
525         /* The caller *must* terminate their block with a DHCP_END character */
526         if ((used_bytes < 2) || ((uint8_t)data[used_bytes - 1] != DHCP_END)) {
527                 /* Block too short or DHCP_END missing */
528                 rc = ENOENT;
529                 goto fail5;
530         }
531
532         /* Check that the hardware has support for this much data */
533         if (used_bytes > MIN(sector_length, BOOTCFG_MAX_SIZE)) {
534                 rc = ENOSPC;
535                 goto fail6;
536         }
537
538         /*
539          * If the BOOTCFG sector is stored in a shared partition, then we must
540          * read the whole partition and insert the updated bootcfg sector at the
541          * correct offset.
542          */
543         EFSYS_KMEM_ALLOC(enp->en_esip, partn_length, partn_data);
544         if (partn_data == NULL) {
545                 rc = ENOMEM;
546                 goto fail7;
547         }
548
549         rc = efx_nvram_rw_start(enp, EFX_NVRAM_BOOTROM_CFG, NULL);
550         if (rc != 0)
551                 goto fail8;
552
553         /* Read the entire partition */
554         rc = efx_nvram_read_chunk(enp, EFX_NVRAM_BOOTROM_CFG, 0,
555                                     (caddr_t)partn_data, partn_length);
556         if (rc != 0)
557                 goto fail9;
558
559         /*
560          * Insert the BOOTCFG sector into the partition, Zero out all data after
561          * the DHCP_END tag, and adjust the checksum.
562          */
563         (void) memset(partn_data + sector_offset, 0x0, sector_length);
564         (void) memcpy(partn_data + sector_offset, data, used_bytes);
565
566         checksum = efx_bootcfg_csum(enp, data, used_bytes);
567         partn_data[sector_offset] -= checksum;
568
569         if ((rc = efx_nvram_erase(enp, EFX_NVRAM_BOOTROM_CFG)) != 0)
570                 goto fail10;
571
572         if ((rc = efx_nvram_write_chunk(enp, EFX_NVRAM_BOOTROM_CFG,
573                     0, (caddr_t)partn_data, partn_length)) != 0)
574                 goto fail11;
575
576         if ((rc = efx_nvram_rw_finish(enp, EFX_NVRAM_BOOTROM_CFG, NULL)) != 0)
577                 goto fail12;
578
579         EFSYS_KMEM_FREE(enp->en_esip, partn_length, partn_data);
580
581         return (0);
582
583 fail12:
584         EFSYS_PROBE(fail12);
585 fail11:
586         EFSYS_PROBE(fail11);
587 fail10:
588         EFSYS_PROBE(fail10);
589 fail9:
590         EFSYS_PROBE(fail9);
591
592         (void) efx_nvram_rw_finish(enp, EFX_NVRAM_BOOTROM_CFG, NULL);
593 fail8:
594         EFSYS_PROBE(fail8);
595
596         EFSYS_KMEM_FREE(enp->en_esip, partn_length, partn_data);
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 #endif  /* EFSYS_OPT_BOOTCFG */