13d672205SThomas Huth /* 23d672205SThomas Huth * S390x DIAG instruction helper functions 33d672205SThomas Huth * 43d672205SThomas Huth * This program is free software; you can redistribute it and/or modify 53d672205SThomas Huth * it under the terms of the GNU General Public License as published by 63d672205SThomas Huth * the Free Software Foundation; either version 2 of the License, or 73d672205SThomas Huth * (at your option) any later version. 83d672205SThomas Huth * 93d672205SThomas Huth * This program is distributed in the hope that it will be useful, 103d672205SThomas Huth * but WITHOUT ANY WARRANTY; without even the implied warranty of 113d672205SThomas Huth * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 123d672205SThomas Huth * GNU General Public License for more details. 133d672205SThomas Huth */ 143d672205SThomas Huth 153d672205SThomas Huth #include "qemu/osdep.h" 163d672205SThomas Huth #include "cpu.h" 174e58b838SDavid Hildenbrand #include "internal.h" 183d672205SThomas Huth #include "exec/address-spaces.h" 193d672205SThomas Huth #include "exec/exec-all.h" 203d672205SThomas Huth #include "hw/watchdog/wdt_diag288.h" 213d672205SThomas Huth #include "sysemu/cpus.h" 223d672205SThomas Huth #include "hw/s390x/ipl.h" 2319c69829SDavid Hildenbrand #include "hw/s390x/s390-virtio-ccw.h" 243d672205SThomas Huth 253d672205SThomas Huth int handle_diag_288(CPUS390XState *env, uint64_t r1, uint64_t r3) 263d672205SThomas Huth { 273d672205SThomas Huth uint64_t func = env->regs[r1]; 283d672205SThomas Huth uint64_t timeout = env->regs[r1 + 1]; 293d672205SThomas Huth uint64_t action = env->regs[r3]; 303d672205SThomas Huth Object *obj; 313d672205SThomas Huth DIAG288State *diag288; 323d672205SThomas Huth DIAG288Class *diag288_class; 333d672205SThomas Huth 343d672205SThomas Huth if (r1 % 2 || action != 0) { 353d672205SThomas Huth return -1; 363d672205SThomas Huth } 373d672205SThomas Huth 383d672205SThomas Huth /* Timeout must be more than 15 seconds except for timer deletion */ 393d672205SThomas Huth if (func != WDT_DIAG288_CANCEL && timeout < 15) { 403d672205SThomas Huth return -1; 413d672205SThomas Huth } 423d672205SThomas Huth 433d672205SThomas Huth obj = object_resolve_path_type("", TYPE_WDT_DIAG288, NULL); 443d672205SThomas Huth if (!obj) { 453d672205SThomas Huth return -1; 463d672205SThomas Huth } 473d672205SThomas Huth 483d672205SThomas Huth diag288 = DIAG288(obj); 493d672205SThomas Huth diag288_class = DIAG288_GET_CLASS(diag288); 503d672205SThomas Huth return diag288_class->handle_timer(diag288, func, timeout); 513d672205SThomas Huth } 523d672205SThomas Huth 533d672205SThomas Huth #define DIAG_308_RC_OK 0x0001 543d672205SThomas Huth #define DIAG_308_RC_NO_CONF 0x0102 553d672205SThomas Huth #define DIAG_308_RC_INVALID 0x0402 563d672205SThomas Huth 57968db419SDavid Hildenbrand void handle_diag_308(CPUS390XState *env, uint64_t r1, uint64_t r3, uintptr_t ra) 583d672205SThomas Huth { 59*a30fb811SDavid Hildenbrand CPUState *cs = CPU(s390_env_get_cpu(env)); 603d672205SThomas Huth uint64_t addr = env->regs[r1]; 613d672205SThomas Huth uint64_t subcode = env->regs[r3]; 623d672205SThomas Huth IplParameterBlock *iplb; 633d672205SThomas Huth 643d672205SThomas Huth if (env->psw.mask & PSW_MASK_PSTATE) { 65968db419SDavid Hildenbrand s390_program_interrupt(env, PGM_PRIVILEGED, ILEN_AUTO, ra); 663d672205SThomas Huth return; 673d672205SThomas Huth } 683d672205SThomas Huth 693d672205SThomas Huth if ((subcode & ~0x0ffffULL) || (subcode > 6)) { 70968db419SDavid Hildenbrand s390_program_interrupt(env, PGM_SPECIFICATION, ILEN_AUTO, ra); 713d672205SThomas Huth return; 723d672205SThomas Huth } 733d672205SThomas Huth 743d672205SThomas Huth switch (subcode) { 753d672205SThomas Huth case 0: 76*a30fb811SDavid Hildenbrand s390_ipl_reset_request(cs, S390_RESET_MODIFIED_CLEAR); 773d672205SThomas Huth break; 783d672205SThomas Huth case 1: 79*a30fb811SDavid Hildenbrand s390_ipl_reset_request(cs, S390_RESET_LOAD_NORMAL); 803d672205SThomas Huth break; 813d672205SThomas Huth case 3: 82*a30fb811SDavid Hildenbrand s390_ipl_reset_request(cs, S390_RESET_REIPL); 833d672205SThomas Huth break; 843d672205SThomas Huth case 5: 853d672205SThomas Huth if ((r1 & 1) || (addr & 0x0fffULL)) { 86968db419SDavid Hildenbrand s390_program_interrupt(env, PGM_SPECIFICATION, ILEN_AUTO, ra); 873d672205SThomas Huth return; 883d672205SThomas Huth } 893d672205SThomas Huth if (!address_space_access_valid(&address_space_memory, addr, 903d672205SThomas Huth sizeof(IplParameterBlock), false)) { 91968db419SDavid Hildenbrand s390_program_interrupt(env, PGM_ADDRESSING, ILEN_AUTO, ra); 923d672205SThomas Huth return; 933d672205SThomas Huth } 9496f64aa8SMarc-André Lureau iplb = g_new0(IplParameterBlock, 1); 953d672205SThomas Huth cpu_physical_memory_read(addr, iplb, sizeof(iplb->len)); 963d672205SThomas Huth if (!iplb_valid_len(iplb)) { 973d672205SThomas Huth env->regs[r1 + 1] = DIAG_308_RC_INVALID; 983d672205SThomas Huth goto out; 993d672205SThomas Huth } 1003d672205SThomas Huth 1013d672205SThomas Huth cpu_physical_memory_read(addr, iplb, be32_to_cpu(iplb->len)); 1023d672205SThomas Huth 1033d672205SThomas Huth if (!iplb_valid_ccw(iplb) && !iplb_valid_fcp(iplb)) { 1043d672205SThomas Huth env->regs[r1 + 1] = DIAG_308_RC_INVALID; 1053d672205SThomas Huth goto out; 1063d672205SThomas Huth } 1073d672205SThomas Huth 1083d672205SThomas Huth s390_ipl_update_diag308(iplb); 1093d672205SThomas Huth env->regs[r1 + 1] = DIAG_308_RC_OK; 1103d672205SThomas Huth out: 1113d672205SThomas Huth g_free(iplb); 1123d672205SThomas Huth return; 1133d672205SThomas Huth case 6: 1143d672205SThomas Huth if ((r1 & 1) || (addr & 0x0fffULL)) { 115968db419SDavid Hildenbrand s390_program_interrupt(env, PGM_SPECIFICATION, ILEN_AUTO, ra); 1163d672205SThomas Huth return; 1173d672205SThomas Huth } 1183d672205SThomas Huth if (!address_space_access_valid(&address_space_memory, addr, 1193d672205SThomas Huth sizeof(IplParameterBlock), true)) { 120968db419SDavid Hildenbrand s390_program_interrupt(env, PGM_ADDRESSING, ILEN_AUTO, ra); 1213d672205SThomas Huth return; 1223d672205SThomas Huth } 1233d672205SThomas Huth iplb = s390_ipl_get_iplb(); 1243d672205SThomas Huth if (iplb) { 1253d672205SThomas Huth cpu_physical_memory_write(addr, iplb, be32_to_cpu(iplb->len)); 1263d672205SThomas Huth env->regs[r1 + 1] = DIAG_308_RC_OK; 1273d672205SThomas Huth } else { 1283d672205SThomas Huth env->regs[r1 + 1] = DIAG_308_RC_NO_CONF; 1293d672205SThomas Huth } 1303d672205SThomas Huth return; 1313d672205SThomas Huth default: 1323d672205SThomas Huth hw_error("Unhandled diag308 subcode %" PRIx64, subcode); 1333d672205SThomas Huth break; 1343d672205SThomas Huth } 1353d672205SThomas Huth } 136