diff --git a/hypervisor/arch/x86/guest/vcat.c b/hypervisor/arch/x86/guest/vcat.c index fdfc5551c..b760fd2a4 100644 --- a/hypervisor/arch/x86/guest/vcat.c +++ b/hypervisor/arch/x86/guest/vcat.c @@ -209,20 +209,97 @@ int32_t read_vcbm(const struct acrn_vcpu *vcpu, uint32_t vmsr, uint64_t *rval) return ret; } +/** + * @brief Map vCBM to pCBM + * + * @pre vm != NULL && ((vcbm & vcat_get_max_vcbm(vm, res)) == vcbm) + */ +static uint64_t vcbm_to_pcbm(const struct acrn_vm *vm, uint64_t vcbm, 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); + + return vcbm << low; +} + +/* + * Check if bitmask is contiguous: + * All (and only) contiguous '1' combinations are allowed (e.g. FFFFH, 0FF0H, 003CH, etc.) + */ +static bool is_contiguous(uint64_t bitmask) +{ + bool ret = false; + + if (bitmask != 0UL) { + uint16_t low = ffs64(bitmask); + uint16_t high = fls64(bitmask); + + if (((2UL << high) - (1UL << low)) == bitmask) { + ret = true; + } + } + + return ret; +} + /** * @brief vCBM MSR write handler * * @pre vcpu != NULL && vcpu->vm != NULL */ -int32_t write_vcbm(__unused struct acrn_vcpu *vcpu, __unused uint32_t vmsr, __unused uint64_t val) +int32_t write_vcbm(struct acrn_vcpu *vcpu, uint32_t vmsr, uint64_t val) { - /* - * TODO: this is going to be implemented in a subsequent commit, will perform the following actions: - * write vCBM - * vmsr to pmsr and vcbm to pcbm - * write pCBM - */ - return -EACCES; + int ret = -EACCES; + struct acrn_vm *vm = vcpu->vm; + int res = -1; + uint32_t msr_base; + + if (is_vcat_configured(vm)) { + if (is_l2_vcbm_msr(vm, vmsr)) { + res = RDT_RESOURCE_L2; + msr_base = MSR_IA32_L2_MASK_BASE; + } else if (is_l3_vcbm_msr(vm, vmsr)) { + res = RDT_RESOURCE_L3; + msr_base = MSR_IA32_L3_MASK_BASE; + } + } + + if (res >= 0) { + /* + * vcbm set bits should only be in the range of [0, vcbm_len) (vcat_get_max_vcbm), + * so mask with vcat_get_max_vcbm to prevent erroneous vCBM value + */ + uint64_t masked_vcbm = val & vcat_get_max_vcbm(vm, res); + + /* + * Validity check on val: + * Bits 63:32 of val are reserved and must be written with zeros + * (satisfied by the masked_vcbm == val condition) + * vCBM must be contiguous + */ + if ((masked_vcbm == val) && is_contiguous(val)) { + uint32_t pmsr; + uint16_t vclosid; + uint64_t pcbm, pvalue; + + /* Write vCBM first: */ + vcpu_set_guest_msr(vcpu, vmsr, val); + + /* Write pCBM: */ + vclosid = (uint16_t)(vmsr - msr_base); + pmsr = msr_base + (uint32_t)vclosid_to_pclosid(vm, vclosid); + pcbm = vcbm_to_pcbm(vm, val, res); + /* Preserve reserved bits, and only set the pCBM bits */ + pvalue = (msr_read(pmsr) & ~get_max_pcbm(vm, res)) | pcbm; + msr_write(pmsr, pvalue); + + ret = 0; + } + } + + return ret; } /** diff --git a/hypervisor/arch/x86/guest/vmsr.c b/hypervisor/arch/x86/guest/vmsr.c index 5df1b4612..9843a8fea 100644 --- a/hypervisor/arch/x86/guest/vmsr.c +++ b/hypervisor/arch/x86/guest/vmsr.c @@ -1098,6 +1098,19 @@ int32_t wrmsr_vmexit_handler(struct acrn_vcpu *vcpu) } break; } +#ifdef CONFIG_VCAT_ENABLED + case MSR_IA32_L2_MASK_BASE ... (MSR_IA32_L2_MASK_BASE + NUM_VCAT_L2_MSRS - 1U): + case MSR_IA32_L3_MASK_BASE ... (MSR_IA32_L3_MASK_BASE + NUM_VCAT_L3_MSRS - 1U): + { + err = write_vcbm(vcpu, msr, v); + break; + } + case MSR_IA32_PQR_ASSOC: + { + err = write_vclosid(vcpu, v); + break; + } +#endif default: { if (is_x2apic_msr(msr)) { diff --git a/hypervisor/include/arch/x86/asm/guest/vcat.h b/hypervisor/include/arch/x86/asm/guest/vcat.h index af764435a..f400b7529 100644 --- a/hypervisor/include/arch/x86/asm/guest/vcat.h +++ b/hypervisor/include/arch/x86/asm/guest/vcat.h @@ -16,7 +16,9 @@ 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); int32_t read_vcbm(const struct acrn_vcpu *vcpu, uint32_t vmsr, uint64_t *rval); +int32_t write_vcbm(struct acrn_vcpu *vcpu, uint32_t vmsr, uint64_t val); int32_t read_vclosid(const struct acrn_vcpu *vcpu, uint64_t *rval); +int32_t write_vclosid(struct acrn_vcpu *vcpu, uint64_t val); #endif /* VCAT_H_ */