Subversion Repositories HelenOS-historic

Rev

Rev 1088 | Rev 1141 | 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. #include <arch.h>
  30. #include <proc/task.h>
  31.  
  32. #include <errno.h>
  33. #include <mm/page.h>
  34. #include <memstr.h>
  35. #include <debug.h>
  36. #include <ipc/ipc.h>
  37. #include <ipc/sysipc.h>
  38. #include <ipc/ipcrsc.h>
  39.  
  40.  
  41. #include <print.h>
  42. #include <arch.h>
  43. #include <proc/thread.h>
  44.  
  45. #define GET_CHECK_PHONE(phone,phoneid,err) { \
  46.       if (phoneid > IPC_MAX_PHONES) { err; } \
  47.       phone = &TASK->phones[phoneid]; \
  48. }
  49.  
  50. #define STRUCT_TO_USPACE(dst,src) copy_to_uspace(dst,src,sizeof(*(src)))
  51.  
  52. /** Return true if the method is a system method */
  53. static inline int is_system_method(__native method)
  54. {
  55.     if (method <= IPC_M_LAST_SYSTEM)
  56.         return 1;
  57.     return 0;
  58. }
  59.  
  60. /** Return true if the message with this method is forwardable
  61.  *
  62.  * - some system messages may be forwarded, for some of them
  63.  *   it is useless
  64.  */
  65. static inline int is_forwardable(__native method)
  66. {
  67.     if (method == IPC_M_PHONE_HUNGUP)
  68.         return 0; /* This message is meant only for the receiver */
  69.     return 1;
  70. }
  71.  
  72. /****************************************************/
  73. /* Functions that preprocess answer before sending
  74.  * it to the recepient
  75.  */
  76.  
  77. /** Return true if the caller (ipc_answer) should save
  78.  * the old call contents for answer_preprocess
  79.  */
  80. static inline int answer_need_old(call_t *call)
  81. {
  82.     if (IPC_GET_METHOD(call->data) == IPC_M_CONNECT_TO_ME)
  83.         return 1;
  84.     if (IPC_GET_METHOD(call->data) == IPC_M_CONNECT_ME_TO)
  85.         return 1;
  86.     return 0;
  87. }
  88.  
  89. /** Interpret process answer as control information */
  90. static inline void answer_preprocess(call_t *answer, ipc_data_t *olddata)
  91. {
  92.     int phoneid;
  93.  
  94.     if (IPC_GET_RETVAL(answer->data) == EHANGUP) {
  95.         /* Atomic operation */
  96.         answer->data.phone->callee = NULL;
  97.     }
  98.  
  99.     if (!olddata)
  100.         return;
  101.  
  102.     if (IPC_GET_METHOD(*olddata) == IPC_M_CONNECT_TO_ME) {
  103.         phoneid = IPC_GET_ARG3(*olddata);
  104.         if (IPC_GET_RETVAL(answer->data)) {
  105.             /* The connection was not accepted */
  106.             phone_dealloc(phoneid);
  107.         } else {
  108.             /* The connection was accepted */
  109.             phone_connect(phoneid,&answer->sender->answerbox);
  110.             /* Set 'phone identification' as arg3 of response */
  111.             IPC_SET_ARG3(answer->data, (__native)&TASK->phones[phoneid]);
  112.         }
  113.     } else if (IPC_GET_METHOD(*olddata) == IPC_M_CONNECT_ME_TO) {
  114.         /* If the users accepted call, connect */
  115.         if (!IPC_GET_RETVAL(answer->data)) {
  116.             ipc_phone_connect((phone_t *)IPC_GET_ARG3(*olddata),
  117.                       &TASK->answerbox);
  118.         }
  119.     }
  120. }
  121.  
  122. /** Called before the request is sent
  123.  *
  124.  * @return 0 - no error, -1 - report error to user
  125.  */
  126. static int request_preprocess(call_t *call)
  127. {
  128.     int newphid;
  129.  
  130.     switch (IPC_GET_METHOD(call->data)) {
  131.     case IPC_M_CONNECT_ME_TO:
  132.         newphid = phone_alloc();
  133.         if (newphid < 0)
  134.             return ELIMIT;
  135.         /* Set arg3 for server */
  136.         IPC_SET_ARG3(call->data, (__native)&TASK->phones[newphid]);
  137.         call->flags |= IPC_CALL_CONN_ME_TO;
  138.         call->private = newphid;
  139.         break;
  140.     default:
  141.         break;
  142.     }
  143.     return 0;
  144. }
  145.  
  146. /****************************************************/
  147. /* Functions called to process received call/answer
  148.  * before passing to uspace
  149.  */
  150.  
  151. /** Do basic kernel processing of received call answer */
  152. static void process_answer(call_t *call)
  153. {
  154.     if (IPC_GET_RETVAL(call->data) == EHANGUP && \
  155.         call->flags & IPC_CALL_FORWARDED)
  156.         IPC_SET_RETVAL(call->data, EFORWARD);
  157.  
  158.     if (call->flags & IPC_CALL_CONN_ME_TO) {
  159.         if (IPC_GET_RETVAL(call->data))
  160.             phone_dealloc(call->private);
  161.         else
  162.             IPC_SET_ARG3(call->data, call->private);
  163.     }
  164. }
  165.  
  166. /** Do basic kernel processing of received call request
  167.  *
  168.  * @return 0 - the call should be passed to userspace, 1 - ignore call
  169.  */
  170. static int process_request(answerbox_t *box,call_t *call)
  171. {
  172.     int phoneid;
  173.  
  174.     if (IPC_GET_METHOD(call->data) == IPC_M_CONNECT_TO_ME) {
  175.         phoneid = phone_alloc();
  176.         if (phoneid < 0) { /* Failed to allocate phone */
  177.             IPC_SET_RETVAL(call->data, ELIMIT);
  178.             ipc_answer(box,call);
  179.             return -1;
  180.         }
  181.         IPC_SET_ARG3(call->data, phoneid);
  182.     }
  183.     return 0;
  184. }
  185.  
  186. /** Send a call over IPC, wait for reply, return to user
  187.  *
  188.  * @return Call identification, returns -1 on fatal error,
  189.            -2 on 'Too many async request, handle answers first
  190.  */
  191. __native sys_ipc_call_sync_fast(__native phoneid, __native method,
  192.                 __native arg1, ipc_data_t *data)
  193. {
  194.     call_t call;
  195.     phone_t *phone;
  196.     int res;
  197.  
  198.     GET_CHECK_PHONE(phone, phoneid, return ENOENT);
  199.  
  200.     ipc_call_static_init(&call);
  201.     IPC_SET_METHOD(call.data, method);
  202.     IPC_SET_ARG1(call.data, arg1);
  203.  
  204.     if (!(res=request_preprocess(&call))) {
  205.         ipc_call_sync(phone, &call);
  206.         process_answer(&call);
  207.     } else
  208.         IPC_SET_RETVAL(call.data, res);
  209.     STRUCT_TO_USPACE(&data->args, &call.data.args);
  210.  
  211.     return 0;
  212. }
  213.  
  214. /** Synchronous IPC call allowing to send whole message */
  215. __native sys_ipc_call_sync(__native phoneid, ipc_data_t *question,
  216.                ipc_data_t *reply)
  217. {
  218.     call_t call;
  219.     phone_t *phone;
  220.     int res;
  221.  
  222.     ipc_call_static_init(&call);
  223.     copy_from_uspace(&call.data.args, &question->args, sizeof(call.data.args));
  224.  
  225.     GET_CHECK_PHONE(phone, phoneid, return ENOENT);
  226.  
  227.     if (!(res=request_preprocess(&call))) {
  228.         ipc_call_sync(phone, &call);
  229.         process_answer(&call);
  230.     } else
  231.         IPC_SET_RETVAL(call.data, res);
  232.  
  233.     STRUCT_TO_USPACE(&reply->args, &call.data.args);
  234.  
  235.     return 0;
  236. }
  237.  
  238. /** Check that the task did not exceed allowed limit
  239.  *
  240.  * @return 0 - Limit OK,   -1 - limit exceeded
  241.  */
  242. static int check_call_limit(void)
  243. {
  244.     if (atomic_preinc(&TASK->active_calls) > IPC_MAX_ASYNC_CALLS) {
  245.         atomic_dec(&TASK->active_calls);
  246.         return -1;
  247.     }
  248.     return 0;
  249. }
  250.  
  251. /** Send an asynchronous call over ipc
  252.  *
  253.  * @return Call identification, returns -1 on fatal error,
  254.            -2 on 'Too many async request, handle answers first
  255.  */
  256. __native sys_ipc_call_async_fast(__native phoneid, __native method,
  257.                  __native arg1, __native arg2)
  258. {
  259.     call_t *call;
  260.     phone_t *phone;
  261.     int res;
  262.  
  263.     if (check_call_limit())
  264.         return IPC_CALLRET_TEMPORARY;
  265.  
  266.     GET_CHECK_PHONE(phone, phoneid, return IPC_CALLRET_FATAL);
  267.  
  268.     call = ipc_call_alloc();
  269.     IPC_SET_METHOD(call->data, method);
  270.     IPC_SET_ARG1(call->data, arg1);
  271.     IPC_SET_ARG2(call->data, arg2);
  272.  
  273.     if (!(res=request_preprocess(call)))
  274.         ipc_call(phone, call);
  275.     else
  276.         ipc_backsend_err(phone, call, res);
  277.  
  278.     return (__native) call;
  279. }
  280.  
  281. /** Synchronous IPC call allowing to send whole message
  282.  *
  283.  * @return The same as sys_ipc_call_async
  284.  */
  285. __native sys_ipc_call_async(__native phoneid, ipc_data_t *data)
  286. {
  287.     call_t *call;
  288.     phone_t *phone;
  289.     int res;
  290.  
  291.     if (check_call_limit())
  292.         return IPC_CALLRET_TEMPORARY;
  293.  
  294.     GET_CHECK_PHONE(phone, phoneid, return IPC_CALLRET_FATAL);
  295.  
  296.     call = ipc_call_alloc();
  297.     copy_from_uspace(&call->data.args, &data->args, sizeof(call->data.args));
  298.     if (!(res=request_preprocess(call)))
  299.         ipc_call(phone, call);
  300.     else
  301.         ipc_backsend_err(phone, call, res);
  302.  
  303.     return (__native) call;
  304. }
  305.  
  306. /** Forward received call to another destination
  307.  *
  308.  * The arg1 and arg2 are changed in the forwarded message
  309.  *
  310.  * Warning: If implementing non-fast version, make sure that
  311.  *          arg3 is not rewritten for certain system IPC
  312.  */
  313. __native sys_ipc_forward_fast(__native callid, __native phoneid,
  314.                   __native method, __native arg1)
  315. {
  316.     call_t *call;
  317.     phone_t *phone;
  318.  
  319.     call = get_call(callid);
  320.     if (!call)
  321.         return ENOENT;
  322.  
  323.     call->flags |= IPC_CALL_FORWARDED;
  324.  
  325.     GET_CHECK_PHONE(phone, phoneid, {
  326.         IPC_SET_RETVAL(call->data, EFORWARD);
  327.         ipc_answer(&TASK->answerbox, call);
  328.         return ENOENT;
  329.     });    
  330.  
  331.     if (!is_forwardable(IPC_GET_METHOD(call->data))) {
  332.         IPC_SET_RETVAL(call->data, EFORWARD);
  333.         ipc_answer(&TASK->answerbox, call);
  334.         return EPERM;
  335.     }
  336.  
  337.     /* Userspace is not allowed to change method of system methods
  338.      * on forward, allow changing ARG1 and ARG2 by means of method and arg1
  339.      */
  340.     if (is_system_method(IPC_GET_METHOD(call->data))) {
  341.         if (IPC_GET_METHOD(call->data) == IPC_M_CONNECT_TO_ME)
  342.             phone_dealloc(IPC_GET_ARG3(call->data));
  343.  
  344.         IPC_SET_ARG1(call->data, method);
  345.         IPC_SET_ARG2(call->data, arg1);
  346.     } else {
  347.         IPC_SET_METHOD(call->data, method);
  348.         IPC_SET_ARG1(call->data, arg1);
  349.     }
  350.  
  351.     return ipc_forward(call, phone, &TASK->answerbox);
  352. }
  353.  
  354. /** Send IPC answer */
  355. __native sys_ipc_answer_fast(__native callid, __native retval,
  356.                  __native arg1, __native arg2)
  357. {
  358.     call_t *call;
  359.     ipc_data_t saved_data;
  360.     int saveddata = 0;
  361.  
  362.     call = get_call(callid);
  363.     if (!call)
  364.         return ENOENT;
  365.  
  366.     if (answer_need_old(call)) {
  367.         memcpy(&saved_data, &call->data, sizeof(call->data));
  368.         saveddata = 1;
  369.     }
  370.  
  371.     IPC_SET_RETVAL(call->data, retval);
  372.     IPC_SET_ARG1(call->data, arg1);
  373.     IPC_SET_ARG2(call->data, arg2);
  374.     answer_preprocess(call, saveddata ? &saved_data : NULL);
  375.  
  376.     ipc_answer(&TASK->answerbox, call);
  377.     return 0;
  378. }
  379.  
  380. /** Send IPC answer */
  381. __native sys_ipc_answer(__native callid, ipc_data_t *data)
  382. {
  383.     call_t *call;
  384.     ipc_data_t saved_data;
  385.     int saveddata = 0;
  386.  
  387.     call = get_call(callid);
  388.     if (!call)
  389.         return ENOENT;
  390.  
  391.     if (answer_need_old(call)) {
  392.         memcpy(&saved_data, &call->data, sizeof(call->data));
  393.         saveddata = 1;
  394.     }
  395.     copy_from_uspace(&call->data.args, &data->args,
  396.              sizeof(call->data.args));
  397.  
  398.     answer_preprocess(call, saveddata ? &saved_data : NULL);
  399.    
  400.     ipc_answer(&TASK->answerbox, call);
  401.  
  402.     return 0;
  403. }
  404.  
  405. /** Hang up the phone
  406.  *
  407.  */
  408. __native sys_ipc_hangup(int phoneid)
  409. {
  410.     phone_t *phone;
  411.  
  412.     GET_CHECK_PHONE(phone, phoneid, return ENOENT);
  413.  
  414.     if (ipc_phone_hangup(phone))
  415.         return -1;
  416.  
  417.     return 0;
  418. }
  419.  
  420. /** Wait for incoming ipc call or answer
  421.  *
  422.  * @param calldata Pointer to buffer where the call/answer data is stored
  423.  * @param taskid On 'CONNECT_ME_TO' call it is filled with 'taskid' of
  424.  *               the caller.
  425.  * @param flags
  426.  * @return Callid, if callid & 1, then the call is answer
  427.  */
  428. __native sys_ipc_wait_for_call(ipc_data_t *calldata, __native flags)
  429. {
  430.     call_t *call;
  431.  
  432. restart:   
  433.     call = ipc_wait_for_call(&TASK->answerbox, flags);
  434.     if (!call)
  435.         return 0;
  436.  
  437.     if (call->flags & IPC_CALL_ANSWERED) {
  438.         process_answer(call);
  439.  
  440.         ASSERT(! (call->flags & IPC_CALL_STATIC_ALLOC));
  441.  
  442.         atomic_dec(&TASK->active_calls);
  443.  
  444.         if (call->flags & IPC_CALL_DISCARD_ANSWER) {
  445.             ipc_call_free(call);
  446.             goto restart;
  447.         }
  448.  
  449.         STRUCT_TO_USPACE(&calldata->args, &call->data.args);
  450.         ipc_call_free(call);
  451.  
  452.         return ((__native)call) | IPC_CALLID_ANSWERED;
  453.     }
  454.  
  455.     if (process_request(&TASK->answerbox, call))
  456.         goto restart;
  457.  
  458.     /* Include phone address('id') of the caller in the request,
  459.      * copy whole call->data, not only call->data.args */
  460.     STRUCT_TO_USPACE(calldata, &call->data);
  461.     return (__native)call;
  462. }
  463.