1 /*- 2 * Copyright 2009 Solarflare Communications Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS AND 14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23 * SUCH DAMAGE. 24 */ 25 26 #include <sys/cdefs.h> 27 __FBSDID("$FreeBSD$"); 28 29 #include "efsys.h" 30 #include "efx.h" 31 #include "efx_types.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 int 63 efx_bootcfg_verify( 64 __in efx_nic_t *enp, 65 __in_bcount(size) caddr_t data, 66 __in size_t size, 67 __out size_t *usedp) 68 { 69 size_t offset = 0; 70 size_t used = 0; 71 int 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, int, rc); 124 125 return (rc); 126 } 127 128 int 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 int 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 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, int, rc); 237 238 return (rc); 239 } 240 241 int 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 int 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 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, int, rc); 341 342 return (rc); 343 } 344 345 #endif /* EFSYS_OPT_BOOTCFG */ 346