diff options
Diffstat (limited to 'target-s390x')
-rw-r--r-- | target-s390x/cpu.h | 3 | ||||
-rw-r--r-- | target-s390x/ioinst.c | 33 |
2 files changed, 21 insertions, 15 deletions
diff --git a/target-s390x/cpu.h b/target-s390x/cpu.h index 7180d68429..7c07ca6304 100644 --- a/target-s390x/cpu.h +++ b/target-s390x/cpu.h @@ -455,7 +455,8 @@ int css_do_xsch(SubchDev *sch); int css_do_csch(SubchDev *sch); int css_do_hsch(SubchDev *sch); int css_do_ssch(SubchDev *sch, ORB *orb); -int css_do_tsch(SubchDev *sch, IRB *irb); +int css_do_tsch_get_irb(SubchDev *sch, IRB *irb, int *irb_len); +void css_do_tsch_update_subch(SubchDev *sch); int css_do_stcrw(CRW *crw); int css_do_tpi(IOIntCode *int_code, int lowcore); int css_collect_chp_desc(int m, uint8_t cssid, uint8_t f_chpid, uint8_t l_chpid, diff --git a/target-s390x/ioinst.c b/target-s390x/ioinst.c index 8052886276..aa55ca8870 100644 --- a/target-s390x/ioinst.c +++ b/target-s390x/ioinst.c @@ -341,10 +341,9 @@ int ioinst_handle_tsch(S390CPU *cpu, uint64_t reg1, uint32_t ipb) CPUS390XState *env = &cpu->env; int cssid, ssid, schid, m; SubchDev *sch; - IRB *irb; + IRB irb; uint64_t addr; - int cc; - hwaddr len = sizeof(*irb); + int cc, irb_len; if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) { program_interrupt(env, PGM_OPERAND, 2); @@ -356,23 +355,29 @@ int ioinst_handle_tsch(S390CPU *cpu, uint64_t reg1, uint32_t ipb) program_interrupt(env, PGM_SPECIFICATION, 2); return -EIO; } - irb = s390_cpu_physical_memory_map(env, addr, &len, 1); - if (!irb || len != sizeof(*irb)) { - program_interrupt(env, PGM_ADDRESSING, 2); - cc = -EIO; - goto out; - } + sch = css_find_subch(m, cssid, ssid, schid); if (sch && css_subch_visible(sch)) { - cc = css_do_tsch(sch, irb); - /* 0 - status pending, 1 - not status pending */ + cc = css_do_tsch_get_irb(sch, &irb, &irb_len); } else { cc = 3; } + /* 0 - status pending, 1 - not status pending, 3 - not operational */ + if (cc != 3) { + if (s390_cpu_virt_mem_write(cpu, addr, &irb, irb_len) != 0) { + return -EFAULT; + } + css_do_tsch_update_subch(sch); + } else { + irb_len = sizeof(irb) - sizeof(irb.emw); + /* Access exceptions have a higher priority than cc3 */ + if (s390_cpu_virt_mem_check_write(cpu, addr, irb_len) != 0) { + return -EFAULT; + } + } + setcc(cpu, cc); -out: - s390_cpu_physical_memory_unmap(env, irb, sizeof(*irb), 1); - return cc; + return 0; } typedef struct ChscReq { |