diff --git a/hypervisor/arch/x86/guest/vcat.c b/hypervisor/arch/x86/guest/vcat.c index 8376cc0e5..7b84a4972 100644 --- a/hypervisor/arch/x86/guest/vcat.c +++ b/hypervisor/arch/x86/guest/vcat.c @@ -151,6 +151,22 @@ static uint64_t vcat_get_max_vcbm(const struct acrn_vm *vm, int res) return max_pcbm >> low; } +/** + * @brief Map pCBM to vCBM + * + * @pre vm != NULL + */ +uint64_t vcat_pcbm_to_vcbm(const struct acrn_vm *vm, uint64_t pcbm, int res) +{ + uint64_t max_pcbm = get_max_pcbm(vm, res); + + /* Find the position low (the first bit set) in max_pcbm */ + uint16_t low = ffs64(max_pcbm); + + /* pcbm set bits should only be in the range of [low, high] */ + return (pcbm & max_pcbm) >> low; +} + /** * @brief vCBM MSR write handler * diff --git a/hypervisor/arch/x86/guest/vcpuid.c b/hypervisor/arch/x86/guest/vcpuid.c index bc19d0481..af5ee941a 100644 --- a/hypervisor/arch/x86/guest/vcpuid.c +++ b/hypervisor/arch/x86/guest/vcpuid.c @@ -15,6 +15,8 @@ #include #include #include +#include +#include static inline const struct vcpuid_entry *local_find_vcpuid_entry(const struct acrn_vcpu *vcpu, uint32_t leaf, uint32_t subleaf) @@ -286,6 +288,149 @@ static int32_t set_vcpuid_sgx(struct acrn_vm *vm) return result; } +#ifdef CONFIG_VCAT_ENABLED +/** + * @brief * Number of ways (CBM length) is detected with CPUID.0x4 + * + * @pre vm != NULL + */ +static int32_t set_vcpuid_vcat_04h(const struct acrn_vm *vm, struct vcpuid_entry *entry) +{ + uint32_t cache_type = entry->eax & 0x1FU; /* EAX bits 04:00 */ + uint32_t cache_level = (entry->eax >> 5U) & 0x7U; /* EAX bits 07:05 */ + uint16_t vcbm_len = 0U; + + if (cache_level == 2U) { + vcbm_len = vcat_get_vcbm_len(vm, RDT_RESOURCE_L2); + } else if (cache_level == 3U) { + vcbm_len = vcat_get_vcbm_len(vm, RDT_RESOURCE_L3); + } + + /* + * cache_type: + * 0 = Null - No more caches. + * 1 = Data Cache. + * 2 = Instruction Cache. + * 3 = Unified Cache. + * 4-31 = Reserved + * + * cache_level (starts at 1): + * 2 = L2 + * 3 = L3 + */ + if (((cache_type == 0x1U) || (cache_type == 0x3U)) && (vcbm_len != 0U)) { + /* + * EBX Bits 11 - 00: L = System Coherency Line Size**. + * Bits 21 - 12: P = Physical Line partitions**. + * Bits 31 - 22: W = Ways of associativity**. + */ + entry->ebx &= ~0xFFC00000U; + /* Report # of cache ways (CBM length) to guest VM */ + entry->ebx |= (vcbm_len - 1U) << 22U; + } + + return 0; +} + +/** + * @brief RDT allocation enumeration sub-leaf (EAX = 10H, ECX = 0) + * Expose CAT capabilities to guest VM + * + * @pre vm != NULL + */ +static int32_t set_vcpuid_vcat_10h_subleaf_0(struct acrn_vm *vm, bool l2, bool l3) +{ + struct vcpuid_entry entry; + + init_vcpuid_entry(CPUID_RDT_ALLOCATION, 0U, CPUID_CHECK_SUBLEAF, &entry); + + entry.ebx &= ~0xeU; /* Set the L3/L2/MBA bits (bits 1, 2, and 3) all to 0 (not supported) */ + + if (l2) { + /* Bit 02: Supports L2 Cache Allocation Technology if 1 */ + entry.ebx |= 0x4U; + } + + if (l3) { + /* Bit 01: Supports L3 Cache Allocation Technology if 1 */ + entry.ebx |= 0x2U; + } + + return set_vcpuid_entry(vm, &entry); +} + +/** + * @brief L2/L3 enumeration sub-leaf + * + * @pre vm != NULL + */ +static int32_t set_vcpuid_vcat_10h_subleaf_res(struct acrn_vm *vm, uint32_t subleaf, uint16_t num_vclosids) +{ + struct vcpuid_entry entry; + uint16_t vcbm_len; + int res; + + if (subleaf == 1U) { + res = RDT_RESOURCE_L3; + } else { + res = RDT_RESOURCE_L2; + } + vcbm_len = vcat_get_vcbm_len(vm, res); + + /* Set cache cbm_len */ + init_vcpuid_entry(CPUID_RDT_ALLOCATION, subleaf, CPUID_CHECK_SUBLEAF, &entry); + + if ((entry.eax != 0U) && (vcbm_len != 0U)) { + /* Bits 4 - 00: Length of the capacity bit mask for the corresponding ResID using minus-one notation */ + entry.eax = (entry.eax & ~0x1F) | (vcbm_len - 1U); + + /* Bits 31 - 00: Bit-granular map of isolation/contention of allocation units + * Each set bit within the length of the CBM indicates the corresponding unit of the L2/L3 allocation + * may be used by other entities in the platform. Each cleared bit within the length of the CBM + * indicates the corresponding allocation unit can be configured to implement a priority-based + * allocation scheme chosen by an OS/VMM without interference with other hardware agents in the system. + */ + entry.ebx = (uint32_t)vcat_pcbm_to_vcbm(vm, entry.ebx, res); + + /* Do not support CDP for now */ + entry.ecx &= ~0x4U; + + /* Report max CLOS to guest VM + * Bits 15 - 00: Highest COS number supported for this ResID using minus-one notation + */ + entry.edx = (entry.edx & 0xFFFF0000U) | (num_vclosids - 1U); + } + + return set_vcpuid_entry(vm, &entry); +} + +/** + * @pre vm != NULL + */ +static int32_t set_vcpuid_vcat_10h(struct acrn_vm *vm) +{ + int32_t result; + uint16_t num_vclosids = vcat_get_num_vclosids(vm); + bool l2 = is_l2_vcat_configured(vm); + bool l3 = is_l3_vcat_configured(vm); + + /* RDT allocation enumeration sub-leaf (EAX=10H, ECX=0) */ + result = set_vcpuid_vcat_10h_subleaf_0(vm, l2, l3); + + if ((result == 0) && l2) { + /* L2 enumeration sub-leaf (EAX=10H, ECX=2) */ + result = set_vcpuid_vcat_10h_subleaf_res(vm, 2U, num_vclosids); + } + + if ((result == 0) && l3) { + /* L3 enumeration sub-leaf (EAX=10H, ECX=1) */ + result = set_vcpuid_vcat_10h_subleaf_res(vm, 1U, num_vclosids); + } + + return result; +} +#endif + static int32_t set_vcpuid_extended_function(struct acrn_vm *vm) { uint32_t i, limit; @@ -376,6 +521,12 @@ int32_t set_vcpuid_entries(struct acrn_vm *vm) if (entry.eax == 0U) { break; } + +#ifdef CONFIG_VCAT_ENABLED + if (is_vcat_configured(vm)) { + result = set_vcpuid_vcat_04h(vm, &entry); + } +#endif result = set_vcpuid_entry(vm, &entry); if (result != 0) { /* wants to break out of switch */ @@ -392,6 +543,13 @@ int32_t set_vcpuid_entries(struct acrn_vm *vm) if (is_vsgx_supported(vm->vm_id)) { entry.ebx |= CPUID_EBX_SGX; } + +#ifdef CONFIG_VCAT_ENABLED + if (is_vcat_configured(vm)) { + /* Bit 15: Supports Intel Resource Director Technology (Intel RDT) Allocation capability if 1 */ + entry.ebx |= CPUID_EBX_PQE; + } +#endif result = set_vcpuid_entry(vm, &entry); break; case 0x12U: @@ -408,7 +566,16 @@ int32_t set_vcpuid_entries(struct acrn_vm *vm) /* Intel RDT */ case 0x0fU: + break; + /* Intel RDT */ case 0x10U: +#ifdef CONFIG_VCAT_ENABLED + if (is_vcat_configured(vm)) { + result = set_vcpuid_vcat_10h(vm); + } +#endif + break; + /* Intel Processor Trace */ case 0x14U: /* PCONFIG */ diff --git a/hypervisor/include/arch/x86/asm/guest/vcat.h b/hypervisor/include/arch/x86/asm/guest/vcat.h index 4cc2934c3..a9518bded 100644 --- a/hypervisor/include/arch/x86/asm/guest/vcat.h +++ b/hypervisor/include/arch/x86/asm/guest/vcat.h @@ -9,7 +9,12 @@ #include +bool is_l2_vcat_configured(const struct acrn_vm *vm); +bool is_l3_vcat_configured(const struct acrn_vm *vm); +uint16_t vcat_get_vcbm_len(const struct acrn_vm *vm, int res); void init_vcat_msrs(struct acrn_vcpu *vcpu); +uint16_t vcat_get_num_vclosids(const struct acrn_vm *vm); +uint64_t vcat_pcbm_to_vcbm(const struct acrn_vm *vm, uint64_t pcbm, int res); #endif /* VCAT_H_ */