diff options
| author | Ingo Molnar <mingo@elte.hu> | 2007-02-19 07:37:47 -0500 |
|---|---|---|
| committer | Avi Kivity <avi@qumranet.com> | 2007-03-04 04:12:40 -0500 |
| commit | 102d8325a1d2f266d3d0a03fdde948544e72c12d (patch) | |
| tree | 21024c8b9b2b702c79200343e26f14f075da0479 | |
| parent | 5972e9535e94bf875eb8eab8a667ba04c7583874 (diff) | |
KVM: add MSR based hypercall API
This adds a special MSR based hypercall API to KVM. This is to be
used by paravirtual kernels and virtual drivers.
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Signed-off-by: Avi Kivity <avi@qumranet.com>
| -rw-r--r-- | drivers/kvm/kvm.h | 6 | ||||
| -rw-r--r-- | drivers/kvm/kvm_main.c | 73 | ||||
| -rw-r--r-- | drivers/kvm/svm.c | 13 | ||||
| -rw-r--r-- | drivers/kvm/vmx.c | 13 | ||||
| -rw-r--r-- | include/linux/kvm_para.h | 55 |
5 files changed, 160 insertions, 0 deletions
diff --git a/drivers/kvm/kvm.h b/drivers/kvm/kvm.h index 9a49b2ed2a1e..fd7746a2bc3e 100644 --- a/drivers/kvm/kvm.h +++ b/drivers/kvm/kvm.h | |||
| @@ -14,6 +14,7 @@ | |||
| 14 | 14 | ||
| 15 | #include "vmx.h" | 15 | #include "vmx.h" |
| 16 | #include <linux/kvm.h> | 16 | #include <linux/kvm.h> |
| 17 | #include <linux/kvm_para.h> | ||
| 17 | 18 | ||
| 18 | #define CR0_PE_MASK (1ULL << 0) | 19 | #define CR0_PE_MASK (1ULL << 0) |
| 19 | #define CR0_TS_MASK (1ULL << 3) | 20 | #define CR0_TS_MASK (1ULL << 3) |
| @@ -237,6 +238,9 @@ struct kvm_vcpu { | |||
| 237 | unsigned long cr0; | 238 | unsigned long cr0; |
| 238 | unsigned long cr2; | 239 | unsigned long cr2; |
| 239 | unsigned long cr3; | 240 | unsigned long cr3; |
| 241 | gpa_t para_state_gpa; | ||
| 242 | struct page *para_state_page; | ||
| 243 | gpa_t hypercall_gpa; | ||
| 240 | unsigned long cr4; | 244 | unsigned long cr4; |
| 241 | unsigned long cr8; | 245 | unsigned long cr8; |
| 242 | u64 pdptrs[4]; /* pae */ | 246 | u64 pdptrs[4]; /* pae */ |
| @@ -382,6 +386,8 @@ struct kvm_arch_ops { | |||
| 382 | int (*run)(struct kvm_vcpu *vcpu, struct kvm_run *run); | 386 | int (*run)(struct kvm_vcpu *vcpu, struct kvm_run *run); |
| 383 | int (*vcpu_setup)(struct kvm_vcpu *vcpu); | 387 | int (*vcpu_setup)(struct kvm_vcpu *vcpu); |
| 384 | void (*skip_emulated_instruction)(struct kvm_vcpu *vcpu); | 388 | void (*skip_emulated_instruction)(struct kvm_vcpu *vcpu); |
| 389 | void (*patch_hypercall)(struct kvm_vcpu *vcpu, | ||
| 390 | unsigned char *hypercall_addr); | ||
| 385 | }; | 391 | }; |
| 386 | 392 | ||
| 387 | extern struct kvm_stat kvm_stat; | 393 | extern struct kvm_stat kvm_stat; |
diff --git a/drivers/kvm/kvm_main.c b/drivers/kvm/kvm_main.c index 122c05f283e1..757a41f1db84 100644 --- a/drivers/kvm/kvm_main.c +++ b/drivers/kvm/kvm_main.c | |||
| @@ -1204,6 +1204,73 @@ void realmode_set_cr(struct kvm_vcpu *vcpu, int cr, unsigned long val, | |||
| 1204 | } | 1204 | } |
| 1205 | } | 1205 | } |
| 1206 | 1206 | ||
| 1207 | /* | ||
| 1208 | * Register the para guest with the host: | ||
| 1209 | */ | ||
| 1210 | static int vcpu_register_para(struct kvm_vcpu *vcpu, gpa_t para_state_gpa) | ||
| 1211 | { | ||
| 1212 | struct kvm_vcpu_para_state *para_state; | ||
| 1213 | hpa_t para_state_hpa, hypercall_hpa; | ||
| 1214 | struct page *para_state_page; | ||
| 1215 | unsigned char *hypercall; | ||
| 1216 | gpa_t hypercall_gpa; | ||
| 1217 | |||
| 1218 | printk(KERN_DEBUG "kvm: guest trying to enter paravirtual mode\n"); | ||
| 1219 | printk(KERN_DEBUG ".... para_state_gpa: %08Lx\n", para_state_gpa); | ||
| 1220 | |||
| 1221 | /* | ||
| 1222 | * Needs to be page aligned: | ||
| 1223 | */ | ||
| 1224 | if (para_state_gpa != PAGE_ALIGN(para_state_gpa)) | ||
| 1225 | goto err_gp; | ||
| 1226 | |||
| 1227 | para_state_hpa = gpa_to_hpa(vcpu, para_state_gpa); | ||
| 1228 | printk(KERN_DEBUG ".... para_state_hpa: %08Lx\n", para_state_hpa); | ||
| 1229 | if (is_error_hpa(para_state_hpa)) | ||
| 1230 | goto err_gp; | ||
| 1231 | |||
| 1232 | para_state_page = pfn_to_page(para_state_hpa >> PAGE_SHIFT); | ||
| 1233 | para_state = kmap_atomic(para_state_page, KM_USER0); | ||
| 1234 | |||
| 1235 | printk(KERN_DEBUG ".... guest version: %d\n", para_state->guest_version); | ||
| 1236 | printk(KERN_DEBUG ".... size: %d\n", para_state->size); | ||
| 1237 | |||
| 1238 | para_state->host_version = KVM_PARA_API_VERSION; | ||
| 1239 | /* | ||
| 1240 | * We cannot support guests that try to register themselves | ||
| 1241 | * with a newer API version than the host supports: | ||
| 1242 | */ | ||
| 1243 | if (para_state->guest_version > KVM_PARA_API_VERSION) { | ||
| 1244 | para_state->ret = -KVM_EINVAL; | ||
| 1245 | goto err_kunmap_skip; | ||
| 1246 | } | ||
| 1247 | |||
| 1248 | hypercall_gpa = para_state->hypercall_gpa; | ||
| 1249 | hypercall_hpa = gpa_to_hpa(vcpu, hypercall_gpa); | ||
| 1250 | printk(KERN_DEBUG ".... hypercall_hpa: %08Lx\n", hypercall_hpa); | ||
| 1251 | if (is_error_hpa(hypercall_hpa)) { | ||
| 1252 | para_state->ret = -KVM_EINVAL; | ||
| 1253 | goto err_kunmap_skip; | ||
| 1254 | } | ||
| 1255 | |||
| 1256 | printk(KERN_DEBUG "kvm: para guest successfully registered.\n"); | ||
| 1257 | vcpu->para_state_page = para_state_page; | ||
| 1258 | vcpu->para_state_gpa = para_state_gpa; | ||
| 1259 | vcpu->hypercall_gpa = hypercall_gpa; | ||
| 1260 | |||
| 1261 | hypercall = kmap_atomic(pfn_to_page(hypercall_hpa >> PAGE_SHIFT), | ||
| 1262 | KM_USER1) + (hypercall_hpa & ~PAGE_MASK); | ||
| 1263 | kvm_arch_ops->patch_hypercall(vcpu, hypercall); | ||
| 1264 | kunmap_atomic(hypercall, KM_USER1); | ||
| 1265 | |||
| 1266 | para_state->ret = 0; | ||
| 1267 | err_kunmap_skip: | ||
| 1268 | kunmap_atomic(para_state, KM_USER0); | ||
| 1269 | return 0; | ||
| 1270 | err_gp: | ||
| 1271 | return 1; | ||
| 1272 | } | ||
| 1273 | |||
| 1207 | int kvm_get_msr_common(struct kvm_vcpu *vcpu, u32 msr, u64 *pdata) | 1274 | int kvm_get_msr_common(struct kvm_vcpu *vcpu, u32 msr, u64 *pdata) |
| 1208 | { | 1275 | { |
| 1209 | u64 data; | 1276 | u64 data; |
| @@ -1312,6 +1379,12 @@ int kvm_set_msr_common(struct kvm_vcpu *vcpu, u32 msr, u64 data) | |||
| 1312 | case MSR_IA32_MISC_ENABLE: | 1379 | case MSR_IA32_MISC_ENABLE: |
| 1313 | vcpu->ia32_misc_enable_msr = data; | 1380 | vcpu->ia32_misc_enable_msr = data; |
| 1314 | break; | 1381 | break; |
| 1382 | /* | ||
| 1383 | * This is the 'probe whether the host is KVM' logic: | ||
| 1384 | */ | ||
| 1385 | case MSR_KVM_API_MAGIC: | ||
| 1386 | return vcpu_register_para(vcpu, data); | ||
| 1387 | |||
| 1315 | default: | 1388 | default: |
| 1316 | printk(KERN_ERR "kvm: unhandled wrmsr: 0x%x\n", msr); | 1389 | printk(KERN_ERR "kvm: unhandled wrmsr: 0x%x\n", msr); |
| 1317 | return 1; | 1390 | return 1; |
diff --git a/drivers/kvm/svm.c b/drivers/kvm/svm.c index 72cac0488b31..f6e86528f031 100644 --- a/drivers/kvm/svm.c +++ b/drivers/kvm/svm.c | |||
| @@ -1669,6 +1669,18 @@ static int is_disabled(void) | |||
| 1669 | return 0; | 1669 | return 0; |
| 1670 | } | 1670 | } |
| 1671 | 1671 | ||
| 1672 | static void | ||
| 1673 | svm_patch_hypercall(struct kvm_vcpu *vcpu, unsigned char *hypercall) | ||
| 1674 | { | ||
| 1675 | /* | ||
| 1676 | * Patch in the VMMCALL instruction: | ||
| 1677 | */ | ||
| 1678 | hypercall[0] = 0x0f; | ||
| 1679 | hypercall[1] = 0x01; | ||
| 1680 | hypercall[2] = 0xd9; | ||
| 1681 | hypercall[3] = 0xc3; | ||
| 1682 | } | ||
| 1683 | |||
| 1672 | static struct kvm_arch_ops svm_arch_ops = { | 1684 | static struct kvm_arch_ops svm_arch_ops = { |
| 1673 | .cpu_has_kvm_support = has_svm, | 1685 | .cpu_has_kvm_support = has_svm, |
| 1674 | .disabled_by_bios = is_disabled, | 1686 | .disabled_by_bios = is_disabled, |
| @@ -1717,6 +1729,7 @@ static struct kvm_arch_ops svm_arch_ops = { | |||
| 1717 | .run = svm_vcpu_run, | 1729 | .run = svm_vcpu_run, |
| 1718 | .skip_emulated_instruction = skip_emulated_instruction, | 1730 | .skip_emulated_instruction = skip_emulated_instruction, |
| 1719 | .vcpu_setup = svm_vcpu_setup, | 1731 | .vcpu_setup = svm_vcpu_setup, |
| 1732 | .patch_hypercall = svm_patch_hypercall, | ||
| 1720 | }; | 1733 | }; |
| 1721 | 1734 | ||
| 1722 | static int __init svm_init(void) | 1735 | static int __init svm_init(void) |
diff --git a/drivers/kvm/vmx.c b/drivers/kvm/vmx.c index d1198e2b2b5d..0198d400037f 100644 --- a/drivers/kvm/vmx.c +++ b/drivers/kvm/vmx.c | |||
| @@ -1469,6 +1469,18 @@ static int handle_io(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run) | |||
| 1469 | return 0; | 1469 | return 0; |
| 1470 | } | 1470 | } |
| 1471 | 1471 | ||
| 1472 | static void | ||
| 1473 | vmx_patch_hypercall(struct kvm_vcpu *vcpu, unsigned char *hypercall) | ||
| 1474 | { | ||
| 1475 | /* | ||
| 1476 | * Patch in the VMCALL instruction: | ||
| 1477 | */ | ||
| 1478 | hypercall[0] = 0x0f; | ||
| 1479 | hypercall[1] = 0x01; | ||
| 1480 | hypercall[2] = 0xc1; | ||
| 1481 | hypercall[3] = 0xc3; | ||
| 1482 | } | ||
| 1483 | |||
| 1472 | static int handle_cr(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run) | 1484 | static int handle_cr(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run) |
| 1473 | { | 1485 | { |
| 1474 | u64 exit_qualification; | 1486 | u64 exit_qualification; |
| @@ -2064,6 +2076,7 @@ static struct kvm_arch_ops vmx_arch_ops = { | |||
| 2064 | .run = vmx_vcpu_run, | 2076 | .run = vmx_vcpu_run, |
| 2065 | .skip_emulated_instruction = skip_emulated_instruction, | 2077 | .skip_emulated_instruction = skip_emulated_instruction, |
| 2066 | .vcpu_setup = vmx_vcpu_setup, | 2078 | .vcpu_setup = vmx_vcpu_setup, |
| 2079 | .patch_hypercall = vmx_patch_hypercall, | ||
| 2067 | }; | 2080 | }; |
| 2068 | 2081 | ||
| 2069 | static int __init vmx_init(void) | 2082 | static int __init vmx_init(void) |
diff --git a/include/linux/kvm_para.h b/include/linux/kvm_para.h new file mode 100644 index 000000000000..74be5c1002ec --- /dev/null +++ b/include/linux/kvm_para.h | |||
| @@ -0,0 +1,55 @@ | |||
| 1 | #ifndef __LINUX_KVM_PARA_H | ||
| 2 | #define __LINUX_KVM_PARA_H | ||
| 3 | |||
| 4 | /* | ||
| 5 | * Guest OS interface for KVM paravirtualization | ||
| 6 | * | ||
| 7 | * Note: this interface is totally experimental, and is certain to change | ||
| 8 | * as we make progress. | ||
| 9 | */ | ||
| 10 | |||
| 11 | /* | ||
| 12 | * Per-VCPU descriptor area shared between guest and host. Writable to | ||
| 13 | * both guest and host. Registered with the host by the guest when | ||
| 14 | * a guest acknowledges paravirtual mode. | ||
| 15 | * | ||
| 16 | * NOTE: all addresses are guest-physical addresses (gpa), to make it | ||
| 17 | * easier for the hypervisor to map between the various addresses. | ||
| 18 | */ | ||
| 19 | struct kvm_vcpu_para_state { | ||
| 20 | /* | ||
| 21 | * API version information for compatibility. If there's any support | ||
| 22 | * mismatch (too old host trying to execute too new guest) then | ||
| 23 | * the host will deny entry into paravirtual mode. Any other | ||
| 24 | * combination (new host + old guest and new host + new guest) | ||
| 25 | * is supposed to work - new host versions will support all old | ||
| 26 | * guest API versions. | ||
| 27 | */ | ||
| 28 | u32 guest_version; | ||
| 29 | u32 host_version; | ||
| 30 | u32 size; | ||
| 31 | u32 ret; | ||
| 32 | |||
| 33 | /* | ||
| 34 | * The address of the vm exit instruction (VMCALL or VMMCALL), | ||
| 35 | * which the host will patch according to the CPU model the | ||
| 36 | * VM runs on: | ||
| 37 | */ | ||
| 38 | u64 hypercall_gpa; | ||
| 39 | |||
| 40 | } __attribute__ ((aligned(PAGE_SIZE))); | ||
| 41 | |||
| 42 | #define KVM_PARA_API_VERSION 1 | ||
| 43 | |||
| 44 | /* | ||
| 45 | * This is used for an RDMSR's ECX parameter to probe for a KVM host. | ||
| 46 | * Hopefully no CPU vendor will use up this number. This is placed well | ||
| 47 | * out of way of the typical space occupied by CPU vendors' MSR indices, | ||
| 48 | * and we think (or at least hope) it wont be occupied in the future | ||
| 49 | * either. | ||
| 50 | */ | ||
| 51 | #define MSR_KVM_API_MAGIC 0x87655678 | ||
| 52 | |||
| 53 | #define KVM_EINVAL 1 | ||
| 54 | |||
| 55 | #endif | ||
