arch/x86/kvm/lapic.c

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

File Facts

System
Linux kernel
Corpus path
arch/x86/kvm/lapic.c
Extension
.c
Size
98516 bytes
Lines
3629
Domain
Architecture Layer
Bucket
arch/x86
Inferred role
Architecture Layer: exported/initcall integration point
Status
integration 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

if (offset <= max_apic_id) {
			u8 cluster_size = min(max_apic_id - offset + 1, 16U);

			offset = array_index_nospec(offset, map->max_apic_id + 1);
			*cluster = &map->phys_map[offset];
			*mask = dest_id & (0xffff >> (16 - cluster_size));
		} else {
			*mask = 0;
		}

		return true;
		}
	case KVM_APIC_MODE_XAPIC_FLAT:
		*cluster = map->xapic_flat_map;
		*mask = dest_id & 0xff;
		return true;
	case KVM_APIC_MODE_XAPIC_CLUSTER:
		*cluster = map->xapic_cluster_map[(dest_id >> 4) & 0xf];
		*mask = dest_id & 0xf;
		return true;
	case KVM_APIC_MODE_MAP_DISABLED:
		return false;
	default:
		WARN_ON_ONCE(1);
		return false;
	}
}

static int kvm_recalculate_phys_map(struct kvm_apic_map *new,
				    struct kvm_vcpu *vcpu,
				    bool *xapic_id_mismatch)
{
	struct kvm_lapic *apic = vcpu->arch.apic;
	u32 x2apic_id = kvm_x2apic_id(apic);
	u32 xapic_id = kvm_xapic_id(apic);
	u32 physical_id;

	/*
	 * For simplicity, KVM always allocates enough space for all possible
	 * xAPIC IDs.  Yell, but don't kill the VM, as KVM can continue on
	 * without the optimized map.
	 */
	if (WARN_ON_ONCE(xapic_id > new->max_apic_id))
		return -EINVAL;

	/*
	 * Bail if a vCPU was added and/or enabled its APIC between allocating
	 * the map and doing the actual calculations for the map.  Note, KVM
	 * hardcodes the x2APIC ID to vcpu_id, i.e. there's no TOCTOU bug if
	 * the compiler decides to reload x2apic_id after this check.
	 */
	if (x2apic_id > new->max_apic_id)
		return -E2BIG;

	/*
	 * Deliberately truncate the vCPU ID when detecting a mismatched APIC
	 * ID to avoid false positives if the vCPU ID, i.e. x2APIC ID, is a
	 * 32-bit value.  Any unwanted aliasing due to truncation results will
	 * be detected below.
	 */
	if (!apic_x2apic_mode(apic) && xapic_id != (u8)vcpu->vcpu_id)
		*xapic_id_mismatch = true;

	/*
	 * Apply KVM's hotplug hack if userspace has enable 32-bit APIC IDs.
	 * Allow sending events to vCPUs by their x2APIC ID even if the target
	 * vCPU is in legacy xAPIC mode, and silently ignore aliased xAPIC IDs
	 * (the x2APIC ID is truncated to 8 bits, causing IDs > 0xff to wrap
	 * and collide).
	 *
	 * Honor the architectural (and KVM's non-optimized) behavior if
	 * userspace has not enabled 32-bit x2APIC IDs.  Each APIC is supposed
	 * to process messages independently.  If multiple vCPUs have the same
	 * effective APIC ID, e.g. due to the x2APIC wrap or because the guest
	 * manually modified its xAPIC IDs, events targeting that ID are
	 * supposed to be recognized by all vCPUs with said ID.
	 */
	if (vcpu->kvm->arch.x2apic_format) {
		/* See also kvm_apic_match_physical_addr(). */
		if (apic_x2apic_mode(apic) || x2apic_id > 0xff)
			new->phys_map[x2apic_id] = apic;

		if (!apic_x2apic_mode(apic) && !new->phys_map[xapic_id])
			new->phys_map[xapic_id] = apic;
	} else {
		/*
		 * Disable the optimized map if the physical APIC ID is already
		 * mapped, i.e. is aliased to multiple vCPUs.  The optimized
		 * map requires a strict 1:1 mapping between IDs and vCPUs.
		 */

Annotation

Implementation Notes