KVM: PPC: booke: category E.HV (GS-mode) support
Chips such as e500mc that implement category E.HV in Power ISA 2.06 provide hardware virtualization features, including a new MSR mode for guest state. The guest OS can perform many operations without trapping into the hypervisor, including transitions to and from guest userspace. Since we can use SRR1[GS] to reliably tell whether an exception came from guest state, instead of messing around with IVPR, we use DO_KVM similarly to book3s. Current issues include: - Machine checks from guest state are not routed to the host handler. - The guest can cause a host oops by executing an emulated instruction in a page that lacks read permission. Existing e500/4xx support has the same problem. Includes work by Ashish Kalra <Ashish.Kalra@freescale.com>, Varun Sethi <Varun.Sethi@freescale.com>, and Liu Yu <yu.liu@freescale.com>. Signed-off-by: Scott Wood <scottwood@freescale.com> [agraf: remove pt_regs usage] Signed-off-by: Alexander Graf <agraf@suse.de> Signed-off-by: Avi Kivity <avi@redhat.com>
This commit is contained in:
parent
cfac57847a
commit
d30f6e4800
@ -19,6 +19,7 @@
|
|||||||
|
|
||||||
#define PPC_DBELL_MSG_BRDCAST (0x04000000)
|
#define PPC_DBELL_MSG_BRDCAST (0x04000000)
|
||||||
#define PPC_DBELL_TYPE(x) (((x) & 0xf) << (63-36))
|
#define PPC_DBELL_TYPE(x) (((x) & 0xf) << (63-36))
|
||||||
|
#define PPC_DBELL_LPID(x) ((x) << (63 - 49))
|
||||||
enum ppc_dbell {
|
enum ppc_dbell {
|
||||||
PPC_DBELL = 0, /* doorbell */
|
PPC_DBELL = 0, /* doorbell */
|
||||||
PPC_DBELL_CRIT = 1, /* critical doorbell */
|
PPC_DBELL_CRIT = 1, /* critical doorbell */
|
||||||
|
@ -48,6 +48,14 @@
|
|||||||
#define BOOKE_INTERRUPT_SPE_FP_DATA 33
|
#define BOOKE_INTERRUPT_SPE_FP_DATA 33
|
||||||
#define BOOKE_INTERRUPT_SPE_FP_ROUND 34
|
#define BOOKE_INTERRUPT_SPE_FP_ROUND 34
|
||||||
#define BOOKE_INTERRUPT_PERFORMANCE_MONITOR 35
|
#define BOOKE_INTERRUPT_PERFORMANCE_MONITOR 35
|
||||||
|
#define BOOKE_INTERRUPT_DOORBELL 36
|
||||||
|
#define BOOKE_INTERRUPT_DOORBELL_CRITICAL 37
|
||||||
|
|
||||||
|
/* booke_hv */
|
||||||
|
#define BOOKE_INTERRUPT_GUEST_DBELL 38
|
||||||
|
#define BOOKE_INTERRUPT_GUEST_DBELL_CRIT 39
|
||||||
|
#define BOOKE_INTERRUPT_HV_SYSCALL 40
|
||||||
|
#define BOOKE_INTERRUPT_HV_PRIV 41
|
||||||
|
|
||||||
/* book3s */
|
/* book3s */
|
||||||
|
|
||||||
|
49
arch/powerpc/include/asm/kvm_booke_hv_asm.h
Normal file
49
arch/powerpc/include/asm/kvm_booke_hv_asm.h
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2011 Freescale Semiconductor, Inc.
|
||||||
|
*
|
||||||
|
* 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 ASM_KVM_BOOKE_HV_ASM_H
|
||||||
|
#define ASM_KVM_BOOKE_HV_ASM_H
|
||||||
|
|
||||||
|
#ifdef __ASSEMBLY__
|
||||||
|
|
||||||
|
/*
|
||||||
|
* All exceptions from guest state must go through KVM
|
||||||
|
* (except for those which are delivered directly to the guest) --
|
||||||
|
* there are no exceptions for which we fall through directly to
|
||||||
|
* the normal host handler.
|
||||||
|
*
|
||||||
|
* Expected inputs (normal exceptions):
|
||||||
|
* SCRATCH0 = saved r10
|
||||||
|
* r10 = thread struct
|
||||||
|
* r11 = appropriate SRR1 variant (currently used as scratch)
|
||||||
|
* r13 = saved CR
|
||||||
|
* *(r10 + THREAD_NORMSAVE(0)) = saved r11
|
||||||
|
* *(r10 + THREAD_NORMSAVE(2)) = saved r13
|
||||||
|
*
|
||||||
|
* Expected inputs (crit/mcheck/debug exceptions):
|
||||||
|
* appropriate SCRATCH = saved r8
|
||||||
|
* r8 = exception level stack frame
|
||||||
|
* r9 = *(r8 + _CCR) = saved CR
|
||||||
|
* r11 = appropriate SRR1 variant (currently used as scratch)
|
||||||
|
* *(r8 + GPR9) = saved r9
|
||||||
|
* *(r8 + GPR10) = saved r10 (r10 not yet clobbered)
|
||||||
|
* *(r8 + GPR11) = saved r11
|
||||||
|
*/
|
||||||
|
.macro DO_KVM intno srr1
|
||||||
|
#ifdef CONFIG_KVM_BOOKE_HV
|
||||||
|
BEGIN_FTR_SECTION
|
||||||
|
mtocrf 0x80, r11 /* check MSR[GS] without clobbering reg */
|
||||||
|
bf 3, kvmppc_resume_\intno\()_\srr1
|
||||||
|
b kvmppc_handler_\intno\()_\srr1
|
||||||
|
kvmppc_resume_\intno\()_\srr1:
|
||||||
|
END_FTR_SECTION_IFSET(CPU_FTR_EMB_HV)
|
||||||
|
#endif
|
||||||
|
.endm
|
||||||
|
|
||||||
|
#endif /*__ASSEMBLY__ */
|
||||||
|
#endif /* ASM_KVM_BOOKE_HV_ASM_H */
|
@ -106,6 +106,8 @@ struct kvm_vcpu_stat {
|
|||||||
u32 dec_exits;
|
u32 dec_exits;
|
||||||
u32 ext_intr_exits;
|
u32 ext_intr_exits;
|
||||||
u32 halt_wakeup;
|
u32 halt_wakeup;
|
||||||
|
u32 dbell_exits;
|
||||||
|
u32 gdbell_exits;
|
||||||
#ifdef CONFIG_PPC_BOOK3S
|
#ifdef CONFIG_PPC_BOOK3S
|
||||||
u32 pf_storage;
|
u32 pf_storage;
|
||||||
u32 pf_instruc;
|
u32 pf_instruc;
|
||||||
@ -140,6 +142,7 @@ enum kvm_exit_types {
|
|||||||
EMULATED_TLBSX_EXITS,
|
EMULATED_TLBSX_EXITS,
|
||||||
EMULATED_TLBWE_EXITS,
|
EMULATED_TLBWE_EXITS,
|
||||||
EMULATED_RFI_EXITS,
|
EMULATED_RFI_EXITS,
|
||||||
|
EMULATED_RFCI_EXITS,
|
||||||
DEC_EXITS,
|
DEC_EXITS,
|
||||||
EXT_INTR_EXITS,
|
EXT_INTR_EXITS,
|
||||||
HALT_WAKEUP,
|
HALT_WAKEUP,
|
||||||
@ -147,6 +150,8 @@ enum kvm_exit_types {
|
|||||||
FP_UNAVAIL,
|
FP_UNAVAIL,
|
||||||
DEBUG_EXITS,
|
DEBUG_EXITS,
|
||||||
TIMEINGUEST,
|
TIMEINGUEST,
|
||||||
|
DBELL_EXITS,
|
||||||
|
GDBELL_EXITS,
|
||||||
__NUMBER_OF_KVM_EXIT_TYPES
|
__NUMBER_OF_KVM_EXIT_TYPES
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -217,10 +222,10 @@ struct kvm_arch_memory_slot {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct kvm_arch {
|
struct kvm_arch {
|
||||||
|
unsigned int lpid;
|
||||||
#ifdef CONFIG_KVM_BOOK3S_64_HV
|
#ifdef CONFIG_KVM_BOOK3S_64_HV
|
||||||
unsigned long hpt_virt;
|
unsigned long hpt_virt;
|
||||||
struct revmap_entry *revmap;
|
struct revmap_entry *revmap;
|
||||||
unsigned int lpid;
|
|
||||||
unsigned int host_lpid;
|
unsigned int host_lpid;
|
||||||
unsigned long host_lpcr;
|
unsigned long host_lpcr;
|
||||||
unsigned long sdr1;
|
unsigned long sdr1;
|
||||||
@ -345,6 +350,17 @@ struct kvm_vcpu_arch {
|
|||||||
u64 vsr[64];
|
u64 vsr[64];
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef CONFIG_KVM_BOOKE_HV
|
||||||
|
u32 host_mas4;
|
||||||
|
u32 host_mas6;
|
||||||
|
u32 shadow_epcr;
|
||||||
|
u32 epcr;
|
||||||
|
u32 shadow_msrp;
|
||||||
|
u32 eplc;
|
||||||
|
u32 epsc;
|
||||||
|
u32 oldpir;
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef CONFIG_PPC_BOOK3S
|
#ifdef CONFIG_PPC_BOOK3S
|
||||||
/* For Gekko paired singles */
|
/* For Gekko paired singles */
|
||||||
u32 qpr[32];
|
u32 qpr[32];
|
||||||
@ -428,6 +444,7 @@ struct kvm_vcpu_arch {
|
|||||||
ulong queued_esr;
|
ulong queued_esr;
|
||||||
u32 tlbcfg[4];
|
u32 tlbcfg[4];
|
||||||
u32 mmucfg;
|
u32 mmucfg;
|
||||||
|
u32 epr;
|
||||||
#endif
|
#endif
|
||||||
gpa_t paddr_accessed;
|
gpa_t paddr_accessed;
|
||||||
|
|
||||||
|
@ -139,6 +139,9 @@ extern int kvmppc_core_prepare_memory_region(struct kvm *kvm,
|
|||||||
extern void kvmppc_core_commit_memory_region(struct kvm *kvm,
|
extern void kvmppc_core_commit_memory_region(struct kvm *kvm,
|
||||||
struct kvm_userspace_memory_region *mem);
|
struct kvm_userspace_memory_region *mem);
|
||||||
|
|
||||||
|
extern int kvmppc_bookehv_init(void);
|
||||||
|
extern void kvmppc_bookehv_exit(void);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Cuts out inst bits with ordering according to spec.
|
* Cuts out inst bits with ordering according to spec.
|
||||||
* That means the leftmost bit is zero. All given bits are included.
|
* That means the leftmost bit is zero. All given bits are included.
|
||||||
|
@ -104,6 +104,8 @@
|
|||||||
#define MAS4_TSIZED_MASK 0x00000f80 /* Default TSIZE */
|
#define MAS4_TSIZED_MASK 0x00000f80 /* Default TSIZE */
|
||||||
#define MAS4_TSIZED_SHIFT 7
|
#define MAS4_TSIZED_SHIFT 7
|
||||||
|
|
||||||
|
#define MAS5_SGS 0x80000000
|
||||||
|
|
||||||
#define MAS6_SPID0 0x3FFF0000
|
#define MAS6_SPID0 0x3FFF0000
|
||||||
#define MAS6_SPID1 0x00007FFE
|
#define MAS6_SPID1 0x00007FFE
|
||||||
#define MAS6_ISIZE(x) MAS1_TSIZE(x)
|
#define MAS6_ISIZE(x) MAS1_TSIZE(x)
|
||||||
@ -118,6 +120,10 @@
|
|||||||
|
|
||||||
#define MAS7_RPN 0xFFFFFFFF
|
#define MAS7_RPN 0xFFFFFFFF
|
||||||
|
|
||||||
|
#define MAS8_TGS 0x80000000 /* Guest space */
|
||||||
|
#define MAS8_VF 0x40000000 /* Virtualization Fault */
|
||||||
|
#define MAS8_TLPID 0x000000ff
|
||||||
|
|
||||||
/* Bit definitions for MMUCFG */
|
/* Bit definitions for MMUCFG */
|
||||||
#define MMUCFG_MAVN 0x00000003 /* MMU Architecture Version Number */
|
#define MMUCFG_MAVN 0x00000003 /* MMU Architecture Version Number */
|
||||||
#define MMUCFG_MAVN_V1 0x00000000 /* v1.0 */
|
#define MMUCFG_MAVN_V1 0x00000000 /* v1.0 */
|
||||||
|
@ -243,6 +243,9 @@ struct thread_struct {
|
|||||||
#ifdef CONFIG_KVM_BOOK3S_32_HANDLER
|
#ifdef CONFIG_KVM_BOOK3S_32_HANDLER
|
||||||
void* kvm_shadow_vcpu; /* KVM internal data */
|
void* kvm_shadow_vcpu; /* KVM internal data */
|
||||||
#endif /* CONFIG_KVM_BOOK3S_32_HANDLER */
|
#endif /* CONFIG_KVM_BOOK3S_32_HANDLER */
|
||||||
|
#if defined(CONFIG_KVM) && defined(CONFIG_BOOKE)
|
||||||
|
struct kvm_vcpu *kvm_vcpu;
|
||||||
|
#endif
|
||||||
#ifdef CONFIG_PPC64
|
#ifdef CONFIG_PPC64
|
||||||
unsigned long dscr;
|
unsigned long dscr;
|
||||||
int dscr_inherit;
|
int dscr_inherit;
|
||||||
|
@ -257,7 +257,9 @@
|
|||||||
#define LPCR_LPES_SH 2
|
#define LPCR_LPES_SH 2
|
||||||
#define LPCR_RMI 0x00000002 /* real mode is cache inhibit */
|
#define LPCR_RMI 0x00000002 /* real mode is cache inhibit */
|
||||||
#define LPCR_HDICE 0x00000001 /* Hyp Decr enable (HV,PR,EE) */
|
#define LPCR_HDICE 0x00000001 /* Hyp Decr enable (HV,PR,EE) */
|
||||||
|
#ifndef SPRN_LPID
|
||||||
#define SPRN_LPID 0x13F /* Logical Partition Identifier */
|
#define SPRN_LPID 0x13F /* Logical Partition Identifier */
|
||||||
|
#endif
|
||||||
#define LPID_RSVD 0x3ff /* Reserved LPID for partn switching */
|
#define LPID_RSVD 0x3ff /* Reserved LPID for partn switching */
|
||||||
#define SPRN_HMER 0x150 /* Hardware m? error recovery */
|
#define SPRN_HMER 0x150 /* Hardware m? error recovery */
|
||||||
#define SPRN_HMEER 0x151 /* Hardware m? enable error recovery */
|
#define SPRN_HMEER 0x151 /* Hardware m? enable error recovery */
|
||||||
|
@ -61,18 +61,30 @@ extern u32 booke_wdt_period;
|
|||||||
#define SPRN_SPRG7W 0x117 /* Special Purpose Register General 7 Write */
|
#define SPRN_SPRG7W 0x117 /* Special Purpose Register General 7 Write */
|
||||||
#define SPRN_EPCR 0x133 /* Embedded Processor Control Register */
|
#define SPRN_EPCR 0x133 /* Embedded Processor Control Register */
|
||||||
#define SPRN_DBCR2 0x136 /* Debug Control Register 2 */
|
#define SPRN_DBCR2 0x136 /* Debug Control Register 2 */
|
||||||
|
#define SPRN_MSRP 0x137 /* MSR Protect Register */
|
||||||
#define SPRN_IAC3 0x13A /* Instruction Address Compare 3 */
|
#define SPRN_IAC3 0x13A /* Instruction Address Compare 3 */
|
||||||
#define SPRN_IAC4 0x13B /* Instruction Address Compare 4 */
|
#define SPRN_IAC4 0x13B /* Instruction Address Compare 4 */
|
||||||
#define SPRN_DVC1 0x13E /* Data Value Compare Register 1 */
|
#define SPRN_DVC1 0x13E /* Data Value Compare Register 1 */
|
||||||
#define SPRN_DVC2 0x13F /* Data Value Compare Register 2 */
|
#define SPRN_DVC2 0x13F /* Data Value Compare Register 2 */
|
||||||
|
#define SPRN_LPID 0x152 /* Logical Partition ID */
|
||||||
#define SPRN_MAS8 0x155 /* MMU Assist Register 8 */
|
#define SPRN_MAS8 0x155 /* MMU Assist Register 8 */
|
||||||
#define SPRN_TLB0PS 0x158 /* TLB 0 Page Size Register */
|
#define SPRN_TLB0PS 0x158 /* TLB 0 Page Size Register */
|
||||||
#define SPRN_TLB1PS 0x159 /* TLB 1 Page Size Register */
|
#define SPRN_TLB1PS 0x159 /* TLB 1 Page Size Register */
|
||||||
#define SPRN_MAS5_MAS6 0x15c /* MMU Assist Register 5 || 6 */
|
#define SPRN_MAS5_MAS6 0x15c /* MMU Assist Register 5 || 6 */
|
||||||
#define SPRN_MAS8_MAS1 0x15d /* MMU Assist Register 8 || 1 */
|
#define SPRN_MAS8_MAS1 0x15d /* MMU Assist Register 8 || 1 */
|
||||||
#define SPRN_EPTCFG 0x15e /* Embedded Page Table Config */
|
#define SPRN_EPTCFG 0x15e /* Embedded Page Table Config */
|
||||||
|
#define SPRN_GSPRG0 0x170 /* Guest SPRG0 */
|
||||||
|
#define SPRN_GSPRG1 0x171 /* Guest SPRG1 */
|
||||||
|
#define SPRN_GSPRG2 0x172 /* Guest SPRG2 */
|
||||||
|
#define SPRN_GSPRG3 0x173 /* Guest SPRG3 */
|
||||||
#define SPRN_MAS7_MAS3 0x174 /* MMU Assist Register 7 || 3 */
|
#define SPRN_MAS7_MAS3 0x174 /* MMU Assist Register 7 || 3 */
|
||||||
#define SPRN_MAS0_MAS1 0x175 /* MMU Assist Register 0 || 1 */
|
#define SPRN_MAS0_MAS1 0x175 /* MMU Assist Register 0 || 1 */
|
||||||
|
#define SPRN_GSRR0 0x17A /* Guest SRR0 */
|
||||||
|
#define SPRN_GSRR1 0x17B /* Guest SRR1 */
|
||||||
|
#define SPRN_GEPR 0x17C /* Guest EPR */
|
||||||
|
#define SPRN_GDEAR 0x17D /* Guest DEAR */
|
||||||
|
#define SPRN_GPIR 0x17E /* Guest PIR */
|
||||||
|
#define SPRN_GESR 0x17F /* Guest Exception Syndrome Register */
|
||||||
#define SPRN_IVOR0 0x190 /* Interrupt Vector Offset Register 0 */
|
#define SPRN_IVOR0 0x190 /* Interrupt Vector Offset Register 0 */
|
||||||
#define SPRN_IVOR1 0x191 /* Interrupt Vector Offset Register 1 */
|
#define SPRN_IVOR1 0x191 /* Interrupt Vector Offset Register 1 */
|
||||||
#define SPRN_IVOR2 0x192 /* Interrupt Vector Offset Register 2 */
|
#define SPRN_IVOR2 0x192 /* Interrupt Vector Offset Register 2 */
|
||||||
@ -93,6 +105,13 @@ extern u32 booke_wdt_period;
|
|||||||
#define SPRN_IVOR39 0x1B1 /* Interrupt Vector Offset Register 39 */
|
#define SPRN_IVOR39 0x1B1 /* Interrupt Vector Offset Register 39 */
|
||||||
#define SPRN_IVOR40 0x1B2 /* Interrupt Vector Offset Register 40 */
|
#define SPRN_IVOR40 0x1B2 /* Interrupt Vector Offset Register 40 */
|
||||||
#define SPRN_IVOR41 0x1B3 /* Interrupt Vector Offset Register 41 */
|
#define SPRN_IVOR41 0x1B3 /* Interrupt Vector Offset Register 41 */
|
||||||
|
#define SPRN_GIVOR2 0x1B8 /* Guest IVOR2 */
|
||||||
|
#define SPRN_GIVOR3 0x1B9 /* Guest IVOR3 */
|
||||||
|
#define SPRN_GIVOR4 0x1BA /* Guest IVOR4 */
|
||||||
|
#define SPRN_GIVOR8 0x1BB /* Guest IVOR8 */
|
||||||
|
#define SPRN_GIVOR13 0x1BC /* Guest IVOR13 */
|
||||||
|
#define SPRN_GIVOR14 0x1BD /* Guest IVOR14 */
|
||||||
|
#define SPRN_GIVPR 0x1BF /* Guest IVPR */
|
||||||
#define SPRN_SPEFSCR 0x200 /* SPE & Embedded FP Status & Control */
|
#define SPRN_SPEFSCR 0x200 /* SPE & Embedded FP Status & Control */
|
||||||
#define SPRN_BBEAR 0x201 /* Branch Buffer Entry Address Register */
|
#define SPRN_BBEAR 0x201 /* Branch Buffer Entry Address Register */
|
||||||
#define SPRN_BBTAR 0x202 /* Branch Buffer Target Address Register */
|
#define SPRN_BBTAR 0x202 /* Branch Buffer Target Address Register */
|
||||||
@ -245,6 +264,10 @@ extern u32 booke_wdt_period;
|
|||||||
#define MCSR_LDG 0x00002000UL /* Guarded Load */
|
#define MCSR_LDG 0x00002000UL /* Guarded Load */
|
||||||
#define MCSR_TLBSYNC 0x00000002UL /* Multiple tlbsyncs detected */
|
#define MCSR_TLBSYNC 0x00000002UL /* Multiple tlbsyncs detected */
|
||||||
#define MCSR_BSL2_ERR 0x00000001UL /* Backside L2 cache error */
|
#define MCSR_BSL2_ERR 0x00000001UL /* Backside L2 cache error */
|
||||||
|
|
||||||
|
#define MSRP_UCLEP 0x04000000 /* Protect MSR[UCLE] */
|
||||||
|
#define MSRP_DEP 0x00000200 /* Protect MSR[DE] */
|
||||||
|
#define MSRP_PMMP 0x00000004 /* Protect MSR[PMM] */
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef CONFIG_E200
|
#ifdef CONFIG_E200
|
||||||
@ -599,6 +622,17 @@ extern u32 booke_wdt_period;
|
|||||||
#define SPRN_EPCR_DMIUH 0x00400000 /* Disable MAS Interrupt updates
|
#define SPRN_EPCR_DMIUH 0x00400000 /* Disable MAS Interrupt updates
|
||||||
* for hypervisor */
|
* for hypervisor */
|
||||||
|
|
||||||
|
/* Bit definitions for EPLC/EPSC */
|
||||||
|
#define EPC_EPR 0x80000000 /* 1 = user, 0 = kernel */
|
||||||
|
#define EPC_EPR_SHIFT 31
|
||||||
|
#define EPC_EAS 0x40000000 /* Address Space */
|
||||||
|
#define EPC_EAS_SHIFT 30
|
||||||
|
#define EPC_EGS 0x20000000 /* 1 = guest, 0 = hypervisor */
|
||||||
|
#define EPC_EGS_SHIFT 29
|
||||||
|
#define EPC_ELPID 0x00ff0000
|
||||||
|
#define EPC_ELPID_SHIFT 16
|
||||||
|
#define EPC_EPID 0x00003fff
|
||||||
|
#define EPC_EPID_SHIFT 0
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The IBM-403 is an even more odd special case, as it is much
|
* The IBM-403 is an even more odd special case, as it is much
|
||||||
|
@ -116,6 +116,9 @@ int main(void)
|
|||||||
#ifdef CONFIG_KVM_BOOK3S_32_HANDLER
|
#ifdef CONFIG_KVM_BOOK3S_32_HANDLER
|
||||||
DEFINE(THREAD_KVM_SVCPU, offsetof(struct thread_struct, kvm_shadow_vcpu));
|
DEFINE(THREAD_KVM_SVCPU, offsetof(struct thread_struct, kvm_shadow_vcpu));
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef CONFIG_KVM_BOOKE_HV
|
||||||
|
DEFINE(THREAD_KVM_VCPU, offsetof(struct thread_struct, kvm_vcpu));
|
||||||
|
#endif
|
||||||
|
|
||||||
DEFINE(TI_FLAGS, offsetof(struct thread_info, flags));
|
DEFINE(TI_FLAGS, offsetof(struct thread_info, flags));
|
||||||
DEFINE(TI_LOCAL_FLAGS, offsetof(struct thread_info, local_flags));
|
DEFINE(TI_LOCAL_FLAGS, offsetof(struct thread_info, local_flags));
|
||||||
@ -387,6 +390,7 @@ int main(void)
|
|||||||
#ifdef CONFIG_KVM
|
#ifdef CONFIG_KVM
|
||||||
DEFINE(VCPU_HOST_STACK, offsetof(struct kvm_vcpu, arch.host_stack));
|
DEFINE(VCPU_HOST_STACK, offsetof(struct kvm_vcpu, arch.host_stack));
|
||||||
DEFINE(VCPU_HOST_PID, offsetof(struct kvm_vcpu, arch.host_pid));
|
DEFINE(VCPU_HOST_PID, offsetof(struct kvm_vcpu, arch.host_pid));
|
||||||
|
DEFINE(VCPU_GUEST_PID, offsetof(struct kvm_vcpu, arch.pid));
|
||||||
DEFINE(VCPU_GPRS, offsetof(struct kvm_vcpu, arch.gpr));
|
DEFINE(VCPU_GPRS, offsetof(struct kvm_vcpu, arch.gpr));
|
||||||
DEFINE(VCPU_VRSAVE, offsetof(struct kvm_vcpu, arch.vrsave));
|
DEFINE(VCPU_VRSAVE, offsetof(struct kvm_vcpu, arch.vrsave));
|
||||||
DEFINE(VCPU_FPRS, offsetof(struct kvm_vcpu, arch.fpr));
|
DEFINE(VCPU_FPRS, offsetof(struct kvm_vcpu, arch.fpr));
|
||||||
@ -429,9 +433,11 @@ int main(void)
|
|||||||
DEFINE(VCPU_SHARED_MAS4, offsetof(struct kvm_vcpu_arch_shared, mas4));
|
DEFINE(VCPU_SHARED_MAS4, offsetof(struct kvm_vcpu_arch_shared, mas4));
|
||||||
DEFINE(VCPU_SHARED_MAS6, offsetof(struct kvm_vcpu_arch_shared, mas6));
|
DEFINE(VCPU_SHARED_MAS6, offsetof(struct kvm_vcpu_arch_shared, mas6));
|
||||||
|
|
||||||
|
DEFINE(VCPU_KVM, offsetof(struct kvm_vcpu, kvm));
|
||||||
|
DEFINE(KVM_LPID, offsetof(struct kvm, arch.lpid));
|
||||||
|
|
||||||
/* book3s */
|
/* book3s */
|
||||||
#ifdef CONFIG_KVM_BOOK3S_64_HV
|
#ifdef CONFIG_KVM_BOOK3S_64_HV
|
||||||
DEFINE(KVM_LPID, offsetof(struct kvm, arch.lpid));
|
|
||||||
DEFINE(KVM_SDR1, offsetof(struct kvm, arch.sdr1));
|
DEFINE(KVM_SDR1, offsetof(struct kvm, arch.sdr1));
|
||||||
DEFINE(KVM_HOST_LPID, offsetof(struct kvm, arch.host_lpid));
|
DEFINE(KVM_HOST_LPID, offsetof(struct kvm, arch.host_lpid));
|
||||||
DEFINE(KVM_HOST_LPCR, offsetof(struct kvm, arch.host_lpcr));
|
DEFINE(KVM_HOST_LPCR, offsetof(struct kvm, arch.host_lpcr));
|
||||||
@ -446,7 +452,6 @@ int main(void)
|
|||||||
DEFINE(VCPU_DAR, offsetof(struct kvm_vcpu, arch.shregs.dar));
|
DEFINE(VCPU_DAR, offsetof(struct kvm_vcpu, arch.shregs.dar));
|
||||||
#endif
|
#endif
|
||||||
#ifdef CONFIG_PPC_BOOK3S
|
#ifdef CONFIG_PPC_BOOK3S
|
||||||
DEFINE(VCPU_KVM, offsetof(struct kvm_vcpu, kvm));
|
|
||||||
DEFINE(VCPU_VCPUID, offsetof(struct kvm_vcpu, vcpu_id));
|
DEFINE(VCPU_VCPUID, offsetof(struct kvm_vcpu, vcpu_id));
|
||||||
DEFINE(VCPU_PURR, offsetof(struct kvm_vcpu, arch.purr));
|
DEFINE(VCPU_PURR, offsetof(struct kvm_vcpu, arch.purr));
|
||||||
DEFINE(VCPU_SPURR, offsetof(struct kvm_vcpu, arch.spurr));
|
DEFINE(VCPU_SPURR, offsetof(struct kvm_vcpu, arch.spurr));
|
||||||
@ -597,6 +602,12 @@ int main(void)
|
|||||||
DEFINE(VCPU_HOST_SPEFSCR, offsetof(struct kvm_vcpu, arch.host_spefscr));
|
DEFINE(VCPU_HOST_SPEFSCR, offsetof(struct kvm_vcpu, arch.host_spefscr));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef CONFIG_KVM_BOOKE_HV
|
||||||
|
DEFINE(VCPU_HOST_MAS4, offsetof(struct kvm_vcpu, arch.host_mas4));
|
||||||
|
DEFINE(VCPU_HOST_MAS6, offsetof(struct kvm_vcpu, arch.host_mas6));
|
||||||
|
DEFINE(VCPU_EPLC, offsetof(struct kvm_vcpu, arch.eplc));
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef CONFIG_KVM_EXIT_TIMING
|
#ifdef CONFIG_KVM_EXIT_TIMING
|
||||||
DEFINE(VCPU_TIMING_EXIT_TBU, offsetof(struct kvm_vcpu,
|
DEFINE(VCPU_TIMING_EXIT_TBU, offsetof(struct kvm_vcpu,
|
||||||
arch.timing_exit.tv32.tbu));
|
arch.timing_exit.tv32.tbu));
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
#include <asm/ptrace.h> /* for STACK_FRAME_REGS_MARKER */
|
#include <asm/ptrace.h> /* for STACK_FRAME_REGS_MARKER */
|
||||||
#include <asm/kvm_asm.h>
|
#include <asm/kvm_asm.h>
|
||||||
|
#include <asm/kvm_booke_hv_asm.h>
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Macros used for common Book-e exception handling
|
* Macros used for common Book-e exception handling
|
||||||
@ -36,8 +37,9 @@
|
|||||||
stw r11, THREAD_NORMSAVE(0)(r10); \
|
stw r11, THREAD_NORMSAVE(0)(r10); \
|
||||||
stw r13, THREAD_NORMSAVE(2)(r10); \
|
stw r13, THREAD_NORMSAVE(2)(r10); \
|
||||||
mfcr r13; /* save CR in r13 for now */\
|
mfcr r13; /* save CR in r13 for now */\
|
||||||
mfspr r11,SPRN_SRR1; /* check whether user or kernel */\
|
mfspr r11, SPRN_SRR1; \
|
||||||
andi. r11,r11,MSR_PR; \
|
DO_KVM BOOKE_INTERRUPT_##intno SPRN_SRR1; \
|
||||||
|
andi. r11, r11, MSR_PR; /* check whether user or kernel */\
|
||||||
mr r11, r1; \
|
mr r11, r1; \
|
||||||
beq 1f; \
|
beq 1f; \
|
||||||
/* if from user, start at top of this thread's kernel stack */ \
|
/* if from user, start at top of this thread's kernel stack */ \
|
||||||
@ -123,8 +125,9 @@
|
|||||||
stw r10,GPR10(r8); \
|
stw r10,GPR10(r8); \
|
||||||
stw r11,GPR11(r8); \
|
stw r11,GPR11(r8); \
|
||||||
stw r9,_CCR(r8); /* save CR on stack */\
|
stw r9,_CCR(r8); /* save CR on stack */\
|
||||||
mfspr r10,exc_level_srr1; /* check whether user or kernel */\
|
mfspr r11,exc_level_srr1; /* check whether user or kernel */\
|
||||||
andi. r10,r10,MSR_PR; \
|
DO_KVM BOOKE_INTERRUPT_##intno exc_level_srr1; \
|
||||||
|
andi. r11,r11,MSR_PR; \
|
||||||
mfspr r11,SPRN_SPRG_THREAD; /* if from user, start at top of */\
|
mfspr r11,SPRN_SPRG_THREAD; /* if from user, start at top of */\
|
||||||
lwz r11,THREAD_INFO-THREAD(r11); /* this thread's kernel stack */\
|
lwz r11,THREAD_INFO-THREAD(r11); /* this thread's kernel stack */\
|
||||||
addi r11,r11,EXC_LVL_FRAME_OVERHEAD; /* allocate stack frame */\
|
addi r11,r11,EXC_LVL_FRAME_OVERHEAD; /* allocate stack frame */\
|
||||||
@ -172,6 +175,23 @@
|
|||||||
EXC_LEVEL_EXCEPTION_PROLOG(MC, MACHINE_CHECK, \
|
EXC_LEVEL_EXCEPTION_PROLOG(MC, MACHINE_CHECK, \
|
||||||
SPRN_MCSRR0, SPRN_MCSRR1)
|
SPRN_MCSRR0, SPRN_MCSRR1)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Guest Doorbell -- this is a bit odd in that uses GSRR0/1 despite
|
||||||
|
* being delivered to the host. This exception can only happen
|
||||||
|
* inside a KVM guest -- so we just handle up to the DO_KVM rather
|
||||||
|
* than try to fit this into one of the existing prolog macros.
|
||||||
|
*/
|
||||||
|
#define GUEST_DOORBELL_EXCEPTION \
|
||||||
|
START_EXCEPTION(GuestDoorbell); \
|
||||||
|
mtspr SPRN_SPRG_WSCRATCH0, r10; /* save one register */ \
|
||||||
|
mfspr r10, SPRN_SPRG_THREAD; \
|
||||||
|
stw r11, THREAD_NORMSAVE(0)(r10); \
|
||||||
|
mfspr r11, SPRN_SRR1; \
|
||||||
|
stw r13, THREAD_NORMSAVE(2)(r10); \
|
||||||
|
mfcr r13; /* save CR in r13 for now */\
|
||||||
|
DO_KVM BOOKE_INTERRUPT_GUEST_DBELL SPRN_GSRR1; \
|
||||||
|
trap
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Exception vectors.
|
* Exception vectors.
|
||||||
*/
|
*/
|
||||||
|
@ -90,6 +90,9 @@ config KVM_BOOK3S_64_PR
|
|||||||
depends on KVM_BOOK3S_64 && !KVM_BOOK3S_64_HV
|
depends on KVM_BOOK3S_64 && !KVM_BOOK3S_64_HV
|
||||||
select KVM_BOOK3S_PR
|
select KVM_BOOK3S_PR
|
||||||
|
|
||||||
|
config KVM_BOOKE_HV
|
||||||
|
bool
|
||||||
|
|
||||||
config KVM_440
|
config KVM_440
|
||||||
bool "KVM support for PowerPC 440 processors"
|
bool "KVM support for PowerPC 440 processors"
|
||||||
depends on EXPERIMENTAL && 44x
|
depends on EXPERIMENTAL && 44x
|
||||||
|
@ -17,6 +17,8 @@
|
|||||||
*
|
*
|
||||||
* Authors: Hollis Blanchard <hollisb@us.ibm.com>
|
* Authors: Hollis Blanchard <hollisb@us.ibm.com>
|
||||||
* Christian Ehrhardt <ehrhardt@linux.vnet.ibm.com>
|
* Christian Ehrhardt <ehrhardt@linux.vnet.ibm.com>
|
||||||
|
* Scott Wood <scottwood@freescale.com>
|
||||||
|
* Varun Sethi <varun.sethi@freescale.com>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/errno.h>
|
#include <linux/errno.h>
|
||||||
@ -30,9 +32,12 @@
|
|||||||
#include <asm/cputable.h>
|
#include <asm/cputable.h>
|
||||||
#include <asm/uaccess.h>
|
#include <asm/uaccess.h>
|
||||||
#include <asm/kvm_ppc.h>
|
#include <asm/kvm_ppc.h>
|
||||||
#include "timing.h"
|
|
||||||
#include <asm/cacheflush.h>
|
#include <asm/cacheflush.h>
|
||||||
|
#include <asm/dbell.h>
|
||||||
|
#include <asm/hw_irq.h>
|
||||||
|
#include <asm/irq.h>
|
||||||
|
|
||||||
|
#include "timing.h"
|
||||||
#include "booke.h"
|
#include "booke.h"
|
||||||
|
|
||||||
unsigned long kvmppc_booke_handlers;
|
unsigned long kvmppc_booke_handlers;
|
||||||
@ -55,6 +60,8 @@ struct kvm_stats_debugfs_item debugfs_entries[] = {
|
|||||||
{ "dec", VCPU_STAT(dec_exits) },
|
{ "dec", VCPU_STAT(dec_exits) },
|
||||||
{ "ext_intr", VCPU_STAT(ext_intr_exits) },
|
{ "ext_intr", VCPU_STAT(ext_intr_exits) },
|
||||||
{ "halt_wakeup", VCPU_STAT(halt_wakeup) },
|
{ "halt_wakeup", VCPU_STAT(halt_wakeup) },
|
||||||
|
{ "doorbell", VCPU_STAT(dbell_exits) },
|
||||||
|
{ "guest doorbell", VCPU_STAT(gdbell_exits) },
|
||||||
{ NULL }
|
{ NULL }
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -121,6 +128,10 @@ void kvmppc_set_msr(struct kvm_vcpu *vcpu, u32 new_msr)
|
|||||||
{
|
{
|
||||||
u32 old_msr = vcpu->arch.shared->msr;
|
u32 old_msr = vcpu->arch.shared->msr;
|
||||||
|
|
||||||
|
#ifdef CONFIG_KVM_BOOKE_HV
|
||||||
|
new_msr |= MSR_GS;
|
||||||
|
#endif
|
||||||
|
|
||||||
vcpu->arch.shared->msr = new_msr;
|
vcpu->arch.shared->msr = new_msr;
|
||||||
|
|
||||||
kvmppc_mmu_msr_notify(vcpu, old_msr);
|
kvmppc_mmu_msr_notify(vcpu, old_msr);
|
||||||
@ -195,6 +206,75 @@ void kvmppc_core_dequeue_external(struct kvm_vcpu *vcpu,
|
|||||||
clear_bit(BOOKE_IRQPRIO_EXTERNAL_LEVEL, &vcpu->arch.pending_exceptions);
|
clear_bit(BOOKE_IRQPRIO_EXTERNAL_LEVEL, &vcpu->arch.pending_exceptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void set_guest_srr(struct kvm_vcpu *vcpu, unsigned long srr0, u32 srr1)
|
||||||
|
{
|
||||||
|
#ifdef CONFIG_KVM_BOOKE_HV
|
||||||
|
mtspr(SPRN_GSRR0, srr0);
|
||||||
|
mtspr(SPRN_GSRR1, srr1);
|
||||||
|
#else
|
||||||
|
vcpu->arch.shared->srr0 = srr0;
|
||||||
|
vcpu->arch.shared->srr1 = srr1;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static void set_guest_csrr(struct kvm_vcpu *vcpu, unsigned long srr0, u32 srr1)
|
||||||
|
{
|
||||||
|
vcpu->arch.csrr0 = srr0;
|
||||||
|
vcpu->arch.csrr1 = srr1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void set_guest_dsrr(struct kvm_vcpu *vcpu, unsigned long srr0, u32 srr1)
|
||||||
|
{
|
||||||
|
if (cpu_has_feature(CPU_FTR_DEBUG_LVL_EXC)) {
|
||||||
|
vcpu->arch.dsrr0 = srr0;
|
||||||
|
vcpu->arch.dsrr1 = srr1;
|
||||||
|
} else {
|
||||||
|
set_guest_csrr(vcpu, srr0, srr1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void set_guest_mcsrr(struct kvm_vcpu *vcpu, unsigned long srr0, u32 srr1)
|
||||||
|
{
|
||||||
|
vcpu->arch.mcsrr0 = srr0;
|
||||||
|
vcpu->arch.mcsrr1 = srr1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned long get_guest_dear(struct kvm_vcpu *vcpu)
|
||||||
|
{
|
||||||
|
#ifdef CONFIG_KVM_BOOKE_HV
|
||||||
|
return mfspr(SPRN_GDEAR);
|
||||||
|
#else
|
||||||
|
return vcpu->arch.shared->dar;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static void set_guest_dear(struct kvm_vcpu *vcpu, unsigned long dear)
|
||||||
|
{
|
||||||
|
#ifdef CONFIG_KVM_BOOKE_HV
|
||||||
|
mtspr(SPRN_GDEAR, dear);
|
||||||
|
#else
|
||||||
|
vcpu->arch.shared->dar = dear;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned long get_guest_esr(struct kvm_vcpu *vcpu)
|
||||||
|
{
|
||||||
|
#ifdef CONFIG_KVM_BOOKE_HV
|
||||||
|
return mfspr(SPRN_GESR);
|
||||||
|
#else
|
||||||
|
return vcpu->arch.shared->esr;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static void set_guest_esr(struct kvm_vcpu *vcpu, u32 esr)
|
||||||
|
{
|
||||||
|
#ifdef CONFIG_KVM_BOOKE_HV
|
||||||
|
mtspr(SPRN_GESR, esr);
|
||||||
|
#else
|
||||||
|
vcpu->arch.shared->esr = esr;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
/* Deliver the interrupt of the corresponding priority, if possible. */
|
/* Deliver the interrupt of the corresponding priority, if possible. */
|
||||||
static int kvmppc_booke_irqprio_deliver(struct kvm_vcpu *vcpu,
|
static int kvmppc_booke_irqprio_deliver(struct kvm_vcpu *vcpu,
|
||||||
unsigned int priority)
|
unsigned int priority)
|
||||||
@ -206,6 +286,7 @@ static int kvmppc_booke_irqprio_deliver(struct kvm_vcpu *vcpu,
|
|||||||
ulong crit_r1 = kvmppc_get_gpr(vcpu, 1);
|
ulong crit_r1 = kvmppc_get_gpr(vcpu, 1);
|
||||||
bool crit;
|
bool crit;
|
||||||
bool keep_irq = false;
|
bool keep_irq = false;
|
||||||
|
enum int_class int_class;
|
||||||
|
|
||||||
/* Truncate crit indicators in 32 bit mode */
|
/* Truncate crit indicators in 32 bit mode */
|
||||||
if (!(vcpu->arch.shared->msr & MSR_SF)) {
|
if (!(vcpu->arch.shared->msr & MSR_SF)) {
|
||||||
@ -241,16 +322,20 @@ static int kvmppc_booke_irqprio_deliver(struct kvm_vcpu *vcpu,
|
|||||||
case BOOKE_IRQPRIO_AP_UNAVAIL:
|
case BOOKE_IRQPRIO_AP_UNAVAIL:
|
||||||
case BOOKE_IRQPRIO_ALIGNMENT:
|
case BOOKE_IRQPRIO_ALIGNMENT:
|
||||||
allowed = 1;
|
allowed = 1;
|
||||||
msr_mask = MSR_CE|MSR_ME|MSR_DE;
|
msr_mask = MSR_GS | MSR_CE | MSR_ME | MSR_DE;
|
||||||
|
int_class = INT_CLASS_NONCRIT;
|
||||||
break;
|
break;
|
||||||
case BOOKE_IRQPRIO_CRITICAL:
|
case BOOKE_IRQPRIO_CRITICAL:
|
||||||
case BOOKE_IRQPRIO_WATCHDOG:
|
|
||||||
allowed = vcpu->arch.shared->msr & MSR_CE;
|
allowed = vcpu->arch.shared->msr & MSR_CE;
|
||||||
msr_mask = MSR_ME;
|
allowed = allowed && !crit;
|
||||||
|
msr_mask = MSR_GS | MSR_ME;
|
||||||
|
int_class = INT_CLASS_CRIT;
|
||||||
break;
|
break;
|
||||||
case BOOKE_IRQPRIO_MACHINE_CHECK:
|
case BOOKE_IRQPRIO_MACHINE_CHECK:
|
||||||
allowed = vcpu->arch.shared->msr & MSR_ME;
|
allowed = vcpu->arch.shared->msr & MSR_ME;
|
||||||
msr_mask = 0;
|
allowed = allowed && !crit;
|
||||||
|
msr_mask = MSR_GS;
|
||||||
|
int_class = INT_CLASS_MC;
|
||||||
break;
|
break;
|
||||||
case BOOKE_IRQPRIO_DECREMENTER:
|
case BOOKE_IRQPRIO_DECREMENTER:
|
||||||
case BOOKE_IRQPRIO_FIT:
|
case BOOKE_IRQPRIO_FIT:
|
||||||
@ -259,28 +344,62 @@ static int kvmppc_booke_irqprio_deliver(struct kvm_vcpu *vcpu,
|
|||||||
case BOOKE_IRQPRIO_EXTERNAL:
|
case BOOKE_IRQPRIO_EXTERNAL:
|
||||||
allowed = vcpu->arch.shared->msr & MSR_EE;
|
allowed = vcpu->arch.shared->msr & MSR_EE;
|
||||||
allowed = allowed && !crit;
|
allowed = allowed && !crit;
|
||||||
msr_mask = MSR_CE|MSR_ME|MSR_DE;
|
msr_mask = MSR_GS | MSR_CE | MSR_ME | MSR_DE;
|
||||||
|
int_class = INT_CLASS_NONCRIT;
|
||||||
break;
|
break;
|
||||||
case BOOKE_IRQPRIO_DEBUG:
|
case BOOKE_IRQPRIO_DEBUG:
|
||||||
allowed = vcpu->arch.shared->msr & MSR_DE;
|
allowed = vcpu->arch.shared->msr & MSR_DE;
|
||||||
msr_mask = MSR_ME;
|
allowed = allowed && !crit;
|
||||||
|
msr_mask = MSR_GS | MSR_ME;
|
||||||
|
int_class = INT_CLASS_CRIT;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (allowed) {
|
if (allowed) {
|
||||||
vcpu->arch.shared->srr0 = vcpu->arch.pc;
|
switch (int_class) {
|
||||||
vcpu->arch.shared->srr1 = vcpu->arch.shared->msr;
|
case INT_CLASS_NONCRIT:
|
||||||
|
set_guest_srr(vcpu, vcpu->arch.pc,
|
||||||
|
vcpu->arch.shared->msr);
|
||||||
|
break;
|
||||||
|
case INT_CLASS_CRIT:
|
||||||
|
set_guest_csrr(vcpu, vcpu->arch.pc,
|
||||||
|
vcpu->arch.shared->msr);
|
||||||
|
break;
|
||||||
|
case INT_CLASS_DBG:
|
||||||
|
set_guest_dsrr(vcpu, vcpu->arch.pc,
|
||||||
|
vcpu->arch.shared->msr);
|
||||||
|
break;
|
||||||
|
case INT_CLASS_MC:
|
||||||
|
set_guest_mcsrr(vcpu, vcpu->arch.pc,
|
||||||
|
vcpu->arch.shared->msr);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
vcpu->arch.pc = vcpu->arch.ivpr | vcpu->arch.ivor[priority];
|
vcpu->arch.pc = vcpu->arch.ivpr | vcpu->arch.ivor[priority];
|
||||||
if (update_esr == true)
|
if (update_esr == true)
|
||||||
vcpu->arch.shared->esr = vcpu->arch.queued_esr;
|
set_guest_esr(vcpu, vcpu->arch.queued_esr);
|
||||||
if (update_dear == true)
|
if (update_dear == true)
|
||||||
vcpu->arch.shared->dar = vcpu->arch.queued_dear;
|
set_guest_dear(vcpu, vcpu->arch.queued_dear);
|
||||||
kvmppc_set_msr(vcpu, vcpu->arch.shared->msr & msr_mask);
|
kvmppc_set_msr(vcpu, vcpu->arch.shared->msr & msr_mask);
|
||||||
|
|
||||||
if (!keep_irq)
|
if (!keep_irq)
|
||||||
clear_bit(priority, &vcpu->arch.pending_exceptions);
|
clear_bit(priority, &vcpu->arch.pending_exceptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_KVM_BOOKE_HV
|
||||||
|
/*
|
||||||
|
* If an interrupt is pending but masked, raise a guest doorbell
|
||||||
|
* so that we are notified when the guest enables the relevant
|
||||||
|
* MSR bit.
|
||||||
|
*/
|
||||||
|
if (vcpu->arch.pending_exceptions & BOOKE_IRQMASK_EE)
|
||||||
|
kvmppc_set_pending_interrupt(vcpu, INT_CLASS_NONCRIT);
|
||||||
|
if (vcpu->arch.pending_exceptions & BOOKE_IRQMASK_CE)
|
||||||
|
kvmppc_set_pending_interrupt(vcpu, INT_CLASS_CRIT);
|
||||||
|
if (vcpu->arch.pending_exceptions & BOOKE_IRQPRIO_MACHINE_CHECK)
|
||||||
|
kvmppc_set_pending_interrupt(vcpu, INT_CLASS_MC);
|
||||||
|
#endif
|
||||||
|
|
||||||
return allowed;
|
return allowed;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -344,6 +463,11 @@ int kvmppc_vcpu_run(struct kvm_run *kvm_run, struct kvm_vcpu *vcpu)
|
|||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!current->thread.kvm_vcpu) {
|
||||||
|
WARN(1, "no vcpu\n");
|
||||||
|
return -EPERM;
|
||||||
|
}
|
||||||
|
|
||||||
local_irq_disable();
|
local_irq_disable();
|
||||||
|
|
||||||
kvmppc_core_prepare_to_enter(vcpu);
|
kvmppc_core_prepare_to_enter(vcpu);
|
||||||
@ -363,6 +487,38 @@ out:
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int emulation_exit(struct kvm_run *run, struct kvm_vcpu *vcpu)
|
||||||
|
{
|
||||||
|
enum emulation_result er;
|
||||||
|
|
||||||
|
er = kvmppc_emulate_instruction(run, vcpu);
|
||||||
|
switch (er) {
|
||||||
|
case EMULATE_DONE:
|
||||||
|
/* don't overwrite subtypes, just account kvm_stats */
|
||||||
|
kvmppc_account_exit_stat(vcpu, EMULATED_INST_EXITS);
|
||||||
|
/* Future optimization: only reload non-volatiles if
|
||||||
|
* they were actually modified by emulation. */
|
||||||
|
return RESUME_GUEST_NV;
|
||||||
|
|
||||||
|
case EMULATE_DO_DCR:
|
||||||
|
run->exit_reason = KVM_EXIT_DCR;
|
||||||
|
return RESUME_HOST;
|
||||||
|
|
||||||
|
case EMULATE_FAIL:
|
||||||
|
/* XXX Deliver Program interrupt to guest. */
|
||||||
|
printk(KERN_CRIT "%s: emulation at %lx failed (%08x)\n",
|
||||||
|
__func__, vcpu->arch.pc, vcpu->arch.last_inst);
|
||||||
|
/* For debugging, encode the failing instruction and
|
||||||
|
* report it to userspace. */
|
||||||
|
run->hw.hardware_exit_reason = ~0ULL << 32;
|
||||||
|
run->hw.hardware_exit_reason |= vcpu->arch.last_inst;
|
||||||
|
return RESUME_HOST;
|
||||||
|
|
||||||
|
default:
|
||||||
|
BUG();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* kvmppc_handle_exit
|
* kvmppc_handle_exit
|
||||||
*
|
*
|
||||||
@ -371,12 +527,30 @@ out:
|
|||||||
int kvmppc_handle_exit(struct kvm_run *run, struct kvm_vcpu *vcpu,
|
int kvmppc_handle_exit(struct kvm_run *run, struct kvm_vcpu *vcpu,
|
||||||
unsigned int exit_nr)
|
unsigned int exit_nr)
|
||||||
{
|
{
|
||||||
enum emulation_result er;
|
|
||||||
int r = RESUME_HOST;
|
int r = RESUME_HOST;
|
||||||
|
|
||||||
/* update before a new last_exit_type is rewritten */
|
/* update before a new last_exit_type is rewritten */
|
||||||
kvmppc_update_timing_stats(vcpu);
|
kvmppc_update_timing_stats(vcpu);
|
||||||
|
|
||||||
|
switch (exit_nr) {
|
||||||
|
case BOOKE_INTERRUPT_EXTERNAL:
|
||||||
|
do_IRQ(current->thread.regs);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case BOOKE_INTERRUPT_DECREMENTER:
|
||||||
|
timer_interrupt(current->thread.regs);
|
||||||
|
break;
|
||||||
|
|
||||||
|
#if defined(CONFIG_PPC_FSL_BOOK3E) || defined(CONFIG_PPC_BOOK3E_64)
|
||||||
|
case BOOKE_INTERRUPT_DOORBELL:
|
||||||
|
doorbell_exception(current->thread.regs);
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
case BOOKE_INTERRUPT_MACHINE_CHECK:
|
||||||
|
/* FIXME */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
local_irq_enable();
|
local_irq_enable();
|
||||||
|
|
||||||
run->exit_reason = KVM_EXIT_UNKNOWN;
|
run->exit_reason = KVM_EXIT_UNKNOWN;
|
||||||
@ -384,30 +558,56 @@ int kvmppc_handle_exit(struct kvm_run *run, struct kvm_vcpu *vcpu,
|
|||||||
|
|
||||||
switch (exit_nr) {
|
switch (exit_nr) {
|
||||||
case BOOKE_INTERRUPT_MACHINE_CHECK:
|
case BOOKE_INTERRUPT_MACHINE_CHECK:
|
||||||
printk("MACHINE CHECK: %lx\n", mfspr(SPRN_MCSR));
|
kvm_resched(vcpu);
|
||||||
kvmppc_dump_vcpu(vcpu);
|
r = RESUME_GUEST;
|
||||||
r = RESUME_HOST;
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case BOOKE_INTERRUPT_EXTERNAL:
|
case BOOKE_INTERRUPT_EXTERNAL:
|
||||||
kvmppc_account_exit(vcpu, EXT_INTR_EXITS);
|
kvmppc_account_exit(vcpu, EXT_INTR_EXITS);
|
||||||
if (need_resched())
|
kvm_resched(vcpu);
|
||||||
cond_resched();
|
|
||||||
r = RESUME_GUEST;
|
r = RESUME_GUEST;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case BOOKE_INTERRUPT_DECREMENTER:
|
case BOOKE_INTERRUPT_DECREMENTER:
|
||||||
/* Since we switched IVPR back to the host's value, the host
|
|
||||||
* handled this interrupt the moment we enabled interrupts.
|
|
||||||
* Now we just offer it a chance to reschedule the guest. */
|
|
||||||
kvmppc_account_exit(vcpu, DEC_EXITS);
|
kvmppc_account_exit(vcpu, DEC_EXITS);
|
||||||
if (need_resched())
|
kvm_resched(vcpu);
|
||||||
cond_resched();
|
|
||||||
r = RESUME_GUEST;
|
r = RESUME_GUEST;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case BOOKE_INTERRUPT_DOORBELL:
|
||||||
|
kvmppc_account_exit(vcpu, DBELL_EXITS);
|
||||||
|
kvm_resched(vcpu);
|
||||||
|
r = RESUME_GUEST;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case BOOKE_INTERRUPT_GUEST_DBELL_CRIT:
|
||||||
|
kvmppc_account_exit(vcpu, GDBELL_EXITS);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We are here because there is a pending guest interrupt
|
||||||
|
* which could not be delivered as MSR_CE or MSR_ME was not
|
||||||
|
* set. Once we break from here we will retry delivery.
|
||||||
|
*/
|
||||||
|
r = RESUME_GUEST;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case BOOKE_INTERRUPT_GUEST_DBELL:
|
||||||
|
kvmppc_account_exit(vcpu, GDBELL_EXITS);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We are here because there is a pending guest interrupt
|
||||||
|
* which could not be delivered as MSR_EE was not set. Once
|
||||||
|
* we break from here we will retry delivery.
|
||||||
|
*/
|
||||||
|
r = RESUME_GUEST;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case BOOKE_INTERRUPT_HV_PRIV:
|
||||||
|
r = emulation_exit(run, vcpu);
|
||||||
|
break;
|
||||||
|
|
||||||
case BOOKE_INTERRUPT_PROGRAM:
|
case BOOKE_INTERRUPT_PROGRAM:
|
||||||
if (vcpu->arch.shared->msr & MSR_PR) {
|
if (vcpu->arch.shared->msr & (MSR_PR | MSR_GS)) {
|
||||||
/* Program traps generated by user-level software must be handled
|
/* Program traps generated by user-level software must be handled
|
||||||
* by the guest kernel. */
|
* by the guest kernel. */
|
||||||
kvmppc_core_queue_program(vcpu, vcpu->arch.fault_esr);
|
kvmppc_core_queue_program(vcpu, vcpu->arch.fault_esr);
|
||||||
@ -416,32 +616,7 @@ int kvmppc_handle_exit(struct kvm_run *run, struct kvm_vcpu *vcpu,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
er = kvmppc_emulate_instruction(run, vcpu);
|
r = emulation_exit(run, vcpu);
|
||||||
switch (er) {
|
|
||||||
case EMULATE_DONE:
|
|
||||||
/* don't overwrite subtypes, just account kvm_stats */
|
|
||||||
kvmppc_account_exit_stat(vcpu, EMULATED_INST_EXITS);
|
|
||||||
/* Future optimization: only reload non-volatiles if
|
|
||||||
* they were actually modified by emulation. */
|
|
||||||
r = RESUME_GUEST_NV;
|
|
||||||
break;
|
|
||||||
case EMULATE_DO_DCR:
|
|
||||||
run->exit_reason = KVM_EXIT_DCR;
|
|
||||||
r = RESUME_HOST;
|
|
||||||
break;
|
|
||||||
case EMULATE_FAIL:
|
|
||||||
/* XXX Deliver Program interrupt to guest. */
|
|
||||||
printk(KERN_CRIT "%s: emulation at %lx failed (%08x)\n",
|
|
||||||
__func__, vcpu->arch.pc, vcpu->arch.last_inst);
|
|
||||||
/* For debugging, encode the failing instruction and
|
|
||||||
* report it to userspace. */
|
|
||||||
run->hw.hardware_exit_reason = ~0ULL << 32;
|
|
||||||
run->hw.hardware_exit_reason |= vcpu->arch.last_inst;
|
|
||||||
r = RESUME_HOST;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
BUG();
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case BOOKE_INTERRUPT_FP_UNAVAIL:
|
case BOOKE_INTERRUPT_FP_UNAVAIL:
|
||||||
@ -506,6 +681,21 @@ int kvmppc_handle_exit(struct kvm_run *run, struct kvm_vcpu *vcpu,
|
|||||||
r = RESUME_GUEST;
|
r = RESUME_GUEST;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
#ifdef CONFIG_KVM_BOOKE_HV
|
||||||
|
case BOOKE_INTERRUPT_HV_SYSCALL:
|
||||||
|
if (!(vcpu->arch.shared->msr & MSR_PR)) {
|
||||||
|
kvmppc_set_gpr(vcpu, 3, kvmppc_kvm_pv(vcpu));
|
||||||
|
} else {
|
||||||
|
/*
|
||||||
|
* hcall from guest userspace -- send privileged
|
||||||
|
* instruction program check.
|
||||||
|
*/
|
||||||
|
kvmppc_core_queue_program(vcpu, ESR_PPR);
|
||||||
|
}
|
||||||
|
|
||||||
|
r = RESUME_GUEST;
|
||||||
|
break;
|
||||||
|
#else
|
||||||
case BOOKE_INTERRUPT_SYSCALL:
|
case BOOKE_INTERRUPT_SYSCALL:
|
||||||
if (!(vcpu->arch.shared->msr & MSR_PR) &&
|
if (!(vcpu->arch.shared->msr & MSR_PR) &&
|
||||||
(((u32)kvmppc_get_gpr(vcpu, 0)) == KVM_SC_MAGIC_R0)) {
|
(((u32)kvmppc_get_gpr(vcpu, 0)) == KVM_SC_MAGIC_R0)) {
|
||||||
@ -519,6 +709,7 @@ int kvmppc_handle_exit(struct kvm_run *run, struct kvm_vcpu *vcpu,
|
|||||||
kvmppc_account_exit(vcpu, SYSCALL_EXITS);
|
kvmppc_account_exit(vcpu, SYSCALL_EXITS);
|
||||||
r = RESUME_GUEST;
|
r = RESUME_GUEST;
|
||||||
break;
|
break;
|
||||||
|
#endif
|
||||||
|
|
||||||
case BOOKE_INTERRUPT_DTLB_MISS: {
|
case BOOKE_INTERRUPT_DTLB_MISS: {
|
||||||
unsigned long eaddr = vcpu->arch.fault_dear;
|
unsigned long eaddr = vcpu->arch.fault_dear;
|
||||||
@ -659,12 +850,15 @@ int kvm_arch_vcpu_setup(struct kvm_vcpu *vcpu)
|
|||||||
int r;
|
int r;
|
||||||
|
|
||||||
vcpu->arch.pc = 0;
|
vcpu->arch.pc = 0;
|
||||||
vcpu->arch.shared->msr = 0;
|
|
||||||
vcpu->arch.shadow_msr = MSR_USER | MSR_DE | MSR_IS | MSR_DS;
|
|
||||||
vcpu->arch.shared->pir = vcpu->vcpu_id;
|
vcpu->arch.shared->pir = vcpu->vcpu_id;
|
||||||
kvmppc_set_gpr(vcpu, 1, (16<<20) - 8); /* -8 for the callee-save LR slot */
|
kvmppc_set_gpr(vcpu, 1, (16<<20) - 8); /* -8 for the callee-save LR slot */
|
||||||
|
kvmppc_set_msr(vcpu, 0);
|
||||||
|
|
||||||
|
#ifndef CONFIG_KVM_BOOKE_HV
|
||||||
|
vcpu->arch.shadow_msr = MSR_USER | MSR_DE | MSR_IS | MSR_DS;
|
||||||
vcpu->arch.shadow_pid = 1;
|
vcpu->arch.shadow_pid = 1;
|
||||||
|
vcpu->arch.shared->msr = 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Eye-catching numbers so we know if the guest takes an interrupt
|
/* Eye-catching numbers so we know if the guest takes an interrupt
|
||||||
* before it's programmed its own IVPR/IVORs. */
|
* before it's programmed its own IVPR/IVORs. */
|
||||||
@ -745,8 +939,8 @@ static void get_sregs_base(struct kvm_vcpu *vcpu,
|
|||||||
sregs->u.e.csrr0 = vcpu->arch.csrr0;
|
sregs->u.e.csrr0 = vcpu->arch.csrr0;
|
||||||
sregs->u.e.csrr1 = vcpu->arch.csrr1;
|
sregs->u.e.csrr1 = vcpu->arch.csrr1;
|
||||||
sregs->u.e.mcsr = vcpu->arch.mcsr;
|
sregs->u.e.mcsr = vcpu->arch.mcsr;
|
||||||
sregs->u.e.esr = vcpu->arch.shared->esr;
|
sregs->u.e.esr = get_guest_esr(vcpu);
|
||||||
sregs->u.e.dear = vcpu->arch.shared->dar;
|
sregs->u.e.dear = get_guest_dear(vcpu);
|
||||||
sregs->u.e.tsr = vcpu->arch.tsr;
|
sregs->u.e.tsr = vcpu->arch.tsr;
|
||||||
sregs->u.e.tcr = vcpu->arch.tcr;
|
sregs->u.e.tcr = vcpu->arch.tcr;
|
||||||
sregs->u.e.dec = kvmppc_get_dec(vcpu, tb);
|
sregs->u.e.dec = kvmppc_get_dec(vcpu, tb);
|
||||||
@ -763,8 +957,8 @@ static int set_sregs_base(struct kvm_vcpu *vcpu,
|
|||||||
vcpu->arch.csrr0 = sregs->u.e.csrr0;
|
vcpu->arch.csrr0 = sregs->u.e.csrr0;
|
||||||
vcpu->arch.csrr1 = sregs->u.e.csrr1;
|
vcpu->arch.csrr1 = sregs->u.e.csrr1;
|
||||||
vcpu->arch.mcsr = sregs->u.e.mcsr;
|
vcpu->arch.mcsr = sregs->u.e.mcsr;
|
||||||
vcpu->arch.shared->esr = sregs->u.e.esr;
|
set_guest_esr(vcpu, sregs->u.e.esr);
|
||||||
vcpu->arch.shared->dar = sregs->u.e.dear;
|
set_guest_dear(vcpu, sregs->u.e.dear);
|
||||||
vcpu->arch.vrsave = sregs->u.e.vrsave;
|
vcpu->arch.vrsave = sregs->u.e.vrsave;
|
||||||
kvmppc_set_tcr(vcpu, sregs->u.e.tcr);
|
kvmppc_set_tcr(vcpu, sregs->u.e.tcr);
|
||||||
|
|
||||||
@ -961,14 +1155,17 @@ void kvmppc_decrementer_func(unsigned long data)
|
|||||||
|
|
||||||
void kvmppc_booke_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
|
void kvmppc_booke_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
|
||||||
{
|
{
|
||||||
|
current->thread.kvm_vcpu = vcpu;
|
||||||
}
|
}
|
||||||
|
|
||||||
void kvmppc_booke_vcpu_put(struct kvm_vcpu *vcpu)
|
void kvmppc_booke_vcpu_put(struct kvm_vcpu *vcpu)
|
||||||
{
|
{
|
||||||
|
current->thread.kvm_vcpu = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
int __init kvmppc_booke_init(void)
|
int __init kvmppc_booke_init(void)
|
||||||
{
|
{
|
||||||
|
#ifndef CONFIG_KVM_BOOKE_HV
|
||||||
unsigned long ivor[16];
|
unsigned long ivor[16];
|
||||||
unsigned long max_ivor = 0;
|
unsigned long max_ivor = 0;
|
||||||
int i;
|
int i;
|
||||||
@ -1011,7 +1208,7 @@ int __init kvmppc_booke_init(void)
|
|||||||
}
|
}
|
||||||
flush_icache_range(kvmppc_booke_handlers,
|
flush_icache_range(kvmppc_booke_handlers,
|
||||||
kvmppc_booke_handlers + max_ivor + kvmppc_handler_len);
|
kvmppc_booke_handlers + max_ivor + kvmppc_handler_len);
|
||||||
|
#endif /* !BOOKE_HV */
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,7 +48,20 @@
|
|||||||
#define BOOKE_IRQPRIO_PERFORMANCE_MONITOR 19
|
#define BOOKE_IRQPRIO_PERFORMANCE_MONITOR 19
|
||||||
/* Internal pseudo-irqprio for level triggered externals */
|
/* Internal pseudo-irqprio for level triggered externals */
|
||||||
#define BOOKE_IRQPRIO_EXTERNAL_LEVEL 20
|
#define BOOKE_IRQPRIO_EXTERNAL_LEVEL 20
|
||||||
#define BOOKE_IRQPRIO_MAX 20
|
#define BOOKE_IRQPRIO_DBELL 21
|
||||||
|
#define BOOKE_IRQPRIO_DBELL_CRIT 22
|
||||||
|
#define BOOKE_IRQPRIO_MAX 23
|
||||||
|
|
||||||
|
#define BOOKE_IRQMASK_EE ((1 << BOOKE_IRQPRIO_EXTERNAL_LEVEL) | \
|
||||||
|
(1 << BOOKE_IRQPRIO_PERFORMANCE_MONITOR) | \
|
||||||
|
(1 << BOOKE_IRQPRIO_DBELL) | \
|
||||||
|
(1 << BOOKE_IRQPRIO_DECREMENTER) | \
|
||||||
|
(1 << BOOKE_IRQPRIO_FIT) | \
|
||||||
|
(1 << BOOKE_IRQPRIO_EXTERNAL))
|
||||||
|
|
||||||
|
#define BOOKE_IRQMASK_CE ((1 << BOOKE_IRQPRIO_DBELL_CRIT) | \
|
||||||
|
(1 << BOOKE_IRQPRIO_WATCHDOG) | \
|
||||||
|
(1 << BOOKE_IRQPRIO_CRITICAL))
|
||||||
|
|
||||||
extern unsigned long kvmppc_booke_handlers;
|
extern unsigned long kvmppc_booke_handlers;
|
||||||
|
|
||||||
@ -74,4 +87,13 @@ void kvmppc_vcpu_disable_spe(struct kvm_vcpu *vcpu);
|
|||||||
void kvmppc_booke_vcpu_load(struct kvm_vcpu *vcpu, int cpu);
|
void kvmppc_booke_vcpu_load(struct kvm_vcpu *vcpu, int cpu);
|
||||||
void kvmppc_booke_vcpu_put(struct kvm_vcpu *vcpu);
|
void kvmppc_booke_vcpu_put(struct kvm_vcpu *vcpu);
|
||||||
|
|
||||||
|
enum int_class {
|
||||||
|
INT_CLASS_NONCRIT,
|
||||||
|
INT_CLASS_CRIT,
|
||||||
|
INT_CLASS_MC,
|
||||||
|
INT_CLASS_DBG,
|
||||||
|
};
|
||||||
|
|
||||||
|
void kvmppc_set_pending_interrupt(struct kvm_vcpu *vcpu, enum int_class type);
|
||||||
|
|
||||||
#endif /* __KVM_BOOKE_H__ */
|
#endif /* __KVM_BOOKE_H__ */
|
||||||
|
@ -99,6 +99,12 @@ int kvmppc_booke_emulate_op(struct kvm_run *run, struct kvm_vcpu *vcpu,
|
|||||||
return emulated;
|
return emulated;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* NOTE: some of these registers are not emulated on BOOKE_HV (GS-mode).
|
||||||
|
* Their backing store is in real registers, and these functions
|
||||||
|
* will return the wrong result if called for them in another context
|
||||||
|
* (such as debugging).
|
||||||
|
*/
|
||||||
int kvmppc_booke_emulate_mtspr(struct kvm_vcpu *vcpu, int sprn, int rs)
|
int kvmppc_booke_emulate_mtspr(struct kvm_vcpu *vcpu, int sprn, int rs)
|
||||||
{
|
{
|
||||||
int emulated = EMULATE_DONE;
|
int emulated = EMULATE_DONE;
|
||||||
@ -122,9 +128,11 @@ int kvmppc_booke_emulate_mtspr(struct kvm_vcpu *vcpu, int sprn, int rs)
|
|||||||
kvmppc_set_tcr(vcpu, spr_val);
|
kvmppc_set_tcr(vcpu, spr_val);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/* Note: SPRG4-7 are user-readable. These values are
|
/*
|
||||||
* loaded into the real SPRGs when resuming the
|
* Note: SPRG4-7 are user-readable.
|
||||||
* guest. */
|
* These values are loaded into the real SPRGs when resuming the
|
||||||
|
* guest (PR-mode only).
|
||||||
|
*/
|
||||||
case SPRN_SPRG4:
|
case SPRN_SPRG4:
|
||||||
vcpu->arch.shared->sprg4 = spr_val; break;
|
vcpu->arch.shared->sprg4 = spr_val; break;
|
||||||
case SPRN_SPRG5:
|
case SPRN_SPRG5:
|
||||||
@ -136,6 +144,9 @@ int kvmppc_booke_emulate_mtspr(struct kvm_vcpu *vcpu, int sprn, int rs)
|
|||||||
|
|
||||||
case SPRN_IVPR:
|
case SPRN_IVPR:
|
||||||
vcpu->arch.ivpr = spr_val;
|
vcpu->arch.ivpr = spr_val;
|
||||||
|
#ifdef CONFIG_KVM_BOOKE_HV
|
||||||
|
mtspr(SPRN_GIVPR, spr_val);
|
||||||
|
#endif
|
||||||
break;
|
break;
|
||||||
case SPRN_IVOR0:
|
case SPRN_IVOR0:
|
||||||
vcpu->arch.ivor[BOOKE_IRQPRIO_CRITICAL] = spr_val;
|
vcpu->arch.ivor[BOOKE_IRQPRIO_CRITICAL] = spr_val;
|
||||||
@ -145,6 +156,9 @@ int kvmppc_booke_emulate_mtspr(struct kvm_vcpu *vcpu, int sprn, int rs)
|
|||||||
break;
|
break;
|
||||||
case SPRN_IVOR2:
|
case SPRN_IVOR2:
|
||||||
vcpu->arch.ivor[BOOKE_IRQPRIO_DATA_STORAGE] = spr_val;
|
vcpu->arch.ivor[BOOKE_IRQPRIO_DATA_STORAGE] = spr_val;
|
||||||
|
#ifdef CONFIG_KVM_BOOKE_HV
|
||||||
|
mtspr(SPRN_GIVOR2, spr_val);
|
||||||
|
#endif
|
||||||
break;
|
break;
|
||||||
case SPRN_IVOR3:
|
case SPRN_IVOR3:
|
||||||
vcpu->arch.ivor[BOOKE_IRQPRIO_INST_STORAGE] = spr_val;
|
vcpu->arch.ivor[BOOKE_IRQPRIO_INST_STORAGE] = spr_val;
|
||||||
@ -163,6 +177,9 @@ int kvmppc_booke_emulate_mtspr(struct kvm_vcpu *vcpu, int sprn, int rs)
|
|||||||
break;
|
break;
|
||||||
case SPRN_IVOR8:
|
case SPRN_IVOR8:
|
||||||
vcpu->arch.ivor[BOOKE_IRQPRIO_SYSCALL] = spr_val;
|
vcpu->arch.ivor[BOOKE_IRQPRIO_SYSCALL] = spr_val;
|
||||||
|
#ifdef CONFIG_KVM_BOOKE_HV
|
||||||
|
mtspr(SPRN_GIVOR8, spr_val);
|
||||||
|
#endif
|
||||||
break;
|
break;
|
||||||
case SPRN_IVOR9:
|
case SPRN_IVOR9:
|
||||||
vcpu->arch.ivor[BOOKE_IRQPRIO_AP_UNAVAIL] = spr_val;
|
vcpu->arch.ivor[BOOKE_IRQPRIO_AP_UNAVAIL] = spr_val;
|
||||||
|
587
arch/powerpc/kvm/bookehv_interrupts.S
Normal file
587
arch/powerpc/kvm/bookehv_interrupts.S
Normal file
@ -0,0 +1,587 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2010-2011 Freescale Semiconductor, Inc.
|
||||||
|
*
|
||||||
|
* Author: Varun Sethi <varun.sethi@freescale.com>
|
||||||
|
* Author: Scott Wood <scotwood@freescale.com>
|
||||||
|
*
|
||||||
|
* This file is derived from arch/powerpc/kvm/booke_interrupts.S
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <asm/ppc_asm.h>
|
||||||
|
#include <asm/kvm_asm.h>
|
||||||
|
#include <asm/reg.h>
|
||||||
|
#include <asm/mmu-44x.h>
|
||||||
|
#include <asm/page.h>
|
||||||
|
#include <asm/asm-compat.h>
|
||||||
|
#include <asm/asm-offsets.h>
|
||||||
|
#include <asm/bitsperlong.h>
|
||||||
|
|
||||||
|
#include "../kernel/head_booke.h" /* for THREAD_NORMSAVE() */
|
||||||
|
|
||||||
|
#define GET_VCPU(vcpu, thread) \
|
||||||
|
PPC_LL vcpu, THREAD_KVM_VCPU(thread)
|
||||||
|
|
||||||
|
#define SET_VCPU(vcpu) \
|
||||||
|
PPC_STL vcpu, (THREAD + THREAD_KVM_VCPU)(r2)
|
||||||
|
|
||||||
|
#define LONGBYTES (BITS_PER_LONG / 8)
|
||||||
|
|
||||||
|
#define VCPU_GPR(n) (VCPU_GPRS + (n * LONGBYTES))
|
||||||
|
#define VCPU_GUEST_SPRG(n) (VCPU_GUEST_SPRGS + (n * LONGBYTES))
|
||||||
|
|
||||||
|
/* The host stack layout: */
|
||||||
|
#define HOST_R1 (0 * LONGBYTES) /* Implied by stwu. */
|
||||||
|
#define HOST_CALLEE_LR (1 * LONGBYTES)
|
||||||
|
#define HOST_RUN (2 * LONGBYTES) /* struct kvm_run */
|
||||||
|
/*
|
||||||
|
* r2 is special: it holds 'current', and it made nonvolatile in the
|
||||||
|
* kernel with the -ffixed-r2 gcc option.
|
||||||
|
*/
|
||||||
|
#define HOST_R2 (3 * LONGBYTES)
|
||||||
|
#define HOST_NV_GPRS (4 * LONGBYTES)
|
||||||
|
#define HOST_NV_GPR(n) (HOST_NV_GPRS + ((n - 14) * LONGBYTES))
|
||||||
|
#define HOST_MIN_STACK_SIZE (HOST_NV_GPR(31) + LONGBYTES)
|
||||||
|
#define HOST_STACK_SIZE ((HOST_MIN_STACK_SIZE + 15) & ~15) /* Align. */
|
||||||
|
#define HOST_STACK_LR (HOST_STACK_SIZE + LONGBYTES) /* In caller stack frame. */
|
||||||
|
|
||||||
|
#define NEED_EMU 0x00000001 /* emulation -- save nv regs */
|
||||||
|
#define NEED_DEAR 0x00000002 /* save faulting DEAR */
|
||||||
|
#define NEED_ESR 0x00000004 /* save faulting ESR */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* On entry:
|
||||||
|
* r4 = vcpu, r5 = srr0, r6 = srr1
|
||||||
|
* saved in vcpu: cr, ctr, r3-r13
|
||||||
|
*/
|
||||||
|
.macro kvm_handler_common intno, srr0, flags
|
||||||
|
mfspr r10, SPRN_PID
|
||||||
|
lwz r8, VCPU_HOST_PID(r4)
|
||||||
|
PPC_LL r11, VCPU_SHARED(r4)
|
||||||
|
PPC_STL r14, VCPU_GPR(r14)(r4) /* We need a non-volatile GPR. */
|
||||||
|
li r14, \intno
|
||||||
|
|
||||||
|
stw r10, VCPU_GUEST_PID(r4)
|
||||||
|
mtspr SPRN_PID, r8
|
||||||
|
|
||||||
|
.if \flags & NEED_EMU
|
||||||
|
lwz r9, VCPU_KVM(r4)
|
||||||
|
.endif
|
||||||
|
|
||||||
|
#ifdef CONFIG_KVM_EXIT_TIMING
|
||||||
|
/* save exit time */
|
||||||
|
1: mfspr r7, SPRN_TBRU
|
||||||
|
mfspr r8, SPRN_TBRL
|
||||||
|
mfspr r9, SPRN_TBRU
|
||||||
|
cmpw r9, r7
|
||||||
|
PPC_STL r8, VCPU_TIMING_EXIT_TBL(r4)
|
||||||
|
bne- 1b
|
||||||
|
PPC_STL r9, VCPU_TIMING_EXIT_TBU(r4)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
oris r8, r6, MSR_CE@h
|
||||||
|
#ifndef CONFIG_64BIT
|
||||||
|
stw r6, (VCPU_SHARED_MSR + 4)(r11)
|
||||||
|
#else
|
||||||
|
std r6, (VCPU_SHARED_MSR)(r11)
|
||||||
|
#endif
|
||||||
|
ori r8, r8, MSR_ME | MSR_RI
|
||||||
|
PPC_STL r5, VCPU_PC(r4)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Make sure CE/ME/RI are set (if appropriate for exception type)
|
||||||
|
* whether or not the guest had it set. Since mfmsr/mtmsr are
|
||||||
|
* somewhat expensive, skip in the common case where the guest
|
||||||
|
* had all these bits set (and thus they're still set if
|
||||||
|
* appropriate for the exception type).
|
||||||
|
*/
|
||||||
|
cmpw r6, r8
|
||||||
|
.if \flags & NEED_EMU
|
||||||
|
lwz r9, KVM_LPID(r9)
|
||||||
|
.endif
|
||||||
|
beq 1f
|
||||||
|
mfmsr r7
|
||||||
|
.if \srr0 != SPRN_MCSRR0 && \srr0 != SPRN_CSRR0
|
||||||
|
oris r7, r7, MSR_CE@h
|
||||||
|
.endif
|
||||||
|
.if \srr0 != SPRN_MCSRR0
|
||||||
|
ori r7, r7, MSR_ME | MSR_RI
|
||||||
|
.endif
|
||||||
|
mtmsr r7
|
||||||
|
1:
|
||||||
|
|
||||||
|
.if \flags & NEED_EMU
|
||||||
|
/*
|
||||||
|
* This assumes you have external PID support.
|
||||||
|
* To support a bookehv CPU without external PID, you'll
|
||||||
|
* need to look up the TLB entry and create a temporary mapping.
|
||||||
|
*
|
||||||
|
* FIXME: we don't currently handle if the lwepx faults. PR-mode
|
||||||
|
* booke doesn't handle it either. Since Linux doesn't use
|
||||||
|
* broadcast tlbivax anymore, the only way this should happen is
|
||||||
|
* if the guest maps its memory execute-but-not-read, or if we
|
||||||
|
* somehow take a TLB miss in the middle of this entry code and
|
||||||
|
* evict the relevant entry. On e500mc, all kernel lowmem is
|
||||||
|
* bolted into TLB1 large page mappings, and we don't use
|
||||||
|
* broadcast invalidates, so we should not take a TLB miss here.
|
||||||
|
*
|
||||||
|
* Later we'll need to deal with faults here. Disallowing guest
|
||||||
|
* mappings that are execute-but-not-read could be an option on
|
||||||
|
* e500mc, but not on chips with an LRAT if it is used.
|
||||||
|
*/
|
||||||
|
|
||||||
|
mfspr r3, SPRN_EPLC /* will already have correct ELPID and EGS */
|
||||||
|
PPC_STL r15, VCPU_GPR(r15)(r4)
|
||||||
|
PPC_STL r16, VCPU_GPR(r16)(r4)
|
||||||
|
PPC_STL r17, VCPU_GPR(r17)(r4)
|
||||||
|
PPC_STL r18, VCPU_GPR(r18)(r4)
|
||||||
|
PPC_STL r19, VCPU_GPR(r19)(r4)
|
||||||
|
mr r8, r3
|
||||||
|
PPC_STL r20, VCPU_GPR(r20)(r4)
|
||||||
|
rlwimi r8, r6, EPC_EAS_SHIFT - MSR_IR_LG, EPC_EAS
|
||||||
|
PPC_STL r21, VCPU_GPR(r21)(r4)
|
||||||
|
rlwimi r8, r6, EPC_EPR_SHIFT - MSR_PR_LG, EPC_EPR
|
||||||
|
PPC_STL r22, VCPU_GPR(r22)(r4)
|
||||||
|
rlwimi r8, r10, EPC_EPID_SHIFT, EPC_EPID
|
||||||
|
PPC_STL r23, VCPU_GPR(r23)(r4)
|
||||||
|
PPC_STL r24, VCPU_GPR(r24)(r4)
|
||||||
|
PPC_STL r25, VCPU_GPR(r25)(r4)
|
||||||
|
PPC_STL r26, VCPU_GPR(r26)(r4)
|
||||||
|
PPC_STL r27, VCPU_GPR(r27)(r4)
|
||||||
|
PPC_STL r28, VCPU_GPR(r28)(r4)
|
||||||
|
PPC_STL r29, VCPU_GPR(r29)(r4)
|
||||||
|
PPC_STL r30, VCPU_GPR(r30)(r4)
|
||||||
|
PPC_STL r31, VCPU_GPR(r31)(r4)
|
||||||
|
mtspr SPRN_EPLC, r8
|
||||||
|
isync
|
||||||
|
lwepx r9, 0, r5
|
||||||
|
mtspr SPRN_EPLC, r3
|
||||||
|
stw r9, VCPU_LAST_INST(r4)
|
||||||
|
.endif
|
||||||
|
|
||||||
|
.if \flags & NEED_ESR
|
||||||
|
mfspr r8, SPRN_ESR
|
||||||
|
PPC_STL r8, VCPU_FAULT_ESR(r4)
|
||||||
|
.endif
|
||||||
|
|
||||||
|
.if \flags & NEED_DEAR
|
||||||
|
mfspr r9, SPRN_DEAR
|
||||||
|
PPC_STL r9, VCPU_FAULT_DEAR(r4)
|
||||||
|
.endif
|
||||||
|
|
||||||
|
b kvmppc_resume_host
|
||||||
|
.endm
|
||||||
|
|
||||||
|
/*
|
||||||
|
* For input register values, see arch/powerpc/include/asm/kvm_booke_hv_asm.h
|
||||||
|
*/
|
||||||
|
.macro kvm_handler intno srr0, srr1, flags
|
||||||
|
_GLOBAL(kvmppc_handler_\intno\()_\srr1)
|
||||||
|
GET_VCPU(r11, r10)
|
||||||
|
PPC_STL r3, VCPU_GPR(r3)(r11)
|
||||||
|
mfspr r3, SPRN_SPRG_RSCRATCH0
|
||||||
|
PPC_STL r4, VCPU_GPR(r4)(r11)
|
||||||
|
PPC_LL r4, THREAD_NORMSAVE(0)(r10)
|
||||||
|
PPC_STL r5, VCPU_GPR(r5)(r11)
|
||||||
|
PPC_STL r13, VCPU_CR(r11)
|
||||||
|
mfspr r5, \srr0
|
||||||
|
PPC_STL r3, VCPU_GPR(r10)(r11)
|
||||||
|
PPC_LL r3, THREAD_NORMSAVE(2)(r10)
|
||||||
|
PPC_STL r6, VCPU_GPR(r6)(r11)
|
||||||
|
PPC_STL r4, VCPU_GPR(r11)(r11)
|
||||||
|
mfspr r6, \srr1
|
||||||
|
PPC_STL r7, VCPU_GPR(r7)(r11)
|
||||||
|
PPC_STL r8, VCPU_GPR(r8)(r11)
|
||||||
|
PPC_STL r9, VCPU_GPR(r9)(r11)
|
||||||
|
PPC_STL r3, VCPU_GPR(r13)(r11)
|
||||||
|
mfctr r7
|
||||||
|
PPC_STL r12, VCPU_GPR(r12)(r11)
|
||||||
|
PPC_STL r7, VCPU_CTR(r11)
|
||||||
|
mr r4, r11
|
||||||
|
kvm_handler_common \intno, \srr0, \flags
|
||||||
|
.endm
|
||||||
|
|
||||||
|
.macro kvm_lvl_handler intno scratch srr0, srr1, flags
|
||||||
|
_GLOBAL(kvmppc_handler_\intno\()_\srr1)
|
||||||
|
mfspr r10, SPRN_SPRG_THREAD
|
||||||
|
GET_VCPU(r11, r10)
|
||||||
|
PPC_STL r3, VCPU_GPR(r3)(r11)
|
||||||
|
mfspr r3, \scratch
|
||||||
|
PPC_STL r4, VCPU_GPR(r4)(r11)
|
||||||
|
PPC_LL r4, GPR9(r8)
|
||||||
|
PPC_STL r5, VCPU_GPR(r5)(r11)
|
||||||
|
PPC_STL r9, VCPU_CR(r11)
|
||||||
|
mfspr r5, \srr0
|
||||||
|
PPC_STL r3, VCPU_GPR(r8)(r11)
|
||||||
|
PPC_LL r3, GPR10(r8)
|
||||||
|
PPC_STL r6, VCPU_GPR(r6)(r11)
|
||||||
|
PPC_STL r4, VCPU_GPR(r9)(r11)
|
||||||
|
mfspr r6, \srr1
|
||||||
|
PPC_LL r4, GPR11(r8)
|
||||||
|
PPC_STL r7, VCPU_GPR(r7)(r11)
|
||||||
|
PPC_STL r8, VCPU_GPR(r8)(r11)
|
||||||
|
PPC_STL r3, VCPU_GPR(r10)(r11)
|
||||||
|
mfctr r7
|
||||||
|
PPC_STL r12, VCPU_GPR(r12)(r11)
|
||||||
|
PPC_STL r4, VCPU_GPR(r11)(r11)
|
||||||
|
PPC_STL r7, VCPU_CTR(r11)
|
||||||
|
mr r4, r11
|
||||||
|
kvm_handler_common \intno, \srr0, \flags
|
||||||
|
.endm
|
||||||
|
|
||||||
|
kvm_lvl_handler BOOKE_INTERRUPT_CRITICAL, \
|
||||||
|
SPRN_SPRG_RSCRATCH_CRIT, SPRN_CSRR0, SPRN_CSRR1, 0
|
||||||
|
kvm_lvl_handler BOOKE_INTERRUPT_MACHINE_CHECK, \
|
||||||
|
SPRN_SPRG_RSCRATCH_MC, SPRN_MCSRR0, SPRN_MCSRR1, 0
|
||||||
|
kvm_handler BOOKE_INTERRUPT_DATA_STORAGE, \
|
||||||
|
SPRN_SRR0, SPRN_SRR1, (NEED_EMU | NEED_DEAR)
|
||||||
|
kvm_handler BOOKE_INTERRUPT_INST_STORAGE, SPRN_SRR0, SPRN_SRR1, NEED_ESR
|
||||||
|
kvm_handler BOOKE_INTERRUPT_EXTERNAL, SPRN_SRR0, SPRN_SRR1, 0
|
||||||
|
kvm_handler BOOKE_INTERRUPT_ALIGNMENT, \
|
||||||
|
SPRN_SRR0, SPRN_SRR1, (NEED_DEAR | NEED_ESR)
|
||||||
|
kvm_handler BOOKE_INTERRUPT_PROGRAM, SPRN_SRR0, SPRN_SRR1, NEED_ESR
|
||||||
|
kvm_handler BOOKE_INTERRUPT_FP_UNAVAIL, SPRN_SRR0, SPRN_SRR1, 0
|
||||||
|
kvm_handler BOOKE_INTERRUPT_SYSCALL, SPRN_SRR0, SPRN_SRR1, 0
|
||||||
|
kvm_handler BOOKE_INTERRUPT_AP_UNAVAIL, SPRN_SRR0, SPRN_SRR1, 0
|
||||||
|
kvm_handler BOOKE_INTERRUPT_DECREMENTER, SPRN_SRR0, SPRN_SRR1, 0
|
||||||
|
kvm_handler BOOKE_INTERRUPT_FIT, SPRN_SRR0, SPRN_SRR1, 0
|
||||||
|
kvm_lvl_handler BOOKE_INTERRUPT_WATCHDOG, \
|
||||||
|
SPRN_SPRG_RSCRATCH_CRIT, SPRN_CSRR0, SPRN_CSRR1, 0
|
||||||
|
kvm_handler BOOKE_INTERRUPT_DTLB_MISS, \
|
||||||
|
SPRN_SRR0, SPRN_SRR1, (NEED_EMU | NEED_DEAR | NEED_ESR)
|
||||||
|
kvm_handler BOOKE_INTERRUPT_ITLB_MISS, SPRN_SRR0, SPRN_SRR1, 0
|
||||||
|
kvm_handler BOOKE_INTERRUPT_SPE_UNAVAIL, SPRN_SRR0, SPRN_SRR1, 0
|
||||||
|
kvm_handler BOOKE_INTERRUPT_SPE_FP_DATA, SPRN_SRR0, SPRN_SRR1, 0
|
||||||
|
kvm_handler BOOKE_INTERRUPT_SPE_FP_ROUND, SPRN_SRR0, SPRN_SRR1, 0
|
||||||
|
kvm_handler BOOKE_INTERRUPT_PERFORMANCE_MONITOR, SPRN_SRR0, SPRN_SRR1, 0
|
||||||
|
kvm_handler BOOKE_INTERRUPT_DOORBELL, SPRN_SRR0, SPRN_SRR1, 0
|
||||||
|
kvm_lvl_handler BOOKE_INTERRUPT_DOORBELL_CRITICAL, \
|
||||||
|
SPRN_SPRG_RSCRATCH_CRIT, SPRN_CSRR0, SPRN_CSRR1, 0
|
||||||
|
kvm_handler BOOKE_INTERRUPT_HV_PRIV, SPRN_SRR0, SPRN_SRR1, NEED_EMU
|
||||||
|
kvm_handler BOOKE_INTERRUPT_HV_SYSCALL, SPRN_SRR0, SPRN_SRR1, 0
|
||||||
|
kvm_handler BOOKE_INTERRUPT_GUEST_DBELL, SPRN_GSRR0, SPRN_GSRR1, 0
|
||||||
|
kvm_lvl_handler BOOKE_INTERRUPT_GUEST_DBELL_CRIT, \
|
||||||
|
SPRN_SPRG_RSCRATCH_CRIT, SPRN_CSRR0, SPRN_CSRR1, 0
|
||||||
|
kvm_lvl_handler BOOKE_INTERRUPT_DEBUG, \
|
||||||
|
SPRN_SPRG_RSCRATCH_CRIT, SPRN_CSRR0, SPRN_CSRR1, 0
|
||||||
|
kvm_lvl_handler BOOKE_INTERRUPT_DEBUG, \
|
||||||
|
SPRN_SPRG_RSCRATCH_DBG, SPRN_DSRR0, SPRN_DSRR1, 0
|
||||||
|
|
||||||
|
|
||||||
|
/* Registers:
|
||||||
|
* SPRG_SCRATCH0: guest r10
|
||||||
|
* r4: vcpu pointer
|
||||||
|
* r11: vcpu->arch.shared
|
||||||
|
* r14: KVM exit number
|
||||||
|
*/
|
||||||
|
_GLOBAL(kvmppc_resume_host)
|
||||||
|
/* Save remaining volatile guest register state to vcpu. */
|
||||||
|
mfspr r3, SPRN_VRSAVE
|
||||||
|
PPC_STL r0, VCPU_GPR(r0)(r4)
|
||||||
|
PPC_STL r1, VCPU_GPR(r1)(r4)
|
||||||
|
mflr r5
|
||||||
|
mfspr r6, SPRN_SPRG4
|
||||||
|
PPC_STL r2, VCPU_GPR(r2)(r4)
|
||||||
|
PPC_STL r5, VCPU_LR(r4)
|
||||||
|
mfspr r7, SPRN_SPRG5
|
||||||
|
PPC_STL r3, VCPU_VRSAVE(r4)
|
||||||
|
PPC_STL r6, VCPU_SHARED_SPRG4(r11)
|
||||||
|
mfspr r8, SPRN_SPRG6
|
||||||
|
PPC_STL r7, VCPU_SHARED_SPRG5(r11)
|
||||||
|
mfspr r9, SPRN_SPRG7
|
||||||
|
PPC_STL r8, VCPU_SHARED_SPRG6(r11)
|
||||||
|
mfxer r3
|
||||||
|
PPC_STL r9, VCPU_SHARED_SPRG7(r11)
|
||||||
|
|
||||||
|
/* save guest MAS registers and restore host mas4 & mas6 */
|
||||||
|
mfspr r5, SPRN_MAS0
|
||||||
|
PPC_STL r3, VCPU_XER(r4)
|
||||||
|
mfspr r6, SPRN_MAS1
|
||||||
|
stw r5, VCPU_SHARED_MAS0(r11)
|
||||||
|
mfspr r7, SPRN_MAS2
|
||||||
|
stw r6, VCPU_SHARED_MAS1(r11)
|
||||||
|
#ifndef CONFIG_64BIT
|
||||||
|
stw r7, (VCPU_SHARED_MAS2 + 4)(r11)
|
||||||
|
#else
|
||||||
|
std r7, (VCPU_SHARED_MAS2)(r11)
|
||||||
|
#endif
|
||||||
|
mfspr r5, SPRN_MAS3
|
||||||
|
mfspr r6, SPRN_MAS4
|
||||||
|
stw r5, VCPU_SHARED_MAS7_3+4(r11)
|
||||||
|
mfspr r7, SPRN_MAS6
|
||||||
|
stw r6, VCPU_SHARED_MAS4(r11)
|
||||||
|
mfspr r5, SPRN_MAS7
|
||||||
|
lwz r6, VCPU_HOST_MAS4(r4)
|
||||||
|
stw r7, VCPU_SHARED_MAS6(r11)
|
||||||
|
lwz r8, VCPU_HOST_MAS6(r4)
|
||||||
|
mtspr SPRN_MAS4, r6
|
||||||
|
stw r5, VCPU_SHARED_MAS7_3+0(r11)
|
||||||
|
mtspr SPRN_MAS6, r8
|
||||||
|
mfspr r3, SPRN_EPCR
|
||||||
|
rlwinm r3, r3, 0, ~SPRN_EPCR_DMIUH
|
||||||
|
mtspr SPRN_EPCR, r3
|
||||||
|
isync
|
||||||
|
|
||||||
|
/* Restore host stack pointer */
|
||||||
|
PPC_LL r1, VCPU_HOST_STACK(r4)
|
||||||
|
PPC_LL r2, HOST_R2(r1)
|
||||||
|
|
||||||
|
/* Switch to kernel stack and jump to handler. */
|
||||||
|
PPC_LL r3, HOST_RUN(r1)
|
||||||
|
mr r5, r14 /* intno */
|
||||||
|
mr r14, r4 /* Save vcpu pointer. */
|
||||||
|
bl kvmppc_handle_exit
|
||||||
|
|
||||||
|
/* Restore vcpu pointer and the nonvolatiles we used. */
|
||||||
|
mr r4, r14
|
||||||
|
PPC_LL r14, VCPU_GPR(r14)(r4)
|
||||||
|
|
||||||
|
andi. r5, r3, RESUME_FLAG_NV
|
||||||
|
beq skip_nv_load
|
||||||
|
PPC_LL r15, VCPU_GPR(r15)(r4)
|
||||||
|
PPC_LL r16, VCPU_GPR(r16)(r4)
|
||||||
|
PPC_LL r17, VCPU_GPR(r17)(r4)
|
||||||
|
PPC_LL r18, VCPU_GPR(r18)(r4)
|
||||||
|
PPC_LL r19, VCPU_GPR(r19)(r4)
|
||||||
|
PPC_LL r20, VCPU_GPR(r20)(r4)
|
||||||
|
PPC_LL r21, VCPU_GPR(r21)(r4)
|
||||||
|
PPC_LL r22, VCPU_GPR(r22)(r4)
|
||||||
|
PPC_LL r23, VCPU_GPR(r23)(r4)
|
||||||
|
PPC_LL r24, VCPU_GPR(r24)(r4)
|
||||||
|
PPC_LL r25, VCPU_GPR(r25)(r4)
|
||||||
|
PPC_LL r26, VCPU_GPR(r26)(r4)
|
||||||
|
PPC_LL r27, VCPU_GPR(r27)(r4)
|
||||||
|
PPC_LL r28, VCPU_GPR(r28)(r4)
|
||||||
|
PPC_LL r29, VCPU_GPR(r29)(r4)
|
||||||
|
PPC_LL r30, VCPU_GPR(r30)(r4)
|
||||||
|
PPC_LL r31, VCPU_GPR(r31)(r4)
|
||||||
|
skip_nv_load:
|
||||||
|
/* Should we return to the guest? */
|
||||||
|
andi. r5, r3, RESUME_FLAG_HOST
|
||||||
|
beq lightweight_exit
|
||||||
|
|
||||||
|
srawi r3, r3, 2 /* Shift -ERR back down. */
|
||||||
|
|
||||||
|
heavyweight_exit:
|
||||||
|
/* Not returning to guest. */
|
||||||
|
PPC_LL r5, HOST_STACK_LR(r1)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We already saved guest volatile register state; now save the
|
||||||
|
* non-volatiles.
|
||||||
|
*/
|
||||||
|
|
||||||
|
PPC_STL r15, VCPU_GPR(r15)(r4)
|
||||||
|
PPC_STL r16, VCPU_GPR(r16)(r4)
|
||||||
|
PPC_STL r17, VCPU_GPR(r17)(r4)
|
||||||
|
PPC_STL r18, VCPU_GPR(r18)(r4)
|
||||||
|
PPC_STL r19, VCPU_GPR(r19)(r4)
|
||||||
|
PPC_STL r20, VCPU_GPR(r20)(r4)
|
||||||
|
PPC_STL r21, VCPU_GPR(r21)(r4)
|
||||||
|
PPC_STL r22, VCPU_GPR(r22)(r4)
|
||||||
|
PPC_STL r23, VCPU_GPR(r23)(r4)
|
||||||
|
PPC_STL r24, VCPU_GPR(r24)(r4)
|
||||||
|
PPC_STL r25, VCPU_GPR(r25)(r4)
|
||||||
|
PPC_STL r26, VCPU_GPR(r26)(r4)
|
||||||
|
PPC_STL r27, VCPU_GPR(r27)(r4)
|
||||||
|
PPC_STL r28, VCPU_GPR(r28)(r4)
|
||||||
|
PPC_STL r29, VCPU_GPR(r29)(r4)
|
||||||
|
PPC_STL r30, VCPU_GPR(r30)(r4)
|
||||||
|
PPC_STL r31, VCPU_GPR(r31)(r4)
|
||||||
|
|
||||||
|
/* Load host non-volatile register state from host stack. */
|
||||||
|
PPC_LL r14, HOST_NV_GPR(r14)(r1)
|
||||||
|
PPC_LL r15, HOST_NV_GPR(r15)(r1)
|
||||||
|
PPC_LL r16, HOST_NV_GPR(r16)(r1)
|
||||||
|
PPC_LL r17, HOST_NV_GPR(r17)(r1)
|
||||||
|
PPC_LL r18, HOST_NV_GPR(r18)(r1)
|
||||||
|
PPC_LL r19, HOST_NV_GPR(r19)(r1)
|
||||||
|
PPC_LL r20, HOST_NV_GPR(r20)(r1)
|
||||||
|
PPC_LL r21, HOST_NV_GPR(r21)(r1)
|
||||||
|
PPC_LL r22, HOST_NV_GPR(r22)(r1)
|
||||||
|
PPC_LL r23, HOST_NV_GPR(r23)(r1)
|
||||||
|
PPC_LL r24, HOST_NV_GPR(r24)(r1)
|
||||||
|
PPC_LL r25, HOST_NV_GPR(r25)(r1)
|
||||||
|
PPC_LL r26, HOST_NV_GPR(r26)(r1)
|
||||||
|
PPC_LL r27, HOST_NV_GPR(r27)(r1)
|
||||||
|
PPC_LL r28, HOST_NV_GPR(r28)(r1)
|
||||||
|
PPC_LL r29, HOST_NV_GPR(r29)(r1)
|
||||||
|
PPC_LL r30, HOST_NV_GPR(r30)(r1)
|
||||||
|
PPC_LL r31, HOST_NV_GPR(r31)(r1)
|
||||||
|
|
||||||
|
/* Return to kvm_vcpu_run(). */
|
||||||
|
mtlr r5
|
||||||
|
addi r1, r1, HOST_STACK_SIZE
|
||||||
|
/* r3 still contains the return code from kvmppc_handle_exit(). */
|
||||||
|
blr
|
||||||
|
|
||||||
|
/* Registers:
|
||||||
|
* r3: kvm_run pointer
|
||||||
|
* r4: vcpu pointer
|
||||||
|
*/
|
||||||
|
_GLOBAL(__kvmppc_vcpu_run)
|
||||||
|
stwu r1, -HOST_STACK_SIZE(r1)
|
||||||
|
PPC_STL r1, VCPU_HOST_STACK(r4) /* Save stack pointer to vcpu. */
|
||||||
|
|
||||||
|
/* Save host state to stack. */
|
||||||
|
PPC_STL r3, HOST_RUN(r1)
|
||||||
|
mflr r3
|
||||||
|
PPC_STL r3, HOST_STACK_LR(r1)
|
||||||
|
|
||||||
|
/* Save host non-volatile register state to stack. */
|
||||||
|
PPC_STL r14, HOST_NV_GPR(r14)(r1)
|
||||||
|
PPC_STL r15, HOST_NV_GPR(r15)(r1)
|
||||||
|
PPC_STL r16, HOST_NV_GPR(r16)(r1)
|
||||||
|
PPC_STL r17, HOST_NV_GPR(r17)(r1)
|
||||||
|
PPC_STL r18, HOST_NV_GPR(r18)(r1)
|
||||||
|
PPC_STL r19, HOST_NV_GPR(r19)(r1)
|
||||||
|
PPC_STL r20, HOST_NV_GPR(r20)(r1)
|
||||||
|
PPC_STL r21, HOST_NV_GPR(r21)(r1)
|
||||||
|
PPC_STL r22, HOST_NV_GPR(r22)(r1)
|
||||||
|
PPC_STL r23, HOST_NV_GPR(r23)(r1)
|
||||||
|
PPC_STL r24, HOST_NV_GPR(r24)(r1)
|
||||||
|
PPC_STL r25, HOST_NV_GPR(r25)(r1)
|
||||||
|
PPC_STL r26, HOST_NV_GPR(r26)(r1)
|
||||||
|
PPC_STL r27, HOST_NV_GPR(r27)(r1)
|
||||||
|
PPC_STL r28, HOST_NV_GPR(r28)(r1)
|
||||||
|
PPC_STL r29, HOST_NV_GPR(r29)(r1)
|
||||||
|
PPC_STL r30, HOST_NV_GPR(r30)(r1)
|
||||||
|
PPC_STL r31, HOST_NV_GPR(r31)(r1)
|
||||||
|
|
||||||
|
/* Load guest non-volatiles. */
|
||||||
|
PPC_LL r14, VCPU_GPR(r14)(r4)
|
||||||
|
PPC_LL r15, VCPU_GPR(r15)(r4)
|
||||||
|
PPC_LL r16, VCPU_GPR(r16)(r4)
|
||||||
|
PPC_LL r17, VCPU_GPR(r17)(r4)
|
||||||
|
PPC_LL r18, VCPU_GPR(r18)(r4)
|
||||||
|
PPC_LL r19, VCPU_GPR(r19)(r4)
|
||||||
|
PPC_LL r20, VCPU_GPR(r20)(r4)
|
||||||
|
PPC_LL r21, VCPU_GPR(r21)(r4)
|
||||||
|
PPC_LL r22, VCPU_GPR(r22)(r4)
|
||||||
|
PPC_LL r23, VCPU_GPR(r23)(r4)
|
||||||
|
PPC_LL r24, VCPU_GPR(r24)(r4)
|
||||||
|
PPC_LL r25, VCPU_GPR(r25)(r4)
|
||||||
|
PPC_LL r26, VCPU_GPR(r26)(r4)
|
||||||
|
PPC_LL r27, VCPU_GPR(r27)(r4)
|
||||||
|
PPC_LL r28, VCPU_GPR(r28)(r4)
|
||||||
|
PPC_LL r29, VCPU_GPR(r29)(r4)
|
||||||
|
PPC_LL r30, VCPU_GPR(r30)(r4)
|
||||||
|
PPC_LL r31, VCPU_GPR(r31)(r4)
|
||||||
|
|
||||||
|
|
||||||
|
lightweight_exit:
|
||||||
|
PPC_STL r2, HOST_R2(r1)
|
||||||
|
|
||||||
|
mfspr r3, SPRN_PID
|
||||||
|
stw r3, VCPU_HOST_PID(r4)
|
||||||
|
lwz r3, VCPU_GUEST_PID(r4)
|
||||||
|
mtspr SPRN_PID, r3
|
||||||
|
|
||||||
|
/* Save vcpu pointer for the exception handlers
|
||||||
|
* must be done before loading guest r2.
|
||||||
|
*/
|
||||||
|
// SET_VCPU(r4)
|
||||||
|
|
||||||
|
PPC_LL r11, VCPU_SHARED(r4)
|
||||||
|
/* Save host mas4 and mas6 and load guest MAS registers */
|
||||||
|
mfspr r3, SPRN_MAS4
|
||||||
|
stw r3, VCPU_HOST_MAS4(r4)
|
||||||
|
mfspr r3, SPRN_MAS6
|
||||||
|
stw r3, VCPU_HOST_MAS6(r4)
|
||||||
|
lwz r3, VCPU_SHARED_MAS0(r11)
|
||||||
|
lwz r5, VCPU_SHARED_MAS1(r11)
|
||||||
|
#ifndef CONFIG_64BIT
|
||||||
|
lwz r6, (VCPU_SHARED_MAS2 + 4)(r11)
|
||||||
|
#else
|
||||||
|
ld r6, (VCPU_SHARED_MAS2)(r11)
|
||||||
|
#endif
|
||||||
|
lwz r7, VCPU_SHARED_MAS7_3+4(r11)
|
||||||
|
lwz r8, VCPU_SHARED_MAS4(r11)
|
||||||
|
mtspr SPRN_MAS0, r3
|
||||||
|
mtspr SPRN_MAS1, r5
|
||||||
|
mtspr SPRN_MAS2, r6
|
||||||
|
mtspr SPRN_MAS3, r7
|
||||||
|
mtspr SPRN_MAS4, r8
|
||||||
|
lwz r3, VCPU_SHARED_MAS6(r11)
|
||||||
|
lwz r5, VCPU_SHARED_MAS7_3+0(r11)
|
||||||
|
mtspr SPRN_MAS6, r3
|
||||||
|
mtspr SPRN_MAS7, r5
|
||||||
|
/* Disable MAS register updates via exception */
|
||||||
|
mfspr r3, SPRN_EPCR
|
||||||
|
oris r3, r3, SPRN_EPCR_DMIUH@h
|
||||||
|
mtspr SPRN_EPCR, r3
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Host interrupt handlers may have clobbered these guest-readable
|
||||||
|
* SPRGs, so we need to reload them here with the guest's values.
|
||||||
|
*/
|
||||||
|
lwz r3, VCPU_VRSAVE(r4)
|
||||||
|
lwz r5, VCPU_SHARED_SPRG4(r11)
|
||||||
|
mtspr SPRN_VRSAVE, r3
|
||||||
|
lwz r6, VCPU_SHARED_SPRG5(r11)
|
||||||
|
mtspr SPRN_SPRG4W, r5
|
||||||
|
lwz r7, VCPU_SHARED_SPRG6(r11)
|
||||||
|
mtspr SPRN_SPRG5W, r6
|
||||||
|
lwz r8, VCPU_SHARED_SPRG7(r11)
|
||||||
|
mtspr SPRN_SPRG6W, r7
|
||||||
|
mtspr SPRN_SPRG7W, r8
|
||||||
|
|
||||||
|
/* Load some guest volatiles. */
|
||||||
|
PPC_LL r3, VCPU_LR(r4)
|
||||||
|
PPC_LL r5, VCPU_XER(r4)
|
||||||
|
PPC_LL r6, VCPU_CTR(r4)
|
||||||
|
PPC_LL r7, VCPU_CR(r4)
|
||||||
|
PPC_LL r8, VCPU_PC(r4)
|
||||||
|
#ifndef CONFIG_64BIT
|
||||||
|
lwz r9, (VCPU_SHARED_MSR + 4)(r11)
|
||||||
|
#else
|
||||||
|
ld r9, (VCPU_SHARED_MSR)(r11)
|
||||||
|
#endif
|
||||||
|
PPC_LL r0, VCPU_GPR(r0)(r4)
|
||||||
|
PPC_LL r1, VCPU_GPR(r1)(r4)
|
||||||
|
PPC_LL r2, VCPU_GPR(r2)(r4)
|
||||||
|
PPC_LL r10, VCPU_GPR(r10)(r4)
|
||||||
|
PPC_LL r11, VCPU_GPR(r11)(r4)
|
||||||
|
PPC_LL r12, VCPU_GPR(r12)(r4)
|
||||||
|
PPC_LL r13, VCPU_GPR(r13)(r4)
|
||||||
|
mtlr r3
|
||||||
|
mtxer r5
|
||||||
|
mtctr r6
|
||||||
|
mtcr r7
|
||||||
|
mtsrr0 r8
|
||||||
|
mtsrr1 r9
|
||||||
|
|
||||||
|
#ifdef CONFIG_KVM_EXIT_TIMING
|
||||||
|
/* save enter time */
|
||||||
|
1:
|
||||||
|
mfspr r6, SPRN_TBRU
|
||||||
|
mfspr r7, SPRN_TBRL
|
||||||
|
mfspr r8, SPRN_TBRU
|
||||||
|
cmpw r8, r6
|
||||||
|
PPC_STL r7, VCPU_TIMING_LAST_ENTER_TBL(r4)
|
||||||
|
bne 1b
|
||||||
|
PPC_STL r8, VCPU_TIMING_LAST_ENTER_TBU(r4)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Finish loading guest volatiles and jump to guest. */
|
||||||
|
PPC_LL r5, VCPU_GPR(r5)(r4)
|
||||||
|
PPC_LL r6, VCPU_GPR(r6)(r4)
|
||||||
|
PPC_LL r7, VCPU_GPR(r7)(r4)
|
||||||
|
PPC_LL r8, VCPU_GPR(r8)(r4)
|
||||||
|
PPC_LL r9, VCPU_GPR(r9)(r4)
|
||||||
|
|
||||||
|
PPC_LL r3, VCPU_GPR(r3)(r4)
|
||||||
|
PPC_LL r4, VCPU_GPR(r4)(r4)
|
||||||
|
rfi
|
@ -114,6 +114,11 @@ int kvmppc_sanity_check(struct kvm_vcpu *vcpu)
|
|||||||
goto out;
|
goto out;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef CONFIG_KVM_BOOKE_HV
|
||||||
|
if (!cpu_has_feature(CPU_FTR_EMB_HV))
|
||||||
|
goto out;
|
||||||
|
#endif
|
||||||
|
|
||||||
r = true;
|
r = true;
|
||||||
|
|
||||||
out:
|
out:
|
||||||
|
@ -93,6 +93,12 @@ static inline void kvmppc_account_exit_stat(struct kvm_vcpu *vcpu, int type)
|
|||||||
case SIGNAL_EXITS:
|
case SIGNAL_EXITS:
|
||||||
vcpu->stat.signal_exits++;
|
vcpu->stat.signal_exits++;
|
||||||
break;
|
break;
|
||||||
|
case DBELL_EXITS:
|
||||||
|
vcpu->stat.dbell_exits++;
|
||||||
|
break;
|
||||||
|
case GDBELL_EXITS:
|
||||||
|
vcpu->stat.gdbell_exits++;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user