1235778Sgber/*-
2235778Sgber * Copyright (c) 2012 Semihalf
3235778Sgber * Copyright (c) 2009 Jakub Klama <jakub.klama@uj.edu.pl>
4235778Sgber * All rights reserved.
5235778Sgber *
6235778Sgber * Redistribution and use in source and binary forms, with or without
7235778Sgber * modification, are permitted provided that the following conditions
8235778Sgber * are met:
9235778Sgber * 1. Redistributions of source code must retain the above copyright
10235778Sgber *    notice, this list of conditions and the following disclaimer.
11235778Sgber * 2. Redistributions in binary form must reproduce the above copyright
12235778Sgber *    notice, this list of conditions and the following disclaimer in the
13235778Sgber *    documentation and/or other materials provided with the distribution.
14235778Sgber *
15235778Sgber * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16235778Sgber * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17235778Sgber * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18235778Sgber * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19235778Sgber * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20235778Sgber * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21235778Sgber * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22235778Sgber * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23235778Sgber * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24235778Sgber * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25235778Sgber * SUCH DAMAGE.
26235778Sgber */
27235778Sgber
28235778Sgber#include <sys/cdefs.h>
29235778Sgber__FBSDID("$FreeBSD: stable/10/sys/geom/geom_flashmap.c 320168 2017-06-20 19:59:57Z marius $");
30235778Sgber
31235778Sgber#include <sys/param.h>
32235778Sgber#include <sys/systm.h>
33235778Sgber#include <sys/kernel.h>
34235778Sgber#include <sys/malloc.h>
35235778Sgber#include <sys/lock.h>
36235778Sgber#include <sys/mutex.h>
37320168Smarius#include <sys/proc.h>
38235778Sgber#include <sys/slicer.h>
39235778Sgber
40235778Sgber#include <geom/geom.h>
41235778Sgber#include <geom/geom_slice.h>
42235778Sgber#include <geom/geom_disk.h>
43318159Smarius
44235778Sgber#include <dev/nand/nand_dev.h>
45235778Sgber
46318159Smarius#define	FLASHMAP_CLASS_NAME "Flashmap"
47235778Sgber
48235778Sgberstruct g_flashmap_slice {
49235778Sgber	off_t		sl_start;
50235778Sgber	off_t		sl_end;
51235778Sgber	const char	*sl_name;
52235778Sgber
53235778Sgber	STAILQ_ENTRY(g_flashmap_slice) sl_link;
54235778Sgber};
55235778Sgber
56235778SgberSTAILQ_HEAD(g_flashmap_head, g_flashmap_slice);
57235778Sgber
58318159Smariusstatic struct {
59318159Smarius	const char	*type;
60318159Smarius	flash_slicer_t	slicer;
61318159Smarius} g_flashmap_slicers[] = {
62318159Smarius	{ "NAND::device",	NULL },
63318159Smarius	{ "CFI::device",	NULL },
64318159Smarius	{ "SPI::device",	NULL },
65318159Smarius	{ "MMC::device",	NULL }
66318159Smarius};
67235778Sgber
68318159Smariusstatic g_ioctl_t g_flashmap_ioctl;
69318159Smariusstatic g_taste_t g_flashmap_taste;
70318159Smarius
71318159Smariusstatic int g_flashmap_load(device_t dev, struct g_provider *pp,
72318159Smarius    flash_slicer_t slicer, struct g_flashmap_head *head);
73318159Smariusstatic int g_flashmap_modify(struct g_geom *gp, const char *devname,
74318159Smarius    int secsize, struct g_flashmap_head *slices);
75318159Smariusstatic void g_flashmap_print(struct g_flashmap_slice *slice);
76318159Smarius
77235778SgberMALLOC_DECLARE(M_FLASHMAP);
78235778SgberMALLOC_DEFINE(M_FLASHMAP, "geom_flashmap", "GEOM flash memory slicer class");
79235778Sgber
80235778Sgberstatic void
81235778Sgberg_flashmap_print(struct g_flashmap_slice *slice)
82235778Sgber{
83235778Sgber
84235858Sdelphij	printf("%08jx-%08jx: %s (%juKB)\n", (uintmax_t)slice->sl_start,
85235858Sdelphij	    (uintmax_t)slice->sl_end, slice->sl_name,
86235858Sdelphij	    (uintmax_t)(slice->sl_end - slice->sl_start) / 1024);
87235778Sgber}
88235778Sgber
89235778Sgberstatic int
90235778Sgberg_flashmap_modify(struct g_geom *gp, const char *devname, int secsize,
91235778Sgber    struct g_flashmap_head *slices)
92235778Sgber{
93235778Sgber	struct g_flashmap_slice *slice;
94235778Sgber	int i, error;
95235778Sgber
96235778Sgber	g_topology_assert();
97235778Sgber
98235778Sgber	i = 0;
99235778Sgber	STAILQ_FOREACH(slice, slices, sl_link) {
100235778Sgber		if (bootverbose) {
101235778Sgber			printf("%s: slice ", devname);
102235778Sgber			g_flashmap_print(slice);
103235778Sgber		}
104235778Sgber
105235778Sgber		error = g_slice_config(gp, i++, G_SLICE_CONFIG_CHECK,
106235778Sgber		    slice->sl_start,
107235778Sgber		    slice->sl_end - slice->sl_start + 1,
108318159Smarius		    secsize, FLASH_SLICES_FMT, gp->name, slice->sl_name);
109235778Sgber
110235778Sgber		if (error)
111235778Sgber			return (error);
112235778Sgber	}
113235778Sgber
114235778Sgber	i = 0;
115235778Sgber	STAILQ_FOREACH(slice, slices, sl_link) {
116235778Sgber		error = g_slice_config(gp, i++, G_SLICE_CONFIG_SET,
117235778Sgber		    slice->sl_start,
118235778Sgber		    slice->sl_end - slice->sl_start + 1,
119235778Sgber		    secsize, "%ss.%s", gp->name, slice->sl_name);
120235778Sgber
121235778Sgber		if (error)
122235778Sgber			return (error);
123235778Sgber	}
124235778Sgber
125235778Sgber	return (0);
126235778Sgber}
127235778Sgber
128235778Sgberstatic int
129235778Sgberg_flashmap_ioctl(struct g_provider *pp, u_long cmd, void *data, int fflag,
130235778Sgber    struct thread *td)
131235778Sgber{
132235778Sgber	struct g_consumer *cp;
133235778Sgber	struct g_geom *gp;
134235778Sgber
135235778Sgber	if (cmd != NAND_IO_GET_CHIP_PARAM)
136235778Sgber		return (ENOIOCTL);
137235778Sgber
138235778Sgber	cp = LIST_FIRST(&pp->geom->consumer);
139235778Sgber	if (cp == NULL)
140235778Sgber		return (ENOIOCTL);
141235778Sgber	gp = cp->provider->geom;
142235778Sgber	if (gp->ioctl == NULL)
143235778Sgber		return (ENOIOCTL);
144235778Sgber
145235778Sgber	return (gp->ioctl(cp->provider, cmd, data, fflag, td));
146235778Sgber}
147235778Sgber
148235778Sgberstatic struct g_geom *
149235778Sgberg_flashmap_taste(struct g_class *mp, struct g_provider *pp, int flags)
150235778Sgber{
151318159Smarius	struct g_geom *gp;
152235778Sgber	struct g_consumer *cp;
153235778Sgber	struct g_flashmap_head head;
154235778Sgber	struct g_flashmap_slice *slice, *slice_temp;
155318159Smarius	flash_slicer_t slicer;
156235778Sgber	device_t dev;
157318159Smarius	int i, size;
158235778Sgber
159235778Sgber	g_trace(G_T_TOPOLOGY, "flashmap_taste(%s,%s)", mp->name, pp->name);
160235778Sgber	g_topology_assert();
161235778Sgber
162235778Sgber	if (flags == G_TF_NORMAL &&
163249556Sbrooks	    strcmp(pp->geom->class->name, G_DISK_CLASS_NAME) != 0)
164235778Sgber		return (NULL);
165235778Sgber
166318159Smarius	gp = g_slice_new(mp, FLASH_SLICES_MAX_NUM, pp, &cp, NULL, 0, NULL);
167235778Sgber	if (gp == NULL)
168235778Sgber		return (NULL);
169235778Sgber
170235778Sgber	STAILQ_INIT(&head);
171235778Sgber
172235778Sgber	do {
173318159Smarius		slicer = NULL;
174318159Smarius		for (i = 0; i < nitems(g_flashmap_slicers); i++) {
175251117Sbrooks			size = sizeof(device_t);
176318159Smarius			if (g_io_getattr(g_flashmap_slicers[i].type, cp,
177318159Smarius			    &size, &dev) == 0) {
178318159Smarius				slicer = g_flashmap_slicers[i].slicer;
179251117Sbrooks				break;
180318159Smarius			}
181251117Sbrooks		}
182318159Smarius		if (slicer == NULL)
183318159Smarius			break;
184235778Sgber
185318159Smarius		if (g_flashmap_load(dev, pp, slicer, &head) == 0)
186235778Sgber			break;
187235778Sgber
188235778Sgber		g_flashmap_modify(gp, cp->provider->name,
189235778Sgber		    cp->provider->sectorsize, &head);
190235778Sgber	} while (0);
191235778Sgber
192235778Sgber	g_access(cp, -1, 0, 0);
193235778Sgber
194318159Smarius	STAILQ_FOREACH_SAFE(slice, &head, sl_link, slice_temp)
195235778Sgber		free(slice, M_FLASHMAP);
196235778Sgber
197235778Sgber	if (LIST_EMPTY(&gp->provider)) {
198235778Sgber		g_slice_spoiled(cp);
199235778Sgber		return (NULL);
200235778Sgber	}
201235778Sgber	return (gp);
202235778Sgber}
203235778Sgber
204235778Sgberstatic int
205318159Smariusg_flashmap_load(device_t dev, struct g_provider *pp, flash_slicer_t slicer,
206318159Smarius    struct g_flashmap_head *head)
207235778Sgber{
208235778Sgber	struct flash_slice *slices;
209235778Sgber	struct g_flashmap_slice *slice;
210318159Smarius	int i, nslices = 0;
211235778Sgber
212318159Smarius	slices = malloc(sizeof(struct flash_slice) * FLASH_SLICES_MAX_NUM,
213318159Smarius	    M_FLASHMAP, M_WAITOK | M_ZERO);
214318159Smarius	if (slicer(dev, pp->name, slices, &nslices) == 0) {
215235778Sgber		for (i = 0; i < nslices; i++) {
216235778Sgber			slice = malloc(sizeof(struct g_flashmap_slice),
217235778Sgber			    M_FLASHMAP, M_WAITOK);
218235778Sgber
219235778Sgber			slice->sl_name = slices[i].label;
220235778Sgber			slice->sl_start = slices[i].base;
221235778Sgber			slice->sl_end = slices[i].base + slices[i].size - 1;
222235778Sgber
223235778Sgber			STAILQ_INSERT_TAIL(head, slice, sl_link);
224235778Sgber		}
225235778Sgber	}
226235778Sgber
227235778Sgber	free(slices, M_FLASHMAP);
228235778Sgber	return (nslices);
229235778Sgber}
230235778Sgber
231318159Smariusvoid flash_register_slicer(flash_slicer_t slicer, u_int type, bool force)
232318159Smarius{
233318159Smarius
234320168Smarius	DROP_GIANT();
235318159Smarius	g_topology_lock();
236318159Smarius	if (g_flashmap_slicers[type].slicer == NULL || force == TRUE)
237318159Smarius		g_flashmap_slicers[type].slicer = slicer;
238318159Smarius	g_topology_unlock();
239320168Smarius	PICKUP_GIANT();
240318159Smarius}
241318159Smarius
242235778Sgberstatic struct g_class g_flashmap_class = {
243235778Sgber	.name = FLASHMAP_CLASS_NAME,
244235778Sgber	.version = G_VERSION,
245235778Sgber	.taste = g_flashmap_taste,
246235778Sgber	.ioctl = g_flashmap_ioctl,
247235778Sgber};
248235778Sgber
249235778SgberDECLARE_GEOM_CLASS(g_flashmap_class, g_flashmap);
250318159SmariusMODULE_VERSION(g_flashmap, 0);
251