baron · 3月27日 · 四川

linux kernel中设置向量表基地址

快速连接

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


 title=


★★★ 友情链接 : 个人博客导读首页---点击此处 ★★★

在linux kernel中,是如何设置向量表基地址的(如何设置VBAR的)?

1、linux kernel的arm32下设置向量表基地址VBAR

(1)、在entry-armv.S中,软件中定义了向量表
__vectors_start是向量表基地址,vector_irq/vector_fiq是向量表中的offset

需要:

  • (1)、将__vectors_start基地址写入到VBAR寄存器.
  • (2)、向量表中的offset要和arm文档中定义的一致
(kernel-4.14/arch/arm/kernel/entry-armv.S)
    .section .vectors, "ax", %progbits
.L__vectors_start:
    W(b)    vector_rst
    W(b)    vector_und
    W(ldr)    pc, .L__vectors_start + 0x1000
    W(b)    vector_pabt
    W(b)    vector_dabt
    W(b)    vector_addrexcptn
    W(b)    vector_irq
    W(b)    vector_fiq

(软件中的向量表和硬件中的offset定义的一致性)
在这里插入图片描述

(2)、在vmlinux.lds.S描述了__vectors_start的起始位置,从0xffff0000开始

(kernel-4.14/arch/arm/kernel/vmlinux.lds.S)
__vectors_start = .;
.vectors 0xffff0000 : AT(__vectors_start) {
    *(.vectors)
}
. = __vectors_start + SIZEOF(.vectors);
__vectors_end = .;

__stubs_start = .;
.stubs ADDR(.vectors) + 0x1000 : AT(__stubs_start) {
    *(.stubs)
}
. = __stubs_start + SIZEOF(.stubs);
__stubs_end = .;

(3)、在nommu.c中,setup_vectors_base函数通过操作cp15协处理器来写入VBAR.

(kernel-4.14/arch/arm/mm/nommu.c)
static unsigned long __init setup_vectors_base(void)
{
    unsigned long reg = get_cr();

    set_cr(reg | CR_V);   //其实就是将CR_V写入到了cp15, 也就是写入到了VBAR寄存器.
    return 0xffff0000;
}
static inline void set_cr(unsigned long val)
{
    asm volatile("mcr p15, 0, %0, c1, c0, 0    @ set CR"
      : : "r" (val) : "cc");
    isb();
}

(4)、而CR_V的定义在cp15.h中,恰好就是0xffff0000 (<font color=red size=3>也就是最地址处,空出64KB的地方,给vector使用</font>)

(kernel-4.14/arch/arm/include/asm/cp15.h)
#define CR_M    (1 << 0)    /* MMU enable                */
#define CR_A    (1 << 1)    /* Alignment abort enable        */
#define CR_C    (1 << 2)    /* Dcache enable            */
#define CR_W    (1 << 3)    /* Write buffer enable            */
#define CR_P    (1 << 4)    /* 32-bit exception handler        */
#define CR_D    (1 << 5)    /* 32-bit data address range        */
#define CR_L    (1 << 6)    /* Implementation defined        */
#define CR_B    (1 << 7)    /* Big endian                */
#define CR_S    (1 << 8)    /* System MMU protection        */
#define CR_R    (1 << 9)    /* ROM MMU protection            */
#define CR_F    (1 << 10)    /* Implementation defined        */
#define CR_Z    (1 << 11)    /* Implementation defined        */
#define CR_I    (1 << 12)    /* Icache enable            */
#define CR_V    (1 << 13)    /* Vectors relocated to 0xffff0000    */
#define CR_RR    (1 << 14)    /* Round Robin cache replacement    */
#define CR_L4    (1 << 15)    /* LDR pc can set T bit            */
#define CR_DT    (1 << 16)

(5)、setup_vectors_base是在开机的时候调用的

setup_arch ----> arm_memblock_init ----> arm_mm_memblock_reserve  ---> setup_vectors_base

2、linux kernel的arm64下设置向量表基地址VBAR

(1)、在entry.S定义了向量表,然后我们需要:

  • 将vectors地址写入到VBAR_EL1中
  • 确保向量表中的偏移和ARM文档中的定义一致
(kernel-4.14/arch/arm64/kernel/entry.S)
/*
 * Exception vectors.
 */

    .align    11
ENTRY(vectors)
    kernel_ventry    1, sync_invalid            // Synchronous EL1t
    kernel_ventry    1, irq_invalid            // IRQ EL1t
    kernel_ventry    1, fiq_invalid            // FIQ EL1t
    kernel_ventry    1, error_invalid        // Error EL1t

    kernel_ventry    1, sync                // Synchronous EL1h
    kernel_ventry    1, irq                // IRQ EL1h
    kernel_ventry    1, fiq_invalid            // FIQ EL1h
    kernel_ventry    1, error_invalid        // Error EL1h

    kernel_ventry    0, sync                // Synchronous 64-bit EL0
    kernel_ventry    0, irq                // IRQ 64-bit EL0
    kernel_ventry    0, fiq_invalid            // FIQ 64-bit EL0
    kernel_ventry    0, error_invalid        // Error 64-bit EL0

#ifdef CONFIG_COMPAT
    kernel_ventry    0, sync_compat, 32        // Synchronous 32-bit EL0
    kernel_ventry    0, irq_compat, 32        // IRQ 32-bit EL0
    kernel_ventry    0, fiq_invalid_compat, 32    // FIQ 32-bit EL0
    kernel_ventry    0, error_invalid_compat, 32    // Error 32-bit EL0
#else
    kernel_ventry    0, sync_invalid, 32        // Synchronous 32-bit EL0
    kernel_ventry    0, irq_invalid, 32        // IRQ 32-bit EL0
    kernel_ventry    0, fiq_invalid, 32        // FIQ 32-bit EL0
    kernel_ventry    0, error_invalid, 32        // Error 32-bit EL0
#endif
END(vectors)

(2)、kernel_ventry是一个宏,(align 7)的对齐方式,也就是0x80的对齐方式,恰好和arm文档中的offset一致

    .macro kernel_ventry, el, label, regsize = 64
    .align 7
#ifdef CONFIG_UNMAP_KERNEL_AT_EL0
alternative_if ARM64_UNMAP_KERNEL_AT_EL0
    .if    \el == 0
    .if    \regsize == 64
    mrs    x30, tpidrro_el0
    msr    tpidrro_el0, xzr
    .else
    mov    x30, xzr
    .endif
    .endif
alternative_else_nop_endif
#endif

在这里插入图片描述

(3)、再看写入VBAR_EL1基地址的地方
在__primary_switched将vectors写入到了VBAR_EL1

__primary_switched:
    adrp    x4, init_thread_union
    add    sp, x4, #THREAD_SIZE
    adr_l    x5, init_task
    msr    sp_el0, x5            // Save thread_info

    adr_l    x8, vectors            // load VBAR_EL1 with virtual
    msr    vbar_el1, x8            // vector table address
    isb

    stp    xzr, x30, [sp, #-16]!
    mov    x29, sp

    str_l    x21, __fdt_pointer, x5        // Save FDT pointer

..
    b    start_kernel
ENDPROC(__primary_switched)

补充cpu启动的小知识:
1)先启动主CPU,启动过程和传统的单核系统类似:stext-->start_kernel-->rest_init-->cpu_startup_entry
2)启动其它CPU,可以有多种方式,例如CPU hotplug等,启动过程为:secondary_startup-->__secondary_switched-->secondary_start_kernel-->cpu_startup_entry


在这里插入图片描述
《ARMv8/ARMv9架构学习系列课程》全系列,共计51节课,超15h的视频课程


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

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