Commit e7d26f28 authored by Benjamin Herrenschmidt's avatar Benjamin Herrenschmidt Committed by Alexander Graf

KVM: PPC: Book3S HV: Add support for real mode ICP in XICS emulation

This adds an implementation of the XICS hypercalls in real mode for HV
KVM, which allows us to avoid exiting the guest MMU context on all
threads for a variety of operations such as fetching a pending
interrupt, EOI of messages, IPIs, etc.
Signed-off-by: default avatarBenjamin Herrenschmidt <benh@kernel.crashing.org>
Signed-off-by: default avatarPaul Mackerras <paulus@samba.org>
Signed-off-by: default avatarAlexander Graf <agraf@suse.de>
parent 54695c30
......@@ -72,12 +72,15 @@ kvm-book3s_64-objs-$(CONFIG_KVM_BOOK3S_64_HV) := \
book3s_hv.o \
book3s_hv_interrupts.o \
book3s_64_mmu_hv.o
kvm-book3s_64-builtin-xics-objs-$(CONFIG_KVM_XICS) := \
book3s_hv_rm_xics.o
kvm-book3s_64-builtin-objs-$(CONFIG_KVM_BOOK3S_64_HV) := \
book3s_hv_rmhandlers.o \
book3s_hv_rm_mmu.o \
book3s_64_vio_hv.o \
book3s_hv_ras.o \
book3s_hv_builtin.o
book3s_hv_builtin.o \
$(kvm-book3s_64-builtin-xics-objs-y)
kvm-book3s_64-objs-$(CONFIG_KVM_XICS) += \
book3s_xics.o
......
This diff is collapsed.
......@@ -1424,11 +1424,19 @@ hcall_real_table:
.long 0 /* 0x58 */
.long 0 /* 0x5c */
.long 0 /* 0x60 */
.long 0 /* 0x64 */
.long 0 /* 0x68 */
.long 0 /* 0x6c */
.long 0 /* 0x70 */
.long 0 /* 0x74 */
#ifdef CONFIG_KVM_XICS
.long .kvmppc_rm_h_eoi - hcall_real_table
.long .kvmppc_rm_h_cppr - hcall_real_table
.long .kvmppc_rm_h_ipi - hcall_real_table
.long 0 /* 0x70 - H_IPOLL */
.long .kvmppc_rm_h_xirr - hcall_real_table
#else
.long 0 /* 0x64 - H_EOI */
.long 0 /* 0x68 - H_CPPR */
.long 0 /* 0x6c - H_IPI */
.long 0 /* 0x70 - H_IPOLL */
.long 0 /* 0x74 - H_XIRR */
#endif
.long 0 /* 0x78 */
.long 0 /* 0x7c */
.long 0 /* 0x80 */
......
......@@ -30,6 +30,9 @@
#define XICS_DBG(fmt...) trace_printk(fmt)
#endif
#define ENABLE_REALMODE true
#define DEBUG_REALMODE false
/*
* LOCKING
* =======
......@@ -220,8 +223,10 @@ static inline bool icp_try_update(struct kvmppc_icp *icp,
* in Accept (H_XIRR) and Up_Cppr (H_XPPR).
*
* We also do not try to figure out whether the EE state has changed,
* we unconditionally set it if the new state calls for it for the
* same reason.
* we unconditionally set it if the new state calls for it. The reason
* for that is that we opportunistically remove the pending interrupt
* flag when raising CPPR, so we need to set it back here if an
* interrupt is still pending.
*/
if (new.out_ee) {
kvmppc_book3s_queue_irqprio(icp->vcpu,
......@@ -483,7 +488,7 @@ static void icp_down_cppr(struct kvmppc_xics *xics, struct kvmppc_icp *icp,
icp_check_resend(xics, icp);
}
static noinline unsigned long h_xirr(struct kvm_vcpu *vcpu)
static noinline unsigned long kvmppc_h_xirr(struct kvm_vcpu *vcpu)
{
union kvmppc_icp_state old_state, new_state;
struct kvmppc_icp *icp = vcpu->arch.icp;
......@@ -517,7 +522,7 @@ static noinline unsigned long h_xirr(struct kvm_vcpu *vcpu)
return xirr;
}
static noinline int h_ipi(struct kvm_vcpu *vcpu, unsigned long server,
static noinline int kvmppc_h_ipi(struct kvm_vcpu *vcpu, unsigned long server,
unsigned long mfrr)
{
union kvmppc_icp_state old_state, new_state;
......@@ -586,7 +591,7 @@ static noinline int h_ipi(struct kvm_vcpu *vcpu, unsigned long server,
return H_SUCCESS;
}
static noinline void h_cppr(struct kvm_vcpu *vcpu, unsigned long cppr)
static noinline void kvmppc_h_cppr(struct kvm_vcpu *vcpu, unsigned long cppr)
{
union kvmppc_icp_state old_state, new_state;
struct kvmppc_xics *xics = vcpu->kvm->arch.xics;
......@@ -643,7 +648,7 @@ static noinline void h_cppr(struct kvm_vcpu *vcpu, unsigned long cppr)
icp_deliver_irq(xics, icp, reject);
}
static noinline int h_eoi(struct kvm_vcpu *vcpu, unsigned long xirr)
static noinline int kvmppc_h_eoi(struct kvm_vcpu *vcpu, unsigned long xirr)
{
struct kvmppc_xics *xics = vcpu->kvm->arch.xics;
struct kvmppc_icp *icp = vcpu->arch.icp;
......@@ -693,28 +698,53 @@ static noinline int h_eoi(struct kvm_vcpu *vcpu, unsigned long xirr)
return H_SUCCESS;
}
static noinline int kvmppc_xics_rm_complete(struct kvm_vcpu *vcpu, u32 hcall)
{
struct kvmppc_xics *xics = vcpu->kvm->arch.xics;
struct kvmppc_icp *icp = vcpu->arch.icp;
XICS_DBG("XICS_RM: H_%x completing, act: %x state: %lx tgt: %p\n",
hcall, icp->rm_action, icp->rm_dbgstate.raw, icp->rm_dbgtgt);
if (icp->rm_action & XICS_RM_KICK_VCPU)
kvmppc_fast_vcpu_kick(icp->rm_kick_target);
if (icp->rm_action & XICS_RM_CHECK_RESEND)
icp_check_resend(xics, icp);
if (icp->rm_action & XICS_RM_REJECT)
icp_deliver_irq(xics, icp, icp->rm_reject);
icp->rm_action = 0;
return H_SUCCESS;
}
int kvmppc_xics_hcall(struct kvm_vcpu *vcpu, u32 req)
{
struct kvmppc_xics *xics = vcpu->kvm->arch.xics;
unsigned long res;
int rc = H_SUCCESS;
/* Check if we have an ICP */
if (!vcpu->arch.icp || !vcpu->kvm->arch.xics)
if (!xics || !vcpu->arch.icp)
return H_HARDWARE;
/* Check for real mode returning too hard */
if (xics->real_mode)
return kvmppc_xics_rm_complete(vcpu, req);
switch (req) {
case H_XIRR:
res = h_xirr(vcpu);
res = kvmppc_h_xirr(vcpu);
kvmppc_set_gpr(vcpu, 4, res);
break;
case H_CPPR:
h_cppr(vcpu, kvmppc_get_gpr(vcpu, 4));
kvmppc_h_cppr(vcpu, kvmppc_get_gpr(vcpu, 4));
break;
case H_EOI:
rc = h_eoi(vcpu, kvmppc_get_gpr(vcpu, 4));
rc = kvmppc_h_eoi(vcpu, kvmppc_get_gpr(vcpu, 4));
break;
case H_IPI:
rc = h_ipi(vcpu, kvmppc_get_gpr(vcpu, 4),
rc = kvmppc_h_ipi(vcpu, kvmppc_get_gpr(vcpu, 4),
kvmppc_get_gpr(vcpu, 5));
break;
}
......@@ -933,6 +963,14 @@ int kvm_xics_create(struct kvm *kvm, u32 type)
xics_debugfs_init(xics);
#ifdef CONFIG_KVM_BOOK3S_64_HV
if (cpu_has_feature(CPU_FTR_ARCH_206)) {
/* Enable real mode support */
xics->real_mode = ENABLE_REALMODE;
xics->real_mode_dbg = DEBUG_REALMODE;
}
#endif /* CONFIG_KVM_BOOK3S_64_HV */
return 0;
}
......
......@@ -64,6 +64,20 @@ struct kvmppc_icp {
unsigned long server_num;
union kvmppc_icp_state state;
unsigned long resend_map[ICP_RESEND_MAP_SIZE];
/* Real mode might find something too hard, here's the action
* it might request from virtual mode
*/
#define XICS_RM_KICK_VCPU 0x1
#define XICS_RM_CHECK_RESEND 0x2
#define XICS_RM_REJECT 0x4
u32 rm_action;
struct kvm_vcpu *rm_kick_target;
u32 rm_reject;
/* Debug stuff for real mode */
union kvmppc_icp_state rm_dbgstate;
struct kvm_vcpu *rm_dbgtgt;
};
struct kvmppc_ics {
......@@ -76,6 +90,8 @@ struct kvmppc_xics {
struct kvm *kvm;
struct dentry *dentry;
u32 max_icsid;
bool real_mode;
bool real_mode_dbg;
struct kvmppc_ics *ics[KVMPPC_XICS_MAX_ICS_ID + 1];
};
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment