aboutsummaryrefslogtreecommitdiffstats
path: root/roms/u-boot/drivers/xen
diff options
context:
space:
mode:
authorAngelos Mouzakitis <a.mouzakitis@virtualopensystems.com>2023-10-10 14:33:42 +0000
committerAngelos Mouzakitis <a.mouzakitis@virtualopensystems.com>2023-10-10 14:33:42 +0000
commitaf1a266670d040d2f4083ff309d732d648afba2a (patch)
tree2fc46203448ddcc6f81546d379abfaeb323575e9 /roms/u-boot/drivers/xen
parente02cda008591317b1625707ff8e115a4841aa889 (diff)
Add submodule dependency filesHEADmaster
Change-Id: Iaf8d18082d3991dec7c0ebbea540f092188eb4ec
Diffstat (limited to 'roms/u-boot/drivers/xen')
-rw-r--r--roms/u-boot/drivers/xen/Kconfig10
-rw-r--r--roms/u-boot/drivers/xen/Makefile10
-rw-r--r--roms/u-boot/drivers/xen/events.c199
-rw-r--r--roms/u-boot/drivers/xen/gnttab.c218
-rw-r--r--roms/u-boot/drivers/xen/hypervisor.c254
-rw-r--r--roms/u-boot/drivers/xen/pvblock.c868
-rw-r--r--roms/u-boot/drivers/xen/xenbus.c557
7 files changed, 2116 insertions, 0 deletions
diff --git a/roms/u-boot/drivers/xen/Kconfig b/roms/u-boot/drivers/xen/Kconfig
new file mode 100644
index 000000000..6ad2a9366
--- /dev/null
+++ b/roms/u-boot/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/roms/u-boot/drivers/xen/Makefile b/roms/u-boot/drivers/xen/Makefile
new file mode 100644
index 000000000..87157df69
--- /dev/null
+++ b/roms/u-boot/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/roms/u-boot/drivers/xen/events.c b/roms/u-boot/drivers/xen/events.c
new file mode 100644
index 000000000..c490f87b2
--- /dev/null
+++ b/roms/u-boot/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/roms/u-boot/drivers/xen/gnttab.c b/roms/u-boot/drivers/xen/gnttab.c
new file mode 100644
index 000000000..778729d64
--- /dev/null
+++ b/roms/u-boot/drivers/xen/gnttab.c
@@ -0,0 +1,218 @@
+// 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 <asm/global_data.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 {
+ flags = nflags;
+ if ((flags) & (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/roms/u-boot/drivers/xen/hypervisor.c b/roms/u-boot/drivers/xen/hypervisor.c
new file mode 100644
index 000000000..256089483
--- /dev/null
+++ b/roms/u-boot/drivers/xen/hypervisor.c
@@ -0,0 +1,254 @@
+// 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]);
+}
+
+int xen_init(void)
+{
+ debug("%s\n", __func__);
+
+ map_shared_info(NULL);
+ init_events();
+ init_xenbus();
+ init_gnttab();
+
+ return 0;
+}
+
+void xen_fini(void)
+{
+ debug("%s\n", __func__);
+
+ fini_gnttab();
+ fini_xenbus();
+ fini_events();
+}
diff --git a/roms/u-boot/drivers/xen/pvblock.c b/roms/u-boot/drivers/xen/pvblock.c
new file mode 100644
index 000000000..1c5d039ef
--- /dev/null
+++ b/roms/u-boot/drivers/xen/pvblock.c
@@ -0,0 +1,868 @@
+// 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/global_data.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_plat {
+ 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);
+
+ err = xenbus_switch_state(XBT_NIL, nodename, XenbusStateClosing);
+ if (err) {
+ 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);
+
+ err = xenbus_switch_state(XBT_NIL, nodename, XenbusStateClosed);
+ if (err) {
+ 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);
+ }
+
+ err = xenbus_switch_state(XBT_NIL, nodename, XenbusStateInitialising);
+ if (err) {
+ 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_plat(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_plat(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_plat *plat = dev_get_plat(udev);
+ struct blk_desc *desc = dev_get_uclass_plat(udev);
+ int ret, devid;
+
+ devid = plat->devid;
+ free(plat);
+
+ 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 = 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_plat *plat;
+ int ret;
+
+ debug("New " DRV_NAME_BLK ", device ID %d\n", devid);
+
+ plat = malloc(sizeof(struct blkfront_plat));
+ if (!plat) {
+ printf("Failed to allocate platform data\n");
+ return -ENOMEM;
+ }
+
+ plat->devid = devid;
+
+ info.name = DRV_NAME_BLK;
+ info.plat = plat;
+
+ 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(plat);
+ }
+ 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_plat(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/roms/u-boot/drivers/xen/xenbus.c b/roms/u-boot/drivers/xen/xenbus.c
new file mode 100644
index 000000000..177d14472
--- /dev/null
+++ b/roms/u-boot/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__);
+}