@@ -190,6 +190,16 @@ config STATIC_SHM
help
This option enables statically shared memory on a dom0less system.
+config VIRTIO_PCI
+ bool "Support of virtio-pci transport" if EXPERT
+ depends on ARM_64
+ select HAS_PCI
+ select HAS_VPCI
+ select IOREQ_SERVER
+ default n
+ help
+ This option enables support of virtio-pci transport
+
endmenu
menu "ARM errata workaround via the alternative framework"
@@ -28,9 +28,9 @@
#include <asm/tee/tee.h>
#include <asm/vfp.h>
#include <asm/vgic.h>
+#include <asm/vpci.h>
#include <asm/vtimer.h>
-#include "vpci.h"
#include "vuart.h"
DEFINE_PER_CPU(struct vcpu *, curr_vcpu);
similarity index 75%
rename from xen/arch/arm/vpci.h
rename to xen/arch/arm/include/asm/vpci.h
@@ -30,6 +30,17 @@ static inline unsigned int domain_vpci_get_num_mmio_handlers(struct domain *d)
}
#endif
+#ifdef CONFIG_VIRTIO_PCI
+bool virtio_pci_ioreq_server_get_addr(const struct domain *d,
+ paddr_t gpa, uint64_t *addr);
+#else
+static inline bool virtio_pci_ioreq_server_get_addr(const struct domain *d,
+ paddr_t gpa, uint64_t *addr)
+{
+ return false;
+}
+#endif
+
#endif /* __ARCH_ARM_VPCI_H__ */
/*
@@ -26,6 +26,7 @@ static enum io_state handle_read(const struct mmio_handler *handler,
{
const struct hsr_dabt dabt = info->dabt;
struct cpu_user_regs *regs = guest_cpu_user_regs();
+ int ret;
/*
* Initialize to zero to avoid leaking data if there is an
* implementation error in the emulation (such as not correctly
@@ -33,8 +34,9 @@ static enum io_state handle_read(const struct mmio_handler *handler,
*/
register_t r = 0;
- if ( !handler->ops->read(v, info, &r, handler->priv) )
- return IO_ABORT;
+ ret = handler->ops->read(v, info, &r, handler->priv);
+ if ( ret != IO_HANDLED )
+ return ret != IO_RETRY ? IO_ABORT : ret;
r = sign_extend(dabt, r);
@@ -53,7 +55,7 @@ static enum io_state handle_write(const struct mmio_handler *handler,
ret = handler->ops->write(v, info, get_user_reg(regs, dabt.reg),
handler->priv);
- return ret ? IO_HANDLED : IO_ABORT;
+ return ret != IO_HANDLED && ret != IO_RETRY ? IO_ABORT : ret;
}
/* This function assumes that mmio regions are not overlapped */
@@ -10,6 +10,7 @@
#include <asm/traps.h>
#include <asm/ioreq.h>
+#include <asm/vpci.h>
#include <public/hvm/ioreq.h>
@@ -193,12 +194,24 @@ bool arch_ioreq_server_get_type_addr(const struct domain *d,
uint8_t *type,
uint64_t *addr)
{
+ uint64_t pci_addr;
+
if ( p->type != IOREQ_TYPE_COPY && p->type != IOREQ_TYPE_PIO )
return false;
- *type = (p->type == IOREQ_TYPE_PIO) ?
- XEN_DMOP_IO_RANGE_PORT : XEN_DMOP_IO_RANGE_MEMORY;
- *addr = p->addr;
+ if ( p->type == IOREQ_TYPE_COPY &&
+ virtio_pci_ioreq_server_get_addr(d, p->addr, &pci_addr) )
+ {
+ /* PCI config data cycle */
+ *type = XEN_DMOP_IO_RANGE_PCI;
+ *addr = pci_addr;
+ }
+ else
+ {
+ *type = (p->type == IOREQ_TYPE_PIO) ?
+ XEN_DMOP_IO_RANGE_PORT : XEN_DMOP_IO_RANGE_MEMORY;
+ *addr = p->addr;
+ }
return true;
}
@@ -2,9 +2,12 @@
/*
* xen/arch/arm/vpci.c
*/
+#include <xen/ioreq.h>
#include <xen/sched.h>
+#include <xen/sizes.h>
#include <xen/vpci.h>
+#include <asm/ioreq.h>
#include <asm/mmio.h>
static pci_sbdf_t vpci_sbdf_from_gpa(const struct pci_host_bridge *bridge,
@@ -24,6 +27,27 @@ static pci_sbdf_t vpci_sbdf_from_gpa(const struct pci_host_bridge *bridge,
return sbdf;
}
+bool virtio_pci_ioreq_server_get_addr(const struct domain *d,
+ paddr_t gpa, uint64_t *addr)
+{
+ pci_sbdf_t sbdf;
+
+ if ( !has_vpci(d) )
+ return false;
+
+ if ( gpa < GUEST_VIRTIO_PCI_ECAM_BASE ||
+ gpa >= GUEST_VIRTIO_PCI_ECAM_BASE + GUEST_VIRTIO_PCI_TOTAL_ECAM_SIZE )
+ return false;
+
+ sbdf.sbdf = VPCI_ECAM_BDF((gpa - GUEST_VIRTIO_PCI_ECAM_BASE) %
+ GUEST_VIRTIO_PCI_HOST_ECAM_SIZE);
+ sbdf.seg = (gpa - GUEST_VIRTIO_PCI_ECAM_BASE) /
+ GUEST_VIRTIO_PCI_HOST_ECAM_SIZE;
+ *addr = ((uint64_t)sbdf.sbdf << 32) | ECAM_REG_OFFSET(gpa);
+
+ return true;
+}
+
static int vpci_mmio_read(struct vcpu *v, mmio_info_t *info,
register_t *r, void *p)
{
@@ -36,12 +60,12 @@ static int vpci_mmio_read(struct vcpu *v, mmio_info_t *info,
1U << info->dabt.size, &data) )
{
*r = data;
- return 1;
+ return IO_HANDLED;
}
*r = ~0ul;
- return 0;
+ return IO_ABORT;
}
static int vpci_mmio_write(struct vcpu *v, mmio_info_t *info,
@@ -59,6 +83,61 @@ static const struct mmio_handler_ops vpci_mmio_handler = {
.write = vpci_mmio_write,
};
+#ifdef CONFIG_VIRTIO_PCI
+static int virtio_pci_mmio_read(struct vcpu *v, mmio_info_t *info,
+ register_t *r, void *p)
+{
+ const uint8_t access_size = (1 << info->dabt.size) * 8;
+ const uint64_t access_mask = GENMASK_ULL(access_size - 1, 0);
+ int rc = IO_HANDLED;
+
+ ASSERT(!is_hardware_domain(v->domain));
+
+ if ( domain_has_ioreq_server(v->domain) )
+ {
+ rc = try_fwd_ioserv(guest_cpu_user_regs(), v, info);
+ if ( rc == IO_HANDLED )
+ {
+ *r = v->io.req.data;
+ v->io.req.state = STATE_IOREQ_NONE;
+ return IO_HANDLED;
+ }
+ else if ( rc == IO_UNHANDLED )
+ rc = IO_HANDLED;
+ }
+
+ *r = access_mask;
+ return rc;
+}
+
+static int virtio_pci_mmio_write(struct vcpu *v, mmio_info_t *info,
+ register_t r, void *p)
+{
+ int rc = IO_HANDLED;
+
+ ASSERT(!is_hardware_domain(v->domain));
+
+ if ( domain_has_ioreq_server(v->domain) )
+ {
+ rc = try_fwd_ioserv(guest_cpu_user_regs(), v, info);
+ if ( rc == IO_HANDLED )
+ {
+ v->io.req.state = STATE_IOREQ_NONE;
+ return IO_HANDLED;
+ }
+ else if ( rc == IO_UNHANDLED )
+ rc = IO_HANDLED;
+ }
+
+ return rc;
+}
+
+static const struct mmio_handler_ops virtio_pci_mmio_handler = {
+ .read = virtio_pci_mmio_read,
+ .write = virtio_pci_mmio_write,
+};
+#endif
+
static int vpci_setup_mmio_handler_cb(struct domain *d,
struct pci_host_bridge *bridge)
{
@@ -90,9 +169,17 @@ int domain_vpci_init(struct domain *d)
return ret;
}
else
+ {
register_mmio_handler(d, &vpci_mmio_handler,
GUEST_VPCI_ECAM_BASE, GUEST_VPCI_ECAM_SIZE, NULL);
+#ifdef CONFIG_VIRTIO_PCI
+ register_mmio_handler(d, &virtio_pci_mmio_handler,
+ GUEST_VIRTIO_PCI_ECAM_BASE,
+ GUEST_VIRTIO_PCI_TOTAL_ECAM_SIZE, NULL);
+#endif
+ }
+
return 0;
}
@@ -105,6 +192,8 @@ static int vpci_get_num_handlers_cb(struct domain *d,
unsigned int domain_vpci_get_num_mmio_handlers(struct domain *d)
{
+ unsigned int count;
+
if ( !has_vpci(d) )
return 0;
@@ -125,7 +214,18 @@ unsigned int domain_vpci_get_num_mmio_handlers(struct domain *d)
* For guests each host bridge requires one region to cover the
* configuration space. At the moment, we only expose a single host bridge.
*/
- return 1;
+ count = 1;
+
+ /*
+ * In order to not mix PCI passthrough with virtio-pci features we add
+ * one more region to cover the total configuration space for all possible
+ * host bridges which can serve virtio devices for that guest.
+ * We expose one host bridge per virtio backend domain.
+ */
+ if ( IS_ENABLED(CONFIG_VIRTIO_PCI) )
+ count++;
+
+ return count;
}
/*