|  | #define _LARGEFILE64_SOURCE /* See feature_test_macros(7) */ | 
|  | #include <fcntl.h> | 
|  | #include <parlib/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; | 
|  |  | 
|  | #define DPRINTF(fmt, ...)                                                      \ | 
|  | do {                                                                           \ | 
|  | if (debug_virtio_blk) {                                                \ | 
|  | fprintf(stderr, "virtio_blk: " fmt, ##__VA_ARGS__);            \ | 
|  | }                                                                      \ | 
|  | } while (0) | 
|  |  | 
|  | /* 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; | 
|  | struct stat stat_result; | 
|  |  | 
|  | diskfd = open(filename, O_RDWR); | 
|  | if (diskfd < 0) | 
|  | VIRTIO_DEV_ERRX(vqdev, "Could not open disk image file %s", | 
|  | filename); | 
|  |  | 
|  | 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; | 
|  |  | 
|  | assert(vq != NULL); | 
|  |  | 
|  | 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; | 
|  |  | 
|  | 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. | 
|  | */ | 
|  | assert(olen + ilen == 3); | 
|  |  | 
|  | 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; | 
|  | 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) { | 
|  |  | 
|  | 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], 1); | 
|  |  | 
|  | if (ret >= 0 && ret == iov[1].iov_len) | 
|  | *status = VIRTIO_BLK_S_OK; | 
|  | else | 
|  | *status = VIRTIO_BLK_S_IOERR; | 
|  | wlen = sizeof(*status); | 
|  | } else { | 
|  | ret = readv(diskfd, &iov[1], 1); | 
|  | if (ret >= 0) { | 
|  | wlen = sizeof(*status) + ret; | 
|  | *status = VIRTIO_BLK_S_OK; | 
|  | } 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, dev->dest); | 
|  | } | 
|  | return 0; | 
|  | } |