xref: /openbmc/qemu/pc-bios/s390-ccw/virtio.c (revision 1cfe48c1)
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 "virtio.h"
14 #include "virtio-scsi.h"
15 #include "bswap.h"
16 
17 #define VRING_WAIT_REPLY_TIMEOUT 3
18 
19 static VRing block[VIRTIO_MAX_VQS];
20 static char ring_area[VIRTIO_RING_SIZE * VIRTIO_MAX_VQS]
21                      __attribute__((__aligned__(PAGE_SIZE)));
22 
23 static char chsc_page[PAGE_SIZE] __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)
94 {
95     Ccw1 ccw = {};
96     CmdOrb orb = {};
97     Schib schib;
98     int r;
99 
100     /* start command processing */
101     stsch_err(vdev->schid, &schib);
102     /* enable the subchannel for IPL device */
103     schib.pmcw.ena = 1;
104     msch(vdev->schid, &schib);
105 
106     /* start subchannel command */
107     orb.fmt = 1;
108     orb.cpa = (u32)(long)&ccw;
109     orb.lpm = 0x80;
110 
111     ccw.cmd_code = cmd;
112     ccw.cda = (long)ptr;
113     ccw.count = len;
114 
115     r = ssch(vdev->schid, &orb);
116     /*
117      * XXX Wait until device is done processing the CCW. For now we can
118      *     assume that a simple tsch will have finished the CCW processing,
119      *     but the architecture allows for asynchronous operation
120      */
121     if (!r) {
122         r = drain_irqs(vdev->schid);
123     }
124     return r;
125 }
126 
127 static void vring_init(VRing *vr, VqInfo *info)
128 {
129     void *p = (void *) info->queue;
130 
131     debug_print_addr("init p", p);
132     vr->id = info->index;
133     vr->num = info->num;
134     vr->desc = p;
135     vr->avail = p + info->num * sizeof(VRingDesc);
136     vr->used = (void *)(((unsigned long)&vr->avail->ring[info->num]
137                + info->align - 1) & ~(info->align - 1));
138 
139     /* Zero out all relevant field */
140     vr->avail->flags = 0;
141     vr->avail->idx = 0;
142 
143     /* We're running with interrupts off anyways, so don't bother */
144     vr->used->flags = VRING_USED_F_NO_NOTIFY;
145     vr->used->idx = 0;
146     vr->used_idx = 0;
147     vr->next_idx = 0;
148     vr->cookie = 0;
149 
150     debug_print_addr("init vr", vr);
151 }
152 
153 bool vring_notify(VRing *vr)
154 {
155     vr->cookie = virtio_notify(vr->schid, vr->id, vr->cookie);
156     return vr->cookie >= 0;
157 }
158 
159 void vring_send_buf(VRing *vr, void *p, int len, int flags)
160 {
161     /* For follow-up chains we need to keep the first entry point */
162     if (!(flags & VRING_HIDDEN_IS_CHAIN)) {
163         vr->avail->ring[vr->avail->idx % vr->num] = vr->next_idx;
164     }
165 
166     vr->desc[vr->next_idx].addr = (ulong)p;
167     vr->desc[vr->next_idx].len = len;
168     vr->desc[vr->next_idx].flags = flags & ~VRING_HIDDEN_IS_CHAIN;
169     vr->desc[vr->next_idx].next = vr->next_idx;
170     vr->desc[vr->next_idx].next++;
171     vr->next_idx++;
172 
173     /* Chains only have a single ID */
174     if (!(flags & VRING_DESC_F_NEXT)) {
175         vr->avail->idx++;
176     }
177 }
178 
179 static u64 get_clock(void)
180 {
181     u64 r;
182 
183     asm volatile("stck %0" : "=Q" (r) : : "cc");
184     return r;
185 }
186 
187 ulong get_second(void)
188 {
189     return (get_clock() >> 12) / 1000000;
190 }
191 
192 int vr_poll(VRing *vr)
193 {
194     if (vr->used->idx == vr->used_idx) {
195         vring_notify(vr);
196         yield();
197         return 0;
198     }
199 
200     vr->used_idx = vr->used->idx;
201     vr->next_idx = 0;
202     vr->desc[0].len = 0;
203     vr->desc[0].flags = 0;
204     return 1; /* vr has been updated */
205 }
206 
207 /*
208  * Wait for the host to reply.
209  *
210  * timeout is in seconds if > 0.
211  *
212  * Returns 0 on success, 1 on timeout.
213  */
214 int vring_wait_reply(void)
215 {
216     ulong target_second = get_second() + vdev.wait_reply_timeout;
217 
218     /* Wait for any queue to be updated by the host */
219     do {
220         int i, r = 0;
221 
222         for (i = 0; i < vdev.nr_vqs; i++) {
223             r += vr_poll(&vdev.vrings[i]);
224         }
225         yield();
226         if (r) {
227             return 0;
228         }
229     } while (!vdev.wait_reply_timeout || (get_second() < target_second));
230 
231     return 1;
232 }
233 
234 int virtio_run(VDev *vdev, int vqid, VirtioCmd *cmd)
235 {
236     VRing *vr = &vdev->vrings[vqid];
237     int i = 0;
238 
239     do {
240         vring_send_buf(vr, cmd[i].data, cmd[i].size,
241                        cmd[i].flags | (i ? VRING_HIDDEN_IS_CHAIN : 0));
242     } while (cmd[i++].flags & VRING_DESC_F_NEXT);
243 
244     vring_wait_reply();
245     if (drain_irqs(vr->schid)) {
246         return -1;
247     }
248     return 0;
249 }
250 
251 void virtio_setup_ccw(VDev *vdev)
252 {
253     int i, rc, cfg_size = 0;
254     unsigned char status = VIRTIO_CONFIG_S_DRIVER_OK;
255     struct VirtioFeatureDesc {
256         uint32_t features;
257         uint8_t index;
258     } __attribute__((packed)) feats;
259 
260     IPL_assert(virtio_is_supported(vdev->schid), "PE");
261     /* device ID has been established now */
262 
263     vdev->config.blk.blk_size = 0; /* mark "illegal" - setup started... */
264     vdev->guessed_disk_nature = VIRTIO_GDN_NONE;
265 
266     run_ccw(vdev, CCW_CMD_VDEV_RESET, NULL, 0);
267 
268     switch (vdev->senseid.cu_model) {
269     case VIRTIO_ID_NET:
270         vdev->nr_vqs = 2;
271         vdev->cmd_vr_idx = 0;
272         cfg_size = sizeof(vdev->config.net);
273         break;
274     case VIRTIO_ID_BLOCK:
275         vdev->nr_vqs = 1;
276         vdev->cmd_vr_idx = 0;
277         cfg_size = sizeof(vdev->config.blk);
278         break;
279     case VIRTIO_ID_SCSI:
280         vdev->nr_vqs = 3;
281         vdev->cmd_vr_idx = VR_REQUEST;
282         cfg_size = sizeof(vdev->config.scsi);
283         break;
284     default:
285         panic("Unsupported virtio device\n");
286     }
287     IPL_assert(run_ccw(vdev, CCW_CMD_READ_CONF, &vdev->config, cfg_size) == 0,
288                "Could not get block device configuration");
289 
290     /* Feature negotiation */
291     for (i = 0; i < ARRAY_SIZE(vdev->guest_features); i++) {
292         feats.features = 0;
293         feats.index = i;
294         rc = run_ccw(vdev, CCW_CMD_READ_FEAT, &feats, sizeof(feats));
295         IPL_assert(rc == 0, "Could not get features bits");
296         vdev->guest_features[i] &= bswap32(feats.features);
297         feats.features = bswap32(vdev->guest_features[i]);
298         rc = run_ccw(vdev, CCW_CMD_WRITE_FEAT, &feats, sizeof(feats));
299         IPL_assert(rc == 0, "Could not set features bits");
300     }
301 
302     for (i = 0; i < vdev->nr_vqs; i++) {
303         VqInfo info = {
304             .queue = (unsigned long long) ring_area + (i * VIRTIO_RING_SIZE),
305             .align = KVM_S390_VIRTIO_RING_ALIGN,
306             .index = i,
307             .num = 0,
308         };
309         VqConfig config = {
310             .index = i,
311             .num = 0,
312         };
313 
314         IPL_assert(
315             run_ccw(vdev, CCW_CMD_READ_VQ_CONF, &config, sizeof(config)) == 0,
316             "Could not get block device VQ configuration");
317         info.num = config.num;
318         vring_init(&vdev->vrings[i], &info);
319         vdev->vrings[i].schid = vdev->schid;
320         IPL_assert(run_ccw(vdev, CCW_CMD_SET_VQ, &info, sizeof(info)) == 0,
321                    "Cannot set VQ info");
322     }
323     IPL_assert(
324         run_ccw(vdev, CCW_CMD_WRITE_STATUS, &status, sizeof(status)) == 0,
325         "Could not write status to host");
326 }
327 
328 bool virtio_is_supported(SubChannelId schid)
329 {
330     vdev.schid = schid;
331     memset(&vdev.senseid, 0, sizeof(vdev.senseid));
332     /* run sense id command */
333     if (run_ccw(&vdev, CCW_CMD_SENSE_ID, &vdev.senseid, sizeof(vdev.senseid))) {
334         return false;
335     }
336     if (vdev.senseid.cu_type == 0x3832) {
337         switch (vdev.senseid.cu_model) {
338         case VIRTIO_ID_BLOCK:
339         case VIRTIO_ID_SCSI:
340         case VIRTIO_ID_NET:
341             return true;
342         }
343     }
344     return false;
345 }
346 
347 int enable_mss_facility(void)
348 {
349     int ret;
350     ChscAreaSda *sda_area = (ChscAreaSda *) chsc_page;
351 
352     memset(sda_area, 0, PAGE_SIZE);
353     sda_area->request.length = 0x0400;
354     sda_area->request.code = 0x0031;
355     sda_area->operation_code = 0x2;
356 
357     ret = chsc(sda_area);
358     if ((ret == 0) && (sda_area->response.code == 0x0001)) {
359         return 0;
360     }
361     return -EIO;
362 }
363