Download raw body.
teaching vmd virtio 1.2
Hi Dave,
I installed this diff on my AMD host, built a release, and ran my
usual network tests with and without SEV. Works fine for me.
bluhm
On Fri, Aug 01, 2025 at 08:34:45AM -0400, Dave Voutila wrote:
> Dave Voutila <dv@sisu.io> writes:
>
> > Dave Voutila <dv@sisu.io> writes:
> >
> >> Testers wanted (who's running an archaic Linux guest?), but also looking
> >> for ok's.
> >>
> >> tl;dr: updates vmd's virtio implementation to support virtio 1.2 and
> >> upgrade vioscsi and viornd to use the new 1.x implementation. In doing
> >> so, cleans up core virtqueue handling for devices still in the virtio
> >> 0.9 dark ages.
> >>
> >> The below diff has already seen testing in a previous form back in June,
> >> but I've worked to include some critical feedback from sf@ related to
> >> the virtio 1.x spec:
> >>
> >> - adjusts queue size and masks to be dynamic for v1.x devices (caps
> >> max queue size as IOV_MAX) and adds power-of-2 checks
> >> - adds pci revision support and sets the v1.x devices to rev 1 to
> >> report they are non-transitional devices and want only the v1.x
> >> protocol
> >> - cleans up the virtqueue reset handling across all devices to use a
> >> common function
> >>
> >> These changes warrant some more tire kicking and scrutiny.
> >>
> >> Hoping to get this into the tree early next week if people have time to
> >> do a final review and testing. Next step is transitioning remaining
> >> devices (net, block, vmmci).
> >>
> >
> > Had ample time on a flight and first day of hackathon, so here's a diff
> > that includes migrating virtio block and network devices as
> > well. Only device left is/will be vmmci.
> >
> > Positive test reports on the previous diff and this just builds off of
> > that one.
> >
> > I can either carve out the vioblk and vionet stuff from this diff and
> > commit separately or all at once.
> >
> > OKs or feedback?
> >
>
> mlarkin@ found a regression in vioscsi on older Linux kernels (circa
> v4.19 used in at least Alpine Linux 3.10). Took awhile, but I tracked
> down the issue to mis-reporting the number of SCSI request queues.
>
> Older Linux kernels used this as a way to compute the number of
> virtqueues and this caused probe failures. Newer kernels either compute
> the virtqueue quantity differently or handle the mismatch betweewn the
> heuristic and the dedicated virtio pci register gracefully.
>
> The unvetted parts of this diff is in vioblk.c and vionet.c if someone
> wants to review.
>
> Test reports haven't found any regressions other than the aforementioned
> vioscsi problem.
>
>
> diffstat refs/heads/master refs/heads/vionet2
> M usr.sbin/vmd/mc146818.c | 1+ 1-
> M usr.sbin/vmd/pci.c | 55+ 9-
> M usr.sbin/vmd/pci.h | 16+ 1-
> M usr.sbin/vmd/vioblk.c | 104+ 254-
> M usr.sbin/vmd/vionet.c | 413+ 190-
> M usr.sbin/vmd/vioscsi.c | 359+ 599-
> M usr.sbin/vmd/virtio.c | 837+ 299-
> M usr.sbin/vmd/virtio.h | 132+ 91-
> M usr.sbin/vmd/vm.c | 2+ 2-
> M usr.sbin/vmd/vmd.h | 3+ 0-
>
> 10 files changed, 1922 insertions(+), 1446 deletions(-)
>
> diff refs/heads/master refs/heads/vionet2
> commit - 788294299689adc0a6c392611e2b1f3c1288bdd5
> commit + 737154316339eb628049ce1827b69f02ca36b3fa
> blob - 2c6b76c6c8387579b7c09672c1fcc34860c7593f
> blob + 50e20c4646c71a4de6eeded38d458e5814c7dd1b
> --- usr.sbin/vmd/mc146818.c
> +++ usr.sbin/vmd/mc146818.c
> @@ -127,7 +127,7 @@ rtc_fire1(int fd, short type, void *arg)
> if (rtc.now - old > 5) {
> log_debug("%s: RTC clock drift (%llds), requesting guest "
> "resync", __func__, (rtc.now - old));
> - vmmci_ctl(VMMCI_SYNCRTC);
> + vmmci_ctl(&vmmci, VMMCI_SYNCRTC);
> }
> evtimer_add(&rtc.sec, &rtc.sec_tv);
> }
> blob - ef5bbeb94c3e1345147078952303d429b8a9de52
> blob + 242397ad4aa9a9e54ec2d1dd049f1247bb858c03
> --- usr.sbin/vmd/pci.c
> +++ usr.sbin/vmd/pci.c
> @@ -22,6 +22,7 @@
> #include <dev/pci/pcidevs.h>
> #include <dev/vmm/vmm.h>
>
> +#include <stddef.h>
> #include <string.h>
> #include <unistd.h>
>
> @@ -54,7 +55,7 @@ const uint8_t pci_pic_irqs[PCI_MAX_PIC_IRQS] = {3, 5,
> * barfn: callback function invoked on BAR access
> * cookie: cookie passed to barfn on access
> *
> - * Returns 0 if the BAR was added successfully, 1 otherwise.
> + * Returns the index of the BAR if added successfully, -1 otherwise.
> */
> int
> pci_add_bar(uint8_t id, uint32_t type, void *barfn, void *cookie)
> @@ -63,18 +64,18 @@ pci_add_bar(uint8_t id, uint32_t type, void *barfn, vo
>
> /* Check id */
> if (id >= pci.pci_dev_ct)
> - return (1);
> + return (-1);
>
> /* Can only add PCI_MAX_BARS BARs to any device */
> bar_ct = pci.pci_devices[id].pd_bar_ct;
> if (bar_ct >= PCI_MAX_BARS)
> - return (1);
> + return (-1);
>
> /* Compute BAR address and add */
> bar_reg_idx = (PCI_MAPREG_START + (bar_ct * 4)) / 4;
> if (type == PCI_MAPREG_TYPE_MEM) {
> if (pci.pci_next_mmio_bar >= PCI_MMIO_BAR_END)
> - return (1);
> + return (-1);
>
> pci.pci_devices[id].pd_cfg_space[bar_reg_idx] =
> PCI_MAPREG_MEM_ADDR(pci.pci_next_mmio_bar);
> @@ -88,7 +89,7 @@ pci_add_bar(uint8_t id, uint32_t type, void *barfn, vo
> #ifdef __amd64__
> else if (type == PCI_MAPREG_TYPE_IO) {
> if (pci.pci_next_io_bar >= VM_PCI_IO_BAR_END)
> - return (1);
> + return (-1);
>
> pci.pci_devices[id].pd_cfg_space[bar_reg_idx] =
> PCI_MAPREG_IO_ADDR(pci.pci_next_io_bar) |
> @@ -104,7 +105,7 @@ pci_add_bar(uint8_t id, uint32_t type, void *barfn, vo
> }
> #endif /* __amd64__ */
>
> - return (0);
> + return ((int)bar_ct);
> }
>
> int
> @@ -156,6 +157,7 @@ pci_get_dev_irq(uint8_t id)
> * subclass: PCI 'subclass' of the new device
> * subsys_vid: subsystem VID of the new device
> * subsys_id: subsystem ID of the new device
> + * rev_id: revision id
> * irq_needed: 1 if an IRQ should be assigned to this PCI device, 0 otherwise
> * csfunc: PCI config space callback function when the guest VM accesses
> * CS of this PCI device
> @@ -167,7 +169,7 @@ pci_get_dev_irq(uint8_t id)
> int
> pci_add_device(uint8_t *id, uint16_t vid, uint16_t pid, uint8_t class,
> uint8_t subclass, uint16_t subsys_vid, uint16_t subsys_id,
> - uint8_t irq_needed, pci_cs_fn_t csfunc)
> + uint8_t rev_id, uint8_t irq_needed, pci_cs_fn_t csfunc)
> {
> /* Exceeded max devices? */
> if (pci.pci_dev_ct >= PCI_CONFIG_MAX_DEV)
> @@ -182,6 +184,7 @@ pci_add_device(uint8_t *id, uint16_t vid, uint16_t pid
>
> pci.pci_devices[*id].pd_vid = vid;
> pci.pci_devices[*id].pd_did = pid;
> + pci.pci_devices[*id].pd_rev = rev_id;
> pci.pci_devices[*id].pd_class = class;
> pci.pci_devices[*id].pd_subclass = subclass;
> pci.pci_devices[*id].pd_subsys_vid = subsys_vid;
> @@ -204,6 +207,34 @@ pci_add_device(uint8_t *id, uint16_t vid, uint16_t pid
> return (0);
> }
>
> +int
> +pci_add_capability(uint8_t id, struct pci_cap *cap)
> +{
> + uint8_t cid;
> + struct pci_dev *dev = NULL;
> +
> + if (id >= pci.pci_dev_ct)
> + return (-1);
> + dev = &pci.pci_devices[id];
> +
> + if (dev->pd_cap_ct >= PCI_MAX_CAPS)
> + return (-1);
> + cid = dev->pd_cap_ct;
> +
> + memcpy(&dev->pd_caps[cid], cap, sizeof(dev->pd_caps[0]));
> +
> + /* Update the linkage. */
> + if (cid > 0)
> + dev->pd_caps[cid - 1].pc_next = (sizeof(struct pci_cap) * cid) +
> + offsetof(struct pci_dev, pd_caps);
> +
> + dev->pd_cap_ct++;
> + dev->pd_cap = offsetof(struct pci_dev, pd_caps);
> + dev->pd_status |= (PCI_STATUS_CAPLIST_SUPPORT >> 16);
> +
> + return (cid);
> +}
> +
> /*
> * pci_init
> *
> @@ -216,15 +247,18 @@ pci_init(void)
> uint8_t id;
>
> memset(&pci, 0, sizeof(pci));
> +
> + /* Check if changes to struct pci_dev create an invalid config space. */
> + CTASSERT(sizeof(pci.pci_devices[0].pd_cfg_space) <= 256);
> +
> pci.pci_next_mmio_bar = PCI_MMIO_BAR_BASE;
> -
> #ifdef __amd64__
> pci.pci_next_io_bar = VM_PCI_IO_BAR_BASE;
> #endif /* __amd64__ */
>
> if (pci_add_device(&id, PCI_VENDOR_OPENBSD, PCI_PRODUCT_OPENBSD_PCHB,
> PCI_CLASS_BRIDGE, PCI_SUBCLASS_BRIDGE_HOST,
> - PCI_VENDOR_OPENBSD, 0, 0, NULL)) {
> + PCI_VENDOR_OPENBSD, 0, 0, 0, NULL)) {
> log_warnx("%s: can't add PCI host bridge", __progname);
> return;
> }
> @@ -442,3 +476,15 @@ pci_find_first_device(uint16_t subsys_id)
> return (i);
> return (-1);
> }
> +
> +/*
> + * Retrieve the subsystem identifier for a PCI device if found, otherwise 0.
> + */
> +uint16_t
> +pci_get_subsys_id(uint8_t pci_id)
> +{
> + if (pci_id >= pci.pci_dev_ct)
> + return (0);
> + else
> + return (pci.pci_devices[pci_id].pd_subsys_id);
> +}
> blob - bb874674aafa7d26bfd790d182f45daf4d5cb014
> blob + 1d417572421db8b189ef85b89ab6bf74305db8d2
> --- usr.sbin/vmd/pci.h
> +++ usr.sbin/vmd/pci.h
> @@ -30,6 +30,7 @@
> #define PCI_MODE1_DATA_REG 0x0cfc
> #define PCI_CONFIG_MAX_DEV 32
> #define PCI_MAX_BARS 6
> +#define PCI_MAX_CAPS 8
>
> #define PCI_BAR_TYPE_IO 0x0
> #define PCI_BAR_TYPE_MMIO 0x1
> @@ -44,6 +45,15 @@ typedef int (*pci_iobar_fn_t)(int dir, uint16_t reg, u
> void *, uint8_t);
> typedef int (*pci_mmiobar_fn_t)(int dir, uint32_t ofs, uint32_t *data);
>
> +/*
> + * Represents a PCI Capability entry with enough space for the virtio-specific
> + * capabilities.
> + */
> +struct pci_cap {
> + uint8_t pc_vndr; /* Vendor-specific ID */
> + uint8_t pc_next; /* Link to next capability */
> + uint8_t pc_extra[22]; /* Enough space for Virtio PCI data. */
> +} __packed;
>
> struct pci_dev {
> union {
> @@ -73,9 +83,12 @@ struct pci_dev {
> uint8_t pd_int;
> uint8_t pd_min_grant;
> uint8_t pd_max_grant;
> + struct pci_cap pd_caps[PCI_MAX_CAPS];
> } __packed;
> };
> +
> uint8_t pd_bar_ct;
> + uint8_t pd_cap_ct;
> pci_cs_fn_t pd_csfunc;
>
> uint8_t pd_bartype[PCI_MAX_BARS];
> @@ -97,10 +110,12 @@ struct pci {
> int pci_find_first_device(uint16_t);
> void pci_init(void);
> int pci_add_device(uint8_t *, uint16_t, uint16_t, uint8_t, uint8_t, uint16_t,
> - uint16_t, uint8_t, pci_cs_fn_t);
> + uint16_t, uint8_t, uint8_t, pci_cs_fn_t);
> +int pci_add_capability(uint8_t, struct pci_cap *);
> int pci_add_bar(uint8_t, uint32_t, void *, void *);
> int pci_set_bar_fn(uint8_t, uint8_t, void *, void *);
> uint8_t pci_get_dev_irq(uint8_t);
> +uint16_t pci_get_subsys_id(uint8_t);
>
> #ifdef __amd64__
> void pci_handle_address_reg(struct vm_run_params *);
> blob - a2da2897ae2f9a3c88609ef22c16628148bbb2b3
> blob + ebd63a04cd71c00a681c8c9175dc0af05b552b95
> --- usr.sbin/vmd/vioblk.c
> +++ usr.sbin/vmd/vioblk.c
> @@ -35,18 +35,16 @@
>
> extern char *__progname;
> extern struct vmd_vm *current_vm;
> -struct iovec io_v[VIOBLK_QUEUE_SIZE];
> +struct iovec io_v[VIRTIO_QUEUE_SIZE_MAX];
>
> static const char *disk_type(int);
> -static uint32_t handle_io_read(struct viodev_msg *, struct virtio_dev *,
> - int8_t *);
> -static int handle_io_write(struct viodev_msg *, struct virtio_dev *);
> +static uint32_t vio1_read(struct virtio_dev *, struct viodev_msg *, int *);
> +static int vio1_write(struct virtio_dev *, struct viodev_msg *);
> +static uint32_t vioblk_dev_read(struct virtio_dev *, struct viodev_msg *);
>
> -static void vioblk_update_qs(struct vioblk_dev *);
> -static void vioblk_update_qa(struct vioblk_dev *);
> -static int vioblk_notifyq(struct vioblk_dev *);
> -static ssize_t vioblk_rw(struct vioblk_dev *, int, off_t,
> - struct vring_desc *, struct vring_desc **);
> +static int vioblk_notifyq(struct virtio_dev *, uint16_t);
> +static ssize_t vioblk_rw(struct vioblk_dev *, struct virtio_vq_info *, int,
> + off_t, struct vring_desc *, struct vring_desc **);
>
> static void dev_dispatch_vm(int, short, void *);
> static void handle_sync_io(int, short, void *);
> @@ -243,43 +241,6 @@ vioblk_cmd_name(uint32_t type)
> }
> }
>
> -static void
> -vioblk_update_qa(struct vioblk_dev *dev)
> -{
> - struct virtio_vq_info *vq_info;
> - void *hva = NULL;
> -
> - /* Invalid queue? */
> - if (dev->cfg.queue_select > 0)
> - return;
> -
> - vq_info = &dev->vq[dev->cfg.queue_select];
> - vq_info->q_gpa = (uint64_t)dev->cfg.queue_pfn * VIRTIO_PAGE_SIZE;
> -
> - hva = hvaddr_mem(vq_info->q_gpa, vring_size(VIOBLK_QUEUE_SIZE));
> - if (hva == NULL)
> - fatal("vioblk_update_qa");
> - vq_info->q_hva = hva;
> -}
> -
> -static void
> -vioblk_update_qs(struct vioblk_dev *dev)
> -{
> - struct virtio_vq_info *vq_info;
> -
> - /* Invalid queue? */
> - if (dev->cfg.queue_select > 0) {
> - dev->cfg.queue_size = 0;
> - return;
> - }
> -
> - vq_info = &dev->vq[dev->cfg.queue_select];
> -
> - /* Update queue pfn/size based on queue select */
> - dev->cfg.queue_pfn = vq_info->q_gpa >> 12;
> - dev->cfg.queue_size = vq_info->qs;
> -}
> -
> /*
> * Process virtqueue notifications. If an unrecoverable error occurs, puts
> * device into a "needs reset" state.
> @@ -287,26 +248,28 @@ vioblk_update_qs(struct vioblk_dev *dev)
> * Returns 1 if an we need to assert an IRQ.
> */
> static int
> -vioblk_notifyq(struct vioblk_dev *dev)
> +vioblk_notifyq(struct virtio_dev *dev, uint16_t vq_idx)
> {
> uint32_t cmd_len;
> uint16_t idx, cmd_desc_idx;
> uint8_t ds;
> off_t offset;
> ssize_t sz;
> - int is_write, notify = 0, i;
> + int is_write, notify = 0;
> char *vr;
> + size_t i;
> struct vring_desc *table, *desc;
> struct vring_avail *avail;
> struct vring_used *used;
> struct virtio_blk_req_hdr *cmd;
> struct virtio_vq_info *vq_info;
> + struct vioblk_dev *vioblk = &dev->vioblk;
>
> /* Invalid queue? */
> - if (dev->cfg.queue_notify > 0)
> + if (vq_idx > dev->num_queues)
> return (0);
>
> - vq_info = &dev->vq[dev->cfg.queue_notify];
> + vq_info = &dev->vq[vq_idx];
> idx = vq_info->last_avail;
> vr = vq_info->q_hva;
> if (vr == NULL)
> @@ -319,7 +282,7 @@ vioblk_notifyq(struct vioblk_dev *dev)
>
> while (idx != avail->idx) {
> /* Retrieve Command descriptor. */
> - cmd_desc_idx = avail->ring[idx & VIOBLK_QUEUE_MASK];
> + cmd_desc_idx = avail->ring[idx & vq_info->mask];
> desc = &table[cmd_desc_idx];
> cmd_len = desc->len;
>
> @@ -342,7 +305,7 @@ vioblk_notifyq(struct vioblk_dev *dev)
> goto reset;
>
> /* Advance to the 2nd descriptor. */
> - desc = &table[desc->next & VIOBLK_QUEUE_MASK];
> + desc = &table[desc->next & vq_info->mask];
>
> /* Process each available command & chain. */
> switch (cmd->type) {
> @@ -351,7 +314,8 @@ vioblk_notifyq(struct vioblk_dev *dev)
> /* Read (IN) & Write (OUT) */
> is_write = (cmd->type == VIRTIO_BLK_T_OUT) ? 1 : 0;
> offset = cmd->sector * VIRTIO_BLK_SECTOR_SIZE;
> - sz = vioblk_rw(dev, is_write, offset, table, &desc);
> + sz = vioblk_rw(vioblk, vq_info, is_write, offset, table,
> + &desc);
> if (sz == -1)
> ds = VIRTIO_BLK_S_IOERR;
> else
> @@ -376,8 +340,8 @@ vioblk_notifyq(struct vioblk_dev *dev)
> /* Advance to the end of the chain, if needed. */
> i = 0;
> while (desc->flags & VRING_DESC_F_NEXT) {
> - desc = &table[desc->next & VIOBLK_QUEUE_MASK];
> - if (++i >= VIOBLK_QUEUE_SIZE) {
> + desc = &table[desc->next & vq_info->mask];
> + if (++i >= vq_info->qs) {
> /*
> * If we encounter an infinite/looping chain,
> * not much we can do but say we need a reset.
> @@ -398,11 +362,11 @@ vioblk_notifyq(struct vioblk_dev *dev)
> log_warnx("%s: can't write device status data "
> "@ 0x%llx",__func__, desc->addr);
>
> - dev->cfg.isr_status |= 1;
> + dev->isr |= 1;
> notify = 1;
>
> - used->ring[used->idx & VIOBLK_QUEUE_MASK].id = cmd_desc_idx;
> - used->ring[used->idx & VIOBLK_QUEUE_MASK].len = cmd_len;
> + used->ring[used->idx & vq_info->mask].id = cmd_desc_idx;
> + used->ring[used->idx & vq_info->mask].len = cmd_len;
>
> __sync_synchronize();
> used->idx++;
> @@ -417,8 +381,8 @@ reset:
> * When setting the "needs reset" flag, the driver is notified
> * via a configuration change interrupt.
> */
> - dev->cfg.device_status |= DEVICE_NEEDS_RESET;
> - dev->cfg.isr_status |= VIRTIO_CONFIG_ISR_CONFIG_CHANGE;
> + dev->status |= DEVICE_NEEDS_RESET;
> + dev->isr |= VIRTIO_CONFIG_ISR_CONFIG_CHANGE;
> return (1);
> }
>
> @@ -498,7 +462,7 @@ handle_sync_io(int fd, short event, void *arg)
> struct viodev_msg msg;
> struct imsg imsg;
> ssize_t n;
> - int8_t intr = INTR_STATE_NOOP;
> + int deassert = 0;
>
> if (event & EV_READ) {
> if ((n = imsgbuf_read(ibuf)) == -1)
> @@ -538,15 +502,18 @@ handle_sync_io(int fd, short event, void *arg)
> switch (msg.type) {
> case VIODEV_MSG_IO_READ:
> /* Read IO: make sure to send a reply */
> - msg.data = handle_io_read(&msg, dev, &intr);
> + msg.data = vio1_read(dev, &msg, &deassert);
> msg.data_valid = 1;
> - msg.state = intr;
> + if (deassert) {
> + /* Inline any interrupt deassertions. */
> + msg.state = INTR_STATE_DEASSERT;
> + }
> imsg_compose_event(iev, IMSG_DEVOP_MSG, 0, 0, -1, &msg,
> sizeof(msg));
> break;
> case VIODEV_MSG_IO_WRITE:
> - /* Write IO: no reply needed */
> - if (handle_io_write(&msg, dev) == 1)
> + /* Write IO: no reply needed, but maybe an irq assert */
> + if (vio1_write(dev, &msg))
> virtio_assert_irq(dev, 0);
> break;
> case VIODEV_MSG_SHUTDOWN:
> @@ -561,223 +528,106 @@ handle_sync_io(int fd, short event, void *arg)
> }
>
> static int
> -handle_io_write(struct viodev_msg *msg, struct virtio_dev *dev)
> +vio1_write(struct virtio_dev *dev, struct viodev_msg *msg)
> {
> - struct vioblk_dev *vioblk = &dev->vioblk;
> uint32_t data = msg->data;
> + uint16_t reg = msg->reg;
> + uint8_t sz = msg->io_sz;
> int intr = 0;
>
> - switch (msg->reg) {
> - case VIRTIO_CONFIG_DEVICE_FEATURES:
> - case VIRTIO_CONFIG_QUEUE_SIZE:
> - case VIRTIO_CONFIG_ISR_STATUS:
> - log_warnx("%s: illegal write %x to %s", __progname, data,
> - virtio_reg_name(msg->reg));
> + switch (reg & 0xFF00) {
> + case VIO1_CFG_BAR_OFFSET:
> + (void)virtio_io_cfg(dev, VEI_DIR_OUT, (reg & 0x00FF), data, sz);
> break;
> - case VIRTIO_CONFIG_GUEST_FEATURES:
> - vioblk->cfg.guest_feature = data;
> + case VIO1_DEV_BAR_OFFSET:
> + /* Ignore all writes to device configuration registers. */
> break;
> - case VIRTIO_CONFIG_QUEUE_PFN:
> - vioblk->cfg.queue_pfn = data;
> - vioblk_update_qa(vioblk);
> + case VIO1_NOTIFY_BAR_OFFSET:
> + intr = vioblk_notifyq(dev, (uint16_t)(msg->data));
> break;
> - case VIRTIO_CONFIG_QUEUE_SELECT:
> - vioblk->cfg.queue_select = data;
> - vioblk_update_qs(vioblk);
> + case VIO1_ISR_BAR_OFFSET:
> + /* Ignore writes to ISR. */
> break;
> - case VIRTIO_CONFIG_QUEUE_NOTIFY:
> - /* XXX We should be stricter about status checks. */
> - if (!(vioblk->cfg.device_status & DEVICE_NEEDS_RESET)) {
> - vioblk->cfg.queue_notify = data;
> - if (vioblk_notifyq(vioblk))
> - intr = 1;
> - }
> - break;
> - case VIRTIO_CONFIG_DEVICE_STATUS:
> - vioblk->cfg.device_status = data;
> - if (vioblk->cfg.device_status == 0) {
> - vioblk->cfg.guest_feature = 0;
> - vioblk->cfg.queue_pfn = 0;
> - vioblk_update_qa(vioblk);
> - vioblk->cfg.queue_size = 0;
> - vioblk_update_qs(vioblk);
> - vioblk->cfg.queue_select = 0;
> - vioblk->cfg.queue_notify = 0;
> - vioblk->cfg.isr_status = 0;
> - vioblk->vq[0].last_avail = 0;
> - vioblk->vq[0].notified_avail = 0;
> - virtio_deassert_irq(dev, msg->vcpu);
> - }
> - break;
> default:
> - break;
> + log_debug("%s: no handler for reg 0x%04x", __func__, reg);
> }
> +
> return (intr);
> }
>
> static uint32_t
> -handle_io_read(struct viodev_msg *msg, struct virtio_dev *dev, int8_t *intr)
> +vio1_read(struct virtio_dev *dev, struct viodev_msg *msg, int *deassert)
> {
> - struct vioblk_dev *vioblk = &dev->vioblk;
> + uint32_t data = (uint32_t)(-1);
> + uint16_t reg = msg->reg;
> uint8_t sz = msg->io_sz;
> - uint32_t data;
>
> - if (msg->data_valid)
> - data = msg->data;
> - else
> - data = 0;
> -
> - switch (msg->reg) {
> - case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI:
> - switch (sz) {
> - case 4:
> - data = (uint32_t)(vioblk->capacity);
> - break;
> - case 2:
> - data &= 0xFFFF0000;
> - data |= (uint32_t)(vioblk->capacity) & 0xFFFF;
> - break;
> - case 1:
> - data &= 0xFFFFFF00;
> - data |= (uint32_t)(vioblk->capacity) & 0xFF;
> - break;
> - }
> - /* XXX handle invalid sz */
> + switch (reg & 0xFF00) {
> + case VIO1_CFG_BAR_OFFSET:
> + data = virtio_io_cfg(dev, VEI_DIR_IN, (uint8_t)reg, 0, sz);
> break;
> - case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 1:
> - if (sz == 1) {
> - data &= 0xFFFFFF00;
> - data |= (uint32_t)(vioblk->capacity >> 8) & 0xFF;
> - }
> - /* XXX handle invalid sz */
> + case VIO1_DEV_BAR_OFFSET:
> + data = vioblk_dev_read(dev, msg);
> break;
> - case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 2:
> - if (sz == 1) {
> - data &= 0xFFFFFF00;
> - data |= (uint32_t)(vioblk->capacity >> 16) & 0xFF;
> - } else if (sz == 2) {
> - data &= 0xFFFF0000;
> - data |= (uint32_t)(vioblk->capacity >> 16) & 0xFFFF;
> - }
> - /* XXX handle invalid sz */
> + case VIO1_NOTIFY_BAR_OFFSET:
> + /* Reads of notify register return all 1's. */
> break;
> - case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 3:
> - if (sz == 1) {
> - data &= 0xFFFFFF00;
> - data |= (uint32_t)(vioblk->capacity >> 24) & 0xFF;
> - }
> - /* XXX handle invalid sz */
> + case VIO1_ISR_BAR_OFFSET:
> + data = dev->isr;
> + dev->isr = 0;
> + *deassert = 1;
> break;
> - case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 4:
> - switch (sz) {
> - case 4:
> - data = (uint32_t)(vioblk->capacity >> 32);
> + default:
> + log_debug("%s: no handler for reg 0x%04x", __func__, reg);
> + }
> +
> + return (data);
> +}
> +
> +static uint32_t
> +vioblk_dev_read(struct virtio_dev *dev, struct viodev_msg *msg)
> +{
> + struct vioblk_dev *vioblk = (struct vioblk_dev *)&dev->vioblk;
> + uint32_t data = (uint32_t)(-1);
> + uint16_t reg = msg->reg;
> + uint8_t sz = msg->io_sz;
> +
> + switch (reg & 0xFF) {
> + case VIRTIO_BLK_CONFIG_CAPACITY:
> + if (sz != 4) {
> + log_warnx("%s: unaligned read from capacity register",
> + __func__);
> break;
> - case 2:
> - data &= 0xFFFF0000;
> - data |= (uint32_t)(vioblk->capacity >> 32) & 0xFFFF;
> - break;
> - case 1:
> - data &= 0xFFFFFF00;
> - data |= (uint32_t)(vioblk->capacity >> 32) & 0xFF;
> - break;
> }
> - /* XXX handle invalid sz */
> + data = (uint32_t)(0xFFFFFFFF & vioblk->capacity);
> break;
> - case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 5:
> - if (sz == 1) {
> - data &= 0xFFFFFF00;
> - data |= (uint32_t)(vioblk->capacity >> 40) & 0xFF;
> + case VIRTIO_BLK_CONFIG_CAPACITY + 4:
> + if (sz != 4) {
> + log_warnx("%s: unaligned read from capacity register",
> + __func__);
> + break;
> }
> - /* XXX handle invalid sz */
> + data = (uint32_t)(vioblk->capacity >> 32);
> break;
> - case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 6:
> - if (sz == 1) {
> - data &= 0xFFFFFF00;
> - data |= (uint32_t)(vioblk->capacity >> 48) & 0xFF;
> - } else if (sz == 2) {
> - data &= 0xFFFF0000;
> - data |= (uint32_t)(vioblk->capacity >> 48) & 0xFFFF;
> - }
> - /* XXX handle invalid sz */
> - break;
> - case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 7:
> - if (sz == 1) {
> - data &= 0xFFFFFF00;
> - data |= (uint32_t)(vioblk->capacity >> 56) & 0xFF;
> - }
> - /* XXX handle invalid sz */
> - break;
> - case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 12:
> - switch (sz) {
> - case 4:
> - data = (uint32_t)(vioblk->seg_max);
> + case VIRTIO_BLK_CONFIG_SEG_MAX:
> + if (sz != 4) {
> + log_warnx("%s: unaligned read from segment max "
> + "register", __func__);
> break;
> - case 2:
> - data &= 0xFFFF0000;
> - data |= (uint32_t)(vioblk->seg_max) & 0xFFFF;
> - break;
> - case 1:
> - data &= 0xFFFFFF00;
> - data |= (uint32_t)(vioblk->seg_max) & 0xFF;
> - break;
> }
> - /* XXX handle invalid sz */
> + data = vioblk->seg_max;
> break;
> - case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 13:
> - if (sz == 1) {
> - data &= 0xFFFFFF00;
> - data |= (uint32_t)(vioblk->seg_max >> 8) & 0xFF;
> - }
> - /* XXX handle invalid sz */
> + case VIRTIO_BLK_CONFIG_GEOMETRY_C:
> + case VIRTIO_BLK_CONFIG_GEOMETRY_H:
> + case VIRTIO_BLK_CONFIG_GEOMETRY_S:
> + /*
> + * SeaBIOS unconditionally reads without checking the
> + * geometry feature flag.
> + */
> break;
> - case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 14:
> - if (sz == 1) {
> - data &= 0xFFFFFF00;
> - data |= (uint32_t)(vioblk->seg_max >> 16) & 0xFF;
> - } else if (sz == 2) {
> - data &= 0xFFFF0000;
> - data |= (uint32_t)(vioblk->seg_max >> 16)
> - & 0xFFFF;
> - }
> - /* XXX handle invalid sz */
> - break;
> - case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 15:
> - if (sz == 1) {
> - data &= 0xFFFFFF00;
> - data |= (uint32_t)(vioblk->seg_max >> 24) & 0xFF;
> - }
> - /* XXX handle invalid sz */
> - break;
> - case VIRTIO_CONFIG_DEVICE_FEATURES:
> - data = vioblk->cfg.device_feature;
> - break;
> - case VIRTIO_CONFIG_GUEST_FEATURES:
> - data = vioblk->cfg.guest_feature;
> - break;
> - case VIRTIO_CONFIG_QUEUE_PFN:
> - data = vioblk->cfg.queue_pfn;
> - break;
> - case VIRTIO_CONFIG_QUEUE_SIZE:
> - data = vioblk->cfg.queue_size;
> - break;
> - case VIRTIO_CONFIG_QUEUE_SELECT:
> - data = vioblk->cfg.queue_select;
> - break;
> - case VIRTIO_CONFIG_QUEUE_NOTIFY:
> - data = vioblk->cfg.queue_notify;
> - break;
> - case VIRTIO_CONFIG_DEVICE_STATUS:
> - data = vioblk->cfg.device_status;
> - break;
> - case VIRTIO_CONFIG_ISR_STATUS:
> - data = vioblk->cfg.isr_status;
> - vioblk->cfg.isr_status = 0;
> - if (intr != NULL)
> - *intr = INTR_STATE_DEASSERT;
> - break;
> default:
> - return (0xFFFFFFFF);
> + log_warnx("%s: invalid register 0x%04x", __func__, reg);
> + return (uint32_t)(-1);
> }
>
> return (data);
> @@ -791,8 +641,8 @@ handle_io_read(struct viodev_msg *msg, struct virtio_d
> * On error, returns -1 and descriptor (desc) remains at its current position.
> */
> static ssize_t
> -vioblk_rw(struct vioblk_dev *dev, int is_write, off_t offset,
> - struct vring_desc *desc_tbl, struct vring_desc **desc)
> +vioblk_rw(struct vioblk_dev *dev, struct virtio_vq_info *vq_info, int is_write,
> + off_t offset, struct vring_desc *desc_tbl, struct vring_desc **desc)
> {
> struct iovec *iov = NULL;
> ssize_t sz = 0;
> @@ -830,7 +680,7 @@ vioblk_rw(struct vioblk_dev *dev, int is_write, off_t
> }
>
> /* Advance to the next descriptor. */
> - *desc = &desc_tbl[(*desc)->next & VIOBLK_QUEUE_MASK];
> + *desc = &desc_tbl[(*desc)->next & vq_info->mask];
> } while ((*desc)->flags & VRING_DESC_F_NEXT);
>
> /*
> blob - b55efb57613e670d9160a3d063b77ab2d4c384c0
> blob + 8c6406fc030a66f82ce5478e0069b28c75b6db88
> --- usr.sbin/vmd/vionet.c
> +++ usr.sbin/vmd/vionet.c
> @@ -38,6 +38,18 @@
> #include "virtio.h"
> #include "vmd.h"
>
> +#define VIONET_DEBUG 1
> +#ifdef DPRINTF
> +#undef DPRINTF
> +#endif
> +#if VIONET_DEBUG
> +#define DPRINTF log_debug
> +#else
> +#define DPRINTF(x...) do {} while(0)
> +#endif /* VIONET_DEBUG */
> +
> +#define VIRTIO_NET_CONFIG_MAC 0 /* 8 bit x 6 byte */
> +
> #define VIRTIO_NET_F_MAC (1 << 5)
> #define RXQ 0
> #define TXQ 1
> @@ -52,17 +64,20 @@ struct packet {
>
> static void *rx_run_loop(void *);
> static void *tx_run_loop(void *);
> -static int vionet_rx(struct vionet_dev *, int);
> +static int vionet_rx(struct virtio_dev *, int);
> static ssize_t vionet_rx_copy(struct vionet_dev *, int, const struct iovec *,
> int, size_t);
> static ssize_t vionet_rx_zerocopy(struct vionet_dev *, int,
> const struct iovec *, int);
> static void vionet_rx_event(int, short, void *);
> -static uint32_t handle_io_read(struct viodev_msg *, struct virtio_dev *,
> - int8_t *);
> -static void handle_io_write(struct viodev_msg *, struct virtio_dev *);
> +static uint32_t vio1_read(struct virtio_dev *, struct viodev_msg *, int *);
> +static void vio1_write(struct virtio_dev *, struct viodev_msg *);
> +static uint32_t vionet_cfg_read(struct virtio_dev *, struct viodev_msg *);
> +static void vionet_cfg_write(struct virtio_dev *, struct viodev_msg *);
> +
> static int vionet_tx(struct virtio_dev *);
> -static void vionet_notifyq(struct virtio_dev *);
> +static void vionet_notifyq(struct virtio_dev *, uint16_t);
> +static uint32_t vionet_dev_read(struct virtio_dev *, struct viodev_msg *);
> static void dev_dispatch_vm(int, short, void *);
> static void handle_sync_io(int, short, void *);
> static void read_pipe_main(int, short, void *);
> @@ -85,8 +100,8 @@ struct vm_dev_pipe pipe_tx;
> int pipe_inject[2];
> #define READ 0
> #define WRITE 1
> -struct iovec iov_rx[VIONET_QUEUE_SIZE];
> -struct iovec iov_tx[VIONET_QUEUE_SIZE];
> +struct iovec iov_rx[VIRTIO_QUEUE_SIZE_MAX];
> +struct iovec iov_tx[VIRTIO_QUEUE_SIZE_MAX];
> pthread_rwlock_t lock = NULL; /* Guards device config state. */
> int resetting = 0; /* Transient reset state used to coordinate reset. */
> int rx_enabled = 0; /* 1: we expect to read the tap, 0: wait for notify. */
> @@ -287,56 +302,6 @@ fail:
> }
>
> /*
> - * Update the gpa and hva of the virtqueue.
> - */
> -static void
> -vionet_update_qa(struct vionet_dev *dev)
> -{
> - struct virtio_vq_info *vq_info;
> - void *hva = NULL;
> -
> - /* Invalid queue? */
> - if (dev->cfg.queue_select > 1)
> - return;
> -
> - vq_info = &dev->vq[dev->cfg.queue_select];
> - vq_info->q_gpa = (uint64_t)dev->cfg.queue_pfn * VIRTIO_PAGE_SIZE;
> - dev->cfg.queue_pfn = vq_info->q_gpa >> 12;
> -
> - if (vq_info->q_gpa == 0)
> - vq_info->q_hva = NULL;
> -
> - hva = hvaddr_mem(vq_info->q_gpa, vring_size(VIONET_QUEUE_SIZE));
> - if (hva == NULL)
> - fatalx("%s: hva == NULL", __func__);
> -
> - vq_info->q_hva = hva;
> -}
> -
> -/*
> - * Update the queue size.
> - */
> -static void
> -vionet_update_qs(struct vionet_dev *dev)
> -{
> - struct virtio_vq_info *vq_info;
> -
> - /* Invalid queue? */
> - if (dev->cfg.queue_select > 1) {
> - log_warnx("%s: !!! invalid queue selector %d", __func__,
> - dev->cfg.queue_select);
> - dev->cfg.queue_size = 0;
> - return;
> - }
> -
> - vq_info = &dev->vq[dev->cfg.queue_select];
> -
> - /* Update queue pfn/size based on queue select */
> - dev->cfg.queue_pfn = vq_info->q_gpa >> 12;
> - dev->cfg.queue_size = vq_info->qs;
> -}
> -
> -/*
> * vionet_rx
> *
> * Pull packet from the provided fd and fill the receive-side virtqueue. We
> @@ -346,21 +311,23 @@ vionet_update_qs(struct vionet_dev *dev)
> * or 0 if no notification is needed.
> */
> static int
> -vionet_rx(struct vionet_dev *dev, int fd)
> +vionet_rx(struct virtio_dev *dev, int fd)
> {
> uint16_t idx, hdr_idx;
> char *vr = NULL;
> size_t chain_len = 0, iov_cnt;
> + struct vionet_dev *vionet = &dev->vionet;
> struct vring_desc *desc, *table;
> struct vring_avail *avail;
> struct vring_used *used;
> + struct virtio_net_hdr *hdr = NULL;
> struct virtio_vq_info *vq_info;
> struct iovec *iov;
> int notify = 0;
> ssize_t sz;
> uint8_t status = 0;
>
> - status = dev->cfg.device_status & VIRTIO_CONFIG_DEVICE_STATUS_DRIVER_OK;
> + status = dev->status & VIRTIO_CONFIG_DEVICE_STATUS_DRIVER_OK;
> if (status != VIRTIO_CONFIG_DEVICE_STATUS_DRIVER_OK) {
> log_warnx("%s: driver not ready", __func__);
> return (0);
> @@ -379,8 +346,8 @@ vionet_rx(struct vionet_dev *dev, int fd)
> used->flags |= VRING_USED_F_NO_NOTIFY;
>
> while (idx != avail->idx) {
> - hdr_idx = avail->ring[idx & VIONET_QUEUE_MASK];
> - desc = &table[hdr_idx & VIONET_QUEUE_MASK];
> + hdr_idx = avail->ring[idx & vq_info->mask];
> + desc = &table[hdr_idx & vq_info->mask];
> if (!DESC_WRITABLE(desc)) {
> log_warnx("%s: invalid descriptor state", __func__);
> goto reset;
> @@ -407,7 +374,8 @@ vionet_rx(struct vionet_dev *dev, int fd)
> iov->iov_base = hvaddr_mem(desc->addr, iov->iov_len);
> if (iov->iov_base == NULL)
> goto reset;
> - memset(iov->iov_base, 0, sizeof(struct virtio_net_hdr));
> + hdr = iov->iov_base;
> + memset(hdr, 0, sizeof(struct virtio_net_hdr));
>
> /* Tweak the iovec to account for the virtio_net_hdr. */
> iov->iov_len -= sizeof(struct virtio_net_hdr);
> @@ -422,7 +390,7 @@ vionet_rx(struct vionet_dev *dev, int fd)
> * and lengths.
> */
> while (desc->flags & VRING_DESC_F_NEXT) {
> - desc = &table[desc->next & VIONET_QUEUE_MASK];
> + desc = &table[desc->next & vq_info->mask];
> if (!DESC_WRITABLE(desc)) {
> log_warnx("%s: invalid descriptor state",
> __func__);
> @@ -452,15 +420,17 @@ vionet_rx(struct vionet_dev *dev, int fd)
> goto reset;
> }
>
> + hdr->num_buffers = iov_cnt;
> +
> /*
> * If we're enforcing hardware address or handling an injected
> * packet, we need to use a copy-based approach.
> */
> - if (dev->lockedmac || fd != dev->data_fd)
> - sz = vionet_rx_copy(dev, fd, iov_rx, iov_cnt,
> + if (vionet->lockedmac || fd != vionet->data_fd)
> + sz = vionet_rx_copy(vionet, fd, iov_rx, iov_cnt,
> chain_len);
> else
> - sz = vionet_rx_zerocopy(dev, fd, iov_rx, iov_cnt);
> + sz = vionet_rx_zerocopy(vionet, fd, iov_rx, iov_cnt);
> if (sz == -1)
> goto reset;
> if (sz == 0) /* No packets, so bail out for now. */
> @@ -473,8 +443,8 @@ vionet_rx(struct vionet_dev *dev, int fd)
> sz += sizeof(struct virtio_net_hdr);
>
> /* Mark our buffers as used. */
> - used->ring[used->idx & VIONET_QUEUE_MASK].id = hdr_idx;
> - used->ring[used->idx & VIONET_QUEUE_MASK].len = sz;
> + used->ring[used->idx & vq_info->mask].id = hdr_idx;
> + used->ring[used->idx & vq_info->mask].len = sz;
> __sync_synchronize();
> used->idx++;
> idx++;
> @@ -630,14 +600,13 @@ static void
> vionet_rx_event(int fd, short event, void *arg)
> {
> struct virtio_dev *dev = (struct virtio_dev *)arg;
> - struct vionet_dev *vionet = &dev->vionet;
> int ret = 0;
>
> if (!(event & EV_READ))
> fatalx("%s: invalid event type", __func__);
>
> pthread_rwlock_rdlock(&lock);
> - ret = vionet_rx(vionet, fd);
> + ret = vionet_rx(dev, fd);
> pthread_rwlock_unlock(&lock);
>
> if (ret == 0) {
> @@ -648,12 +617,12 @@ vionet_rx_event(int fd, short event, void *arg)
> pthread_rwlock_wrlock(&lock);
> if (ret == 1) {
> /* Notify the driver. */
> - vionet->cfg.isr_status |= 1;
> + dev->isr |= 1;
> } else {
> /* Need a reset. Something went wrong. */
> log_warnx("%s: requesting device reset", __func__);
> - vionet->cfg.device_status |= DEVICE_NEEDS_RESET;
> - vionet->cfg.isr_status |= VIRTIO_CONFIG_ISR_CONFIG_CHANGE;
> + dev->status |= DEVICE_NEEDS_RESET;
> + dev->isr |= VIRTIO_CONFIG_ISR_CONFIG_CHANGE;
> }
> pthread_rwlock_unlock(&lock);
>
> @@ -661,11 +630,9 @@ vionet_rx_event(int fd, short event, void *arg)
> }
>
> static void
> -vionet_notifyq(struct virtio_dev *dev)
> +vionet_notifyq(struct virtio_dev *dev, uint16_t vq_idx)
> {
> - struct vionet_dev *vionet = &dev->vionet;
> -
> - switch (vionet->cfg.queue_notify) {
> + switch (vq_idx) {
> case RXQ:
> rx_enabled = 1;
> vm_pipe_send(&pipe_rx, VIRTIO_NOTIFY);
> @@ -679,7 +646,7 @@ vionet_notifyq(struct virtio_dev *dev)
> * well as any bogus queue IDs.
> */
> log_debug("%s: notify for unimplemented queue ID %d",
> - __func__, vionet->cfg.queue_notify);
> + __func__, dev->cfg.queue_notify);
> break;
> }
> }
> @@ -702,14 +669,13 @@ vionet_tx(struct virtio_dev *dev)
> struct packet pkt;
> uint8_t status = 0;
>
> - status = vionet->cfg.device_status
> - & VIRTIO_CONFIG_DEVICE_STATUS_DRIVER_OK;
> + status = dev->status & VIRTIO_CONFIG_DEVICE_STATUS_DRIVER_OK;
> if (status != VIRTIO_CONFIG_DEVICE_STATUS_DRIVER_OK) {
> log_warnx("%s: driver not ready", __func__);
> return (0);
> }
>
> - vq_info = &vionet->vq[TXQ];
> + vq_info = &dev->vq[TXQ];
> idx = vq_info->last_avail;
> vr = vq_info->q_hva;
> if (vr == NULL)
> @@ -721,8 +687,8 @@ vionet_tx(struct virtio_dev *dev)
> used = (struct vring_used *)(vr + vq_info->vq_usedoffset);
>
> while (idx != avail->idx) {
> - hdr_idx = avail->ring[idx & VIONET_QUEUE_MASK];
> - desc = &table[hdr_idx & VIONET_QUEUE_MASK];
> + hdr_idx = avail->ring[idx & vq_info->mask];
> + desc = &table[hdr_idx & vq_info->mask];
> if (DESC_WRITABLE(desc)) {
> log_warnx("%s: invalid descriptor state", __func__);
> goto reset;
> @@ -733,22 +699,24 @@ vionet_tx(struct virtio_dev *dev)
> chain_len = 0;
>
> /*
> - * As a legacy device, we most likely will receive a lead
> - * descriptor sized to the virtio_net_hdr. However, the framing
> - * is not guaranteed, so check for packet data.
> + * We do not negotiate VIRTIO_NET_F_HASH_REPORT so we
> + * assume the header length is fixed.
> */
> - iov->iov_len = desc->len;
> - if (iov->iov_len < sizeof(struct virtio_net_hdr)) {
> + if (desc->len < sizeof(struct virtio_net_hdr)) {
> log_warnx("%s: invalid descriptor length", __func__);
> goto reset;
> - } else if (iov->iov_len > sizeof(struct virtio_net_hdr)) {
> + }
> + iov->iov_len = desc->len;
> +
> + if (iov->iov_len > sizeof(struct virtio_net_hdr)) {
> /* Chop off the virtio header, leaving packet data. */
> iov->iov_len -= sizeof(struct virtio_net_hdr);
> - chain_len += iov->iov_len;
> iov->iov_base = hvaddr_mem(desc->addr +
> sizeof(struct virtio_net_hdr), iov->iov_len);
> if (iov->iov_base == NULL)
> goto reset;
> +
> + chain_len += iov->iov_len;
> iov_cnt++;
> }
>
> @@ -756,7 +724,7 @@ vionet_tx(struct virtio_dev *dev)
> * Walk the chain and collect remaining addresses and lengths.
> */
> while (desc->flags & VRING_DESC_F_NEXT) {
> - desc = &table[desc->next & VIONET_QUEUE_MASK];
> + desc = &table[desc->next & vq_info->mask];
> if (DESC_WRITABLE(desc)) {
> log_warnx("%s: invalid descriptor state",
> __func__);
> @@ -826,8 +794,8 @@ vionet_tx(struct virtio_dev *dev)
> }
> chain_len += sizeof(struct virtio_net_hdr);
> drop:
> - used->ring[used->idx & VIONET_QUEUE_MASK].id = hdr_idx;
> - used->ring[used->idx & VIONET_QUEUE_MASK].len = chain_len;
> + used->ring[used->idx & vq_info->mask].id = hdr_idx;
> + used->ring[used->idx & vq_info->mask].len = chain_len;
> __sync_synchronize();
> used->idx++;
> idx++;
> @@ -848,8 +816,6 @@ drop:
> __func__);
> free(pkt.buf);
> }
> - log_debug("%s: injected dhcp reply with %ld bytes",
> - __func__, sz);
> }
> }
>
> @@ -857,7 +823,6 @@ drop:
> !(avail->flags & VRING_AVAIL_F_NO_INTERRUPT))
> notify = 1;
>
> -
> vq_info->last_avail = idx;
> return (notify);
> reset:
> @@ -949,7 +914,7 @@ handle_sync_io(int fd, short event, void *arg)
> struct viodev_msg msg;
> struct imsg imsg;
> ssize_t n;
> - int8_t intr = INTR_STATE_NOOP;
> + int deassert = 0;
>
> if (event & EV_READ) {
> if ((n = imsgbuf_read(ibuf)) == -1)
> @@ -989,15 +954,16 @@ handle_sync_io(int fd, short event, void *arg)
> switch (msg.type) {
> case VIODEV_MSG_IO_READ:
> /* Read IO: make sure to send a reply */
> - msg.data = handle_io_read(&msg, dev, &intr);
> + msg.data = vio1_read(dev, &msg, &deassert);
> msg.data_valid = 1;
> - msg.state = intr;
> + if (deassert)
> + msg.state = INTR_STATE_DEASSERT;
> imsg_compose_event2(iev, IMSG_DEVOP_MSG, 0, 0, -1, &msg,
> sizeof(msg), ev_base_main);
> break;
> case VIODEV_MSG_IO_WRITE:
> /* Write IO: no reply needed */
> - handle_io_write(&msg, dev);
> + vio1_write(dev, &msg);
> break;
> case VIODEV_MSG_SHUTDOWN:
> event_del(&dev->sync_iev.ev);
> @@ -1010,49 +976,291 @@ handle_sync_io(int fd, short event, void *arg)
> imsg_event_add2(iev, ev_base_main);
> }
>
> +static uint32_t
> +vionet_cfg_read(struct virtio_dev *dev, struct viodev_msg *msg)
> +{
> + struct virtio_pci_common_cfg *pci_cfg = &dev->pci_cfg;
> + uint32_t data = (uint32_t)(-1);
> + uint16_t reg = msg->reg & 0x00FF;
> +
> + pthread_rwlock_rdlock(&lock);
> + switch (reg) {
> + case VIO1_PCI_DEVICE_FEATURE_SELECT:
> + data = pci_cfg->device_feature_select;
> + break;
> + case VIO1_PCI_DEVICE_FEATURE:
> + if (pci_cfg->device_feature_select == 0)
> + data = dev->device_feature & (uint32_t)(-1);
> + else if (pci_cfg->device_feature_select == 1)
> + data = dev->device_feature >> 32;
> + else {
> + DPRINTF("%s: ignoring device feature read",
> + __func__);
> + }
> + break;
> + case VIO1_PCI_DRIVER_FEATURE_SELECT:
> + data = pci_cfg->driver_feature_select;
> + break;
> + case VIO1_PCI_DRIVER_FEATURE:
> + if (pci_cfg->driver_feature_select == 0)
> + data = dev->driver_feature & (uint32_t)(-1);
> + else if (pci_cfg->driver_feature_select == 1)
> + data = dev->driver_feature >> 32;
> + else {
> + DPRINTF("%s: ignoring driver feature read",
> + __func__);
> + }
> + break;
> + case VIO1_PCI_CONFIG_MSIX_VECTOR:
> + data = VIRTIO_MSI_NO_VECTOR; /* Unsupported */
> + break;
> + case VIO1_PCI_NUM_QUEUES:
> + data = dev->num_queues;
> + break;
> + case VIO1_PCI_DEVICE_STATUS:
> + data = dev->status;
> + break;
> + case VIO1_PCI_CONFIG_GENERATION:
> + data = pci_cfg->config_generation;
> + break;
> + case VIO1_PCI_QUEUE_SELECT:
> + data = pci_cfg->queue_select;
> + break;
> + case VIO1_PCI_QUEUE_SIZE:
> + data = pci_cfg->queue_size;
> + break;
> + case VIO1_PCI_QUEUE_MSIX_VECTOR:
> + data = VIRTIO_MSI_NO_VECTOR; /* Unsupported */
> + break;
> + case VIO1_PCI_QUEUE_ENABLE:
> + data = pci_cfg->queue_enable;
> + break;
> + case VIO1_PCI_QUEUE_NOTIFY_OFF:
> + data = pci_cfg->queue_notify_off;
> + break;
> + case VIO1_PCI_QUEUE_DESC:
> + data = (uint32_t)(0xFFFFFFFF & pci_cfg->queue_desc);
> + break;
> + case VIO1_PCI_QUEUE_DESC + 4:
> + data = (uint32_t)(pci_cfg->queue_desc >> 32);
> + break;
> + case VIO1_PCI_QUEUE_AVAIL:
> + data = (uint32_t)(0xFFFFFFFF & pci_cfg->queue_avail);
> + break;
> + case VIO1_PCI_QUEUE_AVAIL + 4:
> + data = (uint32_t)(pci_cfg->queue_avail >> 32);
> + break;
> + case VIO1_PCI_QUEUE_USED:
> + data = (uint32_t)(0xFFFFFFFF & pci_cfg->queue_used);
> + break;
> + case VIO1_PCI_QUEUE_USED + 4:
> + data = (uint32_t)(pci_cfg->queue_used >> 32);
> + break;
> + default:
> + log_warnx("%s: invalid register 0x%04x", __func__, reg);
> + }
> + pthread_rwlock_unlock(&lock);
> +
> + return (data);
> +}
> +
> static void
> -handle_io_write(struct viodev_msg *msg, struct virtio_dev *dev)
> +vionet_cfg_write(struct virtio_dev *dev, struct viodev_msg *msg)
> {
> - struct vionet_dev *vionet = &dev->vionet;
> - uint32_t data = msg->data;
> - int pause_devices = 0;
> + struct virtio_pci_common_cfg *pci_cfg = &dev->pci_cfg;
> + uint32_t data = msg->data;
> + uint16_t reg = msg->reg & 0xFF;
> + uint8_t sz = msg->io_sz;
> + int i, pause_devices = 0;
>
> + DPRINTF("%s: write reg=%d data=0x%x", __func__, msg->reg, data);
> +
> pthread_rwlock_wrlock(&lock);
> -
> - switch (msg->reg) {
> - case VIRTIO_CONFIG_DEVICE_FEATURES:
> - case VIRTIO_CONFIG_QUEUE_SIZE:
> - case VIRTIO_CONFIG_ISR_STATUS:
> - log_warnx("%s: illegal write %x to %s", __progname, data,
> - virtio_reg_name(msg->reg));
> + switch (reg) {
> + case VIO1_PCI_DEVICE_FEATURE_SELECT:
> + if (sz != 4)
> + log_warnx("%s: unaligned write to device "
> + "feature select (sz=%u)", __func__, sz);
> + else
> + pci_cfg->device_feature_select = data;
> break;
> - case VIRTIO_CONFIG_GUEST_FEATURES:
> - vionet->cfg.guest_feature = data;
> + case VIO1_PCI_DEVICE_FEATURE:
> + log_warnx("%s: illegal write to device feature "
> + "register", __progname);
> break;
> - case VIRTIO_CONFIG_QUEUE_PFN:
> - vionet->cfg.queue_pfn = data;
> - vionet_update_qa(vionet);
> + case VIO1_PCI_DRIVER_FEATURE_SELECT:
> + if (sz != 4)
> + log_warnx("%s: unaligned write to driver "
> + "feature select register (sz=%u)", __func__,
> + sz);
> + else
> + pci_cfg->driver_feature_select = data;
> break;
> - case VIRTIO_CONFIG_QUEUE_SELECT:
> - vionet->cfg.queue_select = data;
> - vionet_update_qs(vionet);
> + case VIO1_PCI_DRIVER_FEATURE:
> + if (sz != 4) {
> + log_warnx("%s: unaligned write to driver "
> + "feature register (sz=%u)", __func__, sz);
> + break;
> + }
> + if (pci_cfg->driver_feature_select > 1) {
> + /* We only support a 64-bit feature space. */
> + DPRINTF("%s: ignoring driver feature write",
> + __func__);
> + break;
> + }
> + pci_cfg->driver_feature = data;
> + if (pci_cfg->driver_feature_select == 0)
> + dev->driver_feature |= pci_cfg->driver_feature;
> + else
> + dev->driver_feature |=
> + ((uint64_t)pci_cfg->driver_feature << 32);
> + dev->driver_feature &= dev->device_feature;
> + DPRINTF("%s: driver features 0x%llx", __func__,
> + dev->driver_feature);
> break;
> - case VIRTIO_CONFIG_QUEUE_NOTIFY:
> - vionet->cfg.queue_notify = data;
> - vionet_notifyq(dev);
> + case VIO1_PCI_CONFIG_MSIX_VECTOR:
> + /* Ignore until we support MSIX. */
> break;
> - case VIRTIO_CONFIG_DEVICE_STATUS:
> - if (data == 0) {
> - resetting = 2; /* Wait on two acks: rx & tx */
> + case VIO1_PCI_NUM_QUEUES:
> + log_warnx("%s: illegal write to num queues register",
> + __progname);
> + break;
> + case VIO1_PCI_DEVICE_STATUS:
> + if (sz != 1) {
> + log_warnx("%s: unaligned write to device "
> + "status register (sz=%u)", __func__, sz);
> + break;
> + }
> + dev->status = data;
> + if (dev->status == 0) {
> + /* Reset device and virtqueues (if any). */
> + dev->driver_feature = 0;
> + dev->isr = 0;
> +
> + pci_cfg->queue_select = 0;
> + virtio_update_qs(dev);
> +
> + if (dev->num_queues > 0) {
> + /*
> + * Reset virtqueues to initial state and
> + * set to disabled status. Clear PCI
> + * configuration registers.
> + */
> + for (i = 0; i < dev->num_queues; i++)
> + virtio_vq_init(dev, i);
> + }
> +
> + resetting = 2; /* Wait on two acks: rx & tx */
> pause_devices = 1;
> - } else {
> - // XXX is this correct?
> - vionet->cfg.device_status = data;
> }
> + DPRINTF("%s: dev %u status [%s%s%s%s%s%s]", __func__,
> + dev->pci_id,
> + (data & VIRTIO_CONFIG_DEVICE_STATUS_ACK) ?
> + "[ack]" : "",
> + (data & VIRTIO_CONFIG_DEVICE_STATUS_DRIVER) ?
> + "[driver]" : "",
> + (data & VIRTIO_CONFIG_DEVICE_STATUS_DRIVER_OK) ?
> + "[driver ok]" : "",
> + (data & VIRTIO_CONFIG_DEVICE_STATUS_FEATURES_OK) ?
> + "[features ok]" : "",
> + (data & VIRTIO_CONFIG_DEVICE_STATUS_DEVICE_NEEDS_RESET)
> + ? "[needs reset]" : "",
> + (data & VIRTIO_CONFIG_DEVICE_STATUS_FAILED) ?
> + "[failed]" : "");
> break;
> + case VIO1_PCI_CONFIG_GENERATION:
> + log_warnx("%s: illegal write to config generation "
> + "register", __progname);
> + break;
> + case VIO1_PCI_QUEUE_SELECT:
> + pci_cfg->queue_select = data;
> + virtio_update_qs(dev);
> + break;
> + case VIO1_PCI_QUEUE_SIZE:
> + if (data <= VIRTIO_QUEUE_SIZE_MAX)
> + pci_cfg->queue_size = data;
> + else {
> + log_warnx("%s: clamping queue size", __func__);
> + pci_cfg->queue_size = VIRTIO_QUEUE_SIZE_MAX;
> + }
> + virtio_update_qa(dev);
> + break;
> + case VIO1_PCI_QUEUE_MSIX_VECTOR:
> + /* Ignore until we support MSI-X. */
> + break;
> + case VIO1_PCI_QUEUE_ENABLE:
> + pci_cfg->queue_enable = data;
> + virtio_update_qa(dev);
> + break;
> + case VIO1_PCI_QUEUE_NOTIFY_OFF:
> + log_warnx("%s: illegal write to queue notify offset "
> + "register", __progname);
> + break;
> + case VIO1_PCI_QUEUE_DESC:
> + if (sz != 4) {
> + log_warnx("%s: unaligned write to queue "
> + "desc. register (sz=%u)", __func__, sz);
> + break;
> + }
> + pci_cfg->queue_desc &= 0xffffffff00000000;
> + pci_cfg->queue_desc |= (uint64_t)data;
> + virtio_update_qa(dev);
> + break;
> + case VIO1_PCI_QUEUE_DESC + 4:
> + if (sz != 4) {
> + log_warnx("%s: unaligned write to queue "
> + "desc. register (sz=%u)", __func__, sz);
> + break;
> + }
> + pci_cfg->queue_desc &= 0x00000000ffffffff;
> + pci_cfg->queue_desc |= ((uint64_t)data << 32);
> + virtio_update_qa(dev);
> + break;
> + case VIO1_PCI_QUEUE_AVAIL:
> + if (sz != 4) {
> + log_warnx("%s: unaligned write to queue "
> + "available register (sz=%u)", __func__, sz);
> + break;
> + }
> + pci_cfg->queue_avail &= 0xffffffff00000000;
> + pci_cfg->queue_avail |= (uint64_t)data;
> + virtio_update_qa(dev);
> + break;
> + case VIO1_PCI_QUEUE_AVAIL + 4:
> + if (sz != 4) {
> + log_warnx("%s: unaligned write to queue "
> + "available register (sz=%u)", __func__, sz);
> + break;
> + }
> + pci_cfg->queue_avail &= 0x00000000ffffffff;
> + pci_cfg->queue_avail |= ((uint64_t)data << 32);
> + virtio_update_qa(dev);
> + break;
> + case VIO1_PCI_QUEUE_USED:
> + if (sz != 4) {
> + log_warnx("%s: unaligned write to queue used "
> + "register (sz=%u)", __func__, sz);
> + break;
> + }
> + pci_cfg->queue_used &= 0xffffffff00000000;
> + pci_cfg->queue_used |= (uint64_t)data;
> + virtio_update_qa(dev);
> + break;
> + case VIO1_PCI_QUEUE_USED + 4:
> + if (sz != 4) {
> + log_warnx("%s: unaligned write to queue used "
> + "register (sz=%u)", __func__, sz);
> + break;
> + }
> + pci_cfg->queue_used &= 0x00000000ffffffff;
> + pci_cfg->queue_used |= ((uint64_t)data << 32);
> + virtio_update_qa(dev);
> + break;
> + default:
> + log_warnx("%s: invalid register 0x%04x", __func__, reg);
> }
> -
> pthread_rwlock_unlock(&lock);
> +
> if (pause_devices) {
> rx_enabled = 0;
> vionet_deassert_pic_irq(dev);
> @@ -1062,60 +1270,82 @@ handle_io_write(struct viodev_msg *msg, struct virtio_
> }
>
> static uint32_t
> -handle_io_read(struct viodev_msg *msg, struct virtio_dev *dev, int8_t *intr)
> +vio1_read(struct virtio_dev *dev, struct viodev_msg *msg, int *deassert)
> {
> - struct vionet_dev *vionet = &dev->vionet;
> - uint32_t data;
> + uint32_t data = (uint32_t)(-1);
> + uint16_t reg = msg->reg;
>
> - pthread_rwlock_rdlock(&lock);
> -
> - switch (msg->reg) {
> - case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI:
> - case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 1:
> - case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 2:
> - case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 3:
> - case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 4:
> - case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 5:
> - data = vionet->mac[msg->reg -
> - VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI];
> + switch (reg & 0xFF00) {
> + case VIO1_CFG_BAR_OFFSET:
> + data = vionet_cfg_read(dev, msg);
> break;
> - case VIRTIO_CONFIG_DEVICE_FEATURES:
> - data = vionet->cfg.device_feature;
> + case VIO1_DEV_BAR_OFFSET:
> + data = vionet_dev_read(dev, msg);
> break;
> - case VIRTIO_CONFIG_GUEST_FEATURES:
> - data = vionet->cfg.guest_feature;
> + case VIO1_NOTIFY_BAR_OFFSET:
> + /* Reads of notify register return all 1's. */
> break;
> - case VIRTIO_CONFIG_QUEUE_PFN:
> - data = vionet->cfg.queue_pfn;
> - break;
> - case VIRTIO_CONFIG_QUEUE_SIZE:
> - data = vionet->cfg.queue_size;
> - break;
> - case VIRTIO_CONFIG_QUEUE_SELECT:
> - data = vionet->cfg.queue_select;
> - break;
> - case VIRTIO_CONFIG_QUEUE_NOTIFY:
> - data = vionet->cfg.queue_notify;
> - break;
> - case VIRTIO_CONFIG_DEVICE_STATUS:
> - data = vionet->cfg.device_status;
> - break;
> - case VIRTIO_CONFIG_ISR_STATUS:
> - pthread_rwlock_unlock(&lock);
> + case VIO1_ISR_BAR_OFFSET:
> pthread_rwlock_wrlock(&lock);
> - data = vionet->cfg.isr_status;
> - vionet->cfg.isr_status = 0;
> - if (intr != NULL)
> - *intr = INTR_STATE_DEASSERT;
> + data = dev->isr;
> + dev->isr = 0;
> + *deassert = 1;
> + pthread_rwlock_unlock(&lock);
> break;
> default:
> - data = 0xFFFFFFFF;
> + log_debug("%s: no handler for reg 0x%04x", __func__, reg);
> }
>
> - pthread_rwlock_unlock(&lock);
> return (data);
> }
>
> +static void
> +vio1_write(struct virtio_dev *dev, struct viodev_msg *msg)
> +{
> + uint16_t reg = msg->reg;
> +
> + switch (reg & 0xFF00) {
> + case VIO1_CFG_BAR_OFFSET:
> + (void)vionet_cfg_write(dev, msg);
> + break;
> + case VIO1_DEV_BAR_OFFSET:
> + /* Ignore all writes to device configuration registers. */
> + break;
> + case VIO1_NOTIFY_BAR_OFFSET:
> + vionet_notifyq(dev, (uint16_t)(msg->data));
> + break;
> + case VIO1_ISR_BAR_OFFSET:
> + /* ignore writes to ISR. */
> + break;
> + default:
> + log_debug("%s: no handler for reg 0x%04x", __func__, reg);
> + }
> +}
> +
> +static uint32_t
> +vionet_dev_read(struct virtio_dev *dev, struct viodev_msg *msg)
> +{
> + struct vionet_dev *vionet = (struct vionet_dev *)&dev->vionet;
> + uint32_t data = (uint32_t)(-1);
> + uint16_t reg = msg->reg & 0xFF;
> +
> + switch (reg) {
> + case VIRTIO_NET_CONFIG_MAC:
> + case VIRTIO_NET_CONFIG_MAC + 1:
> + case VIRTIO_NET_CONFIG_MAC + 2:
> + case VIRTIO_NET_CONFIG_MAC + 3:
> + case VIRTIO_NET_CONFIG_MAC + 4:
> + case VIRTIO_NET_CONFIG_MAC + 5:
> + data = (uint8_t)vionet->mac[reg - VIRTIO_NET_CONFIG_MAC];
> + break;
> + default:
> + log_warnx("%s: invalid register 0x%04x", __func__, reg);
> + return (uint32_t)(-1);
> + }
> +
> + return (data);
> +}
> +
> /*
> * Handle the rx side processing, communicating to the main thread via pipe.
> */
> @@ -1220,7 +1450,6 @@ static void
> read_pipe_tx(int fd, short event, void *arg)
> {
> struct virtio_dev *dev = (struct virtio_dev*)arg;
> - struct vionet_dev *vionet = &dev->vionet;
> enum pipe_msg_type msg;
> int ret = 0;
>
> @@ -1260,12 +1489,12 @@ read_pipe_tx(int fd, short event, void *arg)
> pthread_rwlock_wrlock(&lock);
> if (ret == 1) {
> /* Notify the driver. */
> - vionet->cfg.isr_status |= 1;
> + dev->isr |= 1;
> } else {
> /* Need a reset. Something went wrong. */
> log_warnx("%s: requesting device reset", __func__);
> - vionet->cfg.device_status |= DEVICE_NEEDS_RESET;
> - vionet->cfg.isr_status |= VIRTIO_CONFIG_ISR_CONFIG_CHANGE;
> + dev->status |= DEVICE_NEEDS_RESET;
> + dev->isr |= VIRTIO_CONFIG_ISR_CONFIG_CHANGE;
> }
> pthread_rwlock_unlock(&lock);
>
> @@ -1295,21 +1524,15 @@ read_pipe_main(int fd, short event, void *arg)
> if (resetting == 0) {
> log_debug("%s: resetting virtio network device %d",
> __func__, vionet->idx);
> -
> pthread_rwlock_wrlock(&lock);
> - vionet->cfg.device_status = 0;
> - vionet->cfg.guest_feature = 0;
> - vionet->cfg.queue_pfn = 0;
> - vionet_update_qa(vionet);
> - vionet->cfg.queue_size = 0;
> - vionet_update_qs(vionet);
> - vionet->cfg.queue_select = 0;
> - vionet->cfg.queue_notify = 0;
> - vionet->cfg.isr_status = 0;
> - vionet->vq[RXQ].last_avail = 0;
> - vionet->vq[RXQ].notified_avail = 0;
> - vionet->vq[TXQ].last_avail = 0;
> - vionet->vq[TXQ].notified_avail = 0;
> + dev->status = 0;
> + dev->cfg.guest_feature = 0;
> + dev->cfg.queue_pfn = 0;
> + dev->cfg.queue_select = 0;
> + dev->cfg.queue_notify = 0;
> + dev->isr = 0;
> + virtio_vq_init(dev, TXQ);
> + virtio_vq_init(dev, RXQ);
> pthread_rwlock_unlock(&lock);
> }
> break;
> @@ -1330,7 +1553,7 @@ vionet_assert_pic_irq(struct virtio_dev *dev)
>
> memset(&msg, 0, sizeof(msg));
> msg.irq = dev->irq;
> - msg.vcpu = 0; // XXX
> + msg.vcpu = 0; /* XXX: smp */
> msg.type = VIODEV_MSG_KICK;
> msg.state = INTR_STATE_ASSERT;
>
> @@ -1352,7 +1575,7 @@ vionet_deassert_pic_irq(struct virtio_dev *dev)
>
> memset(&msg, 0, sizeof(msg));
> msg.irq = dev->irq;
> - msg.vcpu = 0; // XXX
> + msg.vcpu = 0; /* XXX: smp */
> msg.type = VIODEV_MSG_KICK;
> msg.state = INTR_STATE_DEASSERT;
>
> blob - 95bc1f46dee702c630f98249195eacd45443b9b5
> blob + 954b747913e2892cad384675d2a3cbc14fe40cb7
> --- usr.sbin/vmd/vioscsi.c
> +++ usr.sbin/vmd/vioscsi.c
> @@ -32,6 +32,16 @@
> #include "vioscsi.h"
> #include "virtio.h"
>
> +#define VIOSCSI_DEBUG 0
> +#ifdef DPRINTF
> +#undef DPRINTF
> +#endif
> +#if VIOSCSI_DEBUG
> +#define DPRINTF log_debug
> +#else
> +#define DPRINTF(x...) do {} while(0)
> +#endif /* VIOSCSI_DEBUG */
> +
> extern char *__progname;
>
> static void
> @@ -66,24 +76,24 @@ vioscsi_prepare_resp(struct virtio_scsi_res_hdr *resp,
> }
>
> static struct vring_desc*
> -vioscsi_next_ring_desc(struct vring_desc* desc, struct vring_desc* cur,
> - uint16_t *idx)
> +vioscsi_next_ring_desc(struct virtio_vq_info *vq_info, struct vring_desc* desc,
> + struct vring_desc* cur, uint16_t *idx)
> {
> - *idx = cur->next & VIOSCSI_QUEUE_MASK;
> + *idx = cur->next & vq_info->mask;
> return &desc[*idx];
> }
>
> static void
> -vioscsi_next_ring_item(struct vioscsi_dev *dev, struct vring_avail *avail,
> - struct vring_used *used, struct vring_desc *desc, uint16_t idx)
> +vioscsi_next_ring_item(struct virtio_vq_info *vq_info,
> + struct vring_avail *avail, struct vring_used *used, struct vring_desc *desc,
> + uint16_t idx)
> {
> - used->ring[used->idx & VIOSCSI_QUEUE_MASK].id = idx;
> - used->ring[used->idx & VIOSCSI_QUEUE_MASK].len = desc->len;
> + used->ring[used->idx & vq_info->mask].id = idx;
> + used->ring[used->idx & vq_info->mask].len = desc->len;
> __sync_synchronize();
> used->idx++;
>
> - dev->vq[dev->cfg.queue_notify].last_avail =
> - avail->idx & VIOSCSI_QUEUE_MASK;
> + vq_info->last_avail = avail->idx & vq_info->mask;
> }
>
> static const char *
> @@ -150,31 +160,25 @@ vioscsi_op_names(uint8_t type)
> }
> }
>
> +#if VIOSCSI_DEBUG
> static const char *
> vioscsi_reg_name(uint8_t reg)
> {
> switch (reg) {
> - case VIRTIO_CONFIG_DEVICE_FEATURES: return "device feature";
> - case VIRTIO_CONFIG_GUEST_FEATURES: return "guest feature";
> - case VIRTIO_CONFIG_QUEUE_PFN: return "queue pfn";
> - case VIRTIO_CONFIG_QUEUE_SIZE: return "queue size";
> - case VIRTIO_CONFIG_QUEUE_SELECT: return "queue select";
> - case VIRTIO_CONFIG_QUEUE_NOTIFY: return "queue notify";
> - case VIRTIO_CONFIG_DEVICE_STATUS: return "device status";
> - case VIRTIO_CONFIG_ISR_STATUS: return "isr status";
> - case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI: return "num_queues";
> - case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 4: return "seg_max";
> - case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 8: return "max_sectors";
> - case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 12: return "cmd_per_lun";
> - case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 16: return "event_info_size";
> - case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 20: return "sense_size";
> - case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 24: return "cdb_size";
> - case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 28: return "max_channel";
> - case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 30: return "max_target";
> - case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 32: return "max_lun";
> + case VIRTIO_SCSI_CONFIG_NUM_QUEUES: return "NUM_QUEUES";
> + case VIRTIO_SCSI_CONFIG_SEG_MAX: return "SEG_MAX";
> + case VIRTIO_SCSI_CONFIG_MAX_SECTORS: return "MAX_SECTORS";
> + case VIRTIO_SCSI_CONFIG_CMD_PER_LUN: return "CMD_PER_LUN";
> + case VIRTIO_SCSI_CONFIG_EVENT_INFO_SIZE: return "EVENT_INFO_SIZE";
> + case VIRTIO_SCSI_CONFIG_SENSE_SIZE: return "SENSE_SIZE";
> + case VIRTIO_SCSI_CONFIG_CDB_SIZE: return "CDB_SIZE";
> + case VIRTIO_SCSI_CONFIG_MAX_CHANNEL: return "MAX_CHANNEL";
> + case VIRTIO_SCSI_CONFIG_MAX_TARGET: return "MAX_TARGET";
> + case VIRTIO_SCSI_CONFIG_MAX_LUN: return "MAX_LUN";
> default: return "unknown";
> }
> }
> +#endif /* VIOSCSI_DEBUG */
>
> static void
> vioscsi_free_info(struct ioinfo *info)
> @@ -186,7 +190,7 @@ vioscsi_free_info(struct ioinfo *info)
> }
>
> static struct ioinfo *
> -vioscsi_start_read(struct vioscsi_dev *dev, off_t block, size_t n_blocks)
> +vioscsi_start_read(struct virtio_dev *dev, off_t block, size_t n_blocks)
> {
> struct ioinfo *info;
>
> @@ -214,10 +218,16 @@ nomem:
> }
>
> static const uint8_t *
> -vioscsi_finish_read(struct vioscsi_dev *dev, struct ioinfo *info)
> +vioscsi_finish_read(struct virtio_dev *dev, struct ioinfo *info)
> {
> - struct virtio_backing *f = &dev->file;
> + struct virtio_backing *f = NULL;
> + struct vioscsi_dev *vioscsi = NULL;
>
> + if (dev->device_id != PCI_PRODUCT_VIRTIO_SCSI)
> + fatalx("%s: virtio device is not a scsi device", __func__);
> + vioscsi = &dev->vioscsi;
> +
> + f = &vioscsi->file;
> if (f->pread(f->p, info->buf, info->len, info->offset) != info->len) {
> log_warn("vioscsi read error");
> return NULL;
> @@ -227,16 +237,16 @@ vioscsi_finish_read(struct vioscsi_dev *dev, struct io
> }
>
> static int
> -vioscsi_handle_tur(struct vioscsi_dev *dev, struct virtio_scsi_req_hdr *req,
> - struct virtio_vq_acct *acct)
> +vioscsi_handle_tur(struct virtio_dev *dev, struct virtio_vq_info *vq_info,
> + struct virtio_scsi_req_hdr *req, struct virtio_vq_acct *acct)
> {
> int ret = 0;
> struct virtio_scsi_res_hdr resp;
>
> memset(&resp, 0, sizeof(resp));
> /* Move index for response */
> - acct->resp_desc = vioscsi_next_ring_desc(acct->desc, acct->req_desc,
> - &(acct->resp_idx));
> + acct->resp_desc = vioscsi_next_ring_desc(vq_info, acct->desc,
> + acct->req_desc, &(acct->resp_idx));
>
> vioscsi_prepare_resp(&resp, VIRTIO_SCSI_S_OK, SCSI_OK, 0, 0, 0);
>
> @@ -245,9 +255,9 @@ vioscsi_handle_tur(struct vioscsi_dev *dev, struct vir
> __func__, acct->resp_desc->addr);
> } else {
> ret = 1;
> - dev->cfg.isr_status = 1;
> + dev->isr = 1;
> /* Move ring indexes */
> - vioscsi_next_ring_item(dev, acct->avail, acct->used,
> + vioscsi_next_ring_item(vq_info, acct->avail, acct->used,
> acct->req_desc, acct->req_idx);
> }
>
> @@ -255,7 +265,7 @@ vioscsi_handle_tur(struct vioscsi_dev *dev, struct vir
> }
>
> static int
> -vioscsi_handle_inquiry(struct vioscsi_dev *dev,
> +vioscsi_handle_inquiry(struct virtio_dev *dev, struct virtio_vq_info *vq_info,
> struct virtio_scsi_req_hdr *req, struct virtio_vq_acct *acct)
> {
> int ret = 0;
> @@ -289,8 +299,8 @@ vioscsi_handle_inquiry(struct vioscsi_dev *dev,
> memcpy(inq_data->revision, INQUIRY_REVISION, INQUIRY_REVISION_LEN);
>
> /* Move index for response */
> - acct->resp_desc = vioscsi_next_ring_desc(acct->desc, acct->req_desc,
> - &(acct->resp_idx));
> + acct->resp_desc = vioscsi_next_ring_desc(vq_info, acct->desc,
> + acct->req_desc, &(acct->resp_idx));
>
> DPRINTF("%s: writing resp to 0x%llx size %d at local "
> "idx %d req_idx %d global_idx %d", __func__, acct->resp_desc->addr,
> @@ -303,8 +313,8 @@ vioscsi_handle_inquiry(struct vioscsi_dev *dev,
> }
>
> /* Move index for inquiry_data */
> - acct->resp_desc = vioscsi_next_ring_desc(acct->desc, acct->resp_desc,
> - &(acct->resp_idx));
> + acct->resp_desc = vioscsi_next_ring_desc(vq_info, acct->desc,
> + acct->resp_desc, &(acct->resp_idx));
>
> DPRINTF("%s: writing inq_data to 0x%llx size %d at "
> "local idx %d req_idx %d global_idx %d",
> @@ -318,9 +328,9 @@ vioscsi_handle_inquiry(struct vioscsi_dev *dev,
> __func__, acct->resp_desc->addr);
> } else {
> ret = 1;
> - dev->cfg.isr_status = 1;
> + dev->isr = 1;
> /* Move ring indexes */
> - vioscsi_next_ring_item(dev, acct->avail, acct->used,
> + vioscsi_next_ring_item(vq_info, acct->avail, acct->used,
> acct->req_desc, acct->req_idx);
> }
>
> @@ -331,8 +341,9 @@ inq_out:
> }
>
> static int
> -vioscsi_handle_mode_sense(struct vioscsi_dev *dev,
> - struct virtio_scsi_req_hdr *req, struct virtio_vq_acct *acct)
> +vioscsi_handle_mode_sense(struct virtio_dev *dev,
> + struct virtio_vq_info *vq_info, struct virtio_scsi_req_hdr *req,
> + struct virtio_vq_acct *acct)
> {
> int ret = 0;
> struct virtio_scsi_res_hdr resp;
> @@ -400,7 +411,7 @@ vioscsi_handle_mode_sense(struct vioscsi_dev *dev,
> }
>
> /* Move index for response */
> - acct->resp_desc = vioscsi_next_ring_desc(acct->desc,
> + acct->resp_desc = vioscsi_next_ring_desc(vq_info, acct->desc,
> acct->req_desc, &(acct->resp_idx));
>
> DPRINTF("%s: writing resp to 0x%llx size %d "
> @@ -417,7 +428,7 @@ vioscsi_handle_mode_sense(struct vioscsi_dev *dev,
> }
>
> /* Move index for mode_reply */
> - acct->resp_desc = vioscsi_next_ring_desc(acct->desc,
> + acct->resp_desc = vioscsi_next_ring_desc(vq_info, acct->desc,
> acct->resp_desc, &(acct->resp_idx));
>
> DPRINTF("%s: writing mode_reply to 0x%llx "
> @@ -437,9 +448,9 @@ vioscsi_handle_mode_sense(struct vioscsi_dev *dev,
> free(mode_reply);
>
> ret = 1;
> - dev->cfg.isr_status = 1;
> + dev->isr = 1;
> /* Move ring indexes */
> - vioscsi_next_ring_item(dev, acct->avail, acct->used,
> + vioscsi_next_ring_item(vq_info, acct->avail, acct->used,
> acct->req_desc, acct->req_idx);
> } else {
> mode_sense_error:
> @@ -449,7 +460,7 @@ mode_sense_error:
> SENSE_ILLEGAL_CDB_FIELD, SENSE_DEFAULT_ASCQ);
>
> /* Move index for response */
> - acct->resp_desc = vioscsi_next_ring_desc(acct->desc,
> + acct->resp_desc = vioscsi_next_ring_desc(vq_info, acct->desc,
> acct->req_desc, &(acct->resp_idx));
>
> if (write_mem(acct->resp_desc->addr, &resp, sizeof(resp))) {
> @@ -459,9 +470,9 @@ mode_sense_error:
> }
>
> ret = 1;
> - dev->cfg.isr_status = 1;
> + dev->isr = 1;
> /* Move ring indexes */
> - vioscsi_next_ring_item(dev, acct->avail, acct->used,
> + vioscsi_next_ring_item(vq_info, acct->avail, acct->used,
> acct->req_desc, acct->req_idx);
> }
> mode_sense_out:
> @@ -469,8 +480,9 @@ mode_sense_out:
> }
>
> static int
> -vioscsi_handle_mode_sense_big(struct vioscsi_dev *dev,
> - struct virtio_scsi_req_hdr *req, struct virtio_vq_acct *acct)
> +vioscsi_handle_mode_sense_big(struct virtio_dev *dev,
> + struct virtio_vq_info *vq_info, struct virtio_scsi_req_hdr *req,
> + struct virtio_vq_acct *acct)
> {
> int ret = 0;
> struct virtio_scsi_res_hdr resp;
> @@ -538,7 +550,7 @@ vioscsi_handle_mode_sense_big(struct vioscsi_dev *dev,
> }
>
> /* Move index for response */
> - acct->resp_desc = vioscsi_next_ring_desc(acct->desc,
> + acct->resp_desc = vioscsi_next_ring_desc(vq_info, acct->desc,
> acct->req_desc, &(acct->resp_idx));
>
> DPRINTF("%s: writing resp to 0x%llx size %d "
> @@ -555,7 +567,7 @@ vioscsi_handle_mode_sense_big(struct vioscsi_dev *dev,
> }
>
> /* Move index for mode_reply */
> - acct->resp_desc = vioscsi_next_ring_desc(acct->desc,
> + acct->resp_desc = vioscsi_next_ring_desc(vq_info, acct->desc,
> acct->resp_desc, &(acct->resp_idx));
>
> DPRINTF("%s: writing mode_reply to 0x%llx "
> @@ -575,9 +587,9 @@ vioscsi_handle_mode_sense_big(struct vioscsi_dev *dev,
> free(mode_reply);
>
> ret = 1;
> - dev->cfg.isr_status = 1;
> + dev->isr = 1;
> /* Move ring indexes */
> - vioscsi_next_ring_item(dev, acct->avail, acct->used,
> + vioscsi_next_ring_item(vq_info, acct->avail, acct->used,
> acct->req_desc, acct->req_idx);
> } else {
> mode_sense_big_error:
> @@ -587,7 +599,7 @@ mode_sense_big_error:
> SENSE_ILLEGAL_CDB_FIELD, SENSE_DEFAULT_ASCQ);
>
> /* Move index for response */
> - acct->resp_desc = vioscsi_next_ring_desc(acct->desc,
> + acct->resp_desc = vioscsi_next_ring_desc(vq_info, acct->desc,
> acct->req_desc, &(acct->resp_idx));
>
> if (write_mem(acct->resp_desc->addr, &resp, sizeof(resp))) {
> @@ -597,9 +609,9 @@ mode_sense_big_error:
> }
>
> ret = 1;
> - dev->cfg.isr_status = 1;
> + dev->isr = 1;
> /* Move ring indexes */
> - vioscsi_next_ring_item(dev, acct->avail, acct->used,
> + vioscsi_next_ring_item(vq_info, acct->avail, acct->used,
> acct->req_desc, acct->req_idx);
> }
> mode_sense_big_out:
> @@ -607,13 +619,19 @@ mode_sense_big_out:
> }
>
> static int
> -vioscsi_handle_read_capacity(struct vioscsi_dev *dev,
> - struct virtio_scsi_req_hdr *req, struct virtio_vq_acct *acct)
> +vioscsi_handle_read_capacity(struct virtio_dev *dev,
> + struct virtio_vq_info *vq_info,struct virtio_scsi_req_hdr *req,
> + struct virtio_vq_acct *acct)
> {
> int ret = 0;
> struct virtio_scsi_res_hdr resp;
> struct scsi_read_cap_data *r_cap_data;
> + struct vioscsi_dev *vioscsi = NULL;
>
> + if (dev->device_id != PCI_PRODUCT_VIRTIO_SCSI)
> + fatalx("%s: virtio device is not a scsi device", __func__);
> + vioscsi = &dev->vioscsi;
> +
> #if DEBUG
> struct scsi_read_capacity *r_cap =
> (struct scsi_read_capacity *)(req->cdb);
> @@ -633,7 +651,7 @@ vioscsi_handle_read_capacity(struct vioscsi_dev *dev,
> }
>
> DPRINTF("%s: ISO has %lld bytes and %lld blocks",
> - __func__, dev->sz, dev->n_blocks);
> + __func__, vioscsi->sz, vioscsi->n_blocks);
>
> /*
> * determine if num blocks of iso image > UINT32_MAX
> @@ -641,20 +659,20 @@ vioscsi_handle_read_capacity(struct vioscsi_dev *dev,
> * indicating to hosts that READ_CAPACITY_16 should
> * be called to retrieve the full size
> */
> - if (dev->n_blocks >= UINT32_MAX) {
> + if (vioscsi->n_blocks >= UINT32_MAX) {
> _lto4b(UINT32_MAX, r_cap_data->addr);
> _lto4b(VIOSCSI_BLOCK_SIZE_CDROM, r_cap_data->length);
> log_warnx("%s: ISO sz %lld is bigger than "
> "UINT32_MAX %u, all data may not be read",
> - __func__, dev->sz, UINT32_MAX);
> + __func__, vioscsi->sz, UINT32_MAX);
> } else {
> - _lto4b(dev->n_blocks - 1, r_cap_data->addr);
> + _lto4b(vioscsi->n_blocks - 1, r_cap_data->addr);
> _lto4b(VIOSCSI_BLOCK_SIZE_CDROM, r_cap_data->length);
> }
>
> /* Move index for response */
> - acct->resp_desc = vioscsi_next_ring_desc(acct->desc, acct->req_desc,
> - &(acct->resp_idx));
> + acct->resp_desc = vioscsi_next_ring_desc(vq_info, acct->desc,
> + acct->req_desc, &(acct->resp_idx));
>
> DPRINTF("%s: writing resp to 0x%llx size %d at local "
> "idx %d req_idx %d global_idx %d",
> @@ -668,8 +686,8 @@ vioscsi_handle_read_capacity(struct vioscsi_dev *dev,
> }
>
> /* Move index for r_cap_data */
> - acct->resp_desc = vioscsi_next_ring_desc(acct->desc, acct->resp_desc,
> - &(acct->resp_idx));
> + acct->resp_desc = vioscsi_next_ring_desc(vq_info, acct->desc,
> + acct->resp_desc, &(acct->resp_idx));
>
> DPRINTF("%s: writing r_cap_data to 0x%llx size %d at "
> "local idx %d req_idx %d global_idx %d",
> @@ -683,9 +701,9 @@ vioscsi_handle_read_capacity(struct vioscsi_dev *dev,
> __func__, acct->resp_desc->addr);
> } else {
> ret = 1;
> - dev->cfg.isr_status = 1;
> + dev->isr = 1;
> /* Move ring indexes */
> - vioscsi_next_ring_item(dev, acct->avail, acct->used,
> + vioscsi_next_ring_item(vq_info, acct->avail, acct->used,
> acct->req_desc, acct->req_idx);
> }
>
> @@ -696,13 +714,19 @@ read_capacity_out:
> }
>
> static int
> -vioscsi_handle_read_capacity_16(struct vioscsi_dev *dev,
> - struct virtio_scsi_req_hdr *req, struct virtio_vq_acct *acct)
> +vioscsi_handle_read_capacity_16(struct virtio_dev *dev,
> + struct virtio_vq_info *vq_info, struct virtio_scsi_req_hdr *req,
> + struct virtio_vq_acct *acct)
> {
> int ret = 0;
> struct virtio_scsi_res_hdr resp;
> struct scsi_read_cap_data_16 *r_cap_data_16;
> + struct vioscsi_dev *vioscsi = NULL;
>
> + if (dev->device_id != PCI_PRODUCT_VIRTIO_SCSI)
> + fatalx("%s: virtio device is not a scsi device", __func__);
> + vioscsi = &dev->vioscsi;
> +
> #if DEBUG
> struct scsi_read_capacity_16 *r_cap_16 =
> (struct scsi_read_capacity_16 *)(req->cdb);
> @@ -722,14 +746,14 @@ vioscsi_handle_read_capacity_16(struct vioscsi_dev *de
> }
>
> DPRINTF("%s: ISO has %lld bytes and %lld blocks", __func__,
> - dev->sz, dev->n_blocks);
> + dev->vioscsi.sz, dev->vioscsi.n_blocks);
>
> - _lto8b(dev->n_blocks - 1, r_cap_data_16->addr);
> + _lto8b(vioscsi->n_blocks - 1, r_cap_data_16->addr);
> _lto4b(VIOSCSI_BLOCK_SIZE_CDROM, r_cap_data_16->length);
>
> /* Move index for response */
> - acct->resp_desc = vioscsi_next_ring_desc(acct->desc, acct->req_desc,
> - &(acct->resp_idx));
> + acct->resp_desc = vioscsi_next_ring_desc(vq_info, acct->desc,
> + acct->req_desc, &(acct->resp_idx));
>
> DPRINTF("%s: writing resp to 0x%llx size %d at local "
> "idx %d req_idx %d global_idx %d",
> @@ -743,8 +767,8 @@ vioscsi_handle_read_capacity_16(struct vioscsi_dev *de
> }
>
> /* Move index for r_cap_data_16 */
> - acct->resp_desc = vioscsi_next_ring_desc(acct->desc, acct->resp_desc,
> - &(acct->resp_idx));
> + acct->resp_desc = vioscsi_next_ring_desc(vq_info, acct->desc,
> + acct->resp_desc, &(acct->resp_idx));
>
> DPRINTF("%s: writing r_cap_data_16 to 0x%llx size %d "
> "at local idx %d req_idx %d global_idx %d",
> @@ -758,9 +782,9 @@ vioscsi_handle_read_capacity_16(struct vioscsi_dev *de
> __func__, acct->resp_desc->addr);
> } else {
> ret = 1;
> - dev->cfg.isr_status = 1;
> + dev->isr = 1;
> /* Move ring indexes */
> - vioscsi_next_ring_item(dev, acct->avail, acct->used,
> + vioscsi_next_ring_item(vq_info, acct->avail, acct->used,
> acct->req_desc, acct->req_idx);
> }
>
> @@ -771,8 +795,9 @@ read_capacity_16_out:
> }
>
> static int
> -vioscsi_handle_report_luns(struct vioscsi_dev *dev,
> - struct virtio_scsi_req_hdr *req, struct virtio_vq_acct *acct)
> +vioscsi_handle_report_luns(struct virtio_dev *dev,
> + struct virtio_vq_info *vq_info, struct virtio_scsi_req_hdr *req,
> + struct virtio_vq_acct *acct)
> {
> int ret = 0;
> struct virtio_scsi_res_hdr resp;
> @@ -796,7 +821,7 @@ vioscsi_handle_report_luns(struct vioscsi_dev *dev,
> SENSE_ILLEGAL_CDB_FIELD, SENSE_DEFAULT_ASCQ);
>
> /* Move index for response */
> - acct->resp_desc = vioscsi_next_ring_desc(acct->desc,
> + acct->resp_desc = vioscsi_next_ring_desc(vq_info, acct->desc,
> acct->req_desc, &(acct->resp_idx));
>
> if (write_mem(acct->resp_desc->addr, &resp, sizeof(resp))) {
> @@ -805,9 +830,9 @@ vioscsi_handle_report_luns(struct vioscsi_dev *dev,
> acct->resp_desc->addr);
> } else {
> ret = 1;
> - dev->cfg.isr_status = 1;
> + dev->isr = 1;
> /* Move ring indexes */
> - vioscsi_next_ring_item(dev, acct->avail, acct->used,
> + vioscsi_next_ring_item(vq_info, acct->avail, acct->used,
> acct->req_desc, acct->req_idx);
> }
> goto rpl_out;
> @@ -828,8 +853,8 @@ vioscsi_handle_report_luns(struct vioscsi_dev *dev,
> VIRTIO_SCSI_S_OK, SCSI_OK, 0, 0, 0);
>
> /* Move index for response */
> - acct->resp_desc = vioscsi_next_ring_desc(acct->desc, acct->req_desc,
> - &(acct->resp_idx));
> + acct->resp_desc = vioscsi_next_ring_desc(vq_info, acct->desc,
> + acct->req_desc, &(acct->resp_idx));
>
> DPRINTF("%s: writing resp to 0x%llx size %d at local "
> "idx %d req_idx %d global_idx %d", __func__, acct->resp_desc->addr,
> @@ -842,8 +867,8 @@ vioscsi_handle_report_luns(struct vioscsi_dev *dev,
> }
>
> /* Move index for reply_rpl */
> - acct->resp_desc = vioscsi_next_ring_desc(acct->desc, acct->resp_desc,
> - &(acct->resp_idx));
> + acct->resp_desc = vioscsi_next_ring_desc(vq_info, acct->desc,
> + acct->resp_desc, &(acct->resp_idx));
>
> DPRINTF("%s: writing reply_rpl to 0x%llx size %d at "
> "local idx %d req_idx %d global_idx %d",
> @@ -857,9 +882,9 @@ vioscsi_handle_report_luns(struct vioscsi_dev *dev,
> __func__, acct->resp_desc->addr);
> } else {
> ret = 1;
> - dev->cfg.isr_status = 1;
> + dev->isr = 1;
> /* Move ring indexes */
> - vioscsi_next_ring_item(dev, acct->avail, acct->used,
> + vioscsi_next_ring_item(vq_info, acct->avail, acct->used,
> acct->req_desc, acct->req_idx);
> }
>
> @@ -870,8 +895,9 @@ rpl_out:
> }
>
> static int
> -vioscsi_handle_read_6(struct vioscsi_dev *dev,
> - struct virtio_scsi_req_hdr *req, struct virtio_vq_acct *acct)
> +vioscsi_handle_read_6(struct virtio_dev *dev,
> + struct virtio_vq_info *vq_info, struct virtio_scsi_req_hdr *req,
> + struct virtio_vq_acct *acct)
> {
> int ret = 0;
> struct virtio_scsi_res_hdr resp;
> @@ -879,26 +905,32 @@ vioscsi_handle_read_6(struct vioscsi_dev *dev,
> uint32_t read_lba;
> struct ioinfo *info;
> struct scsi_rw *read_6;
> + struct vioscsi_dev *vioscsi = NULL;
>
> + if (dev->device_id != PCI_PRODUCT_VIRTIO_SCSI)
> + fatalx("%s: virtio device is not a scsi device", __func__);
> + vioscsi = &dev->vioscsi;
> +
> memset(&resp, 0, sizeof(resp));
> read_6 = (struct scsi_rw *)(req->cdb);
> read_lba = ((read_6->addr[0] & SRW_TOPADDR) << 16 ) |
> (read_6->addr[1] << 8) | read_6->addr[2];
>
> DPRINTF("%s: READ Addr 0x%08x Len %d (%d)",
> - __func__, read_lba, read_6->length, read_6->length * dev->max_xfer);
> + __func__, read_lba, read_6->length,
> + read_6->length * dev->vioscsi.max_xfer);
>
> /* check if lba is in range */
> - if (read_lba > dev->n_blocks - 1) {
> + if (read_lba > vioscsi->n_blocks - 1) {
> DPRINTF("%s: requested block out of range req: %ud max: %lld",
> - __func__, read_lba, dev->n_blocks);
> + __func__, read_lba, vioscsi->n_blocks);
>
> vioscsi_prepare_resp(&resp,
> VIRTIO_SCSI_S_OK, SCSI_CHECK, SKEY_ILLEGAL_REQUEST,
> SENSE_LBA_OUT_OF_RANGE, SENSE_DEFAULT_ASCQ);
>
> /* Move index for response */
> - acct->resp_desc = vioscsi_next_ring_desc(acct->desc,
> + acct->resp_desc = vioscsi_next_ring_desc(vq_info, acct->desc,
> acct->req_desc, &(acct->resp_idx));
>
> if (write_mem(acct->resp_desc->addr, &resp, sizeof(resp))) {
> @@ -907,9 +939,9 @@ vioscsi_handle_read_6(struct vioscsi_dev *dev,
> acct->resp_desc->addr);
> } else {
> ret = 1;
> - dev->cfg.isr_status = 1;
> + dev->isr = 1;
> /* Move ring indexes */
> - vioscsi_next_ring_item(dev, acct->avail, acct->used,
> + vioscsi_next_ring_item(vq_info, acct->avail, acct->used,
> acct->req_desc, acct->req_idx);
> }
> goto read_6_out;
> @@ -933,7 +965,7 @@ vioscsi_handle_read_6(struct vioscsi_dev *dev,
> SENSE_MEDIUM_NOT_PRESENT, SENSE_DEFAULT_ASCQ);
>
> /* Move index for response */
> - acct->resp_desc = vioscsi_next_ring_desc(acct->desc,
> + acct->resp_desc = vioscsi_next_ring_desc(vq_info, acct->desc,
> acct->req_desc, &(acct->resp_idx));
>
> if (write_mem(acct->resp_desc->addr, &resp, sizeof(resp))) {
> @@ -942,9 +974,9 @@ vioscsi_handle_read_6(struct vioscsi_dev *dev,
> acct->resp_desc->addr);
> } else {
> ret = 1;
> - dev->cfg.isr_status = 1;
> + dev->isr = 1;
> /* Move ring indexes */
> - vioscsi_next_ring_item(dev, acct->avail, acct->used,
> + vioscsi_next_ring_item(vq_info, acct->avail, acct->used,
> acct->req_desc, acct->req_idx);
> }
>
> @@ -954,8 +986,8 @@ vioscsi_handle_read_6(struct vioscsi_dev *dev,
> vioscsi_prepare_resp(&resp, VIRTIO_SCSI_S_OK, SCSI_OK, 0, 0, 0);
>
> /* Move index for response */
> - acct->resp_desc = vioscsi_next_ring_desc(acct->desc, acct->req_desc,
> - &(acct->resp_idx));
> + acct->resp_desc = vioscsi_next_ring_desc(vq_info, acct->desc,
> + acct->req_desc, &(acct->resp_idx));
>
> DPRINTF("%s: writing resp to 0x%llx size %d at local "
> "idx %d req_idx %d global_idx %d",
> @@ -969,8 +1001,8 @@ vioscsi_handle_read_6(struct vioscsi_dev *dev,
> }
>
> /* Move index for read_buf */
> - acct->resp_desc = vioscsi_next_ring_desc(acct->desc, acct->resp_desc,
> - &(acct->resp_idx));
> + acct->resp_desc = vioscsi_next_ring_desc(vq_info, acct->desc,
> + acct->resp_desc, &(acct->resp_idx));
>
> DPRINTF("%s: writing read_buf to 0x%llx size %d at "
> "local idx %d req_idx %d global_idx %d",
> @@ -982,9 +1014,9 @@ vioscsi_handle_read_6(struct vioscsi_dev *dev,
> __func__, acct->resp_desc->addr);
> } else {
> ret = 1;
> - dev->cfg.isr_status = 1;
> + dev->isr = 1;
> /* Move ring indexes */
> - vioscsi_next_ring_item(dev, acct->avail, acct->used,
> + vioscsi_next_ring_item(vq_info, acct->avail, acct->used,
> acct->req_desc, acct->req_idx);
> }
>
> @@ -995,8 +1027,9 @@ read_6_out:
> }
>
> static int
> -vioscsi_handle_read_10(struct vioscsi_dev *dev,
> - struct virtio_scsi_req_hdr *req, struct virtio_vq_acct *acct)
> +vioscsi_handle_read_10(struct virtio_dev *dev,
> + struct virtio_vq_info *vq_info, struct virtio_scsi_req_hdr *req,
> + struct virtio_vq_acct *acct)
> {
> int ret = 0;
> struct virtio_scsi_res_hdr resp;
> @@ -1007,7 +1040,12 @@ vioscsi_handle_read_10(struct vioscsi_dev *dev,
> struct ioinfo *info;
> struct scsi_rw_10 *read_10;
> size_t chunk_len = 0;
> + struct vioscsi_dev *vioscsi = NULL;
>
> + if (dev->device_id != PCI_PRODUCT_VIRTIO_SCSI)
> + fatalx("%s: virtio device is not a scsi device", __func__);
> + vioscsi = &dev->vioscsi;
> +
> memset(&resp, 0, sizeof(resp));
> read_10 = (struct scsi_rw_10 *)(req->cdb);
> read_lba = _4btol(read_10->addr);
> @@ -1015,19 +1053,19 @@ vioscsi_handle_read_10(struct vioscsi_dev *dev,
> chunk_offset = 0;
>
> DPRINTF("%s: READ_10 Addr 0x%08x Len %d (%d)",
> - __func__, read_lba, read_10_len, read_10_len * dev->max_xfer);
> + __func__, read_lba, read_10_len, read_10_len * vioscsi->max_xfer);
>
> /* check if lba is in range */
> - if (read_lba > dev->n_blocks - 1) {
> + if (read_lba > vioscsi->n_blocks - 1) {
> DPRINTF("%s: requested block out of range req: %ud max: %lld",
> - __func__, read_lba, dev->n_blocks);
> + __func__, read_lba, vioscsi->n_blocks);
>
> vioscsi_prepare_resp(&resp,
> VIRTIO_SCSI_S_OK, SCSI_CHECK, SKEY_ILLEGAL_REQUEST,
> SENSE_LBA_OUT_OF_RANGE, SENSE_DEFAULT_ASCQ);
>
> /* Move index for response */
> - acct->resp_desc = vioscsi_next_ring_desc(acct->desc,
> + acct->resp_desc = vioscsi_next_ring_desc(vq_info, acct->desc,
> acct->req_desc, &(acct->resp_idx));
>
> if (write_mem(acct->resp_desc->addr, &resp, sizeof(resp))) {
> @@ -1035,9 +1073,9 @@ vioscsi_handle_read_10(struct vioscsi_dev *dev,
> __func__, acct->resp_desc->addr);
> } else {
> ret = 1;
> - dev->cfg.isr_status = 1;
> + dev->isr = 1;
> /* Move ring indexes */
> - vioscsi_next_ring_item(dev, acct->avail, acct->used,
> + vioscsi_next_ring_item(vq_info, acct->avail, acct->used,
> acct->req_desc, acct->req_idx);
> }
>
> @@ -1061,7 +1099,7 @@ vioscsi_handle_read_10(struct vioscsi_dev *dev,
> SENSE_MEDIUM_NOT_PRESENT, SENSE_DEFAULT_ASCQ);
>
> /* Move index for response */
> - acct->resp_desc = vioscsi_next_ring_desc(acct->desc,
> + acct->resp_desc = vioscsi_next_ring_desc(vq_info, acct->desc,
> acct->req_desc, &(acct->resp_idx));
>
> if (write_mem(acct->resp_desc->addr, &resp, sizeof(resp))) {
> @@ -1069,9 +1107,9 @@ vioscsi_handle_read_10(struct vioscsi_dev *dev,
> __func__, acct->resp_desc->addr);
> } else {
> ret = 1;
> - dev->cfg.isr_status = 1;
> + dev->isr = 1;
> /* Move ring indexes */
> - vioscsi_next_ring_item(dev, acct->avail, acct->used,
> + vioscsi_next_ring_item(vq_info, acct->avail, acct->used,
> acct->req_desc, acct->req_idx);
> }
>
> @@ -1081,8 +1119,8 @@ vioscsi_handle_read_10(struct vioscsi_dev *dev,
> vioscsi_prepare_resp(&resp, VIRTIO_SCSI_S_OK, SCSI_OK, 0, 0, 0);
>
> /* Move index for response */
> - acct->resp_desc = vioscsi_next_ring_desc(acct->desc, acct->req_desc,
> - &(acct->resp_idx));
> + acct->resp_desc = vioscsi_next_ring_desc(vq_info, acct->desc,
> + acct->req_desc, &(acct->resp_idx));
>
> DPRINTF("%s: writing resp to 0x%llx size %d at local "
> "idx %d req_idx %d global_idx %d",
> @@ -1103,7 +1141,7 @@ vioscsi_handle_read_10(struct vioscsi_dev *dev,
> */
> do {
> /* Move index for read_buf */
> - acct->resp_desc = vioscsi_next_ring_desc(acct->desc,
> + acct->resp_desc = vioscsi_next_ring_desc(vq_info, acct->desc,
> acct->resp_desc, &(acct->resp_idx));
>
> DPRINTF("%s: writing read_buf to 0x%llx size "
> @@ -1130,9 +1168,9 @@ vioscsi_handle_read_10(struct vioscsi_dev *dev,
> } while (chunk_offset < info->len);
>
> ret = 1;
> - dev->cfg.isr_status = 1;
> + dev->isr = 1;
> /* Move ring indexes */
> - vioscsi_next_ring_item(dev, acct->avail, acct->used, acct->req_desc,
> + vioscsi_next_ring_item(vq_info, acct->avail, acct->used, acct->req_desc,
> acct->req_idx);
>
> free_read_10:
> @@ -1142,35 +1180,41 @@ read_10_out:
> }
>
> static int
> -vioscsi_handle_prevent_allow(struct vioscsi_dev *dev,
> - struct virtio_scsi_req_hdr *req, struct virtio_vq_acct *acct)
> +vioscsi_handle_prevent_allow(struct virtio_dev *dev,
> + struct virtio_vq_info *vq_info, struct virtio_scsi_req_hdr *req,
> + struct virtio_vq_acct *acct)
> {
> int ret = 0;
> + struct vioscsi_dev *vioscsi = NULL;
> struct virtio_scsi_res_hdr resp;
>
> + if (dev->device_id != PCI_PRODUCT_VIRTIO_SCSI)
> + fatalx("%s: virtio device is not a scsi device", __func__);
> + vioscsi = &dev->vioscsi;
> +
> memset(&resp, 0, sizeof(resp));
> /* Move index for response */
> - acct->resp_desc = vioscsi_next_ring_desc(acct->desc, acct->req_desc,
> - &(acct->resp_idx));
> + acct->resp_desc = vioscsi_next_ring_desc(vq_info, acct->desc,
> + acct->req_desc, &(acct->resp_idx));
>
> vioscsi_prepare_resp(&resp, VIRTIO_SCSI_S_OK, SCSI_OK, 0, 0, 0);
>
> - if (dev->locked) {
> + if (vioscsi->locked) {
> DPRINTF("%s: unlocking medium", __func__);
> } else {
> DPRINTF("%s: locking medium", __func__);
> }
>
> - dev->locked = dev->locked ? 0 : 1;
> + vioscsi->locked = vioscsi->locked ? 0 : 1;
>
> if (write_mem(acct->resp_desc->addr, &resp, sizeof(resp))) {
> log_warnx("%s: unable to write OK resp status data @ 0x%llx",
> __func__, acct->resp_desc->addr);
> } else {
> ret = 1;
> - dev->cfg.isr_status = 1;
> + dev->isr = 1;
> /* Move ring indexes */
> - vioscsi_next_ring_item(dev, acct->avail, acct->used,
> + vioscsi_next_ring_item(vq_info, acct->avail, acct->used,
> acct->req_desc, acct->req_idx);
> }
>
> @@ -1178,8 +1222,9 @@ vioscsi_handle_prevent_allow(struct vioscsi_dev *dev,
> }
>
> static int
> -vioscsi_handle_mechanism_status(struct vioscsi_dev *dev,
> - struct virtio_scsi_req_hdr *req, struct virtio_vq_acct *acct)
> +vioscsi_handle_mechanism_status(struct virtio_dev *dev,
> + struct virtio_vq_info *vq_info, struct virtio_scsi_req_hdr *req,
> + struct virtio_vq_acct *acct)
> {
> int ret = 0;
> struct virtio_scsi_res_hdr resp;
> @@ -1200,7 +1245,7 @@ vioscsi_handle_mechanism_status(struct vioscsi_dev *de
> VIRTIO_SCSI_S_OK, SCSI_OK, 0, 0, 0);
>
> /* Move index for response */
> - acct->resp_desc = vioscsi_next_ring_desc(acct->desc,
> + acct->resp_desc = vioscsi_next_ring_desc(vq_info, acct->desc,
> acct->req_desc, &(acct->resp_idx));
>
> if (write_mem(acct->resp_desc->addr, &resp, sizeof(resp))) {
> @@ -1210,8 +1255,8 @@ vioscsi_handle_mechanism_status(struct vioscsi_dev *de
> }
>
> /* Move index for mech_status_header */
> - acct->resp_desc = vioscsi_next_ring_desc(acct->desc, acct->resp_desc,
> - &(acct->resp_idx));
> + acct->resp_desc = vioscsi_next_ring_desc(vq_info, acct->desc,
> + acct->resp_desc, &(acct->resp_idx));
>
> if (write_mem(acct->resp_desc->addr, mech_status_header,
> sizeof(struct scsi_mechanism_status_header))) {
> @@ -1221,9 +1266,9 @@ vioscsi_handle_mechanism_status(struct vioscsi_dev *de
> __func__, acct->resp_desc->addr);
> } else {
> ret = 1;
> - dev->cfg.isr_status = 1;
> + dev->isr = 1;
> /* Move ring indexes */
> - vioscsi_next_ring_item(dev, acct->avail, acct->used,
> + vioscsi_next_ring_item(vq_info, acct->avail, acct->used,
> acct->req_desc, acct->req_idx);
> }
>
> @@ -1234,8 +1279,9 @@ mech_out:
> }
>
> static int
> -vioscsi_handle_read_toc(struct vioscsi_dev *dev,
> - struct virtio_scsi_req_hdr *req, struct virtio_vq_acct *acct)
> +vioscsi_handle_read_toc(struct virtio_dev *dev,
> + struct virtio_vq_info *vq_info, struct virtio_scsi_req_hdr *req,
> + struct virtio_vq_acct *acct)
> {
> int ret = 0;
> struct virtio_scsi_res_hdr resp;
> @@ -1243,7 +1289,12 @@ vioscsi_handle_read_toc(struct vioscsi_dev *dev,
> uint8_t toc_data[TOC_DATA_SIZE];
> uint8_t *toc_data_p;
> struct scsi_read_toc *toc = (struct scsi_read_toc *)(req->cdb);
> + struct vioscsi_dev *vioscsi = NULL;
>
> + if (dev->device_id != PCI_PRODUCT_VIRTIO_SCSI)
> + fatalx("%s: virtio device is not a scsi device", __func__);
> + vioscsi = &dev->vioscsi;
> +
> DPRINTF("%s: %s - MSF %d Track 0x%02x Addr 0x%04x",
> __func__, vioscsi_op_names(toc->opcode), ((toc->byte2 >> 1) & 1),
> toc->from_track, _2btol(toc->data_len));
> @@ -1261,7 +1312,7 @@ vioscsi_handle_read_toc(struct vioscsi_dev *dev,
> SENSE_ILLEGAL_CDB_FIELD, SENSE_DEFAULT_ASCQ);
>
> /* Move index for response */
> - acct->resp_desc = vioscsi_next_ring_desc(acct->desc,
> + acct->resp_desc = vioscsi_next_ring_desc(vq_info, acct->desc,
> acct->req_desc, &(acct->resp_idx));
>
> if (write_mem(acct->resp_desc->addr, &resp, sizeof(resp))) {
> @@ -1271,9 +1322,9 @@ vioscsi_handle_read_toc(struct vioscsi_dev *dev,
> }
>
> ret = 1;
> - dev->cfg.isr_status = 1;
> + dev->isr = 1;
> /* Move ring indexes */
> - vioscsi_next_ring_item(dev, acct->avail, acct->used,
> + vioscsi_next_ring_item(vq_info, acct->avail, acct->used,
> acct->req_desc, acct->req_idx);
>
> goto read_toc_out;
> @@ -1322,7 +1373,7 @@ vioscsi_handle_read_toc(struct vioscsi_dev *dev,
> *toc_data_p++ = READ_TOC_LEAD_OUT_TRACK;
> *toc_data_p++ = 0x0;
>
> - _lto4b((uint32_t)dev->n_blocks, toc_data_p);
> + _lto4b((uint32_t)vioscsi->n_blocks, toc_data_p);
> toc_data_p += 4;
>
> toc_data_len = toc_data_p - toc_data;
> @@ -1332,8 +1383,8 @@ vioscsi_handle_read_toc(struct vioscsi_dev *dev,
> vioscsi_prepare_resp(&resp, VIRTIO_SCSI_S_OK, SCSI_OK, 0, 0, 0);
>
> /* Move index for response */
> - acct->resp_desc = vioscsi_next_ring_desc(acct->desc, acct->req_desc,
> - &(acct->resp_idx));
> + acct->resp_desc = vioscsi_next_ring_desc(vq_info, acct->desc,
> + acct->req_desc, &(acct->resp_idx));
>
> DPRINTF("%s: writing resp to 0x%llx size %d at local "
> "idx %d req_idx %d global_idx %d",
> @@ -1347,8 +1398,8 @@ vioscsi_handle_read_toc(struct vioscsi_dev *dev,
> }
>
> /* Move index for toc descriptor */
> - acct->resp_desc = vioscsi_next_ring_desc(acct->desc, acct->resp_desc,
> - &(acct->resp_idx));
> + acct->resp_desc = vioscsi_next_ring_desc(vq_info, acct->desc,
> + acct->resp_desc, &(acct->resp_idx));
>
> DPRINTF("%s: writing toc_data to 0x%llx size %d at "
> "local idx %d req_idx %d global_idx %d",
> @@ -1360,9 +1411,9 @@ vioscsi_handle_read_toc(struct vioscsi_dev *dev,
> __func__, acct->resp_desc->addr);
> } else {
> ret = 1;
> - dev->cfg.isr_status = 1;
> + dev->isr = 1;
> /* Move ring indexes */
> - vioscsi_next_ring_item(dev, acct->avail, acct->used,
> + vioscsi_next_ring_item(vq_info, acct->avail, acct->used,
> acct->req_desc, acct->req_idx);
> }
>
> @@ -1371,8 +1422,9 @@ read_toc_out:
> }
>
> static int
> -vioscsi_handle_read_disc_info(struct vioscsi_dev *dev,
> - struct virtio_scsi_req_hdr *req, struct virtio_vq_acct *acct)
> +vioscsi_handle_read_disc_info(struct virtio_dev *dev,
> + struct virtio_vq_info *vq_info, struct virtio_scsi_req_hdr *req,
> + struct virtio_vq_acct *acct)
> {
> int ret = 0;
> struct virtio_scsi_res_hdr resp;
> @@ -1387,7 +1439,7 @@ vioscsi_handle_read_disc_info(struct vioscsi_dev *dev,
> SENSE_ILLEGAL_CDB_FIELD, SENSE_DEFAULT_ASCQ);
>
> /* Move index for response */
> - acct->resp_desc = vioscsi_next_ring_desc(acct->desc,
> + acct->resp_desc = vioscsi_next_ring_desc(vq_info, acct->desc,
> acct->req_desc, &(acct->resp_idx));
>
> if (write_mem(acct->resp_desc->addr, &resp, sizeof(resp))) {
> @@ -1395,9 +1447,9 @@ vioscsi_handle_read_disc_info(struct vioscsi_dev *dev,
> __func__, acct->resp_desc->addr);
> } else {
> ret = 1;
> - dev->cfg.isr_status = 1;
> + dev->isr = 1;
> /* Move ring indexes */
> - vioscsi_next_ring_item(dev, acct->avail, acct->used,
> + vioscsi_next_ring_item(vq_info, acct->avail, acct->used,
> acct->req_desc, acct->req_idx);
> }
>
> @@ -1405,8 +1457,9 @@ vioscsi_handle_read_disc_info(struct vioscsi_dev *dev,
> }
>
> static int
> -vioscsi_handle_gesn(struct vioscsi_dev *dev,
> - struct virtio_scsi_req_hdr *req, struct virtio_vq_acct *acct)
> +vioscsi_handle_gesn(struct virtio_dev *dev,
> + struct virtio_vq_info *vq_info, struct virtio_scsi_req_hdr *req,
> + struct virtio_vq_acct *acct)
> {
> int ret = 0;
> struct virtio_scsi_res_hdr resp;
> @@ -1414,7 +1467,12 @@ vioscsi_handle_gesn(struct vioscsi_dev *dev,
> struct scsi_gesn *gesn;
> struct scsi_gesn_event_header *gesn_event_header;
> struct scsi_gesn_power_event *gesn_power_event;
> + struct vioscsi_dev *vioscsi = NULL;
>
> + if (dev->device_id != PCI_PRODUCT_VIRTIO_SCSI)
> + fatalx("%s: virtio device is not a scsi device", __func__);
> + vioscsi = &dev->vioscsi;
> +
> memset(&resp, 0, sizeof(resp));
> gesn = (struct scsi_gesn *)(req->cdb);
> DPRINTF("%s: GESN Method %s", __func__,
> @@ -1427,7 +1485,7 @@ vioscsi_handle_gesn(struct vioscsi_dev *dev,
> SENSE_ILLEGAL_CDB_FIELD, SENSE_DEFAULT_ASCQ);
>
> /* Move index for response */
> - acct->resp_desc = vioscsi_next_ring_desc(acct->desc,
> + acct->resp_desc = vioscsi_next_ring_desc(vq_info, acct->desc,
> acct->req_desc, &(acct->resp_idx));
>
> if (write_mem(acct->resp_desc->addr, &resp, sizeof(resp))) {
> @@ -1437,9 +1495,9 @@ vioscsi_handle_gesn(struct vioscsi_dev *dev,
> }
>
> ret = 1;
> - dev->cfg.isr_status = 1;
> + dev->isr = 1;
> /* Move ring indexes */
> - vioscsi_next_ring_item(dev, acct->avail, acct->used,
> + vioscsi_next_ring_item(vq_info, acct->avail, acct->used,
> acct->req_desc, acct->req_idx);
>
> goto gesn_out;
> @@ -1454,14 +1512,14 @@ vioscsi_handle_gesn(struct vioscsi_dev *dev,
>
> /* set event descriptor */
> gesn_power_event->event_code = GESN_CODE_NOCHG;
> - if (dev->locked)
> + if (vioscsi->locked)
> gesn_power_event->status = GESN_STATUS_ACTIVE;
> else
> gesn_power_event->status = GESN_STATUS_IDLE;
>
> /* Move index for response */
> - acct->resp_desc = vioscsi_next_ring_desc(acct->desc, acct->req_desc,
> - &(acct->resp_idx));
> + acct->resp_desc = vioscsi_next_ring_desc(vq_info, acct->desc,
> + acct->req_desc, &(acct->resp_idx));
>
> DPRINTF("%s: writing resp to 0x%llx size %d at local "
> "idx %d req_idx %d global_idx %d",
> @@ -1475,8 +1533,8 @@ vioscsi_handle_gesn(struct vioscsi_dev *dev,
> }
>
> /* Move index for gesn_reply */
> - acct->resp_desc = vioscsi_next_ring_desc(acct->desc, acct->resp_desc,
> - &(acct->resp_idx));
> + acct->resp_desc = vioscsi_next_ring_desc(vq_info, acct->desc,
> + acct->resp_desc, &(acct->resp_idx));
>
> DPRINTF("%s: writing gesn_reply to 0x%llx size %d at "
> "local idx %d req_idx %d global_idx %d",
> @@ -1489,9 +1547,9 @@ vioscsi_handle_gesn(struct vioscsi_dev *dev,
> __func__, acct->resp_desc->addr);
> } else {
> ret = 1;
> - dev->cfg.isr_status = 1;
> + dev->isr = 1;
> /* Move ring indexes */
> - vioscsi_next_ring_item(dev, acct->avail, acct->used,
> + vioscsi_next_ring_item(vq_info, acct->avail, acct->used,
> acct->req_desc, acct->req_idx);
> }
>
> @@ -1500,8 +1558,9 @@ gesn_out:
> }
>
> static int
> -vioscsi_handle_get_config(struct vioscsi_dev *dev,
> - struct virtio_scsi_req_hdr *req, struct virtio_vq_acct *acct)
> +vioscsi_handle_get_config(struct virtio_dev *dev,
> + struct virtio_vq_info *vq_info, struct virtio_scsi_req_hdr *req,
> + struct virtio_vq_acct *acct)
> {
> int ret = 0;
> struct virtio_scsi_res_hdr resp;
> @@ -1513,6 +1572,7 @@ vioscsi_handle_get_config(struct vioscsi_dev *dev,
> struct scsi_config_morphing_descriptor *config_morphing_desc;
> struct scsi_config_remove_media_descriptor *config_remove_media_desc;
> struct scsi_config_random_read_descriptor *config_random_read_desc;
> + struct vioscsi_dev *vioscsi = NULL;
>
> #if DEBUG
> struct scsi_get_configuration *get_configuration =
> @@ -1522,6 +1582,10 @@ vioscsi_handle_get_config(struct vioscsi_dev *dev,
> _2btol(get_configuration->length));
> #endif /* DEBUG */
>
> + if (dev->device_id != PCI_PRODUCT_VIRTIO_SCSI)
> + fatalx("%s: virtio device is not a scsi device", __func__);
> + vioscsi = &dev->vioscsi;
> +
> get_conf_reply = (uint8_t*)calloc(G_CONFIG_REPLY_SIZE, sizeof(uint8_t));
>
> if (get_conf_reply == NULL)
> @@ -1592,10 +1656,11 @@ vioscsi_handle_get_config(struct vioscsi_dev *dev,
> config_random_read_desc->feature_code);
> config_random_read_desc->byte3 = CONFIG_RANDOM_READ_BYTE3;
> config_random_read_desc->length = CONFIG_RANDOM_READ_LENGTH;
> - if (dev->n_blocks >= UINT32_MAX)
> + if (vioscsi->n_blocks >= UINT32_MAX)
> _lto4b(UINT32_MAX, config_random_read_desc->block_size);
> else
> - _lto4b(dev->n_blocks - 1, config_random_read_desc->block_size);
> + _lto4b(vioscsi->n_blocks - 1,
> + config_random_read_desc->block_size);
> _lto2b(CONFIG_RANDOM_READ_BLOCKING_TYPE,
> config_random_read_desc->blocking_type);
>
> @@ -1603,7 +1668,7 @@ vioscsi_handle_get_config(struct vioscsi_dev *dev,
> vioscsi_prepare_resp(&resp, VIRTIO_SCSI_S_OK, SCSI_OK, 0, 0, 0);
>
> /* Move index for response */
> - acct->resp_desc = vioscsi_next_ring_desc(acct->desc,
> + acct->resp_desc = vioscsi_next_ring_desc(vq_info, acct->desc,
> acct->req_desc, &(acct->resp_idx));
>
> DPRINTF("%s: writing resp to 0x%llx size %d at local "
> @@ -1618,8 +1683,8 @@ vioscsi_handle_get_config(struct vioscsi_dev *dev,
> }
>
> /* Move index for get_conf_reply */
> - acct->resp_desc = vioscsi_next_ring_desc(acct->desc, acct->resp_desc,
> - &(acct->resp_idx));
> + acct->resp_desc = vioscsi_next_ring_desc(vq_info, acct->desc,
> + acct->resp_desc, &(acct->resp_idx));
>
> DPRINTF("%s: writing get_conf_reply to 0x%llx size %d "
> "at local idx %d req_idx %d global_idx %d",
> @@ -1633,9 +1698,9 @@ vioscsi_handle_get_config(struct vioscsi_dev *dev,
> __func__, acct->resp_desc->addr);
> } else {
> ret = 1;
> - dev->cfg.isr_status = 1;
> + dev->isr = 1;
> /* Move ring indexes */
> - vioscsi_next_ring_item(dev, acct->avail, acct->used,
> + vioscsi_next_ring_item(vq_info, acct->avail, acct->used,
> acct->req_desc, acct->req_idx);
> }
>
> @@ -1649,421 +1714,111 @@ int
> vioscsi_io(int dir, uint16_t reg, uint32_t *data, uint8_t *intr,
> void *cookie, uint8_t sz)
> {
> - struct vioscsi_dev *dev = (struct vioscsi_dev *)cookie;
> + struct virtio_dev *dev = (struct virtio_dev *)cookie;
> + struct vioscsi_dev *vioscsi = NULL;
>
> + if (dev->device_id != PCI_PRODUCT_VIRTIO_SCSI)
> + fatalx("%s: virtio device is not a scsi device", __func__);
> + vioscsi = &dev->vioscsi;
> +
> *intr = 0xFF;
>
> - DPRINTF("%s: request %s reg %u, %s sz %u", __func__,
> - dir ? "READ" : "WRITE", reg, vioscsi_reg_name(reg), sz);
> + DPRINTF("%s: request %s reg %s sz %u", __func__,
> + dir ? "READ" : "WRITE", vioscsi_reg_name(reg), sz);
>
> - if (dir == 0) {
> + if (dir == VEI_DIR_OUT) {
> switch (reg) {
> - case VIRTIO_CONFIG_DEVICE_FEATURES:
> - case VIRTIO_CONFIG_QUEUE_SIZE:
> - case VIRTIO_CONFIG_ISR_STATUS:
> - log_warnx("%s: illegal write %x to %s",
> - __progname, *data, vioscsi_reg_name(reg));
> + case VIRTIO_SCSI_CONFIG_SENSE_SIZE:
> + /* XXX Support writing to sense size register. */
> + if (*data != VIOSCSI_SENSE_LEN)
> + log_warnx("%s: guest write to sense size "
> + "register ignored", __func__);
> break;
> - case VIRTIO_CONFIG_GUEST_FEATURES:
> - dev->cfg.guest_feature = *data;
> - DPRINTF("%s: guest feature set to %u",
> - __func__, dev->cfg.guest_feature);
> + case VIRTIO_SCSI_CONFIG_CDB_SIZE:
> + /* XXX Support writing CDB size. */
> + if (*data != VIOSCSI_CDB_LEN)
> + log_warnx("%s: guest write to cdb size "
> + "register ignored", __func__);
> break;
> - case VIRTIO_CONFIG_QUEUE_PFN:
> - dev->cfg.queue_pfn = *data;
> - vioscsi_update_qa(dev);
> - break;
> - case VIRTIO_CONFIG_QUEUE_SELECT:
> - dev->cfg.queue_select = *data;
> - vioscsi_update_qs(dev);
> - break;
> - case VIRTIO_CONFIG_QUEUE_NOTIFY:
> - dev->cfg.queue_notify = *data;
> - if (vioscsi_notifyq(dev))
> - *intr = 1;
> - break;
> - case VIRTIO_CONFIG_DEVICE_STATUS:
> - dev->cfg.device_status = *data;
> - DPRINTF("%s: device status set to %u",
> - __func__, dev->cfg.device_status);
> - if (dev->cfg.device_status == 0) {
> - log_debug("%s: device reset", __func__);
> - dev->cfg.guest_feature = 0;
> - dev->cfg.queue_pfn = 0;
> - vioscsi_update_qa(dev);
> - dev->cfg.queue_size = 0;
> - vioscsi_update_qs(dev);
> - dev->cfg.queue_select = 0;
> - dev->cfg.queue_notify = 0;
> - dev->cfg.isr_status = 0;
> - dev->vq[0].last_avail = 0;
> - dev->vq[1].last_avail = 0;
> - dev->vq[2].last_avail = 0;
> - }
> - break;
> default:
> + log_warnx("%s: invalid register 0x%04x", __func__, reg);
> break;
> }
> } else {
> switch (reg) {
> - case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI:
> - /* VIRTIO_SCSI_CONFIG_NUM_QUEUES, 32bit */
> - if (sz == 4)
> - *data = (uint32_t)VIOSCSI_NUM_QUEUES;
> - else if (sz == 1) {
> - /* read first byte of num_queues */
> - *data &= 0xFFFFFF00;
> - *data |= (uint32_t)(VIOSCSI_NUM_QUEUES) & 0xFF;
> - }
> + case VIRTIO_SCSI_CONFIG_NUM_QUEUES:
> + /* Number of request queues, not number of all queues. */
> + if (sz == sizeof(uint32_t))
> + *data = (uint32_t)(VIOSCSI_REQUEST_QUEUES);
> + else
> + log_warnx("%s: unaligned read of num queues "
> + "register", __func__);
> break;
> - case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 1:
> - if (sz == 1) {
> - /* read second byte of num_queues */
> - *data &= 0xFFFFFF00;
> - *data |=
> - (uint32_t)(VIOSCSI_NUM_QUEUES >> 8) & 0xFF;
> - }
> - break;
> - case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 2:
> - if (sz == 1) {
> - /* read third byte of num_queues */
> - *data &= 0xFFFFFF00;
> - *data |=
> - (uint32_t)(VIOSCSI_NUM_QUEUES >> 16) & 0xFF;
> - }
> - break;
> - case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 3:
> - if (sz == 1) {
> - /* read fourth byte of num_queues */
> - *data &= 0xFFFFFF00;
> - *data |=
> - (uint32_t)(VIOSCSI_NUM_QUEUES >> 24) & 0xFF;
> - }
> - break;
> - case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 4:
> - /* VIRTIO_SCSI_CONFIG_SEG_MAX, 32bit */
> - if (sz == 4)
> + case VIRTIO_SCSI_CONFIG_SEG_MAX:
> + if (sz == sizeof(uint32_t))
> *data = (uint32_t)(VIOSCSI_SEG_MAX);
> - else if (sz == 1) {
> - /* read first byte of seg_max */
> - *data &= 0xFFFFFF00;
> - *data |= (uint32_t)(VIOSCSI_SEG_MAX) & 0xFF;
> - }
> + else
> + log_warnx("%s: unaligned read of seg max "
> + "register", __func__);
> break;
> - case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 5:
> - if (sz == 1) {
> - /* read second byte of seg_max */
> - *data &= 0xFFFFFF00;
> - *data |=
> - (uint32_t)(VIOSCSI_SEG_MAX >> 8) & 0xFF;
> - }
> + case VIRTIO_SCSI_CONFIG_MAX_SECTORS:
> + if (sz == sizeof(uint32_t))
> + *data = (uint32_t)(vioscsi->max_xfer);
> + else
> + log_warnx("%s: unaligned read of max sectors "
> + "register", __func__);
> break;
> - case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 6:
> - if (sz == 1) {
> - /* read third byte of seg_max */
> - *data &= 0xFFFFFF00;
> - *data |=
> - (uint32_t)(VIOSCSI_SEG_MAX >> 16) & 0xFF;
> - }
> - break;
> - case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 7:
> - if (sz == 1) {
> - /* read fourth byte of seg_max */
> - *data &= 0xFFFFFF00;
> - *data |=
> - (uint32_t)(VIOSCSI_SEG_MAX >> 24) & 0xFF;
> - }
> - break;
> - case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 8:
> - /* VIRTIO_SCSI_CONFIG_MAX_SECTORS, 32bit */
> - if (sz == 4)
> - *data = (uint32_t)(dev->max_xfer);
> - else if (sz == 1) {
> - /* read first byte of max_xfer */
> - *data &= 0xFFFFFF00;
> - *data |= (uint32_t)(dev->max_xfer) & 0xFF;
> - }
> - break;
> - case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 9:
> - if (sz == 1) {
> - /* read second byte of max_xfer */
> - *data &= 0xFFFFFF00;
> - *data |=
> - (uint32_t)(dev->max_xfer >> 8) & 0xFF;
> - }
> - break;
> - case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 10:
> - if (sz == 1) {
> - /* read third byte of max_xfer */
> - *data &= 0xFFFFFF00;
> - *data |=
> - (uint32_t)(dev->max_xfer >> 16) & 0xFF;
> - }
> - break;
> - case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 11:
> - if (sz == 1) {
> - /* read fourth byte of max_xfer */
> - *data &= 0xFFFFFF00;
> - *data |=
> - (uint32_t)(dev->max_xfer >> 24) & 0xFF;
> - }
> - break;
> - case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 12:
> - /* VIRTIO_SCSI_CONFIG_CMD_PER_LUN, 32bit */
> - if (sz == 4)
> + case VIRTIO_SCSI_CONFIG_CMD_PER_LUN:
> + if (sz == sizeof(uint32_t))
> *data = (uint32_t)(VIOSCSI_CMD_PER_LUN);
> - else if (sz == 1) {
> - /* read first byte of cmd_per_lun */
> - *data &= 0xFFFFFF00;
> - *data |= (uint32_t)(VIOSCSI_CMD_PER_LUN) & 0xFF;
> - }
> + else
> + log_warnx("%s: unaligned read of cmd per lun "
> + "register", __func__);
> break;
> - case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 13:
> - if (sz == 1) {
> - /* read second byte of cmd_per_lun */
> - *data &= 0xFFFFFF00;
> - *data |=
> - (uint32_t)(VIOSCSI_CMD_PER_LUN >> 8) & 0xFF;
> - }
> + case VIRTIO_SCSI_CONFIG_EVENT_INFO_SIZE:
> + *data = 0;
> break;
> - case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 14:
> - if (sz == 1) {
> - /* read third byte of cmd_per_lun */
> - *data &= 0xFFFFFF00;
> - *data |= (uint32_t)(VIOSCSI_CMD_PER_LUN >> 16)
> - & 0xFF;
> - }
> - break;
> - case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 15:
> - if (sz == 1) {
> - /* read fourth byte of cmd_per_lun */
> - *data &= 0xFFFFFF00;
> - *data |= (uint32_t)(VIOSCSI_CMD_PER_LUN >> 24)
> - & 0xFF;
> - }
> - break;
> - case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 16:
> - /* VIRTIO_SCSI_CONFIG_EVENT_INFO_SIZE, 32bit */
> - *data = 0x00;
> - break;
> - case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 20:
> - /* VIRTIO_SCSI_CONFIG_SENSE_SIZE, 32bit */
> - if (sz == 4)
> + case VIRTIO_SCSI_CONFIG_SENSE_SIZE:
> + if (sz == sizeof(uint32_t))
> *data = (uint32_t)(VIOSCSI_SENSE_LEN);
> - else if (sz == 1) {
> - /* read first byte of sense_size */
> - *data &= 0xFFFFFF00;
> - *data |= (uint32_t)(VIOSCSI_SENSE_LEN) & 0xFF;
> - }
> + else
> + log_warnx("%s: unaligned read of sense size "
> + "register", __func__);
> break;
> - case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 21:
> - if (sz == 1) {
> - /* read second byte of sense_size */
> - *data &= 0xFFFFFF00;
> - *data |=
> - (uint32_t)(VIOSCSI_SENSE_LEN >> 8) & 0xFF;
> - }
> - break;
> - case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 22:
> - if (sz == 1) {
> - /* read third byte of sense_size */
> - *data &= 0xFFFFFF00;
> - *data |=
> - (uint32_t)(VIOSCSI_SENSE_LEN >> 16) & 0xFF;
> - }
> - break;
> - case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 23:
> - if (sz == 1) {
> - /* read fourth byte of sense_size */
> - *data &= 0xFFFFFF00;
> - *data |=
> - (uint32_t)(VIOSCSI_SENSE_LEN >> 24) & 0xFF;
> - }
> - break;
> - case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 24:
> - /* VIRTIO_SCSI_CONFIG_CDB_SIZE, 32bit */
> - if (sz == 4)
> + case VIRTIO_SCSI_CONFIG_CDB_SIZE:
> + if (sz == sizeof(uint32_t))
> *data = (uint32_t)(VIOSCSI_CDB_LEN);
> - else if (sz == 1) {
> - /* read first byte of cdb_len */
> - *data &= 0xFFFFFF00;
> - *data |= (uint32_t)(VIOSCSI_CDB_LEN) & 0xFF;
> - }
> + else
> + log_warnx("%s: unaligned read of cdb size "
> + "register", __func__);
> break;
> - case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 25:
> - if (sz == 1) {
> - /* read second byte of cdb_len */
> - *data &= 0xFFFFFF00;
> - *data |=
> - (uint32_t)(VIOSCSI_CDB_LEN >> 8) & 0xFF;
> - }
> - break;
> - case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 26:
> - if (sz == 1) {
> - /* read third byte of cdb_len */
> - *data &= 0xFFFFFF00;
> - *data |=
> - (uint32_t)(VIOSCSI_CDB_LEN >> 16) & 0xFF;
> - }
> - break;
> - case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 27:
> - if (sz == 1) {
> - /* read fourth byte of cdb_len */
> - *data &= 0xFFFFFF00;
> - *data |=
> - (uint32_t)(VIOSCSI_CDB_LEN >> 24) & 0xFF;
> - }
> - break;
> - case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 28:
> - /* VIRTIO_SCSI_CONFIG_MAX_CHANNEL, 16bit */
> -
> + case VIRTIO_SCSI_CONFIG_MAX_CHANNEL:
> /* defined by standard to be zero */
> - *data &= 0xFFFF0000;
> + *data = 0;
> break;
> - case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 29:
> - /* defined by standard to be zero */
> - *data &= 0xFFFF0000;
> + case VIRTIO_SCSI_CONFIG_MAX_TARGET:
> + if (sz == sizeof(uint16_t))
> + *data = (uint32_t)(VIOSCSI_MAX_TARGET);
> + else
> + log_warnx("%s: unaligned read of max target "
> + "register", __func__);
> break;
> - case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 30:
> - /* VIRTIO_SCSI_CONFIG_MAX_TARGET, 16bit */
> - if (sz == 2) {
> - *data &= 0xFFFF0000;
> - *data |=
> - (uint32_t)(VIOSCSI_MAX_TARGET) & 0xFFFF;
> - } else if (sz == 1) {
> - /* read first byte of max_target */
> - *data &= 0xFFFFFF00;
> - *data |=
> - (uint32_t)(VIOSCSI_MAX_TARGET) & 0xFF;
> - }
> - break;
> - case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 31:
> - if (sz == 1) {
> - /* read second byte of max_target */
> - *data &= 0xFFFFFF00;
> - *data |=
> - (uint32_t)(VIOSCSI_MAX_TARGET >> 8) & 0xFF;
> - }
> - break;
> - case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 32:
> - /* VIRTIO_SCSI_CONFIG_MAX_LUN, 32bit */
> - if (sz == 4)
> + case VIRTIO_SCSI_CONFIG_MAX_LUN:
> + if (sz == sizeof(uint32_t))
> *data = (uint32_t)(VIOSCSI_MAX_LUN);
> - else if (sz == 1) {
> - /* read first byte of max_lun */
> - *data &= 0xFFFFFF00;
> - *data |= (uint32_t)(VIOSCSI_MAX_LUN) & 0xFF;
> - }
> + else
> + log_warnx("%s: unaligned read of max lun "
> + "register", __func__);
> break;
> - case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 33:
> - if (sz == 1) {
> - /* read second byte of max_lun */
> - *data &= 0xFFFFFF00;
> - *data |=
> - (uint32_t)(VIOSCSI_MAX_LUN >> 8) & 0xFF;
> - }
> - break;
> - case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 34:
> - if (sz == 1) {
> - /* read third byte of max_lun */
> - *data &= 0xFFFFFF00;
> - *data |=
> - (uint32_t)(VIOSCSI_MAX_LUN >> 16) & 0xFF;
> - }
> - break;
> - case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 35:
> - if (sz == 1) {
> - /* read fourth byte of max_lun */
> - *data &= 0xFFFFFF00;
> - *data |=
> - (uint32_t)(VIOSCSI_MAX_LUN >> 24) & 0xFF;
> - }
> - break;
> - case VIRTIO_CONFIG_DEVICE_FEATURES:
> - *data = dev->cfg.device_feature;
> - break;
> - case VIRTIO_CONFIG_GUEST_FEATURES:
> - *data = dev->cfg.guest_feature;
> - break;
> - case VIRTIO_CONFIG_QUEUE_PFN:
> - *data = dev->cfg.queue_pfn;
> - break;
> - case VIRTIO_CONFIG_QUEUE_SIZE:
> - if (sz == 4)
> - *data = dev->cfg.queue_size;
> - else if (sz == 2) {
> - *data &= 0xFFFF0000;
> - *data |= (uint16_t)dev->cfg.queue_size;
> - } else if (sz == 1) {
> - *data &= 0xFFFFFF00;
> - *data |= (uint8_t)dev->cfg.queue_size;
> - }
> - break;
> - case VIRTIO_CONFIG_QUEUE_SELECT:
> - *data = dev->cfg.queue_select;
> - break;
> - case VIRTIO_CONFIG_QUEUE_NOTIFY:
> - *data = dev->cfg.queue_notify;
> - break;
> - case VIRTIO_CONFIG_DEVICE_STATUS:
> - if (sz == 4)
> - *data = dev->cfg.device_status;
> - else if (sz == 2) {
> - *data &= 0xFFFF0000;
> - *data |= (uint16_t)dev->cfg.device_status;
> - } else if (sz == 1) {
> - *data &= 0xFFFFFF00;
> - *data |= (uint8_t)dev->cfg.device_status;
> - }
> - break;
> - case VIRTIO_CONFIG_ISR_STATUS:
> - *data = dev->cfg.isr_status;
> - dev->cfg.isr_status = 0;
> - break;
> + default:
> + log_warnx("%s: invalid register 0x%04x", __func__, reg);
> + *data = (uint32_t)(-1);
> }
> }
>
> -
> return (0);
> }
>
> -void
> -vioscsi_update_qs(struct vioscsi_dev *dev)
> -{
> - struct virtio_vq_info *vq_info;
> -
> - /* Invalid queue? */
> - if (dev->cfg.queue_select >= VIRTIO_MAX_QUEUES) {
> - dev->cfg.queue_size = 0;
> - return;
> - }
> -
> - vq_info = &dev->vq[dev->cfg.queue_select];
> -
> - /* Update queue pfn/size based on queue select */
> - dev->cfg.queue_pfn = vq_info->q_gpa >> 12;
> - dev->cfg.queue_size = vq_info->qs;
> -}
> -
> -void
> -vioscsi_update_qa(struct vioscsi_dev *dev)
> -{
> - struct virtio_vq_info *vq_info;
> - void *hva = NULL;
> -
> - /* Invalid queue? */
> - if (dev->cfg.queue_select >= VIRTIO_MAX_QUEUES)
> - return;
> -
> - vq_info = &dev->vq[dev->cfg.queue_select];
> - vq_info->q_gpa = (uint64_t)dev->cfg.queue_pfn * VIRTIO_PAGE_SIZE;
> -
> - hva = hvaddr_mem(vq_info->q_gpa, vring_size(VIOSCSI_QUEUE_SIZE));
> - if (hva == NULL)
> - fatal("vioscsi_update_qa");
> - vq_info->q_hva = hva;
> -}
> -
> /*
> * Process message(s) in the queue(s)
> * vioscsi driver will be placing the following in the queue for each iteration
> @@ -2075,22 +1830,17 @@ vioscsi_update_qa(struct vioscsi_dev *dev)
> * 0 otherwise
> */
> int
> -vioscsi_notifyq(struct vioscsi_dev *dev)
> +vioscsi_notifyq(struct virtio_dev *dev, uint16_t vq_idx)
> {
> - int cnt, ret = 0;
> + size_t cnt;
> + int ret = 0;
> char *vr;
> struct virtio_scsi_req_hdr req;
> struct virtio_scsi_res_hdr resp;
> struct virtio_vq_acct acct;
> struct virtio_vq_info *vq_info;
>
> - ret = 0;
> -
> - /* Invalid queue? */
> - if (dev->cfg.queue_notify >= VIRTIO_MAX_QUEUES)
> - return (ret);
> -
> - vq_info = &dev->vq[dev->cfg.queue_notify];
> + vq_info = &dev->vq[vq_idx];
> vr = vq_info->q_hva;
> if (vr == NULL)
> fatalx("%s: null vring", __func__);
> @@ -2100,23 +1850,23 @@ vioscsi_notifyq(struct vioscsi_dev *dev)
> acct.avail = (struct vring_avail *)(vr + vq_info->vq_availoffset);
> acct.used = (struct vring_used *)(vr + vq_info->vq_usedoffset);
>
> - acct.idx = vq_info->last_avail & VIOSCSI_QUEUE_MASK;
> + acct.idx = vq_info->last_avail & vq_info->mask;
>
> - if ((acct.avail->idx & VIOSCSI_QUEUE_MASK) == acct.idx) {
> + if ((acct.avail->idx & vq_info->mask) == acct.idx) {
> log_debug("%s - nothing to do?", __func__);
> return (0);
> }
>
> cnt = 0;
> - while (acct.idx != (acct.avail->idx & VIOSCSI_QUEUE_MASK)) {
> + while (acct.idx != (acct.avail->idx & vq_info->mask)) {
>
> /* Guard against infinite descriptor chains */
> - if (++cnt >= VIOSCSI_QUEUE_SIZE) {
> + if (++cnt >= vq_info->qs) {
> log_warnx("%s: invalid descriptor table", __func__);
> goto out;
> }
>
> - acct.req_idx = acct.avail->ring[acct.idx] & VIOSCSI_QUEUE_MASK;
> + acct.req_idx = acct.avail->ring[acct.idx] & vq_info->mask;
> acct.req_desc = &(acct.desc[acct.req_idx]);
>
> /* Clear resp for next message */
> @@ -2155,8 +1905,8 @@ vioscsi_notifyq(struct vioscsi_dev *dev)
> __func__, req.cdb[0], vioscsi_op_names(req.cdb[0]),
> req.lun[0], req.lun[1], req.lun[2], req.lun[3]);
> /* Move index for response */
> - acct.resp_desc = vioscsi_next_ring_desc(acct.desc,
> - acct.req_desc, &(acct.resp_idx));
> + acct.resp_desc = vioscsi_next_ring_desc(vq_info,
> + acct.desc, acct.req_desc, &(acct.resp_idx));
>
> vioscsi_prepare_resp(&resp,
> VIRTIO_SCSI_S_BAD_TARGET, SCSI_OK, 0, 0, 0);
> @@ -2175,79 +1925,89 @@ vioscsi_notifyq(struct vioscsi_dev *dev)
> }
>
> ret = 1;
> - dev->cfg.isr_status = 1;
> + dev->isr = 1;
>
> /* Move ring indexes (updates the used ring index) */
> - vioscsi_next_ring_item(dev, acct.avail, acct.used,
> + vioscsi_next_ring_item(vq_info, acct.avail, acct.used,
> acct.req_desc, acct.req_idx);
> goto next_msg;
> }
>
> DPRINTF("%s: Queue %d id 0x%llx lun %u:%u:%u:%u"
> " cdb OP 0x%02x,%s",
> - __func__, dev->cfg.queue_notify, req.id,
> - req.lun[0], req.lun[1], req.lun[2], req.lun[3],
> - req.cdb[0], vioscsi_op_names(req.cdb[0]));
> + __func__, vq_idx, req.id, req.lun[0], req.lun[1],
> + req.lun[2], req.lun[3],req.cdb[0],
> + vioscsi_op_names(req.cdb[0]));
>
> /* opcode is first byte */
> switch (req.cdb[0]) {
> case TEST_UNIT_READY:
> case START_STOP:
> - ret = vioscsi_handle_tur(dev, &req, &acct);
> + ret = vioscsi_handle_tur(dev, vq_info, &req, &acct);
> break;
> case PREVENT_ALLOW:
> - ret = vioscsi_handle_prevent_allow(dev, &req, &acct);
> + ret = vioscsi_handle_prevent_allow(dev, vq_info, &req,
> + &acct);
> break;
> case READ_TOC:
> - ret = vioscsi_handle_read_toc(dev, &req, &acct);
> + ret = vioscsi_handle_read_toc(dev, vq_info, &req,
> + &acct);
> break;
> case READ_CAPACITY:
> - ret = vioscsi_handle_read_capacity(dev, &req, &acct);
> + ret = vioscsi_handle_read_capacity(dev, vq_info, &req,
> + &acct);
> break;
> case READ_CAPACITY_16:
> - ret = vioscsi_handle_read_capacity_16(dev, &req, &acct);
> + ret = vioscsi_handle_read_capacity_16(dev, vq_info,
> + &req, &acct);
> break;
> case READ_COMMAND:
> - ret = vioscsi_handle_read_6(dev, &req, &acct);
> + ret = vioscsi_handle_read_6(dev, vq_info, &req, &acct);
> break;
> case READ_10:
> - ret = vioscsi_handle_read_10(dev, &req, &acct);
> + ret = vioscsi_handle_read_10(dev, vq_info, &req, &acct);
> break;
> case INQUIRY:
> - ret = vioscsi_handle_inquiry(dev, &req, &acct);
> + ret = vioscsi_handle_inquiry(dev, vq_info, &req, &acct);
> break;
> case MODE_SENSE:
> - ret = vioscsi_handle_mode_sense(dev, &req, &acct);
> + ret = vioscsi_handle_mode_sense(dev, vq_info, &req,
> + &acct);
> break;
> case MODE_SENSE_BIG:
> - ret = vioscsi_handle_mode_sense_big(dev, &req, &acct);
> + ret = vioscsi_handle_mode_sense_big(dev, vq_info, &req,
> + &acct);
> break;
> case GET_EVENT_STATUS_NOTIFICATION:
> - ret = vioscsi_handle_gesn(dev, &req, &acct);
> + ret = vioscsi_handle_gesn(dev, vq_info, &req, &acct);
> break;
> case READ_DISC_INFORMATION:
> - ret = vioscsi_handle_read_disc_info(dev, &req, &acct);
> + ret = vioscsi_handle_read_disc_info(dev, vq_info, &req,
> + &acct);
> break;
> case GET_CONFIGURATION:
> - ret = vioscsi_handle_get_config(dev, &req, &acct);
> + ret = vioscsi_handle_get_config(dev, vq_info, &req,
> + &acct);
> break;
> case MECHANISM_STATUS:
> - ret = vioscsi_handle_mechanism_status(dev, &req, &acct);
> + ret = vioscsi_handle_mechanism_status(dev, vq_info,
> + &req, &acct);
> break;
> case REPORT_LUNS:
> - ret = vioscsi_handle_report_luns(dev, &req, &acct);
> + ret = vioscsi_handle_report_luns(dev, vq_info, &req,
> + &acct);
> break;
> default:
> log_warnx("%s: unsupported opcode 0x%02x,%s",
> __func__, req.cdb[0], vioscsi_op_names(req.cdb[0]));
> /* Move ring indexes */
> - vioscsi_next_ring_item(dev, acct.avail, acct.used,
> + vioscsi_next_ring_item(vq_info, acct.avail, acct.used,
> acct.req_desc, acct.req_idx);
> break;
> }
> next_msg:
> /* Increment to the next queue slot */
> - acct.idx = (acct.idx + 1) & VIOSCSI_QUEUE_MASK;
> + acct.idx = (acct.idx + 1) & vq_info->mask;
> }
> out:
> return (ret);
> blob - 561f287fcef819ccf99203916caf5c1e87960925
> blob + 99905bae1f501a28bdc9a5ab5f74c5cf71109c66
> --- usr.sbin/vmd/virtio.c
> +++ usr.sbin/vmd/virtio.c
> @@ -43,12 +43,22 @@
> #include "virtio.h"
> #include "vmd.h"
>
> +#define VIRTIO_DEBUG 0
> +#ifdef DPRINTF
> +#undef DPRINTF
> +#endif
> +#if VIRTIO_DEBUG
> +#define DPRINTF log_debug
> +#else
> +#define DPRINTF(x...) do {} while(0)
> +#endif /* VIRTIO_DEBUG */
> +
> extern struct vmd *env;
> extern char *__progname;
>
> -struct viornd_dev viornd;
> -struct vioscsi_dev *vioscsi;
> -struct vmmci_dev vmmci;
> +struct virtio_dev viornd;
> +struct virtio_dev *vioscsi = NULL;
> +struct virtio_dev vmmci;
>
> /* Devices emulated in subprocesses are inserted into this list. */
> SLIST_HEAD(virtio_dev_head, virtio_dev) virtio_devs;
> @@ -64,12 +74,53 @@ SLIST_HEAD(virtio_dev_head, virtio_dev) virtio_devs;
> #define RXQ 0
> #define TXQ 1
>
> +static void virtio_dev_init(struct virtio_dev *, uint8_t, uint16_t, uint16_t,
> + uint64_t, uint32_t);
> static int virtio_dev_launch(struct vmd_vm *, struct virtio_dev *);
> static void virtio_dispatch_dev(int, short, void *);
> static int handle_dev_msg(struct viodev_msg *, struct virtio_dev *);
> static int virtio_dev_closefds(struct virtio_dev *);
> +static void virtio_pci_add_cap(uint8_t, uint8_t, uint8_t, uint32_t);
> static void vmmci_pipe_dispatch(int, short, void *);
>
> +static int virtio_io_dispatch(int, uint16_t, uint32_t *, uint8_t *, void *,
> + uint8_t);
> +static int virtio_io_isr(int, uint16_t, uint32_t *, uint8_t *, void *, uint8_t);
> +static int virtio_io_notify(int, uint16_t, uint32_t *, uint8_t *, void *,
> + uint8_t);
> +static int viornd_notifyq(struct virtio_dev *, uint16_t);
> +
> +static void vmmci_ack(struct virtio_dev *, unsigned int);
> +
> +#if VIRTIO_DEBUG
> +static const char *
> +virtio1_reg_name(uint16_t reg)
> +{
> + switch (reg) {
> + case VIO1_PCI_DEVICE_FEATURE_SELECT: return "DEVICE_FEATURE_SELECT";
> + case VIO1_PCI_DEVICE_FEATURE: return "DEVICE_FEATURE";
> + case VIO1_PCI_DRIVER_FEATURE_SELECT: return "DRIVER_FEATURE_SELECT";
> + case VIO1_PCI_DRIVER_FEATURE: return "DRIVER_FEATURE";
> + case VIO1_PCI_CONFIG_MSIX_VECTOR: return "CONFIG_MSIX_VECTOR";
> + case VIO1_PCI_NUM_QUEUES: return "NUM_QUEUES";
> + case VIO1_PCI_DEVICE_STATUS: return "DEVICE_STATUS";
> + case VIO1_PCI_CONFIG_GENERATION: return "CONFIG_GENERATION";
> + case VIO1_PCI_QUEUE_SELECT: return "QUEUE_SELECT";
> + case VIO1_PCI_QUEUE_SIZE: return "QUEUE_SIZE";
> + case VIO1_PCI_QUEUE_MSIX_VECTOR: return "QUEUE_MSIX_VECTOR";
> + case VIO1_PCI_QUEUE_ENABLE: return "QUEUE_ENABLE";
> + case VIO1_PCI_QUEUE_NOTIFY_OFF: return "QUEUE_NOTIFY_OFF";
> + case VIO1_PCI_QUEUE_DESC: return "QUEUE_DESC";
> + case VIO1_PCI_QUEUE_DESC + 4: return "QUEUE_DESC (HIGH)";
> + case VIO1_PCI_QUEUE_AVAIL: return "QUEUE_AVAIL";
> + case VIO1_PCI_QUEUE_AVAIL + 4: return "QUEUE_AVAIL (HIGH)";
> + case VIO1_PCI_QUEUE_USED: return "QUEUE_USED";
> + case VIO1_PCI_QUEUE_USED + 4: return "QUEUE_USED (HIGH)";
> + default: return "UNKNOWN";
> + }
> +}
> +#endif /* VIRTIO_DEBUG */
> +
> const char *
> virtio_reg_name(uint8_t reg)
> {
> @@ -111,62 +162,125 @@ vring_size(uint32_t vq_size)
>
> /* Update queue select */
> void
> -viornd_update_qs(void)
> +virtio_update_qs(struct virtio_dev *dev)
> {
> - struct virtio_vq_info *vq_info;
> + struct virtio_vq_info *vq_info = NULL;
>
> - /* Invalid queue? */
> - if (viornd.cfg.queue_select > 0) {
> - viornd.cfg.queue_size = 0;
> - return;
> + if (dev->driver_feature & VIRTIO_F_VERSION_1) {
> + /* Invalid queue */
> + if (dev->pci_cfg.queue_select >= dev->num_queues) {
> + dev->pci_cfg.queue_size = 0;
> + dev->pci_cfg.queue_enable = 0;
> + return;
> + }
> + vq_info = &dev->vq[dev->pci_cfg.queue_select];
> + dev->pci_cfg.queue_size = vq_info->qs;
> + dev->pci_cfg.queue_desc = vq_info->q_gpa;
> + dev->pci_cfg.queue_avail = vq_info->q_gpa + vq_info->vq_availoffset;
> + dev->pci_cfg.queue_used = vq_info->q_gpa + vq_info->vq_usedoffset;
> + dev->pci_cfg.queue_enable = vq_info->vq_enabled;
> + } else {
> + /* Invalid queue? */
> + if (dev->cfg.queue_select >= dev->num_queues) {
> + dev->cfg.queue_size = 0;
> + return;
> + }
> + vq_info = &dev->vq[dev->cfg.queue_select];
> + dev->cfg.queue_pfn = vq_info->q_gpa >> 12;
> + dev->cfg.queue_size = vq_info->qs;
> }
> -
> - vq_info = &viornd.vq[viornd.cfg.queue_select];
> -
> - /* Update queue pfn/size based on queue select */
> - viornd.cfg.queue_pfn = vq_info->q_gpa >> 12;
> - viornd.cfg.queue_size = vq_info->qs;
> }
>
> -/* Update queue address */
> +/* Update queue address. */
> void
> -viornd_update_qa(void)
> +virtio_update_qa(struct virtio_dev *dev)
> {
> - struct virtio_vq_info *vq_info;
> + struct virtio_vq_info *vq_info = NULL;
> void *hva = NULL;
>
> - /* Invalid queue? */
> - if (viornd.cfg.queue_select > 0)
> - return;
> + if (dev->driver_feature & VIRTIO_F_VERSION_1) {
> + if (dev->pci_cfg.queue_select >= dev->num_queues) {
> + log_warnx("%s: invalid queue index", __func__);
> + return;
> + }
> + vq_info = &dev->vq[dev->pci_cfg.queue_select];
> + vq_info->q_gpa = dev->pci_cfg.queue_desc;
>
> - vq_info = &viornd.vq[viornd.cfg.queue_select];
> - vq_info->q_gpa = (uint64_t)viornd.cfg.queue_pfn * VIRTIO_PAGE_SIZE;
> + /*
> + * Queue size is adjustable by the guest in Virtio 1.x.
> + * We validate the max size at time of write and not here.
> + */
> + vq_info->qs = dev->pci_cfg.queue_size;
> + vq_info->mask = vq_info->qs - 1;
>
> - hva = hvaddr_mem(vq_info->q_gpa, vring_size(VIORND_QUEUE_SIZE));
> - if (hva == NULL)
> - fatalx("viornd_update_qa");
> - vq_info->q_hva = hva;
> + if (vq_info->qs > 0 && vq_info->qs % 2 == 0) {
> + vq_info->vq_availoffset = dev->pci_cfg.queue_avail -
> + dev->pci_cfg.queue_desc;
> + vq_info->vq_usedoffset = dev->pci_cfg.queue_used -
> + dev->pci_cfg.queue_desc;
> + vq_info->vq_enabled = (dev->pci_cfg.queue_enable == 1);
> + } else {
> + vq_info->vq_availoffset = 0;
> + vq_info->vq_usedoffset = 0;
> + vq_info->vq_enabled = 0;
> + }
> + } else {
> + /* Invalid queue? */
> + if (dev->cfg.queue_select >= dev->num_queues) {
> + log_warnx("%s: invalid queue index", __func__);
> + return;
> + }
> + vq_info = &dev->vq[dev->cfg.queue_select];
> + vq_info->q_gpa = (uint64_t)dev->cfg.queue_pfn *
> + VIRTIO_PAGE_SIZE;
> +
> + /* Queue size is immutable in Virtio 0.9. */
> + vq_info->vq_availoffset = sizeof(struct vring_desc) *
> + vq_info->qs;
> + vq_info->vq_usedoffset = VIRTQUEUE_ALIGN(
> + sizeof(struct vring_desc) * vq_info->qs +
> + sizeof(uint16_t) * (2 + vq_info->qs));
> + }
> +
> + /* Update any host va mappings. */
> + if (vq_info->q_gpa > 0) {
> + hva = hvaddr_mem(vq_info->q_gpa, vring_size(vq_info->qs));
> + if (hva == NULL)
> + fatalx("%s: failed to translate gpa to hva", __func__);
> + vq_info->q_hva = hva;
> + } else {
> + vq_info->q_hva = NULL;
> + vq_info->last_avail = 0;
> + vq_info->notified_avail = 0;
> + }
> }
>
> -int
> -viornd_notifyq(void)
> +static int
> +viornd_notifyq(struct virtio_dev *dev, uint16_t idx)
> {
> size_t sz;
> - int dxx, ret;
> + int dxx, ret = 0;
> uint16_t aidx, uidx;
> char *vr, *rnd_data;
> - struct vring_desc *desc;
> - struct vring_avail *avail;
> - struct vring_used *used;
> - struct virtio_vq_info *vq_info;
> + struct vring_desc *desc = NULL;
> + struct vring_avail *avail = NULL;
> + struct vring_used *used = NULL;
> + struct virtio_vq_info *vq_info = NULL;
>
> - ret = 0;
> + if (dev->device_id != PCI_PRODUCT_VIRTIO_ENTROPY)
> + fatalx("%s: device is not an entropy device", __func__);
>
> - /* Invalid queue? */
> - if (viornd.cfg.queue_notify > 0)
> + if (idx >= dev->num_queues) {
> + log_warnx("%s: invalid virtqueue index", __func__);
> return (0);
> + }
> + vq_info = &dev->vq[idx];
>
> - vq_info = &viornd.vq[viornd.cfg.queue_notify];
> + if (!vq_info->vq_enabled) {
> + log_warnx("%s: virtqueue not enabled", __func__);
> + return (0);
> + }
> +
> vr = vq_info->q_hva;
> if (vr == NULL)
> fatalx("%s: null vring", __func__);
> @@ -175,107 +289,425 @@ viornd_notifyq(void)
> avail = (struct vring_avail *)(vr + vq_info->vq_availoffset);
> used = (struct vring_used *)(vr + vq_info->vq_usedoffset);
>
> - aidx = avail->idx & VIORND_QUEUE_MASK;
> - uidx = used->idx & VIORND_QUEUE_MASK;
> + aidx = avail->idx & vq_info->mask;
> + uidx = used->idx & vq_info->mask;
>
> - dxx = avail->ring[aidx] & VIORND_QUEUE_MASK;
> + dxx = avail->ring[aidx] & vq_info->mask;
>
> sz = desc[dxx].len;
> if (sz > MAXPHYS)
> fatalx("viornd descriptor size too large (%zu)", sz);
>
> rnd_data = malloc(sz);
> + if (rnd_data == NULL)
> + fatal("memory allocaiton error for viornd data");
>
> - if (rnd_data != NULL) {
> - arc4random_buf(rnd_data, sz);
> - if (write_mem(desc[dxx].addr, rnd_data, sz)) {
> - log_warnx("viornd: can't write random data @ "
> - "0x%llx",
> - desc[dxx].addr);
> - } else {
> - /* ret == 1 -> interrupt needed */
> - /* XXX check VIRTIO_F_NO_INTR */
> - ret = 1;
> - viornd.cfg.isr_status = 1;
> - used->ring[uidx].id = dxx;
> - used->ring[uidx].len = sz;
> - __sync_synchronize();
> - used->idx++;
> - }
> - free(rnd_data);
> - } else
> - fatal("memory allocation error for viornd data");
> + arc4random_buf(rnd_data, sz);
> + if (write_mem(desc[dxx].addr, rnd_data, sz)) {
> + log_warnx("viornd: can't write random data @ 0x%llx",
> + desc[dxx].addr);
> + } else {
> + /* ret == 1 -> interrupt needed */
> + /* XXX check VIRTIO_F_NO_INTR */
> + ret = 1;
> + viornd.isr = 1;
> + used->ring[uidx].id = dxx;
> + used->ring[uidx].len = sz;
> + __sync_synchronize();
> + used->idx++;
> + }
> + free(rnd_data);
>
> return (ret);
> }
>
> -int
> -virtio_rnd_io(int dir, uint16_t reg, uint32_t *data, uint8_t *intr,
> - void *unused, uint8_t sz)
> +static int
> +virtio_io_dispatch(int dir, uint16_t reg, uint32_t *data, uint8_t *intr,
> + void *arg, uint8_t sz)
> {
> - *intr = 0xFF;
> + struct virtio_dev *dev = (struct virtio_dev *)arg;
> + uint8_t actual = (uint8_t)reg;
>
> - if (dir == 0) {
> + switch (reg & 0xFF00) {
> + case VIO1_CFG_BAR_OFFSET:
> + *data = virtio_io_cfg(dev, dir, actual, *data, sz);
> + break;
> + case VIO1_DEV_BAR_OFFSET:
> + if (dev->device_id == PCI_PRODUCT_VIRTIO_SCSI)
> + return vioscsi_io(dir, actual, data, intr, arg, sz);
> + else if (dir == VEI_DIR_IN) {
> + log_debug("%s: no device specific handler", __func__);
> + *data = (uint32_t)(-1);
> + }
> + break;
> + case VIO1_NOTIFY_BAR_OFFSET:
> + return virtio_io_notify(dir, actual, data, intr, arg, sz);
> + case VIO1_ISR_BAR_OFFSET:
> + return virtio_io_isr(dir, actual, data, intr, arg, sz);
> + default:
> + log_debug("%s: no handler for reg 0x%04x", __func__, reg);
> + if (dir == VEI_DIR_IN)
> + *data = (uint32_t)(-1);
> + }
> + return (0);
> +}
> +
> +/*
> + * virtio 1.x PCI config register io. If a register is read, returns the value.
> + * Otherwise returns 0.
> + */
> +uint32_t
> +virtio_io_cfg(struct virtio_dev *dev, int dir, uint8_t reg, uint32_t data,
> + uint8_t sz)
> +{
> + struct virtio_pci_common_cfg *pci_cfg = &dev->pci_cfg;
> + uint32_t res = 0;
> + uint16_t i;
> +
> + if (dir == VEI_DIR_OUT) {
> switch (reg) {
> - case VIRTIO_CONFIG_DEVICE_FEATURES:
> - case VIRTIO_CONFIG_QUEUE_SIZE:
> - case VIRTIO_CONFIG_ISR_STATUS:
> - log_warnx("%s: illegal write %x to %s",
> - __progname, *data, virtio_reg_name(reg));
> + case VIO1_PCI_DEVICE_FEATURE_SELECT:
> + if (sz != 4)
> + log_warnx("%s: unaligned write to device "
> + "feature select (sz=%u)", __func__, sz);
> + else
> + pci_cfg->device_feature_select = data;
> break;
> - case VIRTIO_CONFIG_GUEST_FEATURES:
> - viornd.cfg.guest_feature = *data;
> + case VIO1_PCI_DEVICE_FEATURE:
> + log_warnx("%s: illegal write to device feature "
> + "register", __progname);
> break;
> - case VIRTIO_CONFIG_QUEUE_PFN:
> - viornd.cfg.queue_pfn = *data;
> - viornd_update_qa();
> + case VIO1_PCI_DRIVER_FEATURE_SELECT:
> + if (sz != 4)
> + log_warnx("%s: unaligned write to driver "
> + "feature select register (sz=%u)", __func__,
> + sz);
> + else
> + pci_cfg->driver_feature_select = data;
> break;
> - case VIRTIO_CONFIG_QUEUE_SELECT:
> - viornd.cfg.queue_select = *data;
> - viornd_update_qs();
> + case VIO1_PCI_DRIVER_FEATURE:
> + if (sz != 4) {
> + log_warnx("%s: unaligned write to driver "
> + "feature register (sz=%u)", __func__, sz);
> + break;
> + }
> + if (pci_cfg->driver_feature_select > 1) {
> + /* We only support a 64-bit feature space. */
> + DPRINTF("%s: ignoring driver feature write",
> + __func__);
> + break;
> + }
> + pci_cfg->driver_feature = data;
> + if (pci_cfg->driver_feature_select == 0)
> + dev->driver_feature |= pci_cfg->driver_feature;
> + else
> + dev->driver_feature |=
> + ((uint64_t)pci_cfg->driver_feature << 32);
> + dev->driver_feature &= dev->device_feature;
> + DPRINTF("%s: driver features 0x%llx", __func__,
> + dev->driver_feature);
> break;
> - case VIRTIO_CONFIG_QUEUE_NOTIFY:
> - viornd.cfg.queue_notify = *data;
> - if (viornd_notifyq())
> - *intr = 1;
> + case VIO1_PCI_CONFIG_MSIX_VECTOR:
> + /* Ignore until we support MSIX. */
> break;
> - case VIRTIO_CONFIG_DEVICE_STATUS:
> - viornd.cfg.device_status = *data;
> + case VIO1_PCI_NUM_QUEUES:
> + log_warnx("%s: illegal write to num queues register",
> + __progname);
> break;
> + case VIO1_PCI_DEVICE_STATUS:
> + if (sz != 1) {
> + log_warnx("%s: unaligned write to device "
> + "status register (sz=%u)", __func__, sz);
> + break;
> + }
> + dev->status = data;
> + if (dev->status == 0) {
> + /* Reset device and virtqueues (if any). */
> + dev->driver_feature = 0;
> + dev->isr = 0;
> +
> + pci_cfg->queue_select = 0;
> + virtio_update_qs(dev);
> +
> + if (dev->num_queues > 0) {
> + /*
> + * Reset virtqueues to initial state and
> + * set to disabled status. Clear PCI
> + * configuration registers.
> + */
> + for (i = 0; i < dev->num_queues; i++)
> + virtio_vq_init(dev, i);
> + }
> + }
> +
> + DPRINTF("%s: dev %u status [%s%s%s%s%s%s]", __func__,
> + dev->pci_id,
> + (data & VIRTIO_CONFIG_DEVICE_STATUS_ACK) ?
> + "[ack]" : "",
> + (data & VIRTIO_CONFIG_DEVICE_STATUS_DRIVER) ?
> + "[driver]" : "",
> + (data & VIRTIO_CONFIG_DEVICE_STATUS_DRIVER_OK) ?
> + "[driver ok]" : "",
> + (data & VIRTIO_CONFIG_DEVICE_STATUS_FEATURES_OK) ?
> + "[features ok]" : "",
> + (data &
> + VIRTIO_CONFIG_DEVICE_STATUS_DEVICE_NEEDS_RESET)
> + ? "[needs reset]" : "",
> + (data & VIRTIO_CONFIG_DEVICE_STATUS_FAILED) ?
> + "[failed]" : "");
> +
> + break;
> + case VIO1_PCI_CONFIG_GENERATION:
> + log_warnx("%s: illegal write to config generation "
> + "register", __progname);
> + break;
> + case VIO1_PCI_QUEUE_SELECT:
> + pci_cfg->queue_select = data;
> + virtio_update_qs(dev);
> + break;
> + case VIO1_PCI_QUEUE_SIZE:
> + if (data <= VIRTIO_QUEUE_SIZE_MAX)
> + pci_cfg->queue_size = data;
> + else {
> + log_warnx("%s: clamping queue size", __func__);
> + pci_cfg->queue_size = VIRTIO_QUEUE_SIZE_MAX;
> + }
> + virtio_update_qa(dev);
> + break;
> + case VIO1_PCI_QUEUE_MSIX_VECTOR:
> + /* Ignore until we support MSI-X. */
> + break;
> + case VIO1_PCI_QUEUE_ENABLE:
> + pci_cfg->queue_enable = data;
> + virtio_update_qa(dev);
> + break;
> + case VIO1_PCI_QUEUE_NOTIFY_OFF:
> + log_warnx("%s: illegal write to queue notify offset "
> + "register", __progname);
> + break;
> + case VIO1_PCI_QUEUE_DESC:
> + if (sz != 4) {
> + log_warnx("%s: unaligned write to queue "
> + "desc. register (sz=%u)", __func__, sz);
> + break;
> + }
> + pci_cfg->queue_desc &= 0xffffffff00000000;
> + pci_cfg->queue_desc |= (uint64_t)data;
> + virtio_update_qa(dev);
> + break;
> + case VIO1_PCI_QUEUE_DESC + 4:
> + if (sz != 4) {
> + log_warnx("%s: unaligned write to queue "
> + "desc. register (sz=%u)", __func__, sz);
> + break;
> + }
> + pci_cfg->queue_desc &= 0x00000000ffffffff;
> + pci_cfg->queue_desc |= ((uint64_t)data << 32);
> + virtio_update_qa(dev);
> + break;
> + case VIO1_PCI_QUEUE_AVAIL:
> + if (sz != 4) {
> + log_warnx("%s: unaligned write to queue "
> + "available register (sz=%u)", __func__, sz);
> + break;
> + }
> + pci_cfg->queue_avail &= 0xffffffff00000000;
> + pci_cfg->queue_avail |= (uint64_t)data;
> + virtio_update_qa(dev);
> + break;
> + case VIO1_PCI_QUEUE_AVAIL + 4:
> + if (sz != 4) {
> + log_warnx("%s: unaligned write to queue "
> + "available register (sz=%u)", __func__, sz);
> + break;
> + }
> + pci_cfg->queue_avail &= 0x00000000ffffffff;
> + pci_cfg->queue_avail |= ((uint64_t)data << 32);
> + virtio_update_qa(dev);
> + break;
> + case VIO1_PCI_QUEUE_USED:
> + if (sz != 4) {
> + log_warnx("%s: unaligned write to queue used "
> + "register (sz=%u)", __func__, sz);
> + break;
> + }
> + pci_cfg->queue_used &= 0xffffffff00000000;
> + pci_cfg->queue_used |= (uint64_t)data;
> + virtio_update_qa(dev);
> + break;
> + case VIO1_PCI_QUEUE_USED + 4:
> + if (sz != 4) {
> + log_warnx("%s: unaligned write to queue used "
> + "register (sz=%u)", __func__, sz);
> + break;
> + }
> + pci_cfg->queue_used &= 0x00000000ffffffff;
> + pci_cfg->queue_used |= ((uint64_t)data << 32);
> + virtio_update_qa(dev);
> + break;
> + default:
> + log_warnx("%s: invalid register 0x%04x", __func__, reg);
> }
> } else {
> switch (reg) {
> - case VIRTIO_CONFIG_DEVICE_FEATURES:
> - *data = viornd.cfg.device_feature;
> + case VIO1_PCI_DEVICE_FEATURE_SELECT:
> + res = pci_cfg->device_feature_select;
> break;
> - case VIRTIO_CONFIG_GUEST_FEATURES:
> - *data = viornd.cfg.guest_feature;
> + case VIO1_PCI_DEVICE_FEATURE:
> + if (pci_cfg->device_feature_select == 0)
> + res = dev->device_feature & (uint32_t)(-1);
> + else if (pci_cfg->device_feature_select == 1)
> + res = dev->device_feature >> 32;
> + else {
> + DPRINTF("%s: ignoring device feature read",
> + __func__);
> + }
> break;
> - case VIRTIO_CONFIG_QUEUE_PFN:
> - *data = viornd.cfg.queue_pfn;
> + case VIO1_PCI_DRIVER_FEATURE_SELECT:
> + res = pci_cfg->driver_feature_select;
> break;
> - case VIRTIO_CONFIG_QUEUE_SIZE:
> - *data = viornd.cfg.queue_size;
> + case VIO1_PCI_DRIVER_FEATURE:
> + if (pci_cfg->driver_feature_select == 0)
> + res = dev->driver_feature & (uint32_t)(-1);
> + else if (pci_cfg->driver_feature_select == 1)
> + res = dev->driver_feature >> 32;
> + else {
> + DPRINTF("%s: ignoring driver feature read",
> + __func__);
> + }
> break;
> - case VIRTIO_CONFIG_QUEUE_SELECT:
> - *data = viornd.cfg.queue_select;
> + case VIO1_PCI_CONFIG_MSIX_VECTOR:
> + res = VIRTIO_MSI_NO_VECTOR; /* Unsupported */
> break;
> - case VIRTIO_CONFIG_QUEUE_NOTIFY:
> - *data = viornd.cfg.queue_notify;
> + case VIO1_PCI_NUM_QUEUES:
> + res = dev->num_queues;
> break;
> - case VIRTIO_CONFIG_DEVICE_STATUS:
> - *data = viornd.cfg.device_status;
> + case VIO1_PCI_DEVICE_STATUS:
> + res = dev->status;
> break;
> - case VIRTIO_CONFIG_ISR_STATUS:
> - *data = viornd.cfg.isr_status;
> - viornd.cfg.isr_status = 0;
> - vcpu_deassert_irq(viornd.vm_id, 0, viornd.irq);
> + case VIO1_PCI_CONFIG_GENERATION:
> + res = pci_cfg->config_generation;
> break;
> + case VIO1_PCI_QUEUE_SELECT:
> + res = pci_cfg->queue_select;
> + break;
> + case VIO1_PCI_QUEUE_SIZE:
> + res = pci_cfg->queue_size;
> + break;
> + case VIO1_PCI_QUEUE_MSIX_VECTOR:
> + res = VIRTIO_MSI_NO_VECTOR; /* Unsupported */
> + break;
> + case VIO1_PCI_QUEUE_ENABLE:
> + res = pci_cfg->queue_enable;
> + break;
> + case VIO1_PCI_QUEUE_NOTIFY_OFF:
> + res = pci_cfg->queue_notify_off;
> + break;
> + case VIO1_PCI_QUEUE_DESC:
> + res = (uint32_t)(0xFFFFFFFF & pci_cfg->queue_desc);
> + break;
> + case VIO1_PCI_QUEUE_DESC + 4:
> + res = (uint32_t)(pci_cfg->queue_desc >> 32);
> + break;
> + case VIO1_PCI_QUEUE_AVAIL:
> + res = (uint32_t)(0xFFFFFFFF & pci_cfg->queue_avail);
> + break;
> + case VIO1_PCI_QUEUE_AVAIL + 4:
> + res = (uint32_t)(pci_cfg->queue_avail >> 32);
> + break;
> + case VIO1_PCI_QUEUE_USED:
> + res = (uint32_t)(0xFFFFFFFF & pci_cfg->queue_used);
> + break;
> + case VIO1_PCI_QUEUE_USED + 4:
> + res = (uint32_t)(pci_cfg->queue_used >> 32);
> + break;
> + default:
> + log_warnx("%s: invalid register 0x%04x", __func__, reg);
> }
> }
> +
> + DPRINTF("%s: dev=%u %s sz=%u dir=%s data=0x%04x", __func__, dev->pci_id,
> + virtio1_reg_name(reg), sz, (dir == VEI_DIR_OUT) ? "w" : "r",
> + (dir == VEI_DIR_OUT) ? data : res);
> +
> + return (res);
> +}
> +
> +static int
> +virtio_io_isr(int dir, uint16_t reg, uint32_t *data, uint8_t *intr,
> + void *arg, uint8_t sz)
> +{
> + struct virtio_dev *dev = (struct virtio_dev *)arg;
> + *intr = 0xFF;
> +
> + DPRINTF("%s: dev=%u, reg=0x%04x, sz=%u, dir=%s", __func__,
> + dev->pci_id, reg, sz,
> + (dir == VEI_DIR_OUT) ? "write" : "read");
> +
> + /* Limit to in-process devices. */
> + if (dev->device_id == PCI_PRODUCT_VIRTIO_BLOCK ||
> + dev->device_id == PCI_PRODUCT_VIRTIO_NETWORK)
> + fatalx("%s: cannot use on multi-process virtio dev", __func__);
> +
> + if (dir == VEI_DIR_IN) {
> + *data = dev->isr;
> + dev->isr = 0;
> + vcpu_deassert_irq(dev->vm_id, 0, dev->irq);
> + }
> +
> return (0);
> }
>
> +static int
> +virtio_io_notify(int dir, uint16_t reg, uint32_t *data, uint8_t *intr,
> + void *arg, uint8_t sz)
> +{
> + int raise_intr = 0;
> + struct virtio_dev *dev = (struct virtio_dev *)arg;
> + uint16_t vq_idx = (uint16_t)(0x0000ffff & *data);
> +
> + *intr = 0xFF;
> +
> + DPRINTF("%s: reg=0x%04x, sz=%u, vq_idx=%u, dir=%s", __func__, reg, sz,
> + vq_idx, (dir == VEI_DIR_OUT) ? "write" : "read");
> +
> + /* Limit this handler to in-process devices */
> + if (dev->device_id == PCI_PRODUCT_VIRTIO_BLOCK ||
> + dev->device_id == PCI_PRODUCT_VIRTIO_NETWORK)
> + fatalx("%s: cannot use on multi-process virtio dev", __func__);
> +
> + if (vq_idx >= dev->num_queues) {
> + log_warnx("%s: invalid virtqueue index %u", __func__, vq_idx);
> + return (0);
> + }
> +
> + if (dir == VEI_DIR_IN) {
> + /* Behavior is undefined. */
> + *data = (uint32_t)(-1);
> + return (0);
> + }
> +
> + switch (dev->device_id) {
> + case PCI_PRODUCT_VIRTIO_ENTROPY:
> + raise_intr = viornd_notifyq(dev, vq_idx);
> + break;
> + case PCI_PRODUCT_VIRTIO_SCSI:
> + raise_intr = vioscsi_notifyq(dev, vq_idx);
> + break;
> + case PCI_PRODUCT_VIRTIO_VMMCI:
> + /* Does not use a virtqueue. */
> + break;
> + default:
> + log_warnx("%s: invalid device type %u", __func__,
> + dev->device_id);
> + }
> +
> + if (raise_intr)
> + *intr = 1;
> +
> + return (0);
> +}
> +
> /*
> * vmmci_ctl
> *
> @@ -284,20 +716,24 @@ virtio_rnd_io(int dir, uint16_t reg, uint32_t *data, u
> * Called by the vm process's event(3) loop.
> */
> int
> -vmmci_ctl(unsigned int cmd)
> +vmmci_ctl(struct virtio_dev *dev, unsigned int cmd)
> {
> int ret = 0;
> struct timeval tv = { 0, 0 };
> + struct vmmci_dev *v = NULL;
>
> - mutex_lock(&vmmci.mutex);
> + if (dev->device_id != PCI_PRODUCT_VIRTIO_VMMCI)
> + fatalx("%s: device is not a vmmci device", __func__);
> + v = &dev->vmmci;
>
> - if ((vmmci.cfg.device_status &
> - VIRTIO_CONFIG_DEVICE_STATUS_DRIVER_OK) == 0) {
> + mutex_lock(&v->mutex);
> +
> + if ((dev->status & VIRTIO_CONFIG_DEVICE_STATUS_DRIVER_OK) == 0) {
> ret = -1;
> goto unlock;
> }
>
> - if (cmd == vmmci.cmd)
> + if (cmd == v->cmd)
> goto unlock;
>
> switch (cmd) {
> @@ -306,7 +742,7 @@ vmmci_ctl(unsigned int cmd)
> case VMMCI_SHUTDOWN:
> case VMMCI_REBOOT:
> /* Update command */
> - vmmci.cmd = cmd;
> + v->cmd = cmd;
>
> /*
> * vmm VMs do not support powerdown, send a reboot request
> @@ -316,20 +752,20 @@ vmmci_ctl(unsigned int cmd)
> cmd = VMMCI_REBOOT;
>
> /* Trigger interrupt */
> - vmmci.cfg.isr_status = VIRTIO_CONFIG_ISR_CONFIG_CHANGE;
> - vcpu_assert_irq(vmmci.vm_id, 0, vmmci.irq);
> + dev->isr = VIRTIO_CONFIG_ISR_CONFIG_CHANGE;
> + vcpu_assert_irq(dev->vm_id, 0, dev->irq);
>
> /* Add ACK timeout */
> tv.tv_sec = VMMCI_TIMEOUT_SHORT;
> - evtimer_add(&vmmci.timeout, &tv);
> + evtimer_add(&v->timeout, &tv);
> break;
> case VMMCI_SYNCRTC:
> if (vmmci.cfg.guest_feature & VMMCI_F_SYNCRTC) {
> /* RTC updated, request guest VM resync of its RTC */
> - vmmci.cmd = cmd;
> + v->cmd = cmd;
>
> - vmmci.cfg.isr_status = VIRTIO_CONFIG_ISR_CONFIG_CHANGE;
> - vcpu_assert_irq(vmmci.vm_id, 0, vmmci.irq);
> + dev->isr = VIRTIO_CONFIG_ISR_CONFIG_CHANGE;
> + vcpu_assert_irq(dev->vm_id, 0, dev->irq);
> } else {
> log_debug("%s: RTC sync skipped (guest does not "
> "support RTC sync)\n", __func__);
> @@ -340,7 +776,7 @@ vmmci_ctl(unsigned int cmd)
> }
>
> unlock:
> - mutex_unlock(&vmmci.mutex);
> + mutex_unlock(&v->mutex);
>
> return (ret);
> }
> @@ -352,9 +788,15 @@ unlock:
> *
> * Called by the vcpu thread. Must be called with the mutex held.
> */
> -void
> -vmmci_ack(unsigned int cmd)
> +static void
> +vmmci_ack(struct virtio_dev *dev, unsigned int cmd)
> {
> + struct vmmci_dev *v = NULL;
> +
> + if (dev->device_id != PCI_PRODUCT_VIRTIO_VMMCI)
> + fatalx("%s: device is not a vmmci device", __func__);
> + v = &dev->vmmci;
> +
> switch (cmd) {
> case VMMCI_NONE:
> break;
> @@ -365,10 +807,10 @@ vmmci_ack(unsigned int cmd)
> * timeout to give the VM a chance to reboot before the
> * timer is expired.
> */
> - if (vmmci.cmd == 0) {
> + if (v->cmd == 0) {
> log_debug("%s: vm %u requested shutdown", __func__,
> - vmmci.vm_id);
> - vm_pipe_send(&vmmci.dev_pipe, VMMCI_SET_TIMEOUT_SHORT);
> + dev->vm_id);
> + vm_pipe_send(&v->dev_pipe, VMMCI_SET_TIMEOUT_SHORT);
> return;
> }
> /* FALLTHROUGH */
> @@ -380,16 +822,16 @@ vmmci_ack(unsigned int cmd)
> * rc.shutdown on the VM), so increase the timeout before
> * killing it forcefully.
> */
> - if (cmd == vmmci.cmd) {
> + if (cmd == v->cmd) {
> log_debug("%s: vm %u acknowledged shutdown request",
> - __func__, vmmci.vm_id);
> - vm_pipe_send(&vmmci.dev_pipe, VMMCI_SET_TIMEOUT_LONG);
> + __func__, dev->vm_id);
> + vm_pipe_send(&v->dev_pipe, VMMCI_SET_TIMEOUT_LONG);
> }
> break;
> case VMMCI_SYNCRTC:
> log_debug("%s: vm %u acknowledged RTC sync request",
> - __func__, vmmci.vm_id);
> - vmmci.cmd = VMMCI_NONE;
> + __func__, dev->vm_id);
> + v->cmd = VMMCI_NONE;
> break;
> default:
> log_warnx("%s: illegal request %u", __func__, cmd);
> @@ -400,17 +842,32 @@ vmmci_ack(unsigned int cmd)
> void
> vmmci_timeout(int fd, short type, void *arg)
> {
> - log_debug("%s: vm %u shutdown", __progname, vmmci.vm_id);
> - vm_shutdown(vmmci.cmd == VMMCI_REBOOT ? VMMCI_REBOOT : VMMCI_SHUTDOWN);
> + struct virtio_dev *dev = (struct virtio_dev *)arg;
> + struct vmmci_dev *v = NULL;
> +
> + if (dev->device_id != PCI_PRODUCT_VIRTIO_VMMCI)
> + fatalx("%s: device is not a vmmci device", __func__);
> + v = &dev->vmmci;
> +
> + log_debug("%s: vm %u shutdown", __progname, dev->vm_id);
> + vm_shutdown(v->cmd == VMMCI_REBOOT ? VMMCI_REBOOT : VMMCI_SHUTDOWN);
> }
>
> int
> vmmci_io(int dir, uint16_t reg, uint32_t *data, uint8_t *intr,
> - void *unused, uint8_t sz)
> + void *arg, uint8_t sz)
> {
> + struct virtio_dev *dev = (struct virtio_dev *)arg;
> + struct vmmci_dev *v = NULL;
> +
> + if (dev->device_id != PCI_PRODUCT_VIRTIO_VMMCI)
> + fatalx("%s: device is not a vmmci device (%u)",
> + __func__, dev->device_id);
> + v = &dev->vmmci;
> +
> *intr = 0xFF;
>
> - mutex_lock(&vmmci.mutex);
> + mutex_lock(&v->mutex);
> if (dir == 0) {
> switch (reg) {
> case VIRTIO_CONFIG_DEVICE_FEATURES:
> @@ -420,72 +877,72 @@ vmmci_io(int dir, uint16_t reg, uint32_t *data, uint8_
> __progname, *data, virtio_reg_name(reg));
> break;
> case VIRTIO_CONFIG_GUEST_FEATURES:
> - vmmci.cfg.guest_feature = *data;
> + dev->cfg.guest_feature = *data;
> break;
> case VIRTIO_CONFIG_QUEUE_PFN:
> - vmmci.cfg.queue_pfn = *data;
> + dev->cfg.queue_pfn = *data;
> break;
> case VIRTIO_CONFIG_QUEUE_SELECT:
> - vmmci.cfg.queue_select = *data;
> + dev->cfg.queue_select = *data;
> break;
> case VIRTIO_CONFIG_QUEUE_NOTIFY:
> - vmmci.cfg.queue_notify = *data;
> + dev->cfg.queue_notify = *data;
> break;
> case VIRTIO_CONFIG_DEVICE_STATUS:
> - vmmci.cfg.device_status = *data;
> + dev->status = *data;
> break;
> case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI:
> - vmmci_ack(*data);
> + vmmci_ack(dev, *data);
> break;
> }
> } else {
> switch (reg) {
> case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI:
> - *data = vmmci.cmd;
> + *data = v->cmd;
> break;
> case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 4:
> /* Update time once when reading the first register */
> - gettimeofday(&vmmci.time, NULL);
> - *data = (uint64_t)vmmci.time.tv_sec;
> + gettimeofday(&v->time, NULL);
> + *data = (uint64_t)v->time.tv_sec;
> break;
> case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 8:
> - *data = (uint64_t)vmmci.time.tv_sec << 32;
> + *data = (uint64_t)v->time.tv_sec << 32;
> break;
> case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 12:
> - *data = (uint64_t)vmmci.time.tv_usec;
> + *data = (uint64_t)v->time.tv_usec;
> break;
> case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI + 16:
> - *data = (uint64_t)vmmci.time.tv_usec << 32;
> + *data = (uint64_t)v->time.tv_usec << 32;
> break;
> case VIRTIO_CONFIG_DEVICE_FEATURES:
> - *data = vmmci.cfg.device_feature;
> + *data = dev->cfg.device_feature;
> break;
> case VIRTIO_CONFIG_GUEST_FEATURES:
> - *data = vmmci.cfg.guest_feature;
> + *data = dev->cfg.guest_feature;
> break;
> case VIRTIO_CONFIG_QUEUE_PFN:
> - *data = vmmci.cfg.queue_pfn;
> + *data = dev->cfg.queue_pfn;
> break;
> case VIRTIO_CONFIG_QUEUE_SIZE:
> - *data = vmmci.cfg.queue_size;
> + *data = dev->cfg.queue_size;
> break;
> case VIRTIO_CONFIG_QUEUE_SELECT:
> - *data = vmmci.cfg.queue_select;
> + *data = dev->cfg.queue_select;
> break;
> case VIRTIO_CONFIG_QUEUE_NOTIFY:
> - *data = vmmci.cfg.queue_notify;
> + *data = dev->cfg.queue_notify;
> break;
> case VIRTIO_CONFIG_DEVICE_STATUS:
> - *data = vmmci.cfg.device_status;
> + *data = dev->status;
> break;
> case VIRTIO_CONFIG_ISR_STATUS:
> - *data = vmmci.cfg.isr_status;
> - vmmci.cfg.isr_status = 0;
> - vcpu_deassert_irq(vmmci.vm_id, 0, vmmci.irq);
> + *data = dev->isr;
> + dev->isr = 0;
> + vcpu_deassert_irq(dev->vm_id, 0, dev->irq);
> break;
> }
> }
> - mutex_unlock(&vmmci.mutex);
> + mutex_unlock(&v->mutex);
>
> return (0);
> }
> @@ -506,18 +963,20 @@ virtio_get_base(int fd, char *path, size_t npath, int
> static void
> vmmci_pipe_dispatch(int fd, short event, void *arg)
> {
> - enum pipe_msg_type msg;
> - struct timeval tv = { 0, 0 };
> + struct virtio_dev *dev = (struct virtio_dev *)arg;
> + struct vmmci_dev *v = &dev->vmmci;
> + struct timeval tv = { 0, 0 };
> + enum pipe_msg_type msg;
>
> - msg = vm_pipe_recv(&vmmci.dev_pipe);
> + msg = vm_pipe_recv(&v->dev_pipe);
> switch (msg) {
> case VMMCI_SET_TIMEOUT_SHORT:
> tv.tv_sec = VMMCI_TIMEOUT_SHORT;
> - evtimer_add(&vmmci.timeout, &tv);
> + evtimer_add(&v->timeout, &tv);
> break;
> case VMMCI_SET_TIMEOUT_LONG:
> tv.tv_sec = VMMCI_TIMEOUT_LONG;
> - evtimer_add(&vmmci.timeout, &tv);
> + evtimer_add(&v->timeout, &tv);
> break;
> default:
> log_warnx("%s: invalid pipe message type %d", __func__, msg);
> @@ -531,98 +990,78 @@ virtio_init(struct vmd_vm *vm, int child_cdrom,
> struct vmop_create_params *vmc = &vm->vm_params;
> struct vm_create_params *vcp = &vmc->vmc_params;
> struct virtio_dev *dev;
> - uint8_t id;
> - uint8_t i, j;
> - int ret = 0;
> + uint8_t id, i, j;
> + int bar_id, ret = 0;
>
> - /* Virtio entropy device */
> + SLIST_INIT(&virtio_devs);
> +
> + /* Virtio 1.x Entropy Device */
> if (pci_add_device(&id, PCI_VENDOR_QUMRANET,
> - PCI_PRODUCT_QUMRANET_VIO_RNG, PCI_CLASS_SYSTEM,
> - PCI_SUBCLASS_SYSTEM_MISC,
> - PCI_VENDOR_OPENBSD,
> - PCI_PRODUCT_VIRTIO_ENTROPY, 1, NULL)) {
> + PCI_PRODUCT_QUMRANET_VIO1_RNG, PCI_CLASS_SYSTEM,
> + PCI_SUBCLASS_SYSTEM_MISC, PCI_VENDOR_OPENBSD,
> + PCI_PRODUCT_VIRTIO_ENTROPY, 1, 1, NULL)) {
> log_warnx("%s: can't add PCI virtio rng device",
> __progname);
> return;
> }
> + virtio_dev_init(&viornd, id, VIORND_QUEUE_SIZE_DEFAULT,
> + VIRTIO_RND_QUEUES, VIRTIO_F_VERSION_1, vcp->vcp_id);
>
> - if (pci_add_bar(id, PCI_MAPREG_TYPE_IO, virtio_rnd_io, NULL)) {
> + bar_id = pci_add_bar(id, PCI_MAPREG_TYPE_IO, virtio_io_dispatch,
> + &viornd);
> + if (bar_id == -1 || bar_id > 0xff) {
> log_warnx("%s: can't add bar for virtio rng device",
> __progname);
> return;
> }
> + virtio_pci_add_cap(id, VIRTIO_PCI_CAP_COMMON_CFG, bar_id, 0);
> + virtio_pci_add_cap(id, VIRTIO_PCI_CAP_ISR_CFG, bar_id, 0);
> + virtio_pci_add_cap(id, VIRTIO_PCI_CAP_NOTIFY_CFG, bar_id, 0);
>
> - memset(&viornd, 0, sizeof(viornd));
> - viornd.vq[0].qs = VIORND_QUEUE_SIZE;
> - viornd.vq[0].vq_availoffset = sizeof(struct vring_desc) *
> - VIORND_QUEUE_SIZE;
> - viornd.vq[0].vq_usedoffset = VIRTQUEUE_ALIGN(
> - sizeof(struct vring_desc) * VIORND_QUEUE_SIZE
> - + sizeof(uint16_t) * (2 + VIORND_QUEUE_SIZE));
> - viornd.pci_id = id;
> - viornd.irq = pci_get_dev_irq(id);
> - viornd.vm_id = vcp->vcp_id;
> -
> - SLIST_INIT(&virtio_devs);
> -
> + /* Virtio 1.x Network Devices */
> if (vmc->vmc_nnics > 0) {
> for (i = 0; i < vmc->vmc_nnics; i++) {
> - dev = calloc(1, sizeof(struct virtio_dev));
> + dev = malloc(sizeof(struct virtio_dev));
> if (dev == NULL) {
> log_warn("%s: calloc failure allocating vionet",
> __progname);
> return;
> }
> - /* Virtio network */
> - dev->dev_type = VMD_DEVTYPE_NET;
> -
> if (pci_add_device(&id, PCI_VENDOR_QUMRANET,
> - PCI_PRODUCT_QUMRANET_VIO_NET, PCI_CLASS_SYSTEM,
> + PCI_PRODUCT_QUMRANET_VIO1_NET, PCI_CLASS_SYSTEM,
> PCI_SUBCLASS_SYSTEM_MISC, PCI_VENDOR_OPENBSD,
> - PCI_PRODUCT_VIRTIO_NETWORK, 1, NULL)) {
> + PCI_PRODUCT_VIRTIO_NETWORK, 1, 1, NULL)) {
> log_warnx("%s: can't add PCI virtio net device",
> __progname);
> return;
> }
> - dev->pci_id = id;
> - dev->sync_fd = -1;
> - dev->async_fd = -1;
> - dev->vm_id = vcp->vcp_id;
> - dev->vm_vmid = vm->vm_vmid;
> - dev->irq = pci_get_dev_irq(id);
> + virtio_dev_init(dev, id, VIONET_QUEUE_SIZE_DEFAULT,
> + VIRTIO_NET_QUEUES,
> + (VIRTIO_NET_F_MAC | VIRTIO_F_VERSION_1),
> + vcp->vcp_id);
>
> - /* The vionet pci bar function is called by the vcpu. */
> if (pci_add_bar(id, PCI_MAPREG_TYPE_IO, virtio_pci_io,
> - dev)) {
> + dev) == -1) {
> log_warnx("%s: can't add bar for virtio net "
> "device", __progname);
> return;
> }
> + virtio_pci_add_cap(id, VIRTIO_PCI_CAP_COMMON_CFG,
> + bar_id, 0);
> + virtio_pci_add_cap(id, VIRTIO_PCI_CAP_DEVICE_CFG,
> + bar_id, 8);
> + virtio_pci_add_cap(id, VIRTIO_PCI_CAP_ISR_CFG, bar_id,
> + 0);
> + virtio_pci_add_cap(id, VIRTIO_PCI_CAP_NOTIFY_CFG,
> + bar_id, 0);
>
> - dev->vionet.vq[RXQ].qs = VIONET_QUEUE_SIZE;
> - dev->vionet.vq[RXQ].vq_availoffset =
> - sizeof(struct vring_desc) * VIONET_QUEUE_SIZE;
> - dev->vionet.vq[RXQ].vq_usedoffset = VIRTQUEUE_ALIGN(
> - sizeof(struct vring_desc) * VIONET_QUEUE_SIZE
> - + sizeof(uint16_t) * (2 + VIONET_QUEUE_SIZE));
> - dev->vionet.vq[RXQ].last_avail = 0;
> - dev->vionet.vq[RXQ].notified_avail = 0;
> -
> - dev->vionet.vq[TXQ].qs = VIONET_QUEUE_SIZE;
> - dev->vionet.vq[TXQ].vq_availoffset =
> - sizeof(struct vring_desc) * VIONET_QUEUE_SIZE;
> - dev->vionet.vq[TXQ].vq_usedoffset = VIRTQUEUE_ALIGN(
> - sizeof(struct vring_desc) * VIONET_QUEUE_SIZE
> - + sizeof(uint16_t) * (2 + VIONET_QUEUE_SIZE));
> - dev->vionet.vq[TXQ].last_avail = 0;
> - dev->vionet.vq[TXQ].notified_avail = 0;
> -
> + /* Device specific initializiation. */
> + dev->dev_type = VMD_DEVTYPE_NET;
> + dev->vm_vmid = vm->vm_vmid;
> dev->vionet.data_fd = child_taps[i];
>
> /* MAC address has been assigned by the parent */
> memcpy(&dev->vionet.mac, &vmc->vmc_macs[i], 6);
> - dev->vionet.cfg.device_feature = VIRTIO_NET_F_MAC;
> -
> dev->vionet.lockedmac =
> vmc->vmc_ifflags[i] & VMIFF_LOCKED ? 1 : 0;
> dev->vionet.local =
> @@ -645,52 +1084,50 @@ virtio_init(struct vmd_vm *vm, int child_cdrom,
> }
> }
>
> + /* Virtio 1.x Block Devices */
> if (vmc->vmc_ndisks > 0) {
> for (i = 0; i < vmc->vmc_ndisks; i++) {
> - dev = calloc(1, sizeof(struct virtio_dev));
> + dev = malloc(sizeof(struct virtio_dev));
> if (dev == NULL) {
> - log_warn("%s: calloc failure allocating vioblk",
> - __progname);
> + log_warn("%s: failure allocating vioblk",
> + __func__);
> return;
> }
> -
> - /* One vioblk device for each disk defined in vcp */
> - dev->dev_type = VMD_DEVTYPE_DISK;
> -
> if (pci_add_device(&id, PCI_VENDOR_QUMRANET,
> - PCI_PRODUCT_QUMRANET_VIO_BLOCK,
> + PCI_PRODUCT_QUMRANET_VIO1_BLOCK,
> PCI_CLASS_MASS_STORAGE,
> - PCI_SUBCLASS_MASS_STORAGE_SCSI,
> - PCI_VENDOR_OPENBSD,
> - PCI_PRODUCT_VIRTIO_BLOCK, 1, NULL)) {
> + PCI_SUBCLASS_MASS_STORAGE_SCSI, PCI_VENDOR_OPENBSD,
> + PCI_PRODUCT_VIRTIO_BLOCK, 1, 1, NULL)) {
> log_warnx("%s: can't add PCI virtio block "
> "device", __progname);
> return;
> }
> - dev->pci_id = id;
> - dev->sync_fd = -1;
> - dev->async_fd = -1;
> - dev->vm_id = vcp->vcp_id;
> - dev->vm_vmid = vm->vm_vmid;
> - dev->irq = pci_get_dev_irq(id);
> + virtio_dev_init(dev, id, VIOBLK_QUEUE_SIZE_DEFAULT,
> + VIRTIO_BLK_QUEUES,
> + (VIRTIO_F_VERSION_1 | VIRTIO_BLK_F_SEG_MAX),
> + vcp->vcp_id);
>
> - if (pci_add_bar(id, PCI_MAPREG_TYPE_IO, virtio_pci_io,
> - &dev->vioblk)) {
> + bar_id = pci_add_bar(id, PCI_MAPREG_TYPE_IO, virtio_pci_io,
> + dev);
> + if (bar_id == -1 || bar_id > 0xff) {
> log_warnx("%s: can't add bar for virtio block "
> "device", __progname);
> return;
> }
> - dev->vioblk.vq[0].qs = VIOBLK_QUEUE_SIZE;
> - dev->vioblk.vq[0].vq_availoffset =
> - sizeof(struct vring_desc) * VIOBLK_QUEUE_SIZE;
> - dev->vioblk.vq[0].vq_usedoffset = VIRTQUEUE_ALIGN(
> - sizeof(struct vring_desc) * VIOBLK_QUEUE_SIZE
> - + sizeof(uint16_t) * (2 + VIOBLK_QUEUE_SIZE));
> - dev->vioblk.vq[0].last_avail = 0;
> - dev->vioblk.cfg.device_feature =
> - VIRTIO_BLK_F_SEG_MAX;
> - dev->vioblk.seg_max = VIOBLK_SEG_MAX;
> + virtio_pci_add_cap(id, VIRTIO_PCI_CAP_COMMON_CFG,
> + bar_id, 0);
> + virtio_pci_add_cap(id, VIRTIO_PCI_CAP_DEVICE_CFG,
> + bar_id, 24);
> + virtio_pci_add_cap(id, VIRTIO_PCI_CAP_ISR_CFG, bar_id,
> + 0);
> + virtio_pci_add_cap(id, VIRTIO_PCI_CAP_NOTIFY_CFG,
> + bar_id, 0);
>
> + /* Device specific initialization. */
> + dev->dev_type = VMD_DEVTYPE_DISK;
> + dev->vm_vmid = vm->vm_vmid;
> + dev->vioblk.seg_max = VIOBLK_SEG_MAX_DEFAULT;
> +
> /*
> * Initialize disk fds to an invalid fd (-1), then
> * set any child disk fds.
> @@ -714,89 +1151,74 @@ virtio_init(struct vmd_vm *vm, int child_cdrom,
> fatalx("failed to launch virtio device");
> }
>
> - /* vioscsi cdrom */
> + /* Virtio 1.x SCSI CD-ROM */
> if (strlen(vmc->vmc_cdrom)) {
> - vioscsi = calloc(1, sizeof(struct vioscsi_dev));
> - if (vioscsi == NULL) {
> + dev = malloc(sizeof(struct virtio_dev));
> + if (dev == NULL) {
> log_warn("%s: calloc failure allocating vioscsi",
> __progname);
> return;
> }
> -
> if (pci_add_device(&id, PCI_VENDOR_QUMRANET,
> - PCI_PRODUCT_QUMRANET_VIO_SCSI,
> - PCI_CLASS_MASS_STORAGE,
> - PCI_SUBCLASS_MASS_STORAGE_SCSI,
> - PCI_VENDOR_OPENBSD,
> - PCI_PRODUCT_VIRTIO_SCSI, 1, NULL)) {
> + PCI_PRODUCT_QUMRANET_VIO1_SCSI, PCI_CLASS_MASS_STORAGE,
> + PCI_SUBCLASS_MASS_STORAGE_SCSI, PCI_VENDOR_OPENBSD,
> + PCI_PRODUCT_VIRTIO_SCSI, 1, 1, NULL)) {
> log_warnx("%s: can't add PCI vioscsi device",
> __progname);
> return;
> }
> -
> - if (pci_add_bar(id, PCI_MAPREG_TYPE_IO, vioscsi_io, vioscsi)) {
> + virtio_dev_init(dev, id, VIOSCSI_QUEUE_SIZE_DEFAULT,
> + VIRTIO_SCSI_QUEUES, VIRTIO_F_VERSION_1, vcp->vcp_id);
> + if (pci_add_bar(id, PCI_MAPREG_TYPE_IO, virtio_io_dispatch, dev)
> + == -1) {
> log_warnx("%s: can't add bar for vioscsi device",
> __progname);
> return;
> }
> + virtio_pci_add_cap(id, VIRTIO_PCI_CAP_COMMON_CFG, bar_id, 0);
> + virtio_pci_add_cap(id, VIRTIO_PCI_CAP_DEVICE_CFG, bar_id, 36);
> + virtio_pci_add_cap(id, VIRTIO_PCI_CAP_ISR_CFG, bar_id, 0);
> + virtio_pci_add_cap(id, VIRTIO_PCI_CAP_NOTIFY_CFG, bar_id, 0);
>
> - for (i = 0; i < VIRTIO_MAX_QUEUES; i++) {
> - vioscsi->vq[i].qs = VIOSCSI_QUEUE_SIZE;
> - vioscsi->vq[i].vq_availoffset =
> - sizeof(struct vring_desc) * VIOSCSI_QUEUE_SIZE;
> - vioscsi->vq[i].vq_usedoffset = VIRTQUEUE_ALIGN(
> - sizeof(struct vring_desc) * VIOSCSI_QUEUE_SIZE
> - + sizeof(uint16_t) * (2 + VIOSCSI_QUEUE_SIZE));
> - vioscsi->vq[i].last_avail = 0;
> - }
> - if (virtio_raw_init(&vioscsi->file, &vioscsi->sz, &child_cdrom,
> - 1) == -1) {
> + /* Device specific initialization. */
> + if (virtio_raw_init(&dev->vioscsi.file, &dev->vioscsi.sz,
> + &child_cdrom, 1) == -1) {
> log_warnx("%s: unable to determine iso format",
> __func__);
> return;
> }
> - vioscsi->locked = 0;
> - vioscsi->lba = 0;
> - vioscsi->n_blocks = vioscsi->sz / VIOSCSI_BLOCK_SIZE_CDROM;
> - vioscsi->max_xfer = VIOSCSI_BLOCK_SIZE_CDROM;
> - vioscsi->pci_id = id;
> - vioscsi->vm_id = vcp->vcp_id;
> - vioscsi->irq = pci_get_dev_irq(id);
> + dev->vioscsi.locked = 0;
> + dev->vioscsi.lba = 0;
> + dev->vioscsi.n_blocks = dev->vioscsi.sz /
> + VIOSCSI_BLOCK_SIZE_CDROM;
> + dev->vioscsi.max_xfer = VIOSCSI_BLOCK_SIZE_CDROM;
> }
>
> - /* virtio control device */
> - if (pci_add_device(&id, PCI_VENDOR_OPENBSD,
> - PCI_PRODUCT_OPENBSD_CONTROL,
> - PCI_CLASS_COMMUNICATIONS,
> - PCI_SUBCLASS_COMMUNICATIONS_MISC,
> - PCI_VENDOR_OPENBSD,
> - PCI_PRODUCT_VIRTIO_VMMCI, 1, NULL)) {
> + /* Virtio 0.9 VMM Control Interface */
> + dev = &vmmci;
> + if (pci_add_device(&id, PCI_VENDOR_OPENBSD, PCI_PRODUCT_OPENBSD_CONTROL,
> + PCI_CLASS_COMMUNICATIONS, PCI_SUBCLASS_COMMUNICATIONS_MISC,
> + PCI_VENDOR_OPENBSD, PCI_PRODUCT_VIRTIO_VMMCI, 0, 1, NULL)) {
> log_warnx("%s: can't add PCI vmm control device",
> __progname);
> return;
> }
> -
> - if (pci_add_bar(id, PCI_MAPREG_TYPE_IO, vmmci_io, NULL)) {
> + virtio_dev_init(dev, id, 0, 0,
> + VMMCI_F_TIMESYNC | VMMCI_F_ACK | VMMCI_F_SYNCRTC, vcp->vcp_id);
> + if (pci_add_bar(id, PCI_MAPREG_TYPE_IO, vmmci_io, dev) == -1) {
> log_warnx("%s: can't add bar for vmm control device",
> __progname);
> return;
> }
>
> - memset(&vmmci, 0, sizeof(vmmci));
> - vmmci.cfg.device_feature = VMMCI_F_TIMESYNC | VMMCI_F_ACK |
> - VMMCI_F_SYNCRTC;
> - vmmci.vm_id = vcp->vcp_id;
> - vmmci.irq = pci_get_dev_irq(id);
> - vmmci.pci_id = id;
> - ret = pthread_mutex_init(&vmmci.mutex, NULL);
> + ret = pthread_mutex_init(&dev->vmmci.mutex, NULL);
> if (ret) {
> errno = ret;
> fatal("could not initialize vmmci mutex");
> }
> -
> - evtimer_set(&vmmci.timeout, vmmci_timeout, NULL);
> - vm_pipe_init(&vmmci.dev_pipe, vmmci_pipe_dispatch);
> - event_add(&vmmci.dev_pipe.read_ev, NULL);
> + evtimer_set(&dev->vmmci.timeout, vmmci_timeout, NULL);
> + vm_pipe_init2(&dev->vmmci.dev_pipe, vmmci_pipe_dispatch, dev);
> + event_add(&dev->vmmci.dev_pipe.read_ev, NULL);
> }
>
> /*
> @@ -855,7 +1277,7 @@ virtio_shutdown(struct vmd_vm *vm)
>
> /* Ensure that our disks are synced. */
> if (vioscsi != NULL)
> - vioscsi->file.close(vioscsi->file.p, 0);
> + vioscsi->vioscsi.file.close(vioscsi->vioscsi.file.p, 0);
>
> /*
> * Broadcast shutdown to child devices. We need to do this
> @@ -923,6 +1345,125 @@ virtio_start(struct vmd_vm *vm)
> }
>
> /*
> + * Initialize a new virtio device structure.
> + */
> +static void
> +virtio_dev_init(struct virtio_dev *dev, uint8_t pci_id, uint16_t queue_size,
> + uint16_t num_queues, uint64_t features, uint32_t vm_id)
> +{
> + size_t i;
> + uint16_t device_id;
> +
> + if (num_queues > 0 && num_queues > VIRTIO_MAX_QUEUES)
> + fatalx("%s: num_queues too large", __func__);
> +
> + device_id = pci_get_subsys_id(pci_id);
> + if (!device_id)
> + fatalx("%s: invalid pci device id %u", __func__, pci_id);
> +
> + memset(dev, 0, sizeof(*dev));
> +
> + dev->pci_id = pci_id;
> + dev->device_id = device_id;
> + dev->irq = pci_get_dev_irq(pci_id);
> + dev->isr = 0;
> + dev->vm_id = vm_id;
> +
> + dev->device_feature = features;
> +
> + dev->pci_cfg.config_generation = 0;
> + dev->cfg.device_feature = features;
> +
> + dev->num_queues = num_queues;
> + dev->queue_size = queue_size;
> + dev->cfg.queue_size = queue_size;
> +
> + dev->async_fd = -1;
> + dev->sync_fd = -1;
> +
> + if (num_queues > 0) {
> + for (i = 0; i < num_queues; i++)
> + virtio_vq_init(dev, i);
> + }
> +}
> +
> +void
> +virtio_vq_init(struct virtio_dev *dev, size_t idx)
> +{
> + struct virtio_vq_info *vq_info = NULL;
> + int v1 = (dev->device_feature & VIRTIO_F_VERSION_1) ? 1 : 0;
> +
> + if (idx >= dev->num_queues)
> + fatalx("%s: invalid virtqueue index", __func__);
> + vq_info = &dev->vq[idx];
> +
> + vq_info->q_gpa = 0;
> + vq_info->qs = dev->queue_size;
> + vq_info->mask = dev->queue_size - 1;
> +
> + if (v1) {
> + vq_info->vq_enabled = 0;
> + vq_info->vq_availoffset = 0;
> + vq_info->vq_usedoffset = 0;
> + } else {
> + /* Always enable on pre-1.0 virtio devices. */
> + vq_info->vq_enabled = 1;
> + vq_info->vq_availoffset =
> + sizeof(struct vring_desc) * vq_info->qs;
> + vq_info->vq_usedoffset = VIRTQUEUE_ALIGN(
> + sizeof(struct vring_desc) * vq_info->qs +
> + sizeof(uint16_t) * (2 + vq_info->qs));
> + }
> +
> + vq_info->last_avail = 0;
> + vq_info->notified_avail = 0;
> +}
> +
> +
> +static void
> +virtio_pci_add_cap(uint8_t pci_id, uint8_t cfg_type, uint8_t bar_id,
> + uint32_t dev_cfg_len)
> +{
> + struct virtio_pci_common_cap cap;
> +
> + memset(&cap, 0, sizeof(cap));
> +
> + cap.virtio.cap_vndr = PCI_CAP_VENDSPEC;
> + cap.virtio.cap_len = sizeof(struct virtio_pci_cap);
> + cap.virtio.bar = bar_id;
> + cap.virtio.cfg_type = cfg_type;
> +
> + switch (cfg_type) {
> + case VIRTIO_PCI_CAP_COMMON_CFG:
> + cap.virtio.offset = VIO1_CFG_BAR_OFFSET;
> + cap.virtio.length = sizeof(struct virtio_pci_common_cfg);
> + break;
> + case VIRTIO_PCI_CAP_DEVICE_CFG:
> + /* XXX maybe inspect the virtio device and lookup the len. */
> + cap.virtio.offset = VIO1_DEV_BAR_OFFSET;
> + cap.virtio.length = dev_cfg_len;
> + break;
> + case VIRTIO_PCI_CAP_ISR_CFG:
> + cap.virtio.offset = VIO1_ISR_BAR_OFFSET;
> + cap.virtio.length = sizeof(uint8_t);
> + break;
> + case VIRTIO_PCI_CAP_NOTIFY_CFG:
> + cap.virtio.offset = VIO1_NOTIFY_BAR_OFFSET;
> + cap.virtio.length = sizeof(uint16_t);
> + cap.notify.notify_off_multiplier = 0;
> + break;
> + default:
> + fatalx("%s: invalid pci capability config type %u", __func__,
> + cfg_type);
> + }
> +
> + if (pci_add_capability(pci_id, &cap.pci) == -1) {
> + fatalx("%s: can't add capability for virtio pci device %u",
> + __func__, pci_id);
> + }
> +}
> +
> +/*
> * Fork+exec a child virtio device. Returns 0 on success.
> */
> static int
> @@ -1207,14 +1748,13 @@ static int
> handle_dev_msg(struct viodev_msg *msg, struct virtio_dev *gdev)
> {
> uint32_t vm_id = gdev->vm_id;
> - int irq = gdev->irq;
>
> switch (msg->type) {
> case VIODEV_MSG_KICK:
> if (msg->state == INTR_STATE_ASSERT)
> - vcpu_assert_irq(vm_id, msg->vcpu, irq);
> + vcpu_assert_irq(vm_id, msg->vcpu, msg->irq);
> else if (msg->state == INTR_STATE_DEASSERT)
> - vcpu_deassert_irq(vm_id, msg->vcpu, irq);
> + vcpu_deassert_irq(vm_id, msg->vcpu, msg->irq);
> break;
> case VIODEV_MSG_READY:
> log_debug("%s: device reports ready", __func__);
> @@ -1305,10 +1845,8 @@ virtio_pci_io(int dir, uint16_t reg, uint32_t *data, u
> imsg_free(&imsg);
>
> if (msg.type == VIODEV_MSG_IO_READ && msg.data_valid) {
> -#if DEBUG
> - log_debug("%s: got sync read response (reg=%s)",
> - __func__, virtio_reg_name(msg.reg));
> -#endif /* DEBUG */
> + DPRINTF("%s: got sync read response (reg=%s)", __func__,
> + virtio_reg_name(msg.reg));
> *data = msg.data;
> /*
> * It's possible we're asked to {de,}assert after the
> blob - 4bd6f68b41fcb8ce68781903e4364e543adc0075
> blob + 029e66b216b0c680122dabe81d63752f54155914
> --- usr.sbin/vmd/virtio.h
> +++ usr.sbin/vmd/virtio.h
> @@ -19,11 +19,13 @@
> #include <sys/types.h>
>
> #include <dev/pv/virtioreg.h>
> +#include <dev/pci/virtio_pcireg.h>
> #include <net/if_tun.h>
>
> #include <event.h>
>
> #include "vmd.h"
> +#include "pci.h"
>
> #ifndef _VIRTIO_H_
> #define _VIRTIO_H_
> @@ -33,20 +35,55 @@
> #define ALIGNSZ(sz, align) ((sz + align - 1) & ~(align - 1))
> #define MIN(a,b) (((a)<(b))?(a):(b))
>
> +#define VIO1_PCI_DEVICE_FEATURE_SELECT \
> + (offsetof(struct virtio_pci_common_cfg, device_feature_select))
> +#define VIO1_PCI_DEVICE_FEATURE \
> + (offsetof(struct virtio_pci_common_cfg, device_feature))
> +#define VIO1_PCI_DRIVER_FEATURE_SELECT \
> + (offsetof(struct virtio_pci_common_cfg, driver_feature_select))
> +#define VIO1_PCI_DRIVER_FEATURE \
> + (offsetof(struct virtio_pci_common_cfg, driver_feature))
> +#define VIO1_PCI_CONFIG_MSIX_VECTOR \
> + (offsetof(struct virtio_pci_common_cfg, config_msix_vector))
> +#define VIO1_PCI_NUM_QUEUES \
> + (offsetof(struct virtio_pci_common_cfg, num_queues))
> +#define VIO1_PCI_DEVICE_STATUS \
> + (offsetof(struct virtio_pci_common_cfg, device_status))
> +#define VIO1_PCI_CONFIG_GENERATION \
> + (offsetof(struct virtio_pci_common_cfg, config_generation))
> +#define VIO1_PCI_QUEUE_SELECT \
> + (offsetof(struct virtio_pci_common_cfg, queue_select))
> +#define VIO1_PCI_QUEUE_SIZE \
> + (offsetof(struct virtio_pci_common_cfg, queue_size))
> +#define VIO1_PCI_QUEUE_MSIX_VECTOR \
> + (offsetof(struct virtio_pci_common_cfg, queue_msix_vector))
> +#define VIO1_PCI_QUEUE_ENABLE \
> + (offsetof(struct virtio_pci_common_cfg, queue_enable))
> +#define VIO1_PCI_QUEUE_NOTIFY_OFF \
> + (offsetof(struct virtio_pci_common_cfg, queue_notify_off))
> +#define VIO1_PCI_QUEUE_DESC \
> + (offsetof(struct virtio_pci_common_cfg, queue_desc))
> +#define VIO1_PCI_QUEUE_AVAIL \
> + (offsetof(struct virtio_pci_common_cfg, queue_avail))
> +#define VIO1_PCI_QUEUE_USED \
> + (offsetof(struct virtio_pci_common_cfg, queue_used))
> +
> +#define VIO1_CFG_BAR_OFFSET 0x000
> +#define VIO1_NOTIFY_BAR_OFFSET 0x100
> +#define VIO1_ISR_BAR_OFFSET 0x200
> +#define VIO1_DEV_BAR_OFFSET 0x300
> +
> /* Queue sizes must be power of two and less than IOV_MAX (1024). */
> -#define VIORND_QUEUE_SIZE 64
> -#define VIORND_QUEUE_MASK (VIORND_QUEUE_SIZE - 1)
> +#define VIRTIO_QUEUE_SIZE_MAX IOV_MAX
> +#define VIORND_QUEUE_SIZE_DEFAULT 64
> +#define VIOBLK_QUEUE_SIZE_DEFAULT 128
> +#define VIOSCSI_QUEUE_SIZE_DEFAULT 128
> +#define VIONET_QUEUE_SIZE_DEFAULT 256
>
> -#define VIOBLK_QUEUE_SIZE 128
> -#define VIOBLK_QUEUE_MASK (VIOBLK_QUEUE_SIZE - 1)
> -#define VIOBLK_SEG_MAX (VIOBLK_QUEUE_SIZE - 2)
> +#define VIOBLK_SEG_MAX_DEFAULT (VIOBLK_QUEUE_SIZE_DEFAULT - 2)
>
> -#define VIOSCSI_QUEUE_SIZE 128
> -#define VIOSCSI_QUEUE_MASK (VIOSCSI_QUEUE_SIZE - 1)
> +#define VIOSCSI_REQUEST_QUEUES 1
>
> -#define VIONET_QUEUE_SIZE 256
> -#define VIONET_QUEUE_MASK (VIONET_QUEUE_SIZE - 1)
> -
> /* Virtio network device is backed by tap(4), so inherit limits */
> #define VIONET_HARD_MTU TUNMRU
> #define VIONET_MIN_TXLEN ETHER_HDR_LEN
> @@ -56,12 +93,15 @@
> #define VMMCI_TIMEOUT_SHORT 3
> #define VMMCI_TIMEOUT_LONG 120
>
> -/* All the devices we support have either 1, 2 or 3 queues */
> -/* viornd - 1 queue
> - * vioblk - 1 queue
> - * vionet - 2 queues
> - * vioscsi - 3 queues
> +/*
> + * All the devices we support have either 1, 2 or 3 virtqueues.
> + * No devices currently support VIRTIO_*_F_MQ so values are fixed.
> */
> +#define VIRTIO_RND_QUEUES 1
> +#define VIRTIO_BLK_QUEUES 1
> +#define VIRTIO_NET_QUEUES 2
> +#define VIRTIO_SCSI_QUEUES 3
> +#define VIRTIO_VMMCI_QUEUES 0
> #define VIRTIO_MAX_QUEUES 3
>
> #define MAXPHYS (64 * 1024) /* max raw I/O transfer size */
> @@ -74,6 +114,14 @@
> #define DESC_WRITABLE(/* struct vring_desc */ x) \
> (((x)->flags & VRING_DESC_F_WRITE) ? 1 : 0)
>
> +struct virtio_pci_common_cap {
> + union {
> + struct pci_cap pci;
> + struct virtio_pci_cap virtio;
> + struct virtio_pci_notify_cap notify;
> + struct virtio_pci_cfg_cap cfg;
> + };
> +} __packed;
>
> /*
> * VM <-> Device messaging.
> @@ -104,18 +152,15 @@ struct viodev_msg {
> } __packed;
>
> /*
> - * This struct stores notifications from a virtio driver. There is
> - * one such struct per virtio device.
> + * Legacy Virtio 0.9 register state.
> */
> struct virtio_io_cfg {
> uint32_t device_feature;
> uint32_t guest_feature;
> uint32_t queue_pfn;
> - uint16_t queue_size;
> uint16_t queue_select;
> + uint16_t queue_size;
> uint16_t queue_notify;
> - uint8_t device_status;
> - uint8_t isr_status;
> };
>
> struct virtio_backing {
> @@ -143,6 +188,9 @@ struct virtio_vq_info {
> /* Queue size: number of queue entries in virtq */
> uint32_t qs;
>
> + /* Queue mask */
> + uint32_t mask;
> +
> /*
> * The offset of the 'available' ring within the virtq located at
> * guest physical address qa above
> @@ -166,6 +214,8 @@ struct virtio_vq_info {
> * driver notified to the host.
> */
> uint16_t notified_avail;
> +
> + uint8_t vq_enabled;
> };
>
> /*
> @@ -202,22 +252,10 @@ struct virtio_vq_acct {
> struct vring_used *used;
> };
>
> -struct viornd_dev {
> - struct virtio_io_cfg cfg;
> -
> - struct virtio_vq_info vq[VIRTIO_MAX_QUEUES];
> -
> - uint8_t pci_id;
> - int irq;
> - uint32_t vm_id;
> -};
> -
> struct vioblk_dev {
> - struct virtio_io_cfg cfg;
> - struct virtio_vq_info vq[VIRTIO_MAX_QUEUES];
> struct virtio_backing file;
> -
> int disk_fd[VM_MAX_BASE_PER_DISK]; /* fds for disk image(s) */
> +
> uint8_t ndisk_fd; /* number of valid disk fds */
> uint64_t capacity; /* size in 512 byte sectors */
> uint32_t seg_max; /* maximum number of segments */
> @@ -232,31 +270,16 @@ struct vioblk_dev {
> * 2 - requests
> */
> struct vioscsi_dev {
> - struct virtio_io_cfg cfg;
> -
> - struct virtio_vq_info vq[VIRTIO_MAX_QUEUES];
> -
> struct virtio_backing file;
>
> - /* is the device locked */
> - int locked;
> - /* size of iso file in bytes */
> - uint64_t sz;
> - /* last block address read */
> - uint64_t lba;
> - /* number of blocks represented in iso */
> - uint64_t n_blocks;
> + int locked; /* is the device locked? */
> + uint64_t sz; /* size of iso file in bytes */
> + uint64_t lba; /* last block address read */
> + uint64_t n_blocks; /* number of blocks represented in iso */
> uint32_t max_xfer;
> -
> - uint8_t pci_id;
> - uint32_t vm_id;
> - int irq;
> };
>
> struct vionet_dev {
> - struct virtio_io_cfg cfg;
> - struct virtio_vq_info vq[VIRTIO_MAX_QUEUES];
> -
> int data_fd; /* fd for our tap device */
>
> uint8_t mac[6];
> @@ -269,28 +292,6 @@ struct vionet_dev {
> unsigned int idx;
> };
>
> -struct virtio_dev {
> - union {
> - struct vioblk_dev vioblk;
> - struct vionet_dev vionet;
> - };
> -
> - struct imsgev async_iev;
> - struct imsgev sync_iev;
> -
> - int sync_fd; /* fd for synchronous channel */
> - int async_fd; /* fd for async channel */
> -
> - uint8_t pci_id;
> - uint32_t vm_id;
> - uint32_t vm_vmid;
> - int irq;
> -
> - pid_t dev_pid;
> - char dev_type;
> - SLIST_ENTRY(virtio_dev) dev_next;
> -};
> -
> struct virtio_net_hdr {
> uint8_t flags;
> uint8_t gso_type;
> @@ -298,13 +299,17 @@ struct virtio_net_hdr {
> uint16_t gso_size;
> uint16_t csum_start;
> uint16_t csum_offset;
> + uint16_t num_buffers;
>
> /*
> - * num_buffers is only used if VIRTIO_NET_F_MRG_RXBUF is negotiated.
> - * vmd(8) doesn't negotiate that, but the field is listed here
> - * for completeness sake.
> + * The following fields exist only if VIRTIO_NET_F_HASH_REPORT
> + * is negotiated.
> */
> -/* uint16_t num_buffers; */
> + /*
> + uint32_t hash_value;
> + uint16_t hash_report;
> + uint16_t padding_reserved;
> + */
> };
>
> enum vmmci_cmd {
> @@ -315,13 +320,9 @@ enum vmmci_cmd {
> };
>
> struct vmmci_dev {
> - struct virtio_io_cfg cfg;
> struct event timeout;
> struct timeval time;
> enum vmmci_cmd cmd;
> - uint32_t vm_id;
> - int irq;
> - uint8_t pci_id;
>
> pthread_mutex_t mutex;
> struct vm_dev_pipe dev_pipe;
> @@ -334,8 +335,52 @@ struct ioinfo {
> off_t offset;
> };
>
> +struct virtio_dev {
> + uint16_t device_id; /* Virtio device id [r] */
> + union {
> + /* Multi-process enabled. */
> + struct vioblk_dev vioblk;
> + struct vionet_dev vionet;
> +
> + /* In-process only. */
> + struct vmmci_dev vmmci;
> + struct vioscsi_dev vioscsi;
> + };
> +
> + struct virtio_io_cfg cfg; /* Virtio 0.9 */
> + struct virtio_pci_common_cfg pci_cfg; /* Virtio 1.x */
> + struct virtio_vq_info vq[VIRTIO_MAX_QUEUES]; /* Virtqueues */
> +
> + uint16_t num_queues; /* number of virtqueues [r] */
> + uint16_t queue_size; /* default queue size [r] */
> +
> + uint8_t isr; /* isr status register [rw] */
> + uint8_t status; /* device status register [rw] */
> + uint64_t device_feature; /* device features [r] */
> + uint64_t driver_feature; /* driver features [rw] */
> +
> + uint8_t pci_id; /* pci device id [r] */
> + uint32_t vm_id; /* vmm(4) vm identifier [r] */
> + int irq; /* assigned irq [r] */
> +
> + /* Multi-process emulation fields. */
> + struct imsgev async_iev; /* async imsg event [r] */
> + struct imsgev sync_iev; /* sync imsg event [r] */
> +
> + int sync_fd; /* fd for synchronous channel */
> + int async_fd; /* fd for async channel */
> +
> + uint32_t vm_vmid; /* vmd(8) vm identifier [r] */
> + pid_t dev_pid; /* pid of emulator process */
> + char dev_type; /* device type (as char) */
> + SLIST_ENTRY(virtio_dev) dev_next;
> +};
> +
> /* virtio.c */
> +extern struct virtio_dev vmmci;
> +
> void virtio_init(struct vmd_vm *, int, int[][VM_MAX_BASE_PER_DISK], int *);
> +void virtio_vq_init(struct virtio_dev *, size_t);
> void virtio_broadcast_imsg(struct vmd_vm *, uint16_t, void *, uint16_t);
> void virtio_stop(struct vmd_vm *);
> void virtio_start(struct vmd_vm *);
> @@ -347,11 +392,10 @@ int vm_device_pipe(struct virtio_dev *, void (*)(int,
> int virtio_pci_io(int, uint16_t, uint32_t *, uint8_t *, void *, uint8_t);
> void virtio_assert_irq(struct virtio_dev *, int);
> void virtio_deassert_irq(struct virtio_dev *, int);
> +uint32_t virtio_io_cfg(struct virtio_dev *, int, uint8_t, uint32_t, uint8_t);
>
> -int virtio_rnd_io(int, uint16_t, uint32_t *, uint8_t *, void *, uint8_t);
> -void viornd_update_qs(void);
> -void viornd_update_qa(void);
> -int viornd_notifyq(void);
> +void virtio_update_qs(struct virtio_dev *);
> +void virtio_update_qa(struct virtio_dev *);
>
> ssize_t virtio_qcow2_get_base(int, char *, size_t, const char *);
> int virtio_qcow2_create(const char *, const char *, uint64_t);
> @@ -362,8 +406,7 @@ int virtio_raw_init(struct virtio_backing *, off_t *,
> void vionet_set_hostmac(struct vmd_vm *, unsigned int, uint8_t *);
>
> int vmmci_io(int, uint16_t, uint32_t *, uint8_t *, void *, uint8_t);
> -int vmmci_ctl(unsigned int);
> -void vmmci_ack(unsigned int);
> +int vmmci_ctl(struct virtio_dev *, unsigned int);
> void vmmci_timeout(int, short, void *);
>
> const char *vioblk_cmd_name(uint32_t);
> @@ -373,9 +416,7 @@ ssize_t dhcp_request(struct virtio_dev *, char *, size
>
> /* vioscsi.c */
> int vioscsi_io(int, uint16_t, uint32_t *, uint8_t *, void *, uint8_t);
> -void vioscsi_update_qs(struct vioscsi_dev *);
> -void vioscsi_update_qa(struct vioscsi_dev *);
> -int vioscsi_notifyq(struct vioscsi_dev *);
> +int vioscsi_notifyq(struct virtio_dev *, uint16_t);
>
> /* imsg handling */
> void viodev_msg_read(struct imsg *, struct viodev_msg *);
> blob - 96958e1f611bc01f1ed9224a54d7a0b1399bae3e
> blob + 7908417f86e7210f8cd41e6bd96b17b812990252
> --- usr.sbin/vmd/vm.c
> +++ usr.sbin/vmd/vm.c
> @@ -356,11 +356,11 @@ vm_dispatch_vmm(int fd, short event, void *arg)
> sizeof(verbose));
> break;
> case IMSG_VMDOP_VM_SHUTDOWN:
> - if (vmmci_ctl(VMMCI_SHUTDOWN) == -1)
> + if (vmmci_ctl(&vmmci, VMMCI_SHUTDOWN) == -1)
> _exit(0);
> break;
> case IMSG_VMDOP_VM_REBOOT:
> - if (vmmci_ctl(VMMCI_REBOOT) == -1)
> + if (vmmci_ctl(&vmmci, VMMCI_REBOOT) == -1)
> _exit(0);
> break;
> case IMSG_VMDOP_PAUSE_VM:
> blob - a521d1358fffb4c23170c86931859231b35b5481
> blob + 119d411f279311a87efae595a21748b31fbec853
> --- usr.sbin/vmd/vmd.h
> +++ usr.sbin/vmd/vmd.h
> @@ -43,6 +43,9 @@
>
> #define nitems(_a) (sizeof((_a)) / sizeof((_a)[0]))
>
> +#define CTASSERT(x) extern char _ctassert[(x) ? 1 : -1 ] \
> + __attribute__((__unused__))
> +
> #define MB(x) (x * 1024UL * 1024UL)
> #define GB(x) (x * 1024UL * 1024UL * 1024UL)
teaching vmd virtio 1.2