快速连接
👉👉👉【精选】ARMv8/ARMv9架构入门到精通-目录 👈👈👈
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交流讨论区。