DUJINLABS.COM

Linux v6.18.37 · ARM64

smccc-call.S — SMC/HVC 인자 전달과 반환값 저장

SMCCC는 운영체제와 펌웨어 또는 하이퍼바이저 사이의 호출 규약입니다. 이 파일은 SMC/HVC 명령 실행 전후의 레지스터 값을 C 구조체와 연결합니다.

역할과 입력

  • 기본 형식: x0–x7=인자, [SP]=결과 포인터, [SP+8]=quirk 포인터
  • 1.2 형식: x0=args 구조체, x1=res 구조체

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

102줄 · SHA-256: 3b4068804287a6f675cc6d85795e3f2f03bf0ae8075b34e44624b36c51bf7421

구조와 흐름

SMC/HVC 인자 전달과 반환값 저장
화살표: 처리 흐름입니다. 조건이 적힌 단계에서는 해당 조건에 따라 경로가 갈립니다.

SMCCC 1.2: 구조체와 레지스터 사이의 값 이동

SMCCC 1.2: 구조체와 레지스터 사이의 값 이동
가로 화살표는 데이터 복사 방향입니다. SMC/HVC를 실행하는 동안의 시간 흐름과는 구분합니다.

기본 SMCCC 매크로

명령 인자 instr에 smc 또는 hvc가 들어갑니다. 호출 후 스택에서 결과 포인터를 읽고 x0–x3을 저장합니다. quirk가 없거나 QCOM_A6가 아니면 종료합니다. 해당 quirk에서는 x6도 별도 상태 필드에 저장합니다.

smccc-call.S · L1–L23
1/* SPDX-License-Identifier: GPL-2.0-only */
2/*
3 * Copyright (c) 2015, Linaro Limited
4 */
5#include <linux/linkage.h>
6#include <linux/arm-smccc.h>
7
8#include <asm/asm-offsets.h>
9#include <asm/assembler.h>
10
11	.macro SMCCC instr
12	\instr	#0
13	ldr	x4, [sp]
14	stp	x0, x1, [x4, #ARM_SMCCC_RES_X0_OFFS]
15	stp	x2, x3, [x4, #ARM_SMCCC_RES_X2_OFFS]
16	ldr	x4, [sp, #8]
17	cbz	x4, 1f /* no quirk structure */
18	ldr	x9, [x4, #ARM_SMCCC_QUIRK_ID_OFFS]
19	cmp	x9, #ARM_SMCCC_QUIRK_QCOM_A6
20	b.ne	1f
21	str	x6, [x4, ARM_SMCCC_QUIRK_STATE_OFFS]
221:	ret
23	.endm
L5
C에서 호출할 어셈블리 함수의 심볼 선언과 모듈 공개에 필요한 linkage 정의를 가져옵니다. #include는 전처리 단계에서 헤더 내용을 가져옵니다. CPU가 이 줄에서 함수를 호출하는 동작은 없습니다.
L6
SMCCC는 커널과 펌웨어·하이퍼바이저 사이의 서비스 호출 규약입니다. 이 헤더에서 결과 구조체, 1.2 확장 레지스터 구조체와 Qualcomm A6 quirk 식별값을 정의합니다. #include는 전처리 단계에서 헤더 내용을 가져옵니다. CPU가 이 줄에서 함수를 호출하는 동작은 없습니다.
L8
C의 SMCCC 결과·입력·quirk 구조체 필드 위치를 어셈블리에서 사용할 오프셋으로 가져옵니다. 커널 빌드가 offsetof로 생성하므로 구조체 배치와 별도로 숫자를 추측할 필요가 없습니다. #include는 전처리 단계에서 헤더 내용을 가져옵니다. CPU가 이 줄에서 함수를 호출하는 동작은 없습니다.
L9
ARM64 공통 어셈블리 매크로와 필요한 하위 헤더 정의를 포함합니다. 실제 서비스 요청·결과 이동은 아래의 SMCCC와 SMCCC_1_2 매크로 본문에 구현됩니다. #include는 전처리 단계에서 헤더 내용을 가져옵니다. CPU가 이 줄에서 함수를 호출하는 동작은 없습니다.
L11
SMC와 HVC에 공통인 SMCCC 호출 코드를 만듭니다. x0~x7에는 서비스 번호와 인자가 이미 들어 있으며, 9·10번째 C 인자인 결과 구조체와 선택적 quirk 구조체는 스택에서 가져옵니다. 이 줄은 어셈블러 매크로 정의의 시작입니다. 본문은 매크로를 사용하는 위치에 펼쳐지며, 정의 자체가 런타임 함수 호출은 아닙니다.
L12
매크로 인자에 따라 SMC 또는 HVC를 실행해 펌웨어·하이퍼바이저 서비스에 요청합니다. 실제 서비스 번호는 x0에 있으며 명령의 #0은 그 서비스 번호가 아닙니다. 돌아오면 x0~x3에 서비스 결과가 들어 있습니다. 일반 함수 호출처럼 BL로 이동하는 대신 동기 예외를 통해 요청을 전달합니다.
L13
호출자가 스택에 넘긴 9번째 인자 res를 x4에 읽습니다. 레지스터 인자 x0~x7은 서비스 호출에 썼으므로 결과 저장 위치를 이때 스택에서 가져옵니다. 실제로 sp 주소의 메모리 8바이트를 x4에 읽습니다. 이 주소 형식은 기준 레지스터 sp 자체를 갱신하지 않습니다.
L14
서비스가 돌려준 x0·x1을 res의 a0·a1 필드에 저장합니다. 이 래퍼의 C 반환형은 void이고 실제 결과는 구조체로 전달됩니다. 메모리에서는 x4 + (ARM_SMCCC_RES_X0_OFFS) 위치가 첫 레지스터 x0에 대응하고, 그보다 8바이트 높은 위치가 두 번째 레지스터 x1에 대응합니다. 이 주소 형식은 기준 레지스터 x4 자체를 갱신하지 않습니다.
L15
나머지 표준 결과 x2·x3도 res의 a2·a3에 저장하여 호출자가 네 레지스터 결과를 읽을 수 있게 합니다. 메모리에서는 x4 + (ARM_SMCCC_RES_X2_OFFS) 위치가 첫 레지스터 x2에 대응하고, 그보다 8바이트 높은 위치가 두 번째 레지스터 x3에 대응합니다. 이 주소 형식은 기준 레지스터 x4 자체를 갱신하지 않습니다.
L16
10번째 인자인 quirk 포인터를 스택의 다음 8바이트 자리에서 읽습니다. 특정 펌웨어의 비표준 반환값을 추가로 보존할 때 사용합니다. 이 주소 형식은 기준 레지스터 sp 자체를 갱신하지 않습니다.
L17
quirk 포인터가 NULL이면 제조사별 추가 처리가 필요 없으므로 바로 복귀합니다. 이 분기는 지정 레지스터의 값이나 비트를 직접 검사하며 CMP가 남긴 NZCV를 읽거나 바꾸지 않습니다. 조건이 맞지 않으면 바로 다음 명령으로 진행합니다.
L18
quirk 구조체의 식별값을 읽습니다. 어떤 예외 처리를 요청했는지 확인한 뒤 해당하는 경우에만 추가 레지스터를 보존합니다. 실제로 x4 + (ARM_SMCCC_QUIRK_ID_OFFS) 주소의 메모리 8바이트를 x9에 읽습니다. 이 주소 형식은 기준 레지스터 x4 자체를 갱신하지 않습니다.
L19
Qualcomm의 A6 예외 처리를 요청했는지 비교합니다. 이 값은 일반 SMCCC 결과 x0~x3 외에 호출 후 x6 값도 돌려받아야 하는 경우를 식별합니다. CMP는 두 피연산자의 뺄셈 결과로 NZCV 조건 플래그만 갱신하고 원래 레지스터 값은 유지합니다.
L20
Qualcomm A6 quirk가 아니면 x6를 저장하지 않고 복귀합니다. 앞서 계산한 NZCV 중 Z=0 조건으로 분기합니다. 이 줄은 두 값을 새로 비교하지 않으며 조건이 맞지 않으면 다음 명령으로 진행합니다.
L21
호출 후 x6를 quirk->state.a6에 보관합니다. x6가 표준 결과 구조체에 포함되지 않으므로 이 별도 경로로 호출자에게 전달합니다. 실제로 x4 + (ARM_SMCCC_QUIRK_STATE_OFFS) 주소의 메모리 8바이트를 x6의 값으로 씁니다. 이 주소 형식은 기준 레지스터 x4 자체를 갱신하지 않습니다.
L22
표준 결과와 필요한 추가 결과를 메모리에 저장한 뒤 C 호출자로 돌아갑니다. 피연산자가 없는 RET는 LR(x30)에 들어 있는 주소에서 실행을 이어갑니다.
L23
— 매크로 정의를 끝냅니다.

SMC와 HVC 함수 생성

같은 매크로를 두 번 사용하되 실제 예외 발생 명령만 다릅니다. EXPORT_SYMBOL은 모듈에서 참조할 수 있는 심볼 정보를 만들며 CPU 실행 명령은 아닙니다.

smccc-call.S · L24–L45
24
25/*
26 * void arm_smccc_smc(unsigned long a0, unsigned long a1, unsigned long a2,
27 *		  unsigned long a3, unsigned long a4, unsigned long a5,
28 *		  unsigned long a6, unsigned long a7, struct arm_smccc_res *res,
29 *		  struct arm_smccc_quirk *quirk)
30 */
31SYM_FUNC_START(__arm_smccc_smc)
32	SMCCC	smc
33SYM_FUNC_END(__arm_smccc_smc)
34EXPORT_SYMBOL(__arm_smccc_smc)
35
36/*
37 * void arm_smccc_hvc(unsigned long a0, unsigned long a1, unsigned long a2,
38 *		  unsigned long a3, unsigned long a4, unsigned long a5,
39 *		  unsigned long a6, unsigned long a7, struct arm_smccc_res *res,
40 *		  struct arm_smccc_quirk *quirk)
41 */
42SYM_FUNC_START(__arm_smccc_hvc)
43	SMCCC	hvc
44SYM_FUNC_END(__arm_smccc_hvc)
45EXPORT_SYMBOL(__arm_smccc_hvc)
L31
SMC를 사용해 SMCCC 서비스를 부르는 C 호출 진입점입니다. x0~x7의 요청 인자와 스택의 결과 포인터를 공통 매크로가 처리합니다. 이 선언은 함수 심볼과 정렬을 정하고, ARM64 정의에서는 간접 호출이 도착할 BTI C 명령도 진입부에 넣습니다. 여기서는 전역 심벌로 정의하므로 다른 오브젝트에서도 이 함수 이름을 참조할 수 있습니다. 모듈용 EXPORT_SYMBOL 등록은 별도입니다.
L32
공통 호출 본문의 명령을 SMC로 선택합니다. 보안 모니터 경로에 요청하고 돌아온 x0~x3 및 선택적 Qualcomm x6 결과를 저장합니다. 결과는 호출자가 넘긴 C 구조체의 필드에 기록되므로 C 코드가 레지스터 값을 직접 읽을 필요가 없습니다.
L33
(__arm_smccc_smc)의 심볼 타입과 크기를 기록합니다. 실행을 끝내는 RET를 생성하지 않습니다.
L34
__arm_smccc_smc를 모듈에서도 참조할 수 있도록 커널 심볼 공개 정보를 생성합니다. 드라이버 모듈이 SMC 서비스를 요청할 수 있게 하는 빌드 메타데이터이며 여기서 서비스를 실행하지는 않습니다.
L42
HVC를 사용해 SMCCC 서비스를 부르는 C 호출 진입점입니다. SMC 진입점과 인자·결과 배치는 같고 요청을 전달하는 통로만 다릅니다. 이 선언은 함수 심볼과 정렬을 정하고, ARM64 정의에서는 간접 호출이 도착할 BTI C 명령도 진입부에 넣습니다. 여기서는 전역 심벌로 정의하므로 다른 오브젝트에서도 이 함수 이름을 참조할 수 있습니다. 모듈용 EXPORT_SYMBOL 등록은 별도입니다.
L43
공통 본문에서 HVC 통로를 선택합니다. 하이퍼바이저 경로에 요청하고 결과를 호출자의 구조체로 옮깁니다.
L44
(__arm_smccc_hvc)의 심볼 타입과 크기를 기록합니다. 실행을 끝내는 RET를 생성하지 않습니다.
L45
__arm_smccc_hvc를 모듈에서도 참조할 수 있도록 커널 심볼 공개 정보를 생성합니다. 드라이버 모듈이 HVC 서비스를 요청할 수 있게 하는 빌드 메타데이터이며 여기서 서비스를 실행하지는 않습니다.

SMCCC 1.2의 구조체 교환

결과 포인터 x1과 callee-saved x19를 스택에 저장합니다. args 포인터를 x19에 둔 뒤 x0–x17을 읽으므로 인자 레지스터를 바꿔도 읽기 기준을 잃지 않습니다. 호출 후 x19에 res를 읽어 18개 결과를 저장합니다. 마지막 LDP는 첫 슬롯을 XZR로 버리고 원래 x19를 복구하며 SP를 16 증가시킵니다.

smccc-call.S · L46–L84
46
47	.macro SMCCC_1_2 instr
48	/* Save `res` and free a GPR that won't be clobbered */
49	stp     x1, x19, [sp, #-16]!
50
51	/* Ensure `args` won't be clobbered while loading regs in next step */
52	mov	x19, x0
53
54	/* Load the registers x0 - x17 from the struct arm_smccc_1_2_regs */
55	ldp	x0, x1, [x19, #ARM_SMCCC_1_2_REGS_X0_OFFS]
56	ldp	x2, x3, [x19, #ARM_SMCCC_1_2_REGS_X2_OFFS]
57	ldp	x4, x5, [x19, #ARM_SMCCC_1_2_REGS_X4_OFFS]
58	ldp	x6, x7, [x19, #ARM_SMCCC_1_2_REGS_X6_OFFS]
59	ldp	x8, x9, [x19, #ARM_SMCCC_1_2_REGS_X8_OFFS]
60	ldp	x10, x11, [x19, #ARM_SMCCC_1_2_REGS_X10_OFFS]
61	ldp	x12, x13, [x19, #ARM_SMCCC_1_2_REGS_X12_OFFS]
62	ldp	x14, x15, [x19, #ARM_SMCCC_1_2_REGS_X14_OFFS]
63	ldp	x16, x17, [x19, #ARM_SMCCC_1_2_REGS_X16_OFFS]
64
65	\instr #0
66
67	/* Load the `res` from the stack */
68	ldr	x19, [sp]
69
70	/* Store the registers x0 - x17 into the result structure */
71	stp	x0, x1, [x19, #ARM_SMCCC_1_2_REGS_X0_OFFS]
72	stp	x2, x3, [x19, #ARM_SMCCC_1_2_REGS_X2_OFFS]
73	stp	x4, x5, [x19, #ARM_SMCCC_1_2_REGS_X4_OFFS]
74	stp	x6, x7, [x19, #ARM_SMCCC_1_2_REGS_X6_OFFS]
75	stp	x8, x9, [x19, #ARM_SMCCC_1_2_REGS_X8_OFFS]
76	stp	x10, x11, [x19, #ARM_SMCCC_1_2_REGS_X10_OFFS]
77	stp	x12, x13, [x19, #ARM_SMCCC_1_2_REGS_X12_OFFS]
78	stp	x14, x15, [x19, #ARM_SMCCC_1_2_REGS_X14_OFFS]
79	stp	x16, x17, [x19, #ARM_SMCCC_1_2_REGS_X16_OFFS]
80
81	/* Restore original x19 */
82	ldp     xzr, x19, [sp], #16
83	ret
84.endm
L47
SMCCC 1.2에서 사용하는 x0~x17 전체를 입력·결과 구조체와 주고받는 매크로입니다. 처음의 x0는 args 포인터, x1은 res 포인터이며 곧 레지스터를 요청 값으로 채우므로 두 포인터를 먼저 보존해야 합니다. 이 줄은 어셈블러 매크로 정의의 시작입니다. 본문은 매크로를 사용하는 위치에 펼쳐지며, 정의 자체가 런타임 함수 호출은 아닙니다.
L49
결과 구조체 포인터 x1과 호출자의 x19를 16바이트 스택 공간에 저장합니다. 결과 포인터는 서비스 이후까지, x19는 C 호출 규약에 맞춰 복귀할 때까지 보존합니다. 메모리에서는 sp + (-16) 위치가 첫 레지스터 x1에 대응하고, 그보다 8바이트 높은 위치가 두 번째 레지스터 x19에 대응합니다. 대괄호 뒤의 !는 주소 갱신을 뜻합니다. sp에 오프셋 -16을 더한 주소로 접근하고 그 주소를 sp에도 남깁니다.
L52
입력 구조체 주소를 x19에 옮깁니다. x0부터 요청 값으로 덮어쓰므로 주소를 x0에 그대로 두면 뒤의 인자들을 읽을 수 없습니다. MOV는 x19의 기존 값을 대체합니다. 원본이 주소값이어도 그 주소의 메모리를 읽거나 복사하지 않습니다.
L55
입력 구조체 args의 a0·a1을 x0·x1에 넣습니다. a0는 요청할 SMCCC 서비스 번호이고 a1은 그 서비스가 정의한 인자입니다. 메모리에서는 x19 + (ARM_SMCCC_1_2_REGS_X0_OFFS) 위치가 첫 레지스터 x0에 대응하고, 그보다 8바이트 높은 위치가 두 번째 레지스터 x1에 대응합니다. 이 주소 형식은 기준 레지스터 x19 자체를 갱신하지 않습니다.
L56
입력 구조체 args의 a2·a3을 x2·x3에 넣습니다. 각 값의 구체적인 의미는 a0의 서비스 번호가 정한 호출 규약에 따릅니다. 메모리에서는 x19 + (ARM_SMCCC_1_2_REGS_X2_OFFS) 위치가 첫 레지스터 x2에 대응하고, 그보다 8바이트 높은 위치가 두 번째 레지스터 x3에 대응합니다. 이 주소 형식은 기준 레지스터 x19 자체를 갱신하지 않습니다.
L57
입력 구조체 args의 a4·a5을 x4·x5에 넣습니다. 각 값의 구체적인 의미는 a0의 서비스 번호가 정한 호출 규약에 따릅니다. 메모리에서는 x19 + (ARM_SMCCC_1_2_REGS_X4_OFFS) 위치가 첫 레지스터 x4에 대응하고, 그보다 8바이트 높은 위치가 두 번째 레지스터 x5에 대응합니다. 이 주소 형식은 기준 레지스터 x19 자체를 갱신하지 않습니다.
L58
입력 구조체 args의 a6·a7을 x6·x7에 넣습니다. 각 값의 구체적인 의미는 a0의 서비스 번호가 정한 호출 규약에 따릅니다. 메모리에서는 x19 + (ARM_SMCCC_1_2_REGS_X6_OFFS) 위치가 첫 레지스터 x6에 대응하고, 그보다 8바이트 높은 위치가 두 번째 레지스터 x7에 대응합니다. 이 주소 형식은 기준 레지스터 x19 자체를 갱신하지 않습니다.
L59
입력 구조체 args의 a8·a9을 x8·x9에 넣습니다. 각 값의 구체적인 의미는 a0의 서비스 번호가 정한 호출 규약에 따릅니다. 메모리에서는 x19 + (ARM_SMCCC_1_2_REGS_X8_OFFS) 위치가 첫 레지스터 x8에 대응하고, 그보다 8바이트 높은 위치가 두 번째 레지스터 x9에 대응합니다. 이 주소 형식은 기준 레지스터 x19 자체를 갱신하지 않습니다.
L60
입력 구조체 args의 a10·a11을 x10·x11에 넣습니다. 각 값의 구체적인 의미는 a0의 서비스 번호가 정한 호출 규약에 따릅니다. 메모리에서는 x19 + (ARM_SMCCC_1_2_REGS_X10_OFFS) 위치가 첫 레지스터 x10에 대응하고, 그보다 8바이트 높은 위치가 두 번째 레지스터 x11에 대응합니다. 이 주소 형식은 기준 레지스터 x19 자체를 갱신하지 않습니다.
L61
입력 구조체 args의 a12·a13을 x12·x13에 넣습니다. 각 값의 구체적인 의미는 a0의 서비스 번호가 정한 호출 규약에 따릅니다. 메모리에서는 x19 + (ARM_SMCCC_1_2_REGS_X12_OFFS) 위치가 첫 레지스터 x12에 대응하고, 그보다 8바이트 높은 위치가 두 번째 레지스터 x13에 대응합니다. 이 주소 형식은 기준 레지스터 x19 자체를 갱신하지 않습니다.
L62
입력 구조체 args의 a14·a15을 x14·x15에 넣습니다. 각 값의 구체적인 의미는 a0의 서비스 번호가 정한 호출 규약에 따릅니다. 메모리에서는 x19 + (ARM_SMCCC_1_2_REGS_X14_OFFS) 위치가 첫 레지스터 x14에 대응하고, 그보다 8바이트 높은 위치가 두 번째 레지스터 x15에 대응합니다. 이 주소 형식은 기준 레지스터 x19 자체를 갱신하지 않습니다.
L63
입력 구조체 args의 a16·a17을 x16·x17에 넣습니다. 각 값의 구체적인 의미는 a0의 서비스 번호가 정한 호출 규약에 따릅니다. 메모리에서는 x19 + (ARM_SMCCC_1_2_REGS_X16_OFFS) 위치가 첫 레지스터 x16에 대응하고, 그보다 8바이트 높은 위치가 두 번째 레지스터 x17에 대응합니다. 이 주소 형식은 기준 레지스터 x19 자체를 갱신하지 않습니다.
L65
구조체에서 채운 x0~x17을 사용해 SMC 또는 HVC를 실행합니다. SMCCC 1.2의 더 넓은 인자·결과 레지스터 집합을 사용하는 호출이며, 실제 요청 의미는 x0의 서비스 번호에 따릅니다. 일반 함수 호출처럼 BL로 이동하는 대신 동기 예외를 통해 요청을 전달합니다.
L68
스택에 보관한 res 포인터를 x19에 읽습니다. 서비스가 반환한 x0~x17을 손상시키지 않으면서 결과를 저장하기 위한 기준 주소입니다. 실제로 sp 주소의 메모리 8바이트를 x19에 읽습니다. 이 주소 형식은 기준 레지스터 sp 자체를 갱신하지 않습니다.
L71
서비스가 반환한 x0·x1을 결과 구조체 res의 a0·a1에 저장합니다. SMCCC 1.2에서 이 레지스터 쌍까지 결과로 전달할 수 있도록 빠짐없이 보관합니다. 메모리에서는 x19 + (ARM_SMCCC_1_2_REGS_X0_OFFS) 위치가 첫 레지스터 x0에 대응하고, 그보다 8바이트 높은 위치가 두 번째 레지스터 x1에 대응합니다. 이 주소 형식은 기준 레지스터 x19 자체를 갱신하지 않습니다.
L72
서비스가 반환한 x2·x3을 결과 구조체 res의 a2·a3에 저장합니다. SMCCC 1.2에서 이 레지스터 쌍까지 결과로 전달할 수 있도록 빠짐없이 보관합니다. 메모리에서는 x19 + (ARM_SMCCC_1_2_REGS_X2_OFFS) 위치가 첫 레지스터 x2에 대응하고, 그보다 8바이트 높은 위치가 두 번째 레지스터 x3에 대응합니다. 이 주소 형식은 기준 레지스터 x19 자체를 갱신하지 않습니다.
L73
서비스가 반환한 x4·x5을 결과 구조체 res의 a4·a5에 저장합니다. SMCCC 1.2에서 이 레지스터 쌍까지 결과로 전달할 수 있도록 빠짐없이 보관합니다. 메모리에서는 x19 + (ARM_SMCCC_1_2_REGS_X4_OFFS) 위치가 첫 레지스터 x4에 대응하고, 그보다 8바이트 높은 위치가 두 번째 레지스터 x5에 대응합니다. 이 주소 형식은 기준 레지스터 x19 자체를 갱신하지 않습니다.
L74
서비스가 반환한 x6·x7을 결과 구조체 res의 a6·a7에 저장합니다. SMCCC 1.2에서 이 레지스터 쌍까지 결과로 전달할 수 있도록 빠짐없이 보관합니다. 메모리에서는 x19 + (ARM_SMCCC_1_2_REGS_X6_OFFS) 위치가 첫 레지스터 x6에 대응하고, 그보다 8바이트 높은 위치가 두 번째 레지스터 x7에 대응합니다. 이 주소 형식은 기준 레지스터 x19 자체를 갱신하지 않습니다.
L75
서비스가 반환한 x8·x9을 결과 구조체 res의 a8·a9에 저장합니다. SMCCC 1.2에서 이 레지스터 쌍까지 결과로 전달할 수 있도록 빠짐없이 보관합니다. 메모리에서는 x19 + (ARM_SMCCC_1_2_REGS_X8_OFFS) 위치가 첫 레지스터 x8에 대응하고, 그보다 8바이트 높은 위치가 두 번째 레지스터 x9에 대응합니다. 이 주소 형식은 기준 레지스터 x19 자체를 갱신하지 않습니다.
L76
서비스가 반환한 x10·x11을 결과 구조체 res의 a10·a11에 저장합니다. SMCCC 1.2에서 이 레지스터 쌍까지 결과로 전달할 수 있도록 빠짐없이 보관합니다. 메모리에서는 x19 + (ARM_SMCCC_1_2_REGS_X10_OFFS) 위치가 첫 레지스터 x10에 대응하고, 그보다 8바이트 높은 위치가 두 번째 레지스터 x11에 대응합니다. 이 주소 형식은 기준 레지스터 x19 자체를 갱신하지 않습니다.
L77
서비스가 반환한 x12·x13을 결과 구조체 res의 a12·a13에 저장합니다. SMCCC 1.2에서 이 레지스터 쌍까지 결과로 전달할 수 있도록 빠짐없이 보관합니다. 메모리에서는 x19 + (ARM_SMCCC_1_2_REGS_X12_OFFS) 위치가 첫 레지스터 x12에 대응하고, 그보다 8바이트 높은 위치가 두 번째 레지스터 x13에 대응합니다. 이 주소 형식은 기준 레지스터 x19 자체를 갱신하지 않습니다.
L78
서비스가 반환한 x14·x15을 결과 구조체 res의 a14·a15에 저장합니다. SMCCC 1.2에서 이 레지스터 쌍까지 결과로 전달할 수 있도록 빠짐없이 보관합니다. 메모리에서는 x19 + (ARM_SMCCC_1_2_REGS_X14_OFFS) 위치가 첫 레지스터 x14에 대응하고, 그보다 8바이트 높은 위치가 두 번째 레지스터 x15에 대응합니다. 이 주소 형식은 기준 레지스터 x19 자체를 갱신하지 않습니다.
L79
서비스가 반환한 x16·x17을 결과 구조체 res의 a16·a17에 저장합니다. SMCCC 1.2에서 이 레지스터 쌍까지 결과로 전달할 수 있도록 빠짐없이 보관합니다. 메모리에서는 x19 + (ARM_SMCCC_1_2_REGS_X16_OFFS) 위치가 첫 레지스터 x16에 대응하고, 그보다 8바이트 높은 위치가 두 번째 레지스터 x17에 대응합니다. 이 주소 형식은 기준 레지스터 x19 자체를 갱신하지 않습니다.
L82
스택의 결과 포인터 자리는 xzr로 버리고 호출자의 원래 x19를 복원합니다. SP도 16바이트 되돌려 래퍼가 만든 저장 공간을 해제합니다. 메모리에서는 sp 위치가 첫 레지스터 xzr에 대응하고, 그보다 8바이트 높은 위치가 두 번째 레지스터 x19에 대응합니다. 대괄호 밖의 오프셋 16은 후위 갱신입니다. 기존 sp 주소로 접근한 뒤 sp에 이 값을 더합니다.
L83
x0~x17 결과를 res에 저장한 뒤 C 호출자로 돌아갑니다. 호출자는 함수의 직접 반환값 대신 res 구조체를 확인합니다. 피연산자가 없는 RET는 LR(x30)에 들어 있는 주소에서 실행을 이어갑니다.
L84
— 매크로 정의를 끝냅니다.

1.2 함수 두 개

arm_smccc_1_2_hvc와 arm_smccc_1_2_smc는 각각 SMCCC_1_2 매크로를 확장합니다. 이 래퍼는 호출 대상의 지원 여부를 협상하지 않습니다. 사용할 규약과 기능의 확인은 호출 측에서 수행합니다.

smccc-call.S · L85–L102
85
86/*
87 * void arm_smccc_1_2_hvc(const struct arm_smccc_1_2_regs *args,
88 *			  struct arm_smccc_1_2_regs *res);
89 */
90SYM_FUNC_START(arm_smccc_1_2_hvc)
91	SMCCC_1_2 hvc
92SYM_FUNC_END(arm_smccc_1_2_hvc)
93EXPORT_SYMBOL(arm_smccc_1_2_hvc)
94
95/*
96 * void arm_smccc_1_2_smc(const struct arm_smccc_1_2_regs *args,
97 *			  struct arm_smccc_1_2_regs *res);
98 */
99SYM_FUNC_START(arm_smccc_1_2_smc)
100	SMCCC_1_2 smc
101SYM_FUNC_END(arm_smccc_1_2_smc)
102EXPORT_SYMBOL(arm_smccc_1_2_smc)
L90
입력·결과 구조체를 받는 SMCCC 1.2 HVC 진입점입니다. 두 구조체의 a0~a17이 실제 요청·반환 레지스터와 대응합니다. 이 선언은 함수 심볼과 정렬을 정하고, ARM64 정의에서는 간접 호출이 도착할 BTI C 명령도 진입부에 넣습니다. 여기서는 전역 심벌로 정의하므로 다른 오브젝트에서도 이 함수 이름을 참조할 수 있습니다. 모듈용 EXPORT_SYMBOL 등록은 별도입니다.
L91
SMCCC 1.2 공통 본문을 HVC 방식으로 펼칩니다. 입력 구조체를 레지스터로 옮기고 하이퍼바이저 요청 결과를 다시 구조체에 저장합니다.
L92
(arm_smccc_1_2_hvc)의 심볼 타입과 크기를 기록합니다. 실행을 끝내는 RET를 생성하지 않습니다.
L93
arm_smccc_1_2_hvc를 모듈에서도 참조할 수 있도록 커널 심볼 공개 정보를 생성합니다. 드라이버 모듈이 SMCCC 1.2 HVC 서비스를 요청할 수 있게 하는 빌드 메타데이터이며 여기서 서비스를 실행하지는 않습니다.
L99
입력·결과 구조체를 받는 SMCCC 1.2 SMC 진입점입니다. 통로가 SMC라는 점 외에는 HVC 함수와 같은 구조체 배치를 사용합니다. 이 선언은 함수 심볼과 정렬을 정하고, ARM64 정의에서는 간접 호출이 도착할 BTI C 명령도 진입부에 넣습니다. 여기서는 전역 심벌로 정의하므로 다른 오브젝트에서도 이 함수 이름을 참조할 수 있습니다. 모듈용 EXPORT_SYMBOL 등록은 별도입니다.
L100
SMCCC 1.2 공통 본문을 SMC 방식으로 펼쳐 보안 모니터 경로에 요청합니다. 요청을 마치면 x0~x17을 결과 구조체에 보관합니다.
L101
(arm_smccc_1_2_smc)의 심볼 타입과 크기를 기록합니다. 실행을 끝내는 RET를 생성하지 않습니다.
L102
arm_smccc_1_2_smc를 모듈에서도 참조할 수 있도록 커널 심볼 공개 정보를 생성합니다. 드라이버 모듈이 SMCCC 1.2 SMC 서비스를 요청할 수 있게 하는 빌드 메타데이터이며 여기서 서비스를 실행하지는 않습니다.

매크로 정의와 확장

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

EXPORT_SYMBOL

이 심볼을 모듈이 참조할 수 있도록 내보내기 정보를 생성합니다.

include/linux/export.h L89–L89

앞뒤 조건을 포함한 원문

#define EXPORT_SYMBOL(sym)		_EXPORT_SYMBOL(sym, "")

SMCCC

instr에 SMC 또는 HVC를 대입하고 x0–x3의 결과를 스택에서 얻은 결과 구조체에 저장합니다. 선택적으로 QCOM_A6 상태 x6도 기록합니다.

arch/arm64/kernel/smccc-call.S L11–L23

앞뒤 조건을 포함한 원문

	.macro SMCCC instr
	\instr	#0
	ldr	x4, [sp]
	stp	x0, x1, [x4, #ARM_SMCCC_RES_X0_OFFS]
	stp	x2, x3, [x4, #ARM_SMCCC_RES_X2_OFFS]
	ldr	x4, [sp, #8]
	cbz	x4, 1f /* no quirk structure */
	ldr	x9, [x4, #ARM_SMCCC_QUIRK_ID_OFFS]
	cmp	x9, #ARM_SMCCC_QUIRK_QCOM_A6
	b.ne	1f
	str	x6, [x4, ARM_SMCCC_QUIRK_STATE_OFFS]
1:	ret
	.endm
인자 치환 예: SMCCC smc

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

	smc	#0
	ldr	x4, [sp]
	stp	x0, x1, [x4, #ARM_SMCCC_RES_X0_OFFS]
	stp	x2, x3, [x4, #ARM_SMCCC_RES_X2_OFFS]
	ldr	x4, [sp, #8]
	cbz	x4, 1f /* no quirk structure */
	ldr	x9, [x4, #ARM_SMCCC_QUIRK_ID_OFFS]
	cmp	x9, #ARM_SMCCC_QUIRK_QCOM_A6
	b.ne	1f
	str	x6, [x4, ARM_SMCCC_QUIRK_STATE_OFFS]
1:	ret

SMCCC_1_2

구조체에서 x0–x17을 읽고 지정된 SMC/HVC를 실행한 뒤 결과 구조체에 x0–x17을 저장합니다. 원래 x19와 SP는 복원합니다.

arch/arm64/kernel/smccc-call.S L47–L84

앞뒤 조건을 포함한 원문

	.macro SMCCC_1_2 instr
	/* Save `res` and free a GPR that won't be clobbered */
	stp     x1, x19, [sp, #-16]!

	/* Ensure `args` won't be clobbered while loading regs in next step */
	mov	x19, x0

	/* Load the registers x0 - x17 from the struct arm_smccc_1_2_regs */
	ldp	x0, x1, [x19, #ARM_SMCCC_1_2_REGS_X0_OFFS]
	ldp	x2, x3, [x19, #ARM_SMCCC_1_2_REGS_X2_OFFS]
	ldp	x4, x5, [x19, #ARM_SMCCC_1_2_REGS_X4_OFFS]
	ldp	x6, x7, [x19, #ARM_SMCCC_1_2_REGS_X6_OFFS]
	ldp	x8, x9, [x19, #ARM_SMCCC_1_2_REGS_X8_OFFS]
	ldp	x10, x11, [x19, #ARM_SMCCC_1_2_REGS_X10_OFFS]
	ldp	x12, x13, [x19, #ARM_SMCCC_1_2_REGS_X12_OFFS]
	ldp	x14, x15, [x19, #ARM_SMCCC_1_2_REGS_X14_OFFS]
	ldp	x16, x17, [x19, #ARM_SMCCC_1_2_REGS_X16_OFFS]

	\instr #0

	/* Load the `res` from the stack */
	ldr	x19, [sp]

	/* Store the registers x0 - x17 into the result structure */
	stp	x0, x1, [x19, #ARM_SMCCC_1_2_REGS_X0_OFFS]
	stp	x2, x3, [x19, #ARM_SMCCC_1_2_REGS_X2_OFFS]
	stp	x4, x5, [x19, #ARM_SMCCC_1_2_REGS_X4_OFFS]
	stp	x6, x7, [x19, #ARM_SMCCC_1_2_REGS_X6_OFFS]
	stp	x8, x9, [x19, #ARM_SMCCC_1_2_REGS_X8_OFFS]
	stp	x10, x11, [x19, #ARM_SMCCC_1_2_REGS_X10_OFFS]
	stp	x12, x13, [x19, #ARM_SMCCC_1_2_REGS_X12_OFFS]
	stp	x14, x15, [x19, #ARM_SMCCC_1_2_REGS_X14_OFFS]
	stp	x16, x17, [x19, #ARM_SMCCC_1_2_REGS_X16_OFFS]

	/* Restore original x19 */
	ldp     xzr, x19, [sp], #16
	ret
.endm
인자 치환 예: SMCCC_1_2 hvc

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

	/* Save `res` and free a GPR that won't be clobbered */
	stp     x1, x19, [sp, #-16]!

	/* Ensure `args` won't be clobbered while loading regs in next step */
	mov	x19, x0

	/* Load the registers x0 - x17 from the struct arm_smccc_1_2_regs */
	ldp	x0, x1, [x19, #ARM_SMCCC_1_2_REGS_X0_OFFS]
	ldp	x2, x3, [x19, #ARM_SMCCC_1_2_REGS_X2_OFFS]
	ldp	x4, x5, [x19, #ARM_SMCCC_1_2_REGS_X4_OFFS]
	ldp	x6, x7, [x19, #ARM_SMCCC_1_2_REGS_X6_OFFS]
	ldp	x8, x9, [x19, #ARM_SMCCC_1_2_REGS_X8_OFFS]
	ldp	x10, x11, [x19, #ARM_SMCCC_1_2_REGS_X10_OFFS]
	ldp	x12, x13, [x19, #ARM_SMCCC_1_2_REGS_X12_OFFS]
	ldp	x14, x15, [x19, #ARM_SMCCC_1_2_REGS_X14_OFFS]
	ldp	x16, x17, [x19, #ARM_SMCCC_1_2_REGS_X16_OFFS]

	hvc #0

	/* Load the `res` from the stack */
	ldr	x19, [sp]

	/* Store the registers x0 - x17 into the result structure */
	stp	x0, x1, [x19, #ARM_SMCCC_1_2_REGS_X0_OFFS]
	stp	x2, x3, [x19, #ARM_SMCCC_1_2_REGS_X2_OFFS]
	stp	x4, x5, [x19, #ARM_SMCCC_1_2_REGS_X4_OFFS]
	stp	x6, x7, [x19, #ARM_SMCCC_1_2_REGS_X6_OFFS]
	stp	x8, x9, [x19, #ARM_SMCCC_1_2_REGS_X8_OFFS]
	stp	x10, x11, [x19, #ARM_SMCCC_1_2_REGS_X10_OFFS]
	stp	x12, x13, [x19, #ARM_SMCCC_1_2_REGS_X12_OFFS]
	stp	x14, x15, [x19, #ARM_SMCCC_1_2_REGS_X14_OFFS]
	stp	x16, x17, [x19, #ARM_SMCCC_1_2_REGS_X16_OFFS]

	/* Restore original x19 */
	ldp     xzr, x19, [sp], #16
	ret

SYM_FUNC_END

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

include/linux/linkage.h L248–L249

앞뒤 조건을 포함한 원문

#define SYM_FUNC_END(name)				\
	SYM_END(name, SYM_T_FUNC)

SYM_FUNC_START

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

arch/arm64/include/asm/linkage.h L18–L20

앞뒤 조건을 포함한 원문

#define SYM_FUNC_START(name)				\
	SYM_START(name, SYM_L_GLOBAL, SYM_A_ALIGN)	\
	bti c ;
include/linux/linkage.h L209–L210

앞뒤 조건을 포함한 원문

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

상수·구조체 오프셋

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

asm-offsets.c의 생성 식

맨 위로 ↑