Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/cooloney...
[linux-2.6] / 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);
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 static void prepare_for_halt(struct kvm_vcpu *vcpu)
290 {
291         vcpu->arch.timer_pending = 1;
292         vcpu->arch.timer_fired = 0;
293 }
294
295 int kvm_pal_emul(struct kvm_vcpu *vcpu, struct kvm_run *run)
296 {
297
298         u64 gr28;
299         struct ia64_pal_retval result;
300         int ret = 1;
301
302         gr28 = kvm_get_pal_call_index(vcpu);
303         /*printk("pal_call index:%lx\n",gr28);*/
304         switch (gr28) {
305         case PAL_CACHE_FLUSH:
306                 result = pal_cache_flush(vcpu);
307                 break;
308         case PAL_CACHE_SUMMARY:
309                 result = pal_cache_summary(vcpu);
310                 break;
311         case PAL_HALT_LIGHT:
312         {
313                 INIT_PAL_STATUS_SUCCESS(result);
314                 prepare_for_halt(vcpu);
315                 if (kvm_highest_pending_irq(vcpu) == -1)
316                         ret = kvm_emulate_halt(vcpu);
317         }
318                 break;
319
320         case PAL_FREQ_RATIOS:
321                 result = pal_freq_ratios(vcpu);
322                 break;
323
324         case PAL_FREQ_BASE:
325                 result = pal_freq_base(vcpu);
326                 break;
327
328         case PAL_LOGICAL_TO_PHYSICAL :
329                 result = pal_logical_to_physica(vcpu);
330                 break;
331
332         case PAL_VM_SUMMARY :
333                 result = pal_vm_summary(vcpu);
334                 break;
335
336         case PAL_VM_INFO :
337                 result = pal_vm_info(vcpu);
338                 break;
339         case PAL_PLATFORM_ADDR :
340                 result = pal_platform_addr(vcpu);
341                 break;
342         case PAL_CACHE_INFO:
343                 result = pal_cache_info(vcpu);
344                 break;
345         case PAL_PTCE_INFO:
346                 INIT_PAL_STATUS_SUCCESS(result);
347                 result.v1 = (1L << 32) | 1L;
348                 break;
349         case PAL_VM_PAGE_SIZE:
350                 result.status = ia64_pal_vm_page_size(&result.v0,
351                                                         &result.v1);
352                 break;
353         case PAL_RSE_INFO:
354                 result.status = ia64_pal_rse_info(&result.v0,
355                                         (pal_hints_u_t *)&result.v1);
356                 break;
357         case PAL_PROC_GET_FEATURES:
358                 result = pal_proc_get_features(vcpu);
359                 break;
360         case PAL_DEBUG_INFO:
361                 result.status = ia64_pal_debug_info(&result.v0,
362                                                         &result.v1);
363                 break;
364         case PAL_VERSION:
365                 result.status = ia64_pal_version(
366                                 (pal_version_u_t *)&result.v0,
367                                 (pal_version_u_t *)&result.v1);
368
369                 break;
370         case PAL_FIXED_ADDR:
371                 result.status = PAL_STATUS_SUCCESS;
372                 result.v0 = vcpu->vcpu_id;
373                 break;
374         default:
375                 INIT_PAL_STATUS_UNIMPLEMENTED(result);
376                 printk(KERN_WARNING"kvm: Unsupported pal call,"
377                                         " index:0x%lx\n", gr28);
378         }
379         set_pal_result(vcpu, result);
380         return ret;
381 }
382
383 static struct sal_ret_values sal_emulator(struct kvm *kvm,
384                                 long index, unsigned long in1,
385                                 unsigned long in2, unsigned long in3,
386                                 unsigned long in4, unsigned long in5,
387                                 unsigned long in6, unsigned long in7)
388 {
389         unsigned long r9  = 0;
390         unsigned long r10 = 0;
391         long r11 = 0;
392         long status;
393
394         status = 0;
395         switch (index) {
396         case SAL_FREQ_BASE:
397                 status = ia64_sal_freq_base(in1, &r9, &r10);
398                 break;
399         case SAL_PCI_CONFIG_READ:
400                 printk(KERN_WARNING"kvm: Not allowed to call here!"
401                         " SAL_PCI_CONFIG_READ\n");
402                 break;
403         case SAL_PCI_CONFIG_WRITE:
404                 printk(KERN_WARNING"kvm: Not allowed to call here!"
405                         " SAL_PCI_CONFIG_WRITE\n");
406                 break;
407         case SAL_SET_VECTORS:
408                 if (in1 == SAL_VECTOR_OS_BOOT_RENDEZ) {
409                         if (in4 != 0 || in5 != 0 || in6 != 0 || in7 != 0) {
410                                 status = -2;
411                         } else {
412                                 kvm->arch.rdv_sal_data.boot_ip = in2;
413                                 kvm->arch.rdv_sal_data.boot_gp = in3;
414                         }
415                         printk("Rendvous called! iip:%lx\n\n", in2);
416                 } else
417                         printk(KERN_WARNING"kvm: CALLED SAL_SET_VECTORS %lu."
418                                                         "ignored...\n", in1);
419                 break;
420         case SAL_GET_STATE_INFO:
421                 /* No more info.  */
422                 status = -5;
423                 r9 = 0;
424                 break;
425         case SAL_GET_STATE_INFO_SIZE:
426                 /* Return a dummy size.  */
427                 status = 0;
428                 r9 = 128;
429                 break;
430         case SAL_CLEAR_STATE_INFO:
431                 /* Noop.  */
432                 break;
433         case SAL_MC_RENDEZ:
434                 printk(KERN_WARNING
435                         "kvm: called SAL_MC_RENDEZ. ignored...\n");
436                 break;
437         case SAL_MC_SET_PARAMS:
438                 printk(KERN_WARNING
439                         "kvm: called  SAL_MC_SET_PARAMS.ignored!\n");
440                 break;
441         case SAL_CACHE_FLUSH:
442                 if (1) {
443                         /*Flush using SAL.
444                         This method is faster but has a side
445                         effect on other vcpu running on
446                         this cpu.  */
447                         status = ia64_sal_cache_flush(in1);
448                 } else {
449                         /*Maybe need to implement the method
450                         without side effect!*/
451                         status = 0;
452                 }
453                 break;
454         case SAL_CACHE_INIT:
455                 printk(KERN_WARNING
456                         "kvm: called SAL_CACHE_INIT.  ignored...\n");
457                 break;
458         case SAL_UPDATE_PAL:
459                 printk(KERN_WARNING
460                         "kvm: CALLED SAL_UPDATE_PAL.  ignored...\n");
461                 break;
462         default:
463                 printk(KERN_WARNING"kvm: called SAL_CALL with unknown index."
464                                                 " index:%ld\n", index);
465                 status = -1;
466                 break;
467         }
468         return ((struct sal_ret_values) {status, r9, r10, r11});
469 }
470
471 static void kvm_get_sal_call_data(struct kvm_vcpu *vcpu, u64 *in0, u64 *in1,
472                 u64 *in2, u64 *in3, u64 *in4, u64 *in5, u64 *in6, u64 *in7){
473
474         struct exit_ctl_data *p;
475
476         p = kvm_get_exit_data(vcpu);
477
478         if (p) {
479                 if (p->exit_reason == EXIT_REASON_SAL_CALL) {
480                         *in0 = p->u.sal_data.in0;
481                         *in1 = p->u.sal_data.in1;
482                         *in2 = p->u.sal_data.in2;
483                         *in3 = p->u.sal_data.in3;
484                         *in4 = p->u.sal_data.in4;
485                         *in5 = p->u.sal_data.in5;
486                         *in6 = p->u.sal_data.in6;
487                         *in7 = p->u.sal_data.in7;
488                         return ;
489                 }
490         }
491         *in0 = 0;
492 }
493
494 void kvm_sal_emul(struct kvm_vcpu *vcpu)
495 {
496
497         struct sal_ret_values result;
498         u64 index, in1, in2, in3, in4, in5, in6, in7;
499
500         kvm_get_sal_call_data(vcpu, &index, &in1, &in2,
501                         &in3, &in4, &in5, &in6, &in7);
502         result = sal_emulator(vcpu->kvm, index, in1, in2, in3,
503                                         in4, in5, in6, in7);
504         set_sal_result(vcpu, result);
505 }