aboutsummaryrefslogtreecommitdiffstats
path: root/xen/common/multicall.c
blob: 499e6bd62cbf5d093339e6bd6c5b0c280351fc98 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
/******************************************************************************
 * multicall.c
 */

#include <xen/config.h>
#include <xen/types.h>
#include <xen/lib.h>
#include <xen/mm.h>
#include <xen/sched.h>
#include <xen/event.h>
#include <xen/multicall.h>
#include <xen/guest_access.h>
#include <asm/current.h>
#include <asm/hardirq.h>

DEFINE_PER_CPU(struct mc_state, mc_state);

long
do_multicall(
    XEN_GUEST_HANDLE(multicall_entry_t) call_list, unsigned int nr_calls)
{
    struct mc_state *mcs = &this_cpu(mc_state);
    unsigned int     i;

    if ( unlikely(__test_and_set_bit(_MCSF_in_multicall, &mcs->flags)) )
    {
        gdprintk(XENLOG_INFO, "Multicall reentry is disallowed.\n");
        return -EINVAL;
    }

    if ( unlikely(!guest_handle_okay(call_list, nr_calls)) )
        goto fault;

    for ( i = 0; i < nr_calls; i++ )
    {
        if ( hypercall_preempt_check() )
            goto preempted;

        if ( unlikely(__copy_from_guest(&mcs->call, call_list, 1)) )
            goto fault;

        do_multicall_call(&mcs->call);

#ifndef NDEBUG
        {
            /*
             * Deliberately corrupt the contents of the multicall structure.
             * The caller must depend only on the 'result' field on return.
             */
            struct multicall_entry corrupt;
            memset(&corrupt, 0xAA, sizeof(corrupt));
            (void)__copy_to_guest(call_list, &corrupt, 1);
        }
#endif

        if ( unlikely(__copy_field_to_guest(call_list, &mcs->call, result)) )
            goto fault;

        if ( test_bit(_MCSF_call_preempted, &mcs->flags) )
        {
            /* Copy the sub-call continuation. */
            (void)__copy_to_guest(call_list, &mcs->call, 1);
            goto preempted;
        }

        guest_handle_add_offset(call_list, 1);
    }

    mcs->flags = 0;
    return 0;

 fault:
    mcs->flags = 0;
    return -EFAULT;

 preempted:
    mcs->flags = 0;
    return hypercall_create_continuation(
        __HYPERVISOR_multicall, "hi", call_list, nr_calls-i);
}

/*
 * Local variables:
 * mode: C
 * c-set-style: "BSD"
 * c-basic-offset: 4
 * tab-width: 4
 * indent-tabs-mode: nil
 * End:
 */