天天看点

linux kernel中设置向量表基地址

这目录

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

在linux kernel中,是如何设置向量表基地址的(如何设置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定义的一致性)

linux kernel中设置向量表基地址

(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
           

(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
           
linux kernel中设置向量表基地址
__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)
           

继续阅读