X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=target-s390x%2Fhelper.c;h=da33b38009a46cf2c3a03790ab3bee717223961f;hb=8b7acc79b9adb4dda6cc867b90e3a1e873f4f7e8;hp=9a132e6d2c4521f340dd467bf93e411c86d4582f;hpb=177f7fc6884c47666f6c6eeca376a92432ccda38;p=qemu.git diff --git a/target-s390x/helper.c b/target-s390x/helper.c index 9a132e6d2..da33b3800 100644 --- a/target-s390x/helper.c +++ b/target-s390x/helper.c @@ -57,7 +57,7 @@ void s390x_tod_timer(void *opaque) CPUS390XState *env = &cpu->env; env->pending_int |= INTERRUPT_TOD; - cpu_interrupt(env, CPU_INTERRUPT_HARD); + cpu_interrupt(CPU(cpu), CPU_INTERRUPT_HARD); } void s390x_cpu_timer(void *opaque) @@ -66,33 +66,28 @@ void s390x_cpu_timer(void *opaque) CPUS390XState *env = &cpu->env; env->pending_int |= INTERRUPT_CPUTIMER; - cpu_interrupt(env, CPU_INTERRUPT_HARD); + cpu_interrupt(CPU(cpu), CPU_INTERRUPT_HARD); } #endif S390CPU *cpu_s390x_init(const char *cpu_model) { S390CPU *cpu; - CPUS390XState *env; - static int inited; cpu = S390_CPU(object_new(TYPE_S390_CPU)); - env = &cpu->env; - if (tcg_enabled() && !inited) { - inited = 1; - s390x_translate_init(); - } + object_property_set_bool(OBJECT(cpu), true, "realized", NULL); - env->cpu_model_str = cpu_model; - qemu_init_vcpu(env); return cpu; } #if defined(CONFIG_USER_ONLY) -void do_interrupt(CPUS390XState *env) +void s390_cpu_do_interrupt(CPUState *cs) { + S390CPU *cpu = S390_CPU(cs); + CPUS390XState *env = &cpu->env; + env->exception_index = -1; } @@ -387,7 +382,7 @@ int cpu_s390x_handle_mmu_fault(CPUS390XState *env, target_ulong orig_vaddr, int prot; DPRINTF("%s: address 0x%" PRIx64 " rw %d mmu_idx %d\n", - __func__, _vaddr, rw, mmu_idx); + __func__, orig_vaddr, rw, mmu_idx); orig_vaddr &= TARGET_PAGE_MASK; vaddr = orig_vaddr; @@ -404,8 +399,8 @@ int cpu_s390x_handle_mmu_fault(CPUS390XState *env, target_ulong orig_vaddr, /* check out of RAM access */ if (raddr > (ram_size + virtio_size)) { - DPRINTF("%s: aaddr %" PRIx64 " > ram_size %" PRIx64 "\n", __func__, - (uint64_t)aaddr, (uint64_t)ram_size); + DPRINTF("%s: raddr %" PRIx64 " > ram_size %" PRIx64 "\n", __func__, + (uint64_t)raddr, (uint64_t)ram_size); trigger_pgm_exception(env, PGM_ADDRESSING, ILEN_LATER); return 1; } @@ -419,9 +414,10 @@ int cpu_s390x_handle_mmu_fault(CPUS390XState *env, target_ulong orig_vaddr, return 0; } -hwaddr cpu_get_phys_page_debug(CPUS390XState *env, - target_ulong vaddr) +hwaddr s390_cpu_get_phys_page_debug(CPUState *cs, vaddr vaddr) { + S390CPU *cpu = S390_CPU(cs); + CPUS390XState *env = &cpu->env; target_ulong raddr; int prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; int old_exc = env->exception_index; @@ -441,14 +437,16 @@ hwaddr cpu_get_phys_page_debug(CPUS390XState *env, void load_psw(CPUS390XState *env, uint64_t mask, uint64_t addr) { if (mask & PSW_MASK_WAIT) { + S390CPU *cpu = s390_env_get_cpu(env); + CPUState *cs = CPU(cpu); if (!(mask & (PSW_MASK_IO | PSW_MASK_EXT | PSW_MASK_MCHECK))) { - if (s390_del_running_cpu(env) == 0) { + if (s390_del_running_cpu(cpu) == 0) { #ifndef CONFIG_USER_ONLY qemu_system_shutdown_request(); #endif } } - env->halted = 1; + cs->halted = 1; env->exception_index = EXCP_HLT; } @@ -471,13 +469,56 @@ static uint64_t get_psw_mask(CPUS390XState *env) return r; } +static LowCore *cpu_map_lowcore(CPUS390XState *env) +{ + LowCore *lowcore; + hwaddr len = sizeof(LowCore); + + lowcore = cpu_physical_memory_map(env->psa, &len, 1); + + if (len < sizeof(LowCore)) { + cpu_abort(env, "Could not map lowcore\n"); + } + + return lowcore; +} + +static void cpu_unmap_lowcore(LowCore *lowcore) +{ + cpu_physical_memory_unmap(lowcore, sizeof(LowCore), 1, sizeof(LowCore)); +} + +void *s390_cpu_physical_memory_map(CPUS390XState *env, hwaddr addr, hwaddr *len, + int is_write) +{ + hwaddr start = addr; + + /* Mind the prefix area. */ + if (addr < 8192) { + /* Map the lowcore. */ + start += env->psa; + *len = MIN(*len, 8192 - addr); + } else if ((addr >= env->psa) && (addr < env->psa + 8192)) { + /* Map the 0 page. */ + start -= env->psa; + *len = MIN(*len, 8192 - start); + } + + return cpu_physical_memory_map(start, len, is_write); +} + +void s390_cpu_physical_memory_unmap(CPUS390XState *env, void *addr, hwaddr len, + int is_write) +{ + cpu_physical_memory_unmap(addr, len, is_write, len); +} + static void do_svc_interrupt(CPUS390XState *env) { uint64_t mask, addr; LowCore *lowcore; - hwaddr len = TARGET_PAGE_SIZE; - lowcore = cpu_physical_memory_map(env->psa, &len, 1); + lowcore = cpu_map_lowcore(env); lowcore->svc_code = cpu_to_be16(env->int_svc_code); lowcore->svc_ilen = cpu_to_be16(env->int_svc_ilen); @@ -486,7 +527,7 @@ static void do_svc_interrupt(CPUS390XState *env) mask = be64_to_cpu(lowcore->svc_new_psw.mask); addr = be64_to_cpu(lowcore->svc_new_psw.addr); - cpu_physical_memory_unmap(lowcore, len, 1, len); + cpu_unmap_lowcore(lowcore); load_psw(env, mask, addr); } @@ -495,7 +536,6 @@ static void do_program_interrupt(CPUS390XState *env) { uint64_t mask, addr; LowCore *lowcore; - hwaddr len = TARGET_PAGE_SIZE; int ilen = env->int_pgm_ilen; switch (ilen) { @@ -513,7 +553,7 @@ static void do_program_interrupt(CPUS390XState *env) qemu_log_mask(CPU_LOG_INT, "%s: code=0x%x ilen=%d\n", __func__, env->int_pgm_code, ilen); - lowcore = cpu_physical_memory_map(env->psa, &len, 1); + lowcore = cpu_map_lowcore(env); lowcore->pgm_ilen = cpu_to_be16(ilen); lowcore->pgm_code = cpu_to_be16(env->int_pgm_code); @@ -522,7 +562,7 @@ static void do_program_interrupt(CPUS390XState *env) mask = be64_to_cpu(lowcore->program_new_psw.mask); addr = be64_to_cpu(lowcore->program_new_psw.addr); - cpu_physical_memory_unmap(lowcore, len, 1, len); + cpu_unmap_lowcore(lowcore); DPRINTF("%s: %x %x %" PRIx64 " %" PRIx64 "\n", __func__, env->int_pgm_code, ilen, env->psw.mask, @@ -537,7 +577,6 @@ static void do_ext_interrupt(CPUS390XState *env) { uint64_t mask, addr; LowCore *lowcore; - hwaddr len = TARGET_PAGE_SIZE; ExtQueue *q; if (!(env->psw.mask & PSW_MASK_EXT)) { @@ -549,7 +588,7 @@ static void do_ext_interrupt(CPUS390XState *env) } q = &env->ext_queue[env->ext_index]; - lowcore = cpu_physical_memory_map(env->psa, &len, 1); + lowcore = cpu_map_lowcore(env); lowcore->ext_int_code = cpu_to_be16(q->code); lowcore->ext_params = cpu_to_be32(q->param); @@ -560,7 +599,7 @@ static void do_ext_interrupt(CPUS390XState *env) mask = be64_to_cpu(lowcore->external_new_psw.mask); addr = be64_to_cpu(lowcore->external_new_psw.addr); - cpu_physical_memory_unmap(lowcore, len, 1, len); + cpu_unmap_lowcore(lowcore); env->ext_index--; if (env->ext_index == -1) { @@ -573,12 +612,148 @@ static void do_ext_interrupt(CPUS390XState *env) load_psw(env, mask, addr); } -void do_interrupt(CPUS390XState *env) +static void do_io_interrupt(CPUS390XState *env) +{ + LowCore *lowcore; + IOIntQueue *q; + uint8_t isc; + int disable = 1; + int found = 0; + + if (!(env->psw.mask & PSW_MASK_IO)) { + cpu_abort(env, "I/O int w/o I/O mask\n"); + } + + for (isc = 0; isc < ARRAY_SIZE(env->io_index); isc++) { + uint64_t isc_bits; + + if (env->io_index[isc] < 0) { + continue; + } + if (env->io_index[isc] > MAX_IO_QUEUE) { + cpu_abort(env, "I/O queue overrun for isc %d: %d\n", + isc, env->io_index[isc]); + } + + q = &env->io_queue[env->io_index[isc]][isc]; + isc_bits = ISC_TO_ISC_BITS(IO_INT_WORD_ISC(q->word)); + if (!(env->cregs[6] & isc_bits)) { + disable = 0; + continue; + } + if (!found) { + uint64_t mask, addr; + + found = 1; + lowcore = cpu_map_lowcore(env); + + lowcore->subchannel_id = cpu_to_be16(q->id); + lowcore->subchannel_nr = cpu_to_be16(q->nr); + lowcore->io_int_parm = cpu_to_be32(q->parm); + lowcore->io_int_word = cpu_to_be32(q->word); + lowcore->io_old_psw.mask = cpu_to_be64(get_psw_mask(env)); + lowcore->io_old_psw.addr = cpu_to_be64(env->psw.addr); + mask = be64_to_cpu(lowcore->io_new_psw.mask); + addr = be64_to_cpu(lowcore->io_new_psw.addr); + + cpu_unmap_lowcore(lowcore); + + env->io_index[isc]--; + + DPRINTF("%s: %" PRIx64 " %" PRIx64 "\n", __func__, + env->psw.mask, env->psw.addr); + load_psw(env, mask, addr); + } + if (env->io_index[isc] >= 0) { + disable = 0; + } + continue; + } + + if (disable) { + env->pending_int &= ~INTERRUPT_IO; + } + +} + +static void do_mchk_interrupt(CPUS390XState *env) +{ + uint64_t mask, addr; + LowCore *lowcore; + MchkQueue *q; + int i; + + if (!(env->psw.mask & PSW_MASK_MCHECK)) { + cpu_abort(env, "Machine check w/o mchk mask\n"); + } + + if (env->mchk_index < 0 || env->mchk_index > MAX_MCHK_QUEUE) { + cpu_abort(env, "Mchk queue overrun: %d\n", env->mchk_index); + } + + q = &env->mchk_queue[env->mchk_index]; + + if (q->type != 1) { + /* Don't know how to handle this... */ + cpu_abort(env, "Unknown machine check type %d\n", q->type); + } + if (!(env->cregs[14] & (1 << 28))) { + /* CRW machine checks disabled */ + return; + } + + lowcore = cpu_map_lowcore(env); + + for (i = 0; i < 16; i++) { + lowcore->floating_pt_save_area[i] = cpu_to_be64(env->fregs[i].ll); + lowcore->gpregs_save_area[i] = cpu_to_be64(env->regs[i]); + lowcore->access_regs_save_area[i] = cpu_to_be32(env->aregs[i]); + lowcore->cregs_save_area[i] = cpu_to_be64(env->cregs[i]); + } + lowcore->prefixreg_save_area = cpu_to_be32(env->psa); + lowcore->fpt_creg_save_area = cpu_to_be32(env->fpc); + lowcore->tod_progreg_save_area = cpu_to_be32(env->todpr); + lowcore->cpu_timer_save_area[0] = cpu_to_be32(env->cputm >> 32); + lowcore->cpu_timer_save_area[1] = cpu_to_be32((uint32_t)env->cputm); + lowcore->clock_comp_save_area[0] = cpu_to_be32(env->ckc >> 32); + lowcore->clock_comp_save_area[1] = cpu_to_be32((uint32_t)env->ckc); + + lowcore->mcck_interruption_code[0] = cpu_to_be32(0x00400f1d); + lowcore->mcck_interruption_code[1] = cpu_to_be32(0x40330000); + lowcore->mcck_old_psw.mask = cpu_to_be64(get_psw_mask(env)); + lowcore->mcck_old_psw.addr = cpu_to_be64(env->psw.addr); + mask = be64_to_cpu(lowcore->mcck_new_psw.mask); + addr = be64_to_cpu(lowcore->mcck_new_psw.addr); + + cpu_unmap_lowcore(lowcore); + + env->mchk_index--; + if (env->mchk_index == -1) { + env->pending_int &= ~INTERRUPT_MCHK; + } + + DPRINTF("%s: %" PRIx64 " %" PRIx64 "\n", __func__, + env->psw.mask, env->psw.addr); + + load_psw(env, mask, addr); +} + +void s390_cpu_do_interrupt(CPUState *cs) { + S390CPU *cpu = S390_CPU(cs); + CPUS390XState *env = &cpu->env; + qemu_log_mask(CPU_LOG_INT, "%s: %d at pc=%" PRIx64 "\n", __func__, env->exception_index, env->psw.addr); - s390_add_running_cpu(env); + s390_add_running_cpu(cpu); + /* handle machine checks */ + if ((env->psw.mask & PSW_MASK_MCHECK) && + (env->exception_index == -1)) { + if (env->pending_int & INTERRUPT_MCHK) { + env->exception_index = EXCP_MCHK; + } + } /* handle external interrupts */ if ((env->psw.mask & PSW_MASK_EXT) && env->exception_index == -1) { @@ -586,17 +761,24 @@ void do_interrupt(CPUS390XState *env) /* code is already in env */ env->exception_index = EXCP_EXT; } else if (env->pending_int & INTERRUPT_TOD) { - cpu_inject_ext(env, 0x1004, 0, 0); + cpu_inject_ext(cpu, 0x1004, 0, 0); env->exception_index = EXCP_EXT; env->pending_int &= ~INTERRUPT_EXT; env->pending_int &= ~INTERRUPT_TOD; } else if (env->pending_int & INTERRUPT_CPUTIMER) { - cpu_inject_ext(env, 0x1005, 0, 0); + cpu_inject_ext(cpu, 0x1005, 0, 0); env->exception_index = EXCP_EXT; env->pending_int &= ~INTERRUPT_EXT; env->pending_int &= ~INTERRUPT_TOD; } } + /* handle I/O interrupts */ + if ((env->psw.mask & PSW_MASK_IO) && + (env->exception_index == -1)) { + if (env->pending_int & INTERRUPT_IO) { + env->exception_index = EXCP_IO; + } + } switch (env->exception_index) { case EXCP_PGM: @@ -608,11 +790,17 @@ void do_interrupt(CPUS390XState *env) case EXCP_EXT: do_ext_interrupt(env); break; + case EXCP_IO: + do_io_interrupt(env); + break; + case EXCP_MCHK: + do_mchk_interrupt(env); + break; } env->exception_index = -1; if (!env->pending_int) { - env->interrupt_request &= ~CPU_INTERRUPT_HARD; + cs->interrupt_request &= ~CPU_INTERRUPT_HARD; } }