Subversion Repositories HelenOS

Rev

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