Changeset e8039a86 in mainline for kernel/generic/src/ipc/sysipc.c


Ignore:
Timestamp:
2012-08-20T23:21:41Z (13 years ago)
Author:
Jakub Jermar <jakub@…>
Branches:
lfn, master, serial, ticket/834-toolchain-update, topic/msim-upgrade, topic/simplify-dev-export
Children:
0343a1b
Parents:
642dc72
Message:

Separate system IPC logic into dedicated ops structure hooks.

File:
1 edited

Legend:

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

    r642dc72 re8039a86  
    3434
    3535#include <arch.h>
    36 #include <proc/task.h>
    37 #include <proc/thread.h>
    3836#include <errno.h>
    3937#include <memstr.h>
    40 #include <debug.h>
    4138#include <ipc/ipc.h>
    4239#include <abi/ipc/methods.h>
    4340#include <ipc/sysipc.h>
     41#include <ipc/sysipc_ops.h>
    4442#include <ipc/irq.h>
    4543#include <ipc/ipcrsc.h>
     
    4745#include <ipc/kbox.h>
    4846#include <synch/waitq.h>
    49 #include <udebug/udebug_ipc.h>
    5047#include <arch/interrupt.h>
    5148#include <syscall/copy.h>
    5249#include <security/cap.h>
    5350#include <console/console.h>
    54 #include <mm/as.h>
    5551#include <print.h>
    5652#include <macros.h>
    5753
    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 
    6454#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 }
    8255
    8356/** Decide if the interface and method is a system method.
     
    174147}
    175148
    176 static void cleanup_m_connection_clone(call_t *answer, ipc_data_t *olddata)
    177 {
    178         int phoneid = (int) IPC_GET_ARG1(*olddata);
    179         phone_t *phone = &TASK->phones[phoneid];
    180 
    181         /*
    182          * In this case, the connection was established at the request time and
    183          * therefore we need to slam the phone.  We don't merely hangup as that
    184          * would result in sending IPC_M_HUNGUP to the third party on the other
    185          * side of the cloned phone.
    186          */
    187         mutex_lock(&phone->lock);
    188         if (phone->state == IPC_PHONE_CONNECTED) {
    189                 irq_spinlock_lock(&phone->callee->lock, true);
    190                 list_remove(&phone->link);
    191                 phone->state = IPC_PHONE_SLAMMED;
    192                 irq_spinlock_unlock(&phone->callee->lock, true);
    193         }
    194         mutex_unlock(&phone->lock);
    195 }
    196 
    197 static int a_preprocess_m_connection_clone(call_t *answer, ipc_data_t *olddata)
    198 {
    199         if (IPC_GET_RETVAL(answer->data) != EOK) {
    200                 /*
    201                  * The recipient of the cloned phone rejected the offer.
    202                  */
    203                 cleanup_m_connection_clone(answer, olddata);
    204         }
    205 
    206         return EOK;
    207 }
    208 
    209 static int a_preprocess_m_clone_establish(call_t *answer, ipc_data_t *olddata)
    210 {
    211         phone_t *phone = (phone_t *) IPC_GET_ARG5(*olddata);
    212 
    213         if (IPC_GET_RETVAL(answer->data) != EOK) {
    214                 /*
    215                  * The other party on the cloned phone rejected our request
    216                  * for connection on the protocol level.  We need to break the
    217                  * connection without sending IPC_M_HUNGUP back.
    218                  */
    219                 mutex_lock(&phone->lock);
    220                 if (phone->state == IPC_PHONE_CONNECTED) {
    221                         irq_spinlock_lock(&phone->callee->lock, true);
    222                         list_remove(&phone->link);
    223                         phone->state = IPC_PHONE_SLAMMED;
    224                         irq_spinlock_unlock(&phone->callee->lock, true);
    225                 }
    226                 mutex_unlock(&phone->lock);
    227         }
    228        
    229         return EOK;
    230 }
    231 
    232 static void cleanup_m_connect_to_me(call_t *answer, ipc_data_t *olddata)
    233 {
    234         int phoneid = (int) IPC_GET_ARG5(*olddata);
    235        
    236         phone_dealloc(phoneid);
    237 }
    238 
    239 static int a_preprocess_m_connect_to_me(call_t *answer, ipc_data_t *olddata)
    240 {
    241         int phoneid = (int) IPC_GET_ARG5(*olddata);
    242 
    243         if (IPC_GET_RETVAL(answer->data) != EOK) {
    244                 /* The connection was not accepted */
    245                 cleanup_m_connect_to_me(answer, olddata);
    246         } else {
    247                 /* The connection was accepted */
    248                 phone_connect(phoneid, &answer->sender->answerbox);
    249                 /* Set 'phone hash' as arg5 of response */
    250                 IPC_SET_ARG5(answer->data, (sysarg_t) &TASK->phones[phoneid]);
    251         }
    252 
    253         return EOK;
    254 }
    255 
    256 static void cleanup_m_connect_me_to(call_t *answer, ipc_data_t *olddata)
    257 {
    258         /* FIXME: answer->priv phone needs to be deallocated. */
    259 }
    260 
    261 static int a_preprocess_m_connect_me_to(call_t *answer, ipc_data_t *olddata)
    262 {
    263         phone_t *phone = (phone_t *) IPC_GET_ARG5(*olddata);
    264 
    265         /* If the user accepted call, connect */
    266         if (IPC_GET_RETVAL(answer->data) == EOK)
    267                 ipc_phone_connect(phone, &TASK->answerbox);
    268 
    269         return EOK;
    270 }
    271 
    272 static int a_preprocess_m_share_out(call_t *answer, ipc_data_t *olddata)
    273 {
    274         int rc = EOK;
    275 
    276         if (!IPC_GET_RETVAL(answer->data)) {
    277                 /* Accepted, handle as_area receipt */
    278 
    279                 irq_spinlock_lock(&answer->sender->lock, true);
    280                 as_t *as = answer->sender->as;
    281                 irq_spinlock_unlock(&answer->sender->lock, true);
    282 
    283                 uintptr_t dst_base = (uintptr_t) -1;
    284                 rc = as_area_share(as, IPC_GET_ARG1(*olddata),
    285                     IPC_GET_ARG2(*olddata), AS, IPC_GET_ARG3(*olddata),
    286                     &dst_base, IPC_GET_ARG1(answer->data));
    287                        
    288                 if (rc == EOK) {
    289                         rc = copy_to_uspace((void *) IPC_GET_ARG2(answer->data),
    290                             &dst_base, sizeof(dst_base));
    291                 }
    292                        
    293                 IPC_SET_RETVAL(answer->data, rc);
    294         }
    295 
    296         return rc;
    297 }
    298 
    299 static int a_preprocess_m_share_in(call_t *answer, ipc_data_t *olddata)
    300 {
    301         if (!IPC_GET_RETVAL(answer->data)) {
    302                 irq_spinlock_lock(&answer->sender->lock, true);
    303                 as_t *as = answer->sender->as;
    304                 irq_spinlock_unlock(&answer->sender->lock, true);
    305                        
    306                 uintptr_t dst_base = (uintptr_t) -1;
    307                 int rc = as_area_share(AS, IPC_GET_ARG1(answer->data),
    308                     IPC_GET_ARG1(*olddata), as, IPC_GET_ARG2(answer->data),
    309                     &dst_base, IPC_GET_ARG3(answer->data));
    310                 IPC_SET_ARG4(answer->data, dst_base);
    311                 IPC_SET_RETVAL(answer->data, rc);
    312         }
    313        
    314         return EOK;
    315 }
    316 
    317 static int a_preprocess_m_data_read(call_t *answer, ipc_data_t *olddata)
    318 {
    319         ASSERT(!answer->buffer);
    320         if (!IPC_GET_RETVAL(answer->data)) {
    321                 /* The recipient agreed to send data. */
    322                 uintptr_t src = IPC_GET_ARG1(answer->data);
    323                 uintptr_t dst = IPC_GET_ARG1(*olddata);
    324                 size_t max_size = IPC_GET_ARG2(*olddata);
    325                 size_t size = IPC_GET_ARG2(answer->data);
    326                 if (size && size <= max_size) {
    327                         /*
    328                          * Copy the destination VA so that this piece of
    329                          * information is not lost.
    330                          */
    331                         IPC_SET_ARG1(answer->data, dst);
    332                                
    333                         answer->buffer = malloc(size, 0);
    334                         int rc = copy_from_uspace(answer->buffer,
    335                             (void *) src, size);
    336                         if (rc) {
    337                                 IPC_SET_RETVAL(answer->data, rc);
    338                                 free(answer->buffer);
    339                                 answer->buffer = NULL;
    340                         }
    341                 } else if (!size) {
    342                         IPC_SET_RETVAL(answer->data, EOK);
    343                 } else {
    344                         IPC_SET_RETVAL(answer->data, ELIMIT);
    345                 }
    346         }
    347 
    348         return EOK;
    349 }
    350 
    351 static int a_preprocess_m_data_write(call_t *answer, ipc_data_t *olddata)
    352 {
    353         ASSERT(answer->buffer);
    354         if (!IPC_GET_RETVAL(answer->data)) {
    355                 /* The recipient agreed to receive data. */
    356                 uintptr_t dst = (uintptr_t)IPC_GET_ARG1(answer->data);
    357                 size_t size = (size_t)IPC_GET_ARG2(answer->data);
    358                 size_t max_size = (size_t)IPC_GET_ARG2(*olddata);
    359                        
    360                 if (size <= max_size) {
    361                         int rc = copy_to_uspace((void *) dst,
    362                             answer->buffer, size);
    363                         if (rc)
    364                                 IPC_SET_RETVAL(answer->data, rc);
    365                 } else {
    366                         IPC_SET_RETVAL(answer->data, ELIMIT);
    367                 }
    368         }
    369         free(answer->buffer);
    370         answer->buffer = NULL;
    371 
    372         return EOK;
    373 }
    374 
    375 static int
    376 a_preprocess_m_state_change_authorize(call_t *answer, ipc_data_t *olddata)
    377 {
    378         int rc = EOK;
    379 
    380         if (!IPC_GET_RETVAL(answer->data)) {
    381                 /* The recipient authorized the change of state. */
    382                 phone_t *recipient_phone;
    383                 task_t *other_task_s;
    384                 task_t *other_task_r;
    385 
    386                 rc = phone_get(IPC_GET_ARG1(answer->data),
    387                     &recipient_phone);
    388                 if (rc != EOK) {
    389                         IPC_SET_RETVAL(answer->data, ENOENT);
    390                         return ENOENT;
    391                 }
    392 
    393                 mutex_lock(&recipient_phone->lock);
    394                 if (recipient_phone->state != IPC_PHONE_CONNECTED) {
    395                         mutex_unlock(&recipient_phone->lock);
    396                         IPC_SET_RETVAL(answer->data, EINVAL);
    397                         return EINVAL;
    398                 }
    399 
    400                 other_task_r = recipient_phone->callee->task;
    401                 other_task_s = (task_t *) IPC_GET_ARG5(*olddata);
    402 
    403                 /*
    404                  * See if both the sender and the recipient meant the
    405                  * same third party task.
    406                  */
    407                 if (other_task_r != other_task_s) {
    408                         IPC_SET_RETVAL(answer->data, EINVAL);
    409                         rc = EINVAL;
    410                 } else {
    411                         rc = event_task_notify_5(other_task_r,
    412                             EVENT_TASK_STATE_CHANGE, false,
    413                             IPC_GET_ARG1(*olddata),
    414                             IPC_GET_ARG2(*olddata),
    415                             IPC_GET_ARG3(*olddata),
    416                             LOWER32(olddata->task_id),
    417                             UPPER32(olddata->task_id));
    418                         IPC_SET_RETVAL(answer->data, rc);
    419                 }
    420 
    421                 mutex_unlock(&recipient_phone->lock);
    422         }
    423 
    424         return rc;
    425 }
    426 
    427 /** Cleanup additional resources associated with the answer. */
    428 static void cleanup_forgotten(call_t *answer, ipc_data_t *olddata)
    429 {
    430         if (!olddata)
    431                 return;
    432 
    433         switch (IPC_GET_IMETHOD(*olddata)) {
    434         case IPC_M_CONNECTION_CLONE:
    435                 cleanup_m_connection_clone(answer, olddata);
    436                 break;
    437         case IPC_M_CONNECT_TO_ME:
    438                 cleanup_m_connect_to_me(answer, olddata);
    439                 break;
    440         case IPC_M_CONNECT_ME_TO:
    441                 cleanup_m_connect_me_to(answer, olddata);
    442                 break;
    443         default:
    444                 break;
    445         }
    446 }
    447 
    448149/** Interpret process answer as control information.
    449150 *
     
    466167                 */
    467168                spinlock_unlock(&answer->forget_lock);
    468                 cleanup_forgotten(answer, olddata);
     169                /* TODO: cleanup? */
    469170                return rc;
    470171        } else {
     
    496197        }
    497198       
    498         switch (IPC_GET_IMETHOD(*olddata)) {
    499         case IPC_M_CONNECTION_CLONE:
    500                 rc = a_preprocess_m_connection_clone(answer, olddata);
    501                 break;
    502         case IPC_M_CLONE_ESTABLISH:
    503                 rc = a_preprocess_m_clone_establish(answer, olddata);
    504                 break;
    505         case IPC_M_CONNECT_TO_ME:
    506                 rc = a_preprocess_m_connect_to_me(answer, olddata);
    507                 break;
    508         case IPC_M_CONNECT_ME_TO:
    509                 rc = a_preprocess_m_connect_me_to(answer, olddata);
    510                 break;
    511         case IPC_M_SHARE_OUT:
    512                 rc = a_preprocess_m_share_out(answer, olddata);
    513                 break;
    514         case IPC_M_SHARE_IN:
    515                 rc = a_preprocess_m_share_in(answer, olddata);
    516                 break;
    517         case IPC_M_DATA_READ:
    518                 rc = a_preprocess_m_data_read(answer, olddata);
    519                 break;
    520         case IPC_M_DATA_WRITE:
    521                 rc = a_preprocess_m_data_write(answer, olddata);
    522                 break;
    523         case IPC_M_STATE_CHANGE_AUTHORIZE:
    524                 rc = a_preprocess_m_state_change_authorize(answer, olddata);
    525                 break;
    526         default:
    527                 break;
    528         }
     199
     200        sysipc_ops_t *ops = sysipc_ops_get(IPC_GET_IMETHOD(*olddata));
     201        if (ops->answer_preprocess)
     202                rc = ops->answer_preprocess(answer, olddata);
    529203       
    530204        task_release(answer->sender);
    531205
    532206        return rc;
    533 }
    534 
    535 static void phones_lock(phone_t *p1, phone_t *p2)
    536 {
    537         if (p1 < p2) {
    538                 mutex_lock(&p1->lock);
    539                 mutex_lock(&p2->lock);
    540         } else if (p1 > p2) {
    541                 mutex_lock(&p2->lock);
    542                 mutex_lock(&p1->lock);
    543         } else
    544                 mutex_lock(&p1->lock);
    545 }
    546 
    547 static void phones_unlock(phone_t *p1, phone_t *p2)
    548 {
    549         mutex_unlock(&p1->lock);
    550         if (p1 != p2)
    551                 mutex_unlock(&p2->lock);
    552 }
    553 
    554 static int r_preprocess_m_connection_clone(call_t *call, phone_t *phone)
    555 {
    556         phone_t *cloned_phone;
    557 
    558         if (phone_get(IPC_GET_ARG1(call->data), &cloned_phone) != EOK)
    559                 return ENOENT;
    560                
    561         phones_lock(cloned_phone, phone);
    562                
    563         if ((cloned_phone->state != IPC_PHONE_CONNECTED) ||
    564             phone->state != IPC_PHONE_CONNECTED) {
    565                 phones_unlock(cloned_phone, phone);
    566                 return EINVAL;
    567         }
    568                
    569         /*
    570          * We can be pretty sure now that both tasks exist and we are
    571          * connected to them. As we continue to hold the phone locks,
    572          * we are effectively preventing them from finishing their
    573          * potential cleanup.
    574          *
    575          */
    576         int newphid = phone_alloc(phone->callee->task);
    577         if (newphid < 0) {
    578                 phones_unlock(cloned_phone, phone);
    579                 return ELIMIT;
    580         }
    581                
    582         ipc_phone_connect(&phone->callee->task->phones[newphid],
    583             cloned_phone->callee);
    584         phones_unlock(cloned_phone, phone);
    585                
    586         /* Set the new phone for the callee. */
    587         IPC_SET_ARG1(call->data, newphid);
    588 
    589         return EOK;
    590 }
    591 
    592 static int r_preprocess_m_clone_establish(call_t *call, phone_t *phone)
    593 {
    594         IPC_SET_ARG5(call->data, (sysarg_t) phone);
    595 
    596         return EOK;     
    597 }
    598 
    599 static int r_preprocess_m_connect_me_to(call_t *call, phone_t *phone)
    600 {
    601         int newphid = phone_alloc(TASK);
    602 
    603         if (newphid < 0)
    604                 return ELIMIT;
    605                
    606         /* Set arg5 for server */
    607         IPC_SET_ARG5(call->data, (sysarg_t) &TASK->phones[newphid]);
    608         call->flags |= IPC_CALL_CONN_ME_TO;
    609         call->priv = newphid;
    610 
    611         return EOK;
    612 }
    613 
    614 static int r_preprocess_m_share_out(call_t *call, phone_t *phone)
    615 {
    616         size_t size = as_area_get_size(IPC_GET_ARG1(call->data));
    617 
    618         if (!size)
    619                 return EPERM;
    620         IPC_SET_ARG2(call->data, size);
    621 
    622         return EOK;
    623 }
    624 
    625 static int r_preprocess_m_data_read(call_t *call, phone_t *phone)
    626 {
    627         size_t size = IPC_GET_ARG2(call->data);
    628 
    629         if (size > DATA_XFER_LIMIT) {
    630                 int flags = IPC_GET_ARG3(call->data);
    631 
    632                 if (flags & IPC_XF_RESTRICT)
    633                         IPC_SET_ARG2(call->data, DATA_XFER_LIMIT);
    634                 else
    635                         return ELIMIT;
    636         }
    637 
    638         return EOK;
    639 }
    640 
    641 static int r_preprocess_m_data_write(call_t *call, phone_t *phone)
    642 {
    643         uintptr_t src = IPC_GET_ARG1(call->data);
    644         size_t size = IPC_GET_ARG2(call->data);
    645 
    646         if (size > DATA_XFER_LIMIT) {
    647                 int flags = IPC_GET_ARG3(call->data);
    648 
    649                 if (flags & IPC_XF_RESTRICT) {
    650                         size = DATA_XFER_LIMIT;
    651                         IPC_SET_ARG2(call->data, size);
    652                 } else
    653                         return ELIMIT;
    654         }
    655 
    656         call->buffer = (uint8_t *) malloc(size, 0);
    657         int rc = copy_from_uspace(call->buffer, (void *) src, size);
    658         if (rc != 0) {
    659                 free(call->buffer);
    660                 return rc;
    661         }
    662                
    663         return EOK;
    664 }
    665 
    666 static int r_preprocess_m_state_change_authorize(call_t *call, phone_t *phone)
    667 {
    668         phone_t *sender_phone;
    669         task_t *other_task_s;
    670 
    671         if (phone_get(IPC_GET_ARG5(call->data), &sender_phone) != EOK)
    672                 return ENOENT;
    673 
    674         mutex_lock(&sender_phone->lock);
    675         if (sender_phone->state != IPC_PHONE_CONNECTED) {
    676                 mutex_unlock(&sender_phone->lock);
    677                 return EINVAL;
    678         }
    679 
    680         other_task_s = sender_phone->callee->task;
    681 
    682         mutex_unlock(&sender_phone->lock);
    683 
    684         /* Remember the third party task hash. */
    685         IPC_SET_ARG5(call->data, (sysarg_t) other_task_s);
    686 
    687         return EOK;
    688207}
    689208
     
    700219        int rc = EOK;
    701220
    702         switch (IPC_GET_IMETHOD(call->data)) {
    703         case IPC_M_CONNECTION_CLONE:
    704                 rc = r_preprocess_m_connection_clone(call, phone);
    705                 break;
    706         case IPC_M_CLONE_ESTABLISH:
    707                 rc = r_preprocess_m_clone_establish(call, phone);
    708                 break;
    709         case IPC_M_CONNECT_ME_TO:
    710                 rc = r_preprocess_m_connect_me_to(call, phone);
    711                 break;
    712         case IPC_M_SHARE_OUT:
    713                 rc = r_preprocess_m_share_out(call, phone);
    714                 break;
    715         case IPC_M_DATA_READ:
    716                 rc = r_preprocess_m_data_read(call, phone);
    717                 break;
    718         case IPC_M_DATA_WRITE:
    719                 rc = r_preprocess_m_data_write(call, phone);
    720                 break;
    721         case IPC_M_STATE_CHANGE_AUTHORIZE:
    722                 rc = r_preprocess_m_state_change_authorize(call, phone);
    723                 break;
    724 #ifdef CONFIG_UDEBUG
    725         case IPC_M_DEBUG:
    726                 rc = udebug_request_preprocess(call, phone);
    727                 break;
    728 #endif
    729         default:
    730                 break;
    731         }
     221        sysipc_ops_t *ops = sysipc_ops_get(IPC_GET_IMETHOD(call->data));
     222        if (ops->request_preprocess)
     223                rc = ops->request_preprocess(call, phone);
    732224       
    733225        return rc;
     
    772264}
    773265
    774 static int r_process_m_connect_to_me(answerbox_t *box, call_t *call)
    775 {
    776         int phoneid = phone_alloc(TASK);
    777 
    778         if (phoneid < 0) {  /* Failed to allocate phone */
    779                 IPC_SET_RETVAL(call->data, ELIMIT);
    780                 ipc_answer(box, call);
    781                 return -1;
    782         }
    783                
    784         IPC_SET_ARG5(call->data, phoneid);
    785        
    786         return EOK;
    787 }
    788 
    789 static int r_process_m_debug(answerbox_t *box, call_t *call)
    790 {
    791         return -1;
    792 }
    793266
    794267/** Do basic kernel processing of received call request.
     
    805278        int rc = EOK;
    806279
    807         switch (IPC_GET_IMETHOD(call->data)) {
    808         case IPC_M_CONNECT_TO_ME:
    809                 rc = r_process_m_connect_to_me(box, call);
    810                 break;
    811         case IPC_M_DEBUG:
    812                 rc = r_process_m_debug(box, call);
    813                 break;
    814         default:
    815                 break;
    816         }
     280        sysipc_ops_t *ops = sysipc_ops_get(IPC_GET_IMETHOD(call->data));
     281        if (ops->request_process)
     282                rc = ops->request_process(call, box);
    817283       
    818284        return rc;
Note: See TracChangeset for help on using the changeset viewer.