이번 시간에는 RISC-V 기반 리눅스 커널에 구현된 익셉션 벡터 테이블과 익셉션 벡터 핸들러를 분석하겠습니다.
익셉션 벡터 핸들러를 호출하는 코드 확인하기
다음은 익셉션 벡터 핸들러의 구현부입니다.
https://elixir.bootlin.com/linux/v5.4.30/source/arch/riscv/kernel/entry.S
ENTRY(excp_vect_table)
RISCV_PTR do_trap_insn_misaligned
RISCV_PTR do_trap_insn_fault
RISCV_PTR do_trap_insn_illegal
RISCV_PTR do_trap_break
RISCV_PTR do_trap_load_misaligned
RISCV_PTR do_trap_load_fault
RISCV_PTR do_trap_store_misaligned
RISCV_PTR do_trap_store_fault
RISCV_PTR do_trap_ecall_u /* system call, gets intercepted */
RISCV_PTR do_trap_ecall_s
RISCV_PTR do_trap_unknown
RISCV_PTR do_trap_ecall_m
RISCV_PTR do_page_fault /* instruction page fault */
RISCV_PTR do_page_fault /* load page fault */
RISCV_PTR do_trap_unknown
RISCV_PTR do_page_fault /* store page fault */
excp_vect_table_end:
보다시피, 익셉션 벡터에는 16개의 핸들러로 구성됐다는 사실을 알 수 있습니다.
익셉션의 종류는 크게 메모리 폴트(소프트웨어적으로 치명적인 에러)와 소프트웨어 인터럽트로 구분할 수 있는데, 다음은 메모리 폴트가 발생할 때 브랜치돼 실행되는 익셉션 벡터 핸들러입니다.
* RISCV_PTR do_trap_insn_misaligned
* RISCV_PTR do_trap_insn_fault
* RISCV_PTR do_trap_insn_illegal
* RISCV_PTR do_trap_load_misaligned
* RISCV_PTR do_trap_load_fault
* RISCV_PTR do_trap_store_misaligned
* RISCV_PTR do_trap_store_fault
* RISCV_PTR do_page_fault /* instruction page fault */
* RISCV_PTR do_page_fault /* load page fault */
* RISCV_PTR do_trap_unknown
* RISCV_PTR do_page_fault /* store page fault */
다음 익셉션 벡터 핸들러는 유저 공간에서 시스템 콜이 발생할 때 처리됩니다.
* RISCV_PTR do_trap_ecall_u /* system call, gets intercepted */
이번 시간에는 익셉션 벡터 테이블을 리뷰하는 수준으로 코드를 분석합니다만, 다음에는 RISC-V 어셈블리 명령어로 구현된 익셉션 벡터에 대해 분석할 예정입니다.
익셉션 벡터 핸들러의 선언부
익셉션 종류 별로 선언된 익셉션 벡터 핸들러의 선언부는 다음과 같습니다.
https://elixir.bootlin.com/linux/v5.4.30/source/arch/riscv/kernel/traps.c
DO_ERROR_INFO(do_trap_unknown,
SIGILL, ILL_ILLTRP, "unknown exception");
DO_ERROR_INFO(do_trap_insn_misaligned,
SIGBUS, BUS_ADRALN, "instruction address misaligned");
DO_ERROR_INFO(do_trap_insn_fault,
SIGSEGV, SEGV_ACCERR, "instruction access fault");
DO_ERROR_INFO(do_trap_insn_illegal,
SIGILL, ILL_ILLOPC, "illegal instruction");
DO_ERROR_INFO(do_trap_load_misaligned,
SIGBUS, BUS_ADRALN, "load address misaligned");
DO_ERROR_INFO(do_trap_load_fault,
SIGSEGV, SEGV_ACCERR, "load access fault");
DO_ERROR_INFO(do_trap_store_misaligned,
SIGBUS, BUS_ADRALN, "store (or AMO) address misaligned");
DO_ERROR_INFO(do_trap_store_fault,
SIGSEGV, SEGV_ACCERR, "store (or AMO) access fault");
DO_ERROR_INFO(do_trap_ecall_u,
SIGILL, ILL_ILLTRP, "environment call from U-mode");
DO_ERROR_INFO(do_trap_ecall_s,
SIGILL, ILL_ILLTRP, "environment call from S-mode");
DO_ERROR_INFO(do_trap_ecall_m,
SIGILL, ILL_ILLTRP, "environment call from M-mode");
다들 DO_ERROR_INFO라는 매크로를 선언해 각각 함수를 선언했는데, DO_ERROR_INFO 매크로의 정체는 다음과 같습니다.
https://elixir.bootlin.com/linux/v5.4.30/source/arch/riscv/kernel/traps.c
#define DO_ERROR_INFO(name, signo, code, str) \
asmlinkage __visible void name(struct pt_regs *regs) \
{ \
do_trap_error(regs, signo, code, regs->sepc, "Oops - " str); \
}
DO_ERROR_INFO 매크로는 첫 번째 인자로 전달되는 함수 이름을 바탕으로 함수를 선언합니다. 함수의 구현부는 do_trap_error() 함수를 호출하는 간단한 코드로 구성돼 있습니다.
정리
이번 포스트에서는 RISC-V에서의 익셉션 벡터 핸들러의 종류와 선언부를 간단히 리뷰했습니다. "대략 이런 코드로 구성돼 있다"라는 정도로 코드를 산책해 봤습니다.
다음 시간에는 어셈블리 명령어로 RISC-V의 익셉션 벡터와 익셉션 벡터 테이블의 구현부를 분석하겠습니다.
최근 덧글