| #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; | 
 | } |