baron · 3月27日 · 四川

optee中添加一个中断以及底层代码的相关解读

快速连接

👉👉👉【精选】ARMv8/ARMv9架构入门到精通-目录 👈👈👈


 title=

1、optee中注册一个中断

其实呢,我们在代码全局搜索<font color=red size=4>itr_add</font>是能搜索到好多示例的
在这里插入图片描述
我们以optee_os/core/arch/arm/plat-stm32mp1/plat_tzc400.c代码为例,进行一个讲解:

(1)、定义一个struct itr_handler结构体,.it为硬件中断号,.handler执行中断处理函数

(optee_os/core/arch/arm/plat-stm32mp1/plat_tzc400.c)

  static enum itr_return tzc_it_handler(struct itr_handler *handler __unused)
  {
      EMSG("TZC permission failure");
      tzc_fail_dump();
  
      if (IS_ENABLED(CFG_STM32MP_PANIC_ON_TZC_PERM_VIOLATION))
          panic();
      else
          tzc_int_clear();
  
      return ITRR_HANDLED;
  }
  
  static struct itr_handler tzc_itr_handler = {
      .it = STM32MP1_IRQ_TZC,
      .handler = tzc_it_handler,
  };
  
  
  DECLARE_KEEP_PAGER(tzc_itr_handler);

(2)、在初始化的代码中,注册该中断和enable该中断

itr_add(&tzc_itr_handler);
itr_enable(tzc_itr_handler.it);

至此一个中断示例讲完了,就是这么简单。

2、optee中断的处理流程

在这里插入图片描述
(1)、有关itr_core_handler

在中间层实现了一个名叫itr_core_handler虚函数,如果平台未实现该函数,则发生中断时会调用此处函数。此处逻辑就是panic()

(optee_os/core/kernel/interrupt.c)

134  /* This function is supposed to be overridden in platform specific code */
135  void __weak __noreturn itr_core_handler(void)
136  {
137      panic("Secure interrupt handler not defined");
138  }

(2)、gic层的处理

  • 读gicd寄存器,读取中断号
  • 进行中断处理
  • 写EOIR(End of Interrupt Registers),将该中断号的中断置为inactive
(optee_os/core/drivers/gic.c)

 void gic_it_handle(struct gic_data *gd)
 {
     uint32_t iar;
     uint32_t id;
 
     iar = gic_read_iar(gd);
     id = iar & GICC_IAR_IT_ID_MASK;
 
     if (id <= gd->max_it)
         itr_handle(id);
     else
         DMSG("ignoring interrupt %" PRIu32, id);
 
     gic_write_eoir(gd, iar);
 }

有关EOIR寄存器的介绍,可参见gic文档
在这里插入图片描述

小小小总结一下: optee os中的中断管理,不算复杂, 只有一个硬件中断号,也不需要给中断号做map。不像Linux抽象出来irq domain、中断级联、硬件中断号和软件中断号需要进行map

3、optee中断的API介绍(或叫中间层)

  • itr_handle : 回调driver中注册的handler函数。itr_handle维护者一个LIST,将中断号和driver中的handler结构体绑定
  • itr_add : 注册一个中断
  • itr_enable : enable一个中断,其实就是修改gicd寄存器, <font color=red size=3>注:这是Linux Kernel没有的API吧,optee胜一局</font>
  • itr_disable :disable一个中断,其实就是修改gicd寄存器, <font color=red size=3>注:这是Linux Kernel没有的API吧,optee胜一局</font>
  • itr_raise_sgi : 产生一个SGI中断
  • itr_set_affinity :设置中断affinity,也就是指定一个中断,发生中断时routing到哪颗Core上。 注:这是Linux Kernel没有的API吧,optee胜一局

    (optee_os/core/kernel/interrupt.c)
    
    
     void itr_init(struct itr_chip *chip)
     {
       itr_chip = chip;
     }
     
     #ifdef CFG_DT
     int dt_get_irq(const void *fdt, int node)
     {
       const uint32_t *prop = NULL;
       int len = 0;
       int it_num = DT_INFO_INVALID_INTERRUPT;
     
       if (!itr_chip || !itr_chip->dt_get_irq)
           return it_num;
     
       prop = fdt_getprop(fdt, node, "interrupts", &len);
       if (!prop)
           return it_num;
     
       return itr_chip->dt_get_irq(prop, len);
     }
     #endif
     
     void itr_handle(size_t it)
     {
       struct itr_handler *h = NULL;
       bool was_handled = false;
     
       SLIST_FOREACH(h, &handlers, link) {
           if (h->it == it) {
               if (h->handler(h) == ITRR_HANDLED)
                   was_handled = true;
               else if (!(h->flags & ITRF_SHARED))
                   break;
           }
       }
     
       if (!was_handled) {
           EMSG("Disabling unhandled interrupt %zu", it);
           itr_chip->ops->disable(itr_chip, it);
       }
     }
     
     struct itr_handler *itr_alloc_add(size_t it, itr_handler_t handler,
                     uint32_t flags, void *data)
     {
       struct itr_handler *hdl = calloc(1, sizeof(*hdl));
     
       if (hdl) {
           hdl->it = it;
           hdl->handler = handler;
           hdl->flags = flags;
           hdl->data = data;
           itr_add(hdl);
       }
     
       return hdl;
     }
     
     void itr_free(struct itr_handler *hdl)
     {
       if (!hdl)
           return;
     
       itr_chip->ops->disable(itr_chip, hdl->it);
     
       SLIST_REMOVE(&handlers, hdl, itr_handler, link);
       free(hdl);
     }
     
     void itr_add(struct itr_handler *h)
     {
       struct itr_handler __maybe_unused *hdl = NULL;
     
        SLIST_FOREACH(hdl, &handlers, link)
            if (hdl->it == h->it)
                assert((hdl->flags & ITRF_SHARED) &&
                       (h->flags & ITRF_SHARED));
    
        itr_chip->ops->add(itr_chip, h->it, h->flags);
        SLIST_INSERT_HEAD(&handlers, h, link);
    }
    
    void itr_enable(size_t it)
    {
        itr_chip->ops->enable(itr_chip, it);
    }
    
    void itr_disable(size_t it)
    {
        itr_chip->ops->disable(itr_chip, it);
    }
    
    void itr_raise_pi(size_t it)
    {
        itr_chip->ops->raise_pi(itr_chip, it);
    }
    
    void itr_raise_sgi(size_t it, uint8_t cpu_mask)
    {
        itr_chip->ops->raise_sgi(itr_chip, it, cpu_mask);
    }
    
    void itr_set_affinity(size_t it, uint8_t cpu_mask)
    {
        itr_chip->ops->set_affinity(itr_chip, it, cpu_mask);
    }
    
    /* This function is supposed to be overridden in platform specific code */
    void __weak __noreturn itr_core_handler(void)
    {
        panic("Secure interrupt handler not defined");
    }

4、注册要给中断时底层都做了什么(itr_add做了什么?)

我们知道itr_add()调用了gic_op_add()函数,那么我们就从gic_op_add()看起
在这里插入图片描述

(1)、gic_op_add做了哪些事情呢?

  • gic_it_add:向gic中注册中断
  • gic_it_set_cpu_mask:设置该中断的cpu mask,其实就是设置哪些cpu可以接受该中断,这里设置的是any online core
  • gic_it_set_prio : 设置中断优先级,中断优先级是0x00-0xFF,数字越小优先级越高,这里设置的是0X01
(optee_os/core/drivers/gic.c)

 static void gic_op_add(struct itr_chip *chip, size_t it,
                uint32_t flags __unused)
 {
     struct gic_data *gd = container_of(chip, struct gic_data, chip);
 
     if (it > gd->max_it)
         panic();
 
     gic_it_add(gd, it);
     /* Set the CPU mask to deliver interrupts to any online core */
     gic_it_set_cpu_mask(gd, it, 0xff);
     gic_it_set_prio(gd, it, 0x1);
 }

(2)、gic_it_add向gic中注册中断都干了什么事情呢

  • Disable the interrupt
  • Make it non-pending
  • Assign it to group0
  • Assign it to group1S
(optee_os/core/drivers/gic.c)

 static void gic_it_add(struct gic_data *gd, size_t it)
 {
     size_t idx = it / NUM_INTS_PER_REG;
     uint32_t mask = 1 << (it % NUM_INTS_PER_REG);
 
     /* Disable the interrupt */
     io_write32(gd->gicd_base + GICD_ICENABLER(idx), mask);
     /* Make it non-pending */
     io_write32(gd->gicd_base + GICD_ICPENDR(idx), mask);
     /* Assign it to group0 */
     io_clrbits32(gd->gicd_base + GICD_IGROUPR(idx), mask);
 #if defined(CFG_ARM_GICV3)
     /* Assign it to group1S */
     io_setbits32(gd->gicd_base + GICD_IGROUPMODR(idx), mask);
 #endif
 }

我们以gicv3为例,这里也许你就有疑问了,为啥要Assign it to group0呢? 其实不要看这里的注释,需要在gicv3/gicv3的文档中可以找到答案:

  • 如果是gicv2,那么操作GICD_IGROUPR寄存器,给相关bit清0,就是将该中断配置成group0,gicv2中group0是安全中断,这符合我们的要求。
  • 如果是gicv3,那么先操作GICD_IGROUPR寄存器,给相关bit清0,清零该bit后对应了两种结果:
    将该中断置成了group0或group1s. 那么到底是用哪种结果呢? 那时gic硬件在处理该中断时,还会再看GICD_IGROUPMODR寄存器,如果对应的bit为1, 那么该中断就是group1s,否则是group0。
    所以呢,如果是gicv3,这里结合GICD_IGROUPR和GICD_IGROUPMODR就能将该中断号配置成group1s

GICD_IGROUPMODR对应表格中的Group modifier bit
GICD_IGROUPR对应表格中的Group state bit
在这里插入图片描述


关注"Arm精选"公众号,备注进ARM交流讨论区。
图片1.png

推荐阅读
关注数
9466
内容数
212
以易懂、渐进、有序的方式,深入探讨ARMv8/ARMv9架构的核心概念。我们将从基础知识开始,逐步深入,覆盖最新的架构,不再纠缠于过时技术。本系列内容包含但不限于ARM基础、SOC芯片基础、Trustzone、gic、异常和中断、AMBA、Cache、MMU等内容,并将持续更新。
目录
极术微信服务号
关注极术微信号
实时接收点赞提醒和评论通知
安谋科技学堂公众号
关注安谋科技学堂
实时获取安谋科技及 Arm 教学资源
安谋科技招聘公众号
关注安谋科技招聘
实时获取安谋科技中国职位信息