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