Subversion Repositories HelenOS

Rev

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