aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorIngo Molnar <mingo@elte.hu>2007-02-19 07:37:47 -0500
committerAvi Kivity <avi@qumranet.com>2007-03-04 04:12:40 -0500
commit102d8325a1d2f266d3d0a03fdde948544e72c12d (patch)
tree21024c8b9b2b702c79200343e26f14f075da0479
parent5972e9535e94bf875eb8eab8a667ba04c7583874 (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.h6
-rw-r--r--drivers/kvm/kvm_main.c73
-rw-r--r--drivers/kvm/svm.c13
-rw-r--r--drivers/kvm/vmx.c13
-rw-r--r--include/linux/kvm_para.h55
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
387extern struct kvm_stat kvm_stat; 393extern 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 */
1210static 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;
1267err_kunmap_skip:
1268 kunmap_atomic(para_state, KM_USER0);
1269 return 0;
1270err_gp:
1271 return 1;
1272}
1273
1207int kvm_get_msr_common(struct kvm_vcpu *vcpu, u32 msr, u64 *pdata) 1274int 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
1672static void
1673svm_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
1672static struct kvm_arch_ops svm_arch_ops = { 1684static 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
1722static int __init svm_init(void) 1735static 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
1472static void
1473vmx_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
1472static int handle_cr(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run) 1484static 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
2069static int __init vmx_init(void) 2082static 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 */
19struct 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