1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright (c) 2019 Justin Hibbits 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 19 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 23 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 24 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 25 * POSSIBILITY OF SUCH DAMAGE. 26 * 27 * $FreeBSD$ 28 */ 29 30 #include <sys/param.h> 31 #include <sys/systm.h> 32 #include <sys/module.h> 33 #include <sys/bus.h> 34 #include <sys/conf.h> 35 #include <sys/disk.h> 36 #include <sys/kernel.h> 37 #include <sys/mutex.h> 38 #include <sys/uio.h> 39 40 #include <dev/ofw/openfirm.h> 41 #include <dev/ofw/ofw_bus.h> 42 #include <dev/ofw/ofw_bus_subr.h> 43 44 #include <machine/bus.h> 45 #include <machine/md_var.h> 46 #include <machine/pio.h> 47 #include <machine/resource.h> 48 49 #include "opal.h" 50 51 #include <sys/rman.h> 52 53 #include <vm/vm.h> 54 #include <vm/pmap.h> 55 56 #define NVRAM_BUFSIZE (65536) /* 64k blocks */ 57 58 struct opal_nvram_softc { 59 device_t sc_dev; 60 struct mtx sc_mtx; 61 uint32_t sc_size; 62 uint8_t *sc_buf; 63 vm_paddr_t sc_buf_phys; 64 65 struct cdev *sc_cdev; 66 int sc_isopen; 67 }; 68 69 #define NVRAM_LOCK(sc) mtx_lock(&sc->sc_mtx) 70 #define NVRAM_UNLOCK(sc) mtx_unlock(&sc->sc_mtx) 71 72 /* 73 * Device interface. 74 */ 75 static int opal_nvram_probe(device_t); 76 static int opal_nvram_attach(device_t); 77 static int opal_nvram_detach(device_t); 78 79 /* 80 * Driver methods. 81 */ 82 static device_method_t opal_nvram_methods[] = { 83 /* Device interface */ 84 DEVMETHOD(device_probe, opal_nvram_probe), 85 DEVMETHOD(device_attach, opal_nvram_attach), 86 DEVMETHOD(device_detach, opal_nvram_detach), 87 { 0, 0 } 88 }; 89 90 static driver_t opal_nvram_driver = { 91 "opal_nvram", 92 opal_nvram_methods, 93 sizeof(struct opal_nvram_softc) 94 }; 95 96 DRIVER_MODULE(opal_nvram, opal, opal_nvram_driver, 0, 0); 97 98 /* 99 * Cdev methods. 100 */ 101 102 static d_open_t opal_nvram_open; 103 static d_close_t opal_nvram_close; 104 static d_read_t opal_nvram_read; 105 static d_write_t opal_nvram_write; 106 static d_ioctl_t opal_nvram_ioctl; 107 108 static struct cdevsw opal_nvram_cdevsw = { 109 .d_version = D_VERSION, 110 .d_open = opal_nvram_open, 111 .d_close = opal_nvram_close, 112 .d_read = opal_nvram_read, 113 .d_write = opal_nvram_write, 114 .d_ioctl = opal_nvram_ioctl, 115 .d_name = "nvram", 116 }; 117 118 static int 119 opal_nvram_probe(device_t dev) 120 { 121 122 if (!ofw_bus_is_compatible(dev, "ibm,opal-nvram")) 123 return (ENXIO); 124 125 device_set_desc(dev, "OPAL NVRAM"); 126 return (BUS_PROBE_DEFAULT); 127 } 128 129 static int 130 opal_nvram_attach(device_t dev) 131 { 132 struct opal_nvram_softc *sc; 133 phandle_t node; 134 int err; 135 136 node = ofw_bus_get_node(dev); 137 sc = device_get_softc(dev); 138 139 sc->sc_dev = dev; 140 141 err = OF_getencprop(node, "#bytes", &sc->sc_size, 142 sizeof(sc->sc_size)); 143 144 if (err < 0) 145 return (ENXIO); 146 147 sc->sc_buf = contigmalloc(NVRAM_BUFSIZE, M_DEVBUF, M_WAITOK, 148 0, BUS_SPACE_MAXADDR, PAGE_SIZE, 0); 149 if (sc->sc_buf == NULL) { 150 device_printf(dev, "No memory for buffer.\n"); 151 return (ENXIO); 152 } 153 sc->sc_buf_phys = pmap_kextract((vm_offset_t)sc->sc_buf); 154 sc->sc_cdev = make_dev(&opal_nvram_cdevsw, 0, 0, 0, 0600, 155 "nvram"); 156 sc->sc_cdev->si_drv1 = sc; 157 158 mtx_init(&sc->sc_mtx, "opal_nvram", 0, MTX_DEF); 159 160 return (0); 161 } 162 163 static int 164 opal_nvram_detach(device_t dev) 165 { 166 struct opal_nvram_softc *sc; 167 168 sc = device_get_softc(dev); 169 170 if (sc->sc_cdev != NULL) 171 destroy_dev(sc->sc_cdev); 172 if (sc->sc_buf != NULL) 173 contigfree(sc->sc_buf, NVRAM_BUFSIZE, M_DEVBUF); 174 175 mtx_destroy(&sc->sc_mtx); 176 177 return (0); 178 } 179 180 static int 181 opal_nvram_open(struct cdev *dev, int flags, int fmt, struct thread *td) 182 { 183 struct opal_nvram_softc *sc = dev->si_drv1; 184 int err; 185 186 err = 0; 187 188 NVRAM_LOCK(sc); 189 if (sc->sc_isopen) 190 err = EBUSY; 191 else 192 sc->sc_isopen = 1; 193 NVRAM_UNLOCK(sc); 194 195 return (err); 196 } 197 198 static int 199 opal_nvram_close(struct cdev *dev, int fflag, int devtype, struct thread *td) 200 { 201 struct opal_nvram_softc *sc = dev->si_drv1; 202 203 NVRAM_LOCK(sc); 204 sc->sc_isopen = 0; 205 NVRAM_UNLOCK(sc); 206 207 return (0); 208 } 209 210 static int 211 opal_nvram_read(struct cdev *dev, struct uio *uio, int ioflag) 212 { 213 struct opal_nvram_softc *sc = dev->si_drv1; 214 int rv, amnt; 215 216 rv = 0; 217 218 NVRAM_LOCK(sc); 219 while (uio->uio_resid > 0) { 220 amnt = MIN(uio->uio_resid, sc->sc_size - uio->uio_offset); 221 amnt = MIN(amnt, NVRAM_BUFSIZE); 222 if (amnt == 0) 223 break; 224 225 rv = opal_call(OPAL_READ_NVRAM, sc->sc_buf_phys, 226 amnt, uio->uio_offset); 227 if (rv != OPAL_SUCCESS) { 228 switch (rv) { 229 case OPAL_HARDWARE: 230 rv = EIO; 231 break; 232 case OPAL_PARAMETER: 233 rv = EINVAL; 234 break; 235 } 236 break; 237 } 238 rv = uiomove(sc->sc_buf, amnt, uio); 239 if (rv != 0) 240 break; 241 } 242 NVRAM_UNLOCK(sc); 243 244 return (rv); 245 } 246 247 static int 248 opal_nvram_write(struct cdev *dev, struct uio *uio, int ioflag) 249 { 250 off_t offset; 251 int rv, amnt; 252 struct opal_nvram_softc *sc = dev->si_drv1; 253 254 rv = 0; 255 256 NVRAM_LOCK(sc); 257 while (uio->uio_resid > 0) { 258 amnt = MIN(uio->uio_resid, sc->sc_size - uio->uio_offset); 259 amnt = MIN(amnt, NVRAM_BUFSIZE); 260 if (amnt == 0) { 261 rv = ENOSPC; 262 break; 263 } 264 offset = uio->uio_offset; 265 rv = uiomove(sc->sc_buf, amnt, uio); 266 if (rv != 0) 267 break; 268 rv = opal_call(OPAL_WRITE_NVRAM, sc->sc_buf_phys, amnt, 269 offset); 270 if (rv != OPAL_SUCCESS) { 271 switch (rv) { 272 case OPAL_HARDWARE: 273 rv = EIO; 274 break; 275 case OPAL_PARAMETER: 276 rv = EINVAL; 277 break; 278 } 279 break; 280 } 281 } 282 283 NVRAM_UNLOCK(sc); 284 285 return (rv); 286 } 287 288 static int 289 opal_nvram_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, 290 struct thread *td) 291 { 292 struct opal_nvram_softc *sc = dev->si_drv1; 293 294 switch (cmd) { 295 case DIOCGMEDIASIZE: 296 *(off_t *)data = sc->sc_size; 297 return (0); 298 } 299 return (EINVAL); 300 } 301