ARM TrustZone & OP-TEE
ARM TrustZone 하드웨어 보안 기술과 OP-TEE(Open Portable Trusted Execution Environment)를 통한 신뢰 실행 환경(TEE) 구현을 심층 분석합니다. Secure/Normal World 분리, SMC 호출 규약(Calling Convention), TF-A(Trusted Firmware-A), 커널 TEE 프레임워크, OP-TEE 드라이버, Trusted Application 개발, Secure Storage, fTPM, GlobalPlatform 규격까지 다룹니다.
일상 비유: TrustZone은 은행의 금고실과 같습니다. 일반 업무 공간(Normal World)과 금고실(Secure World)이 물리적으로 분리되어 있고, 금고실에 접근하려면 반드시 보안 게이트(SMC)를 통과해야 합니다. OP-TEE는 금고실 안에서 동작하는 보안 운영체제로, 금고 안에서 암호 키 관리, 생체 인증, DRM 등 민감한 작업을 수행합니다.
핵심 요약
- TrustZone — ARM 프로세서의 하드웨어 보안 확장으로, 단일 물리 코어를 Secure World와 Normal World 두 가상 프로세서로 분리합니다.
- SMC (Secure Monitor Call) — Normal World에서 Secure World로 전환하는 유일한 게이트웨이입니다. SMCCC(SMC Calling Convention)가 호출 규약을 표준화합니다.
- TF-A (Trusted Firmware-A) — EL3 Secure Monitor로, BL1→BL2→BL31→BL32→BL33 부팅 체인과 런타임 SMC 디스패치(Dispatch)를 담당합니다.
- OP-TEE — Linaro가 주도하는 오픈소스 TEE OS로, S-EL1에서 동작하며 GlobalPlatform TEE 규격을 구현합니다.
- 커널 TEE 프레임워크 —
drivers/tee/아래/dev/tee*,/dev/teepriv*장치를 제공하여 사용자 공간(User Space)에서 TA(Trusted Application)와 통신합니다. - Trusted Application — Secure World 내에서 실행되는 독립 프로그램으로, UUID로 식별되며 GlobalPlatform Internal Core API를 사용합니다.
- Secure Storage — REE 파일시스템(Filesystem) 또는 RPMB(Replay Protected Memory Block)를 백엔드로 사용하여 암호화(Encryption)된 데이터를 안전하게 저장합니다.
- fTPM — OP-TEE TA로 구현된 펌웨어(Firmware) TPM 2.0으로, 별도 TPM 칩 없이 TPM 기능을 제공합니다.
TrustZone 아키텍처
ARM TrustZone은 ARMv6K부터 도입된 하드웨어 보안 확장입니다. 프로세서, 메모리 컨트롤러, 버스(Bus) 인터커넥트, 주변 장치까지 시스템 전체에 걸쳐 Secure와 Non-secure(Normal) 두 상태를 하드웨어 수준에서 분리합니다.
NS 비트 (Non-Secure bit)
TrustZone의 핵심은 SCR_EL3.NS (Secure Configuration Register) 비트입니다. 이 1비트가 프로세서의 현재 보안 상태를 결정하며, AXI 버스의 AxPROT[1] 신호로 전파되어 메모리 컨트롤러와 주변 장치가 접근 권한을 판별합니다.
| NS 비트 | 상태 | 접근 가능 메모리 | 용도 |
|---|---|---|---|
0 | Secure | Secure + Non-secure 전체 | TEE OS, TA, Secure 펌웨어 |
1 | Non-secure | Non-secure 영역만 | Linux 커널, 일반 앱 |
TrustZone 하드웨어 구성요소
| 구성요소 | 역할 | 커널 소스 위치 |
|---|---|---|
| CPU NS bit | 프로세서 보안 상태 결정 (SCR_EL3.NS) | arch/arm64/kernel/ |
| TZASC (TZC-400) | DRAM 영역별 Secure/Non-secure 접근 제어(Access Control) | TF-A: drivers/arm/tzc/ |
| TZMA | SRAM 보호 (TrustZone Memory Adapter) | TF-A: plat/ |
| TZPC | 주변 장치 Secure/Non-secure 분류 | TF-A: plat/ |
| GIC Security Extension | 인터럽트 Group 0/1 분리, FIQ 라우팅(Routing) | drivers/irqchip/irq-gic-v3.c |
| AXI/ACE AxPROT | 버스 트랜잭션(Transaction)에 NS 비트 전파 | 하드웨어 시그널(Signal) |
ARMv9 Realm Management Extension (RME): ARMv9에서는 TrustZone의 2-World 모델을 확장하여 Realm 세계를 추가했습니다. 이는 Confidential Computing을 위한 것으로, 기밀 컴퓨팅(Confidential Computing) 문서에서 자세히 다룹니다.
Exception Level과 Security State
ARMv8-A 아키텍처는 4개의 Exception Level(EL0~EL3)에 보안 상태(Secure/Non-secure)를 결합하여 최대 7개의 실행 컨텍스트를 제공합니다. TrustZone 환경에서 각 레벨의 역할을 정확히 이해하는 것이 필수입니다.
| 레벨 | Non-secure | Secure | 전환 방법 |
|---|---|---|---|
| EL3 | Secure Monitor (TF-A BL31) | SMC, FIQ, 시스템 리셋 | |
| EL2 | KVM/Xen Hypervisor | S-EL2: Secure Partition Manager (FF-A) | HVC (EL1→EL2) |
| EL1 | Linux 커널 | OP-TEE OS | SVC (EL0→EL1), ERET (EL3→S-EL1) |
| EL0 | 사용자 앱 | Trusted Application | SVC (EL0→EL1) |
중요: Normal World의 EL2(Hypervisor)는 Secure World에 직접 접근할 수 없습니다. Hypervisor가 아무리 높은 특권을 가져도 NS=1 상태이므로 Secure 메모리에 접근하면 External Abort가 발생합니다. World 전환은 반드시 EL3 Secure Monitor를 경유해야 합니다.
보안 상태 전환 시 레지스터(Register) 저장
World 전환 시 EL3 Monitor는 현재 World의 전체 레지스터 컨텍스트를 저장하고 대상 World의 컨텍스트를 복원합니다. 이 과정에서 다음 레지스터가 관리됩니다.
/* TF-A: lib/el3_runtime/aarch64/context_mgmt.c */
typedef struct cpu_context {
gp_regs_t gpregs_ctx; /* x0-x30, SP_EL0 */
el3_state_t el3state_ctx; /* SCR_EL3, SCTLR_EL3, ... */
el1_sysregs_t el1_sysregs_ctx;/* SCTLR_EL1, TTBR0/1_EL1, ... */
el2_sysregs_t el2_sysregs_ctx;/* HCR_EL2, VTTBR_EL2, ... */
fp_regs_t fpregs_ctx; /* SIMD/FP: v0-v31, FPCR, FPSR */
} cpu_context_t;
/* World 전환: Normal → Secure */
void cm_el1_sysregs_context_save(uint32_t security_state);
void cm_el1_sysregs_context_restore(uint32_t security_state);
void cm_set_next_eret_context(uint32_t security_state);
SMC 호출규약 (SMCCC)
SMC(Secure Monitor Call)는 Normal World에서 Secure World 서비스를 요청하는 동기적 호출 메커니즘입니다. ARM은 SMCCC(SMC Calling Convention) 규격을 통해 Function ID 인코딩, 파라미터 전달, 반환값 규약을 표준화했습니다.
SMC Function ID 인코딩 (32비트)
| 비트 | 필드 | 설명 |
|---|---|---|
| [31] | Calling Convention | 0 = SMC32, 1 = SMC64 |
| [30] | Call Type | 0 = Yielding Call, 1 = Fast Call |
| [29:24] | Service Range | 서비스 소유자 (0x00=ARM, 0x02=SiP, 0x04=OEM, 0x30-0x31=TA, 0x32-0x3F=TEE OS) |
| [23:16] | Reserved | MBZ (Must Be Zero) |
| [15:0] | Function Number | 서비스 내 함수 번호 |
Fast Call vs Yielding Call
| 특성 | Fast Call | Yielding Call (Standard Call) |
|---|---|---|
| 인터럽트 | Secure World에서 비선점(Non-preemptive) | Normal World 인터럽트로 선점(Preemption) 가능 |
| 용도 | PSCI, CPU 전원 관리(Power Management), 짧은 쿼리 | TA 호출, 긴 암호 연산 |
| bit[30] | 1 | 0 |
| 커널 호출 | arm_smccc_smc() | arm_smccc_smc() + 스레드(Thread) 양보(Yield) |
Linux 커널 SMCCC 인터페이스
/* include/linux/arm-smccc.h */
struct arm_smccc_res {
unsigned long a0; /* 반환값 / 에러 코드 */
unsigned long a1;
unsigned long a2;
unsigned long a3;
};
/* SMC 호출 매크로 */
void arm_smccc_smc(
unsigned long a0, unsigned long a1,
unsigned long a2, unsigned long a3,
unsigned long a4, unsigned long a5,
unsigned long a6, unsigned long a7,
struct arm_smccc_res *res);
/* HVC 호출 (EL2 서비스) */
void arm_smccc_hvc(
unsigned long a0, unsigned long a1,
unsigned long a2, unsigned long a3,
unsigned long a4, unsigned long a5,
unsigned long a6, unsigned long a7,
struct arm_smccc_res *res);
/* SMCCC 버전 확인 */
#define ARM_SMCCC_VERSION_1_0 0x10000
#define ARM_SMCCC_VERSION_1_1 0x10001
#define ARM_SMCCC_VERSION_1_2 0x10002
SMCCC 1.2+의 SVE 레지스터: SMCCC 1.2부터 SMC/HVC 호출 시 SVE(Scalable Vector Extension) 레지스터의 보존 규칙이 명시되었습니다. Fast Call은 SVE 레지스터를 보존하지 않으므로 호출자가 저장해야 합니다.
TF-A (Trusted Firmware-A)
TF-A(구 ATF, ARM Trusted Firmware)는 ARM 레퍼런스 Secure Monitor 구현체로, EL3에서 실행됩니다. 시스템 부팅 체인의 Root of Trust를 설정하고, 런타임에 SMC 디스패치와 PSCI(Power State Coordination Interface) 서비스를 제공합니다.
부팅 단계 (Boot Loader Stages)
| 단계 | 실행 레벨 | 역할 | 다음 단계 로딩 |
|---|---|---|---|
| BL1 | EL3 | AP Trusted ROM — 최초 부팅 코드, BL2 인증/로딩 | BL2 |
| BL2 | S-EL1 (또는 EL3) | Trusted Boot Firmware — BL31/BL32/BL33 로딩, 이미지 인증 | BL31, BL32, BL33 |
| BL31 | EL3 | EL3 Runtime Firmware — Secure Monitor, SMC 디스패치, PSCI | 상주 런타임 |
| BL32 | S-EL1 | Secure-EL1 Payload — OP-TEE 또는 다른 TEE OS | 상주 런타임 |
| BL33 | EL2/EL1 | Non-secure Firmware — U-Boot 또는 UEFI | Linux 커널 |
TF-A SMC 디스패치
/* TF-A: services/std_svc/std_svc_setup.c */
static uintptr_t std_svc_smc_handler(
uint32_t smc_fid, /* SMC Function ID */
u_register_t x1, u_register_t x2,
u_register_t x3, u_register_t x4,
void *cookie, void *handle, u_register_t flags)
{
/* PSCI 서비스 (0x84000000-0xBFFF_FFFF) */
if (is_psci_fid(smc_fid))
return psci_smc_handler(smc_fid, x1, x2, x3, x4,
cookie, handle, flags);
/* OP-TEE/TEE OS로 전달 (Yielding Call) */
if (is_tee_fid(smc_fid))
return opteed_smc_handler(smc_fid, x1, x2, x3, x4,
cookie, handle, flags);
SMC_RET1(handle, SMC_UNK); /* 미지원 서비스 */
}
PSCI (Power State Coordination Interface)
PSCI는 TF-A가 제공하는 표준 전원 관리 인터페이스로, Linux 커널이 CPU 온/오프, 시스템 리셋, 서스펜드를 수행할 때 사용합니다.
/* include/linux/psci.h — 주요 PSCI 함수 */
#define PSCI_0_2_FN_CPU_ON 0x84000003
#define PSCI_0_2_FN_CPU_OFF 0x84000002
#define PSCI_0_2_FN_SYSTEM_RESET 0x84000009
#define PSCI_0_2_FN_SYSTEM_OFF 0x84000008
#define PSCI_1_0_FN_SYSTEM_SUSPEND 0xC400000E
/* 커널에서 PSCI 호출 예시 */
static int psci_cpu_on(unsigned long cpuid, unsigned long entry_point)
{
return invoke_psci_fn(PSCI_0_2_FN_CPU_ON, cpuid, entry_point, 0);
}
TF-A Secure Boot Chain
TF-A는 Trusted Board Boot (TBB) 사양에 따라 각 부팅 단계의 이미지를 암호학적으로 검증합니다. Chain of Trust의 Root은 칩 OTP(One-Time Programmable) 퓨즈에 저장된 ROTPK(Root of Trust Public Key) 해시(Hash)입니다.
/* TF-A: drivers/auth/mbedtls/mbedtls_x509_parser.c */
/* 인증서 체인 검증 구조 */
/*
* ROTPK (OTP 퓨즈)
* └─ Trusted Key Certificate
* ├─ SoC Firmware Key Cert → BL31 Content Cert → BL31 이미지
* ├─ Trusted OS Key Cert → BL32 Content Cert → BL32 (OP-TEE)
* └─ Non-Trusted FW Key → BL33 Content Cert → BL33 (U-Boot)
*/
/* 이미지 인증 호출 */
int auth_mod_verify_img(
unsigned int img_id,
void *img_ptr,
unsigned int img_len)
{
const auth_img_desc_t *img_desc;
img_desc = FCONF_GET_PROPERTY(tbbr, cot, img_id);
/* 1. 부모 인증서 검증 */
if (img_desc->parent)
auth_mod_verify_img(img_desc->parent->img_id, ...);
/* 2. 현재 이미지의 해시/서명 검증 */
return auth_hash(img_desc->img_auth_methods, img_ptr, img_len);
}
TF-A 런타임 서비스 등록
/* TF-A: 런타임 서비스 매크로로 등록 */
DECLARE_RT_SVC(
opteed_fast, /* 서비스 이름 */
OEN_TOS_START, /* 시작 OEN (Owning Entity Number) */
OEN_TOS_END, /* 종료 OEN */
SMC_TYPE_FAST, /* Fast Call 타입 */
opteed_setup, /* 초기화 함수 */
opteed_smc_handler /* SMC 핸들러 */
);
/* OP-TEE SPD(Secure Payload Dispatcher) 핸들러 */
static uintptr_t opteed_smc_handler(
uint32_t smc_fid, u_register_t x1,
u_register_t x2, u_register_t x3,
u_register_t x4, void *cookie,
void *handle, u_register_t flags)
{
switch (smc_fid) {
case OPTEE_SMC_CALL_WITH_ARG:
/* Normal→Secure: OP-TEE에 요청 전달 */
cm_el1_sysregs_context_save(NON_SECURE);
cm_el1_sysregs_context_restore(SECURE);
cm_set_next_eret_context(SECURE);
SMC_RET4(handle, x1, x2, x3, x4);
break;
case OPTEE_SMC_RETURN_FROM_RPC:
/* OP-TEE→Normal: RPC 결과 반환 */
cm_el1_sysregs_context_save(SECURE);
cm_el1_sysregs_context_restore(NON_SECURE);
cm_set_next_eret_context(NON_SECURE);
SMC_RET4(handle, x1, x2, x3, x4);
break;
}
SMC_RET1(handle, SMC_UNK);
}
FF-A (Firmware Framework for Arm)
FF-A는 TF-A의 차세대 메시징 프레임워크로, Secure Partition Manager(SPM)를 통해 여러 Secure Partition(SP)을 관리합니다. OP-TEE 3.x+에서는 SMC ABI 대신 FF-A ABI를 선택적으로 사용할 수 있습니다.
| 특성 | 기존 SMC ABI | FF-A ABI |
|---|---|---|
| 메시징 | 직접 SMC 레지스터 전달 | FFA_MSG_SEND_DIRECT_REQ/RESP |
| 메모리 공유 | OPTEE_MSG_CMD_REGISTER_SHM | FFA_MEM_SHARE / FFA_MEM_LEND |
| SP 발견 | OP-TEE 전용 UUID 매칭 | FFA_PARTITION_INFO_GET |
| 인터럽트 | FIQ 직접 라우팅 | Secure/Non-secure 가상 인터럽트 |
| S-EL2 | 미지원 | SPMC at S-EL2 (v8.4+) |
| 커널 드라이버 | optee/smc_abi.c | optee/ffa_abi.c |
메모리 보호 (TZASC/TZC-400)
TrustZone의 메모리 격리(Isolation)는 TZASC(TrustZone Address Space Controller)가 담당합니다. TZASC는 DRAM 컨트롤러 앞에 위치하여 각 메모리 영역에 대한 Secure/Non-secure 접근 권한을 하드웨어 수준에서 강제합니다.
TZC-400 리전 설정
/* TF-A: drivers/arm/tzc/tzc400.c */
void tzc400_configure_region(
unsigned int filters,
unsigned int region,
unsigned long long region_base,
unsigned long long region_top,
unsigned int sec_attr, /* TZC_REGION_S_RDWR 등 */
unsigned int nsaid_permissions)
{
/* Base/Top 주소 설정 */
mmio_write_32(base + REGION_BASE_LOW_OFF, (uint32_t)region_base);
mmio_write_32(base + REGION_TOP_LOW_OFF, (uint32_t)region_top);
/* 보안 속성: Secure R/W, Non-secure 접근 거부 */
mmio_write_32(base + REGION_ATTRIBUTES_OFF,
sec_attr | filters << REGION_ATTRIBUTES_FILTER_BIT_SHIFT);
/* NSAID(Non-Secure Access ID) 퍼미션 */
mmio_write_32(base + REGION_ID_ACCESS_OFF, nsaid_permissions);
}
TZC-400 vs TZC-380: TZC-400은 최대 9개 리전과 4개 필터를 지원하며, 리전 크기가 4KB 정렬이면 됩니다. 구형 TZC-380은 리전 크기가 2의 거듭제곱이어야 하는 제약이 있습니다. 최신 SoC는 대부분 TZC-400을 사용합니다.
GIC 보안 확장
GIC(Generic Interrupt Controller)의 보안 확장은 인터럽트를 Group 0과 Group 1으로 분류하여, Secure World와 Normal World 간 인터럽트 격리를 보장합니다.
| 그룹 | 시그널 | 타겟 | 용도 | 레지스터 |
|---|---|---|---|---|
| Group 0 | FIQ | EL3 → S-EL1 | Secure Timer, Secure Watchdog, TrustZone 전용 | GICD_IGROUPR bit=0 |
| Group 1 Secure | FIQ (S-EL1) | S-EL1 | OP-TEE 내부 인터럽트 | GICD_IGRPMODR bit=1 |
| Group 1 NS | IRQ | EL1 | 일반 디바이스 인터럽트 | GICD_IGROUPR bit=1 |
Normal World 실행 중 Secure 인터럽트 발생
Normal World에서 Linux 커널이 실행 중일 때 Secure 인터럽트(Group 0 FIQ)가 발생하면, 프로세서는 즉시 EL3로 트랩됩니다. EL3 Monitor는 Normal World 컨텍스트를 저장하고, OP-TEE의 인터럽트 핸들러(Handler)에 제어를 넘깁니다.
/* OP-TEE: core/arch/arm/kernel/thread_a64.S */
/* Secure World에서 FIQ 발생 시 진입점 */
thread_fiq_handler:
/* 현재 스레드 컨텍스트 저장 */
sub sp, sp, #THREAD_ABT_REGS_SIZE
store_xregs sp, THREAD_ABT_REGS_X0, 0, 3
/* GIC에서 인터럽트 번호 읽기 */
mrs x0, ICC_IAR0_EL1 /* Group 0 인터럽트 ACK */
/* 핸들러 디스패치 */
bl itr_core_handler
/* EOI */
msr ICC_EOIR0_EL1, x0
GIC 보안 설정 코드
/* TF-A: drivers/arm/gic/v3/gicv3_main.c */
/* 인터럽트를 Group 0 (Secure)로 설정 */
void gicv3_set_interrupt_type(
unsigned int id,
unsigned int type) /* GICV3_G0 or GICV3_G1S or GICV3_G1NS */
{
/* GICD_IGROUPR 비트 설정 */
if (type == GICV3_G0) {
gicd_clr_igroupr(base, id); /* Group bit = 0 */
gicd_clr_igrpmodr(base, id); /* Modifier bit = 0 → Group 0 */
} else if (type == GICV3_G1S) {
gicd_clr_igroupr(base, id); /* Group bit = 0 */
gicd_set_igrpmodr(base, id); /* Modifier bit = 1 → Group 1 Secure */
} else {
gicd_set_igroupr(base, id); /* Group bit = 1 → Group 1 NS */
}
}
/* OP-TEE에서 Secure Timer 인터럽트 등록 */
/* Secure Physical Timer (PPI 29)를 Group 0으로 설정 */
gicv3_set_interrupt_type(29, GICV3_G0);
gicd_set_isenabler(base, 29);
Secure World 실행 중 Normal World 인터럽트
OP-TEE가 TA를 실행하는 동안 Normal World 인터럽트(Group 1 NS IRQ)가 발생하면, OP-TEE는 현재 작업을 일시 중단하고 EL3를 경유하여 Normal World로 복귀합니다. Linux 커널이 인터럽트를 처리한 후, 다시 SMC로 OP-TEE의 중단된 작업을 재개합니다. 이것이 Yielding Call이 선점 가능한 이유입니다.
/* OP-TEE: Normal World 인터럽트 발생 시 처리 */
/* core/arch/arm/kernel/thread_a64.S */
thread_nintr_handler:
/* 1. 현재 TA 스레드 상태를 SUSPENDED로 변경 */
thread_save_state
/* 2. EL3에 RPC 반환: "NW 인터럽트 처리 필요" */
smc #0 /* OPTEE_SMC_RETURN_RPC_FOREIGN_INTR */
/* 3. (NW가 인터럽트 처리 후 다시 SMC로 복귀) */
/* 4. TA 스레드 상태를 ACTIVE로 복원하고 계속 실행 */
thread_resume_state
커널 TEE 프레임워크
Linux 커널의 TEE 프레임워크(drivers/tee/)는 다양한 TEE 구현체(OP-TEE, AMD-TEE, Qualcomm QTEE 등)에 대한 통일된 사용자 공간 인터페이스를 제공합니다.
주요 데이터 구조체(Struct)
/* include/linux/tee_drv.h */
struct tee_device {
char name[32];
const struct tee_desc *desc;
int id;
struct device dev;
struct cdev cdev;
struct tee_shm_pool *pool; /* 공유 메모리 풀 */
};
struct tee_driver_ops {
int (*get_version)(struct tee_device *, struct tee_ioctl_version_data *);
int (*open)(struct tee_context *ctx);
void (*release)(struct tee_context *ctx);
int (*open_session)(struct tee_context *ctx,
struct tee_ioctl_open_session_arg *arg,
struct tee_param *param);
int (*close_session)(struct tee_context *ctx, u32 session);
int (*invoke_func)(struct tee_context *ctx,
struct tee_ioctl_invoke_arg *arg,
struct tee_param *param);
int (*cancel_req)(struct tee_context *ctx, u32 cancel_id, u32 session);
struct tee_shm *(*shm_register)(struct tee_context *ctx,
struct page **pages, size_t num_pages,
unsigned long start);
};
/* TEE 컨텍스트 — 파일 디스크립터당 하나 */
struct tee_context {
struct tee_device *teedev;
void *data; /* 드라이버 private 데이터 */
struct kref refcount;
bool releasing;
bool supp_nowait;
};
TEE ioctl 인터페이스
| ioctl | 디바이스 | 용도 |
|---|---|---|
TEE_IOC_VERSION | /dev/tee0 | TEE 구현체 버전 조회 |
TEE_IOC_OPEN_SESSION | /dev/tee0 | TA 세션 열기 (UUID 지정) |
TEE_IOC_INVOKE | /dev/tee0 | TA 함수 호출 |
TEE_IOC_CLOSE_SESSION | /dev/tee0 | TA 세션 닫기 |
TEE_IOC_SHM_ALLOC | /dev/tee0 | 공유 메모리 할당 |
TEE_IOC_SHM_REGISTER | /dev/tee0 | 기존 버퍼(Buffer)를 공유 메모리로 등록 |
TEE_IOC_SUPPL_RECV | /dev/teepriv0 | supplicant: RPC 요청 수신 |
TEE_IOC_SUPPL_SEND | /dev/teepriv0 | supplicant: RPC 응답 전송 |
OP-TEE 커널 드라이버
OP-TEE 커널 드라이버(drivers/tee/optee/)는 TEE Core 프레임워크의 백엔드로, Normal World 커널과 Secure World OP-TEE OS 사이의 통신을 담당합니다.
드라이버 파일 구조
| 파일 | 역할 |
|---|---|
core.c | 프로브(Probe)/초기화, TEE 장치 등록 |
call.c | SMC 호출 래퍼, Yielding Call 처리 |
rpc.c | RPC(Remote Procedure Call) 처리 — OP-TEE → REE 요청 |
smc_abi.c | SMC ABI 기반 메시지 포맷 |
ffa_abi.c | FF-A(Firmware Framework for Arm) ABI 기반 메시지 포맷 |
shm_pool.c | 공유 메모리 풀 관리 |
supp.c | tee-supplicant 데몬 인터페이스 |
device.c | OP-TEE 하위 장치(TA) 열거 |
notif.c | 비동기 알림 (OP-TEE 3.x+) |
tee-supplicant 데몬
tee-supplicant는 OP-TEE가 Normal World의 서비스(파일 시스템, RPMB, 소켓(Socket) 등)를 필요로 할 때 RPC를 처리하는 사용자 공간 데몬입니다. /dev/teepriv0을 통해 커널 드라이버와 통신합니다.
/* OP-TEE RPC 요청 유형 */
#define OPTEE_RPC_CMD_LOAD_TA 0 /* TA 바이너리 로딩 */
#define OPTEE_RPC_CMD_RPMB 1 /* RPMB 읽기/쓰기 */
#define OPTEE_RPC_CMD_FS 2 /* REE 파일시스템 접근 */
#define OPTEE_RPC_CMD_GET_TIME 3 /* REE 시간 조회 */
#define OPTEE_RPC_CMD_WAIT_QUEUE 4 /* 대기 큐 관리 */
#define OPTEE_RPC_CMD_SUSPEND 5 /* 스레드 중단 */
#define OPTEE_RPC_CMD_SHM_ALLOC 6 /* 공유 메모리 동적 할당 */
#define OPTEE_RPC_CMD_SHM_FREE 7 /* 공유 메모리 해제 */
#define OPTEE_RPC_CMD_NOTIFICATION 8 /* 비동기 알림 */
/* tee-supplicant RPC 루프 */
while (1) {
/* 커널에서 RPC 요청 수신 */
ioctl(fd_priv, TEE_IOC_SUPPL_RECV, &recv_arg);
switch (recv_arg.func) {
case OPTEE_RPC_CMD_LOAD_TA:
load_ta_from_filesystem(recv_arg.params);
break;
case OPTEE_RPC_CMD_FS:
handle_fs_request(recv_arg.params);
break;
case OPTEE_RPC_CMD_RPMB:
handle_rpmb_request(recv_arg.params);
break;
}
/* 결과를 커널로 전송 */
ioctl(fd_priv, TEE_IOC_SUPPL_SEND, &send_arg);
}
OP-TEE 드라이버 프로브 과정
/* drivers/tee/optee/core.c */
static int optee_probe(struct platform_device *pdev)
{
struct optee *optee;
struct tee_device *teedev, *supp_teedev;
int rc;
/* 1. SMC/HVC 호출 방식 결정 (DT의 method 속성) */
rc = optee_get_invoke_func(np);
/* 2. OP-TEE 버전/기능 쿼리 (OPTEE_SMC_CALLS_UID) */
optee_get_version(optee, &sec_caps);
/* 3. Dynamic SHM 지원 여부 확인 */
if (sec_caps & OPTEE_SMC_SEC_CAP_DYNAMIC_SHM)
optee->pool = optee_shm_pool_alloc_pages();
else
optee->pool = optee_shm_pool_alloc_carveout(pdev);
/* 4. /dev/tee0 등록 */
teedev = tee_device_alloc(&optee_clnt_desc, NULL,
optee->pool, optee);
rc = tee_device_register(teedev);
/* 5. /dev/teepriv0 등록 (supplicant 전용) */
supp_teedev = tee_device_alloc(&optee_supp_desc, NULL,
optee->pool, optee);
rc = tee_device_register(supp_teedev);
/* 6. TA 장치 열거 (PTA_DEVICE) */
optee_enumerate_devices(PTA_CMD_GET_DEVICES);
pr_info("optee: initialized driver\n");
return 0;
}
비동기 알림 (Notification)
OP-TEE 3.x+에서는 Secure World가 Normal World에 비동기 알림을 보낼 수 있습니다. 기존에는 OP-TEE가 NW에 알려야 할 이벤트가 있으면 RPC로 반환한 뒤 NW가 다시 호출해야 했지만, 알림 메커니즘으로 불필요한 왕복을 제거합니다.
/* drivers/tee/optee/notif.c */
static irqreturn_t notif_irq_handler(
int irq, void *dev_id)
{
struct optee *optee = dev_id;
/* Secure World에서 GIC를 통해 NW에 인터럽트 전달 */
/* 대기 중인 RPC 요청을 즉시 처리하도록 트리거 */
optee_notif_send(optee, OPTEE_SMC_ASYNC_NOTIF_VALUE_DO_BOTTOM_HALF);
return IRQ_HANDLED;
}
OP-TEE 공유 메모리
Normal World와 Secure World 간 데이터 교환은 공유 메모리(Shared Memory, SHM)를 통해 이루어집니다. 공유 메모리는 양 World에서 모두 접근 가능한 Non-secure 메모리 영역에 위치합니다.
SHM 할당 코드
/* drivers/tee/optee/shm_pool.c */
static struct tee_shm_pool *optee_shm_pool_alloc_pages(void)
{
/* Dynamic SHM: 일반 커널 페이지 할당 후 OP-TEE에 등록 */
struct tee_shm_pool *pool;
pool = kzalloc(sizeof(*pool), GFP_KERNEL);
pool->ops = &pool_ops_generic;
return pool;
}
/* 공유 메모리 등록: 물리 페이지 목록을 OP-TEE에 전달 */
static int optee_shm_register(
struct tee_context *ctx,
struct tee_shm *shm,
struct page **pages,
size_t num_pages,
unsigned long start)
{
/* 페이지 테이블을 MessageArg에 담아 SMC로 OP-TEE에 등록 */
struct optee_msg_arg *msg_arg;
msg_arg->cmd = OPTEE_MSG_CMD_REGISTER_SHM;
/* 물리 주소 목록 설정 */
optee_fill_pages_list(pages_data, pages, num_pages, PAGE_SIZE);
return optee_do_call_with_arg(ctx, msg_arg, 0);
}
TEE Client API
TEE Client API는 GlobalPlatform이 정의한 표준 C API로, Normal World 애플리케이션이 TEE와 상호작용하는 인터페이스입니다. OP-TEE의 libteec 라이브러리가 이를 구현합니다.
핵심 API 함수
| 함수 | 역할 | 내부 동작 |
|---|---|---|
TEEC_InitializeContext() | TEE 연결 초기화 | open(/dev/tee0) |
TEEC_OpenSession() | TA 세션 열기 | ioctl(TEE_IOC_OPEN_SESSION) → SMC → TA_OpenSessionEntryPoint |
TEEC_InvokeCommand() | TA 함수 호출 | ioctl(TEE_IOC_INVOKE) → SMC → TA_InvokeCommandEntryPoint |
TEEC_CloseSession() | TA 세션 닫기 | ioctl(TEE_IOC_CLOSE_SESSION) → SMC → TA_CloseSessionEntryPoint |
TEEC_FinalizeContext() | TEE 연결 종료 | close(fd) |
TEEC_AllocateSharedMemory() | 공유 메모리 할당 | ioctl(TEE_IOC_SHM_ALLOC) + mmap() |
TEEC_RegisterSharedMemory() | 기존 버퍼 등록 | ioctl(TEE_IOC_SHM_REGISTER) |
TEE Client API 사용 예시
#include <tee_client_api.h>
/* TA UUID: 예시 — OP-TEE os_test TA */
static const TEEC_UUID ta_uuid = {
0x5b9e0e40, 0x2636, 0x11e1,
{ 0xad, 0x9e, 0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b }
};
int main(void)
{
TEEC_Context ctx;
TEEC_Session sess;
TEEC_Operation op;
TEEC_Result res;
uint32_t err_origin;
/* 1. TEE 컨텍스트 초기화 */
res = TEEC_InitializeContext(NULL, &ctx);
if (res != TEEC_SUCCESS)
errx(1, "TEEC_InitializeContext failed: 0x%x", res);
/* 2. TA 세션 열기 */
res = TEEC_OpenSession(&ctx, &sess, &ta_uuid,
TEEC_LOGIN_PUBLIC, NULL, NULL, &err_origin);
if (res != TEEC_SUCCESS)
errx(1, "TEEC_OpenSession failed: 0x%x origin=0x%x", res, err_origin);
/* 3. 파라미터 설정 및 TA 함수 호출 */
memset(&op, 0, sizeof(op));
op.paramTypes = TEEC_PARAM_TYPES(
TEEC_VALUE_INOUT, TEEC_NONE, TEEC_NONE, TEEC_NONE);
op.params[0].value.a = 42;
res = TEEC_InvokeCommand(&sess, 0 /* cmd_id */, &op, &err_origin);
printf("TA returned: %u\n", op.params[0].value.a);
/* 4. 정리 */
TEEC_CloseSession(&sess);
TEEC_FinalizeContext(&ctx);
return 0;
}
TEEC_Operation 파라미터 타입
| 타입 | 설명 | 방향 |
|---|---|---|
TEEC_NONE | 미사용 | — |
TEEC_VALUE_INPUT | 32비트 값 2개 (a, b) | CA → TA |
TEEC_VALUE_OUTPUT | 32비트 값 2개 | TA → CA |
TEEC_VALUE_INOUT | 32비트 값 2개 | 양방향 |
TEEC_MEMREF_TEMP_INPUT | 임시 공유 메모리 | CA → TA |
TEEC_MEMREF_TEMP_OUTPUT | 임시 공유 메모리 | TA → CA |
TEEC_MEMREF_TEMP_INOUT | 임시 공유 메모리 | 양방향 |
TEEC_MEMREF_WHOLE | 등록된 SHM 전체 | SHM 플래그 |
TEEC_MEMREF_PARTIAL_* | 등록된 SHM 일부 | 오프셋(Offset) + 크기 |
TA 생명주기
Trusted Application(TA)은 OP-TEE 내에서 실행되는 보안 프로그램으로, GlobalPlatform TEE Internal Core API에 정의된 4개의 진입점 함수를 구현합니다.
TA 진입점 함수
/* GlobalPlatform TEE Internal Core API */
/* TA 로딩 시 1회 호출 — 전역 초기화 */
TEE_Result TA_CreateEntryPoint(void);
/* TA 언로딩 시 1회 호출 — 전역 정리 */
void TA_DestroyEntryPoint(void);
/* TEEC_OpenSession() 시 호출 — 세션별 초기화 */
TEE_Result TA_OpenSessionEntryPoint(
uint32_t param_types,
TEE_Param params[4],
void **sess_ctx); /* 세션 컨텍스트 out */
/* TEEC_CloseSession() 시 호출 — 세션별 정리 */
void TA_CloseSessionEntryPoint(void *sess_ctx);
/* TEEC_InvokeCommand() 시 호출 — 실제 비즈니스 로직 */
TEE_Result TA_InvokeCommandEntryPoint(
void *sess_ctx,
uint32_t cmd_id, /* 명령 식별자 */
uint32_t param_types,
TEE_Param params[4]);
TA 유형
| 유형 | 설명 | 저장 위치 | 로딩 방식 |
|---|---|---|---|
| User TA | S-EL0에서 실행, 격리된 주소 공간(Address Space) | REE 파일시스템 (/lib/optee_armtz/) | tee-supplicant RPC로 로딩 |
| Pseudo TA | S-EL1(OP-TEE 커널 모드)에서 실행 | OP-TEE 바이너리에 내장 | 정적 링크 |
| Early TA | 부팅 초기 사용 가능한 TA | OP-TEE 바이너리에 내장 | tee-supplicant 없이 로딩 |
Single Instance vs Multi Instance: TA 매니페스트의 TA_FLAG_SINGLE_INSTANCE 플래그로 제어합니다. Single Instance TA는 시스템에 하나만 존재하며 모든 세션이 같은 인스턴스를 공유합니다. Multi Instance TA는 세션마다 별도 인스턴스를 생성합니다.
TA 로딩 상세 흐름
User TA가 처음 호출될 때의 전체 로딩 과정입니다. tee-supplicant가 Normal World 파일시스템에서 TA 바이너리를 읽어 Secure World에 전달합니다.
/* TA 로딩 흐름 (시간 순서) */
/*
* 1. CA: TEEC_OpenSession(uuid) → ioctl → 커널 optee 드라이버
* 2. 커널: SMC → EL3 → OP-TEE OS
* 3. OP-TEE: TA 캐시에 uuid 없음 → RPC_CMD_LOAD_TA 반환
* 4. 커널: tee-supplicant에 RPC 전달 (TEE_IOC_SUPPL_RECV)
* 5. tee-supplicant:
* a. /lib/optee_armtz/UUID.ta 파일 열기
* b. 파일 크기 확인 → SHM 할당 (TEE_IOC_SHM_ALLOC)
* c. TA 바이너리를 SHM에 복사
* d. 결과를 커널에 반환 (TEE_IOC_SUPPL_SEND)
* 6. 커널: SMC로 OP-TEE에 SHM 주소 전달
* 7. OP-TEE:
* a. 서명 헤더(SHDR) 파싱
* b. RSA 서명 검증 (ROTPK → TA 공개키)
* c. (Encrypted TA라면) 복호화
* d. ELF 파싱 → S-EL0 가상 주소 공간 생성
* e. .text/.data/.bss 매핑, 스택/힙 할당
* f. TA_CreateEntryPoint() 호출
* g. TA_OpenSessionEntryPoint() 호출
* 8. 커널 → CA: 세션 ID 반환
*/
TA 플래그 상세
| 플래그 | 값 | 설명 |
|---|---|---|
TA_FLAG_SINGLE_INSTANCE | 0x00000001 | 시스템에 인스턴스 1개만 존재 |
TA_FLAG_MULTI_SESSION | 0x00000002 | 여러 세션이 같은 인스턴스 공유 |
TA_FLAG_INSTANCE_KEEP_ALIVE | 0x00000004 | 마지막 세션 종료 후에도 인스턴스 유지 |
TA_FLAG_SECURE_DATA_PATH | 0x00000008 | SDP(Secure Data Path) 버퍼 접근 허용 |
TA_FLAG_CACHE_MAINTENANCE | 0x00000010 | 캐시(Cache) 유지보수 syscall 허용 |
TA간 통신 (TA-to-TA)
OP-TEE에서는 한 TA가 다른 TA의 세션을 열어 함수를 호출할 수 있습니다. 이를 통해 공통 기능(암호 서비스, 키 관리 등)을 별도 TA로 모듈화할 수 있습니다.
/* TA 내부에서 다른 TA 호출 */
TEE_Result call_crypto_ta(void *data, size_t len)
{
TEE_TASessionHandle sess;
TEE_Param params[4];
uint32_t param_types;
TEE_Result res;
uint32_t ret_orig;
/* 다른 TA의 세션 열기 */
static const TEE_UUID crypto_uuid = { ... };
res = TEE_OpenTASession(&crypto_uuid,
TEE_TIMEOUT_INFINITE,
0, NULL, &sess, &ret_orig);
if (res != TEE_SUCCESS)
return res;
/* 다른 TA의 함수 호출 */
param_types = TEE_PARAM_TYPES(
TEE_PARAM_TYPE_MEMREF_INOUT,
TEE_PARAM_TYPE_NONE,
TEE_PARAM_TYPE_NONE,
TEE_PARAM_TYPE_NONE);
params[0].memref.buffer = data;
params[0].memref.size = len;
res = TEE_InvokeTACommand(sess, TEE_TIMEOUT_INFINITE,
0 /* cmd */, param_types,
params, &ret_orig);
TEE_CloseTASession(sess);
return res;
}
TA 개발 가이드
OP-TEE TA 개발은 OP-TEE SDK(ta_dev_kit)를 사용합니다. TA는 고유 UUID로 식별되며, 서명된 바이너리로 배포됩니다.
TA 프로젝트 구조
my_ta/
├── Makefile # TA 빌드 규칙
├── include/
│ └── my_ta.h # CA/TA 공유 헤더 (cmd ID, UUID)
├── sub.mk # OP-TEE 빌드 시스템 통합
├── user_ta_header_defines.h # TA 속성 (UUID, 스택 크기 등)
└── my_ta.c # TA 소스 코드
TA 헤더 정의
/* user_ta_header_defines.h */
#ifndef USER_TA_HEADER_DEFINES_H
#define USER_TA_HEADER_DEFINES_H
#include "my_ta.h"
#define TA_UUID MY_TA_UUID
#define TA_FLAGS (TA_FLAG_SINGLE_INSTANCE | \
TA_FLAG_MULTI_SESSION)
#define TA_STACK_SIZE (2 * 1024) /* 2KB */
#define TA_DATA_SIZE (32 * 1024) /* 32KB */
#define TA_CURRENT_TA_EXT_PROPERTIES \
{ "gp.ta.description", \
USER_TA_PROP_TYPE_STRING, "My Example TA" }, \
{ "gp.ta.version", \
USER_TA_PROP_TYPE_U32, &(const uint32_t){ 0x0010 } }
#endif
TA 빌드 및 서명
# OP-TEE SDK로 TA 빌드
export TA_DEV_KIT_DIR=/path/to/optee_os/out/arm-plat-vexpress/export-ta_arm64
make CROSS_COMPILE=aarch64-linux-gnu- \
PLATFORM=vexpress-qemu_armv8a
# 빌드 결과: UUID.ta (서명된 TA 바이너리)
# 배포: /lib/optee_armtz/UUID.ta 에 복사
ls -la *.ta
# -rw-r--r-- 1 root root 15360 my_ta_uuid.ta
# TA 서명 검증
# OP-TEE는 부팅 시 로딩된 공개키로 TA 서명을 검증
# sign_encrypt.py: TA_SIGN_KEY로 서명 (기본: keys/default_ta.pem)
python3 scripts/sign_encrypt.py \
--key keys/default_ta.pem \
--uuid $UUID \
--ta-version 0 \
--in my_ta.stripped.elf \
--out $UUID.ta
프로덕션 TA 서명: OP-TEE 기본 키(keys/default_ta.pem)는 개발용입니다. 프로덕션 환경에서는 반드시 자체 RSA 2048/4096 키 쌍을 생성하고, 공개키를 OP-TEE OS 빌드에 포함시키며, 개인키는 HSM(Hardware Security Module)에서 관리해야 합니다.
Secure Storage
OP-TEE의 Secure Storage는 TA가 암호화된 데이터를 영구적으로 저장할 수 있는 기능을 제공합니다. 두 가지 백엔드를 지원합니다.
키 체인 구조
| 키 | 파생 방식 | 보호 대상 |
|---|---|---|
| HUK (Hardware Unique Key) | SoC 퓨즈 / TRNG에서 유도 | SSK 파생의 루트 |
| SSK (Secure Storage Key) | HMAC(HUK, "SECURE_STORAGE" + chip_id) | TSK 파생 |
| TSK (TA Storage Key) | HMAC(SSK, TA_UUID) | FEK 암호화 |
| FEK (File Encryption Key) | PRNG (파일 생성 시) | 파일 데이터 암호화 (AES-GCM) |
Secure Storage API 사용 예시
/* TA 내부: Secure Storage에 데이터 저장 */
TEE_Result save_secret(void *data, size_t len)
{
TEE_ObjectHandle obj;
TEE_Result res;
/* 영구 객체 생성 (또는 열기) */
res = TEE_CreatePersistentObject(
TEE_STORAGE_PRIVATE, /* 스토리지 ID */
"my_secret", 9, /* 객체 ID */
TEE_DATA_FLAG_ACCESS_WRITE |
TEE_DATA_FLAG_OVERWRITE,
TEE_HANDLE_NULL, /* 속성 없음 */
data, len, /* 초기 데이터 */
&obj);
if (res == TEE_SUCCESS)
TEE_CloseObject(obj);
return res;
}
/* TA 내부: Secure Storage에서 데이터 읽기 */
TEE_Result load_secret(void *buf, size_t *len)
{
TEE_ObjectHandle obj;
TEE_Result res;
res = TEE_OpenPersistentObject(
TEE_STORAGE_PRIVATE,
"my_secret", 9,
TEE_DATA_FLAG_ACCESS_READ,
&obj);
if (res == TEE_SUCCESS) {
res = TEE_ReadObjectData(obj, buf, *len, len);
TEE_CloseObject(obj);
}
return res;
}
fTPM (Firmware TPM)
fTPM(firmware TPM)은 OP-TEE TA로 구현된 TPM 2.0 에뮬레이션입니다. 별도의 TPM 칩 없이도 TPM 기능(Measured Boot, 키 저장, 원격 증명 등)을 제공합니다. Microsoft가 최초 구현했으며(ms-tpm-20-ref), OP-TEE에 공식 통합되어 있습니다. 커널에서는 tpm_ftpm_tee.c 드라이버가 TEE 프레임워크를 통해 Secure World의 fTPM TA와 통신합니다.
fTPM: fTPM 아키텍처(소프트웨어 스택, 커널 드라이버 내부 구현), dTPM/fTPM/sTPM 보안 경계 비교, 성능 특성, 인증 가능성, fTPM 취약점(Vulnerability) 이력 등의 상세 내용은 TPM 2.0 문서를 참조하세요.
GlobalPlatform 규격
GlobalPlatform(GP)은 TEE 기술의 표준화 단체로, TEE의 아키텍처, API, 보안 요구사항을 규격화합니다. OP-TEE는 GP TEE 규격의 오픈소스 레퍼런스 구현체입니다.
주요 GP TEE 규격
| 규격 | 버전 | 대상 | 주요 내용 |
|---|---|---|---|
| TEE Client API | 1.0 | Normal World 앱 | TEEC_* 함수, 세션/컨텍스트/파라미터 관리 |
| TEE Internal Core API | 1.3.1 | TA 개발 | 암호, 스토리지, 시간, 수학, 속성 관리 |
| TEE System Architecture | 1.3 | TEE OS 구현 | TA 관리, 메모리, 보안 모델, 통신 |
| TEE Protection Profile | 1.3 | 보안 인증 | CC(Common Criteria) EAL 기반 보안 평가 |
| TEE TA Debug | 1.0 | 디버깅(Debugging) | Secure World 디버그 인터페이스 |
TEE Internal Core API 주요 카테고리
| 카테고리 | 주요 함수 | 용도 |
|---|---|---|
| 암호 연산 | TEE_AllocateOperation, TEE_AEInit, TEE_AEEncryptFinal | AES, RSA, ECC, HMAC, SHA |
| 영구 스토리지 | TEE_CreatePersistentObject, TEE_ReadObjectData | 파일 암호화 저장/조회 |
| 임시 객체 | TEE_AllocateTransientObject, TEE_GenerateKey | 키 생성, 속성 관리 |
| 시간 | TEE_GetSystemTime, TEE_GetREETime | Secure/REE 타이머(Timer) 접근 |
| 메모리 | TEE_Malloc, TEE_Realloc, TEE_MemMove | TA 힙 관리 |
| 산술 | TEE_BigIntInit, TEE_BigIntMul, TEE_BigIntMod | 빅넘 연산 |
GP 인증 (Compliance)
GlobalPlatform은 TEE 구현체의 보안 수준을 평가하기 위한 Protection Profile (PP)과 적합성 테스트 스위트를 제공합니다.
| 인증 레벨 | 보안 수준 | 평가 기준 | 대상 |
|---|---|---|---|
| GP TEE PP | CC EAL2+ | 기본 보안 요구사항 | 일반 TEE 구현 |
| GP TEE PP (augmented) | CC EAL4+ | 강화된 취약성 분석 | 금융/결제 TEE |
| SESIP Level 3 | Security Evaluation Standard for IoT Platforms | IoT 보안 평가 | IoT TEE |
GP API 확장: PKCS#11 TA
OP-TEE는 GlobalPlatform API 위에 PKCS#11 TA를 제공하여 표준 암호 토큰 인터페이스를 지원합니다. OpenSSL, p11-kit 등 기존 PKCS#11 소비자가 OP-TEE의 Secure Storage에 키를 저장하고 암호 연산을 수행할 수 있습니다.
# PKCS#11 TA를 통한 키 생성 (pkcs11-tool)
pkcs11-tool --module /usr/lib/libckteec.so \
--keypairgen --key-type rsa:2048 \
--label "my-rsa-key" --id 01
# 생성된 키로 서명
pkcs11-tool --module /usr/lib/libckteec.so \
--sign --mechanism SHA256-RSA-PKCS \
--id 01 --input-file data.bin \
--output-file sig.bin
# OpenSSL PKCS#11 엔진 통합
openssl req -engine pkcs11 \
-keyform engine \
-key "pkcs11:token=OP-TEE;id=%01;type=private" \
-new -x509 -sha256 \
-out cert.pem
OP-TEE 내부 구현
OP-TEE OS는 마이크로커널 스타일의 TEE 운영체제로, S-EL1에서 실행됩니다. User TA는 S-EL0에서 격리된 가상 주소 공간을 가지며, OP-TEE 커널이 시스템 콜(System Call)을 통해 서비스를 제공합니다.
OP-TEE 스레드 모델
/* OP-TEE: core/kernel/thread.c */
struct thread_ctx {
struct thread_ctx_regs regs; /* 범용/시스템 레지스터 */
enum thread_state state; /* FREE, SUSPENDED, ACTIVE */
vaddr_t stack_va_end; /* 스레드 스택 끝 */
uint32_t flags; /* THREAD_FLAGS_* */
void *rpc_arg; /* 현재 RPC 인자 */
struct mutex_head mutexes; /* 보유 중인 뮤텍스 */
};
/* 스레드 수 = CFG_NUM_THREADS (기본 8) */
static struct thread_ctx threads[CFG_NUM_THREADS];
/* 스레드 할당: SMC 진입 시 free 스레드 배정 */
static int thread_alloc_and_run(
uint32_t a0, uint32_t a1,
uint32_t a2, uint32_t a3)
{
size_t n;
for (n = 0; n < CFG_NUM_THREADS; n++) {
if (threads[n].state == THREAD_STATE_FREE) {
threads[n].state = THREAD_STATE_ACTIVE;
thread_resume(&threads[n]);
return n;
}
}
return OPTEE_SMC_RETURN_ETHREAD_LIMIT; /* 스레드 부족 */
}
OP-TEE 메모리 관리(Memory Management)
| 메모리 영역 | 용도 | 보호 수준 |
|---|---|---|
| TEE RAM | OP-TEE 코드, 데이터, BSS, 힙 | Secure only (TZC-400) |
| TA RAM | User TA 코드, 데이터, 스택, 힙 | Secure only, TA별 격리 (MMU) |
| SHM | NW/SW 공유 데이터 | Non-secure (양쪽 접근 가능) |
| MEM_AREA_IO | Secure 주변장치 MMIO | Secure only, 비캐시 |
OP-TEE Pager
Secure RAM이 제한된 환경(수 백 KB)에서 OP-TEE는 자체 페이징 메커니즘으로 코드와 읽기 전용(Read-Only) 데이터를 Non-secure 메모리에 암호화하여 캐싱합니다.
/* OP-TEE: core/mm/pager.c */
/* 페이지 폴트 핸들러: Secure RAM 풀에서 빈 페이지 확보 */
static bool pager_handle_abort(
struct abort_info *ai)
{
struct pager_area *area;
struct page *page;
/* 폴트 주소에 해당하는 pager area 검색 */
area = find_pager_area(ai->va);
if (!area)
return false;
/* LRU 정책으로 희생 페이지 선택 */
page = TAILQ_LAST(&pager_page_list);
/* 1. 희생 페이지를 AES-GCM으로 암호화하여 NS 메모리에 저장 */
pager_save_page(page);
/* 2. 요청 페이지를 NS 메모리에서 읽어와 복호화/무결성 검증 */
pager_restore_page(area, page);
/* 3. MMU 매핑 갱신 */
core_mmu_set_entry(&area->tbl_info, page->pa,
TEE_MATTR_VALID_BLOCK | area->flags);
return true;
}
OP-TEE 시스템 콜
User TA(S-EL0)는 SVC 명령으로 OP-TEE 커널(S-EL1)의 서비스를 호출합니다. 주요 시스템 콜 번호는 다음과 같습니다.
| 시스콜 번호 | 함수 | 용도 |
|---|---|---|
| 0 | syscall_return | TA 진입점 반환 |
| 1 | syscall_log | Secure 로그 출력 |
| 2 | syscall_panic | TA 패닉 (즉시 종료) |
| 5 | syscall_get_property | TA/Client 속성 조회 |
| 10 | syscall_open_ta_session | 다른 TA 세션 열기 (TA→TA) |
| 12 | syscall_invoke_ta_command | 다른 TA 함수 호출 |
| 25 | syscall_cryp_obj_alloc | 암호 객체 할당 |
| 30 | syscall_cryp_state_alloc | 암호 연산 상태 할당 |
| 33 | syscall_cipher_init | 대칭 암호 초기화 |
| 34 | syscall_cipher_update | 대칭 암호 업데이트 |
| 40 | syscall_storage_obj_create | Secure Storage 객체 생성 |
| 44 | syscall_storage_obj_read | Secure Storage 읽기 |
OP-TEE 암호 라이브러리
OP-TEE는 자체 암호 라이브러리로 LibTomCrypt(소프트웨어)와 ARMv8 Crypto Extension(하드웨어) 두 백엔드를 지원합니다. 빌드 시 CFG_CRYPTO_DRIVER로 하드웨어 가속을 선택할 수 있습니다.
/* OP-TEE: core/crypto/aes-gcm.c */
/* ARMv8 CE 가속 AES-GCM */
TEE_Result crypto_aes_gcm_enc_final(
void *ctx,
const uint8_t *src, size_t len,
uint8_t *dst,
uint8_t *tag, size_t *tag_len)
{
struct aes_gcm_ctx *c = ctx;
/* CE 가속: AESE/AESMC/AESD/AESIMC 명령 사용 */
ce_aes_gcm_encrypt(c->key, c->rounds,
src, dst, len,
c->ctr, c->hash_subkey);
/* GHASH 계산: PMULL/PMULL2 명령 */
ce_gcm_ghash(c->hash_subkey, c->ghash, dst, len);
/* 태그 생성 */
ce_aes_gcm_final(c, tag, tag_len);
return TEE_SUCCESS;
}
Crypto Extension 성능 향상: ARMv8 CE를 활용하면 AES-256-GCM 처리량(Throughput)이 소프트웨어 대비 5~10배 향상됩니다. Cortex-A53에서 CE 미사용 시 ~45 MB/s, CE 사용 시 ~310 MB/s 수준입니다. 대부분의 최신 ARM SoC는 CE를 지원합니다.
OP-TEE 보안 기능
OP-TEE는 Secure World 내에서도 다층 보안 메커니즘을 적용하여 TA 간 격리와 코드 무결성(Integrity)을 보장합니다.
보안 메커니즘 요약
| 메커니즘 | 설명 | 설정 |
|---|---|---|
| TA 서명 검증(Signature Verification) | TA 로딩 시 RSA 2048 서명 검증, 무결성 확인 | TA_SIGN_KEY |
| TA 주소 공간 격리 | 각 User TA는 독립 가상 주소 공간 (S-EL0 MMU) | 기본 활성 |
| ASLR | TA 로드 주소 랜덤화 (CFG_TA_ASLR) | CFG_TA_ASLR=y |
| 스택 카나리(Stack Canary) | 스택 오버플로(Stack Overflow) 탐지 | CFG_WITH_STACK_CANARIES=y |
| TA Paging 암호화 | 페이지 아웃된 코드/데이터를 AES-GCM으로 암호화 | CFG_WITH_PAGER=y |
| TA 암호화 (Encrypted TA) | TA 바이너리 자체를 암호화하여 배포 | CFG_ENCRYPT_TA=y |
| PAN (Privileged Access Never) | S-EL1이 S-EL0 메모리를 의도치 않게 접근 방지 | 하드웨어 지원 시 자동 |
| 메모리 태깅 (MTE) | ARMv8.5+ MTE를 활용한 메모리 안전성 | CFG_MEMTAG=y |
TA 서명 검증 흐름
/* OP-TEE: core/kernel/signed_hdr.c */
TEE_Result shdr_verify_signature(
const struct shdr *shdr)
{
struct rsa_public_key key;
TEE_Result res;
/* 1. OP-TEE에 내장된 TA 서명 공개키 로딩 */
res = crypto_acipher_alloc_rsa_public_key(&key, 2048);
/* 2. 해시 검증: SHA-256(TA 코드 + 데이터) */
res = crypto_hash_final(hash_ctx, digest, TEE_SHA256_HASH_SIZE);
/* 3. RSA PKCS#1 v1.5 서명 검증 */
res = crypto_acipher_rsassa_verify(
TEE_ALG_RSASSA_PKCS1_V1_5_SHA256,
&key, -1, digest, TEE_SHA256_HASH_SIZE,
sig, sig_len);
if (res != TEE_SUCCESS)
EMSG("TA signature verification failed!");
return res;
}
TA 주소 공간 격리 상세
각 User TA는 S-EL0에서 독립된 가상 주소 공간을 할당받습니다. OP-TEE 커널(S-EL1)은 TA 전환 시 TTBR0_EL1을 교체하여 TA 간 메모리 격리를 보장합니다.
/* OP-TEE: core/arch/arm/mm/core_mmu.c */
/* TA 전환 시 MMU 컨텍스트 교체 */
void core_mmu_set_user_map(struct core_mmu_user_map *map)
{
if (map) {
/* 새 TA의 페이지 테이블로 교체 */
write_ttbr0_el1(map->user_map);
write_contextidr_el1(map->asid);
} else {
/* TA 없음: TTBR0 무효화 */
write_ttbr0_el1(0);
}
isb();
/* ASID 기반 TLB 무효화 */
tlbi_asid(map ? map->asid : 0);
dsb();
isb();
}
/* TA 메모리 영역 보호 속성 */
/* .text → RX (실행 가능, 쓰기 불가) */
/* .rodata → RO (읽기 전용) */
/* .data/.bss → RW (읽기/쓰기, 실행 불가) */
/* 스택 → RW + 가드 페이지 (스택 오버플로 탐지) */
/* 힙 → RW (TEE_Malloc으로 동적 할당) */
Encrypted TA (암호화된 TA)
OP-TEE 3.7+에서는 CFG_ENCRYPT_TA=y로 TA 바이너리 자체를 암호화하여 배포할 수 있습니다. 이는 TA 코드의 리버스 엔지니어링을 방지하며, REE 파일시스템에 저장된 TA가 평문으로 노출되는 것을 막습니다.
/* OP-TEE Encrypted TA 구조 */
/*
* +------------------+
* | SHDR (서명 헤더) | — RSA 서명, 해시 알고리즘, 이미지 크기
* +------------------+
* | EHD (암호 헤더) | — 암호화 알고리즘, IV/nonce, 태그
* +------------------+
* | 암호화된 TA 코드 | — AES-GCM(TA_ENC_KEY, IV, plaintext TA)
* +------------------+
*
* 로딩 순서:
* 1. SHDR 서명 검증 (공개키)
* 2. EHD에서 IV/태그 추출
* 3. TA_ENC_KEY로 복호화 + GCM 태그 검증
* 4. 복호화된 ELF를 S-EL0 주소 공간에 매핑
*/
TrustZone 공격 벡터와 방어
| 공격 벡터 | 설명 | 방어 메커니즘 |
|---|---|---|
| Cold Boot Attack | 전원 차단 후 DRAM 잔류 데이터 읽기 | 키를 레지스터에만 유지, DRAM 스크럽, MTE |
| DMA 공격 | 악성 DMA 마스터가 Secure 메모리 접근 | TZASC/SMMU, IOMMU 기반 DMA 보호 |
| 사이드 채널 | 캐시 타이밍, 전력 분석으로 키 추출 | 상수 시간 암호 구현, 캐시 플러시(Flush), ASLR |
| RoP/JoP | TA 취약점을 통한 코드 재사용 공격 | PAN, BTI(Branch Target Identification), ASLR |
| Rollback Attack | Secure Storage 파일을 이전 버전으로 교체 | RPMB Write Counter, Anti-Rollback 버전 |
| Fault Injection | 전압/클럭 글리칭으로 서명 검증 우회 | 이중 검증, 중복 검사, 하드웨어 센서 |
| Debug Port 접근 | JTAG/SWD로 Secure 상태 접근 | Secure JTAG 비활성화 (퓨즈), DBGEN=0 |
프로덕션 보안 체크리스트: (1) TA 서명키를 프로덕션 키로 교체, (2) JTAG/SWD를 퓨즈로 비활성화, (3) RPMB 키 프로비저닝, (4) OP-TEE 디버그 로그 비활성화(CFG_TEE_CORE_LOG_LEVEL=0), (5) Encrypted TA 활성화, (6) ASLR 활성화, (7) anti-rollback 버전 관리. 자세한 커널 보안 기법은 커널 하드닝 문서를 참고하세요.
QEMU 실습
OP-TEE는 QEMU(ARM virt/vexpress) 환경에서 전체 스택을 실습할 수 있습니다. OP-TEE 프로젝트의 manifest 레포지토리가 repo를 통한 전체 빌드를 지원합니다.
빌드 및 실행 절차
# 1. repo 도구 설치 및 매니페스트 초기화
mkdir -p optee-qemu && cd optee-qemu
repo init -u https://github.com/OP-TEE/manifest.git \
-m qemu_v8.xml -b 4.1.0
repo sync -j$(nproc)
# 2. 전체 빌드 (TF-A + OP-TEE + U-Boot + Linux + Buildroot)
cd build
make -j$(nproc) toolchains
make -j$(nproc) all
# 3. QEMU 실행
make run
# 별도 터미널에서 Normal World 콘솔 연결
# nc localhost 54320
# 4. OP-TEE xtest (GP 적합성 테스트) 실행
# Normal World 콘솔에서:
xtest
# 5. 예제 TA 실행
optee_example_hello_world
optee_example_secure_storage
optee_example_aes
QEMU GDB 디버깅
# OP-TEE Secure World 디버깅
make run-only QEMU_VIRTFS_AUTOMOUNT=y \
GDBSERVER=y
# 별도 터미널에서 GDB 연결
aarch64-linux-gnu-gdb \
-ex "target remote :1234" \
-ex "symbol-file optee_os/out/arm/core/tee.elf" \
-ex "break thread_std_smc_entry"
# TA 디버깅 (TA ELF 심볼 로딩)
# OP-TEE 로그에서 TA 로드 주소 확인 후:
add-symbol-file path/to/ta.elf 0x40010000
SoC별 구현
주요 SoC 벤더들은 TrustZone 위에 자체 TEE 솔루션을 구축합니다. 각 구현체의 특성과 차이점을 비교합니다.
SoC별 TEE 커널 드라이버 비교
| SoC 벤더 | TEE 구현 | 커널 드라이버 | SMC/통신 방식 | GP 호환 |
|---|---|---|---|---|
| Qualcomm | QSEE/QTEE | drivers/firmware/qcom_scm.c | SCM(Secure Channel Manager) SMC | 부분적 |
| Samsung | TEEGRIS | drivers/misc/tzdev/ (삼성 BSP) | SMC + 공유 메모리 | 완전 |
| MediaTek | Trustonic/OP-TEE | drivers/tee/ 표준 + MTK 확장 | SMC | 완전 |
| NXP i.MX | OP-TEE | drivers/tee/optee/ 표준 | SMC | 완전 |
| STM32MP | OP-TEE | drivers/tee/optee/ 표준 | SMC | 완전 |
| TI AM6x | OP-TEE | drivers/tee/optee/ 표준 | SMC | 완전 |
| Renesas R-Car | OP-TEE | drivers/tee/optee/ 표준 | SMC | 완전 |
| Broadcom | 독자 TEE | BSP 전용 | Mailbox | 부분적 |
Qualcomm SCM(Secure Channel Manager) 인터페이스
Qualcomm SoC에서는 OP-TEE 대신 자체 TEE(QSEE/QTEE)를 사용하며, qcom_scm 드라이버가 SMC 호출을 래핑합니다.
/* drivers/firmware/qcom_scm.c */
int qcom_scm_call(
struct device *dev,
const struct qcom_scm_desc *desc,
struct qcom_scm_res *res)
{
/* Qualcomm SCM은 두 가지 호출 규약을 지원 */
if (likely(__qcom_scm_is_call_available(dev, desc->svc, desc->cmd)))
return qcom_scm_call_smccc(dev, desc, res, false);
else
return qcom_scm_call_legacy(dev, desc, res);
}
/* Qualcomm SCM 서비스 예시 */
#define QCOM_SCM_SVC_PIL 0x02 /* Peripheral Image Loader */
#define QCOM_SCM_SVC_IO 0x05 /* IO 접근 보호 */
#define QCOM_SCM_SVC_INFO 0x06 /* SoC 정보 */
#define QCOM_SCM_SVC_HDCP 0x11 /* HDCP DRM */
#define QCOM_SCM_SVC_SMMU 0x15 /* SMMU 보안 설정 */
OP-TEE 지원 SoC 목록: OP-TEE는 NXP i.MX 6/7/8/9, STM32MP1/2, TI AM62x/64x/65x, Renesas R-Car, Marvell ARMADA, Broadcom BCM2711(Raspberry Pi 4), QEMU virt, Hisilicon, Rockchip 등 30개 이상의 플랫폼을 공식 지원합니다. 전체 목록은 optee_os/core/arch/arm/plat-*/ 디렉토리에서 확인할 수 있습니다.
TrustZone vs CCA vs SEV/TDX 비교
하드웨어 기반 격리 기술은 ARM TrustZone, ARM CCA(Confidential Computing Architecture), AMD SEV, Intel TDX 등 여러 방식이 있습니다. 각 기술의 아키텍처와 보호 범위를 비교합니다.
| 특성 | ARM TrustZone | ARM CCA (Realm) | AMD SEV(-SNP) | Intel TDX |
|---|---|---|---|---|
| 보호 모델 | 2-World (S/NS) | 4-World (Root/Realm/Secure/NS) | VM 메모리 암호화 | TD(Trust Domain) 격리 |
| 격리 단위 | Secure World (단일) | Realm VM (다수) | VM | TD (VM) |
| 하이퍼바이저(Hypervisor) 신뢰 | Normal World만 | 불신 (RMM이 중재) | 불신 | 불신 |
| 메모리 암호화 | 물리적 분리 (TZASC) | GPT 기반 접근 제어 | AES-128 (키 per VM) | AES-128/256 (키 per TD) |
| 원격 증명 | 제한적 (TEE 의존) | CCA 증명 서비스 | SEV-SNP Attestation | TDX Attestation |
| 대상 | 모바일/임베디드 | 서버/클라우드 | 서버/클라우드 | 서버/클라우드 |
| 아키텍처 | ARMv6K+ | ARMv9.2+ | AMD EPYC | Intel Xeon (Sapphire Rapids+) |
| Linux 지원 | drivers/tee/optee/ | drivers/virt/coco/ | arch/x86/coco/sev/ | arch/x86/coco/tdx/ |
TrustZone과 CCA의 관계: ARMv9 CCA에서 TrustZone은 사라지지 않습니다. TrustZone(Secure World)은 펌웨어와 TEE용으로 유지되고, Realm World가 기밀 VM용으로 추가됩니다. 자세한 내용은 기밀 컴퓨팅 문서를 참고하세요.
성능 분석
TrustZone/TEE 호출은 World 전환 오버헤드(Overhead)를 수반합니다. 이 오버헤드를 이해하고 최적화하는 것이 중요합니다.
SMC 호출 오버헤드
| 구간 | 소요 시간 (대략) | 주요 비용 |
|---|---|---|
| SMC 명령 실행 | ~50 ns | EL1→EL3 트랩 |
| 컨텍스트 저장/복원 | ~500 ns - 2 us | GP/시스템/FP 레지스터, TLB/캐시 |
| OP-TEE 스레드 할당 | ~200 ns | free 스레드 검색 |
| TA 세션 조회 | ~100 ns | UUID→세션 매핑(Mapping) |
| 왕복 합계 (빈 TA 호출) | ~5-20 us | 플랫폼 의존 |
| AES-256-GCM 1KB | ~30-50 us | TA 호출 + 암호 연산 + SHM 복사 |
| RSA-2048 서명 | ~2-5 ms | 연산 집약적 |
성능 최적화 기법
| 기법 | 설명 | 효과 |
|---|---|---|
| 배치 호출 | 여러 명령을 하나의 SMC로 묶어 호출 | 왕복 횟수 감소 |
| 세션 재사용 | TA 세션을 열어둔 채 반복 호출 | OpenSession 오버헤드 제거 |
| Dynamic SHM | 대량 데이터 시 메모리 복사 회피 | 데이터 전송 지연(Latency) 감소 |
| Pseudo TA | S-EL1에서 직접 실행 (S-EL0 전환 없음) | 컨텍스트 스위치 제거 |
| CE 가속 | ARMv8 Crypto Extension 활용 | AES/SHA 10-50x 속도향상 |
| 비동기 호출 | Yielding Call로 NW에 CPU 양보 | NW 지연 최소화 |
벤치마크 측정 예시
# xtest 벤치마크 모드
xtest --aes-perf
xtest --sha-perf
# 결과 예시 (Cortex-A53 1.2GHz)
# AES-256-CBC encrypt: 45.2 MB/s (SW) → 312.8 MB/s (CE hw)
# SHA-256 hash: 78.5 MB/s (SW) → 425.1 MB/s (CE hw)
# AES-256-GCM encrypt: 38.7 MB/s (SW) → 287.3 MB/s (CE hw)
# SMC 왕복 레이턴시 측정
xtest 1001 # 빈 TA 호출 왕복 시간
World 전환 비용 분해
SMC 왕복의 비용을 구간별로 분해하면 다음과 같습니다. 최적화 타겟을 식별하는 데 유용합니다.
| 단계 | Cortex-A53 (cycles) | Cortex-A72 (cycles) | 비고 |
|---|---|---|---|
| SMC 트랩 (EL1→EL3) | ~30 | ~25 | 프로세서 파이프라인(Pipeline) 플러시 |
| EL3 핸들러 진입 | ~50 | ~40 | 벡터 테이블 점프 |
| NW 컨텍스트 저장 | ~200 | ~180 | GP + 시스템 레지스터 31개+ |
| SW 컨텍스트 복원 | ~200 | ~180 | GP + 시스템 레지스터 |
| FP/SIMD 레지스터 | ~150 | ~120 | v0-v31 (선택적, lazy) |
| TLB 무효화(Invalidation) | ~100 | ~80 | ASID 기반 최소화 가능 |
| ERET (EL3→S-EL1) | ~20 | ~15 | 예외 복귀 |
| OP-TEE 스레드 디스패치 | ~100 | ~80 | 스레드 상태 확인 |
| 단방향 합계 | ~850 | ~720 | |
| 왕복 합계 | ~1,700 | ~1,440 | 빈 호출 기준 |
OP-TEE ftrace 지원
# OP-TEE 내부 함수 트레이싱 (CFG_FTRACE_SUPPORT=y 빌드 시)
# Normal World에서 ftrace 덤프 트리거:
echo 1 > /proc/optee/ftrace_dump
# OP-TEE 시리얼 로그에 ftrace 출력:
# Thread 0 (0x00000001)
# entry_std_smc_handler 1234 ns
# tee_ta_open_session 567 ns
# ta_load 234 ns
# check_open_params 45 ns
# tee_ta_invoke_command 890 ns
# TA_InvokeCommand 678 ns
캐시 영향 최소화: World 전환 시 L1 캐시를 플러시해야 할 수 있는데(사이드 채널 방어), 이는 큰 성능 비용을 초래합니다. CFG_CORE_UNMAP_CORE_AT_EL0=y로 S-EL0에서 커널 매핑을 제거하면 Spectre 방어가 되지만, TA↔커널 전환 비용이 증가합니다. 성능과 보안 사이의 트레이드오프를 고려하세요.
커널 설정
Linux 커널에서 TEE/OP-TEE를 사용하기 위한 Kconfig 옵션입니다.
필수 설정
# TEE 프레임워크 기본
CONFIG_TEE=y # TEE 서브시스템 활성화
CONFIG_OPTEE=y # OP-TEE 드라이버
# SMCCC 지원
CONFIG_HAVE_ARM_SMCCC=y # ARM SMC Calling Convention
CONFIG_ARM_SMCCC_SOC_ID=y # SoC ID 쿼리 via SMCCC
# fTPM (선택)
CONFIG_TCG_TPM=y # TPM 서브시스템
CONFIG_TCG_FTPM_TEE=y # fTPM via TEE 드라이버
# FF-A (ARMv8.4+ Secure Partition, 선택)
CONFIG_ARM_FFA_TRANSPORT=y # FF-A 메시징
# RPMB 지원 (Secure Storage 백엔드)
CONFIG_MMC=y
CONFIG_MMC_BLOCK=y # eMMC RPMB 접근
# PSCI (전원 관리, TF-A 연동)
CONFIG_ARM_PSCI_FW=y # PSCI 펌웨어 인터페이스
디버그 설정
# TEE 디버그 로그
CONFIG_TEE_DEBUG=y # TEE 프레임워크 디버그 메시지
# 커널 로그에서 확인
dmesg | grep -i optee
# [ 1.234567] optee: probing for conduit method.
# [ 1.234589] optee: revision 3.19 (abcdef01)
# [ 1.234601] optee: dynamic shared memory is enabled
# [ 1.234615] optee: initialized driver
OP-TEE 빌드 시 주요 CFG 옵션
| 옵션 | 기본값 | 설명 |
|---|---|---|
CFG_NUM_THREADS | 8 | 동시 TA 실행 스레드 수 |
CFG_TA_ASLR | y | TA 로드 주소 랜덤화 |
CFG_TA_DYNLINK | y | TA 동적 링킹 (공유 라이브러리(Shared Library)) |
CFG_ENCRYPT_TA | n | TA 바이너리 암호화 |
CFG_WITH_PAGER | n | Secure RAM 페이징 |
CFG_WITH_STACK_CANARIES | y | 스택 카나리 보호 |
CFG_CORE_ASLR | y | OP-TEE 커널 ASLR |
CFG_RPMB_FS | n | RPMB Secure Storage 백엔드 |
CFG_REE_FS | y | REE 파일시스템 Secure Storage 백엔드 |
CFG_CRYPTO_WITH_CE | 플랫폼 의존 | ARMv8 Crypto Extension 활용 |
CFG_TEE_CORE_LOG_LEVEL | 1 | 로그 레벨 (0=없음, 4=상세) |
CFG_FTRACE_SUPPORT | n | 내부 함수 트레이싱 |
CFG_CORE_UNMAP_CORE_AT_EL0 | y | S-EL0에서 커널 매핑 제거 (Spectre 방어) |
CFG_MEMTAG | n | ARMv8.5 MTE 메모리 태깅 |
CFG_PKCS11_TA | y | PKCS#11 TA 포함 |
트러블슈팅
일반적인 문제와 해결
| 증상 | 원인 | 해결 |
|---|---|---|
/dev/tee0 미생성 | OP-TEE 드라이버 미로딩 또는 DT 누락 | CONFIG_OPTEE=y, DT에 linaro,optee-tz 노드 확인 |
SMC 호출 시 SMC_UNK 반환 | EL3 Monitor 미설정 또는 서비스 미등록 | TF-A BL31 설정, OPTEE_SPD=opteed 빌드 옵션 확인 |
TEEC_ERROR_COMMUNICATION | Secure World 크래시 또는 스레드 부족 | OP-TEE 시리얼 로그 확인, CFG_NUM_THREADS 증가 |
TA 로딩 실패 (ITEM_NOT_FOUND) | TA 파일 경로 오류 또는 tee-supplicant 미실행 | /lib/optee_armtz/UUID.ta 확인, tee-supplicant & 실행 |
| TA 서명 검증 실패 | TA 서명키와 OP-TEE 공개키 불일치 | 동일 키 쌍으로 TA 서명 / OP-TEE 빌드 |
| RPMB 접근 오류 | eMMC RPMB 키 미프로비저닝 | mmc rpmb key write로 RPMB 인증키 설정 |
| SHM 할당 실패 | 공유 메모리 풀 소진 | Static SHM 크기 확장 또는 Dynamic SHM 전환 |
| Normal World에서 Secure 메모리 접근 크래시 | TZASC 보호 위반 | TZC-400 리전 설정 확인, DT reserved-memory 확인 |
fTPM /dev/tpm0 미생성 | fTPM TA 미설치 또는 드라이버 미설정 | CONFIG_TCG_FTPM_TEE=y, TA 설치 확인 |
디버깅 명령
# OP-TEE 드라이버 상태 확인
cat /sys/bus/tee/devices/optee/version
cat /sys/bus/tee/devices/optee/pool_size
# TEE 장치 목록
ls -la /dev/tee*
# crw-rw---- 1 root tee 253, 0 /dev/tee0
# crw-rw---- 1 root tee 253, 16 /dev/teepriv0
# tee-supplicant 프로세스 확인
ps aux | grep tee-supplicant
# OP-TEE Secure World 로그 (QEMU)
# 시리얼 콘솔 또는:
cat /sys/kernel/debug/optee/log # (빌드 옵션에 따라)
# SMC 호출 카운터 (ftrace)
echo 1 > /sys/kernel/debug/tracing/events/arm_smccc/enable
cat /sys/kernel/debug/tracing/trace | grep smc
# fTPM 상태
tpm2_getcap properties-fixed
tpm2_pcrread sha256
OP-TEE 에러 코드 참조
| 에러 코드 | 값 | 의미 | 일반적 원인 |
|---|---|---|---|
TEEC_SUCCESS | 0x00000000 | 성공 | — |
TEEC_ERROR_GENERIC | 0xFFFF0000 | 일반 오류 | TA 내부 로직 오류 |
TEEC_ERROR_ACCESS_DENIED | 0xFFFF0001 | 접근 거부 | 잘못된 로그인 방식, 권한 부족 |
TEEC_ERROR_CANCEL | 0xFFFF0002 | 작업 취소 | TEEC_RequestCancellation() 호출 |
TEEC_ERROR_BAD_PARAMETERS | 0xFFFF0006 | 잘못된 파라미터 | param_types 불일치, NULL 포인터 |
TEEC_ERROR_ITEM_NOT_FOUND | 0xFFFF0008 | 항목 미발견 | TA 파일 없음, Secure Storage 객체 없음 |
TEEC_ERROR_NOT_SUPPORTED | 0xFFFF000A | 미지원 | TA가 해당 cmd_id 미구현 |
TEEC_ERROR_COMMUNICATION | 0xFFFF000E | 통신 오류 | SW 크래시, 스레드 부족, SHM 오류 |
TEEC_ERROR_SECURITY | 0xFFFF000F | 보안 오류 | TA 서명 실패, 무결성 위반 |
TEEC_ERROR_SHORT_BUFFER | 0xFFFF0010 | 버퍼 부족 | 출력 버퍼 크기 부족 |
TEEC_ERROR_OUT_OF_MEMORY | 0xFFFF000C | 메모리 부족 | TA 힙/스택 소진, SHM 풀 소진 |
TEEC_ERROR_TARGET_DEAD | 0xFFFF3024 | TA 사망 | TA 패닉, 메모리 접근 위반 |
Origin 코드
TEEC_OpenSession()과 TEEC_InvokeCommand()는 에러 발생 위치를 returnOrigin 파라미터로 반환합니다.
| Origin | 값 | 의미 |
|---|---|---|
TEEC_ORIGIN_API | 0x1 | TEE Client API 라이브러리 내부 오류 |
TEEC_ORIGIN_COMMS | 0x2 | 커널 드라이버/SMC 통신 오류 |
TEEC_ORIGIN_TEE | 0x3 | OP-TEE OS 내부 오류 (TA 관리자 등) |
TEEC_ORIGIN_TRUSTED_APP | 0x4 | TA 코드에서 반환한 오류 |
Device Tree 바인딩
OP-TEE 드라이버는 Device Tree를 통해 Secure World의 존재와 통신 방식을 확인합니다.
Device Tree 예시
/* OP-TEE Device Tree 바인딩 */
/ {
/* Secure 메모리 예약 */
reserved-memory {
#address-cells = <2>;
#size-cells = <2>;
ranges;
/* OP-TEE OS 코어 메모리 (Secure, no-map) */
optee_core: optee-core@0e100000 {
reg = <0x0 0x0e100000 0x0 0x01f00000>;
no-map;
};
/* Static 공유 메모리 (Dynamic SHM 미사용 시) */
optee_shm: optee-shmem@10000000 {
compatible = "restricted-dma-pool";
reg = <0x0 0x10000000 0x0 0x00200000>;
no-map;
};
};
/* OP-TEE 펌웨어 노드 */
firmware {
optee {
compatible = "linaro,optee-tz";
method = "smc"; /* "smc" 또는 "hvc" */
};
};
/* PSCI (TF-A 전원 관리) */
psci {
compatible = "arm,psci-1.0";
method = "smc";
};
};
DT 바인딩 속성 상세
| 속성 | 필수 | 값 | 설명 |
|---|---|---|---|
compatible | 필수 | "linaro,optee-tz" | OP-TEE 드라이버 매칭 |
method | 필수 | "smc" 또는 "hvc" | Secure World 진입 방식 |
reg (reserved-memory) | Static SHM 시 | 물리 주소 + 크기 | 공유 메모리 영역 |
no-map | 권장 | (flag) | 커널 선형 매핑에서 제외 |
Dynamic SHM에서는 optee_shm reserved-memory 노드가 불필요합니다. OP-TEE 3.1+에서 Dynamic SHM이 기본이며, 커널이 일반 페이지를 할당하여 OP-TEE에 등록합니다. Device Tree 관련 상세 내용은 Device Tree 문서를 참고하세요.
SoC별 DT 예시
/* NXP i.MX8MM — OP-TEE DT 바인딩 */
/ {
firmware {
optee: optee {
compatible = "linaro,optee-tz";
method = "smc";
};
};
reserved-memory {
#address-cells = <2>;
#size-cells = <2>;
ranges;
/* i.MX8MM: OP-TEE 메모리 (Secure DDR) */
optee@0x56000000 {
reg = <0 0x56000000 0 0x02000000>;
no-map;
};
};
};
/* STM32MP157 — OP-TEE DT 바인딩 */
/ {
firmware {
optee {
compatible = "linaro,optee-tz";
method = "smc";
};
};
reserved-memory {
/* STM32MP1: 보안 메모리는 TZC-400으로 보호 */
optee@de000000 {
reg = <0xde000000 0x02000000>;
no-map;
};
};
};
커널 DT 매칭 코드
/* drivers/tee/optee/core.c */
static const struct of_device_id optee_dt_match[] = {
{ .compatible = "linaro,optee-tz" },
{},
};
MODULE_DEVICE_TABLE(of, optee_dt_match);
static struct platform_driver optee_driver = {
.probe = optee_probe,
.remove = optee_remove,
.driver = {
.name = "optee",
.of_match_table = optee_dt_match,
},
};
관련 문서
외부 참고 자료
- OP-TEE Documentation — OP-TEE 공식 문서 사이트입니다
- OP-TEE GitHub Organization — OP-TEE 소스 코드 저장소 모음입니다
- ARM TrustZone Technology Overview — ARM 공식 TrustZone 기술 개요 문서입니다
- ARM SMC Calling Convention (SMCCC) — Secure Monitor Call 호출 규약 사양입니다
- GlobalPlatform TEE Specifications — TEE Internal/Client API 등 GlobalPlatform 표준 사양입니다
- TEE Subsystem — Kernel Documentation — 커널 TEE 서브시스템 공식 문서입니다
- Linux 커널 drivers/tee/ 소스 코드 — TEE 드라이버 커널 소스입니다
- Trusted Firmware — OP-TEE — Trusted Firmware 프로젝트 내 OP-TEE 페이지입니다
- ARM Trusted Firmware Design — ARM Trusted Firmware(ATF) BL31/BL32 설계 문서입니다
- OP-TEE Examples — OP-TEE TA 예제 코드 및 빌드 가이드입니다