forked from Minki/linux
5af5099385
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>
156 lines
3.8 KiB
C
156 lines
3.8 KiB
C
/*
|
|
* Copyright 2012 Michael Ellerman, IBM Corporation.
|
|
* Copyright 2012 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_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
|
|
* 1024 sources.
|
|
*/
|
|
#define KVMPPC_XICS_MAX_ICS_ID 1023
|
|
#define KVMPPC_XICS_ICS_SHIFT 10
|
|
#define KVMPPC_XICS_IRQ_PER_ICS (1 << KVMPPC_XICS_ICS_SHIFT)
|
|
#define KVMPPC_XICS_SRC_MASK (KVMPPC_XICS_IRQ_PER_ICS - 1)
|
|
|
|
/*
|
|
* Interrupt source numbers below this are reserved, for example
|
|
* 0 is "no interrupt", and 2 is used for IPIs.
|
|
*/
|
|
#define KVMPPC_XICS_FIRST_IRQ 16
|
|
#define KVMPPC_XICS_NR_IRQS ((KVMPPC_XICS_MAX_ICS_ID + 1) * \
|
|
KVMPPC_XICS_IRQ_PER_ICS)
|
|
|
|
/* Priority value to use for disabling an interrupt */
|
|
#define MASKED 0xff
|
|
|
|
#define PQ_PRESENTED 1
|
|
#define PQ_QUEUED 2
|
|
|
|
/* State for one irq source */
|
|
struct ics_irq_state {
|
|
u32 number;
|
|
u32 server;
|
|
u32 pq_state;
|
|
u8 priority;
|
|
u8 saved_priority;
|
|
u8 resend;
|
|
u8 masked_pending;
|
|
u8 lsi; /* level-sensitive interrupt */
|
|
u8 exists;
|
|
int intr_cpu;
|
|
u32 host_irq;
|
|
};
|
|
|
|
/* Atomic ICP state, updated with a single compare & swap */
|
|
union kvmppc_icp_state {
|
|
unsigned long raw;
|
|
struct {
|
|
u8 out_ee:1;
|
|
u8 need_resend:1;
|
|
u8 cppr;
|
|
u8 mfrr;
|
|
u8 pending_pri;
|
|
u32 xisr;
|
|
};
|
|
};
|
|
|
|
/* One bit per ICS */
|
|
#define ICP_RESEND_MAP_SIZE (KVMPPC_XICS_MAX_ICS_ID / BITS_PER_LONG + 1)
|
|
|
|
struct kvmppc_icp {
|
|
struct kvm_vcpu *vcpu;
|
|
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_NOTIFY_EOI 0x8
|
|
u32 rm_action;
|
|
struct kvm_vcpu *rm_kick_target;
|
|
struct kvmppc_icp *rm_resend_icp;
|
|
u32 rm_reject;
|
|
u32 rm_eoied_irq;
|
|
|
|
/* Counters for each reason we exited real mode */
|
|
unsigned long n_rm_kick_vcpu;
|
|
unsigned long n_rm_check_resend;
|
|
unsigned long n_rm_notify_eoi;
|
|
/* Counters for handling ICP processing in real mode */
|
|
unsigned long n_check_resend;
|
|
unsigned long n_reject;
|
|
|
|
/* Debug stuff for real mode */
|
|
union kvmppc_icp_state rm_dbgstate;
|
|
struct kvm_vcpu *rm_dbgtgt;
|
|
};
|
|
|
|
struct kvmppc_ics {
|
|
arch_spinlock_t lock;
|
|
u16 icsid;
|
|
struct ics_irq_state irq_state[KVMPPC_XICS_IRQ_PER_ICS];
|
|
};
|
|
|
|
struct kvmppc_xics {
|
|
struct kvm *kvm;
|
|
struct kvm_device *dev;
|
|
struct dentry *dentry;
|
|
u32 max_icsid;
|
|
bool real_mode;
|
|
bool real_mode_dbg;
|
|
u32 err_noics;
|
|
u32 err_noicp;
|
|
struct kvmppc_ics *ics[KVMPPC_XICS_MAX_ICS_ID + 1];
|
|
};
|
|
|
|
static inline struct kvmppc_icp *kvmppc_xics_find_server(struct kvm *kvm,
|
|
u32 nr)
|
|
{
|
|
struct kvm_vcpu *vcpu = NULL;
|
|
int i;
|
|
|
|
kvm_for_each_vcpu(i, vcpu, kvm) {
|
|
if (vcpu->arch.icp && nr == vcpu->arch.icp->server_num)
|
|
return vcpu->arch.icp;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static inline struct kvmppc_ics *kvmppc_xics_find_ics(struct kvmppc_xics *xics,
|
|
u32 irq, u16 *source)
|
|
{
|
|
u32 icsid = irq >> KVMPPC_XICS_ICS_SHIFT;
|
|
u16 src = irq & KVMPPC_XICS_SRC_MASK;
|
|
struct kvmppc_ics *ics;
|
|
|
|
if (source)
|
|
*source = src;
|
|
if (icsid > KVMPPC_XICS_MAX_ICS_ID)
|
|
return NULL;
|
|
ics = xics->ics[icsid];
|
|
if (!ics)
|
|
return NULL;
|
|
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 */
|