blob: b031886f961c1fb1dd5a9909b4534200d7a9d9c4 [file] [log] [blame]
#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;
#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;
}