이 파일은 왜 필요한가요?
C 함수가 실행되려면 이미 쓸 수 있는 스택과 메모리 주소 체계가 있어야 합니다. 그런데 전원을 켠 직후에는 커널이 그 준비를 직접 해야 합니다. head.S는 부트로더가 넘긴 정보를 잃지 않으면서 CPU의 실행 상태를 맞추고, 페이지 테이블과 스택을 준비해 C 초기화 코드로 연결하는 부분입니다. 파일의 위에서 아래가 실행 순서는 아닙니다. BL과 B가 어느 라벨로 향하는지 따라가야 합니다.
명령·지시문·라벨을 원본 줄 번호에 맞춰 설명합니다. 주석과 빈 줄을 포함한 전체 원문은 아래에 따로 두었습니다. 매크로 정의와 조건부 코드는 모두 차례대로 실행되는 명령 목록이 아닙니다.
기존 코드 분석과 함께 읽기 · 원본 파일 · 설명 원고
먼저 알아둘 개념: 레지스터와 값, 주소와 주소가 가리키는 내용, SP와 스택 공간, 가상 주소와 MMU, 예외 레벨과 복귀, 매크로와 실제 CPU 명령
처음 읽을 때
처음에는 x0가 FDT 주소라는 점, 함수 호출 뒤에는 x0의 용도가 바뀐다는 점을 따라가세요. 주소를 저장하는 것과 그 주소의 내용을 복사하는 것도 구분합니다.
더 깊이 살펴볼 때
MMU 활성화 전후에 PC의 숫자는 그대로인데 해석이 달라집니다. idmap이 왜 필요한지, 최종 커널 VA로 가는 BR을 왜 별도로 수행하는지 설명해 보세요. EL2에서 시작했다는 기록과 현재 실행 EL 역시 다릅니다.
한 줄씩 레지스터와 메모리 상태 보기
각 코드 아래의 ‘행 그림’을 펼치면, 그 줄에서 읽는 값과 실행 전·후의 상태를 나란히 볼 수 있습니다. 여기서 ‘명령 직전’은 지금 보고 있는 명령이 실행되기 바로 전을 뜻합니다. 함수 진입 때나 소스의 바로 윗줄을 가리키는 말은 아닙니다. 앞서 실행한 명령이 값을 바꿨다면 그 결과가 들어 있습니다.
소스로 정해지는 상수, 호출자가 넘기는 값, 실제 실행에서 정해지는 주소를 구분해서 읽어야 합니다. 주소 숫자를 몰라도 무엇을 가리키는지와 어디서 받아 오는지는 설명할 수 있습니다. ‘명령 직전 x0의 값’처럼 일반 표기로 남은 곳은 한 명령의 동작을 나타내며, 특정 호출 경로의 값까지 확인했다는 뜻은 아닙니다.
일반 명령은 그 한 줄의 효과를, 매크로는 여러 명령을 묶은 주요 결과를 표시합니다. 함수 호출은 호출 직후의 PC와 LR까지만 나타냅니다. 메모리 접근은 정상적으로 끝나는 경우를 가정합니다. 생략된 항목까지 모두 보존된다는 뜻은 아닙니다.
화살표 →는 같은 항목의 전후 비교입니다. 메모리 칸에는 주소와 크기를 따로 적었습니다. SP가 줄어들면 낮은 주소 쪽을 사용하며, 화면의 위아래와 메모리 주소 방향은 별개입니다. 전처리·어셈블·링크 지시문은 CPU 실행과 구분합니다.
‘이전·다음 행’은 소스에 적힌 순서입니다. 분기·반복·호출이 있으므로 파일 위에서 아래로 실제 실행된다고 가정하지 않습니다. 값이 여러 줄에 걸쳐 이어지는 모습은 cpu-reset.S의 단계별 예제에서 보실 수 있습니다.
총 321개 줄에 그림을 붙였습니다. 기존 코드와 설명은 그 아래에서 이어집니다.
먼저 그림으로 살펴보기
소스의 구성과 Image의 앞부분
#include <linux/linkage.h>SYM_FUNC_START·SYM_CODE_START·SYM_INNER_LABEL 등의 심볼 선언 규칙을 가져옵니다. 함수인지 코드 내부 라벨인지, 다른 오브젝트에 보일 이름인지와 정렬·크기 정보를 빌드 도구에 전달하는 데 필요합니다. #include는 전처리 단계에서 헤더 내용을 가져옵니다. CPU가 이 줄에서 함수를 호출하는 동작은 없습니다.
12행 그림 · 전처리 단계
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
#include <linux/init.h>부팅 때만 사용할 코드·데이터의 섹션 표기를 가져옵니다. 초기화가 끝난 뒤 해제 가능한 영역과 계속 남겨야 하는 영역을 링커가 구분하게 합니다. #include는 전처리 단계에서 헤더 내용을 가져옵니다. CPU가 이 줄에서 함수를 호출하는 동작은 없습니다.
13행 그림 · 전처리 단계
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
#include <linux/pgtable.h>페이지 표를 다루는 공통 정의와 아키텍처별 표 정의를 연결합니다. 초기 매핑을 만들 때 필요한 표 항목·속성의 정의를 일관되게 사용하게 합니다. #include는 전처리 단계에서 헤더 내용을 가져옵니다. CPU가 이 줄에서 함수를 호출하는 동작은 없습니다.
14행 그림 · 전처리 단계
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
#include <asm/asm_pointer_auth.h>PAC 키 설치 매크로를 가져옵니다. 사용자·커널 또는 task 사이를 전환할 때 instruction-address 인증 키가 해당 실행 문맥과 맞아야 하므로 필요합니다. #include는 전처리 단계에서 헤더 내용을 가져옵니다. CPU가 이 줄에서 함수를 호출하는 동작은 없습니다.
16행 그림 · 전처리 단계
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
#include <asm/assembler.h>ARM64의 큰 주소 구성, CPU별 값 조회, 시스템 레지스터 처리, 동기화 등에 쓰는 어셈블리 매크로를 가져옵니다. 각 사용 위치에서 일반 명령 여러 개로 펼쳐지는 작업입니다. #include는 전처리 단계에서 헤더 내용을 가져옵니다. CPU가 이 줄에서 함수를 호출하는 동작은 없습니다.
17행 그림 · 전처리 단계
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
#include <asm/boot.h>부트로더에서 ARM64 커널로 진입할 때 사용하는 부팅 관련 정의를 가져옵니다. 펌웨어가 넘긴 실행 상태를 초기 커널의 규칙에 맞추는 코드가 이 공통 정의를 사용합니다. #include는 전처리 단계에서 헤더 내용을 가져옵니다. CPU가 이 줄에서 함수를 호출하는 동작은 없습니다.
18행 그림 · 전처리 단계
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
#include <asm/bug.h>복구를 기대하지 않는 커널 오류를 BRK와 BUG 메타데이터로 표현하는 규칙을 가져옵니다. 돌아오지 않아야 할 초기화 함수가 반환한 경우 등의 오류 경로에서 사용합니다. #include는 전처리 단계에서 헤더 내용을 가져옵니다. CPU가 이 줄에서 함수를 호출하는 동작은 없습니다.
19행 그림 · 전처리 단계
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
#include <asm/ptrace.h>pt_regs와 예외 전 PSTATE의 레벨·폭·마스크 비트 정의를 가져옵니다. 저장된 실행 문맥을 구성하고 32/64비트 복귀 상태를 해석하는 데 필요합니다. #include는 전처리 단계에서 헤더 내용을 가져옵니다. CPU가 이 줄에서 함수를 호출하는 동작은 없습니다.
20행 그림 · 전처리 단계
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
#include <asm/asm-offsets.h>C 구조체에서 계산한 필드 오프셋을 가져옵니다. 어셈블리가 task_struct·pt_regs 등에서 값을 읽고 쓸 때 C 빌드의 실제 배치와 일치하도록 생성하는 헤더입니다. #include는 전처리 단계에서 헤더 내용을 가져옵니다. CPU가 이 줄에서 함수를 호출하는 동작은 없습니다.
21행 그림 · 전처리 단계
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
#include <asm/cache.h>캐시 라인 정렬과 유지보수 관련 크기 정의를 가져옵니다. 초기 데이터와 표를 캐시 단위로 다룰 때 사용하며 CPU 캐시를 이 include가 직접 켜는 것은 아닙니다. #include는 전처리 단계에서 헤더 내용을 가져옵니다. CPU가 이 줄에서 함수를 호출하는 동작은 없습니다.
22행 그림 · 전처리 단계
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
#include <asm/cputype.h>CPU 식별·기능 레지스터를 해석할 정의를 가져옵니다. 구현별 차이나 지원 주소 변환 기능을 판단할 때 숫자 비트 위치를 이름으로 사용할 수 있게 합니다. #include는 전처리 단계에서 헤더 내용을 가져옵니다. CPU가 이 줄에서 함수를 호출하는 동작은 없습니다.
23행 그림 · 전처리 단계
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
#include <asm/el2_setup.h>EL2 초기화 매크로를 가져옵니다. EL2로 들어온 CPU의 타이머·트랩·가상화 관련 상태를 정리한 뒤 커널의 다음 실행 단계로 넘기는 데 쓰입니다. #include는 전처리 단계에서 헤더 내용을 가져옵니다. CPU가 이 줄에서 함수를 호출하는 동작은 없습니다.
24행 그림 · 전처리 단계
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
#include <asm/elf.h>ARM64 ELF 실행 형식과 CPU 기능 표현에 관한 정의를 가져옵니다. ELF 관련 상수는 파일·실행 규약이며 include 자체가 ELF 로딩을 수행하지 않습니다. #include는 전처리 단계에서 헤더 내용을 가져옵니다. CPU가 이 줄에서 함수를 호출하는 동작은 없습니다.
25행 그림 · 전처리 단계
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
#include <asm/image.h>부트로더가 읽는 ARM64 Image 헤더의 매직 값·플래그·필드 표현을 가져옵니다. head.S 앞부분이 외부 부팅 규약에 맞는 헤더를 출력할 때 필요합니다. #include는 전처리 단계에서 헤더 내용을 가져옵니다. CPU가 이 줄에서 함수를 호출하는 동작은 없습니다.
26행 그림 · 전처리 단계
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
#include <asm/kernel-pgtable.h>초기 페이지 표의 크기와 루트 표 사이의 배치 차이를 가져옵니다. TTBR 전환 코드와 링커가 같은 공간·오프셋을 전제로 하게 합니다. #include는 전처리 단계에서 헤더 내용을 가져옵니다. CPU가 이 줄에서 함수를 호출하는 동작은 없습니다.
27행 그림 · 전처리 단계
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
#include <asm/kvm_arm.h>ARM 가상화 제어의 비트와 상수를 가져옵니다. EL2에서 HCR 등의 초기 상태를 구성하여 뒤의 EL1 커널 실행과 가상화 동작을 준비하는 데 사용합니다. #include는 전처리 단계에서 헤더 내용을 가져옵니다. CPU가 이 줄에서 함수를 호출하는 동작은 없습니다.
28행 그림 · 전처리 단계
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
#include <asm/memory.h>커널 가상 주소 배치, 스택 크기·정렬, 주소 변환 관련 상수를 가져옵니다. 같은 숫자가 주소·크기·오프셋 중 무엇인지 각 사용 문맥에서 구분해야 합니다. #include는 전처리 단계에서 헤더 내용을 가져옵니다. CPU가 이 줄에서 함수를 호출하는 동작은 없습니다.
29행 그림 · 전처리 단계
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
#include <asm/pgtable-hwdef.h>CPU가 해석하는 페이지 표 항목과 주소 변환 제어 필드를 가져옵니다. 초기 매핑의 속성과 TCR·TTBR 처리에서 하드웨어 비트 정의를 일관되게 사용합니다. #include는 전처리 단계에서 헤더 내용을 가져옵니다. CPU가 이 줄에서 함수를 호출하는 동작은 없습니다.
30행 그림 · 전처리 단계
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
#include <asm/page.h>빌드가 선택한 페이지 크기와 정렬 단위를 가져옵니다. 4KiB를 항상 가정하지 않고 선택된 PAGE_SIZE로 표와 이미지 경계를 맞추는 데 사용합니다. #include는 전처리 단계에서 헤더 내용을 가져옵니다. CPU가 이 줄에서 함수를 호출하는 동작은 없습니다.
31행 그림 · 전처리 단계
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
#include <asm/scs.h>별도 보호 스택에 복귀 주소를 보관하는 Shadow Call Stack의 저장·복원 규칙을 가져옵니다. task나 IRQ 문맥을 바꿀 때 일반 SP와 SCS 포인터를 함께 관리해야 합니다. #include는 전처리 단계에서 헤더 내용을 가져옵니다. CPU가 이 줄에서 함수를 호출하는 동작은 없습니다.
32행 그림 · 전처리 단계
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
#include <asm/smp.h>보조 CPU 시작과 초기 부팅 상태를 전달하는 SMP 정의를 가져옵니다. secondary_data와 부팅 상태값을 통해 다른 CPU와 시작 과정을 맞춥니다. #include는 전처리 단계에서 헤더 내용을 가져옵니다. CPU가 이 줄에서 함수를 호출하는 동작은 없습니다.
33행 그림 · 전처리 단계
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
#include <asm/sysreg.h>ARM 시스템 레지스터의 번호·필드 및 일반 이름으로 지원되지 않는 접근 인코딩을 가져옵니다. 기능 비트를 검사하거나 제어값을 구성할 때 사용합니다. #include는 전처리 단계에서 헤더 내용을 가져옵니다. CPU가 이 줄에서 함수를 호출하는 동작은 없습니다.
34행 그림 · 전처리 단계
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
#include <asm/stacktrace/frame.h>스택 추적기의 프레임 메타데이터 형식을 가져옵니다. 예외가 끊은 커널 프레임인지 추적의 마지막 프레임인지 표시하여 FP 연결을 안전하게 해석하게 합니다. #include는 전처리 단계에서 헤더 내용을 가져옵니다. CPU가 이 줄에서 함수를 호출하는 동작은 없습니다.
35행 그림 · 전처리 단계
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
#include <asm/thread_info.h>task의 저수준 실행 플래그와 스택 관련 정의를 가져옵니다. 예외 진입·복귀에서 single-step·SSBD·MTE 등 task별 처리를 고를 때 쓰는 정보입니다. #include는 전처리 단계에서 헤더 내용을 가져옵니다. CPU가 이 줄에서 함수를 호출하는 동작은 없습니다.
36행 그림 · 전처리 단계
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
#include <asm/virt.h>EL1·EL2 부팅 모드와 가상화 상태 표시를 가져옵니다. CPU가 어디서 시작했는지와 현재 어느 EL에서 실행하는지를 구분해 기록하는 데 필요합니다. #include는 전처리 단계에서 헤더 내용을 가져옵니다. CPU가 이 줄에서 함수를 호출하는 동작은 없습니다.
37행 그림 · 전처리 단계
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
#include "efi-header.S"ARM64 Image 앞부분에 함께 넣을 EFI PE/COFF 헤더 생성 코드를 가져옵니다. 지원 빌드에서 펌웨어가 같은 파일을 EFI 실행 이미지로도 인식하게 합니다. #include는 전처리 단계에서 헤더 내용을 가져옵니다. CPU가 이 줄에서 함수를 호출하는 동작은 없습니다.
39행 그림 · 전처리 단계
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
#if (PAGE_OFFSET & 0x1fffff) != 0PAGE_OFFSET는 선형 매핑이 시작할 가상 주소입니다. 하위 21비트가 0인지 검사해 최소 2MiB 경계 정렬을 보장합니다. 맞지 않으면 다음 #error로 빌드를 중단하여 초기 페이지 표의 블록 매핑 전제가 깨진 이미지를 만들지 않습니다. 이 조건은 빌드 전처리 단계에서 포함할 코드를 고릅니다. CPU가 실행 중 전처리 조건을 검사하지는 않습니다.
41행 그림 · 전처리 단계
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
#error PAGE_OFFSET must be at least 2MB aligned필요한 배치 조건이 성립하지 않으면 빌드를 멈춥니다. 잘못된 전제로 부팅하도록 두지 않는 검사입니다. 대상: PAGE_OFFSET must be at least 2MB aligned
42행 그림 · 전처리 단계
이 줄을 포함하는 조건: #if (PAGE_OFFSET & 0x1fffff) != 0
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
#endif빌드 조건으로 나누었던 구간을 닫습니다. 대상:
43행 그림 · 전처리 단계
이 줄을 포함하는 조건: #if (PAGE_OFFSET & 0x1fffff) != 0
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
64바이트 Image 헤더와 실행 전 필요한 조건
__HEADImage 맨 앞에 놓일 코드 영역으로 출력 위치를 바꿉니다. 부트로더가 처음 실행할 바이트의 위치를 정하는 빌드 단계의 지시입니다.
57행 그림 · 어셈블·링크 단계
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
efi_signature_nop // special NOP to identity as PE/COFF executableEFI 로더가 알아볼 MZ 서명과 CPU가 읽을 첫 명령을 같은 바이트에 담습니다. EFI 설정이 없으면 일반 NOP가 됩니다.
61행 그림 · 동기화·캐시·대기 상태
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- EFI의 CCMP 형식에서는 NZCV가 바뀔 수 있지만 부팅 코드가 이 결과를 의미 있는 계산값으로 사용하지 않습니다.
efi_signature_nop 매크로 본문 · 10행
arch/arm64/kernel/efi-header.S · Linux v6.18.37
.macro efi_signature_nop
#ifdef CONFIG_EFI
.L_head:
/*
* This ccmp instruction has no meaningful effect except that
* its opcode forms the magic "MZ" signature required by UEFI.
*/
ccmp x18, #0, #0xd, pl
#else
/*
* Bootloaders may inspect the opcode at the start of the kernel
* image to decide if the kernel is capable of booting via UEFI.
* So put an ordinary NOP here, not the "MZ.." pseudo-nop above.
*/
nop
#endif
.endm b primary_entry // branch to kernel start, magicprimary_entry부터 실행을 계속합니다. 돌아올 주소를 새로 적지 않으므로 일반 함수 호출과는 다른 제어 이동입니다.
62행 그림 · 호출과 실행 위치
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 새 복귀 주소를 LR에 기록하지 않습니다. RET 역시 메모리의 스택 값을 자동으로 꺼내는 명령은 아닙니다.
.quad 0 // Image load offset from start of RAM, little-endian각 값을 8바이트 크기로 파일 안에 기록합니다. 0는 CPU가 지금 계산할 식이 아니라 어셈블·링크 단계에서 채울 데이터입니다.
63행 그림 · 어셈블·링크 단계
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
le64sym _kernel_size_le // Effective size of kernel image, little-endian커널의 바이트 순서 설정과 별개로 부팅 규약이 요구하는 리틀 엔디언 64비트 필드를 만듭니다. 같은 숫자라도 저장하는 바이트 순서가 중요합니다.
64행 그림 · 어셈블·링크 단계
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
le64sym _kernel_flags_le // Informative flags, little-endian커널의 바이트 순서 설정과 별개로 부팅 규약이 요구하는 리틀 엔디언 64비트 필드를 만듭니다. 같은 숫자라도 저장하는 바이트 순서가 중요합니다.
65행 그림 · 어셈블·링크 단계
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
.quad 0 // reserved각 값을 8바이트 크기로 파일 안에 기록합니다. 0는 CPU가 지금 계산할 식이 아니라 어셈블·링크 단계에서 채울 데이터입니다.
66행 그림 · 어셈블·링크 단계
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
.quad 0 // reserved각 값을 8바이트 크기로 파일 안에 기록합니다. 0는 CPU가 지금 계산할 식이 아니라 어셈블·링크 단계에서 채울 데이터입니다.
67행 그림 · 어셈블·링크 단계
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
.quad 0 // reserved각 값을 8바이트 크기로 파일 안에 기록합니다. 0는 CPU가 지금 계산할 식이 아니라 어셈블·링크 단계에서 채울 데이터입니다.
68행 그림 · 어셈블·링크 단계
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
.ascii ARM64_IMAGE_MAGIC // Magic number문자열 ARM64_IMAGE_MAGIC를 이미지에 넣습니다. 자동으로 끝의 0 바이트를 붙이지 않습니다.
69행 그림 · 어셈블·링크 단계
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
.long .Lpe_header_offset // Offset to the PE header.각 값을 4바이트 크기로 파일 안에 기록합니다. .Lpe_header_offset는 CPU가 지금 계산할 식이 아니라 어셈블·링크 단계에서 채울 데이터입니다.
70행 그림 · 어셈블·링크 단계
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
__EFI_PE_HEADEREFI 로더에게 줄 이미지 설명서를 생성합니다. 로더가 찾는 진입 주소·크기·권한 정보이며 CPU 함수 호출이 아닙니다.
72행 그림 · 어셈블·링크 단계
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
primary_entry: 부트 CPU의 실행 순서
.section ".idmap.text","a"이후 출력할 바이트를 어느 섹션에 넣을지 바꿉니다. 빌드 도구가 코드를 분류하는 작업이며 현재 CPU의 PC나 SP를 옮기는 줄은 아닙니다. ".idmap.text","a"
74행 그림 · 어셈블·링크 단계
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
SYM_CODE_START(primary_entry)이 위치부터 (primary_entry)라는 코드가 시작한다고 도구에 알려 줍니다. 정렬과 외부 공개 여부도 정의에 포함됩니다. 실제 작업은 아래의 명령들이 수행합니다.
85행 그림 · 어셈블·링크 단계
함수·코드 범위: primary_entry
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
bl record_mmu_state현재 EL과 SCTLR의 MMU·데이터 캐시·엔디언 상태를 확인하는 record_mmu_state를 호출합니다. 결과는 x19에 남으며, 기대한 엔디언과 다르면 MMU를 끄고 엔디언을 맞춥니다. 바로 뒤의 캐시 처리와 EL 초기화가 이 기록을 사용합니다. BL은 다음 명령의 주소를 LR(x30)에 기록하고 목적지로 분기합니다. 호출된 코드가 x0 같은 작업 레지스터를 바꿀 수 있으므로, 호출 뒤의 값은 그 코드의 반환·보존 규칙에 따릅니다.
86행 그림 · 호출과 실행 위치
함수·코드 범위: primary_entry
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 그림은 분기 명령 자체의 효과입니다. 호출된 함수가 실행된 뒤의 레지스터 값은 그 함수의 반환·보존 규칙을 따라야 합니다.
bl preserve_boot_args부트로더의 x0~x3를 boot_args에 보존하고 FDT 물리 주소 x0를 x21에도 남깁니다. 이후 C 호출이 인수 레지스터를 바꾸어도 장치 트리와 부팅 입력을 잃지 않으려는 단계입니다. MMU를 끈 진입이면 저장 영역의 캐시 무효화도 수행합니다. BL은 다음 명령의 주소를 LR(x30)에 기록하고 목적지로 분기합니다.
87행 그림 · 호출과 실행 위치
함수·코드 범위: primary_entry
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 그림은 분기 명령 자체의 효과입니다. 호출된 함수가 실행된 뒤의 레지스터 값은 그 함수의 반환·보존 규칙을 따라야 합니다.
adrp x1, early_init_stack초기 C 코드가 사용할 4KiB 스택의 상단 주소를 구합니다. early_init_stack은 링커가 스택 공간 뒤에 두는 기호이므로 뒤의 SP 설정이 낮은 주소 쪽으로 자랄 공간을 갖습니다. 여기서 얻는 것은 레이블의 주소이며, 그 위치의 메모리 내용은 아직 읽지 않습니다. ADRP가 만드는 주소는 4KiB 경계까지만입니다. 심벌의 하위 12비트가 0이면 그 자체가 심벌 주소이고, 그렇지 않으면 별도 오프셋 계산이 필요합니다.
89행 그림 · 주소·값 구분
함수·코드 범위: primary_entry
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- ADRP의 페이지 단위는 4KiB입니다.
mov sp, x1이제 스택으로 사용할 위치를 x1의 값으로 바꿉니다. 메모리 내용을 옮기는 작업은 아닙니다. 다음 저장·호출부터 새 스택 위치를 사용합니다.
90행 그림 · 값 복사
함수·코드 범위: primary_entry
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 화살표는 값의 변화를 뜻합니다. 주소값을 옮겨도 그 주소의 메모리 내용은 복사하지 않습니다.
mov x29, xzrx29에 0을 넣습니다. 원래 x29의 내용은 덮어쓰며, 다른 레지스터나 그 주소의 메모리까지 복사하지는 않습니다.
91행 그림 · 값 복사
함수·코드 범위: primary_entry
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 화살표는 값의 변화를 뜻합니다. 주소값을 옮겨도 그 주소의 메모리 내용은 복사하지 않습니다.
adrp x0, __pi_init_idmap_pg_dirMMU를 켠 직후에도 현재 코드를 같은 주소로 가져올 수 있도록 만들 초기 identity mapping의 루트 저장 공간을 x0에 전달합니다. 여기서 얻는 것은 레이블의 주소이며, 그 위치의 메모리 내용은 아직 읽지 않습니다. ADRP가 만드는 주소는 4KiB 경계까지만입니다. 심벌의 하위 12비트가 0이면 그 자체가 심벌 주소이고, 그렇지 않으면 별도 오프셋 계산이 필요합니다.
92행 그림 · 주소·값 구분
함수·코드 범위: primary_entry
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- ADRP의 페이지 단위는 4KiB입니다.
mov x1, xzr페이지 표 속성에서 추가로 지울 비트 마스크를 0으로 전달합니다. NULL 페이지 표 포인터가 아니라 create_init_idmap의 두 번째 인수입니다. MOV는 x1의 기존 값을 대체합니다. 원본이 주소값이어도 그 주소의 메모리를 읽거나 복사하지 않습니다.
93행 그림 · 값 복사
함수·코드 범위: primary_entry
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 화살표는 값의 변화를 뜻합니다. 주소값을 옮겨도 그 주소의 메모리 내용은 복사하지 않습니다.
bl __pi_create_init_idmap현재 물리 주소에서 접근할 수 있는 페이지 표에 초기 identity mapping을 만듭니다. x0는 표 공간, x1은 지울 속성 마스크이며 반환 x0는 사용한 표 영역의 끝입니다. MMU를 켜도 다음 명령과 초기 데이터에 접근하도록 준비합니다. BL은 다음 명령의 주소를 LR(x30)에 기록하고 목적지로 분기합니다. 호출된 코드가 x0 같은 작업 레지스터를 바꿀 수 있으므로, 호출 뒤의 값은 그 코드의 반환·보존 규칙에 따릅니다.
94행 그림 · 호출과 실행 위치
함수·코드 범위: primary_entry
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 그림은 분기 명령 자체의 효과입니다. 호출된 함수가 실행된 뒤의 레지스터 값은 그 함수의 반환·보존 규칙을 따라야 합니다.
cbnz x19, 0fx19가 0이 아니면 MMU와 데이터 캐시를 켠 채 들어온 경우의 clean 경로로 갑니다. 0이면 캐시를 끈 초기 표 기록 뒤에 남을 수 있는 캐시 사본을 무효화하는 경로를 사용합니다. 이 분기는 지정 레지스터의 값이나 비트를 직접 검사하며 CMP가 남긴 NZCV를 읽거나 바꾸지 않습니다. 조건이 맞지 않으면 바로 다음 명령으로 진행합니다.
101행 그림 · 조건별 실행 위치
함수·코드 범위: primary_entry
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 조건이 맞는 한 경로만 실행합니다. 이 분기 명령은 NZCV를 새로 계산하지 않습니다.
dmb sy앞뒤의 메모리 접근이 필요한 순서로 관찰되도록 제한합니다. “먼저 썼으니 다른 CPU도 먼저 볼 것”이라는 가정을 명시적인 규칙으로 만드는 줄입니다. 캐시를 직접 비우는 명령은 아닙니다.
102행 그림 · 동기화·캐시·대기 상태
함수·코드 범위: primary_entry
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
mov x1, x0 // end of used regionx1에 x0에 들어 있는 값을 넣습니다. 원래 x1의 내용은 덮어쓰며, 다른 레지스터나 그 주소의 메모리까지 복사하지는 않습니다.
103행 그림 · 값 복사
함수·코드 범위: primary_entry
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 화살표는 값의 변화를 뜻합니다. 주소값을 옮겨도 그 주소의 메모리 내용은 복사하지 않습니다.
adrp x0, __pi_init_idmap_pg_dirx0에 __pi_init_idmap_pg_dir의 위치를 계산합니다. 그 위치의 데이터는 아직 읽지 않습니다. ADRP는 4 KiB 페이지의 시작만 계산하므로 세부 위치는 다음 오프셋 연산에서 더합니다.
104행 그림 · 주소·값 구분
함수·코드 범위: primary_entry
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- ADRP의 페이지 단위는 4KiB입니다.
adr_l x2, dcache_inval_pocx2에 dcache_inval_poc의 위치를 계산합니다. 그 위치의 데이터는 아직 읽지 않습니다. 이후의 메모리 접근이나 분기가 사용할 주소를 준비하는 줄입니다.
105행 그림 · 주소·값 구분
함수·코드 범위: primary_entry
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 이 매크로의 임시 레지스터는 내부에서 사용됩니다.
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 blr x2x2에 넣은 dcache_inval_poc를 호출해 x0부터 x1 직전까지의 초기 페이지 표 캐시 사본을 무효화합니다. MMU·캐시가 꺼진 상태에서 작성한 표에 낡은 캐시 라인이 나중에 덮어써지는 문제를 피하기 위한 처리입니다. BLR은 다음 명령의 주소를 LR(x30)에 기록하고 레지스터에 든 목적지로 분기합니다. 호출된 코드가 x0 같은 작업 레지스터를 바꿀 수 있으므로, 호출 뒤의 값은 그 코드의 반환·보존 규칙에 따릅니다.
106행 그림 · 호출과 실행 위치
함수·코드 범위: primary_entry
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 그림은 분기 명령 자체의 효과입니다. 호출된 함수가 실행된 뒤의 레지스터 값은 그 함수의 반환·보존 규칙을 따라야 합니다.
b 1f1f (L119)부터 실행을 계속합니다. 돌아올 주소를 새로 적지 않으므로 일반 함수 호출과는 다른 제어 이동입니다.
107행 그림 · 호출과 실행 위치
함수·코드 범위: primary_entry
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 새 복귀 주소를 LR에 기록하지 않습니다. RET 역시 메모리의 스택 값을 자동으로 꺼내는 명령은 아닙니다.
0: adrp x0, __idmap_text_startx0에 __idmap_text_start의 위치를 계산합니다. 그 위치의 데이터는 아직 읽지 않습니다. ADRP는 4 KiB 페이지의 시작만 계산하므로 세부 위치는 다음 오프셋 연산에서 더합니다.
114행 그림 · 주소·값 구분
함수·코드 범위: primary_entry
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- ADRP의 페이지 단위는 4KiB입니다.
adr_l x1, __idmap_text_endx1에 __idmap_text_end의 위치를 계산합니다. 그 위치의 데이터는 아직 읽지 않습니다. 이후의 메모리 접근이나 분기가 사용할 주소를 준비하는 줄입니다.
115행 그림 · 주소·값 구분
함수·코드 범위: primary_entry
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 이 매크로의 임시 레지스터는 내부에서 사용됩니다.
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 adr_l x2, dcache_clean_pocx2에 dcache_clean_poc의 위치를 계산합니다. 그 위치의 데이터는 아직 읽지 않습니다. 이후의 메모리 접근이나 분기가 사용할 주소를 준비하는 줄입니다.
116행 그림 · 주소·값 구분
함수·코드 범위: primary_entry
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 이 매크로의 임시 레지스터는 내부에서 사용됩니다.
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 blr x2캐시를 켠 채 들어왔으므로 identity-mapped 코드 영역 [x0, x1)을 dcache_clean_poc로 PoC까지 정리합니다. 뒤에서 MMU·캐시 상태를 바꾸어도 필요한 코드가 메모리에서 보이게 하려는 목적입니다. BLR은 다음 명령의 주소를 LR(x30)에 기록하고 레지스터에 든 목적지로 분기합니다. 호출된 코드가 x0 같은 작업 레지스터를 바꿀 수 있으므로, 호출 뒤의 값은 그 코드의 반환·보존 규칙에 따릅니다.
117행 그림 · 호출과 실행 위치
함수·코드 범위: primary_entry
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 그림은 분기 명령 자체의 효과입니다. 호출된 함수가 실행된 뒤의 레지스터 값은 그 함수의 반환·보존 규칙을 따라야 합니다.
1: mov x0, x19x0에 x19에 들어 있는 값을 넣습니다. 원래 x0의 내용은 덮어쓰며, 다른 레지스터나 그 주소의 메모리까지 복사하지는 않습니다.
119행 그림 · 값 복사
함수·코드 범위: primary_entry
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 화살표는 값의 변화를 뜻합니다. 주소값을 옮겨도 그 주소의 메모리 내용은 복사하지 않습니다.
bl init_kernel_el // w0=cpu_boot_mode기록한 MMU 상태를 x0로 전달해 EL1 또는 EL2 진입 상태를 정리합니다. 이 함수는 필요한 제어 레지스터를 설정하고 ERET로 EL1 실행을 맞춘 뒤 x0에 출발 EL과 E2H 플래그를 남깁니다. 최종 VHE 선택은 뒤의 finalise_el2에서 합니다. BL은 다음 명령의 주소를 LR(x30)에 기록하고 목적지로 분기합니다. 호출된 코드가 x0 같은 작업 레지스터를 바꿀 수 있으므로, 호출 뒤의 값은 그 코드의 반환·보존 규칙에 따릅니다.
120행 그림 · 호출과 실행 위치
함수·코드 범위: primary_entry
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 그림은 분기 명령 자체의 효과입니다. 호출된 함수가 실행된 뒤의 레지스터 값은 그 함수의 반환·보존 규칙을 따라야 합니다.
mov x20, x0x20에 x0에 들어 있는 값을 넣습니다. 원래 x20의 내용은 덮어쓰며, 다른 레지스터나 그 주소의 메모리까지 복사하지는 않습니다.
121행 그림 · 값 복사
함수·코드 범위: primary_entry
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 화살표는 값의 변화를 뜻합니다. 주소값을 옮겨도 그 주소의 메모리 내용은 복사하지 않습니다.
bl __cpu_setup // initialise processorMMU를 켤 CPU 환경을 준비합니다. 지역 TLB를 정리하고 MAIR·TCR 등 주소 변환 속성과 접근 제어를 설정한 뒤, 나중에 SCTLR_EL1에 쓸 값을 x0로 반환합니다. 페이지 표 자체를 이 호출 하나가 전부 만드는 것은 아닙니다. BL은 다음 명령의 주소를 LR(x30)에 기록하고 목적지로 분기합니다. 호출된 코드가 x0 같은 작업 레지스터를 바꿀 수 있으므로, 호출 뒤의 값은 그 코드의 반환·보존 규칙에 따릅니다.
129행 그림 · 호출과 실행 위치
함수·코드 범위: primary_entry
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 그림은 분기 명령 자체의 효과입니다. 호출된 함수가 실행된 뒤의 레지스터 값은 그 함수의 반환·보존 규칙을 따라야 합니다.
b __primary_switch__primary_switch부터 실행을 계속합니다. 돌아올 주소를 새로 적지 않으므로 일반 함수 호출과는 다른 제어 이동입니다.
130행 그림 · 호출과 실행 위치
함수·코드 범위: primary_entry
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 새 복귀 주소를 LR에 기록하지 않습니다. RET 역시 메모리의 스택 값을 자동으로 꺼내는 명령은 아닙니다.
SYM_CODE_END(primary_entry)도구에 이 코드 범위의 끝을 알려 크기와 심볼 정보를 기록합니다. CPU가 이 줄 때문에 자동으로 반환하지는 않습니다.
131행 그림 · 어셈블·링크 단계
함수·코드 범위: primary_entry
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 끝 표기 자체가 RET나 분기를 실행하지는 않습니다.
record_mmu_state: 엔디언, M, C와 NZCV
__INIT부팅 초기화용 코드 영역으로 옮깁니다. 이 분류는 나중에 초기화 메모리를 관리할 때 사용하며, 지금 CPU가 점프하는 명령은 아닙니다.
133행 그림 · 어셈블·링크 단계
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
SYM_CODE_START_LOCAL(record_mmu_state)이 위치부터 (record_mmu_state)라는 코드가 시작한다고 도구에 알려 줍니다. 정렬과 외부 공개 여부도 정의에 포함됩니다. 실제 작업은 아래의 명령들이 수행합니다.
134행 그림 · 어셈블·링크 단계
함수·코드 범위: record_mmu_state
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
mrs x19, CurrentELCurrentEL의 EL 필드를 읽어 지금 EL1인지 EL2인지 판별합니다. 읽어야 할 SCTLR이 어느 레벨 것인지 선택하기 위한 정보이며 CPU 번호가 아닙니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.
135행 그림 · 시스템 레지스터 읽기
함수·코드 범위: record_mmu_state
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 일반 RAM의 메모리 접근과 구분합니다.
cmp x19, #CurrentEL_EL2x19와 CurrentEL_EL2를 비교하여 조건 플래그에 결과를 남깁니다. 비교한 두 값은 그대로이며, 뒤의 조건 분기가 이 결과를 읽습니다.
136행 그림 · 조건 플래그 계산
함수·코드 범위: record_mmu_state
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 일반 레지스터에 연산 결과를 쓰지 않고 조건 플래그만 바꿉니다.
- N은 결과의 부호 비트, Z는 결과가 0인지 나타냅니다. C는 빌림이 없으면 1, V는 부호 있는 뺄셈의 오버플로를 나타냅니다.
mrs x19, sctlr_el1우선 EL1의 SCTLR을 읽습니다. SCTLR은 MMU 활성 M, 데이터 캐시 C, 데이터 엔디언 EE 같은 실행 환경을 담습니다. 현재 EL2라면 뒤에서 SCTLR_EL2 값으로 교체합니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.
137행 그림 · 시스템 레지스터 읽기
함수·코드 범위: record_mmu_state
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 일반 RAM의 메모리 접근과 구분합니다.
b.ne 0f앞서 계산한 조건 플래그가 Z=0이면 0f (L140)로 갑니다. 사이에 플래그를 바꾸지 않는 명령이 있어도 비교 결과는 남아 있습니다.
138행 그림 · 조건별 실행 위치
함수·코드 범위: record_mmu_state
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 조건이 맞는 한 경로만 실행합니다. 이 분기 명령은 NZCV를 새로 계산하지 않습니다.
mrs x19, sctlr_el2EL2에서 진입한 경우 실제 실행에 적용 중인 SCTLR_EL2를 읽어 x19에 둡니다. EL1 레지스터만 검사하면 부트로더가 실제로 사용한 MMU 상태를 잘못 기록할 수 있습니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.
139행 그림 · 시스템 레지스터 읽기
함수·코드 범위: record_mmu_state
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 일반 RAM의 메모리 접근과 구분합니다.
0:여기에 0라는 위치 이름을 붙입니다. 다른 줄의 분기나 주소 계산이 이 위치를 찾아올 수 있습니다. 이름 자체가 CPU 작업을 추가하지는 않습니다.
140행 그림 · 위치에 이름 붙이기
함수·코드 범위: record_mmu_state
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
CPU_LE( tbnz x19, #SCTLR_ELx_EE_SHIFT, 1f )리틀 엔디언 커널에서만 생성되는 검사입니다. 현재 SCTLR의 EE가 1이면 CPU 데이터 바이트 순서가 커널과 달라 수정 경로로 분기합니다. CPU_LE 자체는 빌드 단계의 선택이고, 포함된 TBNZ는 실행 중 EE 비트를 검사합니다.
141행 그림 · 빌드의 엔디언 선택
함수·코드 범위: record_mmu_state
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 선택된 TBZ/TBNZ만 실행 중 레지스터의 비트를 검사합니다.
CPU_BE( tbz x19, #SCTLR_ELx_EE_SHIFT, 1f )빅 엔디언 커널에서만 생성되는 검사입니다. EE가 0이면 커널이 요구한 데이터 바이트 순서와 다르므로 수정 경로로 갑니다. CPU_BE 자체는 빌드 단계의 선택이고, 포함된 TBZ는 실행 중 EE 비트를 검사합니다.
142행 그림 · 빌드의 엔디언 선택
함수·코드 범위: record_mmu_state
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 선택된 TBZ/TBNZ만 실행 중 레지스터의 비트를 검사합니다.
tst x19, #SCTLR_ELx_C // Z := (C == 0)SCTLR.C로 데이터 캐시가 활성 상태였는지 검사합니다. C가 0일 때 Z 플래그를 세워 뒤의 CSEL이 부팅 MMU 기록을 0으로 만들도록 합니다. TST는 두 피연산자의 비트 AND 결과로 조건 플래그를 갱신하며 원래 레지스터 값은 바꾸지 않습니다.
143행 그림 · 조건 플래그 계산
함수·코드 범위: record_mmu_state
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 일반 레지스터에 연산 결과를 쓰지 않고 조건 플래그만 바꿉니다.
- N은 결과의 부호 비트, Z는 결과가 0인지 나타냅니다. 이 AArch64 TST는 C와 V를 0으로 만듭니다.
and x19, x19, #SCTLR_ELx_M // isolate M bitSCTLR 전체 설정에서 MMU 활성 M 비트만 남깁니다. x19를 이후의 초기 캐시 처리에서 쓰는 짧은 부팅 상태 기록으로 바꾸는 단계입니다. AND는 두 피연산자의 같은 위치 비트가 모두 1인 곳만 결과에 남깁니다.
144행 그림 · 레지스터 상태
함수·코드 범위: record_mmu_state
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 결과는 목적 레지스터 폭에 맞습니다. 이 형식은 NZCV를 바꾸지 않습니다.
csel x19, xzr, x19, eq // clear x19 if Z앞서 캐시가 꺼져 있으면 x19를 0으로, 켜져 있으면 M 비트 값을 남깁니다. 따라서 이후 x19가 0이 아닌 조건은 M과 C가 모두 켜진 진입 상태입니다. CSEL은 NZCV 조건에 따라 두 값 중 하나를 선택하며 실행 위치를 분기하거나 조건 플래그를 갱신하지 않습니다.
145행 그림 · 조건에 따른 값 선택
함수·코드 범위: record_mmu_state
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 두 경우 중 하나만 선택합니다. NZCV와 실행 순서는 이 명령으로 바뀌지 않습니다.
retLR(x30)에 적힌 주소로 실행을 돌립니다. 그 주소가 어떻게 준비됐는지가 중요합니다. 정상 함수의 호출자일 수도 있고, 절전 복귀나 trampoline에서 별도로 기록한 재개 지점일 수도 있습니다.
146행 그림 · 호출과 실행 위치
함수·코드 범위: record_mmu_state
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 새 복귀 주소를 LR에 기록하지 않습니다. RET 역시 메모리의 스택 값을 자동으로 꺼내는 명령은 아닙니다.
1: eor x19, x19, #SCTLR_ELx_EE마스크에서 1인 비트를 반전합니다. 엔디언 비트처럼 현재 상태를 반대로 바꿀 때 사용할 수 있습니다. 여기서 결과를 받는 레지스터는 x19입니다.
154행 그림 · 레지스터 상태
함수·코드 범위: record_mmu_state
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 결과는 목적 레지스터 폭에 맞습니다. 이 형식은 NZCV를 바꾸지 않습니다.
bic x19, x19, #SCTLR_ELx_M지정 마스크에 해당하는 비트를 지웁니다. 나머지 설정을 유지하며 특정 기능만 끌 수 있습니다. 여기서 결과를 받는 레지스터는 x19입니다.
155행 그림 · 레지스터 상태
함수·코드 범위: record_mmu_state
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 결과는 목적 레지스터 폭에 맞습니다. 이 형식은 NZCV를 바꾸지 않습니다.
b.ne 2f앞서 계산한 조건 플래그가 Z=0이면 2f (L160)로 갑니다. 사이에 플래그를 바꾸지 않는 명령이 있어도 비교 결과는 남아 있습니다.
156행 그림 · 조건별 실행 위치
함수·코드 범위: record_mmu_state
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 조건이 맞는 한 경로만 실행합니다. 이 분기 명령은 NZCV를 새로 계산하지 않습니다.
pre_disable_mmu_workaroundQualcomm Falkor erratum E1041 우회 빌드에서는 MMU를 끄는 전환에 필요한 ISB를 넣습니다. 설정이 없으면 빈 매크로입니다. SCTLR의 M 비트를 1에서 0으로 바꾸는 MSR 바로 앞에서 실행 순서를 맞추는 CPU 구현상의 보완입니다.
157행 그림 · 동기화·캐시·대기 상태
함수·코드 범위: record_mmu_state
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
pre_disable_mmu_workaround 매크로 본문 · 647행
arch/arm64/include/asm/assembler.h · Linux v6.18.37
정의 선택 조건: #ifndef __ASM_ASSEMBLER_H
.macro pre_disable_mmu_workaround
#ifdef CONFIG_QCOM_FALKOR_ERRATUM_E1041
isb
#endif
.endm msr sctlr_el2, x19EE를 커널 엔디언으로 맞추고 M을 지운 SCTLR 값을 EL2에 씁니다. EL2 진입에서 바이트 순서와 MMU 해석을 함께 정리하며 뒤의 ISB가 새 실행 환경을 반영합니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.
158행 그림 · 시스템 레지스터 쓰기
함수·코드 범위: record_mmu_state
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 접근 권한과 VHE 별칭 규칙에 따라 실제 대상이 정해집니다. 뒤의 ISB·DSB가 필요한지는 이 레지스터와 문맥에 따릅니다.
b 3f3f (L162)부터 실행을 계속합니다. 돌아올 주소를 새로 적지 않으므로 일반 함수 호출과는 다른 제어 이동입니다.
159행 그림 · 호출과 실행 위치
함수·코드 범위: record_mmu_state
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 새 복귀 주소를 LR에 기록하지 않습니다. RET 역시 메모리의 스택 값을 자동으로 꺼내는 명령은 아닙니다.
2: pre_disable_mmu_workaroundQualcomm Falkor erratum E1041 우회 빌드에서는 MMU를 끄는 전환에 필요한 ISB를 넣습니다. 설정이 없으면 빈 매크로입니다. SCTLR의 M 비트를 1에서 0으로 바꾸는 MSR 바로 앞에서 실행 순서를 맞추는 CPU 구현상의 보완입니다.
160행 그림 · 동기화·캐시·대기 상태
함수·코드 범위: record_mmu_state
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
pre_disable_mmu_workaround 매크로 본문 · 647행
arch/arm64/include/asm/assembler.h · Linux v6.18.37
정의 선택 조건: #ifndef __ASM_ASSEMBLER_H
.macro pre_disable_mmu_workaround
#ifdef CONFIG_QCOM_FALKOR_ERRATUM_E1041
isb
#endif
.endm msr sctlr_el1, x19EL1에서 들어온 경우 수정한 SCTLR_EL1을 씁니다. 여기서는 커널과 맞는 엔디언으로 바꾸면서 MMU를 끈 상태를 만들고 이후 x19에도 꺼진 상태를 기록합니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.
161행 그림 · 시스템 레지스터 쓰기
함수·코드 범위: record_mmu_state
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 접근 권한과 VHE 별칭 규칙에 따라 실제 대상이 정해집니다. 뒤의 ISB·DSB가 필요한지는 이 레지스터와 문맥에 따릅니다.
3: isb뒤 명령을 변경된 CPU 제어 상태에 맞춰 실행하도록 동기화합니다. 주소 변환이나 벡터 설정을 바꾼 뒤 예전 실행 상태가 계속 쓰이지 않게 합니다.
162행 그림 · 동기화·캐시·대기 상태
함수·코드 범위: record_mmu_state
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
mov x19, xzrx19에 0을 넣습니다. 원래 x19의 내용은 덮어쓰며, 다른 레지스터나 그 주소의 메모리까지 복사하지는 않습니다.
163행 그림 · 값 복사
함수·코드 범위: record_mmu_state
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 화살표는 값의 변화를 뜻합니다. 주소값을 옮겨도 그 주소의 메모리 내용은 복사하지 않습니다.
retLR(x30)에 적힌 주소로 실행을 돌립니다. 그 주소가 어떻게 준비됐는지가 중요합니다. 정상 함수의 호출자일 수도 있고, 절전 복귀나 trampoline에서 별도로 기록한 재개 지점일 수도 있습니다.
164행 그림 · 호출과 실행 위치
함수·코드 범위: record_mmu_state
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 새 복귀 주소를 LR에 기록하지 않습니다. RET 역시 메모리의 스택 값을 자동으로 꺼내는 명령은 아닙니다.
SYM_CODE_END(record_mmu_state)도구에 이 코드 범위의 끝을 알려 크기와 심볼 정보를 기록합니다. CPU가 이 줄 때문에 자동으로 반환하지는 않습니다.
165행 그림 · 어셈블·링크 단계
함수·코드 범위: record_mmu_state
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 끝 표기 자체가 RET나 분기를 실행하지는 않습니다.
preserve_boot_args: 32바이트 저장과 tail call
SYM_CODE_START_LOCAL(preserve_boot_args)이 위치부터 (preserve_boot_args)라는 코드가 시작한다고 도구에 알려 줍니다. 정렬과 외부 공개 여부도 정의에 포함됩니다. 실제 작업은 아래의 명령들이 수행합니다.
170행 그림 · 어셈블·링크 단계
함수·코드 범위: preserve_boot_args
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
mov x21, x0 // x21=FDTx21에 x0에 들어 있는 값을 넣습니다. 원래 x21의 내용은 덮어쓰며, 다른 레지스터나 그 주소의 메모리까지 복사하지는 않습니다.
171행 그림 · 값 복사
함수·코드 범위: preserve_boot_args
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 화살표는 값의 변화를 뜻합니다. 주소값을 옮겨도 그 주소의 메모리 내용은 복사하지 않습니다.
adr_l x0, boot_args // record the contents ofx0에 boot_args의 위치를 계산합니다. 그 위치의 데이터는 아직 읽지 않습니다. 이후의 메모리 접근이나 분기가 사용할 주소를 준비하는 줄입니다.
173행 그림 · 주소·값 구분
함수·코드 범위: preserve_boot_args
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 이 매크로의 임시 레지스터는 내부에서 사용됩니다.
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 stp x21, x1, [x0] // x0 .. x3 at kernel entryx21, x1의 값을 x0가 가리키는 메모리 위치에 기록합니다. 레지스터 값을 나중에 다시 쓰거나 다른 코드가 읽을 수 있도록 메모리에 남기는 줄입니다. 첫 위치에는 x21, 그보다 8바이트 높은 위치에는 x1의 값이 들어갑니다.
174행 그림 · 메모리 쓰기
함수·코드 범위: preserve_boot_args
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 주소 계산에 의한 기준 레지스터 갱신은 없습니다. 다만 LDR/LDP의 목적 레지스터가 기준 레지스터와 같으면 읽은 값이 그 레지스터를 덮어씁니다.
stp x2, x3, [x0, #16]x2, x3의 값을 x0를 기준으로 16만큼 계산한 메모리 위치에 기록합니다. 레지스터 값을 나중에 다시 쓰거나 다른 코드가 읽을 수 있도록 메모리에 남기는 줄입니다. 첫 위치에는 x2, 그보다 8바이트 높은 위치에는 x3의 값이 들어갑니다.
175행 그림 · 메모리 쓰기
함수·코드 범위: preserve_boot_args
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 주소 계산에 의한 기준 레지스터 갱신은 없습니다. 다만 LDR/LDP의 목적 레지스터가 기준 레지스터와 같으면 읽은 값이 그 레지스터를 덮어씁니다.
cbnz x19, 0f // skip cache invalidation if MMU is onx19의 값가 0이 아니면 0f (L183)로 갑니다. 조건을 만족하지 않으면 바로 다음 명령을 읽습니다. 이 분기는 CMP의 결과가 아니라 지정 레지스터를 직접 검사합니다.
177행 그림 · 조건별 실행 위치
함수·코드 범위: preserve_boot_args
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 조건이 맞는 한 경로만 실행합니다. 이 분기 명령은 NZCV를 새로 계산하지 않습니다.
dmb sy // needed before dc ivac with앞뒤의 메모리 접근이 필요한 순서로 관찰되도록 제한합니다. “먼저 썼으니 다른 CPU도 먼저 볼 것”이라는 가정을 명시적인 규칙으로 만드는 줄입니다. 캐시를 직접 비우는 명령은 아닙니다.
178행 그림 · 동기화·캐시·대기 상태
함수·코드 범위: preserve_boot_args
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
add x1, x0, #0x20 // 4 x 8 bytesx0를 기준으로 0x20을 더해 x1에 결과를 둡니다. 주소 계산에 쓰이는 경우에는 구조체의 필드나 다음 저장 위치를 찾는 동작입니다. 이 형태는 조건 플래그를 바꾸지 않습니다.
181행 그림 · 레지스터 상태
함수·코드 범위: preserve_boot_args
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 결과는 목적 레지스터 폭에 맞습니다. 이 형식은 NZCV를 바꾸지 않습니다.
b dcache_inval_poc // tail calldcache_inval_poc부터 실행을 계속합니다. 돌아올 주소를 새로 적지 않으므로 일반 함수 호출과는 다른 제어 이동입니다.
182행 그림 · 호출과 실행 위치
함수·코드 범위: preserve_boot_args
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 새 복귀 주소를 LR에 기록하지 않습니다. RET 역시 메모리의 스택 값을 자동으로 꺼내는 명령은 아닙니다.
0: str_l x19, mmu_enabled_at_boot, x0진입 때 MMU와 데이터 캐시가 켜졌는지 기록한 x19를 mmu_enabled_at_boot에 보존합니다. 뒤의 초기화 코드가 부트로더가 남긴 실행 상태를 판단할 수 있도록 전역 자료로 남기며 x0는 주소 계산용입니다. str_l은 임시 레지스터로 심벌의 주소를 구하고 STR로 그 메모리에 값을 쓰는 매크로입니다.
183행 그림 · 주소·값 구분
함수·코드 범위: preserve_boot_args
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 주소 계산에 쓰는 임시 레지스터도 바뀝니다.
str_l 매크로 본문 · 203행
arch/arm64/include/asm/assembler.h · Linux v6.18.37
정의 선택 조건: #ifndef __ASM_ASSEMBLER_H
.macro str_l, src, sym, tmp
adrp \tmp, \sym
str \src, [\tmp, :lo12:\sym]
.endm retLR(x30)에 적힌 주소로 실행을 돌립니다. 그 주소가 어떻게 준비됐는지가 중요합니다. 정상 함수의 호출자일 수도 있고, 절전 복귀나 trampoline에서 별도로 기록한 재개 지점일 수도 있습니다.
184행 그림 · 호출과 실행 위치
함수·코드 범위: preserve_boot_args
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 새 복귀 주소를 LR에 기록하지 않습니다. RET 역시 메모리의 스택 값을 자동으로 꺼내는 명령은 아닙니다.
SYM_CODE_END(preserve_boot_args)도구에 이 코드 범위의 끝을 알려 크기와 심볼 정보를 기록합니다. CPU가 이 줄 때문에 자동으로 반환하지는 않습니다.
185행 그림 · 어셈블·링크 단계
함수·코드 범위: preserve_boot_args
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 끝 표기 자체가 RET나 분기를 실행하지는 않습니다.
init_cpu_task: current, 스택, 프레임 끝, per-CPU
.macro init_cpu_task tsk, tmp1, tmp2task 하나를 이 CPU의 실행 주체로 설치하는 매크로를 정의합니다. tsk는 task_struct 주소이고 tmp1·tmp2는 임시 레지스터입니다. current 식별, 일반 스택, unwind 종료 표식, SCS, per-CPU 기준을 한 묶음으로 준비합니다. 이 줄은 어셈블러 매크로 정의의 시작입니다. 본문은 매크로를 사용하는 위치에 펼쳐지며, 정의 자체가 런타임 함수 호출은 아닙니다.
195행 그림 · 어셈블·링크 단계
매크로 본문: init_cpu_task
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 뒤의 본문은 이 이름을 사용하는 위치에 펼쳐집니다. 정의 위치를 CPU가 함수처럼 호출하지 않습니다.
msr sp_el0, \tskSP_EL0에 task_struct 주소를 기록하여 커널 실행 중 current를 찾을 수 있게 합니다. 이 시점의 SP_EL0는 사용자 스택을 복원하는 용도가 아니며 실제 커널 SP는 다음 명령들에서 별도로 정합니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.
196행 그림 · 시스템 레지스터 쓰기
매크로 본문: init_cpu_task
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 접근 권한과 VHE 별칭 규칙에 따라 실제 대상이 정해집니다. 뒤의 ISB·DSB가 필요한지는 이 레지스터와 문맥에 따릅니다.
- 매크로 init_cpu_task의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
ldr \tmp1, [\tsk, #TSK_STACK]task_struct의 stack 포인터를 읽습니다. TSK_STACK은 asm-offsets가 C 구조체 배치에서 생성한 바이트 offset이므로 고정 숫자를 손으로 가정하지 않습니다. 이 주소 형식은 기준 레지스터 \tsk 자체를 갱신하지 않습니다.
198행 그림 · 메모리 읽기
매크로 본문: init_cpu_task
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 주소 계산에 의한 기준 레지스터 갱신은 없습니다. 다만 LDR/LDP의 목적 레지스터가 기준 레지스터와 같으면 읽은 값이 그 레지스터를 덮어씁니다.
- 매크로 init_cpu_task의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
add sp, \tmp1, #THREAD_SIZE할당된 커널 스택 시작에 THREAD_SIZE를 더해 높은 주소의 끝을 SP로 잡습니다. AArch64 스택은 이후 저장 때 낮은 주소 방향으로 내려갑니다. 이 명령은 SP의 주소값을 조정하며, 확보하거나 반환하는 스택 공간의 데이터를 직접 저장하거나 지우지는 않습니다. 이 ADD 형식은 NZCV 조건 플래그를 바꾸지 않습니다.
199행 그림 · 레지스터 상태
매크로 본문: init_cpu_task
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 결과는 목적 레지스터 폭에 맞습니다. 이 형식은 NZCV를 바꾸지 않습니다.
- SP는 주소만 바뀝니다. 스택 데이터 저장·복원은 STR/STP·LDR/LDP 같은 별도 메모리 명령이 수행합니다.
- 매크로 init_cpu_task의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
sub sp, sp, #PT_REGS_SIZE최상단에서 pt_regs 크기를 미리 빼 예외 상태와 종료 프레임 메타데이터가 들어갈 공간을 확보합니다. 이 명령은 SP의 주소값을 조정하며, 확보하거나 반환하는 스택 공간의 데이터를 직접 저장하거나 지우지는 않습니다. 이 SUB 형식은 NZCV 조건 플래그를 바꾸지 않습니다.
200행 그림 · 레지스터 상태
매크로 본문: init_cpu_task
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 결과는 목적 레지스터 폭에 맞습니다. 이 형식은 NZCV를 바꾸지 않습니다.
- SP는 주소만 바뀝니다. 스택 데이터 저장·복원은 STR/STP·LDR/LDP 같은 별도 메모리 명령이 수행합니다.
- 매크로 init_cpu_task의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
stp xzr, xzr, [sp, #S_STACKFRAME]xzr, xzr의 값을 sp를 기준으로 S_STACKFRAME만큼 계산한 메모리 위치에 기록합니다. 레지스터 값을 나중에 다시 쓰거나 다른 코드가 읽을 수 있도록 메모리에 남기는 줄입니다. 첫 위치에는 xzr, 그보다 8바이트 높은 위치에는 xzr의 값이 들어갑니다. 이 스택 위치는 현재 함수 또는 예외 처리의 보관 공간입니다.
202행 그림 · 메모리 쓰기
매크로 본문: init_cpu_task
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 주소 계산에 의한 기준 레지스터 갱신은 없습니다. 다만 LDR/LDP의 목적 레지스터가 기준 레지스터와 같으면 읽은 값이 그 레지스터를 덮어씁니다.
- 메모리 칸은 낮은 주소부터 나열했습니다. SP를 줄이면 더 낮은 주소의 공간을 사용합니다. 화면의 아래쪽이 곧 낮은 주소라는 뜻은 아닙니다.
- 매크로 init_cpu_task의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
mov \tmp1, #FRAME_META_TYPE_FINALFRAME_META_TYPE_FINAL은 스택 추적의 끝임을 표시하는 메타데이터 종류입니다. 일반적인 이전 FP·LR 연결을 계속 따라가면 안 되는 초기 task 스택이기 때문에 이 값을 사용합니다. MOV는 \tmp1의 기존 값을 대체합니다. 원본이 주소값이어도 그 주소의 메모리를 읽거나 복사하지 않습니다.
203행 그림 · 값 복사
매크로 본문: init_cpu_task
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 화살표는 값의 변화를 뜻합니다. 주소값을 옮겨도 그 주소의 메모리 내용은 복사하지 않습니다.
- 매크로 init_cpu_task의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
str \tmp1, [sp, #S_STACKFRAME_TYPE]\tmp1의 값을 sp를 기준으로 S_STACKFRAME_TYPE만큼 계산한 메모리 위치에 기록합니다. 레지스터 값을 나중에 다시 쓰거나 다른 코드가 읽을 수 있도록 메모리에 남기는 줄입니다. 이 스택 위치는 현재 함수 또는 예외 처리의 보관 공간입니다.
204행 그림 · 메모리 쓰기
매크로 본문: init_cpu_task
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 주소 계산에 의한 기준 레지스터 갱신은 없습니다. 다만 LDR/LDP의 목적 레지스터가 기준 레지스터와 같으면 읽은 값이 그 레지스터를 덮어씁니다.
- 메모리 칸은 낮은 주소부터 나열했습니다. SP를 줄이면 더 낮은 주소의 공간을 사용합니다. 화면의 아래쪽이 곧 낮은 주소라는 뜻은 아닙니다.
- 매크로 init_cpu_task의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
add x29, sp, #S_STACKFRAMEsp를 기준으로 S_STACKFRAME을 더해 x29에 결과를 둡니다. 주소 계산에 쓰이는 경우에는 구조체의 필드나 다음 저장 위치를 찾는 동작입니다. 이 형태는 조건 플래그를 바꾸지 않습니다.
205행 그림 · 레지스터 상태
매크로 본문: init_cpu_task
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 결과는 목적 레지스터 폭에 맞습니다. 이 형식은 NZCV를 바꾸지 않습니다.
- 매크로 init_cpu_task의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
scs_load_current태스크에 저장해 둔 반환 주소 보호용 스택 위치를 되살립니다. 일반 SP를 읽는 것과는 다른 상태입니다. 여기의 인자는 없음입니다.
207행 그림 · 매크로 전체의 상태 변화
매크로 본문: init_cpu_task
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- CONFIG_SHADOW_CALL_STACK이 없으면 빈 매크로입니다.
- 매크로 init_cpu_task의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
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]
.endmscs_load_current 매크로 본문 · 22행
arch/arm64/include/asm/scs.h · Linux v6.18.37
정의 선택 조건: #ifndef _ASM_SCS_H / #ifdef __ASSEMBLY__ / 앞 조건의 반대
.macro scs_load_current
.endm adr_l \tmp1, __per_cpu_offset각 논리 CPU가 사용할 per-CPU 영역까지의 거리 표 __per_cpu_offset을 구합니다. 모든 CPU가 같은 전역 이름으로 자기 자료를 찾게 해 주는 기준 표입니다. 여기서 얻는 것은 레이블의 주소이며, 그 위치의 메모리 내용은 아직 읽지 않습니다. adr_l 매크로는 ADRP와 ADD로 주소의 페이지 부분과 하위 오프셋을 합칩니다.
209행 그림 · 주소·값 구분
매크로 본문: init_cpu_task
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 이 매크로의 임시 레지스터는 내부에서 사용됩니다.
- 매크로 init_cpu_task의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
adr_l 매크로 본문 · 175행
arch/arm64/include/asm/assembler.h · Linux v6.18.37
정의 선택 조건: #ifndef __ASM_ASSEMBLER_H
.macro adr_l, dst, sym
adrp \dst, \sym
add \dst, \dst, :lo12:\sym
.endm ldr w\tmp2, [\tsk, #TSK_TI_CPU]task의 thread_info에 기록된 논리 CPU 번호를 읽습니다. MPIDR 하드웨어 ID와 같은 값이라고 가정하지 않습니다. 실제로 \tsk + (TSK_TI_CPU) 주소의 메모리 4바이트를 w\tmp2에 읽습니다. 이 주소 형식은 기준 레지스터 \tsk 자체를 갱신하지 않습니다.
210행 그림 · 메모리 읽기
매크로 본문: init_cpu_task
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 주소 계산에 의한 기준 레지스터 갱신은 없습니다. 다만 LDR/LDP의 목적 레지스터가 기준 레지스터와 같으면 읽은 값이 그 레지스터를 덮어씁니다.
- 매크로 init_cpu_task의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
ldr \tmp1, [\tmp1, \tmp2, lsl #3]CPU 번호에 8을 곱해 unsigned long 배열에서 이 CPU의 per-CPU offset을 읽습니다. 그 결과를 다음 set_this_cpu_offset이 접근 기준으로 설치합니다. 실제로 \tmp1 + (\tmp2 × 8) 주소의 메모리 8바이트를 \tmp1에 읽습니다.
211행 그림 · 메모리 읽기
매크로 본문: init_cpu_task
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 주소 계산에 의한 기준 레지스터 갱신은 없습니다. 다만 LDR/LDP의 목적 레지스터가 기준 레지스터와 같으면 읽은 값이 그 레지스터를 덮어씁니다.
- 매크로 init_cpu_task의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
set_this_cpu_offset \tmp1현재 CPU에 속한 변수들에 접근할 기준 오프셋을 기록합니다. 여러 CPU가 같은 변수 이름을 사용해도 서로 다른 저장 공간을 찾게 합니다.
212행 그림 · 매크로 전체의 상태 변화
매크로 본문: init_cpu_task
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- VHE 사용 여부에 맞춰 실제로 쓰는 시스템 레지스터가 선택됩니다.
- 매크로 init_cpu_task의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
set_this_cpu_offset 매크로 본문 · 224행
arch/arm64/include/asm/assembler.h · Linux v6.18.37
정의 선택 조건: #ifndef __ASM_ASSEMBLER_H / 앞 조건의 반대
.macro set_this_cpu_offset, src
alternative_if_not ARM64_HAS_VIRT_HOST_EXTN
msr tpidr_el1, \src
alternative_else
msr tpidr_el2, \src
alternative_endif
.endm .endm공통 작업의 정의를 여기서 마칩니다. RET가 아니라 빌드 도구가 읽는 경계입니다.
213행 그림 · 어셈블·링크 단계
매크로 본문: init_cpu_task
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 끝 표기 자체가 RET나 분기를 실행하지는 않습니다.
__primary_switched: 최종 가상 주소에서 start_kernel까지
SYM_FUNC_START_LOCAL(__primary_switched)이 위치부터 (__primary_switched)라는 코드가 시작한다고 도구에 알려 줍니다. 정렬과 외부 공개 여부도 정의에 포함됩니다. ARM64 함수 시작 매크로는 간접 호출의 도착점을 표시하는 BTI도 추가합니다.
220행 그림 · 어셈블·링크 단계
함수·코드 범위: __primary_switched
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 함수 시작 매크로는 심벌·정렬 정보와 BTI 진입 명령 등을 배치할 수 있습니다. 생성된 명령의 실행과 심벌 선언을 구분합니다.
adr_l x4, init_taskx4에 init_task의 위치를 계산합니다. 그 위치의 데이터는 아직 읽지 않습니다. 이후의 메모리 접근이나 분기가 사용할 주소를 준비하는 줄입니다.
221행 그림 · 주소·값 구분
함수·코드 범위: __primary_switched
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 이 매크로의 임시 레지스터는 내부에서 사용됩니다.
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 init_cpu_task x4, x5, x6CPU가 사용할 태스크, 커널 스택, 호출 프레임 끝, CPU별 변수 접근 기준을 준비합니다. C 코드를 실행하기 위한 주변 상태를 맞추는 단계입니다.
222행 그림 · 매크로 전체의 주요 상태
함수·코드 범위: __primary_switched
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- SP_EL0는 task 포인터이고 실제 스택 주소는 SP입니다. SCS 사용 시 x18도 읽어 옵니다. 인자로 준 임시 레지스터의 중간값은 본문에 있습니다.
- 여러 명령과 조건부 경로를 묶은 그림입니다. 이 목록이 모든 임시 레지스터의 변경을 열거하는 것은 아닙니다. 아래 매크로 본문에서 각 단계를 확인하실 수 있습니다.
init_cpu_task 매크로 본문 · 195행
arch/arm64/kernel/head.S · Linux v6.18.37
.macro init_cpu_task tsk, tmp1, tmp2
msr sp_el0, \tsk
ldr \tmp1, [\tsk, #TSK_STACK]
add sp, \tmp1, #THREAD_SIZE
sub sp, sp, #PT_REGS_SIZE
stp xzr, xzr, [sp, #S_STACKFRAME]
mov \tmp1, #FRAME_META_TYPE_FINAL
str \tmp1, [sp, #S_STACKFRAME_TYPE]
add x29, sp, #S_STACKFRAME
scs_load_current
adr_l \tmp1, __per_cpu_offset
ldr w\tmp2, [\tsk, #TSK_TI_CPU]
ldr \tmp1, [\tmp1, \tmp2, lsl #3]
set_this_cpu_offset \tmp1
.endm adr_l x8, vectors // load VBAR_EL1 with virtualx8에 vectors의 위치를 계산합니다. 그 위치의 데이터는 아직 읽지 않습니다. 이후의 메모리 접근이나 분기가 사용할 주소를 준비하는 줄입니다.
224행 그림 · 주소·값 구분
함수·코드 범위: __primary_switched
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 이 매크로의 임시 레지스터는 내부에서 사용됩니다.
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 msr vbar_el1, x8 // vector table address예외가 발생할 때 CPU가 사용할 커널 벡터 표의 가상 주소를 VBAR_EL1에 설치합니다. 현재는 최종 커널 매핑으로 옮겨 왔으므로 물리 주소가 아니라 정상 커널 주소의 vectors를 사용합니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.
225행 그림 · 시스템 레지스터 쓰기
함수·코드 범위: __primary_switched
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 접근 권한과 VHE 별칭 규칙에 따라 실제 대상이 정해집니다. 뒤의 ISB·DSB가 필요한지는 이 레지스터와 문맥에 따릅니다.
isb뒤 명령을 변경된 CPU 제어 상태에 맞춰 실행하도록 동기화합니다. 주소 변환이나 벡터 설정을 바꾼 뒤 예전 실행 상태가 계속 쓰이지 않게 합니다.
226행 그림 · 동기화·캐시·대기 상태
함수·코드 범위: __primary_switched
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
stp x29, x30, [sp, #-16]!x29, x30의 값을 sp를 기준으로 -16만큼 계산한 메모리 위치에 기록합니다. 레지스터 값을 나중에 다시 쓰거나 다른 코드가 읽을 수 있도록 메모리에 남기는 줄입니다. 첫 위치에는 x29, 그보다 8바이트 높은 위치에는 x30의 값이 들어갑니다. 이 스택 위치는 현재 함수 또는 예외 처리의 보관 공간입니다. 쓰기 전에 기준 주소를 갱신합니다. SP의 음수 오프셋이면 낮은 주소 쪽에 저장 공간을 확보하는 뜻입니다.
228행 그림 · 메모리 쓰기
함수·코드 범위: __primary_switched
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- !는 전위 갱신입니다. 오프셋을 더한 주소로 접근하고 그 주소를 기준 레지스터에도 남깁니다.
- 메모리 칸은 낮은 주소부터 나열했습니다. SP를 줄이면 더 낮은 주소의 공간을 사용합니다. 화면의 아래쪽이 곧 낮은 주소라는 뜻은 아닙니다.
mov x29, sp현재 SP의 숫자를 x29에 기억해 둡니다. 스택 자체는 이동하거나 복사되지 않습니다. 나중에 이 값으로 원래 위치를 찾을 수 있습니다.
229행 그림 · 값 복사
함수·코드 범위: __primary_switched
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 화살표는 값의 변화를 뜻합니다. 주소값을 옮겨도 그 주소의 메모리 내용은 복사하지 않습니다.
str_l x21, __fdt_pointer, x5 // Save FDT pointer보존해 둔 FDT 물리 주소 x21을 __fdt_pointer에 기록합니다. 뒤의 setup_arch가 장치 트리를 매핑하고 해석할 입력입니다. str_l은 임시 레지스터로 심벌의 주소를 구하고 STR로 그 메모리에 값을 쓰는 매크로입니다.
231행 그림 · 주소·값 구분
함수·코드 범위: __primary_switched
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 주소 계산에 쓰는 임시 레지스터도 바뀝니다.
str_l 매크로 본문 · 203행
arch/arm64/include/asm/assembler.h · Linux v6.18.37
정의 선택 조건: #ifndef __ASM_ASSEMBLER_H
.macro str_l, src, sym, tmp
adrp \tmp, \sym
str \src, [\tmp, :lo12:\sym]
.endm adrp x4, _text // Save the offset betweenx4에 _text의 위치를 계산합니다. 그 위치의 데이터는 아직 읽지 않습니다. ADRP는 4 KiB 페이지의 시작만 계산하므로 세부 위치는 다음 오프셋 연산에서 더합니다.
233행 그림 · 주소·값 구분
함수·코드 범위: __primary_switched
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- ADRP의 페이지 단위는 4KiB입니다.
sub x4, x4, x0 // the kernel virtual andx4를 기준으로 x0을 빼서 x4에 결과를 둡니다. 주소 계산에 쓰이는 경우에는 구조체의 필드나 다음 저장 위치를 찾는 동작입니다. 이 형태는 조건 플래그를 바꾸지 않습니다.
234행 그림 · 레지스터 상태
함수·코드 범위: __primary_switched
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 결과는 목적 레지스터 폭에 맞습니다. 이 형식은 NZCV를 바꾸지 않습니다.
str_l x4, kimage_voffset, x5 // physical mappings커널 기호의 가상 주소와 물리 주소 사이의 차이를 kimage_voffset에 보관합니다. 커널 이미지의 주소 변환에 쓸 값이며 일반 선형 매핑의 모든 주소를 같은 식으로 바꾼다는 뜻은 아닙니다. str_l은 임시 레지스터로 심벌의 주소를 구하고 STR로 그 메모리에 값을 쓰는 매크로입니다.
235행 그림 · 주소·값 구분
함수·코드 범위: __primary_switched
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 주소 계산에 쓰는 임시 레지스터도 바뀝니다.
str_l 매크로 본문 · 203행
arch/arm64/include/asm/assembler.h · Linux v6.18.37
정의 선택 조건: #ifndef __ASM_ASSEMBLER_H
.macro str_l, src, sym, tmp
adrp \tmp, \sym
str \src, [\tmp, :lo12:\sym]
.endm mov x0, x20x0에 x20에 들어 있는 값을 넣습니다. 원래 x0의 내용은 덮어쓰며, 다른 레지스터나 그 주소의 메모리까지 복사하지는 않습니다.
237행 그림 · 값 복사
함수·코드 범위: __primary_switched
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 화살표는 값의 변화를 뜻합니다. 주소값을 옮겨도 그 주소의 메모리 내용은 복사하지 않습니다.
bl set_cpu_boot_mode_flagx20에서 가져온 출발 EL 기록을 전역 __boot_cpu_mode에 남깁니다. 여러 CPU가 일관된 권한 상태로 부팅되었는지 이후 코드가 확인할 수 있게 합니다. BL은 다음 명령의 주소를 LR(x30)에 기록하고 목적지로 분기합니다. 호출된 코드가 x0 같은 작업 레지스터를 바꿀 수 있으므로, 호출 뒤의 값은 그 코드의 반환·보존 규칙에 따릅니다.
238행 그림 · 호출과 실행 위치
함수·코드 범위: __primary_switched
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 그림은 분기 명령 자체의 효과입니다. 호출된 함수가 실행된 뒤의 레지스터 값은 그 함수의 반환·보존 규칙을 따라야 합니다.
#if defined(CONFIG_KASAN_GENERIC) || defined(CONFIG_KASAN_SW_TAGS)KASAN은 커널 메모리의 범위 밖 접근이나 해제 후 사용 등을 찾는 검사 기능입니다. GENERIC과 SW_TAGS는 소프트웨어가 참조할 shadow 메모리가 필요하므로 이 두 설정 중 하나가 켜졌을 때만 다음 초기 매핑 호출을 포함합니다. HW_TAGS는 MTE 기반의 다른 방식이어서 이 조건에는 들어가지 않습니다. 이 조건은 빌드 전처리 단계에서 포함할 코드를 고릅니다. CPU가 실행 중 전처리 조건을 검사하지는 않습니다.
240행 그림 · 전처리 단계
함수·코드 범위: __primary_switched
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
bl kasan_early_initkasan_early_init은 전체 shadow 영역을 초기 shadow 페이지로 연결하여 초기 C 코드의 KASAN 계측이 아직 준비되지 않은 주소를 읽지 않도록 합니다. 실제 메모리 구간별 최종 shadow 구성은 뒤의 KASAN 초기화에서 이루어집니다. BL은 다음 명령의 주소를 LR(x30)에 기록하고 목적지로 분기합니다. 호출된 코드가 x0 같은 작업 레지스터를 바꿀 수 있으므로, 호출 뒤의 값은 그 코드의 반환·보존 규칙에 따릅니다.
241행 그림 · 호출과 실행 위치
함수·코드 범위: __primary_switched
이 줄을 포함하는 조건: #if defined(CONFIG_KASAN_GENERIC) || defined(CONFIG_KASAN_SW_TAGS)
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 그림은 분기 명령 자체의 효과입니다. 호출된 함수가 실행된 뒤의 레지스터 값은 그 함수의 반환·보존 규칙을 따라야 합니다.
#endif빌드 조건으로 나누었던 구간을 닫습니다. 대상:
242행 그림 · 전처리 단계
함수·코드 범위: __primary_switched
이 줄을 포함하는 조건: #if defined(CONFIG_KASAN_GENERIC) || defined(CONFIG_KASAN_SW_TAGS)
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
mov x0, x20x0에 x20에 들어 있는 값을 넣습니다. 원래 x0의 내용은 덮어쓰며, 다른 레지스터나 그 주소의 메모리까지 복사하지는 않습니다.
243행 그림 · 값 복사
함수·코드 범위: __primary_switched
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 화살표는 값의 변화를 뜻합니다. 주소값을 옮겨도 그 주소의 메모리 내용은 복사하지 않습니다.
bl finalise_el2 // Prefer VHE if possible출발 EL이 EL2이고 현재 EL1이라면 HVC로 hyp stub에 EL2 마무리를 요청합니다. CPU의 VHE 지원과 override 정책이 허용하면 호스트를 EL2h로 옮길 수 있고, 조건이 맞지 않으면 EL1에서 계속합니다. EL2 출발 기록만으로 이미 VHE라고 판단하면 안 됩니다. BL은 다음 명령의 주소를 LR(x30)에 기록하고 목적지로 분기합니다. 호출된 코드가 x0 같은 작업 레지스터를 바꿀 수 있으므로, 호출 뒤의 값은 그 코드의 반환·보존 규칙에 따릅니다.
244행 그림 · 호출과 실행 위치
함수·코드 범위: __primary_switched
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 그림은 분기 명령 자체의 효과입니다. 호출된 함수가 실행된 뒤의 레지스터 값은 그 함수의 반환·보존 규칙을 따라야 합니다.
ldp x29, x30, [sp], #16sp가 가리키는 메모리 위치에 보관된 값을 x29, x30로 가져옵니다. 주소 숫자를 옮기는 MOV와 달리 실제 메모리를 읽습니다. 첫 위치에서 x29, 그보다 8바이트 높은 위치에서 x30의 값을 읽습니다. 앞서 보관한 상태를 되살리거나 스택으로 전달된 인자를 얻는 과정입니다. 읽은 뒤 기준 포인터를 옮깁니다. SP가 증가하는 경우는 사용한 스택 공간을 해제하는 단계입니다.
245행 그림 · 메모리 읽기
함수·코드 범위: __primary_switched
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 대괄호 밖의 오프셋은 후위 갱신입니다. 이전 주소로 접근한 뒤 기준 레지스터를 갱신합니다.
- 메모리 칸은 낮은 주소부터 나열했습니다. SP를 줄이면 더 낮은 주소의 공간을 사용합니다. 화면의 아래쪽이 곧 낮은 주소라는 뜻은 아닙니다.
bl start_kernel준비된 커널 스택·주소 변환·예외 벡터를 바탕으로 공통 C 초기화 start_kernel로 넘깁니다. 메모리·스케줄러 등 일반 커널 초기화가 여기서 이어지며 정상적으로 이 어셈블리로 반환하지 않습니다. BL은 다음 명령의 주소를 LR(x30)에 기록하고 목적지로 분기합니다. 호출된 코드가 x0 같은 작업 레지스터를 바꿀 수 있으므로, 호출 뒤의 값은 그 코드의 반환·보존 규칙에 따릅니다.
246행 그림 · 호출과 실행 위치
함수·코드 범위: __primary_switched
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 그림은 분기 명령 자체의 효과입니다. 호출된 함수가 실행된 뒤의 레지스터 값은 그 함수의 반환·보존 규칙을 따라야 합니다.
ASM_BUG()start_kernel은 정상적으로 돌아오지 않아야 합니다. 만약 반환했다면 이어서 다른 초기 코드로 흘러가지 않도록 BRK와 BUG 정보를 만들어 오류를 드러냅니다.
247행 그림 · 매크로 전체의 상태 변화
함수·코드 범위: __primary_switched
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 오류 메타데이터와 BRK 명령을 함께 만듭니다. HVC와 달리 ELR에는 BRK 자신을 가리키는 주소가 저장됩니다.
SYM_FUNC_END(__primary_switched)도구에 이 코드 범위의 끝을 알려 크기와 심볼 정보를 기록합니다. CPU가 이 줄 때문에 자동으로 반환하지는 않습니다.
248행 그림 · 어셈블·링크 단계
함수·코드 범위: __primary_switched
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 끝 표기 자체가 RET나 분기를 실행하지는 않습니다.
init_kernel_el의 EL1 경로: RET 대신 ERET
.section ".idmap.text","a"이후 출력할 바이트를 어느 섹션에 넣을지 바꿉니다. 빌드 도구가 코드를 분류하는 작업이며 현재 CPU의 PC나 SP를 옮기는 줄은 아닙니다. ".idmap.text","a"
254행 그림 · 어셈블·링크 단계
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
SYM_FUNC_START(init_kernel_el)이 위치부터 (init_kernel_el)라는 코드가 시작한다고 도구에 알려 줍니다. 정렬과 외부 공개 여부도 정의에 포함됩니다. ARM64 함수 시작 매크로는 간접 호출의 도착점을 표시하는 BTI도 추가합니다.
270행 그림 · 어셈블·링크 단계
함수·코드 범위: init_kernel_el
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 함수 시작 매크로는 심벌·정렬 정보와 BTI 진입 명령 등을 배치할 수 있습니다. 생성된 명령의 실행과 심벌 선언을 구분합니다.
mrs x1, CurrentEL실제 현재 EL을 읽어 EL1 초기화와 EL2 초기화 중 올바른 경로를 고릅니다. x0의 부팅 MMU 기록과 별도 정보입니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.
271행 그림 · 시스템 레지스터 읽기
함수·코드 범위: init_kernel_el
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 일반 RAM의 메모리 접근과 구분합니다.
cmp x1, #CurrentEL_EL2x1와 CurrentEL_EL2를 비교하여 조건 플래그에 결과를 남깁니다. 비교한 두 값은 그대로이며, 뒤의 조건 분기가 이 결과를 읽습니다.
272행 그림 · 조건 플래그 계산
함수·코드 범위: init_kernel_el
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 일반 레지스터에 연산 결과를 쓰지 않고 조건 플래그만 바꿉니다.
- N은 결과의 부호 비트, Z는 결과가 0인지 나타냅니다. C는 빌림이 없으면 1, V는 부호 있는 뺄셈의 오버플로를 나타냅니다.
b.eq init_el2앞서 계산한 조건 플래그가 Z=1이면 init_el2로 갑니다. 사이에 플래그를 바꾸지 않는 명령이 있어도 비교 결과는 남아 있습니다.
273행 그림 · 조건별 실행 위치
함수·코드 범위: init_kernel_el
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 조건이 맞는 한 경로만 실행합니다. 이 분기 명령은 NZCV를 새로 계산하지 않습니다.
SYM_INNER_LABEL(init_el1, SYM_L_LOCAL)기존 함수 안의 중간 위치에 이름을 붙입니다. 다른 분기에서 찾아올 수 있지만 새 함수를 호출하거나 스택 프레임을 만드는 동작은 아닙니다.
275행 그림 · 어셈블·링크 단계
함수·코드 범위: init_kernel_el
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
mov_q x0, INIT_SCTLR_EL1_MMU_OFFINIT_SCTLR_EL1_MMU_OFF는 EL1에서 쓸 기본 제어값이되 MMU를 꺼 둔 설정입니다. 부트로더의 임의 SCTLR 상태를 그대로 이어 쓰지 않도록 준비합니다. mov_q는 64비트 상수를 필요한 MOVZ/MOVN·MOVK 조합으로 구성하는 매크로입니다. 해당 주소의 메모리에서 데이터를 가져오는 명령은 아닙니다.
276행 그림 · 값 복사
함수·코드 범위: init_kernel_el
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 화살표는 값의 변화를 뜻합니다. 주소값을 옮겨도 그 주소의 메모리 내용은 복사하지 않습니다.
- 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 pre_disable_mmu_workaroundQualcomm Falkor erratum E1041 우회 빌드에서는 MMU를 끄는 전환에 필요한 ISB를 넣습니다. 설정이 없으면 빈 매크로입니다. SCTLR의 M 비트를 1에서 0으로 바꾸는 MSR 바로 앞에서 실행 순서를 맞추는 CPU 구현상의 보완입니다.
277행 그림 · 동기화·캐시·대기 상태
함수·코드 범위: init_kernel_el
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
pre_disable_mmu_workaround 매크로 본문 · 647행
arch/arm64/include/asm/assembler.h · Linux v6.18.37
정의 선택 조건: #ifndef __ASM_ASSEMBLER_H
.macro pre_disable_mmu_workaround
#ifdef CONFIG_QCOM_FALKOR_ERRATUM_E1041
isb
#endif
.endm msr sctlr_el1, x0기본 EL1 제어값을 설치하여 MMU가 꺼진 일관된 초기 실행 환경을 만듭니다. 뒤에서 __cpu_setup과 __enable_mmu가 정식 주소 변환 설정을 완성합니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.
278행 그림 · 시스템 레지스터 쓰기
함수·코드 범위: init_kernel_el
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 접근 권한과 VHE 별칭 규칙에 따라 실제 대상이 정해집니다. 뒤의 ISB·DSB가 필요한지는 이 레지스터와 문맥에 따릅니다.
isb뒤 명령을 변경된 CPU 제어 상태에 맞춰 실행하도록 동기화합니다. 주소 변환이나 벡터 설정을 바꾼 뒤 예전 실행 상태가 계속 쓰이지 않게 합니다.
279행 그림 · 동기화·캐시·대기 상태
함수·코드 범위: init_kernel_el
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
mov_q x0, INIT_PSTATE_EL1INIT_PSTATE_EL1은 ERET 후 사용할 EL1h와 예외 마스크 등의 초기 PSTATE입니다. 단순한 숫자 반환값이 아닙니다. mov_q는 64비트 상수를 필요한 MOVZ/MOVN·MOVK 조합으로 구성하는 매크로입니다. 해당 주소의 메모리에서 데이터를 가져오는 명령은 아닙니다.
280행 그림 · 값 복사
함수·코드 범위: init_kernel_el
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 화살표는 값의 변화를 뜻합니다. 주소값을 옮겨도 그 주소의 메모리 내용은 복사하지 않습니다.
- mov_q는 큰 상수를 여러 MOVZ/MOVN·MOVK 명령으로 구성할 수 있습니다. 여기서는 매크로 전체 결과를 표시합니다.
mov_q 매크로 본문 · 552행
arch/arm64/include/asm/assembler.h · Linux v6.18.37
정의 선택 조건: #ifndef __ASM_ASSEMBLER_H
.macro mov_q, reg, val
.if (((\val) >> 31) == 0 || ((\val) >> 31) == 0x1ffffffff)
movz \reg, :abs_g1_s:\val
.else
.if (((\val) >> 47) == 0 || ((\val) >> 47) == 0x1ffff)
movz \reg, :abs_g2_s:\val
.else
movz \reg, :abs_g3:\val
movk \reg, :abs_g2_nc:\val
.endif
movk \reg, :abs_g1_nc:\val
.endif
movk \reg, :abs_g0_nc:\val
.endm msr spsr_el1, x0ERET가 복원할 PSTATE를 SPSR_EL1에 넣습니다. RET와 달리 ERET를 사용해 상태까지 정규화하려는 준비입니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.
281행 그림 · 시스템 레지스터 쓰기
함수·코드 범위: init_kernel_el
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 접근 권한과 VHE 별칭 규칙에 따라 실제 대상이 정해집니다. 뒤의 ISB·DSB가 필요한지는 이 레지스터와 문맥에 따릅니다.
msr elr_el1, lrBL이 LR에 남긴 다음 명령 주소를 ELR_EL1에 넣습니다. ERET가 이 주소로 돌아가면서 SPSR_EL1의 상태도 적용하도록 합니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.
282행 그림 · 시스템 레지스터 쓰기
함수·코드 범위: init_kernel_el
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 접근 권한과 VHE 별칭 규칙에 따라 실제 대상이 정해집니다. 뒤의 ISB·DSB가 필요한지는 이 레지스터와 문맥에 따릅니다.
mov w0, #BOOT_CPU_MODE_EL1w0에 BOOT_CPU_MODE_EL1라는 값을 넣습니다. 원래 w0의 내용은 덮어쓰며, 다른 레지스터나 그 주소의 메모리까지 복사하지는 않습니다. W 레지스터에 쓰므로 대응 X 레지스터의 상위 절반도 0이 됩니다.
283행 그림 · 값 복사
함수·코드 범위: init_kernel_el
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 화살표는 값의 변화를 뜻합니다. 주소값을 옮겨도 그 주소의 메모리 내용은 복사하지 않습니다.
eret예외가 끝났으므로 저장해 둔 PC와 실행 상태를 함께 되살립니다. 일반 RET와 달리 어느 예외 레벨과 스택 모드로 돌아갈지도 SPSR의 내용에 따라 정해집니다.
284행 그림 · 저장된 실행 상태로 복귀
함수·코드 범위: init_kernel_el
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- ELx는 현재 예외 레벨입니다. SPSR의 모드에 따라 복귀 레벨과 스택 선택도 바뀝니다. 일반 레지스터 복구는 앞의 load 명령들이 담당합니다.
init_kernel_el의 EL2 경로: HCR, EL12, boot flags
SYM_INNER_LABEL(init_el2, SYM_L_LOCAL)기존 함수 안의 중간 위치에 이름을 붙입니다. 다른 분기에서 찾아올 수 있지만 새 함수를 호출하거나 스택 프레임을 만드는 동작은 아닙니다.
286행 그림 · 어셈블·링크 단계
함수·코드 범위: init_kernel_el
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
msr elr_el2, lrEL2 경로 역시 호출자의 복귀 주소를 ELR_EL2에 보관합니다. 뒤의 ERET가 EL1으로 내려오면서 이 위치부터 이어 실행하게 됩니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.
287행 그림 · 시스템 레지스터 쓰기
함수·코드 범위: init_kernel_el
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 접근 권한과 VHE 별칭 규칙에 따라 실제 대상이 정해집니다. 뒤의 ISB·DSB가 필요한지는 이 레지스터와 문맥에 따릅니다.
cbz x0, 0fx0의 값가 0이면 0f (L300)로 갑니다. 조건을 만족하지 않으면 바로 다음 명령을 읽습니다. 이 분기는 CMP의 결과가 아니라 지정 레지스터를 직접 검사합니다.
290행 그림 · 조건별 실행 위치
함수·코드 범위: init_kernel_el
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 조건이 맞는 한 경로만 실행합니다. 이 분기 명령은 NZCV를 새로 계산하지 않습니다.
adrp x0, __hyp_idmap_text_startx0에 __hyp_idmap_text_start의 위치를 계산합니다. 그 위치의 데이터는 아직 읽지 않습니다. ADRP는 4 KiB 페이지의 시작만 계산하므로 세부 위치는 다음 오프셋 연산에서 더합니다.
291행 그림 · 주소·값 구분
함수·코드 범위: init_kernel_el
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- ADRP의 페이지 단위는 4KiB입니다.
adr_l x1, __hyp_text_endx1에 __hyp_text_end의 위치를 계산합니다. 그 위치의 데이터는 아직 읽지 않습니다. 이후의 메모리 접근이나 분기가 사용할 주소를 준비하는 줄입니다.
292행 그림 · 주소·값 구분
함수·코드 범위: init_kernel_el
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 이 매크로의 임시 레지스터는 내부에서 사용됩니다.
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 adr_l x2, dcache_clean_pocx2에 dcache_clean_poc의 위치를 계산합니다. 그 위치의 데이터는 아직 읽지 않습니다. 이후의 메모리 접근이나 분기가 사용할 주소를 준비하는 줄입니다.
293행 그림 · 주소·값 구분
함수·코드 범위: init_kernel_el
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 이 매크로의 임시 레지스터는 내부에서 사용됩니다.
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 blr x2EL2를 MMU·캐시가 켜진 상태로 시작했다면 hyp 코드 [x0,x1)을 PoC까지 clean합니다. EL2 MMU를 끈 뒤에도 메모리에서 초기 hyp 코드를 안정적으로 가져오기 위한 처리입니다. BLR은 다음 명령의 주소를 LR(x30)에 기록하고 레지스터에 든 목적지로 분기합니다. 호출된 코드가 x0 같은 작업 레지스터를 바꿀 수 있으므로, 호출 뒤의 값은 그 코드의 반환·보존 규칙에 따릅니다.
294행 그림 · 호출과 실행 위치
함수·코드 범위: init_kernel_el
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 그림은 분기 명령 자체의 효과입니다. 호출된 함수가 실행된 뒤의 레지스터 값은 그 함수의 반환·보존 규칙을 따라야 합니다.
mov_q x0, INIT_SCTLR_EL2_MMU_OFF한 명령에 담기 어려운 큰 값이나 주소를 레지스터에 구성합니다. 즉시값 조각과 재배치 표현을 사용하며, 메모리에서 그 주소의 본문을 복사하는 작업은 아닙니다.
296행 그림 · 값 복사
함수·코드 범위: init_kernel_el
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 화살표는 값의 변화를 뜻합니다. 주소값을 옮겨도 그 주소의 메모리 내용은 복사하지 않습니다.
- 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 pre_disable_mmu_workaroundQualcomm Falkor erratum E1041 우회 빌드에서는 MMU를 끄는 전환에 필요한 ISB를 넣습니다. 설정이 없으면 빈 매크로입니다. SCTLR의 M 비트를 1에서 0으로 바꾸는 MSR 바로 앞에서 실행 순서를 맞추는 CPU 구현상의 보완입니다.
297행 그림 · 동기화·캐시·대기 상태
함수·코드 범위: init_kernel_el
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
pre_disable_mmu_workaround 매크로 본문 · 647행
arch/arm64/include/asm/assembler.h · Linux v6.18.37
정의 선택 조건: #ifndef __ASM_ASSEMBLER_H
.macro pre_disable_mmu_workaround
#ifdef CONFIG_QCOM_FALKOR_ERRATUM_E1041
isb
#endif
.endm msr sctlr_el2, x0EL2의 SCTLR을 MMU off 기본값으로 바꿉니다. 이 명령 전후의 캐시 clean과 ISB를 함께 봐야 기존 변환을 중단하는 순서가 설명됩니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.
298행 그림 · 시스템 레지스터 쓰기
함수·코드 범위: init_kernel_el
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 접근 권한과 VHE 별칭 규칙에 따라 실제 대상이 정해집니다. 뒤의 ISB·DSB가 필요한지는 이 레지스터와 문맥에 따릅니다.
isb뒤 명령을 변경된 CPU 제어 상태에 맞춰 실행하도록 동기화합니다. 주소 변환이나 벡터 설정을 바꾼 뒤 예전 실행 상태가 계속 쓰이지 않게 합니다.
299행 그림 · 동기화·캐시·대기 상태
함수·코드 범위: init_kernel_el
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
0:여기에 0라는 위치 이름을 붙입니다. 다른 줄의 분기나 주소 계산이 이 위치를 찾아올 수 있습니다. 이름 자체가 CPU 작업을 추가하지는 않습니다.
300행 그림 · 위치에 이름 붙이기
함수·코드 범위: init_kernel_el
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
init_el2_hcr HCR_HOST_NVHE_FLAGSEL2가 하위 실행 환경을 어떻게 제어할지 기본값을 정합니다. 일부 CPU는 VHE 상태를 끌 수 없어 실제 기능에 맞춘 처리가 필요합니다.
302행 그림 · 매크로 안의 상태 변화
함수·코드 범위: init_kernel_el
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 여러 명령과 내부 분기를 묶은 그림입니다. 작업 레지스터를 모두 보존한다는 뜻은 아니며, 정확한 변경 순서는 아래 매크로 본문과 줄별 그림에서 확인합니다.
init_el2_hcr 매크로 본문 · 19행
arch/arm64/include/asm/el2_setup.h · Linux v6.18.37
정의 선택 조건: #ifndef __ARM_KVM_INIT_H__
.macro init_el2_hcr val
mov_q x0, \val
/*
* Compliant CPUs advertise their VHE-onlyness with
* ID_AA64MMFR4_EL1.E2H0 < 0. On such CPUs HCR_EL2.E2H is RES1, but it
* can reset into an UNKNOWN state and might not read as 1 until it has
* been initialized explicitly.
* Initalize HCR_EL2.E2H so that later code can rely upon HCR_EL2.E2H
* indicating whether the CPU is running in E2H mode.
*/
mrs_s x1, SYS_ID_AA64MMFR4_EL1
sbfx x1, x1, #ID_AA64MMFR4_EL1_E2H0_SHIFT, #ID_AA64MMFR4_EL1_E2H0_WIDTH
cmp x1, #0
b.lt .LnE2H0_\@
/*
* Unfortunately, HCR_EL2.E2H can be RES1 even if not advertised
* as such via ID_AA64MMFR4_EL1.E2H0:
*
* - Fruity CPUs predate the !FEAT_E2H0 relaxation, and seem to
* have HCR_EL2.E2H implemented as RAO/WI.
*
* - On CPUs that lack FEAT_FGT, a hypervisor can't trap guest
* reads of ID_AA64MMFR4_EL1 to advertise !FEAT_E2H0. NV
* guests on these hosts can write to HCR_EL2.E2H without
* trapping to the hypervisor, but these writes have no
* functional effect.
*
* Handle both cases by checking for an essential VHE property
* (system register remapping) to decide whether we're
* effectively VHE-only or not.
*/
msr_hcr_el2 x0 // Setup HCR_EL2 as nVHE
isb
mov x1, #1 // Write something to FAR_EL1
msr far_el1, x1
isb
mov x1, #2 // Try to overwrite it via FAR_EL2
msr far_el2, x1
isb
mrs x1, far_el1 // If we see the latest write in FAR_EL1,
cmp x1, #2 // we can safely assume we are VHE only.
b.ne .LnVHE_\@ // Otherwise, we know that nVHE works.
.LnE2H0_\@:
orr x0, x0, #HCR_E2H
msr_hcr_el2 x0
isb
.LnVHE_\@:
.endm init_el2_stateEL1 실행에 영향을 주는 상위 EL2의 타이머·인터럽트·접근 제어를 준비합니다. 하위 레벨만 초기화해도 상위 제약은 남을 수 있기 때문입니다.
303행 그림 · 매크로 안의 상태 변화
함수·코드 범위: init_kernel_el
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 여러 명령과 내부 분기를 묶은 그림입니다. 작업 레지스터를 모두 보존한다는 뜻은 아니며, 정확한 변경 순서는 아래 매크로 본문과 줄별 그림에서 확인합니다.
init_el2_state 매크로 본문 · 448행
arch/arm64/include/asm/el2_setup.h · Linux v6.18.37
정의 선택 조건: #ifndef __ARM_KVM_INIT_H__
.macro init_el2_state
__init_el2_sctlr
__init_el2_hcrx
__init_el2_timers
__init_el2_debug
__init_el2_brbe
__init_el2_lor
__init_el2_stage2
__init_el2_gicv3
__init_el2_gicv5
__init_el2_hstr
__init_el2_nvhe_idregs
__init_el2_cptr
__init_el2_fgt
__init_el2_fgt2
.endm adr_l x0, __hyp_stub_vectorsx0에 __hyp_stub_vectors의 위치를 계산합니다. 그 위치의 데이터는 아직 읽지 않습니다. 이후의 메모리 접근이나 분기가 사용할 주소를 준비하는 줄입니다.
306행 그림 · 주소·값 구분
함수·코드 범위: init_kernel_el
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 이 매크로의 임시 레지스터는 내부에서 사용됩니다.
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 msr vbar_el2, x0EL2 예외를 초기 hyp stub의 벡터로 받도록 VBAR_EL2에 설치합니다. EL1에서 HVC로 finalise_el2 등을 요청할 최소 처리 경로를 마련합니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.
307행 그림 · 시스템 레지스터 쓰기
함수·코드 범위: init_kernel_el
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 접근 권한과 VHE 별칭 규칙에 따라 실제 대상이 정해집니다. 뒤의 ISB·DSB가 필요한지는 이 레지스터와 문맥에 따릅니다.
isb뒤 명령을 변경된 CPU 제어 상태에 맞춰 실행하도록 동기화합니다. 주소 변환이나 벡터 설정을 바꾼 뒤 예전 실행 상태가 계속 쓰이지 않게 합니다.
308행 그림 · 동기화·캐시·대기 상태
함수·코드 범위: init_kernel_el
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
mov_q x1, INIT_SCTLR_EL1_MMU_OFF한 명령에 담기 어려운 큰 값이나 주소를 레지스터에 구성합니다. 즉시값 조각과 재배치 표현을 사용하며, 메모리에서 그 주소의 본문을 복사하는 작업은 아닙니다.
310행 그림 · 값 복사
함수·코드 범위: init_kernel_el
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 화살표는 값의 변화를 뜻합니다. 주소값을 옮겨도 그 주소의 메모리 내용은 복사하지 않습니다.
- 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 mrs x0, hcr_el2HCR_EL2를 읽어 E2H가 실제로 켜져 있는지 확인합니다. HCR은 EL2가 EL1 실행을 제어하는 레지스터이며 E2H에 따라 EL1 이름의 일부 레지스터 접근 의미가 달라집니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.
312행 그림 · 시스템 레지스터 읽기
함수·코드 범위: init_kernel_el
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 일반 RAM의 메모리 접근과 구분합니다.
and x0, x0, #HCR_E2H필요한 비트만 남깁니다. 다른 정보가 섞인 값에서 관심 있는 부분을 고르는 작업입니다. 여기서 결과를 받는 레지스터는 x0입니다.
313행 그림 · 레지스터 상태
함수·코드 범위: init_kernel_el
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 결과는 목적 레지스터 폭에 맞습니다. 이 형식은 NZCV를 바꾸지 않습니다.
cbz x0, 2fx0의 값가 0이면 2f (L321)로 갑니다. 조건을 만족하지 않으면 바로 다음 명령을 읽습니다. 이 분기는 CMP의 결과가 아니라 지정 레지스터를 직접 검사합니다.
314행 그림 · 조건별 실행 위치
함수·코드 범위: init_kernel_el
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 조건이 맞는 한 경로만 실행합니다. 이 분기 명령은 NZCV를 새로 계산하지 않습니다.
msr_s SYS_SCTLR_EL12, x1E2H가 켜진 EL2에서는 SCTLR_EL1 이름이 호스트 EL2 쪽에 연결될 수 있으므로, EL12 별칭으로 실제 하위 EL1의 SCTLR을 초기화합니다. 잘못된 레벨의 MMU를 설정하지 않기 위한 구분입니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.
317행 그림 · 시스템 레지스터 쓰기
함수·코드 범위: init_kernel_el
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 접근 권한과 VHE 별칭 규칙에 따라 실제 대상이 정해집니다. 뒤의 ISB·DSB가 필요한지는 이 레지스터와 문맥에 따릅니다.
msr_s 매크로 본문 · 1140행
arch/arm64/include/asm/sysreg.h · Linux v6.18.37
정의 선택 조건: #ifndef __ASM_SYSREG_H / #ifdef __ASSEMBLY__
.macro msr_s, sreg, rt
__emit_inst(0xd5000000|(\sreg)|(.L__gpr_num_\rt))
.endm mov x2, #BOOT_CPU_FLAG_E2HBOOT_CPU_FLAG_E2H는 EL2 시작 당시 E2H 상태를 함께 전달할 상위 플래그입니다. 하위 w0의 BOOT_CPU_MODE_EL2와 별도 정보입니다. MOV는 x2의 기존 값을 대체합니다. 원본이 주소값이어도 그 주소의 메모리를 읽거나 복사하지 않습니다.
318행 그림 · 값 복사
함수·코드 범위: init_kernel_el
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 화살표는 값의 변화를 뜻합니다. 주소값을 옮겨도 그 주소의 메모리 내용은 복사하지 않습니다.
b 3f3f (L324)부터 실행을 계속합니다. 돌아올 주소를 새로 적지 않으므로 일반 함수 호출과는 다른 제어 이동입니다.
319행 그림 · 호출과 실행 위치
함수·코드 범위: init_kernel_el
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 새 복귀 주소를 LR에 기록하지 않습니다. RET 역시 메모리의 스택 값을 자동으로 꺼내는 명령은 아닙니다.
2:여기에 2라는 위치 이름을 붙입니다. 다른 줄의 분기나 주소 계산이 이 위치를 찾아올 수 있습니다. 이름 자체가 CPU 작업을 추가하지는 않습니다.
321행 그림 · 위치에 이름 붙이기
함수·코드 범위: init_kernel_el
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
msr sctlr_el1, x1E2H가 꺼져 있으면 SCTLR_EL1 이름으로 실제 하위 EL1 제어 레지스터를 설정할 수 있습니다. 이 경로는 EL12 별칭이 필요 없습니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.
322행 그림 · 시스템 레지스터 쓰기
함수·코드 범위: init_kernel_el
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 접근 권한과 VHE 별칭 규칙에 따라 실제 대상이 정해집니다. 뒤의 ISB·DSB가 필요한지는 이 레지스터와 문맥에 따릅니다.
mov x2, xzrx2에 0을 넣습니다. 원래 x2의 내용은 덮어쓰며, 다른 레지스터나 그 주소의 메모리까지 복사하지는 않습니다.
323행 그림 · 값 복사
함수·코드 범위: init_kernel_el
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 화살표는 값의 변화를 뜻합니다. 주소값을 옮겨도 그 주소의 메모리 내용은 복사하지 않습니다.
3:여기에 3라는 위치 이름을 붙입니다. 다른 줄의 분기나 주소 계산이 이 위치를 찾아올 수 있습니다. 이름 자체가 CPU 작업을 추가하지는 않습니다.
324행 그림 · 위치에 이름 붙이기
함수·코드 범위: init_kernel_el
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
mov x0, #INIT_PSTATE_EL1x0에 INIT_PSTATE_EL1라는 값을 넣습니다. 원래 x0의 내용은 덮어쓰며, 다른 레지스터나 그 주소의 메모리까지 복사하지는 않습니다.
325행 그림 · 값 복사
함수·코드 범위: init_kernel_el
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 화살표는 값의 변화를 뜻합니다. 주소값을 옮겨도 그 주소의 메모리 내용은 복사하지 않습니다.
msr spsr_el2, x0EL2에서 ERET한 뒤 실행할 EL1h 및 마스크 상태를 SPSR_EL2에 준비합니다. 출발은 EL2지만 이 ERET 직후의 실행은 EL1입니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.
326행 그림 · 시스템 레지스터 쓰기
함수·코드 범위: init_kernel_el
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 접근 권한과 VHE 별칭 규칙에 따라 실제 대상이 정해집니다. 뒤의 ISB·DSB가 필요한지는 이 레지스터와 문맥에 따릅니다.
mov w0, #BOOT_CPU_MODE_EL2w0에 BOOT_CPU_MODE_EL2라는 값을 넣습니다. 원래 w0의 내용은 덮어쓰며, 다른 레지스터나 그 주소의 메모리까지 복사하지는 않습니다. W 레지스터에 쓰므로 대응 X 레지스터의 상위 절반도 0이 됩니다.
328행 그림 · 값 복사
함수·코드 범위: init_kernel_el
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 화살표는 값의 변화를 뜻합니다. 주소값을 옮겨도 그 주소의 메모리 내용은 복사하지 않습니다.
orr x0, x0, x2여러 비트의 설정을 합칩니다. 이미 1인 비트를 유지하면서 지정한 설정을 추가할 때 사용합니다. 여기서 결과를 받는 레지스터는 x0입니다.
329행 그림 · 레지스터 상태
함수·코드 범위: init_kernel_el
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 결과는 목적 레지스터 폭에 맞습니다. 이 형식은 NZCV를 바꾸지 않습니다.
eret예외가 끝났으므로 저장해 둔 PC와 실행 상태를 함께 되살립니다. 일반 RET와 달리 어느 예외 레벨과 스택 모드로 돌아갈지도 SPSR의 내용에 따라 정해집니다.
330행 그림 · 저장된 실행 상태로 복귀
함수·코드 범위: init_kernel_el
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- ELx는 현재 예외 레벨입니다. SPSR의 모드에 따라 복귀 레벨과 스택 선택도 바뀝니다. 일반 레지스터 복구는 앞의 load 명령들이 담당합니다.
SYM_FUNC_END(init_kernel_el)도구에 이 코드 범위의 끝을 알려 크기와 심볼 정보를 기록합니다. CPU가 이 줄 때문에 자동으로 반환하지는 않습니다.
331행 그림 · 어셈블·링크 단계
함수·코드 범위: init_kernel_el
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 끝 표기 자체가 RET나 분기를 실행하지는 않습니다.
보조 CPU의 두 진입점과 holding pen
SYM_FUNC_START(secondary_holding_pen)이 위치부터 (secondary_holding_pen)라는 코드가 시작한다고 도구에 알려 줍니다. 정렬과 외부 공개 여부도 정의에 포함됩니다. ARM64 함수 시작 매크로는 간접 호출의 도착점을 표시하는 BTI도 추가합니다.
337행 그림 · 어셈블·링크 단계
함수·코드 범위: secondary_holding_pen
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 함수 시작 매크로는 심벌·정렬 정보와 BTI 진입 명령 등을 배치할 수 있습니다. 생성된 명령의 실행과 심벌 선언을 구분합니다.
mov x0, xzrx0에 0을 넣습니다. 원래 x0의 내용은 덮어쓰며, 다른 레지스터나 그 주소의 메모리까지 복사하지는 않습니다.
338행 그림 · 값 복사
함수·코드 범위: secondary_holding_pen
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 화살표는 값의 변화를 뜻합니다. 주소값을 옮겨도 그 주소의 메모리 내용은 복사하지 않습니다.
bl init_kernel_el // w0=cpu_boot_modeholding pen에서 대기할 보조 CPU의 EL 상태를 정규화합니다. x0=0으로 MMU off 진입임을 전달하며, 반환값은 나중에 보조 CPU 초기화에서 사용할 출발 EL 기록입니다. BL은 다음 명령의 주소를 LR(x30)에 기록하고 목적지로 분기합니다.
339행 그림 · 호출과 실행 위치
함수·코드 범위: secondary_holding_pen
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 그림은 분기 명령 자체의 효과입니다. 호출된 함수가 실행된 뒤의 레지스터 값은 그 함수의 반환·보존 규칙을 따라야 합니다.
mrs x2, mpidr_el1MPIDR_EL1에서 이 CPU의 하드웨어 affinity ID를 읽습니다. release 변수에 기록된 대상 CPU ID와 비교해 자기 차례인지 확인할 값입니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.
340행 그림 · 시스템 레지스터 읽기
함수·코드 범위: secondary_holding_pen
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 일반 RAM의 메모리 접근과 구분합니다.
mov_q x1, MPIDR_HWID_BITMASKMPIDR에서 하드웨어 CPU 식별에 쓰는 affinity 비트만 남길 마스크를 준비합니다. unrelated 상태 비트 때문에 release ID 비교가 틀리지 않게 합니다. mov_q는 64비트 상수를 필요한 MOVZ/MOVN·MOVK 조합으로 구성하는 매크로입니다. 해당 주소의 메모리에서 데이터를 가져오는 명령은 아닙니다.
341행 그림 · 값 복사
함수·코드 범위: secondary_holding_pen
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 화살표는 값의 변화를 뜻합니다. 주소값을 옮겨도 그 주소의 메모리 내용은 복사하지 않습니다.
- 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 and x2, x2, x1필요한 비트만 남깁니다. 다른 정보가 섞인 값에서 관심 있는 부분을 고르는 작업입니다. 여기서 결과를 받는 레지스터는 x2입니다.
342행 그림 · 레지스터 상태
함수·코드 범위: secondary_holding_pen
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 결과는 목적 레지스터 폭에 맞습니다. 이 형식은 NZCV를 바꾸지 않습니다.
adr_l x3, secondary_holding_pen_releasex3에 secondary_holding_pen_release의 위치를 계산합니다. 그 위치의 데이터는 아직 읽지 않습니다. 이후의 메모리 접근이나 분기가 사용할 주소를 준비하는 줄입니다.
343행 그림 · 주소·값 구분
함수·코드 범위: secondary_holding_pen
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 이 매크로의 임시 레지스터는 내부에서 사용됩니다.
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
.endmpen: ldr x4, [x3]x3가 가리키는 메모리 위치에 보관된 값을 x4로 가져옵니다. 주소 숫자를 옮기는 MOV와 달리 실제 메모리를 읽습니다.
344행 그림 · 메모리 읽기
함수·코드 범위: secondary_holding_pen
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 주소 계산에 의한 기준 레지스터 갱신은 없습니다. 다만 LDR/LDP의 목적 레지스터가 기준 레지스터와 같으면 읽은 값이 그 레지스터를 덮어씁니다.
cmp x4, x2x4와 x2를 비교하여 조건 플래그에 결과를 남깁니다. 비교한 두 값은 그대로이며, 뒤의 조건 분기가 이 결과를 읽습니다.
345행 그림 · 조건 플래그 계산
함수·코드 범위: secondary_holding_pen
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 일반 레지스터에 연산 결과를 쓰지 않고 조건 플래그만 바꿉니다.
- N은 결과의 부호 비트, Z는 결과가 0인지 나타냅니다. C는 빌림이 없으면 1, V는 부호 있는 뺄셈의 오버플로를 나타냅니다.
b.eq secondary_startup앞서 계산한 조건 플래그가 Z=1이면 secondary_startup로 갑니다. 사이에 플래그를 바꾸지 않는 명령이 있어도 비교 결과는 남아 있습니다.
346행 그림 · 조건별 실행 위치
함수·코드 범위: secondary_holding_pen
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 조건이 맞는 한 경로만 실행합니다. 이 분기 명령은 NZCV를 새로 계산하지 않습니다.
wfe이벤트를 기다릴 수 있도록 CPU에 힌트를 줍니다. 함수가 끝나는 명령은 아니며 깨어나면 다음 줄이 실행될 수 있으므로, 반복 분기나 조건 재검사와 함께 읽습니다.
347행 그림 · 동기화·캐시·대기 상태
함수·코드 범위: secondary_holding_pen
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
b penpen부터 실행을 계속합니다. 돌아올 주소를 새로 적지 않으므로 일반 함수 호출과는 다른 제어 이동입니다.
348행 그림 · 호출과 실행 위치
함수·코드 범위: secondary_holding_pen
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 새 복귀 주소를 LR에 기록하지 않습니다. RET 역시 메모리의 스택 값을 자동으로 꺼내는 명령은 아닙니다.
SYM_FUNC_END(secondary_holding_pen)도구에 이 코드 범위의 끝을 알려 크기와 심볼 정보를 기록합니다. CPU가 이 줄 때문에 자동으로 반환하지는 않습니다.
349행 그림 · 어셈블·링크 단계
함수·코드 범위: secondary_holding_pen
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 끝 표기 자체가 RET나 분기를 실행하지는 않습니다.
SYM_FUNC_START(secondary_entry)이 위치부터 (secondary_entry)라는 코드가 시작한다고 도구에 알려 줍니다. 정렬과 외부 공개 여부도 정의에 포함됩니다. ARM64 함수 시작 매크로는 간접 호출의 도착점을 표시하는 BTI도 추가합니다.
355행 그림 · 어셈블·링크 단계
함수·코드 범위: secondary_entry
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 함수 시작 매크로는 심벌·정렬 정보와 BTI 진입 명령 등을 배치할 수 있습니다. 생성된 명령의 실행과 심벌 선언을 구분합니다.
mov x0, xzrx0에 0을 넣습니다. 원래 x0의 내용은 덮어쓰며, 다른 레지스터나 그 주소의 메모리까지 복사하지는 않습니다.
356행 그림 · 값 복사
함수·코드 범위: secondary_entry
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 화살표는 값의 변화를 뜻합니다. 주소값을 옮겨도 그 주소의 메모리 내용은 복사하지 않습니다.
bl init_kernel_el // w0=cpu_boot_mode펌웨어가 바로 secondary_entry로 보내온 보조 CPU도 EL1 실행 준비와 출발 EL 기록을 수행합니다. holding pen 경로와 합류하기 전에 같은 초기 조건을 만드는 단계입니다. BL은 다음 명령의 주소를 LR(x30)에 기록하고 목적지로 분기합니다. 호출된 코드가 x0 같은 작업 레지스터를 바꿀 수 있으므로, 호출 뒤의 값은 그 코드의 반환·보존 규칙에 따릅니다.
357행 그림 · 호출과 실행 위치
함수·코드 범위: secondary_entry
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 그림은 분기 명령 자체의 효과입니다. 호출된 함수가 실행된 뒤의 레지스터 값은 그 함수의 반환·보존 규칙을 따라야 합니다.
b secondary_startupsecondary_startup부터 실행을 계속합니다. 돌아올 주소를 새로 적지 않으므로 일반 함수 호출과는 다른 제어 이동입니다.
358행 그림 · 호출과 실행 위치
함수·코드 범위: secondary_entry
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 새 복귀 주소를 LR에 기록하지 않습니다. RET 역시 메모리의 스택 값을 자동으로 꺼내는 명령은 아닙니다.
SYM_FUNC_END(secondary_entry)도구에 이 코드 범위의 끝을 알려 크기와 심볼 정보를 기록합니다. CPU가 이 줄 때문에 자동으로 반환하지는 않습니다.
359행 그림 · 어셈블·링크 단계
함수·코드 범위: secondary_entry
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 끝 표기 자체가 RET나 분기를 실행하지는 않습니다.
secondary_startup: 기존 테이블로 MMU 켜기
SYM_FUNC_START_LOCAL(secondary_startup)이 위치부터 (secondary_startup)라는 코드가 시작한다고 도구에 알려 줍니다. 정렬과 외부 공개 여부도 정의에 포함됩니다. ARM64 함수 시작 매크로는 간접 호출의 도착점을 표시하는 BTI도 추가합니다.
361행 그림 · 어셈블·링크 단계
함수·코드 범위: secondary_startup
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 함수 시작 매크로는 심벌·정렬 정보와 BTI 진입 명령 등을 배치할 수 있습니다. 생성된 명령의 실행과 심벌 선언을 구분합니다.
mov x20, x0 // preserve boot modex20에 x0에 들어 있는 값을 넣습니다. 원래 x20의 내용은 덮어쓰며, 다른 레지스터나 그 주소의 메모리까지 복사하지는 않습니다.
365행 그림 · 값 복사
함수·코드 범위: secondary_startup
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 화살표는 값의 변화를 뜻합니다. 주소값을 옮겨도 그 주소의 메모리 내용은 복사하지 않습니다.
#ifdef CONFIG_ARM64_VA_BITS_5252비트 VA를 지원하도록 빌드한 커널에만 보조 CPU의 VA 폭 호환성 검사를 포함합니다. 커널이 실제로 넓은 VA를 사용한다면 이를 지원하지 않는 보조 CPU를 그대로 online시키면 주소 변환에 실패합니다. 이 조건은 빌드 전처리 단계에서 포함할 코드를 고릅니다. CPU가 실행 중 전처리 조건을 검사하지는 않습니다.
367행 그림 · 전처리 단계
함수·코드 범위: secondary_startup
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
alternative_if ARM64_HAS_VA52ARM64_HAS_VA52는 이 시스템에서 52비트 VA 기능을 사용하는 조건입니다. 그 조건이 활성인 경우 검사 호출을 유지하고, 필요 없는 시스템에서는 대안 NOP 경로로 교체하도록 만듭니다. 이 매크로는 기본·교체 명령의 패치 정보를 만들며, CPU가 이 줄을 매번 if 조건처럼 실행하지 않습니다.
368행 그림 · CPU 기능에 따른 명령 패치
함수·코드 범위: secondary_startup
이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_VA_BITS_52
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 빌드할 때 패치 정보를 만들고 커널의 대체 코드 처리에서 명령을 선택합니다. 이 소스 줄을 CPU가 매번 if문으로 실행하는 것은 아닙니다.
bl __cpu_secondary_check52bitva현재 보조 CPU가 시스템에서 사용할 52비트 주소 방식을 지원하는지 읽습니다. 지원하지 않으면 실패 이유를 기록하고 대기하며, 지원하면 돌아와 정상 MMU 준비를 계속합니다. BL은 다음 명령의 주소를 LR(x30)에 기록하고 목적지로 분기합니다. 호출된 코드가 x0 같은 작업 레지스터를 바꿀 수 있으므로, 호출 뒤의 값은 그 코드의 반환·보존 규칙에 따릅니다.
369행 그림 · 호출과 실행 위치
함수·코드 범위: secondary_startup
이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_VA_BITS_52
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 그림은 분기 명령 자체의 효과입니다. 호출된 함수가 실행된 뒤의 레지스터 값은 그 함수의 반환·보존 규칙을 따라야 합니다.
alternative_else_nop_endif앞의 52비트 VA 검사가 필요하지 않은 대안은 같은 크기의 NOP입니다. 빈 조건을 실행하는 문장이 아니라 부팅 시 코드 교체를 위한 분기 정의의 끝입니다. 이 매크로는 기본·교체 명령의 패치 정보를 만들며, CPU가 이 줄을 매번 if 조건처럼 실행하지 않습니다.
370행 그림 · CPU 기능에 따른 명령 패치
함수·코드 범위: secondary_startup
이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_VA_BITS_52
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 빌드할 때 패치 정보를 만들고 커널의 대체 코드 처리에서 명령을 선택합니다. 이 소스 줄을 CPU가 매번 if문으로 실행하는 것은 아닙니다.
#endif빌드 조건으로 나누었던 구간을 닫습니다. 대상:
371행 그림 · 전처리 단계
함수·코드 범위: secondary_startup
이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_VA_BITS_52
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
bl __cpu_setup // initialise processor이 보조 CPU의 TLB와 주소 변환 속성을 초기화하고 SCTLR 값 x0를 준비합니다. 부트 CPU에서 했더라도 시스템 레지스터는 CPU마다 별도로 설정해야 합니다. BL은 다음 명령의 주소를 LR(x30)에 기록하고 목적지로 분기합니다. 호출된 코드가 x0 같은 작업 레지스터를 바꿀 수 있으므로, 호출 뒤의 값은 그 코드의 반환·보존 규칙에 따릅니다.
373행 그림 · 호출과 실행 위치
함수·코드 범위: secondary_startup
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 그림은 분기 명령 자체의 효과입니다. 호출된 함수가 실행된 뒤의 레지스터 값은 그 함수의 반환·보존 규칙을 따라야 합니다.
adrp x1, swapper_pg_dirx1에 swapper_pg_dir의 위치를 계산합니다. 그 위치의 데이터는 아직 읽지 않습니다. ADRP는 4 KiB 페이지의 시작만 계산하므로 세부 위치는 다음 오프셋 연산에서 더합니다.
374행 그림 · 주소·값 구분
함수·코드 범위: secondary_startup
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- ADRP의 페이지 단위는 4KiB입니다.
adrp x2, idmap_pg_dirx2에 idmap_pg_dir의 위치를 계산합니다. 그 위치의 데이터는 아직 읽지 않습니다. ADRP는 4 KiB 페이지의 시작만 계산하므로 세부 위치는 다음 오프셋 연산에서 더합니다.
375행 그림 · 주소·값 구분
함수·코드 범위: secondary_startup
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- ADRP의 페이지 단위는 4KiB입니다.
bl __enable_mmux1=swapper_pg_dir, x2=idmap_pg_dir, x0=SCTLR 설정으로 보조 CPU의 MMU를 켭니다. 페이지 granule 지원을 검사하고 TTBR0·TTBR1을 설치하므로 뒤의 가상 주소 분기가 유효해집니다. BL은 다음 명령의 주소를 LR(x30)에 기록하고 목적지로 분기합니다. 호출된 코드가 x0 같은 작업 레지스터를 바꿀 수 있으므로, 호출 뒤의 값은 그 코드의 반환·보존 규칙에 따릅니다.
376행 그림 · 호출과 실행 위치
함수·코드 범위: secondary_startup
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 그림은 분기 명령 자체의 효과입니다. 호출된 함수가 실행된 뒤의 레지스터 값은 그 함수의 반환·보존 규칙을 따라야 합니다.
ldr x8, =__secondary_switchedx8에 __secondary_switched의 주소 상수를 준비합니다. 그 함수나 변수의 본문을 복사하는 것이 아니라 이후 이동하거나 접근할 위치를 얻습니다.
377행 그림 · 메모리 읽기
함수·코드 범위: secondary_startup
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- =심벌이면 주소 상수를 리터럴 풀에서 읽는 의사 명령입니다. 레이블만 적힌 LDR은 해당 위치의 메모리를 읽습니다.
br x8x8에 준비한 주소부터 실행합니다. 주소가 가리키는 값을 메모리에서 읽는 것이 아니라 CPU의 다음 실행 위치를 바꿉니다. 새 복귀 주소는 만들지 않습니다.
378행 그림 · 호출과 실행 위치
함수·코드 범위: secondary_startup
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 새 복귀 주소를 LR에 기록하지 않습니다. RET 역시 메모리의 스택 값을 자동으로 꺼내는 명령은 아닙니다.
SYM_FUNC_END(secondary_startup)도구에 이 코드 범위의 끝을 알려 크기와 심볼 정보를 기록합니다. CPU가 이 줄 때문에 자동으로 반환하지는 않습니다.
379행 그림 · 어셈블·링크 단계
함수·코드 범위: secondary_startup
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 끝 표기 자체가 RET나 분기를 실행하지는 않습니다.
__secondary_switched와 시작할 태스크가 없는 보조 CPU
.text이후 출력할 바이트를 어느 섹션에 넣을지 바꿉니다. 빌드 도구가 코드를 분류하는 작업이며 현재 CPU의 PC나 SP를 옮기는 줄은 아닙니다.
381행 그림 · 어셈블·링크 단계
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
SYM_FUNC_START_LOCAL(__secondary_switched)이 위치부터 (__secondary_switched)라는 코드가 시작한다고 도구에 알려 줍니다. 정렬과 외부 공개 여부도 정의에 포함됩니다. ARM64 함수 시작 매크로는 간접 호출의 도착점을 표시하는 BTI도 추가합니다.
382행 그림 · 어셈블·링크 단계
함수·코드 범위: __secondary_switched
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 함수 시작 매크로는 심벌·정렬 정보와 BTI 진입 명령 등을 배치할 수 있습니다. 생성된 명령의 실행과 심벌 선언을 구분합니다.
mov x0, x20x0에 x20에 들어 있는 값을 넣습니다. 원래 x0의 내용은 덮어쓰며, 다른 레지스터나 그 주소의 메모리까지 복사하지는 않습니다.
383행 그림 · 값 복사
함수·코드 범위: __secondary_switched
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 화살표는 값의 변화를 뜻합니다. 주소값을 옮겨도 그 주소의 메모리 내용은 복사하지 않습니다.
bl set_cpu_boot_mode_flag보조 CPU가 어떤 EL에서 출발했는지를 공유 부팅 기록에 남깁니다. 현재 EL을 읽는 동작이 아니라 앞에서 x20에 보존한 입력을 기록합니다. BL은 다음 명령의 주소를 LR(x30)에 기록하고 목적지로 분기합니다.
384행 그림 · 호출과 실행 위치
함수·코드 범위: __secondary_switched
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 그림은 분기 명령 자체의 효과입니다. 호출된 함수가 실행된 뒤의 레지스터 값은 그 함수의 반환·보존 규칙을 따라야 합니다.
mov x0, x20x0에 x20에 들어 있는 값을 넣습니다. 원래 x0의 내용은 덮어쓰며, 다른 레지스터나 그 주소의 메모리까지 복사하지는 않습니다.
386행 그림 · 값 복사
함수·코드 범위: __secondary_switched
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 화살표는 값의 변화를 뜻합니다. 주소값을 옮겨도 그 주소의 메모리 내용은 복사하지 않습니다.
bl finalise_el2보조 CPU도 출발 EL 및 VHE 조건에 맞춰 EL2 상태를 마무리합니다. 부트 CPU와 실행 레벨 정책을 맞추는 과정이며 언제나 EL2로 올라간다고 단정하지 않습니다. BL은 다음 명령의 주소를 LR(x30)에 기록하고 목적지로 분기합니다. 호출된 코드가 x0 같은 작업 레지스터를 바꿀 수 있으므로, 호출 뒤의 값은 그 코드의 반환·보존 규칙에 따릅니다.
387행 그림 · 호출과 실행 위치
함수·코드 범위: __secondary_switched
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 그림은 분기 명령 자체의 효과입니다. 호출된 함수가 실행된 뒤의 레지스터 값은 그 함수의 반환·보존 규칙을 따라야 합니다.
str_l xzr, __early_cpu_boot_status, x3보조 CPU의 초기 부팅 상태를 0으로 지웁니다. 이 위치까지 정상적으로 CPU 초기화·MMU 전환을 마쳤다는 흐름을 다른 CPU가 읽을 상태 변수에 반영합니다. str_l은 임시 레지스터로 심벌의 주소를 구하고 STR로 그 메모리에 값을 쓰는 매크로입니다.
389행 그림 · 주소·값 구분
함수·코드 범위: __secondary_switched
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 주소 계산에 쓰는 임시 레지스터도 바뀝니다.
str_l 매크로 본문 · 203행
arch/arm64/include/asm/assembler.h · Linux v6.18.37
정의 선택 조건: #ifndef __ASM_ASSEMBLER_H
.macro str_l, src, sym, tmp
adrp \tmp, \sym
str \src, [\tmp, :lo12:\sym]
.endm adr_l x5, vectorsx5에 vectors의 위치를 계산합니다. 그 위치의 데이터는 아직 읽지 않습니다. 이후의 메모리 접근이나 분기가 사용할 주소를 준비하는 줄입니다.
390행 그림 · 주소·값 구분
함수·코드 범위: __secondary_switched
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 이 매크로의 임시 레지스터는 내부에서 사용됩니다.
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 msr vbar_el1, x5보조 CPU의 VBAR_EL1에 정상 커널 벡터 주소를 설치합니다. 같은 vectors를 사용하더라도 레지스터는 각 CPU에 설정해야 합니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.
391행 그림 · 시스템 레지스터 쓰기
함수·코드 범위: __secondary_switched
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 접근 권한과 VHE 별칭 규칙에 따라 실제 대상이 정해집니다. 뒤의 ISB·DSB가 필요한지는 이 레지스터와 문맥에 따릅니다.
isb뒤 명령을 변경된 CPU 제어 상태에 맞춰 실행하도록 동기화합니다. 주소 변환이나 벡터 설정을 바꾼 뒤 예전 실행 상태가 계속 쓰이지 않게 합니다.
392행 그림 · 동기화·캐시·대기 상태
함수·코드 범위: __secondary_switched
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
adr_l x0, secondary_datax0에 secondary_data의 위치를 계산합니다. 그 위치의 데이터는 아직 읽지 않습니다. 이후의 메모리 접근이나 분기가 사용할 주소를 준비하는 줄입니다.
394행 그림 · 주소·값 구분
함수·코드 범위: __secondary_switched
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 이 매크로의 임시 레지스터는 내부에서 사용됩니다.
adr_l 매크로 본문 · 175행
arch/arm64/include/asm/assembler.h · Linux v6.18.37
정의 선택 조건: #ifndef __ASM_ASSEMBLER_H
.macro adr_l, dst, sym
adrp \dst, \sym
add \dst, \dst, :lo12:\sym
.endm ldr x2, [x0, #CPU_BOOT_TASK]x0를 기준으로 CPU_BOOT_TASK만큼 계산한 메모리 위치에 보관된 값을 x2로 가져옵니다. 주소 숫자를 옮기는 MOV와 달리 실제 메모리를 읽습니다.
395행 그림 · 메모리 읽기
함수·코드 범위: __secondary_switched
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 주소 계산에 의한 기준 레지스터 갱신은 없습니다. 다만 LDR/LDP의 목적 레지스터가 기준 레지스터와 같으면 읽은 값이 그 레지스터를 덮어씁니다.
cbz x2, __secondary_too_slowx2의 값가 0이면 __secondary_too_slow로 갑니다. 조건을 만족하지 않으면 바로 다음 명령을 읽습니다. 이 분기는 CMP의 결과가 아니라 지정 레지스터를 직접 검사합니다.
396행 그림 · 조건별 실행 위치
함수·코드 범위: __secondary_switched
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 조건이 맞는 한 경로만 실행합니다. 이 분기 명령은 NZCV를 새로 계산하지 않습니다.
init_cpu_task x2, x1, x3CPU가 사용할 태스크, 커널 스택, 호출 프레임 끝, CPU별 변수 접근 기준을 준비합니다. C 코드를 실행하기 위한 주변 상태를 맞추는 단계입니다.
398행 그림 · 매크로 전체의 주요 상태
함수·코드 범위: __secondary_switched
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- SP_EL0는 task 포인터이고 실제 스택 주소는 SP입니다. SCS 사용 시 x18도 읽어 옵니다. 인자로 준 임시 레지스터의 중간값은 본문에 있습니다.
- 여러 명령과 조건부 경로를 묶은 그림입니다. 이 목록이 모든 임시 레지스터의 변경을 열거하는 것은 아닙니다. 아래 매크로 본문에서 각 단계를 확인하실 수 있습니다.
init_cpu_task 매크로 본문 · 195행
arch/arm64/kernel/head.S · Linux v6.18.37
.macro init_cpu_task tsk, tmp1, tmp2
msr sp_el0, \tsk
ldr \tmp1, [\tsk, #TSK_STACK]
add sp, \tmp1, #THREAD_SIZE
sub sp, sp, #PT_REGS_SIZE
stp xzr, xzr, [sp, #S_STACKFRAME]
mov \tmp1, #FRAME_META_TYPE_FINAL
str \tmp1, [sp, #S_STACKFRAME_TYPE]
add x29, sp, #S_STACKFRAME
scs_load_current
adr_l \tmp1, __per_cpu_offset
ldr w\tmp2, [\tsk, #TSK_TI_CPU]
ldr \tmp1, [\tmp1, \tmp2, lsl #3]
set_this_cpu_offset \tmp1
.endm#ifdef CONFIG_ARM64_PTR_AUTH포인터 인증 지원을 빌드한 경우에 보조 CPU의 키·제어 상태 초기화를 포함합니다. PAC는 비밀 키로 포인터를 인증하는 기능이며, 꺼진 빌드는 이 준비를 생략합니다. 실제 CPU 지원 여부는 매크로 내부 조건도 따릅니다. 이 조건은 빌드 전처리 단계에서 포함할 코드를 고릅니다. CPU가 실행 중 전처리 조건을 검사하지는 않습니다.
400행 그림 · 전처리 단계
함수·코드 범위: __secondary_switched
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
ptrauth_keys_init_cpu x2, x3, x4, x5현재 태스크와 CPU 설정에 맞게 포인터 인증 상태를 준비합니다. 키와 제어 설정이 일치해야 이후 인증된 복귀가 동작합니다.
401행 그림 · 매크로 안의 상태 변화
함수·코드 범위: __secondary_switched
이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_PTR_AUTH
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 여러 명령과 내부 분기를 묶은 그림입니다. 작업 레지스터를 모두 보존한다는 뜻은 아니며, 정확한 변경 순서는 아래 매크로 본문과 줄별 그림에서 확인합니다.
ptrauth_keys_init_cpu 매크로 본문 · 77행
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_init_cpu tsk, tmp1, tmp2, tmp3
alternative_if_not ARM64_HAS_ADDRESS_AUTH
b .Lno_addr_auth\@
alternative_else_nop_endif
__ptrauth_keys_init_cpu \tsk, \tmp1, \tmp2, \tmp3
.Lno_addr_auth\@:
.endm#endif빌드 조건으로 나누었던 구간을 닫습니다. 대상:
402행 그림 · 전처리 단계
함수·코드 범위: __secondary_switched
이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_PTR_AUTH
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
bl secondary_start_kernel준비된 idle task와 스택을 사용해 보조 CPU의 C 초기화 secondary_start_kernel로 들어갑니다. CPU 기능·인터럽트·스케줄러 준비 후 online 및 idle 실행으로 이어지며 정상 반환을 기대하지 않습니다. BL은 다음 명령의 주소를 LR(x30)에 기록하고 목적지로 분기합니다. 호출된 코드가 x0 같은 작업 레지스터를 바꿀 수 있으므로, 호출 뒤의 값은 그 코드의 반환·보존 규칙에 따릅니다.
404행 그림 · 호출과 실행 위치
함수·코드 범위: __secondary_switched
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 그림은 분기 명령 자체의 효과입니다. 호출된 함수가 실행된 뒤의 레지스터 값은 그 함수의 반환·보존 규칙을 따라야 합니다.
ASM_BUG()secondary_start_kernel은 정상적으로 반환하지 않습니다. 뜻하지 않게 돌아오면 잘못된 복귀 주소로 계속 실행하지 않고 BUG 예외로 멈춥니다. 어셈블 단계에는 진단용 BUG 메타데이터와 BRK 인코딩을 배치하고, 실행이 이 BRK에 도달하면 예외가 발생합니다.
405행 그림 · 매크로 전체의 상태 변화
함수·코드 범위: __secondary_switched
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 오류 메타데이터와 BRK 명령을 함께 만듭니다. HVC와 달리 ELR에는 BRK 자신을 가리키는 주소가 저장됩니다.
SYM_FUNC_END(__secondary_switched)도구에 이 코드 범위의 끝을 알려 크기와 심볼 정보를 기록합니다. CPU가 이 줄 때문에 자동으로 반환하지는 않습니다.
406행 그림 · 어셈블·링크 단계
함수·코드 범위: __secondary_switched
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 끝 표기 자체가 RET나 분기를 실행하지는 않습니다.
SYM_FUNC_START_LOCAL(__secondary_too_slow)이 위치부터 (__secondary_too_slow)라는 코드가 시작한다고 도구에 알려 줍니다. 정렬과 외부 공개 여부도 정의에 포함됩니다. ARM64 함수 시작 매크로는 간접 호출의 도착점을 표시하는 BTI도 추가합니다.
408행 그림 · 어셈블·링크 단계
함수·코드 범위: __secondary_too_slow
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 함수 시작 매크로는 심벌·정렬 정보와 BTI 진입 명령 등을 배치할 수 있습니다. 생성된 명령의 실행과 심벌 선언을 구분합니다.
wfe이벤트를 기다릴 수 있도록 CPU에 힌트를 줍니다. 함수가 끝나는 명령은 아니며 깨어나면 다음 줄이 실행될 수 있으므로, 반복 분기나 조건 재검사와 함께 읽습니다.
409행 그림 · 동기화·캐시·대기 상태
함수·코드 범위: __secondary_too_slow
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
wfi인터럽트를 기다릴 수 있도록 CPU에 힌트를 줍니다. 함수가 끝나는 명령은 아니며 깨어나면 다음 줄이 실행될 수 있으므로, 반복 분기나 조건 재검사와 함께 읽습니다.
410행 그림 · 동기화·캐시·대기 상태
함수·코드 범위: __secondary_too_slow
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
b __secondary_too_slow__secondary_too_slow부터 실행을 계속합니다. 돌아올 주소를 새로 적지 않으므로 일반 함수 호출과는 다른 제어 이동입니다.
411행 그림 · 호출과 실행 위치
함수·코드 범위: __secondary_too_slow
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 새 복귀 주소를 LR에 기록하지 않습니다. RET 역시 메모리의 스택 값을 자동으로 꺼내는 명령은 아닙니다.
SYM_FUNC_END(__secondary_too_slow)도구에 이 코드 범위의 끝을 알려 크기와 심볼 정보를 기록합니다. CPU가 이 줄 때문에 자동으로 반환하지는 않습니다.
412행 그림 · 어셈블·링크 단계
함수·코드 범위: __secondary_too_slow
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 끝 표기 자체가 RET나 분기를 실행하지는 않습니다.
set_cpu_boot_mode_flag: 32비트 슬롯 두 개
SYM_FUNC_START_LOCAL(set_cpu_boot_mode_flag)이 위치부터 (set_cpu_boot_mode_flag)라는 코드가 시작한다고 도구에 알려 줍니다. 정렬과 외부 공개 여부도 정의에 포함됩니다. ARM64 함수 시작 매크로는 간접 호출의 도착점을 표시하는 BTI도 추가합니다.
418행 그림 · 어셈블·링크 단계
함수·코드 범위: set_cpu_boot_mode_flag
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 함수 시작 매크로는 심벌·정렬 정보와 BTI 진입 명령 등을 배치할 수 있습니다. 생성된 명령의 실행과 심벌 선언을 구분합니다.
adr_l x1, __boot_cpu_modex1에 __boot_cpu_mode의 위치를 계산합니다. 그 위치의 데이터는 아직 읽지 않습니다. 이후의 메모리 접근이나 분기가 사용할 주소를 준비하는 줄입니다.
419행 그림 · 주소·값 구분
함수·코드 범위: set_cpu_boot_mode_flag
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 이 매크로의 임시 레지스터는 내부에서 사용됩니다.
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 cmp w0, #BOOT_CPU_MODE_EL2w0와 BOOT_CPU_MODE_EL2를 비교하여 조건 플래그에 결과를 남깁니다. 비교한 두 값은 그대로이며, 뒤의 조건 분기가 이 결과를 읽습니다.
420행 그림 · 조건 플래그 계산
함수·코드 범위: set_cpu_boot_mode_flag
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 일반 레지스터에 연산 결과를 쓰지 않고 조건 플래그만 바꿉니다.
- N은 결과의 부호 비트, Z는 결과가 0인지 나타냅니다. C는 빌림이 없으면 1, V는 부호 있는 뺄셈의 오버플로를 나타냅니다.
b.ne 1f앞서 계산한 조건 플래그가 Z=0이면 1f (L423)로 갑니다. 사이에 플래그를 바꾸지 않는 명령이 있어도 비교 결과는 남아 있습니다.
421행 그림 · 조건별 실행 위치
함수·코드 범위: set_cpu_boot_mode_flag
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 조건이 맞는 한 경로만 실행합니다. 이 분기 명령은 NZCV를 새로 계산하지 않습니다.
add x1, x1, #4x1를 기준으로 4을 더해 x1에 결과를 둡니다. 주소 계산에 쓰이는 경우에는 구조체의 필드나 다음 저장 위치를 찾는 동작입니다. 이 형태는 조건 플래그를 바꾸지 않습니다.
422행 그림 · 레지스터 상태
함수·코드 범위: set_cpu_boot_mode_flag
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 결과는 목적 레지스터 폭에 맞습니다. 이 형식은 NZCV를 바꾸지 않습니다.
1: str w0, [x1] // Save CPU boot modew0의 값을 x1가 가리키는 메모리 위치에 기록합니다. 레지스터 값을 나중에 다시 쓰거나 다른 코드가 읽을 수 있도록 메모리에 남기는 줄입니다.
423행 그림 · 메모리 쓰기
함수·코드 범위: set_cpu_boot_mode_flag
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 주소 계산에 의한 기준 레지스터 갱신은 없습니다. 다만 LDR/LDP의 목적 레지스터가 기준 레지스터와 같으면 읽은 값이 그 레지스터를 덮어씁니다.
retLR(x30)에 적힌 주소로 실행을 돌립니다. 그 주소가 어떻게 준비됐는지가 중요합니다. 정상 함수의 호출자일 수도 있고, 절전 복귀나 trampoline에서 별도로 기록한 재개 지점일 수도 있습니다.
424행 그림 · 호출과 실행 위치
함수·코드 범위: set_cpu_boot_mode_flag
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 새 복귀 주소를 LR에 기록하지 않습니다. RET 역시 메모리의 스택 값을 자동으로 꺼내는 명령은 아닙니다.
SYM_FUNC_END(set_cpu_boot_mode_flag)도구에 이 코드 범위의 끝을 알려 크기와 심볼 정보를 기록합니다. CPU가 이 줄 때문에 자동으로 반환하지는 않습니다.
425행 그림 · 어셈블·링크 단계
함수·코드 범위: set_cpu_boot_mode_flag
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 끝 표기 자체가 RET나 분기를 실행하지는 않습니다.
MMU off 실패 상태를 외부에 남기는 매크로
.macro update_early_cpu_boot_status status, tmp1, tmp2초기 CPU가 더 진행하지 못한 이유를 __early_cpu_boot_status에 쓰는 매크로입니다. status는 실패 종류, tmp1·tmp2는 임시 레지스터이며 MMU off 상태에서도 다른 CPU가 볼 수 있도록 장벽과 캐시 무효화를 포함합니다. 이 줄은 어셈블러 매크로 정의의 시작입니다. 본문은 매크로를 사용하는 위치에 펼쳐지며, 정의 자체가 런타임 함수 호출은 아닙니다.
437행 그림 · 어셈블·링크 단계
매크로 본문: update_early_cpu_boot_status
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 뒤의 본문은 이 이름을 사용하는 위치에 펼쳐집니다. 정의 위치를 CPU가 함수처럼 호출하지 않습니다.
mov \tmp2, #\status\tmp2에 \status라는 값을 넣습니다. 원래 \tmp2의 내용은 덮어쓰며, 다른 레지스터나 그 주소의 메모리까지 복사하지는 않습니다.
438행 그림 · 값 복사
매크로 본문: update_early_cpu_boot_status
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 화살표는 값의 변화를 뜻합니다. 주소값을 옮겨도 그 주소의 메모리 내용은 복사하지 않습니다.
- 매크로 update_early_cpu_boot_status의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
adr_l \tmp1, __early_cpu_boot_status\tmp1에 __early_cpu_boot_status의 위치를 계산합니다. 그 위치의 데이터는 아직 읽지 않습니다. 이후의 메모리 접근이나 분기가 사용할 주소를 준비하는 줄입니다.
439행 그림 · 주소·값 구분
매크로 본문: update_early_cpu_boot_status
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 이 매크로의 임시 레지스터는 내부에서 사용됩니다.
- 매크로 update_early_cpu_boot_status의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
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 str \tmp2, [\tmp1]\tmp2의 값을 \tmp1가 가리키는 메모리 위치에 기록합니다. 레지스터 값을 나중에 다시 쓰거나 다른 코드가 읽을 수 있도록 메모리에 남기는 줄입니다.
440행 그림 · 메모리 쓰기
매크로 본문: update_early_cpu_boot_status
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 주소 계산에 의한 기준 레지스터 갱신은 없습니다. 다만 LDR/LDP의 목적 레지스터가 기준 레지스터와 같으면 읽은 값이 그 레지스터를 덮어씁니다.
- 매크로 update_early_cpu_boot_status의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
dmb sy앞뒤의 메모리 접근이 필요한 순서로 관찰되도록 제한합니다. “먼저 썼으니 다른 CPU도 먼저 볼 것”이라는 가정을 명시적인 규칙으로 만드는 줄입니다. 캐시를 직접 비우는 명령은 아닙니다.
441행 그림 · 동기화·캐시·대기 상태
매크로 본문: update_early_cpu_boot_status
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 매크로 update_early_cpu_boot_status의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
dc ivac, \tmp1 // Invalidate potentially stale cache line데이터 캐시의 해당 주소 라인에 ivac, \tmp1 작업을 적용합니다. 일부 바이트를 지정해도 관리 단위는 캐시 라인이므로 이웃 데이터와 정렬을 함께 고려해야 합니다.
442행 그림 · 동기화·캐시·대기 상태
매크로 본문: update_early_cpu_boot_status
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 관리 요청과 완료 보장은 구분됩니다. 주변 DSB·ISB와 함께 해석합니다.
- 매크로 update_early_cpu_boot_status의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
.endm공통 작업의 정의를 여기서 마칩니다. RET가 아니라 빌드 도구가 읽는 경계입니다.
443행 그림 · 어셈블·링크 단계
매크로 본문: update_early_cpu_boot_status
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 끝 표기 자체가 RET나 분기를 실행하지는 않습니다.
__enable_mmu: TTBR 설치와 MMU를 켠 뒤에도 실행이 이어지는 이유
.section ".idmap.text","a"이후 출력할 바이트를 어느 섹션에 넣을지 바꿉니다. 빌드 도구가 코드를 분류하는 작업이며 현재 CPU의 PC나 SP를 옮기는 줄은 아닙니다. ".idmap.text","a"
458행 그림 · 어셈블·링크 단계
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
SYM_FUNC_START(__enable_mmu)이 위치부터 (__enable_mmu)라는 코드가 시작한다고 도구에 알려 줍니다. 정렬과 외부 공개 여부도 정의에 포함됩니다. ARM64 함수 시작 매크로는 간접 호출의 도착점을 표시하는 BTI도 추가합니다.
459행 그림 · 어셈블·링크 단계
함수·코드 범위: __enable_mmu
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 함수 시작 매크로는 심벌·정렬 정보와 BTI 진입 명령 등을 배치할 수 있습니다. 생성된 명령의 실행과 심벌 선언을 구분합니다.
mrs x3, ID_AA64MMFR0_EL1메모리 모델 기능 ID_AA64MMFR0_EL1을 읽습니다. 바로 뒤에서 현재 빌드의 4KiB·16KiB·64KiB 중 선택한 translation granule을 이 CPU가 지원하는지 판별합니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.
460행 그림 · 시스템 레지스터 읽기
함수·코드 범위: __enable_mmu
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 일반 RAM의 메모리 접근과 구분합니다.
ubfx x3, x3, #ID_AA64MMFR0_EL1_TGRAN_SHIFT, 4빌드한 PAGE_SIZE에 대응하는 TGRAN 필드 4비트를 추출합니다. 임의의 가상 주소 비트를 자르는 것이 아니라 하드웨어 지원 범위를 읽는 동작입니다. 추출한 필드는 결과의 하위 비트에 놓이고 나머지 상위 비트는 0으로 채워집니다.
461행 그림 · 비트 필드 추출
함수·코드 범위: __enable_mmu
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
cmp x3, #ID_AA64MMFR0_EL1_TGRAN_SUPPORTED_MINx3와 ID_AA64MMFR0_EL1_TGRAN_SUPPORTED_MIN를 비교하여 조건 플래그에 결과를 남깁니다. 비교한 두 값은 그대로이며, 뒤의 조건 분기가 이 결과를 읽습니다.
462행 그림 · 조건 플래그 계산
함수·코드 범위: __enable_mmu
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 일반 레지스터에 연산 결과를 쓰지 않고 조건 플래그만 바꿉니다.
- N은 결과의 부호 비트, Z는 결과가 0인지 나타냅니다. C는 빌림이 없으면 1, V는 부호 있는 뺄셈의 오버플로를 나타냅니다.
b.lt __no_granule_support앞서 계산한 조건 플래그가 N≠V (부호 있는 미만)이면 __no_granule_support로 갑니다. 사이에 플래그를 바꾸지 않는 명령이 있어도 비교 결과는 남아 있습니다.
463행 그림 · 조건별 실행 위치
함수·코드 범위: __enable_mmu
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 조건이 맞는 한 경로만 실행합니다. 이 분기 명령은 NZCV를 새로 계산하지 않습니다.
cmp x3, #ID_AA64MMFR0_EL1_TGRAN_SUPPORTED_MAXx3와 ID_AA64MMFR0_EL1_TGRAN_SUPPORTED_MAX를 비교하여 조건 플래그에 결과를 남깁니다. 비교한 두 값은 그대로이며, 뒤의 조건 분기가 이 결과를 읽습니다.
464행 그림 · 조건 플래그 계산
함수·코드 범위: __enable_mmu
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 일반 레지스터에 연산 결과를 쓰지 않고 조건 플래그만 바꿉니다.
- N은 결과의 부호 비트, Z는 결과가 0인지 나타냅니다. C는 빌림이 없으면 1, V는 부호 있는 뺄셈의 오버플로를 나타냅니다.
b.gt __no_granule_support앞서 계산한 조건 플래그가 Z=0이고 N=V이면 __no_granule_support로 갑니다. 사이에 플래그를 바꾸지 않는 명령이 있어도 비교 결과는 남아 있습니다.
465행 그림 · 조건별 실행 위치
함수·코드 범위: __enable_mmu
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 조건이 맞는 한 경로만 실행합니다. 이 분기 명령은 NZCV를 새로 계산하지 않습니다.
phys_to_ttbr x2, x2페이지 테이블의 물리 주소를 TTBR이 요구하는 비트 배치로 바꿉니다. 주소 숫자를 무조건 그대로 쓰는 것과는 다릅니다.
466행 그림 · 매크로 전체의 상태 변화
함수·코드 범위: __enable_mmu
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
phys_to_ttbr 매크로 본문 · 606행
arch/arm64/include/asm/assembler.h · Linux v6.18.37
정의 선택 조건: #ifndef __ASM_ASSEMBLER_H
.macro phys_to_ttbr, ttbr, phys
#ifdef CONFIG_ARM64_PA_BITS_52
orr \ttbr, \phys, \phys, lsr #46
and \ttbr, \ttbr, #TTBR_BADDR_MASK_52
#else
mov \ttbr, \phys
#endif
.endm msr ttbr0_el1, x2 // load TTBR0MMU on 전후 같은 코드 주소를 사용할 idmap 루트 x2를 TTBR0_EL1에 넣습니다. TTBR1의 커널 가상 매핑은 다음 load_ttbr1이 맡습니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.
467행 그림 · 시스템 레지스터 쓰기
함수·코드 범위: __enable_mmu
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 접근 권한과 VHE 별칭 규칙에 따라 실제 대상이 정해집니다. 뒤의 ISB·DSB가 필요한지는 이 레지스터와 문맥에 따릅니다.
load_ttbr1 x1, x1, x3x1의 커널 페이지 표 주소를 TTBR1 형식으로 바꾸고 선택된 주소 폭·페이지 크기에 필요한 보정을 거쳐 설치합니다. 이어지는 ISB까지 포함하여 새 커널 매핑이 뒤의 명령 실행에 적용되도록 합니다.
468행 그림 · 매크로 안의 상태 변화
함수·코드 범위: __enable_mmu
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 여러 명령과 내부 분기를 묶은 그림입니다. 작업 레지스터를 모두 보존한다는 뜻은 아니며, 정확한 변경 순서는 아래 매크로 본문과 줄별 그림에서 확인합니다.
load_ttbr1 매크로 본문 · 454행
arch/arm64/include/asm/assembler.h · Linux v6.18.37
정의 선택 조건: #ifndef __ASM_ASSEMBLER_H
.macro load_ttbr1, pgtbl, tmp1, tmp2
phys_to_ttbr \tmp1, \pgtbl
offset_ttbr1 \tmp1, \tmp2
msr ttbr1_el1, \tmp1
isb
.endm set_sctlr_el1 x0__cpu_setup이 x0에 준비한 SCTLR 값을 설치하여 MMU·캐시 설정을 적용합니다. set_sctlr_el1 매크로가 요구되는 실행 동기화와 보완도 포함합니다. 내부에서는 SCTLR_EL1을 쓴 뒤 ISB를 수행하고, I-cache 무효화와 DSB NSH·ISB로 명령 실행 상태를 동기화합니다.
470행 그림 · 매크로 전체의 상태 변화
함수·코드 범위: __enable_mmu
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- MSR SCTLR_EL1→ISB→IC IALLU→DSB NSH→ISB로 제어 상태와 명령어 캐시를 동기화합니다.
set_sctlr_el1 매크로 본문 · 733행
arch/arm64/include/asm/assembler.h · Linux v6.18.37
정의 선택 조건: #ifndef __ASM_ASSEMBLER_H
.macro set_sctlr_el1, reg
set_sctlr sctlr_el1, \reg
.endm retLR(x30)에 적힌 주소로 실행을 돌립니다. 그 주소가 어떻게 준비됐는지가 중요합니다. 정상 함수의 호출자일 수도 있고, 절전 복귀나 trampoline에서 별도로 기록한 재개 지점일 수도 있습니다.
472행 그림 · 호출과 실행 위치
함수·코드 범위: __enable_mmu
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 새 복귀 주소를 LR에 기록하지 않습니다. RET 역시 메모리의 스택 값을 자동으로 꺼내는 명령은 아닙니다.
SYM_FUNC_END(__enable_mmu)도구에 이 코드 범위의 끝을 알려 크기와 심볼 정보를 기록합니다. CPU가 이 줄 때문에 자동으로 반환하지는 않습니다.
473행 그림 · 어셈블·링크 단계
함수·코드 범위: __enable_mmu
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 끝 표기 자체가 RET나 분기를 실행하지는 않습니다.
지원하지 않는 VA 폭·페이지 크기의 실패 경로
#ifdef CONFIG_ARM64_VA_BITS_5252비트 VA 지원 빌드에서만 아래 보조 CPU 기능 검사 함수 자체를 만듭니다. 일반 VA 폭 빌드에는 이 함수가 필요 없습니다. 이 조건은 빌드 전처리 단계에서 포함할 코드를 고릅니다. CPU가 실행 중 전처리 조건을 검사하지는 않습니다.
475행 그림 · 전처리 단계
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
SYM_FUNC_START(__cpu_secondary_check52bitva)이 위치부터 (__cpu_secondary_check52bitva)라는 코드가 시작한다고 도구에 알려 줍니다. 정렬과 외부 공개 여부도 정의에 포함됩니다. ARM64 함수 시작 매크로는 간접 호출의 도착점을 표시하는 BTI도 추가합니다.
476행 그림 · 어셈블·링크 단계
함수·코드 범위: __cpu_secondary_check52bitva
이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_VA_BITS_52
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 함수 시작 매크로는 심벌·정렬 정보와 BTI 진입 명령 등을 배치할 수 있습니다. 생성된 명령의 실행과 심벌 선언을 구분합니다.
#ifndef CONFIG_ARM64_LPA2LPA2를 사용하지 않는 52비트 VA 구성에서는 MMFR2의 VARange로 지원 여부를 검사합니다. LPA2 구성은 뒤의 #else에서 translation granule의 LPA2 인코딩을 검사하므로 두 방식의 기능 레지스터가 다릅니다. 이 조건은 빌드 전처리 단계에서 포함할 코드를 고릅니다. CPU가 실행 중 전처리 조건을 검사하지는 않습니다.
477행 그림 · 전처리 단계
함수·코드 범위: __cpu_secondary_check52bitva
이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_VA_BITS_52
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
mrs_s x0, SYS_ID_AA64MMFR2_EL1ID_AA64MMFR2_EL1의 가상 주소 범위 기능을 읽습니다. VARange가 넓은 VA를 지원하는지를 판단하여 이 보조 CPU가 시스템 주소 체계에 참여할 수 있는지 확인합니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.
478행 그림 · 시스템 레지스터 읽기
함수·코드 범위: __cpu_secondary_check52bitva
이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_VA_BITS_52 / #ifndef CONFIG_ARM64_LPA2
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 일반 RAM의 메모리 접근과 구분합니다.
mrs_s 매크로 본문 · 1136행
arch/arm64/include/asm/sysreg.h · Linux v6.18.37
정의 선택 조건: #ifndef __ASM_SYSREG_H / #ifdef __ASSEMBLY__
.macro mrs_s, rt, sreg
__emit_inst(0xd5200000|(\sreg)|(.L__gpr_num_\rt))
.endm and x0, x0, ID_AA64MMFR2_EL1_VARange_MASK필요한 비트만 남깁니다. 다른 정보가 섞인 값에서 관심 있는 부분을 고르는 작업입니다. 여기서 결과를 받는 레지스터는 x0입니다.
479행 그림 · 레지스터 상태
함수·코드 범위: __cpu_secondary_check52bitva
이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_VA_BITS_52 / #ifndef CONFIG_ARM64_LPA2
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 결과는 목적 레지스터 폭에 맞습니다. 이 형식은 NZCV를 바꾸지 않습니다.
cbnz x0, 2fx0의 값가 0이 아니면 2f (L494)로 갑니다. 조건을 만족하지 않으면 바로 다음 명령을 읽습니다. 이 분기는 CMP의 결과가 아니라 지정 레지스터를 직접 검사합니다.
480행 그림 · 조건별 실행 위치
함수·코드 범위: __cpu_secondary_check52bitva
이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_VA_BITS_52 / #ifndef CONFIG_ARM64_LPA2
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 조건이 맞는 한 경로만 실행합니다. 이 분기 명령은 NZCV를 새로 계산하지 않습니다.
#elseLPA2가 포함된 빌드에서는 MMFR2 VARange 대신 MMFR0의 선택 granule 필드로 LPA2 지원을 검사합니다. 넓은 주소 지원이라는 목적은 같지만 하드웨어 기능 인코딩이 다릅니다.
481행 그림 · 전처리 단계
함수·코드 범위: __cpu_secondary_check52bitva
이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_VA_BITS_52 / #ifndef CONFIG_ARM64_LPA2
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
mrs x0, id_aa64mmfr0_el1MMFR0를 읽어 현재 페이지 granule의 LPA2 지원 수준을 확인합니다. 다음 SBFX가 4비트 필드를 부호 있는 값으로 꺼냅니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.
482행 그림 · 시스템 레지스터 읽기
함수·코드 범위: __cpu_secondary_check52bitva
이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_VA_BITS_52 / 앞 조건의 반대
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 일반 RAM의 메모리 접근과 구분합니다.
sbfx x0, x0, #ID_AA64MMFR0_EL1_TGRAN_SHIFT, 4선택한 TGRAN 필드를 부호 확장해 꺼냅니다. 미지원 인코딩까지 포함한 비교를 올바르게 수행하기 위해 UBFX와 구분합니다. 추출한 필드의 최상위 비트를 결과의 상위 자리까지 반복해 부호를 유지합니다.
483행 그림 · 비트 필드 추출
함수·코드 범위: __cpu_secondary_check52bitva
이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_VA_BITS_52 / 앞 조건의 반대
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
cmp x0, #ID_AA64MMFR0_EL1_TGRAN_LPA2x0와 ID_AA64MMFR0_EL1_TGRAN_LPA2를 비교하여 조건 플래그에 결과를 남깁니다. 비교한 두 값은 그대로이며, 뒤의 조건 분기가 이 결과를 읽습니다.
484행 그림 · 조건 플래그 계산
함수·코드 범위: __cpu_secondary_check52bitva
이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_VA_BITS_52 / 앞 조건의 반대
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 일반 레지스터에 연산 결과를 쓰지 않고 조건 플래그만 바꿉니다.
- N은 결과의 부호 비트, Z는 결과가 0인지 나타냅니다. C는 빌림이 없으면 1, V는 부호 있는 뺄셈의 오버플로를 나타냅니다.
b.ge 2f앞서 계산한 조건 플래그가 N=V이면 2f (L494)로 갑니다. 사이에 플래그를 바꾸지 않는 명령이 있어도 비교 결과는 남아 있습니다.
485행 그림 · 조건별 실행 위치
함수·코드 범위: __cpu_secondary_check52bitva
이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_VA_BITS_52 / 앞 조건의 반대
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 조건이 맞는 한 경로만 실행합니다. 이 분기 명령은 NZCV를 새로 계산하지 않습니다.
#endif빌드 조건으로 나누었던 구간을 닫습니다. 대상:
486행 그림 · 전처리 단계
함수·코드 범위: __cpu_secondary_check52bitva
이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_VA_BITS_52 / 앞 조건의 반대
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
update_early_cpu_boot_status \이 CPU가 어느 초기화 상태에 있는지 공유 메모리에 기록합니다. 다른 CPU가 실패 원인을 확인할 수 있도록 남기는 정보입니다.
488행 그림 · 매크로 안의 상태 변화
함수·코드 범위: __cpu_secondary_check52bitva
이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_VA_BITS_52
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 여러 명령과 내부 분기를 묶은 그림입니다. 작업 레지스터를 모두 보존한다는 뜻은 아니며, 정확한 변경 순서는 아래 매크로 본문과 줄별 그림에서 확인합니다.
update_early_cpu_boot_status 매크로 본문 · 437행
arch/arm64/kernel/head.S · Linux v6.18.37
.macro update_early_cpu_boot_status status, tmp1, tmp2
mov \tmp2, #\status
adr_l \tmp1, __early_cpu_boot_status
str \tmp2, [\tmp1]
dmb sy
dc ivac, \tmp1 // Invalidate potentially stale cache line
.endm CPU_STUCK_IN_KERNEL | CPU_STUCK_REASON_52_BIT_VA, x0, x1일반적인 커널 초기화 정지 표식에 52비트 VA 미지원 원인을 OR해 기록합니다. x0·x1은 이 매크로의 임시 레지스터이며 함수 반환값이 아닙니다.
489행 그림 · 앞줄에서 이어지는 인수
함수·코드 범위: __cpu_secondary_check52bitva
이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_VA_BITS_52
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 별도의 CPU 명령이 아니라 앞줄의 비트 조합이나 매크로 인자에 이어지는 줄입니다.
1: wfe이벤트를 기다릴 수 있도록 CPU에 힌트를 줍니다. 함수가 끝나는 명령은 아니며 깨어나면 다음 줄이 실행될 수 있으므로, 반복 분기나 조건 재검사와 함께 읽습니다.
490행 그림 · 동기화·캐시·대기 상태
함수·코드 범위: __cpu_secondary_check52bitva
이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_VA_BITS_52
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
wfi인터럽트를 기다릴 수 있도록 CPU에 힌트를 줍니다. 함수가 끝나는 명령은 아니며 깨어나면 다음 줄이 실행될 수 있으므로, 반복 분기나 조건 재검사와 함께 읽습니다.
491행 그림 · 동기화·캐시·대기 상태
함수·코드 범위: __cpu_secondary_check52bitva
이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_VA_BITS_52
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
b 1b1b (L490)부터 실행을 계속합니다. 돌아올 주소를 새로 적지 않으므로 일반 함수 호출과는 다른 제어 이동입니다.
492행 그림 · 호출과 실행 위치
함수·코드 범위: __cpu_secondary_check52bitva
이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_VA_BITS_52
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 새 복귀 주소를 LR에 기록하지 않습니다. RET 역시 메모리의 스택 값을 자동으로 꺼내는 명령은 아닙니다.
2: retLR(x30)에 적힌 주소로 실행을 돌립니다. 그 주소가 어떻게 준비됐는지가 중요합니다. 정상 함수의 호출자일 수도 있고, 절전 복귀나 trampoline에서 별도로 기록한 재개 지점일 수도 있습니다.
494행 그림 · 호출과 실행 위치
함수·코드 범위: __cpu_secondary_check52bitva
이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_VA_BITS_52
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 새 복귀 주소를 LR에 기록하지 않습니다. RET 역시 메모리의 스택 값을 자동으로 꺼내는 명령은 아닙니다.
SYM_FUNC_END(__cpu_secondary_check52bitva)도구에 이 코드 범위의 끝을 알려 크기와 심볼 정보를 기록합니다. CPU가 이 줄 때문에 자동으로 반환하지는 않습니다.
495행 그림 · 어셈블·링크 단계
함수·코드 범위: __cpu_secondary_check52bitva
이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_VA_BITS_52
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 끝 표기 자체가 RET나 분기를 실행하지는 않습니다.
#endif빌드 조건으로 나누었던 구간을 닫습니다. 대상:
496행 그림 · 전처리 단계
이 줄을 포함하는 조건: #ifdef CONFIG_ARM64_VA_BITS_52
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
SYM_FUNC_START_LOCAL(__no_granule_support)이 위치부터 (__no_granule_support)라는 코드가 시작한다고 도구에 알려 줍니다. 정렬과 외부 공개 여부도 정의에 포함됩니다. ARM64 함수 시작 매크로는 간접 호출의 도착점을 표시하는 BTI도 추가합니다.
498행 그림 · 어셈블·링크 단계
함수·코드 범위: __no_granule_support
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 함수 시작 매크로는 심벌·정렬 정보와 BTI 진입 명령 등을 배치할 수 있습니다. 생성된 명령의 실행과 심벌 선언을 구분합니다.
update_early_cpu_boot_status \이 CPU가 어느 초기화 상태에 있는지 공유 메모리에 기록합니다. 다른 CPU가 실패 원인을 확인할 수 있도록 남기는 정보입니다.
500행 그림 · 매크로 안의 상태 변화
함수·코드 범위: __no_granule_support
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 여러 명령과 내부 분기를 묶은 그림입니다. 작업 레지스터를 모두 보존한다는 뜻은 아니며, 정확한 변경 순서는 아래 매크로 본문과 줄별 그림에서 확인합니다.
update_early_cpu_boot_status 매크로 본문 · 437행
arch/arm64/kernel/head.S · Linux v6.18.37
.macro update_early_cpu_boot_status status, tmp1, tmp2
mov \tmp2, #\status
adr_l \tmp1, __early_cpu_boot_status
str \tmp2, [\tmp1]
dmb sy
dc ivac, \tmp1 // Invalidate potentially stale cache line
.endm CPU_STUCK_IN_KERNEL | CPU_STUCK_REASON_NO_GRAN, x1, x2초기화 정지 표식에 선택한 페이지 granule 미지원 원인을 합칩니다. 부트 CPU가 보조 CPU의 실패를 기다리다가 원인을 진단할 수 있게 합니다. 앞줄에서 시작한 식이나 매크로 인자에 이어지는 부분입니다. |로 연결한 비트들은 하나의 값으로 합쳐집니다.
501행 그림 · 앞줄에서 이어지는 인수
함수·코드 범위: __no_granule_support
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 별도의 CPU 명령이 아니라 앞줄의 비트 조합이나 매크로 인자에 이어지는 줄입니다.
1:여기에 1라는 위치 이름을 붙입니다. 다른 줄의 분기나 주소 계산이 이 위치를 찾아올 수 있습니다. 이름 자체가 CPU 작업을 추가하지는 않습니다.
502행 그림 · 위치에 이름 붙이기
함수·코드 범위: __no_granule_support
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
wfe이벤트를 기다릴 수 있도록 CPU에 힌트를 줍니다. 함수가 끝나는 명령은 아니며 깨어나면 다음 줄이 실행될 수 있으므로, 반복 분기나 조건 재검사와 함께 읽습니다.
503행 그림 · 동기화·캐시·대기 상태
함수·코드 범위: __no_granule_support
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
wfi인터럽트를 기다릴 수 있도록 CPU에 힌트를 줍니다. 함수가 끝나는 명령은 아니며 깨어나면 다음 줄이 실행될 수 있으므로, 반복 분기나 조건 재검사와 함께 읽습니다.
504행 그림 · 동기화·캐시·대기 상태
함수·코드 범위: __no_granule_support
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
b 1b1b (L502)부터 실행을 계속합니다. 돌아올 주소를 새로 적지 않으므로 일반 함수 호출과는 다른 제어 이동입니다.
505행 그림 · 호출과 실행 위치
함수·코드 범위: __no_granule_support
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 새 복귀 주소를 LR에 기록하지 않습니다. RET 역시 메모리의 스택 값을 자동으로 꺼내는 명령은 아닙니다.
SYM_FUNC_END(__no_granule_support)도구에 이 코드 범위의 끝을 알려 크기와 심볼 정보를 기록합니다. CPU가 이 줄 때문에 자동으로 반환하지는 않습니다.
506행 그림 · 어셈블·링크 단계
함수·코드 범위: __no_granule_support
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 끝 표기 자체가 RET나 분기를 실행하지는 않습니다.
__primary_switch: MMU on에서 최종 VA로 넘어가기
SYM_FUNC_START_LOCAL(__primary_switch)이 위치부터 (__primary_switch)라는 코드가 시작한다고 도구에 알려 줍니다. 정렬과 외부 공개 여부도 정의에 포함됩니다. ARM64 함수 시작 매크로는 간접 호출의 도착점을 표시하는 BTI도 추가합니다.
508행 그림 · 어셈블·링크 단계
함수·코드 범위: __primary_switch
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 함수 시작 매크로는 심벌·정렬 정보와 BTI 진입 명령 등을 배치할 수 있습니다. 생성된 명령의 실행과 심벌 선언을 구분합니다.
adrp x1, reserved_pg_dirx1에 reserved_pg_dir의 위치를 계산합니다. 그 위치의 데이터는 아직 읽지 않습니다. ADRP는 4 KiB 페이지의 시작만 계산하므로 세부 위치는 다음 오프셋 연산에서 더합니다.
509행 그림 · 주소·값 구분
함수·코드 범위: __primary_switch
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- ADRP의 페이지 단위는 4KiB입니다.
adrp x2, __pi_init_idmap_pg_dirx2에 __pi_init_idmap_pg_dir의 위치를 계산합니다. 그 위치의 데이터는 아직 읽지 않습니다. ADRP는 4 KiB 페이지의 시작만 계산하므로 세부 위치는 다음 오프셋 연산에서 더합니다.
510행 그림 · 주소·값 구분
함수·코드 범위: __primary_switch
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- ADRP의 페이지 단위는 4KiB입니다.
bl __enable_mmu부트 CPU의 TTBR1에는 빈 reserved_pg_dir, TTBR0에는 초기 idmap을 넣어 우선 MMU를 켭니다. 정식 커널 가상 매핑이 아직 준비되지 않았으므로 idmap 위에서 다음 C 초기화를 수행합니다. BL은 다음 명령의 주소를 LR(x30)에 기록하고 목적지로 분기합니다. 호출된 코드가 x0 같은 작업 레지스터를 바꿀 수 있으므로, 호출 뒤의 값은 그 코드의 반환·보존 규칙에 따릅니다.
511행 그림 · 호출과 실행 위치
함수·코드 범위: __primary_switch
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 그림은 분기 명령 자체의 효과입니다. 호출된 함수가 실행된 뒤의 레지스터 값은 그 함수의 반환·보존 규칙을 따라야 합니다.
adrp x1, early_init_stackx1에 early_init_stack의 위치를 계산합니다. 그 위치의 데이터는 아직 읽지 않습니다. ADRP는 4 KiB 페이지의 시작만 계산하므로 세부 위치는 다음 오프셋 연산에서 더합니다.
513행 그림 · 주소·값 구분
함수·코드 범위: __primary_switch
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- ADRP의 페이지 단위는 4KiB입니다.
mov sp, x1이제 스택으로 사용할 위치를 x1의 값으로 바꿉니다. 메모리 내용을 옮기는 작업은 아닙니다. 다음 저장·호출부터 새 스택 위치를 사용합니다.
514행 그림 · 값 복사
함수·코드 범위: __primary_switch
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 화살표는 값의 변화를 뜻합니다. 주소값을 옮겨도 그 주소의 메모리 내용은 복사하지 않습니다.
mov x29, xzrx29에 0을 넣습니다. 원래 x29의 내용은 덮어쓰며, 다른 레지스터나 그 주소의 메모리까지 복사하지는 않습니다.
515행 그림 · 값 복사
함수·코드 범위: __primary_switch
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 화살표는 값의 변화를 뜻합니다. 주소값을 옮겨도 그 주소의 메모리 내용은 복사하지 않습니다.
mov x0, x20 // pass the full boot statusx0에 x20에 들어 있는 값을 넣습니다. 원래 x0의 내용은 덮어쓰며, 다른 레지스터나 그 주소의 메모리까지 복사하지는 않습니다.
516행 그림 · 값 복사
함수·코드 범위: __primary_switch
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 화살표는 값의 변화를 뜻합니다. 주소값을 옮겨도 그 주소의 메모리 내용은 복사하지 않습니다.
mov x1, x21 // pass the FDTx1에 x21에 들어 있는 값을 넣습니다. 원래 x1의 내용은 덮어쓰며, 다른 레지스터나 그 주소의 메모리까지 복사하지는 않습니다.
517행 그림 · 값 복사
함수·코드 범위: __primary_switch
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 화살표는 값의 변화를 뜻합니다. 주소값을 옮겨도 그 주소의 메모리 내용은 복사하지 않습니다.
bl __pi_early_map_kernel // Map and relocate the kernelx0의 부팅 상태와 x1의 FDT 물리 주소를 받아 초기 명령행 기능 override·주소 폭·KASLR 등을 고려한 커널 매핑과 재배치를 준비합니다. 반환 뒤에는 __primary_switched의 최종 가상 주소로 점프할 수 있습니다. BL은 다음 명령의 주소를 LR(x30)에 기록하고 목적지로 분기합니다. 호출된 코드가 x0 같은 작업 레지스터를 바꿀 수 있으므로, 호출 뒤의 값은 그 코드의 반환·보존 규칙에 따릅니다.
518행 그림 · 호출과 실행 위치
함수·코드 범위: __primary_switch
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 그림은 분기 명령 자체의 효과입니다. 호출된 함수가 실행된 뒤의 레지스터 값은 그 함수의 반환·보존 규칙을 따라야 합니다.
ldr x8, =__primary_switchedx8에 __primary_switched의 주소 상수를 준비합니다. 그 함수나 변수의 본문을 복사하는 것이 아니라 이후 이동하거나 접근할 위치를 얻습니다.
520행 그림 · 메모리 읽기
함수·코드 범위: __primary_switch
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- =심벌이면 주소 상수를 리터럴 풀에서 읽는 의사 명령입니다. 레이블만 적힌 LDR은 해당 위치의 메모리를 읽습니다.
adrp x0, KERNEL_START // __pa(KERNEL_START)x0에 KERNEL_START의 위치를 계산합니다. 그 위치의 데이터는 아직 읽지 않습니다. ADRP는 4 KiB 페이지의 시작만 계산하므로 세부 위치는 다음 오프셋 연산에서 더합니다.
521행 그림 · 주소·값 구분
함수·코드 범위: __primary_switch
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- ADRP의 페이지 단위는 4KiB입니다.
br x8x8에 준비한 주소부터 실행합니다. 주소가 가리키는 값을 메모리에서 읽는 것이 아니라 CPU의 다음 실행 위치를 바꿉니다. 새 복귀 주소는 만들지 않습니다.
522행 그림 · 호출과 실행 위치
함수·코드 범위: __primary_switch
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 새 복귀 주소를 LR에 기록하지 않습니다. RET 역시 메모리의 스택 값을 자동으로 꺼내는 명령은 아닙니다.
SYM_FUNC_END(__primary_switch)도구에 이 코드 범위의 끝을 알려 크기와 심볼 정보를 기록합니다. CPU가 이 줄 때문에 자동으로 반환하지는 않습니다.
523행 그림 · 어셈블·링크 단계
함수·코드 범위: __primary_switch
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 끝 표기 자체가 RET나 분기를 실행하지는 않습니다.
설명한 뒤 함께 생각해 볼 질문
MMU를 켜면 PC가 자동으로 높은 커널 주소가 되나요?
아닙니다. 현재 숫자를 가상 주소로 해석하기 시작합니다. idmap으로 같은 물리 코드를 계속 읽고, 이후 BR로 최종 VA에 이동합니다.
FDT 주소를 x21에 복사하는 이유는 무엇인가요?
x0는 함수의 인자와 반환값으로 계속 재사용됩니다. 초기 DTB/FDT 포인터를 나중에도 쓰기 위해 다른 레지스터와 boot_args에 보존합니다.
EL2에서 들어왔으면 start_kernel도 EL2인가요?
init_kernel_el은 EL1으로 복귀할 수 있습니다. 나중에 finalise_el2에서 조건을 만족해야 host VHE로 전환합니다.
주석을 포함한 전체 원문
1/* SPDX-License-Identifier: GPL-2.0-only */2/*3* Low-level CPU initialisation4* Based on arch/arm/kernel/head.S5*6* Copyright (C) 1994-2002 Russell King7* Copyright (C) 2003-2012 ARM Ltd.8* Authors: Catalin Marinas <catalin.marinas@arm.com>9* Will Deacon <will.deacon@arm.com>10*/1112#include <linux/linkage.h>13#include <linux/init.h>14#include <linux/pgtable.h>1516#include <asm/asm_pointer_auth.h>17#include <asm/assembler.h>18#include <asm/boot.h>19#include <asm/bug.h>20#include <asm/ptrace.h>21#include <asm/asm-offsets.h>22#include <asm/cache.h>23#include <asm/cputype.h>24#include <asm/el2_setup.h>25#include <asm/elf.h>26#include <asm/image.h>27#include <asm/kernel-pgtable.h>28#include <asm/kvm_arm.h>29#include <asm/memory.h>30#include <asm/pgtable-hwdef.h>31#include <asm/page.h>32#include <asm/scs.h>33#include <asm/smp.h>34#include <asm/sysreg.h>35#include <asm/stacktrace/frame.h>36#include <asm/thread_info.h>37#include <asm/virt.h>3839#include "efi-header.S"4041#if (PAGE_OFFSET & 0x1fffff) != 042#error PAGE_OFFSET must be at least 2MB aligned43#endif4445/*46* Kernel startup entry point.47* ---------------------------48*49* The requirements are:50* MMU = off, D-cache = off, I-cache = on or off,51* x0 = physical address to the FDT blob.52*53* Note that the callee-saved registers are used for storing variables54* that are useful before the MMU is enabled. The allocations are described55* in the entry routines.56*/57__HEAD58/*59* DO NOT MODIFY. Image header expected by Linux boot-loaders.60*/61efi_signature_nop // special NOP to identity as PE/COFF executable62b primary_entry // branch to kernel start, magic63.quad 0 // Image load offset from start of RAM, little-endian64le64sym _kernel_size_le // Effective size of kernel image, little-endian65le64sym _kernel_flags_le // Informative flags, little-endian66.quad 0 // reserved67.quad 0 // reserved68.quad 0 // reserved69.ascii ARM64_IMAGE_MAGIC // Magic number70.long .Lpe_header_offset // Offset to the PE header.7172__EFI_PE_HEADER7374.section ".idmap.text","a"7576/*77* The following callee saved general purpose registers are used on the78* primary lowlevel boot path:79*80* Register Scope Purpose81* x19 primary_entry() .. start_kernel() whether we entered with the MMU on82* x20 primary_entry() .. __primary_switch() CPU boot mode83* x21 primary_entry() .. start_kernel() FDT pointer passed at boot in x084*/85SYM_CODE_START(primary_entry)86bl record_mmu_state87bl preserve_boot_args8889adrp x1, early_init_stack90mov sp, x191mov x29, xzr92adrp x0, __pi_init_idmap_pg_dir93mov x1, xzr94bl __pi_create_init_idmap9596/*97* If the page tables have been populated with non-cacheable98* accesses (MMU disabled), invalidate those tables again to99* remove any speculatively loaded cache lines.100*/101cbnz x19, 0f102dmb sy103mov x1, x0 // end of used region104adrp x0, __pi_init_idmap_pg_dir105adr_l x2, dcache_inval_poc106blr x2107b 1f108109/*110* If we entered with the MMU and caches on, clean the ID mapped part111* of the primary boot code to the PoC so we can safely execute it with112* the MMU off.113*/1140: adrp x0, __idmap_text_start115adr_l x1, __idmap_text_end116adr_l x2, dcache_clean_poc117blr x21181191: mov x0, x19120bl init_kernel_el // w0=cpu_boot_mode121mov x20, x0122123/*124* The following calls CPU setup code, see arch/arm64/mm/proc.S for125* details.126* On return, the CPU will be ready for the MMU to be turned on and127* the TCR will have been set.128*/129bl __cpu_setup // initialise processor130b __primary_switch131SYM_CODE_END(primary_entry)132133__INIT134SYM_CODE_START_LOCAL(record_mmu_state)135mrs x19, CurrentEL136cmp x19, #CurrentEL_EL2137mrs x19, sctlr_el1138b.ne 0f139mrs x19, sctlr_el21400:141CPU_LE( tbnz x19, #SCTLR_ELx_EE_SHIFT, 1f )142CPU_BE( tbz x19, #SCTLR_ELx_EE_SHIFT, 1f )143tst x19, #SCTLR_ELx_C // Z := (C == 0)144and x19, x19, #SCTLR_ELx_M // isolate M bit145csel x19, xzr, x19, eq // clear x19 if Z146ret147148/*149* Set the correct endianness early so all memory accesses issued150* before init_kernel_el() occur in the correct byte order. Note that151* this means the MMU must be disabled, or the active ID map will end152* up getting interpreted with the wrong byte order.153*/1541: eor x19, x19, #SCTLR_ELx_EE155bic x19, x19, #SCTLR_ELx_M156b.ne 2f157pre_disable_mmu_workaround158msr sctlr_el2, x19159b 3f1602: pre_disable_mmu_workaround161msr sctlr_el1, x191623: isb163mov x19, xzr164ret165SYM_CODE_END(record_mmu_state)166167/*168* Preserve the arguments passed by the bootloader in x0 .. x3169*/170SYM_CODE_START_LOCAL(preserve_boot_args)171mov x21, x0 // x21=FDT172173adr_l x0, boot_args // record the contents of174stp x21, x1, [x0] // x0 .. x3 at kernel entry175stp x2, x3, [x0, #16]176177cbnz x19, 0f // skip cache invalidation if MMU is on178dmb sy // needed before dc ivac with179// MMU off180181add x1, x0, #0x20 // 4 x 8 bytes182b dcache_inval_poc // tail call1830: str_l x19, mmu_enabled_at_boot, x0184ret185SYM_CODE_END(preserve_boot_args)186187/*188* Initialize CPU registers with task-specific and cpu-specific context.189*190* Create a final frame record at task_pt_regs(current)->stackframe, so191* that the unwinder can identify the final frame record of any task by192* its location in the task stack. We reserve the entire pt_regs space193* for consistency with user tasks and kthreads.194*/195.macro init_cpu_task tsk, tmp1, tmp2196msr sp_el0, \tsk197198ldr \tmp1, [\tsk, #TSK_STACK]199add sp, \tmp1, #THREAD_SIZE200sub sp, sp, #PT_REGS_SIZE201202stp xzr, xzr, [sp, #S_STACKFRAME]203mov \tmp1, #FRAME_META_TYPE_FINAL204str \tmp1, [sp, #S_STACKFRAME_TYPE]205add x29, sp, #S_STACKFRAME206207scs_load_current208209adr_l \tmp1, __per_cpu_offset210ldr w\tmp2, [\tsk, #TSK_TI_CPU]211ldr \tmp1, [\tmp1, \tmp2, lsl #3]212set_this_cpu_offset \tmp1213.endm214215/*216* The following fragment of code is executed with the MMU enabled.217*218* x0 = __pa(KERNEL_START)219*/220SYM_FUNC_START_LOCAL(__primary_switched)221adr_l x4, init_task222init_cpu_task x4, x5, x6223224adr_l x8, vectors // load VBAR_EL1 with virtual225msr vbar_el1, x8 // vector table address226isb227228stp x29, x30, [sp, #-16]!229mov x29, sp230231str_l x21, __fdt_pointer, x5 // Save FDT pointer232233adrp x4, _text // Save the offset between234sub x4, x4, x0 // the kernel virtual and235str_l x4, kimage_voffset, x5 // physical mappings236237mov x0, x20238bl set_cpu_boot_mode_flag239240#if defined(CONFIG_KASAN_GENERIC) || defined(CONFIG_KASAN_SW_TAGS)241bl kasan_early_init242#endif243mov x0, x20244bl finalise_el2 // Prefer VHE if possible245ldp x29, x30, [sp], #16246bl start_kernel247ASM_BUG()248SYM_FUNC_END(__primary_switched)249250/*251* end early head section, begin head code that is also used for252* hotplug and needs to have the same protections as the text region253*/254.section ".idmap.text","a"255256/*257* Starting from EL2 or EL1, configure the CPU to execute at the highest258* reachable EL supported by the kernel in a chosen default state. If dropping259* from EL2 to EL1, configure EL2 before configuring EL1.260*261* Since we cannot always rely on ERET synchronizing writes to sysregs (e.g. if262* SCTLR_ELx.EOS is clear), we place an ISB prior to ERET.263*264* Returns either BOOT_CPU_MODE_EL1 or BOOT_CPU_MODE_EL2 in x0 if265* booted in EL1 or EL2 respectively, with the top 32 bits containing266* potential context flags. These flags are *not* stored in __boot_cpu_mode.267*268* x0: whether we are being called from the primary boot path with the MMU on269*/270SYM_FUNC_START(init_kernel_el)271mrs x1, CurrentEL272cmp x1, #CurrentEL_EL2273b.eq init_el2274275SYM_INNER_LABEL(init_el1, SYM_L_LOCAL)276mov_q x0, INIT_SCTLR_EL1_MMU_OFF277pre_disable_mmu_workaround278msr sctlr_el1, x0279isb280mov_q x0, INIT_PSTATE_EL1281msr spsr_el1, x0282msr elr_el1, lr283mov w0, #BOOT_CPU_MODE_EL1284eret285286SYM_INNER_LABEL(init_el2, SYM_L_LOCAL)287msr elr_el2, lr288289// clean all HYP code to the PoC if we booted at EL2 with the MMU on290cbz x0, 0f291adrp x0, __hyp_idmap_text_start292adr_l x1, __hyp_text_end293adr_l x2, dcache_clean_poc294blr x2295296mov_q x0, INIT_SCTLR_EL2_MMU_OFF297pre_disable_mmu_workaround298msr sctlr_el2, x0299isb3000:301302init_el2_hcr HCR_HOST_NVHE_FLAGS303init_el2_state304305/* Hypervisor stub */306adr_l x0, __hyp_stub_vectors307msr vbar_el2, x0308isb309310mov_q x1, INIT_SCTLR_EL1_MMU_OFF311312mrs x0, hcr_el2313and x0, x0, #HCR_E2H314cbz x0, 2f315316/* Set a sane SCTLR_EL1, the VHE way */317msr_s SYS_SCTLR_EL12, x1318mov x2, #BOOT_CPU_FLAG_E2H319b 3f3203212:322msr sctlr_el1, x1323mov x2, xzr3243:325mov x0, #INIT_PSTATE_EL1326msr spsr_el2, x0327328mov w0, #BOOT_CPU_MODE_EL2329orr x0, x0, x2330eret331SYM_FUNC_END(init_kernel_el)332333/*334* This provides a "holding pen" for platforms to hold all secondary335* cores are held until we're ready for them to initialise.336*/337SYM_FUNC_START(secondary_holding_pen)338mov x0, xzr339bl init_kernel_el // w0=cpu_boot_mode340mrs x2, mpidr_el1341mov_q x1, MPIDR_HWID_BITMASK342and x2, x2, x1343adr_l x3, secondary_holding_pen_release344pen: ldr x4, [x3]345cmp x4, x2346b.eq secondary_startup347wfe348b pen349SYM_FUNC_END(secondary_holding_pen)350351/*352* Secondary entry point that jumps straight into the kernel. Only to353* be used where CPUs are brought online dynamically by the kernel.354*/355SYM_FUNC_START(secondary_entry)356mov x0, xzr357bl init_kernel_el // w0=cpu_boot_mode358b secondary_startup359SYM_FUNC_END(secondary_entry)360361SYM_FUNC_START_LOCAL(secondary_startup)362/*363* Common entry point for secondary CPUs.364*/365mov x20, x0 // preserve boot mode366367#ifdef CONFIG_ARM64_VA_BITS_52368alternative_if ARM64_HAS_VA52369bl __cpu_secondary_check52bitva370alternative_else_nop_endif371#endif372373bl __cpu_setup // initialise processor374adrp x1, swapper_pg_dir375adrp x2, idmap_pg_dir376bl __enable_mmu377ldr x8, =__secondary_switched378br x8379SYM_FUNC_END(secondary_startup)380381.text382SYM_FUNC_START_LOCAL(__secondary_switched)383mov x0, x20384bl set_cpu_boot_mode_flag385386mov x0, x20387bl finalise_el2388389str_l xzr, __early_cpu_boot_status, x3390adr_l x5, vectors391msr vbar_el1, x5392isb393394adr_l x0, secondary_data395ldr x2, [x0, #CPU_BOOT_TASK]396cbz x2, __secondary_too_slow397398init_cpu_task x2, x1, x3399400#ifdef CONFIG_ARM64_PTR_AUTH401ptrauth_keys_init_cpu x2, x3, x4, x5402#endif403404bl secondary_start_kernel405ASM_BUG()406SYM_FUNC_END(__secondary_switched)407408SYM_FUNC_START_LOCAL(__secondary_too_slow)409wfe410wfi411b __secondary_too_slow412SYM_FUNC_END(__secondary_too_slow)413414/*415* Sets the __boot_cpu_mode flag depending on the CPU boot mode passed416* in w0. See arch/arm64/include/asm/virt.h for more info.417*/418SYM_FUNC_START_LOCAL(set_cpu_boot_mode_flag)419adr_l x1, __boot_cpu_mode420cmp w0, #BOOT_CPU_MODE_EL2421b.ne 1f422add x1, x1, #44231: str w0, [x1] // Save CPU boot mode424ret425SYM_FUNC_END(set_cpu_boot_mode_flag)426427/*428* The booting CPU updates the failed status @__early_cpu_boot_status,429* with MMU turned off.430*431* update_early_cpu_boot_status tmp, status432* - Corrupts tmp1, tmp2433* - Writes 'status' to __early_cpu_boot_status and makes sure434* it is committed to memory.435*/436437.macro update_early_cpu_boot_status status, tmp1, tmp2438mov \tmp2, #\status439adr_l \tmp1, __early_cpu_boot_status440str \tmp2, [\tmp1]441dmb sy442dc ivac, \tmp1 // Invalidate potentially stale cache line443.endm444445/*446* Enable the MMU.447*448* x0 = SCTLR_EL1 value for turning on the MMU.449* x1 = TTBR1_EL1 value450* x2 = ID map root table address451*452* Returns to the caller via x30/lr. This requires the caller to be covered453* by the .idmap.text section.454*455* Checks if the selected granule size is supported by the CPU.456* If it isn't, park the CPU457*/458.section ".idmap.text","a"459SYM_FUNC_START(__enable_mmu)460mrs x3, ID_AA64MMFR0_EL1461ubfx x3, x3, #ID_AA64MMFR0_EL1_TGRAN_SHIFT, 4462cmp x3, #ID_AA64MMFR0_EL1_TGRAN_SUPPORTED_MIN463b.lt __no_granule_support464cmp x3, #ID_AA64MMFR0_EL1_TGRAN_SUPPORTED_MAX465b.gt __no_granule_support466phys_to_ttbr x2, x2467msr ttbr0_el1, x2 // load TTBR0468load_ttbr1 x1, x1, x3469470set_sctlr_el1 x0471472ret473SYM_FUNC_END(__enable_mmu)474475#ifdef CONFIG_ARM64_VA_BITS_52476SYM_FUNC_START(__cpu_secondary_check52bitva)477#ifndef CONFIG_ARM64_LPA2478mrs_s x0, SYS_ID_AA64MMFR2_EL1479and x0, x0, ID_AA64MMFR2_EL1_VARange_MASK480cbnz x0, 2f481#else482mrs x0, id_aa64mmfr0_el1483sbfx x0, x0, #ID_AA64MMFR0_EL1_TGRAN_SHIFT, 4484cmp x0, #ID_AA64MMFR0_EL1_TGRAN_LPA2485b.ge 2f486#endif487488update_early_cpu_boot_status \489CPU_STUCK_IN_KERNEL | CPU_STUCK_REASON_52_BIT_VA, x0, x14901: wfe491wfi492b 1b4934942: ret495SYM_FUNC_END(__cpu_secondary_check52bitva)496#endif497498SYM_FUNC_START_LOCAL(__no_granule_support)499/* Indicate that this CPU can't boot and is stuck in the kernel */500update_early_cpu_boot_status \501CPU_STUCK_IN_KERNEL | CPU_STUCK_REASON_NO_GRAN, x1, x25021:503wfe504wfi505b 1b506SYM_FUNC_END(__no_granule_support)507508SYM_FUNC_START_LOCAL(__primary_switch)509adrp x1, reserved_pg_dir510adrp x2, __pi_init_idmap_pg_dir511bl __enable_mmu512513adrp x1, early_init_stack514mov sp, x1515mov x29, xzr516mov x0, x20 // pass the full boot status517mov x1, x21 // pass the FDT518bl __pi_early_map_kernel // Map and relocate the kernel519520ldr x8, =__primary_switched521adrp x0, KERNEL_START // __pa(KERNEL_START)522br x8523SYM_FUNC_END(__primary_switch)

