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