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