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