summaryrefslogtreecommitdiff
path: root/xen/arch/arm/platform_hypercall.c
blob: 743687a303909a377a250059fc8ce5e1e164f9e7 (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
/* SPDX-License-Identifier: GPL-2.0 */
/******************************************************************************
 * platform_hypercall.c
 *
 * Hardware platform operations. Intended for use by domain-0 kernel.
 *
 * Copyright (c) 2015, Citrix
 */

#include <xen/types.h>
#include <xen/sched.h>
#include <xen/guest_access.h>
#include <xen/hypercall.h>
#include <xen/spinlock.h>
#include <public/platform.h>
#include <xsm/xsm.h>
#include <asm/current.h>
#include <asm/event.h>

DEFINE_SPINLOCK(xenpf_lock);

long do_platform_op(XEN_GUEST_HANDLE_PARAM(xen_platform_op_t) u_xenpf_op)
{
    long ret;
    struct xen_platform_op curop, *op = &curop;
    struct domain *d;

    if ( copy_from_guest(op, u_xenpf_op, 1) )
        return -EFAULT;

    if ( op->interface_version != XENPF_INTERFACE_VERSION )
        return -EACCES;

    d = rcu_lock_current_domain();
    if ( d == NULL )
        return -ESRCH;

    ret = xsm_platform_op(XSM_PRIV, op->cmd);
    if ( ret )
        return ret;

    /*
     * Trylock here avoids deadlock with an existing platform critical section
     * which might (for some current or future reason) want to synchronise
     * with this vcpu.
     */
    while ( !spin_trylock(&xenpf_lock) )
        if ( hypercall_preempt_check() )
            return hypercall_create_continuation(
                __HYPERVISOR_platform_op, "h", u_xenpf_op);

    switch ( op->cmd )
    {
    case XENPF_settime64:
        if ( likely(!op->u.settime64.mbz) )
            do_settime(op->u.settime64.secs,
                       op->u.settime64.nsecs,
                       op->u.settime64.system_time + SECONDS(d->time_offset.seconds));
        else
            ret = -EINVAL;
        break;

    default:
        ret = -ENOSYS;
        break;
    }

    spin_unlock(&xenpf_lock);
    rcu_unlock_domain(d);
    return ret;
}