aboutsummaryrefslogtreecommitdiffstats
path: root/arch/powerpc
diff options
context:
space:
mode:
authorAlexander Graf <agraf@suse.de>2010-07-29 08:47:48 -0400
committerAvi Kivity <avi@redhat.com>2010-10-24 04:50:45 -0400
commit2a342ed57756ad5d8af5456959433884367e5ab2 (patch)
treebb3f1d707916bc53f48919ace0d0f757c7e2083b /arch/powerpc
parenta73a9599e03eef1324d5aeecaebc1b339d2e1664 (diff)
KVM: PPC: Implement hypervisor interface
To communicate with KVM directly we need to plumb some sort of interface between the guest and KVM. Usually those interfaces use hypercalls. This hypercall implementation is described in the last patch of the series in a special documentation file. Please read that for further information. This patch implements stubs to handle KVM PPC hypercalls on the host and guest side alike. Signed-off-by: Alexander Graf <agraf@suse.de> Signed-off-by: Avi Kivity <avi@redhat.com>
Diffstat (limited to 'arch/powerpc')
-rw-r--r--arch/powerpc/include/asm/kvm_para.h114
-rw-r--r--arch/powerpc/include/asm/kvm_ppc.h1
-rw-r--r--arch/powerpc/kernel/Makefile2
-rw-r--r--arch/powerpc/kernel/kvm.c68
-rw-r--r--arch/powerpc/kvm/book3s.c9
-rw-r--r--arch/powerpc/kvm/booke.c10
-rw-r--r--arch/powerpc/kvm/powerpc.c32
7 files changed, 232 insertions, 4 deletions
diff --git a/arch/powerpc/include/asm/kvm_para.h b/arch/powerpc/include/asm/kvm_para.h
index e402999ba193..556fd59ee0f1 100644
--- a/arch/powerpc/include/asm/kvm_para.h
+++ b/arch/powerpc/include/asm/kvm_para.h
@@ -21,6 +21,7 @@
21#define __POWERPC_KVM_PARA_H__ 21#define __POWERPC_KVM_PARA_H__
22 22
23#include <linux/types.h> 23#include <linux/types.h>
24#include <linux/of.h>
24 25
25struct kvm_vcpu_arch_shared { 26struct kvm_vcpu_arch_shared {
26 __u64 sprg0; 27 __u64 sprg0;
@@ -34,16 +35,127 @@ struct kvm_vcpu_arch_shared {
34 __u32 dsisr; 35 __u32 dsisr;
35}; 36};
36 37
38#define KVM_SC_MAGIC_R0 0x4b564d21 /* "KVM!" */
39#define HC_VENDOR_KVM (42 << 16)
40#define HC_EV_SUCCESS 0
41#define HC_EV_UNIMPLEMENTED 12
42
37#ifdef __KERNEL__ 43#ifdef __KERNEL__
38 44
45#ifdef CONFIG_KVM_GUEST
46
47static inline int kvm_para_available(void)
48{
49 struct device_node *hyper_node;
50
51 hyper_node = of_find_node_by_path("/hypervisor");
52 if (!hyper_node)
53 return 0;
54
55 if (!of_device_is_compatible(hyper_node, "linux,kvm"))
56 return 0;
57
58 return 1;
59}
60
61extern unsigned long kvm_hypercall(unsigned long *in,
62 unsigned long *out,
63 unsigned long nr);
64
65#else
66
39static inline int kvm_para_available(void) 67static inline int kvm_para_available(void)
40{ 68{
41 return 0; 69 return 0;
42} 70}
43 71
72static unsigned long kvm_hypercall(unsigned long *in,
73 unsigned long *out,
74 unsigned long nr)
75{
76 return HC_EV_UNIMPLEMENTED;
77}
78
79#endif
80
81static inline long kvm_hypercall0_1(unsigned int nr, unsigned long *r2)
82{
83 unsigned long in[8];
84 unsigned long out[8];
85 unsigned long r;
86
87 r = kvm_hypercall(in, out, nr | HC_VENDOR_KVM);
88 *r2 = out[0];
89
90 return r;
91}
92
93static inline long kvm_hypercall0(unsigned int nr)
94{
95 unsigned long in[8];
96 unsigned long out[8];
97
98 return kvm_hypercall(in, out, nr | HC_VENDOR_KVM);
99}
100
101static inline long kvm_hypercall1(unsigned int nr, unsigned long p1)
102{
103 unsigned long in[8];
104 unsigned long out[8];
105
106 in[0] = p1;
107 return kvm_hypercall(in, out, nr | HC_VENDOR_KVM);
108}
109
110static inline long kvm_hypercall2(unsigned int nr, unsigned long p1,
111 unsigned long p2)
112{
113 unsigned long in[8];
114 unsigned long out[8];
115
116 in[0] = p1;
117 in[1] = p2;
118 return kvm_hypercall(in, out, nr | HC_VENDOR_KVM);
119}
120
121static inline long kvm_hypercall3(unsigned int nr, unsigned long p1,
122 unsigned long p2, unsigned long p3)
123{
124 unsigned long in[8];
125 unsigned long out[8];
126
127 in[0] = p1;
128 in[1] = p2;
129 in[2] = p3;
130 return kvm_hypercall(in, out, nr | HC_VENDOR_KVM);
131}
132
133static inline long kvm_hypercall4(unsigned int nr, unsigned long p1,
134 unsigned long p2, unsigned long p3,
135 unsigned long p4)
136{
137 unsigned long in[8];
138 unsigned long out[8];
139
140 in[0] = p1;
141 in[1] = p2;
142 in[2] = p3;
143 in[3] = p4;
144 return kvm_hypercall(in, out, nr | HC_VENDOR_KVM);
145}
146
147
44static inline unsigned int kvm_arch_para_features(void) 148static inline unsigned int kvm_arch_para_features(void)
45{ 149{
46 return 0; 150 unsigned long r;
151
152 if (!kvm_para_available())
153 return 0;
154
155 if(kvm_hypercall0_1(KVM_HC_FEATURES, &r))
156 return 0;
157
158 return r;
47} 159}
48 160
49#endif /* __KERNEL__ */ 161#endif /* __KERNEL__ */
diff --git a/arch/powerpc/include/asm/kvm_ppc.h b/arch/powerpc/include/asm/kvm_ppc.h
index 18d139ec2d22..ecb3bc74c344 100644
--- a/arch/powerpc/include/asm/kvm_ppc.h
+++ b/arch/powerpc/include/asm/kvm_ppc.h
@@ -107,6 +107,7 @@ extern int kvmppc_booke_init(void);
107extern void kvmppc_booke_exit(void); 107extern void kvmppc_booke_exit(void);
108 108
109extern void kvmppc_core_destroy_mmu(struct kvm_vcpu *vcpu); 109extern void kvmppc_core_destroy_mmu(struct kvm_vcpu *vcpu);
110extern int kvmppc_kvm_pv(struct kvm_vcpu *vcpu);
110 111
111/* 112/*
112 * Cuts out inst bits with ordering according to spec. 113 * Cuts out inst bits with ordering according to spec.
diff --git a/arch/powerpc/kernel/Makefile b/arch/powerpc/kernel/Makefile
index 1dda70129141..3a6955dc7191 100644
--- a/arch/powerpc/kernel/Makefile
+++ b/arch/powerpc/kernel/Makefile
@@ -127,6 +127,8 @@ ifneq ($(CONFIG_XMON)$(CONFIG_KEXEC),)
127obj-y += ppc_save_regs.o 127obj-y += ppc_save_regs.o
128endif 128endif
129 129
130obj-$(CONFIG_KVM_GUEST) += kvm.o
131
130# Disable GCOV in odd or sensitive code 132# Disable GCOV in odd or sensitive code
131GCOV_PROFILE_prom_init.o := n 133GCOV_PROFILE_prom_init.o := n
132GCOV_PROFILE_ftrace.o := n 134GCOV_PROFILE_ftrace.o := n
diff --git a/arch/powerpc/kernel/kvm.c b/arch/powerpc/kernel/kvm.c
new file mode 100644
index 000000000000..4f85505e4653
--- /dev/null
+++ b/arch/powerpc/kernel/kvm.c
@@ -0,0 +1,68 @@
1/*
2 * Copyright (C) 2010 SUSE Linux Products GmbH. All rights reserved.
3 *
4 * Authors:
5 * Alexander Graf <agraf@suse.de>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License, version 2, as
9 * published by the Free Software Foundation.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 */
20
21#include <linux/kvm_host.h>
22#include <linux/init.h>
23#include <linux/kvm_para.h>
24#include <linux/slab.h>
25#include <linux/of.h>
26
27#include <asm/reg.h>
28#include <asm/kvm_ppc.h>
29#include <asm/sections.h>
30#include <asm/cacheflush.h>
31#include <asm/disassemble.h>
32
33unsigned long kvm_hypercall(unsigned long *in,
34 unsigned long *out,
35 unsigned long nr)
36{
37 unsigned long register r0 asm("r0");
38 unsigned long register r3 asm("r3") = in[0];
39 unsigned long register r4 asm("r4") = in[1];
40 unsigned long register r5 asm("r5") = in[2];
41 unsigned long register r6 asm("r6") = in[3];
42 unsigned long register r7 asm("r7") = in[4];
43 unsigned long register r8 asm("r8") = in[5];
44 unsigned long register r9 asm("r9") = in[6];
45 unsigned long register r10 asm("r10") = in[7];
46 unsigned long register r11 asm("r11") = nr;
47 unsigned long register r12 asm("r12");
48
49 asm volatile("bl kvm_hypercall_start"
50 : "=r"(r0), "=r"(r3), "=r"(r4), "=r"(r5), "=r"(r6),
51 "=r"(r7), "=r"(r8), "=r"(r9), "=r"(r10), "=r"(r11),
52 "=r"(r12)
53 : "r"(r3), "r"(r4), "r"(r5), "r"(r6), "r"(r7), "r"(r8),
54 "r"(r9), "r"(r10), "r"(r11)
55 : "memory", "cc", "xer", "ctr", "lr");
56
57 out[0] = r4;
58 out[1] = r5;
59 out[2] = r6;
60 out[3] = r7;
61 out[4] = r8;
62 out[5] = r9;
63 out[6] = r10;
64 out[7] = r11;
65
66 return r3;
67}
68EXPORT_SYMBOL_GPL(kvm_hypercall);
diff --git a/arch/powerpc/kvm/book3s.c b/arch/powerpc/kvm/book3s.c
index cfd7fe5c3a62..5cb5f0d9381f 100644
--- a/arch/powerpc/kvm/book3s.c
+++ b/arch/powerpc/kvm/book3s.c
@@ -947,10 +947,10 @@ program_interrupt:
947 break; 947 break;
948 } 948 }
949 case BOOK3S_INTERRUPT_SYSCALL: 949 case BOOK3S_INTERRUPT_SYSCALL:
950 // XXX make user settable
951 if (vcpu->arch.osi_enabled && 950 if (vcpu->arch.osi_enabled &&
952 (((u32)kvmppc_get_gpr(vcpu, 3)) == OSI_SC_MAGIC_R3) && 951 (((u32)kvmppc_get_gpr(vcpu, 3)) == OSI_SC_MAGIC_R3) &&
953 (((u32)kvmppc_get_gpr(vcpu, 4)) == OSI_SC_MAGIC_R4)) { 952 (((u32)kvmppc_get_gpr(vcpu, 4)) == OSI_SC_MAGIC_R4)) {
953 /* MOL hypercalls */
954 u64 *gprs = run->osi.gprs; 954 u64 *gprs = run->osi.gprs;
955 int i; 955 int i;
956 956
@@ -959,8 +959,13 @@ program_interrupt:
959 gprs[i] = kvmppc_get_gpr(vcpu, i); 959 gprs[i] = kvmppc_get_gpr(vcpu, i);
960 vcpu->arch.osi_needed = 1; 960 vcpu->arch.osi_needed = 1;
961 r = RESUME_HOST_NV; 961 r = RESUME_HOST_NV;
962 962 } else if (!(vcpu->arch.shared->msr & MSR_PR) &&
963 (((u32)kvmppc_get_gpr(vcpu, 0)) == KVM_SC_MAGIC_R0)) {
964 /* KVM PV hypercalls */
965 kvmppc_set_gpr(vcpu, 3, kvmppc_kvm_pv(vcpu));
966 r = RESUME_GUEST;
963 } else { 967 } else {
968 /* Guest syscalls */
964 vcpu->stat.syscall_exits++; 969 vcpu->stat.syscall_exits++;
965 kvmppc_book3s_queue_irqprio(vcpu, exit_nr); 970 kvmppc_book3s_queue_irqprio(vcpu, exit_nr);
966 r = RESUME_GUEST; 971 r = RESUME_GUEST;
diff --git a/arch/powerpc/kvm/booke.c b/arch/powerpc/kvm/booke.c
index b2c8c423c4d5..13e0747178e3 100644
--- a/arch/powerpc/kvm/booke.c
+++ b/arch/powerpc/kvm/booke.c
@@ -338,7 +338,15 @@ int kvmppc_handle_exit(struct kvm_run *run, struct kvm_vcpu *vcpu,
338 break; 338 break;
339 339
340 case BOOKE_INTERRUPT_SYSCALL: 340 case BOOKE_INTERRUPT_SYSCALL:
341 kvmppc_booke_queue_irqprio(vcpu, BOOKE_IRQPRIO_SYSCALL); 341 if (!(vcpu->arch.shared->msr & MSR_PR) &&
342 (((u32)kvmppc_get_gpr(vcpu, 0)) == KVM_SC_MAGIC_R0)) {
343 /* KVM PV hypercalls */
344 kvmppc_set_gpr(vcpu, 3, kvmppc_kvm_pv(vcpu));
345 r = RESUME_GUEST;
346 } else {
347 /* Guest syscalls */
348 kvmppc_booke_queue_irqprio(vcpu, BOOKE_IRQPRIO_SYSCALL);
349 }
342 kvmppc_account_exit(vcpu, SYSCALL_EXITS); 350 kvmppc_account_exit(vcpu, SYSCALL_EXITS);
343 r = RESUME_GUEST; 351 r = RESUME_GUEST;
344 break; 352 break;
diff --git a/arch/powerpc/kvm/powerpc.c b/arch/powerpc/kvm/powerpc.c
index 22f6fa2982f2..a4cf4b47e232 100644
--- a/arch/powerpc/kvm/powerpc.c
+++ b/arch/powerpc/kvm/powerpc.c
@@ -42,6 +42,38 @@ int kvm_arch_vcpu_runnable(struct kvm_vcpu *v)
42 !!(v->arch.pending_exceptions); 42 !!(v->arch.pending_exceptions);
43} 43}
44 44
45int kvmppc_kvm_pv(struct kvm_vcpu *vcpu)
46{
47 int nr = kvmppc_get_gpr(vcpu, 11);
48 int r;
49 unsigned long __maybe_unused param1 = kvmppc_get_gpr(vcpu, 3);
50 unsigned long __maybe_unused param2 = kvmppc_get_gpr(vcpu, 4);
51 unsigned long __maybe_unused param3 = kvmppc_get_gpr(vcpu, 5);
52 unsigned long __maybe_unused param4 = kvmppc_get_gpr(vcpu, 6);
53 unsigned long r2 = 0;
54
55 if (!(vcpu->arch.shared->msr & MSR_SF)) {
56 /* 32 bit mode */
57 param1 &= 0xffffffff;
58 param2 &= 0xffffffff;
59 param3 &= 0xffffffff;
60 param4 &= 0xffffffff;
61 }
62
63 switch (nr) {
64 case HC_VENDOR_KVM | KVM_HC_FEATURES:
65 r = HC_EV_SUCCESS;
66
67 /* Second return value is in r4 */
68 kvmppc_set_gpr(vcpu, 4, r2);
69 break;
70 default:
71 r = HC_EV_UNIMPLEMENTED;
72 break;
73 }
74
75 return r;
76}
45 77
46int kvmppc_emulate_mmio(struct kvm_run *run, struct kvm_vcpu *vcpu) 78int kvmppc_emulate_mmio(struct kvm_run *run, struct kvm_vcpu *vcpu)
47{ 79{