← Documents Documentation/arch/x86/entry_64.rst GitHub 원문 ↗

Linux 6.18.37 · Architecture

Kernel Entries

x86-64 syscall·interrupt entry와 SWAPGS fast/paranoid gsbase 판별 규칙을 설명합니다.

Source pathDocumentation/arch/x86/entry_64.rst
Source versionLinux v6.18.37
TranslationDUJINLABS 전문 번역 + 해설

요약·해설과 원문, 전문 번역을 서로 분리했습니다. API 이름, symbol, source path는 원문 표기를 사용합니다.

1. 요약·해설

원문의 핵심 논리와 kernel programming 관점의 보충 설명입니다. 아래의 전문 번역과는 별도로 작성했습니다.

요약과 해설

entry_64.rst:1-110

x86-64 kernel entry는 syscall, compat syscall, IDT interrupt, APIC interrupt, architecture exception마다 calling convention과 stack frame이 다릅니다. 특히 `SWAPGS`는 user/kernel GS 전환을 정확히 한 번씩 맞춰야 합니다.

normal boundary는 saved CS로 fast check를 하지만 super-atomic entry는 CS를 저장하고 `SWAPGS`하기 전 틈에 끼어들 수 있어 `RDMSR` 기반 `paranoid=1` check가 필요합니다. 비용이 큰 경로는 꼭 필요한 vector에만 사용합니다.

2. 영어 원문 전체

번역 기준이 된 Linux v6.18.37 원문입니다. 줄 번호는 이 버전의 파일 좌표입니다.

원문 전체 펼치기
1 .. SPDX-License-Identifier: GPL-2.0
2
3 ==============
4 Kernel Entries
5 ==============
6
7 This file documents some of the kernel entries in
8 arch/x86/entry/entry_64.S. A lot of this explanation is adapted from
9 an email from Ingo Molnar:
10
11 https://lore.kernel.org/r/20110529191055.GC9835%40elte.hu
12
13 The x86 architecture has quite a few different ways to jump into
14 kernel code. Most of these entry points are registered in
15 arch/x86/kernel/traps.c and implemented in arch/x86/entry/entry_64.S
16 for 64-bit, arch/x86/entry/entry_32.S for 32-bit and finally
17 arch/x86/entry/entry_64_compat.S which implements the 32-bit compatibility
18 syscall entry points and thus provides for 32-bit processes the
19 ability to execute syscalls when running on 64-bit kernels.
20
21 The IDT vector assignments are listed in arch/x86/include/asm/irq_vectors.h.
22
23 Some of these entries are:
24
25 - system_call: syscall instruction from 64-bit code.
26
27 - entry_INT80_compat: int 0x80 from 32-bit or 64-bit code; compat syscall
28 either way.
29
30 - entry_INT80_compat, ia32_sysenter: syscall and sysenter from 32-bit
31 code
32
33 - interrupt: An array of entries. Every IDT vector that doesn't
34 explicitly point somewhere else gets set to the corresponding
35 value in interrupts. These point to a whole array of
36 magically-generated functions that make their way to common_interrupt()
37 with the interrupt number as a parameter.
38
39 - APIC interrupts: Various special-purpose interrupts for things
40 like TLB shootdown.
41
42 - Architecturally-defined exceptions like divide_error.
43
44 There are a few complexities here. The different x86-64 entries
45 have different calling conventions. The syscall and sysenter
46 instructions have their own peculiar calling conventions. Some of
47 the IDT entries push an error code onto the stack; others don't.
48 IDT entries using the IST alternative stack mechanism need their own
49 magic to get the stack frames right. (You can find some
50 documentation in the AMD APM, Volume 2, Chapter 8 and the Intel SDM,
51 Volume 3, Chapter 6.)
52
53 Dealing with the swapgs instruction is especially tricky. Swapgs
54 toggles whether gs is the kernel gs or the user gs. The swapgs
55 instruction is rather fragile: it must nest perfectly and only in
56 single depth, it should only be used if entering from user mode to
57 kernel mode and then when returning to user-space, and precisely
58 so. If we mess that up even slightly, we crash.
59
60 So when we have a secondary entry, already in kernel mode, we *must
61 not* use SWAPGS blindly - nor must we forget doing a SWAPGS when it's
62 not switched/swapped yet.
63
64 Now, there's a secondary complication: there's a cheap way to test
65 which mode the CPU is in and an expensive way.
66
67 The cheap way is to pick this info off the entry frame on the kernel
68 stack, from the CS of the ptregs area of the kernel stack::
69
70 xorl %ebx,%ebx
71 testl $3,CS+8(%rsp)
72 je error_kernelspace
73 SWAPGS
74
75 The expensive (paranoid) way is to read back the MSR_GS_BASE value
76 (which is what SWAPGS modifies)::
77
78 movl $1,%ebx
79 movl $MSR_GS_BASE,%ecx
80 rdmsr
81 testl %edx,%edx
82 js 1f /* negative -> in kernel */
83 SWAPGS
84 xorl %ebx,%ebx
85 1: ret
86
87 If we are at an interrupt or user-trap/gate-alike boundary then we can
88 use the faster check: the stack will be a reliable indicator of
89 whether SWAPGS was already done: if we see that we are a secondary
90 entry interrupting kernel mode execution, then we know that the GS
91 base has already been switched. If it says that we interrupted
92 user-space execution then we must do the SWAPGS.
93
94 But if we are in an NMI/MCE/DEBUG/whatever super-atomic entry context,
95 which might have triggered right after a normal entry wrote CS to the
96 stack but before we executed SWAPGS, then the only safe way to check
97 for GS is the slower method: the RDMSR.
98
99 Therefore, super-atomic entries (except NMI, which is handled separately)
100 must use idtentry with paranoid=1 to handle gsbase correctly. This
101 triggers three main behavior changes:
102
103 - Interrupt entry will use the slower gsbase check.
104 - Interrupt entry from user mode will switch off the IST stack.
105 - Interrupt exit to kernel mode will not attempt to reschedule.
106
107 We try to only use IST entries and the paranoid entry code for vectors
108 that absolutely need the more expensive check for the GS base - and we
109 generate all 'normal' entry points with the regular (faster) paranoid=0
110 variant.
111

3. 한국어 전문 번역

영어 원문의 문단 순서와 의미를 유지한 전체 번역입니다. 코드, 함수명, symbol과 URL은 원문 표기를 유지합니다.

entry source와 architecture 범위

1-22

이 `GPL-2.0` 문서는 `arch/x86/entry/entry_64.S`의 kernel entry 일부를 설명하며 Ingo Molnar의 email `https://lore.kernel.org/r/20110529191055.GC9835%40elte.hu`를 바탕으로 합니다.

x86에는 kernel code로 들어가는 경로가 많습니다. 대부분 `arch/x86/kernel/traps.c`에 등록되고 64-bit는 `arch/x86/entry/entry_64.S`, 32-bit는 `arch/x86/entry/entry_32.S`가 구현합니다. `arch/x86/entry/entry_64_compat.S`는 64-bit kernel에서 32-bit process가 syscall을 실행하는 compatibility entry를 구현합니다.

IDT vector assignment는 `arch/x86/include/asm/irq_vectors.h`에 있습니다.

syscall, interrupt와 exception entry

23-51
entrysource와 역할
`system_call`64-bit code의 `syscall` instruction
`entry_INT80_compat`32-bit 또는 64-bit code의 `int 0x80`; 어느 쪽이든 compat syscall
`entry_INT80_compat`, `ia32_sysenter`32-bit code의 syscall과 `sysenter`
`interrupt` array다른 곳을 명시적으로 가리키지 않는 모든 IDT vector가 대응 entry를 사용합니다. generated function array가 interrupt number를 argument로 `common_interrupt()`에 도달합니다.
APIC interruptsTLB shootdown 같은 special-purpose interrupt
architecture exception`divide_error` 같은 architecture-defined exception

x86-64 entry마다 calling convention이 다릅니다. `syscall`, `sysenter`는 고유 convention을 사용하고 일부 IDT entry는 stack에 error code를 push하지만 다른 entry는 그렇지 않습니다. IST alternative-stack을 쓰는 entry도 stack frame을 맞추기 위한 별도 처리가 필요합니다.

관련 architecture 설명은 AMD APM Volume 2 Chapter 8과 Intel SDM Volume 3 Chapter 6을 참조합니다.

SWAPGS의 fast와 paranoid check

52-86

`SWAPGS`는 `gs`가 kernel GS인지 user GS인지 전환합니다. 정확히 한 depth로 완벽히 nesting해야 하고 user mode에서 kernel mode로 들어갈 때와 userspace로 돌아갈 때만 정확히 사용해야 합니다. 조금만 어긋나도 crash합니다.

이미 kernel mode인 secondary entry에서는 `SWAPGS`를 무조건 실행해서도 안 되고 아직 switch하지 않았는데 빠뜨려서도 안 됩니다. 현재 mode를 확인하는 fast 방식과 expensive paranoid 방식이 있습니다.

fast 방식은 kernel stack의 entry frame, 즉 `ptregs` area의 CS에서 privilege level을 확인합니다.

xorl %ebx,%ebx
testl $3,CS+8(%rsp)
je error_kernelspace
SWAPGS

paranoid 방식은 `SWAPGS`가 바꾸는 `MSR_GS_BASE`를 `RDMSR`로 읽습니다. 상위 값이 negative이면 kernel GS이므로 swap하지 않고, 그렇지 않으면 `SWAPGS`를 실행합니다.

      movl $1,%ebx
      movl $MSR_GS_BASE,%ecx
      rdmsr
      testl %edx,%edx
      js 1f   /* negative -> in kernel */
      SWAPGS
      xorl %ebx,%ebx
1:        ret

entry boundary별 gsbase 판별

87-110

일반 interrupt 또는 user trap/gate boundary에서는 stack이 이미 `SWAPGS`했는지 신뢰할 수 있어 fast check를 사용합니다. kernel-mode 실행을 interrupt한 secondary entry라면 GS base가 이미 switch됐고, userspace를 interrupt했다면 `SWAPGS`가 필요합니다.

그러나 NMI/MCE/DEBUG 같은 super-atomic entry가 normal entry가 CS를 stack에 쓴 직후, 아직 `SWAPGS` 전인 순간에 발생할 수 있습니다. 이때 GS state를 안전하게 확인하는 유일한 방법은 느린 `RDMSR`입니다.

따라서 별도로 처리되는 NMI를 제외한 super-atomic entry는 `gsbase`를 올바르게 다루기 위해 `idtentry`의 `paranoid=1`을 사용합니다. 이 선택은 다음 세 behavior를 바꿉니다.

  • interrupt entry가 느린 gsbase check를 사용합니다.
  • user mode에서 온 interrupt entry가 IST stack을 switch off합니다.
  • kernel mode로 돌아가는 interrupt exit가 reschedule을 시도하지 않습니다.

expensive GS-base check가 꼭 필요한 vector에만 IST entry와 paranoid code를 사용하고 모든 normal entry point는 더 빠른 `paranoid=0` variant로 생성합니다.