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