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$"); 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> 43267312Sbryanv#include <dev/virtio/virtio_config.h> 44227652Sgrehan#include <dev/virtio/virtqueue.h> 45227652Sgrehan 46227652Sgrehan#include "virtio_bus_if.h" 47227652Sgrehan 48227652Sgrehanstatic int virtio_modevent(module_t, int, void *); 49227652Sgrehanstatic const char *virtio_feature_name(uint64_t, struct virtio_feature_desc *); 50227652Sgrehan 51227652Sgrehanstatic struct virtio_ident { 52252707Sbryanv uint16_t devid; 53252707Sbryanv const char *name; 54227652Sgrehan} virtio_ident_table[] = { 55227652Sgrehan { VIRTIO_ID_NETWORK, "Network" }, 56227652Sgrehan { VIRTIO_ID_BLOCK, "Block" }, 57227652Sgrehan { VIRTIO_ID_CONSOLE, "Console" }, 58227652Sgrehan { VIRTIO_ID_ENTROPY, "Entropy" }, 59227652Sgrehan { VIRTIO_ID_BALLOON, "Balloon" }, 60227652Sgrehan { VIRTIO_ID_IOMEMORY, "IOMemory" }, 61241470Sgrehan { VIRTIO_ID_SCSI, "SCSI" }, 62227652Sgrehan { VIRTIO_ID_9P, "9P Transport" }, 63227652Sgrehan 64227652Sgrehan { 0, NULL } 65227652Sgrehan}; 66227652Sgrehan 67227652Sgrehan/* Device independent features. */ 68227652Sgrehanstatic struct virtio_feature_desc virtio_common_feature_desc[] = { 69227652Sgrehan { VIRTIO_F_NOTIFY_ON_EMPTY, "NotifyOnEmpty" }, 70227652Sgrehan { VIRTIO_RING_F_INDIRECT_DESC, "RingIndirect" }, 71227652Sgrehan { VIRTIO_RING_F_EVENT_IDX, "EventIdx" }, 72227652Sgrehan { VIRTIO_F_BAD_FEATURE, "BadFeature" }, 73227652Sgrehan 74227652Sgrehan { 0, NULL } 75227652Sgrehan}; 76227652Sgrehan 77227652Sgrehanconst char * 78227652Sgrehanvirtio_device_name(uint16_t devid) 79227652Sgrehan{ 80227652Sgrehan struct virtio_ident *ident; 81227652Sgrehan 82227652Sgrehan for (ident = virtio_ident_table; ident->name != NULL; ident++) { 83227652Sgrehan if (ident->devid == devid) 84227652Sgrehan return (ident->name); 85227652Sgrehan } 86227652Sgrehan 87227652Sgrehan return (NULL); 88227652Sgrehan} 89227652Sgrehan 90252707Sbryanvstatic const char * 91252707Sbryanvvirtio_feature_name(uint64_t val, struct virtio_feature_desc *desc) 92252707Sbryanv{ 93252707Sbryanv int i, j; 94252707Sbryanv struct virtio_feature_desc *descs[2] = { desc, 95252707Sbryanv virtio_common_feature_desc }; 96252707Sbryanv 97252707Sbryanv for (i = 0; i < 2; i++) { 98252707Sbryanv if (descs[i] == NULL) 99252707Sbryanv continue; 100252707Sbryanv 101252707Sbryanv for (j = 0; descs[i][j].vfd_val != 0; j++) { 102252707Sbryanv if (val == descs[i][j].vfd_val) 103252707Sbryanv return (descs[i][j].vfd_str); 104252707Sbryanv } 105252707Sbryanv } 106252707Sbryanv 107252707Sbryanv return (NULL); 108252707Sbryanv} 109252707Sbryanv 110227652Sgrehanvoid 111227652Sgrehanvirtio_describe(device_t dev, const char *msg, 112252707Sbryanv uint64_t features, struct virtio_feature_desc *desc) 113227652Sgrehan{ 114227652Sgrehan struct sbuf sb; 115227652Sgrehan uint64_t val; 116227652Sgrehan char *buf; 117227652Sgrehan const char *name; 118227652Sgrehan int n; 119227652Sgrehan 120227652Sgrehan if ((buf = malloc(512, M_TEMP, M_NOWAIT)) == NULL) { 121252707Sbryanv device_printf(dev, "%s features: %#jx\n", msg, (uintmax_t) features); 122227652Sgrehan return; 123227652Sgrehan } 124227652Sgrehan 125227652Sgrehan sbuf_new(&sb, buf, 512, SBUF_FIXEDLEN); 126252707Sbryanv sbuf_printf(&sb, "%s features: %#jx", msg, (uintmax_t) features); 127227652Sgrehan 128227652Sgrehan for (n = 0, val = 1ULL << 63; val != 0; val >>= 1) { 129227652Sgrehan /* 130227652Sgrehan * BAD_FEATURE is used to detect broken Linux clients 131227652Sgrehan * and therefore is not applicable to FreeBSD. 132227652Sgrehan */ 133227652Sgrehan if (((features & val) == 0) || val == VIRTIO_F_BAD_FEATURE) 134227652Sgrehan continue; 135227652Sgrehan 136227652Sgrehan if (n++ == 0) 137227652Sgrehan sbuf_cat(&sb, " <"); 138227652Sgrehan else 139227652Sgrehan sbuf_cat(&sb, ","); 140227652Sgrehan 141252707Sbryanv name = virtio_feature_name(val, desc); 142227652Sgrehan if (name == NULL) 143252707Sbryanv sbuf_printf(&sb, "%#jx", (uintmax_t) val); 144227652Sgrehan else 145227652Sgrehan sbuf_cat(&sb, name); 146227652Sgrehan } 147227652Sgrehan 148227652Sgrehan if (n > 0) 149227652Sgrehan sbuf_cat(&sb, ">"); 150227652Sgrehan 151227652Sgrehan#if __FreeBSD_version < 900020 152227652Sgrehan sbuf_finish(&sb); 153227652Sgrehan if (sbuf_overflowed(&sb) == 0) 154227652Sgrehan#else 155227652Sgrehan if (sbuf_finish(&sb) == 0) 156227652Sgrehan#endif 157227652Sgrehan device_printf(dev, "%s\n", sbuf_data(&sb)); 158227652Sgrehan 159227652Sgrehan sbuf_delete(&sb); 160227652Sgrehan free(buf, M_TEMP); 161227652Sgrehan} 162227652Sgrehan 163227652Sgrehan/* 164227652Sgrehan * VirtIO bus method wrappers. 165227652Sgrehan */ 166227652Sgrehan 167238360Sgrehanvoid 168238360Sgrehanvirtio_read_ivar(device_t dev, int ivar, uintptr_t *val) 169238360Sgrehan{ 170238360Sgrehan 171238360Sgrehan *val = -1; 172238360Sgrehan BUS_READ_IVAR(device_get_parent(dev), dev, ivar, val); 173238360Sgrehan} 174238360Sgrehan 175238360Sgrehanvoid 176238360Sgrehanvirtio_write_ivar(device_t dev, int ivar, uintptr_t val) 177238360Sgrehan{ 178238360Sgrehan 179238360Sgrehan BUS_WRITE_IVAR(device_get_parent(dev), dev, ivar, val); 180238360Sgrehan} 181238360Sgrehan 182227652Sgrehanuint64_t 183227652Sgrehanvirtio_negotiate_features(device_t dev, uint64_t child_features) 184227652Sgrehan{ 185227652Sgrehan 186227652Sgrehan return (VIRTIO_BUS_NEGOTIATE_FEATURES(device_get_parent(dev), 187227652Sgrehan child_features)); 188227652Sgrehan} 189227652Sgrehan 190227652Sgrehanint 191227652Sgrehanvirtio_alloc_virtqueues(device_t dev, int flags, int nvqs, 192227652Sgrehan struct vq_alloc_info *info) 193227652Sgrehan{ 194227652Sgrehan 195227652Sgrehan return (VIRTIO_BUS_ALLOC_VIRTQUEUES(device_get_parent(dev), flags, 196227652Sgrehan nvqs, info)); 197227652Sgrehan} 198227652Sgrehan 199227652Sgrehanint 200227652Sgrehanvirtio_setup_intr(device_t dev, enum intr_type type) 201227652Sgrehan{ 202227652Sgrehan 203227652Sgrehan return (VIRTIO_BUS_SETUP_INTR(device_get_parent(dev), type)); 204227652Sgrehan} 205227652Sgrehan 206227652Sgrehanint 207227652Sgrehanvirtio_with_feature(device_t dev, uint64_t feature) 208227652Sgrehan{ 209227652Sgrehan 210227652Sgrehan return (VIRTIO_BUS_WITH_FEATURE(device_get_parent(dev), feature)); 211227652Sgrehan} 212227652Sgrehan 213227652Sgrehanvoid 214227652Sgrehanvirtio_stop(device_t dev) 215227652Sgrehan{ 216227652Sgrehan 217227652Sgrehan VIRTIO_BUS_STOP(device_get_parent(dev)); 218227652Sgrehan} 219227652Sgrehan 220227652Sgrehanint 221227652Sgrehanvirtio_reinit(device_t dev, uint64_t features) 222227652Sgrehan{ 223227652Sgrehan 224227652Sgrehan return (VIRTIO_BUS_REINIT(device_get_parent(dev), features)); 225227652Sgrehan} 226227652Sgrehan 227227652Sgrehanvoid 228227652Sgrehanvirtio_reinit_complete(device_t dev) 229227652Sgrehan{ 230227652Sgrehan 231227652Sgrehan VIRTIO_BUS_REINIT_COMPLETE(device_get_parent(dev)); 232227652Sgrehan} 233227652Sgrehan 234227652Sgrehanvoid 235227652Sgrehanvirtio_read_device_config(device_t dev, bus_size_t offset, void *dst, int len) 236227652Sgrehan{ 237227652Sgrehan 238227652Sgrehan VIRTIO_BUS_READ_DEVICE_CONFIG(device_get_parent(dev), 239227652Sgrehan offset, dst, len); 240227652Sgrehan} 241227652Sgrehan 242227652Sgrehanvoid 243227652Sgrehanvirtio_write_device_config(device_t dev, bus_size_t offset, void *dst, int len) 244227652Sgrehan{ 245227652Sgrehan 246227652Sgrehan VIRTIO_BUS_WRITE_DEVICE_CONFIG(device_get_parent(dev), 247227652Sgrehan offset, dst, len); 248227652Sgrehan} 249227652Sgrehan 250227652Sgrehanstatic int 251227652Sgrehanvirtio_modevent(module_t mod, int type, void *unused) 252227652Sgrehan{ 253227652Sgrehan int error; 254227652Sgrehan 255227652Sgrehan switch (type) { 256227652Sgrehan case MOD_LOAD: 257227652Sgrehan case MOD_QUIESCE: 258227652Sgrehan case MOD_UNLOAD: 259227652Sgrehan case MOD_SHUTDOWN: 260252707Sbryanv error = 0; 261227652Sgrehan break; 262227652Sgrehan default: 263227652Sgrehan error = EOPNOTSUPP; 264227652Sgrehan break; 265227652Sgrehan } 266227652Sgrehan 267227652Sgrehan return (error); 268227652Sgrehan} 269227652Sgrehan 270227652Sgrehanstatic moduledata_t virtio_mod = { 271227652Sgrehan "virtio", 272227652Sgrehan virtio_modevent, 273241394Skevlo 0 274227652Sgrehan}; 275227652Sgrehan 276227652SgrehanDECLARE_MODULE(virtio, virtio_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST); 277227652SgrehanMODULE_VERSION(virtio, 1); 278