中断控制器(GIC)(下)

简介: 中断控制器(GIC)(下)

ARM中断架构

中断也是异常模式的一种,当外设触发中断时,处理器会切换到特定的异常模式进行处理,而这部分代码都是架构相关的;ARM64的代码位于arch/arm64/kernel/entry.S。

ARM64处理器有四个异常级别Exception Level:0~3,EL0级对应用户态程序,EL1级对应操作系统内核态,EL2级对应Hypervisor,EL3级对应Secure Monitor;

异常触发时,处理器进行切换,并且跳转到异常向量表开始执行,针对中断异常,最终会跳转到irq_handler中;

中断触发,处理器去异常向量表找到对应的入口,比如EL0的中断跳转到el0_irq处,EL1则跳转到el1_irq处;

在GIC驱动中,会调用set_handle_irq接口来设置handle_arch_irq的函数指针,让它指向gic_handle_irq,因此中断触发的时候会跳转到gic_handle_irq处执行;gic_handle_irq函数处理时,分为两种情况,一种是外设触发的中断,硬件中断号在16 ~ 1020之间,一种是软件触发的中断,用于处理器之间的交互,硬件中断号在16以内;

外设触发中断后,根据irq domain去查找对应的Linux IRQ中断号,进而得到中断描述符irq_desc,最终也就能调用到外设的中断处理函数了;

gic_of_init()

static int __init
gic_of_init(struct device_node *node, struct device_node *parent)
{
  void __iomem *cpu_base;
  void __iomem *dist_base;
  u32 percpu_offset;
  int irq;
  if (WARN_ON(!node))
    return -ENODEV;[]
  dist_base = of_iomap(node, 0);//映射GIC Distributor的寄存器地址空间
  WARN(!dist_base, "unable to map gic dist registers\n");
  cpu_base = of_iomap(node, 1);//映射GIC CPU interface的寄存器地址空间
  WARN(!cpu_base, "unable to map gic cpu registers\n");
  if (of_property_read_u32(node, "cpu-offset", &percpu_offset))//处理cpu-offset属性。
    percpu_offset = 0;
  gic_init_bases(gic_cnt, -1, dist_base, cpu_base, percpu_offset, node);//主处理过程,后面详述 
  if (!gic_cnt)
    gic_init_physaddr(node);//对于不支持big.LITTLE switcher(CONFIG_BL_SWITCHER)的系统,该函数为空。
  if (parent) {//处理interrupt级联 
    irq = irq_of_parse_and_map(node, 0);//解析second GIC的interrupts属性,并进行mapping,返回IRQ number 
    gic_cascade_irq(gic_cnt, irq);
  }
  if (IS_ENABLED(CONFIG_ARM_GIC_V2M))
    gicv2m_of_init(node, gic_data[gic_cnt].domain);
  gic_cnt++;
  return 0;
}

gic_init_bases()函数

void __init gic_init_bases(unsigned int gic_nr, int irq_start,
         void __iomem *dist_base, void __iomem *cpu_base,
         u32 percpu_offset, struct device_node *node)
{
  irq_hw_number_t hwirq_base;
  struct gic_chip_data *gic;
  int gic_irqs, irq_base, i;
  BUG_ON(gic_nr >= MAX_GIC_NR);
  gic = &gic_data[gic_nr];
#ifdef CONFIG_GIC_NON_BANKED
  if (percpu_offset) { /* Frankein-GIC without banked registers... */
    unsigned int cpu;
    gic->dist_base.percpu_base = alloc_percpu(void __iomem *);
    gic->cpu_base.percpu_base = alloc_percpu(void __iomem *);
    if (WARN_ON(!gic->dist_base.percpu_base ||
          !gic->cpu_base.percpu_base)) {
      free_percpu(gic->dist_base.percpu_base);
      free_percpu(gic->cpu_base.percpu_base);
      return;
    }
    for_each_possible_cpu(cpu) {
      u32 mpidr = cpu_logical_map(cpu);
      u32 core_id = MPIDR_AFFINITY_LEVEL(mpidr, 0);
      unsigned long offset = percpu_offset * core_id;
      *per_cpu_ptr(gic->dist_base.percpu_base, cpu) = dist_base + offset;
      *per_cpu_ptr(gic->cpu_base.percpu_base, cpu) = cpu_base + offset;
    }
    gic_set_base_accessor(gic, gic_get_percpu_base);
  } else
#endif
  {     /* Normal, sane GIC... */
    WARN(percpu_offset,
         "GIC_NON_BANKED not enabled, ignoring %08x offset!",
         percpu_offset);
    gic->dist_base.common_base = dist_base;
    gic->cpu_base.common_base = cpu_base;
    gic_set_base_accessor(gic, gic_get_common_base);
  }
  /*
   * Initialize the CPU interface map to all CPUs.
   * It will be refined as each CPU probes its ID.
   */
  for (i = 0; i < NR_GIC_CPU_IF; i++)
    gic_cpu_map[i] = 0xff;
  /*
   * Find out how many interrupts are supported.
   * The GIC only supports up to 1020 interrupt sources.
   */
   /*
   读取GIC 的最大支持的中断数目,从 GIC_DIST_CTR 寄存器(这是V1版本的寄存器
   名字,V2中是GICD_TYPER,Interrupt Controller Type Register,)的低五位
   ITLinesNumber获取的。如果ITLinesNumber等于N,那么最大支持的中断数目是
   32(N+1)。此外,GIC规范规定最大的中断数目不能超过1020,1020-1023是有特
   别用户的interrupt ID。
   */
  gic_irqs = readl_relaxed(gic_data_dist_base(gic) + GIC_DIST_CTR) & 0x1f;
  gic_irqs = (gic_irqs + 1) * 32;
  if (gic_irqs > 1020)
    gic_irqs = 1020;
  gic->gic_irqs = gic_irqs;
  /*
  分配一个 irq_domain 的结构,一个 irq_domain 代表了一个 GIC 控制器
  */
  if (node) {   /* DT case */
    gic->domain = irq_domain_add_linear(node, gic_irqs,
                &gic_irq_domain_hierarchy_ops,
                gic);
  } else {    /* Non-DT case */
    /*
     * For primary GICs, skip over SGIs.
     * For secondary GICs, skip over PPIs, too.
     */
    if (gic_nr == 0 && (irq_start & 31) > 0) {
      hwirq_base = 16;
      if (irq_start != -1)
        irq_start = (irq_start & ~31) + 16;
    } else {
      hwirq_base = 32;
    }
    gic_irqs -= hwirq_base; /* calculate # of irqs to allocate */
    irq_base = irq_alloc_descs(irq_start, 16, gic_irqs,
             numa_node_id());
    if (IS_ERR_VALUE(irq_base)) {
      WARN(1, "Cannot allocate irq_descs @ IRQ%d, assuming pre-allocated\n",
           irq_start);
      irq_base = irq_start;
    }
    gic->domain = irq_domain_add_legacy(node, gic_irqs, irq_base,
          hwirq_base, &gic_irq_domain_ops, gic);
  }
  if (WARN_ON(!gic->domain))
    return;
  if (gic_nr == 0) {//只对root GIC操作,因为设定callback、注册Notifier只需要一次就OK了
#ifdef CONFIG_SMP
    set_smp_cross_call(gic_raise_softirq);
    register_cpu_notifier(&gic_cpu_notifier);
#endif
    set_handle_irq(gic_handle_irq);
  }
  gic_dist_init(gic);//GIC Distributer   部分初始化
  gic_cpu_init(gic);//GIC CPU Interface 部分初始化
  gic_pm_init(gic);//GIC PM            部分初始化
}

gic_handle_irq()

static asmlinkage void __exception_irq_entry gic_handle_irq(struct pt_regs *regs)
{
  u64 irqnr;
  do {
    irqnr = gic_read_iar();//读取gic寄存器并获取hwirq中断号
    if (likely(irqnr > 15 && irqnr < 1020) || irqnr >= 8192) {
      int err;
      err = handle_domain_irq(gic_data.domain, irqnr, regs);//外设触发的中断
      if (err) {
        WARN_ONCE(true, "Unexpected interrupt received!\n");
        gic_write_eoir(irqnr);
      }
      continue;
    }
    if (irqnr < 16) {//软件触发的中断SGI
      gic_write_eoir(irqnr);
#ifdef CONFIG_SMP
      handle_IPI(irqnr, regs);//处理核间交互
#else
      WARN_ONCE(true, "Unexpected SGI received!\n");
#endif
      continue;
    }
  } while (irqnr != ICC_IAR1_EL1_SPURIOUS);
}

__handle_domain_irq()

int __handle_domain_irq(struct irq_domain *domain, unsigned int hwirq,
      bool lookup, struct pt_regs *regs)
{
  struct pt_regs *old_regs = set_irq_regs(regs);
  unsigned int irq = hwirq;
  int ret = 0;
  irq_enter();//进入中断上下文
#ifdef CONFIG_IRQ_DOMAIN
  if (lookup)
    irq = irq_find_mapping(domain, hwirq);//根据hwirq去查找linux中断号
#endif
  /*
   * Some hardware gives randomly wrong interrupts.  Rather
   * than crashing, do something sensible.
   */
  if (unlikely(!irq || irq >= nr_irqs)) {
    ack_bad_irq(irq);
    ret = -EINVAL;
  } else {
    generic_handle_irq(irq);
    /*
    根据linux中断号找到对应的中断描述符,从而执行中断服务函数
    */
  }
  irq_exit();//退出中断上下文
  set_irq_regs(old_regs);
  return ret;
}
目录
相关文章
|
3月前
|
Linux
使用funcgraph-retval和bpftrace/kprobe快速定位并解决cpu控制器无法使能的问题
使用funcgraph-retval和bpftrace/kprobe快速定位并解决cpu控制器无法使能的问题
|
5月前
|
7月前
|
芯片 索引
可编程中断控制器
可编程中断控制器
246 0
|
Linux 索引
RISC-V SiFive U54内核——CLINT中断控制器
RISC-V SiFive U54内核——CLINT中断控制器
|
传感器 调度
什么是中断系统?
一、什么是中断系统 中断系统是计算机系统中的一种机制,它允许外部设备和程序请求处理器的注意力,以便进行特定的操作。当一个中断请求被触发时,处理器会暂停当前正在执行的程序,转而执行与中断相关的程序或服务例程。中断系统可以提高计算机系统的效率和响应速度,因为它允许处理器在等待某些事件的同时执行其他任务。常见的中断包括硬件中断(例如键盘输入、鼠标移动、网络数据传输等)和软件中断(例如操作系统调度、系统调用等)。 二、中断系统的特点 中断系统具有以下特点: 1. 实时性:中断系统能够及时响应外部设备的请求,提高计算机系统的响应速度和效率。 2. 可靠性:中断系统能够保证中断请求的可靠性,确保外部设备的
288 0
|
编译器
中断的解析
中断的解析
99 0
|
Linux 索引
中断控制器(GIC)(上)
中断控制器(GIC)
491 0
|
芯片
中断系统结构及中断控制详解
本文详解了中断系统结构及中断控制。
528 0
详解中断系统
本文针对地详解了中断系统
273 0