Commit a44e2c20 authored by Paolo Bonzini's avatar Paolo Bonzini
Browse files

Merge tag 'kvmarm-fixes-5.18-1' of...

Merge tag 'kvmarm-fixes-5.18-1' of git://git.kernel.org/pub/scm/linux/kernel/git/kvmarm/kvmarm into HEAD

KVM/arm64 fixes for 5.18, take #1

- Some PSCI fixes after introducing PSCIv1.1 and SYSTEM_RESET2

- Fix the MMU write-lock not being taken on THP split

- Fix mixed-width VM handling

- Fix potential UAF when debugfs registration fails

- Various selftest updates for all of the above
parents 5593473a 21db8384
Loading
Loading
Loading
Loading
+19 −8
Original line number Diff line number Diff line
@@ -43,10 +43,22 @@ void kvm_inject_pabt(struct kvm_vcpu *vcpu, unsigned long addr);

void kvm_vcpu_wfi(struct kvm_vcpu *vcpu);

#if defined(__KVM_VHE_HYPERVISOR__) || defined(__KVM_NVHE_HYPERVISOR__)
static __always_inline bool vcpu_el1_is_32bit(struct kvm_vcpu *vcpu)
{
	return !(vcpu->arch.hcr_el2 & HCR_RW);
}
#else
static __always_inline bool vcpu_el1_is_32bit(struct kvm_vcpu *vcpu)
{
	struct kvm *kvm = vcpu->kvm;

	WARN_ON_ONCE(!test_bit(KVM_ARCH_FLAG_REG_WIDTH_CONFIGURED,
			       &kvm->arch.flags));

	return test_bit(KVM_ARCH_FLAG_EL1_32BIT, &kvm->arch.flags);
}
#endif

static inline void vcpu_reset_hcr(struct kvm_vcpu *vcpu)
{
@@ -72,15 +84,14 @@ static inline void vcpu_reset_hcr(struct kvm_vcpu *vcpu)
		vcpu->arch.hcr_el2 |= HCR_TVM;
	}

	if (test_bit(KVM_ARM_VCPU_EL1_32BIT, vcpu->arch.features))
	if (vcpu_el1_is_32bit(vcpu))
		vcpu->arch.hcr_el2 &= ~HCR_RW;

	else
		/*
		 * TID3: trap feature register accesses that we virtualise.
		 * For now this is conditional, since no AArch32 feature regs
		 * are currently virtualised.
		 */
	if (!vcpu_el1_is_32bit(vcpu))
		vcpu->arch.hcr_el2 |= HCR_TID3;

	if (cpus_have_const_cap(ARM64_MISMATCHED_CACHE_TYPE) ||
+10 −0
Original line number Diff line number Diff line
@@ -127,6 +127,16 @@ struct kvm_arch {
#define KVM_ARCH_FLAG_MTE_ENABLED			1
	/* At least one vCPU has ran in the VM */
#define KVM_ARCH_FLAG_HAS_RAN_ONCE			2
	/*
	 * The following two bits are used to indicate the guest's EL1
	 * register width configuration. A value of KVM_ARCH_FLAG_EL1_32BIT
	 * bit is valid only when KVM_ARCH_FLAG_REG_WIDTH_CONFIGURED is set.
	 * Otherwise, the guest's EL1 register width has not yet been
	 * determined yet.
	 */
#define KVM_ARCH_FLAG_REG_WIDTH_CONFIGURED		3
#define KVM_ARCH_FLAG_EL1_32BIT				4

	unsigned long flags;

	/*
+7 −4
Original line number Diff line number Diff line
@@ -1079,7 +1079,7 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
	gfn_t gfn;
	kvm_pfn_t pfn;
	bool logging_active = memslot_is_logging(memslot);
	bool logging_perm_fault = false;
	bool use_read_lock = false;
	unsigned long fault_level = kvm_vcpu_trap_get_fault_level(vcpu);
	unsigned long vma_pagesize, fault_granule;
	enum kvm_pgtable_prot prot = KVM_PGTABLE_PROT_R;
@@ -1114,7 +1114,8 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
	if (logging_active) {
		force_pte = true;
		vma_shift = PAGE_SHIFT;
		logging_perm_fault = (fault_status == FSC_PERM && write_fault);
		use_read_lock = (fault_status == FSC_PERM && write_fault &&
				 fault_granule == PAGE_SIZE);
	} else {
		vma_shift = get_vma_page_shift(vma, hva);
	}
@@ -1218,7 +1219,7 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
	 * logging dirty logging, only acquire read lock for permission
	 * relaxation.
	 */
	if (logging_perm_fault)
	if (use_read_lock)
		read_lock(&kvm->mmu_lock);
	else
		write_lock(&kvm->mmu_lock);
@@ -1268,6 +1269,8 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
	if (fault_status == FSC_PERM && vma_pagesize == fault_granule) {
		ret = kvm_pgtable_stage2_relax_perms(pgt, fault_ipa, prot);
	} else {
		WARN_ONCE(use_read_lock, "Attempted stage-2 map outside of write lock\n");

		ret = kvm_pgtable_stage2_map(pgt, fault_ipa, vma_pagesize,
					     __pfn_to_phys(pfn), prot,
					     memcache);
@@ -1280,7 +1283,7 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
	}

out_unlock:
	if (logging_perm_fault)
	if (use_read_lock)
		read_unlock(&kvm->mmu_lock);
	else
		write_unlock(&kvm->mmu_lock);
+14 −17
Original line number Diff line number Diff line
@@ -215,15 +215,11 @@ static void kvm_psci_narrow_to_32bit(struct kvm_vcpu *vcpu)

static unsigned long kvm_psci_check_allowed_function(struct kvm_vcpu *vcpu, u32 fn)
{
	switch(fn) {
	case PSCI_0_2_FN64_CPU_SUSPEND:
	case PSCI_0_2_FN64_CPU_ON:
	case PSCI_0_2_FN64_AFFINITY_INFO:
		/* Disallow these functions for 32bit guests */
		if (vcpu_mode_is_32bit(vcpu))
	/*
	 * Prevent 32 bit guests from calling 64 bit PSCI functions.
	 */
	if ((fn & PSCI_0_2_64BIT) && vcpu_mode_is_32bit(vcpu))
		return PSCI_RET_NOT_SUPPORTED;
		break;
	}

	return 0;
}
@@ -235,10 +231,6 @@ static int kvm_psci_0_2_call(struct kvm_vcpu *vcpu)
	unsigned long val;
	int ret = 1;

	val = kvm_psci_check_allowed_function(vcpu, psci_fn);
	if (val)
		goto out;

	switch (psci_fn) {
	case PSCI_0_2_FN_PSCI_VERSION:
		/*
@@ -306,7 +298,6 @@ static int kvm_psci_0_2_call(struct kvm_vcpu *vcpu)
		break;
	}

out:
	smccc_set_retval(vcpu, val, 0, 0, 0);
	return ret;
}
@@ -318,9 +309,6 @@ static int kvm_psci_1_x_call(struct kvm_vcpu *vcpu, u32 minor)
	unsigned long val;
	int ret = 1;

	if (minor > 1)
		return -EINVAL;

	switch(psci_fn) {
	case PSCI_0_2_FN_PSCI_VERSION:
		val = minor == 0 ? KVM_ARM_PSCI_1_0 : KVM_ARM_PSCI_1_1;
@@ -426,6 +414,15 @@ static int kvm_psci_0_1_call(struct kvm_vcpu *vcpu)
 */
int kvm_psci_call(struct kvm_vcpu *vcpu)
{
	u32 psci_fn = smccc_get_function(vcpu);
	unsigned long val;

	val = kvm_psci_check_allowed_function(vcpu, psci_fn);
	if (val) {
		smccc_set_retval(vcpu, val, 0, 0, 0);
		return 1;
	}

	switch (kvm_psci_version(vcpu)) {
	case KVM_ARM_PSCI_1_1:
		return kvm_psci_1_x_call(vcpu, 1);
+45 −20
Original line number Diff line number Diff line
@@ -181,27 +181,51 @@ static int kvm_vcpu_enable_ptrauth(struct kvm_vcpu *vcpu)
	return 0;
}

static bool vcpu_allowed_register_width(struct kvm_vcpu *vcpu)
/**
 * kvm_set_vm_width() - set the register width for the guest
 * @vcpu: Pointer to the vcpu being configured
 *
 * Set both KVM_ARCH_FLAG_EL1_32BIT and KVM_ARCH_FLAG_REG_WIDTH_CONFIGURED
 * in the VM flags based on the vcpu's requested register width, the HW
 * capabilities and other options (such as MTE).
 * When REG_WIDTH_CONFIGURED is already set, the vcpu settings must be
 * consistent with the value of the FLAG_EL1_32BIT bit in the flags.
 *
 * Return: 0 on success, negative error code on failure.
 */
static int kvm_set_vm_width(struct kvm_vcpu *vcpu)
{
	struct kvm_vcpu *tmp;
	struct kvm *kvm = vcpu->kvm;
	bool is32bit;
	unsigned long i;

	is32bit = vcpu_has_feature(vcpu, KVM_ARM_VCPU_EL1_32BIT);

	lockdep_assert_held(&kvm->lock);

	if (test_bit(KVM_ARCH_FLAG_REG_WIDTH_CONFIGURED, &kvm->arch.flags)) {
		/*
		 * The guest's register width is already configured.
		 * Make sure that the vcpu is consistent with it.
		 */
		if (is32bit == test_bit(KVM_ARCH_FLAG_EL1_32BIT, &kvm->arch.flags))
			return 0;

		return -EINVAL;
	}

	if (!cpus_have_const_cap(ARM64_HAS_32BIT_EL1) && is32bit)
		return false;
		return -EINVAL;

	/* MTE is incompatible with AArch32 */
	if (kvm_has_mte(vcpu->kvm) && is32bit)
		return false;
	if (kvm_has_mte(kvm) && is32bit)
		return -EINVAL;

	/* Check that the vcpus are either all 32bit or all 64bit */
	kvm_for_each_vcpu(i, tmp, vcpu->kvm) {
		if (vcpu_has_feature(tmp, KVM_ARM_VCPU_EL1_32BIT) != is32bit)
			return false;
	}
	if (is32bit)
		set_bit(KVM_ARCH_FLAG_EL1_32BIT, &kvm->arch.flags);

	return true;
	set_bit(KVM_ARCH_FLAG_REG_WIDTH_CONFIGURED, &kvm->arch.flags);

	return 0;
}

/**
@@ -230,10 +254,16 @@ int kvm_reset_vcpu(struct kvm_vcpu *vcpu)
	u32 pstate;

	mutex_lock(&vcpu->kvm->lock);
	ret = kvm_set_vm_width(vcpu);
	if (!ret) {
		reset_state = vcpu->arch.reset_state;
		WRITE_ONCE(vcpu->arch.reset_state.reset, false);
	}
	mutex_unlock(&vcpu->kvm->lock);

	if (ret)
		return ret;

	/* Reset PMU outside of the non-preemptible section */
	kvm_pmu_vcpu_reset(vcpu);

@@ -260,14 +290,9 @@ int kvm_reset_vcpu(struct kvm_vcpu *vcpu)
		}
	}

	if (!vcpu_allowed_register_width(vcpu)) {
		ret = -EINVAL;
		goto out;
	}

	switch (vcpu->arch.target) {
	default:
		if (test_bit(KVM_ARM_VCPU_EL1_32BIT, vcpu->arch.features)) {
		if (vcpu_el1_is_32bit(vcpu)) {
			pstate = VCPU_RESET_PSTATE_SVC;
		} else {
			pstate = VCPU_RESET_PSTATE_EL1;
Loading