aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/s390
diff options
context:
space:
mode:
authorChristian Borntraeger <borntraeger@de.ibm.com>2008-03-25 13:47:46 -0400
committerAvi Kivity <avi@qumranet.com>2008-04-27 05:00:51 -0400
commite976a2b997fc4ad70ccc53acfe62811c4aaec851 (patch)
tree55994dda8046c9220c345a69b11d849323309703 /drivers/s390
parentfa5877439d5a062d91c3abd5a690483bbdb4268e (diff)
s390: KVM guest: virtio device support, and kvm hypercalls
This patch implements kvm guest kernel support for paravirtualized devices and contains two parts: o a basic virtio stub using virtio_ring and external interrupts and hypercalls o full hypercall implementation in kvm_para.h Currently we dont have PCI on s390. Making virtio_pci usable for s390 seems more complicated that providing an own stub. This virtio stub is similar to the lguest one, the memory for the descriptors and the device detection is made via additional mapped memory on top of the guest storage. We use an external interrupt with extint code 0x2603 for host->guest notification. The hypercall definition uses the diag instruction for issuing a hypercall. The parameters are written in R2-R7, the hypercall number is written in R1. This is similar to the system call ABI (svc) which can use R1 for the number and R2-R6 for the parameters. Signed-off-by: Christian Borntraeger <borntraeger@de.ibm.com> Acked-by: Martin Schwidefsky <schwidefsky@de.ibm.com> Signed-off-by: Carsten Otte <cotte@de.ibm.com> Signed-off-by: Avi Kivity <avi@qumranet.com>
Diffstat (limited to 'drivers/s390')
-rw-r--r--drivers/s390/Makefile2
-rw-r--r--drivers/s390/kvm/Makefile9
-rw-r--r--drivers/s390/kvm/kvm_virtio.c338
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 @@
5CFLAGS_sysinfo.o += -Iinclude/math-emu -Iarch/s390/math-emu -w 5CFLAGS_sysinfo.o += -Iinclude/math-emu -Iarch/s390/math-emu -w
6 6
7obj-y += s390mach.o sysinfo.o s390_rdev.o 7obj-y += s390mach.o sysinfo.o s390_rdev.o
8obj-y += cio/ block/ char/ crypto/ net/ scsi/ 8obj-y += cio/ block/ char/ crypto/ net/ scsi/ kvm/
9 9
10drivers-y += drivers/s390/built-in.o 10drivers-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
9obj-$(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 */
31static void *kvm_devices;
32
33/*
34 * Unique numbering for kvm devices.
35 */
36static unsigned int dev_index;
37
38struct 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 */
54static struct kvm_vqconfig *kvm_vq_config(const struct kvm_device_desc *desc)
55{
56 return (struct kvm_vqconfig *)(desc + 1);
57}
58
59static u8 *kvm_vq_features(const struct kvm_device_desc *desc)
60{
61 return (u8 *)(kvm_vq_config(desc) + desc->num_vq);
62}
63
64static 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 */
72static 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 */
83static 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 */
106static 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
115static 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 */
129static u8 kvm_get_status(struct virtio_device *vdev)
130{
131 return to_kvmdev(vdev)->desc->status;
132}
133
134static 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 */
147static 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 */
158static 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 */
169static 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;
204unmap:
205 remove_shared_memory(config->address, vring_size(config->num,
206 PAGE_SIZE));
207out:
208 return ERR_PTR(err);
209}
210
211static 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 */
223static 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 */
238static 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 */
247static 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 */
275static 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 */
293static 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 */
308static 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 */
338postcore_initcall(kvm_devices_init);