Subversion Repositories HelenOS

Rev

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