]> CyberLeo.Net >> Repos - FreeBSD/releng/10.3.git/blob - sys/dev/sfxge/common/efx_bootcfg.c
- Copy stable/10@296371 to releng/10.3 in preparation for 10.3-RC1
[FreeBSD/releng/10.3.git] / sys / dev / sfxge / common / efx_bootcfg.c
1 /*-
2  * Copyright (c) 2009-2015 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  * A multiple of 0x100 so trailing 0xff characters don't contrinbute to the
42  * checksum.
43  */
44 #define BOOTCFG_MAX_SIZE 0x1000
45
46 #define DHCP_END (uint8_t)0xff
47 #define DHCP_PAD (uint8_t)0
48
49 static  __checkReturn           uint8_t
50 efx_bootcfg_csum(
51         __in                    efx_nic_t *enp,
52         __in_bcount(size)       caddr_t data,
53         __in                    size_t size)
54 {
55         _NOTE(ARGUNUSED(enp))
56
57         unsigned int pos;
58         uint8_t checksum = 0;
59
60         for (pos = 0; pos < size; pos++)
61                 checksum += data[pos];
62         return (checksum);
63 }
64
65 static  __checkReturn           efx_rc_t
66 efx_bootcfg_verify(
67         __in                    efx_nic_t *enp,
68         __in_bcount(size)       caddr_t data,
69         __in                    size_t size,
70         __out_opt               size_t *usedp)
71 {
72         size_t offset = 0;
73         size_t used = 0;
74         efx_rc_t rc;
75
76         /* Start parsing tags immediatly after the checksum */
77         for (offset = 1; offset < size; ) {
78                 uint8_t tag;
79                 uint8_t length;
80
81                 /* Consume tag */
82                 tag = data[offset];
83                 if (tag == DHCP_END) {
84                         offset++;
85                         used = offset;
86                         break;
87                 }
88                 if (tag == DHCP_PAD) {
89                         offset++;
90                         continue;
91                 }
92
93                 /* Consume length */
94                 if (offset + 1 >= size) {
95                         rc = ENOSPC;
96                         goto fail1;
97                 }
98                 length = data[offset + 1];
99
100                 /* Consume *length */
101                 if (offset + 1 + length >= size) {
102                         rc = ENOSPC;
103                         goto fail2;
104                 }
105
106                 offset += 2 + length;
107                 used = offset;
108         }
109
110         /* Checksum the entire sector, including bytes after any DHCP_END */
111         if (efx_bootcfg_csum(enp, data, size) != 0) {
112                 rc = EINVAL;
113                 goto fail3;
114         }
115
116         if (usedp != NULL)
117                 *usedp = used;
118
119         return (0);
120
121 fail3:
122         EFSYS_PROBE(fail3);
123 fail2:
124         EFSYS_PROBE(fail2);
125 fail1:
126         EFSYS_PROBE1(fail1, efx_rc_t, rc);
127
128         return (rc);
129 }
130
131                                 efx_rc_t
132 efx_bootcfg_read(
133         __in                    efx_nic_t *enp,
134         __out_bcount(size)      caddr_t data,
135         __in                    size_t size)
136 {
137         uint8_t *payload = NULL;
138         size_t used_bytes;
139         size_t sector_length;
140         efx_rc_t rc;
141
142         rc = efx_nvram_size(enp, EFX_NVRAM_BOOTROM_CFG, &sector_length);
143         if (rc != 0)
144                 goto fail1;
145
146         /*
147          * We need to read the entire BOOTCFG area to ensure we read all the
148          * tags, because legacy bootcfg sectors are not guaranteed to end with
149          * a DHCP_END character. If the user hasn't supplied a sufficiently
150          * large buffer then use our own buffer.
151          */
152         if (sector_length > BOOTCFG_MAX_SIZE)
153                 sector_length = BOOTCFG_MAX_SIZE;
154         if (sector_length > size) {
155                 EFSYS_KMEM_ALLOC(enp->en_esip, sector_length, payload);
156                 if (payload == NULL) {
157                         rc = ENOMEM;
158                         goto fail2;
159                 }
160         } else
161                 payload = (uint8_t *)data;
162
163         if ((rc = efx_nvram_rw_start(enp, EFX_NVRAM_BOOTROM_CFG, NULL)) != 0)
164                 goto fail3;
165
166         rc = efx_nvram_read_chunk(enp, EFX_NVRAM_BOOTROM_CFG, 0,
167                                     (caddr_t)payload, sector_length);
168
169         efx_nvram_rw_finish(enp, EFX_NVRAM_BOOTROM_CFG);
170
171         if (rc != 0)
172                 goto fail4;
173
174         /* Verify that the area is correctly formatted and checksummed */
175         rc = efx_bootcfg_verify(enp, (caddr_t)payload, sector_length,
176                                     &used_bytes);
177         if (rc != 0 || used_bytes == 0) {
178                 payload[0] = (uint8_t)~DHCP_END;
179                 payload[1] = DHCP_END;
180                 used_bytes = 2;
181         }
182
183         EFSYS_ASSERT(used_bytes >= 2);  /* checksum and DHCP_END */
184         EFSYS_ASSERT(used_bytes <= sector_length);
185
186         /*
187          * Legacy bootcfg sectors don't terminate with a DHCP_END character.
188          * Modify the returned payload so it does. BOOTCFG_MAX_SIZE is by
189          * definition large enough for any valid (per-port) bootcfg sector,
190          * so reinitialise the sector if there isn't room for the character.
191          */
192         if (payload[used_bytes - 1] != DHCP_END) {
193                 if (used_bytes + 1 > sector_length) {
194                         payload[0] = 0;
195                         used_bytes = 1;
196                 }
197
198                 payload[used_bytes] = DHCP_END;
199                 ++used_bytes;
200         }
201
202         /*
203          * Verify that the user supplied buffer is large enough for the
204          * entire used bootcfg area, then copy into the user supplied buffer.
205          */
206         if (used_bytes > size) {
207                 rc = ENOSPC;
208                 goto fail5;
209         }
210         if (sector_length > size) {
211                 memcpy(data, payload, used_bytes);
212                 EFSYS_KMEM_FREE(enp->en_esip, sector_length, payload);
213         }
214
215         /* Zero out the unused portion of the user buffer */
216         if (used_bytes < size)
217                 (void) memset(data + used_bytes, 0, size - used_bytes);
218
219         /*
220          * The checksum includes trailing data after any DHCP_END character,
221          * which we've just modified (by truncation or appending DHCP_END).
222          */
223         data[0] -= efx_bootcfg_csum(enp, data, size);
224
225         return (0);
226
227 fail5:
228         EFSYS_PROBE(fail5);
229 fail4:
230         EFSYS_PROBE(fail4);
231 fail3:
232         EFSYS_PROBE(fail3);
233
234         if (sector_length > size)
235                 EFSYS_KMEM_FREE(enp->en_esip, sector_length, payload);
236 fail2:
237         EFSYS_PROBE(fail2);
238 fail1:
239         EFSYS_PROBE1(fail1, efx_rc_t, rc);
240
241         return (rc);
242 }
243
244                                 efx_rc_t
245 efx_bootcfg_write(
246         __in                    efx_nic_t *enp,
247         __in_bcount(size)       caddr_t data,
248         __in                    size_t size)
249 {
250         uint8_t *chunk;
251         uint8_t checksum;
252         size_t sector_length;
253         size_t chunk_length;
254         size_t used_bytes;
255         size_t offset;
256         size_t remaining;
257         efx_rc_t rc;
258
259         rc = efx_nvram_size(enp, EFX_NVRAM_BOOTROM_CFG, &sector_length);
260         if (rc != 0)
261                 goto fail1;
262
263         if (sector_length > BOOTCFG_MAX_SIZE)
264                 sector_length = BOOTCFG_MAX_SIZE;
265
266         if ((rc = efx_bootcfg_verify(enp, data, size, &used_bytes)) != 0)
267                 goto fail2;
268
269         /* The caller *must* terminate their block with a DHCP_END character */
270         EFSYS_ASSERT(used_bytes >= 2);          /* checksum and DHCP_END */
271         if ((uint8_t)data[used_bytes - 1] != DHCP_END) {
272                 rc = ENOENT;
273                 goto fail3;
274         }
275
276         /* Check that the hardware has support for this much data */
277         if (used_bytes > MIN(sector_length, BOOTCFG_MAX_SIZE)) {
278                 rc = ENOSPC;
279                 goto fail4;
280         }
281
282         rc = efx_nvram_rw_start(enp, EFX_NVRAM_BOOTROM_CFG, &chunk_length);
283         if (rc != 0)
284                 goto fail5;
285
286         EFSYS_KMEM_ALLOC(enp->en_esip, chunk_length, chunk);
287         if (chunk == NULL) {
288                 rc = ENOMEM;
289                 goto fail6;
290         }
291
292         if ((rc = efx_nvram_erase(enp, EFX_NVRAM_BOOTROM_CFG)) != 0)
293                 goto fail7;
294
295         /*
296          * Write the entire sector_length bytes of data in chunks. Zero out
297          * all data following the DHCP_END, and adjust the checksum
298          */
299         checksum = efx_bootcfg_csum(enp, data, used_bytes);
300         for (offset = 0; offset < sector_length; offset += remaining) {
301                 remaining = MIN(chunk_length, sector_length - offset);
302
303                 /* Fill chunk */
304                 (void) memset(chunk, 0x0, chunk_length);
305                 if (offset < used_bytes)
306                         memcpy(chunk, data + offset,
307                             MIN(remaining, used_bytes - offset));
308
309                 /* Adjust checksum */
310                 if (offset == 0)
311                         chunk[0] -= checksum;
312
313                 if ((rc = efx_nvram_write_chunk(enp, EFX_NVRAM_BOOTROM_CFG,
314                             offset, (caddr_t)chunk, remaining)) != 0)
315                         goto fail8;
316         }
317
318         efx_nvram_rw_finish(enp, EFX_NVRAM_BOOTROM_CFG);
319
320         EFSYS_KMEM_FREE(enp->en_esip, chunk_length, chunk);
321
322         return (0);
323
324 fail8:
325         EFSYS_PROBE(fail8);
326 fail7:
327         EFSYS_PROBE(fail7);
328
329         EFSYS_KMEM_FREE(enp->en_esip, chunk_length, chunk);
330 fail6:
331         EFSYS_PROBE(fail6);
332
333         efx_nvram_rw_finish(enp, EFX_NVRAM_BOOTROM_CFG);
334 fail5:
335         EFSYS_PROBE(fail5);
336 fail4:
337         EFSYS_PROBE(fail4);
338 fail3:
339         EFSYS_PROBE(fail3);
340 fail2:
341         EFSYS_PROBE(fail2);
342 fail1:
343         EFSYS_PROBE1(fail1, efx_rc_t, rc);
344
345         return (rc);
346 }
347
348 #endif  /* EFSYS_OPT_BOOTCFG */