]>
Commit | Line | Data |
---|---|---|
1e17c2c1 AG |
1 | /* |
2 | * Virtio driver bits | |
3 | * | |
4 | * Copyright (c) 2013 Alexander Graf <[email protected]> | |
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 "s390-ccw.h" | |
12 | #include "virtio.h" | |
13 | ||
14 | struct vring block; | |
15 | ||
c8cda874 DD |
16 | static char chsc_page[PAGE_SIZE] __attribute__((__aligned__(PAGE_SIZE))); |
17 | ||
1e17c2c1 AG |
18 | static long kvm_hypercall(unsigned long nr, unsigned long param1, |
19 | unsigned long param2) | |
20 | { | |
21 | register ulong r_nr asm("1") = nr; | |
22 | register ulong r_param1 asm("2") = param1; | |
23 | register ulong r_param2 asm("3") = param2; | |
24 | register long retval asm("2"); | |
25 | ||
26 | asm volatile ("diag 2,4,0x500" | |
27 | : "=d" (retval) | |
28 | : "d" (r_nr), "0" (r_param1), "r"(r_param2) | |
29 | : "memory", "cc"); | |
30 | ||
31 | return retval; | |
32 | } | |
33 | ||
34 | static void virtio_notify(struct subchannel_id schid) | |
35 | { | |
36 | kvm_hypercall(KVM_S390_VIRTIO_CCW_NOTIFY, *(u32*)&schid, 0); | |
37 | } | |
38 | ||
39 | /*********************************************** | |
40 | * Virtio functions * | |
41 | ***********************************************/ | |
42 | ||
776e7f0f | 43 | static int drain_irqs(struct subchannel_id schid) |
1e17c2c1 AG |
44 | { |
45 | struct irb irb = {}; | |
776e7f0f CH |
46 | int r = 0; |
47 | ||
1e17c2c1 | 48 | while (1) { |
776e7f0f | 49 | /* FIXME: make use of TPI, for that enable subchannel and isc */ |
1e17c2c1 | 50 | if (tsch(schid, &irb)) { |
776e7f0f CH |
51 | /* Might want to differentiate error codes later on. */ |
52 | if (irb.scsw.cstat) { | |
53 | r = -EIO; | |
54 | } else if (irb.scsw.dstat != 0xc) { | |
55 | r = -EIO; | |
56 | } | |
57 | return r; | |
1e17c2c1 AG |
58 | } |
59 | } | |
60 | } | |
61 | ||
62 | static int run_ccw(struct subchannel_id schid, int cmd, void *ptr, int len) | |
63 | { | |
64 | struct ccw1 ccw = {}; | |
65 | struct cmd_orb orb = {}; | |
66 | struct schib schib; | |
67 | int r; | |
68 | ||
69 | /* start command processing */ | |
70 | stsch_err(schid, &schib); | |
71 | schib.scsw.ctrl = SCSW_FCTL_START_FUNC; | |
72 | msch(schid, &schib); | |
73 | ||
74 | /* start subchannel command */ | |
75 | orb.fmt = 1; | |
76 | orb.cpa = (u32)(long)&ccw; | |
77 | orb.lpm = 0x80; | |
78 | ||
79 | ccw.cmd_code = cmd; | |
80 | ccw.cda = (long)ptr; | |
81 | ccw.count = len; | |
82 | ||
83 | r = ssch(schid, &orb); | |
84 | /* | |
85 | * XXX Wait until device is done processing the CCW. For now we can | |
86 | * assume that a simple tsch will have finished the CCW processing, | |
87 | * but the architecture allows for asynchronous operation | |
88 | */ | |
0f3f1f30 CH |
89 | if (!r) { |
90 | r = drain_irqs(schid); | |
91 | } | |
1e17c2c1 AG |
92 | return r; |
93 | } | |
94 | ||
95 | static void virtio_set_status(struct subchannel_id schid, | |
96 | unsigned long dev_addr) | |
97 | { | |
98 | unsigned char status = dev_addr; | |
0f3f1f30 CH |
99 | if (run_ccw(schid, CCW_CMD_WRITE_STATUS, &status, sizeof(status))) { |
100 | virtio_panic("Could not write status to host!\n"); | |
101 | } | |
1e17c2c1 AG |
102 | } |
103 | ||
104 | static void virtio_reset(struct subchannel_id schid) | |
105 | { | |
106 | run_ccw(schid, CCW_CMD_VDEV_RESET, NULL, 0); | |
107 | } | |
108 | ||
109 | static void vring_init(struct vring *vr, unsigned int num, void *p, | |
110 | unsigned long align) | |
111 | { | |
112 | debug_print_addr("init p", p); | |
113 | vr->num = num; | |
114 | vr->desc = p; | |
115 | vr->avail = p + num*sizeof(struct vring_desc); | |
116 | vr->used = (void *)(((unsigned long)&vr->avail->ring[num] + align-1) | |
117 | & ~(align - 1)); | |
118 | ||
39c93c67 CB |
119 | /* Zero out all relevant field */ |
120 | vr->avail->flags = 0; | |
121 | vr->avail->idx = 0; | |
122 | ||
1e17c2c1 AG |
123 | /* We're running with interrupts off anyways, so don't bother */ |
124 | vr->used->flags = VRING_USED_F_NO_NOTIFY; | |
39c93c67 | 125 | vr->used->idx = 0; |
1e17c2c1 AG |
126 | |
127 | debug_print_addr("init vr", vr); | |
128 | } | |
129 | ||
130 | static void vring_notify(struct subchannel_id schid) | |
131 | { | |
132 | virtio_notify(schid); | |
133 | } | |
134 | ||
135 | static void vring_send_buf(struct vring *vr, void *p, int len, int flags) | |
136 | { | |
137 | /* For follow-up chains we need to keep the first entry point */ | |
138 | if (!(flags & VRING_HIDDEN_IS_CHAIN)) { | |
139 | vr->avail->ring[vr->avail->idx % vr->num] = vr->next_idx; | |
140 | } | |
141 | ||
142 | vr->desc[vr->next_idx].addr = (ulong)p; | |
143 | vr->desc[vr->next_idx].len = len; | |
144 | vr->desc[vr->next_idx].flags = flags & ~VRING_HIDDEN_IS_CHAIN; | |
dc03640b CB |
145 | vr->desc[vr->next_idx].next = vr->next_idx; |
146 | vr->desc[vr->next_idx].next++; | |
147 | vr->next_idx++; | |
1e17c2c1 AG |
148 | |
149 | /* Chains only have a single ID */ | |
150 | if (!(flags & VRING_DESC_F_NEXT)) { | |
151 | vr->avail->idx++; | |
152 | } | |
153 | ||
154 | vr->used->idx = vr->next_idx; | |
155 | } | |
156 | ||
157 | static u64 get_clock(void) | |
158 | { | |
159 | u64 r; | |
160 | ||
161 | asm volatile("stck %0" : "=Q" (r) : : "cc"); | |
162 | return r; | |
163 | } | |
164 | ||
165 | static ulong get_second(void) | |
166 | { | |
167 | return (get_clock() >> 12) / 1000000; | |
168 | } | |
169 | ||
170 | /* | |
171 | * Wait for the host to reply. | |
172 | * | |
173 | * timeout is in seconds if > 0. | |
174 | * | |
175 | * Returns 0 on success, 1 on timeout. | |
176 | */ | |
177 | static int vring_wait_reply(struct vring *vr, int timeout) | |
178 | { | |
179 | ulong target_second = get_second() + timeout; | |
180 | struct subchannel_id schid = vr->schid; | |
181 | int r = 0; | |
182 | ||
183 | while (vr->used->idx == vr->next_idx) { | |
184 | vring_notify(schid); | |
185 | if (timeout && (get_second() >= target_second)) { | |
186 | r = 1; | |
187 | break; | |
188 | } | |
189 | yield(); | |
190 | } | |
191 | ||
192 | vr->next_idx = 0; | |
193 | vr->desc[0].len = 0; | |
194 | vr->desc[0].flags = 0; | |
195 | ||
196 | return r; | |
197 | } | |
198 | ||
199 | /*********************************************** | |
200 | * Virtio block * | |
201 | ***********************************************/ | |
202 | ||
203 | static int virtio_read_many(ulong sector, void *load_addr, int sec_num) | |
204 | { | |
205 | struct virtio_blk_outhdr out_hdr; | |
206 | u8 status; | |
0f3f1f30 | 207 | int r; |
1e17c2c1 AG |
208 | |
209 | /* Tell the host we want to read */ | |
210 | out_hdr.type = VIRTIO_BLK_T_IN; | |
211 | out_hdr.ioprio = 99; | |
212 | out_hdr.sector = sector; | |
213 | ||
214 | vring_send_buf(&block, &out_hdr, sizeof(out_hdr), VRING_DESC_F_NEXT); | |
215 | ||
216 | /* This is where we want to receive data */ | |
217 | vring_send_buf(&block, load_addr, SECTOR_SIZE * sec_num, | |
218 | VRING_DESC_F_WRITE | VRING_HIDDEN_IS_CHAIN | | |
219 | VRING_DESC_F_NEXT); | |
220 | ||
221 | /* status field */ | |
222 | vring_send_buf(&block, &status, sizeof(u8), VRING_DESC_F_WRITE | | |
223 | VRING_HIDDEN_IS_CHAIN); | |
224 | ||
225 | /* Now we can tell the host to read */ | |
226 | vring_wait_reply(&block, 0); | |
227 | ||
0f3f1f30 CH |
228 | r = drain_irqs(block.schid); |
229 | if (r) { | |
230 | /* Well, whatever status is supposed to contain... */ | |
231 | status = 1; | |
232 | } | |
1e17c2c1 AG |
233 | return status; |
234 | } | |
235 | ||
236 | unsigned long virtio_load_direct(ulong rec_list1, ulong rec_list2, | |
237 | ulong subchan_id, void *load_addr) | |
238 | { | |
239 | u8 status; | |
240 | int sec = rec_list1; | |
241 | int sec_num = (((rec_list2 >> 32)+ 1) & 0xffff); | |
242 | int sec_len = rec_list2 >> 48; | |
243 | ulong addr = (ulong)load_addr; | |
244 | ||
245 | if (sec_len != SECTOR_SIZE) { | |
246 | return -1; | |
247 | } | |
248 | ||
249 | sclp_print("."); | |
250 | status = virtio_read_many(sec, (void*)addr, sec_num); | |
251 | if (status) { | |
252 | virtio_panic("I/O Error"); | |
253 | } | |
254 | addr += sec_num * SECTOR_SIZE; | |
255 | ||
256 | return addr; | |
257 | } | |
258 | ||
259 | int virtio_read(ulong sector, void *load_addr) | |
260 | { | |
261 | return virtio_read_many(sector, load_addr, 1); | |
262 | } | |
263 | ||
264 | void virtio_setup_block(struct subchannel_id schid) | |
265 | { | |
266 | struct vq_info_block info; | |
abbbe3de | 267 | struct vq_config_block config = {}; |
1e17c2c1 AG |
268 | |
269 | virtio_reset(schid); | |
270 | ||
abbbe3de CH |
271 | config.index = 0; |
272 | if (run_ccw(schid, CCW_CMD_READ_VQ_CONF, &config, sizeof(config))) { | |
273 | virtio_panic("Could not get block device configuration\n"); | |
274 | } | |
275 | vring_init(&block, config.num, (void*)(100 * 1024 * 1024), | |
1e17c2c1 AG |
276 | KVM_S390_VIRTIO_RING_ALIGN); |
277 | ||
278 | info.queue = (100ULL * 1024ULL* 1024ULL); | |
279 | info.align = KVM_S390_VIRTIO_RING_ALIGN; | |
280 | info.index = 0; | |
abbbe3de | 281 | info.num = config.num; |
1e17c2c1 AG |
282 | block.schid = schid; |
283 | ||
0f3f1f30 CH |
284 | if (!run_ccw(schid, CCW_CMD_SET_VQ, &info, sizeof(info))) { |
285 | virtio_set_status(schid, VIRTIO_CONFIG_S_DRIVER_OK); | |
286 | } | |
1e17c2c1 AG |
287 | } |
288 | ||
289 | bool virtio_is_blk(struct subchannel_id schid) | |
290 | { | |
291 | int r; | |
292 | struct senseid senseid = {}; | |
293 | ||
294 | /* run sense id command */ | |
295 | r = run_ccw(schid, CCW_CMD_SENSE_ID, &senseid, sizeof(senseid)); | |
296 | if (r) { | |
297 | return false; | |
298 | } | |
299 | if ((senseid.cu_type != 0x3832) || (senseid.cu_model != VIRTIO_ID_BLOCK)) { | |
300 | return false; | |
301 | } | |
302 | ||
303 | return true; | |
304 | } | |
305 | ||
c8cda874 DD |
306 | int enable_mss_facility(void) |
307 | { | |
308 | int ret; | |
309 | struct chsc_area_sda *sda_area = (struct chsc_area_sda *) chsc_page; | |
310 | ||
311 | memset(sda_area, 0, PAGE_SIZE); | |
312 | sda_area->request.length = 0x0400; | |
313 | sda_area->request.code = 0x0031; | |
314 | sda_area->operation_code = 0x2; | |
315 | ||
316 | ret = chsc(sda_area); | |
317 | if ((ret == 0) && (sda_area->response.code == 0x0001)) { | |
318 | return 0; | |
319 | } | |
320 | return -EIO; | |
321 | } |