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 91 for (i = 0; i < 2; i ++) 92 { 93 switch (i) { 94 case 0: 95 break; 96 case 1: 97 case 2: 98 if (resource_int_value("nvram", device_get_unit(dev), 99 (i == 1) ? "base" : "fallbackbase", &ivar) != 0 || 100 ivar == 0) 101 continue; 102 103 sc->addr = ivar; 104 break; 105 default: 106 break; 107 } 108 109 if (sc->addr == 0) 110 continue; 111 112 if (bootverbose) 113 device_printf(dev, "base=0x%08x sig=0x%08x " 114 "maxsize=0x%08x flags=0x%08x\n", 115 sc->addr, sc->sig, sc->maxsize, sc->flags); 116 117 if (bus_space_map(sc->bst, sc->addr, sc->maxsize, 0, 118 &sc->bsh) != 0) 119 continue; 120 121 sig = bus_space_read_4(sc->bst, sc->bsh, 0); 122 if ( sig == sc->sig /*FLSH*/) 123 { 124 device_printf(dev, "Found NVRAM at %#x\n", 125 (uint32_t)ivar); 126 sc->need_swap = 0; 127 goto unmap_done; 128 } 129 else if ( htole32(sig) == sc->sig /*HSLF*/) 130 { 131 device_printf(dev, "Found NVRAM at %#x\n", 132 (uint32_t)ivar); 133 sc->need_swap = 1; 134 goto unmap_done; 135 } else if (sc->flags & NVRAM_FLAGS_UBOOT) { 136 device_printf(dev, "Use NVRAM at %#x\n", 137 (uint32_t)ivar); 138 sc->crc = sig; 139 goto unmap_done; 140 } 141 bus_space_unmap(sc->bst, sc->bsh, NVRAM_MAX_SIZE); 142 } 143 sc->bst = 0; 144 sc->bsh = 0; 145 sc->addr = 0; 146 return (ENXIO); 147 148unmap_done: 149 bus_space_unmap(sc->bst, sc->bsh, NVRAM_MAX_SIZE); 150 device_set_desc(dev, "NVRAM to ENV pseudo-device"); 151 return (BUS_PROBE_SPECIFIC); 152 153} 154 155static uint32_t read_4(struct nvram2env_softc * sc, int offset) 156{ 157 if (sc->need_swap) 158 return (bswap32(bus_space_read_4(sc->bst, sc->bsh, offset))); 159 else 160 return (bus_space_read_4(sc->bst, sc->bsh, offset)); 161} 162 163 164int 165nvram2env_attach(device_t dev) 166{ 167 struct nvram2env_softc *sc; 168 struct nvram *nv; 169 char *pair, *value, *assign; 170 uint32_t sig, size, i, *tmp; 171 172 sc = device_get_softc(dev); 173 174 if (sc->bst == 0 || sc->addr == 0) 175 return (ENXIO); 176 177 if (bus_space_map(sc->bst, sc->addr, NVRAM_MAX_SIZE, 0, 178 &sc->bsh) != 0) 179 return (ENXIO); 180 181 sig = read_4(sc, 0); 182 size = read_4(sc, 4); 183 184 if (bootverbose) 185 device_printf(dev, " size=0x%05x maxsize=0x%05x\n", size, 186 sc->maxsize); 187 188 size = (size > sc->maxsize)?sc->maxsize:size; 189 190 191 if (sig == sc->sig || (sc->flags & NVRAM_FLAGS_UBOOT)) 192 { 193 194 /* align size to 32bit size*/ 195 size += 3; 196 size &= ~3; 197 198 nv = malloc(size, M_DEVBUF, M_WAITOK | M_ZERO); 199 if (!nv) 200 return (ENOMEM); 201 /* set tmp pointer to begin of NVRAM */ 202 tmp = (uint32_t *) nv; 203 204 /* use read_4 to swap bytes if it's required */ 205 for (i = 0; i < size; i += 4) { 206 *tmp = read_4(sc, i); 207 tmp++; 208 } 209 /* now tmp pointer is end of NVRAM */ 210 211 if (sc->flags & NVRAM_FLAGS_BROADCOM) { 212 device_printf(dev, "sig = %#x\n", nv->sig); 213 device_printf(dev, "size = %#x\n", nv->size); 214 } 215 216 if (!(sc->flags & NVRAM_FLAGS_NOCHECK)) { 217 /* TODO: need checksum verification */ 218 } 219 220 if (sc->flags & NVRAM_FLAGS_GENERIC) 221 pair = (char*)nv+4; 222 if (sc->flags & NVRAM_FLAGS_UBOOT) 223 pair = (char*)nv+4; 224 else if (sc->flags & NVRAM_FLAGS_BROADCOM) 225 pair = (char*)nv+20; 226 else 227 pair = (char*)nv+4; 228 229 /* iterate over buffer till end. tmp points to end of NVRAM */ 230 for ( ; pair < (char*)tmp; 231 pair += strlen(pair) + strlen(value) + 2 ) { 232 233 if (!pair || (strlen(pair) == 0)) 234 break; 235 236 /* hint.nvram.0. */ 237 assign = strchr(pair,'='); 238 assign[0] = '\0'; 239 value = assign+1; 240 241 if (bootverbose) 242 printf("ENV[%p]: %s=%s\n", 243 (void*)((char*)pair - (char*)nv), 244 pair, value); 245 246 kern_setenv(pair, value); 247 248 if (strcasecmp(pair, "WAN_MAC_ADDR") == 0) { 249 /* Alias for MAC address of eth0 */ 250 if (bootverbose) 251 printf("ENV: aliasing " 252 "WAN_MAC_ADDR to ethaddr" 253 " = %s\n", value); 254 kern_setenv("ethaddr", value); 255 } 256 else if (strcasecmp(pair, "LAN_MAC_ADDR") == 0){ 257 /* Alias for MAC address of eth1 */ 258 if (bootverbose) 259 printf("ENV: aliasing " 260 "LAN_MAC_ADDR to eth1addr" 261 " = %s\n", value); 262 kern_setenv("eth1addr", value); 263 } 264 265 if (strcmp(pair, "bootverbose") == 0) 266 bootverbose = strtoul(value, 0, 0); 267 if (strcmp(pair, "boothowto" ) == 0) 268 boothowto = strtoul(value, 0, 0); 269 270 } 271 free(nv, M_DEVBUF); 272 } 273 274 bus_space_unmap(sc->bst, sc->bsh, NVRAM_MAX_SIZE); 275 276 return (0); 277} 278 279static device_method_t nvram2env_methods[] = { 280 /* Device interface */ 281 DEVMETHOD(device_identify, nvram2env_identify), 282 DEVMETHOD(device_probe, nvram2env_probe), 283 DEVMETHOD(device_attach, nvram2env_attach), 284 285 DEVMETHOD_END 286}; 287 288driver_t nvram2env_driver = { 289 "nvram2env", 290 nvram2env_methods, 291 sizeof(struct nvram2env_softc), 292}; 293 294devclass_t nvram2env_devclass; 295