123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867 |
- // 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);
- 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_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,
- };
|