Home | History | Annotate | Line # | Download | only in virtio
virtio_mmio.c revision 1.2
      1 /*	$NetBSD: virtio_mmio.c,v 1.2 2018/06/15 17:13:43 jakllsch Exp $	*/
      2 /*	$OpenBSD: virtio_mmio.c,v 1.2 2017/02/24 17:12:31 patrick Exp $	*/
      3 
      4 /*
      5  * Copyright (c) 2014 Patrick Wildt <patrick (at) blueri.se>
      6  * Copyright (c) 2012 Stefan Fritsch.
      7  * Copyright (c) 2010 Minoura Makoto.
      8  * All rights reserved.
      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 AUTHOR ``AS IS'' AND ANY EXPRESS OR
     20  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     21  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     22  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     23  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     24  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     28  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     29  */
     30 
     31 #include <sys/cdefs.h>
     32 __KERNEL_RCSID(0, "$NetBSD: virtio_mmio.c,v 1.2 2018/06/15 17:13:43 jakllsch Exp $");
     33 
     34 #include <sys/param.h>
     35 #include <sys/systm.h>
     36 #include <sys/kernel.h>
     37 #include <sys/device.h>
     38 #include <sys/mutex.h>
     39 
     40 #define VIRTIO_PRIVATE
     41 #include <dev/virtio/virtio_mmiovar.h>
     42 
     43 #define VIRTIO_MMIO_MAGIC		('v' | 'i' << 8 | 'r' << 16 | 't' << 24)
     44 
     45 #define VIRTIO_MMIO_MAGIC_VALUE		0x000
     46 #define VIRTIO_MMIO_VERSION		0x004
     47 #define VIRTIO_MMIO_DEVICE_ID		0x008
     48 #define VIRTIO_MMIO_VENDOR_ID		0x00c
     49 #define VIRTIO_MMIO_HOST_FEATURES	0x010
     50 #define VIRTIO_MMIO_HOST_FEATURES_SEL	0x014
     51 #define VIRTIO_MMIO_GUEST_FEATURES	0x020
     52 #define VIRTIO_MMIO_GUEST_FEATURES_SEL	0x024
     53 #define VIRTIO_MMIO_GUEST_PAGE_SIZE	0x028
     54 #define VIRTIO_MMIO_QUEUE_SEL		0x030
     55 #define VIRTIO_MMIO_QUEUE_NUM_MAX	0x034
     56 #define VIRTIO_MMIO_QUEUE_NUM		0x038
     57 #define VIRTIO_MMIO_QUEUE_ALIGN		0x03c
     58 #define VIRTIO_MMIO_QUEUE_PFN		0x040
     59 #define VIRTIO_MMIO_QUEUE_NOTIFY	0x050
     60 #define VIRTIO_MMIO_INTERRUPT_STATUS	0x060
     61 #define VIRTIO_MMIO_INTERRUPT_ACK	0x064
     62 #define VIRTIO_MMIO_STATUS		0x070
     63 #define VIRTIO_MMIO_CONFIG		0x100
     64 
     65 #define VIRTIO_MMIO_INT_VRING		(1 << 0)
     66 #define VIRTIO_MMIO_INT_CONFIG		(1 << 1)
     67 
     68 /*
     69  * XXX: Before being used on big endian arches, the access to config registers
     70  * XXX: needs to be reviewed/fixed. The non-device specific registers are
     71  * XXX: PCI-endian while the device specific registers are native endian.
     72  */
     73 
     74 static void	virtio_mmio_kick(struct virtio_softc *, uint16_t);
     75 static uint8_t	virtio_mmio_read_device_config_1(struct virtio_softc *, int);
     76 static uint16_t	virtio_mmio_read_device_config_2(struct virtio_softc *, int);
     77 static uint32_t	virtio_mmio_read_device_config_4(struct virtio_softc *, int);
     78 static uint64_t	virtio_mmio_read_device_config_8(struct virtio_softc *, int);
     79 static void	virtio_mmio_write_device_config_1(struct virtio_softc *, int, uint8_t);
     80 static void	virtio_mmio_write_device_config_2(struct virtio_softc *, int, uint16_t);
     81 static void	virtio_mmio_write_device_config_4(struct virtio_softc *, int, uint32_t);
     82 static void	virtio_mmio_write_device_config_8(struct virtio_softc *, int, uint64_t);
     83 static uint16_t	virtio_mmio_read_queue_size(struct virtio_softc *, uint16_t);
     84 static void	virtio_mmio_setup_queue(struct virtio_softc *, uint16_t, uint32_t);
     85 static void	virtio_mmio_set_status(struct virtio_softc *, int);
     86 static uint32_t	virtio_mmio_negotiate_features(struct virtio_softc *, uint32_t);
     87 static int	virtio_mmio_setup_interrupts(struct virtio_softc *);
     88 static void	virtio_mmio_free_interrupts(struct virtio_softc *);
     89 
     90 static const struct virtio_ops virtio_mmio_ops = {
     91 	.kick = virtio_mmio_kick,
     92 	.read_dev_cfg_1 = virtio_mmio_read_device_config_1,
     93 	.read_dev_cfg_2 = virtio_mmio_read_device_config_2,
     94 	.read_dev_cfg_4 = virtio_mmio_read_device_config_4,
     95 	.read_dev_cfg_8 = virtio_mmio_read_device_config_8,
     96 	.write_dev_cfg_1 = virtio_mmio_write_device_config_1,
     97 	.write_dev_cfg_2 = virtio_mmio_write_device_config_2,
     98 	.write_dev_cfg_4 = virtio_mmio_write_device_config_4,
     99 	.write_dev_cfg_8 = virtio_mmio_write_device_config_8,
    100 	.read_queue_size = virtio_mmio_read_queue_size,
    101 	.setup_queue = virtio_mmio_setup_queue,
    102 	.set_status = virtio_mmio_set_status,
    103 	.neg_features = virtio_mmio_negotiate_features,
    104 	.setup_interrupts = virtio_mmio_setup_interrupts,
    105 	.free_interrupts = virtio_mmio_free_interrupts,
    106 };
    107 
    108 static uint16_t
    109 virtio_mmio_read_queue_size(struct virtio_softc *vsc, uint16_t idx)
    110 {
    111 	struct virtio_mmio_softc *sc = (struct virtio_mmio_softc *)vsc;
    112 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, VIRTIO_MMIO_QUEUE_SEL, idx);
    113 	return bus_space_read_4(sc->sc_iot, sc->sc_ioh,
    114 	    VIRTIO_MMIO_QUEUE_NUM_MAX);
    115 }
    116 
    117 static void
    118 virtio_mmio_setup_queue(struct virtio_softc *vsc, uint16_t idx, uint32_t addr)
    119 {
    120 	struct virtio_mmio_softc *sc = (struct virtio_mmio_softc *)vsc;
    121 
    122 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, VIRTIO_MMIO_QUEUE_SEL, idx);
    123 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, VIRTIO_MMIO_QUEUE_NUM,
    124 	    bus_space_read_4(sc->sc_iot, sc->sc_ioh, VIRTIO_MMIO_QUEUE_NUM_MAX));
    125 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, VIRTIO_MMIO_QUEUE_ALIGN,
    126 	    VIRTIO_PAGE_SIZE);
    127 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, VIRTIO_MMIO_QUEUE_PFN, addr);
    128 }
    129 
    130 static void
    131 virtio_mmio_set_status(struct virtio_softc *vsc, int status)
    132 {
    133 	struct virtio_mmio_softc *sc = (struct virtio_mmio_softc *)vsc;
    134 	int old = 0;
    135 
    136 	if (status != 0)
    137 		old = bus_space_read_4(sc->sc_iot, sc->sc_ioh,
    138 				       VIRTIO_MMIO_STATUS);
    139 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, VIRTIO_MMIO_STATUS,
    140 			  status|old);
    141 }
    142 
    143 void
    144 virtio_mmio_common_attach(struct virtio_mmio_softc *sc)
    145 {
    146 	struct virtio_softc *vsc = &sc->sc_sc;
    147 	uint32_t id, magic, ver;
    148 
    149 	magic = bus_space_read_4(sc->sc_iot, sc->sc_ioh,
    150 	    VIRTIO_MMIO_MAGIC_VALUE);
    151 	if (magic != VIRTIO_MMIO_MAGIC) {
    152 		aprint_error_dev(vsc->sc_dev,
    153 		    "wrong magic value 0x%08x; giving up\n", magic);
    154 		return;
    155 	}
    156 
    157 	ver = bus_space_read_4(sc->sc_iot, sc->sc_ioh, VIRTIO_MMIO_VERSION);
    158 	if (ver != 1) {
    159 		aprint_error_dev(vsc->sc_dev,
    160 		    "unknown version 0x%02x; giving up\n", ver);
    161 		return;
    162 	}
    163 
    164 	id = bus_space_read_4(sc->sc_iot, sc->sc_ioh, VIRTIO_MMIO_DEVICE_ID);
    165 
    166 	/* we could use PAGE_SIZE, but virtio(4) assumes 4KiB for now */
    167 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, VIRTIO_MMIO_GUEST_PAGE_SIZE,
    168 	    VIRTIO_PAGE_SIZE);
    169 
    170 	/* No device connected. */
    171 	if (id == 0)
    172 		return;
    173 
    174 	vsc->sc_ops = &virtio_mmio_ops;
    175 
    176 	virtio_device_reset(vsc);
    177 	virtio_mmio_set_status(vsc, VIRTIO_CONFIG_DEVICE_STATUS_ACK);
    178 	virtio_mmio_set_status(vsc, VIRTIO_CONFIG_DEVICE_STATUS_DRIVER);
    179 
    180 	/* XXX: use softc as aux... */
    181 	vsc->sc_childdevid = id;
    182 	vsc->sc_child = NULL;
    183 }
    184 
    185 int
    186 virtio_mmio_common_detach(struct virtio_mmio_softc *sc, int flags)
    187 {
    188 	struct virtio_softc *vsc = &sc->sc_sc;
    189 	int r;
    190 
    191 	if (vsc->sc_child != NULL && vsc->sc_child != VIRTIO_CHILD_FAILED) {
    192 		r = config_detach(vsc->sc_child, flags);
    193 		if (r)
    194 			return r;
    195 	}
    196 	KASSERT(vsc->sc_child == NULL || vsc->sc_child == VIRTIO_CHILD_FAILED);
    197 	KASSERT(vsc->sc_vqs == NULL);
    198 	KASSERT(sc->sc_ih == NULL);
    199 
    200 	if (sc->sc_iosize) {
    201 		bus_space_unmap(sc->sc_iot, sc->sc_ioh, sc->sc_iosize);
    202 		sc->sc_iosize = 0;
    203 	}
    204 
    205 	return 0;
    206 }
    207 
    208 /*
    209  * Feature negotiation.
    210  */
    211 static uint32_t
    212 virtio_mmio_negotiate_features(struct virtio_softc *vsc, uint32_t
    213     guest_features)
    214 {
    215 	struct virtio_mmio_softc *sc = (struct virtio_mmio_softc *)vsc;
    216 	uint32_t r;
    217 
    218 	bus_space_write_4(sc->sc_iot, sc->sc_ioh,
    219 	    VIRTIO_MMIO_HOST_FEATURES_SEL, 0);
    220 	r = bus_space_read_4(sc->sc_iot, sc->sc_ioh,
    221 				VIRTIO_MMIO_HOST_FEATURES);
    222 	r &= guest_features;
    223 	bus_space_write_4(sc->sc_iot, sc->sc_ioh,
    224 	    VIRTIO_MMIO_GUEST_FEATURES_SEL, 0);
    225 	bus_space_write_4(sc->sc_iot, sc->sc_ioh,
    226 			  VIRTIO_MMIO_GUEST_FEATURES, r);
    227 	return r;
    228 }
    229 
    230 /*
    231  * Device configuration registers.
    232  */
    233 static uint8_t
    234 virtio_mmio_read_device_config_1(struct virtio_softc *vsc, int index)
    235 {
    236 	struct virtio_mmio_softc *sc = (struct virtio_mmio_softc *)vsc;
    237 	return bus_space_read_1(sc->sc_iot, sc->sc_ioh,
    238 				VIRTIO_MMIO_CONFIG + index);
    239 }
    240 
    241 static uint16_t
    242 virtio_mmio_read_device_config_2(struct virtio_softc *vsc, int index)
    243 {
    244 	struct virtio_mmio_softc *sc = (struct virtio_mmio_softc *)vsc;
    245 	return bus_space_read_2(sc->sc_iot, sc->sc_ioh,
    246 				VIRTIO_MMIO_CONFIG + index);
    247 }
    248 
    249 static uint32_t
    250 virtio_mmio_read_device_config_4(struct virtio_softc *vsc, int index)
    251 {
    252 	struct virtio_mmio_softc *sc = (struct virtio_mmio_softc *)vsc;
    253 	return bus_space_read_4(sc->sc_iot, sc->sc_ioh,
    254 				VIRTIO_MMIO_CONFIG + index);
    255 }
    256 
    257 static uint64_t
    258 virtio_mmio_read_device_config_8(struct virtio_softc *vsc, int index)
    259 {
    260 	struct virtio_mmio_softc *sc = (struct virtio_mmio_softc *)vsc;
    261 	uint64_t r;
    262 
    263 	r = bus_space_read_4(sc->sc_iot, sc->sc_ioh,
    264 			     VIRTIO_MMIO_CONFIG + index + sizeof(uint32_t));
    265 	r <<= 32;
    266 	r += bus_space_read_4(sc->sc_iot, sc->sc_ioh,
    267 			      VIRTIO_MMIO_CONFIG + index);
    268 	return r;
    269 }
    270 
    271 static void
    272 virtio_mmio_write_device_config_1(struct virtio_softc *vsc,
    273 			     int index, uint8_t value)
    274 {
    275 	struct virtio_mmio_softc *sc = (struct virtio_mmio_softc *)vsc;
    276 	bus_space_write_1(sc->sc_iot, sc->sc_ioh,
    277 			  VIRTIO_MMIO_CONFIG + index, value);
    278 }
    279 
    280 static void
    281 virtio_mmio_write_device_config_2(struct virtio_softc *vsc,
    282 			     int index, uint16_t value)
    283 {
    284 	struct virtio_mmio_softc *sc = (struct virtio_mmio_softc *)vsc;
    285 	bus_space_write_2(sc->sc_iot, sc->sc_ioh,
    286 			  VIRTIO_MMIO_CONFIG + index, value);
    287 }
    288 
    289 static void
    290 virtio_mmio_write_device_config_4(struct virtio_softc *vsc,
    291 			     int index, uint32_t value)
    292 {
    293 	struct virtio_mmio_softc *sc = (struct virtio_mmio_softc *)vsc;
    294 	bus_space_write_4(sc->sc_iot, sc->sc_ioh,
    295 			  VIRTIO_MMIO_CONFIG + index, value);
    296 }
    297 
    298 static void
    299 virtio_mmio_write_device_config_8(struct virtio_softc *vsc,
    300 			     int index, uint64_t value)
    301 {
    302 	struct virtio_mmio_softc *sc = (struct virtio_mmio_softc *)vsc;
    303 	bus_space_write_4(sc->sc_iot, sc->sc_ioh,
    304 			  VIRTIO_MMIO_CONFIG + index,
    305 			  value & 0xffffffff);
    306 	bus_space_write_4(sc->sc_iot, sc->sc_ioh,
    307 			  VIRTIO_MMIO_CONFIG + index + sizeof(uint32_t),
    308 			  value >> 32);
    309 }
    310 
    311 /*
    312  * Interrupt handler.
    313  */
    314 int
    315 virtio_mmio_intr(void *arg)
    316 {
    317 	struct virtio_mmio_softc *sc = arg;
    318 	struct virtio_softc *vsc = &sc->sc_sc;
    319 	int isr, r = 0;
    320 
    321 	/* check and ack the interrupt */
    322 	isr = bus_space_read_4(sc->sc_iot, sc->sc_ioh,
    323 			       VIRTIO_MMIO_INTERRUPT_STATUS);
    324 	bus_space_write_4(sc->sc_iot, sc->sc_ioh,
    325 			  VIRTIO_MMIO_INTERRUPT_ACK, isr);
    326 	if ((isr & VIRTIO_MMIO_INT_CONFIG) &&
    327 	    (vsc->sc_config_change != NULL))
    328 		r = (vsc->sc_config_change)(vsc);
    329 	if ((isr & VIRTIO_MMIO_INT_VRING) &&
    330 	    (vsc->sc_intrhand != NULL)) {
    331 		if (vsc->sc_soft_ih != NULL)
    332 			softint_schedule(vsc->sc_soft_ih);
    333 		else
    334 			r |= (vsc->sc_intrhand)(vsc);
    335 	}
    336 
    337 	return r;
    338 }
    339 
    340 static void
    341 virtio_mmio_kick(struct virtio_softc *vsc, uint16_t idx)
    342 {
    343 	struct virtio_mmio_softc *sc = (struct virtio_mmio_softc *)vsc;
    344 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, VIRTIO_MMIO_QUEUE_NOTIFY,
    345 	    idx);
    346 }
    347 
    348 static int
    349 virtio_mmio_setup_interrupts(struct virtio_softc *vsc)
    350 {
    351 	struct virtio_mmio_softc * const sc = (struct virtio_mmio_softc *)vsc;
    352 
    353 	return sc->sc_setup_interrupts(sc);
    354 }
    355 
    356 static void
    357 virtio_mmio_free_interrupts(struct virtio_softc *vsc)
    358 {
    359 	struct virtio_mmio_softc * const sc = (struct virtio_mmio_softc *)vsc;
    360 
    361 	sc->sc_free_interrupts(sc);
    362 }
    363