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