]> Pileus Git - ~andy/linux/blob - arch/s390/kvm/intercept.c
Merge tag 'for-linus' of git://git.kernel.org/pub/scm/virt/kvm/kvm
[~andy/linux] / 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         trace_kvm_s390_intercept_prog(vcpu, vcpu->arch.sie_block->iprcc);
116         return kvm_s390_inject_program_int(vcpu, vcpu->arch.sie_block->iprcc);
117 }
118
119 static int handle_instruction_and_prog(struct kvm_vcpu *vcpu)
120 {
121         int rc, rc2;
122
123         vcpu->stat.exit_instr_and_program++;
124         rc = handle_instruction(vcpu);
125         rc2 = handle_prog(vcpu);
126
127         if (rc == -EOPNOTSUPP)
128                 vcpu->arch.sie_block->icptcode = 0x04;
129         if (rc)
130                 return rc;
131         return rc2;
132 }
133
134 static const intercept_handler_t intercept_funcs[] = {
135         [0x00 >> 2] = handle_noop,
136         [0x04 >> 2] = handle_instruction,
137         [0x08 >> 2] = handle_prog,
138         [0x0C >> 2] = handle_instruction_and_prog,
139         [0x10 >> 2] = handle_noop,
140         [0x14 >> 2] = handle_noop,
141         [0x18 >> 2] = handle_noop,
142         [0x1C >> 2] = kvm_s390_handle_wait,
143         [0x20 >> 2] = handle_validity,
144         [0x28 >> 2] = handle_stop,
145 };
146
147 int kvm_handle_sie_intercept(struct kvm_vcpu *vcpu)
148 {
149         intercept_handler_t func;
150         u8 code = vcpu->arch.sie_block->icptcode;
151
152         if (code & 3 || (code >> 2) >= ARRAY_SIZE(intercept_funcs))
153                 return -EOPNOTSUPP;
154         func = intercept_funcs[code >> 2];
155         if (func)
156                 return func(vcpu);
157         return -EOPNOTSUPP;
158 }