DUJINLABS.COM

Linux v6.18.37 · ARM64

hibernate-asm.S — 말로 풀어 읽기

이 파일은 왜 필요한가요?

최대 절전 복귀에서는 저장해 둔 메모리 내용을 현재 메모리에 다시 써야 합니다. 문제는 복사할 목적지에 지금 실행 중인 커널 코드도 들어 있다는 점입니다. 자기 자신을 덮어쓰지 않을 안전한 코드 페이지와 작업용 매핑을 먼저 준비한 다음 복사합니다.

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

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

먼저 알아둘 개념: 가상 주소와 MMU, 주소와 주소가 가리키는 내용, 데이터 캐시와 명령어 캐시, 순서 보장과 완료 대기, 실행 상태의 저장과 복원

처음 읽을 때

페이지를 복사하는 일과 그 페이지를 명령으로 실행할 준비를 하는 일은 구분됩니다. 메모리에 쓴 데이터가 명령어 캐시에도 올바르게 보이도록 정리가 필요합니다.

더 깊이 살펴볼 때

TTBR1을 바꾸는 동안 코드가 TTBR0로 실행되는 구조, break-before-make의 빈 테이블, PoU와 PoC의 범위를 따로 설명해 보세요.

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

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

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

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

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

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

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

먼저 그림으로 살펴보기

최대 절전 이미지의 메모리 복원
화살표: 처리 흐름입니다. 조건이 적힌 단계에서는 해당 조건에 따라 경로가 갈립니다.

안전한 실행 페이지와 테이블 전환

원본 1–59행

메모리 복원 중에도 실행 중인 코드가 남아 있어야 합니다

메모리 복원 중에도 실행 중인 코드가 남아 있어야 합니다의 세 단계 비교
화살표는 페이지 내용 복사와 CPU 상태 복원을 구분합니다. 전체 절전 이미지 읽기는 이 어셈블리 밖의 단계도 포함합니다.

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

메모리 복원 중에도 실행 중인 코드가 남아 있어야 합니다 · 1단계

1단계 고정

GIF 원본 열기

그림의 각 단계를 글로 읽기

1. 복원 준비

현재 사용하는 코드·테이블을 너무 일찍 덮어쓰면 복원이 중단됩니다.

2. 페이지 내용 복사

페이지의 주소를 교환하는 것이 아니라 내용을 복사합니다.

3. 저장 상태에서 재개

단순한 함수 반환보다 많은 실행 환경이 함께 복원되어야 합니다.

#include <linux/linkage.h>

복사해서 실행할 저수준 코드의 시작·끝을 심볼로 표시하는 linkage 매크로를 가져옵니다. 호출·복귀 명령 자체는 아래에 별도로 작성됩니다. #include는 전처리 단계에서 헤더 내용을 가져옵니다. CPU가 이 줄에서 함수를 호출하는 동작은 없습니다.

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

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

#include <linux/errno.h>

커널 오류 번호 정의를 포함합니다. 다만 이 어셈블리 본문은 복원 성공 시 cpu_resume으로 이동하는 경로이며 errno 반환을 수행하는 명령은 없습니다. #include는 전처리 단계에서 헤더 내용을 가져옵니다. CPU가 이 줄에서 함수를 호출하는 동작은 없습니다.

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

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

#include <asm/asm-offsets.h>

복원 페이지 목록 구조체에서 원본 주소·목적지 주소·다음 항목이 위치한 오프셋을 가져옵니다. HIBERN_PBE_ADDR·ORIG·NEXT가 C 구조체와 일치하도록 빌드 과정에서 생성됩니다. #include는 전처리 단계에서 헤더 내용을 가져옵니다. CPU가 이 줄에서 함수를 호출하는 동작은 없습니다.

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

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

#include <asm/assembler.h>

페이지 복사, 데이터 캐시 라인 크기 계산, 안전한 TTBR 전환 매크로를 가져옵니다. 복원 도중 외부 함수 본문이 덮어써질 수 있어 필요한 작업을 여기의 매크로로 펼칩니다. #include는 전처리 단계에서 헤더 내용을 가져옵니다. CPU가 이 줄에서 함수를 호출하는 동작은 없습니다.

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

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

#include <asm/cputype.h>

CPU 식별·기능에 관한 ARM64 정의를 포함합니다. 이 본문에서 CPU별 캐시 문제 대응은 alternative_insn과 ARM64_WORKAROUND_CLEAN_CACHE를 통해 선택됩니다. #include는 전처리 단계에서 헤더 내용을 가져옵니다. CPU가 이 줄에서 함수를 호출하는 동작은 없습니다.

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

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

#include <asm/memory.h>

ARM64 커널 메모리 배치와 크기 관련 정의를 포함합니다. 이 코드는 임시 linear map에서 복원할 페이지 주소를 다루므로 현재 주소 변환 환경과 함께 읽어야 합니다. #include는 전처리 단계에서 헤더 내용을 가져옵니다. CPU가 이 줄에서 함수를 호출하는 동작은 없습니다.

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

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

#include <asm/page.h>

커널 페이지 크기 등의 정의를 가져옵니다. 한 항목이 복원하는 크기와 캐시 처리 끝 주소는 PAGE_SIZE를 사용하며 4KiB로 고정해 읽으면 안 됩니다. #include는 전처리 단계에서 헤더 내용을 가져옵니다. CPU가 이 줄에서 함수를 호출하는 동작은 없습니다.

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

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

#include <asm/virt.h>

ARM64의 EL2 stub 호출 번호와 부팅 모드 등 가상화 공통 정의를 포함합니다. 실제 EL2 재설정은 아래 HVC와 C 쪽 임시 벡터 준비를 함께 봐야 합니다. #include는 전처리 단계에서 헤더 내용을 가져옵니다. CPU가 이 줄에서 함수를 호출하는 동작은 없습니다.

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

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

.pushsection    ".hibernate_exit.text", "ax"

복원 도중 덮어써질 커널 본문과 구분하여, 안전한 페이지로 복사할 실행 코드를 이 섹션에 모읍니다. 복사본은 TTBR0의 별도 매핑에서 실행되므로 복원 대상 페이지를 덮어써도 계속 동작할 수 있습니다. 이는 빌드 중 출력할 바이트의 섹션을 선택하는 지시문이며, 실행 중 PC나 SP를 옮기지 않습니다. 이전 출력 섹션을 함께 기억하므로 뒤의 .popsection으로 되돌릴 수 있습니다.

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

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

SYM_CODE_START(swsusp_arch_suspend_exit)

하이버네이션 이미지의 메모리를 원래 위치에 덮어쓰는 마지막 복원 단계입니다. x0~x5로 임시 페이지 테이블, 복원할 커널의 페이지 테이블·재개 주소, 복사 목록, EL2 벡터, 빈 페이지 주소를 받습니다. 이 매크로는 정렬된 코드 시작 심벌을 정의하고 다른 오브젝트에서도 참조할 수 있게 합니다.

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

함수·코드 범위: swsusp_arch_suspend_exit

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

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

	break_before_make_ttbr_switch	x5, x0, x6, x8

TTBR1을 잠시 빈 페이지 x5로 바꾸고 기존 TLB를 무효화한 뒤 임시 페이지 테이블 x0를 설치합니다. 복원 중 원래 테이블까지 덮어쓰므로 별도로 복사한 linear map이 필요하며, x6·x8은 이 전환의 작업 레지스터입니다.

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

함수·코드 범위: swsusp_arch_suspend_exit

입력
이 명령을 실행하기 직전호출 직전 값
이 명령을 실행한 직후x5, x0, x6, x8
주요 결과
이 명령을 실행하기 직전본문 실행 전
이 명령을 실행한 직후제로 페이지의 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
	mov	x21, x1

메모리 복원이 끝난 뒤 설치할 원래 커널의 페이지 테이블 물리 주소를 x21에 보관합니다. 복사 루프에서 x1을 다른 용도로 쓰기 전에 남겨 둡니다. MOV는 x21의 기존 값을 대체합니다. 원본이 주소값이어도 그 주소의 메모리를 읽거나 복사하지 않습니다.

55행 그림 · 값 복사

함수·코드 범위: swsusp_arch_suspend_exit

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

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

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

최종 재개 주소를 LR인 x30에 넣습니다. 마지막 RET은 이 함수를 호출한 복원용 커널로 돌아가는 대신 저장된 커널의 cpu_resume으로 이동하게 됩니다. MOV는 x30의 기존 값을 대체합니다. 원본이 주소값이어도 그 주소의 메모리를 읽거나 복사하지 않습니다.

cpu_resume를 LR에 보존합니다. 마지막 RET가 여기로 이동합니다.

56행 그림 · 값 복사

함수·코드 범위: swsusp_arch_suspend_exit

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

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

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

복원할 커널의 EL2 벡터 물리 주소를 x24에 보관합니다. 0이면 EL2 벡터 복원이 필요하지 않은 경로입니다. MOV는 x24의 기존 값을 대체합니다. 원본이 주소값이어도 그 주소의 메모리를 읽거나 복사하지 않습니다.

57행 그림 · 값 복사

함수·코드 범위: swsusp_arch_suspend_exit

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

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

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

빈 페이지 물리 주소를 x25에 보관합니다. 루프가 x5를 작업용으로 덮어쓴 뒤에도 원래 페이지 테이블로 바꾸는 break-before-make에 다시 필요합니다. MOV는 x25의 기존 값을 대체합니다. 원본이 주소값이어도 그 주소의 메모리를 읽거나 복사하지 않습니다.

58행 그림 · 값 복사

함수·코드 범위: swsusp_arch_suspend_exit

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

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

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

복원 목록 순회

원본 60–83행

	mov	x19, x3

복원할 페이지 목록 restore_pblist의 첫 항목을 x19에 둡니다. 각 항목에는 임시 보관 위치, 원래 위치, 다음 항목의 포인터가 들어 있습니다. MOV는 x19의 기존 값을 대체합니다. 원본이 주소값이어도 그 주소의 메모리를 읽거나 복사하지 않습니다.

61행 그림 · 값 복사

함수·코드 범위: swsusp_arch_suspend_exit

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

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

  • 화살표는 값의 변화를 뜻합니다. 주소값을 옮겨도 그 주소의 메모리 내용은 복사하지 않습니다.
1:	ldr	x10, [x19, #HIBERN_PBE_ORIG]

현재 항목의 orig_address를 x10에 읽습니다. 이미지에서 되살릴 한 페이지의 원래 주소이며 복사 후 캐시 처리에도 다시 사용합니다. 실제로 x19 + (HIBERN_PBE_ORIG) 주소의 메모리 8바이트를 x10에 읽습니다. 이 주소 형식은 기준 레지스터 x19 자체를 갱신하지 않습니다.

63행 그림 · 메모리 읽기

함수·코드 범위: swsusp_arch_suspend_exit

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

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

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

copy_page의 첫 번째 인자인 목적지 레지스터 x0에 원래 페이지 주소를 넣습니다. MOV는 x0의 기존 값을 대체합니다. 원본이 주소값이어도 그 주소의 메모리를 읽거나 복사하지 않습니다.

x10에 목적지 시작을 따로 남겨 copy_page가 x0를 증가시켜도 캐시 처리 시작을 유지합니다.

64행 그림 · 값 복사

함수·코드 범위: swsusp_arch_suspend_exit

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

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

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

현재 항목의 address를 x1에 읽습니다. 디스크에서 읽은 내용을 임시로 보관한 페이지가 복사 원본이 됩니다. 실제로 x19 + (HIBERN_PBE_ADDR) 주소의 메모리 8바이트를 x1에 읽습니다. 이 주소 형식은 기준 레지스터 x19 자체를 갱신하지 않습니다.

65행 그림 · 메모리 읽기

함수·코드 범위: swsusp_arch_suspend_exit

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

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

  • 주소 계산에 의한 기준 레지스터 갱신은 없습니다. 다만 LDR/LDP의 목적 레지스터가 기준 레지스터와 같으면 읽은 값이 그 레지스터를 덮어씁니다.
	copy_page	x0, x1, x2, x3, x4, x5, x6, x7, x8, x9

x1의 임시 페이지를 x0의 원래 위치로 PAGE_SIZE만큼 복사합니다. x2~x9는 데이터를 운반하는 작업 레지스터이며 x0·x1도 페이지 끝까지 증가합니다. 외부 함수를 호출하지 않는 매크로이므로 복원 중 다른 커널 코드가 덮어써져도 사용할 수 있습니다.

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

함수·코드 범위: swsusp_arch_suspend_exit

입력
이 명령을 실행하기 직전호출 직전 값
이 명령을 실행한 직후x0, x1, x2, x3, x4, x5, x6, x7, x8, x9
주요 결과
이 명령을 실행하기 직전본문 실행 전
이 명령을 실행한 직후한 번에 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, x10, #PAGE_SIZE

원래 주소 x10에 PAGE_SIZE를 더해 캐시 처리 범위의 끝 주소를 x1에 구합니다. 복사된 데이터가 나중에 코드로 실행될 수도 있으므로 페이지 전체를 처리합니다. 이 ADD 형식은 NZCV 조건 플래그를 바꾸지 않습니다.

69행 그림 · 레지스터 상태

함수·코드 범위: swsusp_arch_suspend_exit

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

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

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

현재 CPU의 CTR_EL0에서 최소 데이터 캐시 라인 크기를 읽어 바이트 단위로 x2에 구합니다. x3은 계산용이며, 다음 루프는 이 크기씩 주소를 이동합니다. CTR_EL0.DminLine은 바이트 수 자체가 아닙니다. 4를 이 필드 값만큼 왼쪽으로 이동하여 바이트 수를 구합니다.

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

함수·코드 범위: swsusp_arch_suspend_exit

읽는 값CTR_EL0
x3
이 명령을 실행하기 직전명령 직전 x3의 값
이 명령을 실행한 직후CTR_EL0.DminLine
x2
이 명령을 실행하기 직전명령 직전 x2의 값
이 명령을 실행한 직후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
	sub	x3, x2, #1

캐시 라인 크기에서 1을 빼 정렬 마스크를 만듭니다. 예를 들어 64바이트 라인이면 63이지만 실제 값은 앞서 읽은 CPU 정보에 따릅니다. 이 SUB 형식은 NZCV 조건 플래그를 바꾸지 않습니다.

72행 그림 · 레지스터 상태

함수·코드 범위: swsusp_arch_suspend_exit

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

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

  • 결과는 목적 레지스터 폭에 맞습니다. 이 형식은 NZCV를 바꾸지 않습니다.
	bic	x4, x10, x3

페이지 시작 주소의 하위 정렬 비트를 지워 첫 캐시 라인 시작 주소를 x4에 만듭니다. 페이지의 첫 바이트를 포함하는 라인부터 처리합니다. BIC는 마스크에서 1인 위치만 0으로 만들고 나머지 비트는 유지합니다.

73행 그림 · 레지스터 상태

함수·코드 범위: swsusp_arch_suspend_exit

읽는 값x10x3
x4
이 명령을 실행하기 직전명령 직전 x4의 값
이 명령을 실행한 직후명령 직전 x10의 값 AND NOT 명령 직전 x3의 값

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

  • 결과는 목적 레지스터 폭에 맞습니다. 이 형식은 NZCV를 바꾸지 않습니다.
2:	/* clean D line / unified line */

복사한 페이지에서 캐시 라인 하나를 정리하는 루프의 시작입니다. 뒤의 B.LO 2b가 이 위치로 돌아오며 숫자 라벨 자체는 명령을 추가하지 않습니다. 레이블은 이 위치에 붙인 이름이며, 이름을 적는 것만으로 CPU 명령이 추가되지는 않습니다.

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

함수·코드 범위: swsusp_arch_suspend_exit

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

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

alternative_insn "dc cvau, x4",  "dc civac, x4",  ARM64_WORKAROUND_CLEAN_CACHE

일반 CPU에서는 DC CVAU로 복사한 내용을 명령·데이터가 만나는 PoU까지 정리합니다. ARM64_WORKAROUND_CLEAN_CACHE가 필요한 CPU에는 DC CIVAC로 바꿔 PoC까지 clean하고 invalidate합니다. 이 선택은 부팅 때 CPU 기능에 따른 alternatives 패치로 이루어집니다. 이 매크로는 기본·교체 명령의 패치 정보를 만들며, CPU가 이 줄을 매번 if 조건처럼 실행하지 않습니다.

CPU 우회가 없으면 PoU clean인 CVAU, 해당 우회에서는 PoC clean+invalidate인 CIVAC로 패치됩니다.

75행 그림 · CPU 기능에 따른 명령 패치

함수·코드 범위: swsusp_arch_suspend_exit

패치 전 코드
처리 전기본 명령열
이 줄의 효과기능·정책에 맞는 기본 코드 또는 교체 코드

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

  • 빌드할 때 패치 정보를 만들고 커널의 대체 코드 처리에서 명령을 선택합니다. 이 소스 줄을 CPU가 매번 if문으로 실행하는 것은 아닙니다.
	add	x4, x4, x2

현재 주소를 데이터 캐시 라인 크기만큼 증가시켜 다음 라인을 준비합니다. 이 ADD 형식은 NZCV 조건 플래그를 바꾸지 않습니다.

76행 그림 · 레지스터 상태

함수·코드 범위: swsusp_arch_suspend_exit

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

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

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

다음 캐시 라인 주소와 페이지 끝을 비교합니다. 두 주소의 대소로 아직 처리할 라인이 남았는지 판단합니다. CMP는 두 피연산자의 뺄셈 결과로 NZCV 조건 플래그만 갱신하고 원래 레지스터 값은 유지합니다.

77행 그림 · 조건 플래그 계산

함수·코드 범위: swsusp_arch_suspend_exit

읽는 값x4x1
NZCV
이 명령을 실행하기 직전명령 직전 NZCV의 값
이 명령을 실행한 직후명령 직전 x4의 값 − 명령 직전 x1의 값의 조건 결과

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

  • 일반 레지스터에 연산 결과를 쓰지 않고 조건 플래그만 바꿉니다.
  • N은 결과의 부호 비트, Z는 결과가 0인지 나타냅니다. C는 빌림이 없으면 1, V는 부호 있는 뺄셈의 오버플로를 나타냅니다.
	b.lo	2b

끝 주소보다 작으면 같은 페이지의 다음 캐시 라인을 처리합니다. 메모리 주소 비교이므로 unsigned 조건인 LO를 사용합니다. 앞서 계산한 NZCV 중 C=0 조건으로 분기합니다. 이 줄은 두 값을 새로 비교하지 않으며 조건이 맞지 않으면 다음 명령으로 진행합니다.

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

함수·코드 범위: swsusp_arch_suspend_exit

읽는 값NZCV
PC
이 명령을 실행하기 직전명령 직전 PC의 값
이 명령을 실행한 직후C=0 (부호 없는 미만)이면 2b (L74), 아니면 다음 명령

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

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

복원을 마친 항목에서 다음 페이지 항목의 포인터를 읽습니다. 실제로 x19 + (HIBERN_PBE_NEXT) 주소의 메모리 8바이트를 x19에 읽습니다.

80행 그림 · 메모리 읽기

함수·코드 범위: swsusp_arch_suspend_exit

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

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

  • 주소 계산에 의한 기준 레지스터 갱신은 없습니다. 다만 LDR/LDP의 목적 레지스터가 기준 레지스터와 같으면 읽은 값이 그 레지스터를 덮어씁니다.
	cbnz	x19, 1b

다음 항목이 NULL이 아니면 다시 페이지 복사부터 수행합니다. NULL이면 모든 복원 페이지의 데이터 저장과 캐시 정리 요청이 끝난 상태입니다. 이 분기는 지정 레지스터의 값이나 비트를 직접 검사하며 CMP가 남긴 NZCV를 읽거나 바꾸지 않습니다. 조건이 맞지 않으면 바로 다음 명령으로 진행합니다.

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

함수·코드 범위: swsusp_arch_suspend_exit

읽는 값x19
PC
이 명령을 실행하기 직전명령 직전 PC의 값
이 명령을 실행한 직후명령 직전 x19의 값 ≠ 0이면 1b (L63), 아니면 다음 명령

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

  • 조건이 맞는 한 경로만 실행합니다. 이 분기 명령은 NZCV를 새로 계산하지 않습니다.
	dsb	ish		/* wait for PoU cleaning to finish */

Inner Shareable 영역의 앞선 메모리·캐시 작업 완료를 기다립니다. 복원한 내용을 이후 실행 단계에서 볼 수 있도록, 페이지 테이블 전환 전에 복사 결과를 확정합니다.

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

함수·코드 범위: swsusp_arch_suspend_exit

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

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

복원된 커널의 실행 상태로 이동

원본 84–95행

	break_before_make_ttbr_switch	x25, x21, x6, x8

x25의 빈 페이지를 거쳐 x21에 저장해 둔 원래 커널의 TTBR1 테이블로 바꿉니다. 이제 임시 복사 테이블 대신 하이버네이션 이미지와 함께 복원된 주소 공간을 사용합니다.

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

함수·코드 범위: swsusp_arch_suspend_exit

입력
이 명령을 실행하기 직전호출 직전 값
이 명령을 실행한 직후x25, x21, x6, x8
주요 결과
이 명령을 실행하기 직전본문 실행 전
이 명령을 실행한 직후제로 페이지의 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
	ic	ialluis

Inner Shareable 영역의 명령 캐시를 무효화합니다. 같은 주소에 예전과 다른 명령 바이트를 덮어썼으므로 낡은 명령을 실행하지 않게 해야 합니다. 무효화 요청과 이후 명령 실행에 대한 동기화는 구분되며, 뒤의 DSB·ISB가 그 순서를 마무리합니다.

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

함수·코드 범위: swsusp_arch_suspend_exit

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

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

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

앞서 요청한 명령 캐시 무효화가 완료될 때까지 기다립니다.

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

함수·코드 범위: swsusp_arch_suspend_exit

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

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

	isb

이후 명령을 갱신된 실행 상태에서 다시 가져오게 합니다. 복원한 코드로 분기하기 전에 캐시 동기화 순서를 마무리합니다.

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

함수·코드 범위: swsusp_arch_suspend_exit

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

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

	cbz	x24, 3f		/* Do we need to re-initialise EL2? */

보관한 EL2 벡터 주소 x24가 0이면 HVC를 건너뜁니다. 이 하이버네이션 이미지가 EL2 재설정을 필요로 하는지에 따라 실행 중 선택하는 분기입니다. 이 분기는 지정 레지스터의 값이나 비트를 직접 검사하며 CMP가 남긴 NZCV를 읽거나 바꾸지 않습니다. 조건이 맞지 않으면 바로 다음 명령으로 진행합니다.

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

함수·코드 범위: swsusp_arch_suspend_exit

읽는 값x24
PC
이 명령을 실행하기 직전명령 직전 PC의 값
이 명령을 실행한 직후명령 직전 x24의 값 = 0이면 3f (L93), 아니면 다음 명령

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

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

주석상 목적은 복원된 커널에 맞게 EL2를 다시 설정하는 것입니다. HVC #0의 0은 호출 번호가 아니며 실제 서비스는 x0로 구분됩니다. 다만 이 v6.18.37 함수에는 HVC_SET_VECTORS와 새 벡터 주소를 x0·x1에 준비하는 코드가 보이지 않으므로, 이 한 줄을 두고 벡터 설치가 완료된다고 설명할 수는 없습니다. 호출 인자를 요구하는 trans_pgd_stub_vectors와 함께 확인해야 하는 부분입니다. 일반 함수 호출처럼 BL로 이동하는 대신 동기 예외를 통해 요청을 전달합니다.

x24에는 복원할 EL2 벡터 주소가 남아 있지만, HVC_SET_VECTORS 처리기는 x0의 요청 번호와 x1의 새 벡터 주소를 사용합니다. 이 함수의 표시된 경로에는 그 두 인자를 준비하는 명령이 없으므로 HVC 실행과 벡터 설치 성공을 구분해야 합니다.

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

함수·코드 범위: swsusp_arch_suspend_exit

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

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

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

앞서 x30에 저장해 둔 cpu_resume 주소로 이동합니다. 복원용 커널의 호출 지점으로 돌아가는 것이 아니라, 복원된 커널이 CPU 레지스터와 스택을 되살리는 절차를 시작합니다.

56행에서 기록한 cpu_resume로 이동합니다.

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

함수·코드 범위: swsusp_arch_suspend_exit

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

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

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

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

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

함수·코드 범위: swsusp_arch_suspend_exit

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

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

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

별도 복사용 .hibernate_exit.text의 생성을 끝내고 이전 섹션으로 돌아갑니다. 실행 중 cpu_resume에서 돌아온다는 뜻은 아닙니다. 이는 빌드 중 출력할 바이트의 섹션을 선택하는 지시문이며, 실행 중 PC나 SP를 옮기지 않습니다.

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

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

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

복사 도중 이 함수가 덮어써지면 어떻게 되나요?

다음 명령을 잃게 됩니다. 그래서 안전한 페이지에 옮겨 놓고 그 페이지를 통해 실행합니다.

cpu_resume는 마지막에 어떻게 호출되나요?

앞에서 그 주소를 x30에 보관합니다. 마지막 RET가 저장해 둔 cpu_resume로 이동합니다.

데이터를 복사했는데 왜 I-cache도 정리하나요?

복원된 코드가 새 데이터로 쓰였기 때문입니다. CPU가 이전 명령 캐시 내용을 실행하지 않도록 동기화해야 합니다.

주석을 포함한 전체 원문

Linux stable v6.18.37 · arch/arm64/kernel/hibernate-asm.S

1/* SPDX-License-Identifier: GPL-2.0-only */2/*3 * Hibernate low-level support4 *5 * Copyright (C) 2016 ARM Ltd.6 * Author:	James Morse <james.morse@arm.com>7 */8#include <linux/linkage.h>9#include <linux/errno.h>1011#include <asm/asm-offsets.h>12#include <asm/assembler.h>13#include <asm/cputype.h>14#include <asm/memory.h>15#include <asm/page.h>16#include <asm/virt.h>1718/*19 * Resume from hibernate20 *21 * Loads temporary page tables then restores the memory image.22 * Finally branches to cpu_resume() to restore the state saved by23 * swsusp_arch_suspend().24 *25 * Because this code has to be copied to a 'safe' page, it can't call out to26 * other functions by PC-relative address. Also remember that it may be27 * mid-way through over-writing other functions. For this reason it contains28 * code from caches_clean_inval_pou() and uses the copy_page() macro.29 *30 * This 'safe' page is mapped via ttbr0, and executed from there. This function31 * switches to a copy of the linear map in ttbr1, performs the restore, then32 * switches ttbr1 to the original kernel's swapper_pg_dir.33 *34 * All of memory gets written to, including code. We need to clean the kernel35 * text to the Point of Coherence (PoC) before secondary cores can be booted.36 * Because the kernel modules and executable pages mapped to user space are37 * also written as data, we clean all pages we touch to the Point of38 * Unification (PoU).39 *40 * x0: physical address of temporary page tables41 * x1: physical address of swapper page tables42 * x2: address of cpu_resume43 * x3: linear map address of restore_pblist in the current kernel44 * x4: physical address of __hyp_stub_vectors, or 045 * x5: physical address of a  zero page that remains zero after resume46 */47.pushsection    ".hibernate_exit.text", "ax"48SYM_CODE_START(swsusp_arch_suspend_exit)49	/*50	 * We execute from ttbr0, change ttbr1 to our copied linear map tables51	 * with a break-before-make via the zero page52	 */53	break_before_make_ttbr_switch	x5, x0, x6, x85455	mov	x21, x156	mov	x30, x257	mov	x24, x458	mov	x25, x55960	/* walk the restore_pblist and use copy_page() to over-write memory */61	mov	x19, x362631:	ldr	x10, [x19, #HIBERN_PBE_ORIG]64	mov	x0, x1065	ldr	x1, [x19, #HIBERN_PBE_ADDR]6667	copy_page	x0, x1, x2, x3, x4, x5, x6, x7, x8, x96869	add	x1, x10, #PAGE_SIZE70	/* Clean the copied page to PoU - based on caches_clean_inval_pou() */71	raw_dcache_line_size x2, x372	sub	x3, x2, #173	bic	x4, x10, x3742:	/* clean D line / unified line */75alternative_insn "dc cvau, x4",  "dc civac, x4",  ARM64_WORKAROUND_CLEAN_CACHE76	add	x4, x4, x277	cmp	x4, x178	b.lo	2b7980	ldr	x19, [x19, #HIBERN_PBE_NEXT]81	cbnz	x19, 1b82	dsb	ish		/* wait for PoU cleaning to finish */8384	/* switch to the restored kernels page tables */85	break_before_make_ttbr_switch	x25, x21, x6, x88687	ic	ialluis88	dsb	ish89	isb9091	cbz	x24, 3f		/* Do we need to re-initialise EL2? */92	hvc	#0933:	ret94SYM_CODE_END(swsusp_arch_suspend_exit)95.popsection
맨 위로 ↑