KVM: PPC: booke: Allow multiple exception types

Current kvmppc_booke_handlers uses the same macro (KVM_HANDLER) and
all handlers are considered to be the same size. This will not be
the case if we want to use different macros for different handlers.

This patch improves the kvmppc_booke_handler so that it can
support different macros for different handlers.

Signed-off-by: Liu Yu <yu.liu@freescale.com>
[bharat.bhushan@freescale.com: Substantial changes]
Signed-off-by: Bharat Bhushan <bharat.bhushan@freescale.com>
Signed-off-by: Alexander Graf <agraf@suse.de>
This commit is contained in:
Bharat Bhushan 2013-01-15 22:24:39 +00:00 committed by Alexander Graf
parent ffe129ecd7
commit 1d542d9c2b
5 changed files with 54 additions and 16 deletions

View File

@ -49,8 +49,6 @@ enum emulation_result {
extern int kvmppc_vcpu_run(struct kvm_run *kvm_run, struct kvm_vcpu *vcpu); extern int kvmppc_vcpu_run(struct kvm_run *kvm_run, struct kvm_vcpu *vcpu);
extern int __kvmppc_vcpu_run(struct kvm_run *kvm_run, struct kvm_vcpu *vcpu); extern int __kvmppc_vcpu_run(struct kvm_run *kvm_run, struct kvm_vcpu *vcpu);
extern char kvmppc_handlers_start[];
extern unsigned long kvmppc_handler_len;
extern void kvmppc_handler_highmem(void); extern void kvmppc_handler_highmem(void);
extern void kvmppc_dump_vcpu(struct kvm_vcpu *vcpu); extern void kvmppc_dump_vcpu(struct kvm_vcpu *vcpu);

View File

@ -1594,7 +1594,9 @@ int __init kvmppc_booke_init(void)
{ {
#ifndef CONFIG_KVM_BOOKE_HV #ifndef CONFIG_KVM_BOOKE_HV
unsigned long ivor[16]; unsigned long ivor[16];
unsigned long *handler = kvmppc_booke_handler_addr;
unsigned long max_ivor = 0; unsigned long max_ivor = 0;
unsigned long handler_len;
int i; int i;
/* We install our own exception handlers by hijacking IVPR. IVPR must /* We install our own exception handlers by hijacking IVPR. IVPR must
@ -1627,14 +1629,16 @@ int __init kvmppc_booke_init(void)
for (i = 0; i < 16; i++) { for (i = 0; i < 16; i++) {
if (ivor[i] > max_ivor) if (ivor[i] > max_ivor)
max_ivor = ivor[i]; max_ivor = i;
handler_len = handler[i + 1] - handler[i];
memcpy((void *)kvmppc_booke_handlers + ivor[i], memcpy((void *)kvmppc_booke_handlers + ivor[i],
kvmppc_handlers_start + i * kvmppc_handler_len, (void *)handler[i], handler_len);
kvmppc_handler_len);
} }
flush_icache_range(kvmppc_booke_handlers,
kvmppc_booke_handlers + max_ivor + kvmppc_handler_len); handler_len = handler[max_ivor + 1] - handler[max_ivor];
flush_icache_range(kvmppc_booke_handlers, kvmppc_booke_handlers +
ivor[max_ivor] + handler_len);
#endif /* !BOOKE_HV */ #endif /* !BOOKE_HV */
return 0; return 0;
} }

View File

@ -65,6 +65,7 @@
(1 << BOOKE_IRQPRIO_CRITICAL)) (1 << BOOKE_IRQPRIO_CRITICAL))
extern unsigned long kvmppc_booke_handlers; extern unsigned long kvmppc_booke_handlers;
extern unsigned long kvmppc_booke_handler_addr[];
void kvmppc_set_msr(struct kvm_vcpu *vcpu, u32 new_msr); void kvmppc_set_msr(struct kvm_vcpu *vcpu, u32 new_msr);
void kvmppc_mmu_msr_notify(struct kvm_vcpu *vcpu, u32 old_msr); void kvmppc_mmu_msr_notify(struct kvm_vcpu *vcpu, u32 old_msr);

View File

@ -74,6 +74,14 @@ _GLOBAL(kvmppc_handler_\ivor_nr)
bctr bctr
.endm .endm
.macro KVM_HANDLER_ADDR ivor_nr
.long kvmppc_handler_\ivor_nr
.endm
.macro KVM_HANDLER_END
.long kvmppc_handlers_end
.endm
_GLOBAL(kvmppc_handlers_start) _GLOBAL(kvmppc_handlers_start)
KVM_HANDLER BOOKE_INTERRUPT_CRITICAL SPRN_SPRG_RSCRATCH_CRIT SPRN_CSRR0 KVM_HANDLER BOOKE_INTERRUPT_CRITICAL SPRN_SPRG_RSCRATCH_CRIT SPRN_CSRR0
KVM_HANDLER BOOKE_INTERRUPT_MACHINE_CHECK SPRN_SPRG_RSCRATCH_MC SPRN_MCSRR0 KVM_HANDLER BOOKE_INTERRUPT_MACHINE_CHECK SPRN_SPRG_RSCRATCH_MC SPRN_MCSRR0
@ -94,9 +102,7 @@ KVM_HANDLER BOOKE_INTERRUPT_DEBUG SPRN_SPRG_RSCRATCH_CRIT SPRN_CSRR0
KVM_HANDLER BOOKE_INTERRUPT_SPE_UNAVAIL SPRN_SPRG_RSCRATCH0 SPRN_SRR0 KVM_HANDLER BOOKE_INTERRUPT_SPE_UNAVAIL SPRN_SPRG_RSCRATCH0 SPRN_SRR0
KVM_HANDLER BOOKE_INTERRUPT_SPE_FP_DATA SPRN_SPRG_RSCRATCH0 SPRN_SRR0 KVM_HANDLER BOOKE_INTERRUPT_SPE_FP_DATA SPRN_SPRG_RSCRATCH0 SPRN_SRR0
KVM_HANDLER BOOKE_INTERRUPT_SPE_FP_ROUND SPRN_SPRG_RSCRATCH0 SPRN_SRR0 KVM_HANDLER BOOKE_INTERRUPT_SPE_FP_ROUND SPRN_SPRG_RSCRATCH0 SPRN_SRR0
_GLOBAL(kvmppc_handlers_end)
_GLOBAL(kvmppc_handler_len)
.long kvmppc_handler_1 - kvmppc_handler_0
/* Registers: /* Registers:
* SPRG_SCRATCH0: guest r4 * SPRG_SCRATCH0: guest r4
@ -461,6 +467,31 @@ lightweight_exit:
lwz r4, VCPU_GPR(R4)(r4) lwz r4, VCPU_GPR(R4)(r4)
rfi rfi
.data
.align 4
.globl kvmppc_booke_handler_addr
kvmppc_booke_handler_addr:
KVM_HANDLER_ADDR BOOKE_INTERRUPT_CRITICAL
KVM_HANDLER_ADDR BOOKE_INTERRUPT_MACHINE_CHECK
KVM_HANDLER_ADDR BOOKE_INTERRUPT_DATA_STORAGE
KVM_HANDLER_ADDR BOOKE_INTERRUPT_INST_STORAGE
KVM_HANDLER_ADDR BOOKE_INTERRUPT_EXTERNAL
KVM_HANDLER_ADDR BOOKE_INTERRUPT_ALIGNMENT
KVM_HANDLER_ADDR BOOKE_INTERRUPT_PROGRAM
KVM_HANDLER_ADDR BOOKE_INTERRUPT_FP_UNAVAIL
KVM_HANDLER_ADDR BOOKE_INTERRUPT_SYSCALL
KVM_HANDLER_ADDR BOOKE_INTERRUPT_AP_UNAVAIL
KVM_HANDLER_ADDR BOOKE_INTERRUPT_DECREMENTER
KVM_HANDLER_ADDR BOOKE_INTERRUPT_FIT
KVM_HANDLER_ADDR BOOKE_INTERRUPT_WATCHDOG
KVM_HANDLER_ADDR BOOKE_INTERRUPT_DTLB_MISS
KVM_HANDLER_ADDR BOOKE_INTERRUPT_ITLB_MISS
KVM_HANDLER_ADDR BOOKE_INTERRUPT_DEBUG
KVM_HANDLER_ADDR BOOKE_INTERRUPT_SPE_UNAVAIL
KVM_HANDLER_ADDR BOOKE_INTERRUPT_SPE_FP_DATA
KVM_HANDLER_ADDR BOOKE_INTERRUPT_SPE_FP_ROUND
KVM_HANDLER_END /*Always keep this in end*/
#ifdef CONFIG_SPE #ifdef CONFIG_SPE
_GLOBAL(kvmppc_save_guest_spe) _GLOBAL(kvmppc_save_guest_spe)
cmpi 0,r3,0 cmpi 0,r3,0

View File

@ -491,6 +491,9 @@ static int __init kvmppc_e500_init(void)
{ {
int r, i; int r, i;
unsigned long ivor[3]; unsigned long ivor[3];
/* Process remaining handlers above the generic first 16 */
unsigned long *handler = &kvmppc_booke_handler_addr[16];
unsigned long handler_len;
unsigned long max_ivor = 0; unsigned long max_ivor = 0;
r = kvmppc_core_check_processor_compat(); r = kvmppc_core_check_processor_compat();
@ -506,15 +509,16 @@ static int __init kvmppc_e500_init(void)
ivor[1] = mfspr(SPRN_IVOR33); ivor[1] = mfspr(SPRN_IVOR33);
ivor[2] = mfspr(SPRN_IVOR34); ivor[2] = mfspr(SPRN_IVOR34);
for (i = 0; i < 3; i++) { for (i = 0; i < 3; i++) {
if (ivor[i] > max_ivor) if (ivor[i] > ivor[max_ivor])
max_ivor = ivor[i]; max_ivor = i;
handler_len = handler[i + 1] - handler[i];
memcpy((void *)kvmppc_booke_handlers + ivor[i], memcpy((void *)kvmppc_booke_handlers + ivor[i],
kvmppc_handlers_start + (i + 16) * kvmppc_handler_len, (void *)handler[i], handler_len);
kvmppc_handler_len);
} }
flush_icache_range(kvmppc_booke_handlers, handler_len = handler[max_ivor + 1] - handler[max_ivor];
kvmppc_booke_handlers + max_ivor + kvmppc_handler_len); flush_icache_range(kvmppc_booke_handlers, kvmppc_booke_handlers +
ivor[max_ivor] + handler_len);
return kvm_init(NULL, sizeof(struct kvmppc_vcpu_e500), 0, THIS_MODULE); return kvm_init(NULL, sizeof(struct kvmppc_vcpu_e500), 0, THIS_MODULE);
} }