KVM: ia64: Add kvm sal/pal virtulization support
[linux-2.6.git] / arch / ia64 / kvm / kvm_fw.c
1 /*
2  * PAL/SAL call delegation
3  *
4  * Copyright (c) 2004 Li Susie <susie.li@intel.com>
5  * Copyright (c) 2005 Yu Ke <ke.yu@intel.com>
6  * Copyright (c) 2007 Xiantao Zhang <xiantao.zhang@intel.com>
7  *
8  * This program is free software; you can redistribute it and/or modify it
9  * under the terms and conditions of the GNU General Public License,
10  * version 2, as published by the Free Software Foundation.
11  *
12  * This program is distributed in the hope it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
15  * more details.
16  *
17  * You should have received a copy of the GNU General Public License along with
18  * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
19  * Place - Suite 330, Boston, MA 02111-1307 USA.
20  */
21
22 #include <linux/kvm_host.h>
23 #include <linux/smp.h>
24
25 #include "vti.h"
26 #include "misc.h"
27
28 #include <asm/pal.h>
29 #include <asm/sal.h>
30 #include <asm/tlb.h>
31
32 /*
33  * Handy macros to make sure that the PAL return values start out
34  * as something meaningful.
35  */
36 #define INIT_PAL_STATUS_UNIMPLEMENTED(x)                \
37         {                                               \
38                 x.status = PAL_STATUS_UNIMPLEMENTED;    \
39                 x.v0 = 0;                               \
40                 x.v1 = 0;                               \
41                 x.v2 = 0;                               \
42         }
43
44 #define INIT_PAL_STATUS_SUCCESS(x)                      \
45         {                                               \
46                 x.status = PAL_STATUS_SUCCESS;          \
47                 x.v0 = 0;                               \
48                 x.v1 = 0;                               \
49                 x.v2 = 0;                               \
50     }
51
52 static void kvm_get_pal_call_data(struct kvm_vcpu *vcpu,
53                 u64 *gr28, u64 *gr29, u64 *gr30, u64 *gr31) {
54         struct exit_ctl_data *p;
55
56         if (vcpu) {
57                 p = &vcpu->arch.exit_data;
58                 if (p->exit_reason == EXIT_REASON_PAL_CALL) {
59                         *gr28 = p->u.pal_data.gr28;
60                         *gr29 = p->u.pal_data.gr29;
61                         *gr30 = p->u.pal_data.gr30;
62                         *gr31 = p->u.pal_data.gr31;
63                         return ;
64                 }
65         }
66         printk(KERN_DEBUG"Failed to get vcpu pal data!!!\n");
67 }
68
69 static void set_pal_result(struct kvm_vcpu *vcpu,
70                 struct ia64_pal_retval result) {
71
72         struct exit_ctl_data *p;
73
74         p = kvm_get_exit_data(vcpu);
75         if (p && p->exit_reason == EXIT_REASON_PAL_CALL) {
76                 p->u.pal_data.ret = result;
77                 return ;
78         }
79         INIT_PAL_STATUS_UNIMPLEMENTED(p->u.pal_data.ret);
80 }
81
82 static void set_sal_result(struct kvm_vcpu *vcpu,
83                 struct sal_ret_values result) {
84         struct exit_ctl_data *p;
85
86         p = kvm_get_exit_data(vcpu);
87         if (p && p->exit_reason == EXIT_REASON_SAL_CALL) {
88                 p->u.sal_data.ret = result;
89                 return ;
90         }
91         printk(KERN_WARNING"Failed to set sal result!!\n");
92 }
93
94 struct cache_flush_args {
95         u64 cache_type;
96         u64 operation;
97         u64 progress;
98         long status;
99 };
100
101 cpumask_t cpu_cache_coherent_map;
102
103 static void remote_pal_cache_flush(void *data)
104 {
105         struct cache_flush_args *args = data;
106         long status;
107         u64 progress = args->progress;
108
109         status = ia64_pal_cache_flush(args->cache_type, args->operation,
110                                         &progress, NULL);
111         if (status != 0)
112         args->status = status;
113 }
114
115 static struct ia64_pal_retval pal_cache_flush(struct kvm_vcpu *vcpu)
116 {
117         u64 gr28, gr29, gr30, gr31;
118         struct ia64_pal_retval result = {0, 0, 0, 0};
119         struct cache_flush_args args = {0, 0, 0, 0};
120         long psr;
121
122         gr28 = gr29 = gr30 = gr31 = 0;
123         kvm_get_pal_call_data(vcpu, &gr28, &gr29, &gr30, &gr31);
124
125         if (gr31 != 0)
126                 printk(KERN_ERR"vcpu:%p called cache_flush error!\n", vcpu);
127
128         /* Always call Host Pal in int=1 */
129         gr30 &= ~PAL_CACHE_FLUSH_CHK_INTRS;
130         args.cache_type = gr29;
131         args.operation = gr30;
132         smp_call_function(remote_pal_cache_flush,
133                                 (void *)&args, 1, 1);
134         if (args.status != 0)
135                 printk(KERN_ERR"pal_cache_flush error!,"
136                                 "status:0x%lx\n", args.status);
137         /*
138          * Call Host PAL cache flush
139          * Clear psr.ic when call PAL_CACHE_FLUSH
140          */
141         local_irq_save(psr);
142         result.status = ia64_pal_cache_flush(gr29, gr30, &result.v1,
143                                                 &result.v0);
144         local_irq_restore(psr);
145         if (result.status != 0)
146                 printk(KERN_ERR"vcpu:%p crashed due to cache_flush err:%ld"
147                                 "in1:%lx,in2:%lx\n",
148                                 vcpu, result.status, gr29, gr30);
149
150 #if 0
151         if (gr29 == PAL_CACHE_TYPE_COHERENT) {
152                 cpus_setall(vcpu->arch.cache_coherent_map);
153                 cpu_clear(vcpu->cpu, vcpu->arch.cache_coherent_map);
154                 cpus_setall(cpu_cache_coherent_map);
155                 cpu_clear(vcpu->cpu, cpu_cache_coherent_map);
156         }
157 #endif
158         return result;
159 }
160
161 struct ia64_pal_retval pal_cache_summary(struct kvm_vcpu *vcpu)
162 {
163
164         struct ia64_pal_retval result;
165
166         PAL_CALL(result, PAL_CACHE_SUMMARY, 0, 0, 0);
167         return result;
168 }
169
170 static struct ia64_pal_retval pal_freq_base(struct kvm_vcpu *vcpu)
171 {
172
173         struct ia64_pal_retval result;
174
175         PAL_CALL(result, PAL_FREQ_BASE, 0, 0, 0);
176
177         /*
178          * PAL_FREQ_BASE may not be implemented in some platforms,
179          * call SAL instead.
180          */
181         if (result.v0 == 0) {
182                 result.status = ia64_sal_freq_base(SAL_FREQ_BASE_PLATFORM,
183                                                         &result.v0,
184                                                         &result.v1);
185                 result.v2 = 0;
186         }
187
188         return result;
189 }
190
191 static struct ia64_pal_retval pal_freq_ratios(struct kvm_vcpu *vcpu)
192 {
193
194         struct ia64_pal_retval result;
195
196         PAL_CALL(result, PAL_FREQ_RATIOS, 0, 0, 0);
197         return result;
198 }
199
200 static struct ia64_pal_retval pal_logical_to_physica(struct kvm_vcpu *vcpu)
201 {
202         struct ia64_pal_retval result;
203
204         INIT_PAL_STATUS_UNIMPLEMENTED(result);
205         return result;
206 }
207
208 static struct ia64_pal_retval pal_platform_addr(struct kvm_vcpu *vcpu)
209 {
210
211         struct ia64_pal_retval result;
212
213         INIT_PAL_STATUS_SUCCESS(result);
214         return result;
215 }
216
217 static struct ia64_pal_retval pal_proc_get_features(struct kvm_vcpu *vcpu)
218 {
219
220         struct ia64_pal_retval result = {0, 0, 0, 0};
221         long in0, in1, in2, in3;
222
223         kvm_get_pal_call_data(vcpu, &in0, &in1, &in2, &in3);
224         result.status = ia64_pal_proc_get_features(&result.v0, &result.v1,
225                         &result.v2, in2);
226
227         return result;
228 }
229
230 static struct ia64_pal_retval pal_cache_info(struct kvm_vcpu *vcpu)
231 {
232
233         pal_cache_config_info_t ci;
234         long status;
235         unsigned long in0, in1, in2, in3, r9, r10;
236
237         kvm_get_pal_call_data(vcpu, &in0, &in1, &in2, &in3);
238         status = ia64_pal_cache_config_info(in1, in2, &ci);
239         r9 = ci.pcci_info_1.pcci1_data;
240         r10 = ci.pcci_info_2.pcci2_data;
241         return ((struct ia64_pal_retval){status, r9, r10, 0});
242 }
243
244 #define GUEST_IMPL_VA_MSB       59
245 #define GUEST_RID_BITS          18
246
247 static struct ia64_pal_retval pal_vm_summary(struct kvm_vcpu *vcpu)
248 {
249
250         pal_vm_info_1_u_t vminfo1;
251         pal_vm_info_2_u_t vminfo2;
252         struct ia64_pal_retval result;
253
254         PAL_CALL(result, PAL_VM_SUMMARY, 0, 0, 0);
255         if (!result.status) {
256                 vminfo1.pvi1_val = result.v0;
257                 vminfo1.pal_vm_info_1_s.max_itr_entry = 8;
258                 vminfo1.pal_vm_info_1_s.max_dtr_entry = 8;
259                 result.v0 = vminfo1.pvi1_val;
260                 vminfo2.pal_vm_info_2_s.impl_va_msb = GUEST_IMPL_VA_MSB;
261                 vminfo2.pal_vm_info_2_s.rid_size = GUEST_RID_BITS;
262                 result.v1 = vminfo2.pvi2_val;
263         }
264
265         return result;
266 }
267
268 static struct ia64_pal_retval pal_vm_info(struct kvm_vcpu *vcpu)
269 {
270         struct ia64_pal_retval result;
271
272         INIT_PAL_STATUS_UNIMPLEMENTED(result);
273
274         return result;
275 }
276
277 static  u64 kvm_get_pal_call_index(struct kvm_vcpu *vcpu)
278 {
279         u64 index = 0;
280         struct exit_ctl_data *p;
281
282         p = kvm_get_exit_data(vcpu);
283         if (p && (p->exit_reason == EXIT_REASON_PAL_CALL))
284                 index = p->u.pal_data.gr28;
285
286         return index;
287 }
288
289 int kvm_pal_emul(struct kvm_vcpu *vcpu, struct kvm_run *run)
290 {
291
292         u64 gr28;
293         struct ia64_pal_retval result;
294         int ret = 1;
295
296         gr28 = kvm_get_pal_call_index(vcpu);
297         /*printk("pal_call index:%lx\n",gr28);*/
298         switch (gr28) {
299         case PAL_CACHE_FLUSH:
300                 result = pal_cache_flush(vcpu);
301                 break;
302         case PAL_CACHE_SUMMARY:
303                 result = pal_cache_summary(vcpu);
304                 break;
305         case PAL_HALT_LIGHT:
306         {
307                 vcpu->arch.timer_pending = 1;
308                 INIT_PAL_STATUS_SUCCESS(result);
309                 if (kvm_highest_pending_irq(vcpu) == -1)
310                         ret = kvm_emulate_halt(vcpu);
311
312         }
313                 break;
314
315         case PAL_FREQ_RATIOS:
316                 result = pal_freq_ratios(vcpu);
317                 break;
318
319         case PAL_FREQ_BASE:
320                 result = pal_freq_base(vcpu);
321                 break;
322
323         case PAL_LOGICAL_TO_PHYSICAL :
324                 result = pal_logical_to_physica(vcpu);
325                 break;
326
327         case PAL_VM_SUMMARY :
328                 result = pal_vm_summary(vcpu);
329                 break;
330
331         case PAL_VM_INFO :
332                 result = pal_vm_info(vcpu);
333                 break;
334         case PAL_PLATFORM_ADDR :
335                 result = pal_platform_addr(vcpu);
336                 break;
337         case PAL_CACHE_INFO:
338                 result = pal_cache_info(vcpu);
339                 break;
340         case PAL_PTCE_INFO:
341                 INIT_PAL_STATUS_SUCCESS(result);
342                 result.v1 = (1L << 32) | 1L;
343                 break;
344         case PAL_VM_PAGE_SIZE:
345                 result.status = ia64_pal_vm_page_size(&result.v0,
346                                                         &result.v1);
347                 break;
348         case PAL_RSE_INFO:
349                 result.status = ia64_pal_rse_info(&result.v0,
350                                         (pal_hints_u_t *)&result.v1);
351                 break;
352         case PAL_PROC_GET_FEATURES:
353                 result = pal_proc_get_features(vcpu);
354                 break;
355         case PAL_DEBUG_INFO:
356                 result.status = ia64_pal_debug_info(&result.v0,
357                                                         &result.v1);
358                 break;
359         case PAL_VERSION:
360                 result.status = ia64_pal_version(
361                                 (pal_version_u_t *)&result.v0,
362                                 (pal_version_u_t *)&result.v1);
363
364                 break;
365         case PAL_FIXED_ADDR:
366                 result.status = PAL_STATUS_SUCCESS;
367                 result.v0 = vcpu->vcpu_id;
368                 break;
369         default:
370                 INIT_PAL_STATUS_UNIMPLEMENTED(result);
371                 printk(KERN_WARNING"kvm: Unsupported pal call,"
372                                         " index:0x%lx\n", gr28);
373         }
374         set_pal_result(vcpu, result);
375         return ret;
376 }
377
378 static struct sal_ret_values sal_emulator(struct kvm *kvm,
379                                 long index, unsigned long in1,
380                                 unsigned long in2, unsigned long in3,
381                                 unsigned long in4, unsigned long in5,
382                                 unsigned long in6, unsigned long in7)
383 {
384         unsigned long r9  = 0;
385         unsigned long r10 = 0;
386         long r11 = 0;
387         long status;
388
389         status = 0;
390         switch (index) {
391         case SAL_FREQ_BASE:
392                 status = ia64_sal_freq_base(in1, &r9, &r10);
393                 break;
394         case SAL_PCI_CONFIG_READ:
395                 printk(KERN_WARNING"kvm: Not allowed to call here!"
396                         " SAL_PCI_CONFIG_READ\n");
397                 break;
398         case SAL_PCI_CONFIG_WRITE:
399                 printk(KERN_WARNING"kvm: Not allowed to call here!"
400                         " SAL_PCI_CONFIG_WRITE\n");
401                 break;
402         case SAL_SET_VECTORS:
403                 if (in1 == SAL_VECTOR_OS_BOOT_RENDEZ) {
404                         if (in4 != 0 || in5 != 0 || in6 != 0 || in7 != 0) {
405                                 status = -2;
406                         } else {
407                                 kvm->arch.rdv_sal_data.boot_ip = in2;
408                                 kvm->arch.rdv_sal_data.boot_gp = in3;
409                         }
410                         printk("Rendvous called! iip:%lx\n\n", in2);
411                 } else
412                         printk(KERN_WARNING"kvm: CALLED SAL_SET_VECTORS %lu."
413                                                         "ignored...\n", in1);
414                 break;
415         case SAL_GET_STATE_INFO:
416                 /* No more info.  */
417                 status = -5;
418                 r9 = 0;
419                 break;
420         case SAL_GET_STATE_INFO_SIZE:
421                 /* Return a dummy size.  */
422                 status = 0;
423                 r9 = 128;
424                 break;
425         case SAL_CLEAR_STATE_INFO:
426                 /* Noop.  */
427                 break;
428         case SAL_MC_RENDEZ:
429                 printk(KERN_WARNING
430                         "kvm: called SAL_MC_RENDEZ. ignored...\n");
431                 break;
432         case SAL_MC_SET_PARAMS:
433                 printk(KERN_WARNING
434                         "kvm: called  SAL_MC_SET_PARAMS.ignored!\n");
435                 break;
436         case SAL_CACHE_FLUSH:
437                 if (1) {
438                         /*Flush using SAL.
439                         This method is faster but has a side
440                         effect on other vcpu running on
441                         this cpu.  */
442                         status = ia64_sal_cache_flush(in1);
443                 } else {
444                         /*Maybe need to implement the method
445                         without side effect!*/
446                         status = 0;
447                 }
448                 break;
449         case SAL_CACHE_INIT:
450                 printk(KERN_WARNING
451                         "kvm: called SAL_CACHE_INIT.  ignored...\n");
452                 break;
453         case SAL_UPDATE_PAL:
454                 printk(KERN_WARNING
455                         "kvm: CALLED SAL_UPDATE_PAL.  ignored...\n");
456                 break;
457         default:
458                 printk(KERN_WARNING"kvm: called SAL_CALL with unknown index."
459                                                 " index:%ld\n", index);
460                 status = -1;
461                 break;
462         }
463         return ((struct sal_ret_values) {status, r9, r10, r11});
464 }
465
466 static void kvm_get_sal_call_data(struct kvm_vcpu *vcpu, u64 *in0, u64 *in1,
467                 u64 *in2, u64 *in3, u64 *in4, u64 *in5, u64 *in6, u64 *in7){
468
469         struct exit_ctl_data *p;
470
471         p = kvm_get_exit_data(vcpu);
472
473         if (p) {
474                 if (p->exit_reason == EXIT_REASON_SAL_CALL) {
475                         *in0 = p->u.sal_data.in0;
476                         *in1 = p->u.sal_data.in1;
477                         *in2 = p->u.sal_data.in2;
478                         *in3 = p->u.sal_data.in3;
479                         *in4 = p->u.sal_data.in4;
480                         *in5 = p->u.sal_data.in5;
481                         *in6 = p->u.sal_data.in6;
482                         *in7 = p->u.sal_data.in7;
483                         return ;
484                 }
485         }
486         *in0 = 0;
487 }
488
489 void kvm_sal_emul(struct kvm_vcpu *vcpu)
490 {
491
492         struct sal_ret_values result;
493         u64 index, in1, in2, in3, in4, in5, in6, in7;
494
495         kvm_get_sal_call_data(vcpu, &index, &in1, &in2,
496                         &in3, &in4, &in5, &in6, &in7);
497         result = sal_emulator(vcpu->kvm, index, in1, in2, in3,
498                                         in4, in5, in6, in7);
499         set_sal_result(vcpu, result);
500 }