Subversion Repositories HelenOS

Rev

Rev 3862 | 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 <arch/asm.h>
  53. #include <mm/slab.h>
  54. #include <errno.h>
  55. #include <ddi/irq.h>
  56. #include <ipc/ipc.h>
  57. #include <ipc/irq.h>
  58. #include <syscall/copy.h>
  59. #include <console/console.h>
  60. #include <print.h>
  61.  
  62. #if defined(sparc64) && defined(SUN4V)
  63. #include <arch/drivers/niagara.h>
  64. #endif
  65.  
  66. /** Execute code associated with IRQ notification.
  67.  *
  68.  * @param call      Notification call.
  69.  * @param code      Top-half pseudocode.
  70.  */
  71. static void code_execute(call_t *call, irq_code_t *code)
  72. {
  73.     unsigned int i;
  74.     unative_t dstval = 0;
  75.    
  76.     if (!code)
  77.         return;
  78.    
  79.     for (i = 0; i < code->cmdcount; i++) {
  80.         switch (code->cmds[i].cmd) {
  81.         case CMD_MEM_READ_1:
  82.             dstval = *((uint8_t *) code->cmds[i].addr);
  83.             break;
  84.         case CMD_MEM_READ_2:
  85.             dstval = *((uint16_t *) code->cmds[i].addr);
  86.             break;
  87.         case CMD_MEM_READ_4:
  88.             dstval = *((uint32_t *) code->cmds[i].addr);
  89.             break;
  90.         case CMD_MEM_READ_8:
  91.             dstval = *((uint64_t *) code->cmds[i].addr);
  92.             break;
  93.         case CMD_MEM_WRITE_1:
  94.             *((uint8_t *) code->cmds[i].addr) = code->cmds[i].value;
  95.             break;
  96.         case CMD_MEM_WRITE_2:
  97.             *((uint16_t *) code->cmds[i].addr) =
  98.                 code->cmds[i].value;
  99.             break;
  100.         case CMD_MEM_WRITE_4:
  101.             *((uint32_t *) code->cmds[i].addr) =
  102.                 code->cmds[i].value;
  103.             break;
  104.         case CMD_MEM_WRITE_8:
  105.             *((uint64_t *) code->cmds[i].addr) =
  106.                 code->cmds[i].value;
  107.             break;
  108. #if defined(ia32) || defined(amd64) || defined(ia64)
  109.         case CMD_PORT_READ_1:
  110.             dstval = inb((long) code->cmds[i].addr);
  111.             break;
  112.         case CMD_PORT_WRITE_1:
  113.             outb((long) code->cmds[i].addr, code->cmds[i].value);
  114.             break;
  115. #endif
  116. #if defined(ia64) && defined(SKI)
  117.         case CMD_IA64_GETCHAR:
  118.             dstval = _getc(&ski_uconsole);
  119.             break;
  120. #endif
  121. #if defined(sparc64) && defined(SUN4V)
  122.         case CMD_NIAGARA_GETCHAR:
  123.             dstval = niagara_getc();
  124.             break;
  125. #endif
  126. #if defined(ppc32)
  127.         case CMD_PPC32_GETCHAR:
  128.             dstval = cuda_get_scancode();
  129.             break;
  130. #endif
  131.         default:
  132.             break;
  133.         }
  134.         if (code->cmds[i].dstarg && code->cmds[i].dstarg <
  135.             IPC_CALL_LEN) {
  136.             call->data.args[code->cmds[i].dstarg] = dstval;
  137.         }
  138.     }
  139. }
  140.  
  141. /** Free top-half pseudocode.
  142.  *
  143.  * @param code      Pointer to the top-half pseudocode.
  144.  */
  145. static void code_free(irq_code_t *code)
  146. {
  147.     if (code) {
  148.         free(code->cmds);
  149.         free(code);
  150.     }
  151. }
  152.  
  153. /** Copy top-half pseudocode from userspace into the kernel.
  154.  *
  155.  * @param ucode     Userspace address of the top-half pseudocode.
  156.  *
  157.  * @return      Kernel address of the copied pseudocode.
  158.  */
  159. static irq_code_t *code_from_uspace(irq_code_t *ucode)
  160. {
  161.     irq_code_t *code;
  162.     irq_cmd_t *ucmds;
  163.     int rc;
  164.  
  165.     code = malloc(sizeof(*code), 0);
  166.     rc = copy_from_uspace(code, ucode, sizeof(*code));
  167.     if (rc != 0) {
  168.         free(code);
  169.         return NULL;
  170.     }
  171.    
  172.     if (code->cmdcount > IRQ_MAX_PROG_SIZE) {
  173.         free(code);
  174.         return NULL;
  175.     }
  176.     ucmds = code->cmds;
  177.     code->cmds = malloc(sizeof(code->cmds[0]) * code->cmdcount, 0);
  178.     rc = copy_from_uspace(code->cmds, ucmds,
  179.         sizeof(code->cmds[0]) * code->cmdcount);
  180.     if (rc != 0) {
  181.         free(code->cmds);
  182.         free(code);
  183.         return NULL;
  184.     }
  185.  
  186.     return code;
  187. }
  188.  
  189. /** Unregister task from IRQ notification.
  190.  *
  191.  * @param box       Answerbox associated with the notification.
  192.  * @param inr       IRQ number.
  193.  * @param devno     Device number.
  194.  */
  195. void ipc_irq_unregister(answerbox_t *box, inr_t inr, devno_t devno)
  196. {
  197.     ipl_t ipl;
  198.     irq_t *irq;
  199.  
  200.     ipl = interrupts_disable();
  201.     irq = irq_find_and_lock(inr, devno);
  202.     if (irq) {
  203.         if (irq->notif_cfg.answerbox == box) {
  204.             code_free(irq->notif_cfg.code);
  205.             irq->notif_cfg.notify = false;
  206.             irq->notif_cfg.answerbox = NULL;
  207.             irq->notif_cfg.code = NULL;
  208.             irq->notif_cfg.method = 0;
  209.             irq->notif_cfg.counter = 0;
  210.  
  211.             spinlock_lock(&box->irq_lock);
  212.             list_remove(&irq->notif_cfg.link);
  213.             spinlock_unlock(&box->irq_lock);
  214.            
  215.             spinlock_unlock(&irq->lock);
  216.         }
  217.     }
  218.     interrupts_restore(ipl);
  219. }
  220.  
  221. /** Register an answerbox as a receiving end for IRQ notifications.
  222.  *
  223.  * @param box       Receiving answerbox.
  224.  * @param inr       IRQ number.
  225.  * @param devno     Device number.
  226.  * @param method    Method to be associated with the notification.
  227.  * @param ucode     Uspace pointer to top-half pseudocode.
  228.  *
  229.  * @return      EBADMEM, ENOENT or EEXISTS on failure or 0 on success.
  230.  */
  231. int ipc_irq_register(answerbox_t *box, inr_t inr, devno_t devno,
  232.     unative_t method, irq_code_t *ucode)
  233. {
  234.     ipl_t ipl;
  235.     irq_code_t *code;
  236.     irq_t *irq;
  237.  
  238.     if (ucode) {
  239.         code = code_from_uspace(ucode);
  240.         if (!code)
  241.             return EBADMEM;
  242.     } else {
  243.         code = NULL;
  244.     }
  245.  
  246.     ipl = interrupts_disable();
  247.     irq = irq_find_and_lock(inr, devno);
  248.     if (!irq) {
  249.         interrupts_restore(ipl);
  250.         code_free(code);
  251.         return ENOENT;
  252.     }
  253.    
  254.     if (irq->notif_cfg.answerbox) {
  255.         spinlock_unlock(&irq->lock);
  256.         interrupts_restore(ipl);
  257.         code_free(code);
  258.         return EEXISTS;
  259.     }
  260.    
  261.     irq->notif_cfg.notify = true;
  262.     irq->notif_cfg.answerbox = box;
  263.     irq->notif_cfg.method = method;
  264.     irq->notif_cfg.code = code;
  265.     irq->notif_cfg.counter = 0;
  266.  
  267.     spinlock_lock(&box->irq_lock);
  268.     list_append(&irq->notif_cfg.link, &box->irq_head);
  269.     spinlock_unlock(&box->irq_lock);
  270.  
  271.     spinlock_unlock(&irq->lock);
  272.     interrupts_restore(ipl);
  273.  
  274.     return 0;
  275. }
  276.  
  277. /** Add a call to the proper answerbox queue.
  278.  *
  279.  * Assume irq->lock is locked.
  280.  *
  281.  * @param irq       IRQ structure referencing the target answerbox.
  282.  * @param call      IRQ notification call.
  283.  */
  284. static void send_call(irq_t *irq, call_t *call)
  285. {
  286.     spinlock_lock(&irq->notif_cfg.answerbox->irq_lock);
  287.     list_append(&call->link, &irq->notif_cfg.answerbox->irq_notifs);
  288.     spinlock_unlock(&irq->notif_cfg.answerbox->irq_lock);
  289.        
  290.     waitq_wakeup(&irq->notif_cfg.answerbox->wq, WAKEUP_FIRST);
  291. }
  292.  
  293. /** Send notification message.
  294.  *
  295.  * @param irq       IRQ structure.
  296.  * @param a1        Driver-specific payload argument.
  297.  * @param a2        Driver-specific payload argument.
  298.  * @param a3        Driver-specific payload argument.
  299.  * @param a4        Driver-specific payload argument.
  300.  * @param a5        Driver-specific payload argument.
  301.  */
  302. void ipc_irq_send_msg(irq_t *irq, unative_t a1, unative_t a2, unative_t a3,
  303.     unative_t a4, unative_t a5)
  304. {
  305.     call_t *call;
  306.  
  307.     spinlock_lock(&irq->lock);
  308.  
  309.     if (irq->notif_cfg.answerbox) {
  310.         call = ipc_call_alloc(FRAME_ATOMIC);
  311.         if (!call) {
  312.             spinlock_unlock(&irq->lock);
  313.             return;
  314.         }
  315.         call->flags |= IPC_CALL_NOTIF;
  316.         IPC_SET_METHOD(call->data, irq->notif_cfg.method);
  317.         IPC_SET_ARG1(call->data, a1);
  318.         IPC_SET_ARG2(call->data, a2);
  319.         IPC_SET_ARG3(call->data, a3);
  320.         IPC_SET_ARG4(call->data, a4);
  321.         IPC_SET_ARG5(call->data, a5);
  322.         /* Put a counter to the message */
  323.         call->priv = ++irq->notif_cfg.counter;
  324.        
  325.         send_call(irq, call);
  326.     }
  327.     spinlock_unlock(&irq->lock);
  328. }
  329.  
  330. /** Notify a task that an IRQ had occurred.
  331.  *
  332.  * We expect interrupts to be disabled and the irq->lock already held.
  333.  *
  334.  * @param irq       IRQ structure.
  335.  */
  336. void ipc_irq_send_notif(irq_t *irq)
  337. {
  338.     call_t *call;
  339.  
  340.     ASSERT(irq);
  341.  
  342.     if (irq->notif_cfg.answerbox) {
  343.         call = ipc_call_alloc(FRAME_ATOMIC);
  344.         if (!call) {
  345.             return;
  346.         }
  347.         call->flags |= IPC_CALL_NOTIF;
  348.         /* Put a counter to the message */
  349.         call->priv = ++irq->notif_cfg.counter;
  350.         /* Set up args */
  351.         IPC_SET_METHOD(call->data, irq->notif_cfg.method);
  352.  
  353.         /* Execute code to handle irq */
  354.         code_execute(call, irq->notif_cfg.code);
  355.        
  356.         send_call(irq, call);
  357.     }
  358. }
  359.  
  360. /** Disconnect all IRQ notifications from an answerbox.
  361.  *
  362.  * This function is effective because the answerbox contains
  363.  * list of all irq_t structures that are registered to
  364.  * send notifications to it.
  365.  *
  366.  * @param box       Answerbox for which we want to carry out the cleanup.
  367.  */
  368. void ipc_irq_cleanup(answerbox_t *box)
  369. {
  370.     ipl_t ipl;
  371.    
  372. loop:
  373.     ipl = interrupts_disable();
  374.     spinlock_lock(&box->irq_lock);
  375.    
  376.     while (box->irq_head.next != &box->irq_head) {
  377.         link_t *cur = box->irq_head.next;
  378.         irq_t *irq;
  379.         DEADLOCK_PROBE_INIT(p_irqlock);
  380.        
  381.         irq = list_get_instance(cur, irq_t, notif_cfg.link);
  382.         if (!spinlock_trylock(&irq->lock)) {
  383.             /*
  384.              * Avoid deadlock by trying again.
  385.              */
  386.             spinlock_unlock(&box->irq_lock);
  387.             interrupts_restore(ipl);
  388.             DEADLOCK_PROBE(p_irqlock, DEADLOCK_THRESHOLD);
  389.             goto loop;
  390.         }
  391.        
  392.         ASSERT(irq->notif_cfg.answerbox == box);
  393.        
  394.         list_remove(&irq->notif_cfg.link);
  395.        
  396.         /*
  397.          * Don't forget to free any top-half pseudocode.
  398.          */
  399.         code_free(irq->notif_cfg.code);
  400.        
  401.         irq->notif_cfg.notify = false;
  402.         irq->notif_cfg.answerbox = NULL;
  403.         irq->notif_cfg.code = NULL;
  404.         irq->notif_cfg.method = 0;
  405.         irq->notif_cfg.counter = 0;
  406.  
  407.         spinlock_unlock(&irq->lock);
  408.     }
  409.    
  410.     spinlock_unlock(&box->irq_lock);
  411.     interrupts_restore(ipl);
  412. }
  413.  
  414. /** @}
  415.  */
  416.