当kernel psci 通过smc陷到el3时的入口函数如下:
arm-trusted-firmware-master/bl31/aarch64/runtime_exceptions.S
vector_entry sync_exception_aarch64
/*
* This exception vector will be the entry point for SMCs and traps
* that are unhandled at lower ELs most commonly. SP_EL3 should point
* to a valid cpu context where the general purpose and system register
* state can be saved.
*/
handle_sync_exception
check_vector_size sync_exception_aarch64
在handle_sync_exception中判断是smc_handler64还是smc_handler32
.macro handle_sync_exception
/* Enable the SError interrupt */
msr daifclr, #DAIF_ABT_BIT
str x30, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_LR]
#if ENABLE_RUNTIME_INSTRUMENTATION
/*
* Read the timestamp value and store it in per-cpu data. The value
* will be extracted from per-cpu data by the C level SMC handler and
* saved to the PMF timestamp region.
*/
mrs x30, cntpct_el0
str x29, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X29]
mrs x29, tpidr_el3
str x30, [x29, #CPU_DATA_PMF_TS0_OFFSET]
ldr x29, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X29]
#endif
mrs x30, esr_el3
ubfx x30, x30, #ESR_EC_SHIFT, #ESR_EC_LENGTH
/* Handle SMC exceptions separately from other synchronous exceptions */
cmp x30, #EC_AARCH32_SMC
b.eq smc_handler32
cmp x30, #EC_AARCH64_SMC
b.eq smc_handler64
/* Other kinds of synchronous exceptions are not handled */
no_ret report_unhandled_exception
.endm
我们这里以smc_handler32为例
smc_handler64:
/*
* Populate the parameters for the SMC handler.
* We already have x0-x4 in place. x5 will point to a cookie (not used
* now). x6 will point to the context structure (SP_EL3) and x7 will
* contain flags we need to pass to the handler Hence save x5-x7.
*
* Note: x4 only needs to be preserved for AArch32 callers but we do it
* for AArch64 callers as well for convenience
*/
stp x4, x5, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X4]
stp x6, x7, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X6]
/* Save rest of the gpregs and sp_el0*/
save_x18_to_x29_sp_el0
mov x5, xzr
mov x6, sp
/* Get the unique owning entity number */
ubfx x16, x0, #FUNCID_OEN_SHIFT, #FUNCID_OEN_WIDTH
ubfx x15, x0, #FUNCID_TYPE_SHIFT, #FUNCID_TYPE_WIDTH
orr x16, x16, x15, lsl #FUNCID_OEN_WIDTH
//通过__RT_SVC_DESCS_START__ + RT_SVC_DESC_HANDLE 找到需要处理的函数
adr x11, (__RT_SVC_DESCS_START__ + RT_SVC_DESC_HANDLE)
/* Load descriptor index from array of indices */
adr x14, rt_svc_descs_indices
//将处理的函数指针放到w15中
ldrb w15, [x14, x16]
/*
* Restore the saved C runtime stack value which will become the new
* SP_EL0 i.e. EL3 runtime stack. It was saved in the 'cpu_context'
* structure prior to the last ERET from EL3.
*/
ldr x12, [x6, #CTX_EL3STATE_OFFSET + CTX_RUNTIME_SP]
/*
* Any index greater than 127 is invalid. Check bit 7 for
* a valid index
*/
tbnz w15, 7, smc_unknown
/* Switch to SP_EL0 */
msr spsel, #0
/*
* Get the descriptor using the index
* x11 = (base + off), x15 = index
*
* handler = (base + off) + (index << log2(size))
*/
lsl w10, w15, #RT_SVC_SIZE_LOG2
ldr x15, [x11, w10, uxtw]
/*
* Save the SPSR_EL3, ELR_EL3, & SCR_EL3 in case there is a world
* switch during SMC handling.
* TODO: Revisit if all system registers can be saved later.
*/
mrs x16, spsr_el3
mrs x17, elr_el3
mrs x18, scr_el3
stp x16, x17, [x6, #CTX_EL3STATE_OFFSET + CTX_SPSR_EL3]
str x18, [x6, #CTX_EL3STATE_OFFSET + CTX_SCR_EL3]
/* Copy SCR_EL3.NS bit to the flag to indicate caller's security */
bfi x7, x18, #0, #1
mov sp, x12
/*
* Call the Secure Monitor Call handler and then drop directly into
* el3_exit() which will program any remaining architectural state
* prior to issuing the ERET to the desired lower EL.
*/
#if DEBUG
cbz x15, rt_svc_fw_critical_error
#endif
// 跳到x15 执行psci的函数
blr x15
//返回el1的kernel
b el3_exit
通过x15 跳转到的函数是std_svc_smc_handler
/* Register Standard Service Calls as runtime service */
DECLARE_RT_SVC(
std_svc,
OEN_STD_START,
OEN_STD_END,
SMC_TYPE_FAST,
std_svc_setup,
std_svc_smc_handler
);
这里的std_svc_smc_handler 通过DECLARE_RT_SVC 被定义为runtime service
uintptr_t std_svc_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)
{
/*
* Dispatch PSCI calls to PSCI SMC handler and return its return
* value
*/
if (is_psci_fid(smc_fid)) {
uint64_t ret;
#if ENABLE_RUNTIME_INSTRUMENTATION
/*
* Flush cache line so that even if CPU power down happens
* the timestamp update is reflected in memory.
*/
PMF_WRITE_TIMESTAMP(rt_instr_svc,
RT_INSTR_ENTER_PSCI,
PMF_CACHE_MAINT,
get_cpu_data(cpu_data_pmf_ts[CPU_DATA_PMF_TS0_IDX]));
#endif
ret = psci_smc_handler(smc_fid, x1, x2, x3, x4,
cookie, handle, flags);
#if ENABLE_RUNTIME_INSTRUMENTATION
PMF_CAPTURE_TIMESTAMP(rt_instr_svc,
RT_INSTR_EXIT_PSCI,
PMF_NO_CACHE_MAINT);
#endif
SMC_RET1(handle, ret);
}
switch (smc_fid) {
case ARM_STD_SVC_CALL_COUNT:
/*
* Return the number of Standard Service Calls. PSCI is the only
* standard service implemented; so return number of PSCI calls
*/
SMC_RET1(handle, PSCI_NUM_CALLS);
case ARM_STD_SVC_UID:
/* Return UID to the caller */
SMC_UUID_RET(handle, arm_svc_uid);
case ARM_STD_SVC_VERSION:
/* Return the version of current implementation */
SMC_RET2(handle, STD_SVC_VERSION_MAJOR, STD_SVC_VERSION_MINOR);
default:
WARN("Unimplemented Standard Service Call: 0x%x \n", smc_fid);
SMC_RET1(handle, SMC_UNK);
}
}
在std_svc_smc_handler 中通过is_psci_fid(smc_fid)判断是不是smc。psci当然是smc ,因此is_psci_fid(smc_fid) 返回true
然后就调用psci_smc_handler
/*******************************************************************************
* PSCI top level handler for servicing SMCs.
******************************************************************************/
u_register_t psci_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)
{
if (is_caller_secure(flags))
return SMC_UNK;
/* Check the fid against the capabilities */
if (!(psci_caps & define_psci_cap(smc_fid)))
return SMC_UNK;
if (((smc_fid >> FUNCID_CC_SHIFT) & FUNCID_CC_MASK) == SMC_32) {
/* 32-bit PSCI function, clear top parameter bits */
x1 = (uint32_t)x1;
x2 = (uint32_t)x2;
x3 = (uint32_t)x3;
switch (smc_fid) {
case PSCI_VERSION:
return psci_version();
case PSCI_CPU_OFF:
return psci_cpu_off();
case PSCI_CPU_SUSPEND_AARCH32:
return psci_cpu_suspend(x1, x2, x3);
case PSCI_CPU_ON_AARCH32:
return psci_cpu_on(x1, x2, x3);
case PSCI_AFFINITY_INFO_AARCH32:
return psci_affinity_info(x1, x2);
case PSCI_MIG_AARCH32:
return psci_migrate(x1);
case PSCI_MIG_INFO_TYPE:
return psci_migrate_info_type();
case PSCI_MIG_INFO_UP_CPU_AARCH32:
return psci_migrate_info_up_cpu();
case PSCI_NODE_HW_STATE_AARCH32:
return psci_node_hw_state(x1, x2);
case PSCI_SYSTEM_SUSPEND_AARCH32:
return psci_system_suspend(x1, x2);
case PSCI_SYSTEM_OFF:
psci_system_off();
/* We should never return from psci_system_off() */
case PSCI_SYSTEM_RESET:
psci_system_reset();
/* We should never return from psci_system_reset() */
case PSCI_FEATURES:
return psci_features(x1);
#if ENABLE_PSCI_STAT
case PSCI_STAT_RESIDENCY_AARCH32:
return psci_stat_residency(x1, x2);
case PSCI_STAT_COUNT_AARCH32:
return psci_stat_count(x1, x2);
#endif
default:
break;
}
} else {
/* 64-bit PSCI function */
switch (smc_fid) {
case PSCI_CPU_SUSPEND_AARCH64:
return psci_cpu_suspend(x1, x2, x3);
case PSCI_CPU_ON_AARCH64:
return psci_cpu_on(x1, x2, x3);
case PSCI_AFFINITY_INFO_AARCH64:
return psci_affinity_info(x1, x2);
case PSCI_MIG_AARCH64:
return psci_migrate(x1);
case PSCI_MIG_INFO_UP_CPU_AARCH64:
return psci_migrate_info_up_cpu();
case PSCI_NODE_HW_STATE_AARCH64:
return psci_node_hw_state(x1, x2);
case PSCI_SYSTEM_SUSPEND_AARCH64:
return psci_system_suspend(x1, x2);
#if ENABLE_PSCI_STAT
case PSCI_STAT_RESIDENCY_AARCH64:
return psci_stat_residency(x1, x2);
case PSCI_STAT_COUNT_AARCH64:
return psci_stat_count(x1, x2);
#endif
default:
break;
}
}
WARN("Unimplemented PSCI Call: 0x%x \n", smc_fid);
return SMC_UNK;
}
在psci_smc_handler 中就根据smc的id来调用不同的psci的处理函数。
处理完成后回到smc_handler64,最终通过 b el3_exit,返回到el1的kernel
arm-trusted-firmware-master/bl31/aarch64/runtime_exceptions.S
vector_entry sync_exception_aarch64
/*
* This exception vector will be the entry point for SMCs and traps
* that are unhandled at lower ELs most commonly. SP_EL3 should point
* to a valid cpu context where the general purpose and system register
* state can be saved.
*/
handle_sync_exception
check_vector_size sync_exception_aarch64
在handle_sync_exception中判断是smc_handler64还是smc_handler32
.macro handle_sync_exception
/* Enable the SError interrupt */
msr daifclr, #DAIF_ABT_BIT
str x30, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_LR]
#if ENABLE_RUNTIME_INSTRUMENTATION
/*
* Read the timestamp value and store it in per-cpu data. The value
* will be extracted from per-cpu data by the C level SMC handler and
* saved to the PMF timestamp region.
*/
mrs x30, cntpct_el0
str x29, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X29]
mrs x29, tpidr_el3
str x30, [x29, #CPU_DATA_PMF_TS0_OFFSET]
ldr x29, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X29]
#endif
mrs x30, esr_el3
ubfx x30, x30, #ESR_EC_SHIFT, #ESR_EC_LENGTH
/* Handle SMC exceptions separately from other synchronous exceptions */
cmp x30, #EC_AARCH32_SMC
b.eq smc_handler32
cmp x30, #EC_AARCH64_SMC
b.eq smc_handler64
/* Other kinds of synchronous exceptions are not handled */
no_ret report_unhandled_exception
.endm
我们这里以smc_handler32为例
smc_handler64:
/*
* Populate the parameters for the SMC handler.
* We already have x0-x4 in place. x5 will point to a cookie (not used
* now). x6 will point to the context structure (SP_EL3) and x7 will
* contain flags we need to pass to the handler Hence save x5-x7.
*
* Note: x4 only needs to be preserved for AArch32 callers but we do it
* for AArch64 callers as well for convenience
*/
stp x4, x5, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X4]
stp x6, x7, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X6]
/* Save rest of the gpregs and sp_el0*/
save_x18_to_x29_sp_el0
mov x5, xzr
mov x6, sp
/* Get the unique owning entity number */
ubfx x16, x0, #FUNCID_OEN_SHIFT, #FUNCID_OEN_WIDTH
ubfx x15, x0, #FUNCID_TYPE_SHIFT, #FUNCID_TYPE_WIDTH
orr x16, x16, x15, lsl #FUNCID_OEN_WIDTH
//通过__RT_SVC_DESCS_START__ + RT_SVC_DESC_HANDLE 找到需要处理的函数
adr x11, (__RT_SVC_DESCS_START__ + RT_SVC_DESC_HANDLE)
/* Load descriptor index from array of indices */
adr x14, rt_svc_descs_indices
//将处理的函数指针放到w15中
ldrb w15, [x14, x16]
/*
* Restore the saved C runtime stack value which will become the new
* SP_EL0 i.e. EL3 runtime stack. It was saved in the 'cpu_context'
* structure prior to the last ERET from EL3.
*/
ldr x12, [x6, #CTX_EL3STATE_OFFSET + CTX_RUNTIME_SP]
/*
* Any index greater than 127 is invalid. Check bit 7 for
* a valid index
*/
tbnz w15, 7, smc_unknown
/* Switch to SP_EL0 */
msr spsel, #0
/*
* Get the descriptor using the index
* x11 = (base + off), x15 = index
*
* handler = (base + off) + (index << log2(size))
*/
lsl w10, w15, #RT_SVC_SIZE_LOG2
ldr x15, [x11, w10, uxtw]
/*
* Save the SPSR_EL3, ELR_EL3, & SCR_EL3 in case there is a world
* switch during SMC handling.
* TODO: Revisit if all system registers can be saved later.
*/
mrs x16, spsr_el3
mrs x17, elr_el3
mrs x18, scr_el3
stp x16, x17, [x6, #CTX_EL3STATE_OFFSET + CTX_SPSR_EL3]
str x18, [x6, #CTX_EL3STATE_OFFSET + CTX_SCR_EL3]
/* Copy SCR_EL3.NS bit to the flag to indicate caller's security */
bfi x7, x18, #0, #1
mov sp, x12
/*
* Call the Secure Monitor Call handler and then drop directly into
* el3_exit() which will program any remaining architectural state
* prior to issuing the ERET to the desired lower EL.
*/
#if DEBUG
cbz x15, rt_svc_fw_critical_error
#endif
// 跳到x15 执行psci的函数
blr x15
//返回el1的kernel
b el3_exit
通过x15 跳转到的函数是std_svc_smc_handler
/* Register Standard Service Calls as runtime service */
DECLARE_RT_SVC(
std_svc,
OEN_STD_START,
OEN_STD_END,
SMC_TYPE_FAST,
std_svc_setup,
std_svc_smc_handler
);
这里的std_svc_smc_handler 通过DECLARE_RT_SVC 被定义为runtime service
uintptr_t std_svc_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)
{
/*
* Dispatch PSCI calls to PSCI SMC handler and return its return
* value
*/
if (is_psci_fid(smc_fid)) {
uint64_t ret;
#if ENABLE_RUNTIME_INSTRUMENTATION
/*
* Flush cache line so that even if CPU power down happens
* the timestamp update is reflected in memory.
*/
PMF_WRITE_TIMESTAMP(rt_instr_svc,
RT_INSTR_ENTER_PSCI,
PMF_CACHE_MAINT,
get_cpu_data(cpu_data_pmf_ts[CPU_DATA_PMF_TS0_IDX]));
#endif
ret = psci_smc_handler(smc_fid, x1, x2, x3, x4,
cookie, handle, flags);
#if ENABLE_RUNTIME_INSTRUMENTATION
PMF_CAPTURE_TIMESTAMP(rt_instr_svc,
RT_INSTR_EXIT_PSCI,
PMF_NO_CACHE_MAINT);
#endif
SMC_RET1(handle, ret);
}
switch (smc_fid) {
case ARM_STD_SVC_CALL_COUNT:
/*
* Return the number of Standard Service Calls. PSCI is the only
* standard service implemented; so return number of PSCI calls
*/
SMC_RET1(handle, PSCI_NUM_CALLS);
case ARM_STD_SVC_UID:
/* Return UID to the caller */
SMC_UUID_RET(handle, arm_svc_uid);
case ARM_STD_SVC_VERSION:
/* Return the version of current implementation */
SMC_RET2(handle, STD_SVC_VERSION_MAJOR, STD_SVC_VERSION_MINOR);
default:
WARN("Unimplemented Standard Service Call: 0x%x \n", smc_fid);
SMC_RET1(handle, SMC_UNK);
}
}
在std_svc_smc_handler 中通过is_psci_fid(smc_fid)判断是不是smc。psci当然是smc ,因此is_psci_fid(smc_fid) 返回true
然后就调用psci_smc_handler
/*******************************************************************************
* PSCI top level handler for servicing SMCs.
******************************************************************************/
u_register_t psci_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)
{
if (is_caller_secure(flags))
return SMC_UNK;
/* Check the fid against the capabilities */
if (!(psci_caps & define_psci_cap(smc_fid)))
return SMC_UNK;
if (((smc_fid >> FUNCID_CC_SHIFT) & FUNCID_CC_MASK) == SMC_32) {
/* 32-bit PSCI function, clear top parameter bits */
x1 = (uint32_t)x1;
x2 = (uint32_t)x2;
x3 = (uint32_t)x3;
switch (smc_fid) {
case PSCI_VERSION:
return psci_version();
case PSCI_CPU_OFF:
return psci_cpu_off();
case PSCI_CPU_SUSPEND_AARCH32:
return psci_cpu_suspend(x1, x2, x3);
case PSCI_CPU_ON_AARCH32:
return psci_cpu_on(x1, x2, x3);
case PSCI_AFFINITY_INFO_AARCH32:
return psci_affinity_info(x1, x2);
case PSCI_MIG_AARCH32:
return psci_migrate(x1);
case PSCI_MIG_INFO_TYPE:
return psci_migrate_info_type();
case PSCI_MIG_INFO_UP_CPU_AARCH32:
return psci_migrate_info_up_cpu();
case PSCI_NODE_HW_STATE_AARCH32:
return psci_node_hw_state(x1, x2);
case PSCI_SYSTEM_SUSPEND_AARCH32:
return psci_system_suspend(x1, x2);
case PSCI_SYSTEM_OFF:
psci_system_off();
/* We should never return from psci_system_off() */
case PSCI_SYSTEM_RESET:
psci_system_reset();
/* We should never return from psci_system_reset() */
case PSCI_FEATURES:
return psci_features(x1);
#if ENABLE_PSCI_STAT
case PSCI_STAT_RESIDENCY_AARCH32:
return psci_stat_residency(x1, x2);
case PSCI_STAT_COUNT_AARCH32:
return psci_stat_count(x1, x2);
#endif
default:
break;
}
} else {
/* 64-bit PSCI function */
switch (smc_fid) {
case PSCI_CPU_SUSPEND_AARCH64:
return psci_cpu_suspend(x1, x2, x3);
case PSCI_CPU_ON_AARCH64:
return psci_cpu_on(x1, x2, x3);
case PSCI_AFFINITY_INFO_AARCH64:
return psci_affinity_info(x1, x2);
case PSCI_MIG_AARCH64:
return psci_migrate(x1);
case PSCI_MIG_INFO_UP_CPU_AARCH64:
return psci_migrate_info_up_cpu();
case PSCI_NODE_HW_STATE_AARCH64:
return psci_node_hw_state(x1, x2);
case PSCI_SYSTEM_SUSPEND_AARCH64:
return psci_system_suspend(x1, x2);
#if ENABLE_PSCI_STAT
case PSCI_STAT_RESIDENCY_AARCH64:
return psci_stat_residency(x1, x2);
case PSCI_STAT_COUNT_AARCH64:
return psci_stat_count(x1, x2);
#endif
default:
break;
}
}
WARN("Unimplemented PSCI Call: 0x%x \n", smc_fid);
return SMC_UNK;
}
在psci_smc_handler 中就根据smc的id来调用不同的psci的处理函数。
处理完成后回到smc_handler64,最终通过 b el3_exit,返回到el1的kernel