arch/x86/kvm/svm/avic.c

Source file repositories/reference/linux-study-clean/arch/x86/kvm/svm/avic.c

File Facts

System
Linux kernel
Corpus path
arch/x86/kvm/svm/avic.c
Extension
.c
Size
39515 bytes
Lines
1322
Domain
Architecture Layer
Bucket
arch/x86
Inferred role
Architecture Layer: implementation source
Status
source implementation candidate

Why This File Exists

CPU and platform-specific kernel glue: boot entry, traps, syscall entry, interrupts, page tables, context switch, and low-level barriers.

Dependency Surface

Detected Declarations

Annotated Snippet

hash_for_each_possible(svm_vm_data_hash, k2, hnode, vm_id) {
			if (k2->avic_vm_id == vm_id)
				goto again;
		}
	}
	kvm_svm->avic_vm_id = vm_id;
	hash_add(svm_vm_data_hash, &kvm_svm->hnode, kvm_svm->avic_vm_id);
	spin_unlock_irqrestore(&svm_vm_data_hash_lock, flags);

	return 0;

free_avic:
	avic_vm_destroy(kvm);
	return err;
}

static phys_addr_t avic_get_backing_page_address(struct vcpu_svm *svm)
{
	return __sme_set(__pa(svm->vcpu.arch.apic->regs));
}

void avic_init_vmcb(struct vcpu_svm *svm, struct vmcb *vmcb)
{
	struct kvm_svm *kvm_svm = to_kvm_svm(svm->vcpu.kvm);

	vmcb->control.avic_backing_page = avic_get_backing_page_address(svm);
	vmcb->control.avic_logical_id = __sme_set(__pa(kvm_svm->avic_logical_id_table));
	vmcb->control.avic_physical_id = __sme_set(__pa(kvm_svm->avic_physical_id_table));
	vmcb->control.avic_vapic_bar = APIC_DEFAULT_PHYS_BASE;

	if (kvm_vcpu_apicv_active(&svm->vcpu))
		avic_activate_vmcb(svm);
	else
		avic_deactivate_vmcb(svm);
}

static int avic_init_backing_page(struct kvm_vcpu *vcpu)
{
	u32 max_id = x2avic_enabled ? x2avic_max_physical_id : AVIC_MAX_PHYSICAL_ID;
	struct kvm_svm *kvm_svm = to_kvm_svm(vcpu->kvm);
	struct vcpu_svm *svm = to_svm(vcpu);
	u32 id = vcpu->vcpu_id;
	u64 new_entry;

	/*
	 * Inhibit AVIC if the vCPU ID is bigger than what is supported by AVIC
	 * hardware.  Immediately clear apicv_active, i.e. don't wait until the
	 * KVM_REQ_APICV_UPDATE request is processed on the first KVM_RUN, as
	 * avic_vcpu_load() expects to be called if and only if the vCPU has
	 * fully initialized AVIC.
	 */
	if (id > max_id) {
		kvm_set_apicv_inhibit(vcpu->kvm, APICV_INHIBIT_REASON_PHYSICAL_ID_TOO_BIG);
		vcpu->arch.apic->apicv_active = false;
		return 0;
	}

	BUILD_BUG_ON((AVIC_MAX_PHYSICAL_ID + 1) * sizeof(new_entry) > PAGE_SIZE ||
		     (X2AVIC_MAX_PHYSICAL_ID + 1) * sizeof(new_entry) > PAGE_SIZE);

	if (WARN_ON_ONCE(!vcpu->arch.apic->regs))
		return -EINVAL;

	if (kvm_apicv_activated(vcpu->kvm)) {
		int ret;

		/*
		 * Note, AVIC hardware walks the nested page table to check
		 * permissions, but does not use the SPA address specified in
		 * the leaf SPTE since it uses address in the AVIC_BACKING_PAGE
		 * pointer field of the VMCB.
		 */
		ret = kvm_alloc_apic_access_page(vcpu->kvm);
		if (ret)
			return ret;
	}

	/* Note, fls64() returns the bit position, +1. */
	BUILD_BUG_ON(__PHYSICAL_MASK_SHIFT >
		     fls64(AVIC_PHYSICAL_ID_ENTRY_BACKING_PAGE_MASK));

	/* Setting AVIC backing page address in the phy APIC ID table */
	new_entry = avic_get_backing_page_address(svm) |
		    AVIC_PHYSICAL_ID_ENTRY_VALID_MASK;
	svm->avic_physical_id_entry = new_entry;

	/*
	 * Initialize the real table, as vCPUs must have a valid entry in order
	 * for broadcast IPIs to function correctly (broadcast IPIs ignore
	 * invalid entries, i.e. aren't guaranteed to generate a VM-Exit).

Annotation

Implementation Notes