Subversion Repositories HelenOS

Rev

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