virtio.c revision 252707
1227652Sgrehan/*- 2252707Sbryanv * Copyright (c) 2011, Bryan Venteicher <bryanv@FreeBSD.org> 3227652Sgrehan * All rights reserved. 4227652Sgrehan * 5227652Sgrehan * Redistribution and use in source and binary forms, with or without 6227652Sgrehan * modification, are permitted provided that the following conditions 7227652Sgrehan * are met: 8227652Sgrehan * 1. Redistributions of source code must retain the above copyright 9227652Sgrehan * notice unmodified, this list of conditions, and the following 10227652Sgrehan * disclaimer. 11227652Sgrehan * 2. Redistributions in binary form must reproduce the above copyright 12227652Sgrehan * notice, this list of conditions and the following disclaimer in the 13227652Sgrehan * documentation and/or other materials provided with the distribution. 14227652Sgrehan * 15227652Sgrehan * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16227652Sgrehan * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17227652Sgrehan * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18227652Sgrehan * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19227652Sgrehan * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20227652Sgrehan * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21227652Sgrehan * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22227652Sgrehan * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23227652Sgrehan * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24227652Sgrehan * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25227652Sgrehan */ 26227652Sgrehan 27227652Sgrehan#include <sys/cdefs.h> 28227652Sgrehan__FBSDID("$FreeBSD: head/sys/dev/virtio/virtio.c 252707 2013-07-04 17:57:26Z bryanv $"); 29227652Sgrehan 30227652Sgrehan#include <sys/param.h> 31227652Sgrehan#include <sys/systm.h> 32227652Sgrehan#include <sys/kernel.h> 33227652Sgrehan#include <sys/malloc.h> 34227652Sgrehan#include <sys/module.h> 35227652Sgrehan#include <sys/sbuf.h> 36227652Sgrehan 37227652Sgrehan#include <machine/bus.h> 38227652Sgrehan#include <machine/resource.h> 39227652Sgrehan#include <sys/bus.h> 40227652Sgrehan#include <sys/rman.h> 41227652Sgrehan 42227652Sgrehan#include <dev/virtio/virtio.h> 43227652Sgrehan#include <dev/virtio/virtqueue.h> 44227652Sgrehan 45227652Sgrehan#include "virtio_bus_if.h" 46227652Sgrehan 47227652Sgrehanstatic int virtio_modevent(module_t, int, void *); 48227652Sgrehanstatic const char *virtio_feature_name(uint64_t, struct virtio_feature_desc *); 49227652Sgrehan 50227652Sgrehanstatic struct virtio_ident { 51252707Sbryanv uint16_t devid; 52252707Sbryanv const char *name; 53227652Sgrehan} virtio_ident_table[] = { 54227652Sgrehan { VIRTIO_ID_NETWORK, "Network" }, 55227652Sgrehan { VIRTIO_ID_BLOCK, "Block" }, 56227652Sgrehan { VIRTIO_ID_CONSOLE, "Console" }, 57227652Sgrehan { VIRTIO_ID_ENTROPY, "Entropy" }, 58227652Sgrehan { VIRTIO_ID_BALLOON, "Balloon" }, 59227652Sgrehan { VIRTIO_ID_IOMEMORY, "IOMemory" }, 60241470Sgrehan { VIRTIO_ID_SCSI, "SCSI" }, 61227652Sgrehan { VIRTIO_ID_9P, "9P Transport" }, 62227652Sgrehan 63227652Sgrehan { 0, NULL } 64227652Sgrehan}; 65227652Sgrehan 66227652Sgrehan/* Device independent features. */ 67227652Sgrehanstatic struct virtio_feature_desc virtio_common_feature_desc[] = { 68227652Sgrehan { VIRTIO_F_NOTIFY_ON_EMPTY, "NotifyOnEmpty" }, 69227652Sgrehan { VIRTIO_RING_F_INDIRECT_DESC, "RingIndirect" }, 70227652Sgrehan { VIRTIO_RING_F_EVENT_IDX, "EventIdx" }, 71227652Sgrehan { VIRTIO_F_BAD_FEATURE, "BadFeature" }, 72227652Sgrehan 73227652Sgrehan { 0, NULL } 74227652Sgrehan}; 75227652Sgrehan 76227652Sgrehanconst char * 77227652Sgrehanvirtio_device_name(uint16_t devid) 78227652Sgrehan{ 79227652Sgrehan struct virtio_ident *ident; 80227652Sgrehan 81227652Sgrehan for (ident = virtio_ident_table; ident->name != NULL; ident++) { 82227652Sgrehan if (ident->devid == devid) 83227652Sgrehan return (ident->name); 84227652Sgrehan } 85227652Sgrehan 86227652Sgrehan return (NULL); 87227652Sgrehan} 88227652Sgrehan 89252707Sbryanvstatic const char * 90252707Sbryanvvirtio_feature_name(uint64_t val, struct virtio_feature_desc *desc) 91252707Sbryanv{ 92252707Sbryanv int i, j; 93252707Sbryanv struct virtio_feature_desc *descs[2] = { desc, 94252707Sbryanv virtio_common_feature_desc }; 95252707Sbryanv 96252707Sbryanv for (i = 0; i < 2; i++) { 97252707Sbryanv if (descs[i] == NULL) 98252707Sbryanv continue; 99252707Sbryanv 100252707Sbryanv for (j = 0; descs[i][j].vfd_val != 0; j++) { 101252707Sbryanv if (val == descs[i][j].vfd_val) 102252707Sbryanv return (descs[i][j].vfd_str); 103252707Sbryanv } 104252707Sbryanv } 105252707Sbryanv 106252707Sbryanv return (NULL); 107252707Sbryanv} 108252707Sbryanv 109227652Sgrehanvoid 110227652Sgrehanvirtio_describe(device_t dev, const char *msg, 111252707Sbryanv uint64_t features, struct virtio_feature_desc *desc) 112227652Sgrehan{ 113227652Sgrehan struct sbuf sb; 114227652Sgrehan uint64_t val; 115227652Sgrehan char *buf; 116227652Sgrehan const char *name; 117227652Sgrehan int n; 118227652Sgrehan 119227652Sgrehan if ((buf = malloc(512, M_TEMP, M_NOWAIT)) == NULL) { 120252707Sbryanv device_printf(dev, "%s features: %#jx\n", msg, (uintmax_t) features); 121227652Sgrehan return; 122227652Sgrehan } 123227652Sgrehan 124227652Sgrehan sbuf_new(&sb, buf, 512, SBUF_FIXEDLEN); 125252707Sbryanv sbuf_printf(&sb, "%s features: %#jx", msg, (uintmax_t) features); 126227652Sgrehan 127227652Sgrehan for (n = 0, val = 1ULL << 63; val != 0; val >>= 1) { 128227652Sgrehan /* 129227652Sgrehan * BAD_FEATURE is used to detect broken Linux clients 130227652Sgrehan * and therefore is not applicable to FreeBSD. 131227652Sgrehan */ 132227652Sgrehan if (((features & val) == 0) || val == VIRTIO_F_BAD_FEATURE) 133227652Sgrehan continue; 134227652Sgrehan 135227652Sgrehan if (n++ == 0) 136227652Sgrehan sbuf_cat(&sb, " <"); 137227652Sgrehan else 138227652Sgrehan sbuf_cat(&sb, ","); 139227652Sgrehan 140252707Sbryanv name = virtio_feature_name(val, desc); 141227652Sgrehan if (name == NULL) 142252707Sbryanv sbuf_printf(&sb, "%#jx", (uintmax_t) val); 143227652Sgrehan else 144227652Sgrehan sbuf_cat(&sb, name); 145227652Sgrehan } 146227652Sgrehan 147227652Sgrehan if (n > 0) 148227652Sgrehan sbuf_cat(&sb, ">"); 149227652Sgrehan 150227652Sgrehan#if __FreeBSD_version < 900020 151227652Sgrehan sbuf_finish(&sb); 152227652Sgrehan if (sbuf_overflowed(&sb) == 0) 153227652Sgrehan#else 154227652Sgrehan if (sbuf_finish(&sb) == 0) 155227652Sgrehan#endif 156227652Sgrehan device_printf(dev, "%s\n", sbuf_data(&sb)); 157227652Sgrehan 158227652Sgrehan sbuf_delete(&sb); 159227652Sgrehan free(buf, M_TEMP); 160227652Sgrehan} 161227652Sgrehan 162227652Sgrehan/* 163227652Sgrehan * VirtIO bus method wrappers. 164227652Sgrehan */ 165227652Sgrehan 166238360Sgrehanvoid 167238360Sgrehanvirtio_read_ivar(device_t dev, int ivar, uintptr_t *val) 168238360Sgrehan{ 169238360Sgrehan 170238360Sgrehan *val = -1; 171238360Sgrehan BUS_READ_IVAR(device_get_parent(dev), dev, ivar, val); 172238360Sgrehan} 173238360Sgrehan 174238360Sgrehanvoid 175238360Sgrehanvirtio_write_ivar(device_t dev, int ivar, uintptr_t val) 176238360Sgrehan{ 177238360Sgrehan 178238360Sgrehan BUS_WRITE_IVAR(device_get_parent(dev), dev, ivar, val); 179238360Sgrehan} 180238360Sgrehan 181227652Sgrehanuint64_t 182227652Sgrehanvirtio_negotiate_features(device_t dev, uint64_t child_features) 183227652Sgrehan{ 184227652Sgrehan 185227652Sgrehan return (VIRTIO_BUS_NEGOTIATE_FEATURES(device_get_parent(dev), 186227652Sgrehan child_features)); 187227652Sgrehan} 188227652Sgrehan 189227652Sgrehanint 190227652Sgrehanvirtio_alloc_virtqueues(device_t dev, int flags, int nvqs, 191227652Sgrehan struct vq_alloc_info *info) 192227652Sgrehan{ 193227652Sgrehan 194227652Sgrehan return (VIRTIO_BUS_ALLOC_VIRTQUEUES(device_get_parent(dev), flags, 195227652Sgrehan nvqs, info)); 196227652Sgrehan} 197227652Sgrehan 198227652Sgrehanint 199227652Sgrehanvirtio_setup_intr(device_t dev, enum intr_type type) 200227652Sgrehan{ 201227652Sgrehan 202227652Sgrehan return (VIRTIO_BUS_SETUP_INTR(device_get_parent(dev), type)); 203227652Sgrehan} 204227652Sgrehan 205227652Sgrehanint 206227652Sgrehanvirtio_with_feature(device_t dev, uint64_t feature) 207227652Sgrehan{ 208227652Sgrehan 209227652Sgrehan return (VIRTIO_BUS_WITH_FEATURE(device_get_parent(dev), feature)); 210227652Sgrehan} 211227652Sgrehan 212227652Sgrehanvoid 213227652Sgrehanvirtio_stop(device_t dev) 214227652Sgrehan{ 215227652Sgrehan 216227652Sgrehan VIRTIO_BUS_STOP(device_get_parent(dev)); 217227652Sgrehan} 218227652Sgrehan 219227652Sgrehanint 220227652Sgrehanvirtio_reinit(device_t dev, uint64_t features) 221227652Sgrehan{ 222227652Sgrehan 223227652Sgrehan return (VIRTIO_BUS_REINIT(device_get_parent(dev), features)); 224227652Sgrehan} 225227652Sgrehan 226227652Sgrehanvoid 227227652Sgrehanvirtio_reinit_complete(device_t dev) 228227652Sgrehan{ 229227652Sgrehan 230227652Sgrehan VIRTIO_BUS_REINIT_COMPLETE(device_get_parent(dev)); 231227652Sgrehan} 232227652Sgrehan 233227652Sgrehanvoid 234227652Sgrehanvirtio_read_device_config(device_t dev, bus_size_t offset, void *dst, int len) 235227652Sgrehan{ 236227652Sgrehan 237227652Sgrehan VIRTIO_BUS_READ_DEVICE_CONFIG(device_get_parent(dev), 238227652Sgrehan offset, dst, len); 239227652Sgrehan} 240227652Sgrehan 241227652Sgrehanvoid 242227652Sgrehanvirtio_write_device_config(device_t dev, bus_size_t offset, void *dst, int len) 243227652Sgrehan{ 244227652Sgrehan 245227652Sgrehan VIRTIO_BUS_WRITE_DEVICE_CONFIG(device_get_parent(dev), 246227652Sgrehan offset, dst, len); 247227652Sgrehan} 248227652Sgrehan 249227652Sgrehanstatic int 250227652Sgrehanvirtio_modevent(module_t mod, int type, void *unused) 251227652Sgrehan{ 252227652Sgrehan int error; 253227652Sgrehan 254227652Sgrehan switch (type) { 255227652Sgrehan case MOD_LOAD: 256227652Sgrehan case MOD_QUIESCE: 257227652Sgrehan case MOD_UNLOAD: 258227652Sgrehan case MOD_SHUTDOWN: 259252707Sbryanv error = 0; 260227652Sgrehan break; 261227652Sgrehan default: 262227652Sgrehan error = EOPNOTSUPP; 263227652Sgrehan break; 264227652Sgrehan } 265227652Sgrehan 266227652Sgrehan return (error); 267227652Sgrehan} 268227652Sgrehan 269227652Sgrehanstatic moduledata_t virtio_mod = { 270227652Sgrehan "virtio", 271227652Sgrehan virtio_modevent, 272241394Skevlo 0 273227652Sgrehan}; 274227652Sgrehan 275227652SgrehanDECLARE_MODULE(virtio, virtio_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST); 276227652SgrehanMODULE_VERSION(virtio, 1); 277