| // SPDX-License-Identifier: GPL-2.0-only | 
 | /* | 
 |  * vMTRR implementation | 
 |  * | 
 |  * Copyright (C) 2006 Qumranet, Inc. | 
 |  * Copyright 2010 Red Hat, Inc. and/or its affiliates. | 
 |  * Copyright(C) 2015 Intel Corporation. | 
 |  * | 
 |  * Authors: | 
 |  *   Yaniv Kamay  <yaniv@qumranet.com> | 
 |  *   Avi Kivity   <avi@qumranet.com> | 
 |  *   Marcelo Tosatti <mtosatti@redhat.com> | 
 |  *   Paolo Bonzini <pbonzini@redhat.com> | 
 |  *   Xiao Guangrong <guangrong.xiao@linux.intel.com> | 
 |  */ | 
 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | 
 |  | 
 | #include <linux/kvm_host.h> | 
 | #include <asm/mtrr.h> | 
 |  | 
 | #include "cpuid.h" | 
 |  | 
 | static u64 *find_mtrr(struct kvm_vcpu *vcpu, unsigned int msr) | 
 | { | 
 | 	int index; | 
 |  | 
 | 	switch (msr) { | 
 | 	case MTRRphysBase_MSR(0) ... MTRRphysMask_MSR(KVM_NR_VAR_MTRR - 1): | 
 | 		index = msr - MTRRphysBase_MSR(0); | 
 | 		return &vcpu->arch.mtrr_state.var[index]; | 
 | 	case MSR_MTRRfix64K_00000: | 
 | 		return &vcpu->arch.mtrr_state.fixed_64k; | 
 | 	case MSR_MTRRfix16K_80000: | 
 | 	case MSR_MTRRfix16K_A0000: | 
 | 		index = msr - MSR_MTRRfix16K_80000; | 
 | 		return &vcpu->arch.mtrr_state.fixed_16k[index]; | 
 | 	case MSR_MTRRfix4K_C0000: | 
 | 	case MSR_MTRRfix4K_C8000: | 
 | 	case MSR_MTRRfix4K_D0000: | 
 | 	case MSR_MTRRfix4K_D8000: | 
 | 	case MSR_MTRRfix4K_E0000: | 
 | 	case MSR_MTRRfix4K_E8000: | 
 | 	case MSR_MTRRfix4K_F0000: | 
 | 	case MSR_MTRRfix4K_F8000: | 
 | 		index = msr - MSR_MTRRfix4K_C0000; | 
 | 		return &vcpu->arch.mtrr_state.fixed_4k[index]; | 
 | 	case MSR_MTRRdefType: | 
 | 		return &vcpu->arch.mtrr_state.deftype; | 
 | 	default: | 
 | 		break; | 
 | 	} | 
 | 	return NULL; | 
 | } | 
 |  | 
 | static bool valid_mtrr_type(unsigned t) | 
 | { | 
 | 	return t < 8 && (1 << t) & 0x73; /* 0, 1, 4, 5, 6 */ | 
 | } | 
 |  | 
 | static bool kvm_mtrr_valid(struct kvm_vcpu *vcpu, u32 msr, u64 data) | 
 | { | 
 | 	int i; | 
 | 	u64 mask; | 
 |  | 
 | 	if (msr == MSR_MTRRdefType) { | 
 | 		if (data & ~0xcff) | 
 | 			return false; | 
 | 		return valid_mtrr_type(data & 0xff); | 
 | 	} else if (msr >= MSR_MTRRfix64K_00000 && msr <= MSR_MTRRfix4K_F8000) { | 
 | 		for (i = 0; i < 8 ; i++) | 
 | 			if (!valid_mtrr_type((data >> (i * 8)) & 0xff)) | 
 | 				return false; | 
 | 		return true; | 
 | 	} | 
 |  | 
 | 	/* variable MTRRs */ | 
 | 	if (WARN_ON_ONCE(!(msr >= MTRRphysBase_MSR(0) && | 
 | 			   msr <= MTRRphysMask_MSR(KVM_NR_VAR_MTRR - 1)))) | 
 | 		return false; | 
 |  | 
 | 	mask = kvm_vcpu_reserved_gpa_bits_raw(vcpu); | 
 | 	if ((msr & 1) == 0) { | 
 | 		/* MTRR base */ | 
 | 		if (!valid_mtrr_type(data & 0xff)) | 
 | 			return false; | 
 | 		mask |= 0xf00; | 
 | 	} else { | 
 | 		/* MTRR mask */ | 
 | 		mask |= 0x7ff; | 
 | 	} | 
 |  | 
 | 	return (data & mask) == 0; | 
 | } | 
 |  | 
 | int kvm_mtrr_set_msr(struct kvm_vcpu *vcpu, u32 msr, u64 data) | 
 | { | 
 | 	u64 *mtrr; | 
 |  | 
 | 	mtrr = find_mtrr(vcpu, msr); | 
 | 	if (!mtrr) | 
 | 		return 1; | 
 |  | 
 | 	if (!kvm_mtrr_valid(vcpu, msr, data)) | 
 | 		return 1; | 
 |  | 
 | 	*mtrr = data; | 
 | 	return 0; | 
 | } | 
 |  | 
 | int kvm_mtrr_get_msr(struct kvm_vcpu *vcpu, u32 msr, u64 *pdata) | 
 | { | 
 | 	u64 *mtrr; | 
 |  | 
 | 	/* MSR_MTRRcap is a readonly MSR. */ | 
 | 	if (msr == MSR_MTRRcap) { | 
 | 		/* | 
 | 		 * SMRR = 0 | 
 | 		 * WC = 1 | 
 | 		 * FIX = 1 | 
 | 		 * VCNT = KVM_NR_VAR_MTRR | 
 | 		 */ | 
 | 		*pdata = 0x500 | KVM_NR_VAR_MTRR; | 
 | 		return 0; | 
 | 	} | 
 |  | 
 | 	mtrr = find_mtrr(vcpu, msr); | 
 | 	if (!mtrr) | 
 | 		return 1; | 
 |  | 
 | 	*pdata = *mtrr; | 
 | 	return 0; | 
 | } |