/* * Copyright (C) 2018 Intel Corporation. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ #include static inline struct vcpuid_entry *find_vcpuid_entry(struct vcpu *vcpu, uint32_t leaf, uint32_t subleaf) { int i = 0, nr, half; struct vcpuid_entry *entry = NULL; struct vm *vm = vcpu->vm; nr = vm->vcpuid_entry_nr; half = nr / 2; if (vm->vcpuid_entries[half].leaf < leaf) i = half; for (; i < nr; i++) { struct vcpuid_entry *tmp = &vm->vcpuid_entries[i]; if (tmp->leaf < leaf) continue; if (tmp->leaf == leaf) { if ((tmp->flags & CPUID_CHECK_SUBLEAF) && (tmp->subleaf != subleaf)) continue; entry = tmp; break; } else if (tmp->leaf > leaf) break; } if (entry == NULL) { uint32_t limit; if (leaf & 0x80000000) limit = vm->vcpuid_xlevel; else limit = vm->vcpuid_level; if (leaf > limit) { /* Intel documentation states that invalid EAX input * will return the same information as EAX=cpuid_level * (Intel SDM Vol. 2A - Instruction Set Reference - * CPUID) */ leaf = vm->vcpuid_level; return find_vcpuid_entry(vcpu, leaf, subleaf); } } return entry; } static inline int set_vcpuid_entry(struct vm *vm, struct vcpuid_entry *entry) { struct vcpuid_entry *tmp; size_t entry_size = sizeof(struct vcpuid_entry); if (vm->vcpuid_entry_nr == MAX_VM_VCPUID_ENTRIES) { pr_err("%s, vcpuid entry over MAX_VM_VCPUID_ENTRIES(%d)\n", __func__, MAX_VM_VCPUID_ENTRIES); return -ENOMEM; } tmp = &vm->vcpuid_entries[vm->vcpuid_entry_nr++]; memcpy_s(tmp, entry_size, entry, entry_size); return 0; } /** * initialization of virtual CPUID leaf */ static void init_vcpuid_entry(__unused struct vm *vm, uint32_t leaf, uint32_t subleaf, uint32_t flags, struct vcpuid_entry *entry) { entry->leaf = leaf; entry->subleaf = subleaf; entry->flags = flags; switch (leaf) { case 0x07: if (!subleaf) { cpuid(leaf, &entry->eax, &entry->ebx, &entry->ecx, &entry->edx); /* mask invpcid */ entry->ebx &= ~CPUID_EBX_INVPCID; } else { entry->eax = 0; entry->ebx = 0; entry->ecx = 0; entry->edx = 0; } break; case 0x0a: /* not support pmu */ entry->eax = 0; entry->ebx = 0; entry->ecx = 0; entry->edx = 0; break; /* * Leaf 0x40000000 * This leaf returns the CPUID leaf range supported by the * hypervisor and the hypervisor vendor signature. * * EAX: The maximum input value for CPUID supported by the * hypervisor. * EBX, ECX, EDX: Hypervisor vendor ID signature. */ case 0x40000000: { static const char sig[12] = "ACRNACRNACRN"; const uint32_t *sigptr = (const uint32_t *)sig; entry->eax = 0x40000010; entry->ebx = sigptr[0]; entry->ecx = sigptr[1]; entry->edx = sigptr[2]; break; } /* * Leaf 0x40000010 - Timing Information. * This leaf returns the current TSC frequency and * current Bus frequency in kHz. * * EAX: (Virtual) TSC frequency in kHz. * TSC frequency is calculated from PIT in ACRN * EBX, ECX, EDX: RESERVED (reserved fields are set to zero). */ case 0x40000010: entry->eax = (uint32_t)(tsc_hz / 1000); entry->ebx = 0; entry->ecx = 0; entry->edx = 0; break; default: cpuid_subleaf(leaf, subleaf, &entry->eax, &entry->ebx, &entry->ecx, &entry->edx); break; } } int set_vcpuid_entries(struct vm *vm) { int result; struct vcpuid_entry entry; uint32_t limit; uint32_t i, j; init_vcpuid_entry(vm, 0, 0, 0, &entry); result = set_vcpuid_entry(vm, &entry); if (result) return result; vm->vcpuid_level = limit = entry.eax; for (i = 1; i <= limit; i++) { /* cpuid 1/0xb is percpu related */ if (i == 1 || i == 0xb) continue; switch (i) { case 0x02: { uint32_t times; init_vcpuid_entry(vm, i, 0, CPUID_CHECK_SUBLEAF, &entry); result = set_vcpuid_entry(vm, &entry); if (result) return result; times = entry.eax & 0xff; for (j = 1; j < times; j++) { init_vcpuid_entry(vm, i, j, CPUID_CHECK_SUBLEAF, &entry); result = set_vcpuid_entry(vm, &entry); if (result) return result; } break; } case 0x04: case 0x0d: for (j = 0; ; j++) { if (i == 0x0d && j == 64) break; init_vcpuid_entry(vm, i, j, CPUID_CHECK_SUBLEAF, &entry); if (i == 0x04 && entry.eax == 0) break; if (i == 0x0d && entry.eax == 0) continue; result = set_vcpuid_entry(vm, &entry); if (result) return result; } break; default: init_vcpuid_entry(vm, i, 0, 0, &entry); result = set_vcpuid_entry(vm, &entry); if (result) return result; break; } } init_vcpuid_entry(vm, 0x40000000, 0, 0, &entry); result = set_vcpuid_entry(vm, &entry); if (result) return result; init_vcpuid_entry(vm, 0x40000010, 0, 0, &entry); result = set_vcpuid_entry(vm, &entry); if (result) return result; init_vcpuid_entry(vm, 0x80000000, 0, 0, &entry); result = set_vcpuid_entry(vm, &entry); if (result) return result; vm->vcpuid_xlevel = limit = entry.eax; for (i = 0x80000001; i <= limit; i++) { init_vcpuid_entry(vm, i, 0, 0, &entry); result = set_vcpuid_entry(vm, &entry); if (result) return result; } return 0; } void guest_cpuid(struct vcpu *vcpu, uint32_t *eax, uint32_t *ebx, uint32_t *ecx, uint32_t *edx) { uint32_t leaf = *eax; uint32_t subleaf = *ecx; /* vm related */ if (leaf != 0x1 && leaf != 0xb && leaf != 0xd) { struct vcpuid_entry *entry = find_vcpuid_entry(vcpu, leaf, subleaf); if (entry) { *eax = entry->eax; *ebx = entry->ebx; *ecx = entry->ecx; *edx = entry->edx; } else { *eax = 0; *ebx = 0; *ecx = 0; *edx = 0; } return; } /* percpu related */ switch (leaf) { case 0x01: { cpuid(leaf, eax, ebx, ecx, edx); uint32_t apicid = vlapic_get_id(vcpu->arch_vcpu.vlapic); /* Patching initial APIC ID */ *ebx &= ~APIC_ID_MASK; *ebx |= (apicid & APIC_ID_MASK); /* mask mtrr */ *edx &= ~CPUID_EDX_MTRR; /* Patching X2APIC, X2APIC mode is disabled by default. */ if (x2apic_enabled) *ecx |= CPUID_ECX_x2APIC; else *ecx &= ~CPUID_ECX_x2APIC; /* mask pcid */ *ecx &= ~CPUID_ECX_PCID; /*mask vmx to guest os */ *ecx &= ~CPUID_ECX_VMX; /*no xsave support for guest if it is not enabled on host*/ if (!(*ecx & CPUID_ECX_OSXSAVE)) *ecx &= ~CPUID_ECX_XSAVE; *ecx &= ~CPUID_ECX_OSXSAVE; if (*ecx & CPUID_ECX_XSAVE) { uint64_t cr4; /*read guest CR4*/ cr4 = exec_vmread(VMX_GUEST_CR4); if (cr4 & CR4_OSXSAVE) *ecx |= CPUID_ECX_OSXSAVE; } break; } case 0x0b: /* Patching X2APIC */ if (!x2apic_enabled) { *eax = 0; *ebx = 0; *ecx = 0; *edx = 0; } else cpuid_subleaf(leaf, subleaf, eax, ebx, ecx, edx); break; case 0x0d: if (!cpu_has_cap(X86_FEATURE_OSXSAVE)) { *eax = 0; *ebx = 0; *ecx = 0; *edx = 0; } else cpuid_subleaf(leaf, subleaf, eax, ebx, ecx, edx); break; default: break; } }