Subversion Repositories HelenOS-historic

Rev

Rev 1063 | Rev 1084 | 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. /* TODO: multi-threaded connect-to-me can cause race condition
  46.  * on phone, add counter + thread_kill??
  47.  *
  48.  */
  49.  
  50. /** Return true if the method is a system method */
  51. static inline int is_system_method(__native method)
  52. {
  53.     if (method <= IPC_M_LAST_SYSTEM)
  54.         return 1;
  55.     return 0;
  56. }
  57.  
  58. /** Return true if the message with this method is forwardable
  59.  *
  60.  * - some system messages may be forwarded, for some of them
  61.  *   it is useless
  62.  */
  63. static inline int is_forwardable(__native method)
  64. {
  65.     return 1;
  66. }
  67.  
  68. /****************************************************/
  69. /* Functions that preprocess answer before sending
  70.  * it to the recepient
  71.  */
  72.  
  73. /** Return true if the caller (ipc_answer) should save
  74.  * the old call contents and call answer_preprocess
  75.  */
  76. static inline int answer_will_preprocess(call_t *call)
  77. {
  78.     if (IPC_GET_METHOD(call->data) == IPC_M_CONNECTTOME)
  79.         return 1;
  80.     if (IPC_GET_METHOD(call->data) == IPC_M_CONNECTMETO)
  81.         return 1;
  82.     return 0;
  83. }
  84.  
  85. /** Interpret process answer as control information */
  86. static inline void answer_preprocess(call_t *answer, ipc_data_t *olddata)
  87. {
  88.     int phoneid;
  89.  
  90.     if (IPC_GET_METHOD(*olddata) == IPC_M_CONNECTTOME) {
  91.         phoneid = IPC_GET_ARG3(*olddata);
  92.         if (IPC_GET_RETVAL(answer->data)) {
  93.             /* The connection was not accepted */
  94.             phone_dealloc(phoneid);
  95.         } else {
  96.             /* The connection was accepted */
  97.             phone_connect(phoneid,&answer->sender->answerbox);
  98.         }
  99.     } else if (IPC_GET_METHOD(*olddata) == IPC_M_CONNECTMETO) {
  100.         /* If the users accepted call, connect */
  101.         if (!IPC_GET_RETVAL(answer->data)) {
  102.             ipc_phone_connect((phone_t *)IPC_GET_ARG3(*olddata),
  103.                       &TASK->answerbox);
  104.         }
  105.     }
  106. }
  107.  
  108. /****************************************************/
  109. /* Functions called to process received call/answer
  110.  * before passing to uspace
  111.  */
  112.  
  113. /** Do basic kernel processing of received call answer */
  114. static int process_answer(answerbox_t *box,call_t *call)
  115. {
  116.     return 0;
  117. }
  118.  
  119. /** Do basic kernel processing of received call request
  120.  *
  121.  * @return 0 - the call should be passed to userspace, 1 - ignore call
  122.  */
  123. static int process_request(answerbox_t *box,call_t *call)
  124. {
  125.     int phoneid;
  126.  
  127.     if (IPC_GET_METHOD(call->data) == IPC_M_CONNECTTOME) {
  128.         phoneid = phone_alloc();
  129.         if (phoneid < 0) { /* Failed to allocate phone */
  130.             IPC_SET_RETVAL(call->data, ELIMIT);
  131.             ipc_answer(box,call);
  132.             return -1;
  133.         }
  134.         IPC_SET_ARG3(call->data, phoneid);
  135.     }
  136.     return 0;
  137. }
  138.  
  139. /** Send a call over IPC, wait for reply, return to user
  140.  *
  141.  * @return Call identification, returns -1 on fatal error,
  142.            -2 on 'Too many async request, handle answers first
  143.  */
  144. __native sys_ipc_call_sync_fast(__native phoneid, __native method,
  145.                 __native arg1, __native *data)
  146. {
  147.     call_t call;
  148.     phone_t *phone;
  149.  
  150.     if (is_system_method(method))
  151.         return EPERM;
  152.  
  153.     phone = get_phone_and_lock(phoneid);
  154.     if (!phone)
  155.         return ENOENT;
  156.  
  157.     ipc_call_init(&call);
  158.     IPC_SET_METHOD(call.data, method);
  159.     IPC_SET_ARG1(call.data, arg1);
  160.    
  161.     ipc_call_sync(phone, &call);
  162.  
  163.     copy_to_uspace(data, &call.data, sizeof(call.data));
  164.  
  165.     return 0;
  166. }
  167.  
  168. /** Synchronous IPC call allowing to send whole message */
  169. __native sys_ipc_call_sync(__native phoneid, __native *question,
  170.                __native *reply)
  171. {
  172.     call_t call;
  173.     phone_t *phone;
  174.  
  175.     ipc_call_init(&call);
  176.     copy_from_uspace(&call.data, question, sizeof(call.data));
  177.  
  178.     if (is_system_method(IPC_GET_METHOD(call.data)))
  179.         return EPERM;
  180.    
  181.     phone = get_phone_and_lock(phoneid);
  182.     if (!phone)
  183.         return ENOENT;
  184.  
  185.     ipc_call_sync(phone, &call);
  186.  
  187.     copy_to_uspace(reply, &call.data, sizeof(call.data));
  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.     phone = get_phone_and_lock(phoneid);
  223.     if (!phone)
  224.         return IPC_CALLRET_FATAL;
  225.  
  226.     call = ipc_call_alloc();
  227.     IPC_SET_METHOD(call->data, method);
  228.     IPC_SET_ARG1(call->data, arg1);
  229.     IPC_SET_ARG2(call->data, arg2);
  230.  
  231.     ipc_call(phone, call);
  232.  
  233.     return (__native) call;
  234. }
  235.  
  236. /** Synchronous IPC call allowing to send whole message
  237.  *
  238.  * @return The same as sys_ipc_call_async
  239.  */
  240. __native sys_ipc_call_async(__native phoneid, __native *data)
  241. {
  242.     call_t *call;
  243.     phone_t *phone;
  244.  
  245.     if (check_call_limit())
  246.         return IPC_CALLRET_TEMPORARY;
  247.  
  248.     phone = get_phone_and_lock(phoneid);
  249.     if (!phone)
  250.         return IPC_CALLRET_FATAL;
  251.  
  252.     call = ipc_call_alloc();
  253.     copy_from_uspace(&call->data, data, sizeof(call->data));
  254.  
  255.     if (is_system_method(IPC_GET_METHOD(call->data))) {
  256.         ipc_call_free(call);
  257.         return EPERM;
  258.     }
  259.    
  260.     ipc_call(phone, call);
  261.  
  262.     return (__native) call;
  263. }
  264.  
  265. /** Forward received call to another destination
  266.  *
  267.  * The arg1 and arg2 are changed in the forwarded message
  268.  *
  269.  * Warning: If implementing non-fast version, make sure that
  270.  *          arg3 is not rewritten for certain system IPC
  271.  */
  272. __native sys_ipc_forward_fast(__native callid, __native phoneid,
  273.                   __native method, __native arg1)
  274. {
  275.     call_t *call;
  276.     phone_t *phone;
  277.  
  278.     call = get_call(callid);
  279.     if (!call)
  280.         return ENOENT;
  281.  
  282.     phone = get_phone_and_lock(phoneid);
  283.     if (!phone) {
  284.         IPC_SET_RETVAL(call->data, EFORWARD);
  285.         ipc_answer(&TASK->answerbox, call);
  286.         return ENOENT;
  287.     }
  288.  
  289.     if (!is_forwardable(IPC_GET_METHOD(call->data))) {
  290.         IPC_SET_RETVAL(call->data, EFORWARD);
  291.         ipc_answer(&TASK->answerbox, call);
  292.         return EPERM;
  293.     }
  294.  
  295.     /* Userspace is not allowed to change method of system methods
  296.      * on forward, allow changing ARG1 and ARG2 by means of method and arg1
  297.      */
  298.     if (is_system_method(IPC_GET_METHOD(call->data))) {
  299.         IPC_SET_ARG1(call->data, method);
  300.         IPC_SET_ARG2(call->data, arg1);
  301.     } else {
  302.         IPC_SET_METHOD(call->data, method);
  303.         IPC_SET_ARG1(call->data, arg1);
  304.     }
  305.  
  306.     ipc_forward(call, phone->callee, &TASK->answerbox);
  307.  
  308.     return 0;
  309. }
  310.  
  311. /** Send IPC answer */
  312. __native sys_ipc_answer_fast(__native callid, __native retval,
  313.                  __native arg1, __native arg2)
  314. {
  315.     call_t *call;
  316.     ipc_data_t saved_data;
  317.     int preprocess = 0;
  318.  
  319.     call = get_call(callid);
  320.     if (!call)
  321.         return ENOENT;
  322.  
  323.     if (answer_will_preprocess(call)) {
  324.         memcpy(&saved_data, &call->data, sizeof(call->data));
  325.         preprocess = 1;
  326.     }
  327.  
  328.     IPC_SET_RETVAL(call->data, retval);
  329.     IPC_SET_ARG1(call->data, arg1);
  330.     IPC_SET_ARG2(call->data, arg2);
  331.  
  332.     if (preprocess)
  333.         answer_preprocess(call, &saved_data);
  334.  
  335.     ipc_answer(&TASK->answerbox, call);
  336.     return 0;
  337. }
  338.  
  339. /** Send IPC answer */
  340. inline __native sys_ipc_answer(__native callid, __native *data)
  341. {
  342.     call_t *call;
  343.     ipc_data_t saved_data;
  344.     int preprocess = 0;
  345.  
  346.     call = get_call(callid);
  347.     if (!call)
  348.         return ENOENT;
  349.  
  350.     if (answer_will_preprocess(call)) {
  351.         memcpy(&saved_data, &call->data, sizeof(call->data));
  352.         preprocess = 1;
  353.     }
  354.     copy_from_uspace(&call->data, data, sizeof(call->data));
  355.  
  356.     if (preprocess)
  357.         answer_preprocess(call, &saved_data);
  358.    
  359.     ipc_answer(&TASK->answerbox, call);
  360.  
  361.     return 0;
  362. }
  363.  
  364. /** Ask the other side of connection to do 'callback' connection
  365.  *
  366.  * @return 0 if no error, error otherwise
  367.  */
  368. __native sys_ipc_connect_to_me(__native phoneid, __native arg1,
  369.                    __native arg2, task_id_t *taskid)
  370. {
  371.     call_t call;
  372.     phone_t *phone;
  373.  
  374.     ipc_call_init(&call);
  375.     IPC_SET_METHOD(call.data, IPC_M_CONNECTTOME);
  376.     IPC_SET_ARG1(call.data, arg1);
  377.     IPC_SET_ARG2(call.data, arg2);
  378.    
  379.     phone = get_phone_and_lock(phoneid);
  380.     if (!phone)
  381.         return ENOENT;
  382.  
  383.     ipc_call_sync(phone, &call);
  384.  
  385.     if (!IPC_GET_RETVAL(call.data) && taskid)
  386.         copy_to_uspace(taskid,
  387.                    &phone->callee->task->taskid,
  388.                    sizeof(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.     phone = get_phone_and_lock(phoneid);
  405.     if (!phone)
  406.         return ENOENT;
  407.  
  408.     newphid = phone_alloc();
  409.     if (newphid < 0)
  410.         return ELIMIT;
  411.  
  412.     ipc_call_init(&call);
  413.     IPC_SET_METHOD(call.data, IPC_M_CONNECTMETO);
  414.     IPC_SET_ARG1(call.data, arg1);
  415.     IPC_SET_ARG2(call.data, arg2);
  416.     IPC_SET_ARG3(call.data, (__native)&TASK->phones[newphid]);
  417.  
  418.     ipc_call_sync(phone, &call);
  419.  
  420.     if (IPC_GET_RETVAL(call.data)) { /* Connection failed */
  421.         phone_dealloc(newphid);
  422.         return IPC_GET_RETVAL(call.data);
  423.     }
  424.  
  425.     return newphid;
  426. }
  427.  
  428. /** Wait for incoming ipc call or answer
  429.  *
  430.  * Generic function - can serve either as inkernel or userspace call
  431.  * - inside kernel does probably unnecessary copying of data (TODO)
  432.  *
  433.  * @param result
  434.  * @param taskid
  435.  * @param flags
  436.  * @return Callid, if callid & 1, then the call is answer
  437.  */
  438. inline __native sys_ipc_wait_for_call(ipc_data_t *calldata,
  439.                       task_id_t *taskid,
  440.                       __native flags)
  441.                          
  442. {
  443.     call_t *call;
  444.  
  445. restart:   
  446.     call = ipc_wait_for_call(&TASK->answerbox, flags);
  447.  
  448.     if (call->flags & IPC_CALL_ANSWERED) {
  449.         if (process_answer(&TASK->answerbox, call))
  450.             goto restart;
  451.  
  452.         copy_to_uspace(calldata, &call->data, sizeof(call->data));
  453.         atomic_dec(&TASK->active_calls);
  454.         ASSERT(! (call->flags & IPC_CALL_STATIC_ALLOC));
  455.         ipc_call_free(call);
  456.  
  457.         return ((__native)call) | IPC_CALLID_ANSWERED;
  458.     }
  459.     if (process_request(&TASK->answerbox, call))
  460.         goto restart;
  461.     copy_to_uspace(calldata, &call->data, sizeof(call->data));
  462.     copy_to_uspace(taskid, (void *)&TASK->taskid, sizeof(TASK->taskid));
  463.     return (__native)call;
  464. }
  465.  
  466.