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