]> git.kernelconcepts.de Git - karo-tx-linux.git/blob - arch/s390/kvm/intercept.c
i2o: convert bus code to use dev_groups
[karo-tx-linux.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_RELOADVCPU_ON_STOP) {
66                 vcpu->arch.local_int.action_bits &= ~ACTION_RELOADVCPU_ON_STOP;
67                 rc = SIE_INTERCEPT_RERUNVCPU;
68                 vcpu->run->exit_reason = KVM_EXIT_INTR;
69         }
70
71         if (vcpu->arch.local_int.action_bits & ACTION_STOP_ON_STOP) {
72                 atomic_set_mask(CPUSTAT_STOPPED,
73                                 &vcpu->arch.sie_block->cpuflags);
74                 vcpu->arch.local_int.action_bits &= ~ACTION_STOP_ON_STOP;
75                 VCPU_EVENT(vcpu, 3, "%s", "cpu stopped");
76                 rc = -EOPNOTSUPP;
77         }
78
79         if (vcpu->arch.local_int.action_bits & ACTION_STORE_ON_STOP) {
80                 vcpu->arch.local_int.action_bits &= ~ACTION_STORE_ON_STOP;
81                 /* store status must be called unlocked. Since local_int.lock
82                  * only protects local_int.* and not guest memory we can give
83                  * up the lock here */
84                 spin_unlock_bh(&vcpu->arch.local_int.lock);
85                 rc = kvm_s390_vcpu_store_status(vcpu,
86                                                 KVM_S390_STORE_STATUS_NOADDR);
87                 if (rc >= 0)
88                         rc = -EOPNOTSUPP;
89         } else
90                 spin_unlock_bh(&vcpu->arch.local_int.lock);
91         return rc;
92 }
93
94 static int handle_validity(struct kvm_vcpu *vcpu)
95 {
96         int viwhy = vcpu->arch.sie_block->ipb >> 16;
97
98         vcpu->stat.exit_validity++;
99         trace_kvm_s390_intercept_validity(vcpu, viwhy);
100         WARN_ONCE(true, "kvm: unhandled validity intercept 0x%x\n", viwhy);
101         return -EOPNOTSUPP;
102 }
103
104 static int handle_instruction(struct kvm_vcpu *vcpu)
105 {
106         intercept_handler_t handler;
107
108         vcpu->stat.exit_instruction++;
109         trace_kvm_s390_intercept_instruction(vcpu,
110                                              vcpu->arch.sie_block->ipa,
111                                              vcpu->arch.sie_block->ipb);
112         handler = instruction_handlers[vcpu->arch.sie_block->ipa >> 8];
113         if (handler)
114                 return handler(vcpu);
115         return -EOPNOTSUPP;
116 }
117
118 static int handle_prog(struct kvm_vcpu *vcpu)
119 {
120         vcpu->stat.exit_program_interruption++;
121         trace_kvm_s390_intercept_prog(vcpu, vcpu->arch.sie_block->iprcc);
122         return kvm_s390_inject_program_int(vcpu, vcpu->arch.sie_block->iprcc);
123 }
124
125 static int handle_instruction_and_prog(struct kvm_vcpu *vcpu)
126 {
127         int rc, rc2;
128
129         vcpu->stat.exit_instr_and_program++;
130         rc = handle_instruction(vcpu);
131         rc2 = handle_prog(vcpu);
132
133         if (rc == -EOPNOTSUPP)
134                 vcpu->arch.sie_block->icptcode = 0x04;
135         if (rc)
136                 return rc;
137         return rc2;
138 }
139
140 static const intercept_handler_t intercept_funcs[] = {
141         [0x00 >> 2] = handle_noop,
142         [0x04 >> 2] = handle_instruction,
143         [0x08 >> 2] = handle_prog,
144         [0x0C >> 2] = handle_instruction_and_prog,
145         [0x10 >> 2] = handle_noop,
146         [0x14 >> 2] = handle_noop,
147         [0x18 >> 2] = handle_noop,
148         [0x1C >> 2] = kvm_s390_handle_wait,
149         [0x20 >> 2] = handle_validity,
150         [0x28 >> 2] = handle_stop,
151 };
152
153 int kvm_handle_sie_intercept(struct kvm_vcpu *vcpu)
154 {
155         intercept_handler_t func;
156         u8 code = vcpu->arch.sie_block->icptcode;
157
158         if (code & 3 || (code >> 2) >= ARRAY_SIZE(intercept_funcs))
159                 return -EOPNOTSUPP;
160         func = intercept_funcs[code >> 2];
161         if (func)
162                 return func(vcpu);
163         return -EOPNOTSUPP;
164 }