111447b59SVenkatesh Srinivas /*-
211447b59SVenkatesh Srinivas * Copyright (c) 2011, Bryan Venteicher <bryanv@daemoninthecloset.org>
311447b59SVenkatesh Srinivas * All rights reserved.
411447b59SVenkatesh Srinivas *
511447b59SVenkatesh Srinivas * Redistribution and use in source and binary forms, with or without
611447b59SVenkatesh Srinivas * modification, are permitted provided that the following conditions
711447b59SVenkatesh Srinivas * are met:
811447b59SVenkatesh Srinivas * 1. Redistributions of source code must retain the above copyright
911447b59SVenkatesh Srinivas * notice unmodified, this list of conditions, and the following
1011447b59SVenkatesh Srinivas * disclaimer.
1111447b59SVenkatesh Srinivas * 2. Redistributions in binary form must reproduce the above copyright
1211447b59SVenkatesh Srinivas * notice, this list of conditions and the following disclaimer in the
1311447b59SVenkatesh Srinivas * documentation and/or other materials provided with the distribution.
1411447b59SVenkatesh Srinivas *
1511447b59SVenkatesh Srinivas * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1611447b59SVenkatesh Srinivas * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1711447b59SVenkatesh Srinivas * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1811447b59SVenkatesh Srinivas * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
1911447b59SVenkatesh Srinivas * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2011447b59SVenkatesh Srinivas * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2111447b59SVenkatesh Srinivas * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2211447b59SVenkatesh Srinivas * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2311447b59SVenkatesh Srinivas * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2411447b59SVenkatesh Srinivas * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2511447b59SVenkatesh Srinivas *
2611447b59SVenkatesh Srinivas * $FreeBSD: src/sys/dev/virtio/virtio.c,v 1.1 2011/11/18 05:43:43 grehan Exp $
2711447b59SVenkatesh Srinivas */
2811447b59SVenkatesh Srinivas
2911447b59SVenkatesh Srinivas #include <sys/param.h>
3011447b59SVenkatesh Srinivas #include <sys/systm.h>
3111447b59SVenkatesh Srinivas #include <sys/kernel.h>
3211447b59SVenkatesh Srinivas #include <sys/malloc.h>
3311447b59SVenkatesh Srinivas #include <sys/module.h>
3411447b59SVenkatesh Srinivas #include <sys/sbuf.h>
3511447b59SVenkatesh Srinivas
3611447b59SVenkatesh Srinivas #include <machine/inttypes.h>
3711447b59SVenkatesh Srinivas #include <sys/bus.h>
382f1382caSVenkatesh Srinivas #include <sys/serialize.h>
3911447b59SVenkatesh Srinivas #include <sys/rman.h>
4011447b59SVenkatesh Srinivas
4111447b59SVenkatesh Srinivas #include "virtio.h"
4211447b59SVenkatesh Srinivas #include "virtqueue.h"
4311447b59SVenkatesh Srinivas
4411447b59SVenkatesh Srinivas #include "virtio_bus_if.h"
4511447b59SVenkatesh Srinivas
4611447b59SVenkatesh Srinivas static int virtio_modevent(module_t, int, void *);
4711447b59SVenkatesh Srinivas static const char *virtio_feature_name(uint64_t, struct virtio_feature_desc *);
4811447b59SVenkatesh Srinivas
4911447b59SVenkatesh Srinivas static struct virtio_ident {
5011447b59SVenkatesh Srinivas uint16_t devid;
51866fc571SSascha Wildner const char *name;
5211447b59SVenkatesh Srinivas } virtio_ident_table[] = {
5311447b59SVenkatesh Srinivas { VIRTIO_ID_NETWORK, "Network" },
5411447b59SVenkatesh Srinivas { VIRTIO_ID_BLOCK, "Block" },
5511447b59SVenkatesh Srinivas { VIRTIO_ID_CONSOLE, "Console" },
5611447b59SVenkatesh Srinivas { VIRTIO_ID_ENTROPY, "Entropy" },
5711447b59SVenkatesh Srinivas { VIRTIO_ID_BALLOON, "Balloon" },
5811447b59SVenkatesh Srinivas { VIRTIO_ID_IOMEMORY, "IOMemory" },
59eff15082SSascha Wildner { VIRTIO_ID_SCSI, "SCSI" },
6011447b59SVenkatesh Srinivas { VIRTIO_ID_9P, "9P Transport" },
6111447b59SVenkatesh Srinivas
6211447b59SVenkatesh Srinivas { 0, NULL }
6311447b59SVenkatesh Srinivas };
6411447b59SVenkatesh Srinivas
6511447b59SVenkatesh Srinivas /* Device independent features. */
6611447b59SVenkatesh Srinivas static struct virtio_feature_desc virtio_common_feature_desc[] = {
6711447b59SVenkatesh Srinivas { VIRTIO_F_NOTIFY_ON_EMPTY, "NotifyOnEmpty" },
680ef60afdSImre Vadász { VIRTIO_F_ANY_LAYOUT, "AnyLayout" },
699e39c380SImre Vadász { VIRTIO_RING_F_INDIRECT_DESC, "RingIndirect" },
7011447b59SVenkatesh Srinivas { VIRTIO_RING_F_EVENT_IDX, "EventIdx" },
7111447b59SVenkatesh Srinivas { VIRTIO_F_BAD_FEATURE, "BadFeature" },
7211447b59SVenkatesh Srinivas
7311447b59SVenkatesh Srinivas { 0, NULL }
7411447b59SVenkatesh Srinivas };
7511447b59SVenkatesh Srinivas
7611447b59SVenkatesh Srinivas const char *
virtio_device_name(uint16_t devid)7711447b59SVenkatesh Srinivas virtio_device_name(uint16_t devid)
7811447b59SVenkatesh Srinivas {
7911447b59SVenkatesh Srinivas struct virtio_ident *ident;
8011447b59SVenkatesh Srinivas
8111447b59SVenkatesh Srinivas for (ident = virtio_ident_table; ident->name != NULL; ident++) {
8211447b59SVenkatesh Srinivas if (ident->devid == devid)
8311447b59SVenkatesh Srinivas return (ident->name);
8411447b59SVenkatesh Srinivas }
8511447b59SVenkatesh Srinivas
8611447b59SVenkatesh Srinivas return (NULL);
8711447b59SVenkatesh Srinivas }
8811447b59SVenkatesh Srinivas
8911447b59SVenkatesh Srinivas int
virtio_get_device_type(device_t dev)9011447b59SVenkatesh Srinivas virtio_get_device_type(device_t dev)
9111447b59SVenkatesh Srinivas {
9211447b59SVenkatesh Srinivas uintptr_t devtype;
9311447b59SVenkatesh Srinivas
9411447b59SVenkatesh Srinivas devtype = -1;
9511447b59SVenkatesh Srinivas
9611447b59SVenkatesh Srinivas BUS_READ_IVAR(device_get_parent(dev), dev,
9711447b59SVenkatesh Srinivas VIRTIO_IVAR_DEVTYPE, &devtype);
9811447b59SVenkatesh Srinivas
9911447b59SVenkatesh Srinivas return ((int) devtype);
10011447b59SVenkatesh Srinivas }
10111447b59SVenkatesh Srinivas
10211447b59SVenkatesh Srinivas void
virtio_set_feature_desc(device_t dev,struct virtio_feature_desc * feature_desc)10311447b59SVenkatesh Srinivas virtio_set_feature_desc(device_t dev,
10411447b59SVenkatesh Srinivas struct virtio_feature_desc *feature_desc)
10511447b59SVenkatesh Srinivas {
10611447b59SVenkatesh Srinivas
10711447b59SVenkatesh Srinivas BUS_WRITE_IVAR(device_get_parent(dev), dev,
10811447b59SVenkatesh Srinivas VIRTIO_IVAR_FEATURE_DESC, (uintptr_t) feature_desc);
10911447b59SVenkatesh Srinivas }
11011447b59SVenkatesh Srinivas
11111447b59SVenkatesh Srinivas void
virtio_describe(device_t dev,const char * msg,uint64_t features,struct virtio_feature_desc * desc)11211447b59SVenkatesh Srinivas virtio_describe(device_t dev, const char *msg,
113a77a4c8cSFrançois Tigeot uint64_t features, struct virtio_feature_desc *desc)
11411447b59SVenkatesh Srinivas {
11511447b59SVenkatesh Srinivas struct sbuf sb;
11611447b59SVenkatesh Srinivas uint64_t val;
11711447b59SVenkatesh Srinivas char *buf;
11811447b59SVenkatesh Srinivas const char *name;
11911447b59SVenkatesh Srinivas int n;
12011447b59SVenkatesh Srinivas
121eb55c32fSMatthew Dillon if ((buf = kmalloc(512, M_TEMP, M_INTWAIT)) == NULL) {
12211447b59SVenkatesh Srinivas device_printf(dev, "%s features: 0x%"PRIx64"\n", msg,
12311447b59SVenkatesh Srinivas features);
12411447b59SVenkatesh Srinivas return;
12511447b59SVenkatesh Srinivas }
12611447b59SVenkatesh Srinivas
12711447b59SVenkatesh Srinivas sbuf_new(&sb, buf, 512, SBUF_FIXEDLEN);
12811447b59SVenkatesh Srinivas sbuf_printf(&sb, "%s features: 0x%"PRIx64, msg, features);
12911447b59SVenkatesh Srinivas
13011447b59SVenkatesh Srinivas for (n = 0, val = 1ULL << 63; val != 0; val >>= 1) {
13111447b59SVenkatesh Srinivas /*
13211447b59SVenkatesh Srinivas * BAD_FEATURE is used to detect broken Linux clients
13311447b59SVenkatesh Srinivas * and therefore is not applicable to FreeBSD.
13411447b59SVenkatesh Srinivas */
13511447b59SVenkatesh Srinivas if (((features & val) == 0) || val == VIRTIO_F_BAD_FEATURE)
13611447b59SVenkatesh Srinivas continue;
13711447b59SVenkatesh Srinivas
13811447b59SVenkatesh Srinivas if (n++ == 0)
13911447b59SVenkatesh Srinivas sbuf_cat(&sb, " <");
14011447b59SVenkatesh Srinivas else
14111447b59SVenkatesh Srinivas sbuf_cat(&sb, ",");
14211447b59SVenkatesh Srinivas
143a77a4c8cSFrançois Tigeot name = virtio_feature_name(val, desc);
14411447b59SVenkatesh Srinivas if (name == NULL)
145a77a4c8cSFrançois Tigeot sbuf_printf(&sb, "%#jx", (uintmax_t) val);
14611447b59SVenkatesh Srinivas else
14711447b59SVenkatesh Srinivas sbuf_cat(&sb, name);
14811447b59SVenkatesh Srinivas }
14911447b59SVenkatesh Srinivas
15011447b59SVenkatesh Srinivas if (n > 0)
15111447b59SVenkatesh Srinivas sbuf_cat(&sb, ">");
15211447b59SVenkatesh Srinivas
153a77a4c8cSFrançois Tigeot if (sbuf_finish(&sb) == 0)
15411447b59SVenkatesh Srinivas device_printf(dev, "%s\n", sbuf_data(&sb));
15511447b59SVenkatesh Srinivas
15611447b59SVenkatesh Srinivas sbuf_delete(&sb);
15711447b59SVenkatesh Srinivas kfree(buf, M_TEMP);
15811447b59SVenkatesh Srinivas }
15911447b59SVenkatesh Srinivas
16011447b59SVenkatesh Srinivas static const char *
virtio_feature_name(uint64_t val,struct virtio_feature_desc * desc)161a77a4c8cSFrançois Tigeot virtio_feature_name(uint64_t val, struct virtio_feature_desc *desc)
16211447b59SVenkatesh Srinivas {
163a77a4c8cSFrançois Tigeot int i, j;
164a77a4c8cSFrançois Tigeot struct virtio_feature_desc *descs[2] = { desc,
165a77a4c8cSFrançois Tigeot virtio_common_feature_desc };
16611447b59SVenkatesh Srinivas
167a77a4c8cSFrançois Tigeot for (i = 0; i < 2; i++) {
168a77a4c8cSFrançois Tigeot if (descs[i] == NULL)
169a77a4c8cSFrançois Tigeot continue;
170a77a4c8cSFrançois Tigeot
171a77a4c8cSFrançois Tigeot for (j = 0; descs[i][j].vfd_val != 0; j++) {
172a77a4c8cSFrançois Tigeot if (val == descs[i][j].vfd_val)
173a77a4c8cSFrançois Tigeot return (descs[i][j].vfd_str);
174a77a4c8cSFrançois Tigeot }
175a77a4c8cSFrançois Tigeot }
17611447b59SVenkatesh Srinivas
17711447b59SVenkatesh Srinivas return (NULL);
17811447b59SVenkatesh Srinivas }
17911447b59SVenkatesh Srinivas
18011447b59SVenkatesh Srinivas /*
18111447b59SVenkatesh Srinivas * VirtIO bus method wrappers.
18211447b59SVenkatesh Srinivas */
18311447b59SVenkatesh Srinivas
18411447b59SVenkatesh Srinivas uint64_t
virtio_negotiate_features(device_t dev,uint64_t child_features)18511447b59SVenkatesh Srinivas virtio_negotiate_features(device_t dev, uint64_t child_features)
18611447b59SVenkatesh Srinivas {
18711447b59SVenkatesh Srinivas return (VIRTIO_BUS_NEGOTIATE_FEATURES(device_get_parent(dev),
18811447b59SVenkatesh Srinivas child_features));
18911447b59SVenkatesh Srinivas }
19011447b59SVenkatesh Srinivas
19111447b59SVenkatesh Srinivas int
virtio_alloc_virtqueues(device_t dev,int nvqs,struct vq_alloc_info * info)192099c4d8eSImre Vadász virtio_alloc_virtqueues(device_t dev, int nvqs, struct vq_alloc_info *info)
19311447b59SVenkatesh Srinivas {
194099c4d8eSImre Vadász return (VIRTIO_BUS_ALLOC_VIRTQUEUES(device_get_parent(dev),
19511447b59SVenkatesh Srinivas nvqs, info));
19611447b59SVenkatesh Srinivas }
19711447b59SVenkatesh Srinivas
19811447b59SVenkatesh Srinivas int
virtio_setup_intr(device_t dev,uint irq,lwkt_serialize_t slz)1992f2405bbSImre Vadász virtio_setup_intr(device_t dev, uint irq, lwkt_serialize_t slz)
20011447b59SVenkatesh Srinivas {
2012f2405bbSImre Vadász return (VIRTIO_BUS_SETUP_INTR(device_get_parent(dev), irq, slz));
2022f2405bbSImre Vadász }
2032f2405bbSImre Vadász
2042f2405bbSImre Vadász int
virtio_teardown_intr(device_t dev,uint irq)2052f2405bbSImre Vadász virtio_teardown_intr(device_t dev, uint irq)
2062f2405bbSImre Vadász {
2072f2405bbSImre Vadász return (VIRTIO_BUS_TEARDOWN_INTR(device_get_parent(dev), irq));
2082f2405bbSImre Vadász }
2092f2405bbSImre Vadász
2102f2405bbSImre Vadász int
virtio_intr_count(device_t dev)2112f2405bbSImre Vadász virtio_intr_count(device_t dev)
2122f2405bbSImre Vadász {
2132f2405bbSImre Vadász return (VIRTIO_BUS_INTR_COUNT(device_get_parent(dev)));
2142f2405bbSImre Vadász }
2152f2405bbSImre Vadász
2162f2405bbSImre Vadász int
virtio_intr_alloc(device_t dev,int * cnt,int use_config,int * cpus)2172f2405bbSImre Vadász virtio_intr_alloc(device_t dev, int *cnt, int use_config, int *cpus)
2182f2405bbSImre Vadász {
2192f2405bbSImre Vadász return (VIRTIO_BUS_INTR_ALLOC(device_get_parent(dev), cnt, use_config,
2202f2405bbSImre Vadász cpus));
2212f2405bbSImre Vadász }
2222f2405bbSImre Vadász
2232f2405bbSImre Vadász int
virtio_intr_release(device_t dev)2242f2405bbSImre Vadász virtio_intr_release(device_t dev)
2252f2405bbSImre Vadász {
2262f2405bbSImre Vadász return (VIRTIO_BUS_INTR_RELEASE(device_get_parent(dev)));
2272f2405bbSImre Vadász }
2282f2405bbSImre Vadász
2292f2405bbSImre Vadász int
virtio_bind_intr(device_t dev,uint irq,int what,driver_intr_t handler,void * arg)2309d96478cSImre Vadász virtio_bind_intr(device_t dev, uint irq, int what,
2319d96478cSImre Vadász driver_intr_t handler, void *arg)
2322f2405bbSImre Vadász {
2339d96478cSImre Vadász return (VIRTIO_BUS_BIND_INTR(device_get_parent(dev), irq, what,
2349d96478cSImre Vadász handler, arg));
2352f2405bbSImre Vadász }
2362f2405bbSImre Vadász
2372f2405bbSImre Vadász int
virtio_unbind_intr(device_t dev,int what)2382f2405bbSImre Vadász virtio_unbind_intr(device_t dev, int what)
2392f2405bbSImre Vadász {
2402f2405bbSImre Vadász return (VIRTIO_BUS_UNBIND_INTR(device_get_parent(dev), what));
24111447b59SVenkatesh Srinivas }
24211447b59SVenkatesh Srinivas
24311447b59SVenkatesh Srinivas int
virtio_with_feature(device_t dev,uint64_t feature)24411447b59SVenkatesh Srinivas virtio_with_feature(device_t dev, uint64_t feature)
24511447b59SVenkatesh Srinivas {
24611447b59SVenkatesh Srinivas return (VIRTIO_BUS_WITH_FEATURE(device_get_parent(dev), feature));
24711447b59SVenkatesh Srinivas }
24811447b59SVenkatesh Srinivas
24911447b59SVenkatesh Srinivas void
virtio_stop(device_t dev)25011447b59SVenkatesh Srinivas virtio_stop(device_t dev)
25111447b59SVenkatesh Srinivas {
25211447b59SVenkatesh Srinivas VIRTIO_BUS_STOP(device_get_parent(dev));
25311447b59SVenkatesh Srinivas }
25411447b59SVenkatesh Srinivas
25511447b59SVenkatesh Srinivas int
virtio_reinit(device_t dev,uint64_t features)25611447b59SVenkatesh Srinivas virtio_reinit(device_t dev, uint64_t features)
25711447b59SVenkatesh Srinivas {
25811447b59SVenkatesh Srinivas return (VIRTIO_BUS_REINIT(device_get_parent(dev), features));
25911447b59SVenkatesh Srinivas }
26011447b59SVenkatesh Srinivas
26111447b59SVenkatesh Srinivas void
virtio_reinit_complete(device_t dev)26211447b59SVenkatesh Srinivas virtio_reinit_complete(device_t dev)
26311447b59SVenkatesh Srinivas {
26411447b59SVenkatesh Srinivas VIRTIO_BUS_REINIT_COMPLETE(device_get_parent(dev));
26511447b59SVenkatesh Srinivas }
26611447b59SVenkatesh Srinivas
26711447b59SVenkatesh Srinivas void
virtio_read_device_config(device_t dev,bus_size_t offset,void * dst,int len)26811447b59SVenkatesh Srinivas virtio_read_device_config(device_t dev, bus_size_t offset, void *dst, int len)
26911447b59SVenkatesh Srinivas {
27011447b59SVenkatesh Srinivas VIRTIO_BUS_READ_DEVICE_CONFIG(device_get_parent(dev),
27111447b59SVenkatesh Srinivas offset, dst, len);
27211447b59SVenkatesh Srinivas }
27311447b59SVenkatesh Srinivas
27411447b59SVenkatesh Srinivas void
virtio_write_device_config(device_t dev,bus_size_t offset,void * dst,int len)27511447b59SVenkatesh Srinivas virtio_write_device_config(device_t dev, bus_size_t offset, void *dst, int len)
27611447b59SVenkatesh Srinivas {
27711447b59SVenkatesh Srinivas VIRTIO_BUS_WRITE_DEVICE_CONFIG(device_get_parent(dev),
27811447b59SVenkatesh Srinivas offset, dst, len);
27911447b59SVenkatesh Srinivas }
28011447b59SVenkatesh Srinivas
28111447b59SVenkatesh Srinivas static int
virtio_modevent(module_t mod,int type,void * unused)28211447b59SVenkatesh Srinivas virtio_modevent(module_t mod, int type, void *unused)
28311447b59SVenkatesh Srinivas {
28411447b59SVenkatesh Srinivas int error;
28511447b59SVenkatesh Srinivas
28611447b59SVenkatesh Srinivas error = 0;
28711447b59SVenkatesh Srinivas
28811447b59SVenkatesh Srinivas switch (type) {
28911447b59SVenkatesh Srinivas case MOD_LOAD:
29011447b59SVenkatesh Srinivas case MOD_UNLOAD:
29111447b59SVenkatesh Srinivas case MOD_SHUTDOWN:
29211447b59SVenkatesh Srinivas break;
29311447b59SVenkatesh Srinivas default:
29411447b59SVenkatesh Srinivas error = EOPNOTSUPP;
29511447b59SVenkatesh Srinivas break;
29611447b59SVenkatesh Srinivas }
29711447b59SVenkatesh Srinivas
29811447b59SVenkatesh Srinivas return (error);
29911447b59SVenkatesh Srinivas }
30011447b59SVenkatesh Srinivas
30111447b59SVenkatesh Srinivas static moduledata_t virtio_mod = {
30211447b59SVenkatesh Srinivas "virtio",
30311447b59SVenkatesh Srinivas virtio_modevent,
30411447b59SVenkatesh Srinivas 0
30511447b59SVenkatesh Srinivas };
30611447b59SVenkatesh Srinivas
307*8afeec5dSMatthew Dillon DECLARE_MODULE(virtio, virtio_mod, SI_SUB_DRIVERS, SI_ORDER_EARLIER);
30811447b59SVenkatesh Srinivas MODULE_VERSION(virtio, 1);
309