xref: /openbmc/qemu/pc-bios/s390-ccw/virtio.c (revision 0221d73c)
1 /*
2  * Virtio driver bits
3  *
4  * Copyright (c) 2013 Alexander Graf <agraf@suse.de>
5  *
6  * This work is licensed under the terms of the GNU GPL, version 2 or (at
7  * your option) any later version. See the COPYING file in the top-level
8  * directory.
9  */
10 
11 #include "libc.h"
12 #include "s390-ccw.h"
13 #include "cio.h"
14 #include "virtio.h"
15 #include "virtio-scsi.h"
16 #include "bswap.h"
17 #include "helper.h"
18 
19 #define VRING_WAIT_REPLY_TIMEOUT 30
20 
21 static VRing block[VIRTIO_MAX_VQS];
22 static char ring_area[VIRTIO_RING_SIZE * VIRTIO_MAX_VQS]
23                      __attribute__((__aligned__(PAGE_SIZE)));
24 
25 static VDev vdev = {
26     .nr_vqs = 1,
27     .vrings = block,
28     .cmd_vr_idx = 0,
29     .ring_area = ring_area,
30     .wait_reply_timeout = VRING_WAIT_REPLY_TIMEOUT,
31     .schid = { .one = 1 },
32     .scsi_block_size = VIRTIO_SCSI_BLOCK_SIZE,
33     .blk_factor = 1,
34 };
35 
36 VDev *virtio_get_device(void)
37 {
38     return &vdev;
39 }
40 
41 VirtioDevType virtio_get_device_type(void)
42 {
43     return vdev.senseid.cu_model;
44 }
45 
46 /* virtio spec v1.0 para 4.3.3.2 */
47 static long kvm_hypercall(unsigned long nr, unsigned long param1,
48                           unsigned long param2, unsigned long param3)
49 {
50     register ulong r_nr asm("1") = nr;
51     register ulong r_param1 asm("2") = param1;
52     register ulong r_param2 asm("3") = param2;
53     register ulong r_param3 asm("4") = param3;
54     register long retval asm("2");
55 
56     asm volatile ("diag 2,4,0x500"
57                   : "=d" (retval)
58                   : "d" (r_nr), "0" (r_param1), "r"(r_param2), "d"(r_param3)
59                   : "memory", "cc");
60 
61     return retval;
62 }
63 
64 static long virtio_notify(SubChannelId schid, int vq_idx, long cookie)
65 {
66     return kvm_hypercall(KVM_S390_VIRTIO_CCW_NOTIFY, *(u32 *)&schid,
67                          vq_idx, cookie);
68 }
69 
70 /***********************************************
71  *             Virtio functions                *
72  ***********************************************/
73 
74 int drain_irqs(SubChannelId schid)
75 {
76     Irb irb = {};
77     int r = 0;
78 
79     while (1) {
80         /* FIXME: make use of TPI, for that enable subchannel and isc */
81         if (tsch(schid, &irb)) {
82             /* Might want to differentiate error codes later on. */
83             if (irb.scsw.cstat) {
84                 r = -EIO;
85             } else if (irb.scsw.dstat != 0xc) {
86                 r = -EIO;
87             }
88             return r;
89         }
90     }
91 }
92 
93 static int run_ccw(VDev *vdev, int cmd, void *ptr, int len, bool sli)
94 {
95     Ccw1 ccw = {};
96 
97     ccw.cmd_code = cmd;
98     ccw.cda = (long)ptr;
99     ccw.count = len;
100 
101     if (sli) {
102         ccw.flags |= CCW_FLAG_SLI;
103     }
104 
105     return do_cio(vdev->schid, vdev->senseid.cu_type, ptr2u32(&ccw), CCW_FMT1);
106 }
107 
108 static void vring_init(VRing *vr, VqInfo *info)
109 {
110     void *p = (void *) info->queue;
111 
112     debug_print_addr("init p", p);
113     vr->id = info->index;
114     vr->num = info->num;
115     vr->desc = p;
116     vr->avail = p + info->num * sizeof(VRingDesc);
117     vr->used = (void *)(((unsigned long)&vr->avail->ring[info->num]
118                + info->align - 1) & ~(info->align - 1));
119 
120     /* Zero out all relevant field */
121     vr->avail->flags = 0;
122     vr->avail->idx = 0;
123 
124     /* We're running with interrupts off anyways, so don't bother */
125     vr->used->flags = VRING_USED_F_NO_NOTIFY;
126     vr->used->idx = 0;
127     vr->used_idx = 0;
128     vr->next_idx = 0;
129     vr->cookie = 0;
130 
131     debug_print_addr("init vr", vr);
132 }
133 
134 bool vring_notify(VRing *vr)
135 {
136     vr->cookie = virtio_notify(vr->schid, vr->id, vr->cookie);
137     return vr->cookie >= 0;
138 }
139 
140 void vring_send_buf(VRing *vr, void *p, int len, int flags)
141 {
142     /* For follow-up chains we need to keep the first entry point */
143     if (!(flags & VRING_HIDDEN_IS_CHAIN)) {
144         vr->avail->ring[vr->avail->idx % vr->num] = vr->next_idx;
145     }
146 
147     vr->desc[vr->next_idx].addr = (ulong)p;
148     vr->desc[vr->next_idx].len = len;
149     vr->desc[vr->next_idx].flags = flags & ~VRING_HIDDEN_IS_CHAIN;
150     vr->desc[vr->next_idx].next = vr->next_idx;
151     vr->desc[vr->next_idx].next++;
152     vr->next_idx++;
153 
154     /* Chains only have a single ID */
155     if (!(flags & VRING_DESC_F_NEXT)) {
156         vr->avail->idx++;
157     }
158 }
159 
160 u64 get_clock(void)
161 {
162     u64 r;
163 
164     asm volatile("stck %0" : "=Q" (r) : : "cc");
165     return r;
166 }
167 
168 ulong get_second(void)
169 {
170     return (get_clock() >> 12) / 1000000;
171 }
172 
173 int vr_poll(VRing *vr)
174 {
175     if (vr->used->idx == vr->used_idx) {
176         vring_notify(vr);
177         yield();
178         return 0;
179     }
180 
181     vr->used_idx = vr->used->idx;
182     vr->next_idx = 0;
183     vr->desc[0].len = 0;
184     vr->desc[0].flags = 0;
185     return 1; /* vr has been updated */
186 }
187 
188 /*
189  * Wait for the host to reply.
190  *
191  * timeout is in seconds if > 0.
192  *
193  * Returns 0 on success, 1 on timeout.
194  */
195 int vring_wait_reply(void)
196 {
197     ulong target_second = get_second() + vdev.wait_reply_timeout;
198 
199     /* Wait for any queue to be updated by the host */
200     do {
201         int i, r = 0;
202 
203         for (i = 0; i < vdev.nr_vqs; i++) {
204             r += vr_poll(&vdev.vrings[i]);
205         }
206         yield();
207         if (r) {
208             return 0;
209         }
210     } while (!vdev.wait_reply_timeout || (get_second() < target_second));
211 
212     return 1;
213 }
214 
215 int virtio_run(VDev *vdev, int vqid, VirtioCmd *cmd)
216 {
217     VRing *vr = &vdev->vrings[vqid];
218     int i = 0;
219 
220     do {
221         vring_send_buf(vr, cmd[i].data, cmd[i].size,
222                        cmd[i].flags | (i ? VRING_HIDDEN_IS_CHAIN : 0));
223     } while (cmd[i++].flags & VRING_DESC_F_NEXT);
224 
225     vring_wait_reply();
226     if (drain_irqs(vr->schid)) {
227         return -1;
228     }
229     return 0;
230 }
231 
232 void virtio_setup_ccw(VDev *vdev)
233 {
234     int i, rc, cfg_size = 0;
235     unsigned char status = VIRTIO_CONFIG_S_DRIVER_OK;
236     struct VirtioFeatureDesc {
237         uint32_t features;
238         uint8_t index;
239     } __attribute__((packed)) feats;
240 
241     IPL_assert(virtio_is_supported(vdev->schid), "PE");
242     /* device ID has been established now */
243 
244     vdev->config.blk.blk_size = 0; /* mark "illegal" - setup started... */
245     vdev->guessed_disk_nature = VIRTIO_GDN_NONE;
246 
247     run_ccw(vdev, CCW_CMD_VDEV_RESET, NULL, 0, false);
248 
249     switch (vdev->senseid.cu_model) {
250     case VIRTIO_ID_NET:
251         vdev->nr_vqs = 2;
252         vdev->cmd_vr_idx = 0;
253         cfg_size = sizeof(vdev->config.net);
254         break;
255     case VIRTIO_ID_BLOCK:
256         vdev->nr_vqs = 1;
257         vdev->cmd_vr_idx = 0;
258         cfg_size = sizeof(vdev->config.blk);
259         break;
260     case VIRTIO_ID_SCSI:
261         vdev->nr_vqs = 3;
262         vdev->cmd_vr_idx = VR_REQUEST;
263         cfg_size = sizeof(vdev->config.scsi);
264         break;
265     default:
266         panic("Unsupported virtio device\n");
267     }
268     IPL_assert(
269         run_ccw(vdev, CCW_CMD_READ_CONF, &vdev->config, cfg_size, false) == 0,
270        "Could not get block device configuration");
271 
272     /* Feature negotiation */
273     for (i = 0; i < ARRAY_SIZE(vdev->guest_features); i++) {
274         feats.features = 0;
275         feats.index = i;
276         rc = run_ccw(vdev, CCW_CMD_READ_FEAT, &feats, sizeof(feats), false);
277         IPL_assert(rc == 0, "Could not get features bits");
278         vdev->guest_features[i] &= bswap32(feats.features);
279         feats.features = bswap32(vdev->guest_features[i]);
280         rc = run_ccw(vdev, CCW_CMD_WRITE_FEAT, &feats, sizeof(feats), false);
281         IPL_assert(rc == 0, "Could not set features bits");
282     }
283 
284     for (i = 0; i < vdev->nr_vqs; i++) {
285         VqInfo info = {
286             .queue = (unsigned long long) ring_area + (i * VIRTIO_RING_SIZE),
287             .align = KVM_S390_VIRTIO_RING_ALIGN,
288             .index = i,
289             .num = 0,
290         };
291         VqConfig config = {
292             .index = i,
293             .num = 0,
294         };
295 
296         IPL_assert(
297             run_ccw(vdev, CCW_CMD_READ_VQ_CONF, &config, sizeof(config), false) == 0,
298             "Could not get block device VQ configuration");
299         info.num = config.num;
300         vring_init(&vdev->vrings[i], &info);
301         vdev->vrings[i].schid = vdev->schid;
302         IPL_assert(
303             run_ccw(vdev, CCW_CMD_SET_VQ, &info, sizeof(info), false) == 0,
304             "Cannot set VQ info");
305     }
306     IPL_assert(
307         run_ccw(vdev, CCW_CMD_WRITE_STATUS, &status, sizeof(status), false) == 0,
308         "Could not write status to host");
309 }
310 
311 bool virtio_is_supported(SubChannelId schid)
312 {
313     vdev.schid = schid;
314     memset(&vdev.senseid, 0, sizeof(vdev.senseid));
315 
316     /*
317      * Run sense id command.
318      * The size of the senseid data differs between devices (notably,
319      * between virtio devices and dasds), so specify the largest possible
320      * size and suppress the incorrect length indication for smaller sizes.
321      */
322     if (run_ccw(&vdev, CCW_CMD_SENSE_ID, &vdev.senseid, sizeof(vdev.senseid),
323                 true)) {
324         return false;
325     }
326     if (vdev.senseid.cu_type == 0x3832) {
327         switch (vdev.senseid.cu_model) {
328         case VIRTIO_ID_BLOCK:
329         case VIRTIO_ID_SCSI:
330         case VIRTIO_ID_NET:
331             return true;
332         }
333     }
334     return false;
335 }
336