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