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