이 파일은 왜 필요한가요?
EL1 커널이 EL2의 제어 레지스터를 직접 모두 바꿀 수는 없습니다. 초기 hyp-stub는 HVC 요청을 받아 EL2에서 필요한 작업을 해 주는 작은 처리 코드입니다. 요청 번호를 검사하고, 가능한 작업이면 수행한 뒤 예외 복귀합니다. VHE 전환은 레벨 이름만 바꾸는 일이 아니라 사용할 스택과 주소 변환 상태도 옮기는 과정입니다.
명령·지시문·라벨을 원본 줄 번호에 맞춰 설명합니다. 주석과 빈 줄을 포함한 전체 원문은 아래에 따로 두었습니다. 매크로 정의와 조건부 코드는 모두 차례대로 실행되는 명령 목록이 아닙니다.
기존 코드 분석과 함께 읽기 · 원본 파일 · 설명 원고
먼저 알아둘 개념: 예외 레벨과 복귀, 호출 주소와 복귀 주소, 가상 주소와 MMU, 실행 상태의 저장과 복원, 비교 결과와 조건 분기
처음 읽을 때
HVC를 BL처럼 생각하기보다 더 높은 예외 레벨에 요청을 보내는 통로로 읽어 보세요. 요청 번호와 인자는 여전히 레지스터에 들어 있습니다.
더 깊이 살펴볼 때
HCR.E2H 설정 이후에는 EL1 이름의 일부 레지스터 접근 대상이 달라집니다. 왜 원래 EL1 상태를 EL12 이름으로 읽는지, VHE 진입을 idmap에서 하는 이유를 연결해 설명해 보세요.
한 줄씩 레지스터와 메모리 상태 보기
각 코드 아래의 ‘행 그림’을 펼치면, 그 줄에서 읽는 값과 실행 전·후의 상태를 나란히 볼 수 있습니다. 여기서 ‘명령 직전’은 지금 보고 있는 명령이 실행되기 바로 전을 뜻합니다. 함수 진입 때나 소스의 바로 윗줄을 가리키는 말은 아닙니다. 앞서 실행한 명령이 값을 바꿨다면 그 결과가 들어 있습니다.
소스로 정해지는 상수, 호출자가 넘기는 값, 실제 실행에서 정해지는 주소를 구분해서 읽어야 합니다. 주소 숫자를 몰라도 무엇을 가리키는지와 어디서 받아 오는지는 설명할 수 있습니다. ‘명령 직전 x0의 값’처럼 일반 표기로 남은 곳은 한 명령의 동작을 나타내며, 특정 호출 경로의 값까지 확인했다는 뜻은 아닙니다.
일반 명령은 그 한 줄의 효과를, 매크로는 여러 명령을 묶은 주요 결과를 표시합니다. 함수 호출은 호출 직후의 PC와 LR까지만 나타냅니다. 메모리 접근은 정상적으로 끝나는 경우를 가정합니다. 생략된 항목까지 모두 보존된다는 뜻은 아닙니다.
화살표 →는 같은 항목의 전후 비교입니다. 메모리 칸에는 주소와 크기를 따로 적었습니다. SP가 줄어들면 낮은 주소 쪽을 사용하며, 화면의 위아래와 메모리 주소 방향은 별개입니다. 전처리·어셈블·링크 지시문은 CPU 실행과 구분합니다.
‘이전·다음 행’은 소스에 적힌 순서입니다. 분기·반복·호출이 있으므로 파일 위에서 아래로 실제 실행된다고 가정하지 않습니다. 값이 여러 줄에 걸쳐 이어지는 모습은 cpu-reset.S의 단계별 예제에서 보실 수 있습니다.
총 160개 줄에 그림을 붙였습니다. 기존 코드와 설명은 그 아래에서 이어집니다.
먼저 그림으로 살펴보기
EL2 예외 벡터
#include <linux/init.h>초기화 코드·데이터의 섹션을 지정하는 커널 공통 정의를 포함합니다. 이 파일의 실제 EL2 stub 코드 위치는 아래 .hyp.text와 .idmap.text 지시문으로 지정합니다. #include는 전처리 단계에서 헤더 내용을 가져옵니다. CPU가 이 줄에서 함수를 호출하는 동작은 없습니다.
9행 그림 · 전처리 단계
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
#include <linux/linkage.h>EL2 벡터·특수 코드와 C 호출용 래퍼의 심볼을 선언할 매크로를 가져옵니다. 특수 예외 진입점은 일반 함수와 구분하여 CODE 심볼로 표시합니다. #include는 전처리 단계에서 헤더 내용을 가져옵니다. CPU가 이 줄에서 함수를 호출하는 동작은 없습니다.
10행 그림 · 전처리 단계
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
#include <asm/assembler.h>128바이트 벡터 슬롯을 만드는 ventry, 시스템 레지스터 접근, SCTLR 설정 같은 ARM64 공통 어셈블리 도구를 가져옵니다. #include는 전처리 단계에서 헤더 내용을 가져옵니다. CPU가 이 줄에서 함수를 호출하는 동작은 없습니다.
12행 그림 · 전처리 단계
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
#include <asm/el2_setup.h>EL2 기능의 최종 설정과 CPU 기능 override 검사 매크로를 가져옵니다. finalise_el2_state와 check_override의 실제 구현이 이 헤더에 있습니다. #include는 전처리 단계에서 헤더 내용을 가져옵니다. CPU가 이 줄에서 함수를 호출하는 동작은 없습니다.
13행 그림 · 전처리 단계
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
#include <asm/kvm_arm.h>HCR·MDCR 등의 가상화 제어 비트 정의를 가져옵니다. 커널을 VHE 호스트로 옮길 때 HCR_HOST_VHE_FLAGS와 추적 버퍼 제어 비트를 사용합니다. #include는 전처리 단계에서 헤더 내용을 가져옵니다. CPU가 이 줄에서 함수를 호출하는 동작은 없습니다.
14행 그림 · 전처리 단계
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
#include <asm/kvm_asm.h>KVM과 저수준 EL2 코드가 공유하는 어셈블리용 정의를 포함합니다. 현재 파일의 stub이 처리하는 요청 종류 자체는 asm/virt.h의 HVC_* 정의에 따릅니다. #include는 전처리 단계에서 헤더 내용을 가져옵니다. CPU가 이 줄에서 함수를 호출하는 동작은 없습니다.
15행 그림 · 전처리 단계
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
#include <asm/ptrace.h>PSTATE의 실행 모드 비트와 EL2h 값을 가져옵니다. 예외 복귀 시 커널이 EL1이 아닌 EL2h에서 이어지도록 SPSR의 모드를 바꿀 때 사용합니다. #include는 전처리 단계에서 헤더 내용을 가져옵니다. CPU가 이 줄에서 함수를 호출하는 동작은 없습니다.
16행 그림 · 전처리 단계
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
#include <asm/virt.h>HVC 요청 번호와 오류값, EL1·EL2 출발 모드값을 가져옵니다. 요청 번호는 x0로 전달되며 CurrentEL과 출발 모드를 구별하는 기준도 여기에서 정의합니다. #include는 전처리 단계에서 헤더 내용을 가져옵니다. CPU가 이 줄에서 함수를 호출하는 동작은 없습니다.
17행 그림 · 전처리 단계
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
.text기본 커널 실행 코드 섹션을 선택합니다. 실제 EL2 벡터·처리기는 다음 .hyp.text에 배치하고 C 호출용 래퍼에서 이 .text로 돌아옵니다. 이는 빌드 중 출력할 바이트의 섹션을 선택하는 지시문이며, 실행 중 PC나 SP를 옮기지 않습니다.
19행 그림 · 어셈블·링크 단계
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
.pushsection .hyp.text, "ax"아직 본격적인 하이퍼바이저가 설치되기 전 EL2에서 사용할 최소 처리 코드를 .hyp.text에 둡니다. 벡터 교체, CPU 재시작, EL2 설정 마무리 같은 제한된 요청을 처리합니다. 이는 빌드 중 출력할 바이트의 섹션을 선택하는 지시문이며, 실행 중 PC나 SP를 옮기지 않습니다. 이전 출력 섹션을 함께 기억하므로 뒤의 .popsection으로 되돌릴 수 있습니다.
20행 그림 · 어셈블·링크 단계
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
.align 11EL2 벡터 테이블의 시작을 2KiB 경계에 맞춥니다. VBAR_EL2가 가리키는 테이블에 요구되는 정렬이며, 각 예외 종류는 정해진 오프셋의 슬롯으로 들어옵니다. AArch64의 .align 인자는 바이트 수가 아니라 2의 지수입니다. 빌드할 때 필요한 패딩을 넣어 그 경계를 맞춥니다.
22행 그림 · 어셈블·링크 단계
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
SYM_CODE_START(__hyp_stub_vectors)EL2 예외의 시작 위치들을 모은 벡터 테이블입니다. 현재 EL의 스택 선택과 하위 EL의 실행 상태에 따라 16개 슬롯을 배치하며, 이 stub이 지원하는 동기 예외만 elx_sync로 연결합니다. 이 매크로는 정렬된 코드 시작 심벌을 정의하고 다른 오브젝트에서도 참조할 수 있게 합니다.
24행 그림 · 어셈블·링크 단계
함수·코드 범위: __hyp_stub_vectors
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
ventry el2_sync_invalid // Synchronous EL2t현재 EL2의 SP_EL0 선택(EL2t)에서 들어온 동기 예외의 128바이트 벡터 슬롯입니다. 이 최소 stub의 지원 범위 밖이므로 해당 invalid 처리기로 연결하여 진행을 멈춥니다.
25행 그림 · 벡터·핸들러 코드 생성
함수·코드 범위: __hyp_stub_vectors
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 이 한 줄은 여러 명령을 생성합니다. 레지스터 변화는 펼쳐진 본문의 각 명령과 선택된 진입 경로에 따라 생깁니다.
ventry 매크로 본문 · 131행
arch/arm64/include/asm/assembler.h · Linux v6.18.37
정의 선택 조건: #ifndef __ASM_ASSEMBLER_H
.macro ventry label
.align 7
b \label
.endm ventry el2_irq_invalid // IRQ EL2t현재 EL2t에서 들어온 IRQ 예외의 128바이트 벡터 슬롯입니다. 이 최소 stub의 지원 범위 밖이므로 해당 invalid 처리기로 연결하여 진행을 멈춥니다.
26행 그림 · 벡터·핸들러 코드 생성
함수·코드 범위: __hyp_stub_vectors
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 이 한 줄은 여러 명령을 생성합니다. 레지스터 변화는 펼쳐진 본문의 각 명령과 선택된 진입 경로에 따라 생깁니다.
ventry 매크로 본문 · 131행
arch/arm64/include/asm/assembler.h · Linux v6.18.37
정의 선택 조건: #ifndef __ASM_ASSEMBLER_H
.macro ventry label
.align 7
b \label
.endm ventry el2_fiq_invalid // FIQ EL2t현재 EL2t에서 들어온 FIQ 예외의 128바이트 벡터 슬롯입니다. 이 최소 stub의 지원 범위 밖이므로 해당 invalid 처리기로 연결하여 진행을 멈춥니다.
27행 그림 · 벡터·핸들러 코드 생성
함수·코드 범위: __hyp_stub_vectors
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 이 한 줄은 여러 명령을 생성합니다. 레지스터 변화는 펼쳐진 본문의 각 명령과 선택된 진입 경로에 따라 생깁니다.
ventry 매크로 본문 · 131행
arch/arm64/include/asm/assembler.h · Linux v6.18.37
정의 선택 조건: #ifndef __ASM_ASSEMBLER_H
.macro ventry label
.align 7
b \label
.endm ventry el2_error_invalid // Error EL2t현재 EL2t에서 들어온 SError 예외의 128바이트 벡터 슬롯입니다. 이 최소 stub의 지원 범위 밖이므로 해당 invalid 처리기로 연결하여 진행을 멈춥니다.
28행 그림 · 벡터·핸들러 코드 생성
함수·코드 범위: __hyp_stub_vectors
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 이 한 줄은 여러 명령을 생성합니다. 레지스터 변화는 펼쳐진 본문의 각 명령과 선택된 진입 경로에 따라 생깁니다.
ventry 매크로 본문 · 131행
arch/arm64/include/asm/assembler.h · Linux v6.18.37
정의 선택 조건: #ifndef __ASM_ASSEMBLER_H
.macro ventry label
.align 7
b \label
.endm ventry elx_sync // Synchronous EL2h현재 EL2의 SP_EL2 선택(EL2h)에서 들어온 동기 예외의 128바이트 벡터 슬롯입니다. elx_sync로 연결하여 x0에 지정한 stub 서비스를 해석합니다.
30행 그림 · 벡터·핸들러 코드 생성
함수·코드 범위: __hyp_stub_vectors
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 이 한 줄은 여러 명령을 생성합니다. 레지스터 변화는 펼쳐진 본문의 각 명령과 선택된 진입 경로에 따라 생깁니다.
ventry 매크로 본문 · 131행
arch/arm64/include/asm/assembler.h · Linux v6.18.37
정의 선택 조건: #ifndef __ASM_ASSEMBLER_H
.macro ventry label
.align 7
b \label
.endm ventry el2_irq_invalid // IRQ EL2h현재 EL2h에서 들어온 IRQ 예외의 128바이트 벡터 슬롯입니다. 이 최소 stub의 지원 범위 밖이므로 해당 invalid 처리기로 연결하여 진행을 멈춥니다.
31행 그림 · 벡터·핸들러 코드 생성
함수·코드 범위: __hyp_stub_vectors
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 이 한 줄은 여러 명령을 생성합니다. 레지스터 변화는 펼쳐진 본문의 각 명령과 선택된 진입 경로에 따라 생깁니다.
ventry 매크로 본문 · 131행
arch/arm64/include/asm/assembler.h · Linux v6.18.37
정의 선택 조건: #ifndef __ASM_ASSEMBLER_H
.macro ventry label
.align 7
b \label
.endm ventry el2_fiq_invalid // FIQ EL2h현재 EL2h에서 들어온 FIQ 예외의 128바이트 벡터 슬롯입니다. 이 최소 stub의 지원 범위 밖이므로 해당 invalid 처리기로 연결하여 진행을 멈춥니다.
32행 그림 · 벡터·핸들러 코드 생성
함수·코드 범위: __hyp_stub_vectors
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 이 한 줄은 여러 명령을 생성합니다. 레지스터 변화는 펼쳐진 본문의 각 명령과 선택된 진입 경로에 따라 생깁니다.
ventry 매크로 본문 · 131행
arch/arm64/include/asm/assembler.h · Linux v6.18.37
정의 선택 조건: #ifndef __ASM_ASSEMBLER_H
.macro ventry label
.align 7
b \label
.endm ventry el2_error_invalid // Error EL2h현재 EL2h에서 들어온 SError 예외의 128바이트 벡터 슬롯입니다. 이 최소 stub의 지원 범위 밖이므로 해당 invalid 처리기로 연결하여 진행을 멈춥니다.
33행 그림 · 벡터·핸들러 코드 생성
함수·코드 범위: __hyp_stub_vectors
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 이 한 줄은 여러 명령을 생성합니다. 레지스터 변화는 펼쳐진 본문의 각 명령과 선택된 진입 경로에 따라 생깁니다.
ventry 매크로 본문 · 131행
arch/arm64/include/asm/assembler.h · Linux v6.18.37
정의 선택 조건: #ifndef __ASM_ASSEMBLER_H
.macro ventry label
.align 7
b \label
.endm ventry elx_sync // Synchronous 64-bit EL1하위 AArch64 EL에서 들어온 동기 예외의 128바이트 벡터 슬롯입니다. elx_sync로 연결하여 x0에 지정한 stub 서비스를 해석합니다.
35행 그림 · 벡터·핸들러 코드 생성
함수·코드 범위: __hyp_stub_vectors
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 이 한 줄은 여러 명령을 생성합니다. 레지스터 변화는 펼쳐진 본문의 각 명령과 선택된 진입 경로에 따라 생깁니다.
ventry 매크로 본문 · 131행
arch/arm64/include/asm/assembler.h · Linux v6.18.37
정의 선택 조건: #ifndef __ASM_ASSEMBLER_H
.macro ventry label
.align 7
b \label
.endm ventry el1_irq_invalid // IRQ 64-bit EL1하위 AArch64 EL에서 들어온 IRQ 예외의 128바이트 벡터 슬롯입니다. 이 최소 stub의 지원 범위 밖이므로 해당 invalid 처리기로 연결하여 진행을 멈춥니다.
36행 그림 · 벡터·핸들러 코드 생성
함수·코드 범위: __hyp_stub_vectors
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 이 한 줄은 여러 명령을 생성합니다. 레지스터 변화는 펼쳐진 본문의 각 명령과 선택된 진입 경로에 따라 생깁니다.
ventry 매크로 본문 · 131행
arch/arm64/include/asm/assembler.h · Linux v6.18.37
정의 선택 조건: #ifndef __ASM_ASSEMBLER_H
.macro ventry label
.align 7
b \label
.endm ventry el1_fiq_invalid // FIQ 64-bit EL1하위 AArch64 EL에서 들어온 FIQ 예외의 128바이트 벡터 슬롯입니다. 이 최소 stub의 지원 범위 밖이므로 해당 invalid 처리기로 연결하여 진행을 멈춥니다.
37행 그림 · 벡터·핸들러 코드 생성
함수·코드 범위: __hyp_stub_vectors
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 이 한 줄은 여러 명령을 생성합니다. 레지스터 변화는 펼쳐진 본문의 각 명령과 선택된 진입 경로에 따라 생깁니다.
ventry 매크로 본문 · 131행
arch/arm64/include/asm/assembler.h · Linux v6.18.37
정의 선택 조건: #ifndef __ASM_ASSEMBLER_H
.macro ventry label
.align 7
b \label
.endm ventry el1_error_invalid // Error 64-bit EL1하위 AArch64 EL에서 들어온 SError 예외의 128바이트 벡터 슬롯입니다. 이 최소 stub의 지원 범위 밖이므로 해당 invalid 처리기로 연결하여 진행을 멈춥니다.
38행 그림 · 벡터·핸들러 코드 생성
함수·코드 범위: __hyp_stub_vectors
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 이 한 줄은 여러 명령을 생성합니다. 레지스터 변화는 펼쳐진 본문의 각 명령과 선택된 진입 경로에 따라 생깁니다.
ventry 매크로 본문 · 131행
arch/arm64/include/asm/assembler.h · Linux v6.18.37
정의 선택 조건: #ifndef __ASM_ASSEMBLER_H
.macro ventry label
.align 7
b \label
.endm ventry el1_sync_invalid // Synchronous 32-bit EL1하위 AArch32 EL에서 들어온 동기 예외의 128바이트 벡터 슬롯입니다. 이 최소 stub의 지원 범위 밖이므로 해당 invalid 처리기로 연결하여 진행을 멈춥니다.
40행 그림 · 벡터·핸들러 코드 생성
함수·코드 범위: __hyp_stub_vectors
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 이 한 줄은 여러 명령을 생성합니다. 레지스터 변화는 펼쳐진 본문의 각 명령과 선택된 진입 경로에 따라 생깁니다.
ventry 매크로 본문 · 131행
arch/arm64/include/asm/assembler.h · Linux v6.18.37
정의 선택 조건: #ifndef __ASM_ASSEMBLER_H
.macro ventry label
.align 7
b \label
.endm ventry el1_irq_invalid // IRQ 32-bit EL1하위 AArch32 EL에서 들어온 IRQ 예외의 128바이트 벡터 슬롯입니다. 이 최소 stub의 지원 범위 밖이므로 해당 invalid 처리기로 연결하여 진행을 멈춥니다.
41행 그림 · 벡터·핸들러 코드 생성
함수·코드 범위: __hyp_stub_vectors
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 이 한 줄은 여러 명령을 생성합니다. 레지스터 변화는 펼쳐진 본문의 각 명령과 선택된 진입 경로에 따라 생깁니다.
ventry 매크로 본문 · 131행
arch/arm64/include/asm/assembler.h · Linux v6.18.37
정의 선택 조건: #ifndef __ASM_ASSEMBLER_H
.macro ventry label
.align 7
b \label
.endm ventry el1_fiq_invalid // FIQ 32-bit EL1하위 AArch32 EL에서 들어온 FIQ 예외의 128바이트 벡터 슬롯입니다. 이 최소 stub의 지원 범위 밖이므로 해당 invalid 처리기로 연결하여 진행을 멈춥니다.
42행 그림 · 벡터·핸들러 코드 생성
함수·코드 범위: __hyp_stub_vectors
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 이 한 줄은 여러 명령을 생성합니다. 레지스터 변화는 펼쳐진 본문의 각 명령과 선택된 진입 경로에 따라 생깁니다.
ventry 매크로 본문 · 131행
arch/arm64/include/asm/assembler.h · Linux v6.18.37
정의 선택 조건: #ifndef __ASM_ASSEMBLER_H
.macro ventry label
.align 7
b \label
.endm ventry el1_error_invalid // Error 32-bit EL1하위 AArch32 EL에서 들어온 SError 예외의 128바이트 벡터 슬롯입니다. 이 최소 stub의 지원 범위 밖이므로 해당 invalid 처리기로 연결하여 진행을 멈춥니다.
43행 그림 · 벡터·핸들러 코드 생성
함수·코드 범위: __hyp_stub_vectors
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 이 한 줄은 여러 명령을 생성합니다. 레지스터 변화는 펼쳐진 본문의 각 명령과 선택된 진입 경로에 따라 생깁니다.
ventry 매크로 본문 · 131행
arch/arm64/include/asm/assembler.h · Linux v6.18.37
정의 선택 조건: #ifndef __ASM_ASSEMBLER_H
.macro ventry label
.align 7
b \label
.endmSYM_CODE_END(__hyp_stub_vectors)도구에 이 코드 범위의 끝을 알려 크기와 심볼 정보를 기록합니다. CPU가 이 줄 때문에 자동으로 반환하지는 않습니다.
44행 그림 · 어셈블·링크 단계
함수·코드 범위: __hyp_stub_vectors
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 끝 표기 자체가 RET나 분기를 실행하지는 않습니다.
.align 11벡터 슬롯 배치가 끝난 뒤 다음 처리 코드를 2KiB 경계에 맞춥니다. 마지막 벡터 슬롯 뒤로 처리 코드가 섞이지 않도록 경계를 확정합니다. AArch64의 .align 인자는 바이트 수가 아니라 2의 지수입니다. 빌드할 때 필요한 패딩을 넣어 그 경계를 맞춥니다.
46행 그림 · 어셈블·링크 단계
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
HVC 번호별 분기
SYM_CODE_START_LOCAL(elx_sync)지원하는 동기 예외에서 x0의 hypercall 번호를 해석하는 처리기입니다. 이 stub은 일반적인 EL2 예외를 모두 복구하는 운영체제 예외 처리기가 아닙니다. 이 매크로는 정렬된 코드 시작 심벌을 정의합니다. LOCAL 형식은 그 이름을 현재 오브젝트 안에서만 쓰도록 합니다.
48행 그림 · 어셈블·링크 단계
함수·코드 범위: elx_sync
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
cmp x0, #HVC_SET_VECTORSx0가 HVC_SET_VECTORS인지 검사합니다. 이 요청은 x1에 받은 새 벡터 테이블의 물리 주소를 VBAR_EL2에 설치하는 작업입니다. CMP는 두 피연산자의 뺄셈 결과로 NZCV 조건 플래그만 갱신하고 원래 레지스터 값은 유지합니다.
49행 그림 · 조건 플래그 계산
함수·코드 범위: elx_sync
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 일반 레지스터에 연산 결과를 쓰지 않고 조건 플래그만 바꿉니다.
- N은 결과의 부호 비트, Z는 결과가 0인지 나타냅니다. C는 빌림이 없으면 1, V는 부호 있는 뺄셈의 오버플로를 나타냅니다.
b.ne 1f벡터 교체 요청이 아니면 다음 서비스 번호인 HVC_FINALISE_EL2를 검사합니다. 앞서 계산한 NZCV 중 Z=0 조건으로 분기합니다. 이 줄은 두 값을 새로 비교하지 않으며 조건이 맞지 않으면 다음 명령으로 진행합니다.
50행 그림 · 조건별 실행 위치
함수·코드 범위: elx_sync
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 조건이 맞는 한 경로만 실행합니다. 이 분기 명령은 NZCV를 새로 계산하지 않습니다.
msr vbar_el2, x1x1의 새 벡터 테이블 주소를 VBAR_EL2에 설치합니다. 호출자는 2KiB 정렬 등 벡터 요구 조건을 만족하는 주소를 넘겨야 합니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.
요청 인자 x1이 새 EL2 벡터 테이블의 물리 주소입니다.
51행 그림 · 시스템 레지스터 쓰기
함수·코드 범위: elx_sync
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 접근 권한과 VHE 별칭 규칙에 따라 실제 대상이 정해집니다. 뒤의 ISB·DSB가 필요한지는 이 레지스터와 문맥에 따릅니다.
b 9f벡터 설치를 마쳤으므로 성공값 0을 돌려주는 공통 반환 지점으로 갑니다. 이 분기는 LR에 새 복귀 주소를 기록하지 않습니다.
52행 그림 · 호출과 실행 위치
함수·코드 범위: elx_sync
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 새 복귀 주소를 LR에 기록하지 않습니다. RET 역시 메모리의 스택 값을 자동으로 꺼내는 명령은 아닙니다.
1: cmp x0, #HVC_FINALISE_EL2EL2 기능 설정을 마무리하고 가능하면 VHE로 전환하는 HVC_FINALISE_EL2 요청인지 검사합니다. CMP는 두 피연산자의 뺄셈 결과로 NZCV 조건 플래그만 갱신하고 원래 레지스터 값은 유지합니다.
54행 그림 · 조건 플래그 계산
함수·코드 범위: elx_sync
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 일반 레지스터에 연산 결과를 쓰지 않고 조건 플래그만 바꿉니다.
- N은 결과의 부호 비트, Z는 결과가 0인지 나타냅니다. C는 빌림이 없으면 1, V는 부호 있는 뺄셈의 오버플로를 나타냅니다.
b.eq __finalise_el2해당 요청이면 __finalise_el2로 넘어갑니다. 지원 기능을 설정한 뒤 커널을 EL2에서 실행할 수 있는지 별도 조건을 확인합니다. 앞서 계산한 NZCV 중 Z=1 조건으로 분기합니다. 이 줄은 두 값을 새로 비교하지 않으며 조건이 맞지 않으면 다음 명령으로 진행합니다.
55행 그림 · 조건별 실행 위치
함수·코드 범위: elx_sync
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 조건이 맞는 한 경로만 실행합니다. 이 분기 명령은 NZCV를 새로 계산하지 않습니다.
2: cmp x0, #HVC_SOFT_RESTARTHVC_SOFT_RESTART인지 검사합니다. 이 요청은 현재 실행을 끝내고 x1의 새 진입점으로 제어를 넘기는 작업입니다. CMP는 두 피연산자의 뺄셈 결과로 NZCV 조건 플래그만 갱신하고 원래 레지스터 값은 유지합니다.
57행 그림 · 조건 플래그 계산
함수·코드 범위: elx_sync
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 일반 레지스터에 연산 결과를 쓰지 않고 조건 플래그만 바꿉니다.
- N은 결과의 부호 비트, Z는 결과가 0인지 나타냅니다. C는 빌림이 없으면 1, V는 부호 있는 뺄셈의 오버플로를 나타냅니다.
b.ne 3f재시작 요청이 아니면 마지막으로 벡터 원상 복구 요청인지 검사합니다. 앞서 계산한 NZCV 중 Z=0 조건으로 분기합니다. 이 줄은 두 값을 새로 비교하지 않으며 조건이 맞지 않으면 다음 명령으로 진행합니다.
58행 그림 · 조건별 실행 위치
함수·코드 범위: elx_sync
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 조건이 맞는 한 경로만 실행합니다. 이 분기 명령은 NZCV를 새로 계산하지 않습니다.
mov x0, x2새 진입점의 첫 번째 인자를 요청 시 x2에서 x0로 옮깁니다. 요청 번호가 차지했던 x0를 실제 부팅 인자로 재사용합니다. MOV는 x0의 기존 값을 대체합니다. 원본이 주소값이어도 그 주소의 메모리를 읽거나 복사하지 않습니다.
59행 그림 · 값 복사
함수·코드 범위: elx_sync
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 화살표는 값의 변화를 뜻합니다. 주소값을 옮겨도 그 주소의 메모리 내용은 복사하지 않습니다.
mov x2, x4새 진입점의 세 번째 인자를 x4에서 x2로 옮깁니다. 곧 x4를 분기 주소 보관에 재사용하므로 먼저 인자를 옮깁니다. MOV는 x2의 기존 값을 대체합니다. 원본이 주소값이어도 그 주소의 메모리를 읽거나 복사하지 않습니다.
60행 그림 · 값 복사
함수·코드 범위: elx_sync
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 화살표는 값의 변화를 뜻합니다. 주소값을 옮겨도 그 주소의 메모리 내용은 복사하지 않습니다.
mov x4, x1새 진입 주소를 x1에서 x4로 보존합니다. x1은 다음 줄에서 두 번째 인자로 바뀝니다. MOV는 x4의 기존 값을 대체합니다. 원본이 주소값이어도 그 주소의 메모리를 읽거나 복사하지 않습니다.
61행 그림 · 값 복사
함수·코드 범위: elx_sync
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 화살표는 값의 변화를 뜻합니다. 주소값을 옮겨도 그 주소의 메모리 내용은 복사하지 않습니다.
mov x1, x3새 진입점의 두 번째 인자를 x3에서 x1로 옮겨 x0~x2의 인자 배치를 완성합니다. MOV는 x1의 기존 값을 대체합니다. 원본이 주소값이어도 그 주소의 메모리를 읽거나 복사하지 않습니다.
62행 그림 · 값 복사
함수·코드 범위: elx_sync
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 화살표는 값의 변화를 뜻합니다. 주소값을 옮겨도 그 주소의 메모리 내용은 복사하지 않습니다.
br x4 // no returnEL2에서 x4의 새 코드로 직접 분기합니다. HVC 호출자로 ERET하는 경로가 아니므로 이전 커널 실행은 여기서 끝납니다. 이 분기는 LR에 새 복귀 주소를 기록하지 않습니다. 레지스터가 담은 주소로 실행 위치를 바꾸며, 그 주소에서 값을 읽어 다른 레지스터에 복사하는 load 명령은 아닙니다.
새 entry로 이동하며 EL1 요청 위치로 ERET하지 않습니다.
63행 그림 · 호출과 실행 위치
함수·코드 범위: elx_sync
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 새 복귀 주소를 LR에 기록하지 않습니다. RET 역시 메모리의 스택 값을 자동으로 꺼내는 명령은 아닙니다.
3: cmp x0, #HVC_RESET_VECTORSHVC_RESET_VECTORS 요청인지 확인합니다. 본격적인 KVM 벡터를 사용하는 경우에는 원래 stub 벡터로 되돌릴 때 쓰는 번호입니다. CMP는 두 피연산자의 뺄셈 결과로 NZCV 조건 플래그만 갱신하고 원래 레지스터 값은 유지합니다.
65행 그림 · 조건 플래그 계산
함수·코드 범위: elx_sync
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 일반 레지스터에 연산 결과를 쓰지 않고 조건 플래그만 바꿉니다.
- N은 결과의 부호 비트, Z는 결과가 0인지 나타냅니다. C는 빌림이 없으면 1, V는 부호 있는 뺄셈의 오버플로를 나타냅니다.
beq 9f // Nothing to reset!현재는 이미 원래 hyp stub이 요청을 받고 있으므로 다시 설치할 벡터가 없습니다. 성공값을 반환하는 경로로 넘어갑니다. 앞서 계산한 NZCV 중 Z=1 조건으로 분기합니다. 이 줄은 두 값을 새로 비교하지 않으며 조건이 맞지 않으면 다음 명령으로 진행합니다.
66행 그림 · 조건별 실행 위치
함수·코드 범위: elx_sync
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 조건이 맞는 한 경로만 실행합니다. 이 분기 명령은 NZCV를 새로 계산하지 않습니다.
mov_q x0, HVC_STUB_ERR지원하지 않는 요청에 대해 HVC_STUB_ERR인 0xbadca11을 x0에 넣습니다. 완전한 KVM 하이퍼바이저가 있어야 처리할 요청을 이 최소 stub에 보낸 경우도 여기에 해당합니다. mov_q는 64비트 상수를 필요한 MOVZ/MOVN·MOVK 조합으로 구성하는 매크로입니다. 해당 주소의 메모리에서 데이터를 가져오는 명령은 아닙니다.
69행 그림 · 값 복사
함수·코드 범위: elx_sync
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 화살표는 값의 변화를 뜻합니다. 주소값을 옮겨도 그 주소의 메모리 내용은 복사하지 않습니다.
- mov_q는 큰 상수를 여러 MOVZ/MOVN·MOVK 명령으로 구성할 수 있습니다. 여기서는 매크로 전체 결과를 표시합니다.
mov_q 매크로 본문 · 552행
arch/arm64/include/asm/assembler.h · Linux v6.18.37
정의 선택 조건: #ifndef __ASM_ASSEMBLER_H
.macro mov_q, reg, val
.if (((\val) >> 31) == 0 || ((\val) >> 31) == 0x1ffffffff)
movz \reg, :abs_g1_s:\val
.else
.if (((\val) >> 47) == 0 || ((\val) >> 47) == 0x1ffff)
movz \reg, :abs_g2_s:\val
.else
movz \reg, :abs_g3:\val
movk \reg, :abs_g2_nc:\val
.endif
movk \reg, :abs_g1_nc:\val
.endif
movk \reg, :abs_g0_nc:\val
.endm eret예외 복귀로 요청한 코드에 돌아가며 x0의 오류값을 전달합니다. 일반 RET과 달리 예외 진입 때 저장한 PC·PSTATE를 사용합니다. 복귀 PC는 ELR에서, 예외 레벨·스택 선택·마스크를 포함한 PSTATE는 SPSR에서 가져옵니다.
70행 그림 · 저장된 실행 상태로 복귀
함수·코드 범위: elx_sync
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- ELx는 현재 예외 레벨입니다. SPSR의 모드에 따라 복귀 레벨과 스택 선택도 바뀝니다. 일반 레지스터 복구는 앞의 load 명령들이 담당합니다.
9: mov x0, xzr처리한 요청의 성공값 0을 x0에 넣습니다. MOV는 x0의 기존 값을 대체합니다. 원본이 주소값이어도 그 주소의 메모리를 읽거나 복사하지 않습니다.
72행 그림 · 값 복사
함수·코드 범위: elx_sync
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 화살표는 값의 변화를 뜻합니다. 주소값을 옮겨도 그 주소의 메모리 내용은 복사하지 않습니다.
eret예외 진입 전에 실행하던 코드로 돌아가 성공 결과를 전달합니다. 복귀 PC는 ELR에서, 예외 레벨·스택 선택·마스크를 포함한 PSTATE는 SPSR에서 가져옵니다.
73행 그림 · 저장된 실행 상태로 복귀
함수·코드 범위: elx_sync
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- ELx는 현재 예외 레벨입니다. SPSR의 모드에 따라 복귀 레벨과 스택 선택도 바뀝니다. 일반 레지스터 복구는 앞의 load 명령들이 담당합니다.
SYM_CODE_END(elx_sync)도구에 이 코드 범위의 끝을 알려 크기와 심볼 정보를 기록합니다. CPU가 이 줄 때문에 자동으로 반환하지는 않습니다.
74행 그림 · 어셈블·링크 단계
함수·코드 범위: elx_sync
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 끝 표기 자체가 RET나 분기를 실행하지는 않습니다.
VHE 전환 조건
SYM_CODE_START_LOCAL(__finalise_el2)HVC_FINALISE_EL2의 실제 처리기입니다. 먼저 CPU 기능에 맞는 EL2 상태를 마무리하고, 이후 VHE 전환 조건을 검사합니다. VHE는 커널이 EL2에서 호스트로 실행되도록 하는 하드웨어 기능입니다. 이 매크로는 정렬된 코드 시작 심벌을 정의합니다. LOCAL 형식은 그 이름을 현재 오브젝트 안에서만 쓰도록 합니다.
76행 그림 · 어셈블·링크 단계
함수·코드 범위: __finalise_el2
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
finalise_el2_stateCPU 기능과 override 설정을 확인하며 MPAM, GCS, SVE, SME 등 지원되는 기능의 EL2 제어 상태를 마무리합니다. 예를 들어 MPAM trap을 정리하고 SVE·SME 접근과 벡터 길이를 설정합니다. 이 매크로 자체가 곧바로 커널을 VHE로 옮기는 것은 아닙니다.
77행 그림 · 매크로 안의 상태 변화
함수·코드 범위: __finalise_el2
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 여러 명령과 내부 분기를 묶은 그림입니다. 작업 레지스터를 모두 보존한다는 뜻은 아니며, 정확한 변경 순서는 아래 매크로 본문과 줄별 그림에서 확인합니다.
finalise_el2_state 매크로 본문 · 503행
arch/arm64/include/asm/el2_setup.h · Linux v6.18.37
정의 선택 조건: #ifndef __ARM_KVM_INIT_H__
.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 mrs x1, sctlr_el2EL2의 SCTLR을 읽어 현재 EL2 MMU가 꺼져 있는지 확인할 준비를 합니다. 이어지는 VHE 주소 변환 환경 교체는 MMU가 꺼져 있다는 전제를 가집니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.
81행 그림 · 시스템 레지스터 읽기
함수·코드 범위: __finalise_el2
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 일반 RAM의 메모리 접근과 구분합니다.
tbnz x1, #0, 1fSCTLR_EL2의 M 비트인 비트 0이 1이면 전환을 거부합니다. 이미 EL2 MMU를 쓰는 상태에서 이 초기 전환 절차를 수행하지 않도록 막습니다. 이 분기는 지정 레지스터의 값이나 비트를 직접 검사하며 CMP가 남긴 NZCV를 읽거나 바꾸지 않습니다. 조건이 맞지 않으면 바로 다음 명령으로 진행합니다.
EL2 MMU가 이미 켜져 있으면 VHE 전환을 거절합니다.
82행 그림 · 조건별 실행 위치
함수·코드 범위: __finalise_el2
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 조건이 맞는 한 경로만 실행합니다. 이 분기 명령은 NZCV를 새로 계산하지 않습니다.
check_override id_aa64mmfr1 ID_AA64MMFR1_EL1_VH_SHIFT 0f 1f x1 x2ID_AA64MMFR1_EL1의 VH 필드와 커널의 기능 override를 함께 검사합니다. VHE가 실제로 지원되고 설정상 허용되면 0f로, 사용할 수 없으면 오류 경로 1f로 갑니다. x1·x2는 검사에 사용하는 작업 레지스터입니다.
85행 그림 · 매크로 안의 상태 변화
함수·코드 범위: __finalise_el2
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 여러 명령과 내부 분기를 묶은 그림입니다. 작업 레지스터를 모두 보존한다는 뜻은 아니며, 정확한 변경 순서는 아래 매크로 본문과 줄별 그림에서 확인합니다.
check_override 매크로 본문 · 485행
arch/arm64/include/asm/el2_setup.h · Linux v6.18.37
정의 선택 조건: #ifndef __ARM_KVM_INIT_H__ / #ifndef __KVM_NVHE_HYPERVISOR__
.macro check_override idreg, fld, pass, fail, tmp1, tmp2
mrs \tmp1, \idreg\()_el1
__check_override \idreg \fld 4 \pass \fail \tmp1 \tmp2
.endmcheck_override 매크로 본문 · 498행
arch/arm64/include/asm/el2_setup.h · Linux v6.18.37
정의 선택 조건: #ifndef __ARM_KVM_INIT_H__ / 앞 조건의 반대
.macro check_override idreg, fld, pass, fail, tmp, ignore
__check_override \idreg \fld 4 \pass \fail \tmp \ignore
.endm0: // Check whether we only want the hypervisor to run VHE, not the kernel하드웨어 VHE 검사에 통과한 뒤 소프트웨어의 hVHE 정책을 검사하는 지점입니다. CPU 지원만으로 커널의 EL2 실행을 결정하지 않습니다. 레이블은 이 위치에 붙인 이름이며, 이름을 적는 것만으로 CPU 명령이 추가되지는 않습니다.
87행 그림 · 위치에 이름 붙이기
함수·코드 범위: __finalise_el2
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
adr_l x1, arm64_sw_feature_override소프트웨어 기능 override 구조체의 주소를 구합니다. CPU에 VHE가 있더라도 커널 전체를 EL2로 옮길지 여부는 소프트웨어 정책까지 확인해야 합니다. 여기서 얻는 것은 레이블의 주소이며, 그 위치의 메모리 내용은 아직 읽지 않습니다. adr_l 매크로는 ADRP와 ADD로 주소의 페이지 부분과 하위 오프셋을 합칩니다.
88행 그림 · 주소·값 구분
함수·코드 범위: __finalise_el2
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 이 매크로의 임시 레지스터는 내부에서 사용됩니다.
adr_l 매크로 본문 · 175행
arch/arm64/include/asm/assembler.h · Linux v6.18.37
정의 선택 조건: #ifndef __ASM_ASSEMBLER_H
.macro adr_l, dst, sym
adrp \dst, \sym
add \dst, \dst, :lo12:\sym
.endm ldr x2, [x1, FTR_OVR_VAL_OFFSET]override가 지정한 기능값을 x2에 읽습니다. 실제로 x1 + (FTR_OVR_VAL_OFFSET) 주소의 메모리 8바이트를 x2에 읽습니다. 이 주소 형식은 기준 레지스터 x1 자체를 갱신하지 않습니다.
89행 그림 · 메모리 읽기
함수·코드 범위: __finalise_el2
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 주소 계산에 의한 기준 레지스터 갱신은 없습니다. 다만 LDR/LDP의 목적 레지스터가 기준 레지스터와 같으면 읽은 값이 그 레지스터를 덮어씁니다.
ldr x1, [x1, FTR_OVR_MASK_OFFSET]어떤 기능값을 덮어쓰도록 지정했는지를 나타내는 mask를 x1에 읽습니다. 지정하지 않은 비트는 정책 판단에 포함하지 않습니다. 실제로 x1 + (FTR_OVR_MASK_OFFSET) 주소의 메모리 8바이트를 x1에 읽습니다.
90행 그림 · 메모리 읽기
함수·코드 범위: __finalise_el2
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 주소 계산에 의한 기준 레지스터 갱신은 없습니다. 다만 LDR/LDP의 목적 레지스터가 기준 레지스터와 같으면 읽은 값이 그 레지스터를 덮어씁니다.
and x2, x2, x1기능값과 mask를 AND하여 실제로 지정한 override 비트만 남깁니다. AND는 두 피연산자의 같은 위치 비트가 모두 1인 곳만 결과에 남깁니다.
91행 그림 · 레지스터 상태
함수·코드 범위: __finalise_el2
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 결과는 목적 레지스터 폭에 맞습니다. 이 형식은 NZCV를 바꾸지 않습니다.
ubfx x2, x2, #ARM64_SW_FEATURE_OVERRIDE_HVHE, #4hVHE 정책 필드의 4비트를 꺼냅니다. hVHE를 선택한 경우 하이퍼바이저는 VHE를 사용하되 일반 커널은 EL1에 남아야 합니다. 추출한 필드는 결과의 하위 비트에 놓이고 나머지 상위 비트는 0으로 채워집니다.
92행 그림 · 비트 필드 추출
함수·코드 범위: __finalise_el2
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
cbz x2, 2fhVHE 정책이 0이면 일반 커널까지 VHE로 옮기는 전환을 진행합니다. 0이 아니면 아래 오류 반환 경로로 흘러가 EL1 커널을 유지합니다. 이 분기는 지정 레지스터의 값이나 비트를 직접 검사하며 CMP가 남긴 NZCV를 읽거나 바꾸지 않습니다. 조건이 맞지 않으면 바로 다음 명령으로 진행합니다.
마스크에 반영된 HVHE override가 0일 때만 host VHE 전환을 계속합니다.
93행 그림 · 조건별 실행 위치
함수·코드 범위: __finalise_el2
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 조건이 맞는 한 경로만 실행합니다. 이 분기 명령은 NZCV를 새로 계산하지 않습니다.
1: mov_q x0, HVC_STUB_ERRVHE 전환을 진행할 수 없는 경우 HVC_STUB_ERR를 x0에 넣습니다. 앞서 수행한 EL2 기능 초기화와 커널의 EL2 이동 성공 여부는 구분해야 합니다. mov_q는 64비트 상수를 필요한 MOVZ/MOVN·MOVK 조합으로 구성하는 매크로입니다. 해당 주소의 메모리에서 데이터를 가져오는 명령은 아닙니다.
95행 그림 · 값 복사
함수·코드 범위: __finalise_el2
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 화살표는 값의 변화를 뜻합니다. 주소값을 옮겨도 그 주소의 메모리 내용은 복사하지 않습니다.
- mov_q는 큰 상수를 여러 MOVZ/MOVN·MOVK 명령으로 구성할 수 있습니다. 여기서는 매크로 전체 결과를 표시합니다.
mov_q 매크로 본문 · 552행
arch/arm64/include/asm/assembler.h · Linux v6.18.37
정의 선택 조건: #ifndef __ASM_ASSEMBLER_H
.macro mov_q, reg, val
.if (((\val) >> 31) == 0 || ((\val) >> 31) == 0x1ffffffff)
movz \reg, :abs_g1_s:\val
.else
.if (((\val) >> 47) == 0 || ((\val) >> 47) == 0x1ffff)
movz \reg, :abs_g2_s:\val
.else
movz \reg, :abs_g3:\val
movk \reg, :abs_g2_nc:\val
.endif
movk \reg, :abs_g1_nc:\val
.endif
movk \reg, :abs_g0_nc:\val
.endm eret전환하지 않은 상태로 HVC 호출자에게 복귀합니다. 복귀 PC는 ELR에서, 예외 레벨·스택 선택·마스크를 포함한 PSTATE는 SPSR에서 가져옵니다.
96행 그림 · 저장된 실행 상태로 복귀
함수·코드 범위: __finalise_el2
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- ELx는 현재 예외 레벨입니다. SPSR의 모드에 따라 복귀 레벨과 스택 선택도 바뀝니다. 일반 레지스터 복구는 앞의 load 명령들이 담당합니다.
2:VHE 전환 조건을 모두 통과했을 때 들어오는 지점입니다. 여기부터 HCR·스택·주소 변환 상태를 EL2 호스트용으로 옮깁니다. 레이블은 이 위치에 붙인 이름이며, 이름을 적는 것만으로 CPU 명령이 추가되지는 않습니다.
97행 그림 · 위치에 이름 붙이기
함수·코드 범위: __finalise_el2
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
mov_q x0, HCR_HOST_VHE_FLAGSHCR_HOST_VHE_FLAGS를 준비합니다. AArch64 실행을 지정하는 RW, 호스트 레지스터 동작을 선택하는 E2H, EL2 호스트로 예외를 향하게 하는 TGE와 AMO·IMO·FMO 등이 포함됩니다. mov_q는 64비트 상수를 필요한 MOVZ/MOVN·MOVK 조합으로 구성하는 매크로입니다. 해당 주소의 메모리에서 데이터를 가져오는 명령은 아닙니다.
99행 그림 · 값 복사
함수·코드 범위: __finalise_el2
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 화살표는 값의 변화를 뜻합니다. 주소값을 옮겨도 그 주소의 메모리 내용은 복사하지 않습니다.
- mov_q는 큰 상수를 여러 MOVZ/MOVN·MOVK 명령으로 구성할 수 있습니다. 여기서는 매크로 전체 결과를 표시합니다.
mov_q 매크로 본문 · 552행
arch/arm64/include/asm/assembler.h · Linux v6.18.37
정의 선택 조건: #ifndef __ASM_ASSEMBLER_H
.macro mov_q, reg, val
.if (((\val) >> 31) == 0 || ((\val) >> 31) == 0x1ffffffff)
movz \reg, :abs_g1_s:\val
.else
.if (((\val) >> 47) == 0 || ((\val) >> 47) == 0x1ffff)
movz \reg, :abs_g2_s:\val
.else
movz \reg, :abs_g3:\val
movk \reg, :abs_g2_nc:\val
.endif
movk \reg, :abs_g1_nc:\val
.endif
movk \reg, :abs_g0_nc:\val
.endm msr_hcr_el2 x0HCR_EL2에 VHE 호스트 플래그를 적용합니다. CONFIG_AMPERE_ERRATUM_AC04_CPU_23이 켜진 빌드는 Ampere CPU의 해당 문제를 우회하도록 DSB NSH와 ISB를 쓰기 앞뒤에 추가합니다. 이후 EL1 이름으로 접근하는 여러 레지스터가 EL2 호스트용 별칭으로 동작합니다.
E2H/TGE를 포함하는 host VHE 설정 후 EL1 이름의 일부 레지스터 접근은 host EL2 대상으로 바뀝니다.
100행 그림 · 매크로 전체의 상태 변화
함수·코드 범위: __finalise_el2
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- HCR_EL2를 쓰며 해당 CPU 우회가 필요한 구성에서는 주변 동기화 명령을 추가합니다.
msr_hcr_el2 매크로 본문 · 1144행
arch/arm64/include/asm/sysreg.h · Linux v6.18.37
정의 선택 조건: #ifndef __ASM_SYSREG_H / #ifdef __ASSEMBLY__
.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 isbHCR 변경을 반영합니다. 바로 뒤의 스택·시스템 레지스터 이전은 VHE 호스트 레지스터 접근 규칙을 전제로 합니다.
101행 그림 · 동기화·캐시·대기 상태
함수·코드 범위: __finalise_el2
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
EL1의 상태를 host EL2로 복사
mrs x0, sp_el1기존 EL1 커널이 사용하던 스택 포인터를 SP_EL1에서 읽습니다. EL2로 옮겨도 같은 커널 호출 흐름을 이어가기 위한 값입니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.
104행 그림 · 시스템 레지스터 읽기
함수·코드 범위: __finalise_el2
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 일반 RAM의 메모리 접근과 구분합니다.
mov sp, x0EL2의 현재 SP에 기존 EL1 스택 포인터를 설치합니다. 커널의 프레임을 새로 만드는 것이 아니라 같은 스택을 이어 사용합니다. 바뀌는 것은 SP가 담은 주소입니다. 원래 스택의 데이터가 새 주소로 복사되는 것은 아닙니다.
105행 그림 · 값 복사
함수·코드 범위: __finalise_el2
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 화살표는 값의 변화를 뜻합니다. 주소값을 옮겨도 그 주소의 메모리 내용은 복사하지 않습니다.
mrs x0, tpidr_el1EL1에서 유지하던 per-CPU 오프셋을 TPIDR_EL1에서 읽습니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.
106행 그림 · 시스템 레지스터 읽기
함수·코드 범위: __finalise_el2
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 일반 RAM의 메모리 접근과 구분합니다.
msr tpidr_el2, x0per-CPU 오프셋을 TPIDR_EL2에 옮깁니다. VHE로 실행 위치가 바뀐 뒤에도 현재 CPU의 커널 데이터를 같은 방식으로 찾기 위해 필요합니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.
107행 그림 · 시스템 레지스터 쓰기
함수·코드 범위: __finalise_el2
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 접근 권한과 VHE 별칭 규칙에 따라 실제 대상이 정해집니다. 뒤의 ISB·DSB가 필요한지는 이 레지스터와 문맥에 따릅니다.
mrs_s x0, SYS_CPACR_EL12EL12 별칭으로 기존 EL1의 FP/SIMD 등 접근 제어값 CPACR을 읽습니다. VHE 설정 이후에도 EL12 이름은 이전 EL1 상태를 명시적으로 가리킵니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.
EL12로 원래 EL1의 CPACR을 읽습니다.
110행 그림 · 시스템 레지스터 읽기
함수·코드 범위: __finalise_el2
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 일반 RAM의 메모리 접근과 구분합니다.
mrs_s 매크로 본문 · 1136행
arch/arm64/include/asm/sysreg.h · Linux v6.18.37
정의 선택 조건: #ifndef __ASM_SYSREG_H / #ifdef __ASSEMBLY__
.macro mrs_s, rt, sreg
__emit_inst(0xd5200000|(\sreg)|(.L__gpr_num_\rt))
.endm msr cpacr_el1, x0읽은 접근 제어값을 VHE 호스트의 CPACR_EL1 접근으로 설치합니다. 커널이 EL2로 이동해도 FP/SIMD 등의 허용 상태를 이어받습니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.
111행 그림 · 시스템 레지스터 쓰기
함수·코드 범위: __finalise_el2
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 접근 권한과 VHE 별칭 규칙에 따라 실제 대상이 정해집니다. 뒤의 ISB·DSB가 필요한지는 이 레지스터와 문맥에 따릅니다.
mrs_s x0, SYS_VBAR_EL12기존 EL1의 예외 벡터 주소를 VBAR_EL12로 읽습니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.
112행 그림 · 시스템 레지스터 읽기
함수·코드 범위: __finalise_el2
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 일반 RAM의 메모리 접근과 구분합니다.
mrs_s 매크로 본문 · 1136행
arch/arm64/include/asm/sysreg.h · Linux v6.18.37
정의 선택 조건: #ifndef __ASM_SYSREG_H / #ifdef __ASSEMBLY__
.macro mrs_s, rt, sreg
__emit_inst(0xd5200000|(\sreg)|(.L__gpr_num_\rt))
.endm msr vbar_el1, x0같은 커널 예외 벡터를 VHE 호스트 쪽에 설치합니다. EL2에서 커널을 실행할 때도 원래 커널 예외 처리 코드로 연결해야 합니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.
113행 그림 · 시스템 레지스터 쓰기
함수·코드 범위: __finalise_el2
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 접근 권한과 VHE 별칭 규칙에 따라 실제 대상이 정해집니다. 뒤의 ISB·DSB가 필요한지는 이 레지스터와 문맥에 따릅니다.
mrs x0, mdcr_el2디버그·추적 관련 EL2 제어값 MDCR_EL2를 읽습니다. SPE와 TRBE가 버퍼에 접근할 주소 변환 환경도 커널의 새 실행 EL에 맞춰야 합니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.
116행 그림 · 시스템 레지스터 읽기
함수·코드 범위: __finalise_el2
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 일반 RAM의 메모리 접근과 구분합니다.
bic x0, x0, #MDCR_EL2_E2PB_MASKE2PB 필드를 지워 SPE의 profiling buffer가 EL2 주소 변환을 사용하고 EL1에서 해당 제어에 접근하지 못하도록 하는 설정을 준비합니다. BIC는 마스크에서 1인 위치만 0으로 만들고 나머지 비트는 유지합니다.
117행 그림 · 레지스터 상태
함수·코드 범위: __finalise_el2
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 결과는 목적 레지스터 폭에 맞습니다. 이 형식은 NZCV를 바꾸지 않습니다.
bic x0, x0, #MDCR_EL2_E2TB_MASKE2TB 필드도 지워 TRBE의 trace buffer에 같은 EL2 호스트 정책을 적용합니다. BIC는 마스크에서 1인 위치만 0으로 만들고 나머지 비트는 유지합니다.
118행 그림 · 레지스터 상태
함수·코드 범위: __finalise_el2
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 결과는 목적 레지스터 폭에 맞습니다. 이 형식은 NZCV를 바꾸지 않습니다.
msr mdcr_el2, x0수정한 SPE·TRBE 제어값을 MDCR_EL2에 기록합니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.
119행 그림 · 시스템 레지스터 쓰기
함수·코드 범위: __finalise_el2
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 접근 권한과 VHE 별칭 규칙에 따라 실제 대상이 정해집니다. 뒤의 ISB·DSB가 필요한지는 이 레지스터와 문맥에 따릅니다.
mrs_s x0, SYS_TCR_EL12기존 EL1의 주소 변환 제어값을 TCR_EL12로 읽습니다. 주소 크기와 페이지 변환 방식 등의 설정을 VHE 호스트가 이어받아야 합니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.
122행 그림 · 시스템 레지스터 읽기
함수·코드 범위: __finalise_el2
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 일반 RAM의 메모리 접근과 구분합니다.
mrs_s 매크로 본문 · 1136행
arch/arm64/include/asm/sysreg.h · Linux v6.18.37
정의 선택 조건: #ifndef __ASM_SYSREG_H / #ifdef __ASSEMBLY__
.macro mrs_s, rt, sreg
__emit_inst(0xd5200000|(\sreg)|(.L__gpr_num_\rt))
.endm msr tcr_el1, x0기존 TCR 값을 VHE 호스트의 변환 제어 레지스터에 설치합니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.
123행 그림 · 시스템 레지스터 쓰기
함수·코드 범위: __finalise_el2
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 접근 권한과 VHE 별칭 규칙에 따라 실제 대상이 정해집니다. 뒤의 ISB·DSB가 필요한지는 이 레지스터와 문맥에 따릅니다.
mrs_s x0, SYS_TTBR0_EL12기존 EL1의 TTBR0 페이지 테이블 기준값을 읽습니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.
124행 그림 · 시스템 레지스터 읽기
함수·코드 범위: __finalise_el2
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 일반 RAM의 메모리 접근과 구분합니다.
mrs_s 매크로 본문 · 1136행
arch/arm64/include/asm/sysreg.h · Linux v6.18.37
정의 선택 조건: #ifndef __ASM_SYSREG_H / #ifdef __ASSEMBLY__
.macro mrs_s, rt, sreg
__emit_inst(0xd5200000|(\sreg)|(.L__gpr_num_\rt))
.endm msr ttbr0_el1, x0TTBR0 기준값을 VHE 호스트의 주소 변환 환경으로 옮깁니다. 이미 쓰던 낮은 가상 주소 영역의 매핑을 이어받습니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.
125행 그림 · 시스템 레지스터 쓰기
함수·코드 범위: __finalise_el2
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 접근 권한과 VHE 별칭 규칙에 따라 실제 대상이 정해집니다. 뒤의 ISB·DSB가 필요한지는 이 레지스터와 문맥에 따릅니다.
mrs_s x0, SYS_TTBR1_EL12기존 EL1의 TTBR1 페이지 테이블 기준값을 읽습니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.
126행 그림 · 시스템 레지스터 읽기
함수·코드 범위: __finalise_el2
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 일반 RAM의 메모리 접근과 구분합니다.
mrs_s 매크로 본문 · 1136행
arch/arm64/include/asm/sysreg.h · Linux v6.18.37
정의 선택 조건: #ifndef __ASM_SYSREG_H / #ifdef __ASSEMBLY__
.macro mrs_s, rt, sreg
__emit_inst(0xd5200000|(\sreg)|(.L__gpr_num_\rt))
.endm msr ttbr1_el1, x0TTBR1 기준값을 VHE 호스트에 설치하여 커널 가상 주소 매핑을 이어받습니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.
127행 그림 · 시스템 레지스터 쓰기
함수·코드 범위: __finalise_el2
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 접근 권한과 VHE 별칭 규칙에 따라 실제 대상이 정해집니다. 뒤의 ISB·DSB가 필요한지는 이 레지스터와 문맥에 따릅니다.
mrs_s x0, SYS_MAIR_EL12기존 EL1의 MAIR 메모리 속성표를 읽습니다. 페이지 테이블의 AttrIndx가 뜻하는 장치·일반 메모리 속성을 유지해야 합니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.
128행 그림 · 시스템 레지스터 읽기
함수·코드 범위: __finalise_el2
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 일반 RAM의 메모리 접근과 구분합니다.
mrs_s 매크로 본문 · 1136행
arch/arm64/include/asm/sysreg.h · Linux v6.18.37
정의 선택 조건: #ifndef __ASM_SYSREG_H / #ifdef __ASSEMBLY__
.macro mrs_s, rt, sreg
__emit_inst(0xd5200000|(\sreg)|(.L__gpr_num_\rt))
.endm msr mair_el1, x0같은 MAIR 값을 VHE 호스트 쪽에 설치하여 동일한 페이지 테이블이 같은 메모리 속성으로 해석되게 합니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.
129행 그림 · 시스템 레지스터 쓰기
함수·코드 범위: __finalise_el2
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 접근 권한과 VHE 별칭 규칙에 따라 실제 대상이 정해집니다. 뒤의 ISB·DSB가 필요한지는 이 레지스터와 문맥에 따릅니다.
mrs x1, REG_ID_AA64MMFR3_EL1CPU의 ID_AA64MMFR3_EL1을 읽어 추가 변환 제어 레지스터 TCR2 지원 여부를 확인합니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.
130행 그림 · 시스템 레지스터 읽기
함수·코드 범위: __finalise_el2
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 일반 RAM의 메모리 접근과 구분합니다.
ubfx x1, x1, #ID_AA64MMFR3_EL1_TCRX_SHIFT, #4TCRX 기능 필드 4비트를 꺼내 TCR2 레지스터가 있는지 판단할 값을 만듭니다. 추출한 필드는 결과의 하위 비트에 놓이고 나머지 상위 비트는 0으로 채워집니다.
131행 그림 · 비트 필드 추출
함수·코드 범위: __finalise_el2
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
cbz x1, .Lskip_tcr2TCR2를 지원하지 않으면 해당 레지스터에 접근하지 않고 건너뜁니다. 이 소스의 건너뛰기 지점은 아래 permission indirection 이전 구간까지 지난 위치에 있습니다. 이 분기는 지정 레지스터의 값이나 비트를 직접 검사하며 CMP가 남긴 NZCV를 읽거나 바꾸지 않습니다. 조건이 맞지 않으면 바로 다음 명령으로 진행합니다.
132행 그림 · 조건별 실행 위치
함수·코드 범위: __finalise_el2
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 조건이 맞는 한 경로만 실행합니다. 이 분기 명령은 NZCV를 새로 계산하지 않습니다.
mrs x0, REG_TCR2_EL12지원되는 CPU에서 기존 EL1의 TCR2_EL12 값을 읽습니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.
133행 그림 · 시스템 레지스터 읽기
함수·코드 범위: __finalise_el2
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 일반 RAM의 메모리 접근과 구분합니다.
msr REG_TCR2_EL1, x0추가 주소 변환 제어값을 VHE 호스트의 TCR2에 옮깁니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.
134행 그림 · 시스템 레지스터 쓰기
함수·코드 범위: __finalise_el2
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 접근 권한과 VHE 별칭 규칙에 따라 실제 대상이 정해집니다. 뒤의 ISB·DSB가 필요한지는 이 레지스터와 문맥에 따릅니다.
mrs x1, REG_ID_AA64MMFR3_EL1같은 기능 레지스터를 다시 읽어 Stage-1 Permission Indirection 지원 여부를 검사합니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.
137행 그림 · 시스템 레지스터 읽기
함수·코드 범위: __finalise_el2
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 일반 RAM의 메모리 접근과 구분합니다.
ubfx x1, x1, #ID_AA64MMFR3_EL1_S1PIE_SHIFT, #4S1PIE 기능 필드를 꺼냅니다. 이 기능은 페이지별 권한을 간접 권한표로 해석할 수 있게 합니다. 추출한 필드는 결과의 하위 비트에 놓이고 나머지 상위 비트는 0으로 채워집니다.
138행 그림 · 비트 필드 추출
함수·코드 범위: __finalise_el2
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
cbz x1, .Lskip_indirection권한 간접 참조를 지원하지 않으면 PIR 계열 레지스터 읽기·쓰기를 건너뜁니다. 이 분기는 지정 레지스터의 값이나 비트를 직접 검사하며 CMP가 남긴 NZCV를 읽거나 바꾸지 않습니다. 조건이 맞지 않으면 바로 다음 명령으로 진행합니다.
139행 그림 · 조건별 실행 위치
함수·코드 범위: __finalise_el2
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 조건이 맞는 한 경로만 실행합니다. 이 분기 명령은 NZCV를 새로 계산하지 않습니다.
mrs x0, REG_PIRE0_EL12기존 EL1의 EL0용 권한 간접 참조표 PIRE0를 읽습니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.
140행 그림 · 시스템 레지스터 읽기
함수·코드 범위: __finalise_el2
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 일반 RAM의 메모리 접근과 구분합니다.
msr REG_PIRE0_EL1, x0PIRE0 값을 VHE 호스트 쪽으로 옮겨 EL0 접근 권한 해석을 유지합니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.
141행 그림 · 시스템 레지스터 쓰기
함수·코드 범위: __finalise_el2
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 접근 권한과 VHE 별칭 규칙에 따라 실제 대상이 정해집니다. 뒤의 ISB·DSB가 필요한지는 이 레지스터와 문맥에 따릅니다.
mrs x0, REG_PIR_EL12기존 EL1의 PIR 권한 간접 참조표를 읽습니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.
142행 그림 · 시스템 레지스터 읽기
함수·코드 범위: __finalise_el2
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 일반 RAM의 메모리 접근과 구분합니다.
msr REG_PIR_EL1, x0PIR 값을 VHE 호스트에 설치하여 기존 페이지 권한 설정을 이어받습니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.
143행 그림 · 시스템 레지스터 쓰기
함수·코드 범위: __finalise_el2
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 접근 권한과 VHE 별칭 규칙에 따라 실제 대상이 정해집니다. 뒤의 ISB·DSB가 필요한지는 이 레지스터와 문맥에 따릅니다.
.Lskip_indirection:권한 간접 참조 레지스터를 지원하지 않거나 그 이전을 마친 경우 합류하는 위치입니다. 레이블은 이 위치에 붙인 이름이며, 이름을 적는 것만으로 CPU 명령이 추가되지는 않습니다.
145행 그림 · 위치에 이름 붙이기
함수·코드 범위: __finalise_el2
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
.Lskip_tcr2:TCR2를 지원하지 않는 CPU가 건너오는 위치입니다. 이 소스에서는 permission indirection 이전 부분도 함께 건너뛴 뒤 ISB부터 계속합니다. 레이블은 이 위치에 붙인 이름이며, 이름을 적는 것만으로 CPU 명령이 추가되지는 않습니다.
146행 그림 · 위치에 이름 붙이기
함수·코드 범위: __finalise_el2
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
isb지금까지 옮긴 시스템 레지스터 설정을 뒤의 실행에 반영합니다.
148행 그림 · 동기화·캐시·대기 상태
함수·코드 범위: __finalise_el2
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
mrs x0, spsr_el1HVC 예외 복귀에 사용할 저장 PSTATE를 읽습니다. VHE의 레지스터 별칭 때문에 여기의 SPSR_EL1 접근은 호스트 EL2의 예외 복귀 상태를 다룹니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.
151행 그림 · 시스템 레지스터 읽기
함수·코드 범위: __finalise_el2
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 일반 RAM의 메모리 접근과 구분합니다.
and x0, x0, #~PSR_MODE_MASK저장 PSTATE에서 실행 모드 비트만 지웁니다. 다른 상태 비트는 유지하면서 복귀할 EL과 스택 선택만 바꾸려는 작업입니다. AND는 두 피연산자의 같은 위치 비트가 모두 1인 곳만 결과에 남깁니다.
152행 그림 · 레지스터 상태
함수·코드 범위: __finalise_el2
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 결과는 목적 레지스터 폭에 맞습니다. 이 형식은 NZCV를 바꾸지 않습니다.
mov x1, #PSR_MODE_EL2hEL2h 모드 값을 준비합니다. EL2에서 SP_EL2를 사용하는 호스트 실행 상태를 뜻합니다. MOV는 x1의 기존 값을 대체합니다. 원본이 주소값이어도 그 주소의 메모리를 읽거나 복사하지 않습니다.
153행 그림 · 값 복사
함수·코드 범위: __finalise_el2
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 화살표는 값의 변화를 뜻합니다. 주소값을 옮겨도 그 주소의 메모리 내용은 복사하지 않습니다.
orr x0, x0, x1기존 PSTATE의 나머지 비트에 EL2h 모드를 합칩니다. HVC 이전 코드로 돌아가되 실행 EL은 EL2로 바뀌게 됩니다. ORR은 어느 한쪽이 1인 비트를 결과에 세우므로 원래 1이던 비트도 유지됩니다.
154행 그림 · 레지스터 상태
함수·코드 범위: __finalise_el2
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 결과는 목적 레지스터 폭에 맞습니다. 이 형식은 NZCV를 바꾸지 않습니다.
msr spsr_el1, x0수정한 예외 복귀 상태를 기록합니다. 마지막 ERET은 이 값에 따라 EL1 대신 EL2h에서 실행을 이어갑니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.
복귀 모드를 EL2h로 설정해 이후 ERET가 EL2 실행을 유지하게 합니다.
155행 그림 · 시스템 레지스터 쓰기
함수·코드 범위: __finalise_el2
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 접근 권한과 VHE 별칭 규칙에 따라 실제 대상이 정해집니다. 뒤의 ISB·DSB가 필요한지는 이 레지스터와 문맥에 따릅니다.
b enter_vheMMU를 켤 수 있는 idmap의 enter_vhe로 넘어갑니다. 아직 EL2 MMU가 꺼진 상태이므로 가상 주소 변환이 바뀌어도 유효한 실행 위치가 필요합니다. 이 분기는 LR에 새 복귀 주소를 기록하지 않습니다.
157행 그림 · 호출과 실행 위치
함수·코드 범위: __finalise_el2
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 새 복귀 주소를 LR에 기록하지 않습니다. RET 역시 메모리의 스택 값을 자동으로 꺼내는 명령은 아닙니다.
SYM_CODE_END(__finalise_el2)도구에 이 코드 범위의 끝을 알려 크기와 심볼 정보를 기록합니다. CPU가 이 줄 때문에 자동으로 반환하지는 않습니다.
158행 그림 · 어셈블·링크 단계
함수·코드 범위: __finalise_el2
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 끝 표기 자체가 RET나 분기를 실행하지는 않습니다.
idmap에서 MMU 켜기와 잘못된 벡터
.pushsection .idmap.text, "ax"MMU 켜기 직전·직후에 같은 주소로 실행할 수 있도록 enter_vhe를 idmap 섹션에 둡니다. 이는 빌드 중 출력할 바이트의 섹션을 선택하는 지시문이며, 실행 중 PC나 SP를 옮기지 않습니다. 이전 출력 섹션을 함께 기억하므로 뒤의 .popsection으로 되돌릴 수 있습니다.
165행 그림 · 어셈블·링크 단계
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
SYM_CODE_START_LOCAL(enter_vhe)EL1에서 넘겨받은 페이지 테이블과 설정으로 EL2 호스트 MMU를 실제로 켜는 마지막 전환 구간입니다. 이 매크로는 정렬된 코드 시작 심벌을 정의합니다. LOCAL 형식은 그 이름을 현재 오브젝트 안에서만 쓰도록 합니다.
167행 그림 · 어셈블·링크 단계
함수·코드 범위: enter_vhe
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
tlbi vmalle1VHE 호스트가 사용할 주소 변환 체계의 기존 TLB 항목을 무효화합니다. 새로 옮긴 페이지 테이블 설정과 이전 캐시된 변환이 섞이지 않도록 합니다. 페이지 테이블 메모리를 수정해도 이미 저장된 TLB 변환이 자동으로 없어지는 것은 아니므로 별도로 무효화합니다.
169행 그림 · 동기화·캐시·대기 상태
함수·코드 범위: enter_vhe
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 관리 요청과 완료 보장은 구분됩니다. 주변 DSB·ISB와 함께 해석합니다.
dsb nsh요청한 TLB 무효화가 로컬에서 완료되기를 기다립니다.
170행 그림 · 동기화·캐시·대기 상태
함수·코드 범위: enter_vhe
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
isbTLB·시스템 상태 변경 뒤 명령 실행을 동기화합니다.
171행 그림 · 동기화·캐시·대기 상태
함수·코드 범위: enter_vhe
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
mrs_s x0, SYS_SCTLR_EL12기존 EL1에서 MMU를 켜고 사용하던 SCTLR 값을 EL12 별칭으로 읽습니다. 새로 임의의 설정을 만드는 대신 이전 커널 실행 환경을 이어받습니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.
174행 그림 · 시스템 레지스터 읽기
함수·코드 범위: enter_vhe
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 일반 RAM의 메모리 접근과 구분합니다.
mrs_s 매크로 본문 · 1136행
arch/arm64/include/asm/sysreg.h · Linux v6.18.37
정의 선택 조건: #ifndef __ASM_SYSREG_H / #ifdef __ASSEMBLY__
.macro mrs_s, rt, sreg
__emit_inst(0xd5200000|(\sreg)|(.L__gpr_num_\rt))
.endm set_sctlr_el1 x0읽은 SCTLR을 VHE 호스트 쪽에 적용하여 EL2의 Stage-1 MMU를 켭니다. 매크로는 SCTLR 쓰기 뒤 ISB와 로컬 명령 캐시 무효화·완료 대기까지 수행합니다.
VHE 상태에서 SCTLR_EL1 이름으로 쓰는 대상은 host EL2의 변환 제어입니다.
175행 그림 · 매크로 전체의 상태 변화
함수·코드 범위: enter_vhe
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- MSR SCTLR_EL1→ISB→IC IALLU→DSB NSH→ISB로 제어 상태와 명령어 캐시를 동기화합니다.
set_sctlr_el1 매크로 본문 · 733행
arch/arm64/include/asm/assembler.h · Linux v6.18.37
정의 선택 조건: #ifndef __ASM_ASSEMBLER_H
.macro set_sctlr_el1, reg
set_sctlr sctlr_el1, \reg
.endm mov_q x0, INIT_SCTLR_EL1_MMU_OFF이제 사용하지 않을 EL1 쪽에는 MMU-off SCTLR 초기값을 준비합니다. 호스트 EL2 쪽 설정과 기존 EL1 쪽 설정을 분리하여 마무리합니다. mov_q는 64비트 상수를 필요한 MOVZ/MOVN·MOVK 조합으로 구성하는 매크로입니다. 해당 주소의 메모리에서 데이터를 가져오는 명령은 아닙니다.
178행 그림 · 값 복사
함수·코드 범위: enter_vhe
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 화살표는 값의 변화를 뜻합니다. 주소값을 옮겨도 그 주소의 메모리 내용은 복사하지 않습니다.
- mov_q는 큰 상수를 여러 MOVZ/MOVN·MOVK 명령으로 구성할 수 있습니다. 여기서는 매크로 전체 결과를 표시합니다.
mov_q 매크로 본문 · 552행
arch/arm64/include/asm/assembler.h · Linux v6.18.37
정의 선택 조건: #ifndef __ASM_ASSEMBLER_H
.macro mov_q, reg, val
.if (((\val) >> 31) == 0 || ((\val) >> 31) == 0x1ffffffff)
movz \reg, :abs_g1_s:\val
.else
.if (((\val) >> 47) == 0 || ((\val) >> 47) == 0x1ffff)
movz \reg, :abs_g2_s:\val
.else
movz \reg, :abs_g3:\val
movk \reg, :abs_g2_nc:\val
.endif
movk \reg, :abs_g1_nc:\val
.endif
movk \reg, :abs_g0_nc:\val
.endm msr_s SYS_SCTLR_EL12, x0EL12 별칭을 통해 기존 EL1의 SCTLR에 MMU-off 값을 기록합니다. 방금 켠 EL2 호스트 MMU를 끄는 줄이 아닙니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.
SYS_SCTLR_EL12는 원래 EL1의 제어 레지스터를 지정합니다.
179행 그림 · 시스템 레지스터 쓰기
함수·코드 범위: enter_vhe
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 접근 권한과 VHE 별칭 규칙에 따라 실제 대상이 정해집니다. 뒤의 ISB·DSB가 필요한지는 이 레지스터와 문맥에 따릅니다.
msr_s 매크로 본문 · 1140행
arch/arm64/include/asm/sysreg.h · Linux v6.18.37
정의 선택 조건: #ifndef __ASM_SYSREG_H / #ifdef __ASSEMBLY__
.macro msr_s, sreg, rt
__emit_inst(0xd5000000|(\sreg)|(.L__gpr_num_\rt))
.endm mov x0, xzr전환 성공을 나타내는 0을 반환 레지스터 x0에 넣습니다. MOV는 x0의 기존 값을 대체합니다. 원본이 주소값이어도 그 주소의 메모리를 읽거나 복사하지 않습니다.
181행 그림 · 값 복사
함수·코드 범위: enter_vhe
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 화살표는 값의 변화를 뜻합니다. 주소값을 옮겨도 그 주소의 메모리 내용은 복사하지 않습니다.
eretHVC 이후 코드로 예외 복귀합니다. 미리 SPSR의 모드를 바꿨으므로 커널은 EL2h/VHE 상태에서 같은 흐름을 이어갑니다.
183행 그림 · 저장된 실행 상태로 복귀
함수·코드 범위: enter_vhe
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- ELx는 현재 예외 레벨입니다. SPSR의 모드에 따라 복귀 레벨과 스택 선택도 바뀝니다. 일반 레지스터 복구는 앞의 load 명령들이 담당합니다.
SYM_CODE_END(enter_vhe)도구에 이 코드 범위의 끝을 알려 크기와 심볼 정보를 기록합니다. CPU가 이 줄 때문에 자동으로 반환하지는 않습니다.
184행 그림 · 어셈블·링크 단계
함수·코드 범위: enter_vhe
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 끝 표기 자체가 RET나 분기를 실행하지는 않습니다.
.popsectionMMU를 켜는 idmap 구간의 배치를 끝내고 앞의 .hyp.text로 돌아갑니다. 뒤의 invalid 예외 처리 코드도 hyp 영역에 둡니다. 이는 빌드 중 출력할 바이트의 섹션을 선택하는 지시문이며, 실행 중 PC나 SP를 옮기지 않습니다.
186행 그림 · 어셈블·링크 단계
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
.macro invalid_vector label이 최소 stub이 처리하지 않는 예외마다 자기 자신으로 분기하는 정지 지점을 만드는 매크로입니다. 알 수 없는 예외를 정상 처리한 것처럼 반환하지 않습니다. 이 줄은 어셈블러 매크로 정의의 시작입니다. 본문은 매크로를 사용하는 위치에 펼쳐지며, 정의 자체가 런타임 함수 호출은 아닙니다.
188행 그림 · 어셈블·링크 단계
매크로 본문: invalid_vector
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 뒤의 본문은 이 이름을 사용하는 위치에 펼쳐집니다. 정의 위치를 CPU가 함수처럼 호출하지 않습니다.
SYM_CODE_START_LOCAL(\label)매크로에 전달한 예외 라벨 이름으로 파일 내부용 코드 심볼을 만듭니다. 여러 invalid_vector 사용이 각각 별도 정지 루틴을 생성하며 아래 자기 분기가 실제 동작입니다.
189행 그림 · 어셈블·링크 단계
매크로 본문: invalid_vector
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
b \label해당 예외 라벨로 계속 분기하여 진행을 멈춥니다. 장치 IRQ를 처리하거나 오류에서 복구하는 루틴은 아닙니다. 이 분기는 LR에 새 복귀 주소를 기록하지 않습니다.
190행 그림 · 호출과 실행 위치
매크로 본문: invalid_vector
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 새 복귀 주소를 LR에 기록하지 않습니다. RET 역시 메모리의 스택 값을 자동으로 꺼내는 명령은 아닙니다.
- 매크로 invalid_vector의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
SYM_CODE_END(\label)도구에 이 코드 범위의 끝을 알려 크기와 심볼 정보를 기록합니다. CPU가 이 줄 때문에 자동으로 반환하지는 않습니다.
191행 그림 · 어셈블·링크 단계
매크로 본문: invalid_vector
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 끝 표기 자체가 RET나 분기를 실행하지는 않습니다.
.endm공통 작업의 정의를 여기서 마칩니다. RET가 아니라 빌드 도구가 읽는 경계입니다.
192행 그림 · 어셈블·링크 단계
매크로 본문: invalid_vector
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 끝 표기 자체가 RET나 분기를 실행하지는 않습니다.
invalid_vector el2_sync_invalid지원하지 않는 EL2 동기 예외가 들어왔을 때 사용할 자기 분기 루틴을 만듭니다. 위 벡터 슬롯에서 이 라벨로 연결된 경우 더 진행하지 않습니다.
194행 그림 · 벡터·핸들러 코드 생성
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 이 한 줄은 여러 명령을 생성합니다. 레지스터 변화는 펼쳐진 본문의 각 명령과 선택된 진입 경로에 따라 생깁니다.
invalid_vector 매크로 본문 · 188행
arch/arm64/kernel/hyp-stub.S · Linux v6.18.37
.macro invalid_vector label
SYM_CODE_START_LOCAL(\label)
b \label
SYM_CODE_END(\label)
.endm invalid_vector el2_irq_invalid지원하지 않는 EL2 IRQ 예외가 들어왔을 때 사용할 자기 분기 루틴을 만듭니다. 위 벡터 슬롯에서 이 라벨로 연결된 경우 더 진행하지 않습니다.
195행 그림 · 벡터·핸들러 코드 생성
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 이 한 줄은 여러 명령을 생성합니다. 레지스터 변화는 펼쳐진 본문의 각 명령과 선택된 진입 경로에 따라 생깁니다.
invalid_vector 매크로 본문 · 188행
arch/arm64/kernel/hyp-stub.S · Linux v6.18.37
.macro invalid_vector label
SYM_CODE_START_LOCAL(\label)
b \label
SYM_CODE_END(\label)
.endm invalid_vector el2_fiq_invalid지원하지 않는 EL2 FIQ 예외가 들어왔을 때 사용할 자기 분기 루틴을 만듭니다. 위 벡터 슬롯에서 이 라벨로 연결된 경우 더 진행하지 않습니다.
196행 그림 · 벡터·핸들러 코드 생성
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 이 한 줄은 여러 명령을 생성합니다. 레지스터 변화는 펼쳐진 본문의 각 명령과 선택된 진입 경로에 따라 생깁니다.
invalid_vector 매크로 본문 · 188행
arch/arm64/kernel/hyp-stub.S · Linux v6.18.37
.macro invalid_vector label
SYM_CODE_START_LOCAL(\label)
b \label
SYM_CODE_END(\label)
.endm invalid_vector el2_error_invalid지원하지 않는 EL2 SError 예외가 들어왔을 때 사용할 자기 분기 루틴을 만듭니다. 위 벡터 슬롯에서 이 라벨로 연결된 경우 더 진행하지 않습니다.
197행 그림 · 벡터·핸들러 코드 생성
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 이 한 줄은 여러 명령을 생성합니다. 레지스터 변화는 펼쳐진 본문의 각 명령과 선택된 진입 경로에 따라 생깁니다.
invalid_vector 매크로 본문 · 188행
arch/arm64/kernel/hyp-stub.S · Linux v6.18.37
.macro invalid_vector label
SYM_CODE_START_LOCAL(\label)
b \label
SYM_CODE_END(\label)
.endm invalid_vector el1_sync_invalid지원하지 않는 하위 EL 동기 예외가 들어왔을 때 사용할 자기 분기 루틴을 만듭니다. 위 벡터 슬롯에서 이 라벨로 연결된 경우 더 진행하지 않습니다.
198행 그림 · 벡터·핸들러 코드 생성
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 이 한 줄은 여러 명령을 생성합니다. 레지스터 변화는 펼쳐진 본문의 각 명령과 선택된 진입 경로에 따라 생깁니다.
invalid_vector 매크로 본문 · 188행
arch/arm64/kernel/hyp-stub.S · Linux v6.18.37
.macro invalid_vector label
SYM_CODE_START_LOCAL(\label)
b \label
SYM_CODE_END(\label)
.endm invalid_vector el1_irq_invalid지원하지 않는 하위 EL IRQ 예외가 들어왔을 때 사용할 자기 분기 루틴을 만듭니다. 위 벡터 슬롯에서 이 라벨로 연결된 경우 더 진행하지 않습니다.
199행 그림 · 벡터·핸들러 코드 생성
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 이 한 줄은 여러 명령을 생성합니다. 레지스터 변화는 펼쳐진 본문의 각 명령과 선택된 진입 경로에 따라 생깁니다.
invalid_vector 매크로 본문 · 188행
arch/arm64/kernel/hyp-stub.S · Linux v6.18.37
.macro invalid_vector label
SYM_CODE_START_LOCAL(\label)
b \label
SYM_CODE_END(\label)
.endm invalid_vector el1_fiq_invalid지원하지 않는 하위 EL FIQ 예외가 들어왔을 때 사용할 자기 분기 루틴을 만듭니다. 위 벡터 슬롯에서 이 라벨로 연결된 경우 더 진행하지 않습니다.
200행 그림 · 벡터·핸들러 코드 생성
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 이 한 줄은 여러 명령을 생성합니다. 레지스터 변화는 펼쳐진 본문의 각 명령과 선택된 진입 경로에 따라 생깁니다.
invalid_vector 매크로 본문 · 188행
arch/arm64/kernel/hyp-stub.S · Linux v6.18.37
.macro invalid_vector label
SYM_CODE_START_LOCAL(\label)
b \label
SYM_CODE_END(\label)
.endm invalid_vector el1_error_invalid지원하지 않는 하위 EL SError 예외가 들어왔을 때 사용할 자기 분기 루틴을 만듭니다. 위 벡터 슬롯에서 이 라벨로 연결된 경우 더 진행하지 않습니다.
201행 그림 · 벡터·핸들러 코드 생성
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 이 한 줄은 여러 명령을 생성합니다. 레지스터 변화는 펼쳐진 본문의 각 명령과 선택된 진입 경로에 따라 생깁니다.
invalid_vector 매크로 본문 · 188행
arch/arm64/kernel/hyp-stub.S · Linux v6.18.37
.macro invalid_vector label
SYM_CODE_START_LOCAL(\label)
b \label
SYM_CODE_END(\label)
.endm .popsectionEL2에서 실행할 .hyp.text 배치를 끝내고 일반 .text로 돌아갑니다. 이후 __hyp_set_vectors 등의 래퍼는 커널 측에서 HVC를 요청하는 코드입니다. 이는 빌드 중 출력할 바이트의 섹션을 선택하는 지시문이며, 실행 중 PC나 SP를 옮기지 않습니다.
203행 그림 · 어셈블·링크 단계
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
EL1에서 호출하는 래퍼
SYM_FUNC_START(__hyp_set_vectors)새 EL2 벡터 주소를 x0로 받아 HVC 서비스에 전달하는 C 호출용 래퍼입니다. 모든 CPU에 stub이 설치되어 있는지 확인한 뒤 각 CPU에서 호출해야 합니다. 이 선언은 함수 심볼과 정렬을 정하고, ARM64 정의에서는 간접 호출이 도착할 BTI C 명령도 진입부에 넣습니다. 여기서는 전역 심벌로 정의하므로 다른 오브젝트에서도 이 함수 이름을 참조할 수 있습니다. 모듈용 EXPORT_SYMBOL 등록은 별도입니다.
226행 그림 · 어셈블·링크 단계
함수·코드 범위: __hyp_set_vectors
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 함수 시작 매크로는 심벌·정렬 정보와 BTI 진입 명령 등을 배치할 수 있습니다. 생성된 명령의 실행과 심벌 선언을 구분합니다.
mov x1, x0호출자가 넘긴 새 벡터 주소를 서비스 인자 x1로 옮깁니다. x0는 다음 줄에서 요청 번호로 바뀝니다. MOV는 x1의 기존 값을 대체합니다. 원본이 주소값이어도 그 주소의 메모리를 읽거나 복사하지 않습니다.
227행 그림 · 값 복사
함수·코드 범위: __hyp_set_vectors
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 화살표는 값의 변화를 뜻합니다. 주소값을 옮겨도 그 주소의 메모리 내용은 복사하지 않습니다.
mov x0, #HVC_SET_VECTORSx0에 HVC_SET_VECTORS를 넣어 벡터 교체 작업을 선택합니다. MOV는 x0의 기존 값을 대체합니다. 원본이 주소값이어도 그 주소의 메모리를 읽거나 복사하지 않습니다.
228행 그림 · 값 복사
함수·코드 범위: __hyp_set_vectors
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 화살표는 값의 변화를 뜻합니다. 주소값을 옮겨도 그 주소의 메모리 내용은 복사하지 않습니다.
hvc #0현재 EL2 벡터의 처리기에 벡터 교체를 요청합니다. hyp stub은 x1의 주소를 VBAR_EL2에 기록하고 결과를 x0로 돌려줍니다. 일반 함수 호출처럼 BL로 이동하는 대신 동기 예외를 통해 요청을 전달합니다.
229행 그림 · 예외 처리기로 제어 전달
함수·코드 범위: __hyp_set_vectors
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 허용된 정상 예외 진입을 나타냅니다. 사용이 금지되거나 해당 EL이 없으면 예외 결과가 달라집니다. x0 등의 요청 인자는 처리기가 규약에 따라 읽으며 HVC/SMC 자체가 메모리로 복사하지는 않습니다.
- 서비스를 처리한 뒤의 반환 레지스터는 서비스 규약에 따릅니다. 여기서는 진입까지만 표시합니다.
retEL2 요청 처리가 끝나면 원래 C 호출자로 돌아갑니다. 피연산자가 없는 RET는 LR(x30)에 들어 있는 주소에서 실행을 이어갑니다.
230행 그림 · 호출과 실행 위치
함수·코드 범위: __hyp_set_vectors
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 새 복귀 주소를 LR에 기록하지 않습니다. RET 역시 메모리의 스택 값을 자동으로 꺼내는 명령은 아닙니다.
SYM_FUNC_END(__hyp_set_vectors)도구에 이 코드 범위의 끝을 알려 크기와 심볼 정보를 기록합니다. CPU가 이 줄 때문에 자동으로 반환하지는 않습니다.
231행 그림 · 어셈블·링크 단계
함수·코드 범위: __hyp_set_vectors
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 끝 표기 자체가 RET나 분기를 실행하지는 않습니다.
SYM_FUNC_START(__hyp_reset_vectors)EL2 벡터를 원래 hyp stub으로 돌려놓는 요청의 C 호출용 래퍼입니다. 현재 EL2에 설치된 처리기가 이 요청을 해석합니다. 이 선언은 함수 심볼과 정렬을 정하고, ARM64 정의에서는 간접 호출이 도착할 BTI C 명령도 진입부에 넣습니다. 여기서는 전역 심벌로 정의하므로 다른 오브젝트에서도 이 함수 이름을 참조할 수 있습니다. 모듈용 EXPORT_SYMBOL 등록은 별도입니다.
233행 그림 · 어셈블·링크 단계
함수·코드 범위: __hyp_reset_vectors
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 함수 시작 매크로는 심벌·정렬 정보와 BTI 진입 명령 등을 배치할 수 있습니다. 생성된 명령의 실행과 심벌 선언을 구분합니다.
mov x0, #HVC_RESET_VECTORSHVC_RESET_VECTORS 번호를 x0에 준비합니다. 이미 stub이면 이 번호에 대해 성공만 반환하고, KVM 벡터라면 원래 stub으로 되돌리는 처리가 필요합니다. MOV는 x0의 기존 값을 대체합니다. 원본이 주소값이어도 그 주소의 메모리를 읽거나 복사하지 않습니다.
234행 그림 · 값 복사
함수·코드 범위: __hyp_reset_vectors
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 화살표는 값의 변화를 뜻합니다. 주소값을 옮겨도 그 주소의 메모리 내용은 복사하지 않습니다.
hvc #0EL2에 벡터 초기화 요청을 보냅니다. #0은 HVC 명령의 즉시값이며 실제 요청 종류는 x0에 들어 있습니다. 일반 함수 호출처럼 BL로 이동하는 대신 동기 예외를 통해 요청을 전달합니다.
235행 그림 · 예외 처리기로 제어 전달
함수·코드 범위: __hyp_reset_vectors
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 허용된 정상 예외 진입을 나타냅니다. 사용이 금지되거나 해당 EL이 없으면 예외 결과가 달라집니다. x0 등의 요청 인자는 처리기가 규약에 따라 읽으며 HVC/SMC 자체가 메모리로 복사하지는 않습니다.
- 서비스를 처리한 뒤의 반환 레지스터는 서비스 규약에 따릅니다. 여기서는 진입까지만 표시합니다.
ret벡터 초기화 요청이 끝나면 C 호출자로 돌아갑니다. 피연산자가 없는 RET는 LR(x30)에 들어 있는 주소에서 실행을 이어갑니다.
236행 그림 · 호출과 실행 위치
함수·코드 범위: __hyp_reset_vectors
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 새 복귀 주소를 LR에 기록하지 않습니다. RET 역시 메모리의 스택 값을 자동으로 꺼내는 명령은 아닙니다.
SYM_FUNC_END(__hyp_reset_vectors)도구에 이 코드 범위의 끝을 알려 크기와 심볼 정보를 기록합니다. CPU가 이 줄 때문에 자동으로 반환하지는 않습니다.
237행 그림 · 어셈블·링크 단계
함수·코드 범위: __hyp_reset_vectors
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 끝 표기 자체가 RET나 분기를 실행하지는 않습니다.
SYM_FUNC_START(finalise_el2)init_kernel_el이 돌려준 출발 EL을 w0로 받아 EL2 후속 설정을 요청하는 래퍼입니다. 출발한 EL과 현재 실행하는 EL은 서로 다를 수 있으므로 둘 다 검사합니다. 이 선언은 함수 심볼과 정렬을 정하고, ARM64 정의에서는 간접 호출이 도착할 BTI C 명령도 진입부에 넣습니다. 여기서는 전역 심벌로 정의하므로 다른 오브젝트에서도 이 함수 이름을 참조할 수 있습니다. 모듈용 EXPORT_SYMBOL 등록은 별도입니다.
244행 그림 · 어셈블·링크 단계
함수·코드 범위: finalise_el2
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 함수 시작 매크로는 심벌·정렬 정보와 BTI 진입 명령 등을 배치할 수 있습니다. 생성된 명령의 실행과 심벌 선언을 구분합니다.
cmp w0, #BOOT_CPU_MODE_EL2출발 모드가 BOOT_CPU_MODE_EL2인 0xe12인지 비교합니다. w0만 비교하므로 x0의 상위 비트에 함께 전달된 출발 상태 플래그와 구분합니다. CMP는 두 피연산자의 뺄셈 결과로 NZCV 조건 플래그만 갱신하고 원래 레지스터 값은 유지합니다.
246행 그림 · 조건 플래그 계산
함수·코드 범위: finalise_el2
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 일반 레지스터에 연산 결과를 쓰지 않고 조건 플래그만 바꿉니다.
- N은 결과의 부호 비트, Z는 결과가 0인지 나타냅니다. C는 빌림이 없으면 1, V는 부호 있는 뺄셈의 오버플로를 나타냅니다.
b.ne 1fEL2에서 출발하지 않았다면 EL2를 직접 소유한다고 가정할 수 없으므로 HVC 요청 없이 돌아갑니다. 앞서 계산한 NZCV 중 Z=0 조건으로 분기합니다. 이 줄은 두 값을 새로 비교하지 않으며 조건이 맞지 않으면 다음 명령으로 진행합니다.
247행 그림 · 조건별 실행 위치
함수·코드 범위: finalise_el2
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 조건이 맞는 한 경로만 실행합니다. 이 분기 명령은 NZCV를 새로 계산하지 않습니다.
mrs x0, CurrentELCurrentEL을 읽어 지금 실제로 실행 중인 예외 수준을 확인합니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.
250행 그림 · 시스템 레지스터 읽기
함수·코드 범위: finalise_el2
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 일반 RAM의 메모리 접근과 구분합니다.
cmp x0, #CurrentEL_EL1현재 EL이 EL1인지 비교합니다. EL2 출발 CPU도 앞선 초기화에서 EL1로 내려와 있을 수 있습니다. CMP는 두 피연산자의 뺄셈 결과로 NZCV 조건 플래그만 갱신하고 원래 레지스터 값은 유지합니다.
251행 그림 · 조건 플래그 계산
함수·코드 범위: finalise_el2
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 일반 레지스터에 연산 결과를 쓰지 않고 조건 플래그만 바꿉니다.
- N은 결과의 부호 비트, Z는 결과가 0인지 나타냅니다. C는 빌림이 없으면 1, V는 부호 있는 뺄셈의 오버플로를 나타냅니다.
b.ne 1f현재 EL1이 아니면 전환 요청을 생략합니다. 이 경로는 EL1에서 HVC를 통해 EL2 설정을 마무리하도록 마련되어 있습니다. 앞서 계산한 NZCV 중 Z=0 조건으로 분기합니다. 이 줄은 두 값을 새로 비교하지 않으며 조건이 맞지 않으면 다음 명령으로 진행합니다.
252행 그림 · 조건별 실행 위치
함수·코드 범위: finalise_el2
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 조건이 맞는 한 경로만 실행합니다. 이 분기 명령은 NZCV를 새로 계산하지 않습니다.
mov x0, #HVC_FINALISE_EL2조건을 모두 만족했으므로 HVC_FINALISE_EL2 번호를 준비합니다. MOV는 x0의 기존 값을 대체합니다. 원본이 주소값이어도 그 주소의 메모리를 읽거나 복사하지 않습니다.
254행 그림 · 값 복사
함수·코드 범위: finalise_el2
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 화살표는 값의 변화를 뜻합니다. 주소값을 옮겨도 그 주소의 메모리 내용은 복사하지 않습니다.
hvc #0EL2 기능 설정과 가능한 VHE 전환을 요청합니다. VHE 허용 조건을 만족하면 같은 커널 코드가 EL2h로 이어지고, 만족하지 않으면 전환하지 않은 상태로 돌아옵니다. 일반 함수 호출처럼 BL로 이동하는 대신 동기 예외를 통해 요청을 전달합니다.
255행 그림 · 예외 처리기로 제어 전달
함수·코드 범위: finalise_el2
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 허용된 정상 예외 진입을 나타냅니다. 사용이 금지되거나 해당 EL이 없으면 예외 결과가 달라집니다. x0 등의 요청 인자는 처리기가 규약에 따라 읽으며 HVC/SMC 자체가 메모리로 복사하지는 않습니다.
- 서비스를 처리한 뒤의 반환 레지스터는 서비스 규약에 따릅니다. 여기서는 진입까지만 표시합니다.
1:EL2에서 출발하지 않았거나 현재 EL1이 아닌 경우, 또는 HVC 요청을 마친 경우가 합류하는 복귀 위치입니다. 레이블은 이 위치에 붙인 이름이며, 이름을 적는 것만으로 CPU 명령이 추가되지는 않습니다.
256행 그림 · 위치에 이름 붙이기
함수·코드 범위: finalise_el2
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
ret필요한 설정 요청을 마친 뒤 호출자에게 돌아갑니다. EL2 출발 여부만으로 무조건 VHE가 선택된다고 해석하면 안 됩니다. 피연산자가 없는 RET는 LR(x30)에 들어 있는 주소에서 실행을 이어갑니다.
257행 그림 · 호출과 실행 위치
함수·코드 범위: finalise_el2
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 새 복귀 주소를 LR에 기록하지 않습니다. RET 역시 메모리의 스택 값을 자동으로 꺼내는 명령은 아닙니다.
SYM_FUNC_END(finalise_el2)도구에 이 코드 범위의 끝을 알려 크기와 심볼 정보를 기록합니다. CPU가 이 줄 때문에 자동으로 반환하지는 않습니다.
258행 그림 · 어셈블·링크 단계
함수·코드 범위: finalise_el2
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 끝 표기 자체가 RET나 분기를 실행하지는 않습니다.
설명한 뒤 함께 생각해 볼 질문
EL2에 들어가면 바로 VHE인가요?
아닙니다. 기능 지원, 현재 MMU 상태, override 정책을 확인한 뒤 전환합니다.
알 수 없는 HVC 요청은 어떻게 되나요?
HVC_STUB_ERR를 x0에 넣어 ERET합니다. 지원하지 않는 벡터의 무한 분기와는 다른 경로입니다.
EL2 전환 때 스택도 바꾸는 이유는 무엇인가요?
커널이 쓰던 유효한 EL1 스택을 host EL2에서도 이어서 사용하려고 상태를 옮깁니다.
주석을 포함한 전체 원문
1/* SPDX-License-Identifier: GPL-2.0-only */2/*3* Hypervisor stub4*5* Copyright (C) 2012 ARM Ltd.6* Author: Marc Zyngier <marc.zyngier@arm.com>7*/89#include <linux/init.h>10#include <linux/linkage.h>1112#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>1819.text20.pushsection .hyp.text, "ax"2122.align 112324SYM_CODE_START(__hyp_stub_vectors)25ventry el2_sync_invalid // Synchronous EL2t26ventry el2_irq_invalid // IRQ EL2t27ventry el2_fiq_invalid // FIQ EL2t28ventry el2_error_invalid // Error EL2t2930ventry elx_sync // Synchronous EL2h31ventry el2_irq_invalid // IRQ EL2h32ventry el2_fiq_invalid // FIQ EL2h33ventry el2_error_invalid // Error EL2h3435ventry elx_sync // Synchronous 64-bit EL136ventry el1_irq_invalid // IRQ 64-bit EL137ventry el1_fiq_invalid // FIQ 64-bit EL138ventry el1_error_invalid // Error 64-bit EL13940ventry el1_sync_invalid // Synchronous 32-bit EL141ventry el1_irq_invalid // IRQ 32-bit EL142ventry el1_fiq_invalid // FIQ 32-bit EL143ventry el1_error_invalid // Error 32-bit EL144SYM_CODE_END(__hyp_stub_vectors)4546.align 114748SYM_CODE_START_LOCAL(elx_sync)49cmp x0, #HVC_SET_VECTORS50b.ne 1f51msr vbar_el2, x152b 9f53541: cmp x0, #HVC_FINALISE_EL255b.eq __finalise_el256572: cmp x0, #HVC_SOFT_RESTART58b.ne 3f59mov x0, x260mov x2, x461mov x4, x162mov x1, x363br x4 // no return64653: cmp x0, #HVC_RESET_VECTORS66beq 9f // Nothing to reset!6768/* Someone called kvm_call_hyp() against the hyp-stub... */69mov_q x0, HVC_STUB_ERR70eret71729: mov x0, xzr73eret74SYM_CODE_END(elx_sync)7576SYM_CODE_START_LOCAL(__finalise_el2)77finalise_el2_state7879// nVHE? No way! Give me the real thing!80// Sanity check: MMU *must* be off81mrs x1, sctlr_el282tbnz x1, #0, 1f8384// Needs to be VHE capable, obviously85check_override id_aa64mmfr1 ID_AA64MMFR1_EL1_VH_SHIFT 0f 1f x1 x286870: // Check whether we only want the hypervisor to run VHE, not the kernel88adr_l x1, arm64_sw_feature_override89ldr x2, [x1, FTR_OVR_VAL_OFFSET]90ldr x1, [x1, FTR_OVR_MASK_OFFSET]91and x2, x2, x192ubfx x2, x2, #ARM64_SW_FEATURE_OVERRIDE_HVHE, #493cbz x2, 2f94951: mov_q x0, HVC_STUB_ERR96eret972:98// Engage the VHE magic!99mov_q x0, HCR_HOST_VHE_FLAGS100msr_hcr_el2 x0101isb102103// Use the EL1 allocated stack, per-cpu offset104mrs x0, sp_el1105mov sp, x0106mrs x0, tpidr_el1107msr tpidr_el2, x0108109// FP configuration, vectors110mrs_s x0, SYS_CPACR_EL12111msr cpacr_el1, x0112mrs_s x0, SYS_VBAR_EL12113msr vbar_el1, x0114115// Use EL2 translations for SPE & TRBE and disable access from EL1116mrs x0, mdcr_el2117bic x0, x0, #MDCR_EL2_E2PB_MASK118bic x0, x0, #MDCR_EL2_E2TB_MASK119msr mdcr_el2, x0120121// Transfer the MM state from EL1 to EL2122mrs_s x0, SYS_TCR_EL12123msr tcr_el1, x0124mrs_s x0, SYS_TTBR0_EL12125msr ttbr0_el1, x0126mrs_s x0, SYS_TTBR1_EL12127msr ttbr1_el1, x0128mrs_s x0, SYS_MAIR_EL12129msr mair_el1, x0130mrs x1, REG_ID_AA64MMFR3_EL1131ubfx x1, x1, #ID_AA64MMFR3_EL1_TCRX_SHIFT, #4132cbz x1, .Lskip_tcr2133mrs x0, REG_TCR2_EL12134msr REG_TCR2_EL1, x0135136// Transfer permission indirection state137mrs x1, REG_ID_AA64MMFR3_EL1138ubfx x1, x1, #ID_AA64MMFR3_EL1_S1PIE_SHIFT, #4139cbz x1, .Lskip_indirection140mrs x0, REG_PIRE0_EL12141msr REG_PIRE0_EL1, x0142mrs x0, REG_PIR_EL12143msr REG_PIR_EL1, x0144145.Lskip_indirection:146.Lskip_tcr2:147148isb149150// Hack the exception return to stay at EL2151mrs x0, spsr_el1152and x0, x0, #~PSR_MODE_MASK153mov x1, #PSR_MODE_EL2h154orr x0, x0, x1155msr spsr_el1, x0156157b enter_vhe158SYM_CODE_END(__finalise_el2)159160// At the point where we reach enter_vhe(), we run with161// the MMU off (which is enforced by __finalise_el2()).162// We thus need to be in the idmap, or everything will163// explode when enabling the MMU.164165.pushsection .idmap.text, "ax"166167SYM_CODE_START_LOCAL(enter_vhe)168// Invalidate TLBs before enabling the MMU169tlbi vmalle1170dsb nsh171isb172173// Enable the EL2 S1 MMU, as set up from EL1174mrs_s x0, SYS_SCTLR_EL12175set_sctlr_el1 x0176177// Disable the EL1 S1 MMU for a good measure178mov_q x0, INIT_SCTLR_EL1_MMU_OFF179msr_s SYS_SCTLR_EL12, x0180181mov x0, xzr182183eret184SYM_CODE_END(enter_vhe)185186.popsection187188.macro invalid_vector label189SYM_CODE_START_LOCAL(\label)190b \label191SYM_CODE_END(\label)192.endm193194invalid_vector el2_sync_invalid195invalid_vector el2_irq_invalid196invalid_vector el2_fiq_invalid197invalid_vector el2_error_invalid198invalid_vector el1_sync_invalid199invalid_vector el1_irq_invalid200invalid_vector el1_fiq_invalid201invalid_vector el1_error_invalid202203.popsection204205/*206* __hyp_set_vectors: Call this after boot to set the initial hypervisor207* vectors as part of hypervisor installation. On an SMP system, this should208* be called on each CPU.209*210* x0 must be the physical address of the new vector table, and must be211* 2KB aligned.212*213* Before calling this, you must check that the stub hypervisor is installed214* everywhere, by waiting for any secondary CPUs to be brought up and then215* checking that is_hyp_mode_available() is true.216*217* If not, there is a pre-existing hypervisor, some CPUs failed to boot, or218* something else went wrong... in such cases, trying to install a new219* 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's223* initialisation entry point.224*/225226SYM_FUNC_START(__hyp_set_vectors)227mov x1, x0228mov x0, #HVC_SET_VECTORS229hvc #0230ret231SYM_FUNC_END(__hyp_set_vectors)232233SYM_FUNC_START(__hyp_reset_vectors)234mov x0, #HVC_RESET_VECTORS235hvc #0236ret237SYM_FUNC_END(__hyp_reset_vectors)238239/*240* Entry point to finalise EL2 and switch to VHE if deemed capable241*242* w0: boot mode, as returned by init_kernel_el()243*/244SYM_FUNC_START(finalise_el2)245// Need to have booted at EL2246cmp w0, #BOOT_CPU_MODE_EL2247b.ne 1f248249// and still be at EL1250mrs x0, CurrentEL251cmp x0, #CurrentEL_EL1252b.ne 1f253254mov x0, #HVC_FINALISE_EL2255hvc #02561:257ret258SYM_FUNC_END(finalise_el2)

