x86:
* Fixes for the new scalable MMU * Fixes for migration of nested hypervisors on AMD * Fix for clang integrated assembler * Fix for left shift by 64 (UBSAN) * Small cleanups * Straggler SEV-ES patch ARM: * VM init cleanups * PSCI relay cleanups * Kill CONFIG_KVM_ARM_PMU * Fixup __init annotations * Fixup reg_to_encoding() * Fix spurious PMCR_EL0 access * selftests cleanups -----BEGIN PGP SIGNATURE----- iQFIBAABCAAyFiEE8TM4V0tmI4mGbHaCv/vSX3jHroMFAl/4YOMUHHBib256aW5p QHJlZGhhdC5jb20ACgkQv/vSX3jHroMg0Qf/eGDOoLL18KhA9MpoPXusR5kU+82G AcbFj9siNW46EF7mL+sw/xAx+gZaqSpIEmn/f6BzgiaUBdFTv9CKX3B54e43e59G HAKD0NpqwvDIi1b0T6bcgC92mY3Qx/IDCc7/9JYjBs/iORfqyWW6xVtkF/Gfymxt eK+MnfMqqNZODgR/cZnCH1E48fuwOvRMxLqilLi3OOMSUfs2cQOSLTNfYQYqjeaJ dsQ4YeyPJO5JHtfHFr6VPIo/jDhowniac9CNvOomWWVIx2zPYVSl9d8ub6ESEPNF GM7UeBCOMmZG/a3qFEZPAUJ7znW4yYE85Z6pjxlrGhd1I54MJi4dd+RApw== =5Nfj -----END PGP SIGNATURE----- Merge tag 'for-linus' of git://git.kernel.org/pub/scm/virt/kvm/kvm Pull kvm fixes from Paolo Bonzini: "x86: - Fixes for the new scalable MMU - Fixes for migration of nested hypervisors on AMD - Fix for clang integrated assembler - Fix for left shift by 64 (UBSAN) - Small cleanups - Straggler SEV-ES patch ARM: - VM init cleanups - PSCI relay cleanups - Kill CONFIG_KVM_ARM_PMU - Fixup __init annotations - Fixup reg_to_encoding() - Fix spurious PMCR_EL0 access Misc: - selftests cleanups" * tag 'for-linus' of git://git.kernel.org/pub/scm/virt/kvm/kvm: (38 commits) KVM: x86: __kvm_vcpu_halt can be static KVM: SVM: Add support for booting APs in an SEV-ES guest KVM: nSVM: cancel KVM_REQ_GET_NESTED_STATE_PAGES on nested vmexit KVM: nSVM: mark vmcb as dirty when forcingly leaving the guest mode KVM: nSVM: correctly restore nested_run_pending on migration KVM: x86/mmu: Clarify TDP MMU page list invariants KVM: x86/mmu: Ensure TDP MMU roots are freed after yield kvm: check tlbs_dirty directly KVM: x86: change in pv_eoi_get_pending() to make code more readable MAINTAINERS: Really update email address for Sean Christopherson KVM: x86: fix shift out of bounds reported by UBSAN KVM: selftests: Implement perf_test_util more conventionally KVM: selftests: Use vm_create_with_vcpus in create_vm KVM: selftests: Factor out guest mode code KVM/SVM: Remove leftover __svm_vcpu_run prototype from svm.c KVM: SVM: Add register operand to vmsave call in sev_es_vcpu_load KVM: x86/mmu: Optimize not-present/MMIO SPTE check in get_mmio_spte() KVM: x86/mmu: Use raw level to index into MMIO walks' sptes array KVM: x86/mmu: Get root level from walkers when retrieving MMIO SPTE KVM: x86/mmu: Use -1 to flag an undefined spte in get_mmio_spte() ...
This commit is contained in:
		
						commit
						2a190b22aa
					
				| @ -392,9 +392,14 @@ This ioctl is obsolete and has been removed. | ||||
| 
 | ||||
| Errors: | ||||
| 
 | ||||
|   =====      ============================= | ||||
|   =======    ============================================================== | ||||
|   EINTR      an unmasked signal is pending | ||||
|   =====      ============================= | ||||
|   ENOEXEC    the vcpu hasn't been initialized or the guest tried to execute | ||||
|              instructions from device memory (arm64) | ||||
|   ENOSYS     data abort outside memslots with no syndrome info and | ||||
|              KVM_CAP_ARM_NISV_TO_USER not enabled (arm64) | ||||
|   EPERM      SVE feature set but not finalized (arm64) | ||||
|   =======    ============================================================== | ||||
| 
 | ||||
| This ioctl is used to run a guest virtual cpu.  While there are no | ||||
| explicit parameters, there is an implicit parameter block that can be | ||||
|  | ||||
| @ -9776,7 +9776,7 @@ F:	tools/testing/selftests/kvm/s390x/ | ||||
| 
 | ||||
| KERNEL VIRTUAL MACHINE FOR X86 (KVM/x86) | ||||
| M:	Paolo Bonzini <pbonzini@redhat.com> | ||||
| R:	Sean Christopherson <sean.j.christopherson@intel.com> | ||||
| R:	Sean Christopherson <seanjc@google.com> | ||||
| R:	Vitaly Kuznetsov <vkuznets@redhat.com> | ||||
| R:	Wanpeng Li <wanpengli@tencent.com> | ||||
| R:	Jim Mattson <jmattson@google.com> | ||||
|  | ||||
| @ -17,6 +17,7 @@ | ||||
| #include <linux/jump_label.h> | ||||
| #include <linux/kvm_types.h> | ||||
| #include <linux/percpu.h> | ||||
| #include <linux/psci.h> | ||||
| #include <asm/arch_gicv3.h> | ||||
| #include <asm/barrier.h> | ||||
| #include <asm/cpufeature.h> | ||||
| @ -240,6 +241,28 @@ struct kvm_host_data { | ||||
| 	struct kvm_pmu_events pmu_events; | ||||
| }; | ||||
| 
 | ||||
| struct kvm_host_psci_config { | ||||
| 	/* PSCI version used by host. */ | ||||
| 	u32 version; | ||||
| 
 | ||||
| 	/* Function IDs used by host if version is v0.1. */ | ||||
| 	struct psci_0_1_function_ids function_ids_0_1; | ||||
| 
 | ||||
| 	bool psci_0_1_cpu_suspend_implemented; | ||||
| 	bool psci_0_1_cpu_on_implemented; | ||||
| 	bool psci_0_1_cpu_off_implemented; | ||||
| 	bool psci_0_1_migrate_implemented; | ||||
| }; | ||||
| 
 | ||||
| extern struct kvm_host_psci_config kvm_nvhe_sym(kvm_host_psci_config); | ||||
| #define kvm_host_psci_config CHOOSE_NVHE_SYM(kvm_host_psci_config) | ||||
| 
 | ||||
| extern s64 kvm_nvhe_sym(hyp_physvirt_offset); | ||||
| #define hyp_physvirt_offset CHOOSE_NVHE_SYM(hyp_physvirt_offset) | ||||
| 
 | ||||
| extern u64 kvm_nvhe_sym(hyp_cpu_logical_map)[NR_CPUS]; | ||||
| #define hyp_cpu_logical_map CHOOSE_NVHE_SYM(hyp_cpu_logical_map) | ||||
| 
 | ||||
| struct vcpu_reset_state { | ||||
| 	unsigned long	pc; | ||||
| 	unsigned long	r0; | ||||
|  | ||||
| @ -2568,7 +2568,7 @@ static void verify_hyp_capabilities(void) | ||||
| 	int parange, ipa_max; | ||||
| 	unsigned int safe_vmid_bits, vmid_bits; | ||||
| 
 | ||||
| 	if (!IS_ENABLED(CONFIG_KVM) || !IS_ENABLED(CONFIG_KVM_ARM_HOST)) | ||||
| 	if (!IS_ENABLED(CONFIG_KVM)) | ||||
| 		return; | ||||
| 
 | ||||
| 	safe_mmfr1 = read_sanitised_ftr_reg(SYS_ID_AA64MMFR1_EL1); | ||||
|  | ||||
| @ -434,7 +434,7 @@ static void __init hyp_mode_check(void) | ||||
| 			   "CPU: CPUs started in inconsistent modes"); | ||||
| 	else | ||||
| 		pr_info("CPU: All CPU(s) started at EL1\n"); | ||||
| 	if (IS_ENABLED(CONFIG_KVM)) | ||||
| 	if (IS_ENABLED(CONFIG_KVM) && !is_kernel_in_hyp_mode()) | ||||
| 		kvm_compute_layout(); | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -49,14 +49,6 @@ if KVM | ||||
| 
 | ||||
| source "virt/kvm/Kconfig" | ||||
| 
 | ||||
| config KVM_ARM_PMU | ||||
| 	bool "Virtual Performance Monitoring Unit (PMU) support" | ||||
| 	depends on HW_PERF_EVENTS | ||||
| 	default y | ||||
| 	help | ||||
| 	  Adds support for a virtual Performance Monitoring Unit (PMU) in | ||||
| 	  virtual machines. | ||||
| 
 | ||||
| endif # KVM | ||||
| 
 | ||||
| endif # VIRTUALIZATION | ||||
|  | ||||
| @ -24,4 +24,4 @@ kvm-y := $(KVM)/kvm_main.o $(KVM)/coalesced_mmio.o $(KVM)/eventfd.o \ | ||||
| 	 vgic/vgic-mmio-v3.o vgic/vgic-kvm-device.o \
 | ||||
| 	 vgic/vgic-its.o vgic/vgic-debug.o | ||||
| 
 | ||||
| kvm-$(CONFIG_KVM_ARM_PMU)  += pmu-emul.o | ||||
| kvm-$(CONFIG_HW_PERF_EVENTS)  += pmu-emul.o | ||||
|  | ||||
| @ -1129,9 +1129,10 @@ int kvm_timer_enable(struct kvm_vcpu *vcpu) | ||||
| 	if (!irqchip_in_kernel(vcpu->kvm)) | ||||
| 		goto no_vgic; | ||||
| 
 | ||||
| 	if (!vgic_initialized(vcpu->kvm)) | ||||
| 		return -ENODEV; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * At this stage, we have the guarantee that the vgic is both | ||||
| 	 * available and initialized. | ||||
| 	 */ | ||||
| 	if (!timer_irqs_are_valid(vcpu)) { | ||||
| 		kvm_debug("incorrectly configured timer irqs\n"); | ||||
| 		return -EINVAL; | ||||
|  | ||||
| @ -65,10 +65,6 @@ static bool vgic_present; | ||||
| static DEFINE_PER_CPU(unsigned char, kvm_arm_hardware_enabled); | ||||
| DEFINE_STATIC_KEY_FALSE(userspace_irqchip_in_use); | ||||
| 
 | ||||
| extern u64 kvm_nvhe_sym(__cpu_logical_map)[NR_CPUS]; | ||||
| extern u32 kvm_nvhe_sym(kvm_host_psci_version); | ||||
| extern struct psci_0_1_function_ids kvm_nvhe_sym(kvm_host_psci_0_1_function_ids); | ||||
| 
 | ||||
| int kvm_arch_vcpu_should_kick(struct kvm_vcpu *vcpu) | ||||
| { | ||||
| 	return kvm_vcpu_exiting_guest_mode(vcpu) == IN_GUEST_MODE; | ||||
| @ -584,11 +580,9 @@ static int kvm_vcpu_first_run_init(struct kvm_vcpu *vcpu) | ||||
| 		 * Map the VGIC hardware resources before running a vcpu the | ||||
| 		 * first time on this VM. | ||||
| 		 */ | ||||
| 		if (unlikely(!vgic_ready(kvm))) { | ||||
| 		ret = kvm_vgic_map_resources(kvm); | ||||
| 		if (ret) | ||||
| 			return ret; | ||||
| 		} | ||||
| 	} else { | ||||
| 		/*
 | ||||
| 		 * Tell the rest of the code that there are userspace irqchip | ||||
| @ -1574,12 +1568,12 @@ static struct notifier_block hyp_init_cpu_pm_nb = { | ||||
| 	.notifier_call = hyp_init_cpu_pm_notifier, | ||||
| }; | ||||
| 
 | ||||
| static void __init hyp_cpu_pm_init(void) | ||||
| static void hyp_cpu_pm_init(void) | ||||
| { | ||||
| 	if (!is_protected_kvm_enabled()) | ||||
| 		cpu_pm_register_notifier(&hyp_init_cpu_pm_nb); | ||||
| } | ||||
| static void __init hyp_cpu_pm_exit(void) | ||||
| static void hyp_cpu_pm_exit(void) | ||||
| { | ||||
| 	if (!is_protected_kvm_enabled()) | ||||
| 		cpu_pm_unregister_notifier(&hyp_init_cpu_pm_nb); | ||||
| @ -1604,9 +1598,12 @@ static void init_cpu_logical_map(void) | ||||
| 	 * allow any other CPUs from the `possible` set to boot. | ||||
| 	 */ | ||||
| 	for_each_online_cpu(cpu) | ||||
| 		kvm_nvhe_sym(__cpu_logical_map)[cpu] = cpu_logical_map(cpu); | ||||
| 		hyp_cpu_logical_map[cpu] = cpu_logical_map(cpu); | ||||
| } | ||||
| 
 | ||||
| #define init_psci_0_1_impl_state(config, what)	\ | ||||
| 	config.psci_0_1_ ## what ## _implemented = psci_ops.what | ||||
| 
 | ||||
| static bool init_psci_relay(void) | ||||
| { | ||||
| 	/*
 | ||||
| @ -1618,8 +1615,15 @@ static bool init_psci_relay(void) | ||||
| 		return false; | ||||
| 	} | ||||
| 
 | ||||
| 	kvm_nvhe_sym(kvm_host_psci_version) = psci_ops.get_version(); | ||||
| 	kvm_nvhe_sym(kvm_host_psci_0_1_function_ids) = get_psci_0_1_function_ids(); | ||||
| 	kvm_host_psci_config.version = psci_ops.get_version(); | ||||
| 
 | ||||
| 	if (kvm_host_psci_config.version == PSCI_VERSION(0, 1)) { | ||||
| 		kvm_host_psci_config.function_ids_0_1 = get_psci_0_1_function_ids(); | ||||
| 		init_psci_0_1_impl_state(kvm_host_psci_config, cpu_suspend); | ||||
| 		init_psci_0_1_impl_state(kvm_host_psci_config, cpu_on); | ||||
| 		init_psci_0_1_impl_state(kvm_host_psci_config, cpu_off); | ||||
| 		init_psci_0_1_impl_state(kvm_host_psci_config, migrate); | ||||
| 	} | ||||
| 	return true; | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -59,4 +59,13 @@ static inline void __adjust_pc(struct kvm_vcpu *vcpu) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Skip an instruction while host sysregs are live. | ||||
|  * Assumes host is always 64-bit. | ||||
|  */ | ||||
| static inline void kvm_skip_host_instr(void) | ||||
| { | ||||
| 	write_sysreg_el2(read_sysreg_el2(SYS_ELR) + 4, SYS_ELR); | ||||
| } | ||||
| 
 | ||||
| #endif | ||||
|  | ||||
| @ -157,11 +157,6 @@ static void default_host_smc_handler(struct kvm_cpu_context *host_ctxt) | ||||
| 	__kvm_hyp_host_forward_smc(host_ctxt); | ||||
| } | ||||
| 
 | ||||
| static void skip_host_instruction(void) | ||||
| { | ||||
| 	write_sysreg_el2(read_sysreg_el2(SYS_ELR) + 4, SYS_ELR); | ||||
| } | ||||
| 
 | ||||
| static void handle_host_smc(struct kvm_cpu_context *host_ctxt) | ||||
| { | ||||
| 	bool handled; | ||||
| @ -170,11 +165,8 @@ static void handle_host_smc(struct kvm_cpu_context *host_ctxt) | ||||
| 	if (!handled) | ||||
| 		default_host_smc_handler(host_ctxt); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Unlike HVC, the return address of an SMC is the instruction's PC. | ||||
| 	 * Move the return address past the instruction. | ||||
| 	 */ | ||||
| 	skip_host_instruction(); | ||||
| 	/* SMC was trapped, move ELR past the current PC. */ | ||||
| 	kvm_skip_host_instr(); | ||||
| } | ||||
| 
 | ||||
| void handle_trap(struct kvm_cpu_context *host_ctxt) | ||||
|  | ||||
| @ -14,14 +14,14 @@ | ||||
|  * Other CPUs should not be allowed to boot because their features were | ||||
|  * not checked against the finalized system capabilities. | ||||
|  */ | ||||
| u64 __ro_after_init __cpu_logical_map[NR_CPUS] = { [0 ... NR_CPUS-1] = INVALID_HWID }; | ||||
| u64 __ro_after_init hyp_cpu_logical_map[NR_CPUS] = { [0 ... NR_CPUS-1] = INVALID_HWID }; | ||||
| 
 | ||||
| u64 cpu_logical_map(unsigned int cpu) | ||||
| { | ||||
| 	if (cpu >= ARRAY_SIZE(__cpu_logical_map)) | ||||
| 	if (cpu >= ARRAY_SIZE(hyp_cpu_logical_map)) | ||||
| 		hyp_panic(); | ||||
| 
 | ||||
| 	return __cpu_logical_map[cpu]; | ||||
| 	return hyp_cpu_logical_map[cpu]; | ||||
| } | ||||
| 
 | ||||
| unsigned long __hyp_per_cpu_offset(unsigned int cpu) | ||||
|  | ||||
| @ -7,11 +7,8 @@ | ||||
| #include <asm/kvm_asm.h> | ||||
| #include <asm/kvm_hyp.h> | ||||
| #include <asm/kvm_mmu.h> | ||||
| #include <kvm/arm_hypercalls.h> | ||||
| #include <linux/arm-smccc.h> | ||||
| #include <linux/kvm_host.h> | ||||
| #include <linux/psci.h> | ||||
| #include <kvm/arm_psci.h> | ||||
| #include <uapi/linux/psci.h> | ||||
| 
 | ||||
| #include <nvhe/trap_handler.h> | ||||
| @ -22,9 +19,8 @@ void kvm_hyp_cpu_resume(unsigned long r0); | ||||
| void __noreturn __host_enter(struct kvm_cpu_context *host_ctxt); | ||||
| 
 | ||||
| /* Config options set by the host. */ | ||||
| __ro_after_init u32 kvm_host_psci_version; | ||||
| __ro_after_init struct psci_0_1_function_ids kvm_host_psci_0_1_function_ids; | ||||
| __ro_after_init s64 hyp_physvirt_offset; | ||||
| struct kvm_host_psci_config __ro_after_init kvm_host_psci_config; | ||||
| s64 __ro_after_init hyp_physvirt_offset; | ||||
| 
 | ||||
| #define __hyp_pa(x) ((phys_addr_t)((x)) + hyp_physvirt_offset) | ||||
| 
 | ||||
| @ -47,19 +43,16 @@ struct psci_boot_args { | ||||
| static DEFINE_PER_CPU(struct psci_boot_args, cpu_on_args) = PSCI_BOOT_ARGS_INIT; | ||||
| static DEFINE_PER_CPU(struct psci_boot_args, suspend_args) = PSCI_BOOT_ARGS_INIT; | ||||
| 
 | ||||
| static u64 get_psci_func_id(struct kvm_cpu_context *host_ctxt) | ||||
| { | ||||
| 	DECLARE_REG(u64, func_id, host_ctxt, 0); | ||||
| 
 | ||||
| 	return func_id; | ||||
| } | ||||
| #define	is_psci_0_1(what, func_id)					\ | ||||
| 	(kvm_host_psci_config.psci_0_1_ ## what ## _implemented &&	\ | ||||
| 	 (func_id) == kvm_host_psci_config.function_ids_0_1.what) | ||||
| 
 | ||||
| static bool is_psci_0_1_call(u64 func_id) | ||||
| { | ||||
| 	return (func_id == kvm_host_psci_0_1_function_ids.cpu_suspend) || | ||||
| 	       (func_id == kvm_host_psci_0_1_function_ids.cpu_on) || | ||||
| 	       (func_id == kvm_host_psci_0_1_function_ids.cpu_off) || | ||||
| 	       (func_id == kvm_host_psci_0_1_function_ids.migrate); | ||||
| 	return (is_psci_0_1(cpu_suspend, func_id) || | ||||
| 		is_psci_0_1(cpu_on, func_id) || | ||||
| 		is_psci_0_1(cpu_off, func_id) || | ||||
| 		is_psci_0_1(migrate, func_id)); | ||||
| } | ||||
| 
 | ||||
| static bool is_psci_0_2_call(u64 func_id) | ||||
| @ -69,16 +62,6 @@ static bool is_psci_0_2_call(u64 func_id) | ||||
| 	       (PSCI_0_2_FN64(0) <= func_id && func_id <= PSCI_0_2_FN64(31)); | ||||
| } | ||||
| 
 | ||||
| static bool is_psci_call(u64 func_id) | ||||
| { | ||||
| 	switch (kvm_host_psci_version) { | ||||
| 	case PSCI_VERSION(0, 1): | ||||
| 		return is_psci_0_1_call(func_id); | ||||
| 	default: | ||||
| 		return is_psci_0_2_call(func_id); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static unsigned long psci_call(unsigned long fn, unsigned long arg0, | ||||
| 			       unsigned long arg1, unsigned long arg2) | ||||
| { | ||||
| @ -248,14 +231,13 @@ asmlinkage void __noreturn kvm_host_psci_cpu_entry(bool is_cpu_on) | ||||
| 
 | ||||
| static unsigned long psci_0_1_handler(u64 func_id, struct kvm_cpu_context *host_ctxt) | ||||
| { | ||||
| 	if ((func_id == kvm_host_psci_0_1_function_ids.cpu_off) || | ||||
| 	    (func_id == kvm_host_psci_0_1_function_ids.migrate)) | ||||
| 	if (is_psci_0_1(cpu_off, func_id) || is_psci_0_1(migrate, func_id)) | ||||
| 		return psci_forward(host_ctxt); | ||||
| 	else if (func_id == kvm_host_psci_0_1_function_ids.cpu_on) | ||||
| 	if (is_psci_0_1(cpu_on, func_id)) | ||||
| 		return psci_cpu_on(func_id, host_ctxt); | ||||
| 	else if (func_id == kvm_host_psci_0_1_function_ids.cpu_suspend) | ||||
| 	if (is_psci_0_1(cpu_suspend, func_id)) | ||||
| 		return psci_cpu_suspend(func_id, host_ctxt); | ||||
| 	else | ||||
| 
 | ||||
| 	return PSCI_RET_NOT_SUPPORTED; | ||||
| } | ||||
| 
 | ||||
| @ -298,20 +280,23 @@ static unsigned long psci_1_0_handler(u64 func_id, struct kvm_cpu_context *host_ | ||||
| 
 | ||||
| bool kvm_host_psci_handler(struct kvm_cpu_context *host_ctxt) | ||||
| { | ||||
| 	u64 func_id = get_psci_func_id(host_ctxt); | ||||
| 	DECLARE_REG(u64, func_id, host_ctxt, 0); | ||||
| 	unsigned long ret; | ||||
| 
 | ||||
| 	if (!is_psci_call(func_id)) | ||||
| 		return false; | ||||
| 
 | ||||
| 	switch (kvm_host_psci_version) { | ||||
| 	switch (kvm_host_psci_config.version) { | ||||
| 	case PSCI_VERSION(0, 1): | ||||
| 		if (!is_psci_0_1_call(func_id)) | ||||
| 			return false; | ||||
| 		ret = psci_0_1_handler(func_id, host_ctxt); | ||||
| 		break; | ||||
| 	case PSCI_VERSION(0, 2): | ||||
| 		if (!is_psci_0_2_call(func_id)) | ||||
| 			return false; | ||||
| 		ret = psci_0_2_handler(func_id, host_ctxt); | ||||
| 		break; | ||||
| 	default: | ||||
| 		if (!is_psci_0_2_call(func_id)) | ||||
| 			return false; | ||||
| 		ret = psci_1_0_handler(func_id, host_ctxt); | ||||
| 		break; | ||||
| 	} | ||||
|  | ||||
| @ -850,8 +850,6 @@ int kvm_arm_pmu_v3_enable(struct kvm_vcpu *vcpu) | ||||
| 		   return -EINVAL; | ||||
| 	} | ||||
| 
 | ||||
| 	kvm_pmu_vcpu_reset(vcpu); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -594,6 +594,10 @@ static void reset_pmcr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r) | ||||
| { | ||||
| 	u64 pmcr, val; | ||||
| 
 | ||||
| 	/* No PMU available, PMCR_EL0 may UNDEF... */ | ||||
| 	if (!kvm_arm_support_pmu_v3()) | ||||
| 		return; | ||||
| 
 | ||||
| 	pmcr = read_sysreg(pmcr_el0); | ||||
| 	/*
 | ||||
| 	 * Writable bits of PMCR_EL0 (ARMV8_PMU_PMCR_MASK) are reset to UNKNOWN | ||||
| @ -919,7 +923,7 @@ static bool access_pmuserenr(struct kvm_vcpu *vcpu, struct sys_reg_params *p, | ||||
| 
 | ||||
| #define reg_to_encoding(x)						\ | ||||
| 	sys_reg((u32)(x)->Op0, (u32)(x)->Op1,				\ | ||||
| 		(u32)(x)->CRn, (u32)(x)->CRm, (u32)(x)->Op2); | ||||
| 		(u32)(x)->CRn, (u32)(x)->CRm, (u32)(x)->Op2) | ||||
| 
 | ||||
| /* Silly macro to expand the DBG{BCR,BVR,WVR,WCR}n_EL1 registers in one go */ | ||||
| #define DBG_BCR_BVR_WCR_WVR_EL1(n)					\ | ||||
|  | ||||
| @ -34,17 +34,16 @@ static u64 __early_kern_hyp_va(u64 addr) | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Store a hyp VA <-> PA offset into a hyp-owned variable. | ||||
|  * Store a hyp VA <-> PA offset into a EL2-owned variable. | ||||
|  */ | ||||
| static void init_hyp_physvirt_offset(void) | ||||
| { | ||||
| 	extern s64 kvm_nvhe_sym(hyp_physvirt_offset); | ||||
| 	u64 kern_va, hyp_va; | ||||
| 
 | ||||
| 	/* Compute the offset from the hyp VA and PA of a random symbol. */ | ||||
| 	kern_va = (u64)kvm_ksym_ref(__hyp_text_start); | ||||
| 	kern_va = (u64)lm_alias(__hyp_text_start); | ||||
| 	hyp_va = __early_kern_hyp_va(kern_va); | ||||
| 	CHOOSE_NVHE_SYM(hyp_physvirt_offset) = (s64)__pa(kern_va) - (s64)hyp_va; | ||||
| 	hyp_physvirt_offset = (s64)__pa(kern_va) - (s64)hyp_va; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  | ||||
| @ -419,7 +419,8 @@ int vgic_lazy_init(struct kvm *kvm) | ||||
|  * Map the MMIO regions depending on the VGIC model exposed to the guest | ||||
|  * called on the first VCPU run. | ||||
|  * Also map the virtual CPU interface into the VM. | ||||
|  * v2/v3 derivatives call vgic_init if not already done. | ||||
|  * v2 calls vgic_init() if not already done. | ||||
|  * v3 and derivatives return an error if the VGIC is not initialized. | ||||
|  * vgic_ready() returns true if this function has succeeded. | ||||
|  * @kvm: kvm struct pointer | ||||
|  */ | ||||
| @ -428,7 +429,13 @@ int kvm_vgic_map_resources(struct kvm *kvm) | ||||
| 	struct vgic_dist *dist = &kvm->arch.vgic; | ||||
| 	int ret = 0; | ||||
| 
 | ||||
| 	if (likely(vgic_ready(kvm))) | ||||
| 		return 0; | ||||
| 
 | ||||
| 	mutex_lock(&kvm->lock); | ||||
| 	if (vgic_ready(kvm)) | ||||
| 		goto out; | ||||
| 
 | ||||
| 	if (!irqchip_in_kernel(kvm)) | ||||
| 		goto out; | ||||
| 
 | ||||
| @ -439,6 +446,8 @@ int kvm_vgic_map_resources(struct kvm *kvm) | ||||
| 
 | ||||
| 	if (ret) | ||||
| 		__kvm_vgic_destroy(kvm); | ||||
| 	else | ||||
| 		dist->ready = true; | ||||
| 
 | ||||
| out: | ||||
| 	mutex_unlock(&kvm->lock); | ||||
|  | ||||
| @ -306,20 +306,15 @@ int vgic_v2_map_resources(struct kvm *kvm) | ||||
| 	struct vgic_dist *dist = &kvm->arch.vgic; | ||||
| 	int ret = 0; | ||||
| 
 | ||||
| 	if (vgic_ready(kvm)) | ||||
| 		goto out; | ||||
| 
 | ||||
| 	if (IS_VGIC_ADDR_UNDEF(dist->vgic_dist_base) || | ||||
| 	    IS_VGIC_ADDR_UNDEF(dist->vgic_cpu_base)) { | ||||
| 		kvm_err("Need to set vgic cpu and dist addresses first\n"); | ||||
| 		ret = -ENXIO; | ||||
| 		goto out; | ||||
| 		return -ENXIO; | ||||
| 	} | ||||
| 
 | ||||
| 	if (!vgic_v2_check_base(dist->vgic_dist_base, dist->vgic_cpu_base)) { | ||||
| 		kvm_err("VGIC CPU and dist frames overlap\n"); | ||||
| 		ret = -EINVAL; | ||||
| 		goto out; | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 
 | ||||
| 	/*
 | ||||
| @ -329,13 +324,13 @@ int vgic_v2_map_resources(struct kvm *kvm) | ||||
| 	ret = vgic_init(kvm); | ||||
| 	if (ret) { | ||||
| 		kvm_err("Unable to initialize VGIC dynamic data structures\n"); | ||||
| 		goto out; | ||||
| 		return ret; | ||||
| 	} | ||||
| 
 | ||||
| 	ret = vgic_register_dist_iodev(kvm, dist->vgic_dist_base, VGIC_V2); | ||||
| 	if (ret) { | ||||
| 		kvm_err("Unable to register VGIC MMIO regions\n"); | ||||
| 		goto out; | ||||
| 		return ret; | ||||
| 	} | ||||
| 
 | ||||
| 	if (!static_branch_unlikely(&vgic_v2_cpuif_trap)) { | ||||
| @ -344,14 +339,11 @@ int vgic_v2_map_resources(struct kvm *kvm) | ||||
| 					    KVM_VGIC_V2_CPU_SIZE, true); | ||||
| 		if (ret) { | ||||
| 			kvm_err("Unable to remap VGIC CPU to VCPU\n"); | ||||
| 			goto out; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	dist->ready = true; | ||||
| 
 | ||||
| out: | ||||
| 			return ret; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| DEFINE_STATIC_KEY_FALSE(vgic_v2_cpuif_trap); | ||||
|  | ||||
| @ -500,29 +500,23 @@ int vgic_v3_map_resources(struct kvm *kvm) | ||||
| 	int ret = 0; | ||||
| 	int c; | ||||
| 
 | ||||
| 	if (vgic_ready(kvm)) | ||||
| 		goto out; | ||||
| 
 | ||||
| 	kvm_for_each_vcpu(c, vcpu, kvm) { | ||||
| 		struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu; | ||||
| 
 | ||||
| 		if (IS_VGIC_ADDR_UNDEF(vgic_cpu->rd_iodev.base_addr)) { | ||||
| 			kvm_debug("vcpu %d redistributor base not set\n", c); | ||||
| 			ret = -ENXIO; | ||||
| 			goto out; | ||||
| 			return -ENXIO; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if (IS_VGIC_ADDR_UNDEF(dist->vgic_dist_base)) { | ||||
| 		kvm_err("Need to set vgic distributor addresses first\n"); | ||||
| 		ret = -ENXIO; | ||||
| 		goto out; | ||||
| 		return -ENXIO; | ||||
| 	} | ||||
| 
 | ||||
| 	if (!vgic_v3_check_base(kvm)) { | ||||
| 		kvm_err("VGIC redist and dist frames overlap\n"); | ||||
| 		ret = -EINVAL; | ||||
| 		goto out; | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 
 | ||||
| 	/*
 | ||||
| @ -530,22 +524,19 @@ int vgic_v3_map_resources(struct kvm *kvm) | ||||
| 	 * the VGIC before we need to use it. | ||||
| 	 */ | ||||
| 	if (!vgic_initialized(kvm)) { | ||||
| 		ret = -EBUSY; | ||||
| 		goto out; | ||||
| 		return -EBUSY; | ||||
| 	} | ||||
| 
 | ||||
| 	ret = vgic_register_dist_iodev(kvm, dist->vgic_dist_base, VGIC_V3); | ||||
| 	if (ret) { | ||||
| 		kvm_err("Unable to register VGICv3 dist MMIO regions\n"); | ||||
| 		goto out; | ||||
| 		return ret; | ||||
| 	} | ||||
| 
 | ||||
| 	if (kvm_vgic_global_state.has_gicv4_1) | ||||
| 		vgic_v4_configure_vsgis(kvm); | ||||
| 	dist->ready = true; | ||||
| 
 | ||||
| out: | ||||
| 	return ret; | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| DEFINE_STATIC_KEY_FALSE(vgic_v3_cpuif_trap); | ||||
|  | ||||
| @ -1010,9 +1010,21 @@ struct kvm_arch { | ||||
| 	 */ | ||||
| 	bool tdp_mmu_enabled; | ||||
| 
 | ||||
| 	/* List of struct tdp_mmu_pages being used as roots */ | ||||
| 	/*
 | ||||
| 	 * List of struct kvmp_mmu_pages being used as roots. | ||||
| 	 * All struct kvm_mmu_pages in the list should have | ||||
| 	 * tdp_mmu_page set. | ||||
| 	 * All struct kvm_mmu_pages in the list should have a positive | ||||
| 	 * root_count except when a thread holds the MMU lock and is removing | ||||
| 	 * an entry from the list. | ||||
| 	 */ | ||||
| 	struct list_head tdp_mmu_roots; | ||||
| 	/* List of struct tdp_mmu_pages not being used as roots */ | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * List of struct kvmp_mmu_pages not being used as roots. | ||||
| 	 * All struct kvm_mmu_pages in the list should have | ||||
| 	 * tdp_mmu_page set and a root_count of 0. | ||||
| 	 */ | ||||
| 	struct list_head tdp_mmu_pages; | ||||
| }; | ||||
| 
 | ||||
| @ -1287,6 +1299,8 @@ struct kvm_x86_ops { | ||||
| 	void (*migrate_timers)(struct kvm_vcpu *vcpu); | ||||
| 	void (*msr_filter_changed)(struct kvm_vcpu *vcpu); | ||||
| 	int (*complete_emulated_msr)(struct kvm_vcpu *vcpu, int err); | ||||
| 
 | ||||
| 	void (*vcpu_deliver_sipi_vector)(struct kvm_vcpu *vcpu, u8 vector); | ||||
| }; | ||||
| 
 | ||||
| struct kvm_x86_nested_ops { | ||||
| @ -1468,6 +1482,7 @@ int kvm_fast_pio(struct kvm_vcpu *vcpu, int size, unsigned short port, int in); | ||||
| int kvm_emulate_cpuid(struct kvm_vcpu *vcpu); | ||||
| int kvm_emulate_halt(struct kvm_vcpu *vcpu); | ||||
| int kvm_vcpu_halt(struct kvm_vcpu *vcpu); | ||||
| int kvm_emulate_ap_reset_hold(struct kvm_vcpu *vcpu); | ||||
| int kvm_emulate_wbinvd(struct kvm_vcpu *vcpu); | ||||
| 
 | ||||
| void kvm_get_segment(struct kvm_vcpu *vcpu, struct kvm_segment *var, int seg); | ||||
|  | ||||
| @ -674,7 +674,7 @@ static bool pv_eoi_get_pending(struct kvm_vcpu *vcpu) | ||||
| 			   (unsigned long long)vcpu->arch.pv_eoi.msr_val); | ||||
| 		return false; | ||||
| 	} | ||||
| 	return val & 0x1; | ||||
| 	return val & KVM_PV_EOI_ENABLED; | ||||
| } | ||||
| 
 | ||||
| static void pv_eoi_set_pending(struct kvm_vcpu *vcpu) | ||||
| @ -2898,7 +2898,7 @@ void kvm_apic_accept_events(struct kvm_vcpu *vcpu) | ||||
| 			/* evaluate pending_events before reading the vector */ | ||||
| 			smp_rmb(); | ||||
| 			sipi_vector = apic->sipi_vector; | ||||
| 			kvm_vcpu_deliver_sipi_vector(vcpu, sipi_vector); | ||||
| 			kvm_x86_ops.vcpu_deliver_sipi_vector(vcpu, sipi_vector); | ||||
| 			vcpu->arch.mp_state = KVM_MP_STATE_RUNNABLE; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| @ -49,7 +49,7 @@ static inline u64 rsvd_bits(int s, int e) | ||||
| 	if (e < s) | ||||
| 		return 0; | ||||
| 
 | ||||
| 	return ((1ULL << (e - s + 1)) - 1) << s; | ||||
| 	return ((2ULL << (e - s)) - 1) << s; | ||||
| } | ||||
| 
 | ||||
| void kvm_mmu_set_mmio_spte_mask(u64 mmio_value, u64 access_mask); | ||||
|  | ||||
| @ -3493,26 +3493,25 @@ static bool mmio_info_in_cache(struct kvm_vcpu *vcpu, u64 addr, bool direct) | ||||
|  * Return the level of the lowest level SPTE added to sptes. | ||||
|  * That SPTE may be non-present. | ||||
|  */ | ||||
| static int get_walk(struct kvm_vcpu *vcpu, u64 addr, u64 *sptes) | ||||
| static int get_walk(struct kvm_vcpu *vcpu, u64 addr, u64 *sptes, int *root_level) | ||||
| { | ||||
| 	struct kvm_shadow_walk_iterator iterator; | ||||
| 	int leaf = vcpu->arch.mmu->root_level; | ||||
| 	int leaf = -1; | ||||
| 	u64 spte; | ||||
| 
 | ||||
| 
 | ||||
| 	walk_shadow_page_lockless_begin(vcpu); | ||||
| 
 | ||||
| 	for (shadow_walk_init(&iterator, vcpu, addr); | ||||
| 	for (shadow_walk_init(&iterator, vcpu, addr), | ||||
| 	     *root_level = iterator.level; | ||||
| 	     shadow_walk_okay(&iterator); | ||||
| 	     __shadow_walk_next(&iterator, spte)) { | ||||
| 		leaf = iterator.level; | ||||
| 		spte = mmu_spte_get_lockless(iterator.sptep); | ||||
| 
 | ||||
| 		sptes[leaf - 1] = spte; | ||||
| 		sptes[leaf] = spte; | ||||
| 
 | ||||
| 		if (!is_shadow_present_pte(spte)) | ||||
| 			break; | ||||
| 
 | ||||
| 	} | ||||
| 
 | ||||
| 	walk_shadow_page_lockless_end(vcpu); | ||||
| @ -3520,14 +3519,12 @@ static int get_walk(struct kvm_vcpu *vcpu, u64 addr, u64 *sptes) | ||||
| 	return leaf; | ||||
| } | ||||
| 
 | ||||
| /* return true if reserved bit is detected on spte. */ | ||||
| /* return true if reserved bit(s) are detected on a valid, non-MMIO SPTE. */ | ||||
| static bool get_mmio_spte(struct kvm_vcpu *vcpu, u64 addr, u64 *sptep) | ||||
| { | ||||
| 	u64 sptes[PT64_ROOT_MAX_LEVEL]; | ||||
| 	u64 sptes[PT64_ROOT_MAX_LEVEL + 1]; | ||||
| 	struct rsvd_bits_validate *rsvd_check; | ||||
| 	int root = vcpu->arch.mmu->shadow_root_level; | ||||
| 	int leaf; | ||||
| 	int level; | ||||
| 	int root, leaf, level; | ||||
| 	bool reserved = false; | ||||
| 
 | ||||
| 	if (!VALID_PAGE(vcpu->arch.mmu->root_hpa)) { | ||||
| @ -3536,35 +3533,45 @@ static bool get_mmio_spte(struct kvm_vcpu *vcpu, u64 addr, u64 *sptep) | ||||
| 	} | ||||
| 
 | ||||
| 	if (is_tdp_mmu_root(vcpu->kvm, vcpu->arch.mmu->root_hpa)) | ||||
| 		leaf = kvm_tdp_mmu_get_walk(vcpu, addr, sptes); | ||||
| 		leaf = kvm_tdp_mmu_get_walk(vcpu, addr, sptes, &root); | ||||
| 	else | ||||
| 		leaf = get_walk(vcpu, addr, sptes); | ||||
| 		leaf = get_walk(vcpu, addr, sptes, &root); | ||||
| 
 | ||||
| 	if (unlikely(leaf < 0)) { | ||||
| 		*sptep = 0ull; | ||||
| 		return reserved; | ||||
| 	} | ||||
| 
 | ||||
| 	*sptep = sptes[leaf]; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Skip reserved bits checks on the terminal leaf if it's not a valid | ||||
| 	 * SPTE.  Note, this also (intentionally) skips MMIO SPTEs, which, by | ||||
| 	 * design, always have reserved bits set.  The purpose of the checks is | ||||
| 	 * to detect reserved bits on non-MMIO SPTEs. i.e. buggy SPTEs. | ||||
| 	 */ | ||||
| 	if (!is_shadow_present_pte(sptes[leaf])) | ||||
| 		leaf++; | ||||
| 
 | ||||
| 	rsvd_check = &vcpu->arch.mmu->shadow_zero_check; | ||||
| 
 | ||||
| 	for (level = root; level >= leaf; level--) { | ||||
| 		if (!is_shadow_present_pte(sptes[level - 1])) | ||||
| 			break; | ||||
| 	for (level = root; level >= leaf; level--) | ||||
| 		/*
 | ||||
| 		 * Use a bitwise-OR instead of a logical-OR to aggregate the | ||||
| 		 * reserved bit and EPT's invalid memtype/XWR checks to avoid | ||||
| 		 * adding a Jcc in the loop. | ||||
| 		 */ | ||||
| 		reserved |= __is_bad_mt_xwr(rsvd_check, sptes[level - 1]) | | ||||
| 			    __is_rsvd_bits_set(rsvd_check, sptes[level - 1], | ||||
| 					       level); | ||||
| 	} | ||||
| 		reserved |= __is_bad_mt_xwr(rsvd_check, sptes[level]) | | ||||
| 			    __is_rsvd_bits_set(rsvd_check, sptes[level], level); | ||||
| 
 | ||||
| 	if (reserved) { | ||||
| 		pr_err("%s: detect reserved bits on spte, addr 0x%llx, dump hierarchy:\n", | ||||
| 		       __func__, addr); | ||||
| 		for (level = root; level >= leaf; level--) | ||||
| 			pr_err("------ spte 0x%llx level %d.\n", | ||||
| 			       sptes[level - 1], level); | ||||
| 			       sptes[level], level); | ||||
| 	} | ||||
| 
 | ||||
| 	*sptep = sptes[leaf - 1]; | ||||
| 
 | ||||
| 	return reserved; | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -44,6 +44,47 @@ void kvm_mmu_uninit_tdp_mmu(struct kvm *kvm) | ||||
| 	WARN_ON(!list_empty(&kvm->arch.tdp_mmu_roots)); | ||||
| } | ||||
| 
 | ||||
| static void tdp_mmu_put_root(struct kvm *kvm, struct kvm_mmu_page *root) | ||||
| { | ||||
| 	if (kvm_mmu_put_root(kvm, root)) | ||||
| 		kvm_tdp_mmu_free_root(kvm, root); | ||||
| } | ||||
| 
 | ||||
| static inline bool tdp_mmu_next_root_valid(struct kvm *kvm, | ||||
| 					   struct kvm_mmu_page *root) | ||||
| { | ||||
| 	lockdep_assert_held(&kvm->mmu_lock); | ||||
| 
 | ||||
| 	if (list_entry_is_head(root, &kvm->arch.tdp_mmu_roots, link)) | ||||
| 		return false; | ||||
| 
 | ||||
| 	kvm_mmu_get_root(kvm, root); | ||||
| 	return true; | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| static inline struct kvm_mmu_page *tdp_mmu_next_root(struct kvm *kvm, | ||||
| 						     struct kvm_mmu_page *root) | ||||
| { | ||||
| 	struct kvm_mmu_page *next_root; | ||||
| 
 | ||||
| 	next_root = list_next_entry(root, link); | ||||
| 	tdp_mmu_put_root(kvm, root); | ||||
| 	return next_root; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Note: this iterator gets and puts references to the roots it iterates over. | ||||
|  * This makes it safe to release the MMU lock and yield within the loop, but | ||||
|  * if exiting the loop early, the caller must drop the reference to the most | ||||
|  * recent root. (Unless keeping a live reference is desirable.) | ||||
|  */ | ||||
| #define for_each_tdp_mmu_root_yield_safe(_kvm, _root)				\ | ||||
| 	for (_root = list_first_entry(&_kvm->arch.tdp_mmu_roots,	\ | ||||
| 				      typeof(*_root), link);		\ | ||||
| 	     tdp_mmu_next_root_valid(_kvm, _root);			\ | ||||
| 	     _root = tdp_mmu_next_root(_kvm, _root)) | ||||
| 
 | ||||
| #define for_each_tdp_mmu_root(_kvm, _root)				\ | ||||
| 	list_for_each_entry(_root, &_kvm->arch.tdp_mmu_roots, link) | ||||
| 
 | ||||
| @ -447,18 +488,9 @@ bool kvm_tdp_mmu_zap_gfn_range(struct kvm *kvm, gfn_t start, gfn_t end) | ||||
| 	struct kvm_mmu_page *root; | ||||
| 	bool flush = false; | ||||
| 
 | ||||
| 	for_each_tdp_mmu_root(kvm, root) { | ||||
| 		/*
 | ||||
| 		 * Take a reference on the root so that it cannot be freed if | ||||
| 		 * this thread releases the MMU lock and yields in this loop. | ||||
| 		 */ | ||||
| 		kvm_mmu_get_root(kvm, root); | ||||
| 
 | ||||
| 	for_each_tdp_mmu_root_yield_safe(kvm, root) | ||||
| 		flush |= zap_gfn_range(kvm, root, start, end, true); | ||||
| 
 | ||||
| 		kvm_mmu_put_root(kvm, root); | ||||
| 	} | ||||
| 
 | ||||
| 	return flush; | ||||
| } | ||||
| 
 | ||||
| @ -619,13 +651,7 @@ static int kvm_tdp_mmu_handle_hva_range(struct kvm *kvm, unsigned long start, | ||||
| 	int ret = 0; | ||||
| 	int as_id; | ||||
| 
 | ||||
| 	for_each_tdp_mmu_root(kvm, root) { | ||||
| 		/*
 | ||||
| 		 * Take a reference on the root so that it cannot be freed if | ||||
| 		 * this thread releases the MMU lock and yields in this loop. | ||||
| 		 */ | ||||
| 		kvm_mmu_get_root(kvm, root); | ||||
| 
 | ||||
| 	for_each_tdp_mmu_root_yield_safe(kvm, root) { | ||||
| 		as_id = kvm_mmu_page_as_id(root); | ||||
| 		slots = __kvm_memslots(kvm, as_id); | ||||
| 		kvm_for_each_memslot(memslot, slots) { | ||||
| @ -647,8 +673,6 @@ static int kvm_tdp_mmu_handle_hva_range(struct kvm *kvm, unsigned long start, | ||||
| 			ret |= handler(kvm, memslot, root, gfn_start, | ||||
| 				       gfn_end, data); | ||||
| 		} | ||||
| 
 | ||||
| 		kvm_mmu_put_root(kvm, root); | ||||
| 	} | ||||
| 
 | ||||
| 	return ret; | ||||
| @ -838,21 +862,13 @@ bool kvm_tdp_mmu_wrprot_slot(struct kvm *kvm, struct kvm_memory_slot *slot, | ||||
| 	int root_as_id; | ||||
| 	bool spte_set = false; | ||||
| 
 | ||||
| 	for_each_tdp_mmu_root(kvm, root) { | ||||
| 	for_each_tdp_mmu_root_yield_safe(kvm, root) { | ||||
| 		root_as_id = kvm_mmu_page_as_id(root); | ||||
| 		if (root_as_id != slot->as_id) | ||||
| 			continue; | ||||
| 
 | ||||
| 		/*
 | ||||
| 		 * Take a reference on the root so that it cannot be freed if | ||||
| 		 * this thread releases the MMU lock and yields in this loop. | ||||
| 		 */ | ||||
| 		kvm_mmu_get_root(kvm, root); | ||||
| 
 | ||||
| 		spte_set |= wrprot_gfn_range(kvm, root, slot->base_gfn, | ||||
| 			     slot->base_gfn + slot->npages, min_level); | ||||
| 
 | ||||
| 		kvm_mmu_put_root(kvm, root); | ||||
| 	} | ||||
| 
 | ||||
| 	return spte_set; | ||||
| @ -906,21 +922,13 @@ bool kvm_tdp_mmu_clear_dirty_slot(struct kvm *kvm, struct kvm_memory_slot *slot) | ||||
| 	int root_as_id; | ||||
| 	bool spte_set = false; | ||||
| 
 | ||||
| 	for_each_tdp_mmu_root(kvm, root) { | ||||
| 	for_each_tdp_mmu_root_yield_safe(kvm, root) { | ||||
| 		root_as_id = kvm_mmu_page_as_id(root); | ||||
| 		if (root_as_id != slot->as_id) | ||||
| 			continue; | ||||
| 
 | ||||
| 		/*
 | ||||
| 		 * Take a reference on the root so that it cannot be freed if | ||||
| 		 * this thread releases the MMU lock and yields in this loop. | ||||
| 		 */ | ||||
| 		kvm_mmu_get_root(kvm, root); | ||||
| 
 | ||||
| 		spte_set |= clear_dirty_gfn_range(kvm, root, slot->base_gfn, | ||||
| 				slot->base_gfn + slot->npages); | ||||
| 
 | ||||
| 		kvm_mmu_put_root(kvm, root); | ||||
| 	} | ||||
| 
 | ||||
| 	return spte_set; | ||||
| @ -1029,21 +1037,13 @@ bool kvm_tdp_mmu_slot_set_dirty(struct kvm *kvm, struct kvm_memory_slot *slot) | ||||
| 	int root_as_id; | ||||
| 	bool spte_set = false; | ||||
| 
 | ||||
| 	for_each_tdp_mmu_root(kvm, root) { | ||||
| 	for_each_tdp_mmu_root_yield_safe(kvm, root) { | ||||
| 		root_as_id = kvm_mmu_page_as_id(root); | ||||
| 		if (root_as_id != slot->as_id) | ||||
| 			continue; | ||||
| 
 | ||||
| 		/*
 | ||||
| 		 * Take a reference on the root so that it cannot be freed if | ||||
| 		 * this thread releases the MMU lock and yields in this loop. | ||||
| 		 */ | ||||
| 		kvm_mmu_get_root(kvm, root); | ||||
| 
 | ||||
| 		spte_set |= set_dirty_gfn_range(kvm, root, slot->base_gfn, | ||||
| 				slot->base_gfn + slot->npages); | ||||
| 
 | ||||
| 		kvm_mmu_put_root(kvm, root); | ||||
| 	} | ||||
| 	return spte_set; | ||||
| } | ||||
| @ -1089,21 +1089,13 @@ void kvm_tdp_mmu_zap_collapsible_sptes(struct kvm *kvm, | ||||
| 	struct kvm_mmu_page *root; | ||||
| 	int root_as_id; | ||||
| 
 | ||||
| 	for_each_tdp_mmu_root(kvm, root) { | ||||
| 	for_each_tdp_mmu_root_yield_safe(kvm, root) { | ||||
| 		root_as_id = kvm_mmu_page_as_id(root); | ||||
| 		if (root_as_id != slot->as_id) | ||||
| 			continue; | ||||
| 
 | ||||
| 		/*
 | ||||
| 		 * Take a reference on the root so that it cannot be freed if | ||||
| 		 * this thread releases the MMU lock and yields in this loop. | ||||
| 		 */ | ||||
| 		kvm_mmu_get_root(kvm, root); | ||||
| 
 | ||||
| 		zap_collapsible_spte_range(kvm, root, slot->base_gfn, | ||||
| 					   slot->base_gfn + slot->npages); | ||||
| 
 | ||||
| 		kvm_mmu_put_root(kvm, root); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| @ -1160,16 +1152,19 @@ bool kvm_tdp_mmu_write_protect_gfn(struct kvm *kvm, | ||||
|  * Return the level of the lowest level SPTE added to sptes. | ||||
|  * That SPTE may be non-present. | ||||
|  */ | ||||
| int kvm_tdp_mmu_get_walk(struct kvm_vcpu *vcpu, u64 addr, u64 *sptes) | ||||
| int kvm_tdp_mmu_get_walk(struct kvm_vcpu *vcpu, u64 addr, u64 *sptes, | ||||
| 			 int *root_level) | ||||
| { | ||||
| 	struct tdp_iter iter; | ||||
| 	struct kvm_mmu *mmu = vcpu->arch.mmu; | ||||
| 	int leaf = vcpu->arch.mmu->shadow_root_level; | ||||
| 	gfn_t gfn = addr >> PAGE_SHIFT; | ||||
| 	int leaf = -1; | ||||
| 
 | ||||
| 	*root_level = vcpu->arch.mmu->shadow_root_level; | ||||
| 
 | ||||
| 	tdp_mmu_for_each_pte(iter, mmu, gfn, gfn + 1) { | ||||
| 		leaf = iter.level; | ||||
| 		sptes[leaf - 1] = iter.old_spte; | ||||
| 		sptes[leaf] = iter.old_spte; | ||||
| 	} | ||||
| 
 | ||||
| 	return leaf; | ||||
|  | ||||
| @ -44,5 +44,7 @@ void kvm_tdp_mmu_zap_collapsible_sptes(struct kvm *kvm, | ||||
| bool kvm_tdp_mmu_write_protect_gfn(struct kvm *kvm, | ||||
| 				   struct kvm_memory_slot *slot, gfn_t gfn); | ||||
| 
 | ||||
| int kvm_tdp_mmu_get_walk(struct kvm_vcpu *vcpu, u64 addr, u64 *sptes); | ||||
| int kvm_tdp_mmu_get_walk(struct kvm_vcpu *vcpu, u64 addr, u64 *sptes, | ||||
| 			 int *root_level); | ||||
| 
 | ||||
| #endif /* __KVM_X86_MMU_TDP_MMU_H */ | ||||
|  | ||||
| @ -199,6 +199,7 @@ static bool nested_svm_vmrun_msrpm(struct vcpu_svm *svm) | ||||
| static bool svm_get_nested_state_pages(struct kvm_vcpu *vcpu) | ||||
| { | ||||
| 	struct vcpu_svm *svm = to_svm(vcpu); | ||||
| 
 | ||||
| 	if (!nested_svm_vmrun_msrpm(svm)) { | ||||
| 		vcpu->run->exit_reason = KVM_EXIT_INTERNAL_ERROR; | ||||
| 		vcpu->run->internal.suberror = | ||||
| @ -595,6 +596,8 @@ int nested_svm_vmexit(struct vcpu_svm *svm) | ||||
| 	svm->nested.vmcb12_gpa = 0; | ||||
| 	WARN_ON_ONCE(svm->nested.nested_run_pending); | ||||
| 
 | ||||
| 	kvm_clear_request(KVM_REQ_GET_NESTED_STATE_PAGES, &svm->vcpu); | ||||
| 
 | ||||
| 	/* in case we halted in L2 */ | ||||
| 	svm->vcpu.arch.mp_state = KVM_MP_STATE_RUNNABLE; | ||||
| 
 | ||||
| @ -754,6 +757,7 @@ void svm_leave_nested(struct vcpu_svm *svm) | ||||
| 		leave_guest_mode(&svm->vcpu); | ||||
| 		copy_vmcb_control_area(&vmcb->control, &hsave->control); | ||||
| 		nested_svm_uninit_mmu_context(&svm->vcpu); | ||||
| 		vmcb_mark_all_dirty(svm->vmcb); | ||||
| 	} | ||||
| 
 | ||||
| 	kvm_clear_request(KVM_REQ_GET_NESTED_STATE_PAGES, &svm->vcpu); | ||||
| @ -1194,6 +1198,10 @@ static int svm_set_nested_state(struct kvm_vcpu *vcpu, | ||||
| 	 * in the registers, the save area of the nested state instead | ||||
| 	 * contains saved L1 state. | ||||
| 	 */ | ||||
| 
 | ||||
| 	svm->nested.nested_run_pending = | ||||
| 		!!(kvm_state->flags & KVM_STATE_NESTED_RUN_PENDING); | ||||
| 
 | ||||
| 	copy_vmcb_control_area(&hsave->control, &svm->vmcb->control); | ||||
| 	hsave->save = *save; | ||||
| 
 | ||||
|  | ||||
| @ -1563,6 +1563,7 @@ static int sev_es_validate_vmgexit(struct vcpu_svm *svm) | ||||
| 			goto vmgexit_err; | ||||
| 		break; | ||||
| 	case SVM_VMGEXIT_NMI_COMPLETE: | ||||
| 	case SVM_VMGEXIT_AP_HLT_LOOP: | ||||
| 	case SVM_VMGEXIT_AP_JUMP_TABLE: | ||||
| 	case SVM_VMGEXIT_UNSUPPORTED_EVENT: | ||||
| 		break; | ||||
| @ -1888,6 +1889,9 @@ int sev_handle_vmgexit(struct vcpu_svm *svm) | ||||
| 	case SVM_VMGEXIT_NMI_COMPLETE: | ||||
| 		ret = svm_invoke_exit_handler(svm, SVM_EXIT_IRET); | ||||
| 		break; | ||||
| 	case SVM_VMGEXIT_AP_HLT_LOOP: | ||||
| 		ret = kvm_emulate_ap_reset_hold(&svm->vcpu); | ||||
| 		break; | ||||
| 	case SVM_VMGEXIT_AP_JUMP_TABLE: { | ||||
| 		struct kvm_sev_info *sev = &to_kvm_svm(svm->vcpu.kvm)->sev_info; | ||||
| 
 | ||||
| @ -2001,7 +2005,7 @@ void sev_es_vcpu_load(struct vcpu_svm *svm, int cpu) | ||||
| 	 * of which one step is to perform a VMLOAD. Since hardware does not | ||||
| 	 * perform a VMSAVE on VMRUN, the host savearea must be updated. | ||||
| 	 */ | ||||
| 	asm volatile(__ex("vmsave") : : "a" (__sme_page_pa(sd->save_area)) : "memory"); | ||||
| 	asm volatile(__ex("vmsave %0") : : "a" (__sme_page_pa(sd->save_area)) : "memory"); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Certain MSRs are restored on VMEXIT, only save ones that aren't | ||||
| @ -2040,3 +2044,21 @@ void sev_es_vcpu_put(struct vcpu_svm *svm) | ||||
| 		wrmsrl(host_save_user_msrs[i].index, svm->host_user_msrs[i]); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| void sev_vcpu_deliver_sipi_vector(struct kvm_vcpu *vcpu, u8 vector) | ||||
| { | ||||
| 	struct vcpu_svm *svm = to_svm(vcpu); | ||||
| 
 | ||||
| 	/* First SIPI: Use the values as initially set by the VMM */ | ||||
| 	if (!svm->received_first_sipi) { | ||||
| 		svm->received_first_sipi = true; | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Subsequent SIPI: Return from an AP Reset Hold VMGEXIT, where | ||||
| 	 * the guest will set the CS and RIP. Set SW_EXIT_INFO_2 to a | ||||
| 	 * non-zero value. | ||||
| 	 */ | ||||
| 	ghcb_set_sw_exit_info_2(svm->ghcb, 1); | ||||
| } | ||||
|  | ||||
| @ -3677,8 +3677,6 @@ static fastpath_t svm_exit_handlers_fastpath(struct kvm_vcpu *vcpu) | ||||
| 	return EXIT_FASTPATH_NONE; | ||||
| } | ||||
| 
 | ||||
| void __svm_vcpu_run(unsigned long vmcb_pa, unsigned long *regs); | ||||
| 
 | ||||
| static noinstr void svm_vcpu_enter_exit(struct kvm_vcpu *vcpu, | ||||
| 					struct vcpu_svm *svm) | ||||
| { | ||||
| @ -4384,6 +4382,14 @@ static bool svm_apic_init_signal_blocked(struct kvm_vcpu *vcpu) | ||||
| 		   (vmcb_is_intercept(&svm->vmcb->control, INTERCEPT_INIT)); | ||||
| } | ||||
| 
 | ||||
| static void svm_vcpu_deliver_sipi_vector(struct kvm_vcpu *vcpu, u8 vector) | ||||
| { | ||||
| 	if (!sev_es_guest(vcpu->kvm)) | ||||
| 		return kvm_vcpu_deliver_sipi_vector(vcpu, vector); | ||||
| 
 | ||||
| 	sev_vcpu_deliver_sipi_vector(vcpu, vector); | ||||
| } | ||||
| 
 | ||||
| static void svm_vm_destroy(struct kvm *kvm) | ||||
| { | ||||
| 	avic_vm_destroy(kvm); | ||||
| @ -4526,6 +4532,8 @@ static struct kvm_x86_ops svm_x86_ops __initdata = { | ||||
| 
 | ||||
| 	.msr_filter_changed = svm_msr_filter_changed, | ||||
| 	.complete_emulated_msr = svm_complete_emulated_msr, | ||||
| 
 | ||||
| 	.vcpu_deliver_sipi_vector = svm_vcpu_deliver_sipi_vector, | ||||
| }; | ||||
| 
 | ||||
| static struct kvm_x86_init_ops svm_init_ops __initdata = { | ||||
|  | ||||
| @ -185,6 +185,7 @@ struct vcpu_svm { | ||||
| 	struct vmcb_save_area *vmsa; | ||||
| 	struct ghcb *ghcb; | ||||
| 	struct kvm_host_map ghcb_map; | ||||
| 	bool received_first_sipi; | ||||
| 
 | ||||
| 	/* SEV-ES scratch area support */ | ||||
| 	void *ghcb_sa; | ||||
| @ -591,6 +592,7 @@ void sev_es_init_vmcb(struct vcpu_svm *svm); | ||||
| void sev_es_create_vcpu(struct vcpu_svm *svm); | ||||
| void sev_es_vcpu_load(struct vcpu_svm *svm, int cpu); | ||||
| void sev_es_vcpu_put(struct vcpu_svm *svm); | ||||
| void sev_vcpu_deliver_sipi_vector(struct kvm_vcpu *vcpu, u8 vector); | ||||
| 
 | ||||
| /* vmenter.S */ | ||||
| 
 | ||||
|  | ||||
| @ -4442,6 +4442,8 @@ void nested_vmx_vmexit(struct kvm_vcpu *vcpu, u32 vm_exit_reason, | ||||
| 	/* trying to cancel vmlaunch/vmresume is a bug */ | ||||
| 	WARN_ON_ONCE(vmx->nested.nested_run_pending); | ||||
| 
 | ||||
| 	kvm_clear_request(KVM_REQ_GET_NESTED_STATE_PAGES, vcpu); | ||||
| 
 | ||||
| 	/* Service the TLB flush request for L2 before switching to L1. */ | ||||
| 	if (kvm_check_request(KVM_REQ_TLB_FLUSH_CURRENT, vcpu)) | ||||
| 		kvm_vcpu_flush_tlb_current(vcpu); | ||||
|  | ||||
| @ -7707,6 +7707,8 @@ static struct kvm_x86_ops vmx_x86_ops __initdata = { | ||||
| 	.msr_filter_changed = vmx_msr_filter_changed, | ||||
| 	.complete_emulated_msr = kvm_complete_insn_gp, | ||||
| 	.cpu_dirty_log_size = vmx_cpu_dirty_log_size, | ||||
| 
 | ||||
| 	.vcpu_deliver_sipi_vector = kvm_vcpu_deliver_sipi_vector, | ||||
| }; | ||||
| 
 | ||||
| static __init int hardware_setup(void) | ||||
|  | ||||
| @ -7976,17 +7976,22 @@ void kvm_arch_exit(void) | ||||
| 	kmem_cache_destroy(x86_fpu_cache); | ||||
| } | ||||
| 
 | ||||
| int kvm_vcpu_halt(struct kvm_vcpu *vcpu) | ||||
| static int __kvm_vcpu_halt(struct kvm_vcpu *vcpu, int state, int reason) | ||||
| { | ||||
| 	++vcpu->stat.halt_exits; | ||||
| 	if (lapic_in_kernel(vcpu)) { | ||||
| 		vcpu->arch.mp_state = KVM_MP_STATE_HALTED; | ||||
| 		vcpu->arch.mp_state = state; | ||||
| 		return 1; | ||||
| 	} else { | ||||
| 		vcpu->run->exit_reason = KVM_EXIT_HLT; | ||||
| 		vcpu->run->exit_reason = reason; | ||||
| 		return 0; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| int kvm_vcpu_halt(struct kvm_vcpu *vcpu) | ||||
| { | ||||
| 	return __kvm_vcpu_halt(vcpu, KVM_MP_STATE_HALTED, KVM_EXIT_HLT); | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(kvm_vcpu_halt); | ||||
| 
 | ||||
| int kvm_emulate_halt(struct kvm_vcpu *vcpu) | ||||
| @ -8000,6 +8005,14 @@ int kvm_emulate_halt(struct kvm_vcpu *vcpu) | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(kvm_emulate_halt); | ||||
| 
 | ||||
| int kvm_emulate_ap_reset_hold(struct kvm_vcpu *vcpu) | ||||
| { | ||||
| 	int ret = kvm_skip_emulated_instruction(vcpu); | ||||
| 
 | ||||
| 	return __kvm_vcpu_halt(vcpu, KVM_MP_STATE_AP_RESET_HOLD, KVM_EXIT_AP_RESET_HOLD) && ret; | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(kvm_emulate_ap_reset_hold); | ||||
| 
 | ||||
| #ifdef CONFIG_X86_64 | ||||
| static int kvm_pv_clock_pairing(struct kvm_vcpu *vcpu, gpa_t paddr, | ||||
| 			        unsigned long clock_type) | ||||
| @ -8789,7 +8802,9 @@ static int vcpu_enter_guest(struct kvm_vcpu *vcpu) | ||||
| 
 | ||||
| 	if (kvm_request_pending(vcpu)) { | ||||
| 		if (kvm_check_request(KVM_REQ_GET_NESTED_STATE_PAGES, vcpu)) { | ||||
| 			if (unlikely(!kvm_x86_ops.nested_ops->get_nested_state_pages(vcpu))) { | ||||
| 			if (WARN_ON_ONCE(!is_guest_mode(vcpu))) | ||||
| 				; | ||||
| 			else if (unlikely(!kvm_x86_ops.nested_ops->get_nested_state_pages(vcpu))) { | ||||
| 				r = 0; | ||||
| 				goto out; | ||||
| 			} | ||||
| @ -9094,6 +9109,7 @@ static inline int vcpu_block(struct kvm *kvm, struct kvm_vcpu *vcpu) | ||||
| 	kvm_apic_accept_events(vcpu); | ||||
| 	switch(vcpu->arch.mp_state) { | ||||
| 	case KVM_MP_STATE_HALTED: | ||||
| 	case KVM_MP_STATE_AP_RESET_HOLD: | ||||
| 		vcpu->arch.pv.pv_unhalted = false; | ||||
| 		vcpu->arch.mp_state = | ||||
| 			KVM_MP_STATE_RUNNABLE; | ||||
| @ -9520,7 +9536,8 @@ int kvm_arch_vcpu_ioctl_get_mpstate(struct kvm_vcpu *vcpu, | ||||
| 		kvm_load_guest_fpu(vcpu); | ||||
| 
 | ||||
| 	kvm_apic_accept_events(vcpu); | ||||
| 	if (vcpu->arch.mp_state == KVM_MP_STATE_HALTED && | ||||
| 	if ((vcpu->arch.mp_state == KVM_MP_STATE_HALTED || | ||||
| 	     vcpu->arch.mp_state == KVM_MP_STATE_AP_RESET_HOLD) && | ||||
| 	    vcpu->arch.pv.pv_unhalted) | ||||
| 		mp_state->mp_state = KVM_MP_STATE_RUNNABLE; | ||||
| 	else | ||||
| @ -10152,6 +10169,7 @@ void kvm_vcpu_deliver_sipi_vector(struct kvm_vcpu *vcpu, u8 vector) | ||||
| 	kvm_set_segment(vcpu, &cs, VCPU_SREG_CS); | ||||
| 	kvm_rip_write(vcpu, 0); | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(kvm_vcpu_deliver_sipi_vector); | ||||
| 
 | ||||
| int kvm_arch_hardware_enable(void) | ||||
| { | ||||
|  | ||||
| @ -13,7 +13,7 @@ | ||||
| #define ARMV8_PMU_CYCLE_IDX		(ARMV8_PMU_MAX_COUNTERS - 1) | ||||
| #define ARMV8_PMU_MAX_COUNTER_PAIRS	((ARMV8_PMU_MAX_COUNTERS + 1) >> 1) | ||||
| 
 | ||||
| #ifdef CONFIG_KVM_ARM_PMU | ||||
| #ifdef CONFIG_HW_PERF_EVENTS | ||||
| 
 | ||||
| struct kvm_pmc { | ||||
| 	u8 idx;	/* index into the pmu->pmc array */ | ||||
|  | ||||
| @ -251,6 +251,7 @@ struct kvm_hyperv_exit { | ||||
| #define KVM_EXIT_X86_RDMSR        29 | ||||
| #define KVM_EXIT_X86_WRMSR        30 | ||||
| #define KVM_EXIT_DIRTY_RING_FULL  31 | ||||
| #define KVM_EXIT_AP_RESET_HOLD    32 | ||||
| 
 | ||||
| /* For KVM_EXIT_INTERNAL_ERROR */ | ||||
| /* Emulate instruction failed. */ | ||||
| @ -573,6 +574,7 @@ struct kvm_vapic_addr { | ||||
| #define KVM_MP_STATE_CHECK_STOP        6 | ||||
| #define KVM_MP_STATE_OPERATING         7 | ||||
| #define KVM_MP_STATE_LOAD              8 | ||||
| #define KVM_MP_STATE_AP_RESET_HOLD     9 | ||||
| 
 | ||||
| struct kvm_mp_state { | ||||
| 	__u32 mp_state; | ||||
|  | ||||
| @ -33,7 +33,7 @@ ifeq ($(ARCH),s390) | ||||
| 	UNAME_M := s390x | ||||
| endif | ||||
| 
 | ||||
| LIBKVM = lib/assert.c lib/elf.c lib/io.c lib/kvm_util.c lib/sparsebit.c lib/test_util.c | ||||
| LIBKVM = lib/assert.c lib/elf.c lib/io.c lib/kvm_util.c lib/sparsebit.c lib/test_util.c lib/guest_modes.c lib/perf_test_util.c | ||||
| LIBKVM_x86_64 = lib/x86_64/processor.c lib/x86_64/vmx.c lib/x86_64/svm.c lib/x86_64/ucall.c lib/x86_64/handlers.S | ||||
| LIBKVM_aarch64 = lib/aarch64/processor.c lib/aarch64/ucall.c | ||||
| LIBKVM_s390x = lib/s390x/processor.c lib/s390x/ucall.c lib/s390x/diag318_test_handler.c | ||||
|  | ||||
| @ -7,23 +7,20 @@ | ||||
|  * Copyright (C) 2019, Google, Inc. | ||||
|  */ | ||||
| 
 | ||||
| #define _GNU_SOURCE /* for program_invocation_name */ | ||||
| #define _GNU_SOURCE /* for pipe2 */ | ||||
| 
 | ||||
| #include <stdio.h> | ||||
| #include <stdlib.h> | ||||
| #include <sys/syscall.h> | ||||
| #include <unistd.h> | ||||
| #include <asm/unistd.h> | ||||
| #include <time.h> | ||||
| #include <poll.h> | ||||
| #include <pthread.h> | ||||
| #include <linux/bitmap.h> | ||||
| #include <linux/bitops.h> | ||||
| #include <linux/userfaultfd.h> | ||||
| #include <sys/syscall.h> | ||||
| 
 | ||||
| #include "perf_test_util.h" | ||||
| #include "processor.h" | ||||
| #include "kvm_util.h" | ||||
| #include "test_util.h" | ||||
| #include "perf_test_util.h" | ||||
| #include "guest_modes.h" | ||||
| 
 | ||||
| #ifdef __NR_userfaultfd | ||||
| 
 | ||||
| @ -39,12 +36,14 @@ | ||||
| #define PER_VCPU_DEBUG(...) _no_printf(__VA_ARGS__) | ||||
| #endif | ||||
| 
 | ||||
| static int nr_vcpus = 1; | ||||
| static uint64_t guest_percpu_mem_size = DEFAULT_PER_VCPU_MEM_SIZE; | ||||
| static char *guest_data_prototype; | ||||
| 
 | ||||
| static void *vcpu_worker(void *data) | ||||
| { | ||||
| 	int ret; | ||||
| 	struct vcpu_args *vcpu_args = (struct vcpu_args *)data; | ||||
| 	struct perf_test_vcpu_args *vcpu_args = (struct perf_test_vcpu_args *)data; | ||||
| 	int vcpu_id = vcpu_args->vcpu_id; | ||||
| 	struct kvm_vm *vm = perf_test_args.vm; | ||||
| 	struct kvm_run *run; | ||||
| @ -248,9 +247,14 @@ static int setup_demand_paging(struct kvm_vm *vm, | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static void run_test(enum vm_guest_mode mode, bool use_uffd, | ||||
| 		     useconds_t uffd_delay) | ||||
| struct test_params { | ||||
| 	bool use_uffd; | ||||
| 	useconds_t uffd_delay; | ||||
| }; | ||||
| 
 | ||||
| static void run_test(enum vm_guest_mode mode, void *arg) | ||||
| { | ||||
| 	struct test_params *p = arg; | ||||
| 	pthread_t *vcpu_threads; | ||||
| 	pthread_t *uffd_handler_threads = NULL; | ||||
| 	struct uffd_handler_args *uffd_args = NULL; | ||||
| @ -261,7 +265,7 @@ static void run_test(enum vm_guest_mode mode, bool use_uffd, | ||||
| 	int vcpu_id; | ||||
| 	int r; | ||||
| 
 | ||||
| 	vm = create_vm(mode, nr_vcpus, guest_percpu_mem_size); | ||||
| 	vm = perf_test_create_vm(mode, nr_vcpus, guest_percpu_mem_size); | ||||
| 
 | ||||
| 	perf_test_args.wr_fract = 1; | ||||
| 
 | ||||
| @ -273,9 +277,9 @@ static void run_test(enum vm_guest_mode mode, bool use_uffd, | ||||
| 	vcpu_threads = malloc(nr_vcpus * sizeof(*vcpu_threads)); | ||||
| 	TEST_ASSERT(vcpu_threads, "Memory allocation failed"); | ||||
| 
 | ||||
| 	add_vcpus(vm, nr_vcpus, guest_percpu_mem_size); | ||||
| 	perf_test_setup_vcpus(vm, nr_vcpus, guest_percpu_mem_size); | ||||
| 
 | ||||
| 	if (use_uffd) { | ||||
| 	if (p->use_uffd) { | ||||
| 		uffd_handler_threads = | ||||
| 			malloc(nr_vcpus * sizeof(*uffd_handler_threads)); | ||||
| 		TEST_ASSERT(uffd_handler_threads, "Memory allocation failed"); | ||||
| @ -308,7 +312,7 @@ static void run_test(enum vm_guest_mode mode, bool use_uffd, | ||||
| 			r = setup_demand_paging(vm, | ||||
| 						&uffd_handler_threads[vcpu_id], | ||||
| 						pipefds[vcpu_id * 2], | ||||
| 						uffd_delay, &uffd_args[vcpu_id], | ||||
| 						p->uffd_delay, &uffd_args[vcpu_id], | ||||
| 						vcpu_hva, guest_percpu_mem_size); | ||||
| 			if (r < 0) | ||||
| 				exit(-r); | ||||
| @ -339,7 +343,7 @@ static void run_test(enum vm_guest_mode mode, bool use_uffd, | ||||
| 
 | ||||
| 	pr_info("All vCPU threads joined\n"); | ||||
| 
 | ||||
| 	if (use_uffd) { | ||||
| 	if (p->use_uffd) { | ||||
| 		char c; | ||||
| 
 | ||||
| 		/* Tell the user fault fd handler threads to quit */ | ||||
| @ -357,43 +361,23 @@ static void run_test(enum vm_guest_mode mode, bool use_uffd, | ||||
| 		perf_test_args.vcpu_args[0].pages * nr_vcpus / | ||||
| 		((double)ts_diff.tv_sec + (double)ts_diff.tv_nsec / 100000000.0)); | ||||
| 
 | ||||
| 	ucall_uninit(vm); | ||||
| 	kvm_vm_free(vm); | ||||
| 	perf_test_destroy_vm(vm); | ||||
| 
 | ||||
| 	free(guest_data_prototype); | ||||
| 	free(vcpu_threads); | ||||
| 	if (use_uffd) { | ||||
| 	if (p->use_uffd) { | ||||
| 		free(uffd_handler_threads); | ||||
| 		free(uffd_args); | ||||
| 		free(pipefds); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| struct guest_mode { | ||||
| 	bool supported; | ||||
| 	bool enabled; | ||||
| }; | ||||
| static struct guest_mode guest_modes[NUM_VM_MODES]; | ||||
| 
 | ||||
| #define guest_mode_init(mode, supported, enabled) ({ \ | ||||
| 	guest_modes[mode] = (struct guest_mode){ supported, enabled }; \ | ||||
| }) | ||||
| 
 | ||||
| static void help(char *name) | ||||
| { | ||||
| 	int i; | ||||
| 
 | ||||
| 	puts(""); | ||||
| 	printf("usage: %s [-h] [-m mode] [-u] [-d uffd_delay_usec]\n" | ||||
| 	       "          [-b memory] [-v vcpus]\n", name); | ||||
| 	printf(" -m: specify the guest mode ID to test\n" | ||||
| 	       "     (default: test all supported modes)\n" | ||||
| 	       "     This option may be used multiple times.\n" | ||||
| 	       "     Guest mode IDs:\n"); | ||||
| 	for (i = 0; i < NUM_VM_MODES; ++i) { | ||||
| 		printf("         %d:    %s%s\n", i, vm_guest_mode_string(i), | ||||
| 		       guest_modes[i].supported ? " (supported)" : ""); | ||||
| 	} | ||||
| 	guest_modes_help(); | ||||
| 	printf(" -u: use User Fault FD to handle vCPU page\n" | ||||
| 	       "     faults.\n"); | ||||
| 	printf(" -d: add a delay in usec to the User Fault\n" | ||||
| @ -410,53 +394,22 @@ static void help(char *name) | ||||
| int main(int argc, char *argv[]) | ||||
| { | ||||
| 	int max_vcpus = kvm_check_cap(KVM_CAP_MAX_VCPUS); | ||||
| 	bool mode_selected = false; | ||||
| 	unsigned int mode; | ||||
| 	int opt, i; | ||||
| 	bool use_uffd = false; | ||||
| 	useconds_t uffd_delay = 0; | ||||
| 	struct test_params p = {}; | ||||
| 	int opt; | ||||
| 
 | ||||
| #ifdef __x86_64__ | ||||
| 	guest_mode_init(VM_MODE_PXXV48_4K, true, true); | ||||
| #endif | ||||
| #ifdef __aarch64__ | ||||
| 	guest_mode_init(VM_MODE_P40V48_4K, true, true); | ||||
| 	guest_mode_init(VM_MODE_P40V48_64K, true, true); | ||||
| 	{ | ||||
| 		unsigned int limit = kvm_check_cap(KVM_CAP_ARM_VM_IPA_SIZE); | ||||
| 
 | ||||
| 		if (limit >= 52) | ||||
| 			guest_mode_init(VM_MODE_P52V48_64K, true, true); | ||||
| 		if (limit >= 48) { | ||||
| 			guest_mode_init(VM_MODE_P48V48_4K, true, true); | ||||
| 			guest_mode_init(VM_MODE_P48V48_64K, true, true); | ||||
| 		} | ||||
| 	} | ||||
| #endif | ||||
| #ifdef __s390x__ | ||||
| 	guest_mode_init(VM_MODE_P40V48_4K, true, true); | ||||
| #endif | ||||
| 	guest_modes_append_default(); | ||||
| 
 | ||||
| 	while ((opt = getopt(argc, argv, "hm:ud:b:v:")) != -1) { | ||||
| 		switch (opt) { | ||||
| 		case 'm': | ||||
| 			if (!mode_selected) { | ||||
| 				for (i = 0; i < NUM_VM_MODES; ++i) | ||||
| 					guest_modes[i].enabled = false; | ||||
| 				mode_selected = true; | ||||
| 			} | ||||
| 			mode = strtoul(optarg, NULL, 10); | ||||
| 			TEST_ASSERT(mode < NUM_VM_MODES, | ||||
| 				    "Guest mode ID %d too big", mode); | ||||
| 			guest_modes[mode].enabled = true; | ||||
| 			guest_modes_cmdline(optarg); | ||||
| 			break; | ||||
| 		case 'u': | ||||
| 			use_uffd = true; | ||||
| 			p.use_uffd = true; | ||||
| 			break; | ||||
| 		case 'd': | ||||
| 			uffd_delay = strtoul(optarg, NULL, 0); | ||||
| 			TEST_ASSERT(uffd_delay >= 0, | ||||
| 				    "A negative UFFD delay is not supported."); | ||||
| 			p.uffd_delay = strtoul(optarg, NULL, 0); | ||||
| 			TEST_ASSERT(p.uffd_delay >= 0, "A negative UFFD delay is not supported."); | ||||
| 			break; | ||||
| 		case 'b': | ||||
| 			guest_percpu_mem_size = parse_size(optarg); | ||||
| @ -473,14 +426,7 @@ int main(int argc, char *argv[]) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	for (i = 0; i < NUM_VM_MODES; ++i) { | ||||
| 		if (!guest_modes[i].enabled) | ||||
| 			continue; | ||||
| 		TEST_ASSERT(guest_modes[i].supported, | ||||
| 			    "Guest mode ID %d (%s) not supported.", | ||||
| 			    i, vm_guest_mode_string(i)); | ||||
| 		run_test(i, use_uffd, uffd_delay); | ||||
| 	} | ||||
| 	for_each_guest_mode(run_test, &p); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| @ -8,29 +8,28 @@ | ||||
|  * Copyright (C) 2020, Google, Inc. | ||||
|  */ | ||||
| 
 | ||||
| #define _GNU_SOURCE /* for program_invocation_name */ | ||||
| 
 | ||||
| #include <stdio.h> | ||||
| #include <stdlib.h> | ||||
| #include <unistd.h> | ||||
| #include <time.h> | ||||
| #include <pthread.h> | ||||
| #include <linux/bitmap.h> | ||||
| #include <linux/bitops.h> | ||||
| 
 | ||||
| #include "kvm_util.h" | ||||
| #include "perf_test_util.h" | ||||
| #include "processor.h" | ||||
| #include "test_util.h" | ||||
| #include "perf_test_util.h" | ||||
| #include "guest_modes.h" | ||||
| 
 | ||||
| /* How many host loops to run by default (one KVM_GET_DIRTY_LOG for each loop)*/ | ||||
| #define TEST_HOST_LOOP_N		2UL | ||||
| 
 | ||||
| static int nr_vcpus = 1; | ||||
| static uint64_t guest_percpu_mem_size = DEFAULT_PER_VCPU_MEM_SIZE; | ||||
| 
 | ||||
| /* Host variables */ | ||||
| static u64 dirty_log_manual_caps; | ||||
| static bool host_quit; | ||||
| static uint64_t iteration; | ||||
| static uint64_t vcpu_last_completed_iteration[MAX_VCPUS]; | ||||
| static uint64_t vcpu_last_completed_iteration[KVM_MAX_VCPUS]; | ||||
| 
 | ||||
| static void *vcpu_worker(void *data) | ||||
| { | ||||
| @ -42,7 +41,7 @@ static void *vcpu_worker(void *data) | ||||
| 	struct timespec ts_diff; | ||||
| 	struct timespec total = (struct timespec){0}; | ||||
| 	struct timespec avg; | ||||
| 	struct vcpu_args *vcpu_args = (struct vcpu_args *)data; | ||||
| 	struct perf_test_vcpu_args *vcpu_args = (struct perf_test_vcpu_args *)data; | ||||
| 	int vcpu_id = vcpu_args->vcpu_id; | ||||
| 
 | ||||
| 	vcpu_args_set(vm, vcpu_id, 1, vcpu_id); | ||||
| @ -89,9 +88,15 @@ static void *vcpu_worker(void *data) | ||||
| 	return NULL; | ||||
| } | ||||
| 
 | ||||
| static void run_test(enum vm_guest_mode mode, unsigned long iterations, | ||||
| 		     uint64_t phys_offset, int wr_fract) | ||||
| struct test_params { | ||||
| 	unsigned long iterations; | ||||
| 	uint64_t phys_offset; | ||||
| 	int wr_fract; | ||||
| }; | ||||
| 
 | ||||
| static void run_test(enum vm_guest_mode mode, void *arg) | ||||
| { | ||||
| 	struct test_params *p = arg; | ||||
| 	pthread_t *vcpu_threads; | ||||
| 	struct kvm_vm *vm; | ||||
| 	unsigned long *bmap; | ||||
| @ -106,9 +111,9 @@ static void run_test(enum vm_guest_mode mode, unsigned long iterations, | ||||
| 	struct kvm_enable_cap cap = {}; | ||||
| 	struct timespec clear_dirty_log_total = (struct timespec){0}; | ||||
| 
 | ||||
| 	vm = create_vm(mode, nr_vcpus, guest_percpu_mem_size); | ||||
| 	vm = perf_test_create_vm(mode, nr_vcpus, guest_percpu_mem_size); | ||||
| 
 | ||||
| 	perf_test_args.wr_fract = wr_fract; | ||||
| 	perf_test_args.wr_fract = p->wr_fract; | ||||
| 
 | ||||
| 	guest_num_pages = (nr_vcpus * guest_percpu_mem_size) >> vm_get_page_shift(vm); | ||||
| 	guest_num_pages = vm_adjust_num_guest_pages(mode, guest_num_pages); | ||||
| @ -124,7 +129,7 @@ static void run_test(enum vm_guest_mode mode, unsigned long iterations, | ||||
| 	vcpu_threads = malloc(nr_vcpus * sizeof(*vcpu_threads)); | ||||
| 	TEST_ASSERT(vcpu_threads, "Memory allocation failed"); | ||||
| 
 | ||||
| 	add_vcpus(vm, nr_vcpus, guest_percpu_mem_size); | ||||
| 	perf_test_setup_vcpus(vm, nr_vcpus, guest_percpu_mem_size); | ||||
| 
 | ||||
| 	sync_global_to_guest(vm, perf_test_args); | ||||
| 
 | ||||
| @ -150,13 +155,13 @@ static void run_test(enum vm_guest_mode mode, unsigned long iterations, | ||||
| 
 | ||||
| 	/* Enable dirty logging */ | ||||
| 	clock_gettime(CLOCK_MONOTONIC, &start); | ||||
| 	vm_mem_region_set_flags(vm, TEST_MEM_SLOT_INDEX, | ||||
| 	vm_mem_region_set_flags(vm, PERF_TEST_MEM_SLOT_INDEX, | ||||
| 				KVM_MEM_LOG_DIRTY_PAGES); | ||||
| 	ts_diff = timespec_diff_now(start); | ||||
| 	pr_info("Enabling dirty logging time: %ld.%.9lds\n\n", | ||||
| 		ts_diff.tv_sec, ts_diff.tv_nsec); | ||||
| 
 | ||||
| 	while (iteration < iterations) { | ||||
| 	while (iteration < p->iterations) { | ||||
| 		/*
 | ||||
| 		 * Incrementing the iteration number will start the vCPUs | ||||
| 		 * dirtying memory again. | ||||
| @ -177,7 +182,7 @@ static void run_test(enum vm_guest_mode mode, unsigned long iterations, | ||||
| 			iteration, ts_diff.tv_sec, ts_diff.tv_nsec); | ||||
| 
 | ||||
| 		clock_gettime(CLOCK_MONOTONIC, &start); | ||||
| 		kvm_vm_get_dirty_log(vm, TEST_MEM_SLOT_INDEX, bmap); | ||||
| 		kvm_vm_get_dirty_log(vm, PERF_TEST_MEM_SLOT_INDEX, bmap); | ||||
| 
 | ||||
| 		ts_diff = timespec_diff_now(start); | ||||
| 		get_dirty_log_total = timespec_add(get_dirty_log_total, | ||||
| @ -187,7 +192,7 @@ static void run_test(enum vm_guest_mode mode, unsigned long iterations, | ||||
| 
 | ||||
| 		if (dirty_log_manual_caps) { | ||||
| 			clock_gettime(CLOCK_MONOTONIC, &start); | ||||
| 			kvm_vm_clear_dirty_log(vm, TEST_MEM_SLOT_INDEX, bmap, 0, | ||||
| 			kvm_vm_clear_dirty_log(vm, PERF_TEST_MEM_SLOT_INDEX, bmap, 0, | ||||
| 					       host_num_pages); | ||||
| 
 | ||||
| 			ts_diff = timespec_diff_now(start); | ||||
| @ -205,43 +210,30 @@ static void run_test(enum vm_guest_mode mode, unsigned long iterations, | ||||
| 
 | ||||
| 	/* Disable dirty logging */ | ||||
| 	clock_gettime(CLOCK_MONOTONIC, &start); | ||||
| 	vm_mem_region_set_flags(vm, TEST_MEM_SLOT_INDEX, 0); | ||||
| 	vm_mem_region_set_flags(vm, PERF_TEST_MEM_SLOT_INDEX, 0); | ||||
| 	ts_diff = timespec_diff_now(start); | ||||
| 	pr_info("Disabling dirty logging time: %ld.%.9lds\n", | ||||
| 		ts_diff.tv_sec, ts_diff.tv_nsec); | ||||
| 
 | ||||
| 	avg = timespec_div(get_dirty_log_total, iterations); | ||||
| 	avg = timespec_div(get_dirty_log_total, p->iterations); | ||||
| 	pr_info("Get dirty log over %lu iterations took %ld.%.9lds. (Avg %ld.%.9lds/iteration)\n", | ||||
| 		iterations, get_dirty_log_total.tv_sec, | ||||
| 		p->iterations, get_dirty_log_total.tv_sec, | ||||
| 		get_dirty_log_total.tv_nsec, avg.tv_sec, avg.tv_nsec); | ||||
| 
 | ||||
| 	if (dirty_log_manual_caps) { | ||||
| 		avg = timespec_div(clear_dirty_log_total, iterations); | ||||
| 		avg = timespec_div(clear_dirty_log_total, p->iterations); | ||||
| 		pr_info("Clear dirty log over %lu iterations took %ld.%.9lds. (Avg %ld.%.9lds/iteration)\n", | ||||
| 			iterations, clear_dirty_log_total.tv_sec, | ||||
| 			p->iterations, clear_dirty_log_total.tv_sec, | ||||
| 			clear_dirty_log_total.tv_nsec, avg.tv_sec, avg.tv_nsec); | ||||
| 	} | ||||
| 
 | ||||
| 	free(bmap); | ||||
| 	free(vcpu_threads); | ||||
| 	ucall_uninit(vm); | ||||
| 	kvm_vm_free(vm); | ||||
| 	perf_test_destroy_vm(vm); | ||||
| } | ||||
| 
 | ||||
| struct guest_mode { | ||||
| 	bool supported; | ||||
| 	bool enabled; | ||||
| }; | ||||
| static struct guest_mode guest_modes[NUM_VM_MODES]; | ||||
| 
 | ||||
| #define guest_mode_init(mode, supported, enabled) ({ \ | ||||
| 	guest_modes[mode] = (struct guest_mode){ supported, enabled }; \ | ||||
| }) | ||||
| 
 | ||||
| static void help(char *name) | ||||
| { | ||||
| 	int i; | ||||
| 
 | ||||
| 	puts(""); | ||||
| 	printf("usage: %s [-h] [-i iterations] [-p offset] " | ||||
| 	       "[-m mode] [-b vcpu bytes] [-v vcpus]\n", name); | ||||
| @ -250,14 +242,7 @@ static void help(char *name) | ||||
| 	       TEST_HOST_LOOP_N); | ||||
| 	printf(" -p: specify guest physical test memory offset\n" | ||||
| 	       "     Warning: a low offset can conflict with the loaded test code.\n"); | ||||
| 	printf(" -m: specify the guest mode ID to test " | ||||
| 	       "(default: test all supported modes)\n" | ||||
| 	       "     This option may be used multiple times.\n" | ||||
| 	       "     Guest mode IDs:\n"); | ||||
| 	for (i = 0; i < NUM_VM_MODES; ++i) { | ||||
| 		printf("         %d:    %s%s\n", i, vm_guest_mode_string(i), | ||||
| 		       guest_modes[i].supported ? " (supported)" : ""); | ||||
| 	} | ||||
| 	guest_modes_help(); | ||||
| 	printf(" -b: specify the size of the memory region which should be\n" | ||||
| 	       "     dirtied by each vCPU. e.g. 10M or 3G.\n" | ||||
| 	       "     (default: 1G)\n"); | ||||
| @ -272,74 +257,43 @@ static void help(char *name) | ||||
| 
 | ||||
| int main(int argc, char *argv[]) | ||||
| { | ||||
| 	unsigned long iterations = TEST_HOST_LOOP_N; | ||||
| 	bool mode_selected = false; | ||||
| 	uint64_t phys_offset = 0; | ||||
| 	unsigned int mode; | ||||
| 	int opt, i; | ||||
| 	int wr_fract = 1; | ||||
| 	int max_vcpus = kvm_check_cap(KVM_CAP_MAX_VCPUS); | ||||
| 	struct test_params p = { | ||||
| 		.iterations = TEST_HOST_LOOP_N, | ||||
| 		.wr_fract = 1, | ||||
| 	}; | ||||
| 	int opt; | ||||
| 
 | ||||
| 	dirty_log_manual_caps = | ||||
| 		kvm_check_cap(KVM_CAP_MANUAL_DIRTY_LOG_PROTECT2); | ||||
| 	dirty_log_manual_caps &= (KVM_DIRTY_LOG_MANUAL_PROTECT_ENABLE | | ||||
| 				  KVM_DIRTY_LOG_INITIALLY_SET); | ||||
| 
 | ||||
| #ifdef __x86_64__ | ||||
| 	guest_mode_init(VM_MODE_PXXV48_4K, true, true); | ||||
| #endif | ||||
| #ifdef __aarch64__ | ||||
| 	guest_mode_init(VM_MODE_P40V48_4K, true, true); | ||||
| 	guest_mode_init(VM_MODE_P40V48_64K, true, true); | ||||
| 
 | ||||
| 	{ | ||||
| 		unsigned int limit = kvm_check_cap(KVM_CAP_ARM_VM_IPA_SIZE); | ||||
| 
 | ||||
| 		if (limit >= 52) | ||||
| 			guest_mode_init(VM_MODE_P52V48_64K, true, true); | ||||
| 		if (limit >= 48) { | ||||
| 			guest_mode_init(VM_MODE_P48V48_4K, true, true); | ||||
| 			guest_mode_init(VM_MODE_P48V48_64K, true, true); | ||||
| 		} | ||||
| 	} | ||||
| #endif | ||||
| #ifdef __s390x__ | ||||
| 	guest_mode_init(VM_MODE_P40V48_4K, true, true); | ||||
| #endif | ||||
| 	guest_modes_append_default(); | ||||
| 
 | ||||
| 	while ((opt = getopt(argc, argv, "hi:p:m:b:f:v:")) != -1) { | ||||
| 		switch (opt) { | ||||
| 		case 'i': | ||||
| 			iterations = strtol(optarg, NULL, 10); | ||||
| 			p.iterations = strtol(optarg, NULL, 10); | ||||
| 			break; | ||||
| 		case 'p': | ||||
| 			phys_offset = strtoull(optarg, NULL, 0); | ||||
| 			p.phys_offset = strtoull(optarg, NULL, 0); | ||||
| 			break; | ||||
| 		case 'm': | ||||
| 			if (!mode_selected) { | ||||
| 				for (i = 0; i < NUM_VM_MODES; ++i) | ||||
| 					guest_modes[i].enabled = false; | ||||
| 				mode_selected = true; | ||||
| 			} | ||||
| 			mode = strtoul(optarg, NULL, 10); | ||||
| 			TEST_ASSERT(mode < NUM_VM_MODES, | ||||
| 				    "Guest mode ID %d too big", mode); | ||||
| 			guest_modes[mode].enabled = true; | ||||
| 			guest_modes_cmdline(optarg); | ||||
| 			break; | ||||
| 		case 'b': | ||||
| 			guest_percpu_mem_size = parse_size(optarg); | ||||
| 			break; | ||||
| 		case 'f': | ||||
| 			wr_fract = atoi(optarg); | ||||
| 			TEST_ASSERT(wr_fract >= 1, | ||||
| 			p.wr_fract = atoi(optarg); | ||||
| 			TEST_ASSERT(p.wr_fract >= 1, | ||||
| 				    "Write fraction cannot be less than one"); | ||||
| 			break; | ||||
| 		case 'v': | ||||
| 			nr_vcpus = atoi(optarg); | ||||
| 			TEST_ASSERT(nr_vcpus > 0, | ||||
| 				    "Must have a positive number of vCPUs"); | ||||
| 			TEST_ASSERT(nr_vcpus <= MAX_VCPUS, | ||||
| 				    "This test does not currently support\n" | ||||
| 				    "more than %d vCPUs.", MAX_VCPUS); | ||||
| 			TEST_ASSERT(nr_vcpus > 0 && nr_vcpus <= max_vcpus, | ||||
| 				    "Invalid number of vcpus, must be between 1 and %d", max_vcpus); | ||||
| 			break; | ||||
| 		case 'h': | ||||
| 		default: | ||||
| @ -348,18 +302,11 @@ int main(int argc, char *argv[]) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	TEST_ASSERT(iterations >= 2, "The test should have at least two iterations"); | ||||
| 	TEST_ASSERT(p.iterations >= 2, "The test should have at least two iterations"); | ||||
| 
 | ||||
| 	pr_info("Test iterations: %"PRIu64"\n",	iterations); | ||||
| 	pr_info("Test iterations: %"PRIu64"\n",	p.iterations); | ||||
| 
 | ||||
| 	for (i = 0; i < NUM_VM_MODES; ++i) { | ||||
| 		if (!guest_modes[i].enabled) | ||||
| 			continue; | ||||
| 		TEST_ASSERT(guest_modes[i].supported, | ||||
| 			    "Guest mode ID %d (%s) not supported.", | ||||
| 			    i, vm_guest_mode_string(i)); | ||||
| 		run_test(i, iterations, phys_offset, wr_fract); | ||||
| 	} | ||||
| 	for_each_guest_mode(run_test, &p); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| @ -9,8 +9,6 @@ | ||||
| 
 | ||||
| #include <stdio.h> | ||||
| #include <stdlib.h> | ||||
| #include <unistd.h> | ||||
| #include <time.h> | ||||
| #include <pthread.h> | ||||
| #include <semaphore.h> | ||||
| #include <sys/types.h> | ||||
| @ -20,8 +18,9 @@ | ||||
| #include <linux/bitops.h> | ||||
| #include <asm/barrier.h> | ||||
| 
 | ||||
| #include "test_util.h" | ||||
| #include "kvm_util.h" | ||||
| #include "test_util.h" | ||||
| #include "guest_modes.h" | ||||
| #include "processor.h" | ||||
| 
 | ||||
| #define VCPU_ID				1 | ||||
| @ -673,9 +672,15 @@ static struct kvm_vm *create_vm(enum vm_guest_mode mode, uint32_t vcpuid, | ||||
| #define DIRTY_MEM_BITS 30 /* 1G */ | ||||
| #define PAGE_SHIFT_4K  12 | ||||
| 
 | ||||
| static void run_test(enum vm_guest_mode mode, unsigned long iterations, | ||||
| 		     unsigned long interval, uint64_t phys_offset) | ||||
| struct test_params { | ||||
| 	unsigned long iterations; | ||||
| 	unsigned long interval; | ||||
| 	uint64_t phys_offset; | ||||
| }; | ||||
| 
 | ||||
| static void run_test(enum vm_guest_mode mode, void *arg) | ||||
| { | ||||
| 	struct test_params *p = arg; | ||||
| 	struct kvm_vm *vm; | ||||
| 	unsigned long *bmap; | ||||
| 
 | ||||
| @ -709,12 +714,12 @@ static void run_test(enum vm_guest_mode mode, unsigned long iterations, | ||||
| 	host_page_size = getpagesize(); | ||||
| 	host_num_pages = vm_num_host_pages(mode, guest_num_pages); | ||||
| 
 | ||||
| 	if (!phys_offset) { | ||||
| 	if (!p->phys_offset) { | ||||
| 		guest_test_phys_mem = (vm_get_max_gfn(vm) - | ||||
| 				       guest_num_pages) * guest_page_size; | ||||
| 		guest_test_phys_mem &= ~(host_page_size - 1); | ||||
| 	} else { | ||||
| 		guest_test_phys_mem = phys_offset; | ||||
| 		guest_test_phys_mem = p->phys_offset; | ||||
| 	} | ||||
| 
 | ||||
| #ifdef __s390x__ | ||||
| @ -758,9 +763,9 @@ static void run_test(enum vm_guest_mode mode, unsigned long iterations, | ||||
| 
 | ||||
| 	pthread_create(&vcpu_thread, NULL, vcpu_worker, vm); | ||||
| 
 | ||||
| 	while (iteration < iterations) { | ||||
| 	while (iteration < p->iterations) { | ||||
| 		/* Give the vcpu thread some time to dirty some pages */ | ||||
| 		usleep(interval * 1000); | ||||
| 		usleep(p->interval * 1000); | ||||
| 		log_mode_collect_dirty_pages(vm, TEST_MEM_SLOT_INDEX, | ||||
| 					     bmap, host_num_pages); | ||||
| 		vm_dirty_log_verify(mode, bmap); | ||||
| @ -783,20 +788,8 @@ static void run_test(enum vm_guest_mode mode, unsigned long iterations, | ||||
| 	kvm_vm_free(vm); | ||||
| } | ||||
| 
 | ||||
| struct guest_mode { | ||||
| 	bool supported; | ||||
| 	bool enabled; | ||||
| }; | ||||
| static struct guest_mode guest_modes[NUM_VM_MODES]; | ||||
| 
 | ||||
| #define guest_mode_init(mode, supported, enabled) ({ \ | ||||
| 	guest_modes[mode] = (struct guest_mode){ supported, enabled }; \ | ||||
| }) | ||||
| 
 | ||||
| static void help(char *name) | ||||
| { | ||||
| 	int i; | ||||
| 
 | ||||
| 	puts(""); | ||||
| 	printf("usage: %s [-h] [-i iterations] [-I interval] " | ||||
| 	       "[-p offset] [-m mode]\n", name); | ||||
| @ -813,51 +806,23 @@ static void help(char *name) | ||||
| 	printf(" -M: specify the host logging mode " | ||||
| 	       "(default: run all log modes).  Supported modes: \n\t"); | ||||
| 	log_modes_dump(); | ||||
| 	printf(" -m: specify the guest mode ID to test " | ||||
| 	       "(default: test all supported modes)\n" | ||||
| 	       "     This option may be used multiple times.\n" | ||||
| 	       "     Guest mode IDs:\n"); | ||||
| 	for (i = 0; i < NUM_VM_MODES; ++i) { | ||||
| 		printf("         %d:    %s%s\n", i, vm_guest_mode_string(i), | ||||
| 		       guest_modes[i].supported ? " (supported)" : ""); | ||||
| 	} | ||||
| 	guest_modes_help(); | ||||
| 	puts(""); | ||||
| 	exit(0); | ||||
| } | ||||
| 
 | ||||
| int main(int argc, char *argv[]) | ||||
| { | ||||
| 	unsigned long iterations = TEST_HOST_LOOP_N; | ||||
| 	unsigned long interval = TEST_HOST_LOOP_INTERVAL; | ||||
| 	bool mode_selected = false; | ||||
| 	uint64_t phys_offset = 0; | ||||
| 	unsigned int mode; | ||||
| 	int opt, i, j; | ||||
| 	struct test_params p = { | ||||
| 		.iterations = TEST_HOST_LOOP_N, | ||||
| 		.interval = TEST_HOST_LOOP_INTERVAL, | ||||
| 	}; | ||||
| 	int opt, i; | ||||
| 
 | ||||
| 	sem_init(&dirty_ring_vcpu_stop, 0, 0); | ||||
| 	sem_init(&dirty_ring_vcpu_cont, 0, 0); | ||||
| 
 | ||||
| #ifdef __x86_64__ | ||||
| 	guest_mode_init(VM_MODE_PXXV48_4K, true, true); | ||||
| #endif | ||||
| #ifdef __aarch64__ | ||||
| 	guest_mode_init(VM_MODE_P40V48_4K, true, true); | ||||
| 	guest_mode_init(VM_MODE_P40V48_64K, true, true); | ||||
| 
 | ||||
| 	{ | ||||
| 		unsigned int limit = kvm_check_cap(KVM_CAP_ARM_VM_IPA_SIZE); | ||||
| 
 | ||||
| 		if (limit >= 52) | ||||
| 			guest_mode_init(VM_MODE_P52V48_64K, true, true); | ||||
| 		if (limit >= 48) { | ||||
| 			guest_mode_init(VM_MODE_P48V48_4K, true, true); | ||||
| 			guest_mode_init(VM_MODE_P48V48_64K, true, true); | ||||
| 		} | ||||
| 	} | ||||
| #endif | ||||
| #ifdef __s390x__ | ||||
| 	guest_mode_init(VM_MODE_P40V48_4K, true, true); | ||||
| #endif | ||||
| 	guest_modes_append_default(); | ||||
| 
 | ||||
| 	while ((opt = getopt(argc, argv, "c:hi:I:p:m:M:")) != -1) { | ||||
| 		switch (opt) { | ||||
| @ -865,24 +830,16 @@ int main(int argc, char *argv[]) | ||||
| 			test_dirty_ring_count = strtol(optarg, NULL, 10); | ||||
| 			break; | ||||
| 		case 'i': | ||||
| 			iterations = strtol(optarg, NULL, 10); | ||||
| 			p.iterations = strtol(optarg, NULL, 10); | ||||
| 			break; | ||||
| 		case 'I': | ||||
| 			interval = strtol(optarg, NULL, 10); | ||||
| 			p.interval = strtol(optarg, NULL, 10); | ||||
| 			break; | ||||
| 		case 'p': | ||||
| 			phys_offset = strtoull(optarg, NULL, 0); | ||||
| 			p.phys_offset = strtoull(optarg, NULL, 0); | ||||
| 			break; | ||||
| 		case 'm': | ||||
| 			if (!mode_selected) { | ||||
| 				for (i = 0; i < NUM_VM_MODES; ++i) | ||||
| 					guest_modes[i].enabled = false; | ||||
| 				mode_selected = true; | ||||
| 			} | ||||
| 			mode = strtoul(optarg, NULL, 10); | ||||
| 			TEST_ASSERT(mode < NUM_VM_MODES, | ||||
| 				    "Guest mode ID %d too big", mode); | ||||
| 			guest_modes[mode].enabled = true; | ||||
| 			guest_modes_cmdline(optarg); | ||||
| 			break; | ||||
| 		case 'M': | ||||
| 			if (!strcmp(optarg, "all")) { | ||||
| @ -911,32 +868,24 @@ int main(int argc, char *argv[]) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	TEST_ASSERT(iterations > 2, "Iterations must be greater than two"); | ||||
| 	TEST_ASSERT(interval > 0, "Interval must be greater than zero"); | ||||
| 	TEST_ASSERT(p.iterations > 2, "Iterations must be greater than two"); | ||||
| 	TEST_ASSERT(p.interval > 0, "Interval must be greater than zero"); | ||||
| 
 | ||||
| 	pr_info("Test iterations: %"PRIu64", interval: %"PRIu64" (ms)\n", | ||||
| 		iterations, interval); | ||||
| 		p.iterations, p.interval); | ||||
| 
 | ||||
| 	srandom(time(0)); | ||||
| 
 | ||||
| 	for (i = 0; i < NUM_VM_MODES; ++i) { | ||||
| 		if (!guest_modes[i].enabled) | ||||
| 			continue; | ||||
| 		TEST_ASSERT(guest_modes[i].supported, | ||||
| 			    "Guest mode ID %d (%s) not supported.", | ||||
| 			    i, vm_guest_mode_string(i)); | ||||
| 	if (host_log_mode_option == LOG_MODE_ALL) { | ||||
| 		/* Run each log mode */ | ||||
| 			for (j = 0; j < LOG_MODE_NUM; j++) { | ||||
| 				pr_info("Testing Log Mode '%s'\n", | ||||
| 					log_modes[j].name); | ||||
| 				host_log_mode = j; | ||||
| 				run_test(i, iterations, interval, phys_offset); | ||||
| 		for (i = 0; i < LOG_MODE_NUM; i++) { | ||||
| 			pr_info("Testing Log Mode '%s'\n", log_modes[i].name); | ||||
| 			host_log_mode = i; | ||||
| 			for_each_guest_mode(run_test, &p); | ||||
| 		} | ||||
| 	} else { | ||||
| 		host_log_mode = host_log_mode_option; | ||||
| 			run_test(i, iterations, interval, phys_offset); | ||||
| 		} | ||||
| 		for_each_guest_mode(run_test, &p); | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
|  | ||||
							
								
								
									
										21
									
								
								tools/testing/selftests/kvm/include/guest_modes.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								tools/testing/selftests/kvm/include/guest_modes.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,21 @@ | ||||
| /* SPDX-License-Identifier: GPL-2.0 */ | ||||
| /*
 | ||||
|  * Copyright (C) 2020, Red Hat, Inc. | ||||
|  */ | ||||
| #include "kvm_util.h" | ||||
| 
 | ||||
| struct guest_mode { | ||||
| 	bool supported; | ||||
| 	bool enabled; | ||||
| }; | ||||
| 
 | ||||
| extern struct guest_mode guest_modes[NUM_VM_MODES]; | ||||
| 
 | ||||
| #define guest_mode_append(mode, supported, enabled) ({ \ | ||||
| 	guest_modes[mode] = (struct guest_mode){ supported, enabled }; \ | ||||
| }) | ||||
| 
 | ||||
| void guest_modes_append_default(void); | ||||
| void for_each_guest_mode(void (*func)(enum vm_guest_mode, void *), void *arg); | ||||
| void guest_modes_help(void); | ||||
| void guest_modes_cmdline(const char *arg); | ||||
| @ -16,6 +16,7 @@ | ||||
| 
 | ||||
| #include "sparsebit.h" | ||||
| 
 | ||||
| #define KVM_MAX_VCPUS 512 | ||||
| 
 | ||||
| /*
 | ||||
|  * Callers of kvm_util only have an incomplete/opaque description of the | ||||
| @ -70,6 +71,14 @@ enum vm_guest_mode { | ||||
| #define vm_guest_mode_string(m) vm_guest_mode_string[m] | ||||
| extern const char * const vm_guest_mode_string[]; | ||||
| 
 | ||||
| struct vm_guest_mode_params { | ||||
| 	unsigned int pa_bits; | ||||
| 	unsigned int va_bits; | ||||
| 	unsigned int page_size; | ||||
| 	unsigned int page_shift; | ||||
| }; | ||||
| extern const struct vm_guest_mode_params vm_guest_mode_params[]; | ||||
| 
 | ||||
| enum vm_mem_backing_src_type { | ||||
| 	VM_MEM_SRC_ANONYMOUS, | ||||
| 	VM_MEM_SRC_ANONYMOUS_THP, | ||||
|  | ||||
| @ -9,38 +9,15 @@ | ||||
| #define SELFTEST_KVM_PERF_TEST_UTIL_H | ||||
| 
 | ||||
| #include "kvm_util.h" | ||||
| #include "processor.h" | ||||
| 
 | ||||
| #define MAX_VCPUS 512 | ||||
| 
 | ||||
| #define PAGE_SHIFT_4K  12 | ||||
| #define PTES_PER_4K_PT 512 | ||||
| 
 | ||||
| #define TEST_MEM_SLOT_INDEX		1 | ||||
| 
 | ||||
| /* Default guest test virtual memory offset */ | ||||
| #define DEFAULT_GUEST_TEST_MEM		0xc0000000 | ||||
| 
 | ||||
| #define DEFAULT_PER_VCPU_MEM_SIZE	(1 << 30) /* 1G */ | ||||
| 
 | ||||
| /*
 | ||||
|  * Guest physical memory offset of the testing memory slot. | ||||
|  * This will be set to the topmost valid physical address minus | ||||
|  * the test memory size. | ||||
|  */ | ||||
| static uint64_t guest_test_phys_mem; | ||||
| #define PERF_TEST_MEM_SLOT_INDEX	1 | ||||
| 
 | ||||
| /*
 | ||||
|  * Guest virtual memory offset of the testing memory slot. | ||||
|  * Must not conflict with identity mapped test code. | ||||
|  */ | ||||
| static uint64_t guest_test_virt_mem = DEFAULT_GUEST_TEST_MEM; | ||||
| static uint64_t guest_percpu_mem_size = DEFAULT_PER_VCPU_MEM_SIZE; | ||||
| 
 | ||||
| /* Number of VCPUs for the test */ | ||||
| static int nr_vcpus = 1; | ||||
| 
 | ||||
| struct vcpu_args { | ||||
| struct perf_test_vcpu_args { | ||||
| 	uint64_t gva; | ||||
| 	uint64_t pages; | ||||
| 
 | ||||
| @ -54,141 +31,21 @@ struct perf_test_args { | ||||
| 	uint64_t guest_page_size; | ||||
| 	int wr_fract; | ||||
| 
 | ||||
| 	struct vcpu_args vcpu_args[MAX_VCPUS]; | ||||
| 	struct perf_test_vcpu_args vcpu_args[KVM_MAX_VCPUS]; | ||||
| }; | ||||
| 
 | ||||
| static struct perf_test_args perf_test_args; | ||||
| extern struct perf_test_args perf_test_args; | ||||
| 
 | ||||
| /*
 | ||||
|  * Continuously write to the first 8 bytes of each page in the | ||||
|  * specified region. | ||||
|  * Guest physical memory offset of the testing memory slot. | ||||
|  * This will be set to the topmost valid physical address minus | ||||
|  * the test memory size. | ||||
|  */ | ||||
| static void guest_code(uint32_t vcpu_id) | ||||
| { | ||||
| 	struct vcpu_args *vcpu_args = &perf_test_args.vcpu_args[vcpu_id]; | ||||
| 	uint64_t gva; | ||||
| 	uint64_t pages; | ||||
| 	int i; | ||||
| extern uint64_t guest_test_phys_mem; | ||||
| 
 | ||||
| 	/* Make sure vCPU args data structure is not corrupt. */ | ||||
| 	GUEST_ASSERT(vcpu_args->vcpu_id == vcpu_id); | ||||
| 
 | ||||
| 	gva = vcpu_args->gva; | ||||
| 	pages = vcpu_args->pages; | ||||
| 
 | ||||
| 	while (true) { | ||||
| 		for (i = 0; i < pages; i++) { | ||||
| 			uint64_t addr = gva + (i * perf_test_args.guest_page_size); | ||||
| 
 | ||||
| 			if (i % perf_test_args.wr_fract == 0) | ||||
| 				*(uint64_t *)addr = 0x0123456789ABCDEF; | ||||
| 			else | ||||
| 				READ_ONCE(*(uint64_t *)addr); | ||||
| 		} | ||||
| 
 | ||||
| 		GUEST_SYNC(1); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static struct kvm_vm *create_vm(enum vm_guest_mode mode, int vcpus, | ||||
| 				uint64_t vcpu_memory_bytes) | ||||
| { | ||||
| 	struct kvm_vm *vm; | ||||
| 	uint64_t pages = DEFAULT_GUEST_PHY_PAGES; | ||||
| 	uint64_t guest_num_pages; | ||||
| 
 | ||||
| 	/* Account for a few pages per-vCPU for stacks */ | ||||
| 	pages += DEFAULT_STACK_PGS * vcpus; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Reserve twice the ammount of memory needed to map the test region and | ||||
| 	 * the page table / stacks region, at 4k, for page tables. Do the | ||||
| 	 * calculation with 4K page size: the smallest of all archs. (e.g., 64K | ||||
| 	 * page size guest will need even less memory for page tables). | ||||
| 	 */ | ||||
| 	pages += (2 * pages) / PTES_PER_4K_PT; | ||||
| 	pages += ((2 * vcpus * vcpu_memory_bytes) >> PAGE_SHIFT_4K) / | ||||
| 		 PTES_PER_4K_PT; | ||||
| 	pages = vm_adjust_num_guest_pages(mode, pages); | ||||
| 
 | ||||
| 	pr_info("Testing guest mode: %s\n", vm_guest_mode_string(mode)); | ||||
| 
 | ||||
| 	vm = vm_create(mode, pages, O_RDWR); | ||||
| 	kvm_vm_elf_load(vm, program_invocation_name, 0, 0); | ||||
| #ifdef __x86_64__ | ||||
| 	vm_create_irqchip(vm); | ||||
| #endif | ||||
| 
 | ||||
| 	perf_test_args.vm = vm; | ||||
| 	perf_test_args.guest_page_size = vm_get_page_size(vm); | ||||
| 	perf_test_args.host_page_size = getpagesize(); | ||||
| 
 | ||||
| 	TEST_ASSERT(vcpu_memory_bytes % perf_test_args.guest_page_size == 0, | ||||
| 		    "Guest memory size is not guest page size aligned."); | ||||
| 
 | ||||
| 	guest_num_pages = (vcpus * vcpu_memory_bytes) / | ||||
| 			  perf_test_args.guest_page_size; | ||||
| 	guest_num_pages = vm_adjust_num_guest_pages(mode, guest_num_pages); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * If there should be more memory in the guest test region than there | ||||
| 	 * can be pages in the guest, it will definitely cause problems. | ||||
| 	 */ | ||||
| 	TEST_ASSERT(guest_num_pages < vm_get_max_gfn(vm), | ||||
| 		    "Requested more guest memory than address space allows.\n" | ||||
| 		    "    guest pages: %lx max gfn: %x vcpus: %d wss: %lx]\n", | ||||
| 		    guest_num_pages, vm_get_max_gfn(vm), vcpus, | ||||
| 		    vcpu_memory_bytes); | ||||
| 
 | ||||
| 	TEST_ASSERT(vcpu_memory_bytes % perf_test_args.host_page_size == 0, | ||||
| 		    "Guest memory size is not host page size aligned."); | ||||
| 
 | ||||
| 	guest_test_phys_mem = (vm_get_max_gfn(vm) - guest_num_pages) * | ||||
| 			      perf_test_args.guest_page_size; | ||||
| 	guest_test_phys_mem &= ~(perf_test_args.host_page_size - 1); | ||||
| 
 | ||||
| #ifdef __s390x__ | ||||
| 	/* Align to 1M (segment size) */ | ||||
| 	guest_test_phys_mem &= ~((1 << 20) - 1); | ||||
| #endif | ||||
| 
 | ||||
| 	pr_info("guest physical test memory offset: 0x%lx\n", guest_test_phys_mem); | ||||
| 
 | ||||
| 	/* Add an extra memory slot for testing */ | ||||
| 	vm_userspace_mem_region_add(vm, VM_MEM_SRC_ANONYMOUS, | ||||
| 				    guest_test_phys_mem, | ||||
| 				    TEST_MEM_SLOT_INDEX, | ||||
| 				    guest_num_pages, 0); | ||||
| 
 | ||||
| 	/* Do mapping for the demand paging memory slot */ | ||||
| 	virt_map(vm, guest_test_virt_mem, guest_test_phys_mem, guest_num_pages, 0); | ||||
| 
 | ||||
| 	ucall_init(vm, NULL); | ||||
| 
 | ||||
| 	return vm; | ||||
| } | ||||
| 
 | ||||
| static void add_vcpus(struct kvm_vm *vm, int vcpus, uint64_t vcpu_memory_bytes) | ||||
| { | ||||
| 	vm_paddr_t vcpu_gpa; | ||||
| 	struct vcpu_args *vcpu_args; | ||||
| 	int vcpu_id; | ||||
| 
 | ||||
| 	for (vcpu_id = 0; vcpu_id < vcpus; vcpu_id++) { | ||||
| 		vcpu_args = &perf_test_args.vcpu_args[vcpu_id]; | ||||
| 
 | ||||
| 		vm_vcpu_add_default(vm, vcpu_id, guest_code); | ||||
| 
 | ||||
| 		vcpu_args->vcpu_id = vcpu_id; | ||||
| 		vcpu_args->gva = guest_test_virt_mem + | ||||
| 				 (vcpu_id * vcpu_memory_bytes); | ||||
| 		vcpu_args->pages = vcpu_memory_bytes / | ||||
| 				   perf_test_args.guest_page_size; | ||||
| 
 | ||||
| 		vcpu_gpa = guest_test_phys_mem + (vcpu_id * vcpu_memory_bytes); | ||||
| 		pr_debug("Added VCPU %d with test mem gpa [%lx, %lx)\n", | ||||
| 			 vcpu_id, vcpu_gpa, vcpu_gpa + vcpu_memory_bytes); | ||||
| 	} | ||||
| } | ||||
| struct kvm_vm *perf_test_create_vm(enum vm_guest_mode mode, int vcpus, | ||||
| 				uint64_t vcpu_memory_bytes); | ||||
| void perf_test_destroy_vm(struct kvm_vm *vm); | ||||
| void perf_test_setup_vcpus(struct kvm_vm *vm, int vcpus, uint64_t vcpu_memory_bytes); | ||||
| 
 | ||||
| #endif /* SELFTEST_KVM_PERF_TEST_UTIL_H */ | ||||
|  | ||||
							
								
								
									
										70
									
								
								tools/testing/selftests/kvm/lib/guest_modes.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										70
									
								
								tools/testing/selftests/kvm/lib/guest_modes.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,70 @@ | ||||
| // SPDX-License-Identifier: GPL-2.0
 | ||||
| /*
 | ||||
|  * Copyright (C) 2020, Red Hat, Inc. | ||||
|  */ | ||||
| #include "guest_modes.h" | ||||
| 
 | ||||
| struct guest_mode guest_modes[NUM_VM_MODES]; | ||||
| 
 | ||||
| void guest_modes_append_default(void) | ||||
| { | ||||
| 	guest_mode_append(VM_MODE_DEFAULT, true, true); | ||||
| 
 | ||||
| #ifdef __aarch64__ | ||||
| 	guest_mode_append(VM_MODE_P40V48_64K, true, true); | ||||
| 	{ | ||||
| 		unsigned int limit = kvm_check_cap(KVM_CAP_ARM_VM_IPA_SIZE); | ||||
| 		if (limit >= 52) | ||||
| 			guest_mode_append(VM_MODE_P52V48_64K, true, true); | ||||
| 		if (limit >= 48) { | ||||
| 			guest_mode_append(VM_MODE_P48V48_4K, true, true); | ||||
| 			guest_mode_append(VM_MODE_P48V48_64K, true, true); | ||||
| 		} | ||||
| 	} | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| void for_each_guest_mode(void (*func)(enum vm_guest_mode, void *), void *arg) | ||||
| { | ||||
| 	int i; | ||||
| 
 | ||||
| 	for (i = 0; i < NUM_VM_MODES; ++i) { | ||||
| 		if (!guest_modes[i].enabled) | ||||
| 			continue; | ||||
| 		TEST_ASSERT(guest_modes[i].supported, | ||||
| 			    "Guest mode ID %d (%s) not supported.", | ||||
| 			    i, vm_guest_mode_string(i)); | ||||
| 		func(i, arg); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| void guest_modes_help(void) | ||||
| { | ||||
| 	int i; | ||||
| 
 | ||||
| 	printf(" -m: specify the guest mode ID to test\n" | ||||
| 	       "     (default: test all supported modes)\n" | ||||
| 	       "     This option may be used multiple times.\n" | ||||
| 	       "     Guest mode IDs:\n"); | ||||
| 	for (i = 0; i < NUM_VM_MODES; ++i) { | ||||
| 		printf("         %d:    %s%s\n", i, vm_guest_mode_string(i), | ||||
| 		       guest_modes[i].supported ? " (supported)" : ""); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| void guest_modes_cmdline(const char *arg) | ||||
| { | ||||
| 	static bool mode_selected; | ||||
| 	unsigned int mode; | ||||
| 	int i; | ||||
| 
 | ||||
| 	if (!mode_selected) { | ||||
| 		for (i = 0; i < NUM_VM_MODES; ++i) | ||||
| 			guest_modes[i].enabled = false; | ||||
| 		mode_selected = true; | ||||
| 	} | ||||
| 
 | ||||
| 	mode = strtoul(optarg, NULL, 10); | ||||
| 	TEST_ASSERT(mode < NUM_VM_MODES, "Guest mode ID %d too big", mode); | ||||
| 	guest_modes[mode].enabled = true; | ||||
| } | ||||
| @ -153,14 +153,7 @@ const char * const vm_guest_mode_string[] = { | ||||
| _Static_assert(sizeof(vm_guest_mode_string)/sizeof(char *) == NUM_VM_MODES, | ||||
| 	       "Missing new mode strings?"); | ||||
| 
 | ||||
| struct vm_guest_mode_params { | ||||
| 	unsigned int pa_bits; | ||||
| 	unsigned int va_bits; | ||||
| 	unsigned int page_size; | ||||
| 	unsigned int page_shift; | ||||
| }; | ||||
| 
 | ||||
| static const struct vm_guest_mode_params vm_guest_mode_params[] = { | ||||
| const struct vm_guest_mode_params vm_guest_mode_params[] = { | ||||
| 	{ 52, 48,  0x1000, 12 }, | ||||
| 	{ 52, 48, 0x10000, 16 }, | ||||
| 	{ 48, 48,  0x1000, 12 }, | ||||
|  | ||||
							
								
								
									
										134
									
								
								tools/testing/selftests/kvm/lib/perf_test_util.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										134
									
								
								tools/testing/selftests/kvm/lib/perf_test_util.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,134 @@ | ||||
| // SPDX-License-Identifier: GPL-2.0
 | ||||
| /*
 | ||||
|  * Copyright (C) 2020, Google LLC. | ||||
|  */ | ||||
| 
 | ||||
| #include "kvm_util.h" | ||||
| #include "perf_test_util.h" | ||||
| #include "processor.h" | ||||
| 
 | ||||
| struct perf_test_args perf_test_args; | ||||
| 
 | ||||
| uint64_t guest_test_phys_mem; | ||||
| 
 | ||||
| /*
 | ||||
|  * Guest virtual memory offset of the testing memory slot. | ||||
|  * Must not conflict with identity mapped test code. | ||||
|  */ | ||||
| static uint64_t guest_test_virt_mem = DEFAULT_GUEST_TEST_MEM; | ||||
| 
 | ||||
| /*
 | ||||
|  * Continuously write to the first 8 bytes of each page in the | ||||
|  * specified region. | ||||
|  */ | ||||
| static void guest_code(uint32_t vcpu_id) | ||||
| { | ||||
| 	struct perf_test_vcpu_args *vcpu_args = &perf_test_args.vcpu_args[vcpu_id]; | ||||
| 	uint64_t gva; | ||||
| 	uint64_t pages; | ||||
| 	int i; | ||||
| 
 | ||||
| 	/* Make sure vCPU args data structure is not corrupt. */ | ||||
| 	GUEST_ASSERT(vcpu_args->vcpu_id == vcpu_id); | ||||
| 
 | ||||
| 	gva = vcpu_args->gva; | ||||
| 	pages = vcpu_args->pages; | ||||
| 
 | ||||
| 	while (true) { | ||||
| 		for (i = 0; i < pages; i++) { | ||||
| 			uint64_t addr = gva + (i * perf_test_args.guest_page_size); | ||||
| 
 | ||||
| 			if (i % perf_test_args.wr_fract == 0) | ||||
| 				*(uint64_t *)addr = 0x0123456789ABCDEF; | ||||
| 			else | ||||
| 				READ_ONCE(*(uint64_t *)addr); | ||||
| 		} | ||||
| 
 | ||||
| 		GUEST_SYNC(1); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| struct kvm_vm *perf_test_create_vm(enum vm_guest_mode mode, int vcpus, | ||||
| 				   uint64_t vcpu_memory_bytes) | ||||
| { | ||||
| 	struct kvm_vm *vm; | ||||
| 	uint64_t guest_num_pages; | ||||
| 
 | ||||
| 	pr_info("Testing guest mode: %s\n", vm_guest_mode_string(mode)); | ||||
| 
 | ||||
| 	perf_test_args.host_page_size = getpagesize(); | ||||
| 	perf_test_args.guest_page_size = vm_guest_mode_params[mode].page_size; | ||||
| 
 | ||||
| 	guest_num_pages = vm_adjust_num_guest_pages(mode, | ||||
| 				(vcpus * vcpu_memory_bytes) / perf_test_args.guest_page_size); | ||||
| 
 | ||||
| 	TEST_ASSERT(vcpu_memory_bytes % perf_test_args.host_page_size == 0, | ||||
| 		    "Guest memory size is not host page size aligned."); | ||||
| 	TEST_ASSERT(vcpu_memory_bytes % perf_test_args.guest_page_size == 0, | ||||
| 		    "Guest memory size is not guest page size aligned."); | ||||
| 
 | ||||
| 	vm = vm_create_with_vcpus(mode, vcpus, | ||||
| 				  (vcpus * vcpu_memory_bytes) / perf_test_args.guest_page_size, | ||||
| 				  0, guest_code, NULL); | ||||
| 
 | ||||
| 	perf_test_args.vm = vm; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * If there should be more memory in the guest test region than there | ||||
| 	 * can be pages in the guest, it will definitely cause problems. | ||||
| 	 */ | ||||
| 	TEST_ASSERT(guest_num_pages < vm_get_max_gfn(vm), | ||||
| 		    "Requested more guest memory than address space allows.\n" | ||||
| 		    "    guest pages: %lx max gfn: %x vcpus: %d wss: %lx]\n", | ||||
| 		    guest_num_pages, vm_get_max_gfn(vm), vcpus, | ||||
| 		    vcpu_memory_bytes); | ||||
| 
 | ||||
| 	guest_test_phys_mem = (vm_get_max_gfn(vm) - guest_num_pages) * | ||||
| 			      perf_test_args.guest_page_size; | ||||
| 	guest_test_phys_mem &= ~(perf_test_args.host_page_size - 1); | ||||
| #ifdef __s390x__ | ||||
| 	/* Align to 1M (segment size) */ | ||||
| 	guest_test_phys_mem &= ~((1 << 20) - 1); | ||||
| #endif | ||||
| 	pr_info("guest physical test memory offset: 0x%lx\n", guest_test_phys_mem); | ||||
| 
 | ||||
| 	/* Add an extra memory slot for testing */ | ||||
| 	vm_userspace_mem_region_add(vm, VM_MEM_SRC_ANONYMOUS, | ||||
| 				    guest_test_phys_mem, | ||||
| 				    PERF_TEST_MEM_SLOT_INDEX, | ||||
| 				    guest_num_pages, 0); | ||||
| 
 | ||||
| 	/* Do mapping for the demand paging memory slot */ | ||||
| 	virt_map(vm, guest_test_virt_mem, guest_test_phys_mem, guest_num_pages, 0); | ||||
| 
 | ||||
| 	ucall_init(vm, NULL); | ||||
| 
 | ||||
| 	return vm; | ||||
| } | ||||
| 
 | ||||
| void perf_test_destroy_vm(struct kvm_vm *vm) | ||||
| { | ||||
| 	ucall_uninit(vm); | ||||
| 	kvm_vm_free(vm); | ||||
| } | ||||
| 
 | ||||
| void perf_test_setup_vcpus(struct kvm_vm *vm, int vcpus, uint64_t vcpu_memory_bytes) | ||||
| { | ||||
| 	vm_paddr_t vcpu_gpa; | ||||
| 	struct perf_test_vcpu_args *vcpu_args; | ||||
| 	int vcpu_id; | ||||
| 
 | ||||
| 	for (vcpu_id = 0; vcpu_id < vcpus; vcpu_id++) { | ||||
| 		vcpu_args = &perf_test_args.vcpu_args[vcpu_id]; | ||||
| 
 | ||||
| 		vcpu_args->vcpu_id = vcpu_id; | ||||
| 		vcpu_args->gva = guest_test_virt_mem + | ||||
| 				 (vcpu_id * vcpu_memory_bytes); | ||||
| 		vcpu_args->pages = vcpu_memory_bytes / | ||||
| 				   perf_test_args.guest_page_size; | ||||
| 
 | ||||
| 		vcpu_gpa = guest_test_phys_mem + (vcpu_id * vcpu_memory_bytes); | ||||
| 		pr_debug("Added VCPU %d with test mem gpa [%lx, %lx)\n", | ||||
| 			 vcpu_id, vcpu_gpa, vcpu_gpa + vcpu_memory_bytes); | ||||
| 	} | ||||
| } | ||||
| @ -485,9 +485,8 @@ static int kvm_mmu_notifier_invalidate_range_start(struct mmu_notifier *mn, | ||||
| 	kvm->mmu_notifier_count++; | ||||
| 	need_tlb_flush = kvm_unmap_hva_range(kvm, range->start, range->end, | ||||
| 					     range->flags); | ||||
| 	need_tlb_flush |= kvm->tlbs_dirty; | ||||
| 	/* we've to flush the tlb before the pages can be freed */ | ||||
| 	if (need_tlb_flush) | ||||
| 	if (need_tlb_flush || kvm->tlbs_dirty) | ||||
| 		kvm_flush_remote_tlbs(kvm); | ||||
| 
 | ||||
| 	spin_unlock(&kvm->mmu_lock); | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user