text: /*
   * Copyright (c) 2007, 2008, 2009, 2010, 2011, ETH Zurich.
   * All rights reserved.
   *
   * This file is distributed under the terms in the attached LICENSE file.
   * If you do not find this file, copies can be found by writing to:
   * ETH Zurich D-INFK, Universitaetstrasse 6, CH-8092 Zurich. Attn: Systems Group.
   */
  #include <kernel.h>
  #include <stdio.h>
  #include <string.h>
  #include <arch/arm/arm.h>
  #include <arch/arm/platform.h>
  #include <barrelfish_kpi/lmp.h>
  #include <barrelfish_kpi/syscalls.h>
  #include <barrelfish_kpi/sys_debug.h>
  
  #include <irq.h>
  
  #include <paging_kernel_arch.h>
  #include <dispatch.h>
  #include <exec.h>
  #include <stdio.h>
  #include <syscall.h>
  #include <arch/arm/syscall_arm.h>
  #include <kcb.h>
  
  /**
   * \brief User-space IRQ dispatch table.
   *
   * This is essentially a big CNode holding #NDISPATCH capability
   * entries to local endpoints of user-space applications listening to
   * the interrupts.
   */
  static struct cte irq_dispatch[NDISPATCH];
  
  errval_t irq_table_set(unsigned int nidt, capaddr_t endpoint)
  {
      errval_t err;
      struct cte *recv;
  
      err = caps_lookup_slot(&dcb_current->cspace.cap, endpoint,
                             2, &recv, CAPRIGHTS_WRITE);
      if (err_is_fail(err)) {
          return err_push(err, SYS_ERR_IRQ_LOOKUP);
      }
  
      assert(recv != NULL);
  
      // Return w/error if cap is not an endpoint
      if (recv->cap.type != ObjType_EndPointLMP) {
          return SYS_ERR_IRQ_NOT_ENDPOINT;
      }
  
      // Return w/error if no listener on endpoint
      if (recv->cap.u.endpointlmp.listener == NULL) {
          return SYS_ERR_IRQ_NO_LISTENER;
      }
  
      if (nidt < NDISPATCH) {
          // check that we don't overwrite someone else's handler
          if (irq_dispatch[nidt].cap.type != ObjType_Null) {
              printf("kernel: replacing handler for IRQ %d\n", nidt);
          }
          err = caps_copy_to_cte(&irq_dispatch[nidt], recv, false, 0, 0);
  
          // Correct interrupt forwarding by the distributor must be ensured
          // in userspace.
  
          return err;
      }
  
      return SYS_ERR_IRQ_INVALID;
  }
  
  errval_t irq_connect(struct capability *irq_dest, capaddr_t endpoint) {
      assert(irq_dest->type == ObjType_IRQDest);
      return irq_table_set(irq_dest->u.irqdest.vector, endpoint);
  }
  
  errval_t irq_table_delete(unsigned int nidt)
  {
      if (nidt < NDISPATCH) {
          irq_dispatch[nidt].cap.type = ObjType_Null;
  
          /* todo: gic disable irq */
  
          return SYS_ERR_OK;
      }
      return SYS_ERR_IRQ_INVALID;
  }
  
  errval_t irq_table_alloc_dest_cap(uint8_t dcn_level, capaddr_t dcn,
                                    capaddr_t out_cap_addr, int vec_hint)
  {
      errval_t err;
      if(vec_hint < 0){
          printk(LOG_WARN, "irq: vec_hint must be provided on ARM\n", vec_hint);
          return SYS_ERR_IRQ_INVALID;
      }
      if(vec_hint >= NDISPATCH){
          printk(LOG_WARN, "irq: vec_hint (%d) invalid\n", vec_hint);
          return SYS_ERR_IRQ_INVALID;
      }
  
      // TODO: Keep track of allocations
      struct cte * cn;
      err = caps_lookup_slot(&dcb_current->cspace.cap, dcn, dcn_level,
                             &cn, CAPRIGHTS_WRITE);
      if(err_is_fail(err)){
          return err;
      }
  
      struct cte out_cap;
      memset(&out_cap, 0, sizeof(struct cte));
      out_cap.cap.type = ObjType_IRQDest;
      out_cap.cap.u.irqdest.cpu = my_core_id;
      out_cap.cap.u.irqdest.vector = vec_hint;
      caps_copy_to_cnode(cn, out_cap_addr, &out_cap, 0, 0, 0);
      return  SYS_ERR_OK;
  }
  
  errval_t irq_table_notify_domains(struct kcb *kcb)
  {
      uintptr_t msg[] = { 1 };
      for (int i = 0; i < NDISPATCH; i++) {
          if (kcb->irq_dispatch[i].cap.type == ObjType_EndPointLMP) {
              struct capability *cap = &kcb->irq_dispatch[i].cap;
              // 1 word message as notification
              errval_t err = lmp_deliver_payload(cap, NULL, msg, 1, false, false);
              if (err_is_fail(err)) {
                  if (err_no(err) == SYS_ERR_LMP_BUF_OVERFLOW) {
                      struct dispatcher_shared_generic *disp =
                          get_dispatcher_shared_generic(cap->u.endpointlmp.listener->disp);
                      printk(LOG_DEBUG, "%.*s: IRQ message buffer overflow\n",
                              DISP_NAME_LEN, disp->name);
                  } else {
                      printk(LOG_ERR, "Unexpected error delivering IRQ\n");
                  }
              }
          }
          kcb->irq_dispatch[i].cap.type = ObjType_Null;
      }
      return SYS_ERR_OK;
  }
  
  /**
   * \brief Send interrupt notification to user-space listener.
   *
   * Sends an interrupt notification IDC to a local endpoint that
   * listens for IRQ notifications.
   *
   * \param irq   IRQ# to send in notification.
   */
  void send_user_interrupt(int irq)
  {
      assert(irq >= 0 && irq < NDISPATCH);
      struct capability *cap = &irq_dispatch[irq].cap;
  
      // Return on null cap (unhandled interrupt)
      if (cap->type == ObjType_Null) {
          printk(LOG_WARN, "unhandled IRQ %d\n", irq);
          return;
      }
  
      // Otherwise, cap needs to be an endpoint
      assert(cap->type == ObjType_EndPointLMP);
      errval_t err = lmp_deliver_notification(cap);
      if (err_is_fail(err)) {
          if (err_no(err) == SYS_ERR_LMP_BUF_OVERFLOW) {
              struct dispatcher_shared_generic *disp =
                      get_dispatcher_shared_generic(
                              cap->u.endpointlmp.listener->disp);
              printk(LOG_DEBUG, "%.*s: IRQ message buffer overflow\n",
                      DISP_NAME_LEN, disp->name);
          } else {
              printk(LOG_ERR, "Unexpected error delivering IRQ\n");
          }
      }
  
  #ifdef SCHEDULER_RR
      /* XXX: run the handler dispatcher immediately
       * we shouldn't do this (we should let the scheduler decide), but because
       * our default scheduler is braindead, this is a quick hack to make sure
       * that mostly-sane things happen
       */
      dispatch(cap->u.endpointlmp.listener);
  #else
      dispatch(schedule());
  #endif
  }
  