blob: 926f93fd722d8bdfb1bca7bec7b4c18269b19cd4 [file] [log] [blame]
Alexander Graf2a342ed2010-07-29 14:47:48 +02001/*
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
Alexander Grafd17051c2010-07-29 14:47:57 +020033#define KVM_MAGIC_PAGE (-4096L)
34#define magic_var(x) KVM_MAGIC_PAGE + offsetof(struct kvm_vcpu_arch_shared, x)
35
Alexander Grafd1293c92010-07-29 14:47:59 +020036#define KVM_INST_LWZ 0x80000000
37#define KVM_INST_STW 0x90000000
38#define KVM_INST_LD 0xe8000000
39#define KVM_INST_STD 0xf8000000
40#define KVM_INST_NOP 0x60000000
41#define KVM_INST_B 0x48000000
42#define KVM_INST_B_MASK 0x03ffffff
43#define KVM_INST_B_MAX 0x01ffffff
44
Alexander Graf73a18102010-07-29 14:47:58 +020045#define KVM_MASK_RT 0x03e00000
Alexander Grafd1293c92010-07-29 14:47:59 +020046#define KVM_INST_MFMSR 0x7c0000a6
47#define KVM_INST_MFSPR_SPRG0 0x7c1042a6
48#define KVM_INST_MFSPR_SPRG1 0x7c1142a6
49#define KVM_INST_MFSPR_SPRG2 0x7c1242a6
50#define KVM_INST_MFSPR_SPRG3 0x7c1342a6
51#define KVM_INST_MFSPR_SRR0 0x7c1a02a6
52#define KVM_INST_MFSPR_SRR1 0x7c1b02a6
53#define KVM_INST_MFSPR_DAR 0x7c1302a6
54#define KVM_INST_MFSPR_DSISR 0x7c1202a6
55
56#define KVM_INST_MTSPR_SPRG0 0x7c1043a6
57#define KVM_INST_MTSPR_SPRG1 0x7c1143a6
58#define KVM_INST_MTSPR_SPRG2 0x7c1243a6
59#define KVM_INST_MTSPR_SPRG3 0x7c1343a6
60#define KVM_INST_MTSPR_SRR0 0x7c1a03a6
61#define KVM_INST_MTSPR_SRR1 0x7c1b03a6
62#define KVM_INST_MTSPR_DAR 0x7c1303a6
63#define KVM_INST_MTSPR_DSISR 0x7c1203a6
Alexander Graf73a18102010-07-29 14:47:58 +020064
Alexander Grafd1290b152010-07-29 14:48:00 +020065#define KVM_INST_TLBSYNC 0x7c00046c
66
Alexander Graf73a18102010-07-29 14:47:58 +020067static bool kvm_patching_worked = true;
Alexander Graf2d4f5672010-07-29 14:48:01 +020068static char kvm_tmp[1024 * 1024];
69static int kvm_tmp_index;
Alexander Graf73a18102010-07-29 14:47:58 +020070
71static inline void kvm_patch_ins(u32 *inst, u32 new_inst)
72{
73 *inst = new_inst;
74 flush_icache_range((ulong)inst, (ulong)inst + 4);
75}
76
Alexander Grafd1293c92010-07-29 14:47:59 +020077static void kvm_patch_ins_ld(u32 *inst, long addr, u32 rt)
78{
79#ifdef CONFIG_64BIT
80 kvm_patch_ins(inst, KVM_INST_LD | rt | (addr & 0x0000fffc));
81#else
82 kvm_patch_ins(inst, KVM_INST_LWZ | rt | ((addr + 4) & 0x0000fffc));
83#endif
84}
85
86static void kvm_patch_ins_lwz(u32 *inst, long addr, u32 rt)
87{
88 kvm_patch_ins(inst, KVM_INST_LWZ | rt | (addr & 0x0000ffff));
89}
90
91static void kvm_patch_ins_std(u32 *inst, long addr, u32 rt)
92{
93#ifdef CONFIG_64BIT
94 kvm_patch_ins(inst, KVM_INST_STD | rt | (addr & 0x0000fffc));
95#else
96 kvm_patch_ins(inst, KVM_INST_STW | rt | ((addr + 4) & 0x0000fffc));
97#endif
98}
99
100static void kvm_patch_ins_stw(u32 *inst, long addr, u32 rt)
101{
102 kvm_patch_ins(inst, KVM_INST_STW | rt | (addr & 0x0000fffc));
103}
104
Alexander Grafd1290b152010-07-29 14:48:00 +0200105static void kvm_patch_ins_nop(u32 *inst)
106{
107 kvm_patch_ins(inst, KVM_INST_NOP);
108}
109
Alexander Graf2d4f5672010-07-29 14:48:01 +0200110static u32 *kvm_alloc(int len)
111{
112 u32 *p;
113
114 if ((kvm_tmp_index + len) > ARRAY_SIZE(kvm_tmp)) {
115 printk(KERN_ERR "KVM: No more space (%d + %d)\n",
116 kvm_tmp_index, len);
117 kvm_patching_worked = false;
118 return NULL;
119 }
120
121 p = (void*)&kvm_tmp[kvm_tmp_index];
122 kvm_tmp_index += len;
123
124 return p;
125}
126
Alexander Graf73a18102010-07-29 14:47:58 +0200127static void kvm_map_magic_page(void *data)
128{
129 kvm_hypercall2(KVM_HC_PPC_MAP_MAGIC_PAGE,
130 KVM_MAGIC_PAGE, /* Physical Address */
131 KVM_MAGIC_PAGE); /* Effective Address */
132}
133
134static void kvm_check_ins(u32 *inst)
135{
136 u32 _inst = *inst;
137 u32 inst_no_rt = _inst & ~KVM_MASK_RT;
138 u32 inst_rt = _inst & KVM_MASK_RT;
139
140 switch (inst_no_rt) {
Alexander Grafd1293c92010-07-29 14:47:59 +0200141 /* Loads */
142 case KVM_INST_MFMSR:
143 kvm_patch_ins_ld(inst, magic_var(msr), inst_rt);
144 break;
145 case KVM_INST_MFSPR_SPRG0:
146 kvm_patch_ins_ld(inst, magic_var(sprg0), inst_rt);
147 break;
148 case KVM_INST_MFSPR_SPRG1:
149 kvm_patch_ins_ld(inst, magic_var(sprg1), inst_rt);
150 break;
151 case KVM_INST_MFSPR_SPRG2:
152 kvm_patch_ins_ld(inst, magic_var(sprg2), inst_rt);
153 break;
154 case KVM_INST_MFSPR_SPRG3:
155 kvm_patch_ins_ld(inst, magic_var(sprg3), inst_rt);
156 break;
157 case KVM_INST_MFSPR_SRR0:
158 kvm_patch_ins_ld(inst, magic_var(srr0), inst_rt);
159 break;
160 case KVM_INST_MFSPR_SRR1:
161 kvm_patch_ins_ld(inst, magic_var(srr1), inst_rt);
162 break;
163 case KVM_INST_MFSPR_DAR:
164 kvm_patch_ins_ld(inst, magic_var(dar), inst_rt);
165 break;
166 case KVM_INST_MFSPR_DSISR:
167 kvm_patch_ins_lwz(inst, magic_var(dsisr), inst_rt);
168 break;
169
170 /* Stores */
171 case KVM_INST_MTSPR_SPRG0:
172 kvm_patch_ins_std(inst, magic_var(sprg0), inst_rt);
173 break;
174 case KVM_INST_MTSPR_SPRG1:
175 kvm_patch_ins_std(inst, magic_var(sprg1), inst_rt);
176 break;
177 case KVM_INST_MTSPR_SPRG2:
178 kvm_patch_ins_std(inst, magic_var(sprg2), inst_rt);
179 break;
180 case KVM_INST_MTSPR_SPRG3:
181 kvm_patch_ins_std(inst, magic_var(sprg3), inst_rt);
182 break;
183 case KVM_INST_MTSPR_SRR0:
184 kvm_patch_ins_std(inst, magic_var(srr0), inst_rt);
185 break;
186 case KVM_INST_MTSPR_SRR1:
187 kvm_patch_ins_std(inst, magic_var(srr1), inst_rt);
188 break;
189 case KVM_INST_MTSPR_DAR:
190 kvm_patch_ins_std(inst, magic_var(dar), inst_rt);
191 break;
192 case KVM_INST_MTSPR_DSISR:
193 kvm_patch_ins_stw(inst, magic_var(dsisr), inst_rt);
194 break;
Alexander Grafd1290b152010-07-29 14:48:00 +0200195
196 /* Nops */
197 case KVM_INST_TLBSYNC:
198 kvm_patch_ins_nop(inst);
199 break;
Alexander Graf73a18102010-07-29 14:47:58 +0200200 }
201
202 switch (_inst) {
203 }
204}
205
206static void kvm_use_magic_page(void)
207{
208 u32 *p;
209 u32 *start, *end;
210 u32 tmp;
211
212 /* Tell the host to map the magic page to -4096 on all CPUs */
213 on_each_cpu(kvm_map_magic_page, NULL, 1);
214
215 /* Quick self-test to see if the mapping works */
216 if (__get_user(tmp, (u32*)KVM_MAGIC_PAGE)) {
217 kvm_patching_worked = false;
218 return;
219 }
220
221 /* Now loop through all code and find instructions */
222 start = (void*)_stext;
223 end = (void*)_etext;
224
225 for (p = start; p < end; p++)
226 kvm_check_ins(p);
227
228 printk(KERN_INFO "KVM: Live patching for a fast VM %s\n",
229 kvm_patching_worked ? "worked" : "failed");
230}
231
Alexander Graf2a342ed2010-07-29 14:47:48 +0200232unsigned long kvm_hypercall(unsigned long *in,
233 unsigned long *out,
234 unsigned long nr)
235{
236 unsigned long register r0 asm("r0");
237 unsigned long register r3 asm("r3") = in[0];
238 unsigned long register r4 asm("r4") = in[1];
239 unsigned long register r5 asm("r5") = in[2];
240 unsigned long register r6 asm("r6") = in[3];
241 unsigned long register r7 asm("r7") = in[4];
242 unsigned long register r8 asm("r8") = in[5];
243 unsigned long register r9 asm("r9") = in[6];
244 unsigned long register r10 asm("r10") = in[7];
245 unsigned long register r11 asm("r11") = nr;
246 unsigned long register r12 asm("r12");
247
248 asm volatile("bl kvm_hypercall_start"
249 : "=r"(r0), "=r"(r3), "=r"(r4), "=r"(r5), "=r"(r6),
250 "=r"(r7), "=r"(r8), "=r"(r9), "=r"(r10), "=r"(r11),
251 "=r"(r12)
252 : "r"(r3), "r"(r4), "r"(r5), "r"(r6), "r"(r7), "r"(r8),
253 "r"(r9), "r"(r10), "r"(r11)
254 : "memory", "cc", "xer", "ctr", "lr");
255
256 out[0] = r4;
257 out[1] = r5;
258 out[2] = r6;
259 out[3] = r7;
260 out[4] = r8;
261 out[5] = r9;
262 out[6] = r10;
263 out[7] = r11;
264
265 return r3;
266}
267EXPORT_SYMBOL_GPL(kvm_hypercall);
Alexander Graf73a18102010-07-29 14:47:58 +0200268
269static int kvm_para_setup(void)
270{
271 extern u32 kvm_hypercall_start;
272 struct device_node *hyper_node;
273 u32 *insts;
274 int len, i;
275
276 hyper_node = of_find_node_by_path("/hypervisor");
277 if (!hyper_node)
278 return -1;
279
280 insts = (u32*)of_get_property(hyper_node, "hcall-instructions", &len);
281 if (len % 4)
282 return -1;
283 if (len > (4 * 4))
284 return -1;
285
286 for (i = 0; i < (len / 4); i++)
287 kvm_patch_ins(&(&kvm_hypercall_start)[i], insts[i]);
288
289 return 0;
290}
291
Alexander Graf2d4f5672010-07-29 14:48:01 +0200292static __init void kvm_free_tmp(void)
293{
294 unsigned long start, end;
295
296 start = (ulong)&kvm_tmp[kvm_tmp_index + (PAGE_SIZE - 1)] & PAGE_MASK;
297 end = (ulong)&kvm_tmp[ARRAY_SIZE(kvm_tmp)] & PAGE_MASK;
298
299 /* Free the tmp space we don't need */
300 for (; start < end; start += PAGE_SIZE) {
301 ClearPageReserved(virt_to_page(start));
302 init_page_count(virt_to_page(start));
303 free_page(start);
304 totalram_pages++;
305 }
306}
307
Alexander Graf73a18102010-07-29 14:47:58 +0200308static int __init kvm_guest_init(void)
309{
310 if (!kvm_para_available())
Alexander Graf2d4f5672010-07-29 14:48:01 +0200311 goto free_tmp;
Alexander Graf73a18102010-07-29 14:47:58 +0200312
313 if (kvm_para_setup())
Alexander Graf2d4f5672010-07-29 14:48:01 +0200314 goto free_tmp;
Alexander Graf73a18102010-07-29 14:47:58 +0200315
316 if (kvm_para_has_feature(KVM_FEATURE_MAGIC_PAGE))
317 kvm_use_magic_page();
318
Alexander Graf2d4f5672010-07-29 14:48:01 +0200319free_tmp:
320 kvm_free_tmp();
321
Alexander Graf73a18102010-07-29 14:47:58 +0200322 return 0;
323}
324
325postcore_initcall(kvm_guest_init);