Subversion Repositories HelenOS

Rev

Rev 1923 | Rev 2098 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

  1. /*
  2.  * Copyright (c) 2006 Ondrej Palkovsky
  3.  * All rights reserved.
  4.  *
  5.  * Redistribution and use in source and binary forms, with or without
  6.  * modification, are permitted provided that the following conditions
  7.  * are met:
  8.  *
  9.  * - Redistributions of source code must retain the above copyright
  10.  *   notice, this list of conditions and the following disclaimer.
  11.  * - Redistributions in binary form must reproduce the above copyright
  12.  *   notice, this list of conditions and the following disclaimer in the
  13.  *   documentation and/or other materials provided with the distribution.
  14.  * - The name of the author may not be used to endorse or promote products
  15.  *   derived from this software without specific prior written permission.
  16.  *
  17.  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
  18.  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  19.  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
  20.  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
  21.  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  22.  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  23.  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  24.  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  25.  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  26.  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  27.  */
  28.  
  29. /** @addtogroup genericipc
  30.  * @{
  31.  */
  32. /** @file
  33.  */
  34.  
  35. #include <arch.h>
  36. #include <proc/task.h>
  37. #include <proc/thread.h>
  38. #include <errno.h>
  39. #include <memstr.h>
  40. #include <debug.h>
  41. #include <ipc/ipc.h>
  42. #include <ipc/sysipc.h>
  43. #include <ipc/irq.h>
  44. #include <ipc/ipcrsc.h>
  45. #include <arch/interrupt.h>
  46. #include <print.h>
  47. #include <syscall/copy.h>
  48. #include <security/cap.h>
  49. #include <mm/as.h>
  50. #include <print.h>
  51.  
  52. #define GET_CHECK_PHONE(phone,phoneid,err) { \
  53.       if (phoneid > IPC_MAX_PHONES) { err; } \
  54.       phone = &TASK->phones[phoneid]; \
  55. }
  56.  
  57. #define STRUCT_TO_USPACE(dst,src) copy_to_uspace(dst,src,sizeof(*(src)))
  58.  
  59. /** Return true if the method is a system method */
  60. static inline int is_system_method(unative_t method)
  61. {
  62.     if (method <= IPC_M_LAST_SYSTEM)
  63.         return 1;
  64.     return 0;
  65. }
  66.  
  67. /** Return true if the message with this method is forwardable
  68.  *
  69.  * - some system messages may be forwarded, for some of them
  70.  *   it is useless
  71.  */
  72. static inline int is_forwardable(unative_t method)
  73. {
  74.     if (method == IPC_M_PHONE_HUNGUP || method == IPC_M_AS_AREA_SEND \
  75.         || method == IPC_M_AS_AREA_RECV)
  76.         return 0; /* This message is meant only for the receiver */
  77.     return 1;
  78. }
  79.  
  80. /****************************************************/
  81. /* Functions that preprocess answer before sending
  82.  * it to the recepient
  83.  */
  84.  
  85. /** Return true if the caller (ipc_answer) should save
  86.  * the old call contents for answer_preprocess
  87.  */
  88. static inline int answer_need_old(call_t *call)
  89. {
  90.     if (IPC_GET_METHOD(call->data) == IPC_M_CONNECT_TO_ME)
  91.         return 1;
  92.     if (IPC_GET_METHOD(call->data) == IPC_M_CONNECT_ME_TO)
  93.         return 1;
  94.     if (IPC_GET_METHOD(call->data) == IPC_M_AS_AREA_SEND)
  95.         return 1;
  96.     if (IPC_GET_METHOD(call->data) == IPC_M_AS_AREA_RECV)
  97.         return 1;
  98.     return 0;
  99. }
  100.  
  101. /** Interpret process answer as control information
  102.  *
  103.  * This function is called directly after sys_ipc_answer
  104.  */
  105. static inline int answer_preprocess(call_t *answer, ipc_data_t *olddata)
  106. {
  107.     int phoneid;
  108.  
  109.     if (IPC_GET_RETVAL(answer->data) == EHANGUP) {
  110.         /* In case of forward, hangup the forwared phone,
  111.          * not the originator
  112.          */
  113.         spinlock_lock(&answer->data.phone->lock);
  114.         spinlock_lock(&TASK->answerbox.lock);
  115.         if (answer->data.phone->state == IPC_PHONE_CONNECTED) {
  116.             list_remove(&answer->data.phone->link);
  117.             answer->data.phone->state = IPC_PHONE_SLAMMED;
  118.         }
  119.         spinlock_unlock(&TASK->answerbox.lock);
  120.         spinlock_unlock(&answer->data.phone->lock);
  121.     }
  122.  
  123.     if (!olddata)
  124.         return 0;
  125.  
  126.     if (IPC_GET_METHOD(*olddata) == IPC_M_CONNECT_TO_ME) {
  127.         phoneid = IPC_GET_ARG3(*olddata);
  128.         if (IPC_GET_RETVAL(answer->data)) {
  129.             /* The connection was not accepted */
  130.             phone_dealloc(phoneid);
  131.         } else {
  132.             /* The connection was accepted */
  133.             phone_connect(phoneid,&answer->sender->answerbox);
  134.             /* Set 'phone identification' as arg3 of response */
  135.             IPC_SET_ARG3(answer->data, (unative_t)&TASK->phones[phoneid]);
  136.         }
  137.     } else if (IPC_GET_METHOD(*olddata) == IPC_M_CONNECT_ME_TO) {
  138.         /* If the users accepted call, connect */
  139.         if (!IPC_GET_RETVAL(answer->data)) {
  140.             ipc_phone_connect((phone_t *)IPC_GET_ARG3(*olddata),
  141.                       &TASK->answerbox);
  142.         }
  143.     } else if (IPC_GET_METHOD(*olddata) == IPC_M_AS_AREA_SEND) {
  144.         if (!IPC_GET_RETVAL(answer->data)) { /* Accepted, handle as_area receipt */
  145.             ipl_t ipl;
  146.             int rc;
  147.             as_t *as;
  148.            
  149.             ipl = interrupts_disable();
  150.             spinlock_lock(&answer->sender->lock);
  151.             as = answer->sender->as;
  152.             spinlock_unlock(&answer->sender->lock);
  153.             interrupts_restore(ipl);
  154.            
  155.             rc = as_area_share(as, IPC_GET_ARG1(*olddata), IPC_GET_ARG2(*olddata),
  156.                        AS, IPC_GET_ARG1(answer->data), IPC_GET_ARG3(*olddata));
  157.             IPC_SET_RETVAL(answer->data, rc);
  158.             return rc;
  159.         }
  160.     } else if (IPC_GET_METHOD(*olddata) == IPC_M_AS_AREA_RECV) {
  161.         if (!IPC_GET_RETVAL(answer->data)) {
  162.             ipl_t ipl;
  163.             as_t *as;
  164.             int rc;
  165.            
  166.             ipl = interrupts_disable();
  167.             spinlock_lock(&answer->sender->lock);
  168.             as = answer->sender->as;
  169.             spinlock_unlock(&answer->sender->lock);
  170.             interrupts_restore(ipl);
  171.            
  172.             rc = as_area_share(AS, IPC_GET_ARG1(answer->data), IPC_GET_ARG2(*olddata),
  173.                        as, IPC_GET_ARG1(*olddata), IPC_GET_ARG2(answer->data));
  174.             IPC_SET_RETVAL(answer->data, rc);
  175.         }
  176.     }
  177.     return 0;
  178. }
  179.  
  180. /** Called before the request is sent
  181.  *
  182.  * @return 0 - no error, -1 - report error to user
  183.  */
  184. static int request_preprocess(call_t *call)
  185. {
  186.     int newphid;
  187.     size_t size;
  188.  
  189.     switch (IPC_GET_METHOD(call->data)) {
  190.     case IPC_M_CONNECT_ME_TO:
  191.         newphid = phone_alloc();
  192.         if (newphid < 0)
  193.             return ELIMIT;
  194.         /* Set arg3 for server */
  195.         IPC_SET_ARG3(call->data, (unative_t)&TASK->phones[newphid]);
  196.         call->flags |= IPC_CALL_CONN_ME_TO;
  197.         call->private = newphid;
  198.         break;
  199.     case IPC_M_AS_AREA_SEND:
  200.         size = as_get_size(IPC_GET_ARG1(call->data));
  201.         if (!size) {
  202.             return EPERM;
  203.         }
  204.         IPC_SET_ARG2(call->data, size);
  205.         break;
  206.     default:
  207.         break;
  208.     }
  209.     return 0;
  210. }
  211.  
  212. /****************************************************/
  213. /* Functions called to process received call/answer
  214.  * before passing to uspace
  215.  */
  216.  
  217. /** Do basic kernel processing of received call answer */
  218. static void process_answer(call_t *call)
  219. {
  220.     if (IPC_GET_RETVAL(call->data) == EHANGUP && \
  221.         call->flags & IPC_CALL_FORWARDED)
  222.         IPC_SET_RETVAL(call->data, EFORWARD);
  223.  
  224.     if (call->flags & IPC_CALL_CONN_ME_TO) {
  225.         if (IPC_GET_RETVAL(call->data))
  226.             phone_dealloc(call->private);
  227.         else
  228.             IPC_SET_ARG3(call->data, call->private);
  229.     }
  230. }
  231.  
  232. /** Do basic kernel processing of received call request
  233.  *
  234.  * @return 0 - the call should be passed to userspace, 1 - ignore call
  235.  */
  236. static int process_request(answerbox_t *box,call_t *call)
  237. {
  238.     int phoneid;
  239.  
  240.     if (IPC_GET_METHOD(call->data) == IPC_M_CONNECT_TO_ME) {
  241.         phoneid = phone_alloc();
  242.         if (phoneid < 0) { /* Failed to allocate phone */
  243.             IPC_SET_RETVAL(call->data, ELIMIT);
  244.             ipc_answer(box,call);
  245.             return -1;
  246.         }
  247.         IPC_SET_ARG3(call->data, phoneid);
  248.     }
  249.     return 0;
  250. }
  251.  
  252. /** Send a call over IPC, wait for reply, return to user
  253.  *
  254.  * @return Call identification, returns -1 on fatal error,
  255.            -2 on 'Too many async request, handle answers first
  256.  */
  257. unative_t sys_ipc_call_sync_fast(unative_t phoneid, unative_t method,
  258.                 unative_t arg1, ipc_data_t *data)
  259. {
  260.     call_t call;
  261.     phone_t *phone;
  262.     int res;
  263.  
  264.     GET_CHECK_PHONE(phone, phoneid, return ENOENT);
  265.  
  266.     ipc_call_static_init(&call);
  267.     IPC_SET_METHOD(call.data, method);
  268.     IPC_SET_ARG1(call.data, arg1);
  269.  
  270.     if (!(res=request_preprocess(&call))) {
  271.         ipc_call_sync(phone, &call);
  272.         process_answer(&call);
  273.     } else
  274.         IPC_SET_RETVAL(call.data, res);
  275.     STRUCT_TO_USPACE(&data->args, &call.data.args);
  276.  
  277.     return 0;
  278. }
  279.  
  280. /** Synchronous IPC call allowing to send whole message */
  281. unative_t sys_ipc_call_sync(unative_t phoneid, ipc_data_t *question,
  282.                ipc_data_t *reply)
  283. {
  284.     call_t call;
  285.     phone_t *phone;
  286.     int res;
  287.     int rc;
  288.  
  289.     ipc_call_static_init(&call);
  290.     rc = copy_from_uspace(&call.data.args, &question->args, sizeof(call.data.args));
  291.     if (rc != 0)
  292.         return (unative_t) rc;
  293.  
  294.     GET_CHECK_PHONE(phone, phoneid, return ENOENT);
  295.  
  296.     if (!(res=request_preprocess(&call))) {
  297.         ipc_call_sync(phone, &call);
  298.         process_answer(&call);
  299.     } else
  300.         IPC_SET_RETVAL(call.data, res);
  301.  
  302.     rc = STRUCT_TO_USPACE(&reply->args, &call.data.args);
  303.     if (rc != 0)
  304.         return rc;
  305.  
  306.     return 0;
  307. }
  308.  
  309. /** Check that the task did not exceed allowed limit
  310.  *
  311.  * @return 0 - Limit OK,   -1 - limit exceeded
  312.  */
  313. static int check_call_limit(void)
  314. {
  315.     if (atomic_preinc(&TASK->active_calls) > IPC_MAX_ASYNC_CALLS) {
  316.         atomic_dec(&TASK->active_calls);
  317.         return -1;
  318.     }
  319.     return 0;
  320. }
  321.  
  322. /** Send an asynchronous call over ipc
  323.  *
  324.  * @return Call identification, returns -1 on fatal error,
  325.            -2 on 'Too many async request, handle answers first
  326.  */
  327. unative_t sys_ipc_call_async_fast(unative_t phoneid, unative_t method,
  328.                  unative_t arg1, unative_t arg2)
  329. {
  330.     call_t *call;
  331.     phone_t *phone;
  332.     int res;
  333.  
  334.     if (check_call_limit())
  335.         return IPC_CALLRET_TEMPORARY;
  336.  
  337.     GET_CHECK_PHONE(phone, phoneid, return IPC_CALLRET_FATAL);
  338.  
  339.     call = ipc_call_alloc(0);
  340.     IPC_SET_METHOD(call->data, method);
  341.     IPC_SET_ARG1(call->data, arg1);
  342.     IPC_SET_ARG2(call->data, arg2);
  343.     IPC_SET_ARG3(call->data, 0);
  344.  
  345.     if (!(res=request_preprocess(call)))
  346.         ipc_call(phone, call);
  347.     else
  348.         ipc_backsend_err(phone, call, res);
  349.  
  350.     return (unative_t) call;
  351. }
  352.  
  353. /** Synchronous IPC call allowing to send whole message
  354.  *
  355.  * @return The same as sys_ipc_call_async
  356.  */
  357. unative_t sys_ipc_call_async(unative_t phoneid, ipc_data_t *data)
  358. {
  359.     call_t *call;
  360.     phone_t *phone;
  361.     int res;
  362.     int rc;
  363.  
  364.     if (check_call_limit())
  365.         return IPC_CALLRET_TEMPORARY;
  366.  
  367.     GET_CHECK_PHONE(phone, phoneid, return IPC_CALLRET_FATAL);
  368.  
  369.     call = ipc_call_alloc(0);
  370.     rc = copy_from_uspace(&call->data.args, &data->args, sizeof(call->data.args));
  371.     if (rc != 0) {
  372.         ipc_call_free(call);
  373.         return (unative_t) rc;
  374.     }
  375.     if (!(res=request_preprocess(call)))
  376.         ipc_call(phone, call);
  377.     else
  378.         ipc_backsend_err(phone, call, res);
  379.  
  380.     return (unative_t) call;
  381. }
  382.  
  383. /** Forward received call to another destination
  384.  *
  385.  * The arg1 and arg2 are changed in the forwarded message
  386.  *
  387.  * Warning: If implementing non-fast version, make sure that
  388.  *          arg3 is not rewritten for certain system IPC
  389.  */
  390. unative_t sys_ipc_forward_fast(unative_t callid, unative_t phoneid,
  391.                   unative_t method, unative_t arg1)
  392. {
  393.     call_t *call;
  394.     phone_t *phone;
  395.  
  396.     call = get_call(callid);
  397.     if (!call)
  398.         return ENOENT;
  399.  
  400.     call->flags |= IPC_CALL_FORWARDED;
  401.  
  402.     GET_CHECK_PHONE(phone, phoneid, {
  403.         IPC_SET_RETVAL(call->data, EFORWARD);
  404.         ipc_answer(&TASK->answerbox, call);
  405.         return ENOENT;
  406.     });    
  407.  
  408.     if (!is_forwardable(IPC_GET_METHOD(call->data))) {
  409.         IPC_SET_RETVAL(call->data, EFORWARD);
  410.         ipc_answer(&TASK->answerbox, call);
  411.         return EPERM;
  412.     }
  413.  
  414.     /* Userspace is not allowed to change method of system methods
  415.      * on forward, allow changing ARG1 and ARG2 by means of method and arg1
  416.      */
  417.     if (is_system_method(IPC_GET_METHOD(call->data))) {
  418.         if (IPC_GET_METHOD(call->data) == IPC_M_CONNECT_TO_ME)
  419.             phone_dealloc(IPC_GET_ARG3(call->data));
  420.  
  421.         IPC_SET_ARG1(call->data, method);
  422.         IPC_SET_ARG2(call->data, arg1);
  423.     } else {
  424.         IPC_SET_METHOD(call->data, method);
  425.         IPC_SET_ARG1(call->data, arg1);
  426.     }
  427.  
  428.     return ipc_forward(call, phone, &TASK->answerbox);
  429. }
  430.  
  431. /** Send IPC answer */
  432. unative_t sys_ipc_answer_fast(unative_t callid, unative_t retval,
  433.                  unative_t arg1, unative_t arg2)
  434. {
  435.     call_t *call;
  436.     ipc_data_t saved_data;
  437.     int saveddata = 0;
  438.     int rc;
  439.  
  440.     /* Do not answer notification callids */
  441.     if (callid & IPC_CALLID_NOTIFICATION)
  442.         return 0;
  443.  
  444.     call = get_call(callid);
  445.     if (!call)
  446.         return ENOENT;
  447.  
  448.     if (answer_need_old(call)) {
  449.         memcpy(&saved_data, &call->data, sizeof(call->data));
  450.         saveddata = 1;
  451.     }
  452.  
  453.     IPC_SET_RETVAL(call->data, retval);
  454.     IPC_SET_ARG1(call->data, arg1);
  455.     IPC_SET_ARG2(call->data, arg2);
  456.     rc = answer_preprocess(call, saveddata ? &saved_data : NULL);
  457.  
  458.     ipc_answer(&TASK->answerbox, call);
  459.     return rc;
  460. }
  461.  
  462. /** Send IPC answer */
  463. unative_t sys_ipc_answer(unative_t callid, ipc_data_t *data)
  464. {
  465.     call_t *call;
  466.     ipc_data_t saved_data;
  467.     int saveddata = 0;
  468.     int rc;
  469.  
  470.     /* Do not answer notification callids */
  471.     if (callid & IPC_CALLID_NOTIFICATION)
  472.         return 0;
  473.  
  474.     call = get_call(callid);
  475.     if (!call)
  476.         return ENOENT;
  477.  
  478.     if (answer_need_old(call)) {
  479.         memcpy(&saved_data, &call->data, sizeof(call->data));
  480.         saveddata = 1;
  481.     }
  482.     rc = copy_from_uspace(&call->data.args, &data->args,
  483.              sizeof(call->data.args));
  484.     if (rc != 0)
  485.         return rc;
  486.  
  487.     rc = answer_preprocess(call, saveddata ? &saved_data : NULL);
  488.    
  489.     ipc_answer(&TASK->answerbox, call);
  490.  
  491.     return rc;
  492. }
  493.  
  494. /** Hang up the phone
  495.  *
  496.  */
  497. unative_t sys_ipc_hangup(int phoneid)
  498. {
  499.     phone_t *phone;
  500.  
  501.     GET_CHECK_PHONE(phone, phoneid, return ENOENT);
  502.  
  503.     if (ipc_phone_hangup(phone))
  504.         return -1;
  505.  
  506.     return 0;
  507. }
  508.  
  509. /** Wait for incoming ipc call or answer
  510.  *
  511.  * @param calldata Pointer to buffer where the call/answer data is stored
  512.  * @param usec Timeout. See waitq_sleep_timeout() for explanation.
  513.  * @param flags Select mode of sleep operation. See waitq_sleep_timeout() for explanation.
  514.  *
  515.  * @return Callid, if callid & 1, then the call is answer
  516.  */
  517. unative_t sys_ipc_wait_for_call(ipc_data_t *calldata, uint32_t usec, int flags)
  518. {
  519.     call_t *call;
  520.  
  521. restart:   
  522.     call = ipc_wait_for_call(&TASK->answerbox, usec, flags | SYNCH_FLAGS_INTERRUPTIBLE);
  523.     if (!call)
  524.         return 0;
  525.  
  526.     if (call->flags & IPC_CALL_NOTIF) {
  527.         ASSERT(! (call->flags & IPC_CALL_STATIC_ALLOC));
  528.  
  529.         /* Set in_phone_hash to the interrupt counter */
  530.         call->data.phone = (void *)call->private;
  531.        
  532.         STRUCT_TO_USPACE(calldata, &call->data);
  533.  
  534.         ipc_call_free(call);
  535.        
  536.         return ((unative_t)call) | IPC_CALLID_NOTIFICATION;
  537.     }
  538.  
  539.     if (call->flags & IPC_CALL_ANSWERED) {
  540.         process_answer(call);
  541.  
  542.         ASSERT(! (call->flags & IPC_CALL_STATIC_ALLOC));
  543.  
  544.         atomic_dec(&TASK->active_calls);
  545.  
  546.         if (call->flags & IPC_CALL_DISCARD_ANSWER) {
  547.             ipc_call_free(call);
  548.             goto restart;
  549.         }
  550.  
  551.         STRUCT_TO_USPACE(&calldata->args, &call->data.args);
  552.         ipc_call_free(call);
  553.  
  554.         return ((unative_t)call) | IPC_CALLID_ANSWERED;
  555.     }
  556.  
  557.     if (process_request(&TASK->answerbox, call))
  558.         goto restart;
  559.  
  560.     /* Include phone address('id') of the caller in the request,
  561.      * copy whole call->data, not only call->data.args */
  562.     if (STRUCT_TO_USPACE(calldata, &call->data)) {
  563.         return 0;
  564.     }
  565.     return (unative_t)call;
  566. }
  567.  
  568. /** Connect irq handler to task.
  569.  *
  570.  * @param inr IRQ number.
  571.  * @param devno Device number.
  572.  * @param method Method to be associated with the notification.
  573.  * @param ucode Uspace pointer to the top-half pseudocode.
  574.  *
  575.  * @return EPERM or a return code returned by ipc_irq_register().
  576.  */
  577. unative_t sys_ipc_register_irq(inr_t inr, devno_t devno, unative_t method, irq_code_t *ucode)
  578. {
  579.     if (!(cap_get(TASK) & CAP_IRQ_REG))
  580.         return EPERM;
  581.  
  582.     return ipc_irq_register(&TASK->answerbox, inr, devno, method, ucode);
  583. }
  584.  
  585. /** Disconnect irq handler from task.
  586.  *
  587.  * @param inr IRQ number.
  588.  * @param devno Device number.
  589.  */
  590. unative_t sys_ipc_unregister_irq(inr_t inr, devno_t devno)
  591. {
  592.     if (!(cap_get(TASK) & CAP_IRQ_REG))
  593.         return EPERM;
  594.  
  595.     ipc_irq_unregister(&TASK->answerbox, inr, devno);
  596.  
  597.     return 0;
  598. }
  599.  
  600. /** @}
  601.  */
  602.