]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - sys/dev/sfxge/common/efx_bootcfg.c
MFC r310815
[FreeBSD/stable/10.git] / sys / dev / sfxge / common / efx_bootcfg.c
1 /*-
2  * Copyright (c) 2009-2016 Solarflare Communications Inc.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *
8  * 1. Redistributions of source code must retain the above copyright notice,
9  *    this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright notice,
11  *    this list of conditions and the following disclaimer in the documentation
12  *    and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
16  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
18  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
21  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
22  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
23  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
24  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  *
26  * The views and conclusions contained in the software and documentation are
27  * those of the authors and should not be interpreted as representing official
28  * policies, either expressed or implied, of the FreeBSD Project.
29  */
30
31 #include <sys/cdefs.h>
32 __FBSDID("$FreeBSD$");
33
34 #include "efx.h"
35 #include "efx_impl.h"
36
37 #if EFSYS_OPT_BOOTCFG
38
39 /*
40  * Maximum size of BOOTCFG block across all nics as understood by SFCgPXE.
41  * NOTE: This is larger than the Medford per-PF bootcfg sector.
42  */
43 #define BOOTCFG_MAX_SIZE 0x1000
44
45 /* Medford per-PF bootcfg sector */
46 #define BOOTCFG_PER_PF   0x800
47 #define BOOTCFG_PF_COUNT 16
48
49 #define DHCP_END ((uint8_t)0xff)
50 #define DHCP_PAD ((uint8_t)0)
51
52
53 /* Report the layout of bootcfg sectors in NVRAM partition. */
54         __checkReturn           efx_rc_t
55 efx_bootcfg_sector_info(
56         __in                    efx_nic_t *enp,
57         __in                    uint32_t pf,
58         __out_opt               uint32_t *sector_countp,
59         __out                   size_t *offsetp,
60         __out                   size_t *max_sizep)
61 {
62         uint32_t count;
63         size_t max_size;
64         size_t offset;
65         int rc;
66
67         switch (enp->en_family) {
68 #if EFSYS_OPT_SIENA
69         case EFX_FAMILY_SIENA:
70                 max_size = BOOTCFG_MAX_SIZE;
71                 offset = 0;
72                 count = 1;
73                 break;
74 #endif /* EFSYS_OPT_SIENA */
75
76 #if EFSYS_OPT_HUNTINGTON
77         case EFX_FAMILY_HUNTINGTON:
78                 max_size = BOOTCFG_MAX_SIZE;
79                 offset = 0;
80                 count = 1;
81                 break;
82 #endif /* EFSYS_OPT_HUNTINGTON */
83
84 #if EFSYS_OPT_MEDFORD
85         case EFX_FAMILY_MEDFORD: {
86                 /* Shared partition (array indexed by PF) */
87                 max_size = BOOTCFG_PER_PF;
88                 count = BOOTCFG_PF_COUNT;
89                 if (pf >= count) {
90                         rc = EINVAL;
91                         goto fail2;
92                 }
93                 offset = max_size * pf;
94                 break;
95         }
96 #endif /* EFSYS_OPT_MEDFORD */
97
98         default:
99                 EFSYS_ASSERT(0);
100                 rc = ENOTSUP;
101                 goto fail1;
102         }
103         EFSYS_ASSERT3U(max_size, <=, BOOTCFG_MAX_SIZE);
104
105         if (sector_countp != NULL)
106                 *sector_countp = count;
107         *offsetp = offset;
108         *max_sizep = max_size;
109
110         return (0);
111
112 #if EFSYS_OPT_MEDFORD
113 fail2:
114         EFSYS_PROBE(fail2);
115 #endif
116 fail1:
117         EFSYS_PROBE1(fail1, efx_rc_t, rc);
118         return (rc);
119 }
120
121
122 static  __checkReturn           uint8_t
123 efx_bootcfg_csum(
124         __in                    efx_nic_t *enp,
125         __in_bcount(size)       uint8_t const *data,
126         __in                    size_t size)
127 {
128         _NOTE(ARGUNUSED(enp))
129
130         unsigned int pos;
131         uint8_t checksum = 0;
132
133         for (pos = 0; pos < size; pos++)
134                 checksum += data[pos];
135         return (checksum);
136 }
137
138 static  __checkReturn           efx_rc_t
139 efx_bootcfg_verify(
140         __in                    efx_nic_t *enp,
141         __in_bcount(size)       uint8_t const *data,
142         __in                    size_t size,
143         __out_opt               size_t *usedp)
144 {
145         size_t offset = 0;
146         size_t used = 0;
147         efx_rc_t rc;
148
149         /* Start parsing tags immediatly after the checksum */
150         for (offset = 1; offset < size; ) {
151                 uint8_t tag;
152                 uint8_t length;
153
154                 /* Consume tag */
155                 tag = data[offset];
156                 if (tag == DHCP_END) {
157                         offset++;
158                         used = offset;
159                         break;
160                 }
161                 if (tag == DHCP_PAD) {
162                         offset++;
163                         continue;
164                 }
165
166                 /* Consume length */
167                 if (offset + 1 >= size) {
168                         rc = ENOSPC;
169                         goto fail1;
170                 }
171                 length = data[offset + 1];
172
173                 /* Consume *length */
174                 if (offset + 1 + length >= size) {
175                         rc = ENOSPC;
176                         goto fail2;
177                 }
178
179                 offset += 2 + length;
180                 used = offset;
181         }
182
183         /* Checksum the entire sector, including bytes after any DHCP_END */
184         if (efx_bootcfg_csum(enp, data, size) != 0) {
185                 rc = EINVAL;
186                 goto fail3;
187         }
188
189         if (usedp != NULL)
190                 *usedp = used;
191
192         return (0);
193
194 fail3:
195         EFSYS_PROBE(fail3);
196 fail2:
197         EFSYS_PROBE(fail2);
198 fail1:
199         EFSYS_PROBE1(fail1, efx_rc_t, rc);
200
201         return (rc);
202 }
203
204 /*
205  * Copy bootcfg sector data to a target buffer which may differ in size.
206  * Optionally corrects format errors in source buffer.
207  */
208                                 efx_rc_t
209 efx_bootcfg_copy_sector(
210         __in                    efx_nic_t *enp,
211         __inout_bcount(sector_length)
212                                 uint8_t *sector,
213         __in                    size_t sector_length,
214         __out_bcount(data_size) uint8_t *data,
215         __in                    size_t data_size,
216         __in                    boolean_t handle_format_errors)
217 {
218         size_t used_bytes;
219         efx_rc_t rc;
220
221         /* Verify that the area is correctly formatted and checksummed */
222         rc = efx_bootcfg_verify(enp, sector, sector_length,
223                                     &used_bytes);
224
225         if (!handle_format_errors) {
226                 if (rc != 0)
227                         goto fail1;
228
229                 if ((used_bytes < 2) ||
230                     (sector[used_bytes - 1] != DHCP_END)) {
231                         /* Block too short, or DHCP_END missing */
232                         rc = ENOENT;
233                         goto fail2;
234                 }
235         }
236
237         /* Synthesize empty format on verification failure */
238         if (rc != 0 || used_bytes == 0) {
239                 sector[0] = 0;
240                 sector[1] = DHCP_END;
241                 used_bytes = 2;
242         }
243         EFSYS_ASSERT(used_bytes >= 2);  /* checksum and DHCP_END */
244         EFSYS_ASSERT(used_bytes <= sector_length);
245         EFSYS_ASSERT(sector_length >= 2);
246
247         /*
248          * Legacy bootcfg sectors don't terminate with a DHCP_END character.
249          * Modify the returned payload so it does.
250          * Reinitialise the sector if there isn't room for the character.
251          */
252         if (sector[used_bytes - 1] != DHCP_END) {
253                 if (used_bytes >= sector_length) {
254                         sector[0] = 0;
255                         used_bytes = 1;
256                 }
257                 sector[used_bytes] = DHCP_END;
258                 ++used_bytes;
259         }
260
261         /*
262          * Verify that the target buffer is large enough for the
263          * entire used bootcfg area, then copy into the target buffer.
264          */
265         if (used_bytes > data_size) {
266                 rc = ENOSPC;
267                 goto fail3;
268         }
269         memcpy(data, sector, used_bytes);
270
271         /* Zero out the unused portion of the target buffer */
272         if (used_bytes < data_size)
273                 (void) memset(data + used_bytes, 0, data_size - used_bytes);
274
275         /*
276          * The checksum includes trailing data after any DHCP_END character,
277          * which we've just modified (by truncation or appending DHCP_END).
278          */
279         data[0] -= efx_bootcfg_csum(enp, data, data_size);
280
281         return (0);
282
283 fail3:
284         EFSYS_PROBE(fail3);
285 fail2:
286         EFSYS_PROBE(fail2);
287 fail1:
288         EFSYS_PROBE1(fail1, efx_rc_t, rc);
289
290         return (rc);
291 }
292
293                                 efx_rc_t
294 efx_bootcfg_read(
295         __in                    efx_nic_t *enp,
296         __out_bcount(size)      caddr_t data,
297         __in                    size_t size)
298 {
299         uint8_t *payload = NULL;
300         size_t used_bytes;
301         size_t partn_length;
302         size_t sector_length;
303         size_t sector_offset;
304         efx_rc_t rc;
305         uint32_t sector_number;
306
307 #if EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD
308         sector_number = enp->en_nic_cfg.enc_pf;
309 #else
310         sector_number = 0;
311 #endif
312         rc = efx_nvram_size(enp, EFX_NVRAM_BOOTROM_CFG, &partn_length);
313         if (rc != 0)
314                 goto fail1;
315
316         /* The bootcfg sector may be stored in a (larger) shared partition */
317         rc = efx_bootcfg_sector_info(enp, sector_number,
318             NULL, &sector_offset, &sector_length);
319         if (rc != 0)
320                 goto fail2;
321
322         if (sector_length > BOOTCFG_MAX_SIZE)
323                 sector_length = BOOTCFG_MAX_SIZE;
324
325         if (sector_offset + sector_length > partn_length) {
326                 /* Partition is too small */
327                 rc = EFBIG;
328                 goto fail3;
329         }
330
331         /*
332          * We need to read the entire BOOTCFG sector to ensure we read all the
333          * tags, because legacy bootcfg sectors are not guaranteed to end with
334          * a DHCP_END character. If the user hasn't supplied a sufficiently
335          * large buffer then use our own buffer.
336          */
337         if (sector_length > size) {
338                 EFSYS_KMEM_ALLOC(enp->en_esip, sector_length, payload);
339                 if (payload == NULL) {
340                         rc = ENOMEM;
341                         goto fail4;
342                 }
343         } else
344                 payload = (uint8_t *)data;
345
346         if ((rc = efx_nvram_rw_start(enp, EFX_NVRAM_BOOTROM_CFG, NULL)) != 0)
347                 goto fail5;
348
349         if ((rc = efx_nvram_read_chunk(enp, EFX_NVRAM_BOOTROM_CFG,
350             sector_offset, (caddr_t)payload, sector_length)) != 0) {
351                 (void) efx_nvram_rw_finish(enp, EFX_NVRAM_BOOTROM_CFG);
352                 goto fail6;
353         }
354
355         if ((rc = efx_nvram_rw_finish(enp, EFX_NVRAM_BOOTROM_CFG)) != 0)
356                 goto fail7;
357
358         /* Verify that the area is correctly formatted and checksummed */
359         rc = efx_bootcfg_verify(enp, (caddr_t)payload, sector_length,
360             &used_bytes);
361         if (rc != 0 || used_bytes == 0) {
362                 payload[0] = (uint8_t)~DHCP_END;
363                 payload[1] = DHCP_END;
364                 used_bytes = 2;
365         }
366
367         EFSYS_ASSERT(used_bytes >= 2);  /* checksum and DHCP_END */
368         EFSYS_ASSERT(used_bytes <= sector_length);
369
370         /*
371          * Legacy bootcfg sectors don't terminate with a DHCP_END character.
372          * Modify the returned payload so it does. BOOTCFG_MAX_SIZE is by
373          * definition large enough for any valid (per-port) bootcfg sector,
374          * so reinitialise the sector if there isn't room for the character.
375          */
376         if (payload[used_bytes - 1] != DHCP_END) {
377                 if (used_bytes + 1 > sector_length) {
378                         payload[0] = 0;
379                         used_bytes = 1;
380                 }
381
382                 payload[used_bytes] = DHCP_END;
383                 ++used_bytes;
384         }
385
386         /*
387          * Verify that the user supplied buffer is large enough for the
388          * entire used bootcfg area, then copy into the user supplied buffer.
389          */
390         if (used_bytes > size) {
391                 rc = ENOSPC;
392                 goto fail8;
393         }
394         if (sector_length > size) {
395                 memcpy(data, payload, used_bytes);
396                 EFSYS_KMEM_FREE(enp->en_esip, sector_length, payload);
397         }
398
399         /* Zero out the unused portion of the user buffer */
400         if (used_bytes < size)
401                 (void) memset(data + used_bytes, 0, size - used_bytes);
402
403         /*
404          * The checksum includes trailing data after any DHCP_END character,
405          * which we've just modified (by truncation or appending DHCP_END).
406          */
407         data[0] -= efx_bootcfg_csum(enp, data, size);
408
409         return (0);
410
411 fail8:
412         EFSYS_PROBE(fail8);
413 fail7:
414         EFSYS_PROBE(fail7);
415 fail6:
416         EFSYS_PROBE(fail6);
417 fail5:
418         EFSYS_PROBE(fail5);
419         if (sector_length > size)
420                 EFSYS_KMEM_FREE(enp->en_esip, sector_length, payload);
421 fail4:
422         EFSYS_PROBE(fail4);
423 fail3:
424         EFSYS_PROBE(fail3);
425 fail2:
426         EFSYS_PROBE(fail2);
427 fail1:
428         EFSYS_PROBE1(fail1, efx_rc_t, rc);
429
430         return (rc);
431 }
432
433                                 efx_rc_t
434 efx_bootcfg_write(
435         __in                    efx_nic_t *enp,
436         __in_bcount(size)       caddr_t data,
437         __in                    size_t size)
438 {
439         uint8_t *partn_data;
440         uint8_t checksum;
441         size_t partn_length;
442         size_t sector_length;
443         size_t sector_offset;
444         size_t used_bytes;
445         efx_rc_t rc;
446         uint32_t sector_number;
447
448 #if EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD
449         sector_number = enp->en_nic_cfg.enc_pf;
450 #else
451         sector_number = 0;
452 #endif
453
454         rc = efx_nvram_size(enp, EFX_NVRAM_BOOTROM_CFG, &partn_length);
455         if (rc != 0)
456                 goto fail1;
457
458         /* The bootcfg sector may be stored in a (larger) shared partition */
459         rc = efx_bootcfg_sector_info(enp, sector_number,
460             NULL, &sector_offset, &sector_length);
461         if (rc != 0)
462                 goto fail2;
463
464         if (sector_length > BOOTCFG_MAX_SIZE)
465                 sector_length = BOOTCFG_MAX_SIZE;
466
467         if (sector_offset + sector_length > partn_length) {
468                 /* Partition is too small */
469                 rc = EFBIG;
470                 goto fail3;
471         }
472
473         if ((rc = efx_bootcfg_verify(enp, data, size, &used_bytes)) != 0)
474                 goto fail4;
475
476         /* The caller *must* terminate their block with a DHCP_END character */
477         if ((used_bytes < 2) || ((uint8_t)data[used_bytes - 1] != DHCP_END)) {
478                 /* Block too short or DHCP_END missing */
479                 rc = ENOENT;
480                 goto fail5;
481         }
482
483         /* Check that the hardware has support for this much data */
484         if (used_bytes > MIN(sector_length, BOOTCFG_MAX_SIZE)) {
485                 rc = ENOSPC;
486                 goto fail6;
487         }
488
489         /*
490          * If the BOOTCFG sector is stored in a shared partition, then we must
491          * read the whole partition and insert the updated bootcfg sector at the
492          * correct offset.
493          */
494         EFSYS_KMEM_ALLOC(enp->en_esip, partn_length, partn_data);
495         if (partn_data == NULL) {
496                 rc = ENOMEM;
497                 goto fail7;
498         }
499
500         rc = efx_nvram_rw_start(enp, EFX_NVRAM_BOOTROM_CFG, NULL);
501         if (rc != 0)
502                 goto fail8;
503
504         /* Read the entire partition */
505         rc = efx_nvram_read_chunk(enp, EFX_NVRAM_BOOTROM_CFG, 0,
506                                     (caddr_t)partn_data, partn_length);
507         if (rc != 0)
508                 goto fail9;
509
510         /*
511          * Insert the BOOTCFG sector into the partition, Zero out all data after
512          * the DHCP_END tag, and adjust the checksum.
513          */
514         (void) memset(partn_data + sector_offset, 0x0, sector_length);
515         (void) memcpy(partn_data + sector_offset, data, used_bytes);
516
517         checksum = efx_bootcfg_csum(enp, data, used_bytes);
518         partn_data[sector_offset] -= checksum;
519
520         if ((rc = efx_nvram_erase(enp, EFX_NVRAM_BOOTROM_CFG)) != 0)
521                 goto fail10;
522
523         if ((rc = efx_nvram_write_chunk(enp, EFX_NVRAM_BOOTROM_CFG,
524                     0, (caddr_t)partn_data, partn_length)) != 0)
525                 goto fail11;
526
527         if ((rc = efx_nvram_rw_finish(enp, EFX_NVRAM_BOOTROM_CFG)) != 0)
528                 goto fail12;
529
530         EFSYS_KMEM_FREE(enp->en_esip, partn_length, partn_data);
531
532         return (0);
533
534 fail12:
535         EFSYS_PROBE(fail12);
536 fail11:
537         EFSYS_PROBE(fail11);
538 fail10:
539         EFSYS_PROBE(fail10);
540 fail9:
541         EFSYS_PROBE(fail9);
542
543         (void) efx_nvram_rw_finish(enp, EFX_NVRAM_BOOTROM_CFG);
544 fail8:
545         EFSYS_PROBE(fail8);
546
547         EFSYS_KMEM_FREE(enp->en_esip, partn_length, partn_data);
548 fail7:
549         EFSYS_PROBE(fail7);
550 fail6:
551         EFSYS_PROBE(fail6);
552 fail5:
553         EFSYS_PROBE(fail5);
554 fail4:
555         EFSYS_PROBE(fail4);
556 fail3:
557         EFSYS_PROBE(fail3);
558 fail2:
559         EFSYS_PROBE(fail2);
560 fail1:
561         EFSYS_PROBE1(fail1, efx_rc_t, rc);
562
563         return (rc);
564 }
565
566 #endif  /* EFSYS_OPT_BOOTCFG */