# smccc-call.S — 말로 풀어 읽기
Linux v6.18.37 · arch/arm64/kernel/smccc-call.S

C 함수가 구조체로 표현한 요청을 펌웨어의 레지스터 호출 규약으로 바꾸는 중간 코드입니다. 펌웨어는 C 구조체 이름을 알지 못합니다. 약속된 레지스터에 값을 넣어 SMC 또는 HVC를 실행하고, 돌아온 레지스터 값을 다시 구조체에 담아 줍니다.

## 처음 읽을 때
args 포인터 자체와 args가 가리키는 값은 다릅니다. x0를 인자 값으로 덮어쓰기 전에 구조체 주소를 x19에 옮기는 이유를 확인하세요.

## 더 깊이 살펴볼 때
기본 SMCCC와 1.2 형식의 레지스터 범위, AAPCS64의 스택 인자, callee-saved x19 보존, Qualcomm quirk를 서로 구분해 보세요.

## 기본 SMCCC 매크로
원본 1–23행

### 5행
```asm
#include <linux/linkage.h>
```
C에서 호출할 어셈블리 함수의 심볼 선언과 모듈 공개에 필요한 linkage 정의를 가져옵니다. #include는 전처리 단계에서 헤더 내용을 가져옵니다. CPU가 이 줄에서 함수를 호출하는 동작은 없습니다.

### 6행
```asm
#include <linux/arm-smccc.h>
```
SMCCC는 커널과 펌웨어·하이퍼바이저 사이의 서비스 호출 규약입니다. 이 헤더에서 결과 구조체, 1.2 확장 레지스터 구조체와 Qualcomm A6 quirk 식별값을 정의합니다. #include는 전처리 단계에서 헤더 내용을 가져옵니다. CPU가 이 줄에서 함수를 호출하는 동작은 없습니다.

### 8행
```asm
#include <asm/asm-offsets.h>
```
C의 SMCCC 결과·입력·quirk 구조체 필드 위치를 어셈블리에서 사용할 오프셋으로 가져옵니다. 커널 빌드가 offsetof로 생성하므로 구조체 배치와 별도로 숫자를 추측할 필요가 없습니다. #include는 전처리 단계에서 헤더 내용을 가져옵니다. CPU가 이 줄에서 함수를 호출하는 동작은 없습니다.

### 9행
```asm
#include <asm/assembler.h>
```
ARM64 공통 어셈블리 매크로와 필요한 하위 헤더 정의를 포함합니다. 실제 서비스 요청·결과 이동은 아래의 SMCCC와 SMCCC_1_2 매크로 본문에 구현됩니다. #include는 전처리 단계에서 헤더 내용을 가져옵니다. CPU가 이 줄에서 함수를 호출하는 동작은 없습니다.

### 11행
```asm
	.macro SMCCC instr
```
SMC와 HVC에 공통인 SMCCC 호출 코드를 만듭니다. x0~x7에는 서비스 번호와 인자가 이미 들어 있으며, 9·10번째 C 인자인 결과 구조체와 선택적 quirk 구조체는 스택에서 가져옵니다. 이 줄은 어셈블러 매크로 정의의 시작입니다. 본문은 매크로를 사용하는 위치에 펼쳐지며, 정의 자체가 런타임 함수 호출은 아닙니다.

### 12행
```asm
	\instr	#0
```
매크로 인자에 따라 SMC 또는 HVC를 실행해 펌웨어·하이퍼바이저 서비스에 요청합니다. 실제 서비스 번호는 x0에 있으며 명령의 #0은 그 서비스 번호가 아닙니다. 돌아오면 x0~x3에 서비스 결과가 들어 있습니다. 일반 함수 호출처럼 BL로 이동하는 대신 동기 예외를 통해 요청을 전달합니다.
instr는 smc/hvc로 치환됩니다. 함수 번호는 즉시값 #0이 아니라 x0입니다.

### 13행
```asm
	ldr	x4, [sp]
```
호출자가 스택에 넘긴 9번째 인자 res를 x4에 읽습니다. 레지스터 인자 x0~x7은 서비스 호출에 썼으므로 결과 저장 위치를 이때 스택에서 가져옵니다. 실제로 sp 주소의 메모리 8바이트를 x4에 읽습니다. 이 주소 형식은 기준 레지스터 sp 자체를 갱신하지 않습니다.
AAPCS64에서 앞의 8개 정수 인자가 x0–x7을 차지해 아홉 번째 결과 포인터가 [SP]에 있습니다.

### 14행
```asm
	stp	x0, x1, [x4, #ARM_SMCCC_RES_X0_OFFS]
```
서비스가 돌려준 x0·x1을 res의 a0·a1 필드에 저장합니다. 이 래퍼의 C 반환형은 void이고 실제 결과는 구조체로 전달됩니다. 메모리에서는 x4 + (ARM_SMCCC_RES_X0_OFFS) 위치가 첫 레지스터 x0에 대응하고, 그보다 8바이트 높은 위치가 두 번째 레지스터 x1에 대응합니다. 이 주소 형식은 기준 레지스터 x4 자체를 갱신하지 않습니다.

### 15행
```asm
	stp	x2, x3, [x4, #ARM_SMCCC_RES_X2_OFFS]
```
나머지 표준 결과 x2·x3도 res의 a2·a3에 저장하여 호출자가 네 레지스터 결과를 읽을 수 있게 합니다. 메모리에서는 x4 + (ARM_SMCCC_RES_X2_OFFS) 위치가 첫 레지스터 x2에 대응하고, 그보다 8바이트 높은 위치가 두 번째 레지스터 x3에 대응합니다. 이 주소 형식은 기준 레지스터 x4 자체를 갱신하지 않습니다.

### 16행
```asm
	ldr	x4, [sp, #8]
```
10번째 인자인 quirk 포인터를 스택의 다음 8바이트 자리에서 읽습니다. 특정 펌웨어의 비표준 반환값을 추가로 보존할 때 사용합니다. 이 주소 형식은 기준 레지스터 sp 자체를 갱신하지 않습니다.
열 번째 인자인 quirk 포인터는 [SP+8]입니다.

### 17행
```asm
	cbz	x4, 1f /* no quirk structure */
```
quirk 포인터가 NULL이면 제조사별 추가 처리가 필요 없으므로 바로 복귀합니다. 이 분기는 지정 레지스터의 값이나 비트를 직접 검사하며 CMP가 남긴 NZCV를 읽거나 바꾸지 않습니다. 조건이 맞지 않으면 바로 다음 명령으로 진행합니다.

### 18행
```asm
	ldr	x9, [x4, #ARM_SMCCC_QUIRK_ID_OFFS]
```
quirk 구조체의 식별값을 읽습니다. 어떤 예외 처리를 요청했는지 확인한 뒤 해당하는 경우에만 추가 레지스터를 보존합니다. 실제로 x4 + (ARM_SMCCC_QUIRK_ID_OFFS) 주소의 메모리 8바이트를 x9에 읽습니다. 이 주소 형식은 기준 레지스터 x4 자체를 갱신하지 않습니다.

### 19행
```asm
	cmp	x9, #ARM_SMCCC_QUIRK_QCOM_A6
```
Qualcomm의 A6 예외 처리를 요청했는지 비교합니다. 이 값은 일반 SMCCC 결과 x0~x3 외에 호출 후 x6 값도 돌려받아야 하는 경우를 식별합니다. CMP는 두 피연산자의 뺄셈 결과로 NZCV 조건 플래그만 갱신하고 원래 레지스터 값은 유지합니다.

### 20행
```asm
	b.ne	1f
```
Qualcomm A6 quirk가 아니면 x6를 저장하지 않고 복귀합니다. 앞서 계산한 NZCV 중 Z=0 조건으로 분기합니다. 이 줄은 두 값을 새로 비교하지 않으며 조건이 맞지 않으면 다음 명령으로 진행합니다.

### 21행
```asm
	str	x6, [x4, ARM_SMCCC_QUIRK_STATE_OFFS]
```
호출 후 x6를 quirk->state.a6에 보관합니다. x6가 표준 결과 구조체에 포함되지 않으므로 이 별도 경로로 호출자에게 전달합니다. 실제로 x4 + (ARM_SMCCC_QUIRK_STATE_OFFS) 주소의 메모리 8바이트를 x6의 값으로 씁니다. 이 주소 형식은 기준 레지스터 x4 자체를 갱신하지 않습니다.
해당 Qualcomm 우회에서만 x6을 추가 결과로 보존합니다.

### 22행
```asm
1:	ret
```
표준 결과와 필요한 추가 결과를 메모리에 저장한 뒤 C 호출자로 돌아갑니다. 피연산자가 없는 RET는 LR(x30)에 들어 있는 주소에서 실행을 이어갑니다.

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

## SMC와 HVC 함수 생성
원본 24–45행

### 31행
```asm
SYM_FUNC_START(__arm_smccc_smc)
```
SMC를 사용해 SMCCC 서비스를 부르는 C 호출 진입점입니다. x0~x7의 요청 인자와 스택의 결과 포인터를 공통 매크로가 처리합니다. 이 선언은 함수 심볼과 정렬을 정하고, ARM64 정의에서는 간접 호출이 도착할 BTI C 명령도 진입부에 넣습니다. 여기서는 전역 심벌로 정의하므로 다른 오브젝트에서도 이 함수 이름을 참조할 수 있습니다. 모듈용 EXPORT_SYMBOL 등록은 별도입니다.

### 32행
```asm
	SMCCC	smc
```
공통 호출 본문의 명령을 SMC로 선택합니다. 보안 모니터 경로에 요청하고 돌아온 x0~x3 및 선택적 Qualcomm x6 결과를 저장합니다. 결과는 호출자가 넘긴 C 구조체의 필드에 기록되므로 C 코드가 레지스터 값을 직접 읽을 필요가 없습니다.

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

### 34행
```asm
EXPORT_SYMBOL(__arm_smccc_smc)
```
__arm_smccc_smc를 모듈에서도 참조할 수 있도록 커널 심볼 공개 정보를 생성합니다. 드라이버 모듈이 SMC 서비스를 요청할 수 있게 하는 빌드 메타데이터이며 여기서 서비스를 실행하지는 않습니다.

### 42행
```asm
SYM_FUNC_START(__arm_smccc_hvc)
```
HVC를 사용해 SMCCC 서비스를 부르는 C 호출 진입점입니다. SMC 진입점과 인자·결과 배치는 같고 요청을 전달하는 통로만 다릅니다. 이 선언은 함수 심볼과 정렬을 정하고, ARM64 정의에서는 간접 호출이 도착할 BTI C 명령도 진입부에 넣습니다. 여기서는 전역 심벌로 정의하므로 다른 오브젝트에서도 이 함수 이름을 참조할 수 있습니다. 모듈용 EXPORT_SYMBOL 등록은 별도입니다.

### 43행
```asm
	SMCCC	hvc
```
공통 본문에서 HVC 통로를 선택합니다. 하이퍼바이저 경로에 요청하고 결과를 호출자의 구조체로 옮깁니다.

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

### 45행
```asm
EXPORT_SYMBOL(__arm_smccc_hvc)
```
__arm_smccc_hvc를 모듈에서도 참조할 수 있도록 커널 심볼 공개 정보를 생성합니다. 드라이버 모듈이 HVC 서비스를 요청할 수 있게 하는 빌드 메타데이터이며 여기서 서비스를 실행하지는 않습니다.

![단계별 개념 그림](../media/smccc-call-overview.png)
화살표는 구조체와 레지스터 사이의 값 복사입니다. 기본 SMCCC 래퍼의 결과 범위와 1.2 래퍼를 혼동하지 않습니다.

## SMCCC 1.2의 구조체 교환
원본 46–84행

### 47행
```asm
	.macro SMCCC_1_2 instr
```
SMCCC 1.2에서 사용하는 x0~x17 전체를 입력·결과 구조체와 주고받는 매크로입니다. 처음의 x0는 args 포인터, x1은 res 포인터이며 곧 레지스터를 요청 값으로 채우므로 두 포인터를 먼저 보존해야 합니다. 이 줄은 어셈블러 매크로 정의의 시작입니다. 본문은 매크로를 사용하는 위치에 펼쳐지며, 정의 자체가 런타임 함수 호출은 아닙니다.

### 49행
```asm
	stp     x1, x19, [sp, #-16]!
```
결과 구조체 포인터 x1과 호출자의 x19를 16바이트 스택 공간에 저장합니다. 결과 포인터는 서비스 이후까지, x19는 C 호출 규약에 맞춰 복귀할 때까지 보존합니다. 메모리에서는 sp + (-16) 위치가 첫 레지스터 x1에 대응하고, 그보다 8바이트 높은 위치가 두 번째 레지스터 x19에 대응합니다. 대괄호 뒤의 !는 주소 갱신을 뜻합니다. sp에 오프셋 -16을 더한 주소로 접근하고 그 주소를 sp에도 남깁니다.
x1의 결과 포인터를 보존하고 x19도 스택에 저장합니다. 총 16바이트를 확보합니다.

### 52행
```asm
	mov	x19, x0
```
입력 구조체 주소를 x19에 옮깁니다. x0부터 요청 값으로 덮어쓰므로 주소를 x0에 그대로 두면 뒤의 인자들을 읽을 수 없습니다. MOV는 x19의 기존 값을 대체합니다. 원본이 주소값이어도 그 주소의 메모리를 읽거나 복사하지 않습니다.
x0를 이후 인자 값으로 덮어써도 구조체를 계속 읽도록 기준을 x19에 둡니다.

### 55행
```asm
	ldp	x0, x1, [x19, #ARM_SMCCC_1_2_REGS_X0_OFFS]
```
입력 구조체 args의 a0·a1을 x0·x1에 넣습니다. a0는 요청할 SMCCC 서비스 번호이고 a1은 그 서비스가 정의한 인자입니다. 메모리에서는 x19 + (ARM_SMCCC_1_2_REGS_X0_OFFS) 위치가 첫 레지스터 x0에 대응하고, 그보다 8바이트 높은 위치가 두 번째 레지스터 x1에 대응합니다. 이 주소 형식은 기준 레지스터 x19 자체를 갱신하지 않습니다.

### 56행
```asm
	ldp	x2, x3, [x19, #ARM_SMCCC_1_2_REGS_X2_OFFS]
```
입력 구조체 args의 a2·a3을 x2·x3에 넣습니다. 각 값의 구체적인 의미는 a0의 서비스 번호가 정한 호출 규약에 따릅니다. 메모리에서는 x19 + (ARM_SMCCC_1_2_REGS_X2_OFFS) 위치가 첫 레지스터 x2에 대응하고, 그보다 8바이트 높은 위치가 두 번째 레지스터 x3에 대응합니다. 이 주소 형식은 기준 레지스터 x19 자체를 갱신하지 않습니다.

### 57행
```asm
	ldp	x4, x5, [x19, #ARM_SMCCC_1_2_REGS_X4_OFFS]
```
입력 구조체 args의 a4·a5을 x4·x5에 넣습니다. 각 값의 구체적인 의미는 a0의 서비스 번호가 정한 호출 규약에 따릅니다. 메모리에서는 x19 + (ARM_SMCCC_1_2_REGS_X4_OFFS) 위치가 첫 레지스터 x4에 대응하고, 그보다 8바이트 높은 위치가 두 번째 레지스터 x5에 대응합니다. 이 주소 형식은 기준 레지스터 x19 자체를 갱신하지 않습니다.

### 58행
```asm
	ldp	x6, x7, [x19, #ARM_SMCCC_1_2_REGS_X6_OFFS]
```
입력 구조체 args의 a6·a7을 x6·x7에 넣습니다. 각 값의 구체적인 의미는 a0의 서비스 번호가 정한 호출 규약에 따릅니다. 메모리에서는 x19 + (ARM_SMCCC_1_2_REGS_X6_OFFS) 위치가 첫 레지스터 x6에 대응하고, 그보다 8바이트 높은 위치가 두 번째 레지스터 x7에 대응합니다. 이 주소 형식은 기준 레지스터 x19 자체를 갱신하지 않습니다.

### 59행
```asm
	ldp	x8, x9, [x19, #ARM_SMCCC_1_2_REGS_X8_OFFS]
```
입력 구조체 args의 a8·a9을 x8·x9에 넣습니다. 각 값의 구체적인 의미는 a0의 서비스 번호가 정한 호출 규약에 따릅니다. 메모리에서는 x19 + (ARM_SMCCC_1_2_REGS_X8_OFFS) 위치가 첫 레지스터 x8에 대응하고, 그보다 8바이트 높은 위치가 두 번째 레지스터 x9에 대응합니다. 이 주소 형식은 기준 레지스터 x19 자체를 갱신하지 않습니다.

### 60행
```asm
	ldp	x10, x11, [x19, #ARM_SMCCC_1_2_REGS_X10_OFFS]
```
입력 구조체 args의 a10·a11을 x10·x11에 넣습니다. 각 값의 구체적인 의미는 a0의 서비스 번호가 정한 호출 규약에 따릅니다. 메모리에서는 x19 + (ARM_SMCCC_1_2_REGS_X10_OFFS) 위치가 첫 레지스터 x10에 대응하고, 그보다 8바이트 높은 위치가 두 번째 레지스터 x11에 대응합니다. 이 주소 형식은 기준 레지스터 x19 자체를 갱신하지 않습니다.

### 61행
```asm
	ldp	x12, x13, [x19, #ARM_SMCCC_1_2_REGS_X12_OFFS]
```
입력 구조체 args의 a12·a13을 x12·x13에 넣습니다. 각 값의 구체적인 의미는 a0의 서비스 번호가 정한 호출 규약에 따릅니다. 메모리에서는 x19 + (ARM_SMCCC_1_2_REGS_X12_OFFS) 위치가 첫 레지스터 x12에 대응하고, 그보다 8바이트 높은 위치가 두 번째 레지스터 x13에 대응합니다. 이 주소 형식은 기준 레지스터 x19 자체를 갱신하지 않습니다.

### 62행
```asm
	ldp	x14, x15, [x19, #ARM_SMCCC_1_2_REGS_X14_OFFS]
```
입력 구조체 args의 a14·a15을 x14·x15에 넣습니다. 각 값의 구체적인 의미는 a0의 서비스 번호가 정한 호출 규약에 따릅니다. 메모리에서는 x19 + (ARM_SMCCC_1_2_REGS_X14_OFFS) 위치가 첫 레지스터 x14에 대응하고, 그보다 8바이트 높은 위치가 두 번째 레지스터 x15에 대응합니다. 이 주소 형식은 기준 레지스터 x19 자체를 갱신하지 않습니다.

### 63행
```asm
	ldp	x16, x17, [x19, #ARM_SMCCC_1_2_REGS_X16_OFFS]
```
입력 구조체 args의 a16·a17을 x16·x17에 넣습니다. 각 값의 구체적인 의미는 a0의 서비스 번호가 정한 호출 규약에 따릅니다. 메모리에서는 x19 + (ARM_SMCCC_1_2_REGS_X16_OFFS) 위치가 첫 레지스터 x16에 대응하고, 그보다 8바이트 높은 위치가 두 번째 레지스터 x17에 대응합니다. 이 주소 형식은 기준 레지스터 x19 자체를 갱신하지 않습니다.

### 65행
```asm
	\instr #0
```
구조체에서 채운 x0~x17을 사용해 SMC 또는 HVC를 실행합니다. SMCCC 1.2의 더 넓은 인자·결과 레지스터 집합을 사용하는 호출이며, 실제 요청 의미는 x0의 서비스 번호에 따릅니다. 일반 함수 호출처럼 BL로 이동하는 대신 동기 예외를 통해 요청을 전달합니다.

### 68행
```asm
	ldr	x19, [sp]
```
스택에 보관한 res 포인터를 x19에 읽습니다. 서비스가 반환한 x0~x17을 손상시키지 않으면서 결과를 저장하기 위한 기준 주소입니다. 실제로 sp 주소의 메모리 8바이트를 x19에 읽습니다. 이 주소 형식은 기준 레지스터 sp 자체를 갱신하지 않습니다.
첫 스택 슬롯에 남긴 res 포인터를 x19로 읽습니다.

### 71행
```asm
	stp	x0, x1, [x19, #ARM_SMCCC_1_2_REGS_X0_OFFS]
```
서비스가 반환한 x0·x1을 결과 구조체 res의 a0·a1에 저장합니다. SMCCC 1.2에서 이 레지스터 쌍까지 결과로 전달할 수 있도록 빠짐없이 보관합니다. 메모리에서는 x19 + (ARM_SMCCC_1_2_REGS_X0_OFFS) 위치가 첫 레지스터 x0에 대응하고, 그보다 8바이트 높은 위치가 두 번째 레지스터 x1에 대응합니다. 이 주소 형식은 기준 레지스터 x19 자체를 갱신하지 않습니다.

### 72행
```asm
	stp	x2, x3, [x19, #ARM_SMCCC_1_2_REGS_X2_OFFS]
```
서비스가 반환한 x2·x3을 결과 구조체 res의 a2·a3에 저장합니다. SMCCC 1.2에서 이 레지스터 쌍까지 결과로 전달할 수 있도록 빠짐없이 보관합니다. 메모리에서는 x19 + (ARM_SMCCC_1_2_REGS_X2_OFFS) 위치가 첫 레지스터 x2에 대응하고, 그보다 8바이트 높은 위치가 두 번째 레지스터 x3에 대응합니다. 이 주소 형식은 기준 레지스터 x19 자체를 갱신하지 않습니다.

### 73행
```asm
	stp	x4, x5, [x19, #ARM_SMCCC_1_2_REGS_X4_OFFS]
```
서비스가 반환한 x4·x5을 결과 구조체 res의 a4·a5에 저장합니다. SMCCC 1.2에서 이 레지스터 쌍까지 결과로 전달할 수 있도록 빠짐없이 보관합니다. 메모리에서는 x19 + (ARM_SMCCC_1_2_REGS_X4_OFFS) 위치가 첫 레지스터 x4에 대응하고, 그보다 8바이트 높은 위치가 두 번째 레지스터 x5에 대응합니다. 이 주소 형식은 기준 레지스터 x19 자체를 갱신하지 않습니다.

### 74행
```asm
	stp	x6, x7, [x19, #ARM_SMCCC_1_2_REGS_X6_OFFS]
```
서비스가 반환한 x6·x7을 결과 구조체 res의 a6·a7에 저장합니다. SMCCC 1.2에서 이 레지스터 쌍까지 결과로 전달할 수 있도록 빠짐없이 보관합니다. 메모리에서는 x19 + (ARM_SMCCC_1_2_REGS_X6_OFFS) 위치가 첫 레지스터 x6에 대응하고, 그보다 8바이트 높은 위치가 두 번째 레지스터 x7에 대응합니다. 이 주소 형식은 기준 레지스터 x19 자체를 갱신하지 않습니다.

### 75행
```asm
	stp	x8, x9, [x19, #ARM_SMCCC_1_2_REGS_X8_OFFS]
```
서비스가 반환한 x8·x9을 결과 구조체 res의 a8·a9에 저장합니다. SMCCC 1.2에서 이 레지스터 쌍까지 결과로 전달할 수 있도록 빠짐없이 보관합니다. 메모리에서는 x19 + (ARM_SMCCC_1_2_REGS_X8_OFFS) 위치가 첫 레지스터 x8에 대응하고, 그보다 8바이트 높은 위치가 두 번째 레지스터 x9에 대응합니다. 이 주소 형식은 기준 레지스터 x19 자체를 갱신하지 않습니다.

### 76행
```asm
	stp	x10, x11, [x19, #ARM_SMCCC_1_2_REGS_X10_OFFS]
```
서비스가 반환한 x10·x11을 결과 구조체 res의 a10·a11에 저장합니다. SMCCC 1.2에서 이 레지스터 쌍까지 결과로 전달할 수 있도록 빠짐없이 보관합니다. 메모리에서는 x19 + (ARM_SMCCC_1_2_REGS_X10_OFFS) 위치가 첫 레지스터 x10에 대응하고, 그보다 8바이트 높은 위치가 두 번째 레지스터 x11에 대응합니다. 이 주소 형식은 기준 레지스터 x19 자체를 갱신하지 않습니다.

### 77행
```asm
	stp	x12, x13, [x19, #ARM_SMCCC_1_2_REGS_X12_OFFS]
```
서비스가 반환한 x12·x13을 결과 구조체 res의 a12·a13에 저장합니다. SMCCC 1.2에서 이 레지스터 쌍까지 결과로 전달할 수 있도록 빠짐없이 보관합니다. 메모리에서는 x19 + (ARM_SMCCC_1_2_REGS_X12_OFFS) 위치가 첫 레지스터 x12에 대응하고, 그보다 8바이트 높은 위치가 두 번째 레지스터 x13에 대응합니다. 이 주소 형식은 기준 레지스터 x19 자체를 갱신하지 않습니다.

### 78행
```asm
	stp	x14, x15, [x19, #ARM_SMCCC_1_2_REGS_X14_OFFS]
```
서비스가 반환한 x14·x15을 결과 구조체 res의 a14·a15에 저장합니다. SMCCC 1.2에서 이 레지스터 쌍까지 결과로 전달할 수 있도록 빠짐없이 보관합니다. 메모리에서는 x19 + (ARM_SMCCC_1_2_REGS_X14_OFFS) 위치가 첫 레지스터 x14에 대응하고, 그보다 8바이트 높은 위치가 두 번째 레지스터 x15에 대응합니다. 이 주소 형식은 기준 레지스터 x19 자체를 갱신하지 않습니다.

### 79행
```asm
	stp	x16, x17, [x19, #ARM_SMCCC_1_2_REGS_X16_OFFS]
```
서비스가 반환한 x16·x17을 결과 구조체 res의 a16·a17에 저장합니다. SMCCC 1.2에서 이 레지스터 쌍까지 결과로 전달할 수 있도록 빠짐없이 보관합니다. 메모리에서는 x19 + (ARM_SMCCC_1_2_REGS_X16_OFFS) 위치가 첫 레지스터 x16에 대응하고, 그보다 8바이트 높은 위치가 두 번째 레지스터 x17에 대응합니다. 이 주소 형식은 기준 레지스터 x19 자체를 갱신하지 않습니다.

### 82행
```asm
	ldp     xzr, x19, [sp], #16
```
스택의 결과 포인터 자리는 xzr로 버리고 호출자의 원래 x19를 복원합니다. SP도 16바이트 되돌려 래퍼가 만든 저장 공간을 해제합니다. 메모리에서는 sp 위치가 첫 레지스터 xzr에 대응하고, 그보다 8바이트 높은 위치가 두 번째 레지스터 x19에 대응합니다. 대괄호 밖의 오프셋 16은 후위 갱신입니다. 기존 sp 주소로 접근한 뒤 sp에 이 값을 더합니다.
res 포인터 저장 슬롯은 버리고 원래 x19를 되살립니다. SP는 16 증가합니다.

### 83행
```asm
	ret
```
x0~x17 결과를 res에 저장한 뒤 C 호출자로 돌아갑니다. 호출자는 함수의 직접 반환값 대신 res 구조체를 확인합니다. 피연산자가 없는 RET는 LR(x30)에 들어 있는 주소에서 실행을 이어갑니다.

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

## 2 함수 두 개
원본 85–102행

### 90행
```asm
SYM_FUNC_START(arm_smccc_1_2_hvc)
```
입력·결과 구조체를 받는 SMCCC 1.2 HVC 진입점입니다. 두 구조체의 a0~a17이 실제 요청·반환 레지스터와 대응합니다. 이 선언은 함수 심볼과 정렬을 정하고, ARM64 정의에서는 간접 호출이 도착할 BTI C 명령도 진입부에 넣습니다. 여기서는 전역 심벌로 정의하므로 다른 오브젝트에서도 이 함수 이름을 참조할 수 있습니다. 모듈용 EXPORT_SYMBOL 등록은 별도입니다.

### 91행
```asm
	SMCCC_1_2 hvc
```
SMCCC 1.2 공통 본문을 HVC 방식으로 펼칩니다. 입력 구조체를 레지스터로 옮기고 하이퍼바이저 요청 결과를 다시 구조체에 저장합니다.

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

### 93행
```asm
EXPORT_SYMBOL(arm_smccc_1_2_hvc)
```
arm_smccc_1_2_hvc를 모듈에서도 참조할 수 있도록 커널 심볼 공개 정보를 생성합니다. 드라이버 모듈이 SMCCC 1.2 HVC 서비스를 요청할 수 있게 하는 빌드 메타데이터이며 여기서 서비스를 실행하지는 않습니다.

### 99행
```asm
SYM_FUNC_START(arm_smccc_1_2_smc)
```
입력·결과 구조체를 받는 SMCCC 1.2 SMC 진입점입니다. 통로가 SMC라는 점 외에는 HVC 함수와 같은 구조체 배치를 사용합니다. 이 선언은 함수 심볼과 정렬을 정하고, ARM64 정의에서는 간접 호출이 도착할 BTI C 명령도 진입부에 넣습니다. 여기서는 전역 심벌로 정의하므로 다른 오브젝트에서도 이 함수 이름을 참조할 수 있습니다. 모듈용 EXPORT_SYMBOL 등록은 별도입니다.

### 100행
```asm
	SMCCC_1_2 smc
```
SMCCC 1.2 공통 본문을 SMC 방식으로 펼쳐 보안 모니터 경로에 요청합니다. 요청을 마치면 x0~x17을 결과 구조체에 보관합니다.

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

### 102행
```asm
EXPORT_SYMBOL(arm_smccc_1_2_smc)
```
arm_smccc_1_2_smc를 모듈에서도 참조할 수 있도록 커널 심볼 공개 정보를 생성합니다. 드라이버 모듈이 SMCCC 1.2 SMC 서비스를 요청할 수 있게 하는 빌드 메타데이터이며 여기서 서비스를 실행하지는 않습니다.

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

### SMC #0의 0이 요청 함수 번호인가요?
아닙니다. 요청 식별자는 호출 규약에 따라 x0에 담깁니다. #0은 SMC/HVC 명령의 즉시값입니다.

### 기본 래퍼에서 결과 포인터가 왜 스택에 있나요?
앞의 정수 인자 8개가 x0–x7을 사용합니다. 아홉 번째 결과 포인터는 AAPCS64 규약에 따라 스택으로 전달됩니다.

### 1.2 래퍼가 원래 x19를 저장하는 이유는 무엇인가요?
래퍼 내부에서는 주소 계산에 사용하지만 x19는 호출자에게 보존해 줘야 하는 레지스터이기 때문입니다.
