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