1 /*-
2 * Copyright (c) 2016 Landon Fuller <landonf@FreeBSD.org>
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
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
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
137 bhnd_nvram_cfe_resume(device_t dev)
138 {
139 return (0);
140 }
141
142 static int
143 bhnd_nvram_cfe_suspend(device_t dev)
144 {
145 return (0);
146 }
147
148 static int
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
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
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
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
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
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
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
383 bhnd_nvram_iocfe_setsize(struct bhnd_nvram_io *io, size_t size)
384 {
385 /* unsupported */
386 return (ENODEV);
387 }
388
389 static int
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
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
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
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);
Cache object: 36ba75e79acff129e6cf2f2f5848818c
|