2 * Copyright (c) 2011 NetApp, Inc.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
14 * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
32 #include <sys/param.h>
33 #include <sys/linker_set.h>
36 #include <sys/ioctl.h>
54 #define VTBLK_RINGSZ 64
56 #define VTBLK_CFGSZ 28
58 #define VTBLK_R_CFG VTCFG_R_CFG0
59 #define VTBLK_R_CFG_END VTBLK_R_CFG + VTBLK_CFGSZ -1
60 #define VTBLK_R_MAX VTBLK_R_CFG_END
62 #define VTBLK_REGSZ VTBLK_R_MAX+1
64 #define VTBLK_MAXSEGS 32
67 #define VTBLK_S_IOERR 1
72 #define VTBLK_S_HOSTCAPS \
73 ( 0x00000004 | /* host maximum request segments */ \
74 0x10000000 ) /* supports indirect descriptors */
79 uint16_t hq_cur_aidx; /* trails behind 'avail_idx' */
81 /* Host-context pointers to the queue */
82 struct virtio_desc *hq_dtable;
83 uint16_t *hq_avail_flags;
84 uint16_t *hq_avail_idx; /* monotonically increasing */
85 uint16_t *hq_avail_ring;
87 uint16_t *hq_used_flags;
88 uint16_t *hq_used_idx; /* monotonically increasing */
89 struct virtio_used *hq_used_ring;
96 uint64_t vbc_capacity;
97 uint32_t vbc_size_max;
102 uint32_t vbc_blk_size;
103 uint32_t vbc_sectors_max;
105 CTASSERT(sizeof(struct vtblk_config) == VTBLK_CFGSZ);
108 * Fixed-size block header
110 struct virtio_blk_hdr {
111 #define VBH_OP_READ 0
112 #define VBH_OP_WRITE 1
121 static int pci_vtblk_debug;
122 #define DPRINTF(params) if (pci_vtblk_debug) printf params
123 #define WPRINTF(params) printf params
128 struct pci_vtblk_softc {
129 struct pci_devinst *vbsc_pi;
134 uint32_t vbsc_features;
136 struct vring_hqueue vbsc_q;
137 struct vtblk_config vbsc_cfg;
141 * Return the number of available descriptors in the vring taking care
142 * of the 16-bit index wraparound.
145 hq_num_avail(struct vring_hqueue *hq)
149 if (*hq->hq_avail_idx >= hq->hq_cur_aidx)
150 ndesc = *hq->hq_avail_idx - hq->hq_cur_aidx;
152 ndesc = UINT16_MAX - hq->hq_cur_aidx + *hq->hq_avail_idx + 1;
154 assert(ndesc >= 0 && ndesc <= hq->hq_size);
160 pci_vtblk_update_status(struct pci_vtblk_softc *sc, uint32_t value)
163 DPRINTF(("vtblk: device reset requested !\n"));
166 sc->vbsc_status = value;
170 pci_vtblk_proc(struct pci_vtblk_softc *sc, struct vring_hqueue *hq)
172 struct iovec iov[VTBLK_MAXSEGS];
173 struct virtio_blk_hdr *vbh;
174 struct virtio_desc *vd, *vid;
175 struct virtio_used *vu;
181 int uidx, aidx, didx;
185 uidx = *hq->hq_used_idx;
186 aidx = hq->hq_cur_aidx;
187 didx = hq->hq_avail_ring[aidx % hq->hq_size];
188 assert(didx >= 0 && didx < hq->hq_size);
190 vd = &hq->hq_dtable[didx];
193 * Verify that the descriptor is indirect, and obtain
194 * the pointer to the indirect descriptor.
195 * There has to be space for at least 3 descriptors
196 * in the indirect descriptor array: the block header,
197 * 1 or more data descriptors, and a status byte.
199 assert(vd->vd_flags & VRING_DESC_F_INDIRECT);
201 nsegs = vd->vd_len / sizeof(struct virtio_desc);
203 assert(nsegs < VTBLK_MAXSEGS + 2);
205 vid = paddr_guest2host(vd->vd_addr);
206 assert((vid->vd_flags & VRING_DESC_F_INDIRECT) == 0);
209 * The first descriptor will be the read-only fixed header
211 vbh = paddr_guest2host(vid[0].vd_addr);
212 assert(vid[0].vd_len == sizeof(struct virtio_blk_hdr));
213 assert(vid[0].vd_flags & VRING_DESC_F_NEXT);
214 assert((vid[0].vd_flags & VRING_DESC_F_WRITE) == 0);
216 writeop = (vbh->vbh_type == VBH_OP_WRITE);
218 offset = vbh->vbh_sector * DEV_BSIZE;
221 * Build up the iovec based on the guest's data descriptors
223 for (i = 1, iolen = 0; i < nsegs - 1; i++) {
224 iov[i-1].iov_base = paddr_guest2host(vid[i].vd_addr);
225 iov[i-1].iov_len = vid[i].vd_len;
226 iolen += vid[i].vd_len;
228 assert(vid[i].vd_flags & VRING_DESC_F_NEXT);
229 assert((vid[i].vd_flags & VRING_DESC_F_INDIRECT) == 0);
232 * - write op implies read-only descriptor,
233 * - read op implies write-only descriptor,
234 * therefore test the inverse of the descriptor bit
237 assert(((vid[i].vd_flags & VRING_DESC_F_WRITE) == 0) ==
241 /* Lastly, get the address of the status byte */
242 status = paddr_guest2host(vid[nsegs - 1].vd_addr);
243 assert(vid[nsegs - 1].vd_len == 1);
244 assert((vid[nsegs - 1].vd_flags & VRING_DESC_F_NEXT) == 0);
245 assert(vid[nsegs - 1].vd_flags & VRING_DESC_F_WRITE);
247 DPRINTF(("virtio-block: %s op, %d bytes, %d segs, offset %ld\n\r",
248 writeop ? "write" : "read", iolen, nsegs - 2, offset));
251 err = pwritev(sc->vbsc_fd, iov, nsegs - 2, offset);
253 err = preadv(sc->vbsc_fd, iov, nsegs - 2, offset);
256 *status = err < 0 ? VTBLK_S_IOERR : VTBLK_S_OK;
259 * Return the single indirect descriptor back to the host
261 vu = &hq->hq_used_ring[uidx % hq->hq_size];
265 *hq->hq_used_idx += 1;
269 pci_vtblk_qnotify(struct pci_vtblk_softc *sc)
271 struct vring_hqueue *hq = &sc->vbsc_q;
276 * Calculate number of ring entries to process
278 ndescs = hq_num_avail(hq);
284 * Run through all the entries, placing them into iovecs and
285 * sending when an end-of-packet is found
287 for (i = 0; i < ndescs; i++)
288 pci_vtblk_proc(sc, hq);
291 * Generate an interrupt if able
293 if ((*hq->hq_avail_flags & VRING_AVAIL_F_NO_INTERRUPT) == 0 &&
296 pci_generate_msi(sc->vbsc_pi, 0);
302 pci_vtblk_ring_init(struct pci_vtblk_softc *sc, uint64_t pfn)
304 struct vring_hqueue *hq;
306 sc->vbsc_pfn = pfn << VRING_PFN;
309 * Set up host pointers to the various parts of the
313 hq->hq_size = VTBLK_RINGSZ;
315 hq->hq_dtable = paddr_guest2host(pfn << VRING_PFN);
316 hq->hq_avail_flags = (uint16_t *)(hq->hq_dtable + hq->hq_size);
317 hq->hq_avail_idx = hq->hq_avail_flags + 1;
318 hq->hq_avail_ring = hq->hq_avail_flags + 2;
319 hq->hq_used_flags = (uint16_t *)roundup2((uintptr_t)hq->hq_avail_ring,
321 hq->hq_used_idx = hq->hq_used_flags + 1;
322 hq->hq_used_ring = (struct virtio_used *)(hq->hq_used_flags + 2);
325 * Initialize queue indexes
331 pci_vtblk_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts)
334 struct pci_vtblk_softc *sc;
340 printf("virtio-block: backing device required\n");
345 * Access to guest memory is required. Fail if
348 if (paddr_guest2host(0) == NULL)
352 * The supplied backing file has to exist
354 fd = open(opts, O_RDWR);
356 perror("Could not open backing file");
360 if (fstat(fd, &sbuf) < 0) {
361 perror("Could not stat backing file");
367 * Deal with raw devices
371 if (S_ISCHR(sbuf.st_mode)) {
372 if (ioctl(fd, DIOCGMEDIASIZE, &size) < 0 ||
373 ioctl(fd, DIOCGSECTORSIZE, §sz)) {
374 perror("Could not fetch dev blk/sector size");
382 sc = malloc(sizeof(struct pci_vtblk_softc));
383 memset(sc, 0, sizeof(struct pci_vtblk_softc));
389 /* setup virtio block config space */
390 sc->vbsc_cfg.vbc_capacity = size / sectsz;
391 sc->vbsc_cfg.vbc_seg_max = VTBLK_MAXSEGS;
392 sc->vbsc_cfg.vbc_blk_size = sectsz;
393 sc->vbsc_cfg.vbc_size_max = 0; /* not negotiated */
394 sc->vbsc_cfg.vbc_geom_c = 0; /* no geometry */
395 sc->vbsc_cfg.vbc_geom_h = 0;
396 sc->vbsc_cfg.vbc_geom_s = 0;
397 sc->vbsc_cfg.vbc_sectors_max = 0;
399 /* initialize config space */
400 pci_set_cfgdata16(pi, PCIR_DEVICE, VIRTIO_DEV_BLOCK);
401 pci_set_cfgdata16(pi, PCIR_VENDOR, VIRTIO_VENDOR);
402 pci_set_cfgdata8(pi, PCIR_CLASS, PCIC_STORAGE);
403 pci_set_cfgdata16(pi, PCIR_SUBDEV_0, VIRTIO_TYPE_BLOCK);
404 pci_emul_add_msicap(pi, 1);
405 pci_emul_alloc_bar(pi, 0, PCIBAR_IO, VTBLK_REGSZ);
411 pci_vtblk_write(struct vmctx *ctx, int vcpu, struct pci_devinst *pi,
412 int baridx, uint64_t offset, int size, uint64_t value)
414 struct pci_vtblk_softc *sc = pi->pi_arg;
418 if (offset + size > VTBLK_REGSZ) {
419 DPRINTF(("vtblk_write: 2big, offset %ld size %d\n",
425 case VTCFG_R_GUESTCAP:
427 sc->vbsc_features = value & VTBLK_S_HOSTCAPS;
431 pci_vtblk_ring_init(sc, value);
435 sc->vbsc_lastq = value;
437 case VTCFG_R_QNOTIFY:
440 pci_vtblk_qnotify(sc);
444 pci_vtblk_update_status(sc, value);
446 case VTCFG_R_HOSTCAP:
449 case VTBLK_R_CFG ... VTBLK_R_CFG_END:
450 DPRINTF(("vtblk: write to readonly reg %ld\n\r", offset));
453 DPRINTF(("vtblk: unknown i/o write offset %ld\n\r", offset));
460 pci_vtblk_read(struct vmctx *ctx, int vcpu, struct pci_devinst *pi,
461 int baridx, uint64_t offset, int size)
463 struct pci_vtblk_softc *sc = pi->pi_arg;
469 if (offset + size > VTBLK_REGSZ) {
470 DPRINTF(("vtblk_read: 2big, offset %ld size %d\n",
476 case VTCFG_R_HOSTCAP:
478 value = VTBLK_S_HOSTCAPS;
480 case VTCFG_R_GUESTCAP:
482 value = sc->vbsc_features; /* XXX never read ? */
486 value = sc->vbsc_pfn >> VRING_PFN;
489 value = (sc->vbsc_lastq == 0) ? VTBLK_RINGSZ: 0;
493 value = sc->vbsc_lastq; /* XXX never read ? */
495 case VTCFG_R_QNOTIFY:
497 value = 0; /* XXX never read ? */
501 value = sc->vbsc_status;
505 value = sc->vbsc_isr;
506 sc->vbsc_isr = 0; /* a read clears this flag */
508 case VTBLK_R_CFG ... VTBLK_R_CFG_END:
509 assert(size + offset <= (VTBLK_R_CFG_END + 1));
510 ptr = (uint8_t *)&sc->vbsc_cfg + offset - VTBLK_R_CFG;
512 value = *(uint8_t *) ptr;
513 } else if (size == 2) {
514 value = *(uint16_t *) ptr;
516 value = *(uint32_t *) ptr;
520 DPRINTF(("vtblk: unknown i/o read offset %ld\n\r", offset));
528 struct pci_devemu pci_de_vblk = {
529 .pe_emu = "virtio-blk",
530 .pe_init = pci_vtblk_init,
531 .pe_barwrite = pci_vtblk_write,
532 .pe_barread = pci_vtblk_read
534 PCI_EMUL_SET(pci_de_vblk);