Subversion Repositories HelenOS

Rev

Rev 1434 | Rev 1502 | 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. #include <proc/thread.h>
  32. #include <errno.h>
  33. #include <memstr.h>
  34. #include <debug.h>
  35. #include <ipc/ipc.h>
  36. #include <ipc/sysipc.h>
  37. #include <ipc/irq.h>
  38. #include <ipc/ipcrsc.h>
  39. #include <arch/interrupt.h>
  40. #include <print.h>
  41. #include <syscall/copy.h>
  42. #include <security/cap.h>
  43. #include <mm/as.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 || method == IPC_M_AS_AREA_SEND \
  68.         || method == IPC_M_AS_AREA_RECV)
  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.     if (IPC_GET_METHOD(call->data) == IPC_M_AS_AREA_SEND)
  88.         return 1;
  89.     if (IPC_GET_METHOD(call->data) == IPC_M_AS_AREA_RECV)
  90.         return 1;
  91.     return 0;
  92. }
  93.  
  94. /** Interpret process answer as control information
  95.  *
  96.  * This function is called directly after sys_ipc_answer
  97.  */
  98. static inline int answer_preprocess(call_t *answer, ipc_data_t *olddata)
  99. {
  100.     int phoneid;
  101.  
  102.     if (IPC_GET_RETVAL(answer->data) == EHANGUP) {
  103.         /* In case of forward, hangup the forwared phone,
  104.          * not the originator
  105.          */
  106.         spinlock_lock(&answer->data.phone->lock);
  107.         spinlock_lock(&TASK->answerbox.lock);
  108.         if (answer->data.phone->callee) {
  109.             list_remove(&answer->data.phone->list);
  110.             answer->data.phone->callee = 0;
  111.         }
  112.         spinlock_unlock(&TASK->answerbox.lock);
  113.         spinlock_unlock(&answer->data.phone->lock);
  114.     }
  115.  
  116.     if (!olddata)
  117.         return 0;
  118.  
  119.     if (IPC_GET_METHOD(*olddata) == IPC_M_CONNECT_TO_ME) {
  120.         phoneid = IPC_GET_ARG3(*olddata);
  121.         if (IPC_GET_RETVAL(answer->data)) {
  122.             /* The connection was not accepted */
  123.             phone_dealloc(phoneid);
  124.         } else {
  125.             /* The connection was accepted */
  126.             phone_connect(phoneid,&answer->sender->answerbox);
  127.             /* Set 'phone identification' as arg3 of response */
  128.             IPC_SET_ARG3(answer->data, (__native)&TASK->phones[phoneid]);
  129.         }
  130.     } else if (IPC_GET_METHOD(*olddata) == IPC_M_CONNECT_ME_TO) {
  131.         /* If the users accepted call, connect */
  132.         if (!IPC_GET_RETVAL(answer->data)) {
  133.             ipc_phone_connect((phone_t *)IPC_GET_ARG3(*olddata),
  134.                       &TASK->answerbox);
  135.         }
  136.     } else if (IPC_GET_METHOD(*olddata) == IPC_M_AS_AREA_SEND) {
  137.         if (!IPC_GET_RETVAL(answer->data)) { /* Accepted, handle as_area receipt */
  138.             ipl_t ipl;
  139.             int rc;
  140.             as_t *as;
  141.            
  142.             ipl = interrupts_disable();
  143.             spinlock_lock(&answer->sender->lock);
  144.             as = answer->sender->as;
  145.             spinlock_unlock(&answer->sender->lock);
  146.             interrupts_restore(ipl);
  147.            
  148.             rc = as_area_share(as, IPC_GET_ARG1(*olddata), IPC_GET_ARG2(*olddata),
  149.                        AS, IPC_GET_ARG1(answer->data), IPC_GET_ARG3(*olddata));
  150.             IPC_SET_RETVAL(answer->data, rc);
  151.             return rc;
  152.         }
  153.     } else if (IPC_GET_METHOD(*olddata) == IPC_M_AS_AREA_RECV) {
  154.         if (!IPC_GET_RETVAL(answer->data)) {
  155.             ipl_t ipl;
  156.             as_t *as;
  157.             int rc;
  158.            
  159.             ipl = interrupts_disable();
  160.             spinlock_lock(&answer->sender->lock);
  161.             as = answer->sender->as;
  162.             spinlock_unlock(&answer->sender->lock);
  163.             interrupts_restore(ipl);
  164.            
  165.             rc = as_area_share(AS, IPC_GET_ARG1(answer->data), IPC_GET_ARG2(*olddata),
  166.                        as, IPC_GET_ARG1(*olddata), IPC_GET_ARG2(answer->data));
  167.             IPC_SET_RETVAL(answer->data, rc);
  168.         }
  169.     }
  170.     return 0;
  171. }
  172.  
  173. /** Called before the request is sent
  174.  *
  175.  * @return 0 - no error, -1 - report error to user
  176.  */
  177. static int request_preprocess(call_t *call)
  178. {
  179.     int newphid;
  180.     size_t size;
  181.  
  182.     switch (IPC_GET_METHOD(call->data)) {
  183.     case IPC_M_CONNECT_ME_TO:
  184.         newphid = phone_alloc();
  185.         if (newphid < 0)
  186.             return ELIMIT;
  187.         /* Set arg3 for server */
  188.         IPC_SET_ARG3(call->data, (__native)&TASK->phones[newphid]);
  189.         call->flags |= IPC_CALL_CONN_ME_TO;
  190.         call->private = newphid;
  191.         break;
  192.     case IPC_M_AS_AREA_SEND:
  193.         size = as_get_size(IPC_GET_ARG1(call->data));
  194.         if (!size) {
  195.             return EPERM;
  196.         }
  197.         IPC_SET_ARG2(call->data, size);
  198.         break;
  199.     default:
  200.         break;
  201.     }
  202.     return 0;
  203. }
  204.  
  205. /****************************************************/
  206. /* Functions called to process received call/answer
  207.  * before passing to uspace
  208.  */
  209.  
  210. /** Do basic kernel processing of received call answer */
  211. static void process_answer(call_t *call)
  212. {
  213.     if (IPC_GET_RETVAL(call->data) == EHANGUP && \
  214.         call->flags & IPC_CALL_FORWARDED)
  215.         IPC_SET_RETVAL(call->data, EFORWARD);
  216.  
  217.     if (call->flags & IPC_CALL_CONN_ME_TO) {
  218.         if (IPC_GET_RETVAL(call->data))
  219.             phone_dealloc(call->private);
  220.         else
  221.             IPC_SET_ARG3(call->data, call->private);
  222.     }
  223. }
  224.  
  225. /** Do basic kernel processing of received call request
  226.  *
  227.  * @return 0 - the call should be passed to userspace, 1 - ignore call
  228.  */
  229. static int process_request(answerbox_t *box,call_t *call)
  230. {
  231.     int phoneid;
  232.  
  233.     if (IPC_GET_METHOD(call->data) == IPC_M_CONNECT_TO_ME) {
  234.         phoneid = phone_alloc();
  235.         if (phoneid < 0) { /* Failed to allocate phone */
  236.             IPC_SET_RETVAL(call->data, ELIMIT);
  237.             ipc_answer(box,call);
  238.             return -1;
  239.         }
  240.         IPC_SET_ARG3(call->data, phoneid);
  241.     }
  242.     return 0;
  243. }
  244.  
  245. /** Send a call over IPC, wait for reply, return to user
  246.  *
  247.  * @return Call identification, returns -1 on fatal error,
  248.            -2 on 'Too many async request, handle answers first
  249.  */
  250. __native sys_ipc_call_sync_fast(__native phoneid, __native method,
  251.                 __native arg1, ipc_data_t *data)
  252. {
  253.     call_t call;
  254.     phone_t *phone;
  255.     int res;
  256.  
  257.     GET_CHECK_PHONE(phone, phoneid, return ENOENT);
  258.  
  259.     ipc_call_static_init(&call);
  260.     IPC_SET_METHOD(call.data, method);
  261.     IPC_SET_ARG1(call.data, arg1);
  262.  
  263.     if (!(res=request_preprocess(&call))) {
  264.         ipc_call_sync(phone, &call);
  265.         process_answer(&call);
  266.     } else
  267.         IPC_SET_RETVAL(call.data, res);
  268.     STRUCT_TO_USPACE(&data->args, &call.data.args);
  269.  
  270.     return 0;
  271. }
  272.  
  273. /** Synchronous IPC call allowing to send whole message */
  274. __native sys_ipc_call_sync(__native phoneid, ipc_data_t *question,
  275.                ipc_data_t *reply)
  276. {
  277.     call_t call;
  278.     phone_t *phone;
  279.     int res;
  280.     int rc;
  281.  
  282.     ipc_call_static_init(&call);
  283.     rc = copy_from_uspace(&call.data.args, &question->args, sizeof(call.data.args));
  284.     if (rc != 0)
  285.         return (__native) rc;
  286.  
  287.     GET_CHECK_PHONE(phone, phoneid, return ENOENT);
  288.  
  289.     if (!(res=request_preprocess(&call))) {
  290.         ipc_call_sync(phone, &call);
  291.         process_answer(&call);
  292.     } else
  293.         IPC_SET_RETVAL(call.data, res);
  294.  
  295.     rc = STRUCT_TO_USPACE(&reply->args, &call.data.args);
  296.     if (rc != 0)
  297.         return rc;
  298.  
  299.     return 0;
  300. }
  301.  
  302. /** Check that the task did not exceed allowed limit
  303.  *
  304.  * @return 0 - Limit OK,   -1 - limit exceeded
  305.  */
  306. static int check_call_limit(void)
  307. {
  308.     if (atomic_preinc(&TASK->active_calls) > IPC_MAX_ASYNC_CALLS) {
  309.         atomic_dec(&TASK->active_calls);
  310.         return -1;
  311.     }
  312.     return 0;
  313. }
  314.  
  315. /** Send an asynchronous call over ipc
  316.  *
  317.  * @return Call identification, returns -1 on fatal error,
  318.            -2 on 'Too many async request, handle answers first
  319.  */
  320. __native sys_ipc_call_async_fast(__native phoneid, __native method,
  321.                  __native arg1, __native arg2)
  322. {
  323.     call_t *call;
  324.     phone_t *phone;
  325.     int res;
  326.  
  327.     if (check_call_limit())
  328.         return IPC_CALLRET_TEMPORARY;
  329.  
  330.     GET_CHECK_PHONE(phone, phoneid, return IPC_CALLRET_FATAL);
  331.  
  332.     call = ipc_call_alloc(0);
  333.     IPC_SET_METHOD(call->data, method);
  334.     IPC_SET_ARG1(call->data, arg1);
  335.     IPC_SET_ARG2(call->data, arg2);
  336.  
  337.     if (!(res=request_preprocess(call)))
  338.         ipc_call(phone, call);
  339.     else
  340.         ipc_backsend_err(phone, call, res);
  341.  
  342.     return (__native) call;
  343. }
  344.  
  345. /** Synchronous IPC call allowing to send whole message
  346.  *
  347.  * @return The same as sys_ipc_call_async
  348.  */
  349. __native sys_ipc_call_async(__native phoneid, ipc_data_t *data)
  350. {
  351.     call_t *call;
  352.     phone_t *phone;
  353.     int res;
  354.     int rc;
  355.  
  356.     if (check_call_limit())
  357.         return IPC_CALLRET_TEMPORARY;
  358.  
  359.     GET_CHECK_PHONE(phone, phoneid, return IPC_CALLRET_FATAL);
  360.  
  361.     call = ipc_call_alloc(0);
  362.     rc = copy_from_uspace(&call->data.args, &data->args, sizeof(call->data.args));
  363.     if (rc != 0) {
  364.         ipc_call_free(call);
  365.         return (__native) rc;
  366.     }
  367.     if (!(res=request_preprocess(call)))
  368.         ipc_call(phone, call);
  369.     else
  370.         ipc_backsend_err(phone, call, res);
  371.  
  372.     return (__native) call;
  373. }
  374.  
  375. /** Forward received call to another destination
  376.  *
  377.  * The arg1 and arg2 are changed in the forwarded message
  378.  *
  379.  * Warning: If implementing non-fast version, make sure that
  380.  *          arg3 is not rewritten for certain system IPC
  381.  */
  382. __native sys_ipc_forward_fast(__native callid, __native phoneid,
  383.                   __native method, __native arg1)
  384. {
  385.     call_t *call;
  386.     phone_t *phone;
  387.  
  388.     call = get_call(callid);
  389.     if (!call)
  390.         return ENOENT;
  391.  
  392.     call->flags |= IPC_CALL_FORWARDED;
  393.  
  394.     GET_CHECK_PHONE(phone, phoneid, {
  395.         IPC_SET_RETVAL(call->data, EFORWARD);
  396.         ipc_answer(&TASK->answerbox, call);
  397.         return ENOENT;
  398.     });    
  399.  
  400.     if (!is_forwardable(IPC_GET_METHOD(call->data))) {
  401.         IPC_SET_RETVAL(call->data, EFORWARD);
  402.         ipc_answer(&TASK->answerbox, call);
  403.         return EPERM;
  404.     }
  405.  
  406.     /* Userspace is not allowed to change method of system methods
  407.      * on forward, allow changing ARG1 and ARG2 by means of method and arg1
  408.      */
  409.     if (is_system_method(IPC_GET_METHOD(call->data))) {
  410.         if (IPC_GET_METHOD(call->data) == IPC_M_CONNECT_TO_ME)
  411.             phone_dealloc(IPC_GET_ARG3(call->data));
  412.  
  413.         IPC_SET_ARG1(call->data, method);
  414.         IPC_SET_ARG2(call->data, arg1);
  415.     } else {
  416.         IPC_SET_METHOD(call->data, method);
  417.         IPC_SET_ARG1(call->data, arg1);
  418.     }
  419.  
  420.     return ipc_forward(call, phone, &TASK->answerbox);
  421. }
  422.  
  423. /** Send IPC answer */
  424. __native sys_ipc_answer_fast(__native callid, __native retval,
  425.                  __native arg1, __native arg2)
  426. {
  427.     call_t *call;
  428.     ipc_data_t saved_data;
  429.     int saveddata = 0;
  430.     int rc;
  431.  
  432.     /* Do not answer notification callids */
  433.     if (callid & IPC_CALLID_NOTIFICATION)
  434.         return 0;
  435.  
  436.     call = get_call(callid);
  437.     if (!call)
  438.         return ENOENT;
  439.  
  440.     if (answer_need_old(call)) {
  441.         memcpy(&saved_data, &call->data, sizeof(call->data));
  442.         saveddata = 1;
  443.     }
  444.  
  445.     IPC_SET_RETVAL(call->data, retval);
  446.     IPC_SET_ARG1(call->data, arg1);
  447.     IPC_SET_ARG2(call->data, arg2);
  448.     rc = answer_preprocess(call, saveddata ? &saved_data : NULL);
  449.  
  450.     ipc_answer(&TASK->answerbox, call);
  451.     return rc;
  452. }
  453.  
  454. /** Send IPC answer */
  455. __native sys_ipc_answer(__native callid, ipc_data_t *data)
  456. {
  457.     call_t *call;
  458.     ipc_data_t saved_data;
  459.     int saveddata = 0;
  460.     int rc;
  461.  
  462.     /* Do not answer notification callids */
  463.     if (callid & IPC_CALLID_NOTIFICATION)
  464.         return 0;
  465.  
  466.     call = get_call(callid);
  467.     if (!call)
  468.         return ENOENT;
  469.  
  470.     if (answer_need_old(call)) {
  471.         memcpy(&saved_data, &call->data, sizeof(call->data));
  472.         saveddata = 1;
  473.     }
  474.     rc = copy_from_uspace(&call->data.args, &data->args,
  475.              sizeof(call->data.args));
  476.     if (rc != 0)
  477.         return rc;
  478.  
  479.     rc = answer_preprocess(call, saveddata ? &saved_data : NULL);
  480.    
  481.     ipc_answer(&TASK->answerbox, call);
  482.  
  483.     return rc;
  484. }
  485.  
  486. /** Hang up the phone
  487.  *
  488.  */
  489. __native sys_ipc_hangup(int phoneid)
  490. {
  491.     phone_t *phone;
  492.  
  493.     GET_CHECK_PHONE(phone, phoneid, return ENOENT);
  494.  
  495.     if (ipc_phone_hangup(phone))
  496.         return -1;
  497.  
  498.     return 0;
  499. }
  500.  
  501. /** Wait for incoming ipc call or answer
  502.  *
  503.  * @param calldata Pointer to buffer where the call/answer data is stored
  504.  * @param usec Timeout. See waitq_sleep_timeout() for explanation.
  505.  * @param nonblocking See waitq_sleep_timeout() for explanation.
  506.  *
  507.  * @return Callid, if callid & 1, then the call is answer
  508.  */
  509. __native sys_ipc_wait_for_call(ipc_data_t *calldata, __u32 usec, int nonblocking)
  510. {
  511.     call_t *call;
  512.  
  513. restart:   
  514.     call = ipc_wait_for_call(&TASK->answerbox, usec, nonblocking);
  515.     if (!call)
  516.         return 0;
  517.  
  518.     if (call->flags & IPC_CALL_NOTIF) {
  519.         ASSERT(! (call->flags & IPC_CALL_STATIC_ALLOC));
  520.         STRUCT_TO_USPACE(&calldata->args, &call->data.args);
  521.         ipc_call_free(call);
  522.        
  523.         return ((__native)call) | IPC_CALLID_NOTIFICATION;
  524.     }
  525.  
  526.     if (call->flags & IPC_CALL_ANSWERED) {
  527.         process_answer(call);
  528.  
  529.         ASSERT(! (call->flags & IPC_CALL_STATIC_ALLOC));
  530.  
  531.         atomic_dec(&TASK->active_calls);
  532.  
  533.         if (call->flags & IPC_CALL_DISCARD_ANSWER) {
  534.             ipc_call_free(call);
  535.             goto restart;
  536.         }
  537.  
  538.         STRUCT_TO_USPACE(&calldata->args, &call->data.args);
  539.         ipc_call_free(call);
  540.  
  541.         return ((__native)call) | IPC_CALLID_ANSWERED;
  542.     }
  543.  
  544.     if (process_request(&TASK->answerbox, call))
  545.         goto restart;
  546.  
  547.     /* Include phone address('id') of the caller in the request,
  548.      * copy whole call->data, not only call->data.args */
  549.     if (STRUCT_TO_USPACE(calldata, &call->data)) {
  550.         return 0;
  551.     }
  552.     return (__native)call;
  553. }
  554.  
  555. /** Connect irq handler to task */
  556. __native sys_ipc_register_irq(__native irq, irq_code_t *ucode)
  557. {
  558.     if (!(cap_get(TASK) & CAP_IRQ_REG))
  559.         return EPERM;
  560.  
  561.     if (irq >= IRQ_COUNT)
  562.         return (__native) ELIMIT;
  563.  
  564.     irq_ipc_bind_arch(irq);
  565.  
  566.     return ipc_irq_register(&TASK->answerbox, irq, ucode);
  567. }
  568.  
  569. /* Disconnect irq handler from task */
  570. __native sys_ipc_unregister_irq(__native irq)
  571. {
  572.     if (!(cap_get(TASK) & CAP_IRQ_REG))
  573.         return EPERM;
  574.  
  575.     if (irq >= IRQ_COUNT)
  576.         return (__native) ELIMIT;
  577.  
  578.     ipc_irq_unregister(&TASK->answerbox, irq);
  579.  
  580.     return 0;
  581. }
  582.