本文以ARM64为例,介绍如何添加系统调用,首先来介绍一些代码执行流程:

首先来看异常向量表的配置,内核在arch/arm64/kernel/entry.S汇编代码中设置了异常向量表。

/** Exception vectors.*/.pushsection ".entry.text", "ax".align  11
ENTRY(vectors)kernel_ventry   1, sync_invalid         // Synchronous EL1tkernel_ventry   1, irq_invalid          // IRQ EL1tkernel_ventry   1, fiq_invalid          // FIQ EL1tkernel_ventry   1, error_invalid        // Error EL1tkernel_ventry   1, sync             // Synchronous EL1hkernel_ventry   1, irq              // IRQ EL1hkernel_ventry   1, fiq_invalid          // FIQ EL1hkernel_ventry   1, error_invalid        // Error EL1hkernel_ventry   0, sync             // Synchronous 64-bit EL0kernel_ventry   0, irq              // IRQ 64-bit EL0kernel_ventry   0, fiq_invalid          // FIQ 64-bit EL0kernel_ventry   0, error_invalid        // Error 64-bit EL0#ifdef CONFIG_COMPATkernel_ventry   0, sync_compat, 32      // Synchronous 32-bit EL0kernel_ventry   0, irq_compat, 32       // IRQ 32-bit EL0kernel_ventry   0, fiq_invalid_compat, 32   // FIQ 32-bit EL0kernel_ventry   0, error_invalid_compat, 32 // Error 32-bit EL0
#elsekernel_ventry   0, sync_invalid, 32     // Synchronous 32-bit EL0kernel_ventry   0, irq_invalid, 32      // IRQ 32-bit EL0kernel_ventry   0, fiq_invalid, 32      // FIQ 32-bit EL0kernel_ventry   0, error_invalid, 32        // Error 32-bit EL0
#endif
END(vectors)

上面的代码进一步展开,即使就是设置不同mode下的异常向量表,异常可以分为4组,每组异常有4个,所以这里一共会设置16个entry。4组异常分别对应4种情况下发生异常时的处理。上面的4组,按照顺序分别对应如下4中情况:

(1)运行级别不发生切换,从ELx变化到ELx,使用SP_EL0,这种情况在Linux kernel都是不处理的,使用invalid entry。

(2)运行级别不发生切换,从ELx变化到ELx,使用SP_ELx。这种情况下在Linux中比较常见。

(3)异常需要进行级别切换来进行处理,并且使用aarch64模式处理,比如64位用户态程序发生系统调用,CPU会从EL0切换到EL1,并且使用aarch64模式处理异常。

(4)异常需要进行级别切换来进行处理,并且使用aarch32模式处理。比如32位用户态程序发生系统调用,CPU会从EL0切换到EL1,并且使用aarch32模式进行处理。

前面设置了异常向量表,我们来进一步查看SVC mode的处理。当系统调用时CPU会切换到SVC mode,并跳转到对应的地址去运行。

kernel中会配置两个SVC Handler,分别对应这SVC_32/SVC_64两种mode,32bit程序和64bit程序执行系统调用会跳转到两个不同的handler去执行。

内核在arch/arm64/kernel/entry.S汇编代码中设置了SVC异常entry。

64-bit运行模式解析

如下函数设置了64-bit状态下的异常向量表设置,其中红色部分是svc handler配置:

arch/arm64/kernel/entry.S:.align  6
el0_sync:kernel_entry 0mrs x25, esr_el1            // read the syndrome registerlsr x24, x25, #ESR_ELx_EC_SHIFT // exception classcmp x24, #ESR_ELx_EC_SVC64      // SVC in 64-bit stateb.eq    el0_svccmp x24, #ESR_ELx_EC_DABT_LOW   // data abort in EL0b.eq    el0_dacmp x24, #ESR_ELx_EC_IABT_LOW   // instruction abort in EL0b.eq    el0_iacmp x24, #ESR_ELx_EC_FP_ASIMD   // FP/ASIMD accessb.eq    el0_fpsimd_acccmp x24, #ESR_ELx_EC_FP_EXC64   // FP/ASIMD exceptionb.eq    el0_fpsimd_exccmp x24, #ESR_ELx_EC_SYS64      // configurable trapb.eq    el0_syscmp x24, #ESR_ELx_EC_SP_ALIGN   // stack alignment exceptionb.eq    el0_sp_pccmp x24, #ESR_ELx_EC_PC_ALIGN   // pc alignment exceptionb.eq    el0_sp_pccmp x24, #ESR_ELx_EC_UNKNOWN    // unknown exception in EL0b.eq    el0_undefcmp x24, #ESR_ELx_EC_BREAKPT_LOW    // debug exception in EL0b.ge    el0_dbgb   el0_inv

el0_svc的实现如下:

/** SVC handler.*/.align  6
el0_svc:adrp    stbl, sys_call_table        // load syscall table pointeruxtw    scno, w8            // syscall number in w8mov sc_nr, #__NR_syscalls
el0_svc_naked:                  // compat entry pointstp x0, scno, [sp, #S_ORIG_X0]  // save the original x0 and syscall numberenable_dbg_and_irqct_user_exit 1ldr x16, [tsk, #TSK_TI_FLAGS]   // check for syscall hookstst x16, #_TIF_SYSCALL_WORKb.ne    __sys_tracecmp     scno, sc_nr                     // check upper syscall limitb.hs    ni_sysldr x16, [stbl, scno, lsl #3]   // address in the syscall tableblr x16             // call sys_* routineb   ret_fast_syscall
ni_sys:mov x0, spbl  do_ni_syscallb   ret_fast_syscall
ENDPROC(el0_svc)

可以看到它会去查找sys_call_table这个数组并找到对应的系统调用函数去执行,注意其中有一个关键函数do_ni_syscall,(no implement syscall),当系统调用遇到一些限制或者问题时会跳转到该函数去执行。

sys_call_table的定义在如下文件中:

arch/arm64/kernel/sys.c:
/** The sys_call_table array must be 4K aligned to be accessible from* kernel/entry.S.*/
void * const sys_call_table[__NR_syscalls] __aligned(4096) = {[0 ... __NR_syscalls - 1] = sys_ni_syscall,
#include <asm/unistd.h>
};

这个数组在创建时首先会把所有的数组成员设置为sys_ni_syscall,而后根据asm/unistd.h中的内容做进一步初始化。其实最终该头文件会把include/uapi/asm-generic//unistd.h包含进来,也就是这个头文件会是最终定义数组的地方。

......
__SYSCALL(__NR_epoll_wait, sys_epoll_wait)
#define __NR_ustat 1070
__SYSCALL(__NR_ustat, sys_ustat)
#define __NR_vfork 1071
__SYSCALL(__NR_vfork, sys_vfork)
#define __NR_oldwait4 1072
__SYSCALL(__NR_oldwait4, sys_wait4)
#define __NR_recv 1073
__SYSCALL(__NR_recv, sys_recv)
#define __NR_send 1074
__SYSCALL(__NR_send, sys_send)
#define __NR_bdflush 1075
__SYSCALL(__NR_bdflush, sys_bdflush)
#define __NR_umount 1076
__SYSCALL(__NR_umount, sys_oldumount)
#define __ARCH_WANT_SYS_OLDUMOUNT
#define __NR_uselib 1077
__SYSCALL(__NR_uselib, sys_uselib)
#define __NR__sysctl 1078
__SYSCALL(__NR__sysctl, sys_sysctl)
#define __NR_fork 1079
#ifdef CONFIG_MMU
__SYSCALL(__NR_fork, sys_fork)
#else
__SYSCALL(__NR_fork, sys_ni_syscall)
#endif /* CONFIG_MMU */
......

32-bit运行模式解析

如下函数设置了32-bit状态下的异常向量表设置,其中红色部分是svc handler配置:

arch/arm64/kernel/entry.S#ifdef CONFIG_COMPAT.align  6el0_sync_compat:kernel_entry 0, 32mrs x25, esr_el1            // read the syndrome registerlsr x24, x25, #ESR_ELx_EC_SHIFT // exception classcmp x24, #ESR_ELx_EC_SVC32      // SVC in 32-bit stateb.eq    el0_svc_compatcmp x24, #ESR_ELx_EC_DABT_LOW   // data abort in EL0b.eq    el0_dacmp x24, #ESR_ELx_EC_IABT_LOW   // instruction abort in EL0b.eq    el0_iacmp x24, #ESR_ELx_EC_FP_ASIMD   // FP/ASIMD accessb.eq    el0_fpsimd_acccmp x24, #ESR_ELx_EC_FP_EXC32   // FP/ASIMD exceptionb.eq    el0_fpsimd_exccmp x24, #ESR_ELx_EC_PC_ALIGN   // pc alignment exceptionb.eq    el0_sp_pccmp x24, #ESR_ELx_EC_UNKNOWN    // unknown exception in EL0b.eq    el0_undefcmp x24, #ESR_ELx_EC_CP15_32    // CP15 MRC/MCR trapb.eq    el0_undefcmp x24, #ESR_ELx_EC_CP15_64    // CP15 MRRC/MCRR trapb.eq    el0_undefcmp x24, #ESR_ELx_EC_CP14_MR    // CP14 MRC/MCR trapb.eq    el0_undefcmp x24, #ESR_ELx_EC_CP14_LS    // CP14 LDC/STC trapb.eq    el0_undefcmp x24, #ESR_ELx_EC_CP14_64    // CP14 MRRC/MCRR trapb.eq    el0_undefcmp x24, #ESR_ELx_EC_BREAKPT_LOW    // debug exception in EL0b.ge    el0_dbgb   el0_invel0_svc_compat:/** AArch32 syscall handling*/adrp    stbl, compat_sys_call_table // load compat syscall table pointeruxtw    scno, w7            // syscall number in w7 (r7)mov     sc_nr, #__NR_compat_syscallsb   el0_svc_naked.align  6el0_irq_compat:kernel_entry 0, 32b   el0_irq_naked#endif

el0_svc_compat的实现如下:

el0_svc_compat:/** AArch32 syscall handling*/adrp    stbl, compat_sys_call_table // load compat syscall table pointeruxtw    scno, w7            // syscall number in w7 (r7)mov     sc_nr, #__NR_compat_syscallsb   el0_svc_naked.align  6
el0_irq_compat:kernel_entry 0, 32b   el0_irq_naked

可以看到它会去查找compat_sys_call_table这个数组并找到对应的系统调用函数去执行,compat_sys_call_table的定义在如下文件中:

arch/arm64/kernel/sys32.c:/** The sys_call_table array must be 4K aligned to be accessible from* kernel/entry.S.*/void * const compat_sys_call_table[__NR_compat_syscalls] __aligned(4096) = {[0 ... __NR_compat_syscalls - 1] = sys_ni_syscall,#include <asm/unistd32.h>};

这个数组在创建时首先会把所有的数组成员设置为sys_ni_syscall,而后根据asm/unistd32.h中的内容做进一步初始化。其实最终该头文件会把arch/arm64/include/asm/unistd32.h包含进来,也就是这个头文件会是最终定义函数数组的地方。

arch/arm64/include/asm/unistd32.h:
......__SYSCALL(__NR_process_vm_writev, compat_sys_process_vm_writev)#define __NR_kcmp 378__SYSCALL(__NR_kcmp, sys_kcmp)#define __NR_finit_module 379__SYSCALL(__NR_finit_module, sys_finit_module)#define __NR_sched_setattr 380__SYSCALL(__NR_sched_setattr, sys_sched_setattr)#define __NR_sched_getattr 381__SYSCALL(__NR_sched_getattr, sys_sched_getattr)#define __NR_renameat2 382__SYSCALL(__NR_renameat2, sys_renameat2)#define __NR_seccomp 383__SYSCALL(__NR_seccomp, sys_seccomp)#define __NR_getrandom 384__SYSCALL(__NR_getrandom, sys_getrandom)#define __NR_memfd_create 385__SYSCALL(__NR_memfd_create, sys_memfd_create)#define __NR_bpf 386__SYSCALL(__NR_bpf, sys_bpf)#define __NR_execveat 387__SYSCALL(__NR_execveat, compat_sys_execveat)#define __NR_userfaultfd 388__SYSCALL(__NR_userfaultfd, sys_userfaultfd)#define __NR_membarrier 389__SYSCALL(__NR_membarrier, sys_membarrier)#define __NR_mlock2 390__SYSCALL(__NR_mlock2, sys_mlock2)#define __NR_copy_file_range 391__SYSCALL(__NR_copy_file_range, sys_copy_file_range)#define __NR_preadv2 392__SYSCALL(__NR_preadv2, compat_sys_preadv2)#define __NR_pwritev2 393__SYSCALL(__NR_pwritev2, compat_sys_pwritev2)
......

最后来看一下do_ni_syscall,内核中没有意义的系统调用号都会执行到该函数上面:

asmlinkage long do_ni_syscall(struct pt_regs *regs)
{
#ifdef CONFIG_COMPATlong ret;if (is_compat_task()) {ret = compat_arm_syscall(regs);if (ret != -ENOSYS)return ret;}
#endifif (show_unhandled_signals_ratelimited()) {pr_info("%s[%d]: syscall %d\n", current->commtask_pid_nr(current), (int)regs->syscallno);dump_instr("", regs);if (user_mode(regs))     __show_regs(regs);} return sys_ni_syscall();
}

本文基于kernel-4.9版本,原创文章,转载请标注。

ARM64内核系统调用详解(基于kernel-4.9)相关推荐

  1. linux内核makefile详解,linux kernel编译Makefile和Kconfig,make menuconfig详解

    Sam需要看看2.6 kernel中USB Mouse的代码.顺便谈谈Kernel中Makefile和Kconfig文件的关系以及配合使用. 背景知识: 背景知识一:Kconfig介绍: 在#make ...

  2. Linux系统调用详解(实现机制分析)

    为什么需要系统调用   linux内核中设置了一组用于实现系统功能的子程序,称为系统调用.系统调用和普通库函数调用非常相似,只是系统调用由操作系统核心提供,运行于内核态,而普通的函数调用由函数库或用户 ...

  3. linux内核中断详解

    linux内核中断详解 1.中断的硬件触发流程 外设:如果外设有操作或者有数据可用,那么就会产生一个电信号,这个电信号发送给中断控制器. 中断控制器:中断控制器接收到外设发来的电信号以后,进行进一步的 ...

  4. linux 2.4内核编译,linux 2.4内核编译详解

    2.4内核编译详解 内核简介 内核,是一个操作系统的核心.它负责管理系统的进程.内存.设备驱动程序.文件和网络系统,决定着系统的性能和稳定性. Linux的一个重要的特点就是其源代码的公开性,所有的内 ...

  5. oracle安装过程中内核参数详解

    转载网址:https://www.cnblogs.com/colben/p/4120439.html 在安装Oracle的时候需要调整linux的内核参数,但是各参数代表什么含义呢,下面做详细解析. ...

  6. 网络驱动开发样例snull详解(基于3.10.0)

    网络驱动开发样例snull详解(基于3.10.0) 本章素材为ldd3书中的网络驱动snull部分.由于现在内核的更新,导致其在最新的内核中无法编译该网络驱动,需要针对修改,顾为此文(内核3.10.0 ...

  7. 详解基于 Cortex-M3 的任务调度(下)

    文章目录 工程说明 实验结果 代码讲解 时钟节拍 任务切换 task_switch() PendSV_Handler 任务的代码 重要的全局变量 main() 函数 代码下载 在 详解基于 Corte ...

  8. 《嵌入式Linux软硬件开发详解——基于S5PV210处理器》——2.2 DDR2 SDRAM芯片

    本节书摘来自异步社区<嵌入式Linux软硬件开发详解--基于S5PV210处理器>一书中的第2章,第2.2节,作者 刘龙,更多章节内容可以访问云栖社区"异步社区"公众号 ...

  9. python selenium爬虫_详解基于python +Selenium的爬虫

    详解基于python +Selenium的爬虫 一.背景 1. Selenium Selenium 是一个用于web应用程序自动化测试的工具,直接运行在浏览器当中,支持chrome.firefox等主 ...

最新文章

  1. Sublime text 2/3 中 Package Control 的安装与使用方法
  2. 大神推荐:国内较强的NLP高校实验室有哪些?
  3. 数据结构:用栈实现表达式的转换(文字描述+详细步骤示例)——中缀转后缀
  4. 决策树学习笔记整理【转】
  5. 前端面试时面试官想要听到什么答案(关于一些Vue的问题)
  6. MyCat分布式数据库集群架构工作笔记0005---Mycat的安装
  7. 19. PHP 表单验证 - 必填字段
  8. 高性能Mysql中文版
  9. rabbitmq 存入mysql_将RabbitMQ使用者数据保存到数据库中
  10. 单位旧计算机处理,单位出售旧电脑增值税税率是多少?
  11. 蓝桥杯(java)个人赛真题:书号验证
  12. PLC无线通讯方案,支持西门子,三菱,欧姆龙,台达等各品牌PLC
  13. 生成组合仿射变换矩阵,裁剪+缩放+平移+斜切+旋转
  14. Myeclipse 是如何启动tomcat服务
  15. 困兽之斗--乐视2017暑期实习生笔试题(二)
  16. 华为OJ(MP3光标移动)
  17. 光伏多峰最大功率点跟踪MPPT MATLAB/Simulink仿真模型
  18. 被周鸿祎夸奖的闷骚型产品经理,究竟是如何做产品的?
  19. 编程开发:Linux网络编程学习笔记
  20. 打开CHM文件是空白的

热门文章

  1. 人生不设限 没有不可能
  2. 办公室必备-上班偷看小说利器
  3. 晶体三极管的主要参数
  4. python练习题 21-30
  5. 时间戳转换工具java_时间戳转换_时间戳转换工具_时间戳转换成时间日期_55查询...
  6. 2017“中国好SaaS”上海站Top3出炉,企业级SaaS创业正在向产业化迈进
  7. 绝对路径! 报错:[gazebo-2] process has died [pid 2382, exit code 134
  8. 周鸿祎的互联网方法论:用户至上与颠覆式创新
  9. 微信小程序之微信登入
  10. 国内人才申领《上海市居住证》审核试行办法