diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/s390/Makefile | 2 | ||||
-rw-r--r-- | drivers/s390/kvm/Makefile | 9 | ||||
-rw-r--r-- | drivers/s390/kvm/kvm_virtio.c | 338 |
3 files changed, 348 insertions, 1 deletions
diff --git a/drivers/s390/Makefile b/drivers/s390/Makefile index 5a888704a8d0..4f4e7cf105d4 100644 --- a/drivers/s390/Makefile +++ b/drivers/s390/Makefile | |||
@@ -5,7 +5,7 @@ | |||
5 | CFLAGS_sysinfo.o += -Iinclude/math-emu -Iarch/s390/math-emu -w | 5 | CFLAGS_sysinfo.o += -Iinclude/math-emu -Iarch/s390/math-emu -w |
6 | 6 | ||
7 | obj-y += s390mach.o sysinfo.o s390_rdev.o | 7 | obj-y += s390mach.o sysinfo.o s390_rdev.o |
8 | obj-y += cio/ block/ char/ crypto/ net/ scsi/ | 8 | obj-y += cio/ block/ char/ crypto/ net/ scsi/ kvm/ |
9 | 9 | ||
10 | drivers-y += drivers/s390/built-in.o | 10 | drivers-y += drivers/s390/built-in.o |
11 | 11 | ||
diff --git a/drivers/s390/kvm/Makefile b/drivers/s390/kvm/Makefile new file mode 100644 index 000000000000..4a5ec39f9ca6 --- /dev/null +++ b/drivers/s390/kvm/Makefile | |||
@@ -0,0 +1,9 @@ | |||
1 | # Makefile for kvm guest drivers on s390 | ||
2 | # | ||
3 | # Copyright IBM Corp. 2008 | ||
4 | # | ||
5 | # This program is free software; you can redistribute it and/or modify | ||
6 | # it under the terms of the GNU General Public License (version 2 only) | ||
7 | # as published by the Free Software Foundation. | ||
8 | |||
9 | obj-$(CONFIG_VIRTIO) += kvm_virtio.o | ||
diff --git a/drivers/s390/kvm/kvm_virtio.c b/drivers/s390/kvm/kvm_virtio.c new file mode 100644 index 000000000000..bbef3764fbf8 --- /dev/null +++ b/drivers/s390/kvm/kvm_virtio.c | |||
@@ -0,0 +1,338 @@ | |||
1 | /* | ||
2 | * kvm_virtio.c - virtio for kvm on s390 | ||
3 | * | ||
4 | * Copyright IBM Corp. 2008 | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License (version 2 only) | ||
8 | * as published by the Free Software Foundation. | ||
9 | * | ||
10 | * Author(s): Christian Borntraeger <borntraeger@de.ibm.com> | ||
11 | */ | ||
12 | |||
13 | #include <linux/init.h> | ||
14 | #include <linux/bootmem.h> | ||
15 | #include <linux/err.h> | ||
16 | #include <linux/virtio.h> | ||
17 | #include <linux/virtio_config.h> | ||
18 | #include <linux/interrupt.h> | ||
19 | #include <linux/virtio_ring.h> | ||
20 | #include <asm/io.h> | ||
21 | #include <asm/kvm_para.h> | ||
22 | #include <asm/kvm_virtio.h> | ||
23 | #include <asm/setup.h> | ||
24 | #include <asm/s390_ext.h> | ||
25 | |||
26 | #define VIRTIO_SUBCODE_64 0x0D00 | ||
27 | |||
28 | /* | ||
29 | * The pointer to our (page) of device descriptions. | ||
30 | */ | ||
31 | static void *kvm_devices; | ||
32 | |||
33 | /* | ||
34 | * Unique numbering for kvm devices. | ||
35 | */ | ||
36 | static unsigned int dev_index; | ||
37 | |||
38 | struct kvm_device { | ||
39 | struct virtio_device vdev; | ||
40 | struct kvm_device_desc *desc; | ||
41 | }; | ||
42 | |||
43 | #define to_kvmdev(vd) container_of(vd, struct kvm_device, vdev) | ||
44 | |||
45 | /* | ||
46 | * memory layout: | ||
47 | * - kvm_device_descriptor | ||
48 | * struct kvm_device_desc | ||
49 | * - configuration | ||
50 | * struct kvm_vqconfig | ||
51 | * - feature bits | ||
52 | * - config space | ||
53 | */ | ||
54 | static struct kvm_vqconfig *kvm_vq_config(const struct kvm_device_desc *desc) | ||
55 | { | ||
56 | return (struct kvm_vqconfig *)(desc + 1); | ||
57 | } | ||
58 | |||
59 | static u8 *kvm_vq_features(const struct kvm_device_desc *desc) | ||
60 | { | ||
61 | return (u8 *)(kvm_vq_config(desc) + desc->num_vq); | ||
62 | } | ||
63 | |||
64 | static u8 *kvm_vq_configspace(const struct kvm_device_desc *desc) | ||
65 | { | ||
66 | return kvm_vq_features(desc) + desc->feature_len * 2; | ||
67 | } | ||
68 | |||
69 | /* | ||
70 | * The total size of the config page used by this device (incl. desc) | ||
71 | */ | ||
72 | static unsigned desc_size(const struct kvm_device_desc *desc) | ||
73 | { | ||
74 | return sizeof(*desc) | ||
75 | + desc->num_vq * sizeof(struct kvm_vqconfig) | ||
76 | + desc->feature_len * 2 | ||
77 | + desc->config_len; | ||
78 | } | ||
79 | |||
80 | /* | ||
81 | * This tests (and acknowleges) a feature bit. | ||
82 | */ | ||
83 | static bool kvm_feature(struct virtio_device *vdev, unsigned fbit) | ||
84 | { | ||
85 | struct kvm_device_desc *desc = to_kvmdev(vdev)->desc; | ||
86 | u8 *features; | ||
87 | |||
88 | if (fbit / 8 > desc->feature_len) | ||
89 | return false; | ||
90 | |||
91 | features = kvm_vq_features(desc); | ||
92 | if (!(features[fbit / 8] & (1 << (fbit % 8)))) | ||
93 | return false; | ||
94 | |||
95 | /* | ||
96 | * We set the matching bit in the other half of the bitmap to tell the | ||
97 | * Host we want to use this feature. | ||
98 | */ | ||
99 | features[desc->feature_len + fbit / 8] |= (1 << (fbit % 8)); | ||
100 | return true; | ||
101 | } | ||
102 | |||
103 | /* | ||
104 | * Reading and writing elements in config space | ||
105 | */ | ||
106 | static void kvm_get(struct virtio_device *vdev, unsigned int offset, | ||
107 | void *buf, unsigned len) | ||
108 | { | ||
109 | struct kvm_device_desc *desc = to_kvmdev(vdev)->desc; | ||
110 | |||
111 | BUG_ON(offset + len > desc->config_len); | ||
112 | memcpy(buf, kvm_vq_configspace(desc) + offset, len); | ||
113 | } | ||
114 | |||
115 | static void kvm_set(struct virtio_device *vdev, unsigned int offset, | ||
116 | const void *buf, unsigned len) | ||
117 | { | ||
118 | struct kvm_device_desc *desc = to_kvmdev(vdev)->desc; | ||
119 | |||
120 | BUG_ON(offset + len > desc->config_len); | ||
121 | memcpy(kvm_vq_configspace(desc) + offset, buf, len); | ||
122 | } | ||
123 | |||
124 | /* | ||
125 | * The operations to get and set the status word just access | ||
126 | * the status field of the device descriptor. set_status will also | ||
127 | * make a hypercall to the host, to tell about status changes | ||
128 | */ | ||
129 | static u8 kvm_get_status(struct virtio_device *vdev) | ||
130 | { | ||
131 | return to_kvmdev(vdev)->desc->status; | ||
132 | } | ||
133 | |||
134 | static void kvm_set_status(struct virtio_device *vdev, u8 status) | ||
135 | { | ||
136 | BUG_ON(!status); | ||
137 | to_kvmdev(vdev)->desc->status = status; | ||
138 | kvm_hypercall1(KVM_S390_VIRTIO_SET_STATUS, | ||
139 | (unsigned long) to_kvmdev(vdev)->desc); | ||
140 | } | ||
141 | |||
142 | /* | ||
143 | * To reset the device, we use the KVM_VIRTIO_RESET hypercall, using the | ||
144 | * descriptor address. The Host will zero the status and all the | ||
145 | * features. | ||
146 | */ | ||
147 | static void kvm_reset(struct virtio_device *vdev) | ||
148 | { | ||
149 | kvm_hypercall1(KVM_S390_VIRTIO_RESET, | ||
150 | (unsigned long) to_kvmdev(vdev)->desc); | ||
151 | } | ||
152 | |||
153 | /* | ||
154 | * When the virtio_ring code wants to notify the Host, it calls us here and we | ||
155 | * make a hypercall. We hand the address of the virtqueue so the Host | ||
156 | * knows which virtqueue we're talking about. | ||
157 | */ | ||
158 | static void kvm_notify(struct virtqueue *vq) | ||
159 | { | ||
160 | struct kvm_vqconfig *config = vq->priv; | ||
161 | |||
162 | kvm_hypercall1(KVM_S390_VIRTIO_NOTIFY, config->address); | ||
163 | } | ||
164 | |||
165 | /* | ||
166 | * This routine finds the first virtqueue described in the configuration of | ||
167 | * this device and sets it up. | ||
168 | */ | ||
169 | static struct virtqueue *kvm_find_vq(struct virtio_device *vdev, | ||
170 | unsigned index, | ||
171 | void (*callback)(struct virtqueue *vq)) | ||
172 | { | ||
173 | struct kvm_device *kdev = to_kvmdev(vdev); | ||
174 | struct kvm_vqconfig *config; | ||
175 | struct virtqueue *vq; | ||
176 | int err; | ||
177 | |||
178 | if (index >= kdev->desc->num_vq) | ||
179 | return ERR_PTR(-ENOENT); | ||
180 | |||
181 | config = kvm_vq_config(kdev->desc)+index; | ||
182 | |||
183 | if (add_shared_memory(config->address, | ||
184 | vring_size(config->num, PAGE_SIZE))) { | ||
185 | err = -ENOMEM; | ||
186 | goto out; | ||
187 | } | ||
188 | |||
189 | vq = vring_new_virtqueue(config->num, vdev, (void *) config->address, | ||
190 | kvm_notify, callback); | ||
191 | if (!vq) { | ||
192 | err = -ENOMEM; | ||
193 | goto unmap; | ||
194 | } | ||
195 | |||
196 | /* | ||
197 | * register a callback token | ||
198 | * The host will sent this via the external interrupt parameter | ||
199 | */ | ||
200 | config->token = (u64) vq; | ||
201 | |||
202 | vq->priv = config; | ||
203 | return vq; | ||
204 | unmap: | ||
205 | remove_shared_memory(config->address, vring_size(config->num, | ||
206 | PAGE_SIZE)); | ||
207 | out: | ||
208 | return ERR_PTR(err); | ||
209 | } | ||
210 | |||
211 | static void kvm_del_vq(struct virtqueue *vq) | ||
212 | { | ||
213 | struct kvm_vqconfig *config = vq->priv; | ||
214 | |||
215 | vring_del_virtqueue(vq); | ||
216 | remove_shared_memory(config->address, | ||
217 | vring_size(config->num, PAGE_SIZE)); | ||
218 | } | ||
219 | |||
220 | /* | ||
221 | * The config ops structure as defined by virtio config | ||
222 | */ | ||
223 | static struct virtio_config_ops kvm_vq_configspace_ops = { | ||
224 | .feature = kvm_feature, | ||
225 | .get = kvm_get, | ||
226 | .set = kvm_set, | ||
227 | .get_status = kvm_get_status, | ||
228 | .set_status = kvm_set_status, | ||
229 | .reset = kvm_reset, | ||
230 | .find_vq = kvm_find_vq, | ||
231 | .del_vq = kvm_del_vq, | ||
232 | }; | ||
233 | |||
234 | /* | ||
235 | * The root device for the kvm virtio devices. | ||
236 | * This makes them appear as /sys/devices/kvm_s390/0,1,2 not /sys/devices/0,1,2. | ||
237 | */ | ||
238 | static struct device kvm_root = { | ||
239 | .parent = NULL, | ||
240 | .bus_id = "kvm_s390", | ||
241 | }; | ||
242 | |||
243 | /* | ||
244 | * adds a new device and register it with virtio | ||
245 | * appropriate drivers are loaded by the device model | ||
246 | */ | ||
247 | static void add_kvm_device(struct kvm_device_desc *d) | ||
248 | { | ||
249 | struct kvm_device *kdev; | ||
250 | |||
251 | kdev = kzalloc(sizeof(*kdev), GFP_KERNEL); | ||
252 | if (!kdev) { | ||
253 | printk(KERN_EMERG "Cannot allocate kvm dev %u\n", | ||
254 | dev_index++); | ||
255 | return; | ||
256 | } | ||
257 | |||
258 | kdev->vdev.dev.parent = &kvm_root; | ||
259 | kdev->vdev.index = dev_index++; | ||
260 | kdev->vdev.id.device = d->type; | ||
261 | kdev->vdev.config = &kvm_vq_configspace_ops; | ||
262 | kdev->desc = d; | ||
263 | |||
264 | if (register_virtio_device(&kdev->vdev) != 0) { | ||
265 | printk(KERN_ERR "Failed to register kvm device %u\n", | ||
266 | kdev->vdev.index); | ||
267 | kfree(kdev); | ||
268 | } | ||
269 | } | ||
270 | |||
271 | /* | ||
272 | * scan_devices() simply iterates through the device page. | ||
273 | * The type 0 is reserved to mean "end of devices". | ||
274 | */ | ||
275 | static void scan_devices(void) | ||
276 | { | ||
277 | unsigned int i; | ||
278 | struct kvm_device_desc *d; | ||
279 | |||
280 | for (i = 0; i < PAGE_SIZE; i += desc_size(d)) { | ||
281 | d = kvm_devices + i; | ||
282 | |||
283 | if (d->type == 0) | ||
284 | break; | ||
285 | |||
286 | add_kvm_device(d); | ||
287 | } | ||
288 | } | ||
289 | |||
290 | /* | ||
291 | * we emulate the request_irq behaviour on top of s390 extints | ||
292 | */ | ||
293 | static void kvm_extint_handler(u16 code) | ||
294 | { | ||
295 | void *data = (void *) *(long *) __LC_PFAULT_INTPARM; | ||
296 | u16 subcode = S390_lowcore.cpu_addr; | ||
297 | |||
298 | if ((subcode & 0xff00) != VIRTIO_SUBCODE_64) | ||
299 | return; | ||
300 | |||
301 | vring_interrupt(0, data); | ||
302 | } | ||
303 | |||
304 | /* | ||
305 | * Init function for virtio | ||
306 | * devices are in a single page above top of "normal" mem | ||
307 | */ | ||
308 | static int __init kvm_devices_init(void) | ||
309 | { | ||
310 | int rc; | ||
311 | |||
312 | if (!MACHINE_IS_KVM) | ||
313 | return -ENODEV; | ||
314 | |||
315 | rc = device_register(&kvm_root); | ||
316 | if (rc) { | ||
317 | printk(KERN_ERR "Could not register kvm_s390 root device"); | ||
318 | return rc; | ||
319 | } | ||
320 | |||
321 | if (add_shared_memory((max_pfn) << PAGE_SHIFT, PAGE_SIZE)) { | ||
322 | device_unregister(&kvm_root); | ||
323 | return -ENOMEM; | ||
324 | } | ||
325 | |||
326 | kvm_devices = (void *) (max_pfn << PAGE_SHIFT); | ||
327 | |||
328 | ctl_set_bit(0, 9); | ||
329 | register_external_interrupt(0x2603, kvm_extint_handler); | ||
330 | |||
331 | scan_devices(); | ||
332 | return 0; | ||
333 | } | ||
334 | |||
335 | /* | ||
336 | * We do this after core stuff, but before the drivers. | ||
337 | */ | ||
338 | postcore_initcall(kvm_devices_init); | ||