We were able to get various Linux kernels to mount a disk image. Used linux/lguest.c and virtio_net.c as starting points. This included adding the block device struct and request and init functions.
Change-Id: I91a603d5a6e9c27c87a29d0784de6fe17cc94916 Signed-off-by: Kyle Milka <[email protected]> Signed-off-by: Gan Shun Lim <[email protected]> --- tests/vmm/vmrunkernel.c | 45 ++++++++++++ user/vmm/include/vmm/virtio_blk.h | 3 + user/vmm/include/vmm/vmm.h | 1 + user/vmm/virtio.c | 2 + user/vmm/virtio_blk.c | 145 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 196 insertions(+) create mode 100644 user/vmm/virtio_blk.c diff --git a/tests/vmm/vmrunkernel.c b/tests/vmm/vmrunkernel.c index 034b4fc..609f7d2 100644 --- a/tests/vmm/vmrunkernel.c +++ b/tests/vmm/vmrunkernel.c @@ -23,6 +23,7 @@ #include <vmm/linux_bootparam.h> #include <vmm/virtio.h> +#include <vmm/virtio_blk.h> #include <vmm/virtio_mmio.h> #include <vmm/virtio_ids.h> #include <vmm/virtio_config.h> @@ -252,6 +253,36 @@ static struct virtio_vq_dev net_vqdev = { } }; +static struct virtio_mmio_dev blk_mmio_dev = { + .poke_guest = virtio_poke_guest, +}; + +static struct virtio_blk_config blk_cfg = { +}; + +static struct virtio_blk_config blk_cfg_d = { +}; + +static struct virtio_vq_dev blk_vqdev = { + .name = "block", + .dev_id = VIRTIO_ID_BLOCK, + .dev_feat = (1ULL << VIRTIO_F_VERSION_1), + + .num_vqs = 1, + .cfg = &blk_cfg, + .cfg_d = &blk_cfg_d, + .cfg_sz = sizeof(struct virtio_blk_config), + .transport_dev = &blk_mmio_dev, + .vqs = { + { + .name = "blk_request", + .qnum_max = 64, + .srv_fn = blk_request, + .vqdev = &blk_vqdev + }, + } +}; + void lowmem() { __asm__ __volatile__ (".section .lowmem, \"aw\"\n\tlow: \n\t.=0x1000\n\t.align 0x100000\n\t.previous\n"); } @@ -346,6 +377,7 @@ int main(int argc, char **argv) uint64_t tsc_freq_khz; char *cmdlinep; int cmdlinesz, len; + char *disk_image_file = NULL; fprintf(stderr, "%p %p %p %p\n", PGSIZE, PGSHIFT, PML1_SHIFT, PML1_PTE_REACH); @@ -384,6 +416,7 @@ int main(int argc, char **argv) argc--, argv++; // switches ... // Sorry, I don't much like the gnu opt parsing code. + // TODO(dcross): Convert this to use getopt() while (1) { if (*argv[0] != '-') break; @@ -407,6 +440,10 @@ int main(int argc, char **argv) case 's': /* scp */ parlib_wants_to_be_mcp = FALSE; break; + case 'f': /* file to pass to blk_init */ + argc--; argv++; + disk_image_file = *argv; + break; default: fprintf(stderr, "BMAFR\n"); break; @@ -600,6 +637,14 @@ int main(int argc, char **argv) net_mmio_dev.vqdev = &net_vqdev; vm->virtio_mmio_devices[VIRTIO_MMIO_NETWORK_DEV] = &net_mmio_dev; + if (disk_image_file != NULL) { + blk_mmio_dev.addr = + virtio_mmio_base_addr + PGSIZE * VIRTIO_MMIO_BLOCK_DEV; + blk_mmio_dev.vqdev = &blk_vqdev; + vm->virtio_mmio_devices[VIRTIO_MMIO_BLOCK_DEV] = &blk_mmio_dev; + blk_init_fn(&blk_vqdev, disk_image_file); + } + net_init_fn(&net_vqdev, default_nic); /* Set the kernel command line parameters */ diff --git a/user/vmm/include/vmm/virtio_blk.h b/user/vmm/include/vmm/virtio_blk.h index dbaaca4..b39d855 100644 --- a/user/vmm/include/vmm/virtio_blk.h +++ b/user/vmm/include/vmm/virtio_blk.h @@ -144,3 +144,6 @@ struct virtio_scsi_inhdr { #define VIRTIO_BLK_S_OK 0 #define VIRTIO_BLK_S_IOERR 1 #define VIRTIO_BLK_S_UNSUPP 2 + +void blk_request(void *_vq); +void blk_init_fn(struct virtio_vq_dev *vqdev, const char *filename); diff --git a/user/vmm/include/vmm/vmm.h b/user/vmm/include/vmm/vmm.h index ca1d6ec..fdfb24d 100644 --- a/user/vmm/include/vmm/vmm.h +++ b/user/vmm/include/vmm/vmm.h @@ -14,6 +14,7 @@ enum { VIRTIO_MMIO_CONSOLE_DEV, VIRTIO_MMIO_NETWORK_DEV, + VIRTIO_MMIO_BLOCK_DEV, /* This should always be the last entry. */ VIRTIO_MMIO_MAX_NUM_DEV, diff --git a/user/vmm/virtio.c b/user/vmm/virtio.c index f64d1e2..42e30c1 100644 --- a/user/vmm/virtio.c +++ b/user/vmm/virtio.c @@ -37,6 +37,8 @@ const char *virtio_validate_feat(struct virtio_vq_dev *vqdev, uint64_t feat) // either the device can set its own MAC Address (as it does now) // or the driver can set it using a controller thread. break; + case VIRTIO_ID_BLOCK: + break; case 0: return "Invalid device id (0x0)! On the MMIO transport, this value indicates that the device is a system memory map with placeholder devices at static, well known addresses. In any case, this is not something you validate features for."; default: diff --git a/user/vmm/virtio_blk.c b/user/vmm/virtio_blk.c new file mode 100644 index 0000000..0493c69 --- /dev/null +++ b/user/vmm/virtio_blk.c @@ -0,0 +1,145 @@ +#define _LARGEFILE64_SOURCE /* See feature_test_macros(7) */ +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> +#include <vmm/virtio.h> +#include <vmm/virtio_blk.h> +#include <vmm/virtio_mmio.h> + +int debug_virtio_blk = 0; + +#define DPRINTF(fmt, ...) \ + if (debug_virtio_blk) { \ + fprintf(stderr, "virtio_blk: " fmt, ##__VA_ARGS__); \ + } + +/* TODO(ganshun): multiple disks */ +static int diskfd; + +void blk_init_fn(struct virtio_vq_dev *vqdev, const char *filename) +{ + struct virtio_blk_config *cfg = vqdev->cfg; + struct virtio_blk_config *cfg_d = vqdev->cfg_d; + uint64_t len; + + diskfd = open(filename, O_RDWR); + if (diskfd < 0) + VIRTIO_DEV_ERRX(vqdev, "Could not open disk image file %s", filename); + + struct stat stat_result; + if (stat(filename, &stat_result) == -1) + VIRTIO_DEV_ERRX(vqdev, "Could not stat file %s", filename); + len = stat_result.st_size / 512; + + cfg->capacity = len; + cfg_d->capacity = len; +} + +void blk_request(void *_vq) +{ + struct virtio_vq *vq = _vq; + struct virtio_mmio_dev *dev = vq->vqdev->transport_dev; + struct iovec *iov; + uint32_t head; + uint32_t olen, ilen; + struct virtio_blk_outhdr *out; + uint64_t offset; + int64_t ret; + size_t wlen; + uint8_t *status; + struct virtio_blk_config *cfg = vq->vqdev->cfg; + uint64_t total_read = 0; + + DPRINTF("blk_request entered\n"); + + assert(vq != NULL); + + if (vq->qready != 0x1) + VIRTIO_DEV_ERRX(vq->vqdev, + "The service function for queue '%s' was launched " + "before the driver set QueueReady to 0x1.", + vq->name); + + if (!dev->poke_guest) + VIRTIO_DEV_ERRX(vq->vqdev, + "The 'poke_guest' function pointer was not set."); + + iov = malloc(vq->qnum_max * sizeof(struct iovec)); + if (iov == NULL) + VIRTIO_DEV_ERRX(vq->vqdev, + "malloc returned null trying to allocate iov.\n"); + + for (;;) { + head = virtio_next_avail_vq_desc(vq, iov, &olen, &ilen); + /* There are always three iovecs. + * The first is the header. + * The second is the actual data. + * The third contains just the status byte. + */ + + status = iov[2].iov_base; + if (!status) + VIRTIO_DEV_ERRX(vq->vqdev, "no room for status\n"); + + out = iov[0].iov_base; + if (out->type & VIRTIO_BLK_T_FLUSH) + VIRTIO_DEV_ERRX(vq->vqdev, "Flush not supported.\n"); + + offset = out->sector * 512; + DPRINTF("offset is: %llu sector is: %lld\n", offset, out->sector); + if (lseek64(diskfd, offset, SEEK_SET) != offset) + VIRTIO_DEV_ERRX(vq->vqdev, "Bad seek at sector %llu\n", + out->sector); + + if (out->type & VIRTIO_BLK_T_OUT) { + DPRINTF("blk device write\n"); + + if ((offset + iov[1].iov_len) > (cfg->capacity * 512)) + VIRTIO_DEV_ERRX(vq->vqdev, "write past end of file!\n"); + + ret = writev(diskfd, iov + 1, olen - 1); + + if (ret >= 0 && ret == iov[1].iov_len) + *status = VIRTIO_BLK_S_OK; + else + *status = VIRTIO_BLK_S_IOERR; + wlen = sizeof(*status); + } else { + DPRINTF("blk device read\n"); + DPRINTF("olen is: %d ilen is: %d\n", olen, ilen); + DPRINTF("iov[1] is: %d\n", iov[1].iov_len); + ret = readv(diskfd, iov + olen, ilen - 1); + DPRINTF("read %d bytes, total_read is:%d\n", ret, total_read); + DPRINTF("blk device read done\n"); + if (ret >= 0) { + wlen = sizeof(*status) + ret; + *status = VIRTIO_BLK_S_OK; + DPRINTF("status is set\n"); + total_read += ret; + } else { + wlen = sizeof(*status); + *status = VIRTIO_BLK_S_IOERR; + } + + // Hexdump for debugging. + if (debug_virtio_blk && ret >= 0) { + char *pf = ""; + for (int i = 0; i < iov[olen].iov_len; i += 2) { + uint8_t *p = (uint8_t *)iov[olen].iov_base + i; + fprintf(stderr, "%s%02x", pf, *(p + 1)); + fprintf(stderr, "%02x", *p); + fprintf(stderr, " "); + pf = ((i + 2) % 16) ? " " : "\n"; + } + } + } + + virtio_add_used_desc(vq, head, wlen); + virtio_mmio_set_vring_irq(dev); + dev->poke_guest(dev->vec); + DPRINTF("done with everything\n"); + } +} -- 2.8.0.rc3.226.g39d4020 -- You received this message because you are subscribed to the Google Groups "Akaros" group. To unsubscribe from this group and stop receiving emails from it, send an email to [email protected]. To post to this group, send email to [email protected]. For more options, visit https://groups.google.com/d/optout.
