点击查看系列文章 =》 Interrupt Pipeline系列文章大纲-优快云博客
原创不易,需要大家多多鼓励!您的关注、点赞、收藏就是我的创作动力!
5.1. ARM64 Linux系统调用
5.1.1 用户层:用hello world演示系统调用
5.1.2 内核层:ARM64 Linux系统调用的流程
参考之前分析的《2.2 ARM64异常处理》,对于用户层(EL0)触发的同步异常,其对应的异常向量表表项是“kernel_ventry 0,sync”,最终跳转到el0_sync。
第一步,从el0_sync跳转到el0_svc。
同步异常有很多种,需要根据同步异常的类别,跳转到不同的处理程序。对应syscall,最终跳转到el0_svc。
第653行,mrs 指令将系统寄存器esr_el1的内容复制到通用寄存器x25。ESR_EL1(Exception Syndrome Register at Exception Level 1)是 ARMv8-A 架构中的一个系统寄存器,用于提供关于异常的信息。当处理器进入异常处理模式时,ESR_EL1 寄存器会被自动填充有关引起异常的详细信息。
第654行,lsr 指令执行逻辑右移操作。这里,x25 的值被右移 ESR_ELx_EC_SHIFT(26)位,以提取出异常类(exception class),存入x24。ESR_EL1 是一个 32 位寄存器,其各个字段提供了不同类型的异常信息。其中 ESR_EL1 [31:26]表示异常的类别(exception class),这是识别异常类型的关键部分。
第655行~656行,对x24 中的异常类与 ESR_ELx_EC_SVC64(0x15) 常量进行比较,检查是否为 64 位状态下的 SVC(Supervisor Call)异常。如果异常确实是 64 位状态下的 SVC,则跳转到 el0_svc 处理程序。
第二步,从el0_svc跳转到c函数el0_svc_handler
el0_svc非常简单,跳转到c函数el0_svc_handler。
第三步,从el0_svc_handler调用el0_svc_common
第一个入参regs,是struct pt_regs *regs,在处理系统调用和异常时,它用于保存处理器寄存器的状态,当用户空间程序进入内核模式(例如通过系统调用或异常)时,这些寄存器的值会被保存到 pt_regs 结构体中。
第二个入参scno的值,是从x8读出的系统调用号;
第三个入参为宏定义__NR_syscalls(294),它表示系统调用表的大小,即支持的最大系统调用编号加一,定义在include/uapi/asm-generic/unistd.h。
第四个入参是函数指针数组sys_call_table,也称为系统调用表,后面用到的时候再展开。它定义在arch/arm64/kernel/sys.c。
第四步,el0_svc_common调用invoke_syscall
el0_svc_common把自身的四个入参直接传递给invoke_syscall。
第45行,检查给定的系统调用号是否在有效的范围内(小于 sc_nr)。sc_nr传入的值为__NR_syscalls。即如果不在有效范围内,则会跳到第50行调用 do_ni_syscall 处理未知或不支持的系统调用。
第46~48行,使用 array_index_nospec 函数来确保索引的安全性,防止越界访问。然后从 syscall_table 中获取系统调用号所对应的处理函数指针,保存到syscall_fn。最后,通过__invoke_syscall执行syscall_fn(regs)。
以上文中hello world程序使用的系统调用write为例,参考上图的调用堆栈,其对应的syscall_fn就是__arm64_sys_write。
__arm64_sys_write是如何定义的?
又是如何插入sys_call_table的呢?
下一章《5.1.3 内核层:ARM64 Linux系统调用的定义》
点击查看系列文章 =》 Interrupt Pipeline系列文章大纲-优快云博客
原创不易,需要大家多多鼓励!您的关注、点赞、收藏就是我的创作动力!