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