aboutsummaryrefslogtreecommitdiff
path: root/target
diff options
context:
space:
mode:
Diffstat (limited to 'target')
-rw-r--r--target/ppc/cpu.h8
-rw-r--r--target/ppc/excp_helper.c79
-rw-r--r--target/ppc/helper.h4
-rw-r--r--target/ppc/kvm.c43
-rw-r--r--target/ppc/kvm_ppc.h8
-rw-r--r--target/ppc/mem_helper.c197
-rw-r--r--target/ppc/misc_helper.c63
-rw-r--r--target/ppc/mmu-radix64.c6
-rw-r--r--target/ppc/trace-events1
-rw-r--r--target/ppc/translate.c26
-rw-r--r--target/ppc/translate_init.inc.c20
11 files changed, 393 insertions, 62 deletions
diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h
index 8ebeaba649..3a1eb76004 100644
--- a/target/ppc/cpu.h
+++ b/target/ppc/cpu.h
@@ -180,7 +180,7 @@ enum {
POWERPC_EXCP_TRAP = 0x40,
};
-#define PPC_INPUT(env) (env->bus_model)
+#define PPC_INPUT(env) ((env)->bus_model)
/*****************************************************************************/
typedef struct opc_handler_t opc_handler_t;
@@ -397,6 +397,10 @@ typedef struct ppc_v3_pate_t {
#define PSSCR_ESL PPC_BIT(42) /* Enable State Loss */
#define PSSCR_EC PPC_BIT(43) /* Exit Criterion */
+/* HFSCR bits */
+#define HFSCR_MSGP PPC_BIT(53) /* Privileged Message Send Facilities */
+#define HFSCR_IC_MSGP 0xA
+
#define msr_sf ((env->msr >> MSR_SF) & 1)
#define msr_isf ((env->msr >> MSR_ISF) & 1)
#define msr_shv ((env->msr >> MSR_SHV) & 1)
@@ -1329,6 +1333,8 @@ void cpu_ppc_set_vhyp(PowerPCCPU *cpu, PPCVirtualHypervisor *vhyp);
#endif
void store_fpscr(CPUPPCState *env, uint64_t arg, uint32_t mask);
+void helper_hfscr_facility_check(CPUPPCState *env, uint32_t bit,
+ const char *caller, uint32_t cause);
static inline uint64_t ppc_dump_gpr(CPUPPCState *env, int gprn)
{
diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c
index 5752ed4a4d..027f54c0ed 100644
--- a/target/ppc/excp_helper.c
+++ b/target/ppc/excp_helper.c
@@ -473,6 +473,15 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp)
env->spr[SPR_FSCR] |= ((target_ulong)env->error_code << 56);
#endif
break;
+ case POWERPC_EXCP_HV_FU: /* Hypervisor Facility Unavailable Exception */
+#ifdef TARGET_PPC64
+ env->spr[SPR_HFSCR] |= ((target_ulong)env->error_code << FSCR_IC_POS);
+ srr0 = SPR_HSRR0;
+ srr1 = SPR_HSRR1;
+ new_msr |= (target_ulong)MSR_HVB;
+ new_msr |= env->msr & ((target_ulong)1 << MSR_RI);
+#endif
+ break;
case POWERPC_EXCP_PIT: /* Programmable interval timer interrupt */
LOG_EXCP("PIT exception\n");
break;
@@ -900,7 +909,11 @@ static void ppc_hw_interrupt(CPUPPCState *env)
}
if (env->pending_interrupts & (1 << PPC_INTERRUPT_DOORBELL)) {
env->pending_interrupts &= ~(1 << PPC_INTERRUPT_DOORBELL);
- powerpc_excp(cpu, env->excp_model, POWERPC_EXCP_DOORI);
+ if (is_book3s_arch2x(env)) {
+ powerpc_excp(cpu, env->excp_model, POWERPC_EXCP_SDOOR);
+ } else {
+ powerpc_excp(cpu, env->excp_model, POWERPC_EXCP_DOORI);
+ }
return;
}
if (env->pending_interrupts & (1 << PPC_INTERRUPT_HDOORBELL)) {
@@ -1221,39 +1234,30 @@ void helper_msgsnd(target_ulong rb)
}
/* Server Processor Control */
-static int book3s_dbell2irq(target_ulong rb)
-{
- int msg = rb & DBELL_TYPE_MASK;
+static bool dbell_type_server(target_ulong rb)
+{
/*
* A Directed Hypervisor Doorbell message is sent only if the
* message type is 5. All other types are reserved and the
* instruction is a no-op
*/
- return msg == DBELL_TYPE_DBELL_SERVER ? PPC_INTERRUPT_HDOORBELL : -1;
+ return (rb & DBELL_TYPE_MASK) == DBELL_TYPE_DBELL_SERVER;
}
void helper_book3s_msgclr(CPUPPCState *env, target_ulong rb)
{
- int irq = book3s_dbell2irq(rb);
-
- if (irq < 0) {
+ if (!dbell_type_server(rb)) {
return;
}
- env->pending_interrupts &= ~(1 << irq);
+ env->pending_interrupts &= ~(1 << PPC_INTERRUPT_HDOORBELL);
}
-void helper_book3s_msgsnd(target_ulong rb)
+static void book3s_msgsnd_common(int pir, int irq)
{
- int irq = book3s_dbell2irq(rb);
- int pir = rb & DBELL_PROCIDTAG_MASK;
CPUState *cs;
- if (irq < 0) {
- return;
- }
-
qemu_mutex_lock_iothread();
CPU_FOREACH(cs) {
PowerPCCPU *cpu = POWERPC_CPU(cs);
@@ -1267,6 +1271,49 @@ void helper_book3s_msgsnd(target_ulong rb)
}
qemu_mutex_unlock_iothread();
}
+
+void helper_book3s_msgsnd(target_ulong rb)
+{
+ int pir = rb & DBELL_PROCIDTAG_MASK;
+
+ if (!dbell_type_server(rb)) {
+ return;
+ }
+
+ book3s_msgsnd_common(pir, PPC_INTERRUPT_HDOORBELL);
+}
+
+#if defined(TARGET_PPC64)
+void helper_book3s_msgclrp(CPUPPCState *env, target_ulong rb)
+{
+ helper_hfscr_facility_check(env, HFSCR_MSGP, "msgclrp", HFSCR_IC_MSGP);
+
+ if (!dbell_type_server(rb)) {
+ return;
+ }
+
+ env->pending_interrupts &= ~(1 << PPC_INTERRUPT_DOORBELL);
+}
+
+/*
+ * sends a message to other threads that are on the same
+ * multi-threaded processor
+ */
+void helper_book3s_msgsndp(CPUPPCState *env, target_ulong rb)
+{
+ int pir = env->spr_cb[SPR_PIR].default_value;
+
+ helper_hfscr_facility_check(env, HFSCR_MSGP, "msgsndp", HFSCR_IC_MSGP);
+
+ if (!dbell_type_server(rb)) {
+ return;
+ }
+
+ /* TODO: TCG supports only one thread */
+
+ book3s_msgsnd_common(pir, PPC_INTERRUPT_DOORBELL);
+}
+#endif
#endif
void ppc_cpu_do_unaligned_access(CPUState *cs, vaddr vaddr,
diff --git a/target/ppc/helper.h b/target/ppc/helper.h
index cd0dfe383a..cfb4c07085 100644
--- a/target/ppc/helper.h
+++ b/target/ppc/helper.h
@@ -657,6 +657,10 @@ DEF_HELPER_FLAGS_1(load_601_rtcu, TCG_CALL_NO_RWG, tl, env)
DEF_HELPER_FLAGS_1(load_purr, TCG_CALL_NO_RWG, tl, env)
DEF_HELPER_FLAGS_2(store_purr, TCG_CALL_NO_RWG, void, env, tl)
DEF_HELPER_2(store_ptcr, void, env, tl)
+DEF_HELPER_FLAGS_1(load_dpdes, TCG_CALL_NO_RWG, tl, env)
+DEF_HELPER_FLAGS_2(store_dpdes, TCG_CALL_NO_RWG, void, env, tl)
+DEF_HELPER_2(book3s_msgsndp, void, env, tl)
+DEF_HELPER_2(book3s_msgclrp, void, env, tl)
#endif
DEF_HELPER_2(store_sdr1, void, env, tl)
DEF_HELPER_2(store_pidr, void, env, tl)
diff --git a/target/ppc/kvm.c b/target/ppc/kvm.c
index 06fd0cc162..7f44b1aa1a 100644
--- a/target/ppc/kvm.c
+++ b/target/ppc/kvm.c
@@ -53,6 +53,9 @@
#define PROC_DEVTREE_CPU "/proc/device-tree/cpus/"
+#define DEBUG_RETURN_GUEST 0
+#define DEBUG_RETURN_GDB 1
+
const KVMCapabilityInfo kvm_arch_required_capabilities[] = {
KVM_CAP_LAST_INFO
};
@@ -1564,7 +1567,7 @@ void kvm_arch_update_guest_debug(CPUState *cs, struct kvm_guest_debug *dbg)
static int kvm_handle_hw_breakpoint(CPUState *cs,
struct kvm_debug_exit_arch *arch_info)
{
- int handle = 0;
+ int handle = DEBUG_RETURN_GUEST;
int n;
int flag = 0;
@@ -1572,13 +1575,13 @@ static int kvm_handle_hw_breakpoint(CPUState *cs,
if (arch_info->status & KVMPPC_DEBUG_BREAKPOINT) {
n = find_hw_breakpoint(arch_info->address, GDB_BREAKPOINT_HW);
if (n >= 0) {
- handle = 1;
+ handle = DEBUG_RETURN_GDB;
}
} else if (arch_info->status & (KVMPPC_DEBUG_WATCH_READ |
KVMPPC_DEBUG_WATCH_WRITE)) {
n = find_hw_watchpoint(arch_info->address, &flag);
if (n >= 0) {
- handle = 1;
+ handle = DEBUG_RETURN_GDB;
cs->watchpoint_hit = &hw_watchpoint;
hw_watchpoint.vaddr = hw_debug_points[n].addr;
hw_watchpoint.flags = flag;
@@ -1590,12 +1593,12 @@ static int kvm_handle_hw_breakpoint(CPUState *cs,
static int kvm_handle_singlestep(void)
{
- return 1;
+ return DEBUG_RETURN_GDB;
}
static int kvm_handle_sw_breakpoint(void)
{
- return 1;
+ return DEBUG_RETURN_GDB;
}
static int kvm_handle_debug(PowerPCCPU *cpu, struct kvm_run *run)
@@ -1647,7 +1650,7 @@ static int kvm_handle_debug(PowerPCCPU *cpu, struct kvm_run *run)
env->error_code = POWERPC_EXCP_INVAL;
ppc_cpu_do_interrupt(cs);
- return 0;
+ return DEBUG_RETURN_GUEST;
}
int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run)
@@ -1702,6 +1705,13 @@ int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run)
ret = 0;
break;
+#if defined(TARGET_PPC64)
+ case KVM_EXIT_NMI:
+ trace_kvm_handle_nmi_exception();
+ ret = kvm_handle_nmi(cpu, run);
+ break;
+#endif
+
default:
fprintf(stderr, "KVM: unknown exit reason %d\n", run->exit_reason);
ret = -1;
@@ -2054,6 +2064,14 @@ void kvmppc_set_mpic_proxy(PowerPCCPU *cpu, int mpic_proxy)
}
}
+int kvmppc_set_fwnmi(void)
+{
+ PowerPCCPU *cpu = POWERPC_CPU(first_cpu);
+ CPUState *cs = CPU(cpu);
+
+ return kvm_vcpu_enable_cap(cs, KVM_CAP_PPC_FWNMI, 0);
+}
+
int kvmppc_smt_threads(void)
{
return cap_ppc_smt ? cap_ppc_smt : 1;
@@ -2789,6 +2807,19 @@ int kvm_arch_msi_data_to_gsi(uint32_t data)
return data & 0xffff;
}
+#if defined(TARGET_PPC64)
+int kvm_handle_nmi(PowerPCCPU *cpu, struct kvm_run *run)
+{
+ bool recovered = run->flags & KVM_RUN_PPC_NMI_DISP_FULLY_RECOV;
+
+ cpu_synchronize_state(CPU(cpu));
+
+ spapr_mce_req_event(cpu, recovered);
+
+ return 0;
+}
+#endif
+
int kvmppc_enable_hwrng(void)
{
if (!kvm_enabled() || !kvm_check_extension(kvm_state, KVM_CAP_PPC_HWRNG)) {
diff --git a/target/ppc/kvm_ppc.h b/target/ppc/kvm_ppc.h
index b713097bfb..9e4f2357cc 100644
--- a/target/ppc/kvm_ppc.h
+++ b/target/ppc/kvm_ppc.h
@@ -27,6 +27,7 @@ void kvmppc_enable_h_page_init(void);
void kvmppc_set_papr(PowerPCCPU *cpu);
int kvmppc_set_compat(PowerPCCPU *cpu, uint32_t compat_pvr);
void kvmppc_set_mpic_proxy(PowerPCCPU *cpu, int mpic_proxy);
+int kvmppc_set_fwnmi(void);
int kvmppc_smt_threads(void);
void kvmppc_error_append_smt_possible_hint(Error *const *errp);
int kvmppc_set_smt_threads(int smt);
@@ -83,6 +84,8 @@ void kvm_check_mmu(PowerPCCPU *cpu, Error **errp);
void kvmppc_set_reg_ppc_online(PowerPCCPU *cpu, unsigned int online);
void kvmppc_set_reg_tb_offset(PowerPCCPU *cpu, int64_t tb_offset);
+int kvm_handle_nmi(PowerPCCPU *cpu, struct kvm_run *run);
+
#else
static inline uint32_t kvmppc_get_tbfreq(void)
@@ -160,6 +163,11 @@ static inline void kvmppc_set_mpic_proxy(PowerPCCPU *cpu, int mpic_proxy)
{
}
+static inline int kvmppc_set_fwnmi(void)
+{
+ return -1;
+}
+
static inline int kvmppc_smt_threads(void)
{
return 1;
diff --git a/target/ppc/mem_helper.c b/target/ppc/mem_helper.c
index e8e2a8ac2a..98f589552b 100644
--- a/target/ppc/mem_helper.c
+++ b/target/ppc/mem_helper.c
@@ -56,51 +56,138 @@ static inline target_ulong addr_add(CPUPPCState *env, target_ulong addr,
}
}
+static void *probe_contiguous(CPUPPCState *env, target_ulong addr, uint32_t nb,
+ MMUAccessType access_type, int mmu_idx,
+ uintptr_t raddr)
+{
+ void *host1, *host2;
+ uint32_t nb_pg1, nb_pg2;
+
+ nb_pg1 = -(addr | TARGET_PAGE_MASK);
+ if (likely(nb <= nb_pg1)) {
+ /* The entire operation is on a single page. */
+ return probe_access(env, addr, nb, access_type, mmu_idx, raddr);
+ }
+
+ /* The operation spans two pages. */
+ nb_pg2 = nb - nb_pg1;
+ host1 = probe_access(env, addr, nb_pg1, access_type, mmu_idx, raddr);
+ addr = addr_add(env, addr, nb_pg1);
+ host2 = probe_access(env, addr, nb_pg2, access_type, mmu_idx, raddr);
+
+ /* If the two host pages are contiguous, optimize. */
+ if (host2 == host1 + nb_pg1) {
+ return host1;
+ }
+ return NULL;
+}
+
void helper_lmw(CPUPPCState *env, target_ulong addr, uint32_t reg)
{
- for (; reg < 32; reg++) {
- if (needs_byteswap(env)) {
- env->gpr[reg] = bswap32(cpu_ldl_data_ra(env, addr, GETPC()));
- } else {
- env->gpr[reg] = cpu_ldl_data_ra(env, addr, GETPC());
+ uintptr_t raddr = GETPC();
+ int mmu_idx = cpu_mmu_index(env, false);
+ void *host = probe_contiguous(env, addr, (32 - reg) * 4,
+ MMU_DATA_LOAD, mmu_idx, raddr);
+
+ if (likely(host)) {
+ /* Fast path -- the entire operation is in RAM at host. */
+ for (; reg < 32; reg++) {
+ env->gpr[reg] = (uint32_t)ldl_be_p(host);
+ host += 4;
+ }
+ } else {
+ /* Slow path -- at least some of the operation requires i/o. */
+ for (; reg < 32; reg++) {
+ env->gpr[reg] = cpu_ldl_mmuidx_ra(env, addr, mmu_idx, raddr);
+ addr = addr_add(env, addr, 4);
}
- addr = addr_add(env, addr, 4);
}
}
void helper_stmw(CPUPPCState *env, target_ulong addr, uint32_t reg)
{
- for (; reg < 32; reg++) {
- if (needs_byteswap(env)) {
- cpu_stl_data_ra(env, addr, bswap32((uint32_t)env->gpr[reg]),
- GETPC());
- } else {
- cpu_stl_data_ra(env, addr, (uint32_t)env->gpr[reg], GETPC());
+ uintptr_t raddr = GETPC();
+ int mmu_idx = cpu_mmu_index(env, false);
+ void *host = probe_contiguous(env, addr, (32 - reg) * 4,
+ MMU_DATA_STORE, mmu_idx, raddr);
+
+ if (likely(host)) {
+ /* Fast path -- the entire operation is in RAM at host. */
+ for (; reg < 32; reg++) {
+ stl_be_p(host, env->gpr[reg]);
+ host += 4;
+ }
+ } else {
+ /* Slow path -- at least some of the operation requires i/o. */
+ for (; reg < 32; reg++) {
+ cpu_stl_mmuidx_ra(env, addr, env->gpr[reg], mmu_idx, raddr);
+ addr = addr_add(env, addr, 4);
}
- addr = addr_add(env, addr, 4);
}
}
static void do_lsw(CPUPPCState *env, target_ulong addr, uint32_t nb,
uint32_t reg, uintptr_t raddr)
{
- int sh;
+ int mmu_idx;
+ void *host;
+ uint32_t val;
- for (; nb > 3; nb -= 4) {
- env->gpr[reg] = cpu_ldl_data_ra(env, addr, raddr);
- reg = (reg + 1) % 32;
- addr = addr_add(env, addr, 4);
+ if (unlikely(nb == 0)) {
+ return;
}
- if (unlikely(nb > 0)) {
- env->gpr[reg] = 0;
- for (sh = 24; nb > 0; nb--, sh -= 8) {
- env->gpr[reg] |= cpu_ldub_data_ra(env, addr, raddr) << sh;
- addr = addr_add(env, addr, 1);
+
+ mmu_idx = cpu_mmu_index(env, false);
+ host = probe_contiguous(env, addr, nb, MMU_DATA_LOAD, mmu_idx, raddr);
+
+ if (likely(host)) {
+ /* Fast path -- the entire operation is in RAM at host. */
+ for (; nb > 3; nb -= 4) {
+ env->gpr[reg] = (uint32_t)ldl_be_p(host);
+ reg = (reg + 1) % 32;
+ host += 4;
+ }
+ switch (nb) {
+ default:
+ return;
+ case 1:
+ val = ldub_p(host) << 24;
+ break;
+ case 2:
+ val = lduw_be_p(host) << 16;
+ break;
+ case 3:
+ val = (lduw_be_p(host) << 16) | (ldub_p(host + 2) << 8);
+ break;
+ }
+ } else {
+ /* Slow path -- at least some of the operation requires i/o. */
+ for (; nb > 3; nb -= 4) {
+ env->gpr[reg] = cpu_ldl_mmuidx_ra(env, addr, mmu_idx, raddr);
+ reg = (reg + 1) % 32;
+ addr = addr_add(env, addr, 4);
+ }
+ switch (nb) {
+ default:
+ return;
+ case 1:
+ val = cpu_ldub_mmuidx_ra(env, addr, mmu_idx, raddr) << 24;
+ break;
+ case 2:
+ val = cpu_lduw_mmuidx_ra(env, addr, mmu_idx, raddr) << 16;
+ break;
+ case 3:
+ val = cpu_lduw_mmuidx_ra(env, addr, mmu_idx, raddr) << 16;
+ addr = addr_add(env, addr, 2);
+ val |= cpu_ldub_mmuidx_ra(env, addr, mmu_idx, raddr) << 8;
+ break;
}
}
+ env->gpr[reg] = val;
}
-void helper_lsw(CPUPPCState *env, target_ulong addr, uint32_t nb, uint32_t reg)
+void helper_lsw(CPUPPCState *env, target_ulong addr,
+ uint32_t nb, uint32_t reg)
{
do_lsw(env, addr, nb, reg, GETPC());
}
@@ -130,17 +217,57 @@ void helper_lswx(CPUPPCState *env, target_ulong addr, uint32_t reg,
void helper_stsw(CPUPPCState *env, target_ulong addr, uint32_t nb,
uint32_t reg)
{
- int sh;
+ uintptr_t raddr = GETPC();
+ int mmu_idx;
+ void *host;
+ uint32_t val;
- for (; nb > 3; nb -= 4) {
- cpu_stl_data_ra(env, addr, env->gpr[reg], GETPC());
- reg = (reg + 1) % 32;
- addr = addr_add(env, addr, 4);
+ if (unlikely(nb == 0)) {
+ return;
}
- if (unlikely(nb > 0)) {
- for (sh = 24; nb > 0; nb--, sh -= 8) {
- cpu_stb_data_ra(env, addr, (env->gpr[reg] >> sh) & 0xFF, GETPC());
- addr = addr_add(env, addr, 1);
+
+ mmu_idx = cpu_mmu_index(env, false);
+ host = probe_contiguous(env, addr, nb, MMU_DATA_STORE, mmu_idx, raddr);
+
+ if (likely(host)) {
+ /* Fast path -- the entire operation is in RAM at host. */
+ for (; nb > 3; nb -= 4) {
+ stl_be_p(host, env->gpr[reg]);
+ reg = (reg + 1) % 32;
+ host += 4;
+ }
+ val = env->gpr[reg];
+ switch (nb) {
+ case 1:
+ stb_p(host, val >> 24);
+ break;
+ case 2:
+ stw_be_p(host, val >> 16);
+ break;
+ case 3:
+ stw_be_p(host, val >> 16);
+ stb_p(host + 2, val >> 8);
+ break;
+ }
+ } else {
+ for (; nb > 3; nb -= 4) {
+ cpu_stl_mmuidx_ra(env, addr, env->gpr[reg], mmu_idx, raddr);
+ reg = (reg + 1) % 32;
+ addr = addr_add(env, addr, 4);
+ }
+ val = env->gpr[reg];
+ switch (nb) {
+ case 1:
+ cpu_stb_mmuidx_ra(env, addr, val >> 24, mmu_idx, raddr);
+ break;
+ case 2:
+ cpu_stw_mmuidx_ra(env, addr, val >> 16, mmu_idx, raddr);
+ break;
+ case 3:
+ cpu_stw_mmuidx_ra(env, addr, val >> 16, mmu_idx, raddr);
+ addr = addr_add(env, addr, 2);
+ cpu_stb_mmuidx_ra(env, addr, val >> 8, mmu_idx, raddr);
+ break;
}
}
}
@@ -166,12 +293,12 @@ static void dcbz_common(CPUPPCState *env, target_ulong addr,
addr &= mask;
/* Check reservation */
- if ((env->reserve_addr & mask) == (addr & mask)) {
+ if ((env->reserve_addr & mask) == addr) {
env->reserve_addr = (target_ulong)-1ULL;
}
/* Try fast path translate */
- haddr = tlb_vaddr_to_host(env, addr, MMU_DATA_STORE, mmu_idx);
+ haddr = probe_write(env, addr, dcbz_size, mmu_idx, retaddr);
if (haddr) {
memset(haddr, 0, dcbz_size);
} else {
diff --git a/target/ppc/misc_helper.c b/target/ppc/misc_helper.c
index 2318f3ab45..55b68d1246 100644
--- a/target/ppc/misc_helper.c
+++ b/target/ppc/misc_helper.c
@@ -41,6 +41,18 @@ void helper_store_dump_spr(CPUPPCState *env, uint32_t sprn)
}
#ifdef TARGET_PPC64
+static void raise_hv_fu_exception(CPUPPCState *env, uint32_t bit,
+ const char *caller, uint32_t cause,
+ uintptr_t raddr)
+{
+ qemu_log_mask(CPU_LOG_INT, "HV Facility %d is unavailable (%s)\n",
+ bit, caller);
+
+ env->spr[SPR_HFSCR] &= ~((target_ulong)FSCR_IC_MASK << FSCR_IC_POS);
+
+ raise_exception_err_ra(env, POWERPC_EXCP_HV_FU, cause, raddr);
+}
+
static void raise_fu_exception(CPUPPCState *env, uint32_t bit,
uint32_t sprn, uint32_t cause,
uintptr_t raddr)
@@ -55,6 +67,17 @@ static void raise_fu_exception(CPUPPCState *env, uint32_t bit,
}
#endif
+void helper_hfscr_facility_check(CPUPPCState *env, uint32_t bit,
+ const char *caller, uint32_t cause)
+{
+#ifdef TARGET_PPC64
+ if ((env->msr_mask & MSR_HVB) && !msr_hv &&
+ !(env->spr[SPR_HFSCR] & (1UL << bit))) {
+ raise_hv_fu_exception(env, bit, caller, cause, GETPC());
+ }
+#endif
+}
+
void helper_fscr_facility_check(CPUPPCState *env, uint32_t bit,
uint32_t sprn, uint32_t cause)
{
@@ -105,6 +128,46 @@ void helper_store_pcr(CPUPPCState *env, target_ulong value)
env->spr[SPR_PCR] = value & pcc->pcr_mask;
}
+
+/*
+ * DPDES register is shared. Each bit reflects the state of the
+ * doorbell interrupt of a thread of the same core.
+ */
+target_ulong helper_load_dpdes(CPUPPCState *env)
+{
+ target_ulong dpdes = 0;
+
+ helper_hfscr_facility_check(env, HFSCR_MSGP, "load DPDES", HFSCR_IC_MSGP);
+
+ /* TODO: TCG supports only one thread */
+ if (env->pending_interrupts & (1 << PPC_INTERRUPT_DOORBELL)) {
+ dpdes = 1;
+ }
+
+ return dpdes;
+}
+
+void helper_store_dpdes(CPUPPCState *env, target_ulong val)
+{
+ PowerPCCPU *cpu = env_archcpu(env);
+ CPUState *cs = CPU(cpu);
+
+ helper_hfscr_facility_check(env, HFSCR_MSGP, "store DPDES", HFSCR_IC_MSGP);
+
+ /* TODO: TCG supports only one thread */
+ if (val & ~0x1) {
+ qemu_log_mask(LOG_GUEST_ERROR, "Invalid DPDES register value "
+ TARGET_FMT_lx"\n", val);
+ return;
+ }
+
+ if (val & 0x1) {
+ env->pending_interrupts |= 1 << PPC_INTERRUPT_DOORBELL;
+ cpu_interrupt(cs, CPU_INTERRUPT_HARD);
+ } else {
+ env->pending_interrupts &= ~(1 << PPC_INTERRUPT_DOORBELL);
+ }
+}
#endif /* defined(TARGET_PPC64) */
void helper_store_pidr(CPUPPCState *env, target_ulong val)
diff --git a/target/ppc/mmu-radix64.c b/target/ppc/mmu-radix64.c
index 066e324464..224e646c50 100644
--- a/target/ppc/mmu-radix64.c
+++ b/target/ppc/mmu-radix64.c
@@ -235,6 +235,12 @@ int ppc_radix64_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr, int rwx,
/* In real mode top 4 effective addr bits (mostly) ignored */
raddr = eaddr & 0x0FFFFFFFFFFFFFFFULL;
+ /* In HV mode, add HRMOR if top EA bit is clear */
+ if (msr_hv || !env->has_hv_mode) {
+ if (!(eaddr >> 63)) {
+ raddr |= env->spr[SPR_HRMOR];
+ }
+ }
tlb_set_page(cs, eaddr & TARGET_PAGE_MASK, raddr & TARGET_PAGE_MASK,
PAGE_READ | PAGE_WRITE | PAGE_EXEC, mmu_idx,
TARGET_PAGE_SIZE);
diff --git a/target/ppc/trace-events b/target/ppc/trace-events
index 3dc6740706..6d15aa90b4 100644
--- a/target/ppc/trace-events
+++ b/target/ppc/trace-events
@@ -28,3 +28,4 @@ kvm_handle_papr_hcall(void) "handle PAPR hypercall"
kvm_handle_epr(void) "handle epr"
kvm_handle_watchdog_expiry(void) "handle watchdog expiry"
kvm_handle_debug_exception(void) "handle debug exception"
+kvm_handle_nmi_exception(void) "handle NMI exception"
diff --git a/target/ppc/translate.c b/target/ppc/translate.c
index 9dcf8dc261..36fa27367c 100644
--- a/target/ppc/translate.c
+++ b/target/ppc/translate.c
@@ -6645,6 +6645,28 @@ static void gen_msgsnd(DisasContext *ctx)
#endif /* defined(CONFIG_USER_ONLY) */
}
+#if defined(TARGET_PPC64)
+static void gen_msgclrp(DisasContext *ctx)
+{
+#if defined(CONFIG_USER_ONLY)
+ GEN_PRIV;
+#else
+ CHK_SV;
+ gen_helper_book3s_msgclrp(cpu_env, cpu_gpr[rB(ctx->opcode)]);
+#endif /* defined(CONFIG_USER_ONLY) */
+}
+
+static void gen_msgsndp(DisasContext *ctx)
+{
+#if defined(CONFIG_USER_ONLY)
+ GEN_PRIV;
+#else
+ CHK_SV;
+ gen_helper_book3s_msgsndp(cpu_env, cpu_gpr[rB(ctx->opcode)]);
+#endif /* defined(CONFIG_USER_ONLY) */
+}
+#endif
+
static void gen_msgsync(DisasContext *ctx)
{
#if defined(CONFIG_USER_ONLY)
@@ -7187,6 +7209,10 @@ GEN_HANDLER(vmladduhm, 0x04, 0x11, 0xFF, 0x00000000, PPC_ALTIVEC),
GEN_HANDLER_E(maddhd_maddhdu, 0x04, 0x18, 0xFF, 0x00000000, PPC_NONE,
PPC2_ISA300),
GEN_HANDLER_E(maddld, 0x04, 0x19, 0xFF, 0x00000000, PPC_NONE, PPC2_ISA300),
+GEN_HANDLER2_E(msgsndp, "msgsndp", 0x1F, 0x0E, 0x04, 0x03ff0001,
+ PPC_NONE, PPC2_ISA207S),
+GEN_HANDLER2_E(msgclrp, "msgclrp", 0x1F, 0x0E, 0x05, 0x03ff0001,
+ PPC_NONE, PPC2_ISA207S),
#endif
#undef GEN_INT_ARITH_ADD
diff --git a/target/ppc/translate_init.inc.c b/target/ppc/translate_init.inc.c
index 2d3efad233..53995f62ea 100644
--- a/target/ppc/translate_init.inc.c
+++ b/target/ppc/translate_init.inc.c
@@ -464,6 +464,17 @@ static void spr_write_pcr(DisasContext *ctx, int sprn, int gprn)
{
gen_helper_store_pcr(cpu_env, cpu_gpr[gprn]);
}
+
+/* DPDES */
+static void spr_read_dpdes(DisasContext *ctx, int gprn, int sprn)
+{
+ gen_helper_load_dpdes(cpu_gpr[gprn], cpu_env);
+}
+
+static void spr_write_dpdes(DisasContext *ctx, int sprn, int gprn)
+{
+ gen_helper_store_dpdes(cpu_env, cpu_gpr[gprn]);
+}
#endif
#endif
@@ -8238,10 +8249,11 @@ static void gen_spr_power8_dpdes(CPUPPCState *env)
{
#if !defined(CONFIG_USER_ONLY)
/* Directed Privileged Door-bell Exception State, used for IPI */
- spr_register(env, SPR_DPDES, "DPDES",
- SPR_NOACCESS, SPR_NOACCESS,
- &spr_read_generic, SPR_NOACCESS,
- 0x00000000);
+ spr_register_kvm_hv(env, SPR_DPDES, "DPDES",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_dpdes, SPR_NOACCESS,
+ &spr_read_dpdes, &spr_write_dpdes,
+ KVM_REG_PPC_DPDES, 0x00000000);
#endif
}