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