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 | ||