]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/mips/broadcom/bcm_nvram_cfe.c
Remove incomplete support for plain MD5 from OCF.
[FreeBSD/FreeBSD.git] / sys / mips / broadcom / bcm_nvram_cfe.c
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
108 static int
109 bhnd_nvram_cfe_attach(device_t dev)
110 {
111         struct bcm_platform             *bp;
112         struct bhnd_nvram_cfe_softc     *sc;
113         int                              error;
114
115         bp = bcm_get_platform();
116         KASSERT(bp->nvram_io != NULL, ("missing NVRAM I/O context"));
117         KASSERT(bp->nvram_cls != NULL, ("missing NVRAM class"));
118
119         sc = device_get_softc(dev);
120         sc->dev = dev;
121
122         error = bhnd_nvram_store_parse_new(&sc->store, bp->nvram_io,
123             bp->nvram_cls);
124         if (error)
125                 return (error);
126
127         error = bhnd_service_registry_add(&bp->services, dev,
128             BHND_SERVICE_NVRAM, 0);
129         if (error) {
130                 bhnd_nvram_store_free(sc->store);
131                 return (error);
132         }
133
134         return (error);
135 }
136
137 static int
138 bhnd_nvram_cfe_resume(device_t dev)
139 {
140         return (0);
141 }
142
143 static int
144 bhnd_nvram_cfe_suspend(device_t dev)
145 {
146         return (0);
147 }
148
149 static int
150 bhnd_nvram_cfe_detach(device_t dev)
151 {
152         struct bcm_platform             *bp;
153         struct bhnd_nvram_cfe_softc     *sc;
154         int                              error;
155
156         bp = bcm_get_platform();
157         sc = device_get_softc(dev);
158
159         error = bhnd_service_registry_remove(&bp->services, dev,
160             BHND_SERVICE_ANY);
161         if (error)
162                 return (error);
163
164         bhnd_nvram_store_free(sc->store);
165
166         return (0);
167 }
168
169 static int
170 bhnd_nvram_cfe_getvar(device_t dev, const char *name, void *buf, size_t *len,
171     bhnd_nvram_type type)
172 {
173         struct bhnd_nvram_cfe_softc *sc = device_get_softc(dev);
174
175         return (bhnd_nvram_store_getvar(sc->store, name, buf, len, type));
176 }
177
178 static int
179 bhnd_nvram_cfe_setvar(device_t dev, const char *name, const void *buf,
180     size_t len, bhnd_nvram_type type)
181 {
182         struct bhnd_nvram_cfe_softc *sc = device_get_softc(dev);
183
184         return (bhnd_nvram_store_setvar(sc->store, name, buf, len, type));
185 }
186
187 /**
188  * Find, open, identify, and initialize an I/O context mapping the CFE NVRAM
189  * device.
190  * 
191  * @param[out]  iocfe           On success, an I/O context mapping the CFE NVRAM
192  *                              device.
193  * @param[out]  cls             On success, the identified NVRAM data format
194  *                              class.
195  *
196  * @retval 0            success. the caller inherits ownership of @p iocfe.
197  * @retval non-zero     if no usable CFE NVRAM device can be found, a standard
198  *                      unix error will be returned.
199  */
200 int
201 bcm_nvram_find_cfedev(struct bcm_nvram_iocfe *iocfe,
202     bhnd_nvram_data_class **cls)
203 {
204         char    *dname;
205         int      devinfo;
206         int      error, result;
207
208         for (u_int i = 0; i < nitems(nvram_cfe_fmts); i++) {
209                 *cls = nvram_cfe_fmts[i];
210
211                 for (u_int j = 0; j < nitems(nvram_cfe_devs); j++) {
212                         dname = nvram_cfe_devs[j];
213
214                         /* Does the device exist? */
215                         if ((devinfo = cfe_getdevinfo(dname)) < 0) {
216                                 if (devinfo != CFE_ERR_DEVNOTFOUND) {
217                                         BCM_ERR("cfe_getdevinfo(%s) failed: "
218                                             "%d\n", dname, devinfo);
219                                 }
220
221                                 continue;
222                         }
223
224                         /* Open for reading */
225                         if ((error = bcm_nvram_iocfe_init(iocfe, dname)))
226                                 continue;
227
228                         /* Probe */
229                         result = bhnd_nvram_data_probe(*cls, &iocfe->io);
230                         if (result <= 0) {
231                                 /* Found a supporting NVRAM data class */
232                                 return (0);
233                         }
234
235                         /* Keep searching */
236                         bhnd_nvram_io_free(&iocfe->io);
237                 }
238         }
239
240         return (ENODEV);
241 }
242
243
244 /**
245  * Initialize a new CFE device-backed I/O context.
246  *
247  * The caller is responsible for releasing all resources held by the returned
248  * I/O context via bhnd_nvram_io_free().
249  * 
250  * @param[out]  io      On success, will be initialized as an I/O context for
251  *                      CFE device @p dname.
252  * @param       dname   The name of the CFE device to be opened for reading.
253  *
254  * @retval 0            success.
255  * @retval non-zero     if opening @p dname otherwise fails, a standard unix
256  *                      error will be returned.
257  */
258 static int
259 bcm_nvram_iocfe_init(struct bcm_nvram_iocfe *iocfe, char *dname)
260 {
261         nvram_info_t             nvram_info;
262         int                      cerr, devinfo, dtype, rlen;
263         int64_t                  nv_offset;
264         u_int                    nv_size;
265         bool                     req_blk_erase;
266         int                      error;
267
268         iocfe->io.iops = &bhnd_nvram_iocfe_ops;
269         iocfe->dname = dname;
270
271         /* Try to open the device */
272         iocfe->fd = cfe_open(dname);
273         if (iocfe->fd <= 0) {
274                 IOCFE_LOG(iocfe, "cfe_open() failed: %d\n", iocfe->fd);
275
276                 return (ENXIO);
277         }
278
279         /* Try to fetch device info */
280         if ((devinfo = cfe_getdevinfo(iocfe->dname)) < 0) {
281                 IOCFE_LOG(iocfe, "cfe_getdevinfo() failed: %d\n", devinfo);
282                 error = ENXIO;
283                 goto failed;
284         }
285
286         /* Verify device type */
287         dtype = devinfo & CFE_DEV_MASK;
288         switch (dtype) {
289         case CFE_DEV_FLASH:
290         case CFE_DEV_NVRAM:
291                 /* Valid device type */
292                 break;
293         default:
294                 IOCFE_LOG(iocfe, "unknown device type: %d\n", dtype);
295                 error = ENXIO;
296                 goto failed;
297         }
298
299         /* Try to fetch nvram info from CFE */
300         cerr = cfe_ioctl(iocfe->fd, IOCTL_NVRAM_GETINFO,
301             (unsigned char *)&nvram_info, sizeof(nvram_info), &rlen, 0);
302         if (cerr == CFE_OK) {
303                 /* Sanity check the result; must not be a negative integer */
304                 if (nvram_info.nvram_size < 0 ||
305                     nvram_info.nvram_offset < 0)
306                 {
307                         IOCFE_LOG(iocfe, "invalid NVRAM layout (%d/%d)\n",
308                             nvram_info.nvram_size, nvram_info.nvram_offset);
309                         error = ENXIO;
310                         goto failed;
311                 }
312
313                 nv_offset       = nvram_info.nvram_offset;
314                 nv_size         = nvram_info.nvram_size;
315                 req_blk_erase   = (nvram_info.nvram_eraseflg != 0);
316         } else if (cerr != CFE_OK && cerr != CFE_ERR_INV_COMMAND) {
317                 IOCFE_LOG(iocfe, "IOCTL_NVRAM_GETINFO failed: %d\n", cerr);
318                 error = ENXIO;
319                 goto failed;
320         }
321
322         /* Fall back on flash info.
323          * 
324          * This is known to be required on the Asus RT-N53 (CFE 5.70.55.33, 
325          * BBP 1.0.37, BCM5358UB0), where IOCTL_NVRAM_GETINFO returns
326          * CFE_ERR_INV_COMMAND.
327          */
328         if (cerr == CFE_ERR_INV_COMMAND) {
329                 flash_info_t fi;
330
331                 cerr = cfe_ioctl(iocfe->fd, IOCTL_FLASH_GETINFO,
332                     (unsigned char *)&fi, sizeof(fi), &rlen, 0);
333
334                 if (cerr != CFE_OK) {
335                         IOCFE_LOG(iocfe, "IOCTL_FLASH_GETINFO failed %d\n",
336                             cerr);
337                         error = ENXIO;
338                         goto failed;
339                 }
340
341                 nv_offset       = 0x0;
342                 nv_size         = fi.flash_size;
343                 req_blk_erase   = !(fi.flash_flags & FLASH_FLAG_NOERASE);
344         }
345
346         
347         /* Verify that the full NVRAM layout can be represented via size_t */
348         if (nv_size > SIZE_MAX || SIZE_MAX - nv_size < nv_offset) {
349                 IOCFE_LOG(iocfe, "invalid NVRAM layout (%#x/%#jx)\n",
350                     nv_size, (intmax_t)nv_offset);
351                 error = ENXIO;
352                 goto failed;
353         }
354
355         iocfe->offset = nv_offset;
356         iocfe->size = nv_size;
357         iocfe->req_blk_erase = req_blk_erase;
358
359         return (CFE_OK);
360
361 failed:
362         if (iocfe->fd >= 0)
363                 cfe_close(iocfe->fd);
364
365         return (error);
366 }
367
368 static void
369 bhnd_nvram_iocfe_free(struct bhnd_nvram_io *io)
370 {
371         struct bcm_nvram_iocfe  *iocfe = (struct bcm_nvram_iocfe *)io;
372
373         /* CFE I/O instances are statically allocated; we do not need to free
374          * the instance itself */
375         cfe_close(iocfe->fd);
376 }
377
378 static size_t
379 bhnd_nvram_iocfe_getsize(struct bhnd_nvram_io *io)
380 {
381         struct bcm_nvram_iocfe  *iocfe = (struct bcm_nvram_iocfe *)io;
382         return (iocfe->size);
383 }
384
385 static int
386 bhnd_nvram_iocfe_setsize(struct bhnd_nvram_io *io, size_t size)
387 {
388         /* unsupported */
389         return (ENODEV);
390 }
391
392 static int
393 bhnd_nvram_iocfe_read_ptr(struct bhnd_nvram_io *io, size_t offset,
394     const void **ptr, size_t nbytes, size_t *navail)
395 {
396         /* unsupported */
397         return (ENODEV);
398 }
399
400 static int
401 bhnd_nvram_iocfe_write_ptr(struct bhnd_nvram_io *io, size_t offset,
402     void **ptr, size_t nbytes, size_t *navail)
403 {
404         /* unsupported */
405         return (ENODEV);
406 }
407
408 static int
409 bhnd_nvram_iocfe_write(struct bhnd_nvram_io *io, size_t offset, void *buffer,
410     size_t nbytes)
411 {
412         /* unsupported */
413         return (ENODEV);
414 }
415
416 static int
417 bhnd_nvram_iocfe_read(struct bhnd_nvram_io *io, size_t offset, void *buffer,
418     size_t nbytes)
419 {
420         struct bcm_nvram_iocfe  *iocfe;
421         size_t                   remain;
422         int64_t                  cfe_offset;
423         int                      nr, nreq;
424
425         iocfe = (struct bcm_nvram_iocfe *)io;
426
427         /* Determine (and validate) the base CFE offset */
428 #if (SIZE_MAX > INT64_MAX)
429         if (iocfe->offset > INT64_MAX || offset > INT64_MAX)
430                 return (ENXIO);
431 #endif
432
433         if (INT64_MAX - offset < iocfe->offset)
434                 return (ENXIO);
435
436         cfe_offset = iocfe->offset + offset;
437
438         /* Verify that cfe_offset + nbytes is representable */
439         if (INT64_MAX - cfe_offset < nbytes)
440                 return (ENXIO);
441
442         /* Perform the read */
443         for (remain = nbytes; remain > 0;) {
444                 void    *p;
445                 size_t   nread;
446                 int64_t  cfe_noff;
447
448                 nread = (nbytes - remain);
449                 cfe_noff = cfe_offset + nread;
450                 p = ((uint8_t *)buffer + nread);
451                 nreq = ummin(INT_MAX, remain);
452         
453                 nr = cfe_readblk(iocfe->fd, cfe_noff, p, nreq);
454                 if (nr < 0) {
455                         IOCFE_LOG(iocfe, "cfe_readblk() failed: %d\n", nr);
456                         return (ENXIO);
457                 }
458
459                 /* Check for unexpected short read */
460                 if (nr == 0 && remain > 0) {
461                         /* If the request fits entirely within the CFE
462                          * device range, we shouldn't hit EOF */
463                         if (remain < iocfe->size &&
464                             iocfe->size - remain > offset)
465                         {
466                                 IOCFE_LOG(iocfe, "cfe_readblk() returned "
467                                     "unexpected short read (%d/%d)\n", nr,
468                                     nreq);
469                                 return (ENXIO);
470                         }
471                 }
472
473                 if (nr == 0)
474                         break;
475
476                 remain -= nr;
477         }
478
479         /* Check for short read */
480         if (remain > 0)
481                 return (ENXIO);
482
483         return (0);
484 }
485
486 static device_method_t bhnd_nvram_cfe_methods[] = {
487         /* Device interface */
488         DEVMETHOD(device_probe,         bhnd_nvram_cfe_probe),
489         DEVMETHOD(device_attach,        bhnd_nvram_cfe_attach),
490         DEVMETHOD(device_resume,        bhnd_nvram_cfe_resume),
491         DEVMETHOD(device_suspend,       bhnd_nvram_cfe_suspend),
492         DEVMETHOD(device_detach,        bhnd_nvram_cfe_detach),
493
494         /* NVRAM interface */
495         DEVMETHOD(bhnd_nvram_getvar,    bhnd_nvram_cfe_getvar),
496         DEVMETHOD(bhnd_nvram_setvar,    bhnd_nvram_cfe_setvar),
497
498         DEVMETHOD_END
499 };
500
501 DEFINE_CLASS_0(bhnd_nvram, bhnd_nvram_cfe, bhnd_nvram_cfe_methods,
502     sizeof(struct bhnd_nvram_cfe_softc));
503 EARLY_DRIVER_MODULE(bhnd_nvram_cfe, nexus, bhnd_nvram_cfe,
504     bhnd_nvram_devclass, NULL, NULL, BUS_PASS_BUS + BUS_PASS_ORDER_EARLY);