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