ppc patch queue for 2016-05-31
Here's another ppc patch queue. This batch is all preliminaries towards two significant features: 1) Full hypervisor-mode support for POWER8 Patches 1-8 start fixing various bugs with TCG's handling of hypervisor mode 2) CPU hotplug support Patches 9-12 make some preliminary fixes towards implementing CPU hotplug on ppc64 (and other non-x86 platforms). These patches are actually to generic code, not ppc, but are included here with Paolo's ACK. -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJXTN1QAAoJEGw4ysog2bOSM4kP/2TKm/wkGo3nsGm7vG0CArs+ JVIlTWI9Le7Cq5ijCkTwV9gjeG2CYz+Us2PCh2ZAoHpXgZtP7px2HRcDv07SbCnt SaCwCS+EGf3ZO9baQrzG0zfe8XrlJF+XXTejD2zWtOZw7sZ/4OPWF9KdcZbjWqFp PzJuXrpYOAaIyXyEPJSZFpHY+AC9NIblqHlUrKntPLLOYbqQBYP4IMxsUmOgu2IX rFK/5A8t20BJN0lbmx8JNKh0voorFpHY/hhaH/1T7rKxsRkKMh3VbYSxD6EYs3Uc nZ4ufQQW6C4CEFta3YHNwoClcsQUbnZQh3Ra+gKo9bXvqDzasVpq/mBgl3BDjGeG LQPSA6sfmEA8lqtRikVdgSgdXDnwy5YXJLVmIXeAIG1KHa6eRuUxC3o+ScOkcH3A ynLglCEBl9slsG9/yYkDcFW2u0t/txTBUvaxMfOQomAejrGjOLGZqnSWMd2UC4Gt KQRP+b7igkXC7+bfrpBPWKyYGvOOCukESw3OV90hLBIzOthI1dI5hO0Gj61C/nlI NXMRbx0qTztgj3tfSTTs6e9Ke8PEKnyXqgol0+t9Yxntlz28f2alTubUoyv/vZjx 8J1IOlNms3PnO26TxUVBu7/KaCGCM25eTQgllbTx8rhaqin3wAH3dRc1RTlWWhwJ SgADl+MWf8sa7DkcxnZa =vAIf -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/dgibson/tags/ppc-for-2.7-20160531' into staging ppc patch queue for 2016-05-31 Here's another ppc patch queue. This batch is all preliminaries towards two significant features: 1) Full hypervisor-mode support for POWER8 Patches 1-8 start fixing various bugs with TCG's handling of hypervisor mode 2) CPU hotplug support Patches 9-12 make some preliminary fixes towards implementing CPU hotplug on ppc64 (and other non-x86 platforms). These patches are actually to generic code, not ppc, but are included here with Paolo's ACK. # gpg: Signature made Tue 31 May 2016 01:39:44 BST using RSA key ID 20D9B392 # gpg: Good signature from "David Gibson <david@gibson.dropbear.id.au>" # gpg: aka "David Gibson (Red Hat) <dgibson@redhat.com>" # gpg: aka "David Gibson (ozlabs.org) <dgibson@ozlabs.org>" # gpg: WARNING: This key is not certified with sufficiently trusted signatures! # gpg: It is not certain that the signature belongs to the owner. # Primary key fingerprint: 75F4 6586 AE61 A66C C44E 87DC 6C38 CACA 20D9 B392 * remotes/dgibson/tags/ppc-for-2.7-20160531: cpu: Add a sync version of cpu_remove() cpu: Reclaim vCPU objects exec: Do vmstate unregistration from cpu_exec_exit() exec: Remove cpu from cpus list during cpu_exec_exit() ppc: Add PPC_64H instruction flag to POWER7 and POWER8 ppc: Get out of emulation on SMT "OR" ops ppc: Fix sign extension issue in mtmsr(d) emulation ppc: Change 'invalid' bit mask of tlbiel and tlbie ppc: tlbie, tlbia and tlbisync are HV only ppc: Do some batching of TCG tlb flushes ppc: Use split I/D mmu modes to avoid flushes on interrupts ppc: Remove MMU_MODEn_SUFFIX definitions Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
500acc9c41
51
cpus.c
51
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;
|
||||
}
|
||||
}
|
||||
|
43
exec.c
43
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);
|
||||
|
@ -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,
|
||||
|
@ -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.
|
||||
|
@ -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"
|
||||
|
57
kvm-all.c
57
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) {
|
||||
|
@ -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;
|
||||
|
@ -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"
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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__) */
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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 |
|
||||
|
Loading…
Reference in New Issue
Block a user