123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429 |
- /* SPDX-License-Identifier: GPL-2.0-only */
- #include <asm/asm-offsets.h>
- #include <asm/cache.h>
- #include <asm/code-patching-asm.h>
- #include <asm/exception-64s.h>
- #include <asm/export.h>
- #include <asm/kvm_asm.h>
- #include <asm/kvm_book3s_asm.h>
- #include <asm/mmu.h>
- #include <asm/ppc_asm.h>
- #include <asm/ptrace.h>
- #include <asm/reg.h>
- #include <asm/ultravisor-api.h>
- /*
- * These are branched to from interrupt handlers in exception-64s.S which set
- * IKVM_REAL or IKVM_VIRT, if HSTATE_IN_GUEST was found to be non-zero.
- */
- /*
- * This is a hcall, so register convention is as
- * Documentation/powerpc/papr_hcalls.rst.
- *
- * This may also be a syscall from PR-KVM userspace that is to be
- * reflected to the PR guest kernel, so registers may be set up for
- * a system call rather than hcall. We don't currently clobber
- * anything here, but the 0xc00 handler has already clobbered CTR
- * and CR0, so PR-KVM can not support a guest kernel that preserves
- * those registers across its system calls.
- *
- * The state of registers is as kvmppc_interrupt, except CFAR is not
- * saved, R13 is not in SCRATCH0, and R10 does not contain the trap.
- */
- .global kvmppc_hcall
- .balign IFETCH_ALIGN_BYTES
- kvmppc_hcall:
- #ifdef CONFIG_KVM_BOOK3S_HV_POSSIBLE
- lbz r10,HSTATE_IN_GUEST(r13)
- cmpwi r10,KVM_GUEST_MODE_HV_P9
- beq kvmppc_p9_exit_hcall
- #endif
- ld r10,PACA_EXGEN+EX_R13(r13)
- SET_SCRATCH0(r10)
- li r10,0xc00
- /* Now we look like kvmppc_interrupt */
- li r11,PACA_EXGEN
- b .Lgot_save_area
- /*
- * KVM interrupt entry occurs after GEN_INT_ENTRY runs, and follows that
- * call convention:
- *
- * guest R9-R13, CTR, CFAR, PPR saved in PACA EX_xxx save area
- * guest (H)DAR, (H)DSISR are also in the save area for relevant interrupts
- * guest R13 also saved in SCRATCH0
- * R13 = PACA
- * R11 = (H)SRR0
- * R12 = (H)SRR1
- * R9 = guest CR
- * PPR is set to medium
- *
- * With the addition for KVM:
- * R10 = trap vector
- */
- .global kvmppc_interrupt
- .balign IFETCH_ALIGN_BYTES
- kvmppc_interrupt:
- #ifdef CONFIG_KVM_BOOK3S_HV_POSSIBLE
- std r10,HSTATE_SCRATCH0(r13)
- lbz r10,HSTATE_IN_GUEST(r13)
- cmpwi r10,KVM_GUEST_MODE_HV_P9
- beq kvmppc_p9_exit_interrupt
- ld r10,HSTATE_SCRATCH0(r13)
- #endif
- li r11,PACA_EXGEN
- cmpdi r10,0x200
- bgt+ .Lgot_save_area
- li r11,PACA_EXMC
- beq .Lgot_save_area
- li r11,PACA_EXNMI
- .Lgot_save_area:
- add r11,r11,r13
- BEGIN_FTR_SECTION
- ld r12,EX_CFAR(r11)
- std r12,HSTATE_CFAR(r13)
- END_FTR_SECTION_IFSET(CPU_FTR_CFAR)
- ld r12,EX_CTR(r11)
- mtctr r12
- BEGIN_FTR_SECTION
- ld r12,EX_PPR(r11)
- std r12,HSTATE_PPR(r13)
- END_FTR_SECTION_IFSET(CPU_FTR_HAS_PPR)
- ld r12,EX_R12(r11)
- std r12,HSTATE_SCRATCH0(r13)
- sldi r12,r9,32
- or r12,r12,r10
- ld r9,EX_R9(r11)
- ld r10,EX_R10(r11)
- ld r11,EX_R11(r11)
- /*
- * Hcalls and other interrupts come here after normalising register
- * contents and save locations:
- *
- * R12 = (guest CR << 32) | interrupt vector
- * R13 = PACA
- * guest R12 saved in shadow HSTATE_SCRATCH0
- * guest R13 saved in SPRN_SCRATCH0
- */
- std r9,HSTATE_SCRATCH2(r13)
- lbz r9,HSTATE_IN_GUEST(r13)
- cmpwi r9,KVM_GUEST_MODE_SKIP
- beq- .Lmaybe_skip
- .Lno_skip:
- #ifdef CONFIG_KVM_BOOK3S_HV_POSSIBLE
- #ifdef CONFIG_KVM_BOOK3S_PR_POSSIBLE
- cmpwi r9,KVM_GUEST_MODE_GUEST
- beq kvmppc_interrupt_pr
- #endif
- b kvmppc_interrupt_hv
- #else
- b kvmppc_interrupt_pr
- #endif
- /*
- * "Skip" interrupts are part of a trick KVM uses a with hash guests to load
- * the faulting instruction in guest memory from the hypervisor without
- * walking page tables.
- *
- * When the guest takes a fault that requires the hypervisor to load the
- * instruction (e.g., MMIO emulation), KVM is running in real-mode with HV=1
- * and the guest MMU context loaded. It sets KVM_GUEST_MODE_SKIP, and sets
- * MSR[DR]=1 while leaving MSR[IR]=0, so it continues to fetch HV instructions
- * but loads and stores will access the guest context. This is used to load
- * the faulting instruction using the faulting guest effective address.
- *
- * However the guest context may not be able to translate, or it may cause a
- * machine check or other issue, which results in a fault in the host
- * (even with KVM-HV).
- *
- * These faults come here because KVM_GUEST_MODE_SKIP was set, so if they
- * are (or are likely) caused by that load, the instruction is skipped by
- * just returning with the PC advanced +4, where it is noticed the load did
- * not execute and it goes to the slow path which walks the page tables to
- * read guest memory.
- */
- .Lmaybe_skip:
- cmpwi r12,BOOK3S_INTERRUPT_MACHINE_CHECK
- beq 1f
- cmpwi r12,BOOK3S_INTERRUPT_DATA_STORAGE
- beq 1f
- cmpwi r12,BOOK3S_INTERRUPT_DATA_SEGMENT
- beq 1f
- #ifdef CONFIG_KVM_BOOK3S_HV_POSSIBLE
- /* HSRR interrupts get 2 added to interrupt number */
- cmpwi r12,BOOK3S_INTERRUPT_H_DATA_STORAGE | 0x2
- beq 2f
- #endif
- b .Lno_skip
- 1: mfspr r9,SPRN_SRR0
- addi r9,r9,4
- mtspr SPRN_SRR0,r9
- ld r12,HSTATE_SCRATCH0(r13)
- ld r9,HSTATE_SCRATCH2(r13)
- GET_SCRATCH0(r13)
- RFI_TO_KERNEL
- #ifdef CONFIG_KVM_BOOK3S_HV_POSSIBLE
- 2: mfspr r9,SPRN_HSRR0
- addi r9,r9,4
- mtspr SPRN_HSRR0,r9
- ld r12,HSTATE_SCRATCH0(r13)
- ld r9,HSTATE_SCRATCH2(r13)
- GET_SCRATCH0(r13)
- HRFI_TO_KERNEL
- #endif
- #ifdef CONFIG_KVM_BOOK3S_HV_POSSIBLE
- /* Stack frame offsets for kvmppc_p9_enter_guest */
- #define SFS (144 + STACK_FRAME_MIN_SIZE)
- #define STACK_SLOT_NVGPRS (SFS - 144) /* 18 gprs */
- /*
- * void kvmppc_p9_enter_guest(struct vcpu *vcpu);
- *
- * Enter the guest on a ISAv3.0 or later system.
- */
- .balign IFETCH_ALIGN_BYTES
- _GLOBAL(kvmppc_p9_enter_guest)
- EXPORT_SYMBOL_GPL(kvmppc_p9_enter_guest)
- mflr r0
- std r0,PPC_LR_STKOFF(r1)
- stdu r1,-SFS(r1)
- std r1,HSTATE_HOST_R1(r13)
- mfcr r4
- stw r4,SFS+8(r1)
- reg = 14
- .rept 18
- std reg,STACK_SLOT_NVGPRS + ((reg - 14) * 8)(r1)
- reg = reg + 1
- .endr
- ld r4,VCPU_LR(r3)
- mtlr r4
- ld r4,VCPU_CTR(r3)
- mtctr r4
- ld r4,VCPU_XER(r3)
- mtspr SPRN_XER,r4
- ld r1,VCPU_CR(r3)
- BEGIN_FTR_SECTION
- ld r4,VCPU_CFAR(r3)
- mtspr SPRN_CFAR,r4
- END_FTR_SECTION_IFSET(CPU_FTR_CFAR)
- BEGIN_FTR_SECTION
- ld r4,VCPU_PPR(r3)
- mtspr SPRN_PPR,r4
- END_FTR_SECTION_IFSET(CPU_FTR_HAS_PPR)
- reg = 4
- .rept 28
- ld reg,__VCPU_GPR(reg)(r3)
- reg = reg + 1
- .endr
- ld r4,VCPU_KVM(r3)
- lbz r4,KVM_SECURE_GUEST(r4)
- cmpdi r4,0
- ld r4,VCPU_GPR(R4)(r3)
- bne .Lret_to_ultra
- mtcr r1
- ld r0,VCPU_GPR(R0)(r3)
- ld r1,VCPU_GPR(R1)(r3)
- ld r2,VCPU_GPR(R2)(r3)
- ld r3,VCPU_GPR(R3)(r3)
- HRFI_TO_GUEST
- b .
- /*
- * Use UV_RETURN ultracall to return control back to the Ultravisor
- * after processing an hypercall or interrupt that was forwarded
- * (a.k.a. reflected) to the Hypervisor.
- *
- * All registers have already been reloaded except the ucall requires:
- * R0 = hcall result
- * R2 = SRR1, so UV can detect a synthesized interrupt (if any)
- * R3 = UV_RETURN
- */
- .Lret_to_ultra:
- mtcr r1
- ld r1,VCPU_GPR(R1)(r3)
- ld r0,VCPU_GPR(R3)(r3)
- mfspr r2,SPRN_SRR1
- LOAD_REG_IMMEDIATE(r3, UV_RETURN)
- sc 2
- /*
- * kvmppc_p9_exit_hcall and kvmppc_p9_exit_interrupt are branched to from
- * above if the interrupt was taken for a guest that was entered via
- * kvmppc_p9_enter_guest().
- *
- * The exit code recovers the host stack and vcpu pointer, saves all guest GPRs
- * and CR, LR, XER as well as guest MSR and NIA into the VCPU, then re-
- * establishes the host stack and registers to return from the
- * kvmppc_p9_enter_guest() function, which saves CTR and other guest registers
- * (SPRs and FP, VEC, etc).
- */
- .balign IFETCH_ALIGN_BYTES
- kvmppc_p9_exit_hcall:
- mfspr r11,SPRN_SRR0
- mfspr r12,SPRN_SRR1
- li r10,0xc00
- std r10,HSTATE_SCRATCH0(r13)
- .balign IFETCH_ALIGN_BYTES
- kvmppc_p9_exit_interrupt:
- /*
- * If set to KVM_GUEST_MODE_HV_P9 but we're still in the
- * hypervisor, that means we can't return from the entry stack.
- */
- rldicl. r10,r12,64-MSR_HV_LG,63
- bne- kvmppc_p9_bad_interrupt
- std r1,HSTATE_SCRATCH1(r13)
- std r3,HSTATE_SCRATCH2(r13)
- ld r1,HSTATE_HOST_R1(r13)
- ld r3,HSTATE_KVM_VCPU(r13)
- std r9,VCPU_CR(r3)
- 1:
- std r11,VCPU_PC(r3)
- std r12,VCPU_MSR(r3)
- reg = 14
- .rept 18
- std reg,__VCPU_GPR(reg)(r3)
- reg = reg + 1
- .endr
- /* r1, r3, r9-r13 are saved to vcpu by C code */
- std r0,VCPU_GPR(R0)(r3)
- std r2,VCPU_GPR(R2)(r3)
- reg = 4
- .rept 5
- std reg,__VCPU_GPR(reg)(r3)
- reg = reg + 1
- .endr
- LOAD_PACA_TOC()
- mflr r4
- std r4,VCPU_LR(r3)
- mfspr r4,SPRN_XER
- std r4,VCPU_XER(r3)
- reg = 14
- .rept 18
- ld reg,STACK_SLOT_NVGPRS + ((reg - 14) * 8)(r1)
- reg = reg + 1
- .endr
- lwz r4,SFS+8(r1)
- mtcr r4
- /*
- * Flush the link stack here, before executing the first blr on the
- * way out of the guest.
- *
- * The link stack won't match coming out of the guest anyway so the
- * only cost is the flush itself. The call clobbers r0.
- */
- 1: nop
- patch_site 1b patch__call_kvm_flush_link_stack_p9
- addi r1,r1,SFS
- ld r0,PPC_LR_STKOFF(r1)
- mtlr r0
- blr
- /*
- * Took an interrupt somewhere right before HRFID to guest, so registers are
- * in a bad way. Return things hopefully enough to run host virtual code and
- * run the Linux interrupt handler (SRESET or MCE) to print something useful.
- *
- * We could be really clever and save all host registers in known locations
- * before setting HSTATE_IN_GUEST, then restoring them all here, and setting
- * return address to a fixup that sets them up again. But that's a lot of
- * effort for a small bit of code. Lots of other things to do first.
- */
- kvmppc_p9_bad_interrupt:
- BEGIN_MMU_FTR_SECTION
- /*
- * Hash host doesn't try to recover MMU (requires host SLB reload)
- */
- b .
- END_MMU_FTR_SECTION_IFCLR(MMU_FTR_TYPE_RADIX)
- /*
- * Clean up guest registers to give host a chance to run.
- */
- li r10,0
- mtspr SPRN_AMR,r10
- mtspr SPRN_IAMR,r10
- mtspr SPRN_CIABR,r10
- mtspr SPRN_DAWRX0,r10
- BEGIN_FTR_SECTION
- mtspr SPRN_DAWRX1,r10
- END_FTR_SECTION_IFSET(CPU_FTR_DAWR1)
- /*
- * Switch to host MMU mode (don't have the real host PID but we aren't
- * going back to userspace).
- */
- hwsync
- isync
- mtspr SPRN_PID,r10
- ld r10, HSTATE_KVM_VCPU(r13)
- ld r10, VCPU_KVM(r10)
- lwz r10, KVM_HOST_LPID(r10)
- mtspr SPRN_LPID,r10
- ld r10, HSTATE_KVM_VCPU(r13)
- ld r10, VCPU_KVM(r10)
- ld r10, KVM_HOST_LPCR(r10)
- mtspr SPRN_LPCR,r10
- isync
- /*
- * Set GUEST_MODE_NONE so the handler won't branch to KVM, and clear
- * MSR_RI in r12 ([H]SRR1) so the handler won't try to return.
- */
- li r10,KVM_GUEST_MODE_NONE
- stb r10,HSTATE_IN_GUEST(r13)
- li r10,MSR_RI
- andc r12,r12,r10
- /*
- * Go back to interrupt handler. MCE and SRESET have their specific
- * PACA save area so they should be used directly. They set up their
- * own stack. The other handlers all use EXGEN. They will use the
- * guest r1 if it looks like a kernel stack, so just load the
- * emergency stack and go to program check for all other interrupts.
- */
- ld r10,HSTATE_SCRATCH0(r13)
- cmpwi r10,BOOK3S_INTERRUPT_MACHINE_CHECK
- beq .Lcall_machine_check_common
- cmpwi r10,BOOK3S_INTERRUPT_SYSTEM_RESET
- beq .Lcall_system_reset_common
- b .
- .Lcall_machine_check_common:
- b machine_check_common
- .Lcall_system_reset_common:
- b system_reset_common
- #endif
|