ia64/xen-unstable

annotate xen/common/event_channel.c @ 7204:ec84b119e4ed

Allow EVTCHNOP_bind_{ipi,virq} to specify the vcpu to
bind to. Previously the alloacted port was implicitly
bound to the calling vcpu.

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