pci_virtio_block.c revision 221828
1221828Sgrehan/*- 2221828Sgrehan * Copyright (c) 2011 NetApp, Inc. 3221828Sgrehan * All rights reserved. 4221828Sgrehan * 5221828Sgrehan * Redistribution and use in source and binary forms, with or without 6221828Sgrehan * modification, are permitted provided that the following conditions 7221828Sgrehan * are met: 8221828Sgrehan * 1. Redistributions of source code must retain the above copyright 9221828Sgrehan * notice, this list of conditions and the following disclaimer. 10221828Sgrehan * 2. Redistributions in binary form must reproduce the above copyright 11221828Sgrehan * notice, this list of conditions and the following disclaimer in the 12221828Sgrehan * documentation and/or other materials provided with the distribution. 13221828Sgrehan * 14221828Sgrehan * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND 15221828Sgrehan * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16221828Sgrehan * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17221828Sgrehan * ARE DISCLAIMED. IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE 18221828Sgrehan * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19221828Sgrehan * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20221828Sgrehan * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21221828Sgrehan * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22221828Sgrehan * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23221828Sgrehan * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24221828Sgrehan * SUCH DAMAGE. 25221828Sgrehan * 26221828Sgrehan * $FreeBSD$ 27221828Sgrehan */ 28221828Sgrehan 29221828Sgrehan#include <sys/cdefs.h> 30221828Sgrehan__FBSDID("$FreeBSD$"); 31221828Sgrehan 32221828Sgrehan#include <sys/param.h> 33221828Sgrehan#include <sys/linker_set.h> 34221828Sgrehan#include <sys/stat.h> 35221828Sgrehan#include <sys/uio.h> 36221828Sgrehan#include <sys/ioctl.h> 37221828Sgrehan 38221828Sgrehan#include <errno.h> 39221828Sgrehan#include <fcntl.h> 40221828Sgrehan#include <stdio.h> 41221828Sgrehan#include <stdlib.h> 42221828Sgrehan#include <stdint.h> 43221828Sgrehan#include <string.h> 44221828Sgrehan#include <strings.h> 45221828Sgrehan#include <unistd.h> 46221828Sgrehan#include <assert.h> 47221828Sgrehan#include <pthread.h> 48221828Sgrehan 49221828Sgrehan#include "fbsdrun.h" 50221828Sgrehan#include "pci_emul.h" 51221828Sgrehan#include "virtio.h" 52221828Sgrehan 53221828Sgrehan#define VTBLK_RINGSZ 64 54221828Sgrehan 55221828Sgrehan#define VTBLK_CFGSZ 28 56221828Sgrehan 57221828Sgrehan#define VTBLK_R_CFG VTCFG_R_CFG0 58221828Sgrehan#define VTBLK_R_CFG_END VTBLK_R_CFG + VTBLK_CFGSZ -1 59221828Sgrehan#define VTBLK_R_MAX VTBLK_R_CFG_END 60221828Sgrehan 61221828Sgrehan#define VTBLK_REGSZ VTBLK_R_MAX+1 62221828Sgrehan 63221828Sgrehan#define VTBLK_MAXSEGS 32 64221828Sgrehan 65221828Sgrehan#define VTBLK_S_OK 0 66221828Sgrehan#define VTBLK_S_IOERR 1 67221828Sgrehan 68221828Sgrehan/* 69221828Sgrehan * Host capabilities 70221828Sgrehan */ 71221828Sgrehan#define VTBLK_S_HOSTCAPS \ 72221828Sgrehan ( 0x00000004 | /* host maximum request segments */ \ 73221828Sgrehan 0x10000000 ) /* supports indirect descriptors */ 74221828Sgrehan 75221828Sgrehanstruct vring_hqueue { 76221828Sgrehan /* Internal state */ 77221828Sgrehan uint16_t hq_size; 78221828Sgrehan uint16_t hq_cur_aidx; /* trails behind 'avail_idx' */ 79221828Sgrehan 80221828Sgrehan /* Host-context pointers to the queue */ 81221828Sgrehan struct virtio_desc *hq_dtable; 82221828Sgrehan uint16_t *hq_avail_flags; 83221828Sgrehan uint16_t *hq_avail_idx; /* monotonically increasing */ 84221828Sgrehan uint16_t *hq_avail_ring; 85221828Sgrehan 86221828Sgrehan uint16_t *hq_used_flags; 87221828Sgrehan uint16_t *hq_used_idx; /* monotonically increasing */ 88221828Sgrehan struct virtio_used *hq_used_ring; 89221828Sgrehan}; 90221828Sgrehan 91221828Sgrehan/* 92221828Sgrehan * Config space 93221828Sgrehan */ 94221828Sgrehanstruct vtblk_config { 95221828Sgrehan uint64_t vbc_capacity; 96221828Sgrehan uint32_t vbc_size_max; 97221828Sgrehan uint32_t vbc_seg_max; 98221828Sgrehan uint16_t vbc_geom_c; 99221828Sgrehan uint8_t vbc_geom_h; 100221828Sgrehan uint8_t vbc_geom_s; 101221828Sgrehan uint32_t vbc_blk_size; 102221828Sgrehan uint32_t vbc_sectors_max; 103221828Sgrehan} __packed; 104221828SgrehanCTASSERT(sizeof(struct vtblk_config) == VTBLK_CFGSZ); 105221828Sgrehan 106221828Sgrehan/* 107221828Sgrehan * Fixed-size block header 108221828Sgrehan */ 109221828Sgrehanstruct virtio_blk_hdr { 110221828Sgrehan#define VBH_OP_READ 0 111221828Sgrehan#define VBH_OP_WRITE 1 112221828Sgrehan uint32_t vbh_type; 113221828Sgrehan uint32_t vbh_ioprio; 114221828Sgrehan uint64_t vbh_sector; 115221828Sgrehan} __packed; 116221828Sgrehan 117221828Sgrehan/* 118221828Sgrehan * Debug printf 119221828Sgrehan */ 120221828Sgrehanstatic int pci_vtblk_debug; 121221828Sgrehan#define DPRINTF(params) if (pci_vtblk_debug) printf params 122221828Sgrehan#define WPRINTF(params) printf params 123221828Sgrehan 124221828Sgrehan/* 125221828Sgrehan * Per-device softc 126221828Sgrehan */ 127221828Sgrehanstruct pci_vtblk_softc { 128221828Sgrehan struct pci_devinst *vbsc_pi; 129221828Sgrehan int vbsc_fd; 130221828Sgrehan int vbsc_status; 131221828Sgrehan int vbsc_isr; 132221828Sgrehan int vbsc_lastq; 133221828Sgrehan uint32_t vbsc_features; 134221828Sgrehan uint64_t vbsc_pfn; 135221828Sgrehan struct vring_hqueue vbsc_q; 136221828Sgrehan struct vtblk_config vbsc_cfg; 137221828Sgrehan}; 138221828Sgrehan 139221828Sgrehan/* 140221828Sgrehan * Return the number of available descriptors in the vring taking care 141221828Sgrehan * of the 16-bit index wraparound. 142221828Sgrehan */ 143221828Sgrehanstatic int 144221828Sgrehanhq_num_avail(struct vring_hqueue *hq) 145221828Sgrehan{ 146221828Sgrehan int ndesc; 147221828Sgrehan 148221828Sgrehan if (*hq->hq_avail_idx >= hq->hq_cur_aidx) 149221828Sgrehan ndesc = *hq->hq_avail_idx - hq->hq_cur_aidx; 150221828Sgrehan else 151221828Sgrehan ndesc = UINT16_MAX - hq->hq_cur_aidx + *hq->hq_avail_idx + 1; 152221828Sgrehan 153221828Sgrehan assert(ndesc >= 0 && ndesc <= hq->hq_size); 154221828Sgrehan 155221828Sgrehan return (ndesc); 156221828Sgrehan} 157221828Sgrehan 158221828Sgrehanstatic void 159221828Sgrehanpci_vtblk_update_status(struct pci_vtblk_softc *sc, uint32_t value) 160221828Sgrehan{ 161221828Sgrehan if (value == 0) { 162221828Sgrehan DPRINTF(("vtblk: device reset requested !\n")); 163221828Sgrehan } 164221828Sgrehan 165221828Sgrehan sc->vbsc_status = value; 166221828Sgrehan} 167221828Sgrehan 168221828Sgrehanstatic void 169221828Sgrehanpci_vtblk_proc(struct pci_vtblk_softc *sc, struct vring_hqueue *hq) 170221828Sgrehan{ 171221828Sgrehan struct iovec iov[VTBLK_MAXSEGS]; 172221828Sgrehan struct virtio_blk_hdr *vbh; 173221828Sgrehan struct virtio_desc *vd, *vid; 174221828Sgrehan struct virtio_used *vu; 175221828Sgrehan uint8_t *status; 176221828Sgrehan int i; 177221828Sgrehan int err; 178221828Sgrehan int iolen; 179221828Sgrehan int nsegs; 180221828Sgrehan int uidx, aidx, didx; 181221828Sgrehan int writeop; 182221828Sgrehan off_t offset; 183221828Sgrehan 184221828Sgrehan uidx = *hq->hq_used_idx; 185221828Sgrehan aidx = hq->hq_cur_aidx; 186221828Sgrehan didx = hq->hq_avail_ring[aidx % hq->hq_size]; 187221828Sgrehan assert(didx >= 0 && didx < hq->hq_size); 188221828Sgrehan 189221828Sgrehan vd = &hq->hq_dtable[didx]; 190221828Sgrehan 191221828Sgrehan /* 192221828Sgrehan * Verify that the descriptor is indirect, and obtain 193221828Sgrehan * the pointer to the indirect descriptor. 194221828Sgrehan * There has to be space for at least 3 descriptors 195221828Sgrehan * in the indirect descriptor array: the block header, 196221828Sgrehan * 1 or more data descriptors, and a status byte. 197221828Sgrehan */ 198221828Sgrehan assert(vd->vd_flags & VRING_DESC_F_INDIRECT); 199221828Sgrehan 200221828Sgrehan nsegs = vd->vd_len / sizeof(struct virtio_desc); 201221828Sgrehan assert(nsegs >= 3); 202221828Sgrehan assert(nsegs < VTBLK_MAXSEGS + 2); 203221828Sgrehan 204221828Sgrehan vid = paddr_guest2host(vd->vd_addr); 205221828Sgrehan assert((vid->vd_flags & VRING_DESC_F_INDIRECT) == 0); 206221828Sgrehan 207221828Sgrehan /* 208221828Sgrehan * The first descriptor will be the read-only fixed header 209221828Sgrehan */ 210221828Sgrehan vbh = paddr_guest2host(vid[0].vd_addr); 211221828Sgrehan assert(vid[0].vd_len == sizeof(struct virtio_blk_hdr)); 212221828Sgrehan assert(vid[0].vd_flags & VRING_DESC_F_NEXT); 213221828Sgrehan assert((vid[0].vd_flags & VRING_DESC_F_WRITE) == 0); 214221828Sgrehan 215221828Sgrehan writeop = (vbh->vbh_type == VBH_OP_WRITE); 216221828Sgrehan 217221828Sgrehan offset = vbh->vbh_sector * DEV_BSIZE; 218221828Sgrehan 219221828Sgrehan /* 220221828Sgrehan * Build up the iovec based on the guest's data descriptors 221221828Sgrehan */ 222221828Sgrehan for (i = 1, iolen = 0; i < nsegs - 1; i++) { 223221828Sgrehan iov[i-1].iov_base = paddr_guest2host(vid[i].vd_addr); 224221828Sgrehan iov[i-1].iov_len = vid[i].vd_len; 225221828Sgrehan iolen += vid[i].vd_len; 226221828Sgrehan 227221828Sgrehan assert(vid[i].vd_flags & VRING_DESC_F_NEXT); 228221828Sgrehan assert((vid[i].vd_flags & VRING_DESC_F_INDIRECT) == 0); 229221828Sgrehan 230221828Sgrehan /* 231221828Sgrehan * - write op implies read-only descriptor, 232221828Sgrehan * - read op implies write-only descriptor, 233221828Sgrehan * therefore test the inverse of the descriptor bit 234221828Sgrehan * to the op. 235221828Sgrehan */ 236221828Sgrehan assert(((vid[i].vd_flags & VRING_DESC_F_WRITE) == 0) == 237221828Sgrehan writeop); 238221828Sgrehan } 239221828Sgrehan 240221828Sgrehan /* Lastly, get the address of the status byte */ 241221828Sgrehan status = paddr_guest2host(vid[nsegs - 1].vd_addr); 242221828Sgrehan assert(vid[nsegs - 1].vd_len == 1); 243221828Sgrehan assert((vid[nsegs - 1].vd_flags & VRING_DESC_F_NEXT) == 0); 244221828Sgrehan assert(vid[nsegs - 1].vd_flags & VRING_DESC_F_WRITE); 245221828Sgrehan 246221828Sgrehan DPRINTF(("virtio-block: %s op, %d bytes, %d segs, offset %ld\n\r", 247221828Sgrehan writeop ? "write" : "read", iolen, nsegs - 2, offset)); 248221828Sgrehan 249221828Sgrehan if (writeop){ 250221828Sgrehan err = pwritev(sc->vbsc_fd, iov, nsegs - 2, offset); 251221828Sgrehan } else { 252221828Sgrehan err = preadv(sc->vbsc_fd, iov, nsegs - 2, offset); 253221828Sgrehan } 254221828Sgrehan 255221828Sgrehan *status = err < 0 ? VTBLK_S_IOERR : VTBLK_S_OK; 256221828Sgrehan 257221828Sgrehan /* 258221828Sgrehan * Return the single indirect descriptor back to the host 259221828Sgrehan */ 260221828Sgrehan vu = &hq->hq_used_ring[uidx % hq->hq_size]; 261221828Sgrehan vu->vu_idx = didx; 262221828Sgrehan vu->vu_tlen = 1; 263221828Sgrehan hq->hq_cur_aidx++; 264221828Sgrehan *hq->hq_used_idx += 1; 265221828Sgrehan} 266221828Sgrehan 267221828Sgrehanstatic void 268221828Sgrehanpci_vtblk_qnotify(struct pci_vtblk_softc *sc) 269221828Sgrehan{ 270221828Sgrehan struct vring_hqueue *hq = &sc->vbsc_q; 271221828Sgrehan int i; 272221828Sgrehan int ndescs; 273221828Sgrehan 274221828Sgrehan /* 275221828Sgrehan * Calculate number of ring entries to process 276221828Sgrehan */ 277221828Sgrehan ndescs = hq_num_avail(hq); 278221828Sgrehan 279221828Sgrehan if (ndescs == 0) 280221828Sgrehan return; 281221828Sgrehan 282221828Sgrehan /* 283221828Sgrehan * Run through all the entries, placing them into iovecs and 284221828Sgrehan * sending when an end-of-packet is found 285221828Sgrehan */ 286221828Sgrehan for (i = 0; i < ndescs; i++) 287221828Sgrehan pci_vtblk_proc(sc, hq); 288221828Sgrehan 289221828Sgrehan /* 290221828Sgrehan * Generate an interrupt if able 291221828Sgrehan */ 292221828Sgrehan if ((*hq->hq_avail_flags & VRING_AVAIL_F_NO_INTERRUPT) == 0 && 293221828Sgrehan sc->vbsc_isr == 0) { 294221828Sgrehan sc->vbsc_isr = 1; 295221828Sgrehan pci_generate_msi(sc->vbsc_pi, 0); 296221828Sgrehan } 297221828Sgrehan 298221828Sgrehan} 299221828Sgrehan 300221828Sgrehanstatic void 301221828Sgrehanpci_vtblk_ring_init(struct pci_vtblk_softc *sc, uint64_t pfn) 302221828Sgrehan{ 303221828Sgrehan struct vring_hqueue *hq; 304221828Sgrehan 305221828Sgrehan sc->vbsc_pfn = pfn << VRING_PFN; 306221828Sgrehan 307221828Sgrehan /* 308221828Sgrehan * Set up host pointers to the various parts of the 309221828Sgrehan * queue 310221828Sgrehan */ 311221828Sgrehan hq = &sc->vbsc_q; 312221828Sgrehan hq->hq_size = VTBLK_RINGSZ; 313221828Sgrehan 314221828Sgrehan hq->hq_dtable = paddr_guest2host(pfn << VRING_PFN); 315221828Sgrehan hq->hq_avail_flags = (uint16_t *)(hq->hq_dtable + hq->hq_size); 316221828Sgrehan hq->hq_avail_idx = hq->hq_avail_flags + 1; 317221828Sgrehan hq->hq_avail_ring = hq->hq_avail_flags + 2; 318221828Sgrehan hq->hq_used_flags = (uint16_t *)roundup2((uintptr_t)hq->hq_avail_ring, 319221828Sgrehan VRING_ALIGN); 320221828Sgrehan hq->hq_used_idx = hq->hq_used_flags + 1; 321221828Sgrehan hq->hq_used_ring = (struct virtio_used *)(hq->hq_used_flags + 2); 322221828Sgrehan 323221828Sgrehan /* 324221828Sgrehan * Initialize queue indexes 325221828Sgrehan */ 326221828Sgrehan hq->hq_cur_aidx = 0; 327221828Sgrehan} 328221828Sgrehan 329221828Sgrehanstatic int 330221828Sgrehanpci_vtblk_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts) 331221828Sgrehan{ 332221828Sgrehan struct stat sbuf; 333221828Sgrehan struct pci_vtblk_softc *sc; 334221828Sgrehan int fd; 335221828Sgrehan 336221828Sgrehan if (opts == NULL) { 337221828Sgrehan printf("virtio-block: backing device required\n"); 338221828Sgrehan return (1); 339221828Sgrehan } 340221828Sgrehan 341221828Sgrehan /* 342221828Sgrehan * Access to guest memory is required. Fail if 343221828Sgrehan * memory not mapped 344221828Sgrehan */ 345221828Sgrehan if (paddr_guest2host(0) == NULL) 346221828Sgrehan return (1); 347221828Sgrehan 348221828Sgrehan /* 349221828Sgrehan * The supplied backing file has to exist 350221828Sgrehan */ 351221828Sgrehan fd = open(opts, O_RDWR); 352221828Sgrehan if (fd < 0) { 353221828Sgrehan perror("Could not open backing file"); 354221828Sgrehan return (1); 355221828Sgrehan } 356221828Sgrehan 357221828Sgrehan if (fstat(fd, &sbuf) < 0) { 358221828Sgrehan perror("Could not stat backing file"); 359221828Sgrehan close(fd); 360221828Sgrehan return (1); 361221828Sgrehan } 362221828Sgrehan 363221828Sgrehan sc = malloc(sizeof(struct pci_vtblk_softc)); 364221828Sgrehan memset(sc, 0, sizeof(struct pci_vtblk_softc)); 365221828Sgrehan 366221828Sgrehan pi->pi_arg = sc; 367221828Sgrehan sc->vbsc_pi = pi; 368221828Sgrehan sc->vbsc_fd = fd; 369221828Sgrehan 370221828Sgrehan /* setup virtio block config space */ 371221828Sgrehan sc->vbsc_cfg.vbc_capacity = sbuf.st_size / DEV_BSIZE; 372221828Sgrehan sc->vbsc_cfg.vbc_seg_max = VTBLK_MAXSEGS; 373221828Sgrehan sc->vbsc_cfg.vbc_blk_size = DEV_BSIZE; 374221828Sgrehan sc->vbsc_cfg.vbc_size_max = 0; /* not negotiated */ 375221828Sgrehan sc->vbsc_cfg.vbc_geom_c = 0; /* no geometry */ 376221828Sgrehan sc->vbsc_cfg.vbc_geom_h = 0; 377221828Sgrehan sc->vbsc_cfg.vbc_geom_s = 0; 378221828Sgrehan sc->vbsc_cfg.vbc_sectors_max = 0; 379221828Sgrehan 380221828Sgrehan /* initialize config space */ 381221828Sgrehan pci_set_cfgdata16(pi, PCIR_DEVICE, VIRTIO_DEV_BLOCK); 382221828Sgrehan pci_set_cfgdata16(pi, PCIR_VENDOR, VIRTIO_VENDOR); 383221828Sgrehan pci_set_cfgdata8(pi, PCIR_CLASS, PCIC_STORAGE); 384221828Sgrehan pci_set_cfgdata16(pi, PCIR_SUBDEV_0, VIRTIO_TYPE_BLOCK); 385221828Sgrehan pci_emul_alloc_bar(pi, 0, 0, PCIBAR_IO, VTBLK_REGSZ); 386221828Sgrehan pci_emul_add_msicap(pi, 1); 387221828Sgrehan 388221828Sgrehan return (0); 389221828Sgrehan} 390221828Sgrehan 391221828Sgrehanstatic void 392221828Sgrehanpci_vtblk_write(struct pci_devinst *pi, int baridx, int offset, int size, 393221828Sgrehan uint32_t value) 394221828Sgrehan{ 395221828Sgrehan struct pci_vtblk_softc *sc = pi->pi_arg; 396221828Sgrehan 397221828Sgrehan if (offset + size > VTBLK_REGSZ) { 398221828Sgrehan DPRINTF(("vtblk_write: 2big, offset %d size %d\n", 399221828Sgrehan offset, size)); 400221828Sgrehan return; 401221828Sgrehan } 402221828Sgrehan 403221828Sgrehan switch (offset) { 404221828Sgrehan case VTCFG_R_GUESTCAP: 405221828Sgrehan assert(size == 4); 406221828Sgrehan sc->vbsc_features = value & VTBLK_S_HOSTCAPS; 407221828Sgrehan break; 408221828Sgrehan case VTCFG_R_PFN: 409221828Sgrehan assert(size == 4); 410221828Sgrehan pci_vtblk_ring_init(sc, value); 411221828Sgrehan break; 412221828Sgrehan case VTCFG_R_QSEL: 413221828Sgrehan assert(size == 2); 414221828Sgrehan sc->vbsc_lastq = value; 415221828Sgrehan break; 416221828Sgrehan case VTCFG_R_QNOTIFY: 417221828Sgrehan assert(size == 2); 418221828Sgrehan assert(value == 0); 419221828Sgrehan pci_vtblk_qnotify(sc); 420221828Sgrehan break; 421221828Sgrehan case VTCFG_R_STATUS: 422221828Sgrehan assert(size == 1); 423221828Sgrehan pci_vtblk_update_status(sc, value); 424221828Sgrehan break; 425221828Sgrehan case VTCFG_R_HOSTCAP: 426221828Sgrehan case VTCFG_R_QNUM: 427221828Sgrehan case VTCFG_R_ISR: 428221828Sgrehan case VTBLK_R_CFG ... VTBLK_R_CFG_END: 429221828Sgrehan DPRINTF(("vtblk: write to readonly reg %d\n\r", offset)); 430221828Sgrehan break; 431221828Sgrehan default: 432221828Sgrehan DPRINTF(("vtblk: unknown i/o write offset %d\n\r", offset)); 433221828Sgrehan value = 0; 434221828Sgrehan break; 435221828Sgrehan } 436221828Sgrehan} 437221828Sgrehan 438221828Sgrehanuint32_t 439221828Sgrehanpci_vtblk_read(struct pci_devinst *pi, int baridx, int offset, int size) 440221828Sgrehan{ 441221828Sgrehan struct pci_vtblk_softc *sc = pi->pi_arg; 442221828Sgrehan uint32_t value; 443221828Sgrehan 444221828Sgrehan if (offset + size > VTBLK_REGSZ) { 445221828Sgrehan DPRINTF(("vtblk_read: 2big, offset %d size %d\n", 446221828Sgrehan offset, size)); 447221828Sgrehan return (0); 448221828Sgrehan } 449221828Sgrehan 450221828Sgrehan switch (offset) { 451221828Sgrehan case VTCFG_R_HOSTCAP: 452221828Sgrehan assert(size == 4); 453221828Sgrehan value = VTBLK_S_HOSTCAPS; 454221828Sgrehan break; 455221828Sgrehan case VTCFG_R_GUESTCAP: 456221828Sgrehan assert(size == 4); 457221828Sgrehan value = sc->vbsc_features; /* XXX never read ? */ 458221828Sgrehan break; 459221828Sgrehan case VTCFG_R_PFN: 460221828Sgrehan assert(size == 4); 461221828Sgrehan value = sc->vbsc_pfn >> VRING_PFN; 462221828Sgrehan break; 463221828Sgrehan case VTCFG_R_QNUM: 464221828Sgrehan value = (sc->vbsc_lastq == 0) ? VTBLK_RINGSZ: 0; 465221828Sgrehan break; 466221828Sgrehan case VTCFG_R_QSEL: 467221828Sgrehan assert(size == 2); 468221828Sgrehan value = sc->vbsc_lastq; /* XXX never read ? */ 469221828Sgrehan break; 470221828Sgrehan case VTCFG_R_QNOTIFY: 471221828Sgrehan assert(size == 2); 472221828Sgrehan value = 0; /* XXX never read ? */ 473221828Sgrehan break; 474221828Sgrehan case VTCFG_R_STATUS: 475221828Sgrehan assert(size == 1); 476221828Sgrehan value = sc->vbsc_status; 477221828Sgrehan break; 478221828Sgrehan case VTCFG_R_ISR: 479221828Sgrehan assert(size == 1); 480221828Sgrehan value = sc->vbsc_isr; 481221828Sgrehan sc->vbsc_isr = 0; /* a read clears this flag */ 482221828Sgrehan break; 483221828Sgrehan case VTBLK_R_CFG ... VTBLK_R_CFG_END: 484221828Sgrehan assert(size == 1); 485221828Sgrehan value = *((uint8_t *)&sc->vbsc_cfg + offset - VTBLK_R_CFG); 486221828Sgrehan break; 487221828Sgrehan default: 488221828Sgrehan DPRINTF(("vtblk: unknown i/o read offset %d\n\r", offset)); 489221828Sgrehan value = 0; 490221828Sgrehan break; 491221828Sgrehan } 492221828Sgrehan 493221828Sgrehan return (value); 494221828Sgrehan} 495221828Sgrehan 496221828Sgrehanstruct pci_devemu pci_de_vblk = { 497221828Sgrehan .pe_emu = "virtio-blk", 498221828Sgrehan .pe_init = pci_vtblk_init, 499221828Sgrehan .pe_iow = pci_vtblk_write, 500221828Sgrehan .pe_ior = pci_vtblk_read, 501221828Sgrehan}; 502221828SgrehanPCI_EMUL_SET(pci_de_vblk); 503