DUJINLABS.COM

Linux v6.18.37 · ARM64

relocate_kernel.S — kexec 페이지 목록을 복사하고 새 커널 시작하기

현재 커널 메모리를 덮어쓰면서 새 커널을 배치하므로 이 루틴은 안전한 페이지로 복사되어 실행됩니다. kimage 구조체 역시 덮어써질 수 있어 필요한 값부터 레지스터로 옮깁니다.

역할과 입력

  • x0: kimage 구조체 주소
  • 목록 항목 하위 비트: SOURCE / INDIRECTION / DESTINATION / DONE

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

100줄 · SHA-256: 3a00a3683cdd216ebd9c8801b41da4e883cb1944163959f15c974e57367f7750

구조와 흐름

kexec 페이지 목록을 복사하고 새 커널 시작하기
화살표: 처리 흐름입니다. 조건이 적힌 단계에서는 해당 조건에 따라 경로가 갈립니다.

복사 가능한 코드 영역

turn_off_mmu는 SCTLR 초기값을 기록하고 ISB를 실행하는 로컬 매크로입니다. tmp2 인자는 이 정의에서 사용되지 않습니다. .kexec_relocate.text 범위의 크기와 시작 주소는 링커 스크립트의 ASSERT로 검사합니다.

relocate_kernel.S · L1–L38
1/* SPDX-License-Identifier: GPL-2.0-only */
2/*
3 * kexec for arm64
4 *
5 * Copyright (C) Linaro.
6 * Copyright (C) Huawei Futurewei Technologies.
7 * Copyright (C) 2021, Microsoft Corporation.
8 * Pasha Tatashin <pasha.tatashin@soleen.com>
9 */
10
11#include <linux/kexec.h>
12#include <linux/linkage.h>
13
14#include <asm/assembler.h>
15#include <asm/kexec.h>
16#include <asm/page.h>
17#include <asm/sysreg.h>
18#include <asm/virt.h>
19
20.macro turn_off_mmu tmp1, tmp2
21	mov_q   \tmp1, INIT_SCTLR_EL1_MMU_OFF
22	pre_disable_mmu_workaround
23	msr	sctlr_el1, \tmp1
24	isb
25.endm
26
27.section    ".kexec_relocate.text", "ax"
28/*
29 * arm64_relocate_new_kernel - Put a 2nd stage image in place and boot it.
30 *
31 * The memory that the old kernel occupies may be overwritten when copying the
32 * new image to its final location.  To assure that the
33 * arm64_relocate_new_kernel routine which does that copy is not overwritten,
34 * all code and data needed by arm64_relocate_new_kernel must be between the
35 * symbols arm64_relocate_new_kernel and arm64_relocate_new_kernel_end.  The
36 * machine_kexec() routine will copy arm64_relocate_new_kernel to the kexec
37 * safe memory that has been set up to be preserved during the copy operation.
38 */
L11
kexec 복사 목록의 항목 종류 비트 정의를 가져옵니다. 낮은 비트의 SOURCE·DESTINATION·INDIRECTION·DONE을 주소와 분리하여 목록을 해석합니다. #include는 전처리 단계에서 헤더 내용을 가져옵니다. CPU가 이 줄에서 함수를 호출하는 동작은 없습니다.
L12
복사 루틴의 심볼 시작·끝을 선언할 linkage 매크로를 가져옵니다. C 코드가 이 코드 범위를 별도 안전한 페이지로 복사하는 데 필요한 심볼 정보를 제공합니다. #include는 전처리 단계에서 헤더 내용을 가져옵니다. CPU가 이 줄에서 함수를 호출하는 동작은 없습니다.
L14
페이지 복사, 캐시 정리, TTBR 전환 같은 ARM64 공통 매크로를 가져옵니다. 기존 커널 메모리를 덮어쓰는 동안 외부 함수 호출에 의존하지 않고 작업하도록 펼쳐집니다. #include는 전처리 단계에서 헤더 내용을 가져옵니다. CPU가 이 줄에서 함수를 호출하는 동작은 없습니다.
L15
ARM64 kexec의 제어 코드 크기·아키텍처 번호·구조체 관련 정의를 포함합니다. 본문에서 쓰는 KIMAGE_* 필드 오프셋은 이 헤더 자체의 숫자 정의가 아니라 asm-offsets.c가 C 구조체 배치에서 생성하며, assembler.h를 통해 포함됩니다. #include는 전처리 단계에서 헤더 내용을 가져옵니다. CPU가 이 줄에서 함수를 호출하는 동작은 없습니다.
L16
PAGE_SIZE와 PAGE_MASK 등의 페이지 정의를 가져옵니다. 목록 엔트리에서 종류 비트를 지우고 한 페이지씩 복사하는 기준이 됩니다. #include는 전처리 단계에서 헤더 내용을 가져옵니다. CPU가 이 줄에서 함수를 호출하는 동작은 없습니다.
L17
새 이미지로 넘기기 전에 사용할 SCTLR의 MMU-off 설정을 가져옵니다. #include는 전처리 단계에서 헤더 내용을 가져옵니다. CPU가 이 줄에서 함수를 호출하는 동작은 없습니다.
L18
HVC_SOFT_RESTART 등 EL2로 재시작을 요청하는 데 필요한 번호를 가져옵니다. #include는 전처리 단계에서 헤더 내용을 가져옵니다. CPU가 이 줄에서 함수를 호출하는 동작은 없습니다.
L20
페이지 복사를 끝낸 뒤 새 이미지가 요구하는 MMU-off 상태로 바꾸는 인라인 매크로입니다. 이 정의에서 실제 사용되는 작업 레지스터는 tmp1이며 tmp2는 본문에서 사용하지 않습니다. 이 줄은 어셈블러 매크로 정의의 시작입니다. 본문은 매크로를 사용하는 위치에 펼쳐지며, 정의 자체가 런타임 함수 호출은 아닙니다.
L21
MMU·캐시를 끈 SCTLR 초기값을 tmp1에 준비합니다. 엔디언과 필요한 제어 비트를 포함하므로 단순히 0으로 쓰는 동작과는 다릅니다. mov_q는 64비트 상수를 필요한 MOVZ/MOVN·MOVK 조합으로 구성하는 매크로입니다. 해당 주소의 메모리에서 데이터를 가져오는 명령은 아닙니다.
L22
해당 Qualcomm Falkor 우회 설정이 있는 빌드에서는 SCTLR의 MMU 비트를 내리기 직전에 ISB를 넣습니다.
L23
준비한 제어값을 SCTLR_EL1에 써서 주소 변환과 캐시 사용을 중단합니다. 복사한 코드는 이미 idmap에서 실행 중이므로 이 전환을 견딜 수 있습니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.
L24
SCTLR 변경을 뒤따르는 명령에 반영합니다. 이후에는 새 이미지의 물리 진입 주소로 넘어갈 준비가 된 상태입니다.
L25
— 매크로 정의를 끝냅니다.
L27
kexec가 별도 안전한 페이지로 복사해 실행할 코드를 이 섹션에 모읍니다. 새 이미지로 기존 커널 메모리를 덮어쓰는 중에도 복사 루틴 자신은 살아 있어야 합니다. 이는 빌드 중 출력할 바이트의 섹션을 선택하는 지시문이며, 실행 중 PC나 SP를 옮기지 않습니다.

kimage에서 필요한 값 읽기

x28·x27·x26에 진입점·EL2 벡터·DTB 주소를 보존합니다. x18은 제로 페이지, x17은 작업용 선형 매핑, x16은 첫 목록 항목, x22는 주소 변환 오프셋입니다. x15에는 캐시 라인 크기를 보존합니다.

relocate_kernel.S · L39–L55
39SYM_CODE_START(arm64_relocate_new_kernel)
40	/*
41	 * The kimage structure isn't allocated specially and may be clobbered
42	 * during relocation. We must load any values we need from it prior to
43	 * any relocation occurring.
44	 */
45	ldr	x28, [x0, #KIMAGE_START]
46	ldr	x27, [x0, #KIMAGE_ARCH_EL2_VECTORS]
47	ldr	x26, [x0, #KIMAGE_ARCH_DTB_MEM]
48
49	/* Setup the list loop variables. */
50	ldr	x18, [x0, #KIMAGE_ARCH_ZERO_PAGE] /* x18 = zero page for BBM */
51	ldr	x17, [x0, #KIMAGE_ARCH_TTBR1]	/* x17 = linear map copy */
52	ldr	x16, [x0, #KIMAGE_HEAD]		/* x16 = kimage_head */
53	ldr	x22, [x0, #KIMAGE_ARCH_PHYS_OFFSET]	/* x22 phys_offset */
54	raw_dcache_line_size x15, x1		/* x15 = dcache line size */
55	break_before_make_ttbr_switch	x18, x17, x1, x2 /* set linear map */
L39
x0의 kimage 구조체를 받아 새 커널의 페이지를 최종 위치로 옮기는 진입점입니다. C 코드가 미리 마련한 전환용 페이지 테이블과 복사 목록을 사용하며, 완료하면 새 이미지로 넘어갑니다. 이 매크로는 정렬된 코드 시작 심벌을 정의하고 다른 오브젝트에서도 참조할 수 있게 합니다.
L45
kimage.start를 x28에 보관합니다. 복사가 끝난 뒤 분기할 주소이며, 적재 방식에 따라 커널 진입점 또는 진입 전 검증을 수행하는 purgatory일 수 있습니다. 실제로 x0 + (KIMAGE_START) 주소의 메모리 8바이트를 x28에 읽습니다. 이 주소 형식은 기준 레지스터 x0 자체를 갱신하지 않습니다.
L46
전환 중 사용할 EL2 벡터 주소를 x27에 읽습니다. 0이 아니면 마지막에 EL2의 HVC_SOFT_RESTART 경로를 이용해야 함을 뜻합니다. 실제로 x0 + (KIMAGE_ARCH_EL2_VECTORS) 주소의 메모리 8바이트를 x27에 읽습니다. 이 주소 형식은 기준 레지스터 x0 자체를 갱신하지 않습니다.
L47
새 커널에 넘길 DTB의 물리 주소를 x26에 보관합니다. 복사 루프가 끝나면 부팅 규약에 맞춰 x0에 전달합니다. 실제로 x0 + (KIMAGE_ARCH_DTB_MEM) 주소의 메모리 8바이트를 x26에 읽습니다. 이 주소 형식은 기준 레지스터 x0 자체를 갱신하지 않습니다.
L50
TTBR1 교체 때 임시로 사용할 빈 페이지의 물리 주소를 x18에 읽습니다. 기존·새 페이지 테이블을 섞어 참조하지 않도록 중간에 빈 테이블을 거칩니다. 실제로 x0 + (KIMAGE_ARCH_ZERO_PAGE) 주소의 메모리 8바이트를 x18에 읽습니다. 이 주소 형식은 기준 레지스터 x0 자체를 갱신하지 않습니다.
L51
복사 작업 동안 사용할 별도 linear map 테이블의 물리 주소를 x17에 읽습니다. 기존 커널의 원래 페이지 테이블은 곧 덮어써질 수 있습니다. 실제로 x0 + (KIMAGE_ARCH_TTBR1) 주소의 메모리 8바이트를 x17에 읽습니다. 이 주소 형식은 기준 레지스터 x0 자체를 갱신하지 않습니다.
L52
물리 주소와 항목 종류 비트가 결합된 복사 목록의 첫 엔트리를 x16에 읽습니다. 이후 SOURCE, INDIRECTION, DESTINATION, DONE 비트를 구분하여 해석합니다. 실제로 x0 + (KIMAGE_HEAD) 주소의 메모리 8바이트를 x16에 읽습니다. 이 주소 형식은 기준 레지스터 x0 자체를 갱신하지 않습니다.
L53
물리 주소와 현재 linear map 가상 주소 사이의 차이를 x22에 읽습니다. C 코드가 virt_to_phys(kimage) - (long)kimage로 준비한 값이며 페이지 주소에서 이를 빼면 복사에 사용할 가상 주소가 됩니다. 실제로 x0 + (KIMAGE_ARCH_PHYS_OFFSET) 주소의 메모리 8바이트를 x22에 읽습니다. 이 주소 형식은 기준 레지스터 x0 자체를 갱신하지 않습니다.
L54
현재 CPU의 CTR_EL0에서 데이터 캐시 라인 크기를 x15에 구합니다. 나중에 복사한 페이지를 이 간격으로 clean·invalidate하며 x1은 이 계산에만 쓰는 임시 값입니다. CTR_EL0.DminLine은 바이트 수 자체가 아닙니다. 4를 이 필드 값만큼 왼쪽으로 이동하여 바이트 수를 구합니다.
L55
빈 페이지 x18을 거쳐 전환용 테이블 x17을 TTBR1에 설치합니다. 기존 TLB 항목을 무효화하여 원래 커널의 페이지 테이블이 덮어써져도 복사에 필요한 주소 변환을 유지합니다.

플래그로 구분하는 페이지 목록

PAGE_MASK로 항목의 주소만 남긴 뒤 x22를 빼 현재 선형 매핑의 VA를 얻습니다. SOURCE 항목은 x13의 목적지에 복사하고 CIVAC를 수행합니다. INDIRECTION은 다음 목록 포인터 x14를, DESTINATION은 x13을 바꿉니다. x14에서 다음 8바이트 항목을 읽고 DONE 비트가 없으면 반복합니다.

relocate_kernel.S · L56–L78
56.Lloop:
57	and	x12, x16, PAGE_MASK		/* x12 = addr */
58	sub	x12, x12, x22			/* Convert x12 to virt */
59	/* Test the entry flags. */
60.Ltest_source:
61	tbz	x16, IND_SOURCE_BIT, .Ltest_indirection
62
63	/* Invalidate dest page to PoC. */
64	mov	x19, x13
65	copy_page x13, x12, x1, x2, x3, x4, x5, x6, x7, x8
66	add	x1, x19, #PAGE_SIZE
67	dcache_by_myline_op civac, sy, x19, x1, x15, x20
68	b	.Lnext
69.Ltest_indirection:
70	tbz	x16, IND_INDIRECTION_BIT, .Ltest_destination
71	mov	x14, x12			/* ptr = addr */
72	b	.Lnext
73.Ltest_destination:
74	tbz	x16, IND_DESTINATION_BIT, .Lnext
75	mov	x13, x12			/* dest = addr */
76.Lnext:
77	ldr	x16, [x14], #8			/* entry = *ptr++ */
78	tbz	x16, IND_DONE_BIT, .Lloop	/* while (!(entry & DONE)) */
L56
x16의 복사 목록 엔트리에서 페이지 주소를 꺼내 해석을 시작하는 반복 지점입니다. 레이블은 이 위치에 붙인 이름이며, 이름을 적는 것만으로 CPU 명령이 추가되지는 않습니다.
L57
목록 엔트리의 하위 종류 비트를 PAGE_MASK로 제거하여 페이지 물리 주소만 x12에 남깁니다. AND는 두 피연산자의 같은 위치 비트가 모두 1인 곳만 결과에 남깁니다.
L58
페이지 물리 주소에서 미리 계산한 차이 x22를 빼 linear map의 가상 주소로 바꿉니다. 아직 MMU가 켜져 있으므로 페이지 복사와 목록 읽기는 이 가상 주소를 사용합니다. 이 SUB 형식은 NZCV 조건 플래그를 바꾸지 않습니다.
L60
현재 엔트리가 복사 원본 페이지인지 검사할 위치입니다. 원본이 아니면 다른 항목 종류를 순서대로 검사합니다. 레이블은 이 위치에 붙인 이름이며, 이름을 적는 것만으로 CPU 명령이 추가되지는 않습니다.
L61
SOURCE 비트가 없으면 이 항목을 원본 데이터 페이지로 해석하지 않고 다음 종류 검사로 넘어갑니다. 비트가 있으면 x12의 내용을 현재 목적지 x13으로 복사합니다. 이 분기는 지정 레지스터의 값이나 비트를 직접 검사하며 CMP가 남긴 NZCV를 읽거나 바꾸지 않습니다. 조건이 맞지 않으면 바로 다음 명령으로 진행합니다.
L64
목적지의 시작 주소를 x19에 남깁니다. copy_page가 x13을 페이지 끝까지 증가시키므로 복사 후 캐시를 정리할 시작 주소가 따로 필요합니다. MOV는 x19의 기존 값을 대체합니다. 원본이 주소값이어도 그 주소의 메모리를 읽거나 복사하지 않습니다.
L65
x12의 원본 페이지를 현재 목적지 x13으로 복사합니다. x1~x8은 64바이트씩 운반하는 작업 레지스터이며 x12·x13은 복사 후 다음 페이지 위치까지 증가합니다.
L66
보관한 목적지 시작 x19에 PAGE_SIZE를 더해 캐시 처리 범위의 끝 주소를 만듭니다. 이 ADD 형식은 NZCV 조건 플래그를 바꾸지 않습니다.
L67
복사한 [x19, x1) 범위를 x15의 캐시 라인 크기씩 돌며 DC CIVAC를 수행합니다. 새 내용이 PoC까지 반영되도록 clean하고 캐시 라인을 invalidate한 뒤 DSB SY로 완료를 기다립니다. 시작 주소를 먼저 캐시 라인 경계로 내림 정렬하므로 범위의 첫 바이트가 속한 라인부터 처리합니다.
L68
한 원본 페이지 처리를 마쳤으므로 다음 목록 엔트리를 읽는 공통 지점으로 넘어갑니다. 이 분기는 LR에 새 복귀 주소를 기록하지 않습니다.
L69
현재 엔트리가 다른 목록 페이지를 가리키는지 검사할 위치입니다. 레이블은 이 위치에 붙인 이름이며, 이름을 적는 것만으로 CPU 명령이 추가되지는 않습니다.
L70
INDIRECTION 비트가 없으면 목적지 지정 항목인지 검사합니다. 이 비트가 있으면 x12는 이미지 데이터가 아니라 다음 엔트리들이 담긴 목록 페이지 주소입니다. 이 분기는 지정 레지스터의 값이나 비트를 직접 검사하며 CMP가 남긴 NZCV를 읽거나 바꾸지 않습니다. 조건이 맞지 않으면 바로 다음 명령으로 진행합니다.
L71
새 목록 페이지 주소를 목록 커서 x14에 넣습니다. 이후 x14에서 8바이트 엔트리를 차례로 읽어 갑니다. MOV는 x14의 기존 값을 대체합니다. 원본이 주소값이어도 그 주소의 메모리를 읽거나 복사하지 않습니다.
L72
목록 페이지를 바꿨으므로 공통 읽기 지점에서 그 페이지의 첫 엔트리를 가져옵니다. 이 분기는 LR에 새 복귀 주소를 기록하지 않습니다.
L73
현재 엔트리가 이후 복사의 목적지 주소를 지정하는지 검사할 위치입니다. 레이블은 이 위치에 붙인 이름이며, 이름을 적는 것만으로 CPU 명령이 추가되지는 않습니다.
L74
DESTINATION 비트도 없으면 목적지를 바꾸지 않고 다음 엔트리로 넘어갑니다. 있으면 이어서 나올 SOURCE 페이지들의 복사 목적지를 지정합니다. 이 분기는 지정 레지스터의 값이나 비트를 직접 검사하며 CMP가 남긴 NZCV를 읽거나 바꾸지 않습니다. 조건이 맞지 않으면 바로 다음 명령으로 진행합니다.
L75
엔트리가 지정한 목적지 가상 주소를 x13에 저장합니다. 뒤따르는 원본 페이지들을 복사할 때 이 주소가 한 페이지씩 전진합니다. MOV는 x13의 기존 값을 대체합니다. 원본이 주소값이어도 그 주소의 메모리를 읽거나 복사하지 않습니다.
L76
각 항목 처리가 끝난 뒤 다음 8바이트 엔트리를 읽는 공통 지점입니다. 레이블은 이 위치에 붙인 이름이며, 이름을 적는 것만으로 CPU 명령이 추가되지는 않습니다.
L77
목록 커서 x14에서 64비트 엔트리 하나를 x16에 읽고 커서를 8바이트 전진시킵니다. 아직 데이터 페이지를 복사하는 명령은 아닙니다. 대괄호 밖의 오프셋 8은 후위 갱신입니다. 기존 x14 주소로 접근한 뒤 x14에 이 값을 더합니다.
L78
DONE 비트가 없으면 새로 읽은 엔트리를 처리합니다. DONE이 있으면 복사 목록의 끝이므로 캐시·MMU 전환 마무리로 진행합니다. 이 분기는 지정 레지스터의 값이나 비트를 직접 검사하며 CMP가 남긴 NZCV를 읽거나 바꾸지 않습니다. 조건이 맞지 않으면 바로 다음 명령으로 진행합니다.

복사 완료와 새 진입점

DSB와 I-cache 무효화 뒤 MMU를 끕니다. x27이 0이 아니면 hyp-stub의 HVC_SOFT_RESTART를 사용합니다. 직접 경로에서는 x0=DTB, x1=x2=x3=0을 설정한 뒤 BR x28로 새 Image를 시작합니다.

relocate_kernel.S · L79–L100
79	/* wait for writes from copy_page to finish */
80	dsb	nsh
81	ic	iallu
82	dsb	nsh
83	isb
84	turn_off_mmu x12, x13
85
86	/* Start new image. */
87	cbz	x27, .Lel1
88	mov	x1, x28				/* kernel entry point */
89	mov	x2, x26				/* dtb address */
90	mov	x3, xzr
91	mov	x4, xzr
92	mov     x0, #HVC_SOFT_RESTART
93	hvc	#0				/* Jumps from el2 */
94.Lel1:
95	mov	x0, x26				/* dtb address */
96	mov	x1, xzr
97	mov	x2, xzr
98	mov	x3, xzr
99	br	x28				/* Jumps from el1 */
100SYM_CODE_END(arm64_relocate_new_kernel)
L80
앞선 메모리·캐시 작업의 로컬 완료를 기다립니다. 새 코드를 실행하기 전에 복사 결과가 준비되어 있어야 합니다.
L81
현재 CPU의 명령 캐시를 무효화하여 새로 복사한 명령 대신 낡은 명령을 가져오는 일을 막습니다. 무효화 요청과 이후 명령 실행에 대한 동기화는 구분되며, 뒤의 DSB·ISB가 그 순서를 마무리합니다.
L82
명령 캐시 무효화가 완료될 때까지 기다립니다.
L83
파이프라인을 동기화하여 이후 명령 가져오기가 갱신된 캐시 상태를 따르게 합니다.
L84
복사와 캐시 정리가 끝났으므로 MMU와 캐시를 끕니다. x12는 SCTLR 값을 만드는 데 사용되며 x13 인자는 이 매크로 본문에서는 사용하지 않습니다. 매크로는 SCTLR_EL1에 MMU-off 제어값을 쓰고 ISB로 이후 명령을 동기화합니다.
L87
EL2 벡터 주소가 0이면 현재 EL에서 새 이미지로 직접 분기합니다. 주소가 있으면 전환용 EL2 벡터에 HVC를 보내 EL2에서 시작하게 합니다. 이 분기는 지정 레지스터의 값이나 비트를 직접 검사하며 CMP가 남긴 NZCV를 읽거나 바꾸지 않습니다. 조건이 맞지 않으면 바로 다음 명령으로 진행합니다.
L88
HVC_SOFT_RESTART의 진입 주소 인자로 kimage.start를 x1에 넣습니다. MOV는 x1의 기존 값을 대체합니다. 원본이 주소값이어도 그 주소의 메모리를 읽거나 복사하지 않습니다.
L89
새 이미지의 첫 인자인 DTB 물리 주소를 x2에 넣습니다. EL2 처리기가 이를 x0로 옮겨 새 이미지에 전달합니다. MOV는 x2의 기존 값을 대체합니다. 원본이 주소값이어도 그 주소의 메모리를 읽거나 복사하지 않습니다.
L90
새 이미지의 두 번째 인자를 0으로 준비합니다. EL2 처리기에서 x1로 옮겨집니다. MOV는 x3의 기존 값을 대체합니다. 원본이 주소값이어도 그 주소의 메모리를 읽거나 복사하지 않습니다.
L91
새 이미지의 세 번째 인자를 0으로 준비합니다. EL2 처리기에서 x2로 옮겨집니다. MOV는 x4의 기존 값을 대체합니다. 원본이 주소값이어도 그 주소의 메모리를 읽거나 복사하지 않습니다.
L92
x0에 HVC_SOFT_RESTART 서비스 번호를 넣어 기존 값을 덮어씁니다. 이 MOV는 x1~x4와 메모리를 바꾸지 않습니다. 다음 hvc #0이 실행되면 EL2 처리 코드가 앞서 준비한 진입 주소와 인자를 사용합니다. HVC 명령의 즉시값 #0과 달리 x0의 HVC_SOFT_RESTART 값이 요청할 서비스 종류를 정합니다.
L93
복사 중에도 유지되는 EL2 벡터의 재시작 처리기로 넘어갑니다. 처리기는 준비한 인자를 재배치하고 새 이미지 주소로 직접 분기합니다. 일반 함수 호출처럼 BL로 이동하는 대신 동기 예외를 통해 요청을 전달합니다.
L94
EL2 경유가 필요 없는 경우 부팅 인자를 직접 배치하고 새 이미지로 분기하는 지점입니다. 레이블은 이 위치에 붙인 이름이며, 이름을 적는 것만으로 CPU 명령이 추가되지는 않습니다.
L95
직접 분기 경로에서는 DTB의 물리 주소를 부팅 인자 x0에 넣습니다. MOV는 x0의 기존 값을 대체합니다. 원본이 주소값이어도 그 주소의 메모리를 읽거나 복사하지 않습니다.
L96
AArch64 커널 진입 규약에 따라 예약된 부팅 인자 x1을 0으로 만듭니다. MOV는 x1의 기존 값을 대체합니다. 원본이 주소값이어도 그 주소의 메모리를 읽거나 복사하지 않습니다.
L97
예약된 부팅 인자 x2를 0으로 만듭니다. MOV는 x2의 기존 값을 대체합니다. 원본이 주소값이어도 그 주소의 메모리를 읽거나 복사하지 않습니다.
L98
예약된 부팅 인자 x3을 0으로 만듭니다. MOV는 x3의 기존 값을 대체합니다. 원본이 주소값이어도 그 주소의 메모리를 읽거나 복사하지 않습니다.
L99
복사를 마친 kimage.start 주소로 넘어갑니다. 적재 방식에 따라 커널 또는 purgatory가 실행되며, 이전 커널의 복사 루틴으로 돌아오지 않습니다. 이 분기는 LR에 새 복귀 주소를 기록하지 않습니다. 레지스터가 담은 주소로 실행 위치를 바꾸며, 그 주소에서 값을 읽어 다른 레지스터에 복사하는 load 명령은 아닙니다.
L100
(arm64_relocate_new_kernel)의 심볼 타입과 크기를 기록합니다. 실행을 끝내는 RET를 생성하지 않습니다.

매크로 정의와 확장

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

SYM_CODE_END

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

include/linux/linkage.h L305–L306

앞뒤 조건을 포함한 원문

#define SYM_CODE_END(name)				\
	SYM_END(name, SYM_T_NONE)

SYM_CODE_START

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

include/linux/linkage.h L278–L279

앞뒤 조건을 포함한 원문

#define SYM_CODE_START(name)				\
	SYM_START(name, SYM_L_GLOBAL, SYM_A_ALIGN)

turn_off_mmu

SCTLR_EL1에 MMU-off 초기 제어값을 쓰고 ISB로 동기화합니다. 두 번째 매개변수는 현재 정의에서 사용되지 않습니다.

arch/arm64/kernel/relocate_kernel.S L20–L25

앞뒤 조건을 포함한 원문

.macro turn_off_mmu tmp1, tmp2
	mov_q   \tmp1, INIT_SCTLR_EL1_MMU_OFF
	pre_disable_mmu_workaround
	msr	sctlr_el1, \tmp1
	isb
.endm
인자 치환 예: turn_off_mmu x12, x13

1단계 치환 결과입니다. 내부 매크로·조건·고유 라벨 번호는 아직 펼치지 않았습니다.

	mov_q   x12, INIT_SCTLR_EL1_MMU_OFF
	pre_disable_mmu_workaround
	msr	sctlr_el1, x12
	isb

상수·구조체 오프셋

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

asm-offsets.c의 생성 식

맨 위로 ↑