diff options
author | Gleb Natapov <gleb@redhat.com> | 2010-01-17 08:51:22 -0500 |
---|---|---|
committer | Marcelo Tosatti <mtosatti@redhat.com> | 2010-03-01 10:35:57 -0500 |
commit | 55cd8e5a4edb8e235163ffe8264b9aaa8d7c050f (patch) | |
tree | f7d0091ead058a6c5bc427253b031374b3463b8d /arch/x86/kvm/x86.c | |
parent | 1d5103c11e32b5028262c073d56375691d51a886 (diff) |
KVM: Implement bare minimum of HYPER-V MSRs
Minimum HYPER-V implementation should have GUEST_OS_ID, HYPERCALL and
VP_INDEX MSRs.
[avi: fix build on i386]
Signed-off-by: Gleb Natapov <gleb@redhat.com>
Signed-off-by: Vadim Rozenfeld <vrozenfe@redhat.com>
Signed-off-by: Avi Kivity <avi@redhat.com>
Diffstat (limited to 'arch/x86/kvm/x86.c')
-rw-r--r-- | arch/x86/kvm/x86.c | 193 |
1 files changed, 192 insertions, 1 deletions
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 1ad34d185da9..480137db4770 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c | |||
@@ -622,9 +622,10 @@ static inline u32 bit(int bitno) | |||
622 | * kvm-specific. Those are put in the beginning of the list. | 622 | * kvm-specific. Those are put in the beginning of the list. |
623 | */ | 623 | */ |
624 | 624 | ||
625 | #define KVM_SAVE_MSRS_BEGIN 2 | 625 | #define KVM_SAVE_MSRS_BEGIN 4 |
626 | static u32 msrs_to_save[] = { | 626 | static u32 msrs_to_save[] = { |
627 | MSR_KVM_SYSTEM_TIME, MSR_KVM_WALL_CLOCK, | 627 | MSR_KVM_SYSTEM_TIME, MSR_KVM_WALL_CLOCK, |
628 | HV_X64_MSR_GUEST_OS_ID, HV_X64_MSR_HYPERCALL, | ||
628 | MSR_IA32_SYSENTER_CS, MSR_IA32_SYSENTER_ESP, MSR_IA32_SYSENTER_EIP, | 629 | MSR_IA32_SYSENTER_CS, MSR_IA32_SYSENTER_ESP, MSR_IA32_SYSENTER_EIP, |
629 | MSR_K6_STAR, | 630 | MSR_K6_STAR, |
630 | #ifdef CONFIG_X86_64 | 631 | #ifdef CONFIG_X86_64 |
@@ -1004,6 +1005,74 @@ out: | |||
1004 | return r; | 1005 | return r; |
1005 | } | 1006 | } |
1006 | 1007 | ||
1008 | static bool kvm_hv_hypercall_enabled(struct kvm *kvm) | ||
1009 | { | ||
1010 | return kvm->arch.hv_hypercall & HV_X64_MSR_HYPERCALL_ENABLE; | ||
1011 | } | ||
1012 | |||
1013 | static bool kvm_hv_msr_partition_wide(u32 msr) | ||
1014 | { | ||
1015 | bool r = false; | ||
1016 | switch (msr) { | ||
1017 | case HV_X64_MSR_GUEST_OS_ID: | ||
1018 | case HV_X64_MSR_HYPERCALL: | ||
1019 | r = true; | ||
1020 | break; | ||
1021 | } | ||
1022 | |||
1023 | return r; | ||
1024 | } | ||
1025 | |||
1026 | static int set_msr_hyperv_pw(struct kvm_vcpu *vcpu, u32 msr, u64 data) | ||
1027 | { | ||
1028 | struct kvm *kvm = vcpu->kvm; | ||
1029 | |||
1030 | switch (msr) { | ||
1031 | case HV_X64_MSR_GUEST_OS_ID: | ||
1032 | kvm->arch.hv_guest_os_id = data; | ||
1033 | /* setting guest os id to zero disables hypercall page */ | ||
1034 | if (!kvm->arch.hv_guest_os_id) | ||
1035 | kvm->arch.hv_hypercall &= ~HV_X64_MSR_HYPERCALL_ENABLE; | ||
1036 | break; | ||
1037 | case HV_X64_MSR_HYPERCALL: { | ||
1038 | u64 gfn; | ||
1039 | unsigned long addr; | ||
1040 | u8 instructions[4]; | ||
1041 | |||
1042 | /* if guest os id is not set hypercall should remain disabled */ | ||
1043 | if (!kvm->arch.hv_guest_os_id) | ||
1044 | break; | ||
1045 | if (!(data & HV_X64_MSR_HYPERCALL_ENABLE)) { | ||
1046 | kvm->arch.hv_hypercall = data; | ||
1047 | break; | ||
1048 | } | ||
1049 | gfn = data >> HV_X64_MSR_HYPERCALL_PAGE_ADDRESS_SHIFT; | ||
1050 | addr = gfn_to_hva(kvm, gfn); | ||
1051 | if (kvm_is_error_hva(addr)) | ||
1052 | return 1; | ||
1053 | kvm_x86_ops->patch_hypercall(vcpu, instructions); | ||
1054 | ((unsigned char *)instructions)[3] = 0xc3; /* ret */ | ||
1055 | if (copy_to_user((void __user *)addr, instructions, 4)) | ||
1056 | return 1; | ||
1057 | kvm->arch.hv_hypercall = data; | ||
1058 | break; | ||
1059 | } | ||
1060 | default: | ||
1061 | pr_unimpl(vcpu, "HYPER-V unimplemented wrmsr: 0x%x " | ||
1062 | "data 0x%llx\n", msr, data); | ||
1063 | return 1; | ||
1064 | } | ||
1065 | return 0; | ||
1066 | } | ||
1067 | |||
1068 | static int set_msr_hyperv(struct kvm_vcpu *vcpu, u32 msr, u64 data) | ||
1069 | { | ||
1070 | pr_unimpl(vcpu, "HYPER-V unimplemented wrmsr: 0x%x data 0x%llx\n", | ||
1071 | msr, data); | ||
1072 | |||
1073 | return 1; | ||
1074 | } | ||
1075 | |||
1007 | int kvm_set_msr_common(struct kvm_vcpu *vcpu, u32 msr, u64 data) | 1076 | int kvm_set_msr_common(struct kvm_vcpu *vcpu, u32 msr, u64 data) |
1008 | { | 1077 | { |
1009 | switch (msr) { | 1078 | switch (msr) { |
@@ -1118,6 +1187,16 @@ int kvm_set_msr_common(struct kvm_vcpu *vcpu, u32 msr, u64 data) | |||
1118 | pr_unimpl(vcpu, "unimplemented perfctr wrmsr: " | 1187 | pr_unimpl(vcpu, "unimplemented perfctr wrmsr: " |
1119 | "0x%x data 0x%llx\n", msr, data); | 1188 | "0x%x data 0x%llx\n", msr, data); |
1120 | break; | 1189 | break; |
1190 | case HV_X64_MSR_GUEST_OS_ID ... HV_X64_MSR_SINT15: | ||
1191 | if (kvm_hv_msr_partition_wide(msr)) { | ||
1192 | int r; | ||
1193 | mutex_lock(&vcpu->kvm->lock); | ||
1194 | r = set_msr_hyperv_pw(vcpu, msr, data); | ||
1195 | mutex_unlock(&vcpu->kvm->lock); | ||
1196 | return r; | ||
1197 | } else | ||
1198 | return set_msr_hyperv(vcpu, msr, data); | ||
1199 | break; | ||
1121 | default: | 1200 | default: |
1122 | if (msr && (msr == vcpu->kvm->arch.xen_hvm_config.msr)) | 1201 | if (msr && (msr == vcpu->kvm->arch.xen_hvm_config.msr)) |
1123 | return xen_hvm_config(vcpu, data); | 1202 | return xen_hvm_config(vcpu, data); |
@@ -1217,6 +1296,48 @@ static int get_msr_mce(struct kvm_vcpu *vcpu, u32 msr, u64 *pdata) | |||
1217 | return 0; | 1296 | return 0; |
1218 | } | 1297 | } |
1219 | 1298 | ||
1299 | static int get_msr_hyperv_pw(struct kvm_vcpu *vcpu, u32 msr, u64 *pdata) | ||
1300 | { | ||
1301 | u64 data = 0; | ||
1302 | struct kvm *kvm = vcpu->kvm; | ||
1303 | |||
1304 | switch (msr) { | ||
1305 | case HV_X64_MSR_GUEST_OS_ID: | ||
1306 | data = kvm->arch.hv_guest_os_id; | ||
1307 | break; | ||
1308 | case HV_X64_MSR_HYPERCALL: | ||
1309 | data = kvm->arch.hv_hypercall; | ||
1310 | break; | ||
1311 | default: | ||
1312 | pr_unimpl(vcpu, "Hyper-V unhandled rdmsr: 0x%x\n", msr); | ||
1313 | return 1; | ||
1314 | } | ||
1315 | |||
1316 | *pdata = data; | ||
1317 | return 0; | ||
1318 | } | ||
1319 | |||
1320 | static int get_msr_hyperv(struct kvm_vcpu *vcpu, u32 msr, u64 *pdata) | ||
1321 | { | ||
1322 | u64 data = 0; | ||
1323 | |||
1324 | switch (msr) { | ||
1325 | case HV_X64_MSR_VP_INDEX: { | ||
1326 | int r; | ||
1327 | struct kvm_vcpu *v; | ||
1328 | kvm_for_each_vcpu(r, v, vcpu->kvm) | ||
1329 | if (v == vcpu) | ||
1330 | data = r; | ||
1331 | break; | ||
1332 | } | ||
1333 | default: | ||
1334 | pr_unimpl(vcpu, "Hyper-V unhandled rdmsr: 0x%x\n", msr); | ||
1335 | return 1; | ||
1336 | } | ||
1337 | *pdata = data; | ||
1338 | return 0; | ||
1339 | } | ||
1340 | |||
1220 | int kvm_get_msr_common(struct kvm_vcpu *vcpu, u32 msr, u64 *pdata) | 1341 | int kvm_get_msr_common(struct kvm_vcpu *vcpu, u32 msr, u64 *pdata) |
1221 | { | 1342 | { |
1222 | u64 data; | 1343 | u64 data; |
@@ -1283,6 +1404,16 @@ int kvm_get_msr_common(struct kvm_vcpu *vcpu, u32 msr, u64 *pdata) | |||
1283 | case MSR_IA32_MCG_STATUS: | 1404 | case MSR_IA32_MCG_STATUS: |
1284 | case MSR_IA32_MC0_CTL ... MSR_IA32_MC0_CTL + 4 * KVM_MAX_MCE_BANKS - 1: | 1405 | case MSR_IA32_MC0_CTL ... MSR_IA32_MC0_CTL + 4 * KVM_MAX_MCE_BANKS - 1: |
1285 | return get_msr_mce(vcpu, msr, pdata); | 1406 | return get_msr_mce(vcpu, msr, pdata); |
1407 | case HV_X64_MSR_GUEST_OS_ID ... HV_X64_MSR_SINT15: | ||
1408 | if (kvm_hv_msr_partition_wide(msr)) { | ||
1409 | int r; | ||
1410 | mutex_lock(&vcpu->kvm->lock); | ||
1411 | r = get_msr_hyperv_pw(vcpu, msr, pdata); | ||
1412 | mutex_unlock(&vcpu->kvm->lock); | ||
1413 | return r; | ||
1414 | } else | ||
1415 | return get_msr_hyperv(vcpu, msr, pdata); | ||
1416 | break; | ||
1286 | default: | 1417 | default: |
1287 | if (!ignore_msrs) { | 1418 | if (!ignore_msrs) { |
1288 | pr_unimpl(vcpu, "unhandled rdmsr: 0x%x\n", msr); | 1419 | pr_unimpl(vcpu, "unhandled rdmsr: 0x%x\n", msr); |
@@ -1398,6 +1529,7 @@ int kvm_dev_ioctl_check_extension(long ext) | |||
1398 | case KVM_CAP_XEN_HVM: | 1529 | case KVM_CAP_XEN_HVM: |
1399 | case KVM_CAP_ADJUST_CLOCK: | 1530 | case KVM_CAP_ADJUST_CLOCK: |
1400 | case KVM_CAP_VCPU_EVENTS: | 1531 | case KVM_CAP_VCPU_EVENTS: |
1532 | case KVM_CAP_HYPERV: | ||
1401 | r = 1; | 1533 | r = 1; |
1402 | break; | 1534 | break; |
1403 | case KVM_CAP_COALESCED_MMIO: | 1535 | case KVM_CAP_COALESCED_MMIO: |
@@ -3618,11 +3750,70 @@ static inline gpa_t hc_gpa(struct kvm_vcpu *vcpu, unsigned long a0, | |||
3618 | return a0 | ((gpa_t)a1 << 32); | 3750 | return a0 | ((gpa_t)a1 << 32); |
3619 | } | 3751 | } |
3620 | 3752 | ||
3753 | int kvm_hv_hypercall(struct kvm_vcpu *vcpu) | ||
3754 | { | ||
3755 | u64 param, ingpa, outgpa, ret; | ||
3756 | uint16_t code, rep_idx, rep_cnt, res = HV_STATUS_SUCCESS, rep_done = 0; | ||
3757 | bool fast, longmode; | ||
3758 | int cs_db, cs_l; | ||
3759 | |||
3760 | /* | ||
3761 | * hypercall generates UD from non zero cpl and real mode | ||
3762 | * per HYPER-V spec | ||
3763 | */ | ||
3764 | if (kvm_x86_ops->get_cpl(vcpu) != 0 || | ||
3765 | !kvm_read_cr0_bits(vcpu, X86_CR0_PE)) { | ||
3766 | kvm_queue_exception(vcpu, UD_VECTOR); | ||
3767 | return 0; | ||
3768 | } | ||
3769 | |||
3770 | kvm_x86_ops->get_cs_db_l_bits(vcpu, &cs_db, &cs_l); | ||
3771 | longmode = is_long_mode(vcpu) && cs_l == 1; | ||
3772 | |||
3773 | if (!longmode) { | ||
3774 | param = (kvm_register_read(vcpu, VCPU_REGS_RDX) << 32) | | ||
3775 | (kvm_register_read(vcpu, VCPU_REGS_RAX) & 0xffffff); | ||
3776 | ingpa = (kvm_register_read(vcpu, VCPU_REGS_RBX) << 32) | | ||
3777 | (kvm_register_read(vcpu, VCPU_REGS_RCX) & 0xffffff); | ||
3778 | outgpa = (kvm_register_read(vcpu, VCPU_REGS_RDI) << 32) | | ||
3779 | (kvm_register_read(vcpu, VCPU_REGS_RSI) & 0xffffff); | ||
3780 | } | ||
3781 | #ifdef CONFIG_X86_64 | ||
3782 | else { | ||
3783 | param = kvm_register_read(vcpu, VCPU_REGS_RCX); | ||
3784 | ingpa = kvm_register_read(vcpu, VCPU_REGS_RDX); | ||
3785 | outgpa = kvm_register_read(vcpu, VCPU_REGS_R8); | ||
3786 | } | ||
3787 | #endif | ||
3788 | |||
3789 | code = param & 0xffff; | ||
3790 | fast = (param >> 16) & 0x1; | ||
3791 | rep_cnt = (param >> 32) & 0xfff; | ||
3792 | rep_idx = (param >> 48) & 0xfff; | ||
3793 | |||
3794 | trace_kvm_hv_hypercall(code, fast, rep_cnt, rep_idx, ingpa, outgpa); | ||
3795 | |||
3796 | res = HV_STATUS_INVALID_HYPERCALL_CODE; | ||
3797 | |||
3798 | ret = res | (((u64)rep_done & 0xfff) << 32); | ||
3799 | if (longmode) { | ||
3800 | kvm_register_write(vcpu, VCPU_REGS_RAX, ret); | ||
3801 | } else { | ||
3802 | kvm_register_write(vcpu, VCPU_REGS_RDX, ret >> 32); | ||
3803 | kvm_register_write(vcpu, VCPU_REGS_RAX, ret & 0xffffffff); | ||
3804 | } | ||
3805 | |||
3806 | return 1; | ||
3807 | } | ||
3808 | |||
3621 | int kvm_emulate_hypercall(struct kvm_vcpu *vcpu) | 3809 | int kvm_emulate_hypercall(struct kvm_vcpu *vcpu) |
3622 | { | 3810 | { |
3623 | unsigned long nr, a0, a1, a2, a3, ret; | 3811 | unsigned long nr, a0, a1, a2, a3, ret; |
3624 | int r = 1; | 3812 | int r = 1; |
3625 | 3813 | ||
3814 | if (kvm_hv_hypercall_enabled(vcpu->kvm)) | ||
3815 | return kvm_hv_hypercall(vcpu); | ||
3816 | |||
3626 | nr = kvm_register_read(vcpu, VCPU_REGS_RAX); | 3817 | nr = kvm_register_read(vcpu, VCPU_REGS_RAX); |
3627 | a0 = kvm_register_read(vcpu, VCPU_REGS_RBX); | 3818 | a0 = kvm_register_read(vcpu, VCPU_REGS_RBX); |
3628 | a1 = kvm_register_read(vcpu, VCPU_REGS_RCX); | 3819 | a1 = kvm_register_read(vcpu, VCPU_REGS_RCX); |