Merge remote-tracking branch 'remotes/bonzini-gitlab/tags/for-upstream' into staging
[qemu/ar7.git] / target / s390x / diag.c
blob1a4842956402e308426c0ed5ce5cc6042150361f
1 /*
2 * S390x DIAG instruction helper functions
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
15 #include "qemu/osdep.h"
16 #include "cpu.h"
17 #include "internal.h"
18 #include "exec/address-spaces.h"
19 #include "hw/watchdog/wdt_diag288.h"
20 #include "sysemu/cpus.h"
21 #include "hw/s390x/ipl.h"
22 #include "hw/s390x/s390-virtio-ccw.h"
23 #include "hw/s390x/pv.h"
24 #include "kvm_s390x.h"
26 int handle_diag_288(CPUS390XState *env, uint64_t r1, uint64_t r3)
28 uint64_t func = env->regs[r1];
29 uint64_t timeout = env->regs[r1 + 1];
30 uint64_t action = env->regs[r3];
31 Object *obj;
32 DIAG288State *diag288;
33 DIAG288Class *diag288_class;
35 if (r1 % 2 || action != 0) {
36 return -1;
39 /* Timeout must be more than 15 seconds except for timer deletion */
40 if (func != WDT_DIAG288_CANCEL && timeout < 15) {
41 return -1;
44 obj = object_resolve_path_type("", TYPE_WDT_DIAG288, NULL);
45 if (!obj) {
46 return -1;
49 diag288 = DIAG288(obj);
50 diag288_class = DIAG288_GET_CLASS(diag288);
51 return diag288_class->handle_timer(diag288, func, timeout);
54 static int diag308_parm_check(CPUS390XState *env, uint64_t r1, uint64_t addr,
55 uintptr_t ra, bool write)
57 /* Handled by the Ultravisor */
58 if (s390_is_pv()) {
59 return 0;
61 if ((r1 & 1) || (addr & ~TARGET_PAGE_MASK)) {
62 s390_program_interrupt(env, PGM_SPECIFICATION, ra);
63 return -1;
65 if (!address_space_access_valid(&address_space_memory, addr,
66 sizeof(IplParameterBlock), write,
67 MEMTXATTRS_UNSPECIFIED)) {
68 s390_program_interrupt(env, PGM_ADDRESSING, ra);
69 return -1;
71 return 0;
74 void handle_diag_308(CPUS390XState *env, uint64_t r1, uint64_t r3, uintptr_t ra)
76 bool valid;
77 CPUState *cs = env_cpu(env);
78 S390CPU *cpu = S390_CPU(cs);
79 uint64_t addr = env->regs[r1];
80 uint64_t subcode = env->regs[r3];
81 IplParameterBlock *iplb;
83 if (env->psw.mask & PSW_MASK_PSTATE) {
84 s390_program_interrupt(env, PGM_PRIVILEGED, ra);
85 return;
88 if (subcode & ~0x0ffffULL) {
89 s390_program_interrupt(env, PGM_SPECIFICATION, ra);
90 return;
93 if (subcode >= DIAG308_PV_SET && !s390_has_feat(S390_FEAT_UNPACK)) {
94 s390_program_interrupt(env, PGM_SPECIFICATION, ra);
95 return;
98 switch (subcode) {
99 case DIAG308_RESET_MOD_CLR:
100 s390_ipl_reset_request(cs, S390_RESET_MODIFIED_CLEAR);
101 break;
102 case DIAG308_RESET_LOAD_NORM:
103 s390_ipl_reset_request(cs, S390_RESET_LOAD_NORMAL);
104 break;
105 case DIAG308_LOAD_CLEAR:
106 /* Well we still lack the clearing bit... */
107 s390_ipl_reset_request(cs, S390_RESET_REIPL);
108 break;
109 case DIAG308_SET:
110 case DIAG308_PV_SET:
111 if (diag308_parm_check(env, r1, addr, ra, false)) {
112 return;
114 iplb = g_new0(IplParameterBlock, 1);
115 if (!s390_is_pv()) {
116 cpu_physical_memory_read(addr, iplb, sizeof(iplb->len));
117 } else {
118 s390_cpu_pv_mem_read(cpu, 0, iplb, sizeof(iplb->len));
121 if (!iplb_valid_len(iplb)) {
122 env->regs[r1 + 1] = DIAG_308_RC_INVALID;
123 goto out;
126 if (!s390_is_pv()) {
127 cpu_physical_memory_read(addr, iplb, be32_to_cpu(iplb->len));
128 } else {
129 s390_cpu_pv_mem_read(cpu, 0, iplb, be32_to_cpu(iplb->len));
132 valid = subcode == DIAG308_PV_SET ? iplb_valid_pv(iplb) : iplb_valid(iplb);
133 if (!valid) {
134 env->regs[r1 + 1] = DIAG_308_RC_INVALID;
135 goto out;
138 s390_ipl_update_diag308(iplb);
139 env->regs[r1 + 1] = DIAG_308_RC_OK;
140 out:
141 g_free(iplb);
142 return;
143 case DIAG308_STORE:
144 case DIAG308_PV_STORE:
145 if (diag308_parm_check(env, r1, addr, ra, true)) {
146 return;
148 if (subcode == DIAG308_PV_STORE) {
149 iplb = s390_ipl_get_iplb_pv();
150 } else {
151 iplb = s390_ipl_get_iplb();
153 if (!iplb) {
154 env->regs[r1 + 1] = DIAG_308_RC_NO_CONF;
155 return;
158 if (!s390_is_pv()) {
159 cpu_physical_memory_write(addr, iplb, be32_to_cpu(iplb->len));
160 } else {
161 s390_cpu_pv_mem_write(cpu, 0, iplb, be32_to_cpu(iplb->len));
163 env->regs[r1 + 1] = DIAG_308_RC_OK;
164 return;
165 case DIAG308_PV_START:
166 iplb = s390_ipl_get_iplb_pv();
167 if (!iplb) {
168 env->regs[r1 + 1] = DIAG_308_RC_NO_PV_CONF;
169 return;
172 if (kvm_s390_get_hpage_1m()) {
173 error_report("Protected VMs can currently not be backed with "
174 "huge pages");
175 env->regs[r1 + 1] = DIAG_308_RC_INVAL_FOR_PV;
176 return;
179 s390_ipl_reset_request(cs, S390_RESET_PV);
180 break;
181 default:
182 s390_program_interrupt(env, PGM_SPECIFICATION, ra);
183 break;