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