geom_flashmap.c revision 251117
1/*-
2 * Copyright (c) 2012 Semihalf
3 * Copyright (c) 2009 Jakub Klama <jakub.klama@uj.edu.pl>
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 */
27
28#include <sys/cdefs.h>
29__FBSDID("$FreeBSD: head/sys/geom/geom_flashmap.c 251117 2013-05-30 01:19:02Z brooks $");
30
31#include <sys/param.h>
32#include <sys/endian.h>
33#include <sys/systm.h>
34#include <sys/kernel.h>
35#include <sys/fcntl.h>
36#include <sys/malloc.h>
37#include <sys/bio.h>
38#include <sys/bus.h>
39#include <sys/lock.h>
40#include <sys/mutex.h>
41#include <sys/slicer.h>
42
43#include <geom/geom.h>
44#include <geom/geom_slice.h>
45#include <geom/geom_disk.h>
46#include <dev/nand/nand_dev.h>
47
48#define FLASHMAP_CLASS_NAME "Flashmap"
49
50struct g_flashmap_slice {
51	off_t		sl_start;
52	off_t		sl_end;
53	const char	*sl_name;
54
55	STAILQ_ENTRY(g_flashmap_slice) sl_link;
56};
57
58STAILQ_HEAD(g_flashmap_head, g_flashmap_slice);
59
60static void g_flashmap_print(struct g_flashmap_slice *);
61static int g_flashmap_modify(struct g_geom *, const char *,
62    int, struct g_flashmap_head *);
63static int g_flashmap_start(struct bio *);
64static int g_flashmap_ioctl(struct g_provider *, u_long, void *,
65    int, struct thread *);
66static void g_flashmap_dumpconf(struct sbuf *, const char *,
67    struct g_geom *, struct g_consumer *, struct g_provider *);
68static struct g_geom *g_flashmap_taste(struct g_class *,
69    struct g_provider *, int);
70static void g_flashmap_config(struct gctl_req *, struct g_class *,
71    const char *);
72static int g_flashmap_load(device_t, struct g_flashmap_head *);
73
74MALLOC_DECLARE(M_FLASHMAP);
75MALLOC_DEFINE(M_FLASHMAP, "geom_flashmap", "GEOM flash memory slicer class");
76
77static void
78g_flashmap_print(struct g_flashmap_slice *slice)
79{
80
81	printf("%08jx-%08jx: %s (%juKB)\n", (uintmax_t)slice->sl_start,
82	    (uintmax_t)slice->sl_end, slice->sl_name,
83	    (uintmax_t)(slice->sl_end - slice->sl_start) / 1024);
84}
85
86static int
87g_flashmap_modify(struct g_geom *gp, const char *devname, int secsize,
88    struct g_flashmap_head *slices)
89{
90	struct g_flashmap_slice *slice;
91	int i, error;
92
93	g_topology_assert();
94
95	i = 0;
96	STAILQ_FOREACH(slice, slices, sl_link) {
97		if (bootverbose) {
98			printf("%s: slice ", devname);
99			g_flashmap_print(slice);
100		}
101
102		error = g_slice_config(gp, i++, G_SLICE_CONFIG_CHECK,
103		    slice->sl_start,
104		    slice->sl_end - slice->sl_start + 1,
105		    secsize, "%ss.%s", gp->name, slice->sl_name);
106
107		if (error)
108			return (error);
109	}
110
111	i = 0;
112	STAILQ_FOREACH(slice, slices, sl_link) {
113		error = g_slice_config(gp, i++, G_SLICE_CONFIG_SET,
114		    slice->sl_start,
115		    slice->sl_end - slice->sl_start + 1,
116		    secsize, "%ss.%s", gp->name, slice->sl_name);
117
118		if (error)
119			return (error);
120	}
121
122	return (0);
123}
124
125static int
126g_flashmap_start(struct bio *bp)
127{
128
129	return (0);
130}
131
132static void
133g_flashmap_dumpconf(struct sbuf *sb, const char *indent, struct g_geom *gp,
134    struct g_consumer *cp __unused, struct g_provider *pp)
135{
136	struct g_slicer *gsp;
137
138	gsp = gp->softc;
139	g_slice_dumpconf(sb, indent, gp, cp, pp);
140}
141
142static int
143g_flashmap_ioctl(struct g_provider *pp, u_long cmd, void *data, int fflag,
144    struct thread *td)
145{
146	struct g_consumer *cp;
147	struct g_geom *gp;
148
149	if (cmd != NAND_IO_GET_CHIP_PARAM)
150		return (ENOIOCTL);
151
152	cp = LIST_FIRST(&pp->geom->consumer);
153	if (cp == NULL)
154		return (ENOIOCTL);
155	gp = cp->provider->geom;
156	if (gp->ioctl == NULL)
157		return (ENOIOCTL);
158
159	return (gp->ioctl(cp->provider, cmd, data, fflag, td));
160}
161
162
163static struct g_geom *
164g_flashmap_taste(struct g_class *mp, struct g_provider *pp, int flags)
165{
166	struct g_geom *gp = NULL;
167	struct g_consumer *cp;
168	struct g_flashmap_head head;
169	struct g_flashmap_slice *slice, *slice_temp;
170	device_t dev;
171	int nslices, size;
172
173	g_trace(G_T_TOPOLOGY, "flashmap_taste(%s,%s)", mp->name, pp->name);
174	g_topology_assert();
175
176	if (flags == G_TF_NORMAL &&
177	    strcmp(pp->geom->class->name, G_DISK_CLASS_NAME) != 0)
178		return (NULL);
179
180	gp = g_slice_new(mp, FLASH_SLICES_MAX_NUM, pp, &cp, NULL, 0,
181	    g_flashmap_start);
182	if (gp == NULL)
183		return (NULL);
184
185	STAILQ_INIT(&head);
186
187	do {
188		size = sizeof(device_t);
189		if (g_io_getattr("NAND::device", cp, &size, &dev)) {
190			size = sizeof(device_t);
191			if (g_io_getattr("CFI::device", cp, &size, &dev))
192				break;
193		}
194
195		nslices = g_flashmap_load(dev, &head);
196		if (nslices == 0)
197			break;
198
199		g_flashmap_modify(gp, cp->provider->name,
200		    cp->provider->sectorsize, &head);
201	} while (0);
202
203	g_access(cp, -1, 0, 0);
204
205	STAILQ_FOREACH_SAFE(slice, &head, sl_link, slice_temp) {
206		free(slice, M_FLASHMAP);
207	}
208
209	if (LIST_EMPTY(&gp->provider)) {
210		g_slice_spoiled(cp);
211		return (NULL);
212	}
213	return (gp);
214}
215
216static void
217g_flashmap_config(struct gctl_req *req, struct g_class *mp, const char *verb)
218{
219
220	gctl_error(req, "unknown config verb");
221}
222
223static int
224g_flashmap_load(device_t dev, struct g_flashmap_head *head)
225{
226	struct flash_slice *slices;
227	struct g_flashmap_slice *slice;
228	uint32_t i, buf_size;
229	int nslices = 0;
230
231	buf_size = sizeof(struct flash_slice) * FLASH_SLICES_MAX_NUM;
232	slices = malloc(buf_size, M_FLASHMAP, M_WAITOK | M_ZERO);
233	if (flash_fill_slices(dev, slices, &nslices) == 0) {
234		for (i = 0; i < nslices; i++) {
235			slice = malloc(sizeof(struct g_flashmap_slice),
236			    M_FLASHMAP, M_WAITOK);
237
238			slice->sl_name = slices[i].label;
239			slice->sl_start = slices[i].base;
240			slice->sl_end = slices[i].base + slices[i].size - 1;
241
242			STAILQ_INSERT_TAIL(head, slice, sl_link);
243		}
244	}
245
246	free(slices, M_FLASHMAP);
247	return (nslices);
248}
249
250static struct g_class g_flashmap_class = {
251	.name = FLASHMAP_CLASS_NAME,
252	.version = G_VERSION,
253	.taste = g_flashmap_taste,
254	.dumpconf = g_flashmap_dumpconf,
255	.ioctl = g_flashmap_ioctl,
256	.ctlreq = g_flashmap_config,
257};
258
259DECLARE_GEOM_CLASS(g_flashmap_class, g_flashmap);
260