DUJINLABS.COM

Linux v6.18.37 · ARM64

entry.S — 말로 풀어 읽기

이 파일은 왜 필요한가요?

예외는 실행 중인 프로그램을 중간에 멈추고 다른 코드를 실행해야 하는 상황입니다. CPU는 복귀할 PC와 실행 상태를 일부 시스템 레지스터에 남기지만, 프로그램이 사용하던 모든 일반 레지스터를 대신 저장해 주지는 않습니다. entry.S는 그 나머지를 맡습니다. 나중에 마치 중단되지 않았던 것처럼 돌아가려면 무엇을 어디에 보관해야 하는지가 이 파일의 중심 질문입니다.

명령·지시문·라벨을 원본 줄 번호에 맞춰 설명합니다. 주석과 빈 줄을 포함한 전체 원문은 아래에 따로 두었습니다. 매크로 정의와 조건부 코드는 모두 차례대로 실행되는 명령 목록이 아닙니다.

기존 코드 분석과 함께 읽기 · 원본 파일 · 설명 원고

먼저 알아둘 개념: 예외 레벨과 복귀, 레지스터와 값, SP와 스택 공간, 스택 프레임과 복귀 기록, 비교 결과와 조건 분기, 가상 주소와 MMU, 매크로와 실제 CPU 명령

처음 읽을 때

한 줄의 STP를 볼 때는 “예외 처리 코드가 이 두 레지스터를 쓰기 전에 원래 값을 메모리에 보관한다”고 읽으세요. 복귀의 LDP는 같은 기록을 다시 꺼내는 단계입니다.

더 깊이 살펴볼 때

예외 프레임 보존과 태스크 문맥 전환은 저장 범위가 다릅니다. 하드웨어가 저장하는 상태, ABI가 보존을 요구하는 상태, Linux가 추가하는 메타데이터를 나누어 보세요. KPTI에서는 매핑을 지운 뒤에도 실행 가능한 최소 코드가 필요합니다.

한 줄씩 레지스터와 메모리 상태 보기

각 코드 아래의 ‘행 그림’을 펼치면, 그 줄에서 읽는 값과 실행 전·후의 상태를 나란히 볼 수 있습니다. 여기서 ‘명령 직전’은 지금 보고 있는 명령이 실행되기 바로 전을 뜻합니다. 함수 진입 때나 소스의 바로 윗줄을 가리키는 말은 아닙니다. 앞서 실행한 명령이 값을 바꿨다면 그 결과가 들어 있습니다.

소스로 정해지는 상수, 호출자가 넘기는 값, 실제 실행에서 정해지는 주소를 구분해서 읽어야 합니다. 주소 숫자를 몰라도 무엇을 가리키는지와 어디서 받아 오는지는 설명할 수 있습니다. ‘명령 직전 x0의 값’처럼 일반 표기로 남은 곳은 한 명령의 동작을 나타내며, 특정 호출 경로의 값까지 확인했다는 뜻은 아닙니다.

일반 명령은 그 한 줄의 효과를, 매크로는 여러 명령을 묶은 주요 결과를 표시합니다. 함수 호출은 호출 직후의 PC와 LR까지만 나타냅니다. 메모리 접근은 정상적으로 끝나는 경우를 가정합니다. 생략된 항목까지 모두 보존된다는 뜻은 아닙니다.

화살표 →는 같은 항목의 전후 비교입니다. 메모리 칸에는 주소와 크기를 따로 적었습니다. SP가 줄어들면 낮은 주소 쪽을 사용하며, 화면의 위아래와 메모리 주소 방향은 별개입니다. 전처리·어셈블·링크 지시문은 CPU 실행과 구분합니다.

‘이전·다음 행’은 소스에 적힌 순서입니다. 분기·반복·호출이 있으므로 파일 위에서 아래로 실제 실행된다고 가정하지 않습니다. 값이 여러 줄에 걸쳐 이어지는 모습은 cpu-reset.S의 단계별 예제에서 보실 수 있습니다.

총 730개 줄에 그림을 붙였습니다. 기존 코드와 설명은 그 아래에서 이어집니다.

먼저 그림으로 살펴보기

예외 진입·복귀와 태스크·스택 전환
화살표: 처리 흐름입니다. 조건이 적힌 단계에서는 해당 조건에 따라 경로가 갈립니다.
kernel_entry의 pt_regs 저장 위치
세로 배치는 낮은 오프셋에서 높은 오프셋 순입니다. 실행 순서와 SP의 이동 방향을 나타내지 않습니다.

헤더와 범용 레지스터 초기화

원본 1–38행

#include <linux/arm-smccc.h>

SMC/HVC로 호출하는 펌웨어 서비스의 함수 번호와 규약을 가져옵니다. 이 파일의 Spectre 완화 요청은 이 번호를 w0에 넣어 펌웨어가 서비스를 구분하도록 합니다. #include는 전처리 단계에서 헤더 내용을 가져옵니다. CPU가 이 줄에서 함수를 호출하는 동작은 없습니다.

10행 그림 · 전처리 단계
소스
처리 전전처리 전
이 줄의 효과#include <linux/arm-smccc.h>
CPU 레지스터
처리 전해당 없음
이 줄의 효과실행되는 CPU 명령이 없습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

#include <linux/init.h>

부팅 때만 사용할 코드·데이터의 섹션 표기를 가져옵니다. 초기화가 끝난 뒤 해제 가능한 영역과 계속 남겨야 하는 영역을 링커가 구분하게 합니다. #include는 전처리 단계에서 헤더 내용을 가져옵니다. CPU가 이 줄에서 함수를 호출하는 동작은 없습니다.

11행 그림 · 전처리 단계
소스
처리 전전처리 전
이 줄의 효과#include <linux/init.h>
CPU 레지스터
처리 전해당 없음
이 줄의 효과실행되는 CPU 명령이 없습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

#include <linux/linkage.h>

SYM_FUNC_START·SYM_CODE_START·SYM_INNER_LABEL 등의 심볼 선언 규칙을 가져옵니다. 함수인지 코드 내부 라벨인지, 다른 오브젝트에 보일 이름인지와 정렬·크기 정보를 빌드 도구에 전달하는 데 필요합니다. #include는 전처리 단계에서 헤더 내용을 가져옵니다. CPU가 이 줄에서 함수를 호출하는 동작은 없습니다.

12행 그림 · 전처리 단계
소스
처리 전전처리 전
이 줄의 효과#include <linux/linkage.h>
CPU 레지스터
처리 전해당 없음
이 줄의 효과실행되는 CPU 명령이 없습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

#include <asm/alternative.h>

CPU 기능에 따라 같은 위치의 명령을 부팅 시 교체하는 alternatives 문법을 가져옵니다. CONFIG의 빌드 선택과 달리 실제 CPU에 맞는 코드가 남도록 하는 장치입니다. #include는 전처리 단계에서 헤더 내용을 가져옵니다. CPU가 이 줄에서 함수를 호출하는 동작은 없습니다.

14행 그림 · 전처리 단계
소스
처리 전전처리 전
이 줄의 효과#include <asm/alternative.h>
CPU 레지스터
처리 전해당 없음
이 줄의 효과실행되는 CPU 명령이 없습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

#include <asm/assembler.h>

ARM64의 큰 주소 구성, CPU별 값 조회, 시스템 레지스터 처리, 동기화 등에 쓰는 어셈블리 매크로를 가져옵니다. 각 사용 위치에서 일반 명령 여러 개로 펼쳐지는 작업입니다. #include는 전처리 단계에서 헤더 내용을 가져옵니다. CPU가 이 줄에서 함수를 호출하는 동작은 없습니다.

15행 그림 · 전처리 단계
소스
처리 전전처리 전
이 줄의 효과#include <asm/assembler.h>
CPU 레지스터
처리 전해당 없음
이 줄의 효과실행되는 CPU 명령이 없습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

#include <asm/asm-offsets.h>

C 구조체에서 계산한 필드 오프셋을 가져옵니다. 어셈블리가 task_struct·pt_regs 등에서 값을 읽고 쓸 때 C 빌드의 실제 배치와 일치하도록 생성하는 헤더입니다. #include는 전처리 단계에서 헤더 내용을 가져옵니다. CPU가 이 줄에서 함수를 호출하는 동작은 없습니다.

16행 그림 · 전처리 단계
소스
처리 전전처리 전
이 줄의 효과#include <asm/asm-offsets.h>
CPU 레지스터
처리 전해당 없음
이 줄의 효과실행되는 CPU 명령이 없습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

#include <asm/asm_pointer_auth.h>

PAC 키 설치 매크로를 가져옵니다. 사용자·커널 또는 task 사이를 전환할 때 instruction-address 인증 키가 해당 실행 문맥과 맞아야 하므로 필요합니다. #include는 전처리 단계에서 헤더 내용을 가져옵니다. CPU가 이 줄에서 함수를 호출하는 동작은 없습니다.

17행 그림 · 전처리 단계
소스
처리 전전처리 전
이 줄의 효과#include <asm/asm_pointer_auth.h>
CPU 레지스터
처리 전해당 없음
이 줄의 효과실행되는 CPU 명령이 없습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

#include <asm/bug.h>

복구를 기대하지 않는 커널 오류를 BRK와 BUG 메타데이터로 표현하는 규칙을 가져옵니다. 돌아오지 않아야 할 초기화 함수가 반환한 경우 등의 오류 경로에서 사용합니다. #include는 전처리 단계에서 헤더 내용을 가져옵니다. CPU가 이 줄에서 함수를 호출하는 동작은 없습니다.

18행 그림 · 전처리 단계
소스
처리 전전처리 전
이 줄의 효과#include <asm/bug.h>
CPU 레지스터
처리 전해당 없음
이 줄의 효과실행되는 CPU 명령이 없습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

#include <asm/cpufeature.h>

CPU 기능과 erratum capability 이름을 가져옵니다. alternatives가 MTE·PAC·PAN·CPU 우회 코드 중 어느 명령열을 사용할지 선택하는 기준입니다. #include는 전처리 단계에서 헤더 내용을 가져옵니다. CPU가 이 줄에서 함수를 호출하는 동작은 없습니다.

19행 그림 · 전처리 단계
소스
처리 전전처리 전
이 줄의 효과#include <asm/cpufeature.h>
CPU 레지스터
처리 전해당 없음
이 줄의 효과실행되는 CPU 명령이 없습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

#include <asm/errno.h>

아키텍처의 오류 번호 정의를 가져옵니다. 음수 반환값을 오류 종류와 연결하는 정수 상수이며 CPU 예외 벡터 번호와는 다릅니다. #include는 전처리 단계에서 헤더 내용을 가져옵니다. CPU가 이 줄에서 함수를 호출하는 동작은 없습니다.

20행 그림 · 전처리 단계
소스
처리 전전처리 전
이 줄의 효과#include <asm/errno.h>
CPU 레지스터
처리 전해당 없음
이 줄의 효과실행되는 CPU 명령이 없습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

#include <asm/esr.h>

ESR 예외 원인 레지스터의 종류와 필드를 해석할 정의를 가져옵니다. 예외 번호와 시스템 호출 번호는 서로 다른 정보입니다. #include는 전처리 단계에서 헤더 내용을 가져옵니다. CPU가 이 줄에서 함수를 호출하는 동작은 없습니다.

21행 그림 · 전처리 단계
소스
처리 전전처리 전
이 줄의 효과#include <asm/esr.h>
CPU 레지스터
처리 전해당 없음
이 줄의 효과실행되는 CPU 명령이 없습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

#include <asm/irq.h>

ARM64 IRQ의 스택 크기와 관련 인터럽트 정의를 가져옵니다. 별도 IRQ 스택으로 실행을 옮길 때 task 스택과 크기·역할을 구분하는 데 필요합니다. #include는 전처리 단계에서 헤더 내용을 가져옵니다. CPU가 이 줄에서 함수를 호출하는 동작은 없습니다.

22행 그림 · 전처리 단계
소스
처리 전전처리 전
이 줄의 효과#include <asm/irq.h>
CPU 레지스터
처리 전해당 없음
이 줄의 효과실행되는 CPU 명령이 없습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

#include <asm/memory.h>

커널 가상 주소 배치, 스택 크기·정렬, 주소 변환 관련 상수를 가져옵니다. 같은 숫자가 주소·크기·오프셋 중 무엇인지 각 사용 문맥에서 구분해야 합니다. #include는 전처리 단계에서 헤더 내용을 가져옵니다. CPU가 이 줄에서 함수를 호출하는 동작은 없습니다.

23행 그림 · 전처리 단계
소스
처리 전전처리 전
이 줄의 효과#include <asm/memory.h>
CPU 레지스터
처리 전해당 없음
이 줄의 효과실행되는 CPU 명령이 없습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

#include <asm/mmu.h>

주소 공간 식별과 MMU 상태 관련 정의를 가져옵니다. 소프트웨어 PAN과 KPTI의 TTBR·ASID 전환에서 주소 변환 문맥을 구분하는 데 사용합니다. #include는 전처리 단계에서 헤더 내용을 가져옵니다. CPU가 이 줄에서 함수를 호출하는 동작은 없습니다.

24행 그림 · 전처리 단계
소스
처리 전전처리 전
이 줄의 효과#include <asm/mmu.h>
CPU 레지스터
처리 전해당 없음
이 줄의 효과실행되는 CPU 명령이 없습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

#include <asm/processor.h>

ARM64 task의 CPU 실행 상태에 관한 정의를 가져옵니다. 저장 문맥과 task별 제어 정보를 저수준 코드에서 일관되게 해석하도록 합니다. #include는 전처리 단계에서 헤더 내용을 가져옵니다. CPU가 이 줄에서 함수를 호출하는 동작은 없습니다.

25행 그림 · 전처리 단계
소스
처리 전전처리 전
이 줄의 효과#include <asm/processor.h>
CPU 레지스터
처리 전해당 없음
이 줄의 효과실행되는 CPU 명령이 없습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

#include <asm/ptrace.h>

pt_regs와 예외 전 PSTATE의 레벨·폭·마스크 비트 정의를 가져옵니다. 저장된 실행 문맥을 구성하고 32/64비트 복귀 상태를 해석하는 데 필요합니다. #include는 전처리 단계에서 헤더 내용을 가져옵니다. CPU가 이 줄에서 함수를 호출하는 동작은 없습니다.

26행 그림 · 전처리 단계
소스
처리 전전처리 전
이 줄의 효과#include <asm/ptrace.h>
CPU 레지스터
처리 전해당 없음
이 줄의 효과실행되는 CPU 명령이 없습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

#include <asm/scs.h>

별도 보호 스택에 복귀 주소를 보관하는 Shadow Call Stack의 저장·복원 규칙을 가져옵니다. task나 IRQ 문맥을 바꿀 때 일반 SP와 SCS 포인터를 함께 관리해야 합니다. #include는 전처리 단계에서 헤더 내용을 가져옵니다. CPU가 이 줄에서 함수를 호출하는 동작은 없습니다.

27행 그림 · 전처리 단계
소스
처리 전전처리 전
이 줄의 효과#include <asm/scs.h>
CPU 레지스터
처리 전해당 없음
이 줄의 효과실행되는 CPU 명령이 없습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

#include <asm/stacktrace/frame.h>

스택 추적기의 프레임 메타데이터 형식을 가져옵니다. 예외가 끊은 커널 프레임인지 추적의 마지막 프레임인지 표시하여 FP 연결을 안전하게 해석하게 합니다. #include는 전처리 단계에서 헤더 내용을 가져옵니다. CPU가 이 줄에서 함수를 호출하는 동작은 없습니다.

28행 그림 · 전처리 단계
소스
처리 전전처리 전
이 줄의 효과#include <asm/stacktrace/frame.h>
CPU 레지스터
처리 전해당 없음
이 줄의 효과실행되는 CPU 명령이 없습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

#include <asm/thread_info.h>

task의 저수준 실행 플래그와 스택 관련 정의를 가져옵니다. 예외 진입·복귀에서 single-step·SSBD·MTE 등 task별 처리를 고를 때 쓰는 정보입니다. #include는 전처리 단계에서 헤더 내용을 가져옵니다. CPU가 이 줄에서 함수를 호출하는 동작은 없습니다.

29행 그림 · 전처리 단계
소스
처리 전전처리 전
이 줄의 효과#include <asm/thread_info.h>
CPU 레지스터
처리 전해당 없음
이 줄의 효과실행되는 CPU 명령이 없습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

#include <asm/asm-uaccess.h>

커널의 사용자 메모리 접근을 열고 닫는 어셈블리 매크로를 가져옵니다. 소프트웨어 PAN에서 TTBR0를 예약 표와 사용자 표 사이로 바꾸는 구현을 제공합니다. #include는 전처리 단계에서 헤더 내용을 가져옵니다. CPU가 이 줄에서 함수를 호출하는 동작은 없습니다.

30행 그림 · 전처리 단계
소스
처리 전전처리 전
이 줄의 효과#include <asm/asm-uaccess.h>
CPU 레지스터
처리 전해당 없음
이 줄의 효과실행되는 CPU 명령이 없습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

#include <asm/unistd.h>

ARM64 시스템 호출 관련 번호와 정의를 가져옵니다. 예외로 들어왔다는 사실만으로 syscall이라고 단정하지 않도록 NO_SYSCALL 같은 구분도 함께 사용합니다. #include는 전처리 단계에서 헤더 내용을 가져옵니다. CPU가 이 줄에서 함수를 호출하는 동작은 없습니다.

31행 그림 · 전처리 단계
소스
처리 전전처리 전
이 줄의 효과#include <asm/unistd.h>
CPU 레지스터
처리 전해당 없음
이 줄의 효과실행되는 CPU 명령이 없습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	.macro	clear_gp_regs

x0~x29를 0으로 정리할 매크로입니다. 사용자 예외 진입에서는 원래 값을 먼저 pt_regs에 저장한 뒤 사용하여 복귀 상태를 보존하면서 커널이 불필요한 사용자 레지스터 값을 계속 들고 있지 않게 합니다. 이 줄은 어셈블러 매크로 정의의 시작입니다. 본문은 매크로를 사용하는 위치에 펼쳐지며, 정의 자체가 런타임 함수 호출은 아닙니다.

33행 그림 · 어셈블·링크 단계

매크로 본문: clear_gp_regs

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과.macro clear_gp_regs
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 뒤의 본문은 이 이름을 사용하는 위치에 펼쳐집니다. 정의 위치를 CPU가 함수처럼 호출하지 않습니다.
	.irp	n,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29

여러 레지스터 이름에 대해 같은 패턴을 반복 생성합니다. 소스에는 짧게 적혀도 결과에는 여러 명령이 생깁니다. n,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29

34행 그림 · 어셈블·링크 단계

매크로 본문: clear_gp_regs

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과.irp n,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	mov	x\n, xzr

x\n에 0을 넣습니다. 원래 x\n의 내용은 덮어쓰며, 다른 레지스터나 그 주소의 메모리까지 복사하지는 않습니다.

35행 그림 · 값 복사

매크로 본문: clear_gp_regs

읽는 값xzr
x\n
이 명령을 실행하기 직전명령 직전 x의 값\n
이 명령을 실행한 직후0

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 화살표는 값의 변화를 뜻합니다. 주소값을 옮겨도 그 주소의 메모리 내용은 복사하지 않습니다.
  • 매크로 clear_gp_regs의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	.endr

반복 생성할 범위의 끝입니다.

36행 그림 · 어셈블·링크 단계

매크로 본문: clear_gp_regs

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과.endr
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	.endm

공통 작업의 정의를 여기서 마칩니다. RET가 아니라 빌드 도구가 읽는 경계입니다.

37행 그림 · 어셈블·링크 단계

매크로 본문: clear_gp_regs

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과.endm
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 끝 표기 자체가 RET나 분기를 실행하지는 않습니다.

벡터 슬롯과 스택 초과 검사

원본 39–101행

	.macro kernel_ventry, el:req, ht:req, regsize:req, label:req

128바이트 예외 벡터 입구 하나를 만드는 매크로입니다. el은 중단된 레벨, ht는 SP 선택, regsize는 AArch64/AArch32, label은 sync·irq·fiq·error 종류입니다. 저장 공간과 스택 넘침 검사를 마련한 뒤 그 조합의 실제 handler로 연결합니다. 이 줄은 어셈블러 매크로 정의의 시작입니다. 본문은 매크로를 사용하는 위치에 펼쳐지며, 정의 자체가 런타임 함수 호출은 아닙니다.

39행 그림 · 어셈블·링크 단계

매크로 본문: kernel_ventry

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과.macro kernel_ventry, el:req, ht:req, regsize:req, label:req
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 뒤의 본문은 이 이름을 사용하는 위치에 펼쳐집니다. 정의 위치를 CPU가 함수처럼 호출하지 않습니다.
	.align 7

벡터 입구를 2^7=128바이트 경계에 맞춥니다. AArch64 하드웨어가 예외 종류별 고정 offset으로 입구를 고르기 때문에 단순한 성능 정렬이 아니라 벡터 배치 규칙입니다.

40행 그림 · 어셈블·링크 단계

매크로 본문: kernel_ventry

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과.align 7
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

.Lventry_start\@:

여기에 .Lventry_start\@라는 위치 이름을 붙입니다. 다른 줄의 분기나 주소 계산이 이 위치를 찾아올 수 있습니다. 이름 자체가 CPU 작업을 추가하지는 않습니다.

41행 그림 · 위치에 이름 붙이기

매크로 본문: kernel_ventry

레이블
처리 전현재 배치 위치
이 줄의 효과.Lventry_start\@
CPU 레지스터
처리 전그대로
이 줄의 효과레이블 자체가 값을 바꾸지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	.if	\el == 0

어셈블 단계에서 인자나 식을 검사해 필요한 명령만 만듭니다. \el == 0

42행 그림 · 어셈블·링크 단계

매크로 본문: kernel_ventry

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과.if \el == 0
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	b	.Lskip_tramp_vectors_cleanup\@

.Lskip_tramp_vectors_cleanup\@부터 실행을 계속합니다. 돌아올 주소를 새로 적지 않으므로 일반 함수 호출과는 다른 제어 이동입니다.

47행 그림 · 호출과 실행 위치

매크로 본문: kernel_ventry

이 줄을 포함하는 조건: .if \el == 0

PC
이 명령을 실행하기 직전명령 직전 PC의 값
이 명령을 실행한 직후.Lskip_tramp_vectors_cleanup\@

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 새 복귀 주소를 LR에 기록하지 않습니다. RET 역시 메모리의 스택 값을 자동으로 꺼내는 명령은 아닙니다.
  • 매크로 kernel_ventry의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	.if	\regsize == 64

어셈블 단계에서 인자나 식을 검사해 필요한 명령만 만듭니다. \regsize == 64

48행 그림 · 어셈블·링크 단계

매크로 본문: kernel_ventry

이 줄을 포함하는 조건: .if \el == 0

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과.if \regsize == 64
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	mrs	x30, tpidrro_el0

trampoline이 임시로 TPIDRRO_EL0에 보관한 사용자 x30을 복구합니다. 일반 TLS 값을 읽는 문맥이 아니라 제한된 매핑으로 들어오는 동안 잃지 않도록 맡겨 둔 복귀 주소를 되찾는 단계입니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.

49행 그림 · 시스템 레지스터 읽기

매크로 본문: kernel_ventry

이 줄을 포함하는 조건: .if \el == 0 / .if \regsize == 64

읽는 값TPIDRRO_EL0
x30
이 명령을 실행하기 직전명령 직전 x30의 값
이 명령을 실행한 직후명령 직전 TPIDRRO_EL0의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 일반 RAM의 메모리 접근과 구분합니다.
  • 매크로 kernel_ventry의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	msr	tpidrro_el0, xzr

임시 저장에 사용한 TPIDRRO_EL0를 0으로 정리합니다. 사용자 x30은 앞줄에서 복구했으며 이후 커널 진입이 이 임시값을 다시 사용자 데이터로 오인하지 않게 합니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.

50행 그림 · 시스템 레지스터 쓰기

매크로 본문: kernel_ventry

이 줄을 포함하는 조건: .if \el == 0 / .if \regsize == 64

읽는 값xzr
TPIDRRO_EL0
이 명령을 실행하기 직전명령 직전 TPIDRRO_EL0의 값
이 명령을 실행한 직후0

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 접근 권한과 VHE 별칭 규칙에 따라 실제 대상이 정해집니다. 뒤의 ISB·DSB가 필요한지는 이 레지스터와 문맥에 따릅니다.
  • 매크로 kernel_ventry의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	.else

앞 어셈블 조건의 반대 경우에 생성할 코드입니다.

51행 그림 · 어셈블·링크 단계

매크로 본문: kernel_ventry

이 줄을 포함하는 조건: .if \el == 0 / .if \regsize == 64

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과.else
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	mov	x30, xzr

x30에 0을 넣습니다. 원래 x30의 내용은 덮어쓰며, 다른 레지스터나 그 주소의 메모리까지 복사하지는 않습니다.

52행 그림 · 값 복사

매크로 본문: kernel_ventry

이 줄을 포함하는 조건: .if \el == 0 / 앞 어셈블 조건의 반대

읽는 값xzr
x30
이 명령을 실행하기 직전명령 직전 x30의 값
이 명령을 실행한 직후0

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 화살표는 값의 변화를 뜻합니다. 주소값을 옮겨도 그 주소의 메모리 내용은 복사하지 않습니다.
  • 매크로 kernel_ventry의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	.endif

어셈블 조건에 따라 생성할 코드를 나누던 범위가 끝납니다.

53행 그림 · 어셈블·링크 단계

매크로 본문: kernel_ventry

이 줄을 포함하는 조건: .if \el == 0 / 앞 어셈블 조건의 반대

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과.endif
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

.Lskip_tramp_vectors_cleanup\@:

여기에 .Lskip_tramp_vectors_cleanup\@라는 위치 이름을 붙입니다. 다른 줄의 분기나 주소 계산이 이 위치를 찾아올 수 있습니다. 이름 자체가 CPU 작업을 추가하지는 않습니다.

54행 그림 · 위치에 이름 붙이기

매크로 본문: kernel_ventry

이 줄을 포함하는 조건: .if \el == 0

레이블
처리 전현재 배치 위치
이 줄의 효과.Lskip_tramp_vectors_cleanup\@
CPU 레지스터
처리 전그대로
이 줄의 효과레이블 자체가 값을 바꾸지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	.endif

어셈블 조건에 따라 생성할 코드를 나누던 범위가 끝납니다.

55행 그림 · 어셈블·링크 단계

매크로 본문: kernel_ventry

이 줄을 포함하는 조건: .if \el == 0

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과.endif
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	sub	sp, sp, #PT_REGS_SIZE

pt_regs를 놓을 만큼 커널 SP를 낮춥니다. 뒤의 레지스터 저장이 호출자의 스택 자료를 덮지 않게 확보하는 공간입니다. 이 명령은 SP의 주소값을 조정하며, 확보하거나 반환하는 스택 공간의 데이터를 직접 저장하거나 지우지는 않습니다. 이 SUB 형식은 NZCV 조건 플래그를 바꾸지 않습니다.

pt_regs 프레임을 위한 공간 확보입니다. 이 줄 자체가 기존 레지스터 값을 저장하지는 않습니다.

57행 그림 · 레지스터 상태

매크로 본문: kernel_ventry

읽는 값sp
sp
이 명령을 실행하기 직전명령 직전 sp의 값
이 명령을 실행한 직후명령 직전 sp의 값 − PT_REGS_SIZE

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 결과는 목적 레지스터 폭에 맞습니다. 이 형식은 NZCV를 바꾸지 않습니다.
  • SP는 주소만 바뀝니다. 스택 데이터 저장·복원은 STR/STP·LDR/LDP 같은 별도 메모리 명령이 수행합니다.
  • 매크로 kernel_ventry의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	add	sp, sp, x0			// sp' = sp + x0

sp를 기준으로 x0을 더해 sp에 결과를 둡니다. 주소 계산에 쓰이는 경우에는 구조체의 필드나 다음 저장 위치를 찾는 동작입니다. 이 형태는 조건 플래그를 바꾸지 않습니다.

x0를 잃지 않고 원래 SP를 검사하기 위한 임시 산술입니다. 일반적인 스택 할당 ADD가 아닙니다.

63행 그림 · 레지스터 상태

매크로 본문: kernel_ventry

읽는 값spx0
sp
이 명령을 실행하기 직전명령 직전 sp의 값
이 명령을 실행한 직후명령 직전 sp의 값 + 명령 직전 x0의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 결과는 목적 레지스터 폭에 맞습니다. 이 형식은 NZCV를 바꾸지 않습니다.
  • SP는 주소만 바뀝니다. 스택 데이터 저장·복원은 STR/STP·LDR/LDP 같은 별도 메모리 명령이 수행합니다.
  • 매크로 kernel_ventry의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	sub	x0, sp, x0			// x0' = sp' - x0 = (sp + x0) - x0 = sp

sp를 기준으로 x0을 빼서 x0에 결과를 둡니다. 주소 계산에 쓰이는 경우에는 구조체의 필드나 다음 저장 위치를 찾는 동작입니다. 이 형태는 조건 플래그를 바꾸지 않습니다.

새 x0=(원래 SP−PT_REGS_SIZE)입니다.

64행 그림 · 레지스터 상태

매크로 본문: kernel_ventry

읽는 값spx0
x0
이 명령을 실행하기 직전명령 직전 x0의 값
이 명령을 실행한 직후명령 직전 sp의 값 − 명령 직전 x0의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 결과는 목적 레지스터 폭에 맞습니다. 이 형식은 NZCV를 바꾸지 않습니다.
  • 매크로 kernel_ventry의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	tbnz	x0, #THREAD_SHIFT, 0f

THREAD_SHIFT 위치의 비트로 정렬된 task 스택 경계를 넘어갔는지 검사합니다. 이 구간은 임시 레지스터를 잃지 않도록 SP와 x0의 가역 계산을 사용해 원래 SP를 x0에 복원한 상태입니다. 이 분기는 지정 레지스터의 값이나 비트를 직접 검사하며 CMP가 남긴 NZCV를 읽거나 바꾸지 않습니다. 조건이 맞지 않으면 바로 다음 명령으로 진행합니다.

스택 정렬 규칙에 따라 THREAD_SHIFT 비트가 1이면 overflow 경로를 선택합니다.

65행 그림 · 조건별 실행 위치

매크로 본문: kernel_ventry

읽는 값x0
PC
이 명령을 실행하기 직전명령 직전 PC의 값
이 명령을 실행한 직후명령 직전 x0의 값의 bit THREAD_SHIFT = 1이면 0f (L70), 아니면 다음 명령

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 조건이 맞는 한 경로만 실행합니다. 이 분기 명령은 NZCV를 새로 계산하지 않습니다.
  • 매크로 kernel_ventry의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	sub	x0, sp, x0			// x0'' = sp' - x0' = (sp + x0) - sp = x0

sp를 기준으로 x0을 빼서 x0에 결과를 둡니다. 주소 계산에 쓰이는 경우에는 구조체의 필드나 다음 저장 위치를 찾는 동작입니다. 이 형태는 조건 플래그를 바꾸지 않습니다.

원래 x0 값을 재구성합니다.

66행 그림 · 레지스터 상태

매크로 본문: kernel_ventry

읽는 값spx0
x0
이 명령을 실행하기 직전명령 직전 x0의 값
이 명령을 실행한 직후명령 직전 sp의 값 − 명령 직전 x0의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 결과는 목적 레지스터 폭에 맞습니다. 이 형식은 NZCV를 바꾸지 않습니다.
  • 매크로 kernel_ventry의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	sub	sp, sp, x0			// sp'' = sp' - x0 = (sp + x0) - x0 = sp

sp를 기준으로 x0을 빼서 sp에 결과를 둡니다. 주소 계산에 쓰이는 경우에는 구조체의 필드나 다음 저장 위치를 찾는 동작입니다. 이 형태는 조건 플래그를 바꾸지 않습니다.

pt_regs 공간을 확보한 SP로 되돌립니다.

67행 그림 · 레지스터 상태

매크로 본문: kernel_ventry

읽는 값spx0
sp
이 명령을 실행하기 직전명령 직전 sp의 값
이 명령을 실행한 직후명령 직전 sp의 값 − 명령 직전 x0의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 결과는 목적 레지스터 폭에 맞습니다. 이 형식은 NZCV를 바꾸지 않습니다.
  • SP는 주소만 바뀝니다. 스택 데이터 저장·복원은 STR/STP·LDR/LDP 같은 별도 메모리 명령이 수행합니다.
  • 매크로 kernel_ventry의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	b	el\el\ht\()_\regsize\()_\label

el\el\ht\()_\regsize\()_\label부터 실행을 계속합니다. 돌아올 주소를 새로 적지 않으므로 일반 함수 호출과는 다른 제어 이동입니다.

68행 그림 · 호출과 실행 위치

매크로 본문: kernel_ventry

PC
이 명령을 실행하기 직전명령 직전 PC의 값
이 명령을 실행한 직후el\el\ht\()_\regsize\()_\label

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 새 복귀 주소를 LR에 기록하지 않습니다. RET 역시 메모리의 스택 값을 자동으로 꺼내는 명령은 아닙니다.
  • 매크로 kernel_ventry의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
0:

여기에 0라는 위치 이름을 붙입니다. 다른 줄의 분기나 주소 계산이 이 위치를 찾아올 수 있습니다. 이름 자체가 CPU 작업을 추가하지는 않습니다.

70행 그림 · 위치에 이름 붙이기

매크로 본문: kernel_ventry

레이블
처리 전현재 배치 위치
이 줄의 효과0
CPU 레지스터
처리 전그대로
이 줄의 효과레이블 자체가 값을 바꾸지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	msr	tpidr_el0, x0

스택 넘침 처리에서 원래 SP를 TPIDR_EL0에 임시 보관합니다. 보통 사용자 TLS 용도로 알려진 레지스터지만 이 비정상 진입 구간에서는 안전한 스택으로 옮기기 위한 scratch 저장소입니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.

78행 그림 · 시스템 레지스터 쓰기

매크로 본문: kernel_ventry

읽는 값x0
TPIDR_EL0
이 명령을 실행하기 직전명령 직전 TPIDR_EL0의 값
이 명령을 실행한 직후명령 직전 x0의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 접근 권한과 VHE 별칭 규칙에 따라 실제 대상이 정해집니다. 뒤의 ISB·DSB가 필요한지는 이 레지스터와 문맥에 따릅니다.
  • 매크로 kernel_ventry의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	sub	x0, sp, x0

sp를 기준으로 x0을 빼서 x0에 결과를 둡니다. 주소 계산에 쓰이는 경우에는 구조체의 필드나 다음 저장 위치를 찾는 동작입니다. 이 형태는 조건 플래그를 바꾸지 않습니다.

81행 그림 · 레지스터 상태

매크로 본문: kernel_ventry

읽는 값spx0
x0
이 명령을 실행하기 직전명령 직전 x0의 값
이 명령을 실행한 직후명령 직전 sp의 값 − 명령 직전 x0의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 결과는 목적 레지스터 폭에 맞습니다. 이 형식은 NZCV를 바꾸지 않습니다.
  • 매크로 kernel_ventry의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	msr	tpidrro_el0, x0

스택 검사 중 보존해야 할 원래 x0를 TPIDRRO_EL0에 임시 저장합니다. SP를 overflow stack으로 바꾼 뒤 원래 예외 문맥을 다시 만들기 위해서입니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.

82행 그림 · 시스템 레지스터 쓰기

매크로 본문: kernel_ventry

읽는 값x0
TPIDRRO_EL0
이 명령을 실행하기 직전명령 직전 TPIDRRO_EL0의 값
이 명령을 실행한 직후명령 직전 x0의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 접근 권한과 VHE 별칭 규칙에 따라 실제 대상이 정해집니다. 뒤의 ISB·DSB가 필요한지는 이 레지스터와 문맥에 따릅니다.
  • 매크로 kernel_ventry의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	adr_this_cpu sp, overflow_stack + OVERFLOW_STACK_SIZE, x0

현재 CPU의 overflow stack 높은 주소 끝을 SP로 선택합니다. 정상 task 스택이 넘쳤을 수 있으므로 같은 스택에 오류 처리 프레임을 더 쌓지 않게 합니다. 심벌의 기준 주소에 현재 CPU의 per-CPU 오프셋을 더해 이 CPU의 저장 위치를 찾습니다.

overflow_stack의 끝 주소에서 낮은 주소 방향으로 스택을 사용합니다.

85행 그림 · 주소·값 구분

매크로 본문: kernel_ventry

sp
이 명령을 실행하기 직전명령 직전 sp의 값
이 명령을 실행한 직후주소(overflow_stack + OVERFLOW_STACK_SIZE) + 현재 CPU 오프셋

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 이 매크로의 임시 레지스터는 내부에서 사용됩니다.
  • 매크로 kernel_ventry의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
adr_this_cpu 매크로 본문 · 238행

arch/arm64/include/asm/assembler.h · Linux v6.18.37

정의 선택 조건: #ifndef __ASM_ASSEMBLER_H

.macro adr_this_cpu, dst, sym, tmp adrp \tmp, \sym add \dst, \tmp, #:lo12:\sym get_this_cpu_offset \tmp add \dst, \dst, \tmp .endm
	mrs	x0, tpidr_el0			// sp of interrupted context

TPIDR_EL0에 맡겨 둔 중단 시 SP를 다시 읽습니다. 새 overflow stack 범위 안에서 이미 처리 중이었는지 다음 차이 계산으로 검사합니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.

91행 그림 · 시스템 레지스터 읽기

매크로 본문: kernel_ventry

읽는 값TPIDR_EL0
x0
이 명령을 실행하기 직전명령 직전 x0의 값
이 명령을 실행한 직후명령 직전 TPIDR_EL0의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 일반 RAM의 메모리 접근과 구분합니다.
  • 매크로 kernel_ventry의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	sub	x0, sp, x0			// delta with top of overflow stack

sp를 기준으로 x0을 빼서 x0에 결과를 둡니다. 주소 계산에 쓰이는 경우에는 구조체의 필드나 다음 저장 위치를 찾는 동작입니다. 이 형태는 조건 플래그를 바꾸지 않습니다.

92행 그림 · 레지스터 상태

매크로 본문: kernel_ventry

읽는 값spx0
x0
이 명령을 실행하기 직전명령 직전 x0의 값
이 명령을 실행한 직후명령 직전 sp의 값 − 명령 직전 x0의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 결과는 목적 레지스터 폭에 맞습니다. 이 형식은 NZCV를 바꾸지 않습니다.
  • 매크로 kernel_ventry의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	tst	x0, #~(OVERFLOW_STACK_SIZE - 1)	// within range?

원래 SP와 overflow stack 상단의 차이가 OVERFLOW_STACK_SIZE 범위에 들어가는지 검사합니다. 이미 overflow stack에서 발생한 중첩 예외와 최초 스택 손상을 구분하려는 조건입니다. TST는 두 피연산자의 비트 AND 결과로 조건 플래그를 갱신하며 원래 레지스터 값은 바꾸지 않습니다.

93행 그림 · 조건 플래그 계산

매크로 본문: kernel_ventry

읽는 값x0
NZCV
이 명령을 실행하기 직전명령 직전 NZCV의 값
이 명령을 실행한 직후명령 직전 x0의 값 AND ~(OVERFLOW_STACK_SIZE - 1)의 조건 결과

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 일반 레지스터에 연산 결과를 쓰지 않고 조건 플래그만 바꿉니다.
  • N은 결과의 부호 비트, Z는 결과가 0인지 나타냅니다. 이 AArch64 TST는 C와 V를 0으로 만듭니다.
  • 매크로 kernel_ventry의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	b.ne	__bad_stack			// no? -> bad stack pointer

앞서 계산한 조건 플래그가 Z=0이면 __bad_stack로 갑니다. 사이에 플래그를 바꾸지 않는 명령이 있어도 비교 결과는 남아 있습니다.

94행 그림 · 조건별 실행 위치

매크로 본문: kernel_ventry

읽는 값NZCV
PC
이 명령을 실행하기 직전명령 직전 PC의 값
이 명령을 실행한 직후Z=0이면 __bad_stack, 아니면 다음 명령

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 조건이 맞는 한 경로만 실행합니다. 이 분기 명령은 NZCV를 새로 계산하지 않습니다.
  • 매크로 kernel_ventry의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	sub	sp, sp, x0

sp를 기준으로 x0을 빼서 sp에 결과를 둡니다. 주소 계산에 쓰이는 경우에는 구조체의 필드나 다음 저장 위치를 찾는 동작입니다. 이 형태는 조건 플래그를 바꾸지 않습니다.

97행 그림 · 레지스터 상태

매크로 본문: kernel_ventry

읽는 값spx0
sp
이 명령을 실행하기 직전명령 직전 sp의 값
이 명령을 실행한 직후명령 직전 sp의 값 − 명령 직전 x0의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 결과는 목적 레지스터 폭에 맞습니다. 이 형식은 NZCV를 바꾸지 않습니다.
  • SP는 주소만 바뀝니다. 스택 데이터 저장·복원은 STR/STP·LDR/LDP 같은 별도 메모리 명령이 수행합니다.
  • 매크로 kernel_ventry의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	mrs	x0, tpidrro_el0

임시 TPIDRRO_EL0에서 원래 x0를 복구하여 정상 handler가 보는 저장 문맥을 유지합니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.

98행 그림 · 시스템 레지스터 읽기

매크로 본문: kernel_ventry

읽는 값TPIDRRO_EL0
x0
이 명령을 실행하기 직전명령 직전 x0의 값
이 명령을 실행한 직후명령 직전 TPIDRRO_EL0의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 일반 RAM의 메모리 접근과 구분합니다.
  • 매크로 kernel_ventry의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	b	el\el\ht\()_\regsize\()_\label

el\el\ht\()_\regsize\()_\label부터 실행을 계속합니다. 돌아올 주소를 새로 적지 않으므로 일반 함수 호출과는 다른 제어 이동입니다.

99행 그림 · 호출과 실행 위치

매크로 본문: kernel_ventry

PC
이 명령을 실행하기 직전명령 직전 PC의 값
이 명령을 실행한 직후el\el\ht\()_\regsize\()_\label

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 새 복귀 주소를 LR에 기록하지 않습니다. RET 역시 메모리의 스택 값을 자동으로 꺼내는 명령은 아닙니다.
  • 매크로 kernel_ventry의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
.org .Lventry_start\@ + 128	// Did we overflow the ventry slot?

이 벡터 입구가 시작부터 128바이트를 넘지 않도록 assembler 위치를 강제합니다. 코드가 슬롯보다 커지면 빌드에서 드러나게 하여 다음 벡터를 덮어쓰지 않게 합니다.

100행 그림 · 어셈블·링크 단계

매크로 본문: kernel_ventry

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과.org .Lventry_start\@ + 128
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	.endm

공통 작업의 정의를 여기서 마칩니다. RET가 아니라 빌드 도구가 읽는 경계입니다.

101행 그림 · 어셈블·링크 단계

매크로 본문: kernel_ventry

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과.endm
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 끝 표기 자체가 RET나 분기를 실행하지는 않습니다.

trampoline 주소와 SSBD

원본 102–128행

	.macro	tramp_alias, dst, sym

trampoline의 최소 매핑에서 접근할 별칭 주소를 dst에 만드는 매크로입니다. sym의 일반 커널 가상 주소 대신 TRAMP_VALIAS와 섹션 내부 offset을 사용하여 커널 전체 매핑을 없앤 뒤에도 해당 코드를 실행할 수 있게 합니다. 이 줄은 어셈블러 매크로 정의의 시작입니다. 본문은 매크로를 사용하는 위치에 펼쳐지며, 정의 자체가 런타임 함수 호출은 아닙니다.

103행 그림 · 어셈블·링크 단계

매크로 본문: tramp_alias

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과.macro tramp_alias, dst, sym
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 뒤의 본문은 이 이름을 사용하는 위치에 펼쳐집니다. 정의 위치를 CPU가 함수처럼 호출하지 않습니다.
	.set	.Lalias\@, TRAMP_VALIAS + \sym - .entry.tramp.text

sym이 .entry.tramp.text 시작에서 떨어진 거리에 고정 trampoline 가상 기준을 더합니다. 같은 코드 바이트의 다른 매핑 주소를 계산하는 식이며 코드를 복사하지 않습니다. 이름에 식의 값을 부여하는 어셈블러 지시문입니다. 실행 중 일반 레지스터나 메모리에 값을 쓰는 명령은 아닙니다.

104행 그림 · 어셈블·링크 단계

매크로 본문: tramp_alias

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과.set .Lalias\@, TRAMP_VALIAS + \sym - .entry.tramp.text
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	movz	\dst, :abs_g2_s:.Lalias\@

한 명령에 담기 어려운 큰 값이나 주소를 레지스터에 구성합니다. 즉시값 조각과 재배치 표현을 사용하며, 메모리에서 그 주소의 본문을 복사하는 작업은 아닙니다.

105행 그림 · 상수의 비트 조각 구성

매크로 본문: tramp_alias

\dst
이 명령을 실행하기 직전명령 직전 \dst의 값
이 명령을 실행한 직후심벌의 부호에 맞게 MOVZ 또는 MOVN으로 구성한 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 부호 있는 재배치가 실제 명령의 MOVZ/MOVN 선택에도 영향을 주므로 남은 비트를 항상 0이라고 표시하지 않습니다.
  • 매크로 tramp_alias의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	movk	\dst, :abs_g1_nc:.Lalias\@

한 명령에 담기 어려운 큰 값이나 주소를 레지스터에 구성합니다. MOVK는 일부 16비트만 덮어쓰므로 앞에서 만든 나머지 조각을 유지합니다.

106행 그림 · 상수의 비트 조각 구성

매크로 본문: tramp_alias

읽는 값\dst
\dst
이 명령을 실행하기 직전명령 직전 \dst의 값
이 명령을 실행한 직후명령 직전 값에서 [31:16]만 :abs_g1_nc:.Lalias\@로 교체

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 선택한 16비트 이외의 비트는 그대로 남습니다.
  • 매크로 tramp_alias의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	movk	\dst, :abs_g0_nc:.Lalias\@

한 명령에 담기 어려운 큰 값이나 주소를 레지스터에 구성합니다. MOVK는 일부 16비트만 덮어쓰므로 앞에서 만든 나머지 조각을 유지합니다.

107행 그림 · 상수의 비트 조각 구성

매크로 본문: tramp_alias

읽는 값\dst
\dst
이 명령을 실행하기 직전명령 직전 \dst의 값
이 명령을 실행한 직후명령 직전 값에서 [15:0]만 :abs_g0_nc:.Lalias\@로 교체

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 선택한 16비트 이외의 비트는 그대로 남습니다.
  • 매크로 tramp_alias의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	.endm

공통 작업의 정의를 여기서 마칩니다. RET가 아니라 빌드 도구가 읽는 경계입니다.

108행 그림 · 어셈블·링크 단계

매크로 본문: tramp_alias

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과.endm
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 끝 표기 자체가 RET나 분기를 실행하지는 않습니다.
	.macro	apply_ssbd, state, tmp1, tmp2

Spectre v4의 speculative store bypass 완화를 펌웨어에 요청하는 매크로입니다. state가 1이면 커널 진입 쪽의 활성 요청, 0이면 사용자 복귀 쪽 요청에 쓰이며 CPU·task 정책상 필요할 때만 SMCCC 호출이 남습니다. 이 줄은 어셈블러 매크로 정의의 시작입니다. 본문은 매크로를 사용하는 위치에 펼쳐지며, 정의 자체가 런타임 함수 호출은 아닙니다.

114행 그림 · 어셈블·링크 단계

매크로 본문: apply_ssbd

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과.macro apply_ssbd, state, tmp1, tmp2
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 뒤의 본문은 이 이름을 사용하는 위치에 펼쳐집니다. 정의 위치를 CPU가 함수처럼 호출하지 않습니다.
alternative_cb	ARM64_ALWAYS_SYSTEM, spectre_v4_patch_fw_mitigation_enable

시스템 전체의 Spectre v4 정책을 본 callback이 다음 skip 분기를 NOP로 바꿀지 결정하도록 패치 지점을 등록합니다. ARM64_ALWAYS_SYSTEM은 여기서 정책 callback을 실행시키는 공통 capability이며 모든 CPU가 무조건 펌웨어 완화를 호출한다는 뜻은 아닙니다. 이 매크로는 기본·교체 명령의 패치 정보를 만들며, CPU가 이 줄을 매번 if 조건처럼 실행하지 않습니다.

115행 그림 · CPU 기능에 따른 명령 패치

매크로 본문: apply_ssbd

패치 전 코드
처리 전기본 명령열
이 줄의 효과기능·정책에 맞는 기본 코드 또는 교체 코드

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 빌드할 때 패치 정보를 만들고 커널의 대체 코드 처리에서 명령을 선택합니다. 이 소스 줄을 CPU가 매번 if문으로 실행하는 것은 아닙니다.
	b	.L__asm_ssbd_skip\@		// Patched to NOP

.L__asm_ssbd_skip\@부터 실행을 계속합니다. 돌아올 주소를 새로 적지 않으므로 일반 함수 호출과는 다른 제어 이동입니다.

116행 그림 · 호출과 실행 위치

매크로 본문: apply_ssbd

PC
이 명령을 실행하기 직전명령 직전 PC의 값
이 명령을 실행한 직후.L__asm_ssbd_skip\@

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 새 복귀 주소를 LR에 기록하지 않습니다. RET 역시 메모리의 스택 값을 자동으로 꺼내는 명령은 아닙니다.
  • 매크로 apply_ssbd의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
alternative_cb_end

115행에서 시작한 ARM64_ALWAYS_SYSTEM, spectre_v4_patch_fw_mitigation_enable 패치 묶음을 닫습니다. callback이 선택한 분기·NOP·펌웨어 호출 명령이 같은 자리를 사용하도록 교체 범위를 확정합니다. callback 함수가 여기서 매번 호출되는 것은 아닙니다.

117행 그림 · CPU 기능에 따른 명령 패치

매크로 본문: apply_ssbd

패치 전 코드
처리 전기본 명령열
이 줄의 효과기능·정책에 맞는 기본 코드 또는 교체 코드

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 빌드할 때 패치 정보를 만들고 커널의 대체 코드 처리에서 명령을 선택합니다. 이 소스 줄을 CPU가 매번 if문으로 실행하는 것은 아닙니다.
	ldr_this_cpu	\tmp2, arm64_ssbd_callback_required, \tmp1

이 CPU가 SSBD 변경을 펌웨어 callback으로 처리해야 하는지 per-CPU 플래그를 읽습니다. 하드웨어나 정책에 따라 callback이 필요 없는 CPU는 뒤에서 바로 건너뜁니다. 심벌의 기준 주소에 현재 CPU의 per-CPU 오프셋을 더해 이 CPU의 저장 위치를 찾습니다.

118행 그림 · 주소·값 구분

매크로 본문: apply_ssbd

\tmp2
이 명령을 실행하기 직전명령 직전 \tmp2의 값
이 명령을 실행한 직후메모리[주소(arm64_ssbd_callback_required) + 현재 CPU 오프셋]의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 이 매크로의 임시 레지스터는 내부에서 사용됩니다.
  • 매크로 apply_ssbd의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
ldr_this_cpu 매크로 본문 · 250행

arch/arm64/include/asm/assembler.h · Linux v6.18.37

정의 선택 조건: #ifndef __ASM_ASSEMBLER_H

.macro ldr_this_cpu dst, sym, tmp adr_l \dst, \sym get_this_cpu_offset \tmp ldr \dst, [\dst, \tmp] .endm
	cbz	\tmp2,	.L__asm_ssbd_skip\@

\tmp2의 값가 0이면 .L__asm_ssbd_skip\@로 갑니다. 조건을 만족하지 않으면 바로 다음 명령을 읽습니다. 이 분기는 CMP의 결과가 아니라 지정 레지스터를 직접 검사합니다.

119행 그림 · 조건별 실행 위치

매크로 본문: apply_ssbd

읽는 값\tmp2
PC
이 명령을 실행하기 직전명령 직전 PC의 값
이 명령을 실행한 직후명령 직전 \tmp2의 값 = 0이면 .L__asm_ssbd_skip\@, 아니면 다음 명령

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 조건이 맞는 한 경로만 실행합니다. 이 분기 명령은 NZCV를 새로 계산하지 않습니다.
  • 매크로 apply_ssbd의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	ldr	\tmp2, [tsk, #TSK_TI_FLAGS]

tsk를 기준으로 TSK_TI_FLAGS만큼 계산한 메모리 위치에 보관된 값을 \tmp2로 가져옵니다. 주소 숫자를 옮기는 MOV와 달리 실제 메모리를 읽습니다.

120행 그림 · 메모리 읽기

매크로 본문: apply_ssbd

읽는 값tsk
메모리[이전 tsk + (TSK_TI_FLAGS)] · 8바이트
이 명령을 실행하기 직전M0 (이 주소에 저장된 값)
이 명령을 실행한 직후M0 (이 주소에 저장된 값)
\tmp2
이 명령을 실행하기 직전명령 직전 \tmp2의 값
이 명령을 실행한 직후M0

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 주소 계산에 의한 기준 레지스터 갱신은 없습니다. 다만 LDR/LDP의 목적 레지스터가 기준 레지스터와 같으면 읽은 값이 그 레지스터를 덮어씁니다.
  • 매크로 apply_ssbd의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	tbnz	\tmp2, #TIF_SSBD, .L__asm_ssbd_skip\@

task의 TIF_SSBD 정책이 이미 완화를 유지하도록 요구하는 경우 진입·복귀 때 상태를 바꾸는 호출을 건너뜁니다. 비트가 세워졌다는 사실만 보고 여기서 취약성 완화를 끈다고 해석하면 안 됩니다. 이 분기는 지정 레지스터의 값이나 비트를 직접 검사하며 CMP가 남긴 NZCV를 읽거나 바꾸지 않습니다. 조건이 맞지 않으면 바로 다음 명령으로 진행합니다.

121행 그림 · 조건별 실행 위치

매크로 본문: apply_ssbd

읽는 값\tmp2
PC
이 명령을 실행하기 직전명령 직전 PC의 값
이 명령을 실행한 직후명령 직전 \tmp2의의 값 bit TIF_SSBD = 1이면 .L__asm_ssbd_skip\@, 아니면 다음 명령

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 조건이 맞는 한 경로만 실행합니다. 이 분기 명령은 NZCV를 새로 계산하지 않습니다.
  • 매크로 apply_ssbd의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	mov	w0, #ARM_SMCCC_ARCH_WORKAROUND_2

SMCCC ARCH_WORKAROUND_2 함수 ID를 첫 인수 w0에 넣습니다. 펌웨어에 Spectre v4의 SSBD 상태를 요청하는 인터페이스 번호입니다. MOV는 w0의 기존 값을 대체합니다. 원본이 주소값이어도 그 주소의 메모리를 읽거나 복사하지 않습니다. w0에 쓰면 대응하는 x0의 상위 32비트도 0이 됩니다.

122행 그림 · 값 복사

매크로 본문: apply_ssbd

w0
이 명령을 실행하기 직전명령 직전 w0의 값
이 명령을 실행한 직후ARM_SMCCC_ARCH_WORKAROUND_2
x0[63:32]
이 명령을 실행하기 직전명령 직전 x0[63:32]의 값
이 명령을 실행한 직후0

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 화살표는 값의 변화를 뜻합니다. 주소값을 옮겨도 그 주소의 메모리 내용은 복사하지 않습니다.
  • 매크로 apply_ssbd의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	mov	w1, #\state

매크로 호출자가 지정한 완화 상태 state를 펌웨어의 두 번째 인수 w1에 넣습니다. 이 값은 호출 서비스 번호와 구분됩니다. MOV는 w1의 기존 값을 대체합니다. 원본이 주소값이어도 그 주소의 메모리를 읽거나 복사하지 않습니다. w1에 쓰면 대응하는 x1의 상위 32비트도 0이 됩니다.

123행 그림 · 값 복사

매크로 본문: apply_ssbd

읽는 값\state
w1
이 명령을 실행하기 직전명령 직전 w1의 값
이 명령을 실행한 직후명령 직전 \state의 값
x1[63:32]
이 명령을 실행하기 직전명령 직전 x1[63:32]의 값
이 명령을 실행한 직후0

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 화살표는 값의 변화를 뜻합니다. 주소값을 옮겨도 그 주소의 메모리 내용은 복사하지 않습니다.
  • 매크로 apply_ssbd의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
alternative_cb	ARM64_ALWAYS_SYSTEM, smccc_patch_fw_mitigation_conduit

시스템의 SMCCC 통로에 맞춰 다음 NOP를 SMC 또는 HVC 명령으로 바꾸는 callback을 등록합니다. 펌웨어가 어느 레벨에서 서비스를 제공하는지에 맞추기 위한 코드 패치입니다. 이 매크로는 기본·교체 명령의 패치 정보를 만들며, CPU가 이 줄을 매번 if 조건처럼 실행하지 않습니다.

124행 그림 · CPU 기능에 따른 명령 패치

매크로 본문: apply_ssbd

패치 전 코드
처리 전기본 명령열
이 줄의 효과기능·정책에 맞는 기본 코드 또는 교체 코드

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 빌드할 때 패치 정보를 만들고 커널의 대체 코드 처리에서 명령을 선택합니다. 이 소스 줄을 CPU가 매번 if문으로 실행하는 것은 아닙니다.
	nop					// Patched to SMC/HVC #0

초기 NOP는 펌웨어 완화 통로가 결정되면 SMC #0 또는 HVC #0으로 바뀔 수 있습니다. 실행 시에는 w0의 서비스 번호와 w1의 상태를 그 통로로 전달합니다.

125행 그림 · 동기화·캐시·대기 상태

매크로 본문: apply_ssbd

일반 레지스터
이 명령을 실행하기 직전실행 전 상태
이 명령을 실행한 직후변경 없음

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 매크로 apply_ssbd의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
alternative_cb_end

124행에서 시작한 ARM64_ALWAYS_SYSTEM, smccc_patch_fw_mitigation_conduit 패치 묶음을 닫습니다. callback이 선택한 분기·NOP·펌웨어 호출 명령이 같은 자리를 사용하도록 교체 범위를 확정합니다. callback 함수가 여기서 매번 호출되는 것은 아닙니다.

126행 그림 · CPU 기능에 따른 명령 패치

매크로 본문: apply_ssbd

패치 전 코드
처리 전기본 명령열
이 줄의 효과기능·정책에 맞는 기본 코드 또는 교체 코드

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 빌드할 때 패치 정보를 만들고 커널의 대체 코드 처리에서 명령을 선택합니다. 이 소스 줄을 CPU가 매번 if문으로 실행하는 것은 아닙니다.
.L__asm_ssbd_skip\@:

여기에 .L__asm_ssbd_skip\@라는 위치 이름을 붙입니다. 다른 줄의 분기나 주소 계산이 이 위치를 찾아올 수 있습니다. 이름 자체가 CPU 작업을 추가하지는 않습니다.

127행 그림 · 위치에 이름 붙이기

매크로 본문: apply_ssbd

레이블
처리 전현재 배치 위치
이 줄의 효과.L__asm_ssbd_skip\@
CPU 레지스터
처리 전그대로
이 줄의 효과레이블 자체가 값을 바꾸지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	.endm

공통 작업의 정의를 여기서 마칩니다. RET가 아니라 빌드 도구가 읽는 경계입니다.

128행 그림 · 어셈블·링크 단계

매크로 본문: apply_ssbd

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과.endm
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 끝 표기 자체가 RET나 분기를 실행하지는 않습니다.

MTE 비동기 오류와 태그 생성 설정

원본 129–196행

	.macro check_mte_async_tcf, tmp, ti_flags, thread_sctlr

사용자 실행 중 누적된 비동기 MTE 태그 오류를 task 플래그에 반영하는 매크로입니다. tmp는 상태 조회용, ti_flags는 task 플래그 주소용, thread_sctlr는 이 task의 검사 mode를 담습니다. 이 줄은 어셈블러 매크로 정의의 시작입니다. 본문은 매크로를 사용하는 위치에 펼쳐지며, 정의 자체가 런타임 함수 호출은 아닙니다.

131행 그림 · 어셈블·링크 단계

매크로 본문: check_mte_async_tcf

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과.macro check_mte_async_tcf, tmp, ti_flags, thread_sctlr
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 뒤의 본문은 이 이름을 사용하는 위치에 펼쳐집니다. 정의 위치를 CPU가 함수처럼 호출하지 않습니다.
#ifdef CONFIG_ARM64_MTE

MTE는 포인터의 논리 태그와 메모리의 할당 태그를 검사해 일부 잘못된 접근을 찾는 기능입니다. 지원 빌드에만 비동기 오류 조회를 포함하며, 꺼지면 이 매크로의 본문이 사라집니다. 실제 CPU 지원은 아래 alternative에서 다시 확인합니다. 이 조건은 빌드 전처리 단계에서 포함할 코드를 고릅니다. CPU가 실행 중 전처리 조건을 검사하지는 않습니다.

132행 그림 · 전처리 단계

매크로 본문: check_mte_async_tcf

소스
처리 전전처리 전
이 줄의 효과#ifdef CONFIG_ARM64_MTE
CPU 레지스터
처리 전해당 없음
이 줄의 효과실행되는 CPU 명령이 없습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	.arch_extension lse

어셈블러가 뒤의 LSE 원자 명령 STSET를 받아들이도록 명령 확장을 허용합니다. 여기서 CPU 기능을 켜는 것이 아니라 MTE 오류 플래그를 원자적으로 OR할 명령의 빌드 문법을 준비합니다.

133행 그림 · 어셈블·링크 단계

매크로 본문: check_mte_async_tcf

이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_MTE

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과.arch_extension lse
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

alternative_if_not ARM64_MTE

실제 시스템에 ARM64_MTE가 없으면 다음 건너뛰기 분기가 남도록 합니다. MTE가 있으면 그 분기를 NOP로 바꿔 태그 오류 레지스터를 검사합니다. 이 매크로는 기본·교체 명령의 패치 정보를 만들며, CPU가 이 줄을 매번 if 조건처럼 실행하지 않습니다.

134행 그림 · CPU 기능에 따른 명령 패치

매크로 본문: check_mte_async_tcf

이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_MTE

패치 전 코드
처리 전기본 명령열
이 줄의 효과기능·정책에 맞는 기본 코드 또는 교체 코드

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 빌드할 때 패치 정보를 만들고 커널의 대체 코드 처리에서 명령을 선택합니다. 이 소스 줄을 CPU가 매번 if문으로 실행하는 것은 아닙니다.
	b	1f

1f (L149)부터 실행을 계속합니다. 돌아올 주소를 새로 적지 않으므로 일반 함수 호출과는 다른 제어 이동입니다.

135행 그림 · 호출과 실행 위치

매크로 본문: check_mte_async_tcf

이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_MTE

PC
이 명령을 실행하기 직전명령 직전 PC의 값
이 명령을 실행한 직후1f (L149)

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 새 복귀 주소를 LR에 기록하지 않습니다. RET 역시 메모리의 스택 값을 자동으로 꺼내는 명령은 아닙니다.
  • 매크로 check_mte_async_tcf의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
alternative_else_nop_endif

134행에서 시작한 ARM64_MTE 패치 묶음을 닫습니다. 조건에 맞는 본문을 사용하지 않는 쪽은 같은 길이의 NOP로 채워 뒤 명령의 배치를 유지합니다. 이 줄 자체가 CPU에서 조건을 검사하는 명령은 아닙니다.

136행 그림 · CPU 기능에 따른 명령 패치

매크로 본문: check_mte_async_tcf

이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_MTE

패치 전 코드
처리 전기본 명령열
이 줄의 효과기능·정책에 맞는 기본 코드 또는 교체 코드

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 빌드할 때 패치 정보를 만들고 커널의 대체 코드 처리에서 명령을 선택합니다. 이 소스 줄을 CPU가 매번 if문으로 실행하는 것은 아닙니다.
	tbz	\thread_sctlr, #(SCTLR_EL1_TCF0_SHIFT + 1), 1f

task의 TCF0 mode가 비동기 또는 비대칭 검사에 해당하는지 관련 비트를 확인합니다. 동기 mode만 사용하면 이 누적 오류 조회는 필요 없어 끝으로 갑니다. 이 분기는 지정 레지스터의 값이나 비트를 직접 검사하며 CMP가 남긴 NZCV를 읽거나 바꾸지 않습니다. 조건이 맞지 않으면 바로 다음 명령으로 진행합니다.

142행 그림 · 조건별 실행 위치

매크로 본문: check_mte_async_tcf

이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_MTE

읽는 값\thread_sctlr
PC
이 명령을 실행하기 직전명령 직전 PC의 값
이 명령을 실행한 직후명령 직전 \thread_sctlr의의 값 bit (SCTLR_EL1_TCF0_SHIFT + 1) = 0이면 1f (L149), 아니면 다음 명령

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 조건이 맞는 한 경로만 실행합니다. 이 분기 명령은 NZCV를 새로 계산하지 않습니다.
  • 매크로 check_mte_async_tcf의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	mrs_s	\tmp, SYS_TFSRE0_EL1

TFSRE0_EL1에서 EL0 태그 검사 실패의 누적 상태를 읽습니다. 비동기 실패는 원래 접근 즉시 일반 동기 fault로 처리되지 않을 수 있으므로 커널 진입 때 수거합니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.

143행 그림 · 시스템 레지스터 읽기

매크로 본문: check_mte_async_tcf

이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_MTE

읽는 값SYS_TFSRE0_EL1
\tmp
이 명령을 실행하기 직전명령 직전 \tmp의 값
이 명령을 실행한 직후명령 직전 SYS_TFSRE0_EL1의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 일반 RAM의 메모리 접근과 구분합니다.
  • 매크로 check_mte_async_tcf의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
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
	tbz	\tmp, #SYS_TFSR_EL1_TF0_SHIFT, 1f

EL0 태그 실패 표식 TF0가 없으면 task에 새 오류를 표시할 필요가 없습니다. 세워져 있을 때만 다음 원자적 플래그 갱신을 합니다. 이 분기는 지정 레지스터의 값이나 비트를 직접 검사하며 CMP가 남긴 NZCV를 읽거나 바꾸지 않습니다. 조건이 맞지 않으면 바로 다음 명령으로 진행합니다.

144행 그림 · 조건별 실행 위치

매크로 본문: check_mte_async_tcf

이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_MTE

읽는 값\tmp
PC
이 명령을 실행하기 직전명령 직전 PC의 값
이 명령을 실행한 직후명령 직전 \tmp의의 값 bit SYS_TFSR_EL1_TF0_SHIFT = 0이면 1f (L149), 아니면 다음 명령

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 조건이 맞는 한 경로만 실행합니다. 이 분기 명령은 NZCV를 새로 계산하지 않습니다.
  • 매크로 check_mte_async_tcf의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	mov	\tmp, #_TIF_MTE_ASYNC_FAULT

후속 사용자 복귀 경로가 비동기 MTE 오류를 처리하도록 _TIF_MTE_ASYNC_FAULT 마스크를 준비합니다. MOV는 \tmp의 기존 값을 대체합니다. 원본이 주소값이어도 그 주소의 메모리를 읽거나 복사하지 않습니다.

146행 그림 · 값 복사

매크로 본문: check_mte_async_tcf

이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_MTE

\tmp
이 명령을 실행하기 직전명령 직전 \tmp의 값
이 명령을 실행한 직후_TIF_MTE_ASYNC_FAULT

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 화살표는 값의 변화를 뜻합니다. 주소값을 옮겨도 그 주소의 메모리 내용은 복사하지 않습니다.
  • 매크로 check_mte_async_tcf의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	add	\ti_flags, tsk, #TSK_TI_FLAGS

tsk를 기준으로 TSK_TI_FLAGS을 더해 \ti_flags에 결과를 둡니다. 주소 계산에 쓰이는 경우에는 구조체의 필드나 다음 저장 위치를 찾는 동작입니다. 이 형태는 조건 플래그를 바꾸지 않습니다.

147행 그림 · 레지스터 상태

매크로 본문: check_mte_async_tcf

이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_MTE

읽는 값tsk
\ti_flags
이 명령을 실행하기 직전명령 직전 \ti_flags의 값
이 명령을 실행한 직후명령 직전 tsk의 값 + TSK_TI_FLAGS

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 결과는 목적 레지스터 폭에 맞습니다. 이 형식은 NZCV를 바꾸지 않습니다.
  • 매크로 check_mte_async_tcf의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	stset	\tmp, [\ti_flags]

task의 thread-info flags에 MTE 오류 비트를 원자적으로 OR합니다. 다른 비트 갱신을 잃지 않고 비동기 태그 실패를 커널의 후속 처리에 전달하기 위해 STSET를 사용합니다.

148행 그림 · 원자적 비트 설정

매크로 본문: check_mte_async_tcf

이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_MTE

읽는 값\tmp\ti_flags
메모리[\ti_flags]
이 명령을 실행하기 직전이전 메모리 값
이 명령을 실행한 직후이전 메모리 값 OR 명령 직전 \tmp의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 다른 CPU와 경쟁해도 OR 갱신을 원자적으로 수행합니다. 이전 값을 받을 목적 레지스터는 없습니다.
  • 매크로 check_mte_async_tcf의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
1:

여기에 1라는 위치 이름을 붙입니다. 다른 줄의 분기나 주소 계산이 이 위치를 찾아올 수 있습니다. 이름 자체가 CPU 작업을 추가하지는 않습니다.

149행 그림 · 위치에 이름 붙이기

매크로 본문: check_mte_async_tcf

이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_MTE

레이블
처리 전현재 배치 위치
이 줄의 효과1
CPU 레지스터
처리 전그대로
이 줄의 효과레이블 자체가 값을 바꾸지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

#endif

빌드 조건으로 나누었던 구간을 닫습니다. 대상:

150행 그림 · 전처리 단계

매크로 본문: check_mte_async_tcf

이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_MTE

소스
처리 전전처리 전
이 줄의 효과#endif
CPU 레지스터
처리 전해당 없음
이 줄의 효과실행되는 CPU 명령이 없습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	.endm

공통 작업의 정의를 여기서 마칩니다. RET가 아니라 빌드 도구가 읽는 경계입니다.

151행 그림 · 어셈블·링크 단계

매크로 본문: check_mte_async_tcf

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과.endm
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 끝 표기 자체가 RET나 분기를 실행하지는 않습니다.
	.macro clear_mte_async_tcf thread_sctlr

사용자 복귀 전에 이전 비동기 MTE 실패의 누적 상태를 지우는 매크로입니다. 인수 thread_sctlr로 비동기 수거 대상 mode인지 확인해 필요 없는 레지스터 작업은 생략합니다. 이 줄은 어셈블러 매크로 정의의 시작입니다. 본문은 매크로를 사용하는 위치에 펼쳐지며, 정의 자체가 런타임 함수 호출은 아닙니다.

154행 그림 · 어셈블·링크 단계

매크로 본문: clear_mte_async_tcf

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과.macro clear_mte_async_tcf thread_sctlr
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 뒤의 본문은 이 이름을 사용하는 위치에 펼쳐집니다. 정의 위치를 CPU가 함수처럼 호출하지 않습니다.
#ifdef CONFIG_ARM64_MTE

MTE 지원 빌드에만 오류 상태 정리 코드를 포함합니다. 이 기능이 꺼져 있으면 MTE 시스템 레지스터를 쓰는 본문은 생성되지 않습니다. 이 조건은 빌드 전처리 단계에서 포함할 코드를 고릅니다. CPU가 실행 중 전처리 조건을 검사하지는 않습니다.

155행 그림 · 전처리 단계

매크로 본문: clear_mte_async_tcf

소스
처리 전전처리 전
이 줄의 효과#ifdef CONFIG_ARM64_MTE
CPU 레지스터
처리 전해당 없음
이 줄의 효과실행되는 CPU 명령이 없습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

alternative_if ARM64_MTE

실제 MTE 지원이 확인된 시스템에만 아래 TFSRE0_EL1 정리가 남습니다. 빌드 지원과 실제 CPU 기능을 나누어 없는 레지스터에 접근하지 않게 합니다. 이 매크로는 기본·교체 명령의 패치 정보를 만들며, CPU가 이 줄을 매번 if 조건처럼 실행하지 않습니다.

156행 그림 · CPU 기능에 따른 명령 패치

매크로 본문: clear_mte_async_tcf

이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_MTE

패치 전 코드
처리 전기본 명령열
이 줄의 효과기능·정책에 맞는 기본 코드 또는 교체 코드

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 빌드할 때 패치 정보를 만들고 커널의 대체 코드 처리에서 명령을 선택합니다. 이 소스 줄을 CPU가 매번 if문으로 실행하는 것은 아닙니다.
	tbz	\thread_sctlr, #(SCTLR_EL1_TCF0_SHIFT + 1), 1f

\thread_sctlr의 (SCTLR_EL1_TCF0_SHIFT + 1)번 비트가 0이면 1f (L161)로 갑니다. 조건을 만족하지 않으면 바로 다음 명령을 읽습니다. 이 분기는 CMP의 결과가 아니라 지정 레지스터를 직접 검사합니다.

158행 그림 · 조건별 실행 위치

매크로 본문: clear_mte_async_tcf

이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_MTE

읽는 값\thread_sctlr
PC
이 명령을 실행하기 직전명령 직전 PC의 값
이 명령을 실행한 직후명령 직전 \thread_sctlr의의 값 bit (SCTLR_EL1_TCF0_SHIFT + 1) = 0이면 1f (L161), 아니면 다음 명령

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 조건이 맞는 한 경로만 실행합니다. 이 분기 명령은 NZCV를 새로 계산하지 않습니다.
  • 매크로 clear_mte_async_tcf의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	dsb	ish

기존 태그 검사 결과가 필요한 공유 범위에서 반영된 뒤 상태를 지우도록 DSB ISH를 수행합니다. 누적 실패가 늦게 나타나 다음 사용자 실행에 섞이는 것을 피하려는 순서입니다. DSB는 지정한 범위에 대해 앞선 작업의 완료를 기다린 뒤 다음 명령으로 진행하게 합니다.

159행 그림 · 동기화·캐시·대기 상태

매크로 본문: clear_mte_async_tcf

이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_MTE

작업 완료
이 명령을 실행하기 직전실행 전 상태
이 명령을 실행한 직후지정 범위의 앞선 작업 완료까지 기다림

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 매크로 clear_mte_async_tcf의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	msr_s	SYS_TFSRE0_EL1, xzr

TFSRE0_EL1의 누적 오류 표식을 0으로 지웁니다. 앞에서 task 플래그로 넘긴 오류와 이후 사용자 실행에서 발생할 새 오류를 구분하기 위해서입니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.

160행 그림 · 시스템 레지스터 쓰기

매크로 본문: clear_mte_async_tcf

이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_MTE

읽는 값xzr
SYS_TFSRE0_EL1
이 명령을 실행하기 직전명령 직전 SYS_TFSRE0_EL1의 값
이 명령을 실행한 직후0

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 접근 권한과 VHE 별칭 규칙에 따라 실제 대상이 정해집니다. 뒤의 ISB·DSB가 필요한지는 이 레지스터와 문맥에 따릅니다.
  • 매크로 clear_mte_async_tcf의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
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
1:

여기에 1라는 위치 이름을 붙입니다. 다른 줄의 분기나 주소 계산이 이 위치를 찾아올 수 있습니다. 이름 자체가 CPU 작업을 추가하지는 않습니다.

161행 그림 · 위치에 이름 붙이기

매크로 본문: clear_mte_async_tcf

이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_MTE

레이블
처리 전현재 배치 위치
이 줄의 효과1
CPU 레지스터
처리 전그대로
이 줄의 효과레이블 자체가 값을 바꾸지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

alternative_else_nop_endif

156행에서 시작한 ARM64_MTE 패치 묶음을 닫습니다. 조건에 맞는 본문을 사용하지 않는 쪽은 같은 길이의 NOP로 채워 뒤 명령의 배치를 유지합니다. 이 줄 자체가 CPU에서 조건을 검사하는 명령은 아닙니다.

162행 그림 · CPU 기능에 따른 명령 패치

매크로 본문: clear_mte_async_tcf

이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_MTE

패치 전 코드
처리 전기본 명령열
이 줄의 효과기능·정책에 맞는 기본 코드 또는 교체 코드

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 빌드할 때 패치 정보를 만들고 커널의 대체 코드 처리에서 명령을 선택합니다. 이 소스 줄을 CPU가 매번 if문으로 실행하는 것은 아닙니다.
#endif

빌드 조건으로 나누었던 구간을 닫습니다. 대상:

163행 그림 · 전처리 단계

매크로 본문: clear_mte_async_tcf

이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_MTE

소스
처리 전전처리 전
이 줄의 효과#endif
CPU 레지스터
처리 전해당 없음
이 줄의 효과실행되는 CPU 명령이 없습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	.endm

공통 작업의 정의를 여기서 마칩니다. RET가 아니라 빌드 도구가 읽는 경계입니다.

164행 그림 · 어셈블·링크 단계

매크로 본문: clear_mte_async_tcf

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과.endm
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 끝 표기 자체가 RET나 분기를 실행하지는 않습니다.
	.macro mte_set_gcr, mte_ctrl, tmp

MTE 태그 생성 제어 GCR_EL1을 task 정책에 맞게 만드는 매크로입니다. mte_ctrl에서 제외 태그 마스크를 꺼내 임의 태그 생성 제어와 함께 tmp에 만든 뒤 레지스터에 씁니다. 이 줄은 어셈블러 매크로 정의의 시작입니다. 본문은 매크로를 사용하는 위치에 펼쳐지며, 정의 자체가 런타임 함수 호출은 아닙니다.

166행 그림 · 어셈블·링크 단계

매크로 본문: mte_set_gcr

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과.macro mte_set_gcr, mte_ctrl, tmp
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 뒤의 본문은 이 이름을 사용하는 위치에 펼쳐집니다. 정의 위치를 CPU가 함수처럼 호출하지 않습니다.
#ifdef CONFIG_ARM64_MTE

MTE 지원 빌드에서만 태그 생성 제어를 갱신합니다. 꺼진 빌드에는 GCR_EL1 쓰기가 없어 일반 CPU 실행 경로에 영향을 주지 않습니다. 이 조건은 빌드 전처리 단계에서 포함할 코드를 고릅니다. CPU가 실행 중 전처리 조건을 검사하지는 않습니다.

167행 그림 · 전처리 단계

매크로 본문: mte_set_gcr

소스
처리 전전처리 전
이 줄의 효과#ifdef CONFIG_ARM64_MTE
CPU 레지스터
처리 전해당 없음
이 줄의 효과실행되는 CPU 명령이 없습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	ubfx	\tmp, \mte_ctrl, #MTE_CTRL_GCR_USER_EXCL_SHIFT, #16

task의 mte_ctrl에서 사용자 제외 태그 마스크 16비트를 꺼냅니다. 각 비트는 임의 태그 생성에서 제외할 태그를 나타내며 주소의 16비트 일부가 아닙니다. 추출한 필드는 결과의 하위 비트에 놓이고 나머지 상위 비트는 0으로 채워집니다.

168행 그림 · 비트 필드 추출

매크로 본문: mte_set_gcr

이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_MTE

읽는 값\mte_ctrl
\tmp
이 명령을 실행하기 직전명령 직전 \tmp의 값
이 명령을 실행한 직후명령 직전 \mte_ctrl의의 값 bit MTE_CTRL_GCR_USER_EXCL_SHIFT부터 16비트 → 0으로 확장

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 매크로 mte_set_gcr의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	orr	\tmp, \tmp, #SYS_GCR_EL1_RRND

GCR_EL1의 RRND 비트를 더해 지원되는 임의 태그 선택 동작을 사용하도록 합니다. 제외 마스크와 임의 생성 제어는 다른 필드입니다. ORR은 어느 한쪽이 1인 비트를 결과에 세우므로 원래 1이던 비트도 유지됩니다.

169행 그림 · 레지스터 상태

매크로 본문: mte_set_gcr

이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_MTE

읽는 값\tmp
\tmp
이 명령을 실행하기 직전명령 직전 \tmp의 값
이 명령을 실행한 직후명령 직전 \tmp의 값 OR SYS_GCR_EL1_RRND

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 결과는 목적 레지스터 폭에 맞습니다. 이 형식은 NZCV를 바꾸지 않습니다.
  • 매크로 mte_set_gcr의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	msr_s	SYS_GCR_EL1, \tmp

계산한 제외 태그와 생성 정책을 GCR_EL1에 설치합니다. 이후 태그 생성 명령이 현재 task에 맞는 정책을 사용하게 합니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.

170행 그림 · 시스템 레지스터 쓰기

매크로 본문: mte_set_gcr

이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_MTE

읽는 값\tmp
SYS_GCR_EL1
이 명령을 실행하기 직전명령 직전 SYS_GCR_EL1의 값
이 명령을 실행한 직후명령 직전 \tmp의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 접근 권한과 VHE 별칭 규칙에 따라 실제 대상이 정해집니다. 뒤의 ISB·DSB가 필요한지는 이 레지스터와 문맥에 따릅니다.
  • 매크로 mte_set_gcr의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
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
#endif

빌드 조건으로 나누었던 구간을 닫습니다. 대상:

171행 그림 · 전처리 단계

매크로 본문: mte_set_gcr

이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_MTE

소스
처리 전전처리 전
이 줄의 효과#endif
CPU 레지스터
처리 전해당 없음
이 줄의 효과실행되는 CPU 명령이 없습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	.endm

공통 작업의 정의를 여기서 마칩니다. RET가 아니라 빌드 도구가 읽는 경계입니다.

172행 그림 · 어셈블·링크 단계

매크로 본문: mte_set_gcr

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과.endm
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 끝 표기 자체가 RET나 분기를 실행하지는 않습니다.
	.macro mte_set_kernel_gcr, tmp, tmp2

커널의 하드웨어 태그 KASAN이 사용하는 태그 생성 정책으로 GCR을 전환하는 매크로입니다. 사용자 정책을 그대로 쓰면 커널이 기대한 태그 범위와 달라질 수 있습니다. 이 줄은 어셈블러 매크로 정의의 시작입니다. 본문은 매크로를 사용하는 위치에 펼쳐지며, 정의 자체가 런타임 함수 호출은 아닙니다.

174행 그림 · 어셈블·링크 단계

매크로 본문: mte_set_kernel_gcr

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과.macro mte_set_kernel_gcr, tmp, tmp2
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 뒤의 본문은 이 이름을 사용하는 위치에 펼쳐집니다. 정의 위치를 CPU가 함수처럼 호출하지 않습니다.
#ifdef CONFIG_KASAN_HW_TAGS

KASAN_HW_TAGS는 Arm MTE 하드웨어로 커널 메모리 태그를 검사하는 KASAN 방식입니다. 켜진 빌드에만 커널용 GCR 전환을 포함하고, 꺼지면 이 매크로는 비어 있습니다. 소프트웨어 shadow를 사용하는 GENERIC/SW_TAGS와 구분합니다. 이 조건은 빌드 전처리 단계에서 포함할 코드를 고릅니다. CPU가 실행 중 전처리 조건을 검사하지는 않습니다.

175행 그림 · 전처리 단계

매크로 본문: mte_set_kernel_gcr

소스
처리 전전처리 전
이 줄의 효과#ifdef CONFIG_KASAN_HW_TAGS
CPU 레지스터
처리 전해당 없음
이 줄의 효과실행되는 CPU 명령이 없습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

alternative_cb	ARM64_ALWAYS_SYSTEM, kasan_hw_tags_enable

KASAN 하드웨어 태그 검사를 런타임에도 활성화했는지 보는 callback입니다. 활성인 경우 다음 skip 분기를 NOP로 바꾸어 커널 GCR을 설치하고, 비활성이면 건너뜁니다. 이 매크로는 기본·교체 명령의 패치 정보를 만들며, CPU가 이 줄을 매번 if 조건처럼 실행하지 않습니다.

176행 그림 · CPU 기능에 따른 명령 패치

매크로 본문: mte_set_kernel_gcr

이 줄을 포함하는 조건: #ifdef CONFIG_KASAN_HW_TAGS

패치 전 코드
처리 전기본 명령열
이 줄의 효과기능·정책에 맞는 기본 코드 또는 교체 코드

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 빌드할 때 패치 정보를 만들고 커널의 대체 코드 처리에서 명령을 선택합니다. 이 소스 줄을 CPU가 매번 if문으로 실행하는 것은 아닙니다.
	b	1f

1f (L181)부터 실행을 계속합니다. 돌아올 주소를 새로 적지 않으므로 일반 함수 호출과는 다른 제어 이동입니다.

177행 그림 · 호출과 실행 위치

매크로 본문: mte_set_kernel_gcr

이 줄을 포함하는 조건: #ifdef CONFIG_KASAN_HW_TAGS

PC
이 명령을 실행하기 직전명령 직전 PC의 값
이 명령을 실행한 직후1f (L181)

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 새 복귀 주소를 LR에 기록하지 않습니다. RET 역시 메모리의 스택 값을 자동으로 꺼내는 명령은 아닙니다.
  • 매크로 mte_set_kernel_gcr의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
alternative_cb_end

176행에서 시작한 ARM64_ALWAYS_SYSTEM, kasan_hw_tags_enable 패치 묶음을 닫습니다. callback이 선택한 분기·NOP·펌웨어 호출 명령이 같은 자리를 사용하도록 교체 범위를 확정합니다. callback 함수가 여기서 매번 호출되는 것은 아닙니다.

178행 그림 · CPU 기능에 따른 명령 패치

매크로 본문: mte_set_kernel_gcr

이 줄을 포함하는 조건: #ifdef CONFIG_KASAN_HW_TAGS

패치 전 코드
처리 전기본 명령열
이 줄의 효과기능·정책에 맞는 기본 코드 또는 교체 코드

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 빌드할 때 패치 정보를 만들고 커널의 대체 코드 처리에서 명령을 선택합니다. 이 소스 줄을 CPU가 매번 if문으로 실행하는 것은 아닙니다.
	mov	\tmp, KERNEL_GCR_EL1

커널 태그 KASAN이 사용할 GCR_EL1의 제외 마스크와 생성 설정 KERNEL_GCR_EL1을 준비합니다. 사용자 task의 mte_ctrl 값과 의도적으로 다른 정책입니다. MOV는 \tmp의 기존 값을 대체합니다. 원본이 주소값이어도 그 주소의 메모리를 읽거나 복사하지 않습니다.

179행 그림 · 값 복사

매크로 본문: mte_set_kernel_gcr

이 줄을 포함하는 조건: #ifdef CONFIG_KASAN_HW_TAGS

\tmp
이 명령을 실행하기 직전명령 직전 \tmp의 값
이 명령을 실행한 직후KERNEL_GCR_EL1

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 화살표는 값의 변화를 뜻합니다. 주소값을 옮겨도 그 주소의 메모리 내용은 복사하지 않습니다.
  • 매크로 mte_set_kernel_gcr의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	msr_s	SYS_GCR_EL1, \tmp

커널 메모리용 태그 생성 제어를 실제 GCR_EL1에 씁니다. 사용자 진입에서 커널 태그 정책으로 넘어오는 지점입니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.

180행 그림 · 시스템 레지스터 쓰기

매크로 본문: mte_set_kernel_gcr

이 줄을 포함하는 조건: #ifdef CONFIG_KASAN_HW_TAGS

읽는 값\tmp
SYS_GCR_EL1
이 명령을 실행하기 직전명령 직전 SYS_GCR_EL1의 값
이 명령을 실행한 직후명령 직전 \tmp의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 접근 권한과 VHE 별칭 규칙에 따라 실제 대상이 정해집니다. 뒤의 ISB·DSB가 필요한지는 이 레지스터와 문맥에 따릅니다.
  • 매크로 mte_set_kernel_gcr의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
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
1:

여기에 1라는 위치 이름을 붙입니다. 다른 줄의 분기나 주소 계산이 이 위치를 찾아올 수 있습니다. 이름 자체가 CPU 작업을 추가하지는 않습니다.

181행 그림 · 위치에 이름 붙이기

매크로 본문: mte_set_kernel_gcr

이 줄을 포함하는 조건: #ifdef CONFIG_KASAN_HW_TAGS

레이블
처리 전현재 배치 위치
이 줄의 효과1
CPU 레지스터
처리 전그대로
이 줄의 효과레이블 자체가 값을 바꾸지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

#endif

빌드 조건으로 나누었던 구간을 닫습니다. 대상:

182행 그림 · 전처리 단계

매크로 본문: mte_set_kernel_gcr

이 줄을 포함하는 조건: #ifdef CONFIG_KASAN_HW_TAGS

소스
처리 전전처리 전
이 줄의 효과#endif
CPU 레지스터
처리 전해당 없음
이 줄의 효과실행되는 CPU 명령이 없습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	.endm

공통 작업의 정의를 여기서 마칩니다. RET가 아니라 빌드 도구가 읽는 경계입니다.

183행 그림 · 어셈블·링크 단계

매크로 본문: mte_set_kernel_gcr

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과.endm
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 끝 표기 자체가 RET나 분기를 실행하지는 않습니다.
	.macro mte_set_user_gcr, tsk, tmp, tmp2

커널 하드웨어 태그 KASAN 때문에 바뀐 GCR을 사용자 task 정책으로 되돌리는 매크로입니다. tsk에서 mte_ctrl을 읽어 mte_set_gcr에 넘기며 tmp·tmp2를 계산용으로 씁니다. 이 줄은 어셈블러 매크로 정의의 시작입니다. 본문은 매크로를 사용하는 위치에 펼쳐지며, 정의 자체가 런타임 함수 호출은 아닙니다.

185행 그림 · 어셈블·링크 단계

매크로 본문: mte_set_user_gcr

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과.macro mte_set_user_gcr, tsk, tmp, tmp2
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 뒤의 본문은 이 이름을 사용하는 위치에 펼쳐집니다. 정의 위치를 CPU가 함수처럼 호출하지 않습니다.
#ifdef CONFIG_KASAN_HW_TAGS

KASAN_HW_TAGS 구성에서는 커널 실행 중 GCR을 바꾸므로 사용자 복귀 때 task의 태그 생성 정책을 복원해야 합니다. 이 구성이 아니면 해당 전환 코드 자체를 생략합니다. 이 조건은 빌드 전처리 단계에서 포함할 코드를 고릅니다. CPU가 실행 중 전처리 조건을 검사하지는 않습니다.

186행 그림 · 전처리 단계

매크로 본문: mte_set_user_gcr

소스
처리 전전처리 전
이 줄의 효과#ifdef CONFIG_KASAN_HW_TAGS
CPU 레지스터
처리 전해당 없음
이 줄의 효과실행되는 CPU 명령이 없습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

alternative_cb	ARM64_ALWAYS_SYSTEM, kasan_hw_tags_enable

런타임 하드웨어 태그 KASAN이 실제 활성일 때만 사용자 GCR 복원 경로를 남기는 callback입니다. 커널이 GCR을 바꾸지 않은 경우 불필요한 복원을 피합니다. 이 매크로는 기본·교체 명령의 패치 정보를 만들며, CPU가 이 줄을 매번 if 조건처럼 실행하지 않습니다.

187행 그림 · CPU 기능에 따른 명령 패치

매크로 본문: mte_set_user_gcr

이 줄을 포함하는 조건: #ifdef CONFIG_KASAN_HW_TAGS

패치 전 코드
처리 전기본 명령열
이 줄의 효과기능·정책에 맞는 기본 코드 또는 교체 코드

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 빌드할 때 패치 정보를 만들고 커널의 대체 코드 처리에서 명령을 선택합니다. 이 소스 줄을 CPU가 매번 if문으로 실행하는 것은 아닙니다.
	b	1f

1f (L193)부터 실행을 계속합니다. 돌아올 주소를 새로 적지 않으므로 일반 함수 호출과는 다른 제어 이동입니다.

188행 그림 · 호출과 실행 위치

매크로 본문: mte_set_user_gcr

이 줄을 포함하는 조건: #ifdef CONFIG_KASAN_HW_TAGS

PC
이 명령을 실행하기 직전명령 직전 PC의 값
이 명령을 실행한 직후1f (L193)

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 새 복귀 주소를 LR에 기록하지 않습니다. RET 역시 메모리의 스택 값을 자동으로 꺼내는 명령은 아닙니다.
  • 매크로 mte_set_user_gcr의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
alternative_cb_end

187행에서 시작한 ARM64_ALWAYS_SYSTEM, kasan_hw_tags_enable 패치 묶음을 닫습니다. callback이 선택한 분기·NOP·펌웨어 호출 명령이 같은 자리를 사용하도록 교체 범위를 확정합니다. callback 함수가 여기서 매번 호출되는 것은 아닙니다.

189행 그림 · CPU 기능에 따른 명령 패치

매크로 본문: mte_set_user_gcr

이 줄을 포함하는 조건: #ifdef CONFIG_KASAN_HW_TAGS

패치 전 코드
처리 전기본 명령열
이 줄의 효과기능·정책에 맞는 기본 코드 또는 교체 코드

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 빌드할 때 패치 정보를 만들고 커널의 대체 코드 처리에서 명령을 선택합니다. 이 소스 줄을 CPU가 매번 if문으로 실행하는 것은 아닙니다.
	ldr	\tmp, [\tsk, #THREAD_MTE_CTRL]

현재 task의 THREAD_MTE_CTRL에서 사용자 태그 생성 정책을 읽습니다. 이전 task나 커널의 제외 마스크를 사용자 실행에 남기지 않으려는 입력입니다. 실제로 \tsk + (THREAD_MTE_CTRL) 주소의 메모리 8바이트를 \tmp에 읽습니다. 이 주소 형식은 기준 레지스터 \tsk 자체를 갱신하지 않습니다.

190행 그림 · 메모리 읽기

매크로 본문: mte_set_user_gcr

이 줄을 포함하는 조건: #ifdef CONFIG_KASAN_HW_TAGS

읽는 값\tsk
메모리[이전 \tsk + (THREAD_MTE_CTRL)] · 8바이트
이 명령을 실행하기 직전M0 (이 주소에 저장된 값)
이 명령을 실행한 직후M0 (이 주소에 저장된 값)
\tmp
이 명령을 실행하기 직전명령 직전 \tmp의 값
이 명령을 실행한 직후M0

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 주소 계산에 의한 기준 레지스터 갱신은 없습니다. 다만 LDR/LDP의 목적 레지스터가 기준 레지스터와 같으면 읽은 값이 그 레지스터를 덮어씁니다.
  • 매크로 mte_set_user_gcr의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	mte_set_gcr \tmp, \tmp2

현재 task에서 읽은 MTE 제어값으로 사용자 제외 태그 마스크와 RRND를 조합해 GCR_EL1에 기록합니다. 커널 하드웨어 태그 KASAN의 생성 정책을 사용자 복귀에 남기지 않도록 되돌리는 호출입니다.

192행 그림 · 매크로 안의 상태 변화

매크로 본문: mte_set_user_gcr

이 줄을 포함하는 조건: #ifdef CONFIG_KASAN_HW_TAGS

입력
이 명령을 실행하기 직전호출 직전 값
이 명령을 실행한 직후\tmp, \tmp2
주요 결과
이 명령을 실행하기 직전본문 실행 전
이 명령을 실행한 직후태스크의 제외 마스크와 RRND를 조합해 GCR_EL1에 기록합니다.

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 여러 명령과 내부 분기를 묶은 그림입니다. 작업 레지스터를 모두 보존한다는 뜻은 아니며, 정확한 변경 순서는 아래 매크로 본문과 줄별 그림에서 확인합니다.
  • 매크로 mte_set_user_gcr의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
mte_set_gcr 매크로 본문 · 166행

arch/arm64/kernel/entry.S · Linux v6.18.37

.macro mte_set_gcr, mte_ctrl, tmp #ifdef CONFIG_ARM64_MTE ubfx \tmp, \mte_ctrl, #MTE_CTRL_GCR_USER_EXCL_SHIFT, #16 orr \tmp, \tmp, #SYS_GCR_EL1_RRND msr_s SYS_GCR_EL1, \tmp #endif .endm
1:

여기에 1라는 위치 이름을 붙입니다. 다른 줄의 분기나 주소 계산이 이 위치를 찾아올 수 있습니다. 이름 자체가 CPU 작업을 추가하지는 않습니다.

193행 그림 · 위치에 이름 붙이기

매크로 본문: mte_set_user_gcr

이 줄을 포함하는 조건: #ifdef CONFIG_KASAN_HW_TAGS

레이블
처리 전현재 배치 위치
이 줄의 효과1
CPU 레지스터
처리 전그대로
이 줄의 효과레이블 자체가 값을 바꾸지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

#endif

빌드 조건으로 나누었던 구간을 닫습니다. 대상:

194행 그림 · 전처리 단계

매크로 본문: mte_set_user_gcr

이 줄을 포함하는 조건: #ifdef CONFIG_KASAN_HW_TAGS

소스
처리 전전처리 전
이 줄의 효과#endif
CPU 레지스터
처리 전해당 없음
이 줄의 효과실행되는 CPU 명령이 없습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	.endm

공통 작업의 정의를 여기서 마칩니다. RET가 아니라 빌드 도구가 읽는 경계입니다.

195행 그림 · 어셈블·링크 단계

매크로 본문: mte_set_user_gcr

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과.endm
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 끝 표기 자체가 RET나 분기를 실행하지는 않습니다.

kernel_entry: x0–x29 저장과 current 설정

원본 197–280행

예외가 끝나면 원래 프로그램의 값을 되돌립니다

예외가 끝나면 원래 프로그램의 값을 되돌립니다의 세 단계 비교
A와 B는 설명용 값입니다. 오른쪽 화살표는 메모리 저장, 왼쪽은 레지스터 복원을 뜻합니다. 여러 명령을 단계별로 묶었습니다.

움직임으로 따라가기 · 재생하거나 한 단계씩 고정해 보실 수 있습니다.

예외가 끝나면 원래 프로그램의 값을 되돌립니다 · 1단계

1단계 고정

GIF 원본 열기

그림의 각 단계를 글로 읽기

1. 예외 직후

CPU가 모든 일반 레지스터를 자동으로 스택에 저장하지는 않습니다.

2. kernel_entry에서 저장

그림은 x0와 x1을 대표로 보여 줍니다. 실제 보존 범위는 더 넓습니다.

3. kernel_exit에서 복원

일반 레지스터 복원과 예외 복귀 정보 설정을 마친 뒤 ERET합니다.

	.macro	kernel_entry, el, regsize = 64

예외 프레임에 범용 레지스터·SP·PC·PSTATE를 저장하고 current, 보안 제어, unwind 메타데이터를 준비하는 진입 매크로입니다. el=0이면 사용자 진입용 전환을 추가하고 regsize=32이면 AArch32 인수 폭을 정리합니다. 이 줄은 어셈블러 매크로 정의의 시작입니다. 본문은 매크로를 사용하는 위치에 펼쳐지며, 정의 자체가 런타임 함수 호출은 아닙니다.

197행 그림 · 어셈블·링크 단계

매크로 본문: kernel_entry

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과.macro kernel_entry, el, regsize = 64
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 뒤의 본문은 이 이름을 사용하는 위치에 펼쳐집니다. 정의 위치를 CPU가 함수처럼 호출하지 않습니다.
	.if	\el == 0

어셈블 단계에서 인자나 식을 검사해 필요한 명령만 만듭니다. \el == 0

198행 그림 · 어셈블·링크 단계

매크로 본문: kernel_entry

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과.if \el == 0
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	alternative_insn nop, SET_PSTATE_DIT(1), ARM64_HAS_DIT

DIT 지원 CPU에서는 EL0에서 들어올 때 PSTATE.DIT를 1로 만드는 명령으로 NOP를 교체합니다. DIT는 지원 명령의 실행 시간이 데이터 값에 의존하는 것을 제한하는 기능이며 모든 코드의 절대 상수 시간을 보장한다는 뜻은 아닙니다. 이 매크로는 기본·교체 명령의 패치 정보를 만들며, CPU가 이 줄을 매번 if 조건처럼 실행하지 않습니다.

199행 그림 · CPU 기능에 따른 명령 패치

매크로 본문: kernel_entry

이 줄을 포함하는 조건: .if \el == 0

패치 전 코드
처리 전기본 명령열
이 줄의 효과기능·정책에 맞는 기본 코드 또는 교체 코드

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 빌드할 때 패치 정보를 만들고 커널의 대체 코드 처리에서 명령을 선택합니다. 이 소스 줄을 CPU가 매번 if문으로 실행하는 것은 아닙니다.
	.endif

어셈블 조건에 따라 생성할 코드를 나누던 범위가 끝납니다.

200행 그림 · 어셈블·링크 단계

매크로 본문: kernel_entry

이 줄을 포함하는 조건: .if \el == 0

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과.endif
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	.if	\regsize == 32

어셈블 단계에서 인자나 식을 검사해 필요한 명령만 만듭니다. \regsize == 32

201행 그림 · 어셈블·링크 단계

매크로 본문: kernel_entry

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과.if \regsize == 32
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	mov	w0, w0				// zero upper 32 bits of x0

w0에 w0에 들어 있는 값을 넣습니다. 원래 w0의 내용은 덮어쓰며, 다른 레지스터나 그 주소의 메모리까지 복사하지는 않습니다. W 레지스터에 쓰므로 대응 X 레지스터의 상위 절반도 0이 됩니다.

32비트 사용자 x0의 상위 32비트를 확실히 0으로 만듭니다.

202행 그림 · 값 복사

매크로 본문: kernel_entry

이 줄을 포함하는 조건: .if \regsize == 32

읽는 값w0
w0
이 명령을 실행하기 직전명령 직전 w0의 값
이 명령을 실행한 직후명령 직전 w0의 값
x0[63:32]
이 명령을 실행하기 직전명령 직전 x0[63:32]의 값
이 명령을 실행한 직후0

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 화살표는 값의 변화를 뜻합니다. 주소값을 옮겨도 그 주소의 메모리 내용은 복사하지 않습니다.
  • 매크로 kernel_entry의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	.endif

어셈블 조건에 따라 생성할 코드를 나누던 범위가 끝납니다.

203행 그림 · 어셈블·링크 단계

매크로 본문: kernel_entry

이 줄을 포함하는 조건: .if \regsize == 32

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과.endif
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	stp	x0, x1, [sp, #16 * 0]

x0, x1의 값을 sp를 기준으로 16 * 0만큼 계산한 메모리 위치에 기록합니다. 레지스터 값을 나중에 다시 쓰거나 다른 코드가 읽을 수 있도록 메모리에 남기는 줄입니다. 첫 위치에는 x0, 그보다 8바이트 높은 위치에는 x1의 값이 들어갑니다. 이 스택 위치는 현재 함수 또는 예외 처리의 보관 공간입니다.

204행 그림 · 메모리 쓰기

매크로 본문: kernel_entry

읽는 값spx0x1
메모리[이전 sp + (16 * 0)] · 8바이트
이 명령을 실행하기 직전기존 메모리 값
이 명령을 실행한 직후명령 직전 x0의 값
메모리[이전 sp + (16 * 0) + 8] · 8바이트
이 명령을 실행하기 직전기존 메모리 값
이 명령을 실행한 직후명령 직전 x1의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 주소 계산에 의한 기준 레지스터 갱신은 없습니다. 다만 LDR/LDP의 목적 레지스터가 기준 레지스터와 같으면 읽은 값이 그 레지스터를 덮어씁니다.
  • 메모리 칸은 낮은 주소부터 나열했습니다. SP를 줄이면 더 낮은 주소의 공간을 사용합니다. 화면의 아래쪽이 곧 낮은 주소라는 뜻은 아닙니다.
  • 매크로 kernel_entry의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	stp	x2, x3, [sp, #16 * 1]

x2, x3의 값을 sp를 기준으로 16 * 1만큼 계산한 메모리 위치에 기록합니다. 레지스터 값을 나중에 다시 쓰거나 다른 코드가 읽을 수 있도록 메모리에 남기는 줄입니다. 첫 위치에는 x2, 그보다 8바이트 높은 위치에는 x3의 값이 들어갑니다. 이 스택 위치는 현재 함수 또는 예외 처리의 보관 공간입니다.

205행 그림 · 메모리 쓰기

매크로 본문: kernel_entry

읽는 값spx2x3
메모리[이전 sp + (16 * 1)] · 8바이트
이 명령을 실행하기 직전기존 메모리 값
이 명령을 실행한 직후명령 직전 x2의 값
메모리[이전 sp + (16 * 1) + 8] · 8바이트
이 명령을 실행하기 직전기존 메모리 값
이 명령을 실행한 직후명령 직전 x3의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 주소 계산에 의한 기준 레지스터 갱신은 없습니다. 다만 LDR/LDP의 목적 레지스터가 기준 레지스터와 같으면 읽은 값이 그 레지스터를 덮어씁니다.
  • 메모리 칸은 낮은 주소부터 나열했습니다. SP를 줄이면 더 낮은 주소의 공간을 사용합니다. 화면의 아래쪽이 곧 낮은 주소라는 뜻은 아닙니다.
  • 매크로 kernel_entry의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	stp	x4, x5, [sp, #16 * 2]

x4, x5의 값을 sp를 기준으로 16 * 2만큼 계산한 메모리 위치에 기록합니다. 레지스터 값을 나중에 다시 쓰거나 다른 코드가 읽을 수 있도록 메모리에 남기는 줄입니다. 첫 위치에는 x4, 그보다 8바이트 높은 위치에는 x5의 값이 들어갑니다. 이 스택 위치는 현재 함수 또는 예외 처리의 보관 공간입니다.

206행 그림 · 메모리 쓰기

매크로 본문: kernel_entry

읽는 값spx4x5
메모리[이전 sp + (16 * 2)] · 8바이트
이 명령을 실행하기 직전기존 메모리 값
이 명령을 실행한 직후명령 직전 x4의 값
메모리[이전 sp + (16 * 2) + 8] · 8바이트
이 명령을 실행하기 직전기존 메모리 값
이 명령을 실행한 직후명령 직전 x5의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 주소 계산에 의한 기준 레지스터 갱신은 없습니다. 다만 LDR/LDP의 목적 레지스터가 기준 레지스터와 같으면 읽은 값이 그 레지스터를 덮어씁니다.
  • 메모리 칸은 낮은 주소부터 나열했습니다. SP를 줄이면 더 낮은 주소의 공간을 사용합니다. 화면의 아래쪽이 곧 낮은 주소라는 뜻은 아닙니다.
  • 매크로 kernel_entry의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	stp	x6, x7, [sp, #16 * 3]

x6, x7의 값을 sp를 기준으로 16 * 3만큼 계산한 메모리 위치에 기록합니다. 레지스터 값을 나중에 다시 쓰거나 다른 코드가 읽을 수 있도록 메모리에 남기는 줄입니다. 첫 위치에는 x6, 그보다 8바이트 높은 위치에는 x7의 값이 들어갑니다. 이 스택 위치는 현재 함수 또는 예외 처리의 보관 공간입니다.

207행 그림 · 메모리 쓰기

매크로 본문: kernel_entry

읽는 값spx6x7
메모리[이전 sp + (16 * 3)] · 8바이트
이 명령을 실행하기 직전기존 메모리 값
이 명령을 실행한 직후명령 직전 x6의 값
메모리[이전 sp + (16 * 3) + 8] · 8바이트
이 명령을 실행하기 직전기존 메모리 값
이 명령을 실행한 직후명령 직전 x7의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 주소 계산에 의한 기준 레지스터 갱신은 없습니다. 다만 LDR/LDP의 목적 레지스터가 기준 레지스터와 같으면 읽은 값이 그 레지스터를 덮어씁니다.
  • 메모리 칸은 낮은 주소부터 나열했습니다. SP를 줄이면 더 낮은 주소의 공간을 사용합니다. 화면의 아래쪽이 곧 낮은 주소라는 뜻은 아닙니다.
  • 매크로 kernel_entry의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	stp	x8, x9, [sp, #16 * 4]

x8, x9의 값을 sp를 기준으로 16 * 4만큼 계산한 메모리 위치에 기록합니다. 레지스터 값을 나중에 다시 쓰거나 다른 코드가 읽을 수 있도록 메모리에 남기는 줄입니다. 첫 위치에는 x8, 그보다 8바이트 높은 위치에는 x9의 값이 들어갑니다. 이 스택 위치는 현재 함수 또는 예외 처리의 보관 공간입니다.

208행 그림 · 메모리 쓰기

매크로 본문: kernel_entry

읽는 값spx8x9
메모리[이전 sp + (16 * 4)] · 8바이트
이 명령을 실행하기 직전기존 메모리 값
이 명령을 실행한 직후명령 직전 x8의 값
메모리[이전 sp + (16 * 4) + 8] · 8바이트
이 명령을 실행하기 직전기존 메모리 값
이 명령을 실행한 직후명령 직전 x9의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 주소 계산에 의한 기준 레지스터 갱신은 없습니다. 다만 LDR/LDP의 목적 레지스터가 기준 레지스터와 같으면 읽은 값이 그 레지스터를 덮어씁니다.
  • 메모리 칸은 낮은 주소부터 나열했습니다. SP를 줄이면 더 낮은 주소의 공간을 사용합니다. 화면의 아래쪽이 곧 낮은 주소라는 뜻은 아닙니다.
  • 매크로 kernel_entry의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	stp	x10, x11, [sp, #16 * 5]

x10, x11의 값을 sp를 기준으로 16 * 5만큼 계산한 메모리 위치에 기록합니다. 레지스터 값을 나중에 다시 쓰거나 다른 코드가 읽을 수 있도록 메모리에 남기는 줄입니다. 첫 위치에는 x10, 그보다 8바이트 높은 위치에는 x11의 값이 들어갑니다. 이 스택 위치는 현재 함수 또는 예외 처리의 보관 공간입니다.

209행 그림 · 메모리 쓰기

매크로 본문: kernel_entry

읽는 값spx10x11
메모리[이전 sp + (16 * 5)] · 8바이트
이 명령을 실행하기 직전기존 메모리 값
이 명령을 실행한 직후명령 직전 x10의 값
메모리[이전 sp + (16 * 5) + 8] · 8바이트
이 명령을 실행하기 직전기존 메모리 값
이 명령을 실행한 직후명령 직전 x11의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 주소 계산에 의한 기준 레지스터 갱신은 없습니다. 다만 LDR/LDP의 목적 레지스터가 기준 레지스터와 같으면 읽은 값이 그 레지스터를 덮어씁니다.
  • 메모리 칸은 낮은 주소부터 나열했습니다. SP를 줄이면 더 낮은 주소의 공간을 사용합니다. 화면의 아래쪽이 곧 낮은 주소라는 뜻은 아닙니다.
  • 매크로 kernel_entry의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	stp	x12, x13, [sp, #16 * 6]

x12, x13의 값을 sp를 기준으로 16 * 6만큼 계산한 메모리 위치에 기록합니다. 레지스터 값을 나중에 다시 쓰거나 다른 코드가 읽을 수 있도록 메모리에 남기는 줄입니다. 첫 위치에는 x12, 그보다 8바이트 높은 위치에는 x13의 값이 들어갑니다. 이 스택 위치는 현재 함수 또는 예외 처리의 보관 공간입니다.

210행 그림 · 메모리 쓰기

매크로 본문: kernel_entry

읽는 값spx12x13
메모리[이전 sp + (16 * 6)] · 8바이트
이 명령을 실행하기 직전기존 메모리 값
이 명령을 실행한 직후명령 직전 x12의 값
메모리[이전 sp + (16 * 6) + 8] · 8바이트
이 명령을 실행하기 직전기존 메모리 값
이 명령을 실행한 직후명령 직전 x13의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 주소 계산에 의한 기준 레지스터 갱신은 없습니다. 다만 LDR/LDP의 목적 레지스터가 기준 레지스터와 같으면 읽은 값이 그 레지스터를 덮어씁니다.
  • 메모리 칸은 낮은 주소부터 나열했습니다. SP를 줄이면 더 낮은 주소의 공간을 사용합니다. 화면의 아래쪽이 곧 낮은 주소라는 뜻은 아닙니다.
  • 매크로 kernel_entry의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	stp	x14, x15, [sp, #16 * 7]

x14, x15의 값을 sp를 기준으로 16 * 7만큼 계산한 메모리 위치에 기록합니다. 레지스터 값을 나중에 다시 쓰거나 다른 코드가 읽을 수 있도록 메모리에 남기는 줄입니다. 첫 위치에는 x14, 그보다 8바이트 높은 위치에는 x15의 값이 들어갑니다. 이 스택 위치는 현재 함수 또는 예외 처리의 보관 공간입니다.

211행 그림 · 메모리 쓰기

매크로 본문: kernel_entry

읽는 값spx14x15
메모리[이전 sp + (16 * 7)] · 8바이트
이 명령을 실행하기 직전기존 메모리 값
이 명령을 실행한 직후명령 직전 x14의 값
메모리[이전 sp + (16 * 7) + 8] · 8바이트
이 명령을 실행하기 직전기존 메모리 값
이 명령을 실행한 직후명령 직전 x15의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 주소 계산에 의한 기준 레지스터 갱신은 없습니다. 다만 LDR/LDP의 목적 레지스터가 기준 레지스터와 같으면 읽은 값이 그 레지스터를 덮어씁니다.
  • 메모리 칸은 낮은 주소부터 나열했습니다. SP를 줄이면 더 낮은 주소의 공간을 사용합니다. 화면의 아래쪽이 곧 낮은 주소라는 뜻은 아닙니다.
  • 매크로 kernel_entry의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	stp	x16, x17, [sp, #16 * 8]

x16, x17의 값을 sp를 기준으로 16 * 8만큼 계산한 메모리 위치에 기록합니다. 레지스터 값을 나중에 다시 쓰거나 다른 코드가 읽을 수 있도록 메모리에 남기는 줄입니다. 첫 위치에는 x16, 그보다 8바이트 높은 위치에는 x17의 값이 들어갑니다. 이 스택 위치는 현재 함수 또는 예외 처리의 보관 공간입니다.

212행 그림 · 메모리 쓰기

매크로 본문: kernel_entry

읽는 값spx16x17
메모리[이전 sp + (16 * 8)] · 8바이트
이 명령을 실행하기 직전기존 메모리 값
이 명령을 실행한 직후명령 직전 x16의 값
메모리[이전 sp + (16 * 8) + 8] · 8바이트
이 명령을 실행하기 직전기존 메모리 값
이 명령을 실행한 직후명령 직전 x17의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 주소 계산에 의한 기준 레지스터 갱신은 없습니다. 다만 LDR/LDP의 목적 레지스터가 기준 레지스터와 같으면 읽은 값이 그 레지스터를 덮어씁니다.
  • 메모리 칸은 낮은 주소부터 나열했습니다. SP를 줄이면 더 낮은 주소의 공간을 사용합니다. 화면의 아래쪽이 곧 낮은 주소라는 뜻은 아닙니다.
  • 매크로 kernel_entry의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	stp	x18, x19, [sp, #16 * 9]

x18, x19의 값을 sp를 기준으로 16 * 9만큼 계산한 메모리 위치에 기록합니다. 레지스터 값을 나중에 다시 쓰거나 다른 코드가 읽을 수 있도록 메모리에 남기는 줄입니다. 첫 위치에는 x18, 그보다 8바이트 높은 위치에는 x19의 값이 들어갑니다. 이 스택 위치는 현재 함수 또는 예외 처리의 보관 공간입니다.

213행 그림 · 메모리 쓰기

매크로 본문: kernel_entry

읽는 값spx18x19
메모리[이전 sp + (16 * 9)] · 8바이트
이 명령을 실행하기 직전기존 메모리 값
이 명령을 실행한 직후명령 직전 x18의 값
메모리[이전 sp + (16 * 9) + 8] · 8바이트
이 명령을 실행하기 직전기존 메모리 값
이 명령을 실행한 직후명령 직전 x19의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 주소 계산에 의한 기준 레지스터 갱신은 없습니다. 다만 LDR/LDP의 목적 레지스터가 기준 레지스터와 같으면 읽은 값이 그 레지스터를 덮어씁니다.
  • 메모리 칸은 낮은 주소부터 나열했습니다. SP를 줄이면 더 낮은 주소의 공간을 사용합니다. 화면의 아래쪽이 곧 낮은 주소라는 뜻은 아닙니다.
  • 매크로 kernel_entry의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	stp	x20, x21, [sp, #16 * 10]

x20, x21의 값을 sp를 기준으로 16 * 10만큼 계산한 메모리 위치에 기록합니다. 레지스터 값을 나중에 다시 쓰거나 다른 코드가 읽을 수 있도록 메모리에 남기는 줄입니다. 첫 위치에는 x20, 그보다 8바이트 높은 위치에는 x21의 값이 들어갑니다. 이 스택 위치는 현재 함수 또는 예외 처리의 보관 공간입니다.

214행 그림 · 메모리 쓰기

매크로 본문: kernel_entry

읽는 값spx20x21
메모리[이전 sp + (16 * 10)] · 8바이트
이 명령을 실행하기 직전기존 메모리 값
이 명령을 실행한 직후명령 직전 x20의 값
메모리[이전 sp + (16 * 10) + 8] · 8바이트
이 명령을 실행하기 직전기존 메모리 값
이 명령을 실행한 직후명령 직전 x21의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 주소 계산에 의한 기준 레지스터 갱신은 없습니다. 다만 LDR/LDP의 목적 레지스터가 기준 레지스터와 같으면 읽은 값이 그 레지스터를 덮어씁니다.
  • 메모리 칸은 낮은 주소부터 나열했습니다. SP를 줄이면 더 낮은 주소의 공간을 사용합니다. 화면의 아래쪽이 곧 낮은 주소라는 뜻은 아닙니다.
  • 매크로 kernel_entry의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	stp	x22, x23, [sp, #16 * 11]

x22, x23의 값을 sp를 기준으로 16 * 11만큼 계산한 메모리 위치에 기록합니다. 레지스터 값을 나중에 다시 쓰거나 다른 코드가 읽을 수 있도록 메모리에 남기는 줄입니다. 첫 위치에는 x22, 그보다 8바이트 높은 위치에는 x23의 값이 들어갑니다. 이 스택 위치는 현재 함수 또는 예외 처리의 보관 공간입니다.

215행 그림 · 메모리 쓰기

매크로 본문: kernel_entry

읽는 값spx22x23
메모리[이전 sp + (16 * 11)] · 8바이트
이 명령을 실행하기 직전기존 메모리 값
이 명령을 실행한 직후명령 직전 x22의 값
메모리[이전 sp + (16 * 11) + 8] · 8바이트
이 명령을 실행하기 직전기존 메모리 값
이 명령을 실행한 직후명령 직전 x23의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 주소 계산에 의한 기준 레지스터 갱신은 없습니다. 다만 LDR/LDP의 목적 레지스터가 기준 레지스터와 같으면 읽은 값이 그 레지스터를 덮어씁니다.
  • 메모리 칸은 낮은 주소부터 나열했습니다. SP를 줄이면 더 낮은 주소의 공간을 사용합니다. 화면의 아래쪽이 곧 낮은 주소라는 뜻은 아닙니다.
  • 매크로 kernel_entry의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	stp	x24, x25, [sp, #16 * 12]

x24, x25의 값을 sp를 기준으로 16 * 12만큼 계산한 메모리 위치에 기록합니다. 레지스터 값을 나중에 다시 쓰거나 다른 코드가 읽을 수 있도록 메모리에 남기는 줄입니다. 첫 위치에는 x24, 그보다 8바이트 높은 위치에는 x25의 값이 들어갑니다. 이 스택 위치는 현재 함수 또는 예외 처리의 보관 공간입니다.

216행 그림 · 메모리 쓰기

매크로 본문: kernel_entry

읽는 값spx24x25
메모리[이전 sp + (16 * 12)] · 8바이트
이 명령을 실행하기 직전기존 메모리 값
이 명령을 실행한 직후명령 직전 x24의 값
메모리[이전 sp + (16 * 12) + 8] · 8바이트
이 명령을 실행하기 직전기존 메모리 값
이 명령을 실행한 직후명령 직전 x25의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 주소 계산에 의한 기준 레지스터 갱신은 없습니다. 다만 LDR/LDP의 목적 레지스터가 기준 레지스터와 같으면 읽은 값이 그 레지스터를 덮어씁니다.
  • 메모리 칸은 낮은 주소부터 나열했습니다. SP를 줄이면 더 낮은 주소의 공간을 사용합니다. 화면의 아래쪽이 곧 낮은 주소라는 뜻은 아닙니다.
  • 매크로 kernel_entry의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	stp	x26, x27, [sp, #16 * 13]

x26, x27의 값을 sp를 기준으로 16 * 13만큼 계산한 메모리 위치에 기록합니다. 레지스터 값을 나중에 다시 쓰거나 다른 코드가 읽을 수 있도록 메모리에 남기는 줄입니다. 첫 위치에는 x26, 그보다 8바이트 높은 위치에는 x27의 값이 들어갑니다. 이 스택 위치는 현재 함수 또는 예외 처리의 보관 공간입니다.

217행 그림 · 메모리 쓰기

매크로 본문: kernel_entry

읽는 값spx26x27
메모리[이전 sp + (16 * 13)] · 8바이트
이 명령을 실행하기 직전기존 메모리 값
이 명령을 실행한 직후명령 직전 x26의 값
메모리[이전 sp + (16 * 13) + 8] · 8바이트
이 명령을 실행하기 직전기존 메모리 값
이 명령을 실행한 직후명령 직전 x27의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 주소 계산에 의한 기준 레지스터 갱신은 없습니다. 다만 LDR/LDP의 목적 레지스터가 기준 레지스터와 같으면 읽은 값이 그 레지스터를 덮어씁니다.
  • 메모리 칸은 낮은 주소부터 나열했습니다. SP를 줄이면 더 낮은 주소의 공간을 사용합니다. 화면의 아래쪽이 곧 낮은 주소라는 뜻은 아닙니다.
  • 매크로 kernel_entry의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	stp	x28, x29, [sp, #16 * 14]

x28, x29의 값을 sp를 기준으로 16 * 14만큼 계산한 메모리 위치에 기록합니다. 레지스터 값을 나중에 다시 쓰거나 다른 코드가 읽을 수 있도록 메모리에 남기는 줄입니다. 첫 위치에는 x28, 그보다 8바이트 높은 위치에는 x29의 값이 들어갑니다. 이 스택 위치는 현재 함수 또는 예외 처리의 보관 공간입니다.

218행 그림 · 메모리 쓰기

매크로 본문: kernel_entry

읽는 값spx28x29
메모리[이전 sp + (16 * 14)] · 8바이트
이 명령을 실행하기 직전기존 메모리 값
이 명령을 실행한 직후명령 직전 x28의 값
메모리[이전 sp + (16 * 14) + 8] · 8바이트
이 명령을 실행하기 직전기존 메모리 값
이 명령을 실행한 직후명령 직전 x29의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 주소 계산에 의한 기준 레지스터 갱신은 없습니다. 다만 LDR/LDP의 목적 레지스터가 기준 레지스터와 같으면 읽은 값이 그 레지스터를 덮어씁니다.
  • 메모리 칸은 낮은 주소부터 나열했습니다. SP를 줄이면 더 낮은 주소의 공간을 사용합니다. 화면의 아래쪽이 곧 낮은 주소라는 뜻은 아닙니다.
  • 매크로 kernel_entry의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	.if	\el == 0

어셈블 단계에서 인자나 식을 검사해 필요한 명령만 만듭니다. \el == 0

220행 그림 · 어셈블·링크 단계

매크로 본문: kernel_entry

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과.if \el == 0
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	clear_gp_regs

원래 프로그램의 값은 이미 스택에 보관했습니다. 이제 예외 처리 코드에서 사용할 일반 레지스터를 0으로 정리합니다. 여기의 인자는 없음입니다.

221행 그림 · 매크로 전체의 상태 변화

매크로 본문: kernel_entry

이 줄을 포함하는 조건: .if \el == 0

x0~x29
이 명령을 실행하기 직전명령 직전 x0의 값~x29
이 명령을 실행한 직후각 레지스터에 0

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • x30과 SP까지 0으로 만드는 매크로는 아닙니다.
  • 매크로 kernel_entry의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
clear_gp_regs 매크로 본문 · 33행

arch/arm64/kernel/entry.S · Linux v6.18.37

.macro clear_gp_regs .irp n,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29 mov x\n, xzr .endr .endm
	mrs	x21, sp_el0

예외 전 사용자 SP를 SP_EL0에서 x21로 읽어 pt_regs에 보존할 준비를 합니다. 이후 SP_EL0를 커널 current 포인터로 바꾸기 전에 원래 사용자 스택 위치를 확보해야 합니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.

EL0에서 들어온 당시 SP_EL0는 사용자 스택 포인터입니다.

222행 그림 · 시스템 레지스터 읽기

매크로 본문: kernel_entry

이 줄을 포함하는 조건: .if \el == 0

읽는 값SP_EL0
x21
이 명령을 실행하기 직전명령 직전 x21의 값
이 명령을 실행한 직후명령 직전 SP_EL0의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 일반 RAM의 메모리 접근과 구분합니다.
  • 매크로 kernel_entry의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	ldr_this_cpu	tsk, __entry_task, x20

CPU별 __entry_task에서 실제 현재 task 주소를 얻습니다. EL0에서 들어온 순간 SP_EL0에는 사용자 SP가 있으므로 그것을 current로 사용할 수 없습니다. 심벌의 기준 주소에 현재 CPU의 per-CPU 오프셋을 더해 이 CPU의 저장 위치를 찾습니다.

223행 그림 · 주소·값 구분

매크로 본문: kernel_entry

이 줄을 포함하는 조건: .if \el == 0

tsk
이 명령을 실행하기 직전명령 직전 tsk의 값
이 명령을 실행한 직후메모리[주소(__entry_task) + 현재 CPU 오프셋]의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 이 매크로의 임시 레지스터는 내부에서 사용됩니다.
  • 매크로 kernel_entry의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
ldr_this_cpu 매크로 본문 · 250행

arch/arm64/include/asm/assembler.h · Linux v6.18.37

정의 선택 조건: #ifndef __ASM_ASSEMBLER_H

.macro ldr_this_cpu dst, sym, tmp adr_l \dst, \sym get_this_cpu_offset \tmp ldr \dst, [\dst, \tmp] .endm
	msr	sp_el0, tsk

SP_EL0를 current task 주소로 바꾸어 커널 내부의 current 조회를 가능하게 합니다. 사용자 SP는 x21에 따로 저장했고 뒤에서 프레임에 기록합니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.

이후 커널 실행에서는 SP_EL0에 current 태스크 포인터가 들어갑니다.

224행 그림 · 시스템 레지스터 쓰기

매크로 본문: kernel_entry

이 줄을 포함하는 조건: .if \el == 0

읽는 값tsk
SP_EL0
이 명령을 실행하기 직전명령 직전 SP_EL0의 값
이 명령을 실행한 직후명령 직전 tsk의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 접근 권한과 VHE 별칭 규칙에 따라 실제 대상이 정해집니다. 뒤의 ISB·DSB가 필요한지는 이 레지스터와 문맥에 따릅니다.
  • 매크로 kernel_entry의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	ldr	x19, [tsk, #TSK_TI_FLAGS]

tsk를 기준으로 TSK_TI_FLAGS만큼 계산한 메모리 위치에 보관된 값을 x19로 가져옵니다. 주소 숫자를 옮기는 MOV와 달리 실제 메모리를 읽습니다.

230행 그림 · 메모리 읽기

매크로 본문: kernel_entry

이 줄을 포함하는 조건: .if \el == 0

읽는 값tsk
메모리[이전 tsk + (TSK_TI_FLAGS)] · 8바이트
이 명령을 실행하기 직전M0 (이 주소에 저장된 값)
이 명령을 실행한 직후M0 (이 주소에 저장된 값)
x19
이 명령을 실행하기 직전명령 직전 x19의 값
이 명령을 실행한 직후M0

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 주소 계산에 의한 기준 레지스터 갱신은 없습니다. 다만 LDR/LDP의 목적 레지스터가 기준 레지스터와 같으면 읽은 값이 그 레지스터를 덮어씁니다.
  • 매크로 kernel_entry의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	disable_step_tsk x19, x20

task가 사용자 single-step 상태였더라도 커널 진입 코드를 한 명령씩 사용자 디버거에 노출하지 않도록 스텝 상태를 끕니다. 원래 task 플래그는 복귀 시 다시 확인합니다. TIF_SINGLESTEP이 설정된 경우 MDSCR_EL1.SS를 지우고 ISB로 동기화합니다.

231행 그림 · 매크로 전체의 상태 변화

매크로 본문: kernel_entry

이 줄을 포함하는 조건: .if \el == 0

읽는 값x19
MDSCR_EL1.SS
이 명령을 실행하기 직전명령 직전 MDSCR_EL1.SS의 값
이 명령을 실행한 직후x19의 TIF_SINGLESTEP=1이면 0, 아니면 그대로

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 조건이 맞을 때만 x20을 작업 레지스터로 사용합니다.
  • 매크로 kernel_entry의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
disable_step_tsk 매크로 본문 · 58행

arch/arm64/include/asm/assembler.h · Linux v6.18.37

정의 선택 조건: #ifndef __ASM_ASSEMBLER_H

.macro disable_step_tsk, flgs, tmp tbz \flgs, #TIF_SINGLESTEP, 9990f mrs \tmp, mdscr_el1 bic \tmp, \tmp, #MDSCR_EL1_SS msr mdscr_el1, \tmp isb // Take effect before a subsequent clear of DAIF.D 9990: .endm
	ldr	x0, [tsk, THREAD_SCTLR_USER]

task의 사용자 SCTLR 정책 사본을 읽습니다. 뒤에서 MTE 검사 mode와 사용자 포인터 인증 활성 여부를 판단할 공통 입력입니다. 실제로 tsk + (THREAD_SCTLR_USER) 주소의 메모리 8바이트를 x0에 읽습니다. 이 주소 형식은 기준 레지스터 tsk 자체를 갱신하지 않습니다.

234행 그림 · 메모리 읽기

매크로 본문: kernel_entry

이 줄을 포함하는 조건: .if \el == 0

읽는 값tsk
메모리[이전 tsk + (THREAD_SCTLR_USER)] · 8바이트
이 명령을 실행하기 직전M0 (이 주소에 저장된 값)
이 명령을 실행한 직후M0 (이 주소에 저장된 값)
x0
이 명령을 실행하기 직전명령 직전 x0의 값
이 명령을 실행한 직후M0

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 주소 계산에 의한 기준 레지스터 갱신은 없습니다. 다만 LDR/LDP의 목적 레지스터가 기준 레지스터와 같으면 읽은 값이 그 레지스터를 덮어씁니다.
  • 매크로 kernel_entry의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	check_mte_async_tcf x22, x23, x0

이 task가 비동기 MTE를 사용했다면 누적 태그 실패를 확인해 TIF_MTE_ASYNC_FAULT에 반영합니다. x22·x23은 조회와 thread flags 주소 계산용 임시 레지스터입니다.

235행 그림 · 매크로 안의 상태 변화

매크로 본문: kernel_entry

이 줄을 포함하는 조건: .if \el == 0

입력
이 명령을 실행하기 직전호출 직전 값
이 명령을 실행한 직후x22, x23, x0
주요 결과
이 명령을 실행하기 직전본문 실행 전
이 명령을 실행한 직후사용자 비동기 태그 오류 상태를 검사하고 발견 시 태스크의 MTE 비동기 오류 플래그를 설정합니다.

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 여러 명령과 내부 분기를 묶은 그림입니다. 작업 레지스터를 모두 보존한다는 뜻은 아니며, 정확한 변경 순서는 아래 매크로 본문과 줄별 그림에서 확인합니다.
  • 매크로 kernel_entry의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
check_mte_async_tcf 매크로 본문 · 131행

arch/arm64/kernel/entry.S · Linux v6.18.37

.macro check_mte_async_tcf, tmp, ti_flags, thread_sctlr #ifdef CONFIG_ARM64_MTE .arch_extension lse alternative_if_not ARM64_MTE b 1f alternative_else_nop_endif /* * Asynchronous tag check faults are only possible in ASYNC (2) or * ASYM (3) modes. In each of these modes bit 1 of SCTLR_EL1.TCF0 is * set, so skip the check if it is unset. */ tbz \thread_sctlr, #(SCTLR_EL1_TCF0_SHIFT + 1), 1f mrs_s \tmp, SYS_TFSRE0_EL1 tbz \tmp, #SYS_TFSR_EL1_TF0_SHIFT, 1f /* Asynchronous TCF occurred for TTBR0 access, set the TI flag */ mov \tmp, #_TIF_MTE_ASYNC_FAULT add \ti_flags, tsk, #TSK_TI_FLAGS stset \tmp, [\ti_flags] 1: #endif .endm
#ifdef CONFIG_ARM64_PTR_AUTH

포인터 인증 지원 빌드에만 사용자→커널 전환의 PAC 키/제어 처리를 포함합니다. PAC는 포인터를 키로 서명·인증하며, 커널에서 사용할 키와 사용자 키를 구분해야 하므로 이 위치에서 전환합니다. 이 조건은 빌드 전처리 단계에서 포함할 코드를 고릅니다. CPU가 실행 중 전처리 조건을 검사하지는 않습니다.

237행 그림 · 전처리 단계

매크로 본문: kernel_entry

이 줄을 포함하는 조건: .if \el == 0

소스
처리 전전처리 전
이 줄의 효과#ifdef CONFIG_ARM64_PTR_AUTH
CPU 레지스터
처리 전해당 없음
이 줄의 효과실행되는 CPU 명령이 없습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

alternative_if ARM64_HAS_ADDRESS_AUTH

실제 주소 인증 기능이 있는 CPU에서만 아래 APIA 키와 ENIA 처리가 남도록 합니다. 커널 설정만 켰다고 미지원 CPU에서 인증 레지스터를 쓰지 않습니다. 이 매크로는 기본·교체 명령의 패치 정보를 만들며, CPU가 이 줄을 매번 if 조건처럼 실행하지 않습니다.

238행 그림 · CPU 기능에 따른 명령 패치

매크로 본문: kernel_entry

이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_PTR_AUTH / .if \el == 0

패치 전 코드
처리 전기본 명령열
이 줄의 효과기능·정책에 맞는 기본 코드 또는 교체 코드

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 빌드할 때 패치 정보를 만들고 커널의 대체 코드 처리에서 명령을 선택합니다. 이 소스 줄을 CPU가 매번 if문으로 실행하는 것은 아닙니다.
	tbz	x0, SCTLR_ELx_ENIA_SHIFT, 1f

task의 사용자 SCTLR.ENIA가 켜져 있으면 현재 APIA 키가 사용자 키일 수 있으므로 커널 키 설치 경로를 탑니다. 꺼져 있으면 커널 키를 유지한 상태로 ENIA만 켜는 경로를 선택합니다. 이 분기는 지정 레지스터의 값이나 비트를 직접 검사하며 CMP가 남긴 NZCV를 읽거나 바꾸지 않습니다. 조건이 맞지 않으면 바로 다음 명령으로 진행합니다.

248행 그림 · 조건별 실행 위치

매크로 본문: kernel_entry

이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_PTR_AUTH / .if \el == 0

읽는 값x0
PC
이 명령을 실행하기 직전명령 직전 PC의 값
이 명령을 실행한 직후명령 직전 x0의 값의 bit SCTLR_ELx_ENIA_SHIFT = 0이면 1f (L251), 아니면 다음 명령

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 조건이 맞는 한 경로만 실행합니다. 이 분기 명령은 NZCV를 새로 계산하지 않습니다.
  • 매크로 kernel_entry의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	__ptrauth_keys_install_kernel_nosync tsk, x20, x22, x23

current task의 커널 APIA 키를 설치합니다. x20·x22·x23은 키를 읽고 시스템 레지스터에 쓸 임시 레지스터이며 nosync는 공통 ISB를 뒤에서 모아 수행한다는 뜻입니다.

249행 그림 · 매크로 안의 상태 변화

매크로 본문: kernel_entry

이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_PTR_AUTH / .if \el == 0

입력
이 명령을 실행하기 직전호출 직전 값
이 명령을 실행한 직후tsk, x20, x22, x23
주요 결과
이 명령을 실행하기 직전본문 실행 전
이 명령을 실행한 직후커널용 instruction authentication 키를 설치하며 이 매크로 끝에 ISB를 추가하지 않습니다. 호출 경로가 동기화 시점을 정합니다.

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 여러 명령과 내부 분기를 묶은 그림입니다. 작업 레지스터를 모두 보존한다는 뜻은 아니며, 정확한 변경 순서는 아래 매크로 본문과 줄별 그림에서 확인합니다.
  • 매크로 kernel_entry의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
__ptrauth_keys_install_kernel_nosync 매크로 본문 · 12행

arch/arm64/include/asm/asm_pointer_auth.h · Linux v6.18.37

정의 선택 조건: #ifndef __ASM_ASM_POINTER_AUTH_H / #ifdef CONFIG_ARM64_PTR_AUTH_KERNEL

.macro __ptrauth_keys_install_kernel_nosync tsk, tmp1, tmp2, tmp3 mov \tmp1, #THREAD_KEYS_KERNEL add \tmp1, \tsk, \tmp1 ldp \tmp2, \tmp3, [\tmp1, #PTRAUTH_KERNEL_KEY_APIA] msr_s SYS_APIAKEYLO_EL1, \tmp2 msr_s SYS_APIAKEYHI_EL1, \tmp3 .endm
__ptrauth_keys_install_kernel_nosync 매크로 본문 · 35행

arch/arm64/include/asm/asm_pointer_auth.h · Linux v6.18.37

정의 선택 조건: #ifndef __ASM_ASM_POINTER_AUTH_H / 앞 조건의 반대

.macro __ptrauth_keys_install_kernel_nosync tsk, tmp1, tmp2, tmp3 .endm
	b	2f

2f (L255)부터 실행을 계속합니다. 돌아올 주소를 새로 적지 않으므로 일반 함수 호출과는 다른 제어 이동입니다.

250행 그림 · 호출과 실행 위치

매크로 본문: kernel_entry

이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_PTR_AUTH / .if \el == 0

PC
이 명령을 실행하기 직전명령 직전 PC의 값
이 명령을 실행한 직후2f (L255)

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 새 복귀 주소를 LR에 기록하지 않습니다. RET 역시 메모리의 스택 값을 자동으로 꺼내는 명령은 아닙니다.
  • 매크로 kernel_entry의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
1:

여기에 1라는 위치 이름을 붙입니다. 다른 줄의 분기나 주소 계산이 이 위치를 찾아올 수 있습니다. 이름 자체가 CPU 작업을 추가하지는 않습니다.

251행 그림 · 위치에 이름 붙이기

매크로 본문: kernel_entry

이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_PTR_AUTH / .if \el == 0

레이블
처리 전현재 배치 위치
이 줄의 효과1
CPU 레지스터
처리 전그대로
이 줄의 효과레이블 자체가 값을 바꾸지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	mrs	x0, sctlr_el1

사용자에서 ENIA를 꺼 둔 경우 현재 SCTLR을 읽고 다른 설정을 보존하면서 커널 주소 인증만 다시 활성화할 준비를 합니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.

252행 그림 · 시스템 레지스터 읽기

매크로 본문: kernel_entry

이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_PTR_AUTH / .if \el == 0

읽는 값SCTLR_EL1
x0
이 명령을 실행하기 직전명령 직전 x0의 값
이 명령을 실행한 직후명령 직전 SCTLR_EL1의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 일반 RAM의 메모리 접근과 구분합니다.
  • 매크로 kernel_entry의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	orr	x0, x0, SCTLR_ELx_ENIA

SCTLR.ENIA 비트를 세워 커널 코드의 instruction-address A-key 인증을 사용할 수 있게 합니다. 사용자 task가 요청한 비활성 상태를 커널에 그대로 적용하지 않습니다. ORR은 어느 한쪽이 1인 비트를 결과에 세우므로 원래 1이던 비트도 유지됩니다.

253행 그림 · 레지스터 상태

매크로 본문: kernel_entry

이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_PTR_AUTH / .if \el == 0

읽는 값x0
x0
이 명령을 실행하기 직전명령 직전 x0의 값
이 명령을 실행한 직후명령 직전 x0의 값 OR SCTLR_ELx_ENIA

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 결과는 목적 레지스터 폭에 맞습니다. 이 형식은 NZCV를 바꾸지 않습니다.
  • 매크로 kernel_entry의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	msr	sctlr_el1, x0

커널에서 쓸 ENIA 활성 SCTLR을 설치합니다. 아래 공통 ISB가 PAC/MTE 변경 뒤의 실행을 동기화합니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.

254행 그림 · 시스템 레지스터 쓰기

매크로 본문: kernel_entry

이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_PTR_AUTH / .if \el == 0

읽는 값x0
SCTLR_EL1
이 명령을 실행하기 직전명령 직전 SCTLR_EL1의 값
이 명령을 실행한 직후명령 직전 x0의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 접근 권한과 VHE 별칭 규칙에 따라 실제 대상이 정해집니다. 뒤의 ISB·DSB가 필요한지는 이 레지스터와 문맥에 따릅니다.
  • 매크로 kernel_entry의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
2:

여기에 2라는 위치 이름을 붙입니다. 다른 줄의 분기나 주소 계산이 이 위치를 찾아올 수 있습니다. 이름 자체가 CPU 작업을 추가하지는 않습니다.

255행 그림 · 위치에 이름 붙이기

매크로 본문: kernel_entry

이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_PTR_AUTH / .if \el == 0

레이블
처리 전현재 배치 위치
이 줄의 효과2
CPU 레지스터
처리 전그대로
이 줄의 효과레이블 자체가 값을 바꾸지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

alternative_else_nop_endif

238행에서 시작한 ARM64_HAS_ADDRESS_AUTH 패치 묶음을 닫습니다. 조건에 맞는 본문을 사용하지 않는 쪽은 같은 길이의 NOP로 채워 뒤 명령의 배치를 유지합니다. 이 줄 자체가 CPU에서 조건을 검사하는 명령은 아닙니다.

256행 그림 · CPU 기능에 따른 명령 패치

매크로 본문: kernel_entry

이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_PTR_AUTH / .if \el == 0

패치 전 코드
처리 전기본 명령열
이 줄의 효과기능·정책에 맞는 기본 코드 또는 교체 코드

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 빌드할 때 패치 정보를 만들고 커널의 대체 코드 처리에서 명령을 선택합니다. 이 소스 줄을 CPU가 매번 if문으로 실행하는 것은 아닙니다.
#endif

빌드 조건으로 나누었던 구간을 닫습니다. 대상:

257행 그림 · 전처리 단계

매크로 본문: kernel_entry

이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_PTR_AUTH / .if \el == 0

소스
처리 전전처리 전
이 줄의 효과#endif
CPU 레지스터
처리 전해당 없음
이 줄의 효과실행되는 CPU 명령이 없습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	apply_ssbd 1, x22, x23

커널 진입 쪽 SSBD 상태 1을 요청합니다. 펌웨어 완화가 필요한 CPU와 task 정책에서만 실제 호출이 남으며 Spectre v4 완화 상태를 사용자 실행과 구분합니다.

259행 그림 · 매크로 안의 상태 변화

매크로 본문: kernel_entry

이 줄을 포함하는 조건: .if \el == 0

입력
이 명령을 실행하기 직전호출 직전 값
이 명령을 실행한 직후1, x22, x23
주요 결과
이 명령을 실행하기 직전본문 실행 전
이 명령을 실행한 직후CPU별 callback 필요 여부와 태스크 플래그에 따라 펌웨어의 SSBD 설정을 요청합니다. state=1은 진입, 0은 복귀 경로입니다.

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 여러 명령과 내부 분기를 묶은 그림입니다. 작업 레지스터를 모두 보존한다는 뜻은 아니며, 정확한 변경 순서는 아래 매크로 본문과 줄별 그림에서 확인합니다.
  • 매크로 kernel_entry의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
apply_ssbd 매크로 본문 · 114행

arch/arm64/kernel/entry.S · Linux v6.18.37

.macro apply_ssbd, state, tmp1, tmp2 alternative_cb ARM64_ALWAYS_SYSTEM, spectre_v4_patch_fw_mitigation_enable b .L__asm_ssbd_skip\@ // Patched to NOP alternative_cb_end ldr_this_cpu \tmp2, arm64_ssbd_callback_required, \tmp1 cbz \tmp2, .L__asm_ssbd_skip\@ ldr \tmp2, [tsk, #TSK_TI_FLAGS] tbnz \tmp2, #TIF_SSBD, .L__asm_ssbd_skip\@ mov w0, #ARM_SMCCC_ARCH_WORKAROUND_2 mov w1, #\state alternative_cb ARM64_ALWAYS_SYSTEM, smccc_patch_fw_mitigation_conduit nop // Patched to SMC/HVC #0 alternative_cb_end .L__asm_ssbd_skip\@: .endm
	mte_set_kernel_gcr x22, x23

하드웨어 태그 KASAN이 활성일 때 GCR을 커널 태그 생성 정책으로 바꿉니다. x22·x23은 그 계산에 사용하는 임시 레지스터입니다.

261행 그림 · 매크로 안의 상태 변화

매크로 본문: kernel_entry

이 줄을 포함하는 조건: .if \el == 0

입력
이 명령을 실행하기 직전호출 직전 값
이 명령을 실행한 직후x22, x23
주요 결과
이 명령을 실행하기 직전본문 실행 전
이 명령을 실행한 직후하드웨어 태그 KASAN이 활성화된 경우 커널의 GCR 값을 적용합니다.

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 여러 명령과 내부 분기를 묶은 그림입니다. 작업 레지스터를 모두 보존한다는 뜻은 아니며, 정확한 변경 순서는 아래 매크로 본문과 줄별 그림에서 확인합니다.
  • 매크로 kernel_entry의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
mte_set_kernel_gcr 매크로 본문 · 174행

arch/arm64/kernel/entry.S · Linux v6.18.37

.macro mte_set_kernel_gcr, tmp, tmp2 #ifdef CONFIG_KASAN_HW_TAGS alternative_cb ARM64_ALWAYS_SYSTEM, kasan_hw_tags_enable b 1f alternative_cb_end mov \tmp, KERNEL_GCR_EL1 msr_s SYS_GCR_EL1, \tmp 1: #endif .endm
alternative_if ARM64_MTE

MTE 제어를 바꾼 CPU에는 동기화 ISB가 필요하므로 아래 한 번의 ISB를 남깁니다. 뒤의 PAC 경로와 중복 수행하지 않도록 공통 끝으로 분기합니다. 이 매크로는 기본·교체 명령의 패치 정보를 만들며, CPU가 이 줄을 매번 if 조건처럼 실행하지 않습니다.

267행 그림 · CPU 기능에 따른 명령 패치

매크로 본문: kernel_entry

이 줄을 포함하는 조건: .if \el == 0

패치 전 코드
처리 전기본 명령열
이 줄의 효과기능·정책에 맞는 기본 코드 또는 교체 코드

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 빌드할 때 패치 정보를 만들고 커널의 대체 코드 처리에서 명령을 선택합니다. 이 소스 줄을 CPU가 매번 if문으로 실행하는 것은 아닙니다.
	isb

뒤 명령을 변경된 CPU 제어 상태에 맞춰 실행하도록 동기화합니다. 주소 변환이나 벡터 설정을 바꾼 뒤 예전 실행 상태가 계속 쓰이지 않게 합니다.

268행 그림 · 동기화·캐시·대기 상태

매크로 본문: kernel_entry

이 줄을 포함하는 조건: .if \el == 0

명령 실행 문맥
이 명령을 실행하기 직전실행 전 상태
이 명령을 실행한 직후앞서 바꾼 상태를 이후 명령 실행에 반영

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 매크로 kernel_entry의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	b	1f

1f (L274)부터 실행을 계속합니다. 돌아올 주소를 새로 적지 않으므로 일반 함수 호출과는 다른 제어 이동입니다.

269행 그림 · 호출과 실행 위치

매크로 본문: kernel_entry

이 줄을 포함하는 조건: .if \el == 0

PC
이 명령을 실행하기 직전명령 직전 PC의 값
이 명령을 실행한 직후1f (L274)

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 새 복귀 주소를 LR에 기록하지 않습니다. RET 역시 메모리의 스택 값을 자동으로 꺼내는 명령은 아닙니다.
  • 매크로 kernel_entry의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
alternative_else_nop_endif

267행에서 시작한 ARM64_MTE 패치 묶음을 닫습니다. 조건에 맞는 본문을 사용하지 않는 쪽은 같은 길이의 NOP로 채워 뒤 명령의 배치를 유지합니다. 이 줄 자체가 CPU에서 조건을 검사하는 명령은 아닙니다.

270행 그림 · CPU 기능에 따른 명령 패치

매크로 본문: kernel_entry

이 줄을 포함하는 조건: .if \el == 0

패치 전 코드
처리 전기본 명령열
이 줄의 효과기능·정책에 맞는 기본 코드 또는 교체 코드

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 빌드할 때 패치 정보를 만들고 커널의 대체 코드 처리에서 명령을 선택합니다. 이 소스 줄을 CPU가 매번 if문으로 실행하는 것은 아닙니다.
alternative_if ARM64_HAS_ADDRESS_AUTH

MTE 경로에서 이미 ISB를 하지 않았더라도 주소 인증 상태를 바꾼 CPU에는 ISB를 남깁니다. 두 기능이 모두 없어 제어를 바꾸지 않으면 이 추가 ISB는 NOP 대안으로 빠집니다. 이 매크로는 기본·교체 명령의 패치 정보를 만들며, CPU가 이 줄을 매번 if 조건처럼 실행하지 않습니다.

271행 그림 · CPU 기능에 따른 명령 패치

매크로 본문: kernel_entry

이 줄을 포함하는 조건: .if \el == 0

패치 전 코드
처리 전기본 명령열
이 줄의 효과기능·정책에 맞는 기본 코드 또는 교체 코드

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 빌드할 때 패치 정보를 만들고 커널의 대체 코드 처리에서 명령을 선택합니다. 이 소스 줄을 CPU가 매번 if문으로 실행하는 것은 아닙니다.
	isb

뒤 명령을 변경된 CPU 제어 상태에 맞춰 실행하도록 동기화합니다. 주소 변환이나 벡터 설정을 바꾼 뒤 예전 실행 상태가 계속 쓰이지 않게 합니다.

272행 그림 · 동기화·캐시·대기 상태

매크로 본문: kernel_entry

이 줄을 포함하는 조건: .if \el == 0

명령 실행 문맥
이 명령을 실행하기 직전실행 전 상태
이 명령을 실행한 직후앞서 바꾼 상태를 이후 명령 실행에 반영

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 매크로 kernel_entry의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
alternative_else_nop_endif

271행에서 시작한 ARM64_HAS_ADDRESS_AUTH 패치 묶음을 닫습니다. 조건에 맞는 본문을 사용하지 않는 쪽은 같은 길이의 NOP로 채워 뒤 명령의 배치를 유지합니다. 이 줄 자체가 CPU에서 조건을 검사하는 명령은 아닙니다.

273행 그림 · CPU 기능에 따른 명령 패치

매크로 본문: kernel_entry

이 줄을 포함하는 조건: .if \el == 0

패치 전 코드
처리 전기본 명령열
이 줄의 효과기능·정책에 맞는 기본 코드 또는 교체 코드

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 빌드할 때 패치 정보를 만들고 커널의 대체 코드 처리에서 명령을 선택합니다. 이 소스 줄을 CPU가 매번 if문으로 실행하는 것은 아닙니다.
1:

여기에 1라는 위치 이름을 붙입니다. 다른 줄의 분기나 주소 계산이 이 위치를 찾아올 수 있습니다. 이름 자체가 CPU 작업을 추가하지는 않습니다.

274행 그림 · 위치에 이름 붙이기

매크로 본문: kernel_entry

이 줄을 포함하는 조건: .if \el == 0

레이블
처리 전현재 배치 위치
이 줄의 효과1
CPU 레지스터
처리 전그대로
이 줄의 효과레이블 자체가 값을 바꾸지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	scs_load_current

태스크에 저장해 둔 반환 주소 보호용 스택 위치를 되살립니다. 일반 SP를 읽는 것과는 다른 상태입니다. 여기의 인자는 없음입니다.

276행 그림 · 매크로 전체의 상태 변화

매크로 본문: kernel_entry

이 줄을 포함하는 조건: .if \el == 0

x18
이 명령을 실행하기 직전명령 직전 x18의 값
이 명령을 실행한 직후메모리[명령 직전 SP_EL0의 값 + TSK_TI_SCS_SP]

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • CONFIG_SHADOW_CALL_STACK이 없으면 빈 매크로입니다.
  • 매크로 kernel_entry의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
scs_load_current 매크로 본문 · 13행

arch/arm64/include/asm/scs.h · Linux v6.18.37

정의 선택 조건: #ifndef _ASM_SCS_H / #ifdef __ASSEMBLY__ / #ifdef CONFIG_SHADOW_CALL_STACK

.macro scs_load_current get_current_task scs_sp ldr scs_sp, [scs_sp, #TSK_TI_SCS_SP] .endm
scs_load_current 매크로 본문 · 22행

arch/arm64/include/asm/scs.h · Linux v6.18.37

정의 선택 조건: #ifndef _ASM_SCS_H / #ifdef __ASSEMBLY__ / 앞 조건의 반대

.macro scs_load_current .endm
	.else

앞 어셈블 조건의 반대 경우에 생성할 코드입니다.

277행 그림 · 어셈블·링크 단계

매크로 본문: kernel_entry

이 줄을 포함하는 조건: .if \el == 0

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과.else
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	add	x21, sp, #PT_REGS_SIZE

sp를 기준으로 PT_REGS_SIZE을 더해 x21에 결과를 둡니다. 주소 계산에 쓰이는 경우에는 구조체의 필드나 다음 저장 위치를 찾는 동작입니다. 이 형태는 조건 플래그를 바꾸지 않습니다.

EL1에서 중단된 원래 SP는 프레임을 잡기 전의 SP입니다.

278행 그림 · 레지스터 상태

매크로 본문: kernel_entry

이 줄을 포함하는 조건: 앞 어셈블 조건의 반대

읽는 값sp
x21
이 명령을 실행하기 직전명령 직전 x21의 값
이 명령을 실행한 직후명령 직전 sp의 값 + PT_REGS_SIZE

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 결과는 목적 레지스터 폭에 맞습니다. 이 형식은 NZCV를 바꾸지 않습니다.
  • 매크로 kernel_entry의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	get_current_task tsk

지금 이 CPU가 실행 중인 태스크의 주소를 가져옵니다. 단순한 정수 레지스터 값에 운영체제의 current라는 의미를 부여하는 부분입니다. 여기의 인자는 tsk입니다.

279행 그림 · 매크로 전체의 상태 변화

매크로 본문: kernel_entry

이 줄을 포함하는 조건: 앞 어셈블 조건의 반대

읽는 값SP_EL0
tsk
이 명령을 실행하기 직전명령 직전 tsk의 값
이 명령을 실행한 직후명령 직전 SP_EL0의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 매크로 kernel_entry의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
get_current_task 매크로 본문 · 570행

arch/arm64/include/asm/assembler.h · Linux v6.18.37

정의 선택 조건: #ifndef __ASM_ASSEMBLER_H

.macro get_current_task, rd mrs \rd, sp_el0 .endm
	.endif /* \el == 0 */

어셈블 조건에 따라 생성할 코드를 나누던 범위가 끝납니다.

280행 그림 · 어셈블·링크 단계

매크로 본문: kernel_entry

이 줄을 포함하는 조건: 앞 어셈블 조건의 반대

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과.endif
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

kernel_entry: PC·PSTATE·프레임 메타데이터

원본 281–333행

	mrs	x22, elr_el1

ELR_EL1은 예외 후 돌아갈 PC를 담습니다. C handler·시그널·디버거가 저장 프레임을 통해 복귀 위치를 다룰 수 있도록 x22에 읽어 둡니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.

281행 그림 · 시스템 레지스터 읽기

매크로 본문: kernel_entry

읽는 값ELR_EL1
x22
이 명령을 실행하기 직전명령 직전 x22의 값
이 명령을 실행한 직후명령 직전 ELR_EL1의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 일반 RAM의 메모리 접근과 구분합니다.
  • 매크로 kernel_entry의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	mrs	x23, spsr_el1

SPSR_EL1의 예외 전 PSTATE를 x23에 읽습니다. 원래 실행 레벨·마스크·condition flags를 pt_regs에 보존해 나중에 ERET로 복원할 자료입니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.

282행 그림 · 시스템 레지스터 읽기

매크로 본문: kernel_entry

읽는 값SPSR_EL1
x23
이 명령을 실행하기 직전명령 직전 x23의 값
이 명령을 실행한 직후명령 직전 SPSR_EL1의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 일반 RAM의 메모리 접근과 구분합니다.
  • 매크로 kernel_entry의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	stp	lr, x21, [sp, #S_LR]

lr, x21의 값을 sp를 기준으로 S_LR만큼 계산한 메모리 위치에 기록합니다. 레지스터 값을 나중에 다시 쓰거나 다른 코드가 읽을 수 있도록 메모리에 남기는 줄입니다. 이 스택 위치는 현재 함수 또는 예외 처리의 보관 공간입니다.

LR과 중단된 SP를 S_LR에서 시작하는 연속 두 슬롯에 저장합니다.

283행 그림 · 메모리 쓰기

매크로 본문: kernel_entry

읽는 값splrx21
메모리[이전 sp + (S_LR)] · 8바이트
이 명령을 실행하기 직전기존 메모리 값
이 명령을 실행한 직후명령 직전 lr의 값
메모리[이전 sp + (S_LR) + 8] · 8바이트
이 명령을 실행하기 직전기존 메모리 값
이 명령을 실행한 직후명령 직전 x21의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 주소 계산에 의한 기준 레지스터 갱신은 없습니다. 다만 LDR/LDP의 목적 레지스터가 기준 레지스터와 같으면 읽은 값이 그 레지스터를 덮어씁니다.
  • 메모리 칸은 낮은 주소부터 나열했습니다. SP를 줄이면 더 낮은 주소의 공간을 사용합니다. 화면의 아래쪽이 곧 낮은 주소라는 뜻은 아닙니다.
  • 매크로 kernel_entry의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	stp	xzr, xzr, [sp, #S_STACKFRAME]

xzr, xzr의 값을 sp를 기준으로 S_STACKFRAME만큼 계산한 메모리 위치에 기록합니다. 레지스터 값을 나중에 다시 쓰거나 다른 코드가 읽을 수 있도록 메모리에 남기는 줄입니다. 첫 위치에는 xzr, 그보다 8바이트 높은 위치에는 xzr의 값이 들어갑니다. 이 스택 위치는 현재 함수 또는 예외 처리의 보관 공간입니다.

289행 그림 · 메모리 쓰기

매크로 본문: kernel_entry

읽는 값spxzr
메모리[이전 sp + (S_STACKFRAME)] · 8바이트
이 명령을 실행하기 직전기존 메모리 값
이 명령을 실행한 직후0
메모리[이전 sp + (S_STACKFRAME) + 8] · 8바이트
이 명령을 실행하기 직전기존 메모리 값
이 명령을 실행한 직후0

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 주소 계산에 의한 기준 레지스터 갱신은 없습니다. 다만 LDR/LDP의 목적 레지스터가 기준 레지스터와 같으면 읽은 값이 그 레지스터를 덮어씁니다.
  • 메모리 칸은 낮은 주소부터 나열했습니다. SP를 줄이면 더 낮은 주소의 공간을 사용합니다. 화면의 아래쪽이 곧 낮은 주소라는 뜻은 아닙니다.
  • 매크로 kernel_entry의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	.if \el == 0

어셈블 단계에서 인자나 식을 검사해 필요한 명령만 만듭니다. \el == 0

290행 그림 · 어셈블·링크 단계

매크로 본문: kernel_entry

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과.if \el == 0
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	mov	x0, #FRAME_META_TYPE_FINAL

사용자에서 왔으면 커널 스택 추적이 여기서 끝나도록 FRAME_META_TYPE_FINAL을 선택합니다. 사용자 FP를 커널 스택 연결로 계속 따라가지 않게 합니다. MOV는 x0의 기존 값을 대체합니다. 원본이 주소값이어도 그 주소의 메모리를 읽거나 복사하지 않습니다.

291행 그림 · 값 복사

매크로 본문: kernel_entry

이 줄을 포함하는 조건: .if \el == 0

x0
이 명령을 실행하기 직전명령 직전 x0의 값
이 명령을 실행한 직후FRAME_META_TYPE_FINAL

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 화살표는 값의 변화를 뜻합니다. 주소값을 옮겨도 그 주소의 메모리 내용은 복사하지 않습니다.
  • 매크로 kernel_entry의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	.else

앞 어셈블 조건의 반대 경우에 생성할 코드입니다.

292행 그림 · 어셈블·링크 단계

매크로 본문: kernel_entry

이 줄을 포함하는 조건: .if \el == 0

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과.else
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	mov	x0, #FRAME_META_TYPE_PT_REGS

커널 실행 중 예외이면 FRAME_META_TYPE_PT_REGS로 표시하여 unwinder가 저장된 pt_regs를 통해 중단된 커널 프레임으로 이어갈 수 있게 합니다. MOV는 x0의 기존 값을 대체합니다. 원본이 주소값이어도 그 주소의 메모리를 읽거나 복사하지 않습니다.

293행 그림 · 값 복사

매크로 본문: kernel_entry

이 줄을 포함하는 조건: 앞 어셈블 조건의 반대

x0
이 명령을 실행하기 직전명령 직전 x0의 값
이 명령을 실행한 직후FRAME_META_TYPE_PT_REGS

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 화살표는 값의 변화를 뜻합니다. 주소값을 옮겨도 그 주소의 메모리 내용은 복사하지 않습니다.
  • 매크로 kernel_entry의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	.endif

어셈블 조건에 따라 생성할 코드를 나누던 범위가 끝납니다.

294행 그림 · 어셈블·링크 단계

매크로 본문: kernel_entry

이 줄을 포함하는 조건: 앞 어셈블 조건의 반대

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과.endif
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	str	x0, [sp, #S_STACKFRAME_TYPE]

x0의 값을 sp를 기준으로 S_STACKFRAME_TYPE만큼 계산한 메모리 위치에 기록합니다. 레지스터 값을 나중에 다시 쓰거나 다른 코드가 읽을 수 있도록 메모리에 남기는 줄입니다. 이 스택 위치는 현재 함수 또는 예외 처리의 보관 공간입니다.

295행 그림 · 메모리 쓰기

매크로 본문: kernel_entry

읽는 값spx0
메모리[이전 sp + (S_STACKFRAME_TYPE)] · 8바이트
이 명령을 실행하기 직전기존 메모리 값
이 명령을 실행한 직후명령 직전 x0의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 주소 계산에 의한 기준 레지스터 갱신은 없습니다. 다만 LDR/LDP의 목적 레지스터가 기준 레지스터와 같으면 읽은 값이 그 레지스터를 덮어씁니다.
  • 메모리 칸은 낮은 주소부터 나열했습니다. SP를 줄이면 더 낮은 주소의 공간을 사용합니다. 화면의 아래쪽이 곧 낮은 주소라는 뜻은 아닙니다.
  • 매크로 kernel_entry의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	add	x29, sp, #S_STACKFRAME

sp를 기준으로 S_STACKFRAME을 더해 x29에 결과를 둡니다. 주소 계산에 쓰이는 경우에는 구조체의 필드나 다음 저장 위치를 찾는 동작입니다. 이 형태는 조건 플래그를 바꾸지 않습니다.

296행 그림 · 레지스터 상태

매크로 본문: kernel_entry

읽는 값sp
x29
이 명령을 실행하기 직전명령 직전 x29의 값
이 명령을 실행한 직후명령 직전 sp의 값 + S_STACKFRAME

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 결과는 목적 레지스터 폭에 맞습니다. 이 형식은 NZCV를 바꾸지 않습니다.
  • 매크로 kernel_entry의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
#ifdef CONFIG_ARM64_SW_TTBR0_PAN

소프트웨어 PAN은 TTBR0를 예약된 표로 바꿔 커널의 무분별한 사용자 메모리 접근을 막는 구성입니다. 켜진 빌드에서는 진입 시 그 상태를 보존·차단하고, 꺼지면 아래 emulation 호출은 없습니다. 이 조건은 빌드 전처리 단계에서 포함할 코드를 고릅니다. CPU가 실행 중 전처리 조건을 검사하지는 않습니다.

298행 그림 · 전처리 단계

매크로 본문: kernel_entry

소스
처리 전전처리 전
이 줄의 효과#ifdef CONFIG_ARM64_SW_TTBR0_PAN
CPU 레지스터
처리 전해당 없음
이 줄의 효과실행되는 CPU 명령이 없습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

alternative_if_not ARM64_HAS_PAN

하드웨어 PAN이 없는 CPU에서만 소프트웨어 TTBR0 전환 호출을 유지합니다. PAN이 있으면 하드웨어 접근 차단을 쓰므로 이 호출은 NOP로 교체됩니다. 이 매크로는 기본·교체 명령의 패치 정보를 만들며, CPU가 이 줄을 매번 if 조건처럼 실행하지 않습니다.

299행 그림 · CPU 기능에 따른 명령 패치

매크로 본문: kernel_entry

이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_SW_TTBR0_PAN

패치 전 코드
처리 전기본 명령열
이 줄의 효과기능·정책에 맞는 기본 코드 또는 교체 코드

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 빌드할 때 패치 정보를 만들고 커널의 대체 코드 처리에서 명령을 선택합니다. 이 소스 줄을 CPU가 매번 if문으로 실행하는 것은 아닙니다.
	bl	__swpan_entry_el\el

중단된 레벨에 맞는 __swpan_entry_el0 또는 el1을 호출합니다. EL1 진입이면 원래 사용자 접근 허용 여부를 SPSR의 모의 PAN 표식에 남기고, 필요하면 TTBR0를 예약 상태로 바꿉니다. BL은 다음 명령의 주소를 LR(x30)에 기록하고 목적지로 분기합니다. 호출된 코드가 x0 같은 작업 레지스터를 바꿀 수 있으므로, 호출 뒤의 값은 그 코드의 반환·보존 규칙에 따릅니다.

300행 그림 · 호출과 실행 위치

매크로 본문: kernel_entry

이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_SW_TTBR0_PAN

PC
이 명령을 실행하기 직전명령 직전 PC의 값
이 명령을 실행한 직후__swpan_entry_el\el
LR (x30)
이 명령을 실행하기 직전명령 직전 LR의 값 (x30)
이 명령을 실행한 직후이 BL/BLR 다음 명령 주소

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 그림은 분기 명령 자체의 효과입니다. 호출된 함수가 실행된 뒤의 레지스터 값은 그 함수의 반환·보존 규칙을 따라야 합니다.
  • 매크로 kernel_entry의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
alternative_else_nop_endif

299행에서 시작한 ARM64_HAS_PAN 패치 묶음을 닫습니다. 조건에 맞는 본문을 사용하지 않는 쪽은 같은 길이의 NOP로 채워 뒤 명령의 배치를 유지합니다. 이 줄 자체가 CPU에서 조건을 검사하는 명령은 아닙니다.

301행 그림 · CPU 기능에 따른 명령 패치

매크로 본문: kernel_entry

이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_SW_TTBR0_PAN

패치 전 코드
처리 전기본 명령열
이 줄의 효과기능·정책에 맞는 기본 코드 또는 교체 코드

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 빌드할 때 패치 정보를 만들고 커널의 대체 코드 처리에서 명령을 선택합니다. 이 소스 줄을 CPU가 매번 if문으로 실행하는 것은 아닙니다.
#endif

빌드 조건으로 나누었던 구간을 닫습니다. 대상:

302행 그림 · 전처리 단계

매크로 본문: kernel_entry

이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_SW_TTBR0_PAN

소스
처리 전전처리 전
이 줄의 효과#endif
CPU 레지스터
처리 전해당 없음
이 줄의 효과실행되는 CPU 명령이 없습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	stp	x22, x23, [sp, #S_PC]

x22, x23의 값을 sp를 기준으로 S_PC만큼 계산한 메모리 위치에 기록합니다. 레지스터 값을 나중에 다시 쓰거나 다른 코드가 읽을 수 있도록 메모리에 남기는 줄입니다. 첫 위치에는 x22, 그보다 8바이트 높은 위치에는 x23의 값이 들어갑니다. 이 스택 위치는 현재 함수 또는 예외 처리의 보관 공간입니다.

x22=ELR, x23=SPSR이므로 S_PC에는 복귀 PC와 PSTATE가 들어갑니다.

304행 그림 · 메모리 쓰기

매크로 본문: kernel_entry

읽는 값spx22x23
메모리[이전 sp + (S_PC)] · 8바이트
이 명령을 실행하기 직전기존 메모리 값
이 명령을 실행한 직후명령 직전 x22의 값
메모리[이전 sp + (S_PC) + 8] · 8바이트
이 명령을 실행하기 직전기존 메모리 값
이 명령을 실행한 직후명령 직전 x23의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 주소 계산에 의한 기준 레지스터 갱신은 없습니다. 다만 LDR/LDP의 목적 레지스터가 기준 레지스터와 같으면 읽은 값이 그 레지스터를 덮어씁니다.
  • 메모리 칸은 낮은 주소부터 나열했습니다. SP를 줄이면 더 낮은 주소의 공간을 사용합니다. 화면의 아래쪽이 곧 낮은 주소라는 뜻은 아닙니다.
  • 매크로 kernel_entry의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	.if	\el == 0

어셈블 단계에서 인자나 식을 검사해 필요한 명령만 만듭니다. \el == 0

307행 그림 · 어셈블·링크 단계

매크로 본문: kernel_entry

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과.if \el == 0
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	mov	w21, #NO_SYSCALL

NO_SYSCALL은 아직 이 예외가 시스템 호출로 판정되지 않았음을 나타내는 값입니다. EL0 예외에는 IRQ·fault도 있으므로 진입부터 유효한 syscall 번호라고 가정하지 않습니다. MOV는 w21의 기존 값을 대체합니다. 원본이 주소값이어도 그 주소의 메모리를 읽거나 복사하지 않습니다. w21에 쓰면 대응하는 x21의 상위 32비트도 0이 됩니다.

아직 시스템 호출이라고 확정하지 않은 예외 프레임의 초기 번호입니다.

308행 그림 · 값 복사

매크로 본문: kernel_entry

이 줄을 포함하는 조건: .if \el == 0

w21
이 명령을 실행하기 직전명령 직전 w21의 값
이 명령을 실행한 직후NO_SYSCALL
x21[63:32]
이 명령을 실행하기 직전명령 직전 x21[63:32]의 값
이 명령을 실행한 직후0

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 화살표는 값의 변화를 뜻합니다. 주소값을 옮겨도 그 주소의 메모리 내용은 복사하지 않습니다.
  • 매크로 kernel_entry의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	str	w21, [sp, #S_SYSCALLNO]

w21의 값을 sp를 기준으로 S_SYSCALLNO만큼 계산한 메모리 위치에 기록합니다. 레지스터 값을 나중에 다시 쓰거나 다른 코드가 읽을 수 있도록 메모리에 남기는 줄입니다. 이 스택 위치는 현재 함수 또는 예외 처리의 보관 공간입니다.

309행 그림 · 메모리 쓰기

매크로 본문: kernel_entry

이 줄을 포함하는 조건: .if \el == 0

읽는 값spw21
메모리[이전 sp + (S_SYSCALLNO)] · 4바이트
이 명령을 실행하기 직전기존 메모리 값
이 명령을 실행한 직후명령 직전 w21의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 주소 계산에 의한 기준 레지스터 갱신은 없습니다. 다만 LDR/LDP의 목적 레지스터가 기준 레지스터와 같으면 읽은 값이 그 레지스터를 덮어씁니다.
  • 메모리 칸은 낮은 주소부터 나열했습니다. SP를 줄이면 더 낮은 주소의 공간을 사용합니다. 화면의 아래쪽이 곧 낮은 주소라는 뜻은 아닙니다.
  • 매크로 kernel_entry의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	.endif

어셈블 조건에 따라 생성할 코드를 나누던 범위가 끝납니다.

310행 그림 · 어셈블·링크 단계

매크로 본문: kernel_entry

이 줄을 포함하는 조건: .if \el == 0

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과.endif
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

#ifdef CONFIG_ARM64_PSEUDO_NMI

pseudo-NMI는 GIC 우선순위 마스크로 일반 IRQ를 막아도 더 높은 우선순위의 요청을 받도록 하는 기능입니다. 켜진 빌드에서 PMR 저장·복원을 포함하며, 꺼지면 일반 DAIF 중심의 경로만 남습니다. 이 조건은 빌드 전처리 단계에서 포함할 코드를 고릅니다. CPU가 실행 중 전처리 조건을 검사하지는 않습니다.

312행 그림 · 전처리 단계

매크로 본문: kernel_entry

소스
처리 전전처리 전
이 줄의 효과#ifdef CONFIG_ARM64_PSEUDO_NMI
CPU 레지스터
처리 전해당 없음
이 줄의 효과실행되는 CPU 명령이 없습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

alternative_if_not ARM64_HAS_GIC_PRIO_MASKING

실제 시스템이 GIC 우선순위 마스킹을 사용하지 않으면 PMR 저장 코드를 건너뛰도록 분기를 유지합니다. 사용하는 시스템에서는 분기를 NOP로 바꿔 PMR 상태를 프레임에 담습니다. 이 매크로는 기본·교체 명령의 패치 정보를 만들며, CPU가 이 줄을 매번 if 조건처럼 실행하지 않습니다.

313행 그림 · CPU 기능에 따른 명령 패치

매크로 본문: kernel_entry

이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_PSEUDO_NMI

패치 전 코드
처리 전기본 명령열
이 줄의 효과기능·정책에 맞는 기본 코드 또는 교체 코드

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 빌드할 때 패치 정보를 만들고 커널의 대체 코드 처리에서 명령을 선택합니다. 이 소스 줄을 CPU가 매번 if문으로 실행하는 것은 아닙니다.
	b	.Lskip_pmr_save\@

.Lskip_pmr_save\@부터 실행을 계속합니다. 돌아올 주소를 새로 적지 않으므로 일반 함수 호출과는 다른 제어 이동입니다.

314행 그림 · 호출과 실행 위치

매크로 본문: kernel_entry

이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_PSEUDO_NMI

PC
이 명령을 실행하기 직전명령 직전 PC의 값
이 명령을 실행한 직후.Lskip_pmr_save\@

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 새 복귀 주소를 LR에 기록하지 않습니다. RET 역시 메모리의 스택 값을 자동으로 꺼내는 명령은 아닙니다.
  • 매크로 kernel_entry의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
alternative_else_nop_endif

313행에서 시작한 ARM64_HAS_GIC_PRIO_MASKING 패치 묶음을 닫습니다. 조건에 맞는 본문을 사용하지 않는 쪽은 같은 길이의 NOP로 채워 뒤 명령의 배치를 유지합니다. 이 줄 자체가 CPU에서 조건을 검사하는 명령은 아닙니다.

315행 그림 · CPU 기능에 따른 명령 패치

매크로 본문: kernel_entry

이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_PSEUDO_NMI

패치 전 코드
처리 전기본 명령열
이 줄의 효과기능·정책에 맞는 기본 코드 또는 교체 코드

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 빌드할 때 패치 정보를 만들고 커널의 대체 코드 처리에서 명령을 선택합니다. 이 소스 줄을 CPU가 매번 if문으로 실행하는 것은 아닙니다.
	mrs_s	x20, SYS_ICC_PMR_EL1

ICC_PMR_EL1에서 현재 CPU가 받아들일 인터럽트 우선순위 문턱을 읽습니다. 단순 IRQ 허용 비트보다 세분화된 마스크 상태이므로 예외 프레임에 별도로 저장합니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.

317행 그림 · 시스템 레지스터 읽기

매크로 본문: kernel_entry

이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_PSEUDO_NMI

읽는 값SYS_ICC_PMR_EL1
x20
이 명령을 실행하기 직전명령 직전 x20의 값
이 명령을 실행한 직후명령 직전 SYS_ICC_PMR_EL1의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 일반 RAM의 메모리 접근과 구분합니다.
  • 매크로 kernel_entry의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
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
	str	w20, [sp, #S_PMR]

w20의 값을 sp를 기준으로 S_PMR만큼 계산한 메모리 위치에 기록합니다. 레지스터 값을 나중에 다시 쓰거나 다른 코드가 읽을 수 있도록 메모리에 남기는 줄입니다. 이 스택 위치는 현재 함수 또는 예외 처리의 보관 공간입니다.

318행 그림 · 메모리 쓰기

매크로 본문: kernel_entry

이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_PSEUDO_NMI

읽는 값spw20
메모리[이전 sp + (S_PMR)] · 4바이트
이 명령을 실행하기 직전기존 메모리 값
이 명령을 실행한 직후명령 직전 w20의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 주소 계산에 의한 기준 레지스터 갱신은 없습니다. 다만 LDR/LDP의 목적 레지스터가 기준 레지스터와 같으면 읽은 값이 그 레지스터를 덮어씁니다.
  • 메모리 칸은 낮은 주소부터 나열했습니다. SP를 줄이면 더 낮은 주소의 공간을 사용합니다. 화면의 아래쪽이 곧 낮은 주소라는 뜻은 아닙니다.
  • 매크로 kernel_entry의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	mov	x20, #GIC_PRIO_IRQON | GIC_PRIO_PSR_I_SET

커널 진입 후 사용할 GIC 우선순위 기준과 PSTATE.I 관련 표시를 결합합니다. 일반 IRQ와 pseudo-NMI의 차단 수준을 맞추기 위한 값입니다. MOV는 x20의 기존 값을 대체합니다. 원본이 주소값이어도 그 주소의 메모리를 읽거나 복사하지 않습니다.

319행 그림 · 값 복사

매크로 본문: kernel_entry

이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_PSEUDO_NMI

x20
이 명령을 실행하기 직전명령 직전 x20의 값
이 명령을 실행한 직후GIC_PRIO_IRQON | GIC_PRIO_PSR_I_SET

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 화살표는 값의 변화를 뜻합니다. 주소값을 옮겨도 그 주소의 메모리 내용은 복사하지 않습니다.
  • 매크로 kernel_entry의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	msr_s	SYS_ICC_PMR_EL1, x20

준비한 우선순위 마스크를 ICC_PMR_EL1에 설치하여 이 예외 처리 구간의 인터럽트 수신 조건을 정합니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.

320행 그림 · 시스템 레지스터 쓰기

매크로 본문: kernel_entry

이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_PSEUDO_NMI

읽는 값x20
SYS_ICC_PMR_EL1
이 명령을 실행하기 직전명령 직전 SYS_ICC_PMR_EL1의 값
이 명령을 실행한 직후명령 직전 x20의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 접근 권한과 VHE 별칭 규칙에 따라 실제 대상이 정해집니다. 뒤의 ISB·DSB가 필요한지는 이 레지스터와 문맥에 따릅니다.
  • 매크로 kernel_entry의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
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
.Lskip_pmr_save\@:

여기에 .Lskip_pmr_save\@라는 위치 이름을 붙입니다. 다른 줄의 분기나 주소 계산이 이 위치를 찾아올 수 있습니다. 이름 자체가 CPU 작업을 추가하지는 않습니다.

322행 그림 · 위치에 이름 붙이기

매크로 본문: kernel_entry

이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_PSEUDO_NMI

레이블
처리 전현재 배치 위치
이 줄의 효과.Lskip_pmr_save\@
CPU 레지스터
처리 전그대로
이 줄의 효과레이블 자체가 값을 바꾸지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

#endif

빌드 조건으로 나누었던 구간을 닫습니다. 대상:

323행 그림 · 전처리 단계

매크로 본문: kernel_entry

이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_PSEUDO_NMI

소스
처리 전전처리 전
이 줄의 효과#endif
CPU 레지스터
처리 전해당 없음
이 줄의 효과실행되는 CPU 명령이 없습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	.endm

공통 작업의 정의를 여기서 마칩니다. RET가 아니라 빌드 도구가 읽는 경계입니다.

333행 그림 · 어셈블·링크 단계

매크로 본문: kernel_entry

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과.endm
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 끝 표기 자체가 RET나 분기를 실행하지는 않습니다.

kernel_exit: 복귀 전 제어 상태

원본 334–410행

	.macro	kernel_exit, el

pt_regs에 저장한 예외 전 문맥을 복원하는 매크로입니다. el=0은 사용자 복귀라 PAC·MTE·KPTI 등의 전환을 포함하고, el=1은 중단된 커널 실행으로 돌아갈 상태를 복구합니다. 이 줄은 어셈블러 매크로 정의의 시작입니다. 본문은 매크로를 사용하는 위치에 펼쳐지며, 정의 자체가 런타임 함수 호출은 아닙니다.

335행 그림 · 어셈블·링크 단계

매크로 본문: kernel_exit

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과.macro kernel_exit, el
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 뒤의 본문은 이 이름을 사용하는 위치에 펼쳐집니다. 정의 위치를 CPU가 함수처럼 호출하지 않습니다.
	.if	\el != 0

어셈블 단계에서 인자나 식을 검사해 필요한 명령만 만듭니다. \el != 0

336행 그림 · 어셈블·링크 단계

매크로 본문: kernel_exit

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과.if \el != 0
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	disable_daif

EL1으로 돌아갈 상태를 복구하는 동안 새 디버그·SError·IRQ·FIQ 예외가 끼지 않도록 DAIF 마스크를 모두 세웁니다. 최종 ERET가 저장된 SPSR의 원래 마스크 상태를 복원합니다. 매크로가 펼치는 MSR DAIFSet, #0xf는 네 예외 마스크 비트를 모두 1로 만듭니다.

337행 그림 · 매크로 전체의 상태 변화

매크로 본문: kernel_exit

이 줄을 포함하는 조건: .if \el != 0

DAIF
이 명령을 실행하기 직전명령 직전 DAIF의 값
이 명령을 실행한 직후D=A=I=F=1

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 매크로 kernel_exit의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
disable_daif 매크로 본문 · 37행

arch/arm64/include/asm/assembler.h · Linux v6.18.37

정의 선택 조건: #ifndef __ASM_ASSEMBLER_H

.macro disable_daif msr daifset, #0xf .endm
	.endif

어셈블 조건에 따라 생성할 코드를 나누던 범위가 끝납니다.

338행 그림 · 어셈블·링크 단계

매크로 본문: kernel_exit

이 줄을 포함하는 조건: .if \el != 0

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과.endif
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

#ifdef CONFIG_ARM64_PSEUDO_NMI

pseudo-NMI 지원 빌드에서는 진입 때 저장했던 PMR도 복원해야 원래 코드의 인터럽트 우선순위 차단 상태가 돌아옵니다. 미지원 빌드는 이 추가 복원이 없습니다. 이 조건은 빌드 전처리 단계에서 포함할 코드를 고릅니다. CPU가 실행 중 전처리 조건을 검사하지는 않습니다.

340행 그림 · 전처리 단계

매크로 본문: kernel_exit

소스
처리 전전처리 전
이 줄의 효과#ifdef CONFIG_ARM64_PSEUDO_NMI
CPU 레지스터
처리 전해당 없음
이 줄의 효과실행되는 CPU 명령이 없습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

alternative_if_not ARM64_HAS_GIC_PRIO_MASKING

GIC 우선순위 마스킹을 실제로 사용하지 않는 CPU는 아래 PMR 복원을 건너뜁니다. 사용 여부에 따른 런타임 코드 패치이며 매번 capability를 C if로 검사하는 문장은 아닙니다.

341행 그림 · CPU 기능에 따른 명령 패치

매크로 본문: kernel_exit

이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_PSEUDO_NMI

패치 전 코드
처리 전기본 명령열
이 줄의 효과기능·정책에 맞는 기본 코드 또는 교체 코드

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 빌드할 때 패치 정보를 만들고 커널의 대체 코드 처리에서 명령을 선택합니다. 이 소스 줄을 CPU가 매번 if문으로 실행하는 것은 아닙니다.
	b	.Lskip_pmr_restore\@

.Lskip_pmr_restore\@부터 실행을 계속합니다. 돌아올 주소를 새로 적지 않으므로 일반 함수 호출과는 다른 제어 이동입니다.

342행 그림 · 호출과 실행 위치

매크로 본문: kernel_exit

이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_PSEUDO_NMI

PC
이 명령을 실행하기 직전명령 직전 PC의 값
이 명령을 실행한 직후.Lskip_pmr_restore\@

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 새 복귀 주소를 LR에 기록하지 않습니다. RET 역시 메모리의 스택 값을 자동으로 꺼내는 명령은 아닙니다.
  • 매크로 kernel_exit의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
alternative_else_nop_endif

341행에서 시작한 ARM64_HAS_GIC_PRIO_MASKING 패치 묶음을 닫습니다. 조건에 맞는 본문을 사용하지 않는 쪽은 같은 길이의 NOP로 채워 뒤 명령의 배치를 유지합니다. 이 줄 자체가 CPU에서 조건을 검사하는 명령은 아닙니다.

343행 그림 · CPU 기능에 따른 명령 패치

매크로 본문: kernel_exit

이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_PSEUDO_NMI

패치 전 코드
처리 전기본 명령열
이 줄의 효과기능·정책에 맞는 기본 코드 또는 교체 코드

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 빌드할 때 패치 정보를 만들고 커널의 대체 코드 처리에서 명령을 선택합니다. 이 소스 줄을 CPU가 매번 if문으로 실행하는 것은 아닙니다.
	ldr	w20, [sp, #S_PMR]

sp를 기준으로 S_PMR만큼 계산한 메모리 위치에 보관된 값을 w20로 가져옵니다. 주소 숫자를 옮기는 MOV와 달리 실제 메모리를 읽습니다. 앞서 보관한 상태를 되살리거나 스택으로 전달된 인자를 얻는 과정입니다.

345행 그림 · 메모리 읽기

매크로 본문: kernel_exit

이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_PSEUDO_NMI

읽는 값sp
메모리[이전 sp + (S_PMR)] · 4바이트
이 명령을 실행하기 직전M0 (이 주소에 저장된 값)
이 명령을 실행한 직후M0 (이 주소에 저장된 값)
w20
이 명령을 실행하기 직전명령 직전 w20의 값
이 명령을 실행한 직후M0
x20[63:32]
이 명령을 실행하기 직전명령 직전 x20[63:32]의 값
이 명령을 실행한 직후0

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 주소 계산에 의한 기준 레지스터 갱신은 없습니다. 다만 LDR/LDP의 목적 레지스터가 기준 레지스터와 같으면 읽은 값이 그 레지스터를 덮어씁니다.
  • 메모리 칸은 낮은 주소부터 나열했습니다. SP를 줄이면 더 낮은 주소의 공간을 사용합니다. 화면의 아래쪽이 곧 낮은 주소라는 뜻은 아닙니다.
  • 매크로 kernel_exit의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	msr_s	SYS_ICC_PMR_EL1, x20

pt_regs에 저장한 우선순위 마스크를 ICC_PMR_EL1에 되돌립니다. 예외 전 코드가 막아 두었던 IRQ가 복귀 과정에서 잘못 허용되지 않게 합니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.

346행 그림 · 시스템 레지스터 쓰기

매크로 본문: kernel_exit

이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_PSEUDO_NMI

읽는 값x20
SYS_ICC_PMR_EL1
이 명령을 실행하기 직전명령 직전 SYS_ICC_PMR_EL1의 값
이 명령을 실행한 직후명령 직전 x20의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 접근 권한과 VHE 별칭 규칙에 따라 실제 대상이 정해집니다. 뒤의 ISB·DSB가 필요한지는 이 레지스터와 문맥에 따릅니다.
  • 매크로 kernel_exit의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
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
alternative_if_not ARM64_HAS_GIC_PRIO_RELAXED_SYNC

GIC가 PMR 갱신의 완화된 동기화 조건을 지원하지 않으면 DSB SY를 남겨 변경 완료를 기다립니다. 지원하는 시스템에서는 이 DSB를 NOP 대안으로 제거할 수 있습니다. 이 매크로는 기본·교체 명령의 패치 정보를 만들며, CPU가 이 줄을 매번 if 조건처럼 실행하지 않습니다.

349행 그림 · CPU 기능에 따른 명령 패치

매크로 본문: kernel_exit

이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_PSEUDO_NMI

패치 전 코드
처리 전기본 명령열
이 줄의 효과기능·정책에 맞는 기본 코드 또는 교체 코드

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 빌드할 때 패치 정보를 만들고 커널의 대체 코드 처리에서 명령을 선택합니다. 이 소스 줄을 CPU가 매번 if문으로 실행하는 것은 아닙니다.
	dsb	sy

지정 범위·종류의 앞선 작업이 완료되기를 기다립니다. 페이지 복사나 캐시 관리가 끝나기 전에 다음 상태로 넘어가지 않도록 하는 대기 지점입니다.

350행 그림 · 동기화·캐시·대기 상태

매크로 본문: kernel_exit

이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_PSEUDO_NMI

작업 완료
이 명령을 실행하기 직전실행 전 상태
이 명령을 실행한 직후지정 범위의 앞선 작업 완료까지 기다림

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 매크로 kernel_exit의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
alternative_else_nop_endif

349행에서 시작한 ARM64_HAS_GIC_PRIO_RELAXED_SYNC 패치 묶음을 닫습니다. 조건에 맞는 본문을 사용하지 않는 쪽은 같은 길이의 NOP로 채워 뒤 명령의 배치를 유지합니다. 이 줄 자체가 CPU에서 조건을 검사하는 명령은 아닙니다.

351행 그림 · CPU 기능에 따른 명령 패치

매크로 본문: kernel_exit

이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_PSEUDO_NMI

패치 전 코드
처리 전기본 명령열
이 줄의 효과기능·정책에 맞는 기본 코드 또는 교체 코드

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 빌드할 때 패치 정보를 만들고 커널의 대체 코드 처리에서 명령을 선택합니다. 이 소스 줄을 CPU가 매번 if문으로 실행하는 것은 아닙니다.
.Lskip_pmr_restore\@:

여기에 .Lskip_pmr_restore\@라는 위치 이름을 붙입니다. 다른 줄의 분기나 주소 계산이 이 위치를 찾아올 수 있습니다. 이름 자체가 CPU 작업을 추가하지는 않습니다.

353행 그림 · 위치에 이름 붙이기

매크로 본문: kernel_exit

이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_PSEUDO_NMI

레이블
처리 전현재 배치 위치
이 줄의 효과.Lskip_pmr_restore\@
CPU 레지스터
처리 전그대로
이 줄의 효과레이블 자체가 값을 바꾸지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

#endif

빌드 조건으로 나누었던 구간을 닫습니다. 대상:

354행 그림 · 전처리 단계

매크로 본문: kernel_exit

이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_PSEUDO_NMI

소스
처리 전전처리 전
이 줄의 효과#endif
CPU 레지스터
처리 전해당 없음
이 줄의 효과실행되는 CPU 명령이 없습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	ldp	x21, x22, [sp, #S_PC]		// load ELR, SPSR

sp를 기준으로 S_PC만큼 계산한 메모리 위치에 보관된 값을 x21, x22로 가져옵니다. 주소 숫자를 옮기는 MOV와 달리 실제 메모리를 읽습니다. 첫 위치에서 x21, 그보다 8바이트 높은 위치에서 x22의 값을 읽습니다. 앞서 보관한 상태를 되살리거나 스택으로 전달된 인자를 얻는 과정입니다.

356행 그림 · 메모리 읽기

매크로 본문: kernel_exit

읽는 값sp
메모리[이전 sp + (S_PC)] · 8바이트
이 명령을 실행하기 직전M0 (이 주소에 저장된 값)
이 명령을 실행한 직후M0 (이 주소에 저장된 값)
x21
이 명령을 실행하기 직전명령 직전 x21의 값
이 명령을 실행한 직후M0
메모리[이전 sp + (S_PC) + 8] · 8바이트
이 명령을 실행하기 직전M1 (이 주소에 저장된 값)
이 명령을 실행한 직후M1 (이 주소에 저장된 값)
x22
이 명령을 실행하기 직전명령 직전 x22의 값
이 명령을 실행한 직후M1

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 주소 계산에 의한 기준 레지스터 갱신은 없습니다. 다만 LDR/LDP의 목적 레지스터가 기준 레지스터와 같으면 읽은 값이 그 레지스터를 덮어씁니다.
  • 메모리 칸은 낮은 주소부터 나열했습니다. SP를 줄이면 더 낮은 주소의 공간을 사용합니다. 화면의 아래쪽이 곧 낮은 주소라는 뜻은 아닙니다.
  • 매크로 kernel_exit의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
#ifdef CONFIG_ARM64_SW_TTBR0_PAN

소프트웨어 PAN을 사용하는 빌드에서는 복귀 대상이 원래 사용자 접근을 허용했는지에 따라 TTBR0를 복구합니다. 설정이 없으면 이 emulation 경로를 포함하지 않습니다. 이 조건은 빌드 전처리 단계에서 포함할 코드를 고릅니다. CPU가 실행 중 전처리 조건을 검사하지는 않습니다.

358행 그림 · 전처리 단계

매크로 본문: kernel_exit

소스
처리 전전처리 전
이 줄의 효과#ifdef CONFIG_ARM64_SW_TTBR0_PAN
CPU 레지스터
처리 전해당 없음
이 줄의 효과실행되는 CPU 명령이 없습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

alternative_if_not ARM64_HAS_PAN

하드웨어 PAN이 없는 CPU에서만 __swpan_exit 호출을 남깁니다. 하드웨어 PAN이 있으면 저장된 PSTATE의 실제 PAN 상태가 의미를 갖고 이 소프트웨어 TTBR0 복원은 필요하지 않습니다. 이 매크로는 기본·교체 명령의 패치 정보를 만들며, CPU가 이 줄을 매번 if 조건처럼 실행하지 않습니다.

359행 그림 · CPU 기능에 따른 명령 패치

매크로 본문: kernel_exit

이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_SW_TTBR0_PAN

패치 전 코드
처리 전기본 명령열
이 줄의 효과기능·정책에 맞는 기본 코드 또는 교체 코드

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 빌드할 때 패치 정보를 만들고 커널의 대체 코드 처리에서 명령을 선택합니다. 이 소스 줄을 CPU가 매번 if문으로 실행하는 것은 아닙니다.
	bl	__swpan_exit_el\el

복귀 대상 EL에 맞는 소프트웨어 PAN 해제를 호출합니다. EL0 복귀는 사용자 표를 다시 연결하고, EL1 복귀는 저장한 모의 PAN 표식에 따라 기존 사용자 접근 허용 여부를 복원합니다. BL은 다음 명령의 주소를 LR(x30)에 기록하고 목적지로 분기합니다.

360행 그림 · 호출과 실행 위치

매크로 본문: kernel_exit

이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_SW_TTBR0_PAN

PC
이 명령을 실행하기 직전명령 직전 PC의 값
이 명령을 실행한 직후__swpan_exit_el\el
LR (x30)
이 명령을 실행하기 직전명령 직전 LR의 값 (x30)
이 명령을 실행한 직후이 BL/BLR 다음 명령 주소

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 그림은 분기 명령 자체의 효과입니다. 호출된 함수가 실행된 뒤의 레지스터 값은 그 함수의 반환·보존 규칙을 따라야 합니다.
  • 매크로 kernel_exit의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
alternative_else_nop_endif

359행에서 시작한 ARM64_HAS_PAN 패치 묶음을 닫습니다. 조건에 맞는 본문을 사용하지 않는 쪽은 같은 길이의 NOP로 채워 뒤 명령의 배치를 유지합니다. 이 줄 자체가 CPU에서 조건을 검사하는 명령은 아닙니다.

361행 그림 · CPU 기능에 따른 명령 패치

매크로 본문: kernel_exit

이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_SW_TTBR0_PAN

패치 전 코드
처리 전기본 명령열
이 줄의 효과기능·정책에 맞는 기본 코드 또는 교체 코드

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 빌드할 때 패치 정보를 만들고 커널의 대체 코드 처리에서 명령을 선택합니다. 이 소스 줄을 CPU가 매번 if문으로 실행하는 것은 아닙니다.
#endif

빌드 조건으로 나누었던 구간을 닫습니다. 대상:

362행 그림 · 전처리 단계

매크로 본문: kernel_exit

이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_SW_TTBR0_PAN

소스
처리 전전처리 전
이 줄의 효과#endif
CPU 레지스터
처리 전해당 없음
이 줄의 효과실행되는 CPU 명령이 없습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	.if	\el == 0

어셈블 단계에서 인자나 식을 검사해 필요한 명령만 만듭니다. \el == 0

364행 그림 · 어셈블·링크 단계

매크로 본문: kernel_exit

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과.if \el == 0
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	ldr	x23, [sp, #S_SP]		// load return stack pointer

sp를 기준으로 S_SP만큼 계산한 메모리 위치에 보관된 값을 x23로 가져옵니다. 주소 숫자를 옮기는 MOV와 달리 실제 메모리를 읽습니다. 앞서 보관한 상태를 되살리거나 스택으로 전달된 인자를 얻는 과정입니다.

365행 그림 · 메모리 읽기

매크로 본문: kernel_exit

이 줄을 포함하는 조건: .if \el == 0

읽는 값sp
메모리[이전 sp + (S_SP)] · 8바이트
이 명령을 실행하기 직전M0 (이 주소에 저장된 값)
이 명령을 실행한 직후M0 (이 주소에 저장된 값)
x23
이 명령을 실행하기 직전명령 직전 x23의 값
이 명령을 실행한 직후M0

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 주소 계산에 의한 기준 레지스터 갱신은 없습니다. 다만 LDR/LDP의 목적 레지스터가 기준 레지스터와 같으면 읽은 값이 그 레지스터를 덮어씁니다.
  • 메모리 칸은 낮은 주소부터 나열했습니다. SP를 줄이면 더 낮은 주소의 공간을 사용합니다. 화면의 아래쪽이 곧 낮은 주소라는 뜻은 아닙니다.
  • 매크로 kernel_exit의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	msr	sp_el0, x23

pt_regs에서 읽은 사용자 SP를 SP_EL0에 되돌립니다. 커널 실행 중에는 이 레지스터를 current 포인터로 썼지만 ERET 후에는 사용자 스택 위치여야 합니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.

366행 그림 · 시스템 레지스터 쓰기

매크로 본문: kernel_exit

이 줄을 포함하는 조건: .if \el == 0

읽는 값x23
SP_EL0
이 명령을 실행하기 직전명령 직전 SP_EL0의 값
이 명령을 실행한 직후명령 직전 x23의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 접근 권한과 VHE 별칭 규칙에 따라 실제 대상이 정해집니다. 뒤의 ISB·DSB가 필요한지는 이 레지스터와 문맥에 따릅니다.
  • 매크로 kernel_exit의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	tst	x22, #PSR_MODE32_BIT		// native task?

저장한 PSTATE의 MODE32 비트로 AArch32 사용자 복귀인지 확인합니다. 아래 Cortex-A53 erratum 처리는 이 32비트 복귀 문맥에만 필요합니다. TST는 두 피연산자의 비트 AND 결과로 조건 플래그를 갱신하며 원래 레지스터 값은 바꾸지 않습니다.

NZCV는 이 TST가 정합니다. EQ이면 AArch64 사용자 태스크 경로입니다.

367행 그림 · 조건 플래그 계산

매크로 본문: kernel_exit

이 줄을 포함하는 조건: .if \el == 0

읽는 값x22
NZCV
이 명령을 실행하기 직전명령 직전 NZCV의 값
이 명령을 실행한 직후명령 직전 x22의 값 AND PSR_MODE32_BIT의 조건 결과

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 일반 레지스터에 연산 결과를 쓰지 않고 조건 플래그만 바꿉니다.
  • N은 결과의 부호 비트, Z는 결과가 0인지 나타냅니다. 이 AArch64 TST는 C와 V를 0으로 만듭니다.
  • 매크로 kernel_exit의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	b.eq	3f

앞서 계산한 조건 플래그가 Z=1이면 3f (L380)로 갑니다. 사이에 플래그를 바꾸지 않는 명령이 있어도 비교 결과는 남아 있습니다.

368행 그림 · 조건별 실행 위치

매크로 본문: kernel_exit

이 줄을 포함하는 조건: .if \el == 0

읽는 값NZCV
PC
이 명령을 실행하기 직전명령 직전 PC의 값
이 명령을 실행한 직후Z=1이면 3f (L380), 아니면 다음 명령

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 조건이 맞는 한 경로만 실행합니다. 이 분기 명령은 NZCV를 새로 계산하지 않습니다.
  • 매크로 kernel_exit의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
#ifdef CONFIG_ARM64_ERRATUM_845719

Cortex-A53 erratum 845719 우회 코드를 빌드하는 설정입니다. 켜진 빌드에서는 영향을 받는 CPU의 AArch32 복귀 전에 CONTEXTIDR을 다시 쓰는 경로를 포함하고, 꺼지면 해당 보완이 없습니다. 이 조건은 빌드 전처리 단계에서 포함할 코드를 고릅니다. CPU가 실행 중 전처리 조건을 검사하지는 않습니다.

370행 그림 · 전처리 단계

매크로 본문: kernel_exit

이 줄을 포함하는 조건: .if \el == 0

소스
처리 전전처리 전
이 줄의 효과#ifdef CONFIG_ARM64_ERRATUM_845719
CPU 레지스터
처리 전해당 없음
이 줄의 효과실행되는 CPU 명령이 없습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

alternative_if ARM64_WORKAROUND_845719

실제로 erratum 845719의 영향을 받는 CPU에서만 CONTEXTIDR 재기록을 남깁니다. 설정을 켰어도 영향을 받지 않으면 같은 크기의 NOP로 바뀝니다. 이 매크로는 기본·교체 명령의 패치 정보를 만들며, CPU가 이 줄을 매번 if 조건처럼 실행하지 않습니다.

371행 그림 · CPU 기능에 따른 명령 패치

매크로 본문: kernel_exit

이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_ERRATUM_845719 / .if \el == 0

패치 전 코드
처리 전기본 명령열
이 줄의 효과기능·정책에 맞는 기본 코드 또는 교체 코드

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 빌드할 때 패치 정보를 만들고 커널의 대체 코드 처리에서 명령을 선택합니다. 이 소스 줄을 CPU가 매번 if문으로 실행하는 것은 아닙니다.
#ifdef CONFIG_PID_IN_CONTEXTIDR

PID를 CONTEXTIDR에 기록해 추적하는 빌드라면 그 값을 보존한 채 다시 써야 합니다. 이 기능이 꺼진 빌드는 아래 #else에서 0을 써도 됩니다. 이 조건은 빌드 전처리 단계에서 포함할 코드를 고릅니다. CPU가 실행 중 전처리 조건을 검사하지는 않습니다.

372행 그림 · 전처리 단계

매크로 본문: kernel_exit

이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_ERRATUM_845719 / .if \el == 0

소스
처리 전전처리 전
이 줄의 효과#ifdef CONFIG_PID_IN_CONTEXTIDR
CPU 레지스터
처리 전해당 없음
이 줄의 효과실행되는 CPU 명령이 없습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	mrs	x29, contextidr_el1

추적용 PID 등의 기존 CONTEXTIDR 값을 읽습니다. erratum 보완을 위해 재기록하되 디버깅·추적 식별자를 잃지 않으려는 목적입니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.

373행 그림 · 시스템 레지스터 읽기

매크로 본문: kernel_exit

이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_ERRATUM_845719 / #ifdef CONFIG_PID_IN_CONTEXTIDR / .if \el == 0

읽는 값CONTEXTIDR_EL1
x29
이 명령을 실행하기 직전명령 직전 x29의 값
이 명령을 실행한 직후명령 직전 CONTEXTIDR_EL1의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 일반 RAM의 메모리 접근과 구분합니다.
  • 매크로 kernel_exit의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	msr	contextidr_el1, x29

방금 읽은 CONTEXTIDR을 같은 값으로 다시 기록합니다. 값 변경이 아니라 이 시스템 레지스터 쓰기 자체가 필요한 CPU 보완입니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.

374행 그림 · 시스템 레지스터 쓰기

매크로 본문: kernel_exit

이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_ERRATUM_845719 / #ifdef CONFIG_PID_IN_CONTEXTIDR / .if \el == 0

읽는 값x29
CONTEXTIDR_EL1
이 명령을 실행하기 직전명령 직전 CONTEXTIDR_EL1의 값
이 명령을 실행한 직후명령 직전 x29의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 접근 권한과 VHE 별칭 규칙에 따라 실제 대상이 정해집니다. 뒤의 ISB·DSB가 필요한지는 이 레지스터와 문맥에 따릅니다.
  • 매크로 kernel_exit의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
#else

CONTEXTIDR에 PID를 기록하지 않는 빌드는 유지해야 할 PID 값이 없으므로 0을 재기록하는 보완 경로를 사용합니다.

375행 그림 · 전처리 단계

매크로 본문: kernel_exit

이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_ERRATUM_845719 / #ifdef CONFIG_PID_IN_CONTEXTIDR / .if \el == 0

소스
처리 전전처리 전
이 줄의 효과#else
CPU 레지스터
처리 전해당 없음
이 줄의 효과실행되는 CPU 명령이 없습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	msr contextidr_el1, xzr

CONTEXTIDR에 0을 써 AArch32 복귀에 필요한 erratum 보완 동작을 수행합니다. 현재 task의 PID를 0으로 바꾸는 코드가 아닙니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.

376행 그림 · 시스템 레지스터 쓰기

매크로 본문: kernel_exit

이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_ERRATUM_845719 / 앞 조건의 반대 / .if \el == 0

읽는 값xzr
CONTEXTIDR_EL1
이 명령을 실행하기 직전명령 직전 CONTEXTIDR_EL1의 값
이 명령을 실행한 직후0

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 접근 권한과 VHE 별칭 규칙에 따라 실제 대상이 정해집니다. 뒤의 ISB·DSB가 필요한지는 이 레지스터와 문맥에 따릅니다.
  • 매크로 kernel_exit의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
#endif

빌드 조건으로 나누었던 구간을 닫습니다. 대상:

377행 그림 · 전처리 단계

매크로 본문: kernel_exit

이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_ERRATUM_845719 / 앞 조건의 반대 / .if \el == 0

소스
처리 전전처리 전
이 줄의 효과#endif
CPU 레지스터
처리 전해당 없음
이 줄의 효과실행되는 CPU 명령이 없습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

alternative_else_nop_endif

371행에서 시작한 ARM64_WORKAROUND_845719 패치 묶음을 닫습니다. 조건에 맞는 본문을 사용하지 않는 쪽은 같은 길이의 NOP로 채워 뒤 명령의 배치를 유지합니다. 이 줄 자체가 CPU에서 조건을 검사하는 명령은 아닙니다.

378행 그림 · CPU 기능에 따른 명령 패치

매크로 본문: kernel_exit

이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_ERRATUM_845719 / .if \el == 0

패치 전 코드
처리 전기본 명령열
이 줄의 효과기능·정책에 맞는 기본 코드 또는 교체 코드

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 빌드할 때 패치 정보를 만들고 커널의 대체 코드 처리에서 명령을 선택합니다. 이 소스 줄을 CPU가 매번 if문으로 실행하는 것은 아닙니다.
#endif

빌드 조건으로 나누었던 구간을 닫습니다. 대상:

379행 그림 · 전처리 단계

매크로 본문: kernel_exit

이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_ERRATUM_845719 / .if \el == 0

소스
처리 전전처리 전
이 줄의 효과#endif
CPU 레지스터
처리 전해당 없음
이 줄의 효과실행되는 CPU 명령이 없습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

3:

여기에 3라는 위치 이름을 붙입니다. 다른 줄의 분기나 주소 계산이 이 위치를 찾아올 수 있습니다. 이름 자체가 CPU 작업을 추가하지는 않습니다.

380행 그림 · 위치에 이름 붙이기

매크로 본문: kernel_exit

이 줄을 포함하는 조건: .if \el == 0

레이블
처리 전현재 배치 위치
이 줄의 효과3
CPU 레지스터
처리 전그대로
이 줄의 효과레이블 자체가 값을 바꾸지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	scs_save tsk

일반 스택과 별도로 보호하는 반환 주소 스택의 현재 위치를 태스크에 기억해 둡니다. 여기의 인자는 tsk입니다.

381행 그림 · 매크로 전체의 상태 변화

매크로 본문: kernel_exit

이 줄을 포함하는 조건: .if \el == 0

읽는 값x18tsk
메모리[tsk + TSK_TI_SCS_SP]
이 명령을 실행하기 직전기존 값
이 명령을 실행한 직후명령 직전 x18의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • CONFIG_SHADOW_CALL_STACK이 없으면 저장 명령이 생성되지 않습니다.
  • 매크로 kernel_exit의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
scs_save 매크로 본문 · 18행

arch/arm64/include/asm/scs.h · Linux v6.18.37

정의 선택 조건: #ifndef _ASM_SCS_H / #ifdef __ASSEMBLY__ / #ifdef CONFIG_SHADOW_CALL_STACK

.macro scs_save tsk str scs_sp, [\tsk, #TSK_TI_SCS_SP] .endm
scs_save 매크로 본문 · 25행

arch/arm64/include/asm/scs.h · Linux v6.18.37

정의 선택 조건: #ifndef _ASM_SCS_H / #ifdef __ASSEMBLY__ / 앞 조건의 반대

.macro scs_save tsk .endm
	ldr	x0, [tsk, THREAD_SCTLR_USER]

사용자 task의 SCTLR 정책을 다시 읽어 MTE 오류 정리와 PAC 사용자 상태 복원에 사용합니다. 커널이 쓰던 설정을 그대로 사용자에게 넘기지 않기 위한 기준입니다. 실제로 tsk + (THREAD_SCTLR_USER) 주소의 메모리 8바이트를 x0에 읽습니다. 이 주소 형식은 기준 레지스터 tsk 자체를 갱신하지 않습니다.

384행 그림 · 메모리 읽기

매크로 본문: kernel_exit

이 줄을 포함하는 조건: .if \el == 0

읽는 값tsk
메모리[이전 tsk + (THREAD_SCTLR_USER)] · 8바이트
이 명령을 실행하기 직전M0 (이 주소에 저장된 값)
이 명령을 실행한 직후M0 (이 주소에 저장된 값)
x0
이 명령을 실행하기 직전명령 직전 x0의 값
이 명령을 실행한 직후M0

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 주소 계산에 의한 기준 레지스터 갱신은 없습니다. 다만 LDR/LDP의 목적 레지스터가 기준 레지스터와 같으면 읽은 값이 그 레지스터를 덮어씁니다.
  • 매크로 kernel_exit의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	clear_mte_async_tcf x0

비동기 MTE mode의 이전 실패 누적 상태를 동기화한 뒤 지웁니다. 새 사용자 실행에서 발생할 태그 오류와 이미 처리 중인 오류를 분리합니다. 매크로는 DSB로 앞선 태그 검사 처리를 동기화한 뒤 TFSRE0_EL1에 0을 씁니다.

385행 그림 · 매크로 안의 상태 변화

매크로 본문: kernel_exit

이 줄을 포함하는 조건: .if \el == 0

입력
이 명령을 실행하기 직전호출 직전 값
이 명령을 실행한 직후x0
주요 결과
이 명령을 실행하기 직전본문 실행 전
이 명령을 실행한 직후비동기 태그 검사 모드이면 DSB 후 TFSRE0_EL1을 0으로 지웁니다.

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 여러 명령과 내부 분기를 묶은 그림입니다. 작업 레지스터를 모두 보존한다는 뜻은 아니며, 정확한 변경 순서는 아래 매크로 본문과 줄별 그림에서 확인합니다.
  • 매크로 kernel_exit의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
clear_mte_async_tcf 매크로 본문 · 154행

arch/arm64/kernel/entry.S · Linux v6.18.37

.macro clear_mte_async_tcf thread_sctlr #ifdef CONFIG_ARM64_MTE alternative_if ARM64_MTE /* See comment in check_mte_async_tcf above. */ tbz \thread_sctlr, #(SCTLR_EL1_TCF0_SHIFT + 1), 1f dsb ish msr_s SYS_TFSRE0_EL1, xzr 1: alternative_else_nop_endif #endif .endm
#ifdef CONFIG_ARM64_PTR_AUTH

포인터 인증 지원 빌드에서는 사용자로 돌아가기 전에 사용자 APIA 키 또는 ENIA 비활성 정책을 복원합니다. 꺼진 빌드는 이 키·제어 전환 코드가 없습니다. 이 조건은 빌드 전처리 단계에서 포함할 코드를 고릅니다. CPU가 실행 중 전처리 조건을 검사하지는 않습니다.

387행 그림 · 전처리 단계

매크로 본문: kernel_exit

이 줄을 포함하는 조건: .if \el == 0

소스
처리 전전처리 전
이 줄의 효과#ifdef CONFIG_ARM64_PTR_AUTH
CPU 레지스터
처리 전해당 없음
이 줄의 효과실행되는 CPU 명령이 없습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

alternative_if ARM64_HAS_ADDRESS_AUTH

실제 주소 인증 기능이 있을 때만 사용자 키/ENIA 복구를 수행합니다. 없는 CPU는 시스템 레지스터 접근 대신 NOP 대안을 사용합니다. 이 매크로는 기본·교체 명령의 패치 정보를 만들며, CPU가 이 줄을 매번 if 조건처럼 실행하지 않습니다.

388행 그림 · CPU 기능에 따른 명령 패치

매크로 본문: kernel_exit

이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_PTR_AUTH / .if \el == 0

패치 전 코드
처리 전기본 명령열
이 줄의 효과기능·정책에 맞는 기본 코드 또는 교체 코드

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 빌드할 때 패치 정보를 만들고 커널의 대체 코드 처리에서 명령을 선택합니다. 이 소스 줄을 CPU가 매번 if문으로 실행하는 것은 아닙니다.
	tbz	x0, SCTLR_ELx_ENIA_SHIFT, 1f

사용자 task가 ENIA를 활성화했으면 사용자 키를 설치하고, 꺼 두었으면 ENIA를 지우는 경로를 선택합니다. task별 인증 정책을 존중하는 분기입니다. 이 분기는 지정 레지스터의 값이나 비트를 직접 검사하며 CMP가 남긴 NZCV를 읽거나 바꾸지 않습니다. 조건이 맞지 않으면 바로 다음 명령으로 진행합니다.

396행 그림 · 조건별 실행 위치

매크로 본문: kernel_exit

이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_PTR_AUTH / .if \el == 0

읽는 값x0
PC
이 명령을 실행하기 직전명령 직전 PC의 값
이 명령을 실행한 직후명령 직전 x0의 값의 bit SCTLR_ELx_ENIA_SHIFT = 0이면 1f (L399), 아니면 다음 명령

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 조건이 맞는 한 경로만 실행합니다. 이 분기 명령은 NZCV를 새로 계산하지 않습니다.
  • 매크로 kernel_exit의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	__ptrauth_keys_install_user tsk, x0, x1, x2

task_struct에 보존된 사용자 포인터 인증 키를 CPU에 설치합니다. 커널 키로 사용자 포인터를 검사하면 정상 복귀 주소도 인증되지 않으므로 구분해야 합니다. 이 매크로에서 복원하는 키는 사용자 명령어 주소 인증용 APIA 키이며, 모든 종류의 PAC 키를 한꺼번에 설치하는 것은 아닙니다.

397행 그림 · 매크로 안의 상태 변화

매크로 본문: kernel_exit

이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_PTR_AUTH / .if \el == 0

입력
이 명령을 실행하기 직전호출 직전 값
이 명령을 실행한 직후tsk, x0, x1, x2
주요 결과
이 명령을 실행하기 직전본문 실행 전
이 명령을 실행한 직후태스크의 사용자 instruction authentication 키를 시스템 레지스터에 설치합니다.

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 여러 명령과 내부 분기를 묶은 그림입니다. 작업 레지스터를 모두 보존한다는 뜻은 아니며, 정확한 변경 순서는 아래 매크로 본문과 줄별 그림에서 확인합니다.
  • 매크로 kernel_exit의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
__ptrauth_keys_install_user 매크로 본문 · 52행

arch/arm64/include/asm/asm_pointer_auth.h · Linux v6.18.37

정의 선택 조건: #ifndef __ASM_ASM_POINTER_AUTH_H / #ifdef CONFIG_ARM64_PTR_AUTH

.macro __ptrauth_keys_install_user tsk, tmp1, tmp2, tmp3 mov \tmp1, #THREAD_KEYS_USER add \tmp1, \tsk, \tmp1 ldp \tmp2, \tmp3, [\tmp1, #PTRAUTH_USER_KEY_APIA] msr_s SYS_APIAKEYLO_EL1, \tmp2 msr_s SYS_APIAKEYHI_EL1, \tmp3 .endm
	b	2f

2f (L403)부터 실행을 계속합니다. 돌아올 주소를 새로 적지 않으므로 일반 함수 호출과는 다른 제어 이동입니다.

398행 그림 · 호출과 실행 위치

매크로 본문: kernel_exit

이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_PTR_AUTH / .if \el == 0

PC
이 명령을 실행하기 직전명령 직전 PC의 값
이 명령을 실행한 직후2f (L403)

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 새 복귀 주소를 LR에 기록하지 않습니다. RET 역시 메모리의 스택 값을 자동으로 꺼내는 명령은 아닙니다.
  • 매크로 kernel_exit의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
1:

여기에 1라는 위치 이름을 붙입니다. 다른 줄의 분기나 주소 계산이 이 위치를 찾아올 수 있습니다. 이름 자체가 CPU 작업을 추가하지는 않습니다.

399행 그림 · 위치에 이름 붙이기

매크로 본문: kernel_exit

이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_PTR_AUTH / .if \el == 0

레이블
처리 전현재 배치 위치
이 줄의 효과1
CPU 레지스터
처리 전그대로
이 줄의 효과레이블 자체가 값을 바꾸지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	mrs	x0, sctlr_el1

기존 SCTLR의 다른 비트는 유지하면서 사용자 요청에 따라 ENIA만 끌 값을 준비합니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.

400행 그림 · 시스템 레지스터 읽기

매크로 본문: kernel_exit

이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_PTR_AUTH / .if \el == 0

읽는 값SCTLR_EL1
x0
이 명령을 실행하기 직전명령 직전 x0의 값
이 명령을 실행한 직후명령 직전 SCTLR_EL1의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 일반 RAM의 메모리 접근과 구분합니다.
  • 매크로 kernel_exit의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	bic	x0, x0, SCTLR_ELx_ENIA

사용자가 instruction-address A-key 인증을 비활성화한 정책에 맞춰 ENIA 비트를 지웁니다. 모든 PAC 기능과 모든 키를 0으로 만드는 동작은 아닙니다. BIC는 마스크에서 1인 위치만 0으로 만들고 나머지 비트는 유지합니다.

401행 그림 · 레지스터 상태

매크로 본문: kernel_exit

이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_PTR_AUTH / .if \el == 0

읽는 값x0
x0
이 명령을 실행하기 직전명령 직전 x0의 값
이 명령을 실행한 직후명령 직전 x0의 값 AND NOT SCTLR_ELx_ENIA

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 결과는 목적 레지스터 폭에 맞습니다. 이 형식은 NZCV를 바꾸지 않습니다.
  • 매크로 kernel_exit의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	msr	sctlr_el1, x0

ENIA를 지운 사용자 실행용 SCTLR 값을 설치합니다. 커널 진입 때 다시 켰던 상태를 사용자 요청으로 되돌리는 단계입니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.

402행 그림 · 시스템 레지스터 쓰기

매크로 본문: kernel_exit

이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_PTR_AUTH / .if \el == 0

읽는 값x0
SCTLR_EL1
이 명령을 실행하기 직전명령 직전 SCTLR_EL1의 값
이 명령을 실행한 직후명령 직전 x0의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 접근 권한과 VHE 별칭 규칙에 따라 실제 대상이 정해집니다. 뒤의 ISB·DSB가 필요한지는 이 레지스터와 문맥에 따릅니다.
  • 매크로 kernel_exit의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
2:

여기에 2라는 위치 이름을 붙입니다. 다른 줄의 분기나 주소 계산이 이 위치를 찾아올 수 있습니다. 이름 자체가 CPU 작업을 추가하지는 않습니다.

403행 그림 · 위치에 이름 붙이기

매크로 본문: kernel_exit

이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_PTR_AUTH / .if \el == 0

레이블
처리 전현재 배치 위치
이 줄의 효과2
CPU 레지스터
처리 전그대로
이 줄의 효과레이블 자체가 값을 바꾸지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

alternative_else_nop_endif

388행에서 시작한 ARM64_HAS_ADDRESS_AUTH 패치 묶음을 닫습니다. 조건에 맞는 본문을 사용하지 않는 쪽은 같은 길이의 NOP로 채워 뒤 명령의 배치를 유지합니다. 이 줄 자체가 CPU에서 조건을 검사하는 명령은 아닙니다.

404행 그림 · CPU 기능에 따른 명령 패치

매크로 본문: kernel_exit

이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_PTR_AUTH / .if \el == 0

패치 전 코드
처리 전기본 명령열
이 줄의 효과기능·정책에 맞는 기본 코드 또는 교체 코드

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 빌드할 때 패치 정보를 만들고 커널의 대체 코드 처리에서 명령을 선택합니다. 이 소스 줄을 CPU가 매번 if문으로 실행하는 것은 아닙니다.
#endif

빌드 조건으로 나누었던 구간을 닫습니다. 대상:

405행 그림 · 전처리 단계

매크로 본문: kernel_exit

이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_PTR_AUTH / .if \el == 0

소스
처리 전전처리 전
이 줄의 효과#endif
CPU 레지스터
처리 전해당 없음
이 줄의 효과실행되는 CPU 명령이 없습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	mte_set_user_gcr tsk, x0, x1

커널 하드웨어 태그 KASAN이 바꾸었던 GCR을 현재 사용자 task의 제외 태그 정책으로 복원합니다. tsk가 정책의 출처이며 x0·x1은 임시 레지스터입니다. 매크로는 task의 THREAD_MTE_CTRL 필드에서 사용자 정책을 읽습니다.

407행 그림 · 매크로 안의 상태 변화

매크로 본문: kernel_exit

이 줄을 포함하는 조건: .if \el == 0

입력
이 명령을 실행하기 직전호출 직전 값
이 명령을 실행한 직후tsk, x0, x1
주요 결과
이 명령을 실행하기 직전본문 실행 전
이 명령을 실행한 직후태스크의 THREAD_MTE_CTRL을 읽어 사용자 GCR 값을 적용합니다.

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 여러 명령과 내부 분기를 묶은 그림입니다. 작업 레지스터를 모두 보존한다는 뜻은 아니며, 정확한 변경 순서는 아래 매크로 본문과 줄별 그림에서 확인합니다.
  • 매크로 kernel_exit의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
mte_set_user_gcr 매크로 본문 · 185행

arch/arm64/kernel/entry.S · Linux v6.18.37

.macro mte_set_user_gcr, tsk, tmp, tmp2 #ifdef CONFIG_KASAN_HW_TAGS alternative_cb ARM64_ALWAYS_SYSTEM, kasan_hw_tags_enable b 1f alternative_cb_end ldr \tmp, [\tsk, #THREAD_MTE_CTRL] mte_set_gcr \tmp, \tmp2 1: #endif .endm
	apply_ssbd 0, x0, x1

사용자 복귀 쪽 SSBD 상태 0을 요청합니다. task가 완화를 유지하도록 요구하는 경우 등에는 apply_ssbd 내부에서 이 전환을 건너뛰므로 무조건 완화를 끄는 명령이 아닙니다.

409행 그림 · 매크로 안의 상태 변화

매크로 본문: kernel_exit

이 줄을 포함하는 조건: .if \el == 0

입력
이 명령을 실행하기 직전호출 직전 값
이 명령을 실행한 직후0, x0, x1
주요 결과
이 명령을 실행하기 직전본문 실행 전
이 명령을 실행한 직후CPU별 callback 필요 여부와 태스크 플래그에 따라 펌웨어의 SSBD 설정을 요청합니다. state=1은 진입, 0은 복귀 경로입니다.

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 여러 명령과 내부 분기를 묶은 그림입니다. 작업 레지스터를 모두 보존한다는 뜻은 아니며, 정확한 변경 순서는 아래 매크로 본문과 줄별 그림에서 확인합니다.
  • 매크로 kernel_exit의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
apply_ssbd 매크로 본문 · 114행

arch/arm64/kernel/entry.S · Linux v6.18.37

.macro apply_ssbd, state, tmp1, tmp2 alternative_cb ARM64_ALWAYS_SYSTEM, spectre_v4_patch_fw_mitigation_enable b .L__asm_ssbd_skip\@ // Patched to NOP alternative_cb_end ldr_this_cpu \tmp2, arm64_ssbd_callback_required, \tmp1 cbz \tmp2, .L__asm_ssbd_skip\@ ldr \tmp2, [tsk, #TSK_TI_FLAGS] tbnz \tmp2, #TIF_SSBD, .L__asm_ssbd_skip\@ mov w0, #ARM_SMCCC_ARCH_WORKAROUND_2 mov w1, #\state alternative_cb ARM64_ALWAYS_SYSTEM, smccc_patch_fw_mitigation_conduit nop // Patched to SMC/HVC #0 alternative_cb_end .L__asm_ssbd_skip\@: .endm
	.endif

어셈블 조건에 따라 생성할 코드를 나누던 범위가 끝납니다.

410행 그림 · 어셈블·링크 단계

매크로 본문: kernel_exit

이 줄을 포함하는 조건: .if \el == 0

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과.endif
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

kernel_exit: 레지스터 복원과 ERET

원본 411–463행

	msr	elr_el1, x21			// set up the return data

저장 프레임의 복귀 PC를 ELR_EL1에 설치합니다. 시그널·디버거 등이 pt_regs.pc를 바꾸었다면 변경된 위치로 ERET하게 됩니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.

412행 그림 · 시스템 레지스터 쓰기

매크로 본문: kernel_exit

읽는 값x21
ELR_EL1
이 명령을 실행하기 직전명령 직전 ELR_EL1의 값
이 명령을 실행한 직후명령 직전 x21의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 접근 권한과 VHE 별칭 규칙에 따라 실제 대상이 정해집니다. 뒤의 ISB·DSB가 필요한지는 이 레지스터와 문맥에 따릅니다.
  • 매크로 kernel_exit의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	msr	spsr_el1, x22

저장된 복귀 PSTATE를 SPSR_EL1에 설치합니다. ERET가 실행 레벨·마스크·flags를 이 값에서 복원합니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.

413행 그림 · 시스템 레지스터 쓰기

매크로 본문: kernel_exit

읽는 값x22
SPSR_EL1
이 명령을 실행하기 직전명령 직전 SPSR_EL1의 값
이 명령을 실행한 직후명령 직전 x22의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 접근 권한과 VHE 별칭 규칙에 따라 실제 대상이 정해집니다. 뒤의 ISB·DSB가 필요한지는 이 레지스터와 문맥에 따릅니다.
  • 매크로 kernel_exit의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	ldp	x0, x1, [sp, #16 * 0]

sp를 기준으로 16 * 0만큼 계산한 메모리 위치에 보관된 값을 x0, x1로 가져옵니다. 주소 숫자를 옮기는 MOV와 달리 실제 메모리를 읽습니다. 첫 위치에서 x0, 그보다 8바이트 높은 위치에서 x1의 값을 읽습니다. 앞서 보관한 상태를 되살리거나 스택으로 전달된 인자를 얻는 과정입니다.

414행 그림 · 메모리 읽기

매크로 본문: kernel_exit

읽는 값sp
메모리[이전 sp + (16 * 0)] · 8바이트
이 명령을 실행하기 직전M0 (이 주소에 저장된 값)
이 명령을 실행한 직후M0 (이 주소에 저장된 값)
x0
이 명령을 실행하기 직전명령 직전 x0의 값
이 명령을 실행한 직후M0
메모리[이전 sp + (16 * 0) + 8] · 8바이트
이 명령을 실행하기 직전M1 (이 주소에 저장된 값)
이 명령을 실행한 직후M1 (이 주소에 저장된 값)
x1
이 명령을 실행하기 직전명령 직전 x1의 값
이 명령을 실행한 직후M1

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 주소 계산에 의한 기준 레지스터 갱신은 없습니다. 다만 LDR/LDP의 목적 레지스터가 기준 레지스터와 같으면 읽은 값이 그 레지스터를 덮어씁니다.
  • 메모리 칸은 낮은 주소부터 나열했습니다. SP를 줄이면 더 낮은 주소의 공간을 사용합니다. 화면의 아래쪽이 곧 낮은 주소라는 뜻은 아닙니다.
  • 매크로 kernel_exit의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	ldp	x2, x3, [sp, #16 * 1]

sp를 기준으로 16 * 1만큼 계산한 메모리 위치에 보관된 값을 x2, x3로 가져옵니다. 주소 숫자를 옮기는 MOV와 달리 실제 메모리를 읽습니다. 첫 위치에서 x2, 그보다 8바이트 높은 위치에서 x3의 값을 읽습니다. 앞서 보관한 상태를 되살리거나 스택으로 전달된 인자를 얻는 과정입니다.

415행 그림 · 메모리 읽기

매크로 본문: kernel_exit

읽는 값sp
메모리[이전 sp + (16 * 1)] · 8바이트
이 명령을 실행하기 직전M0 (이 주소에 저장된 값)
이 명령을 실행한 직후M0 (이 주소에 저장된 값)
x2
이 명령을 실행하기 직전명령 직전 x2의 값
이 명령을 실행한 직후M0
메모리[이전 sp + (16 * 1) + 8] · 8바이트
이 명령을 실행하기 직전M1 (이 주소에 저장된 값)
이 명령을 실행한 직후M1 (이 주소에 저장된 값)
x3
이 명령을 실행하기 직전명령 직전 x3의 값
이 명령을 실행한 직후M1

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 주소 계산에 의한 기준 레지스터 갱신은 없습니다. 다만 LDR/LDP의 목적 레지스터가 기준 레지스터와 같으면 읽은 값이 그 레지스터를 덮어씁니다.
  • 메모리 칸은 낮은 주소부터 나열했습니다. SP를 줄이면 더 낮은 주소의 공간을 사용합니다. 화면의 아래쪽이 곧 낮은 주소라는 뜻은 아닙니다.
  • 매크로 kernel_exit의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	ldp	x4, x5, [sp, #16 * 2]

sp를 기준으로 16 * 2만큼 계산한 메모리 위치에 보관된 값을 x4, x5로 가져옵니다. 주소 숫자를 옮기는 MOV와 달리 실제 메모리를 읽습니다. 첫 위치에서 x4, 그보다 8바이트 높은 위치에서 x5의 값을 읽습니다. 앞서 보관한 상태를 되살리거나 스택으로 전달된 인자를 얻는 과정입니다.

416행 그림 · 메모리 읽기

매크로 본문: kernel_exit

읽는 값sp
메모리[이전 sp + (16 * 2)] · 8바이트
이 명령을 실행하기 직전M0 (이 주소에 저장된 값)
이 명령을 실행한 직후M0 (이 주소에 저장된 값)
x4
이 명령을 실행하기 직전명령 직전 x4의 값
이 명령을 실행한 직후M0
메모리[이전 sp + (16 * 2) + 8] · 8바이트
이 명령을 실행하기 직전M1 (이 주소에 저장된 값)
이 명령을 실행한 직후M1 (이 주소에 저장된 값)
x5
이 명령을 실행하기 직전명령 직전 x5의 값
이 명령을 실행한 직후M1

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 주소 계산에 의한 기준 레지스터 갱신은 없습니다. 다만 LDR/LDP의 목적 레지스터가 기준 레지스터와 같으면 읽은 값이 그 레지스터를 덮어씁니다.
  • 메모리 칸은 낮은 주소부터 나열했습니다. SP를 줄이면 더 낮은 주소의 공간을 사용합니다. 화면의 아래쪽이 곧 낮은 주소라는 뜻은 아닙니다.
  • 매크로 kernel_exit의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	ldp	x6, x7, [sp, #16 * 3]

sp를 기준으로 16 * 3만큼 계산한 메모리 위치에 보관된 값을 x6, x7로 가져옵니다. 주소 숫자를 옮기는 MOV와 달리 실제 메모리를 읽습니다. 첫 위치에서 x6, 그보다 8바이트 높은 위치에서 x7의 값을 읽습니다. 앞서 보관한 상태를 되살리거나 스택으로 전달된 인자를 얻는 과정입니다.

417행 그림 · 메모리 읽기

매크로 본문: kernel_exit

읽는 값sp
메모리[이전 sp + (16 * 3)] · 8바이트
이 명령을 실행하기 직전M0 (이 주소에 저장된 값)
이 명령을 실행한 직후M0 (이 주소에 저장된 값)
x6
이 명령을 실행하기 직전명령 직전 x6의 값
이 명령을 실행한 직후M0
메모리[이전 sp + (16 * 3) + 8] · 8바이트
이 명령을 실행하기 직전M1 (이 주소에 저장된 값)
이 명령을 실행한 직후M1 (이 주소에 저장된 값)
x7
이 명령을 실행하기 직전명령 직전 x7의 값
이 명령을 실행한 직후M1

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 주소 계산에 의한 기준 레지스터 갱신은 없습니다. 다만 LDR/LDP의 목적 레지스터가 기준 레지스터와 같으면 읽은 값이 그 레지스터를 덮어씁니다.
  • 메모리 칸은 낮은 주소부터 나열했습니다. SP를 줄이면 더 낮은 주소의 공간을 사용합니다. 화면의 아래쪽이 곧 낮은 주소라는 뜻은 아닙니다.
  • 매크로 kernel_exit의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	ldp	x8, x9, [sp, #16 * 4]

sp를 기준으로 16 * 4만큼 계산한 메모리 위치에 보관된 값을 x8, x9로 가져옵니다. 주소 숫자를 옮기는 MOV와 달리 실제 메모리를 읽습니다. 첫 위치에서 x8, 그보다 8바이트 높은 위치에서 x9의 값을 읽습니다. 앞서 보관한 상태를 되살리거나 스택으로 전달된 인자를 얻는 과정입니다.

418행 그림 · 메모리 읽기

매크로 본문: kernel_exit

읽는 값sp
메모리[이전 sp + (16 * 4)] · 8바이트
이 명령을 실행하기 직전M0 (이 주소에 저장된 값)
이 명령을 실행한 직후M0 (이 주소에 저장된 값)
x8
이 명령을 실행하기 직전명령 직전 x8의 값
이 명령을 실행한 직후M0
메모리[이전 sp + (16 * 4) + 8] · 8바이트
이 명령을 실행하기 직전M1 (이 주소에 저장된 값)
이 명령을 실행한 직후M1 (이 주소에 저장된 값)
x9
이 명령을 실행하기 직전명령 직전 x9의 값
이 명령을 실행한 직후M1

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 주소 계산에 의한 기준 레지스터 갱신은 없습니다. 다만 LDR/LDP의 목적 레지스터가 기준 레지스터와 같으면 읽은 값이 그 레지스터를 덮어씁니다.
  • 메모리 칸은 낮은 주소부터 나열했습니다. SP를 줄이면 더 낮은 주소의 공간을 사용합니다. 화면의 아래쪽이 곧 낮은 주소라는 뜻은 아닙니다.
  • 매크로 kernel_exit의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	ldp	x10, x11, [sp, #16 * 5]

sp를 기준으로 16 * 5만큼 계산한 메모리 위치에 보관된 값을 x10, x11로 가져옵니다. 주소 숫자를 옮기는 MOV와 달리 실제 메모리를 읽습니다. 첫 위치에서 x10, 그보다 8바이트 높은 위치에서 x11의 값을 읽습니다. 앞서 보관한 상태를 되살리거나 스택으로 전달된 인자를 얻는 과정입니다.

419행 그림 · 메모리 읽기

매크로 본문: kernel_exit

읽는 값sp
메모리[이전 sp + (16 * 5)] · 8바이트
이 명령을 실행하기 직전M0 (이 주소에 저장된 값)
이 명령을 실행한 직후M0 (이 주소에 저장된 값)
x10
이 명령을 실행하기 직전명령 직전 x10의 값
이 명령을 실행한 직후M0
메모리[이전 sp + (16 * 5) + 8] · 8바이트
이 명령을 실행하기 직전M1 (이 주소에 저장된 값)
이 명령을 실행한 직후M1 (이 주소에 저장된 값)
x11
이 명령을 실행하기 직전명령 직전 x11의 값
이 명령을 실행한 직후M1

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 주소 계산에 의한 기준 레지스터 갱신은 없습니다. 다만 LDR/LDP의 목적 레지스터가 기준 레지스터와 같으면 읽은 값이 그 레지스터를 덮어씁니다.
  • 메모리 칸은 낮은 주소부터 나열했습니다. SP를 줄이면 더 낮은 주소의 공간을 사용합니다. 화면의 아래쪽이 곧 낮은 주소라는 뜻은 아닙니다.
  • 매크로 kernel_exit의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	ldp	x12, x13, [sp, #16 * 6]

sp를 기준으로 16 * 6만큼 계산한 메모리 위치에 보관된 값을 x12, x13로 가져옵니다. 주소 숫자를 옮기는 MOV와 달리 실제 메모리를 읽습니다. 첫 위치에서 x12, 그보다 8바이트 높은 위치에서 x13의 값을 읽습니다. 앞서 보관한 상태를 되살리거나 스택으로 전달된 인자를 얻는 과정입니다.

420행 그림 · 메모리 읽기

매크로 본문: kernel_exit

읽는 값sp
메모리[이전 sp + (16 * 6)] · 8바이트
이 명령을 실행하기 직전M0 (이 주소에 저장된 값)
이 명령을 실행한 직후M0 (이 주소에 저장된 값)
x12
이 명령을 실행하기 직전명령 직전 x12의 값
이 명령을 실행한 직후M0
메모리[이전 sp + (16 * 6) + 8] · 8바이트
이 명령을 실행하기 직전M1 (이 주소에 저장된 값)
이 명령을 실행한 직후M1 (이 주소에 저장된 값)
x13
이 명령을 실행하기 직전명령 직전 x13의 값
이 명령을 실행한 직후M1

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 주소 계산에 의한 기준 레지스터 갱신은 없습니다. 다만 LDR/LDP의 목적 레지스터가 기준 레지스터와 같으면 읽은 값이 그 레지스터를 덮어씁니다.
  • 메모리 칸은 낮은 주소부터 나열했습니다. SP를 줄이면 더 낮은 주소의 공간을 사용합니다. 화면의 아래쪽이 곧 낮은 주소라는 뜻은 아닙니다.
  • 매크로 kernel_exit의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	ldp	x14, x15, [sp, #16 * 7]

sp를 기준으로 16 * 7만큼 계산한 메모리 위치에 보관된 값을 x14, x15로 가져옵니다. 주소 숫자를 옮기는 MOV와 달리 실제 메모리를 읽습니다. 첫 위치에서 x14, 그보다 8바이트 높은 위치에서 x15의 값을 읽습니다. 앞서 보관한 상태를 되살리거나 스택으로 전달된 인자를 얻는 과정입니다.

421행 그림 · 메모리 읽기

매크로 본문: kernel_exit

읽는 값sp
메모리[이전 sp + (16 * 7)] · 8바이트
이 명령을 실행하기 직전M0 (이 주소에 저장된 값)
이 명령을 실행한 직후M0 (이 주소에 저장된 값)
x14
이 명령을 실행하기 직전명령 직전 x14의 값
이 명령을 실행한 직후M0
메모리[이전 sp + (16 * 7) + 8] · 8바이트
이 명령을 실행하기 직전M1 (이 주소에 저장된 값)
이 명령을 실행한 직후M1 (이 주소에 저장된 값)
x15
이 명령을 실행하기 직전명령 직전 x15의 값
이 명령을 실행한 직후M1

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 주소 계산에 의한 기준 레지스터 갱신은 없습니다. 다만 LDR/LDP의 목적 레지스터가 기준 레지스터와 같으면 읽은 값이 그 레지스터를 덮어씁니다.
  • 메모리 칸은 낮은 주소부터 나열했습니다. SP를 줄이면 더 낮은 주소의 공간을 사용합니다. 화면의 아래쪽이 곧 낮은 주소라는 뜻은 아닙니다.
  • 매크로 kernel_exit의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	ldp	x16, x17, [sp, #16 * 8]

sp를 기준으로 16 * 8만큼 계산한 메모리 위치에 보관된 값을 x16, x17로 가져옵니다. 주소 숫자를 옮기는 MOV와 달리 실제 메모리를 읽습니다. 첫 위치에서 x16, 그보다 8바이트 높은 위치에서 x17의 값을 읽습니다. 앞서 보관한 상태를 되살리거나 스택으로 전달된 인자를 얻는 과정입니다.

422행 그림 · 메모리 읽기

매크로 본문: kernel_exit

읽는 값sp
메모리[이전 sp + (16 * 8)] · 8바이트
이 명령을 실행하기 직전M0 (이 주소에 저장된 값)
이 명령을 실행한 직후M0 (이 주소에 저장된 값)
x16
이 명령을 실행하기 직전명령 직전 x16의 값
이 명령을 실행한 직후M0
메모리[이전 sp + (16 * 8) + 8] · 8바이트
이 명령을 실행하기 직전M1 (이 주소에 저장된 값)
이 명령을 실행한 직후M1 (이 주소에 저장된 값)
x17
이 명령을 실행하기 직전명령 직전 x17의 값
이 명령을 실행한 직후M1

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 주소 계산에 의한 기준 레지스터 갱신은 없습니다. 다만 LDR/LDP의 목적 레지스터가 기준 레지스터와 같으면 읽은 값이 그 레지스터를 덮어씁니다.
  • 메모리 칸은 낮은 주소부터 나열했습니다. SP를 줄이면 더 낮은 주소의 공간을 사용합니다. 화면의 아래쪽이 곧 낮은 주소라는 뜻은 아닙니다.
  • 매크로 kernel_exit의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	ldp	x18, x19, [sp, #16 * 9]

sp를 기준으로 16 * 9만큼 계산한 메모리 위치에 보관된 값을 x18, x19로 가져옵니다. 주소 숫자를 옮기는 MOV와 달리 실제 메모리를 읽습니다. 첫 위치에서 x18, 그보다 8바이트 높은 위치에서 x19의 값을 읽습니다. 앞서 보관한 상태를 되살리거나 스택으로 전달된 인자를 얻는 과정입니다.

423행 그림 · 메모리 읽기

매크로 본문: kernel_exit

읽는 값sp
메모리[이전 sp + (16 * 9)] · 8바이트
이 명령을 실행하기 직전M0 (이 주소에 저장된 값)
이 명령을 실행한 직후M0 (이 주소에 저장된 값)
x18
이 명령을 실행하기 직전명령 직전 x18의 값
이 명령을 실행한 직후M0
메모리[이전 sp + (16 * 9) + 8] · 8바이트
이 명령을 실행하기 직전M1 (이 주소에 저장된 값)
이 명령을 실행한 직후M1 (이 주소에 저장된 값)
x19
이 명령을 실행하기 직전명령 직전 x19의 값
이 명령을 실행한 직후M1

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 주소 계산에 의한 기준 레지스터 갱신은 없습니다. 다만 LDR/LDP의 목적 레지스터가 기준 레지스터와 같으면 읽은 값이 그 레지스터를 덮어씁니다.
  • 메모리 칸은 낮은 주소부터 나열했습니다. SP를 줄이면 더 낮은 주소의 공간을 사용합니다. 화면의 아래쪽이 곧 낮은 주소라는 뜻은 아닙니다.
  • 매크로 kernel_exit의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	ldp	x20, x21, [sp, #16 * 10]

sp를 기준으로 16 * 10만큼 계산한 메모리 위치에 보관된 값을 x20, x21로 가져옵니다. 주소 숫자를 옮기는 MOV와 달리 실제 메모리를 읽습니다. 첫 위치에서 x20, 그보다 8바이트 높은 위치에서 x21의 값을 읽습니다. 앞서 보관한 상태를 되살리거나 스택으로 전달된 인자를 얻는 과정입니다.

424행 그림 · 메모리 읽기

매크로 본문: kernel_exit

읽는 값sp
메모리[이전 sp + (16 * 10)] · 8바이트
이 명령을 실행하기 직전M0 (이 주소에 저장된 값)
이 명령을 실행한 직후M0 (이 주소에 저장된 값)
x20
이 명령을 실행하기 직전명령 직전 x20의 값
이 명령을 실행한 직후M0
메모리[이전 sp + (16 * 10) + 8] · 8바이트
이 명령을 실행하기 직전M1 (이 주소에 저장된 값)
이 명령을 실행한 직후M1 (이 주소에 저장된 값)
x21
이 명령을 실행하기 직전명령 직전 x21의 값
이 명령을 실행한 직후M1

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 주소 계산에 의한 기준 레지스터 갱신은 없습니다. 다만 LDR/LDP의 목적 레지스터가 기준 레지스터와 같으면 읽은 값이 그 레지스터를 덮어씁니다.
  • 메모리 칸은 낮은 주소부터 나열했습니다. SP를 줄이면 더 낮은 주소의 공간을 사용합니다. 화면의 아래쪽이 곧 낮은 주소라는 뜻은 아닙니다.
  • 매크로 kernel_exit의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	ldp	x22, x23, [sp, #16 * 11]

sp를 기준으로 16 * 11만큼 계산한 메모리 위치에 보관된 값을 x22, x23로 가져옵니다. 주소 숫자를 옮기는 MOV와 달리 실제 메모리를 읽습니다. 첫 위치에서 x22, 그보다 8바이트 높은 위치에서 x23의 값을 읽습니다. 앞서 보관한 상태를 되살리거나 스택으로 전달된 인자를 얻는 과정입니다.

425행 그림 · 메모리 읽기

매크로 본문: kernel_exit

읽는 값sp
메모리[이전 sp + (16 * 11)] · 8바이트
이 명령을 실행하기 직전M0 (이 주소에 저장된 값)
이 명령을 실행한 직후M0 (이 주소에 저장된 값)
x22
이 명령을 실행하기 직전명령 직전 x22의 값
이 명령을 실행한 직후M0
메모리[이전 sp + (16 * 11) + 8] · 8바이트
이 명령을 실행하기 직전M1 (이 주소에 저장된 값)
이 명령을 실행한 직후M1 (이 주소에 저장된 값)
x23
이 명령을 실행하기 직전명령 직전 x23의 값
이 명령을 실행한 직후M1

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 주소 계산에 의한 기준 레지스터 갱신은 없습니다. 다만 LDR/LDP의 목적 레지스터가 기준 레지스터와 같으면 읽은 값이 그 레지스터를 덮어씁니다.
  • 메모리 칸은 낮은 주소부터 나열했습니다. SP를 줄이면 더 낮은 주소의 공간을 사용합니다. 화면의 아래쪽이 곧 낮은 주소라는 뜻은 아닙니다.
  • 매크로 kernel_exit의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	ldp	x24, x25, [sp, #16 * 12]

sp를 기준으로 16 * 12만큼 계산한 메모리 위치에 보관된 값을 x24, x25로 가져옵니다. 주소 숫자를 옮기는 MOV와 달리 실제 메모리를 읽습니다. 첫 위치에서 x24, 그보다 8바이트 높은 위치에서 x25의 값을 읽습니다. 앞서 보관한 상태를 되살리거나 스택으로 전달된 인자를 얻는 과정입니다.

426행 그림 · 메모리 읽기

매크로 본문: kernel_exit

읽는 값sp
메모리[이전 sp + (16 * 12)] · 8바이트
이 명령을 실행하기 직전M0 (이 주소에 저장된 값)
이 명령을 실행한 직후M0 (이 주소에 저장된 값)
x24
이 명령을 실행하기 직전명령 직전 x24의 값
이 명령을 실행한 직후M0
메모리[이전 sp + (16 * 12) + 8] · 8바이트
이 명령을 실행하기 직전M1 (이 주소에 저장된 값)
이 명령을 실행한 직후M1 (이 주소에 저장된 값)
x25
이 명령을 실행하기 직전명령 직전 x25의 값
이 명령을 실행한 직후M1

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 주소 계산에 의한 기준 레지스터 갱신은 없습니다. 다만 LDR/LDP의 목적 레지스터가 기준 레지스터와 같으면 읽은 값이 그 레지스터를 덮어씁니다.
  • 메모리 칸은 낮은 주소부터 나열했습니다. SP를 줄이면 더 낮은 주소의 공간을 사용합니다. 화면의 아래쪽이 곧 낮은 주소라는 뜻은 아닙니다.
  • 매크로 kernel_exit의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	ldp	x26, x27, [sp, #16 * 13]

sp를 기준으로 16 * 13만큼 계산한 메모리 위치에 보관된 값을 x26, x27로 가져옵니다. 주소 숫자를 옮기는 MOV와 달리 실제 메모리를 읽습니다. 첫 위치에서 x26, 그보다 8바이트 높은 위치에서 x27의 값을 읽습니다. 앞서 보관한 상태를 되살리거나 스택으로 전달된 인자를 얻는 과정입니다.

427행 그림 · 메모리 읽기

매크로 본문: kernel_exit

읽는 값sp
메모리[이전 sp + (16 * 13)] · 8바이트
이 명령을 실행하기 직전M0 (이 주소에 저장된 값)
이 명령을 실행한 직후M0 (이 주소에 저장된 값)
x26
이 명령을 실행하기 직전명령 직전 x26의 값
이 명령을 실행한 직후M0
메모리[이전 sp + (16 * 13) + 8] · 8바이트
이 명령을 실행하기 직전M1 (이 주소에 저장된 값)
이 명령을 실행한 직후M1 (이 주소에 저장된 값)
x27
이 명령을 실행하기 직전명령 직전 x27의 값
이 명령을 실행한 직후M1

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 주소 계산에 의한 기준 레지스터 갱신은 없습니다. 다만 LDR/LDP의 목적 레지스터가 기준 레지스터와 같으면 읽은 값이 그 레지스터를 덮어씁니다.
  • 메모리 칸은 낮은 주소부터 나열했습니다. SP를 줄이면 더 낮은 주소의 공간을 사용합니다. 화면의 아래쪽이 곧 낮은 주소라는 뜻은 아닙니다.
  • 매크로 kernel_exit의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	ldp	x28, x29, [sp, #16 * 14]

sp를 기준으로 16 * 14만큼 계산한 메모리 위치에 보관된 값을 x28, x29로 가져옵니다. 주소 숫자를 옮기는 MOV와 달리 실제 메모리를 읽습니다. 첫 위치에서 x28, 그보다 8바이트 높은 위치에서 x29의 값을 읽습니다. 앞서 보관한 상태를 되살리거나 스택으로 전달된 인자를 얻는 과정입니다.

428행 그림 · 메모리 읽기

매크로 본문: kernel_exit

읽는 값sp
메모리[이전 sp + (16 * 14)] · 8바이트
이 명령을 실행하기 직전M0 (이 주소에 저장된 값)
이 명령을 실행한 직후M0 (이 주소에 저장된 값)
x28
이 명령을 실행하기 직전명령 직전 x28의 값
이 명령을 실행한 직후M0
메모리[이전 sp + (16 * 14) + 8] · 8바이트
이 명령을 실행하기 직전M1 (이 주소에 저장된 값)
이 명령을 실행한 직후M1 (이 주소에 저장된 값)
x29
이 명령을 실행하기 직전명령 직전 x29의 값
이 명령을 실행한 직후M1

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 주소 계산에 의한 기준 레지스터 갱신은 없습니다. 다만 LDR/LDP의 목적 레지스터가 기준 레지스터와 같으면 읽은 값이 그 레지스터를 덮어씁니다.
  • 메모리 칸은 낮은 주소부터 나열했습니다. SP를 줄이면 더 낮은 주소의 공간을 사용합니다. 화면의 아래쪽이 곧 낮은 주소라는 뜻은 아닙니다.
  • 매크로 kernel_exit의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	.if	\el == 0

어셈블 단계에서 인자나 식을 검사해 필요한 명령만 만듭니다. \el == 0

430행 그림 · 어셈블·링크 단계

매크로 본문: kernel_exit

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과.if \el == 0
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

#ifdef CONFIG_UNMAP_KERNEL_AT_EL0

KPTI는 사용자 실행 중 커널의 넓은 매핑을 감추어 일부 추측 실행 정보 노출을 줄이는 기능입니다. 지원 빌드에서만 최소 trampoline으로 이동해 매핑을 제거하는 복귀 경로를 포함합니다. 이 조건은 빌드 전처리 단계에서 포함할 코드를 고릅니다. CPU가 실행 중 전처리 조건을 검사하지는 않습니다.

431행 그림 · 전처리 단계

매크로 본문: kernel_exit

이 줄을 포함하는 조건: .if \el == 0

소스
처리 전전처리 전
이 줄의 효과#ifdef CONFIG_UNMAP_KERNEL_AT_EL0
CPU 레지스터
처리 전해당 없음
이 줄의 효과실행되는 CPU 명령이 없습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	alternative_insn "b .L_skip_tramp_exit_\@", nop, ARM64_UNMAP_KERNEL_AT_EL0

실제 KPTI가 활성인 시스템에서는 skip 분기를 NOP로 바꿔 trampoline 복귀를 실행합니다. 비활성이면 일반 ERET 경로로 건너뜁니다. CONFIG 지원과 런타임 적용 여부는 다릅니다. 이 매크로는 기본·교체 명령의 패치 정보를 만들며, CPU가 이 줄을 매번 if 조건처럼 실행하지 않습니다.

432행 그림 · CPU 기능에 따른 명령 패치

매크로 본문: kernel_exit

이 줄을 포함하는 조건: #ifdef CONFIG_UNMAP_KERNEL_AT_EL0 / .if \el == 0

패치 전 코드
처리 전기본 명령열
이 줄의 효과기능·정책에 맞는 기본 코드 또는 교체 코드

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 빌드할 때 패치 정보를 만들고 커널의 대체 코드 처리에서 명령을 선택합니다. 이 소스 줄을 CPU가 매번 if문으로 실행하는 것은 아닙니다.
	msr	far_el1, x29

사용자 x29를 FAR_EL1에 임시 보관합니다. 여기서는 fault 주소를 보고하는 것이 아니라 커널 매핑을 제거할 때 x29를 작업용으로 쓰기 위한 scratch 사용입니다. tramp_exit에서 되찾습니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.

trampoline 분기 주소를 담기 위해 x29를 빌리므로 원래 사용자 x29를 FAR_EL1에 잠시 보존합니다.

434행 그림 · 시스템 레지스터 쓰기

매크로 본문: kernel_exit

이 줄을 포함하는 조건: #ifdef CONFIG_UNMAP_KERNEL_AT_EL0 / .if \el == 0

읽는 값x29
FAR_EL1
이 명령을 실행하기 직전명령 직전 FAR_EL1의 값
이 명령을 실행한 직후명령 직전 x29의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 접근 권한과 VHE 별칭 규칙에 따라 실제 대상이 정해집니다. 뒤의 ISB·DSB가 필요한지는 이 레지스터와 문맥에 따릅니다.
  • 매크로 kernel_exit의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	ldr_this_cpu	x30, this_cpu_vector, x29

이 CPU의 KPTI/BHB 정책에 맞는 진입 벡터 주소를 읽습니다. 다음 사용자 예외가 올바른 trampoline·완화 경로로 들어오게 할 값입니다. 심벌의 기준 주소에 현재 CPU의 per-CPU 오프셋을 더해 이 CPU의 저장 위치를 찾습니다.

436행 그림 · 주소·값 구분

매크로 본문: kernel_exit

이 줄을 포함하는 조건: #ifdef CONFIG_UNMAP_KERNEL_AT_EL0 / .if \el == 0

x30
이 명령을 실행하기 직전명령 직전 x30의 값
이 명령을 실행한 직후메모리[주소(this_cpu_vector) + 현재 CPU 오프셋]의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 이 매크로의 임시 레지스터는 내부에서 사용됩니다.
  • 매크로 kernel_exit의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
ldr_this_cpu 매크로 본문 · 250행

arch/arm64/include/asm/assembler.h · Linux v6.18.37

정의 선택 조건: #ifndef __ASM_ASSEMBLER_H

.macro ldr_this_cpu dst, sym, tmp adr_l \dst, \sym get_this_cpu_offset \tmp ldr \dst, [\dst, \tmp] .endm
	tramp_alias	x29, tramp_exit

최소 trampoline 매핑에서도 접근할 수 있는 코드 주소를 계산합니다. 같은 코드라도 어느 매핑에서 부르는지에 따라 사용할 주소가 달라집니다. 여기의 인자는 x29, tramp_exit입니다.

437행 그림 · 주소·값 구분

매크로 본문: kernel_exit

이 줄을 포함하는 조건: #ifdef CONFIG_UNMAP_KERNEL_AT_EL0 / .if \el == 0

x29
이 명령을 실행하기 직전명령 직전 x29의 값
이 명령을 실행한 직후TRAMP_VALIAS + (주소(tramp_exit) − .entry.tramp.text 섹션 시작)

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 이 매크로의 임시 레지스터는 내부에서 사용됩니다.
  • 매크로 kernel_exit의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
tramp_alias 매크로 본문 · 103행

arch/arm64/kernel/entry.S · Linux v6.18.37

.macro tramp_alias, dst, sym .set .Lalias\@, TRAMP_VALIAS + \sym - .entry.tramp.text movz \dst, :abs_g2_s:.Lalias\@ movk \dst, :abs_g1_nc:.Lalias\@ movk \dst, :abs_g0_nc:.Lalias\@ .endm
	msr		vbar_el1, x30		// install vector table

다음 EL0 예외를 받을 trampoline 벡터 주소를 VBAR_EL1에 설치합니다. 커널 전체 매핑을 없앤 뒤에도 이 입구는 접근 가능해야 합니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.

438행 그림 · 시스템 레지스터 쓰기

매크로 본문: kernel_exit

이 줄을 포함하는 조건: #ifdef CONFIG_UNMAP_KERNEL_AT_EL0 / .if \el == 0

읽는 값x30
VBAR_EL1
이 명령을 실행하기 직전명령 직전 VBAR_EL1의 값
이 명령을 실행한 직후명령 직전 x30의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 접근 권한과 VHE 별칭 규칙에 따라 실제 대상이 정해집니다. 뒤의 ISB·DSB가 필요한지는 이 레지스터와 문맥에 따릅니다.
  • 매크로 kernel_exit의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	ldr		lr, [sp, #S_LR]		// restore x30

sp를 기준으로 S_LR만큼 계산한 메모리 위치에 보관된 값을 lr로 가져옵니다. 주소 숫자를 옮기는 MOV와 달리 실제 메모리를 읽습니다. 앞서 보관한 상태를 되살리거나 스택으로 전달된 인자를 얻는 과정입니다.

439행 그림 · 메모리 읽기

매크로 본문: kernel_exit

이 줄을 포함하는 조건: #ifdef CONFIG_UNMAP_KERNEL_AT_EL0 / .if \el == 0

읽는 값sp
메모리[이전 sp + (S_LR)] · 8바이트
이 명령을 실행하기 직전M0 (이 주소에 저장된 값)
이 명령을 실행한 직후M0 (이 주소에 저장된 값)
lr
이 명령을 실행하기 직전명령 직전 lr의 값
이 명령을 실행한 직후M0

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 주소 계산에 의한 기준 레지스터 갱신은 없습니다. 다만 LDR/LDP의 목적 레지스터가 기준 레지스터와 같으면 읽은 값이 그 레지스터를 덮어씁니다.
  • 메모리 칸은 낮은 주소부터 나열했습니다. SP를 줄이면 더 낮은 주소의 공간을 사용합니다. 화면의 아래쪽이 곧 낮은 주소라는 뜻은 아닙니다.
  • 매크로 kernel_exit의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	add		sp, sp, #PT_REGS_SIZE	// restore sp

현재 사용한 스택 공간 PT_REGS_SIZE바이트를 해제합니다. SP를 높은 주소로 되돌리며 메모리 내용을 0으로 지우는 것은 아닙니다.

440행 그림 · 레지스터 상태

매크로 본문: kernel_exit

이 줄을 포함하는 조건: #ifdef CONFIG_UNMAP_KERNEL_AT_EL0 / .if \el == 0

읽는 값sp
sp
이 명령을 실행하기 직전명령 직전 sp의 값
이 명령을 실행한 직후명령 직전 sp의 값 + PT_REGS_SIZE

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 결과는 목적 레지스터 폭에 맞습니다. 이 형식은 NZCV를 바꾸지 않습니다.
  • SP는 주소만 바뀝니다. 스택 데이터 저장·복원은 STR/STP·LDR/LDP 같은 별도 메모리 명령이 수행합니다.
  • 매크로 kernel_exit의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	br		x29

x29에 준비한 주소부터 실행합니다. 주소가 가리키는 값을 메모리에서 읽는 것이 아니라 CPU의 다음 실행 위치를 바꿉니다. 새 복귀 주소는 만들지 않습니다.

현재 x29는 복구된 사용자 FP가 아니라 trampoline 별칭 주소입니다.

441행 그림 · 호출과 실행 위치

매크로 본문: kernel_exit

이 줄을 포함하는 조건: #ifdef CONFIG_UNMAP_KERNEL_AT_EL0 / .if \el == 0

읽는 값x29
PC
이 명령을 실행하기 직전명령 직전 PC의 값
이 명령을 실행한 직후명령 직전 x29의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 새 복귀 주소를 LR에 기록하지 않습니다. RET 역시 메모리의 스택 값을 자동으로 꺼내는 명령은 아닙니다.
  • 매크로 kernel_exit의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
.L_skip_tramp_exit_\@:

여기에 .L_skip_tramp_exit_\@라는 위치 이름을 붙입니다. 다른 줄의 분기나 주소 계산이 이 위치를 찾아올 수 있습니다. 이름 자체가 CPU 작업을 추가하지는 않습니다.

443행 그림 · 위치에 이름 붙이기

매크로 본문: kernel_exit

이 줄을 포함하는 조건: #ifdef CONFIG_UNMAP_KERNEL_AT_EL0 / .if \el == 0

레이블
처리 전현재 배치 위치
이 줄의 효과.L_skip_tramp_exit_\@
CPU 레지스터
처리 전그대로
이 줄의 효과레이블 자체가 값을 바꾸지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

#endif

빌드 조건으로 나누었던 구간을 닫습니다. 대상:

444행 그림 · 전처리 단계

매크로 본문: kernel_exit

이 줄을 포함하는 조건: #ifdef CONFIG_UNMAP_KERNEL_AT_EL0 / .if \el == 0

소스
처리 전전처리 전
이 줄의 효과#endif
CPU 레지스터
처리 전해당 없음
이 줄의 효과실행되는 CPU 명령이 없습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	.endif

어셈블 조건에 따라 생성할 코드를 나누던 범위가 끝납니다.

445행 그림 · 어셈블·링크 단계

매크로 본문: kernel_exit

이 줄을 포함하는 조건: .if \el == 0

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과.endif
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	ldr	lr, [sp, #S_LR]

sp를 기준으로 S_LR만큼 계산한 메모리 위치에 보관된 값을 lr로 가져옵니다. 주소 숫자를 옮기는 MOV와 달리 실제 메모리를 읽습니다. 앞서 보관한 상태를 되살리거나 스택으로 전달된 인자를 얻는 과정입니다.

447행 그림 · 메모리 읽기

매크로 본문: kernel_exit

읽는 값sp
메모리[이전 sp + (S_LR)] · 8바이트
이 명령을 실행하기 직전M0 (이 주소에 저장된 값)
이 명령을 실행한 직후M0 (이 주소에 저장된 값)
lr
이 명령을 실행하기 직전명령 직전 lr의 값
이 명령을 실행한 직후M0

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 주소 계산에 의한 기준 레지스터 갱신은 없습니다. 다만 LDR/LDP의 목적 레지스터가 기준 레지스터와 같으면 읽은 값이 그 레지스터를 덮어씁니다.
  • 메모리 칸은 낮은 주소부터 나열했습니다. SP를 줄이면 더 낮은 주소의 공간을 사용합니다. 화면의 아래쪽이 곧 낮은 주소라는 뜻은 아닙니다.
  • 매크로 kernel_exit의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	add	sp, sp, #PT_REGS_SIZE		// restore sp

현재 사용한 스택 공간 PT_REGS_SIZE바이트를 해제합니다. SP를 높은 주소로 되돌리며 메모리 내용을 0으로 지우는 것은 아닙니다.

pt_regs 프레임 공간을 해제합니다. 저장된 LR은 바로 앞에서 읽었습니다.

448행 그림 · 레지스터 상태

매크로 본문: kernel_exit

읽는 값sp
sp
이 명령을 실행하기 직전명령 직전 sp의 값
이 명령을 실행한 직후명령 직전 sp의 값 + PT_REGS_SIZE

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 결과는 목적 레지스터 폭에 맞습니다. 이 형식은 NZCV를 바꾸지 않습니다.
  • SP는 주소만 바뀝니다. 스택 데이터 저장·복원은 STR/STP·LDR/LDP 같은 별도 메모리 명령이 수행합니다.
  • 매크로 kernel_exit의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	.if \el == 0

어셈블 단계에서 인자나 식을 검사해 필요한 명령만 만듭니다. \el == 0

450행 그림 · 어셈블·링크 단계

매크로 본문: kernel_exit

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과.if \el == 0
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

alternative_if ARM64_WORKAROUND_SPECULATIVE_UNPRIV_LOAD

speculative unprivileged load 관련 CPU 보완이 필요한 경우에만 다음 TLBI와 DSB를 남깁니다. 사용자 복귀 경계에서 잘못된 추측 접근 상태를 정리하는 CPU별 우회이며 일반적인 페이지 테이블 변경 flush로 설명하면 안 됩니다. 이 매크로는 기본·교체 명령의 패치 정보를 만들며, CPU가 이 줄을 매번 if 조건처럼 실행하지 않습니다.

452행 그림 · CPU 기능에 따른 명령 패치

매크로 본문: kernel_exit

이 줄을 포함하는 조건: .if \el == 0

패치 전 코드
처리 전기본 명령열
이 줄의 효과기능·정책에 맞는 기본 코드 또는 교체 코드

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 빌드할 때 패치 정보를 만들고 커널의 대체 코드 처리에서 명령을 선택합니다. 이 소스 줄을 CPU가 매번 if문으로 실행하는 것은 아닙니다.
	tlbi	vale1, xzr

CPU가 기억해 둔 주소 변환 결과를 무효화합니다. 메모리의 페이지 테이블을 고쳤다고 기존 TLB 기록까지 자동으로 사라지는 것은 아닙니다.

453행 그림 · 동기화·캐시·대기 상태

매크로 본문: kernel_exit

이 줄을 포함하는 조건: .if \el == 0

읽는 값xzr
TLB
이 명령을 실행하기 직전실행 전 상태
이 명령을 실행한 직후지정한 주소 변환 기록 무효화 요청

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 관리 요청과 완료 보장은 구분됩니다. 주변 DSB·ISB와 함께 해석합니다.
  • 매크로 kernel_exit의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	dsb	nsh

지정 범위·종류의 앞선 작업이 완료되기를 기다립니다. 페이지 복사나 캐시 관리가 끝나기 전에 다음 상태로 넘어가지 않도록 하는 대기 지점입니다.

454행 그림 · 동기화·캐시·대기 상태

매크로 본문: kernel_exit

이 줄을 포함하는 조건: .if \el == 0

작업 완료
이 명령을 실행하기 직전실행 전 상태
이 명령을 실행한 직후지정 범위의 앞선 작업 완료까지 기다림

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 매크로 kernel_exit의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
alternative_else_nop_endif

452행에서 시작한 ARM64_WORKAROUND_SPECULATIVE_UNPRIV_LOAD 패치 묶음을 닫습니다. 조건에 맞는 본문을 사용하지 않는 쪽은 같은 길이의 NOP로 채워 뒤 명령의 배치를 유지합니다. 이 줄 자체가 CPU에서 조건을 검사하는 명령은 아닙니다.

455행 그림 · CPU 기능에 따른 명령 패치

매크로 본문: kernel_exit

이 줄을 포함하는 조건: .if \el == 0

패치 전 코드
처리 전기본 명령열
이 줄의 효과기능·정책에 맞는 기본 코드 또는 교체 코드

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 빌드할 때 패치 정보를 만들고 커널의 대체 코드 처리에서 명령을 선택합니다. 이 소스 줄을 CPU가 매번 if문으로 실행하는 것은 아닙니다.
	.else

앞 어셈블 조건의 반대 경우에 생성할 코드입니다.

456행 그림 · 어셈블·링크 단계

매크로 본문: kernel_exit

이 줄을 포함하는 조건: .if \el == 0

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과.else
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	alternative_insn nop, "dmb sy", ARM64_WORKAROUND_1508412

erratum 1508412가 적용되는 CPU의 커널 복귀 경로에는 NOP 대신 DMB SY를 넣습니다. 정상 프로그램의 함수 인수가 아니라 영향을 받는 CPU에서 필요한 메모리 순서를 보완합니다. 이 매크로는 기본·교체 명령의 패치 정보를 만들며, CPU가 이 줄을 매번 if 조건처럼 실행하지 않습니다.

458행 그림 · CPU 기능에 따른 명령 패치

매크로 본문: kernel_exit

이 줄을 포함하는 조건: 앞 어셈블 조건의 반대

패치 전 코드
처리 전기본 명령열
이 줄의 효과기능·정책에 맞는 기본 코드 또는 교체 코드

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 빌드할 때 패치 정보를 만들고 커널의 대체 코드 처리에서 명령을 선택합니다. 이 소스 줄을 CPU가 매번 if문으로 실행하는 것은 아닙니다.
	.endif

어셈블 조건에 따라 생성할 코드를 나누던 범위가 끝납니다.

459행 그림 · 어셈블·링크 단계

매크로 본문: kernel_exit

이 줄을 포함하는 조건: 앞 어셈블 조건의 반대

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과.endif
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	eret

예외가 끝났으므로 저장해 둔 PC와 실행 상태를 함께 되살립니다. 일반 RET와 달리 어느 예외 레벨과 스택 모드로 돌아갈지도 SPSR의 내용에 따라 정해집니다.

461행 그림 · 저장된 실행 상태로 복귀

매크로 본문: kernel_exit

읽는 값ELR_ELxSPSR_ELx
PC
이 명령을 실행하기 직전명령 직전 PC의 값
이 명령을 실행한 직후명령 직전 ELR_ELx의 값
PSTATE
이 명령을 실행하기 직전명령 직전 PSTATE의 값
이 명령을 실행한 직후명령 직전 SPSR_ELx의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • ELx는 현재 예외 레벨입니다. SPSR의 모드에 따라 복귀 레벨과 스택 선택도 바뀝니다. 일반 레지스터 복구는 앞의 load 명령들이 담당합니다.
  • 매크로 kernel_exit의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	sb

ERET 뒤의 명령이 잘못된 직선 경로로 추측 실행되는 것을 막는 배리어입니다. SB 지원 CPU는 SB를, 미지원 CPU는 DSB NSH와 ISB를 사용하며 정상 복귀 경로는 이미 ERET로 떠납니다.

462행 그림 · 동기화·캐시·대기 상태

매크로 본문: kernel_exit

추측 실행
이 명령을 실행하기 직전실행 전 상태
이 명령을 실행한 직후지정한 경계를 넘는 추측 실행을 제한

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 매크로 kernel_exit의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
sb 매크로 본문 · 104행

arch/arm64/include/asm/assembler.h · Linux v6.18.37

정의 선택 조건: #ifndef __ASM_ASSEMBLER_H

.macro sb alternative_if_not ARM64_HAS_SB dsb nsh isb alternative_else SB_BARRIER_INSN nop alternative_endif .endm
	.endm

공통 작업의 정의를 여기서 마칩니다. RET가 아니라 빌드 도구가 읽는 경계입니다.

463행 그림 · 어셈블·링크 단계

매크로 본문: kernel_exit

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과.endm
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 끝 표기 자체가 RET나 분기를 실행하지는 않습니다.

소프트웨어 PAN과 tsk 별명

원본 464–509행

#ifdef CONFIG_ARM64_SW_TTBR0_PAN

하드웨어 PAN이 없는 CPU에서도 사용자 주소에 대한 커널의 직접 접근을 막을 수 있도록 TTBR0 기반의 소프트웨어 PAN 함수들을 빌드합니다. 사용자 복사 helper가 필요할 때만 유효 TTBR0를 임시로 복원합니다. 이 조건은 빌드 전처리 단계에서 포함할 코드를 고릅니다. CPU가 실행 중 전처리 조건을 검사하지는 않습니다.

465행 그림 · 전처리 단계
소스
처리 전전처리 전
이 줄의 효과#ifdef CONFIG_ARM64_SW_TTBR0_PAN
CPU 레지스터
처리 전해당 없음
이 줄의 효과실행되는 CPU 명령이 없습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

SYM_CODE_START_LOCAL(__swpan_entry_el1)

이 위치부터 (__swpan_entry_el1)라는 코드가 시작한다고 도구에 알려 줍니다. 정렬과 외부 공개 여부도 정의에 포함됩니다. 실제 작업은 아래의 명령들이 수행합니다.

474행 그림 · 어셈블·링크 단계

함수·코드 범위: __swpan_entry_el1

이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_SW_TTBR0_PAN

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과SYM_CODE_START_LOCAL(__swpan_entry_el1)
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	mrs	x21, ttbr0_el1

현재 TTBR0의 ASID 필드를 읽어 사용자 접근용 표가 이미 예약 상태로 비활성화되어 있었는지 알아냅니다. 단순 페이지 표 주소만 검사하는 것이 아닙니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.

475행 그림 · 시스템 레지스터 읽기

함수·코드 범위: __swpan_entry_el1

이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_SW_TTBR0_PAN

읽는 값TTBR0_EL1
x21
이 명령을 실행하기 직전명령 직전 x21의 값
이 명령을 실행한 직후명령 직전 TTBR0_EL1의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 일반 RAM의 메모리 접근과 구분합니다.
	tst	x21, #TTBR_ASID_MASK		// Check for the reserved ASID

예약 ASID 여부를 검사합니다. 예약값이면 예외 전에도 소프트웨어 PAN이 사용자 접근을 막고 있었으므로 복귀 때도 그 상태를 유지해야 합니다. TST는 두 피연산자의 비트 AND 결과로 조건 플래그를 갱신하며 원래 레지스터 값은 바꾸지 않습니다.

476행 그림 · 조건 플래그 계산

함수·코드 범위: __swpan_entry_el1

이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_SW_TTBR0_PAN

읽는 값x21
NZCV
이 명령을 실행하기 직전명령 직전 NZCV의 값
이 명령을 실행한 직후명령 직전 x21의 값 AND TTBR_ASID_MASK의 조건 결과

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 일반 레지스터에 연산 결과를 쓰지 않고 조건 플래그만 바꿉니다.
  • N은 결과의 부호 비트, Z는 결과가 0인지 나타냅니다. 이 AArch64 TST는 C와 V를 0으로 만듭니다.
	orr	x23, x23, #PSR_PAN_BIT		// Set the emulated PAN in the saved SPSR

저장 PSTATE에 모의 PAN 비트를 우선 세웁니다. 하드웨어 PAN이 없는 CPU에서 이 비트는 소프트웨어가 예외 전 사용자 접근 상태를 기록하는 용도입니다. ORR은 어느 한쪽이 1인 비트를 결과에 세우므로 원래 1이던 비트도 유지됩니다.

477행 그림 · 레지스터 상태

함수·코드 범위: __swpan_entry_el1

이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_SW_TTBR0_PAN

읽는 값x23
x23
이 명령을 실행하기 직전명령 직전 x23의 값
이 명령을 실행한 직후명령 직전 x23의 값 OR PSR_PAN_BIT

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 결과는 목적 레지스터 폭에 맞습니다. 이 형식은 NZCV를 바꾸지 않습니다.
	b.eq	1f				// TTBR0 access already disabled

앞서 계산한 조건 플래그가 Z=1이면 1f (L482)로 갑니다. 사이에 플래그를 바꾸지 않는 명령이 있어도 비교 결과는 남아 있습니다.

478행 그림 · 조건별 실행 위치

함수·코드 범위: __swpan_entry_el1

이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_SW_TTBR0_PAN

읽는 값NZCV
PC
이 명령을 실행하기 직전명령 직전 PC의 값
이 명령을 실행한 직후Z=1이면 1f (L482), 아니면 다음 명령

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 조건이 맞는 한 경로만 실행합니다. 이 분기 명령은 NZCV를 새로 계산하지 않습니다.
	and	x23, x23, #~PSR_PAN_BIT		// Clear the emulated PAN in the saved SPSR

TTBR0가 유효한 사용자 접근 상태였다면 모의 PAN 표식을 지웁니다. EL1으로 복귀할 때 원래 허용 상태를 되살릴 근거가 됩니다. AND는 두 피연산자의 같은 위치 비트가 모두 1인 곳만 결과에 남깁니다.

479행 그림 · 레지스터 상태

함수·코드 범위: __swpan_entry_el1

이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_SW_TTBR0_PAN

읽는 값x23
x23
이 명령을 실행하기 직전명령 직전 x23의 값
이 명령을 실행한 직후명령 직전 x23의 값 AND ~PSR_PAN_BIT

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 결과는 목적 레지스터 폭에 맞습니다. 이 형식은 NZCV를 바꾸지 않습니다.
SYM_INNER_LABEL(__swpan_entry_el0, SYM_L_LOCAL)

기존 함수 안의 중간 위치에 이름을 붙입니다. 다른 분기에서 찾아올 수 있지만 새 함수를 호출하거나 스택 프레임을 만드는 동작은 아닙니다.

480행 그림 · 어셈블·링크 단계

함수·코드 범위: __swpan_entry_el1

이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_SW_TTBR0_PAN

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과SYM_INNER_LABEL(__swpan_entry_el0, SYM_L_LOCAL)
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	__uaccess_ttbr0_disable x21

유효한 사용자 TTBR0를 예약된 표와 ASID로 바꾸어 일반 커널 코드의 사용자 메모리 접근을 막습니다. tmp x21을 쓰며 명시적인 uaccess에서만 다시 허용합니다.

481행 그림 · 매크로 안의 상태 변화

함수·코드 범위: __swpan_entry_el1

이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_SW_TTBR0_PAN

입력
이 명령을 실행하기 직전호출 직전 값
이 명령을 실행한 직후x21
주요 결과
이 명령을 실행하기 직전본문 실행 전
이 명령을 실행한 직후TTBR0를 예약된 매핑으로 바꾸고 관련 ASID 상태를 조정하여 사용자 주소 접근을 차단합니다.

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 여러 명령과 내부 분기를 묶은 그림입니다. 작업 레지스터를 모두 보존한다는 뜻은 아니며, 정확한 변경 순서는 아래 매크로 본문과 줄별 그림에서 확인합니다.
__uaccess_ttbr0_disable 매크로 본문 · 16행

arch/arm64/include/asm/asm-uaccess.h · Linux v6.18.37

정의 선택 조건: #ifndef __ASM_ASM_UACCESS_H / #ifdef CONFIG_ARM64_SW_TTBR0_PAN

.macro __uaccess_ttbr0_disable, tmp1 mrs \tmp1, ttbr1_el1 // swapper_pg_dir bic \tmp1, \tmp1, #TTBR_ASID_MASK sub \tmp1, \tmp1, #RESERVED_SWAPPER_OFFSET // reserved_pg_dir msr ttbr0_el1, \tmp1 // set reserved TTBR0_EL1 add \tmp1, \tmp1, #RESERVED_SWAPPER_OFFSET msr ttbr1_el1, \tmp1 // set reserved ASID isb .endm
1:	ret

LR(x30)에 적힌 주소로 실행을 돌립니다. 그 주소가 어떻게 준비됐는지가 중요합니다. 정상 함수의 호출자일 수도 있고, 절전 복귀나 trampoline에서 별도로 기록한 재개 지점일 수도 있습니다.

482행 그림 · 호출과 실행 위치

함수·코드 범위: __swpan_entry_el1

이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_SW_TTBR0_PAN

읽는 값lr
PC
이 명령을 실행하기 직전명령 직전 PC의 값
이 명령을 실행한 직후명령 직전 lr의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 새 복귀 주소를 LR에 기록하지 않습니다. RET 역시 메모리의 스택 값을 자동으로 꺼내는 명령은 아닙니다.
SYM_CODE_END(__swpan_entry_el1)

도구에 이 코드 범위의 끝을 알려 크기와 심볼 정보를 기록합니다. CPU가 이 줄 때문에 자동으로 반환하지는 않습니다.

483행 그림 · 어셈블·링크 단계

함수·코드 범위: __swpan_entry_el1

이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_SW_TTBR0_PAN

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과SYM_CODE_END(__swpan_entry_el1)
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 끝 표기 자체가 RET나 분기를 실행하지는 않습니다.
SYM_CODE_START_LOCAL(__swpan_exit_el1)

이 위치부터 (__swpan_exit_el1)라는 코드가 시작한다고 도구에 알려 줍니다. 정렬과 외부 공개 여부도 정의에 포함됩니다. 실제 작업은 아래의 명령들이 수행합니다.

489행 그림 · 어셈블·링크 단계

함수·코드 범위: __swpan_exit_el1

이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_SW_TTBR0_PAN

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과SYM_CODE_START_LOCAL(__swpan_exit_el1)
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	tbnz	x22, #22, 1f			// Skip re-enabling TTBR0 access if the PSR_PAN_BIT is set

저장한 모의 PAN bit 22가 켜져 있으면 예외 전에도 사용자 접근이 막혀 있었으므로 TTBR0를 다시 열지 않습니다. 이 분기는 지정 레지스터의 값이나 비트를 직접 검사하며 CMP가 남긴 NZCV를 읽거나 바꾸지 않습니다. 조건이 맞지 않으면 바로 다음 명령으로 진행합니다.

490행 그림 · 조건별 실행 위치

함수·코드 범위: __swpan_exit_el1

이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_SW_TTBR0_PAN

읽는 값x22
PC
이 명령을 실행하기 직전명령 직전 PC의 값
이 명령을 실행한 직후명령 직전 x22의 값의 bit 22 = 1이면 1f (L492), 아니면 다음 명령

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 조건이 맞는 한 경로만 실행합니다. 이 분기 명령은 NZCV를 새로 계산하지 않습니다.
	__uaccess_ttbr0_enable x0, x1

예외 전 커널이 uaccess 중이었다면 유효한 TTBR0를 복원해 그 접근을 계속할 수 있게 합니다. x0·x1은 그 전환에 쓰는 임시 레지스터입니다. 매크로는 현재 task에 저장된 TTBR0와 그 주소 공간의 ASID를 함께 복원합니다.

491행 그림 · 매크로 안의 상태 변화

함수·코드 범위: __swpan_exit_el1

이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_SW_TTBR0_PAN

입력
이 명령을 실행하기 직전호출 직전 값
이 명령을 실행한 직후x0, x1
주요 결과
이 명령을 실행하기 직전본문 실행 전
이 명령을 실행한 직후태스크가 저장한 TTBR0와 해당 ASID를 복원해 사용자 주소 접근을 허용합니다.

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 여러 명령과 내부 분기를 묶은 그림입니다. 작업 레지스터를 모두 보존한다는 뜻은 아니며, 정확한 변경 순서는 아래 매크로 본문과 줄별 그림에서 확인합니다.
__uaccess_ttbr0_enable 매크로 본문 · 26행

arch/arm64/include/asm/asm-uaccess.h · Linux v6.18.37

정의 선택 조건: #ifndef __ASM_ASM_UACCESS_H / #ifdef CONFIG_ARM64_SW_TTBR0_PAN

.macro __uaccess_ttbr0_enable, tmp1, tmp2 get_current_task \tmp1 ldr \tmp1, [\tmp1, #TSK_TI_TTBR0] // load saved TTBR0_EL1 mrs \tmp2, ttbr1_el1 extr \tmp2, \tmp2, \tmp1, #48 ror \tmp2, \tmp2, #16 msr ttbr1_el1, \tmp2 // set the active ASID msr ttbr0_el1, \tmp1 // set the non-PAN TTBR0_EL1 isb .endm
1:	and	x22, x22, #~PSR_PAN_BIT		// ARMv8.0 CPUs do not understand this bit

하드웨어 PAN을 모르는 ARMv8.0 CPU에 실제 PSTATE 예약 비트를 넘기지 않도록 모의 PAN 표식을 지웁니다. 소프트웨어 기록은 이미 TTBR0 복원 판단에 사용했습니다. AND는 두 피연산자의 같은 위치 비트가 모두 1인 곳만 결과에 남깁니다.

492행 그림 · 레지스터 상태

함수·코드 범위: __swpan_exit_el1

이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_SW_TTBR0_PAN

읽는 값x22
x22
이 명령을 실행하기 직전명령 직전 x22의 값
이 명령을 실행한 직후명령 직전 x22의 값 AND ~PSR_PAN_BIT

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 결과는 목적 레지스터 폭에 맞습니다. 이 형식은 NZCV를 바꾸지 않습니다.
	ret

LR(x30)에 적힌 주소로 실행을 돌립니다. 그 주소가 어떻게 준비됐는지가 중요합니다. 정상 함수의 호출자일 수도 있고, 절전 복귀나 trampoline에서 별도로 기록한 재개 지점일 수도 있습니다.

493행 그림 · 호출과 실행 위치

함수·코드 범위: __swpan_exit_el1

이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_SW_TTBR0_PAN

읽는 값lr
PC
이 명령을 실행하기 직전명령 직전 PC의 값
이 명령을 실행한 직후명령 직전 lr의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 새 복귀 주소를 LR에 기록하지 않습니다. RET 역시 메모리의 스택 값을 자동으로 꺼내는 명령은 아닙니다.
SYM_CODE_END(__swpan_exit_el1)

도구에 이 코드 범위의 끝을 알려 크기와 심볼 정보를 기록합니다. CPU가 이 줄 때문에 자동으로 반환하지는 않습니다.

494행 그림 · 어셈블·링크 단계

함수·코드 범위: __swpan_exit_el1

이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_SW_TTBR0_PAN

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과SYM_CODE_END(__swpan_exit_el1)
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 끝 표기 자체가 RET나 분기를 실행하지는 않습니다.
SYM_CODE_START_LOCAL(__swpan_exit_el0)

이 위치부터 (__swpan_exit_el0)라는 코드가 시작한다고 도구에 알려 줍니다. 정렬과 외부 공개 여부도 정의에 포함됩니다. 실제 작업은 아래의 명령들이 수행합니다.

496행 그림 · 어셈블·링크 단계

함수·코드 범위: __swpan_exit_el0

이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_SW_TTBR0_PAN

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과SYM_CODE_START_LOCAL(__swpan_exit_el0)
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	__uaccess_ttbr0_enable x0, x1

사용자 모드로 돌아가므로 task의 유효 TTBR0를 복원합니다. 사용자 자신의 메모리 접근을 소프트웨어 PAN의 예약 표로 막아 두어서는 안 됩니다. 매크로는 현재 task에 저장된 TTBR0와 그 주소 공간의 ASID를 함께 복원합니다.

497행 그림 · 매크로 안의 상태 변화

함수·코드 범위: __swpan_exit_el0

이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_SW_TTBR0_PAN

입력
이 명령을 실행하기 직전호출 직전 값
이 명령을 실행한 직후x0, x1
주요 결과
이 명령을 실행하기 직전본문 실행 전
이 명령을 실행한 직후태스크가 저장한 TTBR0와 해당 ASID를 복원해 사용자 주소 접근을 허용합니다.

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 여러 명령과 내부 분기를 묶은 그림입니다. 작업 레지스터를 모두 보존한다는 뜻은 아니며, 정확한 변경 순서는 아래 매크로 본문과 줄별 그림에서 확인합니다.
__uaccess_ttbr0_enable 매크로 본문 · 26행

arch/arm64/include/asm/asm-uaccess.h · Linux v6.18.37

정의 선택 조건: #ifndef __ASM_ASM_UACCESS_H / #ifdef CONFIG_ARM64_SW_TTBR0_PAN

.macro __uaccess_ttbr0_enable, tmp1, tmp2 get_current_task \tmp1 ldr \tmp1, [\tmp1, #TSK_TI_TTBR0] // load saved TTBR0_EL1 mrs \tmp2, ttbr1_el1 extr \tmp2, \tmp2, \tmp1, #48 ror \tmp2, \tmp2, #16 msr ttbr1_el1, \tmp2 // set the active ASID msr ttbr0_el1, \tmp1 // set the non-PAN TTBR0_EL1 isb .endm
	b	post_ttbr_update_workaround

TTBR 변경 뒤 필요한 CPU별 보완으로 tail-call합니다. 이 함수에서 새 LR을 만들지 않아 보완 함수가 RET하면 원래 kernel_exit 호출 위치로 돌아갑니다.

504행 그림 · 호출과 실행 위치

함수·코드 범위: __swpan_exit_el0

이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_SW_TTBR0_PAN

PC
이 명령을 실행하기 직전명령 직전 PC의 값
이 명령을 실행한 직후post_ttbr_update_workaround

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 새 복귀 주소를 LR에 기록하지 않습니다. RET 역시 메모리의 스택 값을 자동으로 꺼내는 명령은 아닙니다.
SYM_CODE_END(__swpan_exit_el0)

도구에 이 코드 범위의 끝을 알려 크기와 심볼 정보를 기록합니다. CPU가 이 줄 때문에 자동으로 반환하지는 않습니다.

505행 그림 · 어셈블·링크 단계

함수·코드 범위: __swpan_exit_el0

이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_SW_TTBR0_PAN

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과SYM_CODE_END(__swpan_exit_el0)
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 끝 표기 자체가 RET나 분기를 실행하지는 않습니다.
#endif

빌드 조건으로 나누었던 구간을 닫습니다. 대상:

506행 그림 · 전처리 단계

이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_SW_TTBR0_PAN

소스
처리 전전처리 전
이 줄의 효과#endif
CPU 레지스터
처리 전해당 없음
이 줄의 효과실행되는 CPU 명령이 없습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

tsk	.req	x28		// current thread_info

같은 레지스터에 역할을 나타내는 별명을 붙입니다. 이름이 두 개가 되어도 저장 공간이 두 개 생기는 것은 아닙니다. tsk .req x28

509행 그림 · 어셈블·링크 단계
빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과tsk .req x28
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

16개 벡터와 잘못된 스택

원본 510–567행

	.text

이후 출력할 바이트를 어느 섹션에 넣을지 바꿉니다. 빌드 도구가 코드를 분류하는 작업이며 현재 CPU의 PC나 SP를 옮기는 줄은 아닙니다.

511행 그림 · 어셈블·링크 단계
빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과.text
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	.pushsection ".entry.text", "ax"

이후 출력할 바이트를 어느 섹션에 넣을지 바꿉니다. 빌드 도구가 코드를 분류하는 작업이며 현재 CPU의 PC나 SP를 옮기는 줄은 아닙니다. ".entry.text", "ax"

516행 그림 · 어셈블·링크 단계
빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과.pushsection ".entry.text", "ax"
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	.align	11

전체 벡터 표를 2KiB 경계에 맞춥니다. 16개 입구 각각 128바이트라는 하드웨어 벡터 배치에 필요한 정렬입니다. AArch64의 .align 인자는 바이트 수가 아니라 2의 지수입니다. 빌드할 때 필요한 패딩을 넣어 그 경계를 맞춥니다.

518행 그림 · 어셈블·링크 단계
빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과.align 11
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

SYM_CODE_START(vectors)

이 위치부터 (vectors)라는 코드가 시작한다고 도구에 알려 줍니다. 정렬과 외부 공개 여부도 정의에 포함됩니다. 실제 작업은 아래의 명령들이 수행합니다.

전체 16개 슬롯의 시작이며 VBAR_EL1이 가리키는 기준 주소입니다.

519행 그림 · 어셈블·링크 단계

함수·코드 범위: vectors

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과SYM_CODE_START(vectors)
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	kernel_ventry	1, t, 64, sync		// Synchronous EL1t

EL1t 커널 실행(SP_EL0 선택) 중 발생한 동기 예외(SVC·fault·debug 등)의 128바이트 벡터 입구를 생성합니다. CPU는 벡터 표의 고정 위치를 고르며 이 입구는 pt_regs 공간·스택 검사 후 같은 종류의 실제 handler로 이어집니다. 이 줄에서 동기 예외(SVC·fault·debug 등)을 일부러 발생시키는 것은 아닙니다.

520행 그림 · 벡터·핸들러 코드 생성

함수·코드 범위: vectors

매크로 인수
이 명령을 실행하기 직전호출 위치의 인수
이 명령을 실행한 직후1, t, 64, sync
생성되는 코드
이 명령을 실행하기 직전어셈블 전
이 명령을 실행한 직후128바이트 슬롯, pt_regs 공간 확보, 스택 초과 검사, 대상 예외 핸들러 분기를 생성합니다. 인자는 EL·SP 모드·실행 폭·예외 종류입니다.

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 이 한 줄은 여러 명령을 생성합니다. 레지스터 변화는 펼쳐진 본문의 각 명령과 선택된 진입 경로에 따라 생깁니다.
kernel_ventry 매크로 본문 · 39행

arch/arm64/kernel/entry.S · Linux v6.18.37

.macro kernel_ventry, el:req, ht:req, regsize:req, label:req .align 7 .Lventry_start\@: .if \el == 0 /* * This must be the first instruction of the EL0 vector entries. It is * skipped by the trampoline vectors, to trigger the cleanup. */ b .Lskip_tramp_vectors_cleanup\@ .if \regsize == 64 mrs x30, tpidrro_el0 msr tpidrro_el0, xzr .else mov x30, xzr .endif .Lskip_tramp_vectors_cleanup\@: .endif sub sp, sp, #PT_REGS_SIZE /* * Test whether the SP has overflowed, without corrupting a GPR. * Task and IRQ stacks are aligned so that SP & (1 << THREAD_SHIFT) * should always be zero. */ add sp, sp, x0 // sp' = sp + x0 sub x0, sp, x0 // x0' = sp' - x0 = (sp + x0) - x0 = sp tbnz x0, #THREAD_SHIFT, 0f sub x0, sp, x0 // x0'' = sp' - x0' = (sp + x0) - sp = x0 sub sp, sp, x0 // sp'' = sp' - x0 = (sp + x0) - x0 = sp b el\el\ht\()_\regsize\()_\label 0: /* * Either we've just detected an overflow, or we've taken an exception * while on the overflow stack. Either way, we won't return to * userspace, and can clobber EL0 registers to free up GPRs. */ /* Stash the original SP (minus PT_REGS_SIZE) in tpidr_el0. */ msr tpidr_el0, x0 /* Recover the original x0 value and stash it in tpidrro_el0 */ sub x0, sp, x0 msr tpidrro_el0, x0 /* Switch to the overflow stack */ adr_this_cpu sp, overflow_stack + OVERFLOW_STACK_SIZE, x0 /* * Check whether we were already on the overflow stack. This may happen * after panic() re-enables interrupts. */ mrs x0, tpidr_el0 // sp of interrupted context sub x0, sp, x0 // delta with top of overflow stack tst x0, #~(OVERFLOW_STACK_SIZE - 1) // within range? b.ne __bad_stack // no? -> bad stack pointer /* We were already on the overflow stack. Restore sp/x0 and carry on. */ sub sp, sp, x0 mrs x0, tpidrro_el0 b el\el\ht\()_\regsize\()_\label .org .Lventry_start\@ + 128 // Did we overflow the ventry slot? .endm
	kernel_ventry	1, t, 64, irq		// IRQ EL1t

EL1t 커널 실행(SP_EL0 선택) 중 발생한 IRQ의 128바이트 벡터 입구를 생성합니다. CPU는 벡터 표의 고정 위치를 고르며 이 입구는 pt_regs 공간·스택 검사 후 같은 종류의 실제 handler로 이어집니다. 이 줄에서 IRQ을 일부러 발생시키는 것은 아닙니다.

521행 그림 · 벡터·핸들러 코드 생성

함수·코드 범위: vectors

매크로 인수
이 명령을 실행하기 직전호출 위치의 인수
이 명령을 실행한 직후1, t, 64, irq
생성되는 코드
이 명령을 실행하기 직전어셈블 전
이 명령을 실행한 직후128바이트 슬롯, pt_regs 공간 확보, 스택 초과 검사, 대상 예외 핸들러 분기를 생성합니다. 인자는 EL·SP 모드·실행 폭·예외 종류입니다.

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 이 한 줄은 여러 명령을 생성합니다. 레지스터 변화는 펼쳐진 본문의 각 명령과 선택된 진입 경로에 따라 생깁니다.
kernel_ventry 매크로 본문 · 39행

arch/arm64/kernel/entry.S · Linux v6.18.37

.macro kernel_ventry, el:req, ht:req, regsize:req, label:req .align 7 .Lventry_start\@: .if \el == 0 /* * This must be the first instruction of the EL0 vector entries. It is * skipped by the trampoline vectors, to trigger the cleanup. */ b .Lskip_tramp_vectors_cleanup\@ .if \regsize == 64 mrs x30, tpidrro_el0 msr tpidrro_el0, xzr .else mov x30, xzr .endif .Lskip_tramp_vectors_cleanup\@: .endif sub sp, sp, #PT_REGS_SIZE /* * Test whether the SP has overflowed, without corrupting a GPR. * Task and IRQ stacks are aligned so that SP & (1 << THREAD_SHIFT) * should always be zero. */ add sp, sp, x0 // sp' = sp + x0 sub x0, sp, x0 // x0' = sp' - x0 = (sp + x0) - x0 = sp tbnz x0, #THREAD_SHIFT, 0f sub x0, sp, x0 // x0'' = sp' - x0' = (sp + x0) - sp = x0 sub sp, sp, x0 // sp'' = sp' - x0 = (sp + x0) - x0 = sp b el\el\ht\()_\regsize\()_\label 0: /* * Either we've just detected an overflow, or we've taken an exception * while on the overflow stack. Either way, we won't return to * userspace, and can clobber EL0 registers to free up GPRs. */ /* Stash the original SP (minus PT_REGS_SIZE) in tpidr_el0. */ msr tpidr_el0, x0 /* Recover the original x0 value and stash it in tpidrro_el0 */ sub x0, sp, x0 msr tpidrro_el0, x0 /* Switch to the overflow stack */ adr_this_cpu sp, overflow_stack + OVERFLOW_STACK_SIZE, x0 /* * Check whether we were already on the overflow stack. This may happen * after panic() re-enables interrupts. */ mrs x0, tpidr_el0 // sp of interrupted context sub x0, sp, x0 // delta with top of overflow stack tst x0, #~(OVERFLOW_STACK_SIZE - 1) // within range? b.ne __bad_stack // no? -> bad stack pointer /* We were already on the overflow stack. Restore sp/x0 and carry on. */ sub sp, sp, x0 mrs x0, tpidrro_el0 b el\el\ht\()_\regsize\()_\label .org .Lventry_start\@ + 128 // Did we overflow the ventry slot? .endm
	kernel_ventry	1, t, 64, fiq		// FIQ EL1t

EL1t 커널 실행(SP_EL0 선택) 중 발생한 FIQ의 128바이트 벡터 입구를 생성합니다. CPU는 벡터 표의 고정 위치를 고르며 이 입구는 pt_regs 공간·스택 검사 후 같은 종류의 실제 handler로 이어집니다. 이 줄에서 FIQ을 일부러 발생시키는 것은 아닙니다.

522행 그림 · 벡터·핸들러 코드 생성

함수·코드 범위: vectors

매크로 인수
이 명령을 실행하기 직전호출 위치의 인수
이 명령을 실행한 직후1, t, 64, fiq
생성되는 코드
이 명령을 실행하기 직전어셈블 전
이 명령을 실행한 직후128바이트 슬롯, pt_regs 공간 확보, 스택 초과 검사, 대상 예외 핸들러 분기를 생성합니다. 인자는 EL·SP 모드·실행 폭·예외 종류입니다.

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 이 한 줄은 여러 명령을 생성합니다. 레지스터 변화는 펼쳐진 본문의 각 명령과 선택된 진입 경로에 따라 생깁니다.
kernel_ventry 매크로 본문 · 39행

arch/arm64/kernel/entry.S · Linux v6.18.37

.macro kernel_ventry, el:req, ht:req, regsize:req, label:req .align 7 .Lventry_start\@: .if \el == 0 /* * This must be the first instruction of the EL0 vector entries. It is * skipped by the trampoline vectors, to trigger the cleanup. */ b .Lskip_tramp_vectors_cleanup\@ .if \regsize == 64 mrs x30, tpidrro_el0 msr tpidrro_el0, xzr .else mov x30, xzr .endif .Lskip_tramp_vectors_cleanup\@: .endif sub sp, sp, #PT_REGS_SIZE /* * Test whether the SP has overflowed, without corrupting a GPR. * Task and IRQ stacks are aligned so that SP & (1 << THREAD_SHIFT) * should always be zero. */ add sp, sp, x0 // sp' = sp + x0 sub x0, sp, x0 // x0' = sp' - x0 = (sp + x0) - x0 = sp tbnz x0, #THREAD_SHIFT, 0f sub x0, sp, x0 // x0'' = sp' - x0' = (sp + x0) - sp = x0 sub sp, sp, x0 // sp'' = sp' - x0 = (sp + x0) - x0 = sp b el\el\ht\()_\regsize\()_\label 0: /* * Either we've just detected an overflow, or we've taken an exception * while on the overflow stack. Either way, we won't return to * userspace, and can clobber EL0 registers to free up GPRs. */ /* Stash the original SP (minus PT_REGS_SIZE) in tpidr_el0. */ msr tpidr_el0, x0 /* Recover the original x0 value and stash it in tpidrro_el0 */ sub x0, sp, x0 msr tpidrro_el0, x0 /* Switch to the overflow stack */ adr_this_cpu sp, overflow_stack + OVERFLOW_STACK_SIZE, x0 /* * Check whether we were already on the overflow stack. This may happen * after panic() re-enables interrupts. */ mrs x0, tpidr_el0 // sp of interrupted context sub x0, sp, x0 // delta with top of overflow stack tst x0, #~(OVERFLOW_STACK_SIZE - 1) // within range? b.ne __bad_stack // no? -> bad stack pointer /* We were already on the overflow stack. Restore sp/x0 and carry on. */ sub sp, sp, x0 mrs x0, tpidrro_el0 b el\el\ht\()_\regsize\()_\label .org .Lventry_start\@ + 128 // Did we overflow the ventry slot? .endm
	kernel_ventry	1, t, 64, error		// Error EL1t

EL1t 커널 실행(SP_EL0 선택) 중 발생한 SError의 128바이트 벡터 입구를 생성합니다. CPU는 벡터 표의 고정 위치를 고르며 이 입구는 pt_regs 공간·스택 검사 후 같은 종류의 실제 handler로 이어집니다. 이 줄에서 SError을 일부러 발생시키는 것은 아닙니다.

523행 그림 · 벡터·핸들러 코드 생성

함수·코드 범위: vectors

매크로 인수
이 명령을 실행하기 직전호출 위치의 인수
이 명령을 실행한 직후1, t, 64, error
생성되는 코드
이 명령을 실행하기 직전어셈블 전
이 명령을 실행한 직후128바이트 슬롯, pt_regs 공간 확보, 스택 초과 검사, 대상 예외 핸들러 분기를 생성합니다. 인자는 EL·SP 모드·실행 폭·예외 종류입니다.

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 이 한 줄은 여러 명령을 생성합니다. 레지스터 변화는 펼쳐진 본문의 각 명령과 선택된 진입 경로에 따라 생깁니다.
kernel_ventry 매크로 본문 · 39행

arch/arm64/kernel/entry.S · Linux v6.18.37

.macro kernel_ventry, el:req, ht:req, regsize:req, label:req .align 7 .Lventry_start\@: .if \el == 0 /* * This must be the first instruction of the EL0 vector entries. It is * skipped by the trampoline vectors, to trigger the cleanup. */ b .Lskip_tramp_vectors_cleanup\@ .if \regsize == 64 mrs x30, tpidrro_el0 msr tpidrro_el0, xzr .else mov x30, xzr .endif .Lskip_tramp_vectors_cleanup\@: .endif sub sp, sp, #PT_REGS_SIZE /* * Test whether the SP has overflowed, without corrupting a GPR. * Task and IRQ stacks are aligned so that SP & (1 << THREAD_SHIFT) * should always be zero. */ add sp, sp, x0 // sp' = sp + x0 sub x0, sp, x0 // x0' = sp' - x0 = (sp + x0) - x0 = sp tbnz x0, #THREAD_SHIFT, 0f sub x0, sp, x0 // x0'' = sp' - x0' = (sp + x0) - sp = x0 sub sp, sp, x0 // sp'' = sp' - x0 = (sp + x0) - x0 = sp b el\el\ht\()_\regsize\()_\label 0: /* * Either we've just detected an overflow, or we've taken an exception * while on the overflow stack. Either way, we won't return to * userspace, and can clobber EL0 registers to free up GPRs. */ /* Stash the original SP (minus PT_REGS_SIZE) in tpidr_el0. */ msr tpidr_el0, x0 /* Recover the original x0 value and stash it in tpidrro_el0 */ sub x0, sp, x0 msr tpidrro_el0, x0 /* Switch to the overflow stack */ adr_this_cpu sp, overflow_stack + OVERFLOW_STACK_SIZE, x0 /* * Check whether we were already on the overflow stack. This may happen * after panic() re-enables interrupts. */ mrs x0, tpidr_el0 // sp of interrupted context sub x0, sp, x0 // delta with top of overflow stack tst x0, #~(OVERFLOW_STACK_SIZE - 1) // within range? b.ne __bad_stack // no? -> bad stack pointer /* We were already on the overflow stack. Restore sp/x0 and carry on. */ sub sp, sp, x0 mrs x0, tpidrro_el0 b el\el\ht\()_\regsize\()_\label .org .Lventry_start\@ + 128 // Did we overflow the ventry slot? .endm
	kernel_ventry	1, h, 64, sync		// Synchronous EL1h

EL1h 커널 실행(SP_EL1 선택) 중 발생한 동기 예외(SVC·fault·debug 등)의 128바이트 벡터 입구를 생성합니다. CPU는 벡터 표의 고정 위치를 고르며 이 입구는 pt_regs 공간·스택 검사 후 같은 종류의 실제 handler로 이어집니다. 이 줄에서 동기 예외(SVC·fault·debug 등)을 일부러 발생시키는 것은 아닙니다.

525행 그림 · 벡터·핸들러 코드 생성

함수·코드 범위: vectors

매크로 인수
이 명령을 실행하기 직전호출 위치의 인수
이 명령을 실행한 직후1, h, 64, sync
생성되는 코드
이 명령을 실행하기 직전어셈블 전
이 명령을 실행한 직후128바이트 슬롯, pt_regs 공간 확보, 스택 초과 검사, 대상 예외 핸들러 분기를 생성합니다. 인자는 EL·SP 모드·실행 폭·예외 종류입니다.

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 이 한 줄은 여러 명령을 생성합니다. 레지스터 변화는 펼쳐진 본문의 각 명령과 선택된 진입 경로에 따라 생깁니다.
kernel_ventry 매크로 본문 · 39행

arch/arm64/kernel/entry.S · Linux v6.18.37

.macro kernel_ventry, el:req, ht:req, regsize:req, label:req .align 7 .Lventry_start\@: .if \el == 0 /* * This must be the first instruction of the EL0 vector entries. It is * skipped by the trampoline vectors, to trigger the cleanup. */ b .Lskip_tramp_vectors_cleanup\@ .if \regsize == 64 mrs x30, tpidrro_el0 msr tpidrro_el0, xzr .else mov x30, xzr .endif .Lskip_tramp_vectors_cleanup\@: .endif sub sp, sp, #PT_REGS_SIZE /* * Test whether the SP has overflowed, without corrupting a GPR. * Task and IRQ stacks are aligned so that SP & (1 << THREAD_SHIFT) * should always be zero. */ add sp, sp, x0 // sp' = sp + x0 sub x0, sp, x0 // x0' = sp' - x0 = (sp + x0) - x0 = sp tbnz x0, #THREAD_SHIFT, 0f sub x0, sp, x0 // x0'' = sp' - x0' = (sp + x0) - sp = x0 sub sp, sp, x0 // sp'' = sp' - x0 = (sp + x0) - x0 = sp b el\el\ht\()_\regsize\()_\label 0: /* * Either we've just detected an overflow, or we've taken an exception * while on the overflow stack. Either way, we won't return to * userspace, and can clobber EL0 registers to free up GPRs. */ /* Stash the original SP (minus PT_REGS_SIZE) in tpidr_el0. */ msr tpidr_el0, x0 /* Recover the original x0 value and stash it in tpidrro_el0 */ sub x0, sp, x0 msr tpidrro_el0, x0 /* Switch to the overflow stack */ adr_this_cpu sp, overflow_stack + OVERFLOW_STACK_SIZE, x0 /* * Check whether we were already on the overflow stack. This may happen * after panic() re-enables interrupts. */ mrs x0, tpidr_el0 // sp of interrupted context sub x0, sp, x0 // delta with top of overflow stack tst x0, #~(OVERFLOW_STACK_SIZE - 1) // within range? b.ne __bad_stack // no? -> bad stack pointer /* We were already on the overflow stack. Restore sp/x0 and carry on. */ sub sp, sp, x0 mrs x0, tpidrro_el0 b el\el\ht\()_\regsize\()_\label .org .Lventry_start\@ + 128 // Did we overflow the ventry slot? .endm
	kernel_ventry	1, h, 64, irq		// IRQ EL1h

EL1h 커널 실행(SP_EL1 선택) 중 발생한 IRQ의 128바이트 벡터 입구를 생성합니다. CPU는 벡터 표의 고정 위치를 고르며 이 입구는 pt_regs 공간·스택 검사 후 같은 종류의 실제 handler로 이어집니다. 이 줄에서 IRQ을 일부러 발생시키는 것은 아닙니다.

526행 그림 · 벡터·핸들러 코드 생성

함수·코드 범위: vectors

매크로 인수
이 명령을 실행하기 직전호출 위치의 인수
이 명령을 실행한 직후1, h, 64, irq
생성되는 코드
이 명령을 실행하기 직전어셈블 전
이 명령을 실행한 직후128바이트 슬롯, pt_regs 공간 확보, 스택 초과 검사, 대상 예외 핸들러 분기를 생성합니다. 인자는 EL·SP 모드·실행 폭·예외 종류입니다.

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 이 한 줄은 여러 명령을 생성합니다. 레지스터 변화는 펼쳐진 본문의 각 명령과 선택된 진입 경로에 따라 생깁니다.
kernel_ventry 매크로 본문 · 39행

arch/arm64/kernel/entry.S · Linux v6.18.37

.macro kernel_ventry, el:req, ht:req, regsize:req, label:req .align 7 .Lventry_start\@: .if \el == 0 /* * This must be the first instruction of the EL0 vector entries. It is * skipped by the trampoline vectors, to trigger the cleanup. */ b .Lskip_tramp_vectors_cleanup\@ .if \regsize == 64 mrs x30, tpidrro_el0 msr tpidrro_el0, xzr .else mov x30, xzr .endif .Lskip_tramp_vectors_cleanup\@: .endif sub sp, sp, #PT_REGS_SIZE /* * Test whether the SP has overflowed, without corrupting a GPR. * Task and IRQ stacks are aligned so that SP & (1 << THREAD_SHIFT) * should always be zero. */ add sp, sp, x0 // sp' = sp + x0 sub x0, sp, x0 // x0' = sp' - x0 = (sp + x0) - x0 = sp tbnz x0, #THREAD_SHIFT, 0f sub x0, sp, x0 // x0'' = sp' - x0' = (sp + x0) - sp = x0 sub sp, sp, x0 // sp'' = sp' - x0 = (sp + x0) - x0 = sp b el\el\ht\()_\regsize\()_\label 0: /* * Either we've just detected an overflow, or we've taken an exception * while on the overflow stack. Either way, we won't return to * userspace, and can clobber EL0 registers to free up GPRs. */ /* Stash the original SP (minus PT_REGS_SIZE) in tpidr_el0. */ msr tpidr_el0, x0 /* Recover the original x0 value and stash it in tpidrro_el0 */ sub x0, sp, x0 msr tpidrro_el0, x0 /* Switch to the overflow stack */ adr_this_cpu sp, overflow_stack + OVERFLOW_STACK_SIZE, x0 /* * Check whether we were already on the overflow stack. This may happen * after panic() re-enables interrupts. */ mrs x0, tpidr_el0 // sp of interrupted context sub x0, sp, x0 // delta with top of overflow stack tst x0, #~(OVERFLOW_STACK_SIZE - 1) // within range? b.ne __bad_stack // no? -> bad stack pointer /* We were already on the overflow stack. Restore sp/x0 and carry on. */ sub sp, sp, x0 mrs x0, tpidrro_el0 b el\el\ht\()_\regsize\()_\label .org .Lventry_start\@ + 128 // Did we overflow the ventry slot? .endm
	kernel_ventry	1, h, 64, fiq		// FIQ EL1h

EL1h 커널 실행(SP_EL1 선택) 중 발생한 FIQ의 128바이트 벡터 입구를 생성합니다. CPU는 벡터 표의 고정 위치를 고르며 이 입구는 pt_regs 공간·스택 검사 후 같은 종류의 실제 handler로 이어집니다. 이 줄에서 FIQ을 일부러 발생시키는 것은 아닙니다.

527행 그림 · 벡터·핸들러 코드 생성

함수·코드 범위: vectors

매크로 인수
이 명령을 실행하기 직전호출 위치의 인수
이 명령을 실행한 직후1, h, 64, fiq
생성되는 코드
이 명령을 실행하기 직전어셈블 전
이 명령을 실행한 직후128바이트 슬롯, pt_regs 공간 확보, 스택 초과 검사, 대상 예외 핸들러 분기를 생성합니다. 인자는 EL·SP 모드·실행 폭·예외 종류입니다.

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 이 한 줄은 여러 명령을 생성합니다. 레지스터 변화는 펼쳐진 본문의 각 명령과 선택된 진입 경로에 따라 생깁니다.
kernel_ventry 매크로 본문 · 39행

arch/arm64/kernel/entry.S · Linux v6.18.37

.macro kernel_ventry, el:req, ht:req, regsize:req, label:req .align 7 .Lventry_start\@: .if \el == 0 /* * This must be the first instruction of the EL0 vector entries. It is * skipped by the trampoline vectors, to trigger the cleanup. */ b .Lskip_tramp_vectors_cleanup\@ .if \regsize == 64 mrs x30, tpidrro_el0 msr tpidrro_el0, xzr .else mov x30, xzr .endif .Lskip_tramp_vectors_cleanup\@: .endif sub sp, sp, #PT_REGS_SIZE /* * Test whether the SP has overflowed, without corrupting a GPR. * Task and IRQ stacks are aligned so that SP & (1 << THREAD_SHIFT) * should always be zero. */ add sp, sp, x0 // sp' = sp + x0 sub x0, sp, x0 // x0' = sp' - x0 = (sp + x0) - x0 = sp tbnz x0, #THREAD_SHIFT, 0f sub x0, sp, x0 // x0'' = sp' - x0' = (sp + x0) - sp = x0 sub sp, sp, x0 // sp'' = sp' - x0 = (sp + x0) - x0 = sp b el\el\ht\()_\regsize\()_\label 0: /* * Either we've just detected an overflow, or we've taken an exception * while on the overflow stack. Either way, we won't return to * userspace, and can clobber EL0 registers to free up GPRs. */ /* Stash the original SP (minus PT_REGS_SIZE) in tpidr_el0. */ msr tpidr_el0, x0 /* Recover the original x0 value and stash it in tpidrro_el0 */ sub x0, sp, x0 msr tpidrro_el0, x0 /* Switch to the overflow stack */ adr_this_cpu sp, overflow_stack + OVERFLOW_STACK_SIZE, x0 /* * Check whether we were already on the overflow stack. This may happen * after panic() re-enables interrupts. */ mrs x0, tpidr_el0 // sp of interrupted context sub x0, sp, x0 // delta with top of overflow stack tst x0, #~(OVERFLOW_STACK_SIZE - 1) // within range? b.ne __bad_stack // no? -> bad stack pointer /* We were already on the overflow stack. Restore sp/x0 and carry on. */ sub sp, sp, x0 mrs x0, tpidrro_el0 b el\el\ht\()_\regsize\()_\label .org .Lventry_start\@ + 128 // Did we overflow the ventry slot? .endm
	kernel_ventry	1, h, 64, error		// Error EL1h

EL1h 커널 실행(SP_EL1 선택) 중 발생한 SError의 128바이트 벡터 입구를 생성합니다. CPU는 벡터 표의 고정 위치를 고르며 이 입구는 pt_regs 공간·스택 검사 후 같은 종류의 실제 handler로 이어집니다. 이 줄에서 SError을 일부러 발생시키는 것은 아닙니다.

528행 그림 · 벡터·핸들러 코드 생성

함수·코드 범위: vectors

매크로 인수
이 명령을 실행하기 직전호출 위치의 인수
이 명령을 실행한 직후1, h, 64, error
생성되는 코드
이 명령을 실행하기 직전어셈블 전
이 명령을 실행한 직후128바이트 슬롯, pt_regs 공간 확보, 스택 초과 검사, 대상 예외 핸들러 분기를 생성합니다. 인자는 EL·SP 모드·실행 폭·예외 종류입니다.

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 이 한 줄은 여러 명령을 생성합니다. 레지스터 변화는 펼쳐진 본문의 각 명령과 선택된 진입 경로에 따라 생깁니다.
kernel_ventry 매크로 본문 · 39행

arch/arm64/kernel/entry.S · Linux v6.18.37

.macro kernel_ventry, el:req, ht:req, regsize:req, label:req .align 7 .Lventry_start\@: .if \el == 0 /* * This must be the first instruction of the EL0 vector entries. It is * skipped by the trampoline vectors, to trigger the cleanup. */ b .Lskip_tramp_vectors_cleanup\@ .if \regsize == 64 mrs x30, tpidrro_el0 msr tpidrro_el0, xzr .else mov x30, xzr .endif .Lskip_tramp_vectors_cleanup\@: .endif sub sp, sp, #PT_REGS_SIZE /* * Test whether the SP has overflowed, without corrupting a GPR. * Task and IRQ stacks are aligned so that SP & (1 << THREAD_SHIFT) * should always be zero. */ add sp, sp, x0 // sp' = sp + x0 sub x0, sp, x0 // x0' = sp' - x0 = (sp + x0) - x0 = sp tbnz x0, #THREAD_SHIFT, 0f sub x0, sp, x0 // x0'' = sp' - x0' = (sp + x0) - sp = x0 sub sp, sp, x0 // sp'' = sp' - x0 = (sp + x0) - x0 = sp b el\el\ht\()_\regsize\()_\label 0: /* * Either we've just detected an overflow, or we've taken an exception * while on the overflow stack. Either way, we won't return to * userspace, and can clobber EL0 registers to free up GPRs. */ /* Stash the original SP (minus PT_REGS_SIZE) in tpidr_el0. */ msr tpidr_el0, x0 /* Recover the original x0 value and stash it in tpidrro_el0 */ sub x0, sp, x0 msr tpidrro_el0, x0 /* Switch to the overflow stack */ adr_this_cpu sp, overflow_stack + OVERFLOW_STACK_SIZE, x0 /* * Check whether we were already on the overflow stack. This may happen * after panic() re-enables interrupts. */ mrs x0, tpidr_el0 // sp of interrupted context sub x0, sp, x0 // delta with top of overflow stack tst x0, #~(OVERFLOW_STACK_SIZE - 1) // within range? b.ne __bad_stack // no? -> bad stack pointer /* We were already on the overflow stack. Restore sp/x0 and carry on. */ sub sp, sp, x0 mrs x0, tpidrro_el0 b el\el\ht\()_\regsize\()_\label .org .Lventry_start\@ + 128 // Did we overflow the ventry slot? .endm
	kernel_ventry	0, t, 64, sync		// Synchronous 64-bit EL0

EL0의 AArch64 사용자 실행 중 발생한 동기 예외(SVC·fault·debug 등)의 128바이트 벡터 입구를 생성합니다. CPU는 벡터 표의 고정 위치를 고르며 이 입구는 pt_regs 공간·스택 검사 후 같은 종류의 실제 handler로 이어집니다. 이 줄에서 동기 예외(SVC·fault·debug 등)을 일부러 발생시키는 것은 아닙니다.

530행 그림 · 벡터·핸들러 코드 생성

함수·코드 범위: vectors

매크로 인수
이 명령을 실행하기 직전호출 위치의 인수
이 명령을 실행한 직후0, t, 64, sync
생성되는 코드
이 명령을 실행하기 직전어셈블 전
이 명령을 실행한 직후128바이트 슬롯, pt_regs 공간 확보, 스택 초과 검사, 대상 예외 핸들러 분기를 생성합니다. 인자는 EL·SP 모드·실행 폭·예외 종류입니다.

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 이 한 줄은 여러 명령을 생성합니다. 레지스터 변화는 펼쳐진 본문의 각 명령과 선택된 진입 경로에 따라 생깁니다.
kernel_ventry 매크로 본문 · 39행

arch/arm64/kernel/entry.S · Linux v6.18.37

.macro kernel_ventry, el:req, ht:req, regsize:req, label:req .align 7 .Lventry_start\@: .if \el == 0 /* * This must be the first instruction of the EL0 vector entries. It is * skipped by the trampoline vectors, to trigger the cleanup. */ b .Lskip_tramp_vectors_cleanup\@ .if \regsize == 64 mrs x30, tpidrro_el0 msr tpidrro_el0, xzr .else mov x30, xzr .endif .Lskip_tramp_vectors_cleanup\@: .endif sub sp, sp, #PT_REGS_SIZE /* * Test whether the SP has overflowed, without corrupting a GPR. * Task and IRQ stacks are aligned so that SP & (1 << THREAD_SHIFT) * should always be zero. */ add sp, sp, x0 // sp' = sp + x0 sub x0, sp, x0 // x0' = sp' - x0 = (sp + x0) - x0 = sp tbnz x0, #THREAD_SHIFT, 0f sub x0, sp, x0 // x0'' = sp' - x0' = (sp + x0) - sp = x0 sub sp, sp, x0 // sp'' = sp' - x0 = (sp + x0) - x0 = sp b el\el\ht\()_\regsize\()_\label 0: /* * Either we've just detected an overflow, or we've taken an exception * while on the overflow stack. Either way, we won't return to * userspace, and can clobber EL0 registers to free up GPRs. */ /* Stash the original SP (minus PT_REGS_SIZE) in tpidr_el0. */ msr tpidr_el0, x0 /* Recover the original x0 value and stash it in tpidrro_el0 */ sub x0, sp, x0 msr tpidrro_el0, x0 /* Switch to the overflow stack */ adr_this_cpu sp, overflow_stack + OVERFLOW_STACK_SIZE, x0 /* * Check whether we were already on the overflow stack. This may happen * after panic() re-enables interrupts. */ mrs x0, tpidr_el0 // sp of interrupted context sub x0, sp, x0 // delta with top of overflow stack tst x0, #~(OVERFLOW_STACK_SIZE - 1) // within range? b.ne __bad_stack // no? -> bad stack pointer /* We were already on the overflow stack. Restore sp/x0 and carry on. */ sub sp, sp, x0 mrs x0, tpidrro_el0 b el\el\ht\()_\regsize\()_\label .org .Lventry_start\@ + 128 // Did we overflow the ventry slot? .endm
	kernel_ventry	0, t, 64, irq		// IRQ 64-bit EL0

EL0의 AArch64 사용자 실행 중 발생한 IRQ의 128바이트 벡터 입구를 생성합니다. CPU는 벡터 표의 고정 위치를 고르며 이 입구는 pt_regs 공간·스택 검사 후 같은 종류의 실제 handler로 이어집니다. 이 줄에서 IRQ을 일부러 발생시키는 것은 아닙니다.

531행 그림 · 벡터·핸들러 코드 생성

함수·코드 범위: vectors

매크로 인수
이 명령을 실행하기 직전호출 위치의 인수
이 명령을 실행한 직후0, t, 64, irq
생성되는 코드
이 명령을 실행하기 직전어셈블 전
이 명령을 실행한 직후128바이트 슬롯, pt_regs 공간 확보, 스택 초과 검사, 대상 예외 핸들러 분기를 생성합니다. 인자는 EL·SP 모드·실행 폭·예외 종류입니다.

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 이 한 줄은 여러 명령을 생성합니다. 레지스터 변화는 펼쳐진 본문의 각 명령과 선택된 진입 경로에 따라 생깁니다.
kernel_ventry 매크로 본문 · 39행

arch/arm64/kernel/entry.S · Linux v6.18.37

.macro kernel_ventry, el:req, ht:req, regsize:req, label:req .align 7 .Lventry_start\@: .if \el == 0 /* * This must be the first instruction of the EL0 vector entries. It is * skipped by the trampoline vectors, to trigger the cleanup. */ b .Lskip_tramp_vectors_cleanup\@ .if \regsize == 64 mrs x30, tpidrro_el0 msr tpidrro_el0, xzr .else mov x30, xzr .endif .Lskip_tramp_vectors_cleanup\@: .endif sub sp, sp, #PT_REGS_SIZE /* * Test whether the SP has overflowed, without corrupting a GPR. * Task and IRQ stacks are aligned so that SP & (1 << THREAD_SHIFT) * should always be zero. */ add sp, sp, x0 // sp' = sp + x0 sub x0, sp, x0 // x0' = sp' - x0 = (sp + x0) - x0 = sp tbnz x0, #THREAD_SHIFT, 0f sub x0, sp, x0 // x0'' = sp' - x0' = (sp + x0) - sp = x0 sub sp, sp, x0 // sp'' = sp' - x0 = (sp + x0) - x0 = sp b el\el\ht\()_\regsize\()_\label 0: /* * Either we've just detected an overflow, or we've taken an exception * while on the overflow stack. Either way, we won't return to * userspace, and can clobber EL0 registers to free up GPRs. */ /* Stash the original SP (minus PT_REGS_SIZE) in tpidr_el0. */ msr tpidr_el0, x0 /* Recover the original x0 value and stash it in tpidrro_el0 */ sub x0, sp, x0 msr tpidrro_el0, x0 /* Switch to the overflow stack */ adr_this_cpu sp, overflow_stack + OVERFLOW_STACK_SIZE, x0 /* * Check whether we were already on the overflow stack. This may happen * after panic() re-enables interrupts. */ mrs x0, tpidr_el0 // sp of interrupted context sub x0, sp, x0 // delta with top of overflow stack tst x0, #~(OVERFLOW_STACK_SIZE - 1) // within range? b.ne __bad_stack // no? -> bad stack pointer /* We were already on the overflow stack. Restore sp/x0 and carry on. */ sub sp, sp, x0 mrs x0, tpidrro_el0 b el\el\ht\()_\regsize\()_\label .org .Lventry_start\@ + 128 // Did we overflow the ventry slot? .endm
	kernel_ventry	0, t, 64, fiq		// FIQ 64-bit EL0

EL0의 AArch64 사용자 실행 중 발생한 FIQ의 128바이트 벡터 입구를 생성합니다. CPU는 벡터 표의 고정 위치를 고르며 이 입구는 pt_regs 공간·스택 검사 후 같은 종류의 실제 handler로 이어집니다. 이 줄에서 FIQ을 일부러 발생시키는 것은 아닙니다.

532행 그림 · 벡터·핸들러 코드 생성

함수·코드 범위: vectors

매크로 인수
이 명령을 실행하기 직전호출 위치의 인수
이 명령을 실행한 직후0, t, 64, fiq
생성되는 코드
이 명령을 실행하기 직전어셈블 전
이 명령을 실행한 직후128바이트 슬롯, pt_regs 공간 확보, 스택 초과 검사, 대상 예외 핸들러 분기를 생성합니다. 인자는 EL·SP 모드·실행 폭·예외 종류입니다.

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 이 한 줄은 여러 명령을 생성합니다. 레지스터 변화는 펼쳐진 본문의 각 명령과 선택된 진입 경로에 따라 생깁니다.
kernel_ventry 매크로 본문 · 39행

arch/arm64/kernel/entry.S · Linux v6.18.37

.macro kernel_ventry, el:req, ht:req, regsize:req, label:req .align 7 .Lventry_start\@: .if \el == 0 /* * This must be the first instruction of the EL0 vector entries. It is * skipped by the trampoline vectors, to trigger the cleanup. */ b .Lskip_tramp_vectors_cleanup\@ .if \regsize == 64 mrs x30, tpidrro_el0 msr tpidrro_el0, xzr .else mov x30, xzr .endif .Lskip_tramp_vectors_cleanup\@: .endif sub sp, sp, #PT_REGS_SIZE /* * Test whether the SP has overflowed, without corrupting a GPR. * Task and IRQ stacks are aligned so that SP & (1 << THREAD_SHIFT) * should always be zero. */ add sp, sp, x0 // sp' = sp + x0 sub x0, sp, x0 // x0' = sp' - x0 = (sp + x0) - x0 = sp tbnz x0, #THREAD_SHIFT, 0f sub x0, sp, x0 // x0'' = sp' - x0' = (sp + x0) - sp = x0 sub sp, sp, x0 // sp'' = sp' - x0 = (sp + x0) - x0 = sp b el\el\ht\()_\regsize\()_\label 0: /* * Either we've just detected an overflow, or we've taken an exception * while on the overflow stack. Either way, we won't return to * userspace, and can clobber EL0 registers to free up GPRs. */ /* Stash the original SP (minus PT_REGS_SIZE) in tpidr_el0. */ msr tpidr_el0, x0 /* Recover the original x0 value and stash it in tpidrro_el0 */ sub x0, sp, x0 msr tpidrro_el0, x0 /* Switch to the overflow stack */ adr_this_cpu sp, overflow_stack + OVERFLOW_STACK_SIZE, x0 /* * Check whether we were already on the overflow stack. This may happen * after panic() re-enables interrupts. */ mrs x0, tpidr_el0 // sp of interrupted context sub x0, sp, x0 // delta with top of overflow stack tst x0, #~(OVERFLOW_STACK_SIZE - 1) // within range? b.ne __bad_stack // no? -> bad stack pointer /* We were already on the overflow stack. Restore sp/x0 and carry on. */ sub sp, sp, x0 mrs x0, tpidrro_el0 b el\el\ht\()_\regsize\()_\label .org .Lventry_start\@ + 128 // Did we overflow the ventry slot? .endm
	kernel_ventry	0, t, 64, error		// Error 64-bit EL0

EL0의 AArch64 사용자 실행 중 발생한 SError의 128바이트 벡터 입구를 생성합니다. CPU는 벡터 표의 고정 위치를 고르며 이 입구는 pt_regs 공간·스택 검사 후 같은 종류의 실제 handler로 이어집니다. 이 줄에서 SError을 일부러 발생시키는 것은 아닙니다.

533행 그림 · 벡터·핸들러 코드 생성

함수·코드 범위: vectors

매크로 인수
이 명령을 실행하기 직전호출 위치의 인수
이 명령을 실행한 직후0, t, 64, error
생성되는 코드
이 명령을 실행하기 직전어셈블 전
이 명령을 실행한 직후128바이트 슬롯, pt_regs 공간 확보, 스택 초과 검사, 대상 예외 핸들러 분기를 생성합니다. 인자는 EL·SP 모드·실행 폭·예외 종류입니다.

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 이 한 줄은 여러 명령을 생성합니다. 레지스터 변화는 펼쳐진 본문의 각 명령과 선택된 진입 경로에 따라 생깁니다.
kernel_ventry 매크로 본문 · 39행

arch/arm64/kernel/entry.S · Linux v6.18.37

.macro kernel_ventry, el:req, ht:req, regsize:req, label:req .align 7 .Lventry_start\@: .if \el == 0 /* * This must be the first instruction of the EL0 vector entries. It is * skipped by the trampoline vectors, to trigger the cleanup. */ b .Lskip_tramp_vectors_cleanup\@ .if \regsize == 64 mrs x30, tpidrro_el0 msr tpidrro_el0, xzr .else mov x30, xzr .endif .Lskip_tramp_vectors_cleanup\@: .endif sub sp, sp, #PT_REGS_SIZE /* * Test whether the SP has overflowed, without corrupting a GPR. * Task and IRQ stacks are aligned so that SP & (1 << THREAD_SHIFT) * should always be zero. */ add sp, sp, x0 // sp' = sp + x0 sub x0, sp, x0 // x0' = sp' - x0 = (sp + x0) - x0 = sp tbnz x0, #THREAD_SHIFT, 0f sub x0, sp, x0 // x0'' = sp' - x0' = (sp + x0) - sp = x0 sub sp, sp, x0 // sp'' = sp' - x0 = (sp + x0) - x0 = sp b el\el\ht\()_\regsize\()_\label 0: /* * Either we've just detected an overflow, or we've taken an exception * while on the overflow stack. Either way, we won't return to * userspace, and can clobber EL0 registers to free up GPRs. */ /* Stash the original SP (minus PT_REGS_SIZE) in tpidr_el0. */ msr tpidr_el0, x0 /* Recover the original x0 value and stash it in tpidrro_el0 */ sub x0, sp, x0 msr tpidrro_el0, x0 /* Switch to the overflow stack */ adr_this_cpu sp, overflow_stack + OVERFLOW_STACK_SIZE, x0 /* * Check whether we were already on the overflow stack. This may happen * after panic() re-enables interrupts. */ mrs x0, tpidr_el0 // sp of interrupted context sub x0, sp, x0 // delta with top of overflow stack tst x0, #~(OVERFLOW_STACK_SIZE - 1) // within range? b.ne __bad_stack // no? -> bad stack pointer /* We were already on the overflow stack. Restore sp/x0 and carry on. */ sub sp, sp, x0 mrs x0, tpidrro_el0 b el\el\ht\()_\regsize\()_\label .org .Lventry_start\@ + 128 // Did we overflow the ventry slot? .endm
	kernel_ventry	0, t, 32, sync		// Synchronous 32-bit EL0

EL0의 AArch32 사용자 실행 중 발생한 동기 예외(SVC·fault·debug 등)의 128바이트 벡터 입구를 생성합니다. CPU는 벡터 표의 고정 위치를 고르며 이 입구는 pt_regs 공간·스택 검사 후 같은 종류의 실제 handler로 이어집니다. 이 줄에서 동기 예외(SVC·fault·debug 등)을 일부러 발생시키는 것은 아닙니다.

535행 그림 · 벡터·핸들러 코드 생성

함수·코드 범위: vectors

매크로 인수
이 명령을 실행하기 직전호출 위치의 인수
이 명령을 실행한 직후0, t, 32, sync
생성되는 코드
이 명령을 실행하기 직전어셈블 전
이 명령을 실행한 직후128바이트 슬롯, pt_regs 공간 확보, 스택 초과 검사, 대상 예외 핸들러 분기를 생성합니다. 인자는 EL·SP 모드·실행 폭·예외 종류입니다.

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 이 한 줄은 여러 명령을 생성합니다. 레지스터 변화는 펼쳐진 본문의 각 명령과 선택된 진입 경로에 따라 생깁니다.
kernel_ventry 매크로 본문 · 39행

arch/arm64/kernel/entry.S · Linux v6.18.37

.macro kernel_ventry, el:req, ht:req, regsize:req, label:req .align 7 .Lventry_start\@: .if \el == 0 /* * This must be the first instruction of the EL0 vector entries. It is * skipped by the trampoline vectors, to trigger the cleanup. */ b .Lskip_tramp_vectors_cleanup\@ .if \regsize == 64 mrs x30, tpidrro_el0 msr tpidrro_el0, xzr .else mov x30, xzr .endif .Lskip_tramp_vectors_cleanup\@: .endif sub sp, sp, #PT_REGS_SIZE /* * Test whether the SP has overflowed, without corrupting a GPR. * Task and IRQ stacks are aligned so that SP & (1 << THREAD_SHIFT) * should always be zero. */ add sp, sp, x0 // sp' = sp + x0 sub x0, sp, x0 // x0' = sp' - x0 = (sp + x0) - x0 = sp tbnz x0, #THREAD_SHIFT, 0f sub x0, sp, x0 // x0'' = sp' - x0' = (sp + x0) - sp = x0 sub sp, sp, x0 // sp'' = sp' - x0 = (sp + x0) - x0 = sp b el\el\ht\()_\regsize\()_\label 0: /* * Either we've just detected an overflow, or we've taken an exception * while on the overflow stack. Either way, we won't return to * userspace, and can clobber EL0 registers to free up GPRs. */ /* Stash the original SP (minus PT_REGS_SIZE) in tpidr_el0. */ msr tpidr_el0, x0 /* Recover the original x0 value and stash it in tpidrro_el0 */ sub x0, sp, x0 msr tpidrro_el0, x0 /* Switch to the overflow stack */ adr_this_cpu sp, overflow_stack + OVERFLOW_STACK_SIZE, x0 /* * Check whether we were already on the overflow stack. This may happen * after panic() re-enables interrupts. */ mrs x0, tpidr_el0 // sp of interrupted context sub x0, sp, x0 // delta with top of overflow stack tst x0, #~(OVERFLOW_STACK_SIZE - 1) // within range? b.ne __bad_stack // no? -> bad stack pointer /* We were already on the overflow stack. Restore sp/x0 and carry on. */ sub sp, sp, x0 mrs x0, tpidrro_el0 b el\el\ht\()_\regsize\()_\label .org .Lventry_start\@ + 128 // Did we overflow the ventry slot? .endm
	kernel_ventry	0, t, 32, irq		// IRQ 32-bit EL0

EL0의 AArch32 사용자 실행 중 발생한 IRQ의 128바이트 벡터 입구를 생성합니다. CPU는 벡터 표의 고정 위치를 고르며 이 입구는 pt_regs 공간·스택 검사 후 같은 종류의 실제 handler로 이어집니다. 이 줄에서 IRQ을 일부러 발생시키는 것은 아닙니다.

536행 그림 · 벡터·핸들러 코드 생성

함수·코드 범위: vectors

매크로 인수
이 명령을 실행하기 직전호출 위치의 인수
이 명령을 실행한 직후0, t, 32, irq
생성되는 코드
이 명령을 실행하기 직전어셈블 전
이 명령을 실행한 직후128바이트 슬롯, pt_regs 공간 확보, 스택 초과 검사, 대상 예외 핸들러 분기를 생성합니다. 인자는 EL·SP 모드·실행 폭·예외 종류입니다.

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 이 한 줄은 여러 명령을 생성합니다. 레지스터 변화는 펼쳐진 본문의 각 명령과 선택된 진입 경로에 따라 생깁니다.
kernel_ventry 매크로 본문 · 39행

arch/arm64/kernel/entry.S · Linux v6.18.37

.macro kernel_ventry, el:req, ht:req, regsize:req, label:req .align 7 .Lventry_start\@: .if \el == 0 /* * This must be the first instruction of the EL0 vector entries. It is * skipped by the trampoline vectors, to trigger the cleanup. */ b .Lskip_tramp_vectors_cleanup\@ .if \regsize == 64 mrs x30, tpidrro_el0 msr tpidrro_el0, xzr .else mov x30, xzr .endif .Lskip_tramp_vectors_cleanup\@: .endif sub sp, sp, #PT_REGS_SIZE /* * Test whether the SP has overflowed, without corrupting a GPR. * Task and IRQ stacks are aligned so that SP & (1 << THREAD_SHIFT) * should always be zero. */ add sp, sp, x0 // sp' = sp + x0 sub x0, sp, x0 // x0' = sp' - x0 = (sp + x0) - x0 = sp tbnz x0, #THREAD_SHIFT, 0f sub x0, sp, x0 // x0'' = sp' - x0' = (sp + x0) - sp = x0 sub sp, sp, x0 // sp'' = sp' - x0 = (sp + x0) - x0 = sp b el\el\ht\()_\regsize\()_\label 0: /* * Either we've just detected an overflow, or we've taken an exception * while on the overflow stack. Either way, we won't return to * userspace, and can clobber EL0 registers to free up GPRs. */ /* Stash the original SP (minus PT_REGS_SIZE) in tpidr_el0. */ msr tpidr_el0, x0 /* Recover the original x0 value and stash it in tpidrro_el0 */ sub x0, sp, x0 msr tpidrro_el0, x0 /* Switch to the overflow stack */ adr_this_cpu sp, overflow_stack + OVERFLOW_STACK_SIZE, x0 /* * Check whether we were already on the overflow stack. This may happen * after panic() re-enables interrupts. */ mrs x0, tpidr_el0 // sp of interrupted context sub x0, sp, x0 // delta with top of overflow stack tst x0, #~(OVERFLOW_STACK_SIZE - 1) // within range? b.ne __bad_stack // no? -> bad stack pointer /* We were already on the overflow stack. Restore sp/x0 and carry on. */ sub sp, sp, x0 mrs x0, tpidrro_el0 b el\el\ht\()_\regsize\()_\label .org .Lventry_start\@ + 128 // Did we overflow the ventry slot? .endm
	kernel_ventry	0, t, 32, fiq		// FIQ 32-bit EL0

EL0의 AArch32 사용자 실행 중 발생한 FIQ의 128바이트 벡터 입구를 생성합니다. CPU는 벡터 표의 고정 위치를 고르며 이 입구는 pt_regs 공간·스택 검사 후 같은 종류의 실제 handler로 이어집니다. 이 줄에서 FIQ을 일부러 발생시키는 것은 아닙니다.

537행 그림 · 벡터·핸들러 코드 생성

함수·코드 범위: vectors

매크로 인수
이 명령을 실행하기 직전호출 위치의 인수
이 명령을 실행한 직후0, t, 32, fiq
생성되는 코드
이 명령을 실행하기 직전어셈블 전
이 명령을 실행한 직후128바이트 슬롯, pt_regs 공간 확보, 스택 초과 검사, 대상 예외 핸들러 분기를 생성합니다. 인자는 EL·SP 모드·실행 폭·예외 종류입니다.

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 이 한 줄은 여러 명령을 생성합니다. 레지스터 변화는 펼쳐진 본문의 각 명령과 선택된 진입 경로에 따라 생깁니다.
kernel_ventry 매크로 본문 · 39행

arch/arm64/kernel/entry.S · Linux v6.18.37

.macro kernel_ventry, el:req, ht:req, regsize:req, label:req .align 7 .Lventry_start\@: .if \el == 0 /* * This must be the first instruction of the EL0 vector entries. It is * skipped by the trampoline vectors, to trigger the cleanup. */ b .Lskip_tramp_vectors_cleanup\@ .if \regsize == 64 mrs x30, tpidrro_el0 msr tpidrro_el0, xzr .else mov x30, xzr .endif .Lskip_tramp_vectors_cleanup\@: .endif sub sp, sp, #PT_REGS_SIZE /* * Test whether the SP has overflowed, without corrupting a GPR. * Task and IRQ stacks are aligned so that SP & (1 << THREAD_SHIFT) * should always be zero. */ add sp, sp, x0 // sp' = sp + x0 sub x0, sp, x0 // x0' = sp' - x0 = (sp + x0) - x0 = sp tbnz x0, #THREAD_SHIFT, 0f sub x0, sp, x0 // x0'' = sp' - x0' = (sp + x0) - sp = x0 sub sp, sp, x0 // sp'' = sp' - x0 = (sp + x0) - x0 = sp b el\el\ht\()_\regsize\()_\label 0: /* * Either we've just detected an overflow, or we've taken an exception * while on the overflow stack. Either way, we won't return to * userspace, and can clobber EL0 registers to free up GPRs. */ /* Stash the original SP (minus PT_REGS_SIZE) in tpidr_el0. */ msr tpidr_el0, x0 /* Recover the original x0 value and stash it in tpidrro_el0 */ sub x0, sp, x0 msr tpidrro_el0, x0 /* Switch to the overflow stack */ adr_this_cpu sp, overflow_stack + OVERFLOW_STACK_SIZE, x0 /* * Check whether we were already on the overflow stack. This may happen * after panic() re-enables interrupts. */ mrs x0, tpidr_el0 // sp of interrupted context sub x0, sp, x0 // delta with top of overflow stack tst x0, #~(OVERFLOW_STACK_SIZE - 1) // within range? b.ne __bad_stack // no? -> bad stack pointer /* We were already on the overflow stack. Restore sp/x0 and carry on. */ sub sp, sp, x0 mrs x0, tpidrro_el0 b el\el\ht\()_\regsize\()_\label .org .Lventry_start\@ + 128 // Did we overflow the ventry slot? .endm
	kernel_ventry	0, t, 32, error		// Error 32-bit EL0

EL0의 AArch32 사용자 실행 중 발생한 SError의 128바이트 벡터 입구를 생성합니다. CPU는 벡터 표의 고정 위치를 고르며 이 입구는 pt_regs 공간·스택 검사 후 같은 종류의 실제 handler로 이어집니다. 이 줄에서 SError을 일부러 발생시키는 것은 아닙니다.

538행 그림 · 벡터·핸들러 코드 생성

함수·코드 범위: vectors

매크로 인수
이 명령을 실행하기 직전호출 위치의 인수
이 명령을 실행한 직후0, t, 32, error
생성되는 코드
이 명령을 실행하기 직전어셈블 전
이 명령을 실행한 직후128바이트 슬롯, pt_regs 공간 확보, 스택 초과 검사, 대상 예외 핸들러 분기를 생성합니다. 인자는 EL·SP 모드·실행 폭·예외 종류입니다.

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 이 한 줄은 여러 명령을 생성합니다. 레지스터 변화는 펼쳐진 본문의 각 명령과 선택된 진입 경로에 따라 생깁니다.
kernel_ventry 매크로 본문 · 39행

arch/arm64/kernel/entry.S · Linux v6.18.37

.macro kernel_ventry, el:req, ht:req, regsize:req, label:req .align 7 .Lventry_start\@: .if \el == 0 /* * This must be the first instruction of the EL0 vector entries. It is * skipped by the trampoline vectors, to trigger the cleanup. */ b .Lskip_tramp_vectors_cleanup\@ .if \regsize == 64 mrs x30, tpidrro_el0 msr tpidrro_el0, xzr .else mov x30, xzr .endif .Lskip_tramp_vectors_cleanup\@: .endif sub sp, sp, #PT_REGS_SIZE /* * Test whether the SP has overflowed, without corrupting a GPR. * Task and IRQ stacks are aligned so that SP & (1 << THREAD_SHIFT) * should always be zero. */ add sp, sp, x0 // sp' = sp + x0 sub x0, sp, x0 // x0' = sp' - x0 = (sp + x0) - x0 = sp tbnz x0, #THREAD_SHIFT, 0f sub x0, sp, x0 // x0'' = sp' - x0' = (sp + x0) - sp = x0 sub sp, sp, x0 // sp'' = sp' - x0 = (sp + x0) - x0 = sp b el\el\ht\()_\regsize\()_\label 0: /* * Either we've just detected an overflow, or we've taken an exception * while on the overflow stack. Either way, we won't return to * userspace, and can clobber EL0 registers to free up GPRs. */ /* Stash the original SP (minus PT_REGS_SIZE) in tpidr_el0. */ msr tpidr_el0, x0 /* Recover the original x0 value and stash it in tpidrro_el0 */ sub x0, sp, x0 msr tpidrro_el0, x0 /* Switch to the overflow stack */ adr_this_cpu sp, overflow_stack + OVERFLOW_STACK_SIZE, x0 /* * Check whether we were already on the overflow stack. This may happen * after panic() re-enables interrupts. */ mrs x0, tpidr_el0 // sp of interrupted context sub x0, sp, x0 // delta with top of overflow stack tst x0, #~(OVERFLOW_STACK_SIZE - 1) // within range? b.ne __bad_stack // no? -> bad stack pointer /* We were already on the overflow stack. Restore sp/x0 and carry on. */ sub sp, sp, x0 mrs x0, tpidrro_el0 b el\el\ht\()_\regsize\()_\label .org .Lventry_start\@ + 128 // Did we overflow the ventry slot? .endm
SYM_CODE_END(vectors)

도구에 이 코드 범위의 끝을 알려 크기와 심볼 정보를 기록합니다. CPU가 이 줄 때문에 자동으로 반환하지는 않습니다.

539행 그림 · 어셈블·링크 단계

함수·코드 범위: vectors

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과SYM_CODE_END(vectors)
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 끝 표기 자체가 RET나 분기를 실행하지는 않습니다.
SYM_CODE_START_LOCAL(__bad_stack)

이 위치부터 (__bad_stack)라는 코드가 시작한다고 도구에 알려 줍니다. 정렬과 외부 공개 여부도 정의에 포함됩니다. 실제 작업은 아래의 명령들이 수행합니다.

541행 그림 · 어셈블·링크 단계

함수·코드 범위: __bad_stack

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과SYM_CODE_START_LOCAL(__bad_stack)
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	mrs	x0, tpidrro_el0

정상 스택을 사용할 수 없던 시점에 TPIDRRO_EL0에 맡긴 원래 x0를 읽어 비정상 예외 문맥을 재구성합니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.

549행 그림 · 시스템 레지스터 읽기

함수·코드 범위: __bad_stack

읽는 값TPIDRRO_EL0
x0
이 명령을 실행하기 직전명령 직전 x0의 값
이 명령을 실행한 직후명령 직전 TPIDRRO_EL0의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 일반 RAM의 메모리 접근과 구분합니다.
	sub	sp, sp, #PT_REGS_SIZE

스택에서 PT_REGS_SIZE바이트만큼 사용할 공간을 잡습니다. SP가 낮은 주소로 이동하지만, 아직 이 명령이 데이터를 저장한 것은 아닙니다.

555행 그림 · 레지스터 상태

함수·코드 범위: __bad_stack

읽는 값sp
sp
이 명령을 실행하기 직전명령 직전 sp의 값
이 명령을 실행한 직후명령 직전 sp의 값 − PT_REGS_SIZE

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 결과는 목적 레지스터 폭에 맞습니다. 이 형식은 NZCV를 바꾸지 않습니다.
  • SP는 주소만 바뀝니다. 스택 데이터 저장·복원은 STR/STP·LDR/LDP 같은 별도 메모리 명령이 수행합니다.
	kernel_entry 1

overflow stack에서 EL1 예외 프레임을 완성하여 오류 진단 코드가 레지스터를 안전하게 볼 수 있게 합니다. 손상된 원래 SP는 뒤에서 별도로 덮어써 프레임에 기록합니다.

556행 그림 · 매크로 전체의 주요 상태

함수·코드 범위: __bad_stack

SP의 pt_regs 저장 공간
이 명령을 실행하기 직전기존 내용
이 명령을 실행한 직후진입 때 일반 레지스터와 SP·ELR·SPSR 등 복귀에 필요한 상태
x29
이 명령을 실행하기 직전명령 직전 x29의 값
이 명령을 실행한 직후이 예외 프레임의 S_STACKFRAME 위치
x21
이 명령을 실행하기 직전명령 직전 x21의 값
이 명령을 실행한 직후EL0이면 명령 직전 SP_EL0의 값, EL1이면 pt_regs 확보 전 SP

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • pt_regs 공간은 앞의 kernel_ventry가 확보합니다. 이 매크로는 그 공간에 저장한 뒤 여러 작업 레지스터를 사용합니다. 32비트 진입에서는 저장 전에 w0 쓰기로 x0 상위 비트를 지웁니다.
  • 여러 명령과 조건부 경로를 묶은 그림입니다. 이 목록이 모든 임시 레지스터의 변경을 열거하는 것은 아닙니다. 아래 매크로 본문에서 각 단계를 확인하실 수 있습니다.
kernel_entry 매크로 본문 · 197행

arch/arm64/kernel/entry.S · Linux v6.18.37

.macro kernel_entry, el, regsize = 64 .if \el == 0 alternative_insn nop, SET_PSTATE_DIT(1), ARM64_HAS_DIT .endif .if \regsize == 32 mov w0, w0 // zero upper 32 bits of x0 .endif stp x0, x1, [sp, #16 * 0] stp x2, x3, [sp, #16 * 1] stp x4, x5, [sp, #16 * 2] stp x6, x7, [sp, #16 * 3] stp x8, x9, [sp, #16 * 4] stp x10, x11, [sp, #16 * 5] stp x12, x13, [sp, #16 * 6] stp x14, x15, [sp, #16 * 7] stp x16, x17, [sp, #16 * 8] stp x18, x19, [sp, #16 * 9] stp x20, x21, [sp, #16 * 10] stp x22, x23, [sp, #16 * 11] stp x24, x25, [sp, #16 * 12] stp x26, x27, [sp, #16 * 13] stp x28, x29, [sp, #16 * 14] .if \el == 0 clear_gp_regs mrs x21, sp_el0 ldr_this_cpu tsk, __entry_task, x20 msr sp_el0, tsk /* * Ensure MDSCR_EL1.SS is clear, since we can unmask debug exceptions * when scheduling. */ ldr x19, [tsk, #TSK_TI_FLAGS] disable_step_tsk x19, x20 /* Check for asynchronous tag check faults in user space */ ldr x0, [tsk, THREAD_SCTLR_USER] check_mte_async_tcf x22, x23, x0 #ifdef CONFIG_ARM64_PTR_AUTH alternative_if ARM64_HAS_ADDRESS_AUTH /* * Enable IA for in-kernel PAC if the task had it disabled. Although * this could be implemented with an unconditional MRS which would avoid * a load, this was measured to be slower on Cortex-A75 and Cortex-A76. * * Install the kernel IA key only if IA was enabled in the task. If IA * was disabled on kernel exit then we would have left the kernel IA * installed so there is no need to install it again. */ tbz x0, SCTLR_ELx_ENIA_SHIFT, 1f __ptrauth_keys_install_kernel_nosync tsk, x20, x22, x23 b 2f 1: mrs x0, sctlr_el1 orr x0, x0, SCTLR_ELx_ENIA msr sctlr_el1, x0 2: alternative_else_nop_endif #endif apply_ssbd 1, x22, x23 mte_set_kernel_gcr x22, x23 /* * Any non-self-synchronizing system register updates required for * kernel entry should be placed before this point. */ alternative_if ARM64_MTE isb b 1f alternative_else_nop_endif alternative_if ARM64_HAS_ADDRESS_AUTH isb alternative_else_nop_endif 1: scs_load_current .else add x21, sp, #PT_REGS_SIZE get_current_task tsk .endif /* \el == 0 */ mrs x22, elr_el1 mrs x23, spsr_el1 stp lr, x21, [sp, #S_LR] /* * Create a metadata frame record. The unwinder will use this to * identify and unwind exception boundaries. */ stp xzr, xzr, [sp, #S_STACKFRAME] .if \el == 0 mov x0, #FRAME_META_TYPE_FINAL .else mov x0, #FRAME_META_TYPE_PT_REGS .endif str x0, [sp, #S_STACKFRAME_TYPE] add x29, sp, #S_STACKFRAME #ifdef CONFIG_ARM64_SW_TTBR0_PAN alternative_if_not ARM64_HAS_PAN bl __swpan_entry_el\el alternative_else_nop_endif #endif stp x22, x23, [sp, #S_PC] /* Not in a syscall by default (el0_svc overwrites for real syscall) */ .if \el == 0 mov w21, #NO_SYSCALL str w21, [sp, #S_SYSCALLNO] .endif #ifdef CONFIG_ARM64_PSEUDO_NMI alternative_if_not ARM64_HAS_GIC_PRIO_MASKING b .Lskip_pmr_save\@ alternative_else_nop_endif mrs_s x20, SYS_ICC_PMR_EL1 str w20, [sp, #S_PMR] mov x20, #GIC_PRIO_IRQON | GIC_PRIO_PSR_I_SET msr_s SYS_ICC_PMR_EL1, x20 .Lskip_pmr_save\@: #endif /* * Registers that may be useful after this macro is invoked: * * x20 - ICC_PMR_EL1 * x21 - aborted SP * x22 - aborted PC * x23 - aborted PSTATE */ .endm
	mrs	x0, tpidr_el0

TPIDR_EL0에 맡긴 중단 당시 SP를 읽습니다. 현재 SP는 안전한 overflow stack이므로 둘을 구분해 오류 보고에 원래 값을 남깁니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.

557행 그림 · 시스템 레지스터 읽기

함수·코드 범위: __bad_stack

읽는 값TPIDR_EL0
x0
이 명령을 실행하기 직전명령 직전 x0의 값
이 명령을 실행한 직후명령 직전 TPIDR_EL0의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 일반 RAM의 메모리 접근과 구분합니다.
	add	x0, x0, #PT_REGS_SIZE

x0를 기준으로 PT_REGS_SIZE을 더해 x0에 결과를 둡니다. 주소 계산에 쓰이는 경우에는 구조체의 필드나 다음 저장 위치를 찾는 동작입니다. 이 형태는 조건 플래그를 바꾸지 않습니다.

558행 그림 · 레지스터 상태

함수·코드 범위: __bad_stack

읽는 값x0
x0
이 명령을 실행하기 직전명령 직전 x0의 값
이 명령을 실행한 직후명령 직전 x0의 값 + PT_REGS_SIZE

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 결과는 목적 레지스터 폭에 맞습니다. 이 형식은 NZCV를 바꾸지 않습니다.
	str	x0, [sp, #S_SP]

x0의 값을 sp를 기준으로 S_SP만큼 계산한 메모리 위치에 기록합니다. 레지스터 값을 나중에 다시 쓰거나 다른 코드가 읽을 수 있도록 메모리에 남기는 줄입니다. 이 스택 위치는 현재 함수 또는 예외 처리의 보관 공간입니다.

559행 그림 · 메모리 쓰기

함수·코드 범위: __bad_stack

읽는 값spx0
메모리[이전 sp + (S_SP)] · 8바이트
이 명령을 실행하기 직전기존 메모리 값
이 명령을 실행한 직후명령 직전 x0의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 주소 계산에 의한 기준 레지스터 갱신은 없습니다. 다만 LDR/LDP의 목적 레지스터가 기준 레지스터와 같으면 읽은 값이 그 레지스터를 덮어씁니다.
  • 메모리 칸은 낮은 주소부터 나열했습니다. SP를 줄이면 더 낮은 주소의 공간을 사용합니다. 화면의 아래쪽이 곧 낮은 주소라는 뜻은 아닙니다.
	mov	x0, sp

현재 SP의 숫자를 x0에 기억해 둡니다. 스택 자체는 이동하거나 복사되지 않습니다. 나중에 이 값으로 원래 위치를 찾을 수 있습니다.

562행 그림 · 값 복사

함수·코드 범위: __bad_stack

읽는 값sp
x0
이 명령을 실행하기 직전명령 직전 x0의 값
이 명령을 실행한 직후명령 직전 sp의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 화살표는 값의 변화를 뜻합니다. 주소값을 옮겨도 그 주소의 메모리 내용은 복사하지 않습니다.
	bl	handle_bad_stack

x0의 pt_regs를 handle_bad_stack에 전달해 task 스택 넘침/손상 상태를 보고합니다. 이미 안전한 overflow stack에 옮겼으며 정상적인 사용자 복귀를 기대하는 경로가 아닙니다. BL은 다음 명령의 주소를 LR(x30)에 기록하고 목적지로 분기합니다. 호출된 코드가 x0 같은 작업 레지스터를 바꿀 수 있으므로, 호출 뒤의 값은 그 코드의 반환·보존 규칙에 따릅니다.

565행 그림 · 호출과 실행 위치

함수·코드 범위: __bad_stack

PC
이 명령을 실행하기 직전명령 직전 PC의 값
이 명령을 실행한 직후handle_bad_stack
LR (x30)
이 명령을 실행하기 직전명령 직전 LR의 값 (x30)
이 명령을 실행한 직후이 BL/BLR 다음 명령 주소

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 그림은 분기 명령 자체의 효과입니다. 호출된 함수가 실행된 뒤의 레지스터 값은 그 함수의 반환·보존 규칙을 따라야 합니다.
	ASM_BUG()

handle_bad_stack이 뜻하지 않게 반환해도 손상된 스택의 실행을 이어가지 않도록 BUG 예외를 발생시킵니다. 원래 예외로 정상 복귀하는 지점이 아닙니다. 어셈블 단계에는 진단용 BUG 메타데이터와 BRK 인코딩을 배치하고, 실행이 이 BRK에 도달하면 예외가 발생합니다.

566행 그림 · 매크로 전체의 상태 변화

함수·코드 범위: __bad_stack

PC
이 명령을 실행하기 직전명령 직전 PC의 값
이 명령을 실행한 직후BRK에 해당하는 동기 예외 벡터
ELR_ELx
이 명령을 실행하기 직전명령 직전 ELR_ELx의 값
이 명령을 실행한 직후BRK 명령 주소
SPSR_ELx
이 명령을 실행하기 직전명령 직전 SPSR_ELx의 값
이 명령을 실행한 직후예외 직전 PSTATE

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 오류 메타데이터와 BRK 명령을 함께 만듭니다. HVC와 달리 ELR에는 BRK 자신을 가리키는 주소가 저장됩니다.
SYM_CODE_END(__bad_stack)

도구에 이 코드 범위의 끝을 알려 크기와 심볼 정보를 기록합니다. CPU가 이 줄 때문에 자동으로 반환하지는 않습니다.

567행 그림 · 어셈블·링크 단계

함수·코드 범위: __bad_stack

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과SYM_CODE_END(__bad_stack)
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 끝 표기 자체가 RET나 분기를 실행하지는 않습니다.

C 핸들러와 사용자·커널 복귀

원본 568–619행

	.macro entry_handler el:req, ht:req, regsize:req, label:req

벡터별 처리 본문을 만드는 매크로입니다. 예외 문맥을 저장하고 x0=pt_regs로 해당 C handler를 호출한 뒤, 중단된 EL에 맞춰 ret_to_user 또는 ret_to_kernel로 연결합니다. 이 줄은 어셈블러 매크로 정의의 시작입니다. 본문은 매크로를 사용하는 위치에 펼쳐지며, 정의 자체가 런타임 함수 호출은 아닙니다.

570행 그림 · 어셈블·링크 단계

매크로 본문: entry_handler

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과.macro entry_handler el:req, ht:req, regsize:req, label:req
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 뒤의 본문은 이 이름을 사용하는 위치에 펼쳐집니다. 정의 위치를 CPU가 함수처럼 호출하지 않습니다.
SYM_CODE_START_LOCAL(el\el\ht\()_\regsize\()_\label)

이 위치부터 (el\el\ht\()_\regsize\()_\label)라는 코드가 시작한다고 도구에 알려 줍니다. 정렬과 외부 공개 여부도 정의에 포함됩니다. 실제 작업은 아래의 명령들이 수행합니다.

571행 그림 · 어셈블·링크 단계

매크로 본문: entry_handler

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과SYM_CODE_START_LOCAL(el\el\ht\()_\regsize\()_\label)
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	kernel_entry \el, \regsize

중단된 프로그램의 상태를 스택에 보관하고 커널이 사용할 현재 태스크와 제어 상태를 준비합니다. 이후 C 함수가 레지스터를 써도 복귀 기록은 남습니다. 여기의 인자는 \el, \regsize입니다.

572행 그림 · 매크로 전체의 주요 상태

매크로 본문: entry_handler

SP의 pt_regs 저장 공간
이 명령을 실행하기 직전기존 내용
이 명령을 실행한 직후진입 때 일반 레지스터와 SP·ELR·SPSR 등 복귀에 필요한 상태
x29
이 명령을 실행하기 직전명령 직전 x29의 값
이 명령을 실행한 직후이 예외 프레임의 S_STACKFRAME 위치
x21
이 명령을 실행하기 직전명령 직전 x21의 값
이 명령을 실행한 직후EL0이면 명령 직전 SP_EL0의 값, EL1이면 pt_regs 확보 전 SP

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • pt_regs 공간은 앞의 kernel_ventry가 확보합니다. 이 매크로는 그 공간에 저장한 뒤 여러 작업 레지스터를 사용합니다. 32비트 진입에서는 저장 전에 w0 쓰기로 x0 상위 비트를 지웁니다.
  • 여러 명령과 조건부 경로를 묶은 그림입니다. 이 목록이 모든 임시 레지스터의 변경을 열거하는 것은 아닙니다. 아래 매크로 본문에서 각 단계를 확인하실 수 있습니다.
  • 매크로 entry_handler의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
kernel_entry 매크로 본문 · 197행

arch/arm64/kernel/entry.S · Linux v6.18.37

.macro kernel_entry, el, regsize = 64 .if \el == 0 alternative_insn nop, SET_PSTATE_DIT(1), ARM64_HAS_DIT .endif .if \regsize == 32 mov w0, w0 // zero upper 32 bits of x0 .endif stp x0, x1, [sp, #16 * 0] stp x2, x3, [sp, #16 * 1] stp x4, x5, [sp, #16 * 2] stp x6, x7, [sp, #16 * 3] stp x8, x9, [sp, #16 * 4] stp x10, x11, [sp, #16 * 5] stp x12, x13, [sp, #16 * 6] stp x14, x15, [sp, #16 * 7] stp x16, x17, [sp, #16 * 8] stp x18, x19, [sp, #16 * 9] stp x20, x21, [sp, #16 * 10] stp x22, x23, [sp, #16 * 11] stp x24, x25, [sp, #16 * 12] stp x26, x27, [sp, #16 * 13] stp x28, x29, [sp, #16 * 14] .if \el == 0 clear_gp_regs mrs x21, sp_el0 ldr_this_cpu tsk, __entry_task, x20 msr sp_el0, tsk /* * Ensure MDSCR_EL1.SS is clear, since we can unmask debug exceptions * when scheduling. */ ldr x19, [tsk, #TSK_TI_FLAGS] disable_step_tsk x19, x20 /* Check for asynchronous tag check faults in user space */ ldr x0, [tsk, THREAD_SCTLR_USER] check_mte_async_tcf x22, x23, x0 #ifdef CONFIG_ARM64_PTR_AUTH alternative_if ARM64_HAS_ADDRESS_AUTH /* * Enable IA for in-kernel PAC if the task had it disabled. Although * this could be implemented with an unconditional MRS which would avoid * a load, this was measured to be slower on Cortex-A75 and Cortex-A76. * * Install the kernel IA key only if IA was enabled in the task. If IA * was disabled on kernel exit then we would have left the kernel IA * installed so there is no need to install it again. */ tbz x0, SCTLR_ELx_ENIA_SHIFT, 1f __ptrauth_keys_install_kernel_nosync tsk, x20, x22, x23 b 2f 1: mrs x0, sctlr_el1 orr x0, x0, SCTLR_ELx_ENIA msr sctlr_el1, x0 2: alternative_else_nop_endif #endif apply_ssbd 1, x22, x23 mte_set_kernel_gcr x22, x23 /* * Any non-self-synchronizing system register updates required for * kernel entry should be placed before this point. */ alternative_if ARM64_MTE isb b 1f alternative_else_nop_endif alternative_if ARM64_HAS_ADDRESS_AUTH isb alternative_else_nop_endif 1: scs_load_current .else add x21, sp, #PT_REGS_SIZE get_current_task tsk .endif /* \el == 0 */ mrs x22, elr_el1 mrs x23, spsr_el1 stp lr, x21, [sp, #S_LR] /* * Create a metadata frame record. The unwinder will use this to * identify and unwind exception boundaries. */ stp xzr, xzr, [sp, #S_STACKFRAME] .if \el == 0 mov x0, #FRAME_META_TYPE_FINAL .else mov x0, #FRAME_META_TYPE_PT_REGS .endif str x0, [sp, #S_STACKFRAME_TYPE] add x29, sp, #S_STACKFRAME #ifdef CONFIG_ARM64_SW_TTBR0_PAN alternative_if_not ARM64_HAS_PAN bl __swpan_entry_el\el alternative_else_nop_endif #endif stp x22, x23, [sp, #S_PC] /* Not in a syscall by default (el0_svc overwrites for real syscall) */ .if \el == 0 mov w21, #NO_SYSCALL str w21, [sp, #S_SYSCALLNO] .endif #ifdef CONFIG_ARM64_PSEUDO_NMI alternative_if_not ARM64_HAS_GIC_PRIO_MASKING b .Lskip_pmr_save\@ alternative_else_nop_endif mrs_s x20, SYS_ICC_PMR_EL1 str w20, [sp, #S_PMR] mov x20, #GIC_PRIO_IRQON | GIC_PRIO_PSR_I_SET msr_s SYS_ICC_PMR_EL1, x20 .Lskip_pmr_save\@: #endif /* * Registers that may be useful after this macro is invoked: * * x20 - ICC_PMR_EL1 * x21 - aborted SP * x22 - aborted PC * x23 - aborted PSTATE */ .endm
	mov	x0, sp

현재 SP의 숫자를 x0에 기억해 둡니다. 스택 자체는 이동하거나 복사되지 않습니다. 나중에 이 값으로 원래 위치를 찾을 수 있습니다.

C 예외 핸들러의 첫 인자로 pt_regs 포인터를 전달합니다.

573행 그림 · 값 복사

매크로 본문: entry_handler

읽는 값sp
x0
이 명령을 실행하기 직전명령 직전 x0의 값
이 명령을 실행한 직후명령 직전 sp의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 화살표는 값의 변화를 뜻합니다. 주소값을 옮겨도 그 주소의 메모리 내용은 복사하지 않습니다.
  • 매크로 entry_handler의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	bl	el\el\ht\()_\regsize\()_\label\()_handler

el·ht·regsize·label로 조립한 예외 종류별 C handler를 호출합니다. x0는 완성된 pt_regs이며, 예를 들어 EL0 AArch64 sync는 ESR을 해석해 SVC·fault 등의 실제 처리로 분기합니다. BL은 다음 명령의 주소를 LR(x30)에 기록하고 목적지로 분기합니다. 호출된 코드가 x0 같은 작업 레지스터를 바꿀 수 있으므로, 호출 뒤의 값은 그 코드의 반환·보존 규칙에 따릅니다.

574행 그림 · 호출과 실행 위치

매크로 본문: entry_handler

PC
이 명령을 실행하기 직전명령 직전 PC의 값
이 명령을 실행한 직후el\el\ht\()_\regsize\()_\label\()_handler
LR (x30)
이 명령을 실행하기 직전명령 직전 LR의 값 (x30)
이 명령을 실행한 직후이 BL/BLR 다음 명령 주소

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 그림은 분기 명령 자체의 효과입니다. 호출된 함수가 실행된 뒤의 레지스터 값은 그 함수의 반환·보존 규칙을 따라야 합니다.
  • 매크로 entry_handler의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	.if \el == 0

어셈블 단계에서 인자나 식을 검사해 필요한 명령만 만듭니다. \el == 0

575행 그림 · 어셈블·링크 단계

매크로 본문: entry_handler

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과.if \el == 0
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	b	ret_to_user

ret_to_user부터 실행을 계속합니다. 돌아올 주소를 새로 적지 않으므로 일반 함수 호출과는 다른 제어 이동입니다.

576행 그림 · 호출과 실행 위치

매크로 본문: entry_handler

이 줄을 포함하는 조건: .if \el == 0

PC
이 명령을 실행하기 직전명령 직전 PC의 값
이 명령을 실행한 직후ret_to_user

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 새 복귀 주소를 LR에 기록하지 않습니다. RET 역시 메모리의 스택 값을 자동으로 꺼내는 명령은 아닙니다.
  • 매크로 entry_handler의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	.else

앞 어셈블 조건의 반대 경우에 생성할 코드입니다.

577행 그림 · 어셈블·링크 단계

매크로 본문: entry_handler

이 줄을 포함하는 조건: .if \el == 0

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과.else
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	b	ret_to_kernel

ret_to_kernel부터 실행을 계속합니다. 돌아올 주소를 새로 적지 않으므로 일반 함수 호출과는 다른 제어 이동입니다.

578행 그림 · 호출과 실행 위치

매크로 본문: entry_handler

이 줄을 포함하는 조건: 앞 어셈블 조건의 반대

PC
이 명령을 실행하기 직전명령 직전 PC의 값
이 명령을 실행한 직후ret_to_kernel

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 새 복귀 주소를 LR에 기록하지 않습니다. RET 역시 메모리의 스택 값을 자동으로 꺼내는 명령은 아닙니다.
  • 매크로 entry_handler의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	.endif

어셈블 조건에 따라 생성할 코드를 나누던 범위가 끝납니다.

579행 그림 · 어셈블·링크 단계

매크로 본문: entry_handler

이 줄을 포함하는 조건: 앞 어셈블 조건의 반대

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과.endif
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

SYM_CODE_END(el\el\ht\()_\regsize\()_\label)

도구에 이 코드 범위의 끝을 알려 크기와 심볼 정보를 기록합니다. CPU가 이 줄 때문에 자동으로 반환하지는 않습니다.

580행 그림 · 어셈블·링크 단계

매크로 본문: entry_handler

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과SYM_CODE_END(el\el\ht\()_\regsize\()_\label)
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 끝 표기 자체가 RET나 분기를 실행하지는 않습니다.
	.endm

공통 작업의 정의를 여기서 마칩니다. RET가 아니라 빌드 도구가 읽는 경계입니다.

581행 그림 · 어셈블·링크 단계

매크로 본문: entry_handler

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과.endm
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 끝 표기 자체가 RET나 분기를 실행하지는 않습니다.
	entry_handler	1, t, 64, sync

EL1t 커널 실행(SP_EL0 선택)에서 온 동기 예외(SVC·fault·debug 등)의 저장·C 호출·복귀 본문을 생성합니다. kernel_entry로 원래 상태를 보존하고 x0=pt_regs를 해당 C handler에 전달한 뒤 ret_to_kernel로 이어집니다.

586행 그림 · 벡터·핸들러 코드 생성
매크로 인수
이 명령을 실행하기 직전호출 위치의 인수
이 명령을 실행한 직후1, t, 64, sync
생성되는 코드
이 명령을 실행하기 직전어셈블 전
이 명령을 실행한 직후kernel_entry 뒤 SP를 x0로 전달해 해당 C handler를 부르고 ret_to_user 또는 ret_to_kernel로 분기합니다.

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 이 한 줄은 여러 명령을 생성합니다. 레지스터 변화는 펼쳐진 본문의 각 명령과 선택된 진입 경로에 따라 생깁니다.
entry_handler 매크로 본문 · 570행

arch/arm64/kernel/entry.S · Linux v6.18.37

.macro entry_handler el:req, ht:req, regsize:req, label:req SYM_CODE_START_LOCAL(el\el\ht\()_\regsize\()_\label) kernel_entry \el, \regsize mov x0, sp bl el\el\ht\()_\regsize\()_\label\()_handler .if \el == 0 b ret_to_user .else b ret_to_kernel .endif SYM_CODE_END(el\el\ht\()_\regsize\()_\label) .endm
	entry_handler	1, t, 64, irq

EL1t 커널 실행(SP_EL0 선택)에서 온 IRQ의 저장·C 호출·복귀 본문을 생성합니다. kernel_entry로 원래 상태를 보존하고 x0=pt_regs를 해당 C handler에 전달한 뒤 ret_to_kernel로 이어집니다.

587행 그림 · 벡터·핸들러 코드 생성
매크로 인수
이 명령을 실행하기 직전호출 위치의 인수
이 명령을 실행한 직후1, t, 64, irq
생성되는 코드
이 명령을 실행하기 직전어셈블 전
이 명령을 실행한 직후kernel_entry 뒤 SP를 x0로 전달해 해당 C handler를 부르고 ret_to_user 또는 ret_to_kernel로 분기합니다.

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 이 한 줄은 여러 명령을 생성합니다. 레지스터 변화는 펼쳐진 본문의 각 명령과 선택된 진입 경로에 따라 생깁니다.
entry_handler 매크로 본문 · 570행

arch/arm64/kernel/entry.S · Linux v6.18.37

.macro entry_handler el:req, ht:req, regsize:req, label:req SYM_CODE_START_LOCAL(el\el\ht\()_\regsize\()_\label) kernel_entry \el, \regsize mov x0, sp bl el\el\ht\()_\regsize\()_\label\()_handler .if \el == 0 b ret_to_user .else b ret_to_kernel .endif SYM_CODE_END(el\el\ht\()_\regsize\()_\label) .endm
	entry_handler	1, t, 64, fiq

EL1t 커널 실행(SP_EL0 선택)에서 온 FIQ의 저장·C 호출·복귀 본문을 생성합니다. kernel_entry로 원래 상태를 보존하고 x0=pt_regs를 해당 C handler에 전달한 뒤 ret_to_kernel로 이어집니다.

588행 그림 · 벡터·핸들러 코드 생성
매크로 인수
이 명령을 실행하기 직전호출 위치의 인수
이 명령을 실행한 직후1, t, 64, fiq
생성되는 코드
이 명령을 실행하기 직전어셈블 전
이 명령을 실행한 직후kernel_entry 뒤 SP를 x0로 전달해 해당 C handler를 부르고 ret_to_user 또는 ret_to_kernel로 분기합니다.

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 이 한 줄은 여러 명령을 생성합니다. 레지스터 변화는 펼쳐진 본문의 각 명령과 선택된 진입 경로에 따라 생깁니다.
entry_handler 매크로 본문 · 570행

arch/arm64/kernel/entry.S · Linux v6.18.37

.macro entry_handler el:req, ht:req, regsize:req, label:req SYM_CODE_START_LOCAL(el\el\ht\()_\regsize\()_\label) kernel_entry \el, \regsize mov x0, sp bl el\el\ht\()_\regsize\()_\label\()_handler .if \el == 0 b ret_to_user .else b ret_to_kernel .endif SYM_CODE_END(el\el\ht\()_\regsize\()_\label) .endm
	entry_handler	1, t, 64, error

EL1t 커널 실행(SP_EL0 선택)에서 온 SError의 저장·C 호출·복귀 본문을 생성합니다. kernel_entry로 원래 상태를 보존하고 x0=pt_regs를 해당 C handler에 전달한 뒤 ret_to_kernel로 이어집니다.

589행 그림 · 벡터·핸들러 코드 생성
매크로 인수
이 명령을 실행하기 직전호출 위치의 인수
이 명령을 실행한 직후1, t, 64, error
생성되는 코드
이 명령을 실행하기 직전어셈블 전
이 명령을 실행한 직후kernel_entry 뒤 SP를 x0로 전달해 해당 C handler를 부르고 ret_to_user 또는 ret_to_kernel로 분기합니다.

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 이 한 줄은 여러 명령을 생성합니다. 레지스터 변화는 펼쳐진 본문의 각 명령과 선택된 진입 경로에 따라 생깁니다.
entry_handler 매크로 본문 · 570행

arch/arm64/kernel/entry.S · Linux v6.18.37

.macro entry_handler el:req, ht:req, regsize:req, label:req SYM_CODE_START_LOCAL(el\el\ht\()_\regsize\()_\label) kernel_entry \el, \regsize mov x0, sp bl el\el\ht\()_\regsize\()_\label\()_handler .if \el == 0 b ret_to_user .else b ret_to_kernel .endif SYM_CODE_END(el\el\ht\()_\regsize\()_\label) .endm
	entry_handler	1, h, 64, sync

EL1h 커널 실행(SP_EL1 선택)에서 온 동기 예외(SVC·fault·debug 등)의 저장·C 호출·복귀 본문을 생성합니다. kernel_entry로 원래 상태를 보존하고 x0=pt_regs를 해당 C handler에 전달한 뒤 ret_to_kernel로 이어집니다.

591행 그림 · 벡터·핸들러 코드 생성
매크로 인수
이 명령을 실행하기 직전호출 위치의 인수
이 명령을 실행한 직후1, h, 64, sync
생성되는 코드
이 명령을 실행하기 직전어셈블 전
이 명령을 실행한 직후kernel_entry 뒤 SP를 x0로 전달해 해당 C handler를 부르고 ret_to_user 또는 ret_to_kernel로 분기합니다.

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 이 한 줄은 여러 명령을 생성합니다. 레지스터 변화는 펼쳐진 본문의 각 명령과 선택된 진입 경로에 따라 생깁니다.
entry_handler 매크로 본문 · 570행

arch/arm64/kernel/entry.S · Linux v6.18.37

.macro entry_handler el:req, ht:req, regsize:req, label:req SYM_CODE_START_LOCAL(el\el\ht\()_\regsize\()_\label) kernel_entry \el, \regsize mov x0, sp bl el\el\ht\()_\regsize\()_\label\()_handler .if \el == 0 b ret_to_user .else b ret_to_kernel .endif SYM_CODE_END(el\el\ht\()_\regsize\()_\label) .endm
	entry_handler	1, h, 64, irq

EL1h 커널 실행(SP_EL1 선택)에서 온 IRQ의 저장·C 호출·복귀 본문을 생성합니다. kernel_entry로 원래 상태를 보존하고 x0=pt_regs를 해당 C handler에 전달한 뒤 ret_to_kernel로 이어집니다.

592행 그림 · 벡터·핸들러 코드 생성
매크로 인수
이 명령을 실행하기 직전호출 위치의 인수
이 명령을 실행한 직후1, h, 64, irq
생성되는 코드
이 명령을 실행하기 직전어셈블 전
이 명령을 실행한 직후kernel_entry 뒤 SP를 x0로 전달해 해당 C handler를 부르고 ret_to_user 또는 ret_to_kernel로 분기합니다.

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 이 한 줄은 여러 명령을 생성합니다. 레지스터 변화는 펼쳐진 본문의 각 명령과 선택된 진입 경로에 따라 생깁니다.
entry_handler 매크로 본문 · 570행

arch/arm64/kernel/entry.S · Linux v6.18.37

.macro entry_handler el:req, ht:req, regsize:req, label:req SYM_CODE_START_LOCAL(el\el\ht\()_\regsize\()_\label) kernel_entry \el, \regsize mov x0, sp bl el\el\ht\()_\regsize\()_\label\()_handler .if \el == 0 b ret_to_user .else b ret_to_kernel .endif SYM_CODE_END(el\el\ht\()_\regsize\()_\label) .endm
	entry_handler	1, h, 64, fiq

EL1h 커널 실행(SP_EL1 선택)에서 온 FIQ의 저장·C 호출·복귀 본문을 생성합니다. kernel_entry로 원래 상태를 보존하고 x0=pt_regs를 해당 C handler에 전달한 뒤 ret_to_kernel로 이어집니다.

593행 그림 · 벡터·핸들러 코드 생성
매크로 인수
이 명령을 실행하기 직전호출 위치의 인수
이 명령을 실행한 직후1, h, 64, fiq
생성되는 코드
이 명령을 실행하기 직전어셈블 전
이 명령을 실행한 직후kernel_entry 뒤 SP를 x0로 전달해 해당 C handler를 부르고 ret_to_user 또는 ret_to_kernel로 분기합니다.

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 이 한 줄은 여러 명령을 생성합니다. 레지스터 변화는 펼쳐진 본문의 각 명령과 선택된 진입 경로에 따라 생깁니다.
entry_handler 매크로 본문 · 570행

arch/arm64/kernel/entry.S · Linux v6.18.37

.macro entry_handler el:req, ht:req, regsize:req, label:req SYM_CODE_START_LOCAL(el\el\ht\()_\regsize\()_\label) kernel_entry \el, \regsize mov x0, sp bl el\el\ht\()_\regsize\()_\label\()_handler .if \el == 0 b ret_to_user .else b ret_to_kernel .endif SYM_CODE_END(el\el\ht\()_\regsize\()_\label) .endm
	entry_handler	1, h, 64, error

EL1h 커널 실행(SP_EL1 선택)에서 온 SError의 저장·C 호출·복귀 본문을 생성합니다. kernel_entry로 원래 상태를 보존하고 x0=pt_regs를 해당 C handler에 전달한 뒤 ret_to_kernel로 이어집니다.

594행 그림 · 벡터·핸들러 코드 생성
매크로 인수
이 명령을 실행하기 직전호출 위치의 인수
이 명령을 실행한 직후1, h, 64, error
생성되는 코드
이 명령을 실행하기 직전어셈블 전
이 명령을 실행한 직후kernel_entry 뒤 SP를 x0로 전달해 해당 C handler를 부르고 ret_to_user 또는 ret_to_kernel로 분기합니다.

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 이 한 줄은 여러 명령을 생성합니다. 레지스터 변화는 펼쳐진 본문의 각 명령과 선택된 진입 경로에 따라 생깁니다.
entry_handler 매크로 본문 · 570행

arch/arm64/kernel/entry.S · Linux v6.18.37

.macro entry_handler el:req, ht:req, regsize:req, label:req SYM_CODE_START_LOCAL(el\el\ht\()_\regsize\()_\label) kernel_entry \el, \regsize mov x0, sp bl el\el\ht\()_\regsize\()_\label\()_handler .if \el == 0 b ret_to_user .else b ret_to_kernel .endif SYM_CODE_END(el\el\ht\()_\regsize\()_\label) .endm
	entry_handler	0, t, 64, sync

EL0의 AArch64 사용자 실행에서 온 동기 예외(SVC·fault·debug 등)의 저장·C 호출·복귀 본문을 생성합니다. kernel_entry로 원래 상태를 보존하고 x0=pt_regs를 해당 C handler에 전달한 뒤 ret_to_user로 이어집니다.

596행 그림 · 벡터·핸들러 코드 생성
매크로 인수
이 명령을 실행하기 직전호출 위치의 인수
이 명령을 실행한 직후0, t, 64, sync
생성되는 코드
이 명령을 실행하기 직전어셈블 전
이 명령을 실행한 직후kernel_entry 뒤 SP를 x0로 전달해 해당 C handler를 부르고 ret_to_user 또는 ret_to_kernel로 분기합니다.

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 이 한 줄은 여러 명령을 생성합니다. 레지스터 변화는 펼쳐진 본문의 각 명령과 선택된 진입 경로에 따라 생깁니다.
entry_handler 매크로 본문 · 570행

arch/arm64/kernel/entry.S · Linux v6.18.37

.macro entry_handler el:req, ht:req, regsize:req, label:req SYM_CODE_START_LOCAL(el\el\ht\()_\regsize\()_\label) kernel_entry \el, \regsize mov x0, sp bl el\el\ht\()_\regsize\()_\label\()_handler .if \el == 0 b ret_to_user .else b ret_to_kernel .endif SYM_CODE_END(el\el\ht\()_\regsize\()_\label) .endm
	entry_handler	0, t, 64, irq

EL0의 AArch64 사용자 실행에서 온 IRQ의 저장·C 호출·복귀 본문을 생성합니다. kernel_entry로 원래 상태를 보존하고 x0=pt_regs를 해당 C handler에 전달한 뒤 ret_to_user로 이어집니다.

597행 그림 · 벡터·핸들러 코드 생성
매크로 인수
이 명령을 실행하기 직전호출 위치의 인수
이 명령을 실행한 직후0, t, 64, irq
생성되는 코드
이 명령을 실행하기 직전어셈블 전
이 명령을 실행한 직후kernel_entry 뒤 SP를 x0로 전달해 해당 C handler를 부르고 ret_to_user 또는 ret_to_kernel로 분기합니다.

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 이 한 줄은 여러 명령을 생성합니다. 레지스터 변화는 펼쳐진 본문의 각 명령과 선택된 진입 경로에 따라 생깁니다.
entry_handler 매크로 본문 · 570행

arch/arm64/kernel/entry.S · Linux v6.18.37

.macro entry_handler el:req, ht:req, regsize:req, label:req SYM_CODE_START_LOCAL(el\el\ht\()_\regsize\()_\label) kernel_entry \el, \regsize mov x0, sp bl el\el\ht\()_\regsize\()_\label\()_handler .if \el == 0 b ret_to_user .else b ret_to_kernel .endif SYM_CODE_END(el\el\ht\()_\regsize\()_\label) .endm
	entry_handler	0, t, 64, fiq

EL0의 AArch64 사용자 실행에서 온 FIQ의 저장·C 호출·복귀 본문을 생성합니다. kernel_entry로 원래 상태를 보존하고 x0=pt_regs를 해당 C handler에 전달한 뒤 ret_to_user로 이어집니다.

598행 그림 · 벡터·핸들러 코드 생성
매크로 인수
이 명령을 실행하기 직전호출 위치의 인수
이 명령을 실행한 직후0, t, 64, fiq
생성되는 코드
이 명령을 실행하기 직전어셈블 전
이 명령을 실행한 직후kernel_entry 뒤 SP를 x0로 전달해 해당 C handler를 부르고 ret_to_user 또는 ret_to_kernel로 분기합니다.

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 이 한 줄은 여러 명령을 생성합니다. 레지스터 변화는 펼쳐진 본문의 각 명령과 선택된 진입 경로에 따라 생깁니다.
entry_handler 매크로 본문 · 570행

arch/arm64/kernel/entry.S · Linux v6.18.37

.macro entry_handler el:req, ht:req, regsize:req, label:req SYM_CODE_START_LOCAL(el\el\ht\()_\regsize\()_\label) kernel_entry \el, \regsize mov x0, sp bl el\el\ht\()_\regsize\()_\label\()_handler .if \el == 0 b ret_to_user .else b ret_to_kernel .endif SYM_CODE_END(el\el\ht\()_\regsize\()_\label) .endm
	entry_handler	0, t, 64, error

EL0의 AArch64 사용자 실행에서 온 SError의 저장·C 호출·복귀 본문을 생성합니다. kernel_entry로 원래 상태를 보존하고 x0=pt_regs를 해당 C handler에 전달한 뒤 ret_to_user로 이어집니다.

599행 그림 · 벡터·핸들러 코드 생성
매크로 인수
이 명령을 실행하기 직전호출 위치의 인수
이 명령을 실행한 직후0, t, 64, error
생성되는 코드
이 명령을 실행하기 직전어셈블 전
이 명령을 실행한 직후kernel_entry 뒤 SP를 x0로 전달해 해당 C handler를 부르고 ret_to_user 또는 ret_to_kernel로 분기합니다.

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 이 한 줄은 여러 명령을 생성합니다. 레지스터 변화는 펼쳐진 본문의 각 명령과 선택된 진입 경로에 따라 생깁니다.
entry_handler 매크로 본문 · 570행

arch/arm64/kernel/entry.S · Linux v6.18.37

.macro entry_handler el:req, ht:req, regsize:req, label:req SYM_CODE_START_LOCAL(el\el\ht\()_\regsize\()_\label) kernel_entry \el, \regsize mov x0, sp bl el\el\ht\()_\regsize\()_\label\()_handler .if \el == 0 b ret_to_user .else b ret_to_kernel .endif SYM_CODE_END(el\el\ht\()_\regsize\()_\label) .endm
	entry_handler	0, t, 32, sync

EL0의 AArch32 사용자 실행에서 온 동기 예외(SVC·fault·debug 등)의 저장·C 호출·복귀 본문을 생성합니다. kernel_entry로 원래 상태를 보존하고 x0=pt_regs를 해당 C handler에 전달한 뒤 ret_to_user로 이어집니다.

601행 그림 · 벡터·핸들러 코드 생성
매크로 인수
이 명령을 실행하기 직전호출 위치의 인수
이 명령을 실행한 직후0, t, 32, sync
생성되는 코드
이 명령을 실행하기 직전어셈블 전
이 명령을 실행한 직후kernel_entry 뒤 SP를 x0로 전달해 해당 C handler를 부르고 ret_to_user 또는 ret_to_kernel로 분기합니다.

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 이 한 줄은 여러 명령을 생성합니다. 레지스터 변화는 펼쳐진 본문의 각 명령과 선택된 진입 경로에 따라 생깁니다.
entry_handler 매크로 본문 · 570행

arch/arm64/kernel/entry.S · Linux v6.18.37

.macro entry_handler el:req, ht:req, regsize:req, label:req SYM_CODE_START_LOCAL(el\el\ht\()_\regsize\()_\label) kernel_entry \el, \regsize mov x0, sp bl el\el\ht\()_\regsize\()_\label\()_handler .if \el == 0 b ret_to_user .else b ret_to_kernel .endif SYM_CODE_END(el\el\ht\()_\regsize\()_\label) .endm
	entry_handler	0, t, 32, irq

EL0의 AArch32 사용자 실행에서 온 IRQ의 저장·C 호출·복귀 본문을 생성합니다. kernel_entry로 원래 상태를 보존하고 x0=pt_regs를 해당 C handler에 전달한 뒤 ret_to_user로 이어집니다.

602행 그림 · 벡터·핸들러 코드 생성
매크로 인수
이 명령을 실행하기 직전호출 위치의 인수
이 명령을 실행한 직후0, t, 32, irq
생성되는 코드
이 명령을 실행하기 직전어셈블 전
이 명령을 실행한 직후kernel_entry 뒤 SP를 x0로 전달해 해당 C handler를 부르고 ret_to_user 또는 ret_to_kernel로 분기합니다.

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 이 한 줄은 여러 명령을 생성합니다. 레지스터 변화는 펼쳐진 본문의 각 명령과 선택된 진입 경로에 따라 생깁니다.
entry_handler 매크로 본문 · 570행

arch/arm64/kernel/entry.S · Linux v6.18.37

.macro entry_handler el:req, ht:req, regsize:req, label:req SYM_CODE_START_LOCAL(el\el\ht\()_\regsize\()_\label) kernel_entry \el, \regsize mov x0, sp bl el\el\ht\()_\regsize\()_\label\()_handler .if \el == 0 b ret_to_user .else b ret_to_kernel .endif SYM_CODE_END(el\el\ht\()_\regsize\()_\label) .endm
	entry_handler	0, t, 32, fiq

EL0의 AArch32 사용자 실행에서 온 FIQ의 저장·C 호출·복귀 본문을 생성합니다. kernel_entry로 원래 상태를 보존하고 x0=pt_regs를 해당 C handler에 전달한 뒤 ret_to_user로 이어집니다.

603행 그림 · 벡터·핸들러 코드 생성
매크로 인수
이 명령을 실행하기 직전호출 위치의 인수
이 명령을 실행한 직후0, t, 32, fiq
생성되는 코드
이 명령을 실행하기 직전어셈블 전
이 명령을 실행한 직후kernel_entry 뒤 SP를 x0로 전달해 해당 C handler를 부르고 ret_to_user 또는 ret_to_kernel로 분기합니다.

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 이 한 줄은 여러 명령을 생성합니다. 레지스터 변화는 펼쳐진 본문의 각 명령과 선택된 진입 경로에 따라 생깁니다.
entry_handler 매크로 본문 · 570행

arch/arm64/kernel/entry.S · Linux v6.18.37

.macro entry_handler el:req, ht:req, regsize:req, label:req SYM_CODE_START_LOCAL(el\el\ht\()_\regsize\()_\label) kernel_entry \el, \regsize mov x0, sp bl el\el\ht\()_\regsize\()_\label\()_handler .if \el == 0 b ret_to_user .else b ret_to_kernel .endif SYM_CODE_END(el\el\ht\()_\regsize\()_\label) .endm
	entry_handler	0, t, 32, error

EL0의 AArch32 사용자 실행에서 온 SError의 저장·C 호출·복귀 본문을 생성합니다. kernel_entry로 원래 상태를 보존하고 x0=pt_regs를 해당 C handler에 전달한 뒤 ret_to_user로 이어집니다.

604행 그림 · 벡터·핸들러 코드 생성
매크로 인수
이 명령을 실행하기 직전호출 위치의 인수
이 명령을 실행한 직후0, t, 32, error
생성되는 코드
이 명령을 실행하기 직전어셈블 전
이 명령을 실행한 직후kernel_entry 뒤 SP를 x0로 전달해 해당 C handler를 부르고 ret_to_user 또는 ret_to_kernel로 분기합니다.

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 이 한 줄은 여러 명령을 생성합니다. 레지스터 변화는 펼쳐진 본문의 각 명령과 선택된 진입 경로에 따라 생깁니다.
entry_handler 매크로 본문 · 570행

arch/arm64/kernel/entry.S · Linux v6.18.37

.macro entry_handler el:req, ht:req, regsize:req, label:req SYM_CODE_START_LOCAL(el\el\ht\()_\regsize\()_\label) kernel_entry \el, \regsize mov x0, sp bl el\el\ht\()_\regsize\()_\label\()_handler .if \el == 0 b ret_to_user .else b ret_to_kernel .endif SYM_CODE_END(el\el\ht\()_\regsize\()_\label) .endm
SYM_CODE_START_LOCAL(ret_to_kernel)

이 위치부터 (ret_to_kernel)라는 코드가 시작한다고 도구에 알려 줍니다. 정렬과 외부 공개 여부도 정의에 포함됩니다. 실제 작업은 아래의 명령들이 수행합니다.

606행 그림 · 어셈블·링크 단계

함수·코드 범위: ret_to_kernel

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과SYM_CODE_START_LOCAL(ret_to_kernel)
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	kernel_exit 1

스택의 기록을 CPU로 되돌리고 원래 실행을 재개합니다. 사용자에게 돌아가는 경우에는 사용자용 접근 제어와 매핑도 맞춥니다. 여기의 인자는 1입니다.

607행 그림 · 매크로 전체의 주요 상태

함수·코드 범위: ret_to_kernel

x0~x30
이 명령을 실행하기 직전명령 직전 x0의 값~x30
이 명령을 실행한 직후pt_regs에 저장된 복귀 값
PC
이 명령을 실행하기 직전명령 직전 PC의 값
이 명령을 실행한 직후pt_regs의 S_PC에 저장된 복귀 주소
PSTATE
이 명령을 실행하기 직전명령 직전 PSTATE의 값
이 명령을 실행한 직후pt_regs의 S_PSTATE에 저장된 복귀 상태
SP
이 명령을 실행하기 직전명령 직전 SP의 값
이 명령을 실행한 직후예외 프레임을 해제한 SP; EL0 복귀의 사용자 SP는 S_SP에서 복원

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • KPTI 사용 시 일부 복원과 ERET는 trampoline에서 마칩니다. 그림은 선택한 복귀 경로가 완료되는 시점의 주요 상태입니다. MTE·PAC·SCS 등 구성별 작업은 본문에 있습니다.
  • 여러 명령과 조건부 경로를 묶은 그림입니다. 이 목록이 모든 임시 레지스터의 변경을 열거하는 것은 아닙니다. 아래 매크로 본문에서 각 단계를 확인하실 수 있습니다.
kernel_exit 매크로 본문 · 335행

arch/arm64/kernel/entry.S · Linux v6.18.37

.macro kernel_exit, el .if \el != 0 disable_daif .endif #ifdef CONFIG_ARM64_PSEUDO_NMI alternative_if_not ARM64_HAS_GIC_PRIO_MASKING b .Lskip_pmr_restore\@ alternative_else_nop_endif ldr w20, [sp, #S_PMR] msr_s SYS_ICC_PMR_EL1, x20 /* Ensure priority change is seen by redistributor */ alternative_if_not ARM64_HAS_GIC_PRIO_RELAXED_SYNC dsb sy alternative_else_nop_endif .Lskip_pmr_restore\@: #endif ldp x21, x22, [sp, #S_PC] // load ELR, SPSR #ifdef CONFIG_ARM64_SW_TTBR0_PAN alternative_if_not ARM64_HAS_PAN bl __swpan_exit_el\el alternative_else_nop_endif #endif .if \el == 0 ldr x23, [sp, #S_SP] // load return stack pointer msr sp_el0, x23 tst x22, #PSR_MODE32_BIT // native task? b.eq 3f #ifdef CONFIG_ARM64_ERRATUM_845719 alternative_if ARM64_WORKAROUND_845719 #ifdef CONFIG_PID_IN_CONTEXTIDR mrs x29, contextidr_el1 msr contextidr_el1, x29 #else msr contextidr_el1, xzr #endif alternative_else_nop_endif #endif 3: scs_save tsk /* Ignore asynchronous tag check faults in the uaccess routines */ ldr x0, [tsk, THREAD_SCTLR_USER] clear_mte_async_tcf x0 #ifdef CONFIG_ARM64_PTR_AUTH alternative_if ARM64_HAS_ADDRESS_AUTH /* * IA was enabled for in-kernel PAC. Disable it now if needed, or * alternatively install the user's IA. All other per-task keys and * SCTLR bits were updated on task switch. * * No kernel C function calls after this. */ tbz x0, SCTLR_ELx_ENIA_SHIFT, 1f __ptrauth_keys_install_user tsk, x0, x1, x2 b 2f 1: mrs x0, sctlr_el1 bic x0, x0, SCTLR_ELx_ENIA msr sctlr_el1, x0 2: alternative_else_nop_endif #endif mte_set_user_gcr tsk, x0, x1 apply_ssbd 0, x0, x1 .endif msr elr_el1, x21 // set up the return data msr spsr_el1, x22 ldp x0, x1, [sp, #16 * 0] ldp x2, x3, [sp, #16 * 1] ldp x4, x5, [sp, #16 * 2] ldp x6, x7, [sp, #16 * 3] ldp x8, x9, [sp, #16 * 4] ldp x10, x11, [sp, #16 * 5] ldp x12, x13, [sp, #16 * 6] ldp x14, x15, [sp, #16 * 7] ldp x16, x17, [sp, #16 * 8] ldp x18, x19, [sp, #16 * 9] ldp x20, x21, [sp, #16 * 10] ldp x22, x23, [sp, #16 * 11] ldp x24, x25, [sp, #16 * 12] ldp x26, x27, [sp, #16 * 13] ldp x28, x29, [sp, #16 * 14] .if \el == 0 #ifdef CONFIG_UNMAP_KERNEL_AT_EL0 alternative_insn "b .L_skip_tramp_exit_\@", nop, ARM64_UNMAP_KERNEL_AT_EL0 msr far_el1, x29 ldr_this_cpu x30, this_cpu_vector, x29 tramp_alias x29, tramp_exit msr vbar_el1, x30 // install vector table ldr lr, [sp, #S_LR] // restore x30 add sp, sp, #PT_REGS_SIZE // restore sp br x29 .L_skip_tramp_exit_\@: #endif .endif ldr lr, [sp, #S_LR] add sp, sp, #PT_REGS_SIZE // restore sp .if \el == 0 /* This must be after the last explicit memory access */ alternative_if ARM64_WORKAROUND_SPECULATIVE_UNPRIV_LOAD tlbi vale1, xzr dsb nsh alternative_else_nop_endif .else /* Ensure any device/NC reads complete */ alternative_insn nop, "dmb sy", ARM64_WORKAROUND_1508412 .endif eret sb .endm
SYM_CODE_END(ret_to_kernel)

도구에 이 코드 범위의 끝을 알려 크기와 심볼 정보를 기록합니다. CPU가 이 줄 때문에 자동으로 반환하지는 않습니다.

608행 그림 · 어셈블·링크 단계

함수·코드 범위: ret_to_kernel

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과SYM_CODE_END(ret_to_kernel)
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 끝 표기 자체가 RET나 분기를 실행하지는 않습니다.
SYM_CODE_START_LOCAL(ret_to_user)

이 위치부터 (ret_to_user)라는 코드가 시작한다고 도구에 알려 줍니다. 정렬과 외부 공개 여부도 정의에 포함됩니다. 실제 작업은 아래의 명령들이 수행합니다.

610행 그림 · 어셈블·링크 단계

함수·코드 범위: ret_to_user

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과SYM_CODE_START_LOCAL(ret_to_user)
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	ldr	x19, [tsk, #TSK_TI_FLAGS]	// re-check for single-step

tsk를 기준으로 TSK_TI_FLAGS만큼 계산한 메모리 위치에 보관된 값을 x19로 가져옵니다. 주소 숫자를 옮기는 MOV와 달리 실제 메모리를 읽습니다.

611행 그림 · 메모리 읽기

함수·코드 범위: ret_to_user

읽는 값tsk
메모리[이전 tsk + (TSK_TI_FLAGS)] · 8바이트
이 명령을 실행하기 직전M0 (이 주소에 저장된 값)
이 명령을 실행한 직후M0 (이 주소에 저장된 값)
x19
이 명령을 실행하기 직전명령 직전 x19의 값
이 명령을 실행한 직후M0

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 주소 계산에 의한 기준 레지스터 갱신은 없습니다. 다만 LDR/LDP의 목적 레지스터가 기준 레지스터와 같으면 읽은 값이 그 레지스터를 덮어씁니다.
	enable_step_tsk x19, x2

사용자 복귀 직전에 task의 TIF_SINGLESTEP를 다시 확인해 디버거가 요청한 단일 스텝 상태를 복구합니다. 커널 진입 때 꺼 두었던 스텝 상태와 대응합니다. 실제 설정 대상은 MDSCR_EL1.SS이며 TIF_SINGLESTEP이 있는 경우에만 이 비트를 세웁니다.

612행 그림 · 매크로 전체의 상태 변화

함수·코드 범위: ret_to_user

읽는 값x19
MDSCR_EL1.SS
이 명령을 실행하기 직전명령 직전 MDSCR_EL1.SS의 값
이 명령을 실행한 직후x19의 TIF_SINGLESTEP=1이면 1, 아니면 그대로

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 조건이 맞을 때만 x2을 작업 레지스터로 사용합니다.
enable_step_tsk 매크로 본문 · 68행

arch/arm64/include/asm/assembler.h · Linux v6.18.37

정의 선택 조건: #ifndef __ASM_ASSEMBLER_H

.macro enable_step_tsk, flgs, tmp tbz \flgs, #TIF_SINGLESTEP, 9990f mrs \tmp, mdscr_el1 orr \tmp, \tmp, #MDSCR_EL1_SS msr mdscr_el1, \tmp 9990: .endm
#ifdef CONFIG_KSTACK_ERASE

KSTACK_ERASE는 syscall/예외 처리에서 사용한 커널 스택의 잔여값을 지워 정보 노출 위험을 줄이는 기능입니다. 켜진 빌드에서는 사용자 복귀 전에 정리 함수를 호출하고, 꺼지면 추가 스택 지우기 비용이 없습니다. 이 조건은 빌드 전처리 단계에서 포함할 코드를 고릅니다. CPU가 실행 중 전처리 조건을 검사하지는 않습니다.

613행 그림 · 전처리 단계

함수·코드 범위: ret_to_user

소스
처리 전전처리 전
이 줄의 효과#ifdef CONFIG_KSTACK_ERASE
CPU 레지스터
처리 전해당 없음
이 줄의 효과실행되는 CPU 명령이 없습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	bl	stackleak_erase_on_task_stack

현재 task 스택에서 지울 수 있는 사용 흔적을 정리합니다. 예외 복귀에 필요한 활성 프레임을 무작정 0으로 만드는 것이 아니라 stackleak가 추적한 범위와 현재 SP를 고려하는 호출입니다. BL은 다음 명령의 주소를 LR(x30)에 기록하고 목적지로 분기합니다. 호출된 코드가 x0 같은 작업 레지스터를 바꿀 수 있으므로, 호출 뒤의 값은 그 코드의 반환·보존 규칙에 따릅니다.

614행 그림 · 호출과 실행 위치

함수·코드 범위: ret_to_user

이 줄을 포함하는 조건: #ifdef CONFIG_KSTACK_ERASE

PC
이 명령을 실행하기 직전명령 직전 PC의 값
이 명령을 실행한 직후stackleak_erase_on_task_stack
LR (x30)
이 명령을 실행하기 직전명령 직전 LR의 값 (x30)
이 명령을 실행한 직후이 BL/BLR 다음 명령 주소

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 그림은 분기 명령 자체의 효과입니다. 호출된 함수가 실행된 뒤의 레지스터 값은 그 함수의 반환·보존 규칙을 따라야 합니다.
#endif

빌드 조건으로 나누었던 구간을 닫습니다. 대상:

615행 그림 · 전처리 단계

함수·코드 범위: ret_to_user

이 줄을 포함하는 조건: #ifdef CONFIG_KSTACK_ERASE

소스
처리 전전처리 전
이 줄의 효과#endif
CPU 레지스터
처리 전해당 없음
이 줄의 효과실행되는 CPU 명령이 없습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	kernel_exit 0

스택의 기록을 CPU로 되돌리고 원래 실행을 재개합니다. 사용자에게 돌아가는 경우에는 사용자용 접근 제어와 매핑도 맞춥니다. 여기의 인자는 0입니다.

616행 그림 · 매크로 전체의 주요 상태

함수·코드 범위: ret_to_user

x0~x30
이 명령을 실행하기 직전명령 직전 x0의 값~x30
이 명령을 실행한 직후pt_regs에 저장된 복귀 값
PC
이 명령을 실행하기 직전명령 직전 PC의 값
이 명령을 실행한 직후pt_regs의 S_PC에 저장된 복귀 주소
PSTATE
이 명령을 실행하기 직전명령 직전 PSTATE의 값
이 명령을 실행한 직후pt_regs의 S_PSTATE에 저장된 복귀 상태
SP
이 명령을 실행하기 직전명령 직전 SP의 값
이 명령을 실행한 직후예외 프레임을 해제한 SP; EL0 복귀의 사용자 SP는 S_SP에서 복원

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • KPTI 사용 시 일부 복원과 ERET는 trampoline에서 마칩니다. 그림은 선택한 복귀 경로가 완료되는 시점의 주요 상태입니다. MTE·PAC·SCS 등 구성별 작업은 본문에 있습니다.
  • 여러 명령과 조건부 경로를 묶은 그림입니다. 이 목록이 모든 임시 레지스터의 변경을 열거하는 것은 아닙니다. 아래 매크로 본문에서 각 단계를 확인하실 수 있습니다.
kernel_exit 매크로 본문 · 335행

arch/arm64/kernel/entry.S · Linux v6.18.37

.macro kernel_exit, el .if \el != 0 disable_daif .endif #ifdef CONFIG_ARM64_PSEUDO_NMI alternative_if_not ARM64_HAS_GIC_PRIO_MASKING b .Lskip_pmr_restore\@ alternative_else_nop_endif ldr w20, [sp, #S_PMR] msr_s SYS_ICC_PMR_EL1, x20 /* Ensure priority change is seen by redistributor */ alternative_if_not ARM64_HAS_GIC_PRIO_RELAXED_SYNC dsb sy alternative_else_nop_endif .Lskip_pmr_restore\@: #endif ldp x21, x22, [sp, #S_PC] // load ELR, SPSR #ifdef CONFIG_ARM64_SW_TTBR0_PAN alternative_if_not ARM64_HAS_PAN bl __swpan_exit_el\el alternative_else_nop_endif #endif .if \el == 0 ldr x23, [sp, #S_SP] // load return stack pointer msr sp_el0, x23 tst x22, #PSR_MODE32_BIT // native task? b.eq 3f #ifdef CONFIG_ARM64_ERRATUM_845719 alternative_if ARM64_WORKAROUND_845719 #ifdef CONFIG_PID_IN_CONTEXTIDR mrs x29, contextidr_el1 msr contextidr_el1, x29 #else msr contextidr_el1, xzr #endif alternative_else_nop_endif #endif 3: scs_save tsk /* Ignore asynchronous tag check faults in the uaccess routines */ ldr x0, [tsk, THREAD_SCTLR_USER] clear_mte_async_tcf x0 #ifdef CONFIG_ARM64_PTR_AUTH alternative_if ARM64_HAS_ADDRESS_AUTH /* * IA was enabled for in-kernel PAC. Disable it now if needed, or * alternatively install the user's IA. All other per-task keys and * SCTLR bits were updated on task switch. * * No kernel C function calls after this. */ tbz x0, SCTLR_ELx_ENIA_SHIFT, 1f __ptrauth_keys_install_user tsk, x0, x1, x2 b 2f 1: mrs x0, sctlr_el1 bic x0, x0, SCTLR_ELx_ENIA msr sctlr_el1, x0 2: alternative_else_nop_endif #endif mte_set_user_gcr tsk, x0, x1 apply_ssbd 0, x0, x1 .endif msr elr_el1, x21 // set up the return data msr spsr_el1, x22 ldp x0, x1, [sp, #16 * 0] ldp x2, x3, [sp, #16 * 1] ldp x4, x5, [sp, #16 * 2] ldp x6, x7, [sp, #16 * 3] ldp x8, x9, [sp, #16 * 4] ldp x10, x11, [sp, #16 * 5] ldp x12, x13, [sp, #16 * 6] ldp x14, x15, [sp, #16 * 7] ldp x16, x17, [sp, #16 * 8] ldp x18, x19, [sp, #16 * 9] ldp x20, x21, [sp, #16 * 10] ldp x22, x23, [sp, #16 * 11] ldp x24, x25, [sp, #16 * 12] ldp x26, x27, [sp, #16 * 13] ldp x28, x29, [sp, #16 * 14] .if \el == 0 #ifdef CONFIG_UNMAP_KERNEL_AT_EL0 alternative_insn "b .L_skip_tramp_exit_\@", nop, ARM64_UNMAP_KERNEL_AT_EL0 msr far_el1, x29 ldr_this_cpu x30, this_cpu_vector, x29 tramp_alias x29, tramp_exit msr vbar_el1, x30 // install vector table ldr lr, [sp, #S_LR] // restore x30 add sp, sp, #PT_REGS_SIZE // restore sp br x29 .L_skip_tramp_exit_\@: #endif .endif ldr lr, [sp, #S_LR] add sp, sp, #PT_REGS_SIZE // restore sp .if \el == 0 /* This must be after the last explicit memory access */ alternative_if ARM64_WORKAROUND_SPECULATIVE_UNPRIV_LOAD tlbi vale1, xzr dsb nsh alternative_else_nop_endif .else /* Ensure any device/NC reads complete */ alternative_insn nop, "dmb sy", ARM64_WORKAROUND_1508412 .endif eret sb .endm
SYM_CODE_END(ret_to_user)

도구에 이 코드 범위의 끝을 알려 크기와 심볼 정보를 기록합니다. CPU가 이 줄 때문에 자동으로 반환하지는 않습니다.

617행 그림 · 어셈블·링크 단계

함수·코드 범위: ret_to_user

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과SYM_CODE_END(ret_to_user)
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 끝 표기 자체가 RET나 분기를 실행하지는 않습니다.
	.popsection				// .entry.text

이후 출력할 바이트를 어느 섹션에 넣을지 바꿉니다. 빌드 도구가 코드를 분류하는 작업이며 현재 CPU의 PC나 SP를 옮기는 줄은 아닙니다.

619행 그림 · 어셈블·링크 단계
빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과.popsection
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

KPTI 매핑 교체와 데이터 접근

원본 620–677행

	.macro tramp_map_kernel, tmp

KPTI의 최소 trampoline 매핑에서 정상 커널 매핑으로 돌아가는 매크로입니다. TTBR1의 두 표 사이 고정 offset과 사용자 ASID 표식을 조정하며 필요한 CPU 보완을 수행합니다. 이 줄은 어셈블러 매크로 정의의 시작입니다. 본문은 매크로를 사용하는 위치에 펼쳐지며, 정의 자체가 런타임 함수 호출은 아닙니다.

622행 그림 · 어셈블·링크 단계

매크로 본문: tramp_map_kernel

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과.macro tramp_map_kernel, tmp
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 뒤의 본문은 이 이름을 사용하는 위치에 펼쳐집니다. 정의 위치를 CPU가 함수처럼 호출하지 않습니다.
	mrs	\tmp, ttbr1_el1

현재 trampoline 표를 가리키는 TTBR1을 읽습니다. 이 값을 기준으로 커널 swapper 표 주소를 계산하므로 별도 메모리 포인터를 먼저 읽지 않아도 됩니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.

623행 그림 · 시스템 레지스터 읽기

매크로 본문: tramp_map_kernel

읽는 값TTBR1_EL1
\tmp
이 명령을 실행하기 직전명령 직전 \tmp의 값
이 명령을 실행한 직후명령 직전 TTBR1_EL1의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 일반 RAM의 메모리 접근과 구분합니다.
  • 매크로 tramp_map_kernel의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	add	\tmp, \tmp, #TRAMP_SWAPPER_OFFSET

링커가 검증한 TRAMP_SWAPPER_OFFSET을 더해 trampoline 표에서 정상 swapper 페이지 표 루트로 이동합니다. 임의 메모리 offset이 아니라 두 표의 배치 차이입니다. 이 ADD 형식은 NZCV 조건 플래그를 바꾸지 않습니다.

624행 그림 · 레지스터 상태

매크로 본문: tramp_map_kernel

읽는 값\tmp
\tmp
이 명령을 실행하기 직전명령 직전 \tmp의 값
이 명령을 실행한 직후명령 직전 \tmp의 값 + TRAMP_SWAPPER_OFFSET

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 결과는 목적 레지스터 폭에 맞습니다. 이 형식은 NZCV를 바꾸지 않습니다.
  • 매크로 tramp_map_kernel의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	bic	\tmp, \tmp, #USER_ASID_FLAG

TTBR1에서 사용자 쪽 ASID 선택 표식을 지워 커널 쪽 번역 식별자를 사용하게 합니다. KPTI의 두 매핑 번역을 섞지 않도록 하는 전환입니다. BIC는 마스크에서 1인 위치만 0으로 만들고 나머지 비트는 유지합니다.

625행 그림 · 레지스터 상태

매크로 본문: tramp_map_kernel

읽는 값\tmp
\tmp
이 명령을 실행하기 직전명령 직전 \tmp의 값
이 명령을 실행한 직후명령 직전 \tmp의 값 AND NOT USER_ASID_FLAG

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 결과는 목적 레지스터 폭에 맞습니다. 이 형식은 NZCV를 바꾸지 않습니다.
  • 매크로 tramp_map_kernel의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	msr	ttbr1_el1, \tmp

계산한 정상 커널 TTBR1을 설치합니다. 뒤에서 원래 커널 vectors와 데이터에 접근할 수 있게 만드는 핵심 매핑 복구입니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.

626행 그림 · 시스템 레지스터 쓰기

매크로 본문: tramp_map_kernel

읽는 값\tmp
TTBR1_EL1
이 명령을 실행하기 직전명령 직전 TTBR1_EL1의 값
이 명령을 실행한 직후명령 직전 \tmp의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 접근 권한과 VHE 별칭 규칙에 따라 실제 대상이 정해집니다. 뒤의 ISB·DSB가 필요한지는 이 레지스터와 문맥에 따릅니다.
  • 매크로 tramp_map_kernel의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
#ifdef CONFIG_QCOM_FALKOR_ERRATUM_1003

Qualcomm Falkor erratum 1003 우회 지원을 빌드하면 KPTI 매핑 복구 뒤의 추가 TLBI 경로를 포함합니다. 꺼지면 이 CPU별 보완 코드는 생성되지 않습니다. 이 조건은 빌드 전처리 단계에서 포함할 코드를 고릅니다. CPU가 실행 중 전처리 조건을 검사하지는 않습니다.

627행 그림 · 전처리 단계

매크로 본문: tramp_map_kernel

소스
처리 전전처리 전
이 줄의 효과#ifdef CONFIG_QCOM_FALKOR_ERRATUM_1003
CPU 레지스터
처리 전해당 없음
이 줄의 효과실행되는 CPU 명령이 없습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

alternative_if ARM64_WORKAROUND_QCOM_FALKOR_E1003

실제로 Falkor E1003 영향을 받는 시스템에만 trampoline 영역 번역 무효화와 동기화를 남깁니다. 같은 CPU별 작업을 일반 KPTI 필수 작업과 구분합니다. 이 매크로는 기본·교체 명령의 패치 정보를 만들며, CPU가 이 줄을 매번 if 조건처럼 실행하지 않습니다.

628행 그림 · CPU 기능에 따른 명령 패치

매크로 본문: tramp_map_kernel

이 줄을 포함하는 조건: #ifdef CONFIG_QCOM_FALKOR_ERRATUM_1003

패치 전 코드
처리 전기본 명령열
이 줄의 효과기능·정책에 맞는 기본 코드 또는 교체 코드

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 빌드할 때 패치 정보를 만들고 커널의 대체 코드 처리에서 명령을 선택합니다. 이 소스 줄을 CPU가 매번 if문으로 실행하는 것은 아닙니다.
	movk	\tmp, #:abs_g2_nc:(TRAMP_VALIAS >> 12)

한 명령에 담기 어려운 큰 값이나 주소를 레지스터에 구성합니다. MOVK는 일부 16비트만 덮어쓰므로 앞에서 만든 나머지 조각을 유지합니다.

630행 그림 · 상수의 비트 조각 구성

매크로 본문: tramp_map_kernel

이 줄을 포함하는 조건: #ifdef CONFIG_QCOM_FALKOR_ERRATUM_1003

읽는 값\tmp
\tmp
이 명령을 실행하기 직전명령 직전 \tmp의 값
이 명령을 실행한 직후명령 직전 값에서 [47:32]만 :abs_g2_nc:(TRAMP_VALIAS >> 12)로 교체

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 선택한 16비트 이외의 비트는 그대로 남습니다.
  • 매크로 tramp_map_kernel의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	movk	\tmp, #:abs_g1_nc:(TRAMP_VALIAS >> 12)

한 명령에 담기 어려운 큰 값이나 주소를 레지스터에 구성합니다. MOVK는 일부 16비트만 덮어쓰므로 앞에서 만든 나머지 조각을 유지합니다.

631행 그림 · 상수의 비트 조각 구성

매크로 본문: tramp_map_kernel

이 줄을 포함하는 조건: #ifdef CONFIG_QCOM_FALKOR_ERRATUM_1003

읽는 값\tmp
\tmp
이 명령을 실행하기 직전명령 직전 \tmp의 값
이 명령을 실행한 직후명령 직전 값에서 [31:16]만 :abs_g1_nc:(TRAMP_VALIAS >> 12)로 교체

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 선택한 16비트 이외의 비트는 그대로 남습니다.
  • 매크로 tramp_map_kernel의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	movk	\tmp, #:abs_g0_nc:((TRAMP_VALIAS & ~(SZ_2M - 1)) >> 12)

한 명령에 담기 어려운 큰 값이나 주소를 레지스터에 구성합니다. MOVK는 일부 16비트만 덮어쓰므로 앞에서 만든 나머지 조각을 유지합니다.

633행 그림 · 상수의 비트 조각 구성

매크로 본문: tramp_map_kernel

이 줄을 포함하는 조건: #ifdef CONFIG_QCOM_FALKOR_ERRATUM_1003

읽는 값\tmp
\tmp
이 명령을 실행하기 직전명령 직전 \tmp의 값
이 명령을 실행한 직후명령 직전 값에서 [15:0]만 :abs_g0_nc:((TRAMP_VALIAS & ~(SZ_2M - 1)) >> 12)로 교체

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 선택한 16비트 이외의 비트는 그대로 남습니다.
  • 매크로 tramp_map_kernel의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	isb

뒤 명령을 변경된 CPU 제어 상태에 맞춰 실행하도록 동기화합니다. 주소 변환이나 벡터 설정을 바꾼 뒤 예전 실행 상태가 계속 쓰이지 않게 합니다.

634행 그림 · 동기화·캐시·대기 상태

매크로 본문: tramp_map_kernel

이 줄을 포함하는 조건: #ifdef CONFIG_QCOM_FALKOR_ERRATUM_1003

명령 실행 문맥
이 명령을 실행하기 직전실행 전 상태
이 명령을 실행한 직후앞서 바꾼 상태를 이후 명령 실행에 반영

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 매크로 tramp_map_kernel의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	tlbi	vae1, \tmp

앞에서 만든 trampoline 영역 주소를 TLBI 피연산자로 사용해 관련 번역을 무효화합니다. Falkor E1003 보완의 일부이며 데이터 캐시 내용을 삭제하는 명령이 아닙니다. 페이지 테이블 메모리를 수정해도 이미 저장된 TLB 변환이 자동으로 없어지는 것은 아니므로 별도로 무효화합니다.

635행 그림 · 동기화·캐시·대기 상태

매크로 본문: tramp_map_kernel

이 줄을 포함하는 조건: #ifdef CONFIG_QCOM_FALKOR_ERRATUM_1003

읽는 값\tmp
TLB
이 명령을 실행하기 직전실행 전 상태
이 명령을 실행한 직후지정한 주소 변환 기록 무효화 요청

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 관리 요청과 완료 보장은 구분됩니다. 주변 DSB·ISB와 함께 해석합니다.
  • 매크로 tramp_map_kernel의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	dsb	nsh

지정 범위·종류의 앞선 작업이 완료되기를 기다립니다. 페이지 복사나 캐시 관리가 끝나기 전에 다음 상태로 넘어가지 않도록 하는 대기 지점입니다.

636행 그림 · 동기화·캐시·대기 상태

매크로 본문: tramp_map_kernel

이 줄을 포함하는 조건: #ifdef CONFIG_QCOM_FALKOR_ERRATUM_1003

작업 완료
이 명령을 실행하기 직전실행 전 상태
이 명령을 실행한 직후지정 범위의 앞선 작업 완료까지 기다림

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 매크로 tramp_map_kernel의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
alternative_else_nop_endif

628행에서 시작한 ARM64_WORKAROUND_QCOM_FALKOR_E1003 패치 묶음을 닫습니다. 조건에 맞는 본문을 사용하지 않는 쪽은 같은 길이의 NOP로 채워 뒤 명령의 배치를 유지합니다. 이 줄 자체가 CPU에서 조건을 검사하는 명령은 아닙니다.

637행 그림 · CPU 기능에 따른 명령 패치

매크로 본문: tramp_map_kernel

이 줄을 포함하는 조건: #ifdef CONFIG_QCOM_FALKOR_ERRATUM_1003

패치 전 코드
처리 전기본 명령열
이 줄의 효과기능·정책에 맞는 기본 코드 또는 교체 코드

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 빌드할 때 패치 정보를 만들고 커널의 대체 코드 처리에서 명령을 선택합니다. 이 소스 줄을 CPU가 매번 if문으로 실행하는 것은 아닙니다.
#endif /* CONFIG_QCOM_FALKOR_ERRATUM_1003 */

빌드 조건으로 나누었던 구간을 닫습니다. 대상:

638행 그림 · 전처리 단계

매크로 본문: tramp_map_kernel

이 줄을 포함하는 조건: #ifdef CONFIG_QCOM_FALKOR_ERRATUM_1003

소스
처리 전전처리 전
이 줄의 효과#endif
CPU 레지스터
처리 전해당 없음
이 줄의 효과실행되는 CPU 명령이 없습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	.endm

공통 작업의 정의를 여기서 마칩니다. RET가 아니라 빌드 도구가 읽는 경계입니다.

639행 그림 · 어셈블·링크 단계

매크로 본문: tramp_map_kernel

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과.endm
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 끝 표기 자체가 RET나 분기를 실행하지는 않습니다.
	.macro tramp_unmap_kernel, tmp

사용자 복귀를 위해 정상 커널 TTBR1에서 최소 trampoline 표로 바꾸는 매크로입니다. 커널의 넓은 주소를 감추되 이 매크로 다음의 복귀 코드는 계속 실행할 수 있어야 합니다. 이 줄은 어셈블러 매크로 정의의 시작입니다. 본문은 매크로를 사용하는 위치에 펼쳐지며, 정의 자체가 런타임 함수 호출은 아닙니다.

642행 그림 · 어셈블·링크 단계

매크로 본문: tramp_unmap_kernel

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과.macro tramp_unmap_kernel, tmp
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 뒤의 본문은 이 이름을 사용하는 위치에 펼쳐집니다. 정의 위치를 CPU가 함수처럼 호출하지 않습니다.
	mrs	\tmp, ttbr1_el1

현재 정상 커널 루트와 ASID를 담은 TTBR1을 읽어 제한된 사용자 실행용 매핑으로 바꿀 기준으로 사용합니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.

643행 그림 · 시스템 레지스터 읽기

매크로 본문: tramp_unmap_kernel

읽는 값TTBR1_EL1
\tmp
이 명령을 실행하기 직전명령 직전 \tmp의 값
이 명령을 실행한 직후명령 직전 TTBR1_EL1의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 일반 RAM의 메모리 접근과 구분합니다.
  • 매크로 tramp_unmap_kernel의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	sub	\tmp, \tmp, #TRAMP_SWAPPER_OFFSET

swapper와 trampoline 루트 사이의 고정 배치 차이를 빼서 최소 표 주소로 이동합니다. 이 SUB 형식은 NZCV 조건 플래그를 바꾸지 않습니다.

644행 그림 · 레지스터 상태

매크로 본문: tramp_unmap_kernel

읽는 값\tmp
\tmp
이 명령을 실행하기 직전명령 직전 \tmp의 값
이 명령을 실행한 직후명령 직전 \tmp의 값 − TRAMP_SWAPPER_OFFSET

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 결과는 목적 레지스터 폭에 맞습니다. 이 형식은 NZCV를 바꾸지 않습니다.
  • 매크로 tramp_unmap_kernel의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	orr	\tmp, \tmp, #USER_ASID_FLAG

사용자 쪽 ASID 표식을 세워 최소 매핑의 번역을 커널 쪽 번역과 구분합니다. ORR은 어느 한쪽이 1인 비트를 결과에 세우므로 원래 1이던 비트도 유지됩니다.

645행 그림 · 레지스터 상태

매크로 본문: tramp_unmap_kernel

읽는 값\tmp
\tmp
이 명령을 실행하기 직전명령 직전 \tmp의 값
이 명령을 실행한 직후명령 직전 \tmp의 값 OR USER_ASID_FLAG

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 결과는 목적 레지스터 폭에 맞습니다. 이 형식은 NZCV를 바꾸지 않습니다.
  • 매크로 tramp_unmap_kernel의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	msr	ttbr1_el1, \tmp

사용자 실행용 최소 TTBR1을 설치합니다. 이후에는 일반 커널 데이터에 기대지 않고 trampoline 안에서 ERET까지 마쳐야 합니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.

646행 그림 · 시스템 레지스터 쓰기

매크로 본문: tramp_unmap_kernel

읽는 값\tmp
TTBR1_EL1
이 명령을 실행하기 직전명령 직전 TTBR1_EL1의 값
이 명령을 실행한 직후명령 직전 \tmp의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 접근 권한과 VHE 별칭 규칙에 따라 실제 대상이 정해집니다. 뒤의 ISB·DSB가 필요한지는 이 레지스터와 문맥에 따릅니다.
  • 매크로 tramp_unmap_kernel의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	.endm

공통 작업의 정의를 여기서 마칩니다. RET가 아니라 빌드 도구가 읽는 경계입니다.

652행 그림 · 어셈블·링크 단계

매크로 본문: tramp_unmap_kernel

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과.endm
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 끝 표기 자체가 RET나 분기를 실행하지는 않습니다.
	.macro		tramp_data_read_var	dst, var

최소 trampoline 매핑에서 사용할 외부 기호 주소를 읽는 매크로입니다. 재배치 커널은 전용 rodata 슬롯을 통해 런타임 주소를 얻고, 고정 주소 커널은 MOVZ/MOVK로 주소 상수를 조립합니다. 이 줄은 어셈블러 매크로 정의의 시작입니다. 본문은 매크로를 사용하는 위치에 펼쳐지며, 정의 자체가 런타임 함수 호출은 아닙니다.

654행 그림 · 어셈블·링크 단계

매크로 본문: tramp_data_read_var

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과.macro tramp_data_read_var dst, var
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 뒤의 본문은 이 이름을 사용하는 위치에 펼쳐집니다. 정의 위치를 CPU가 함수처럼 호출하지 않습니다.
#ifdef CONFIG_RELOCATABLE

재배치 가능한 커널은 적재 위치·KASLR에 따라 기호 주소가 달라질 수 있으므로 trampoline 전용 데이터 슬롯에 든 주소를 읽습니다. 꺼진 빌드는 고정 가상 주소를 명령 즉시값으로 만들 수 있습니다. 이 조건은 빌드 전처리 단계에서 포함할 코드를 고릅니다. CPU가 실행 중 전처리 조건을 검사하지는 않습니다.

655행 그림 · 전처리 단계

매크로 본문: tramp_data_read_var

소스
처리 전전처리 전
이 줄의 효과#ifdef CONFIG_RELOCATABLE
CPU 레지스터
처리 전해당 없음
이 줄의 효과실행되는 CPU 명령이 없습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	ldr		\dst, .L__tramp_data_\var

trampoline rodata의 해당 기호 슬롯에서 재배치된 실제 주소를 읽습니다. 제한된 매핑 안에서도 사용할 수 있게 따로 둔 리터럴입니다. 이 LDR 리터럴 형식은 PC 기준으로 레이블 위치를 찾아 그곳의 메모리를 읽습니다. 레이블 주소 자체를 구하는 ADR과 구분합니다.

656행 그림 · 메모리 읽기

매크로 본문: tramp_data_read_var

이 줄을 포함하는 조건: #ifdef CONFIG_RELOCATABLE

\dst
이 명령을 실행하기 직전명령 직전 \dst의 값
이 명령을 실행한 직후메모리[.L__tramp_data_\var]의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • =심벌이면 주소 상수를 리터럴 풀에서 읽는 의사 명령입니다. 레이블만 적힌 LDR은 해당 위치의 메모리를 읽습니다.
  • 매크로 tramp_data_read_var의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	.ifndef		.L__tramp_data_\var

아직 만들어지지 않은 심볼인지 검사해 중복 생성을 피합니다. .L__tramp_data_\var

657행 그림 · 어셈블·링크 단계

매크로 본문: tramp_data_read_var

이 줄을 포함하는 조건: #ifdef CONFIG_RELOCATABLE

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과.ifndef .L__tramp_data_\var
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	.pushsection	".entry.tramp.rodata", "a", %progbits

이후 출력할 바이트를 어느 섹션에 넣을지 바꿉니다. 빌드 도구가 코드를 분류하는 작업이며 현재 CPU의 PC나 SP를 옮기는 줄은 아닙니다. ".entry.tramp.rodata", "a", %progbits

658행 그림 · 어셈블·링크 단계

매크로 본문: tramp_data_read_var

이 줄을 포함하는 조건: #ifdef CONFIG_RELOCATABLE / .ifndef .L__tramp_data_\var

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과.pushsection ".entry.tramp.rodata", "a", %progbits
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	.align		3

다음 코드나 데이터가 정해진 경계에서 시작하도록 빈 공간을 채웁니다. 이 .align 인자는 바이트 수가 아니라 2의 지수입니다. 값은 3입니다.

659행 그림 · 어셈블·링크 단계

매크로 본문: tramp_data_read_var

이 줄을 포함하는 조건: #ifdef CONFIG_RELOCATABLE / .ifndef .L__tramp_data_\var

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과.align 3
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

.L__tramp_data_\var:

여기에 .L__tramp_data_\var라는 위치 이름을 붙입니다. 다른 줄의 분기나 주소 계산이 이 위치를 찾아올 수 있습니다. 이름 자체가 CPU 작업을 추가하지는 않습니다.

660행 그림 · 위치에 이름 붙이기

매크로 본문: tramp_data_read_var

이 줄을 포함하는 조건: #ifdef CONFIG_RELOCATABLE / .ifndef .L__tramp_data_\var

레이블
처리 전현재 배치 위치
이 줄의 효과.L__tramp_data_\var
CPU 레지스터
처리 전그대로
이 줄의 효과레이블 자체가 값을 바꾸지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	.quad		\var

각 값을 8바이트 크기로 파일 안에 기록합니다. \var는 CPU가 지금 계산할 식이 아니라 어셈블·링크 단계에서 채울 데이터입니다.

661행 그림 · 어셈블·링크 단계

매크로 본문: tramp_data_read_var

이 줄을 포함하는 조건: #ifdef CONFIG_RELOCATABLE / .ifndef .L__tramp_data_\var

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과.quad \var
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	.popsection

이후 출력할 바이트를 어느 섹션에 넣을지 바꿉니다. 빌드 도구가 코드를 분류하는 작업이며 현재 CPU의 PC나 SP를 옮기는 줄은 아닙니다.

662행 그림 · 어셈블·링크 단계

매크로 본문: tramp_data_read_var

이 줄을 포함하는 조건: #ifdef CONFIG_RELOCATABLE / .ifndef .L__tramp_data_\var

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과.popsection
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	.endif

어셈블 조건에 따라 생성할 코드를 나누던 범위가 끝납니다.

663행 그림 · 어셈블·링크 단계

매크로 본문: tramp_data_read_var

이 줄을 포함하는 조건: #ifdef CONFIG_RELOCATABLE / .ifndef .L__tramp_data_\var

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과.endif
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

#else

고정 주소 빌드에서는 기호의 가상 주소를 MOVZ/MOVK 즉시값으로 조립합니다. 재배치 포인터를 담은 별도 trampoline rodata 슬롯이 필요 없는 대안입니다.

664행 그림 · 전처리 단계

매크로 본문: tramp_data_read_var

이 줄을 포함하는 조건: #ifdef CONFIG_RELOCATABLE

소스
처리 전전처리 전
이 줄의 효과#else
CPU 레지스터
처리 전해당 없음
이 줄의 효과실행되는 CPU 명령이 없습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	movz		\dst, :abs_g2_s:\var

한 명령에 담기 어려운 큰 값이나 주소를 레지스터에 구성합니다. 즉시값 조각과 재배치 표현을 사용하며, 메모리에서 그 주소의 본문을 복사하는 작업은 아닙니다.

673행 그림 · 상수의 비트 조각 구성

매크로 본문: tramp_data_read_var

이 줄을 포함하는 조건: 앞 조건의 반대

\dst
이 명령을 실행하기 직전명령 직전 \dst의 값
이 명령을 실행한 직후심벌의 부호에 맞게 MOVZ 또는 MOVN으로 구성한 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 부호 있는 재배치가 실제 명령의 MOVZ/MOVN 선택에도 영향을 주므로 남은 비트를 항상 0이라고 표시하지 않습니다.
  • 매크로 tramp_data_read_var의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	movk		\dst, :abs_g1_nc:\var

한 명령에 담기 어려운 큰 값이나 주소를 레지스터에 구성합니다. MOVK는 일부 16비트만 덮어쓰므로 앞에서 만든 나머지 조각을 유지합니다.

674행 그림 · 상수의 비트 조각 구성

매크로 본문: tramp_data_read_var

이 줄을 포함하는 조건: 앞 조건의 반대

읽는 값\dst
\dst
이 명령을 실행하기 직전명령 직전 \dst의 값
이 명령을 실행한 직후명령 직전 값에서 [31:16]만 :abs_g1_nc:\var로 교체

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 선택한 16비트 이외의 비트는 그대로 남습니다.
  • 매크로 tramp_data_read_var의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	movk		\dst, :abs_g0_nc:\var

한 명령에 담기 어려운 큰 값이나 주소를 레지스터에 구성합니다. MOVK는 일부 16비트만 덮어쓰므로 앞에서 만든 나머지 조각을 유지합니다.

675행 그림 · 상수의 비트 조각 구성

매크로 본문: tramp_data_read_var

이 줄을 포함하는 조건: 앞 조건의 반대

읽는 값\dst
\dst
이 명령을 실행하기 직전명령 직전 \dst의 값
이 명령을 실행한 직후명령 직전 값에서 [15:0]만 :abs_g0_nc:\var로 교체

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 선택한 16비트 이외의 비트는 그대로 남습니다.
  • 매크로 tramp_data_read_var의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
#endif

빌드 조건으로 나누었던 구간을 닫습니다. 대상:

676행 그림 · 전처리 단계

매크로 본문: tramp_data_read_var

이 줄을 포함하는 조건: 앞 조건의 반대

소스
처리 전전처리 전
이 줄의 효과#endif
CPU 레지스터
처리 전해당 없음
이 줄의 효과실행되는 CPU 명령이 없습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	.endm

공통 작업의 정의를 여기서 마칩니다. RET가 아니라 빌드 도구가 읽는 경계입니다.

677행 그림 · 어셈블·링크 단계

매크로 본문: tramp_data_read_var

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과.endm
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 끝 표기 자체가 RET나 분기를 실행하지는 않습니다.

분기 이력 완화와 trampoline 슬롯

원본 678–750행

#define BHB_MITIGATION_NONE	0

BHB_MITIGATION_NONE=0은 분기 이력 완화를 추가하지 않은 벡터 변형을 고르는 생성 인수입니다. 런타임 오류 코드가 아닙니다. 전처리기는 이후 이 이름이 쓰인 곳에 정의한 내용을 치환합니다. 실행 중 값을 보관하는 변수를 만드는 줄은 아닙니다.

679행 그림 · 전처리 단계
소스
처리 전전처리 전
이 줄의 효과#define BHB_MITIGATION_NONE 0
CPU 레지스터
처리 전해당 없음
이 줄의 효과실행되는 CPU 명령이 없습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

#define BHB_MITIGATION_LOOP	1

BHB_MITIGATION_LOOP=1은 CPU에 맞는 분기 반복으로 이력 영향을 줄이는 벡터 변형입니다. 반복 자체를 보안 목적의 진입 코드로 생성합니다. 전처리기는 이후 이 이름이 쓰인 곳에 정의한 내용을 치환합니다. 실행 중 값을 보관하는 변수를 만드는 줄은 아닙니다.

680행 그림 · 전처리 단계
소스
처리 전전처리 전
이 줄의 효과#define BHB_MITIGATION_LOOP 1
CPU 레지스터
처리 전해당 없음
이 줄의 효과실행되는 CPU 명령이 없습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

#define BHB_MITIGATION_FW	2

BHB_MITIGATION_FW=2는 펌웨어 SMCCC 완화를 호출하는 벡터 변형입니다. LOOP 방식과 동시에 모두 실행하라는 뜻이 아니라 별도 후보를 구분합니다. 전처리기는 이후 이 이름이 쓰인 곳에 정의한 내용을 치환합니다. 실행 중 값을 보관하는 변수를 만드는 줄은 아닙니다.

681행 그림 · 전처리 단계
소스
처리 전전처리 전
이 줄의 효과#define BHB_MITIGATION_FW 2
CPU 레지스터
처리 전해당 없음
이 줄의 효과실행되는 CPU 명령이 없습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

#define BHB_MITIGATION_INSN	3

BHB_MITIGATION_INSN=3은 CLEARBHB 명령으로 분기 이력을 정리하는 벡터 변형입니다. 지원 방식에 맞는 벡터가 이후 CPU별로 선택됩니다. 전처리기는 이후 이 이름이 쓰인 곳에 정의한 내용을 치환합니다. 실행 중 값을 보관하는 변수를 만드는 줄은 아닙니다.

682행 그림 · 전처리 단계
소스
처리 전전처리 전
이 줄의 효과#define BHB_MITIGATION_INSN 3
CPU 레지스터
처리 전해당 없음
이 줄의 효과실행되는 CPU 명령이 없습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	.macro tramp_ventry, vector_start, regsize, kpti, bhb

사용자 예외용 trampoline 입구를 생성합니다. regsize는 원래 사용자 상태의 폭, kpti는 매핑 교체 여부, bhb는 Spectre 분기 이력 완화 방식을 선택하며 vector_start로 같은 예외 슬롯의 정상 벡터 offset을 계산합니다. 이 줄은 어셈블러 매크로 정의의 시작입니다. 본문은 매크로를 사용하는 위치에 펼쳐지며, 정의 자체가 런타임 함수 호출은 아닙니다.

684행 그림 · 어셈블·링크 단계

매크로 본문: tramp_ventry

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과.macro tramp_ventry, vector_start, regsize, kpti, bhb
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 뒤의 본문은 이 이름을 사용하는 위치에 펼쳐집니다. 정의 위치를 CPU가 함수처럼 호출하지 않습니다.
	.align	7

다음 코드나 데이터가 정해진 경계에서 시작하도록 빈 공간을 채웁니다. 이 .align 인자는 바이트 수가 아니라 2의 지수입니다. 값은 7입니다.

685행 그림 · 어셈블·링크 단계

매크로 본문: tramp_ventry

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과.align 7
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

1:

여기에 1라는 위치 이름을 붙입니다. 다른 줄의 분기나 주소 계산이 이 위치를 찾아올 수 있습니다. 이름 자체가 CPU 작업을 추가하지는 않습니다.

686행 그림 · 위치에 이름 붙이기

매크로 본문: tramp_ventry

레이블
처리 전현재 배치 위치
이 줄의 효과1
CPU 레지스터
처리 전그대로
이 줄의 효과레이블 자체가 값을 바꾸지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	.if	\regsize == 64

어셈블 단계에서 인자나 식을 검사해 필요한 명령만 만듭니다. \regsize == 64

687행 그림 · 어셈블·링크 단계

매크로 본문: tramp_ventry

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과.if \regsize == 64
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	msr	tpidrro_el0, x30	// Restored in kernel_ventry

최소 매핑에서 x30을 임시로 사용하기 전에 TPIDRRO_EL0에 사용자 값을 보관합니다. 정상 kernel_ventry에 도착하면 앞의 복구 코드가 다시 x30으로 읽습니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.

688행 그림 · 시스템 레지스터 쓰기

매크로 본문: tramp_ventry

이 줄을 포함하는 조건: .if \regsize == 64

읽는 값x30
TPIDRRO_EL0
이 명령을 실행하기 직전명령 직전 TPIDRRO_EL0의 값
이 명령을 실행한 직후명령 직전 x30의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 접근 권한과 VHE 별칭 규칙에 따라 실제 대상이 정해집니다. 뒤의 ISB·DSB가 필요한지는 이 레지스터와 문맥에 따릅니다.
  • 매크로 tramp_ventry의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	.endif

어셈블 조건에 따라 생성할 코드를 나누던 범위가 끝납니다.

689행 그림 · 어셈블·링크 단계

매크로 본문: tramp_ventry

이 줄을 포함하는 조건: .if \regsize == 64

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과.endif
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	.if	\bhb == BHB_MITIGATION_LOOP

어셈블 단계에서 인자나 식을 검사해 필요한 명령만 만듭니다. \bhb == BHB_MITIGATION_LOOP

691행 그림 · 어셈블·링크 단계

매크로 본문: tramp_ventry

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과.if \bhb == BHB_MITIGATION_LOOP
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	__mitigate_spectre_bhb_loop	x30

CPU별로 준비된 분기 반복을 실행해 사용자 분기 이력이 커널의 추측 실행에 미치는 영향을 줄입니다. x30은 아직 원래 값을 TPIDRRO_EL0에 보관해 둔 임시 레지스터입니다. 반복 횟수는 CPU에 필요한 값으로 패치됩니다.

696행 그림 · 매크로 안의 상태 변화

매크로 본문: tramp_ventry

이 줄을 포함하는 조건: .if \bhb == BHB_MITIGATION_LOOP

입력
이 명령을 실행하기 직전호출 직전 값
이 명령을 실행한 직후x30
주요 결과
이 명령을 실행하기 직전본문 실행 전
이 명령을 실행한 직후CPU별로 패치된 반복 횟수로 분기 루프를 실행하여 분기 이력 공격을 완화합니다.

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 여러 명령과 내부 분기를 묶은 그림입니다. 작업 레지스터를 모두 보존한다는 뜻은 아니며, 정확한 변경 순서는 아래 매크로 본문과 줄별 그림에서 확인합니다.
  • 매크로 tramp_ventry의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
__mitigate_spectre_bhb_loop 매크로 본문 · 823행

arch/arm64/include/asm/assembler.h · Linux v6.18.37

정의 선택 조건: #ifndef __ASM_ASSEMBLER_H

.macro __mitigate_spectre_bhb_loop tmp #ifdef CONFIG_MITIGATE_SPECTRE_BRANCH_HISTORY alternative_cb ARM64_ALWAYS_SYSTEM, spectre_bhb_patch_loop_iter mov \tmp, #32 // Patched to correct the immediate alternative_cb_end .Lspectre_bhb_loop\@: b . + 4 subs \tmp, \tmp, #1 b.ne .Lspectre_bhb_loop\@ sb #endif /* CONFIG_MITIGATE_SPECTRE_BRANCH_HISTORY */ .endm
	.endif // \bhb == BHB_MITIGATION_LOOP

어셈블 조건에 따라 생성할 코드를 나누던 범위가 끝납니다.

697행 그림 · 어셈블·링크 단계

매크로 본문: tramp_ventry

이 줄을 포함하는 조건: .if \bhb == BHB_MITIGATION_LOOP

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과.endif
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	.if	\bhb == BHB_MITIGATION_INSN

어셈블 단계에서 인자나 식을 검사해 필요한 명령만 만듭니다. \bhb == BHB_MITIGATION_INSN

699행 그림 · 어셈블·링크 단계

매크로 본문: tramp_ventry

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과.if \bhb == BHB_MITIGATION_INSN
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	clearbhb

지원 CPU에서 분기 이력 상태를 정리하는 CLEARBHB를 실행합니다. 일반 메모리 캐시를 비우는 명령이 아니며 다음 ISB와 함께 진입 완화를 구성합니다. 매크로는 HINT #22 인코딩을 출력합니다.

700행 그림 · 동기화·캐시·대기 상태

매크로 본문: tramp_ventry

이 줄을 포함하는 조건: .if \bhb == BHB_MITIGATION_INSN

분기 이력
이 명령을 실행하기 직전실행 전 상태
이 명령을 실행한 직후지원 CPU에서 분기 이력 정리

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 매크로 tramp_ventry의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
clearbhb 매크로 본문 · 97행

arch/arm64/include/asm/assembler.h · Linux v6.18.37

정의 선택 조건: #ifndef __ASM_ASSEMBLER_H

.macro clearbhb hint #22 .endm
	isb

뒤 명령을 변경된 CPU 제어 상태에 맞춰 실행하도록 동기화합니다. 주소 변환이나 벡터 설정을 바꾼 뒤 예전 실행 상태가 계속 쓰이지 않게 합니다.

701행 그림 · 동기화·캐시·대기 상태

매크로 본문: tramp_ventry

이 줄을 포함하는 조건: .if \bhb == BHB_MITIGATION_INSN

명령 실행 문맥
이 명령을 실행하기 직전실행 전 상태
이 명령을 실행한 직후앞서 바꾼 상태를 이후 명령 실행에 반영

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 매크로 tramp_ventry의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	.endif // \bhb == BHB_MITIGATION_INSN

어셈블 조건에 따라 생성할 코드를 나누던 범위가 끝납니다.

702행 그림 · 어셈블·링크 단계

매크로 본문: tramp_ventry

이 줄을 포함하는 조건: .if \bhb == BHB_MITIGATION_INSN

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과.endif
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	.if	\kpti == 1

어셈블 단계에서 인자나 식을 검사해 필요한 명령만 만듭니다. \kpti == 1

704행 그림 · 어셈블·링크 단계

매크로 본문: tramp_ventry

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과.if \kpti == 1
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	bl	2f

바로 아래 지역 라벨을 BL로 호출해 반환 예측용 이력을 준비합니다. 일반 서비스 함수 호출이 아니라 trampoline의 추측 실행 방어 순서를 만드는 제어 흐름입니다. BL은 다음 명령의 주소를 LR(x30)에 기록하고 목적지로 분기합니다. 호출된 코드가 x0 같은 작업 레지스터를 바꿀 수 있으므로, 호출 뒤의 값은 그 코드의 반환·보존 규칙에 따릅니다.

710행 그림 · 호출과 실행 위치

매크로 본문: tramp_ventry

이 줄을 포함하는 조건: .if \kpti == 1

PC
이 명령을 실행하기 직전명령 직전 PC의 값
이 명령을 실행한 직후2f (L712)
LR (x30)
이 명령을 실행하기 직전명령 직전 LR의 값 (x30)
이 명령을 실행한 직후이 BL/BLR 다음 명령 주소

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 그림은 분기 명령 자체의 효과입니다. 호출된 함수가 실행된 뒤의 레지스터 값은 그 함수의 반환·보존 규칙을 따라야 합니다.
  • 매크로 tramp_ventry의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	b	.

같은 명령 위치에서 반복합니다. 이 경로에서는 더 진행하거나 호출자에게 돌아갈 정상 절차를 마련하지 않았습니다.

711행 그림 · 호출과 실행 위치

매크로 본문: tramp_ventry

이 줄을 포함하는 조건: .if \kpti == 1

PC
이 명령을 실행하기 직전명령 직전 PC의 값
이 명령을 실행한 직후.

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 새 복귀 주소를 LR에 기록하지 않습니다. RET 역시 메모리의 스택 값을 자동으로 꺼내는 명령은 아닙니다.
  • 매크로 tramp_ventry의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
2:

여기에 2라는 위치 이름을 붙입니다. 다른 줄의 분기나 주소 계산이 이 위치를 찾아올 수 있습니다. 이름 자체가 CPU 작업을 추가하지는 않습니다.

712행 그림 · 위치에 이름 붙이기

매크로 본문: tramp_ventry

이 줄을 포함하는 조건: .if \kpti == 1

레이블
처리 전현재 배치 위치
이 줄의 효과2
CPU 레지스터
처리 전그대로
이 줄의 효과레이블 자체가 값을 바꾸지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	tramp_map_kernel	x30

trampoline 표에서 정상 커널 표로 TTBR1을 전환합니다. 원래 vectors 주소와 커널 데이터에 접근하기 전에 매핑을 복구해야 합니다.

713행 그림 · 매크로 전체의 주요 상태

매크로 본문: tramp_ventry

이 줄을 포함하는 조건: .if \kpti == 1

TTBR1_EL1
이 명령을 실행하기 직전명령 직전 TTBR1_EL1의 값
이 명령을 실행한 직후(명령 직전 TTBR1_EL1의 값 + TRAMP_SWAPPER_OFFSET) AND NOT USER_ASID_FLAG

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • x30을 임시 레지스터로 사용합니다. map 경로의 Falkor 우회에서는 이 임시값을 TLBI 피연산자로 다시 구성할 수 있습니다.
  • 여러 명령과 조건부 경로를 묶은 그림입니다. 이 목록이 모든 임시 레지스터의 변경을 열거하는 것은 아닙니다. 아래 매크로 본문에서 각 단계를 확인하실 수 있습니다.
  • 매크로 tramp_ventry의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
tramp_map_kernel 매크로 본문 · 622행

arch/arm64/kernel/entry.S · Linux v6.18.37

.macro tramp_map_kernel, tmp mrs \tmp, ttbr1_el1 add \tmp, \tmp, #TRAMP_SWAPPER_OFFSET bic \tmp, \tmp, #USER_ASID_FLAG msr ttbr1_el1, \tmp #ifdef CONFIG_QCOM_FALKOR_ERRATUM_1003 alternative_if ARM64_WORKAROUND_QCOM_FALKOR_E1003 /* ASID already in \tmp[63:48] */ movk \tmp, #:abs_g2_nc:(TRAMP_VALIAS >> 12) movk \tmp, #:abs_g1_nc:(TRAMP_VALIAS >> 12) /* 2MB boundary containing the vectors, so we nobble the walk cache */ movk \tmp, #:abs_g0_nc:((TRAMP_VALIAS & ~(SZ_2M - 1)) >> 12) isb tlbi vae1, \tmp dsb nsh alternative_else_nop_endif #endif /* CONFIG_QCOM_FALKOR_ERRATUM_1003 */ .endm
alternative_insn isb, nop, ARM64_WORKAROUND_QCOM_FALKOR_E1003

Falkor E1003 경로는 tramp_map_kernel 안에서 필요한 ISB를 이미 수행하므로 이 추가 ISB를 NOP로 바꿉니다. 다른 CPU에는 TTBR 갱신 뒤 실행 동기화를 위해 ISB가 남습니다. 이 매크로는 기본·교체 명령의 패치 정보를 만들며, CPU가 이 줄을 매번 if 조건처럼 실행하지 않습니다.

714행 그림 · CPU 기능에 따른 명령 패치

매크로 본문: tramp_ventry

이 줄을 포함하는 조건: .if \kpti == 1

패치 전 코드
처리 전기본 명령열
이 줄의 효과기능·정책에 맞는 기본 코드 또는 교체 코드

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 빌드할 때 패치 정보를 만들고 커널의 대체 코드 처리에서 명령을 선택합니다. 이 소스 줄을 CPU가 매번 if문으로 실행하는 것은 아닙니다.
	tramp_data_read_var	x30, vectors

재배치 여부에 맞는 방법으로 정상 커널 vectors 주소를 x30에 가져옵니다. 최소 trampoline에서 커널 진입점으로 연결할 목적지입니다. 재배치 구성에서는 별도 rodata의 리터럴을 읽고, 그렇지 않은 구성에서는 MOVZ/MOVK로 주소를 구성합니다.

715행 그림 · 주소·값 구분

매크로 본문: tramp_ventry

이 줄을 포함하는 조건: .if \kpti == 1

x30
이 명령을 실행하기 직전명령 직전 x30의 값
이 명령을 실행한 직후주소(vectors)

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 이 매크로의 임시 레지스터는 내부에서 사용됩니다.
  • 재배치 구성은 리터럴을 읽고, 그 외 구성은 MOVZ/MOVK로 주소를 만듭니다.
  • 매크로 tramp_ventry의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
tramp_data_read_var 매크로 본문 · 654행

arch/arm64/kernel/entry.S · Linux v6.18.37

.macro tramp_data_read_var dst, var #ifdef CONFIG_RELOCATABLE ldr \dst, .L__tramp_data_\var .ifndef .L__tramp_data_\var .pushsection ".entry.tramp.rodata", "a", %progbits .align 3 .L__tramp_data_\var: .quad \var .popsection .endif #else /* * As !RELOCATABLE implies !RANDOMIZE_BASE the address is always a * compile time constant (and hence not secret and not worth hiding). * * As statically allocated kernel code and data always live in the top * 47 bits of the address space we can sign-extend bit 47 and avoid an * instruction to load the upper 16 bits (which must be 0xFFFF). */ movz \dst, :abs_g2_s:\var movk \dst, :abs_g1_nc:\var movk \dst, :abs_g0_nc:\var #endif .endm
alternative_if_not ARM64_WORKAROUND_CAVIUM_TX2_219_PRFM

ThunderX2의 특정 prefetch 문제 보완이 필요한 CPU에서는 다음 코드 prefetch를 NOP로 대체합니다. 영향을 받지 않는 경우만 정상 벡터의 명령 가져오기를 미리 요청합니다. 이 매크로는 기본·교체 명령의 패치 정보를 만들며, CPU가 이 줄을 매번 if 조건처럼 실행하지 않습니다.

716행 그림 · CPU 기능에 따른 명령 패치

매크로 본문: tramp_ventry

이 줄을 포함하는 조건: .if \kpti == 1

패치 전 코드
처리 전기본 명령열
이 줄의 효과기능·정책에 맞는 기본 코드 또는 교체 코드

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 빌드할 때 패치 정보를 만들고 커널의 대체 코드 처리에서 명령을 선택합니다. 이 소스 줄을 CPU가 매번 if문으로 실행하는 것은 아닙니다.
	prfm	plil1strm, [x30, #(1b - \vector_start)]

정상 벡터 표에서 현재 예외와 같은 슬롯의 명령을 instruction cache 쪽으로 prefetch합니다. 실제 handler 호출을 수행하는 것은 아니며 CPU 보완 조건에 따라 빠질 수 있습니다. 이는 미리 가져오라는 힌트이며 LDR처럼 결과를 일반 레지스터에 넣지 않습니다.

717행 그림 · 동기화·캐시·대기 상태

매크로 본문: tramp_ventry

이 줄을 포함하는 조건: .if \kpti == 1

읽는 값x30\vector_start
미리 가져오기
이 명령을 실행하기 직전실행 전 상태
이 명령을 실행한 직후선택한 캐시 접근을 위한 힌트

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 관리 요청과 완료 보장은 구분됩니다. 주변 DSB·ISB와 함께 해석합니다.
  • 매크로 tramp_ventry의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
alternative_else_nop_endif

716행에서 시작한 ARM64_WORKAROUND_CAVIUM_TX2_219_PRFM 패치 묶음을 닫습니다. 조건에 맞는 본문을 사용하지 않는 쪽은 같은 길이의 NOP로 채워 뒤 명령의 배치를 유지합니다. 이 줄 자체가 CPU에서 조건을 검사하는 명령은 아닙니다.

718행 그림 · CPU 기능에 따른 명령 패치

매크로 본문: tramp_ventry

이 줄을 포함하는 조건: .if \kpti == 1

패치 전 코드
처리 전기본 명령열
이 줄의 효과기능·정책에 맞는 기본 코드 또는 교체 코드

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 빌드할 때 패치 정보를 만들고 커널의 대체 코드 처리에서 명령을 선택합니다. 이 소스 줄을 CPU가 매번 if문으로 실행하는 것은 아닙니다.
	msr	vbar_el1, x30

VBAR_EL1을 정상 커널 벡터 표로 바꿔 이후 커널 실행 중의 예외도 정상 입구로 들어오게 합니다. 이어지는 ISB가 새 벡터 설정을 반영합니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.

720행 그림 · 시스템 레지스터 쓰기

매크로 본문: tramp_ventry

이 줄을 포함하는 조건: .if \kpti == 1

읽는 값x30
VBAR_EL1
이 명령을 실행하기 직전명령 직전 VBAR_EL1의 값
이 명령을 실행한 직후명령 직전 x30의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 접근 권한과 VHE 별칭 규칙에 따라 실제 대상이 정해집니다. 뒤의 ISB·DSB가 필요한지는 이 레지스터와 문맥에 따릅니다.
  • 매크로 tramp_ventry의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	isb

뒤 명령을 변경된 CPU 제어 상태에 맞춰 실행하도록 동기화합니다. 주소 변환이나 벡터 설정을 바꾼 뒤 예전 실행 상태가 계속 쓰이지 않게 합니다.

721행 그림 · 동기화·캐시·대기 상태

매크로 본문: tramp_ventry

이 줄을 포함하는 조건: .if \kpti == 1

명령 실행 문맥
이 명령을 실행하기 직전실행 전 상태
이 명령을 실행한 직후앞서 바꾼 상태를 이후 명령 실행에 반영

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 매크로 tramp_ventry의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	.else

앞 어셈블 조건의 반대 경우에 생성할 코드입니다.

722행 그림 · 어셈블·링크 단계

매크로 본문: tramp_ventry

이 줄을 포함하는 조건: .if \kpti == 1

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과.else
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	adr_l	x30, vectors

x30에 vectors의 위치를 계산합니다. 그 위치의 데이터는 아직 읽지 않습니다. 이후의 메모리 접근이나 분기가 사용할 주소를 준비하는 줄입니다.

723행 그림 · 주소·값 구분

매크로 본문: tramp_ventry

이 줄을 포함하는 조건: 앞 어셈블 조건의 반대

x30
이 명령을 실행하기 직전명령 직전 x30의 값
이 명령을 실행한 직후주소(vectors)

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 이 매크로의 임시 레지스터는 내부에서 사용됩니다.
  • 매크로 tramp_ventry의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
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
	.endif // \kpti == 1

어셈블 조건에 따라 생성할 코드를 나누던 범위가 끝납니다.

724행 그림 · 어셈블·링크 단계

매크로 본문: tramp_ventry

이 줄을 포함하는 조건: 앞 어셈블 조건의 반대

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과.endif
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	.if	\bhb == BHB_MITIGATION_FW

어셈블 단계에서 인자나 식을 검사해 필요한 명령만 만듭니다. \bhb == BHB_MITIGATION_FW

726행 그림 · 어셈블·링크 단계

매크로 본문: tramp_ventry

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과.if \bhb == BHB_MITIGATION_FW
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	__mitigate_spectre_bhb_fw

이 CPU가 사용하는 펌웨어 BHB 완화를 호출합니다. LOOP 또는 CLEARBHB를 선택한 별도 벡터에는 이 호출이 생성되지 않습니다. 요청에 사용하는 x0~x3는 스택에 보존했다가 펌웨어에서 돌아온 뒤 복원합니다.

732행 그림 · 매크로 안의 상태 변화

매크로 본문: tramp_ventry

이 줄을 포함하는 조건: .if \bhb == BHB_MITIGATION_FW

입력
이 명령을 실행하기 직전호출 직전 값
이 명령을 실행한 직후현재 task·CPU 상태
주요 결과
이 명령을 실행하기 직전본문 실행 전
이 명령을 실행한 직후필요한 인자 레지스터를 보존하고 펌웨어 BHB 완화 호출을 수행한 뒤 복원합니다.

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 여러 명령과 내부 분기를 묶은 그림입니다. 작업 레지스터를 모두 보존한다는 뜻은 아니며, 정확한 변경 순서는 아래 매크로 본문과 줄별 그림에서 확인합니다.
  • 매크로 tramp_ventry의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
__mitigate_spectre_bhb_fw 매크로 본문 · 847행

arch/arm64/include/asm/assembler.h · Linux v6.18.37

정의 선택 조건: #ifndef __ASM_ASSEMBLER_H

.macro __mitigate_spectre_bhb_fw #ifdef CONFIG_MITIGATE_SPECTRE_BRANCH_HISTORY stp x0, x1, [sp, #-16]! stp x2, x3, [sp, #-16]! mov w0, #ARM_SMCCC_ARCH_WORKAROUND_3 alternative_cb ARM64_ALWAYS_SYSTEM, smccc_patch_fw_mitigation_conduit nop // Patched to SMC/HVC #0 alternative_cb_end ldp x2, x3, [sp], #16 ldp x0, x1, [sp], #16 #endif /* CONFIG_MITIGATE_SPECTRE_BRANCH_HISTORY */ .endm
	.endif // \bhb == BHB_MITIGATION_FW

어셈블 조건에 따라 생성할 코드를 나누던 범위가 끝납니다.

733행 그림 · 어셈블·링크 단계

매크로 본문: tramp_ventry

이 줄을 포함하는 조건: .if \bhb == BHB_MITIGATION_FW

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과.endif
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	add	x30, x30, #(1b - \vector_start + 4)

현재 trampoline 슬롯과 같은 종류의 정상 벡터 위치를 계산하되 첫 분기 4바이트는 건너뜁니다. 그래야 정상 kernel_ventry의 trampoline 복구 부분으로 들어갑니다. 이 ADD 형식은 NZCV 조건 플래그를 바꾸지 않습니다.

대응 슬롯의 첫 B를 건너뛰는 +4입니다. kernel_ventry의 trampoline 정리 부분으로 들어갑니다.

735행 그림 · 레지스터 상태

매크로 본문: tramp_ventry

읽는 값x30\vector_start
x30
이 명령을 실행하기 직전명령 직전 x30의 값
이 명령을 실행한 직후명령 직전 x30의 값 + (1b - \vector_start + 4)

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 결과는 목적 레지스터 폭에 맞습니다. 이 형식은 NZCV를 바꾸지 않습니다.
  • 매크로 tramp_ventry의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	ret

LR(x30)에 적힌 주소로 실행을 돌립니다. 그 주소가 어떻게 준비됐는지가 중요합니다. 정상 함수의 호출자일 수도 있고, 절전 복귀나 trampoline에서 별도로 기록한 재개 지점일 수도 있습니다.

여기서 RET는 예외 복귀 ERET가 아니라 x30에 계산한 kernel vector 내부 주소로 이동합니다.

736행 그림 · 호출과 실행 위치

매크로 본문: tramp_ventry

읽는 값lr
PC
이 명령을 실행하기 직전명령 직전 PC의 값
이 명령을 실행한 직후명령 직전 lr의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 새 복귀 주소를 LR에 기록하지 않습니다. RET 역시 메모리의 스택 값을 자동으로 꺼내는 명령은 아닙니다.
  • 매크로 tramp_ventry의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
.org 1b + 128	// Did we overflow the ventry slot?

현재 출력 위치를 정해 둔 슬롯 끝까지 맞춥니다. 너무 많은 코드가 들어가 이미 지나쳤다면 빌드에서 드러납니다. 1b + 128

737행 그림 · 어셈블·링크 단계

매크로 본문: tramp_ventry

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과.org 1b + 128
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	.endm

공통 작업의 정의를 여기서 마칩니다. RET가 아니라 빌드 도구가 읽는 경계입니다.

738행 그림 · 어셈블·링크 단계

매크로 본문: tramp_ventry

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과.endm
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 끝 표기 자체가 RET나 분기를 실행하지는 않습니다.
	.macro	generate_tramp_vector,	kpti, bhb

KPTI 사용 여부와 BHB 완화 방식 한 조합의 벡터 표를 생성합니다. 사용하지 않는 EL1 슬롯 0x400바이트를 건너뛰고 AArch64·AArch32 EL0 예외 입구를 네 개씩 만듭니다. 이 줄은 어셈블러 매크로 정의의 시작입니다. 본문은 매크로를 사용하는 위치에 펼쳐지며, 정의 자체가 런타임 함수 호출은 아닙니다.

740행 그림 · 어셈블·링크 단계

매크로 본문: generate_tramp_vector

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과.macro generate_tramp_vector, kpti, bhb
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 뒤의 본문은 이 이름을 사용하는 위치에 펼쳐집니다. 정의 위치를 CPU가 함수처럼 호출하지 않습니다.
.Lvector_start\@:

여기에 .Lvector_start\@라는 위치 이름을 붙입니다. 다른 줄의 분기나 주소 계산이 이 위치를 찾아올 수 있습니다. 이름 자체가 CPU 작업을 추가하지는 않습니다.

741행 그림 · 위치에 이름 붙이기

매크로 본문: generate_tramp_vector

레이블
처리 전현재 배치 위치
이 줄의 효과.Lvector_start\@
CPU 레지스터
처리 전그대로
이 줄의 효과레이블 자체가 값을 바꾸지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	.space	0x400

원하는 크기의 빈 공간을 배치합니다. 뒤 코드의 상대 위치를 조정하는 빌드 작업입니다. 0x400

742행 그림 · 어셈블·링크 단계

매크로 본문: generate_tramp_vector

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과.space 0x400
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	.rept	4

지정 횟수만큼 코드를 생성합니다. 실행 중 반복문을 도는 것과는 다릅니다. 4

744행 그림 · 어셈블·링크 단계

매크로 본문: generate_tramp_vector

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과.rept 4
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	tramp_ventry	.Lvector_start\@, 64, \kpti, \bhb

AArch64 사용자 예외의 128바이트 진입 슬롯을 만듭니다. kpti 인수에 따라 최소 매핑에서 정상 매핑으로 전환하고 \bhb가 지정한 분기 이력 완화를 적용한 뒤 대응하는 정상 vectors 슬롯의 첫 분기 다음(+4)으로 연결합니다.

745행 그림 · 벡터·핸들러 코드 생성

매크로 본문: generate_tramp_vector

매크로 인수
이 명령을 실행하기 직전호출 위치의 인수
이 명령을 실행한 직후.Lvector_start\@, 64, \kpti, \bhb
생성되는 코드
이 명령을 실행하기 직전어셈블 전
이 명령을 실행한 직후선택한 KPTI와 BHB 완화 방식에 맞는 128바이트 슬롯을 생성하고 실제 vectors 슬롯의 +4 위치로 RET합니다.

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 이 한 줄은 여러 명령을 생성합니다. 레지스터 변화는 펼쳐진 본문의 각 명령과 선택된 진입 경로에 따라 생깁니다.
  • 매크로 generate_tramp_vector의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
tramp_ventry 매크로 본문 · 684행

arch/arm64/kernel/entry.S · Linux v6.18.37

.macro tramp_ventry, vector_start, regsize, kpti, bhb .align 7 1: .if \regsize == 64 msr tpidrro_el0, x30 // Restored in kernel_ventry .endif .if \bhb == BHB_MITIGATION_LOOP /* * This sequence must appear before the first indirect branch. i.e. the * ret out of tramp_ventry. It appears here because x30 is free. */ __mitigate_spectre_bhb_loop x30 .endif // \bhb == BHB_MITIGATION_LOOP .if \bhb == BHB_MITIGATION_INSN clearbhb isb .endif // \bhb == BHB_MITIGATION_INSN .if \kpti == 1 /* * Defend against branch aliasing attacks by pushing a dummy * entry onto the return stack and using a RET instruction to * enter the full-fat kernel vectors. */ bl 2f b . 2: tramp_map_kernel x30 alternative_insn isb, nop, ARM64_WORKAROUND_QCOM_FALKOR_E1003 tramp_data_read_var x30, vectors alternative_if_not ARM64_WORKAROUND_CAVIUM_TX2_219_PRFM prfm plil1strm, [x30, #(1b - \vector_start)] alternative_else_nop_endif msr vbar_el1, x30 isb .else adr_l x30, vectors .endif // \kpti == 1 .if \bhb == BHB_MITIGATION_FW /* * The firmware sequence must appear before the first indirect branch. * i.e. the ret out of tramp_ventry. But it also needs the stack to be * mapped to save/restore the registers the SMC clobbers. */ __mitigate_spectre_bhb_fw .endif // \bhb == BHB_MITIGATION_FW add x30, x30, #(1b - \vector_start + 4) ret .org 1b + 128 // Did we overflow the ventry slot? .endm
	.endr

반복 생성할 범위의 끝입니다.

746행 그림 · 어셈블·링크 단계

매크로 본문: generate_tramp_vector

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과.endr
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	.rept	4

지정 횟수만큼 코드를 생성합니다. 실행 중 반복문을 도는 것과는 다릅니다. 4

747행 그림 · 어셈블·링크 단계

매크로 본문: generate_tramp_vector

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과.rept 4
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	tramp_ventry	.Lvector_start\@, 32, \kpti, \bhb

AArch32 사용자 예외의 128바이트 진입 슬롯을 만듭니다. kpti 인수에 따라 최소 매핑에서 정상 매핑으로 전환하고 \bhb가 지정한 분기 이력 완화를 적용한 뒤 대응하는 정상 vectors 슬롯의 첫 분기 다음(+4)으로 연결합니다.

748행 그림 · 벡터·핸들러 코드 생성

매크로 본문: generate_tramp_vector

매크로 인수
이 명령을 실행하기 직전호출 위치의 인수
이 명령을 실행한 직후.Lvector_start\@, 32, \kpti, \bhb
생성되는 코드
이 명령을 실행하기 직전어셈블 전
이 명령을 실행한 직후선택한 KPTI와 BHB 완화 방식에 맞는 128바이트 슬롯을 생성하고 실제 vectors 슬롯의 +4 위치로 RET합니다.

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 이 한 줄은 여러 명령을 생성합니다. 레지스터 변화는 펼쳐진 본문의 각 명령과 선택된 진입 경로에 따라 생깁니다.
  • 매크로 generate_tramp_vector의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
tramp_ventry 매크로 본문 · 684행

arch/arm64/kernel/entry.S · Linux v6.18.37

.macro tramp_ventry, vector_start, regsize, kpti, bhb .align 7 1: .if \regsize == 64 msr tpidrro_el0, x30 // Restored in kernel_ventry .endif .if \bhb == BHB_MITIGATION_LOOP /* * This sequence must appear before the first indirect branch. i.e. the * ret out of tramp_ventry. It appears here because x30 is free. */ __mitigate_spectre_bhb_loop x30 .endif // \bhb == BHB_MITIGATION_LOOP .if \bhb == BHB_MITIGATION_INSN clearbhb isb .endif // \bhb == BHB_MITIGATION_INSN .if \kpti == 1 /* * Defend against branch aliasing attacks by pushing a dummy * entry onto the return stack and using a RET instruction to * enter the full-fat kernel vectors. */ bl 2f b . 2: tramp_map_kernel x30 alternative_insn isb, nop, ARM64_WORKAROUND_QCOM_FALKOR_E1003 tramp_data_read_var x30, vectors alternative_if_not ARM64_WORKAROUND_CAVIUM_TX2_219_PRFM prfm plil1strm, [x30, #(1b - \vector_start)] alternative_else_nop_endif msr vbar_el1, x30 isb .else adr_l x30, vectors .endif // \kpti == 1 .if \bhb == BHB_MITIGATION_FW /* * The firmware sequence must appear before the first indirect branch. * i.e. the ret out of tramp_ventry. But it also needs the stack to be * mapped to save/restore the registers the SMC clobbers. */ __mitigate_spectre_bhb_fw .endif // \bhb == BHB_MITIGATION_FW add x30, x30, #(1b - \vector_start + 4) ret .org 1b + 128 // Did we overflow the ventry slot? .endm
	.endr

반복 생성할 범위의 끝입니다.

749행 그림 · 어셈블·링크 단계

매크로 본문: generate_tramp_vector

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과.endr
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	.endm

공통 작업의 정의를 여기서 마칩니다. RET가 아니라 빌드 도구가 읽는 경계입니다.

750행 그림 · 어셈블·링크 단계

매크로 본문: generate_tramp_vector

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과.endm
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 끝 표기 자체가 RET나 분기를 실행하지는 않습니다.

trampoline 테이블과 EL1 완화 벡터

원본 751–813행

#ifdef CONFIG_UNMAP_KERNEL_AT_EL0

KPTI를 지원하는 빌드에서만 커널 전체 매핑이 없어도 접근할 trampoline 벡터와 복귀 코드를 전용 섹션에 둡니다. 설정이 꺼지면 이 최소 매핑 경로는 없습니다. 이 조건은 빌드 전처리 단계에서 포함할 코드를 고릅니다. CPU가 실행 중 전처리 조건을 검사하지는 않습니다.

752행 그림 · 전처리 단계
소스
처리 전전처리 전
이 줄의 효과#ifdef CONFIG_UNMAP_KERNEL_AT_EL0
CPU 레지스터
처리 전해당 없음
이 줄의 효과실행되는 CPU 명령이 없습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	.pushsection ".entry.tramp.text", "ax"

이후 출력할 바이트를 어느 섹션에 넣을지 바꿉니다. 빌드 도구가 코드를 분류하는 작업이며 현재 CPU의 PC나 SP를 옮기는 줄은 아닙니다. ".entry.tramp.text", "ax"

758행 그림 · 어셈블·링크 단계

이 줄을 포함하는 조건: #ifdef CONFIG_UNMAP_KERNEL_AT_EL0

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과.pushsection ".entry.tramp.text", "ax"
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	.align	11

다음 코드나 데이터가 정해진 경계에서 시작하도록 빈 공간을 채웁니다. 이 .align 인자는 바이트 수가 아니라 2의 지수입니다. 값은 11입니다.

759행 그림 · 어셈블·링크 단계

이 줄을 포함하는 조건: #ifdef CONFIG_UNMAP_KERNEL_AT_EL0

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과.align 11
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

SYM_CODE_START_LOCAL_NOALIGN(tramp_vectors)

이 위치부터 (tramp_vectors)라는 코드가 시작한다고 도구에 알려 줍니다. 정렬과 외부 공개 여부도 정의에 포함됩니다. 실제 작업은 아래의 명령들이 수행합니다.

760행 그림 · 어셈블·링크 단계

함수·코드 범위: tramp_vectors

이 줄을 포함하는 조건: #ifdef CONFIG_UNMAP_KERNEL_AT_EL0

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과SYM_CODE_START_LOCAL_NOALIGN(tramp_vectors)
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

#ifdef CONFIG_MITIGATE_SPECTRE_BRANCH_HISTORY

Spectre 분기 이력 완화 지원 빌드에는 LOOP·펌웨어·명령 방식의 KPTI 벡터 후보를 모두 생성합니다. 실제로 매번 모두 실행하는 것이 아니라 CPU·정책에 맞는 후보를 선택할 수 있게 합니다. 이 조건은 빌드 전처리 단계에서 포함할 코드를 고릅니다. CPU가 실행 중 전처리 조건을 검사하지는 않습니다.

761행 그림 · 전처리 단계

함수·코드 범위: tramp_vectors

이 줄을 포함하는 조건: #ifdef CONFIG_UNMAP_KERNEL_AT_EL0

소스
처리 전전처리 전
이 줄의 효과#ifdef CONFIG_MITIGATE_SPECTRE_BRANCH_HISTORY
CPU 레지스터
처리 전해당 없음
이 줄의 효과실행되는 CPU 명령이 없습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	generate_tramp_vector	kpti=1, bhb=BHB_MITIGATION_LOOP

분기 반복 LOOP 방식의 벡터 표 후보를 생성합니다. KPTI를 사용하는 진입이라 정상 커널 매핑 복구도 포함합니다. 실행 중 모든 후보를 순서대로 거치는 것이 아니라 CPU와 정책에 맞는 벡터가 선택됩니다.

762행 그림 · 벡터·핸들러 코드 생성

함수·코드 범위: tramp_vectors

이 줄을 포함하는 조건: #ifdef CONFIG_UNMAP_KERNEL_AT_EL0 / #ifdef CONFIG_MITIGATE_SPECTRE_BRANCH_HISTORY

매크로 인수
이 명령을 실행하기 직전호출 위치의 인수
이 명령을 실행한 직후kpti=1, bhb=BHB_MITIGATION_LOOP
생성되는 코드
이 명령을 실행하기 직전어셈블 전
이 명령을 실행한 직후0x400바이트를 비우고 64비트와 32비트 하위 EL 슬롯을 각각 네 개 생성합니다.

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 이 한 줄은 여러 명령을 생성합니다. 레지스터 변화는 펼쳐진 본문의 각 명령과 선택된 진입 경로에 따라 생깁니다.
generate_tramp_vector 매크로 본문 · 740행

arch/arm64/kernel/entry.S · Linux v6.18.37

.macro generate_tramp_vector, kpti, bhb .Lvector_start\@: .space 0x400 .rept 4 tramp_ventry .Lvector_start\@, 64, \kpti, \bhb .endr .rept 4 tramp_ventry .Lvector_start\@, 32, \kpti, \bhb .endr .endm
	generate_tramp_vector	kpti=1, bhb=BHB_MITIGATION_FW

펌웨어 호출 FW 방식의 벡터 표 후보를 생성합니다. KPTI를 사용하는 진입이라 정상 커널 매핑 복구도 포함합니다. 실행 중 모든 후보를 순서대로 거치는 것이 아니라 CPU와 정책에 맞는 벡터가 선택됩니다.

763행 그림 · 벡터·핸들러 코드 생성

함수·코드 범위: tramp_vectors

이 줄을 포함하는 조건: #ifdef CONFIG_UNMAP_KERNEL_AT_EL0 / #ifdef CONFIG_MITIGATE_SPECTRE_BRANCH_HISTORY

매크로 인수
이 명령을 실행하기 직전호출 위치의 인수
이 명령을 실행한 직후kpti=1, bhb=BHB_MITIGATION_FW
생성되는 코드
이 명령을 실행하기 직전어셈블 전
이 명령을 실행한 직후0x400바이트를 비우고 64비트와 32비트 하위 EL 슬롯을 각각 네 개 생성합니다.

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 이 한 줄은 여러 명령을 생성합니다. 레지스터 변화는 펼쳐진 본문의 각 명령과 선택된 진입 경로에 따라 생깁니다.
generate_tramp_vector 매크로 본문 · 740행

arch/arm64/kernel/entry.S · Linux v6.18.37

.macro generate_tramp_vector, kpti, bhb .Lvector_start\@: .space 0x400 .rept 4 tramp_ventry .Lvector_start\@, 64, \kpti, \bhb .endr .rept 4 tramp_ventry .Lvector_start\@, 32, \kpti, \bhb .endr .endm
	generate_tramp_vector	kpti=1, bhb=BHB_MITIGATION_INSN

CLEARBHB 명령 INSN 방식의 벡터 표 후보를 생성합니다. KPTI를 사용하는 진입이라 정상 커널 매핑 복구도 포함합니다. 실행 중 모든 후보를 순서대로 거치는 것이 아니라 CPU와 정책에 맞는 벡터가 선택됩니다.

764행 그림 · 벡터·핸들러 코드 생성

함수·코드 범위: tramp_vectors

이 줄을 포함하는 조건: #ifdef CONFIG_UNMAP_KERNEL_AT_EL0 / #ifdef CONFIG_MITIGATE_SPECTRE_BRANCH_HISTORY

매크로 인수
이 명령을 실행하기 직전호출 위치의 인수
이 명령을 실행한 직후kpti=1, bhb=BHB_MITIGATION_INSN
생성되는 코드
이 명령을 실행하기 직전어셈블 전
이 명령을 실행한 직후0x400바이트를 비우고 64비트와 32비트 하위 EL 슬롯을 각각 네 개 생성합니다.

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 이 한 줄은 여러 명령을 생성합니다. 레지스터 변화는 펼쳐진 본문의 각 명령과 선택된 진입 경로에 따라 생깁니다.
generate_tramp_vector 매크로 본문 · 740행

arch/arm64/kernel/entry.S · Linux v6.18.37

.macro generate_tramp_vector, kpti, bhb .Lvector_start\@: .space 0x400 .rept 4 tramp_ventry .Lvector_start\@, 64, \kpti, \bhb .endr .rept 4 tramp_ventry .Lvector_start\@, 32, \kpti, \bhb .endr .endm
#endif /* CONFIG_MITIGATE_SPECTRE_BRANCH_HISTORY */

빌드 조건으로 나누었던 구간을 닫습니다. 대상:

765행 그림 · 전처리 단계

함수·코드 범위: tramp_vectors

이 줄을 포함하는 조건: #ifdef CONFIG_UNMAP_KERNEL_AT_EL0 / #ifdef CONFIG_MITIGATE_SPECTRE_BRANCH_HISTORY

소스
처리 전전처리 전
이 줄의 효과#endif
CPU 레지스터
처리 전해당 없음
이 줄의 효과실행되는 CPU 명령이 없습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	generate_tramp_vector	kpti=1, bhb=BHB_MITIGATION_NONE

추가 BHB 완화가 없는 NONE 방식의 벡터 표 후보를 생성합니다. KPTI를 사용하는 진입이라 정상 커널 매핑 복구도 포함합니다. 실행 중 모든 후보를 순서대로 거치는 것이 아니라 CPU와 정책에 맞는 벡터가 선택됩니다.

766행 그림 · 벡터·핸들러 코드 생성

함수·코드 범위: tramp_vectors

이 줄을 포함하는 조건: #ifdef CONFIG_UNMAP_KERNEL_AT_EL0

매크로 인수
이 명령을 실행하기 직전호출 위치의 인수
이 명령을 실행한 직후kpti=1, bhb=BHB_MITIGATION_NONE
생성되는 코드
이 명령을 실행하기 직전어셈블 전
이 명령을 실행한 직후0x400바이트를 비우고 64비트와 32비트 하위 EL 슬롯을 각각 네 개 생성합니다.

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 이 한 줄은 여러 명령을 생성합니다. 레지스터 변화는 펼쳐진 본문의 각 명령과 선택된 진입 경로에 따라 생깁니다.
generate_tramp_vector 매크로 본문 · 740행

arch/arm64/kernel/entry.S · Linux v6.18.37

.macro generate_tramp_vector, kpti, bhb .Lvector_start\@: .space 0x400 .rept 4 tramp_ventry .Lvector_start\@, 64, \kpti, \bhb .endr .rept 4 tramp_ventry .Lvector_start\@, 32, \kpti, \bhb .endr .endm
SYM_CODE_END(tramp_vectors)

도구에 이 코드 범위의 끝을 알려 크기와 심볼 정보를 기록합니다. CPU가 이 줄 때문에 자동으로 반환하지는 않습니다.

767행 그림 · 어셈블·링크 단계

함수·코드 범위: tramp_vectors

이 줄을 포함하는 조건: #ifdef CONFIG_UNMAP_KERNEL_AT_EL0

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과SYM_CODE_END(tramp_vectors)
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 끝 표기 자체가 RET나 분기를 실행하지는 않습니다.
SYM_CODE_START_LOCAL(tramp_exit)

이 위치부터 (tramp_exit)라는 코드가 시작한다고 도구에 알려 줍니다. 정렬과 외부 공개 여부도 정의에 포함됩니다. 실제 작업은 아래의 명령들이 수행합니다.

769행 그림 · 어셈블·링크 단계

함수·코드 범위: tramp_exit

이 줄을 포함하는 조건: #ifdef CONFIG_UNMAP_KERNEL_AT_EL0

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과SYM_CODE_START_LOCAL(tramp_exit)
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	tramp_unmap_kernel	x29

사용자 실행으로 돌아가기 전에 커널의 넓은 매핑을 다시 감춥니다. 이 이후에도 실행할 최소 trampoline 코드는 남아 있어야 합니다. 여기의 인자는 x29입니다.

770행 그림 · 매크로 전체의 주요 상태

함수·코드 범위: tramp_exit

이 줄을 포함하는 조건: #ifdef CONFIG_UNMAP_KERNEL_AT_EL0

TTBR1_EL1
이 명령을 실행하기 직전명령 직전 TTBR1_EL1의 값
이 명령을 실행한 직후(명령 직전 TTBR1_EL1의 값 − TRAMP_SWAPPER_OFFSET) OR USER_ASID_FLAG

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • x29을 임시 레지스터로 사용합니다. map 경로의 Falkor 우회에서는 이 임시값을 TLBI 피연산자로 다시 구성할 수 있습니다.
  • 여러 명령과 조건부 경로를 묶은 그림입니다. 이 목록이 모든 임시 레지스터의 변경을 열거하는 것은 아닙니다. 아래 매크로 본문에서 각 단계를 확인하실 수 있습니다.
tramp_unmap_kernel 매크로 본문 · 642행

arch/arm64/kernel/entry.S · Linux v6.18.37

.macro tramp_unmap_kernel, tmp mrs \tmp, ttbr1_el1 sub \tmp, \tmp, #TRAMP_SWAPPER_OFFSET orr \tmp, \tmp, #USER_ASID_FLAG msr ttbr1_el1, \tmp /* * We avoid running the post_ttbr_update_workaround here because * it's only needed by Cavium ThunderX, which requires KPTI to be * disabled. */ .endm
	mrs		x29, far_el1		// restore x29

kernel_exit가 FAR_EL1에 임시 보관한 사용자 x29를 되찾습니다. 여기서 FAR는 fault 주소가 아니며, 매핑 제거에 쓴 x29를 원래 사용자 값으로 돌린 뒤 ERET합니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.

771행 그림 · 시스템 레지스터 읽기

함수·코드 범위: tramp_exit

이 줄을 포함하는 조건: #ifdef CONFIG_UNMAP_KERNEL_AT_EL0

읽는 값FAR_EL1
x29
이 명령을 실행하기 직전명령 직전 x29의 값
이 명령을 실행한 직후명령 직전 FAR_EL1의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 일반 RAM의 메모리 접근과 구분합니다.
	eret

예외가 끝났으므로 저장해 둔 PC와 실행 상태를 함께 되살립니다. 일반 RET와 달리 어느 예외 레벨과 스택 모드로 돌아갈지도 SPSR의 내용에 따라 정해집니다.

772행 그림 · 저장된 실행 상태로 복귀

함수·코드 범위: tramp_exit

이 줄을 포함하는 조건: #ifdef CONFIG_UNMAP_KERNEL_AT_EL0

읽는 값ELR_ELxSPSR_ELx
PC
이 명령을 실행하기 직전명령 직전 PC의 값
이 명령을 실행한 직후명령 직전 ELR_ELx의 값
PSTATE
이 명령을 실행하기 직전명령 직전 PSTATE의 값
이 명령을 실행한 직후명령 직전 SPSR_ELx의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • ELx는 현재 예외 레벨입니다. SPSR의 모드에 따라 복귀 레벨과 스택 선택도 바뀝니다. 일반 레지스터 복구는 앞의 load 명령들이 담당합니다.
	sb

trampoline ERET 다음에 추측 실행 방지 배리어를 둡니다. 정상 실행은 ERET로 사용자 공간에 갔으며, CPU의 잘못된 직선 추측이 뒤 코드를 진행하지 못하게 합니다.

773행 그림 · 동기화·캐시·대기 상태

함수·코드 범위: tramp_exit

이 줄을 포함하는 조건: #ifdef CONFIG_UNMAP_KERNEL_AT_EL0

추측 실행
이 명령을 실행하기 직전실행 전 상태
이 명령을 실행한 직후지정한 경계를 넘는 추측 실행을 제한

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

sb 매크로 본문 · 104행

arch/arm64/include/asm/assembler.h · Linux v6.18.37

정의 선택 조건: #ifndef __ASM_ASSEMBLER_H

.macro sb alternative_if_not ARM64_HAS_SB dsb nsh isb alternative_else SB_BARRIER_INSN nop alternative_endif .endm
SYM_CODE_END(tramp_exit)

도구에 이 코드 범위의 끝을 알려 크기와 심볼 정보를 기록합니다. CPU가 이 줄 때문에 자동으로 반환하지는 않습니다.

774행 그림 · 어셈블·링크 단계

함수·코드 범위: tramp_exit

이 줄을 포함하는 조건: #ifdef CONFIG_UNMAP_KERNEL_AT_EL0

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과SYM_CODE_END(tramp_exit)
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 끝 표기 자체가 RET나 분기를 실행하지는 않습니다.
	.popsection				// .entry.tramp.text

이후 출력할 바이트를 어느 섹션에 넣을지 바꿉니다. 빌드 도구가 코드를 분류하는 작업이며 현재 CPU의 PC나 SP를 옮기는 줄은 아닙니다.

775행 그림 · 어셈블·링크 단계

이 줄을 포함하는 조건: #ifdef CONFIG_UNMAP_KERNEL_AT_EL0

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과.popsection
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

#endif /* CONFIG_UNMAP_KERNEL_AT_EL0 */

빌드 조건으로 나누었던 구간을 닫습니다. 대상:

776행 그림 · 전처리 단계

이 줄을 포함하는 조건: #ifdef CONFIG_UNMAP_KERNEL_AT_EL0

소스
처리 전전처리 전
이 줄의 효과#endif
CPU 레지스터
처리 전해당 없음
이 줄의 효과실행되는 CPU 명령이 없습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	.macro generate_el1_vector, bhb

KPTI 없이도 사용자 예외에 BHB 완화를 적용할 벡터 표를 만드는 매크로입니다. EL1 예외 입구는 정상 kernel_ventry로 만들고 EL0 입구는 지정된 bhb 완화를 포함합니다. 이 줄은 어셈블러 매크로 정의의 시작입니다. 본문은 매크로를 사용하는 위치에 펼쳐지며, 정의 자체가 런타임 함수 호출은 아닙니다.

782행 그림 · 어셈블·링크 단계

매크로 본문: generate_el1_vector

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과.macro generate_el1_vector, bhb
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 뒤의 본문은 이 이름을 사용하는 위치에 펼쳐집니다. 정의 위치를 CPU가 함수처럼 호출하지 않습니다.
.Lvector_start\@:

여기에 .Lvector_start\@라는 위치 이름을 붙입니다. 다른 줄의 분기나 주소 계산이 이 위치를 찾아올 수 있습니다. 이름 자체가 CPU 작업을 추가하지는 않습니다.

783행 그림 · 위치에 이름 붙이기

매크로 본문: generate_el1_vector

레이블
처리 전현재 배치 위치
이 줄의 효과.Lvector_start\@
CPU 레지스터
처리 전그대로
이 줄의 효과레이블 자체가 값을 바꾸지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	kernel_ventry	1, t, 64, sync		// Synchronous EL1t

EL1t 커널 실행(SP_EL0 선택) 중 발생한 동기 예외(SVC·fault·debug 등)의 128바이트 벡터 입구를 생성합니다. CPU는 벡터 표의 고정 위치를 고르며 이 입구는 pt_regs 공간·스택 검사 후 같은 종류의 실제 handler로 이어집니다. 이 줄에서 동기 예외(SVC·fault·debug 등)을 일부러 발생시키는 것은 아닙니다.

784행 그림 · 벡터·핸들러 코드 생성

매크로 본문: generate_el1_vector

매크로 인수
이 명령을 실행하기 직전호출 위치의 인수
이 명령을 실행한 직후1, t, 64, sync
생성되는 코드
이 명령을 실행하기 직전어셈블 전
이 명령을 실행한 직후128바이트 슬롯, pt_regs 공간 확보, 스택 초과 검사, 대상 예외 핸들러 분기를 생성합니다. 인자는 EL·SP 모드·실행 폭·예외 종류입니다.

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 이 한 줄은 여러 명령을 생성합니다. 레지스터 변화는 펼쳐진 본문의 각 명령과 선택된 진입 경로에 따라 생깁니다.
  • 매크로 generate_el1_vector의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
kernel_ventry 매크로 본문 · 39행

arch/arm64/kernel/entry.S · Linux v6.18.37

.macro kernel_ventry, el:req, ht:req, regsize:req, label:req .align 7 .Lventry_start\@: .if \el == 0 /* * This must be the first instruction of the EL0 vector entries. It is * skipped by the trampoline vectors, to trigger the cleanup. */ b .Lskip_tramp_vectors_cleanup\@ .if \regsize == 64 mrs x30, tpidrro_el0 msr tpidrro_el0, xzr .else mov x30, xzr .endif .Lskip_tramp_vectors_cleanup\@: .endif sub sp, sp, #PT_REGS_SIZE /* * Test whether the SP has overflowed, without corrupting a GPR. * Task and IRQ stacks are aligned so that SP & (1 << THREAD_SHIFT) * should always be zero. */ add sp, sp, x0 // sp' = sp + x0 sub x0, sp, x0 // x0' = sp' - x0 = (sp + x0) - x0 = sp tbnz x0, #THREAD_SHIFT, 0f sub x0, sp, x0 // x0'' = sp' - x0' = (sp + x0) - sp = x0 sub sp, sp, x0 // sp'' = sp' - x0 = (sp + x0) - x0 = sp b el\el\ht\()_\regsize\()_\label 0: /* * Either we've just detected an overflow, or we've taken an exception * while on the overflow stack. Either way, we won't return to * userspace, and can clobber EL0 registers to free up GPRs. */ /* Stash the original SP (minus PT_REGS_SIZE) in tpidr_el0. */ msr tpidr_el0, x0 /* Recover the original x0 value and stash it in tpidrro_el0 */ sub x0, sp, x0 msr tpidrro_el0, x0 /* Switch to the overflow stack */ adr_this_cpu sp, overflow_stack + OVERFLOW_STACK_SIZE, x0 /* * Check whether we were already on the overflow stack. This may happen * after panic() re-enables interrupts. */ mrs x0, tpidr_el0 // sp of interrupted context sub x0, sp, x0 // delta with top of overflow stack tst x0, #~(OVERFLOW_STACK_SIZE - 1) // within range? b.ne __bad_stack // no? -> bad stack pointer /* We were already on the overflow stack. Restore sp/x0 and carry on. */ sub sp, sp, x0 mrs x0, tpidrro_el0 b el\el\ht\()_\regsize\()_\label .org .Lventry_start\@ + 128 // Did we overflow the ventry slot? .endm
	kernel_ventry	1, t, 64, irq		// IRQ EL1t

EL1t 커널 실행(SP_EL0 선택) 중 발생한 IRQ의 128바이트 벡터 입구를 생성합니다. CPU는 벡터 표의 고정 위치를 고르며 이 입구는 pt_regs 공간·스택 검사 후 같은 종류의 실제 handler로 이어집니다. 이 줄에서 IRQ을 일부러 발생시키는 것은 아닙니다.

785행 그림 · 벡터·핸들러 코드 생성

매크로 본문: generate_el1_vector

매크로 인수
이 명령을 실행하기 직전호출 위치의 인수
이 명령을 실행한 직후1, t, 64, irq
생성되는 코드
이 명령을 실행하기 직전어셈블 전
이 명령을 실행한 직후128바이트 슬롯, pt_regs 공간 확보, 스택 초과 검사, 대상 예외 핸들러 분기를 생성합니다. 인자는 EL·SP 모드·실행 폭·예외 종류입니다.

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 이 한 줄은 여러 명령을 생성합니다. 레지스터 변화는 펼쳐진 본문의 각 명령과 선택된 진입 경로에 따라 생깁니다.
  • 매크로 generate_el1_vector의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
kernel_ventry 매크로 본문 · 39행

arch/arm64/kernel/entry.S · Linux v6.18.37

.macro kernel_ventry, el:req, ht:req, regsize:req, label:req .align 7 .Lventry_start\@: .if \el == 0 /* * This must be the first instruction of the EL0 vector entries. It is * skipped by the trampoline vectors, to trigger the cleanup. */ b .Lskip_tramp_vectors_cleanup\@ .if \regsize == 64 mrs x30, tpidrro_el0 msr tpidrro_el0, xzr .else mov x30, xzr .endif .Lskip_tramp_vectors_cleanup\@: .endif sub sp, sp, #PT_REGS_SIZE /* * Test whether the SP has overflowed, without corrupting a GPR. * Task and IRQ stacks are aligned so that SP & (1 << THREAD_SHIFT) * should always be zero. */ add sp, sp, x0 // sp' = sp + x0 sub x0, sp, x0 // x0' = sp' - x0 = (sp + x0) - x0 = sp tbnz x0, #THREAD_SHIFT, 0f sub x0, sp, x0 // x0'' = sp' - x0' = (sp + x0) - sp = x0 sub sp, sp, x0 // sp'' = sp' - x0 = (sp + x0) - x0 = sp b el\el\ht\()_\regsize\()_\label 0: /* * Either we've just detected an overflow, or we've taken an exception * while on the overflow stack. Either way, we won't return to * userspace, and can clobber EL0 registers to free up GPRs. */ /* Stash the original SP (minus PT_REGS_SIZE) in tpidr_el0. */ msr tpidr_el0, x0 /* Recover the original x0 value and stash it in tpidrro_el0 */ sub x0, sp, x0 msr tpidrro_el0, x0 /* Switch to the overflow stack */ adr_this_cpu sp, overflow_stack + OVERFLOW_STACK_SIZE, x0 /* * Check whether we were already on the overflow stack. This may happen * after panic() re-enables interrupts. */ mrs x0, tpidr_el0 // sp of interrupted context sub x0, sp, x0 // delta with top of overflow stack tst x0, #~(OVERFLOW_STACK_SIZE - 1) // within range? b.ne __bad_stack // no? -> bad stack pointer /* We were already on the overflow stack. Restore sp/x0 and carry on. */ sub sp, sp, x0 mrs x0, tpidrro_el0 b el\el\ht\()_\regsize\()_\label .org .Lventry_start\@ + 128 // Did we overflow the ventry slot? .endm
	kernel_ventry	1, t, 64, fiq		// FIQ EL1h

EL1t 커널 실행(SP_EL0 선택) 중 발생한 FIQ의 128바이트 벡터 입구를 생성합니다. CPU는 벡터 표의 고정 위치를 고르며 이 입구는 pt_regs 공간·스택 검사 후 같은 종류의 실제 handler로 이어집니다. 이 줄에서 FIQ을 일부러 발생시키는 것은 아닙니다.

786행 그림 · 벡터·핸들러 코드 생성

매크로 본문: generate_el1_vector

매크로 인수
이 명령을 실행하기 직전호출 위치의 인수
이 명령을 실행한 직후1, t, 64, fiq
생성되는 코드
이 명령을 실행하기 직전어셈블 전
이 명령을 실행한 직후128바이트 슬롯, pt_regs 공간 확보, 스택 초과 검사, 대상 예외 핸들러 분기를 생성합니다. 인자는 EL·SP 모드·실행 폭·예외 종류입니다.

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 이 한 줄은 여러 명령을 생성합니다. 레지스터 변화는 펼쳐진 본문의 각 명령과 선택된 진입 경로에 따라 생깁니다.
  • 매크로 generate_el1_vector의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
kernel_ventry 매크로 본문 · 39행

arch/arm64/kernel/entry.S · Linux v6.18.37

.macro kernel_ventry, el:req, ht:req, regsize:req, label:req .align 7 .Lventry_start\@: .if \el == 0 /* * This must be the first instruction of the EL0 vector entries. It is * skipped by the trampoline vectors, to trigger the cleanup. */ b .Lskip_tramp_vectors_cleanup\@ .if \regsize == 64 mrs x30, tpidrro_el0 msr tpidrro_el0, xzr .else mov x30, xzr .endif .Lskip_tramp_vectors_cleanup\@: .endif sub sp, sp, #PT_REGS_SIZE /* * Test whether the SP has overflowed, without corrupting a GPR. * Task and IRQ stacks are aligned so that SP & (1 << THREAD_SHIFT) * should always be zero. */ add sp, sp, x0 // sp' = sp + x0 sub x0, sp, x0 // x0' = sp' - x0 = (sp + x0) - x0 = sp tbnz x0, #THREAD_SHIFT, 0f sub x0, sp, x0 // x0'' = sp' - x0' = (sp + x0) - sp = x0 sub sp, sp, x0 // sp'' = sp' - x0 = (sp + x0) - x0 = sp b el\el\ht\()_\regsize\()_\label 0: /* * Either we've just detected an overflow, or we've taken an exception * while on the overflow stack. Either way, we won't return to * userspace, and can clobber EL0 registers to free up GPRs. */ /* Stash the original SP (minus PT_REGS_SIZE) in tpidr_el0. */ msr tpidr_el0, x0 /* Recover the original x0 value and stash it in tpidrro_el0 */ sub x0, sp, x0 msr tpidrro_el0, x0 /* Switch to the overflow stack */ adr_this_cpu sp, overflow_stack + OVERFLOW_STACK_SIZE, x0 /* * Check whether we were already on the overflow stack. This may happen * after panic() re-enables interrupts. */ mrs x0, tpidr_el0 // sp of interrupted context sub x0, sp, x0 // delta with top of overflow stack tst x0, #~(OVERFLOW_STACK_SIZE - 1) // within range? b.ne __bad_stack // no? -> bad stack pointer /* We were already on the overflow stack. Restore sp/x0 and carry on. */ sub sp, sp, x0 mrs x0, tpidrro_el0 b el\el\ht\()_\regsize\()_\label .org .Lventry_start\@ + 128 // Did we overflow the ventry slot? .endm
	kernel_ventry	1, t, 64, error		// Error EL1t

EL1t 커널 실행(SP_EL0 선택) 중 발생한 SError의 128바이트 벡터 입구를 생성합니다. CPU는 벡터 표의 고정 위치를 고르며 이 입구는 pt_regs 공간·스택 검사 후 같은 종류의 실제 handler로 이어집니다. 이 줄에서 SError을 일부러 발생시키는 것은 아닙니다.

787행 그림 · 벡터·핸들러 코드 생성

매크로 본문: generate_el1_vector

매크로 인수
이 명령을 실행하기 직전호출 위치의 인수
이 명령을 실행한 직후1, t, 64, error
생성되는 코드
이 명령을 실행하기 직전어셈블 전
이 명령을 실행한 직후128바이트 슬롯, pt_regs 공간 확보, 스택 초과 검사, 대상 예외 핸들러 분기를 생성합니다. 인자는 EL·SP 모드·실행 폭·예외 종류입니다.

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 이 한 줄은 여러 명령을 생성합니다. 레지스터 변화는 펼쳐진 본문의 각 명령과 선택된 진입 경로에 따라 생깁니다.
  • 매크로 generate_el1_vector의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
kernel_ventry 매크로 본문 · 39행

arch/arm64/kernel/entry.S · Linux v6.18.37

.macro kernel_ventry, el:req, ht:req, regsize:req, label:req .align 7 .Lventry_start\@: .if \el == 0 /* * This must be the first instruction of the EL0 vector entries. It is * skipped by the trampoline vectors, to trigger the cleanup. */ b .Lskip_tramp_vectors_cleanup\@ .if \regsize == 64 mrs x30, tpidrro_el0 msr tpidrro_el0, xzr .else mov x30, xzr .endif .Lskip_tramp_vectors_cleanup\@: .endif sub sp, sp, #PT_REGS_SIZE /* * Test whether the SP has overflowed, without corrupting a GPR. * Task and IRQ stacks are aligned so that SP & (1 << THREAD_SHIFT) * should always be zero. */ add sp, sp, x0 // sp' = sp + x0 sub x0, sp, x0 // x0' = sp' - x0 = (sp + x0) - x0 = sp tbnz x0, #THREAD_SHIFT, 0f sub x0, sp, x0 // x0'' = sp' - x0' = (sp + x0) - sp = x0 sub sp, sp, x0 // sp'' = sp' - x0 = (sp + x0) - x0 = sp b el\el\ht\()_\regsize\()_\label 0: /* * Either we've just detected an overflow, or we've taken an exception * while on the overflow stack. Either way, we won't return to * userspace, and can clobber EL0 registers to free up GPRs. */ /* Stash the original SP (minus PT_REGS_SIZE) in tpidr_el0. */ msr tpidr_el0, x0 /* Recover the original x0 value and stash it in tpidrro_el0 */ sub x0, sp, x0 msr tpidrro_el0, x0 /* Switch to the overflow stack */ adr_this_cpu sp, overflow_stack + OVERFLOW_STACK_SIZE, x0 /* * Check whether we were already on the overflow stack. This may happen * after panic() re-enables interrupts. */ mrs x0, tpidr_el0 // sp of interrupted context sub x0, sp, x0 // delta with top of overflow stack tst x0, #~(OVERFLOW_STACK_SIZE - 1) // within range? b.ne __bad_stack // no? -> bad stack pointer /* We were already on the overflow stack. Restore sp/x0 and carry on. */ sub sp, sp, x0 mrs x0, tpidrro_el0 b el\el\ht\()_\regsize\()_\label .org .Lventry_start\@ + 128 // Did we overflow the ventry slot? .endm
	kernel_ventry	1, h, 64, sync		// Synchronous EL1h

EL1h 커널 실행(SP_EL1 선택) 중 발생한 동기 예외(SVC·fault·debug 등)의 128바이트 벡터 입구를 생성합니다. CPU는 벡터 표의 고정 위치를 고르며 이 입구는 pt_regs 공간·스택 검사 후 같은 종류의 실제 handler로 이어집니다. 이 줄에서 동기 예외(SVC·fault·debug 등)을 일부러 발생시키는 것은 아닙니다.

789행 그림 · 벡터·핸들러 코드 생성

매크로 본문: generate_el1_vector

매크로 인수
이 명령을 실행하기 직전호출 위치의 인수
이 명령을 실행한 직후1, h, 64, sync
생성되는 코드
이 명령을 실행하기 직전어셈블 전
이 명령을 실행한 직후128바이트 슬롯, pt_regs 공간 확보, 스택 초과 검사, 대상 예외 핸들러 분기를 생성합니다. 인자는 EL·SP 모드·실행 폭·예외 종류입니다.

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 이 한 줄은 여러 명령을 생성합니다. 레지스터 변화는 펼쳐진 본문의 각 명령과 선택된 진입 경로에 따라 생깁니다.
  • 매크로 generate_el1_vector의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
kernel_ventry 매크로 본문 · 39행

arch/arm64/kernel/entry.S · Linux v6.18.37

.macro kernel_ventry, el:req, ht:req, regsize:req, label:req .align 7 .Lventry_start\@: .if \el == 0 /* * This must be the first instruction of the EL0 vector entries. It is * skipped by the trampoline vectors, to trigger the cleanup. */ b .Lskip_tramp_vectors_cleanup\@ .if \regsize == 64 mrs x30, tpidrro_el0 msr tpidrro_el0, xzr .else mov x30, xzr .endif .Lskip_tramp_vectors_cleanup\@: .endif sub sp, sp, #PT_REGS_SIZE /* * Test whether the SP has overflowed, without corrupting a GPR. * Task and IRQ stacks are aligned so that SP & (1 << THREAD_SHIFT) * should always be zero. */ add sp, sp, x0 // sp' = sp + x0 sub x0, sp, x0 // x0' = sp' - x0 = (sp + x0) - x0 = sp tbnz x0, #THREAD_SHIFT, 0f sub x0, sp, x0 // x0'' = sp' - x0' = (sp + x0) - sp = x0 sub sp, sp, x0 // sp'' = sp' - x0 = (sp + x0) - x0 = sp b el\el\ht\()_\regsize\()_\label 0: /* * Either we've just detected an overflow, or we've taken an exception * while on the overflow stack. Either way, we won't return to * userspace, and can clobber EL0 registers to free up GPRs. */ /* Stash the original SP (minus PT_REGS_SIZE) in tpidr_el0. */ msr tpidr_el0, x0 /* Recover the original x0 value and stash it in tpidrro_el0 */ sub x0, sp, x0 msr tpidrro_el0, x0 /* Switch to the overflow stack */ adr_this_cpu sp, overflow_stack + OVERFLOW_STACK_SIZE, x0 /* * Check whether we were already on the overflow stack. This may happen * after panic() re-enables interrupts. */ mrs x0, tpidr_el0 // sp of interrupted context sub x0, sp, x0 // delta with top of overflow stack tst x0, #~(OVERFLOW_STACK_SIZE - 1) // within range? b.ne __bad_stack // no? -> bad stack pointer /* We were already on the overflow stack. Restore sp/x0 and carry on. */ sub sp, sp, x0 mrs x0, tpidrro_el0 b el\el\ht\()_\regsize\()_\label .org .Lventry_start\@ + 128 // Did we overflow the ventry slot? .endm
	kernel_ventry	1, h, 64, irq		// IRQ EL1h

EL1h 커널 실행(SP_EL1 선택) 중 발생한 IRQ의 128바이트 벡터 입구를 생성합니다. CPU는 벡터 표의 고정 위치를 고르며 이 입구는 pt_regs 공간·스택 검사 후 같은 종류의 실제 handler로 이어집니다. 이 줄에서 IRQ을 일부러 발생시키는 것은 아닙니다.

790행 그림 · 벡터·핸들러 코드 생성

매크로 본문: generate_el1_vector

매크로 인수
이 명령을 실행하기 직전호출 위치의 인수
이 명령을 실행한 직후1, h, 64, irq
생성되는 코드
이 명령을 실행하기 직전어셈블 전
이 명령을 실행한 직후128바이트 슬롯, pt_regs 공간 확보, 스택 초과 검사, 대상 예외 핸들러 분기를 생성합니다. 인자는 EL·SP 모드·실행 폭·예외 종류입니다.

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 이 한 줄은 여러 명령을 생성합니다. 레지스터 변화는 펼쳐진 본문의 각 명령과 선택된 진입 경로에 따라 생깁니다.
  • 매크로 generate_el1_vector의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
kernel_ventry 매크로 본문 · 39행

arch/arm64/kernel/entry.S · Linux v6.18.37

.macro kernel_ventry, el:req, ht:req, regsize:req, label:req .align 7 .Lventry_start\@: .if \el == 0 /* * This must be the first instruction of the EL0 vector entries. It is * skipped by the trampoline vectors, to trigger the cleanup. */ b .Lskip_tramp_vectors_cleanup\@ .if \regsize == 64 mrs x30, tpidrro_el0 msr tpidrro_el0, xzr .else mov x30, xzr .endif .Lskip_tramp_vectors_cleanup\@: .endif sub sp, sp, #PT_REGS_SIZE /* * Test whether the SP has overflowed, without corrupting a GPR. * Task and IRQ stacks are aligned so that SP & (1 << THREAD_SHIFT) * should always be zero. */ add sp, sp, x0 // sp' = sp + x0 sub x0, sp, x0 // x0' = sp' - x0 = (sp + x0) - x0 = sp tbnz x0, #THREAD_SHIFT, 0f sub x0, sp, x0 // x0'' = sp' - x0' = (sp + x0) - sp = x0 sub sp, sp, x0 // sp'' = sp' - x0 = (sp + x0) - x0 = sp b el\el\ht\()_\regsize\()_\label 0: /* * Either we've just detected an overflow, or we've taken an exception * while on the overflow stack. Either way, we won't return to * userspace, and can clobber EL0 registers to free up GPRs. */ /* Stash the original SP (minus PT_REGS_SIZE) in tpidr_el0. */ msr tpidr_el0, x0 /* Recover the original x0 value and stash it in tpidrro_el0 */ sub x0, sp, x0 msr tpidrro_el0, x0 /* Switch to the overflow stack */ adr_this_cpu sp, overflow_stack + OVERFLOW_STACK_SIZE, x0 /* * Check whether we were already on the overflow stack. This may happen * after panic() re-enables interrupts. */ mrs x0, tpidr_el0 // sp of interrupted context sub x0, sp, x0 // delta with top of overflow stack tst x0, #~(OVERFLOW_STACK_SIZE - 1) // within range? b.ne __bad_stack // no? -> bad stack pointer /* We were already on the overflow stack. Restore sp/x0 and carry on. */ sub sp, sp, x0 mrs x0, tpidrro_el0 b el\el\ht\()_\regsize\()_\label .org .Lventry_start\@ + 128 // Did we overflow the ventry slot? .endm
	kernel_ventry	1, h, 64, fiq		// FIQ EL1h

EL1h 커널 실행(SP_EL1 선택) 중 발생한 FIQ의 128바이트 벡터 입구를 생성합니다. CPU는 벡터 표의 고정 위치를 고르며 이 입구는 pt_regs 공간·스택 검사 후 같은 종류의 실제 handler로 이어집니다. 이 줄에서 FIQ을 일부러 발생시키는 것은 아닙니다.

791행 그림 · 벡터·핸들러 코드 생성

매크로 본문: generate_el1_vector

매크로 인수
이 명령을 실행하기 직전호출 위치의 인수
이 명령을 실행한 직후1, h, 64, fiq
생성되는 코드
이 명령을 실행하기 직전어셈블 전
이 명령을 실행한 직후128바이트 슬롯, pt_regs 공간 확보, 스택 초과 검사, 대상 예외 핸들러 분기를 생성합니다. 인자는 EL·SP 모드·실행 폭·예외 종류입니다.

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 이 한 줄은 여러 명령을 생성합니다. 레지스터 변화는 펼쳐진 본문의 각 명령과 선택된 진입 경로에 따라 생깁니다.
  • 매크로 generate_el1_vector의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
kernel_ventry 매크로 본문 · 39행

arch/arm64/kernel/entry.S · Linux v6.18.37

.macro kernel_ventry, el:req, ht:req, regsize:req, label:req .align 7 .Lventry_start\@: .if \el == 0 /* * This must be the first instruction of the EL0 vector entries. It is * skipped by the trampoline vectors, to trigger the cleanup. */ b .Lskip_tramp_vectors_cleanup\@ .if \regsize == 64 mrs x30, tpidrro_el0 msr tpidrro_el0, xzr .else mov x30, xzr .endif .Lskip_tramp_vectors_cleanup\@: .endif sub sp, sp, #PT_REGS_SIZE /* * Test whether the SP has overflowed, without corrupting a GPR. * Task and IRQ stacks are aligned so that SP & (1 << THREAD_SHIFT) * should always be zero. */ add sp, sp, x0 // sp' = sp + x0 sub x0, sp, x0 // x0' = sp' - x0 = (sp + x0) - x0 = sp tbnz x0, #THREAD_SHIFT, 0f sub x0, sp, x0 // x0'' = sp' - x0' = (sp + x0) - sp = x0 sub sp, sp, x0 // sp'' = sp' - x0 = (sp + x0) - x0 = sp b el\el\ht\()_\regsize\()_\label 0: /* * Either we've just detected an overflow, or we've taken an exception * while on the overflow stack. Either way, we won't return to * userspace, and can clobber EL0 registers to free up GPRs. */ /* Stash the original SP (minus PT_REGS_SIZE) in tpidr_el0. */ msr tpidr_el0, x0 /* Recover the original x0 value and stash it in tpidrro_el0 */ sub x0, sp, x0 msr tpidrro_el0, x0 /* Switch to the overflow stack */ adr_this_cpu sp, overflow_stack + OVERFLOW_STACK_SIZE, x0 /* * Check whether we were already on the overflow stack. This may happen * after panic() re-enables interrupts. */ mrs x0, tpidr_el0 // sp of interrupted context sub x0, sp, x0 // delta with top of overflow stack tst x0, #~(OVERFLOW_STACK_SIZE - 1) // within range? b.ne __bad_stack // no? -> bad stack pointer /* We were already on the overflow stack. Restore sp/x0 and carry on. */ sub sp, sp, x0 mrs x0, tpidrro_el0 b el\el\ht\()_\regsize\()_\label .org .Lventry_start\@ + 128 // Did we overflow the ventry slot? .endm
	kernel_ventry	1, h, 64, error		// Error EL1h

EL1h 커널 실행(SP_EL1 선택) 중 발생한 SError의 128바이트 벡터 입구를 생성합니다. CPU는 벡터 표의 고정 위치를 고르며 이 입구는 pt_regs 공간·스택 검사 후 같은 종류의 실제 handler로 이어집니다. 이 줄에서 SError을 일부러 발생시키는 것은 아닙니다.

792행 그림 · 벡터·핸들러 코드 생성

매크로 본문: generate_el1_vector

매크로 인수
이 명령을 실행하기 직전호출 위치의 인수
이 명령을 실행한 직후1, h, 64, error
생성되는 코드
이 명령을 실행하기 직전어셈블 전
이 명령을 실행한 직후128바이트 슬롯, pt_regs 공간 확보, 스택 초과 검사, 대상 예외 핸들러 분기를 생성합니다. 인자는 EL·SP 모드·실행 폭·예외 종류입니다.

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 이 한 줄은 여러 명령을 생성합니다. 레지스터 변화는 펼쳐진 본문의 각 명령과 선택된 진입 경로에 따라 생깁니다.
  • 매크로 generate_el1_vector의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
kernel_ventry 매크로 본문 · 39행

arch/arm64/kernel/entry.S · Linux v6.18.37

.macro kernel_ventry, el:req, ht:req, regsize:req, label:req .align 7 .Lventry_start\@: .if \el == 0 /* * This must be the first instruction of the EL0 vector entries. It is * skipped by the trampoline vectors, to trigger the cleanup. */ b .Lskip_tramp_vectors_cleanup\@ .if \regsize == 64 mrs x30, tpidrro_el0 msr tpidrro_el0, xzr .else mov x30, xzr .endif .Lskip_tramp_vectors_cleanup\@: .endif sub sp, sp, #PT_REGS_SIZE /* * Test whether the SP has overflowed, without corrupting a GPR. * Task and IRQ stacks are aligned so that SP & (1 << THREAD_SHIFT) * should always be zero. */ add sp, sp, x0 // sp' = sp + x0 sub x0, sp, x0 // x0' = sp' - x0 = (sp + x0) - x0 = sp tbnz x0, #THREAD_SHIFT, 0f sub x0, sp, x0 // x0'' = sp' - x0' = (sp + x0) - sp = x0 sub sp, sp, x0 // sp'' = sp' - x0 = (sp + x0) - x0 = sp b el\el\ht\()_\regsize\()_\label 0: /* * Either we've just detected an overflow, or we've taken an exception * while on the overflow stack. Either way, we won't return to * userspace, and can clobber EL0 registers to free up GPRs. */ /* Stash the original SP (minus PT_REGS_SIZE) in tpidr_el0. */ msr tpidr_el0, x0 /* Recover the original x0 value and stash it in tpidrro_el0 */ sub x0, sp, x0 msr tpidrro_el0, x0 /* Switch to the overflow stack */ adr_this_cpu sp, overflow_stack + OVERFLOW_STACK_SIZE, x0 /* * Check whether we were already on the overflow stack. This may happen * after panic() re-enables interrupts. */ mrs x0, tpidr_el0 // sp of interrupted context sub x0, sp, x0 // delta with top of overflow stack tst x0, #~(OVERFLOW_STACK_SIZE - 1) // within range? b.ne __bad_stack // no? -> bad stack pointer /* We were already on the overflow stack. Restore sp/x0 and carry on. */ sub sp, sp, x0 mrs x0, tpidrro_el0 b el\el\ht\()_\regsize\()_\label .org .Lventry_start\@ + 128 // Did we overflow the ventry slot? .endm
	.rept	4

지정 횟수만큼 코드를 생성합니다. 실행 중 반복문을 도는 것과는 다릅니다. 4

794행 그림 · 어셈블·링크 단계

매크로 본문: generate_el1_vector

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과.rept 4
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	tramp_ventry	.Lvector_start\@, 64, 0, \bhb

AArch64 사용자 예외의 128바이트 진입 슬롯을 만듭니다. KPTI 매핑 전환은 하지 않고 \bhb가 지정한 분기 이력 완화를 적용한 뒤 대응하는 정상 vectors 슬롯의 첫 분기 다음(+4)으로 연결합니다.

795행 그림 · 벡터·핸들러 코드 생성

매크로 본문: generate_el1_vector

매크로 인수
이 명령을 실행하기 직전호출 위치의 인수
이 명령을 실행한 직후.Lvector_start\@, 64, 0, \bhb
생성되는 코드
이 명령을 실행하기 직전어셈블 전
이 명령을 실행한 직후선택한 KPTI와 BHB 완화 방식에 맞는 128바이트 슬롯을 생성하고 실제 vectors 슬롯의 +4 위치로 RET합니다.

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 이 한 줄은 여러 명령을 생성합니다. 레지스터 변화는 펼쳐진 본문의 각 명령과 선택된 진입 경로에 따라 생깁니다.
  • 매크로 generate_el1_vector의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
tramp_ventry 매크로 본문 · 684행

arch/arm64/kernel/entry.S · Linux v6.18.37

.macro tramp_ventry, vector_start, regsize, kpti, bhb .align 7 1: .if \regsize == 64 msr tpidrro_el0, x30 // Restored in kernel_ventry .endif .if \bhb == BHB_MITIGATION_LOOP /* * This sequence must appear before the first indirect branch. i.e. the * ret out of tramp_ventry. It appears here because x30 is free. */ __mitigate_spectre_bhb_loop x30 .endif // \bhb == BHB_MITIGATION_LOOP .if \bhb == BHB_MITIGATION_INSN clearbhb isb .endif // \bhb == BHB_MITIGATION_INSN .if \kpti == 1 /* * Defend against branch aliasing attacks by pushing a dummy * entry onto the return stack and using a RET instruction to * enter the full-fat kernel vectors. */ bl 2f b . 2: tramp_map_kernel x30 alternative_insn isb, nop, ARM64_WORKAROUND_QCOM_FALKOR_E1003 tramp_data_read_var x30, vectors alternative_if_not ARM64_WORKAROUND_CAVIUM_TX2_219_PRFM prfm plil1strm, [x30, #(1b - \vector_start)] alternative_else_nop_endif msr vbar_el1, x30 isb .else adr_l x30, vectors .endif // \kpti == 1 .if \bhb == BHB_MITIGATION_FW /* * The firmware sequence must appear before the first indirect branch. * i.e. the ret out of tramp_ventry. But it also needs the stack to be * mapped to save/restore the registers the SMC clobbers. */ __mitigate_spectre_bhb_fw .endif // \bhb == BHB_MITIGATION_FW add x30, x30, #(1b - \vector_start + 4) ret .org 1b + 128 // Did we overflow the ventry slot? .endm
	.endr

반복 생성할 범위의 끝입니다.

796행 그림 · 어셈블·링크 단계

매크로 본문: generate_el1_vector

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과.endr
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	.rept 4

지정 횟수만큼 코드를 생성합니다. 실행 중 반복문을 도는 것과는 다릅니다. 4

797행 그림 · 어셈블·링크 단계

매크로 본문: generate_el1_vector

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과.rept 4
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	tramp_ventry	.Lvector_start\@, 32, 0, \bhb

AArch32 사용자 예외의 128바이트 진입 슬롯을 만듭니다. KPTI 매핑 전환은 하지 않고 \bhb가 지정한 분기 이력 완화를 적용한 뒤 대응하는 정상 vectors 슬롯의 첫 분기 다음(+4)으로 연결합니다.

798행 그림 · 벡터·핸들러 코드 생성

매크로 본문: generate_el1_vector

매크로 인수
이 명령을 실행하기 직전호출 위치의 인수
이 명령을 실행한 직후.Lvector_start\@, 32, 0, \bhb
생성되는 코드
이 명령을 실행하기 직전어셈블 전
이 명령을 실행한 직후선택한 KPTI와 BHB 완화 방식에 맞는 128바이트 슬롯을 생성하고 실제 vectors 슬롯의 +4 위치로 RET합니다.

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 이 한 줄은 여러 명령을 생성합니다. 레지스터 변화는 펼쳐진 본문의 각 명령과 선택된 진입 경로에 따라 생깁니다.
  • 매크로 generate_el1_vector의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
tramp_ventry 매크로 본문 · 684행

arch/arm64/kernel/entry.S · Linux v6.18.37

.macro tramp_ventry, vector_start, regsize, kpti, bhb .align 7 1: .if \regsize == 64 msr tpidrro_el0, x30 // Restored in kernel_ventry .endif .if \bhb == BHB_MITIGATION_LOOP /* * This sequence must appear before the first indirect branch. i.e. the * ret out of tramp_ventry. It appears here because x30 is free. */ __mitigate_spectre_bhb_loop x30 .endif // \bhb == BHB_MITIGATION_LOOP .if \bhb == BHB_MITIGATION_INSN clearbhb isb .endif // \bhb == BHB_MITIGATION_INSN .if \kpti == 1 /* * Defend against branch aliasing attacks by pushing a dummy * entry onto the return stack and using a RET instruction to * enter the full-fat kernel vectors. */ bl 2f b . 2: tramp_map_kernel x30 alternative_insn isb, nop, ARM64_WORKAROUND_QCOM_FALKOR_E1003 tramp_data_read_var x30, vectors alternative_if_not ARM64_WORKAROUND_CAVIUM_TX2_219_PRFM prfm plil1strm, [x30, #(1b - \vector_start)] alternative_else_nop_endif msr vbar_el1, x30 isb .else adr_l x30, vectors .endif // \kpti == 1 .if \bhb == BHB_MITIGATION_FW /* * The firmware sequence must appear before the first indirect branch. * i.e. the ret out of tramp_ventry. But it also needs the stack to be * mapped to save/restore the registers the SMC clobbers. */ __mitigate_spectre_bhb_fw .endif // \bhb == BHB_MITIGATION_FW add x30, x30, #(1b - \vector_start + 4) ret .org 1b + 128 // Did we overflow the ventry slot? .endm
	.endr

반복 생성할 범위의 끝입니다.

799행 그림 · 어셈블·링크 단계

매크로 본문: generate_el1_vector

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과.endr
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	.endm

공통 작업의 정의를 여기서 마칩니다. RET가 아니라 빌드 도구가 읽는 경계입니다.

800행 그림 · 어셈블·링크 단계

매크로 본문: generate_el1_vector

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과.endm
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 끝 표기 자체가 RET나 분기를 실행하지는 않습니다.
	.pushsection ".entry.text", "ax"

이후 출력할 바이트를 어느 섹션에 넣을지 바꿉니다. 빌드 도구가 코드를 분류하는 작업이며 현재 CPU의 PC나 SP를 옮기는 줄은 아닙니다. ".entry.text", "ax"

803행 그림 · 어셈블·링크 단계
빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과.pushsection ".entry.text", "ax"
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	.align	11

다음 코드나 데이터가 정해진 경계에서 시작하도록 빈 공간을 채웁니다. 이 .align 인자는 바이트 수가 아니라 2의 지수입니다. 값은 11입니다.

804행 그림 · 어셈블·링크 단계
빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과.align 11
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

SYM_CODE_START(__bp_harden_el1_vectors)

이 위치부터 (__bp_harden_el1_vectors)라는 코드가 시작한다고 도구에 알려 줍니다. 정렬과 외부 공개 여부도 정의에 포함됩니다. 실제 작업은 아래의 명령들이 수행합니다.

805행 그림 · 어셈블·링크 단계

함수·코드 범위: __bp_harden_el1_vectors

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과SYM_CODE_START(__bp_harden_el1_vectors)
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

#ifdef CONFIG_MITIGATE_SPECTRE_BRANCH_HISTORY

BHB 완화를 지원하는 빌드에만 KPTI 없는 경우의 LOOP·펌웨어·명령 벡터 후보를 생성합니다. 커널 매핑을 감추는 기능과 분기 이력 완화는 별개이므로 이 표가 따로 필요합니다. 이 조건은 빌드 전처리 단계에서 포함할 코드를 고릅니다. CPU가 실행 중 전처리 조건을 검사하지는 않습니다.

806행 그림 · 전처리 단계

함수·코드 범위: __bp_harden_el1_vectors

소스
처리 전전처리 전
이 줄의 효과#ifdef CONFIG_MITIGATE_SPECTRE_BRANCH_HISTORY
CPU 레지스터
처리 전해당 없음
이 줄의 효과실행되는 CPU 명령이 없습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	generate_el1_vector	bhb=BHB_MITIGATION_LOOP

분기 반복 LOOP 방식의 벡터 표 후보를 생성합니다. KPTI 없이 커널 매핑을 유지하는 경우의 진입입니다. 실행 중 모든 후보를 순서대로 거치는 것이 아니라 CPU와 정책에 맞는 벡터가 선택됩니다.

807행 그림 · 벡터·핸들러 코드 생성

함수·코드 범위: __bp_harden_el1_vectors

이 줄을 포함하는 조건: #ifdef CONFIG_MITIGATE_SPECTRE_BRANCH_HISTORY

매크로 인수
이 명령을 실행하기 직전호출 위치의 인수
이 명령을 실행한 직후bhb=BHB_MITIGATION_LOOP
생성되는 코드
이 명령을 실행하기 직전어셈블 전
이 명령을 실행한 직후상위 EL 슬롯들과 하위 EL의 완화용 trampoline 슬롯들을 생성합니다.

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 이 한 줄은 여러 명령을 생성합니다. 레지스터 변화는 펼쳐진 본문의 각 명령과 선택된 진입 경로에 따라 생깁니다.
generate_el1_vector 매크로 본문 · 782행

arch/arm64/kernel/entry.S · Linux v6.18.37

.macro generate_el1_vector, bhb .Lvector_start\@: kernel_ventry 1, t, 64, sync // Synchronous EL1t kernel_ventry 1, t, 64, irq // IRQ EL1t kernel_ventry 1, t, 64, fiq // FIQ EL1h kernel_ventry 1, t, 64, error // Error EL1t kernel_ventry 1, h, 64, sync // Synchronous EL1h kernel_ventry 1, h, 64, irq // IRQ EL1h kernel_ventry 1, h, 64, fiq // FIQ EL1h kernel_ventry 1, h, 64, error // Error EL1h .rept 4 tramp_ventry .Lvector_start\@, 64, 0, \bhb .endr .rept 4 tramp_ventry .Lvector_start\@, 32, 0, \bhb .endr .endm
	generate_el1_vector	bhb=BHB_MITIGATION_FW

펌웨어 호출 FW 방식의 벡터 표 후보를 생성합니다. KPTI 없이 커널 매핑을 유지하는 경우의 진입입니다. 실행 중 모든 후보를 순서대로 거치는 것이 아니라 CPU와 정책에 맞는 벡터가 선택됩니다.

808행 그림 · 벡터·핸들러 코드 생성

함수·코드 범위: __bp_harden_el1_vectors

이 줄을 포함하는 조건: #ifdef CONFIG_MITIGATE_SPECTRE_BRANCH_HISTORY

매크로 인수
이 명령을 실행하기 직전호출 위치의 인수
이 명령을 실행한 직후bhb=BHB_MITIGATION_FW
생성되는 코드
이 명령을 실행하기 직전어셈블 전
이 명령을 실행한 직후상위 EL 슬롯들과 하위 EL의 완화용 trampoline 슬롯들을 생성합니다.

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 이 한 줄은 여러 명령을 생성합니다. 레지스터 변화는 펼쳐진 본문의 각 명령과 선택된 진입 경로에 따라 생깁니다.
generate_el1_vector 매크로 본문 · 782행

arch/arm64/kernel/entry.S · Linux v6.18.37

.macro generate_el1_vector, bhb .Lvector_start\@: kernel_ventry 1, t, 64, sync // Synchronous EL1t kernel_ventry 1, t, 64, irq // IRQ EL1t kernel_ventry 1, t, 64, fiq // FIQ EL1h kernel_ventry 1, t, 64, error // Error EL1t kernel_ventry 1, h, 64, sync // Synchronous EL1h kernel_ventry 1, h, 64, irq // IRQ EL1h kernel_ventry 1, h, 64, fiq // FIQ EL1h kernel_ventry 1, h, 64, error // Error EL1h .rept 4 tramp_ventry .Lvector_start\@, 64, 0, \bhb .endr .rept 4 tramp_ventry .Lvector_start\@, 32, 0, \bhb .endr .endm
	generate_el1_vector	bhb=BHB_MITIGATION_INSN

CLEARBHB 명령 INSN 방식의 벡터 표 후보를 생성합니다. KPTI 없이 커널 매핑을 유지하는 경우의 진입입니다. 실행 중 모든 후보를 순서대로 거치는 것이 아니라 CPU와 정책에 맞는 벡터가 선택됩니다.

809행 그림 · 벡터·핸들러 코드 생성

함수·코드 범위: __bp_harden_el1_vectors

이 줄을 포함하는 조건: #ifdef CONFIG_MITIGATE_SPECTRE_BRANCH_HISTORY

매크로 인수
이 명령을 실행하기 직전호출 위치의 인수
이 명령을 실행한 직후bhb=BHB_MITIGATION_INSN
생성되는 코드
이 명령을 실행하기 직전어셈블 전
이 명령을 실행한 직후상위 EL 슬롯들과 하위 EL의 완화용 trampoline 슬롯들을 생성합니다.

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 이 한 줄은 여러 명령을 생성합니다. 레지스터 변화는 펼쳐진 본문의 각 명령과 선택된 진입 경로에 따라 생깁니다.
generate_el1_vector 매크로 본문 · 782행

arch/arm64/kernel/entry.S · Linux v6.18.37

.macro generate_el1_vector, bhb .Lvector_start\@: kernel_ventry 1, t, 64, sync // Synchronous EL1t kernel_ventry 1, t, 64, irq // IRQ EL1t kernel_ventry 1, t, 64, fiq // FIQ EL1h kernel_ventry 1, t, 64, error // Error EL1t kernel_ventry 1, h, 64, sync // Synchronous EL1h kernel_ventry 1, h, 64, irq // IRQ EL1h kernel_ventry 1, h, 64, fiq // FIQ EL1h kernel_ventry 1, h, 64, error // Error EL1h .rept 4 tramp_ventry .Lvector_start\@, 64, 0, \bhb .endr .rept 4 tramp_ventry .Lvector_start\@, 32, 0, \bhb .endr .endm
#endif /* CONFIG_MITIGATE_SPECTRE_BRANCH_HISTORY */

빌드 조건으로 나누었던 구간을 닫습니다. 대상:

810행 그림 · 전처리 단계

함수·코드 범위: __bp_harden_el1_vectors

이 줄을 포함하는 조건: #ifdef CONFIG_MITIGATE_SPECTRE_BRANCH_HISTORY

소스
처리 전전처리 전
이 줄의 효과#endif
CPU 레지스터
처리 전해당 없음
이 줄의 효과실행되는 CPU 명령이 없습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

SYM_CODE_END(__bp_harden_el1_vectors)

도구에 이 코드 범위의 끝을 알려 크기와 심볼 정보를 기록합니다. CPU가 이 줄 때문에 자동으로 반환하지는 않습니다.

811행 그림 · 어셈블·링크 단계

함수·코드 범위: __bp_harden_el1_vectors

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과SYM_CODE_END(__bp_harden_el1_vectors)
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 끝 표기 자체가 RET나 분기를 실행하지는 않습니다.
	.popsection

이후 출력할 바이트를 어느 섹션에 넣을지 바꿉니다. 빌드 도구가 코드를 분류하는 작업이며 현재 CPU의 PC나 SP를 옮기는 줄은 아닙니다.

812행 그림 · 어셈블·링크 단계
빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과.popsection
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

cpu_switch_to: 태스크 상태 교체

원본 814–851행

SYM_FUNC_START(cpu_switch_to)

이 위치부터 (cpu_switch_to)라는 코드가 시작한다고 도구에 알려 줍니다. 정렬과 외부 공개 여부도 정의에 포함됩니다. ARM64 함수 시작 매크로는 간접 호출의 도착점을 표시하는 BTI도 추가합니다.

823행 그림 · 어셈블·링크 단계

함수·코드 범위: cpu_switch_to

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과SYM_FUNC_START(cpu_switch_to)
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 함수 시작 매크로는 심벌·정렬 정보와 BTI 진입 명령 등을 배치할 수 있습니다. 생성된 명령의 실행과 심벌 선언을 구분합니다.
	save_and_disable_daif x11

현재 예외 마스크를 기억해 둔 뒤 상태 교체 도중 예외가 끼어들지 않도록 마스크합니다. 작업이 끝나면 원래 값을 복구할 수 있습니다. 여기의 인자는 x11입니다.

SP/current가 교체되는 동안 예외 관찰 상태가 섞이지 않도록 DAIF를 보존하고 마스크합니다.

824행 그림 · 매크로 전체의 상태 변화

함수·코드 범위: cpu_switch_to

읽는 값DAIF
x11
이 명령을 실행하기 직전명령 직전 x11의 값
이 명령을 실행한 직후명령 직전 DAIF의 값
DAIF
이 명령을 실행하기 직전명령 직전 DAIF의 값
이 명령을 실행한 직후D=A=I=F=1

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

save_and_disable_daif 매크로 본문 · 44행

arch/arm64/include/asm/assembler.h · Linux v6.18.37

정의 선택 조건: #ifndef __ASM_ASSEMBLER_H

.macro save_and_disable_daif, flags mrs \flags, daif msr daifset, #0xf .endm
	mov	x10, #THREAD_CPU_CONTEXT

x10에 THREAD_CPU_CONTEXT라는 값을 넣습니다. 원래 x10의 내용은 덮어쓰며, 다른 레지스터나 그 주소의 메모리까지 복사하지는 않습니다.

825행 그림 · 값 복사

함수·코드 범위: cpu_switch_to

x10
이 명령을 실행하기 직전명령 직전 x10의 값
이 명령을 실행한 직후THREAD_CPU_CONTEXT

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 화살표는 값의 변화를 뜻합니다. 주소값을 옮겨도 그 주소의 메모리 내용은 복사하지 않습니다.
	add	x8, x0, x10

x0를 기준으로 x10을 더해 x8에 결과를 둡니다. 주소 계산에 쓰이는 경우에는 구조체의 필드나 다음 저장 위치를 찾는 동작입니다. 이 형태는 조건 플래그를 바꾸지 않습니다.

826행 그림 · 레지스터 상태

함수·코드 범위: cpu_switch_to

읽는 값x0x10
x8
이 명령을 실행하기 직전명령 직전 x8의 값
이 명령을 실행한 직후명령 직전 x0의 값 + 명령 직전 x10의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 결과는 목적 레지스터 폭에 맞습니다. 이 형식은 NZCV를 바꾸지 않습니다.
	mov	x9, sp

현재 SP의 숫자를 x9에 기억해 둡니다. 스택 자체는 이동하거나 복사되지 않습니다. 나중에 이 값으로 원래 위치를 찾을 수 있습니다.

827행 그림 · 값 복사

함수·코드 범위: cpu_switch_to

읽는 값sp
x9
이 명령을 실행하기 직전명령 직전 x9의 값
이 명령을 실행한 직후명령 직전 sp의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 화살표는 값의 변화를 뜻합니다. 주소값을 옮겨도 그 주소의 메모리 내용은 복사하지 않습니다.
	stp	x19, x20, [x8], #16		// store callee-saved registers

x19, x20의 값을 x8가 가리키는 메모리 위치에 기록합니다. 레지스터 값을 나중에 다시 쓰거나 다른 코드가 읽을 수 있도록 메모리에 남기는 줄입니다. 첫 위치에는 x19, 그보다 8바이트 높은 위치에는 x20의 값이 들어갑니다. 이번 저장을 마친 뒤 기준 포인터를 다음 위치로 옮깁니다.

828행 그림 · 메모리 쓰기

함수·코드 범위: cpu_switch_to

읽는 값x8x19x20
메모리[이전 x8] · 8바이트
이 명령을 실행하기 직전기존 메모리 값
이 명령을 실행한 직후명령 직전 x19의 값
메모리[이전 x8 + 8] · 8바이트
이 명령을 실행하기 직전기존 메모리 값
이 명령을 실행한 직후명령 직전 x20의 값
x8
이 명령을 실행하기 직전명령 직전 x8의 값
이 명령을 실행한 직후명령 직전 x8의 값 + (16)

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 대괄호 밖의 오프셋은 후위 갱신입니다. 이전 주소로 접근한 뒤 기준 레지스터를 갱신합니다.
	stp	x21, x22, [x8], #16

x21, x22의 값을 x8가 가리키는 메모리 위치에 기록합니다. 레지스터 값을 나중에 다시 쓰거나 다른 코드가 읽을 수 있도록 메모리에 남기는 줄입니다. 첫 위치에는 x21, 그보다 8바이트 높은 위치에는 x22의 값이 들어갑니다. 이번 저장을 마친 뒤 기준 포인터를 다음 위치로 옮깁니다.

829행 그림 · 메모리 쓰기

함수·코드 범위: cpu_switch_to

읽는 값x8x21x22
메모리[이전 x8] · 8바이트
이 명령을 실행하기 직전기존 메모리 값
이 명령을 실행한 직후명령 직전 x21의 값
메모리[이전 x8 + 8] · 8바이트
이 명령을 실행하기 직전기존 메모리 값
이 명령을 실행한 직후명령 직전 x22의 값
x8
이 명령을 실행하기 직전명령 직전 x8의 값
이 명령을 실행한 직후명령 직전 x8의 값 + (16)

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 대괄호 밖의 오프셋은 후위 갱신입니다. 이전 주소로 접근한 뒤 기준 레지스터를 갱신합니다.
	stp	x23, x24, [x8], #16

x23, x24의 값을 x8가 가리키는 메모리 위치에 기록합니다. 레지스터 값을 나중에 다시 쓰거나 다른 코드가 읽을 수 있도록 메모리에 남기는 줄입니다. 첫 위치에는 x23, 그보다 8바이트 높은 위치에는 x24의 값이 들어갑니다. 이번 저장을 마친 뒤 기준 포인터를 다음 위치로 옮깁니다.

830행 그림 · 메모리 쓰기

함수·코드 범위: cpu_switch_to

읽는 값x8x23x24
메모리[이전 x8] · 8바이트
이 명령을 실행하기 직전기존 메모리 값
이 명령을 실행한 직후명령 직전 x23의 값
메모리[이전 x8 + 8] · 8바이트
이 명령을 실행하기 직전기존 메모리 값
이 명령을 실행한 직후명령 직전 x24의 값
x8
이 명령을 실행하기 직전명령 직전 x8의 값
이 명령을 실행한 직후명령 직전 x8의 값 + (16)

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 대괄호 밖의 오프셋은 후위 갱신입니다. 이전 주소로 접근한 뒤 기준 레지스터를 갱신합니다.
	stp	x25, x26, [x8], #16

x25, x26의 값을 x8가 가리키는 메모리 위치에 기록합니다. 레지스터 값을 나중에 다시 쓰거나 다른 코드가 읽을 수 있도록 메모리에 남기는 줄입니다. 첫 위치에는 x25, 그보다 8바이트 높은 위치에는 x26의 값이 들어갑니다. 이번 저장을 마친 뒤 기준 포인터를 다음 위치로 옮깁니다.

831행 그림 · 메모리 쓰기

함수·코드 범위: cpu_switch_to

읽는 값x8x25x26
메모리[이전 x8] · 8바이트
이 명령을 실행하기 직전기존 메모리 값
이 명령을 실행한 직후명령 직전 x25의 값
메모리[이전 x8 + 8] · 8바이트
이 명령을 실행하기 직전기존 메모리 값
이 명령을 실행한 직후명령 직전 x26의 값
x8
이 명령을 실행하기 직전명령 직전 x8의 값
이 명령을 실행한 직후명령 직전 x8의 값 + (16)

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 대괄호 밖의 오프셋은 후위 갱신입니다. 이전 주소로 접근한 뒤 기준 레지스터를 갱신합니다.
	stp	x27, x28, [x8], #16

x27, x28의 값을 x8가 가리키는 메모리 위치에 기록합니다. 레지스터 값을 나중에 다시 쓰거나 다른 코드가 읽을 수 있도록 메모리에 남기는 줄입니다. 첫 위치에는 x27, 그보다 8바이트 높은 위치에는 x28의 값이 들어갑니다. 이번 저장을 마친 뒤 기준 포인터를 다음 위치로 옮깁니다.

832행 그림 · 메모리 쓰기

함수·코드 범위: cpu_switch_to

읽는 값x8x27x28
메모리[이전 x8] · 8바이트
이 명령을 실행하기 직전기존 메모리 값
이 명령을 실행한 직후명령 직전 x27의 값
메모리[이전 x8 + 8] · 8바이트
이 명령을 실행하기 직전기존 메모리 값
이 명령을 실행한 직후명령 직전 x28의 값
x8
이 명령을 실행하기 직전명령 직전 x8의 값
이 명령을 실행한 직후명령 직전 x8의 값 + (16)

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 대괄호 밖의 오프셋은 후위 갱신입니다. 이전 주소로 접근한 뒤 기준 레지스터를 갱신합니다.
	stp	x29, x9, [x8], #16

x29, x9의 값을 x8가 가리키는 메모리 위치에 기록합니다. 레지스터 값을 나중에 다시 쓰거나 다른 코드가 읽을 수 있도록 메모리에 남기는 줄입니다. 첫 위치에는 x29, 그보다 8바이트 높은 위치에는 x9의 값이 들어갑니다. 이번 저장을 마친 뒤 기준 포인터를 다음 위치로 옮깁니다.

이전 태스크의 FP와 SP를 연속해서 저장합니다.

833행 그림 · 메모리 쓰기

함수·코드 범위: cpu_switch_to

읽는 값x8x29x9
메모리[이전 x8] · 8바이트
이 명령을 실행하기 직전기존 메모리 값
이 명령을 실행한 직후명령 직전 x29의 값
메모리[이전 x8 + 8] · 8바이트
이 명령을 실행하기 직전기존 메모리 값
이 명령을 실행한 직후명령 직전 x9의 값
x8
이 명령을 실행하기 직전명령 직전 x8의 값
이 명령을 실행한 직후명령 직전 x8의 값 + (16)

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 대괄호 밖의 오프셋은 후위 갱신입니다. 이전 주소로 접근한 뒤 기준 레지스터를 갱신합니다.
	str	lr, [x8]

lr의 값을 x8가 가리키는 메모리 위치에 기록합니다. 레지스터 값을 나중에 다시 쓰거나 다른 코드가 읽을 수 있도록 메모리에 남기는 줄입니다.

834행 그림 · 메모리 쓰기

함수·코드 범위: cpu_switch_to

읽는 값x8lr
메모리[이전 x8] · 8바이트
이 명령을 실행하기 직전기존 메모리 값
이 명령을 실행한 직후명령 직전 lr의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 주소 계산에 의한 기준 레지스터 갱신은 없습니다. 다만 LDR/LDP의 목적 레지스터가 기준 레지스터와 같으면 읽은 값이 그 레지스터를 덮어씁니다.
	add	x8, x1, x10

x1를 기준으로 x10을 더해 x8에 결과를 둡니다. 주소 계산에 쓰이는 경우에는 구조체의 필드나 다음 저장 위치를 찾는 동작입니다. 이 형태는 조건 플래그를 바꾸지 않습니다.

835행 그림 · 레지스터 상태

함수·코드 범위: cpu_switch_to

읽는 값x1x10
x8
이 명령을 실행하기 직전명령 직전 x8의 값
이 명령을 실행한 직후명령 직전 x1의 값 + 명령 직전 x10의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 결과는 목적 레지스터 폭에 맞습니다. 이 형식은 NZCV를 바꾸지 않습니다.
	ldp	x19, x20, [x8], #16		// restore callee-saved registers

x8가 가리키는 메모리 위치에 보관된 값을 x19, x20로 가져옵니다. 주소 숫자를 옮기는 MOV와 달리 실제 메모리를 읽습니다. 첫 위치에서 x19, 그보다 8바이트 높은 위치에서 x20의 값을 읽습니다. 읽은 뒤 기준 포인터를 옮깁니다. SP가 증가하는 경우는 사용한 스택 공간을 해제하는 단계입니다.

836행 그림 · 메모리 읽기

함수·코드 범위: cpu_switch_to

읽는 값x8
메모리[이전 x8] · 8바이트
이 명령을 실행하기 직전M0 (이 주소에 저장된 값)
이 명령을 실행한 직후M0 (이 주소에 저장된 값)
x19
이 명령을 실행하기 직전명령 직전 x19의 값
이 명령을 실행한 직후M0
메모리[이전 x8 + 8] · 8바이트
이 명령을 실행하기 직전M1 (이 주소에 저장된 값)
이 명령을 실행한 직후M1 (이 주소에 저장된 값)
x20
이 명령을 실행하기 직전명령 직전 x20의 값
이 명령을 실행한 직후M1
x8
이 명령을 실행하기 직전명령 직전 x8의 값
이 명령을 실행한 직후명령 직전 x8의 값 + (16)

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 대괄호 밖의 오프셋은 후위 갱신입니다. 이전 주소로 접근한 뒤 기준 레지스터를 갱신합니다.
	ldp	x21, x22, [x8], #16

x8가 가리키는 메모리 위치에 보관된 값을 x21, x22로 가져옵니다. 주소 숫자를 옮기는 MOV와 달리 실제 메모리를 읽습니다. 첫 위치에서 x21, 그보다 8바이트 높은 위치에서 x22의 값을 읽습니다. 읽은 뒤 기준 포인터를 옮깁니다. SP가 증가하는 경우는 사용한 스택 공간을 해제하는 단계입니다.

837행 그림 · 메모리 읽기

함수·코드 범위: cpu_switch_to

읽는 값x8
메모리[이전 x8] · 8바이트
이 명령을 실행하기 직전M0 (이 주소에 저장된 값)
이 명령을 실행한 직후M0 (이 주소에 저장된 값)
x21
이 명령을 실행하기 직전명령 직전 x21의 값
이 명령을 실행한 직후M0
메모리[이전 x8 + 8] · 8바이트
이 명령을 실행하기 직전M1 (이 주소에 저장된 값)
이 명령을 실행한 직후M1 (이 주소에 저장된 값)
x22
이 명령을 실행하기 직전명령 직전 x22의 값
이 명령을 실행한 직후M1
x8
이 명령을 실행하기 직전명령 직전 x8의 값
이 명령을 실행한 직후명령 직전 x8의 값 + (16)

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 대괄호 밖의 오프셋은 후위 갱신입니다. 이전 주소로 접근한 뒤 기준 레지스터를 갱신합니다.
	ldp	x23, x24, [x8], #16

x8가 가리키는 메모리 위치에 보관된 값을 x23, x24로 가져옵니다. 주소 숫자를 옮기는 MOV와 달리 실제 메모리를 읽습니다. 첫 위치에서 x23, 그보다 8바이트 높은 위치에서 x24의 값을 읽습니다. 읽은 뒤 기준 포인터를 옮깁니다. SP가 증가하는 경우는 사용한 스택 공간을 해제하는 단계입니다.

838행 그림 · 메모리 읽기

함수·코드 범위: cpu_switch_to

읽는 값x8
메모리[이전 x8] · 8바이트
이 명령을 실행하기 직전M0 (이 주소에 저장된 값)
이 명령을 실행한 직후M0 (이 주소에 저장된 값)
x23
이 명령을 실행하기 직전명령 직전 x23의 값
이 명령을 실행한 직후M0
메모리[이전 x8 + 8] · 8바이트
이 명령을 실행하기 직전M1 (이 주소에 저장된 값)
이 명령을 실행한 직후M1 (이 주소에 저장된 값)
x24
이 명령을 실행하기 직전명령 직전 x24의 값
이 명령을 실행한 직후M1
x8
이 명령을 실행하기 직전명령 직전 x8의 값
이 명령을 실행한 직후명령 직전 x8의 값 + (16)

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 대괄호 밖의 오프셋은 후위 갱신입니다. 이전 주소로 접근한 뒤 기준 레지스터를 갱신합니다.
	ldp	x25, x26, [x8], #16

x8가 가리키는 메모리 위치에 보관된 값을 x25, x26로 가져옵니다. 주소 숫자를 옮기는 MOV와 달리 실제 메모리를 읽습니다. 첫 위치에서 x25, 그보다 8바이트 높은 위치에서 x26의 값을 읽습니다. 읽은 뒤 기준 포인터를 옮깁니다. SP가 증가하는 경우는 사용한 스택 공간을 해제하는 단계입니다.

839행 그림 · 메모리 읽기

함수·코드 범위: cpu_switch_to

읽는 값x8
메모리[이전 x8] · 8바이트
이 명령을 실행하기 직전M0 (이 주소에 저장된 값)
이 명령을 실행한 직후M0 (이 주소에 저장된 값)
x25
이 명령을 실행하기 직전명령 직전 x25의 값
이 명령을 실행한 직후M0
메모리[이전 x8 + 8] · 8바이트
이 명령을 실행하기 직전M1 (이 주소에 저장된 값)
이 명령을 실행한 직후M1 (이 주소에 저장된 값)
x26
이 명령을 실행하기 직전명령 직전 x26의 값
이 명령을 실행한 직후M1
x8
이 명령을 실행하기 직전명령 직전 x8의 값
이 명령을 실행한 직후명령 직전 x8의 값 + (16)

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 대괄호 밖의 오프셋은 후위 갱신입니다. 이전 주소로 접근한 뒤 기준 레지스터를 갱신합니다.
	ldp	x27, x28, [x8], #16

x8가 가리키는 메모리 위치에 보관된 값을 x27, x28로 가져옵니다. 주소 숫자를 옮기는 MOV와 달리 실제 메모리를 읽습니다. 첫 위치에서 x27, 그보다 8바이트 높은 위치에서 x28의 값을 읽습니다. 읽은 뒤 기준 포인터를 옮깁니다. SP가 증가하는 경우는 사용한 스택 공간을 해제하는 단계입니다.

840행 그림 · 메모리 읽기

함수·코드 범위: cpu_switch_to

읽는 값x8
메모리[이전 x8] · 8바이트
이 명령을 실행하기 직전M0 (이 주소에 저장된 값)
이 명령을 실행한 직후M0 (이 주소에 저장된 값)
x27
이 명령을 실행하기 직전명령 직전 x27의 값
이 명령을 실행한 직후M0
메모리[이전 x8 + 8] · 8바이트
이 명령을 실행하기 직전M1 (이 주소에 저장된 값)
이 명령을 실행한 직후M1 (이 주소에 저장된 값)
x28
이 명령을 실행하기 직전명령 직전 x28의 값
이 명령을 실행한 직후M1
x8
이 명령을 실행하기 직전명령 직전 x8의 값
이 명령을 실행한 직후명령 직전 x8의 값 + (16)

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 대괄호 밖의 오프셋은 후위 갱신입니다. 이전 주소로 접근한 뒤 기준 레지스터를 갱신합니다.
	ldp	x29, x9, [x8], #16

x8가 가리키는 메모리 위치에 보관된 값을 x29, x9로 가져옵니다. 주소 숫자를 옮기는 MOV와 달리 실제 메모리를 읽습니다. 첫 위치에서 x29, 그보다 8바이트 높은 위치에서 x9의 값을 읽습니다. 읽은 뒤 기준 포인터를 옮깁니다. SP가 증가하는 경우는 사용한 스택 공간을 해제하는 단계입니다.

841행 그림 · 메모리 읽기

함수·코드 범위: cpu_switch_to

읽는 값x8
메모리[이전 x8] · 8바이트
이 명령을 실행하기 직전M0 (이 주소에 저장된 값)
이 명령을 실행한 직후M0 (이 주소에 저장된 값)
x29
이 명령을 실행하기 직전명령 직전 x29의 값
이 명령을 실행한 직후M0
메모리[이전 x8 + 8] · 8바이트
이 명령을 실행하기 직전M1 (이 주소에 저장된 값)
이 명령을 실행한 직후M1 (이 주소에 저장된 값)
x9
이 명령을 실행하기 직전명령 직전 x9의 값
이 명령을 실행한 직후M1
x8
이 명령을 실행하기 직전명령 직전 x8의 값
이 명령을 실행한 직후명령 직전 x8의 값 + (16)

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 대괄호 밖의 오프셋은 후위 갱신입니다. 이전 주소로 접근한 뒤 기준 레지스터를 갱신합니다.
	ldr	lr, [x8]

x8가 가리키는 메모리 위치에 보관된 값을 lr로 가져옵니다. 주소 숫자를 옮기는 MOV와 달리 실제 메모리를 읽습니다.

842행 그림 · 메모리 읽기

함수·코드 범위: cpu_switch_to

읽는 값x8
메모리[이전 x8] · 8바이트
이 명령을 실행하기 직전M0 (이 주소에 저장된 값)
이 명령을 실행한 직후M0 (이 주소에 저장된 값)
lr
이 명령을 실행하기 직전명령 직전 lr의 값
이 명령을 실행한 직후M0

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 주소 계산에 의한 기준 레지스터 갱신은 없습니다. 다만 LDR/LDP의 목적 레지스터가 기준 레지스터와 같으면 읽은 값이 그 레지스터를 덮어씁니다.
	mov	sp, x9

이제 스택으로 사용할 위치를 x9의 값으로 바꿉니다. 메모리 내용을 옮기는 작업은 아닙니다. 다음 저장·호출부터 새 스택 위치를 사용합니다.

이 시점부터 다음 태스크의 커널 스택을 사용합니다.

843행 그림 · 값 복사

함수·코드 범위: cpu_switch_to

읽는 값x9
sp
이 명령을 실행하기 직전명령 직전 sp의 값
이 명령을 실행한 직후명령 직전 x9의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 화살표는 값의 변화를 뜻합니다. 주소값을 옮겨도 그 주소의 메모리 내용은 복사하지 않습니다.
	msr	sp_el0, x1

SP_EL0에 다음 task_struct를 기록하여 커널의 current 조회를 새 task로 바꿉니다. 실제 스택은 앞의 MOV SP로 이미 바뀌었으며 SP_EL0를 그 스택 주소로 쓰는 문맥이 아닙니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.

current를 다음 task_struct로 바꿉니다.

844행 그림 · 시스템 레지스터 쓰기

함수·코드 범위: cpu_switch_to

읽는 값x1
SP_EL0
이 명령을 실행하기 직전명령 직전 SP_EL0의 값
이 명령을 실행한 직후명령 직전 x1의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 접근 권한과 VHE 별칭 규칙에 따라 실제 대상이 정해집니다. 뒤의 ISB·DSB가 필요한지는 이 레지스터와 문맥에 따릅니다.
	ptrauth_keys_install_kernel x1, x8, x9, x10

x1이 가리키는 다음 task의 커널 instruction-address 인증 키를 설치합니다. x8~x10은 임시 레지스터입니다. 태스크마다 다른 PAC 키를 쓰므로 SP/current를 바꾼 전환에서 이전 task의 키를 그대로 유지하면 안 됩니다.

845행 그림 · 매크로 안의 상태 변화

함수·코드 범위: cpu_switch_to

입력
이 명령을 실행하기 직전호출 직전 값
이 명령을 실행한 직후x1, x8, x9, x10
주요 결과
이 명령을 실행하기 직전본문 실행 전
이 명령을 실행한 직후현재 구성에 맞는 커널 instruction authentication 키를 설치합니다. 실제 키 쓰기와 대체 패치 조건은 정의 원문에 있습니다.

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 여러 명령과 내부 분기를 묶은 그림입니다. 작업 레지스터를 모두 보존한다는 뜻은 아니며, 정확한 변경 순서는 아래 매크로 본문과 줄별 그림에서 확인합니다.
ptrauth_keys_install_kernel 매크로 본문 · 26행

arch/arm64/include/asm/asm_pointer_auth.h · Linux v6.18.37

정의 선택 조건: #ifndef __ASM_ASM_POINTER_AUTH_H / #ifdef CONFIG_ARM64_PTR_AUTH_KERNEL

.macro ptrauth_keys_install_kernel tsk, tmp1, tmp2, tmp3 alternative_if ARM64_HAS_ADDRESS_AUTH __ptrauth_keys_install_kernel_nosync \tsk, \tmp1, \tmp2, \tmp3 isb alternative_else_nop_endif .endm
ptrauth_keys_install_kernel 매크로 본문 · 41행

arch/arm64/include/asm/asm_pointer_auth.h · Linux v6.18.37

정의 선택 조건: #ifndef __ASM_ASM_POINTER_AUTH_H / 앞 조건의 반대

.macro ptrauth_keys_install_kernel tsk, tmp1, tmp2, tmp3 .endm
	scs_save x0

일반 스택과 별도로 보호하는 반환 주소 스택의 현재 위치를 태스크에 기억해 둡니다. 여기의 인자는 x0입니다.

846행 그림 · 매크로 전체의 상태 변화

함수·코드 범위: cpu_switch_to

읽는 값x18x0
메모리[x0 + TSK_TI_SCS_SP]
이 명령을 실행하기 직전기존 값
이 명령을 실행한 직후명령 직전 x18의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • CONFIG_SHADOW_CALL_STACK이 없으면 저장 명령이 생성되지 않습니다.
scs_save 매크로 본문 · 18행

arch/arm64/include/asm/scs.h · Linux v6.18.37

정의 선택 조건: #ifndef _ASM_SCS_H / #ifdef __ASSEMBLY__ / #ifdef CONFIG_SHADOW_CALL_STACK

.macro scs_save tsk str scs_sp, [\tsk, #TSK_TI_SCS_SP] .endm
scs_save 매크로 본문 · 25행

arch/arm64/include/asm/scs.h · Linux v6.18.37

정의 선택 조건: #ifndef _ASM_SCS_H / #ifdef __ASSEMBLY__ / 앞 조건의 반대

.macro scs_save tsk .endm
	scs_load_current

태스크에 저장해 둔 반환 주소 보호용 스택 위치를 되살립니다. 일반 SP를 읽는 것과는 다른 상태입니다. 여기의 인자는 없음입니다.

847행 그림 · 매크로 전체의 상태 변화

함수·코드 범위: cpu_switch_to

x18
이 명령을 실행하기 직전명령 직전 x18의 값
이 명령을 실행한 직후메모리[명령 직전 SP_EL0의 값 + TSK_TI_SCS_SP]

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • CONFIG_SHADOW_CALL_STACK이 없으면 빈 매크로입니다.
scs_load_current 매크로 본문 · 13행

arch/arm64/include/asm/scs.h · Linux v6.18.37

정의 선택 조건: #ifndef _ASM_SCS_H / #ifdef __ASSEMBLY__ / #ifdef CONFIG_SHADOW_CALL_STACK

.macro scs_load_current get_current_task scs_sp ldr scs_sp, [scs_sp, #TSK_TI_SCS_SP] .endm
scs_load_current 매크로 본문 · 22행

arch/arm64/include/asm/scs.h · Linux v6.18.37

정의 선택 조건: #ifndef _ASM_SCS_H / #ifdef __ASSEMBLY__ / 앞 조건의 반대

.macro scs_load_current .endm
	restore_irq x11

상태 전환 전에 기록해 둔 예외 마스크를 돌려놓습니다. 무조건 인터럽트를 켜는 것이 아니라 원래 상태를 복원합니다. 여기의 인자는 x11입니다.

848행 그림 · 매크로 전체의 상태 변화

함수·코드 범위: cpu_switch_to

읽는 값x11
DAIF
이 명령을 실행하기 직전명령 직전 DAIF의 값
이 명령을 실행한 직후명령 직전 x11의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

restore_irq 매크로 본문 · 54행

arch/arm64/include/asm/assembler.h · Linux v6.18.37

정의 선택 조건: #ifndef __ASM_ASSEMBLER_H

.macro restore_irq, flags msr daif, \flags .endm
	ret

LR(x30)에 적힌 주소로 실행을 돌립니다. 그 주소가 어떻게 준비됐는지가 중요합니다. 정상 함수의 호출자일 수도 있고, 절전 복귀나 trampoline에서 별도로 기록한 재개 지점일 수도 있습니다.

다음 태스크에 저장된 LR로 실행을 이어갑니다. x0는 이전 태스크 포인터를 유지합니다.

849행 그림 · 호출과 실행 위치

함수·코드 범위: cpu_switch_to

읽는 값lr
PC
이 명령을 실행하기 직전명령 직전 PC의 값
이 명령을 실행한 직후명령 직전 lr의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 새 복귀 주소를 LR에 기록하지 않습니다. RET 역시 메모리의 스택 값을 자동으로 꺼내는 명령은 아닙니다.
SYM_FUNC_END(cpu_switch_to)

도구에 이 코드 범위의 끝을 알려 크기와 심볼 정보를 기록합니다. CPU가 이 줄 때문에 자동으로 반환하지는 않습니다.

850행 그림 · 어셈블·링크 단계

함수·코드 범위: cpu_switch_to

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과SYM_FUNC_END(cpu_switch_to)
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 끝 표기 자체가 RET나 분기를 실행하지는 않습니다.
NOKPROBE(cpu_switch_to)

cpu_switch_to을 kprobe 설치 금지 목록에 넣습니다. 스택·레지스터·주소 공간을 바꾸는 예외/전환 경로 중간에 probe 예외를 추가하지 못하게 보호합니다. CONFIG_KPROBES가 꺼지면 이 목록 항목도 필요 없습니다. 빌드할 때 심벌을 가리키는 메타데이터를 만드는 표기이며, 이 위치에서 함수를 호출하는 명령은 아닙니다.

851행 그림 · 어셈블·링크 단계
빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과NOKPROBE(cpu_switch_to)
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

fork 뒤의 첫 실행

원본 852–866행

SYM_CODE_START(ret_from_fork)

이 위치부터 (ret_from_fork)라는 코드가 시작한다고 도구에 알려 줍니다. 정렬과 외부 공개 여부도 정의에 포함됩니다. 실제 작업은 아래의 명령들이 수행합니다.

856행 그림 · 어셈블·링크 단계

함수·코드 범위: ret_from_fork

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과SYM_CODE_START(ret_from_fork)
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	bl	schedule_tail

새 task가 처음 실행되는 경로에서 schedule_tail을 호출하여 이전 task와의 스케줄 전환 마무리와 첫 실행 bookkeeping을 완료합니다. 일반 task가 이미 한 번 실행한 뒤의 cpu_switch_to 복귀와 구분합니다. BL은 다음 명령의 주소를 LR(x30)에 기록하고 목적지로 분기합니다. 호출된 코드가 x0 같은 작업 레지스터를 바꿀 수 있으므로, 호출 뒤의 값은 그 코드의 반환·보존 규칙에 따릅니다.

857행 그림 · 호출과 실행 위치

함수·코드 범위: ret_from_fork

PC
이 명령을 실행하기 직전명령 직전 PC의 값
이 명령을 실행한 직후schedule_tail
LR (x30)
이 명령을 실행하기 직전명령 직전 LR의 값 (x30)
이 명령을 실행한 직후이 BL/BLR 다음 명령 주소

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 그림은 분기 명령 자체의 효과입니다. 호출된 함수가 실행된 뒤의 레지스터 값은 그 함수의 반환·보존 규칙을 따라야 합니다.
	cbz	x19, 1f				// not a kernel thread

x19의 값가 0이면 1f (L861)로 갑니다. 조건을 만족하지 않으면 바로 다음 명령을 읽습니다. 이 분기는 CMP의 결과가 아니라 지정 레지스터를 직접 검사합니다.

858행 그림 · 조건별 실행 위치

함수·코드 범위: ret_from_fork

읽는 값x19
PC
이 명령을 실행하기 직전명령 직전 PC의 값
이 명령을 실행한 직후명령 직전 x19의 값 = 0이면 1f (L861), 아니면 다음 명령

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 조건이 맞는 한 경로만 실행합니다. 이 분기 명령은 NZCV를 새로 계산하지 않습니다.
	mov	x0, x20

x0에 x20에 들어 있는 값을 넣습니다. 원래 x0의 내용은 덮어쓰며, 다른 레지스터나 그 주소의 메모리까지 복사하지는 않습니다.

859행 그림 · 값 복사

함수·코드 범위: ret_from_fork

읽는 값x20
x0
이 명령을 실행하기 직전명령 직전 x0의 값
이 명령을 실행한 직후명령 직전 x20의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 화살표는 값의 변화를 뜻합니다. 주소값을 옮겨도 그 주소의 메모리 내용은 복사하지 않습니다.
	blr	x19

커널 스레드라면 x19의 시작 함수를 x0=x20 인수로 호출합니다. x19가 0인 사용자 task는 앞의 CBZ로 이 호출을 건너뛰고 사용자 복귀 준비를 합니다. BLR은 다음 명령의 주소를 LR(x30)에 기록하고 레지스터에 든 목적지로 분기합니다. 호출된 코드가 x0 같은 작업 레지스터를 바꿀 수 있으므로, 호출 뒤의 값은 그 코드의 반환·보존 규칙에 따릅니다.

860행 그림 · 호출과 실행 위치

함수·코드 범위: ret_from_fork

읽는 값x19
PC
이 명령을 실행하기 직전명령 직전 PC의 값
이 명령을 실행한 직후명령 직전 x19의 값
LR (x30)
이 명령을 실행하기 직전명령 직전 LR의 값 (x30)
이 명령을 실행한 직후이 BL/BLR 다음 명령 주소

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 그림은 분기 명령 자체의 효과입니다. 호출된 함수가 실행된 뒤의 레지스터 값은 그 함수의 반환·보존 규칙을 따라야 합니다.
1:	get_current_task tsk

지금 이 CPU가 실행 중인 태스크의 주소를 가져옵니다. 단순한 정수 레지스터 값에 운영체제의 current라는 의미를 부여하는 부분입니다. 여기의 인자는 tsk입니다.

861행 그림 · 매크로 전체의 상태 변화

함수·코드 범위: ret_from_fork

읽는 값SP_EL0
tsk
이 명령을 실행하기 직전명령 직전 tsk의 값
이 명령을 실행한 직후명령 직전 SP_EL0의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

get_current_task 매크로 본문 · 570행

arch/arm64/include/asm/assembler.h · Linux v6.18.37

정의 선택 조건: #ifndef __ASM_ASSEMBLER_H

.macro get_current_task, rd mrs \rd, sp_el0 .endm
	mov	x0, sp

현재 SP의 숫자를 x0에 기억해 둡니다. 스택 자체는 이동하거나 복사되지 않습니다. 나중에 이 값으로 원래 위치를 찾을 수 있습니다.

862행 그림 · 값 복사

함수·코드 범위: ret_from_fork

읽는 값sp
x0
이 명령을 실행하기 직전명령 직전 x0의 값
이 명령을 실행한 직후명령 직전 sp의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 화살표는 값의 변화를 뜻합니다. 주소값을 옮겨도 그 주소의 메모리 내용은 복사하지 않습니다.
	bl	asm_exit_to_user_mode

x0=현재 pt_regs를 전달해 처음 사용자 실행으로 나가기 전 필요한 exit-to-user 작업을 처리합니다. 그 다음 ret_to_user가 실제 레지스터와 예외 상태를 복원합니다. BL은 다음 명령의 주소를 LR(x30)에 기록하고 목적지로 분기합니다.

863행 그림 · 호출과 실행 위치

함수·코드 범위: ret_from_fork

PC
이 명령을 실행하기 직전명령 직전 PC의 값
이 명령을 실행한 직후asm_exit_to_user_mode
LR (x30)
이 명령을 실행하기 직전명령 직전 LR의 값 (x30)
이 명령을 실행한 직후이 BL/BLR 다음 명령 주소

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 그림은 분기 명령 자체의 효과입니다. 호출된 함수가 실행된 뒤의 레지스터 값은 그 함수의 반환·보존 규칙을 따라야 합니다.
	b	ret_to_user

ret_to_user부터 실행을 계속합니다. 돌아올 주소를 새로 적지 않으므로 일반 함수 호출과는 다른 제어 이동입니다.

864행 그림 · 호출과 실행 위치

함수·코드 범위: ret_from_fork

PC
이 명령을 실행하기 직전명령 직전 PC의 값
이 명령을 실행한 직후ret_to_user

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 새 복귀 주소를 LR에 기록하지 않습니다. RET 역시 메모리의 스택 값을 자동으로 꺼내는 명령은 아닙니다.
SYM_CODE_END(ret_from_fork)

도구에 이 코드 범위의 끝을 알려 크기와 심볼 정보를 기록합니다. CPU가 이 줄 때문에 자동으로 반환하지는 않습니다.

865행 그림 · 어셈블·링크 단계

함수·코드 범위: ret_from_fork

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과SYM_CODE_END(ret_from_fork)
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 끝 표기 자체가 RET나 분기를 실행하지는 않습니다.
NOKPROBE(ret_from_fork)

ret_from_fork을 kprobe 설치 금지 목록에 넣습니다. 스택·레지스터·주소 공간을 바꾸는 예외/전환 경로 중간에 probe 예외를 추가하지 못하게 보호합니다. CONFIG_KPROBES가 꺼지면 이 목록 항목도 필요 없습니다. 빌드할 때 심벌을 가리키는 메타데이터를 만드는 표기이며, 이 위치에서 함수를 호출하는 명령은 아닙니다.

866행 그림 · 어셈블·링크 단계
빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과NOKPROBE(ret_from_fork)
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

IRQ 스택에서 함수 호출

원본 867–905행

SYM_FUNC_START(call_on_irq_stack)

이 위치부터 (call_on_irq_stack)라는 코드가 시작한다고 도구에 알려 줍니다. 정렬과 외부 공개 여부도 정의에 포함됩니다. ARM64 함수 시작 매크로는 간접 호출의 도착점을 표시하는 BTI도 추가합니다.

874행 그림 · 어셈블·링크 단계

함수·코드 범위: call_on_irq_stack

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과SYM_FUNC_START(call_on_irq_stack)
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 함수 시작 매크로는 심벌·정렬 정보와 BTI 진입 명령 등을 배치할 수 있습니다. 생성된 명령의 실행과 심벌 선언을 구분합니다.
	save_and_disable_daif x9

현재 예외 마스크를 기억해 둔 뒤 상태 교체 도중 예외가 끼어들지 않도록 마스크합니다. 작업이 끝나면 원래 값을 복구할 수 있습니다. 여기의 인자는 x9입니다.

875행 그림 · 매크로 전체의 상태 변화

함수·코드 범위: call_on_irq_stack

읽는 값DAIF
x9
이 명령을 실행하기 직전명령 직전 x9의 값
이 명령을 실행한 직후명령 직전 DAIF의 값
DAIF
이 명령을 실행하기 직전명령 직전 DAIF의 값
이 명령을 실행한 직후D=A=I=F=1

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

save_and_disable_daif 매크로 본문 · 44행

arch/arm64/include/asm/assembler.h · Linux v6.18.37

정의 선택 조건: #ifndef __ASM_ASSEMBLER_H

.macro save_and_disable_daif, flags mrs \flags, daif msr daifset, #0xf .endm
#ifdef CONFIG_SHADOW_CALL_STACK

Shadow Call Stack은 일반 스택과 별도 영역에 복귀 주소를 보관하는 보호 기법입니다. 켜진 빌드에서는 task의 SCS 포인터를 저장하고 IRQ 전용 SCS를 사용하며, 꺼지면 일반 IRQ 스택 전환만 수행합니다. 이 조건은 빌드 전처리 단계에서 포함할 코드를 고릅니다. CPU가 실행 중 전처리 조건을 검사하지는 않습니다.

876행 그림 · 전처리 단계

함수·코드 범위: call_on_irq_stack

소스
처리 전전처리 전
이 줄의 효과#ifdef CONFIG_SHADOW_CALL_STACK
CPU 레지스터
처리 전해당 없음
이 줄의 효과실행되는 CPU 명령이 없습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	get_current_task x16

지금 이 CPU가 실행 중인 태스크의 주소를 가져옵니다. 단순한 정수 레지스터 값에 운영체제의 current라는 의미를 부여하는 부분입니다. 여기의 인자는 x16입니다.

877행 그림 · 매크로 전체의 상태 변화

함수·코드 범위: call_on_irq_stack

이 줄을 포함하는 조건: #ifdef CONFIG_SHADOW_CALL_STACK

읽는 값SP_EL0
x16
이 명령을 실행하기 직전명령 직전 x16의 값
이 명령을 실행한 직후명령 직전 SP_EL0의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

get_current_task 매크로 본문 · 570행

arch/arm64/include/asm/assembler.h · Linux v6.18.37

정의 선택 조건: #ifndef __ASM_ASSEMBLER_H

.macro get_current_task, rd mrs \rd, sp_el0 .endm
	scs_save x16

일반 스택과 별도로 보호하는 반환 주소 스택의 현재 위치를 태스크에 기억해 둡니다. 여기의 인자는 x16입니다.

878행 그림 · 매크로 전체의 상태 변화

함수·코드 범위: call_on_irq_stack

이 줄을 포함하는 조건: #ifdef CONFIG_SHADOW_CALL_STACK

읽는 값x18x16
메모리[x16 + TSK_TI_SCS_SP]
이 명령을 실행하기 직전기존 값
이 명령을 실행한 직후명령 직전 x18의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • CONFIG_SHADOW_CALL_STACK이 없으면 저장 명령이 생성되지 않습니다.
scs_save 매크로 본문 · 18행

arch/arm64/include/asm/scs.h · Linux v6.18.37

정의 선택 조건: #ifndef _ASM_SCS_H / #ifdef __ASSEMBLY__ / #ifdef CONFIG_SHADOW_CALL_STACK

.macro scs_save tsk str scs_sp, [\tsk, #TSK_TI_SCS_SP] .endm
scs_save 매크로 본문 · 25행

arch/arm64/include/asm/scs.h · Linux v6.18.37

정의 선택 조건: #ifndef _ASM_SCS_H / #ifdef __ASSEMBLY__ / 앞 조건의 반대

.macro scs_save tsk .endm
	ldr_this_cpu scs_sp, irq_shadow_call_stack_ptr, x17

CPU별 IRQ 전용 shadow call stack 포인터를 읽습니다. IRQ 처리의 복귀 주소가 task의 SCS 공간을 계속 소비하지 않도록 일반 IRQ 스택과 함께 보호용 스택도 바꿉니다. 심벌의 기준 주소에 현재 CPU의 per-CPU 오프셋을 더해 이 CPU의 저장 위치를 찾습니다.

879행 그림 · 주소·값 구분

함수·코드 범위: call_on_irq_stack

이 줄을 포함하는 조건: #ifdef CONFIG_SHADOW_CALL_STACK

scs_sp
이 명령을 실행하기 직전명령 직전 scs_sp의 값
이 명령을 실행한 직후메모리[주소(irq_shadow_call_stack_ptr) + 현재 CPU 오프셋]의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 이 매크로의 임시 레지스터는 내부에서 사용됩니다.
ldr_this_cpu 매크로 본문 · 250행

arch/arm64/include/asm/assembler.h · Linux v6.18.37

정의 선택 조건: #ifndef __ASM_ASSEMBLER_H

.macro ldr_this_cpu dst, sym, tmp adr_l \dst, \sym get_this_cpu_offset \tmp ldr \dst, [\dst, \tmp] .endm
#endif

빌드 조건으로 나누었던 구간을 닫습니다. 대상:

880행 그림 · 전처리 단계

함수·코드 범위: call_on_irq_stack

이 줄을 포함하는 조건: #ifdef CONFIG_SHADOW_CALL_STACK

소스
처리 전전처리 전
이 줄의 효과#endif
CPU 레지스터
처리 전해당 없음
이 줄의 효과실행되는 CPU 명령이 없습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	stp	x29, x30, [sp, #-16]!

x29, x30의 값을 sp를 기준으로 -16만큼 계산한 메모리 위치에 기록합니다. 레지스터 값을 나중에 다시 쓰거나 다른 코드가 읽을 수 있도록 메모리에 남기는 줄입니다. 첫 위치에는 x29, 그보다 8바이트 높은 위치에는 x30의 값이 들어갑니다. 이 스택 위치는 현재 함수 또는 예외 처리의 보관 공간입니다. 쓰기 전에 기준 주소를 갱신합니다. SP의 음수 오프셋이면 낮은 주소 쪽에 저장 공간을 확보하는 뜻입니다.

원래 스택에서 만든 16바이트 프레임이 IRQ 스택 사용 후 돌아갈 위치를 보존합니다.

883행 그림 · 메모리 쓰기

함수·코드 범위: call_on_irq_stack

읽는 값spx29x30
메모리[이전 sp + (-16)] · 8바이트
이 명령을 실행하기 직전기존 메모리 값
이 명령을 실행한 직후명령 직전 x29의 값
메모리[이전 sp + (-16) + 8] · 8바이트
이 명령을 실행하기 직전기존 메모리 값
이 명령을 실행한 직후명령 직전 x30의 값
sp
이 명령을 실행하기 직전명령 직전 sp의 값
이 명령을 실행한 직후명령 직전 sp의 값 + (-16)

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • !는 전위 갱신입니다. 오프셋을 더한 주소로 접근하고 그 주소를 기준 레지스터에도 남깁니다.
  • 메모리 칸은 낮은 주소부터 나열했습니다. SP를 줄이면 더 낮은 주소의 공간을 사용합니다. 화면의 아래쪽이 곧 낮은 주소라는 뜻은 아닙니다.
	mov	x29, sp

현재 SP의 숫자를 x29에 기억해 둡니다. 스택 자체는 이동하거나 복사되지 않습니다. 나중에 이 값으로 원래 위치를 찾을 수 있습니다.

884행 그림 · 값 복사

함수·코드 범위: call_on_irq_stack

읽는 값sp
x29
이 명령을 실행하기 직전명령 직전 x29의 값
이 명령을 실행한 직후명령 직전 sp의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 화살표는 값의 변화를 뜻합니다. 주소값을 옮겨도 그 주소의 메모리 내용은 복사하지 않습니다.
	ldr_this_cpu x16, irq_stack_ptr, x17

현재 CPU의 IRQ 스택 시작을 읽습니다. task 스택에서 IRQ 처리 깊이가 과도하게 늘지 않도록 별도 스택으로 작업을 옮길 준비입니다. 심벌의 기준 주소에 현재 CPU의 per-CPU 오프셋을 더해 이 CPU의 저장 위치를 찾습니다.

886행 그림 · 주소·값 구분

함수·코드 범위: call_on_irq_stack

x16
이 명령을 실행하기 직전명령 직전 x16의 값
이 명령을 실행한 직후메모리[주소(irq_stack_ptr) + 현재 CPU 오프셋]의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 이 매크로의 임시 레지스터는 내부에서 사용됩니다.
ldr_this_cpu 매크로 본문 · 250행

arch/arm64/include/asm/assembler.h · Linux v6.18.37

정의 선택 조건: #ifndef __ASM_ASSEMBLER_H

.macro ldr_this_cpu dst, sym, tmp adr_l \dst, \sym get_this_cpu_offset \tmp ldr \dst, [\dst, \tmp] .endm
	add	sp, x16, #IRQ_STACK_SIZE

x16를 기준으로 IRQ_STACK_SIZE을 더해 sp에 결과를 둡니다. 주소 계산에 쓰이는 경우에는 구조체의 필드나 다음 저장 위치를 찾는 동작입니다. 이 형태는 조건 플래그를 바꾸지 않습니다.

IRQ 스택 끝을 SP로 선택합니다. 이후 저장할 때 SP는 낮은 주소로 이동합니다.

889행 그림 · 레지스터 상태

함수·코드 범위: call_on_irq_stack

읽는 값x16
sp
이 명령을 실행하기 직전명령 직전 sp의 값
이 명령을 실행한 직후명령 직전 x16의 값 + IRQ_STACK_SIZE

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 결과는 목적 레지스터 폭에 맞습니다. 이 형식은 NZCV를 바꾸지 않습니다.
  • SP는 주소만 바뀝니다. 스택 데이터 저장·복원은 STR/STP·LDR/LDP 같은 별도 메모리 명령이 수행합니다.
	restore_irq x9

상태 전환 전에 기록해 둔 예외 마스크를 돌려놓습니다. 무조건 인터럽트를 켜는 것이 아니라 원래 상태를 복원합니다. 여기의 인자는 x9입니다.

890행 그림 · 매크로 전체의 상태 변화

함수·코드 범위: call_on_irq_stack

읽는 값x9
DAIF
이 명령을 실행하기 직전명령 직전 DAIF의 값
이 명령을 실행한 직후명령 직전 x9의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

restore_irq 매크로 본문 · 54행

arch/arm64/include/asm/assembler.h · Linux v6.18.37

정의 선택 조건: #ifndef __ASM_ASSEMBLER_H

.macro restore_irq, flags msr daif, \flags .endm
	blr	x1

x0의 pt_regs를 그대로 인수로 전달해 x1의 IRQ 처리 함수를 호출합니다. SP와 필요 시 SCS는 이미 이 CPU의 IRQ 전용 공간으로 바뀌었고, 반환 뒤 원래 task 스택을 복구합니다. BLR은 다음 명령의 주소를 LR(x30)에 기록하고 레지스터에 든 목적지로 분기합니다. 호출된 코드가 x0 같은 작업 레지스터를 바꿀 수 있으므로, 호출 뒤의 값은 그 코드의 반환·보존 규칙에 따릅니다.

x0=pt_regs를 그대로 전달하고 IRQ 스택에서 x1의 함수를 호출합니다.

891행 그림 · 호출과 실행 위치

함수·코드 범위: call_on_irq_stack

읽는 값x1
PC
이 명령을 실행하기 직전명령 직전 PC의 값
이 명령을 실행한 직후명령 직전 x1의 값
LR (x30)
이 명령을 실행하기 직전명령 직전 LR의 값 (x30)
이 명령을 실행한 직후이 BL/BLR 다음 명령 주소

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 그림은 분기 명령 자체의 효과입니다. 호출된 함수가 실행된 뒤의 레지스터 값은 그 함수의 반환·보존 규칙을 따라야 합니다.
	save_and_disable_daif x9

현재 예외 마스크를 기억해 둔 뒤 상태 교체 도중 예외가 끼어들지 않도록 마스크합니다. 작업이 끝나면 원래 값을 복구할 수 있습니다. 여기의 인자는 x9입니다.

893행 그림 · 매크로 전체의 상태 변화

함수·코드 범위: call_on_irq_stack

읽는 값DAIF
x9
이 명령을 실행하기 직전명령 직전 x9의 값
이 명령을 실행한 직후명령 직전 DAIF의 값
DAIF
이 명령을 실행하기 직전명령 직전 DAIF의 값
이 명령을 실행한 직후D=A=I=F=1

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

save_and_disable_daif 매크로 본문 · 44행

arch/arm64/include/asm/assembler.h · Linux v6.18.37

정의 선택 조건: #ifndef __ASM_ASSEMBLER_H

.macro save_and_disable_daif, flags mrs \flags, daif msr daifset, #0xf .endm
	mov	sp, x29

이제 스택으로 사용할 위치를 x29의 값으로 바꿉니다. 메모리 내용을 옮기는 작업은 아닙니다. 다음 저장·호출부터 새 스택 위치를 사용합니다.

898행 그림 · 값 복사

함수·코드 범위: call_on_irq_stack

읽는 값x29
sp
이 명령을 실행하기 직전명령 직전 sp의 값
이 명령을 실행한 직후명령 직전 x29의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 화살표는 값의 변화를 뜻합니다. 주소값을 옮겨도 그 주소의 메모리 내용은 복사하지 않습니다.
	ldp	x29, x30, [sp], #16

sp가 가리키는 메모리 위치에 보관된 값을 x29, x30로 가져옵니다. 주소 숫자를 옮기는 MOV와 달리 실제 메모리를 읽습니다. 첫 위치에서 x29, 그보다 8바이트 높은 위치에서 x30의 값을 읽습니다. 앞서 보관한 상태를 되살리거나 스택으로 전달된 인자를 얻는 과정입니다. 읽은 뒤 기준 포인터를 옮깁니다. SP가 증가하는 경우는 사용한 스택 공간을 해제하는 단계입니다.

899행 그림 · 메모리 읽기

함수·코드 범위: call_on_irq_stack

읽는 값sp
메모리[이전 sp] · 8바이트
이 명령을 실행하기 직전M0 (이 주소에 저장된 값)
이 명령을 실행한 직후M0 (이 주소에 저장된 값)
x29
이 명령을 실행하기 직전명령 직전 x29의 값
이 명령을 실행한 직후M0
메모리[이전 sp + 8] · 8바이트
이 명령을 실행하기 직전M1 (이 주소에 저장된 값)
이 명령을 실행한 직후M1 (이 주소에 저장된 값)
x30
이 명령을 실행하기 직전명령 직전 x30의 값
이 명령을 실행한 직후M1
sp
이 명령을 실행하기 직전명령 직전 sp의 값
이 명령을 실행한 직후명령 직전 sp의 값 + (16)

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 대괄호 밖의 오프셋은 후위 갱신입니다. 이전 주소로 접근한 뒤 기준 레지스터를 갱신합니다.
  • 메모리 칸은 낮은 주소부터 나열했습니다. SP를 줄이면 더 낮은 주소의 공간을 사용합니다. 화면의 아래쪽이 곧 낮은 주소라는 뜻은 아닙니다.
	scs_load_current

태스크에 저장해 둔 반환 주소 보호용 스택 위치를 되살립니다. 일반 SP를 읽는 것과는 다른 상태입니다. 여기의 인자는 없음입니다.

900행 그림 · 매크로 전체의 상태 변화

함수·코드 범위: call_on_irq_stack

x18
이 명령을 실행하기 직전명령 직전 x18의 값
이 명령을 실행한 직후메모리[명령 직전 SP_EL0의 값 + TSK_TI_SCS_SP]

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • CONFIG_SHADOW_CALL_STACK이 없으면 빈 매크로입니다.
scs_load_current 매크로 본문 · 13행

arch/arm64/include/asm/scs.h · Linux v6.18.37

정의 선택 조건: #ifndef _ASM_SCS_H / #ifdef __ASSEMBLY__ / #ifdef CONFIG_SHADOW_CALL_STACK

.macro scs_load_current get_current_task scs_sp ldr scs_sp, [scs_sp, #TSK_TI_SCS_SP] .endm
scs_load_current 매크로 본문 · 22행

arch/arm64/include/asm/scs.h · Linux v6.18.37

정의 선택 조건: #ifndef _ASM_SCS_H / #ifdef __ASSEMBLY__ / 앞 조건의 반대

.macro scs_load_current .endm
	restore_irq x9

상태 전환 전에 기록해 둔 예외 마스크를 돌려놓습니다. 무조건 인터럽트를 켜는 것이 아니라 원래 상태를 복원합니다. 여기의 인자는 x9입니다.

901행 그림 · 매크로 전체의 상태 변화

함수·코드 범위: call_on_irq_stack

읽는 값x9
DAIF
이 명령을 실행하기 직전명령 직전 DAIF의 값
이 명령을 실행한 직후명령 직전 x9의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

restore_irq 매크로 본문 · 54행

arch/arm64/include/asm/assembler.h · Linux v6.18.37

정의 선택 조건: #ifndef __ASM_ASSEMBLER_H

.macro restore_irq, flags msr daif, \flags .endm
	ret

LR(x30)에 적힌 주소로 실행을 돌립니다. 그 주소가 어떻게 준비됐는지가 중요합니다. 정상 함수의 호출자일 수도 있고, 절전 복귀나 trampoline에서 별도로 기록한 재개 지점일 수도 있습니다.

902행 그림 · 호출과 실행 위치

함수·코드 범위: call_on_irq_stack

읽는 값lr
PC
이 명령을 실행하기 직전명령 직전 PC의 값
이 명령을 실행한 직후명령 직전 lr의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 새 복귀 주소를 LR에 기록하지 않습니다. RET 역시 메모리의 스택 값을 자동으로 꺼내는 명령은 아닙니다.
SYM_FUNC_END(call_on_irq_stack)

도구에 이 코드 범위의 끝을 알려 크기와 심볼 정보를 기록합니다. CPU가 이 줄 때문에 자동으로 반환하지는 않습니다.

903행 그림 · 어셈블·링크 단계

함수·코드 범위: call_on_irq_stack

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과SYM_FUNC_END(call_on_irq_stack)
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 끝 표기 자체가 RET나 분기를 실행하지는 않습니다.
NOKPROBE(call_on_irq_stack)

call_on_irq_stack을 kprobe 설치 금지 목록에 넣습니다. 스택·레지스터·주소 공간을 바꾸는 예외/전환 경로 중간에 probe 예외를 추가하지 못하게 보호합니다. CONFIG_KPROBES가 꺼지면 이 목록 항목도 필요 없습니다. 빌드할 때 심벌을 가리키는 메타데이터를 만드는 표기이며, 이 위치에서 함수를 호출하는 명령은 아닙니다.

904행 그림 · 어셈블·링크 단계
빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과NOKPROBE(call_on_irq_stack)
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

SDEI 진입·복귀 trampoline

원본 906–966행

#ifdef CONFIG_ARM_SDE_INTERFACE

SDEI는 펌웨어가 운영체제에 소프트웨어 위임 예외 이벤트를 전달하는 인터페이스입니다. 켜진 빌드에만 별도 이벤트 스택·저장 문맥·SMC/HVC 완료 경로를 포함하며, 일반 장치 IRQ의 GIC handler와는 다른 진입 규약입니다. 이 조건은 빌드 전처리 단계에서 포함할 코드를 고릅니다. CPU가 실행 중 전처리 조건을 검사하지는 않습니다.

906행 그림 · 전처리 단계
소스
처리 전전처리 전
이 줄의 효과#ifdef CONFIG_ARM_SDE_INTERFACE
CPU 레지스터
처리 전해당 없음
이 줄의 효과실행되는 CPU 명령이 없습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

#include <asm/sdei.h>

SDEI 이벤트의 커널 저장 형식·우선순위·스택 관련 정의를 가져옵니다. 일반 IRQ와 다른 펌웨어 이벤트 진입 문맥을 보존하는 데 사용합니다. #include는 전처리 단계에서 헤더 내용을 가져옵니다. CPU가 이 줄에서 함수를 호출하는 동작은 없습니다.

908행 그림 · 전처리 단계

이 줄을 포함하는 조건: #ifdef CONFIG_ARM_SDE_INTERFACE

소스
처리 전전처리 전
이 줄의 효과#include <asm/sdei.h>
CPU 레지스터
처리 전해당 없음
이 줄의 효과실행되는 CPU 명령이 없습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

#include <uapi/linux/arm_sdei.h>

펌웨어와 주고받는 SDEI 서비스 번호와 완료 상태 등 인터페이스 상수를 가져옵니다. EVENT_COMPLETE와 COMPLETE_AND_RESUME 같은 요청을 구분하는 데 필요합니다. #include는 전처리 단계에서 헤더 내용을 가져옵니다. CPU가 이 줄에서 함수를 호출하는 동작은 없습니다.

909행 그림 · 전처리 단계

이 줄을 포함하는 조건: #ifdef CONFIG_ARM_SDE_INTERFACE

소스
처리 전전처리 전
이 줄의 효과#include <uapi/linux/arm_sdei.h>
CPU 레지스터
처리 전해당 없음
이 줄의 효과실행되는 CPU 명령이 없습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

.macro sdei_handler_exit exit_mode

SDEI 이벤트 처리를 펌웨어에 완료 통지하는 매크로입니다. exit_mode가 SMC인지 HVC인지에 따라 통로를 선택하며 x0·x1에 준비한 완료 서비스와 복귀 주소를 전달합니다. 정상 완료하면 원래 중단 문맥으로 돌아가므로 이 호출 위치로 반환하지 않습니다. 이 줄은 어셈블러 매크로 정의의 시작입니다. 본문은 매크로를 사용하는 위치에 펼쳐지며, 정의 자체가 런타임 함수 호출은 아닙니다.

911행 그림 · 어셈블·링크 단계

매크로 본문: sdei_handler_exit

이 줄을 포함하는 조건: #ifdef CONFIG_ARM_SDE_INTERFACE

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과.macro sdei_handler_exit exit_mode
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 뒤의 본문은 이 이름을 사용하는 위치에 펼쳐집니다. 정의 위치를 CPU가 함수처럼 호출하지 않습니다.
	cmp	\exit_mode, #SDEI_EXIT_SMC

펌웨어 SDEI 완료 통로가 SMC인지 확인합니다. 이벤트 번호나 성공 여부를 비교하는 값이 아니라 호출 방식 선택입니다. CMP는 두 피연산자의 뺄셈 결과로 NZCV 조건 플래그만 갱신하고 원래 레지스터 값은 유지합니다.

913행 그림 · 조건 플래그 계산

매크로 본문: sdei_handler_exit

이 줄을 포함하는 조건: #ifdef CONFIG_ARM_SDE_INTERFACE

읽는 값\exit_mode
NZCV
이 명령을 실행하기 직전명령 직전 NZCV의 값
이 명령을 실행한 직후명령 직전 \exit_mode의 값 − SDEI_EXIT_SMC의 조건 결과

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 일반 레지스터에 연산 결과를 쓰지 않고 조건 플래그만 바꿉니다.
  • N은 결과의 부호 비트, Z는 결과가 0인지 나타냅니다. C는 빌림이 없으면 1, V는 부호 있는 뺄셈의 오버플로를 나타냅니다.
  • 매크로 sdei_handler_exit의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	b.ne	99f

앞서 계산한 조건 플래그가 Z=0이면 99f (L917)로 갑니다. 사이에 플래그를 바꾸지 않는 명령이 있어도 비교 결과는 남아 있습니다.

914행 그림 · 조건별 실행 위치

매크로 본문: sdei_handler_exit

이 줄을 포함하는 조건: #ifdef CONFIG_ARM_SDE_INTERFACE

읽는 값NZCV
PC
이 명령을 실행하기 직전명령 직전 PC의 값
이 명령을 실행한 직후Z=0이면 99f (L917), 아니면 다음 명령

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 조건이 맞는 한 경로만 실행합니다. 이 분기 명령은 NZCV를 새로 계산하지 않습니다.
  • 매크로 sdei_handler_exit의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	smc	#0

x0·x1에 준비한 SDEI 완료 또는 complete-and-resume 요청을 SMC로 전달합니다. 성공하면 펌웨어가 중단된 실행을 복원하므로 다음 무한 루프는 비정상 반환을 막는 경로입니다. 일반 함수 호출처럼 BL로 이동하는 대신 동기 예외를 통해 요청을 전달합니다.

915행 그림 · 예외 처리기로 제어 전달

매크로 본문: sdei_handler_exit

이 줄을 포함하는 조건: #ifdef CONFIG_ARM_SDE_INTERFACE

PC
이 명령을 실행하기 직전명령 직전 PC의 값
이 명령을 실행한 직후EL3 또는 설정에 따른 EL2 트랩의 예외 벡터
ELR_ELx
이 명령을 실행하기 직전명령 직전 ELR_ELx의 값
이 명령을 실행한 직후복귀할 다음 명령 주소
SPSR_ELx
이 명령을 실행하기 직전명령 직전 SPSR_ELx의 값
이 명령을 실행한 직후예외 직전 PSTATE

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 허용된 정상 예외 진입을 나타냅니다. 사용이 금지되거나 해당 EL이 없으면 예외 결과가 달라집니다. x0 등의 요청 인자는 처리기가 규약에 따라 읽으며 HVC/SMC 자체가 메모리로 복사하지는 않습니다.
  • 서비스를 처리한 뒤의 반환 레지스터는 서비스 규약에 따릅니다. 여기서는 진입까지만 표시합니다.
  • 매크로 sdei_handler_exit의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	b	.

같은 명령 위치에서 반복합니다. 이 경로에서는 더 진행하거나 호출자에게 돌아갈 정상 절차를 마련하지 않았습니다.

916행 그림 · 호출과 실행 위치

매크로 본문: sdei_handler_exit

이 줄을 포함하는 조건: #ifdef CONFIG_ARM_SDE_INTERFACE

PC
이 명령을 실행하기 직전명령 직전 PC의 값
이 명령을 실행한 직후.

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 새 복귀 주소를 LR에 기록하지 않습니다. RET 역시 메모리의 스택 값을 자동으로 꺼내는 명령은 아닙니다.
  • 매크로 sdei_handler_exit의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
99:	hvc	#0

SMC가 아닌 구성은 HVC 통로로 SDEI 완료 요청을 보냅니다. 이벤트의 일반 C callback을 호출하는 명령이 아니라 상위 펌웨어/하이퍼바이저 인터페이스입니다. 일반 함수 호출처럼 BL로 이동하는 대신 동기 예외를 통해 요청을 전달합니다.

917행 그림 · 예외 처리기로 제어 전달

매크로 본문: sdei_handler_exit

이 줄을 포함하는 조건: #ifdef CONFIG_ARM_SDE_INTERFACE

PC
이 명령을 실행하기 직전명령 직전 PC의 값
이 명령을 실행한 직후EL2의 예외 벡터
ELR_ELx
이 명령을 실행하기 직전명령 직전 ELR_ELx의 값
이 명령을 실행한 직후복귀할 다음 명령 주소
SPSR_ELx
이 명령을 실행하기 직전명령 직전 SPSR_ELx의 값
이 명령을 실행한 직후예외 직전 PSTATE

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 허용된 정상 예외 진입을 나타냅니다. 사용이 금지되거나 해당 EL이 없으면 예외 결과가 달라집니다. x0 등의 요청 인자는 처리기가 규약에 따라 읽으며 HVC/SMC 자체가 메모리로 복사하지는 않습니다.
  • 서비스를 처리한 뒤의 반환 레지스터는 서비스 규약에 따릅니다. 여기서는 진입까지만 표시합니다.
  • 매크로 sdei_handler_exit의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	b	.

같은 명령 위치에서 반복합니다. 이 경로에서는 더 진행하거나 호출자에게 돌아갈 정상 절차를 마련하지 않았습니다.

918행 그림 · 호출과 실행 위치

매크로 본문: sdei_handler_exit

이 줄을 포함하는 조건: #ifdef CONFIG_ARM_SDE_INTERFACE

PC
이 명령을 실행하기 직전명령 직전 PC의 값
이 명령을 실행한 직후.

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 새 복귀 주소를 LR에 기록하지 않습니다. RET 역시 메모리의 스택 값을 자동으로 꺼내는 명령은 아닙니다.
  • 매크로 sdei_handler_exit의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
.endm

공통 작업의 정의를 여기서 마칩니다. RET가 아니라 빌드 도구가 읽는 경계입니다.

919행 그림 · 어셈블·링크 단계

매크로 본문: sdei_handler_exit

이 줄을 포함하는 조건: #ifdef CONFIG_ARM_SDE_INTERFACE

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과.endm
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 끝 표기 자체가 RET나 분기를 실행하지는 않습니다.
#ifdef CONFIG_UNMAP_KERNEL_AT_EL0

KPTI가 사용자 실행 중 정상 SDEI 코드까지 매핑에서 제외할 수 있으므로 지원 빌드에는 최소 매핑 안의 SDEI 진입·복귀 trampoline을 포함합니다. 없으면 직접 SDEI handler로 진입할 수 있습니다. 이 조건은 빌드 전처리 단계에서 포함할 코드를 고릅니다. CPU가 실행 중 전처리 조건을 검사하지는 않습니다.

921행 그림 · 전처리 단계

이 줄을 포함하는 조건: #ifdef CONFIG_ARM_SDE_INTERFACE

소스
처리 전전처리 전
이 줄의 효과#ifdef CONFIG_UNMAP_KERNEL_AT_EL0
CPU 레지스터
처리 전해당 없음
이 줄의 효과실행되는 CPU 명령이 없습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

.pushsection ".entry.tramp.text", "ax"

이후 출력할 바이트를 어느 섹션에 넣을지 바꿉니다. 빌드 도구가 코드를 분류하는 작업이며 현재 CPU의 PC나 SP를 옮기는 줄은 아닙니다. ".entry.tramp.text", "ax"

930행 그림 · 어셈블·링크 단계

이 줄을 포함하는 조건: #ifdef CONFIG_ARM_SDE_INTERFACE / #ifdef CONFIG_UNMAP_KERNEL_AT_EL0

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과.pushsection ".entry.tramp.text", "ax"
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

SYM_CODE_START(__sdei_asm_entry_trampoline)

이 위치부터 (__sdei_asm_entry_trampoline)라는 코드가 시작한다고 도구에 알려 줍니다. 정렬과 외부 공개 여부도 정의에 포함됩니다. 실제 작업은 아래의 명령들이 수행합니다.

931행 그림 · 어셈블·링크 단계

함수·코드 범위: __sdei_asm_entry_trampoline

이 줄을 포함하는 조건: #ifdef CONFIG_ARM_SDE_INTERFACE / #ifdef CONFIG_UNMAP_KERNEL_AT_EL0

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과SYM_CODE_START(__sdei_asm_entry_trampoline)
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	mrs	x4, ttbr1_el1

SDEI가 들어왔을 때 TTBR1이 사용자 쪽 최소 매핑인지 확인할 값을 읽습니다. 아래 USER_ASID_BIT가 세워져 있으면 일반 커널 데이터 접근 전 매핑 복구가 필요합니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.

932행 그림 · 시스템 레지스터 읽기

함수·코드 범위: __sdei_asm_entry_trampoline

이 줄을 포함하는 조건: #ifdef CONFIG_ARM_SDE_INTERFACE / #ifdef CONFIG_UNMAP_KERNEL_AT_EL0

읽는 값TTBR1_EL1
x4
이 명령을 실행하기 직전명령 직전 x4의 값
이 명령을 실행한 직후명령 직전 TTBR1_EL1의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 일반 RAM의 메모리 접근과 구분합니다.
	tbz	x4, #USER_ASID_BIT, 1f

USER_ASID_BIT가 없으면 이미 커널 매핑이므로 추가 전환을 건너뜁니다. 있으면 trampoline이 매핑을 복구하고 나중에 되돌릴 필요가 있음을 기록합니다. 이 분기는 지정 레지스터의 값이나 비트를 직접 검사하며 CMP가 남긴 NZCV를 읽거나 바꾸지 않습니다. 조건이 맞지 않으면 바로 다음 명령으로 진행합니다.

933행 그림 · 조건별 실행 위치

함수·코드 범위: __sdei_asm_entry_trampoline

이 줄을 포함하는 조건: #ifdef CONFIG_ARM_SDE_INTERFACE / #ifdef CONFIG_UNMAP_KERNEL_AT_EL0

읽는 값x4
PC
이 명령을 실행하기 직전명령 직전 PC의 값
이 명령을 실행한 직후명령 직전 x4의 값의 bit USER_ASID_BIT = 0이면 1f (L942), 아니면 다음 명령

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 조건이 맞는 한 경로만 실행합니다. 이 분기 명령은 NZCV를 새로 계산하지 않습니다.
	tramp_map_kernel tmp=x4

사용자 실행 동안 감춰 둔 커널의 넓은 주소 공간을 다시 연결합니다. 그래야 본래 예외 처리 코드와 데이터를 읽을 수 있습니다. 여기의 인자는 tmp=x4입니다.

935행 그림 · 매크로 전체의 주요 상태

함수·코드 범위: __sdei_asm_entry_trampoline

이 줄을 포함하는 조건: #ifdef CONFIG_ARM_SDE_INTERFACE / #ifdef CONFIG_UNMAP_KERNEL_AT_EL0

TTBR1_EL1
이 명령을 실행하기 직전명령 직전 TTBR1_EL1의 값
이 명령을 실행한 직후(명령 직전 TTBR1_EL1의 값 + TRAMP_SWAPPER_OFFSET) AND NOT USER_ASID_FLAG

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • tmp=x4을 임시 레지스터로 사용합니다. map 경로의 Falkor 우회에서는 이 임시값을 TLBI 피연산자로 다시 구성할 수 있습니다.
  • 여러 명령과 조건부 경로를 묶은 그림입니다. 이 목록이 모든 임시 레지스터의 변경을 열거하는 것은 아닙니다. 아래 매크로 본문에서 각 단계를 확인하실 수 있습니다.
tramp_map_kernel 매크로 본문 · 622행

arch/arm64/kernel/entry.S · Linux v6.18.37

.macro tramp_map_kernel, tmp mrs \tmp, ttbr1_el1 add \tmp, \tmp, #TRAMP_SWAPPER_OFFSET bic \tmp, \tmp, #USER_ASID_FLAG msr ttbr1_el1, \tmp #ifdef CONFIG_QCOM_FALKOR_ERRATUM_1003 alternative_if ARM64_WORKAROUND_QCOM_FALKOR_E1003 /* ASID already in \tmp[63:48] */ movk \tmp, #:abs_g2_nc:(TRAMP_VALIAS >> 12) movk \tmp, #:abs_g1_nc:(TRAMP_VALIAS >> 12) /* 2MB boundary containing the vectors, so we nobble the walk cache */ movk \tmp, #:abs_g0_nc:((TRAMP_VALIAS & ~(SZ_2M - 1)) >> 12) isb tlbi vae1, \tmp dsb nsh alternative_else_nop_endif #endif /* CONFIG_QCOM_FALKOR_ERRATUM_1003 */ .endm
	isb

뒤 명령을 변경된 CPU 제어 상태에 맞춰 실행하도록 동기화합니다. 주소 변환이나 벡터 설정을 바꾼 뒤 예전 실행 상태가 계속 쓰이지 않게 합니다.

936행 그림 · 동기화·캐시·대기 상태

함수·코드 범위: __sdei_asm_entry_trampoline

이 줄을 포함하는 조건: #ifdef CONFIG_ARM_SDE_INTERFACE / #ifdef CONFIG_UNMAP_KERNEL_AT_EL0

명령 실행 문맥
이 명령을 실행하기 직전실행 전 상태
이 명령을 실행한 직후앞서 바꾼 상태를 이후 명령 실행에 반영

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	mov	x4, xzr

x4에 0을 넣습니다. 원래 x4의 내용은 덮어쓰며, 다른 레지스터나 그 주소의 메모리까지 복사하지는 않습니다.

937행 그림 · 값 복사

함수·코드 범위: __sdei_asm_entry_trampoline

이 줄을 포함하는 조건: #ifdef CONFIG_ARM_SDE_INTERFACE / #ifdef CONFIG_UNMAP_KERNEL_AT_EL0

읽는 값xzr
x4
이 명령을 실행하기 직전명령 직전 x4의 값
이 명령을 실행한 직후0

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 화살표는 값의 변화를 뜻합니다. 주소값을 옮겨도 그 주소의 메모리 내용은 복사하지 않습니다.
1:	str	x4, [x1, #(SDEI_EVENT_INTREGS + S_SDEI_TTBR1)]

이벤트 저장 영역에 진입 전 매핑 상태 표식을 남깁니다. 0은 이 trampoline이 커널 매핑을 복구했으므로 복귀 때 다시 감춰야 한다는 뜻이고, 0이 아니면 기존 커널 매핑을 유지합니다. 실제로 x1 + ((SDEI_EVENT_INTREGS + S_SDEI_TTBR1)) 주소의 메모리 8바이트를 x4의 값으로 씁니다. 이 주소 형식은 기준 레지스터 x1 자체를 갱신하지 않습니다.

942행 그림 · 메모리 쓰기

함수·코드 범위: __sdei_asm_entry_trampoline

이 줄을 포함하는 조건: #ifdef CONFIG_ARM_SDE_INTERFACE / #ifdef CONFIG_UNMAP_KERNEL_AT_EL0

읽는 값x1x4
메모리[이전 x1 + ((SDEI_EVENT_INTREGS + S_SDEI_TTBR1))] · 8바이트
이 명령을 실행하기 직전기존 메모리 값
이 명령을 실행한 직후명령 직전 x4의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 주소 계산에 의한 기준 레지스터 갱신은 없습니다. 다만 LDR/LDP의 목적 레지스터가 기준 레지스터와 같으면 읽은 값이 그 레지스터를 덮어씁니다.
	tramp_data_read_var     x4, __sdei_asm_handler

최소 매핑에서 접근 가능한 방법으로 정상 __sdei_asm_handler 주소를 얻습니다. x1의 이벤트 자료를 읽을 수 있도록 앞에서 커널 매핑을 복구한 뒤 그 handler로 이어집니다. 재배치 구성에서는 별도 rodata의 리터럴을 읽고, 그렇지 않은 구성에서는 MOVZ/MOVK로 주소를 구성합니다.

943행 그림 · 주소·값 구분

함수·코드 범위: __sdei_asm_entry_trampoline

이 줄을 포함하는 조건: #ifdef CONFIG_ARM_SDE_INTERFACE / #ifdef CONFIG_UNMAP_KERNEL_AT_EL0

x4
이 명령을 실행하기 직전명령 직전 x4의 값
이 명령을 실행한 직후주소(__sdei_asm_handler)

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 이 매크로의 임시 레지스터는 내부에서 사용됩니다.
  • 재배치 구성은 리터럴을 읽고, 그 외 구성은 MOVZ/MOVK로 주소를 만듭니다.
tramp_data_read_var 매크로 본문 · 654행

arch/arm64/kernel/entry.S · Linux v6.18.37

.macro tramp_data_read_var dst, var #ifdef CONFIG_RELOCATABLE ldr \dst, .L__tramp_data_\var .ifndef .L__tramp_data_\var .pushsection ".entry.tramp.rodata", "a", %progbits .align 3 .L__tramp_data_\var: .quad \var .popsection .endif #else /* * As !RELOCATABLE implies !RANDOMIZE_BASE the address is always a * compile time constant (and hence not secret and not worth hiding). * * As statically allocated kernel code and data always live in the top * 47 bits of the address space we can sign-extend bit 47 and avoid an * instruction to load the upper 16 bits (which must be 0xFFFF). */ movz \dst, :abs_g2_s:\var movk \dst, :abs_g1_nc:\var movk \dst, :abs_g0_nc:\var #endif .endm
	br	x4

x4에 준비한 주소부터 실행합니다. 주소가 가리키는 값을 메모리에서 읽는 것이 아니라 CPU의 다음 실행 위치를 바꿉니다. 새 복귀 주소는 만들지 않습니다.

944행 그림 · 호출과 실행 위치

함수·코드 범위: __sdei_asm_entry_trampoline

이 줄을 포함하는 조건: #ifdef CONFIG_ARM_SDE_INTERFACE / #ifdef CONFIG_UNMAP_KERNEL_AT_EL0

읽는 값x4
PC
이 명령을 실행하기 직전명령 직전 PC의 값
이 명령을 실행한 직후명령 직전 x4의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 새 복귀 주소를 LR에 기록하지 않습니다. RET 역시 메모리의 스택 값을 자동으로 꺼내는 명령은 아닙니다.
SYM_CODE_END(__sdei_asm_entry_trampoline)

도구에 이 코드 범위의 끝을 알려 크기와 심볼 정보를 기록합니다. CPU가 이 줄 때문에 자동으로 반환하지는 않습니다.

945행 그림 · 어셈블·링크 단계

함수·코드 범위: __sdei_asm_entry_trampoline

이 줄을 포함하는 조건: #ifdef CONFIG_ARM_SDE_INTERFACE / #ifdef CONFIG_UNMAP_KERNEL_AT_EL0

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과SYM_CODE_END(__sdei_asm_entry_trampoline)
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 끝 표기 자체가 RET나 분기를 실행하지는 않습니다.
NOKPROBE(__sdei_asm_entry_trampoline)

__sdei_asm_entry_trampoline을 kprobe 설치 금지 목록에 넣습니다. 스택·레지스터·주소 공간을 바꾸는 예외/전환 경로 중간에 probe 예외를 추가하지 못하게 보호합니다. CONFIG_KPROBES가 꺼지면 이 목록 항목도 필요 없습니다. 빌드할 때 심벌을 가리키는 메타데이터를 만드는 표기이며, 이 위치에서 함수를 호출하는 명령은 아닙니다.

946행 그림 · 어셈블·링크 단계

이 줄을 포함하는 조건: #ifdef CONFIG_ARM_SDE_INTERFACE / #ifdef CONFIG_UNMAP_KERNEL_AT_EL0

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과NOKPROBE(__sdei_asm_entry_trampoline)
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

SYM_CODE_START(__sdei_asm_exit_trampoline)

이 위치부터 (__sdei_asm_exit_trampoline)라는 코드가 시작한다고 도구에 알려 줍니다. 정렬과 외부 공개 여부도 정의에 포함됩니다. 실제 작업은 아래의 명령들이 수행합니다.

955행 그림 · 어셈블·링크 단계

함수·코드 범위: __sdei_asm_exit_trampoline

이 줄을 포함하는 조건: #ifdef CONFIG_ARM_SDE_INTERFACE / #ifdef CONFIG_UNMAP_KERNEL_AT_EL0

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과SYM_CODE_START(__sdei_asm_exit_trampoline)
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	ldr	x4, [x4, #(SDEI_EVENT_INTREGS + S_SDEI_TTBR1)]

SDEI 진입 때 남긴 TTBR1 상태 표식을 읽습니다. 현재 이벤트가 사용자 최소 매핑에서 들어왔는지에 따라 원래 매핑으로 되돌릴지 결정합니다. 실제로 x4 + ((SDEI_EVENT_INTREGS + S_SDEI_TTBR1)) 주소의 메모리 8바이트를 x4에 읽습니다.

956행 그림 · 메모리 읽기

함수·코드 범위: __sdei_asm_exit_trampoline

이 줄을 포함하는 조건: #ifdef CONFIG_ARM_SDE_INTERFACE / #ifdef CONFIG_UNMAP_KERNEL_AT_EL0

읽는 값x4
메모리[이전 x4 + ((SDEI_EVENT_INTREGS + S_SDEI_TTBR1))] · 8바이트
이 명령을 실행하기 직전M0 (이 주소에 저장된 값)
이 명령을 실행한 직후M0 (이 주소에 저장된 값)
x4
이 명령을 실행하기 직전명령 직전 x4의 값
이 명령을 실행한 직후M0

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 주소 계산에 의한 기준 레지스터 갱신은 없습니다. 다만 LDR/LDP의 목적 레지스터가 기준 레지스터와 같으면 읽은 값이 그 레지스터를 덮어씁니다.
	cbnz	x4, 1f

0이 아니면 진입 전에도 커널 매핑이었으므로 unmap을 생략합니다. 0이면 이 이벤트 때문에 복구했던 커널 매핑을 다시 감춥니다. 이 분기는 지정 레지스터의 값이나 비트를 직접 검사하며 CMP가 남긴 NZCV를 읽거나 바꾸지 않습니다. 조건이 맞지 않으면 바로 다음 명령으로 진행합니다.

957행 그림 · 조건별 실행 위치

함수·코드 범위: __sdei_asm_exit_trampoline

이 줄을 포함하는 조건: #ifdef CONFIG_ARM_SDE_INTERFACE / #ifdef CONFIG_UNMAP_KERNEL_AT_EL0

읽는 값x4
PC
이 명령을 실행하기 직전명령 직전 PC의 값
이 명령을 실행한 직후명령 직전 x4의 값 ≠ 0이면 1f (L961), 아니면 다음 명령

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 조건이 맞는 한 경로만 실행합니다. 이 분기 명령은 NZCV를 새로 계산하지 않습니다.
	tramp_unmap_kernel	tmp=x4

사용자 실행으로 돌아가기 전에 커널의 넓은 매핑을 다시 감춥니다. 이 이후에도 실행할 최소 trampoline 코드는 남아 있어야 합니다. 여기의 인자는 tmp=x4입니다.

959행 그림 · 매크로 전체의 주요 상태

함수·코드 범위: __sdei_asm_exit_trampoline

이 줄을 포함하는 조건: #ifdef CONFIG_ARM_SDE_INTERFACE / #ifdef CONFIG_UNMAP_KERNEL_AT_EL0

TTBR1_EL1
이 명령을 실행하기 직전명령 직전 TTBR1_EL1의 값
이 명령을 실행한 직후(명령 직전 TTBR1_EL1의 값 − TRAMP_SWAPPER_OFFSET) OR USER_ASID_FLAG

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • tmp=x4을 임시 레지스터로 사용합니다. map 경로의 Falkor 우회에서는 이 임시값을 TLBI 피연산자로 다시 구성할 수 있습니다.
  • 여러 명령과 조건부 경로를 묶은 그림입니다. 이 목록이 모든 임시 레지스터의 변경을 열거하는 것은 아닙니다. 아래 매크로 본문에서 각 단계를 확인하실 수 있습니다.
tramp_unmap_kernel 매크로 본문 · 642행

arch/arm64/kernel/entry.S · Linux v6.18.37

.macro tramp_unmap_kernel, tmp mrs \tmp, ttbr1_el1 sub \tmp, \tmp, #TRAMP_SWAPPER_OFFSET orr \tmp, \tmp, #USER_ASID_FLAG msr ttbr1_el1, \tmp /* * We avoid running the post_ttbr_update_workaround here because * it's only needed by Cavium ThunderX, which requires KPTI to be * disabled. */ .endm
1:	sdei_handler_exit exit_mode=x2

펌웨어에게 이벤트 처리가 끝났다고 알리고 실행을 넘깁니다. 성공하면 보통 함수처럼 다음 줄로 돌아오지 않는 경로입니다. 여기의 인자는 exit_mode=x2입니다.

961행 그림 · 매크로 전체의 주요 상태

함수·코드 범위: __sdei_asm_exit_trampoline

이 줄을 포함하는 조건: #ifdef CONFIG_ARM_SDE_INTERFACE / #ifdef CONFIG_UNMAP_KERNEL_AT_EL0

PC
이 명령을 실행하기 직전명령 직전 PC의 값
이 명령을 실행한 직후exit_mode=x2=SDEI_EXIT_SMC이면 SMC, 그 외에는 HVC의 예외 처리기
NZCV
이 명령을 실행하기 직전명령 직전 NZCV의 값
이 명령을 실행한 직후exit_mode=x2 − SDEI_EXIT_SMC의 비교 결과

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 성공한 이벤트 완료 요청은 돌아오지 않습니다. 호출이 반환하면 바로 뒤의 B .에서 반복합니다. 요청 인자 레지스터는 앞선 줄에서 준비합니다.
  • 여러 명령과 조건부 경로를 묶은 그림입니다. 이 목록이 모든 임시 레지스터의 변경을 열거하는 것은 아닙니다. 아래 매크로 본문에서 각 단계를 확인하실 수 있습니다.
sdei_handler_exit 매크로 본문 · 911행

arch/arm64/kernel/entry.S · Linux v6.18.37

정의 선택 조건: #ifdef CONFIG_ARM_SDE_INTERFACE

.macro sdei_handler_exit exit_mode /* On success, this call never returns... */ cmp \exit_mode, #SDEI_EXIT_SMC b.ne 99f smc #0 b . 99: hvc #0 b . .endm
SYM_CODE_END(__sdei_asm_exit_trampoline)

도구에 이 코드 범위의 끝을 알려 크기와 심볼 정보를 기록합니다. CPU가 이 줄 때문에 자동으로 반환하지는 않습니다.

962행 그림 · 어셈블·링크 단계

함수·코드 범위: __sdei_asm_exit_trampoline

이 줄을 포함하는 조건: #ifdef CONFIG_ARM_SDE_INTERFACE / #ifdef CONFIG_UNMAP_KERNEL_AT_EL0

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과SYM_CODE_END(__sdei_asm_exit_trampoline)
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 끝 표기 자체가 RET나 분기를 실행하지는 않습니다.
NOKPROBE(__sdei_asm_exit_trampoline)

__sdei_asm_exit_trampoline을 kprobe 설치 금지 목록에 넣습니다. 스택·레지스터·주소 공간을 바꾸는 예외/전환 경로 중간에 probe 예외를 추가하지 못하게 보호합니다. CONFIG_KPROBES가 꺼지면 이 목록 항목도 필요 없습니다. 빌드할 때 심벌을 가리키는 메타데이터를 만드는 표기이며, 이 위치에서 함수를 호출하는 명령은 아닙니다.

963행 그림 · 어셈블·링크 단계

이 줄을 포함하는 조건: #ifdef CONFIG_ARM_SDE_INTERFACE / #ifdef CONFIG_UNMAP_KERNEL_AT_EL0

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과NOKPROBE(__sdei_asm_exit_trampoline)
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

.popsection		// .entry.tramp.text

이후 출력할 바이트를 어느 섹션에 넣을지 바꿉니다. 빌드 도구가 코드를 분류하는 작업이며 현재 CPU의 PC나 SP를 옮기는 줄은 아닙니다.

964행 그림 · 어셈블·링크 단계

이 줄을 포함하는 조건: #ifdef CONFIG_ARM_SDE_INTERFACE / #ifdef CONFIG_UNMAP_KERNEL_AT_EL0

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과.popsection
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

#endif /* CONFIG_UNMAP_KERNEL_AT_EL0 */

빌드 조건으로 나누었던 구간을 닫습니다. 대상:

965행 그림 · 전처리 단계

이 줄을 포함하는 조건: #ifdef CONFIG_ARM_SDE_INTERFACE / #ifdef CONFIG_UNMAP_KERNEL_AT_EL0

소스
처리 전전처리 전
이 줄의 효과#endif
CPU 레지스터
처리 전해당 없음
이 줄의 효과실행되는 CPU 명령이 없습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

SDEI 레지스터·스택 준비

원본 967–1050행

SYM_CODE_START(__sdei_asm_handler)

이 위치부터 (__sdei_asm_handler)라는 코드가 시작한다고 도구에 알려 줍니다. 정렬과 외부 공개 여부도 정의에 포함됩니다. 실제 작업은 아래의 명령들이 수행합니다.

980행 그림 · 어셈블·링크 단계

함수·코드 범위: __sdei_asm_handler

이 줄을 포함하는 조건: #ifdef CONFIG_ARM_SDE_INTERFACE

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과SYM_CODE_START(__sdei_asm_handler)
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	stp     x2, x3, [x1, #SDEI_EVENT_INTREGS + S_PC]

x2, x3의 값을 x1를 기준으로 SDEI_EVENT_INTREGS + S_PC만큼 계산한 메모리 위치에 기록합니다. 레지스터 값을 나중에 다시 쓰거나 다른 코드가 읽을 수 있도록 메모리에 남기는 줄입니다. 첫 위치에는 x2, 그보다 8바이트 높은 위치에는 x3의 값이 들어갑니다.

981행 그림 · 메모리 쓰기

함수·코드 범위: __sdei_asm_handler

이 줄을 포함하는 조건: #ifdef CONFIG_ARM_SDE_INTERFACE

읽는 값x1x2x3
메모리[이전 x1 + (SDEI_EVENT_INTREGS + S_PC)] · 8바이트
이 명령을 실행하기 직전기존 메모리 값
이 명령을 실행한 직후명령 직전 x2의 값
메모리[이전 x1 + (SDEI_EVENT_INTREGS + S_PC) + 8] · 8바이트
이 명령을 실행하기 직전기존 메모리 값
이 명령을 실행한 직후명령 직전 x3의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 주소 계산에 의한 기준 레지스터 갱신은 없습니다. 다만 LDR/LDP의 목적 레지스터가 기준 레지스터와 같으면 읽은 값이 그 레지스터를 덮어씁니다.
	stp     x4, x5, [x1, #SDEI_EVENT_INTREGS + 16 * 2]

x4, x5의 값을 x1를 기준으로 SDEI_EVENT_INTREGS + 16 * 2만큼 계산한 메모리 위치에 기록합니다. 레지스터 값을 나중에 다시 쓰거나 다른 코드가 읽을 수 있도록 메모리에 남기는 줄입니다. 첫 위치에는 x4, 그보다 8바이트 높은 위치에는 x5의 값이 들어갑니다.

982행 그림 · 메모리 쓰기

함수·코드 범위: __sdei_asm_handler

이 줄을 포함하는 조건: #ifdef CONFIG_ARM_SDE_INTERFACE

읽는 값x1x4x5
메모리[이전 x1 + (SDEI_EVENT_INTREGS + 16 * 2)] · 8바이트
이 명령을 실행하기 직전기존 메모리 값
이 명령을 실행한 직후명령 직전 x4의 값
메모리[이전 x1 + (SDEI_EVENT_INTREGS + 16 * 2) + 8] · 8바이트
이 명령을 실행하기 직전기존 메모리 값
이 명령을 실행한 직후명령 직전 x5의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 주소 계산에 의한 기준 레지스터 갱신은 없습니다. 다만 LDR/LDP의 목적 레지스터가 기준 레지스터와 같으면 읽은 값이 그 레지스터를 덮어씁니다.
	stp     x6, x7, [x1, #SDEI_EVENT_INTREGS + 16 * 3]

x6, x7의 값을 x1를 기준으로 SDEI_EVENT_INTREGS + 16 * 3만큼 계산한 메모리 위치에 기록합니다. 레지스터 값을 나중에 다시 쓰거나 다른 코드가 읽을 수 있도록 메모리에 남기는 줄입니다. 첫 위치에는 x6, 그보다 8바이트 높은 위치에는 x7의 값이 들어갑니다.

983행 그림 · 메모리 쓰기

함수·코드 범위: __sdei_asm_handler

이 줄을 포함하는 조건: #ifdef CONFIG_ARM_SDE_INTERFACE

읽는 값x1x6x7
메모리[이전 x1 + (SDEI_EVENT_INTREGS + 16 * 3)] · 8바이트
이 명령을 실행하기 직전기존 메모리 값
이 명령을 실행한 직후명령 직전 x6의 값
메모리[이전 x1 + (SDEI_EVENT_INTREGS + 16 * 3) + 8] · 8바이트
이 명령을 실행하기 직전기존 메모리 값
이 명령을 실행한 직후명령 직전 x7의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 주소 계산에 의한 기준 레지스터 갱신은 없습니다. 다만 LDR/LDP의 목적 레지스터가 기준 레지스터와 같으면 읽은 값이 그 레지스터를 덮어씁니다.
	stp     x8, x9, [x1, #SDEI_EVENT_INTREGS + 16 * 4]

x8, x9의 값을 x1를 기준으로 SDEI_EVENT_INTREGS + 16 * 4만큼 계산한 메모리 위치에 기록합니다. 레지스터 값을 나중에 다시 쓰거나 다른 코드가 읽을 수 있도록 메모리에 남기는 줄입니다. 첫 위치에는 x8, 그보다 8바이트 높은 위치에는 x9의 값이 들어갑니다.

984행 그림 · 메모리 쓰기

함수·코드 범위: __sdei_asm_handler

이 줄을 포함하는 조건: #ifdef CONFIG_ARM_SDE_INTERFACE

읽는 값x1x8x9
메모리[이전 x1 + (SDEI_EVENT_INTREGS + 16 * 4)] · 8바이트
이 명령을 실행하기 직전기존 메모리 값
이 명령을 실행한 직후명령 직전 x8의 값
메모리[이전 x1 + (SDEI_EVENT_INTREGS + 16 * 4) + 8] · 8바이트
이 명령을 실행하기 직전기존 메모리 값
이 명령을 실행한 직후명령 직전 x9의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 주소 계산에 의한 기준 레지스터 갱신은 없습니다. 다만 LDR/LDP의 목적 레지스터가 기준 레지스터와 같으면 읽은 값이 그 레지스터를 덮어씁니다.
	stp     x10, x11, [x1, #SDEI_EVENT_INTREGS + 16 * 5]

x10, x11의 값을 x1를 기준으로 SDEI_EVENT_INTREGS + 16 * 5만큼 계산한 메모리 위치에 기록합니다. 레지스터 값을 나중에 다시 쓰거나 다른 코드가 읽을 수 있도록 메모리에 남기는 줄입니다. 첫 위치에는 x10, 그보다 8바이트 높은 위치에는 x11의 값이 들어갑니다.

985행 그림 · 메모리 쓰기

함수·코드 범위: __sdei_asm_handler

이 줄을 포함하는 조건: #ifdef CONFIG_ARM_SDE_INTERFACE

읽는 값x1x10x11
메모리[이전 x1 + (SDEI_EVENT_INTREGS + 16 * 5)] · 8바이트
이 명령을 실행하기 직전기존 메모리 값
이 명령을 실행한 직후명령 직전 x10의 값
메모리[이전 x1 + (SDEI_EVENT_INTREGS + 16 * 5) + 8] · 8바이트
이 명령을 실행하기 직전기존 메모리 값
이 명령을 실행한 직후명령 직전 x11의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 주소 계산에 의한 기준 레지스터 갱신은 없습니다. 다만 LDR/LDP의 목적 레지스터가 기준 레지스터와 같으면 읽은 값이 그 레지스터를 덮어씁니다.
	stp     x12, x13, [x1, #SDEI_EVENT_INTREGS + 16 * 6]

x12, x13의 값을 x1를 기준으로 SDEI_EVENT_INTREGS + 16 * 6만큼 계산한 메모리 위치에 기록합니다. 레지스터 값을 나중에 다시 쓰거나 다른 코드가 읽을 수 있도록 메모리에 남기는 줄입니다. 첫 위치에는 x12, 그보다 8바이트 높은 위치에는 x13의 값이 들어갑니다.

986행 그림 · 메모리 쓰기

함수·코드 범위: __sdei_asm_handler

이 줄을 포함하는 조건: #ifdef CONFIG_ARM_SDE_INTERFACE

읽는 값x1x12x13
메모리[이전 x1 + (SDEI_EVENT_INTREGS + 16 * 6)] · 8바이트
이 명령을 실행하기 직전기존 메모리 값
이 명령을 실행한 직후명령 직전 x12의 값
메모리[이전 x1 + (SDEI_EVENT_INTREGS + 16 * 6) + 8] · 8바이트
이 명령을 실행하기 직전기존 메모리 값
이 명령을 실행한 직후명령 직전 x13의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 주소 계산에 의한 기준 레지스터 갱신은 없습니다. 다만 LDR/LDP의 목적 레지스터가 기준 레지스터와 같으면 읽은 값이 그 레지스터를 덮어씁니다.
	stp     x14, x15, [x1, #SDEI_EVENT_INTREGS + 16 * 7]

x14, x15의 값을 x1를 기준으로 SDEI_EVENT_INTREGS + 16 * 7만큼 계산한 메모리 위치에 기록합니다. 레지스터 값을 나중에 다시 쓰거나 다른 코드가 읽을 수 있도록 메모리에 남기는 줄입니다. 첫 위치에는 x14, 그보다 8바이트 높은 위치에는 x15의 값이 들어갑니다.

987행 그림 · 메모리 쓰기

함수·코드 범위: __sdei_asm_handler

이 줄을 포함하는 조건: #ifdef CONFIG_ARM_SDE_INTERFACE

읽는 값x1x14x15
메모리[이전 x1 + (SDEI_EVENT_INTREGS + 16 * 7)] · 8바이트
이 명령을 실행하기 직전기존 메모리 값
이 명령을 실행한 직후명령 직전 x14의 값
메모리[이전 x1 + (SDEI_EVENT_INTREGS + 16 * 7) + 8] · 8바이트
이 명령을 실행하기 직전기존 메모리 값
이 명령을 실행한 직후명령 직전 x15의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 주소 계산에 의한 기준 레지스터 갱신은 없습니다. 다만 LDR/LDP의 목적 레지스터가 기준 레지스터와 같으면 읽은 값이 그 레지스터를 덮어씁니다.
	stp     x16, x17, [x1, #SDEI_EVENT_INTREGS + 16 * 8]

x16, x17의 값을 x1를 기준으로 SDEI_EVENT_INTREGS + 16 * 8만큼 계산한 메모리 위치에 기록합니다. 레지스터 값을 나중에 다시 쓰거나 다른 코드가 읽을 수 있도록 메모리에 남기는 줄입니다. 첫 위치에는 x16, 그보다 8바이트 높은 위치에는 x17의 값이 들어갑니다.

988행 그림 · 메모리 쓰기

함수·코드 범위: __sdei_asm_handler

이 줄을 포함하는 조건: #ifdef CONFIG_ARM_SDE_INTERFACE

읽는 값x1x16x17
메모리[이전 x1 + (SDEI_EVENT_INTREGS + 16 * 8)] · 8바이트
이 명령을 실행하기 직전기존 메모리 값
이 명령을 실행한 직후명령 직전 x16의 값
메모리[이전 x1 + (SDEI_EVENT_INTREGS + 16 * 8) + 8] · 8바이트
이 명령을 실행하기 직전기존 메모리 값
이 명령을 실행한 직후명령 직전 x17의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 주소 계산에 의한 기준 레지스터 갱신은 없습니다. 다만 LDR/LDP의 목적 레지스터가 기준 레지스터와 같으면 읽은 값이 그 레지스터를 덮어씁니다.
	stp     x18, x19, [x1, #SDEI_EVENT_INTREGS + 16 * 9]

x18, x19의 값을 x1를 기준으로 SDEI_EVENT_INTREGS + 16 * 9만큼 계산한 메모리 위치에 기록합니다. 레지스터 값을 나중에 다시 쓰거나 다른 코드가 읽을 수 있도록 메모리에 남기는 줄입니다. 첫 위치에는 x18, 그보다 8바이트 높은 위치에는 x19의 값이 들어갑니다.

989행 그림 · 메모리 쓰기

함수·코드 범위: __sdei_asm_handler

이 줄을 포함하는 조건: #ifdef CONFIG_ARM_SDE_INTERFACE

읽는 값x1x18x19
메모리[이전 x1 + (SDEI_EVENT_INTREGS + 16 * 9)] · 8바이트
이 명령을 실행하기 직전기존 메모리 값
이 명령을 실행한 직후명령 직전 x18의 값
메모리[이전 x1 + (SDEI_EVENT_INTREGS + 16 * 9) + 8] · 8바이트
이 명령을 실행하기 직전기존 메모리 값
이 명령을 실행한 직후명령 직전 x19의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 주소 계산에 의한 기준 레지스터 갱신은 없습니다. 다만 LDR/LDP의 목적 레지스터가 기준 레지스터와 같으면 읽은 값이 그 레지스터를 덮어씁니다.
	stp     x20, x21, [x1, #SDEI_EVENT_INTREGS + 16 * 10]

x20, x21의 값을 x1를 기준으로 SDEI_EVENT_INTREGS + 16 * 10만큼 계산한 메모리 위치에 기록합니다. 레지스터 값을 나중에 다시 쓰거나 다른 코드가 읽을 수 있도록 메모리에 남기는 줄입니다. 첫 위치에는 x20, 그보다 8바이트 높은 위치에는 x21의 값이 들어갑니다.

990행 그림 · 메모리 쓰기

함수·코드 범위: __sdei_asm_handler

이 줄을 포함하는 조건: #ifdef CONFIG_ARM_SDE_INTERFACE

읽는 값x1x20x21
메모리[이전 x1 + (SDEI_EVENT_INTREGS + 16 * 10)] · 8바이트
이 명령을 실행하기 직전기존 메모리 값
이 명령을 실행한 직후명령 직전 x20의 값
메모리[이전 x1 + (SDEI_EVENT_INTREGS + 16 * 10) + 8] · 8바이트
이 명령을 실행하기 직전기존 메모리 값
이 명령을 실행한 직후명령 직전 x21의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 주소 계산에 의한 기준 레지스터 갱신은 없습니다. 다만 LDR/LDP의 목적 레지스터가 기준 레지스터와 같으면 읽은 값이 그 레지스터를 덮어씁니다.
	stp     x22, x23, [x1, #SDEI_EVENT_INTREGS + 16 * 11]

x22, x23의 값을 x1를 기준으로 SDEI_EVENT_INTREGS + 16 * 11만큼 계산한 메모리 위치에 기록합니다. 레지스터 값을 나중에 다시 쓰거나 다른 코드가 읽을 수 있도록 메모리에 남기는 줄입니다. 첫 위치에는 x22, 그보다 8바이트 높은 위치에는 x23의 값이 들어갑니다.

991행 그림 · 메모리 쓰기

함수·코드 범위: __sdei_asm_handler

이 줄을 포함하는 조건: #ifdef CONFIG_ARM_SDE_INTERFACE

읽는 값x1x22x23
메모리[이전 x1 + (SDEI_EVENT_INTREGS + 16 * 11)] · 8바이트
이 명령을 실행하기 직전기존 메모리 값
이 명령을 실행한 직후명령 직전 x22의 값
메모리[이전 x1 + (SDEI_EVENT_INTREGS + 16 * 11) + 8] · 8바이트
이 명령을 실행하기 직전기존 메모리 값
이 명령을 실행한 직후명령 직전 x23의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 주소 계산에 의한 기준 레지스터 갱신은 없습니다. 다만 LDR/LDP의 목적 레지스터가 기준 레지스터와 같으면 읽은 값이 그 레지스터를 덮어씁니다.
	stp     x24, x25, [x1, #SDEI_EVENT_INTREGS + 16 * 12]

x24, x25의 값을 x1를 기준으로 SDEI_EVENT_INTREGS + 16 * 12만큼 계산한 메모리 위치에 기록합니다. 레지스터 값을 나중에 다시 쓰거나 다른 코드가 읽을 수 있도록 메모리에 남기는 줄입니다. 첫 위치에는 x24, 그보다 8바이트 높은 위치에는 x25의 값이 들어갑니다.

992행 그림 · 메모리 쓰기

함수·코드 범위: __sdei_asm_handler

이 줄을 포함하는 조건: #ifdef CONFIG_ARM_SDE_INTERFACE

읽는 값x1x24x25
메모리[이전 x1 + (SDEI_EVENT_INTREGS + 16 * 12)] · 8바이트
이 명령을 실행하기 직전기존 메모리 값
이 명령을 실행한 직후명령 직전 x24의 값
메모리[이전 x1 + (SDEI_EVENT_INTREGS + 16 * 12) + 8] · 8바이트
이 명령을 실행하기 직전기존 메모리 값
이 명령을 실행한 직후명령 직전 x25의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 주소 계산에 의한 기준 레지스터 갱신은 없습니다. 다만 LDR/LDP의 목적 레지스터가 기준 레지스터와 같으면 읽은 값이 그 레지스터를 덮어씁니다.
	stp     x26, x27, [x1, #SDEI_EVENT_INTREGS + 16 * 13]

x26, x27의 값을 x1를 기준으로 SDEI_EVENT_INTREGS + 16 * 13만큼 계산한 메모리 위치에 기록합니다. 레지스터 값을 나중에 다시 쓰거나 다른 코드가 읽을 수 있도록 메모리에 남기는 줄입니다. 첫 위치에는 x26, 그보다 8바이트 높은 위치에는 x27의 값이 들어갑니다.

993행 그림 · 메모리 쓰기

함수·코드 범위: __sdei_asm_handler

이 줄을 포함하는 조건: #ifdef CONFIG_ARM_SDE_INTERFACE

읽는 값x1x26x27
메모리[이전 x1 + (SDEI_EVENT_INTREGS + 16 * 13)] · 8바이트
이 명령을 실행하기 직전기존 메모리 값
이 명령을 실행한 직후명령 직전 x26의 값
메모리[이전 x1 + (SDEI_EVENT_INTREGS + 16 * 13) + 8] · 8바이트
이 명령을 실행하기 직전기존 메모리 값
이 명령을 실행한 직후명령 직전 x27의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 주소 계산에 의한 기준 레지스터 갱신은 없습니다. 다만 LDR/LDP의 목적 레지스터가 기준 레지스터와 같으면 읽은 값이 그 레지스터를 덮어씁니다.
	stp     x28, x29, [x1, #SDEI_EVENT_INTREGS + 16 * 14]

x28, x29의 값을 x1를 기준으로 SDEI_EVENT_INTREGS + 16 * 14만큼 계산한 메모리 위치에 기록합니다. 레지스터 값을 나중에 다시 쓰거나 다른 코드가 읽을 수 있도록 메모리에 남기는 줄입니다. 첫 위치에는 x28, 그보다 8바이트 높은 위치에는 x29의 값이 들어갑니다.

994행 그림 · 메모리 쓰기

함수·코드 범위: __sdei_asm_handler

이 줄을 포함하는 조건: #ifdef CONFIG_ARM_SDE_INTERFACE

읽는 값x1x28x29
메모리[이전 x1 + (SDEI_EVENT_INTREGS + 16 * 14)] · 8바이트
이 명령을 실행하기 직전기존 메모리 값
이 명령을 실행한 직후명령 직전 x28의 값
메모리[이전 x1 + (SDEI_EVENT_INTREGS + 16 * 14) + 8] · 8바이트
이 명령을 실행하기 직전기존 메모리 값
이 명령을 실행한 직후명령 직전 x29의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 주소 계산에 의한 기준 레지스터 갱신은 없습니다. 다만 LDR/LDP의 목적 레지스터가 기준 레지스터와 같으면 읽은 값이 그 레지스터를 덮어씁니다.
	mov	x4, sp

현재 SP의 숫자를 x4에 기억해 둡니다. 스택 자체는 이동하거나 복사되지 않습니다. 나중에 이 값으로 원래 위치를 찾을 수 있습니다.

995행 그림 · 값 복사

함수·코드 범위: __sdei_asm_handler

이 줄을 포함하는 조건: #ifdef CONFIG_ARM_SDE_INTERFACE

읽는 값sp
x4
이 명령을 실행하기 직전명령 직전 x4의 값
이 명령을 실행한 직후명령 직전 sp의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 화살표는 값의 변화를 뜻합니다. 주소값을 옮겨도 그 주소의 메모리 내용은 복사하지 않습니다.
	stp     lr, x4, [x1, #SDEI_EVENT_INTREGS + S_LR]

lr, x4의 값을 x1를 기준으로 SDEI_EVENT_INTREGS + S_LR만큼 계산한 메모리 위치에 기록합니다. 레지스터 값을 나중에 다시 쓰거나 다른 코드가 읽을 수 있도록 메모리에 남기는 줄입니다.

996행 그림 · 메모리 쓰기

함수·코드 범위: __sdei_asm_handler

이 줄을 포함하는 조건: #ifdef CONFIG_ARM_SDE_INTERFACE

읽는 값x1lrx4
메모리[이전 x1 + (SDEI_EVENT_INTREGS + S_LR)] · 8바이트
이 명령을 실행하기 직전기존 메모리 값
이 명령을 실행한 직후명령 직전 lr의 값
메모리[이전 x1 + (SDEI_EVENT_INTREGS + S_LR) + 8] · 8바이트
이 명령을 실행하기 직전기존 메모리 값
이 명령을 실행한 직후명령 직전 x4의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 주소 계산에 의한 기준 레지스터 갱신은 없습니다. 다만 LDR/LDP의 목적 레지스터가 기준 레지스터와 같으면 읽은 값이 그 레지스터를 덮어씁니다.
	mov	x19, x1

x19에 x1에 들어 있는 값을 넣습니다. 원래 x19의 내용은 덮어쓰며, 다른 레지스터나 그 주소의 메모리까지 복사하지는 않습니다.

998행 그림 · 값 복사

함수·코드 범위: __sdei_asm_handler

이 줄을 포함하는 조건: #ifdef CONFIG_ARM_SDE_INTERFACE

읽는 값x1
x19
이 명령을 실행하기 직전명령 직전 x19의 값
이 명령을 실행한 직후명령 직전 x1의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 화살표는 값의 변화를 뜻합니다. 주소값을 옮겨도 그 주소의 메모리 내용은 복사하지 않습니다.
	ldrb	w4, [x19, #SDEI_EVENT_PRIORITY]

등록된 SDEI 이벤트의 normal/critical 우선순위를 읽습니다. 두 이벤트가 중첩될 수 있으므로 실행 중 이벤트 포인터와 스택을 우선순위별로 분리합니다. 실제로 x19 + (SDEI_EVENT_PRIORITY) 주소의 메모리 1바이트를 w4에 읽습니다. 이 주소 형식은 기준 레지스터 x19 자체를 갱신하지 않습니다.

1001행 그림 · 메모리 읽기

함수·코드 범위: __sdei_asm_handler

이 줄을 포함하는 조건: #ifdef CONFIG_ARM_SDE_INTERFACE

읽는 값x19
메모리[이전 x19 + (SDEI_EVENT_PRIORITY)] · 1바이트
이 명령을 실행하기 직전M0 (이 주소에 저장된 값)
이 명령을 실행한 직후M0 (이 주소에 저장된 값)
w4
이 명령을 실행하기 직전명령 직전 w4의 값
이 명령을 실행한 직후M0를 0으로 확장
x4[63:32]
이 명령을 실행하기 직전명령 직전 x4[63:32]의 값
이 명령을 실행한 직후0

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 주소 계산에 의한 기준 레지스터 갱신은 없습니다. 다만 LDR/LDP의 목적 레지스터가 기준 레지스터와 같으면 읽은 값이 그 레지스터를 덮어씁니다.
	cbnz	w4, 1f

w4의 값가 0이 아니면 1f (L1005)로 갑니다. 조건을 만족하지 않으면 바로 다음 명령을 읽습니다. 이 분기는 CMP의 결과가 아니라 지정 레지스터를 직접 검사합니다.

1002행 그림 · 조건별 실행 위치

함수·코드 범위: __sdei_asm_handler

이 줄을 포함하는 조건: #ifdef CONFIG_ARM_SDE_INTERFACE

읽는 값w4
PC
이 명령을 실행하기 직전명령 직전 PC의 값
이 명령을 실행한 직후명령 직전 w4의 값 ≠ 0이면 1f (L1005), 아니면 다음 명령

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 조건이 맞는 한 경로만 실행합니다. 이 분기 명령은 NZCV를 새로 계산하지 않습니다.
	adr_this_cpu dst=x5, sym=sdei_active_normal_event, tmp=x6

이 CPU의 normal SDEI 활성 이벤트 포인터 슬롯 주소를 x5에 구합니다. 이어지는 store가 현재 이벤트 객체를 여기에 기록하며 x6는 주소 계산용입니다. 심벌의 기준 주소에 현재 CPU의 per-CPU 오프셋을 더해 이 CPU의 저장 위치를 찾습니다.

1003행 그림 · 주소·값 구분

함수·코드 범위: __sdei_asm_handler

이 줄을 포함하는 조건: #ifdef CONFIG_ARM_SDE_INTERFACE

dst=x5
이 명령을 실행하기 직전명령 직전 dst의 값=x5
이 명령을 실행한 직후주소(sym=sdei_active_normal_event) + 현재 CPU 오프셋

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 이 매크로의 임시 레지스터는 내부에서 사용됩니다.
adr_this_cpu 매크로 본문 · 238행

arch/arm64/include/asm/assembler.h · Linux v6.18.37

정의 선택 조건: #ifndef __ASM_ASSEMBLER_H

.macro adr_this_cpu, dst, sym, tmp adrp \tmp, \sym add \dst, \tmp, #:lo12:\sym get_this_cpu_offset \tmp add \dst, \dst, \tmp .endm
	b	2f

2f (L1006)부터 실행을 계속합니다. 돌아올 주소를 새로 적지 않으므로 일반 함수 호출과는 다른 제어 이동입니다.

1004행 그림 · 호출과 실행 위치

함수·코드 범위: __sdei_asm_handler

이 줄을 포함하는 조건: #ifdef CONFIG_ARM_SDE_INTERFACE

PC
이 명령을 실행하기 직전명령 직전 PC의 값
이 명령을 실행한 직후2f (L1006)

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 새 복귀 주소를 LR에 기록하지 않습니다. RET 역시 메모리의 스택 값을 자동으로 꺼내는 명령은 아닙니다.
1:	adr_this_cpu dst=x5, sym=sdei_active_critical_event, tmp=x6

critical 이벤트는 별도의 CPU별 활성 포인터 슬롯을 사용합니다. normal 이벤트를 중첩 중단해도 두 종류의 활성 상태를 덮어쓰지 않도록 x5를 critical 슬롯으로 바꿉니다. 심벌의 기준 주소에 현재 CPU의 per-CPU 오프셋을 더해 이 CPU의 저장 위치를 찾습니다.

1005행 그림 · 주소·값 구분

함수·코드 범위: __sdei_asm_handler

이 줄을 포함하는 조건: #ifdef CONFIG_ARM_SDE_INTERFACE

dst=x5
이 명령을 실행하기 직전명령 직전 dst의 값=x5
이 명령을 실행한 직후주소(sym=sdei_active_critical_event) + 현재 CPU 오프셋

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 이 매크로의 임시 레지스터는 내부에서 사용됩니다.
adr_this_cpu 매크로 본문 · 238행

arch/arm64/include/asm/assembler.h · Linux v6.18.37

정의 선택 조건: #ifndef __ASM_ASSEMBLER_H

.macro adr_this_cpu, dst, sym, tmp adrp \tmp, \sym add \dst, \tmp, #:lo12:\sym get_this_cpu_offset \tmp add \dst, \dst, \tmp .endm
2:	str	x19, [x5]

현재 CPU가 처리 중인 이벤트 객체를 normal 또는 critical 슬롯에 기록합니다. crash_smp_send_stop 등의 비정상 종료 경로도 활성 이벤트를 찾을 수 있게 합니다. 실제로 x5 주소의 메모리 8바이트를 x19의 값으로 씁니다. 이 주소 형식은 기준 레지스터 x5 자체를 갱신하지 않습니다.

1006행 그림 · 메모리 쓰기

함수·코드 범위: __sdei_asm_handler

이 줄을 포함하는 조건: #ifdef CONFIG_ARM_SDE_INTERFACE

읽는 값x5x19
메모리[이전 x5] · 8바이트
이 명령을 실행하기 직전기존 메모리 값
이 명령을 실행한 직후명령 직전 x19의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 주소 계산에 의한 기준 레지스터 갱신은 없습니다. 다만 LDR/LDP의 목적 레지스터가 기준 레지스터와 같으면 읽은 값이 그 레지스터를 덮어씁니다.
	cbnz	w4, 1f

w4의 값가 0이 아니면 1f (L1016)로 갑니다. 조건을 만족하지 않으면 바로 다음 명령을 읽습니다. 이 분기는 CMP의 결과가 아니라 지정 레지스터를 직접 검사합니다.

1013행 그림 · 조건별 실행 위치

함수·코드 범위: __sdei_asm_handler

이 줄을 포함하는 조건: #ifdef CONFIG_ARM_SDE_INTERFACE

읽는 값w4
PC
이 명령을 실행하기 직전명령 직전 PC의 값
이 명령을 실행한 직후명령 직전 w4의 값 ≠ 0이면 1f (L1016), 아니면 다음 명령

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 조건이 맞는 한 경로만 실행합니다. 이 분기 명령은 NZCV를 새로 계산하지 않습니다.
	ldr_this_cpu dst=x5, sym=sdei_stack_normal_ptr, tmp=x6

normal SDEI 이벤트용 CPU별 스택을 선택합니다. 예외가 커널 진입의 임시 SP 사용 중간을 끊을 수도 있으므로 기존 SP를 신뢰하지 않고 별도 스택을 씁니다. 심벌의 기준 주소에 현재 CPU의 per-CPU 오프셋을 더해 이 CPU의 저장 위치를 찾습니다.

1014행 그림 · 주소·값 구분

함수·코드 범위: __sdei_asm_handler

이 줄을 포함하는 조건: #ifdef CONFIG_ARM_SDE_INTERFACE

dst=x5
이 명령을 실행하기 직전명령 직전 dst의 값=x5
이 명령을 실행한 직후메모리[주소(sym=sdei_stack_normal_ptr) + 현재 CPU 오프셋]의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 이 매크로의 임시 레지스터는 내부에서 사용됩니다.
ldr_this_cpu 매크로 본문 · 250행

arch/arm64/include/asm/assembler.h · Linux v6.18.37

정의 선택 조건: #ifndef __ASM_ASSEMBLER_H

.macro ldr_this_cpu dst, sym, tmp adr_l \dst, \sym get_this_cpu_offset \tmp ldr \dst, [\dst, \tmp] .endm
	b	2f

2f (L1017)부터 실행을 계속합니다. 돌아올 주소를 새로 적지 않으므로 일반 함수 호출과는 다른 제어 이동입니다.

1015행 그림 · 호출과 실행 위치

함수·코드 범위: __sdei_asm_handler

이 줄을 포함하는 조건: #ifdef CONFIG_ARM_SDE_INTERFACE

PC
이 명령을 실행하기 직전명령 직전 PC의 값
이 명령을 실행한 직후2f (L1017)

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 새 복귀 주소를 LR에 기록하지 않습니다. RET 역시 메모리의 스택 값을 자동으로 꺼내는 명령은 아닙니다.
1:	ldr_this_cpu dst=x5, sym=sdei_stack_critical_ptr, tmp=x6

critical 이벤트면 normal과 다른 CPU별 스택을 선택하여 critical 이벤트가 normal 처리의 스택을 덮지 않게 합니다. 심벌의 기준 주소에 현재 CPU의 per-CPU 오프셋을 더해 이 CPU의 저장 위치를 찾습니다.

1016행 그림 · 주소·값 구분

함수·코드 범위: __sdei_asm_handler

이 줄을 포함하는 조건: #ifdef CONFIG_ARM_SDE_INTERFACE

dst=x5
이 명령을 실행하기 직전명령 직전 dst의 값=x5
이 명령을 실행한 직후메모리[주소(sym=sdei_stack_critical_ptr) + 현재 CPU 오프셋]의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 이 매크로의 임시 레지스터는 내부에서 사용됩니다.
ldr_this_cpu 매크로 본문 · 250행

arch/arm64/include/asm/assembler.h · Linux v6.18.37

정의 선택 조건: #ifndef __ASM_ASSEMBLER_H

.macro ldr_this_cpu dst, sym, tmp adr_l \dst, \sym get_this_cpu_offset \tmp ldr \dst, [\dst, \tmp] .endm
2:	mov	x6, #SDEI_STACK_SIZE

x6에 SDEI_STACK_SIZE라는 값을 넣습니다. 원래 x6의 내용은 덮어쓰며, 다른 레지스터나 그 주소의 메모리까지 복사하지는 않습니다.

1017행 그림 · 값 복사

함수·코드 범위: __sdei_asm_handler

이 줄을 포함하는 조건: #ifdef CONFIG_ARM_SDE_INTERFACE

x6
이 명령을 실행하기 직전명령 직전 x6의 값
이 명령을 실행한 직후SDEI_STACK_SIZE

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 화살표는 값의 변화를 뜻합니다. 주소값을 옮겨도 그 주소의 메모리 내용은 복사하지 않습니다.
	add	x5, x5, x6

x5를 기준으로 x6을 더해 x5에 결과를 둡니다. 주소 계산에 쓰이는 경우에는 구조체의 필드나 다음 저장 위치를 찾는 동작입니다. 이 형태는 조건 플래그를 바꾸지 않습니다.

1018행 그림 · 레지스터 상태

함수·코드 범위: __sdei_asm_handler

이 줄을 포함하는 조건: #ifdef CONFIG_ARM_SDE_INTERFACE

읽는 값x5x6
x5
이 명령을 실행하기 직전명령 직전 x5의 값
이 명령을 실행한 직후명령 직전 x5의 값 + 명령 직전 x6의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 결과는 목적 레지스터 폭에 맞습니다. 이 형식은 NZCV를 바꾸지 않습니다.
	mov	sp, x5

이제 스택으로 사용할 위치를 x5의 값으로 바꿉니다. 메모리 내용을 옮기는 작업은 아닙니다. 다음 저장·호출부터 새 스택 위치를 사용합니다.

1019행 그림 · 값 복사

함수·코드 범위: __sdei_asm_handler

이 줄을 포함하는 조건: #ifdef CONFIG_ARM_SDE_INTERFACE

읽는 값x5
sp
이 명령을 실행하기 직전명령 직전 sp의 값
이 명령을 실행한 직후명령 직전 x5의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 화살표는 값의 변화를 뜻합니다. 주소값을 옮겨도 그 주소의 메모리 내용은 복사하지 않습니다.
#ifdef CONFIG_SHADOW_CALL_STACK

SCS 보호 빌드는 SDEI에서도 normal·critical 이벤트마다 별도 shadow call stack을 선택합니다. 일반 스택을 나눠도 보호용 복귀 스택을 공유하면 중첩 처리 문맥이 섞일 수 있기 때문입니다. 이 조건은 빌드 전처리 단계에서 포함할 코드를 고릅니다. CPU가 실행 중 전처리 조건을 검사하지는 않습니다.

1021행 그림 · 전처리 단계

함수·코드 범위: __sdei_asm_handler

이 줄을 포함하는 조건: #ifdef CONFIG_ARM_SDE_INTERFACE

소스
처리 전전처리 전
이 줄의 효과#ifdef CONFIG_SHADOW_CALL_STACK
CPU 레지스터
처리 전해당 없음
이 줄의 효과실행되는 CPU 명령이 없습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	cbnz	w4, 3f

w4의 값가 0이 아니면 3f (L1026)로 갑니다. 조건을 만족하지 않으면 바로 다음 명령을 읽습니다. 이 분기는 CMP의 결과가 아니라 지정 레지스터를 직접 검사합니다.

1023행 그림 · 조건별 실행 위치

함수·코드 범위: __sdei_asm_handler

이 줄을 포함하는 조건: #ifdef CONFIG_ARM_SDE_INTERFACE / #ifdef CONFIG_SHADOW_CALL_STACK

읽는 값w4
PC
이 명령을 실행하기 직전명령 직전 PC의 값
이 명령을 실행한 직후명령 직전 w4의 값 ≠ 0이면 3f (L1026), 아니면 다음 명령

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 조건이 맞는 한 경로만 실행합니다. 이 분기 명령은 NZCV를 새로 계산하지 않습니다.
	ldr_this_cpu dst=scs_sp, sym=sdei_shadow_call_stack_normal_ptr, tmp=x6

normal SDEI 이벤트 전용 shadow call stack 포인터를 x18에 읽습니다. 일반 task·IRQ의 보호용 복귀 스택과 구분하여 비동기 이벤트 중첩에 대비합니다. 심벌의 기준 주소에 현재 CPU의 per-CPU 오프셋을 더해 이 CPU의 저장 위치를 찾습니다.

1024행 그림 · 주소·값 구분

함수·코드 범위: __sdei_asm_handler

이 줄을 포함하는 조건: #ifdef CONFIG_ARM_SDE_INTERFACE / #ifdef CONFIG_SHADOW_CALL_STACK

dst=scs_sp
이 명령을 실행하기 직전명령 직전 dst의 값=scs_sp
이 명령을 실행한 직후메모리[주소(sym=sdei_shadow_call_stack_normal_ptr) + 현재 CPU 오프셋]의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 이 매크로의 임시 레지스터는 내부에서 사용됩니다.
ldr_this_cpu 매크로 본문 · 250행

arch/arm64/include/asm/assembler.h · Linux v6.18.37

정의 선택 조건: #ifndef __ASM_ASSEMBLER_H

.macro ldr_this_cpu dst, sym, tmp adr_l \dst, \sym get_this_cpu_offset \tmp ldr \dst, [\dst, \tmp] .endm
	b	4f

4f (L1027)부터 실행을 계속합니다. 돌아올 주소를 새로 적지 않으므로 일반 함수 호출과는 다른 제어 이동입니다.

1025행 그림 · 호출과 실행 위치

함수·코드 범위: __sdei_asm_handler

이 줄을 포함하는 조건: #ifdef CONFIG_ARM_SDE_INTERFACE / #ifdef CONFIG_SHADOW_CALL_STACK

PC
이 명령을 실행하기 직전명령 직전 PC의 값
이 명령을 실행한 직후4f (L1027)

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 새 복귀 주소를 LR에 기록하지 않습니다. RET 역시 메모리의 스택 값을 자동으로 꺼내는 명령은 아닙니다.
3:	ldr_this_cpu dst=scs_sp, sym=sdei_shadow_call_stack_critical_ptr, tmp=x6

critical 이벤트의 별도 shadow call stack을 선택합니다. normal SDEI handler를 중단한 경우에도 그 보호용 스택을 계속 소비하지 않도록 합니다. 심벌의 기준 주소에 현재 CPU의 per-CPU 오프셋을 더해 이 CPU의 저장 위치를 찾습니다.

1026행 그림 · 주소·값 구분

함수·코드 범위: __sdei_asm_handler

이 줄을 포함하는 조건: #ifdef CONFIG_ARM_SDE_INTERFACE / #ifdef CONFIG_SHADOW_CALL_STACK

dst=scs_sp
이 명령을 실행하기 직전명령 직전 dst의 값=scs_sp
이 명령을 실행한 직후메모리[주소(sym=sdei_shadow_call_stack_critical_ptr) + 현재 CPU 오프셋]의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 이 매크로의 임시 레지스터는 내부에서 사용됩니다.
ldr_this_cpu 매크로 본문 · 250행

arch/arm64/include/asm/assembler.h · Linux v6.18.37

정의 선택 조건: #ifndef __ASM_ASSEMBLER_H

.macro ldr_this_cpu dst, sym, tmp adr_l \dst, \sym get_this_cpu_offset \tmp ldr \dst, [\dst, \tmp] .endm
4:

여기에 4라는 위치 이름을 붙입니다. 다른 줄의 분기나 주소 계산이 이 위치를 찾아올 수 있습니다. 이름 자체가 CPU 작업을 추가하지는 않습니다.

1027행 그림 · 위치에 이름 붙이기

함수·코드 범위: __sdei_asm_handler

이 줄을 포함하는 조건: #ifdef CONFIG_ARM_SDE_INTERFACE / #ifdef CONFIG_SHADOW_CALL_STACK

레이블
처리 전현재 배치 위치
이 줄의 효과4
CPU 레지스터
처리 전그대로
이 줄의 효과레이블 자체가 값을 바꾸지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

#endif

빌드 조건으로 나누었던 구간을 닫습니다. 대상:

1028행 그림 · 전처리 단계

함수·코드 범위: __sdei_asm_handler

이 줄을 포함하는 조건: #ifdef CONFIG_ARM_SDE_INTERFACE / #ifdef CONFIG_SHADOW_CALL_STACK

소스
처리 전전처리 전
이 줄의 효과#endif
CPU 레지스터
처리 전해당 없음
이 줄의 효과실행되는 CPU 명령이 없습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	mrs	x28, sp_el0

중단 시 SP_EL0를 x28에 보존합니다. 이벤트가 사용자·게스트·전환 도중 어디서든 올 수 있어 이 값이 이미 커널 current라고 가정하지 않습니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.

중단 당시 SP_EL0를 x28에 보존하여 이벤트 처리 후 되돌릴 수 있게 합니다.

1034행 그림 · 시스템 레지스터 읽기

함수·코드 범위: __sdei_asm_handler

이 줄을 포함하는 조건: #ifdef CONFIG_ARM_SDE_INTERFACE

읽는 값SP_EL0
x28
이 명령을 실행하기 직전명령 직전 x28의 값
이 명령을 실행한 직후명령 직전 SP_EL0의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 일반 RAM의 메모리 접근과 구분합니다.
	ldr_this_cpu	dst=x0, sym=__entry_task, tmp=x1

CPU별 __entry_task에서 신뢰할 현재 task 주소를 얻습니다. SDEI C handler가 current를 사용할 수 있게 할 입력입니다. 심벌의 기준 주소에 현재 CPU의 per-CPU 오프셋을 더해 이 CPU의 저장 위치를 찾습니다.

1035행 그림 · 주소·값 구분

함수·코드 범위: __sdei_asm_handler

이 줄을 포함하는 조건: #ifdef CONFIG_ARM_SDE_INTERFACE

dst=x0
이 명령을 실행하기 직전명령 직전 dst의 값=x0
이 명령을 실행한 직후메모리[주소(sym=__entry_task) + 현재 CPU 오프셋]의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 이 매크로의 임시 레지스터는 내부에서 사용됩니다.
ldr_this_cpu 매크로 본문 · 250행

arch/arm64/include/asm/assembler.h · Linux v6.18.37

정의 선택 조건: #ifndef __ASM_ASSEMBLER_H

.macro ldr_this_cpu dst, sym, tmp adr_l \dst, \sym get_this_cpu_offset \tmp ldr \dst, [\dst, \tmp] .endm
	msr	sp_el0, x0

SP_EL0에 커널 current 포인터를 설치합니다. 원래 값은 x28에 보관했으므로 이벤트 처리가 끝난 뒤 정확히 되돌릴 수 있습니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.

1036행 그림 · 시스템 레지스터 쓰기

함수·코드 범위: __sdei_asm_handler

이 줄을 포함하는 조건: #ifdef CONFIG_ARM_SDE_INTERFACE

읽는 값x0
SP_EL0
이 명령을 실행하기 직전명령 직전 SP_EL0의 값
이 명령을 실행한 직후명령 직전 x0의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 접근 권한과 VHE 별칭 규칙에 따라 실제 대상이 정해집니다. 뒤의 ISB·DSB가 필요한지는 이 레지스터와 문맥에 따릅니다.
	and     x0, x3, #0xc

필요한 비트만 남깁니다. 다른 정보가 섞인 값에서 관심 있는 부분을 고르는 작업입니다. 여기서 결과를 받는 레지스터는 x0입니다.

1039행 그림 · 레지스터 상태

함수·코드 범위: __sdei_asm_handler

이 줄을 포함하는 조건: #ifdef CONFIG_ARM_SDE_INTERFACE

읽는 값x3
x0
이 명령을 실행하기 직전명령 직전 x0의 값
이 명령을 실행한 직후명령 직전 x3의 값 AND 0xc

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 결과는 목적 레지스터 폭에 맞습니다. 이 형식은 NZCV를 바꾸지 않습니다.
	mrs     x1, CurrentEL

현재 실행 EL과 펌웨어가 준 중단 PSTATE의 EL을 비교할 값을 읽습니다. 같은 커널 문맥이면 기존 FP·PC를 연결하고 사용자나 다른 EL 문맥이면 커널 unwind 연결을 끊습니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.

1040행 그림 · 시스템 레지스터 읽기

함수·코드 범위: __sdei_asm_handler

이 줄을 포함하는 조건: #ifdef CONFIG_ARM_SDE_INTERFACE

읽는 값CURRENTEL
x1
이 명령을 실행하기 직전명령 직전 x1의 값
이 명령을 실행한 직후명령 직전 CURRENTEL의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 일반 RAM의 메모리 접근과 구분합니다.
	cmp     x0, x1

x0와 x1를 비교하여 조건 플래그에 결과를 남깁니다. 비교한 두 값은 그대로이며, 뒤의 조건 분기가 이 결과를 읽습니다.

1041행 그림 · 조건 플래그 계산

함수·코드 범위: __sdei_asm_handler

이 줄을 포함하는 조건: #ifdef CONFIG_ARM_SDE_INTERFACE

읽는 값x0x1
NZCV
이 명령을 실행하기 직전명령 직전 NZCV의 값
이 명령을 실행한 직후명령 직전 x0의 값 − 명령 직전 x1의 값의 조건 결과

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 일반 레지스터에 연산 결과를 쓰지 않고 조건 플래그만 바꿉니다.
  • N은 결과의 부호 비트, Z는 결과가 0인지 나타냅니다. C는 빌림이 없으면 1, V는 부호 있는 뺄셈의 오버플로를 나타냅니다.
	csel	x29, x29, xzr, eq	// fp, or zero

분기하지 않고 두 값 중 하나를 고릅니다. Z=1이면 x29, 아니면 xzr를 x29에 넣습니다. 조건 플래그 자체는 유지합니다.

1042행 그림 · 조건에 따른 값 선택

함수·코드 범위: __sdei_asm_handler

이 줄을 포함하는 조건: #ifdef CONFIG_ARM_SDE_INTERFACE

읽는 값x29xzrNZCV
x29
이 명령을 실행하기 직전명령 직전 x29의 값
이 명령을 실행한 직후Z=1이면 명령 직전 x29의 값, 아니면 0

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 두 경우 중 하나만 선택합니다. NZCV와 실행 순서는 이 명령으로 바뀌지 않습니다.
	csel	x4, x2, xzr, eq		// elr, or zero

분기하지 않고 두 값 중 하나를 고릅니다. Z=1이면 x2, 아니면 xzr를 x4에 넣습니다. 조건 플래그 자체는 유지합니다.

1043행 그림 · 조건에 따른 값 선택

함수·코드 범위: __sdei_asm_handler

이 줄을 포함하는 조건: #ifdef CONFIG_ARM_SDE_INTERFACE

읽는 값x2xzrNZCV
x4
이 명령을 실행하기 직전명령 직전 x4의 값
이 명령을 실행한 직후Z=1이면 명령 직전 x2의 값, 아니면 0

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 두 경우 중 하나만 선택합니다. NZCV와 실행 순서는 이 명령으로 바뀌지 않습니다.
	stp	x29, x4, [sp, #-16]!

x29, x4의 값을 sp를 기준으로 -16만큼 계산한 메모리 위치에 기록합니다. 레지스터 값을 나중에 다시 쓰거나 다른 코드가 읽을 수 있도록 메모리에 남기는 줄입니다. 첫 위치에는 x29, 그보다 8바이트 높은 위치에는 x4의 값이 들어갑니다. 이 스택 위치는 현재 함수 또는 예외 처리의 보관 공간입니다. 쓰기 전에 기준 주소를 갱신합니다. SP의 음수 오프셋이면 낮은 주소 쪽에 저장 공간을 확보하는 뜻입니다.

1045행 그림 · 메모리 쓰기

함수·코드 범위: __sdei_asm_handler

이 줄을 포함하는 조건: #ifdef CONFIG_ARM_SDE_INTERFACE

읽는 값spx29x4
메모리[이전 sp + (-16)] · 8바이트
이 명령을 실행하기 직전기존 메모리 값
이 명령을 실행한 직후명령 직전 x29의 값
메모리[이전 sp + (-16) + 8] · 8바이트
이 명령을 실행하기 직전기존 메모리 값
이 명령을 실행한 직후명령 직전 x4의 값
sp
이 명령을 실행하기 직전명령 직전 sp의 값
이 명령을 실행한 직후명령 직전 sp의 값 + (-16)

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • !는 전위 갱신입니다. 오프셋을 더한 주소로 접근하고 그 주소를 기준 레지스터에도 남깁니다.
  • 메모리 칸은 낮은 주소부터 나열했습니다. SP를 줄이면 더 낮은 주소의 공간을 사용합니다. 화면의 아래쪽이 곧 낮은 주소라는 뜻은 아닙니다.
	mov	x29, sp

현재 SP의 숫자를 x29에 기억해 둡니다. 스택 자체는 이동하거나 복사되지 않습니다. 나중에 이 값으로 원래 위치를 찾을 수 있습니다.

1046행 그림 · 값 복사

함수·코드 범위: __sdei_asm_handler

이 줄을 포함하는 조건: #ifdef CONFIG_ARM_SDE_INTERFACE

읽는 값sp
x29
이 명령을 실행하기 직전명령 직전 x29의 값
이 명령을 실행한 직후명령 직전 sp의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 화살표는 값의 변화를 뜻합니다. 주소값을 옮겨도 그 주소의 메모리 내용은 복사하지 않습니다.
	add	x0, x19, #SDEI_EVENT_INTREGS

x19를 기준으로 SDEI_EVENT_INTREGS을 더해 x0에 결과를 둡니다. 주소 계산에 쓰이는 경우에는 구조체의 필드나 다음 저장 위치를 찾는 동작입니다. 이 형태는 조건 플래그를 바꾸지 않습니다.

1048행 그림 · 레지스터 상태

함수·코드 범위: __sdei_asm_handler

이 줄을 포함하는 조건: #ifdef CONFIG_ARM_SDE_INTERFACE

읽는 값x19
x0
이 명령을 실행하기 직전명령 직전 x0의 값
이 명령을 실행한 직후명령 직전 x19의 값 + SDEI_EVENT_INTREGS

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 결과는 목적 레지스터 폭에 맞습니다. 이 형식은 NZCV를 바꾸지 않습니다.
	mov	x1, x19

x1에 x19에 들어 있는 값을 넣습니다. 원래 x1의 내용은 덮어쓰며, 다른 레지스터나 그 주소의 메모리까지 복사하지는 않습니다.

1049행 그림 · 값 복사

함수·코드 범위: __sdei_asm_handler

이 줄을 포함하는 조건: #ifdef CONFIG_ARM_SDE_INTERFACE

읽는 값x19
x1
이 명령을 실행하기 직전명령 직전 x1의 값
이 명령을 실행한 직후명령 직전 x19의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 화살표는 값의 변화를 뜻합니다. 주소값을 옮겨도 그 주소의 메모리 내용은 복사하지 않습니다.
	bl	__sdei_handler

x0=저장 레지스터, x1=등록 이벤트 객체로 __sdei_handler를 호출합니다. 펌웨어가 보존한 값 수거와 실제 이벤트 callback 등을 처리하고, 반환값은 완료 상태 또는 재개할 주소 선택에 사용됩니다. BL은 다음 명령의 주소를 LR(x30)에 기록하고 목적지로 분기합니다.

1050행 그림 · 호출과 실행 위치

함수·코드 범위: __sdei_asm_handler

이 줄을 포함하는 조건: #ifdef CONFIG_ARM_SDE_INTERFACE

PC
이 명령을 실행하기 직전명령 직전 PC의 값
이 명령을 실행한 직후__sdei_handler
LR (x30)
이 명령을 실행하기 직전명령 직전 LR의 값 (x30)
이 명령을 실행한 직후이 BL/BLR 다음 명령 주소

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 그림은 분기 명령 자체의 효과입니다. 호출된 함수가 실행된 뒤의 레지스터 값은 그 함수의 반환·보존 규칙을 따라야 합니다.

SDEI 완료와 중단 처리

원본 1051–1100행

	msr	sp_el0, x28

이벤트 전 SP_EL0를 되돌립니다. 커널 handler를 위해 임시로 설치했던 current 값을 사용자·게스트의 원래 실행 상태에 남기지 않게 합니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.

1052행 그림 · 시스템 레지스터 쓰기

함수·코드 범위: __sdei_asm_handler

이 줄을 포함하는 조건: #ifdef CONFIG_ARM_SDE_INTERFACE

읽는 값x28
SP_EL0
이 명령을 실행하기 직전명령 직전 SP_EL0의 값
이 명령을 실행한 직후명령 직전 x28의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 접근 권한과 VHE 별칭 규칙에 따라 실제 대상이 정해집니다. 뒤의 ISB·DSB가 필요한지는 이 레지스터와 문맥에 따릅니다.
	mov	x4, x19         // keep x4 for __sdei_asm_exit_trampoline

x4에 x19에 들어 있는 값을 넣습니다. 원래 x4의 내용은 덮어쓰며, 다른 레지스터나 그 주소의 메모리까지 복사하지는 않습니다.

1054행 그림 · 값 복사

함수·코드 범위: __sdei_asm_handler

이 줄을 포함하는 조건: #ifdef CONFIG_ARM_SDE_INTERFACE

읽는 값x19
x4
이 명령을 실행하기 직전명령 직전 x4의 값
이 명령을 실행한 직후명령 직전 x19의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 화살표는 값의 변화를 뜻합니다. 주소값을 옮겨도 그 주소의 메모리 내용은 복사하지 않습니다.
	ldp	x28, x29, [x4, #SDEI_EVENT_INTREGS + 16 * 14]

x4를 기준으로 SDEI_EVENT_INTREGS + 16 * 14만큼 계산한 메모리 위치에 보관된 값을 x28, x29로 가져옵니다. 주소 숫자를 옮기는 MOV와 달리 실제 메모리를 읽습니다. 첫 위치에서 x28, 그보다 8바이트 높은 위치에서 x29의 값을 읽습니다.

1055행 그림 · 메모리 읽기

함수·코드 범위: __sdei_asm_handler

이 줄을 포함하는 조건: #ifdef CONFIG_ARM_SDE_INTERFACE

읽는 값x4
메모리[이전 x4 + (SDEI_EVENT_INTREGS + 16 * 14)] · 8바이트
이 명령을 실행하기 직전M0 (이 주소에 저장된 값)
이 명령을 실행한 직후M0 (이 주소에 저장된 값)
x28
이 명령을 실행하기 직전명령 직전 x28의 값
이 명령을 실행한 직후M0
메모리[이전 x4 + (SDEI_EVENT_INTREGS + 16 * 14) + 8] · 8바이트
이 명령을 실행하기 직전M1 (이 주소에 저장된 값)
이 명령을 실행한 직후M1 (이 주소에 저장된 값)
x29
이 명령을 실행하기 직전명령 직전 x29의 값
이 명령을 실행한 직후M1

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 주소 계산에 의한 기준 레지스터 갱신은 없습니다. 다만 LDR/LDP의 목적 레지스터가 기준 레지스터와 같으면 읽은 값이 그 레지스터를 덮어씁니다.
	ldp	x18, x19, [x4, #SDEI_EVENT_INTREGS + 16 * 9]

x4를 기준으로 SDEI_EVENT_INTREGS + 16 * 9만큼 계산한 메모리 위치에 보관된 값을 x18, x19로 가져옵니다. 주소 숫자를 옮기는 MOV와 달리 실제 메모리를 읽습니다. 첫 위치에서 x18, 그보다 8바이트 높은 위치에서 x19의 값을 읽습니다.

1056행 그림 · 메모리 읽기

함수·코드 범위: __sdei_asm_handler

이 줄을 포함하는 조건: #ifdef CONFIG_ARM_SDE_INTERFACE

읽는 값x4
메모리[이전 x4 + (SDEI_EVENT_INTREGS + 16 * 9)] · 8바이트
이 명령을 실행하기 직전M0 (이 주소에 저장된 값)
이 명령을 실행한 직후M0 (이 주소에 저장된 값)
x18
이 명령을 실행하기 직전명령 직전 x18의 값
이 명령을 실행한 직후M0
메모리[이전 x4 + (SDEI_EVENT_INTREGS + 16 * 9) + 8] · 8바이트
이 명령을 실행하기 직전M1 (이 주소에 저장된 값)
이 명령을 실행한 직후M1 (이 주소에 저장된 값)
x19
이 명령을 실행하기 직전명령 직전 x19의 값
이 명령을 실행한 직후M1

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 주소 계산에 의한 기준 레지스터 갱신은 없습니다. 다만 LDR/LDP의 목적 레지스터가 기준 레지스터와 같으면 읽은 값이 그 레지스터를 덮어씁니다.
	ldp	lr, x1, [x4, #SDEI_EVENT_INTREGS + S_LR]

x4를 기준으로 SDEI_EVENT_INTREGS + S_LR만큼 계산한 메모리 위치에 보관된 값을 lr, x1로 가져옵니다. 주소 숫자를 옮기는 MOV와 달리 실제 메모리를 읽습니다.

1057행 그림 · 메모리 읽기

함수·코드 범위: __sdei_asm_handler

이 줄을 포함하는 조건: #ifdef CONFIG_ARM_SDE_INTERFACE

읽는 값x4
메모리[이전 x4 + (SDEI_EVENT_INTREGS + S_LR)] · 8바이트
이 명령을 실행하기 직전M0 (이 주소에 저장된 값)
이 명령을 실행한 직후M0 (이 주소에 저장된 값)
lr
이 명령을 실행하기 직전명령 직전 lr의 값
이 명령을 실행한 직후M0
메모리[이전 x4 + (SDEI_EVENT_INTREGS + S_LR) + 8] · 8바이트
이 명령을 실행하기 직전M1 (이 주소에 저장된 값)
이 명령을 실행한 직후M1 (이 주소에 저장된 값)
x1
이 명령을 실행하기 직전명령 직전 x1의 값
이 명령을 실행한 직후M1

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 주소 계산에 의한 기준 레지스터 갱신은 없습니다. 다만 LDR/LDP의 목적 레지스터가 기준 레지스터와 같으면 읽은 값이 그 레지스터를 덮어씁니다.
	mov	sp, x1

이제 스택으로 사용할 위치를 x1의 값으로 바꿉니다. 메모리 내용을 옮기는 작업은 아닙니다. 다음 저장·호출부터 새 스택 위치를 사용합니다.

1058행 그림 · 값 복사

함수·코드 범위: __sdei_asm_handler

이 줄을 포함하는 조건: #ifdef CONFIG_ARM_SDE_INTERFACE

읽는 값x1
sp
이 명령을 실행하기 직전명령 직전 sp의 값
이 명령을 실행한 직후명령 직전 x1의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 화살표는 값의 변화를 뜻합니다. 주소값을 옮겨도 그 주소의 메모리 내용은 복사하지 않습니다.
	mov	x1, x0			// address to complete_and_resume

x1에 x0에 들어 있는 값을 넣습니다. 원래 x1의 내용은 덮어쓰며, 다른 레지스터나 그 주소의 메모리까지 복사하지는 않습니다.

1060행 그림 · 값 복사

함수·코드 범위: __sdei_asm_handler

이 줄을 포함하는 조건: #ifdef CONFIG_ARM_SDE_INTERFACE

읽는 값x0
x1
이 명령을 실행하기 직전명령 직전 x1의 값
이 명령을 실행한 직후명령 직전 x0의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 화살표는 값의 변화를 뜻합니다. 주소값을 옮겨도 그 주소의 메모리 내용은 복사하지 않습니다.
	cmp	x0, #SDEI_EV_FAILED

C handler 반환값이 완료 상태 코드 범위인지 아니면 별도 재개 주소인지 구분합니다. 아래 CSEL은 펌웨어의 EVENT_COMPLETE와 COMPLETE_AND_RESUME 중 어떤 API를 보낼지 고릅니다. CMP는 두 피연산자의 뺄셈 결과로 NZCV 조건 플래그만 갱신하고 원래 레지스터 값은 유지합니다.

1064행 그림 · 조건 플래그 계산

함수·코드 범위: __sdei_asm_handler

이 줄을 포함하는 조건: #ifdef CONFIG_ARM_SDE_INTERFACE

읽는 값x0
NZCV
이 명령을 실행하기 직전명령 직전 NZCV의 값
이 명령을 실행한 직후명령 직전 x0의 값 − SDEI_EV_FAILED의 조건 결과

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 일반 레지스터에 연산 결과를 쓰지 않고 조건 플래그만 바꿉니다.
  • N은 결과의 부호 비트, Z는 결과가 0인지 나타냅니다. C는 빌림이 없으면 1, V는 부호 있는 뺄셈의 오버플로를 나타냅니다.
	mov_q	x2, SDEI_1_0_FN_SDEI_EVENT_COMPLETE

원래 중단 문맥으로 돌아가는 SDEI EVENT_COMPLETE 함수 ID를 준비합니다. 정상 반환 상태를 펌웨어에 보고하는 서비스 번호입니다. mov_q는 64비트 상수를 필요한 MOVZ/MOVN·MOVK 조합으로 구성하는 매크로입니다. 해당 주소의 메모리에서 데이터를 가져오는 명령은 아닙니다.

1065행 그림 · 값 복사

함수·코드 범위: __sdei_asm_handler

이 줄을 포함하는 조건: #ifdef CONFIG_ARM_SDE_INTERFACE

x2
이 명령을 실행하기 직전명령 직전 x2의 값
이 명령을 실행한 직후SDEI_1_0_FN_SDEI_EVENT_COMPLETE

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 화살표는 값의 변화를 뜻합니다. 주소값을 옮겨도 그 주소의 메모리 내용은 복사하지 않습니다.
  • 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
	mov_q	x3, SDEI_1_0_FN_SDEI_EVENT_COMPLETE_AND_RESUME

명시한 주소에서 실행을 재개하는 EVENT_COMPLETE_AND_RESUME 함수 ID를 준비합니다. x1에 앞에서 복사한 재개 주소와 짝을 이루는 서비스입니다. mov_q는 64비트 상수를 필요한 MOVZ/MOVN·MOVK 조합으로 구성하는 매크로입니다. 해당 주소의 메모리에서 데이터를 가져오는 명령은 아닙니다.

1066행 그림 · 값 복사

함수·코드 범위: __sdei_asm_handler

이 줄을 포함하는 조건: #ifdef CONFIG_ARM_SDE_INTERFACE

x3
이 명령을 실행하기 직전명령 직전 x3의 값
이 명령을 실행한 직후SDEI_1_0_FN_SDEI_EVENT_COMPLETE_AND_RESUME

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 화살표는 값의 변화를 뜻합니다. 주소값을 옮겨도 그 주소의 메모리 내용은 복사하지 않습니다.
  • 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
	csel	x0, x2, x3, ls

분기하지 않고 두 값 중 하나를 고릅니다. C=0 또는 Z=1이면 x2, 아니면 x3를 x0에 넣습니다. 조건 플래그 자체는 유지합니다.

LS는 부호 없는 이하(C=0 또는 Z=1)입니다. 실패 이하이면 COMPLETE, 그 외에는 COMPLETE_AND_RESUME를 선택합니다.

1067행 그림 · 조건에 따른 값 선택

함수·코드 범위: __sdei_asm_handler

이 줄을 포함하는 조건: #ifdef CONFIG_ARM_SDE_INTERFACE

읽는 값x2x3NZCV
x0
이 명령을 실행하기 직전명령 직전 x0의 값
이 명령을 실행한 직후C=0 또는 Z=1이면 명령 직전 x2의 값, 아니면 명령 직전 x3의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 두 경우 중 하나만 선택합니다. NZCV와 실행 순서는 이 명령으로 바뀌지 않습니다.
	ldr_l	x2, sdei_exit_mode

SDEI 등록 시 정해진 완료 통로 SMC/HVC 종류를 읽습니다. 뒤의 sdei_handler_exit가 이 값으로 실제 명령을 선택합니다. ldr_l은 ADRP로 심벌이 속한 페이지를 구한 뒤 LDR로 그 위치의 값을 읽는 매크로입니다. 주소를 구하는 adr_l과 달리 실제 메모리 접근이 있습니다.

1069행 그림 · 주소·값 구분

함수·코드 범위: __sdei_asm_handler

이 줄을 포함하는 조건: #ifdef CONFIG_ARM_SDE_INTERFACE

x2
이 명령을 실행하기 직전명령 직전 x2의 값
이 명령을 실행한 직후메모리[주소(sdei_exit_mode)]의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 이 매크로의 임시 레지스터는 내부에서 사용됩니다.
ldr_l 매크로 본문 · 187행

arch/arm64/include/asm/assembler.h · Linux v6.18.37

정의 선택 조건: #ifndef __ASM_ASSEMBLER_H

.macro ldr_l, dst, sym, tmp= .ifb \tmp adrp \dst, \sym ldr \dst, [\dst, :lo12:\sym] .else adrp \tmp, \sym ldr \dst, [\tmp, :lo12:\sym] .endif .endm
	ldrb	w3, [x4, #SDEI_EVENT_PRIORITY]

x4를 기준으로 SDEI_EVENT_PRIORITY만큼 계산한 메모리 위치에 보관된 값을 w3로 가져옵니다. 주소 숫자를 옮기는 MOV와 달리 실제 메모리를 읽습니다.

1072행 그림 · 메모리 읽기

함수·코드 범위: __sdei_asm_handler

이 줄을 포함하는 조건: #ifdef CONFIG_ARM_SDE_INTERFACE

읽는 값x4
메모리[이전 x4 + (SDEI_EVENT_PRIORITY)] · 1바이트
이 명령을 실행하기 직전M0 (이 주소에 저장된 값)
이 명령을 실행한 직후M0 (이 주소에 저장된 값)
w3
이 명령을 실행하기 직전명령 직전 w3의 값
이 명령을 실행한 직후M0를 0으로 확장
x3[63:32]
이 명령을 실행하기 직전명령 직전 x3[63:32]의 값
이 명령을 실행한 직후0

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 주소 계산에 의한 기준 레지스터 갱신은 없습니다. 다만 LDR/LDP의 목적 레지스터가 기준 레지스터와 같으면 읽은 값이 그 레지스터를 덮어씁니다.
	cbnz	w3, 1f

w3의 값가 0이 아니면 1f (L1076)로 갑니다. 조건을 만족하지 않으면 바로 다음 명령을 읽습니다. 이 분기는 CMP의 결과가 아니라 지정 레지스터를 직접 검사합니다.

1073행 그림 · 조건별 실행 위치

함수·코드 범위: __sdei_asm_handler

이 줄을 포함하는 조건: #ifdef CONFIG_ARM_SDE_INTERFACE

읽는 값w3
PC
이 명령을 실행하기 직전명령 직전 PC의 값
이 명령을 실행한 직후명령 직전 w3의 값 ≠ 0이면 1f (L1076), 아니면 다음 명령

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 조건이 맞는 한 경로만 실행합니다. 이 분기 명령은 NZCV를 새로 계산하지 않습니다.
	adr_this_cpu dst=x5, sym=sdei_active_normal_event, tmp=x6

normal 이벤트 처리를 마친 뒤 비울 CPU별 활성 슬롯의 주소를 x5에 구합니다. 뒤의 store 0은 이벤트 객체 해제가 아니라 활성 표시 제거입니다. 심벌의 기준 주소에 현재 CPU의 per-CPU 오프셋을 더해 이 CPU의 저장 위치를 찾습니다.

1074행 그림 · 주소·값 구분

함수·코드 범위: __sdei_asm_handler

이 줄을 포함하는 조건: #ifdef CONFIG_ARM_SDE_INTERFACE

dst=x5
이 명령을 실행하기 직전명령 직전 dst의 값=x5
이 명령을 실행한 직후주소(sym=sdei_active_normal_event) + 현재 CPU 오프셋

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 이 매크로의 임시 레지스터는 내부에서 사용됩니다.
adr_this_cpu 매크로 본문 · 238행

arch/arm64/include/asm/assembler.h · Linux v6.18.37

정의 선택 조건: #ifndef __ASM_ASSEMBLER_H

.macro adr_this_cpu, dst, sym, tmp adrp \tmp, \sym add \dst, \tmp, #:lo12:\sym get_this_cpu_offset \tmp add \dst, \dst, \tmp .endm
	b	2f

2f (L1077)부터 실행을 계속합니다. 돌아올 주소를 새로 적지 않으므로 일반 함수 호출과는 다른 제어 이동입니다.

1075행 그림 · 호출과 실행 위치

함수·코드 범위: __sdei_asm_handler

이 줄을 포함하는 조건: #ifdef CONFIG_ARM_SDE_INTERFACE

PC
이 명령을 실행하기 직전명령 직전 PC의 값
이 명령을 실행한 직후2f (L1077)

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 새 복귀 주소를 LR에 기록하지 않습니다. RET 역시 메모리의 스택 값을 자동으로 꺼내는 명령은 아닙니다.
1:	adr_this_cpu dst=x5, sym=sdei_active_critical_event, tmp=x6

critical 이벤트였다면 normal 슬롯 대신 critical 활성 슬롯 주소를 선택합니다. 방금 종료할 이벤트의 표시만 지우기 위한 선택입니다. 심벌의 기준 주소에 현재 CPU의 per-CPU 오프셋을 더해 이 CPU의 저장 위치를 찾습니다.

1076행 그림 · 주소·값 구분

함수·코드 범위: __sdei_asm_handler

이 줄을 포함하는 조건: #ifdef CONFIG_ARM_SDE_INTERFACE

dst=x5
이 명령을 실행하기 직전명령 직전 dst의 값=x5
이 명령을 실행한 직후주소(sym=sdei_active_critical_event) + 현재 CPU 오프셋

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 이 매크로의 임시 레지스터는 내부에서 사용됩니다.
adr_this_cpu 매크로 본문 · 238행

arch/arm64/include/asm/assembler.h · Linux v6.18.37

정의 선택 조건: #ifndef __ASM_ASSEMBLER_H

.macro adr_this_cpu, dst, sym, tmp adrp \tmp, \sym add \dst, \tmp, #:lo12:\sym get_this_cpu_offset \tmp add \dst, \dst, \tmp .endm
2:	str	xzr, [x5]

CPU별 활성 SDEI 이벤트 포인터를 0으로 비워 처리가 끝났음을 표시합니다. 이벤트 객체 자체를 해제하는 store가 아니라 crash 경로에서 볼 활성 표식 정리입니다. 실제로 x5 주소의 메모리 8바이트를 xzr의 값으로 씁니다. 이 주소 형식은 기준 레지스터 x5 자체를 갱신하지 않습니다.

1077행 그림 · 메모리 쓰기

함수·코드 범위: __sdei_asm_handler

이 줄을 포함하는 조건: #ifdef CONFIG_ARM_SDE_INTERFACE

읽는 값x5xzr
메모리[이전 x5] · 8바이트
이 명령을 실행하기 직전기존 메모리 값
이 명령을 실행한 직후0

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 주소 계산에 의한 기준 레지스터 갱신은 없습니다. 다만 LDR/LDP의 목적 레지스터가 기준 레지스터와 같으면 읽은 값이 그 레지스터를 덮어씁니다.
alternative_if_not ARM64_UNMAP_KERNEL_AT_EL0

실제 KPTI가 비활성이면 정상 커널 매핑에서 곧바로 펌웨어 완료 호출을 해도 되므로 이 경로를 유지합니다. 활성인 경우는 최소 exit trampoline에서 매핑을 되돌린 뒤 호출해야 합니다. 이 매크로는 기본·교체 명령의 패치 정보를 만들며, CPU가 이 줄을 매번 if 조건처럼 실행하지 않습니다.

1079행 그림 · CPU 기능에 따른 명령 패치

함수·코드 범위: __sdei_asm_handler

이 줄을 포함하는 조건: #ifdef CONFIG_ARM_SDE_INTERFACE

패치 전 코드
처리 전기본 명령열
이 줄의 효과기능·정책에 맞는 기본 코드 또는 교체 코드

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 빌드할 때 패치 정보를 만들고 커널의 대체 코드 처리에서 명령을 선택합니다. 이 소스 줄을 CPU가 매번 if문으로 실행하는 것은 아닙니다.
	sdei_handler_exit exit_mode=x2

펌웨어에게 이벤트 처리가 끝났다고 알리고 실행을 넘깁니다. 성공하면 보통 함수처럼 다음 줄로 돌아오지 않는 경로입니다. 여기의 인자는 exit_mode=x2입니다.

1080행 그림 · 매크로 전체의 주요 상태

함수·코드 범위: __sdei_asm_handler

이 줄을 포함하는 조건: #ifdef CONFIG_ARM_SDE_INTERFACE

PC
이 명령을 실행하기 직전명령 직전 PC의 값
이 명령을 실행한 직후exit_mode=x2=SDEI_EXIT_SMC이면 SMC, 그 외에는 HVC의 예외 처리기
NZCV
이 명령을 실행하기 직전명령 직전 NZCV의 값
이 명령을 실행한 직후exit_mode=x2 − SDEI_EXIT_SMC의 비교 결과

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 성공한 이벤트 완료 요청은 돌아오지 않습니다. 호출이 반환하면 바로 뒤의 B .에서 반복합니다. 요청 인자 레지스터는 앞선 줄에서 준비합니다.
  • 여러 명령과 조건부 경로를 묶은 그림입니다. 이 목록이 모든 임시 레지스터의 변경을 열거하는 것은 아닙니다. 아래 매크로 본문에서 각 단계를 확인하실 수 있습니다.
sdei_handler_exit 매크로 본문 · 911행

arch/arm64/kernel/entry.S · Linux v6.18.37

정의 선택 조건: #ifdef CONFIG_ARM_SDE_INTERFACE

.macro sdei_handler_exit exit_mode /* On success, this call never returns... */ cmp \exit_mode, #SDEI_EXIT_SMC b.ne 99f smc #0 b . 99: hvc #0 b . .endm
alternative_else_nop_endif

1079행에서 시작한 ARM64_UNMAP_KERNEL_AT_EL0 패치 묶음을 닫습니다. 조건에 맞는 본문을 사용하지 않는 쪽은 같은 길이의 NOP로 채워 뒤 명령의 배치를 유지합니다. 이 줄 자체가 CPU에서 조건을 검사하는 명령은 아닙니다.

1081행 그림 · CPU 기능에 따른 명령 패치

함수·코드 범위: __sdei_asm_handler

이 줄을 포함하는 조건: #ifdef CONFIG_ARM_SDE_INTERFACE

패치 전 코드
처리 전기본 명령열
이 줄의 효과기능·정책에 맞는 기본 코드 또는 교체 코드

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 빌드할 때 패치 정보를 만들고 커널의 대체 코드 처리에서 명령을 선택합니다. 이 소스 줄을 CPU가 매번 if문으로 실행하는 것은 아닙니다.
#ifdef CONFIG_UNMAP_KERNEL_AT_EL0

KPTI 지원 빌드에는 이벤트 전 매핑 상태로 되돌릴 exit trampoline 분기를 포함합니다. 실제 활성 시스템에서 위의 직접 완료 경로가 빠졌을 때 이 경로로 이어집니다. 이 조건은 빌드 전처리 단계에서 포함할 코드를 고릅니다. CPU가 실행 중 전처리 조건을 검사하지는 않습니다.

1083행 그림 · 전처리 단계

함수·코드 범위: __sdei_asm_handler

이 줄을 포함하는 조건: #ifdef CONFIG_ARM_SDE_INTERFACE

소스
처리 전전처리 전
이 줄의 효과#ifdef CONFIG_UNMAP_KERNEL_AT_EL0
CPU 레지스터
처리 전해당 없음
이 줄의 효과실행되는 CPU 명령이 없습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	tramp_alias	dst=x5, sym=__sdei_asm_exit_trampoline

최소 trampoline 매핑에서도 접근할 수 있는 코드 주소를 계산합니다. 같은 코드라도 어느 매핑에서 부르는지에 따라 사용할 주소가 달라집니다. 여기의 인자는 dst=x5, sym=__sdei_asm_exit_trampoline입니다.

1084행 그림 · 주소·값 구분

함수·코드 범위: __sdei_asm_handler

이 줄을 포함하는 조건: #ifdef CONFIG_ARM_SDE_INTERFACE / #ifdef CONFIG_UNMAP_KERNEL_AT_EL0

dst=x5
이 명령을 실행하기 직전명령 직전 dst의 값=x5
이 명령을 실행한 직후TRAMP_VALIAS + (주소(sym=__sdei_asm_exit_trampoline) − .entry.tramp.text 섹션 시작)

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 이 매크로의 임시 레지스터는 내부에서 사용됩니다.
tramp_alias 매크로 본문 · 103행

arch/arm64/kernel/entry.S · Linux v6.18.37

.macro tramp_alias, dst, sym .set .Lalias\@, TRAMP_VALIAS + \sym - .entry.tramp.text movz \dst, :abs_g2_s:.Lalias\@ movk \dst, :abs_g1_nc:.Lalias\@ movk \dst, :abs_g0_nc:.Lalias\@ .endm
	br	x5

x5에 준비한 주소부터 실행합니다. 주소가 가리키는 값을 메모리에서 읽는 것이 아니라 CPU의 다음 실행 위치를 바꿉니다. 새 복귀 주소는 만들지 않습니다.

1085행 그림 · 호출과 실행 위치

함수·코드 범위: __sdei_asm_handler

이 줄을 포함하는 조건: #ifdef CONFIG_ARM_SDE_INTERFACE / #ifdef CONFIG_UNMAP_KERNEL_AT_EL0

읽는 값x5
PC
이 명령을 실행하기 직전명령 직전 PC의 값
이 명령을 실행한 직후명령 직전 x5의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 새 복귀 주소를 LR에 기록하지 않습니다. RET 역시 메모리의 스택 값을 자동으로 꺼내는 명령은 아닙니다.
#endif

빌드 조건으로 나누었던 구간을 닫습니다. 대상:

1086행 그림 · 전처리 단계

함수·코드 범위: __sdei_asm_handler

이 줄을 포함하는 조건: #ifdef CONFIG_ARM_SDE_INTERFACE / #ifdef CONFIG_UNMAP_KERNEL_AT_EL0

소스
처리 전전처리 전
이 줄의 효과#endif
CPU 레지스터
처리 전해당 없음
이 줄의 효과실행되는 CPU 명령이 없습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

SYM_CODE_END(__sdei_asm_handler)

도구에 이 코드 범위의 끝을 알려 크기와 심볼 정보를 기록합니다. CPU가 이 줄 때문에 자동으로 반환하지는 않습니다.

1087행 그림 · 어셈블·링크 단계

함수·코드 범위: __sdei_asm_handler

이 줄을 포함하는 조건: #ifdef CONFIG_ARM_SDE_INTERFACE

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과SYM_CODE_END(__sdei_asm_handler)
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 끝 표기 자체가 RET나 분기를 실행하지는 않습니다.
NOKPROBE(__sdei_asm_handler)

__sdei_asm_handler을 kprobe 설치 금지 목록에 넣습니다. 스택·레지스터·주소 공간을 바꾸는 예외/전환 경로 중간에 probe 예외를 추가하지 못하게 보호합니다. CONFIG_KPROBES가 꺼지면 이 목록 항목도 필요 없습니다. 빌드할 때 심벌을 가리키는 메타데이터를 만드는 표기이며, 이 위치에서 함수를 호출하는 명령은 아닙니다.

1088행 그림 · 어셈블·링크 단계

이 줄을 포함하는 조건: #ifdef CONFIG_ARM_SDE_INTERFACE

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과NOKPROBE(__sdei_asm_handler)
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

SYM_CODE_START(__sdei_handler_abort)

이 위치부터 (__sdei_handler_abort)라는 코드가 시작한다고 도구에 알려 줍니다. 정렬과 외부 공개 여부도 정의에 포함됩니다. 실제 작업은 아래의 명령들이 수행합니다.

1090행 그림 · 어셈블·링크 단계

함수·코드 범위: __sdei_handler_abort

이 줄을 포함하는 조건: #ifdef CONFIG_ARM_SDE_INTERFACE

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과SYM_CODE_START(__sdei_handler_abort)
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	mov_q	x0, SDEI_1_0_FN_SDEI_EVENT_COMPLETE_AND_RESUME

진행 중인 SDEI handler를 중단하면서 바로 아래 지역 라벨에서 다시 실행하도록 COMPLETE_AND_RESUME 서비스 번호를 준비합니다. task 시그널 반환 번호와는 다른 펌웨어 API입니다. mov_q는 64비트 상수를 필요한 MOVZ/MOVN·MOVK 조합으로 구성하는 매크로입니다. 해당 주소의 메모리에서 데이터를 가져오는 명령은 아닙니다.

1091행 그림 · 값 복사

함수·코드 범위: __sdei_handler_abort

이 줄을 포함하는 조건: #ifdef CONFIG_ARM_SDE_INTERFACE

x0
이 명령을 실행하기 직전명령 직전 x0의 값
이 명령을 실행한 직후SDEI_1_0_FN_SDEI_EVENT_COMPLETE_AND_RESUME

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 화살표는 값의 변화를 뜻합니다. 주소값을 옮겨도 그 주소의 메모리 내용은 복사하지 않습니다.
  • 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
	adr	x1, 1f

펌웨어가 이벤트 종료 뒤 재개할 주소로 1f를 전달합니다. 원래 LR로 즉시 RET하는 대신 펌웨어의 이벤트 상태도 정상적으로 정리해야 하기 때문입니다. 여기서 얻는 것은 레이블의 주소이며, 그 위치의 메모리 내용은 아직 읽지 않습니다.

1092행 그림 · 주소·값 구분

함수·코드 범위: __sdei_handler_abort

이 줄을 포함하는 조건: #ifdef CONFIG_ARM_SDE_INTERFACE

x1
이 명령을 실행하기 직전명령 직전 x1의 값
이 명령을 실행한 직후주소(1f)

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 주소 계산이며 그 주소의 메모리는 읽지 않습니다.
	ldr_l	x2, sdei_exit_mode

SDEI가 사용하는 펌웨어 완료 통로의 종류를 x2에 읽습니다. 뒤의 exit 매크로가 SMC인지 HVC인지 골라 중단한 이벤트의 상태를 정리합니다. ldr_l은 ADRP로 심벌이 속한 페이지를 구한 뒤 LDR로 그 위치의 값을 읽는 매크로입니다. 주소를 구하는 adr_l과 달리 실제 메모리 접근이 있습니다.

1093행 그림 · 주소·값 구분

함수·코드 범위: __sdei_handler_abort

이 줄을 포함하는 조건: #ifdef CONFIG_ARM_SDE_INTERFACE

x2
이 명령을 실행하기 직전명령 직전 x2의 값
이 명령을 실행한 직후메모리[주소(sdei_exit_mode)]의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 이 매크로의 임시 레지스터는 내부에서 사용됩니다.
ldr_l 매크로 본문 · 187행

arch/arm64/include/asm/assembler.h · Linux v6.18.37

정의 선택 조건: #ifndef __ASM_ASSEMBLER_H

.macro ldr_l, dst, sym, tmp= .ifb \tmp adrp \dst, \sym ldr \dst, [\dst, :lo12:\sym] .else adrp \tmp, \sym ldr \dst, [\tmp, :lo12:\sym] .endif .endm
	sdei_handler_exit exit_mode=x2

펌웨어에게 이벤트 처리가 끝났다고 알리고 실행을 넘깁니다. 성공하면 보통 함수처럼 다음 줄로 돌아오지 않는 경로입니다. 여기의 인자는 exit_mode=x2입니다.

1094행 그림 · 매크로 전체의 주요 상태

함수·코드 범위: __sdei_handler_abort

이 줄을 포함하는 조건: #ifdef CONFIG_ARM_SDE_INTERFACE

PC
이 명령을 실행하기 직전명령 직전 PC의 값
이 명령을 실행한 직후exit_mode=x2=SDEI_EXIT_SMC이면 SMC, 그 외에는 HVC의 예외 처리기
NZCV
이 명령을 실행하기 직전명령 직전 NZCV의 값
이 명령을 실행한 직후exit_mode=x2 − SDEI_EXIT_SMC의 비교 결과

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 성공한 이벤트 완료 요청은 돌아오지 않습니다. 호출이 반환하면 바로 뒤의 B .에서 반복합니다. 요청 인자 레지스터는 앞선 줄에서 준비합니다.
  • 여러 명령과 조건부 경로를 묶은 그림입니다. 이 목록이 모든 임시 레지스터의 변경을 열거하는 것은 아닙니다. 아래 매크로 본문에서 각 단계를 확인하실 수 있습니다.
sdei_handler_exit 매크로 본문 · 911행

arch/arm64/kernel/entry.S · Linux v6.18.37

정의 선택 조건: #ifdef CONFIG_ARM_SDE_INTERFACE

.macro sdei_handler_exit exit_mode /* On success, this call never returns... */ cmp \exit_mode, #SDEI_EXIT_SMC b.ne 99f smc #0 b . 99: hvc #0 b . .endm
1:	ret

LR(x30)에 적힌 주소로 실행을 돌립니다. 그 주소가 어떻게 준비됐는지가 중요합니다. 정상 함수의 호출자일 수도 있고, 절전 복귀나 trampoline에서 별도로 기록한 재개 지점일 수도 있습니다.

1097행 그림 · 호출과 실행 위치

함수·코드 범위: __sdei_handler_abort

이 줄을 포함하는 조건: #ifdef CONFIG_ARM_SDE_INTERFACE

읽는 값lr
PC
이 명령을 실행하기 직전명령 직전 PC의 값
이 명령을 실행한 직후명령 직전 lr의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 새 복귀 주소를 LR에 기록하지 않습니다. RET 역시 메모리의 스택 값을 자동으로 꺼내는 명령은 아닙니다.
SYM_CODE_END(__sdei_handler_abort)

도구에 이 코드 범위의 끝을 알려 크기와 심볼 정보를 기록합니다. CPU가 이 줄 때문에 자동으로 반환하지는 않습니다.

1098행 그림 · 어셈블·링크 단계

함수·코드 범위: __sdei_handler_abort

이 줄을 포함하는 조건: #ifdef CONFIG_ARM_SDE_INTERFACE

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과SYM_CODE_END(__sdei_handler_abort)
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 끝 표기 자체가 RET나 분기를 실행하지는 않습니다.
NOKPROBE(__sdei_handler_abort)

__sdei_handler_abort을 kprobe 설치 금지 목록에 넣습니다. 스택·레지스터·주소 공간을 바꾸는 예외/전환 경로 중간에 probe 예외를 추가하지 못하게 보호합니다. CONFIG_KPROBES가 꺼지면 이 목록 항목도 필요 없습니다. 빌드할 때 심벌을 가리키는 메타데이터를 만드는 표기이며, 이 위치에서 함수를 호출하는 명령은 아닙니다.

1099행 그림 · 어셈블·링크 단계

이 줄을 포함하는 조건: #ifdef CONFIG_ARM_SDE_INTERFACE

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과NOKPROBE(__sdei_handler_abort)
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

#endif /* CONFIG_ARM_SDE_INTERFACE */

빌드 조건으로 나누었던 구간을 닫습니다. 대상:

1100행 그림 · 전처리 단계

이 줄을 포함하는 조건: #ifdef CONFIG_ARM_SDE_INTERFACE

소스
처리 전전처리 전
이 줄의 효과#endif
CPU 레지스터
처리 전해당 없음
이 줄의 효과실행되는 CPU 명령이 없습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

설명한 뒤 함께 생각해 볼 질문

CPU가 ELR과 SPSR을 저장했는데 STP가 왜 많이 필요한가요?

ELR/SPSR에는 중단된 PC와 실행 상태가 들어갑니다. 일반 레지스터 x0–x30은 별도 보존이 필요합니다.

cpu_switch_to의 RET는 누구에게 돌아가나요?

다음 태스크에서 읽은 LR로 이동합니다. 이전 태스크의 호출 흐름을 그대로 계속하는 RET가 아닙니다.

SP_EL0는 항상 사용자 스택 주소인가요?

EL0에서 예외가 들어올 당시에는 사용자 SP로 읽지만, 커널은 이후 current 태스크 포인터를 담는 용도로 사용합니다. 시점을 구분해야 합니다.

주석을 포함한 전체 원문

Linux stable v6.18.37 · arch/arm64/kernel/entry.S

1/* SPDX-License-Identifier: GPL-2.0-only */2/*3 * Low-level exception handling code4 *5 * Copyright (C) 2012 ARM Ltd.6 * Authors:	Catalin Marinas <catalin.marinas@arm.com>7 *		Will Deacon <will.deacon@arm.com>8 */910#include <linux/arm-smccc.h>11#include <linux/init.h>12#include <linux/linkage.h>1314#include <asm/alternative.h>15#include <asm/assembler.h>16#include <asm/asm-offsets.h>17#include <asm/asm_pointer_auth.h>18#include <asm/bug.h>19#include <asm/cpufeature.h>20#include <asm/errno.h>21#include <asm/esr.h>22#include <asm/irq.h>23#include <asm/memory.h>24#include <asm/mmu.h>25#include <asm/processor.h>26#include <asm/ptrace.h>27#include <asm/scs.h>28#include <asm/stacktrace/frame.h>29#include <asm/thread_info.h>30#include <asm/asm-uaccess.h>31#include <asm/unistd.h>3233	.macro	clear_gp_regs34	.irp	n,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,2935	mov	x\n, xzr36	.endr37	.endm3839	.macro kernel_ventry, el:req, ht:req, regsize:req, label:req40	.align 741.Lventry_start\@:42	.if	\el == 043	/*44	 * This must be the first instruction of the EL0 vector entries. It is45	 * skipped by the trampoline vectors, to trigger the cleanup.46	 */47	b	.Lskip_tramp_vectors_cleanup\@48	.if	\regsize == 6449	mrs	x30, tpidrro_el050	msr	tpidrro_el0, xzr51	.else52	mov	x30, xzr53	.endif54.Lskip_tramp_vectors_cleanup\@:55	.endif5657	sub	sp, sp, #PT_REGS_SIZE58	/*59	 * Test whether the SP has overflowed, without corrupting a GPR.60	 * Task and IRQ stacks are aligned so that SP & (1 << THREAD_SHIFT)61	 * should always be zero.62	 */63	add	sp, sp, x0			// sp' = sp + x064	sub	x0, sp, x0			// x0' = sp' - x0 = (sp + x0) - x0 = sp65	tbnz	x0, #THREAD_SHIFT, 0f66	sub	x0, sp, x0			// x0'' = sp' - x0' = (sp + x0) - sp = x067	sub	sp, sp, x0			// sp'' = sp' - x0 = (sp + x0) - x0 = sp68	b	el\el\ht\()_\regsize\()_\label69700:71	/*72	 * Either we've just detected an overflow, or we've taken an exception73	 * while on the overflow stack. Either way, we won't return to74	 * userspace, and can clobber EL0 registers to free up GPRs.75	 */7677	/* Stash the original SP (minus PT_REGS_SIZE) in tpidr_el0. */78	msr	tpidr_el0, x07980	/* Recover the original x0 value and stash it in tpidrro_el0 */81	sub	x0, sp, x082	msr	tpidrro_el0, x08384	/* Switch to the overflow stack */85	adr_this_cpu sp, overflow_stack + OVERFLOW_STACK_SIZE, x08687	/*88	 * Check whether we were already on the overflow stack. This may happen89	 * after panic() re-enables interrupts.90	 */91	mrs	x0, tpidr_el0			// sp of interrupted context92	sub	x0, sp, x0			// delta with top of overflow stack93	tst	x0, #~(OVERFLOW_STACK_SIZE - 1)	// within range?94	b.ne	__bad_stack			// no? -> bad stack pointer9596	/* We were already on the overflow stack. Restore sp/x0 and carry on. */97	sub	sp, sp, x098	mrs	x0, tpidrro_el099	b	el\el\ht\()_\regsize\()_\label100.org .Lventry_start\@ + 128	// Did we overflow the ventry slot?101	.endm102103	.macro	tramp_alias, dst, sym104	.set	.Lalias\@, TRAMP_VALIAS + \sym - .entry.tramp.text105	movz	\dst, :abs_g2_s:.Lalias\@106	movk	\dst, :abs_g1_nc:.Lalias\@107	movk	\dst, :abs_g0_nc:.Lalias\@108	.endm109110	/*111	 * This macro corrupts x0-x3. It is the caller's duty  to save/restore112	 * them if required.113	 */114	.macro	apply_ssbd, state, tmp1, tmp2115alternative_cb	ARM64_ALWAYS_SYSTEM, spectre_v4_patch_fw_mitigation_enable116	b	.L__asm_ssbd_skip\@		// Patched to NOP117alternative_cb_end118	ldr_this_cpu	\tmp2, arm64_ssbd_callback_required, \tmp1119	cbz	\tmp2,	.L__asm_ssbd_skip\@120	ldr	\tmp2, [tsk, #TSK_TI_FLAGS]121	tbnz	\tmp2, #TIF_SSBD, .L__asm_ssbd_skip\@122	mov	w0, #ARM_SMCCC_ARCH_WORKAROUND_2123	mov	w1, #\state124alternative_cb	ARM64_ALWAYS_SYSTEM, smccc_patch_fw_mitigation_conduit125	nop					// Patched to SMC/HVC #0126alternative_cb_end127.L__asm_ssbd_skip\@:128	.endm129130	/* Check for MTE asynchronous tag check faults */131	.macro check_mte_async_tcf, tmp, ti_flags, thread_sctlr132#ifdef CONFIG_ARM64_MTE133	.arch_extension lse134alternative_if_not ARM64_MTE135	b	1f136alternative_else_nop_endif137	/*138	 * Asynchronous tag check faults are only possible in ASYNC (2) or139	 * ASYM (3) modes. In each of these modes bit 1 of SCTLR_EL1.TCF0 is140	 * set, so skip the check if it is unset.141	 */142	tbz	\thread_sctlr, #(SCTLR_EL1_TCF0_SHIFT + 1), 1f143	mrs_s	\tmp, SYS_TFSRE0_EL1144	tbz	\tmp, #SYS_TFSR_EL1_TF0_SHIFT, 1f145	/* Asynchronous TCF occurred for TTBR0 access, set the TI flag */146	mov	\tmp, #_TIF_MTE_ASYNC_FAULT147	add	\ti_flags, tsk, #TSK_TI_FLAGS148	stset	\tmp, [\ti_flags]1491:150#endif151	.endm152153	/* Clear the MTE asynchronous tag check faults */154	.macro clear_mte_async_tcf thread_sctlr155#ifdef CONFIG_ARM64_MTE156alternative_if ARM64_MTE157	/* See comment in check_mte_async_tcf above. */158	tbz	\thread_sctlr, #(SCTLR_EL1_TCF0_SHIFT + 1), 1f159	dsb	ish160	msr_s	SYS_TFSRE0_EL1, xzr1611:162alternative_else_nop_endif163#endif164	.endm165166	.macro mte_set_gcr, mte_ctrl, tmp167#ifdef CONFIG_ARM64_MTE168	ubfx	\tmp, \mte_ctrl, #MTE_CTRL_GCR_USER_EXCL_SHIFT, #16169	orr	\tmp, \tmp, #SYS_GCR_EL1_RRND170	msr_s	SYS_GCR_EL1, \tmp171#endif172	.endm173174	.macro mte_set_kernel_gcr, tmp, tmp2175#ifdef CONFIG_KASAN_HW_TAGS176alternative_cb	ARM64_ALWAYS_SYSTEM, kasan_hw_tags_enable177	b	1f178alternative_cb_end179	mov	\tmp, KERNEL_GCR_EL1180	msr_s	SYS_GCR_EL1, \tmp1811:182#endif183	.endm184185	.macro mte_set_user_gcr, tsk, tmp, tmp2186#ifdef CONFIG_KASAN_HW_TAGS187alternative_cb	ARM64_ALWAYS_SYSTEM, kasan_hw_tags_enable188	b	1f189alternative_cb_end190	ldr	\tmp, [\tsk, #THREAD_MTE_CTRL]191192	mte_set_gcr \tmp, \tmp21931:194#endif195	.endm196197	.macro	kernel_entry, el, regsize = 64198	.if	\el == 0199	alternative_insn nop, SET_PSTATE_DIT(1), ARM64_HAS_DIT200	.endif201	.if	\regsize == 32202	mov	w0, w0				// zero upper 32 bits of x0203	.endif204	stp	x0, x1, [sp, #16 * 0]205	stp	x2, x3, [sp, #16 * 1]206	stp	x4, x5, [sp, #16 * 2]207	stp	x6, x7, [sp, #16 * 3]208	stp	x8, x9, [sp, #16 * 4]209	stp	x10, x11, [sp, #16 * 5]210	stp	x12, x13, [sp, #16 * 6]211	stp	x14, x15, [sp, #16 * 7]212	stp	x16, x17, [sp, #16 * 8]213	stp	x18, x19, [sp, #16 * 9]214	stp	x20, x21, [sp, #16 * 10]215	stp	x22, x23, [sp, #16 * 11]216	stp	x24, x25, [sp, #16 * 12]217	stp	x26, x27, [sp, #16 * 13]218	stp	x28, x29, [sp, #16 * 14]219220	.if	\el == 0221	clear_gp_regs222	mrs	x21, sp_el0223	ldr_this_cpu	tsk, __entry_task, x20224	msr	sp_el0, tsk225226	/*227	 * Ensure MDSCR_EL1.SS is clear, since we can unmask debug exceptions228	 * when scheduling.229	 */230	ldr	x19, [tsk, #TSK_TI_FLAGS]231	disable_step_tsk x19, x20232233	/* Check for asynchronous tag check faults in user space */234	ldr	x0, [tsk, THREAD_SCTLR_USER]235	check_mte_async_tcf x22, x23, x0236237#ifdef CONFIG_ARM64_PTR_AUTH238alternative_if ARM64_HAS_ADDRESS_AUTH239	/*240	 * Enable IA for in-kernel PAC if the task had it disabled. Although241	 * this could be implemented with an unconditional MRS which would avoid242	 * a load, this was measured to be slower on Cortex-A75 and Cortex-A76.243	 *244	 * Install the kernel IA key only if IA was enabled in the task. If IA245	 * was disabled on kernel exit then we would have left the kernel IA246	 * installed so there is no need to install it again.247	 */248	tbz	x0, SCTLR_ELx_ENIA_SHIFT, 1f249	__ptrauth_keys_install_kernel_nosync tsk, x20, x22, x23250	b	2f2511:252	mrs	x0, sctlr_el1253	orr	x0, x0, SCTLR_ELx_ENIA254	msr	sctlr_el1, x02552:256alternative_else_nop_endif257#endif258259	apply_ssbd 1, x22, x23260261	mte_set_kernel_gcr x22, x23262263	/*264	 * Any non-self-synchronizing system register updates required for265	 * kernel entry should be placed before this point.266	 */267alternative_if ARM64_MTE268	isb269	b	1f270alternative_else_nop_endif271alternative_if ARM64_HAS_ADDRESS_AUTH272	isb273alternative_else_nop_endif2741:275276	scs_load_current277	.else278	add	x21, sp, #PT_REGS_SIZE279	get_current_task tsk280	.endif /* \el == 0 */281	mrs	x22, elr_el1282	mrs	x23, spsr_el1283	stp	lr, x21, [sp, #S_LR]284285	/*286	 * Create a metadata frame record. The unwinder will use this to287	 * identify and unwind exception boundaries.288	 */289	stp	xzr, xzr, [sp, #S_STACKFRAME]290	.if \el == 0291	mov	x0, #FRAME_META_TYPE_FINAL292	.else293	mov	x0, #FRAME_META_TYPE_PT_REGS294	.endif295	str	x0, [sp, #S_STACKFRAME_TYPE]296	add	x29, sp, #S_STACKFRAME297298#ifdef CONFIG_ARM64_SW_TTBR0_PAN299alternative_if_not ARM64_HAS_PAN300	bl	__swpan_entry_el\el301alternative_else_nop_endif302#endif303304	stp	x22, x23, [sp, #S_PC]305306	/* Not in a syscall by default (el0_svc overwrites for real syscall) */307	.if	\el == 0308	mov	w21, #NO_SYSCALL309	str	w21, [sp, #S_SYSCALLNO]310	.endif311312#ifdef CONFIG_ARM64_PSEUDO_NMI313alternative_if_not ARM64_HAS_GIC_PRIO_MASKING314	b	.Lskip_pmr_save\@315alternative_else_nop_endif316317	mrs_s	x20, SYS_ICC_PMR_EL1318	str	w20, [sp, #S_PMR]319	mov	x20, #GIC_PRIO_IRQON | GIC_PRIO_PSR_I_SET320	msr_s	SYS_ICC_PMR_EL1, x20321322.Lskip_pmr_save\@:323#endif324325	/*326	 * Registers that may be useful after this macro is invoked:327	 *328	 * x20 - ICC_PMR_EL1329	 * x21 - aborted SP330	 * x22 - aborted PC331	 * x23 - aborted PSTATE332	*/333	.endm334335	.macro	kernel_exit, el336	.if	\el != 0337	disable_daif338	.endif339340#ifdef CONFIG_ARM64_PSEUDO_NMI341alternative_if_not ARM64_HAS_GIC_PRIO_MASKING342	b	.Lskip_pmr_restore\@343alternative_else_nop_endif344345	ldr	w20, [sp, #S_PMR]346	msr_s	SYS_ICC_PMR_EL1, x20347348	/* Ensure priority change is seen by redistributor */349alternative_if_not ARM64_HAS_GIC_PRIO_RELAXED_SYNC350	dsb	sy351alternative_else_nop_endif352353.Lskip_pmr_restore\@:354#endif355356	ldp	x21, x22, [sp, #S_PC]		// load ELR, SPSR357358#ifdef CONFIG_ARM64_SW_TTBR0_PAN359alternative_if_not ARM64_HAS_PAN360	bl	__swpan_exit_el\el361alternative_else_nop_endif362#endif363364	.if	\el == 0365	ldr	x23, [sp, #S_SP]		// load return stack pointer366	msr	sp_el0, x23367	tst	x22, #PSR_MODE32_BIT		// native task?368	b.eq	3f369370#ifdef CONFIG_ARM64_ERRATUM_845719371alternative_if ARM64_WORKAROUND_845719372#ifdef CONFIG_PID_IN_CONTEXTIDR373	mrs	x29, contextidr_el1374	msr	contextidr_el1, x29375#else376	msr contextidr_el1, xzr377#endif378alternative_else_nop_endif379#endif3803:381	scs_save tsk382383	/* Ignore asynchronous tag check faults in the uaccess routines */384	ldr	x0, [tsk, THREAD_SCTLR_USER]385	clear_mte_async_tcf x0386387#ifdef CONFIG_ARM64_PTR_AUTH388alternative_if ARM64_HAS_ADDRESS_AUTH389	/*390	 * IA was enabled for in-kernel PAC. Disable it now if needed, or391	 * alternatively install the user's IA. All other per-task keys and392	 * SCTLR bits were updated on task switch.393	 *394	 * No kernel C function calls after this.395	 */396	tbz	x0, SCTLR_ELx_ENIA_SHIFT, 1f397	__ptrauth_keys_install_user tsk, x0, x1, x2398	b	2f3991:400	mrs	x0, sctlr_el1401	bic	x0, x0, SCTLR_ELx_ENIA402	msr	sctlr_el1, x04032:404alternative_else_nop_endif405#endif406407	mte_set_user_gcr tsk, x0, x1408409	apply_ssbd 0, x0, x1410	.endif411412	msr	elr_el1, x21			// set up the return data413	msr	spsr_el1, x22414	ldp	x0, x1, [sp, #16 * 0]415	ldp	x2, x3, [sp, #16 * 1]416	ldp	x4, x5, [sp, #16 * 2]417	ldp	x6, x7, [sp, #16 * 3]418	ldp	x8, x9, [sp, #16 * 4]419	ldp	x10, x11, [sp, #16 * 5]420	ldp	x12, x13, [sp, #16 * 6]421	ldp	x14, x15, [sp, #16 * 7]422	ldp	x16, x17, [sp, #16 * 8]423	ldp	x18, x19, [sp, #16 * 9]424	ldp	x20, x21, [sp, #16 * 10]425	ldp	x22, x23, [sp, #16 * 11]426	ldp	x24, x25, [sp, #16 * 12]427	ldp	x26, x27, [sp, #16 * 13]428	ldp	x28, x29, [sp, #16 * 14]429430	.if	\el == 0431#ifdef CONFIG_UNMAP_KERNEL_AT_EL0432	alternative_insn "b .L_skip_tramp_exit_\@", nop, ARM64_UNMAP_KERNEL_AT_EL0433434	msr	far_el1, x29435436	ldr_this_cpu	x30, this_cpu_vector, x29437	tramp_alias	x29, tramp_exit438	msr		vbar_el1, x30		// install vector table439	ldr		lr, [sp, #S_LR]		// restore x30440	add		sp, sp, #PT_REGS_SIZE	// restore sp441	br		x29442443.L_skip_tramp_exit_\@:444#endif445	.endif446447	ldr	lr, [sp, #S_LR]448	add	sp, sp, #PT_REGS_SIZE		// restore sp449450	.if \el == 0451	/* This must be after the last explicit memory access */452alternative_if ARM64_WORKAROUND_SPECULATIVE_UNPRIV_LOAD453	tlbi	vale1, xzr454	dsb	nsh455alternative_else_nop_endif456	.else457	/* Ensure any device/NC reads complete */458	alternative_insn nop, "dmb sy", ARM64_WORKAROUND_1508412459	.endif460461	eret462	sb463	.endm464465#ifdef CONFIG_ARM64_SW_TTBR0_PAN466	/*467	 * Set the TTBR0 PAN bit in SPSR. When the exception is taken from468	 * EL0, there is no need to check the state of TTBR0_EL1 since469	 * accesses are always enabled.470	 * Note that the meaning of this bit differs from the ARMv8.1 PAN471	 * feature as all TTBR0_EL1 accesses are disabled, not just those to472	 * user mappings.473	 */474SYM_CODE_START_LOCAL(__swpan_entry_el1)475	mrs	x21, ttbr0_el1476	tst	x21, #TTBR_ASID_MASK		// Check for the reserved ASID477	orr	x23, x23, #PSR_PAN_BIT		// Set the emulated PAN in the saved SPSR478	b.eq	1f				// TTBR0 access already disabled479	and	x23, x23, #~PSR_PAN_BIT		// Clear the emulated PAN in the saved SPSR480SYM_INNER_LABEL(__swpan_entry_el0, SYM_L_LOCAL)481	__uaccess_ttbr0_disable x214821:	ret483SYM_CODE_END(__swpan_entry_el1)484485	/*486	 * Restore access to TTBR0_EL1. If returning to EL0, no need for SPSR487	 * PAN bit checking.488	 */489SYM_CODE_START_LOCAL(__swpan_exit_el1)490	tbnz	x22, #22, 1f			// Skip re-enabling TTBR0 access if the PSR_PAN_BIT is set491	__uaccess_ttbr0_enable x0, x14921:	and	x22, x22, #~PSR_PAN_BIT		// ARMv8.0 CPUs do not understand this bit493	ret494SYM_CODE_END(__swpan_exit_el1)495496SYM_CODE_START_LOCAL(__swpan_exit_el0)497	__uaccess_ttbr0_enable x0, x1498	/*499	 * Enable errata workarounds only if returning to user. The only500	 * workaround currently required for TTBR0_EL1 changes are for the501	 * Cavium erratum 27456 (broadcast TLBI instructions may cause I-cache502	 * corruption).503	 */504	b	post_ttbr_update_workaround505SYM_CODE_END(__swpan_exit_el0)506#endif507508/* GPRs used by entry code */509tsk	.req	x28		// current thread_info510511	.text512513/*514 * Exception vectors.515 */516	.pushsection ".entry.text", "ax"517518	.align	11519SYM_CODE_START(vectors)520	kernel_ventry	1, t, 64, sync		// Synchronous EL1t521	kernel_ventry	1, t, 64, irq		// IRQ EL1t522	kernel_ventry	1, t, 64, fiq		// FIQ EL1t523	kernel_ventry	1, t, 64, error		// Error EL1t524525	kernel_ventry	1, h, 64, sync		// Synchronous EL1h526	kernel_ventry	1, h, 64, irq		// IRQ EL1h527	kernel_ventry	1, h, 64, fiq		// FIQ EL1h528	kernel_ventry	1, h, 64, error		// Error EL1h529530	kernel_ventry	0, t, 64, sync		// Synchronous 64-bit EL0531	kernel_ventry	0, t, 64, irq		// IRQ 64-bit EL0532	kernel_ventry	0, t, 64, fiq		// FIQ 64-bit EL0533	kernel_ventry	0, t, 64, error		// Error 64-bit EL0534535	kernel_ventry	0, t, 32, sync		// Synchronous 32-bit EL0536	kernel_ventry	0, t, 32, irq		// IRQ 32-bit EL0537	kernel_ventry	0, t, 32, fiq		// FIQ 32-bit EL0538	kernel_ventry	0, t, 32, error		// Error 32-bit EL0539SYM_CODE_END(vectors)540541SYM_CODE_START_LOCAL(__bad_stack)542	/*543	 * We detected an overflow in kernel_ventry, which switched to the544	 * overflow stack. Stash the exception regs, and head to our overflow545	 * handler.546	 */547548	/* Restore the original x0 value */549	mrs	x0, tpidrro_el0550551	/*552	 * Store the original GPRs to the new stack. The orginal SP (minus553	 * PT_REGS_SIZE) was stashed in tpidr_el0 by kernel_ventry.554	 */555	sub	sp, sp, #PT_REGS_SIZE556	kernel_entry 1557	mrs	x0, tpidr_el0558	add	x0, x0, #PT_REGS_SIZE559	str	x0, [sp, #S_SP]560561	/* Stash the regs for handle_bad_stack */562	mov	x0, sp563564	/* Time to die */565	bl	handle_bad_stack566	ASM_BUG()567SYM_CODE_END(__bad_stack)568569570	.macro entry_handler el:req, ht:req, regsize:req, label:req571SYM_CODE_START_LOCAL(el\el\ht\()_\regsize\()_\label)572	kernel_entry \el, \regsize573	mov	x0, sp574	bl	el\el\ht\()_\regsize\()_\label\()_handler575	.if \el == 0576	b	ret_to_user577	.else578	b	ret_to_kernel579	.endif580SYM_CODE_END(el\el\ht\()_\regsize\()_\label)581	.endm582583/*584 * Early exception handlers585 */586	entry_handler	1, t, 64, sync587	entry_handler	1, t, 64, irq588	entry_handler	1, t, 64, fiq589	entry_handler	1, t, 64, error590591	entry_handler	1, h, 64, sync592	entry_handler	1, h, 64, irq593	entry_handler	1, h, 64, fiq594	entry_handler	1, h, 64, error595596	entry_handler	0, t, 64, sync597	entry_handler	0, t, 64, irq598	entry_handler	0, t, 64, fiq599	entry_handler	0, t, 64, error600601	entry_handler	0, t, 32, sync602	entry_handler	0, t, 32, irq603	entry_handler	0, t, 32, fiq604	entry_handler	0, t, 32, error605606SYM_CODE_START_LOCAL(ret_to_kernel)607	kernel_exit 1608SYM_CODE_END(ret_to_kernel)609610SYM_CODE_START_LOCAL(ret_to_user)611	ldr	x19, [tsk, #TSK_TI_FLAGS]	// re-check for single-step612	enable_step_tsk x19, x2613#ifdef CONFIG_KSTACK_ERASE614	bl	stackleak_erase_on_task_stack615#endif616	kernel_exit 0617SYM_CODE_END(ret_to_user)618619	.popsection				// .entry.text620621	// Move from tramp_pg_dir to swapper_pg_dir622	.macro tramp_map_kernel, tmp623	mrs	\tmp, ttbr1_el1624	add	\tmp, \tmp, #TRAMP_SWAPPER_OFFSET625	bic	\tmp, \tmp, #USER_ASID_FLAG626	msr	ttbr1_el1, \tmp627#ifdef CONFIG_QCOM_FALKOR_ERRATUM_1003628alternative_if ARM64_WORKAROUND_QCOM_FALKOR_E1003629	/* ASID already in \tmp[63:48] */630	movk	\tmp, #:abs_g2_nc:(TRAMP_VALIAS >> 12)631	movk	\tmp, #:abs_g1_nc:(TRAMP_VALIAS >> 12)632	/* 2MB boundary containing the vectors, so we nobble the walk cache */633	movk	\tmp, #:abs_g0_nc:((TRAMP_VALIAS & ~(SZ_2M - 1)) >> 12)634	isb635	tlbi	vae1, \tmp636	dsb	nsh637alternative_else_nop_endif638#endif /* CONFIG_QCOM_FALKOR_ERRATUM_1003 */639	.endm640641	// Move from swapper_pg_dir to tramp_pg_dir642	.macro tramp_unmap_kernel, tmp643	mrs	\tmp, ttbr1_el1644	sub	\tmp, \tmp, #TRAMP_SWAPPER_OFFSET645	orr	\tmp, \tmp, #USER_ASID_FLAG646	msr	ttbr1_el1, \tmp647	/*648	 * We avoid running the post_ttbr_update_workaround here because649	 * it's only needed by Cavium ThunderX, which requires KPTI to be650	 * disabled.651	 */652	.endm653654	.macro		tramp_data_read_var	dst, var655#ifdef CONFIG_RELOCATABLE656	ldr		\dst, .L__tramp_data_\var657	.ifndef		.L__tramp_data_\var658	.pushsection	".entry.tramp.rodata", "a", %progbits659	.align		3660.L__tramp_data_\var:661	.quad		\var662	.popsection663	.endif664#else665	/*666	 * As !RELOCATABLE implies !RANDOMIZE_BASE the address is always a667	 * compile time constant (and hence not secret and not worth hiding).668	 *669	 * As statically allocated kernel code and data always live in the top670	 * 47 bits of the address space we can sign-extend bit 47 and avoid an671	 * instruction to load the upper 16 bits (which must be 0xFFFF).672	 */673	movz		\dst, :abs_g2_s:\var674	movk		\dst, :abs_g1_nc:\var675	movk		\dst, :abs_g0_nc:\var676#endif677	.endm678679#define BHB_MITIGATION_NONE	0680#define BHB_MITIGATION_LOOP	1681#define BHB_MITIGATION_FW	2682#define BHB_MITIGATION_INSN	3683684	.macro tramp_ventry, vector_start, regsize, kpti, bhb685	.align	76861:687	.if	\regsize == 64688	msr	tpidrro_el0, x30	// Restored in kernel_ventry689	.endif690691	.if	\bhb == BHB_MITIGATION_LOOP692	/*693	 * This sequence must appear before the first indirect branch. i.e. the694	 * ret out of tramp_ventry. It appears here because x30 is free.695	 */696	__mitigate_spectre_bhb_loop	x30697	.endif // \bhb == BHB_MITIGATION_LOOP698699	.if	\bhb == BHB_MITIGATION_INSN700	clearbhb701	isb702	.endif // \bhb == BHB_MITIGATION_INSN703704	.if	\kpti == 1705	/*706	 * Defend against branch aliasing attacks by pushing a dummy707	 * entry onto the return stack and using a RET instruction to708	 * enter the full-fat kernel vectors.709	 */710	bl	2f711	b	.7122:713	tramp_map_kernel	x30714alternative_insn isb, nop, ARM64_WORKAROUND_QCOM_FALKOR_E1003715	tramp_data_read_var	x30, vectors716alternative_if_not ARM64_WORKAROUND_CAVIUM_TX2_219_PRFM717	prfm	plil1strm, [x30, #(1b - \vector_start)]718alternative_else_nop_endif719720	msr	vbar_el1, x30721	isb722	.else723	adr_l	x30, vectors724	.endif // \kpti == 1725726	.if	\bhb == BHB_MITIGATION_FW727	/*728	 * The firmware sequence must appear before the first indirect branch.729	 * i.e. the ret out of tramp_ventry. But it also needs the stack to be730	 * mapped to save/restore the registers the SMC clobbers.731	 */732	__mitigate_spectre_bhb_fw733	.endif // \bhb == BHB_MITIGATION_FW734735	add	x30, x30, #(1b - \vector_start + 4)736	ret737.org 1b + 128	// Did we overflow the ventry slot?738	.endm739740	.macro	generate_tramp_vector,	kpti, bhb741.Lvector_start\@:742	.space	0x400743744	.rept	4745	tramp_ventry	.Lvector_start\@, 64, \kpti, \bhb746	.endr747	.rept	4748	tramp_ventry	.Lvector_start\@, 32, \kpti, \bhb749	.endr750	.endm751752#ifdef CONFIG_UNMAP_KERNEL_AT_EL0753/*754 * Exception vectors trampoline.755 * The order must match __bp_harden_el1_vectors and the756 * arm64_bp_harden_el1_vectors enum.757 */758	.pushsection ".entry.tramp.text", "ax"759	.align	11760SYM_CODE_START_LOCAL_NOALIGN(tramp_vectors)761#ifdef CONFIG_MITIGATE_SPECTRE_BRANCH_HISTORY762	generate_tramp_vector	kpti=1, bhb=BHB_MITIGATION_LOOP763	generate_tramp_vector	kpti=1, bhb=BHB_MITIGATION_FW764	generate_tramp_vector	kpti=1, bhb=BHB_MITIGATION_INSN765#endif /* CONFIG_MITIGATE_SPECTRE_BRANCH_HISTORY */766	generate_tramp_vector	kpti=1, bhb=BHB_MITIGATION_NONE767SYM_CODE_END(tramp_vectors)768769SYM_CODE_START_LOCAL(tramp_exit)770	tramp_unmap_kernel	x29771	mrs		x29, far_el1		// restore x29772	eret773	sb774SYM_CODE_END(tramp_exit)775	.popsection				// .entry.tramp.text776#endif /* CONFIG_UNMAP_KERNEL_AT_EL0 */777778/*779 * Exception vectors for spectre mitigations on entry from EL1 when780 * kpti is not in use.781 */782	.macro generate_el1_vector, bhb783.Lvector_start\@:784	kernel_ventry	1, t, 64, sync		// Synchronous EL1t785	kernel_ventry	1, t, 64, irq		// IRQ EL1t786	kernel_ventry	1, t, 64, fiq		// FIQ EL1h787	kernel_ventry	1, t, 64, error		// Error EL1t788789	kernel_ventry	1, h, 64, sync		// Synchronous EL1h790	kernel_ventry	1, h, 64, irq		// IRQ EL1h791	kernel_ventry	1, h, 64, fiq		// FIQ EL1h792	kernel_ventry	1, h, 64, error		// Error EL1h793794	.rept	4795	tramp_ventry	.Lvector_start\@, 64, 0, \bhb796	.endr797	.rept 4798	tramp_ventry	.Lvector_start\@, 32, 0, \bhb799	.endr800	.endm801802/* The order must match tramp_vecs and the arm64_bp_harden_el1_vectors enum. */803	.pushsection ".entry.text", "ax"804	.align	11805SYM_CODE_START(__bp_harden_el1_vectors)806#ifdef CONFIG_MITIGATE_SPECTRE_BRANCH_HISTORY807	generate_el1_vector	bhb=BHB_MITIGATION_LOOP808	generate_el1_vector	bhb=BHB_MITIGATION_FW809	generate_el1_vector	bhb=BHB_MITIGATION_INSN810#endif /* CONFIG_MITIGATE_SPECTRE_BRANCH_HISTORY */811SYM_CODE_END(__bp_harden_el1_vectors)812	.popsection813814815/*816 * Register switch for AArch64. The callee-saved registers need to be saved817 * and restored. On entry:818 *   x0 = previous task_struct (must be preserved across the switch)819 *   x1 = next task_struct820 * Previous and next are guaranteed not to be the same.821 *822 */823SYM_FUNC_START(cpu_switch_to)824	save_and_disable_daif x11825	mov	x10, #THREAD_CPU_CONTEXT826	add	x8, x0, x10827	mov	x9, sp828	stp	x19, x20, [x8], #16		// store callee-saved registers829	stp	x21, x22, [x8], #16830	stp	x23, x24, [x8], #16831	stp	x25, x26, [x8], #16832	stp	x27, x28, [x8], #16833	stp	x29, x9, [x8], #16834	str	lr, [x8]835	add	x8, x1, x10836	ldp	x19, x20, [x8], #16		// restore callee-saved registers837	ldp	x21, x22, [x8], #16838	ldp	x23, x24, [x8], #16839	ldp	x25, x26, [x8], #16840	ldp	x27, x28, [x8], #16841	ldp	x29, x9, [x8], #16842	ldr	lr, [x8]843	mov	sp, x9844	msr	sp_el0, x1845	ptrauth_keys_install_kernel x1, x8, x9, x10846	scs_save x0847	scs_load_current848	restore_irq x11849	ret850SYM_FUNC_END(cpu_switch_to)851NOKPROBE(cpu_switch_to)852853/*854 * This is how we return from a fork.855 */856SYM_CODE_START(ret_from_fork)857	bl	schedule_tail858	cbz	x19, 1f				// not a kernel thread859	mov	x0, x20860	blr	x198611:	get_current_task tsk862	mov	x0, sp863	bl	asm_exit_to_user_mode864	b	ret_to_user865SYM_CODE_END(ret_from_fork)866NOKPROBE(ret_from_fork)867868/*869 * void call_on_irq_stack(struct pt_regs *regs,870 * 		          void (*func)(struct pt_regs *));871 *872 * Calls func(regs) using this CPU's irq stack and shadow irq stack.873 */874SYM_FUNC_START(call_on_irq_stack)875	save_and_disable_daif x9876#ifdef CONFIG_SHADOW_CALL_STACK877	get_current_task x16878	scs_save x16879	ldr_this_cpu scs_sp, irq_shadow_call_stack_ptr, x17880#endif881882	/* Create a frame record to save our LR and SP (implicit in FP) */883	stp	x29, x30, [sp, #-16]!884	mov	x29, sp885886	ldr_this_cpu x16, irq_stack_ptr, x17887888	/* Move to the new stack and call the function there */889	add	sp, x16, #IRQ_STACK_SIZE890	restore_irq x9891	blr	x1892893	save_and_disable_daif x9894	/*895	 * Restore the SP from the FP, and restore the FP and LR from the frame896	 * record.897	 */898	mov	sp, x29899	ldp	x29, x30, [sp], #16900	scs_load_current901	restore_irq x9902	ret903SYM_FUNC_END(call_on_irq_stack)904NOKPROBE(call_on_irq_stack)905906#ifdef CONFIG_ARM_SDE_INTERFACE907908#include <asm/sdei.h>909#include <uapi/linux/arm_sdei.h>910911.macro sdei_handler_exit exit_mode912	/* On success, this call never returns... */913	cmp	\exit_mode, #SDEI_EXIT_SMC914	b.ne	99f915	smc	#0916	b	.91799:	hvc	#0918	b	.919.endm920921#ifdef CONFIG_UNMAP_KERNEL_AT_EL0922/*923 * The regular SDEI entry point may have been unmapped along with the rest of924 * the kernel. This trampoline restores the kernel mapping to make the x1 memory925 * argument accessible.926 *927 * This clobbers x4, __sdei_handler() will restore this from firmware's928 * copy.929 */930.pushsection ".entry.tramp.text", "ax"931SYM_CODE_START(__sdei_asm_entry_trampoline)932	mrs	x4, ttbr1_el1933	tbz	x4, #USER_ASID_BIT, 1f934935	tramp_map_kernel tmp=x4936	isb937	mov	x4, xzr938939	/*940	 * Remember whether to unmap the kernel on exit.941	 */9421:	str	x4, [x1, #(SDEI_EVENT_INTREGS + S_SDEI_TTBR1)]943	tramp_data_read_var     x4, __sdei_asm_handler944	br	x4945SYM_CODE_END(__sdei_asm_entry_trampoline)946NOKPROBE(__sdei_asm_entry_trampoline)947948/*949 * Make the exit call and restore the original ttbr1_el1950 *951 * x0 & x1: setup for the exit API call952 * x2: exit_mode953 * x4: struct sdei_registered_event argument from registration time.954 */955SYM_CODE_START(__sdei_asm_exit_trampoline)956	ldr	x4, [x4, #(SDEI_EVENT_INTREGS + S_SDEI_TTBR1)]957	cbnz	x4, 1f958959	tramp_unmap_kernel	tmp=x49609611:	sdei_handler_exit exit_mode=x2962SYM_CODE_END(__sdei_asm_exit_trampoline)963NOKPROBE(__sdei_asm_exit_trampoline)964.popsection		// .entry.tramp.text965#endif /* CONFIG_UNMAP_KERNEL_AT_EL0 */966967/*968 * Software Delegated Exception entry point.969 *970 * x0: Event number971 * x1: struct sdei_registered_event argument from registration time.972 * x2: interrupted PC973 * x3: interrupted PSTATE974 * x4: maybe clobbered by the trampoline975 *976 * Firmware has preserved x0->x17 for us, we must save/restore the rest to977 * follow SMC-CC. We save (or retrieve) all the registers as the handler may978 * want them.979 */980SYM_CODE_START(__sdei_asm_handler)981	stp     x2, x3, [x1, #SDEI_EVENT_INTREGS + S_PC]982	stp     x4, x5, [x1, #SDEI_EVENT_INTREGS + 16 * 2]983	stp     x6, x7, [x1, #SDEI_EVENT_INTREGS + 16 * 3]984	stp     x8, x9, [x1, #SDEI_EVENT_INTREGS + 16 * 4]985	stp     x10, x11, [x1, #SDEI_EVENT_INTREGS + 16 * 5]986	stp     x12, x13, [x1, #SDEI_EVENT_INTREGS + 16 * 6]987	stp     x14, x15, [x1, #SDEI_EVENT_INTREGS + 16 * 7]988	stp     x16, x17, [x1, #SDEI_EVENT_INTREGS + 16 * 8]989	stp     x18, x19, [x1, #SDEI_EVENT_INTREGS + 16 * 9]990	stp     x20, x21, [x1, #SDEI_EVENT_INTREGS + 16 * 10]991	stp     x22, x23, [x1, #SDEI_EVENT_INTREGS + 16 * 11]992	stp     x24, x25, [x1, #SDEI_EVENT_INTREGS + 16 * 12]993	stp     x26, x27, [x1, #SDEI_EVENT_INTREGS + 16 * 13]994	stp     x28, x29, [x1, #SDEI_EVENT_INTREGS + 16 * 14]995	mov	x4, sp996	stp     lr, x4, [x1, #SDEI_EVENT_INTREGS + S_LR]997998	mov	x19, x19991000	/* Store the registered-event for crash_smp_send_stop() */1001	ldrb	w4, [x19, #SDEI_EVENT_PRIORITY]1002	cbnz	w4, 1f1003	adr_this_cpu dst=x5, sym=sdei_active_normal_event, tmp=x61004	b	2f10051:	adr_this_cpu dst=x5, sym=sdei_active_critical_event, tmp=x610062:	str	x19, [x5]10071008	/*1009	 * entry.S may have been using sp as a scratch register, find whether1010	 * this is a normal or critical event and switch to the appropriate1011	 * stack for this CPU.1012	 */1013	cbnz	w4, 1f1014	ldr_this_cpu dst=x5, sym=sdei_stack_normal_ptr, tmp=x61015	b	2f10161:	ldr_this_cpu dst=x5, sym=sdei_stack_critical_ptr, tmp=x610172:	mov	x6, #SDEI_STACK_SIZE1018	add	x5, x5, x61019	mov	sp, x510201021#ifdef CONFIG_SHADOW_CALL_STACK1022	/* Use a separate shadow call stack for normal and critical events */1023	cbnz	w4, 3f1024	ldr_this_cpu dst=scs_sp, sym=sdei_shadow_call_stack_normal_ptr, tmp=x61025	b	4f10263:	ldr_this_cpu dst=scs_sp, sym=sdei_shadow_call_stack_critical_ptr, tmp=x610274:1028#endif10291030	/*1031	 * We may have interrupted userspace, or a guest, or exit-from or1032	 * return-to either of these. We can't trust sp_el0, restore it.1033	 */1034	mrs	x28, sp_el01035	ldr_this_cpu	dst=x0, sym=__entry_task, tmp=x11036	msr	sp_el0, x010371038	/* If we interrupted the kernel point to the previous stack/frame. */1039	and     x0, x3, #0xc1040	mrs     x1, CurrentEL1041	cmp     x0, x11042	csel	x29, x29, xzr, eq	// fp, or zero1043	csel	x4, x2, xzr, eq		// elr, or zero10441045	stp	x29, x4, [sp, #-16]!1046	mov	x29, sp10471048	add	x0, x19, #SDEI_EVENT_INTREGS1049	mov	x1, x191050	bl	__sdei_handler10511052	msr	sp_el0, x281053	/* restore regs >x17 that we clobbered */1054	mov	x4, x19         // keep x4 for __sdei_asm_exit_trampoline1055	ldp	x28, x29, [x4, #SDEI_EVENT_INTREGS + 16 * 14]1056	ldp	x18, x19, [x4, #SDEI_EVENT_INTREGS + 16 * 9]1057	ldp	lr, x1, [x4, #SDEI_EVENT_INTREGS + S_LR]1058	mov	sp, x110591060	mov	x1, x0			// address to complete_and_resume1061	/* x0 = (x0 <= SDEI_EV_FAILED) ?1062	 * EVENT_COMPLETE:EVENT_COMPLETE_AND_RESUME1063	 */1064	cmp	x0, #SDEI_EV_FAILED1065	mov_q	x2, SDEI_1_0_FN_SDEI_EVENT_COMPLETE1066	mov_q	x3, SDEI_1_0_FN_SDEI_EVENT_COMPLETE_AND_RESUME1067	csel	x0, x2, x3, ls10681069	ldr_l	x2, sdei_exit_mode10701071	/* Clear the registered-event seen by crash_smp_send_stop() */1072	ldrb	w3, [x4, #SDEI_EVENT_PRIORITY]1073	cbnz	w3, 1f1074	adr_this_cpu dst=x5, sym=sdei_active_normal_event, tmp=x61075	b	2f10761:	adr_this_cpu dst=x5, sym=sdei_active_critical_event, tmp=x610772:	str	xzr, [x5]10781079alternative_if_not ARM64_UNMAP_KERNEL_AT_EL01080	sdei_handler_exit exit_mode=x21081alternative_else_nop_endif10821083#ifdef CONFIG_UNMAP_KERNEL_AT_EL01084	tramp_alias	dst=x5, sym=__sdei_asm_exit_trampoline1085	br	x51086#endif1087SYM_CODE_END(__sdei_asm_handler)1088NOKPROBE(__sdei_asm_handler)10891090SYM_CODE_START(__sdei_handler_abort)1091	mov_q	x0, SDEI_1_0_FN_SDEI_EVENT_COMPLETE_AND_RESUME1092	adr	x1, 1f1093	ldr_l	x2, sdei_exit_mode1094	sdei_handler_exit exit_mode=x21095	// exit the handler and jump to the next instruction.1096	// Exit will stomp x0-x17, PSTATE, ELR_ELx, and SPSR_ELx.10971:	ret1098SYM_CODE_END(__sdei_handler_abort)1099NOKPROBE(__sdei_handler_abort)1100#endif /* CONFIG_ARM_SDE_INTERFACE */
맨 위로 ↑