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