快速连接
👉👉👉【精选】ARMv8/ARMv9架构入门到精通-目录 👈👈👈
★★★ 友情链接 : 个人博客导读首页---点击此处 ★★★
在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交流讨论区。