1 /*
2  * xen/common/monitor.c
3  *
4  * Common monitor_op domctl handler.
5  *
6  * Copyright (c) 2015 Tamas K Lengyel (tamas@tklengyel.com)
7  * Copyright (c) 2016, Bitdefender S.R.L.
8  *
9  * This program is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU General Public
11  * License v2 as published by the Free Software Foundation.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public
19  * License along with this program; If not, see <http://www.gnu.org/licenses/>.
20  */
21 
22 #include <xen/event.h>
23 #include <xen/monitor.h>
24 #include <xen/sched.h>
25 #include <xen/vm_event.h>
26 #include <xsm/xsm.h>
27 #include <asm/altp2m.h>
28 #include <asm/monitor.h>
29 #include <asm/vm_event.h>
30 
monitor_domctl(struct domain * d,struct xen_domctl_monitor_op * mop)31 int monitor_domctl(struct domain *d, struct xen_domctl_monitor_op *mop)
32 {
33     int rc;
34     bool requested_status = false;
35 
36     if ( unlikely(current->domain == d) ) /* no domain_pause() */
37         return -EPERM;
38 
39     rc = xsm_vm_event_control(XSM_PRIV, d, mop->op, mop->event);
40     if ( unlikely(rc) )
41         return rc;
42 
43     switch ( mop->op )
44     {
45     case XEN_DOMCTL_MONITOR_OP_ENABLE:
46         requested_status = true;
47         /* fallthrough */
48     case XEN_DOMCTL_MONITOR_OP_DISABLE:
49         /* sanity check: avoid left-shift undefined behavior */
50         if ( unlikely(mop->event > 31) )
51             return -EINVAL;
52         /* Check if event type is available. */
53         if ( unlikely(!(arch_monitor_get_capabilities(d) & (1U << mop->event))) )
54             return -EOPNOTSUPP;
55         break;
56 
57     case XEN_DOMCTL_MONITOR_OP_GET_CAPABILITIES:
58         mop->event = arch_monitor_get_capabilities(d);
59         return 0;
60 
61     default:
62         /* The monitor op is probably handled on the arch-side. */
63         return arch_monitor_domctl_op(d, mop);
64     }
65 
66     switch ( mop->event )
67     {
68     case XEN_DOMCTL_MONITOR_EVENT_GUEST_REQUEST:
69     {
70         bool old_status = d->monitor.guest_request_enabled;
71 
72         if ( unlikely(old_status == requested_status) )
73             return -EEXIST;
74 
75         domain_pause(d);
76         d->monitor.guest_request_sync = mop->u.guest_request.sync;
77         d->monitor.guest_request_enabled = requested_status;
78         arch_monitor_allow_userspace(d, mop->u.guest_request.allow_userspace);
79         domain_unpause(d);
80         break;
81     }
82 
83     default:
84         /* Give arch-side the chance to handle this event */
85         return arch_monitor_domctl_event(d, mop);
86     }
87 
88     return 0;
89 }
90 
monitor_traps(struct vcpu * v,bool sync,vm_event_request_t * req)91 int monitor_traps(struct vcpu *v, bool sync, vm_event_request_t *req)
92 {
93     int rc;
94     struct domain *d = v->domain;
95 
96     rc = vm_event_claim_slot(d, d->vm_event_monitor);
97     switch ( rc )
98     {
99     case 0:
100         break;
101     case -EOPNOTSUPP:
102         /*
103          * If there was no ring to handle the event, then
104          * simply continue executing normally.
105          */
106         return 0;
107     default:
108         return rc;
109     };
110 
111     req->vcpu_id = v->vcpu_id;
112 
113     if ( sync )
114     {
115         req->flags |= VM_EVENT_FLAG_VCPU_PAUSED;
116         vm_event_sync_event(v, true);
117         vm_event_vcpu_pause(v);
118         rc = 1;
119     }
120 
121     if ( altp2m_active(d) )
122     {
123         req->flags |= VM_EVENT_FLAG_ALTERNATE_P2M;
124         req->altp2m_idx = altp2m_vcpu_idx(v);
125     }
126 
127     vm_event_fill_regs(req);
128     vm_event_put_request(d, d->vm_event_monitor, req);
129 
130     return rc;
131 }
132 
monitor_guest_request(void)133 void monitor_guest_request(void)
134 {
135     struct vcpu *curr = current;
136     struct domain *d = curr->domain;
137 
138     if ( d->monitor.guest_request_enabled )
139     {
140         vm_event_request_t req = {
141             .reason = VM_EVENT_REASON_GUEST_REQUEST,
142             .vcpu_id = curr->vcpu_id,
143         };
144 
145         monitor_traps(curr, d->monitor.guest_request_sync, &req);
146     }
147 }
148 
149 /*
150  * Local variables:
151  * mode: C
152  * c-file-style: "BSD"
153  * c-basic-offset: 4
154  * indent-tabs-mode: nil
155  * End:
156  */
157