1 /*-
2  * Copyright (c) 2016 Landon Fuller <[email protected]>
3  * Copyright (c) 2017 The FreeBSD Foundation
4  * All rights reserved.
5  *
6  * Portions of this software were developed by Landon Fuller
7  * under sponsorship from the FreeBSD Foundation.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer,
14  *    without modification.
15  * 2. Redistributions in binary form must reproduce at minimum a disclaimer
16  *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
17  *    redistribution must be conditioned upon including a substantially
18  *    similar Disclaimer requirement for further binary redistribution.
19  *
20  * NO WARRANTY
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
24  * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
25  * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
26  * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
29  * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
31  * THE POSSIBILITY OF SUCH DAMAGES.
32  */
33 
34 #include <sys/cdefs.h>
35 __FBSDID("$FreeBSD$");
36 
37 /*
38  * BHND CFE NVRAM driver.
39  *
40  * Provides access to device NVRAM via CFE.
41  */
42 
43 #include <sys/param.h>
44 #include <sys/kernel.h>
45 #include <sys/bus.h>
46 #include <sys/limits.h>
47 #include <sys/malloc.h>
48 #include <sys/module.h>
49 #include <sys/systm.h>
50 
51 #include <machine/bus.h>
52 #include <sys/rman.h>
53 #include <machine/resource.h>
54 
55 #include <dev/bhnd/bhnd.h>
56 
57 #include <dev/cfe/cfe_api.h>
58 #include <dev/cfe/cfe_error.h>
59 #include <dev/cfe/cfe_ioctl.h>
60 
61 #include "bhnd_nvram_if.h"
62 
63 #include "bcm_machdep.h"
64 #include "bcm_nvram_cfevar.h"
65 
66 BHND_NVRAM_IOPS_DEFN(iocfe)
67 
68 #define IOCFE_LOG(_io, _fmt, ...)	\
69 	printf("%s/%s: " _fmt, __FUNCTION__, (_io)->dname, ##__VA_ARGS__)
70 
71 static int	bcm_nvram_iocfe_init(struct bcm_nvram_iocfe *iocfe,
72 		    char *dname);
73 
74 /** Known CFE NVRAM device names, in probe order. */
75 static char *nvram_cfe_devs[] = {
76 	"nflash0.nvram",	/* NAND */
77 	"nflash1.nvram",
78 	"flash0.nvram",
79 	"flash1.nvram",
80 };
81 
82 /** Supported CFE NVRAM formats, in probe order. */
83 static bhnd_nvram_data_class * const nvram_cfe_fmts[] = {
84 	&bhnd_nvram_bcm_class,
85 	&bhnd_nvram_tlv_class
86 };
87 
88 static int
bhnd_nvram_cfe_probe(device_t dev)89 bhnd_nvram_cfe_probe(device_t dev)
90 {
91 	struct bcm_platform *bp;
92 
93 	/* Fetch platform NVRAM I/O context */
94 	bp = bcm_get_platform();
95 	if (bp->nvram_io == NULL)
96 		return (ENXIO);
97 
98 	KASSERT(bp->nvram_cls != NULL, ("missing NVRAM class"));
99 
100 	/* Set the device description */
101 	device_set_desc(dev, bhnd_nvram_data_class_desc(bp->nvram_cls));
102 
103 	/* Refuse wildcard attachments */
104 	return (BUS_PROBE_NOWILDCARD);
105 }
106 
107 static int
bhnd_nvram_cfe_attach(device_t dev)108 bhnd_nvram_cfe_attach(device_t dev)
109 {
110 	struct bcm_platform		*bp;
111 	struct bhnd_nvram_cfe_softc	*sc;
112 	int				 error;
113 
114 	bp = bcm_get_platform();
115 	KASSERT(bp->nvram_io != NULL, ("missing NVRAM I/O context"));
116 	KASSERT(bp->nvram_cls != NULL, ("missing NVRAM class"));
117 
118 	sc = device_get_softc(dev);
119 	sc->dev = dev;
120 
121 	error = bhnd_nvram_store_parse_new(&sc->store, bp->nvram_io,
122 	    bp->nvram_cls);
123 	if (error)
124 		return (error);
125 
126 	error = bhnd_service_registry_add(&bp->services, dev,
127 	    BHND_SERVICE_NVRAM, 0);
128 	if (error) {
129 		bhnd_nvram_store_free(sc->store);
130 		return (error);
131 	}
132 
133 	return (error);
134 }
135 
136 static int
bhnd_nvram_cfe_resume(device_t dev)137 bhnd_nvram_cfe_resume(device_t dev)
138 {
139 	return (0);
140 }
141 
142 static int
bhnd_nvram_cfe_suspend(device_t dev)143 bhnd_nvram_cfe_suspend(device_t dev)
144 {
145 	return (0);
146 }
147 
148 static int
bhnd_nvram_cfe_detach(device_t dev)149 bhnd_nvram_cfe_detach(device_t dev)
150 {
151 	struct bcm_platform		*bp;
152 	struct bhnd_nvram_cfe_softc	*sc;
153 	int				 error;
154 
155 	bp = bcm_get_platform();
156 	sc = device_get_softc(dev);
157 
158 	error = bhnd_service_registry_remove(&bp->services, dev,
159 	    BHND_SERVICE_ANY);
160 	if (error)
161 		return (error);
162 
163 	bhnd_nvram_store_free(sc->store);
164 
165 	return (0);
166 }
167 
168 static int
bhnd_nvram_cfe_getvar(device_t dev,const char * name,void * buf,size_t * len,bhnd_nvram_type type)169 bhnd_nvram_cfe_getvar(device_t dev, const char *name, void *buf, size_t *len,
170     bhnd_nvram_type type)
171 {
172 	struct bhnd_nvram_cfe_softc *sc = device_get_softc(dev);
173 
174 	return (bhnd_nvram_store_getvar(sc->store, name, buf, len, type));
175 }
176 
177 static int
bhnd_nvram_cfe_setvar(device_t dev,const char * name,const void * buf,size_t len,bhnd_nvram_type type)178 bhnd_nvram_cfe_setvar(device_t dev, const char *name, const void *buf,
179     size_t len, bhnd_nvram_type type)
180 {
181 	struct bhnd_nvram_cfe_softc *sc = device_get_softc(dev);
182 
183 	return (bhnd_nvram_store_setvar(sc->store, name, buf, len, type));
184 }
185 
186 /**
187  * Find, open, identify, and initialize an I/O context mapping the CFE NVRAM
188  * device.
189  *
190  * @param[out]	iocfe		On success, an I/O context mapping the CFE NVRAM
191  *				device.
192  * @param[out]	cls		On success, the identified NVRAM data format
193  *				class.
194  *
195  * @retval 0		success. the caller inherits ownership of @p iocfe.
196  * @retval non-zero	if no usable CFE NVRAM device can be found, a standard
197  *			unix error will be returned.
198  */
199 int
bcm_nvram_find_cfedev(struct bcm_nvram_iocfe * iocfe,bhnd_nvram_data_class ** cls)200 bcm_nvram_find_cfedev(struct bcm_nvram_iocfe *iocfe,
201     bhnd_nvram_data_class **cls)
202 {
203 	char	*dname;
204 	int	 devinfo;
205 	int	 error, result;
206 
207 	for (u_int i = 0; i < nitems(nvram_cfe_fmts); i++) {
208 		*cls = nvram_cfe_fmts[i];
209 
210 		for (u_int j = 0; j < nitems(nvram_cfe_devs); j++) {
211 			dname = nvram_cfe_devs[j];
212 
213 			/* Does the device exist? */
214 			if ((devinfo = cfe_getdevinfo(dname)) < 0) {
215 				if (devinfo != CFE_ERR_DEVNOTFOUND) {
216 					BCM_ERR("cfe_getdevinfo(%s) failed: "
217 					    "%d\n", dname, devinfo);
218 				}
219 
220 				continue;
221 			}
222 
223 			/* Open for reading */
224 			if ((error = bcm_nvram_iocfe_init(iocfe, dname)))
225 				continue;
226 
227 			/* Probe */
228 			result = bhnd_nvram_data_probe(*cls, &iocfe->io);
229 			if (result <= 0) {
230 				/* Found a supporting NVRAM data class */
231 				return (0);
232 			}
233 
234 			/* Keep searching */
235 			bhnd_nvram_io_free(&iocfe->io);
236 		}
237 	}
238 
239 	return (ENODEV);
240 }
241 
242 /**
243  * Initialize a new CFE device-backed I/O context.
244  *
245  * The caller is responsible for releasing all resources held by the returned
246  * I/O context via bhnd_nvram_io_free().
247  *
248  * @param[out]	io	On success, will be initialized as an I/O context for
249  *			CFE device @p dname.
250  * @param	dname	The name of the CFE device to be opened for reading.
251  *
252  * @retval 0		success.
253  * @retval non-zero	if opening @p dname otherwise fails, a standard unix
254  *			error will be returned.
255  */
256 static int
bcm_nvram_iocfe_init(struct bcm_nvram_iocfe * iocfe,char * dname)257 bcm_nvram_iocfe_init(struct bcm_nvram_iocfe *iocfe, char *dname)
258 {
259 	nvram_info_t		 nvram_info;
260 	int			 cerr, devinfo, dtype, rlen;
261 	int64_t			 nv_offset;
262 	u_int			 nv_size;
263 	bool			 req_blk_erase;
264 	int			 error;
265 
266 	iocfe->io.iops = &bhnd_nvram_iocfe_ops;
267 	iocfe->dname = dname;
268 
269 	/* Try to open the device */
270 	iocfe->fd = cfe_open(dname);
271 	if (iocfe->fd <= 0) {
272 		IOCFE_LOG(iocfe, "cfe_open() failed: %d\n", iocfe->fd);
273 
274 		return (ENXIO);
275 	}
276 
277 	/* Try to fetch device info */
278 	if ((devinfo = cfe_getdevinfo(iocfe->dname)) < 0) {
279 		IOCFE_LOG(iocfe, "cfe_getdevinfo() failed: %d\n", devinfo);
280 		error = ENXIO;
281 		goto failed;
282 	}
283 
284 	/* Verify device type */
285 	dtype = devinfo & CFE_DEV_MASK;
286 	switch (dtype) {
287 	case CFE_DEV_FLASH:
288 	case CFE_DEV_NVRAM:
289 		/* Valid device type */
290 		break;
291 	default:
292 		IOCFE_LOG(iocfe, "unknown device type: %d\n", dtype);
293 		error = ENXIO;
294 		goto failed;
295 	}
296 
297 	/* Try to fetch nvram info from CFE */
298 	cerr = cfe_ioctl(iocfe->fd, IOCTL_NVRAM_GETINFO,
299 	    (unsigned char *)&nvram_info, sizeof(nvram_info), &rlen, 0);
300 	if (cerr == CFE_OK) {
301 		/* Sanity check the result; must not be a negative integer */
302 		if (nvram_info.nvram_size < 0 ||
303 		    nvram_info.nvram_offset < 0)
304 		{
305 			IOCFE_LOG(iocfe, "invalid NVRAM layout (%d/%d)\n",
306 			    nvram_info.nvram_size, nvram_info.nvram_offset);
307 			error = ENXIO;
308 			goto failed;
309 		}
310 
311 		nv_offset	= nvram_info.nvram_offset;
312 		nv_size		= nvram_info.nvram_size;
313 		req_blk_erase	= (nvram_info.nvram_eraseflg != 0);
314 	} else if (cerr != CFE_OK && cerr != CFE_ERR_INV_COMMAND) {
315 		IOCFE_LOG(iocfe, "IOCTL_NVRAM_GETINFO failed: %d\n", cerr);
316 		error = ENXIO;
317 		goto failed;
318 	}
319 
320 	/* Fall back on flash info.
321 	 *
322 	 * This is known to be required on the Asus RT-N53 (CFE 5.70.55.33,
323 	 * BBP 1.0.37, BCM5358UB0), where IOCTL_NVRAM_GETINFO returns
324 	 * CFE_ERR_INV_COMMAND.
325 	 */
326 	if (cerr == CFE_ERR_INV_COMMAND) {
327 		flash_info_t fi;
328 
329 		cerr = cfe_ioctl(iocfe->fd, IOCTL_FLASH_GETINFO,
330 		    (unsigned char *)&fi, sizeof(fi), &rlen, 0);
331 
332 		if (cerr != CFE_OK) {
333 			IOCFE_LOG(iocfe, "IOCTL_FLASH_GETINFO failed %d\n",
334 			    cerr);
335 			error = ENXIO;
336 			goto failed;
337 		}
338 
339 		nv_offset	= 0x0;
340 		nv_size		= fi.flash_size;
341 		req_blk_erase	= !(fi.flash_flags & FLASH_FLAG_NOERASE);
342 	}
343 
344 	/* Verify that the full NVRAM layout can be represented via size_t */
345 	if (nv_size > SIZE_MAX || SIZE_MAX - nv_size < nv_offset) {
346 		IOCFE_LOG(iocfe, "invalid NVRAM layout (%#x/%#jx)\n",
347 		    nv_size, (intmax_t)nv_offset);
348 		error = ENXIO;
349 		goto failed;
350 	}
351 
352 	iocfe->offset = nv_offset;
353 	iocfe->size = nv_size;
354 	iocfe->req_blk_erase = req_blk_erase;
355 
356 	return (CFE_OK);
357 
358 failed:
359 	if (iocfe->fd >= 0)
360 		cfe_close(iocfe->fd);
361 
362 	return (error);
363 }
364 
365 static void
bhnd_nvram_iocfe_free(struct bhnd_nvram_io * io)366 bhnd_nvram_iocfe_free(struct bhnd_nvram_io *io)
367 {
368 	struct bcm_nvram_iocfe	*iocfe = (struct bcm_nvram_iocfe *)io;
369 
370 	/* CFE I/O instances are statically allocated; we do not need to free
371 	 * the instance itself */
372 	cfe_close(iocfe->fd);
373 }
374 
375 static size_t
bhnd_nvram_iocfe_getsize(struct bhnd_nvram_io * io)376 bhnd_nvram_iocfe_getsize(struct bhnd_nvram_io *io)
377 {
378 	struct bcm_nvram_iocfe	*iocfe = (struct bcm_nvram_iocfe *)io;
379 	return (iocfe->size);
380 }
381 
382 static int
bhnd_nvram_iocfe_setsize(struct bhnd_nvram_io * io,size_t size)383 bhnd_nvram_iocfe_setsize(struct bhnd_nvram_io *io, size_t size)
384 {
385 	/* unsupported */
386 	return (ENODEV);
387 }
388 
389 static int
bhnd_nvram_iocfe_read_ptr(struct bhnd_nvram_io * io,size_t offset,const void ** ptr,size_t nbytes,size_t * navail)390 bhnd_nvram_iocfe_read_ptr(struct bhnd_nvram_io *io, size_t offset,
391     const void **ptr, size_t nbytes, size_t *navail)
392 {
393 	/* unsupported */
394 	return (ENODEV);
395 }
396 
397 static int
bhnd_nvram_iocfe_write_ptr(struct bhnd_nvram_io * io,size_t offset,void ** ptr,size_t nbytes,size_t * navail)398 bhnd_nvram_iocfe_write_ptr(struct bhnd_nvram_io *io, size_t offset,
399     void **ptr, size_t nbytes, size_t *navail)
400 {
401 	/* unsupported */
402 	return (ENODEV);
403 }
404 
405 static int
bhnd_nvram_iocfe_write(struct bhnd_nvram_io * io,size_t offset,void * buffer,size_t nbytes)406 bhnd_nvram_iocfe_write(struct bhnd_nvram_io *io, size_t offset, void *buffer,
407     size_t nbytes)
408 {
409 	/* unsupported */
410 	return (ENODEV);
411 }
412 
413 static int
bhnd_nvram_iocfe_read(struct bhnd_nvram_io * io,size_t offset,void * buffer,size_t nbytes)414 bhnd_nvram_iocfe_read(struct bhnd_nvram_io *io, size_t offset, void *buffer,
415     size_t nbytes)
416 {
417 	struct bcm_nvram_iocfe	*iocfe;
418 	size_t			 remain;
419 	int64_t			 cfe_offset;
420 	int			 nr, nreq;
421 
422 	iocfe = (struct bcm_nvram_iocfe *)io;
423 
424 	/* Determine (and validate) the base CFE offset */
425 #if (SIZE_MAX > INT64_MAX)
426 	if (iocfe->offset > INT64_MAX || offset > INT64_MAX)
427 		return (ENXIO);
428 #endif
429 
430 	if (INT64_MAX - offset < iocfe->offset)
431 		return (ENXIO);
432 
433 	cfe_offset = iocfe->offset + offset;
434 
435 	/* Verify that cfe_offset + nbytes is representable */
436 	if (INT64_MAX - cfe_offset < nbytes)
437 		return (ENXIO);
438 
439 	/* Perform the read */
440 	for (remain = nbytes; remain > 0;) {
441 		void	*p;
442 		size_t	 nread;
443 		int64_t	 cfe_noff;
444 
445 		nread = (nbytes - remain);
446 		cfe_noff = cfe_offset + nread;
447 		p = ((uint8_t *)buffer + nread);
448 		nreq = ummin(INT_MAX, remain);
449 
450 		nr = cfe_readblk(iocfe->fd, cfe_noff, p, nreq);
451 		if (nr < 0) {
452 			IOCFE_LOG(iocfe, "cfe_readblk() failed: %d\n", nr);
453 			return (ENXIO);
454 		}
455 
456 		/* Check for unexpected short read */
457 		if (nr == 0 && remain > 0) {
458 			/* If the request fits entirely within the CFE
459 			 * device range, we shouldn't hit EOF */
460 			if (remain < iocfe->size &&
461 			    iocfe->size - remain > offset)
462 			{
463 				IOCFE_LOG(iocfe, "cfe_readblk() returned "
464 				    "unexpected short read (%d/%d)\n", nr,
465 				    nreq);
466 				return (ENXIO);
467 			}
468 		}
469 
470 		if (nr == 0)
471 			break;
472 
473 		remain -= nr;
474 	}
475 
476 	/* Check for short read */
477 	if (remain > 0)
478 		return (ENXIO);
479 
480 	return (0);
481 }
482 
483 static device_method_t bhnd_nvram_cfe_methods[] = {
484 	/* Device interface */
485 	DEVMETHOD(device_probe,		bhnd_nvram_cfe_probe),
486 	DEVMETHOD(device_attach,	bhnd_nvram_cfe_attach),
487 	DEVMETHOD(device_resume,	bhnd_nvram_cfe_resume),
488 	DEVMETHOD(device_suspend,	bhnd_nvram_cfe_suspend),
489 	DEVMETHOD(device_detach,	bhnd_nvram_cfe_detach),
490 
491 	/* NVRAM interface */
492 	DEVMETHOD(bhnd_nvram_getvar,	bhnd_nvram_cfe_getvar),
493 	DEVMETHOD(bhnd_nvram_setvar,	bhnd_nvram_cfe_setvar),
494 
495 	DEVMETHOD_END
496 };
497 
498 DEFINE_CLASS_0(bhnd_nvram, bhnd_nvram_cfe, bhnd_nvram_cfe_methods,
499     sizeof(struct bhnd_nvram_cfe_softc));
500 EARLY_DRIVER_MODULE(bhnd_nvram_cfe, nexus, bhnd_nvram_cfe,
501     bhnd_nvram_devclass, NULL, NULL, BUS_PASS_BUS + BUS_PASS_ORDER_EARLY);
502