Commit 89d35b23 authored by Nicholas Piggin's avatar Nicholas Piggin Committed by Michael Ellerman
Browse files

KVM: PPC: Book3S HV P9: Implement the rest of the P9 path in C



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
very quickly in this low level realmode is not so important for P9
performance, and are important to avoid for security, observability,
debugability reasons.

Signed-off-by: default avatarNicholas Piggin <npiggin@gmail.com>
Reviewed-by: default avatarAlexey Kardashevskiy <aik@ozlabs.ru>
Signed-off-by: default avatarMichael Ellerman <mpe@ellerman.id.au>
Link: https://lore.kernel.org/r/20210528090752.3542186-15-npiggin@gmail.com
parent 9dc2babc
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -120,6 +120,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;
@@ -140,7 +141,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,
+1 −0
Original line number Diff line number Diff line
@@ -147,6 +147,7 @@
#define KVM_GUEST_MODE_SKIP	2
#define KVM_GUEST_MODE_GUEST_HV	3
#define KVM_GUEST_MODE_HOST_HV	4
#define KVM_GUEST_MODE_HV_FAST	5 /* ISA >= v3.0 host+guest radix, indep thr */

#define KVM_INST_FETCH_FAILED	-1

+8 −0
Original line number Diff line number Diff line
@@ -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
+6 −1
Original line number Diff line number Diff line
@@ -683,7 +683,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
+4 −1
Original line number Diff line number Diff line
@@ -432,16 +432,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

Loading