diff options
Diffstat (limited to 'arch')
-rw-r--r-- | arch/powerpc/include/asm/kvm_para.h | 114 | ||||
-rw-r--r-- | arch/powerpc/include/asm/kvm_ppc.h | 1 | ||||
-rw-r--r-- | arch/powerpc/kernel/Makefile | 2 | ||||
-rw-r--r-- | arch/powerpc/kernel/kvm.c | 68 | ||||
-rw-r--r-- | arch/powerpc/kvm/book3s.c | 9 | ||||
-rw-r--r-- | arch/powerpc/kvm/booke.c | 10 | ||||
-rw-r--r-- | arch/powerpc/kvm/powerpc.c | 32 |
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 | ||
25 | struct kvm_vcpu_arch_shared { | 26 | struct 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 | |||
47 | static 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 | |||
61 | extern unsigned long kvm_hypercall(unsigned long *in, | ||
62 | unsigned long *out, | ||
63 | unsigned long nr); | ||
64 | |||
65 | #else | ||
66 | |||
39 | static inline int kvm_para_available(void) | 67 | static inline int kvm_para_available(void) |
40 | { | 68 | { |
41 | return 0; | 69 | return 0; |
42 | } | 70 | } |
43 | 71 | ||
72 | static 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 | |||
81 | static 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 | |||
93 | static 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 | |||
101 | static 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 | |||
110 | static 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 | |||
121 | static 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 | |||
133 | static 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 | |||
44 | static inline unsigned int kvm_arch_para_features(void) | 148 | static 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); | |||
107 | extern void kvmppc_booke_exit(void); | 107 | extern void kvmppc_booke_exit(void); |
108 | 108 | ||
109 | extern void kvmppc_core_destroy_mmu(struct kvm_vcpu *vcpu); | 109 | extern void kvmppc_core_destroy_mmu(struct kvm_vcpu *vcpu); |
110 | extern 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),) | |||
127 | obj-y += ppc_save_regs.o | 127 | obj-y += ppc_save_regs.o |
128 | endif | 128 | endif |
129 | 129 | ||
130 | obj-$(CONFIG_KVM_GUEST) += kvm.o | ||
131 | |||
130 | # Disable GCOV in odd or sensitive code | 132 | # Disable GCOV in odd or sensitive code |
131 | GCOV_PROFILE_prom_init.o := n | 133 | GCOV_PROFILE_prom_init.o := n |
132 | GCOV_PROFILE_ftrace.o := n | 134 | GCOV_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 | |||
33 | unsigned 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 | } | ||
68 | EXPORT_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 | ||
45 | int 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 | ||
46 | int kvmppc_emulate_mmio(struct kvm_run *run, struct kvm_vcpu *vcpu) | 78 | int kvmppc_emulate_mmio(struct kvm_run *run, struct kvm_vcpu *vcpu) |
47 | { | 79 | { |