역할과 입력
- 기본 형식: x0–x7=인자, [SP]=결과 포인터, [SP+8]=quirk 포인터
- 1.2 형식: x0=args 구조체, x1=res 구조체
원본 .S · 빌드 조건: Makefile · 명령·주소 표기 읽는 법
102줄 · SHA-256: 3b4068804287a6f675cc6d85795e3f2f03bf0ae8075b34e44624b36c51bf7421
구조와 흐름
SMCCC 1.2: 구조체와 레지스터 사이의 값 이동
기본 SMCCC 매크로
명령 인자 instr에 smc 또는 hvc가 들어갑니다. 호출 후 스택에서 결과 포인터를 읽고 x0–x3을 저장합니다. quirk가 없거나 QCOM_A6가 아니면 종료합니다. 해당 quirk에서는 x6도 별도 상태 필드에 저장합니다.
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 실행 명령은 아닙니다.
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 증가시킵니다.
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 매크로를 확장합니다. 이 래퍼는 호출 대상의 지원 여부를 협상하지 않습니다. 사용할 규약과 기능의 확인은 호출 측에서 수행합니다.
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
이 심볼을 모듈이 참조할 수 있도록 내보내기 정보를 생성합니다.
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: retSMCCC_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
retSYM_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 구조체 배치에서 생성됩니다. 숫자를 고정하지 않고 원래 기호를 사용했습니다.
ARM_SMCCC_QUIRK_QCOM_A6: include/linux/arm-smccc.h:67