Subversion Repositories HelenOS-historic

Rev

Rev 1595 | Rev 1693 | 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->state == IPC_PHONE_CONNECTED) {
  109.             list_remove(&answer->data.phone->link);
  110.             answer->data.phone->state = IPC_PHONE_SLAMMED;
  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.     IPC_SET_ARG3(call->data, 0);
  337.  
  338.     if (!(res=request_preprocess(call)))
  339.         ipc_call(phone, call);
  340.     else
  341.         ipc_backsend_err(phone, call, res);
  342.  
  343.     return (__native) call;
  344. }
  345.  
  346. /** Synchronous IPC call allowing to send whole message
  347.  *
  348.  * @return The same as sys_ipc_call_async
  349.  */
  350. __native sys_ipc_call_async(__native phoneid, ipc_data_t *data)
  351. {
  352.     call_t *call;
  353.     phone_t *phone;
  354.     int res;
  355.     int rc;
  356.  
  357.     if (check_call_limit())
  358.         return IPC_CALLRET_TEMPORARY;
  359.  
  360.     GET_CHECK_PHONE(phone, phoneid, return IPC_CALLRET_FATAL);
  361.  
  362.     call = ipc_call_alloc(0);
  363.     rc = copy_from_uspace(&call->data.args, &data->args, sizeof(call->data.args));
  364.     if (rc != 0) {
  365.         ipc_call_free(call);
  366.         return (__native) rc;
  367.     }
  368.     if (!(res=request_preprocess(call)))
  369.         ipc_call(phone, call);
  370.     else
  371.         ipc_backsend_err(phone, call, res);
  372.  
  373.     return (__native) call;
  374. }
  375.  
  376. /** Forward received call to another destination
  377.  *
  378.  * The arg1 and arg2 are changed in the forwarded message
  379.  *
  380.  * Warning: If implementing non-fast version, make sure that
  381.  *          arg3 is not rewritten for certain system IPC
  382.  */
  383. __native sys_ipc_forward_fast(__native callid, __native phoneid,
  384.                   __native method, __native arg1)
  385. {
  386.     call_t *call;
  387.     phone_t *phone;
  388.  
  389.     call = get_call(callid);
  390.     if (!call)
  391.         return ENOENT;
  392.  
  393.     call->flags |= IPC_CALL_FORWARDED;
  394.  
  395.     GET_CHECK_PHONE(phone, phoneid, {
  396.         IPC_SET_RETVAL(call->data, EFORWARD);
  397.         ipc_answer(&TASK->answerbox, call);
  398.         return ENOENT;
  399.     });    
  400.  
  401.     if (!is_forwardable(IPC_GET_METHOD(call->data))) {
  402.         IPC_SET_RETVAL(call->data, EFORWARD);
  403.         ipc_answer(&TASK->answerbox, call);
  404.         return EPERM;
  405.     }
  406.  
  407.     /* Userspace is not allowed to change method of system methods
  408.      * on forward, allow changing ARG1 and ARG2 by means of method and arg1
  409.      */
  410.     if (is_system_method(IPC_GET_METHOD(call->data))) {
  411.         if (IPC_GET_METHOD(call->data) == IPC_M_CONNECT_TO_ME)
  412.             phone_dealloc(IPC_GET_ARG3(call->data));
  413.  
  414.         IPC_SET_ARG1(call->data, method);
  415.         IPC_SET_ARG2(call->data, arg1);
  416.     } else {
  417.         IPC_SET_METHOD(call->data, method);
  418.         IPC_SET_ARG1(call->data, arg1);
  419.     }
  420.  
  421.     return ipc_forward(call, phone, &TASK->answerbox);
  422. }
  423.  
  424. /** Send IPC answer */
  425. __native sys_ipc_answer_fast(__native callid, __native retval,
  426.                  __native arg1, __native arg2)
  427. {
  428.     call_t *call;
  429.     ipc_data_t saved_data;
  430.     int saveddata = 0;
  431.     int rc;
  432.  
  433.     /* Do not answer notification callids */
  434.     if (callid & IPC_CALLID_NOTIFICATION)
  435.         return 0;
  436.  
  437.     call = get_call(callid);
  438.     if (!call)
  439.         return ENOENT;
  440.  
  441.     if (answer_need_old(call)) {
  442.         memcpy(&saved_data, &call->data, sizeof(call->data));
  443.         saveddata = 1;
  444.     }
  445.  
  446.     IPC_SET_RETVAL(call->data, retval);
  447.     IPC_SET_ARG1(call->data, arg1);
  448.     IPC_SET_ARG2(call->data, arg2);
  449.     rc = answer_preprocess(call, saveddata ? &saved_data : NULL);
  450.  
  451.     ipc_answer(&TASK->answerbox, call);
  452.     return rc;
  453. }
  454.  
  455. /** Send IPC answer */
  456. __native sys_ipc_answer(__native callid, ipc_data_t *data)
  457. {
  458.     call_t *call;
  459.     ipc_data_t saved_data;
  460.     int saveddata = 0;
  461.     int rc;
  462.  
  463.     /* Do not answer notification callids */
  464.     if (callid & IPC_CALLID_NOTIFICATION)
  465.         return 0;
  466.  
  467.     call = get_call(callid);
  468.     if (!call)
  469.         return ENOENT;
  470.  
  471.     if (answer_need_old(call)) {
  472.         memcpy(&saved_data, &call->data, sizeof(call->data));
  473.         saveddata = 1;
  474.     }
  475.     rc = copy_from_uspace(&call->data.args, &data->args,
  476.              sizeof(call->data.args));
  477.     if (rc != 0)
  478.         return rc;
  479.  
  480.     rc = answer_preprocess(call, saveddata ? &saved_data : NULL);
  481.    
  482.     ipc_answer(&TASK->answerbox, call);
  483.  
  484.     return rc;
  485. }
  486.  
  487. /** Hang up the phone
  488.  *
  489.  */
  490. __native sys_ipc_hangup(int phoneid)
  491. {
  492.     phone_t *phone;
  493.  
  494.     GET_CHECK_PHONE(phone, phoneid, return ENOENT);
  495.  
  496.     if (ipc_phone_hangup(phone))
  497.         return -1;
  498.  
  499.     return 0;
  500. }
  501.  
  502. /** Wait for incoming ipc call or answer
  503.  *
  504.  * @param calldata Pointer to buffer where the call/answer data is stored
  505.  * @param usec Timeout. See waitq_sleep_timeout() for explanation.
  506.  * @param flags Select mode of sleep operation. See waitq_sleep_timeout() for explanation.
  507.  *
  508.  * @return Callid, if callid & 1, then the call is answer
  509.  */
  510. __native sys_ipc_wait_for_call(ipc_data_t *calldata, __u32 usec, int flags)
  511. {
  512.     call_t *call;
  513.  
  514. restart:   
  515.     call = ipc_wait_for_call(&TASK->answerbox, usec, flags | SYNCH_FLAGS_INTERRUPTIBLE);
  516.     if (!call)
  517.         return 0;
  518.  
  519.     if (call->flags & IPC_CALL_NOTIF) {
  520.         ASSERT(! (call->flags & IPC_CALL_STATIC_ALLOC));
  521.         STRUCT_TO_USPACE(&calldata->args, &call->data.args);
  522.         ipc_call_free(call);
  523.        
  524.         return ((__native)call) | IPC_CALLID_NOTIFICATION;
  525.     }
  526.  
  527.     if (call->flags & IPC_CALL_ANSWERED) {
  528.         process_answer(call);
  529.  
  530.         ASSERT(! (call->flags & IPC_CALL_STATIC_ALLOC));
  531.  
  532.         atomic_dec(&TASK->active_calls);
  533.  
  534.         if (call->flags & IPC_CALL_DISCARD_ANSWER) {
  535.             ipc_call_free(call);
  536.             goto restart;
  537.         }
  538.  
  539.         STRUCT_TO_USPACE(&calldata->args, &call->data.args);
  540.         ipc_call_free(call);
  541.  
  542.         return ((__native)call) | IPC_CALLID_ANSWERED;
  543.     }
  544.  
  545.     if (process_request(&TASK->answerbox, call))
  546.         goto restart;
  547.  
  548.     /* Include phone address('id') of the caller in the request,
  549.      * copy whole call->data, not only call->data.args */
  550.     if (STRUCT_TO_USPACE(calldata, &call->data)) {
  551.         return 0;
  552.     }
  553.     return (__native)call;
  554. }
  555.  
  556. /** Connect irq handler to task */
  557. __native sys_ipc_register_irq(int irq, irq_code_t *ucode)
  558. {
  559.     if (!(cap_get(TASK) & CAP_IRQ_REG))
  560.         return EPERM;
  561.  
  562.     if (irq >= IRQ_COUNT || irq <= -IPC_IRQ_RESERVED_VIRTUAL)
  563.         return (__native) ELIMIT;
  564.    
  565.     irq_ipc_bind_arch(irq);
  566.  
  567.     return ipc_irq_register(&TASK->answerbox, irq, ucode);
  568. }
  569.  
  570. /* Disconnect irq handler from task */
  571. __native sys_ipc_unregister_irq(int irq)
  572. {
  573.     if (!(cap_get(TASK) & CAP_IRQ_REG))
  574.         return EPERM;
  575.  
  576.     if (irq >= IRQ_COUNT || irq <= -IPC_IRQ_RESERVED_VIRTUAL)
  577.         return (__native) ELIMIT;
  578.  
  579.     ipc_irq_unregister(&TASK->answerbox, irq);
  580.  
  581.     return 0;
  582. }
  583.