diff options
Diffstat (limited to 'roms/u-boot/drivers/xen/pvblock.c')
-rw-r--r-- | roms/u-boot/drivers/xen/pvblock.c | 868 |
1 files changed, 868 insertions, 0 deletions
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, +}; |