/*
* Copyright (c) 2020 The NetBSD Foundation, Inc.
* Copyright (c) 2012 Stefan Fritsch.
* Copyright (c) 2010 Minoura Makoto.
* 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 ``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 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 <dev/pci/virtioreg.h> /* XXX: move to non-pci */
#include <dev/pci/virtio_pcireg.h>
#define VIRTIO_PRIVATE
#include <dev/pci/virtiovar.h> /* XXX: move to non-pci */
#if defined(__alpha__) || defined(__sparc64__)
/*
* XXX VIRTIO_F_ACCESS_PLATFORM is required for standard PCI DMA
* XXX to work on these platforms, at least by Qemu.
* XXX
* XXX Generalize this later.
*/
#define __NEED_VIRTIO_F_ACCESS_PLATFORM
#endif /* __alpha__ || __sparc64__ */
/*
* For big-endian aarch64/armv7 on QEMU (and most real HW), only CPU cores
* are running in big-endian mode, with all peripheral being configured to
* little-endian mode. Their default bus_space(9) functions forcibly swap
* byte-order. This guarantees that PIO'ed data from pci(4), e.g., are
* correctly handled by bus_space(9), while DMA'ed ones should be swapped
* by hand, in violation of virtio(4) specifications.
*/
static int
virtio_pci_match(device_t parent, cfdata_t match, void *aux)
{
const struct pci_attach_args * const pa = aux;
switch (PCI_VENDOR(pa->pa_id)) {
case PCI_VENDOR_QUMRANET:
/* Transitional devices MUST have a PCI Revision ID of 0. */
if (((PCI_PRODUCT_QUMRANET_VIRTIO_1000 <=
PCI_PRODUCT(pa->pa_id)) &&
(PCI_PRODUCT(pa->pa_id) <=
PCI_PRODUCT_QUMRANET_VIRTIO_103F)) &&
PCI_REVISION(pa->pa_class) == 0)
return 1;
/*
* Non-transitional devices SHOULD have a PCI Revision
* ID of 1 or higher. Drivers MUST match any PCI
* Revision ID value.
*/
if (((PCI_PRODUCT_QUMRANET_VIRTIO_1040 <=
PCI_PRODUCT(pa->pa_id)) &&
(PCI_PRODUCT(pa->pa_id) <=
PCI_PRODUCT_QUMRANET_VIRTIO_107F)) &&
/* XXX: TODO */
PCI_REVISION(pa->pa_class) == 1)
return 1;
break;
}
return 0;
}
static void
virtio_pci_attach(device_t parent, device_t self, void *aux)
{
struct virtio_pci_softc * const psc = device_private(self);
struct virtio_softc * const sc = &psc->sc_sc;
const struct pci_attach_args * const pa = aux;
pci_chipset_tag_t pc = pa->pa_pc;
pcitag_t tag = pa->pa_tag;
int revision;
int ret;
pcireg_t id;
pcireg_t csr;
revision = PCI_REVISION(pa->pa_class);
switch (revision) {
case 0:
/* subsystem ID shows what I am */
id = PCI_SUBSYS_ID(pci_conf_read(pc, tag, PCI_SUBSYS_ID_REG));
break;
case 1:
/* pci product number shows what I am */
id = PCI_PRODUCT(pa->pa_id) - PCI_PRODUCT_QUMRANET_VIRTIO_1040;
break;
default:
aprint_normal(": unknown revision 0x%02x; giving up\n",
revision);
return;
}
sc->sc_dmat = pa->pa_dmat;
if (pci_dma64_available(pa))
sc->sc_dmat = pa->pa_dmat64;
/* attach is dependent on revision */
ret = 0;
if (revision == 1) {
/* try to attach 1.0 */
ret = virtio_pci_attach_10(self, aux);
}
if (ret == 0 && revision == 0) {
/*
* revision 0 means 0.9 only or both 0.9 and 1.0. The
* latter are so-called "Transitional Devices". For
* those devices, we want to use the 1.0 interface if
* possible.
*
* XXX Currently only on platforms that require 1.0
* XXX features, such as VIRTIO_F_ACCESS_PLATFORM.
*/
#ifdef __NEED_VIRTIO_F_ACCESS_PLATFORM
/* First, try to attach 1.0 */
ret = virtio_pci_attach_10(self, aux);
if (ret != 0) {
aprint_error_dev(self,
"VirtIO 1.0 error = %d, falling back to 0.9\n",
ret);
/* Fall back to 0.9. */
ret = virtio_pci_attach_09(self, aux);
}
#else
ret = virtio_pci_attach_09(self, aux);
#endif /* __NEED_VIRTIO_F_ACCESS_PLATFORM */
}
if (ret) {
aprint_error_dev(self, "cannot attach (%d)\n", ret);
return;
}
KASSERT(sc->sc_ops);
/* preset config region */
psc->sc_devcfg_offset = VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI;
if (virtio_pci_adjust_config_region(psc))
return;
/* set our version 0.9 ops */
sc->sc_ops = &virtio_pci_ops_09;
sc->sc_bus_endian = READ_ENDIAN_09;
sc->sc_struct_endian = STRUCT_ENDIAN_09;
return 0;
}
static int
virtio_pci_attach_10(device_t self, void *aux)
{
struct virtio_pci_softc * const psc = device_private(self);
const struct pci_attach_args * const pa = aux;
struct virtio_softc * const sc = &psc->sc_sc;
const pci_chipset_tag_t pc = pa->pa_pc;
const pcitag_t tag = pa->pa_tag;
struct virtio_pci_cap common, isr, device;
struct virtio_pci_notify_cap notify;
int have_device_cfg = 0;
bus_size_t bars[NMAPREG] = { 0 };
int bars_idx[NMAPREG] = { 0 };
struct virtio_pci_cap * const caps[] =
{ &common, &isr, &device, ¬ify.cap };
int i, j, ret = 0;
if (virtio_pci_find_cap(psc, VIRTIO_PCI_CAP_COMMON_CFG,
&common, sizeof(common)))
return ENODEV;
if (virtio_pci_find_cap(psc, VIRTIO_PCI_CAP_NOTIFY_CFG,
¬ify, sizeof(notify)))
return ENODEV;
if (virtio_pci_find_cap(psc, VIRTIO_PCI_CAP_ISR_CFG,
&isr, sizeof(isr)))
return ENODEV;
if (virtio_pci_find_cap(psc, VIRTIO_PCI_CAP_DEVICE_CFG,
&device, sizeof(device)))
memset(&device, 0, sizeof(device));
else
have_device_cfg = 1;
/* Figure out which bars we need to map */
for (i = 0; i < __arraycount(caps); i++) {
int bar = caps[i]->bar;
bus_size_t len = caps[i]->offset + caps[i]->length;
if (caps[i]->length == 0)
continue;
if (bars[bar] < len)
bars[bar] = len;
}
for (i = j = 0; i < __arraycount(bars); i++) {
int reg;
pcireg_t type;
i = bars_idx[isr.bar];
if (bus_space_subregion(psc->sc_bars_iot[i], psc->sc_bars_ioh[i],
isr.offset, isr.length, &psc->sc_isr_ioh)) {
aprint_error_dev(self, "can't map isr i/o space\n");
ret = EIO;
goto err;
}
psc->sc_isr_iosize = isr.length;
psc->sc_isr_iot = psc->sc_bars_iot[i];
i = bars_idx[common.bar];
if (bus_space_subregion(psc->sc_bars_iot[i], psc->sc_bars_ioh[i],
common.offset, common.length, &psc->sc_ioh)) {
aprint_error_dev(self, "can't map common i/o space\n");
ret = EIO;
goto err;
}
psc->sc_iosize = common.length;
psc->sc_iot = psc->sc_bars_iot[i];
psc->sc_sc.sc_version_1 = 1;
/* set our version 1.0 ops */
sc->sc_ops = &virtio_pci_ops_10;
sc->sc_bus_endian = READ_ENDIAN_10;
sc->sc_struct_endian = STRUCT_ENDIAN_10;
return 0;
err:
/* undo our pci_mapreg_map()s */
for (i = 0; i < __arraycount(bars); i++) {
if (psc->sc_bars_iosize[i] == 0)
continue;
bus_space_unmap(psc->sc_bars_iot[i], psc->sc_bars_ioh[i],
psc->sc_bars_iosize[i]);
psc->sc_bars_iosize[i] = 0;
}
return ret;
}
/* v1.0 attach helper */
static int
virtio_pci_find_cap(struct virtio_pci_softc *psc, int cfg_type, void *buf,
int buflen)
{
device_t self = psc->sc_sc.sc_dev;
pci_chipset_tag_t pc = psc->sc_pa.pa_pc;
pcitag_t tag = psc->sc_pa.pa_tag;
unsigned int offset, i, len;
union {
pcireg_t reg[8];
struct virtio_pci_cap vcap;
} *v = buf;
if (buflen < sizeof(struct virtio_pci_cap))
return ERANGE;
if (!pci_get_capability(pc, tag, PCI_CAP_VENDSPEC, &offset,
&v->reg[0]))
return ENOENT;
do {
for (i = 0; i < 4; i++)
v->reg[i] =
le32toh(pci_conf_read(pc, tag, offset + i * 4));
if (v->vcap.cfg_type == cfg_type)
break;
offset = v->vcap.cap_next;
} while (offset != 0);
if (offset == 0)
return ENOENT;
if (v->vcap.cap_len > sizeof(struct virtio_pci_cap)) {
len = roundup(v->vcap.cap_len, sizeof(pcireg_t));
if (len > buflen) {
aprint_error_dev(self, "%s cap too large\n", __func__);
return ERANGE;
}
for (i = 4; i < len / sizeof(pcireg_t); i++)
v->reg[i] =
le32toh(pci_conf_read(pc, tag, offset + i * 4));
}
/* only applicable for v 0.9 but also called for 1.0 */
static int
virtio_pci_adjust_config_region(struct virtio_pci_softc *psc)
{
struct virtio_softc * const sc = &psc->sc_sc;
device_t self = sc->sc_dev;
/*
* By definition little endian only in v1.0. NB: "MAY" in the text
* below refers to "independently" (i.e. the order of accesses) not
* "32-bit" (which is restricted by the earlier "MUST").
*
* 4.1.3.1 Driver Requirements: PCI Device Layout
*
* For device configuration access, the driver MUST use ... 32-bit
* wide and aligned accesses for ... 64-bit wide fields. For 64-bit
* fields, the driver MAY access each of the high and low 32-bit parts
* of the field independently.
*/
static __inline void
virtio_pci_bus_space_write_8(bus_space_tag_t iot, bus_space_handle_t ioh,
bus_size_t offset, uint64_t value)
{
bus_space_write_4(iot, ioh, offset, BUS_ADDR_LO32(value));
bus_space_write_4(iot, ioh, offset + 4, BUS_ADDR_HI32(value));
}
guest_features |= VIRTIO_F_VERSION_1;
#ifdef __NEED_VIRTIO_F_ACCESS_PLATFORM
/* XXX This could use some work. */
guest_features |= VIRTIO_F_ACCESS_PLATFORM;
#endif /* __NEED_VIRTIO_F_ACCESS_PLATFORM */
/* notify on empty is 0.9 only */
guest_features &= ~VIRTIO_F_NOTIFY_ON_EMPTY;
sc->sc_active_features = 0;
/* We need at least two: one for config and the other for queues */
if ((sc->sc_flags & VIRTIO_F_INTR_MSIX) == 0 || nmsix < 2) {
/* Try INTx only */
max_type = PCI_INTR_TYPE_INTX;
counts[PCI_INTR_TYPE_INTX] = 1;
} else {
/* Try MSI-X first and INTx second */
if (ISSET(sc->sc_flags, VIRTIO_F_INTR_PERVQ) &&
sc->sc_nvqs + VIRTIO_MSIX_QUEUE_VECTOR_INDEX <= nmsix) {
nmsix = sc->sc_nvqs + VIRTIO_MSIX_QUEUE_VECTOR_INDEX;
} else {
nmsix = 2;
}