이 파일은 왜 필요한가요?
현재 커널이 사용하던 가상 주소 체계를 끝내고 다른 코드에게 실행을 넘기는 상황입니다. 주소 변환을 끄면 다음 명령을 어디서 읽을지도 달라질 수 있습니다. 그래서 이 코드는 가상 주소와 물리 주소가 같은 곳에서 실행합니다. 인자를 보존한 뒤 제어 레지스터를 바꾸고 마지막에는 일반 함수처럼 돌아오지 않고 새 진입점으로 이동합니다.
명령·지시문·라벨을 원본 줄 번호에 맞춰 설명합니다. 주석과 빈 줄을 포함한 전체 원문은 아래에 따로 두었습니다. 매크로 정의와 조건부 코드는 모두 차례대로 실행되는 명령 목록이 아닙니다.
기존 코드 분석과 함께 읽기 · 원본 파일 · 설명 원고
먼저 알아둘 개념: 주소와 주소가 가리키는 내용, 가상 주소와 MMU, 레지스터와 값, 호출 주소와 복귀 주소, 예외 레벨과 복귀
처음 읽을 때
함수 주소와 함수 인자를 구분하세요. x1의 주소를 x8에 옮기는 것은 함수를 복사하는 일이 아니라, 이후 x1을 다른 인자로 사용할 준비입니다.
더 깊이 살펴볼 때
SCTLR 설정, ISB, idmap은 서로 다른 역할을 맡습니다. 같은 레지스터 이름이 VHE에서 가리키는 대상까지 고려하면 왜 단순히 “MMU 비트 하나를 0으로 만든다”로 끝나지 않는지 볼 수 있습니다.
한 줄씩 레지스터와 메모리 상태 보기
각 코드 아래의 ‘행 그림’을 펼치면, 그 줄에서 읽는 값과 실행 전·후의 상태를 나란히 볼 수 있습니다. 여기서 ‘명령 직전’은 지금 보고 있는 명령이 실행되기 바로 전을 뜻합니다. 함수 진입 때나 소스의 바로 윗줄을 가리키는 말은 아닙니다. 앞서 실행한 명령이 값을 바꿨다면 그 결과가 들어 있습니다.
소스로 정해지는 상수, 호출자가 넘기는 값, 실제 실행에서 정해지는 주소를 구분해서 읽어야 합니다. 주소 숫자를 몰라도 무엇을 가리키는지와 어디서 받아 오는지는 설명할 수 있습니다. ‘명령 직전 x0의 값’처럼 일반 표기로 남은 곳은 한 명령의 동작을 나타내며, 특정 호출 경로의 값까지 확인했다는 뜻은 아닙니다.
일반 명령은 그 한 줄의 효과를, 매크로는 여러 명령을 묶은 주요 결과를 표시합니다. 함수 호출은 호출 직후의 PC와 LR까지만 나타냅니다. 메모리 접근은 정상적으로 끝나는 경우를 가정합니다. 생략된 항목까지 모두 보존된다는 뜻은 아닙니다.
화살표 →는 같은 항목의 전후 비교입니다. 메모리 칸에는 주소와 크기를 따로 적었습니다. SP가 줄어들면 낮은 주소 쪽을 사용하며, 화면의 위아래와 메모리 주소 방향은 별개입니다. 전처리·어셈블·링크 지시문은 CPU 실행과 구분합니다.
‘이전·다음 행’은 소스에 적힌 순서입니다. 분기·반복·호출이 있으므로 파일 위에서 아래로 실제 실행된다고 가정하지 않습니다. 아래 예제에서는 경로를 선택하고 앞줄의 값을 다음 줄까지 이어서 볼 수 있습니다.
총 22개 줄에 그림을 붙였습니다. 기존 코드와 설명은 그 아래에서 이어집니다.
이 함수에 실제로 들어오는 값
아래 그림은 machine_kexec에서 kimage->head & IND_DONE이 참인 경로를 기준으로 합니다. cpu_soft_restart 자체의 인자 이름과 이 호출부가 넣는 값을 구분해 보세요.
restart = (void *)__pa_symbol(cpu_soft_restart);
restart(is_hyp_nvhe(), kimage->start, kimage->arch.dtb_mem, 0, 0);
| 진입 때 레지스터 | 호출부가 넣는 값 | 용도 |
|---|---|---|
| x0 | is_hyp_nvhe(): 0 또는 1 | EL2의 hyp stub를 거칠지 결정합니다. |
| x1 | kimage->start | 새 커널 또는 purgatory의 진입 주소입니다. |
| x2 | kimage->arch.dtb_mem | 새 진입점의 첫 인자 arg0입니다. kexec_file 경로에서는 DTB의 물리 주소입니다. |
| x3 | 0 | 새 진입점의 두 번째 인자 arg1입니다. |
| x4 | 0 | 새 진입점의 세 번째 인자 arg2입니다. |
DTB(Device Tree Blob)는 새 커널에 메모리·CPU·장치 구성 등을 전달하는 데이터입니다. dtb_mem은 그 데이터를 놓을 물리 주소이며, DTB 내용 전체가 레지스터 안에 들어가는 것은 아닙니다. 파일 로더가 kexec_add_buffer로 배치할 위치를 정한 뒤 kbuf.mem을 image->arch.dtb_mem에 기록합니다. 실제 주소 숫자는 메모리 배치에 따라 달라집니다.
기존 kexec의 purgatory 경로에서는 사용자 도구가 진입 주소와 DTB 정보를 purgatory에 넣을 수 있습니다. 그러므로 어떤 호출에서도 x2가 반드시 유효한 DTB 주소라고 일반화하면 안 됩니다. 이 함수가 받는 일반적인 의미는 ‘새 진입점의 첫 인자’이고, 여기서 확인한 전달값은 kimage->arch.dtb_mem입니다.
cpu-reset.S 머리말의 arg0 옆에 남아 있는 relocation list라는 주석만으로 x2의 용도를 판단하지 않았습니다. v6.18.37의 실제 호출식을 기준으로 했습니다.
cpu_soft_restart: 앞줄의 값을 다음 줄로 이어서 보기
이 예제는 위 호출부가 넘기는 값을 넣고 EL1에서 시작합니다. x1은 kimage->start, x2는 kimage->arch.dtb_mem, x3·x4는 0입니다. HVC 경로는 EL2가 있고 HVC가 허용된 경우입니다. EL2에서 시작하는 VHE 경로는 이 예제에 포함하지 않았습니다.
1 / 11 · 함수 진입 직후
x0=is_hyp_nvhe(), x1=kimage->start, x2=kimage->arch.dtb_mem, x3=0, x4=0x2는 새 진입점의 첫 인자이며, kexec_file 경로에서는 DTB의 물리 주소입니다. x3·x4는 이 호출부에서 0으로 정해집니다.
파란 칸과 ‘변경’ 표시는 이번 단계에서 바뀐 값입니다. →는 같은 레지스터의 전후 비교이며, 유지된 값도 함께 표시합니다. PC의 행 번호는 설명 편의를 위한 표기로 실제 주소가 아닙니다. 매크로는 한 단계로 묶었습니다.
먼저 그림으로 살펴보기
실행 위치와 입력
#include <linux/linkage.h>어셈블리 함수의 심볼과 크기를 선언하는 linkage 정의를 가져옵니다. cpu_soft_restart를 C 코드에서 호출할 수 있는 함수로 내보냅니다. #include는 전처리 단계에서 헤더 내용을 가져옵니다. CPU가 이 줄에서 함수를 호출하는 동작은 없습니다.
10행 그림 · 전처리 단계
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
#include <linux/cfi_types.h>간접 호출의 함수 형식 검사에 쓰는 CFI 관련 정의를 가져옵니다. cpu_soft_restart는 함수 포인터로도 호출되므로 SYM_TYPED_FUNC_START로 형식 정보가 있는 진입점을 선언합니다. #include는 전처리 단계에서 헤더 내용을 가져옵니다. CPU가 이 줄에서 함수를 호출하는 동작은 없습니다.
11행 그림 · 전처리 단계
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
#include <asm/assembler.h>큰 상수를 만드는 mov_q와 MMU 비활성화 직전의 CPU 문제 우회 매크로 등 공통 어셈블리 도구를 가져옵니다. #include는 전처리 단계에서 헤더 내용을 가져옵니다. CPU가 이 줄에서 함수를 호출하는 동작은 없습니다.
12행 그림 · 전처리 단계
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
#include <asm/sysreg.h>SCTLR 제어 비트와 MMU-off 초기값을 가져옵니다. 주소 변환을 끌 때 필요한 엔디언·제어 설정을 하드코딩한 0 대신 이 정의로 구성합니다. #include는 전처리 단계에서 헤더 내용을 가져옵니다. CPU가 이 줄에서 함수를 호출하는 동작은 없습니다.
13행 그림 · 전처리 단계
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
#include <asm/virt.h>HVC_SOFT_RESTART 같은 EL2 stub 요청 번호를 가져옵니다. EL2에서 새 이미지로 시작해야 하는 경우 이 번호를 x0로 전달합니다. #include는 전처리 단계에서 헤더 내용을 가져옵니다. CPU가 이 줄에서 함수를 호출하는 동작은 없습니다.
14행 그림 · 전처리 단계
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
.text기본 실행 코드 섹션을 선택합니다. 바로 뒤에서 실제 재시작 함수는 MMU 전환을 견디도록 .idmap.text에 배치합니다. 이는 빌드 중 출력할 바이트의 섹션을 선택하는 지시문이며, 실행 중 PC나 SP를 옮기지 않습니다.
16행 그림 · 어셈블·링크 단계
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
.pushsection .idmap.text, "a"MMU를 끄는 동안에도 실행 주소가 유효하도록 이 함수를 가상 주소와 물리 주소가 같은 idmap 영역에 둡니다. 이는 빌드 중 출력할 바이트의 섹션을 선택하는 지시문이며, 실행 중 PC나 SP를 옮기지 않습니다. 이전 출력 섹션을 함께 기억하므로 뒤의 .popsection으로 되돌릴 수 있습니다.
17행 그림 · 어셈블·링크 단계
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
주소 변환 끄기
SYM_TYPED_FUNC_START(cpu_soft_restart)CPU를 소프트웨어로 다음 실행 이미지에 넘기는 함수입니다. x0는 EL2로 전환할지, x1은 진입 주소, x2~x4는 그 진입점에 전달할 세 인자입니다. 주변 장치 전체에 하드웨어 리셋 신호를 보내는 함수는 아닙니다. 이 선언은 함수 심볼과 정렬을 정하고, ARM64 정의에서는 간접 호출이 도착할 BTI C 명령도 진입부에 넣습니다. TYPED 형식은 구성에 따라 간접 호출 검사에 필요한 함수 형식 정보도 제공합니다. 여기서는 전역 심벌로 정의하므로 다른 오브젝트에서도 이 함수 이름을 참조할 수 있습니다. 모듈용 EXPORT_SYMBOL 등록은 별도입니다.
32행 그림 · 어셈블·링크 단계
함수·코드 범위: cpu_soft_restart
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 함수 시작 매크로는 심벌·정렬 정보와 BTI 진입 명령 등을 배치할 수 있습니다. 생성된 명령의 실행과 심벌 선언을 구분합니다.
mov_q x12, INIT_SCTLR_EL1_MMU_OFFMMU와 캐시를 끈 상태로 사용할 SCTLR 초기값을 x12에 만듭니다. INIT_SCTLR_EL1_MMU_OFF는 단순한 0이 아니라 엔디언 설정과 필요한 제어 비트를 포함하며 M·C·I 비트는 포함하지 않습니다. mov_q는 64비트 상수를 필요한 MOVZ/MOVN·MOVK 조합으로 구성하는 매크로입니다. 해당 주소의 메모리에서 데이터를 가져오는 명령은 아닙니다.
INIT_SCTLR_EL1_MMU_OFF는 단순한 정수 0이 아니라 커널이 정한 초기 제어 비트 조합입니다.
33행 그림 · 값 복사
함수·코드 범위: cpu_soft_restart
v6.18.37 machine_kexec의 IND_DONE 경로에서 호출한 경우
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 화살표는 값의 변화를 뜻합니다. 주소값을 옮겨도 그 주소의 메모리 내용은 복사하지 않습니다.
- mov_q는 큰 상수를 여러 MOVZ/MOVN·MOVK 명령으로 구성할 수 있습니다. 여기서는 매크로 전체 결과를 표시합니다.
- x12의 진입 때 숫자는 이 코드의 결과에 영향을 주지 않습니다. 이 줄이 먼저 제어 상수로 덮어씁니다.
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_workaroundCONFIG_QCOM_FALKOR_ERRATUM_E1041이 설정된 빌드에서는 SCTLR의 M 비트를 내리기 직전에 ISB를 넣습니다. Qualcomm Falkor의 해당 CPU 문제를 우회하기 위한 순서 보장입니다.
34행 그림 · 동기화·캐시·대기 상태
함수·코드 범위: cpu_soft_restart
v6.18.37 machine_kexec의 IND_DONE 경로에서 호출한 경우
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
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, x12준비한 값을 SCTLR_EL1에 써서 주소 변환과 캐시 사용을 중단합니다. 이 지점 뒤에도 실행할 수 있도록 호출자가 idmap을 준비해 두어야 합니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.
39행 그림 · 시스템 레지스터 쓰기
함수·코드 범위: cpu_soft_restart
v6.18.37 machine_kexec의 IND_DONE 경로에서 호출한 경우
읽는 값의 내용과 출처
x12 = INIT_SCTLR_EL1_MMU_OFF · MMU를 끌 때 쓸 제어값
SCTLR_EL1에 쓸 제어 비트들의 묶음입니다.
출처: 33행 mov_q가 이 상수를 구성했습니다.
수치가 실행 때 임의로 생기는 값은 아닙니다. 구성의 엔디언 선택 등이 빌드 시 비트 조합을 정합니다.
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 접근 권한과 VHE 별칭 규칙에 따라 실제 대상이 정해집니다. 뒤의 ISB·DSB가 필요한지는 이 레지스터와 문맥에 따릅니다.
isbSCTLR 변경을 이후 명령 실행에 반영합니다. 다음 분기는 MMU를 끈 실행 환경에서 이루어집니다.
40행 그림 · 동기화·캐시·대기 상태
함수·코드 범위: cpu_soft_restart
v6.18.37 machine_kexec의 IND_DONE 경로에서 호출한 경우
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
EL2 경유 또는 직접 이동
cbz x0, 1f // el2_switch?EL2 전환 플래그가 0이면 현재 EL에서 직접 새 진입점으로 가는 경로를 택합니다. 0이 아니면 HVC를 통해 EL2 쪽 재시작 처리로 들어갑니다. 이 분기는 지정 레지스터의 값이나 비트를 직접 검사하며 CMP가 남긴 NZCV를 읽거나 바꾸지 않습니다. 조건이 맞지 않으면 바로 다음 명령으로 진행합니다.
42행 그림 · 조건별 실행 위치
함수·코드 범위: cpu_soft_restart
v6.18.37 machine_kexec의 IND_DONE 경로에서 호출한 경우
읽는 값의 내용과 출처
x0 = is_hyp_nvhe()의 결과: 0 또는 1
EL2의 hyp stub를 거칠지 고르는 플래그입니다.
출처: machine_kexec가 첫 번째 인자로 전달합니다.
EL2 사용 가능 여부와 커널이 실행 중인 EL에 따라 달라집니다.
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 조건이 맞는 한 경로만 실행합니다. 이 분기 명령은 NZCV를 새로 계산하지 않습니다.
mov x0, #HVC_SOFT_RESTARTx0에 HVC_SOFT_RESTART 서비스 번호를 넣어 기존의 EL2 전환 플래그를 덮어씁니다. 이 MOV는 x0의 값만 바꾸며, x1~x4나 메모리의 내용을 복사하거나 수정하지 않습니다. 다음 줄의 hvc #0이 실행될 때 EL2 처리 코드가 x0의 요청 번호, x1의 진입 주소, x2~x4의 인자를 받습니다.
HVC 요청 번호를 넣기 전에 x1의 진입점과 x2–x4의 인자는 그대로 유지됩니다.
43행 그림 · 값 복사
함수·코드 범위: cpu_soft_restart
v6.18.37 machine_kexec의 IND_DONE 경로에서 호출한 경우 · 42행에서 x0=1이어서 HVC로 진행하는 경로
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 화살표는 값의 변화를 뜻합니다. 주소값을 옮겨도 그 주소의 메모리 내용은 복사하지 않습니다.
- 이 호출 경로에서는 전환 플래그와 HVC_SOFT_RESTART의 숫자가 모두 1입니다. 비트값은 같지만, 이 줄부터 x0의 의미는 전환 여부가 아니라 요청할 서비스 번호입니다.
hvc #0 // no returnEL2의 hyp stub에 소프트웨어 재시작을 요청합니다. 처리기는 x1의 주소로 분기하면서 x2~x4를 새 x0~x2로 옮기므로 성공 경로에서는 이 HVC 다음 줄로 돌아오지 않습니다. 일반 함수 호출처럼 BL로 이동하는 대신 동기 예외를 통해 요청을 전달합니다.
성공하면 hyp-stub가 새 진입점으로 BR하므로 이 호출 뒤로 반환하지 않습니다.
44행 그림 · 예외 처리기로 제어 전달
함수·코드 범위: cpu_soft_restart
v6.18.37 machine_kexec의 IND_DONE 경로에서 호출한 경우 · 42행에서 x0=1이어서 HVC로 진행하는 경로
읽는 값의 내용과 출처
x0 = 1 · HVC_SOFT_RESTART
EL2의 소프트 재시작 처리 요청입니다.
출처: 43행에서 설정했습니다.
x1 = kimage->start · 새 커널 또는 purgatory 진입 주소
최종적으로 실행을 넘길 주소입니다. 주소가 가리키는 명령 바이트 자체를 담고 있는 것은 아닙니다.
출처: machine_kexec가 kimage->start를 전달합니다.
구체적인 주소는 새 이미지의 적재 위치와 진입점에 따라 달라집니다.
x2 = kimage->arch.dtb_mem
새 진입점에 넘길 첫 번째 인자 arg0입니다. kexec_file 경로에서는 DTB(Device Tree Blob)를 놓을 물리 주소입니다. DTB는 메모리·CPU·장치 구성 등을 새 커널에 전달하는 데이터입니다.
출처: machine_kexec가 cpu_soft_restart의 세 번째 인자로 전달합니다. 파일 로더는 DTB를 배치한 kbuf.mem을 이 필드에 기록합니다.
주소의 16진수 값은 메모리 배치에 따라 달라집니다. 주소값 하나를 복사하며 DTB 데이터 전체를 복사하지 않습니다.
x3 = 0
새 진입점의 두 번째 인자 arg1입니다. 이 호출부는 0을 넘깁니다.
출처: machine_kexec의 네 번째 인자에 적힌 0입니다.
x4 = 0
새 진입점의 세 번째 인자 arg2입니다. 이 호출부는 0을 넘깁니다.
출처: machine_kexec의 다섯 번째 인자에 적힌 0입니다.
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 허용된 정상 예외 진입을 나타냅니다. 사용이 금지되거나 해당 EL이 없으면 예외 결과가 달라집니다. x0 등의 요청 인자는 처리기가 규약에 따라 읽으며 HVC/SMC 자체가 메모리로 복사하지는 않습니다.
- 서비스를 처리한 뒤의 반환 레지스터는 서비스 규약에 따릅니다. 여기서는 진입까지만 표시합니다.
- HVC 진입 자체는 x1~x4의 인자를 복사하지 않습니다. 이어서 hyp-stub의 소프트 재시작 경로가 x1을 분기 주소로 사용하고 x2~x4를 새 x0~x2로 옮깁니다.
- cpu-reset.S 머리말의 arg0 옆에는 relocation list라는 주석이 남아 있습니다. 여기서는 해당 버전의 실제 C 호출식이 전달하는 kimage->arch.dtb_mem을 기준으로 표시했습니다. 일반 인터페이스의 arg0 의미는 호출부와 새 진입점의 규약에 따라 정해집니다.
호출부: machine_kexec.c 187–195행 · kexec_file의 DTB 배치: machine_kexec_file.c 161–186행
1: mov x8, x1 // entry직접 분기 경로에서 진입 주소를 x8에 옮깁니다. x1을 새 이미지의 인자 레지스터로 바꾸기 전에 분기 목적지를 보존합니다. MOV는 x8의 기존 값을 대체합니다. 원본이 주소값이어도 그 주소의 메모리를 읽거나 복사하지 않습니다.
다음 줄에서 x1을 덮어쓰기 전에 entry를 x8에 보존합니다.
46행 그림 · 값 복사
함수·코드 범위: cpu_soft_restart
v6.18.37 machine_kexec의 IND_DONE 경로에서 호출한 경우 · 42행에서 x0=0으로 46행에 분기한 정상 경로
읽는 값의 내용과 출처
x1 = kimage->start · 새 커널 또는 purgatory 진입 주소
최종적으로 실행을 넘길 주소입니다. 주소가 가리키는 명령 바이트 자체를 담고 있는 것은 아닙니다.
출처: machine_kexec가 kimage->start를 전달합니다.
구체적인 주소는 새 이미지의 적재 위치와 진입점에 따라 달라집니다.
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 화살표는 값의 변화를 뜻합니다. 주소값을 옮겨도 그 주소의 메모리 내용은 복사하지 않습니다.
mov x0, x2 // arg0호출자가 넘긴 첫 번째 새 이미지 인자를 x2에서 x0로 옮깁니다. machine_kexec에서는 여기에 DTB 물리 주소를 전달합니다. MOV는 x0의 기존 값을 대체합니다. 원본이 주소값이어도 그 주소의 메모리를 읽거나 복사하지 않습니다.
47행 그림 · 값 복사
함수·코드 범위: cpu_soft_restart
v6.18.37 machine_kexec의 IND_DONE 경로에서 호출한 경우 · 42행에서 x0=0으로 46행에 분기한 정상 경로
읽는 값의 내용과 출처
x2 = kimage->arch.dtb_mem
새 진입점에 넘길 첫 번째 인자 arg0입니다. kexec_file 경로에서는 DTB(Device Tree Blob)를 놓을 물리 주소입니다. DTB는 메모리·CPU·장치 구성 등을 새 커널에 전달하는 데이터입니다.
출처: machine_kexec가 cpu_soft_restart의 세 번째 인자로 전달합니다. 파일 로더는 DTB를 배치한 kbuf.mem을 이 필드에 기록합니다.
주소의 16진수 값은 메모리 배치에 따라 달라집니다. 주소값 하나를 복사하며 DTB 데이터 전체를 복사하지 않습니다.
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 화살표는 값의 변화를 뜻합니다. 주소값을 옮겨도 그 주소의 메모리 내용은 복사하지 않습니다.
- x0가 0이라고 쓸 수 있는 근거는 42행의 CBZ입니다. 46행은 x8만 바꾸므로 47행 직전까지 x0=0, x2=호출자가 전달한 arg0가 유지됩니다. 이 MOV가 바꾸는 레지스터는 x0뿐이며 x2는 그대로입니다.
- cpu-reset.S 머리말의 arg0 옆에는 relocation list라는 주석이 남아 있습니다. 여기서는 해당 버전의 실제 C 호출식이 전달하는 kimage->arch.dtb_mem을 기준으로 표시했습니다. 일반 인터페이스의 arg0 의미는 호출부와 새 진입점의 규약에 따라 정해집니다.
호출부: machine_kexec.c 187–195행 · kexec_file의 DTB 배치: machine_kexec_file.c 161–186행
mov x1, x3 // arg1두 번째 새 이미지 인자를 x3에서 x1로 옮깁니다. machine_kexec의 호출에서는 0이 전달됩니다. MOV는 x1의 기존 값을 대체합니다. 원본이 주소값이어도 그 주소의 메모리를 읽거나 복사하지 않습니다.
48행 그림 · 값 복사
함수·코드 범위: cpu_soft_restart
v6.18.37 machine_kexec의 IND_DONE 경로에서 호출한 경우 · 42행에서 x0=0으로 46행에 분기한 정상 경로
읽는 값의 내용과 출처
x3 = 0
새 진입점의 두 번째 인자 arg1입니다. 이 호출부는 0을 넘깁니다.
출처: machine_kexec의 네 번째 인자에 적힌 0입니다.
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 화살표는 값의 변화를 뜻합니다. 주소값을 옮겨도 그 주소의 메모리 내용은 복사하지 않습니다.
mov x2, x4 // arg2세 번째 새 이미지 인자를 x4에서 x2로 옮깁니다. machine_kexec의 호출에서는 0이 전달됩니다. MOV는 x2의 기존 값을 대체합니다. 원본이 주소값이어도 그 주소의 메모리를 읽거나 복사하지 않습니다.
49행 그림 · 값 복사
함수·코드 범위: cpu_soft_restart
v6.18.37 machine_kexec의 IND_DONE 경로에서 호출한 경우 · 42행에서 x0=0으로 46행에 분기한 정상 경로
읽는 값의 내용과 출처
x4 = 0
새 진입점의 세 번째 인자 arg2입니다. 이 호출부는 0을 넘깁니다.
출처: machine_kexec의 다섯 번째 인자에 적힌 0입니다.
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 화살표는 값의 변화를 뜻합니다. 주소값을 옮겨도 그 주소의 메모리 내용은 복사하지 않습니다.
- cpu-reset.S 머리말의 arg0 옆에는 relocation list라는 주석이 남아 있습니다. 여기서는 해당 버전의 실제 C 호출식이 전달하는 kimage->arch.dtb_mem을 기준으로 표시했습니다. 일반 인터페이스의 arg0 의미는 호출부와 새 진입점의 규약에 따라 정해집니다.
호출부: machine_kexec.c 187–195행 · kexec_file의 DTB 배치: machine_kexec_file.c 161–186행
br x8x8의 진입 주소로 직접 분기하여 새 커널 또는 purgatory에 제어를 넘깁니다. 이전 커널의 이 함수로 돌아오기 위한 복귀 주소는 만들지 않습니다. 레지스터가 담은 주소로 실행 위치를 바꾸며, 그 주소에서 값을 읽어 다른 레지스터에 복사하는 load 명령은 아닙니다.
50행 그림 · 호출과 실행 위치
함수·코드 범위: cpu_soft_restart
v6.18.37 machine_kexec의 IND_DONE 경로에서 호출한 경우 · 42행에서 x0=0으로 46행에 분기한 정상 경로
읽는 값의 내용과 출처
x8 = kimage->start · 새 커널 또는 purgatory 진입 주소
x1을 덮어쓴 뒤에도 사용할 분기 목적지입니다.
출처: 46행 mov x8, x1이 진입 때의 x1을 보관했습니다.
→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 새 복귀 주소를 LR에 기록하지 않습니다. RET 역시 메모리의 스택 값을 자동으로 꺼내는 명령은 아닙니다.
SYM_FUNC_END(cpu_soft_restart)도구에 이 코드 범위의 끝을 알려 크기와 심볼 정보를 기록합니다. CPU가 이 줄 때문에 자동으로 반환하지는 않습니다.
51행 그림 · 어셈블·링크 단계
함수·코드 범위: cpu_soft_restart
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
- 끝 표기 자체가 RET나 분기를 실행하지는 않습니다.
.popsectioncpu_soft_restart를 idmap에 배치하는 작업을 마치고 이전 .text 섹션으로 돌아갑니다. 이는 빌드 시의 섹션 선택입니다. 이는 빌드 중 출력할 바이트의 섹션을 선택하는 지시문이며, 실행 중 PC나 SP를 옮기지 않습니다.
53행 그림 · 어셈블·링크 단계
→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.
설명한 뒤 함께 생각해 볼 질문
진입점을 왜 x8에 먼저 옮기나요?
다음 줄에서 x1을 새 코드의 인자로 덮어씁니다. 분기할 주소를 그 전에 보존해야 합니다.
BR 대신 BLR을 쓰면 무엇이 달라지나요?
BLR은 LR에 복귀 주소를 새로 만듭니다. 이 경로는 새 코드로 실행을 넘기고 돌아오지 않으므로 BR을 사용합니다.
이 코드는 하드웨어 리셋과 완전히 같나요?
아닙니다. 필요한 CPU 제어 상태를 바꾸고 지정 주소로 이동하는 소프트웨어 경로입니다. 주변 장치 전체를 전원 초기 상태로 돌리는 코드는 아닙니다.
주석을 포함한 전체 원문
1/* SPDX-License-Identifier: GPL-2.0-only */2/*3* CPU reset routines4*5* Copyright (C) 2001 Deep Blue Solutions Ltd.6* Copyright (C) 2012 ARM Ltd.7* Copyright (C) 2015 Huawei Futurewei Technologies.8*/910#include <linux/linkage.h>11#include <linux/cfi_types.h>12#include <asm/assembler.h>13#include <asm/sysreg.h>14#include <asm/virt.h>1516.text17.pushsection .idmap.text, "a"1819/*20* cpu_soft_restart(el2_switch, entry, arg0, arg1, arg2)21*22* @el2_switch: Flag to indicate a switch to EL2 is needed.23* @entry: Location to jump to for soft reset.24* arg0: First argument passed to @entry. (relocation list)25* arg1: Second argument passed to @entry.(physical kernel entry)26* arg2: Third argument passed to @entry. (physical dtb address)27*28* Put the CPU into the same state as it would be if it had been reset, and29* branch to what would be the reset vector. It must be executed with the30* flat identity mapping.31*/32SYM_TYPED_FUNC_START(cpu_soft_restart)33mov_q x12, INIT_SCTLR_EL1_MMU_OFF34pre_disable_mmu_workaround35/*36* either disable EL1&0 translation regime or disable EL2&0 translation37* regime if HCR_EL2.E2H == 138*/39msr sctlr_el1, x1240isb4142cbz x0, 1f // el2_switch?43mov x0, #HVC_SOFT_RESTART44hvc #0 // no return45461: mov x8, x1 // entry47mov x0, x2 // arg048mov x1, x3 // arg149mov x2, x4 // arg250br x851SYM_FUNC_END(cpu_soft_restart)5253.popsection

