mirror of
https://github.com/Dasharo/linux.git
synced 2026-03-06 15:25:10 -08:00
KVM: PPC: Book3S HV: Native usage of the XIVE interrupt controller
This patch makes KVM capable of using the XIVE interrupt controller to provide the standard PAPR "XICS" style hypercalls. It is necessary for proper operations when the host uses XIVE natively. This has been lightly tested on an actual system, including PCI pass-through with a TG3 device. Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org> [mpe: Cleanup pr_xxx(), unsplit pr_xxx() strings, etc., fix build failures by adding KVM_XIVE which depends on KVM_XICS and XIVE, and adding empty stubs for the kvm_xive_xxx() routines, fixup subject, integrate fixes from Paul for building PR=y HV=n] Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
This commit is contained in:
committed by
Michael Ellerman
parent
8bf8f2e8c7
commit
5af5099385
@@ -111,6 +111,8 @@ struct kvmppc_host_state {
|
||||
struct kvm_vcpu *kvm_vcpu;
|
||||
struct kvmppc_vcore *kvm_vcore;
|
||||
void __iomem *xics_phys;
|
||||
void __iomem *xive_tima_phys;
|
||||
void __iomem *xive_tima_virt;
|
||||
u32 saved_xirr;
|
||||
u64 dabr;
|
||||
u64 host_mmcr[7]; /* MMCR 0,1,A, SIAR, SDAR, MMCR2, SIER */
|
||||
|
||||
@@ -205,6 +205,12 @@ struct kvmppc_spapr_tce_table {
|
||||
/* XICS components, defined in book3s_xics.c */
|
||||
struct kvmppc_xics;
|
||||
struct kvmppc_icp;
|
||||
extern struct kvm_device_ops kvm_xics_ops;
|
||||
|
||||
/* XIVE components, defined in book3s_xive.c */
|
||||
struct kvmppc_xive;
|
||||
struct kvmppc_xive_vcpu;
|
||||
extern struct kvm_device_ops kvm_xive_ops;
|
||||
|
||||
struct kvmppc_passthru_irqmap;
|
||||
|
||||
@@ -293,6 +299,7 @@ struct kvm_arch {
|
||||
#endif
|
||||
#ifdef CONFIG_KVM_XICS
|
||||
struct kvmppc_xics *xics;
|
||||
struct kvmppc_xive *xive;
|
||||
struct kvmppc_passthru_irqmap *pimap;
|
||||
#endif
|
||||
struct kvmppc_ops *kvm_ops;
|
||||
@@ -421,7 +428,7 @@ struct kvmppc_passthru_irqmap {
|
||||
|
||||
#define KVMPPC_IRQ_DEFAULT 0
|
||||
#define KVMPPC_IRQ_MPIC 1
|
||||
#define KVMPPC_IRQ_XICS 2
|
||||
#define KVMPPC_IRQ_XICS 2 /* Includes a XIVE option */
|
||||
|
||||
#define MMIO_HPTE_CACHE_SIZE 4
|
||||
|
||||
@@ -443,6 +450,21 @@ struct mmio_hpte_cache {
|
||||
|
||||
struct openpic;
|
||||
|
||||
/* W0 and W1 of a XIVE thread management context */
|
||||
union xive_tma_w01 {
|
||||
struct {
|
||||
u8 nsr;
|
||||
u8 cppr;
|
||||
u8 ipb;
|
||||
u8 lsmfb;
|
||||
u8 ack;
|
||||
u8 inc;
|
||||
u8 age;
|
||||
u8 pipr;
|
||||
};
|
||||
__be64 w01;
|
||||
};
|
||||
|
||||
struct kvm_vcpu_arch {
|
||||
ulong host_stack;
|
||||
u32 host_pid;
|
||||
@@ -688,6 +710,10 @@ struct kvm_vcpu_arch {
|
||||
struct openpic *mpic; /* KVM_IRQ_MPIC */
|
||||
#ifdef CONFIG_KVM_XICS
|
||||
struct kvmppc_icp *icp; /* XICS presentation controller */
|
||||
struct kvmppc_xive_vcpu *xive_vcpu; /* XIVE virtual CPU data */
|
||||
__be32 xive_cam_word; /* Cooked W2 in proper endian with valid bit */
|
||||
u32 xive_pushed; /* Is the VP pushed on the physical CPU ? */
|
||||
union xive_tma_w01 xive_saved_state; /* W0..1 of XIVE thread state */
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_KVM_BOOK3S_HV_POSSIBLE
|
||||
|
||||
@@ -225,6 +225,7 @@ int kvm_vcpu_ioctl_interrupt(struct kvm_vcpu *vcpu, struct kvm_interrupt *irq);
|
||||
extern int kvm_vm_ioctl_rtas_define_token(struct kvm *kvm, void __user *argp);
|
||||
extern int kvmppc_rtas_hcall(struct kvm_vcpu *vcpu);
|
||||
extern void kvmppc_rtas_tokens_free(struct kvm *kvm);
|
||||
|
||||
extern int kvmppc_xics_set_xive(struct kvm *kvm, u32 irq, u32 server,
|
||||
u32 priority);
|
||||
extern int kvmppc_xics_get_xive(struct kvm *kvm, u32 irq, u32 *server,
|
||||
@@ -412,6 +413,14 @@ static inline void kvmppc_set_xics_phys(int cpu, unsigned long addr)
|
||||
paca[cpu].kvm_hstate.xics_phys = (void __iomem *)addr;
|
||||
}
|
||||
|
||||
static inline void kvmppc_set_xive_tima(int cpu,
|
||||
unsigned long phys_addr,
|
||||
void __iomem *virt_addr)
|
||||
{
|
||||
paca[cpu].kvm_hstate.xive_tima_phys = (void __iomem *)phys_addr;
|
||||
paca[cpu].kvm_hstate.xive_tima_virt = virt_addr;
|
||||
}
|
||||
|
||||
static inline u32 kvmppc_get_xics_latch(void)
|
||||
{
|
||||
u32 xirr;
|
||||
@@ -442,6 +451,11 @@ static inline void __init kvm_cma_reserve(void)
|
||||
static inline void kvmppc_set_xics_phys(int cpu, unsigned long addr)
|
||||
{}
|
||||
|
||||
static inline void kvmppc_set_xive_tima(int cpu,
|
||||
unsigned long phys_addr,
|
||||
void __iomem *virt_addr)
|
||||
{}
|
||||
|
||||
static inline u32 kvmppc_get_xics_latch(void)
|
||||
{
|
||||
return 0;
|
||||
@@ -492,6 +506,10 @@ extern long kvmppc_deliver_irq_passthru(struct kvm_vcpu *vcpu, __be32 xirr,
|
||||
struct kvmppc_irq_map *irq_map,
|
||||
struct kvmppc_passthru_irqmap *pimap,
|
||||
bool *again);
|
||||
|
||||
extern int kvmppc_xics_set_irq(struct kvm *kvm, int irq_source_id, u32 irq,
|
||||
int level, bool line_status);
|
||||
|
||||
extern int h_ipi_redirect;
|
||||
#else
|
||||
static inline struct kvmppc_passthru_irqmap *kvmppc_get_passthru_irqmap(
|
||||
@@ -509,6 +527,60 @@ static inline int kvmppc_xics_hcall(struct kvm_vcpu *vcpu, u32 cmd)
|
||||
{ return 0; }
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_KVM_XIVE
|
||||
/*
|
||||
* Below the first "xive" is the "eXternal Interrupt Virtualization Engine"
|
||||
* ie. P9 new interrupt controller, while the second "xive" is the legacy
|
||||
* "eXternal Interrupt Vector Entry" which is the configuration of an
|
||||
* interrupt on the "xics" interrupt controller on P8 and earlier. Those
|
||||
* two function consume or produce a legacy "XIVE" state from the
|
||||
* new "XIVE" interrupt controller.
|
||||
*/
|
||||
extern int kvmppc_xive_set_xive(struct kvm *kvm, u32 irq, u32 server,
|
||||
u32 priority);
|
||||
extern int kvmppc_xive_get_xive(struct kvm *kvm, u32 irq, u32 *server,
|
||||
u32 *priority);
|
||||
extern int kvmppc_xive_int_on(struct kvm *kvm, u32 irq);
|
||||
extern int kvmppc_xive_int_off(struct kvm *kvm, u32 irq);
|
||||
extern void kvmppc_xive_init_module(void);
|
||||
extern void kvmppc_xive_exit_module(void);
|
||||
|
||||
extern int kvmppc_xive_connect_vcpu(struct kvm_device *dev,
|
||||
struct kvm_vcpu *vcpu, u32 cpu);
|
||||
extern void kvmppc_xive_cleanup_vcpu(struct kvm_vcpu *vcpu);
|
||||
extern int kvmppc_xive_set_mapped(struct kvm *kvm, unsigned long guest_irq,
|
||||
struct irq_desc *host_desc);
|
||||
extern int kvmppc_xive_clr_mapped(struct kvm *kvm, unsigned long guest_irq,
|
||||
struct irq_desc *host_desc);
|
||||
extern u64 kvmppc_xive_get_icp(struct kvm_vcpu *vcpu);
|
||||
extern int kvmppc_xive_set_icp(struct kvm_vcpu *vcpu, u64 icpval);
|
||||
|
||||
extern int kvmppc_xive_set_irq(struct kvm *kvm, int irq_source_id, u32 irq,
|
||||
int level, bool line_status);
|
||||
#else
|
||||
static inline int kvmppc_xive_set_xive(struct kvm *kvm, u32 irq, u32 server,
|
||||
u32 priority) { return -1; }
|
||||
static inline int kvmppc_xive_get_xive(struct kvm *kvm, u32 irq, u32 *server,
|
||||
u32 *priority) { return -1; }
|
||||
static inline int kvmppc_xive_int_on(struct kvm *kvm, u32 irq) { return -1; }
|
||||
static inline int kvmppc_xive_int_off(struct kvm *kvm, u32 irq) { return -1; }
|
||||
static inline void kvmppc_xive_init_module(void) { }
|
||||
static inline void kvmppc_xive_exit_module(void) { }
|
||||
|
||||
static inline int kvmppc_xive_connect_vcpu(struct kvm_device *dev,
|
||||
struct kvm_vcpu *vcpu, u32 cpu) { return -EBUSY; }
|
||||
static inline void kvmppc_xive_cleanup_vcpu(struct kvm_vcpu *vcpu) { }
|
||||
static inline int kvmppc_xive_set_mapped(struct kvm *kvm, unsigned long guest_irq,
|
||||
struct irq_desc *host_desc) { return -ENODEV; }
|
||||
static inline int kvmppc_xive_clr_mapped(struct kvm *kvm, unsigned long guest_irq,
|
||||
struct irq_desc *host_desc) { return -ENODEV; }
|
||||
static inline u64 kvmppc_xive_get_icp(struct kvm_vcpu *vcpu) { return 0; }
|
||||
static inline int kvmppc_xive_set_icp(struct kvm_vcpu *vcpu, u64 icpval) { return -ENOENT; }
|
||||
|
||||
static inline int kvmppc_xive_set_irq(struct kvm *kvm, int irq_source_id, u32 irq,
|
||||
int level, bool line_status) { return -ENODEV; }
|
||||
#endif /* CONFIG_KVM_XIVE */
|
||||
|
||||
/*
|
||||
* Prototypes for functions called only from assembler code.
|
||||
* Having prototypes reduces sparse errors.
|
||||
@@ -546,6 +618,8 @@ long kvmppc_h_clear_mod(struct kvm_vcpu *vcpu, unsigned long flags,
|
||||
long kvmppc_hpte_hv_fault(struct kvm_vcpu *vcpu, unsigned long addr,
|
||||
unsigned long slb_v, unsigned int status, bool data);
|
||||
unsigned long kvmppc_rm_h_xirr(struct kvm_vcpu *vcpu);
|
||||
unsigned long kvmppc_rm_h_xirr_x(struct kvm_vcpu *vcpu);
|
||||
unsigned long kvmppc_rm_h_ipoll(struct kvm_vcpu *vcpu, unsigned long server);
|
||||
int kvmppc_rm_h_ipi(struct kvm_vcpu *vcpu, unsigned long server,
|
||||
unsigned long mfrr);
|
||||
int kvmppc_rm_h_cppr(struct kvm_vcpu *vcpu, unsigned long cppr);
|
||||
|
||||
@@ -99,7 +99,6 @@ struct xive_q {
|
||||
#define XIVE_ESB_SET_PQ_01 0xd00
|
||||
#define XIVE_ESB_SET_PQ_10 0xe00
|
||||
#define XIVE_ESB_SET_PQ_11 0xf00
|
||||
#define XIVE_ESB_MASK XIVE_ESB_SET_PQ_01
|
||||
|
||||
#define XIVE_ESB_VAL_P 0x2
|
||||
#define XIVE_ESB_VAL_Q 0x1
|
||||
@@ -136,11 +135,11 @@ extern int xive_native_configure_queue(u32 vp_id, struct xive_q *q, u8 prio,
|
||||
__be32 *qpage, u32 order, bool can_escalate);
|
||||
extern void xive_native_disable_queue(u32 vp_id, struct xive_q *q, u8 prio);
|
||||
|
||||
extern bool __xive_irq_trigger(struct xive_irq_data *xd);
|
||||
extern bool __xive_irq_retrigger(struct xive_irq_data *xd);
|
||||
extern void xive_do_source_eoi(u32 hw_irq, struct xive_irq_data *xd);
|
||||
|
||||
extern void xive_native_sync_source(u32 hw_irq);
|
||||
extern bool is_xive_irq(struct irq_chip *chip);
|
||||
extern int xive_native_enable_vp(u32 vp_id);
|
||||
extern int xive_native_disable_vp(u32 vp_id);
|
||||
extern int xive_native_get_vp_info(u32 vp_id, u32 *out_cam_id, u32 *out_chip_id);
|
||||
|
||||
#else
|
||||
|
||||
|
||||
@@ -630,6 +630,8 @@ int main(void)
|
||||
HSTATE_FIELD(HSTATE_KVM_VCPU, kvm_vcpu);
|
||||
HSTATE_FIELD(HSTATE_KVM_VCORE, kvm_vcore);
|
||||
HSTATE_FIELD(HSTATE_XICS_PHYS, xics_phys);
|
||||
HSTATE_FIELD(HSTATE_XIVE_TIMA_PHYS, xive_tima_phys);
|
||||
HSTATE_FIELD(HSTATE_XIVE_TIMA_VIRT, xive_tima_virt);
|
||||
HSTATE_FIELD(HSTATE_SAVED_XIRR, saved_xirr);
|
||||
HSTATE_FIELD(HSTATE_HOST_IPI, host_ipi);
|
||||
HSTATE_FIELD(HSTATE_PTID, ptid);
|
||||
@@ -715,6 +717,14 @@ int main(void)
|
||||
OFFSET(VCPU_HOST_MAS6, kvm_vcpu, arch.host_mas6);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_KVM_XICS
|
||||
DEFINE(VCPU_XIVE_SAVED_STATE, offsetof(struct kvm_vcpu,
|
||||
arch.xive_saved_state));
|
||||
DEFINE(VCPU_XIVE_CAM_WORD, offsetof(struct kvm_vcpu,
|
||||
arch.xive_cam_word));
|
||||
DEFINE(VCPU_XIVE_PUSHED, offsetof(struct kvm_vcpu, arch.xive_pushed));
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_KVM_EXIT_TIMING
|
||||
OFFSET(VCPU_TIMING_EXIT_TBU, kvm_vcpu, arch.timing_exit.tv32.tbu);
|
||||
OFFSET(VCPU_TIMING_EXIT_TBL, kvm_vcpu, arch.timing_exit.tv32.tbl);
|
||||
|
||||
@@ -196,6 +196,11 @@ config KVM_XICS
|
||||
Specification) interrupt controller architecture used on
|
||||
IBM POWER (pSeries) servers.
|
||||
|
||||
config KVM_XIVE
|
||||
bool
|
||||
default y
|
||||
depends on KVM_XICS && PPC_XIVE_NATIVE && KVM_BOOK3S_HV_POSSIBLE
|
||||
|
||||
source drivers/vhost/Kconfig
|
||||
|
||||
endif # VIRTUALIZATION
|
||||
|
||||
@@ -74,7 +74,7 @@ kvm-hv-y += \
|
||||
book3s_64_mmu_radix.o
|
||||
|
||||
kvm-book3s_64-builtin-xics-objs-$(CONFIG_KVM_XICS) := \
|
||||
book3s_hv_rm_xics.o
|
||||
book3s_hv_rm_xics.o book3s_hv_rm_xive.o
|
||||
|
||||
ifdef CONFIG_KVM_BOOK3S_HV_POSSIBLE
|
||||
kvm-book3s_64-builtin-objs-$(CONFIG_KVM_BOOK3S_64_HANDLER) += \
|
||||
@@ -89,6 +89,8 @@ endif
|
||||
kvm-book3s_64-objs-$(CONFIG_KVM_XICS) += \
|
||||
book3s_xics.o
|
||||
|
||||
kvm-book3s_64-objs-$(CONFIG_KVM_XIVE) += book3s_xive.o
|
||||
|
||||
kvm-book3s_64-module-objs := \
|
||||
$(common-objs-y) \
|
||||
book3s.o \
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
#include <asm/kvm_book3s.h>
|
||||
#include <asm/mmu_context.h>
|
||||
#include <asm/page.h>
|
||||
#include <asm/xive.h>
|
||||
|
||||
#include "book3s.h"
|
||||
#include "trace.h"
|
||||
@@ -578,11 +579,14 @@ int kvmppc_get_one_reg(struct kvm_vcpu *vcpu, u64 id,
|
||||
break;
|
||||
#ifdef CONFIG_KVM_XICS
|
||||
case KVM_REG_PPC_ICP_STATE:
|
||||
if (!vcpu->arch.icp) {
|
||||
if (!vcpu->arch.icp && !vcpu->arch.xive_vcpu) {
|
||||
r = -ENXIO;
|
||||
break;
|
||||
}
|
||||
*val = get_reg_val(id, kvmppc_xics_get_icp(vcpu));
|
||||
if (xive_enabled())
|
||||
*val = get_reg_val(id, kvmppc_xive_get_icp(vcpu));
|
||||
else
|
||||
*val = get_reg_val(id, kvmppc_xics_get_icp(vcpu));
|
||||
break;
|
||||
#endif /* CONFIG_KVM_XICS */
|
||||
case KVM_REG_PPC_FSCR:
|
||||
@@ -648,12 +652,14 @@ int kvmppc_set_one_reg(struct kvm_vcpu *vcpu, u64 id,
|
||||
#endif /* CONFIG_VSX */
|
||||
#ifdef CONFIG_KVM_XICS
|
||||
case KVM_REG_PPC_ICP_STATE:
|
||||
if (!vcpu->arch.icp) {
|
||||
if (!vcpu->arch.icp && !vcpu->arch.xive_vcpu) {
|
||||
r = -ENXIO;
|
||||
break;
|
||||
}
|
||||
r = kvmppc_xics_set_icp(vcpu,
|
||||
set_reg_val(id, *val));
|
||||
if (xive_enabled())
|
||||
r = kvmppc_xive_set_icp(vcpu, set_reg_val(id, *val));
|
||||
else
|
||||
r = kvmppc_xics_set_icp(vcpu, set_reg_val(id, *val));
|
||||
break;
|
||||
#endif /* CONFIG_KVM_XICS */
|
||||
case KVM_REG_PPC_FSCR:
|
||||
@@ -924,6 +930,50 @@ int kvmppc_book3s_hcall_implemented(struct kvm *kvm, unsigned long hcall)
|
||||
return kvm->arch.kvm_ops->hcall_implemented(hcall);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_KVM_XICS
|
||||
int kvm_set_irq(struct kvm *kvm, int irq_source_id, u32 irq, int level,
|
||||
bool line_status)
|
||||
{
|
||||
if (xive_enabled())
|
||||
return kvmppc_xive_set_irq(kvm, irq_source_id, irq, level,
|
||||
line_status);
|
||||
else
|
||||
return kvmppc_xics_set_irq(kvm, irq_source_id, irq, level,
|
||||
line_status);
|
||||
}
|
||||
|
||||
int kvm_arch_set_irq_inatomic(struct kvm_kernel_irq_routing_entry *irq_entry,
|
||||
struct kvm *kvm, int irq_source_id,
|
||||
int level, bool line_status)
|
||||
{
|
||||
return kvm_set_irq(kvm, irq_source_id, irq_entry->gsi,
|
||||
level, line_status);
|
||||
}
|
||||
static int kvmppc_book3s_set_irq(struct kvm_kernel_irq_routing_entry *e,
|
||||
struct kvm *kvm, int irq_source_id, int level,
|
||||
bool line_status)
|
||||
{
|
||||
return kvm_set_irq(kvm, irq_source_id, e->gsi, level, line_status);
|
||||
}
|
||||
|
||||
int kvm_irq_map_gsi(struct kvm *kvm,
|
||||
struct kvm_kernel_irq_routing_entry *entries, int gsi)
|
||||
{
|
||||
entries->gsi = gsi;
|
||||
entries->type = KVM_IRQ_ROUTING_IRQCHIP;
|
||||
entries->set = kvmppc_book3s_set_irq;
|
||||
entries->irqchip.irqchip = 0;
|
||||
entries->irqchip.pin = gsi;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int kvm_irq_map_chip_pin(struct kvm *kvm, unsigned irqchip, unsigned pin)
|
||||
{
|
||||
return pin;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_KVM_XICS */
|
||||
|
||||
static int kvmppc_book3s_init(void)
|
||||
{
|
||||
int r;
|
||||
@@ -934,12 +984,25 @@ static int kvmppc_book3s_init(void)
|
||||
#ifdef CONFIG_KVM_BOOK3S_32_HANDLER
|
||||
r = kvmppc_book3s_init_pr();
|
||||
#endif
|
||||
return r;
|
||||
|
||||
#ifdef CONFIG_KVM_XICS
|
||||
#ifdef CONFIG_KVM_XIVE
|
||||
if (xive_enabled()) {
|
||||
kvmppc_xive_init_module();
|
||||
kvm_register_device_ops(&kvm_xive_ops, KVM_DEV_TYPE_XICS);
|
||||
} else
|
||||
#endif
|
||||
kvm_register_device_ops(&kvm_xics_ops, KVM_DEV_TYPE_XICS);
|
||||
#endif
|
||||
return r;
|
||||
}
|
||||
|
||||
static void kvmppc_book3s_exit(void)
|
||||
{
|
||||
#ifdef CONFIG_KVM_XICS
|
||||
if (xive_enabled())
|
||||
kvmppc_xive_exit_module();
|
||||
#endif
|
||||
#ifdef CONFIG_KVM_BOOK3S_32_HANDLER
|
||||
kvmppc_book3s_exit_pr();
|
||||
#endif
|
||||
|
||||
@@ -67,6 +67,7 @@
|
||||
#include <asm/mmu.h>
|
||||
#include <asm/opal.h>
|
||||
#include <asm/xics.h>
|
||||
#include <asm/xive.h>
|
||||
|
||||
#include "book3s.h"
|
||||
|
||||
@@ -837,6 +838,10 @@ int kvmppc_pseries_do_hcall(struct kvm_vcpu *vcpu)
|
||||
case H_IPOLL:
|
||||
case H_XIRR_X:
|
||||
if (kvmppc_xics_enabled(vcpu)) {
|
||||
if (xive_enabled()) {
|
||||
ret = H_NOT_AVAILABLE;
|
||||
return RESUME_GUEST;
|
||||
}
|
||||
ret = kvmppc_xics_hcall(vcpu, req);
|
||||
break;
|
||||
}
|
||||
@@ -2947,8 +2952,12 @@ static int kvmppc_vcpu_run_hv(struct kvm_run *run, struct kvm_vcpu *vcpu)
|
||||
r = kvmppc_book3s_hv_page_fault(run, vcpu,
|
||||
vcpu->arch.fault_dar, vcpu->arch.fault_dsisr);
|
||||
srcu_read_unlock(&vcpu->kvm->srcu, srcu_idx);
|
||||
} else if (r == RESUME_PASSTHROUGH)
|
||||
r = kvmppc_xics_rm_complete(vcpu, 0);
|
||||
} else if (r == RESUME_PASSTHROUGH) {
|
||||
if (WARN_ON(xive_enabled()))
|
||||
r = H_SUCCESS;
|
||||
else
|
||||
r = kvmppc_xics_rm_complete(vcpu, 0);
|
||||
}
|
||||
} while (is_kvmppc_resume_guest(r));
|
||||
|
||||
out:
|
||||
@@ -3400,10 +3409,20 @@ static int kvmppc_core_init_vm_hv(struct kvm *kvm)
|
||||
/*
|
||||
* On POWER9, VPM0 bit is reserved (VPM0=1 behaviour is assumed)
|
||||
* Set HVICE bit to enable hypervisor virtualization interrupts.
|
||||
* Set HEIC to prevent OS interrupts to go to hypervisor (should
|
||||
* be unnecessary but better safe than sorry in case we re-enable
|
||||
* EE in HV mode with this LPCR still set)
|
||||
*/
|
||||
if (cpu_has_feature(CPU_FTR_ARCH_300)) {
|
||||
lpcr &= ~LPCR_VPM0;
|
||||
lpcr |= LPCR_HVICE;
|
||||
lpcr |= LPCR_HVICE | LPCR_HEIC;
|
||||
|
||||
/*
|
||||
* If xive is enabled, we route 0x500 interrupts directly
|
||||
* to the guest.
|
||||
*/
|
||||
if (xive_enabled())
|
||||
lpcr |= LPCR_LPES;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -3533,7 +3552,7 @@ static int kvmppc_set_passthru_irq(struct kvm *kvm, int host_irq, int guest_gsi)
|
||||
struct kvmppc_irq_map *irq_map;
|
||||
struct kvmppc_passthru_irqmap *pimap;
|
||||
struct irq_chip *chip;
|
||||
int i;
|
||||
int i, rc = 0;
|
||||
|
||||
if (!kvm_irq_bypass)
|
||||
return 1;
|
||||
@@ -3558,10 +3577,10 @@ static int kvmppc_set_passthru_irq(struct kvm *kvm, int host_irq, int guest_gsi)
|
||||
/*
|
||||
* For now, we only support interrupts for which the EOI operation
|
||||
* is an OPAL call followed by a write to XIRR, since that's
|
||||
* what our real-mode EOI code does.
|
||||
* what our real-mode EOI code does, or a XIVE interrupt
|
||||
*/
|
||||
chip = irq_data_get_irq_chip(&desc->irq_data);
|
||||
if (!chip || !is_pnv_opal_msi(chip)) {
|
||||
if (!chip || !(is_pnv_opal_msi(chip) || is_xive_irq(chip))) {
|
||||
pr_warn("kvmppc_set_passthru_irq_hv: Could not assign IRQ map for (%d,%d)\n",
|
||||
host_irq, guest_gsi);
|
||||
mutex_unlock(&kvm->lock);
|
||||
@@ -3603,7 +3622,12 @@ static int kvmppc_set_passthru_irq(struct kvm *kvm, int host_irq, int guest_gsi)
|
||||
if (i == pimap->n_mapped)
|
||||
pimap->n_mapped++;
|
||||
|
||||
kvmppc_xics_set_mapped(kvm, guest_gsi, desc->irq_data.hwirq);
|
||||
if (xive_enabled())
|
||||
rc = kvmppc_xive_set_mapped(kvm, guest_gsi, desc);
|
||||
else
|
||||
kvmppc_xics_set_mapped(kvm, guest_gsi, desc->irq_data.hwirq);
|
||||
if (rc)
|
||||
irq_map->r_hwirq = 0;
|
||||
|
||||
mutex_unlock(&kvm->lock);
|
||||
|
||||
@@ -3614,7 +3638,7 @@ static int kvmppc_clr_passthru_irq(struct kvm *kvm, int host_irq, int guest_gsi)
|
||||
{
|
||||
struct irq_desc *desc;
|
||||
struct kvmppc_passthru_irqmap *pimap;
|
||||
int i;
|
||||
int i, rc = 0;
|
||||
|
||||
if (!kvm_irq_bypass)
|
||||
return 0;
|
||||
@@ -3641,9 +3665,12 @@ static int kvmppc_clr_passthru_irq(struct kvm *kvm, int host_irq, int guest_gsi)
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
kvmppc_xics_clr_mapped(kvm, guest_gsi, pimap->mapped[i].r_hwirq);
|
||||
if (xive_enabled())
|
||||
rc = kvmppc_xive_clr_mapped(kvm, guest_gsi, pimap->mapped[i].desc);
|
||||
else
|
||||
kvmppc_xics_clr_mapped(kvm, guest_gsi, pimap->mapped[i].r_hwirq);
|
||||
|
||||
/* invalidate the entry */
|
||||
/* invalidate the entry (what do do on error from the above ?) */
|
||||
pimap->mapped[i].r_hwirq = 0;
|
||||
|
||||
/*
|
||||
@@ -3652,7 +3679,7 @@ static int kvmppc_clr_passthru_irq(struct kvm *kvm, int host_irq, int guest_gsi)
|
||||
*/
|
||||
|
||||
mutex_unlock(&kvm->lock);
|
||||
return 0;
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int kvmppc_irq_bypass_add_producer_hv(struct irq_bypass_consumer *cons,
|
||||
@@ -3930,7 +3957,7 @@ static int kvmppc_book3s_init_hv(void)
|
||||
* indirectly, via OPAL.
|
||||
*/
|
||||
#ifdef CONFIG_SMP
|
||||
if (!get_paca()->kvm_hstate.xics_phys) {
|
||||
if (!xive_enabled() && !get_paca()->kvm_hstate.xics_phys) {
|
||||
struct device_node *np;
|
||||
|
||||
np = of_find_compatible_node(NULL, NULL, "ibm,opal-intc");
|
||||
|
||||
@@ -32,6 +32,24 @@
|
||||
|
||||
#define KVM_CMA_CHUNK_ORDER 18
|
||||
|
||||
#include "book3s_xics.h"
|
||||
#include "book3s_xive.h"
|
||||
|
||||
/*
|
||||
* The XIVE module will populate these when it loads
|
||||
*/
|
||||
unsigned long (*__xive_vm_h_xirr)(struct kvm_vcpu *vcpu);
|
||||
unsigned long (*__xive_vm_h_ipoll)(struct kvm_vcpu *vcpu, unsigned long server);
|
||||
int (*__xive_vm_h_ipi)(struct kvm_vcpu *vcpu, unsigned long server,
|
||||
unsigned long mfrr);
|
||||
int (*__xive_vm_h_cppr)(struct kvm_vcpu *vcpu, unsigned long cppr);
|
||||
int (*__xive_vm_h_eoi)(struct kvm_vcpu *vcpu, unsigned long xirr);
|
||||
EXPORT_SYMBOL_GPL(__xive_vm_h_xirr);
|
||||
EXPORT_SYMBOL_GPL(__xive_vm_h_ipoll);
|
||||
EXPORT_SYMBOL_GPL(__xive_vm_h_ipi);
|
||||
EXPORT_SYMBOL_GPL(__xive_vm_h_cppr);
|
||||
EXPORT_SYMBOL_GPL(__xive_vm_h_eoi);
|
||||
|
||||
/*
|
||||
* Hash page table alignment on newer cpus(CPU_FTR_ARCH_206)
|
||||
* should be power of 2.
|
||||
@@ -210,6 +228,7 @@ void kvmhv_rm_send_ipi(int cpu)
|
||||
__asm__ __volatile__ (PPC_MSGSND(%0) : : "r" (msg));
|
||||
return;
|
||||
}
|
||||
|
||||
/* On POWER8 for IPIs to threads in the same core, use msgsnd. */
|
||||
if (cpu_has_feature(CPU_FTR_ARCH_207S) &&
|
||||
cpu_first_thread_sibling(cpu) ==
|
||||
@@ -406,6 +425,9 @@ static long kvmppc_read_one_intr(bool *again)
|
||||
u8 host_ipi;
|
||||
int64_t rc;
|
||||
|
||||
if (xive_enabled())
|
||||
return 1;
|
||||
|
||||
/* see if a host IPI is pending */
|
||||
host_ipi = local_paca->kvm_hstate.host_ipi;
|
||||
if (host_ipi)
|
||||
@@ -490,3 +512,84 @@ static long kvmppc_read_one_intr(bool *again)
|
||||
|
||||
return kvmppc_check_passthru(xisr, xirr, again);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_KVM_XICS
|
||||
static inline bool is_rm(void)
|
||||
{
|
||||
return !(mfmsr() & MSR_DR);
|
||||
}
|
||||
|
||||
unsigned long kvmppc_rm_h_xirr(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
if (xive_enabled()) {
|
||||
if (is_rm())
|
||||
return xive_rm_h_xirr(vcpu);
|
||||
if (unlikely(!__xive_vm_h_xirr))
|
||||
return H_NOT_AVAILABLE;
|
||||
return __xive_vm_h_xirr(vcpu);
|
||||
} else
|
||||
return xics_rm_h_xirr(vcpu);
|
||||
}
|
||||
|
||||
unsigned long kvmppc_rm_h_xirr_x(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
vcpu->arch.gpr[5] = get_tb();
|
||||
if (xive_enabled()) {
|
||||
if (is_rm())
|
||||
return xive_rm_h_xirr(vcpu);
|
||||
if (unlikely(!__xive_vm_h_xirr))
|
||||
return H_NOT_AVAILABLE;
|
||||
return __xive_vm_h_xirr(vcpu);
|
||||
} else
|
||||
return xics_rm_h_xirr(vcpu);
|
||||
}
|
||||
|
||||
unsigned long kvmppc_rm_h_ipoll(struct kvm_vcpu *vcpu, unsigned long server)
|
||||
{
|
||||
if (xive_enabled()) {
|
||||
if (is_rm())
|
||||
return xive_rm_h_ipoll(vcpu, server);
|
||||
if (unlikely(!__xive_vm_h_ipoll))
|
||||
return H_NOT_AVAILABLE;
|
||||
return __xive_vm_h_ipoll(vcpu, server);
|
||||
} else
|
||||
return H_TOO_HARD;
|
||||
}
|
||||
|
||||
int kvmppc_rm_h_ipi(struct kvm_vcpu *vcpu, unsigned long server,
|
||||
unsigned long mfrr)
|
||||
{
|
||||
if (xive_enabled()) {
|
||||
if (is_rm())
|
||||
return xive_rm_h_ipi(vcpu, server, mfrr);
|
||||
if (unlikely(!__xive_vm_h_ipi))
|
||||
return H_NOT_AVAILABLE;
|
||||
return __xive_vm_h_ipi(vcpu, server, mfrr);
|
||||
} else
|
||||
return xics_rm_h_ipi(vcpu, server, mfrr);
|
||||
}
|
||||
|
||||
int kvmppc_rm_h_cppr(struct kvm_vcpu *vcpu, unsigned long cppr)
|
||||
{
|
||||
if (xive_enabled()) {
|
||||
if (is_rm())
|
||||
return xive_rm_h_cppr(vcpu, cppr);
|
||||
if (unlikely(!__xive_vm_h_cppr))
|
||||
return H_NOT_AVAILABLE;
|
||||
return __xive_vm_h_cppr(vcpu, cppr);
|
||||
} else
|
||||
return xics_rm_h_cppr(vcpu, cppr);
|
||||
}
|
||||
|
||||
int kvmppc_rm_h_eoi(struct kvm_vcpu *vcpu, unsigned long xirr)
|
||||
{
|
||||
if (xive_enabled()) {
|
||||
if (is_rm())
|
||||
return xive_rm_h_eoi(vcpu, xirr);
|
||||
if (unlikely(!__xive_vm_h_eoi))
|
||||
return H_NOT_AVAILABLE;
|
||||
return __xive_vm_h_eoi(vcpu, xirr);
|
||||
} else
|
||||
return xics_rm_h_eoi(vcpu, xirr);
|
||||
}
|
||||
#endif /* CONFIG_KVM_XICS */
|
||||
|
||||
@@ -485,7 +485,7 @@ static void icp_rm_down_cppr(struct kvmppc_xics *xics, struct kvmppc_icp *icp,
|
||||
}
|
||||
|
||||
|
||||
unsigned long kvmppc_rm_h_xirr(struct kvm_vcpu *vcpu)
|
||||
unsigned long xics_rm_h_xirr(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
union kvmppc_icp_state old_state, new_state;
|
||||
struct kvmppc_xics *xics = vcpu->kvm->arch.xics;
|
||||
@@ -523,8 +523,8 @@ unsigned long kvmppc_rm_h_xirr(struct kvm_vcpu *vcpu)
|
||||
return check_too_hard(xics, icp);
|
||||
}
|
||||
|
||||
int kvmppc_rm_h_ipi(struct kvm_vcpu *vcpu, unsigned long server,
|
||||
unsigned long mfrr)
|
||||
int xics_rm_h_ipi(struct kvm_vcpu *vcpu, unsigned long server,
|
||||
unsigned long mfrr)
|
||||
{
|
||||
union kvmppc_icp_state old_state, new_state;
|
||||
struct kvmppc_xics *xics = vcpu->kvm->arch.xics;
|
||||
@@ -610,7 +610,7 @@ int kvmppc_rm_h_ipi(struct kvm_vcpu *vcpu, unsigned long server,
|
||||
return check_too_hard(xics, this_icp);
|
||||
}
|
||||
|
||||
int kvmppc_rm_h_cppr(struct kvm_vcpu *vcpu, unsigned long cppr)
|
||||
int xics_rm_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;
|
||||
@@ -730,7 +730,7 @@ static int ics_rm_eoi(struct kvm_vcpu *vcpu, u32 irq)
|
||||
return check_too_hard(xics, icp);
|
||||
}
|
||||
|
||||
int kvmppc_rm_h_eoi(struct kvm_vcpu *vcpu, unsigned long xirr)
|
||||
int xics_rm_h_eoi(struct kvm_vcpu *vcpu, unsigned long xirr)
|
||||
{
|
||||
struct kvmppc_xics *xics = vcpu->kvm->arch.xics;
|
||||
struct kvmppc_icp *icp = vcpu->arch.icp;
|
||||
|
||||
47
arch/powerpc/kvm/book3s_hv_rm_xive.c
Normal file
47
arch/powerpc/kvm/book3s_hv_rm_xive.c
Normal file
@@ -0,0 +1,47 @@
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/kvm_host.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/kernel_stat.h>
|
||||
|
||||
#include <asm/kvm_book3s.h>
|
||||
#include <asm/kvm_ppc.h>
|
||||
#include <asm/hvcall.h>
|
||||
#include <asm/xics.h>
|
||||
#include <asm/debug.h>
|
||||
#include <asm/synch.h>
|
||||
#include <asm/cputhreads.h>
|
||||
#include <asm/pgtable.h>
|
||||
#include <asm/ppc-opcode.h>
|
||||
#include <asm/pnv-pci.h>
|
||||
#include <asm/opal.h>
|
||||
#include <asm/smp.h>
|
||||
#include <asm/asm-prototypes.h>
|
||||
#include <asm/xive.h>
|
||||
#include <asm/xive-regs.h>
|
||||
|
||||
#include "book3s_xive.h"
|
||||
|
||||
/* XXX */
|
||||
#include <asm/udbg.h>
|
||||
//#define DBG(fmt...) udbg_printf(fmt)
|
||||
#define DBG(fmt...) do { } while(0)
|
||||
|
||||
static inline void __iomem *get_tima_phys(void)
|
||||
{
|
||||
return local_paca->kvm_hstate.xive_tima_phys;
|
||||
}
|
||||
|
||||
#undef XIVE_RUNTIME_CHECKS
|
||||
#define X_PFX xive_rm_
|
||||
#define X_STATIC
|
||||
#define X_STAT_PFX stat_rm_
|
||||
#define __x_tima get_tima_phys()
|
||||
#define __x_eoi_page(xd) ((void __iomem *)((xd)->eoi_page))
|
||||
#define __x_trig_page(xd) ((void __iomem *)((xd)->trig_page))
|
||||
#define __x_readb __raw_rm_readb
|
||||
#define __x_writeb __raw_rm_writeb
|
||||
#define __x_readw __raw_rm_readw
|
||||
#define __x_readq __raw_rm_readq
|
||||
#define __x_writeq __raw_rm_writeq
|
||||
|
||||
#include "book3s_xive_template.c"
|
||||
@@ -30,6 +30,7 @@
|
||||
#include <asm/book3s/64/mmu-hash.h>
|
||||
#include <asm/tm.h>
|
||||
#include <asm/opal.h>
|
||||
#include <asm/xive-regs.h>
|
||||
|
||||
#define VCPU_GPRS_TM(reg) (((reg) * ULONG_SIZE) + VCPU_GPR_TM)
|
||||
|
||||
@@ -970,6 +971,23 @@ ALT_FTR_SECTION_END_IFCLR(CPU_FTR_ARCH_300)
|
||||
cmpwi r3, 512 /* 1 microsecond */
|
||||
blt hdec_soon
|
||||
|
||||
#ifdef CONFIG_KVM_XICS
|
||||
/* We are entering the guest on that thread, push VCPU to XIVE */
|
||||
ld r10, HSTATE_XIVE_TIMA_PHYS(r13)
|
||||
cmpldi cr0, r10, r0
|
||||
beq no_xive
|
||||
ld r11, VCPU_XIVE_SAVED_STATE(r4)
|
||||
li r9, TM_QW1_OS
|
||||
stdcix r11,r9,r10
|
||||
eieio
|
||||
lwz r11, VCPU_XIVE_CAM_WORD(r4)
|
||||
li r9, TM_QW1_OS + TM_WORD2
|
||||
stwcix r11,r9,r10
|
||||
li r9, 1
|
||||
stw r9, VCPU_XIVE_PUSHED(r4)
|
||||
no_xive:
|
||||
#endif /* CONFIG_KVM_XICS */
|
||||
|
||||
deliver_guest_interrupt:
|
||||
ld r6, VCPU_CTR(r4)
|
||||
ld r7, VCPU_XER(r4)
|
||||
@@ -1307,6 +1325,42 @@ END_FTR_SECTION_IFSET(CPU_FTR_HAS_PPR)
|
||||
blt deliver_guest_interrupt
|
||||
|
||||
guest_exit_cont: /* r9 = vcpu, r12 = trap, r13 = paca */
|
||||
#ifdef CONFIG_KVM_XICS
|
||||
/* We are exiting, pull the VP from the XIVE */
|
||||
lwz r0, VCPU_XIVE_PUSHED(r9)
|
||||
cmpwi cr0, r0, 0
|
||||
beq 1f
|
||||
li r7, TM_SPC_PULL_OS_CTX
|
||||
li r6, TM_QW1_OS
|
||||
mfmsr r0
|
||||
andi. r0, r0, MSR_IR /* in real mode? */
|
||||
beq 2f
|
||||
ld r10, HSTATE_XIVE_TIMA_VIRT(r13)
|
||||
cmpldi cr0, r10, 0
|
||||
beq 1f
|
||||
/* First load to pull the context, we ignore the value */
|
||||
lwzx r11, r7, r10
|
||||
eieio
|
||||
/* Second load to recover the context state (Words 0 and 1) */
|
||||
ldx r11, r6, r10
|
||||
b 3f
|
||||
2: ld r10, HSTATE_XIVE_TIMA_PHYS(r13)
|
||||
cmpldi cr0, r10, 0
|
||||
beq 1f
|
||||
/* First load to pull the context, we ignore the value */
|
||||
lwzcix r11, r7, r10
|
||||
eieio
|
||||
/* Second load to recover the context state (Words 0 and 1) */
|
||||
ldcix r11, r6, r10
|
||||
3: std r11, VCPU_XIVE_SAVED_STATE(r9)
|
||||
/* Fixup some of the state for the next load */
|
||||
li r10, 0
|
||||
li r0, 0xff
|
||||
stw r10, VCPU_XIVE_PUSHED(r9)
|
||||
stb r10, (VCPU_XIVE_SAVED_STATE+3)(r9)
|
||||
stb r0, (VCPU_XIVE_SAVED_STATE+4)(r9)
|
||||
1:
|
||||
#endif /* CONFIG_KVM_XICS */
|
||||
/* Save more register state */
|
||||
mfdar r6
|
||||
mfdsisr r7
|
||||
@@ -2011,7 +2065,7 @@ hcall_real_table:
|
||||
.long DOTSYM(kvmppc_rm_h_eoi) - hcall_real_table
|
||||
.long DOTSYM(kvmppc_rm_h_cppr) - hcall_real_table
|
||||
.long DOTSYM(kvmppc_rm_h_ipi) - hcall_real_table
|
||||
.long 0 /* 0x70 - H_IPOLL */
|
||||
.long DOTSYM(kvmppc_rm_h_ipoll) - hcall_real_table
|
||||
.long DOTSYM(kvmppc_rm_h_xirr) - hcall_real_table
|
||||
#else
|
||||
.long 0 /* 0x64 - H_EOI */
|
||||
@@ -2181,7 +2235,11 @@ hcall_real_table:
|
||||
.long 0 /* 0x2f0 */
|
||||
.long 0 /* 0x2f4 */
|
||||
.long 0 /* 0x2f8 */
|
||||
.long 0 /* 0x2fc */
|
||||
#ifdef CONFIG_KVM_XICS
|
||||
.long DOTSYM(kvmppc_rm_h_xirr_x) - hcall_real_table
|
||||
#else
|
||||
.long 0 /* 0x2fc - H_XIRR_X*/
|
||||
#endif
|
||||
.long DOTSYM(kvmppc_h_random) - hcall_real_table
|
||||
.globl hcall_real_table_end
|
||||
hcall_real_table_end:
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include <asm/kvm_ppc.h>
|
||||
#include <asm/hvcall.h>
|
||||
#include <asm/rtas.h>
|
||||
#include <asm/xive.h>
|
||||
|
||||
#ifdef CONFIG_KVM_XICS
|
||||
static void kvm_rtas_set_xive(struct kvm_vcpu *vcpu, struct rtas_args *args)
|
||||
@@ -32,7 +33,10 @@ static void kvm_rtas_set_xive(struct kvm_vcpu *vcpu, struct rtas_args *args)
|
||||
server = be32_to_cpu(args->args[1]);
|
||||
priority = be32_to_cpu(args->args[2]);
|
||||
|
||||
rc = kvmppc_xics_set_xive(vcpu->kvm, irq, server, priority);
|
||||
if (xive_enabled())
|
||||
rc = kvmppc_xive_set_xive(vcpu->kvm, irq, server, priority);
|
||||
else
|
||||
rc = kvmppc_xics_set_xive(vcpu->kvm, irq, server, priority);
|
||||
if (rc)
|
||||
rc = -3;
|
||||
out:
|
||||
@@ -52,7 +56,10 @@ static void kvm_rtas_get_xive(struct kvm_vcpu *vcpu, struct rtas_args *args)
|
||||
irq = be32_to_cpu(args->args[0]);
|
||||
|
||||
server = priority = 0;
|
||||
rc = kvmppc_xics_get_xive(vcpu->kvm, irq, &server, &priority);
|
||||
if (xive_enabled())
|
||||
rc = kvmppc_xive_get_xive(vcpu->kvm, irq, &server, &priority);
|
||||
else
|
||||
rc = kvmppc_xics_get_xive(vcpu->kvm, irq, &server, &priority);
|
||||
if (rc) {
|
||||
rc = -3;
|
||||
goto out;
|
||||
@@ -76,7 +83,10 @@ static void kvm_rtas_int_off(struct kvm_vcpu *vcpu, struct rtas_args *args)
|
||||
|
||||
irq = be32_to_cpu(args->args[0]);
|
||||
|
||||
rc = kvmppc_xics_int_off(vcpu->kvm, irq);
|
||||
if (xive_enabled())
|
||||
rc = kvmppc_xive_int_off(vcpu->kvm, irq);
|
||||
else
|
||||
rc = kvmppc_xics_int_off(vcpu->kvm, irq);
|
||||
if (rc)
|
||||
rc = -3;
|
||||
out:
|
||||
@@ -95,7 +105,10 @@ static void kvm_rtas_int_on(struct kvm_vcpu *vcpu, struct rtas_args *args)
|
||||
|
||||
irq = be32_to_cpu(args->args[0]);
|
||||
|
||||
rc = kvmppc_xics_int_on(vcpu->kvm, irq);
|
||||
if (xive_enabled())
|
||||
rc = kvmppc_xive_int_on(vcpu->kvm, irq);
|
||||
else
|
||||
rc = kvmppc_xics_int_on(vcpu->kvm, irq);
|
||||
if (rc)
|
||||
rc = -3;
|
||||
out:
|
||||
|
||||
@@ -1307,8 +1307,8 @@ static int xics_set_source(struct kvmppc_xics *xics, long irq, u64 addr)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int kvm_set_irq(struct kvm *kvm, int irq_source_id, u32 irq, int level,
|
||||
bool line_status)
|
||||
int kvmppc_xics_set_irq(struct kvm *kvm, int irq_source_id, u32 irq, int level,
|
||||
bool line_status)
|
||||
{
|
||||
struct kvmppc_xics *xics = kvm->arch.xics;
|
||||
|
||||
@@ -1317,14 +1317,6 @@ int kvm_set_irq(struct kvm *kvm, int irq_source_id, u32 irq, int level,
|
||||
return ics_deliver_irq(xics, irq, level);
|
||||
}
|
||||
|
||||
int kvm_arch_set_irq_inatomic(struct kvm_kernel_irq_routing_entry *irq_entry,
|
||||
struct kvm *kvm, int irq_source_id,
|
||||
int level, bool line_status)
|
||||
{
|
||||
return kvm_set_irq(kvm, irq_source_id, irq_entry->gsi,
|
||||
level, line_status);
|
||||
}
|
||||
|
||||
static int xics_set_attr(struct kvm_device *dev, struct kvm_device_attr *attr)
|
||||
{
|
||||
struct kvmppc_xics *xics = dev->private;
|
||||
@@ -1458,29 +1450,6 @@ void kvmppc_xics_free_icp(struct kvm_vcpu *vcpu)
|
||||
vcpu->arch.irq_type = KVMPPC_IRQ_DEFAULT;
|
||||
}
|
||||
|
||||
static int xics_set_irq(struct kvm_kernel_irq_routing_entry *e,
|
||||
struct kvm *kvm, int irq_source_id, int level,
|
||||
bool line_status)
|
||||
{
|
||||
return kvm_set_irq(kvm, irq_source_id, e->gsi, level, line_status);
|
||||
}
|
||||
|
||||
int kvm_irq_map_gsi(struct kvm *kvm,
|
||||
struct kvm_kernel_irq_routing_entry *entries, int gsi)
|
||||
{
|
||||
entries->gsi = gsi;
|
||||
entries->type = KVM_IRQ_ROUTING_IRQCHIP;
|
||||
entries->set = xics_set_irq;
|
||||
entries->irqchip.irqchip = 0;
|
||||
entries->irqchip.pin = gsi;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int kvm_irq_map_chip_pin(struct kvm *kvm, unsigned irqchip, unsigned pin)
|
||||
{
|
||||
return pin;
|
||||
}
|
||||
|
||||
void kvmppc_xics_set_mapped(struct kvm *kvm, unsigned long irq,
|
||||
unsigned long host_irq)
|
||||
{
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#ifndef _KVM_PPC_BOOK3S_XICS_H
|
||||
#define _KVM_PPC_BOOK3S_XICS_H
|
||||
|
||||
#ifdef CONFIG_KVM_XICS
|
||||
/*
|
||||
* We use a two-level tree to store interrupt source information.
|
||||
* There are up to 1024 ICS nodes, each of which can represent
|
||||
@@ -144,5 +145,11 @@ static inline struct kvmppc_ics *kvmppc_xics_find_ics(struct kvmppc_xics *xics,
|
||||
return ics;
|
||||
}
|
||||
|
||||
extern unsigned long xics_rm_h_xirr(struct kvm_vcpu *vcpu);
|
||||
extern int xics_rm_h_ipi(struct kvm_vcpu *vcpu, unsigned long server,
|
||||
unsigned long mfrr);
|
||||
extern int xics_rm_h_cppr(struct kvm_vcpu *vcpu, unsigned long cppr);
|
||||
extern int xics_rm_h_eoi(struct kvm_vcpu *vcpu, unsigned long xirr);
|
||||
|
||||
#endif /* CONFIG_KVM_XICS */
|
||||
#endif /* _KVM_PPC_BOOK3S_XICS_H */
|
||||
|
||||
1893
arch/powerpc/kvm/book3s_xive.c
Normal file
1893
arch/powerpc/kvm/book3s_xive.c
Normal file
File diff suppressed because it is too large
Load Diff
256
arch/powerpc/kvm/book3s_xive.h
Normal file
256
arch/powerpc/kvm/book3s_xive.h
Normal file
@@ -0,0 +1,256 @@
|
||||
/*
|
||||
* Copyright 2017 Benjamin Herrenschmidt, IBM Corporation
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License, version 2, as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#ifndef _KVM_PPC_BOOK3S_XIVE_H
|
||||
#define _KVM_PPC_BOOK3S_XIVE_H
|
||||
|
||||
#ifdef CONFIG_KVM_XICS
|
||||
#include "book3s_xics.h"
|
||||
|
||||
/*
|
||||
* State for one guest irq source.
|
||||
*
|
||||
* For each guest source we allocate a HW interrupt in the XIVE
|
||||
* which we use for all SW triggers. It will be unused for
|
||||
* pass-through but it's easier to keep around as the same
|
||||
* guest interrupt can alternatively be emulated or pass-through
|
||||
* if a physical device is hot unplugged and replaced with an
|
||||
* emulated one.
|
||||
*
|
||||
* This state structure is very similar to the XICS one with
|
||||
* additional XIVE specific tracking.
|
||||
*/
|
||||
struct kvmppc_xive_irq_state {
|
||||
bool valid; /* Interrupt entry is valid */
|
||||
|
||||
u32 number; /* Guest IRQ number */
|
||||
u32 ipi_number; /* XIVE IPI HW number */
|
||||
struct xive_irq_data ipi_data; /* XIVE IPI associated data */
|
||||
u32 pt_number; /* XIVE Pass-through number if any */
|
||||
struct xive_irq_data *pt_data; /* XIVE Pass-through associated data */
|
||||
|
||||
/* Targetting as set by guest */
|
||||
u32 guest_server; /* Current guest selected target */
|
||||
u8 guest_priority; /* Guest set priority */
|
||||
u8 saved_priority; /* Saved priority when masking */
|
||||
|
||||
/* Actual targetting */
|
||||
u32 act_server; /* Actual server */
|
||||
u8 act_priority; /* Actual priority */
|
||||
|
||||
/* Various state bits */
|
||||
bool in_eoi; /* Synchronize with H_EOI */
|
||||
bool old_p; /* P bit state when masking */
|
||||
bool old_q; /* Q bit state when masking */
|
||||
bool lsi; /* level-sensitive interrupt */
|
||||
bool asserted; /* Only for emulated LSI: current state */
|
||||
|
||||
/* Saved for migration state */
|
||||
bool in_queue;
|
||||
bool saved_p;
|
||||
bool saved_q;
|
||||
u8 saved_scan_prio;
|
||||
};
|
||||
|
||||
/* Select the "right" interrupt (IPI vs. passthrough) */
|
||||
static inline void kvmppc_xive_select_irq(struct kvmppc_xive_irq_state *state,
|
||||
u32 *out_hw_irq,
|
||||
struct xive_irq_data **out_xd)
|
||||
{
|
||||
if (state->pt_number) {
|
||||
if (out_hw_irq)
|
||||
*out_hw_irq = state->pt_number;
|
||||
if (out_xd)
|
||||
*out_xd = state->pt_data;
|
||||
} else {
|
||||
if (out_hw_irq)
|
||||
*out_hw_irq = state->ipi_number;
|
||||
if (out_xd)
|
||||
*out_xd = &state->ipi_data;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This corresponds to an "ICS" in XICS terminology, we use it
|
||||
* as a mean to break up source information into multiple structures.
|
||||
*/
|
||||
struct kvmppc_xive_src_block {
|
||||
arch_spinlock_t lock;
|
||||
u16 id;
|
||||
struct kvmppc_xive_irq_state irq_state[KVMPPC_XICS_IRQ_PER_ICS];
|
||||
};
|
||||
|
||||
|
||||
struct kvmppc_xive {
|
||||
struct kvm *kvm;
|
||||
struct kvm_device *dev;
|
||||
struct dentry *dentry;
|
||||
|
||||
/* VP block associated with the VM */
|
||||
u32 vp_base;
|
||||
|
||||
/* Blocks of sources */
|
||||
struct kvmppc_xive_src_block *src_blocks[KVMPPC_XICS_MAX_ICS_ID + 1];
|
||||
u32 max_sbid;
|
||||
|
||||
/*
|
||||
* For state save, we lazily scan the queues on the first interrupt
|
||||
* being migrated. We don't have a clean way to reset that flags
|
||||
* so we keep track of the number of valid sources and how many of
|
||||
* them were migrated so we can reset when all of them have been
|
||||
* processed.
|
||||
*/
|
||||
u32 src_count;
|
||||
u32 saved_src_count;
|
||||
|
||||
/*
|
||||
* Some irqs are delayed on restore until the source is created,
|
||||
* keep track here of how many of them
|
||||
*/
|
||||
u32 delayed_irqs;
|
||||
|
||||
/* Which queues (priorities) are in use by the guest */
|
||||
u8 qmap;
|
||||
|
||||
/* Queue orders */
|
||||
u32 q_order;
|
||||
u32 q_page_order;
|
||||
|
||||
};
|
||||
|
||||
#define KVMPPC_XIVE_Q_COUNT 8
|
||||
|
||||
struct kvmppc_xive_vcpu {
|
||||
struct kvmppc_xive *xive;
|
||||
struct kvm_vcpu *vcpu;
|
||||
bool valid;
|
||||
|
||||
/* Server number. This is the HW CPU ID from a guest perspective */
|
||||
u32 server_num;
|
||||
|
||||
/*
|
||||
* HW VP corresponding to this VCPU. This is the base of the VP
|
||||
* block plus the server number.
|
||||
*/
|
||||
u32 vp_id;
|
||||
u32 vp_chip_id;
|
||||
u32 vp_cam;
|
||||
|
||||
/* IPI used for sending ... IPIs */
|
||||
u32 vp_ipi;
|
||||
struct xive_irq_data vp_ipi_data;
|
||||
|
||||
/* Local emulation state */
|
||||
uint8_t cppr; /* guest CPPR */
|
||||
uint8_t hw_cppr;/* Hardware CPPR */
|
||||
uint8_t mfrr;
|
||||
uint8_t pending;
|
||||
|
||||
/* Each VP has 8 queues though we only provision some */
|
||||
struct xive_q queues[KVMPPC_XIVE_Q_COUNT];
|
||||
u32 esc_virq[KVMPPC_XIVE_Q_COUNT];
|
||||
char *esc_virq_names[KVMPPC_XIVE_Q_COUNT];
|
||||
|
||||
/* Stash a delayed irq on restore from migration (see set_icp) */
|
||||
u32 delayed_irq;
|
||||
|
||||
/* Stats */
|
||||
u64 stat_rm_h_xirr;
|
||||
u64 stat_rm_h_ipoll;
|
||||
u64 stat_rm_h_cppr;
|
||||
u64 stat_rm_h_eoi;
|
||||
u64 stat_rm_h_ipi;
|
||||
u64 stat_vm_h_xirr;
|
||||
u64 stat_vm_h_ipoll;
|
||||
u64 stat_vm_h_cppr;
|
||||
u64 stat_vm_h_eoi;
|
||||
u64 stat_vm_h_ipi;
|
||||
};
|
||||
|
||||
static inline struct kvm_vcpu *kvmppc_xive_find_server(struct kvm *kvm, u32 nr)
|
||||
{
|
||||
struct kvm_vcpu *vcpu = NULL;
|
||||
int i;
|
||||
|
||||
kvm_for_each_vcpu(i, vcpu, kvm) {
|
||||
if (vcpu->arch.xive_vcpu && nr == vcpu->arch.xive_vcpu->server_num)
|
||||
return vcpu;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline struct kvmppc_xive_src_block *kvmppc_xive_find_source(struct kvmppc_xive *xive,
|
||||
u32 irq, u16 *source)
|
||||
{
|
||||
u32 bid = irq >> KVMPPC_XICS_ICS_SHIFT;
|
||||
u16 src = irq & KVMPPC_XICS_SRC_MASK;
|
||||
|
||||
if (source)
|
||||
*source = src;
|
||||
if (bid > KVMPPC_XICS_MAX_ICS_ID)
|
||||
return NULL;
|
||||
return xive->src_blocks[bid];
|
||||
}
|
||||
|
||||
/*
|
||||
* Mapping between guest priorities and host priorities
|
||||
* is as follow.
|
||||
*
|
||||
* Guest request for 0...6 are honored. Guest request for anything
|
||||
* higher results in a priority of 7 being applied.
|
||||
*
|
||||
* However, when XIRR is returned via H_XIRR, 7 is translated to 0xb
|
||||
* in order to match AIX expectations
|
||||
*
|
||||
* Similar mapping is done for CPPR values
|
||||
*/
|
||||
static inline u8 xive_prio_from_guest(u8 prio)
|
||||
{
|
||||
if (prio == 0xff || prio < 8)
|
||||
return prio;
|
||||
return 7;
|
||||
}
|
||||
|
||||
static inline u8 xive_prio_to_guest(u8 prio)
|
||||
{
|
||||
if (prio == 0xff || prio < 7)
|
||||
return prio;
|
||||
return 0xb;
|
||||
}
|
||||
|
||||
static inline u32 __xive_read_eq(__be32 *qpage, u32 msk, u32 *idx, u32 *toggle)
|
||||
{
|
||||
u32 cur;
|
||||
|
||||
if (!qpage)
|
||||
return 0;
|
||||
cur = be32_to_cpup(qpage + *idx);
|
||||
if ((cur >> 31) == *toggle)
|
||||
return 0;
|
||||
*idx = (*idx + 1) & msk;
|
||||
if (*idx == 0)
|
||||
(*toggle) ^= 1;
|
||||
return cur & 0x7fffffff;
|
||||
}
|
||||
|
||||
extern unsigned long xive_rm_h_xirr(struct kvm_vcpu *vcpu);
|
||||
extern unsigned long xive_rm_h_ipoll(struct kvm_vcpu *vcpu, unsigned long server);
|
||||
extern int xive_rm_h_ipi(struct kvm_vcpu *vcpu, unsigned long server,
|
||||
unsigned long mfrr);
|
||||
extern int xive_rm_h_cppr(struct kvm_vcpu *vcpu, unsigned long cppr);
|
||||
extern int xive_rm_h_eoi(struct kvm_vcpu *vcpu, unsigned long xirr);
|
||||
|
||||
extern unsigned long (*__xive_vm_h_xirr)(struct kvm_vcpu *vcpu);
|
||||
extern unsigned long (*__xive_vm_h_ipoll)(struct kvm_vcpu *vcpu, unsigned long server);
|
||||
extern int (*__xive_vm_h_ipi)(struct kvm_vcpu *vcpu, unsigned long server,
|
||||
unsigned long mfrr);
|
||||
extern int (*__xive_vm_h_cppr)(struct kvm_vcpu *vcpu, unsigned long cppr);
|
||||
extern int (*__xive_vm_h_eoi)(struct kvm_vcpu *vcpu, unsigned long xirr);
|
||||
|
||||
#endif /* CONFIG_KVM_XICS */
|
||||
#endif /* _KVM_PPC_BOOK3S_XICS_H */
|
||||
503
arch/powerpc/kvm/book3s_xive_template.c
Normal file
503
arch/powerpc/kvm/book3s_xive_template.c
Normal file
File diff suppressed because it is too large
Load Diff
@@ -12,6 +12,7 @@ static inline int irqchip_in_kernel(struct kvm *kvm)
|
||||
#endif
|
||||
#ifdef CONFIG_KVM_XICS
|
||||
ret = ret || (kvm->arch.xics != NULL);
|
||||
ret = ret || (kvm->arch.xive != NULL);
|
||||
#endif
|
||||
smp_rmb();
|
||||
return ret;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user