1 1.1 imil /* $NetBSD: virtio_mmio_cmdline.c,v 1.1 2025/01/15 13:16:23 imil Exp $ */ 2 1.1 imil 3 1.1 imil /*- 4 1.1 imil * Copyright (c) 2025 The NetBSD Foundation, Inc. 5 1.1 imil * All rights reserved. 6 1.1 imil * 7 1.1 imil * This code is derived from software contributed to The NetBSD Foundation 8 1.1 imil * by Emile 'iMil' Heitor. 9 1.1 imil * 10 1.1 imil * Redistribution and use in source and binary forms, with or without 11 1.1 imil * modification, are permitted provided that the following conditions 12 1.1 imil * are met: 13 1.1 imil * 1. Redistributions of source code must retain the above copyright 14 1.1 imil * notice, this list of conditions and the following disclaimer. 15 1.1 imil * 2. Redistributions in binary form must reproduce the above copyright 16 1.1 imil * notice, this list of conditions and the following disclaimer in the 17 1.1 imil * documentation and/or other materials provided with the distribution. 18 1.1 imil * 19 1.1 imil * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 1.1 imil * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 1.1 imil * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 1.1 imil * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 1.1 imil * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 1.1 imil * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 1.1 imil * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 1.1 imil * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 1.1 imil * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 1.1 imil * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 1.1 imil * POSSIBILITY OF SUCH DAMAGE. 30 1.1 imil */ 31 1.1 imil 32 1.1 imil /*- 33 1.1 imil * Copyright (c) 2022 Colin Percival 34 1.1 imil * 35 1.1 imil * Redistribution and use in source and binary forms, with or without 36 1.1 imil * modification, are permitted provided that the following conditions 37 1.1 imil * are met: 38 1.1 imil * 1. Redistributions of source code must retain the above copyright 39 1.1 imil * notice, this list of conditions and the following disclaimer. 40 1.1 imil * 2. Redistributions in binary form must reproduce the above copyright 41 1.1 imil * notice, this list of conditions and the following disclaimer in the 42 1.1 imil * documentation and/or other materials provided with the distribution. 43 1.1 imil * 44 1.1 imil * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 45 1.1 imil * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 46 1.1 imil * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 47 1.1 imil * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 48 1.1 imil * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 49 1.1 imil * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 50 1.1 imil * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 51 1.1 imil * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 52 1.1 imil * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 53 1.1 imil * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 54 1.1 imil * SUCH DAMAGE. 55 1.1 imil */ 56 1.1 imil 57 1.1 imil #include <sys/param.h> 58 1.1 imil #include <sys/bus.h> 59 1.1 imil #include <sys/device.h> 60 1.1 imil #include <sys/kernel.h> 61 1.1 imil #include <sys/module.h> 62 1.1 imil #include <sys/systm.h> 63 1.1 imil 64 1.1 imil #define VIRTIO_PRIVATE 65 1.1 imil #include <dev/virtio/virtio_mmiovar.h> 66 1.1 imil #include <arch/x86/pv/pvvar.h> 67 1.1 imil #include <xen/hypervisor.h> 68 1.1 imil 69 1.1 imil #include <machine/i82093var.h> 70 1.1 imil #include "ioapic.h" 71 1.1 imil 72 1.1 imil #define VMMIOSTR "virtio_mmio.device=" 73 1.1 imil 74 1.1 imil struct mmio_args { 75 1.1 imil uint64_t sz; 76 1.1 imil uint64_t baseaddr; 77 1.1 imil uint64_t irq; 78 1.1 imil uint64_t id; 79 1.1 imil }; 80 1.1 imil 81 1.1 imil struct virtio_mmio_cmdline_softc { 82 1.1 imil struct virtio_mmio_softc sc_msc; 83 1.1 imil struct mmio_args margs; 84 1.1 imil }; 85 1.1 imil 86 1.1 imil static int virtio_mmio_cmdline_match(device_t, cfdata_t, void *); 87 1.1 imil static void virtio_mmio_cmdline_attach(device_t, device_t, void *); 88 1.1 imil static int virtio_mmio_cmdline_do_attach(device_t, 89 1.1 imil struct pv_attach_args *, struct mmio_args *); 90 1.1 imil static int virtio_mmio_cmdline_detach(device_t, int); 91 1.1 imil static int virtio_mmio_cmdline_rescan(device_t, const char *, const int *); 92 1.1 imil static int virtio_mmio_cmdline_alloc_interrupts(struct virtio_mmio_softc *); 93 1.1 imil static void virtio_mmio_cmdline_free_interrupts(struct virtio_mmio_softc *); 94 1.1 imil 95 1.1 imil CFATTACH_DECL3_NEW(mmio_cmdline, 96 1.1 imil sizeof(struct virtio_mmio_cmdline_softc), 97 1.1 imil virtio_mmio_cmdline_match, virtio_mmio_cmdline_attach, 98 1.1 imil virtio_mmio_cmdline_detach, NULL, 99 1.1 imil virtio_mmio_cmdline_rescan, NULL, 0); 100 1.1 imil 101 1.1 imil static int 102 1.1 imil virtio_mmio_cmdline_match(device_t parent, cfdata_t match, void *aux) 103 1.1 imil { 104 1.1 imil if (strstr(xen_start_info.cmd_line, VMMIOSTR) == NULL) 105 1.1 imil return 0; 106 1.1 imil 107 1.1 imil return 1; 108 1.1 imil } 109 1.1 imil 110 1.1 imil static void 111 1.1 imil parsearg(struct mmio_args *margs, const char *arg) 112 1.1 imil { 113 1.1 imil char *p; 114 1.1 imil 115 1.1 imil /* <size> */ 116 1.1 imil margs->sz = strtoull(arg, (char **)&p, 0); 117 1.1 imil if ((margs->sz == 0) || (margs->sz == UINT64_MAX)) 118 1.1 imil goto bad; 119 1.1 imil switch (*p) { 120 1.1 imil case 'E': case 'e': 121 1.1 imil /* Check for overflow */ 122 1.1 imil if (margs->sz > (UINT64_MAX >> 60)) 123 1.1 imil goto bad; 124 1.1 imil margs->sz <<= 10; 125 1.1 imil /* FALLTHROUGH */ 126 1.1 imil case 'P': case 'p': 127 1.1 imil if (margs->sz > (UINT64_MAX >> 50)) 128 1.1 imil goto bad; 129 1.1 imil margs->sz <<= 10; 130 1.1 imil /* FALLTHROUGH */ 131 1.1 imil case 'T': case 't': 132 1.1 imil if (margs->sz > (UINT64_MAX >> 40)) 133 1.1 imil goto bad; 134 1.1 imil margs->sz <<= 10; 135 1.1 imil /* FALLTHROUGH */ 136 1.1 imil case 'G': case 'g': 137 1.1 imil if (margs->sz > (UINT64_MAX >> 30)) 138 1.1 imil goto bad; 139 1.1 imil margs->sz <<= 10; 140 1.1 imil /* FALLTHROUGH */ 141 1.1 imil case 'M': case 'm': 142 1.1 imil if (margs->sz > (UINT64_MAX >> 20)) 143 1.1 imil goto bad; 144 1.1 imil margs->sz <<= 10; 145 1.1 imil /* FALLTHROUGH */ 146 1.1 imil case 'K': case 'k': 147 1.1 imil if (margs->sz > (UINT64_MAX >> 10)) 148 1.1 imil goto bad; 149 1.1 imil margs->sz <<= 10; 150 1.1 imil p++; 151 1.1 imil break; 152 1.1 imil } 153 1.1 imil 154 1.1 imil /* @<baseaddr> */ 155 1.1 imil if (*p++ != '@') 156 1.1 imil goto bad; 157 1.1 imil margs->baseaddr = strtoull(p, (char **)&p, 0); 158 1.1 imil if ((margs->baseaddr == 0) || (margs->baseaddr == UINT64_MAX)) 159 1.1 imil goto bad; 160 1.1 imil 161 1.1 imil /* :<irq> */ 162 1.1 imil if (*p++ != ':') 163 1.1 imil goto bad; 164 1.1 imil margs->irq = strtoull(p, (char **)&p, 0); 165 1.1 imil if ((margs->irq == 0) || (margs->irq == UINT64_MAX)) 166 1.1 imil goto bad; 167 1.1 imil 168 1.1 imil /* Optionally, :<id> */ 169 1.1 imil if (*p) { 170 1.1 imil if (*p++ != ':') 171 1.1 imil goto bad; 172 1.1 imil margs->id = strtoull(p, (char **)&p, 0); 173 1.1 imil if ((margs->id == 0) || (margs->id == UINT64_MAX)) 174 1.1 imil goto bad; 175 1.1 imil } else { 176 1.1 imil margs->id = 0; 177 1.1 imil } 178 1.1 imil 179 1.1 imil /* Should have reached the end of the string. */ 180 1.1 imil if (*p) 181 1.1 imil goto bad; 182 1.1 imil 183 1.1 imil return; 184 1.1 imil 185 1.1 imil bad: 186 1.1 imil aprint_error("Error parsing virtio_mmio parameter: %s\n", arg); 187 1.1 imil } 188 1.1 imil 189 1.1 imil static void 190 1.1 imil virtio_mmio_cmdline_attach(device_t parent, device_t self, void *aux) 191 1.1 imil { 192 1.1 imil struct virtio_mmio_cmdline_softc *sc = device_private(self); 193 1.1 imil struct pv_attach_args *pvaa = aux; 194 1.1 imil struct mmio_args *margs = &sc->margs; 195 1.1 imil int keylen = strlen(VMMIOSTR); 196 1.1 imil char *next; 197 1.1 imil static char cmdline[LINE_MAX], *parg = NULL; 198 1.1 imil 199 1.1 imil aprint_normal("\n"); 200 1.1 imil aprint_naive("\n"); 201 1.1 imil 202 1.1 imil if (parg == NULL) { /* first pass */ 203 1.1 imil strlcpy(cmdline, xen_start_info.cmd_line, sizeof(cmdline)); 204 1.1 imil aprint_verbose_dev(self, "kernel parameters: %s\n", 205 1.1 imil cmdline); 206 1.1 imil parg = strstr(cmdline, VMMIOSTR); 207 1.1 imil } 208 1.1 imil 209 1.1 imil if (parg != NULL) { 210 1.1 imil parg += keylen; 211 1.1 imil if (!*parg) 212 1.1 imil return; 213 1.1 imil 214 1.1 imil next = parg; 215 1.1 imil while (*next && *next != ' ') /* find end of argument */ 216 1.1 imil next++; 217 1.1 imil if (*next) { /* space */ 218 1.1 imil *next++ = '\0'; /* end the argument string */ 219 1.1 imil next = strstr(next, VMMIOSTR); 220 1.1 imil } 221 1.1 imil 222 1.1 imil aprint_normal_dev(self, "viommio: %s\n", parg); 223 1.1 imil parsearg(margs, parg); 224 1.1 imil 225 1.1 imil if (virtio_mmio_cmdline_do_attach(self, pvaa, margs)) 226 1.1 imil return; 227 1.1 imil 228 1.1 imil if (next) { 229 1.1 imil parg = next; 230 1.1 imil config_found(parent, pvaa, NULL, CFARGS_NONE); 231 1.1 imil } 232 1.1 imil } 233 1.1 imil } 234 1.1 imil 235 1.1 imil static int 236 1.1 imil virtio_mmio_cmdline_do_attach(device_t self, 237 1.1 imil struct pv_attach_args *pvaa, 238 1.1 imil struct mmio_args *margs) 239 1.1 imil { 240 1.1 imil struct virtio_mmio_cmdline_softc *sc = device_private(self); 241 1.1 imil struct virtio_mmio_softc *const msc = &sc->sc_msc; 242 1.1 imil struct virtio_softc *const vsc = &msc->sc_sc; 243 1.1 imil int error; 244 1.1 imil 245 1.1 imil msc->sc_iot = pvaa->pvaa_memt; 246 1.1 imil vsc->sc_dmat = pvaa->pvaa_dmat; 247 1.1 imil msc->sc_iosize = margs->sz; 248 1.1 imil vsc->sc_dev = self; 249 1.1 imil 250 1.1 imil error = bus_space_map(msc->sc_iot, margs->baseaddr, margs->sz, 0, 251 1.1 imil &msc->sc_ioh); 252 1.1 imil if (error) { 253 1.1 imil aprint_error_dev(self, "couldn't map %#" PRIx64 ": %d", 254 1.1 imil margs->baseaddr, error); 255 1.1 imil return error; 256 1.1 imil } 257 1.1 imil 258 1.1 imil msc->sc_alloc_interrupts = virtio_mmio_cmdline_alloc_interrupts; 259 1.1 imil msc->sc_free_interrupts = virtio_mmio_cmdline_free_interrupts; 260 1.1 imil 261 1.1 imil virtio_mmio_common_attach(msc); 262 1.1 imil virtio_mmio_cmdline_rescan(self, "virtio", NULL); 263 1.1 imil 264 1.1 imil return 0; 265 1.1 imil } 266 1.1 imil 267 1.1 imil static int 268 1.1 imil virtio_mmio_cmdline_detach(device_t self, int flags) 269 1.1 imil { 270 1.1 imil struct virtio_mmio_cmdline_softc * const sc = device_private(self); 271 1.1 imil struct virtio_mmio_softc * const msc = &sc->sc_msc; 272 1.1 imil 273 1.1 imil return virtio_mmio_common_detach(msc, flags); 274 1.1 imil } 275 1.1 imil 276 1.1 imil static int 277 1.1 imil virtio_mmio_cmdline_rescan(device_t self, const char *ifattr, const int *locs) 278 1.1 imil { 279 1.1 imil struct virtio_mmio_cmdline_softc *const sc = device_private(self); 280 1.1 imil struct virtio_mmio_softc *const msc = &sc->sc_msc; 281 1.1 imil struct virtio_softc *const vsc = &msc->sc_sc; 282 1.1 imil struct virtio_attach_args va; 283 1.1 imil 284 1.1 imil if (vsc->sc_child) 285 1.1 imil return 0; 286 1.1 imil 287 1.1 imil memset(&va, 0, sizeof(va)); 288 1.1 imil va.sc_childdevid = vsc->sc_childdevid; 289 1.1 imil 290 1.1 imil config_found(self, &va, NULL, CFARGS_NONE); 291 1.1 imil 292 1.1 imil if (virtio_attach_failed(vsc)) 293 1.1 imil return 0; 294 1.1 imil 295 1.1 imil return 0; 296 1.1 imil } 297 1.1 imil 298 1.1 imil 299 1.1 imil static int 300 1.1 imil virtio_mmio_cmdline_alloc_interrupts(struct virtio_mmio_softc *msc) 301 1.1 imil { 302 1.1 imil struct virtio_mmio_cmdline_softc *const sc = 303 1.1 imil (struct virtio_mmio_cmdline_softc *)msc; 304 1.1 imil struct virtio_softc *const vsc = &msc->sc_sc; 305 1.1 imil struct ioapic_softc *ioapic; 306 1.1 imil struct pic *pic; 307 1.1 imil int irq = sc->margs.irq; 308 1.1 imil int pin = irq; 309 1.1 imil bool mpsafe; 310 1.1 imil 311 1.1 imil ioapic = ioapic_find_bybase(irq); 312 1.1 imil 313 1.1 imil if (ioapic != NULL) { 314 1.1 imil KASSERT(ioapic->sc_pic.pic_type == PIC_IOAPIC); 315 1.1 imil pic = &ioapic->sc_pic; 316 1.1 imil pin = irq - pic->pic_vecbase; 317 1.1 imil irq = -1; 318 1.1 imil } else 319 1.1 imil pic = &i8259_pic; 320 1.1 imil 321 1.1 imil mpsafe = (0 != (vsc->sc_flags & VIRTIO_F_INTR_MPSAFE)); 322 1.1 imil 323 1.1 imil msc->sc_ih = intr_establish_xname(irq, pic, pin, IST_LEVEL, vsc->sc_ipl, 324 1.1 imil virtio_mmio_intr, msc, mpsafe, device_xname(vsc->sc_dev)); 325 1.1 imil if (msc->sc_ih == NULL) { 326 1.1 imil aprint_error_dev(vsc->sc_dev, 327 1.1 imil "failed to establish interrupt\n"); 328 1.1 imil return -1; 329 1.1 imil } 330 1.1 imil aprint_normal_dev(vsc->sc_dev, "interrupting on %d\n", irq); 331 1.1 imil 332 1.1 imil return 0; 333 1.1 imil } 334 1.1 imil 335 1.1 imil static void 336 1.1 imil virtio_mmio_cmdline_free_interrupts(struct virtio_mmio_softc *msc) 337 1.1 imil { 338 1.1 imil if (msc->sc_ih != NULL) { 339 1.1 imil intr_disestablish(msc->sc_ih); 340 1.1 imil msc->sc_ih = NULL; 341 1.1 imil } 342 1.1 imil } 343 1.1 imil 344