parlib: have 2LS libraries #include parlib/stdio.h
[akaros.git] / user / vmm / virtio_blk.c
1 #define _LARGEFILE64_SOURCE /* See feature_test_macros(7) */
2 #include <fcntl.h>
3 #include <parlib/stdio.h>
4 #include <stdlib.h>
5 #include <sys/stat.h>
6 #include <sys/types.h>
7 #include <unistd.h>
8 #include <vmm/virtio.h>
9 #include <vmm/virtio_blk.h>
10 #include <vmm/virtio_mmio.h>
11
12 int debug_virtio_blk;
13
14 #define DPRINTF(fmt, ...)                                                      \
15 do {                                                                           \
16         if (debug_virtio_blk) {                                                \
17                 fprintf(stderr, "virtio_blk: " fmt, ##__VA_ARGS__);            \
18         }                                                                      \
19 } while (0)
20
21 /* TODO(ganshun): multiple disks */
22 static int diskfd;
23
24 void blk_init_fn(struct virtio_vq_dev *vqdev, const char *filename)
25 {
26         struct virtio_blk_config *cfg = vqdev->cfg;
27         struct virtio_blk_config *cfg_d = vqdev->cfg_d;
28         uint64_t len;
29         struct stat stat_result;
30
31         diskfd = open(filename, O_RDWR);
32         if (diskfd < 0)
33                 VIRTIO_DEV_ERRX(vqdev, "Could not open disk image file %s",
34                                 filename);
35
36         if (stat(filename, &stat_result) == -1)
37                 VIRTIO_DEV_ERRX(vqdev, "Could not stat file %s", filename);
38         len = stat_result.st_size / 512;
39
40         cfg->capacity = len;
41         cfg_d->capacity = len;
42 }
43
44 void *blk_request(void *_vq)
45 {
46         struct virtio_vq *vq = _vq;
47
48         assert(vq != NULL);
49
50         struct virtio_mmio_dev *dev = vq->vqdev->transport_dev;
51         struct iovec *iov;
52         uint32_t head;
53         uint32_t olen, ilen;
54         struct virtio_blk_outhdr *out;
55         uint64_t offset;
56         int64_t ret;
57         size_t wlen;
58         uint8_t *status;
59         struct virtio_blk_config *cfg = vq->vqdev->cfg;
60
61         if (vq->qready != 0x1)
62                 VIRTIO_DEV_ERRX(vq->vqdev,
63                                 "The service function for queue '%s' was launched before the driver set QueueReady to 0x1.",
64                                  vq->name);
65
66         if (!dev->poke_guest)
67                 VIRTIO_DEV_ERRX(vq->vqdev,
68                         "The 'poke_guest' function pointer was not set.");
69
70         iov = malloc(vq->qnum_max * sizeof(struct iovec));
71         if (iov == NULL)
72                 VIRTIO_DEV_ERRX(vq->vqdev,
73                         "malloc returned null trying to allocate iov.\n");
74
75         for (;;) {
76                 head = virtio_next_avail_vq_desc(vq, iov, &olen, &ilen);
77                 /* There are always three iovecs.
78                  * The first is the header.
79                  * The second is the actual data.
80                  * The third contains just the status byte.
81                  */
82                 assert(olen + ilen == 3);
83
84                 status = iov[2].iov_base;
85                 if (!status)
86                         VIRTIO_DEV_ERRX(vq->vqdev, "no room for status\n");
87
88                 out = iov[0].iov_base;
89                 if (out->type & VIRTIO_BLK_T_FLUSH)
90                         VIRTIO_DEV_ERRX(vq->vqdev, "Flush not supported.\n");
91
92                 offset = out->sector * 512;
93                 if (lseek64(diskfd, offset, SEEK_SET) != offset)
94                         VIRTIO_DEV_ERRX(vq->vqdev, "Bad seek at sector %llu\n",
95                                         out->sector);
96
97                 if (out->type & VIRTIO_BLK_T_OUT) {
98
99                         if ((offset + iov[1].iov_len) > (cfg->capacity * 512))
100                                 VIRTIO_DEV_ERRX(vq->vqdev,
101                                                 "write past end of file!\n");
102
103                         ret = writev(diskfd, &iov[1], 1);
104
105                         if (ret >= 0 && ret == iov[1].iov_len)
106                                 *status = VIRTIO_BLK_S_OK;
107                         else
108                                 *status = VIRTIO_BLK_S_IOERR;
109                         wlen = sizeof(*status);
110                 } else {
111                         ret = readv(diskfd, &iov[1], 1);
112                         if (ret >= 0) {
113                                 wlen = sizeof(*status) + ret;
114                                 *status = VIRTIO_BLK_S_OK;
115                         } else {
116                                 wlen = sizeof(*status);
117                                 *status = VIRTIO_BLK_S_IOERR;
118                         }
119
120                         // Hexdump for debugging.
121                         if (debug_virtio_blk && ret >= 0) {
122                                 char *pf = "";
123
124                                 for (int i = 0; i < iov[olen].iov_len; i += 2) {
125                                         uint8_t *p =
126                                           (uint8_t*)iov[olen].iov_base + i;
127
128                                         fprintf(stderr, "%s%02x", pf, *(p + 1));
129                                         fprintf(stderr, "%02x", *p);
130                                         fprintf(stderr, " ");
131                                         pf = ((i + 2) % 16) ? " " : "\n";
132                                 }
133                         }
134                 }
135
136                 virtio_add_used_desc(vq, head, wlen);
137                 virtio_mmio_set_vring_irq(dev);
138                 dev->poke_guest(dev->vec, dev->dest);
139         }
140         return 0;
141 }