QUESTION
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 호출 예시다.
STRUCTURE
CPU별 보안 상태와 복귀 context
현재 CPU의 slot
cm_get_context(SECURE)Secure slot → cpu_context_tcm_get_context(NON_SECURE)Normal World slot → 별도 cpu_context_tENABLE_RME && IMAGE_BL31 분기에서 초기화선택된 cpu_context_t
el3_state_tSCR·SPSR·ELR 등 복귀 정보el1_sysregs_t와 gp_regs_tEL1 시스템 상태와 범용 레지스터 framecm_set_next_eret_context(state)선택한 pointer를 다음 exit context로 지정pointer 선택과 register 복원은 다른 작업이다. 각 slot이 보유한 상태와 next ERET pointer를 함께 확인한다.
CALL PATH
EL3 복귀 준비와 OPTEED의 SMC 전환을 구분한다
cm_prepare_el3_exit(state)
cm_get_context(state); NULL assert!CTX_INCLUDE_EL2_REGS || IMAGE_BL1cpu_context_t * 저장OPTEED runtime SMC 예시
CALL_DONESecure EL1 save → NS EL1 restore → NS context로 반환FIQ_DONESecure save 생략; OP-TEE가 해당 상태를 보존해야 함두 열은 단일 필수 호출열이 아니다. cm_prepare_el3_exit()가 모든 outgoing state를 직접 저장한다는 뜻도 아니다.
STATE LEDGER
실제 입력·결과·조건
| 경계 | 입력 | 결과 | 조건 |
|---|---|---|---|
| outgoing save | 현재 world context | cm_el1_sysregs_context_save(state) | 해당 호출자에서 필요할 때 |
| target 조회 | security_state | 현재 CPU의 cpu_context_t * | NULL이면 assertion |
| Non-secure EL2 | EL2 구현·SCR_HCE_BIT | 사용/미사용 EL2 준비 | target이 Non-secure일 때만 |
| EL1 restore | target EL1 sysregs | cm_el1_sysregs_context_restore(state) | !CTX_INCLUDE_EL2_REGS || IMAGE_BL1 |
| next ERET | target context | cm_set_next_eret_context(state) | cm_prepare_el3_exit의 마지막 단계 |
첫 줄은 호출자별 선택 동작이고, 뒤 네 줄은 cm_prepare_el3_exit()의 분기다. 범용 레지스터까지 이 C 함수가 저장한다고 해석하지 않는다.
VISUAL WALKTHROUGH
OPTEED 예시와 world별 context
cm_get_context(NON_SECURE)cm_el1_sysregs_context_saveSMC_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.
Non-secure slot
SCR_EL3.NS=1NS target 복귀 상태Secure slot
SCR_EL3.NS=0Secure target 복귀 상태FP/SVE·timer 확장 상태가 언제 저장되는지는 설정과 별도 구현을 확인해야 한다. 여기서 항상 자동 복원된다고 단정하지 않는다.
UPSTREAM SOURCE
원본 코드
아래 코드는 Trusted Firmware-A LTS 2.14.3의 lib/el3_runtime/aarch64/context_mgmt.c에서 1126-1214줄을 그대로 가져온 것이다. 설명을 위해 실제 코드를 가짜 의사 코드로 바꾸지 않았다.
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
READING CONTRACT
발췌 범위와 읽기 기준
원문 1126–1214행 발췌는 앞 함수의 끝과 cm_prepare_el3_exit()의 앞부분을 함께 담는다. 함수 전체는 1150–1228행이고, 발췌 뒤에 EL1 restore 빌드 조건과 next ERET context 지정이 이어진다. 아래 관련 원본의 cm_el1_sysregs_context_save/restore()는 별도 함수다.
RELATED SOURCE
함께 읽어야 하는 원본 코드
첫 코드 조각만으로 동작이 완성되지 않는 경우 호출 매크로, 객체 정의와 실제 실행 목록을 같은 페이지에 묶었다. 각 조각은 같은 기준 commit에서 가져왔다.
01. outgoing EL1 system register context 저장
lib/el3_runtime/aarch64/context_mgmt.c 1954-2034줄이다.
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줄이다.
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
DETAILS
저장, 준비, pointer 선택의 경계
outgoing save는 호출자별로 결정한다
cm_el1_sysregs_context_save(state)는 현재 EL1 register를 state별 context에 기록한다(1964–1980행). OPTEED는 Normal caller SMC와 Secure CALL_DONE에서 이를 부르지만 FIQ_DONE에서는 생략한다.
exit 준비는 target·빌드 조건에 의존한다
cm_prepare_el3_exit()의 Non-secure EL2 분기(1157–1222행), EL1 restore 전처리 조건(1223–1226행), next context 지정(1227행)을 구분한다.
pointer 선택은 register 복원이 아니다
cm_set_next_eret_context()는 state별 cpu_context_t *를 얻어 cm_set_next_context(ctx)에 건넨다(2093–2101행). 이 호출만으로 모든 EL1 register가 복원됐다고 말할 수 없다.
IMPLEMENTATION NOTES
조건부 경로를 지우지 않고 읽기
Non-secure EL2의 두 갈래
EL2가 구현되고 SCR_HCE_BIT가 켜졌다면 SCTLR_EL2 초기값을 기록한다. EL2가 구현됐지만 쓰지 않는다면 init_nonsecure_el2_unused(ctx)로 간다(1157–1212행).
feature와 빌드 옵션
FEAT_HCX·FGT·FGWTE3 작업은 CPU feature 조건부다. EL1 restore도 #if !CTX_INCLUDE_EL2_REGS || IMAGE_BL1 조건 아래 있다. 어느 빌드에서나 같은 호출열이라고 그리지 않는다.
OP-TEE dispatcher와 공통 API
OPTEED의 runtime SMC handler는 요청 종류별로 save, vector 선택, restore, next context 지정, 반환값 배치를 한다. OP-TEE 최초 init return과 PSCI secondary hook은 별도 시점이다.
DEEP ANALYSIS
소스가 보장하는 분기
| 조건 | 고정 원문의 처리 | 구분할 설명 |
|---|---|---|
security_state == NON_SECURE | EL2 구현·HCE·feature 검사 | Secure target에도 같은 EL2 절차를 적용하지 않음 |
!CTX_INCLUDE_EL2_REGS || IMAGE_BL1 | 이 함수 본문에서 EL1 restore | 모든 빌드가 같은 C 호출열은 아님 |
cm_el1_sysregs_context_save/restore | state별 EL1 register 묶음을 다룸 | GPR·FP/SVE·timer까지 한꺼번에 보장하지 않음 |
cm_set_next_eret_context | target pointer를 next context로 지정 | target pointer 선택 단계 |
OBJECT LIFETIME
context pointer와 register 묶음
| 대상 | 설정·소비 | 주의점 |
|---|---|---|
| 현재 CPU의 security-state slot | cm_set_context로 연결, cm_get_context로 조회 | 다른 CPU/state slot과 혼동하지 않음 |
cpu_context_t | EL3 state·EL1 sysregs·GPR frame 보관 | 이 함수에서 동적 생성·해제하지 않음 |
el3_state_t | SCR·ELR·SPSR 등 복귀 정보 | target state와 일치해야 함 |
el1_sysregs_t | state별 save/restore | 빌드 옵션에 따른 restore 위치 확인 |
FAILURE PATH
잘못된 복귀를 좁히는 경계
| 증상 | 먼저 확인할 조건 | 고정 소스 |
|---|---|---|
| 다른 world의 register로 복귀 | 현재 CPU slot과 target state | context_mgmt.c 1153–1155, 2093–2100 |
| NS EL2에서 예외 | EL2 구현과 SCR_HCE_BIT | context_mgmt.c 1157–1212 |
| EL1 복원 누락 의심 | CTX_INCLUDE_EL2_REGS/IMAGE_BL1 | context_mgmt.c 1223–1227 |
| OP-TEE SMC 뒤 NS 복귀 불량 | CALL_DONE과 FIQ_DONE 차이 | opteed_main.c 805–842 |
각 증상을 target slot 조회, 조건부 복원, 반환 경계에서 따로 판정한다.
EVIDENCE
빌드와 실행에서 확인할 자료
아래 명령과 계측 항목은 해당 보드의 빌드 결과가 있을 때 사용하는 확인 방법이다. 이 글의 코드 분석을 보드에서 재현한 실행 결과로 해석해서는 안 된다.
| 자료 | 예시 | 판정 |
|---|---|---|
| 고정 원문 | context_mgmt.c 1150–1228 | EL2/EL1 분기와 next context 대조 |
| BL31 ELF symbol | ${CROSS_COMPILE}nm -n build/<platform>/debug/bl31/bl31.elf | grep cm_prepare_el3_exit | C 선언 문자열이 아닌 symbol 이름 검색 |
| 허용된 디버그 계측 | CPU index, state, target pointer, ELR/SPSR | 올바른 CPU/state slot 선택 확인 |
LAB
조건을 분리해 대조하기
- 01같은 CPU의 두 slot
허용된 디버그 환경에서
SECURE와NON_SECUREcontext pointer와 target 선택을 비교한다. - 02EL2 사용 여부
빌드 옵션·target EL·
SCR_HCE_BIT를 기록해 HCE 사용과 unused-EL2 경로를 구분한다. - 03OPTEED SMC 분기
Normal 요청,
CALL_DONE,FIQ_DONE에서 save/restore 호출 차이를 고정 원문과 대조한다.
PRIMARY REFERENCES