Merge branch 'msm-next' of git://people.freedesktop.org/~robclark/linux into drm...
[pandora-kernel.git] / arch / s390 / kvm / intercept.c
1 /*
2  * in-kernel handling for sie intercepts
3  *
4  * Copyright IBM Corp. 2008, 2009
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 #include <linux/kvm_host.h>
15 #include <linux/errno.h>
16 #include <linux/pagemap.h>
17
18 #include <asm/kvm_host.h>
19
20 #include "kvm-s390.h"
21 #include "gaccess.h"
22 #include "trace.h"
23 #include "trace-s390.h"
24
25
26 static const intercept_handler_t instruction_handlers[256] = {
27         [0x01] = kvm_s390_handle_01,
28         [0x82] = kvm_s390_handle_lpsw,
29         [0x83] = kvm_s390_handle_diag,
30         [0xae] = kvm_s390_handle_sigp,
31         [0xb2] = kvm_s390_handle_b2,
32         [0xb7] = kvm_s390_handle_lctl,
33         [0xb9] = kvm_s390_handle_b9,
34         [0xe5] = kvm_s390_handle_e5,
35         [0xeb] = kvm_s390_handle_eb,
36 };
37
38 static int handle_noop(struct kvm_vcpu *vcpu)
39 {
40         switch (vcpu->arch.sie_block->icptcode) {
41         case 0x0:
42                 vcpu->stat.exit_null++;
43                 break;
44         case 0x10:
45                 vcpu->stat.exit_external_request++;
46                 break;
47         case 0x14:
48                 vcpu->stat.exit_external_interrupt++;
49                 break;
50         default:
51                 break; /* nothing */
52         }
53         return 0;
54 }
55
56 static int handle_stop(struct kvm_vcpu *vcpu)
57 {
58         int rc = 0;
59
60         vcpu->stat.exit_stop_request++;
61         spin_lock_bh(&vcpu->arch.local_int.lock);
62
63         trace_kvm_s390_stop_request(vcpu->arch.local_int.action_bits);
64
65         if (vcpu->arch.local_int.action_bits & ACTION_STOP_ON_STOP) {
66                 atomic_set_mask(CPUSTAT_STOPPED,
67                                 &vcpu->arch.sie_block->cpuflags);
68                 vcpu->arch.local_int.action_bits &= ~ACTION_STOP_ON_STOP;
69                 VCPU_EVENT(vcpu, 3, "%s", "cpu stopped");
70                 rc = -EOPNOTSUPP;
71         }
72
73         if (vcpu->arch.local_int.action_bits & ACTION_STORE_ON_STOP) {
74                 vcpu->arch.local_int.action_bits &= ~ACTION_STORE_ON_STOP;
75                 /* store status must be called unlocked. Since local_int.lock
76                  * only protects local_int.* and not guest memory we can give
77                  * up the lock here */
78                 spin_unlock_bh(&vcpu->arch.local_int.lock);
79                 rc = kvm_s390_vcpu_store_status(vcpu,
80                                                 KVM_S390_STORE_STATUS_NOADDR);
81                 if (rc >= 0)
82                         rc = -EOPNOTSUPP;
83         } else
84                 spin_unlock_bh(&vcpu->arch.local_int.lock);
85         return rc;
86 }
87
88 static int handle_validity(struct kvm_vcpu *vcpu)
89 {
90         int viwhy = vcpu->arch.sie_block->ipb >> 16;
91
92         vcpu->stat.exit_validity++;
93         trace_kvm_s390_intercept_validity(vcpu, viwhy);
94         WARN_ONCE(true, "kvm: unhandled validity intercept 0x%x\n", viwhy);
95         return -EOPNOTSUPP;
96 }
97
98 static int handle_instruction(struct kvm_vcpu *vcpu)
99 {
100         intercept_handler_t handler;
101
102         vcpu->stat.exit_instruction++;
103         trace_kvm_s390_intercept_instruction(vcpu,
104                                              vcpu->arch.sie_block->ipa,
105                                              vcpu->arch.sie_block->ipb);
106         handler = instruction_handlers[vcpu->arch.sie_block->ipa >> 8];
107         if (handler)
108                 return handler(vcpu);
109         return -EOPNOTSUPP;
110 }
111
112 static int handle_prog(struct kvm_vcpu *vcpu)
113 {
114         vcpu->stat.exit_program_interruption++;
115
116         /* Restore ITDB to Program-Interruption TDB in guest memory */
117         if (IS_TE_ENABLED(vcpu) &&
118             !(current->thread.per_flags & PER_FLAG_NO_TE) &&
119             IS_ITDB_VALID(vcpu)) {
120                 copy_to_guest(vcpu, TDB_ADDR, vcpu->arch.sie_block->itdba,
121                               sizeof(struct kvm_s390_itdb));
122                 memset((void *) vcpu->arch.sie_block->itdba, 0,
123                        sizeof(struct kvm_s390_itdb));
124         }
125
126         trace_kvm_s390_intercept_prog(vcpu, vcpu->arch.sie_block->iprcc);
127         return kvm_s390_inject_program_int(vcpu, vcpu->arch.sie_block->iprcc);
128 }
129
130 static int handle_instruction_and_prog(struct kvm_vcpu *vcpu)
131 {
132         int rc, rc2;
133
134         vcpu->stat.exit_instr_and_program++;
135         rc = handle_instruction(vcpu);
136         rc2 = handle_prog(vcpu);
137
138         if (rc == -EOPNOTSUPP)
139                 vcpu->arch.sie_block->icptcode = 0x04;
140         if (rc)
141                 return rc;
142         return rc2;
143 }
144
145 static const intercept_handler_t intercept_funcs[] = {
146         [0x00 >> 2] = handle_noop,
147         [0x04 >> 2] = handle_instruction,
148         [0x08 >> 2] = handle_prog,
149         [0x0C >> 2] = handle_instruction_and_prog,
150         [0x10 >> 2] = handle_noop,
151         [0x14 >> 2] = handle_noop,
152         [0x18 >> 2] = handle_noop,
153         [0x1C >> 2] = kvm_s390_handle_wait,
154         [0x20 >> 2] = handle_validity,
155         [0x28 >> 2] = handle_stop,
156 };
157
158 int kvm_handle_sie_intercept(struct kvm_vcpu *vcpu)
159 {
160         intercept_handler_t func;
161         u8 code = vcpu->arch.sie_block->icptcode;
162
163         if (code & 3 || (code >> 2) >= ARRAY_SIZE(intercept_funcs))
164                 return -EOPNOTSUPP;
165         func = intercept_funcs[code >> 2];
166         if (func)
167                 return func(vcpu);
168         return -EOPNOTSUPP;
169 }