DUJINLABS.COM

Linux v6.18.37 · ARM64

relocate_kernel.S — 말로 풀어 읽기

이 파일은 왜 필요한가요?

kexec는 현재 커널에서 새 커널로 넘어가는 기능입니다. 새 커널 페이지를 최종 위치에 복사하는 동안 현재 커널의 자료구조도 덮어써질 수 있습니다. 그래서 먼저 필요한 값을 레지스터에 보존하고, 페이지 목록을 해석해 복사를 마친 뒤 새 진입점으로 이동합니다.

명령·지시문·라벨을 원본 줄 번호에 맞춰 설명합니다. 주석과 빈 줄을 포함한 전체 원문은 아래에 따로 두었습니다. 매크로 정의와 조건부 코드는 모두 차례대로 실행되는 명령 목록이 아닙니다.

기존 코드 분석과 함께 읽기 · 원본 파일 · 설명 원고

먼저 알아둘 개념: 주소와 주소가 가리키는 내용, 레지스터와 값, 가상 주소와 MMU, 데이터 캐시와 명령어 캐시, 순서 보장과 완료 대기

처음 읽을 때

목록 항목은 주소만 담지 않고 하위 비트에 종류 표시도 붙입니다. 먼저 주소 부분과 플래그 부분을 분리해 읽으세요.

더 깊이 살펴볼 때

안전한 실행 페이지, 별도 선형 매핑, 복사 뒤 캐시 관리, MMU-off 전환을 하나의 의존 순서로 설명해 보세요. 어느 단계가 빠지면 다음 단계가 성립하지 않는지 생각해 봅니다.

한 줄씩 레지스터와 메모리 상태 보기

각 코드 아래의 ‘행 그림’을 펼치면, 그 줄에서 읽는 값과 실행 전·후의 상태를 나란히 볼 수 있습니다. 여기서 ‘명령 직전’은 지금 보고 있는 명령이 실행되기 바로 전을 뜻합니다. 함수 진입 때나 소스의 바로 윗줄을 가리키는 말은 아닙니다. 앞서 실행한 명령이 값을 바꿨다면 그 결과가 들어 있습니다.

소스로 정해지는 상수, 호출자가 넘기는 값, 실제 실행에서 정해지는 주소를 구분해서 읽어야 합니다. 주소 숫자를 몰라도 무엇을 가리키는지와 어디서 받아 오는지는 설명할 수 있습니다. ‘명령 직전 x0의 값’처럼 일반 표기로 남은 곳은 한 명령의 동작을 나타내며, 특정 호출 경로의 값까지 확인했다는 뜻은 아닙니다.

일반 명령은 그 한 줄의 효과를, 매크로는 여러 명령을 묶은 주요 결과를 표시합니다. 함수 호출은 호출 직후의 PC와 LR까지만 나타냅니다. 메모리 접근은 정상적으로 끝나는 경우를 가정합니다. 생략된 항목까지 모두 보존된다는 뜻은 아닙니다.

화살표 →는 같은 항목의 전후 비교입니다. 메모리 칸에는 주소와 크기를 따로 적었습니다. SP가 줄어들면 낮은 주소 쪽을 사용하며, 화면의 위아래와 메모리 주소 방향은 별개입니다. 전처리·어셈블·링크 지시문은 CPU 실행과 구분합니다.

‘이전·다음 행’은 소스에 적힌 순서입니다. 분기·반복·호출이 있으므로 파일 위에서 아래로 실제 실행된다고 가정하지 않습니다. 값이 여러 줄에 걸쳐 이어지는 모습은 cpu-reset.S의 단계별 예제에서 보실 수 있습니다.

총 63개 줄에 그림을 붙였습니다. 기존 코드와 설명은 그 아래에서 이어집니다.

먼저 그림으로 살펴보기

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

복사 가능한 코드 영역

원본 1–38행

#include <linux/kexec.h>

kexec 복사 목록의 항목 종류 비트 정의를 가져옵니다. 낮은 비트의 SOURCE·DESTINATION·INDIRECTION·DONE을 주소와 분리하여 목록을 해석합니다. #include는 전처리 단계에서 헤더 내용을 가져옵니다. CPU가 이 줄에서 함수를 호출하는 동작은 없습니다.

11행 그림 · 전처리 단계
소스
처리 전전처리 전
이 줄의 효과#include <linux/kexec.h>
CPU 레지스터
처리 전해당 없음
이 줄의 효과실행되는 CPU 명령이 없습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

#include <linux/linkage.h>

복사 루틴의 심볼 시작·끝을 선언할 linkage 매크로를 가져옵니다. C 코드가 이 코드 범위를 별도 안전한 페이지로 복사하는 데 필요한 심볼 정보를 제공합니다. #include는 전처리 단계에서 헤더 내용을 가져옵니다. CPU가 이 줄에서 함수를 호출하는 동작은 없습니다.

12행 그림 · 전처리 단계
소스
처리 전전처리 전
이 줄의 효과#include <linux/linkage.h>
CPU 레지스터
처리 전해당 없음
이 줄의 효과실행되는 CPU 명령이 없습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

#include <asm/assembler.h>

페이지 복사, 캐시 정리, TTBR 전환 같은 ARM64 공통 매크로를 가져옵니다. 기존 커널 메모리를 덮어쓰는 동안 외부 함수 호출에 의존하지 않고 작업하도록 펼쳐집니다. #include는 전처리 단계에서 헤더 내용을 가져옵니다. CPU가 이 줄에서 함수를 호출하는 동작은 없습니다.

14행 그림 · 전처리 단계
소스
처리 전전처리 전
이 줄의 효과#include <asm/assembler.h>
CPU 레지스터
처리 전해당 없음
이 줄의 효과실행되는 CPU 명령이 없습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

#include <asm/kexec.h>

ARM64 kexec의 제어 코드 크기·아키텍처 번호·구조체 관련 정의를 포함합니다. 본문에서 쓰는 KIMAGE_* 필드 오프셋은 이 헤더 자체의 숫자 정의가 아니라 asm-offsets.c가 C 구조체 배치에서 생성하며, assembler.h를 통해 포함됩니다. #include는 전처리 단계에서 헤더 내용을 가져옵니다. CPU가 이 줄에서 함수를 호출하는 동작은 없습니다.

15행 그림 · 전처리 단계
소스
처리 전전처리 전
이 줄의 효과#include <asm/kexec.h>
CPU 레지스터
처리 전해당 없음
이 줄의 효과실행되는 CPU 명령이 없습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

#include <asm/page.h>

PAGE_SIZE와 PAGE_MASK 등의 페이지 정의를 가져옵니다. 목록 엔트리에서 종류 비트를 지우고 한 페이지씩 복사하는 기준이 됩니다. #include는 전처리 단계에서 헤더 내용을 가져옵니다. CPU가 이 줄에서 함수를 호출하는 동작은 없습니다.

16행 그림 · 전처리 단계
소스
처리 전전처리 전
이 줄의 효과#include <asm/page.h>
CPU 레지스터
처리 전해당 없음
이 줄의 효과실행되는 CPU 명령이 없습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

#include <asm/sysreg.h>

새 이미지로 넘기기 전에 사용할 SCTLR의 MMU-off 설정을 가져옵니다. #include는 전처리 단계에서 헤더 내용을 가져옵니다. CPU가 이 줄에서 함수를 호출하는 동작은 없습니다.

17행 그림 · 전처리 단계
소스
처리 전전처리 전
이 줄의 효과#include <asm/sysreg.h>
CPU 레지스터
처리 전해당 없음
이 줄의 효과실행되는 CPU 명령이 없습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

#include <asm/virt.h>

HVC_SOFT_RESTART 등 EL2로 재시작을 요청하는 데 필요한 번호를 가져옵니다. #include는 전처리 단계에서 헤더 내용을 가져옵니다. CPU가 이 줄에서 함수를 호출하는 동작은 없습니다.

18행 그림 · 전처리 단계
소스
처리 전전처리 전
이 줄의 효과#include <asm/virt.h>
CPU 레지스터
처리 전해당 없음
이 줄의 효과실행되는 CPU 명령이 없습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

.macro turn_off_mmu tmp1, tmp2

페이지 복사를 끝낸 뒤 새 이미지가 요구하는 MMU-off 상태로 바꾸는 인라인 매크로입니다. 이 정의에서 실제 사용되는 작업 레지스터는 tmp1이며 tmp2는 본문에서 사용하지 않습니다. 이 줄은 어셈블러 매크로 정의의 시작입니다. 본문은 매크로를 사용하는 위치에 펼쳐지며, 정의 자체가 런타임 함수 호출은 아닙니다.

20행 그림 · 어셈블·링크 단계

매크로 본문: turn_off_mmu

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과.macro turn_off_mmu tmp1, tmp2
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 뒤의 본문은 이 이름을 사용하는 위치에 펼쳐집니다. 정의 위치를 CPU가 함수처럼 호출하지 않습니다.
	mov_q   \tmp1, INIT_SCTLR_EL1_MMU_OFF

MMU·캐시를 끈 SCTLR 초기값을 tmp1에 준비합니다. 엔디언과 필요한 제어 비트를 포함하므로 단순히 0으로 쓰는 동작과는 다릅니다. mov_q는 64비트 상수를 필요한 MOVZ/MOVN·MOVK 조합으로 구성하는 매크로입니다. 해당 주소의 메모리에서 데이터를 가져오는 명령은 아닙니다.

21행 그림 · 값 복사

매크로 본문: turn_off_mmu

\tmp1
이 명령을 실행하기 직전명령 직전 \tmp1의 값
이 명령을 실행한 직후INIT_SCTLR_EL1_MMU_OFF

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 화살표는 값의 변화를 뜻합니다. 주소값을 옮겨도 그 주소의 메모리 내용은 복사하지 않습니다.
  • mov_q는 큰 상수를 여러 MOVZ/MOVN·MOVK 명령으로 구성할 수 있습니다. 여기서는 매크로 전체 결과를 표시합니다.
  • 매크로 turn_off_mmu의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
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_workaround

해당 Qualcomm Falkor 우회 설정이 있는 빌드에서는 SCTLR의 MMU 비트를 내리기 직전에 ISB를 넣습니다.

22행 그림 · 동기화·캐시·대기 상태

매크로 본문: turn_off_mmu

명령 실행 문맥
이 명령을 실행하기 직전실행 전 상태
이 명령을 실행한 직후Falkor 우회 빌드이면 ISB, 아니면 빈 매크로

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 매크로 turn_off_mmu의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
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, \tmp1

준비한 제어값을 SCTLR_EL1에 써서 주소 변환과 캐시 사용을 중단합니다. 복사한 코드는 이미 idmap에서 실행 중이므로 이 전환을 견딜 수 있습니다. 이 접근 대상은 CPU의 시스템 레지스터이며, 일반 RAM에 대한 load/store가 아닙니다.

23행 그림 · 시스템 레지스터 쓰기

매크로 본문: turn_off_mmu

읽는 값\tmp1
SCTLR_EL1
이 명령을 실행하기 직전명령 직전 SCTLR_EL1의 값
이 명령을 실행한 직후명령 직전 \tmp1의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 접근 권한과 VHE 별칭 규칙에 따라 실제 대상이 정해집니다. 뒤의 ISB·DSB가 필요한지는 이 레지스터와 문맥에 따릅니다.
  • 매크로 turn_off_mmu의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
	isb

SCTLR 변경을 뒤따르는 명령에 반영합니다. 이후에는 새 이미지의 물리 진입 주소로 넘어갈 준비가 된 상태입니다.

24행 그림 · 동기화·캐시·대기 상태

매크로 본문: turn_off_mmu

명령 실행 문맥
이 명령을 실행하기 직전실행 전 상태
이 명령을 실행한 직후앞서 바꾼 상태를 이후 명령 실행에 반영

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 매크로 turn_off_mmu의 본문입니다. \이름은 호출 위치에서 실제 레지스터나 인수로 바뀝니다.
.endm

공통 작업의 정의를 여기서 마칩니다. RET가 아니라 빌드 도구가 읽는 경계입니다.

25행 그림 · 어셈블·링크 단계

매크로 본문: turn_off_mmu

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과.endm
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 끝 표기 자체가 RET나 분기를 실행하지는 않습니다.
.section    ".kexec_relocate.text", "ax"

kexec가 별도 안전한 페이지로 복사해 실행할 코드를 이 섹션에 모읍니다. 새 이미지로 기존 커널 메모리를 덮어쓰는 중에도 복사 루틴 자신은 살아 있어야 합니다. 이는 빌드 중 출력할 바이트의 섹션을 선택하는 지시문이며, 실행 중 PC나 SP를 옮기지 않습니다.

27행 그림 · 어셈블·링크 단계
빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과.section ".kexec_relocate.text", "ax"
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

kimage에서 필요한 값 읽기

원본 39–55행

페이지 목록을 읽고 새 커널 내용을 옮깁니다

페이지 목록을 읽고 새 커널 내용을 옮깁니다의 세 단계 비교
화살표는 항목 해석, 메모리 복사, 실행 이동을 구분합니다. 세부 TTBR 교체와 예외 경로는 원문에서 이어 읽습니다.

움직임으로 따라가기 · 재생하거나 한 단계씩 고정해 보실 수 있습니다.

페이지 목록을 읽고 새 커널 내용을 옮깁니다 · 1단계

1단계 고정

GIF 원본 열기

그림의 각 단계를 글로 읽기

1. 항목 해석

모든 목록 항목을 데이터 페이지로 읽으면 안 됩니다.

2. 내용 복사

복사 루프의 포인터 이동과 다음 목록 항목 이동을 구분합니다.

3. 새 커널로 연결

메모리에 쓴 코드와 CPU가 읽는 명령이 일치하도록 동기화가 필요합니다.

SYM_CODE_START(arm64_relocate_new_kernel)

x0의 kimage 구조체를 받아 새 커널의 페이지를 최종 위치로 옮기는 진입점입니다. C 코드가 미리 마련한 전환용 페이지 테이블과 복사 목록을 사용하며, 완료하면 새 이미지로 넘어갑니다. 이 매크로는 정렬된 코드 시작 심벌을 정의하고 다른 오브젝트에서도 참조할 수 있게 합니다.

39행 그림 · 어셈블·링크 단계

함수·코드 범위: arm64_relocate_new_kernel

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과SYM_CODE_START(arm64_relocate_new_kernel)
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	ldr	x28, [x0, #KIMAGE_START]

kimage.start를 x28에 보관합니다. 복사가 끝난 뒤 분기할 주소이며, 적재 방식에 따라 커널 진입점 또는 진입 전 검증을 수행하는 purgatory일 수 있습니다. 실제로 x0 + (KIMAGE_START) 주소의 메모리 8바이트를 x28에 읽습니다. 이 주소 형식은 기준 레지스터 x0 자체를 갱신하지 않습니다.

kimage가 복사 중 덮어써질 수 있어 진입점부터 레지스터에 보존합니다.

45행 그림 · 메모리 읽기

함수·코드 범위: arm64_relocate_new_kernel

읽는 값x0
메모리[이전 x0 + (KIMAGE_START)] · 8바이트
이 명령을 실행하기 직전M0 (이 주소에 저장된 값)
이 명령을 실행한 직후M0 (이 주소에 저장된 값)
x28
이 명령을 실행하기 직전명령 직전 x28의 값
이 명령을 실행한 직후M0

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 주소 계산에 의한 기준 레지스터 갱신은 없습니다. 다만 LDR/LDP의 목적 레지스터가 기준 레지스터와 같으면 읽은 값이 그 레지스터를 덮어씁니다.
	ldr	x27, [x0, #KIMAGE_ARCH_EL2_VECTORS]

전환 중 사용할 EL2 벡터 주소를 x27에 읽습니다. 0이 아니면 마지막에 EL2의 HVC_SOFT_RESTART 경로를 이용해야 함을 뜻합니다. 실제로 x0 + (KIMAGE_ARCH_EL2_VECTORS) 주소의 메모리 8바이트를 x27에 읽습니다. 이 주소 형식은 기준 레지스터 x0 자체를 갱신하지 않습니다.

46행 그림 · 메모리 읽기

함수·코드 범위: arm64_relocate_new_kernel

읽는 값x0
메모리[이전 x0 + (KIMAGE_ARCH_EL2_VECTORS)] · 8바이트
이 명령을 실행하기 직전M0 (이 주소에 저장된 값)
이 명령을 실행한 직후M0 (이 주소에 저장된 값)
x27
이 명령을 실행하기 직전명령 직전 x27의 값
이 명령을 실행한 직후M0

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 주소 계산에 의한 기준 레지스터 갱신은 없습니다. 다만 LDR/LDP의 목적 레지스터가 기준 레지스터와 같으면 읽은 값이 그 레지스터를 덮어씁니다.
	ldr	x26, [x0, #KIMAGE_ARCH_DTB_MEM]

새 커널에 넘길 DTB의 물리 주소를 x26에 보관합니다. 복사 루프가 끝나면 부팅 규약에 맞춰 x0에 전달합니다. 실제로 x0 + (KIMAGE_ARCH_DTB_MEM) 주소의 메모리 8바이트를 x26에 읽습니다. 이 주소 형식은 기준 레지스터 x0 자체를 갱신하지 않습니다.

47행 그림 · 메모리 읽기

함수·코드 범위: arm64_relocate_new_kernel

읽는 값x0
메모리[이전 x0 + (KIMAGE_ARCH_DTB_MEM)] · 8바이트
이 명령을 실행하기 직전M0 (이 주소에 저장된 값)
이 명령을 실행한 직후M0 (이 주소에 저장된 값)
x26
이 명령을 실행하기 직전명령 직전 x26의 값
이 명령을 실행한 직후M0

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 주소 계산에 의한 기준 레지스터 갱신은 없습니다. 다만 LDR/LDP의 목적 레지스터가 기준 레지스터와 같으면 읽은 값이 그 레지스터를 덮어씁니다.
	ldr	x18, [x0, #KIMAGE_ARCH_ZERO_PAGE] /* x18 = zero page for BBM */

TTBR1 교체 때 임시로 사용할 빈 페이지의 물리 주소를 x18에 읽습니다. 기존·새 페이지 테이블을 섞어 참조하지 않도록 중간에 빈 테이블을 거칩니다. 실제로 x0 + (KIMAGE_ARCH_ZERO_PAGE) 주소의 메모리 8바이트를 x18에 읽습니다. 이 주소 형식은 기준 레지스터 x0 자체를 갱신하지 않습니다.

50행 그림 · 메모리 읽기

함수·코드 범위: arm64_relocate_new_kernel

읽는 값x0
메모리[이전 x0 + (KIMAGE_ARCH_ZERO_PAGE)] · 8바이트
이 명령을 실행하기 직전M0 (이 주소에 저장된 값)
이 명령을 실행한 직후M0 (이 주소에 저장된 값)
x18
이 명령을 실행하기 직전명령 직전 x18의 값
이 명령을 실행한 직후M0

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 주소 계산에 의한 기준 레지스터 갱신은 없습니다. 다만 LDR/LDP의 목적 레지스터가 기준 레지스터와 같으면 읽은 값이 그 레지스터를 덮어씁니다.
	ldr	x17, [x0, #KIMAGE_ARCH_TTBR1]	/* x17 = linear map copy */

복사 작업 동안 사용할 별도 linear map 테이블의 물리 주소를 x17에 읽습니다. 기존 커널의 원래 페이지 테이블은 곧 덮어써질 수 있습니다. 실제로 x0 + (KIMAGE_ARCH_TTBR1) 주소의 메모리 8바이트를 x17에 읽습니다. 이 주소 형식은 기준 레지스터 x0 자체를 갱신하지 않습니다.

51행 그림 · 메모리 읽기

함수·코드 범위: arm64_relocate_new_kernel

읽는 값x0
메모리[이전 x0 + (KIMAGE_ARCH_TTBR1)] · 8바이트
이 명령을 실행하기 직전M0 (이 주소에 저장된 값)
이 명령을 실행한 직후M0 (이 주소에 저장된 값)
x17
이 명령을 실행하기 직전명령 직전 x17의 값
이 명령을 실행한 직후M0

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 주소 계산에 의한 기준 레지스터 갱신은 없습니다. 다만 LDR/LDP의 목적 레지스터가 기준 레지스터와 같으면 읽은 값이 그 레지스터를 덮어씁니다.
	ldr	x16, [x0, #KIMAGE_HEAD]		/* x16 = kimage_head */

물리 주소와 항목 종류 비트가 결합된 복사 목록의 첫 엔트리를 x16에 읽습니다. 이후 SOURCE, INDIRECTION, DESTINATION, DONE 비트를 구분하여 해석합니다. 실제로 x0 + (KIMAGE_HEAD) 주소의 메모리 8바이트를 x16에 읽습니다. 이 주소 형식은 기준 레지스터 x0 자체를 갱신하지 않습니다.

52행 그림 · 메모리 읽기

함수·코드 범위: arm64_relocate_new_kernel

읽는 값x0
메모리[이전 x0 + (KIMAGE_HEAD)] · 8바이트
이 명령을 실행하기 직전M0 (이 주소에 저장된 값)
이 명령을 실행한 직후M0 (이 주소에 저장된 값)
x16
이 명령을 실행하기 직전명령 직전 x16의 값
이 명령을 실행한 직후M0

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 주소 계산에 의한 기준 레지스터 갱신은 없습니다. 다만 LDR/LDP의 목적 레지스터가 기준 레지스터와 같으면 읽은 값이 그 레지스터를 덮어씁니다.
	ldr	x22, [x0, #KIMAGE_ARCH_PHYS_OFFSET]	/* x22 phys_offset */

물리 주소와 현재 linear map 가상 주소 사이의 차이를 x22에 읽습니다. C 코드가 virt_to_phys(kimage) - (long)kimage로 준비한 값이며 페이지 주소에서 이를 빼면 복사에 사용할 가상 주소가 됩니다. 실제로 x0 + (KIMAGE_ARCH_PHYS_OFFSET) 주소의 메모리 8바이트를 x22에 읽습니다. 이 주소 형식은 기준 레지스터 x0 자체를 갱신하지 않습니다.

53행 그림 · 메모리 읽기

함수·코드 범위: arm64_relocate_new_kernel

읽는 값x0
메모리[이전 x0 + (KIMAGE_ARCH_PHYS_OFFSET)] · 8바이트
이 명령을 실행하기 직전M0 (이 주소에 저장된 값)
이 명령을 실행한 직후M0 (이 주소에 저장된 값)
x22
이 명령을 실행하기 직전명령 직전 x22의 값
이 명령을 실행한 직후M0

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 주소 계산에 의한 기준 레지스터 갱신은 없습니다. 다만 LDR/LDP의 목적 레지스터가 기준 레지스터와 같으면 읽은 값이 그 레지스터를 덮어씁니다.
	raw_dcache_line_size x15, x1		/* x15 = dcache line size */

현재 CPU의 CTR_EL0에서 데이터 캐시 라인 크기를 x15에 구합니다. 나중에 복사한 페이지를 이 간격으로 clean·invalidate하며 x1은 이 계산에만 쓰는 임시 값입니다. CTR_EL0.DminLine은 바이트 수 자체가 아닙니다. 4를 이 필드 값만큼 왼쪽으로 이동하여 바이트 수를 구합니다.

54행 그림 · 매크로 전체의 상태 변화

함수·코드 범위: arm64_relocate_new_kernel

읽는 값CTR_EL0
x1
이 명령을 실행하기 직전명령 직전 x1의 값
이 명령을 실행한 직후CTR_EL0.DminLine
x15
이 명령을 실행하기 직전명령 직전 x15의 값
이 명령을 실행한 직후4 × 2^(CTR_EL0.DminLine) 바이트

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

raw_dcache_line_size 매크로 본문 · 286행

arch/arm64/include/asm/assembler.h · Linux v6.18.37

정의 선택 조건: #ifndef __ASM_ASSEMBLER_H

.macro raw_dcache_line_size, reg, tmp mrs \tmp, ctr_el0 // read CTR ubfm \tmp, \tmp, #16, #19 // cache line size encoding mov \reg, #4 // bytes per word lsl \reg, \reg, \tmp // actual cache line size .endm
	break_before_make_ttbr_switch	x18, x17, x1, x2 /* set linear map */

빈 페이지 x18을 거쳐 전환용 테이블 x17을 TTBR1에 설치합니다. 기존 TLB 항목을 무효화하여 원래 커널의 페이지 테이블이 덮어써져도 복사에 필요한 주소 변환을 유지합니다.

55행 그림 · 매크로 안의 상태 변화

함수·코드 범위: arm64_relocate_new_kernel

입력
이 명령을 실행하기 직전호출 직전 값
이 명령을 실행한 직후x18, x17, x1, x2
주요 결과
이 명령을 실행하기 직전본문 실행 전
이 명령을 실행한 직후제로 페이지의 TTBR1을 먼저 설치하고 ISB→TLBI→DSB를 수행한 뒤 새 TTBR1을 설치합니다.

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 여러 명령과 내부 분기를 묶은 그림입니다. 작업 레지스터를 모두 보존한다는 뜻은 아니며, 정확한 변경 순서는 아래 매크로 본문과 줄별 그림에서 확인합니다.
break_before_make_ttbr_switch 매크로 본문 · 468행

arch/arm64/include/asm/assembler.h · Linux v6.18.37

정의 선택 조건: #ifndef __ASM_ASSEMBLER_H

.macro break_before_make_ttbr_switch zero_page, page_table, tmp, tmp2 phys_to_ttbr \tmp, \zero_page msr ttbr1_el1, \tmp isb tlbi vmalle1 dsb nsh load_ttbr1 \page_table, \tmp, \tmp2 .endm

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

원본 56–78행

.Lloop:

x16의 복사 목록 엔트리에서 페이지 주소를 꺼내 해석을 시작하는 반복 지점입니다. 레이블은 이 위치에 붙인 이름이며, 이름을 적는 것만으로 CPU 명령이 추가되지는 않습니다.

56행 그림 · 위치에 이름 붙이기

함수·코드 범위: arm64_relocate_new_kernel

레이블
처리 전현재 배치 위치
이 줄의 효과.Lloop
CPU 레지스터
처리 전그대로
이 줄의 효과레이블 자체가 값을 바꾸지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	and	x12, x16, PAGE_MASK		/* x12 = addr */

목록 엔트리의 하위 종류 비트를 PAGE_MASK로 제거하여 페이지 물리 주소만 x12에 남깁니다. AND는 두 피연산자의 같은 위치 비트가 모두 1인 곳만 결과에 남깁니다.

57행 그림 · 레지스터 상태

함수·코드 범위: arm64_relocate_new_kernel

읽는 값x16
x12
이 명령을 실행하기 직전명령 직전 x12의 값
이 명령을 실행한 직후명령 직전 x16의 값 AND PAGE_MASK

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 결과는 목적 레지스터 폭에 맞습니다. 이 형식은 NZCV를 바꾸지 않습니다.
	sub	x12, x12, x22			/* Convert x12 to virt */

페이지 물리 주소에서 미리 계산한 차이 x22를 빼 linear map의 가상 주소로 바꿉니다. 아직 MMU가 켜져 있으므로 페이지 복사와 목록 읽기는 이 가상 주소를 사용합니다. 이 SUB 형식은 NZCV 조건 플래그를 바꾸지 않습니다.

x22는 이 작업용 선형 매핑의 물리/가상 차이입니다. 모든 VA→PA 변환에 쓰는 보편적인 상수는 아닙니다.

58행 그림 · 레지스터 상태

함수·코드 범위: arm64_relocate_new_kernel

읽는 값x12x22
x12
이 명령을 실행하기 직전명령 직전 x12의 값
이 명령을 실행한 직후명령 직전 x12의 값 − 명령 직전 x22의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 결과는 목적 레지스터 폭에 맞습니다. 이 형식은 NZCV를 바꾸지 않습니다.
.Ltest_source:

현재 엔트리가 복사 원본 페이지인지 검사할 위치입니다. 원본이 아니면 다른 항목 종류를 순서대로 검사합니다. 레이블은 이 위치에 붙인 이름이며, 이름을 적는 것만으로 CPU 명령이 추가되지는 않습니다.

60행 그림 · 위치에 이름 붙이기

함수·코드 범위: arm64_relocate_new_kernel

레이블
처리 전현재 배치 위치
이 줄의 효과.Ltest_source
CPU 레지스터
처리 전그대로
이 줄의 효과레이블 자체가 값을 바꾸지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	tbz	x16, IND_SOURCE_BIT, .Ltest_indirection

SOURCE 비트가 없으면 이 항목을 원본 데이터 페이지로 해석하지 않고 다음 종류 검사로 넘어갑니다. 비트가 있으면 x12의 내용을 현재 목적지 x13으로 복사합니다. 이 분기는 지정 레지스터의 값이나 비트를 직접 검사하며 CMP가 남긴 NZCV를 읽거나 바꾸지 않습니다. 조건이 맞지 않으면 바로 다음 명령으로 진행합니다.

61행 그림 · 조건별 실행 위치

함수·코드 범위: arm64_relocate_new_kernel

읽는 값x16
PC
이 명령을 실행하기 직전명령 직전 PC의 값
이 명령을 실행한 직후명령 직전 x16의 값의 bit IND_SOURCE_BIT = 0이면 .Ltest_indirection, 아니면 다음 명령

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 조건이 맞는 한 경로만 실행합니다. 이 분기 명령은 NZCV를 새로 계산하지 않습니다.
	mov	x19, x13

목적지의 시작 주소를 x19에 남깁니다. copy_page가 x13을 페이지 끝까지 증가시키므로 복사 후 캐시를 정리할 시작 주소가 따로 필요합니다. MOV는 x19의 기존 값을 대체합니다. 원본이 주소값이어도 그 주소의 메모리를 읽거나 복사하지 않습니다.

64행 그림 · 값 복사

함수·코드 범위: arm64_relocate_new_kernel

읽는 값x13
x19
이 명령을 실행하기 직전명령 직전 x19의 값
이 명령을 실행한 직후명령 직전 x13의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 화살표는 값의 변화를 뜻합니다. 주소값을 옮겨도 그 주소의 메모리 내용은 복사하지 않습니다.
	copy_page x13, x12, x1, x2, x3, x4, x5, x6, x7, x8

x12의 원본 페이지를 현재 목적지 x13으로 복사합니다. x1~x8은 64바이트씩 운반하는 작업 레지스터이며 x12·x13은 복사 후 다음 페이지 위치까지 증가합니다.

x13과 x12가 복사 뒤 증가합니다. x19에는 캐시 처리용 원래 목적지가 남아 있습니다.

65행 그림 · 매크로 안의 상태 변화

함수·코드 범위: arm64_relocate_new_kernel

입력
이 명령을 실행하기 직전호출 직전 값
이 명령을 실행한 직후x13, x12, x1, x2, x3, x4, x5, x6, x7, x8
주요 결과
이 명령을 실행하기 직전본문 실행 전
이 명령을 실행한 직후한 번에 64바이트씩 읽고 저장하여 페이지 하나를 복사합니다. src와 dest 포인터 및 임시 레지스터를 갱신합니다.

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 여러 명령과 내부 분기를 묶은 그림입니다. 작업 레지스터를 모두 보존한다는 뜻은 아니며, 정확한 변경 순서는 아래 매크로 본문과 줄별 그림에서 확인합니다.
copy_page 매크로 본문 · 503행

arch/arm64/include/asm/assembler.h · Linux v6.18.37

정의 선택 조건: #ifndef __ASM_ASSEMBLER_H

.macro copy_page dest:req src:req t1:req t2:req t3:req t4:req t5:req t6:req t7:req t8:req 9998: ldp \t1, \t2, [\src] ldp \t3, \t4, [\src, #16] ldp \t5, \t6, [\src, #32] ldp \t7, \t8, [\src, #48] add \src, \src, #64 stnp \t1, \t2, [\dest] stnp \t3, \t4, [\dest, #16] stnp \t5, \t6, [\dest, #32] stnp \t7, \t8, [\dest, #48] add \dest, \dest, #64 tst \src, #(PAGE_SIZE - 1) b.ne 9998b .endm
	add	x1, x19, #PAGE_SIZE

보관한 목적지 시작 x19에 PAGE_SIZE를 더해 캐시 처리 범위의 끝 주소를 만듭니다. 이 ADD 형식은 NZCV 조건 플래그를 바꾸지 않습니다.

66행 그림 · 레지스터 상태

함수·코드 범위: arm64_relocate_new_kernel

읽는 값x19
x1
이 명령을 실행하기 직전명령 직전 x1의 값
이 명령을 실행한 직후명령 직전 x19의 값 + PAGE_SIZE

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 결과는 목적 레지스터 폭에 맞습니다. 이 형식은 NZCV를 바꾸지 않습니다.
	dcache_by_myline_op civac, sy, x19, x1, x15, x20

복사한 [x19, x1) 범위를 x15의 캐시 라인 크기씩 돌며 DC CIVAC를 수행합니다. 새 내용이 PoC까지 반영되도록 clean하고 캐시 라인을 invalidate한 뒤 DSB SY로 완료를 기다립니다. 시작 주소를 먼저 캐시 라인 경계로 내림 정렬하므로 범위의 첫 바이트가 속한 라인부터 처리합니다.

67행 그림 · 매크로 안의 상태 변화

함수·코드 범위: arm64_relocate_new_kernel

입력
이 명령을 실행하기 직전호출 직전 값
이 명령을 실행한 직후civac, sy, x19, x1, x15, x20
주요 결과
이 명령을 실행하기 직전본문 실행 전
이 명령을 실행한 직후전달받은 캐시 라인 크기로 시작 주소를 정렬하고 끝 주소 미만까지 지정 DC 명령을 반복한 뒤 DSB를 실행합니다.

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 여러 명령과 내부 분기를 묶은 그림입니다. 작업 레지스터를 모두 보존한다는 뜻은 아니며, 정확한 변경 순서는 아래 매크로 본문과 줄별 그림에서 확인합니다.
dcache_by_myline_op 매크로 본문 · 381행

arch/arm64/include/asm/assembler.h · Linux v6.18.37

정의 선택 조건: #ifndef __ASM_ASSEMBLER_H

.macro dcache_by_myline_op op, domain, start, end, linesz, tmp, fixup sub \tmp, \linesz, #1 bic \start, \start, \tmp .Ldcache_op\@: .ifc \op, cvau __dcache_op_workaround_clean_cache \op, \start .else .ifc \op, cvac __dcache_op_workaround_clean_cache \op, \start .else .ifc \op, cvap sys 3, c7, c12, 1, \start // dc cvap .else .ifc \op, cvadp sys 3, c7, c13, 1, \start // dc cvadp .else dc \op, \start .endif .endif .endif .endif add \start, \start, \linesz cmp \start, \end b.lo .Ldcache_op\@ dsb \domain _cond_uaccess_extable .Ldcache_op\@, \fixup .endm
	b	.Lnext

한 원본 페이지 처리를 마쳤으므로 다음 목록 엔트리를 읽는 공통 지점으로 넘어갑니다. 이 분기는 LR에 새 복귀 주소를 기록하지 않습니다.

68행 그림 · 호출과 실행 위치

함수·코드 범위: arm64_relocate_new_kernel

PC
이 명령을 실행하기 직전명령 직전 PC의 값
이 명령을 실행한 직후.Lnext

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 새 복귀 주소를 LR에 기록하지 않습니다. RET 역시 메모리의 스택 값을 자동으로 꺼내는 명령은 아닙니다.
.Ltest_indirection:

현재 엔트리가 다른 목록 페이지를 가리키는지 검사할 위치입니다. 레이블은 이 위치에 붙인 이름이며, 이름을 적는 것만으로 CPU 명령이 추가되지는 않습니다.

69행 그림 · 위치에 이름 붙이기

함수·코드 범위: arm64_relocate_new_kernel

레이블
처리 전현재 배치 위치
이 줄의 효과.Ltest_indirection
CPU 레지스터
처리 전그대로
이 줄의 효과레이블 자체가 값을 바꾸지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	tbz	x16, IND_INDIRECTION_BIT, .Ltest_destination

INDIRECTION 비트가 없으면 목적지 지정 항목인지 검사합니다. 이 비트가 있으면 x12는 이미지 데이터가 아니라 다음 엔트리들이 담긴 목록 페이지 주소입니다. 이 분기는 지정 레지스터의 값이나 비트를 직접 검사하며 CMP가 남긴 NZCV를 읽거나 바꾸지 않습니다. 조건이 맞지 않으면 바로 다음 명령으로 진행합니다.

70행 그림 · 조건별 실행 위치

함수·코드 범위: arm64_relocate_new_kernel

읽는 값x16
PC
이 명령을 실행하기 직전명령 직전 PC의 값
이 명령을 실행한 직후명령 직전 x16의 값의 bit IND_INDIRECTION_BIT = 0이면 .Ltest_destination, 아니면 다음 명령

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 조건이 맞는 한 경로만 실행합니다. 이 분기 명령은 NZCV를 새로 계산하지 않습니다.
	mov	x14, x12			/* ptr = addr */

새 목록 페이지 주소를 목록 커서 x14에 넣습니다. 이후 x14에서 8바이트 엔트리를 차례로 읽어 갑니다. MOV는 x14의 기존 값을 대체합니다. 원본이 주소값이어도 그 주소의 메모리를 읽거나 복사하지 않습니다.

71행 그림 · 값 복사

함수·코드 범위: arm64_relocate_new_kernel

읽는 값x12
x14
이 명령을 실행하기 직전명령 직전 x14의 값
이 명령을 실행한 직후명령 직전 x12의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 화살표는 값의 변화를 뜻합니다. 주소값을 옮겨도 그 주소의 메모리 내용은 복사하지 않습니다.
	b	.Lnext

목록 페이지를 바꿨으므로 공통 읽기 지점에서 그 페이지의 첫 엔트리를 가져옵니다. 이 분기는 LR에 새 복귀 주소를 기록하지 않습니다.

72행 그림 · 호출과 실행 위치

함수·코드 범위: arm64_relocate_new_kernel

PC
이 명령을 실행하기 직전명령 직전 PC의 값
이 명령을 실행한 직후.Lnext

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 새 복귀 주소를 LR에 기록하지 않습니다. RET 역시 메모리의 스택 값을 자동으로 꺼내는 명령은 아닙니다.
.Ltest_destination:

현재 엔트리가 이후 복사의 목적지 주소를 지정하는지 검사할 위치입니다. 레이블은 이 위치에 붙인 이름이며, 이름을 적는 것만으로 CPU 명령이 추가되지는 않습니다.

73행 그림 · 위치에 이름 붙이기

함수·코드 범위: arm64_relocate_new_kernel

레이블
처리 전현재 배치 위치
이 줄의 효과.Ltest_destination
CPU 레지스터
처리 전그대로
이 줄의 효과레이블 자체가 값을 바꾸지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	tbz	x16, IND_DESTINATION_BIT, .Lnext

DESTINATION 비트도 없으면 목적지를 바꾸지 않고 다음 엔트리로 넘어갑니다. 있으면 이어서 나올 SOURCE 페이지들의 복사 목적지를 지정합니다. 이 분기는 지정 레지스터의 값이나 비트를 직접 검사하며 CMP가 남긴 NZCV를 읽거나 바꾸지 않습니다. 조건이 맞지 않으면 바로 다음 명령으로 진행합니다.

74행 그림 · 조건별 실행 위치

함수·코드 범위: arm64_relocate_new_kernel

읽는 값x16
PC
이 명령을 실행하기 직전명령 직전 PC의 값
이 명령을 실행한 직후명령 직전 x16의 값의 bit IND_DESTINATION_BIT = 0이면 .Lnext, 아니면 다음 명령

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 조건이 맞는 한 경로만 실행합니다. 이 분기 명령은 NZCV를 새로 계산하지 않습니다.
	mov	x13, x12			/* dest = addr */

엔트리가 지정한 목적지 가상 주소를 x13에 저장합니다. 뒤따르는 원본 페이지들을 복사할 때 이 주소가 한 페이지씩 전진합니다. MOV는 x13의 기존 값을 대체합니다. 원본이 주소값이어도 그 주소의 메모리를 읽거나 복사하지 않습니다.

75행 그림 · 값 복사

함수·코드 범위: arm64_relocate_new_kernel

읽는 값x12
x13
이 명령을 실행하기 직전명령 직전 x13의 값
이 명령을 실행한 직후명령 직전 x12의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 화살표는 값의 변화를 뜻합니다. 주소값을 옮겨도 그 주소의 메모리 내용은 복사하지 않습니다.
.Lnext:

각 항목 처리가 끝난 뒤 다음 8바이트 엔트리를 읽는 공통 지점입니다. 레이블은 이 위치에 붙인 이름이며, 이름을 적는 것만으로 CPU 명령이 추가되지는 않습니다.

76행 그림 · 위치에 이름 붙이기

함수·코드 범위: arm64_relocate_new_kernel

레이블
처리 전현재 배치 위치
이 줄의 효과.Lnext
CPU 레지스터
처리 전그대로
이 줄의 효과레이블 자체가 값을 바꾸지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	ldr	x16, [x14], #8			/* entry = *ptr++ */

목록 커서 x14에서 64비트 엔트리 하나를 x16에 읽고 커서를 8바이트 전진시킵니다. 아직 데이터 페이지를 복사하는 명령은 아닙니다. 대괄호 밖의 오프셋 8은 후위 갱신입니다. 기존 x14 주소로 접근한 뒤 x14에 이 값을 더합니다.

다음 목록 항목을 읽고 목록 포인터 x14를 8 증가시킵니다.

77행 그림 · 메모리 읽기

함수·코드 범위: arm64_relocate_new_kernel

읽는 값x14
메모리[이전 x14] · 8바이트
이 명령을 실행하기 직전M0 (이 주소에 저장된 값)
이 명령을 실행한 직후M0 (이 주소에 저장된 값)
x16
이 명령을 실행하기 직전명령 직전 x16의 값
이 명령을 실행한 직후M0
x14
이 명령을 실행하기 직전명령 직전 x14의 값
이 명령을 실행한 직후명령 직전 x14의 값 + (8)

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 대괄호 밖의 오프셋은 후위 갱신입니다. 이전 주소로 접근한 뒤 기준 레지스터를 갱신합니다.
	tbz	x16, IND_DONE_BIT, .Lloop	/* while (!(entry & DONE)) */

DONE 비트가 없으면 새로 읽은 엔트리를 처리합니다. DONE이 있으면 복사 목록의 끝이므로 캐시·MMU 전환 마무리로 진행합니다. 이 분기는 지정 레지스터의 값이나 비트를 직접 검사하며 CMP가 남긴 NZCV를 읽거나 바꾸지 않습니다. 조건이 맞지 않으면 바로 다음 명령으로 진행합니다.

78행 그림 · 조건별 실행 위치

함수·코드 범위: arm64_relocate_new_kernel

읽는 값x16
PC
이 명령을 실행하기 직전명령 직전 PC의 값
이 명령을 실행한 직후명령 직전 x16의 값의 bit IND_DONE_BIT = 0이면 .Lloop, 아니면 다음 명령

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 조건이 맞는 한 경로만 실행합니다. 이 분기 명령은 NZCV를 새로 계산하지 않습니다.

복사 완료와 새 진입점

원본 79–100행

	dsb	nsh

앞선 메모리·캐시 작업의 로컬 완료를 기다립니다. 새 코드를 실행하기 전에 복사 결과가 준비되어 있어야 합니다.

80행 그림 · 동기화·캐시·대기 상태

함수·코드 범위: arm64_relocate_new_kernel

작업 완료
이 명령을 실행하기 직전실행 전 상태
이 명령을 실행한 직후지정 범위의 앞선 작업 완료까지 기다림

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	ic	iallu

현재 CPU의 명령 캐시를 무효화하여 새로 복사한 명령 대신 낡은 명령을 가져오는 일을 막습니다. 무효화 요청과 이후 명령 실행에 대한 동기화는 구분되며, 뒤의 DSB·ISB가 그 순서를 마무리합니다.

81행 그림 · 동기화·캐시·대기 상태

함수·코드 범위: arm64_relocate_new_kernel

명령 캐시
이 명령을 실행하기 직전실행 전 상태
이 명령을 실행한 직후지정 범위의 명령 캐시 무효화 요청

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 관리 요청과 완료 보장은 구분됩니다. 주변 DSB·ISB와 함께 해석합니다.
	dsb	nsh

명령 캐시 무효화가 완료될 때까지 기다립니다.

82행 그림 · 동기화·캐시·대기 상태

함수·코드 범위: arm64_relocate_new_kernel

작업 완료
이 명령을 실행하기 직전실행 전 상태
이 명령을 실행한 직후지정 범위의 앞선 작업 완료까지 기다림

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	isb

파이프라인을 동기화하여 이후 명령 가져오기가 갱신된 캐시 상태를 따르게 합니다.

83행 그림 · 동기화·캐시·대기 상태

함수·코드 범위: arm64_relocate_new_kernel

명령 실행 문맥
이 명령을 실행하기 직전실행 전 상태
이 명령을 실행한 직후앞서 바꾼 상태를 이후 명령 실행에 반영

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	turn_off_mmu x12, x13

복사와 캐시 정리가 끝났으므로 MMU와 캐시를 끕니다. x12는 SCTLR 값을 만드는 데 사용되며 x13 인자는 이 매크로 본문에서는 사용하지 않습니다. 매크로는 SCTLR_EL1에 MMU-off 제어값을 쓰고 ISB로 이후 명령을 동기화합니다.

이후에도 같은 명령을 읽을 수 있도록 idmap에서 실행하고 있습니다.

84행 그림 · 매크로 전체의 상태 변화

함수·코드 범위: arm64_relocate_new_kernel

x12
이 명령을 실행하기 직전명령 직전 x12의 값
이 명령을 실행한 직후INIT_SCTLR_EL1_MMU_OFF
SCTLR_EL1
이 명령을 실행하기 직전명령 직전 SCTLR_EL1의 값
이 명령을 실행한 직후INIT_SCTLR_EL1_MMU_OFF

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 두 번째 인수는 이 정의에서 사용하지 않습니다. 제어값을 쓴 뒤 ISB로 실행 문맥을 동기화합니다.
turn_off_mmu 매크로 본문 · 20행

arch/arm64/kernel/relocate_kernel.S · Linux v6.18.37

.macro turn_off_mmu tmp1, tmp2 mov_q \tmp1, INIT_SCTLR_EL1_MMU_OFF pre_disable_mmu_workaround msr sctlr_el1, \tmp1 isb .endm
	cbz	x27, .Lel1

EL2 벡터 주소가 0이면 현재 EL에서 새 이미지로 직접 분기합니다. 주소가 있으면 전환용 EL2 벡터에 HVC를 보내 EL2에서 시작하게 합니다. 이 분기는 지정 레지스터의 값이나 비트를 직접 검사하며 CMP가 남긴 NZCV를 읽거나 바꾸지 않습니다. 조건이 맞지 않으면 바로 다음 명령으로 진행합니다.

87행 그림 · 조건별 실행 위치

함수·코드 범위: arm64_relocate_new_kernel

읽는 값x27
PC
이 명령을 실행하기 직전명령 직전 PC의 값
이 명령을 실행한 직후명령 직전 x27의 값 = 0이면 .Lel1, 아니면 다음 명령

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 조건이 맞는 한 경로만 실행합니다. 이 분기 명령은 NZCV를 새로 계산하지 않습니다.
	mov	x1, x28				/* kernel entry point */

HVC_SOFT_RESTART의 진입 주소 인자로 kimage.start를 x1에 넣습니다. MOV는 x1의 기존 값을 대체합니다. 원본이 주소값이어도 그 주소의 메모리를 읽거나 복사하지 않습니다.

88행 그림 · 값 복사

함수·코드 범위: arm64_relocate_new_kernel

읽는 값x28
x1
이 명령을 실행하기 직전명령 직전 x1의 값
이 명령을 실행한 직후명령 직전 x28의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 화살표는 값의 변화를 뜻합니다. 주소값을 옮겨도 그 주소의 메모리 내용은 복사하지 않습니다.
	mov	x2, x26				/* dtb address */

새 이미지의 첫 인자인 DTB 물리 주소를 x2에 넣습니다. EL2 처리기가 이를 x0로 옮겨 새 이미지에 전달합니다. MOV는 x2의 기존 값을 대체합니다. 원본이 주소값이어도 그 주소의 메모리를 읽거나 복사하지 않습니다.

89행 그림 · 값 복사

함수·코드 범위: arm64_relocate_new_kernel

읽는 값x26
x2
이 명령을 실행하기 직전명령 직전 x2의 값
이 명령을 실행한 직후명령 직전 x26의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 화살표는 값의 변화를 뜻합니다. 주소값을 옮겨도 그 주소의 메모리 내용은 복사하지 않습니다.
	mov	x3, xzr

새 이미지의 두 번째 인자를 0으로 준비합니다. EL2 처리기에서 x1로 옮겨집니다. MOV는 x3의 기존 값을 대체합니다. 원본이 주소값이어도 그 주소의 메모리를 읽거나 복사하지 않습니다.

90행 그림 · 값 복사

함수·코드 범위: arm64_relocate_new_kernel

읽는 값xzr
x3
이 명령을 실행하기 직전명령 직전 x3의 값
이 명령을 실행한 직후0

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 화살표는 값의 변화를 뜻합니다. 주소값을 옮겨도 그 주소의 메모리 내용은 복사하지 않습니다.
	mov	x4, xzr

새 이미지의 세 번째 인자를 0으로 준비합니다. EL2 처리기에서 x2로 옮겨집니다. MOV는 x4의 기존 값을 대체합니다. 원본이 주소값이어도 그 주소의 메모리를 읽거나 복사하지 않습니다.

91행 그림 · 값 복사

함수·코드 범위: arm64_relocate_new_kernel

읽는 값xzr
x4
이 명령을 실행하기 직전명령 직전 x4의 값
이 명령을 실행한 직후0

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 화살표는 값의 변화를 뜻합니다. 주소값을 옮겨도 그 주소의 메모리 내용은 복사하지 않습니다.
	mov     x0, #HVC_SOFT_RESTART

x0에 HVC_SOFT_RESTART 서비스 번호를 넣어 기존 값을 덮어씁니다. 이 MOV는 x1~x4와 메모리를 바꾸지 않습니다. 다음 hvc #0이 실행되면 EL2 처리 코드가 앞서 준비한 진입 주소와 인자를 사용합니다. HVC 명령의 즉시값 #0과 달리 x0의 HVC_SOFT_RESTART 값이 요청할 서비스 종류를 정합니다.

92행 그림 · 값 복사

함수·코드 범위: arm64_relocate_new_kernel

x0
이 명령을 실행하기 직전명령 직전 x0의 값
이 명령을 실행한 직후HVC_SOFT_RESTART

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 화살표는 값의 변화를 뜻합니다. 주소값을 옮겨도 그 주소의 메모리 내용은 복사하지 않습니다.
	hvc	#0				/* Jumps from el2 */

복사 중에도 유지되는 EL2 벡터의 재시작 처리기로 넘어갑니다. 처리기는 준비한 인자를 재배치하고 새 이미지 주소로 직접 분기합니다. 일반 함수 호출처럼 BL로 이동하는 대신 동기 예외를 통해 요청을 전달합니다.

hyp-stub는 x2=DTB를 새 커널의 x0로 옮깁니다.

93행 그림 · 예외 처리기로 제어 전달

함수·코드 범위: arm64_relocate_new_kernel

PC
이 명령을 실행하기 직전명령 직전 PC의 값
이 명령을 실행한 직후EL2의 예외 벡터
ELR_ELx
이 명령을 실행하기 직전명령 직전 ELR_ELx의 값
이 명령을 실행한 직후복귀할 다음 명령 주소
SPSR_ELx
이 명령을 실행하기 직전명령 직전 SPSR_ELx의 값
이 명령을 실행한 직후예외 직전 PSTATE

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 허용된 정상 예외 진입을 나타냅니다. 사용이 금지되거나 해당 EL이 없으면 예외 결과가 달라집니다. x0 등의 요청 인자는 처리기가 규약에 따라 읽으며 HVC/SMC 자체가 메모리로 복사하지는 않습니다.
  • 서비스를 처리한 뒤의 반환 레지스터는 서비스 규약에 따릅니다. 여기서는 진입까지만 표시합니다.
.Lel1:

EL2 경유가 필요 없는 경우 부팅 인자를 직접 배치하고 새 이미지로 분기하는 지점입니다. 레이블은 이 위치에 붙인 이름이며, 이름을 적는 것만으로 CPU 명령이 추가되지는 않습니다.

94행 그림 · 위치에 이름 붙이기

함수·코드 범위: arm64_relocate_new_kernel

레이블
처리 전현재 배치 위치
이 줄의 효과.Lel1
CPU 레지스터
처리 전그대로
이 줄의 효과레이블 자체가 값을 바꾸지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

	mov	x0, x26				/* dtb address */

직접 분기 경로에서는 DTB의 물리 주소를 부팅 인자 x0에 넣습니다. MOV는 x0의 기존 값을 대체합니다. 원본이 주소값이어도 그 주소의 메모리를 읽거나 복사하지 않습니다.

95행 그림 · 값 복사

함수·코드 범위: arm64_relocate_new_kernel

읽는 값x26
x0
이 명령을 실행하기 직전명령 직전 x0의 값
이 명령을 실행한 직후명령 직전 x26의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 화살표는 값의 변화를 뜻합니다. 주소값을 옮겨도 그 주소의 메모리 내용은 복사하지 않습니다.
	mov	x1, xzr

AArch64 커널 진입 규약에 따라 예약된 부팅 인자 x1을 0으로 만듭니다. MOV는 x1의 기존 값을 대체합니다. 원본이 주소값이어도 그 주소의 메모리를 읽거나 복사하지 않습니다.

96행 그림 · 값 복사

함수·코드 범위: arm64_relocate_new_kernel

읽는 값xzr
x1
이 명령을 실행하기 직전명령 직전 x1의 값
이 명령을 실행한 직후0

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 화살표는 값의 변화를 뜻합니다. 주소값을 옮겨도 그 주소의 메모리 내용은 복사하지 않습니다.
	mov	x2, xzr

예약된 부팅 인자 x2를 0으로 만듭니다. MOV는 x2의 기존 값을 대체합니다. 원본이 주소값이어도 그 주소의 메모리를 읽거나 복사하지 않습니다.

97행 그림 · 값 복사

함수·코드 범위: arm64_relocate_new_kernel

읽는 값xzr
x2
이 명령을 실행하기 직전명령 직전 x2의 값
이 명령을 실행한 직후0

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 화살표는 값의 변화를 뜻합니다. 주소값을 옮겨도 그 주소의 메모리 내용은 복사하지 않습니다.
	mov	x3, xzr

예약된 부팅 인자 x3을 0으로 만듭니다. MOV는 x3의 기존 값을 대체합니다. 원본이 주소값이어도 그 주소의 메모리를 읽거나 복사하지 않습니다.

98행 그림 · 값 복사

함수·코드 범위: arm64_relocate_new_kernel

읽는 값xzr
x3
이 명령을 실행하기 직전명령 직전 x3의 값
이 명령을 실행한 직후0

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 화살표는 값의 변화를 뜻합니다. 주소값을 옮겨도 그 주소의 메모리 내용은 복사하지 않습니다.
	br	x28				/* Jumps from el1 */

복사를 마친 kimage.start 주소로 넘어갑니다. 적재 방식에 따라 커널 또는 purgatory가 실행되며, 이전 커널의 복사 루틴으로 돌아오지 않습니다. 이 분기는 LR에 새 복귀 주소를 기록하지 않습니다. 레지스터가 담은 주소로 실행 위치를 바꾸며, 그 주소에서 값을 읽어 다른 레지스터에 복사하는 load 명령은 아닙니다.

kimage.start가 직접 커널을 가리키면 x0의 DTB 주소를 커널 진입점에 전달합니다. 일반 kexec 경로에서는 purgatory가 먼저 실행될 수도 있으며, kexec_file 경로는 커널을 직접 시작합니다.

99행 그림 · 호출과 실행 위치

함수·코드 범위: arm64_relocate_new_kernel

읽는 값x28
PC
이 명령을 실행하기 직전명령 직전 PC의 값
이 명령을 실행한 직후명령 직전 x28의 값

→는 같은 항목의 실행 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 새 복귀 주소를 LR에 기록하지 않습니다. RET 역시 메모리의 스택 값을 자동으로 꺼내는 명령은 아닙니다.
SYM_CODE_END(arm64_relocate_new_kernel)

도구에 이 코드 범위의 끝을 알려 크기와 심볼 정보를 기록합니다. CPU가 이 줄 때문에 자동으로 반환하지는 않습니다.

100행 그림 · 어셈블·링크 단계

함수·코드 범위: arm64_relocate_new_kernel

빌드 도구
처리 전소스의 명령·배치 표기
이 줄의 효과SYM_CODE_END(arm64_relocate_new_kernel)
CPU 레지스터
처리 전해당 없음
이 줄의 효과지시문 자체는 레지스터를 쓰지 않습니다.

→는 같은 항목의 처리 전과 후를 비교하는 표시입니다. 메모리 주소의 증가 방향이나 데이터 전송 경로를 뜻하지 않습니다.

  • 끝 표기 자체가 RET나 분기를 실행하지는 않습니다.

설명한 뒤 함께 생각해 볼 질문

kimage에서 값을 필요할 때마다 읽으면 안 되나요?

복사 도중 kimage 자체가 덮어써질 수 있습니다. 이후에도 필요한 값은 복사 전에 확보합니다.

SOURCE와 DESTINATION 항목은 같은 뜻인가요?

SOURCE는 현재 목적지로 복사할 페이지이고, DESTINATION은 이후 복사가 쓸 목적지 포인터를 설정합니다.

새 커널에 DTB 주소는 어떻게 전달되나요?

직접 경로에서는 x0에 넣습니다. EL2 경유 경로는 hyp-stub가 전달 인자를 재배치해 새 진입점의 x0로 보냅니다.

주석을 포함한 전체 원문

Linux stable v6.18.37 · arch/arm64/kernel/relocate_kernel.S

1/* SPDX-License-Identifier: GPL-2.0-only */2/*3 * kexec for arm644 *5 * Copyright (C) Linaro.6 * Copyright (C) Huawei Futurewei Technologies.7 * Copyright (C) 2021, Microsoft Corporation.8 * Pasha Tatashin <[email protected]>9 */1011#include <linux/kexec.h>12#include <linux/linkage.h>1314#include <asm/assembler.h>15#include <asm/kexec.h>16#include <asm/page.h>17#include <asm/sysreg.h>18#include <asm/virt.h>1920.macro turn_off_mmu tmp1, tmp221	mov_q   \tmp1, INIT_SCTLR_EL1_MMU_OFF22	pre_disable_mmu_workaround23	msr	sctlr_el1, \tmp124	isb25.endm2627.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 the32 * new image to its final location.  To assure that the33 * 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 the35 * symbols arm64_relocate_new_kernel and arm64_relocate_new_kernel_end.  The36 * machine_kexec() routine will copy arm64_relocate_new_kernel to the kexec37 * safe memory that has been set up to be preserved during the copy operation.38 */39SYM_CODE_START(arm64_relocate_new_kernel)40	/*41	 * The kimage structure isn't allocated specially and may be clobbered42	 * during relocation. We must load any values we need from it prior to43	 * 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]4849	/* 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 */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_indirection6263	/* Invalidate dest page to PoC. */64	mov	x19, x1365	copy_page x13, x12, x1, x2, x3, x4, x5, x6, x7, x866	add	x1, x19, #PAGE_SIZE67	dcache_by_myline_op civac, sy, x19, x1, x15, x2068	b	.Lnext69.Ltest_indirection:70	tbz	x16, IND_INDIRECTION_BIT, .Ltest_destination71	mov	x14, x12			/* ptr = addr */72	b	.Lnext73.Ltest_destination:74	tbz	x16, IND_DESTINATION_BIT, .Lnext75	mov	x13, x12			/* dest = addr */76.Lnext:77	ldr	x16, [x14], #8			/* entry = *ptr++ */78	tbz	x16, IND_DONE_BIT, .Lloop	/* while (!(entry & DONE)) */79	/* wait for writes from copy_page to finish */80	dsb	nsh81	ic	iallu82	dsb	nsh83	isb84	turn_off_mmu x12, x138586	/* Start new image. */87	cbz	x27, .Lel188	mov	x1, x28				/* kernel entry point */89	mov	x2, x26				/* dtb address */90	mov	x3, xzr91	mov	x4, xzr92	mov     x0, #HVC_SOFT_RESTART93	hvc	#0				/* Jumps from el2 */94.Lel1:95	mov	x0, x26				/* dtb address */96	mov	x1, xzr97	mov	x2, xzr98	mov	x3, xzr99	br	x28				/* Jumps from el1 */100SYM_CODE_END(arm64_relocate_new_kernel)
맨 위로 ↑