aboutsummaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/Kconfig2
-rw-r--r--drivers/Makefile1
-rw-r--r--drivers/block/blk-uclass.c2
-rw-r--r--drivers/pci/Kconfig7
-rw-r--r--drivers/pci/Makefile1
-rw-r--r--drivers/pci/pci-uclass.c42
-rw-r--r--drivers/pci/pcie_iproc.c1287
-rw-r--r--drivers/serial/Kconfig7
-rw-r--r--drivers/serial/Makefile1
-rw-r--r--drivers/serial/serial_xen.c182
-rw-r--r--drivers/usb/gadget/ether.c1
-rw-r--r--drivers/usb/musb-new/linux-compat.h4
-rw-r--r--drivers/xen/Kconfig10
-rw-r--r--drivers/xen/Makefile10
-rw-r--r--drivers/xen/events.c199
-rw-r--r--drivers/xen/gnttab.c216
-rw-r--r--drivers/xen/hypervisor.c252
-rw-r--r--drivers/xen/pvblock.c867
-rw-r--r--drivers/xen/xenbus.c557
19 files changed, 3643 insertions, 5 deletions
diff --git a/drivers/Kconfig b/drivers/Kconfig
index 119e412849f..613669cb381 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -136,6 +136,8 @@ source "drivers/w1-eeprom/Kconfig"
source "drivers/watchdog/Kconfig"
+source "drivers/xen/Kconfig"
+
config PHYS_TO_BUS
bool "Custom physical to bus address mapping"
help
diff --git a/drivers/Makefile b/drivers/Makefile
index 2178871bfb5..33126b2da7b 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -29,6 +29,7 @@ obj-$(CONFIG_$(SPL_)REMOTEPROC) += remoteproc/
obj-$(CONFIG_$(SPL_TPL_)TPM) += tpm/
obj-$(CONFIG_$(SPL_TPL_)ACPI_PMC) += power/acpi_pmc/
obj-$(CONFIG_$(SPL_)BOARD) += board/
+obj-$(CONFIG_XEN) += xen/
ifndef CONFIG_TPL_BUILD
ifdef CONFIG_SPL_BUILD
diff --git a/drivers/block/blk-uclass.c b/drivers/block/blk-uclass.c
index b46a1ac8d21..2fb9f6b765e 100644
--- a/drivers/block/blk-uclass.c
+++ b/drivers/block/blk-uclass.c
@@ -28,6 +28,7 @@ static const char *if_typename_str[IF_TYPE_COUNT] = {
[IF_TYPE_NVME] = "nvme",
[IF_TYPE_EFI] = "efi",
[IF_TYPE_VIRTIO] = "virtio",
+ [IF_TYPE_PVBLOCK] = "pvblock",
};
static enum uclass_id if_type_uclass_id[IF_TYPE_COUNT] = {
@@ -43,6 +44,7 @@ static enum uclass_id if_type_uclass_id[IF_TYPE_COUNT] = {
[IF_TYPE_NVME] = UCLASS_NVME,
[IF_TYPE_EFI] = UCLASS_EFI,
[IF_TYPE_VIRTIO] = UCLASS_VIRTIO,
+ [IF_TYPE_PVBLOCK] = UCLASS_PVBLOCK,
};
static enum if_type if_typename_to_iftype(const char *if_typename)
diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig
index 91065e67f1b..5e0a39396bb 100644
--- a/drivers/pci/Kconfig
+++ b/drivers/pci/Kconfig
@@ -191,6 +191,13 @@ config PCIE_INTEL_FPGA
Say Y here if you want to enable PCIe controller support on Intel
FPGA, example Stratix 10.
+config PCIE_IPROC
+ bool "Iproc PCIe support"
+ depends on DM_PCI
+ help
+ Broadcom iProc PCIe controller driver.
+ Say Y here if you want to enable Broadcom iProc PCIe controller,
+
config PCI_MVEBU
bool "Enable Armada XP/38x PCIe driver"
depends on ARCH_MVEBU
diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile
index 9faebffa488..9db90fb53c5 100644
--- a/drivers/pci/Makefile
+++ b/drivers/pci/Makefile
@@ -30,6 +30,7 @@ obj-$(CONFIG_SH4_PCI) += pci_sh4.o
obj-$(CONFIG_SH7751_PCI) +=pci_sh7751.o
obj-$(CONFIG_SH7780_PCI) +=pci_sh7780.o
obj-$(CONFIG_PCI_TEGRA) += pci_tegra.o
+obj-$(CONFIG_PCIE_IPROC) += pcie_iproc.o
obj-$(CONFIG_PCI_AARDVARK) += pci-aardvark.o
obj-$(CONFIG_PCIE_DW_MVEBU) += pcie_dw_mvebu.o
obj-$(CONFIG_PCIE_FSL) += pcie_fsl.o pcie_fsl_fixup.o
diff --git a/drivers/pci/pci-uclass.c b/drivers/pci/pci-uclass.c
index 834526c5a47..40cc9f1090e 100644
--- a/drivers/pci/pci-uclass.c
+++ b/drivers/pci/pci-uclass.c
@@ -1179,6 +1179,48 @@ ulong pci_conv_size_to_32(ulong old, ulong value, uint offset,
return value;
}
+int pci_get_dma_regions(struct udevice *dev, struct pci_region *memp, int index)
+{
+ int pci_addr_cells, addr_cells, size_cells;
+ int cells_per_record;
+ const u32 *prop;
+ int len;
+ int i = 0;
+
+ prop = ofnode_get_property(dev_ofnode(dev), "dma-ranges", &len);
+ if (!prop) {
+ log_err("PCI: Device '%s': Cannot decode dma-ranges\n",
+ dev->name);
+ return -EINVAL;
+ }
+
+ pci_addr_cells = ofnode_read_simple_addr_cells(dev_ofnode(dev));
+ addr_cells = ofnode_read_simple_addr_cells(dev_ofnode(dev->parent));
+ size_cells = ofnode_read_simple_size_cells(dev_ofnode(dev));
+
+ /* PCI addresses are always 3-cells */
+ len /= sizeof(u32);
+ cells_per_record = pci_addr_cells + addr_cells + size_cells;
+ debug("%s: len=%d, cells_per_record=%d\n", __func__, len,
+ cells_per_record);
+
+ while (len) {
+ memp->bus_start = fdtdec_get_number(prop + 1, 2);
+ prop += pci_addr_cells;
+ memp->phys_start = fdtdec_get_number(prop, addr_cells);
+ prop += addr_cells;
+ memp->size = fdtdec_get_number(prop, size_cells);
+ prop += size_cells;
+
+ if (i == index)
+ return 0;
+ i++;
+ len -= cells_per_record;
+ }
+
+ return -EINVAL;
+}
+
int pci_get_regions(struct udevice *dev, struct pci_region **iop,
struct pci_region **memp, struct pci_region **prefp)
{
diff --git a/drivers/pci/pcie_iproc.c b/drivers/pci/pcie_iproc.c
new file mode 100644
index 00000000000..d77735fcf26
--- /dev/null
+++ b/drivers/pci/pcie_iproc.c
@@ -0,0 +1,1287 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2020 Broadcom
+ *
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <errno.h>
+#include <generic-phy.h>
+#include <pci.h>
+#include <malloc.h>
+#include <asm/io.h>
+#include <dm/device_compat.h>
+#include <linux/log2.h>
+
+#define EP_PERST_SOURCE_SELECT_SHIFT 2
+#define EP_PERST_SOURCE_SELECT BIT(EP_PERST_SOURCE_SELECT_SHIFT)
+#define EP_MODE_SURVIVE_PERST_SHIFT 1
+#define EP_MODE_SURVIVE_PERST BIT(EP_MODE_SURVIVE_PERST_SHIFT)
+#define RC_PCIE_RST_OUTPUT_SHIFT 0
+#define RC_PCIE_RST_OUTPUT BIT(RC_PCIE_RST_OUTPUT_SHIFT)
+
+#define CFG_IND_ADDR_MASK 0x00001ffc
+
+#define CFG_ADDR_BUS_NUM_SHIFT 20
+#define CFG_ADDR_BUS_NUM_MASK 0x0ff00000
+#define CFG_ADDR_DEV_NUM_SHIFT 15
+#define CFG_ADDR_DEV_NUM_MASK 0x000f8000
+#define CFG_ADDR_FUNC_NUM_SHIFT 12
+#define CFG_ADDR_FUNC_NUM_MASK 0x00007000
+#define CFG_ADDR_REG_NUM_SHIFT 2
+#define CFG_ADDR_REG_NUM_MASK 0x00000ffc
+#define CFG_ADDR_CFG_TYPE_SHIFT 0
+#define CFG_ADDR_CFG_TYPE_MASK 0x00000003
+
+#define IPROC_PCI_PM_CAP 0x48
+#define IPROC_PCI_PM_CAP_MASK 0xffff
+#define IPROC_PCI_EXP_CAP 0xac
+
+#define IPROC_PCIE_REG_INVALID 0xffff
+
+#define PCI_EXP_TYPE_ROOT_PORT 0x4 /* Root Port */
+#define PCI_EXP_RTCTL 28 /* Root Control */
+/* CRS Software Visibility capability */
+#define PCI_EXP_RTCAP_CRSVIS 0x0001
+
+#define PCI_EXP_LNKSTA 18 /* Link Status */
+#define PCI_EXP_LNKSTA_NLW 0x03f0 /* Negotiated Link Width */
+
+#define PCIE_PHYLINKUP_SHIFT 3
+#define PCIE_PHYLINKUP BIT(PCIE_PHYLINKUP_SHIFT)
+#define PCIE_DL_ACTIVE_SHIFT 2
+#define PCIE_DL_ACTIVE BIT(PCIE_DL_ACTIVE_SHIFT)
+
+/* derive the enum index of the outbound/inbound mapping registers */
+#define MAP_REG(base_reg, index) ((base_reg) + (index) * 2)
+
+/*
+ * Maximum number of outbound mapping window sizes that can be supported by any
+ * OARR/OMAP mapping pair
+ */
+#define MAX_NUM_OB_WINDOW_SIZES 4
+
+#define OARR_VALID_SHIFT 0
+#define OARR_VALID BIT(OARR_VALID_SHIFT)
+#define OARR_SIZE_CFG_SHIFT 1
+
+/*
+ * Maximum number of inbound mapping region sizes that can be supported by an
+ * IARR
+ */
+#define MAX_NUM_IB_REGION_SIZES 9
+
+#define IMAP_VALID_SHIFT 0
+#define IMAP_VALID BIT(IMAP_VALID_SHIFT)
+
+#define APB_ERR_EN_SHIFT 0
+#define APB_ERR_EN BIT(APB_ERR_EN_SHIFT)
+
+/**
+ * iProc PCIe host registers
+ */
+enum iproc_pcie_reg {
+ /* clock/reset signal control */
+ IPROC_PCIE_CLK_CTRL = 0,
+
+ /*
+ * To allow MSI to be steered to an external MSI controller (e.g., ARM
+ * GICv3 ITS)
+ */
+ IPROC_PCIE_MSI_GIC_MODE,
+
+ /*
+ * IPROC_PCIE_MSI_BASE_ADDR and IPROC_PCIE_MSI_WINDOW_SIZE define the
+ * window where the MSI posted writes are written, for the writes to be
+ * interpreted as MSI writes.
+ */
+ IPROC_PCIE_MSI_BASE_ADDR,
+ IPROC_PCIE_MSI_WINDOW_SIZE,
+
+ /*
+ * To hold the address of the register where the MSI writes are
+ * programed. When ARM GICv3 ITS is used, this should be programmed
+ * with the address of the GITS_TRANSLATER register.
+ */
+ IPROC_PCIE_MSI_ADDR_LO,
+ IPROC_PCIE_MSI_ADDR_HI,
+
+ /* enable MSI */
+ IPROC_PCIE_MSI_EN_CFG,
+
+ /* allow access to root complex configuration space */
+ IPROC_PCIE_CFG_IND_ADDR,
+ IPROC_PCIE_CFG_IND_DATA,
+
+ /* allow access to device configuration space */
+ IPROC_PCIE_CFG_ADDR,
+ IPROC_PCIE_CFG_DATA,
+
+ /* enable INTx */
+ IPROC_PCIE_INTX_EN,
+ IPROC_PCIE_INTX_CSR,
+
+ /* outbound address mapping */
+ IPROC_PCIE_OARR0,
+ IPROC_PCIE_OMAP0,
+ IPROC_PCIE_OARR1,
+ IPROC_PCIE_OMAP1,
+ IPROC_PCIE_OARR2,
+ IPROC_PCIE_OMAP2,
+ IPROC_PCIE_OARR3,
+ IPROC_PCIE_OMAP3,
+
+ /* inbound address mapping */
+ IPROC_PCIE_IARR0,
+ IPROC_PCIE_IMAP0,
+ IPROC_PCIE_IARR1,
+ IPROC_PCIE_IMAP1,
+ IPROC_PCIE_IARR2,
+ IPROC_PCIE_IMAP2,
+ IPROC_PCIE_IARR3,
+ IPROC_PCIE_IMAP3,
+ IPROC_PCIE_IARR4,
+ IPROC_PCIE_IMAP4,
+
+ /* config read status */
+ IPROC_PCIE_CFG_RD_STATUS,
+
+ /* link status */
+ IPROC_PCIE_LINK_STATUS,
+
+ /* enable APB error for unsupported requests */
+ IPROC_PCIE_APB_ERR_EN,
+
+ /* Ordering Mode configuration registers */
+ IPROC_PCIE_ORDERING_CFG,
+ IPROC_PCIE_IMAP0_RO_CONTROL,
+ IPROC_PCIE_IMAP1_RO_CONTROL,
+ IPROC_PCIE_IMAP2_RO_CONTROL,
+ IPROC_PCIE_IMAP3_RO_CONTROL,
+ IPROC_PCIE_IMAP4_RO_CONTROL,
+
+ /* total number of core registers */
+ IPROC_PCIE_MAX_NUM_REG,
+};
+
+/* iProc PCIe PAXB v2 registers */
+static const u16 iproc_pcie_reg_paxb_v2[] = {
+ [IPROC_PCIE_CLK_CTRL] = 0x000,
+ [IPROC_PCIE_CFG_IND_ADDR] = 0x120,
+ [IPROC_PCIE_CFG_IND_DATA] = 0x124,
+ [IPROC_PCIE_CFG_ADDR] = 0x1f8,
+ [IPROC_PCIE_CFG_DATA] = 0x1fc,
+ [IPROC_PCIE_INTX_EN] = 0x330,
+ [IPROC_PCIE_INTX_CSR] = 0x334,
+ [IPROC_PCIE_OARR0] = 0xd20,
+ [IPROC_PCIE_OMAP0] = 0xd40,
+ [IPROC_PCIE_OARR1] = 0xd28,
+ [IPROC_PCIE_OMAP1] = 0xd48,
+ [IPROC_PCIE_OARR2] = 0xd60,
+ [IPROC_PCIE_OMAP2] = 0xd68,
+ [IPROC_PCIE_OARR3] = 0xdf0,
+ [IPROC_PCIE_OMAP3] = 0xdf8,
+ [IPROC_PCIE_IARR0] = 0xd00,
+ [IPROC_PCIE_IMAP0] = 0xc00,
+ [IPROC_PCIE_IARR2] = 0xd10,
+ [IPROC_PCIE_IMAP2] = 0xcc0,
+ [IPROC_PCIE_IARR3] = 0xe00,
+ [IPROC_PCIE_IMAP3] = 0xe08,
+ [IPROC_PCIE_IARR4] = 0xe68,
+ [IPROC_PCIE_IMAP4] = 0xe70,
+ [IPROC_PCIE_CFG_RD_STATUS] = 0xee0,
+ [IPROC_PCIE_LINK_STATUS] = 0xf0c,
+ [IPROC_PCIE_APB_ERR_EN] = 0xf40,
+ [IPROC_PCIE_ORDERING_CFG] = 0x2000,
+ [IPROC_PCIE_IMAP0_RO_CONTROL] = 0x201c,
+ [IPROC_PCIE_IMAP1_RO_CONTROL] = 0x2020,
+ [IPROC_PCIE_IMAP2_RO_CONTROL] = 0x2024,
+ [IPROC_PCIE_IMAP3_RO_CONTROL] = 0x2028,
+ [IPROC_PCIE_IMAP4_RO_CONTROL] = 0x202c,
+};
+
+/* iProc PCIe PAXC v2 registers */
+static const u16 iproc_pcie_reg_paxc_v2[] = {
+ [IPROC_PCIE_MSI_GIC_MODE] = 0x050,
+ [IPROC_PCIE_MSI_BASE_ADDR] = 0x074,
+ [IPROC_PCIE_MSI_WINDOW_SIZE] = 0x078,
+ [IPROC_PCIE_MSI_ADDR_LO] = 0x07c,
+ [IPROC_PCIE_MSI_ADDR_HI] = 0x080,
+ [IPROC_PCIE_MSI_EN_CFG] = 0x09c,
+ [IPROC_PCIE_CFG_IND_ADDR] = 0x1f0,
+ [IPROC_PCIE_CFG_IND_DATA] = 0x1f4,
+ [IPROC_PCIE_CFG_ADDR] = 0x1f8,
+ [IPROC_PCIE_CFG_DATA] = 0x1fc,
+};
+
+/**
+ * List of device IDs of controllers that have corrupted
+ * capability list that require SW fixup
+ */
+static const u16 iproc_pcie_corrupt_cap_did[] = {
+ 0x16cd,
+ 0x16f0,
+ 0xd802,
+ 0xd804
+};
+
+enum iproc_pcie_type {
+ IPROC_PCIE_PAXB_V2,
+ IPROC_PCIE_PAXC,
+ IPROC_PCIE_PAXC_V2,
+};
+
+/**
+ * struct iproc_pcie_ob - iProc PCIe outbound mapping
+ *
+ * @axi_offset: offset from the AXI address to the internal address used by
+ * the iProc PCIe core
+ * @nr_windows: total number of supported outbound mapping windows
+ */
+struct iproc_pcie_ob {
+ resource_size_t axi_offset;
+ unsigned int nr_windows;
+};
+
+/**
+ * struct iproc_pcie_ib - iProc PCIe inbound mapping
+ *
+ * @nr_regions: total number of supported inbound mapping regions
+ */
+struct iproc_pcie_ib {
+ unsigned int nr_regions;
+};
+
+/**
+ * struct iproc_pcie_ob_map - outbound mapping controller specific parameters
+ *
+ * @window_sizes: list of supported outbound mapping window sizes in MB
+ * @nr_sizes: number of supported outbound mapping window sizes
+ */
+struct iproc_pcie_ob_map {
+ resource_size_t window_sizes[MAX_NUM_OB_WINDOW_SIZES];
+ unsigned int nr_sizes;
+};
+
+static const struct iproc_pcie_ob_map paxb_v2_ob_map[] = {
+ {
+ /* OARR0/OMAP0 */
+ .window_sizes = { 128, 256 },
+ .nr_sizes = 2,
+ },
+ {
+ /* OARR1/OMAP1 */
+ .window_sizes = { 128, 256 },
+ .nr_sizes = 2,
+ },
+ {
+ /* OARR2/OMAP2 */
+ .window_sizes = { 128, 256, 512, 1024 },
+ .nr_sizes = 4,
+ },
+ {
+ /* OARR3/OMAP3 */
+ .window_sizes = { 128, 256, 512, 1024 },
+ .nr_sizes = 4,
+ },
+};
+
+/**
+ * iProc PCIe inbound mapping type
+ */
+enum iproc_pcie_ib_map_type {
+ /* for DDR memory */
+ IPROC_PCIE_IB_MAP_MEM = 0,
+
+ /* for device I/O memory */
+ IPROC_PCIE_IB_MAP_IO,
+
+ /* invalid or unused */
+ IPROC_PCIE_IB_MAP_INVALID
+};
+
+/**
+ * struct iproc_pcie_ib_map - inbound mapping controller specific parameters
+ *
+ * @type: inbound mapping region type
+ * @size_unit: inbound mapping region size unit, could be SZ_1K, SZ_1M, or SZ_1G
+ * @region_sizes: list of supported inbound mapping region sizes in KB, MB, or
+ * GB, depedning on the size unit
+ * @nr_sizes: number of supported inbound mapping region sizes
+ * @nr_windows: number of supported inbound mapping windows for the region
+ * @imap_addr_offset: register offset between the upper and lower 32-bit
+ * IMAP address registers
+ * @imap_window_offset: register offset between each IMAP window
+ */
+struct iproc_pcie_ib_map {
+ enum iproc_pcie_ib_map_type type;
+ unsigned int size_unit;
+ resource_size_t region_sizes[MAX_NUM_IB_REGION_SIZES];
+ unsigned int nr_sizes;
+ unsigned int nr_windows;
+ u16 imap_addr_offset;
+ u16 imap_window_offset;
+};
+
+static const struct iproc_pcie_ib_map paxb_v2_ib_map[] = {
+ {
+ /* IARR0/IMAP0 */
+ .type = IPROC_PCIE_IB_MAP_IO,
+ .size_unit = SZ_1K,
+ .region_sizes = { 32 },
+ .nr_sizes = 1,
+ .nr_windows = 8,
+ .imap_addr_offset = 0x40,
+ .imap_window_offset = 0x4,
+ },
+ {
+ /* IARR1/IMAP1 (currently unused) */
+ .type = IPROC_PCIE_IB_MAP_INVALID,
+ },
+ {
+ /* IARR2/IMAP2 */
+ .type = IPROC_PCIE_IB_MAP_MEM,
+ .size_unit = SZ_1M,
+ .region_sizes = { 64, 128, 256, 512, 1024, 2048, 4096, 8192,
+ 16384 },
+ .nr_sizes = 9,
+ .nr_windows = 1,
+ .imap_addr_offset = 0x4,
+ .imap_window_offset = 0x8,
+ },
+ {
+ /* IARR3/IMAP3 */
+ .type = IPROC_PCIE_IB_MAP_MEM,
+ .size_unit = SZ_1G,
+ .region_sizes = { 1, 2, 4, 8, 16, 32 },
+ .nr_sizes = 6,
+ .nr_windows = 8,
+ .imap_addr_offset = 0x4,
+ .imap_window_offset = 0x8,
+ },
+ {
+ /* IARR4/IMAP4 */
+ .type = IPROC_PCIE_IB_MAP_MEM,
+ .size_unit = SZ_1G,
+ .region_sizes = { 32, 64, 128, 256, 512 },
+ .nr_sizes = 5,
+ .nr_windows = 8,
+ .imap_addr_offset = 0x4,
+ .imap_window_offset = 0x8,
+ },
+};
+
+/**
+ * struct iproc_pcie - iproc pcie device instance
+ *
+ * @dev: pointer to pcie udevice
+ * @base: device I/O base address
+ * @type: pci device type, PAXC or PAXB
+ * @reg_offsets: pointer to pcie host register
+ * @fix_paxc_cap: paxc capability
+ * @need_ob_cfg: outbound mapping status
+ * @ob: pcie outbound mapping
+ * @ob_map: pointer to outbound mapping parameters
+ * @need_ib_cfg: inbound mapping status
+ * @ib: pcie inbound mapping
+ * @ib_map: pointer to inbound mapping parameters
+ * @ep_is_internal: ep status
+ * @phy: phy device
+ * @link_is_active: link up status
+ * @has_apb_err_disable: apb error status
+ */
+struct iproc_pcie {
+ struct udevice *dev;
+ void __iomem *base;
+ enum iproc_pcie_type type;
+ u16 *reg_offsets;
+ bool fix_paxc_cap;
+ bool need_ob_cfg;
+ struct iproc_pcie_ob ob;
+ const struct iproc_pcie_ob_map *ob_map;
+ bool need_ib_cfg;
+ struct iproc_pcie_ib ib;
+ const struct iproc_pcie_ib_map *ib_map;
+ bool ep_is_internal;
+ struct phy phy;
+ bool link_is_active;
+ bool has_apb_err_disable;
+};
+
+static inline bool iproc_pcie_reg_is_invalid(u16 reg_offset)
+{
+ return !!(reg_offset == IPROC_PCIE_REG_INVALID);
+}
+
+static inline u16 iproc_pcie_reg_offset(struct iproc_pcie *pcie,
+ enum iproc_pcie_reg reg)
+{
+ return pcie->reg_offsets[reg];
+}
+
+static inline u32 iproc_pcie_read_reg(struct iproc_pcie *pcie,
+ enum iproc_pcie_reg reg)
+{
+ u16 offset = iproc_pcie_reg_offset(pcie, reg);
+
+ if (iproc_pcie_reg_is_invalid(offset))
+ return 0;
+
+ return readl(pcie->base + offset);
+}
+
+static inline void iproc_pcie_write_reg(struct iproc_pcie *pcie,
+ enum iproc_pcie_reg reg, u32 val)
+{
+ u16 offset = iproc_pcie_reg_offset(pcie, reg);
+
+ if (iproc_pcie_reg_is_invalid(offset))
+ return;
+
+ writel(val, pcie->base + offset);
+}
+
+static int iproc_pcie_map_ep_cfg_reg(const struct udevice *udev, pci_dev_t bdf,
+ uint where, void **paddress)
+{
+ struct iproc_pcie *pcie = dev_get_priv(udev);
+ unsigned int busno = PCI_BUS(bdf);
+ unsigned int slot = PCI_DEV(bdf);
+ unsigned int fn = PCI_FUNC(bdf);
+
+ u16 offset;
+ u32 val;
+
+ /* root complex access */
+ if (busno == 0) {
+ if (slot > 0 || fn > 0)
+ return -ENODEV;
+
+ iproc_pcie_write_reg(pcie, IPROC_PCIE_CFG_IND_ADDR,
+ where & CFG_IND_ADDR_MASK);
+ offset = iproc_pcie_reg_offset(pcie, IPROC_PCIE_CFG_IND_DATA);
+ if (iproc_pcie_reg_is_invalid(offset))
+ return -ENODEV;
+
+ *paddress = (pcie->base + offset);
+ return 0;
+ }
+
+ if (!pcie->link_is_active)
+ return -ENODEV;
+
+ /* EP device access */
+ val = (busno << CFG_ADDR_BUS_NUM_SHIFT) |
+ (slot << CFG_ADDR_DEV_NUM_SHIFT) |
+ (fn << CFG_ADDR_FUNC_NUM_SHIFT) |
+ (where & CFG_ADDR_REG_NUM_MASK) |
+ (1 & CFG_ADDR_CFG_TYPE_MASK);
+
+ iproc_pcie_write_reg(pcie, IPROC_PCIE_CFG_ADDR, val);
+ offset = iproc_pcie_reg_offset(pcie, IPROC_PCIE_CFG_DATA);
+
+ if (iproc_pcie_reg_is_invalid(offset))
+ return -ENODEV;
+
+ *paddress = (pcie->base + offset);
+
+ return 0;
+}
+
+static void iproc_pcie_fix_cap(struct iproc_pcie *pcie, int where, ulong *val)
+{
+ u32 i, dev_id;
+
+ switch (where & ~0x3) {
+ case PCI_VENDOR_ID:
+ dev_id = *val >> 16;
+
+ /*
+ * Activate fixup for those controllers that have corrupted
+ * capability list registers
+ */
+ for (i = 0; i < ARRAY_SIZE(iproc_pcie_corrupt_cap_did); i++)
+ if (dev_id == iproc_pcie_corrupt_cap_did[i])
+ pcie->fix_paxc_cap = true;
+ break;
+
+ case IPROC_PCI_PM_CAP:
+ if (pcie->fix_paxc_cap) {
+ /* advertise PM, force next capability to PCIe */
+ *val &= ~IPROC_PCI_PM_CAP_MASK;
+ *val |= IPROC_PCI_EXP_CAP << 8 | PCI_CAP_ID_PM;
+ }
+ break;
+
+ case IPROC_PCI_EXP_CAP:
+ if (pcie->fix_paxc_cap) {
+ /* advertise root port, version 2, terminate here */
+ *val = (PCI_EXP_TYPE_ROOT_PORT << 4 | 2) << 16 |
+ PCI_CAP_ID_EXP;
+ }
+ break;
+
+ case IPROC_PCI_EXP_CAP + PCI_EXP_RTCTL:
+ /* Don't advertise CRS SV support */
+ *val &= ~(PCI_EXP_RTCAP_CRSVIS << 16);
+ break;
+
+ default:
+ break;
+ }
+}
+
+static int iproc_pci_raw_config_read32(struct iproc_pcie *pcie,
+ unsigned int devfn, int where,
+ int size, u32 *val)
+{
+ void __iomem *addr;
+ int ret;
+
+ ret = iproc_pcie_map_ep_cfg_reg(pcie->dev, devfn, where & ~0x3, &addr);
+ if (ret) {
+ *val = ~0;
+ return -EINVAL;
+ }
+
+ *val = readl(addr);
+
+ if (size <= 2)
+ *val = (*val >> (8 * (where & 3))) & ((1 << (size * 8)) - 1);
+
+ return 0;
+}
+
+static int iproc_pci_raw_config_write32(struct iproc_pcie *pcie,
+ unsigned int devfn, int where,
+ int size, u32 val)
+{
+ void __iomem *addr;
+ int ret;
+ u32 mask, tmp;
+
+ ret = iproc_pcie_map_ep_cfg_reg(pcie->dev, devfn, where & ~0x3, &addr);
+ if (ret)
+ return -EINVAL;
+
+ if (size == 4) {
+ writel(val, addr);
+ return 0;
+ }
+
+ mask = ~(((1 << (size * 8)) - 1) << ((where & 0x3) * 8));
+ tmp = readl(addr) & mask;
+ tmp |= val << ((where & 0x3) * 8);
+ writel(tmp, addr);
+ return 0;
+}
+
+/**
+ * iproc_pcie_apb_err_disable() - configure apb error
+ *
+ * APB error forwarding can be disabled during access of configuration
+ * registers of the endpoint device, to prevent unsupported requests
+ * (typically seen during enumeration with multi-function devices) from
+ * triggering a system exception.
+ *
+ * @bus: pcie udevice
+ * @bdf: pdf value
+ * @disabled: flag to enable/disabled apb error
+ */
+static inline void iproc_pcie_apb_err_disable(const struct udevice *bus,
+ pci_dev_t bdf, bool disable)
+{
+ struct iproc_pcie *pcie = dev_get_priv(bus);
+ u32 val;
+
+ if (PCI_BUS(bdf) && pcie->has_apb_err_disable) {
+ val = iproc_pcie_read_reg(pcie, IPROC_PCIE_APB_ERR_EN);
+ if (disable)
+ val &= ~APB_ERR_EN;
+ else
+ val |= APB_ERR_EN;
+ iproc_pcie_write_reg(pcie, IPROC_PCIE_APB_ERR_EN, val);
+ }
+}
+
+static int iproc_pcie_config_read32(const struct udevice *bus, pci_dev_t bdf,
+ uint offset, ulong *valuep,
+ enum pci_size_t size)
+{
+ struct iproc_pcie *pcie = dev_get_priv(bus);
+ int ret;
+ ulong data;
+
+ iproc_pcie_apb_err_disable(bus, bdf, true);
+ ret = pci_generic_mmap_read_config(bus, iproc_pcie_map_ep_cfg_reg,
+ bdf, offset, &data, PCI_SIZE_32);
+ iproc_pcie_apb_err_disable(bus, bdf, false);
+ if (size <= PCI_SIZE_16)
+ *valuep = (data >> (8 * (offset & 3))) &
+ ((1 << (BIT(size) * 8)) - 1);
+ else
+ *valuep = data;
+
+ if (!ret && PCI_BUS(bdf) == 0)
+ iproc_pcie_fix_cap(pcie, offset, valuep);
+
+ return ret;
+}
+
+static int iproc_pcie_config_write32(struct udevice *bus, pci_dev_t bdf,
+ uint offset, ulong value,
+ enum pci_size_t size)
+{
+ void *addr;
+ ulong mask, tmp;
+ int ret;
+
+ ret = iproc_pcie_map_ep_cfg_reg(bus, bdf, offset, &addr);
+ if (ret)
+ return ret;
+
+ if (size == PCI_SIZE_32) {
+ writel(value, addr);
+ return ret;
+ }
+
+ iproc_pcie_apb_err_disable(bus, bdf, true);
+ mask = ~(((1 << (BIT(size) * 8)) - 1) << ((offset & 0x3) * 8));
+ tmp = readl(addr) & mask;
+ tmp |= (value << ((offset & 0x3) * 8));
+ writel(tmp, addr);
+ iproc_pcie_apb_err_disable(bus, bdf, false);
+
+ return ret;
+}
+
+const static struct dm_pci_ops iproc_pcie_ops = {
+ .read_config = iproc_pcie_config_read32,
+ .write_config = iproc_pcie_config_write32,
+};
+
+static int iproc_pcie_rev_init(struct iproc_pcie *pcie)
+{
+ unsigned int reg_idx;
+ const u16 *regs;
+ u16 num_elements;
+
+ switch (pcie->type) {
+ case IPROC_PCIE_PAXC_V2:
+ pcie->ep_is_internal = true;
+ regs = iproc_pcie_reg_paxc_v2;
+ num_elements = ARRAY_SIZE(iproc_pcie_reg_paxc_v2);
+ break;
+ case IPROC_PCIE_PAXB_V2:
+ regs = iproc_pcie_reg_paxb_v2;
+ num_elements = ARRAY_SIZE(iproc_pcie_reg_paxb_v2);
+ pcie->has_apb_err_disable = true;
+ if (pcie->need_ob_cfg) {
+ pcie->ob.axi_offset = 0;
+ pcie->ob_map = paxb_v2_ob_map;
+ pcie->ob.nr_windows = ARRAY_SIZE(paxb_v2_ob_map);
+ }
+ pcie->need_ib_cfg = true;
+ pcie->ib.nr_regions = ARRAY_SIZE(paxb_v2_ib_map);
+ pcie->ib_map = paxb_v2_ib_map;
+ break;
+ default:
+ dev_dbg(pcie->dev, "incompatible iProc PCIe interface\n");
+ return -EINVAL;
+ }
+
+ pcie->reg_offsets = calloc(IPROC_PCIE_MAX_NUM_REG,
+ sizeof(*pcie->reg_offsets));
+ if (!pcie->reg_offsets)
+ return -ENOMEM;
+
+ /* go through the register table and populate all valid registers */
+ pcie->reg_offsets[0] = (pcie->type == IPROC_PCIE_PAXC_V2) ?
+ IPROC_PCIE_REG_INVALID : regs[0];
+ for (reg_idx = 1; reg_idx < num_elements; reg_idx++)
+ pcie->reg_offsets[reg_idx] = regs[reg_idx] ?
+ regs[reg_idx] : IPROC_PCIE_REG_INVALID;
+
+ return 0;
+}
+
+static inline bool iproc_pcie_ob_is_valid(struct iproc_pcie *pcie,
+ int window_idx)
+{
+ u32 val;
+
+ val = iproc_pcie_read_reg(pcie, MAP_REG(IPROC_PCIE_OARR0, window_idx));
+
+ return !!(val & OARR_VALID);
+}
+
+static inline int iproc_pcie_ob_write(struct iproc_pcie *pcie, int window_idx,
+ int size_idx, u64 axi_addr, u64 pci_addr)
+{
+ u16 oarr_offset, omap_offset;
+
+ /*
+ * Derive the OARR/OMAP offset from the first pair (OARR0/OMAP0) based
+ * on window index.
+ */
+ oarr_offset = iproc_pcie_reg_offset(pcie, MAP_REG(IPROC_PCIE_OARR0,
+ window_idx));
+ omap_offset = iproc_pcie_reg_offset(pcie, MAP_REG(IPROC_PCIE_OMAP0,
+ window_idx));
+ if (iproc_pcie_reg_is_invalid(oarr_offset) ||
+ iproc_pcie_reg_is_invalid(omap_offset))
+ return -EINVAL;
+
+ /*
+ * Program the OARR registers. The upper 32-bit OARR register is
+ * always right after the lower 32-bit OARR register.
+ */
+ writel(lower_32_bits(axi_addr) | (size_idx << OARR_SIZE_CFG_SHIFT) |
+ OARR_VALID, pcie->base + oarr_offset);
+ writel(upper_32_bits(axi_addr), pcie->base + oarr_offset + 4);
+
+ /* now program the OMAP registers */
+ writel(lower_32_bits(pci_addr), pcie->base + omap_offset);
+ writel(upper_32_bits(pci_addr), pcie->base + omap_offset + 4);
+
+ debug("ob window [%d]: offset 0x%x axi %pap pci %pap\n",
+ window_idx, oarr_offset, &axi_addr, &pci_addr);
+ debug("oarr lo 0x%x oarr hi 0x%x\n",
+ readl(pcie->base + oarr_offset),
+ readl(pcie->base + oarr_offset + 4));
+ debug("omap lo 0x%x omap hi 0x%x\n",
+ readl(pcie->base + omap_offset),
+ readl(pcie->base + omap_offset + 4));
+
+ return 0;
+}
+
+/**
+ * iproc_pcie_setup_ob() - setup outbound address mapping
+ *
+ * Some iProc SoCs require the SW to configure the outbound address mapping
+ * Outbound address translation:
+ *
+ * iproc_pcie_address = axi_address - axi_offset
+ * OARR = iproc_pcie_address
+ * OMAP = pci_addr
+ * axi_addr -> iproc_pcie_address -> OARR -> OMAP -> pci_address
+ *
+ * @pcie: pcie device
+ * @axi_addr: axi address to be translated
+ * @pci_addr: pci address
+ * @size: window size
+ *
+ * @return: 0 on success and -ve on failure
+ */
+static int iproc_pcie_setup_ob(struct iproc_pcie *pcie, u64 axi_addr,
+ u64 pci_addr, resource_size_t size)
+{
+ struct iproc_pcie_ob *ob = &pcie->ob;
+ int ret = -EINVAL, window_idx, size_idx;
+
+ if (axi_addr < ob->axi_offset) {
+ pr_err("axi address %pap less than offset %pap\n",
+ &axi_addr, &ob->axi_offset);
+ return -EINVAL;
+ }
+
+ /*
+ * Translate the AXI address to the internal address used by the iProc
+ * PCIe core before programming the OARR
+ */
+ axi_addr -= ob->axi_offset;
+
+ /* iterate through all OARR/OMAP mapping windows */
+ for (window_idx = ob->nr_windows - 1; window_idx >= 0; window_idx--) {
+ const struct iproc_pcie_ob_map *ob_map =
+ &pcie->ob_map[window_idx];
+
+ /*
+ * If current outbound window is already in use, move on to the
+ * next one.
+ */
+ if (iproc_pcie_ob_is_valid(pcie, window_idx))
+ continue;
+
+ /*
+ * Iterate through all supported window sizes within the
+ * OARR/OMAP pair to find a match. Go through the window sizes
+ * in a descending order.
+ */
+ for (size_idx = ob_map->nr_sizes - 1; size_idx >= 0;
+ size_idx--) {
+ resource_size_t window_size =
+ ob_map->window_sizes[size_idx] * SZ_1M;
+
+ /*
+ * Keep iterating until we reach the last window and
+ * with the minimal window size at index zero. In this
+ * case, we take a compromise by mapping it using the
+ * minimum window size that can be supported
+ */
+ if (size < window_size) {
+ if (size_idx > 0 || window_idx > 0)
+ continue;
+
+ /*
+ * For the corner case of reaching the minimal
+ * window size that can be supported on the
+ * last window
+ */
+ axi_addr = ALIGN_DOWN(axi_addr, window_size);
+ pci_addr = ALIGN_DOWN(pci_addr, window_size);
+ size = window_size;
+ }
+
+ if (!IS_ALIGNED(axi_addr, window_size) ||
+ !IS_ALIGNED(pci_addr, window_size)) {
+ pr_err("axi %pap or pci %pap not aligned\n",
+ &axi_addr, &pci_addr);
+ return -EINVAL;
+ }
+
+ /*
+ * Match found! Program both OARR and OMAP and mark
+ * them as a valid entry.
+ */
+ ret = iproc_pcie_ob_write(pcie, window_idx, size_idx,
+ axi_addr, pci_addr);
+ if (ret)
+ goto err_ob;
+
+ size -= window_size;
+ if (size == 0)
+ return 0;
+
+ /*
+ * If we are here, we are done with the current window,
+ * but not yet finished all mappings. Need to move on
+ * to the next window.
+ */
+ axi_addr += window_size;
+ pci_addr += window_size;
+ break;
+ }
+ }
+
+err_ob:
+ pr_err("unable to configure outbound mapping\n");
+ pr_err("axi %pap, axi offset %pap, pci %pap, res size %pap\n",
+ &axi_addr, &ob->axi_offset, &pci_addr, &size);
+
+ return ret;
+}
+
+static int iproc_pcie_map_ranges(struct udevice *dev)
+{
+ struct iproc_pcie *pcie = dev_get_priv(dev);
+ struct udevice *bus = pci_get_controller(dev);
+ struct pci_controller *hose = dev_get_uclass_priv(bus);
+ int i, ret;
+
+ for (i = 0; i < hose->region_count; i++) {
+ if (hose->regions[i].flags == PCI_REGION_MEM ||
+ hose->regions[i].flags == PCI_REGION_PREFETCH) {
+ debug("%d: bus_addr %p, axi_addr %p, size 0x%lx\n",
+ i, &hose->regions[i].bus_start,
+ &hose->regions[i].phys_start,
+ hose->regions[i].size);
+ ret = iproc_pcie_setup_ob(pcie,
+ hose->regions[i].phys_start,
+ hose->regions[i].bus_start,
+ hose->regions[i].size);
+ if (ret)
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static inline bool iproc_pcie_ib_is_in_use(struct iproc_pcie *pcie,
+ int region_idx)
+{
+ const struct iproc_pcie_ib_map *ib_map = &pcie->ib_map[region_idx];
+ u32 val;
+
+ val = iproc_pcie_read_reg(pcie, MAP_REG(IPROC_PCIE_IARR0, region_idx));
+
+ return !!(val & (BIT(ib_map->nr_sizes) - 1));
+}
+
+static inline bool
+iproc_pcie_ib_check_type(const struct iproc_pcie_ib_map *ib_map,
+ enum iproc_pcie_ib_map_type type)
+{
+ return !!(ib_map->type == type);
+}
+
+static int iproc_pcie_ib_write(struct iproc_pcie *pcie, int region_idx,
+ int size_idx, int nr_windows, u64 axi_addr,
+ u64 pci_addr, resource_size_t size)
+{
+ const struct iproc_pcie_ib_map *ib_map = &pcie->ib_map[region_idx];
+ u16 iarr_offset, imap_offset;
+ u32 val;
+ int window_idx;
+
+ iarr_offset = iproc_pcie_reg_offset(pcie, MAP_REG(IPROC_PCIE_IARR0,
+ region_idx));
+ imap_offset = iproc_pcie_reg_offset(pcie, MAP_REG(IPROC_PCIE_IMAP0,
+ region_idx));
+ if (iproc_pcie_reg_is_invalid(iarr_offset) ||
+ iproc_pcie_reg_is_invalid(imap_offset))
+ return -EINVAL;
+
+ debug("ib region [%d]: offset 0x%x axi %pap pci %pap\n",
+ region_idx, iarr_offset, &axi_addr, &pci_addr);
+
+ /*
+ * Program the IARR registers. The upper 32-bit IARR register is
+ * always right after the lower 32-bit IARR register.
+ */
+ writel(lower_32_bits(pci_addr) | BIT(size_idx),
+ pcie->base + iarr_offset);
+ writel(upper_32_bits(pci_addr), pcie->base + iarr_offset + 4);
+
+ debug("iarr lo 0x%x iarr hi 0x%x\n",
+ readl(pcie->base + iarr_offset),
+ readl(pcie->base + iarr_offset + 4));
+
+ /*
+ * Now program the IMAP registers. Each IARR region may have one or
+ * more IMAP windows.
+ */
+ size >>= ilog2(nr_windows);
+ for (window_idx = 0; window_idx < nr_windows; window_idx++) {
+ val = readl(pcie->base + imap_offset);
+ val |= lower_32_bits(axi_addr) | IMAP_VALID;
+ writel(val, pcie->base + imap_offset);
+ writel(upper_32_bits(axi_addr),
+ pcie->base + imap_offset + ib_map->imap_addr_offset);
+
+ debug("imap window [%d] lo 0x%x hi 0x%x\n",
+ window_idx, readl(pcie->base + imap_offset),
+ readl(pcie->base + imap_offset +
+ ib_map->imap_addr_offset));
+
+ imap_offset += ib_map->imap_window_offset;
+ axi_addr += size;
+ }
+
+ return 0;
+}
+
+/**
+ * iproc_pcie_setup_ib() - setup inbound address mapping
+ *
+ * @pcie: pcie device
+ * @axi_addr: axi address to be translated
+ * @pci_addr: pci address
+ * @size: window size
+ * @type: inbound mapping type
+ *
+ * @return: 0 on success and -ve on failure
+ */
+static int iproc_pcie_setup_ib(struct iproc_pcie *pcie, u64 axi_addr,
+ u64 pci_addr, resource_size_t size,
+ enum iproc_pcie_ib_map_type type)
+{
+ struct iproc_pcie_ib *ib = &pcie->ib;
+ int ret;
+ unsigned int region_idx, size_idx;
+
+ /* iterate through all IARR mapping regions */
+ for (region_idx = 0; region_idx < ib->nr_regions; region_idx++) {
+ const struct iproc_pcie_ib_map *ib_map =
+ &pcie->ib_map[region_idx];
+
+ /*
+ * If current inbound region is already in use or not a
+ * compatible type, move on to the next.
+ */
+ if (iproc_pcie_ib_is_in_use(pcie, region_idx) ||
+ !iproc_pcie_ib_check_type(ib_map, type))
+ continue;
+
+ /* iterate through all supported region sizes to find a match */
+ for (size_idx = 0; size_idx < ib_map->nr_sizes; size_idx++) {
+ resource_size_t region_size =
+ ib_map->region_sizes[size_idx] * ib_map->size_unit;
+
+ if (size != region_size)
+ continue;
+
+ if (!IS_ALIGNED(axi_addr, region_size) ||
+ !IS_ALIGNED(pci_addr, region_size)) {
+ pr_err("axi %pap or pci %pap not aligned\n",
+ &axi_addr, &pci_addr);
+ return -EINVAL;
+ }
+
+ /* Match found! Program IARR and all IMAP windows. */
+ ret = iproc_pcie_ib_write(pcie, region_idx, size_idx,
+ ib_map->nr_windows, axi_addr,
+ pci_addr, size);
+ if (ret)
+ goto err_ib;
+ else
+ return 0;
+ }
+ }
+ ret = -EINVAL;
+
+err_ib:
+ pr_err("unable to configure inbound mapping\n");
+ pr_err("axi %pap, pci %pap, res size %pap\n",
+ &axi_addr, &pci_addr, &size);
+
+ return ret;
+}
+
+static int iproc_pcie_map_dma_ranges(struct iproc_pcie *pcie)
+{
+ int ret;
+ struct pci_region regions;
+ int i = 0;
+
+ while (!pci_get_dma_regions(pcie->dev, &regions, i)) {
+ dev_dbg(pcie->dev,
+ "dma %d: bus_addr %#lx, axi_addr %#llx, size %#lx\n",
+ i, regions.bus_start, regions.phys_start, regions.size);
+
+ /* Each range entry corresponds to an inbound mapping region */
+ ret = iproc_pcie_setup_ib(pcie, regions.phys_start,
+ regions.bus_start,
+ regions.size,
+ IPROC_PCIE_IB_MAP_MEM);
+ if (ret)
+ return ret;
+ i++;
+ }
+ return 0;
+}
+
+static void iproc_pcie_reset_map_regs(struct iproc_pcie *pcie)
+{
+ struct iproc_pcie_ib *ib = &pcie->ib;
+ struct iproc_pcie_ob *ob = &pcie->ob;
+ int window_idx, region_idx;
+
+ if (pcie->ep_is_internal)
+ return;
+
+ /* iterate through all OARR mapping regions */
+ for (window_idx = ob->nr_windows - 1; window_idx >= 0; window_idx--) {
+ iproc_pcie_write_reg(pcie, MAP_REG(IPROC_PCIE_OARR0,
+ window_idx), 0);
+ }
+
+ /* iterate through all IARR mapping regions */
+ for (region_idx = 0; region_idx < ib->nr_regions; region_idx++) {
+ iproc_pcie_write_reg(pcie, MAP_REG(IPROC_PCIE_IARR0,
+ region_idx), 0);
+ }
+}
+
+static void iproc_pcie_reset(struct iproc_pcie *pcie)
+{
+ u32 val;
+
+ /*
+ * PAXC and the internal emulated endpoint device downstream should not
+ * be reset. If firmware has been loaded on the endpoint device at an
+ * earlier boot stage, reset here causes issues.
+ */
+ if (pcie->ep_is_internal)
+ return;
+
+ /*
+ * Select perst_b signal as reset source. Put the device into reset,
+ * and then bring it out of reset
+ */
+ val = iproc_pcie_read_reg(pcie, IPROC_PCIE_CLK_CTRL);
+ val &= ~EP_PERST_SOURCE_SELECT & ~EP_MODE_SURVIVE_PERST &
+ ~RC_PCIE_RST_OUTPUT;
+ iproc_pcie_write_reg(pcie, IPROC_PCIE_CLK_CTRL, val);
+ udelay(250);
+
+ val |= RC_PCIE_RST_OUTPUT;
+ iproc_pcie_write_reg(pcie, IPROC_PCIE_CLK_CTRL, val);
+ mdelay(100);
+}
+
+static inline bool iproc_pcie_link_is_active(struct iproc_pcie *pcie)
+{
+ u32 val;
+
+ val = iproc_pcie_read_reg(pcie, IPROC_PCIE_LINK_STATUS);
+ return !!((val & PCIE_PHYLINKUP) && (val & PCIE_DL_ACTIVE));
+}
+
+static int iproc_pcie_check_link(struct iproc_pcie *pcie)
+{
+ u32 link_status, class;
+
+ pcie->link_is_active = false;
+ /* force class to PCI_CLASS_BRIDGE_PCI (0x0604) */
+#define PCI_BRIDGE_CTRL_REG_OFFSET 0x43c
+#define PCI_CLASS_BRIDGE_MASK 0xffff00
+#define PCI_CLASS_BRIDGE_SHIFT 8
+ iproc_pci_raw_config_read32(pcie, 0,
+ PCI_BRIDGE_CTRL_REG_OFFSET,
+ 4, &class);
+ class &= ~PCI_CLASS_BRIDGE_MASK;
+ class |= (PCI_CLASS_BRIDGE_PCI << PCI_CLASS_BRIDGE_SHIFT);
+ iproc_pci_raw_config_write32(pcie, 0,
+ PCI_BRIDGE_CTRL_REG_OFFSET,
+ 4, class);
+
+ /*
+ * PAXC connects to emulated endpoint devices directly and does not
+ * have a Serdes. Therefore skip the link detection logic here.
+ */
+ if (pcie->ep_is_internal) {
+ pcie->link_is_active = true;
+ return 0;
+ }
+
+ if (!iproc_pcie_link_is_active(pcie)) {
+ pr_err("PHY or data link is INACTIVE!\n");
+ return -ENODEV;
+ }
+
+#define PCI_TARGET_LINK_SPEED_MASK 0xf
+#define PCI_TARGET_LINK_WIDTH_MASK 0x3f
+#define PCI_TARGET_LINK_WIDTH_OFFSET 0x4
+
+ /* check link status to see if link is active */
+ iproc_pci_raw_config_read32(pcie, 0,
+ IPROC_PCI_EXP_CAP + PCI_EXP_LNKSTA,
+ 2, &link_status);
+ if (link_status & PCI_EXP_LNKSTA_NLW)
+ pcie->link_is_active = true;
+
+ if (pcie->link_is_active)
+ pr_info("link UP @ Speed Gen-%d and width-x%d\n",
+ link_status & PCI_TARGET_LINK_SPEED_MASK,
+ (link_status >> PCI_TARGET_LINK_WIDTH_OFFSET) &
+ PCI_TARGET_LINK_WIDTH_MASK);
+ else
+ pr_info("link DOWN\n");
+
+ return 0;
+}
+
+static int iproc_pcie_probe(struct udevice *dev)
+{
+ struct iproc_pcie *pcie = dev_get_priv(dev);
+ int ret;
+
+ pcie->type = (enum iproc_pcie_type)dev_get_driver_data(dev);
+ debug("PAX type %d\n", pcie->type);
+ pcie->base = dev_read_addr_ptr(dev);
+ debug("PAX reg base %p\n", pcie->base);
+
+ if (!pcie->base)
+ return -ENODEV;
+
+ if (dev_read_bool(dev, "brcm,pcie-ob"))
+ pcie->need_ob_cfg = true;
+
+ pcie->dev = dev;
+ ret = iproc_pcie_rev_init(pcie);
+ if (ret)
+ return ret;
+
+ if (!pcie->ep_is_internal) {
+ ret = generic_phy_get_by_name(dev, "pcie-phy", &pcie->phy);
+ if (!ret) {
+ ret = generic_phy_init(&pcie->phy);
+ if (ret) {
+ pr_err("failed to init %s PHY\n", dev->name);
+ return ret;
+ }
+
+ ret = generic_phy_power_on(&pcie->phy);
+ if (ret) {
+ pr_err("power on %s PHY failed\n", dev->name);
+ goto err_exit_phy;
+ }
+ }
+ }
+
+ iproc_pcie_reset(pcie);
+
+ if (pcie->need_ob_cfg) {
+ ret = iproc_pcie_map_ranges(dev);
+ if (ret) {
+ pr_err("outbound map failed\n");
+ goto err_power_off_phy;
+ }
+ }
+
+ if (pcie->need_ib_cfg) {
+ ret = iproc_pcie_map_dma_ranges(pcie);
+ if (ret) {
+ pr_err("inbound map failed\n");
+ goto err_power_off_phy;
+ }
+ }
+
+ if (iproc_pcie_check_link(pcie))
+ pr_info("no PCIe EP device detected\n");
+
+ return 0;
+
+err_power_off_phy:
+ generic_phy_power_off(&pcie->phy);
+err_exit_phy:
+ generic_phy_exit(&pcie->phy);
+ return ret;
+}
+
+static int iproc_pcie_remove(struct udevice *dev)
+{
+ struct iproc_pcie *pcie = dev_get_priv(dev);
+ int ret;
+
+ iproc_pcie_reset_map_regs(pcie);
+
+ if (generic_phy_valid(&pcie->phy)) {
+ ret = generic_phy_power_off(&pcie->phy);
+ if (ret) {
+ pr_err("failed to power off PCIe phy\n");
+ return ret;
+ }
+
+ ret = generic_phy_exit(&pcie->phy);
+ if (ret) {
+ pr_err("failed to power off PCIe phy\n");
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static const struct udevice_id pci_iproc_ids[] = {
+ { .compatible = "brcm,iproc-pcie-paxb-v2",
+ .data = IPROC_PCIE_PAXB_V2 },
+ { .compatible = "brcm,iproc-pcie-paxc-v2",
+ .data = IPROC_PCIE_PAXC_V2 },
+ { }
+};
+
+U_BOOT_DRIVER(pci_iproc) = {
+ .name = "pci_iproc",
+ .id = UCLASS_PCI,
+ .of_match = pci_iproc_ids,
+ .ops = &iproc_pcie_ops,
+ .probe = iproc_pcie_probe,
+ .remove = iproc_pcie_remove,
+ .priv_auto_alloc_size = sizeof(struct iproc_pcie),
+ .flags = DM_REMOVE_OS_PREPARE,
+};
diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig
index e146ffc5f86..e344677f91f 100644
--- a/drivers/serial/Kconfig
+++ b/drivers/serial/Kconfig
@@ -822,6 +822,13 @@ config MPC8XX_CONS
depends on MPC8xx
default y
+config XEN_SERIAL
+ bool "XEN serial support"
+ depends on XEN
+ help
+ If built without DM support, then requires Xen
+ to be built with CONFIG_VERBOSE_DEBUG.
+
choice
prompt "Console port"
default 8xx_CONS_SMC1
diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile
index e4a92bbbb71..25f7f8d342c 100644
--- a/drivers/serial/Makefile
+++ b/drivers/serial/Makefile
@@ -70,6 +70,7 @@ obj-$(CONFIG_OWL_SERIAL) += serial_owl.o
obj-$(CONFIG_OMAP_SERIAL) += serial_omap.o
obj-$(CONFIG_MTK_SERIAL) += serial_mtk.o
obj-$(CONFIG_SIFIVE_SERIAL) += serial_sifive.o
+obj-$(CONFIG_XEN_SERIAL) += serial_xen.o
ifndef CONFIG_SPL_BUILD
obj-$(CONFIG_USB_TTY) += usbtty.o
diff --git a/drivers/serial/serial_xen.c b/drivers/serial/serial_xen.c
new file mode 100644
index 00000000000..ba6504b9479
--- /dev/null
+++ b/drivers/serial/serial_xen.c
@@ -0,0 +1,182 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * (C) 2018 NXP
+ * (C) 2020 EPAM Systems Inc.
+ */
+#include <common.h>
+#include <cpu_func.h>
+#include <dm.h>
+#include <serial.h>
+#include <watchdog.h>
+
+#include <linux/bug.h>
+
+#include <xen/hvm.h>
+#include <xen/events.h>
+
+#include <xen/interface/sched.h>
+#include <xen/interface/hvm/hvm_op.h>
+#include <xen/interface/hvm/params.h>
+#include <xen/interface/io/console.h>
+#include <xen/interface/io/ring.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+u32 console_evtchn;
+
+/*
+ * struct xen_uart_priv - Structure representing a Xen UART info
+ * @intf: Console I/O interface for Xen guest OSes
+ * @evtchn: Console event channel
+ */
+struct xen_uart_priv {
+ struct xencons_interface *intf;
+ u32 evtchn;
+};
+
+int xen_serial_setbrg(struct udevice *dev, int baudrate)
+{
+ return 0;
+}
+
+static int xen_serial_probe(struct udevice *dev)
+{
+ struct xen_uart_priv *priv = dev_get_priv(dev);
+ u64 val = 0;
+ unsigned long gfn;
+ int ret;
+
+ ret = hvm_get_parameter(HVM_PARAM_CONSOLE_EVTCHN, &val);
+ if (ret < 0 || val == 0)
+ return ret;
+
+ priv->evtchn = val;
+ console_evtchn = val;
+
+ ret = hvm_get_parameter(HVM_PARAM_CONSOLE_PFN, &val);
+ if (ret < 0)
+ return ret;
+
+ if (!val)
+ return -EINVAL;
+
+ gfn = val;
+ priv->intf = (struct xencons_interface *)(gfn << XEN_PAGE_SHIFT);
+
+ return 0;
+}
+
+static int xen_serial_pending(struct udevice *dev, bool input)
+{
+ struct xen_uart_priv *priv = dev_get_priv(dev);
+ struct xencons_interface *intf = priv->intf;
+
+ if (!input || intf->in_cons == intf->in_prod)
+ return 0;
+
+ return 1;
+}
+
+static int xen_serial_getc(struct udevice *dev)
+{
+ struct xen_uart_priv *priv = dev_get_priv(dev);
+ struct xencons_interface *intf = priv->intf;
+ XENCONS_RING_IDX cons;
+ char c;
+
+ while (intf->in_cons == intf->in_prod)
+ mb(); /* wait */
+
+ cons = intf->in_cons;
+ mb(); /* get pointers before reading ring */
+
+ c = intf->in[MASK_XENCONS_IDX(cons++, intf->in)];
+
+ mb(); /* read ring before consuming */
+ intf->in_cons = cons;
+
+ notify_remote_via_evtchn(priv->evtchn);
+
+ return c;
+}
+
+static int __write_console(struct udevice *dev, const char *data, int len)
+{
+ struct xen_uart_priv *priv = dev_get_priv(dev);
+ struct xencons_interface *intf = priv->intf;
+ XENCONS_RING_IDX cons, prod;
+ int sent = 0;
+
+ cons = intf->out_cons;
+ prod = intf->out_prod;
+ mb(); /* Update pointer */
+
+ WARN_ON((prod - cons) > sizeof(intf->out));
+
+ while ((sent < len) && ((prod - cons) < sizeof(intf->out)))
+ intf->out[MASK_XENCONS_IDX(prod++, intf->out)] = data[sent++];
+
+ mb(); /* Update data before pointer */
+ intf->out_prod = prod;
+
+ if (sent)
+ notify_remote_via_evtchn(priv->evtchn);
+
+ return sent;
+}
+
+static int write_console(struct udevice *dev, const char *data, int len)
+{
+ /*
+ * Make sure the whole buffer is emitted, polling if
+ * necessary. We don't ever want to rely on the hvc daemon
+ * because the most interesting console output is when the
+ * kernel is crippled.
+ */
+ while (len) {
+ int sent = __write_console(dev, data, len);
+
+ data += sent;
+ len -= sent;
+
+ if (unlikely(len))
+ HYPERVISOR_sched_op(SCHEDOP_yield, NULL);
+ }
+
+ return 0;
+}
+
+static int xen_serial_putc(struct udevice *dev, const char ch)
+{
+ write_console(dev, &ch, 1);
+
+ return 0;
+}
+
+static const struct dm_serial_ops xen_serial_ops = {
+ .putc = xen_serial_putc,
+ .getc = xen_serial_getc,
+ .pending = xen_serial_pending,
+};
+
+#if CONFIG_IS_ENABLED(OF_CONTROL)
+static const struct udevice_id xen_serial_ids[] = {
+ { .compatible = "xen,xen" },
+ { }
+};
+#endif
+
+U_BOOT_DRIVER(serial_xen) = {
+ .name = "serial_xen",
+ .id = UCLASS_SERIAL,
+#if CONFIG_IS_ENABLED(OF_CONTROL)
+ .of_match = xen_serial_ids,
+#endif
+ .priv_auto_alloc_size = sizeof(struct xen_uart_priv),
+ .probe = xen_serial_probe,
+ .ops = &xen_serial_ops,
+#if !CONFIG_IS_ENABLED(OF_CONTROL)
+ .flags = DM_FLAG_PRE_RELOC,
+#endif
+};
+
diff --git a/drivers/usb/gadget/ether.c b/drivers/usb/gadget/ether.c
index a939918e973..a8e8bfc04b4 100644
--- a/drivers/usb/gadget/ether.c
+++ b/drivers/usb/gadget/ether.c
@@ -33,7 +33,6 @@
#define USB_NET_NAME "usb_ether"
-#define atomic_read
extern struct platform_data brd;
diff --git a/drivers/usb/musb-new/linux-compat.h b/drivers/usb/musb-new/linux-compat.h
index 733b197f593..6d9f19dfe6b 100644
--- a/drivers/usb/musb-new/linux-compat.h
+++ b/drivers/usb/musb-new/linux-compat.h
@@ -10,10 +10,6 @@
#define platform_data device_data
-#ifndef wmb
-#define wmb() asm volatile ("" : : : "memory")
-#endif
-
#define msleep(a) udelay(a * 1000)
/*
diff --git a/drivers/xen/Kconfig b/drivers/xen/Kconfig
new file mode 100644
index 00000000000..6ad2a936682
--- /dev/null
+++ b/drivers/xen/Kconfig
@@ -0,0 +1,10 @@
+config PVBLOCK
+ bool "Xen para-virtualized block device"
+ depends on DM
+ select BLK
+ select HAVE_BLOCK_DEVICE
+ help
+ This driver implements the front-end of the Xen virtual
+ block device driver. It communicates with a back-end driver
+ in another domain which drives the actual block device.
+
diff --git a/drivers/xen/Makefile b/drivers/xen/Makefile
new file mode 100644
index 00000000000..87157df69b8
--- /dev/null
+++ b/drivers/xen/Makefile
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: GPL-2.0+
+#
+# (C) Copyright 2020 EPAM Systems Inc.
+
+obj-y += hypervisor.o
+obj-y += events.o
+obj-y += xenbus.o
+obj-y += gnttab.o
+
+obj-$(CONFIG_PVBLOCK) += pvblock.o
diff --git a/drivers/xen/events.c b/drivers/xen/events.c
new file mode 100644
index 00000000000..c490f87b2fc
--- /dev/null
+++ b/drivers/xen/events.c
@@ -0,0 +1,199 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * (C) 2003 - Rolf Neugebauer - Intel Research Cambridge
+ * (C) 2005 - Grzegorz Milos - Intel Research Cambridge
+ * (C) 2020 - EPAM Systems Inc.
+ *
+ * File: events.c [1]
+ * Author: Rolf Neugebauer (neugebar@dcs.gla.ac.uk)
+ * Changes: Grzegorz Milos (gm281@cam.ac.uk)
+ *
+ * Date: Jul 2003, changes Jun 2005
+ *
+ * Description: Deals with events received on event channels
+ *
+ * [1] - http://xenbits.xen.org/gitweb/?p=mini-os.git;a=summary
+ */
+#include <common.h>
+#include <log.h>
+
+#include <asm/io.h>
+#include <asm/xen/system.h>
+
+#include <xen/events.h>
+#include <xen/hvm.h>
+
+extern u32 console_evtchn;
+
+#define NR_EVS 1024
+
+/**
+ * struct _ev_action - represents a event handler.
+ *
+ * Chaining or sharing is not allowed
+ */
+struct _ev_action {
+ void (*handler)(evtchn_port_t port, struct pt_regs *regs, void *data);
+ void *data;
+ u32 count;
+};
+
+static struct _ev_action ev_actions[NR_EVS];
+void default_handler(evtchn_port_t port, struct pt_regs *regs, void *data);
+
+static unsigned long bound_ports[NR_EVS / (8 * sizeof(unsigned long))];
+
+void unbind_all_ports(void)
+{
+ int i;
+ int cpu = 0;
+ struct shared_info *s = HYPERVISOR_shared_info;
+ struct vcpu_info *vcpu_info = &s->vcpu_info[cpu];
+
+ for (i = 0; i < NR_EVS; i++) {
+ if (i == console_evtchn)
+ continue;
+ if (test_and_clear_bit(i, bound_ports)) {
+ printf("port %d still bound!\n", i);
+ unbind_evtchn(i);
+ }
+ }
+ vcpu_info->evtchn_upcall_pending = 0;
+ vcpu_info->evtchn_pending_sel = 0;
+}
+
+int do_event(evtchn_port_t port, struct pt_regs *regs)
+{
+ struct _ev_action *action;
+
+ clear_evtchn(port);
+
+ if (port >= NR_EVS) {
+ printk("WARN: do_event(): Port number too large: %d\n", port);
+ return 1;
+ }
+
+ action = &ev_actions[port];
+ action->count++;
+
+ /* call the handler */
+ action->handler(port, regs, action->data);
+
+ return 1;
+}
+
+evtchn_port_t bind_evtchn(evtchn_port_t port,
+ void (*handler)(evtchn_port_t, struct pt_regs *, void *),
+ void *data)
+{
+ if (ev_actions[port].handler != default_handler)
+ printf("WARN: Handler for port %d already registered, replacing\n",
+ port);
+
+ ev_actions[port].data = data;
+ wmb();
+ ev_actions[port].handler = handler;
+ synch_set_bit(port, bound_ports);
+
+ return port;
+}
+
+/**
+ * unbind_evtchn() - Unbind event channel for selected port
+ */
+void unbind_evtchn(evtchn_port_t port)
+{
+ struct evtchn_close close;
+ int rc;
+
+ if (ev_actions[port].handler == default_handler)
+ debug("Default handler for port %d when unbinding\n", port);
+ mask_evtchn(port);
+ clear_evtchn(port);
+
+ ev_actions[port].handler = default_handler;
+ wmb();
+ ev_actions[port].data = NULL;
+ synch_clear_bit(port, bound_ports);
+
+ close.port = port;
+ rc = HYPERVISOR_event_channel_op(EVTCHNOP_close, &close);
+ if (rc)
+ printf("WARN: close_port %d failed rc=%d. ignored\n", port, rc);
+}
+
+void default_handler(evtchn_port_t port, struct pt_regs *regs, void *ignore)
+{
+ debug("[Port %d] - event received\n", port);
+}
+
+/**
+ * evtchn_alloc_unbound() - Create a port available to the pal for
+ * exchanging notifications.
+ *
+ * Unfortunate confusion of terminology: the port is unbound as far
+ * as Xen is concerned, but we automatically bind a handler to it.
+ *
+ * Return: The result of the hypervisor call.
+ */
+int evtchn_alloc_unbound(domid_t pal,
+ void (*handler)(evtchn_port_t, struct pt_regs *, void *),
+ void *data, evtchn_port_t *port)
+{
+ int rc;
+
+ struct evtchn_alloc_unbound op;
+
+ op.dom = DOMID_SELF;
+ op.remote_dom = pal;
+ rc = HYPERVISOR_event_channel_op(EVTCHNOP_alloc_unbound, &op);
+ if (rc) {
+ printf("ERROR: alloc_unbound failed with rc=%d", rc);
+ return rc;
+ }
+ if (!handler)
+ handler = default_handler;
+ *port = bind_evtchn(op.port, handler, data);
+ return rc;
+}
+
+/**
+ * eventchn_poll() - Event channel polling function
+ *
+ * Check and process any pending events
+ */
+void eventchn_poll(void)
+{
+ do_hypervisor_callback(NULL);
+}
+
+/**
+ * init_events() - Initialize event handler
+ *
+ * Initially all events are without a handler and disabled.
+ */
+void init_events(void)
+{
+ int i;
+
+ debug("%s\n", __func__);
+
+ for (i = 0; i < NR_EVS; i++) {
+ ev_actions[i].handler = default_handler;
+ mask_evtchn(i);
+ }
+}
+
+/**
+ * fini_events() - Close all ports
+ *
+ * Mask and clear event channels. Close port using EVTCHNOP_close
+ * hypercall.
+ */
+void fini_events(void)
+{
+ debug("%s\n", __func__);
+ /* Dealloc all events */
+ unbind_all_ports();
+}
+
diff --git a/drivers/xen/gnttab.c b/drivers/xen/gnttab.c
new file mode 100644
index 00000000000..becf7a79fbf
--- /dev/null
+++ b/drivers/xen/gnttab.c
@@ -0,0 +1,216 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * (C) 2006 - Cambridge University
+ * (C) 2020 - EPAM Systems Inc.
+ *
+ * File: gnttab.c [1]
+ * Author: Steven Smith (sos22@cam.ac.uk)
+ * Changes: Grzegorz Milos (gm281@cam.ac.uk)
+ *
+ * Date: July 2006
+ *
+ * Description: Simple grant tables implementation. About as stupid as it's
+ * possible to be and still work.
+ *
+ * [1] - http://xenbits.xen.org/gitweb/?p=mini-os.git;a=summary
+ */
+#include <common.h>
+#include <linux/compiler.h>
+#include <log.h>
+#include <malloc.h>
+
+#include <asm/armv8/mmu.h>
+#include <asm/io.h>
+#include <asm/xen/system.h>
+
+#include <linux/bug.h>
+
+#include <xen/gnttab.h>
+#include <xen/hvm.h>
+
+#include <xen/interface/memory.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+#define NR_RESERVED_ENTRIES 8
+
+/* NR_GRANT_FRAMES must be less than or equal to that configured in Xen */
+#define NR_GRANT_FRAMES 1
+#define NR_GRANT_ENTRIES (NR_GRANT_FRAMES * PAGE_SIZE / sizeof(struct grant_entry_v1))
+
+static struct grant_entry_v1 *gnttab_table;
+static grant_ref_t gnttab_list[NR_GRANT_ENTRIES];
+
+static void put_free_entry(grant_ref_t ref)
+{
+ unsigned long flags;
+
+ local_irq_save(flags);
+ gnttab_list[ref] = gnttab_list[0];
+ gnttab_list[0] = ref;
+ local_irq_restore(flags);
+}
+
+static grant_ref_t get_free_entry(void)
+{
+ unsigned int ref;
+ unsigned long flags;
+
+ local_irq_save(flags);
+ ref = gnttab_list[0];
+ BUG_ON(ref < NR_RESERVED_ENTRIES || ref >= NR_GRANT_ENTRIES);
+ gnttab_list[0] = gnttab_list[ref];
+ local_irq_restore(flags);
+ return ref;
+}
+
+/**
+ * gnttab_grant_access() - Allow access to the given frame.
+ * The function creates an entry in the grant table according
+ * to the specified parameters.
+ * @domid: the id of the domain for which access is allowed
+ * @frame: the number of the shared frame
+ * @readonly: determines whether the frame is shared read-only or read-write
+ *
+ * Return: relevant grant reference
+ */
+grant_ref_t gnttab_grant_access(domid_t domid, unsigned long frame, int readonly)
+{
+ grant_ref_t ref;
+
+ ref = get_free_entry();
+ gnttab_table[ref].frame = frame;
+ gnttab_table[ref].domid = domid;
+ wmb();
+ readonly *= GTF_readonly;
+ gnttab_table[ref].flags = GTF_permit_access | readonly;
+
+ return ref;
+}
+
+/**
+ * gnttab_end_access() - End of memory sharing. The function invalidates
+ * the entry in the grant table.
+ */
+int gnttab_end_access(grant_ref_t ref)
+{
+ u16 flags, nflags;
+
+ BUG_ON(ref >= NR_GRANT_ENTRIES || ref < NR_RESERVED_ENTRIES);
+
+ nflags = gnttab_table[ref].flags;
+ do {
+ if ((flags = nflags) & (GTF_reading | GTF_writing)) {
+ printf("WARNING: g.e. still in use! (%x)\n", flags);
+ return 0;
+ }
+ } while ((nflags = synch_cmpxchg(&gnttab_table[ref].flags, flags, 0)) !=
+ flags);
+
+ put_free_entry(ref);
+ return 1;
+}
+
+grant_ref_t gnttab_alloc_and_grant(void **map)
+{
+ unsigned long mfn;
+ grant_ref_t gref;
+
+ *map = (void *)memalign(PAGE_SIZE, PAGE_SIZE);
+ mfn = virt_to_mfn(*map);
+ gref = gnttab_grant_access(0, mfn, 0);
+ return gref;
+}
+
+static const char * const gnttabop_error_msgs[] = GNTTABOP_error_msgs;
+
+const char *gnttabop_error(int16_t status)
+{
+ status = -status;
+ if (status < 0 || status >= ARRAY_SIZE(gnttabop_error_msgs))
+ return "bad status";
+ else
+ return gnttabop_error_msgs[status];
+}
+
+/* Get Xen's suggested physical page assignments for the grant table. */
+void get_gnttab_base(phys_addr_t *gnttab_base, phys_size_t *gnttab_sz)
+{
+ const void *blob = gd->fdt_blob;
+ struct fdt_resource res;
+ int mem;
+
+ mem = fdt_node_offset_by_compatible(blob, -1, "xen,xen");
+ if (mem < 0) {
+ printf("No xen,xen compatible found\n");
+ BUG();
+ }
+
+ mem = fdt_get_resource(blob, mem, "reg", 0, &res);
+ if (mem == -FDT_ERR_NOTFOUND) {
+ printf("No grant table base in the device tree\n");
+ BUG();
+ }
+
+ *gnttab_base = (phys_addr_t)res.start;
+ if (gnttab_sz)
+ *gnttab_sz = (phys_size_t)(res.end - res.start + 1);
+
+ debug("FDT suggests grant table base at %llx\n",
+ *gnttab_base);
+}
+
+void init_gnttab(void)
+{
+ struct xen_add_to_physmap xatp;
+ struct gnttab_setup_table setup;
+ xen_pfn_t frames[NR_GRANT_FRAMES];
+ int i, rc;
+
+ debug("%s\n", __func__);
+
+ for (i = NR_RESERVED_ENTRIES; i < NR_GRANT_ENTRIES; i++)
+ put_free_entry(i);
+
+ get_gnttab_base((phys_addr_t *)&gnttab_table, NULL);
+
+ for (i = 0; i < NR_GRANT_FRAMES; i++) {
+ xatp.domid = DOMID_SELF;
+ xatp.size = 0;
+ xatp.space = XENMAPSPACE_grant_table;
+ xatp.idx = i;
+ xatp.gpfn = PFN_DOWN((unsigned long)gnttab_table) + i;
+ rc = HYPERVISOR_memory_op(XENMEM_add_to_physmap, &xatp);
+ if (rc)
+ printf("XENMEM_add_to_physmap failed; status = %d\n",
+ rc);
+ BUG_ON(rc != 0);
+ }
+
+ setup.dom = DOMID_SELF;
+ setup.nr_frames = NR_GRANT_FRAMES;
+ set_xen_guest_handle(setup.frame_list, frames);
+}
+
+void fini_gnttab(void)
+{
+ struct xen_remove_from_physmap xrtp;
+ struct gnttab_setup_table setup;
+ int i, rc;
+
+ debug("%s\n", __func__);
+
+ for (i = 0; i < NR_GRANT_FRAMES; i++) {
+ xrtp.domid = DOMID_SELF;
+ xrtp.gpfn = PFN_DOWN((unsigned long)gnttab_table) + i;
+ rc = HYPERVISOR_memory_op(XENMEM_remove_from_physmap, &xrtp);
+ if (rc)
+ printf("XENMEM_remove_from_physmap failed; status = %d\n",
+ rc);
+ BUG_ON(rc != 0);
+ }
+
+ setup.dom = DOMID_SELF;
+ setup.nr_frames = 0;
+}
+
diff --git a/drivers/xen/hypervisor.c b/drivers/xen/hypervisor.c
new file mode 100644
index 00000000000..178c206f5bf
--- /dev/null
+++ b/drivers/xen/hypervisor.c
@@ -0,0 +1,252 @@
+// SPDX-License-Identifier: MIT License
+/*
+ * hypervisor.c
+ *
+ * Communication to/from hypervisor.
+ *
+ * Copyright (c) 2002-2003, K A Fraser
+ * Copyright (c) 2005, Grzegorz Milos, gm281@cam.ac.uk,Intel Research Cambridge
+ * Copyright (c) 2020, EPAM Systems Inc.
+ */
+#include <common.h>
+#include <cpu_func.h>
+#include <log.h>
+#include <memalign.h>
+
+#include <asm/io.h>
+#include <asm/armv8/mmu.h>
+#include <asm/xen/system.h>
+
+#include <linux/bug.h>
+
+#include <xen/hvm.h>
+#include <xen/events.h>
+#include <xen/gnttab.h>
+#include <xen/xenbus.h>
+#include <xen/interface/memory.h>
+
+#define active_evtchns(cpu, sh, idx) \
+ ((sh)->evtchn_pending[idx] & \
+ ~(sh)->evtchn_mask[idx])
+
+int in_callback;
+
+/*
+ * Shared page for communicating with the hypervisor.
+ * Events flags go here, for example.
+ */
+struct shared_info *HYPERVISOR_shared_info;
+
+static const char *param_name(int op)
+{
+#define PARAM(x)[HVM_PARAM_##x] = #x
+ static const char *const names[] = {
+ PARAM(CALLBACK_IRQ),
+ PARAM(STORE_PFN),
+ PARAM(STORE_EVTCHN),
+ PARAM(PAE_ENABLED),
+ PARAM(IOREQ_PFN),
+ PARAM(VPT_ALIGN),
+ PARAM(CONSOLE_PFN),
+ PARAM(CONSOLE_EVTCHN),
+ };
+#undef PARAM
+
+ if (op >= ARRAY_SIZE(names))
+ return "unknown";
+
+ if (!names[op])
+ return "reserved";
+
+ return names[op];
+}
+
+/**
+ * hvm_get_parameter_maintain_dcache - function to obtain a HVM
+ * parameter value.
+ * @idx: HVM parameter index
+ * @value: Value to fill in
+ *
+ * According to Xen on ARM ABI (xen/include/public/arch-arm.h):
+ * all memory which is shared with other entities in the system
+ * (including the hypervisor and other guests) must reside in memory
+ * which is mapped as Normal Inner Write-Back Outer Write-Back
+ * Inner-Shareable.
+ *
+ * Thus, page attributes must be equally set for all the entities
+ * working with that page.
+ *
+ * Before MMU setup the data cache is turned off, so it means that
+ * manual data cache maintenance is required, because of the
+ * difference of page attributes.
+ */
+int hvm_get_parameter_maintain_dcache(int idx, uint64_t *value)
+{
+ struct xen_hvm_param xhv;
+ int ret;
+
+ invalidate_dcache_range((unsigned long)&xhv,
+ (unsigned long)&xhv + sizeof(xhv));
+ xhv.domid = DOMID_SELF;
+ xhv.index = idx;
+ invalidate_dcache_range((unsigned long)&xhv,
+ (unsigned long)&xhv + sizeof(xhv));
+
+ ret = HYPERVISOR_hvm_op(HVMOP_get_param, &xhv);
+ if (ret < 0) {
+ pr_err("Cannot get hvm parameter %s (%d): %d!\n",
+ param_name(idx), idx, ret);
+ BUG();
+ }
+ invalidate_dcache_range((unsigned long)&xhv,
+ (unsigned long)&xhv + sizeof(xhv));
+
+ *value = xhv.value;
+
+ return ret;
+}
+
+int hvm_get_parameter(int idx, uint64_t *value)
+{
+ struct xen_hvm_param xhv;
+ int ret;
+
+ xhv.domid = DOMID_SELF;
+ xhv.index = idx;
+ ret = HYPERVISOR_hvm_op(HVMOP_get_param, &xhv);
+ if (ret < 0) {
+ pr_err("Cannot get hvm parameter %s (%d): %d!\n",
+ param_name(idx), idx, ret);
+ BUG();
+ }
+
+ *value = xhv.value;
+
+ return ret;
+}
+
+struct shared_info *map_shared_info(void *p)
+{
+ struct xen_add_to_physmap xatp;
+
+ HYPERVISOR_shared_info = (struct shared_info *)memalign(PAGE_SIZE,
+ PAGE_SIZE);
+ if (!HYPERVISOR_shared_info)
+ BUG();
+
+ xatp.domid = DOMID_SELF;
+ xatp.idx = 0;
+ xatp.space = XENMAPSPACE_shared_info;
+ xatp.gpfn = virt_to_pfn(HYPERVISOR_shared_info);
+ if (HYPERVISOR_memory_op(XENMEM_add_to_physmap, &xatp) != 0)
+ BUG();
+
+ return HYPERVISOR_shared_info;
+}
+
+void do_hypervisor_callback(struct pt_regs *regs)
+{
+ unsigned long l1, l2, l1i, l2i;
+ unsigned int port;
+ int cpu = 0;
+ struct shared_info *s = HYPERVISOR_shared_info;
+ struct vcpu_info *vcpu_info = &s->vcpu_info[cpu];
+
+ in_callback = 1;
+
+ vcpu_info->evtchn_upcall_pending = 0;
+ l1 = xchg(&vcpu_info->evtchn_pending_sel, 0);
+
+ while (l1 != 0) {
+ l1i = __ffs(l1);
+ l1 &= ~(1UL << l1i);
+
+ while ((l2 = active_evtchns(cpu, s, l1i)) != 0) {
+ l2i = __ffs(l2);
+ l2 &= ~(1UL << l2i);
+
+ port = (l1i * (sizeof(unsigned long) * 8)) + l2i;
+ do_event(port, regs);
+ }
+ }
+
+ in_callback = 0;
+}
+
+void force_evtchn_callback(void)
+{
+#ifdef XEN_HAVE_PV_UPCALL_MASK
+ int save;
+#endif
+ struct vcpu_info *vcpu;
+
+ vcpu = &HYPERVISOR_shared_info->vcpu_info[smp_processor_id()];
+#ifdef XEN_HAVE_PV_UPCALL_MASK
+ save = vcpu->evtchn_upcall_mask;
+#endif
+
+ while (vcpu->evtchn_upcall_pending) {
+#ifdef XEN_HAVE_PV_UPCALL_MASK
+ vcpu->evtchn_upcall_mask = 1;
+#endif
+ do_hypervisor_callback(NULL);
+#ifdef XEN_HAVE_PV_UPCALL_MASK
+ vcpu->evtchn_upcall_mask = save;
+#endif
+ };
+}
+
+void mask_evtchn(uint32_t port)
+{
+ struct shared_info *s = HYPERVISOR_shared_info;
+
+ synch_set_bit(port, &s->evtchn_mask[0]);
+}
+
+void unmask_evtchn(uint32_t port)
+{
+ struct shared_info *s = HYPERVISOR_shared_info;
+ struct vcpu_info *vcpu_info = &s->vcpu_info[smp_processor_id()];
+
+ synch_clear_bit(port, &s->evtchn_mask[0]);
+
+ /*
+ * Just like a real IO-APIC we 'lose the interrupt edge' if the
+ * channel is masked.
+ */
+ if (synch_test_bit(port, &s->evtchn_pending[0]) &&
+ !synch_test_and_set_bit(port / (sizeof(unsigned long) * 8),
+ &vcpu_info->evtchn_pending_sel)) {
+ vcpu_info->evtchn_upcall_pending = 1;
+#ifdef XEN_HAVE_PV_UPCALL_MASK
+ if (!vcpu_info->evtchn_upcall_mask)
+#endif
+ force_evtchn_callback();
+ }
+}
+
+void clear_evtchn(uint32_t port)
+{
+ struct shared_info *s = HYPERVISOR_shared_info;
+
+ synch_clear_bit(port, &s->evtchn_pending[0]);
+}
+
+void xen_init(void)
+{
+ debug("%s\n", __func__);
+
+ map_shared_info(NULL);
+ init_events();
+ init_xenbus();
+ init_gnttab();
+}
+
+void xen_fini(void)
+{
+ debug("%s\n", __func__);
+
+ fini_gnttab();
+ fini_xenbus();
+ fini_events();
+}
diff --git a/drivers/xen/pvblock.c b/drivers/xen/pvblock.c
new file mode 100644
index 00000000000..76e82fbf41d
--- /dev/null
+++ b/drivers/xen/pvblock.c
@@ -0,0 +1,867 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) 2007-2008 Samuel Thibault.
+ * (C) Copyright 2020 EPAM Systems Inc.
+ */
+#include <blk.h>
+#include <common.h>
+#include <dm.h>
+#include <dm/device-internal.h>
+#include <malloc.h>
+#include <part.h>
+
+#include <asm/armv8/mmu.h>
+#include <asm/io.h>
+#include <asm/xen/system.h>
+
+#include <linux/bug.h>
+#include <linux/compat.h>
+
+#include <xen/events.h>
+#include <xen/gnttab.h>
+#include <xen/hvm.h>
+#include <xen/xenbus.h>
+
+#include <xen/interface/io/ring.h>
+#include <xen/interface/io/blkif.h>
+#include <xen/interface/io/protocols.h>
+
+#define DRV_NAME "pvblock"
+#define DRV_NAME_BLK "pvblock_blk"
+
+#define O_RDONLY 00
+#define O_RDWR 02
+#define WAIT_RING_TO_MS 10
+
+struct blkfront_info {
+ u64 sectors;
+ unsigned int sector_size;
+ int mode;
+ int info;
+ int barrier;
+ int flush;
+};
+
+/**
+ * struct blkfront_dev - Struct representing blkfront device
+ * @dom: Domain id
+ * @ring: Front_ring structure
+ * @ring_ref: The grant reference, allowing us to grant access
+ * to the ring to the other end/domain
+ * @evtchn: Event channel used to signal ring events
+ * @handle: Events handle
+ * @nodename: Device XenStore path in format "device/vbd/" + @devid
+ * @backend: Backend XenStore path
+ * @info: Private data
+ * @devid: Device id
+ */
+struct blkfront_dev {
+ domid_t dom;
+
+ struct blkif_front_ring ring;
+ grant_ref_t ring_ref;
+ evtchn_port_t evtchn;
+ blkif_vdev_t handle;
+
+ char *nodename;
+ char *backend;
+ struct blkfront_info info;
+ unsigned int devid;
+ u8 *bounce_buffer;
+};
+
+struct blkfront_platdata {
+ unsigned int devid;
+};
+
+/**
+ * struct blkfront_aiocb - AIO сontrol block
+ * @aio_dev: Blockfront device
+ * @aio_buf: Memory buffer, which must be sector-aligned for
+ * @aio_dev sector
+ * @aio_nbytes: Size of AIO, which must be less than @aio_dev
+ * sector-sized amounts
+ * @aio_offset: Offset, which must not go beyond @aio_dev
+ * sector-aligned location
+ * @data: Data used to receiving response from ring
+ * @gref: Array of grant references
+ * @n: Number of segments
+ * @aio_cb: Represents one I/O request.
+ */
+struct blkfront_aiocb {
+ struct blkfront_dev *aio_dev;
+ u8 *aio_buf;
+ size_t aio_nbytes;
+ off_t aio_offset;
+ void *data;
+
+ grant_ref_t gref[BLKIF_MAX_SEGMENTS_PER_REQUEST];
+ int n;
+
+ void (*aio_cb)(struct blkfront_aiocb *aiocb, int ret);
+};
+
+static void blkfront_sync(struct blkfront_dev *dev);
+
+static void free_blkfront(struct blkfront_dev *dev)
+{
+ mask_evtchn(dev->evtchn);
+ free(dev->backend);
+
+ gnttab_end_access(dev->ring_ref);
+ free(dev->ring.sring);
+
+ unbind_evtchn(dev->evtchn);
+
+ free(dev->bounce_buffer);
+ free(dev->nodename);
+ free(dev);
+}
+
+static int init_blkfront(unsigned int devid, struct blkfront_dev *dev)
+{
+ xenbus_transaction_t xbt;
+ char *err = NULL;
+ char *message = NULL;
+ struct blkif_sring *s;
+ int retry = 0;
+ char *msg = NULL;
+ char *c;
+ char nodename[32];
+ char path[ARRAY_SIZE(nodename) + strlen("/backend-id") + 1];
+
+ sprintf(nodename, "device/vbd/%d", devid);
+
+ memset(dev, 0, sizeof(*dev));
+ dev->nodename = strdup(nodename);
+ dev->devid = devid;
+
+ snprintf(path, sizeof(path), "%s/backend-id", nodename);
+ dev->dom = xenbus_read_integer(path);
+ evtchn_alloc_unbound(dev->dom, NULL, dev, &dev->evtchn);
+
+ s = (struct blkif_sring *)memalign(PAGE_SIZE, PAGE_SIZE);
+ if (!s) {
+ printf("Failed to allocate shared ring\n");
+ goto error;
+ }
+
+ SHARED_RING_INIT(s);
+ FRONT_RING_INIT(&dev->ring, s, PAGE_SIZE);
+
+ dev->ring_ref = gnttab_grant_access(dev->dom, virt_to_pfn(s), 0);
+
+again:
+ err = xenbus_transaction_start(&xbt);
+ if (err) {
+ printf("starting transaction\n");
+ free(err);
+ }
+
+ err = xenbus_printf(xbt, nodename, "ring-ref", "%u", dev->ring_ref);
+ if (err) {
+ message = "writing ring-ref";
+ goto abort_transaction;
+ }
+ err = xenbus_printf(xbt, nodename, "event-channel", "%u", dev->evtchn);
+ if (err) {
+ message = "writing event-channel";
+ goto abort_transaction;
+ }
+ err = xenbus_printf(xbt, nodename, "protocol", "%s",
+ XEN_IO_PROTO_ABI_NATIVE);
+ if (err) {
+ message = "writing protocol";
+ goto abort_transaction;
+ }
+
+ snprintf(path, sizeof(path), "%s/state", nodename);
+ err = xenbus_switch_state(xbt, path, XenbusStateConnected);
+ if (err) {
+ message = "switching state";
+ goto abort_transaction;
+ }
+
+ err = xenbus_transaction_end(xbt, 0, &retry);
+ free(err);
+ if (retry) {
+ goto again;
+ printf("completing transaction\n");
+ }
+
+ goto done;
+
+abort_transaction:
+ free(err);
+ err = xenbus_transaction_end(xbt, 1, &retry);
+ printf("Abort transaction %s\n", message);
+ goto error;
+
+done:
+ snprintf(path, sizeof(path), "%s/backend", nodename);
+ msg = xenbus_read(XBT_NIL, path, &dev->backend);
+ if (msg) {
+ printf("Error %s when reading the backend path %s\n",
+ msg, path);
+ goto error;
+ }
+
+ dev->handle = strtoul(strrchr(nodename, '/') + 1, NULL, 0);
+
+ {
+ XenbusState state;
+ char path[strlen(dev->backend) +
+ strlen("/feature-flush-cache") + 1];
+
+ snprintf(path, sizeof(path), "%s/mode", dev->backend);
+ msg = xenbus_read(XBT_NIL, path, &c);
+ if (msg) {
+ printf("Error %s when reading the mode\n", msg);
+ goto error;
+ }
+ if (*c == 'w')
+ dev->info.mode = O_RDWR;
+ else
+ dev->info.mode = O_RDONLY;
+ free(c);
+
+ snprintf(path, sizeof(path), "%s/state", dev->backend);
+
+ msg = NULL;
+ state = xenbus_read_integer(path);
+ while (!msg && state < XenbusStateConnected)
+ msg = xenbus_wait_for_state_change(path, &state);
+ if (msg || state != XenbusStateConnected) {
+ printf("backend not available, state=%d\n", state);
+ goto error;
+ }
+
+ snprintf(path, sizeof(path), "%s/info", dev->backend);
+ dev->info.info = xenbus_read_integer(path);
+
+ snprintf(path, sizeof(path), "%s/sectors", dev->backend);
+ /*
+ * FIXME: read_integer returns an int, so disk size
+ * limited to 1TB for now
+ */
+ dev->info.sectors = xenbus_read_integer(path);
+
+ snprintf(path, sizeof(path), "%s/sector-size", dev->backend);
+ dev->info.sector_size = xenbus_read_integer(path);
+
+ snprintf(path, sizeof(path), "%s/feature-barrier",
+ dev->backend);
+ dev->info.barrier = xenbus_read_integer(path);
+
+ snprintf(path, sizeof(path), "%s/feature-flush-cache",
+ dev->backend);
+ dev->info.flush = xenbus_read_integer(path);
+ }
+ unmask_evtchn(dev->evtchn);
+
+ dev->bounce_buffer = memalign(dev->info.sector_size,
+ dev->info.sector_size);
+ if (!dev->bounce_buffer) {
+ printf("Failed to allocate bouncing buffer\n");
+ goto error;
+ }
+
+ debug("%llu sectors of %u bytes, bounce buffer at %p\n",
+ dev->info.sectors, dev->info.sector_size,
+ dev->bounce_buffer);
+
+ return 0;
+
+error:
+ free(msg);
+ free(err);
+ free_blkfront(dev);
+ return -ENODEV;
+}
+
+static void shutdown_blkfront(struct blkfront_dev *dev)
+{
+ char *err = NULL, *err2;
+ XenbusState state;
+
+ char path[strlen(dev->backend) + strlen("/state") + 1];
+ char nodename[strlen(dev->nodename) + strlen("/event-channel") + 1];
+
+ debug("Close " DRV_NAME ", device ID %d\n", dev->devid);
+
+ blkfront_sync(dev);
+
+ snprintf(path, sizeof(path), "%s/state", dev->backend);
+ snprintf(nodename, sizeof(nodename), "%s/state", dev->nodename);
+
+ if ((err = xenbus_switch_state(XBT_NIL, nodename,
+ XenbusStateClosing)) != NULL) {
+ printf("%s: error changing state to %d: %s\n", __func__,
+ XenbusStateClosing, err);
+ goto close;
+ }
+
+ state = xenbus_read_integer(path);
+ while (!err && state < XenbusStateClosing)
+ err = xenbus_wait_for_state_change(path, &state);
+ free(err);
+
+ if ((err = xenbus_switch_state(XBT_NIL, nodename,
+ XenbusStateClosed)) != NULL) {
+ printf("%s: error changing state to %d: %s\n", __func__,
+ XenbusStateClosed, err);
+ goto close;
+ }
+
+ state = xenbus_read_integer(path);
+ while (state < XenbusStateClosed) {
+ err = xenbus_wait_for_state_change(path, &state);
+ free(err);
+ }
+
+ if ((err = xenbus_switch_state(XBT_NIL, nodename,
+ XenbusStateInitialising)) != NULL) {
+ printf("%s: error changing state to %d: %s\n", __func__,
+ XenbusStateInitialising, err);
+ goto close;
+ }
+
+ state = xenbus_read_integer(path);
+ while (!err &&
+ (state < XenbusStateInitWait || state >= XenbusStateClosed))
+ err = xenbus_wait_for_state_change(path, &state);
+
+close:
+ free(err);
+
+ snprintf(nodename, sizeof(nodename), "%s/ring-ref", dev->nodename);
+ err2 = xenbus_rm(XBT_NIL, nodename);
+ free(err2);
+ snprintf(nodename, sizeof(nodename), "%s/event-channel", dev->nodename);
+ err2 = xenbus_rm(XBT_NIL, nodename);
+ free(err2);
+
+ if (!err)
+ free_blkfront(dev);
+}
+
+/**
+ * blkfront_aio_poll() - AIO polling function.
+ * @dev: Blkfront device
+ *
+ * Here we receive response from the ring and check its status. This happens
+ * until we read all data from the ring. We read the data from consumed pointer
+ * to the response pointer. Then increase consumed pointer to make it clear that
+ * the data has been read.
+ *
+ * Return: Number of consumed bytes.
+ */
+static int blkfront_aio_poll(struct blkfront_dev *dev)
+{
+ RING_IDX rp, cons;
+ struct blkif_response *rsp;
+ int more;
+ int nr_consumed;
+
+moretodo:
+ rp = dev->ring.sring->rsp_prod;
+ rmb(); /* Ensure we see queued responses up to 'rp'. */
+ cons = dev->ring.rsp_cons;
+
+ nr_consumed = 0;
+ while ((cons != rp)) {
+ struct blkfront_aiocb *aiocbp;
+ int status;
+
+ rsp = RING_GET_RESPONSE(&dev->ring, cons);
+ nr_consumed++;
+
+ aiocbp = (void *)(uintptr_t)rsp->id;
+ status = rsp->status;
+
+ switch (rsp->operation) {
+ case BLKIF_OP_READ:
+ case BLKIF_OP_WRITE:
+ {
+ int j;
+
+ if (status != BLKIF_RSP_OKAY)
+ printf("%s error %d on %s at offset %llu, num bytes %llu\n",
+ rsp->operation == BLKIF_OP_READ ?
+ "read" : "write",
+ status, aiocbp->aio_dev->nodename,
+ (unsigned long long)aiocbp->aio_offset,
+ (unsigned long long)aiocbp->aio_nbytes);
+
+ for (j = 0; j < aiocbp->n; j++)
+ gnttab_end_access(aiocbp->gref[j]);
+
+ break;
+ }
+
+ case BLKIF_OP_WRITE_BARRIER:
+ if (status != BLKIF_RSP_OKAY)
+ printf("write barrier error %d\n", status);
+ break;
+ case BLKIF_OP_FLUSH_DISKCACHE:
+ if (status != BLKIF_RSP_OKAY)
+ printf("flush error %d\n", status);
+ break;
+
+ default:
+ printf("unrecognized block operation %d response (status %d)\n",
+ rsp->operation, status);
+ break;
+ }
+
+ dev->ring.rsp_cons = ++cons;
+ /* Nota: callback frees aiocbp itself */
+ if (aiocbp && aiocbp->aio_cb)
+ aiocbp->aio_cb(aiocbp, status ? -EIO : 0);
+ if (dev->ring.rsp_cons != cons)
+ /* We reentered, we must not continue here */
+ break;
+ }
+
+ RING_FINAL_CHECK_FOR_RESPONSES(&dev->ring, more);
+ if (more)
+ goto moretodo;
+
+ return nr_consumed;
+}
+
+static void blkfront_wait_slot(struct blkfront_dev *dev)
+{
+ /* Wait for a slot */
+ if (RING_FULL(&dev->ring)) {
+ while (true) {
+ blkfront_aio_poll(dev);
+ if (!RING_FULL(&dev->ring))
+ break;
+ wait_event_timeout(NULL, !RING_FULL(&dev->ring),
+ WAIT_RING_TO_MS);
+ }
+ }
+}
+
+/**
+ * blkfront_aio_poll() - Issue an aio.
+ * @aiocbp: AIO control block structure
+ * @write: Describes is it read or write operation
+ * 0 - read
+ * 1 - write
+ *
+ * We check whether the AIO parameters meet the requirements of the device.
+ * Then receive request from ring and define its arguments. After this we
+ * grant access to the grant references. The last step is notifying about AIO
+ * via event channel.
+ */
+static void blkfront_aio(struct blkfront_aiocb *aiocbp, int write)
+{
+ struct blkfront_dev *dev = aiocbp->aio_dev;
+ struct blkif_request *req;
+ RING_IDX i;
+ int notify;
+ int n, j;
+ uintptr_t start, end;
+
+ /* Can't io at non-sector-aligned location */
+ BUG_ON(aiocbp->aio_offset & (dev->info.sector_size - 1));
+ /* Can't io non-sector-sized amounts */
+ BUG_ON(aiocbp->aio_nbytes & (dev->info.sector_size - 1));
+ /* Can't io non-sector-aligned buffer */
+ BUG_ON(((uintptr_t)aiocbp->aio_buf & (dev->info.sector_size - 1)));
+
+ start = (uintptr_t)aiocbp->aio_buf & PAGE_MASK;
+ end = ((uintptr_t)aiocbp->aio_buf + aiocbp->aio_nbytes +
+ PAGE_SIZE - 1) & PAGE_MASK;
+ n = (end - start) / PAGE_SIZE;
+ aiocbp->n = n;
+
+ BUG_ON(n > BLKIF_MAX_SEGMENTS_PER_REQUEST);
+
+ blkfront_wait_slot(dev);
+ i = dev->ring.req_prod_pvt;
+ req = RING_GET_REQUEST(&dev->ring, i);
+
+ req->operation = write ? BLKIF_OP_WRITE : BLKIF_OP_READ;
+ req->nr_segments = n;
+ req->handle = dev->handle;
+ req->id = (uintptr_t)aiocbp;
+ req->sector_number = aiocbp->aio_offset / dev->info.sector_size;
+
+ for (j = 0; j < n; j++) {
+ req->seg[j].first_sect = 0;
+ req->seg[j].last_sect = PAGE_SIZE / dev->info.sector_size - 1;
+ }
+ req->seg[0].first_sect = ((uintptr_t)aiocbp->aio_buf & ~PAGE_MASK) /
+ dev->info.sector_size;
+ req->seg[n - 1].last_sect = (((uintptr_t)aiocbp->aio_buf +
+ aiocbp->aio_nbytes - 1) & ~PAGE_MASK) / dev->info.sector_size;
+ for (j = 0; j < n; j++) {
+ uintptr_t data = start + j * PAGE_SIZE;
+
+ if (!write) {
+ /* Trigger CoW if needed */
+ *(char *)(data + (req->seg[j].first_sect *
+ dev->info.sector_size)) = 0;
+ barrier();
+ }
+ req->seg[j].gref = gnttab_grant_access(dev->dom,
+ virt_to_pfn((void *)data),
+ write);
+ aiocbp->gref[j] = req->seg[j].gref;
+ }
+
+ dev->ring.req_prod_pvt = i + 1;
+
+ wmb();
+ RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(&dev->ring, notify);
+
+ if (notify)
+ notify_remote_via_evtchn(dev->evtchn);
+}
+
+static void blkfront_aio_cb(struct blkfront_aiocb *aiocbp, int ret)
+{
+ aiocbp->data = (void *)1;
+ aiocbp->aio_cb = NULL;
+}
+
+static void blkfront_io(struct blkfront_aiocb *aiocbp, int write)
+{
+ aiocbp->aio_cb = blkfront_aio_cb;
+ blkfront_aio(aiocbp, write);
+ aiocbp->data = NULL;
+
+ while (true) {
+ blkfront_aio_poll(aiocbp->aio_dev);
+ if (aiocbp->data)
+ break;
+ cpu_relax();
+ }
+}
+
+static void blkfront_push_operation(struct blkfront_dev *dev, u8 op,
+ uint64_t id)
+{
+ struct blkif_request *req;
+ int notify, i;
+
+ blkfront_wait_slot(dev);
+ i = dev->ring.req_prod_pvt;
+ req = RING_GET_REQUEST(&dev->ring, i);
+ req->operation = op;
+ req->nr_segments = 0;
+ req->handle = dev->handle;
+ req->id = id;
+ req->sector_number = 0;
+ dev->ring.req_prod_pvt = i + 1;
+ wmb();
+ RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(&dev->ring, notify);
+ if (notify)
+ notify_remote_via_evtchn(dev->evtchn);
+}
+
+static void blkfront_sync(struct blkfront_dev *dev)
+{
+ if (dev->info.mode == O_RDWR) {
+ if (dev->info.barrier == 1)
+ blkfront_push_operation(dev,
+ BLKIF_OP_WRITE_BARRIER, 0);
+
+ if (dev->info.flush == 1)
+ blkfront_push_operation(dev,
+ BLKIF_OP_FLUSH_DISKCACHE, 0);
+ }
+
+ while (true) {
+ blkfront_aio_poll(dev);
+ if (RING_FREE_REQUESTS(&dev->ring) == RING_SIZE(&dev->ring))
+ break;
+ cpu_relax();
+ }
+}
+
+/**
+ * pvblock_iop() - Issue an aio.
+ * @udev: Pvblock device
+ * @blknr: Block number to read from / write to
+ * @blkcnt: Amount of blocks to read / write
+ * @buffer: Memory buffer with data to be read / write
+ * @write: Describes is it read or write operation
+ * 0 - read
+ * 1 - write
+ *
+ * Depending on the operation - reading or writing, data is read / written from the
+ * specified address (@buffer) to the sector (@blknr).
+ */
+static ulong pvblock_iop(struct udevice *udev, lbaint_t blknr,
+ lbaint_t blkcnt, void *buffer, int write)
+{
+ struct blkfront_dev *blk_dev = dev_get_priv(udev);
+ struct blk_desc *desc = dev_get_uclass_platdata(udev);
+ struct blkfront_aiocb aiocb;
+ lbaint_t blocks_todo;
+ bool unaligned;
+
+ if (blkcnt == 0)
+ return 0;
+
+ if ((blknr + blkcnt) > desc->lba) {
+ printf(DRV_NAME ": block number 0x" LBAF " exceeds max(0x" LBAF ")\n",
+ blknr + blkcnt, desc->lba);
+ return 0;
+ }
+
+ unaligned = (uintptr_t)buffer & (blk_dev->info.sector_size - 1);
+
+ aiocb.aio_dev = blk_dev;
+ aiocb.aio_offset = blknr * desc->blksz;
+ aiocb.aio_cb = NULL;
+ aiocb.data = NULL;
+ blocks_todo = blkcnt;
+ do {
+ aiocb.aio_buf = unaligned ? blk_dev->bounce_buffer : buffer;
+
+ if (write && unaligned)
+ memcpy(blk_dev->bounce_buffer, buffer, desc->blksz);
+
+ aiocb.aio_nbytes = unaligned ? desc->blksz :
+ min((size_t)(BLKIF_MAX_SEGMENTS_PER_REQUEST * PAGE_SIZE),
+ (size_t)(blocks_todo * desc->blksz));
+
+ blkfront_io(&aiocb, write);
+
+ if (!write && unaligned)
+ memcpy(buffer, blk_dev->bounce_buffer, desc->blksz);
+
+ aiocb.aio_offset += aiocb.aio_nbytes;
+ buffer += aiocb.aio_nbytes;
+ blocks_todo -= aiocb.aio_nbytes / desc->blksz;
+ } while (blocks_todo > 0);
+
+ return blkcnt;
+}
+
+ulong pvblock_blk_read(struct udevice *udev, lbaint_t blknr, lbaint_t blkcnt,
+ void *buffer)
+{
+ return pvblock_iop(udev, blknr, blkcnt, buffer, 0);
+}
+
+ulong pvblock_blk_write(struct udevice *udev, lbaint_t blknr, lbaint_t blkcnt,
+ const void *buffer)
+{
+ return pvblock_iop(udev, blknr, blkcnt, (void *)buffer, 1);
+}
+
+static int pvblock_blk_bind(struct udevice *udev)
+{
+ struct blk_desc *desc = dev_get_uclass_platdata(udev);
+ int devnum;
+
+ desc->if_type = IF_TYPE_PVBLOCK;
+ /*
+ * Initialize the devnum to -ENODEV. This is to make sure that
+ * blk_next_free_devnum() works as expected, since the default
+ * value 0 is a valid devnum.
+ */
+ desc->devnum = -ENODEV;
+ devnum = blk_next_free_devnum(IF_TYPE_PVBLOCK);
+ if (devnum < 0)
+ return devnum;
+ desc->devnum = devnum;
+ desc->part_type = PART_TYPE_UNKNOWN;
+ desc->bdev = udev;
+
+ strncpy(desc->vendor, "Xen", sizeof(desc->vendor));
+ strncpy(desc->revision, "1", sizeof(desc->revision));
+ strncpy(desc->product, "Virtual disk", sizeof(desc->product));
+
+ return 0;
+}
+
+static int pvblock_blk_probe(struct udevice *udev)
+{
+ struct blkfront_dev *blk_dev = dev_get_priv(udev);
+ struct blkfront_platdata *platdata = dev_get_platdata(udev);
+ struct blk_desc *desc = dev_get_uclass_platdata(udev);
+ int ret, devid;
+
+ devid = platdata->devid;
+ free(platdata);
+
+ ret = init_blkfront(devid, blk_dev);
+ if (ret < 0)
+ return ret;
+
+ desc->blksz = blk_dev->info.sector_size;
+ desc->lba = blk_dev->info.sectors;
+ desc->log2blksz = LOG2(blk_dev->info.sector_size);
+
+ return 0;
+}
+
+static int pvblock_blk_remove(struct udevice *udev)
+{
+ struct blkfront_dev *blk_dev = dev_get_priv(udev);
+
+ shutdown_blkfront(blk_dev);
+ return 0;
+}
+
+static const struct blk_ops pvblock_blk_ops = {
+ .read = pvblock_blk_read,
+ .write = pvblock_blk_write,
+};
+
+U_BOOT_DRIVER(pvblock_blk) = {
+ .name = DRV_NAME_BLK,
+ .id = UCLASS_BLK,
+ .ops = &pvblock_blk_ops,
+ .bind = pvblock_blk_bind,
+ .probe = pvblock_blk_probe,
+ .remove = pvblock_blk_remove,
+ .priv_auto_alloc_size = sizeof(struct blkfront_dev),
+ .flags = DM_FLAG_OS_PREPARE,
+};
+
+/*******************************************************************************
+ * Para-virtual block device class
+ *******************************************************************************/
+
+typedef int (*enum_vbd_callback)(struct udevice *parent, unsigned int devid);
+
+static int on_new_vbd(struct udevice *parent, unsigned int devid)
+{
+ struct driver_info info;
+ struct udevice *udev;
+ struct blkfront_platdata *platdata;
+ int ret;
+
+ debug("New " DRV_NAME_BLK ", device ID %d\n", devid);
+
+ platdata = malloc(sizeof(struct blkfront_platdata));
+ if (!platdata) {
+ printf("Failed to allocate platform data\n");
+ return -ENOMEM;
+ }
+
+ platdata->devid = devid;
+
+ info.name = DRV_NAME_BLK;
+ info.platdata = platdata;
+
+ ret = device_bind_by_name(parent, false, &info, &udev);
+ if (ret < 0) {
+ printf("Failed to bind " DRV_NAME_BLK " to device with ID %d, ret: %d\n",
+ devid, ret);
+ free(platdata);
+ }
+ return ret;
+}
+
+static int xenbus_enumerate_vbd(struct udevice *udev, enum_vbd_callback clb)
+{
+ char **dirs, *msg;
+ int i, ret;
+
+ msg = xenbus_ls(XBT_NIL, "device/vbd", &dirs);
+ if (msg) {
+ printf("Failed to read device/vbd directory: %s\n", msg);
+ free(msg);
+ return -ENODEV;
+ }
+
+ for (i = 0; dirs[i]; i++) {
+ int devid;
+
+ sscanf(dirs[i], "%d", &devid);
+ ret = clb(udev, devid);
+ if (ret < 0)
+ goto fail;
+
+ free(dirs[i]);
+ }
+ ret = 0;
+
+fail:
+ for (; dirs[i]; i++)
+ free(dirs[i]);
+ free(dirs);
+ return ret;
+}
+
+static void print_pvblock_devices(void)
+{
+ struct udevice *udev;
+ bool first = true;
+ const char *class_name;
+
+ class_name = uclass_get_name(UCLASS_PVBLOCK);
+ for (blk_first_device(IF_TYPE_PVBLOCK, &udev); udev;
+ blk_next_device(&udev), first = false) {
+ struct blk_desc *desc = dev_get_uclass_platdata(udev);
+
+ if (!first)
+ puts(", ");
+ printf("%s: %d", class_name, desc->devnum);
+ }
+ printf("\n");
+}
+
+void pvblock_init(void)
+{
+ struct driver_info info;
+ struct udevice *udev;
+ struct uclass *uc;
+ int ret;
+
+ /*
+ * At this point Xen drivers have already initialized,
+ * so we can instantiate the class driver and enumerate
+ * virtual block devices.
+ */
+ info.name = DRV_NAME;
+ ret = device_bind_by_name(gd->dm_root, false, &info, &udev);
+ if (ret < 0)
+ printf("Failed to bind " DRV_NAME ", ret: %d\n", ret);
+
+ /* Bootstrap virtual block devices class driver */
+ ret = uclass_get(UCLASS_PVBLOCK, &uc);
+ if (ret)
+ return;
+ uclass_foreach_dev_probe(UCLASS_PVBLOCK, udev);
+
+ print_pvblock_devices();
+}
+
+static int pvblock_probe(struct udevice *udev)
+{
+ struct uclass *uc;
+ int ret;
+
+ if (xenbus_enumerate_vbd(udev, on_new_vbd) < 0)
+ return -ENODEV;
+
+ ret = uclass_get(UCLASS_BLK, &uc);
+ if (ret)
+ return ret;
+ uclass_foreach_dev_probe(UCLASS_BLK, udev) {
+ if (_ret)
+ return _ret;
+ };
+ return 0;
+}
+
+U_BOOT_DRIVER(pvblock_drv) = {
+ .name = DRV_NAME,
+ .id = UCLASS_PVBLOCK,
+ .probe = pvblock_probe,
+};
+
+UCLASS_DRIVER(pvblock) = {
+ .name = DRV_NAME,
+ .id = UCLASS_PVBLOCK,
+};
diff --git a/drivers/xen/xenbus.c b/drivers/xen/xenbus.c
new file mode 100644
index 00000000000..177d144723c
--- /dev/null
+++ b/drivers/xen/xenbus.c
@@ -0,0 +1,557 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * (C) 2006 - Cambridge University
+ * (C) 2020 - EPAM Systems Inc.
+ *
+ * File: xenbus.c [1]
+ * Author: Steven Smith (sos22@cam.ac.uk)
+ * Changes: Grzegorz Milos (gm281@cam.ac.uk)
+ * Changes: John D. Ramsdell
+ *
+ * Date: Jun 2006, changes Aug 2006
+ *
+ * Description: Minimal implementation of xenbus
+ *
+ * [1] - http://xenbits.xen.org/gitweb/?p=mini-os.git;a=summary
+ */
+
+#include <common.h>
+#include <log.h>
+
+#include <asm/armv8/mmu.h>
+#include <asm/io.h>
+#include <asm/xen/system.h>
+
+#include <linux/bug.h>
+#include <linux/compat.h>
+
+#include <xen/events.h>
+#include <xen/hvm.h>
+#include <xen/xenbus.h>
+
+#include <xen/interface/io/xs_wire.h>
+
+#define map_frame_virt(v) (v << PAGE_SHIFT)
+
+#define SCNd16 "d"
+
+/* Wait for reply time out, ms */
+#define WAIT_XENBUS_TO_MS 5000
+/* Polling time out, ms */
+#define WAIT_XENBUS_POLL_TO_MS 1
+
+static struct xenstore_domain_interface *xenstore_buf;
+
+static char *errmsg(struct xsd_sockmsg *rep);
+
+u32 xenbus_evtchn;
+
+struct write_req {
+ const void *data;
+ unsigned int len;
+};
+
+static void memcpy_from_ring(const void *r, void *d, int off, int len)
+{
+ int c1, c2;
+ const char *ring = r;
+ char *dest = d;
+
+ c1 = min(len, XENSTORE_RING_SIZE - off);
+ c2 = len - c1;
+ memcpy(dest, ring + off, c1);
+ memcpy(dest + c1, ring, c2);
+}
+
+/**
+ * xenbus_get_reply() - Receive reply from xenbus
+ * @req_reply: reply message structure
+ *
+ * Wait for reply message event from the ring and copy received message
+ * to input xsd_sockmsg structure. Repeat until full reply is
+ * proceeded.
+ *
+ * Return: false - timeout
+ * true - reply is received
+ */
+static bool xenbus_get_reply(struct xsd_sockmsg **req_reply)
+{
+ struct xsd_sockmsg msg;
+ unsigned int prod = xenstore_buf->rsp_prod;
+
+again:
+ if (!wait_event_timeout(NULL, prod != xenstore_buf->rsp_prod,
+ WAIT_XENBUS_TO_MS)) {
+ printk("%s: wait_event timeout\n", __func__);
+ return false;
+ }
+
+ prod = xenstore_buf->rsp_prod;
+ if (xenstore_buf->rsp_prod - xenstore_buf->rsp_cons < sizeof(msg))
+ goto again;
+
+ rmb();
+ memcpy_from_ring(xenstore_buf->rsp, &msg,
+ MASK_XENSTORE_IDX(xenstore_buf->rsp_cons),
+ sizeof(msg));
+
+ if (xenstore_buf->rsp_prod - xenstore_buf->rsp_cons < sizeof(msg) + msg.len)
+ goto again;
+
+ /* We do not support and expect any Xen bus wathes. */
+ BUG_ON(msg.type == XS_WATCH_EVENT);
+
+ *req_reply = malloc(sizeof(msg) + msg.len);
+ memcpy_from_ring(xenstore_buf->rsp, *req_reply,
+ MASK_XENSTORE_IDX(xenstore_buf->rsp_cons),
+ msg.len + sizeof(msg));
+ mb();
+ xenstore_buf->rsp_cons += msg.len + sizeof(msg);
+
+ wmb();
+ notify_remote_via_evtchn(xenbus_evtchn);
+ return true;
+}
+
+char *xenbus_switch_state(xenbus_transaction_t xbt, const char *path,
+ XenbusState state)
+{
+ char *current_state;
+ char *msg = NULL;
+ char *msg2 = NULL;
+ char value[2];
+ XenbusState rs;
+ int xbt_flag = 0;
+ int retry = 0;
+
+ do {
+ if (xbt == XBT_NIL) {
+ msg = xenbus_transaction_start(&xbt);
+ if (msg)
+ goto exit;
+ xbt_flag = 1;
+ }
+
+ msg = xenbus_read(xbt, path, &current_state);
+ if (msg)
+ goto exit;
+
+ rs = (XenbusState)(current_state[0] - '0');
+ free(current_state);
+ if (rs == state) {
+ msg = NULL;
+ goto exit;
+ }
+
+ snprintf(value, 2, "%d", state);
+ msg = xenbus_write(xbt, path, value);
+
+exit:
+ if (xbt_flag) {
+ msg2 = xenbus_transaction_end(xbt, 0, &retry);
+ xbt = XBT_NIL;
+ }
+ if (msg == NULL && msg2 != NULL)
+ msg = msg2;
+ else
+ free(msg2);
+ } while (retry);
+
+ return msg;
+}
+
+char *xenbus_wait_for_state_change(const char *path, XenbusState *state)
+{
+ for (;;) {
+ char *res, *msg;
+ XenbusState rs;
+
+ msg = xenbus_read(XBT_NIL, path, &res);
+ if (msg)
+ return msg;
+
+ rs = (XenbusState)(res[0] - 48);
+ free(res);
+
+ if (rs == *state) {
+ wait_event_timeout(NULL, false, WAIT_XENBUS_POLL_TO_MS);
+ } else {
+ *state = rs;
+ break;
+ }
+ }
+ return NULL;
+}
+
+/* Send data to xenbus. This can block. All of the requests are seen
+ * by xenbus as if sent atomically. The header is added
+ * automatically, using type %type, req_id %req_id, and trans_id
+ * %trans_id.
+ */
+static void xb_write(int type, int req_id, xenbus_transaction_t trans_id,
+ const struct write_req *req, int nr_reqs)
+{
+ XENSTORE_RING_IDX prod;
+ int r;
+ int len = 0;
+ const struct write_req *cur_req;
+ int req_off;
+ int total_off;
+ int this_chunk;
+ struct xsd_sockmsg m = {
+ .type = type,
+ .req_id = req_id,
+ .tx_id = trans_id
+ };
+ struct write_req header_req = {
+ &m,
+ sizeof(m)
+ };
+
+ for (r = 0; r < nr_reqs; r++)
+ len += req[r].len;
+ m.len = len;
+ len += sizeof(m);
+
+ cur_req = &header_req;
+
+ BUG_ON(len > XENSTORE_RING_SIZE);
+ prod = xenstore_buf->req_prod;
+ /* We are running synchronously, so it is a bug if we do not
+ * have enough room to send a message: please note that a message
+ * can occupy multiple slots in the ring buffer.
+ */
+ BUG_ON(prod + len - xenstore_buf->req_cons > XENSTORE_RING_SIZE);
+
+ total_off = 0;
+ req_off = 0;
+ while (total_off < len) {
+ this_chunk = min(cur_req->len - req_off,
+ XENSTORE_RING_SIZE - MASK_XENSTORE_IDX(prod));
+ memcpy((char *)xenstore_buf->req + MASK_XENSTORE_IDX(prod),
+ (char *)cur_req->data + req_off, this_chunk);
+ prod += this_chunk;
+ req_off += this_chunk;
+ total_off += this_chunk;
+ if (req_off == cur_req->len) {
+ req_off = 0;
+ if (cur_req == &header_req)
+ cur_req = req;
+ else
+ cur_req++;
+ }
+ }
+
+ BUG_ON(req_off != 0);
+ BUG_ON(total_off != len);
+ BUG_ON(prod > xenstore_buf->req_cons + XENSTORE_RING_SIZE);
+
+ /* Remote must see entire message before updating indexes */
+ wmb();
+
+ xenstore_buf->req_prod += len;
+
+ /* Send evtchn to notify remote */
+ notify_remote_via_evtchn(xenbus_evtchn);
+}
+
+/* Send a message to xenbus, in the same fashion as xb_write, and
+ * block waiting for a reply. The reply is malloced and should be
+ * freed by the caller.
+ */
+struct xsd_sockmsg *xenbus_msg_reply(int type,
+ xenbus_transaction_t trans,
+ struct write_req *io,
+ int nr_reqs)
+{
+ struct xsd_sockmsg *rep;
+
+ /* We do not use request identifier which is echoed in daemon's response. */
+ xb_write(type, 0, trans, io, nr_reqs);
+ /* Now wait for the message to arrive. */
+ if (!xenbus_get_reply(&rep))
+ return NULL;
+ return rep;
+}
+
+static char *errmsg(struct xsd_sockmsg *rep)
+{
+ char *res;
+
+ if (!rep) {
+ char msg[] = "No reply";
+ size_t len = strlen(msg) + 1;
+
+ return memcpy(malloc(len), msg, len);
+ }
+ if (rep->type != XS_ERROR)
+ return NULL;
+ res = malloc(rep->len + 1);
+ memcpy(res, rep + 1, rep->len);
+ res[rep->len] = 0;
+ free(rep);
+ return res;
+}
+
+/* List the contents of a directory. Returns a malloc()ed array of
+ * pointers to malloc()ed strings. The array is NULL terminated. May
+ * block.
+ */
+char *xenbus_ls(xenbus_transaction_t xbt, const char *pre, char ***contents)
+{
+ struct xsd_sockmsg *reply, *repmsg;
+ struct write_req req[] = { { pre, strlen(pre) + 1 } };
+ int nr_elems, x, i;
+ char **res, *msg;
+
+ repmsg = xenbus_msg_reply(XS_DIRECTORY, xbt, req, ARRAY_SIZE(req));
+ msg = errmsg(repmsg);
+ if (msg) {
+ *contents = NULL;
+ return msg;
+ }
+ reply = repmsg + 1;
+ for (x = nr_elems = 0; x < repmsg->len; x++)
+ nr_elems += (((char *)reply)[x] == 0);
+ res = malloc(sizeof(res[0]) * (nr_elems + 1));
+ for (x = i = 0; i < nr_elems; i++) {
+ int l = strlen((char *)reply + x);
+
+ res[i] = malloc(l + 1);
+ memcpy(res[i], (char *)reply + x, l + 1);
+ x += l + 1;
+ }
+ res[i] = NULL;
+ free(repmsg);
+ *contents = res;
+ return NULL;
+}
+
+char *xenbus_read(xenbus_transaction_t xbt, const char *path, char **value)
+{
+ struct write_req req[] = { {path, strlen(path) + 1} };
+ struct xsd_sockmsg *rep;
+ char *res, *msg;
+
+ rep = xenbus_msg_reply(XS_READ, xbt, req, ARRAY_SIZE(req));
+ msg = errmsg(rep);
+ if (msg) {
+ *value = NULL;
+ return msg;
+ }
+ res = malloc(rep->len + 1);
+ memcpy(res, rep + 1, rep->len);
+ res[rep->len] = 0;
+ free(rep);
+ *value = res;
+ return NULL;
+}
+
+char *xenbus_write(xenbus_transaction_t xbt, const char *path,
+ const char *value)
+{
+ struct write_req req[] = {
+ {path, strlen(path) + 1},
+ {value, strlen(value)},
+ };
+ struct xsd_sockmsg *rep;
+ char *msg;
+
+ rep = xenbus_msg_reply(XS_WRITE, xbt, req, ARRAY_SIZE(req));
+ msg = errmsg(rep);
+ if (msg)
+ return msg;
+ free(rep);
+ return NULL;
+}
+
+char *xenbus_rm(xenbus_transaction_t xbt, const char *path)
+{
+ struct write_req req[] = { {path, strlen(path) + 1} };
+ struct xsd_sockmsg *rep;
+ char *msg;
+
+ rep = xenbus_msg_reply(XS_RM, xbt, req, ARRAY_SIZE(req));
+ msg = errmsg(rep);
+ if (msg)
+ return msg;
+ free(rep);
+ return NULL;
+}
+
+char *xenbus_get_perms(xenbus_transaction_t xbt, const char *path, char **value)
+{
+ struct write_req req[] = { {path, strlen(path) + 1} };
+ struct xsd_sockmsg *rep;
+ char *res, *msg;
+
+ rep = xenbus_msg_reply(XS_GET_PERMS, xbt, req, ARRAY_SIZE(req));
+ msg = errmsg(rep);
+ if (msg) {
+ *value = NULL;
+ return msg;
+ }
+ res = malloc(rep->len + 1);
+ memcpy(res, rep + 1, rep->len);
+ res[rep->len] = 0;
+ free(rep);
+ *value = res;
+ return NULL;
+}
+
+#define PERM_MAX_SIZE 32
+char *xenbus_set_perms(xenbus_transaction_t xbt, const char *path,
+ domid_t dom, char perm)
+{
+ char value[PERM_MAX_SIZE];
+ struct write_req req[] = {
+ {path, strlen(path) + 1},
+ {value, 0},
+ };
+ struct xsd_sockmsg *rep;
+ char *msg;
+
+ snprintf(value, PERM_MAX_SIZE, "%c%hu", perm, dom);
+ req[1].len = strlen(value) + 1;
+ rep = xenbus_msg_reply(XS_SET_PERMS, xbt, req, ARRAY_SIZE(req));
+ msg = errmsg(rep);
+ if (msg)
+ return msg;
+ free(rep);
+ return NULL;
+}
+
+char *xenbus_transaction_start(xenbus_transaction_t *xbt)
+{
+ /* Xenstored becomes angry if you send a length 0 message, so just
+ * shove a nul terminator on the end
+ */
+ struct write_req req = { "", 1};
+ struct xsd_sockmsg *rep;
+ char *err;
+
+ rep = xenbus_msg_reply(XS_TRANSACTION_START, 0, &req, 1);
+ err = errmsg(rep);
+ if (err)
+ return err;
+ sscanf((char *)(rep + 1), "%lu", xbt);
+ free(rep);
+ return NULL;
+}
+
+char *xenbus_transaction_end(xenbus_transaction_t t, int abort, int *retry)
+{
+ struct xsd_sockmsg *rep;
+ struct write_req req;
+ char *err;
+
+ *retry = 0;
+
+ req.data = abort ? "F" : "T";
+ req.len = 2;
+ rep = xenbus_msg_reply(XS_TRANSACTION_END, t, &req, 1);
+ err = errmsg(rep);
+ if (err) {
+ if (!strcmp(err, "EAGAIN")) {
+ *retry = 1;
+ free(err);
+ return NULL;
+ } else {
+ return err;
+ }
+ }
+ free(rep);
+ return NULL;
+}
+
+int xenbus_read_integer(const char *path)
+{
+ char *res, *buf;
+ int t;
+
+ res = xenbus_read(XBT_NIL, path, &buf);
+ if (res) {
+ printk("Failed to read %s.\n", path);
+ free(res);
+ return -1;
+ }
+ sscanf(buf, "%d", &t);
+ free(buf);
+ return t;
+}
+
+int xenbus_read_uuid(const char *path, unsigned char uuid[16])
+{
+ char *res, *buf;
+
+ res = xenbus_read(XBT_NIL, path, &buf);
+ if (res) {
+ printk("Failed to read %s.\n", path);
+ free(res);
+ return 0;
+ }
+ if (strlen(buf) != ((2 * 16) + 4) /* 16 hex bytes and 4 hyphens */
+ || sscanf(buf,
+ "%2hhx%2hhx%2hhx%2hhx-"
+ "%2hhx%2hhx-"
+ "%2hhx%2hhx-"
+ "%2hhx%2hhx-"
+ "%2hhx%2hhx%2hhx%2hhx%2hhx%2hhx",
+ uuid, uuid + 1, uuid + 2, uuid + 3,
+ uuid + 4, uuid + 5, uuid + 6, uuid + 7,
+ uuid + 8, uuid + 9, uuid + 10, uuid + 11,
+ uuid + 12, uuid + 13, uuid + 14, uuid + 15) != 16) {
+ printk("Xenbus path %s value %s is not a uuid!\n", path, buf);
+ free(buf);
+ return 0;
+ }
+ free(buf);
+ return 1;
+}
+
+char *xenbus_printf(xenbus_transaction_t xbt,
+ const char *node, const char *path,
+ const char *fmt, ...)
+{
+#define BUFFER_SIZE 256
+ char fullpath[BUFFER_SIZE];
+ char val[BUFFER_SIZE];
+ va_list args;
+
+ BUG_ON(strlen(node) + strlen(path) + 1 >= BUFFER_SIZE);
+ sprintf(fullpath, "%s/%s", node, path);
+ va_start(args, fmt);
+ vsprintf(val, fmt, args);
+ va_end(args);
+ return xenbus_write(xbt, fullpath, val);
+}
+
+domid_t xenbus_get_self_id(void)
+{
+ char *dom_id;
+ domid_t ret;
+
+ BUG_ON(xenbus_read(XBT_NIL, "domid", &dom_id));
+ sscanf(dom_id, "%"SCNd16, &ret);
+
+ return ret;
+}
+
+void init_xenbus(void)
+{
+ u64 v;
+
+ debug("%s\n", __func__);
+ if (hvm_get_parameter(HVM_PARAM_STORE_EVTCHN, &v))
+ BUG();
+ xenbus_evtchn = v;
+
+ if (hvm_get_parameter(HVM_PARAM_STORE_PFN, &v))
+ BUG();
+ xenstore_buf = (struct xenstore_domain_interface *)map_frame_virt(v);
+}
+
+void fini_xenbus(void)
+{
+ debug("%s\n", __func__);
+}