← Architecture 비교DUJINLABS.COM

Linux 6.18.37 LTS · Architecture comparison 08/21

Exception entry: vector table, IDT와 trap entry

CPU가 비동기 interrupt, synchronous fault, debug exception을 감지한 순간부터 pt_regs가 완성되고 C handler로 넘어갈 때까지를 세 ISA에서 나란히 읽습니다.

비교 대상
arm64 / x86-64 / RISC-V
실제 원본
3 files · 263 annotated lines
기준 tag
Linux v6.18.37
분석 축
state · ordering · lifetime · latency

01 · QUESTION

무엇을 확인할 것인가

예외가 들어왔을 때 어느 stack을 선택하고, user register를 어디까지 저장하며, kernel이 다시 sleep 가능한 context라고 판단하는 시점은 언제인가?

모든 architecture는 hardware가 제공한 최소 상태를 kernel entry frame으로 확장하고 exception class를 해석한 뒤 공통 irq, fault, syscall 계층으로 보낸다. 다만 hardware가 자동 저장하는 값, vector 주소 계산법, privilege 전환 때 stack을 바꾸는 방법은 서로 다르다.

assembly entry가 pt_regs를 완성하기 전에는 C ABI와 scheduler의 일반 가정을 적용할 수 없다. NMI와 같은 재진입 경로, user access 차단, speculation 방어, lockdep/RCU 진입은 정해진 순서로만 호출해야 한다.

지연 시간 관점entry latency는 register save, privilege 경계 방어, vector dispatch, interrupt masking과 cache miss의 합이다. NMI/FIQ 급 경로는 일반 IRQ보다 우선하지만 handler가 긴 critical section에 막히면 tail latency가 늘어난다.

02 · CONTRACT

공통 계약과 architecture 구현

architecture핵심 mechanism실패 형태확인할 상태
arm64VBAR_EL1 vector slot과 EL0/EL1 sync, IRQ, FIQ, SError 분기잘못된 vector offset, 불완전한 register save 또는 PAN 복원 누락은 return 직후 register corruption이나 kernel user-memory 접근 허용으로 나타난다.VBAR_EL1, ESR_EL1.EC, ELR_EL1, FAR_EL1, SPSR_EL1과 pt_regs의 pc/pstate/sp를 함께 덤프한다.
x86-64IDT gate와 entry_64.S의 idtentry macroerror-code 유무를 잘못 맞추면 모든 frame offset이 한 word씩 밀린다. SWAPGS 판단 실패는 per-CPU 주소를 user GSBASE로 해석하는 치명적 오류를 만든다.IDT descriptor, vector number, error_code, CS privilege, RSP, CR2와 ORC unwind 결과를 확인한다.
RISC-VSTVEC가 가리키는 handle_exception과 SCAUSE dispatchkernel trap에서 user stack으로 오인하거나 sscratch를 늦게 복원하면 nested trap이 잘못된 task와 stack을 사용한다.STVEC, SCAUSE interrupt bit와 code, SEPC, STVAL, SSTATUS.SPP/SPIE, sscratch와 pt_regs.sp를 기록한다.

03 · DIAGRAMS

세 그림으로 먼저 읽기

그림 1. 같은 목적, 서로 다른 mechanism각 ISA에서 실제로 추적할 state와 checkpoint를 한 줄에 맞췄습니다.

arm64

mechanism
VBAR_EL1 vector slot과 EL0/EL1 sync, IRQ, FIQ, SError 분기
state
hardware는 ELR_EL1, SPSR_EL1, ESR_EL1, FAR_EL1 등을 system register에 남긴다. vector assembly가 x0-x30, SP와 원래 PSTATE를 pt_regs로 옮긴 뒤 el0t_64_sync_handler()가 ESR exception class를 syscall, abort, FP trap 등으로 분기한다.
checkpoint
VBAR_EL1, ESR_EL1.EC, ELR_EL1, FAR_EL1, SPSR_EL1과 pt_regs의 pc/pstate/sp를 함께 덤프한다.

x86-64

mechanism
IDT gate와 entry_64.S의 idtentry macro
state
일부 exception은 CPU가 error code를 push하고 privilege가 바뀌면 SS, RSP, RFLAGS, CS, RIP도 hardware frame에 쌓는다. entry macro가 빠진 register를 저장해 pt_regs 형태를 맞추고 IST가 설정된 vector는 TSS의 전용 stack으로 전환한다.
checkpoint
IDT descriptor, vector number, error_code, CS privilege, RSP, CR2와 ORC unwind 결과를 확인한다.

RISC-V

mechanism
STVEC가 가리키는 handle_exception과 SCAUSE dispatch
state
trap 진입 시 hardware는 SEPC, SCAUSE, STVAL과 SSTATUS만 갱신하고 일반 register는 저장하지 않는다. assembly가 sscratchtp를 이용해 user/kernel trap을 구별하고 task kernel stack의 pt_regs에 x1-x31을 저장한다.
checkpoint
STVEC, SCAUSE interrupt bit와 code, SEPC, STVAL, SSTATUS.SPP/SPIE, sscratch와 pt_regs.sp를 기록한다.
그림 2. 공통 kernel과 architecture hook의 소유권공통 정책이 hardware state를 직접 소유하지 않는 경계를 표시합니다.
Linux common contract모든 architecture는 hardware가 제공한 최소 상태를 kernel entry frame으로 확장하고 exception class를 해석한 뒤 공통 irq, fault, syscall 계층으로 보낸다. 다만 hardware가 자동 저장하는 값, vector 주소 계산법, privilege 전환 때 stack을 바꾸는 방법은 서로 다르다.
arm64VBAR_EL1 vector slot과 EL0/EL1 sync, IRQ, FIQ, SError 분기DAIF mask, PAN/UAO, MTE tag check, speculation barrier와 context tracking 순서가 user memory 접근 가능 시점을 결정한다.
x86-64IDT gate와 entry_64.S의 idtentry macroSWAPGS, CR3/KPTI 전환, speculation 방어와 irqentry_enter 순서가 NMI 재진입과 user GSBASE 안전성을 좌우한다.
RISC-VSTVEC가 가리키는 handle_exception과 SCAUSE dispatchSUM 차단, FS/VS 상태, interrupt enable bit와 sscratch 교환 순서가 재진입 가능한 시점을 정한다.
lifetime boundarypt_regs는 현재 kernel stack 또는 별도 exception stack에 존재하며 handler와 signal/ptrace가 참조하는 동안 유지된다. nested exception은 바깥 frame을 덮지 않도록 stack 경계와 nesting counter를 보존해야 한다.
그림 3. publication과 관찰 순서state를 준비한 뒤 architecture ordering을 거쳐 관찰 가능한 checkpoint가 됩니다.
arm64state 준비DAIF mask, PAN/UAO, MTE tag check, speculation barrier와 context tracking 순서가 user memory 접근 가능 시점을 결정한다.관찰: VBAR_EL1, ESR_EL1.EC, ELR_EL1, FAR_EL1, SPSR_EL1과 pt_regs의 pc/pstate/sp를 함께 덤프한다.
x86-64state 준비SWAPGS, CR3/KPTI 전환, speculation 방어와 irqentry_enter 순서가 NMI 재진입과 user GSBASE 안전성을 좌우한다.관찰: IDT descriptor, vector number, error_code, CS privilege, RSP, CR2와 ORC unwind 결과를 확인한다.
RISC-Vstate 준비SUM 차단, FS/VS 상태, interrupt enable bit와 sscratch 교환 순서가 재진입 가능한 시점을 정한다.관찰: STVEC, SCAUSE interrupt bit와 code, SEPC, STVAL, SSTATUS.SPP/SPIE, sscratch와 pt_regs.sp를 기록한다.

04 · SOURCE

Linux 6.18.37 원본 코드와 줄별 설명

소스 위치를 고정된 숫자로 복사하지 않고 Linux v6.18.37 tree에서 함수 선언을 다시 찾아 발췌했습니다. 아래 코드와 각 줄의 설명은 1:1로 대응합니다.

arm64 · Linux 6.18.37

VBAR_EL1 vector slot과 EL0/EL1 sync, IRQ, FIQ, SError 분기

hardware는 ELR_EL1, SPSR_EL1, ESR_EL1, FAR_EL1 등을 system register에 남긴다. vector assembly가 x0-x30, SP와 원래 PSTATE를 pt_regs로 옮긴 뒤 el0t_64_sync_handler()가 ESR exception class를 syscall, abort, FP trap 등으로 분기한다.

원본 코드: arch/arm64/kernel/entry-common.c:752-834

752}
753 
754static void noinstr el0_fpac(struct pt_regs *regs, unsigned long esr)
755{
756	arm64_enter_from_user_mode(regs);
757	local_daif_restore(DAIF_PROCCTX);
758	do_el0_fpac(regs, esr);
759	arm64_exit_to_user_mode(regs);
760}
761 
762asmlinkage void noinstr el0t_64_sync_handler(struct pt_regs *regs)
763{
764	unsigned long esr = read_sysreg(esr_el1);
765 
766	switch (ESR_ELx_EC(esr)) {
767	case ESR_ELx_EC_SVC64:
768		el0_svc(regs);
769		break;
770	case ESR_ELx_EC_DABT_LOW:
771		el0_da(regs, esr);
772		break;
773	case ESR_ELx_EC_IABT_LOW:
774		el0_ia(regs, esr);
775		break;
776	case ESR_ELx_EC_FP_ASIMD:
777		el0_fpsimd_acc(regs, esr);
778		break;
779	case ESR_ELx_EC_SVE:
780		el0_sve_acc(regs, esr);
781		break;
782	case ESR_ELx_EC_SME:
783		el0_sme_acc(regs, esr);
784		break;
785	case ESR_ELx_EC_FP_EXC64:
786		el0_fpsimd_exc(regs, esr);
787		break;
788	case ESR_ELx_EC_SYS64:
789	case ESR_ELx_EC_WFx:
790		el0_sys(regs, esr);
791		break;
792	case ESR_ELx_EC_SP_ALIGN:
793		el0_sp(regs, esr);
794		break;
795	case ESR_ELx_EC_PC_ALIGN:
796		el0_pc(regs, esr);
797		break;
798	case ESR_ELx_EC_UNKNOWN:
799		el0_undef(regs, esr);
800		break;
801	case ESR_ELx_EC_BTI:
802		el0_bti(regs);
803		break;
804	case ESR_ELx_EC_MOPS:
805		el0_mops(regs, esr);
806		break;
807	case ESR_ELx_EC_GCS:
808		el0_gcs(regs, esr);
809		break;
810	case ESR_ELx_EC_BREAKPT_LOW:
811		el0_breakpt(regs, esr);
812		break;
813	case ESR_ELx_EC_SOFTSTP_LOW:
814		el0_softstp(regs, esr);
815		break;
816	case ESR_ELx_EC_WATCHPT_LOW:
817		el0_watchpt(regs, esr);
818		break;
819	case ESR_ELx_EC_BRK64:
820		el0_brk64(regs, esr);
821		break;
822	case ESR_ELx_EC_FPAC:
823		el0_fpac(regs, esr);
824		break;
825	default:
826		el0_inv(regs, esr);
827	}
828}
829 
830static void noinstr el0_interrupt(struct pt_regs *regs,
831				  void (*handler)(struct pt_regs *))
832{
833	arm64_enter_from_user_mode(regs);
834 

라인 바이 라인 주석

빈 줄과 전처리 경계도 생략하지 않았습니다. 원본의 83개 줄에 각각 설명을 붙였습니다.

L752}

C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.

L753(blank)

빈 줄은 arm64 Exception entry: vector table, IDT와 trap entry 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L754static void noinstr el0_fpac(struct pt_regs *regs, unsigned long esr)

이 함수의 진입 계약이 시작된다. arm64에서 caller context, argument ownership과 반환 시 보장할 architecture state를 먼저 적는다.

L755{

C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.

L756 arm64_enter_from_user_mode(regs);

helper 또는 architecture operation을 실행한다. arm64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.

L757 local_daif_restore(DAIF_PROCCTX);

helper 또는 architecture operation을 실행한다. arm64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.

L758 do_el0_fpac(regs, esr);

최소 한 번 실행되는 retry 또는 순회 구간이다. 탈출 조건이 다른 CPU나 hardware의 진행에 의존하는지 확인한다.

L759 arm64_exit_to_user_mode(regs);

helper 또는 architecture operation을 실행한다. arm64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.

L760}

C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.

L761(blank)

빈 줄은 arm64 Exception entry: vector table, IDT와 trap entry 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L762asmlinkage void noinstr el0t_64_sync_handler(struct pt_regs *regs)

이 함수의 진입 계약이 시작된다. arm64에서 caller context, argument ownership과 반환 시 보장할 architecture state를 먼저 적는다.

L763{

C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.

L764 unsigned long esr = read_sysreg(esr_el1);

helper 또는 architecture operation을 실행한다. arm64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.

L765(blank)

빈 줄은 arm64 Exception entry: vector table, IDT와 trap entry 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L766 switch (ESR_ELx_EC(esr)) {

ESR의 Exception Class를 읽어 system call, instruction/data abort, alignment, debug와 FP/SVE trap을 나누는 중심 분기다.

L767 case ESR_ELx_EC_SVC64:

AArch64 SVC64를 Linux syscall entry로 전달하는 경계다.

L768 el0_svc(regs);

helper 또는 architecture operation을 실행한다. arm64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.

L769 break;

정상 직선 경로를 벗어나 cleanup, retry 또는 다음 항목으로 이동한다. 이동 대상에서 해제하는 resource와 현재까지 획득한 ownership을 맞춘다.

L770 case ESR_ELx_EC_DABT_LOW:

hardware reason, flag 또는 operation 종류를 개별 처리 경로로 분기한다. 각 case가 공통 cleanup과 completion 지점에 도달하는지 확인한다.

L771 el0_da(regs, esr);

helper 또는 architecture operation을 실행한다. arm64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.

L772 break;

정상 직선 경로를 벗어나 cleanup, retry 또는 다음 항목으로 이동한다. 이동 대상에서 해제하는 resource와 현재까지 획득한 ownership을 맞춘다.

L773 case ESR_ELx_EC_IABT_LOW:

hardware reason, flag 또는 operation 종류를 개별 처리 경로로 분기한다. 각 case가 공통 cleanup과 completion 지점에 도달하는지 확인한다.

L774 el0_ia(regs, esr);

helper 또는 architecture operation을 실행한다. arm64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.

L775 break;

정상 직선 경로를 벗어나 cleanup, retry 또는 다음 항목으로 이동한다. 이동 대상에서 해제하는 resource와 현재까지 획득한 ownership을 맞춘다.

L776 case ESR_ELx_EC_FP_ASIMD:

hardware reason, flag 또는 operation 종류를 개별 처리 경로로 분기한다. 각 case가 공통 cleanup과 completion 지점에 도달하는지 확인한다.

L777 el0_fpsimd_acc(regs, esr);

helper 또는 architecture operation을 실행한다. arm64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.

L778 break;

정상 직선 경로를 벗어나 cleanup, retry 또는 다음 항목으로 이동한다. 이동 대상에서 해제하는 resource와 현재까지 획득한 ownership을 맞춘다.

L779 case ESR_ELx_EC_SVE:

hardware reason, flag 또는 operation 종류를 개별 처리 경로로 분기한다. 각 case가 공통 cleanup과 completion 지점에 도달하는지 확인한다.

L780 el0_sve_acc(regs, esr);

helper 또는 architecture operation을 실행한다. arm64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.

L781 break;

정상 직선 경로를 벗어나 cleanup, retry 또는 다음 항목으로 이동한다. 이동 대상에서 해제하는 resource와 현재까지 획득한 ownership을 맞춘다.

L782 case ESR_ELx_EC_SME:

hardware reason, flag 또는 operation 종류를 개별 처리 경로로 분기한다. 각 case가 공통 cleanup과 completion 지점에 도달하는지 확인한다.

L783 el0_sme_acc(regs, esr);

helper 또는 architecture operation을 실행한다. arm64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.

L784 break;

정상 직선 경로를 벗어나 cleanup, retry 또는 다음 항목으로 이동한다. 이동 대상에서 해제하는 resource와 현재까지 획득한 ownership을 맞춘다.

L785 case ESR_ELx_EC_FP_EXC64:

hardware reason, flag 또는 operation 종류를 개별 처리 경로로 분기한다. 각 case가 공통 cleanup과 completion 지점에 도달하는지 확인한다.

L786 el0_fpsimd_exc(regs, esr);

helper 또는 architecture operation을 실행한다. arm64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.

L787 break;

정상 직선 경로를 벗어나 cleanup, retry 또는 다음 항목으로 이동한다. 이동 대상에서 해제하는 resource와 현재까지 획득한 ownership을 맞춘다.

L788 case ESR_ELx_EC_SYS64:

hardware reason, flag 또는 operation 종류를 개별 처리 경로로 분기한다. 각 case가 공통 cleanup과 completion 지점에 도달하는지 확인한다.

L789 case ESR_ELx_EC_WFx:

hardware reason, flag 또는 operation 종류를 개별 처리 경로로 분기한다. 각 case가 공통 cleanup과 completion 지점에 도달하는지 확인한다.

L790 el0_sys(regs, esr);

helper 또는 architecture operation을 실행한다. arm64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.

L791 break;

정상 직선 경로를 벗어나 cleanup, retry 또는 다음 항목으로 이동한다. 이동 대상에서 해제하는 resource와 현재까지 획득한 ownership을 맞춘다.

L792 case ESR_ELx_EC_SP_ALIGN:

hardware reason, flag 또는 operation 종류를 개별 처리 경로로 분기한다. 각 case가 공통 cleanup과 completion 지점에 도달하는지 확인한다.

L793 el0_sp(regs, esr);

helper 또는 architecture operation을 실행한다. arm64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.

L794 break;

정상 직선 경로를 벗어나 cleanup, retry 또는 다음 항목으로 이동한다. 이동 대상에서 해제하는 resource와 현재까지 획득한 ownership을 맞춘다.

L795 case ESR_ELx_EC_PC_ALIGN:

hardware reason, flag 또는 operation 종류를 개별 처리 경로로 분기한다. 각 case가 공통 cleanup과 completion 지점에 도달하는지 확인한다.

L796 el0_pc(regs, esr);

helper 또는 architecture operation을 실행한다. arm64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.

L797 break;

정상 직선 경로를 벗어나 cleanup, retry 또는 다음 항목으로 이동한다. 이동 대상에서 해제하는 resource와 현재까지 획득한 ownership을 맞춘다.

L798 case ESR_ELx_EC_UNKNOWN:

hardware reason, flag 또는 operation 종류를 개별 처리 경로로 분기한다. 각 case가 공통 cleanup과 completion 지점에 도달하는지 확인한다.

L799 el0_undef(regs, esr);

helper 또는 architecture operation을 실행한다. arm64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.

L800 break;

정상 직선 경로를 벗어나 cleanup, retry 또는 다음 항목으로 이동한다. 이동 대상에서 해제하는 resource와 현재까지 획득한 ownership을 맞춘다.

L801 case ESR_ELx_EC_BTI:

hardware reason, flag 또는 operation 종류를 개별 처리 경로로 분기한다. 각 case가 공통 cleanup과 completion 지점에 도달하는지 확인한다.

L802 el0_bti(regs);

helper 또는 architecture operation을 실행한다. arm64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.

L803 break;

정상 직선 경로를 벗어나 cleanup, retry 또는 다음 항목으로 이동한다. 이동 대상에서 해제하는 resource와 현재까지 획득한 ownership을 맞춘다.

L804 case ESR_ELx_EC_MOPS:

hardware reason, flag 또는 operation 종류를 개별 처리 경로로 분기한다. 각 case가 공통 cleanup과 completion 지점에 도달하는지 확인한다.

L805 el0_mops(regs, esr);

helper 또는 architecture operation을 실행한다. arm64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.

L806 break;

정상 직선 경로를 벗어나 cleanup, retry 또는 다음 항목으로 이동한다. 이동 대상에서 해제하는 resource와 현재까지 획득한 ownership을 맞춘다.

L807 case ESR_ELx_EC_GCS:

hardware reason, flag 또는 operation 종류를 개별 처리 경로로 분기한다. 각 case가 공통 cleanup과 completion 지점에 도달하는지 확인한다.

L808 el0_gcs(regs, esr);

helper 또는 architecture operation을 실행한다. arm64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.

L809 break;

정상 직선 경로를 벗어나 cleanup, retry 또는 다음 항목으로 이동한다. 이동 대상에서 해제하는 resource와 현재까지 획득한 ownership을 맞춘다.

L810 case ESR_ELx_EC_BREAKPT_LOW:

hardware reason, flag 또는 operation 종류를 개별 처리 경로로 분기한다. 각 case가 공통 cleanup과 completion 지점에 도달하는지 확인한다.

L811 el0_breakpt(regs, esr);

helper 또는 architecture operation을 실행한다. arm64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.

L812 break;

정상 직선 경로를 벗어나 cleanup, retry 또는 다음 항목으로 이동한다. 이동 대상에서 해제하는 resource와 현재까지 획득한 ownership을 맞춘다.

L813 case ESR_ELx_EC_SOFTSTP_LOW:

hardware reason, flag 또는 operation 종류를 개별 처리 경로로 분기한다. 각 case가 공통 cleanup과 completion 지점에 도달하는지 확인한다.

L814 el0_softstp(regs, esr);

helper 또는 architecture operation을 실행한다. arm64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.

L815 break;

정상 직선 경로를 벗어나 cleanup, retry 또는 다음 항목으로 이동한다. 이동 대상에서 해제하는 resource와 현재까지 획득한 ownership을 맞춘다.

L816 case ESR_ELx_EC_WATCHPT_LOW:

hardware reason, flag 또는 operation 종류를 개별 처리 경로로 분기한다. 각 case가 공통 cleanup과 completion 지점에 도달하는지 확인한다.

L817 el0_watchpt(regs, esr);

helper 또는 architecture operation을 실행한다. arm64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.

L818 break;

정상 직선 경로를 벗어나 cleanup, retry 또는 다음 항목으로 이동한다. 이동 대상에서 해제하는 resource와 현재까지 획득한 ownership을 맞춘다.

L819 case ESR_ELx_EC_BRK64:

hardware reason, flag 또는 operation 종류를 개별 처리 경로로 분기한다. 각 case가 공통 cleanup과 completion 지점에 도달하는지 확인한다.

L820 el0_brk64(regs, esr);

helper 또는 architecture operation을 실행한다. arm64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.

L821 break;

정상 직선 경로를 벗어나 cleanup, retry 또는 다음 항목으로 이동한다. 이동 대상에서 해제하는 resource와 현재까지 획득한 ownership을 맞춘다.

L822 case ESR_ELx_EC_FPAC:

hardware reason, flag 또는 operation 종류를 개별 처리 경로로 분기한다. 각 case가 공통 cleanup과 completion 지점에 도달하는지 확인한다.

L823 el0_fpac(regs, esr);

helper 또는 architecture operation을 실행한다. arm64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.

L824 break;

정상 직선 경로를 벗어나 cleanup, retry 또는 다음 항목으로 이동한다. 이동 대상에서 해제하는 resource와 현재까지 획득한 ownership을 맞춘다.

L825 default:

분기 label이다. 이 위치로 들어오는 모든 선행 경로가 같은 register, stack, lock과 interrupt 상태를 만족하는지 비교한다.

L826 el0_inv(regs, esr);

helper 또는 architecture operation을 실행한다. arm64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.

L827 }

C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.

L828}

C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.

L829(blank)

빈 줄은 arm64 Exception entry: vector table, IDT와 trap entry 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L830static void noinstr el0_interrupt(struct pt_regs *regs,

이 줄이 arm64의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. Exception entry: vector table, IDT와 trap entry의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.

L831 void (*handler)(struct pt_regs *))

이 함수의 진입 계약이 시작된다. arm64에서 caller context, argument ownership과 반환 시 보장할 architecture state를 먼저 적는다.

L832{

C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.

L833 arm64_enter_from_user_mode(regs);

helper 또는 architecture operation을 실행한다. arm64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.

L834(blank)

빈 줄은 arm64 Exception entry: vector table, IDT와 trap entry 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

x86-64 · Linux 6.18.37

IDT gate와 entry_64.S의 idtentry macro

일부 exception은 CPU가 error code를 push하고 privilege가 바뀌면 SS, RSP, RFLAGS, CS, RIP도 hardware frame에 쌓는다. entry macro가 빠진 register를 저장해 pt_regs 형태를 맞추고 IST가 설정된 vector는 TSS의 전용 stack으로 전환한다.

원본 코드: arch/x86/entry/entry_64.S:317-401

317.endm
318 
319/**
320 * idtentry - Macro to generate entry stubs for simple IDT entries
321 * @vector:		Vector number
322 * @asmsym:		ASM symbol for the entry point
323 * @cfunc:		C function to be called
324 * @has_error_code:	Hardware pushed error code on stack
325 *
326 * The macro emits code to set up the kernel context for straight forward
327 * and simple IDT entries. No IST stack, no paranoid entry checks.
328 */
329.macro idtentry vector asmsym cfunc has_error_code:req
330SYM_CODE_START(\asmsym)
331 
332	.if \vector == X86_TRAP_BP
333		/* #BP advances %rip to the next instruction */
334		UNWIND_HINT_IRET_ENTRY offset=\has_error_code*8 signal=0
335	.else
336		UNWIND_HINT_IRET_ENTRY offset=\has_error_code*8
337	.endif
338 
339	ENDBR
340	ASM_CLAC
341	cld
342 
343	.if \has_error_code == 0
344		pushq	$-1			/* ORIG_RAX: no syscall to restart */
345	.endif
346 
347	.if \vector == X86_TRAP_BP
348		/*
349		 * If coming from kernel space, create a 6-word gap to allow the
350		 * int3 handler to emulate a call instruction.
351		 */
352		testb	$3, CS-ORIG_RAX(%rsp)
353		jnz	.Lfrom_usermode_no_gap_\@
354		.rept	6
355		pushq	5*8(%rsp)
356		.endr
357		UNWIND_HINT_IRET_REGS offset=8
358.Lfrom_usermode_no_gap_\@:
359	.endif
360 
361	idtentry_body \cfunc \has_error_code
362 
363_ASM_NOKPROBE(\asmsym)
364SYM_CODE_END(\asmsym)
365.endm
366 
367/*
368 * Interrupt entry/exit.
369 *
370 + The interrupt stubs push (vector) onto the stack, which is the error_code
371 * position of idtentry exceptions, and jump to one of the two idtentry points
372 * (common/spurious).
373 *
374 * common_interrupt is a hotpath, align it to a cache line
375 */
376.macro idtentry_irq vector cfunc
377	.p2align CONFIG_X86_L1_CACHE_SHIFT
378	idtentry \vector asm_\cfunc \cfunc has_error_code=1
379.endm
380 
381/**
382 * idtentry_mce_db - Macro to generate entry stubs for #MC and #DB
383 * @vector:		Vector number
384 * @asmsym:		ASM symbol for the entry point
385 * @cfunc:		C function to be called
386 *
387 * The macro emits code to set up the kernel context for #MC and #DB
388 *
389 * If the entry comes from user space it uses the normal entry path
390 * including the return to user space work and preemption checks on
391 * exit.
392 *
393 * If hits in kernel mode then it needs to go through the paranoid
394 * entry as the exception can hit any random state. No preemption
395 * check on exit to keep the paranoid path simple.
396 */
397.macro idtentry_mce_db vector asmsym cfunc
398SYM_CODE_START(\asmsym)
399	UNWIND_HINT_IRET_ENTRY
400	ENDBR
401	ASM_CLAC

라인 바이 라인 주석

빈 줄과 전처리 경계도 생략하지 않았습니다. 원본의 85개 줄에 각각 설명을 붙였습니다.

L317.endm

이 줄이 x86-64의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. Exception entry: vector table, IDT와 trap entry의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.

L318(blank)

빈 줄은 x86-64 Exception entry: vector table, IDT와 trap entry 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L319/**

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L320 * idtentry - Macro to generate entry stubs for simple IDT entries

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L321 * @vector: Vector number

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L322 * @asmsym: ASM symbol for the entry point

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L323 * @cfunc: C function to be called

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L324 * @has_error_code: Hardware pushed error code on stack

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L325 *

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L326 * The macro emits code to set up the kernel context for straight forward

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L327 * and simple IDT entries. No IST stack, no paranoid entry checks.

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L328 */

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L329.macro idtentry vector asmsym cfunc has_error_code:req

이 줄이 x86-64의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. Exception entry: vector table, IDT와 trap entry의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.

L330SYM_CODE_START(\asmsym)

assembler/linker가 symbol, section과 정렬을 정의한다. runtime instruction은 아니지만 entry 주소와 relocation 가능한 범위를 결정한다.

L331(blank)

빈 줄은 x86-64 Exception entry: vector table, IDT와 trap entry 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L332 .if \vector == X86_TRAP_BP

이 줄이 x86-64의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. Exception entry: vector table, IDT와 trap entry의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.

L333 /* #BP advances %rip to the next instruction */

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L334 UNWIND_HINT_IRET_ENTRY offset=\has_error_code*8 signal=0

계산한 pointer, flag, register image 또는 generation을 다음 단계가 읽을 위치에 저장한다. 값의 단위, address space와 publication ordering을 확인한다.

L335 .else

이 줄이 x86-64의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. Exception entry: vector table, IDT와 trap entry의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.

L336 UNWIND_HINT_IRET_ENTRY offset=\has_error_code*8

계산한 pointer, flag, register image 또는 generation을 다음 단계가 읽을 위치에 저장한다. 값의 단위, address space와 publication ordering을 확인한다.

L337 .endif

이 줄이 x86-64의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. Exception entry: vector table, IDT와 trap entry의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.

L338(blank)

빈 줄은 x86-64 Exception entry: vector table, IDT와 trap entry 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L339 ENDBR

assembler/linker가 symbol, section과 정렬을 정의한다. runtime instruction은 아니지만 entry 주소와 relocation 가능한 범위를 결정한다.

L340 ASM_CLAC

선언 또는 macro 확장 일부다. type의 폭과 signedness, per-CPU/task/object 중 어느 수명을 따르는 값인지 확인한다.

L341 cld

선언 또는 macro 확장 일부다. type의 폭과 signedness, per-CPU/task/object 중 어느 수명을 따르는 값인지 확인한다.

L342(blank)

빈 줄은 x86-64 Exception entry: vector table, IDT와 trap entry 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L343 .if \has_error_code == 0

이 줄이 x86-64의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. Exception entry: vector table, IDT와 trap entry의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.

L344 pushq $-1 /* ORIG_RAX: no syscall to restart */

이 줄이 x86-64의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. Exception entry: vector table, IDT와 trap entry의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.

L345 .endif

이 줄이 x86-64의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. Exception entry: vector table, IDT와 trap entry의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.

L346(blank)

빈 줄은 x86-64 Exception entry: vector table, IDT와 trap entry 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L347 .if \vector == X86_TRAP_BP

이 줄이 x86-64의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. Exception entry: vector table, IDT와 trap entry의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.

L348 /*

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L349 * If coming from kernel space, create a 6-word gap to allow the

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L350 * int3 handler to emulate a call instruction.

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L351 */

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L352 testb $3, CS-ORIG_RAX(%rsp)

helper 또는 architecture operation을 실행한다. x86-64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.

L353 jnz .Lfrom_usermode_no_gap_\@

이 줄이 x86-64의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. Exception entry: vector table, IDT와 trap entry의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.

L354 .rept 6

이 줄이 x86-64의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. Exception entry: vector table, IDT와 trap entry의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.

L355 pushq 5*8(%rsp)

이 함수의 진입 계약이 시작된다. x86-64에서 caller context, argument ownership과 반환 시 보장할 architecture state를 먼저 적는다.

L356 .endr

이 줄이 x86-64의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. Exception entry: vector table, IDT와 trap entry의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.

L357 UNWIND_HINT_IRET_REGS offset=8

계산한 pointer, flag, register image 또는 generation을 다음 단계가 읽을 위치에 저장한다. 값의 단위, address space와 publication ordering을 확인한다.

L358.Lfrom_usermode_no_gap_\@:

이 줄이 x86-64의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. Exception entry: vector table, IDT와 trap entry의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.

L359 .endif

이 줄이 x86-64의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. Exception entry: vector table, IDT와 trap entry의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.

L360(blank)

빈 줄은 x86-64 Exception entry: vector table, IDT와 trap entry 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L361 idtentry_body \cfunc \has_error_code

이 줄이 x86-64의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. Exception entry: vector table, IDT와 trap entry의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.

L362(blank)

빈 줄은 x86-64 Exception entry: vector table, IDT와 trap entry 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L363_ASM_NOKPROBE(\asmsym)

이 함수의 진입 계약이 시작된다. x86-64에서 caller context, argument ownership과 반환 시 보장할 architecture state를 먼저 적는다.

L364SYM_CODE_END(\asmsym)

assembler/linker가 symbol, section과 정렬을 정의한다. runtime instruction은 아니지만 entry 주소와 relocation 가능한 범위를 결정한다.

L365.endm

이 줄이 x86-64의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. Exception entry: vector table, IDT와 trap entry의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.

L366(blank)

빈 줄은 x86-64 Exception entry: vector table, IDT와 trap entry 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L367/*

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L368 * Interrupt entry/exit.

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L369 *

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L370 + The interrupt stubs push (vector) onto the stack, which is the error_code

이 줄이 x86-64의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. Exception entry: vector table, IDT와 trap entry의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.

L371 * position of idtentry exceptions, and jump to one of the two idtentry points

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L372 * (common/spurious).

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L373 *

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L374 * common_interrupt is a hotpath, align it to a cache line

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L375 */

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L376.macro idtentry_irq vector cfunc

이 줄이 x86-64의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. Exception entry: vector table, IDT와 trap entry의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.

L377 .p2align CONFIG_X86_L1_CACHE_SHIFT

이 줄이 x86-64의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. Exception entry: vector table, IDT와 trap entry의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.

L378 idtentry \vector asm_\cfunc \cfunc has_error_code=1

계산한 pointer, flag, register image 또는 generation을 다음 단계가 읽을 위치에 저장한다. 값의 단위, address space와 publication ordering을 확인한다.

L379.endm

이 줄이 x86-64의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. Exception entry: vector table, IDT와 trap entry의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.

L380(blank)

빈 줄은 x86-64 Exception entry: vector table, IDT와 trap entry 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L381/**

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L382 * idtentry_mce_db - Macro to generate entry stubs for #MC and #DB

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L383 * @vector: Vector number

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L384 * @asmsym: ASM symbol for the entry point

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L385 * @cfunc: C function to be called

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L386 *

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L387 * The macro emits code to set up the kernel context for #MC and #DB

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L388 *

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L389 * If the entry comes from user space it uses the normal entry path

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L390 * including the return to user space work and preemption checks on

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L391 * exit.

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L392 *

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L393 * If hits in kernel mode then it needs to go through the paranoid

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L394 * entry as the exception can hit any random state. No preemption

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L395 * check on exit to keep the paranoid path simple.

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L396 */

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L397.macro idtentry_mce_db vector asmsym cfunc

이 줄이 x86-64의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. Exception entry: vector table, IDT와 trap entry의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.

L398SYM_CODE_START(\asmsym)

assembler/linker가 symbol, section과 정렬을 정의한다. runtime instruction은 아니지만 entry 주소와 relocation 가능한 범위를 결정한다.

L399 UNWIND_HINT_IRET_ENTRY

선언 또는 macro 확장 일부다. type의 폭과 signedness, per-CPU/task/object 중 어느 수명을 따르는 값인지 확인한다.

L400 ENDBR

assembler/linker가 symbol, section과 정렬을 정의한다. runtime instruction은 아니지만 entry 주소와 relocation 가능한 범위를 결정한다.

L401 ASM_CLAC

선언 또는 macro 확장 일부다. type의 폭과 signedness, per-CPU/task/object 중 어느 수명을 따르는 값인지 확인한다.

RISC-V · Linux 6.18.37

STVEC가 가리키는 handle_exception과 SCAUSE dispatch

trap 진입 시 hardware는 SEPC, SCAUSE, STVAL과 SSTATUS만 갱신하고 일반 register는 저장하지 않는다. assembly가 sscratchtp를 이용해 user/kernel trap을 구별하고 task kernel stack의 pt_regs에 x1-x31을 저장한다.

원본 코드: arch/riscv/kernel/entry.S:88-182

88	REG_L 	a2, TASK_TI_A2(tp)
89.Lnew_vmalloc_restore_context_a1:
90	REG_L 	a1, TASK_TI_A1(tp)
91.Lnew_vmalloc_restore_context_a0:
92	REG_L	a0, TASK_TI_A0(tp)
93.endm
94 
95 
96SYM_CODE_START(handle_exception)
97	/*
98	 * If coming from userspace, preserve the user thread pointer and load
99	 * the kernel thread pointer.  If we came from the kernel, the scratch
100	 * register will contain 0, and we should continue on the current TP.
101	 */
102	csrrw tp, CSR_SCRATCH, tp
103	bnez tp, .Lsave_context
104 
105.Lrestore_kernel_tpsp:
106	csrr tp, CSR_SCRATCH
107 
108#ifdef CONFIG_64BIT
109	/*
110	 * The RISC-V kernel does not eagerly emit a sfence.vma after each
111	 * new vmalloc mapping, which may result in exceptions:
112	 * - if the uarch caches invalid entries, the new mapping would not be
113	 *   observed by the page table walker and an invalidation is needed.
114	 * - if the uarch does not cache invalid entries, a reordered access
115	 *   could "miss" the new mapping and traps: in that case, we only need
116	 *   to retry the access, no sfence.vma is required.
117	 */
118	new_vmalloc_check
119#endif
120 
121	REG_S sp, TASK_TI_KERNEL_SP(tp)
122 
123#ifdef CONFIG_VMAP_STACK
124	addi sp, sp, -(PT_SIZE_ON_STACK)
125	srli sp, sp, THREAD_SHIFT
126	andi sp, sp, 0x1
127	bnez sp, handle_kernel_stack_overflow
128	REG_L sp, TASK_TI_KERNEL_SP(tp)
129#endif
130 
131.Lsave_context:
132	REG_S sp, TASK_TI_USER_SP(tp)
133	REG_L sp, TASK_TI_KERNEL_SP(tp)
134	addi sp, sp, -(PT_SIZE_ON_STACK)
135	REG_S x1,  PT_RA(sp)
136	REG_S x3,  PT_GP(sp)
137	REG_S x5,  PT_T0(sp)
138	save_from_x6_to_x31
139 
140	/*
141	 * Disable user-mode memory access as it should only be set in the
142	 * actual user copy routines.
143	 *
144	 * Disable the FPU/Vector to detect illegal usage of floating point
145	 * or vector in kernel space.
146	 */
147	li t0, SR_SUM | SR_FS_VS
148 
149	REG_L s0, TASK_TI_USER_SP(tp)
150	csrrc s1, CSR_STATUS, t0
151	csrr s2, CSR_EPC
152	csrr s3, CSR_TVAL
153	csrr s4, CSR_CAUSE
154	csrr s5, CSR_SCRATCH
155	REG_S s0, PT_SP(sp)
156	REG_S s1, PT_STATUS(sp)
157	REG_S s2, PT_EPC(sp)
158	REG_S s3, PT_BADADDR(sp)
159	REG_S s4, PT_CAUSE(sp)
160	REG_S s5, PT_TP(sp)
161 
162	/*
163	 * Set the scratch register to 0, so that if a recursive exception
164	 * occurs, the exception vector knows it came from the kernel
165	 */
166	csrw CSR_SCRATCH, x0
167 
168	/* Load the global pointer */
169	load_global_pointer
170 
171	/* Load the kernel shadow call stack pointer if coming from userspace */
172	scs_load_current_if_task_changed s5
173 
174#ifdef CONFIG_RISCV_ISA_V_PREEMPTIVE
175	move a0, sp
176	call riscv_v_context_nesting_start
177#endif
178	move a0, sp /* pt_regs */
179 
180	/*
181	 * MSB of cause differentiates between
182	 * interrupts and exceptions

라인 바이 라인 주석

빈 줄과 전처리 경계도 생략하지 않았습니다. 원본의 95개 줄에 각각 설명을 붙였습니다.

L88 REG_L a2, TASK_TI_A2(tp)

helper 또는 architecture operation을 실행한다. RISC-V에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.

L89.Lnew_vmalloc_restore_context_a1:

분기 label이다. 이 위치로 들어오는 모든 선행 경로가 같은 register, stack, lock과 interrupt 상태를 만족하는지 비교한다.

L90 REG_L a1, TASK_TI_A1(tp)

helper 또는 architecture operation을 실행한다. RISC-V에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.

L91.Lnew_vmalloc_restore_context_a0:

분기 label이다. 이 위치로 들어오는 모든 선행 경로가 같은 register, stack, lock과 interrupt 상태를 만족하는지 비교한다.

L92 REG_L a0, TASK_TI_A0(tp)

helper 또는 architecture operation을 실행한다. RISC-V에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.

L93.endm

이 줄이 RISC-V의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. Exception entry: vector table, IDT와 trap entry의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.

L94(blank)

빈 줄은 RISC-V Exception entry: vector table, IDT와 trap entry 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L95(blank)

빈 줄은 RISC-V Exception entry: vector table, IDT와 trap entry 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L96SYM_CODE_START(handle_exception)

assembler/linker가 symbol, section과 정렬을 정의한다. runtime instruction은 아니지만 entry 주소와 relocation 가능한 범위를 결정한다.

L97 /*

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L98 * If coming from userspace, preserve the user thread pointer and load

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L99 * the kernel thread pointer. If we came from the kernel, the scratch

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L100 * register will contain 0, and we should continue on the current TP.

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L101 */

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L102 csrrw tp, CSR_SCRATCH, tp

tp와 sscratch를 교환해 user-origin trap이면 current task pointer를 얻고 kernel-origin이면 별도 경로를 선택한다.

L103 bnez tp, .Lsave_context

이 줄이 RISC-V의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. Exception entry: vector table, IDT와 trap entry의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.

L104(blank)

빈 줄은 RISC-V Exception entry: vector table, IDT와 trap entry 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L105.Lrestore_kernel_tpsp:

분기 label이다. 이 위치로 들어오는 모든 선행 경로가 같은 register, stack, lock과 interrupt 상태를 만족하는지 비교한다.

L106 csrr tp, CSR_SCRATCH

privileged system register를 읽거나 쓴다. 일반 변수와 달리 write side effect, privilege level과 serialization 조건을 함께 확인한다.

L107(blank)

빈 줄은 RISC-V Exception entry: vector table, IDT와 trap entry 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L108#ifdef CONFIG_64BIT

Kconfig와 compiler feature에 따라 최종 object에 남는 경로가 달라지는 전처리 경계다. 대상 .config와 disassembly로 실제 선택을 확인한다.

L109 /*

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L110 * The RISC-V kernel does not eagerly emit a sfence.vma after each

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L111 * new vmalloc mapping, which may result in exceptions:

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L112 * - if the uarch caches invalid entries, the new mapping would not be

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L113 * observed by the page table walker and an invalidation is needed.

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L114 * - if the uarch does not cache invalid entries, a reordered access

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L115 * could "miss" the new mapping and traps: in that case, we only need

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L116 * to retry the access, no sfence.vma is required.

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L117 */

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L118 new_vmalloc_check

선언 또는 macro 확장 일부다. type의 폭과 signedness, per-CPU/task/object 중 어느 수명을 따르는 값인지 확인한다.

L119#endif

Kconfig와 compiler feature에 따라 최종 object에 남는 경로가 달라지는 전처리 경계다. 대상 .config와 disassembly로 실제 선택을 확인한다.

L120(blank)

빈 줄은 RISC-V Exception entry: vector table, IDT와 trap entry 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L121 REG_S sp, TASK_TI_KERNEL_SP(tp)

helper 또는 architecture operation을 실행한다. RISC-V에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.

L122(blank)

빈 줄은 RISC-V Exception entry: vector table, IDT와 trap entry 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L123#ifdef CONFIG_VMAP_STACK

Kconfig와 compiler feature에 따라 최종 object에 남는 경로가 달라지는 전처리 경계다. 대상 .config와 disassembly로 실제 선택을 확인한다.

L124 addi sp, sp, -(PT_SIZE_ON_STACK)

이 줄이 RISC-V의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. Exception entry: vector table, IDT와 trap entry의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.

L125 srli sp, sp, THREAD_SHIFT

이 줄이 RISC-V의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. Exception entry: vector table, IDT와 trap entry의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.

L126 andi sp, sp, 0x1

이 줄이 RISC-V의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. Exception entry: vector table, IDT와 trap entry의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.

L127 bnez sp, handle_kernel_stack_overflow

이 줄이 RISC-V의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. Exception entry: vector table, IDT와 trap entry의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.

L128 REG_L sp, TASK_TI_KERNEL_SP(tp)

helper 또는 architecture operation을 실행한다. RISC-V에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.

L129#endif

Kconfig와 compiler feature에 따라 최종 object에 남는 경로가 달라지는 전처리 경계다. 대상 .config와 disassembly로 실제 선택을 확인한다.

L130(blank)

빈 줄은 RISC-V Exception entry: vector table, IDT와 trap entry 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L131.Lsave_context:

분기 label이다. 이 위치로 들어오는 모든 선행 경로가 같은 register, stack, lock과 interrupt 상태를 만족하는지 비교한다.

L132 REG_S sp, TASK_TI_USER_SP(tp)

helper 또는 architecture operation을 실행한다. RISC-V에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.

L133 REG_L sp, TASK_TI_KERNEL_SP(tp)

helper 또는 architecture operation을 실행한다. RISC-V에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.

L134 addi sp, sp, -(PT_SIZE_ON_STACK)

이 줄이 RISC-V의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. Exception entry: vector table, IDT와 trap entry의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.

L135 REG_S x1, PT_RA(sp)

hardware가 저장하지 않은 integer register를 Linux pt_regs layout에 맞춰 쌓기 시작한다.

L136 REG_S x3, PT_GP(sp)

helper 또는 architecture operation을 실행한다. RISC-V에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.

L137 REG_S x5, PT_T0(sp)

helper 또는 architecture operation을 실행한다. RISC-V에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.

L138 save_from_x6_to_x31

선언 또는 macro 확장 일부다. type의 폭과 signedness, per-CPU/task/object 중 어느 수명을 따르는 값인지 확인한다.

L139(blank)

빈 줄은 RISC-V Exception entry: vector table, IDT와 trap entry 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L140 /*

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L141 * Disable user-mode memory access as it should only be set in the

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L142 * actual user copy routines.

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L143 *

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L144 * Disable the FPU/Vector to detect illegal usage of floating point

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L145 * or vector in kernel space.

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L146 */

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L147 li t0, SR_SUM | SR_FS_VS

이 줄이 RISC-V의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. Exception entry: vector table, IDT와 trap entry의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.

L148(blank)

빈 줄은 RISC-V Exception entry: vector table, IDT와 trap entry 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L149 REG_L s0, TASK_TI_USER_SP(tp)

helper 또는 architecture operation을 실행한다. RISC-V에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.

L150 csrrc s1, CSR_STATUS, t0

이 줄이 RISC-V의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. Exception entry: vector table, IDT와 trap entry의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.

L151 csrr s2, CSR_EPC

privileged system register를 읽거나 쓴다. 일반 변수와 달리 write side effect, privilege level과 serialization 조건을 함께 확인한다.

L152 csrr s3, CSR_TVAL

privileged system register를 읽거나 쓴다. 일반 변수와 달리 write side effect, privilege level과 serialization 조건을 함께 확인한다.

L153 csrr s4, CSR_CAUSE

trap 원인과 interrupt bit를 읽어 exception table 또는 interrupt dispatch로 보낸다.

L154 csrr s5, CSR_SCRATCH

privileged system register를 읽거나 쓴다. 일반 변수와 달리 write side effect, privilege level과 serialization 조건을 함께 확인한다.

L155 REG_S s0, PT_SP(sp)

helper 또는 architecture operation을 실행한다. RISC-V에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.

L156 REG_S s1, PT_STATUS(sp)

helper 또는 architecture operation을 실행한다. RISC-V에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.

L157 REG_S s2, PT_EPC(sp)

helper 또는 architecture operation을 실행한다. RISC-V에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.

L158 REG_S s3, PT_BADADDR(sp)

helper 또는 architecture operation을 실행한다. RISC-V에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.

L159 REG_S s4, PT_CAUSE(sp)

helper 또는 architecture operation을 실행한다. RISC-V에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.

L160 REG_S s5, PT_TP(sp)

helper 또는 architecture operation을 실행한다. RISC-V에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.

L161(blank)

빈 줄은 RISC-V Exception entry: vector table, IDT와 trap entry 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L162 /*

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L163 * Set the scratch register to 0, so that if a recursive exception

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L164 * occurs, the exception vector knows it came from the kernel

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L165 */

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L166 csrw CSR_SCRATCH, x0

privileged system register를 읽거나 쓴다. 일반 변수와 달리 write side effect, privilege level과 serialization 조건을 함께 확인한다.

L167(blank)

빈 줄은 RISC-V Exception entry: vector table, IDT와 trap entry 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L168 /* Load the global pointer */

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L169 load_global_pointer

선언 또는 macro 확장 일부다. type의 폭과 signedness, per-CPU/task/object 중 어느 수명을 따르는 값인지 확인한다.

L170(blank)

빈 줄은 RISC-V Exception entry: vector table, IDT와 trap entry 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L171 /* Load the kernel shadow call stack pointer if coming from userspace */

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L172 scs_load_current_if_task_changed s5

선언 또는 macro 확장 일부다. type의 폭과 signedness, per-CPU/task/object 중 어느 수명을 따르는 값인지 확인한다.

L173(blank)

빈 줄은 RISC-V Exception entry: vector table, IDT와 trap entry 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L174#ifdef CONFIG_RISCV_ISA_V_PREEMPTIVE

Kconfig와 compiler feature에 따라 최종 object에 남는 경로가 달라지는 전처리 경계다. 대상 .config와 disassembly로 실제 선택을 확인한다.

L175 move a0, sp

이 줄이 RISC-V의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. Exception entry: vector table, IDT와 trap entry의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.

L176 call riscv_v_context_nesting_start

하위 경로로 제어를 넘긴다. 호출 직전의 argument, interrupt/preemption 상태와 호출 뒤에도 살아 있어야 하는 object를 기록한다.

L177#endif

Kconfig와 compiler feature에 따라 최종 object에 남는 경로가 달라지는 전처리 경계다. 대상 .config와 disassembly로 실제 선택을 확인한다.

L178 move a0, sp /* pt_regs */

이 줄이 RISC-V의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. Exception entry: vector table, IDT와 trap entry의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.

L179(blank)

빈 줄은 RISC-V Exception entry: vector table, IDT와 trap entry 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L180 /*

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L181 * MSB of cause differentiates between

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L182 * interrupts and exceptions

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

05 · WORKED EXAMPLE

숫자로 검산하기

01

user page fault 한 건의 entry frame 비교

user PC 0x400812에서 VA 0x7fff0000을 load하다 translation fault가 발생했다고 가정한다.

  1. hardwarearm64는 ELR/ESR/FAR, x86은 RIP/CS/RFLAGS와 CR2, RISC-V는 SEPC/SCAUSE/STVAL을 각각 남긴다.
  2. stackarm64와 RISC-V entry는 task kernel stack에 pt_regs를 만들고 x86은 privilege frame과 필요 시 IST/TSS stack을 사용한다.
  3. classification세 경로 모두 user-origin과 read fault를 판별한 뒤 architecture page-fault handler에서 mm fault core로 넘어간다.
  4. returnPTE가 복구되면 저장된 PC를 그대로 재실행한다. signal이 필요하면 동일 frame을 signal setup이 수정한다.

결론fault address 하나만 비교하지 말고 origin privilege, saved PC, error class, stack 종류와 register frame 완성 시점을 함께 추적해야 한다.

06 · DEEP DIVE

경계별 상세 분석

01

공통 kernel core와 architecture hook의 경계

모든 architecture는 hardware가 제공한 최소 상태를 kernel entry frame으로 확장하고 exception class를 해석한 뒤 공통 irq, fault, syscall 계층으로 보낸다. 다만 hardware가 자동 저장하는 값, vector 주소 계산법, privilege 전환 때 stack을 바꾸는 방법은 서로 다르다.

assembly entry가 pt_regs를 완성하기 전에는 C ABI와 scheduler의 일반 가정을 적용할 수 없다. NMI와 같은 재진입 경로, user access 차단, speculation 방어, lockdep/RCU 진입은 정해진 순서로만 호출해야 한다.

02

arm64: VBAR_EL1 vector slot과 EL0/EL1 sync, IRQ, FIQ, SError 분기

hardware는 ELR_EL1, SPSR_EL1, ESR_EL1, FAR_EL1 등을 system register에 남긴다. vector assembly가 x0-x30, SP와 원래 PSTATE를 pt_regs로 옮긴 뒤 el0t_64_sync_handler()가 ESR exception class를 syscall, abort, FP trap 등으로 분기한다.

DAIF mask, PAN/UAO, MTE tag check, speculation barrier와 context tracking 순서가 user memory 접근 가능 시점을 결정한다. 디버깅할 때는 VBAR_EL1, ESR_EL1.EC, ELR_EL1, FAR_EL1, SPSR_EL1과 pt_regs의 pc/pstate/sp를 함께 덤프한다.

03

x86-64: IDT gate와 entry_64.S의 idtentry macro

일부 exception은 CPU가 error code를 push하고 privilege가 바뀌면 SS, RSP, RFLAGS, CS, RIP도 hardware frame에 쌓는다. entry macro가 빠진 register를 저장해 pt_regs 형태를 맞추고 IST가 설정된 vector는 TSS의 전용 stack으로 전환한다.

SWAPGS, CR3/KPTI 전환, speculation 방어와 irqentry_enter 순서가 NMI 재진입과 user GSBASE 안전성을 좌우한다. 디버깅할 때는 IDT descriptor, vector number, error_code, CS privilege, RSP, CR2와 ORC unwind 결과를 확인한다.

04

RISC-V: STVEC가 가리키는 handle_exception과 SCAUSE dispatch

trap 진입 시 hardware는 SEPC, SCAUSE, STVAL과 SSTATUS만 갱신하고 일반 register는 저장하지 않는다. assembly가 sscratchtp를 이용해 user/kernel trap을 구별하고 task kernel stack의 pt_regs에 x1-x31을 저장한다.

SUM 차단, FS/VS 상태, interrupt enable bit와 sscratch 교환 순서가 재진입 가능한 시점을 정한다. 디버깅할 때는 STVEC, SCAUSE interrupt bit와 code, SEPC, STVAL, SSTATUS.SPP/SPIE, sscratch와 pt_regs.sp를 기록한다.

05

객체 수명과 소유권을 먼저 고정한다

pt_regs는 현재 kernel stack 또는 별도 exception stack에 존재하며 handler와 signal/ptrace가 참조하는 동안 유지된다. nested exception은 바깥 frame을 덮지 않도록 stack 경계와 nesting counter를 보존해야 한다.

주소나 register 값이 맞는지만 확인하면 stale state를 놓친다. producer, publication, consumer와 폐기 지점을 같은 표에 기록한다.

06

latency upper bound는 hardware instruction 하나가 아니다

entry latency는 register save, privilege 경계 방어, vector dispatch, interrupt masking과 cache miss의 합이다. NMI/FIQ 급 경로는 일반 IRQ보다 우선하지만 handler가 긴 critical section에 막히면 tail latency가 늘어난다.

평균값 외에 interrupt-off 구간, remote CPU 응답, firmware 호출과 retry 횟수를 분리해야 최악 지연의 원인을 찾을 수 있다.

07 · FAILURE

실패를 어떤 증거로 나눌 것인가

분류관찰되는 결과첫 확인값
arm64잘못된 vector offset, 불완전한 register save 또는 PAN 복원 누락은 return 직후 register corruption이나 kernel user-memory 접근 허용으로 나타난다.VBAR_EL1, ESR_EL1.EC, ELR_EL1, FAR_EL1, SPSR_EL1과 pt_regs의 pc/pstate/sp를 함께 덤프한다.
x86-64error-code 유무를 잘못 맞추면 모든 frame offset이 한 word씩 밀린다. SWAPGS 판단 실패는 per-CPU 주소를 user GSBASE로 해석하는 치명적 오류를 만든다.IDT descriptor, vector number, error_code, CS privilege, RSP, CR2와 ORC unwind 결과를 확인한다.
RISC-Vkernel trap에서 user stack으로 오인하거나 sscratch를 늦게 복원하면 nested trap이 잘못된 task와 stack을 사용한다.STVEC, SCAUSE interrupt bit와 code, SEPC, STVAL, SSTATUS.SPP/SPIE, sscratch와 pt_regs.sp를 기록한다.

08 · LAB

재현과 계측 절차

  1. QEMU에서 의도적인 user read fault, kernel fault, breakpoint를 발생시켜 각 entry frame과 C handler까지 function graph를 수집한다.
  2. entry 직전과 irqentry_enter 이후의 RCU/context-tracking 상태를 tracepoint로 비교한다.
  3. 동일한 workload에서 세 architecture의 tracepoint 이름, CPU 번호, PC, stack pointer와 address-space identifier를 같은 열로 기록한다.
  4. 소스만 읽고 끝내지 않고 최종 vmlinuxobjdump -dr, readelf -SW 결과로 선택된 alternative와 section 배치를 확인한다.

09 · REFERENCES

원문 좌표

Linux kernel source: GPL-2.0-only. 이 글의 코드 발췌는 Linux v6.18.37 원문을 기준으로 하며, 분석 문장은 해당 코드의 실행 조건과 상태 경계를 설명합니다.