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