2 * Copyright (c) 2009 Sam Leffler, Errno Consulting
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer,
10 * without modification.
11 * 2. Redistributions in binary form must reproduce at minimum a disclaimer
12 * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
13 * redistribution must be conditioned upon including a substantially
14 * similar Disclaimer requirement for further binary redistribution.
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
20 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
21 * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
22 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
25 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
27 * THE POSSIBILITY OF SUCH DAMAGES.
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD$");
33 #include <sys/param.h>
34 #include <sys/errno.h>
35 #include <sys/endian.h>
36 #include <sys/systm.h>
37 #include <sys/kernel.h>
38 #include <sys/fcntl.h>
39 #include <sys/malloc.h>
42 #include <sys/mutex.h>
45 #include <geom/geom.h>
46 #include <geom/geom_slice.h>
48 #define REDBOOT_CLASS_NAME "REDBOOT"
50 struct fis_image_desc
{
51 uint8_t name
[16]; /* null-terminated name */
52 uint32_t offset
; /* offset in flash */
53 uint32_t addr
; /* address in memory */
54 uint32_t size
; /* image size in bytes */
55 uint32_t entry
; /* offset in image for entry point */
56 uint32_t dsize
; /* data size in bytes */
57 uint8_t pad
[256-(16+7*sizeof(uint32_t)+sizeof(void*))];
58 struct fis_image_desc
*next
; /* linked list (in memory) */
59 uint32_t dsum
; /* descriptor checksum */
60 uint32_t fsum
; /* checksum over image data */
63 #define FISDIR_NAME "FIS directory"
64 #define REDBCFG_NAME "RedBoot config"
65 #define REDBOOT_NAME "RedBoot"
67 #define REDBOOT_MAXSLICE 64
68 #define REDBOOT_MAXOFF \
69 (REDBOOT_MAXSLICE*sizeof(struct fis_image_desc))
71 struct g_redboot_softc
{
72 uint32_t entry
[REDBOOT_MAXSLICE
];
73 uint32_t dsize
[REDBOOT_MAXSLICE
];
74 uint8_t readonly
[REDBOOT_MAXSLICE
];
75 g_access_t
*parent_access
;
79 g_redboot_print(int i
, struct fis_image_desc
*fd
)
82 printf("[%2d] \"%-15.15s\" %08x:%08x", i
, fd
->name
,
83 fd
->offset
, fd
->size
);
84 printf(" addr %08x entry %08x\n", fd
->addr
, fd
->entry
);
85 printf(" dsize 0x%x dsum 0x%x fsum 0x%x\n", fd
->dsize
,
90 g_redboot_ioctl(struct g_provider
*pp
, u_long cmd
, void *data
, int fflag
, struct thread
*td
)
96 g_redboot_access(struct g_provider
*pp
, int dread
, int dwrite
, int dexcl
)
98 struct g_geom
*gp
= pp
->geom
;
99 struct g_slicer
*gsp
= gp
->softc
;
100 struct g_redboot_softc
*sc
= gsp
->softc
;
102 if (dwrite
> 0 && sc
->readonly
[pp
->index
])
104 return (sc
->parent_access(pp
, dread
, dwrite
, dexcl
));
108 g_redboot_start(struct bio
*bp
)
110 struct g_provider
*pp
;
112 struct g_redboot_softc
*sc
;
113 struct g_slicer
*gsp
;
121 if (bp
->bio_cmd
== BIO_GETATTR
) {
122 if (g_handleattr_int(bp
, REDBOOT_CLASS_NAME
"::entry",
125 if (g_handleattr_int(bp
, REDBOOT_CLASS_NAME
"::dsize",
134 g_redboot_dumpconf(struct sbuf
*sb
, const char *indent
, struct g_geom
*gp
,
135 struct g_consumer
*cp __unused
, struct g_provider
*pp
)
137 struct g_redboot_softc
*sc
;
138 struct g_slicer
*gsp
;
142 g_slice_dumpconf(sb
, indent
, gp
, cp
, pp
);
144 if (indent
== NULL
) {
145 sbuf_printf(sb
, " entry %d", sc
->entry
[pp
->index
]);
146 sbuf_printf(sb
, " dsize %d", sc
->dsize
[pp
->index
]);
148 sbuf_printf(sb
, "%s<entry>%d</entry>\n", indent
,
149 sc
->entry
[pp
->index
]);
150 sbuf_printf(sb
, "%s<dsize>%d</dsize>\n", indent
,
151 sc
->dsize
[pp
->index
]);
156 #include <sys/ctype.h>
159 nameok(const char name
[16])
163 /* descriptor names are null-terminated printable ascii */
164 for (i
= 0; i
< 15; i
++)
165 if (!isprint(name
[i
]))
167 return (name
[i
] == '\0');
170 static struct fis_image_desc
*
171 parse_fis_directory(u_char
*buf
, size_t bufsize
, off_t offset
, uint32_t offmask
)
173 #define match(a,b) (bcmp(a, b, sizeof(b)-1) == 0)
174 struct fis_image_desc
*fd
, *efd
;
175 struct fis_image_desc
*fisdir
, *redbcfg
;
176 struct fis_image_desc
*head
, **tail
;
179 fd
= (struct fis_image_desc
*)buf
;
180 efd
= fd
+ (bufsize
/ sizeof(struct fis_image_desc
));
183 * Find the start of the FIS table.
185 while (fd
< efd
&& fd
->name
[0] != 0xff)
190 printf("RedBoot FIS table starts at 0x%jx\n",
191 offset
+ fd
- (struct fis_image_desc
*) buf
);
194 * Scan forward collecting entries in a list.
196 fisdir
= redbcfg
= NULL
;
197 *(tail
= &head
) = NULL
;
198 for (i
= 0; fd
< efd
; i
++, fd
++) {
199 if (fd
->name
[0] == 0xff)
201 if (match(fd
->name
, FISDIR_NAME
))
203 else if (match(fd
->name
, REDBCFG_NAME
))
205 if (nameok(fd
->name
)) {
207 * NB: flash address includes platform mapping;
208 * strip it so we have only a flash offset.
210 fd
->offset
&= offmask
;
212 g_redboot_print(i
, fd
);
214 *(tail
= &fd
->next
) = NULL
;
217 if (fisdir
== NULL
) {
219 printf("No RedBoot FIS table located at %lu\n",
223 if (redbcfg
!= NULL
&&
224 fisdir
->offset
+ fisdir
->size
== redbcfg
->offset
) {
226 * Merged FIS/RedBoot config directory.
229 printf("FIS/RedBoot merged at 0x%jx (not yet)\n",
230 offset
+ fisdir
->offset
);
237 static struct g_geom
*
238 g_redboot_taste(struct g_class
*mp
, struct g_provider
*pp
, int insist
)
241 struct g_consumer
*cp
;
242 struct g_redboot_softc
*sc
;
243 int error
, sectorsize
, i
;
244 struct fis_image_desc
*fd
, *head
;
246 u_int blksize
; /* NB: flash block size stored as stripesize */
250 g_trace(G_T_TOPOLOGY
, "redboot_taste(%s,%s)", mp
->name
, pp
->name
);
252 if (!strcmp(pp
->geom
->class->name
, REDBOOT_CLASS_NAME
))
254 /* XXX only taste flash providers */
255 if (strncmp(pp
->name
, "cfi", 3) &&
256 strncmp(pp
->name
, "flash/spi", 9))
258 gp
= g_slice_new(mp
, REDBOOT_MAXSLICE
, pp
, &cp
, &sc
, sizeof(*sc
),
262 /* interpose our access method */
263 sc
->parent_access
= gp
->access
;
264 gp
->access
= g_redboot_access
;
266 sectorsize
= cp
->provider
->sectorsize
;
267 blksize
= cp
->provider
->stripesize
;
268 if (powerof2(cp
->provider
->mediasize
))
269 offmask
= cp
->provider
->mediasize
-1;
271 offmask
= 0xffffffff; /* XXX */
273 printf("%s: mediasize %ld secsize %d blksize %d offmask 0x%x\n",
274 __func__
, (long) cp
->provider
->mediasize
, sectorsize
,
276 if (sectorsize
< sizeof(struct fis_image_desc
) ||
277 (sectorsize
% sizeof(struct fis_image_desc
)))
281 offset
= cp
->provider
->mediasize
- blksize
;
283 buf
= g_read_data(cp
, offset
, blksize
, NULL
);
285 head
= parse_fis_directory(buf
, blksize
, offset
, offmask
);
286 if (head
== NULL
&& offset
!= 0) {
289 offset
= 0; /* check the front */
299 * Craft a slice for each entry.
301 for (fd
= head
, i
= 0; fd
!= NULL
; fd
= fd
->next
) {
302 if (fd
->name
[0] == '\0')
304 error
= g_slice_config(gp
, i
, G_SLICE_CONFIG_SET
,
305 fd
->offset
, fd
->size
, sectorsize
, "redboot/%s", fd
->name
);
307 printf("%s: g_slice_config returns %d for \"%s\"\n",
308 __func__
, error
, fd
->name
);
309 sc
->entry
[i
] = fd
->entry
;
310 sc
->dsize
[i
] = fd
->dsize
;
311 /* disallow writing hard-to-recover entries */
312 sc
->readonly
[i
] = (strcmp(fd
->name
, FISDIR_NAME
) == 0) ||
313 (strcmp(fd
->name
, REDBOOT_NAME
) == 0);
317 g_access(cp
, -1, 0, 0);
318 if (LIST_EMPTY(&gp
->provider
)) {
326 g_redboot_config(struct gctl_req
*req
, struct g_class
*mp
, const char *verb
)
331 gp
= gctl_get_geom(req
, mp
, "geom");
334 gctl_error(req
, "Unknown verb");
337 static struct g_class g_redboot_class
= {
338 .name
= REDBOOT_CLASS_NAME
,
339 .version
= G_VERSION
,
340 .taste
= g_redboot_taste
,
341 .dumpconf
= g_redboot_dumpconf
,
342 .ctlreq
= g_redboot_config
,
343 .ioctl
= g_redboot_ioctl
,
345 DECLARE_GEOM_CLASS(g_redboot_class
, g_redboot
);