Home | History | Annotate | Line # | Download | only in x86
      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