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> 39e00251b7SMarcel Moolenaar __FBSDID("$FreeBSD$"); 40e00251b7SMarcel Moolenaar 4163425a7fSSam Leffler #include "opt_cfi.h" 4263425a7fSSam Leffler 43e00251b7SMarcel Moolenaar #include <sys/param.h> 44e00251b7SMarcel Moolenaar #include <sys/systm.h> 45e00251b7SMarcel Moolenaar #include <sys/bus.h> 46e00251b7SMarcel Moolenaar #include <sys/conf.h> 47e00251b7SMarcel Moolenaar #include <sys/ioccom.h> 48e00251b7SMarcel Moolenaar #include <sys/kernel.h> 49e00251b7SMarcel Moolenaar #include <sys/malloc.h> 50e00251b7SMarcel Moolenaar #include <sys/proc.h> 51e00251b7SMarcel Moolenaar #include <sys/sysctl.h> 52e00251b7SMarcel Moolenaar #include <sys/types.h> 53e00251b7SMarcel Moolenaar #include <sys/uio.h> 54e00251b7SMarcel Moolenaar 55e00251b7SMarcel Moolenaar #include <sys/cfictl.h> 56e00251b7SMarcel Moolenaar 57e00251b7SMarcel Moolenaar #include <machine/atomic.h> 58e00251b7SMarcel Moolenaar #include <machine/bus.h> 59e00251b7SMarcel Moolenaar 60e00251b7SMarcel Moolenaar #include <dev/cfi/cfi_var.h> 61e00251b7SMarcel Moolenaar 62e00251b7SMarcel Moolenaar static d_open_t cfi_devopen; 63e00251b7SMarcel Moolenaar static d_close_t cfi_devclose; 64e00251b7SMarcel Moolenaar static d_read_t cfi_devread; 65e00251b7SMarcel Moolenaar static d_write_t cfi_devwrite; 66e00251b7SMarcel Moolenaar static d_ioctl_t cfi_devioctl; 67e00251b7SMarcel Moolenaar 68e00251b7SMarcel Moolenaar struct cdevsw cfi_cdevsw = { 69e00251b7SMarcel Moolenaar .d_version = D_VERSION, 70e00251b7SMarcel Moolenaar .d_flags = 0, 71e00251b7SMarcel Moolenaar .d_name = cfi_driver_name, 72e00251b7SMarcel Moolenaar .d_open = cfi_devopen, 73e00251b7SMarcel Moolenaar .d_close = cfi_devclose, 74e00251b7SMarcel Moolenaar .d_read = cfi_devread, 75e00251b7SMarcel Moolenaar .d_write = cfi_devwrite, 76e00251b7SMarcel Moolenaar .d_ioctl = cfi_devioctl, 77e00251b7SMarcel Moolenaar }; 78e00251b7SMarcel Moolenaar 79e00251b7SMarcel Moolenaar /* 80e00251b7SMarcel Moolenaar * Begin writing into a new block/sector. We read the sector into 81e00251b7SMarcel Moolenaar * memory and keep updating that, until we move into another sector 82e00251b7SMarcel Moolenaar * or the process stops writing. At that time we write the whole 83f4358134SBrooks Davis * sector to flash (see cfi_block_finish). To avoid unneeded erase 84f4358134SBrooks Davis * cycles, keep a pristine copy of the sector on hand. 85e00251b7SMarcel Moolenaar */ 86f2c6781bSSam Leffler int 87e00251b7SMarcel Moolenaar cfi_block_start(struct cfi_softc *sc, u_int ofs) 88e00251b7SMarcel Moolenaar { 89e00251b7SMarcel Moolenaar union { 90e00251b7SMarcel Moolenaar uint8_t *x8; 91e00251b7SMarcel Moolenaar uint16_t *x16; 92e00251b7SMarcel Moolenaar uint32_t *x32; 93e00251b7SMarcel Moolenaar } ptr; 94e00251b7SMarcel Moolenaar u_int rofs, rsz; 95e00251b7SMarcel Moolenaar uint32_t val; 96e00251b7SMarcel Moolenaar int r; 97e00251b7SMarcel Moolenaar 98e00251b7SMarcel Moolenaar rofs = 0; 99e00251b7SMarcel Moolenaar for (r = 0; r < sc->sc_regions; r++) { 100e00251b7SMarcel Moolenaar rsz = sc->sc_region[r].r_blocks * sc->sc_region[r].r_blksz; 101e00251b7SMarcel Moolenaar if (ofs < rofs + rsz) 102e00251b7SMarcel Moolenaar break; 103e00251b7SMarcel Moolenaar rofs += rsz; 104e00251b7SMarcel Moolenaar } 105e00251b7SMarcel Moolenaar if (r == sc->sc_regions) 106e00251b7SMarcel Moolenaar return (EFAULT); 107e00251b7SMarcel Moolenaar 108e00251b7SMarcel Moolenaar sc->sc_wrbufsz = sc->sc_region[r].r_blksz; 109e00251b7SMarcel Moolenaar sc->sc_wrbuf = malloc(sc->sc_wrbufsz, M_TEMP, M_WAITOK); 110e00251b7SMarcel Moolenaar sc->sc_wrofs = ofs - (ofs - rofs) % sc->sc_wrbufsz; 111e00251b7SMarcel Moolenaar 112e00251b7SMarcel Moolenaar /* Read the block from flash for byte-serving. */ 113e00251b7SMarcel Moolenaar ptr.x8 = sc->sc_wrbuf; 114e00251b7SMarcel Moolenaar for (r = 0; r < sc->sc_wrbufsz; r += sc->sc_width) { 1159c1f1330SJayachandran C. val = cfi_read_raw(sc, sc->sc_wrofs + r); 116e00251b7SMarcel Moolenaar switch (sc->sc_width) { 117e00251b7SMarcel Moolenaar case 1: 118e00251b7SMarcel Moolenaar *(ptr.x8)++ = val; 119e00251b7SMarcel Moolenaar break; 120e00251b7SMarcel Moolenaar case 2: 121e00251b7SMarcel Moolenaar *(ptr.x16)++ = val; 122e00251b7SMarcel Moolenaar break; 123e00251b7SMarcel Moolenaar case 4: 124e00251b7SMarcel Moolenaar *(ptr.x32)++ = val; 125e00251b7SMarcel Moolenaar break; 126e00251b7SMarcel Moolenaar } 127e00251b7SMarcel Moolenaar } 128f4358134SBrooks Davis sc->sc_wrbufcpy = malloc(sc->sc_wrbufsz, M_TEMP, M_WAITOK); 129f4358134SBrooks Davis memcpy(sc->sc_wrbufcpy, sc->sc_wrbuf, sc->sc_wrbufsz); 130e00251b7SMarcel Moolenaar sc->sc_writing = 1; 131e00251b7SMarcel Moolenaar return (0); 132e00251b7SMarcel Moolenaar } 133e00251b7SMarcel Moolenaar 134e00251b7SMarcel Moolenaar /* 135e00251b7SMarcel Moolenaar * Finish updating the current block/sector by writing the compound 136e00251b7SMarcel Moolenaar * set of changes to the flash. 137e00251b7SMarcel Moolenaar */ 138f2c6781bSSam Leffler int 139e00251b7SMarcel Moolenaar cfi_block_finish(struct cfi_softc *sc) 140e00251b7SMarcel Moolenaar { 141e00251b7SMarcel Moolenaar int error; 142e00251b7SMarcel Moolenaar 143e00251b7SMarcel Moolenaar error = cfi_write_block(sc); 144e00251b7SMarcel Moolenaar free(sc->sc_wrbuf, M_TEMP); 145f4358134SBrooks Davis free(sc->sc_wrbufcpy, M_TEMP); 146e00251b7SMarcel Moolenaar sc->sc_wrbuf = NULL; 147e00251b7SMarcel Moolenaar sc->sc_wrbufsz = 0; 148e00251b7SMarcel Moolenaar sc->sc_wrofs = 0; 149e00251b7SMarcel Moolenaar sc->sc_writing = 0; 150e00251b7SMarcel Moolenaar return (error); 151e00251b7SMarcel Moolenaar } 152e00251b7SMarcel Moolenaar 153e00251b7SMarcel Moolenaar static int 154e00251b7SMarcel Moolenaar cfi_devopen(struct cdev *dev, int oflags, int devtype, struct thread *td) 155e00251b7SMarcel Moolenaar { 156e00251b7SMarcel Moolenaar struct cfi_softc *sc; 157e00251b7SMarcel Moolenaar 158e00251b7SMarcel Moolenaar sc = dev->si_drv1; 159e00251b7SMarcel Moolenaar /* We allow only 1 open. */ 160dc6dc1f5SAttilio Rao if (!atomic_cmpset_acq_ptr((uintptr_t *)&sc->sc_opened, 161dc6dc1f5SAttilio Rao (uintptr_t)NULL, (uintptr_t)td->td_proc)) 162e00251b7SMarcel Moolenaar return (EBUSY); 163e00251b7SMarcel Moolenaar return (0); 164e00251b7SMarcel Moolenaar } 165e00251b7SMarcel Moolenaar 166e00251b7SMarcel Moolenaar static int 167e00251b7SMarcel Moolenaar cfi_devclose(struct cdev *dev, int fflag, int devtype, struct thread *td) 168e00251b7SMarcel Moolenaar { 169e00251b7SMarcel Moolenaar struct cfi_softc *sc; 170e00251b7SMarcel Moolenaar int error; 171e00251b7SMarcel Moolenaar 172e00251b7SMarcel Moolenaar sc = dev->si_drv1; 173e00251b7SMarcel Moolenaar /* Sanity. Not really necessary. */ 174e00251b7SMarcel Moolenaar if (sc->sc_opened != td->td_proc) 175e00251b7SMarcel Moolenaar return (ENXIO); 176e00251b7SMarcel Moolenaar 177e00251b7SMarcel Moolenaar error = (sc->sc_writing) ? cfi_block_finish(sc) : 0; 178e00251b7SMarcel Moolenaar sc->sc_opened = NULL; 179e00251b7SMarcel Moolenaar return (error); 180e00251b7SMarcel Moolenaar } 181e00251b7SMarcel Moolenaar 182e00251b7SMarcel Moolenaar static int 183e00251b7SMarcel Moolenaar cfi_devread(struct cdev *dev, struct uio *uio, int ioflag) 184e00251b7SMarcel Moolenaar { 185e00251b7SMarcel Moolenaar union { 186e00251b7SMarcel Moolenaar uint8_t x8[4]; 187e00251b7SMarcel Moolenaar uint16_t x16[2]; 188e00251b7SMarcel Moolenaar uint32_t x32[1]; 189e00251b7SMarcel Moolenaar } buf; 190e00251b7SMarcel Moolenaar struct cfi_softc *sc; 191e00251b7SMarcel Moolenaar u_int ofs; 192e00251b7SMarcel Moolenaar uint32_t val; 193e00251b7SMarcel Moolenaar int error; 194e00251b7SMarcel Moolenaar 195e00251b7SMarcel Moolenaar sc = dev->si_drv1; 196e00251b7SMarcel Moolenaar 197e00251b7SMarcel Moolenaar error = (sc->sc_writing) ? cfi_block_finish(sc) : 0; 198e00251b7SMarcel Moolenaar if (!error) 199e00251b7SMarcel Moolenaar error = (uio->uio_offset > sc->sc_size) ? EIO : 0; 200e00251b7SMarcel Moolenaar 201e00251b7SMarcel Moolenaar while (error == 0 && uio->uio_resid > 0 && 202e00251b7SMarcel Moolenaar uio->uio_offset < sc->sc_size) { 203e00251b7SMarcel Moolenaar ofs = uio->uio_offset; 2049c1f1330SJayachandran C. val = cfi_read_raw(sc, ofs); 205e00251b7SMarcel Moolenaar switch (sc->sc_width) { 206e00251b7SMarcel Moolenaar case 1: 207e00251b7SMarcel Moolenaar buf.x8[0] = val; 208e00251b7SMarcel Moolenaar break; 209e00251b7SMarcel Moolenaar case 2: 210e00251b7SMarcel Moolenaar buf.x16[0] = val; 211e00251b7SMarcel Moolenaar break; 212e00251b7SMarcel Moolenaar case 4: 213e00251b7SMarcel Moolenaar buf.x32[0] = val; 214e00251b7SMarcel Moolenaar break; 215e00251b7SMarcel Moolenaar } 216e00251b7SMarcel Moolenaar ofs &= sc->sc_width - 1; 217e00251b7SMarcel Moolenaar error = uiomove(buf.x8 + ofs, 218e00251b7SMarcel Moolenaar MIN(uio->uio_resid, sc->sc_width - ofs), uio); 219e00251b7SMarcel Moolenaar } 220e00251b7SMarcel Moolenaar return (error); 221e00251b7SMarcel Moolenaar } 222e00251b7SMarcel Moolenaar 223e00251b7SMarcel Moolenaar static int 224e00251b7SMarcel Moolenaar cfi_devwrite(struct cdev *dev, struct uio *uio, int ioflag) 225e00251b7SMarcel Moolenaar { 226e00251b7SMarcel Moolenaar struct cfi_softc *sc; 227e00251b7SMarcel Moolenaar u_int ofs, top; 228e00251b7SMarcel Moolenaar int error; 229e00251b7SMarcel Moolenaar 230e00251b7SMarcel Moolenaar sc = dev->si_drv1; 231e00251b7SMarcel Moolenaar 232e00251b7SMarcel Moolenaar error = (uio->uio_offset > sc->sc_size) ? EIO : 0; 233e00251b7SMarcel Moolenaar while (error == 0 && uio->uio_resid > 0 && 234e00251b7SMarcel Moolenaar uio->uio_offset < sc->sc_size) { 235e00251b7SMarcel Moolenaar ofs = uio->uio_offset; 236e00251b7SMarcel Moolenaar 237e00251b7SMarcel Moolenaar /* 238e00251b7SMarcel Moolenaar * Finish the current block if we're about to write 239e00251b7SMarcel Moolenaar * to a different block. 240e00251b7SMarcel Moolenaar */ 241e00251b7SMarcel Moolenaar if (sc->sc_writing) { 242e00251b7SMarcel Moolenaar top = sc->sc_wrofs + sc->sc_wrbufsz; 243e00251b7SMarcel Moolenaar if (ofs < sc->sc_wrofs || ofs >= top) 244e00251b7SMarcel Moolenaar cfi_block_finish(sc); 245e00251b7SMarcel Moolenaar } 246e00251b7SMarcel Moolenaar 247e00251b7SMarcel Moolenaar /* Start writing to a (new) block if applicable. */ 248e00251b7SMarcel Moolenaar if (!sc->sc_writing) { 249e00251b7SMarcel Moolenaar error = cfi_block_start(sc, uio->uio_offset); 250e00251b7SMarcel Moolenaar if (error) 251e00251b7SMarcel Moolenaar break; 252e00251b7SMarcel Moolenaar } 253e00251b7SMarcel Moolenaar 254e00251b7SMarcel Moolenaar top = sc->sc_wrofs + sc->sc_wrbufsz; 255e00251b7SMarcel Moolenaar error = uiomove(sc->sc_wrbuf + ofs - sc->sc_wrofs, 256e00251b7SMarcel Moolenaar MIN(top - ofs, uio->uio_resid), uio); 257e00251b7SMarcel Moolenaar } 258e00251b7SMarcel Moolenaar return (error); 259e00251b7SMarcel Moolenaar } 260e00251b7SMarcel Moolenaar 261e00251b7SMarcel Moolenaar static int 262e00251b7SMarcel Moolenaar cfi_devioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, 263e00251b7SMarcel Moolenaar struct thread *td) 264e00251b7SMarcel Moolenaar { 265e00251b7SMarcel Moolenaar struct cfi_softc *sc; 266e00251b7SMarcel Moolenaar struct cfiocqry *rq; 267e00251b7SMarcel Moolenaar int error; 268e00251b7SMarcel Moolenaar u_char val; 269e00251b7SMarcel Moolenaar 270e00251b7SMarcel Moolenaar sc = dev->si_drv1; 2713a114d1fSSam Leffler error = 0; 272e00251b7SMarcel Moolenaar 2733a114d1fSSam Leffler switch (cmd) { 2743a114d1fSSam Leffler case CFIOCQRY: 2753a114d1fSSam Leffler if (sc->sc_writing) { 2763a114d1fSSam Leffler error = cfi_block_finish(sc); 277e00251b7SMarcel Moolenaar if (error) 2783a114d1fSSam Leffler break; 2793a114d1fSSam Leffler } 280e00251b7SMarcel Moolenaar rq = (struct cfiocqry *)data; 281e00251b7SMarcel Moolenaar if (rq->offset >= sc->sc_size / sc->sc_width) 282e00251b7SMarcel Moolenaar return (ESPIPE); 283*985d08feSEd Maste if (rq->offset > ULONG_MAX - rq->count || 284*985d08feSEd Maste rq->offset + rq->count > sc->sc_size / sc->sc_width) 285e00251b7SMarcel Moolenaar return (ENOSPC); 286e00251b7SMarcel Moolenaar 287e00251b7SMarcel Moolenaar while (!error && rq->count--) { 288e00251b7SMarcel Moolenaar val = cfi_read_qry(sc, rq->offset++); 289e00251b7SMarcel Moolenaar error = copyout(&val, rq->buffer++, 1); 290e00251b7SMarcel Moolenaar } 2913a114d1fSSam Leffler break; 29263425a7fSSam Leffler #ifdef CFI_SUPPORT_STRATAFLASH 29363425a7fSSam Leffler case CFIOCGFACTORYPR: 29463425a7fSSam Leffler error = cfi_intel_get_factory_pr(sc, (uint64_t *)data); 29563425a7fSSam Leffler break; 29663425a7fSSam Leffler case CFIOCGOEMPR: 29763425a7fSSam Leffler error = cfi_intel_get_oem_pr(sc, (uint64_t *)data); 29863425a7fSSam Leffler break; 29963425a7fSSam Leffler case CFIOCSOEMPR: 30063425a7fSSam Leffler error = cfi_intel_set_oem_pr(sc, *(uint64_t *)data); 30163425a7fSSam Leffler break; 30263425a7fSSam Leffler case CFIOCGPLR: 30363425a7fSSam Leffler error = cfi_intel_get_plr(sc, (uint32_t *)data); 30463425a7fSSam Leffler break; 30563425a7fSSam Leffler case CFIOCSPLR: 30663425a7fSSam Leffler error = cfi_intel_set_plr(sc); 30763425a7fSSam Leffler break; 30863425a7fSSam Leffler #endif /* CFI_SUPPORT_STRATAFLASH */ 3093a114d1fSSam Leffler default: 3103a114d1fSSam Leffler error = ENOIOCTL; 3113a114d1fSSam Leffler break; 3123a114d1fSSam Leffler } 313e00251b7SMarcel Moolenaar return (error); 314e00251b7SMarcel Moolenaar } 315