master
c 255 lines 5.88 KB
Raw
1 /*
2 * Virtio driver bits
3 *
4 * Copyright (c) 2013 Alexander Graf <agraf@suse.de>
5 * Copyright 2025 IBM Corp.
6 *
7 * Author(s): Jared Rossi <jrossi@linux.ibm.com>
8 *
9 * This work is licensed under the terms of the GNU GPL, version 2 or (at
10 * your option) any later version. See the COPYING file in the top-level
11 * directory.
12 */
13
14 #include <string.h>
15 #include "s390-ccw.h"
16 #include "cio.h"
17 #include "virtio.h"
18 #include "virtio-scsi.h"
19 #include "virtio-ccw.h"
20 #include "virtio-pci.h"
21 #include "bswap.h"
22 #include "helper.h"
23 #include "s390-time.h"
24
25 #define VRING_WAIT_REPLY_TIMEOUT 30
26
27 static VRing block[VIRTIO_MAX_VQS];
28 static char ring_area[VIRTIO_RING_SIZE * VIRTIO_MAX_VQS]
29 __attribute__((__aligned__(PAGE_SIZE)));
30
31 static VDev vdev = {
32 .nr_vqs = 1,
33 .vrings = block,
34 .cmd_vr_idx = 0,
35 .ring_area = ring_area,
36 .wait_reply_timeout = VRING_WAIT_REPLY_TIMEOUT,
37 .schid = { .one = 1 },
38 .scsi_block_size = VIRTIO_SCSI_BLOCK_SIZE,
39 .blk_factor = 1,
40 };
41
42 VDev *virtio_get_device(void)
43 {
44 return &vdev;
45 }
46
47 VirtioDevType virtio_get_device_type(void)
48 {
49 return vdev.dev_type;
50 }
51
52 char *virtio_get_ring_area(int ring_num)
53 {
54 return ring_area + ring_num * VIRTIO_RING_SIZE;
55 }
56
57 /***********************************************
58 * Virtio functions *
59 ***********************************************/
60
61 int drain_irqs(void)
62 {
63 switch (vdev.ipl_type) {
64 case S390_IPL_TYPE_QEMU_SCSI:
65 case S390_IPL_TYPE_CCW:
66 return drain_irqs_ccw(vdev.schid);
67 default:
68 return 0;
69 }
70 }
71
72 int virtio_run(VDev *vdev, int vqid, VirtioCmd *cmd)
73 {
74 switch (vdev->ipl_type) {
75 case S390_IPL_TYPE_QEMU_SCSI:
76 case S390_IPL_TYPE_CCW:
77 return virtio_ccw_run(vdev, vqid, cmd);
78 default:
79 return -1;
80 }
81 }
82
83 void vring_init(VRing *vr, VqInfo *info)
84 {
85 void *p = (void *) info->queue;
86
87 debug_print_addr("init p", p);
88 vr->id = info->index;
89 vr->num = info->num;
90 vr->desc = p;
91 vr->avail = p + info->num * sizeof(VRingDesc);
92 vr->used = (void *)(((unsigned long)&vr->avail->ring[info->num]
93 + info->align - 1) & ~(info->align - 1));
94
95 /* Zero out all relevant field */
96 vr->avail->flags = 0;
97 vr->avail->idx = 0;
98
99 /* We're running with interrupts off anyways, so don't bother */
100 vr->used->flags = be_ipl() ? VRING_USED_F_NO_NOTIFY : bswap16(VRING_USED_F_NO_NOTIFY);
101 vr->used->idx = 0;
102 vr->used_idx = 0;
103 vr->next_idx = 0;
104 vr->cookie = 0;
105
106 debug_print_addr("init vr", vr);
107 }
108
109 bool vring_notify(VRing *vr)
110 {
111 switch (vdev.ipl_type) {
112 case S390_IPL_TYPE_QEMU_SCSI:
113 case S390_IPL_TYPE_CCW:
114 vr->cookie = virtio_ccw_notify(vdev.schid, vr->id, vr->cookie);
115 break;
116 case S390_IPL_TYPE_PCI:
117 vr->cookie = virtio_pci_notify(vr);
118 break;
119 default:
120 return 1;
121 }
122
123 return vr->cookie >= 0;
124 }
125
126 /*
127 * Get endienness of the IPL type
128 * Return true for s390x native big-endian
129 */
130 bool be_ipl(void)
131 {
132 switch (virtio_get_device()->ipl_type) {
133 case S390_IPL_TYPE_QEMU_SCSI:
134 case S390_IPL_TYPE_CCW:
135 return true;
136 case S390_IPL_TYPE_PCI:
137 return false;
138 default:
139 return true;
140 }
141 }
142
143 /*
144 * Format the virtio ring descriptor endianness
145 * Return the available index increment in the appropriate endianness
146 */
147 static void vr_bswap_descriptor(VRingDesc *desc)
148 {
149 desc->addr = bswap64(desc->addr);
150 desc->len = bswap32(desc->len);
151 desc->flags = bswap16(desc->flags);
152 desc->next = bswap16(desc->next);
153 }
154
155 void vring_send_buf(VRing *vr, void *p, int len, int flags)
156 {
157 uint16_t avail_idx;
158
159 avail_idx = be_ipl() ? vr->avail->idx : bswap16(vr->avail->idx);
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[avail_idx % vr->num] = be_ipl() ? vr->next_idx :
164 bswap16(vr->next_idx);
165 }
166
167 vr->desc[vr->next_idx].addr = (unsigned long)p;
168 vr->desc[vr->next_idx].len = len;
169 vr->desc[vr->next_idx].flags = flags & ~VRING_HIDDEN_IS_CHAIN;
170 vr->desc[vr->next_idx].next = vr->next_idx;
171 vr->desc[vr->next_idx].next++;
172
173 if (!be_ipl()) {
174 vr_bswap_descriptor(&vr->desc[vr->next_idx]);
175 }
176
177 vr->next_idx++;
178
179 /* Chains only have a single ID */
180 if (!(flags & VRING_DESC_F_NEXT)) {
181 avail_idx++;
182 vr->avail->idx = be_ipl() ? avail_idx : bswap16(avail_idx);
183 }
184 }
185
186 int vr_poll(VRing *vr)
187 {
188 uint16_t used_idx;
189
190 used_idx = be_ipl() ? vr->used->idx : bswap16(vr->used->idx);
191 if (used_idx == vr->used_idx) {
192 vring_notify(vr);
193 yield();
194 return 0;
195 }
196
197 vr->used_idx = used_idx;
198 vr->next_idx = 0;
199 vr->desc[0].len = 0;
200 vr->desc[0].flags = 0;
201 return 1; /* vr has been updated */
202 }
203
204 /*
205 * Wait for the host to reply.
206 *
207 * timeout is in seconds if > 0.
208 *
209 * Returns 0 on success, 1 on timeout.
210 */
211 int vring_wait_reply(void)
212 {
213 unsigned long target_second = get_time_seconds() + vdev.wait_reply_timeout;
214
215 /* Wait for any queue to be updated by the host */
216 do {
217 int i, r = 0;
218
219 for (i = 0; i < vdev.nr_vqs; i++) {
220 r += vr_poll(&vdev.vrings[i]);
221 }
222 yield();
223 if (r) {
224 return 0;
225 }
226 } while (!vdev.wait_reply_timeout || (get_time_seconds() < target_second));
227
228 return 1;
229 }
230
231 int virtio_reset(VDev *vdev)
232 {
233 switch (vdev->ipl_type) {
234 case S390_IPL_TYPE_QEMU_SCSI:
235 case S390_IPL_TYPE_CCW:
236 return virtio_ccw_reset(vdev);
237 case S390_IPL_TYPE_PCI:
238 return virtio_pci_reset(vdev);
239 default:
240 return -1;
241 }
242 }
243
244 bool virtio_is_supported(VDev *vdev)
245 {
246 switch (vdev->ipl_type) {
247 case S390_IPL_TYPE_QEMU_SCSI:
248 case S390_IPL_TYPE_CCW:
249 return virtio_ccw_is_supported(vdev);
250 case S390_IPL_TYPE_PCI:
251 return virtio_pci_is_supported(vdev);
252 default:
253 return false;
254 }
255 }