xref: /freebsd/sys/dev/cfi/cfi_dev.c (revision f43581345bad430bbb7fc164d577edee19aac124)
1e00251b7SMarcel Moolenaar /*-
2e00251b7SMarcel Moolenaar  * Copyright (c) 2007, Juniper Networks, Inc.
3*f4358134SBrooks Davis  * Copyright (c) 2012-2013, SRI International
4e00251b7SMarcel Moolenaar  * All rights reserved.
5e00251b7SMarcel Moolenaar  *
6*f4358134SBrooks Davis  * Portions of this software were developed by SRI International and the
7*f4358134SBrooks Davis  * University of Cambridge Computer Laboratory under DARPA/AFRL contract
8*f4358134SBrooks Davis  * (FA8750-10-C-0237) ("CTSRD"), as part of the DARPA CRASH research
9*f4358134SBrooks Davis  * programme.
10*f4358134SBrooks Davis  *
11e00251b7SMarcel Moolenaar  * Redistribution and use in source and binary forms, with or without
12e00251b7SMarcel Moolenaar  * modification, are permitted provided that the following conditions
13e00251b7SMarcel Moolenaar  * are met:
14e00251b7SMarcel Moolenaar  * 1. Redistributions of source code must retain the above copyright
15e00251b7SMarcel Moolenaar  *    notice, this list of conditions and the following disclaimer.
16e00251b7SMarcel Moolenaar  * 2. Redistributions in binary form must reproduce the above copyright
17e00251b7SMarcel Moolenaar  *    notice, this list of conditions and the following disclaimer in the
18e00251b7SMarcel Moolenaar  *    documentation and/or other materials provided with the distribution.
19e00251b7SMarcel Moolenaar  * 3. Neither the name of the author nor the names of any co-contributors
20e00251b7SMarcel Moolenaar  *    may be used to endorse or promote products derived from this software
21e00251b7SMarcel Moolenaar  *    without specific prior written permission.
22e00251b7SMarcel Moolenaar  *
23e00251b7SMarcel Moolenaar  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
24e00251b7SMarcel Moolenaar  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
25e00251b7SMarcel Moolenaar  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
26e00251b7SMarcel Moolenaar  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
27e00251b7SMarcel Moolenaar  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
28e00251b7SMarcel Moolenaar  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
29e00251b7SMarcel Moolenaar  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
30e00251b7SMarcel Moolenaar  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
31e00251b7SMarcel Moolenaar  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32e00251b7SMarcel Moolenaar  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33e00251b7SMarcel Moolenaar  * SUCH DAMAGE.
34e00251b7SMarcel Moolenaar  */
35e00251b7SMarcel Moolenaar 
36e00251b7SMarcel Moolenaar #include <sys/cdefs.h>
37e00251b7SMarcel Moolenaar __FBSDID("$FreeBSD$");
38e00251b7SMarcel Moolenaar 
3963425a7fSSam Leffler #include "opt_cfi.h"
4063425a7fSSam Leffler 
41e00251b7SMarcel Moolenaar #include <sys/param.h>
42e00251b7SMarcel Moolenaar #include <sys/systm.h>
43e00251b7SMarcel Moolenaar #include <sys/bus.h>
44e00251b7SMarcel Moolenaar #include <sys/conf.h>
45e00251b7SMarcel Moolenaar #include <sys/ioccom.h>
46e00251b7SMarcel Moolenaar #include <sys/kernel.h>
47e00251b7SMarcel Moolenaar #include <sys/malloc.h>
48e00251b7SMarcel Moolenaar #include <sys/proc.h>
49e00251b7SMarcel Moolenaar #include <sys/sysctl.h>
50e00251b7SMarcel Moolenaar #include <sys/types.h>
51e00251b7SMarcel Moolenaar #include <sys/uio.h>
52e00251b7SMarcel Moolenaar 
53e00251b7SMarcel Moolenaar #include <sys/cfictl.h>
54e00251b7SMarcel Moolenaar 
55e00251b7SMarcel Moolenaar #include <machine/atomic.h>
56e00251b7SMarcel Moolenaar #include <machine/bus.h>
57e00251b7SMarcel Moolenaar 
58e00251b7SMarcel Moolenaar #include <dev/cfi/cfi_var.h>
59e00251b7SMarcel Moolenaar 
60e00251b7SMarcel Moolenaar static d_open_t cfi_devopen;
61e00251b7SMarcel Moolenaar static d_close_t cfi_devclose;
62e00251b7SMarcel Moolenaar static d_read_t cfi_devread;
63e00251b7SMarcel Moolenaar static d_write_t cfi_devwrite;
64e00251b7SMarcel Moolenaar static d_ioctl_t cfi_devioctl;
65e00251b7SMarcel Moolenaar 
66e00251b7SMarcel Moolenaar struct cdevsw cfi_cdevsw = {
67e00251b7SMarcel Moolenaar 	.d_version	=	D_VERSION,
68e00251b7SMarcel Moolenaar 	.d_flags	=	0,
69e00251b7SMarcel Moolenaar 	.d_name		=	cfi_driver_name,
70e00251b7SMarcel Moolenaar 	.d_open		=	cfi_devopen,
71e00251b7SMarcel Moolenaar 	.d_close	=	cfi_devclose,
72e00251b7SMarcel Moolenaar 	.d_read		=	cfi_devread,
73e00251b7SMarcel Moolenaar 	.d_write	=	cfi_devwrite,
74e00251b7SMarcel Moolenaar 	.d_ioctl	=	cfi_devioctl,
75e00251b7SMarcel Moolenaar };
76e00251b7SMarcel Moolenaar 
77e00251b7SMarcel Moolenaar /*
78e00251b7SMarcel Moolenaar  * Begin writing into a new block/sector.  We read the sector into
79e00251b7SMarcel Moolenaar  * memory and keep updating that, until we move into another sector
80e00251b7SMarcel Moolenaar  * or the process stops writing. At that time we write the whole
81*f4358134SBrooks Davis  * sector to flash (see cfi_block_finish).  To avoid unneeded erase
82*f4358134SBrooks Davis  * cycles, keep a pristine copy of the sector on hand.
83e00251b7SMarcel Moolenaar  */
84f2c6781bSSam Leffler int
85e00251b7SMarcel Moolenaar cfi_block_start(struct cfi_softc *sc, u_int ofs)
86e00251b7SMarcel Moolenaar {
87e00251b7SMarcel Moolenaar 	union {
88e00251b7SMarcel Moolenaar 		uint8_t		*x8;
89e00251b7SMarcel Moolenaar 		uint16_t	*x16;
90e00251b7SMarcel Moolenaar 		uint32_t	*x32;
91e00251b7SMarcel Moolenaar 	} ptr;
92e00251b7SMarcel Moolenaar 	u_int rofs, rsz;
93e00251b7SMarcel Moolenaar 	uint32_t val;
94e00251b7SMarcel Moolenaar 	int r;
95e00251b7SMarcel Moolenaar 
96e00251b7SMarcel Moolenaar 	rofs = 0;
97e00251b7SMarcel Moolenaar 	for (r = 0; r < sc->sc_regions; r++) {
98e00251b7SMarcel Moolenaar 		rsz = sc->sc_region[r].r_blocks * sc->sc_region[r].r_blksz;
99e00251b7SMarcel Moolenaar 		if (ofs < rofs + rsz)
100e00251b7SMarcel Moolenaar 			break;
101e00251b7SMarcel Moolenaar 		rofs += rsz;
102e00251b7SMarcel Moolenaar 	}
103e00251b7SMarcel Moolenaar 	if (r == sc->sc_regions)
104e00251b7SMarcel Moolenaar 		return (EFAULT);
105e00251b7SMarcel Moolenaar 
106e00251b7SMarcel Moolenaar 	sc->sc_wrbufsz = sc->sc_region[r].r_blksz;
107e00251b7SMarcel Moolenaar 	sc->sc_wrbuf = malloc(sc->sc_wrbufsz, M_TEMP, M_WAITOK);
108e00251b7SMarcel Moolenaar 	sc->sc_wrofs = ofs - (ofs - rofs) % sc->sc_wrbufsz;
109e00251b7SMarcel Moolenaar 
110e00251b7SMarcel Moolenaar 	/* Read the block from flash for byte-serving. */
111e00251b7SMarcel Moolenaar 	ptr.x8 = sc->sc_wrbuf;
112e00251b7SMarcel Moolenaar 	for (r = 0; r < sc->sc_wrbufsz; r += sc->sc_width) {
1139c1f1330SJayachandran C. 		val = cfi_read_raw(sc, sc->sc_wrofs + r);
114e00251b7SMarcel Moolenaar 		switch (sc->sc_width) {
115e00251b7SMarcel Moolenaar 		case 1:
116e00251b7SMarcel Moolenaar 			*(ptr.x8)++ = val;
117e00251b7SMarcel Moolenaar 			break;
118e00251b7SMarcel Moolenaar 		case 2:
119e00251b7SMarcel Moolenaar 			*(ptr.x16)++ = val;
120e00251b7SMarcel Moolenaar 			break;
121e00251b7SMarcel Moolenaar 		case 4:
122e00251b7SMarcel Moolenaar 			*(ptr.x32)++ = val;
123e00251b7SMarcel Moolenaar 			break;
124e00251b7SMarcel Moolenaar 		}
125e00251b7SMarcel Moolenaar 	}
126*f4358134SBrooks Davis 	sc->sc_wrbufcpy = malloc(sc->sc_wrbufsz, M_TEMP, M_WAITOK);
127*f4358134SBrooks Davis 	memcpy(sc->sc_wrbufcpy, sc->sc_wrbuf, sc->sc_wrbufsz);
128e00251b7SMarcel Moolenaar 	sc->sc_writing = 1;
129e00251b7SMarcel Moolenaar 	return (0);
130e00251b7SMarcel Moolenaar }
131e00251b7SMarcel Moolenaar 
132e00251b7SMarcel Moolenaar /*
133e00251b7SMarcel Moolenaar  * Finish updating the current block/sector by writing the compound
134e00251b7SMarcel Moolenaar  * set of changes to the flash.
135e00251b7SMarcel Moolenaar  */
136f2c6781bSSam Leffler int
137e00251b7SMarcel Moolenaar cfi_block_finish(struct cfi_softc *sc)
138e00251b7SMarcel Moolenaar {
139e00251b7SMarcel Moolenaar 	int error;
140e00251b7SMarcel Moolenaar 
141e00251b7SMarcel Moolenaar 	error = cfi_write_block(sc);
142e00251b7SMarcel Moolenaar 	free(sc->sc_wrbuf, M_TEMP);
143*f4358134SBrooks Davis 	free(sc->sc_wrbufcpy, M_TEMP);
144e00251b7SMarcel Moolenaar 	sc->sc_wrbuf = NULL;
145e00251b7SMarcel Moolenaar 	sc->sc_wrbufsz = 0;
146e00251b7SMarcel Moolenaar 	sc->sc_wrofs = 0;
147e00251b7SMarcel Moolenaar 	sc->sc_writing = 0;
148e00251b7SMarcel Moolenaar 	return (error);
149e00251b7SMarcel Moolenaar }
150e00251b7SMarcel Moolenaar 
151e00251b7SMarcel Moolenaar static int
152e00251b7SMarcel Moolenaar cfi_devopen(struct cdev *dev, int oflags, int devtype, struct thread *td)
153e00251b7SMarcel Moolenaar {
154e00251b7SMarcel Moolenaar 	struct cfi_softc *sc;
155e00251b7SMarcel Moolenaar 
156e00251b7SMarcel Moolenaar 	sc = dev->si_drv1;
157e00251b7SMarcel Moolenaar 	/* We allow only 1 open. */
158dc6dc1f5SAttilio Rao 	if (!atomic_cmpset_acq_ptr((uintptr_t *)&sc->sc_opened,
159dc6dc1f5SAttilio Rao 	    (uintptr_t)NULL, (uintptr_t)td->td_proc))
160e00251b7SMarcel Moolenaar 		return (EBUSY);
161e00251b7SMarcel Moolenaar 	return (0);
162e00251b7SMarcel Moolenaar }
163e00251b7SMarcel Moolenaar 
164e00251b7SMarcel Moolenaar static int
165e00251b7SMarcel Moolenaar cfi_devclose(struct cdev *dev, int fflag, int devtype, struct thread *td)
166e00251b7SMarcel Moolenaar {
167e00251b7SMarcel Moolenaar 	struct cfi_softc *sc;
168e00251b7SMarcel Moolenaar 	int error;
169e00251b7SMarcel Moolenaar 
170e00251b7SMarcel Moolenaar 	sc = dev->si_drv1;
171e00251b7SMarcel Moolenaar 	/* Sanity. Not really necessary. */
172e00251b7SMarcel Moolenaar 	if (sc->sc_opened != td->td_proc)
173e00251b7SMarcel Moolenaar 		return (ENXIO);
174e00251b7SMarcel Moolenaar 
175e00251b7SMarcel Moolenaar 	error = (sc->sc_writing) ? cfi_block_finish(sc) : 0;
176e00251b7SMarcel Moolenaar 	sc->sc_opened = NULL;
177e00251b7SMarcel Moolenaar 	return (error);
178e00251b7SMarcel Moolenaar }
179e00251b7SMarcel Moolenaar 
180e00251b7SMarcel Moolenaar static int
181e00251b7SMarcel Moolenaar cfi_devread(struct cdev *dev, struct uio *uio, int ioflag)
182e00251b7SMarcel Moolenaar {
183e00251b7SMarcel Moolenaar 	union {
184e00251b7SMarcel Moolenaar 		uint8_t		x8[4];
185e00251b7SMarcel Moolenaar 		uint16_t	x16[2];
186e00251b7SMarcel Moolenaar 		uint32_t	x32[1];
187e00251b7SMarcel Moolenaar 	} buf;
188e00251b7SMarcel Moolenaar 	struct cfi_softc *sc;
189e00251b7SMarcel Moolenaar 	u_int ofs;
190e00251b7SMarcel Moolenaar 	uint32_t val;
191e00251b7SMarcel Moolenaar 	int error;
192e00251b7SMarcel Moolenaar 
193e00251b7SMarcel Moolenaar 	sc = dev->si_drv1;
194e00251b7SMarcel Moolenaar 
195e00251b7SMarcel Moolenaar 	error = (sc->sc_writing) ? cfi_block_finish(sc) : 0;
196e00251b7SMarcel Moolenaar 	if (!error)
197e00251b7SMarcel Moolenaar 		error = (uio->uio_offset > sc->sc_size) ? EIO : 0;
198e00251b7SMarcel Moolenaar 
199e00251b7SMarcel Moolenaar 	while (error == 0 && uio->uio_resid > 0 &&
200e00251b7SMarcel Moolenaar 	    uio->uio_offset < sc->sc_size) {
201e00251b7SMarcel Moolenaar 		ofs = uio->uio_offset;
2029c1f1330SJayachandran C. 		val = cfi_read_raw(sc, ofs);
203e00251b7SMarcel Moolenaar 		switch (sc->sc_width) {
204e00251b7SMarcel Moolenaar 		case 1:
205e00251b7SMarcel Moolenaar 			buf.x8[0] = val;
206e00251b7SMarcel Moolenaar 			break;
207e00251b7SMarcel Moolenaar 		case 2:
208e00251b7SMarcel Moolenaar 			buf.x16[0] = val;
209e00251b7SMarcel Moolenaar 			break;
210e00251b7SMarcel Moolenaar 		case 4:
211e00251b7SMarcel Moolenaar 			buf.x32[0] = val;
212e00251b7SMarcel Moolenaar 			break;
213e00251b7SMarcel Moolenaar 		}
214e00251b7SMarcel Moolenaar 		ofs &= sc->sc_width - 1;
215e00251b7SMarcel Moolenaar 		error = uiomove(buf.x8 + ofs,
216e00251b7SMarcel Moolenaar 		    MIN(uio->uio_resid, sc->sc_width - ofs), uio);
217e00251b7SMarcel Moolenaar 	}
218e00251b7SMarcel Moolenaar 	return (error);
219e00251b7SMarcel Moolenaar }
220e00251b7SMarcel Moolenaar 
221e00251b7SMarcel Moolenaar static int
222e00251b7SMarcel Moolenaar cfi_devwrite(struct cdev *dev, struct uio *uio, int ioflag)
223e00251b7SMarcel Moolenaar {
224e00251b7SMarcel Moolenaar 	struct cfi_softc *sc;
225e00251b7SMarcel Moolenaar 	u_int ofs, top;
226e00251b7SMarcel Moolenaar 	int error;
227e00251b7SMarcel Moolenaar 
228e00251b7SMarcel Moolenaar 	sc = dev->si_drv1;
229e00251b7SMarcel Moolenaar 
230e00251b7SMarcel Moolenaar 	error = (uio->uio_offset > sc->sc_size) ? EIO : 0;
231e00251b7SMarcel Moolenaar 	while (error == 0 && uio->uio_resid > 0 &&
232e00251b7SMarcel Moolenaar 	    uio->uio_offset < sc->sc_size) {
233e00251b7SMarcel Moolenaar 		ofs = uio->uio_offset;
234e00251b7SMarcel Moolenaar 
235e00251b7SMarcel Moolenaar 		/*
236e00251b7SMarcel Moolenaar 		 * Finish the current block if we're about to write
237e00251b7SMarcel Moolenaar 		 * to a different block.
238e00251b7SMarcel Moolenaar 		 */
239e00251b7SMarcel Moolenaar 		if (sc->sc_writing) {
240e00251b7SMarcel Moolenaar 			top = sc->sc_wrofs + sc->sc_wrbufsz;
241e00251b7SMarcel Moolenaar 			if (ofs < sc->sc_wrofs || ofs >= top)
242e00251b7SMarcel Moolenaar 				cfi_block_finish(sc);
243e00251b7SMarcel Moolenaar 		}
244e00251b7SMarcel Moolenaar 
245e00251b7SMarcel Moolenaar 		/* Start writing to a (new) block if applicable. */
246e00251b7SMarcel Moolenaar 		if (!sc->sc_writing) {
247e00251b7SMarcel Moolenaar 			error = cfi_block_start(sc, uio->uio_offset);
248e00251b7SMarcel Moolenaar 			if (error)
249e00251b7SMarcel Moolenaar 				break;
250e00251b7SMarcel Moolenaar 		}
251e00251b7SMarcel Moolenaar 
252e00251b7SMarcel Moolenaar 		top = sc->sc_wrofs + sc->sc_wrbufsz;
253e00251b7SMarcel Moolenaar 		error = uiomove(sc->sc_wrbuf + ofs - sc->sc_wrofs,
254e00251b7SMarcel Moolenaar 		    MIN(top - ofs, uio->uio_resid), uio);
255e00251b7SMarcel Moolenaar 	}
256e00251b7SMarcel Moolenaar 	return (error);
257e00251b7SMarcel Moolenaar }
258e00251b7SMarcel Moolenaar 
259e00251b7SMarcel Moolenaar static int
260e00251b7SMarcel Moolenaar cfi_devioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag,
261e00251b7SMarcel Moolenaar     struct thread *td)
262e00251b7SMarcel Moolenaar {
263e00251b7SMarcel Moolenaar 	struct cfi_softc *sc;
264e00251b7SMarcel Moolenaar 	struct cfiocqry *rq;
265e00251b7SMarcel Moolenaar 	int error;
266e00251b7SMarcel Moolenaar 	u_char val;
267e00251b7SMarcel Moolenaar 
268e00251b7SMarcel Moolenaar 	sc = dev->si_drv1;
2693a114d1fSSam Leffler 	error = 0;
270e00251b7SMarcel Moolenaar 
2713a114d1fSSam Leffler 	switch (cmd) {
2723a114d1fSSam Leffler 	case CFIOCQRY:
2733a114d1fSSam Leffler 		if (sc->sc_writing) {
2743a114d1fSSam Leffler 			error = cfi_block_finish(sc);
275e00251b7SMarcel Moolenaar 			if (error)
2763a114d1fSSam Leffler 				break;
2773a114d1fSSam Leffler 		}
278e00251b7SMarcel Moolenaar 		rq = (struct cfiocqry *)data;
279e00251b7SMarcel Moolenaar 		if (rq->offset >= sc->sc_size / sc->sc_width)
280e00251b7SMarcel Moolenaar 			return (ESPIPE);
281e00251b7SMarcel Moolenaar 		if (rq->offset + rq->count > sc->sc_size / sc->sc_width)
282e00251b7SMarcel Moolenaar 			return (ENOSPC);
283e00251b7SMarcel Moolenaar 
284e00251b7SMarcel Moolenaar 		while (!error && rq->count--) {
285e00251b7SMarcel Moolenaar 			val = cfi_read_qry(sc, rq->offset++);
286e00251b7SMarcel Moolenaar 			error = copyout(&val, rq->buffer++, 1);
287e00251b7SMarcel Moolenaar 		}
2883a114d1fSSam Leffler 		break;
28963425a7fSSam Leffler #ifdef CFI_SUPPORT_STRATAFLASH
29063425a7fSSam Leffler 	case CFIOCGFACTORYPR:
29163425a7fSSam Leffler 		error = cfi_intel_get_factory_pr(sc, (uint64_t *)data);
29263425a7fSSam Leffler 		break;
29363425a7fSSam Leffler 	case CFIOCGOEMPR:
29463425a7fSSam Leffler 		error = cfi_intel_get_oem_pr(sc, (uint64_t *)data);
29563425a7fSSam Leffler 		break;
29663425a7fSSam Leffler 	case CFIOCSOEMPR:
29763425a7fSSam Leffler 		error = cfi_intel_set_oem_pr(sc, *(uint64_t *)data);
29863425a7fSSam Leffler 		break;
29963425a7fSSam Leffler 	case CFIOCGPLR:
30063425a7fSSam Leffler 		error = cfi_intel_get_plr(sc, (uint32_t *)data);
30163425a7fSSam Leffler 		break;
30263425a7fSSam Leffler 	case CFIOCSPLR:
30363425a7fSSam Leffler 		error = cfi_intel_set_plr(sc);
30463425a7fSSam Leffler 		break;
30563425a7fSSam Leffler #endif /* CFI_SUPPORT_STRATAFLASH */
3063a114d1fSSam Leffler 	default:
3073a114d1fSSam Leffler 		error = ENOIOCTL;
3083a114d1fSSam Leffler 		break;
3093a114d1fSSam Leffler 	}
310e00251b7SMarcel Moolenaar 	return (error);
311e00251b7SMarcel Moolenaar }
312