64位多核 MIPS 异常和中断内核代码分析 (4)

64位多核 MIPS 异常和中断内核代码分析 (4)

 

1.3 其它例外入口初始化


其它例外的入口初始化位于:

[arch/mips/kernel/traps.c]

void __init trap_init()

{

......

    /*
     * Copy the generic exception handlers to their final destination.
     * This will be overriden later as suitable for a particular
     * configuration.
     */
     set_handler(0x180, &except_vec3_generic, 0x80);

.......

}

 

set_handler 同样定义该文件中:/* Install CPU exception handler */
void __init set_handler(unsigned long offset, void *addr, unsigned long size)
{
    memcpy((void *)(ebase + offset), addr, size);
    local_flush_icache_range(ebase + offset, ebase + offset + size);
}

其主要的操作就是把其它例外处理函数 except_vec3_generic,复制到对应的入口处,这个入口一般为 ebase + 0x180。主要的不同在于 ebase 的值,这个在 cavium 上为 CKSEG0 + read_c0_ebase(),loongson2 上为 CKSEG0 = 0xFFFF FFFF8000 0180:

void __init trap_init()

{

    ......

    if (cpu_has_veic || cpu_has_vint) {
        unsigned long size = 0x200 + VECTORSPACING*64;
        ebase = (unsigned long)
            __alloc_bootmem(size, 1 << fls(size), 0);
    } else {
        ebase = CKSEG0;
        if (cpu_has_mips_r2)
            ebase += (read_c0_ebase() & 0x3ffff000);
    }

    ......

}

其它例外处理函数 except_vec3_generic 定义于:

[arch/mips/kernel/genex.S]

/*
 * General exception vector for all other CPUs.
 *
 * Be careful when changing this, it has to be at most 128 bytes
 * to fit into space reserved for the exception handler.
 */
NESTED(except_vec3_generic, 0, sp)
    .set    push
    .set    noat
#if R5432_CP0_INTERRUPT_WAR
    mfc0    k0, CP0_INDEX
#endif
    mfc0    k1, CP0_CAUSE
    andi    k1, k1, 0x7c         # K1 = ExcCode * 4,32bit
#ifdef CONFIG_64BIT
    dsll    k1, k1, 1                        # k1 = ExcCode * 8,64bit 指针为 8 字节
#endif
    PTR_L   k0, exception_handlers(k1)
    jr  k0
    .set    pop
    END(except_vec3_generic)

代码很短,其功能为:取 cp0_cause 之 ExcCode 值,然后跳转到 exception_handlers[ExcCode] 处。这个 exception_handler 实际是一张表,每一项放的是具体异常处理函数的指针。ExcCode 为 cp0_cause[6:2],cp0_cause & 0x7c 就是 ExcCode * 4;64位下,指针长度为 8 字节,因此其还要左移一位,得 ExcCode * 8

 

这个 exception_handlers 定义于:

[arch/mips/kernel/traps]

unsigned long exception_handlers[32];

 

在 trap_init() 中填冲:

 

    /*
     * Setup default vectors
     */
    for (i = 0; i <= 31; i++)
        set_except_vector(i, handle_reserved);

    ......

    set_except_vector(0, rollback ? rollback_handle_int : handle_int);
    set_except_vector(1, handle_tlbm);
    set_except_vector(2, handle_tlbl);
    set_except_vector(3, handle_tlbs);

    set_except_vector(4, handle_adel);
    set_except_vector(5, handle_ades);

    set_except_vector(6, handle_ibe);
    set_except_vector(7, handle_dbe);

    set_except_vector(8, handle_sys);
    set_except_vector(9, handle_bp);
    set_except_vector(10, rdhwr_noopt ? handle_ri :
              (cpu_has_vtag_icache ?
               handle_ri_rdhwr_vivt : handle_ri_rdhwr));
    set_except_vector(11, handle_cpu);
    set_except_vector(12, handle_ov);
    set_except_vector(13, handle_tr);

    ......

 

这个 set_except_vector() 定义于同一文件中:

void __init *set_except_vector(int n, void *addr)
{
    unsigned long handler = (unsigned long) addr;
    unsigned long old_handler = exception_handlers[n];

    exception_handlers[n] = handler;
    if (n == 0 && cpu_has_divec) { // 处理扩展中断向量的情形, MIPS32/64 R2 都带的一个可选 feature,loongson2 和 cavium 都没有实现,因此我们不关心
        unsigned long jump_mask = ~((1 << 28) - 1);
        u32 *buf = (u32 *)(ebase + 0x200);
        unsigned int k0 = 26;
        if ((handler & jump_mask) == ((ebase + 0x200) & jump_mask)) {
            uasm_i_j(&buf, handler & ~jump_mask);
            uasm_i_nop(&buf);
        } else {
            UASM_i_LA(&buf, k0, handler);
            uasm_i_jr(&buf, k0);
            uasm_i_nop(&buf);
        }
        local_flush_icache_range(ebase + 0x200, (unsigned long)buf);
    }
    return (void *)old_handler;
}

 

其完成的主要操作就是将传来的具体例外处理函数地址赋值给 exception_handlers 的元素 n

可以看到用来索引的 ExcCode 是这样与具体例外处理函数相关的,MIPS 规定 ExcCode 值表示的含义如下:

0             Int           中断

1            Mod            TLB 修改异常

2            TLBL            TLB 读异常

3            TLBS            TLB 写异常

4            AdEL          读地址错误异常

5            AdEs           写地址错误异常

6            IBE           总线错误异常(取指令)

7            DBE           总线错误异常(读写数据)

8            Sys           系统调用异常

......

 

上述的 handle_int, handle_sys, handle_adel, handle_ades, handle_ibe, handle_dbe ... 由下面的宏生成:

[arch/mips/kernel/genex.S]
    BUILD_HANDLER adel ade ade silent       /* #4  */
    BUILD_HANDLER ades ade ade silent       /* #5  */
    BUILD_HANDLER ibe be cli silent         /* #6  */
    BUILD_HANDLER dbe be cli silent         /* #7  */
    BUILD_HANDLER bp bp sti silent          /* #9  */
    BUILD_HANDLER ri ri sti silent          /* #10 */
    BUILD_HANDLER cpu cpu sti silent        /* #11 */
    BUILD_HANDLER ov ov sti silent          /* #12 */
    BUILD_HANDLER tr tr sti silent          /* #13 */
    BUILD_HANDLER fpe fpe fpe silent        /* #15 */
    BUILD_HANDLER mdmx mdmx sti silent      /* #22 */
#ifdef  CONFIG_HARDWARE_WATCHPOINTS
    /*
     * For watch, interrupts will be enabled after the watch
     * registers are read.
     */
    BUILD_HANDLER watch watch cli silent        /* #23 */
#else
    BUILD_HANDLER watch watch sti verbose       /* #23 */
#endif
    BUILD_HANDLER mcheck mcheck cli verbose     /* #24 */
    BUILD_HANDLER mt mt sti silent          /* #25 */
    BUILD_HANDLER dsp dsp sti silent        /* #26 */
    BUILD_HANDLER reserved reserved sti verbose /* others */

 

BUILD_HANDLER 定义为:

    .macro  BUILD_HANDLER exception handler clear verbose
    __BUILD_HANDLER \exception \handler \clear \verbose _int

    .endm

 

   .macro  __BUILD_HANDLER exception handler clear verbose ext
    .align  5
    NESTED(handle_\exception, PT_SIZE, sp)
    .set    noat
    SAVE_ALL
    FEXPORT(handle_\exception\ext)
    __BUILD_clear_\clear
    .set    at
    __BUILD_\verbose \exception
    move    a0, sp
    PTR_LA  ra, ret_from_exception
    j   do_\handler
    END(handle_\exception)
    .endm

你可能感兴趣的:(64位多核 MIPS 异常和中断内核代码分析 (4))