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 free(sc->sc_buf, M_DEVBUF); 171 172 mtx_destroy(&sc->sc_mtx); 173 174 return (0); 175 } 176 177 static int 178 opal_nvram_open(struct cdev *dev, int flags, int fmt, struct thread *td) 179 { 180 struct opal_nvram_softc *sc = dev->si_drv1; 181 int err; 182 183 err = 0; 184 185 NVRAM_LOCK(sc); 186 if (sc->sc_isopen) 187 err = EBUSY; 188 else 189 sc->sc_isopen = 1; 190 NVRAM_UNLOCK(sc); 191 192 return (err); 193 } 194 195 static int 196 opal_nvram_close(struct cdev *dev, int fflag, int devtype, struct thread *td) 197 { 198 struct opal_nvram_softc *sc = dev->si_drv1; 199 200 NVRAM_LOCK(sc); 201 sc->sc_isopen = 0; 202 NVRAM_UNLOCK(sc); 203 204 return (0); 205 } 206 207 static int 208 opal_nvram_read(struct cdev *dev, struct uio *uio, int ioflag) 209 { 210 struct opal_nvram_softc *sc = dev->si_drv1; 211 int rv, amnt; 212 213 rv = 0; 214 215 NVRAM_LOCK(sc); 216 while (uio->uio_resid > 0) { 217 amnt = MIN(uio->uio_resid, sc->sc_size - uio->uio_offset); 218 amnt = MIN(amnt, NVRAM_BUFSIZE); 219 if (amnt == 0) 220 break; 221 222 rv = opal_call(OPAL_READ_NVRAM, sc->sc_buf_phys, 223 amnt, uio->uio_offset); 224 if (rv != OPAL_SUCCESS) { 225 switch (rv) { 226 case OPAL_HARDWARE: 227 rv = EIO; 228 break; 229 case OPAL_PARAMETER: 230 rv = EINVAL; 231 break; 232 } 233 break; 234 } 235 rv = uiomove(sc->sc_buf, amnt, uio); 236 if (rv != 0) 237 break; 238 } 239 NVRAM_UNLOCK(sc); 240 241 return (rv); 242 } 243 244 static int 245 opal_nvram_write(struct cdev *dev, struct uio *uio, int ioflag) 246 { 247 off_t offset; 248 int rv, amnt; 249 struct opal_nvram_softc *sc = dev->si_drv1; 250 251 rv = 0; 252 253 NVRAM_LOCK(sc); 254 while (uio->uio_resid > 0) { 255 amnt = MIN(uio->uio_resid, sc->sc_size - uio->uio_offset); 256 amnt = MIN(amnt, NVRAM_BUFSIZE); 257 if (amnt == 0) { 258 rv = ENOSPC; 259 break; 260 } 261 offset = uio->uio_offset; 262 rv = uiomove(sc->sc_buf, amnt, uio); 263 if (rv != 0) 264 break; 265 rv = opal_call(OPAL_WRITE_NVRAM, sc->sc_buf_phys, amnt, 266 offset); 267 if (rv != OPAL_SUCCESS) { 268 switch (rv) { 269 case OPAL_HARDWARE: 270 rv = EIO; 271 break; 272 case OPAL_PARAMETER: 273 rv = EINVAL; 274 break; 275 } 276 break; 277 } 278 } 279 280 NVRAM_UNLOCK(sc); 281 282 return (rv); 283 } 284 285 static int 286 opal_nvram_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, 287 struct thread *td) 288 { 289 struct opal_nvram_softc *sc = dev->si_drv1; 290 291 switch (cmd) { 292 case DIOCGMEDIASIZE: 293 *(off_t *)data = sc->sc_size; 294 return (0); 295 } 296 return (EINVAL); 297 } 298