역할과 입력
- EL1 호출 측: x0=HVC 요청 번호, x1 이후=요청 인자
- finalise_el2: w0=init_kernel_el이 반환한 부트 모드
원본 .S · 빌드 조건: Makefile · 명령·주소 표기 읽는 법
258줄 · SHA-256: e1bfc2a017b3807bcf7dd4b5d32dcdad268510d78692c4d9b0eb0c6a9a31b169
구조와 흐름
EL2 예외 벡터
2 KiB 정렬의 테이블에 128바이트 슬롯 16개를 배치합니다. EL2h 동기 예외와 64비트 EL1 동기 예외만 elx_sync로 연결됩니다. ventry는 B 한 개와 슬롯 패딩을 만드는 매크로입니다.
1/* SPDX-License-Identifier: GPL-2.0-only */
2/*
3 * Hypervisor stub
4 *
5 * Copyright (C) 2012 ARM Ltd.
6 * Author: Marc Zyngier <marc.zyngier@arm.com>
7 */
8
9#include <linux/init.h>
10#include <linux/linkage.h>
11
12#include <asm/assembler.h>
13#include <asm/el2_setup.h>
14#include <asm/kvm_arm.h>
15#include <asm/kvm_asm.h>
16#include <asm/ptrace.h>
17#include <asm/virt.h>
18
19 .text
20 .pushsection .hyp.text, "ax"
21
22 .align 11
23
24SYM_CODE_START(__hyp_stub_vectors)
25 ventry el2_sync_invalid // Synchronous EL2t
26 ventry el2_irq_invalid // IRQ EL2t
27 ventry el2_fiq_invalid // FIQ EL2t
28 ventry el2_error_invalid // Error EL2t
29
30 ventry elx_sync // Synchronous EL2h
31 ventry el2_irq_invalid // IRQ EL2h
32 ventry el2_fiq_invalid // FIQ EL2h
33 ventry el2_error_invalid // Error EL2h
34
35 ventry elx_sync // Synchronous 64-bit EL1
36 ventry el1_irq_invalid // IRQ 64-bit EL1
37 ventry el1_fiq_invalid // FIQ 64-bit EL1
38 ventry el1_error_invalid // Error 64-bit EL1
39
40 ventry el1_sync_invalid // Synchronous 32-bit EL1
41 ventry el1_irq_invalid // IRQ 32-bit EL1
42 ventry el1_fiq_invalid // FIQ 32-bit EL1
43 ventry el1_error_invalid // Error 32-bit EL1
44SYM_CODE_END(__hyp_stub_vectors)
45
46 .align 11
47
- L9
- 초기화 코드·데이터의 섹션을 지정하는 커널 공통 정의를 포함합니다. 이 파일의 실제 EL2 stub 코드 위치는 아래 .hyp.text와 .idmap.text 지시문으로 지정합니다. #include는 전처리 단계에서 헤더 내용을 가져옵니다. CPU가 이 줄에서 함수를 호출하는 동작은 없습니다.
- L10
- EL2 벡터·특수 코드와 C 호출용 래퍼의 심볼을 선언할 매크로를 가져옵니다. 특수 예외 진입점은 일반 함수와 구분하여 CODE 심볼로 표시합니다. #include는 전처리 단계에서 헤더 내용을 가져옵니다. CPU가 이 줄에서 함수를 호출하는 동작은 없습니다.
- L12
- 128바이트 벡터 슬롯을 만드는 ventry, 시스템 레지스터 접근, SCTLR 설정 같은 ARM64 공통 어셈블리 도구를 가져옵니다. #include는 전처리 단계에서 헤더 내용을 가져옵니다. CPU가 이 줄에서 함수를 호출하는 동작은 없습니다.
- L13
- EL2 기능의 최종 설정과 CPU 기능 override 검사 매크로를 가져옵니다. finalise_el2_state와 check_override의 실제 구현이 이 헤더에 있습니다. #include는 전처리 단계에서 헤더 내용을 가져옵니다. CPU가 이 줄에서 함수를 호출하는 동작은 없습니다.
- L14
- HCR·MDCR 등의 가상화 제어 비트 정의를 가져옵니다. 커널을 VHE 호스트로 옮길 때 HCR_HOST_VHE_FLAGS와 추적 버퍼 제어 비트를 사용합니다. #include는 전처리 단계에서 헤더 내용을 가져옵니다. CPU가 이 줄에서 함수를 호출하는 동작은 없습니다.
- L15
- KVM과 저수준 EL2 코드가 공유하는 어셈블리용 정의를 포함합니다. 현재 파일의 stub이 처리하는 요청 종류 자체는 asm/virt.h의 HVC_* 정의에 따릅니다. #include는 전처리 단계에서 헤더 내용을 가져옵니다. CPU가 이 줄에서 함수를 호출하는 동작은 없습니다.
- L16
- PSTATE의 실행 모드 비트와 EL2h 값을 가져옵니다. 예외 복귀 시 커널이 EL1이 아닌 EL2h에서 이어지도록 SPSR의 모드를 바꿀 때 사용합니다. #include는 전처리 단계에서 헤더 내용을 가져옵니다. CPU가 이 줄에서 함수를 호출하는 동작은 없습니다.
- L17
- HVC 요청 번호와 오류값, EL1·EL2 출발 모드값을 가져옵니다. 요청 번호는 x0로 전달되며 CurrentEL과 출발 모드를 구별하는 기준도 여기에서 정의합니다. #include는 전처리 단계에서 헤더 내용을 가져옵니다. CPU가 이 줄에서 함수를 호출하는 동작은 없습니다.
- L19
- 기본 커널 실행 코드 섹션을 선택합니다. 실제 EL2 벡터·처리기는 다음 .hyp.text에 배치하고 C 호출용 래퍼에서 이 .text로 돌아옵니다. 이는 빌드 중 출력할 바이트의 섹션을 선택하는 지시문이며, 실행 중 PC나 SP를 옮기지 않습니다.
- L20
- 아직 본격적인 하이퍼바이저가 설치되기 전 EL2에서 사용할 최소 처리 코드를 .hyp.text에 둡니다. 벡터 교체, CPU 재시작, EL2 설정 마무리 같은 제한된 요청을 처리합니다. 이는 빌드 중 출력할 바이트의 섹션을 선택하는 지시문이며, 실행 중 PC나 SP를 옮기지 않습니다. 이전 출력 섹션을 함께 기억하므로 뒤의 .popsection으로 되돌릴 수 있습니다.
- L22
- EL2 벡터 테이블의 시작을 2KiB 경계에 맞춥니다. VBAR_EL2가 가리키는 테이블에 요구되는 정렬이며, 각 예외 종류는 정해진 오프셋의 슬롯으로 들어옵니다. AArch64의 .align 인자는 바이트 수가 아니라 2의 지수입니다. 빌드할 때 필요한 패딩을 넣어 그 경계를 맞춥니다.
- L24
- EL2 예외의 시작 위치들을 모은 벡터 테이블입니다. 현재 EL의 스택 선택과 하위 EL의 실행 상태에 따라 16개 슬롯을 배치하며, 이 stub이 지원하는 동기 예외만 elx_sync로 연결합니다. 이 매크로는 정렬된 코드 시작 심벌을 정의하고 다른 오브젝트에서도 참조할 수 있게 합니다.
- L25
- 현재 EL2의 SP_EL0 선택(EL2t)에서 들어온 동기 예외의 128바이트 벡터 슬롯입니다. 이 최소 stub의 지원 범위 밖이므로 해당 invalid 처리기로 연결하여 진행을 멈춥니다.
- L26
- 현재 EL2t에서 들어온 IRQ 예외의 128바이트 벡터 슬롯입니다. 이 최소 stub의 지원 범위 밖이므로 해당 invalid 처리기로 연결하여 진행을 멈춥니다.
- L27
- 현재 EL2t에서 들어온 FIQ 예외의 128바이트 벡터 슬롯입니다. 이 최소 stub의 지원 범위 밖이므로 해당 invalid 처리기로 연결하여 진행을 멈춥니다.
- L28
- 현재 EL2t에서 들어온 SError 예외의 128바이트 벡터 슬롯입니다. 이 최소 stub의 지원 범위 밖이므로 해당 invalid 처리기로 연결하여 진행을 멈춥니다.
- L30
- 현재 EL2의 SP_EL2 선택(EL2h)에서 들어온 동기 예외의 128바이트 벡터 슬롯입니다. elx_sync로 연결하여 x0에 지정한 stub 서비스를 해석합니다.
- L31
- 현재 EL2h에서 들어온 IRQ 예외의 128바이트 벡터 슬롯입니다. 이 최소 stub의 지원 범위 밖이므로 해당 invalid 처리기로 연결하여 진행을 멈춥니다.
- L32
- 현재 EL2h에서 들어온 FIQ 예외의 128바이트 벡터 슬롯입니다. 이 최소 stub의 지원 범위 밖이므로 해당 invalid 처리기로 연결하여 진행을 멈춥니다.
- L33
- 현재 EL2h에서 들어온 SError 예외의 128바이트 벡터 슬롯입니다. 이 최소 stub의 지원 범위 밖이므로 해당 invalid 처리기로 연결하여 진행을 멈춥니다.
- L35
- 하위 AArch64 EL에서 들어온 동기 예외의 128바이트 벡터 슬롯입니다. elx_sync로 연결하여 x0에 지정한 stub 서비스를 해석합니다.
- L36
- 하위 AArch64 EL에서 들어온 IRQ 예외의 128바이트 벡터 슬롯입니다. 이 최소 stub의 지원 범위 밖이므로 해당 invalid 처리기로 연결하여 진행을 멈춥니다.
- L37
- 하위 AArch64 EL에서 들어온 FIQ 예외의 128바이트 벡터 슬롯입니다. 이 최소 stub의 지원 범위 밖이므로 해당 invalid 처리기로 연결하여 진행을 멈춥니다.
- L38
- 하위 AArch64 EL에서 들어온 SError 예외의 128바이트 벡터 슬롯입니다. 이 최소 stub의 지원 범위 밖이므로 해당 invalid 처리기로 연결하여 진행을 멈춥니다.
- L40
- 하위 AArch32 EL에서 들어온 동기 예외의 128바이트 벡터 슬롯입니다. 이 최소 stub의 지원 범위 밖이므로 해당 invalid 처리기로 연결하여 진행을 멈춥니다.
- L41
- 하위 AArch32 EL에서 들어온 IRQ 예외의 128바이트 벡터 슬롯입니다. 이 최소 stub의 지원 범위 밖이므로 해당 invalid 처리기로 연결하여 진행을 멈춥니다.
- L42
- 하위 AArch32 EL에서 들어온 FIQ 예외의 128바이트 벡터 슬롯입니다. 이 최소 stub의 지원 범위 밖이므로 해당 invalid 처리기로 연결하여 진행을 멈춥니다.
- L43
- 하위 AArch32 EL에서 들어온 SError 예외의 128바이트 벡터 슬롯입니다. 이 최소 stub의 지원 범위 밖이므로 해당 invalid 처리기로 연결하여 진행을 멈춥니다.
- L44
- (__hyp_stub_vectors)의 심볼 타입과 크기를 기록합니다. 실행을 끝내는 RET를 생성하지 않습니다.
- L46
- 벡터 슬롯 배치가 끝난 뒤 다음 처리 코드를 2KiB 경계에 맞춥니다. 마지막 벡터 슬롯 뒤로 처리 코드가 섞이지 않도록 경계를 확정합니다. AArch64의 .align 인자는 바이트 수가 아니라 2의 지수입니다. 빌드할 때 필요한 패딩을 넣어 그 경계를 맞춥니다.
HVC 번호별 분기
SET_VECTORS는 x1을 VBAR_EL2에 씁니다. SOFT_RESTART는 x1의 진입점과 x2–x4의 인자를 재배치한 뒤 BR하며 반환하지 않습니다. RESET_VECTORS는 stub 상태에서는 성공으로 끝납니다. 나머지 요청은 HVC_STUB_ERR를 x0에 넣어 ERET합니다.
48SYM_CODE_START_LOCAL(elx_sync)
49 cmp x0, #HVC_SET_VECTORS
50 b.ne 1f
51 msr vbar_el2, x1
52 b 9f
53
541: cmp x0, #HVC_FINALISE_EL2
55 b.eq __finalise_el2
56
572: cmp x0, #HVC_SOFT_RESTART
58 b.ne 3f
59 mov x0, x2
60 mov x2, x4
61 mov x4, x1
62 mov x1, x3
63 br x4 // no return
64
653: cmp x0, #HVC_RESET_VECTORS
66 beq 9f // Nothing to reset!
67
68 /* Someone called kvm_call_hyp() against the hyp-stub... */
69 mov_q x0, HVC_STUB_ERR
70 eret
71
729: mov x0, xzr
73 eret
74SYM_CODE_END(elx_sync)
75
- L48
- 지원하는 동기 예외에서 x0의 hypercall 번호를 해석하는 처리기입니다. 이 stub은 일반적인 EL2 예외를 모두 복구하는 운영체제 예외 처리기가 아닙니다. 이 매크로는 정렬된 코드 시작 심벌을 정의합니다. LOCAL 형식은 그 이름을 현재 오브젝트 안에서만 쓰도록 합니다.
- L49
- x0가 HVC_SET_VECTORS인지 검사합니다. 이 요청은 x1에 받은 새 벡터 테이블의 물리 주소를 VBAR_EL2에 설치하는 작업입니다. CMP는 두 피연산자의 뺄셈 결과로 NZCV 조건 플래그만 갱신하고 원래 레지스터 값은 유지합니다.
- L50
- 벡터 교체 요청이 아니면 다음 서비스 번호인 HVC_FINALISE_EL2를 검사합니다. 앞서 계산한 NZCV 중 Z=0 조건으로 분기합니다. 이 줄은 두 값을 새로 비교하지 않으며 조건이 맞지 않으면 다음 명령으로 진행합니다.
- L51
- x1의 새 벡터 테이블 주소를 VBAR_EL2에 설치합니다. 호출자는 2KiB 정렬 등 벡터 요구 조건을 만족하는 주소를 넘겨야 합니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.
- L52
- 벡터 설치를 마쳤으므로 성공값 0을 돌려주는 공통 반환 지점으로 갑니다. 이 분기는 LR에 새 복귀 주소를 기록하지 않습니다.
- L54
- EL2 기능 설정을 마무리하고 가능하면 VHE로 전환하는 HVC_FINALISE_EL2 요청인지 검사합니다. CMP는 두 피연산자의 뺄셈 결과로 NZCV 조건 플래그만 갱신하고 원래 레지스터 값은 유지합니다.
- L55
- 해당 요청이면 __finalise_el2로 넘어갑니다. 지원 기능을 설정한 뒤 커널을 EL2에서 실행할 수 있는지 별도 조건을 확인합니다. 앞서 계산한 NZCV 중 Z=1 조건으로 분기합니다. 이 줄은 두 값을 새로 비교하지 않으며 조건이 맞지 않으면 다음 명령으로 진행합니다.
- L57
- HVC_SOFT_RESTART인지 검사합니다. 이 요청은 현재 실행을 끝내고 x1의 새 진입점으로 제어를 넘기는 작업입니다. CMP는 두 피연산자의 뺄셈 결과로 NZCV 조건 플래그만 갱신하고 원래 레지스터 값은 유지합니다.
- L58
- 재시작 요청이 아니면 마지막으로 벡터 원상 복구 요청인지 검사합니다. 앞서 계산한 NZCV 중 Z=0 조건으로 분기합니다. 이 줄은 두 값을 새로 비교하지 않으며 조건이 맞지 않으면 다음 명령으로 진행합니다.
- L59
- 새 진입점의 첫 번째 인자를 요청 시 x2에서 x0로 옮깁니다. 요청 번호가 차지했던 x0를 실제 부팅 인자로 재사용합니다. MOV는 x0의 기존 값을 대체합니다. 원본이 주소값이어도 그 주소의 메모리를 읽거나 복사하지 않습니다.
- L60
- 새 진입점의 세 번째 인자를 x4에서 x2로 옮깁니다. 곧 x4를 분기 주소 보관에 재사용하므로 먼저 인자를 옮깁니다. MOV는 x2의 기존 값을 대체합니다. 원본이 주소값이어도 그 주소의 메모리를 읽거나 복사하지 않습니다.
- L61
- 새 진입 주소를 x1에서 x4로 보존합니다. x1은 다음 줄에서 두 번째 인자로 바뀝니다. MOV는 x4의 기존 값을 대체합니다. 원본이 주소값이어도 그 주소의 메모리를 읽거나 복사하지 않습니다.
- L62
- 새 진입점의 두 번째 인자를 x3에서 x1로 옮겨 x0~x2의 인자 배치를 완성합니다. MOV는 x1의 기존 값을 대체합니다. 원본이 주소값이어도 그 주소의 메모리를 읽거나 복사하지 않습니다.
- L63
- EL2에서 x4의 새 코드로 직접 분기합니다. HVC 호출자로 ERET하는 경로가 아니므로 이전 커널 실행은 여기서 끝납니다. 이 분기는 LR에 새 복귀 주소를 기록하지 않습니다. 레지스터가 담은 주소로 실행 위치를 바꾸며, 그 주소에서 값을 읽어 다른 레지스터에 복사하는 load 명령은 아닙니다.
- L65
- HVC_RESET_VECTORS 요청인지 확인합니다. 본격적인 KVM 벡터를 사용하는 경우에는 원래 stub 벡터로 되돌릴 때 쓰는 번호입니다. CMP는 두 피연산자의 뺄셈 결과로 NZCV 조건 플래그만 갱신하고 원래 레지스터 값은 유지합니다.
- L66
- 현재는 이미 원래 hyp stub이 요청을 받고 있으므로 다시 설치할 벡터가 없습니다. 성공값을 반환하는 경로로 넘어갑니다. 앞서 계산한 NZCV 중 Z=1 조건으로 분기합니다. 이 줄은 두 값을 새로 비교하지 않으며 조건이 맞지 않으면 다음 명령으로 진행합니다.
- L69
- 지원하지 않는 요청에 대해 HVC_STUB_ERR인 0xbadca11을 x0에 넣습니다. 완전한 KVM 하이퍼바이저가 있어야 처리할 요청을 이 최소 stub에 보낸 경우도 여기에 해당합니다. mov_q는 64비트 상수를 필요한 MOVZ/MOVN·MOVK 조합으로 구성하는 매크로입니다. 해당 주소의 메모리에서 데이터를 가져오는 명령은 아닙니다.
- L70
- 예외 복귀로 요청한 코드에 돌아가며 x0의 오류값을 전달합니다. 일반 RET과 달리 예외 진입 때 저장한 PC·PSTATE를 사용합니다. 복귀 PC는 ELR에서, 예외 레벨·스택 선택·마스크를 포함한 PSTATE는 SPSR에서 가져옵니다.
- L72
- 처리한 요청의 성공값 0을 x0에 넣습니다. MOV는 x0의 기존 값을 대체합니다. 원본이 주소값이어도 그 주소의 메모리를 읽거나 복사하지 않습니다.
- L73
- 예외 진입 전에 실행하던 코드로 돌아가 성공 결과를 전달합니다. 복귀 PC는 ELR에서, 예외 레벨·스택 선택·마스크를 포함한 PSTATE는 SPSR에서 가져옵니다.
- L74
- (elx_sync)의 심볼 타입과 크기를 기록합니다. 실행을 끝내는 RET를 생성하지 않습니다.
VHE 전환 조건
finalise_el2_state로 나머지 EL2 기능을 설정한 뒤 SCTLR_EL2.M=0을 확인합니다. check_override는 CPU의 VHE 기능과 기능 override를 함께 검사합니다. hypervisor만 VHE를 쓰도록 요청한 HVHE 설정이 있으면 host 커널의 VHE 전환은 거절합니다.
76SYM_CODE_START_LOCAL(__finalise_el2)
77 finalise_el2_state
78
79 // nVHE? No way! Give me the real thing!
80 // Sanity check: MMU *must* be off
81 mrs x1, sctlr_el2
82 tbnz x1, #0, 1f
83
84 // Needs to be VHE capable, obviously
85 check_override id_aa64mmfr1 ID_AA64MMFR1_EL1_VH_SHIFT 0f 1f x1 x2
86
870: // Check whether we only want the hypervisor to run VHE, not the kernel
88 adr_l x1, arm64_sw_feature_override
89 ldr x2, [x1, FTR_OVR_VAL_OFFSET]
90 ldr x1, [x1, FTR_OVR_MASK_OFFSET]
91 and x2, x2, x1
92 ubfx x2, x2, #ARM64_SW_FEATURE_OVERRIDE_HVHE, #4
93 cbz x2, 2f
94
951: mov_q x0, HVC_STUB_ERR
96 eret
972:
98 // Engage the VHE magic!
99 mov_q x0, HCR_HOST_VHE_FLAGS
100 msr_hcr_el2 x0
101 isb
102
- L76
- HVC_FINALISE_EL2의 실제 처리기입니다. 먼저 CPU 기능에 맞는 EL2 상태를 마무리하고, 이후 VHE 전환 조건을 검사합니다. VHE는 커널이 EL2에서 호스트로 실행되도록 하는 하드웨어 기능입니다. 이 매크로는 정렬된 코드 시작 심벌을 정의합니다. LOCAL 형식은 그 이름을 현재 오브젝트 안에서만 쓰도록 합니다.
- L77
- CPU 기능과 override 설정을 확인하며 MPAM, GCS, SVE, SME 등 지원되는 기능의 EL2 제어 상태를 마무리합니다. 예를 들어 MPAM trap을 정리하고 SVE·SME 접근과 벡터 길이를 설정합니다. 이 매크로 자체가 곧바로 커널을 VHE로 옮기는 것은 아닙니다.
- L81
- EL2의 SCTLR을 읽어 현재 EL2 MMU가 꺼져 있는지 확인할 준비를 합니다. 이어지는 VHE 주소 변환 환경 교체는 MMU가 꺼져 있다는 전제를 가집니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.
- L82
- SCTLR_EL2의 M 비트인 비트 0이 1이면 전환을 거부합니다. 이미 EL2 MMU를 쓰는 상태에서 이 초기 전환 절차를 수행하지 않도록 막습니다. 이 분기는 지정 레지스터의 값이나 비트를 직접 검사하며 CMP가 남긴 NZCV를 읽거나 바꾸지 않습니다. 조건이 맞지 않으면 바로 다음 명령으로 진행합니다.
- L85
- ID_AA64MMFR1_EL1의 VH 필드와 커널의 기능 override를 함께 검사합니다. VHE가 실제로 지원되고 설정상 허용되면 0f로, 사용할 수 없으면 오류 경로 1f로 갑니다. x1·x2는 검사에 사용하는 작업 레지스터입니다.
- L87
- 하드웨어 VHE 검사에 통과한 뒤 소프트웨어의 hVHE 정책을 검사하는 지점입니다. CPU 지원만으로 커널의 EL2 실행을 결정하지 않습니다. 레이블은 이 위치에 붙인 이름이며, 이름을 적는 것만으로 CPU 명령이 추가되지는 않습니다.
- L88
- 소프트웨어 기능 override 구조체의 주소를 구합니다. CPU에 VHE가 있더라도 커널 전체를 EL2로 옮길지 여부는 소프트웨어 정책까지 확인해야 합니다. 여기서 얻는 것은 레이블의 주소이며, 그 위치의 메모리 내용은 아직 읽지 않습니다. adr_l 매크로는 ADRP와 ADD로 주소의 페이지 부분과 하위 오프셋을 합칩니다.
- L89
- override가 지정한 기능값을 x2에 읽습니다. 실제로 x1 + (FTR_OVR_VAL_OFFSET) 주소의 메모리 8바이트를 x2에 읽습니다. 이 주소 형식은 기준 레지스터 x1 자체를 갱신하지 않습니다.
- L90
- 어떤 기능값을 덮어쓰도록 지정했는지를 나타내는 mask를 x1에 읽습니다. 지정하지 않은 비트는 정책 판단에 포함하지 않습니다. 실제로 x1 + (FTR_OVR_MASK_OFFSET) 주소의 메모리 8바이트를 x1에 읽습니다.
- L91
- 기능값과 mask를 AND하여 실제로 지정한 override 비트만 남깁니다. AND는 두 피연산자의 같은 위치 비트가 모두 1인 곳만 결과에 남깁니다.
- L92
- hVHE 정책 필드의 4비트를 꺼냅니다. hVHE를 선택한 경우 하이퍼바이저는 VHE를 사용하되 일반 커널은 EL1에 남아야 합니다. 추출한 필드는 결과의 하위 비트에 놓이고 나머지 상위 비트는 0으로 채워집니다.
- L93
- hVHE 정책이 0이면 일반 커널까지 VHE로 옮기는 전환을 진행합니다. 0이 아니면 아래 오류 반환 경로로 흘러가 EL1 커널을 유지합니다. 이 분기는 지정 레지스터의 값이나 비트를 직접 검사하며 CMP가 남긴 NZCV를 읽거나 바꾸지 않습니다. 조건이 맞지 않으면 바로 다음 명령으로 진행합니다.
- L95
- VHE 전환을 진행할 수 없는 경우 HVC_STUB_ERR를 x0에 넣습니다. 앞서 수행한 EL2 기능 초기화와 커널의 EL2 이동 성공 여부는 구분해야 합니다. mov_q는 64비트 상수를 필요한 MOVZ/MOVN·MOVK 조합으로 구성하는 매크로입니다. 해당 주소의 메모리에서 데이터를 가져오는 명령은 아닙니다.
- L96
- 전환하지 않은 상태로 HVC 호출자에게 복귀합니다. 복귀 PC는 ELR에서, 예외 레벨·스택 선택·마스크를 포함한 PSTATE는 SPSR에서 가져옵니다.
- L97
- VHE 전환 조건을 모두 통과했을 때 들어오는 지점입니다. 여기부터 HCR·스택·주소 변환 상태를 EL2 호스트용으로 옮깁니다. 레이블은 이 위치에 붙인 이름이며, 이름을 적는 것만으로 CPU 명령이 추가되지는 않습니다.
- L99
- HCR_HOST_VHE_FLAGS를 준비합니다. AArch64 실행을 지정하는 RW, 호스트 레지스터 동작을 선택하는 E2H, EL2 호스트로 예외를 향하게 하는 TGE와 AMO·IMO·FMO 등이 포함됩니다. mov_q는 64비트 상수를 필요한 MOVZ/MOVN·MOVK 조합으로 구성하는 매크로입니다. 해당 주소의 메모리에서 데이터를 가져오는 명령은 아닙니다.
- L100
- HCR_EL2에 VHE 호스트 플래그를 적용합니다. CONFIG_AMPERE_ERRATUM_AC04_CPU_23이 켜진 빌드는 Ampere CPU의 해당 문제를 우회하도록 DSB NSH와 ISB를 쓰기 앞뒤에 추가합니다. 이후 EL1 이름으로 접근하는 여러 레지스터가 EL2 호스트용 별칭으로 동작합니다.
- L101
- HCR 변경을 반영합니다. 바로 뒤의 스택·시스템 레지스터 이전은 VHE 호스트 레지스터 접근 규칙을 전제로 합니다.
EL1의 상태를 host EL2로 복사
HCR_HOST_VHE_FLAGS를 적용한 뒤 일부 EL1 이름은 host EL2 레지스터를 가리킵니다. 원래 EL1 상태는 EL12 이름으로 읽습니다. 스택·CPU별 오프셋·벡터·TCR·TTBR·MAIR를 옮기고 지원되는 경우 TCR2와 PIE 상태도 옮깁니다. 복귀 PSTATE의 모드 비트를 EL2h로 바꿉니다.
103 // Use the EL1 allocated stack, per-cpu offset
104 mrs x0, sp_el1
105 mov sp, x0
106 mrs x0, tpidr_el1
107 msr tpidr_el2, x0
108
109 // FP configuration, vectors
110 mrs_s x0, SYS_CPACR_EL12
111 msr cpacr_el1, x0
112 mrs_s x0, SYS_VBAR_EL12
113 msr vbar_el1, x0
114
115 // Use EL2 translations for SPE & TRBE and disable access from EL1
116 mrs x0, mdcr_el2
117 bic x0, x0, #MDCR_EL2_E2PB_MASK
118 bic x0, x0, #MDCR_EL2_E2TB_MASK
119 msr mdcr_el2, x0
120
121 // Transfer the MM state from EL1 to EL2
122 mrs_s x0, SYS_TCR_EL12
123 msr tcr_el1, x0
124 mrs_s x0, SYS_TTBR0_EL12
125 msr ttbr0_el1, x0
126 mrs_s x0, SYS_TTBR1_EL12
127 msr ttbr1_el1, x0
128 mrs_s x0, SYS_MAIR_EL12
129 msr mair_el1, x0
130 mrs x1, REG_ID_AA64MMFR3_EL1
131 ubfx x1, x1, #ID_AA64MMFR3_EL1_TCRX_SHIFT, #4
132 cbz x1, .Lskip_tcr2
133 mrs x0, REG_TCR2_EL12
134 msr REG_TCR2_EL1, x0
135
136 // Transfer permission indirection state
137 mrs x1, REG_ID_AA64MMFR3_EL1
138 ubfx x1, x1, #ID_AA64MMFR3_EL1_S1PIE_SHIFT, #4
139 cbz x1, .Lskip_indirection
140 mrs x0, REG_PIRE0_EL12
141 msr REG_PIRE0_EL1, x0
142 mrs x0, REG_PIR_EL12
143 msr REG_PIR_EL1, x0
144
145.Lskip_indirection:
146.Lskip_tcr2:
147
148 isb
149
150 // Hack the exception return to stay at EL2
151 mrs x0, spsr_el1
152 and x0, x0, #~PSR_MODE_MASK
153 mov x1, #PSR_MODE_EL2h
154 orr x0, x0, x1
155 msr spsr_el1, x0
156
157 b enter_vhe
158SYM_CODE_END(__finalise_el2)
- L104
- 기존 EL1 커널이 사용하던 스택 포인터를 SP_EL1에서 읽습니다. EL2로 옮겨도 같은 커널 호출 흐름을 이어가기 위한 값입니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.
- L105
- EL2의 현재 SP에 기존 EL1 스택 포인터를 설치합니다. 커널의 프레임을 새로 만드는 것이 아니라 같은 스택을 이어 사용합니다. 바뀌는 것은 SP가 담은 주소입니다. 원래 스택의 데이터가 새 주소로 복사되는 것은 아닙니다.
- L106
- EL1에서 유지하던 per-CPU 오프셋을 TPIDR_EL1에서 읽습니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.
- L107
- per-CPU 오프셋을 TPIDR_EL2에 옮깁니다. VHE로 실행 위치가 바뀐 뒤에도 현재 CPU의 커널 데이터를 같은 방식으로 찾기 위해 필요합니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.
- L110
- EL12 별칭으로 기존 EL1의 FP/SIMD 등 접근 제어값 CPACR을 읽습니다. VHE 설정 이후에도 EL12 이름은 이전 EL1 상태를 명시적으로 가리킵니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.
- L111
- 읽은 접근 제어값을 VHE 호스트의 CPACR_EL1 접근으로 설치합니다. 커널이 EL2로 이동해도 FP/SIMD 등의 허용 상태를 이어받습니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.
- L112
- 기존 EL1의 예외 벡터 주소를 VBAR_EL12로 읽습니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.
- L113
- 같은 커널 예외 벡터를 VHE 호스트 쪽에 설치합니다. EL2에서 커널을 실행할 때도 원래 커널 예외 처리 코드로 연결해야 합니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.
- L116
- 디버그·추적 관련 EL2 제어값 MDCR_EL2를 읽습니다. SPE와 TRBE가 버퍼에 접근할 주소 변환 환경도 커널의 새 실행 EL에 맞춰야 합니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.
- L117
- E2PB 필드를 지워 SPE의 profiling buffer가 EL2 주소 변환을 사용하고 EL1에서 해당 제어에 접근하지 못하도록 하는 설정을 준비합니다. BIC는 마스크에서 1인 위치만 0으로 만들고 나머지 비트는 유지합니다.
- L118
- E2TB 필드도 지워 TRBE의 trace buffer에 같은 EL2 호스트 정책을 적용합니다. BIC는 마스크에서 1인 위치만 0으로 만들고 나머지 비트는 유지합니다.
- L119
- 수정한 SPE·TRBE 제어값을 MDCR_EL2에 기록합니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.
- L122
- 기존 EL1의 주소 변환 제어값을 TCR_EL12로 읽습니다. 주소 크기와 페이지 변환 방식 등의 설정을 VHE 호스트가 이어받아야 합니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.
- L123
- 기존 TCR 값을 VHE 호스트의 변환 제어 레지스터에 설치합니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.
- L124
- 기존 EL1의 TTBR0 페이지 테이블 기준값을 읽습니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.
- L125
- TTBR0 기준값을 VHE 호스트의 주소 변환 환경으로 옮깁니다. 이미 쓰던 낮은 가상 주소 영역의 매핑을 이어받습니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.
- L126
- 기존 EL1의 TTBR1 페이지 테이블 기준값을 읽습니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.
- L127
- TTBR1 기준값을 VHE 호스트에 설치하여 커널 가상 주소 매핑을 이어받습니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.
- L128
- 기존 EL1의 MAIR 메모리 속성표를 읽습니다. 페이지 테이블의 AttrIndx가 뜻하는 장치·일반 메모리 속성을 유지해야 합니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.
- L129
- 같은 MAIR 값을 VHE 호스트 쪽에 설치하여 동일한 페이지 테이블이 같은 메모리 속성으로 해석되게 합니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.
- L130
- CPU의 ID_AA64MMFR3_EL1을 읽어 추가 변환 제어 레지스터 TCR2 지원 여부를 확인합니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.
- L131
- TCRX 기능 필드 4비트를 꺼내 TCR2 레지스터가 있는지 판단할 값을 만듭니다. 추출한 필드는 결과의 하위 비트에 놓이고 나머지 상위 비트는 0으로 채워집니다.
- L132
- TCR2를 지원하지 않으면 해당 레지스터에 접근하지 않고 건너뜁니다. 이 소스의 건너뛰기 지점은 아래 permission indirection 이전 구간까지 지난 위치에 있습니다. 이 분기는 지정 레지스터의 값이나 비트를 직접 검사하며 CMP가 남긴 NZCV를 읽거나 바꾸지 않습니다. 조건이 맞지 않으면 바로 다음 명령으로 진행합니다.
- L133
- 지원되는 CPU에서 기존 EL1의 TCR2_EL12 값을 읽습니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.
- L134
- 추가 주소 변환 제어값을 VHE 호스트의 TCR2에 옮깁니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.
- L137
- 같은 기능 레지스터를 다시 읽어 Stage-1 Permission Indirection 지원 여부를 검사합니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.
- L138
- S1PIE 기능 필드를 꺼냅니다. 이 기능은 페이지별 권한을 간접 권한표로 해석할 수 있게 합니다. 추출한 필드는 결과의 하위 비트에 놓이고 나머지 상위 비트는 0으로 채워집니다.
- L139
- 권한 간접 참조를 지원하지 않으면 PIR 계열 레지스터 읽기·쓰기를 건너뜁니다. 이 분기는 지정 레지스터의 값이나 비트를 직접 검사하며 CMP가 남긴 NZCV를 읽거나 바꾸지 않습니다. 조건이 맞지 않으면 바로 다음 명령으로 진행합니다.
- L140
- 기존 EL1의 EL0용 권한 간접 참조표 PIRE0를 읽습니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.
- L141
- PIRE0 값을 VHE 호스트 쪽으로 옮겨 EL0 접근 권한 해석을 유지합니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.
- L142
- 기존 EL1의 PIR 권한 간접 참조표를 읽습니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.
- L143
- PIR 값을 VHE 호스트에 설치하여 기존 페이지 권한 설정을 이어받습니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.
- L145
- 권한 간접 참조 레지스터를 지원하지 않거나 그 이전을 마친 경우 합류하는 위치입니다. 레이블은 이 위치에 붙인 이름이며, 이름을 적는 것만으로 CPU 명령이 추가되지는 않습니다.
- L146
- TCR2를 지원하지 않는 CPU가 건너오는 위치입니다. 이 소스에서는 permission indirection 이전 부분도 함께 건너뛴 뒤 ISB부터 계속합니다. 레이블은 이 위치에 붙인 이름이며, 이름을 적는 것만으로 CPU 명령이 추가되지는 않습니다.
- L148
- 지금까지 옮긴 시스템 레지스터 설정을 뒤의 실행에 반영합니다.
- L151
- HVC 예외 복귀에 사용할 저장 PSTATE를 읽습니다. VHE의 레지스터 별칭 때문에 여기의 SPSR_EL1 접근은 호스트 EL2의 예외 복귀 상태를 다룹니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.
- L152
- 저장 PSTATE에서 실행 모드 비트만 지웁니다. 다른 상태 비트는 유지하면서 복귀할 EL과 스택 선택만 바꾸려는 작업입니다. AND는 두 피연산자의 같은 위치 비트가 모두 1인 곳만 결과에 남깁니다.
- L153
- EL2h 모드 값을 준비합니다. EL2에서 SP_EL2를 사용하는 호스트 실행 상태를 뜻합니다. MOV는 x1의 기존 값을 대체합니다. 원본이 주소값이어도 그 주소의 메모리를 읽거나 복사하지 않습니다.
- L154
- 기존 PSTATE의 나머지 비트에 EL2h 모드를 합칩니다. HVC 이전 코드로 돌아가되 실행 EL은 EL2로 바뀌게 됩니다. ORR은 어느 한쪽이 1인 비트를 결과에 세우므로 원래 1이던 비트도 유지됩니다.
- L155
- 수정한 예외 복귀 상태를 기록합니다. 마지막 ERET은 이 값에 따라 EL1 대신 EL2h에서 실행을 이어갑니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.
- L157
- MMU를 켤 수 있는 idmap의 enter_vhe로 넘어갑니다. 아직 EL2 MMU가 꺼진 상태이므로 가상 주소 변환이 바뀌어도 유효한 실행 위치가 필요합니다. 이 분기는 LR에 새 복귀 주소를 기록하지 않습니다.
- L158
- (__finalise_el2)의 심볼 타입과 크기를 기록합니다. 실행을 끝내는 RET를 생성하지 않습니다.
idmap에서 MMU 켜기와 잘못된 벡터
enter_vhe는 주소 변환 전환 중에도 실행이 이어지도록 .idmap.text에 있습니다. TLB를 정리하고 원래 EL1의 SCTLR 값을 host EL2에 적용한 뒤 원래 EL1 MMU는 끕니다. invalid_vector는 각 라벨로 되돌아가는 B를 만들며 정상 복구 경로가 아닙니다.
159
160 // At the point where we reach enter_vhe(), we run with
161 // the MMU off (which is enforced by __finalise_el2()).
162 // We thus need to be in the idmap, or everything will
163 // explode when enabling the MMU.
164
165 .pushsection .idmap.text, "ax"
166
167SYM_CODE_START_LOCAL(enter_vhe)
168 // Invalidate TLBs before enabling the MMU
169 tlbi vmalle1
170 dsb nsh
171 isb
172
173 // Enable the EL2 S1 MMU, as set up from EL1
174 mrs_s x0, SYS_SCTLR_EL12
175 set_sctlr_el1 x0
176
177 // Disable the EL1 S1 MMU for a good measure
178 mov_q x0, INIT_SCTLR_EL1_MMU_OFF
179 msr_s SYS_SCTLR_EL12, x0
180
181 mov x0, xzr
182
183 eret
184SYM_CODE_END(enter_vhe)
185
186 .popsection
187
188.macro invalid_vector label
189SYM_CODE_START_LOCAL(\label)
190 b \label
191SYM_CODE_END(\label)
192.endm
193
194 invalid_vector el2_sync_invalid
195 invalid_vector el2_irq_invalid
196 invalid_vector el2_fiq_invalid
197 invalid_vector el2_error_invalid
198 invalid_vector el1_sync_invalid
199 invalid_vector el1_irq_invalid
200 invalid_vector el1_fiq_invalid
201 invalid_vector el1_error_invalid
202
203 .popsection
204
- L165
- MMU 켜기 직전·직후에 같은 주소로 실행할 수 있도록 enter_vhe를 idmap 섹션에 둡니다. 이는 빌드 중 출력할 바이트의 섹션을 선택하는 지시문이며, 실행 중 PC나 SP를 옮기지 않습니다. 이전 출력 섹션을 함께 기억하므로 뒤의 .popsection으로 되돌릴 수 있습니다.
- L167
- EL1에서 넘겨받은 페이지 테이블과 설정으로 EL2 호스트 MMU를 실제로 켜는 마지막 전환 구간입니다. 이 매크로는 정렬된 코드 시작 심벌을 정의합니다. LOCAL 형식은 그 이름을 현재 오브젝트 안에서만 쓰도록 합니다.
- L169
- VHE 호스트가 사용할 주소 변환 체계의 기존 TLB 항목을 무효화합니다. 새로 옮긴 페이지 테이블 설정과 이전 캐시된 변환이 섞이지 않도록 합니다. 페이지 테이블 메모리를 수정해도 이미 저장된 TLB 변환이 자동으로 없어지는 것은 아니므로 별도로 무효화합니다.
- L170
- 요청한 TLB 무효화가 로컬에서 완료되기를 기다립니다.
- L171
- TLB·시스템 상태 변경 뒤 명령 실행을 동기화합니다.
- L174
- 기존 EL1에서 MMU를 켜고 사용하던 SCTLR 값을 EL12 별칭으로 읽습니다. 새로 임의의 설정을 만드는 대신 이전 커널 실행 환경을 이어받습니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.
- L175
- 읽은 SCTLR을 VHE 호스트 쪽에 적용하여 EL2의 Stage-1 MMU를 켭니다. 매크로는 SCTLR 쓰기 뒤 ISB와 로컬 명령 캐시 무효화·완료 대기까지 수행합니다.
- L178
- 이제 사용하지 않을 EL1 쪽에는 MMU-off SCTLR 초기값을 준비합니다. 호스트 EL2 쪽 설정과 기존 EL1 쪽 설정을 분리하여 마무리합니다. mov_q는 64비트 상수를 필요한 MOVZ/MOVN·MOVK 조합으로 구성하는 매크로입니다. 해당 주소의 메모리에서 데이터를 가져오는 명령은 아닙니다.
- L179
- EL12 별칭을 통해 기존 EL1의 SCTLR에 MMU-off 값을 기록합니다. 방금 켠 EL2 호스트 MMU를 끄는 줄이 아닙니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.
- L181
- 전환 성공을 나타내는 0을 반환 레지스터 x0에 넣습니다. MOV는 x0의 기존 값을 대체합니다. 원본이 주소값이어도 그 주소의 메모리를 읽거나 복사하지 않습니다.
- L183
- HVC 이후 코드로 예외 복귀합니다. 미리 SPSR의 모드를 바꿨으므로 커널은 EL2h/VHE 상태에서 같은 흐름을 이어갑니다.
- L184
- (enter_vhe)의 심볼 타입과 크기를 기록합니다. 실행을 끝내는 RET를 생성하지 않습니다.
- L186
- MMU를 켜는 idmap 구간의 배치를 끝내고 앞의 .hyp.text로 돌아갑니다. 뒤의 invalid 예외 처리 코드도 hyp 영역에 둡니다. 이는 빌드 중 출력할 바이트의 섹션을 선택하는 지시문이며, 실행 중 PC나 SP를 옮기지 않습니다.
- L188
- 이 최소 stub이 처리하지 않는 예외마다 자기 자신으로 분기하는 정지 지점을 만드는 매크로입니다. 알 수 없는 예외를 정상 처리한 것처럼 반환하지 않습니다. 이 줄은 어셈블러 매크로 정의의 시작입니다. 본문은 매크로를 사용하는 위치에 펼쳐지며, 정의 자체가 런타임 함수 호출은 아닙니다.
- L189
- 매크로에 전달한 예외 라벨 이름으로 파일 내부용 코드 심볼을 만듭니다. 여러 invalid_vector 사용이 각각 별도 정지 루틴을 생성하며 아래 자기 분기가 실제 동작입니다.
- L190
- 해당 예외 라벨로 계속 분기하여 진행을 멈춥니다. 장치 IRQ를 처리하거나 오류에서 복구하는 루틴은 아닙니다. 이 분기는 LR에 새 복귀 주소를 기록하지 않습니다.
- L191
- (\label)의 심볼 타입과 크기를 기록합니다. 실행을 끝내는 RET를 생성하지 않습니다.
- L192
- — 매크로 정의를 끝냅니다.
- L194
- 지원하지 않는 EL2 동기 예외가 들어왔을 때 사용할 자기 분기 루틴을 만듭니다. 위 벡터 슬롯에서 이 라벨로 연결된 경우 더 진행하지 않습니다.
- L195
- 지원하지 않는 EL2 IRQ 예외가 들어왔을 때 사용할 자기 분기 루틴을 만듭니다. 위 벡터 슬롯에서 이 라벨로 연결된 경우 더 진행하지 않습니다.
- L196
- 지원하지 않는 EL2 FIQ 예외가 들어왔을 때 사용할 자기 분기 루틴을 만듭니다. 위 벡터 슬롯에서 이 라벨로 연결된 경우 더 진행하지 않습니다.
- L197
- 지원하지 않는 EL2 SError 예외가 들어왔을 때 사용할 자기 분기 루틴을 만듭니다. 위 벡터 슬롯에서 이 라벨로 연결된 경우 더 진행하지 않습니다.
- L198
- 지원하지 않는 하위 EL 동기 예외가 들어왔을 때 사용할 자기 분기 루틴을 만듭니다. 위 벡터 슬롯에서 이 라벨로 연결된 경우 더 진행하지 않습니다.
- L199
- 지원하지 않는 하위 EL IRQ 예외가 들어왔을 때 사용할 자기 분기 루틴을 만듭니다. 위 벡터 슬롯에서 이 라벨로 연결된 경우 더 진행하지 않습니다.
- L200
- 지원하지 않는 하위 EL FIQ 예외가 들어왔을 때 사용할 자기 분기 루틴을 만듭니다. 위 벡터 슬롯에서 이 라벨로 연결된 경우 더 진행하지 않습니다.
- L201
- 지원하지 않는 하위 EL SError 예외가 들어왔을 때 사용할 자기 분기 루틴을 만듭니다. 위 벡터 슬롯에서 이 라벨로 연결된 경우 더 진행하지 않습니다.
- L203
- EL2에서 실행할 .hyp.text 배치를 끝내고 일반 .text로 돌아갑니다. 이후 __hyp_set_vectors 등의 래퍼는 커널 측에서 HVC를 요청하는 코드입니다. 이는 빌드 중 출력할 바이트의 섹션을 선택하는 지시문이며, 실행 중 PC나 SP를 옮기지 않습니다.
EL1에서 호출하는 래퍼
__hyp_set_vectors의 입력은 2 KiB 정렬된 새 벡터의 PA입니다. 모든 CPU에서 stub 사용이 확인된 상태를 전제로 합니다. finalise_el2는 w0의 부트 모드와 CurrentEL을 검사하며 조건이 맞지 않으면 아무 전환 없이 RET합니다.
205/*
206 * __hyp_set_vectors: Call this after boot to set the initial hypervisor
207 * vectors as part of hypervisor installation. On an SMP system, this should
208 * be called on each CPU.
209 *
210 * x0 must be the physical address of the new vector table, and must be
211 * 2KB aligned.
212 *
213 * Before calling this, you must check that the stub hypervisor is installed
214 * everywhere, by waiting for any secondary CPUs to be brought up and then
215 * checking that is_hyp_mode_available() is true.
216 *
217 * If not, there is a pre-existing hypervisor, some CPUs failed to boot, or
218 * something else went wrong... in such cases, trying to install a new
219 * hypervisor is unlikely to work as desired.
220 *
221 * When you call into your shiny new hypervisor, sp_el2 will contain junk,
222 * so you will need to set that to something sensible at the new hypervisor's
223 * initialisation entry point.
224 */
225
226SYM_FUNC_START(__hyp_set_vectors)
227 mov x1, x0
228 mov x0, #HVC_SET_VECTORS
229 hvc #0
230 ret
231SYM_FUNC_END(__hyp_set_vectors)
232
233SYM_FUNC_START(__hyp_reset_vectors)
234 mov x0, #HVC_RESET_VECTORS
235 hvc #0
236 ret
237SYM_FUNC_END(__hyp_reset_vectors)
238
239/*
240 * Entry point to finalise EL2 and switch to VHE if deemed capable
241 *
242 * w0: boot mode, as returned by init_kernel_el()
243 */
244SYM_FUNC_START(finalise_el2)
245 // Need to have booted at EL2
246 cmp w0, #BOOT_CPU_MODE_EL2
247 b.ne 1f
248
249 // and still be at EL1
250 mrs x0, CurrentEL
251 cmp x0, #CurrentEL_EL1
252 b.ne 1f
253
254 mov x0, #HVC_FINALISE_EL2
255 hvc #0
2561:
257 ret
258SYM_FUNC_END(finalise_el2)
- L226
- 새 EL2 벡터 주소를 x0로 받아 HVC 서비스에 전달하는 C 호출용 래퍼입니다. 모든 CPU에 stub이 설치되어 있는지 확인한 뒤 각 CPU에서 호출해야 합니다. 이 선언은 함수 심볼과 정렬을 정하고, ARM64 정의에서는 간접 호출이 도착할 BTI C 명령도 진입부에 넣습니다. 여기서는 전역 심벌로 정의하므로 다른 오브젝트에서도 이 함수 이름을 참조할 수 있습니다. 모듈용 EXPORT_SYMBOL 등록은 별도입니다.
- L227
- 호출자가 넘긴 새 벡터 주소를 서비스 인자 x1로 옮깁니다. x0는 다음 줄에서 요청 번호로 바뀝니다. MOV는 x1의 기존 값을 대체합니다. 원본이 주소값이어도 그 주소의 메모리를 읽거나 복사하지 않습니다.
- L228
- x0에 HVC_SET_VECTORS를 넣어 벡터 교체 작업을 선택합니다. MOV는 x0의 기존 값을 대체합니다. 원본이 주소값이어도 그 주소의 메모리를 읽거나 복사하지 않습니다.
- L229
- 현재 EL2 벡터의 처리기에 벡터 교체를 요청합니다. hyp stub은 x1의 주소를 VBAR_EL2에 기록하고 결과를 x0로 돌려줍니다. 일반 함수 호출처럼 BL로 이동하는 대신 동기 예외를 통해 요청을 전달합니다.
- L230
- EL2 요청 처리가 끝나면 원래 C 호출자로 돌아갑니다. 피연산자가 없는 RET는 LR(x30)에 들어 있는 주소에서 실행을 이어갑니다.
- L231
- (__hyp_set_vectors)의 심볼 타입과 크기를 기록합니다. 실행을 끝내는 RET를 생성하지 않습니다.
- L233
- EL2 벡터를 원래 hyp stub으로 돌려놓는 요청의 C 호출용 래퍼입니다. 현재 EL2에 설치된 처리기가 이 요청을 해석합니다. 이 선언은 함수 심볼과 정렬을 정하고, ARM64 정의에서는 간접 호출이 도착할 BTI C 명령도 진입부에 넣습니다. 여기서는 전역 심벌로 정의하므로 다른 오브젝트에서도 이 함수 이름을 참조할 수 있습니다. 모듈용 EXPORT_SYMBOL 등록은 별도입니다.
- L234
- HVC_RESET_VECTORS 번호를 x0에 준비합니다. 이미 stub이면 이 번호에 대해 성공만 반환하고, KVM 벡터라면 원래 stub으로 되돌리는 처리가 필요합니다. MOV는 x0의 기존 값을 대체합니다. 원본이 주소값이어도 그 주소의 메모리를 읽거나 복사하지 않습니다.
- L235
- EL2에 벡터 초기화 요청을 보냅니다. #0은 HVC 명령의 즉시값이며 실제 요청 종류는 x0에 들어 있습니다. 일반 함수 호출처럼 BL로 이동하는 대신 동기 예외를 통해 요청을 전달합니다.
- L236
- 벡터 초기화 요청이 끝나면 C 호출자로 돌아갑니다. 피연산자가 없는 RET는 LR(x30)에 들어 있는 주소에서 실행을 이어갑니다.
- L237
- (__hyp_reset_vectors)의 심볼 타입과 크기를 기록합니다. 실행을 끝내는 RET를 생성하지 않습니다.
- L244
- init_kernel_el이 돌려준 출발 EL을 w0로 받아 EL2 후속 설정을 요청하는 래퍼입니다. 출발한 EL과 현재 실행하는 EL은 서로 다를 수 있으므로 둘 다 검사합니다. 이 선언은 함수 심볼과 정렬을 정하고, ARM64 정의에서는 간접 호출이 도착할 BTI C 명령도 진입부에 넣습니다. 여기서는 전역 심벌로 정의하므로 다른 오브젝트에서도 이 함수 이름을 참조할 수 있습니다. 모듈용 EXPORT_SYMBOL 등록은 별도입니다.
- L246
- 출발 모드가 BOOT_CPU_MODE_EL2인 0xe12인지 비교합니다. w0만 비교하므로 x0의 상위 비트에 함께 전달된 출발 상태 플래그와 구분합니다. CMP는 두 피연산자의 뺄셈 결과로 NZCV 조건 플래그만 갱신하고 원래 레지스터 값은 유지합니다.
- L247
- EL2에서 출발하지 않았다면 EL2를 직접 소유한다고 가정할 수 없으므로 HVC 요청 없이 돌아갑니다. 앞서 계산한 NZCV 중 Z=0 조건으로 분기합니다. 이 줄은 두 값을 새로 비교하지 않으며 조건이 맞지 않으면 다음 명령으로 진행합니다.
- L250
- CurrentEL을 읽어 지금 실제로 실행 중인 예외 수준을 확인합니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.
- L251
- 현재 EL이 EL1인지 비교합니다. EL2 출발 CPU도 앞선 초기화에서 EL1로 내려와 있을 수 있습니다. CMP는 두 피연산자의 뺄셈 결과로 NZCV 조건 플래그만 갱신하고 원래 레지스터 값은 유지합니다.
- L252
- 현재 EL1이 아니면 전환 요청을 생략합니다. 이 경로는 EL1에서 HVC를 통해 EL2 설정을 마무리하도록 마련되어 있습니다. 앞서 계산한 NZCV 중 Z=0 조건으로 분기합니다. 이 줄은 두 값을 새로 비교하지 않으며 조건이 맞지 않으면 다음 명령으로 진행합니다.
- L254
- 조건을 모두 만족했으므로 HVC_FINALISE_EL2 번호를 준비합니다. MOV는 x0의 기존 값을 대체합니다. 원본이 주소값이어도 그 주소의 메모리를 읽거나 복사하지 않습니다.
- L255
- EL2 기능 설정과 가능한 VHE 전환을 요청합니다. VHE 허용 조건을 만족하면 같은 커널 코드가 EL2h로 이어지고, 만족하지 않으면 전환하지 않은 상태로 돌아옵니다. 일반 함수 호출처럼 BL로 이동하는 대신 동기 예외를 통해 요청을 전달합니다.
- L256
- EL2에서 출발하지 않았거나 현재 EL1이 아닌 경우, 또는 HVC 요청을 마친 경우가 합류하는 복귀 위치입니다. 레이블은 이 위치에 붙인 이름이며, 이름을 적는 것만으로 CPU 명령이 추가되지는 않습니다.
- L257
- 필요한 설정 요청을 마친 뒤 호출자에게 돌아갑니다. EL2 출발 여부만으로 무조건 VHE가 선택된다고 해석하면 안 됩니다. 피연산자가 없는 RET는 LR(x30)에 들어 있는 주소에서 실행을 이어갑니다.
- L258
- (finalise_el2)의 심볼 타입과 크기를 기록합니다. 실행을 끝내는 RET를 생성하지 않습니다.
매크로 정의와 확장
각 정의는 v6.18.37 원문입니다. 역슬래시 인자는 사용 위치의 값으로 치환되며, 안쪽 매크로와 조건부 블록은 이후 단계에서 다시 처리됩니다.
SYM_CODE_END
아래 정의의 인자 치환, 조건부 생성과 중첩 매크로를 통해 코드를 만듭니다. SYM 계열의 타입·범위·BTI 차이는 공통 표기 설명에 정리했습니다.
include/linux/linkage.h L305–L306
#define SYM_CODE_END(name) \
SYM_END(name, SYM_T_NONE)SYM_CODE_START
아래 정의의 인자 치환, 조건부 생성과 중첩 매크로를 통해 코드를 만듭니다. SYM 계열의 타입·범위·BTI 차이는 공통 표기 설명에 정리했습니다.
include/linux/linkage.h L278–L279
#define SYM_CODE_START(name) \
SYM_START(name, SYM_L_GLOBAL, SYM_A_ALIGN)SYM_CODE_START_LOCAL
아래 정의의 인자 치환, 조건부 생성과 중첩 매크로를 통해 코드를 만듭니다. SYM 계열의 타입·범위·BTI 차이는 공통 표기 설명에 정리했습니다.
include/linux/linkage.h L290–L291
#define SYM_CODE_START_LOCAL(name) \
SYM_START(name, SYM_L_LOCAL, SYM_A_ALIGN)SYM_FUNC_END
아래 정의의 인자 치환, 조건부 생성과 중첩 매크로를 통해 코드를 만듭니다. SYM 계열의 타입·범위·BTI 차이는 공통 표기 설명에 정리했습니다.
include/linux/linkage.h L248–L249
#define SYM_FUNC_END(name) \
SYM_END(name, SYM_T_FUNC)SYM_FUNC_START
아래 정의의 인자 치환, 조건부 생성과 중첩 매크로를 통해 코드를 만듭니다. SYM 계열의 타입·범위·BTI 차이는 공통 표기 설명에 정리했습니다.
arch/arm64/include/asm/linkage.h L18–L20
#define SYM_FUNC_START(name) \
SYM_START(name, SYM_L_GLOBAL, SYM_A_ALIGN) \
bti c ;include/linux/linkage.h L209–L210
#define SYM_FUNC_START(name) \
SYM_START(name, SYM_L_GLOBAL, SYM_A_ALIGN)check_override
ID 레지스터의 기능 필드와 override를 확인해 pass 또는 fail 라벨로 분기합니다. VHE 빌드 조건에 따른 정의도 함께 표시합니다.
arch/arm64/include/asm/el2_setup.h L485–L488
.macro check_override idreg, fld, pass, fail, tmp1, tmp2
mrs \tmp1, \idreg\()_el1
__check_override \idreg \fld 4 \pass \fail \tmp1 \tmp2
.endmarch/arm64/include/asm/el2_setup.h L498–L500
.macro check_override idreg, fld, pass, fail, tmp, ignore
__check_override \idreg \fld 4 \pass \fail \tmp \ignore
.endm인자 치환 예: check_override id_aa64mmfr1 ID_AA64MMFR1_EL1_VH_SHIFT 0f 1f x1 x2
1단계 치환 결과입니다. 내부 매크로·조건·고유 라벨 번호는 아직 펼치지 않았습니다.
mrs x1, id_aa64mmfr1_el1
__check_override id_aa64mmfr1 ID_AA64MMFR1_EL1_VH_SHIFT 4 0f 1f x1 x2finalise_el2_state
MPAM·GCS·SVE·SME 등 후반 EL2 설정을 기능 및 override에 따라 적용합니다.
arch/arm64/include/asm/el2_setup.h L503–L591
.macro finalise_el2_state
check_override id_aa64pfr0, ID_AA64PFR0_EL1_MPAM_SHIFT, .Linit_mpam_\@, .Lskip_mpam_\@, x1, x2
.Linit_mpam_\@:
msr_s SYS_MPAM2_EL2, xzr // use the default partition
// and disable lower traps
mrs_s x0, SYS_MPAMIDR_EL1
tbz x0, #MPAMIDR_EL1_HAS_HCR_SHIFT, .Lskip_mpam_\@ // skip if no MPAMHCR reg
msr_s SYS_MPAMHCR_EL2, xzr // clear TRAP_MPAMIDR_EL1 -> EL2
.Lskip_mpam_\@:
check_override id_aa64pfr1, ID_AA64PFR1_EL1_GCS_SHIFT, .Linit_gcs_\@, .Lskip_gcs_\@, x1, x2
.Linit_gcs_\@:
msr_s SYS_GCSCR_EL1, xzr
msr_s SYS_GCSCRE0_EL1, xzr
.Lskip_gcs_\@:
check_override id_aa64pfr0, ID_AA64PFR0_EL1_SVE_SHIFT, .Linit_sve_\@, .Lskip_sve_\@, x1, x2
.Linit_sve_\@: /* SVE register access */
__check_hvhe .Lcptr_nvhe_\@, x1
// (h)VHE case
mrs x0, cpacr_el1 // Disable SVE traps
orr x0, x0, #CPACR_EL1_ZEN
msr cpacr_el1, x0
b .Lskip_set_cptr_\@
.Lcptr_nvhe_\@: // nVHE case
mrs x0, cptr_el2 // Disable SVE traps
bic x0, x0, #CPTR_EL2_TZ
msr cptr_el2, x0
.Lskip_set_cptr_\@:
isb
mov x1, #ZCR_ELx_LEN_MASK // SVE: Enable full vector
msr_s SYS_ZCR_EL2, x1 // length for EL1.
.Lskip_sve_\@:
check_override id_aa64pfr1, ID_AA64PFR1_EL1_SME_SHIFT, .Linit_sme_\@, .Lskip_sme_\@, x1, x2
.Linit_sme_\@: /* SME register access and priority mapping */
__check_hvhe .Lcptr_nvhe_sme_\@, x1
// (h)VHE case
mrs x0, cpacr_el1 // Disable SME traps
orr x0, x0, #CPACR_EL1_SMEN
msr cpacr_el1, x0
b .Lskip_set_cptr_sme_\@
.Lcptr_nvhe_sme_\@: // nVHE case
mrs x0, cptr_el2 // Disable SME traps
bic x0, x0, #CPTR_EL2_TSM
msr cptr_el2, x0
.Lskip_set_cptr_sme_\@:
isb
mrs x1, sctlr_el2
orr x1, x1, #SCTLR_ELx_ENTP2 // Disable TPIDR2 traps
msr sctlr_el2, x1
isb
mov x0, #0 // SMCR controls
// Full FP in SM?
mrs_s x1, SYS_ID_AA64SMFR0_EL1
__check_override id_aa64smfr0, ID_AA64SMFR0_EL1_FA64_SHIFT, 1, .Linit_sme_fa64_\@, .Lskip_sme_fa64_\@, x1, x2
.Linit_sme_fa64_\@:
orr x0, x0, SMCR_ELx_FA64_MASK
.Lskip_sme_fa64_\@:
// ZT0 available?
mrs_s x1, SYS_ID_AA64SMFR0_EL1
__check_override id_aa64smfr0, ID_AA64SMFR0_EL1_SMEver_SHIFT, 4, .Linit_sme_zt0_\@, .Lskip_sme_zt0_\@, x1, x2
.Linit_sme_zt0_\@:
orr x0, x0, SMCR_ELx_EZT0_MASK
.Lskip_sme_zt0_\@:
orr x0, x0, #SMCR_ELx_LEN_MASK // Enable full SME vector
msr_s SYS_SMCR_EL2, x0 // length for EL1.
mrs_s x1, SYS_SMIDR_EL1 // Priority mapping supported?
ubfx x1, x1, #SMIDR_EL1_SMPS_SHIFT, #1
cbz x1, .Lskip_sme_\@
msr_s SYS_SMPRIMAP_EL2, xzr // Make all priorities equal
.Lskip_sme_\@:
.endm인자 치환 예: finalise_el2_state
1단계 치환 결과입니다. 내부 매크로·조건·고유 라벨 번호는 아직 펼치지 않았습니다.
check_override id_aa64pfr0, ID_AA64PFR0_EL1_MPAM_SHIFT, .Linit_mpam_\@, .Lskip_mpam_\@, x1, x2
.Linit_mpam_\@:
msr_s SYS_MPAM2_EL2, xzr // use the default partition
// and disable lower traps
mrs_s x0, SYS_MPAMIDR_EL1
tbz x0, #MPAMIDR_EL1_HAS_HCR_SHIFT, .Lskip_mpam_\@ // skip if no MPAMHCR reg
msr_s SYS_MPAMHCR_EL2, xzr // clear TRAP_MPAMIDR_EL1 -> EL2
.Lskip_mpam_\@:
check_override id_aa64pfr1, ID_AA64PFR1_EL1_GCS_SHIFT, .Linit_gcs_\@, .Lskip_gcs_\@, x1, x2
.Linit_gcs_\@:
msr_s SYS_GCSCR_EL1, xzr
msr_s SYS_GCSCRE0_EL1, xzr
.Lskip_gcs_\@:
check_override id_aa64pfr0, ID_AA64PFR0_EL1_SVE_SHIFT, .Linit_sve_\@, .Lskip_sve_\@, x1, x2
.Linit_sve_\@: /* SVE register access */
__check_hvhe .Lcptr_nvhe_\@, x1
// (h)VHE case
mrs x0, cpacr_el1 // Disable SVE traps
orr x0, x0, #CPACR_EL1_ZEN
msr cpacr_el1, x0
b .Lskip_set_cptr_\@
.Lcptr_nvhe_\@: // nVHE case
mrs x0, cptr_el2 // Disable SVE traps
bic x0, x0, #CPTR_EL2_TZ
msr cptr_el2, x0
.Lskip_set_cptr_\@:
isb
mov x1, #ZCR_ELx_LEN_MASK // SVE: Enable full vector
msr_s SYS_ZCR_EL2, x1 // length for EL1.
.Lskip_sve_\@:
check_override id_aa64pfr1, ID_AA64PFR1_EL1_SME_SHIFT, .Linit_sme_\@, .Lskip_sme_\@, x1, x2
.Linit_sme_\@: /* SME register access and priority mapping */
__check_hvhe .Lcptr_nvhe_sme_\@, x1
// (h)VHE case
mrs x0, cpacr_el1 // Disable SME traps
orr x0, x0, #CPACR_EL1_SMEN
msr cpacr_el1, x0
b .Lskip_set_cptr_sme_\@
.Lcptr_nvhe_sme_\@: // nVHE case
mrs x0, cptr_el2 // Disable SME traps
bic x0, x0, #CPTR_EL2_TSM
msr cptr_el2, x0
.Lskip_set_cptr_sme_\@:
isb
mrs x1, sctlr_el2
orr x1, x1, #SCTLR_ELx_ENTP2 // Disable TPIDR2 traps
msr sctlr_el2, x1
isb
mov x0, #0 // SMCR controls
// Full FP in SM?
mrs_s x1, SYS_ID_AA64SMFR0_EL1
__check_override id_aa64smfr0, ID_AA64SMFR0_EL1_FA64_SHIFT, 1, .Linit_sme_fa64_\@, .Lskip_sme_fa64_\@, x1, x2
.Linit_sme_fa64_\@:
orr x0, x0, SMCR_ELx_FA64_MASK
.Lskip_sme_fa64_\@:
// ZT0 available?
mrs_s x1, SYS_ID_AA64SMFR0_EL1
__check_override id_aa64smfr0, ID_AA64SMFR0_EL1_SMEver_SHIFT, 4, .Linit_sme_zt0_\@, .Lskip_sme_zt0_\@, x1, x2
.Linit_sme_zt0_\@:
orr x0, x0, SMCR_ELx_EZT0_MASK
.Lskip_sme_zt0_\@:
orr x0, x0, #SMCR_ELx_LEN_MASK // Enable full SME vector
msr_s SYS_SMCR_EL2, x0 // length for EL1.
mrs_s x1, SYS_SMIDR_EL1 // Priority mapping supported?
ubfx x1, x1, #SMIDR_EL1_SMPS_SHIFT, #1
cbz x1, .Lskip_sme_\@
msr_s SYS_SMPRIMAP_EL2, xzr // Make all priorities equal
.Lskip_sme_\@:invalid_vector
지정 라벨과 자기 자신으로 돌아오는 B 명령을 생성합니다.
arch/arm64/kernel/hyp-stub.S L188–L192
.macro invalid_vector label
SYM_CODE_START_LOCAL(\label)
b \label
SYM_CODE_END(\label)
.endm인자 치환 예: invalid_vector el2_sync_invalid
1단계 치환 결과입니다. 내부 매크로·조건·고유 라벨 번호는 아직 펼치지 않았습니다.
SYM_CODE_START_LOCAL(el2_sync_invalid)
b el2_sync_invalid
SYM_CODE_END(el2_sync_invalid)mrs_s
SYS_* 수치 인코딩으로 MRS 명령을 생성합니다. 첫 인자는 값을 받을 일반 레지스터입니다.
arch/arm64/include/asm/sysreg.h L1136–L1138
.macro mrs_s, rt, sreg
__emit_inst(0xd5200000|(\sreg)|(.L__gpr_num_\rt))
.endm인자 치환 예: mrs_s x0, SYS_CPACR_EL12
1단계 치환 결과입니다. 내부 매크로·조건·고유 라벨 번호는 아직 펼치지 않았습니다.
__emit_inst(0xd5200000|(SYS_CPACR_EL12)|(.L__gpr_num_x0))msr_hcr_el2
HCR_EL2를 쓰며 해당 CPU 우회가 필요한 구성에서는 주변 동기화 명령을 추가합니다.
arch/arm64/include/asm/sysreg.h L1144–L1152
.macro msr_hcr_el2, reg
#if IS_ENABLED(CONFIG_AMPERE_ERRATUM_AC04_CPU_23)
dsb nsh
msr hcr_el2, \reg
isb
#else
msr hcr_el2, \reg
#endif
.endm인자 치환 예: msr_hcr_el2 x0
1단계 치환 결과입니다. 내부 매크로·조건·고유 라벨 번호는 아직 펼치지 않았습니다.
#if IS_ENABLED(CONFIG_AMPERE_ERRATUM_AC04_CPU_23)
dsb nsh
msr hcr_el2, x0
isb
#else
msr hcr_el2, x0
#endifmsr_s
SYS_* 수치 인코딩으로 MSR 명령을 생성합니다. 두 번째 인자의 일반 레지스터 값을 첫 번째 시스템 레지스터에 씁니다.
arch/arm64/include/asm/sysreg.h L1140–L1142
.macro msr_s, sreg, rt
__emit_inst(0xd5000000|(\sreg)|(.L__gpr_num_\rt))
.endm인자 치환 예: msr_s SYS_SCTLR_EL12, x0
1단계 치환 결과입니다. 내부 매크로·조건·고유 라벨 번호는 아직 펼치지 않았습니다.
__emit_inst(0xd5000000|(SYS_SCTLR_EL12)|(.L__gpr_num_x0))ventry
분기 명령을 128바이트 정렬된 벡터 슬롯에 배치합니다.
arch/arm64/include/asm/assembler.h L131–L134
.macro ventry label
.align 7
b \label
.endm인자 치환 예: ventry el2_sync_invalid
1단계 치환 결과입니다. 내부 매크로·조건·고유 라벨 번호는 아직 펼치지 않았습니다.
.align 7
b el2_sync_invalid상수·구조체 오프셋
S_*·THREAD_*·FREGS_* 등의 구조체 오프셋은 빌드한 C 구조체 배치에서 생성됩니다. 숫자를 고정하지 않고 원래 기호를 사용했습니다.
BOOT_CPU_MODE_EL2: arch/arm64/include/asm/virt.h:50HVC_FINALISE_EL2: arch/arm64/include/asm/virt.h:41HVC_RESET_VECTORS: arch/arm64/include/asm/virt.h:36HVC_SET_VECTORS: arch/arm64/include/asm/virt.h:26HVC_SOFT_RESTART: arch/arm64/include/asm/virt.h:31HVC_STUB_ERR: arch/arm64/include/asm/virt.h:47INIT_SCTLR_EL1_MMU_OFF: arch/arm64/include/asm/sysreg.h:867SYS_MAIR_EL12: arch/arm64/include/asm/sysreg.h:616SYS_TTBR0_EL12: arch/arm64/include/asm/sysreg.h:607SYS_TTBR1_EL12: arch/arm64/include/asm/sysreg.h:608SYS_VBAR_EL12: arch/arm64/include/asm/sysreg.h:618