
From: Simon Glass <sjg@chromium.org> Add an emulator driver for block devices, so that sandbox can test these fully. The emulator uses MMIO to communicate with the controlling virtio device. Signed-off-by: Simon Glass <sjg@chromium.org> --- configs/sandbox_defconfig | 1 + drivers/virtio/Makefile | 2 +- drivers/virtio/emul_blk.c | 153 ++++++++++++++++++++++++++++++++++++ drivers/virtio/virtio_blk.h | 3 + 4 files changed, 158 insertions(+), 1 deletion(-) create mode 100644 drivers/virtio/emul_blk.c diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig index 069536b2cdd..060fa0f9934 100644 --- a/configs/sandbox_defconfig +++ b/configs/sandbox_defconfig @@ -363,3 +363,4 @@ CONFIG_TEST_FDTDEC=y CONFIG_UNIT_TEST=y CONFIG_UT_TIME=y CONFIG_UT_DM=y +CONFIG_VIRTIO_BLK=y diff --git a/drivers/virtio/Makefile b/drivers/virtio/Makefile index d928c7b0ad2..4709e16f789 100644 --- a/drivers/virtio/Makefile +++ b/drivers/virtio/Makefile @@ -8,7 +8,7 @@ obj-$(CONFIG_VIRTIO_MMIO) += virtio_mmio.o obj-$(CONFIG_VIRTIO_PCI) += virtio_pci_modern.o obj-$(CONFIG_VIRTIO_PCI_LEGACY) += virtio_pci_legacy.o obj-$(CONFIG_VIRTIO_SANDBOX) += virtio_sandbox.o -obj-$(CONFIG_VIRTIO_SANDBOX_EMUL) += sandbox_emul.o +obj-$(CONFIG_VIRTIO_SANDBOX_EMUL) += sandbox_emul.o emul_blk.o obj-$(CONFIG_VIRTIO_NET) += virtio_net.o obj-$(CONFIG_VIRTIO_BLK) += virtio_blk.o obj-$(CONFIG_VIRTIO_RNG) += virtio_rng.o diff --git a/drivers/virtio/emul_blk.c b/drivers/virtio/emul_blk.c new file mode 100644 index 00000000000..1d42b34b8ab --- /dev/null +++ b/drivers/virtio/emul_blk.c @@ -0,0 +1,153 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Emulation of a block device. This implements a simple version of the QEMU + * side of the interface. + * + * Copyright 2025 Simon Glass <sjg@chromium.org> + */ + +#define LOG_CATEGORY UCLASS_VIRTIO + +#include <dm.h> +#include <malloc.h> +#include <asm/io.h> +#include <dt-bindings/virtio.h> +#include <linux/sizes.h> +#include "virtio_blk.h" +#include "virtio_ring.h" +#include "sandbox_emul.h" + +enum { + DISK_SIZE_MB = 1, + SECTOR_SIZE = 512, +}; + +/** + * struct virtio_blk_emul_priv - private data for the block emulator + * + * @config: virtio block-device-configuration structure, exposed to the driver + * through the config space + * @disk_data: allocated memory for the virtual disk + * @disk_size: total size of the virtual disk in bytes + */ +struct virtio_blk_emul_priv { + struct virtio_blk_config config; + void *disk_data; + u64 disk_size; +}; + +static int blk_emul_process_request(struct udevice *dev, + struct vring_desc *descs, u32 head_idx, + int *writtenp) +{ + struct virtio_blk_emul_priv *priv = dev_get_priv(dev); + struct vring_desc *hdr_desc, *data_desc, *status_desc; + struct virtio_blk_outhdr *hdr; + void *data_buf; + u64 offset; + u8 *status; + + hdr_desc = &descs[head_idx]; + if (!(hdr_desc->flags & VRING_DESC_F_NEXT)) + return -EIO; + data_desc = &descs[hdr_desc->next]; + if (!(data_desc->flags & VRING_DESC_F_NEXT)) + return -EIO; + status_desc = &descs[data_desc->next]; + + hdr = (struct virtio_blk_outhdr *)hdr_desc->addr; + status = (u8 *)status_desc->addr; + + offset = hdr->sector * SECTOR_SIZE; + if (offset + data_desc->len > priv->disk_size) { + *status = VIRTIO_BLK_S_IOERR; + *writtenp = 1; + return 0; + } + + data_buf = (void *)data_desc->addr; + + switch (hdr->type) { + case VIRTIO_BLK_T_IN: + log_debug("read: sector %lld, len %u\n", hdr->sector, + data_desc->len); + memcpy(data_buf, priv->disk_data + offset, data_desc->len); + *writtenp = data_desc->len; + break; + case VIRTIO_BLK_T_OUT: + log_debug("write: sector %lld, len %u\n", hdr->sector, + data_desc->len); + memcpy(priv->disk_data + offset, data_buf, data_desc->len); + *writtenp = 0; + break; + default: + log_warning("unknown request type 0x%x\n", hdr->type); + *status = VIRTIO_BLK_S_UNSUPP; + *writtenp = 1; + return 0; + } + + *status = VIRTIO_BLK_S_OK; + *writtenp += 1; /* For the status byte */ + + return 0; +} + +static int blk_emul_get_config(struct udevice *dev, ulong offset, void *buf, + enum sandboxio_size_t size) +{ + struct virtio_blk_emul_priv *priv = dev_get_priv(dev); + + if (offset + size > sizeof(priv->config)) + return -EIO; + + memcpy(buf, (u8 *)&priv->config + offset, size); + + return 0; +} + +static u64 blk_emul_get_features(struct udevice *dev) +{ + return BIT(VIRTIO_BLK_F_BLK_SIZE); +} + +static u32 blk_emul_get_device_id(struct udevice *dev) +{ + return VIRTIO_ID_BLOCK; +} + +static int virtio_blk_emul_probe(struct udevice *dev) +{ + struct virtio_blk_emul_priv *priv = dev_get_priv(dev); + + priv->disk_size = (u64)DISK_SIZE_MB * SZ_1M; + priv->disk_data = calloc(1, priv->disk_size); + if (!priv->disk_data) + return -ENOMEM; + + priv->config.capacity = priv->disk_size / SECTOR_SIZE; + priv->config.blk_size = SECTOR_SIZE; + + return 0; +} + +static struct virtio_emul_ops blk_emul_ops = { + .process_request = blk_emul_process_request, + .get_config = blk_emul_get_config, + .get_features = blk_emul_get_features, + .get_device_id = blk_emul_get_device_id, +}; + +static const struct udevice_id virtio_blk_emul_ids[] = { + { .compatible = "sandbox,virtio-blk-emul" }, + { } +}; + +U_BOOT_DRIVER(virtio_blk_emul) = { + .name = "virtio_blk_emul", + .id = UCLASS_VIRTIO_EMUL, + .of_match = virtio_blk_emul_ids, + .probe = virtio_blk_emul_probe, + .ops = &blk_emul_ops, + .priv_auto = sizeof(struct virtio_blk_emul_priv), +}; diff --git a/drivers/virtio/virtio_blk.h b/drivers/virtio/virtio_blk.h index b37ba264df4..cbb6996ebde 100644 --- a/drivers/virtio/virtio_blk.h +++ b/drivers/virtio/virtio_blk.h @@ -9,6 +9,9 @@ #ifndef _LINUX_VIRTIO_BLK_H #define _LINUX_VIRTIO_BLK_H +#include <compiler.h> +#include "virtio_types.h" + /* Feature bits */ #define VIRTIO_BLK_F_SIZE_MAX 1 /* Indicates maximum segment size */ #define VIRTIO_BLK_F_SEG_MAX 2 /* Indicates maximum # of segments */ -- 2.43.0