Kernel 5.x Riscv 中断机制分析

1 中断控制器初始化过程

arch/riscv/kernel/irq.c 
void __init init_IRQ(void)
 20 {
 21     irqchip_init();
 22     if (!handle_arch_irq)
 23         panic("No interrupt controller found.");
 24 }

调用irqchip_init函数初始化中断

/drivers/irqchip/irqchip.c
 29 void __init irqchip_init(void)
 30 {
 31     of_irq_init(__irqchip_of_table);
 32     acpi_probe_device_table(irqchip);
 33 }

1.1 __irqchip_of_table匹配

1 设备树源文件

如sifive的设备数源码如下所示:

arch/riscv/boot/dts/sifive/fu540-c000.dtsi
plic0: interrupt-controller@c000000 {
143             #interrupt-cells = <1>;
144             compatible = "sifive,plic-1.0.0";
145             reg = <0x0 0xc000000 0x0 0x4000000>;
146             riscv,ndev = <53>;
147             interrupt-controller;
148             interrupts-extended = <
149                 &cpu0_intc 0xffffffff
150                 &cpu1_intc 0xffffffff &cpu1_intc 9
151                 &cpu2_intc 0xffffffff &cpu2_intc 9
152                 &cpu3_intc 0xffffffff &cpu3_intc 9
153                 &cpu4_intc 0xffffffff &cpu4_intc 9>;

2 中断控制匹配表

我们把__irqchip_of_table称为中断控制器匹配表

drivers/irqchip/irq-sifive-plic.c 
395 IRQCHIP_DECLARE(sifive_plic, "sifive,plic-1.0.0", plic_init);
396 IRQCHIP_DECLARE(riscv_plic0, "riscv,plic0", plic_init); /* for legacy systems */

把IRQCHIP_DECLARE展开后

#define IRQCHIP_DECLARE(name, compat, fn) OF_DECLARE_2(irqchip, name, compat, fn)

#define OF_DECLARE_2(table, name, compat, fn) \
         _OF_DECLARE(table, name, compat, fn, of_init_fn_2)
===>OF_DECLARE_2(irqchip,sifive_plic, "sifive,plic-1.0.0", plic_init) 
===>_OF_DECLARE(irqchip,sifive_plic, "sifive,plic-1.0.0", plic_init,of_init_fn_2)

#define _OF_DECLARE(table, name, compat, fn, fn_type)           \
        static const struct of_device_id __of_table_##name      \
         __used __section("__" #table "_of_table")       \
         __aligned(__alignof__(struct of_device_id))     \
          = { .compatible = compat,              \
              .data = (fn == (fn_type)NULL) ? fn : fn  }
===>_OF_DECLARE(irqchip,sifive_plic, "sifive,plic-1.0.0", plic_init,of_init_fn_2)
===>static const struct of_device_id __of_table_sifive_plic 
         __used __section("__irqchip_of_table")      
         __aligned(__alignof__(struct of_device_id))     
          = { .compatible = "sifive,plic-1.0.0",              
              .data = plic_init  }

链接器的链接脚本使用__irqchip_of_table 存放节"__irqchip_of_table "的起始地址,也就是中断控制器匹配表的起始地址

linux/arch/riscv/kernel/vmlinux.lds.S
78 INIT_DATA_SECTION(16)

include/asm-generic/vmlinux.lds.h
#define INIT_DATA_SECTION(initsetup_align)              \
     .init.data : AT(ADDR(.init.data) - LOAD_OFFSET) {       \
         INIT_DATA                       \
         INIT_SETUP(initsetup_align)             \
        INIT_CALLS                      \
         CON_INITCALL                        \
         INIT_RAM_FS                     \
     }

#define INIT_DATA                           \
...
IRQCHIP_OF_MATCH_TABLE()                    \
...

#define IRQCHIP_OF_MATCH_TABLE() OF_TABLE(CONFIG_IRQCHIP, irqchip)
#define OF_TABLE(cfg, name) __OF_TABLE(IS_ENABLED(cfg), name)
#define __OF_TABLE(cfg, name)   ___OF_TABLE(cfg, name)
#define ___OF_TABLE(cfg, name)  _OF_TABLE_##cfg(name)
#define _OF_TABLE_0(name)
#define _OF_TABLE_1(name)                       \
   . = ALIGN(8);                           \
   __##name##_of_table = .;                    \
   KEEP(*(__##name##_of_table))                    \
   KEEP(*(__##name##_of_table_end))

===>#define _OF_TABLE_1(irqchip)                       \
   . = ALIGN(8);                           \
   __irqchip_of_table = .;                    \
   KEEP(*(__irqchip_of_table))                    \
   KEEP(*(__irqchip_of_table_end))

3 初始化

初始化函数of_irq_init

472 /**
473  * of_irq_init - Scan and init matching interrupt controllers in DT
474  * @matches: 0 terminated array of nodes to match and init function to call
475  *
476  * This function scans the device tree for matching interrupt controller nodes,
477  * and calls their initialization functions in order with parents first.
478  */
479 void __init of_irq_init(const struct of_device_id *matches)
480 {
481     const struct of_device_id *match;
482     struct device_node *np, *parent = NULL;
483     struct of_intc_desc *desc, *temp_desc;
484     struct list_head intc_desc_list, intc_parent_list;
485 
486     INIT_LIST_HEAD(&intc_desc_list);
487     INIT_LIST_HEAD(&intc_parent_list);
488 
489     for_each_matching_node_and_match(np, matches, &match) {
490         if (!of_property_read_bool(np, "interrupt-controller") ||
491                 !of_device_is_available(np))
492             continue;
493 
494         if (WARN(!match->data, "of_irq_init: no init function for %s\n",
495              match->compatible))
496             continue;
497 
498         /*
499          * Here, we allocate and populate an of_intc_desc with the node
500          * pointer, interrupt-parent device_node etc.
501          */
502         desc = kzalloc(sizeof(*desc), GFP_KERNEL);
503         if (!desc) {
504             of_node_put(np);
505             goto err;
506         }
507 
508         desc->irq_init_cb = match->data;
509         desc->dev = of_node_get(np);
510         desc->interrupt_parent = of_irq_find_parent(np);
511         if (desc->interrupt_parent == np)
512             desc->interrupt_parent = NULL;
513         list_add_tail(&desc->list, &intc_desc_list);
514     }
515 
516     /*
517      * The root irq controller is the one without an interrupt-parent.
518      * That one goes first, followed by the controllers that reference it,
519      * followed by the ones that reference the 2nd level controllers, etc.
520      */
521     while (!list_empty(&intc_desc_list)) {
522         /*
523          * Process all controllers with the current 'parent'.
524          * First pass will be looking for NULL as the parent.
525          * The assumption is that NULL parent means a root controller.
526          */
527         list_for_each_entry_safe(desc, temp_desc, &intc_desc_list, list) {
528             int ret;
529 
530             if (desc->interrupt_parent != parent)
531                 continue;
532 
533             list_del(&desc->list);
534 
535             of_node_set_flag(desc->dev, OF_POPULATED);
536 
537             pr_debug("of_irq_init: init %pOF (%p), parent %p\n",
538                  desc->dev,
539                  desc->dev, desc->interrupt_parent);
540             ret = desc->irq_init_cb(desc->dev,
541                         desc->interrupt_parent);
542             if (ret) {
543                 of_node_clear_flag(desc->dev, OF_POPULATED);
544                 kfree(desc);
545                 continue;
546             }
547 
548             /*
549              * This one is now set up; add it to the parent list so
550              * its children can get processed in a subsequent pass.
551              */
552             list_add_tail(&desc->list, &intc_parent_list);
553         }
554 
555         /* Get the next pending parent that might have children */
556         desc = list_first_entry_or_null(&intc_parent_list,
557                         typeof(*desc), list);
558         if (!desc) {
559             pr_err("of_irq_init: children remain, but no parents\n");
560             break;
561         }
562         list_del(&desc->list);
563         parent = desc->dev;
564         kfree(desc);
565     }
566 
567     list_for_each_entry_safe(desc, temp_desc, &intc_parent_list, list) {
568         list_del(&desc->list);
569         kfree(desc);
570     }
571 err:
572     list_for_each_entry_safe(desc, temp_desc, &intc_desc_list, list) {
573         list_del(&desc->list);
574         of_node_put(desc->dev);
575         kfree(desc);
576     }
577 }

设备树文件arch/riscv/boot/dts/sifive/fu540-c000.dtsi里面的中断控制器属性"compatible"是 "sifive,plic-1.0.0"和中断控制器匹配表中的(sifive_plic, "sifive,plic-1.0.0", plic_init);(riscv_plic0, "riscv,plic0", plic_init); 匹配
初始化函数plic_init

276 static int __init plic_init(struct device_node *node,
277         struct device_node *parent)
278 {
279     int error = 0, nr_contexts, nr_handlers = 0, i;
280     u32 nr_irqs;
281     struct plic_priv *priv;
282     struct plic_handler *handler;
283 
284     priv = kzalloc(sizeof(*priv), GFP_KERNEL);
285     if (!priv)
286         return -ENOMEM;
287 
288     priv->regs = of_iomap(node, 0);
289     if (WARN_ON(!priv->regs)) {
290         error = -EIO;
291         goto out_free_priv;
292     }
293 
294     error = -EINVAL;
295     of_property_read_u32(node, "riscv,ndev", &nr_irqs);
296     if (WARN_ON(!nr_irqs))
297         goto out_iounmap;
298 
299     nr_contexts = of_irq_count(node);
300     if (WARN_ON(!nr_contexts))
301         goto out_iounmap;
302 
303     error = -ENOMEM;
304     priv->irqdomain = irq_domain_add_linear(node, nr_irqs + 1,
305             &plic_irqdomain_ops, priv);
306     if (WARN_ON(!priv->irqdomain))
307         goto out_iounmap;
308 
309     for (i = 0; i < nr_contexts; i++) {
310         struct of_phandle_args parent;
311         irq_hw_number_t hwirq;
312         int cpu, hartid;
313 
314         if (of_irq_parse_one(node, i, &parent)) {
315             pr_err("failed to parse parent for context %d.\n", i);
316             continue;
317         }
318 
319         /*
320          * Skip contexts other than external interrupts for our
321          * privilege level.
322          */
323         if (parent.args[0] != RV_IRQ_EXT)
324             continue;
325 
326         hartid = riscv_of_parent_hartid(parent.np);
327         if (hartid < 0) {
328             pr_warn("failed to parse hart ID for context %d.\n", i);
329             continue;
330         }
331             
332         cpu = riscv_hartid_to_cpuid(hartid);
333         if (cpu < 0) {
334             pr_warn("Invalid cpuid for context %d\n", i);
335             continue;
336         }
337             
338         /* Find parent domain and register chained handler */
339         if (!plic_parent_irq && irq_find_host(parent.np)) {
340             plic_parent_irq = irq_of_parse_and_map(node, i);
341             if (plic_parent_irq)
342                 irq_set_chained_handler(plic_parent_irq,
343                             plic_handle_irq);
344         }   
345                 
346         /*                  
347          * When running in M-mode we need to ignore the S-mode handler.
348          * Here we assume it always comes later, but that might be a
349          * little fragile.
350          */
351         handler = per_cpu_ptr(&plic_handlers, cpu);
352         if (handler->present) {
353             pr_warn("handler already present for context %d.\n", i);
354             plic_set_threshold(handler, PLIC_DISABLE_THRESHOLD);
355             goto done;
356         }
357 
358         cpumask_set_cpu(cpu, &priv->lmask);
359         handler->present = true;
360         handler->hart_base =
361             priv->regs + CONTEXT_BASE + i * CONTEXT_PER_HART;
362         raw_spin_lock_init(&handler->enable_lock);
363         handler->enable_base =
364             priv->regs + ENABLE_BASE + i * ENABLE_PER_HART;
365         handler->priv = priv;
366 done:
367         for (hwirq = 1; hwirq <= nr_irqs; hwirq++)
368             plic_toggle(handler, hwirq, 0);
369         nr_handlers++;
370     }
371 
372     /*
373      * We can have multiple PLIC instances so setup cpuhp state only
374      * when context handler for current/boot CPU is present.
375      */
376     handler = this_cpu_ptr(&plic_handlers);
377     if (handler->present && !plic_cpuhp_setup_done) {
378         cpuhp_setup_state(CPUHP_AP_IRQ_SIFIVE_PLIC_STARTING,
379                   "irqchip/sifive/plic:starting",
380                   plic_starting_cpu, plic_dying_cpu);
381         plic_cpuhp_setup_done = true;
382     }
383 
384     pr_info("%pOFP: mapped %d interrupts with %d handlers for"
385         " %d contexts.\n", node, nr_irqs, nr_handlers, nr_contexts);
386     return 0;
387 
388 out_iounmap:
389     iounmap(priv->regs);
390 out_free_priv:
391     kfree(priv);
392     return error;
393 }

未完待更新





最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,794评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,050评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,587评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,861评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,901评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,898评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,832评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,617评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,077评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,349评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,483评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,199评论 5 341
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,824评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,442评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,632评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,474评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,393评论 2 352

推荐阅读更多精彩内容

  • 一、中断基础概念 所谓中断,指CPU在执行程序的过程中,出现了某些突发事件即待处理,CPU必须暂停当前的程...
    Nothing_655f阅读 2,152评论 0 1
  • 一、前言 笔者一直对中断只有一个简单粗浅的认识,仅仅停留在处理外部事件或者异步事件上。所以对于中断的认识不够清晰。...
    wipping的技术小栈阅读 2,700评论 0 4
  • 系统调用概述 计算机系统的各种硬件资源是有限的,在现代多任务操作系统上同时运行的多个进程都需要访问这些资源,为了更...
    linux大本营阅读 841评论 0 1
  • 什么是中断 操作系统需要对连接到计算机上的所有硬件设备进行管理,要管理这些设备,首先得和它们互相通信才行,一般有两...
    明阳似海阅读 7,949评论 0 7
  • 我是黑夜里大雨纷飞的人啊 1 “又到一年六月,有人笑有人哭,有人欢乐有人忧愁,有人惊喜有人失落,有的觉得收获满满有...
    陌忘宇阅读 8,535评论 28 53