ARM64 커널 시스템 호출 상세 정보(kernel-4.9 기반)

20214 단어 커널 노트
이 문서에서는 ARM64의 예를 들어 시스템 호출을 추가하는 방법에 대해 설명하고, 먼저 코드 실행 프로세스에 대해 설명합니다.
우선 이상 벡터표의 설정을 보십시오. 내부 핵은arch/arm64/kernel/entry에 있습니다.S 어셈블리 코드에 비정상 벡터 테이블이 설정되어 있습니다.
/*
 * Exception vectors.
 */
    .pushsection ".entry.text", "ax"
    .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)

위의 코드는 서로 다른 모델 아래의 이상 벡터표를 설정해도 이상은 4개 조로 나눌 수 있고 각 조의 이상은 4개 있기 때문에 여기에 모두 16개의 entry를 설정한다.4조의 이상은 각각 4가지 상황에서 이상이 발생할 때의 처리에 대응한다.위의 4개 그룹은 다음과 같은 4가지 상황에 순서대로 대응한다.
(1) ELx에서 ELx로 운영 수준 전환 없이 SP 사용EL0, 이 경우 Linuxkernel에서 처리되지 않으며 invalid entry를 사용합니다.
(2) ELx에서 ELx로 운영 단계 전환 없이 SP 사용ELx.이 경우 Linux에서 흔히 볼 수 있습니다.
(3) 이상은 레벨 전환을 통해 처리해야 하고aarch64 모드로 처리해야 한다. 예를 들어 64비트 사용자 모드 프로그램에서 시스템 호출이 발생하면 CPU는 EL0에서 EL1로 전환되고aarch64 모드로 이상을 처리한다.
(4) 이상은 단계 전환을 통해 처리하고aarch32 모드로 처리해야 한다.예를 들어 32개의 사용자 상태 프로그램이 시스템 호출을 일으키면 CPU는 EL0에서 EL1로 전환되고aarch32 모드로 처리된다.
앞에 이상 벡터표가 설정되어 있습니다. SVC 모드의 처리를 더 살펴보겠습니다.시스템이 호출될 때 CPU는 SVC mode로 전환되고 해당하는 주소로 이동합니다.
kernel에서 SVC Handler 두 개를 구성하여 각각 SVC32/SVC_64 두 가지 모델, 32bit 프로그램과 64bit 프로그램 실행 시스템 호출은 두 개의handler로 이동합니다.
커널은arch/arm64/kernel/entry에 있습니다.SVC 예외 entry가 S-어셈블리 코드에 설정되어 있습니다.

64-bit 실행 모드 확인


다음 함수는 64-bit 상태의 예외 벡터 테이블 설정을 설정합니다. 여기서 빨간색 부분은 svc handler 구성입니다.
arch/arm64/kernel/entry.S:

    .align  6
el0_sync:
    kernel_entry 0
    mrs x25, esr_el1            // read the syndrome register
    lsr x24, x25, #ESR_ELx_EC_SHIFT // exception class
    cmp x24, #ESR_ELx_EC_SVC64      // SVC in 64-bit state
    b.eq    el0_svc
    cmp x24, #ESR_ELx_EC_DABT_LOW   // data abort in EL0
    b.eq    el0_da
    cmp x24, #ESR_ELx_EC_IABT_LOW   // instruction abort in EL0
    b.eq    el0_ia
    cmp x24, #ESR_ELx_EC_FP_ASIMD   // FP/ASIMD access
    b.eq    el0_fpsimd_acc
    cmp x24, #ESR_ELx_EC_FP_EXC64   // FP/ASIMD exception
    b.eq    el0_fpsimd_exc
    cmp x24, #ESR_ELx_EC_SYS64      // configurable trap
    b.eq    el0_sys
    cmp x24, #ESR_ELx_EC_SP_ALIGN   // stack alignment exception
    b.eq    el0_sp_pc
    cmp x24, #ESR_ELx_EC_PC_ALIGN   // pc alignment exception
    b.eq    el0_sp_pc
    cmp x24, #ESR_ELx_EC_UNKNOWN    // unknown exception in EL0
    b.eq    el0_undef
    cmp x24, #ESR_ELx_EC_BREAKPT_LOW    // debug exception in EL0
    b.ge    el0_dbg
    b   el0_inv

el0_svc의 구현은 다음과 같습니다.
/*
 * SVC handler.
 */
    .align  6
el0_svc:
    adrp    stbl, sys_call_table        // load syscall table pointer
    uxtw    scno, w8            // syscall number in w8
    mov sc_nr, #__NR_syscalls
el0_svc_naked:                  // compat entry point
    stp x0, scno, [sp, #S_ORIG_X0]  // save the original x0 and syscall number
    enable_dbg_and_irq
    ct_user_exit 1
    ldr x16, [tsk, #TSK_TI_FLAGS]   // check for syscall hooks
    tst x16, #_TIF_SYSCALL_WORK
    b.ne    __sys_trace
    cmp     scno, sc_nr                     // check upper syscall limit
    b.hs    ni_sys
    ldr x16, [stbl, scno, lsl #3]   // address in the syscall table
    blr x16             // call sys_* routine
    b   ret_fast_syscall
ni_sys:
    mov x0, sp
    bl  do_ni_syscall
    b   ret_fast_syscall
ENDPROC(el0_svc)

보시면 sys를 찾으러 갈 거예요call_table 이 그룹을 찾아서 대응하는 시스템 호출 함수를 찾으십시오. 그 중 관건적인 함수do 가 있음을 주의하십시오.ni_syscall, (no implement syscall) 시스템 호출이 제한이나 문제에 부딪혔을 때 이 함수로 이동합니다.
sys_call_테이블의 정의는 다음 파일에 있습니다.
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 
};

이 그룹은 만들 때 우선 모든 그룹 구성원을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  6
 el0_sync_compat:
     kernel_entry 0, 32
     mrs x25, esr_el1            // read the syndrome register
     lsr x24, x25, #ESR_ELx_EC_SHIFT // exception class
     cmp x24, #ESR_ELx_EC_SVC32      // SVC in 32-bit state
     b.eq    el0_svc_compat
     cmp x24, #ESR_ELx_EC_DABT_LOW   // data abort in EL0
     b.eq    el0_da
     cmp x24, #ESR_ELx_EC_IABT_LOW   // instruction abort in EL0
     b.eq    el0_ia
     cmp x24, #ESR_ELx_EC_FP_ASIMD   // FP/ASIMD access
     b.eq    el0_fpsimd_acc
     cmp x24, #ESR_ELx_EC_FP_EXC32   // FP/ASIMD exception
     b.eq    el0_fpsimd_exc
     cmp x24, #ESR_ELx_EC_PC_ALIGN   // pc alignment exception
     b.eq    el0_sp_pc
     cmp x24, #ESR_ELx_EC_UNKNOWN    // unknown exception in EL0
     b.eq    el0_undef
     cmp x24, #ESR_ELx_EC_CP15_32    // CP15 MRC/MCR trap
     b.eq    el0_undef
     cmp x24, #ESR_ELx_EC_CP15_64    // CP15 MRRC/MCRR trap
     b.eq    el0_undef
     cmp x24, #ESR_ELx_EC_CP14_MR    // CP14 MRC/MCR trap
     b.eq    el0_undef
     cmp x24, #ESR_ELx_EC_CP14_LS    // CP14 LDC/STC trap
     b.eq    el0_undef
     cmp x24, #ESR_ELx_EC_CP14_64    // CP14 MRRC/MCRR trap
     b.eq    el0_undef
     cmp x24, #ESR_ELx_EC_BREAKPT_LOW    // debug exception in EL0
     b.ge    el0_dbg
     b   el0_inv
 el0_svc_compat:
     /*
      * AArch32 syscall handling
      */
     adrp    stbl, compat_sys_call_table // load compat syscall table pointer
     uxtw    scno, w7            // syscall number in w7 (r7)
     mov     sc_nr, #__NR_compat_syscalls
     b   el0_svc_naked

     .align  6
 el0_irq_compat:
     kernel_entry 0, 32
     b   el0_irq_naked
 #endif

el0_svc_compat의 구현은 다음과 같습니다.
el0_svc_compat:
    /*
     * AArch32 syscall handling
     */
    adrp    stbl, compat_sys_call_table // load compat syscall table pointer
    uxtw    scno, w7            // syscall number in w7 (r7)
    mov     sc_nr, #__NR_compat_syscalls
    b   el0_svc_naked
    .align  6
el0_irq_compat:
    kernel_entry 0, 32
    b   el0_irq_naked

보시면 compat을 찾으러 갈 거예요sys_call_table 이 그룹을 찾아서 대응하는 시스템 호출 함수를 찾으십시오.compatsys_call_테이블의 정의는 다음 파일에 있습니다.
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 
 };

이 그룹은 만들 때 우선 모든 그룹 구성원을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_COMPAT
    long ret;
    if (is_compat_task()) {
        ret = compat_arm_syscall(regs);
        if (ret != -ENOSYS)
            return ret;
    }
#endif
    if (show_unhandled_signals_ratelimited()) {
        pr_info("%s[%d]: syscall %d
"
, current->comm task_pid_nr(current), (int)regs->syscallno); dump_instr("", regs); if (user_mode(regs)) __show_regs(regs); } return sys_ni_syscall(); }

본고는kernel-4.9버전, 오리지널 문장을 바탕으로 옮겨 싣고 표시해 주십시오.

좋은 웹페이지 즐겨찾기