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, §or_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, §or_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 */ 351