1/*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright (c) 2010 Aleksandr Rybalko. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29/* 30 * Pseudo driver to copy the NVRAM settings from various sources 31 * into the kernel environment. 32 * 33 * Drivers (such as ethernet devices) can then use environment 34 * variables to set default parameters. 35 */ 36 37#include <sys/cdefs.h> 38__FBSDID("$FreeBSD$"); 39 40#include <sys/param.h> 41#include <sys/systm.h> 42#include <sys/bus.h> 43#include <sys/endian.h> 44#include <sys/kernel.h> 45#include <sys/module.h> 46#include <sys/rman.h> 47#include <sys/malloc.h> 48 49#include <machine/bus.h> 50 51#include "nvram2env.h" 52 53static void 54nvram2env_identify(driver_t * drv, device_t parent) 55{ 56 int i, ivar; 57 58 for (i = 0; !resource_int_value("nvram", i, "base", &ivar); i++) 59 BUS_ADD_CHILD(parent, 0, "nvram2env", i); 60} 61 62int 63nvram2env_probe(device_t dev) 64{ 65 uint32_t i, ivar, sig; 66 struct nvram2env_softc * sc = device_get_softc(dev); 67 68 /* 69 * Please ensure that your implementation of NVRAM->ENV specifies 70 * bus tag 71 */ 72 if (sc->bst == NULL) 73 return (ENXIO); 74 75 if (sc->sig == 0) 76 if (resource_int_value("nvram", device_get_unit(dev), "sig", 77 &sc->sig) != 0 || sc->sig == 0) 78 sc->sig = CFE_NVRAM_SIGNATURE; 79 80 if (sc->maxsize == 0) 81 if (resource_int_value("nvram", device_get_unit(dev), "maxsize", 82 &sc->maxsize) != 0 || sc->maxsize == 0) 83 sc->maxsize = NVRAM_MAX_SIZE; 84 85 if (sc->flags == 0) 86 if (resource_int_value("nvram", device_get_unit(dev), "flags", 87 &sc->flags) != 0 || sc->flags == 0) 88 sc->flags = NVRAM_FLAGS_GENERIC; 89 90 for (i = 0; i < 2; i ++) 91 { 92 switch (i) { 93 case 0: 94 break; 95 case 1: 96 case 2: 97 if (resource_int_value("nvram", device_get_unit(dev), 98 (i == 1) ? "base" : "fallbackbase", &ivar) != 0 || 99 ivar == 0) 100 continue; 101 102 sc->addr = ivar; 103 break; 104 default: 105 break; 106 } 107 108 if (sc->addr == 0) 109 continue; 110 111 if (bootverbose) 112 device_printf(dev, "base=0x%08x sig=0x%08x " 113 "maxsize=0x%08x flags=0x%08x\n", 114 sc->addr, sc->sig, sc->maxsize, sc->flags); 115 116 if (bus_space_map(sc->bst, sc->addr, sc->maxsize, 0, 117 &sc->bsh) != 0) 118 continue; 119 120 sig = bus_space_read_4(sc->bst, sc->bsh, 0); 121 if ( sig == sc->sig /*FLSH*/) 122 { 123 device_printf(dev, "Found NVRAM at %#x\n", 124 (uint32_t)ivar); 125 sc->need_swap = 0; 126 goto unmap_done; 127 } 128 else if ( htole32(sig) == sc->sig /*HSLF*/) 129 { 130 device_printf(dev, "Found NVRAM at %#x\n", 131 (uint32_t)ivar); 132 sc->need_swap = 1; 133 goto unmap_done; 134 } else if (sc->flags & NVRAM_FLAGS_UBOOT) { 135 device_printf(dev, "Use NVRAM at %#x\n", 136 (uint32_t)ivar); 137 sc->crc = sig; 138 goto unmap_done; 139 } 140 bus_space_unmap(sc->bst, sc->bsh, NVRAM_MAX_SIZE); 141 } 142 sc->bst = 0; 143 sc->bsh = 0; 144 sc->addr = 0; 145 return (ENXIO); 146 147unmap_done: 148 bus_space_unmap(sc->bst, sc->bsh, NVRAM_MAX_SIZE); 149 device_set_desc(dev, "NVRAM to ENV pseudo-device"); 150 return (BUS_PROBE_SPECIFIC); 151 152} 153 154static uint32_t read_4(struct nvram2env_softc * sc, int offset) 155{ 156 if (sc->need_swap) 157 return (bswap32(bus_space_read_4(sc->bst, sc->bsh, offset))); 158 else 159 return (bus_space_read_4(sc->bst, sc->bsh, offset)); 160} 161 162int 163nvram2env_attach(device_t dev) 164{ 165 struct nvram2env_softc *sc; 166 struct nvram *nv; 167 char *pair, *value, *assign; 168 uint32_t sig, size, i, *tmp; 169 170 sc = device_get_softc(dev); 171 172 if (sc->bst == 0 || sc->addr == 0) 173 return (ENXIO); 174 175 if (bus_space_map(sc->bst, sc->addr, NVRAM_MAX_SIZE, 0, 176 &sc->bsh) != 0) 177 return (ENXIO); 178 179 sig = read_4(sc, 0); 180 size = read_4(sc, 4); 181 182 if (bootverbose) 183 device_printf(dev, " size=0x%05x maxsize=0x%05x\n", size, 184 sc->maxsize); 185 186 size = (size > sc->maxsize)?sc->maxsize:size; 187 188 if (sig == sc->sig || (sc->flags & NVRAM_FLAGS_UBOOT)) 189 { 190 /* align size to 32bit size*/ 191 size += 3; 192 size &= ~3; 193 194 nv = malloc(size, M_DEVBUF, M_WAITOK | M_ZERO); 195 if (!nv) 196 return (ENOMEM); 197 /* set tmp pointer to begin of NVRAM */ 198 tmp = (uint32_t *) nv; 199 200 /* use read_4 to swap bytes if it's required */ 201 for (i = 0; i < size; i += 4) { 202 *tmp = read_4(sc, i); 203 tmp++; 204 } 205 /* now tmp pointer is end of NVRAM */ 206 207 if (sc->flags & NVRAM_FLAGS_BROADCOM) { 208 device_printf(dev, "sig = %#x\n", nv->sig); 209 device_printf(dev, "size = %#x\n", nv->size); 210 } 211 212 if (!(sc->flags & NVRAM_FLAGS_NOCHECK)) { 213 /* TODO: need checksum verification */ 214 } 215 216 if (sc->flags & NVRAM_FLAGS_GENERIC) 217 pair = (char*)nv+4; 218 if (sc->flags & NVRAM_FLAGS_UBOOT) 219 pair = (char*)nv+4; 220 else if (sc->flags & NVRAM_FLAGS_BROADCOM) 221 pair = (char*)nv+20; 222 else 223 pair = (char*)nv+4; 224 225 /* iterate over buffer till end. tmp points to end of NVRAM */ 226 for ( ; pair < (char*)tmp; 227 pair += strlen(pair) + strlen(value) + 2 ) { 228 if (!pair || (strlen(pair) == 0)) 229 break; 230 231 /* hint.nvram.0. */ 232 assign = strchr(pair,'='); 233 assign[0] = '\0'; 234 value = assign+1; 235 236 if (bootverbose) 237 printf("ENV[%p]: %s=%s\n", 238 (void*)((char*)pair - (char*)nv), 239 pair, value); 240 241 kern_setenv(pair, value); 242 243 if (strcasecmp(pair, "WAN_MAC_ADDR") == 0) { 244 /* Alias for MAC address of eth0 */ 245 if (bootverbose) 246 printf("ENV: aliasing " 247 "WAN_MAC_ADDR to ethaddr" 248 " = %s\n", value); 249 kern_setenv("ethaddr", value); 250 } 251 else if (strcasecmp(pair, "LAN_MAC_ADDR") == 0){ 252 /* Alias for MAC address of eth1 */ 253 if (bootverbose) 254 printf("ENV: aliasing " 255 "LAN_MAC_ADDR to eth1addr" 256 " = %s\n", value); 257 kern_setenv("eth1addr", value); 258 } 259 260 if (strcmp(pair, "bootverbose") == 0) 261 bootverbose = strtoul(value, 0, 0); 262 if (strcmp(pair, "boothowto" ) == 0) 263 boothowto = strtoul(value, 0, 0); 264 } 265 free(nv, M_DEVBUF); 266 } 267 268 bus_space_unmap(sc->bst, sc->bsh, NVRAM_MAX_SIZE); 269 270 return (0); 271} 272 273static device_method_t nvram2env_methods[] = { 274 /* Device interface */ 275 DEVMETHOD(device_identify, nvram2env_identify), 276 DEVMETHOD(device_probe, nvram2env_probe), 277 DEVMETHOD(device_attach, nvram2env_attach), 278 279 DEVMETHOD_END 280}; 281 282driver_t nvram2env_driver = { 283 "nvram2env", 284 nvram2env_methods, 285 sizeof(struct nvram2env_softc), 286}; 287 288devclass_t nvram2env_devclass; 289