Ignore:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • kernel/generic/src/ipc/sysipc.c

    r2541646 r6c34f587  
    3434
    3535#include <arch.h>
     36#include <proc/task.h>
     37#include <proc/thread.h>
    3638#include <errno.h>
    3739#include <memstr.h>
     40#include <debug.h>
    3841#include <ipc/ipc.h>
    3942#include <abi/ipc/methods.h>
    4043#include <ipc/sysipc.h>
    41 #include <ipc/sysipc_ops.h>
    42 #include <ipc/sysipc_priv.h>
    4344#include <ipc/irq.h>
    4445#include <ipc/ipcrsc.h>
     
    4647#include <ipc/kbox.h>
    4748#include <synch/waitq.h>
     49#include <udebug/udebug_ipc.h>
    4850#include <arch/interrupt.h>
    4951#include <syscall/copy.h>
    5052#include <security/cap.h>
    5153#include <console/console.h>
     54#include <mm/as.h>
    5255#include <print.h>
    5356#include <macros.h>
    5457
     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
    5564#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 */
     74static 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}
    5682
    5783/** Decide if the interface and method is a system method.
     
    155181 * @param olddata Saved data of the request.
    156182 *
    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 */
     186static 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
    169191                 */
    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);
    210200        }
    211201       
    212202        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
     397static 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
     409static void phones_unlock(phone_t *p1, phone_t *p2)
     410{
     411        mutex_unlock(&p1->lock);
     412        if (p1 != p2)
     413                mutex_unlock(&p2->lock);
    220414}
    221415
     
    230424static int request_preprocess(call_t *call, phone_t *phone)
    231425{
    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;
    241546}
    242547
     
    256561                IPC_SET_RETVAL(call->data, EFORWARD);
    257562       
    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}
    263585
    264586/** Do basic kernel processing of received call request.
     
    273595static int process_request(answerbox_t *box, call_t *call)
    274596{
    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;
    282616}
    283617
     
    411745{
    412746        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 
    419747        if (!call)
    420748                return ENOENT;
    421 
    422         if (need_old)
    423                 old = call->data;
    424        
     749       
     750        call->flags |= IPC_CALL_FORWARDED;
     751       
     752        phone_t *phone;
    425753        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;
    428757        }
    429758       
    430759        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        }
    436764       
    437765        /*
     
    469797        }
    470798       
    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);
    488800}
    489801
Note: See TracChangeset for help on using the changeset viewer.