--- /dev/null
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2018, Bin Meng <bmeng.cn@gmail.com>
+ *
+ * VirtIO Sandbox transport driver, for testing purpose only
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <virtio_types.h>
+#include <virtio.h>
+#include <virtio_ring.h>
+#include <linux/compat.h>
+#include <linux/io.h>
+
+struct virtio_sandbox_priv {
+ u8 id;
+ u8 status;
+ u64 device_features;
+ u64 driver_features;
+ ulong queue_desc;
+ ulong queue_available;
+ ulong queue_used;
+};
+
+static int virtio_sandbox_get_config(struct udevice *udev, unsigned int offset,
+ void *buf, unsigned int len)
+{
+ return 0;
+}
+
+static int virtio_sandbox_set_config(struct udevice *udev, unsigned int offset,
+ const void *buf, unsigned int len)
+{
+ return 0;
+}
+
+static int virtio_sandbox_get_status(struct udevice *udev, u8 *status)
+{
+ struct virtio_sandbox_priv *priv = dev_get_priv(udev);
+
+ *status = priv->status;
+
+ return 0;
+}
+
+static int virtio_sandbox_set_status(struct udevice *udev, u8 status)
+{
+ struct virtio_sandbox_priv *priv = dev_get_priv(udev);
+
+ /* We should never be setting status to 0 */
+ WARN_ON(status == 0);
+
+ priv->status = status;
+
+ return 0;
+}
+
+static int virtio_sandbox_reset(struct udevice *udev)
+{
+ struct virtio_sandbox_priv *priv = dev_get_priv(udev);
+
+ /* 0 status means a reset */
+ priv->status = 0;
+
+ return 0;
+}
+
+static int virtio_sandbox_get_features(struct udevice *udev, u64 *features)
+{
+ struct virtio_sandbox_priv *priv = dev_get_priv(udev);
+
+ *features = priv->device_features;
+
+ return 0;
+}
+
+static int virtio_sandbox_set_features(struct udevice *udev)
+{
+ struct virtio_sandbox_priv *priv = dev_get_priv(udev);
+ struct virtio_dev_priv *uc_priv = dev_get_uclass_priv(udev);
+
+ priv->driver_features = uc_priv->features;
+
+ return 0;
+}
+
+static struct virtqueue *virtio_sandbox_setup_vq(struct udevice *udev,
+ unsigned int index)
+{
+ struct virtio_sandbox_priv *priv = dev_get_priv(udev);
+ struct virtqueue *vq;
+ ulong addr;
+ int err;
+
+ /* Create the vring */
+ vq = vring_create_virtqueue(index, 4, 4096, udev);
+ if (!vq) {
+ err = -ENOMEM;
+ goto error_new_virtqueue;
+ }
+
+ addr = virtqueue_get_desc_addr(vq);
+ priv->queue_desc = addr;
+
+ addr = virtqueue_get_avail_addr(vq);
+ priv->queue_available = addr;
+
+ addr = virtqueue_get_used_addr(vq);
+ priv->queue_used = addr;
+
+ return vq;
+
+error_new_virtqueue:
+ return ERR_PTR(err);
+}
+
+static void virtio_sandbox_del_vq(struct virtqueue *vq)
+{
+ vring_del_virtqueue(vq);
+}
+
+static int virtio_sandbox_del_vqs(struct udevice *udev)
+{
+ struct virtio_dev_priv *uc_priv = dev_get_uclass_priv(udev);
+ struct virtqueue *vq, *n;
+
+ list_for_each_entry_safe(vq, n, &uc_priv->vqs, list)
+ virtio_sandbox_del_vq(vq);
+
+ return 0;
+}
+
+static int virtio_sandbox_find_vqs(struct udevice *udev, unsigned int nvqs,
+ struct virtqueue *vqs[])
+{
+ int i;
+
+ for (i = 0; i < nvqs; ++i) {
+ vqs[i] = virtio_sandbox_setup_vq(udev, i);
+ if (IS_ERR(vqs[i])) {
+ virtio_sandbox_del_vqs(udev);
+ return PTR_ERR(vqs[i]);
+ }
+ }
+
+ return 0;
+}
+
+static int virtio_sandbox_notify(struct udevice *udev, struct virtqueue *vq)
+{
+ return 0;
+}
+
+static int virtio_sandbox_probe(struct udevice *udev)
+{
+ struct virtio_sandbox_priv *priv = dev_get_priv(udev);
+ struct virtio_dev_priv *uc_priv = dev_get_uclass_priv(udev);
+
+ /* fake some information for testing */
+ priv->device_features = VIRTIO_F_VERSION_1;
+ uc_priv->device = VIRTIO_ID_BLOCK;
+ uc_priv->vendor = ('u' << 24) | ('b' << 16) | ('o' << 8) | 't';
+
+ return 0;
+}
+
+/* check virtio device driver's remove routine was called to reset the device */
+static int virtio_sandbox_child_post_remove(struct udevice *vdev)
+{
+ u8 status;
+
+ virtio_get_status(vdev, &status);
+ if (status)
+ panic("virtio device was not reset\n");
+
+ return 0;
+}
+
+static const struct dm_virtio_ops virtio_sandbox1_ops = {
+ .get_config = virtio_sandbox_get_config,
+ .set_config = virtio_sandbox_set_config,
+ .get_status = virtio_sandbox_get_status,
+ .set_status = virtio_sandbox_set_status,
+ .reset = virtio_sandbox_reset,
+ .get_features = virtio_sandbox_get_features,
+ .set_features = virtio_sandbox_set_features,
+ .find_vqs = virtio_sandbox_find_vqs,
+ .del_vqs = virtio_sandbox_del_vqs,
+ .notify = virtio_sandbox_notify,
+};
+
+static const struct udevice_id virtio_sandbox1_ids[] = {
+ { .compatible = "sandbox,virtio1" },
+ { }
+};
+
+U_BOOT_DRIVER(virtio_sandbox1) = {
+ .name = "virtio-sandbox1",
+ .id = UCLASS_VIRTIO,
+ .of_match = virtio_sandbox1_ids,
+ .ops = &virtio_sandbox1_ops,
+ .probe = virtio_sandbox_probe,
+ .child_post_remove = virtio_sandbox_child_post_remove,
+ .priv_auto_alloc_size = sizeof(struct virtio_sandbox_priv),
+};
+
+/* this one without notify op */
+static const struct dm_virtio_ops virtio_sandbox2_ops = {
+ .get_config = virtio_sandbox_get_config,
+ .set_config = virtio_sandbox_set_config,
+ .get_status = virtio_sandbox_get_status,
+ .set_status = virtio_sandbox_set_status,
+ .reset = virtio_sandbox_reset,
+ .get_features = virtio_sandbox_get_features,
+ .set_features = virtio_sandbox_set_features,
+ .find_vqs = virtio_sandbox_find_vqs,
+ .del_vqs = virtio_sandbox_del_vqs,
+};
+
+static const struct udevice_id virtio_sandbox2_ids[] = {
+ { .compatible = "sandbox,virtio2" },
+ { }
+};
+
+U_BOOT_DRIVER(virtio_sandbox2) = {
+ .name = "virtio-sandbox2",
+ .id = UCLASS_VIRTIO,
+ .of_match = virtio_sandbox2_ids,
+ .ops = &virtio_sandbox2_ops,
+ .probe = virtio_sandbox_probe,
+ .priv_auto_alloc_size = sizeof(struct virtio_sandbox_priv),
+};