• 《从0开始写一个微内核操作系统》6-GIC中断


    ChinOS

    https://github.com/jingjin666/GN-base/tree/chinos

    关于GIC

    GIC是一个针对Cortex-A和Cortex-R设计的中断控制器,SOC厂商根据选择的CORE来选择不同的GIC版本进行搭配
    在这里插入图片描述

    GICv3

    GICv3的文档请参考ARM文档中心Documentation – Arm Developer
    Arm Generic Interrupt Controller v3 and v4 Overview

    GICv3架构

    GICv3由以下模块组成

    • Distributor interface
      • SPI中断的管理,将中断发送给redistributor
    • Redistributor interface
      • PPI,SGI,LPI中断的管理,将中断发送给cpu interface
    • CPU interface
      • 传输中断给core
    • ITS
      • 用来解析LPI中断

    其中,cpu interface是实现在core内部的,distributor,redistributor,ITS是实现在gic内部的

    在这里插入图片描述

    中断分组

    在这里插入图片描述

    不同异常等级下的分组:

    • GROUP0下的所有中断都被识别为FIQ中断;
    • 非安全世界下的EL0/1/2下,GROUP1下的所有中断被识别为IRQ中断,这也是我们操作系统最常用的中断分组,一般操作系统和hypervisor都处于非安全世界(除了TrustOS外)

    在这里插入图片描述

    中断类型

    Shared Peripheral Interrupt (SPI)

    共享外设中断,也就是通常用的外设中断,可以多个CPU或者说CORE处理,不限定特定的CPU

    Private Peripheral Interrupt (PPI)

    私有外设中断,这是每个核心私有的中断。PPI会送达到指定的CPU上,应用场景有CPU本地时钟

    Software Generated Interrupt (SGI)

    软中断,软件可以通过写GICD_SGIR寄存器来触发一个中断事件,一般用于核间通信

    Locality-specific Peripheral Interrupt (LPI)

    消息中断,不通过专用中断线向GIC发送消息,通过写GIC寄存器发送中断

    不同中断类型的中断号划分

    在这里插入图片描述

    中断处理流程

    通过distributor的中断流程

    • 外设发起中断,发送给distributor
    • distributor将该中断,分发给合适的re-distributor
    • re-distributor将中断信息,发送给cpu interface
    • cpu interface产生合适的中断异常给处理器
    • 处理器接收该异常,并且处理该中断

    不通过distributor,比如LPI中断

    • 外设发起中断,发送给ITS
    • ITS分析中断,决定将来发送的re-distributor
    • ITS将中断发送给合适的re-distributor

    GICv3初始化

    GIC的初始化实际上就是其主要模块(distributor/redistributor/cpu_interface)的初始化

    Distributor初始化

    Distributor主要初始化和配置SPI中断:

    
    static void distributor_init(void)
    {
        u32 i;
    
        // read gic version
        u32 pidr2 = getreg32(GICD_PIDR2);
        u32 gic_version = bitfield_get(pidr2, 4, 4);
        assert((gic_version == ARCH_REV_GICV3) || (gic_version == ARCH_REV_GICV4));
    
        // read gic max interrupts
        u32 typer = getreg32(GICD_TYPER);
        gic_max_int = (bitfield_get(typer, 0, 5) + 1) * 32;
        kprintf("gic_version = %d, gic_max_int = %d, typer = %p\n", gic_version, gic_max_int, typer);
    
        // disable distributor
        putreg32(0, GICD_CTLR);
        gicd_wait_rwp();
    
        // assume level-trigger
        for (i = 32; i < gic_max_int; i += 16) {
            putreg32(0, GICD_ICFGR(i / 16));
        }
    
        // default priority for global interrupts
        u32 priority = (GIC_PRI_IRQ << 24 | GIC_PRI_IRQ << 16 | GIC_PRI_IRQ << 8 |
                    GIC_PRI_IRQ);
        for (i = 32; i < gic_max_int; i += 4) {
            putreg32(priority, GICD_IPRIORITYR(i / 4));
        }
    
        // distributor config: mask and clear all spis, set group 1.
        for (i = 32; i < gic_max_int; i += 32) {
            putreg32(~0, GICD_ICENABLER(i / 32));
            putreg32(~0, GICD_ICPENDR(i / 32));
            putreg32(~0, GICD_IGROUPR(i / 32));
            putreg32(0, GICD_IGRPMODR(i / 32));
        }
        gicd_wait_rwp();
        
        // enable distributor with ARE, group 1 enable
        putreg32(CTLR_ENABLE_G0 | CTLR_ENABLE_G1NS | CTLR_ARE_S, GICD_CTLR);
        gicd_wait_rwp();
    
        // route all global IRQs to this CPU
        u64 affinity = mpidr_to_gic_affinity();
        kprintf("affinity = %p\n", affinity);
        for (i = 32; i < gic_max_int; i++) {
            putreg64(GICD_IROUTER(i), affinity);
        }
        gicd_wait_rwp();
    
        isb();
        dsb();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55

    Redistributor初始化

    Redistributor主要初始化和配置PPI/SGI/LPI中断

    
    static void redistributor_init(void)
    {
        u32 i;
        u32 cpu = 0;
    
        // mark PE online
        u32 waker = getreg32(GICR_WAKER(cpu));
        kprintf("waker = %p\n", waker);
        waker &= ~GICR_WAKER_ProcessorSleep;
        kprintf("waker = %p\n", waker);
        putreg32(waker, GICR_WAKER(cpu));
        while (getreg32(GICR_WAKER(cpu)) & GICR_WAKER_ChildrenAsleep) {
            kprintf("GICv3: GICR_WAKER returned non-zero %x\n", waker);
            halt();
        }
    
        /* deactivate SGIs/PPIs */
        putreg32(~0, GICR_ICACTIVER0(cpu));
        gicr_wait_rwp();
    
        /* set ICFGR0 for SGIs as edge-triggered */
        u32 icfgr0 = getreg32(GICR_ICFGR0(cpu));
        kprintf("icfgr0 = %p\n", icfgr0);
        putreg32(0xaaaaaaaa, GICR_ICFGR0(cpu));
        gicr_wait_rwp();
    
        /* set ICFGR1 for PPIs as level-triggered */
        u32 icfgr1 = getreg32(GICR_ICFGR1(cpu));
        kprintf("icfgr1 = %p\n", icfgr1);
        putreg32(0, GICR_ICFGR1(cpu));
        gicr_wait_rwp();
    
        /* set priority on PPI and SGI interrupts */
        u32 priority = (GIC_PRI_IRQ << 24 | GIC_PRI_IRQ << 16 | GIC_PRI_IRQ << 8 |
                    GIC_PRI_IRQ);
        for (i = 0; i < 32; i += 4) {
            putreg32(priority, GICR_IPRIORITYR0(cpu) + i);
        }
        gicr_wait_rwp();
    
        // redistributer config: configure sgi/ppi as non-secure group 1.
        putreg32(~0, GICR_IGROUPR0(cpu));
        gicr_wait_rwp();
    
        // redistributer config: clear and mask sgi/ppi.
        putreg32(~0, GICR_ICENABLER0(cpu));
        putreg32(~0, GICR_ICPENDR0(cpu));
        gicr_wait_rwp();
    
        // TODO lpi init
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52

    cpu_interface初始化

    static void cpu_interface_init(void)
    {
        u32 sre;
        // enable system register access
        MRS(ICC_SRE_EL1, sre);
        kprintf("sre = %p\n", sre);
        sre |= GICC_SRE_EL1_SRE;
        kprintf("sre = %p\n", sre);
        MSR(ICC_SRE_EL1, sre);
    
        // no priority grouping: ICC_BPR1_EL1
        MSR(ICC_BPR1_EL1, 0);
    
        // set priority mask register: ICC_PMR_EL
        MSR(ICC_PMR_EL1, DEFAULT_PMR_VALUE);
    
        // EOI drops priority and deactivates the interrupt: ICC_CTLR_EL1
        u32 icc_ctlr;
        MRS(ICC_CTLR_EL1, icc_ctlr);
        kprintf("icc_ctlr = %p\n", icc_ctlr);
        icc_ctlr &= ~GICC_CTLR_EL1_EOImode_drop;
        kprintf("icc_ctlr = %p\n", icc_ctlr);
        MSR(ICC_CTLR_EL1, icc_ctlr);
    
        // Enable Group1 interrupts: ICC_IGRPEN1_EL1
        MSR(ICC_IGRPEN1_EL1, 1);
    
        // Sync at once at the end of cpu interface configuration
        isb();
        dsb();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    中断向量表初始化

    不同异常等级下所对应的中断向量表VBAR_ELx,决定了中断产生后跳转到哪个地址去执行

    static void relocate_vector(void)
    {
        unsigned long current_el;
        unsigned long vbase = (unsigned long)vector;
    
        MRS("CurrentEL", current_el);
        current_el = bitfield_get(current_el, 2, 2);
        switch(current_el)
        {
            case 0:
                break;
            case 1:
                MSR("VBAR_EL1", vbase);
                MRS("VBAR_EL1", vbase);
                kprintf("VBAR_EL1 = %p\n", vbase);
                break;
            case 2:
                MSR("VBAR_EL2", vbase);
                MRS("VBAR_EL2", vbase);
                kprintf("VBAR_EL2 = %p\n", vbase);
                break;
            case 3:
                MSR("VBAR_EL3", vbase);
                MRS("VBAR_EL3", vbase);
                kprintf("VBAR_EL3 = %p\n", vbase);
                break;
            default:
                break;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    vector符号定义在vector.S中,注意一个异常向量表占用0x800空间,需要2^11=2048字节对齐;
    一个异常向量表里有16个表项,每个表项占用0x80空间,需要2^7=128字节对齐

    .global vector
    
    .macro ventry label
    .align 7
        b       \label
    .endm
    
    .section ".vector", "ax"
    .align 11
    
    vector:
        ventry  invalid_vector_entry           // Synchronous EL1t/EL2t
        ventry  invalid_vector_entry           // IRQ EL1t/EL2t
        ventry  invalid_vector_entry           // FIQ EL1t/EL2t
        ventry  invalid_vector_entry           // SError EL1t/EL2t
    
        ventry  cur_el_sync                    // Current EL Synchronous (EL1/2)
        ventry  cur_el_irq                     // IRQ
        ventry  invalid_vector_entry           // FIQ
        ventry  cur_el_serr                    // SError
    
        ventry  lower_el_sync                  // Synchronous 64-bit EL0/EL1
        ventry  lower_el_irq                   // IRQ 64-bit EL0/EL1
        ventry  invalid_vector_entry           // FIQ 64-bit EL0/EL1
        ventry  lower_el_serr                  // SError 64-bit EL0/EL1
    
        ventry  invalid_vector_entry           // Synchronous 32-bit EL0/EL1
        ventry  invalid_vector_entry           // IRQ 32-bit EL0/EL1
        ventry  invalid_vector_entry           // FIQ 32-bit EL0/EL1
        ventry  invalid_vector_entry           // SError 32-bit EL0/EL1
    
    invalid_vector_entry:
        b   .
    
    cur_el_sync:
        b   .
    
    cur_el_irq:
        bl      decode_irq
        eret
        b .
    
    cur_el_serr:
        b   .
    
    lower_el_sync:
        b .
    
    lower_el_irq:
        b .
    
    lower_el_serr:
        b .
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54

    默认情况下,不同异常等级使用各自异常等级下的VBAR_ELx,所以表1和表4一般不使用,表2和表3使用的最多,表2代表在当前异常等级产生的中断,表3代表在比当前异常等级低产生中断。对于我们操作系统来说,操作系统工作在EL1,那么表2代表在内核态(EL1)下产生的中断,表1代表在用户态(EL0)产生的中断

    使能VBAR_ELx

    SPSel为1代表使能

        u64 spsel;
        MRS("SPSel", spsel);
        kprintf("SPSel = %p\n", spsel);
    
    • 1
    • 2
    • 3
  • 相关阅读:
    npm常用命令详解(二)
    MODNet:基于目标分解的实时trimap-free肖像抠图
    Python 判断图片相似度的方法
    hdlbits系列verilog解答(always块nolatch语句)-36
    论文解读(SAGPool)《Self-Attention Graph Pooling》
    Go单测时的Parallel
    效率提升75%!要做矩阵号,更要做好矩阵号管理
    MySQL(三)基本的SELECT语句
    HTTP 基础 (基于 HTTP 的系统的组件、客户端:用户代理、网络服务器)
    基于FPGA的ECG心电信号峰值检测和心率计算,包括testbench测试文件和ECG数据转换为coe文件程序
  • 原文地址:https://blog.csdn.net/GoolyOh/article/details/127694201