board: xen: introduce virtio-blk support

Added new xenguest_arm64_virtio_defconfig which
enables support for virtio-blk using various types
of transport like virtio-pci, vrtio-mmio. Currently
supported: up to 2 PCI host bridges and 10 MMIO devices.
Note: DT parsing code was partly taken from pci-uclass.c
Limitation: All memory regions should be
below 4GB address space.

Signed-off-by: Andrii Chepurnyi <andrii_chepurnyi@epam.com>
diff --git a/board/xen/xenguest_arm64/MAINTAINERS b/board/xen/xenguest_arm64/MAINTAINERS
index 264920e..7a31736 100644
--- a/board/xen/xenguest_arm64/MAINTAINERS
+++ b/board/xen/xenguest_arm64/MAINTAINERS
@@ -6,3 +6,4 @@
 F:	doc/board/xen/
 F:	include/configs/xenguest_arm64.h
 F:	configs/xenguest_arm64_defconfig
+F:	configs/xenguest_arm64_virtio_defconfig
diff --git a/board/xen/xenguest_arm64/xenguest_arm64.c b/board/xen/xenguest_arm64/xenguest_arm64.c
index 6e10bba..244070a 100644
--- a/board/xen/xenguest_arm64/xenguest_arm64.c
+++ b/board/xen/xenguest_arm64/xenguest_arm64.c
@@ -8,12 +8,15 @@
  */
 
 #include <common.h>
+#include <log.h>
 #include <cpu_func.h>
 #include <dm.h>
 #include <errno.h>
 #include <malloc.h>
 #include <xen.h>
 #include <asm/global_data.h>
+#include <virtio_types.h>
+#include <virtio.h>
 
 #include <asm/io.h>
 #include <asm/armv8/mmu.h>
@@ -49,7 +52,14 @@
 	return (void *)rom_pointer[0];
 }
 
-#define MAX_MEM_MAP_REGIONS 5
+/*
+ * MAX_MEM_MAP_REGIONS should respect to:
+ * 3 Xen related regions
+ * 6 regions for 2 PCI Host bridges
+ * 10 regions for MMIO devices
+ * 2 memory regions
+ */
+#define MAX_MEM_MAP_REGIONS 22
 static struct mm_region xen_mem_map[MAX_MEM_MAP_REGIONS];
 struct mm_region *mem_map = xen_mem_map;
 
@@ -62,6 +72,93 @@
 
 	return mem;
 }
+
+#ifdef CONFIG_VIRTIO_BLK
+#ifdef CONFIG_VIRTIO_PCI
+static void add_pci_mem_map(const void *blob, int *cnt)
+{
+	struct fdt_resource reg_res;
+	int node = -1, len = 0, cells_per_record = 0, max_regions = 0;
+	int pci_addr_cells = 0, addr_cells = 0, size_cells = 0;
+
+	while ((node = fdt_node_offset_by_prop_value(blob, node, "compatible",
+						     "pci-host-ecam-generic",
+						     sizeof("pci-host-ecam-generic"))) >= 0) {
+		if ((*cnt) >= MAX_MEM_MAP_REGIONS ||
+		    fdt_get_resource(blob, node, "reg", 0, &reg_res) < 0)
+			return;
+
+		xen_mem_map[*cnt].virt = reg_res.start;
+		xen_mem_map[*cnt].phys = reg_res.start;
+		xen_mem_map[*cnt].size = fdt_resource_size(&reg_res);
+		xen_mem_map[*cnt].attrs = (PTE_BLOCK_MEMTYPE(MT_NORMAL) |
+								   PTE_BLOCK_INNER_SHARE);
+		(*cnt)++;
+
+		const u32 *prop = fdt_getprop(blob, node, "ranges", &len);
+
+		if (!prop)
+			return;
+
+		pci_addr_cells =  fdt_address_cells(blob, node);
+		addr_cells = fdt_address_cells(blob, 0);
+		size_cells = fdt_size_cells(blob, node);
+
+		/* PCI addresses are always 3-cells */
+		len /= sizeof(u32);
+		cells_per_record = pci_addr_cells + addr_cells + size_cells;
+		max_regions = len / cells_per_record + CONFIG_NR_DRAM_BANKS;
+
+		for (int i = 0; i < max_regions; i++, len -= cells_per_record) {
+			u64 pci_addr, addr, size;
+			int space_code;
+			u32 flags;
+
+			if (((*cnt) >= MAX_MEM_MAP_REGIONS) || len < cells_per_record)
+				return;
+
+			flags = fdt32_to_cpu(prop[0]);
+			space_code = (flags >> 24) & 3;
+			pci_addr = fdtdec_get_number(prop + 1, 2);
+			prop += pci_addr_cells;
+			addr = fdtdec_get_number(prop, addr_cells);
+			prop += addr_cells;
+			size = fdtdec_get_number(prop, size_cells);
+			prop += size_cells;
+
+			xen_mem_map[*cnt].virt = addr;
+			xen_mem_map[*cnt].phys = addr;
+			xen_mem_map[*cnt].size = size;
+			xen_mem_map[*cnt].attrs = (PTE_BLOCK_MEMTYPE(MT_NORMAL) |
+						   PTE_BLOCK_INNER_SHARE);
+			(*cnt)++;
+		}
+	}
+}
+#endif
+
+#ifdef CONFIG_VIRTIO_MMIO
+static void add_mmio_mem_map(const void *blob, int *cnt)
+{
+	int node = -1;
+	struct fdt_resource reg_res;
+
+	if ((*cnt) >= MAX_MEM_MAP_REGIONS)
+		return;
+	while ((node = fdt_node_offset_by_prop_value(blob, node, "compatible", "virtio,mmio",
+						     sizeof("virtio,mmio"))) >= 0) {
+		if (fdt_get_resource(blob, node, "reg", 0, &reg_res) < 0)
+			return;
+		xen_mem_map[*cnt].virt = reg_res.start;
+		xen_mem_map[*cnt].phys = reg_res.start;
+		xen_mem_map[*cnt].size = roundup(fdt_resource_size(&reg_res), PAGE_SIZE);
+		xen_mem_map[*cnt].attrs = (PTE_BLOCK_MEMTYPE(MT_NORMAL) |
+				PTE_BLOCK_INNER_SHARE);
+		(*cnt)++;
+	}
+}
+#endif
+#endif
 
 static int setup_mem_map(void)
 {
@@ -72,6 +169,7 @@
 	phys_addr_t gnttab_base;
 	phys_size_t gnttab_sz;
 
+	memset(xen_mem_map, 0, sizeof(xen_mem_map));
 	/*
 	 * Add "magic" region which is used by Xen to provide some essentials
 	 * for the guest: we need console and xenstore.
@@ -143,6 +241,14 @@
 		xen_mem_map[i].attrs = (PTE_BLOCK_MEMTYPE(MT_NORMAL) |
 					PTE_BLOCK_INNER_SHARE);
 	}
+#ifdef CONFIG_VIRTIO_BLK
+#ifdef CONFIG_VIRTIO_PCI
+	add_pci_mem_map(blob, &i);
+#endif
+#ifdef CONFIG_VIRTIO_MMIO
+	add_mmio_mem_map(blob, &i);
+#endif
+#endif
 	return 0;
 }