entry_64.S

dandb3·2024년 7월 29일
0

linux kernel

목록 보기
11/21

callee-preserved

함수 호출 전의 값이 함수 호출 후에도 유지되어야 하는 레지스터를 의미한다.

callee-clobbered

함수 호출 전의 값이 함수 호출 후에 바뀔 수 있는 레지스터를 의미한다.

 * Registers on entry:
 * rax  system call number
 * rcx  return address
 * r11  saved rflags (note: r11 is callee-clobbered register in C ABI)
 * rdi  arg0
 * rsi  arg1
 * rdx  arg2
 * r10  arg3 (needs to be moved to rcx to conform to C ABI)
 * r8   arg4
 * r9   arg5
 * (note: r12-r15, rbp, rbx are callee-preserved in C ABI)

entry_64.S에 존재하는 주석이다.
r12 ~ r15, rbp, rbx 레지스터는 callee-preserved이고, 나머지는 callee-clobbered register에 해당한다.

entry_SYSCALL_64


SYM_CODE_START(entry_SYSCALL_64)
	UNWIND_HINT_ENTRY
	ENDBR

	swapgs
	/* tss.sp2 is scratch space. */
	movq	%rsp, PER_CPU_VAR(cpu_tss_rw + TSS_sp2)
	SWITCH_TO_KERNEL_CR3 scratch_reg=%rsp
	movq	PER_CPU_VAR(pcpu_hot + X86_top_of_stack), %rsp

SYM_INNER_LABEL(entry_SYSCALL_64_safe_stack, SYM_L_GLOBAL)
	ANNOTATE_NOENDBR

	/* Construct struct pt_regs on stack */
	pushq	$__USER_DS				/* pt_regs->ss */
	pushq	PER_CPU_VAR(cpu_tss_rw + TSS_sp2)	/* pt_regs->sp */
	pushq	%r11					/* pt_regs->flags */
	pushq	$__USER_CS				/* pt_regs->cs */
	pushq	%rcx					/* pt_regs->ip */
SYM_INNER_LABEL(entry_SYSCALL_64_after_hwframe, SYM_L_GLOBAL)
	pushq	%rax					/* pt_regs->orig_ax */

	PUSH_AND_CLEAR_REGS rax=$-ENOSYS

	/* IRQs are off. */
	movq	%rsp, %rdi
	/* Sign extend the lower 32bit as syscall numbers are treated as int */
	movslq	%eax, %rsi

	/* clobbers %rax, make sure it is after saving the syscall nr */
	IBRS_ENTER
	UNTRAIN_RET
	CLEAR_BRANCH_HISTORY

	call	do_syscall_64		/* returns with IRQs disabled */

swapgs를 수행한다.

per-cpu 메모리에 userspace의 rsp 값을 저장해 놓는다.

PER_CPU_VAR 매크로

%gs:(cpu_tss_rw + TSS_sp2)(%rip)
gs_base 로 부터 rip + cpu_tss_rw + TSS_sp2의 위치에 있는 값을 의미한다.
참고로 gs_base address에는 per-cpu 메모리의 주소가 저장되어 있다.

cr3 레지스터를 바꾸어 커널의 PGD를 가리키게 한다. (KPTI 인듯)

SWITCH_TO_KERNEL_CR3 매크로

.macro SWITCH_TO_KERNEL_CR3 scratch_reg:req
	ALTERNATIVE "jmp .Lend_\@", "", X86_FEATURE_PTI
	movl	%cr3, \scratch_reg
	/* Test if we are already on kernel CR3 */
	testl	$PTI_SWITCH_MASK, \scratch_reg
	jz	.Lend_\@
	andl	$(~PTI_SWITCH_MASK), \scratch_reg
	movl	\scratch_reg, %cr3
	/* Return original CR3 in \scratch_reg */
	orl	$PTI_SWITCH_MASK, \scratch_reg
.Lend_\@:
.endm

cr3 레지스터를 rsp로 옮겨서 연산한다.
cr3의 PTI_SWITCH_MASK를 unset으로 설정해 주어 cr3가 커널의 PGD를 가리키도록 바꾸어 준다.

ALTERNATIVE 매크로는 ALTERNATIVE(oldinstr, newinstr, ft_flags) 형태를 가지고 있다.

  • oldinstr: 기본적으로 실행될 명령어
  • newinstr: 특정 CPU 기능이 있는 경우 대체될 명령어
  • ft_flags: CPU 기능 비트, 특정 CPU 기능을 나타내는 비트 마스크.

위의 코드의 경우 X86_FEATURE_PTI가 활성화 된 경우에는 "jmp .Lend_\@"""로 치환된다는 뜻이다.
즉, KPTI를 지원하지 않으면 그냥 jmp만 하고 끝난다.

PUSH_AND_CLEAR_REGS 매크로

.macro PUSH_REGS rdx=%rdx rcx=%rcx rax=%rax save_ret=0 unwind_hint=1
	.if \save_ret
	pushq	%rsi		/* pt_regs->si */
	movq	8(%rsp), %rsi	/* temporarily store the return address in %rsi */
	movq	%rdi, 8(%rsp)	/* pt_regs->di (overwriting original return address) */
	.else
	pushq   %rdi		/* pt_regs->di */
	pushq   %rsi		/* pt_regs->si */
	.endif
	pushq	\rdx		/* pt_regs->dx */
	pushq   \rcx		/* pt_regs->cx */
	pushq   \rax		/* pt_regs->ax */
	pushq   %r8		/* pt_regs->r8 */
	pushq   %r9		/* pt_regs->r9 */
	pushq   %r10		/* pt_regs->r10 */
	pushq   %r11		/* pt_regs->r11 */
	pushq	%rbx		/* pt_regs->rbx */
	pushq	%rbp		/* pt_regs->rbp */
	pushq	%r12		/* pt_regs->r12 */
	pushq	%r13		/* pt_regs->r13 */
	pushq	%r14		/* pt_regs->r14 */
	pushq	%r15		/* pt_regs->r15 */

	.if \unwind_hint
	UNWIND_HINT_REGS
	.endif

	.if \save_ret
	pushq	%rsi		/* return address on top of stack */
	.endif
.endm

.macro CLEAR_REGS clear_bp=1
	/*
	 * Sanitize registers of values that a speculation attack might
	 * otherwise want to exploit. The lower registers are likely clobbered
	 * well before they could be put to use in a speculative execution
	 * gadget.
	 */
	xorl	%esi,  %esi	/* nospec si  */
	xorl	%edx,  %edx	/* nospec dx  */
	xorl	%ecx,  %ecx	/* nospec cx  */
	xorl	%r8d,  %r8d	/* nospec r8  */
	xorl	%r9d,  %r9d	/* nospec r9  */
	xorl	%r10d, %r10d	/* nospec r10 */
	xorl	%r11d, %r11d	/* nospec r11 */
	xorl	%ebx,  %ebx	/* nospec rbx */
	.if \clear_bp
	xorl	%ebp,  %ebp	/* nospec rbp */
	.endif
	xorl	%r12d, %r12d	/* nospec r12 */
	xorl	%r13d, %r13d	/* nospec r13 */
	xorl	%r14d, %r14d	/* nospec r14 */
	xorl	%r15d, %r15d	/* nospec r15 */

.endm

.macro PUSH_AND_CLEAR_REGS rdx=%rdx rcx=%rcx rax=%rax save_ret=0 clear_bp=1 unwind_hint=1
	PUSH_REGS rdx=\rdx, rcx=\rcx, rax=\rax, save_ret=\save_ret unwind_hint=\unwind_hint
	CLEAR_REGS clear_bp=\clear_bp
.endm

말 그대로 레지스터 값들을 스택에 push 한 후에 레지스터 값들을 0으로 초기화한다.

레지스터 값들을 스택에 다 저장하고 난 이후에는 do_syscall_64(rsp, eax) 를 호출하게 된다.
레지스터 값들이 스택에 push 되어있는 상태이므로 rdi(=rsp)의 값은 struct pt_regs를 가리키게 되고, rsi(=eax)는 호출한 시스템 콜 넘버이다.

do_syscall_64() 리턴 이후

	ALTERNATIVE "testb %al, %al; jz swapgs_restore_regs_and_return_to_usermode", \
		"jmp swapgs_restore_regs_and_return_to_usermode", X86_FEATURE_XENPV

	/*
	 * We win! This label is here just for ease of understanding
	 * perf profiles. Nothing jumps here.
	 */
syscall_return_via_sysret:
	IBRS_EXIT
	POP_REGS pop_rdi=0

	/*
	 * Now all regs are restored except RSP and RDI.
	 * Save old stack pointer and switch to trampoline stack.
	 */
	movq	%rsp, %rdi
	movq	PER_CPU_VAR(cpu_tss_rw + TSS_sp0), %rsp
	UNWIND_HINT_END_OF_STACK

	pushq	RSP-RDI(%rdi)	/* RSP */
	pushq	(%rdi)		/* RDI */

	/*
	 * We are on the trampoline stack.  All regs except RDI are live.
	 * We can do future final exit work right here.
	 */
	STACKLEAK_ERASE_NOCLOBBER

	SWITCH_TO_USER_CR3_STACK scratch_reg=%rdi

	popq	%rdi
	popq	%rsp
SYM_INNER_LABEL(entry_SYSRETQ_unsafe_stack, SYM_L_GLOBAL)
	ANNOTATE_NOENDBR
	swapgs
	CLEAR_CPU_BUFFERS
	sysretq

do_syscall_64() 함수는 성공 시 true, 실패 시 false 를 리턴한다.
true라면 syscall_return_via_sysret이 실행되게 되고, sysretq를 통해서 유저영역으로 리턴된다.

POP_REGS를 통해 나머지 레지스터 값들을 복원하고, rsp 값을 usermode의 rsp 값으로 복원한 후 cr3 레지스터를 SWITCH_TO_USER_CR3_STACK을 통해 복원한다.

그 후 swapgs -> sysretq를 호출하여 유저모드로 복귀한다.

sysretq에서는 ss, cs, rflags, rip 값을 설정해 준다. 내부 동작은 투머치인듯.
https://www.felixcloutier.com/x86/sysret 여기에 적혀있긴 하다.

sysretq는 iretq에 비해서 더 빠르다고 한다.

profile
공부 내용 저장소

0개의 댓글

관련 채용 정보