diff options
Diffstat (limited to 'arch/x86/kvm/svm.c')
-rw-r--r-- | arch/x86/kvm/svm.c | 108 |
1 files changed, 103 insertions, 5 deletions
diff --git a/arch/x86/kvm/svm.c b/arch/x86/kvm/svm.c index 602b974a60a6..bbc678a66b18 100644 --- a/arch/x86/kvm/svm.c +++ b/arch/x86/kvm/svm.c | |||
@@ -865,6 +865,64 @@ static void svm_disable_lbrv(struct vcpu_svm *svm) | |||
865 | set_msr_interception(msrpm, MSR_IA32_LASTINTTOIP, 0, 0); | 865 | set_msr_interception(msrpm, MSR_IA32_LASTINTTOIP, 0, 0); |
866 | } | 866 | } |
867 | 867 | ||
868 | #define MTRR_TYPE_UC_MINUS 7 | ||
869 | #define MTRR2PROTVAL_INVALID 0xff | ||
870 | |||
871 | static u8 mtrr2protval[8]; | ||
872 | |||
873 | static u8 fallback_mtrr_type(int mtrr) | ||
874 | { | ||
875 | /* | ||
876 | * WT and WP aren't always available in the host PAT. Treat | ||
877 | * them as UC and UC- respectively. Everything else should be | ||
878 | * there. | ||
879 | */ | ||
880 | switch (mtrr) | ||
881 | { | ||
882 | case MTRR_TYPE_WRTHROUGH: | ||
883 | return MTRR_TYPE_UNCACHABLE; | ||
884 | case MTRR_TYPE_WRPROT: | ||
885 | return MTRR_TYPE_UC_MINUS; | ||
886 | default: | ||
887 | BUG(); | ||
888 | } | ||
889 | } | ||
890 | |||
891 | static void build_mtrr2protval(void) | ||
892 | { | ||
893 | int i; | ||
894 | u64 pat; | ||
895 | |||
896 | for (i = 0; i < 8; i++) | ||
897 | mtrr2protval[i] = MTRR2PROTVAL_INVALID; | ||
898 | |||
899 | /* Ignore the invalid MTRR types. */ | ||
900 | mtrr2protval[2] = 0; | ||
901 | mtrr2protval[3] = 0; | ||
902 | |||
903 | /* | ||
904 | * Use host PAT value to figure out the mapping from guest MTRR | ||
905 | * values to nested page table PAT/PCD/PWT values. We do not | ||
906 | * want to change the host PAT value every time we enter the | ||
907 | * guest. | ||
908 | */ | ||
909 | rdmsrl(MSR_IA32_CR_PAT, pat); | ||
910 | for (i = 0; i < 8; i++) { | ||
911 | u8 mtrr = pat >> (8 * i); | ||
912 | |||
913 | if (mtrr2protval[mtrr] == MTRR2PROTVAL_INVALID) | ||
914 | mtrr2protval[mtrr] = __cm_idx2pte(i); | ||
915 | } | ||
916 | |||
917 | for (i = 0; i < 8; i++) { | ||
918 | if (mtrr2protval[i] == MTRR2PROTVAL_INVALID) { | ||
919 | u8 fallback = fallback_mtrr_type(i); | ||
920 | mtrr2protval[i] = mtrr2protval[fallback]; | ||
921 | BUG_ON(mtrr2protval[i] == MTRR2PROTVAL_INVALID); | ||
922 | } | ||
923 | } | ||
924 | } | ||
925 | |||
868 | static __init int svm_hardware_setup(void) | 926 | static __init int svm_hardware_setup(void) |
869 | { | 927 | { |
870 | int cpu; | 928 | int cpu; |
@@ -931,6 +989,7 @@ static __init int svm_hardware_setup(void) | |||
931 | } else | 989 | } else |
932 | kvm_disable_tdp(); | 990 | kvm_disable_tdp(); |
933 | 991 | ||
992 | build_mtrr2protval(); | ||
934 | return 0; | 993 | return 0; |
935 | 994 | ||
936 | err: | 995 | err: |
@@ -1085,6 +1144,39 @@ static u64 svm_compute_tsc_offset(struct kvm_vcpu *vcpu, u64 target_tsc) | |||
1085 | return target_tsc - tsc; | 1144 | return target_tsc - tsc; |
1086 | } | 1145 | } |
1087 | 1146 | ||
1147 | static void svm_set_guest_pat(struct vcpu_svm *svm, u64 *g_pat) | ||
1148 | { | ||
1149 | struct kvm_vcpu *vcpu = &svm->vcpu; | ||
1150 | |||
1151 | /* Unlike Intel, AMD takes the guest's CR0.CD into account. | ||
1152 | * | ||
1153 | * AMD does not have IPAT. To emulate it for the case of guests | ||
1154 | * with no assigned devices, just set everything to WB. If guests | ||
1155 | * have assigned devices, however, we cannot force WB for RAM | ||
1156 | * pages only, so use the guest PAT directly. | ||
1157 | */ | ||
1158 | if (!kvm_arch_has_assigned_device(vcpu->kvm)) | ||
1159 | *g_pat = 0x0606060606060606; | ||
1160 | else | ||
1161 | *g_pat = vcpu->arch.pat; | ||
1162 | } | ||
1163 | |||
1164 | static u64 svm_get_mt_mask(struct kvm_vcpu *vcpu, gfn_t gfn, bool is_mmio) | ||
1165 | { | ||
1166 | u8 mtrr; | ||
1167 | |||
1168 | /* | ||
1169 | * 1. MMIO: trust guest MTRR, so same as item 3. | ||
1170 | * 2. No passthrough: always map as WB, and force guest PAT to WB as well | ||
1171 | * 3. Passthrough: can't guarantee the result, try to trust guest. | ||
1172 | */ | ||
1173 | if (!is_mmio && !kvm_arch_has_assigned_device(vcpu->kvm)) | ||
1174 | return 0; | ||
1175 | |||
1176 | mtrr = kvm_mtrr_get_guest_memory_type(vcpu, gfn); | ||
1177 | return mtrr2protval[mtrr]; | ||
1178 | } | ||
1179 | |||
1088 | static void init_vmcb(struct vcpu_svm *svm, bool init_event) | 1180 | static void init_vmcb(struct vcpu_svm *svm, bool init_event) |
1089 | { | 1181 | { |
1090 | struct vmcb_control_area *control = &svm->vmcb->control; | 1182 | struct vmcb_control_area *control = &svm->vmcb->control; |
@@ -1180,6 +1272,7 @@ static void init_vmcb(struct vcpu_svm *svm, bool init_event) | |||
1180 | clr_cr_intercept(svm, INTERCEPT_CR3_READ); | 1272 | clr_cr_intercept(svm, INTERCEPT_CR3_READ); |
1181 | clr_cr_intercept(svm, INTERCEPT_CR3_WRITE); | 1273 | clr_cr_intercept(svm, INTERCEPT_CR3_WRITE); |
1182 | save->g_pat = svm->vcpu.arch.pat; | 1274 | save->g_pat = svm->vcpu.arch.pat; |
1275 | svm_set_guest_pat(svm, &save->g_pat); | ||
1183 | save->cr3 = 0; | 1276 | save->cr3 = 0; |
1184 | save->cr4 = 0; | 1277 | save->cr4 = 0; |
1185 | } | 1278 | } |
@@ -3254,6 +3347,16 @@ static int svm_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr) | |||
3254 | case MSR_VM_IGNNE: | 3347 | case MSR_VM_IGNNE: |
3255 | vcpu_unimpl(vcpu, "unimplemented wrmsr: 0x%x data 0x%llx\n", ecx, data); | 3348 | vcpu_unimpl(vcpu, "unimplemented wrmsr: 0x%x data 0x%llx\n", ecx, data); |
3256 | break; | 3349 | break; |
3350 | case MSR_IA32_CR_PAT: | ||
3351 | if (npt_enabled) { | ||
3352 | if (!kvm_mtrr_valid(vcpu, MSR_IA32_CR_PAT, data)) | ||
3353 | return 1; | ||
3354 | vcpu->arch.pat = data; | ||
3355 | svm_set_guest_pat(svm, &svm->vmcb->save.g_pat); | ||
3356 | mark_dirty(svm->vmcb, VMCB_NPT); | ||
3357 | break; | ||
3358 | } | ||
3359 | /* fall through */ | ||
3257 | default: | 3360 | default: |
3258 | return kvm_set_msr_common(vcpu, msr); | 3361 | return kvm_set_msr_common(vcpu, msr); |
3259 | } | 3362 | } |
@@ -4088,11 +4191,6 @@ static bool svm_has_high_real_mode_segbase(void) | |||
4088 | return true; | 4191 | return true; |
4089 | } | 4192 | } |
4090 | 4193 | ||
4091 | static u64 svm_get_mt_mask(struct kvm_vcpu *vcpu, gfn_t gfn, bool is_mmio) | ||
4092 | { | ||
4093 | return 0; | ||
4094 | } | ||
4095 | |||
4096 | static void svm_cpuid_update(struct kvm_vcpu *vcpu) | 4194 | static void svm_cpuid_update(struct kvm_vcpu *vcpu) |
4097 | { | 4195 | { |
4098 | } | 4196 | } |