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