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