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

Merge tag 'kvmarm-fixes-7.0-2' of...

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

KVM/arm64 fixes for 7.0, take #2

- Fix a couple of low-severity bugs in our S2 fault handling path,
  affecting the recently introduced LS64 handling and the even more
  esoteric handling of hwpoison in a nested context

- Address yet another syzkaller finding in the vgic initialisation,
  were we would end-up destroying an uninitialised vgic, with nasty
  consequences

- Address an annoying case of pKVM failing to boot when some of the
  memblock regions that the host is faulting in are not page-aligned

- Inject some sanity in the NV stage-2 walker by checking the limits
  against the advertised PA size, and correctly report the resulting
  faults

- Drop an unnecessary ISB when emulating an EL2 S1 address translation
parents 11439c46 3599c714
Loading
Loading
Loading
Loading
+0 −2
Original line number Diff line number Diff line
@@ -1504,8 +1504,6 @@ int __kvm_at_s1e2(struct kvm_vcpu *vcpu, u32 op, u64 vaddr)
			fail = true;
		}

		isb();

		if (!fail)
			par = read_sysreg_par();

+1 −1
Original line number Diff line number Diff line
@@ -518,7 +518,7 @@ static int host_stage2_adjust_range(u64 addr, struct kvm_mem_range *range)
		granule = kvm_granule_size(level);
		cur.start = ALIGN_DOWN(addr, granule);
		cur.end = cur.start + granule;
		if (!range_included(&cur, range))
		if (!range_included(&cur, range) && level < KVM_PGTABLE_LAST_LEVEL)
			continue;
		*range = cur;
		return 0;
+9 −5
Original line number Diff line number Diff line
@@ -1751,6 +1751,7 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,

		force_pte = (max_map_size == PAGE_SIZE);
		vma_pagesize = min_t(long, vma_pagesize, max_map_size);
		vma_shift = __ffs(vma_pagesize);
	}

	/*
@@ -1837,10 +1838,8 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
	if (exec_fault && s2_force_noncacheable)
		ret = -ENOEXEC;

	if (ret) {
		kvm_release_page_unused(page);
		return ret;
	}
	if (ret)
		goto out_put_page;

	/*
	 * Guest performs atomic/exclusive operations on memory with unsupported
@@ -1850,7 +1849,8 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
	 */
	if (esr_fsc_is_excl_atomic_fault(kvm_vcpu_get_esr(vcpu))) {
		kvm_inject_dabt_excl_atomic(vcpu, kvm_vcpu_get_hfar(vcpu));
		return 1;
		ret = 1;
		goto out_put_page;
	}

	if (nested)
@@ -1936,6 +1936,10 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
		mark_page_dirty_in_slot(kvm, memslot, gfn);

	return ret != -EAGAIN ? ret : 0;

out_put_page:
	kvm_release_page_unused(page);
	return ret;
}

/* Resolve the access fault by making the page young again. */
+16 −11
Original line number Diff line number Diff line
@@ -152,31 +152,31 @@ static int get_ia_size(struct s2_walk_info *wi)
	return 64 - wi->t0sz;
}

static int check_base_s2_limits(struct s2_walk_info *wi,
static int check_base_s2_limits(struct kvm_vcpu *vcpu, struct s2_walk_info *wi,
				int level, int input_size, int stride)
{
	int start_size, ia_size;
	int start_size, pa_max;

	ia_size = get_ia_size(wi);
	pa_max = kvm_get_pa_bits(vcpu->kvm);

	/* Check translation limits */
	switch (BIT(wi->pgshift)) {
	case SZ_64K:
		if (level == 0 || (level == 1 && ia_size <= 42))
		if (level == 0 || (level == 1 && pa_max <= 42))
			return -EFAULT;
		break;
	case SZ_16K:
		if (level == 0 || (level == 1 && ia_size <= 40))
		if (level == 0 || (level == 1 && pa_max <= 40))
			return -EFAULT;
		break;
	case SZ_4K:
		if (level < 0 || (level == 0 && ia_size <= 42))
		if (level < 0 || (level == 0 && pa_max <= 42))
			return -EFAULT;
		break;
	}

	/* Check input size limits */
	if (input_size > ia_size)
	if (input_size > pa_max)
		return -EFAULT;

	/* Check number of entries in starting level table */
@@ -269,16 +269,19 @@ static int walk_nested_s2_pgd(struct kvm_vcpu *vcpu, phys_addr_t ipa,
	if (input_size > 48 || input_size < 25)
		return -EFAULT;

	ret = check_base_s2_limits(wi, level, input_size, stride);
	if (WARN_ON(ret))
	ret = check_base_s2_limits(vcpu, wi, level, input_size, stride);
	if (WARN_ON(ret)) {
		out->esr = compute_fsc(0, ESR_ELx_FSC_FAULT);
		return ret;
	}

	base_lower_bound = 3 + input_size - ((3 - level) * stride +
			   wi->pgshift);
	base_addr = wi->baddr & GENMASK_ULL(47, base_lower_bound);

	if (check_output_size(wi, base_addr)) {
		out->esr = compute_fsc(level, ESR_ELx_FSC_ADDRSZ);
		/* R_BFHQH */
		out->esr = compute_fsc(0, ESR_ELx_FSC_ADDRSZ);
		return 1;
	}

@@ -293,8 +296,10 @@ static int walk_nested_s2_pgd(struct kvm_vcpu *vcpu, phys_addr_t ipa,

		paddr = base_addr | index;
		ret = read_guest_s2_desc(vcpu, paddr, &desc, wi);
		if (ret < 0)
		if (ret < 0) {
			out->esr = ESR_ELx_FSC_SEA_TTW(level);
			return ret;
		}

		new_desc = desc;

+16 −16
Original line number Diff line number Diff line
@@ -143,6 +143,21 @@ int kvm_vgic_create(struct kvm *kvm, u32 type)
	kvm->arch.vgic.in_kernel = true;
	kvm->arch.vgic.vgic_model = type;
	kvm->arch.vgic.implementation_rev = KVM_VGIC_IMP_REV_LATEST;
	kvm->arch.vgic.vgic_dist_base = VGIC_ADDR_UNDEF;

	aa64pfr0 = kvm_read_vm_id_reg(kvm, SYS_ID_AA64PFR0_EL1) & ~ID_AA64PFR0_EL1_GIC;
	pfr1 = kvm_read_vm_id_reg(kvm, SYS_ID_PFR1_EL1) & ~ID_PFR1_EL1_GIC;

	if (type == KVM_DEV_TYPE_ARM_VGIC_V2) {
		kvm->arch.vgic.vgic_cpu_base = VGIC_ADDR_UNDEF;
	} else {
		INIT_LIST_HEAD(&kvm->arch.vgic.rd_regions);
		aa64pfr0 |= SYS_FIELD_PREP_ENUM(ID_AA64PFR0_EL1, GIC, IMP);
		pfr1 |= SYS_FIELD_PREP_ENUM(ID_PFR1_EL1, GIC, GICv3);
	}

	kvm_set_vm_id_reg(kvm, SYS_ID_AA64PFR0_EL1, aa64pfr0);
	kvm_set_vm_id_reg(kvm, SYS_ID_PFR1_EL1, pfr1);

	kvm_for_each_vcpu(i, vcpu, kvm) {
		ret = vgic_allocate_private_irqs_locked(vcpu, type);
@@ -157,25 +172,10 @@ int kvm_vgic_create(struct kvm *kvm, u32 type)
			vgic_cpu->private_irqs = NULL;
		}

		kvm->arch.vgic.vgic_model = 0;
		goto out_unlock;
	}

	kvm->arch.vgic.vgic_dist_base = VGIC_ADDR_UNDEF;

	aa64pfr0 = kvm_read_vm_id_reg(kvm, SYS_ID_AA64PFR0_EL1) & ~ID_AA64PFR0_EL1_GIC;
	pfr1 = kvm_read_vm_id_reg(kvm, SYS_ID_PFR1_EL1) & ~ID_PFR1_EL1_GIC;

	if (type == KVM_DEV_TYPE_ARM_VGIC_V2) {
		kvm->arch.vgic.vgic_cpu_base = VGIC_ADDR_UNDEF;
	} else {
		INIT_LIST_HEAD(&kvm->arch.vgic.rd_regions);
		aa64pfr0 |= SYS_FIELD_PREP_ENUM(ID_AA64PFR0_EL1, GIC, IMP);
		pfr1 |= SYS_FIELD_PREP_ENUM(ID_PFR1_EL1, GIC, GICv3);
	}

	kvm_set_vm_id_reg(kvm, SYS_ID_AA64PFR0_EL1, aa64pfr0);
	kvm_set_vm_id_reg(kvm, SYS_ID_PFR1_EL1, pfr1);

	if (type == KVM_DEV_TYPE_ARM_VGIC_V3)
		kvm->arch.vgic.nassgicap = system_supports_direct_sgis();