aboutsummaryrefslogtreecommitdiffstats
path: root/arch/s390
diff options
context:
space:
mode:
authorHeiko Carstens <heiko.carstens@de.ibm.com>2008-03-25 13:47:20 -0400
committerAvi Kivity <avi@qumranet.com>2008-04-27 05:00:42 -0400
commitb0c632db637d68ad39d9f97f452ce176253f5f4e (patch)
tree770d68c17cbcabc1543d1e9125669130fcf4fee4 /arch/s390
parent8a88ac6183975c73c65b45f365f6f3b875c1348b (diff)
KVM: s390: arch backend for the kvm kernel module
This patch contains the port of Qumranet's kvm kernel module to IBM zSeries (aka s390x, mainframe) architecture. It uses the mainframe's virtualization instruction SIE to run virtual machines with up to 64 virtual CPUs each. This port is only usable on 64bit host kernels, and can only run 64bit guest kernels. However, running 31bit applications in guest userspace is possible. The following source files are introduced by this patch arch/s390/kvm/kvm-s390.c similar to arch/x86/kvm/x86.c, this implements all arch callbacks for kvm. __vcpu_run calls back into sie64a to enter the guest machine context arch/s390/kvm/sie64a.S assembler function sie64a, which enters guest context via SIE, and switches world before and after that include/asm-s390/kvm_host.h contains all vital data structures needed to run virtual machines on the mainframe include/asm-s390/kvm.h defines kvm_regs and friends for user access to guest register content arch/s390/kvm/gaccess.h functions similar to uaccess to access guest memory arch/s390/kvm/kvm-s390.h header file for kvm-s390 internals, extended by later patches Acked-by: Martin Schwidefsky <schwidefsky@de.ibm.com> Signed-off-by: Christian Borntraeger <borntraeger@de.ibm.com> Signed-off-by: Heiko Carstens <heiko.carstens@de.ibm.com> Signed-off-by: Carsten Otte <cotte@de.ibm.com> Signed-off-by: Avi Kivity <avi@qumranet.com>
Diffstat (limited to 'arch/s390')
-rw-r--r--arch/s390/Makefile2
-rw-r--r--arch/s390/kernel/vtime.c1
-rw-r--r--arch/s390/kvm/Makefile14
-rw-r--r--arch/s390/kvm/gaccess.h274
-rw-r--r--arch/s390/kvm/kvm-s390.c566
-rw-r--r--arch/s390/kvm/kvm-s390.h29
-rw-r--r--arch/s390/kvm/sie64a.S47
7 files changed, 932 insertions, 1 deletions
diff --git a/arch/s390/Makefile b/arch/s390/Makefile
index f708be367b03..792a4e7743ce 100644
--- a/arch/s390/Makefile
+++ b/arch/s390/Makefile
@@ -87,7 +87,7 @@ LDFLAGS_vmlinux := -e start
87head-y := arch/s390/kernel/head.o arch/s390/kernel/init_task.o 87head-y := arch/s390/kernel/head.o arch/s390/kernel/init_task.o
88 88
89core-y += arch/s390/mm/ arch/s390/kernel/ arch/s390/crypto/ \ 89core-y += arch/s390/mm/ arch/s390/kernel/ arch/s390/crypto/ \
90 arch/s390/appldata/ arch/s390/hypfs/ 90 arch/s390/appldata/ arch/s390/hypfs/ arch/s390/kvm/
91libs-y += arch/s390/lib/ 91libs-y += arch/s390/lib/
92drivers-y += drivers/s390/ 92drivers-y += drivers/s390/
93drivers-$(CONFIG_MATHEMU) += arch/s390/math-emu/ 93drivers-$(CONFIG_MATHEMU) += arch/s390/math-emu/
diff --git a/arch/s390/kernel/vtime.c b/arch/s390/kernel/vtime.c
index c5f05b3fb2c3..ca90ee3f930e 100644
--- a/arch/s390/kernel/vtime.c
+++ b/arch/s390/kernel/vtime.c
@@ -110,6 +110,7 @@ void account_system_vtime(struct task_struct *tsk)
110 S390_lowcore.steal_clock -= cputime << 12; 110 S390_lowcore.steal_clock -= cputime << 12;
111 account_system_time(tsk, 0, cputime); 111 account_system_time(tsk, 0, cputime);
112} 112}
113EXPORT_SYMBOL_GPL(account_system_vtime);
113 114
114static inline void set_vtimer(__u64 expires) 115static inline void set_vtimer(__u64 expires)
115{ 116{
diff --git a/arch/s390/kvm/Makefile b/arch/s390/kvm/Makefile
new file mode 100644
index 000000000000..0d8d1135a278
--- /dev/null
+++ b/arch/s390/kvm/Makefile
@@ -0,0 +1,14 @@
1# Makefile for kernel virtual machines 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
9common-objs = $(addprefix ../../../virt/kvm/, kvm_main.o)
10
11EXTRA_CFLAGS += -Ivirt/kvm -Iarch/s390/kvm
12
13kvm-objs := $(common-objs) kvm-s390.o sie64a.o
14obj-$(CONFIG_KVM) += kvm.o
diff --git a/arch/s390/kvm/gaccess.h b/arch/s390/kvm/gaccess.h
new file mode 100644
index 000000000000..4e0633c413f3
--- /dev/null
+++ b/arch/s390/kvm/gaccess.h
@@ -0,0 +1,274 @@
1/*
2 * gaccess.h - access guest memory
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): Carsten Otte <cotte@de.ibm.com>
11 */
12
13#ifndef __KVM_S390_GACCESS_H
14#define __KVM_S390_GACCESS_H
15
16#include <linux/compiler.h>
17#include <linux/kvm_host.h>
18#include <asm/uaccess.h>
19
20static inline void __user *__guestaddr_to_user(struct kvm_vcpu *vcpu,
21 u64 guestaddr)
22{
23 u64 prefix = vcpu->arch.sie_block->prefix;
24 u64 origin = vcpu->kvm->arch.guest_origin;
25 u64 memsize = vcpu->kvm->arch.guest_memsize;
26
27 if (guestaddr < 2 * PAGE_SIZE)
28 guestaddr += prefix;
29 else if ((guestaddr >= prefix) && (guestaddr < prefix + 2 * PAGE_SIZE))
30 guestaddr -= prefix;
31
32 if (guestaddr > memsize)
33 return (void __user __force *) ERR_PTR(-EFAULT);
34
35 guestaddr += origin;
36
37 return (void __user *) guestaddr;
38}
39
40static inline int get_guest_u64(struct kvm_vcpu *vcpu, u64 guestaddr,
41 u64 *result)
42{
43 void __user *uptr = __guestaddr_to_user(vcpu, guestaddr);
44
45 BUG_ON(guestaddr & 7);
46
47 if (IS_ERR((void __force *) uptr))
48 return PTR_ERR((void __force *) uptr);
49
50 return get_user(*result, (u64 __user *) uptr);
51}
52
53static inline int get_guest_u32(struct kvm_vcpu *vcpu, u64 guestaddr,
54 u32 *result)
55{
56 void __user *uptr = __guestaddr_to_user(vcpu, guestaddr);
57
58 BUG_ON(guestaddr & 3);
59
60 if (IS_ERR((void __force *) uptr))
61 return PTR_ERR((void __force *) uptr);
62
63 return get_user(*result, (u32 __user *) uptr);
64}
65
66static inline int get_guest_u16(struct kvm_vcpu *vcpu, u64 guestaddr,
67 u16 *result)
68{
69 void __user *uptr = __guestaddr_to_user(vcpu, guestaddr);
70
71 BUG_ON(guestaddr & 1);
72
73 if (IS_ERR(uptr))
74 return PTR_ERR(uptr);
75
76 return get_user(*result, (u16 __user *) uptr);
77}
78
79static inline int get_guest_u8(struct kvm_vcpu *vcpu, u64 guestaddr,
80 u8 *result)
81{
82 void __user *uptr = __guestaddr_to_user(vcpu, guestaddr);
83
84 if (IS_ERR((void __force *) uptr))
85 return PTR_ERR((void __force *) uptr);
86
87 return get_user(*result, (u8 __user *) uptr);
88}
89
90static inline int put_guest_u64(struct kvm_vcpu *vcpu, u64 guestaddr,
91 u64 value)
92{
93 void __user *uptr = __guestaddr_to_user(vcpu, guestaddr);
94
95 BUG_ON(guestaddr & 7);
96
97 if (IS_ERR((void __force *) uptr))
98 return PTR_ERR((void __force *) uptr);
99
100 return put_user(value, (u64 __user *) uptr);
101}
102
103static inline int put_guest_u32(struct kvm_vcpu *vcpu, u64 guestaddr,
104 u32 value)
105{
106 void __user *uptr = __guestaddr_to_user(vcpu, guestaddr);
107
108 BUG_ON(guestaddr & 3);
109
110 if (IS_ERR((void __force *) uptr))
111 return PTR_ERR((void __force *) uptr);
112
113 return put_user(value, (u32 __user *) uptr);
114}
115
116static inline int put_guest_u16(struct kvm_vcpu *vcpu, u64 guestaddr,
117 u16 value)
118{
119 void __user *uptr = __guestaddr_to_user(vcpu, guestaddr);
120
121 BUG_ON(guestaddr & 1);
122
123 if (IS_ERR((void __force *) uptr))
124 return PTR_ERR((void __force *) uptr);
125
126 return put_user(value, (u16 __user *) uptr);
127}
128
129static inline int put_guest_u8(struct kvm_vcpu *vcpu, u64 guestaddr,
130 u8 value)
131{
132 void __user *uptr = __guestaddr_to_user(vcpu, guestaddr);
133
134 if (IS_ERR((void __force *) uptr))
135 return PTR_ERR((void __force *) uptr);
136
137 return put_user(value, (u8 __user *) uptr);
138}
139
140
141static inline int __copy_to_guest_slow(struct kvm_vcpu *vcpu, u64 guestdest,
142 const void *from, unsigned long n)
143{
144 int rc;
145 unsigned long i;
146 const u8 *data = from;
147
148 for (i = 0; i < n; i++) {
149 rc = put_guest_u8(vcpu, guestdest++, *(data++));
150 if (rc < 0)
151 return rc;
152 }
153 return 0;
154}
155
156static inline int copy_to_guest(struct kvm_vcpu *vcpu, u64 guestdest,
157 const void *from, unsigned long n)
158{
159 u64 prefix = vcpu->arch.sie_block->prefix;
160 u64 origin = vcpu->kvm->arch.guest_origin;
161 u64 memsize = vcpu->kvm->arch.guest_memsize;
162
163 if ((guestdest < 2 * PAGE_SIZE) && (guestdest + n > 2 * PAGE_SIZE))
164 goto slowpath;
165
166 if ((guestdest < prefix) && (guestdest + n > prefix))
167 goto slowpath;
168
169 if ((guestdest < prefix + 2 * PAGE_SIZE)
170 && (guestdest + n > prefix + 2 * PAGE_SIZE))
171 goto slowpath;
172
173 if (guestdest < 2 * PAGE_SIZE)
174 guestdest += prefix;
175 else if ((guestdest >= prefix) && (guestdest < prefix + 2 * PAGE_SIZE))
176 guestdest -= prefix;
177
178 if (guestdest + n > memsize)
179 return -EFAULT;
180
181 if (guestdest + n < guestdest)
182 return -EFAULT;
183
184 guestdest += origin;
185
186 return copy_to_user((void __user *) guestdest, from, n);
187slowpath:
188 return __copy_to_guest_slow(vcpu, guestdest, from, n);
189}
190
191static inline int __copy_from_guest_slow(struct kvm_vcpu *vcpu, void *to,
192 u64 guestsrc, unsigned long n)
193{
194 int rc;
195 unsigned long i;
196 u8 *data = to;
197
198 for (i = 0; i < n; i++) {
199 rc = get_guest_u8(vcpu, guestsrc++, data++);
200 if (rc < 0)
201 return rc;
202 }
203 return 0;
204}
205
206static inline int copy_from_guest(struct kvm_vcpu *vcpu, void *to,
207 u64 guestsrc, unsigned long n)
208{
209 u64 prefix = vcpu->arch.sie_block->prefix;
210 u64 origin = vcpu->kvm->arch.guest_origin;
211 u64 memsize = vcpu->kvm->arch.guest_memsize;
212
213 if ((guestsrc < 2 * PAGE_SIZE) && (guestsrc + n > 2 * PAGE_SIZE))
214 goto slowpath;
215
216 if ((guestsrc < prefix) && (guestsrc + n > prefix))
217 goto slowpath;
218
219 if ((guestsrc < prefix + 2 * PAGE_SIZE)
220 && (guestsrc + n > prefix + 2 * PAGE_SIZE))
221 goto slowpath;
222
223 if (guestsrc < 2 * PAGE_SIZE)
224 guestsrc += prefix;
225 else if ((guestsrc >= prefix) && (guestsrc < prefix + 2 * PAGE_SIZE))
226 guestsrc -= prefix;
227
228 if (guestsrc + n > memsize)
229 return -EFAULT;
230
231 if (guestsrc + n < guestsrc)
232 return -EFAULT;
233
234 guestsrc += origin;
235
236 return copy_from_user(to, (void __user *) guestsrc, n);
237slowpath:
238 return __copy_from_guest_slow(vcpu, to, guestsrc, n);
239}
240
241static inline int copy_to_guest_absolute(struct kvm_vcpu *vcpu, u64 guestdest,
242 const void *from, unsigned long n)
243{
244 u64 origin = vcpu->kvm->arch.guest_origin;
245 u64 memsize = vcpu->kvm->arch.guest_memsize;
246
247 if (guestdest + n > memsize)
248 return -EFAULT;
249
250 if (guestdest + n < guestdest)
251 return -EFAULT;
252
253 guestdest += origin;
254
255 return copy_to_user((void __user *) guestdest, from, n);
256}
257
258static inline int copy_from_guest_absolute(struct kvm_vcpu *vcpu, void *to,
259 u64 guestsrc, unsigned long n)
260{
261 u64 origin = vcpu->kvm->arch.guest_origin;
262 u64 memsize = vcpu->kvm->arch.guest_memsize;
263
264 if (guestsrc + n > memsize)
265 return -EFAULT;
266
267 if (guestsrc + n < guestsrc)
268 return -EFAULT;
269
270 guestsrc += origin;
271
272 return copy_from_user(to, (void __user *) guestsrc, n);
273}
274#endif
diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c
new file mode 100644
index 000000000000..6e1e1d39ae15
--- /dev/null
+++ b/arch/s390/kvm/kvm-s390.c
@@ -0,0 +1,566 @@
1/*
2 * s390host.c -- hosting zSeries kernel virtual machines
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): Carsten Otte <cotte@de.ibm.com>
11 * Christian Borntraeger <borntraeger@de.ibm.com>
12 * Heiko Carstens <heiko.carstens@de.ibm.com>
13 */
14
15#include <linux/compiler.h>
16#include <linux/err.h>
17#include <linux/fs.h>
18#include <linux/init.h>
19#include <linux/kvm.h>
20#include <linux/kvm_host.h>
21#include <linux/module.h>
22#include <linux/slab.h>
23#include <asm/lowcore.h>
24#include <asm/pgtable.h>
25
26#include "gaccess.h"
27
28#define VCPU_STAT(x) offsetof(struct kvm_vcpu, stat.x), KVM_STAT_VCPU
29
30struct kvm_stats_debugfs_item debugfs_entries[] = {
31 { "userspace_handled", VCPU_STAT(exit_userspace) },
32 { NULL }
33};
34
35
36/* Section: not file related */
37void kvm_arch_hardware_enable(void *garbage)
38{
39 /* every s390 is virtualization enabled ;-) */
40}
41
42void kvm_arch_hardware_disable(void *garbage)
43{
44}
45
46void decache_vcpus_on_cpu(int cpu)
47{
48}
49
50int kvm_arch_hardware_setup(void)
51{
52 return 0;
53}
54
55void kvm_arch_hardware_unsetup(void)
56{
57}
58
59void kvm_arch_check_processor_compat(void *rtn)
60{
61}
62
63int kvm_arch_init(void *opaque)
64{
65 return 0;
66}
67
68void kvm_arch_exit(void)
69{
70}
71
72/* Section: device related */
73long kvm_arch_dev_ioctl(struct file *filp,
74 unsigned int ioctl, unsigned long arg)
75{
76 if (ioctl == KVM_S390_ENABLE_SIE)
77 return s390_enable_sie();
78 return -EINVAL;
79}
80
81int kvm_dev_ioctl_check_extension(long ext)
82{
83 return 0;
84}
85
86/* Section: vm related */
87/*
88 * Get (and clear) the dirty memory log for a memory slot.
89 */
90int kvm_vm_ioctl_get_dirty_log(struct kvm *kvm,
91 struct kvm_dirty_log *log)
92{
93 return 0;
94}
95
96long kvm_arch_vm_ioctl(struct file *filp,
97 unsigned int ioctl, unsigned long arg)
98{
99 struct kvm *kvm = filp->private_data;
100 void __user *argp = (void __user *)arg;
101 int r;
102
103 switch (ioctl) {
104 default:
105 r = -EINVAL;
106 }
107
108 return r;
109}
110
111struct kvm *kvm_arch_create_vm(void)
112{
113 struct kvm *kvm;
114 int rc;
115 char debug_name[16];
116
117 rc = s390_enable_sie();
118 if (rc)
119 goto out_nokvm;
120
121 rc = -ENOMEM;
122 kvm = kzalloc(sizeof(struct kvm), GFP_KERNEL);
123 if (!kvm)
124 goto out_nokvm;
125
126 kvm->arch.sca = (struct sca_block *) get_zeroed_page(GFP_KERNEL);
127 if (!kvm->arch.sca)
128 goto out_nosca;
129
130 sprintf(debug_name, "kvm-%u", current->pid);
131
132 kvm->arch.dbf = debug_register(debug_name, 8, 2, 8 * sizeof(long));
133 if (!kvm->arch.dbf)
134 goto out_nodbf;
135
136 debug_register_view(kvm->arch.dbf, &debug_sprintf_view);
137 VM_EVENT(kvm, 3, "%s", "vm created");
138
139 try_module_get(THIS_MODULE);
140
141 return kvm;
142out_nodbf:
143 free_page((unsigned long)(kvm->arch.sca));
144out_nosca:
145 kfree(kvm);
146out_nokvm:
147 return ERR_PTR(rc);
148}
149
150void kvm_arch_destroy_vm(struct kvm *kvm)
151{
152 debug_unregister(kvm->arch.dbf);
153 free_page((unsigned long)(kvm->arch.sca));
154 kfree(kvm);
155 module_put(THIS_MODULE);
156}
157
158/* Section: vcpu related */
159int kvm_arch_vcpu_init(struct kvm_vcpu *vcpu)
160{
161 return 0;
162}
163
164void kvm_arch_vcpu_uninit(struct kvm_vcpu *vcpu)
165{
166 /* kvm common code refers to this, but does'nt call it */
167 BUG();
168}
169
170void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
171{
172 save_fp_regs(&vcpu->arch.host_fpregs);
173 save_access_regs(vcpu->arch.host_acrs);
174 vcpu->arch.guest_fpregs.fpc &= FPC_VALID_MASK;
175 restore_fp_regs(&vcpu->arch.guest_fpregs);
176 restore_access_regs(vcpu->arch.guest_acrs);
177
178 if (signal_pending(current))
179 atomic_set_mask(CPUSTAT_STOP_INT,
180 &vcpu->arch.sie_block->cpuflags);
181}
182
183void kvm_arch_vcpu_put(struct kvm_vcpu *vcpu)
184{
185 save_fp_regs(&vcpu->arch.guest_fpregs);
186 save_access_regs(vcpu->arch.guest_acrs);
187 restore_fp_regs(&vcpu->arch.host_fpregs);
188 restore_access_regs(vcpu->arch.host_acrs);
189}
190
191static void kvm_s390_vcpu_initial_reset(struct kvm_vcpu *vcpu)
192{
193 /* this equals initial cpu reset in pop, but we don't switch to ESA */
194 vcpu->arch.sie_block->gpsw.mask = 0UL;
195 vcpu->arch.sie_block->gpsw.addr = 0UL;
196 vcpu->arch.sie_block->prefix = 0UL;
197 vcpu->arch.sie_block->ihcpu = 0xffff;
198 vcpu->arch.sie_block->cputm = 0UL;
199 vcpu->arch.sie_block->ckc = 0UL;
200 vcpu->arch.sie_block->todpr = 0;
201 memset(vcpu->arch.sie_block->gcr, 0, 16 * sizeof(__u64));
202 vcpu->arch.sie_block->gcr[0] = 0xE0UL;
203 vcpu->arch.sie_block->gcr[14] = 0xC2000000UL;
204 vcpu->arch.guest_fpregs.fpc = 0;
205 asm volatile("lfpc %0" : : "Q" (vcpu->arch.guest_fpregs.fpc));
206 vcpu->arch.sie_block->gbea = 1;
207}
208
209int kvm_arch_vcpu_setup(struct kvm_vcpu *vcpu)
210{
211 atomic_set(&vcpu->arch.sie_block->cpuflags, CPUSTAT_ZARCH);
212 vcpu->arch.sie_block->gmslm = 0xffffffffffUL;
213 vcpu->arch.sie_block->gmsor = 0x000000000000;
214 vcpu->arch.sie_block->ecb = 2;
215 vcpu->arch.sie_block->eca = 0xC1002001U;
216
217 return 0;
218}
219
220struct kvm_vcpu *kvm_arch_vcpu_create(struct kvm *kvm,
221 unsigned int id)
222{
223 struct kvm_vcpu *vcpu = kzalloc(sizeof(struct kvm_vcpu), GFP_KERNEL);
224 int rc = -ENOMEM;
225
226 if (!vcpu)
227 goto out_nomem;
228
229 vcpu->arch.sie_block = (struct sie_block *) get_zeroed_page(GFP_KERNEL);
230
231 if (!vcpu->arch.sie_block)
232 goto out_free_cpu;
233
234 vcpu->arch.sie_block->icpua = id;
235 BUG_ON(!kvm->arch.sca);
236 BUG_ON(kvm->arch.sca->cpu[id].sda);
237 kvm->arch.sca->cpu[id].sda = (__u64) vcpu->arch.sie_block;
238 vcpu->arch.sie_block->scaoh = (__u32)(((__u64)kvm->arch.sca) >> 32);
239 vcpu->arch.sie_block->scaol = (__u32)(__u64)kvm->arch.sca;
240
241 rc = kvm_vcpu_init(vcpu, kvm, id);
242 if (rc)
243 goto out_free_cpu;
244 VM_EVENT(kvm, 3, "create cpu %d at %p, sie block at %p", id, vcpu,
245 vcpu->arch.sie_block);
246
247 try_module_get(THIS_MODULE);
248
249 return vcpu;
250out_free_cpu:
251 kfree(vcpu);
252out_nomem:
253 return ERR_PTR(rc);
254}
255
256void kvm_arch_vcpu_destroy(struct kvm_vcpu *vcpu)
257{
258 VCPU_EVENT(vcpu, 3, "%s", "destroy cpu");
259 free_page((unsigned long)(vcpu->arch.sie_block));
260 kfree(vcpu);
261 module_put(THIS_MODULE);
262}
263
264int kvm_arch_vcpu_runnable(struct kvm_vcpu *vcpu)
265{
266 /* kvm common code refers to this, but never calls it */
267 BUG();
268 return 0;
269}
270
271static int kvm_arch_vcpu_ioctl_initial_reset(struct kvm_vcpu *vcpu)
272{
273 vcpu_load(vcpu);
274 kvm_s390_vcpu_initial_reset(vcpu);
275 vcpu_put(vcpu);
276 return 0;
277}
278
279int kvm_arch_vcpu_ioctl_set_regs(struct kvm_vcpu *vcpu, struct kvm_regs *regs)
280{
281 vcpu_load(vcpu);
282 memcpy(&vcpu->arch.guest_gprs, &regs->gprs, sizeof(regs->gprs));
283 vcpu_put(vcpu);
284 return 0;
285}
286
287int kvm_arch_vcpu_ioctl_get_regs(struct kvm_vcpu *vcpu, struct kvm_regs *regs)
288{
289 vcpu_load(vcpu);
290 memcpy(&regs->gprs, &vcpu->arch.guest_gprs, sizeof(regs->gprs));
291 vcpu_put(vcpu);
292 return 0;
293}
294
295int kvm_arch_vcpu_ioctl_set_sregs(struct kvm_vcpu *vcpu,
296 struct kvm_sregs *sregs)
297{
298 vcpu_load(vcpu);
299 memcpy(&vcpu->arch.guest_acrs, &sregs->acrs, sizeof(sregs->acrs));
300 memcpy(&vcpu->arch.sie_block->gcr, &sregs->crs, sizeof(sregs->crs));
301 vcpu_put(vcpu);
302 return 0;
303}
304
305int kvm_arch_vcpu_ioctl_get_sregs(struct kvm_vcpu *vcpu,
306 struct kvm_sregs *sregs)
307{
308 vcpu_load(vcpu);
309 memcpy(&sregs->acrs, &vcpu->arch.guest_acrs, sizeof(sregs->acrs));
310 memcpy(&sregs->crs, &vcpu->arch.sie_block->gcr, sizeof(sregs->crs));
311 vcpu_put(vcpu);
312 return 0;
313}
314
315int kvm_arch_vcpu_ioctl_set_fpu(struct kvm_vcpu *vcpu, struct kvm_fpu *fpu)
316{
317 vcpu_load(vcpu);
318 memcpy(&vcpu->arch.guest_fpregs.fprs, &fpu->fprs, sizeof(fpu->fprs));
319 vcpu->arch.guest_fpregs.fpc = fpu->fpc;
320 vcpu_put(vcpu);
321 return 0;
322}
323
324int kvm_arch_vcpu_ioctl_get_fpu(struct kvm_vcpu *vcpu, struct kvm_fpu *fpu)
325{
326 vcpu_load(vcpu);
327 memcpy(&fpu->fprs, &vcpu->arch.guest_fpregs.fprs, sizeof(fpu->fprs));
328 fpu->fpc = vcpu->arch.guest_fpregs.fpc;
329 vcpu_put(vcpu);
330 return 0;
331}
332
333static int kvm_arch_vcpu_ioctl_set_initial_psw(struct kvm_vcpu *vcpu, psw_t psw)
334{
335 int rc = 0;
336
337 vcpu_load(vcpu);
338 if (atomic_read(&vcpu->arch.sie_block->cpuflags) & CPUSTAT_RUNNING)
339 rc = -EBUSY;
340 else
341 vcpu->arch.sie_block->gpsw = psw;
342 vcpu_put(vcpu);
343 return rc;
344}
345
346int kvm_arch_vcpu_ioctl_translate(struct kvm_vcpu *vcpu,
347 struct kvm_translation *tr)
348{
349 return -EINVAL; /* not implemented yet */
350}
351
352int kvm_arch_vcpu_ioctl_debug_guest(struct kvm_vcpu *vcpu,
353 struct kvm_debug_guest *dbg)
354{
355 return -EINVAL; /* not implemented yet */
356}
357
358static void __vcpu_run(struct kvm_vcpu *vcpu)
359{
360 memcpy(&vcpu->arch.sie_block->gg14, &vcpu->arch.guest_gprs[14], 16);
361
362 if (need_resched())
363 schedule();
364
365 vcpu->arch.sie_block->icptcode = 0;
366 local_irq_disable();
367 kvm_guest_enter();
368 local_irq_enable();
369 VCPU_EVENT(vcpu, 6, "entering sie flags %x",
370 atomic_read(&vcpu->arch.sie_block->cpuflags));
371 sie64a(vcpu->arch.sie_block, vcpu->arch.guest_gprs);
372 VCPU_EVENT(vcpu, 6, "exit sie icptcode %d",
373 vcpu->arch.sie_block->icptcode);
374 local_irq_disable();
375 kvm_guest_exit();
376 local_irq_enable();
377
378 memcpy(&vcpu->arch.guest_gprs[14], &vcpu->arch.sie_block->gg14, 16);
379}
380
381int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
382{
383 sigset_t sigsaved;
384
385 vcpu_load(vcpu);
386
387 if (vcpu->sigset_active)
388 sigprocmask(SIG_SETMASK, &vcpu->sigset, &sigsaved);
389
390 atomic_set_mask(CPUSTAT_RUNNING, &vcpu->arch.sie_block->cpuflags);
391
392 __vcpu_run(vcpu);
393
394 if (vcpu->sigset_active)
395 sigprocmask(SIG_SETMASK, &sigsaved, NULL);
396
397 vcpu_put(vcpu);
398
399 vcpu->stat.exit_userspace++;
400 return 0;
401}
402
403static int __guestcopy(struct kvm_vcpu *vcpu, u64 guestdest, const void *from,
404 unsigned long n, int prefix)
405{
406 if (prefix)
407 return copy_to_guest(vcpu, guestdest, from, n);
408 else
409 return copy_to_guest_absolute(vcpu, guestdest, from, n);
410}
411
412/*
413 * store status at address
414 * we use have two special cases:
415 * KVM_S390_STORE_STATUS_NOADDR: -> 0x1200 on 64 bit
416 * KVM_S390_STORE_STATUS_PREFIXED: -> prefix
417 */
418int __kvm_s390_vcpu_store_status(struct kvm_vcpu *vcpu, unsigned long addr)
419{
420 const unsigned char archmode = 1;
421 int prefix;
422
423 if (addr == KVM_S390_STORE_STATUS_NOADDR) {
424 if (copy_to_guest_absolute(vcpu, 163ul, &archmode, 1))
425 return -EFAULT;
426 addr = SAVE_AREA_BASE;
427 prefix = 0;
428 } else if (addr == KVM_S390_STORE_STATUS_PREFIXED) {
429 if (copy_to_guest(vcpu, 163ul, &archmode, 1))
430 return -EFAULT;
431 addr = SAVE_AREA_BASE;
432 prefix = 1;
433 } else
434 prefix = 0;
435
436 if (__guestcopy(vcpu, addr + offsetof(struct save_area_s390x, fp_regs),
437 vcpu->arch.guest_fpregs.fprs, 128, prefix))
438 return -EFAULT;
439
440 if (__guestcopy(vcpu, addr + offsetof(struct save_area_s390x, gp_regs),
441 vcpu->arch.guest_gprs, 128, prefix))
442 return -EFAULT;
443
444 if (__guestcopy(vcpu, addr + offsetof(struct save_area_s390x, psw),
445 &vcpu->arch.sie_block->gpsw, 16, prefix))
446 return -EFAULT;
447
448 if (__guestcopy(vcpu, addr + offsetof(struct save_area_s390x, pref_reg),
449 &vcpu->arch.sie_block->prefix, 4, prefix))
450 return -EFAULT;
451
452 if (__guestcopy(vcpu,
453 addr + offsetof(struct save_area_s390x, fp_ctrl_reg),
454 &vcpu->arch.guest_fpregs.fpc, 4, prefix))
455 return -EFAULT;
456
457 if (__guestcopy(vcpu, addr + offsetof(struct save_area_s390x, tod_reg),
458 &vcpu->arch.sie_block->todpr, 4, prefix))
459 return -EFAULT;
460
461 if (__guestcopy(vcpu, addr + offsetof(struct save_area_s390x, timer),
462 &vcpu->arch.sie_block->cputm, 8, prefix))
463 return -EFAULT;
464
465 if (__guestcopy(vcpu, addr + offsetof(struct save_area_s390x, clk_cmp),
466 &vcpu->arch.sie_block->ckc, 8, prefix))
467 return -EFAULT;
468
469 if (__guestcopy(vcpu, addr + offsetof(struct save_area_s390x, acc_regs),
470 &vcpu->arch.guest_acrs, 64, prefix))
471 return -EFAULT;
472
473 if (__guestcopy(vcpu,
474 addr + offsetof(struct save_area_s390x, ctrl_regs),
475 &vcpu->arch.sie_block->gcr, 128, prefix))
476 return -EFAULT;
477 return 0;
478}
479
480static int kvm_s390_vcpu_store_status(struct kvm_vcpu *vcpu, unsigned long addr)
481{
482 int rc;
483
484 vcpu_load(vcpu);
485 rc = __kvm_s390_vcpu_store_status(vcpu, addr);
486 vcpu_put(vcpu);
487 return rc;
488}
489
490long kvm_arch_vcpu_ioctl(struct file *filp,
491 unsigned int ioctl, unsigned long arg)
492{
493 struct kvm_vcpu *vcpu = filp->private_data;
494 void __user *argp = (void __user *)arg;
495
496 switch (ioctl) {
497 case KVM_S390_STORE_STATUS:
498 return kvm_s390_vcpu_store_status(vcpu, arg);
499 case KVM_S390_SET_INITIAL_PSW: {
500 psw_t psw;
501
502 if (copy_from_user(&psw, argp, sizeof(psw)))
503 return -EFAULT;
504 return kvm_arch_vcpu_ioctl_set_initial_psw(vcpu, psw);
505 }
506 case KVM_S390_INITIAL_RESET:
507 return kvm_arch_vcpu_ioctl_initial_reset(vcpu);
508 default:
509 ;
510 }
511 return -EINVAL;
512}
513
514/* Section: memory related */
515int kvm_arch_set_memory_region(struct kvm *kvm,
516 struct kvm_userspace_memory_region *mem,
517 struct kvm_memory_slot old,
518 int user_alloc)
519{
520 /* A few sanity checks. We can have exactly one memory slot which has
521 to start at guest virtual zero and which has to be located at a
522 page boundary in userland and which has to end at a page boundary.
523 The memory in userland is ok to be fragmented into various different
524 vmas. It is okay to mmap() and munmap() stuff in this slot after
525 doing this call at any time */
526
527 if (mem->slot)
528 return -EINVAL;
529
530 if (mem->guest_phys_addr)
531 return -EINVAL;
532
533 if (mem->userspace_addr & (PAGE_SIZE - 1))
534 return -EINVAL;
535
536 if (mem->memory_size & (PAGE_SIZE - 1))
537 return -EINVAL;
538
539 kvm->arch.guest_origin = mem->userspace_addr;
540 kvm->arch.guest_memsize = mem->memory_size;
541
542 /* FIXME: we do want to interrupt running CPUs and update their memory
543 configuration now to avoid race conditions. But hey, changing the
544 memory layout while virtual CPUs are running is usually bad
545 programming practice. */
546
547 return 0;
548}
549
550gfn_t unalias_gfn(struct kvm *kvm, gfn_t gfn)
551{
552 return gfn;
553}
554
555static int __init kvm_s390_init(void)
556{
557 return kvm_init(NULL, sizeof(struct kvm_vcpu), THIS_MODULE);
558}
559
560static void __exit kvm_s390_exit(void)
561{
562 kvm_exit();
563}
564
565module_init(kvm_s390_init);
566module_exit(kvm_s390_exit);
diff --git a/arch/s390/kvm/kvm-s390.h b/arch/s390/kvm/kvm-s390.h
new file mode 100644
index 000000000000..ed64a22ca86f
--- /dev/null
+++ b/arch/s390/kvm/kvm-s390.h
@@ -0,0 +1,29 @@
1/*
2 * kvm_s390.h - definition 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): Carsten Otte <cotte@de.ibm.com>
11 * Christian Borntraeger <borntraeger@de.ibm.com>
12 */
13
14#ifndef ARCH_S390_KVM_S390_H
15#define ARCH_S390_KVM_S390_H
16#define VM_EVENT(d_kvm, d_loglevel, d_string, d_args...)\
17do { \
18 debug_sprintf_event(d_kvm->arch.dbf, d_loglevel, d_string "\n", \
19 d_args); \
20} while (0)
21
22#define VCPU_EVENT(d_vcpu, d_loglevel, d_string, d_args...)\
23do { \
24 debug_sprintf_event(d_vcpu->kvm->arch.dbf, d_loglevel, \
25 "%02d[%016lx-%016lx]: " d_string "\n", d_vcpu->vcpu_id, \
26 d_vcpu->arch.sie_block->gpsw.mask, d_vcpu->arch.sie_block->gpsw.addr,\
27 d_args); \
28} while (0)
29#endif
diff --git a/arch/s390/kvm/sie64a.S b/arch/s390/kvm/sie64a.S
new file mode 100644
index 000000000000..934fd6a885f6
--- /dev/null
+++ b/arch/s390/kvm/sie64a.S
@@ -0,0 +1,47 @@
1/*
2 * sie64a.S - low level sie call
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): Heiko Carstens <heiko.carstens@de.ibm.com>
11 */
12
13#include <linux/errno.h>
14#include <asm/asm-offsets.h>
15
16SP_R5 = 5 * 8 # offset into stackframe
17SP_R6 = 6 * 8
18
19/*
20 * sie64a calling convention:
21 * %r2 pointer to sie control block
22 * %r3 guest register save area
23 */
24 .globl sie64a
25sie64a:
26 lgr %r5,%r3
27 stmg %r5,%r14,SP_R5(%r15) # save register on entry
28 lgr %r14,%r2 # pointer to sie control block
29 lmg %r0,%r13,0(%r3) # load guest gprs 0-13
30sie_inst:
31 sie 0(%r14)
32 lg %r14,SP_R5(%r15)
33 stmg %r0,%r13,0(%r14) # save guest gprs 0-13
34 lghi %r2,0
35 lmg %r6,%r14,SP_R6(%r15)
36 br %r14
37
38sie_err:
39 lg %r14,SP_R5(%r15)
40 stmg %r0,%r13,0(%r14) # save guest gprs 0-13
41 lghi %r2,-EFAULT
42 lmg %r6,%r14,SP_R6(%r15)
43 br %r14
44
45 .section __ex_table,"a"
46 .quad sie_inst,sie_err
47 .previous