1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 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 28 #include <sys/param.h> 29 #include <sys/systm.h> 30 #include <sys/module.h> 31 #include <sys/bus.h> 32 #include <sys/conf.h> 33 #include <sys/disk.h> 34 #include <sys/kernel.h> 35 #include <sys/mutex.h> 36 #include <sys/uio.h> 37 38 #include <dev/ofw/openfirm.h> 39 #include <dev/ofw/ofw_bus.h> 40 #include <dev/ofw/ofw_bus_subr.h> 41 42 #include <machine/bus.h> 43 #include <machine/md_var.h> 44 #include <machine/pio.h> 45 #include <machine/resource.h> 46 47 #include "opal.h" 48 49 #include <sys/rman.h> 50 51 #include <vm/vm.h> 52 #include <vm/pmap.h> 53 54 #define NVRAM_BUFSIZE (65536) /* 64k blocks */ 55 56 struct opal_nvram_softc { 57 device_t sc_dev; 58 struct mtx sc_mtx; 59 uint32_t sc_size; 60 uint8_t *sc_buf; 61 vm_paddr_t sc_buf_phys; 62 63 struct cdev *sc_cdev; 64 int sc_isopen; 65 }; 66 67 #define NVRAM_LOCK(sc) mtx_lock(&sc->sc_mtx) 68 #define NVRAM_UNLOCK(sc) mtx_unlock(&sc->sc_mtx) 69 70 /* 71 * Device interface. 72 */ 73 static int opal_nvram_probe(device_t); 74 static int opal_nvram_attach(device_t); 75 static int opal_nvram_detach(device_t); 76 77 /* 78 * Driver methods. 79 */ 80 static device_method_t opal_nvram_methods[] = { 81 /* Device interface */ 82 DEVMETHOD(device_probe, opal_nvram_probe), 83 DEVMETHOD(device_attach, opal_nvram_attach), 84 DEVMETHOD(device_detach, opal_nvram_detach), 85 { 0, 0 } 86 }; 87 88 static driver_t opal_nvram_driver = { 89 "opal_nvram", 90 opal_nvram_methods, 91 sizeof(struct opal_nvram_softc) 92 }; 93 94 DRIVER_MODULE(opal_nvram, opal, opal_nvram_driver, 0, 0); 95 96 /* 97 * Cdev methods. 98 */ 99 100 static d_open_t opal_nvram_open; 101 static d_close_t opal_nvram_close; 102 static d_read_t opal_nvram_read; 103 static d_write_t opal_nvram_write; 104 static d_ioctl_t opal_nvram_ioctl; 105 106 static struct cdevsw opal_nvram_cdevsw = { 107 .d_version = D_VERSION, 108 .d_open = opal_nvram_open, 109 .d_close = opal_nvram_close, 110 .d_read = opal_nvram_read, 111 .d_write = opal_nvram_write, 112 .d_ioctl = opal_nvram_ioctl, 113 .d_name = "nvram", 114 }; 115 116 static int 117 opal_nvram_probe(device_t dev) 118 { 119 120 if (!ofw_bus_is_compatible(dev, "ibm,opal-nvram")) 121 return (ENXIO); 122 123 device_set_desc(dev, "OPAL NVRAM"); 124 return (BUS_PROBE_DEFAULT); 125 } 126 127 static int 128 opal_nvram_attach(device_t dev) 129 { 130 struct opal_nvram_softc *sc; 131 phandle_t node; 132 int err; 133 134 node = ofw_bus_get_node(dev); 135 sc = device_get_softc(dev); 136 137 sc->sc_dev = dev; 138 139 err = OF_getencprop(node, "#bytes", &sc->sc_size, 140 sizeof(sc->sc_size)); 141 142 if (err < 0) 143 return (ENXIO); 144 145 sc->sc_buf = contigmalloc(NVRAM_BUFSIZE, M_DEVBUF, M_WAITOK, 146 0, BUS_SPACE_MAXADDR, PAGE_SIZE, 0); 147 if (sc->sc_buf == NULL) { 148 device_printf(dev, "No memory for buffer.\n"); 149 return (ENXIO); 150 } 151 sc->sc_buf_phys = pmap_kextract((vm_offset_t)sc->sc_buf); 152 sc->sc_cdev = make_dev(&opal_nvram_cdevsw, 0, 0, 0, 0600, 153 "nvram"); 154 sc->sc_cdev->si_drv1 = sc; 155 156 mtx_init(&sc->sc_mtx, "opal_nvram", 0, MTX_DEF); 157 158 return (0); 159 } 160 161 static int 162 opal_nvram_detach(device_t dev) 163 { 164 struct opal_nvram_softc *sc; 165 166 sc = device_get_softc(dev); 167 168 if (sc->sc_cdev != NULL) 169 destroy_dev(sc->sc_cdev); 170 if (sc->sc_buf != NULL) 171 contigfree(sc->sc_buf, NVRAM_BUFSIZE, M_DEVBUF); 172 173 mtx_destroy(&sc->sc_mtx); 174 175 return (0); 176 } 177 178 static int 179 opal_nvram_open(struct cdev *dev, int flags, int fmt, struct thread *td) 180 { 181 struct opal_nvram_softc *sc = dev->si_drv1; 182 int err; 183 184 err = 0; 185 186 NVRAM_LOCK(sc); 187 if (sc->sc_isopen) 188 err = EBUSY; 189 else 190 sc->sc_isopen = 1; 191 NVRAM_UNLOCK(sc); 192 193 return (err); 194 } 195 196 static int 197 opal_nvram_close(struct cdev *dev, int fflag, int devtype, struct thread *td) 198 { 199 struct opal_nvram_softc *sc = dev->si_drv1; 200 201 NVRAM_LOCK(sc); 202 sc->sc_isopen = 0; 203 NVRAM_UNLOCK(sc); 204 205 return (0); 206 } 207 208 static int 209 opal_nvram_read(struct cdev *dev, struct uio *uio, int ioflag) 210 { 211 struct opal_nvram_softc *sc = dev->si_drv1; 212 int rv, amnt; 213 214 rv = 0; 215 216 NVRAM_LOCK(sc); 217 while (uio->uio_resid > 0) { 218 amnt = MIN(uio->uio_resid, sc->sc_size - uio->uio_offset); 219 amnt = MIN(amnt, NVRAM_BUFSIZE); 220 if (amnt == 0) 221 break; 222 223 rv = opal_call(OPAL_READ_NVRAM, sc->sc_buf_phys, 224 amnt, uio->uio_offset); 225 if (rv != OPAL_SUCCESS) { 226 switch (rv) { 227 case OPAL_HARDWARE: 228 rv = EIO; 229 break; 230 case OPAL_PARAMETER: 231 rv = EINVAL; 232 break; 233 } 234 break; 235 } 236 rv = uiomove(sc->sc_buf, amnt, uio); 237 if (rv != 0) 238 break; 239 } 240 NVRAM_UNLOCK(sc); 241 242 return (rv); 243 } 244 245 static int 246 opal_nvram_write(struct cdev *dev, struct uio *uio, int ioflag) 247 { 248 off_t offset; 249 int rv, amnt; 250 struct opal_nvram_softc *sc = dev->si_drv1; 251 252 rv = 0; 253 254 NVRAM_LOCK(sc); 255 while (uio->uio_resid > 0) { 256 amnt = MIN(uio->uio_resid, sc->sc_size - uio->uio_offset); 257 amnt = MIN(amnt, NVRAM_BUFSIZE); 258 if (amnt == 0) { 259 rv = ENOSPC; 260 break; 261 } 262 offset = uio->uio_offset; 263 rv = uiomove(sc->sc_buf, amnt, uio); 264 if (rv != 0) 265 break; 266 rv = opal_call(OPAL_WRITE_NVRAM, sc->sc_buf_phys, amnt, 267 offset); 268 if (rv != OPAL_SUCCESS) { 269 switch (rv) { 270 case OPAL_HARDWARE: 271 rv = EIO; 272 break; 273 case OPAL_PARAMETER: 274 rv = EINVAL; 275 break; 276 } 277 break; 278 } 279 } 280 281 NVRAM_UNLOCK(sc); 282 283 return (rv); 284 } 285 286 static int 287 opal_nvram_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, 288 struct thread *td) 289 { 290 struct opal_nvram_softc *sc = dev->si_drv1; 291 292 switch (cmd) { 293 case DIOCGMEDIASIZE: 294 *(off_t *)data = sc->sc_size; 295 return (0); 296 } 297 return (EINVAL); 298 } 299