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