]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/geom/geom_redboot.c
Add two missing eventhandler.h headers
[FreeBSD/FreeBSD.git] / sys / geom / geom_redboot.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2009 Sam Leffler, Errno Consulting
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer,
12  *    without modification.
13  * 2. Redistributions in binary form must reproduce at minimum a disclaimer
14  *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
15  *    redistribution must be conditioned upon including a substantially
16  *    similar Disclaimer requirement for further binary redistribution.
17  *
18  * NO WARRANTY
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21  * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
22  * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
23  * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
24  * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
27  * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
29  * THE POSSIBILITY OF SUCH DAMAGES.
30  */
31
32 #include <sys/cdefs.h>
33 __FBSDID("$FreeBSD$");
34
35 #include <sys/param.h>
36 #include <sys/errno.h>
37 #include <sys/endian.h>
38 #include <sys/systm.h>
39 #include <sys/kernel.h>
40 #include <sys/fcntl.h>
41 #include <sys/malloc.h>
42 #include <sys/bio.h>
43 #include <sys/lock.h>
44 #include <sys/mutex.h>
45 #include <sys/bus.h>
46
47 #include <sys/sbuf.h>
48 #include <geom/geom.h>
49 #include <geom/geom_slice.h>
50
51 #define REDBOOT_CLASS_NAME "REDBOOT"
52
53 struct fis_image_desc {
54         uint8_t         name[16];       /* null-terminated name */
55         uint32_t        offset;         /* offset in flash */
56         uint32_t        addr;           /* address in memory */
57         uint32_t        size;           /* image size in bytes */
58         uint32_t        entry;          /* offset in image for entry point */
59         uint32_t        dsize;          /* data size in bytes */
60         uint8_t         pad[256-(16+7*sizeof(uint32_t)+sizeof(void*))];
61         struct fis_image_desc *next;    /* linked list (in memory) */
62         uint32_t        dsum;           /* descriptor checksum */
63         uint32_t        fsum;           /* checksum over image data */
64 };
65
66 #define FISDIR_NAME     "FIS directory"
67 #define REDBCFG_NAME    "RedBoot config"
68 #define REDBOOT_NAME    "RedBoot"
69
70 #define REDBOOT_MAXSLICE        64
71 #define REDBOOT_MAXOFF \
72         (REDBOOT_MAXSLICE*sizeof(struct fis_image_desc))
73
74 struct g_redboot_softc {
75         uint32_t        entry[REDBOOT_MAXSLICE];
76         uint32_t        dsize[REDBOOT_MAXSLICE];
77         uint8_t         readonly[REDBOOT_MAXSLICE];
78         g_access_t      *parent_access;
79 };
80
81 static void
82 g_redboot_print(int i, struct fis_image_desc *fd)
83 {
84
85         printf("[%2d] \"%-15.15s\" %08x:%08x", i, fd->name,
86             fd->offset, fd->size);
87         printf(" addr %08x entry %08x\n", fd->addr, fd->entry);
88         printf("     dsize 0x%x dsum 0x%x fsum 0x%x\n", fd->dsize,
89             fd->dsum, fd->fsum);
90 }
91
92 static int
93 g_redboot_ioctl(struct g_provider *pp, u_long cmd, void *data, int fflag, struct thread *td)
94 {
95         return (ENOIOCTL);
96 }
97
98 static int
99 g_redboot_access(struct g_provider *pp, int dread, int dwrite, int dexcl)
100 {
101         struct g_geom *gp = pp->geom;
102         struct g_slicer *gsp = gp->softc;
103         struct g_redboot_softc *sc = gsp->softc;
104
105         if (dwrite > 0 && sc->readonly[pp->index])
106                 return (EPERM);
107         return (sc->parent_access(pp, dread, dwrite, dexcl));
108 }
109
110 static int
111 g_redboot_start(struct bio *bp)
112 {
113         struct g_provider *pp;
114         struct g_geom *gp;
115         struct g_redboot_softc *sc;
116         struct g_slicer *gsp;
117         int idx;
118
119         pp = bp->bio_to;
120         idx = pp->index;
121         gp = pp->geom;
122         gsp = gp->softc;
123         sc = gsp->softc;
124         if (bp->bio_cmd == BIO_GETATTR) {
125                 if (g_handleattr_int(bp, REDBOOT_CLASS_NAME "::entry",
126                     sc->entry[idx]))
127                         return (1);
128                 if (g_handleattr_int(bp, REDBOOT_CLASS_NAME "::dsize",
129                     sc->dsize[idx]))
130                         return (1);
131         }
132
133         return (0);
134 }
135
136 static void
137 g_redboot_dumpconf(struct sbuf *sb, const char *indent, struct g_geom *gp,
138         struct g_consumer *cp __unused, struct g_provider *pp)
139 {
140         struct g_redboot_softc *sc;
141         struct g_slicer *gsp;
142
143         gsp = gp->softc;
144         sc = gsp->softc;
145         g_slice_dumpconf(sb, indent, gp, cp, pp);
146         if (pp != NULL) {
147                 if (indent == NULL) {
148                         sbuf_printf(sb, " entry %d", sc->entry[pp->index]);
149                         sbuf_printf(sb, " dsize %d", sc->dsize[pp->index]);
150                 } else {
151                         sbuf_printf(sb, "%s<entry>%d</entry>\n", indent,
152                             sc->entry[pp->index]);
153                         sbuf_printf(sb, "%s<dsize>%d</dsize>\n", indent,
154                             sc->dsize[pp->index]);
155                 }
156         }
157 }
158
159 #include <sys/ctype.h>
160
161 static int
162 nameok(const char name[16])
163 {
164         int i;
165
166         /* descriptor names are null-terminated printable ascii */
167         for (i = 0; i < 15; i++)
168                 if (!isprint(name[i]))
169                         break;
170         return (name[i] == '\0');
171 }
172
173 static struct fis_image_desc *
174 parse_fis_directory(u_char *buf, size_t bufsize, off_t offset, uint32_t offmask)
175 {
176 #define match(a,b)      (bcmp(a, b, sizeof(b)-1) == 0)
177         struct fis_image_desc *fd, *efd;
178         struct fis_image_desc *fisdir, *redbcfg;
179         struct fis_image_desc *head, **tail;
180         int i;
181
182         fd = (struct fis_image_desc *)buf;
183         efd = fd + (bufsize / sizeof(struct fis_image_desc));
184 #if 0
185         /*
186          * Find the start of the FIS table.
187          */
188         while (fd < efd && fd->name[0] != 0xff)
189                 fd++;
190         if (fd == efd)
191                 return (NULL);
192         if (bootverbose)
193                 printf("RedBoot FIS table starts at 0x%jx\n",
194                     offset + fd - (struct fis_image_desc *) buf);
195 #endif
196         /*
197          * Scan forward collecting entries in a list.
198          */
199         fisdir = redbcfg = NULL;
200         *(tail = &head) = NULL;
201         for (i = 0; fd < efd; i++, fd++) {
202                 if (fd->name[0] == 0xff)
203                         continue;
204                 if (match(fd->name, FISDIR_NAME))
205                         fisdir = fd;
206                 else if (match(fd->name, REDBCFG_NAME))
207                         redbcfg = fd;
208                 if (nameok(fd->name)) {
209                         /*
210                          * NB: flash address includes platform mapping;
211                          *     strip it so we have only a flash offset.
212                          */
213                         fd->offset &= offmask;
214                         if (bootverbose)
215                                 g_redboot_print(i, fd);
216                         *tail = fd;
217                         *(tail = &fd->next) = NULL;
218                 }
219         }
220         if (fisdir == NULL) {
221                 if (bootverbose)
222                         printf("No RedBoot FIS table located at %lu\n",
223                             (long) offset);
224                 return (NULL);
225         }
226         if (redbcfg != NULL &&
227             fisdir->offset + fisdir->size == redbcfg->offset) {
228                 /*
229                  * Merged FIS/RedBoot config directory.
230                  */
231                 if (bootverbose)
232                         printf("FIS/RedBoot merged at 0x%jx (not yet)\n",
233                             offset + fisdir->offset);
234                 /* XXX */
235         }
236         return head;
237 #undef match
238 }
239
240 static struct g_geom *
241 g_redboot_taste(struct g_class *mp, struct g_provider *pp, int insist)
242 {
243         struct g_geom *gp;
244         struct g_consumer *cp;
245         struct g_redboot_softc *sc;
246         int error, sectorsize, i;
247         struct fis_image_desc *fd, *head;
248         uint32_t offmask;
249         off_t blksize;          /* NB: flash block size stored as stripesize */
250         u_char *buf;
251         off_t offset;
252         const char *value;
253         char *op;
254
255         offset = 0;
256         if (resource_string_value("redboot", 0, "fisoffset", &value) == 0) {
257                 offset = strtouq(value, &op, 0);
258                 if (*op != '\0') {
259                         offset = 0;
260                 }
261         }
262
263         g_trace(G_T_TOPOLOGY, "redboot_taste(%s,%s)", mp->name, pp->name);
264         g_topology_assert();
265         if (!strcmp(pp->geom->class->name, REDBOOT_CLASS_NAME))
266                 return (NULL);
267         /* XXX only taste flash providers */
268         if (strncmp(pp->name, "cfi", 3) && 
269             strncmp(pp->name, "flash/spi", 9))
270                 return (NULL);
271         gp = g_slice_new(mp, REDBOOT_MAXSLICE, pp, &cp, &sc, sizeof(*sc),
272             g_redboot_start);
273         if (gp == NULL)
274                 return (NULL);
275         /* interpose our access method */
276         sc->parent_access = gp->access;
277         gp->access = g_redboot_access;
278
279         sectorsize = cp->provider->sectorsize;
280         blksize = cp->provider->stripesize;
281         if (powerof2(cp->provider->mediasize))
282                 offmask = cp->provider->mediasize-1;
283         else
284                 offmask = 0xffffffff;           /* XXX */
285         if (bootverbose)
286                 printf("%s: mediasize %ld secsize %d blksize %ju offmask 0x%x\n",
287                     __func__, (long) cp->provider->mediasize, sectorsize,
288                     (uintmax_t)blksize, offmask);
289         if (sectorsize < sizeof(struct fis_image_desc) ||
290             (sectorsize % sizeof(struct fis_image_desc)))
291                 return (NULL);
292         g_topology_unlock();
293         head = NULL;
294         if(offset == 0)
295                 offset = cp->provider->mediasize - blksize;
296 again:
297         buf = g_read_data(cp, offset, blksize, NULL);
298         if (buf != NULL)
299                 head = parse_fis_directory(buf, blksize, offset, offmask);
300         if (head == NULL && offset != 0) {
301                 if (buf != NULL)
302                         g_free(buf);
303                 offset = 0;                     /* check the front */
304                 goto again;
305         }
306         g_topology_lock();
307         if (head == NULL) {
308                 if (buf != NULL)
309                         g_free(buf);
310                 return NULL;
311         }
312         /*
313          * Craft a slice for each entry.
314          */
315         for (fd = head, i = 0; fd != NULL; fd = fd->next) {
316                 if (fd->name[0] == '\0')
317                         continue;
318                 error = g_slice_config(gp, i, G_SLICE_CONFIG_SET,
319                     fd->offset, fd->size, sectorsize, "redboot/%s", fd->name);
320                 if (error)
321                         printf("%s: g_slice_config returns %d for \"%s\"\n",
322                             __func__, error, fd->name);
323                 sc->entry[i] = fd->entry;
324                 sc->dsize[i] = fd->dsize;
325                 /* disallow writing hard-to-recover entries */
326                 sc->readonly[i] = (strcmp(fd->name, FISDIR_NAME) == 0) ||
327                                   (strcmp(fd->name, REDBOOT_NAME) == 0);
328                 i++;
329         }
330         g_free(buf);
331         g_access(cp, -1, 0, 0);
332         if (LIST_EMPTY(&gp->provider)) {
333                 g_slice_spoiled(cp);
334                 return (NULL);
335         }
336         return (gp);
337 }
338
339 static void
340 g_redboot_config(struct gctl_req *req, struct g_class *mp, const char *verb)
341 {
342         struct g_geom *gp;
343
344         g_topology_assert();
345         gp = gctl_get_geom(req, mp, "geom");
346         if (gp == NULL)
347                 return;
348         gctl_error(req, "Unknown verb");
349 }
350
351 static struct g_class g_redboot_class   = {
352         .name           = REDBOOT_CLASS_NAME,
353         .version        = G_VERSION,
354         .taste          = g_redboot_taste,
355         .dumpconf       = g_redboot_dumpconf,
356         .ctlreq         = g_redboot_config,
357         .ioctl          = g_redboot_ioctl,
358 };
359 DECLARE_GEOM_CLASS(g_redboot_class, g_redboot);
360 MODULE_VERSION(geom_redboot, 0);