/****************************************************************************** * arch/x86/msr.c * * Policy objects for Model-Specific Registers. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; If not, see . * * Copyright (c) 2017 Citrix Systems Ltd. */ #include #include #include #include struct msr_domain_policy __read_mostly hvm_max_msr_domain_policy, __read_mostly pv_max_msr_domain_policy; struct msr_vcpu_policy __read_mostly hvm_max_msr_vcpu_policy, __read_mostly pv_max_msr_vcpu_policy; static void __init calculate_hvm_max_policy(void) { struct msr_domain_policy *dp = &hvm_max_msr_domain_policy; struct msr_vcpu_policy *vp = &hvm_max_msr_vcpu_policy; if ( !hvm_enabled ) return; /* 0x000000ce MSR_INTEL_PLATFORM_INFO */ if ( boot_cpu_data.x86_vendor == X86_VENDOR_INTEL || boot_cpu_data.x86_vendor == X86_VENDOR_AMD ) { dp->plaform_info.available = true; dp->plaform_info.cpuid_faulting = true; } /* 0x00000140 MSR_INTEL_MISC_FEATURES_ENABLES */ vp->misc_features_enables.available = dp->plaform_info.available; } static void __init calculate_pv_max_policy(void) { struct msr_domain_policy *dp = &pv_max_msr_domain_policy; struct msr_vcpu_policy *vp = &pv_max_msr_vcpu_policy; /* 0x000000ce MSR_INTEL_PLATFORM_INFO */ if ( cpu_has_cpuid_faulting ) { dp->plaform_info.available = true; dp->plaform_info.cpuid_faulting = true; } /* 0x00000140 MSR_INTEL_MISC_FEATURES_ENABLES */ vp->misc_features_enables.available = dp->plaform_info.available; } void __init init_guest_msr_policy(void) { calculate_hvm_max_policy(); calculate_pv_max_policy(); } int init_domain_msr_policy(struct domain *d) { struct msr_domain_policy *dp; dp = xmalloc(struct msr_domain_policy); if ( !dp ) return -ENOMEM; *dp = is_pv_domain(d) ? pv_max_msr_domain_policy : hvm_max_msr_domain_policy; /* See comment in intel_ctxt_switch_levelling() */ if ( is_control_domain(d) ) { dp->plaform_info.available = false; dp->plaform_info.cpuid_faulting = false; } d->arch.msr = dp; return 0; } int init_vcpu_msr_policy(struct vcpu *v) { struct domain *d = v->domain; struct msr_vcpu_policy *vp; vp = xmalloc(struct msr_vcpu_policy); if ( !vp ) return -ENOMEM; *vp = is_pv_domain(d) ? pv_max_msr_vcpu_policy : hvm_max_msr_vcpu_policy; /* See comment in intel_ctxt_switch_levelling() */ if ( is_control_domain(d) ) vp->misc_features_enables.available = false; v->arch.msr = vp; return 0; } int guest_rdmsr(const struct vcpu *v, uint32_t msr, uint64_t *val) { const struct msr_domain_policy *dp = v->domain->arch.msr; const struct msr_vcpu_policy *vp = v->arch.msr; switch ( msr ) { case MSR_INTEL_PLATFORM_INFO: if ( !dp->plaform_info.available ) goto gp_fault; *val = (uint64_t)dp->plaform_info.cpuid_faulting << _MSR_PLATFORM_INFO_CPUID_FAULTING; break; case MSR_INTEL_MISC_FEATURES_ENABLES: if ( !vp->misc_features_enables.available ) goto gp_fault; *val = (uint64_t)vp->misc_features_enables.cpuid_faulting << _MSR_MISC_FEATURES_CPUID_FAULTING; break; default: return X86EMUL_UNHANDLEABLE; } return X86EMUL_OKAY; gp_fault: return X86EMUL_EXCEPTION; } int guest_wrmsr(struct vcpu *v, uint32_t msr, uint64_t val) { const struct vcpu *curr = current; struct domain *d = v->domain; struct msr_domain_policy *dp = d->arch.msr; struct msr_vcpu_policy *vp = v->arch.msr; switch ( msr ) { case MSR_INTEL_PLATFORM_INFO: goto gp_fault; case MSR_INTEL_MISC_FEATURES_ENABLES: { uint64_t rsvd = ~0ull; bool old_cpuid_faulting = vp->misc_features_enables.cpuid_faulting; if ( !vp->misc_features_enables.available ) goto gp_fault; if ( dp->plaform_info.cpuid_faulting ) rsvd &= ~MSR_MISC_FEATURES_CPUID_FAULTING; if ( val & rsvd ) goto gp_fault; vp->misc_features_enables.cpuid_faulting = val & MSR_MISC_FEATURES_CPUID_FAULTING; if ( v == curr && is_hvm_domain(d) && cpu_has_cpuid_faulting && (old_cpuid_faulting ^ vp->misc_features_enables.cpuid_faulting) ) ctxt_switch_levelling(v); break; } default: return X86EMUL_UNHANDLEABLE; } return X86EMUL_OKAY; gp_fault: return X86EMUL_EXCEPTION; } /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */