Trusted Firmware-A · source analysis

Secure/Non-secure CPU context 전환

SCR_EL3, SPSR, EL1/EL2 시스템 레지스터, 범용 레지스터를 저장·복원해 보안 영역과 비보안 영역 사이를 전환하는 과정을 살펴봅니다.

표의 버전은 소스 분석 기준이며 최신 보안 버전 권고가 아닙니다. TF-A LTS 2.14.3 이후에는 보안 수정이 있습니다. 제품 적용 시 공식 변경 기록과 사용 플랫폼의 errata를 확인하세요.

Series
07 / 12
Baseline
LTS 2.14.3
Commit
f5e765a3443f
Source
lib/el3_runtime/aarch64/context_mgmt.c:1126-1214 (발췌 범위)

EL3가 어느 CPU의 어느 보안 상태로 돌아갈지 어떻게 정하는가?

cpu_context_t는 현재 CPU의 security-state slot에 연결된다. 호출자가 필요할 때 outgoing EL1 system register를 저장하고 target slot을 고른다. cm_prepare_el3_exit() 자체는 outgoing 저장 함수가 아니다.

고정 LTS 2.14.3에서는 Non-secure target에만 EL2 초기화 분기가 있고 EL1 복원은 !CTX_INCLUDE_EL2_REGS || IMAGE_BL1 조건 아래에 있다. 함수 끝에서는 target context를 다음 ERET 대상으로 지정한다. OP-TEE SPD의 SMC 전환은 별도의 save/restore 호출 예시다.

cm_prepare_el3_exit 1150–1228 · OPTEED runtime 658–725

CPU별 보안 상태와 복귀 context

그림 1. 현재 CPU의 slot을 선택해 다음 ERET context를 가리킨다

현재 CPU의 slot

cm_get_context(SECURE)Secure slot → cpu_context_t
cm_get_context(NON_SECURE)Normal World slot → 별도 cpu_context_t
Realm은 조건부ENABLE_RME && IMAGE_BL31 분기에서 초기화

선택된 cpu_context_t

el3_state_tSCR·SPSR·ELR 등 복귀 정보
el1_sysregs_t와 gp_regs_tEL1 시스템 상태와 범용 레지스터 frame
cm_set_next_eret_context(state)선택한 pointer를 다음 exit context로 지정

pointer 선택과 register 복원은 다른 작업이다. 각 slot이 보유한 상태와 next ERET pointer를 함께 확인한다.

원문 2093–2101

EL3 복귀 준비와 OPTEED의 SMC 전환을 구분한다

그림 2. 호출자와 빌드 조건에 따라 갈라지는 save·restore

cm_prepare_el3_exit(state)

target slot 조회cm_get_context(state); NULL assert
Non-secure이면 EL2 분기EL2 구현·SCR.HCE·CPU feature 조건
EL1 restore는 빌드 조건부!CTX_INCLUDE_EL2_REGS || IMAGE_BL1
next ERET context 지정target cpu_context_t * 저장

OPTEED runtime SMC 예시

Normal → SecureNS EL1 save → vector 선택 → Secure EL1 restore → next 지정
CALL_DONESecure EL1 save → NS EL1 restore → NS context로 반환
FIQ_DONESecure save 생략; OP-TEE가 해당 상태를 보존해야 함

두 열은 단일 필수 호출열이 아니다. cm_prepare_el3_exit()가 모든 outgoing state를 직접 저장한다는 뜻도 아니다.

context_mgmt.c 1150–1228 · opteed_main.c 658–842

실제 입력·결과·조건

경계입력결과조건
outgoing save현재 world contextcm_el1_sysregs_context_save(state)해당 호출자에서 필요할 때
target 조회security_state현재 CPU의 cpu_context_t *NULL이면 assertion
Non-secure EL2EL2 구현·SCR_HCE_BIT사용/미사용 EL2 준비target이 Non-secure일 때만
EL1 restoretarget EL1 sysregscm_el1_sysregs_context_restore(state)!CTX_INCLUDE_EL2_REGS || IMAGE_BL1
next ERETtarget contextcm_set_next_eret_context(state)cm_prepare_el3_exit의 마지막 단계

첫 줄은 호출자별 선택 동작이고, 뒤 네 줄은 cm_prepare_el3_exit()의 분기다. 범용 레지스터까지 이 C 함수가 저장한다고 해석하지 않는다.

OPTEED 예시와 world별 context

그림 3. OPTEED가 초기화된 뒤의 Normal → Secure SMC 경로
NS handle 확인cm_get_context(NON_SECURE)
NS EL1 저장cm_el1_sysregs_context_save
vector 선택fast / yielding
Secure EL1 복원next ERET context 지정
Secure로 반환SMC_RET4

이 그림은 opteed_smc_handler()에서 OP-TEE vector table과 CPU context가 준비된 Non-secure 호출자의 정상 경로다. vector table이 없으면 오류를 반환하고, CPU 상태가 OPTEE_PSTATE_UNKNOWN이면 opteed_cpu_on_finish_handler(0)를 먼저 호출한다. BL31 최초 초기화나 모든 인터럽트의 공통 경로는 아니다. 원문 658–725.

그림 4. 같은 CPU에도 Secure와 Non-secure slot은 다르다

Non-secure slot

saved SCR_EL3.NS=1NS target 복귀 상태
NS EL1 system registers필요한 경로에서 저장·복원
EL2는 조건부구현·target EL·빌드 옵션에 따름

Secure slot

saved SCR_EL3.NS=0Secure target 복귀 상태
S-EL1 system registersOP-TEE SPD가 context를 연결·전환
범용 레지스터EL3 exception/exit frame에서 별도 취급

FP/SVE·timer 확장 상태가 언제 저장되는지는 설정과 별도 구현을 확인해야 한다. 여기서 항상 자동 복원된다고 단정하지 않는다.

원본 코드

아래 코드는 Trusted Firmware-A LTS 2.14.3의 lib/el3_runtime/aarch64/context_mgmt.c에서 1126-1214줄을 그대로 가져온 것이다. 설명을 위해 실제 코드를 가짜 의사 코드로 바꾸지 않았다.

commitf5e765a3443fe21154aa908228e75ee2600085f6anchor · L1150void cm_prepare_el3_exit(size_t security_state)
1126	write_hstr_el2(HSTR_EL2_RESET_VAL & ~(HSTR_EL2_T_MASK));
1127
1128	/*
1129	 * Initialise CNTHP_CTL_EL2. All fields are architecturally UNKNOWN on
1130	 * reset.
1131	 *
1132	 * CNTHP_CTL_EL2:ENABLE: Set to zero to disable the EL2 physical timer
1133	 * and prevent timer interrupts.
1134	 */
1135	write_cnthp_ctl_el2(CNTHP_CTL_RESET_VAL & ~(CNTHP_CTL_ENABLE_BIT));
1136
1137	manage_extensions_nonsecure_el2_unused();
1138#endif /* INIT_UNUSED_NS_EL2 */
1139}
1140
1141/*******************************************************************************
1142 * Prepare the CPU system registers for first entry into realm, secure, or
1143 * normal world.
1144 *
1145 * If execution is requested to EL2 or hyp mode, SCTLR_EL2 is initialized
1146 * If execution is requested to non-secure EL1 or svc mode, and the CPU supports
1147 * EL2 then EL2 is disabled by configuring all necessary EL2 registers.
1148 * For all entries, the EL1 registers are initialized from the cpu_context
1149 ******************************************************************************/
1150void cm_prepare_el3_exit(size_t security_state)
1151{
1152	u_register_t sctlr_el2, scr_el3;
1153	cpu_context_t *ctx = cm_get_context(security_state);
1154
1155	assert(ctx != NULL);
1156
1157	if (security_state == NON_SECURE) {
1158		uint64_t el2_implemented = el_implemented(2);
1159
1160		scr_el3 = read_ctx_reg(get_el3state_ctx(ctx),
1161						 CTX_SCR_EL3);
1162
1163		if (el2_implemented != EL_IMPL_NONE) {
1164
1165			/*
1166			 * If context is not being used for EL2, initialize
1167			 * HCRX_EL2 with its init value here.
1168			 */
1169			if (is_feat_hcx_supported()) {
1170				write_hcrx_el2(HCRX_EL2_INIT_VAL);
1171			}
1172
1173			/*
1174			 * Initialize Fine-grained trap registers introduced
1175			 * by FEAT_FGT so all traps are initially disabled when
1176			 * switching to EL2 or a lower EL, preventing undesired
1177			 * behavior.
1178			 */
1179			if (is_feat_fgt_supported()) {
1180				/*
1181				 * Initialize HFG*_EL2 registers with a default
1182				 * value so legacy systems unaware of FEAT_FGT
1183				 * do not get trapped due to their lack of
1184				 * initialization for this feature.
1185				 */
1186				write_hfgitr_el2(HFGITR_EL2_INIT_VAL);
1187				write_hfgrtr_el2(HFGRTR_EL2_INIT_VAL);
1188				write_hfgwtr_el2(HFGWTR_EL2_INIT_VAL);
1189			}
1190
1191			/* Condition to ensure EL2 is being used. */
1192			if ((scr_el3 & SCR_HCE_BIT) != 0U) {
1193				/* Initialize SCTLR_EL2 register with reset value. */
1194				sctlr_el2 = SCTLR_EL2_RES1;
1195
1196				/*
1197				 * If workaround of errata 764081 for Cortex-A75
1198				 * is used then set SCTLR_EL2.IESB to enable
1199				 * Implicit Error Synchronization Barrier.
1200				 */
1201				if (errata_a75_764081_applies()) {
1202					sctlr_el2 |= SCTLR_IESB_BIT;
1203				}
1204
1205				write_sctlr_el2(sctlr_el2);
1206			} else {
1207				/*
1208				 * (scr_el3 & SCR_HCE_BIT==0)
1209				 * EL2 implemented but unused.
1210				 */
1211				init_nonsecure_el2_unused(ctx);
1212			}
1213		}
1214

발췌 범위와 읽기 기준

원문 1126–1214행 발췌는 앞 함수의 끝과 cm_prepare_el3_exit()의 앞부분을 함께 담는다. 함수 전체는 1150–1228행이고, 발췌 뒤에 EL1 restore 빌드 조건과 next ERET context 지정이 이어진다. 아래 관련 원본의 cm_el1_sysregs_context_save/restore()는 별도 함수다.

함께 읽어야 하는 원본 코드

첫 코드 조각만으로 동작이 완성되지 않는 경우 호출 매크로, 객체 정의와 실제 실행 목록을 같은 페이지에 묶었다. 각 조각은 같은 기준 commit에서 가져왔다.

01. outgoing EL1 system register context 저장

lib/el3_runtime/aarch64/context_mgmt.c 1954-2034줄이다.

filelib/el3_runtime/aarch64/context_mgmt.canchor · L1964void cm_el1_sysregs_context_save
1954
1955	if (is_feat_ls64_accdata_supported()) {
1956		write_accdata_el1(read_el1_ctx_ls64(ctx, accdata_el1));
1957	}
1958}
1959
1960/*******************************************************************************
1961 * The next couple of functions are used by runtime services to save and restore
1962 * EL1 context on the 'cpu_context' structure for the specified security state.
1963 ******************************************************************************/
1964void cm_el1_sysregs_context_save(uint32_t security_state)
1965{
1966	cpu_context_t *ctx;
1967
1968	ctx = cm_get_context(security_state);
1969	assert(ctx != NULL);
1970
1971	el1_sysregs_context_save(get_el1_sysregs_ctx(ctx));
1972
1973#if IMAGE_BL31
1974	if (security_state == SECURE) {
1975		PUBLISH_EVENT(cm_exited_secure_world);
1976	} else {
1977		PUBLISH_EVENT(cm_exited_normal_world);
1978	}
1979#endif
1980}
1981
1982void cm_el1_sysregs_context_restore(uint32_t security_state)
1983{
1984	cpu_context_t *ctx;
1985
1986	ctx = cm_get_context(security_state);
1987	assert(ctx != NULL);
1988
1989	el1_sysregs_context_restore(get_el1_sysregs_ctx(ctx));
1990
1991#if IMAGE_BL31
1992	if (security_state == SECURE) {
1993		PUBLISH_EVENT(cm_entering_secure_world);
1994	} else {
1995		PUBLISH_EVENT(cm_entering_normal_world);
1996	}
1997#endif
1998}
1999
2000#endif /* ((IMAGE_BL1) || (IMAGE_BL31 && (!CTX_INCLUDE_EL2_REGS))) */
2001
2002/*******************************************************************************
2003 * This function populates ELR_EL3 member of 'cpu_context' pertaining to the
2004 * given security state with the given entrypoint
2005 ******************************************************************************/
2006void cm_set_elr_el3(uint32_t security_state, uintptr_t entrypoint)
2007{
2008	cpu_context_t *ctx;
2009	el3_state_t *state;
2010
2011	ctx = cm_get_context(security_state);
2012	assert(ctx != NULL);
2013
2014	/* Populate EL3 state so that ERET jumps to the correct entry */
2015	state = get_el3state_ctx(ctx);
2016	write_ctx_reg(state, CTX_ELR_EL3, entrypoint);
2017}
2018
2019/*******************************************************************************
2020 * This function populates ELR_EL3 and SPSR_EL3 members of 'cpu_context'
2021 * pertaining to the given security state
2022 ******************************************************************************/
2023void cm_set_elr_spsr_el3(uint32_t security_state,
2024			uintptr_t entrypoint, uint32_t spsr)
2025{
2026	cpu_context_t *ctx;
2027	el3_state_t *state;
2028
2029	ctx = cm_get_context(security_state);
2030	assert(ctx != NULL);
2031
2032	/* Populate EL3 state so that ERET jumps to the correct entry */
2033	state = get_el3state_ctx(ctx);
2034	write_ctx_reg(state, CTX_ELR_EL3, entrypoint);

02. target world의 EL1 system register context 복원

lib/el3_runtime/aarch64/context_mgmt.c 1972-2040줄이다.

filelib/el3_runtime/aarch64/context_mgmt.canchor · L1982void cm_el1_sysregs_context_restore
1972
1973#if IMAGE_BL31
1974	if (security_state == SECURE) {
1975		PUBLISH_EVENT(cm_exited_secure_world);
1976	} else {
1977		PUBLISH_EVENT(cm_exited_normal_world);
1978	}
1979#endif
1980}
1981
1982void cm_el1_sysregs_context_restore(uint32_t security_state)
1983{
1984	cpu_context_t *ctx;
1985
1986	ctx = cm_get_context(security_state);
1987	assert(ctx != NULL);
1988
1989	el1_sysregs_context_restore(get_el1_sysregs_ctx(ctx));
1990
1991#if IMAGE_BL31
1992	if (security_state == SECURE) {
1993		PUBLISH_EVENT(cm_entering_secure_world);
1994	} else {
1995		PUBLISH_EVENT(cm_entering_normal_world);
1996	}
1997#endif
1998}
1999
2000#endif /* ((IMAGE_BL1) || (IMAGE_BL31 && (!CTX_INCLUDE_EL2_REGS))) */
2001
2002/*******************************************************************************
2003 * This function populates ELR_EL3 member of 'cpu_context' pertaining to the
2004 * given security state with the given entrypoint
2005 ******************************************************************************/
2006void cm_set_elr_el3(uint32_t security_state, uintptr_t entrypoint)
2007{
2008	cpu_context_t *ctx;
2009	el3_state_t *state;
2010
2011	ctx = cm_get_context(security_state);
2012	assert(ctx != NULL);
2013
2014	/* Populate EL3 state so that ERET jumps to the correct entry */
2015	state = get_el3state_ctx(ctx);
2016	write_ctx_reg(state, CTX_ELR_EL3, entrypoint);
2017}
2018
2019/*******************************************************************************
2020 * This function populates ELR_EL3 and SPSR_EL3 members of 'cpu_context'
2021 * pertaining to the given security state
2022 ******************************************************************************/
2023void cm_set_elr_spsr_el3(uint32_t security_state,
2024			uintptr_t entrypoint, uint32_t spsr)
2025{
2026	cpu_context_t *ctx;
2027	el3_state_t *state;
2028
2029	ctx = cm_get_context(security_state);
2030	assert(ctx != NULL);
2031
2032	/* Populate EL3 state so that ERET jumps to the correct entry */
2033	state = get_el3state_ctx(ctx);
2034	write_ctx_reg(state, CTX_ELR_EL3, entrypoint);
2035	write_ctx_reg(state, CTX_SPSR_EL3, spsr);
2036}
2037
2038/*******************************************************************************
2039 * This function updates a single bit in the SCR_EL3 member of the 'cpu_context'
2040 * pertaining to the given security state using the value and bit position

저장, 준비, pointer 선택의 경계

01

outgoing save는 호출자별로 결정한다

cm_el1_sysregs_context_save(state)는 현재 EL1 register를 state별 context에 기록한다(1964–1980행). OPTEED는 Normal caller SMC와 Secure CALL_DONE에서 이를 부르지만 FIQ_DONE에서는 생략한다.

02

exit 준비는 target·빌드 조건에 의존한다

cm_prepare_el3_exit()의 Non-secure EL2 분기(1157–1222행), EL1 restore 전처리 조건(1223–1226행), next context 지정(1227행)을 구분한다.

03

pointer 선택은 register 복원이 아니다

cm_set_next_eret_context()는 state별 cpu_context_t *를 얻어 cm_set_next_context(ctx)에 건넨다(2093–2101행). 이 호출만으로 모든 EL1 register가 복원됐다고 말할 수 없다.

조건부 경로를 지우지 않고 읽기

01

Non-secure EL2의 두 갈래

EL2가 구현되고 SCR_HCE_BIT가 켜졌다면 SCTLR_EL2 초기값을 기록한다. EL2가 구현됐지만 쓰지 않는다면 init_nonsecure_el2_unused(ctx)로 간다(1157–1212행).

02

feature와 빌드 옵션

FEAT_HCX·FGT·FGWTE3 작업은 CPU feature 조건부다. EL1 restore도 #if !CTX_INCLUDE_EL2_REGS || IMAGE_BL1 조건 아래 있다. 어느 빌드에서나 같은 호출열이라고 그리지 않는다.

03

OP-TEE dispatcher와 공통 API

OPTEED의 runtime SMC handler는 요청 종류별로 save, vector 선택, restore, next context 지정, 반환값 배치를 한다. OP-TEE 최초 init return과 PSCI secondary hook은 별도 시점이다.

소스가 보장하는 분기

조건고정 원문의 처리구분할 설명
security_state == NON_SECUREEL2 구현·HCE·feature 검사Secure target에도 같은 EL2 절차를 적용하지 않음
!CTX_INCLUDE_EL2_REGS || IMAGE_BL1이 함수 본문에서 EL1 restore모든 빌드가 같은 C 호출열은 아님
cm_el1_sysregs_context_save/restorestate별 EL1 register 묶음을 다룸GPR·FP/SVE·timer까지 한꺼번에 보장하지 않음
cm_set_next_eret_contexttarget pointer를 next context로 지정target pointer 선택 단계

1150–1228 · 1960–2000 · 2093–2101.

context pointer와 register 묶음

대상설정·소비주의점
현재 CPU의 security-state slotcm_set_context로 연결, cm_get_context로 조회다른 CPU/state slot과 혼동하지 않음
cpu_context_tEL3 state·EL1 sysregs·GPR frame 보관이 함수에서 동적 생성·해제하지 않음
el3_state_tSCR·ELR·SPSR 등 복귀 정보target state와 일치해야 함
el1_sysregs_tstate별 save/restore빌드 옵션에 따른 restore 위치 확인

잘못된 복귀를 좁히는 경계

증상먼저 확인할 조건고정 소스
다른 world의 register로 복귀현재 CPU slot과 target statecontext_mgmt.c 1153–1155, 2093–2100
NS EL2에서 예외EL2 구현과 SCR_HCE_BITcontext_mgmt.c 1157–1212
EL1 복원 누락 의심CTX_INCLUDE_EL2_REGS/IMAGE_BL1context_mgmt.c 1223–1227
OP-TEE SMC 뒤 NS 복귀 불량CALL_DONE과 FIQ_DONE 차이opteed_main.c 805–842

각 증상을 target slot 조회, 조건부 복원, 반환 경계에서 따로 판정한다.

빌드와 실행에서 확인할 자료

아래 명령과 계측 항목은 해당 보드의 빌드 결과가 있을 때 사용하는 확인 방법이다. 이 글의 코드 분석을 보드에서 재현한 실행 결과로 해석해서는 안 된다.

자료예시판정
고정 원문context_mgmt.c 1150–1228EL2/EL1 분기와 next context 대조
BL31 ELF symbol${CROSS_COMPILE}nm -n build/<platform>/debug/bl31/bl31.elf | grep cm_prepare_el3_exitC 선언 문자열이 아닌 symbol 이름 검색
허용된 디버그 계측CPU index, state, target pointer, ELR/SPSR올바른 CPU/state slot 선택 확인

조건을 분리해 대조하기

  1. 01
    같은 CPU의 두 slot

    허용된 디버그 환경에서 SECURE와 NON_SECURE context pointer와 target 선택을 비교한다.

  2. 02
    EL2 사용 여부

    빌드 옵션·target EL·SCR_HCE_BIT를 기록해 HCE 사용과 unused-EL2 경로를 구분한다.

  3. 03
    OPTEED SMC 분기

    Normal 요청, CALL_DONE, FIQ_DONE에서 save/restore 호출 차이를 고정 원문과 대조한다.

원문과 다음 글