1 /* 2 * S390x DIAG instruction helper functions 3 * 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. 8 * 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. 13 */ 14 15 #include "qemu/osdep.h" 16 #include "cpu.h" 17 #include "internal.h" 18 #include "exec/address-spaces.h" 19 #include "exec/exec-all.h" 20 #include "hw/watchdog/wdt_diag288.h" 21 #include "sysemu/cpus.h" 22 #include "hw/s390x/ipl.h" 23 #include "hw/s390x/s390-virtio-ccw.h" 24 25 static int modified_clear_reset(S390CPU *cpu) 26 { 27 S390CPUClass *scc = S390_CPU_GET_CLASS(cpu); 28 CPUState *t; 29 30 pause_all_vcpus(); 31 cpu_synchronize_all_states(); 32 CPU_FOREACH(t) { 33 run_on_cpu(t, s390_do_cpu_full_reset, RUN_ON_CPU_NULL); 34 } 35 s390_cmma_reset(); 36 subsystem_reset(); 37 s390_crypto_reset(); 38 scc->load_normal(CPU(cpu)); 39 cpu_synchronize_all_post_reset(); 40 resume_all_vcpus(); 41 return 0; 42 } 43 44 static inline void s390_do_cpu_reset(CPUState *cs, run_on_cpu_data arg) 45 { 46 S390CPUClass *scc = S390_CPU_GET_CLASS(cs); 47 48 scc->cpu_reset(cs); 49 } 50 51 static int load_normal_reset(S390CPU *cpu) 52 { 53 S390CPUClass *scc = S390_CPU_GET_CLASS(cpu); 54 CPUState *t; 55 56 pause_all_vcpus(); 57 cpu_synchronize_all_states(); 58 CPU_FOREACH(t) { 59 run_on_cpu(t, s390_do_cpu_reset, RUN_ON_CPU_NULL); 60 } 61 s390_cmma_reset(); 62 subsystem_reset(); 63 scc->initial_cpu_reset(CPU(cpu)); 64 scc->load_normal(CPU(cpu)); 65 cpu_synchronize_all_post_reset(); 66 resume_all_vcpus(); 67 return 0; 68 } 69 70 int handle_diag_288(CPUS390XState *env, uint64_t r1, uint64_t r3) 71 { 72 uint64_t func = env->regs[r1]; 73 uint64_t timeout = env->regs[r1 + 1]; 74 uint64_t action = env->regs[r3]; 75 Object *obj; 76 DIAG288State *diag288; 77 DIAG288Class *diag288_class; 78 79 if (r1 % 2 || action != 0) { 80 return -1; 81 } 82 83 /* Timeout must be more than 15 seconds except for timer deletion */ 84 if (func != WDT_DIAG288_CANCEL && timeout < 15) { 85 return -1; 86 } 87 88 obj = object_resolve_path_type("", TYPE_WDT_DIAG288, NULL); 89 if (!obj) { 90 return -1; 91 } 92 93 diag288 = DIAG288(obj); 94 diag288_class = DIAG288_GET_CLASS(diag288); 95 return diag288_class->handle_timer(diag288, func, timeout); 96 } 97 98 #define DIAG_308_RC_OK 0x0001 99 #define DIAG_308_RC_NO_CONF 0x0102 100 #define DIAG_308_RC_INVALID 0x0402 101 102 void handle_diag_308(CPUS390XState *env, uint64_t r1, uint64_t r3) 103 { 104 uint64_t addr = env->regs[r1]; 105 uint64_t subcode = env->regs[r3]; 106 IplParameterBlock *iplb; 107 108 if (env->psw.mask & PSW_MASK_PSTATE) { 109 program_interrupt(env, PGM_PRIVILEGED, ILEN_AUTO); 110 return; 111 } 112 113 if ((subcode & ~0x0ffffULL) || (subcode > 6)) { 114 program_interrupt(env, PGM_SPECIFICATION, ILEN_AUTO); 115 return; 116 } 117 118 switch (subcode) { 119 case 0: 120 modified_clear_reset(s390_env_get_cpu(env)); 121 if (tcg_enabled()) { 122 cpu_loop_exit(CPU(s390_env_get_cpu(env))); 123 } 124 break; 125 case 1: 126 load_normal_reset(s390_env_get_cpu(env)); 127 if (tcg_enabled()) { 128 cpu_loop_exit(CPU(s390_env_get_cpu(env))); 129 } 130 break; 131 case 3: 132 s390_reipl_request(); 133 if (tcg_enabled()) { 134 cpu_loop_exit(CPU(s390_env_get_cpu(env))); 135 } 136 break; 137 case 5: 138 if ((r1 & 1) || (addr & 0x0fffULL)) { 139 program_interrupt(env, PGM_SPECIFICATION, ILEN_AUTO); 140 return; 141 } 142 if (!address_space_access_valid(&address_space_memory, addr, 143 sizeof(IplParameterBlock), false)) { 144 program_interrupt(env, PGM_ADDRESSING, ILEN_AUTO); 145 return; 146 } 147 iplb = g_new0(IplParameterBlock, 1); 148 cpu_physical_memory_read(addr, iplb, sizeof(iplb->len)); 149 if (!iplb_valid_len(iplb)) { 150 env->regs[r1 + 1] = DIAG_308_RC_INVALID; 151 goto out; 152 } 153 154 cpu_physical_memory_read(addr, iplb, be32_to_cpu(iplb->len)); 155 156 if (!iplb_valid_ccw(iplb) && !iplb_valid_fcp(iplb)) { 157 env->regs[r1 + 1] = DIAG_308_RC_INVALID; 158 goto out; 159 } 160 161 s390_ipl_update_diag308(iplb); 162 env->regs[r1 + 1] = DIAG_308_RC_OK; 163 out: 164 g_free(iplb); 165 return; 166 case 6: 167 if ((r1 & 1) || (addr & 0x0fffULL)) { 168 program_interrupt(env, PGM_SPECIFICATION, ILEN_AUTO); 169 return; 170 } 171 if (!address_space_access_valid(&address_space_memory, addr, 172 sizeof(IplParameterBlock), true)) { 173 program_interrupt(env, PGM_ADDRESSING, ILEN_AUTO); 174 return; 175 } 176 iplb = s390_ipl_get_iplb(); 177 if (iplb) { 178 cpu_physical_memory_write(addr, iplb, be32_to_cpu(iplb->len)); 179 env->regs[r1 + 1] = DIAG_308_RC_OK; 180 } else { 181 env->regs[r1 + 1] = DIAG_308_RC_NO_CONF; 182 } 183 return; 184 default: 185 hw_error("Unhandled diag308 subcode %" PRIx64, subcode); 186 break; 187 } 188 } 189