]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/bhyve/pci_virtio_block.c
IFC @ r244983.
[FreeBSD/FreeBSD.git] / usr.sbin / bhyve / pci_virtio_block.c
1 /*-
2  * Copyright (c) 2011 NetApp, Inc.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
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.
13  *
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
24  * SUCH DAMAGE.
25  *
26  * $FreeBSD$
27  */
28
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
31
32 #include <sys/param.h>
33 #include <sys/linker_set.h>
34 #include <sys/stat.h>
35 #include <sys/uio.h>
36 #include <sys/ioctl.h>
37 #include <sys/disk.h>
38
39 #include <errno.h>
40 #include <fcntl.h>
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <stdint.h>
44 #include <string.h>
45 #include <strings.h>
46 #include <unistd.h>
47 #include <assert.h>
48 #include <pthread.h>
49
50 #include "bhyverun.h"
51 #include "pci_emul.h"
52 #include "virtio.h"
53
54 #define VTBLK_RINGSZ    64
55
56 #define VTBLK_CFGSZ     28
57
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
61
62 #define VTBLK_REGSZ             VTBLK_R_MAX+1
63
64 #define VTBLK_MAXSEGS   32
65
66 #define VTBLK_S_OK      0
67 #define VTBLK_S_IOERR   1
68
69 /*
70  * Host capabilities
71  */
72 #define VTBLK_S_HOSTCAPS      \
73   ( 0x00000004 |        /* host maximum request segments */ \
74     0x10000000 )        /* supports indirect descriptors */
75
76 struct vring_hqueue {
77         /* Internal state */
78         uint16_t        hq_size;
79         uint16_t        hq_cur_aidx;            /* trails behind 'avail_idx' */
80
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;
86
87         uint16_t        *hq_used_flags;
88         uint16_t        *hq_used_idx;           /* monotonically increasing */
89         struct virtio_used *hq_used_ring;
90 };
91
92 /*
93  * Config space
94  */
95 struct vtblk_config {
96         uint64_t        vbc_capacity;
97         uint32_t        vbc_size_max;
98         uint32_t        vbc_seg_max;
99         uint16_t        vbc_geom_c;
100         uint8_t         vbc_geom_h;
101         uint8_t         vbc_geom_s;
102         uint32_t        vbc_blk_size;
103         uint32_t        vbc_sectors_max;
104 } __packed;
105 CTASSERT(sizeof(struct vtblk_config) == VTBLK_CFGSZ);
106
107 /*
108  * Fixed-size block header
109  */
110 struct virtio_blk_hdr {
111 #define VBH_OP_READ     0
112 #define VBH_OP_WRITE    1
113         uint32_t        vbh_type;
114         uint32_t        vbh_ioprio;
115         uint64_t        vbh_sector;
116 } __packed;
117
118 /*
119  * Debug printf
120  */
121 static int pci_vtblk_debug;
122 #define DPRINTF(params) if (pci_vtblk_debug) printf params
123 #define WPRINTF(params) printf params
124
125 /*
126  * Per-device softc
127  */
128 struct pci_vtblk_softc {
129         struct pci_devinst *vbsc_pi;
130         int             vbsc_fd;
131         int             vbsc_status;
132         int             vbsc_isr;
133         int             vbsc_lastq;
134         uint32_t        vbsc_features;
135         uint64_t        vbsc_pfn;
136         struct vring_hqueue vbsc_q;
137         struct vtblk_config vbsc_cfg;   
138 };
139
140 /*
141  * Return the number of available descriptors in the vring taking care
142  * of the 16-bit index wraparound.
143  */
144 static int
145 hq_num_avail(struct vring_hqueue *hq)
146 {
147         int ndesc;
148
149         if (*hq->hq_avail_idx >= hq->hq_cur_aidx)
150                 ndesc = *hq->hq_avail_idx - hq->hq_cur_aidx;
151         else
152                 ndesc = UINT16_MAX - hq->hq_cur_aidx + *hq->hq_avail_idx + 1;
153
154         assert(ndesc >= 0 && ndesc <= hq->hq_size);
155
156         return (ndesc);
157 }
158
159 static void
160 pci_vtblk_update_status(struct pci_vtblk_softc *sc, uint32_t value)
161 {
162         if (value == 0) {
163                 DPRINTF(("vtblk: device reset requested !\n"));
164         }
165
166         sc->vbsc_status = value;
167 }
168
169 static void
170 pci_vtblk_proc(struct pci_vtblk_softc *sc, struct vring_hqueue *hq)
171 {
172         struct iovec iov[VTBLK_MAXSEGS];
173         struct virtio_blk_hdr *vbh;
174         struct virtio_desc *vd, *vid;
175         struct virtio_used *vu;
176         uint8_t *status;
177         int i;
178         int err;
179         int iolen;
180         int nsegs;
181         int uidx, aidx, didx;
182         int writeop;
183         off_t offset;
184
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);
189
190         vd = &hq->hq_dtable[didx];
191
192         /*
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.
198          */
199         assert(vd->vd_flags & VRING_DESC_F_INDIRECT);
200
201         nsegs = vd->vd_len / sizeof(struct virtio_desc);
202         assert(nsegs >= 3);
203         assert(nsegs < VTBLK_MAXSEGS + 2);
204
205         vid = paddr_guest2host(vd->vd_addr);
206         assert((vid->vd_flags & VRING_DESC_F_INDIRECT) == 0);
207
208         /*
209          * The first descriptor will be the read-only fixed header
210          */
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);
215
216         writeop = (vbh->vbh_type == VBH_OP_WRITE);
217
218         offset = vbh->vbh_sector * DEV_BSIZE;
219
220         /*
221          * Build up the iovec based on the guest's data descriptors
222          */
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;
227
228                 assert(vid[i].vd_flags & VRING_DESC_F_NEXT);
229                 assert((vid[i].vd_flags & VRING_DESC_F_INDIRECT) == 0);
230
231                 /*
232                  * - write op implies read-only descriptor,
233                  * - read op implies write-only descriptor,
234                  * therefore test the inverse of the descriptor bit
235                  * to the op.
236                  */
237                 assert(((vid[i].vd_flags & VRING_DESC_F_WRITE) == 0) ==
238                        writeop);
239         }
240
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);
246
247         DPRINTF(("virtio-block: %s op, %d bytes, %d segs, offset %ld\n\r", 
248                  writeop ? "write" : "read", iolen, nsegs - 2, offset));
249
250         if (writeop){
251                 err = pwritev(sc->vbsc_fd, iov, nsegs - 2, offset);
252         } else {
253                 err = preadv(sc->vbsc_fd, iov, nsegs - 2, offset);
254         }
255
256         *status = err < 0 ? VTBLK_S_IOERR : VTBLK_S_OK;
257
258         /*
259          * Return the single indirect descriptor back to the host
260          */
261         vu = &hq->hq_used_ring[uidx % hq->hq_size];
262         vu->vu_idx = didx;
263         vu->vu_tlen = 1;
264         hq->hq_cur_aidx++;
265         *hq->hq_used_idx += 1;
266 }
267
268 static void
269 pci_vtblk_qnotify(struct pci_vtblk_softc *sc)
270 {
271         struct vring_hqueue *hq = &sc->vbsc_q;
272         int i;
273         int ndescs;
274
275         /*
276          * Calculate number of ring entries to process
277          */
278         ndescs = hq_num_avail(hq);
279
280         if (ndescs == 0)
281                 return;
282
283         /*
284          * Run through all the entries, placing them into iovecs and
285          * sending when an end-of-packet is found
286          */
287         for (i = 0; i < ndescs; i++)
288                 pci_vtblk_proc(sc, hq);
289
290         /*
291          * Generate an interrupt if able
292          */
293         if ((*hq->hq_avail_flags & VRING_AVAIL_F_NO_INTERRUPT) == 0 &&
294                 sc->vbsc_isr == 0) {
295                 sc->vbsc_isr = 1;
296                 pci_generate_msi(sc->vbsc_pi, 0);
297         }
298         
299 }
300
301 static void
302 pci_vtblk_ring_init(struct pci_vtblk_softc *sc, uint64_t pfn)
303 {
304         struct vring_hqueue *hq;
305
306         sc->vbsc_pfn = pfn << VRING_PFN;
307         
308         /*
309          * Set up host pointers to the various parts of the
310          * queue
311          */
312         hq = &sc->vbsc_q;
313         hq->hq_size = VTBLK_RINGSZ;
314
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,
320                                                  VRING_ALIGN);
321         hq->hq_used_idx = hq->hq_used_flags + 1;
322         hq->hq_used_ring = (struct virtio_used *)(hq->hq_used_flags + 2);
323
324         /*
325          * Initialize queue indexes
326          */
327         hq->hq_cur_aidx = 0;
328 }
329
330 static int
331 pci_vtblk_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts)
332 {
333         struct stat sbuf;
334         struct pci_vtblk_softc *sc;
335         off_t size;     
336         int fd;
337         int sectsz;
338
339         if (opts == NULL) {
340                 printf("virtio-block: backing device required\n");
341                 return (1);
342         }
343
344         /*
345          * Access to guest memory is required. Fail if
346          * memory not mapped
347          */
348         if (paddr_guest2host(0) == NULL)
349                 return (1);
350
351         /*
352          * The supplied backing file has to exist
353          */
354         fd = open(opts, O_RDWR);
355         if (fd < 0) {
356                 perror("Could not open backing file");
357                 return (1);
358         }
359
360         if (fstat(fd, &sbuf) < 0) {
361                 perror("Could not stat backing file");
362                 close(fd);
363                 return (1);
364         }
365
366         /*
367          * Deal with raw devices
368          */
369         size = sbuf.st_size;
370         sectsz = DEV_BSIZE;
371         if (S_ISCHR(sbuf.st_mode)) {
372                 if (ioctl(fd, DIOCGMEDIASIZE, &size) < 0 ||
373                     ioctl(fd, DIOCGSECTORSIZE, &sectsz)) {
374                         perror("Could not fetch dev blk/sector size");
375                         close(fd);
376                         return (1);
377                 }
378                 assert(size != 0);
379                 assert(sectsz != 0);
380         }
381
382         sc = malloc(sizeof(struct pci_vtblk_softc));
383         memset(sc, 0, sizeof(struct pci_vtblk_softc));
384
385         pi->pi_arg = sc;
386         sc->vbsc_pi = pi;
387         sc->vbsc_fd = fd;
388
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;
398
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);
406
407         return (0);
408 }
409
410 static void
411 pci_vtblk_write(struct vmctx *ctx, int vcpu, struct pci_devinst *pi,
412                 int baridx, uint64_t offset, int size, uint64_t value)
413 {
414         struct pci_vtblk_softc *sc = pi->pi_arg;
415
416         assert(baridx == 0);
417
418         if (offset + size > VTBLK_REGSZ) {
419                 DPRINTF(("vtblk_write: 2big, offset %ld size %d\n",
420                          offset, size));
421                 return;
422         }
423
424         switch (offset) {
425         case VTCFG_R_GUESTCAP:
426                 assert(size == 4);
427                 sc->vbsc_features = value & VTBLK_S_HOSTCAPS;
428                 break;
429         case VTCFG_R_PFN:
430                 assert(size == 4);
431                 pci_vtblk_ring_init(sc, value);
432                 break;
433         case VTCFG_R_QSEL:
434                 assert(size == 2);
435                 sc->vbsc_lastq = value;
436                 break;
437         case VTCFG_R_QNOTIFY:
438                 assert(size == 2);
439                 assert(value == 0);
440                 pci_vtblk_qnotify(sc);
441                 break;
442         case VTCFG_R_STATUS:
443                 assert(size == 1);
444                 pci_vtblk_update_status(sc, value);
445                 break;
446         case VTCFG_R_HOSTCAP:
447         case VTCFG_R_QNUM:
448         case VTCFG_R_ISR:
449         case VTBLK_R_CFG ... VTBLK_R_CFG_END:
450                 DPRINTF(("vtblk: write to readonly reg %ld\n\r", offset));
451                 break;
452         default:
453                 DPRINTF(("vtblk: unknown i/o write offset %ld\n\r", offset));
454                 value = 0;
455                 break;
456         }
457 }
458
459 uint64_t
460 pci_vtblk_read(struct vmctx *ctx, int vcpu, struct pci_devinst *pi,
461                int baridx, uint64_t offset, int size)
462 {
463         struct pci_vtblk_softc *sc = pi->pi_arg;
464         void *ptr;
465         uint32_t value;
466
467         assert(baridx == 0);
468
469         if (offset + size > VTBLK_REGSZ) {
470                 DPRINTF(("vtblk_read: 2big, offset %ld size %d\n",
471                          offset, size));
472                 return (0);
473         }
474
475         switch (offset) {
476         case VTCFG_R_HOSTCAP:
477                 assert(size == 4);
478                 value = VTBLK_S_HOSTCAPS;
479                 break;
480         case VTCFG_R_GUESTCAP:
481                 assert(size == 4);
482                 value = sc->vbsc_features; /* XXX never read ? */
483                 break;
484         case VTCFG_R_PFN:
485                 assert(size == 4);
486                 value = sc->vbsc_pfn >> VRING_PFN;
487                 break;
488         case VTCFG_R_QNUM:
489                 value = (sc->vbsc_lastq == 0) ? VTBLK_RINGSZ: 0;
490                 break;
491         case VTCFG_R_QSEL:
492                 assert(size == 2);
493                 value = sc->vbsc_lastq; /* XXX never read ? */
494                 break;
495         case VTCFG_R_QNOTIFY:
496                 assert(size == 2);
497                 value = 0; /* XXX never read ? */
498                 break;
499         case VTCFG_R_STATUS:
500                 assert(size == 1);
501                 value = sc->vbsc_status;
502                 break;
503         case VTCFG_R_ISR:
504                 assert(size == 1);
505                 value = sc->vbsc_isr;
506                 sc->vbsc_isr = 0;     /* a read clears this flag */
507                 break;
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;
511                 if (size == 1) {
512                         value = *(uint8_t *) ptr;
513                 } else if (size == 2) {
514                         value = *(uint16_t *) ptr;
515                 } else {
516                         value = *(uint32_t *) ptr;
517                 }
518                 break;
519         default:
520                 DPRINTF(("vtblk: unknown i/o read offset %ld\n\r", offset));
521                 value = 0;
522                 break;
523         }
524
525         return (value);
526 }
527
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
533 };
534 PCI_EMUL_SET(pci_de_vblk);