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