欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

linux kernel中设置向量表基地址

程序员文章站 2024-02-25 15:04:03
...

在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定义的一致性)
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

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

linux kernel中设置向量表基地址

(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