Subversion Repositories HelenOS

Rev

Rev 2307 | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

  1. /*
  2.  * Copyright (c) 2006 Ondrej Palkovsky
  3.  * Copyright (c) 2006 Jakub Jermar
  4.  * All rights reserved.
  5.  *
  6.  * Redistribution and use in source and binary forms, with or without
  7.  * modification, are permitted provided that the following conditions
  8.  * are met:
  9.  *
  10.  * - Redistributions of source code must retain the above copyright
  11.  *   notice, this list of conditions and the following disclaimer.
  12.  * - Redistributions in binary form must reproduce the above copyright
  13.  *   notice, this list of conditions and the following disclaimer in the
  14.  *   documentation and/or other materials provided with the distribution.
  15.  * - The name of the author may not be used to endorse or promote products
  16.  *   derived from this software without specific prior written permission.
  17.  *
  18.  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
  19.  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  20.  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
  21.  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
  22.  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  23.  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  24.  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  25.  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  26.  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  27.  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  28.  */
  29.  
  30. /** @addtogroup genericipc
  31.  * @{
  32.  */
  33. /**
  34.  * @file
  35.  * @brief IRQ notification framework.
  36.  *
  37.  * This framework allows applications to register to receive a notification
  38.  * when interrupt is detected. The application may provide a simple 'top-half'
  39.  * handler as part of its registration, which can perform simple operations
  40.  * (read/write port/memory, add information to notification ipc message).
  41.  *
  42.  * The structure of a notification message is as follows:
  43.  * - METHOD: method as registered by the SYS_IPC_REGISTER_IRQ syscall
  44.  * - ARG1: payload modified by a 'top-half' handler
  45.  * - ARG2: payload modified by a 'top-half' handler
  46.  * - ARG3: payload modified by a 'top-half' handler
  47.  * - in_phone_hash: interrupt counter (may be needed to assure correct order
  48.  *         in multithreaded drivers)
  49.  */
  50.  
  51. #include <arch.h>
  52. #include <mm/slab.h>
  53. #include <errno.h>
  54. #include <ddi/irq.h>
  55. #include <ipc/ipc.h>
  56. #include <ipc/irq.h>
  57. #include <syscall/copy.h>
  58. #include <console/console.h>
  59. #include <print.h>
  60. #include <synch/rcu.h>
  61. #include <adt/listrcu.h>
  62.  
  63. /** Execute code associated with IRQ notification.
  64.  *
  65.  * @param call Notification call.
  66.  * @param code Top-half pseudocode.
  67.  */
  68. static void code_execute(call_t *call, irq_code_t *code)
  69. {
  70.     int i;
  71.     unative_t dstval = 0;
  72.    
  73.     if (!code)
  74.         return;
  75.    
  76.     for (i=0; i < code->cmdcount;i++) {
  77.         switch (code->cmds[i].cmd) {
  78.         case CMD_MEM_READ_1:
  79.             dstval = *((uint8_t *)code->cmds[i].addr);
  80.             break;
  81.         case CMD_MEM_READ_2:
  82.             dstval = *((uint16_t *)code->cmds[i].addr);
  83.             break;
  84.         case CMD_MEM_READ_4:
  85.             dstval = *((uint32_t *)code->cmds[i].addr);
  86.             break;
  87.         case CMD_MEM_READ_8:
  88.             dstval = *((uint64_t *)code->cmds[i].addr);
  89.             break;
  90.         case CMD_MEM_WRITE_1:
  91.             *((uint8_t *)code->cmds[i].addr) = code->cmds[i].value;
  92.             break;
  93.         case CMD_MEM_WRITE_2:
  94.             *((uint16_t *)code->cmds[i].addr) = code->cmds[i].value;
  95.             break;
  96.         case CMD_MEM_WRITE_4:
  97.             *((uint32_t *)code->cmds[i].addr) = code->cmds[i].value;
  98.             break;
  99.         case CMD_MEM_WRITE_8:
  100.             *((uint64_t *)code->cmds[i].addr) = code->cmds[i].value;
  101.             break;
  102. #if defined(ia32) || defined(amd64)
  103.         case CMD_PORT_READ_1:
  104.             dstval = inb((long)code->cmds[i].addr);
  105.             break;
  106.         case CMD_PORT_WRITE_1:
  107.             outb((long)code->cmds[i].addr, code->cmds[i].value);
  108.             break;
  109. #endif
  110. #if defined(ia64) && defined(SKI)
  111.         case CMD_IA64_GETCHAR:
  112.             dstval = _getc(&ski_uconsole);
  113.             break;
  114. #endif
  115. #if defined(ppc32)
  116.         case CMD_PPC32_GETCHAR:
  117.             dstval = cuda_get_scancode();
  118.             break;
  119. #endif
  120.         default:
  121.             break;
  122.         }
  123.         if (code->cmds[i].dstarg && code->cmds[i].dstarg < 4) {
  124.             call->data.args[code->cmds[i].dstarg] = dstval;
  125.         }
  126.     }
  127. }
  128.  
  129. static void code_free(irq_code_t *code)
  130. {
  131.     if (code) {
  132.         free(code->cmds);
  133.         free(code);
  134.     }
  135. }
  136.  
  137. static irq_code_t * code_from_uspace(irq_code_t *ucode)
  138. {
  139.     irq_code_t *code;
  140.     irq_cmd_t *ucmds;
  141.     int rc;
  142.  
  143.     code = malloc(sizeof(*code), 0);
  144.     rc = copy_from_uspace(code, ucode, sizeof(*code));
  145.     if (rc != 0) {
  146.         free(code);
  147.         return NULL;
  148.     }
  149.    
  150.     if (code->cmdcount > IRQ_MAX_PROG_SIZE) {
  151.         free(code);
  152.         return NULL;
  153.     }
  154.     ucmds = code->cmds;
  155.     code->cmds = malloc(sizeof(code->cmds[0]) * (code->cmdcount), 0);
  156.     rc = copy_from_uspace(code->cmds, ucmds, sizeof(code->cmds[0]) * (code->cmdcount));
  157.     if (rc != 0) {
  158.         free(code->cmds);
  159.         free(code);
  160.         return NULL;
  161.     }
  162.  
  163.     return code;
  164. }
  165.  
  166. /** Unregister task from IRQ notification.
  167.  *
  168.  * @param box Answerbox associated with the notification.
  169.  * @param inr IRQ numbe.
  170.  * @param devno Device number.
  171.  */
  172. void ipc_irq_unregister(answerbox_t *box, inr_t inr, devno_t devno)
  173. {
  174.     ipl_t ipl;
  175.     irq_t *irq;
  176.     ipc_notif_cfg_t *new_notify, *old_notify;
  177.  
  178.     ipl = interrupts_disable();
  179.     irq = irq_find_and_lock(inr, devno);
  180.     if (irq) {
  181.         if (rcu_dereference_pointer(irq->notif_cfg).answerbox == box) {
  182.             new_notify = malloc(sizeof(ipc_notif_cfg_t),0);
  183.  
  184.             new_notify->notify = false;
  185.             new_notify->answerbox = NULL;
  186.             new_notify->code = NULL;
  187.             new_notify->method = 0;
  188.             new_notify->counter = 0;
  189.             old_notify = irq->notif_cfg;
  190.             copy_link_rcu(&irq->notif_cfg->link, &new_notify->link);
  191.             rcu_assign_pointer(old_notify, new_notify);
  192.  
  193.             spinlock_lock(&box->irq_lock);
  194.             list_remove_rcu(&rcu_dereference_pointer(irq->notif_cfg).link);
  195.             spinlock_unlock(&box->irq_lock);
  196.             rcu_sync_callback_normal_alloc(&ipc_notif_free_callback, irq->notif_cfg);
  197.            
  198.             spinlock_unlock(&irq->lock);
  199.         }
  200.     }
  201.     interrupts_restore(ipl);
  202. }
  203.  
  204. void ipc_notif_free_callback(void* notif)
  205. {
  206.     code_free(((ipc_notif_cfg_t *)notif)->code);
  207.     free(notif);
  208.  
  209. }
  210.  
  211. /** Register an answerbox as a receiving end for IRQ notifications.
  212.  *
  213.  * @param box Receiving answerbox.
  214.  * @param inr IRQ number.
  215.  * @param devno Device number.
  216.  * @param method Method to be associated with the notification.
  217.  * @param ucode Uspace pointer to top-half pseudocode.
  218.  *
  219.  * @return EBADMEM, ENOENT or EEXISTS on failure or 0 on success.
  220.  */
  221. int ipc_irq_register(answerbox_t *box, inr_t inr, devno_t devno, unative_t method, irq_code_t *ucode)
  222. {
  223.     ipl_t ipl;
  224.     irq_code_t *code;
  225.     irq_t *irq;
  226.     ipc_notif_cfg_t *new_notify, *old_notify;
  227.  
  228.     if (ucode) {
  229.         code = code_from_uspace(ucode);
  230.         if (!code)
  231.             return EBADMEM;
  232.     } else
  233.         code = NULL;
  234.  
  235.     ipl = interrupts_disable();
  236.     irq = irq_find_and_lock(inr, devno);
  237.     if (!irq) {
  238.         interrupts_restore(ipl);
  239.         code_free(code);
  240.         return ENOENT;
  241.     }
  242.    
  243.     if (rcu_dereference_pointer(irq->notif_cfg).answerbox) {
  244.         spinlock_unlock(&irq->lock);
  245.         interrupts_restore(ipl);
  246.         code_free(code);
  247.         return EEXISTS;
  248.     }
  249.     new_notify = malloc(sizeof(ipc_notif_cfg_t),0);
  250.     new_notify->notify = true;
  251.     new_notify->answerbox = box;
  252.     new_notify->method = method;
  253.     new_notify->code = code;
  254.     new_notify->counter = 0;
  255.     copy_link_rcu(&irq->notif_cfg->link, &new_notify->link);
  256.     old_notify = irq->notif_cfg;
  257.     rcu_assign_pointer(irq->notif_cfg, new_notify);
  258.     rcu_sync_callback_normal_alloc(&ipc_notif_free_callback, old_notify);
  259.  
  260.     spinlock_lock(&box->irq_lock);
  261.     list_append_rcu(&new_notify->link, &box->irq_head);
  262.     spinlock_unlock(&box->irq_lock);
  263.  
  264.     spinlock_unlock(&irq->lock);
  265.     interrupts_restore(ipl);
  266.  
  267.     return 0;
  268. }
  269.  
  270. /** Add call to proper answerbox queue.
  271.  *
  272.  * Assume irq->lock is locked.
  273.  *
  274.  */
  275. static void send_call(irq_t *irq, call_t *call)
  276. {
  277.     spinlock_lock(&rcu_dereference_pointer(irq->notif_cfg).answerbox->irq_lock);
  278.     list_append_rcu(&call->link, &rcu_dereference_pointer(irq->notif_cfg).answerbox->irq_notifs);
  279.     spinlock_unlock(&rcu_dereference_pointer(irq->notif_cfg).answerbox->irq_lock);
  280.        
  281.     waitq_wakeup(&rcu_dereference_pointer(irq->notif_cfg).answerbox->wq, WAKEUP_FIRST);
  282. }
  283.  
  284. /** Send notification message
  285.  *
  286.  */
  287. void ipc_irq_send_msg(irq_t *irq, unative_t a1, unative_t a2, unative_t a3)
  288. {
  289.     call_t *call;
  290.  
  291.     spinlock_lock(&irq->lock);
  292.  
  293.     if (rcu_dereference_pointer(irq->notif_cfg).answerbox) {
  294.         call = ipc_call_alloc(FRAME_ATOMIC);
  295.         if (!call) {
  296.             spinlock_unlock(&irq->lock);
  297.             return;
  298.         }
  299.         call->flags |= IPC_CALL_NOTIF;
  300.         IPC_SET_METHOD(call->data, rcu_dereference_pointer(irq->notif_cfg).method);
  301.         IPC_SET_ARG1(call->data, a1);
  302.         IPC_SET_ARG2(call->data, a2);
  303.         IPC_SET_ARG3(call->data, a3);
  304.         /* Put a counter to the message */
  305.         call->priv = ++rcu_dereference_pointer(irq->notif_cfg).counter;
  306.        
  307.         send_call(irq, call);
  308.     }
  309.     spinlock_unlock(&irq->lock);
  310. }
  311.  
  312. /** Notify task that an irq had occurred.
  313.  *
  314.  * We expect interrupts to be disabled and the irq->lock already held.
  315.  */
  316. void ipc_irq_send_notif(irq_t *irq)
  317. {
  318.     call_t *call;
  319.  
  320.     ASSERT(irq);
  321.  
  322.     if (rcu_dereference_pointer(irq->notif_cfg).answerbox) {
  323.         call = ipc_call_alloc(FRAME_ATOMIC);
  324.         if (!call) {
  325.             return;
  326.         }
  327.         call->flags |= IPC_CALL_NOTIF;
  328.         /* Put a counter to the message */
  329.         call->priv = ++rcu_dereference_pointer(irq->notif_cfg).counter;
  330.         /* Set up args */
  331.         IPC_SET_METHOD(call->data, rcu_dereference_pointer(irq->notif_cfg).method);
  332.  
  333.         /* Execute code to handle irq */
  334.         code_execute(call, rcu_dereference_pointer(irq->notif_cfg).code);
  335.        
  336.         send_call(irq, call);
  337.     }
  338. }
  339.  
  340. /** Disconnect all IRQ notifications from an answerbox.
  341.  *
  342.  * This function is effective because the answerbox contains
  343.  * list of all irq_t structures that are registered to
  344.  * send notifications to it.
  345.  *
  346.  * @param box Answerbox for which we want to carry out the cleanup.
  347.  */
  348. void ipc_irq_cleanup(answerbox_t *box)
  349. {
  350.     ipl_t ipl;
  351.     ipc_notif_cfg_t *new_notify, *old_notify;
  352.    
  353. loop:
  354.     ipl = interrupts_disable();
  355.     spinlock_lock(&box->irq_lock);
  356.    
  357.     while (box->irq_head.next != &box->irq_head) {
  358.         link_t *cur = box->irq_head.next;
  359.         irq_t *irq;
  360.         DEADLOCK_PROBE_INIT(p_irqlock);
  361.        
  362.         irq = list_get_instance(cur, irq_t, notif_cfg->link);
  363.         if (!spinlock_trylock(&irq->lock)) {
  364.             /*
  365.              * Avoid deadlock by trying again.
  366.              */
  367.             spinlock_unlock(&box->irq_lock);
  368.             interrupts_restore(ipl);
  369.             DEADLOCK_PROBE(p_irqlock, DEADLOCK_THRESHOLD);
  370.             goto loop;
  371.         }
  372.        
  373.         ASSERT(irq->notif_cfg->answerbox == box);
  374.        
  375.         list_remove_rcu(&irq->notif_cfg->link);
  376.        
  377.         new_notify = malloc(sizeof(ipc_notif_cfg_t),0);
  378.         new_notify->notify = false;
  379.         new_notify->answerbox = NULL;
  380.         new_notify->method = 0;
  381.         new_notify->code = NULL;
  382.         new_notify->counter = 0;
  383.         copy_link_rcu(&irq->notif_cfg->link, &new_notify->link);
  384.         old_notify = irq->notif_cfg;
  385.         rcu_assign_pointer(irq->notif_cfg, new_notify);
  386.         rcu_sync_callback_normal_alloc(&ipc_notif_free_callback, old_notify);
  387.  
  388.        
  389.         spinlock_unlock(&irq->lock);
  390.     }
  391.    
  392.     spinlock_unlock(&box->irq_lock);
  393.     interrupts_restore(ipl);
  394. }
  395.  
  396. /** @}
  397.  */
  398.