linux kernel中设置向量表基地址
在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 (也就是最地址处,空出64KB的地方,给vector使用)
(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基地址的地方
在kernel中有两处,分别是__mmap_switched和启动其它cpu的地方
(kernel-4.14/arch/arm64/kernel/head.S)
__mmap_switched:
mov x28, lr // preserve LR
adr_l x8, vectors // load VBAR_EL1 with virtual
msr vbar_el1, x8 // vector table address
isb
// Clear BSS
adr_l x0, __bss_start
mov x1, xzr
adr_l x2, __bss_stop
sub x2, x2, x0
bl __pi_memset
dsb ishst // Make zero page visible to PTW
ENTRY(__secondary_switched)
adr_l x5, vectors
msr vbar_el1, x5
isb
#ifdef CONFIG_THREAD_INFO_IN_TASK
adr_l x0, secondary_data
ldr x1, [x0, #CPU_BOOT_STACK] // get secondary_data.stack
mov sp, x1
ldr x2, [x0, #CPU_BOOT_TASK]
msr sp_el0, x2
#else
ldr_l x0, secondary_data // get secondary_data.stack
mov sp, x0
and x0, x0, #~(THREAD_SIZE - 1)
msr sp_el0, x0 // save thread_info
#endif
mov x29, #0
b secondary_start_kernel
ENDPROC(__secondary_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
下一篇: IAP_3_代码大小配置、生成bin文件