int vgic_v3_probe()

in kvm/vgic/vgic-v3.c [619:704]


int vgic_v3_probe(const struct gic_kvm_info *info)
{
	u64 ich_vtr_el2 = kvm_call_hyp_ret(__vgic_v3_get_gic_config);
	bool has_v2;
	int ret;

	has_v2 = ich_vtr_el2 >> 63;
	ich_vtr_el2 = (u32)ich_vtr_el2;

	/*
	 * The ListRegs field is 5 bits, but there is an architectural
	 * maximum of 16 list registers. Just ignore bit 4...
	 */
	kvm_vgic_global_state.nr_lr = (ich_vtr_el2 & 0xf) + 1;
	kvm_vgic_global_state.can_emulate_gicv2 = false;
	kvm_vgic_global_state.ich_vtr_el2 = ich_vtr_el2;

	/* GICv4 support? */
	if (info->has_v4) {
		kvm_vgic_global_state.has_gicv4 = gicv4_enable;
		kvm_vgic_global_state.has_gicv4_1 = info->has_v4_1 && gicv4_enable;
		kvm_info("GICv4%s support %sabled\n",
			 kvm_vgic_global_state.has_gicv4_1 ? ".1" : "",
			 gicv4_enable ? "en" : "dis");
	}

	kvm_vgic_global_state.vcpu_base = 0;

	if (!info->vcpu.start) {
		kvm_info("GICv3: no GICV resource entry\n");
	} else if (!has_v2) {
		pr_warn(FW_BUG "CPU interface incapable of MMIO access\n");
	} else if (!PAGE_ALIGNED(info->vcpu.start)) {
		pr_warn("GICV physical address 0x%llx not page aligned\n",
			(unsigned long long)info->vcpu.start);
	} else if (kvm_get_mode() != KVM_MODE_PROTECTED) {
		kvm_vgic_global_state.vcpu_base = info->vcpu.start;
		kvm_vgic_global_state.can_emulate_gicv2 = true;
		ret = kvm_register_vgic_device(KVM_DEV_TYPE_ARM_VGIC_V2);
		if (ret) {
			kvm_err("Cannot register GICv2 KVM device.\n");
			return ret;
		}
		kvm_info("vgic-v2@%llx\n", info->vcpu.start);
	}
	ret = kvm_register_vgic_device(KVM_DEV_TYPE_ARM_VGIC_V3);
	if (ret) {
		kvm_err("Cannot register GICv3 KVM device.\n");
		kvm_unregister_device_ops(KVM_DEV_TYPE_ARM_VGIC_V2);
		return ret;
	}

	if (kvm_vgic_global_state.vcpu_base == 0)
		kvm_info("disabling GICv2 emulation\n");

	if (cpus_have_const_cap(ARM64_WORKAROUND_CAVIUM_30115)) {
		group0_trap = true;
		group1_trap = true;
	}

	if (kvm_vgic_global_state.ich_vtr_el2 & ICH_VTR_SEIS_MASK) {
		kvm_info("GICv3 with locally generated SEI\n");

		group0_trap = true;
		group1_trap = true;
		if (ich_vtr_el2 & ICH_VTR_TDS_MASK)
			dir_trap = true;
		else
			common_trap = true;
	}

	if (group0_trap || group1_trap || common_trap | dir_trap) {
		kvm_info("GICv3 sysreg trapping enabled ([%s%s%s%s], reduced performance)\n",
			 group0_trap ? "G0" : "",
			 group1_trap ? "G1" : "",
			 common_trap ? "C"  : "",
			 dir_trap    ? "D"  : "");
		static_branch_enable(&vgic_v3_cpuif_trap);
	}

	kvm_vgic_global_state.vctrl_base = NULL;
	kvm_vgic_global_state.type = VGIC_V3;
	kvm_vgic_global_state.max_gic_vcpus = VGIC_V3_MAX_CPUS;

	return 0;
}