source: trunk/packages/xen-3.1/xen-3.1/xen/arch/ia64/xen/hypercall.c @ 34

Last change on this file since 34 was 34, checked in by hartmans, 18 years ago

Add xen and xen-common

File size: 11.9 KB
Line 
1/*
2 * Hypercall implementations
3 *
4 * Copyright (C) 2005 Hewlett-Packard Co.
5 *      Dan Magenheimer (dan.magenheimer@hp.com)
6 *
7 */
8
9#include <xen/config.h>
10#include <xen/sched.h>
11#include <xen/hypercall.h>
12#include <xen/multicall.h>
13#include <xen/guest_access.h>
14#include <xen/mm.h>
15
16#include <linux/efi.h>  /* FOR EFI_UNIMPLEMENTED */
17#include <asm/sal.h>    /* FOR struct ia64_sal_retval */
18#include <asm/fpswa.h>  /* FOR struct fpswa_ret_t */
19
20#include <asm/vmx_vcpu.h>
21#include <asm/vcpu.h>
22#include <asm/dom_fw.h>
23#include <public/domctl.h>
24#include <public/sysctl.h>
25#include <public/event_channel.h>
26#include <public/memory.h>
27#include <public/sched.h>
28#include <xen/irq.h>
29#include <asm/hw_irq.h>
30#include <public/physdev.h>
31#include <xen/domain.h>
32#include <public/callback.h>
33#include <xen/event.h>
34#include <xen/perfc.h>
35
36extern long do_physdev_op(int cmd, XEN_GUEST_HANDLE(void) arg);
37extern long do_callback_op(int cmd, XEN_GUEST_HANDLE(void) arg);
38
39static IA64FAULT
40xen_hypercall (struct pt_regs *regs)
41{
42        uint32_t cmd = (uint32_t)regs->r2;
43        printk("Warning %s should not be called %d\n", __FUNCTION__, cmd);
44        return IA64_NO_FAULT;
45}
46
47static IA64FAULT
48xen_fast_hypercall (struct pt_regs *regs)
49{
50        uint32_t cmd = (uint32_t)regs->r2;
51        switch (cmd) {
52        case __HYPERVISOR_ia64_fast_eoi:
53                printk("Warning %s should not be called %d\n",
54                       __FUNCTION__, cmd);
55                break;
56        default:
57                regs->r8 = -ENOSYS;
58        }
59        return IA64_NO_FAULT;
60}
61
62long do_pirq_guest_eoi(int pirq)
63{
64        return pirq_guest_eoi(current->domain, pirq);
65}
66   
67
68static void
69fw_hypercall_ipi (struct pt_regs *regs)
70{
71        int cpu = regs->r14;
72        int vector = regs->r15;
73        struct vcpu *targ;
74        struct domain *d = current->domain;
75
76        /* Be sure the target exists.  */
77        if (cpu > MAX_VIRT_CPUS)
78                return;
79        targ = d->vcpu[cpu];
80        if (targ == NULL)
81                return;
82
83        if (vector == XEN_SAL_BOOT_RENDEZ_VEC
84            && (!targ->is_initialised
85                || test_bit(_VPF_down, &targ->pause_flags))) {
86
87                /* First start: initialize vpcu.  */
88                if (!targ->is_initialised) {
89                        struct vcpu_guest_context c;
90               
91                        memset (&c, 0, sizeof (c));
92
93                        if (arch_set_info_guest (targ, &c) != 0) {
94                                printk ("arch_boot_vcpu: failure\n");
95                                return;
96                        }
97                }
98                       
99                /* First or next rendez-vous: set registers.  */
100                vcpu_init_regs (targ);
101                vcpu_regs (targ)->cr_iip = d->arch.sal_data->boot_rdv_ip;
102                vcpu_regs (targ)->r1 = d->arch.sal_data->boot_rdv_r1;
103                vcpu_regs (targ)->b0 = FW_HYPERCALL_SAL_RETURN_PADDR;
104
105                if (test_and_clear_bit(_VPF_down,
106                                       &targ->pause_flags)) {
107                        vcpu_wake(targ);
108                        printk(XENLOG_INFO "arch_boot_vcpu: vcpu %d awaken\n",
109                               targ->vcpu_id);
110                }
111                else
112                        printk ("arch_boot_vcpu: huu, already awaken!\n");
113        }
114        else {
115                int running = targ->is_running;
116                vcpu_pend_interrupt(targ, vector);
117                vcpu_unblock(targ);
118                if (running)
119                        smp_send_event_check_cpu(targ->processor);
120        }
121        return;
122}
123
124static fpswa_ret_t
125fw_hypercall_fpswa (struct vcpu *v)
126{
127        return PSCBX(v, fpswa_ret);
128}
129
130IA64FAULT
131ia64_hypercall(struct pt_regs *regs)
132{
133        struct vcpu *v = current;
134        struct sal_ret_values x;
135        efi_status_t efi_ret_value;
136        fpswa_ret_t fpswa_ret;
137        IA64FAULT fault; 
138        unsigned long index = regs->r2 & FW_HYPERCALL_NUM_MASK_HIGH;
139
140        perfc_incra(fw_hypercall, index >> 8);
141        switch (index) {
142        case FW_HYPERCALL_XEN:
143                return xen_hypercall(regs);
144
145        case FW_HYPERCALL_XEN_FAST:
146                return xen_fast_hypercall(regs);
147
148        case FW_HYPERCALL_PAL_CALL:
149                //printk("*** PAL hypercall: index=%d\n",regs->r28);
150                //FIXME: This should call a C routine
151#if 0
152                // This is very conservative, but avoids a possible
153                // (and deadly) freeze in paravirtualized domains due
154                // to a yet-to-be-found bug where pending_interruption
155                // is zero when it shouldn't be. Since PAL is called
156                // in the idle loop, this should resolve it
157                VCPU(v,pending_interruption) = 1;
158#endif
159                if (regs->r28 == PAL_HALT_LIGHT) {
160                        if (vcpu_deliverable_interrupts(v) ||
161                                event_pending(v)) {
162                                perfc_incr(idle_when_pending);
163                                vcpu_pend_unspecified_interrupt(v);
164//printk("idle w/int#%d pending!\n",pi);
165//this shouldn't happen, but it apparently does quite a bit!  so don't
166//allow it to happen... i.e. if a domain has an interrupt pending and
167//it tries to halt itself because it thinks it is idle, just return here
168//as deliver_pending_interrupt is called on the way out and will deliver it
169                        }
170                        else {
171                                perfc_incr(pal_halt_light);
172                                migrate_timer(&v->arch.hlt_timer,
173                                              v->processor);
174                                set_timer(&v->arch.hlt_timer,
175                                          vcpu_get_next_timer_ns(v));
176                                do_sched_op_compat(SCHEDOP_block, 0);
177                                /* do_block only pends a softirq */
178                                do_softirq();
179                                stop_timer(&v->arch.hlt_timer);
180                        }
181                        regs->r8 = 0;
182                        regs->r9 = 0;
183                        regs->r10 = 0;
184                        regs->r11 = 0;
185                }
186                else {
187                        struct ia64_pal_retval y;
188
189                        if (regs->r28 >= PAL_COPY_PAL)
190                                y = xen_pal_emulator
191                                        (regs->r28, vcpu_get_gr (v, 33),
192                                         vcpu_get_gr (v, 34),
193                                         vcpu_get_gr (v, 35));
194                        else
195                                y = xen_pal_emulator(regs->r28,regs->r29,
196                                                     regs->r30,regs->r31);
197                        regs->r8 = y.status; regs->r9 = y.v0;
198                        regs->r10 = y.v1; regs->r11 = y.v2;
199                }
200                break;
201        case FW_HYPERCALL_SAL_CALL:
202                x = sal_emulator(vcpu_get_gr(v,32),vcpu_get_gr(v,33),
203                        vcpu_get_gr(v,34),vcpu_get_gr(v,35),
204                        vcpu_get_gr(v,36),vcpu_get_gr(v,37),
205                        vcpu_get_gr(v,38),vcpu_get_gr(v,39));
206                regs->r8 = x.r8; regs->r9 = x.r9;
207                regs->r10 = x.r10; regs->r11 = x.r11;
208                break;
209        case FW_HYPERCALL_SAL_RETURN:
210                if ( !test_and_set_bit(_VPF_down, &v->pause_flags) )
211                        vcpu_sleep_nosync(v);
212                break;
213        case FW_HYPERCALL_EFI_CALL:
214                efi_ret_value = efi_emulator (regs, &fault);
215                if (fault != IA64_NO_FAULT) return fault;
216                regs->r8 = efi_ret_value;
217                break;
218        case FW_HYPERCALL_IPI:
219                fw_hypercall_ipi (regs);
220                break;
221        case FW_HYPERCALL_SET_SHARED_INFO_VA:
222                regs->r8 = domain_set_shared_info_va (regs->r28);
223                break;
224        case FW_HYPERCALL_FPSWA:
225                fpswa_ret = fw_hypercall_fpswa (v);
226                regs->r8  = fpswa_ret.status;
227                regs->r9  = fpswa_ret.err0;
228                regs->r10 = fpswa_ret.err1;
229                regs->r11 = fpswa_ret.err2;
230                break;
231        default:
232                printk("unknown ia64 fw hypercall %lx\n", regs->r2);
233                regs->r8 = do_ni_hypercall();
234        }
235        return IA64_NO_FAULT;
236}
237
238unsigned long hypercall_create_continuation(
239        unsigned int op, const char *format, ...)
240{
241    struct mc_state *mcs = &this_cpu(mc_state);
242    struct vcpu *v = current;
243    const char *p = format;
244    unsigned long arg;
245    unsigned int i;
246    va_list args;
247
248    va_start(args, format);
249    if (test_bit(_MCSF_in_multicall, &mcs->flags))
250        panic("PREEMPT happen in multicall\n"); // Not support yet
251
252    vcpu_set_gr(v, 15, op, 0);
253
254    for (i = 0; *p != '\0'; i++) {
255        switch ( *p++ )
256        {
257        case 'i':
258            arg = (unsigned long)va_arg(args, unsigned int);
259            break;
260        case 'l':
261            arg = (unsigned long)va_arg(args, unsigned long);
262            break;
263        case 'h':
264            arg = (unsigned long)va_arg(args, void *);
265            break;
266        default:
267            arg = 0;
268            BUG();
269        }
270        vcpu_set_gr(v, 16 + i, arg, 0);
271    }
272   
273    if (i >= 6)
274        panic("Too many args for hypercall continuation\n");
275
276    // Clean other argument to 0
277    while (i < 6) {
278        vcpu_set_gr(v, 16 + i, 0, 0);
279        i++;
280    }
281
282    // re-execute break;
283    vcpu_decrement_iip(v);
284   
285    v->arch.hypercall_continuation = 1;
286    va_end(args);
287    return op;
288}
289
290/* Need make this function common */
291extern int
292iosapic_guest_read(
293    unsigned long physbase, unsigned int reg, u32 *pval);
294extern int
295iosapic_guest_write(
296    unsigned long physbase, unsigned int reg, u32 pval);
297
298long do_physdev_op(int cmd, XEN_GUEST_HANDLE(void) arg)
299{
300    int irq;
301    long ret;
302
303    switch ( cmd )
304    {
305    case PHYSDEVOP_eoi: {
306        struct physdev_eoi eoi;
307        ret = -EFAULT;
308        if ( copy_from_guest(&eoi, arg, 1) != 0 )
309            break;
310        ret = pirq_guest_eoi(current->domain, eoi.irq);
311        break;
312    }
313
314    /* Legacy since 0x00030202. */
315    case PHYSDEVOP_IRQ_UNMASK_NOTIFY: {
316        ret = pirq_guest_unmask(current->domain);
317        break;
318    }
319
320    case PHYSDEVOP_irq_status_query: {
321        struct physdev_irq_status_query irq_status_query;
322        ret = -EFAULT;
323        if ( copy_from_guest(&irq_status_query, arg, 1) != 0 )
324            break;
325        irq = irq_status_query.irq;
326        ret = -EINVAL;
327        if ( (irq < 0) || (irq >= NR_IRQS) )
328            break;
329        irq_status_query.flags = 0;
330        /* Edge-triggered interrupts don't need an explicit unmask downcall. */
331        if ( !strstr(irq_desc[irq_to_vector(irq)].handler->typename, "edge") )
332            irq_status_query.flags |= XENIRQSTAT_needs_eoi;
333        ret = copy_to_guest(arg, &irq_status_query, 1) ? -EFAULT : 0;
334        break;
335    }
336
337    case PHYSDEVOP_apic_read: {
338        struct physdev_apic apic;
339        ret = -EFAULT;
340        if ( copy_from_guest(&apic, arg, 1) != 0 )
341            break;
342        ret = -EPERM;
343        if ( !IS_PRIV(current->domain) )
344            break;
345        ret = iosapic_guest_read(apic.apic_physbase, apic.reg, &apic.value);
346        if ( copy_to_guest(arg, &apic, 1) != 0 )
347            ret = -EFAULT;
348        break;
349    }
350
351    case PHYSDEVOP_apic_write: {
352        struct physdev_apic apic;
353        ret = -EFAULT;
354        if ( copy_from_guest(&apic, arg, 1) != 0 )
355            break;
356        ret = -EPERM;
357        if ( !IS_PRIV(current->domain) )
358            break;
359        ret = iosapic_guest_write(apic.apic_physbase, apic.reg, apic.value);
360        break;
361    }
362
363    case PHYSDEVOP_alloc_irq_vector: {
364        struct physdev_irq irq_op;
365
366        ret = -EFAULT;
367        if ( copy_from_guest(&irq_op, arg, 1) != 0 )
368            break;
369
370        ret = -EPERM;
371        if ( !IS_PRIV(current->domain) )
372            break;
373
374        ret = -EINVAL;
375        if ( (irq = irq_op.irq) >= NR_IRQS )
376            break;
377       
378        irq_op.vector = assign_irq_vector(irq);
379        ret = copy_to_guest(arg, &irq_op, 1) ? -EFAULT : 0;
380        break;
381    }
382
383    case PHYSDEVOP_free_irq_vector: {
384        struct physdev_irq irq_op;
385        int vector;
386
387        ret = -EFAULT;
388        if ( copy_from_guest(&irq_op, arg, 1) != 0 )
389            break;
390
391        ret = -EPERM;
392        if ( !IS_PRIV(current->domain) )
393            break;
394
395        ret = -EINVAL;
396        vector = irq_op.vector;
397        if (vector < IA64_FIRST_DEVICE_VECTOR ||
398            vector > IA64_LAST_DEVICE_VECTOR)
399            break;
400       
401        /* XXX This should be called, but causes a NAT consumption via the
402         * reboot notifier_call_chain in dom0 if a device is hidden for
403         * a driver domain using pciback.hide= (specifically, hiding function
404         * 1 of a 2 port e1000 card).
405         * free_irq_vector(vector);
406         */
407        ret = 0;
408        break;
409    }
410
411    default:
412        ret = -ENOSYS;
413        break;
414    }
415
416    return ret;
417}
418
419static long register_guest_callback(struct callback_register *reg)
420{
421    long ret = 0;
422    struct vcpu *v = current;
423
424    if (IS_VMM_ADDRESS(reg->address))
425        return -EINVAL;
426
427    switch ( reg->type )
428    {
429    case CALLBACKTYPE_event:
430        v->arch.event_callback_ip    = reg->address;
431        break;
432
433    case CALLBACKTYPE_failsafe:
434        v->arch.failsafe_callback_ip = reg->address;
435        break;
436
437    default:
438        ret = -ENOSYS;
439        break;
440    }
441
442    return ret;
443}
444
445static long unregister_guest_callback(struct callback_unregister *unreg)
446{
447    return -EINVAL;
448}
449
450/* First time to add callback to xen/ia64, so let's just stick to
451 * the newer callback interface.
452 */
453long do_callback_op(int cmd, XEN_GUEST_HANDLE(void) arg)
454{
455    long ret;
456
457    switch ( cmd )
458    {
459    case CALLBACKOP_register:
460    {
461        struct callback_register reg;
462
463        ret = -EFAULT;
464        if ( copy_from_guest(&reg, arg, 1) )
465            break;
466
467        ret = register_guest_callback(&reg);
468    }
469    break;
470
471    case CALLBACKOP_unregister:
472    {
473        struct callback_unregister unreg;
474
475        ret = -EFAULT;
476        if ( copy_from_guest(&unreg, arg, 1) )
477            break;
478
479        ret = unregister_guest_callback(&unreg);
480    }
481    break;
482
483    default:
484        ret = -ENOSYS;
485        break;
486    }
487
488    return ret;
489}
Note: See TracBrowser for help on using the repository browser.