initial import

internal commit: 0ab1ea615e5cfbb0687a9d593a86a7b774386076

Signed-off-by: Anthony Xu <anthony.xu@intel.com>
This commit is contained in:
Anthony Xu
2018-03-07 21:01:19 +08:00
committed by lijinxia
parent b966397914
commit bd31b1c53e
93 changed files with 37861 additions and 0 deletions

View File

@@ -0,0 +1,790 @@
/*-
* Copyright (c) 2013 Chris Torek <torek @ torek net>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/cdefs.h>
#include <sys/param.h>
#include <sys/uio.h>
#include <stdio.h>
#include <stdint.h>
#include <pthread.h>
#include "dm.h"
#include "pci_core.h"
#include "virtio.h"
/*
* Functions for dealing with generalized "virtual devices" as
* defined by <https://www.google.com/#output=search&q=virtio+spec>
*/
/*
* In case we decide to relax the "virtio struct comes at the
* front of virtio-based device struct" constraint, let's use
* this to convert.
*/
#define DEV_STRUCT(vs) ((void *)(vs))
/*
* Link a virtio_base to its constants, the virtio device, and
* the PCI emulation.
*/
void
virtio_linkup(struct virtio_base *base, struct virtio_ops *vops,
void *pci_virtio_dev, struct pci_vdev *dev,
struct virtio_vq_info *queues)
{
int i;
/* base and pci_virtio_dev addresses must match */
assert((void *)base == pci_virtio_dev);
base->vops = vops;
base->dev = dev;
dev->arg = base;
base->queues = queues;
for (i = 0; i < vops->nvq; i++) {
queues[i].base = base;
queues[i].num = i;
}
}
/*
* Reset device (device-wide). This erases all queues, i.e.,
* all the queues become invalid (though we don't wipe out the
* internal pointers, we just clear the VQ_ALLOC flag).
*
* It resets negotiated features to "none".
*
* If MSI-X is enabled, this also resets all the vectors to NO_VECTOR.
*/
void
virtio_reset_dev(struct virtio_base *base)
{
struct virtio_vq_info *vq;
int i, nvq;
/* if (base->mtx) */
/* assert(pthread_mutex_isowned_np(base->mtx)); */
nvq = base->vops->nvq;
for (vq = base->queues, i = 0; i < nvq; vq++, i++) {
vq->flags = 0;
vq->last_avail = 0;
vq->save_used = 0;
vq->pfn = 0;
vq->msix_idx = VIRTIO_MSI_NO_VECTOR;
}
base->negotiated_caps = 0;
base->curq = 0;
/* base->status = 0; -- redundant */
if (base->isr)
pci_lintr_deassert(base->dev);
base->isr = 0;
base->msix_cfg_idx = VIRTIO_MSI_NO_VECTOR;
}
/*
* Set I/O BAR (usually 0) to map PCI config registers.
*/
void
virtio_set_io_bar(struct virtio_base *base, int barnum)
{
size_t size;
/*
* ??? should we use CFG0 if MSI-X is disabled?
* Existing code did not...
*/
size = VIRTIO_CR_CFG1 + base->vops->cfgsize;
pci_emul_alloc_bar(base->dev, barnum, PCIBAR_IO, size);
}
/*
* Initialize MSI-X vector capabilities if we're to use MSI-X,
* or MSI capabilities if not.
*
* We assume we want one MSI-X vector per queue, here, plus one
* for the config vec.
*/
int
virtio_intr_init(struct virtio_base *base, int barnum, int use_msix)
{
int nvec;
if (use_msix) {
base->flags |= VIRTIO_USE_MSIX;
VIRTIO_BASE_LOCK(base);
virtio_reset_dev(base); /* set all vectors to NO_VECTOR */
VIRTIO_BASE_UNLOCK(base);
nvec = base->vops->nvq + 1;
if (pci_emul_add_msixcap(base->dev, nvec, barnum))
return -1;
} else
base->flags &= ~VIRTIO_USE_MSIX;
/* Only 1 MSI vector for acrn-dm */
pci_emul_add_msicap(base->dev, 1);
/* Legacy interrupts are mandatory for virtio devices */
pci_lintr_request(base->dev);
return 0;
}
/*
* Initialize MSI-X vector capabilities if we're to use MSI-X,
* or MSI capabilities if not.
*
* Wrapper function for virtio_intr_init() since by default we
* will use bar 1 for MSI-X.
*/
int
virtio_interrupt_init(struct virtio_base *base, int use_msix)
{
return virtio_intr_init(base, 1, use_msix);
}
/*
* Initialize the currently-selected virtio queue (base->curq).
* The guest just gave us a page frame number, from which we can
* calculate the addresses of the queue.
*/
void
virtio_vq_init(struct virtio_base *base, uint32_t pfn)
{
struct virtio_vq_info *vq;
uint64_t phys;
size_t size;
char *vb;
vq = &base->queues[base->curq];
vq->pfn = pfn;
phys = (uint64_t)pfn << VRING_PAGE_BITS;
size = vring_size(vq->qsize);
vb = paddr_guest2host(base->dev->vmctx, phys, size);
/* First page(s) are descriptors... */
vq->desc = (struct virtio_desc *)vb;
vb += vq->qsize * sizeof(struct virtio_desc);
/* ... immediately followed by "avail" ring (entirely uint16_t's) */
vq->avail = (struct vring_avail *)vb;
vb += (2 + vq->qsize + 1) * sizeof(uint16_t);
/* Then it's rounded up to the next page... */
vb = (char *)roundup2((uintptr_t)vb, VRING_ALIGN);
/* ... and the last page(s) are the used ring. */
vq->used = (struct vring_used *)vb;
/* Mark queue as allocated, and start at 0 when we use it. */
vq->flags = VQ_ALLOC;
vq->last_avail = 0;
vq->save_used = 0;
}
/*
* Helper inline for vq_getchain(): record the i'th "real"
* descriptor.
*/
static inline void
_vq_record(int i, volatile struct virtio_desc *vd, struct vmctx *ctx,
struct iovec *iov, int n_iov, uint16_t *flags) {
if (i >= n_iov)
return;
iov[i].iov_base = paddr_guest2host(ctx, vd->addr, vd->len);
iov[i].iov_len = vd->len;
if (flags != NULL)
flags[i] = vd->flags;
}
#define VQ_MAX_DESCRIPTORS 512 /* see below */
/*
* Examine the chain of descriptors starting at the "next one" to
* make sure that they describe a sensible request. If so, return
* the number of "real" descriptors that would be needed/used in
* acting on this request. This may be smaller than the number of
* available descriptors, e.g., if there are two available but
* they are two separate requests, this just returns 1. Or, it
* may be larger: if there are indirect descriptors involved,
* there may only be one descriptor available but it may be an
* indirect pointing to eight more. We return 8 in this case,
* i.e., we do not count the indirect descriptors, only the "real"
* ones.
*
* Basically, this vets the flags and vd_next field of each
* descriptor and tells you how many are involved. Since some may
* be indirect, this also needs the vmctx (in the pci_vdev
* at base->dev) so that it can find indirect descriptors.
*
* As we process each descriptor, we copy and adjust it (guest to
* host address wise, also using the vmtctx) into the given iov[]
* array (of the given size). If the array overflows, we stop
* placing values into the array but keep processing descriptors,
* up to VQ_MAX_DESCRIPTORS, before giving up and returning -1.
* So you, the caller, must not assume that iov[] is as big as the
* return value (you can process the same thing twice to allocate
* a larger iov array if needed, or supply a zero length to find
* out how much space is needed).
*
* If you want to verify the WRITE flag on each descriptor, pass a
* non-NULL "flags" pointer to an array of "uint16_t" of the same size
* as n_iov and we'll copy each flags field after unwinding any
* indirects.
*
* If some descriptor(s) are invalid, this prints a diagnostic message
* and returns -1. If no descriptors are ready now it simply returns 0.
*
* You are assumed to have done a vq_ring_ready() if needed (note
* that vq_has_descs() does one).
*/
int
vq_getchain(struct virtio_vq_info *vq, uint16_t *pidx,
struct iovec *iov, int n_iov, uint16_t *flags)
{
int i;
u_int ndesc, n_indir;
u_int idx, next;
volatile struct virtio_desc *vdir, *vindir, *vp;
struct vmctx *ctx;
struct virtio_base *base;
const char *name;
base = vq->base;
name = base->vops->name;
/*
* Note: it's the responsibility of the guest not to
* update vq->avail->idx until all of the descriptors
* the guest has written are valid (including all their
* next fields and vd_flags).
*
* Compute (last_avail - idx) in integers mod 2**16. This is
* the number of descriptors the device has made available
* since the last time we updated vq->last_avail.
*
* We just need to do the subtraction as an unsigned int,
* then trim off excess bits.
*/
idx = vq->last_avail;
ndesc = (uint16_t)((u_int)vq->avail->idx - idx);
if (ndesc == 0)
return 0;
if (ndesc > vq->qsize) {
/* XXX need better way to diagnose issues */
fprintf(stderr,
"%s: ndesc (%u) out of range, driver confused?\r\n",
name, (u_int)ndesc);
return -1;
}
/*
* Now count/parse "involved" descriptors starting from
* the head of the chain.
*
* To prevent loops, we could be more complicated and
* check whether we're re-visiting a previously visited
* index, but we just abort if the count gets excessive.
*/
ctx = base->dev->vmctx;
*pidx = next = vq->avail->ring[idx & (vq->qsize - 1)];
vq->last_avail++;
for (i = 0; i < VQ_MAX_DESCRIPTORS; next = vdir->next) {
if (next >= vq->qsize) {
fprintf(stderr,
"%s: descriptor index %u out of range, "
"driver confused?\r\n",
name, next);
return -1;
}
vdir = &vq->desc[next];
if ((vdir->flags & VRING_DESC_F_INDIRECT) == 0) {
_vq_record(i, vdir, ctx, iov, n_iov, flags);
i++;
} else if ((base->vops->hv_caps &
VIRTIO_RING_F_INDIRECT_DESC) == 0) {
fprintf(stderr,
"%s: descriptor has forbidden INDIRECT flag, "
"driver confused?\r\n",
name);
return -1;
} else {
n_indir = vdir->len / 16;
if ((vdir->len & 0xf) || n_indir == 0) {
fprintf(stderr,
"%s: invalid indir len 0x%x, "
"driver confused?\r\n",
name, (u_int)vdir->len);
return -1;
}
vindir = paddr_guest2host(ctx,
vdir->addr, vdir->len);
/*
* Indirects start at the 0th, then follow
* their own embedded "next"s until those run
* out. Each one's indirect flag must be off
* (we don't really have to check, could just
* ignore errors...).
*/
next = 0;
for (;;) {
vp = &vindir[next];
if (vp->flags & VRING_DESC_F_INDIRECT) {
fprintf(stderr,
"%s: indirect desc has INDIR flag,"
" driver confused?\r\n",
name);
return -1;
}
_vq_record(i, vp, ctx, iov, n_iov, flags);
if (++i > VQ_MAX_DESCRIPTORS)
goto loopy;
if ((vp->flags & VRING_DESC_F_NEXT) == 0)
break;
next = vp->next;
if (next >= n_indir) {
fprintf(stderr,
"%s: invalid next %u > %u, "
"driver confused?\r\n",
name, (u_int)next, n_indir);
return -1;
}
}
}
if ((vdir->flags & VRING_DESC_F_NEXT) == 0)
return i;
}
loopy:
fprintf(stderr,
"%s: descriptor loop? count > %d - driver confused?\r\n",
name, i);
return -1;
}
/*
* Return the currently-first request chain back to the available queue.
*
* (This chain is the one you handled when you called vq_getchain()
* and used its positive return value.)
*/
void
vq_retchain(struct virtio_vq_info *vq)
{
vq->last_avail--;
}
/*
* Return specified request chain to the guest, setting its I/O length
* to the provided value.
*
* (This chain is the one you handled when you called vq_getchain()
* and used its positive return value.)
*/
void
vq_relchain(struct virtio_vq_info *vq, uint16_t idx, uint32_t iolen)
{
uint16_t uidx, mask;
volatile struct vring_used *vuh;
volatile struct virtio_used *vue;
/*
* Notes:
* - mask is N-1 where N is a power of 2 so computes x % N
* - vuh points to the "used" data shared with guest
* - vue points to the "used" ring entry we want to update
* - head is the same value we compute in vq_iovecs().
*
* (I apologize for the two fields named idx; the
* virtio spec calls the one that vue points to, "id"...)
*/
mask = vq->qsize - 1;
vuh = vq->used;
uidx = vuh->idx;
vue = &vuh->ring[uidx++ & mask];
vue->idx = idx;
vue->tlen = iolen;
vuh->idx = uidx;
}
/*
* Driver has finished processing "available" chains and calling
* vq_relchain on each one. If driver used all the available
* chains, used_all should be set.
*
* If the "used" index moved we may need to inform the guest, i.e.,
* deliver an interrupt. Even if the used index did NOT move we
* may need to deliver an interrupt, if the avail ring is empty and
* we are supposed to interrupt on empty.
*
* Note that used_all_avail is provided by the caller because it's
* a snapshot of the ring state when he decided to finish interrupt
* processing -- it's possible that descriptors became available after
* that point. (It's also typically a constant 1/True as well.)
*/
void
vq_endchains(struct virtio_vq_info *vq, int used_all_avail)
{
struct virtio_base *base;
uint16_t event_idx, new_idx, old_idx;
int intr;
/*
* Interrupt generation: if we're using EVENT_IDX,
* interrupt if we've crossed the event threshold.
* Otherwise interrupt is generated if we added "used" entries,
* but suppressed by VRING_AVAIL_F_NO_INTERRUPT.
*
* In any case, though, if NOTIFY_ON_EMPTY is set and the
* entire avail was processed, we need to interrupt always.
*/
base = vq->base;
old_idx = vq->save_used;
vq->save_used = new_idx = vq->used->idx;
if (used_all_avail &&
(base->negotiated_caps & VIRTIO_F_NOTIFY_ON_EMPTY))
intr = 1;
else if (base->negotiated_caps & VIRTIO_RING_F_EVENT_IDX) {
event_idx = VQ_USED_EVENT_IDX(vq);
/*
* This calculation is per docs and the kernel
* (see src/sys/dev/virtio/virtio_ring.h).
*/
intr = (uint16_t)(new_idx - event_idx - 1) <
(uint16_t)(new_idx - old_idx);
} else {
intr = new_idx != old_idx &&
!(vq->avail->flags & VRING_AVAIL_F_NO_INTERRUPT);
}
if (intr)
vq_interrupt(base, vq);
}
/* Note: these are in sorted order to make for a fast search */
static struct config_reg {
uint16_t offset; /* register offset */
uint8_t size; /* size (bytes) */
uint8_t ro; /* true => reg is read only */
const char *name; /* name of reg */
} config_regs[] = {
{ VIRTIO_CR_HOSTCAP, 4, 1, "HOSTCAP" },
{ VIRTIO_CR_GUESTCAP, 4, 0, "GUESTCAP" },
{ VIRTIO_CR_PFN, 4, 0, "PFN" },
{ VIRTIO_CR_QNUM, 2, 1, "QNUM" },
{ VIRTIO_CR_QSEL, 2, 0, "QSEL" },
{ VIRTIO_CR_QNOTIFY, 2, 0, "QNOTIFY" },
{ VIRTIO_CR_STATUS, 1, 0, "STATUS" },
{ VIRTIO_CR_ISR, 1, 0, "ISR" },
{ VIRTIO_CR_CFGVEC, 2, 0, "CFGVEC" },
{ VIRTIO_CR_QVEC, 2, 0, "QVEC" },
};
static inline struct config_reg *
virtio_find_cr(int offset) {
u_int hi, lo, mid;
struct config_reg *cr;
lo = 0;
hi = sizeof(config_regs) / sizeof(*config_regs) - 1;
while (hi >= lo) {
mid = (hi + lo) >> 1;
cr = &config_regs[mid];
if (cr->offset == offset)
return cr;
if (cr->offset < offset)
lo = mid + 1;
else
hi = mid - 1;
}
return NULL;
}
/*
* Handle pci config space reads.
* If it's to the MSI-X info, do that.
* If it's part of the virtio standard stuff, do that.
* Otherwise dispatch to the actual driver.
*/
uint64_t
virtio_pci_read(struct vmctx *ctx, int vcpu, struct pci_vdev *dev,
int baridx, uint64_t offset, int size)
{
struct virtio_base *base = dev->arg;
struct virtio_ops *vops;
struct config_reg *cr;
uint64_t virtio_config_size, max;
const char *name;
uint32_t newoff;
uint32_t value;
int error;
if (base->flags & VIRTIO_USE_MSIX) {
if (baridx == pci_msix_table_bar(dev) ||
baridx == pci_msix_pba_bar(dev)) {
return pci_emul_msix_tread(dev, offset, size);
}
}
/* XXX probably should do something better than just assert() */
assert(baridx == 0);
if (base->mtx)
pthread_mutex_lock(base->mtx);
vops = base->vops;
name = vops->name;
value = size == 1 ? 0xff : size == 2 ? 0xffff : 0xffffffff;
if (size != 1 && size != 2 && size != 4)
goto bad;
if (pci_msix_enabled(dev))
virtio_config_size = VIRTIO_CR_CFG1;
else
virtio_config_size = VIRTIO_CR_CFG0;
if (offset >= virtio_config_size) {
/*
* Subtract off the standard size (including MSI-X
* registers if enabled) and dispatch to underlying driver.
* If that fails, fall into general code.
*/
newoff = offset - virtio_config_size;
max = vops->cfgsize ? vops->cfgsize : 0x100000000;
if (newoff + size > max)
goto bad;
error = (*vops->cfgread)(DEV_STRUCT(base), newoff,
size, &value);
if (!error)
goto done;
}
bad:
cr = virtio_find_cr(offset);
if (cr == NULL || cr->size != size) {
if (cr != NULL) {
/* offset must be OK, so size must be bad */
fprintf(stderr,
"%s: read from %s: bad size %d\r\n",
name, cr->name, size);
} else {
fprintf(stderr,
"%s: read from bad offset/size %jd/%d\r\n",
name, (uintmax_t)offset, size);
}
goto done;
}
switch (offset) {
case VIRTIO_CR_HOSTCAP:
value = vops->hv_caps;
break;
case VIRTIO_CR_GUESTCAP:
value = base->negotiated_caps;
break;
case VIRTIO_CR_PFN:
if (base->curq < vops->nvq)
value = base->queues[base->curq].pfn;
break;
case VIRTIO_CR_QNUM:
value = base->curq < vops->nvq ?
base->queues[base->curq].qsize : 0;
break;
case VIRTIO_CR_QSEL:
value = base->curq;
break;
case VIRTIO_CR_QNOTIFY:
value = 0; /* XXX */
break;
case VIRTIO_CR_STATUS:
value = base->status;
break;
case VIRTIO_CR_ISR:
value = base->isr;
base->isr = 0; /* a read clears this flag */
if (value)
pci_lintr_deassert(dev);
break;
case VIRTIO_CR_CFGVEC:
value = base->msix_cfg_idx;
break;
case VIRTIO_CR_QVEC:
value = base->curq < vops->nvq ?
base->queues[base->curq].msix_idx :
VIRTIO_MSI_NO_VECTOR;
break;
}
done:
if (base->mtx)
pthread_mutex_unlock(base->mtx);
return value;
}
/*
* Handle pci config space writes.
* If it's to the MSI-X info, do that.
* If it's part of the virtio standard stuff, do that.
* Otherwise dispatch to the actual driver.
*/
void
virtio_pci_write(struct vmctx *ctx, int vcpu, struct pci_vdev *dev,
int baridx, uint64_t offset, int size, uint64_t value)
{
struct virtio_base *base = dev->arg;
struct virtio_vq_info *vq;
struct virtio_ops *vops;
struct config_reg *cr;
uint64_t virtio_config_size, max;
const char *name;
uint32_t newoff;
int error;
if (base->flags & VIRTIO_USE_MSIX) {
if (baridx == pci_msix_table_bar(dev) ||
baridx == pci_msix_pba_bar(dev)) {
pci_emul_msix_twrite(dev, offset, size, value);
return;
}
}
/* XXX probably should do something better than just assert() */
assert(baridx == 0);
if (base->mtx)
pthread_mutex_lock(base->mtx);
vops = base->vops;
name = vops->name;
if (size != 1 && size != 2 && size != 4)
goto bad;
if (pci_msix_enabled(dev))
virtio_config_size = VIRTIO_CR_CFG1;
else
virtio_config_size = VIRTIO_CR_CFG0;
if (offset >= virtio_config_size) {
/*
* Subtract off the standard size (including MSI-X
* registers if enabled) and dispatch to underlying driver.
*/
newoff = offset - virtio_config_size;
max = vops->cfgsize ? vops->cfgsize : 0x100000000;
if (newoff + size > max)
goto bad;
error = (*vops->cfgwrite)(DEV_STRUCT(base), newoff,
size, value);
if (!error)
goto done;
}
bad:
cr = virtio_find_cr(offset);
if (cr == NULL || cr->size != size || cr->ro) {
if (cr != NULL) {
/* offset must be OK, wrong size and/or reg is R/O */
if (cr->size != size)
fprintf(stderr,
"%s: write to %s: bad size %d\r\n",
name, cr->name, size);
if (cr->ro)
fprintf(stderr,
"%s: write to read-only reg %s\r\n",
name, cr->name);
} else {
fprintf(stderr,
"%s: write to bad offset/size %jd/%d\r\n",
name, (uintmax_t)offset, size);
}
goto done;
}
switch (offset) {
case VIRTIO_CR_GUESTCAP:
base->negotiated_caps = value & vops->hv_caps;
if (vops->apply_features)
(*vops->apply_features)(DEV_STRUCT(base),
base->negotiated_caps);
break;
case VIRTIO_CR_PFN:
if (base->curq >= vops->nvq)
goto bad_qindex;
virtio_vq_init(base, value);
break;
case VIRTIO_CR_QSEL:
/*
* Note that the guest is allowed to select an
* invalid queue; we just need to return a QNUM
* of 0 while the bad queue is selected.
*/
base->curq = value;
break;
case VIRTIO_CR_QNOTIFY:
if (value >= vops->nvq) {
fprintf(stderr, "%s: queue %d notify out of range\r\n",
name, (int)value);
goto done;
}
vq = &base->queues[value];
if (vq->notify)
(*vq->notify)(DEV_STRUCT(base), vq);
else if (vops->qnotify)
(*vops->qnotify)(DEV_STRUCT(base), vq);
else
fprintf(stderr,
"%s: qnotify queue %d: missing vq/vops notify\r\n",
name, (int)value);
break;
case VIRTIO_CR_STATUS:
base->status = value;
if (vops->set_status)
(*vops->set_status)(DEV_STRUCT(base), value);
if (value == 0)
(*vops->reset)(DEV_STRUCT(base));
break;
case VIRTIO_CR_CFGVEC:
base->msix_cfg_idx = value;
break;
case VIRTIO_CR_QVEC:
if (base->curq >= vops->nvq)
goto bad_qindex;
vq = &base->queues[base->curq];
vq->msix_idx = value;
break;
}
goto done;
bad_qindex:
fprintf(stderr,
"%s: write config reg %s: curq %d >= max %d\r\n",
name, cr->name, base->curq, vops->nvq);
done:
if (base->mtx)
pthread_mutex_unlock(base->mtx);
}

View File

@@ -0,0 +1,441 @@
/*-
* Copyright (c) 2011 NetApp, Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD$
*/
#include <sys/cdefs.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <sys/uio.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>
#include <assert.h>
#include <pthread.h>
#include <openssl/md5.h>
#include "dm.h"
#include "pci_core.h"
#include "virtio.h"
#include "block_if.h"
#define VIRTIO_BLK_RINGSZ 64
#define VIRTIO_BLK_S_OK 0
#define VIRTIO_BLK_S_IOERR 1
#define VIRTIO_BLK_S_UNSUPP 2
#define VIRTIO_BLK_BLK_ID_BYTES 20
/* Capability bits */
#define VIRTIO_BLK_F_SEG_MAX (1 << 2) /* Maximum request segments */
#define VIRTIO_BLK_F_BLK_SIZE (1 << 6) /* cfg block size valid */
#define VIRTIO_BLK_F_FLUSH (1 << 9) /* Cache flush support */
#define VIRTIO_BLK_F_TOPOLOGY (1 << 10) /* Optimal I/O alignment */
/*
* Host capabilities
*/
#define VIRTIO_BLK_S_HOSTCAPS \
(VIRTIO_BLK_F_SEG_MAX | \
VIRTIO_BLK_F_BLK_SIZE | \
VIRTIO_BLK_F_FLUSH | \
VIRTIO_BLK_F_TOPOLOGY | \
VIRTIO_RING_F_INDIRECT_DESC) /* indirect descriptors */
/*
* Config space "registers"
*/
struct virtio_blk_config {
uint64_t capacity;
uint32_t size_max;
uint32_t seg_max;
struct {
uint16_t cylinders;
uint8_t heads;
uint8_t sectors;
} geometry;
uint32_t blk_size;
struct {
uint8_t physical_block_exp;
uint8_t alignment_offset;
uint16_t min_io_size;
uint32_t opt_io_size;
} topology;
uint8_t writeback;
} __attribute__((packed));
/*
* Fixed-size block header
*/
struct virtio_blk_hdr {
#define VBH_OP_READ 0
#define VBH_OP_WRITE 1
#define VBH_OP_FLUSH 4
#define VBH_OP_FLUSH_OUT 5
#define VBH_OP_IDENT 8
#define VBH_FLAG_BARRIER 0x80000000 /* OR'ed into type */
uint32_t type;
uint32_t ioprio;
uint64_t sector;
} __attribute__((packed));
/*
* Debug printf
*/
static int virtio_blk_debug;
#define DPRINTF(params) do { if (virtio_blk_debug) printf params; } while (0)
#define WPRINTF(params) (printf params)
struct virtio_blk_ioreq {
struct blockif_req req;
struct virtio_blk *blk;
uint8_t *status;
uint16_t idx;
};
/*
* Per-device struct
*/
struct virtio_blk {
struct virtio_base base;
pthread_mutex_t mtx;
struct virtio_vq_info vq;
struct virtio_blk_config cfg;
struct blockif_ctxt *bc;
char ident[VIRTIO_BLK_BLK_ID_BYTES + 1];
struct virtio_blk_ioreq ios[VIRTIO_BLK_RINGSZ];
};
static void virtio_blk_reset(void *);
static void virtio_blk_notify(void *, struct virtio_vq_info *);
static int virtio_blk_cfgread(void *, int, int, uint32_t *);
static int virtio_blk_cfgwrite(void *, int, int, uint32_t);
static struct virtio_ops virtio_blk_ops = {
"virtio_blk", /* our name */
1, /* we support 1 virtqueue */
sizeof(struct virtio_blk_config), /* config reg size */
virtio_blk_reset, /* reset */
virtio_blk_notify, /* device-wide qnotify */
virtio_blk_cfgread, /* read PCI config */
virtio_blk_cfgwrite, /* write PCI config */
NULL, /* apply negotiated features */
NULL, /* called on guest set status */
VIRTIO_BLK_S_HOSTCAPS, /* our capabilities */
};
static void
virtio_blk_reset(void *vdev)
{
struct virtio_blk *blk = vdev;
DPRINTF(("virtio_blk: device reset requested !\n"));
virtio_reset_dev(&blk->base);
}
static void
virtio_blk_done(struct blockif_req *br, int err)
{
struct virtio_blk_ioreq *io = br->param;
struct virtio_blk *blk = io->blk;
/* convert errno into a virtio block error return */
if (err == EOPNOTSUPP || err == ENOSYS)
*io->status = VIRTIO_BLK_S_UNSUPP;
else if (err != 0)
*io->status = VIRTIO_BLK_S_IOERR;
else
*io->status = VIRTIO_BLK_S_OK;
/*
* Return the descriptor back to the host.
* We wrote 1 byte (our status) to host.
*/
pthread_mutex_lock(&blk->mtx);
vq_relchain(&blk->vq, io->idx, 1);
vq_endchains(&blk->vq, 0);
pthread_mutex_unlock(&blk->mtx);
}
static void
virtio_blk_proc(struct virtio_blk *blk, struct virtio_vq_info *vq)
{
struct virtio_blk_hdr *vbh;
struct virtio_blk_ioreq *io;
int i, n;
int err;
ssize_t iolen;
int writeop, type;
struct iovec iov[BLOCKIF_IOV_MAX + 2];
uint16_t idx, flags[BLOCKIF_IOV_MAX + 2];
n = vq_getchain(vq, &idx, iov, BLOCKIF_IOV_MAX + 2, flags);
/*
* The first descriptor will be the read-only fixed header,
* and the last is for status (hence +2 above and below).
* The remaining iov's are the actual data I/O vectors.
*
* XXX - note - this fails on crash dump, which does a
* VIRTIO_BLK_T_FLUSH with a zero transfer length
*/
assert(n >= 2 && n <= BLOCKIF_IOV_MAX + 2);
io = &blk->ios[idx];
assert((flags[0] & VRING_DESC_F_WRITE) == 0);
assert(iov[0].iov_len == sizeof(struct virtio_blk_hdr));
vbh = iov[0].iov_base;
memcpy(&io->req.iov, &iov[1], sizeof(struct iovec) * (n - 2));
io->req.iovcnt = n - 2;
io->req.offset = vbh->sector * DEV_BSIZE;
io->status = iov[--n].iov_base;
assert(iov[n].iov_len == 1);
assert(flags[n] & VRING_DESC_F_WRITE);
/*
* XXX
* The guest should not be setting the BARRIER flag because
* we don't advertise the capability.
*/
type = vbh->type & ~VBH_FLAG_BARRIER;
writeop = (type == VBH_OP_WRITE);
iolen = 0;
for (i = 1; i < n; i++) {
/*
* - write op implies read-only descriptor,
* - read/ident op implies write-only descriptor,
* therefore test the inverse of the descriptor bit
* to the op.
*/
assert(((flags[i] & VRING_DESC_F_WRITE) == 0) == writeop);
iolen += iov[i].iov_len;
}
io->req.resid = iolen;
DPRINTF(("virtio-block: %s op, %zd bytes, %d segs, offset %ld\n\r",
writeop ? "write" : "read/ident", iolen, i - 1,
io->req.offset));
switch (type) {
case VBH_OP_READ:
err = blockif_read(blk->bc, &io->req);
break;
case VBH_OP_WRITE:
err = blockif_write(blk->bc, &io->req);
break;
case VBH_OP_FLUSH:
case VBH_OP_FLUSH_OUT:
err = blockif_flush(blk->bc, &io->req);
break;
case VBH_OP_IDENT:
/* Assume a single buffer */
/* S/n equal to buffer is not zero-terminated. */
memset(iov[1].iov_base, 0, iov[1].iov_len);
strncpy(iov[1].iov_base, blk->ident,
MIN(iov[1].iov_len, sizeof(blk->ident)));
virtio_blk_done(&io->req, 0);
return;
default:
virtio_blk_done(&io->req, EOPNOTSUPP);
return;
}
assert(err == 0);
}
static void
virtio_blk_notify(void *vdev, struct virtio_vq_info *vq)
{
struct virtio_blk *blk = vdev;
while (vq_has_descs(vq))
virtio_blk_proc(blk, vq);
}
static int
virtio_blk_init(struct vmctx *ctx, struct pci_vdev *dev, char *opts)
{
char bident[sizeof("XX:X:X")];
struct blockif_ctxt *bctxt;
MD5_CTX mdctx;
u_char digest[16];
struct virtio_blk *blk;
off_t size;
int i, sectsz, sts, sto;
pthread_mutexattr_t attr;
int rc;
if (opts == NULL) {
printf("virtio-block: backing device required\n");
return -1;
}
/*
* The supplied backing file has to exist
*/
snprintf(bident, sizeof(bident), "%d:%d", dev->slot, dev->func);
bctxt = blockif_open(opts, bident);
if (bctxt == NULL) {
perror("Could not open backing file");
return -1;
}
size = blockif_size(bctxt);
sectsz = blockif_sectsz(bctxt);
blockif_psectsz(bctxt, &sts, &sto);
blk = calloc(1, sizeof(struct virtio_blk));
if (!blk) {
WPRINTF(("virtio_blk: calloc returns NULL\n"));
return -1;
}
blk->bc = bctxt;
for (i = 0; i < VIRTIO_BLK_RINGSZ; i++) {
struct virtio_blk_ioreq *io = &blk->ios[i];
io->req.callback = virtio_blk_done;
io->req.param = io;
io->blk = blk;
io->idx = i;
}
/* init mutex attribute properly to avoid deadlock */
rc = pthread_mutexattr_init(&attr);
if (rc)
DPRINTF(("mutexattr init failed with erro %d!\n", rc));
rc = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
if (rc)
DPRINTF(("virtio_blk: mutexattr_settype failed with "
"error %d!\n", rc));
rc = pthread_mutex_init(&blk->mtx, &attr);
if (rc)
DPRINTF(("virtio_blk: pthread_mutex_init failed with "
"error %d!\n", rc));
/* init virtio struct and virtqueues */
virtio_linkup(&blk->base, &virtio_blk_ops, blk, dev, &blk->vq);
blk->base.mtx = &blk->mtx;
blk->vq.qsize = VIRTIO_BLK_RINGSZ;
/* blk->vq.vq_notify = we have no per-queue notify */
/*
* Create an identifier for the backing file. Use parts of the
* md5 sum of the filename
*/
MD5_Init(&mdctx);
MD5_Update(&mdctx, opts, strlen(opts));
MD5_Final(digest, &mdctx);
sprintf(blk->ident, "ACRN--%02X%02X-%02X%02X-%02X%02X",
digest[0], digest[1], digest[2], digest[3], digest[4], digest[5]);
/* setup virtio block config space */
blk->cfg.capacity = size / DEV_BSIZE; /* 512-byte units */
blk->cfg.size_max = 0; /* not negotiated */
blk->cfg.seg_max = BLOCKIF_IOV_MAX;
blk->cfg.geometry.cylinders = 0; /* no geometry */
blk->cfg.geometry.heads = 0;
blk->cfg.geometry.sectors = 0;
blk->cfg.blk_size = sectsz;
blk->cfg.topology.physical_block_exp =
(sts > sectsz) ? (ffsll(sts / sectsz) - 1) : 0;
blk->cfg.topology.alignment_offset =
(sto != 0) ? ((sts - sto) / sectsz) : 0;
blk->cfg.topology.min_io_size = 0;
blk->cfg.topology.opt_io_size = 0;
blk->cfg.writeback = 0;
/*
* Should we move some of this into virtio.c? Could
* have the device, class, and subdev_0 as fields in
* the virtio constants structure.
*/
pci_set_cfgdata16(dev, PCIR_DEVICE, VIRTIO_DEV_BLOCK);
pci_set_cfgdata16(dev, PCIR_VENDOR, VIRTIO_VENDOR);
pci_set_cfgdata8(dev, PCIR_CLASS, PCIC_STORAGE);
pci_set_cfgdata16(dev, PCIR_SUBDEV_0, VIRTIO_TYPE_BLOCK);
pci_set_cfgdata16(dev, PCIR_SUBVEND_0, VIRTIO_VENDOR);
if (virtio_interrupt_init(&blk->base, fbsdrun_virtio_msix())) {
blockif_close(blk->bc);
free(blk);
return -1;
}
virtio_set_io_bar(&blk->base, 0);
return 0;
}
static void
virtio_blk_deinit(struct vmctx *ctx, struct pci_vdev *dev, char *opts)
{
struct blockif_ctxt *bctxt;
struct virtio_blk *blk;
if (dev->arg) {
DPRINTF(("virtio_blk: deinit\n"));
blk = (struct virtio_blk *) dev->arg;
bctxt = blk->bc;
blockif_close(bctxt);
free(blk);
}
}
static int
virtio_blk_cfgwrite(void *vdev, int offset, int size, uint32_t value)
{
DPRINTF(("virtio_blk: write to readonly reg %d\n\r", offset));
return -1;
}
static int
virtio_blk_cfgread(void *vdev, int offset, int size, uint32_t *retval)
{
struct virtio_blk *blk = vdev;
void *ptr;
/* our caller has already verified offset and size */
ptr = (uint8_t *)&blk->cfg + offset;
memcpy(retval, ptr, size);
return 0;
}
struct pci_vdev_ops pci_ops_virtio_blk = {
.class_name = "virtio-blk",
.vdev_init = virtio_blk_init,
.vdev_deinit = virtio_blk_deinit,
.vdev_barwrite = virtio_pci_write,
.vdev_barread = virtio_pci_read
};
DEFINE_PCI_DEVTYPE(pci_ops_virtio_blk);

View File

@@ -0,0 +1,937 @@
/*-
* Copyright (c) 2016 iXsystems Inc.
* All rights reserved.
*
* This software was developed by Jakub Klama <jceel@FreeBSD.org>
* under sponsorship from iXsystems Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer
* in this position and unchanged.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/cdefs.h>
#include <sys/param.h>
#include <sys/uio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <unistd.h>
#include <assert.h>
#include <pthread.h>
#include <libgen.h>
#include <sysexits.h>
#include <termios.h>
#include "dm.h"
#include "pci_core.h"
#include "virtio.h"
#include "mevent.h"
#define VIRTIO_CONSOLE_RINGSZ 64
#define VIRTIO_CONSOLE_MAXPORTS 16
#define VIRTIO_CONSOLE_MAXQ (VIRTIO_CONSOLE_MAXPORTS * 2 + 2)
#define VIRTIO_CONSOLE_DEVICE_READY 0
#define VIRTIO_CONSOLE_DEVICE_ADD 1
#define VIRTIO_CONSOLE_DEVICE_REMOVE 2
#define VIRTIO_CONSOLE_PORT_READY 3
#define VIRTIO_CONSOLE_CONSOLE_PORT 4
#define VIRTIO_CONSOLE_CONSOLE_RESIZE 5
#define VIRTIO_CONSOLE_PORT_OPEN 6
#define VIRTIO_CONSOLE_PORT_NAME 7
#define VIRTIO_CONSOLE_F_SIZE 0
#define VIRTIO_CONSOLE_F_MULTIPORT 1
#define VIRTIO_CONSOLE_F_EMERG_WRITE 2
#define VIRTIO_CONSOLE_S_HOSTCAPS \
(VIRTIO_CONSOLE_F_SIZE | \
VIRTIO_CONSOLE_F_MULTIPORT | \
VIRTIO_CONSOLE_F_EMERG_WRITE)
static int virtio_console_debug;
#define DPRINTF(params) do { \
if (virtio_console_debug) \
printf params; \
} while (0)
#define WPRINTF(params) (printf params)
struct virtio_console;
struct virtio_console_port;
struct virtio_console_config;
typedef void (virtio_console_cb_t)(struct virtio_console_port *, void *,
struct iovec *, int);
enum virtio_console_be_type {
VIRTIO_CONSOLE_BE_STDIO = 0,
VIRTIO_CONSOLE_BE_TTY,
VIRTIO_CONSOLE_BE_PTY,
VIRTIO_CONSOLE_BE_FILE,
VIRTIO_CONSOLE_BE_MAX,
VIRTIO_CONSOLE_BE_INVALID = VIRTIO_CONSOLE_BE_MAX
};
struct virtio_console_port {
struct virtio_console *console;
int id;
const char *name;
bool enabled;
bool is_console;
bool rx_ready;
bool open;
int rxq;
int txq;
void *arg;
virtio_console_cb_t *cb;
};
struct virtio_console_backend {
struct virtio_console_port *port;
struct mevent *evp;
int fd;
bool open;
enum virtio_console_be_type be_type;
int pts_fd; /* only valid for PTY */
};
struct virtio_console {
struct virtio_base base;
struct virtio_vq_info queues[VIRTIO_CONSOLE_MAXQ];
pthread_mutex_t mtx;
uint64_t cfg;
uint64_t features;
int nports;
bool ready;
struct virtio_console_port control_port;
struct virtio_console_port ports[VIRTIO_CONSOLE_MAXPORTS];
struct virtio_console_config *config;
};
struct virtio_console_config {
uint16_t cols;
uint16_t rows;
uint32_t max_nr_ports;
uint32_t emerg_wr;
} __attribute__((packed));
struct virtio_console_control {
uint32_t id;
uint16_t event;
uint16_t value;
} __attribute__((packed));
struct virtio_console_console_resize {
uint16_t cols;
uint16_t rows;
} __attribute__((packed));
static void virtio_console_reset(void *);
static void virtio_console_notify_rx(void *, struct virtio_vq_info *);
static void virtio_console_notify_tx(void *, struct virtio_vq_info *);
static int virtio_console_cfgread(void *, int, int, uint32_t *);
static int virtio_console_cfgwrite(void *, int, int, uint32_t);
static void virtio_console_neg_features(void *, uint64_t);
static void virtio_console_control_send(struct virtio_console *,
struct virtio_console_control *, const void *, size_t);
static void virtio_console_announce_port(struct virtio_console_port *);
static void virtio_console_open_port(struct virtio_console_port *, bool);
static struct virtio_ops virtio_console_ops = {
"vtcon", /* our name */
VIRTIO_CONSOLE_MAXQ, /* we support VTCON_MAXQ virtqueues */
sizeof(struct virtio_console_config), /* config reg size */
virtio_console_reset, /* reset */
NULL, /* device-wide qnotify */
virtio_console_cfgread, /* read virtio config */
virtio_console_cfgwrite, /* write virtio config */
virtio_console_neg_features, /* apply negotiated features */
NULL, /* called on guest set status */
VIRTIO_CONSOLE_S_HOSTCAPS, /* our capabilities */
};
static const char *virtio_console_be_table[VIRTIO_CONSOLE_BE_MAX] = {
[VIRTIO_CONSOLE_BE_STDIO] = "stdio",
[VIRTIO_CONSOLE_BE_TTY] = "tty",
[VIRTIO_CONSOLE_BE_PTY] = "pty",
[VIRTIO_CONSOLE_BE_FILE] = "file"
};
static struct termios virtio_console_saved_tio;
static int virtio_console_saved_flags;
static void
virtio_console_reset(void *vdev)
{
struct virtio_console *console;
console = vdev;
DPRINTF(("vtcon: device reset requested!\n"));
virtio_reset_dev(&console->base);
}
static void
virtio_console_neg_features(void *vdev, uint64_t negotiated_features)
{
struct virtio_console *console = vdev;
console->features = negotiated_features;
}
static int
virtio_console_cfgread(void *vdev, int offset, int size, uint32_t *retval)
{
struct virtio_console *console = vdev;
void *ptr;
ptr = (uint8_t *)console->config + offset;
memcpy(retval, ptr, size);
return 0;
}
static int
virtio_console_cfgwrite(void *vdev, int offset, int size, uint32_t val)
{
return 0;
}
static inline struct virtio_console_port *
virtio_console_vq_to_port(struct virtio_console *console,
struct virtio_vq_info *vq)
{
uint16_t num = vq->num;
if (num == 0 || num == 1)
return &console->ports[0];
if (num == 2 || num == 3)
return &console->control_port;
return &console->ports[(num / 2) - 1];
}
static inline struct virtio_vq_info *
virtio_console_port_to_vq(struct virtio_console_port *port, bool tx_queue)
{
int qnum;
qnum = tx_queue ? port->txq : port->rxq;
return &port->console->queues[qnum];
}
static struct virtio_console_port *
virtio_console_add_port(struct virtio_console *console, const char *name,
virtio_console_cb_t *cb, void *arg, bool is_console)
{
struct virtio_console_port *port;
if (console->nports == VIRTIO_CONSOLE_MAXPORTS) {
errno = EBUSY;
return NULL;
}
port = &console->ports[console->nports++];
port->id = console->nports - 1;
port->console = console;
port->name = name;
port->cb = cb;
port->arg = arg;
port->is_console = is_console;
if (port->id == 0) {
/* port0 */
port->txq = 0;
port->rxq = 1;
} else {
port->txq = console->nports * 2;
port->rxq = port->txq + 1;
}
port->enabled = true;
return port;
}
static void
virtio_console_control_tx(struct virtio_console_port *port, void *arg,
struct iovec *iov, int niov)
{
struct virtio_console *console;
struct virtio_console_port *tmp;
struct virtio_console_control resp, *ctrl;
int i;
assert(niov == 1);
console = port->console;
ctrl = (struct virtio_console_control *)iov->iov_base;
switch (ctrl->event) {
case VIRTIO_CONSOLE_DEVICE_READY:
console->ready = true;
/* set port ready events for registered ports */
for (i = 0; i < VIRTIO_CONSOLE_MAXPORTS; i++) {
tmp = &console->ports[i];
if (tmp->enabled)
virtio_console_announce_port(tmp);
if (tmp->open)
virtio_console_open_port(tmp, true);
}
break;
case VIRTIO_CONSOLE_PORT_READY:
if (ctrl->id >= console->nports) {
WPRINTF(("VTCONSOLE_PORT_READY for unknown port %d\n",
ctrl->id));
return;
}
tmp = &console->ports[ctrl->id];
if (tmp->is_console) {
resp.event = VIRTIO_CONSOLE_CONSOLE_PORT;
resp.id = ctrl->id;
resp.value = 1;
virtio_console_control_send(console, &resp, NULL, 0);
}
break;
}
}
static void
virtio_console_announce_port(struct virtio_console_port *port)
{
struct virtio_console_control event;
event.id = port->id;
event.event = VIRTIO_CONSOLE_DEVICE_ADD;
event.value = 1;
virtio_console_control_send(port->console, &event, NULL, 0);
event.event = VIRTIO_CONSOLE_PORT_NAME;
virtio_console_control_send(port->console, &event, port->name,
strlen(port->name));
}
static void
virtio_console_open_port(struct virtio_console_port *port, bool open)
{
struct virtio_console_control event;
if (!port->console->ready) {
port->open = true;
return;
}
event.id = port->id;
event.event = VIRTIO_CONSOLE_PORT_OPEN;
event.value = (int)open;
virtio_console_control_send(port->console, &event, NULL, 0);
}
static void
virtio_console_control_send(struct virtio_console *console,
struct virtio_console_control *ctrl,
const void *payload, size_t len)
{
struct virtio_vq_info *vq;
struct iovec iov;
uint16_t idx;
int n;
vq = virtio_console_port_to_vq(&console->control_port, true);
if (!vq_has_descs(vq))
return;
n = vq_getchain(vq, &idx, &iov, 1, NULL);
assert(n == 1);
memcpy(iov.iov_base, ctrl, sizeof(struct virtio_console_control));
if (payload != NULL && len > 0)
memcpy(iov.iov_base + sizeof(struct virtio_console_control),
payload, len);
vq_relchain(vq, idx, sizeof(struct virtio_console_control) + len);
vq_endchains(vq, 1);
}
static void
virtio_console_notify_tx(void *vdev, struct virtio_vq_info *vq)
{
struct virtio_console *console;
struct virtio_console_port *port;
struct iovec iov[1];
uint16_t idx;
uint16_t flags[8];
console = vdev;
port = virtio_console_vq_to_port(console, vq);
while (vq_has_descs(vq)) {
vq_getchain(vq, &idx, iov, 1, flags);
if (port != NULL)
port->cb(port, port->arg, iov, 1);
/*
* Release this chain and handle more
*/
vq_relchain(vq, idx, 0);
}
vq_endchains(vq, 1); /* Generate interrupt if appropriate. */
}
static void
virtio_console_notify_rx(void *vdev, struct virtio_vq_info *vq)
{
struct virtio_console *console;
struct virtio_console_port *port;
console = vdev;
port = virtio_console_vq_to_port(console, vq);
if (!port->rx_ready) {
port->rx_ready = 1;
vq->used->flags |= VRING_USED_F_NO_NOTIFY;
}
}
static void
virtio_console_reset_backend(struct virtio_console_backend *be)
{
if (!be)
return;
if (be->fd != STDIN_FILENO)
mevent_delete_close(be->evp);
else
mevent_delete(be->evp);
if (be->be_type == VIRTIO_CONSOLE_BE_PTY && be->pts_fd > 0) {
close(be->pts_fd);
be->pts_fd = -1;
}
be->evp = NULL;
be->fd = -1;
be->open = false;
}
static void
virtio_console_backend_read(int fd __attribute__((unused)),
enum ev_type t __attribute__((unused)),
void *arg)
{
struct virtio_console_port *port;
struct virtio_console_backend *be = arg;
struct virtio_vq_info *vq;
struct iovec iov;
static char dummybuf[2048];
int len, n;
uint16_t idx;
port = be->port;
vq = virtio_console_port_to_vq(port, true);
if (!be->open || !port->rx_ready) {
len = read(be->fd, dummybuf, sizeof(dummybuf));
if (len == 0)
goto close;
return;
}
if (!vq_has_descs(vq)) {
len = read(be->fd, dummybuf, sizeof(dummybuf));
vq_endchains(vq, 1);
if (len == 0)
goto close;
return;
}
do {
n = vq_getchain(vq, &idx, &iov, 1, NULL);
len = readv(be->fd, &iov, n);
if (len <= 0) {
vq_retchain(vq);
vq_endchains(vq, 0);
/* no data available */
if (len == -1 && errno == EAGAIN)
return;
/* any other errors */
goto close;
}
vq_relchain(vq, idx, len);
} while (vq_has_descs(vq));
vq_endchains(vq, 1);
close:
virtio_console_reset_backend(be);
WPRINTF(("vtcon: be read failed and close! len = %d, errno = %d\n",
len, errno));
}
static void
virtio_console_backend_write(struct virtio_console_port *port, void *arg,
struct iovec *iov, int niov)
{
struct virtio_console_backend *be;
int ret;
be = arg;
if (be->fd == -1)
return;
ret = writev(be->fd, iov, niov);
if (ret <= 0) {
/* backend cannot receive more data. For example when pts is
* not connected to any client, its tty buffer will become full.
* In this case we just drop data from guest hvc console.
*/
if (ret == -1 && errno == EAGAIN)
return;
virtio_console_reset_backend(be);
WPRINTF(("vtcon: be write failed! errno = %d\n", errno));
}
}
static void
virtio_console_restore_stdio(void)
{
tcsetattr(STDIN_FILENO, TCSANOW, &virtio_console_saved_tio);
fcntl(STDIN_FILENO, F_SETFL, virtio_console_saved_flags);
stdio_in_use = false;
}
static bool
virtio_console_backend_can_read(enum virtio_console_be_type be_type)
{
return (be_type == VIRTIO_CONSOLE_BE_FILE) ? false : true;
}
static int
virtio_console_open_backend(const char *path,
enum virtio_console_be_type be_type)
{
int fd = -1;
switch (be_type) {
case VIRTIO_CONSOLE_BE_PTY:
fd = posix_openpt(O_RDWR | O_NOCTTY);
if (fd == -1)
WPRINTF(("vtcon: posix_openpt failed, errno = %d\n",
errno));
else if (grantpt(fd) == -1 || unlockpt(fd) == -1) {
WPRINTF(("vtcon: grant/unlock failed, errno = %d\n",
errno));
close(fd);
fd = -1;
}
break;
case VIRTIO_CONSOLE_BE_STDIO:
if (stdio_in_use) {
WPRINTF(("vtcon: stdio is used by other device\n"));
break;
}
fd = STDIN_FILENO;
stdio_in_use = true;
break;
case VIRTIO_CONSOLE_BE_TTY:
fd = open(path, O_RDWR | O_NONBLOCK);
if (fd < 0)
WPRINTF(("vtcon: open failed: %s\n", path));
else if (!isatty(fd)) {
WPRINTF(("vtcon: not a tty: %s\n", path));
close(fd);
fd = -1;
}
break;
case VIRTIO_CONSOLE_BE_FILE:
fd = open(path, O_WRONLY|O_CREAT|O_APPEND|O_NONBLOCK, 0666);
if (fd < 0)
WPRINTF(("vtcon: open failed: %s\n", path));
break;
default:
WPRINTF(("not supported backend %d!\n", be_type));
}
return fd;
}
static int
virtio_console_config_backend(struct virtio_console_backend *be)
{
int fd, flags;
char *pts_name = NULL;
int slave_fd = -1;
struct termios tio, saved_tio;
if (!be || be->fd == -1)
return -1;
fd = be->fd;
switch (be->be_type) {
case VIRTIO_CONSOLE_BE_PTY:
pts_name = ptsname(fd);
if (pts_name == NULL) {
WPRINTF(("vtcon: ptsname return NULL, errno = %d\n",
errno));
return -1;
}
slave_fd = open(pts_name, O_RDWR);
if (slave_fd == -1) {
WPRINTF(("vtcon: slave_fd open failed, errno = %d\n",
errno));
return -1;
}
tcgetattr(slave_fd, &tio);
cfmakeraw(&tio);
tcsetattr(slave_fd, TCSAFLUSH, &tio);
be->pts_fd = slave_fd;
WPRINTF(("***********************************************\n"));
WPRINTF(("virt-console backend redirected to %s\n", pts_name));
WPRINTF(("***********************************************\n"));
flags = fcntl(fd, F_GETFL);
fcntl(fd, F_SETFL, flags | O_NONBLOCK);
break;
case VIRTIO_CONSOLE_BE_TTY:
case VIRTIO_CONSOLE_BE_STDIO:
tcgetattr(fd, &tio);
saved_tio = tio;
cfmakeraw(&tio);
tio.c_cflag |= CLOCAL;
tcsetattr(fd, TCSANOW, &tio);
if (be->be_type == VIRTIO_CONSOLE_BE_STDIO) {
flags = fcntl(fd, F_GETFL);
fcntl(fd, F_SETFL, flags | O_NONBLOCK);
virtio_console_saved_flags = flags;
virtio_console_saved_tio = saved_tio;
atexit(virtio_console_restore_stdio);
}
break;
default:
break; /* nothing to do */
}
return 0;
}
static int
virtio_console_add_backend(struct virtio_console *console,
const char *name, const char *path,
enum virtio_console_be_type be_type,
bool is_console)
{
struct virtio_console_backend *be;
int error = 0, fd = -1;
be = calloc(1, sizeof(struct virtio_console_backend));
if (be == NULL) {
error = -1;
goto out;
}
fd = virtio_console_open_backend(path, be_type);
if (fd < 0) {
error = -1;
goto out;
}
be->fd = fd;
be->be_type = be_type;
if (virtio_console_config_backend(be) < 0) {
WPRINTF(("vtcon: virtio_console_config_backend failed\n"));
error = -1;
goto out;
}
be->port = virtio_console_add_port(console, name,
virtio_console_backend_write, be, is_console);
if (be->port == NULL) {
WPRINTF(("vtcon: virtio_console_add_port failed\n"));
error = -1;
goto out;
}
if (virtio_console_backend_can_read(be_type)) {
be->evp = mevent_add(fd, EVF_READ,
virtio_console_backend_read, be);
if (be->evp == NULL) {
WPRINTF(("vtcon: mevent_add failed\n"));
error = -1;
goto out;
}
}
virtio_console_open_port(be->port, true);
be->open = true;
out:
if (error != 0) {
if (be) {
if (be->evp)
mevent_delete(be->evp);
if (be->port) {
be->port->enabled = false;
be->port->arg = NULL;
}
if (be->be_type == VIRTIO_CONSOLE_BE_PTY &&
be->pts_fd > 0)
close(be->pts_fd);
free(be);
}
if (fd != -1 && fd != STDIN_FILENO)
close(fd);
}
return error;
}
static void
virtio_console_close_backend(struct virtio_console_backend *be)
{
if (!be)
return;
switch (be->be_type) {
case VIRTIO_CONSOLE_BE_PTY:
if (be->pts_fd > 0) {
close(be->pts_fd);
be->pts_fd = -1;
}
break;
case VIRTIO_CONSOLE_BE_STDIO:
virtio_console_restore_stdio();
break;
default:
break;
}
be->fd = -1;
be->open = false;
memset(be->port, 0, sizeof(*be->port));
}
static void
virtio_console_close_all(struct virtio_console *console)
{
int i;
struct virtio_console_port *port;
struct virtio_console_backend *be;
for (i = 0; i < console->nports; i++) {
port = &console->ports[i];
if (!port->enabled)
continue;
be = (struct virtio_console_backend *)port->arg;
if (be) {
virtio_console_close_backend(be);
free(be);
}
}
}
static enum virtio_console_be_type
virtio_console_get_be_type(const char *backend)
{
int i;
for (i = 0; i < VIRTIO_CONSOLE_BE_MAX; i++)
if (strcasecmp(backend, virtio_console_be_table[i]) == 0)
return i;
return VIRTIO_CONSOLE_BE_INVALID;
}
static int
virtio_console_init(struct vmctx *ctx, struct pci_vdev *dev, char *opts)
{
struct virtio_console *console;
char *backend = NULL;
char *portname = NULL;
char *portpath = NULL;
char *opt;
int i;
pthread_mutexattr_t attr;
enum virtio_console_be_type be_type;
bool is_console = false;
int rc;
if (!opts) {
WPRINTF(("vtcon: invalid opts\n"));
return -1;
}
console = calloc(1, sizeof(struct virtio_console));
if (!console) {
WPRINTF(("vtcon: calloc returns NULL\n"));
return -1;
}
console->config = calloc(1, sizeof(struct virtio_console_config));
if (!console->config) {
WPRINTF(("vtcon->config: calloc returns NULL\n"));
free(console);
return -1;
}
console->config->max_nr_ports = VIRTIO_CONSOLE_MAXPORTS;
console->config->cols = 80;
console->config->rows = 25;
/* init mutex attribute properly to avoid deadlock */
rc = pthread_mutexattr_init(&attr);
if (rc)
DPRINTF(("mutexattr init failed with erro %d!\n", rc));
rc = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
if (rc)
DPRINTF(("virtio_console: mutexattr_settype failed with "
"error %d!\n", rc));
rc = pthread_mutex_init(&console->mtx, &attr);
if (rc)
DPRINTF(("virtio_console: pthread_mutex_init failed with "
"error %d!\n", rc));
virtio_linkup(&console->base, &virtio_console_ops, console, dev,
console->queues);
console->base.mtx = &console->mtx;
for (i = 0; i < VIRTIO_CONSOLE_MAXQ; i++) {
console->queues[i].qsize = VIRTIO_CONSOLE_RINGSZ;
console->queues[i].notify = i % 2 == 0
? virtio_console_notify_rx
: virtio_console_notify_tx;
}
/* initialize config space */
pci_set_cfgdata16(dev, PCIR_DEVICE, VIRTIO_DEV_CONSOLE);
pci_set_cfgdata16(dev, PCIR_VENDOR, VIRTIO_VENDOR);
pci_set_cfgdata8(dev, PCIR_CLASS, PCIC_SIMPLECOMM);
pci_set_cfgdata16(dev, PCIR_SUBDEV_0, VIRTIO_TYPE_CONSOLE);
pci_set_cfgdata16(dev, PCIR_SUBVEND_0, VIRTIO_VENDOR);
if (virtio_interrupt_init(&console->base, fbsdrun_virtio_msix())) {
if (console) {
if (console->config)
free(console->config);
free(console);
}
return -1;
}
virtio_set_io_bar(&console->base, 0);
/* create control port */
console->control_port.console = console;
console->control_port.txq = 2;
console->control_port.rxq = 3;
console->control_port.cb = virtio_console_control_tx;
console->control_port.enabled = true;
/* virtio-console,[@]stdio|tty|pty|file:portname[=portpath]
* [,[@]stdio|tty|pty|file:portname[=portpath]]
*/
while ((opt = strsep(&opts, ",")) != NULL) {
backend = strsep(&opt, ":");
if (backend == NULL) {
WPRINTF(("vtcon: no backend is specified!\n"));
return -1;
}
if (backend[0] == '@') {
is_console = true;
backend++;
}
be_type = virtio_console_get_be_type(backend);
if (be_type == VIRTIO_CONSOLE_BE_INVALID) {
WPRINTF(("vtcon: invalid backend %s!\n",
backend));
return -1;
}
if (opt != NULL) {
portname = strsep(&opt, "=");
portpath = opt;
if (portpath == NULL
&& be_type != VIRTIO_CONSOLE_BE_STDIO
&& be_type != VIRTIO_CONSOLE_BE_PTY) {
WPRINTF(("vtcon: portpath missing for %s\n",
portname));
return -1;
}
if (virtio_console_add_backend(console, portname,
portpath, be_type, is_console) < 0) {
WPRINTF(("vtcon: add port failed %s\n",
portname));
return -1;
}
}
}
return 0;
}
static void
virtio_console_deinit(struct vmctx *ctx, struct pci_vdev *dev, char *opts)
{
struct virtio_console *console;
console = (struct virtio_console *)dev->arg;
if (console) {
virtio_console_close_all(console);
if (console->config)
free(console->config);
free(console);
}
}
struct pci_vdev_ops pci_ops_virtio_console = {
.class_name = "virtio-console",
.vdev_init = virtio_console_init,
.vdev_deinit = virtio_console_deinit,
.vdev_barwrite = virtio_pci_write,
.vdev_barread = virtio_pci_read
};
DEFINE_PCI_DEVTYPE(pci_ops_virtio_console);

View File

@@ -0,0 +1,385 @@
/*
* Copyright (C) 2018 Intel Corporation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
/*
* virtio hyper dmabuf
* Allows to share data buffers between VMs using dmabuf like interface
*/
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <assert.h>
#include <pthread.h>
#include "dm.h"
#include "pci_core.h"
#include "virtio.h"
#include "virtio_kernel.h"
#include "vmmapi.h"
/*
* Size of queue was chosen experimentaly in a way
* that it allows to run ~20 shared surfaces without
* any delays on hyper dmabuf dirver side due to lack
* of free buffers in queue
*/
#define HYPER_DMABUF_RINGSZ 128
/* Hyper dmabuf uses two queues one for Rx and one for Tx */
#define HYPER_DMABUF_VQ_NUM 2
const char *hyper_dmabuf_vbs_dev_path = "/dev/vbs_hyper_dmabuf";
static int virtio_hyper_dmabuf_debug;
#define DPRINTF(...)\
do {\
if (virtio_hyper_dmabuf_debug)\
printf(__VA_ARGS__);\
} while (0)
#define WPRINTF(...) printf(__VA_ARGS__)
static enum VBS_K_STATUS kstatus = VIRTIO_DEV_INITIAL;
static int vbs_k_hyper_dmabuf_fd = -1;
static struct vbs_dev_info kdev;
static struct vbs_vqs_info kvqs;
struct virtio_hyper_dmabuf {
struct virtio_base base;
struct virtio_vq_info vq[HYPER_DMABUF_VQ_NUM];
pthread_mutex_t mtx;
};
static int virtio_hyper_dmabuf_k_init(void);
static int virtio_hyper_dmabuf_k_start(void);
static int virtio_hyper_dmabuf_k_stop(void);
static int virtio_hyper_dmabuf_k_reset(void);
static int virtio_hyper_dmabuf_k_dev_set(const char *name, int vmid,
int nvq, uint32_t feature,
uint64_t pio_start, uint64_t pio_len);
static int virtio_hyper_dmabuf_k_vq_set(unsigned int nvq, unsigned int idx,
uint16_t qsize,
uint32_t pfn, uint16_t msix_idx,
uint64_t msix_addr, uint32_t msix_data);
static void virtio_hyper_dmabuf_no_notify(void *, struct virtio_vq_info *);
static void virtio_hyper_dmabuf_set_status(void *, uint64_t);
static void virtio_hyper_dmabuf_reset(void *);
static struct virtio_ops virtio_hyper_dmabuf_ops_k = {
"virtio_hyper_dmabuf", /* our name */
HYPER_DMABUF_VQ_NUM, /* we support 2 virtqueue */
0, /* config reg size */
virtio_hyper_dmabuf_reset, /* reset */
virtio_hyper_dmabuf_no_notify, /* device-wide qnotify */
NULL, /* read virtio config */
NULL, /* write virtio config */
NULL, /* apply negotiated features */
virtio_hyper_dmabuf_set_status, /* called on guest set status */
0, /* our capabilities */
};
static int
virtio_hyper_dmabuf_k_init()
{
if (vbs_k_hyper_dmabuf_fd != -1) {
WPRINTF("virtio_hyper_dmabuf: Ooops! Re-entered!!\n");
return -VIRTIO_ERROR_REENTER;
}
vbs_k_hyper_dmabuf_fd = open(hyper_dmabuf_vbs_dev_path, O_RDWR);
if (vbs_k_hyper_dmabuf_fd < 0) {
WPRINTF("virtio_hyper_dmabuf: Failed to open %s!\n",
hyper_dmabuf_vbs_dev_path);
return -VIRTIO_ERROR_FD_OPEN_FAILED;
}
DPRINTF("virtio_hyper_dmabuf: Open %s success!\n",
hyper_dmabuf_vbs_dev_path);
memset(&kdev, 0, sizeof(kdev));
memset(&kvqs, 0, sizeof(kvqs));
return VIRTIO_SUCCESS;
}
static int
virtio_hyper_dmabuf_k_dev_set(const char *name, int vmid, int nvq,
uint32_t feature, uint64_t pio_start,
uint64_t pio_len)
{
/* init kdev */
strncpy(kdev.name, name, VBS_NAME_LEN);
kdev.vmid = vmid;
kdev.nvq = nvq;
kdev.negotiated_features = feature;
kdev.pio_range_start = pio_start;
kdev.pio_range_len = pio_len;
return VIRTIO_SUCCESS;
}
static int
virtio_hyper_dmabuf_k_vq_set(unsigned int nvq, unsigned int idx,
uint16_t qsize, uint32_t pfn,
uint16_t msix_idx, uint64_t msix_addr,
uint32_t msix_data)
{
if (nvq <= idx) {
WPRINTF("virtio_hyper_dmabuf: wrong idx for vq_set!\n");
return -VIRTIO_ERROR_GENERAL;
}
/* init kvqs */
kvqs.nvq = nvq;
kvqs.vqs[idx].qsize = qsize;
kvqs.vqs[idx].pfn = pfn;
kvqs.vqs[idx].msix_idx = msix_idx;
kvqs.vqs[idx].msix_addr = msix_addr;
kvqs.vqs[idx].msix_data = msix_data;
return VIRTIO_SUCCESS;
}
static int
virtio_hyper_dmabuf_k_start(void)
{
if (vbs_kernel_start(vbs_k_hyper_dmabuf_fd, &kdev, &kvqs) < 0) {
WPRINTF("virtio_hyper_dmabuf: Failed in vbs_kernel_start!\n");
return -VIRTIO_ERROR_START;
}
DPRINTF("virtio_hyper_dmabuf: vbs_kernel_started!\n");
return VIRTIO_SUCCESS;
}
static int
virtio_hyper_dmabuf_k_stop(void)
{
return vbs_kernel_stop(vbs_k_hyper_dmabuf_fd);
}
static int
virtio_hyper_dmabuf_k_reset(void)
{
memset(&kdev, 0, sizeof(kdev));
memset(&kvqs, 0, sizeof(kvqs));
return vbs_kernel_reset(vbs_k_hyper_dmabuf_fd);
}
static void
virtio_hyper_dmabuf_reset(void *base)
{
struct virtio_hyper_dmabuf *hyper_dmabuf;
hyper_dmabuf = (struct virtio_hyper_dmabuf *)base;
DPRINTF("virtio_hyper_dmabuf: device reset requested !\n");
virtio_reset_dev(&hyper_dmabuf->base);
if (kstatus == VIRTIO_DEV_STARTED) {
virtio_hyper_dmabuf_k_stop();
virtio_hyper_dmabuf_k_reset();
kstatus = VIRTIO_DEV_INITIAL;
}
}
static void
virtio_hyper_dmabuf_no_notify(void *base, struct virtio_vq_info *vq)
{
}
/*
* This callback gives us a chance to determine the timings
* to kickoff VBS-K initialization
*/
static void
virtio_hyper_dmabuf_set_status(void *base, uint64_t status)
{
struct virtio_hyper_dmabuf *hyper_dmabuf;
int nvq;
struct msix_table_entry *mte;
uint64_t msix_addr = 0;
uint32_t msix_data = 0;
int rc, i, j;
hyper_dmabuf = (struct virtio_hyper_dmabuf *) base;
nvq = hyper_dmabuf->base.vops->nvq;
if (kstatus == VIRTIO_DEV_INIT_SUCCESS &&
(status & VIRTIO_CR_STATUS_DRIVER_OK)) {
/* time to kickoff VBS-K side */
/* init vdev first */
rc = virtio_hyper_dmabuf_k_dev_set(
hyper_dmabuf->base.vops->name,
hyper_dmabuf->base.dev->vmctx->vmid,
nvq,
hyper_dmabuf->base.negotiated_caps,
/* currently we let VBS-K handle
* kick register
*/
hyper_dmabuf->base.dev->bar[0].addr + 16,
2);
for (i = 0; i < nvq; i++) {
if (hyper_dmabuf->vq[i].msix_idx !=
VIRTIO_MSI_NO_VECTOR) {
j = hyper_dmabuf->vq[i].msix_idx;
mte = &hyper_dmabuf->base.dev->msix.table[j];
msix_addr = mte->addr;
msix_data = mte->msg_data;
}
rc = virtio_hyper_dmabuf_k_vq_set(
nvq, i,
hyper_dmabuf->vq[i].qsize,
hyper_dmabuf->vq[i].pfn,
hyper_dmabuf->vq[i].msix_idx,
msix_addr,
msix_data);
if (rc < 0) {
WPRINTF("virtio_hyper_dmabuf:");
WPRINTF("kernel_set_vq");
WPRINTF("failed, i %d ret %d\n", i, rc);
return;
}
}
rc = virtio_hyper_dmabuf_k_start();
if (rc < 0) {
WPRINTF("virtio_hyper_dmabuf:");
WPRINTF("kernel_start() failed\n");
kstatus = VIRTIO_DEV_START_FAILED;
} else {
kstatus = VIRTIO_DEV_STARTED;
}
}
}
static int
virtio_hyper_dmabuf_init(struct vmctx *ctx, struct pci_vdev *dev, char *opts)
{
struct virtio_hyper_dmabuf *hyper_dmabuf;
kstatus = VIRTIO_DEV_PRE_INIT;
pthread_mutexattr_t attr;
int rc;
hyper_dmabuf = calloc(1, sizeof(struct virtio_hyper_dmabuf));
if (!hyper_dmabuf) {
WPRINTF(("virtio_hdma: calloc returns NULL\n"));
return -1;
}
/* init mutex attribute properly */
rc = pthread_mutexattr_init(&attr);
if (rc)
DPRINTF("mutexattr init failed with erro %d!\n", rc);
if (fbsdrun_virtio_msix()) {
rc = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_DEFAULT);
DPRINTF("virtio_msix: mutexattr_settype ");
DPRINTF("failed with error %d!\n", rc);
} else {
rc = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
DPRINTF("virtio_intx: mutexattr_settype ");
DPRINTF("failed with error %d!\n", rc);
}
rc = pthread_mutex_init(&hyper_dmabuf->mtx, &attr);
if (rc)
DPRINTF("mutex init failed with error %d!\n", rc);
virtio_linkup(&hyper_dmabuf->base,
&virtio_hyper_dmabuf_ops_k,
hyper_dmabuf,
dev,
hyper_dmabuf->vq);
rc = virtio_hyper_dmabuf_k_init();
if (rc < 0) {
WPRINTF("virtio_hyper_dmabuf: VBS-K ");
WPRINTF("init failed with error %d!\n", rc);
kstatus = VIRTIO_DEV_INIT_FAILED;
} else {
kstatus = VIRTIO_DEV_INIT_SUCCESS;
}
hyper_dmabuf->base.mtx = &hyper_dmabuf->mtx;
hyper_dmabuf->vq[0].qsize = HYPER_DMABUF_RINGSZ;
hyper_dmabuf->vq[1].qsize = HYPER_DMABUF_RINGSZ;
/* initialize config space */
pci_set_cfgdata16(dev, PCIR_DEVICE, VIRTIO_DEV_HYPERDMABUF);
pci_set_cfgdata16(dev, PCIR_VENDOR, INTEL_VENDOR_ID);
pci_set_cfgdata8(dev, PCIR_CLASS, PCIC_MEMORY);
pci_set_cfgdata16(dev, PCIR_SUBDEV_0, VIRTIO_TYPE_HYPERDMABUF);
pci_set_cfgdata16(dev, PCIR_SUBVEND_0, INTEL_VENDOR_ID);
if (virtio_interrupt_init(&hyper_dmabuf->base, fbsdrun_virtio_msix())) {
if (hyper_dmabuf)
free(hyper_dmabuf);
return -1;
}
virtio_set_io_bar(&hyper_dmabuf->base, 0);
return 0;
}
static void
virtio_hyper_dmabuf_deinit(struct vmctx *ctx, struct pci_vdev *dev, char *opts)
{
if (kstatus == VIRTIO_DEV_STARTED) {
DPRINTF("virtio_hyper_dmabuf: deinitializing\n");
virtio_hyper_dmabuf_k_stop();
virtio_hyper_dmabuf_k_reset();
kstatus = VIRTIO_DEV_INITIAL;
assert(vbs_k_hyper_dmabuf_fd >= 0);
close(vbs_k_hyper_dmabuf_fd);
vbs_k_hyper_dmabuf_fd = -1;
}
if (dev->arg)
free((struct virtio_hyper_dmabuf *)dev->arg);
}
struct pci_vdev_ops pci_ops_virtio_hyper_dmabuf = {
.class_name = "virtio-hyper_dmabuf",
.vdev_init = virtio_hyper_dmabuf_init,
.vdev_deinit = virtio_hyper_dmabuf_deinit,
.vdev_barwrite = virtio_pci_write,
.vdev_barread = virtio_pci_read
};
DEFINE_PCI_DEVTYPE(pci_ops_virtio_hyper_dmabuf);

View File

@@ -0,0 +1,104 @@
/*
* Copyright (C) 2018 Intel Corporation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
/* Routines to notify the VBS-K in kernel */
#include <stdio.h>
#include <sys/ioctl.h>
#include "virtio_kernel.h"
static int virtio_kernel_debug;
#define DPRINTF(params) do { if (virtio_kernel_debug) printf params; } while (0)
#define WPRINTF(params) (printf params)
static int
vbs_dev_info_set(int fd, void *arg)
{
return ioctl(fd, VBS_K_SET_DEV, arg);
}
static int
vbs_vqs_info_set(int fd, void *arg)
{
return ioctl(fd, VBS_K_SET_VQ, arg);
}
/* VBS-K common ops */
/* VBS-K init/reset */
int
vbs_kernel_init(int fd)
{
return VIRTIO_SUCCESS;
}
int
vbs_kernel_reset(int fd)
{
return VIRTIO_SUCCESS;
}
/*
* We need a way to start/stop vbs_k execution since guest might want to
* change the configuration of the virtio device after VBS-K has been
* initialized.
*/
/* VBS-K start/stop */
int
vbs_kernel_start(int fd, struct vbs_dev_info *dev, struct vbs_vqs_info *vqs)
{
int ret;
if (fd < 0) {
WPRINTF(("%s: fd < 0\n", __func__));
return -VIRTIO_ERROR_FD_OPEN_FAILED;
}
ret = vbs_dev_info_set(fd, dev);
if (ret < 0) {
WPRINTF(("vbs_kernel_set_dev failed: ret %d\n", ret));
return ret;
}
ret = vbs_vqs_info_set(fd, vqs);
if (ret < 0) {
WPRINTF(("vbs_kernel_set_vqs failed: ret %d\n", ret));
return ret;
}
return VIRTIO_SUCCESS;
}
int
vbs_kernel_stop(int fd)
{
DPRINTF(("%s\n", __func__));
return VIRTIO_SUCCESS;
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,482 @@
/*-
* Copyright (c) 2014 Nahanni Systems Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer
* in this position and unchanged.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
* virtio entropy device emulation.
* Randomness is sourced from /dev/random which does not block
* once it has been seeded at bootup.
*/
#include <sys/cdefs.h>
#include <sys/param.h>
#include <sys/uio.h>
#include <err.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <assert.h>
#include <pthread.h>
#include <sysexits.h>
#include "dm.h"
#include "pci_core.h"
#include "virtio.h"
#include "virtio_kernel.h"
#include "vmmapi.h" /* for vmctx */
#define VIRTIO_RND_RINGSZ 64
/*
* Per-device struct
*/
struct virtio_rnd {
/* VBS-U variables */
struct virtio_base base;
struct virtio_vq_info vq;
pthread_mutex_t mtx;
uint64_t cfg;
int fd;
/* VBS-K variables */
struct {
enum VBS_K_STATUS status;
int fd;
struct vbs_dev_info dev;
struct vbs_vqs_info vqs;
} vbs_k;
};
static int virtio_rnd_debug;
#define DPRINTF(params) do { if (virtio_rnd_debug) printf params; } while (0)
#define WPRINTF(params) (printf params)
/* VBS-K interface functions */
static int virtio_rnd_kernel_init(struct virtio_rnd *); /* open VBS-K chardev */
static int virtio_rnd_kernel_start(struct virtio_rnd *);
static int virtio_rnd_kernel_stop(struct virtio_rnd *);
static int virtio_rnd_kernel_reset(struct virtio_rnd *);
static int virtio_rnd_kernel_dev_set(struct vbs_dev_info *kdev,
const char *name, int vmid, int nvq,
uint32_t feature, uint64_t pio_start,
uint64_t pio_len);
static int virtio_rnd_kernel_vq_set(struct vbs_vqs_info *kvqs, unsigned int nvq,
unsigned int idx, uint16_t qsize,
uint32_t pfn, uint16_t msix_idx,
uint64_t msix_addr, uint32_t msix_data);
/* VBS-U virtio_ops */
static void virtio_rnd_reset(void *);
static void virtio_rnd_notify(void *, struct virtio_vq_info *);
static struct virtio_ops virtio_rnd_ops = {
"virtio_rnd", /* our name */
1, /* we support 1 virtqueue */
0, /* config reg size */
virtio_rnd_reset, /* reset */
virtio_rnd_notify, /* device-wide qnotify */
NULL, /* read virtio config */
NULL, /* write virtio config */
NULL, /* apply negotiated features */
NULL, /* called on guest set status */
0, /* our capabilities */
};
/* VBS-K virtio_ops */
static void virtio_rnd_k_no_notify(void *, struct virtio_vq_info *);
static void virtio_rnd_k_set_status(void *, uint64_t);
static struct virtio_ops virtio_rnd_ops_k = {
"virtio_rnd", /* our name */
1, /* we support 1 virtqueue */
0, /* config reg size */
virtio_rnd_reset, /* reset */
virtio_rnd_k_no_notify, /* device-wide qnotify */
NULL, /* read virtio config */
NULL, /* write virtio config */
NULL, /* apply negotiated features */
virtio_rnd_k_set_status,/* called on guest set status */
0, /* our capabilities */
};
/* VBS-K interface function implementations */
static void
virtio_rnd_k_no_notify(void *base, struct virtio_vq_info *vq)
{
WPRINTF(("virtio_rnd: VBS-K mode! Should not reach here!!\n"));
}
/*
* This callback gives us a chance to determine the timings
* to kickoff VBS-K initialization
*/
static void
virtio_rnd_k_set_status(void *base, uint64_t status)
{
struct virtio_rnd *rnd;
int nvq;
struct msix_table_entry *mte;
uint64_t msix_addr = 0;
uint32_t msix_data = 0;
int rc, i, j;
rnd = base;
nvq = rnd->base.vops->nvq;
if (rnd->vbs_k.status == VIRTIO_DEV_INIT_SUCCESS &&
(status & VIRTIO_CR_STATUS_DRIVER_OK)) {
/* time to kickoff VBS-K side */
/* init vdev first */
rc = virtio_rnd_kernel_dev_set(&rnd->vbs_k.dev,
rnd->base.vops->name,
rnd->base.dev->vmctx->vmid,
nvq,
rnd->base.negotiated_caps,
/*
* currently we let VBS-K handle
* kick register
*/
rnd->base.dev->bar[0].addr + 16,
2);
for (i = 0; i < nvq; i++) {
if (rnd->vq.msix_idx != VIRTIO_MSI_NO_VECTOR) {
j = rnd->vq.msix_idx;
mte = &rnd->base.dev->msix.table[j];
msix_addr = mte->addr;
msix_data = mte->msg_data;
}
rc = virtio_rnd_kernel_vq_set(&rnd->vbs_k.vqs,
nvq, i,
rnd->vq.qsize,
rnd->vq.pfn,
rnd->vq.msix_idx,
msix_addr,
msix_data);
if (rc < 0) {
WPRINTF(("rnd_kernel_set_vq fail,i %d ret %d\n",
i, rc));
return;
}
}
rc = virtio_rnd_kernel_start(rnd);
if (rc < 0) {
WPRINTF(("virtio_rnd_kernel_start() failed\n"));
rnd->vbs_k.status = VIRTIO_DEV_START_FAILED;
} else {
rnd->vbs_k.status = VIRTIO_DEV_STARTED;
}
}
}
/*
* Called in virtio_rnd_init(), where the initialization of the
* PCIe device emulation is still on the way by device model.
*/
static int
virtio_rnd_kernel_init(struct virtio_rnd *rnd)
{
assert(rnd->vbs_k.fd == 0);
rnd->vbs_k.fd = open("/dev/vbs_rng", O_RDWR);
if (rnd->vbs_k.fd < 0) {
WPRINTF(("Failed to open /dev/vbs_k_rng!\n"));
return -VIRTIO_ERROR_FD_OPEN_FAILED;
}
DPRINTF(("Open /dev/vbs_rng success!\n"));
memset(&rnd->vbs_k.dev, 0, sizeof(struct vbs_dev_info));
memset(&rnd->vbs_k.vqs, 0, sizeof(struct vbs_vqs_info));
return VIRTIO_SUCCESS;
}
static int
virtio_rnd_kernel_dev_set(struct vbs_dev_info *kdev, const char *name,
int vmid, int nvq, uint32_t feature,
uint64_t pio_start, uint64_t pio_len)
{
/* FE driver has set VIRTIO_CONFIG_S_DRIVER_OK */
/* init kdev */
strncpy(kdev->name, name, VBS_NAME_LEN);
kdev->vmid = vmid;
kdev->nvq = nvq;
kdev->negotiated_features = feature;
kdev->pio_range_start = pio_start;
kdev->pio_range_len = pio_len;
return VIRTIO_SUCCESS;
}
static int
virtio_rnd_kernel_vq_set(struct vbs_vqs_info *kvqs, unsigned int nvq,
unsigned int idx, uint16_t qsize, uint32_t pfn,
uint16_t msix_idx, uint64_t msix_addr,
uint32_t msix_data)
{
/* FE driver has set VIRTIO_CONFIG_S_DRIVER_OK */
if (nvq <= idx) {
WPRINTF(("%s: wrong idx!\n", __func__));
return -VIRTIO_ERROR_GENERAL;
}
/* init kvqs */
kvqs->nvq = nvq;
kvqs->vqs[idx].qsize = qsize;
kvqs->vqs[idx].pfn = pfn;
kvqs->vqs[idx].msix_idx = msix_idx;
kvqs->vqs[idx].msix_addr = msix_addr;
kvqs->vqs[idx].msix_data = msix_data;
return VIRTIO_SUCCESS;
}
static int
virtio_rnd_kernel_start(struct virtio_rnd *rnd)
{
if (vbs_kernel_start(rnd->vbs_k.fd,
&rnd->vbs_k.dev,
&rnd->vbs_k.vqs) < 0) {
WPRINTF(("Failed in vbs_k_start!\n"));
return -VIRTIO_ERROR_START;
}
DPRINTF(("vbs_k_started!\n"));
return VIRTIO_SUCCESS;
}
static int
virtio_rnd_kernel_stop(struct virtio_rnd *rnd)
{
/* device specific cleanups here */
return vbs_kernel_stop(rnd->vbs_k.fd);
}
static int
virtio_rnd_kernel_reset(struct virtio_rnd *rnd)
{
memset(&rnd->vbs_k.dev, 0, sizeof(struct vbs_dev_info));
memset(&rnd->vbs_k.vqs, 0, sizeof(struct vbs_vqs_info));
return vbs_kernel_reset(rnd->vbs_k.fd);
}
static void
virtio_rnd_reset(void *base)
{
struct virtio_rnd *rnd;
rnd = base;
DPRINTF(("virtio_rnd: device reset requested !\n"));
virtio_reset_dev(&rnd->base);
DPRINTF(("virtio_rnd: kstatus %d\n", rnd->vbs_k.status));
if (rnd->vbs_k.status == VIRTIO_DEV_STARTED) {
DPRINTF(("virtio_rnd: VBS-K reset requested!\n"));
virtio_rnd_kernel_stop(rnd);
virtio_rnd_kernel_reset(rnd);
rnd->vbs_k.status = VIRTIO_DEV_INITIAL;
}
}
static void
virtio_rnd_notify(void *base, struct virtio_vq_info *vq)
{
struct iovec iov;
struct virtio_rnd *rnd;
int len;
uint16_t idx;
rnd = base;
if (rnd->fd < 0) {
vq_endchains(vq, 0);
return;
}
while (vq_has_descs(vq)) {
vq_getchain(vq, &idx, &iov, 1, NULL);
len = read(rnd->fd, iov.iov_base, iov.iov_len);
DPRINTF(("%s: %d\r\n", __func__, len));
/* Catastrophe if unable to read from /dev/random */
assert(len > 0);
/*
* Release this chain and handle more
*/
vq_relchain(vq, idx, len);
}
vq_endchains(vq, 1); /* Generate interrupt if appropriate. */
}
static int
virtio_rnd_init(struct vmctx *ctx, struct pci_vdev *dev, char *opts)
{
struct virtio_rnd *rnd;
int fd;
int len;
uint8_t v;
pthread_mutexattr_t attr;
int rc;
char *opt;
char *vbs_k_opt = NULL;
enum VBS_K_STATUS kstat = VIRTIO_DEV_INITIAL;
while ((opt = strsep(&opts, ",")) != NULL) {
/* vbs_k_opt should be kernel=on */
vbs_k_opt = strsep(&opt, "=");
DPRINTF(("vbs_k_opt is %s\n", vbs_k_opt));
if (opt != NULL) {
if (strncmp(opt, "on", 2) == 0)
kstat = VIRTIO_DEV_PRE_INIT;
WPRINTF(("virtio_rnd: VBS-K initializing..."));
}
}
/*
* Should always be able to open /dev/random.
*/
fd = open("/dev/random", O_RDONLY | O_NONBLOCK);
assert(fd >= 0);
/*
* Check that device is seeded and non-blocking.
*/
len = read(fd, &v, sizeof(v));
if (len <= 0) {
WPRINTF(("virtio_rnd: /dev/random not ready, read(): %d", len));
return -1;
}
rnd = calloc(1, sizeof(struct virtio_rnd));
if (!rnd) {
WPRINTF(("virtio_rnd: calloc returns NULL\n"));
return -1;
}
rnd->vbs_k.status = kstat;
/* init mutex attribute properly */
rc = pthread_mutexattr_init(&attr);
if (rc)
DPRINTF(("mutexattr init failed with erro %d!\n", rc));
if (fbsdrun_virtio_msix()) {
rc = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_DEFAULT);
if (rc)
DPRINTF(("virtio_msix: mutexattr_settype failed with "
"error %d!\n", rc));
} else {
rc = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
if (rc)
DPRINTF(("virtio_intx: mutexattr_settype failed with "
"error %d!\n", rc));
}
rc = pthread_mutex_init(&rnd->mtx, &attr);
if (rc)
DPRINTF(("mutex init failed with error %d!\n", rc));
if (rnd->vbs_k.status == VIRTIO_DEV_PRE_INIT) {
DPRINTF(("%s: VBS-K option detected!\n", __func__));
virtio_linkup(&rnd->base, &virtio_rnd_ops_k,
rnd, dev, &rnd->vq);
rc = virtio_rnd_kernel_init(rnd);
if (rc < 0) {
WPRINTF(("virtio_rnd: VBS-K init failed,error %d!\n",
rc));
rnd->vbs_k.status = VIRTIO_DEV_INIT_FAILED;
} else {
rnd->vbs_k.status = VIRTIO_DEV_INIT_SUCCESS;
}
}
if (rnd->vbs_k.status == VIRTIO_DEV_INITIAL ||
rnd->vbs_k.status != VIRTIO_DEV_INIT_SUCCESS) {
DPRINTF(("%s: fallback to VBS-U...\n", __func__));
virtio_linkup(&rnd->base, &virtio_rnd_ops, rnd, dev, &rnd->vq);
}
rnd->base.mtx = &rnd->mtx;
rnd->vq.qsize = VIRTIO_RND_RINGSZ;
/* keep /dev/random opened while emulating */
rnd->fd = fd;
/* initialize config space */
pci_set_cfgdata16(dev, PCIR_DEVICE, VIRTIO_DEV_RANDOM);
pci_set_cfgdata16(dev, PCIR_VENDOR, VIRTIO_VENDOR);
pci_set_cfgdata8(dev, PCIR_CLASS, PCIC_CRYPTO);
pci_set_cfgdata16(dev, PCIR_SUBDEV_0, VIRTIO_TYPE_ENTROPY);
pci_set_cfgdata16(dev, PCIR_SUBVEND_0, VIRTIO_VENDOR);
if (virtio_interrupt_init(&rnd->base, fbsdrun_virtio_msix())) {
if (rnd)
free(rnd);
return -1;
}
virtio_set_io_bar(&rnd->base, 0);
return 0;
}
static void
virtio_rnd_deinit(struct vmctx *ctx, struct pci_vdev *dev, char *opts)
{
struct virtio_rnd *rnd;
rnd = dev->arg;
if (rnd == NULL) {
DPRINTF(("%s: rnd is NULL\n", __func__));
return;
}
if (rnd->vbs_k.status == VIRTIO_DEV_STARTED) {
DPRINTF(("%s: deinit virtio_rnd_k!\n", __func__));
virtio_rnd_kernel_stop(rnd);
virtio_rnd_kernel_reset(rnd);
rnd->vbs_k.status = VIRTIO_DEV_INITIAL;
assert(rnd->vbs_k.fd >= 0);
close(rnd->vbs_k.fd);
rnd->vbs_k.fd = -1;
}
DPRINTF(("%s: free struct virtio_rnd!\n", __func__));
free(rnd);
}
struct pci_vdev_ops pci_ops_virtio_rnd = {
.class_name = "virtio-rnd",
.vdev_init = virtio_rnd_init,
.vdev_deinit = virtio_rnd_deinit,
.vdev_barwrite = virtio_pci_write,
.vdev_barread = virtio_pci_read
};
DEFINE_PCI_DEVTYPE(pci_ops_virtio_rnd);