diff options
author | Anastasiia Lukianenko <anastasiia_lukianenko@epam.com> | 2020-08-06 12:42:57 +0300 |
---|---|---|
committer | Tom Rini <trini@konsulko.com> | 2020-08-14 22:18:30 +0300 |
commit | 17c96f885134922f660e6c78519a4df73ae3c738 (patch) | |
tree | 4c42184aea82ca1ac248dd90458ca6913a987837 /drivers/xen/pvblock.c | |
parent | a99931319e90229a85e9add500375d0357414cf2 (diff) | |
download | u-boot-17c96f885134922f660e6c78519a4df73ae3c738.tar.xz |
xen: pvblock: Read XenStore configuration and initialize
Read essential virtual block device configuration data from XenStore,
initialize front ring and event channel.
Update block device description with actual block size.
Use code for XenStore from mini-os.
Signed-off-by: Oleksandr Andrushchenko <oleksandr_andrushchenko@epam.com>
Signed-off-by: Anastasiia Lukianenko <anastasiia_lukianenko@epam.com>
Diffstat (limited to 'drivers/xen/pvblock.c')
-rw-r--r-- | drivers/xen/pvblock.c | 285 |
1 files changed, 284 insertions, 1 deletions
diff --git a/drivers/xen/pvblock.c b/drivers/xen/pvblock.c index 3ed62cac5d..e247ce33a3 100644 --- a/drivers/xen/pvblock.c +++ b/drivers/xen/pvblock.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0+ /* + * (C) 2007-2008 Samuel Thibault. * (C) Copyright 2020 EPAM Systems Inc. */ #include <blk.h> @@ -9,26 +10,302 @@ #include <malloc.h> #include <part.h> +#include <asm/armv8/mmu.h> +#include <asm/io.h> +#include <asm/xen/system.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 + +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 { - char dummy; + 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; }; struct blkfront_platdata { unsigned int devid; }; +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->nodename); + free(dev); +} + +static void blkfront_handler(evtchn_port_t port, struct pt_regs *regs, + void *data) +{ + printf("%s [Port %d] - event received\n", __func__, port); +} + 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, blkfront_handler, 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); + + debug("%llu sectors of %u bytes\n", + dev->info.sectors, dev->info.sector_size); + 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); + + 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); } ulong pvblock_blk_read(struct udevice *udev, lbaint_t blknr, lbaint_t blkcnt, @@ -73,6 +350,7 @@ 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; @@ -81,6 +359,11 @@ static int pvblock_blk_probe(struct udevice *udev) 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; } |