1220559Sadrian/*-
2220559Sadrian * Copyright (c) 2010-2011 Aleksandr Rybalko <ray@dlink.ua>
3220559Sadrian *   based on geom_redboot.c
4220559Sadrian * Copyright (c) 2009 Sam Leffler, Errno Consulting
5220559Sadrian * All rights reserved.
6220559Sadrian *
7220559Sadrian * Redistribution and use in source and binary forms, with or without
8220559Sadrian * modification, are permitted provided that the following conditions
9220559Sadrian * are met:
10220559Sadrian * 1. Redistributions of source code must retain the above copyright
11220559Sadrian *    notice, this list of conditions and the following disclaimer,
12220559Sadrian *    without modification.
13220559Sadrian * 2. Redistributions in binary form must reproduce at minimum a disclaimer
14220559Sadrian *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
15220559Sadrian *    redistribution must be conditioned upon including a substantially
16220559Sadrian *    similar Disclaimer requirement for further binary redistribution.
17220559Sadrian *
18220559Sadrian * NO WARRANTY
19220559Sadrian * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20220559Sadrian * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21220559Sadrian * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
22220559Sadrian * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
23220559Sadrian * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
24220559Sadrian * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25220559Sadrian * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26220559Sadrian * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
27220559Sadrian * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28220559Sadrian * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
29220559Sadrian * THE POSSIBILITY OF SUCH DAMAGES.
30220559Sadrian */
31220559Sadrian
32220559Sadrian#include <sys/cdefs.h>
33220559Sadrian__FBSDID("$FreeBSD$");
34220559Sadrian
35220559Sadrian#include <sys/param.h>
36220559Sadrian#include <sys/bus.h>
37220559Sadrian#include <sys/errno.h>
38220559Sadrian#include <sys/endian.h>
39220559Sadrian#include <sys/systm.h>
40220559Sadrian#include <sys/kernel.h>
41220559Sadrian#include <sys/fcntl.h>
42220559Sadrian#include <sys/malloc.h>
43220559Sadrian#include <sys/bio.h>
44220559Sadrian#include <sys/lock.h>
45220559Sadrian#include <sys/mutex.h>
46221500Sadrian#include <sys/sbuf.h>
47220559Sadrian
48220559Sadrian#include <geom/geom.h>
49220559Sadrian#include <geom/geom_slice.h>
50220559Sadrian
51221500Sadrian#define	MAP_CLASS_NAME	"MAP"
52220559Sadrian#define	MAP_MAXSLICE	64
53221500Sadrian#define	MAP_MAX_MARKER_LEN	64
54220559Sadrian
55220559Sadrianstruct g_map_softc {
56221500Sadrian	off_t		 offset[MAP_MAXSLICE];	/* offset in flash */
57221500Sadrian	off_t		 size[MAP_MAXSLICE];	/* image size in bytes */
58221500Sadrian	off_t		 entry[MAP_MAXSLICE];
59221500Sadrian	off_t		 dsize[MAP_MAXSLICE];
60221500Sadrian	uint8_t		 readonly[MAP_MAXSLICE];
61221500Sadrian	g_access_t	*parent_access;
62220559Sadrian};
63220559Sadrian
64220559Sadrianstatic int
65220559Sadriang_map_access(struct g_provider *pp, int dread, int dwrite, int dexcl)
66220559Sadrian{
67221500Sadrian	struct g_geom *gp;
68221500Sadrian	struct g_slicer *gsp;
69221500Sadrian	struct g_map_softc *sc;
70220559Sadrian
71221500Sadrian	gp = pp->geom;
72221500Sadrian	gsp = gp->softc;
73221500Sadrian	sc = gsp->softc;
74221500Sadrian
75220559Sadrian	if (dwrite > 0 && sc->readonly[pp->index])
76220559Sadrian		return (EPERM);
77221500Sadrian
78220559Sadrian	return (sc->parent_access(pp, dread, dwrite, dexcl));
79220559Sadrian}
80220559Sadrian
81220559Sadrianstatic int
82220559Sadriang_map_start(struct bio *bp)
83220559Sadrian{
84220559Sadrian	struct g_provider *pp;
85221500Sadrian	struct g_geom *gp;
86220559Sadrian	struct g_map_softc *sc;
87220559Sadrian	struct g_slicer *gsp;
88221500Sadrian	int idx;
89220559Sadrian
90220559Sadrian	pp = bp->bio_to;
91220559Sadrian	idx = pp->index;
92220559Sadrian	gp = pp->geom;
93220559Sadrian	gsp = gp->softc;
94220559Sadrian	sc = gsp->softc;
95221500Sadrian
96220559Sadrian	if (bp->bio_cmd == BIO_GETATTR) {
97220559Sadrian		if (g_handleattr_int(bp, MAP_CLASS_NAME "::entry",
98221500Sadrian		    sc->entry[idx])) {
99220559Sadrian			return (1);
100221500Sadrian		}
101220559Sadrian		if (g_handleattr_int(bp, MAP_CLASS_NAME "::dsize",
102221500Sadrian		    sc->dsize[idx])) {
103220559Sadrian			return (1);
104221500Sadrian		}
105220559Sadrian	}
106221500Sadrian
107220559Sadrian	return (0);
108220559Sadrian}
109220559Sadrian
110220559Sadrianstatic void
111220559Sadriang_map_dumpconf(struct sbuf *sb, const char *indent, struct g_geom *gp,
112221500Sadrian    struct g_consumer *cp __unused, struct g_provider *pp)
113220559Sadrian{
114220559Sadrian	struct g_map_softc *sc;
115220559Sadrian	struct g_slicer *gsp;
116220559Sadrian
117220559Sadrian	gsp = gp->softc;
118220559Sadrian	sc = gsp->softc;
119220559Sadrian	g_slice_dumpconf(sb, indent, gp, cp, pp);
120220559Sadrian	if (pp != NULL) {
121220559Sadrian		if (indent == NULL) {
122221500Sadrian			sbuf_printf(sb, " entry %lld", sc->entry[pp->index]);
123221500Sadrian			sbuf_printf(sb, " dsize %lld", sc->dsize[pp->index]);
124220559Sadrian		} else {
125221500Sadrian			sbuf_printf(sb, "%s<entry>%lld</entry>\n", indent,
126221500Sadrian			    sc->entry[pp->index]);
127221500Sadrian			sbuf_printf(sb, "%s<dsize>%lld</dsize>\n", indent,
128221500Sadrian			    sc->dsize[pp->index]);
129220559Sadrian		}
130220559Sadrian	}
131220559Sadrian}
132220559Sadrian
133221500Sadrianstatic int
134221500Sadrianfind_marker(struct g_consumer *cp, const char *line, off_t *offset)
135220559Sadrian{
136221500Sadrian	off_t search_start, search_offset, search_step;
137221500Sadrian	size_t sectorsize;
138221500Sadrian	uint8_t *buf;
139221500Sadrian	char *op, key[MAP_MAX_MARKER_LEN], search_key[MAP_MAX_MARKER_LEN];
140221500Sadrian	int ret, c;
141220559Sadrian
142221500Sadrian	/* Try convert to numeric first */
143221500Sadrian	*offset = strtouq(line, &op, 0);
144221500Sadrian	if (*op == '\0')
145221500Sadrian		return (0);
146220559Sadrian
147221500Sadrian	bzero(search_key, MAP_MAX_MARKER_LEN);
148221500Sadrian	sectorsize = cp->provider->sectorsize;
149220559Sadrian
150221500Sadrian	ret = sscanf(line, "search:%qi:%qi:%63c",
151221500Sadrian	    &search_start, &search_step, search_key);
152221500Sadrian	if (ret < 3)
153221500Sadrian		return (1);
154220559Sadrian
155221500Sadrian	if (bootverbose) {
156221500Sadrian		printf("MAP: search key \"%s\" from 0x%llx, step 0x%llx\n",
157221500Sadrian		    search_key, search_start, search_step);
158221500Sadrian	}
159220559Sadrian
160221500Sadrian	/* error if search_key is empty */
161221500Sadrian	if (strlen(search_key) < 1)
162221500Sadrian		return (1);
163220559Sadrian
164221500Sadrian	/* sscanf successful, and we start marker search */
165221500Sadrian	for (search_offset = search_start;
166221500Sadrian	     search_offset < cp->provider->mediasize;
167221500Sadrian	     search_offset += search_step) {
168220559Sadrian
169221500Sadrian		g_topology_unlock();
170221500Sadrian		buf = g_read_data(cp, rounddown(search_offset, sectorsize),
171221500Sadrian		    roundup(strlen(search_key), sectorsize), NULL);
172221500Sadrian		g_topology_lock();
173220559Sadrian
174221500Sadrian		/* Wildcard, replace '.' with byte from data */
175221500Sadrian		/* TODO: add support wildcard escape '\.' */
176220559Sadrian
177221500Sadrian		strncpy(key, search_key, MAP_MAX_MARKER_LEN);
178220559Sadrian
179221500Sadrian		for (c = 0; c < MAP_MAX_MARKER_LEN && key[c]; c++) {
180221500Sadrian			if (key[c] == '.') {
181221500Sadrian				key[c] = ((char *)(buf +
182221500Sadrian				    (search_offset % sectorsize)))[c];
183221500Sadrian			}
184221500Sadrian		}
185220559Sadrian
186221500Sadrian		if (buf != NULL && strncmp(buf + search_offset % sectorsize,
187221500Sadrian		    key, strlen(search_key)) == 0) {
188221500Sadrian			g_free(buf);
189221500Sadrian			/* Marker found, so return their offset */
190221500Sadrian			*offset = search_offset;
191221500Sadrian			return (0);
192221500Sadrian		}
193221500Sadrian		g_free(buf);
194221500Sadrian	}
195220559Sadrian
196221500Sadrian	/* Marker not found */
197221500Sadrian	return (1);
198221500Sadrian}
199220559Sadrian
200221500Sadrianstatic int
201221500Sadriang_map_parse_part(struct g_class *mp, struct g_provider *pp,
202221500Sadrian    struct g_consumer *cp, struct g_geom *gp, struct g_map_softc *sc, int i)
203221500Sadrian{
204221500Sadrian	const char *value, *name;
205221500Sadrian	char *op;
206221500Sadrian	off_t start, end, offset, size, dsize;
207221500Sadrian	int readonly, ret;
208220559Sadrian
209221500Sadrian	/* hint.map.0.at="cfid0" - bind to cfid0 media */
210221500Sadrian	if (resource_string_value("map", i, "at", &value) != 0)
211221500Sadrian		return (1);
212220559Sadrian
213221500Sadrian	/* Check if this correct provider */
214221500Sadrian	if (strcmp(pp->name, value) != 0)
215221500Sadrian		return (1);
216220559Sadrian
217221500Sadrian	/*
218221500Sadrian	 * hint.map.0.name="uboot" - name of partition, will be available
219221500Sadrian	 * as "/dev/map/uboot"
220221500Sadrian	 */
221221500Sadrian	if (resource_string_value("map", i, "name", &name) != 0) {
222221500Sadrian		if (bootverbose)
223221500Sadrian			printf("MAP: hint.map.%d has no name\n", i);
224221500Sadrian		return (1);
225221500Sadrian	}
226220559Sadrian
227221500Sadrian	/*
228221500Sadrian	 * hint.map.0.start="0x00010000" - partition start at 0x00010000
229221500Sadrian	 * or hint.map.0.start="search:0x00010000:0x200:marker text" -
230221500Sadrian	 * search for text "marker text", begin at 0x10000, step 0x200
231221500Sadrian	 * until we found marker or end of media reached
232221500Sadrian	 */
233221500Sadrian	if (resource_string_value("map", i, "start", &value) != 0) {
234221500Sadrian		if (bootverbose)
235221500Sadrian			printf("MAP: \"%s\" has no start value\n", name);
236221500Sadrian		return (1);
237221500Sadrian	}
238221500Sadrian	if (find_marker(cp, value, &start) != 0) {
239221500Sadrian		if (bootverbose) {
240221500Sadrian			printf("MAP: \"%s\" can't parse/use start value\n",
241221500Sadrian			    name);
242221500Sadrian		}
243221500Sadrian		return (1);
244221500Sadrian	}
245220559Sadrian
246221500Sadrian	/* like "start" */
247221500Sadrian	if (resource_string_value("map", i, "end", &value) != 0) {
248221500Sadrian		if (bootverbose)
249221500Sadrian			printf("MAP: \"%s\" has no end value\n", name);
250221500Sadrian		return (1);
251221500Sadrian	}
252221500Sadrian	if (find_marker(cp, value, &end) != 0) {
253221500Sadrian		if (bootverbose) {
254221500Sadrian			printf("MAP: \"%s\" can't parse/use start value\n",
255221500Sadrian			    name);
256221500Sadrian		}
257221500Sadrian		return (1);
258221500Sadrian	}
259220559Sadrian
260221500Sadrian	/* variable readonly optional, disable write access */
261221500Sadrian	if (resource_int_value("map", i, "readonly", &readonly) != 0)
262221500Sadrian		readonly = 0;
263220559Sadrian
264221500Sadrian	/* offset of partition data, from partition begin */
265221500Sadrian	if (resource_string_value("map", i, "offset", &value) == 0) {
266221500Sadrian		offset = strtouq(value, &op, 0);
267221500Sadrian		if (*op != '\0') {
268221500Sadrian			if (bootverbose) {
269221500Sadrian				printf("MAP: \"%s\" can't parse offset\n",
270221500Sadrian				    name);
271220559Sadrian			}
272221500Sadrian			return (1);
273220559Sadrian		}
274221500Sadrian	} else {
275221500Sadrian		offset = 0;
276221500Sadrian	}
277220559Sadrian
278221500Sadrian	/* partition data size */
279221500Sadrian	if (resource_string_value("map", i, "dsize", &value) == 0) {
280221500Sadrian		dsize = strtouq(value, &op, 0);
281221500Sadrian		if (*op != '\0') {
282221500Sadrian			if (bootverbose) {
283221500Sadrian				printf("MAP: \"%s\" can't parse dsize\n",
284221500Sadrian				    name);
285221500Sadrian			}
286221500Sadrian			return (1);
287221500Sadrian		}
288221500Sadrian	} else {
289221500Sadrian		dsize = 0;
290221500Sadrian	}
291220559Sadrian
292221500Sadrian	size = end - start;
293221500Sadrian	if (dsize == 0)
294221500Sadrian		dsize = size - offset;
295220559Sadrian
296221500Sadrian	/* end is 0 or size is 0, No MAP - so next */
297221500Sadrian	if (end < start) {
298221500Sadrian		if (bootverbose) {
299221500Sadrian			printf("MAP: \"%s\", \"end\" less than "
300221500Sadrian			    "\"start\"\n", name);
301221500Sadrian		}
302221500Sadrian		return (1);
303221500Sadrian	}
304220559Sadrian
305221500Sadrian	if (offset + dsize > size) {
306221500Sadrian		if (bootverbose) {
307221500Sadrian			printf("MAP: \"%s\", \"dsize\" bigger than "
308221500Sadrian			    "partition - offset\n", name);
309221500Sadrian		}
310221500Sadrian		return (1);
311221500Sadrian	}
312220559Sadrian
313221500Sadrian	ret = g_slice_config(gp, i, G_SLICE_CONFIG_SET, start + offset,
314221500Sadrian	    dsize, cp->provider->sectorsize, "map/%s", name);
315221500Sadrian	if (ret != 0) {
316221500Sadrian		if (bootverbose) {
317221500Sadrian			printf("MAP: g_slice_config returns %d for \"%s\"\n",
318221500Sadrian			    ret, name);
319221500Sadrian		}
320221500Sadrian		return (1);
321220559Sadrian	}
322220559Sadrian
323221500Sadrian	if (bootverbose) {
324221500Sadrian		printf("MAP: %llxx%llx, data=%llxx%llx "
325221500Sadrian		    "\"/dev/map/%s\"\n",
326221500Sadrian		    start, size, offset, dsize, name);
327221500Sadrian	}
328221500Sadrian
329221500Sadrian	sc->offset[i] = start;
330221500Sadrian	sc->size[i] = size;
331221500Sadrian	sc->entry[i] = offset;
332221500Sadrian	sc->dsize[i] = dsize;
333221500Sadrian	sc->readonly[i] = readonly ? 1 : 0;
334221500Sadrian
335221500Sadrian	return (0);
336221500Sadrian}
337221500Sadrian
338221500Sadrianstatic struct g_geom *
339221500Sadriang_map_taste(struct g_class *mp, struct g_provider *pp, int insist __unused)
340221500Sadrian{
341221500Sadrian	struct g_map_softc *sc;
342221500Sadrian	struct g_consumer *cp;
343221500Sadrian	struct g_geom *gp;
344221500Sadrian	int i;
345221500Sadrian
346221500Sadrian	g_trace(G_T_TOPOLOGY, "map_taste(%s,%s)", mp->name, pp->name);
347221500Sadrian	g_topology_assert();
348221500Sadrian	if (strcmp(pp->geom->class->name, MAP_CLASS_NAME) == 0)
349220559Sadrian		return (NULL);
350220559Sadrian
351221500Sadrian	gp = g_slice_new(mp, MAP_MAXSLICE, pp, &cp, &sc, sizeof(*sc),
352221500Sadrian	    g_map_start);
353221500Sadrian	if (gp == NULL)
354221500Sadrian		return (NULL);
355221500Sadrian
356221500Sadrian	/* interpose our access method */
357221500Sadrian	sc->parent_access = gp->access;
358221500Sadrian	gp->access = g_map_access;
359221500Sadrian
360221500Sadrian	for (i = 0; i < MAP_MAXSLICE; i++)
361221500Sadrian		g_map_parse_part(mp, pp, cp, gp, sc, i);
362221500Sadrian
363221500Sadrian
364220559Sadrian	g_access(cp, -1, 0, 0);
365220559Sadrian	if (LIST_EMPTY(&gp->provider)) {
366221500Sadrian		if (bootverbose)
367221500Sadrian			printf("MAP: No valid partition found at %s\n", pp->name);
368220559Sadrian		g_slice_spoiled(cp);
369220559Sadrian		return (NULL);
370220559Sadrian	}
371220559Sadrian	return (gp);
372220559Sadrian}
373220559Sadrian
374220559Sadrianstatic void
375220559Sadriang_map_config(struct gctl_req *req, struct g_class *mp, const char *verb)
376220559Sadrian{
377221500Sadrian	struct g_geom *gp;
378220559Sadrian
379220559Sadrian	g_topology_assert();
380220559Sadrian	gp = gctl_get_geom(req, mp, "geom");
381220559Sadrian	if (gp == NULL)
382220559Sadrian		return;
383220559Sadrian	gctl_error(req, "Unknown verb");
384220559Sadrian}
385220559Sadrian
386220559Sadrianstatic struct g_class g_map_class = {
387220559Sadrian	.name = MAP_CLASS_NAME,
388220559Sadrian	.version = G_VERSION,
389220559Sadrian	.taste = g_map_taste,
390220559Sadrian	.dumpconf = g_map_dumpconf,
391220559Sadrian	.ctlreq = g_map_config,
392220559Sadrian};
393220559SadrianDECLARE_GEOM_CLASS(g_map_class, g_map);
394