ARM64学习笔记---建立异常向量表(二)-LMLPHP
ARM64学习笔记---建立异常向量表(二)-LMLPHP

ARM64学习笔记---建立异常向量表(二)-LMLPHP
ARM64学习笔记---建立异常向量表(二)-LMLPHP

源码:

#include "mm.h"
#include "sysregs.h"

.section  .rodata
.align 3
.globl el_string1
el_string1:
	.string "Booting at EL"

.section ".text.boot"
.globl _start
_start:
    //读取mpidr_el1寄存器的值,该寄存器决定了代码运行在哪个核心上
	mrs	x0, mpidr_el1		 
	//提取mpidr_el1寄存器的低8位
	and	x0, x0,#0xFF		// Check processor id
	//如果全零,说明代码运行在CPU0上
	cbz	x0, master		// Hang for all non-primary CPU
	//如果代码不是运行在CPU0上则挂死
	b	proc_hang

proc_hang: 
	b 	proc_hang

master:
        /* init uart and print the string*/
	bl __init_uart
    /* init uart and print the string*/
    //初始化串口打印
	bl __init_uart
    //读取当前EL状态
	mrs x5, CurrentEL
	//如果是处于EL3说明异常了,调用异常返回
	cmp x5, #CurrentEL_EL3
	b.eq el3_entry
	//否则调用EL2的处理
	b el2_entry

el3_entry:
	eret

el2_entry:
	bl print_el

	/* The Execution state for EL1 is AArch64 
	HCR_EL2寄存器中有一个RW域(Bit[31]),它记录了异常发生后ELI要处在哪个执行 状态下。 
		□ 1表示在AArch64执行状态下。 
		□ 0表示在AArch32执行状态下。
	*/
	ldr x0, =HCR_HOST_NVHE_FLAGS
	msr hcr_el2, x0
	//EL1页表还未建立,因此需要关闭MMU
	ldr x0, =SCTLR_VALUE_MMU_DISABLED
	msr sctlr_el1, x0

    /*因为当前处于EL2,所以发生异常前(EL2切换到EL1本质上是一种异常发生),需要填充当前等级的SPSR,也就是SPSR_EL2,当异常返回的时候(eret)时,再从SPSR_EL2恢复到PSTATE*/
    /*关闭D,A,I,F(关闭中断等),同时设置异常返回的等级,因为我们希望切换到EL1,因此这里设置EL2的异常返回等级为EL1h,这里的 h 代表使用的是SP_EL1作为栈指针*/
	ldr x0, =SPSR_EL1
	msr spsr_el2, x0
   /*PC相对地址加载指令,将el1的入口函数地址赋值给x0寄存器,这里注意和adrp的区别,adrp返回的地址是4K对齐,这样的话就不是函数的地址了*/
	adr x0, el1_entry
	/*将x0寄存器赋值给elr_el2,这里是设置el2的异常返回地址*/
	msr elr_el2, x0
    /*el2异常返回,程序流程会进入到el1*/
	eret

el1_entry:
	bl print_el

	/* 设置异常向量表基地址到vbar寄存器 */
	ldr     x5, =vectors
	msr     vbar_el1, x5
	isb
	//bss段清零
	adr	x0, _bss
	adr	x1, _ebss
	sub	x1, x1, x0
	bl 	memzero

	/*预留栈空间*/
	mov	sp, #LOW_MEMORY 
	/*跳转到C函数*/
	bl	kernel_main
	b 	proc_hang		// should never come here

print_el:
    //先保存LR寄存器,保存子程序的返回地址,避免被破坏
	mov x10, x30

	/*
	   print EL
	 */
	adrp x0, el_string1
	add x0, x0, :lo12:el_string1
	bl put_string_uart

	mrs x5, CurrentEL
	/* get the currentEL value */
	/*CurrentEL寄存器的 [2:3] 保存的是当前异常等级,因此这里右移2位后赋值给x2寄存器*/
	lsr x2, x5, #2
	//48指的是ASCII码为0
	mov x0, #48
	// ‘0’ + 具体的EL值,得到相应EL等级的ASCII码
	add x0, x0, x2
	// 将x0(EL具体值)作为入参传达给串口打印出来
	bl put_uart
	/* print the new line tab */
	//打印换行符
	mov x0, #10
	bl put_uart
    //恢复LR寄存器
	mov x30, x10
	ret

异常向量表的实现:

#define BAD_SYNC        0
#define BAD_IRQ         1
#define BAD_FIQ         2
#define BAD_ERROR       3

/*
   处理无效的异常向量
 */
	.macro inv_entry el, reason
	//kernel_entry el
	//第一个参数是pt_regs(寄存器框架大小),这个实验并没有用到
	mov x0, sp
	//"\reason" 汇编中表示引用入参
	mov x1, #\reason
	//根据寄存器esr_el1来解析同步异常
	mrs x2, esr_el1
	//跳转到C代码
	b bad_mode
	.endm

/*
   vector table entry
   每个表项是128字节, align 7表示128字节对齐
 */
	.macro vtentry label
	.align 7
	b \label
	.endm

/*
 * Vector Table
 *
 * ARM64的异常向量表一共占用2048个字节
 * 分成4组,每组4个表项,每个表项占128字节
 * 参见ARMv8 spec v8.6第D1.10节
 * align 11表示2048字节对齐
 */
.align 11
.global vectors
vectors:
	/* Current EL with SP0
	   当前系统运行在EL1时使用EL0的栈指针SP
	   这是一种异常错误的类型
	 */
	vtentry el1_sync_invalid
	vtentry el1_irq_invalid
	vtentry el1_fiq_invalid
	vtentry el1_error_invalid

	/* Current EL with SPx
	   当前系统运行在EL1时使用EL1的栈指针SP
	   这说明系统在内核态发生了异常

	   Note: 我们暂时只实现IRQ中断
	 */
	vtentry el1_sync_invalid
	vtentry el1_irq_invalid
	vtentry el1_fiq_invalid
	vtentry el1_error_invalid

	/* Lower EL using AArch64
	   在用户态的aarch64的程序发生了异常
	 */
	vtentry el0_sync_invalid
	vtentry el0_irq_invalid
	vtentry el0_fiq_invalid
	vtentry el0_error_invalid

	/* Lower EL using AArch32
	   在用户态的aarch32的程序发生了异常
	 */
	vtentry el0_sync_invalid
	vtentry el0_irq_invalid
	vtentry el0_fiq_invalid
	vtentry el0_error_invalid

el1_sync_invalid:
	inv_entry 1, BAD_SYNC
el1_irq_invalid:
	inv_entry 1, BAD_IRQ
el1_fiq_invalid:
	inv_entry 1, BAD_FIQ
el1_error_invalid:
	inv_entry 1, BAD_ERROR
el0_sync_invalid:
	inv_entry 0, BAD_SYNC
el0_irq_invalid:
	inv_entry 0, BAD_IRQ
el0_fiq_invalid:
	inv_entry 0, BAD_FIQ
el0_error_invalid:
	inv_entry 0, BAD_ERROR

string_test:
	.string "t"

.global trigger_alignment
trigger_alignment:
	ldr x0, =0x80002
	ldr x1, [x0]
	ret

不同异常问题构造:
ARM64学习笔记---建立异常向量表(二)-LMLPHP
这里.string “t” 只会占用2个字节,因此紧随其后的 “trigger_alignment” 会触发指令不对齐异常
ARM64学习笔记---建立异常向量表(二)-LMLPHP

07-10 03:38