Subversion Repositories HelenOS

Rev

Rev 1086 | Rev 1090 | 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.         }
  111.     } else if (IPC_GET_METHOD(*olddata) == IPC_M_CONNECT_ME_TO) {
  112.         /* If the users accepted call, connect */
  113.         if (!IPC_GET_RETVAL(answer->data)) {
  114.             ipc_phone_connect((phone_t *)IPC_GET_ARG3(*olddata),
  115.                       &TASK->answerbox);
  116.         }
  117.     }
  118. }
  119.  
  120. /****************************************************/
  121. /* Functions called to process received call/answer
  122.  * before passing to uspace
  123.  */
  124.  
  125. /** Do basic kernel processing of received call answer */
  126. static int process_answer(answerbox_t *box,call_t *call)
  127. {
  128.     if (IPC_GET_RETVAL(call->data) == EHANGUP && \
  129.         call->flags & IPC_CALL_FORWARDED)
  130.         IPC_SET_RETVAL(call->data, EFORWARD);
  131.     return 0;
  132. }
  133.  
  134. /** Do basic kernel processing of received call request
  135.  *
  136.  * @return 0 - the call should be passed to userspace, 1 - ignore call
  137.  */
  138. static int process_request(answerbox_t *box,call_t *call)
  139. {
  140.     int phoneid;
  141.  
  142.     if (IPC_GET_METHOD(call->data) == IPC_M_CONNECT_TO_ME) {
  143.         phoneid = phone_alloc();
  144.         if (phoneid < 0) { /* Failed to allocate phone */
  145.             IPC_SET_RETVAL(call->data, ELIMIT);
  146.             ipc_answer(box,call);
  147.             return -1;
  148.         }
  149.         IPC_SET_ARG3(call->data, phoneid);
  150.     }
  151.     return 0;
  152. }
  153.  
  154. /** Send a call over IPC, wait for reply, return to user
  155.  *
  156.  * @return Call identification, returns -1 on fatal error,
  157.            -2 on 'Too many async request, handle answers first
  158.  */
  159. __native sys_ipc_call_sync_fast(__native phoneid, __native method,
  160.                 __native arg1, ipc_data_t *data)
  161. {
  162.     call_t call;
  163.     phone_t *phone;
  164.  
  165.     if (is_system_method(method))
  166.         return EPERM;
  167.  
  168.     GET_CHECK_PHONE(phone, phoneid, return ENOENT);
  169.  
  170.     ipc_call_static_init(&call);
  171.     IPC_SET_METHOD(call.data, method);
  172.     IPC_SET_ARG1(call.data, arg1);
  173.    
  174.     ipc_call_sync(phone, &call);
  175.  
  176.     STRUCT_TO_USPACE(&data->args, &call.data.args);
  177.  
  178.     return 0;
  179. }
  180.  
  181. /** Synchronous IPC call allowing to send whole message */
  182. __native sys_ipc_call_sync(__native phoneid, ipc_data_t *question,
  183.                ipc_data_t *reply)
  184. {
  185.     call_t call;
  186.     phone_t *phone;
  187.  
  188.     ipc_call_static_init(&call);
  189.     copy_from_uspace(&call.data.args, &question->args, sizeof(call.data.args));
  190.  
  191.     if (is_system_method(IPC_GET_METHOD(call.data)))
  192.         return EPERM;
  193.    
  194.     GET_CHECK_PHONE(phone, phoneid, return ENOENT);
  195.  
  196.     ipc_call_sync(phone, &call);
  197.  
  198.     STRUCT_TO_USPACE(&reply->args, &call.data.args);
  199.  
  200.     return 0;
  201. }
  202.  
  203. /** Check that the task did not exceed allowed limit
  204.  *
  205.  * @return 0 - Limit OK,   -1 - limit exceeded
  206.  */
  207. static int check_call_limit(void)
  208. {
  209.     if (atomic_preinc(&TASK->active_calls) > IPC_MAX_ASYNC_CALLS) {
  210.         atomic_dec(&TASK->active_calls);
  211.         return -1;
  212.     }
  213.     return 0;
  214. }
  215.  
  216. /** Send an asynchronous call over ipc
  217.  *
  218.  * @return Call identification, returns -1 on fatal error,
  219.            -2 on 'Too many async request, handle answers first
  220.  */
  221. __native sys_ipc_call_async_fast(__native phoneid, __native method,
  222.                  __native arg1, __native arg2)
  223. {
  224.     call_t *call;
  225.     phone_t *phone;
  226.  
  227.     if (is_system_method(method))
  228.         return IPC_CALLRET_FATAL;
  229.  
  230.     if (check_call_limit())
  231.         return IPC_CALLRET_TEMPORARY;
  232.  
  233.     GET_CHECK_PHONE(phone, phoneid, return ENOENT);
  234.  
  235.     call = ipc_call_alloc();
  236.     IPC_SET_METHOD(call->data, method);
  237.     IPC_SET_ARG1(call->data, arg1);
  238.     IPC_SET_ARG2(call->data, arg2);
  239.  
  240.     ipc_call(phone, call);
  241.  
  242.     return (__native) call;
  243. }
  244.  
  245. /** Synchronous IPC call allowing to send whole message
  246.  *
  247.  * @return The same as sys_ipc_call_async
  248.  */
  249. __native sys_ipc_call_async(__native phoneid, ipc_data_t *data)
  250. {
  251.     call_t *call;
  252.     phone_t *phone;
  253.  
  254.     if (check_call_limit())
  255.         return IPC_CALLRET_TEMPORARY;
  256.  
  257.     GET_CHECK_PHONE(phone, phoneid, return ENOENT);
  258.  
  259.     call = ipc_call_alloc();
  260.     copy_from_uspace(&call->data.args, &data->args, sizeof(call->data.args));
  261.  
  262.     if (is_system_method(IPC_GET_METHOD(call->data))) {
  263.         ipc_call_free(call);
  264.         return EPERM;
  265.     }
  266.    
  267.     ipc_call(phone, call);
  268.  
  269.     return (__native) call;
  270. }
  271.  
  272. /** Forward received call to another destination
  273.  *
  274.  * The arg1 and arg2 are changed in the forwarded message
  275.  *
  276.  * Warning: If implementing non-fast version, make sure that
  277.  *          arg3 is not rewritten for certain system IPC
  278.  */
  279. __native sys_ipc_forward_fast(__native callid, __native phoneid,
  280.                   __native method, __native arg1)
  281. {
  282.     call_t *call;
  283.     phone_t *phone;
  284.  
  285.     call = get_call(callid);
  286.     if (!call)
  287.         return ENOENT;
  288.  
  289.     call->flags |= IPC_CALL_FORWARDED;
  290.  
  291.     GET_CHECK_PHONE(phone, phoneid, {
  292.         IPC_SET_RETVAL(call->data, EFORWARD);
  293.         ipc_answer(&TASK->answerbox, call);
  294.         return ENOENT;
  295.     });    
  296.  
  297.     if (!is_forwardable(IPC_GET_METHOD(call->data))) {
  298.         IPC_SET_RETVAL(call->data, EFORWARD);
  299.         ipc_answer(&TASK->answerbox, call);
  300.         return EPERM;
  301.     }
  302.  
  303.     /* Userspace is not allowed to change method of system methods
  304.      * on forward, allow changing ARG1 and ARG2 by means of method and arg1
  305.      */
  306.     if (is_system_method(IPC_GET_METHOD(call->data))) {
  307.         IPC_SET_ARG1(call->data, method);
  308.         IPC_SET_ARG2(call->data, arg1);
  309.     } else {
  310.         IPC_SET_METHOD(call->data, method);
  311.         IPC_SET_ARG1(call->data, arg1);
  312.     }
  313.  
  314.     return ipc_forward(call, phone, &TASK->answerbox);
  315. }
  316.  
  317. /** Send IPC answer */
  318. __native sys_ipc_answer_fast(__native callid, __native retval,
  319.                  __native arg1, __native arg2)
  320. {
  321.     call_t *call;
  322.     ipc_data_t saved_data;
  323.     int saveddata = 0;
  324.  
  325.     call = get_call(callid);
  326.     if (!call)
  327.         return ENOENT;
  328.  
  329.     if (answer_need_old(call)) {
  330.         memcpy(&saved_data, &call->data, sizeof(call->data));
  331.         saveddata = 1;
  332.     }
  333.  
  334.     IPC_SET_RETVAL(call->data, retval);
  335.     IPC_SET_ARG1(call->data, arg1);
  336.     IPC_SET_ARG2(call->data, arg2);
  337.     answer_preprocess(call, saveddata ? &saved_data : NULL);
  338.  
  339.     ipc_answer(&TASK->answerbox, call);
  340.     return 0;
  341. }
  342.  
  343. /** Send IPC answer */
  344. __native sys_ipc_answer(__native callid, ipc_data_t *data)
  345. {
  346.     call_t *call;
  347.     ipc_data_t saved_data;
  348.     int saveddata = 0;
  349.  
  350.     call = get_call(callid);
  351.     if (!call)
  352.         return ENOENT;
  353.  
  354.     if (answer_need_old(call)) {
  355.         memcpy(&saved_data, &call->data, sizeof(call->data));
  356.         saveddata = 1;
  357.     }
  358.     copy_from_uspace(&call->data.args, &data->args,
  359.              sizeof(call->data.args));
  360.  
  361.     answer_preprocess(call, saveddata ? &saved_data : NULL);
  362.    
  363.     ipc_answer(&TASK->answerbox, call);
  364.  
  365.     return 0;
  366. }
  367.  
  368. /** Ask the other side of connection to do 'callback' connection
  369.  *
  370.  * @return 0 if no error, error otherwise
  371.  */
  372. __native sys_ipc_connect_to_me(__native phoneid, __native arg1,
  373.                    __native arg2, task_id_t *taskid)
  374. {
  375.     call_t call;
  376.     phone_t *phone;
  377.  
  378.     ipc_call_static_init(&call);
  379.     IPC_SET_METHOD(call.data, IPC_M_CONNECT_TO_ME);
  380.     IPC_SET_ARG1(call.data, arg1);
  381.     IPC_SET_ARG2(call.data, arg2);
  382.    
  383.     GET_CHECK_PHONE(phone, phoneid, return ENOENT);
  384.  
  385.     ipc_call_sync(phone, &call);
  386.  
  387.     if (!IPC_GET_RETVAL(call.data) && taskid)
  388.         STRUCT_TO_USPACE(taskid, &phone->callee->task->taskid);
  389.  
  390.     return IPC_GET_RETVAL(call.data);
  391. }
  392.  
  393. /** Ask target process to connect me somewhere
  394.  *
  395.  * @return phoneid - on success, error otherwise
  396.  */
  397. __native sys_ipc_connect_me_to(__native phoneid, __native arg1,
  398.                    __native arg2)
  399. {
  400.     call_t call;
  401.     phone_t *phone;
  402.     int newphid;
  403.  
  404.     GET_CHECK_PHONE(phone, phoneid, return ENOENT);
  405.  
  406.     newphid = phone_alloc();
  407.     if (newphid < 0)
  408.         return ELIMIT;
  409.  
  410.     ipc_call_static_init(&call);
  411.     IPC_SET_METHOD(call.data, IPC_M_CONNECT_ME_TO);
  412.     IPC_SET_ARG1(call.data, arg1);
  413.     IPC_SET_ARG2(call.data, arg2);
  414.     IPC_SET_ARG3(call.data, (__native)&TASK->phones[newphid]);
  415.  
  416.     ipc_call_sync(phone, &call);
  417.  
  418.     if (IPC_GET_RETVAL(call.data)) { /* Connection failed */
  419.         phone_dealloc(newphid);
  420.         return IPC_GET_RETVAL(call.data);
  421.     }
  422.  
  423.     return newphid;
  424. }
  425.  
  426. /** Hang up the phone
  427.  *
  428.  *
  429.  *
  430.  */
  431. __native sys_ipc_hangup(int phoneid)
  432. {
  433.     phone_t *phone;
  434.  
  435.     GET_CHECK_PHONE(phone, phoneid, return ENOENT);
  436.  
  437.     if (ipc_phone_hangup(phone))
  438.         return -1;
  439.  
  440.     return 0;
  441. }
  442.  
  443. /** Wait for incoming ipc call or answer
  444.  *
  445.  * @param calldata Pointer to buffer where the call/answer data is stored
  446.  * @param taskid On 'CONNECT_ME_TO' call it is filled with 'taskid' of
  447.  *               the caller.
  448.  * @param flags
  449.  * @return Callid, if callid & 1, then the call is answer
  450.  */
  451. __native sys_ipc_wait_for_call(ipc_data_t *calldata, task_id_t *taskid,
  452.                    __native flags)
  453.                          
  454. {
  455.     call_t *call;
  456.  
  457. restart:   
  458.     call = ipc_wait_for_call(&TASK->answerbox, flags);
  459.     if (!call)
  460.         return 0;
  461.  
  462.     if (call->flags & IPC_CALL_ANSWERED) {
  463.         if (process_answer(&TASK->answerbox, call))
  464.             goto restart;
  465.  
  466.         ASSERT(! (call->flags & IPC_CALL_STATIC_ALLOC));
  467.  
  468.         atomic_dec(&TASK->active_calls);
  469.  
  470.         if (call->flags & IPC_CALL_DISCARD_ANSWER) {
  471.             ipc_call_free(call);
  472.             goto restart;
  473.         }
  474.  
  475.         STRUCT_TO_USPACE(&calldata->args, &call->data.args);
  476.         ipc_call_free(call);
  477.  
  478.         return ((__native)call) | IPC_CALLID_ANSWERED;
  479.     }
  480.  
  481.     if (process_request(&TASK->answerbox, call))
  482.         goto restart;
  483.  
  484.     /* Include phone address('id') of the caller in the request */
  485.     STRUCT_TO_USPACE(calldata, &call->data);
  486.     if (IPC_GET_METHOD(call->data) == IPC_M_CONNECT_ME_TO)
  487.         STRUCT_TO_USPACE(taskid, &TASK->taskid);
  488.     return (__native)call;
  489. }
  490.