]>
Commit | Line | Data |
---|---|---|
c3347ed0 JF |
1 | /* |
2 | * Protected Virtualization functions | |
3 | * | |
4 | * Copyright IBM Corp. 2020 | |
5 | * Author(s): | |
6 | * Janosch Frank <frankja@linux.ibm.com> | |
7 | * | |
8 | * This work is licensed under the terms of the GNU GPL, version 2 or (at | |
9 | * your option) any later version. See the COPYING file in the top-level | |
10 | * directory. | |
11 | */ | |
12 | #include "qemu/osdep.h" | |
13 | ||
14 | #include <linux/kvm.h> | |
15 | ||
651615d9 | 16 | #include "qapi/error.h" |
c3347ed0 JF |
17 | #include "qemu/error-report.h" |
18 | #include "sysemu/kvm.h" | |
651615d9 DG |
19 | #include "qom/object_interfaces.h" |
20 | #include "exec/confidential-guest-support.h" | |
fbc1384c | 21 | #include "hw/s390x/ipl.h" |
c3347ed0 | 22 | #include "hw/s390x/pv.h" |
03d83ecf JF |
23 | #include "target/s390x/kvm/kvm_s390x.h" |
24 | ||
25 | static bool info_valid; | |
26 | static struct kvm_s390_pv_info_vm info_vm; | |
27 | static struct kvm_s390_pv_info_dump info_dump; | |
c3347ed0 JF |
28 | |
29 | static int __s390_pv_cmd(uint32_t cmd, const char *cmdname, void *data) | |
30 | { | |
31 | struct kvm_pv_cmd pv_cmd = { | |
32 | .cmd = cmd, | |
33 | .data = (uint64_t)data, | |
34 | }; | |
e8d12a55 CB |
35 | int rc; |
36 | ||
37 | do { | |
38 | rc = kvm_vm_ioctl(kvm_state, KVM_S390_PV_COMMAND, &pv_cmd); | |
39 | } while (rc == -EINTR); | |
c3347ed0 JF |
40 | |
41 | if (rc) { | |
42 | error_report("KVM PV command %d (%s) failed: header rc %x rrc %x " | |
43 | "IOCTL rc: %d", cmd, cmdname, pv_cmd.rc, pv_cmd.rrc, | |
44 | rc); | |
45 | } | |
46 | return rc; | |
47 | } | |
48 | ||
49 | /* | |
50 | * This macro lets us pass the command as a string to the function so | |
51 | * we can print it on an error. | |
52 | */ | |
36c182bb | 53 | #define s390_pv_cmd(cmd, data) __s390_pv_cmd(cmd, #cmd, data) |
c3347ed0 JF |
54 | #define s390_pv_cmd_exit(cmd, data) \ |
55 | { \ | |
56 | int rc; \ | |
57 | \ | |
58 | rc = __s390_pv_cmd(cmd, #cmd, data);\ | |
59 | if (rc) { \ | |
60 | exit(1); \ | |
61 | } \ | |
62 | } | |
63 | ||
03d83ecf JF |
64 | int s390_pv_query_info(void) |
65 | { | |
66 | struct kvm_s390_pv_info info = { | |
67 | .header.id = KVM_PV_INFO_VM, | |
68 | .header.len_max = sizeof(info.header) + sizeof(info.vm), | |
69 | }; | |
70 | int rc; | |
71 | ||
72 | /* Info API's first user is dump so they are bundled */ | |
73 | if (!kvm_s390_get_protected_dump()) { | |
74 | return 0; | |
75 | } | |
76 | ||
77 | rc = s390_pv_cmd(KVM_PV_INFO, &info); | |
78 | if (rc) { | |
79 | error_report("KVM PV INFO cmd %x failed: %s", | |
80 | info.header.id, strerror(-rc)); | |
81 | return rc; | |
82 | } | |
83 | memcpy(&info_vm, &info.vm, sizeof(info.vm)); | |
84 | ||
85 | info.header.id = KVM_PV_INFO_DUMP; | |
86 | info.header.len_max = sizeof(info.header) + sizeof(info.dump); | |
87 | rc = s390_pv_cmd(KVM_PV_INFO, &info); | |
88 | if (rc) { | |
89 | error_report("KVM PV INFO cmd %x failed: %s", | |
90 | info.header.id, strerror(-rc)); | |
91 | return rc; | |
92 | } | |
93 | ||
94 | memcpy(&info_dump, &info.dump, sizeof(info.dump)); | |
95 | info_valid = true; | |
96 | ||
97 | return rc; | |
98 | } | |
99 | ||
c3347ed0 JF |
100 | int s390_pv_vm_enable(void) |
101 | { | |
102 | return s390_pv_cmd(KVM_PV_ENABLE, NULL); | |
103 | } | |
104 | ||
105 | void s390_pv_vm_disable(void) | |
106 | { | |
107 | s390_pv_cmd_exit(KVM_PV_DISABLE, NULL); | |
108 | } | |
109 | ||
110 | int s390_pv_set_sec_parms(uint64_t origin, uint64_t length) | |
111 | { | |
112 | struct kvm_s390_pv_sec_parm args = { | |
113 | .origin = origin, | |
114 | .length = length, | |
115 | }; | |
116 | ||
117 | return s390_pv_cmd(KVM_PV_SET_SEC_PARMS, &args); | |
118 | } | |
119 | ||
120 | /* | |
121 | * Called for each component in the SE type IPL parameter block 0. | |
122 | */ | |
123 | int s390_pv_unpack(uint64_t addr, uint64_t size, uint64_t tweak) | |
124 | { | |
125 | struct kvm_s390_pv_unp args = { | |
126 | .addr = addr, | |
127 | .size = size, | |
128 | .tweak = tweak, | |
129 | }; | |
130 | ||
131 | return s390_pv_cmd(KVM_PV_UNPACK, &args); | |
132 | } | |
133 | ||
9a432597 | 134 | void s390_pv_prep_reset(void) |
c3347ed0 JF |
135 | { |
136 | s390_pv_cmd_exit(KVM_PV_PREP_RESET, NULL); | |
137 | } | |
138 | ||
139 | int s390_pv_verify(void) | |
140 | { | |
141 | return s390_pv_cmd(KVM_PV_VERIFY, NULL); | |
142 | } | |
143 | ||
144 | void s390_pv_unshare(void) | |
145 | { | |
146 | s390_pv_cmd_exit(KVM_PV_UNSHARE_ALL, NULL); | |
147 | } | |
fbc1384c CB |
148 | |
149 | void s390_pv_inject_reset_error(CPUState *cs) | |
150 | { | |
151 | int r1 = (cs->kvm_run->s390_sieic.ipa & 0x00f0) >> 4; | |
152 | CPUS390XState *env = &S390_CPU(cs)->env; | |
153 | ||
154 | /* Report that we are unable to enter protected mode */ | |
155 | env->regs[r1 + 1] = DIAG_308_RC_INVAL_FOR_PV; | |
156 | } | |
651615d9 | 157 | |
03d83ecf JF |
158 | uint64_t kvm_s390_pv_dmp_get_size_cpu(void) |
159 | { | |
160 | return info_dump.dump_cpu_buffer_len; | |
161 | } | |
162 | ||
163 | uint64_t kvm_s390_pv_dmp_get_size_completion_data(void) | |
164 | { | |
165 | return info_dump.dump_config_finalize_len; | |
166 | } | |
167 | ||
168 | uint64_t kvm_s390_pv_dmp_get_size_mem_state(void) | |
169 | { | |
170 | return info_dump.dump_config_mem_buffer_per_1m; | |
171 | } | |
172 | ||
173 | bool kvm_s390_pv_info_basic_valid(void) | |
174 | { | |
175 | return info_valid; | |
176 | } | |
177 | ||
753ca06f JF |
178 | static int s390_pv_dump_cmd(uint64_t subcmd, uint64_t uaddr, uint64_t gaddr, |
179 | uint64_t len) | |
180 | { | |
181 | struct kvm_s390_pv_dmp dmp = { | |
182 | .subcmd = subcmd, | |
183 | .buff_addr = uaddr, | |
184 | .buff_len = len, | |
185 | .gaddr = gaddr, | |
186 | }; | |
187 | int ret; | |
188 | ||
189 | ret = s390_pv_cmd(KVM_PV_DUMP, (void *)&dmp); | |
190 | if (ret) { | |
191 | error_report("KVM DUMP command %ld failed", subcmd); | |
192 | } | |
193 | return ret; | |
194 | } | |
195 | ||
196 | int kvm_s390_dump_cpu(S390CPU *cpu, void *buff) | |
197 | { | |
198 | struct kvm_s390_pv_dmp dmp = { | |
199 | .subcmd = KVM_PV_DUMP_CPU, | |
200 | .buff_addr = (uint64_t)buff, | |
201 | .gaddr = 0, | |
202 | .buff_len = info_dump.dump_cpu_buffer_len, | |
203 | }; | |
204 | struct kvm_pv_cmd pv = { | |
205 | .cmd = KVM_PV_DUMP, | |
206 | .data = (uint64_t)&dmp, | |
207 | }; | |
208 | ||
209 | return kvm_vcpu_ioctl(CPU(cpu), KVM_S390_PV_CPU_COMMAND, &pv); | |
210 | } | |
211 | ||
212 | int kvm_s390_dump_init(void) | |
213 | { | |
214 | return s390_pv_dump_cmd(KVM_PV_DUMP_INIT, 0, 0, 0); | |
215 | } | |
216 | ||
217 | int kvm_s390_dump_mem_state(uint64_t gaddr, size_t len, void *dest) | |
218 | { | |
219 | return s390_pv_dump_cmd(KVM_PV_DUMP_CONFIG_STOR_STATE, (uint64_t)dest, | |
220 | gaddr, len); | |
221 | } | |
222 | ||
223 | int kvm_s390_dump_completion_data(void *buff) | |
224 | { | |
225 | return s390_pv_dump_cmd(KVM_PV_DUMP_COMPLETE, (uint64_t)buff, 0, | |
226 | info_dump.dump_config_finalize_len); | |
227 | } | |
228 | ||
651615d9 DG |
229 | #define TYPE_S390_PV_GUEST "s390-pv-guest" |
230 | OBJECT_DECLARE_SIMPLE_TYPE(S390PVGuest, S390_PV_GUEST) | |
231 | ||
232 | /** | |
233 | * S390PVGuest: | |
234 | * | |
235 | * The S390PVGuest object is basically a dummy used to tell the | |
236 | * confidential guest support system to use s390's PV mechanism. | |
237 | * | |
238 | * # $QEMU \ | |
239 | * -object s390-pv-guest,id=pv0 \ | |
240 | * -machine ...,confidential-guest-support=pv0 | |
241 | */ | |
242 | struct S390PVGuest { | |
243 | ConfidentialGuestSupport parent_obj; | |
244 | }; | |
245 | ||
246 | typedef struct S390PVGuestClass S390PVGuestClass; | |
247 | ||
248 | struct S390PVGuestClass { | |
249 | ConfidentialGuestSupportClass parent_class; | |
250 | }; | |
251 | ||
252 | int s390_pv_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) | |
253 | { | |
254 | if (!object_dynamic_cast(OBJECT(cgs), TYPE_S390_PV_GUEST)) { | |
255 | return 0; | |
256 | } | |
257 | ||
258 | if (!s390_has_feat(S390_FEAT_UNPACK)) { | |
259 | error_setg(errp, | |
260 | "CPU model does not support Protected Virtualization"); | |
261 | return -1; | |
262 | } | |
263 | ||
264 | cgs->ready = true; | |
265 | ||
266 | return 0; | |
267 | } | |
268 | ||
269 | OBJECT_DEFINE_TYPE_WITH_INTERFACES(S390PVGuest, | |
270 | s390_pv_guest, | |
271 | S390_PV_GUEST, | |
272 | CONFIDENTIAL_GUEST_SUPPORT, | |
273 | { TYPE_USER_CREATABLE }, | |
274 | { NULL }) | |
275 | ||
276 | static void s390_pv_guest_class_init(ObjectClass *oc, void *data) | |
277 | { | |
278 | } | |
279 | ||
280 | static void s390_pv_guest_init(Object *obj) | |
281 | { | |
282 | } | |
283 | ||
284 | static void s390_pv_guest_finalize(Object *obj) | |
285 | { | |
286 | } |