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