[PATCH v6 30/48] KVM: PPC: Book3S HV P9: Implement the rest of the P9 path in C

Nicholas Piggin npiggin at gmail.com
Sat Apr 10 10:37:30 AEST 2021


Excerpts from Alexey Kardashevskiy's message of April 9, 2021 1:57 pm:
> 
> 
> On 05/04/2021 11:19, Nicholas Piggin wrote:
>> Almost all logic is moved to C, by introducing a new in_guest mode for
>> the P9 path that branches very early in the KVM interrupt handler to
>> P9 exit code.
>> 
>> The main P9 entry and exit assembly is now only about 160 lines of low
>> level stack setup and register save/restore, plus a bad-interrupt
>> handler.
>> 
>> There are two motivations for this, the first is just make the code more
>> maintainable being in C. The second is to reduce the amount of code
>> running in a special KVM mode, "realmode". In quotes because with radix
>> it is no longer necessarily real-mode in the MMU, but it still has to be
>> treated specially because it may be in real-mode, and has various
>> important registers like PID, DEC, TB, etc set to guest. This is hostile
>> to the rest of Linux and can't use arbitrary kernel functionality or be
>> instrumented well.
>> 
>> This initial patch is a reasonably faithful conversion of the asm code,
>> but it does lack any loop to return quickly back into the guest without
>> switching out of realmode in the case of unimportant or easily handled
>> interrupts. As explained in previous changes, handling HV interrupts
>> in real mode is not so important for P9.
>> 
>> Use of Linux 64s interrupt entry code register conventions including
>> paca EX_ save areas are brought into the KVM code. There is no point
>> shuffling things into different paca save areas and making up a
>> different calling convention for KVM.
>> 
>> Signed-off-by: Nicholas Piggin <npiggin at gmail.com>
>> ---
>>   arch/powerpc/include/asm/asm-prototypes.h |   3 +-
>>   arch/powerpc/include/asm/kvm_asm.h        |   3 +-
>>   arch/powerpc/include/asm/kvm_book3s_64.h  |   8 +
>>   arch/powerpc/include/asm/kvm_host.h       |   7 +-
>>   arch/powerpc/kernel/security.c            |   5 +-
>>   arch/powerpc/kvm/Makefile                 |   1 +
>>   arch/powerpc/kvm/book3s_64_entry.S        | 247 ++++++++++++++++++++++
>>   arch/powerpc/kvm/book3s_hv.c              |   9 +-
>>   arch/powerpc/kvm/book3s_hv_interrupt.c    | 218 +++++++++++++++++++
>>   arch/powerpc/kvm/book3s_hv_rmhandlers.S   | 125 +----------
>>   10 files changed, 501 insertions(+), 125 deletions(-)
>>   create mode 100644 arch/powerpc/kvm/book3s_hv_interrupt.c
>> 
>> diff --git a/arch/powerpc/include/asm/asm-prototypes.h b/arch/powerpc/include/asm/asm-prototypes.h
>> index 939f3c94c8f3..7c74c80ed994 100644
>> --- a/arch/powerpc/include/asm/asm-prototypes.h
>> +++ b/arch/powerpc/include/asm/asm-prototypes.h
>> @@ -122,6 +122,7 @@ extern s32 patch__call_flush_branch_caches3;
>>   extern s32 patch__flush_count_cache_return;
>>   extern s32 patch__flush_link_stack_return;
>>   extern s32 patch__call_kvm_flush_link_stack;
>> +extern s32 patch__call_kvm_flush_link_stack_p9;
>>   extern s32 patch__memset_nocache, patch__memcpy_nocache;
>>   
>>   extern long flush_branch_caches;
>> @@ -142,7 +143,7 @@ void kvmhv_load_host_pmu(void);
>>   void kvmhv_save_guest_pmu(struct kvm_vcpu *vcpu, bool pmu_in_use);
>>   void kvmhv_load_guest_pmu(struct kvm_vcpu *vcpu);
>>   
>> -int __kvmhv_vcpu_entry_p9(struct kvm_vcpu *vcpu);
>> +void kvmppc_p9_enter_guest(struct kvm_vcpu *vcpu);
>>   
>>   long kvmppc_h_set_dabr(struct kvm_vcpu *vcpu, unsigned long dabr);
>>   long kvmppc_h_set_xdabr(struct kvm_vcpu *vcpu, unsigned long dabr,
>> diff --git a/arch/powerpc/include/asm/kvm_asm.h b/arch/powerpc/include/asm/kvm_asm.h
>> index a3633560493b..b4f9996bd331 100644
>> --- a/arch/powerpc/include/asm/kvm_asm.h
>> +++ b/arch/powerpc/include/asm/kvm_asm.h
>> @@ -146,7 +146,8 @@
>>   #define KVM_GUEST_MODE_GUEST	1
>>   #define KVM_GUEST_MODE_SKIP	2
>>   #define KVM_GUEST_MODE_GUEST_HV	3
>> -#define KVM_GUEST_MODE_HOST_HV	4
>> +#define KVM_GUEST_MODE_GUEST_HV_FAST	4 /* ISA v3.0 with host radix mode */
>> +#define KVM_GUEST_MODE_HOST_HV	5
>>   
>>   #define KVM_INST_FETCH_FAILED	-1
>>   
>> diff --git a/arch/powerpc/include/asm/kvm_book3s_64.h b/arch/powerpc/include/asm/kvm_book3s_64.h
>> index 9bb9bb370b53..c214bcffb441 100644
>> --- a/arch/powerpc/include/asm/kvm_book3s_64.h
>> +++ b/arch/powerpc/include/asm/kvm_book3s_64.h
>> @@ -153,9 +153,17 @@ static inline bool kvmhv_vcpu_is_radix(struct kvm_vcpu *vcpu)
>>   	return radix;
>>   }
>>   
>> +int __kvmhv_vcpu_entry_p9(struct kvm_vcpu *vcpu);
>> +
>>   #define KVM_DEFAULT_HPT_ORDER	24	/* 16MB HPT by default */
>>   #endif
>>   
>> +/*
>> + * Invalid HDSISR value which is used to indicate when HW has not set the reg.
>> + * Used to work around an errata.
>> + */
>> +#define HDSISR_CANARY	0x7fff
>> +
>>   /*
>>    * We use a lock bit in HPTE dword 0 to synchronize updates and
>>    * accesses to each HPTE, and another bit to indicate non-present
>> diff --git a/arch/powerpc/include/asm/kvm_host.h b/arch/powerpc/include/asm/kvm_host.h
>> index 05fb00d37609..fa0083345b11 100644
>> --- a/arch/powerpc/include/asm/kvm_host.h
>> +++ b/arch/powerpc/include/asm/kvm_host.h
>> @@ -690,7 +690,12 @@ struct kvm_vcpu_arch {
>>   	ulong fault_dar;
>>   	u32 fault_dsisr;
>>   	unsigned long intr_msr;
>> -	ulong fault_gpa;	/* guest real address of page fault (POWER9) */
>> +	/*
>> +	 * POWER9 and later, fault_gpa contains the guest real address of page
>> +	 * fault for a radix guest, or segment descriptor (equivalent to result
>> +	 * from slbmfev of SLB entry that translated the EA) for hash guests.
>> +	 */
>> +	ulong fault_gpa;
>>   #endif
>>   
>>   #ifdef CONFIG_BOOKE
>> diff --git a/arch/powerpc/kernel/security.c b/arch/powerpc/kernel/security.c
>> index e4e1a94ccf6a..3a607c11f20f 100644
>> --- a/arch/powerpc/kernel/security.c
>> +++ b/arch/powerpc/kernel/security.c
>> @@ -430,16 +430,19 @@ device_initcall(stf_barrier_debugfs_init);
>>   
>>   static void update_branch_cache_flush(void)
>>   {
>> -	u32 *site;
>> +	u32 *site, __maybe_unused *site2;
>>   
>>   #ifdef CONFIG_KVM_BOOK3S_HV_POSSIBLE
>>   	site = &patch__call_kvm_flush_link_stack;
>> +	site2 = &patch__call_kvm_flush_link_stack_p9;
>>   	// This controls the branch from guest_exit_cont to kvm_flush_link_stack
>>   	if (link_stack_flush_type == BRANCH_CACHE_FLUSH_NONE) {
>>   		patch_instruction_site(site, ppc_inst(PPC_INST_NOP));
>> +		patch_instruction_site(site2, ppc_inst(PPC_INST_NOP));
>>   	} else {
>>   		// Could use HW flush, but that could also flush count cache
>>   		patch_branch_site(site, (u64)&kvm_flush_link_stack, BRANCH_SET_LINK);
>> +		patch_branch_site(site2, (u64)&kvm_flush_link_stack, BRANCH_SET_LINK);
>>   	}
>>   #endif
>>   
>> diff --git a/arch/powerpc/kvm/Makefile b/arch/powerpc/kvm/Makefile
>> index cdd119028f64..ca7c86aa9360 100644
>> --- a/arch/powerpc/kvm/Makefile
>> +++ b/arch/powerpc/kvm/Makefile
>> @@ -88,6 +88,7 @@ kvm-book3s_64-builtin-tm-objs-$(CONFIG_PPC_TRANSACTIONAL_MEM) += \
>>   
>>   ifdef CONFIG_KVM_BOOK3S_HV_POSSIBLE
>>   kvm-book3s_64-builtin-objs-$(CONFIG_KVM_BOOK3S_64_HANDLER) += \
>> +	book3s_hv_interrupt.o \
>>   	book3s_hv_hmi.o \
>>   	book3s_hv_rmhandlers.o \
>>   	book3s_hv_rm_mmu.o \
>> diff --git a/arch/powerpc/kvm/book3s_64_entry.S b/arch/powerpc/kvm/book3s_64_entry.S
>> index 0c79c89c6a4b..d98ad580fd98 100644
>> --- a/arch/powerpc/kvm/book3s_64_entry.S
>> +++ b/arch/powerpc/kvm/book3s_64_entry.S
>> @@ -1,11 +1,16 @@
>>   /* 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
>> @@ -29,10 +34,15 @@
>>   .global	kvmppc_hcall
>>   .balign IFETCH_ALIGN_BYTES
>>   kvmppc_hcall:
>> +	lbz	r10,HSTATE_IN_GUEST(r13)
>> +	cmpwi	r10,KVM_GUEST_MODE_GUEST_HV_FAST
>> +	beq	kvmppc_p9_exit_hcall
>>   	ld	r10,PACA_EXGEN+EX_R13(r13)
>>   	SET_SCRATCH0(r10)
>>   	li	r10,0xc00
>>   	/* Now we look like kvmppc_interrupt */
>> +	li	r11,PACA_EXGEN
>> +	b	1f
>>   
>>   /*
>>    * KVM interrupt entry occurs after GEN_INT_ENTRY runs, and follows that
>> @@ -53,6 +63,12 @@ kvmppc_hcall:
>>   .global	kvmppc_interrupt
>>   .balign IFETCH_ALIGN_BYTES
>>   kvmppc_interrupt:
>> +	std	r10,HSTATE_SCRATCH0(r13)
>> +	lbz	r10,HSTATE_IN_GUEST(r13)
>> +	cmpwi	r10,KVM_GUEST_MODE_GUEST_HV_FAST
>> +	beq	kvmppc_p9_exit_interrupt
>> +	ld	r10,HSTATE_SCRATCH0(r13)
>> +	lbz	r11,HSTATE_IN_GUEST(r13)
>>   	li	r11,PACA_EXGEN
>>   	cmpdi	r10,0x200
>>   	bgt+	1f
>> @@ -154,3 +170,234 @@ END_FTR_SECTION_IFSET(CPU_FTR_HAS_PPR)
>>   	GET_SCRATCH0(r13)
>>   	HRFI_TO_KERNEL
>>   #endif
>> +
>> +/* Stack frame offsets for kvmppc_hv_entry */
>> +#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 where we have exactly
>> + * one vcpu per vcore, and both the host and guest are radix, and threads
>> + * are set to "indepdent mode".
>> + */
>> +.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)
> 
> 
> This does not compile when CONFIG_KVM_BOOK3S_HV_POSSIBLE is not defined.

Thanks, I fixed that. I admittedly haven't tested all such combinations 
well, I'll have to script some more comprehensive compile jobs.

Thanks,
Nick


More information about the Linuxppc-dev mailing list