@@ -8,6 +8,56 @@
#include <assert.h>
#include <xen/device_tree_defs.h>
+/*
+ * There is no clear requirements for the total size of Virtio MMIO region.
+ * The size of control registers is 0x100 and device-specific configuration
+ * registers starts at the offset 0x100, however it's size depends on the device
+ * and the driver. Pick the biggest known size at the moment to cover most
+ * of the devices (also consider allowing the user to configure the size via
+ * config file for the one not conforming with the proposed value).
+ */
+#define VIRTIO_MMIO_DEV_SIZE xen_mk_ullong(0x200)
+
+static uint64_t virtio_mmio_base;
+static uint32_t virtio_mmio_irq;
+
+static void init_virtio_mmio_params(void)
+{
+ virtio_mmio_base = GUEST_VIRTIO_MMIO_BASE;
+ virtio_mmio_irq = GUEST_VIRTIO_MMIO_SPI_FIRST;
+}
+
+static uint64_t alloc_virtio_mmio_base(libxl__gc *gc)
+{
+ uint64_t base = virtio_mmio_base;
+
+ /* Make sure we have enough reserved resources */
+ if ((virtio_mmio_base + VIRTIO_MMIO_DEV_SIZE >
+ GUEST_VIRTIO_MMIO_BASE + GUEST_VIRTIO_MMIO_SIZE)) {
+ LOG(ERROR, "Ran out of reserved range for Virtio MMIO BASE 0x%"PRIx64"\n",
+ virtio_mmio_base);
+ return 0;
+ }
+ virtio_mmio_base += VIRTIO_MMIO_DEV_SIZE;
+
+ return base;
+}
+
+static uint32_t alloc_virtio_mmio_irq(libxl__gc *gc)
+{
+ uint32_t irq = virtio_mmio_irq;
+
+ /* Make sure we have enough reserved resources */
+ if (virtio_mmio_irq > GUEST_VIRTIO_MMIO_SPI_LAST) {
+ LOG(ERROR, "Ran out of reserved range for Virtio MMIO IRQ %u\n",
+ virtio_mmio_irq);
+ return 0;
+ }
+ virtio_mmio_irq++;
+
+ return irq;
+}
+
static const char *gicv_to_string(libxl_gic_version gic_version)
{
switch (gic_version) {
@@ -26,8 +76,8 @@ int libxl__arch_domain_prepare_config(libxl__gc *gc,
{
uint32_t nr_spis = 0;
unsigned int i;
- uint32_t vuart_irq;
- bool vuart_enabled = false;
+ uint32_t vuart_irq, virtio_irq = 0;
+ bool vuart_enabled = false, virtio_enabled = false;
/*
* If pl011 vuart is enabled then increment the nr_spis to allow allocation
@@ -39,6 +89,35 @@ int libxl__arch_domain_prepare_config(libxl__gc *gc,
vuart_enabled = true;
}
+ /*
+ * Virtio MMIO params are non-unique across the whole system and must be
+ * initialized for every new guest.
+ */
+ init_virtio_mmio_params();
+ for (i = 0; i < d_config->num_disks; i++) {
+ libxl_device_disk *disk = &d_config->disks[i];
+
+ if (disk->virtio) {
+ disk->base = alloc_virtio_mmio_base(gc);
+ if (!disk->base)
+ return ERROR_FAIL;
+
+ disk->irq = alloc_virtio_mmio_irq(gc);
+ if (!disk->irq)
+ return ERROR_FAIL;
+
+ if (virtio_irq < disk->irq)
+ virtio_irq = disk->irq;
+ virtio_enabled = true;
+
+ LOG(DEBUG, "Allocate Virtio MMIO params for Vdev %s: IRQ %u BASE 0x%"PRIx64,
+ disk->vdev, disk->irq, disk->base);
+ }
+ }
+
+ if (virtio_enabled)
+ nr_spis += (virtio_irq - 32) + 1;
+
for (i = 0; i < d_config->b_info.num_irqs; i++) {
uint32_t irq = d_config->b_info.irqs[i];
uint32_t spi;
@@ -58,6 +137,13 @@ int libxl__arch_domain_prepare_config(libxl__gc *gc,
return ERROR_FAIL;
}
+ /* The same check as for vpl011 */
+ if (virtio_enabled &&
+ (irq >= GUEST_VIRTIO_MMIO_SPI_FIRST && irq <= virtio_irq)) {
+ LOG(ERROR, "Physical IRQ %u conflicting with Virtio MMIO IRQ range\n", irq);
+ return ERROR_FAIL;
+ }
+
if (irq < 32)
continue;
@@ -660,6 +746,38 @@ static int make_vpl011_uart_node(libxl__gc *gc, void *fdt,
return 0;
}
+static int make_virtio_mmio_node(libxl__gc *gc, void *fdt,
+ uint64_t base, uint32_t irq)
+{
+ int res;
+ gic_interrupt intr;
+ /* Placeholder for virtio@ + a 64-bit number + \0 */
+ char buf[24];
+
+ snprintf(buf, sizeof(buf), "virtio@%"PRIx64, base);
+ res = fdt_begin_node(fdt, buf);
+ if (res) return res;
+
+ res = fdt_property_compat(gc, fdt, 1, "virtio,mmio");
+ if (res) return res;
+
+ res = fdt_property_regs(gc, fdt, GUEST_ROOT_ADDRESS_CELLS, GUEST_ROOT_SIZE_CELLS,
+ 1, base, VIRTIO_MMIO_DEV_SIZE);
+ if (res) return res;
+
+ set_interrupt(intr, irq, 0xf, DT_IRQ_TYPE_EDGE_RISING);
+ res = fdt_property_interrupts(gc, fdt, &intr, 1);
+ if (res) return res;
+
+ res = fdt_property(fdt, "dma-coherent", NULL, 0);
+ if (res) return res;
+
+ res = fdt_end_node(fdt);
+ if (res) return res;
+
+ return 0;
+}
+
static const struct arch_info *get_arch_info(libxl__gc *gc,
const struct xc_dom_image *dom)
{
@@ -860,10 +978,14 @@ static int libxl__prepare_dtb(libxl__gc *gc, libxl_domain_build_info *info,
int rc, res;
size_t fdt_size = 0;
int pfdt_size = 0;
+ unsigned int i;
const libxl_version_info *vers;
const struct arch_info *ainfo;
+ libxl_domain_config *d_config =
+ CONTAINER_OF(info, libxl_domain_config, b_info);
+
vers = libxl_get_version_info(CTX);
if (vers == NULL) return ERROR_FAIL;
@@ -963,6 +1085,13 @@ next_resize:
if (info->tee == LIBXL_TEE_TYPE_OPTEE)
FDT( make_optee_node(gc, fdt) );
+ for (i = 0; i < d_config->num_disks; i++) {
+ libxl_device_disk *disk = &d_config->disks[i];
+
+ if (disk->virtio)
+ FDT( make_virtio_mmio_node(gc, fdt, disk->base, disk->irq) );
+ }
+
if (pfdt)
FDT( copy_partial_fdt(gc, fdt, pfdt) );
@@ -394,6 +394,10 @@ typedef uint64_t xen_callback_t;
/* Physical Address Space */
+/* Virtio MMIO mappings */
+#define GUEST_VIRTIO_MMIO_BASE xen_mk_ullong(0x02000000)
+#define GUEST_VIRTIO_MMIO_SIZE xen_mk_ullong(0x00100000)
+
/*
* vGIC mappings: Only one set of mapping is used by the guest.
* Therefore they can overlap.
@@ -458,6 +462,9 @@ typedef uint64_t xen_callback_t;
#define GUEST_VPL011_SPI 32
+#define GUEST_VIRTIO_MMIO_SPI_FIRST 33
+#define GUEST_VIRTIO_MMIO_SPI_LAST 43
+
/* PSCI functions */
#define PSCI_cpu_suspend 0
#define PSCI_cpu_off 1