diff --git a/cpus.c b/cpus.c index e75895a458..326742f445 100644 --- a/cpus.c +++ b/cpus.c @@ -972,6 +972,18 @@ void async_run_on_cpu(CPUState *cpu, void (*func)(void *data), void *data) qemu_cpu_kick(cpu); } +static void qemu_kvm_destroy_vcpu(CPUState *cpu) +{ + if (kvm_destroy_vcpu(cpu) < 0) { + error_report("kvm_destroy_vcpu failed"); + exit(EXIT_FAILURE); + } +} + +static void qemu_tcg_destroy_vcpu(CPUState *cpu) +{ +} + static void flush_queued_work(CPUState *cpu) { struct qemu_work_item *wi; @@ -1061,7 +1073,7 @@ static void *qemu_kvm_cpu_thread_fn(void *arg) cpu->created = true; qemu_cond_signal(&qemu_cpu_cond); - while (1) { + do { if (cpu_can_run(cpu)) { r = kvm_cpu_exec(cpu); if (r == EXCP_DEBUG) { @@ -1069,8 +1081,12 @@ static void *qemu_kvm_cpu_thread_fn(void *arg) } } qemu_kvm_wait_io_event(cpu); - } + } while (!cpu->unplug || cpu_can_run(cpu)); + qemu_kvm_destroy_vcpu(cpu); + cpu->created = false; + qemu_cond_signal(&qemu_cpu_cond); + qemu_mutex_unlock_iothread(); return NULL; } @@ -1124,6 +1140,7 @@ static void tcg_exec_all(void); static void *qemu_tcg_cpu_thread_fn(void *arg) { CPUState *cpu = arg; + CPUState *remove_cpu = NULL; rcu_register_thread(); @@ -1161,6 +1178,18 @@ static void *qemu_tcg_cpu_thread_fn(void *arg) } } qemu_tcg_wait_io_event(QTAILQ_FIRST(&cpus)); + CPU_FOREACH(cpu) { + if (cpu->unplug && !cpu_can_run(cpu)) { + remove_cpu = cpu; + break; + } + } + if (remove_cpu) { + qemu_tcg_destroy_vcpu(remove_cpu); + cpu->created = false; + qemu_cond_signal(&qemu_cpu_cond); + remove_cpu = NULL; + } } return NULL; @@ -1317,6 +1346,21 @@ void resume_all_vcpus(void) } } +void cpu_remove(CPUState *cpu) +{ + cpu->stop = true; + cpu->unplug = true; + qemu_cpu_kick(cpu); +} + +void cpu_remove_sync(CPUState *cpu) +{ + cpu_remove(cpu); + while (cpu->created) { + qemu_cond_wait(&qemu_cpu_cond, &qemu_global_mutex); + } +} + /* For temporary buffers for forming a name */ #define VCPU_THREAD_NAME_SIZE 16 @@ -1533,6 +1577,9 @@ static void tcg_exec_all(void) break; } } else if (cpu->stop || cpu->stopped) { + if (cpu->unplug) { + next_cpu = CPU_NEXT(cpu); + } break; } } diff --git a/exec.c b/exec.c index 448882154f..f2c9e374f5 100644 --- a/exec.c +++ b/exec.c @@ -57,6 +57,8 @@ #include "exec/ram_addr.h" #include "exec/log.h" +#include "migration/vmstate.h" + #include "qemu/range.h" #ifndef _WIN32 #include "qemu/mmap-alloc.h" @@ -612,15 +614,9 @@ static int cpu_get_free_index(Error **errp) return cpu; } -void cpu_exec_exit(CPUState *cpu) +static void cpu_release_index(CPUState *cpu) { - if (cpu->cpu_index == -1) { - /* cpu_index was never allocated by this @cpu or was already freed. */ - return; - } - bitmap_clear(cpu_index_map, cpu->cpu_index, 1); - cpu->cpu_index = -1; } #else @@ -635,11 +631,42 @@ static int cpu_get_free_index(Error **errp) return cpu_index; } -void cpu_exec_exit(CPUState *cpu) +static void cpu_release_index(CPUState *cpu) { + return; } #endif +void cpu_exec_exit(CPUState *cpu) +{ + CPUClass *cc = CPU_GET_CLASS(cpu); + +#if defined(CONFIG_USER_ONLY) + cpu_list_lock(); +#endif + if (cpu->cpu_index == -1) { + /* cpu_index was never allocated by this @cpu or was already freed. */ +#if defined(CONFIG_USER_ONLY) + cpu_list_unlock(); +#endif + return; + } + + QTAILQ_REMOVE(&cpus, cpu, node); + cpu_release_index(cpu); + cpu->cpu_index = -1; +#if defined(CONFIG_USER_ONLY) + cpu_list_unlock(); +#endif + + if (cc->vmsd != NULL) { + vmstate_unregister(NULL, cc->vmsd, cpu); + } + if (qdev_get_vmsd(DEVICE(cpu)) == NULL) { + vmstate_unregister(NULL, &vmstate_cpu_common, cpu); + } +} + void cpu_exec_init(CPUState *cpu, Error **errp) { CPUClass *cc = CPU_GET_CLASS(cpu); diff --git a/hw/ppc/spapr_hcall.c b/hw/ppc/spapr_hcall.c index feb3629664..9a3f4ecc1e 100644 --- a/hw/ppc/spapr_hcall.c +++ b/hw/ppc/spapr_hcall.c @@ -186,6 +186,7 @@ static RemoveResult remove_hpte(PowerPCCPU *cpu, target_ulong ptex, static target_ulong h_remove(PowerPCCPU *cpu, sPAPRMachineState *spapr, target_ulong opcode, target_ulong *args) { + CPUPPCState *env = &cpu->env; target_ulong flags = args[0]; target_ulong pte_index = args[1]; target_ulong avpn = args[2]; @@ -196,6 +197,7 @@ static target_ulong h_remove(PowerPCCPU *cpu, sPAPRMachineState *spapr, switch (ret) { case REMOVE_SUCCESS: + check_tlb_flush(env); return H_SUCCESS; case REMOVE_NOT_FOUND: @@ -232,7 +234,9 @@ static target_ulong h_remove(PowerPCCPU *cpu, sPAPRMachineState *spapr, static target_ulong h_bulk_remove(PowerPCCPU *cpu, sPAPRMachineState *spapr, target_ulong opcode, target_ulong *args) { + CPUPPCState *env = &cpu->env; int i; + target_ulong rc = H_SUCCESS; for (i = 0; i < H_BULK_REMOVE_MAX_BATCH; i++) { target_ulong *tsh = &args[i*2]; @@ -265,14 +269,18 @@ static target_ulong h_bulk_remove(PowerPCCPU *cpu, sPAPRMachineState *spapr, break; case REMOVE_PARM: - return H_PARAMETER; + rc = H_PARAMETER; + goto exit; case REMOVE_HW: - return H_HARDWARE; + rc = H_HARDWARE; + goto exit; } } + exit: + check_tlb_flush(env); - return H_SUCCESS; + return rc; } static target_ulong h_protect(PowerPCCPU *cpu, sPAPRMachineState *spapr, diff --git a/include/qom/cpu.h b/include/qom/cpu.h index c9ba16ca82..32f3af3e1c 100644 --- a/include/qom/cpu.h +++ b/include/qom/cpu.h @@ -244,6 +244,7 @@ struct qemu_work_item { * @halted: Nonzero if the CPU is in suspended state. * @stop: Indicates a pending stop request. * @stopped: Indicates the CPU has been artificially stopped. + * @unplug: Indicates a pending CPU unplug request. * @crash_occurred: Indicates the OS reported a crash (panic) for this CPU * @tcg_exit_req: Set to force TCG to stop executing linked TBs for this * CPU and return to its top level loop. @@ -296,6 +297,7 @@ struct CPUState { bool created; bool stop; bool stopped; + bool unplug; bool crash_occurred; bool exit_request; bool tb_flushed; @@ -762,6 +764,22 @@ void cpu_exit(CPUState *cpu); */ void cpu_resume(CPUState *cpu); +/** + * cpu_remove: + * @cpu: The CPU to remove. + * + * Requests the CPU to be removed. + */ +void cpu_remove(CPUState *cpu); + + /** + * cpu_remove_sync: + * @cpu: The CPU to remove. + * + * Requests the CPU to be removed and waits till it is removed. + */ +void cpu_remove_sync(CPUState *cpu); + /** * qemu_init_vcpu: * @cpu: The vCPU to initialize. diff --git a/include/sysemu/kvm.h b/include/sysemu/kvm.h index f357ccde91..65569ed438 100644 --- a/include/sysemu/kvm.h +++ b/include/sysemu/kvm.h @@ -216,6 +216,7 @@ int kvm_has_intx_set_mask(void); int kvm_init_vcpu(CPUState *cpu); int kvm_cpu_exec(CPUState *cpu); +int kvm_destroy_vcpu(CPUState *cpu); #ifdef NEED_CPU_H #include "cpu.h" diff --git a/kvm-all.c b/kvm-all.c index e56f385278..d317dcb33e 100644 --- a/kvm-all.c +++ b/kvm-all.c @@ -61,6 +61,12 @@ #define KVM_MSI_HASHTAB_SIZE 256 +struct KVMParkedVcpu { + unsigned long vcpu_id; + int kvm_fd; + QLIST_ENTRY(KVMParkedVcpu) node; +}; + struct KVMState { AccelState parent_obj; @@ -94,6 +100,7 @@ struct KVMState QTAILQ_HEAD(msi_hashtab, KVMMSIRoute) msi_hashtab[KVM_MSI_HASHTAB_SIZE]; #endif KVMMemoryListener memory_listener; + QLIST_HEAD(, KVMParkedVcpu) kvm_parked_vcpus; }; KVMState *kvm_state; @@ -237,6 +244,53 @@ static int kvm_set_user_memory_region(KVMMemoryListener *kml, KVMSlot *slot) return kvm_vm_ioctl(s, KVM_SET_USER_MEMORY_REGION, &mem); } +int kvm_destroy_vcpu(CPUState *cpu) +{ + KVMState *s = kvm_state; + long mmap_size; + struct KVMParkedVcpu *vcpu = NULL; + int ret = 0; + + DPRINTF("kvm_destroy_vcpu\n"); + + mmap_size = kvm_ioctl(s, KVM_GET_VCPU_MMAP_SIZE, 0); + if (mmap_size < 0) { + ret = mmap_size; + DPRINTF("KVM_GET_VCPU_MMAP_SIZE failed\n"); + goto err; + } + + ret = munmap(cpu->kvm_run, mmap_size); + if (ret < 0) { + goto err; + } + + vcpu = g_malloc0(sizeof(*vcpu)); + vcpu->vcpu_id = kvm_arch_vcpu_id(cpu); + vcpu->kvm_fd = cpu->kvm_fd; + QLIST_INSERT_HEAD(&kvm_state->kvm_parked_vcpus, vcpu, node); +err: + return ret; +} + +static int kvm_get_vcpu(KVMState *s, unsigned long vcpu_id) +{ + struct KVMParkedVcpu *cpu; + + QLIST_FOREACH(cpu, &s->kvm_parked_vcpus, node) { + if (cpu->vcpu_id == vcpu_id) { + int kvm_fd; + + QLIST_REMOVE(cpu, node); + kvm_fd = cpu->kvm_fd; + g_free(cpu); + return kvm_fd; + } + } + + return kvm_vm_ioctl(s, KVM_CREATE_VCPU, (void *)vcpu_id); +} + int kvm_init_vcpu(CPUState *cpu) { KVMState *s = kvm_state; @@ -245,7 +299,7 @@ int kvm_init_vcpu(CPUState *cpu) DPRINTF("kvm_init_vcpu\n"); - ret = kvm_vm_ioctl(s, KVM_CREATE_VCPU, (void *)kvm_arch_vcpu_id(cpu)); + ret = kvm_get_vcpu(s, kvm_arch_vcpu_id(cpu)); if (ret < 0) { DPRINTF("kvm_create_vcpu failed\n"); goto err; @@ -1501,6 +1555,7 @@ static int kvm_init(MachineState *ms) #ifdef KVM_CAP_SET_GUEST_DEBUG QTAILQ_INIT(&s->kvm_sw_breakpoints); #endif + QLIST_INIT(&s->kvm_parked_vcpus); s->vmfd = -1; s->fd = qemu_open("/dev/kvm", O_RDWR); if (s->fd == -1) { diff --git a/kvm-stub.c b/kvm-stub.c index 63735a872a..07c09d1141 100644 --- a/kvm-stub.c +++ b/kvm-stub.c @@ -32,6 +32,11 @@ bool kvm_allowed; bool kvm_readonly_mem_allowed; bool kvm_ioeventfd_any_length_allowed; +int kvm_destroy_vcpu(CPUState *cpu) +{ + return -ENOSYS; +} + int kvm_init_vcpu(CPUState *cpu) { return -ENOSYS; diff --git a/target-ppc/cpu.h b/target-ppc/cpu.h index cd33539d1c..98a24a50f3 100644 --- a/target-ppc/cpu.h +++ b/target-ppc/cpu.h @@ -359,6 +359,8 @@ struct ppc_slb_t { #define MSR_EP 6 /* Exception prefix on 601 */ #define MSR_IR 5 /* Instruction relocate */ #define MSR_DR 4 /* Data relocate */ +#define MSR_IS 5 /* Instruction address space (BookE) */ +#define MSR_DS 4 /* Data address space (BookE) */ #define MSR_PE 3 /* Protection enable on 403 */ #define MSR_PX 2 /* Protection exclusive on 403 x */ #define MSR_PMM 2 /* Performance monitor mark on POWER x */ @@ -410,6 +412,8 @@ struct ppc_slb_t { #define msr_ep ((env->msr >> MSR_EP) & 1) #define msr_ir ((env->msr >> MSR_IR) & 1) #define msr_dr ((env->msr >> MSR_DR) & 1) +#define msr_is ((env->msr >> MSR_IS) & 1) +#define msr_ds ((env->msr >> MSR_DS) & 1) #define msr_pe ((env->msr >> MSR_PE) & 1) #define msr_px ((env->msr >> MSR_PX) & 1) #define msr_pmm ((env->msr >> MSR_PMM) & 1) @@ -889,7 +893,7 @@ struct ppc_segment_page_sizes { /*****************************************************************************/ /* The whole PowerPC CPU context */ -#define NB_MMU_MODES 3 +#define NB_MMU_MODES 8 #define PPC_CPU_OPCODES_LEN 0x40 #define PPC_CPU_INDIRECT_OPCODES_LEN 0x20 @@ -954,6 +958,8 @@ struct CPUPPCState { /* PowerPC 64 SLB area */ ppc_slb_t slb[MAX_SLB_ENTRIES]; int32_t slb_nr; + /* tcg TLB needs flush (deferred slb inval instruction typically) */ + uint32_t tlb_need_flush; #endif /* segment registers */ hwaddr htab_base; @@ -1053,7 +1059,8 @@ struct CPUPPCState { /* Those resources are used only in QEMU core */ target_ulong hflags; /* hflags is a MSR & HFLAGS_MASK */ target_ulong hflags_nmsr; /* specific hflags, not coming from MSR */ - int mmu_idx; /* precomputed MMU index to speed up mem accesses */ + int immu_idx; /* precomputed MMU index to speed up insn access */ + int dmmu_idx; /* precomputed MMU index to speed up data accesses */ /* Power management */ int (*check_pow)(CPUPPCState *env); @@ -1242,13 +1249,10 @@ int ppc_dcr_write (ppc_dcr_t *dcr_env, int dcrn, uint32_t val); #define cpu_list ppc_cpu_list /* MMU modes definitions */ -#define MMU_MODE0_SUFFIX _user -#define MMU_MODE1_SUFFIX _kernel -#define MMU_MODE2_SUFFIX _hypv #define MMU_USER_IDX 0 static inline int cpu_mmu_index (CPUPPCState *env, bool ifetch) { - return env->mmu_idx; + return ifetch ? env->immu_idx : env->dmmu_idx; } #include "exec/cpu-all.h" diff --git a/target-ppc/excp_helper.c b/target-ppc/excp_helper.c index 288903ee1d..a37009eb25 100644 --- a/target-ppc/excp_helper.c +++ b/target-ppc/excp_helper.c @@ -646,9 +646,6 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp) if (env->spr[SPR_LPCR] & LPCR_AIL) { new_msr |= (1 << MSR_IR) | (1 << MSR_DR); - } else if (msr & ((1 << MSR_IR) | (1 << MSR_DR))) { - /* If we disactivated any translation, flush TLBs */ - tlb_flush(cs, 1); } #ifdef TARGET_PPC64 @@ -722,13 +719,10 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp) cs->exception_index = POWERPC_EXCP_NONE; env->error_code = 0; - if ((env->mmu_model == POWERPC_MMU_BOOKE) || - (env->mmu_model == POWERPC_MMU_BOOKE206)) { - /* XXX: The BookE changes address space when switching modes, - we should probably implement that as different MMU indexes, - but for the moment we do it the slow way and flush all. */ - tlb_flush(cs, 1); - } + /* Any interrupt is context synchronizing, check if TCG TLB + * needs a delayed flush on ppc64 + */ + check_tlb_flush(env); } void ppc_cpu_do_interrupt(CPUState *cs) @@ -954,6 +948,9 @@ static inline void do_rfi(CPUPPCState *env, target_ulong nip, target_ulong msr, * as rfi is always the last insn of a TB */ cs->interrupt_request |= CPU_INTERRUPT_EXITTB; + + /* Context synchronizing: check if TCG TLB needs flush */ + check_tlb_flush(env); } void helper_rfi(CPUPPCState *env) diff --git a/target-ppc/helper.h b/target-ppc/helper.h index e5a8f7b9b5..0526322f4d 100644 --- a/target-ppc/helper.h +++ b/target-ppc/helper.h @@ -16,6 +16,7 @@ DEF_HELPER_1(rfmci, void, env) DEF_HELPER_1(rfid, void, env) DEF_HELPER_1(hrfid, void, env) #endif +DEF_HELPER_1(check_tlb_flush, void, env) #endif DEF_HELPER_3(lmw, void, env, tl, i32) diff --git a/target-ppc/helper_regs.h b/target-ppc/helper_regs.h index 271fddf17f..57da931e3c 100644 --- a/target-ppc/helper_regs.h +++ b/target-ppc/helper_regs.h @@ -41,11 +41,50 @@ static inline void hreg_swap_gpr_tgpr(CPUPPCState *env) static inline void hreg_compute_mem_idx(CPUPPCState *env) { - /* Precompute MMU index */ - if (msr_pr == 0 && msr_hv != 0) { - env->mmu_idx = 2; + /* This is our encoding for server processors + * + * 0 = Guest User space virtual mode + * 1 = Guest Kernel space virtual mode + * 2 = Guest Kernel space real mode + * 3 = HV User space virtual mode + * 4 = HV Kernel space virtual mode + * 5 = HV Kernel space real mode + * + * The combination PR=1 IR&DR=0 is invalid, we will treat + * it as IR=DR=1 + * + * For BookE, we need 8 MMU modes as follow: + * + * 0 = AS 0 HV User space + * 1 = AS 0 HV Kernel space + * 2 = AS 1 HV User space + * 3 = AS 1 HV Kernel space + * 4 = AS 0 Guest User space + * 5 = AS 0 Guest Kernel space + * 6 = AS 1 Guest User space + * 7 = AS 1 Guest Kernel space + */ + if (env->mmu_model & POWERPC_MMU_BOOKE) { + env->immu_idx = env->dmmu_idx = msr_pr ? 0 : 1; + env->immu_idx += msr_is ? 2 : 0; + env->dmmu_idx += msr_ds ? 2 : 0; + env->immu_idx += msr_gs ? 4 : 0; + env->dmmu_idx += msr_gs ? 4 : 0; } else { - env->mmu_idx = 1 - msr_pr; + /* First calucalte a base value independent of HV */ + if (msr_pr != 0) { + /* User space, ignore IR and DR */ + env->immu_idx = env->dmmu_idx = 0; + } else { + /* Kernel, setup a base I/D value */ + env->immu_idx = msr_ir ? 1 : 2; + env->dmmu_idx = msr_dr ? 1 : 2; + } + /* Then offset it for HV */ + if (msr_hv) { + env->immu_idx += 3; + env->dmmu_idx += 3; + } } } @@ -82,9 +121,10 @@ static inline int hreg_store_msr(CPUPPCState *env, target_ulong value, } if (((value >> MSR_IR) & 1) != msr_ir || ((value >> MSR_DR) & 1) != msr_dr) { - /* Flush all tlb when changing translation mode */ - tlb_flush(cs, 1); - excp = POWERPC_EXCP_NONE; + cs->interrupt_request |= CPU_INTERRUPT_EXITTB; + } + if ((env->mmu_model & POWERPC_MMU_BOOKE) && + ((value >> MSR_GS) & 1) != msr_gs) { cs->interrupt_request |= CPU_INTERRUPT_EXITTB; } if (unlikely((env->flags & POWERPC_FLAG_TGPR) && @@ -111,4 +151,17 @@ static inline int hreg_store_msr(CPUPPCState *env, target_ulong value, return excp; } +#if !defined(CONFIG_USER_ONLY) && defined(TARGET_PPC64) +static inline void check_tlb_flush(CPUPPCState *env) +{ + CPUState *cs = CPU(ppc_env_get_cpu(env)); + if (env->tlb_need_flush) { + env->tlb_need_flush = 0; + tlb_flush(cs, 1); + } +} +#else +static inline void check_tlb_flush(CPUPPCState *env) { } +#endif + #endif /* !defined(__HELPER_REGS_H__) */ diff --git a/target-ppc/machine.c b/target-ppc/machine.c index f6c7256974..4820f22377 100644 --- a/target-ppc/machine.c +++ b/target-ppc/machine.c @@ -97,9 +97,12 @@ static int cpu_load_old(QEMUFile *f, void *opaque, int version_id) qemu_get_betls(f, &env->nip); qemu_get_betls(f, &env->hflags); qemu_get_betls(f, &env->hflags_nmsr); - qemu_get_sbe32s(f, &env->mmu_idx); + qemu_get_sbe32(f); /* Discard unused mmu_idx */ qemu_get_sbe32(f); /* Discard unused power_mode */ + /* Recompute mmu indices */ + hreg_compute_mem_idx(env); + return 0; } diff --git a/target-ppc/mmu-hash64.c b/target-ppc/mmu-hash64.c index 17e24800cb..ea6e99acd1 100644 --- a/target-ppc/mmu-hash64.c +++ b/target-ppc/mmu-hash64.c @@ -99,10 +99,8 @@ void dump_slb(FILE *f, fprintf_function cpu_fprintf, PowerPCCPU *cpu) void helper_slbia(CPUPPCState *env) { - PowerPCCPU *cpu = ppc_env_get_cpu(env); - int n, do_invalidate; + int n; - do_invalidate = 0; /* XXX: Warning: slbia never invalidates the first segment */ for (n = 1; n < env->slb_nr; n++) { ppc_slb_t *slb = &env->slb[n]; @@ -113,12 +111,9 @@ void helper_slbia(CPUPPCState *env) * and we still don't have a tlb_flush_mask(env, n, mask) * in QEMU, we just invalidate all TLBs */ - do_invalidate = 1; + env->tlb_need_flush = 1; } } - if (do_invalidate) { - tlb_flush(CPU(cpu), 1); - } } void helper_slbie(CPUPPCState *env, target_ulong addr) @@ -138,7 +133,7 @@ void helper_slbie(CPUPPCState *env, target_ulong addr) * and we still don't have a tlb_flush_mask(env, n, mask) * in QEMU, we just invalidate all TLBs */ - tlb_flush(CPU(cpu), 1); + env->tlb_need_flush = 1; } } diff --git a/target-ppc/mmu_helper.c b/target-ppc/mmu_helper.c index 2e0e3ca92c..1499af72a0 100644 --- a/target-ppc/mmu_helper.c +++ b/target-ppc/mmu_helper.c @@ -27,6 +27,7 @@ #include "exec/exec-all.h" #include "exec/cpu_ldst.h" #include "exec/log.h" +#include "helper_regs.h" //#define DEBUG_MMU //#define DEBUG_BATS @@ -1924,6 +1925,7 @@ void ppc_tlb_invalidate_all(CPUPPCState *env) case POWERPC_MMU_2_06a: case POWERPC_MMU_2_07: case POWERPC_MMU_2_07a: + env->tlb_need_flush = 0; #endif /* defined(TARGET_PPC64) */ tlb_flush(CPU(cpu), 1); break; @@ -1986,7 +1988,7 @@ void ppc_tlb_invalidate_one(CPUPPCState *env, target_ulong addr) * and we still don't have a tlb_flush_mask(env, n, mask) in QEMU, * we just invalidate all TLBs */ - tlb_flush(CPU(cpu), 1); + env->tlb_need_flush = 1; break; #endif /* defined(TARGET_PPC64) */ default: @@ -2875,6 +2877,11 @@ void helper_booke206_tlbflush(CPUPPCState *env, target_ulong type) } +void helper_check_tlb_flush(CPUPPCState *env) +{ + check_tlb_flush(env); +} + /*****************************************************************************/ /* try to fill the TLB and return an exception if error. If retaddr is diff --git a/target-ppc/translate.c b/target-ppc/translate.c index f5ceae5900..fe10bf8774 100644 --- a/target-ppc/translate.c +++ b/target-ppc/translate.c @@ -1392,6 +1392,19 @@ GEN_LOGICAL2(nand, tcg_gen_nand_tl, 0x0E, PPC_INTEGER); /* nor & nor. */ GEN_LOGICAL2(nor, tcg_gen_nor_tl, 0x03, PPC_INTEGER); +#if defined(TARGET_PPC64) +static void gen_pause(DisasContext *ctx) +{ + TCGv_i32 t0 = tcg_const_i32(0); + tcg_gen_st_i32(t0, cpu_env, + -offsetof(PowerPCCPU, env) + offsetof(CPUState, halted)); + tcg_temp_free_i32(t0); + + /* Stop translation, this gives other CPUs a chance to run */ + gen_exception_err(ctx, EXCP_HLT, 1); +} +#endif /* defined(TARGET_PPC64) */ + /* or & or. */ static void gen_or(DisasContext *ctx) { @@ -1447,7 +1460,7 @@ static void gen_or(DisasContext *ctx) } break; case 7: - if (ctx->hv) { + if (ctx->hv && !ctx->pr) { /* Set process priority to very high */ prio = 7; } @@ -1464,6 +1477,10 @@ static void gen_or(DisasContext *ctx) tcg_gen_ori_tl(t0, t0, ((uint64_t)prio) << 50); gen_store_spr(SPR_PPR, t0); tcg_temp_free(t0); + /* Pause us out of TCG otherwise spin loops with smt_low + * eat too much CPU and the kernel hangs + */ + gen_pause(ctx); } #endif } @@ -1489,8 +1506,6 @@ static void gen_ori(DisasContext *ctx) target_ulong uimm = UIMM(ctx->opcode); if (rS(ctx->opcode) == rA(ctx->opcode) && uimm == 0) { - /* NOP */ - /* XXX: should handle special NOPs for POWER series */ return; } tcg_gen_ori_tl(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)], uimm); @@ -3275,9 +3290,32 @@ static void gen_eieio(DisasContext *ctx) { } +#if !defined(CONFIG_USER_ONLY) && defined(TARGET_PPC64) +static inline void gen_check_tlb_flush(DisasContext *ctx) +{ + TCGv_i32 t = tcg_temp_new_i32(); + TCGLabel *l = gen_new_label(); + + tcg_gen_ld_i32(t, cpu_env, offsetof(CPUPPCState, tlb_need_flush)); + tcg_gen_brcondi_i32(TCG_COND_EQ, t, 0, l); + gen_helper_check_tlb_flush(cpu_env); + gen_set_label(l); + tcg_temp_free_i32(t); +} +#else +static inline void gen_check_tlb_flush(DisasContext *ctx) { } +#endif + /* isync */ static void gen_isync(DisasContext *ctx) { + /* + * We need to check for a pending TLB flush. This can only happen in + * kernel mode however so check MSR_PR + */ + if (!ctx->pr) { + gen_check_tlb_flush(ctx); + } gen_stop_exception(ctx); } @@ -3434,6 +3472,15 @@ STCX(stqcx_, 16); /* sync */ static void gen_sync(DisasContext *ctx) { + uint32_t l = (ctx->opcode >> 21) & 3; + + /* + * For l == 2, it's a ptesync, We need to check for a pending TLB flush. + * This can only happen in kernel mode however so check MSR_PR as well. + */ + if (l == 2 && !ctx->pr) { + gen_check_tlb_flush(ctx); + } } /* wait */ @@ -4349,7 +4396,7 @@ static void gen_mtmsrd(DisasContext *ctx) /* Special form that does not need any synchronisation */ TCGv t0 = tcg_temp_new(); tcg_gen_andi_tl(t0, cpu_gpr[rS(ctx->opcode)], (1 << MSR_RI) | (1 << MSR_EE)); - tcg_gen_andi_tl(cpu_msr, cpu_msr, ~((1 << MSR_RI) | (1 << MSR_EE))); + tcg_gen_andi_tl(cpu_msr, cpu_msr, ~(target_ulong)((1 << MSR_RI) | (1 << MSR_EE))); tcg_gen_or_tl(cpu_msr, cpu_msr, t0); tcg_temp_free(t0); } else { @@ -4380,7 +4427,7 @@ static void gen_mtmsr(DisasContext *ctx) /* Special form that does not need any synchronisation */ TCGv t0 = tcg_temp_new(); tcg_gen_andi_tl(t0, cpu_gpr[rS(ctx->opcode)], (1 << MSR_RI) | (1 << MSR_EE)); - tcg_gen_andi_tl(cpu_msr, cpu_msr, ~((1 << MSR_RI) | (1 << MSR_EE))); + tcg_gen_andi_tl(cpu_msr, cpu_msr, ~(target_ulong)((1 << MSR_RI) | (1 << MSR_EE))); tcg_gen_or_tl(cpu_msr, cpu_msr, t0); tcg_temp_free(t0); } else { @@ -4826,7 +4873,7 @@ static void gen_tlbie(DisasContext *ctx) #if defined(CONFIG_USER_ONLY) gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); #else - if (unlikely(ctx->pr)) { + if (unlikely(ctx->pr || !ctx->hv)) { gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); return; } @@ -4847,14 +4894,15 @@ static void gen_tlbsync(DisasContext *ctx) #if defined(CONFIG_USER_ONLY) gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); #else - if (unlikely(ctx->pr)) { + if (unlikely(ctx->pr || !ctx->hv)) { gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); return; } - /* This has no effect: it should ensure that all previous - * tlbie have completed + /* tlbsync is a nop for server, ptesync handles delayed tlb flush, + * embedded however needs to deal with tlbsync. We don't try to be + * fancy and swallow the overhead of checking for both. */ - gen_stop_exception(ctx); + gen_check_tlb_flush(ctx); #endif } @@ -4865,7 +4913,7 @@ static void gen_slbia(DisasContext *ctx) #if defined(CONFIG_USER_ONLY) gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); #else - if (unlikely(ctx->pr)) { + if (unlikely(ctx->pr || !ctx->hv)) { gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); return; } @@ -9913,8 +9961,10 @@ GEN_HANDLER2(slbmfee, "slbmfee", 0x1F, 0x13, 0x1C, 0x001F0001, PPC_SEGMENT_64B), GEN_HANDLER2(slbmfev, "slbmfev", 0x1F, 0x13, 0x1A, 0x001F0001, PPC_SEGMENT_64B), #endif GEN_HANDLER(tlbia, 0x1F, 0x12, 0x0B, 0x03FFFC01, PPC_MEM_TLBIA), -GEN_HANDLER(tlbiel, 0x1F, 0x12, 0x08, 0x03FF0001, PPC_MEM_TLBIE), -GEN_HANDLER(tlbie, 0x1F, 0x12, 0x09, 0x03FF0001, PPC_MEM_TLBIE), +/* XXX Those instructions will need to be handled differently for + * different ISA versions */ +GEN_HANDLER(tlbiel, 0x1F, 0x12, 0x08, 0x001F0001, PPC_MEM_TLBIE), +GEN_HANDLER(tlbie, 0x1F, 0x12, 0x09, 0x001F0001, PPC_MEM_TLBIE), GEN_HANDLER(tlbsync, 0x1F, 0x16, 0x11, 0x03FFF801, PPC_MEM_TLBSYNC), #if defined(TARGET_PPC64) GEN_HANDLER(slbia, 0x1F, 0x12, 0x0F, 0x03FFFC01, PPC_SLBI), @@ -11220,8 +11270,9 @@ void ppc_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf, env->nip, env->lr, env->ctr, cpu_read_xer(env), cs->cpu_index); cpu_fprintf(f, "MSR " TARGET_FMT_lx " HID0 " TARGET_FMT_lx " HF " - TARGET_FMT_lx " idx %d\n", env->msr, env->spr[SPR_HID0], - env->hflags, env->mmu_idx); + TARGET_FMT_lx " iidx %d didx %d\n", + env->msr, env->spr[SPR_HID0], + env->hflags, env->immu_idx, env->dmmu_idx); #if !defined(NO_TIMER_DUMP) cpu_fprintf(f, "TB %08" PRIu32 " %08" PRIu64 #if !defined(CONFIG_USER_ONLY) @@ -11428,7 +11479,7 @@ void gen_intermediate_code(CPUPPCState *env, struct TranslationBlock *tb) ctx.spr_cb = env->spr_cb; ctx.pr = msr_pr; ctx.hv = !msr_pr && msr_hv; - ctx.mem_idx = env->mmu_idx; + ctx.mem_idx = env->dmmu_idx; ctx.insns_flags = env->insns_flags; ctx.insns_flags2 = env->insns_flags2; ctx.access_type = -1; diff --git a/target-ppc/translate_init.c b/target-ppc/translate_init.c index a003c1029d..83010768ea 100644 --- a/target-ppc/translate_init.c +++ b/target-ppc/translate_init.c @@ -8359,7 +8359,7 @@ POWERPC_FAMILY(POWER7)(ObjectClass *oc, void *data) PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | PPC_MEM_SYNC | PPC_MEM_EIEIO | PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | - PPC_64B | PPC_ALTIVEC | + PPC_64B | PPC_64H | PPC_ALTIVEC | PPC_SEGMENT_64B | PPC_SLBI | PPC_POPCNTB | PPC_POPCNTWD; pcc->insns_flags2 = PPC2_VSX | PPC2_DFP | PPC2_DBRX | PPC2_ISA205 | @@ -8439,7 +8439,7 @@ POWERPC_FAMILY(POWER8)(ObjectClass *oc, void *data) PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | PPC_MEM_SYNC | PPC_MEM_EIEIO | PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | - PPC_64B | PPC_64BX | PPC_ALTIVEC | + PPC_64B | PPC_64H | PPC_64BX | PPC_ALTIVEC | PPC_SEGMENT_64B | PPC_SLBI | PPC_POPCNTB | PPC_POPCNTWD; pcc->insns_flags2 = PPC2_VSX | PPC2_VSX207 | PPC2_DFP | PPC2_DBRX |