ia64/xen-unstable

annotate xen/common/event_channel.c @ 7232:c3d9b7013b14

EVTCHNOP_alloc_unbound can allocate a port in an arbitrary
domain (only if the caller is sufficiently privileged).

Signed-off-by: Keir Fraser <keir@xensource.com>
author kaf24@firebug.cl.cam.ac.uk
date Wed Oct 05 19:14:13 2005 +0100 (2005-10-05)
parents ec84b119e4ed
children 4083eb31def0
rev   line source
kaf24@954 1 /******************************************************************************
kaf24@954 2 * event_channel.c
kaf24@954 3 *
kaf24@1509 4 * Event notifications from VIRQs, PIRQs, and other domains.
kaf24@954 5 *
kaf24@1127 6 * Copyright (c) 2003-2004, K A Fraser.
kaf24@954 7 *
kaf24@954 8 * This program is distributed in the hope that it will be useful,
kaf24@954 9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
kaf24@954 10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
kaf24@954 11 * GNU General Public License for more details.
kaf24@954 12 *
kaf24@954 13 * You should have received a copy of the GNU General Public License
kaf24@954 14 * along with this program; if not, write to the Free Software
kaf24@954 15 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
kaf24@954 16 */
kaf24@954 17
kaf24@1210 18 #include <xen/config.h>
kaf24@1210 19 #include <xen/init.h>
kaf24@1210 20 #include <xen/lib.h>
kaf24@1210 21 #include <xen/errno.h>
kaf24@1210 22 #include <xen/sched.h>
kaf24@1210 23 #include <xen/event.h>
kaf24@1239 24 #include <xen/irq.h>
cl349@5291 25 #include <asm/current.h>
kaf24@954 26
kaf24@2789 27 #include <public/xen.h>
kaf24@2789 28 #include <public/event_channel.h>
smh22@5514 29 #include <acm/acm_hooks.h>
kaf24@1127 30
kaf24@5308 31 #define bucket_from_port(d,p) \
kaf24@5308 32 ((d)->evtchn[(p)/EVTCHNS_PER_BUCKET])
kaf24@5308 33 #define port_is_valid(d,p) \
kaf24@5308 34 (((p) >= 0) && ((p) < MAX_EVTCHNS) && \
kaf24@5308 35 (bucket_from_port(d,p) != NULL))
kaf24@5308 36 #define evtchn_from_port(d,p) \
kaf24@5308 37 (&(bucket_from_port(d,p))[(p)&(EVTCHNS_PER_BUCKET-1)])
kaf24@1127 38
kaf24@5325 39 #define ERROR_EXIT(_errno) do { rc = (_errno); goto out; } while ( 0 )
kaf24@5325 40
kaf24@5308 41 static int get_free_port(struct domain *d)
kaf24@5308 42 {
kaf24@5308 43 struct evtchn *chn;
kaf24@5308 44 int port;
cl349@3114 45
kaf24@5308 46 for ( port = 0; port_is_valid(d, port); port++ )
kaf24@5308 47 if ( evtchn_from_port(d, port)->state == ECS_FREE )
kaf24@5308 48 return port;
kaf24@1127 49
kaf24@5308 50 if ( port == MAX_EVTCHNS )
kaf24@5308 51 return -ENOSPC;
kaf24@1127 52
kaf24@5308 53 chn = xmalloc_array(struct evtchn, EVTCHNS_PER_BUCKET);
kaf24@5308 54 if ( unlikely(chn == NULL) )
kaf24@5308 55 return -ENOMEM;
kaf24@5308 56 memset(chn, 0, EVTCHNS_PER_BUCKET * sizeof(*chn));
kaf24@5308 57 bucket_from_port(d, port) = chn;
kaf24@1127 58
kaf24@1127 59 return port;
kaf24@1127 60 }
kaf24@1127 61
kaf24@2713 62
kaf24@2713 63 static long evtchn_alloc_unbound(evtchn_alloc_unbound_t *alloc)
kaf24@2713 64 {
kaf24@5308 65 struct evtchn *chn;
kaf24@7232 66 struct domain *d;
kaf24@5325 67 int port = alloc->port;
kaf24@7232 68 domid_t dom = alloc->dom;
kaf24@5325 69 long rc = 0;
kaf24@2713 70
kaf24@7232 71 if ( dom == DOMID_SELF )
kaf24@7232 72 dom = current->domain->domain_id;
kaf24@7232 73 else if ( !IS_PRIV(current->domain) )
kaf24@7232 74 return -EPERM;
kaf24@7232 75
kaf24@7232 76 if ( (d = find_domain_by_id(dom)) == NULL )
kaf24@7232 77 return -ESRCH;
kaf24@7232 78
kaf24@5308 79 spin_lock(&d->evtchn_lock);
kaf24@2713 80
kaf24@5325 81 /* Obtain, or ensure that we already have, a valid <port>. */
kaf24@5325 82 if ( port == 0 )
kaf24@2713 83 {
kaf24@5325 84 if ( (port = get_free_port(d)) < 0 )
kaf24@5325 85 ERROR_EXIT(port);
kaf24@5325 86 }
kaf24@5325 87 else if ( !port_is_valid(d, port) )
kaf24@5325 88 ERROR_EXIT(-EINVAL);
kaf24@5325 89 chn = evtchn_from_port(d, port);
kaf24@5325 90
kaf24@5325 91 /* Validate channel's current state. */
kaf24@5325 92 switch ( chn->state )
kaf24@5325 93 {
kaf24@5325 94 case ECS_FREE:
kaf24@5308 95 chn->state = ECS_UNBOUND;
kaf24@7232 96 chn->u.unbound.remote_domid = alloc->remote_dom;
kaf24@5325 97 break;
kaf24@5325 98
kaf24@5325 99 case ECS_UNBOUND:
kaf24@7232 100 if ( chn->u.unbound.remote_domid != alloc->remote_dom )
kaf24@5325 101 ERROR_EXIT(-EINVAL);
kaf24@5325 102 break;
kaf24@5325 103
kaf24@5325 104 default:
kaf24@5325 105 ERROR_EXIT(-EINVAL);
kaf24@2713 106 }
kaf24@2713 107
kaf24@5325 108 out:
kaf24@5308 109 spin_unlock(&d->evtchn_lock);
kaf24@2713 110
kaf24@7232 111 put_domain(d);
kaf24@7232 112
kaf24@2713 113 alloc->port = port;
kaf24@7232 114
kaf24@5325 115 return rc;
kaf24@2713 116 }
kaf24@2713 117
kaf24@2713 118
kaf24@1218 119 static long evtchn_bind_interdomain(evtchn_bind_interdomain_t *bind)
kaf24@1127 120 {
kaf24@5308 121 struct evtchn *chn1, *chn2;
kaf24@1506 122 struct domain *d1, *d2;
kaf24@2713 123 int port1 = bind->port1, port2 = bind->port2;
kaf24@1506 124 domid_t dom1 = bind->dom1, dom2 = bind->dom2;
kaf24@1506 125 long rc = 0;
kaf24@954 126
cl349@2919 127 if ( !IS_PRIV(current->domain) && (dom1 != DOMID_SELF) )
kaf24@1127 128 return -EPERM;
kaf24@954 129
kaf24@1129 130 if ( dom1 == DOMID_SELF )
kaf24@4877 131 dom1 = current->domain->domain_id;
kaf24@1129 132 if ( dom2 == DOMID_SELF )
kaf24@4877 133 dom2 = current->domain->domain_id;
kaf24@1127 134
kaf24@1506 135 if ( ((d1 = find_domain_by_id(dom1)) == NULL) ||
kaf24@1506 136 ((d2 = find_domain_by_id(dom2)) == NULL) )
kaf24@1127 137 {
kaf24@1506 138 if ( d1 != NULL )
kaf24@1506 139 put_domain(d1);
kaf24@1127 140 return -ESRCH;
kaf24@1127 141 }
kaf24@1127 142
kaf24@1127 143 /* Avoid deadlock by first acquiring lock of domain with smaller id. */
kaf24@1542 144 if ( d1 < d2 )
kaf24@954 145 {
kaf24@5308 146 spin_lock(&d1->evtchn_lock);
kaf24@5308 147 spin_lock(&d2->evtchn_lock);
kaf24@954 148 }
kaf24@954 149 else
kaf24@954 150 {
kaf24@1506 151 if ( d1 != d2 )
kaf24@5308 152 spin_lock(&d2->evtchn_lock);
kaf24@5308 153 spin_lock(&d1->evtchn_lock);
kaf24@954 154 }
kaf24@954 155
kaf24@2713 156 /* Obtain, or ensure that we already have, a valid <port1>. */
kaf24@2713 157 if ( port1 == 0 )
kaf24@954 158 {
kaf24@5308 159 if ( (port1 = get_free_port(d1)) < 0 )
kaf24@2713 160 ERROR_EXIT(port1);
kaf24@2713 161 }
kaf24@5308 162 else if ( !port_is_valid(d1, port1) )
kaf24@2713 163 ERROR_EXIT(-EINVAL);
kaf24@5308 164 chn1 = evtchn_from_port(d1, port1);
kaf24@2713 165
kaf24@2713 166 /* Obtain, or ensure that we already have, a valid <port2>. */
kaf24@2713 167 if ( port2 == 0 )
kaf24@2713 168 {
kaf24@2713 169 /* Make port1 non-free while we allocate port2 (in case dom1==dom2). */
kaf24@5308 170 u16 state = chn1->state;
kaf24@5308 171 chn1->state = ECS_INTERDOMAIN;
kaf24@5308 172 port2 = get_free_port(d2);
kaf24@5308 173 chn1->state = state;
kaf24@2713 174 if ( port2 < 0 )
kaf24@2713 175 ERROR_EXIT(port2);
kaf24@2713 176 }
kaf24@5308 177 else if ( !port_is_valid(d2, port2) )
kaf24@2713 178 ERROR_EXIT(-EINVAL);
kaf24@5308 179 chn2 = evtchn_from_port(d2, port2);
kaf24@2713 180
kaf24@2713 181 /* Validate <dom1,port1>'s current state. */
kaf24@5308 182 switch ( chn1->state )
kaf24@2713 183 {
kaf24@2713 184 case ECS_FREE:
kaf24@2713 185 break;
kaf24@2713 186
kaf24@2713 187 case ECS_UNBOUND:
kaf24@5308 188 if ( chn1->u.unbound.remote_domid != dom2 )
kaf24@2713 189 ERROR_EXIT(-EINVAL);
kaf24@2713 190 break;
kaf24@2713 191
kaf24@2713 192 case ECS_INTERDOMAIN:
kaf24@5308 193 if ( chn1->u.interdomain.remote_dom != d2 )
kaf24@2719 194 ERROR_EXIT(-EINVAL);
kaf24@5308 195 if ( (chn1->u.interdomain.remote_port != port2) && (bind->port2 != 0) )
kaf24@2719 196 ERROR_EXIT(-EINVAL);
kaf24@5308 197 port2 = chn1->u.interdomain.remote_port;
kaf24@1127 198 goto out;
kaf24@2713 199
kaf24@2713 200 default:
kaf24@2713 201 ERROR_EXIT(-EINVAL);
kaf24@954 202 }
kaf24@954 203
kaf24@2713 204 /* Validate <dom2,port2>'s current state. */
kaf24@5308 205 switch ( chn2->state )
kaf24@2713 206 {
kaf24@2713 207 case ECS_FREE:
cl349@2919 208 if ( !IS_PRIV(current->domain) && (dom2 != DOMID_SELF) )
kaf24@2717 209 ERROR_EXIT(-EPERM);
kaf24@2713 210 break;
kaf24@1329 211
kaf24@2713 212 case ECS_UNBOUND:
kaf24@5308 213 if ( chn2->u.unbound.remote_domid != dom1 )
kaf24@2713 214 ERROR_EXIT(-EINVAL);
kaf24@2713 215 break;
kaf24@2713 216
kaf24@2719 217 case ECS_INTERDOMAIN:
kaf24@5308 218 if ( chn2->u.interdomain.remote_dom != d1 )
kaf24@2719 219 ERROR_EXIT(-EINVAL);
kaf24@5308 220 if ( (chn2->u.interdomain.remote_port != port1) && (bind->port1 != 0) )
kaf24@2719 221 ERROR_EXIT(-EINVAL);
kaf24@5308 222 port1 = chn2->u.interdomain.remote_port;
kaf24@2719 223 goto out;
kaf24@2719 224
kaf24@2713 225 default:
kaf24@2713 226 ERROR_EXIT(-EINVAL);
kaf24@1127 227 }
kaf24@954 228
kaf24@2713 229 /*
kaf24@2713 230 * Everything checked out okay -- bind <dom1,port1> to <dom2,port2>.
kaf24@2713 231 */
kaf24@2713 232
kaf24@5308 233 chn1->u.interdomain.remote_dom = d2;
kaf24@5308 234 chn1->u.interdomain.remote_port = (u16)port2;
kaf24@5308 235 chn1->state = ECS_INTERDOMAIN;
kaf24@2713 236
kaf24@5308 237 chn2->u.interdomain.remote_dom = d1;
kaf24@5308 238 chn2->u.interdomain.remote_port = (u16)port1;
kaf24@5308 239 chn2->state = ECS_INTERDOMAIN;
kaf24@1127 240
kaf24@954 241 out:
kaf24@5308 242 spin_unlock(&d1->evtchn_lock);
kaf24@1506 243 if ( d1 != d2 )
kaf24@5308 244 spin_unlock(&d2->evtchn_lock);
kaf24@1127 245
kaf24@1506 246 put_domain(d1);
kaf24@1506 247 put_domain(d2);
kaf24@1127 248
kaf24@1218 249 bind->port1 = port1;
kaf24@1218 250 bind->port2 = port2;
kaf24@954 251
kaf24@954 252 return rc;
kaf24@954 253 }
kaf24@954 254
kaf24@954 255
kaf24@1218 256 static long evtchn_bind_virq(evtchn_bind_virq_t *bind)
kaf24@1218 257 {
kaf24@5308 258 struct evtchn *chn;
kaf24@7204 259 struct vcpu *v;
kaf24@7204 260 struct domain *d = current->domain;
kaf24@1506 261 int port, virq = bind->virq;
kaf24@1218 262
kaf24@5289 263 if ( virq >= ARRAY_SIZE(v->virq_to_evtchn) )
kaf24@1218 264 return -EINVAL;
kaf24@1218 265
kaf24@7204 266 if ( (v = d->vcpu[bind->vcpu]) == NULL )
kaf24@7204 267 return -ENOENT;
cl349@6680 268
kaf24@5308 269 spin_lock(&d->evtchn_lock);
kaf24@1218 270
kaf24@1218 271 /*
kaf24@1218 272 * Port 0 is the fallback port for VIRQs that haven't been explicitly
cl349@3297 273 * bound yet.
kaf24@1218 274 */
kaf24@5289 275 if ( ((port = v->virq_to_evtchn[virq]) != 0) ||
kaf24@5308 276 ((port = get_free_port(d)) < 0) )
kaf24@1218 277 goto out;
kaf24@1218 278
kaf24@5308 279 chn = evtchn_from_port(d, port);
kaf24@5308 280 chn->state = ECS_VIRQ;
kaf24@5308 281 chn->notify_vcpu_id = v->vcpu_id;
kaf24@5308 282 chn->u.virq = virq;
kaf24@1218 283
kaf24@5289 284 v->virq_to_evtchn[virq] = port;
kaf24@1218 285
kaf24@1218 286 out:
kaf24@5308 287 spin_unlock(&d->evtchn_lock);
kaf24@1218 288
kaf24@1218 289 if ( port < 0 )
kaf24@1218 290 return port;
kaf24@1218 291
kaf24@1218 292 bind->port = port;
cl349@2932 293 return 0;
cl349@2932 294 }
cl349@2932 295
kaf24@5308 296
cl349@2932 297 static long evtchn_bind_ipi(evtchn_bind_ipi_t *bind)
cl349@2932 298 {
kaf24@5308 299 struct evtchn *chn;
kaf24@5308 300 struct domain *d = current->domain;
kaf24@5703 301 int port;
kaf24@4202 302
kaf24@7204 303 if ( d->vcpu[bind->vcpu] == NULL )
kaf24@7204 304 return -ENOENT;
kaf24@7204 305
kaf24@5308 306 spin_lock(&d->evtchn_lock);
cl349@2932 307
kaf24@5308 308 if ( (port = get_free_port(d)) >= 0 )
cl349@2932 309 {
kaf24@5308 310 chn = evtchn_from_port(d, port);
kaf24@5308 311 chn->state = ECS_IPI;
kaf24@7204 312 chn->notify_vcpu_id = bind->vcpu;
cl349@2932 313 }
cl349@2932 314
kaf24@5308 315 spin_unlock(&d->evtchn_lock);
cl349@2932 316
cl349@2932 317 if ( port < 0 )
cl349@2932 318 return port;
cl349@2932 319
cl349@2932 320 bind->port = port;
kaf24@1218 321 return 0;
kaf24@1218 322 }
kaf24@1218 323
kaf24@1218 324
kaf24@1235 325 static long evtchn_bind_pirq(evtchn_bind_pirq_t *bind)
kaf24@1235 326 {
kaf24@5308 327 struct evtchn *chn;
cl349@2919 328 struct domain *d = current->domain;
kaf24@1506 329 int port, rc, pirq = bind->pirq;
kaf24@1235 330
kaf24@1506 331 if ( pirq >= ARRAY_SIZE(d->pirq_to_evtchn) )
kaf24@1235 332 return -EINVAL;
kaf24@1235 333
kaf24@5308 334 spin_lock(&d->evtchn_lock);
kaf24@1235 335
kaf24@1506 336 if ( ((rc = port = d->pirq_to_evtchn[pirq]) != 0) ||
kaf24@5308 337 ((rc = port = get_free_port(d)) < 0) )
kaf24@1235 338 goto out;
kaf24@1235 339
kaf24@5308 340 chn = evtchn_from_port(d, port);
kaf24@5308 341
kaf24@1506 342 d->pirq_to_evtchn[pirq] = port;
sos22@5700 343 rc = pirq_guest_bind(d->vcpu[0], pirq,
kaf24@1253 344 !!(bind->flags & BIND_PIRQ__WILL_SHARE));
kaf24@1253 345 if ( rc != 0 )
kaf24@1239 346 {
kaf24@1506 347 d->pirq_to_evtchn[pirq] = 0;
kaf24@1239 348 goto out;
kaf24@1239 349 }
kaf24@1239 350
kaf24@5308 351 chn->state = ECS_PIRQ;
kaf24@5308 352 chn->u.pirq = pirq;
kaf24@1235 353
kaf24@1235 354 out:
kaf24@5308 355 spin_unlock(&d->evtchn_lock);
kaf24@1235 356
kaf24@1239 357 if ( rc < 0 )
kaf24@1239 358 return rc;
kaf24@1235 359
kaf24@1235 360 bind->port = port;
kaf24@1235 361 return 0;
kaf24@1235 362 }
kaf24@1235 363
kaf24@1235 364
kaf24@1506 365 static long __evtchn_close(struct domain *d1, int port1)
kaf24@954 366 {
kaf24@5308 367 struct domain *d2 = NULL;
kaf24@5308 368 struct vcpu *v;
kaf24@5308 369 struct evtchn *chn1, *chn2;
kaf24@5308 370 int port2;
kaf24@5308 371 long rc = 0;
kaf24@954 372
kaf24@954 373 again:
kaf24@5308 374 spin_lock(&d1->evtchn_lock);
kaf24@954 375
kaf24@5308 376 if ( !port_is_valid(d1, port1) )
kaf24@954 377 {
kaf24@954 378 rc = -EINVAL;
kaf24@954 379 goto out;
kaf24@954 380 }
kaf24@954 381
kaf24@5308 382 chn1 = evtchn_from_port(d1, port1);
kaf24@5308 383 switch ( chn1->state )
kaf24@954 384 {
kaf24@1218 385 case ECS_FREE:
cl349@3297 386 case ECS_RESERVED:
kaf24@1218 387 rc = -EINVAL;
kaf24@1218 388 goto out;
kaf24@1218 389
kaf24@1218 390 case ECS_UNBOUND:
kaf24@1218 391 break;
kaf24@1218 392
kaf24@1218 393 case ECS_PIRQ:
kaf24@5308 394 if ( (rc = pirq_guest_unbind(d1, chn1->u.pirq)) == 0 )
kaf24@5308 395 d1->pirq_to_evtchn[chn1->u.pirq] = 0;
kaf24@1218 396 break;
kaf24@1218 397
kaf24@1218 398 case ECS_VIRQ:
kaf24@5289 399 for_each_vcpu ( d1, v )
kaf24@5308 400 if ( v->virq_to_evtchn[chn1->u.virq] == port1 )
kaf24@5308 401 v->virq_to_evtchn[chn1->u.virq] = 0;
kaf24@1218 402 break;
kaf24@1218 403
cl349@2932 404 case ECS_IPI:
cl349@2932 405 break;
cl349@2932 406
kaf24@1218 407 case ECS_INTERDOMAIN:
kaf24@1506 408 if ( d2 == NULL )
kaf24@954 409 {
kaf24@5308 410 d2 = chn1->u.interdomain.remote_dom;
kaf24@1505 411
kaf24@1506 412 /* If we unlock d1 then we could lose d2. Must get a reference. */
kaf24@1506 413 if ( unlikely(!get_domain(d2)) )
kaf24@1505 414 {
kaf24@1505 415 /*
kaf24@1506 416 * Failed to obtain a reference. No matter: d2 must be dying
kaf24@1505 417 * and so will close this event channel for us.
kaf24@1505 418 */
kaf24@1506 419 d2 = NULL;
kaf24@1505 420 goto out;
kaf24@1505 421 }
kaf24@1127 422
kaf24@1542 423 if ( d1 < d2 )
kaf24@954 424 {
kaf24@5308 425 spin_lock(&d2->evtchn_lock);
kaf24@954 426 }
kaf24@1506 427 else if ( d1 != d2 )
kaf24@954 428 {
kaf24@5308 429 spin_unlock(&d1->evtchn_lock);
kaf24@5308 430 spin_lock(&d2->evtchn_lock);
kaf24@954 431 goto again;
kaf24@954 432 }
kaf24@954 433 }
kaf24@5308 434 else if ( d2 != chn1->u.interdomain.remote_dom )
kaf24@954 435 {
kaf24@954 436 rc = -EINVAL;
kaf24@954 437 goto out;
kaf24@954 438 }
kaf24@1505 439
kaf24@5308 440 port2 = chn1->u.interdomain.remote_port;
kaf24@5308 441 BUG_ON(!port_is_valid(d2, port2));
kaf24@954 442
kaf24@5308 443 chn2 = evtchn_from_port(d2, port2);
kaf24@5308 444 BUG_ON(chn2->state != ECS_INTERDOMAIN);
kaf24@5308 445 BUG_ON(chn2->u.interdomain.remote_dom != d1);
kaf24@954 446
kaf24@5308 447 chn2->state = ECS_UNBOUND;
kaf24@5308 448 chn2->u.unbound.remote_domid = d1->domain_id;
kaf24@1218 449 break;
kaf24@1218 450
kaf24@1218 451 default:
kaf24@1218 452 BUG();
kaf24@954 453 }
kaf24@954 454
kaf24@5703 455 /* Reset binding to vcpu0 when the channel is freed. */
kaf24@5703 456 chn1->state = ECS_FREE;
kaf24@5703 457 chn1->notify_vcpu_id = 0;
kaf24@1133 458
kaf24@954 459 out:
kaf24@1506 460 if ( d2 != NULL )
kaf24@954 461 {
kaf24@1506 462 if ( d1 != d2 )
kaf24@5308 463 spin_unlock(&d2->evtchn_lock);
kaf24@1506 464 put_domain(d2);
kaf24@954 465 }
kaf24@954 466
kaf24@5308 467 spin_unlock(&d1->evtchn_lock);
kaf24@1145 468
kaf24@954 469 return rc;
kaf24@954 470 }
kaf24@954 471
kaf24@954 472
kaf24@1218 473 static long evtchn_close(evtchn_close_t *close)
kaf24@1127 474 {
kaf24@1506 475 struct domain *d;
kaf24@1506 476 long rc;
kaf24@1506 477 domid_t dom = close->dom;
kaf24@1127 478
kaf24@1129 479 if ( dom == DOMID_SELF )
kaf24@4877 480 dom = current->domain->domain_id;
cl349@2919 481 else if ( !IS_PRIV(current->domain) )
kaf24@1127 482 return -EPERM;
kaf24@1127 483
kaf24@1506 484 if ( (d = find_domain_by_id(dom)) == NULL )
kaf24@1127 485 return -ESRCH;
kaf24@1127 486
kaf24@1506 487 rc = __evtchn_close(d, close->port);
kaf24@1127 488
kaf24@1506 489 put_domain(d);
kaf24@1127 490 return rc;
kaf24@1127 491 }
kaf24@1127 492
kaf24@1127 493
iap10@3290 494 long evtchn_send(int lport)
kaf24@954 495 {
kaf24@5308 496 struct evtchn *lchn, *rchn;
kaf24@5308 497 struct domain *ld = current->domain, *rd;
cl349@2937 498 int rport, ret = 0;
kaf24@954 499
kaf24@5308 500 spin_lock(&ld->evtchn_lock);
kaf24@954 501
kaf24@5308 502 if ( unlikely(!port_is_valid(ld, lport)) )
kaf24@954 503 {
kaf24@5308 504 spin_unlock(&ld->evtchn_lock);
kaf24@954 505 return -EINVAL;
kaf24@954 506 }
kaf24@954 507
kaf24@5308 508 lchn = evtchn_from_port(ld, lport);
kaf24@5308 509 switch ( lchn->state )
cl349@2937 510 {
cl349@2937 511 case ECS_INTERDOMAIN:
kaf24@5308 512 rd = lchn->u.interdomain.remote_dom;
kaf24@5308 513 rport = lchn->u.interdomain.remote_port;
kaf24@5308 514 rchn = evtchn_from_port(rd, rport);
kaf24@5308 515 evtchn_set_pending(rd->vcpu[rchn->notify_vcpu_id], rport);
cl349@2937 516 break;
cl349@2937 517 case ECS_IPI:
kaf24@5308 518 evtchn_set_pending(ld->vcpu[lchn->notify_vcpu_id], lport);
cl349@2937 519 break;
cl349@2937 520 default:
cl349@2937 521 ret = -EINVAL;
cl349@2937 522 }
kaf24@954 523
kaf24@5308 524 spin_unlock(&ld->evtchn_lock);
kaf24@954 525
cl349@2937 526 return ret;
kaf24@954 527 }
kaf24@954 528
kaf24@5308 529 void send_guest_pirq(struct domain *d, int pirq)
kaf24@5308 530 {
kaf24@5308 531 int port = d->pirq_to_evtchn[pirq];
kaf24@5308 532 struct evtchn *chn = evtchn_from_port(d, port);
kaf24@5308 533 evtchn_set_pending(d->vcpu[chn->notify_vcpu_id], port);
kaf24@5308 534 }
kaf24@954 535
kaf24@1218 536 static long evtchn_status(evtchn_status_t *status)
kaf24@954 537 {
kaf24@1506 538 struct domain *d;
kaf24@1506 539 domid_t dom = status->dom;
kaf24@1506 540 int port = status->port;
kaf24@5308 541 struct evtchn *chn;
kaf24@1506 542 long rc = 0;
kaf24@1127 543
kaf24@1129 544 if ( dom == DOMID_SELF )
kaf24@4877 545 dom = current->domain->domain_id;
cl349@2919 546 else if ( !IS_PRIV(current->domain) )
kaf24@1127 547 return -EPERM;
kaf24@1127 548
kaf24@1506 549 if ( (d = find_domain_by_id(dom)) == NULL )
kaf24@1127 550 return -ESRCH;
kaf24@954 551
kaf24@5308 552 spin_lock(&d->evtchn_lock);
kaf24@954 553
kaf24@5308 554 if ( !port_is_valid(d, port) )
kaf24@954 555 {
kaf24@1266 556 rc = -EINVAL;
kaf24@1266 557 goto out;
kaf24@1127 558 }
kaf24@1127 559
kaf24@5308 560 chn = evtchn_from_port(d, port);
kaf24@5308 561 switch ( chn->state )
kaf24@1127 562 {
kaf24@1127 563 case ECS_FREE:
cl349@3297 564 case ECS_RESERVED:
kaf24@1127 565 status->status = EVTCHNSTAT_closed;
kaf24@1127 566 break;
kaf24@1218 567 case ECS_UNBOUND:
kaf24@1218 568 status->status = EVTCHNSTAT_unbound;
kaf24@5308 569 status->u.unbound.dom = chn->u.unbound.remote_domid;
kaf24@1127 570 break;
kaf24@1218 571 case ECS_INTERDOMAIN:
kaf24@1218 572 status->status = EVTCHNSTAT_interdomain;
cl349@2924 573 status->u.interdomain.dom =
kaf24@5308 574 chn->u.interdomain.remote_dom->domain_id;
kaf24@5308 575 status->u.interdomain.port = chn->u.interdomain.remote_port;
kaf24@1218 576 break;
kaf24@1218 577 case ECS_PIRQ:
kaf24@1218 578 status->status = EVTCHNSTAT_pirq;
kaf24@5308 579 status->u.pirq = chn->u.pirq;
kaf24@1218 580 break;
kaf24@1218 581 case ECS_VIRQ:
kaf24@1218 582 status->status = EVTCHNSTAT_virq;
kaf24@5308 583 status->u.virq = chn->u.virq;
kaf24@1127 584 break;
cl349@2932 585 case ECS_IPI:
kaf24@5703 586 status->status = EVTCHNSTAT_ipi;
cl349@2932 587 break;
kaf24@1127 588 default:
kaf24@1127 589 BUG();
kaf24@954 590 }
kaf24@954 591
kaf24@5703 592 status->vcpu = chn->notify_vcpu_id;
kaf24@5703 593
kaf24@1266 594 out:
kaf24@5308 595 spin_unlock(&d->evtchn_lock);
kaf24@1506 596 put_domain(d);
kaf24@1266 597 return rc;
kaf24@954 598 }
kaf24@954 599
kaf24@5703 600 static long evtchn_bind_vcpu(evtchn_bind_vcpu_t *bind)
iap10@5691 601 {
iap10@5691 602 struct domain *d = current->domain;
iap10@5691 603 int port = bind->port;
iap10@5691 604 int vcpu = bind->vcpu;
iap10@5691 605 struct evtchn *chn;
kaf24@5703 606 long rc = 0;
kaf24@5703 607
sos22@5705 608 if ( (vcpu >= MAX_VIRT_CPUS) || (d->vcpu[vcpu] == NULL) ) {
kaf24@5703 609 return -EINVAL;
sos22@5705 610 }
iap10@5691 611
iap10@5691 612 spin_lock(&d->evtchn_lock);
iap10@5691 613
iap10@5691 614 if ( !port_is_valid(d, port) )
iap10@5691 615 {
iap10@5691 616 rc = -EINVAL;
iap10@5691 617 goto out;
iap10@5691 618 }
iap10@5691 619
iap10@5691 620 chn = evtchn_from_port(d, port);
kaf24@5703 621 switch ( chn->state )
kaf24@5703 622 {
kaf24@5703 623 case ECS_UNBOUND:
kaf24@5703 624 case ECS_INTERDOMAIN:
kaf24@5703 625 case ECS_PIRQ:
kaf24@5703 626 chn->notify_vcpu_id = vcpu;
kaf24@5703 627 break;
kaf24@5703 628 default:
kaf24@5703 629 rc = -EINVAL;
kaf24@5703 630 break;
kaf24@5703 631 }
iap10@5691 632
iap10@5691 633 out:
iap10@5691 634 spin_unlock(&d->evtchn_lock);
iap10@5691 635 return rc;
iap10@5691 636 }
kaf24@954 637
kaf24@1127 638 long do_event_channel_op(evtchn_op_t *uop)
kaf24@954 639 {
kaf24@954 640 long rc;
kaf24@1127 641 evtchn_op_t op;
kaf24@954 642
kaf24@1127 643 if ( copy_from_user(&op, uop, sizeof(op)) != 0 )
kaf24@1127 644 return -EFAULT;
kaf24@1127 645
smh22@5514 646 if (acm_pre_event_channel(&op))
smh22@5514 647 return -EACCES;
smh22@5514 648
kaf24@1127 649 switch ( op.cmd )
kaf24@954 650 {
kaf24@2713 651 case EVTCHNOP_alloc_unbound:
kaf24@2713 652 rc = evtchn_alloc_unbound(&op.u.alloc_unbound);
kaf24@2713 653 if ( (rc == 0) && (copy_to_user(uop, &op, sizeof(op)) != 0) )
kaf24@2713 654 rc = -EFAULT; /* Cleaning up here would be a mess! */
kaf24@2713 655 break;
kaf24@2713 656
kaf24@1218 657 case EVTCHNOP_bind_interdomain:
kaf24@1218 658 rc = evtchn_bind_interdomain(&op.u.bind_interdomain);
kaf24@1235 659 if ( (rc == 0) && (copy_to_user(uop, &op, sizeof(op)) != 0) )
kaf24@1218 660 rc = -EFAULT; /* Cleaning up here would be a mess! */
kaf24@1218 661 break;
kaf24@1218 662
kaf24@1218 663 case EVTCHNOP_bind_virq:
kaf24@1218 664 rc = evtchn_bind_virq(&op.u.bind_virq);
kaf24@1235 665 if ( (rc == 0) && (copy_to_user(uop, &op, sizeof(op)) != 0) )
kaf24@1235 666 rc = -EFAULT; /* Cleaning up here would be a mess! */
kaf24@1235 667 break;
kaf24@1235 668
cl349@2932 669 case EVTCHNOP_bind_ipi:
cl349@2932 670 rc = evtchn_bind_ipi(&op.u.bind_ipi);
cl349@2932 671 if ( (rc == 0) && (copy_to_user(uop, &op, sizeof(op)) != 0) )
cl349@2932 672 rc = -EFAULT; /* Cleaning up here would be a mess! */
cl349@2932 673 break;
cl349@2932 674
kaf24@1235 675 case EVTCHNOP_bind_pirq:
kaf24@1235 676 rc = evtchn_bind_pirq(&op.u.bind_pirq);
kaf24@1235 677 if ( (rc == 0) && (copy_to_user(uop, &op, sizeof(op)) != 0) )
kaf24@1127 678 rc = -EFAULT; /* Cleaning up here would be a mess! */
kaf24@954 679 break;
kaf24@954 680
kaf24@954 681 case EVTCHNOP_close:
kaf24@1218 682 rc = evtchn_close(&op.u.close);
kaf24@954 683 break;
kaf24@954 684
kaf24@954 685 case EVTCHNOP_send:
kaf24@1218 686 rc = evtchn_send(op.u.send.local_port);
kaf24@954 687 break;
kaf24@954 688
kaf24@954 689 case EVTCHNOP_status:
kaf24@1218 690 rc = evtchn_status(&op.u.status);
kaf24@1235 691 if ( (rc == 0) && (copy_to_user(uop, &op, sizeof(op)) != 0) )
kaf24@1127 692 rc = -EFAULT;
kaf24@954 693 break;
kaf24@954 694
kaf24@5703 695 case EVTCHNOP_bind_vcpu:
kaf24@5703 696 rc = evtchn_bind_vcpu(&op.u.bind_vcpu);
iap10@5691 697 break;
iap10@5691 698
kaf24@954 699 default:
kaf24@954 700 rc = -ENOSYS;
kaf24@954 701 break;
kaf24@954 702 }
kaf24@954 703
kaf24@954 704 return rc;
kaf24@954 705 }
kaf24@954 706
kaf24@954 707
kaf24@5308 708 int evtchn_init(struct domain *d)
kaf24@1218 709 {
kaf24@5308 710 spin_lock_init(&d->evtchn_lock);
kaf24@5308 711 if ( get_free_port(d) != 0 )
cl349@3291 712 return -EINVAL;
kaf24@5308 713 evtchn_from_port(d, 0)->state = ECS_RESERVED;
cl349@3291 714 return 0;
kaf24@1218 715 }
kaf24@1218 716
kaf24@1218 717
kaf24@5308 718 void evtchn_destroy(struct domain *d)
kaf24@954 719 {
kaf24@954 720 int i;
kaf24@5308 721
kaf24@5308 722 for ( i = 0; port_is_valid(d, i); i++ )
kaf24@1506 723 (void)__evtchn_close(d, i);
kaf24@5308 724
kaf24@5308 725 for ( i = 0; i < NR_EVTCHN_BUCKETS; i++ )
kaf24@5308 726 if ( d->evtchn[i] != NULL )
kaf24@5308 727 xfree(d->evtchn[i]);
kaf24@954 728 }
kaf24@3914 729
kaf24@3914 730 /*
kaf24@3914 731 * Local variables:
kaf24@3914 732 * mode: C
kaf24@3914 733 * c-set-style: "BSD"
kaf24@3914 734 * c-basic-offset: 4
kaf24@3914 735 * tab-width: 4
kaf24@3914 736 * indent-tabs-mode: nil
kaf24@3988 737 * End:
kaf24@3914 738 */