역할과 입력
- x0: kimage 구조체 주소
- 목록 항목 하위 비트: SOURCE / INDIRECTION / DESTINATION / DONE
원본 .S · 빌드 조건: Makefile · 명령·주소 표기 읽는 법
100줄 · SHA-256: 3a00a3683cdd216ebd9c8801b41da4e883cb1944163959f15c974e57367f7750
구조와 흐름
복사 가능한 코드 영역
turn_off_mmu는 SCTLR 초기값을 기록하고 ISB를 실행하는 로컬 매크로입니다. tmp2 인자는 이 정의에서 사용되지 않습니다. .kexec_relocate.text 범위의 크기와 시작 주소는 링커 스크립트의 ASSERT로 검사합니다.
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 <[email protected]>
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에는 캐시 라인 크기를 보존합니다.
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 비트가 없으면 반복합니다.
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를 시작합니다.
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 구조체 배치에서 생성됩니다. 숫자를 고정하지 않고 원래 기호를 사용했습니다.
HVC_SOFT_RESTART: arch/arm64/include/asm/virt.h:31IND_DESTINATION_BIT: include/linux/kexec.h:5IND_DONE_BIT: include/linux/kexec.h:7IND_INDIRECTION_BIT: include/linux/kexec.h:6IND_SOURCE_BIT: include/linux/kexec.h:8INIT_SCTLR_EL1_MMU_OFF: arch/arm64/include/asm/sysreg.h:867