DUJINLABS.COM

Linux v6.18.37 · ARM64

cpu-reset.S — MMU를 끄고 새 진입점으로 이동하기

cpu_soft_restart는 현재 커널의 실행을 끝내고 지정된 주소로 이동합니다. .idmap.text에서 실행하므로 MMU를 꺼도 다음 명령을 같은 물리 위치에서 읽을 수 있습니다. 전원을 켰을 때의 모든 하드웨어 상태를 재현하는 함수는 아닙니다.

역할과 입력

  • x0: EL2 전환 여부 / x1: 새 진입점
  • x2·x3·x4: 새 코드에 전달할 세 인자

원본 .S · 빌드 조건: Makefile · 명령·주소 표기 읽는 법

53줄 · SHA-256: 0fc8025d86c2b3f6020cce56a657755e650a6b11f7a9153f71c7dac6dd23b0db

구조와 흐름

MMU를 끄고 새 진입점으로 이동하기
화살표: 처리 흐름입니다. 조건이 적힌 단계에서는 해당 조건에 따라 경로가 갈립니다.

실행 위치와 입력

x1은 코드 주소이고 x2–x4는 그 코드의 인자입니다. .pushsection은 이후 명령을 .idmap.text에 배치하며, 그 자체가 MMU를 설정하지는 않습니다.

cpu-reset.S · L1–L31
1/* SPDX-License-Identifier: GPL-2.0-only */
2/*
3 * CPU reset routines
4 *
5 * Copyright (C) 2001 Deep Blue Solutions Ltd.
6 * Copyright (C) 2012 ARM Ltd.
7 * Copyright (C) 2015 Huawei Futurewei Technologies.
8 */
9
10#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>
15
16.text
17.pushsection    .idmap.text, "a"
18
19/*
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, and
29 * branch to what would be the reset vector. It must be executed with the
30 * flat identity mapping.
31 */
L10
어셈블리 함수의 심볼과 크기를 선언하는 linkage 정의를 가져옵니다. cpu_soft_restart를 C 코드에서 호출할 수 있는 함수로 내보냅니다. #include는 전처리 단계에서 헤더 내용을 가져옵니다. CPU가 이 줄에서 함수를 호출하는 동작은 없습니다.
L11
간접 호출의 함수 형식 검사에 쓰는 CFI 관련 정의를 가져옵니다. cpu_soft_restart는 함수 포인터로도 호출되므로 SYM_TYPED_FUNC_START로 형식 정보가 있는 진입점을 선언합니다. #include는 전처리 단계에서 헤더 내용을 가져옵니다. CPU가 이 줄에서 함수를 호출하는 동작은 없습니다.
L12
큰 상수를 만드는 mov_q와 MMU 비활성화 직전의 CPU 문제 우회 매크로 등 공통 어셈블리 도구를 가져옵니다. #include는 전처리 단계에서 헤더 내용을 가져옵니다. CPU가 이 줄에서 함수를 호출하는 동작은 없습니다.
L13
SCTLR 제어 비트와 MMU-off 초기값을 가져옵니다. 주소 변환을 끌 때 필요한 엔디언·제어 설정을 하드코딩한 0 대신 이 정의로 구성합니다. #include는 전처리 단계에서 헤더 내용을 가져옵니다. CPU가 이 줄에서 함수를 호출하는 동작은 없습니다.
L14
HVC_SOFT_RESTART 같은 EL2 stub 요청 번호를 가져옵니다. EL2에서 새 이미지로 시작해야 하는 경우 이 번호를 x0로 전달합니다. #include는 전처리 단계에서 헤더 내용을 가져옵니다. CPU가 이 줄에서 함수를 호출하는 동작은 없습니다.
L16
기본 실행 코드 섹션을 선택합니다. 바로 뒤에서 실제 재시작 함수는 MMU 전환을 견디도록 .idmap.text에 배치합니다. 이는 빌드 중 출력할 바이트의 섹션을 선택하는 지시문이며, 실행 중 PC나 SP를 옮기지 않습니다.
L17
MMU를 끄는 동안에도 실행 주소가 유효하도록 이 함수를 가상 주소와 물리 주소가 같은 idmap 영역에 둡니다. 이는 빌드 중 출력할 바이트의 섹션을 선택하는 지시문이며, 실행 중 PC나 SP를 옮기지 않습니다. 이전 출력 섹션을 함께 기억하므로 뒤의 .popsection으로 되돌릴 수 있습니다.

주소 변환 끄기

x12에 MMU-off 초기 제어값을 구성합니다. pre_disable_mmu_workaround가 필요한 CPU 설정에서는 SCTLR 쓰기 전에 ISB가 추가됩니다. HCR_EL2.E2H가 1인 실행 환경에서는 SCTLR_EL1 이름의 접근 대상도 달라집니다.

cpu-reset.S · L32–L40
32SYM_TYPED_FUNC_START(cpu_soft_restart)
33	mov_q	x12, INIT_SCTLR_EL1_MMU_OFF
34	pre_disable_mmu_workaround
35	/*
36	 * either disable EL1&0 translation regime or disable EL2&0 translation
37	 * regime if HCR_EL2.E2H == 1
38	 */
39	msr	sctlr_el1, x12
40	isb
L32
CPU를 소프트웨어로 다음 실행 이미지에 넘기는 함수입니다. x0는 EL2로 전환할지, x1은 진입 주소, x2~x4는 그 진입점에 전달할 세 인자입니다. 주변 장치 전체에 하드웨어 리셋 신호를 보내는 함수는 아닙니다. 이 선언은 함수 심볼과 정렬을 정하고, ARM64 정의에서는 간접 호출이 도착할 BTI C 명령도 진입부에 넣습니다. TYPED 형식은 구성에 따라 간접 호출 검사에 필요한 함수 형식 정보도 제공합니다. 여기서는 전역 심벌로 정의하므로 다른 오브젝트에서도 이 함수 이름을 참조할 수 있습니다. 모듈용 EXPORT_SYMBOL 등록은 별도입니다.
L33
MMU와 캐시를 끈 상태로 사용할 SCTLR 초기값을 x12에 만듭니다. INIT_SCTLR_EL1_MMU_OFF는 단순한 0이 아니라 엔디언 설정과 필요한 제어 비트를 포함하며 M·C·I 비트는 포함하지 않습니다. mov_q는 64비트 상수를 필요한 MOVZ/MOVN·MOVK 조합으로 구성하는 매크로입니다. 해당 주소의 메모리에서 데이터를 가져오는 명령은 아닙니다.
L34
CONFIG_QCOM_FALKOR_ERRATUM_E1041이 설정된 빌드에서는 SCTLR의 M 비트를 내리기 직전에 ISB를 넣습니다. Qualcomm Falkor의 해당 CPU 문제를 우회하기 위한 순서 보장입니다.
L39
준비한 값을 SCTLR_EL1에 써서 주소 변환과 캐시 사용을 중단합니다. 이 지점 뒤에도 실행할 수 있도록 호출자가 idmap을 준비해 두어야 합니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.
L40
SCTLR 변경을 이후 명령 실행에 반영합니다. 다음 분기는 MMU를 끈 실행 환경에서 이루어집니다.

EL2 경유 또는 직접 이동

x0가 0이면 1번 라벨로 이동합니다. 0이 아니면 hyp-stub에 HVC_SOFT_RESTART를 요청합니다. 직접 이동 경로는 x1의 진입 주소를 먼저 x8에 보존한 뒤 인자를 한 칸씩 옮기고 BR x8을 실행합니다. LR을 새로 만들지 않습니다.

cpu-reset.S · L41–L53
41
42	cbz	x0, 1f				// el2_switch?
43	mov	x0, #HVC_SOFT_RESTART
44	hvc	#0				// no return
45
461:	mov	x8, x1				// entry
47	mov	x0, x2				// arg0
48	mov	x1, x3				// arg1
49	mov	x2, x4				// arg2
50	br	x8
51SYM_FUNC_END(cpu_soft_restart)
52
53.popsection
L42
EL2 전환 플래그가 0이면 현재 EL에서 직접 새 진입점으로 가는 경로를 택합니다. 0이 아니면 HVC를 통해 EL2 쪽 재시작 처리로 들어갑니다. 이 분기는 지정 레지스터의 값이나 비트를 직접 검사하며 CMP가 남긴 NZCV를 읽거나 바꾸지 않습니다. 조건이 맞지 않으면 바로 다음 명령으로 진행합니다.
L43
x0에 HVC_SOFT_RESTART 서비스 번호를 넣어 기존의 EL2 전환 플래그를 덮어씁니다. 이 MOV는 x0의 값만 바꾸며, x1~x4나 메모리의 내용을 복사하거나 수정하지 않습니다. 다음 줄의 hvc #0이 실행될 때 EL2 처리 코드가 x0의 요청 번호, x1의 진입 주소, x2~x4의 인자를 받습니다.
L44
EL2의 hyp stub에 소프트웨어 재시작을 요청합니다. 처리기는 x1의 주소로 분기하면서 x2~x4를 새 x0~x2로 옮기므로 성공 경로에서는 이 HVC 다음 줄로 돌아오지 않습니다. 일반 함수 호출처럼 BL로 이동하는 대신 동기 예외를 통해 요청을 전달합니다.
L46
직접 분기 경로에서 진입 주소를 x8에 옮깁니다. x1을 새 이미지의 인자 레지스터로 바꾸기 전에 분기 목적지를 보존합니다. MOV는 x8의 기존 값을 대체합니다. 원본이 주소값이어도 그 주소의 메모리를 읽거나 복사하지 않습니다.
L47
호출자가 넘긴 첫 번째 새 이미지 인자를 x2에서 x0로 옮깁니다. machine_kexec에서는 여기에 DTB 물리 주소를 전달합니다. MOV는 x0의 기존 값을 대체합니다. 원본이 주소값이어도 그 주소의 메모리를 읽거나 복사하지 않습니다.
L48
두 번째 새 이미지 인자를 x3에서 x1로 옮깁니다. machine_kexec의 호출에서는 0이 전달됩니다. MOV는 x1의 기존 값을 대체합니다. 원본이 주소값이어도 그 주소의 메모리를 읽거나 복사하지 않습니다.
L49
세 번째 새 이미지 인자를 x4에서 x2로 옮깁니다. machine_kexec의 호출에서는 0이 전달됩니다. MOV는 x2의 기존 값을 대체합니다. 원본이 주소값이어도 그 주소의 메모리를 읽거나 복사하지 않습니다.
L50
x8의 진입 주소로 직접 분기하여 새 커널 또는 purgatory에 제어를 넘깁니다. 이전 커널의 이 함수로 돌아오기 위한 복귀 주소는 만들지 않습니다. 레지스터가 담은 주소로 실행 위치를 바꾸며, 그 주소에서 값을 읽어 다른 레지스터에 복사하는 load 명령은 아닙니다.
L51
(cpu_soft_restart)의 심볼 타입과 크기를 기록합니다. 실행을 끝내는 RET를 생성하지 않습니다.
L53
cpu_soft_restart를 idmap에 배치하는 작업을 마치고 이전 .text 섹션으로 돌아갑니다. 이는 빌드 시의 섹션 선택입니다. 이는 빌드 중 출력할 바이트의 섹션을 선택하는 지시문이며, 실행 중 PC나 SP를 옮기지 않습니다.

매크로 정의와 확장

각 정의는 v6.18.37 원문입니다. 역슬래시 인자는 사용 위치의 값으로 치환되며, 안쪽 매크로와 조건부 블록은 이후 단계에서 다시 처리됩니다.

SYM_FUNC_END

아래 정의의 인자 치환, 조건부 생성과 중첩 매크로를 통해 코드를 만듭니다. SYM 계열의 타입·범위·BTI 차이는 공통 표기 설명에 정리했습니다.

include/linux/linkage.h L248–L249

앞뒤 조건을 포함한 원문

#define SYM_FUNC_END(name)				\
	SYM_END(name, SYM_T_FUNC)

SYM_TYPED_FUNC_START

아래 정의의 인자 치환, 조건부 생성과 중첩 매크로를 통해 코드를 만듭니다. SYM 계열의 타입·범위·BTI 차이는 공통 표기 설명에 정리했습니다.

arch/arm64/include/asm/linkage.h L42–L44

앞뒤 조건을 포함한 원문

#define SYM_TYPED_FUNC_START(name)				\
	SYM_TYPED_START(name, SYM_L_GLOBAL, SYM_A_ALIGN)	\
	bti c ;
include/linux/cfi_types.h L40–L41

앞뒤 조건을 포함한 원문

#define SYM_TYPED_FUNC_START(name) 			\
	SYM_TYPED_START(name, SYM_L_GLOBAL, SYM_A_ALIGN)

상수·구조체 오프셋

S_*·THREAD_*·FREGS_* 등의 구조체 오프셋은 빌드한 C 구조체 배치에서 생성됩니다. 숫자를 고정하지 않고 원래 기호를 사용했습니다.

asm-offsets.c의 생성 식

맨 위로 ↑