Changes in kernel/generic/src/ipc/sysipc.c [2541646:6c34f587] in mainline
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
kernel/generic/src/ipc/sysipc.c
r2541646 r6c34f587 34 34 35 35 #include <arch.h> 36 #include <proc/task.h> 37 #include <proc/thread.h> 36 38 #include <errno.h> 37 39 #include <memstr.h> 40 #include <debug.h> 38 41 #include <ipc/ipc.h> 39 42 #include <abi/ipc/methods.h> 40 43 #include <ipc/sysipc.h> 41 #include <ipc/sysipc_ops.h>42 #include <ipc/sysipc_priv.h>43 44 #include <ipc/irq.h> 44 45 #include <ipc/ipcrsc.h> … … 46 47 #include <ipc/kbox.h> 47 48 #include <synch/waitq.h> 49 #include <udebug/udebug_ipc.h> 48 50 #include <arch/interrupt.h> 49 51 #include <syscall/copy.h> 50 52 #include <security/cap.h> 51 53 #include <console/console.h> 54 #include <mm/as.h> 52 55 #include <print.h> 53 56 #include <macros.h> 54 57 58 /** 59 * Maximum buffer size allowed for IPC_M_DATA_WRITE and IPC_M_DATA_READ 60 * requests. 61 */ 62 #define DATA_XFER_LIMIT (64 * 1024) 63 55 64 #define STRUCT_TO_USPACE(dst, src) copy_to_uspace((dst), (src), sizeof(*(src))) 65 66 /** Get phone from the current task by ID. 67 * 68 * @param phoneid Phone ID. 69 * @param phone Place to store pointer to phone. 70 * 71 * @return EOK on success, EINVAL if ID is invalid. 72 * 73 */ 74 static int phone_get(sysarg_t phoneid, phone_t **phone) 75 { 76 if (phoneid >= IPC_MAX_PHONES) 77 return EINVAL; 78 79 *phone = &TASK->phones[phoneid]; 80 return EOK; 81 } 56 82 57 83 /** Decide if the interface and method is a system method. … … 155 181 * @param olddata Saved data of the request. 156 182 * 157 * @return Return EOK on success or a negative error code. 158 * 159 */ 160 int answer_preprocess(call_t *answer, ipc_data_t *olddata) 161 { 162 int rc = EOK; 163 sysipc_ops_t *ops; 164 165 spinlock_lock(&answer->forget_lock); 166 if (answer->forget) { 167 /* 168 * This is a forgotten call and answer->sender is not valid. 183 * @return Return 0 on success or an error code. 184 * 185 */ 186 static inline int answer_preprocess(call_t *answer, ipc_data_t *olddata) 187 { 188 if ((native_t) IPC_GET_RETVAL(answer->data) == EHANGUP) { 189 /* In case of forward, hangup the forwared phone, 190 * not the originator 169 191 */ 170 spinlock_unlock(&answer->forget_lock); 171 172 ops = sysipc_ops_get(answer->request_method); 173 if (ops->answer_cleanup) 174 ops->answer_cleanup(answer, olddata); 175 176 return rc; 177 } else { 178 ASSERT(answer->active); 179 180 /* 181 * Mark the call as inactive to prevent _ipc_answer_free_call() 182 * from attempting to remove the call from the active list 183 * itself. 184 */ 185 answer->active = false; 186 187 /* 188 * Remove the call from the sender's active call list. 189 * We enforce this locking order so that any potential 190 * concurrently executing forget operation is forced to 191 * release its active_calls_lock and lose the race to 192 * forget this soon to be answered call. 193 */ 194 spinlock_lock(&answer->sender->active_calls_lock); 195 list_remove(&answer->ta_link); 196 spinlock_unlock(&answer->sender->active_calls_lock); 197 } 198 spinlock_unlock(&answer->forget_lock); 199 200 if ((native_t) IPC_GET_RETVAL(answer->data) == EHANGUP) { 201 phone_t *phone = answer->caller_phone; 202 mutex_lock(&phone->lock); 203 if (phone->state == IPC_PHONE_CONNECTED) { 204 irq_spinlock_lock(&phone->callee->lock, true); 205 list_remove(&phone->link); 206 phone->state = IPC_PHONE_SLAMMED; 207 irq_spinlock_unlock(&phone->callee->lock, true); 208 } 209 mutex_unlock(&phone->lock); 192 mutex_lock(&answer->data.phone->lock); 193 irq_spinlock_lock(&TASK->answerbox.lock, true); 194 if (answer->data.phone->state == IPC_PHONE_CONNECTED) { 195 list_remove(&answer->data.phone->link); 196 answer->data.phone->state = IPC_PHONE_SLAMMED; 197 } 198 irq_spinlock_unlock(&TASK->answerbox.lock, true); 199 mutex_unlock(&answer->data.phone->lock); 210 200 } 211 201 212 202 if (!olddata) 213 return rc; 214 215 ops = sysipc_ops_get(answer->request_method); 216 if (ops->answer_preprocess) 217 rc = ops->answer_preprocess(answer, olddata); 218 219 return rc; 203 return 0; 204 205 if (IPC_GET_IMETHOD(*olddata) == IPC_M_CONNECTION_CLONE) { 206 int phoneid = IPC_GET_ARG1(*olddata); 207 phone_t *phone = &TASK->phones[phoneid]; 208 209 if (IPC_GET_RETVAL(answer->data) != EOK) { 210 /* 211 * The recipient of the cloned phone rejected the offer. 212 * In this case, the connection was established at the 213 * request time and therefore we need to slam the phone. 214 * We don't merely hangup as that would result in 215 * sending IPC_M_HUNGUP to the third party on the 216 * other side of the cloned phone. 217 */ 218 mutex_lock(&phone->lock); 219 if (phone->state == IPC_PHONE_CONNECTED) { 220 irq_spinlock_lock(&phone->callee->lock, true); 221 list_remove(&phone->link); 222 phone->state = IPC_PHONE_SLAMMED; 223 irq_spinlock_unlock(&phone->callee->lock, true); 224 } 225 mutex_unlock(&phone->lock); 226 } 227 } else if (IPC_GET_IMETHOD(*olddata) == IPC_M_CLONE_ESTABLISH) { 228 phone_t *phone = (phone_t *) IPC_GET_ARG5(*olddata); 229 230 if (IPC_GET_RETVAL(answer->data) != EOK) { 231 /* 232 * The other party on the cloned phoned rejected our 233 * request for connection on the protocol level. 234 * We need to break the connection without sending 235 * IPC_M_HUNGUP back. 236 */ 237 mutex_lock(&phone->lock); 238 if (phone->state == IPC_PHONE_CONNECTED) { 239 irq_spinlock_lock(&phone->callee->lock, true); 240 list_remove(&phone->link); 241 phone->state = IPC_PHONE_SLAMMED; 242 irq_spinlock_unlock(&phone->callee->lock, true); 243 } 244 mutex_unlock(&phone->lock); 245 } 246 } else if (IPC_GET_IMETHOD(*olddata) == IPC_M_CONNECT_TO_ME) { 247 int phoneid = IPC_GET_ARG5(*olddata); 248 249 if (IPC_GET_RETVAL(answer->data) != EOK) { 250 /* The connection was not accepted */ 251 phone_dealloc(phoneid); 252 } else { 253 /* The connection was accepted */ 254 phone_connect(phoneid, &answer->sender->answerbox); 255 /* Set 'phone hash' as arg5 of response */ 256 IPC_SET_ARG5(answer->data, 257 (sysarg_t) &TASK->phones[phoneid]); 258 } 259 } else if (IPC_GET_IMETHOD(*olddata) == IPC_M_CONNECT_ME_TO) { 260 /* If the users accepted call, connect */ 261 if (IPC_GET_RETVAL(answer->data) == EOK) { 262 ipc_phone_connect((phone_t *) IPC_GET_ARG5(*olddata), 263 &TASK->answerbox); 264 } 265 } else if (IPC_GET_IMETHOD(*olddata) == IPC_M_SHARE_OUT) { 266 if (!IPC_GET_RETVAL(answer->data)) { 267 /* Accepted, handle as_area receipt */ 268 269 irq_spinlock_lock(&answer->sender->lock, true); 270 as_t *as = answer->sender->as; 271 irq_spinlock_unlock(&answer->sender->lock, true); 272 273 uintptr_t dst_base = (uintptr_t) -1; 274 int rc = as_area_share(as, IPC_GET_ARG1(*olddata), 275 IPC_GET_ARG2(*olddata), AS, IPC_GET_ARG3(*olddata), 276 &dst_base, IPC_GET_ARG1(answer->data)); 277 278 if (rc == EOK) 279 rc = copy_to_uspace((void *) IPC_GET_ARG2(answer->data), 280 &dst_base, sizeof(dst_base)); 281 282 IPC_SET_RETVAL(answer->data, rc); 283 return rc; 284 } 285 } else if (IPC_GET_IMETHOD(*olddata) == IPC_M_SHARE_IN) { 286 if (!IPC_GET_RETVAL(answer->data)) { 287 irq_spinlock_lock(&answer->sender->lock, true); 288 as_t *as = answer->sender->as; 289 irq_spinlock_unlock(&answer->sender->lock, true); 290 291 uintptr_t dst_base = (uintptr_t) -1; 292 int rc = as_area_share(AS, IPC_GET_ARG1(answer->data), 293 IPC_GET_ARG1(*olddata), as, IPC_GET_ARG2(answer->data), 294 &dst_base, IPC_GET_ARG3(answer->data)); 295 IPC_SET_ARG4(answer->data, dst_base); 296 IPC_SET_RETVAL(answer->data, rc); 297 } 298 } else if (IPC_GET_IMETHOD(*olddata) == IPC_M_DATA_READ) { 299 ASSERT(!answer->buffer); 300 if (!IPC_GET_RETVAL(answer->data)) { 301 /* The recipient agreed to send data. */ 302 uintptr_t src = IPC_GET_ARG1(answer->data); 303 uintptr_t dst = IPC_GET_ARG1(*olddata); 304 size_t max_size = IPC_GET_ARG2(*olddata); 305 size_t size = IPC_GET_ARG2(answer->data); 306 if (size && size <= max_size) { 307 /* 308 * Copy the destination VA so that this piece of 309 * information is not lost. 310 */ 311 IPC_SET_ARG1(answer->data, dst); 312 313 answer->buffer = malloc(size, 0); 314 int rc = copy_from_uspace(answer->buffer, 315 (void *) src, size); 316 if (rc) { 317 IPC_SET_RETVAL(answer->data, rc); 318 free(answer->buffer); 319 answer->buffer = NULL; 320 } 321 } else if (!size) { 322 IPC_SET_RETVAL(answer->data, EOK); 323 } else { 324 IPC_SET_RETVAL(answer->data, ELIMIT); 325 } 326 } 327 } else if (IPC_GET_IMETHOD(*olddata) == IPC_M_DATA_WRITE) { 328 ASSERT(answer->buffer); 329 if (!IPC_GET_RETVAL(answer->data)) { 330 /* The recipient agreed to receive data. */ 331 uintptr_t dst = (uintptr_t)IPC_GET_ARG1(answer->data); 332 size_t size = (size_t)IPC_GET_ARG2(answer->data); 333 size_t max_size = (size_t)IPC_GET_ARG2(*olddata); 334 335 if (size <= max_size) { 336 int rc = copy_to_uspace((void *) dst, 337 answer->buffer, size); 338 if (rc) 339 IPC_SET_RETVAL(answer->data, rc); 340 } else { 341 IPC_SET_RETVAL(answer->data, ELIMIT); 342 } 343 } 344 free(answer->buffer); 345 answer->buffer = NULL; 346 } else if (IPC_GET_IMETHOD(*olddata) == IPC_M_STATE_CHANGE_AUTHORIZE) { 347 if (!IPC_GET_RETVAL(answer->data)) { 348 /* The recipient authorized the change of state. */ 349 phone_t *recipient_phone; 350 task_t *other_task_s; 351 task_t *other_task_r; 352 int rc; 353 354 rc = phone_get(IPC_GET_ARG1(answer->data), 355 &recipient_phone); 356 if (rc != EOK) { 357 IPC_SET_RETVAL(answer->data, ENOENT); 358 return ENOENT; 359 } 360 361 mutex_lock(&recipient_phone->lock); 362 if (recipient_phone->state != IPC_PHONE_CONNECTED) { 363 mutex_unlock(&recipient_phone->lock); 364 IPC_SET_RETVAL(answer->data, EINVAL); 365 return EINVAL; 366 } 367 368 other_task_r = recipient_phone->callee->task; 369 other_task_s = (task_t *) IPC_GET_ARG5(*olddata); 370 371 /* 372 * See if both the sender and the recipient meant the 373 * same third party task. 374 */ 375 if (other_task_r != other_task_s) { 376 IPC_SET_RETVAL(answer->data, EINVAL); 377 rc = EINVAL; 378 } else { 379 rc = event_task_notify_5(other_task_r, 380 EVENT_TASK_STATE_CHANGE, false, 381 IPC_GET_ARG1(*olddata), 382 IPC_GET_ARG2(*olddata), 383 IPC_GET_ARG3(*olddata), 384 LOWER32(olddata->task_id), 385 UPPER32(olddata->task_id)); 386 IPC_SET_RETVAL(answer->data, rc); 387 } 388 389 mutex_unlock(&recipient_phone->lock); 390 return rc; 391 } 392 } 393 394 return 0; 395 } 396 397 static void phones_lock(phone_t *p1, phone_t *p2) 398 { 399 if (p1 < p2) { 400 mutex_lock(&p1->lock); 401 mutex_lock(&p2->lock); 402 } else if (p1 > p2) { 403 mutex_lock(&p2->lock); 404 mutex_lock(&p1->lock); 405 } else 406 mutex_lock(&p1->lock); 407 } 408 409 static void phones_unlock(phone_t *p1, phone_t *p2) 410 { 411 mutex_unlock(&p1->lock); 412 if (p1 != p2) 413 mutex_unlock(&p2->lock); 220 414 } 221 415 … … 230 424 static int request_preprocess(call_t *call, phone_t *phone) 231 425 { 232 int rc = EOK; 233 234 call->request_method = IPC_GET_IMETHOD(call->data); 235 236 sysipc_ops_t *ops = sysipc_ops_get(call->request_method); 237 if (ops->request_preprocess) 238 rc = ops->request_preprocess(call, phone); 239 240 return rc; 426 switch (IPC_GET_IMETHOD(call->data)) { 427 case IPC_M_CONNECTION_CLONE: { 428 phone_t *cloned_phone; 429 if (phone_get(IPC_GET_ARG1(call->data), &cloned_phone) != EOK) 430 return ENOENT; 431 432 phones_lock(cloned_phone, phone); 433 434 if ((cloned_phone->state != IPC_PHONE_CONNECTED) || 435 phone->state != IPC_PHONE_CONNECTED) { 436 phones_unlock(cloned_phone, phone); 437 return EINVAL; 438 } 439 440 /* 441 * We can be pretty sure now that both tasks exist and we are 442 * connected to them. As we continue to hold the phone locks, 443 * we are effectively preventing them from finishing their 444 * potential cleanup. 445 * 446 */ 447 int newphid = phone_alloc(phone->callee->task); 448 if (newphid < 0) { 449 phones_unlock(cloned_phone, phone); 450 return ELIMIT; 451 } 452 453 ipc_phone_connect(&phone->callee->task->phones[newphid], 454 cloned_phone->callee); 455 phones_unlock(cloned_phone, phone); 456 457 /* Set the new phone for the callee. */ 458 IPC_SET_ARG1(call->data, newphid); 459 break; 460 } 461 case IPC_M_CLONE_ESTABLISH: 462 IPC_SET_ARG5(call->data, (sysarg_t) phone); 463 break; 464 case IPC_M_CONNECT_ME_TO: { 465 int newphid = phone_alloc(TASK); 466 if (newphid < 0) 467 return ELIMIT; 468 469 /* Set arg5 for server */ 470 IPC_SET_ARG5(call->data, (sysarg_t) &TASK->phones[newphid]); 471 call->flags |= IPC_CALL_CONN_ME_TO; 472 call->priv = newphid; 473 break; 474 } 475 case IPC_M_SHARE_OUT: { 476 size_t size = as_area_get_size(IPC_GET_ARG1(call->data)); 477 if (!size) 478 return EPERM; 479 480 IPC_SET_ARG2(call->data, size); 481 break; 482 } 483 case IPC_M_DATA_READ: { 484 size_t size = IPC_GET_ARG2(call->data); 485 if (size > DATA_XFER_LIMIT) { 486 int flags = IPC_GET_ARG3(call->data); 487 if (flags & IPC_XF_RESTRICT) 488 IPC_SET_ARG2(call->data, DATA_XFER_LIMIT); 489 else 490 return ELIMIT; 491 } 492 break; 493 } 494 case IPC_M_DATA_WRITE: { 495 uintptr_t src = IPC_GET_ARG1(call->data); 496 size_t size = IPC_GET_ARG2(call->data); 497 498 if (size > DATA_XFER_LIMIT) { 499 int flags = IPC_GET_ARG3(call->data); 500 if (flags & IPC_XF_RESTRICT) { 501 size = DATA_XFER_LIMIT; 502 IPC_SET_ARG2(call->data, size); 503 } else 504 return ELIMIT; 505 } 506 507 call->buffer = (uint8_t *) malloc(size, 0); 508 int rc = copy_from_uspace(call->buffer, (void *) src, size); 509 if (rc != 0) { 510 free(call->buffer); 511 return rc; 512 } 513 514 break; 515 } 516 case IPC_M_STATE_CHANGE_AUTHORIZE: { 517 phone_t *sender_phone; 518 task_t *other_task_s; 519 520 if (phone_get(IPC_GET_ARG5(call->data), &sender_phone) != EOK) 521 return ENOENT; 522 523 mutex_lock(&sender_phone->lock); 524 if (sender_phone->state != IPC_PHONE_CONNECTED) { 525 mutex_unlock(&sender_phone->lock); 526 return EINVAL; 527 } 528 529 other_task_s = sender_phone->callee->task; 530 531 mutex_unlock(&sender_phone->lock); 532 533 /* Remember the third party task hash. */ 534 IPC_SET_ARG5(call->data, (sysarg_t) other_task_s); 535 break; 536 } 537 #ifdef CONFIG_UDEBUG 538 case IPC_M_DEBUG: 539 return udebug_request_preprocess(call, phone); 540 #endif 541 default: 542 break; 543 } 544 545 return 0; 241 546 } 242 547 … … 256 561 IPC_SET_RETVAL(call->data, EFORWARD); 257 562 258 sysipc_ops_t *ops = sysipc_ops_get(call->request_method); 259 if (ops->answer_process) 260 (void) ops->answer_process(call); 261 } 262 563 if (call->flags & IPC_CALL_CONN_ME_TO) { 564 if (IPC_GET_RETVAL(call->data)) 565 phone_dealloc(call->priv); 566 else 567 IPC_SET_ARG5(call->data, call->priv); 568 } 569 570 if (call->buffer) { 571 /* 572 * This must be an affirmative answer to IPC_M_DATA_READ 573 * or IPC_M_DEBUG/UDEBUG_M_MEM_READ... 574 * 575 */ 576 uintptr_t dst = IPC_GET_ARG1(call->data); 577 size_t size = IPC_GET_ARG2(call->data); 578 int rc = copy_to_uspace((void *) dst, call->buffer, size); 579 if (rc) 580 IPC_SET_RETVAL(call->data, rc); 581 free(call->buffer); 582 call->buffer = NULL; 583 } 584 } 263 585 264 586 /** Do basic kernel processing of received call request. … … 273 595 static int process_request(answerbox_t *box, call_t *call) 274 596 { 275 int rc = EOK; 276 277 sysipc_ops_t *ops = sysipc_ops_get(call->request_method); 278 if (ops->request_process) 279 rc = ops->request_process(call, box); 280 281 return rc; 597 if (IPC_GET_IMETHOD(call->data) == IPC_M_CONNECT_TO_ME) { 598 int phoneid = phone_alloc(TASK); 599 if (phoneid < 0) { /* Failed to allocate phone */ 600 IPC_SET_RETVAL(call->data, ELIMIT); 601 ipc_answer(box, call); 602 return -1; 603 } 604 605 IPC_SET_ARG5(call->data, phoneid); 606 } 607 608 switch (IPC_GET_IMETHOD(call->data)) { 609 case IPC_M_DEBUG: 610 return -1; 611 default: 612 break; 613 } 614 615 return 0; 282 616 } 283 617 … … 411 745 { 412 746 call_t *call = get_call(callid); 413 phone_t *phone;414 bool need_old = answer_need_old(call);415 bool after_forward = false;416 ipc_data_t old;417 int rc;418 419 747 if (!call) 420 748 return ENOENT; 421 422 if (need_old)423 old = call->data;424 749 750 call->flags |= IPC_CALL_FORWARDED; 751 752 phone_t *phone; 425 753 if (phone_get(phoneid, &phone) != EOK) { 426 rc = ENOENT; 427 goto error; 754 IPC_SET_RETVAL(call->data, EFORWARD); 755 ipc_answer(&TASK->answerbox, call); 756 return ENOENT; 428 757 } 429 758 430 759 if (!method_is_forwardable(IPC_GET_IMETHOD(call->data))) { 431 rc = EPERM; 432 goto error; 433 } 434 435 call->flags |= IPC_CALL_FORWARDED; 760 IPC_SET_RETVAL(call->data, EFORWARD); 761 ipc_answer(&TASK->answerbox, call); 762 return EPERM; 763 } 436 764 437 765 /* … … 469 797 } 470 798 471 rc = ipc_forward(call, phone, &TASK->answerbox, mode); 472 if (rc != EOK) { 473 after_forward = true; 474 goto error; 475 } 476 477 return EOK; 478 479 error: 480 IPC_SET_RETVAL(call->data, EFORWARD); 481 (void) answer_preprocess(call, need_old ? &old : NULL); 482 if (after_forward) 483 _ipc_answer_free_call(call, false); 484 else 485 ipc_answer(&TASK->answerbox, call); 486 487 return rc; 799 return ipc_forward(call, phone, &TASK->answerbox, mode); 488 800 } 489 801
Note:
See TracChangeset
for help on using the changeset viewer.