/* $NetBSD: virtio_mmio_cmdline.c,v 1.1 2025/01/15 13:16:23 imil Exp $ */ /*- * Copyright (c) 2025 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Emile 'iMil' Heitor. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /*- * Copyright (c) 2022 Colin Percival * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include #define VIRTIO_PRIVATE #include #include #include #include #include "ioapic.h" #define VMMIOSTR "virtio_mmio.device=" struct mmio_args { uint64_t sz; uint64_t baseaddr; uint64_t irq; uint64_t id; }; struct virtio_mmio_cmdline_softc { struct virtio_mmio_softc sc_msc; struct mmio_args margs; }; static int virtio_mmio_cmdline_match(device_t, cfdata_t, void *); static void virtio_mmio_cmdline_attach(device_t, device_t, void *); static int virtio_mmio_cmdline_do_attach(device_t, struct pv_attach_args *, struct mmio_args *); static int virtio_mmio_cmdline_detach(device_t, int); static int virtio_mmio_cmdline_rescan(device_t, const char *, const int *); static int virtio_mmio_cmdline_alloc_interrupts(struct virtio_mmio_softc *); static void virtio_mmio_cmdline_free_interrupts(struct virtio_mmio_softc *); CFATTACH_DECL3_NEW(mmio_cmdline, sizeof(struct virtio_mmio_cmdline_softc), virtio_mmio_cmdline_match, virtio_mmio_cmdline_attach, virtio_mmio_cmdline_detach, NULL, virtio_mmio_cmdline_rescan, NULL, 0); static int virtio_mmio_cmdline_match(device_t parent, cfdata_t match, void *aux) { if (strstr(xen_start_info.cmd_line, VMMIOSTR) == NULL) return 0; return 1; } static void parsearg(struct mmio_args *margs, const char *arg) { char *p; /* */ margs->sz = strtoull(arg, (char **)&p, 0); if ((margs->sz == 0) || (margs->sz == UINT64_MAX)) goto bad; switch (*p) { case 'E': case 'e': /* Check for overflow */ if (margs->sz > (UINT64_MAX >> 60)) goto bad; margs->sz <<= 10; /* FALLTHROUGH */ case 'P': case 'p': if (margs->sz > (UINT64_MAX >> 50)) goto bad; margs->sz <<= 10; /* FALLTHROUGH */ case 'T': case 't': if (margs->sz > (UINT64_MAX >> 40)) goto bad; margs->sz <<= 10; /* FALLTHROUGH */ case 'G': case 'g': if (margs->sz > (UINT64_MAX >> 30)) goto bad; margs->sz <<= 10; /* FALLTHROUGH */ case 'M': case 'm': if (margs->sz > (UINT64_MAX >> 20)) goto bad; margs->sz <<= 10; /* FALLTHROUGH */ case 'K': case 'k': if (margs->sz > (UINT64_MAX >> 10)) goto bad; margs->sz <<= 10; p++; break; } /* @ */ if (*p++ != '@') goto bad; margs->baseaddr = strtoull(p, (char **)&p, 0); if ((margs->baseaddr == 0) || (margs->baseaddr == UINT64_MAX)) goto bad; /* : */ if (*p++ != ':') goto bad; margs->irq = strtoull(p, (char **)&p, 0); if ((margs->irq == 0) || (margs->irq == UINT64_MAX)) goto bad; /* Optionally, : */ if (*p) { if (*p++ != ':') goto bad; margs->id = strtoull(p, (char **)&p, 0); if ((margs->id == 0) || (margs->id == UINT64_MAX)) goto bad; } else { margs->id = 0; } /* Should have reached the end of the string. */ if (*p) goto bad; return; bad: aprint_error("Error parsing virtio_mmio parameter: %s\n", arg); } static void virtio_mmio_cmdline_attach(device_t parent, device_t self, void *aux) { struct virtio_mmio_cmdline_softc *sc = device_private(self); struct pv_attach_args *pvaa = aux; struct mmio_args *margs = &sc->margs; int keylen = strlen(VMMIOSTR); char *next; static char cmdline[LINE_MAX], *parg = NULL; aprint_normal("\n"); aprint_naive("\n"); if (parg == NULL) { /* first pass */ strlcpy(cmdline, xen_start_info.cmd_line, sizeof(cmdline)); aprint_verbose_dev(self, "kernel parameters: %s\n", cmdline); parg = strstr(cmdline, VMMIOSTR); } if (parg != NULL) { parg += keylen; if (!*parg) return; next = parg; while (*next && *next != ' ') /* find end of argument */ next++; if (*next) { /* space */ *next++ = '\0'; /* end the argument string */ next = strstr(next, VMMIOSTR); } aprint_normal_dev(self, "viommio: %s\n", parg); parsearg(margs, parg); if (virtio_mmio_cmdline_do_attach(self, pvaa, margs)) return; if (next) { parg = next; config_found(parent, pvaa, NULL, CFARGS_NONE); } } } static int virtio_mmio_cmdline_do_attach(device_t self, struct pv_attach_args *pvaa, struct mmio_args *margs) { struct virtio_mmio_cmdline_softc *sc = device_private(self); struct virtio_mmio_softc *const msc = &sc->sc_msc; struct virtio_softc *const vsc = &msc->sc_sc; int error; msc->sc_iot = pvaa->pvaa_memt; vsc->sc_dmat = pvaa->pvaa_dmat; msc->sc_iosize = margs->sz; vsc->sc_dev = self; error = bus_space_map(msc->sc_iot, margs->baseaddr, margs->sz, 0, &msc->sc_ioh); if (error) { aprint_error_dev(self, "couldn't map %#" PRIx64 ": %d", margs->baseaddr, error); return error; } msc->sc_alloc_interrupts = virtio_mmio_cmdline_alloc_interrupts; msc->sc_free_interrupts = virtio_mmio_cmdline_free_interrupts; virtio_mmio_common_attach(msc); virtio_mmio_cmdline_rescan(self, "virtio", NULL); return 0; } static int virtio_mmio_cmdline_detach(device_t self, int flags) { struct virtio_mmio_cmdline_softc * const sc = device_private(self); struct virtio_mmio_softc * const msc = &sc->sc_msc; return virtio_mmio_common_detach(msc, flags); } static int virtio_mmio_cmdline_rescan(device_t self, const char *ifattr, const int *locs) { struct virtio_mmio_cmdline_softc *const sc = device_private(self); struct virtio_mmio_softc *const msc = &sc->sc_msc; struct virtio_softc *const vsc = &msc->sc_sc; struct virtio_attach_args va; if (vsc->sc_child) return 0; memset(&va, 0, sizeof(va)); va.sc_childdevid = vsc->sc_childdevid; config_found(self, &va, NULL, CFARGS_NONE); if (virtio_attach_failed(vsc)) return 0; return 0; } static int virtio_mmio_cmdline_alloc_interrupts(struct virtio_mmio_softc *msc) { struct virtio_mmio_cmdline_softc *const sc = (struct virtio_mmio_cmdline_softc *)msc; struct virtio_softc *const vsc = &msc->sc_sc; struct ioapic_softc *ioapic; struct pic *pic; int irq = sc->margs.irq; int pin = irq; bool mpsafe; ioapic = ioapic_find_bybase(irq); if (ioapic != NULL) { KASSERT(ioapic->sc_pic.pic_type == PIC_IOAPIC); pic = &ioapic->sc_pic; pin = irq - pic->pic_vecbase; irq = -1; } else pic = &i8259_pic; mpsafe = (0 != (vsc->sc_flags & VIRTIO_F_INTR_MPSAFE)); msc->sc_ih = intr_establish_xname(irq, pic, pin, IST_LEVEL, vsc->sc_ipl, virtio_mmio_intr, msc, mpsafe, device_xname(vsc->sc_dev)); if (msc->sc_ih == NULL) { aprint_error_dev(vsc->sc_dev, "failed to establish interrupt\n"); return -1; } aprint_normal_dev(vsc->sc_dev, "interrupting on %d\n", irq); return 0; } static void virtio_mmio_cmdline_free_interrupts(struct virtio_mmio_softc *msc) { if (msc->sc_ih != NULL) { intr_disestablish(msc->sc_ih); msc->sc_ih = NULL; } }