基于STM32MP1简单梳理Linux suspend/resume涉及到的内容:
- 触发Suspend流程,以及唤醒手段和后续resume流程。
- Linux kernel中Suspend/Resume流程。
- TFA中冷启动、热启动、SMC处理、PSCI实现等等。
- 其他低功耗相关:poweroff、reboot、fiq处理。
- Power Domain Tree介绍;PSCI移植指导等。
1 STM32MP1低功耗手段
强制进入suspend:
echo mem > /sys/power/state
单核offline/online:
echo 0 > /sys/bus/cpu/devices/cpu1/online
设置RTC时钟,首先需要hwclock -w取系统时间设置RTC时钟,否则会报如下错误:
[ 4707.038805] rtc-pcf8563 3-0051: low voltage detected, date/time is not reliable. [ 4707.046361] rtc-pcf8563 3-0051: low voltage detected, date/time is not reliable. cat: read error: Invalid argument
rtc设置闹钟/唤醒闹钟:
rtc wkalmset rtc almset rtc aieon rtc aieoff
2 Suspend/Resume流程
echo mem > /sys/power/state发起suspend/resume流程,示意图如下:
suspend/resume流程log:
[ 25.498912] PM: suspend entry (deep) [ 25.539296] Filesystems sync: 0.038 seconds [ 25.545436] Freezing user space processes ... (elapsed 0.001 seconds) done. [ 25.552690] OOM killer disabled. [ 25.555557] Freezing remaining freezable tasks ... (elapsed 0.001 seconds) done. INFO: arnoldlu psci_smc_handler smc_fid=0x8400000e [r1:r3]=0xc010f0ac 0x00000000 0x00000000 INFO: arnoldlu psci_system_suspend INFO: arnoldlu stm32_validate_ns_entrypoint INFO: arnoldlu stm32_get_sys_suspend_power_state INFO: arnoldlu psci_cpu_suspend_start-163 idx=1, state_info=2, 2, is_power_down_state=1 INFO: arnoldlu psci_cpu_suspend_start-177 idx=0, end_pwrlvl=1, parent_nodes[0]=0 INFO: arnoldlu psci_cpu_suspend_start-185 end_pwrlvl=1, parent_nodes[0]=0 INFO: arnoldlu psci_cpu_suspend_start-203 end_pwrlvl=1, local_pwr_state[0]=2 INFO: arnoldlu stm32_enter_low_power, mode=0x00000002, nsec_addr=0xc010f0ac INFO: arnoldlu enter_cstop-133 INFO: arnoldlu stm32_pwr_domain_pwr_down_wfi-173 INFO: arnoldlu stm32_pwr_down_wfi-338 INFO: arnoldlu stm32_pwr_down_wfi-343 INFO: arnoldlu stm32_pwr_down_wfi-345 INFO: arnoldlu stm32_pwr_down_wfi-348 interrupt=1023 INFO: arnoldlu stm32_exit_cstop-246 INFO: arnoldlu stm32_pwr_domain_pwr_down_wfi-184 INFO: arnoldlu sp_min_warm_boot INFO: arnoldlu psci_warmboot_entrypoint INFO: arnoldlu psci_cpu_suspend_finish INFO: arnoldlu stm32_pwr_domain_suspend_finish [ 25.991364] usb 2-1: reset high-speed USB device number 2 using ehci-platform [ 26.164146] OOM killer enabled. [ 26.165833] Restarting tasks ... done. [ 26.173797] PM: suspend exit # [ 26.226977] mmc1: Problem switching card into high-speed mode!
3 Linux中低功耗代码分析
3.1 psci初始化
psci_dt_init()读取dts中psci的配置,并调用对应版本的初始化函数。比如“arm,psci-1.0”:
psci_1_0_init()
->psci_0_2_init()
->get_set_conduit_method()--根据dts配置,确定使用smc还是hvc处理psci任务。 ->set_donduit()--如果是smc,invoke_psci_fn对应的函数位__invoke_psci_fn_smc。如果是hvc,则对应函数为__invoke_psci_fn_hvc。 ->psci_probe()
->psci_get_version()
->psci_0_2_set_functions()
->psci_init_migrate()
->psci_init_smcc()
->psci_init_cpu_suspend()
->psci_init_system_suspend()
->psci_init_system_suspend()
->suspend_set_ops()--配置suspend_ops指向psci_suspend_ops,建立suspend层和psci层关联。
->psci_init_system_reset2()
3.2 suspend之内核通用层
pm_suspend() ->enter_state() ->valid_state()--判断输入的state是否有效,只支持PM_SUSPEND_MEM。 ->suspend_prepare() ->suspend_devices_and_enter() ->platform_suspend_begin() ->suspend_console ->dpm_suspend_start ->suspend_enter platform_suspend_prepare() dpm_suspend_late() platform_suspend_prepare_late() dpm_suspend_noirq platform_suspend_prepare_noirq suspend_disable_secondary_cpus arch_suspend_disable_irqs syscore_suspend suspend_ops->enter() ->psci_system_suspend_enter--以psci_system_suspend作为参数,在下面展开。 syscore_resume arch_suspend_enable_irqs suspend_enable_secondary_cpus platform_resume_noirq dpm_resume_noirq platform_resume_early dpm_resume_early platform_resume_finish dpm_resume_end resume_console ->suspend_finish()
3.3 suspend之psci层(纽带:invoke_psci_fn)
suspend_set_ops设置suspend_ops为psci_suspend_ops:static const struct platform_suspend_ops psci_suspend_ops = { .valid = suspend_valid_only_mem,--仅支持PM_SUSPEND_MEM模式的suspend。 .enter = psci_system_suspend_enter, }; static int psci_system_suspend_enter(suspend_state_t state) { return cpu_suspend(0, psci_system_suspend); } static int psci_system_suspend(unsigned long unused) { return invoke_psci_fn(PSCI_FN_NATIVE(1_0, SYSTEM_SUSPEND), __pa_symbol(cpu_resume), 0, 0);--从PL3返回PL1的入口地址为cpu_resume的物理地址。 }
和suspend相关的调用有PSCI_SYSTEM_SUSPEND_AARCH32。
psci_system_suspend_enter--以psci_system_suspend作为参数。 ->cpu_suspend ->cpu_logical_map ->__cpu_suspend--位于sleep.S中。 ->保存pgd、virt sp、phys resume fn。 ->__cpu_suspend_save ->取如下3个地址作为参数:idmap_pgd的物理地址;sp指针;取cpu_do_resume(指向cpu_v7_do_resume)的物理地址。
->cpu_do_suspend--指向cpu_v7_do_suspend函数。 ->flush_cache_louis ->__cpuc_flush_dcache_area ->outer_clear_range
<==================================
1. 如果CPU断电情况的suspend,上述Cache flush处理方式不够完整。
2. 如果在bl32中关闭了I/D Cache,会造成部分数据丢失。在恢复的时候造成程序执行异常。
3. 较好的方式flush_cache_all(),然后再cpu_proc_fin()关闭I/D Cache。
==================================>
->psci_system_suspend--在__cpu_suspend结尾跳转到r1传入的参数,即本函数。通过__pa_symbol获取cpu_resume的物理地址。 ->invoke_psci_fn--调用smc命令,传递fid参数SYSTEM_SUSPEND,返回指针为cpu_resume对应的物理地址。 ->__invoke_psci_fn_smc ->arm_smcc_smc ->__arm_smcc_smc--执行smc指令,进入PL3。
<=================================================================
1. 此后通过smc,进入Monitor模式进行低功耗处理。
2. 从Monitor返回后没有立即进入下面代码执行,而是先进入cpu_resume(即cpu_v7_do_resume)处理。
3. 返回到如下代码首先判断返回值是否为0,正确后执行resume流程,首先切换MMU。
=================================================================> ->cpu_switch_mm--指向cpu_v7_switch_mm。 ->local_flush_bp_all ->local_flush_tlb_all ->check_other_bugs ->cpu_check_bugs ->PROC_VTABLE(check_bugs)--指向cpu_v7_bugs_init函数。 ->cpu_v7_spectre_init
唤醒后的入口函数为cpu_resume:
cpu_resume ->根据保存的resume fn地址,即cpu_do_resume(指向cpu_v7_do_resume)。 ->cpu_v7_do_resume ->cpu_resume_mmu
->cpu_resume_after_mm
->cpu_init
->写0到r0,表示返回值为0;
3.4 发起SMC系统调用:invoke_psci_fn
invoke_psci_fn第一个参数为SMC ID,后面跟上3个参数。
static unsigned long __invoke_psci_fn_smc(unsigned long function_id, unsigned long arg0, unsigned long arg1, unsigned long arg2) { struct arm_smccc_res res; arm_smccc_smc(function_id, arg0, arg1, arg2, 0, 0, 0, 0, &res); return res.a0; } .macro SMCCC_SMC __SMC(0) .endm .macro SMCCC_HVC __HVC(0) .endm .macro SMCCC instr UNWIND( .fnstart) mov r12, sp push {r4-r7} UNWIND( .save {r4-r7}) ldm r12, {r4-r7} \instr pop {r4-r7} ldr r12, [sp, #(4 * 4)] stm r12, {r0-r3} bx lr UNWIND( .fnend) .endm
4 TFA低功耗代码分析
4.1 BL32启动流程
BL32向量表:
vector_base sp_min_vector_table b sp_min_entrypoint b plat_panic_handler /* Undef */ b sp_min_handle_smc /* Syscall */ b plat_panic_handler /* Prefetch abort */ b plat_panic_handler /* Data abort */ b plat_panic_handler /* Reserved */ b plat_panic_handler /* IRQ */ b sp_min_handle_fiq /* FIQ */
BL32仅对Reset、SMC调用、FIQ进行了异常处理,plat_panic_handler为死循环。
- sp_min_entrypoint为BL32的入口函数。
- sp_min_handle_smc为BL32响应smc调用的处理函数。
- sp_min_handle_fiq为BL32响应FIQ中断的处理函数。
4.2 冷启动流程注释(sp_min_entrypoint)
启动流程函数如下:
sp_min_entrypoint ->el3_entrypoint_common ->sp_min_early_platform_setup2--BL32中进行平台特定的操作。 ->mmap_add_region--对代码段进行等地址映射。 ->configure_mmu ->mmap_add--增加MMU映射区间。 ->init_xlat_tables--初始化转换页表。 ->enable_mmu_svc_mon--打开Monitor模式MMU配置。 ->sp_min_plat_arch_setup ->setup_page_tables--配置MMU页表等。 ->enable_mmu_svc_mon--使能Monitor下MMU。 ->sp_min_main-进行平台和PSCI库设置,并初始化运行时服务。 ->sp_min_platform_setup--初始化MMU、GIC、唤醒中断等等。 ->ddr_save_sr_mode--进入当前DDR SelfRefresh模式。 ->stm32mp1_security_setup--初始化TrustZone Controller。 ->generic_delay_timer_init--初始化GenericTimer作为udelay/mdelay使用。 ->stm32_gic_init ->gicv2_driver_init--检查GIC版本号,并将gicv2_driver_data_t赋给driver_data。 ->gicv2_distif_init--ColdBoot主CPU进行GIC Distributor初始化。 ->stm32_gic_pcpu_init--每CPU相关的GIC初始化。 ->gicv2_pcpu_distif_init--每CPU相关Distributor初始化。 ->gicv2_set_pe_target_mask ->gicv2_cpuif_enable--每CPU的Interface初始化。 ->configure_wakeup_interrupt ->plat_ic_set_interrupt_priority ->gicv2_set_interrupt_priority ->gicd_set_ipriorityr--设置某一中断的优先级,此优先级高于Mask优先级则可以送达CPU,作为唤醒中断。 ->runtime_svc_init--遍历rt_svc_descs段中的各个结构体init()函数。 ->sp_min_prepare_next_image_entry--初始化BL33非安全CPU上下文,并将相关寄存器拷贝到smc上下文。 ->sp_min_plat_runtime_setup ->console_flush ->clean_dcache_range(.data) ->clean_dcache_range(.bss)--清.data和.bss段DCache。 ->smc_get_next_ctx--获取非安全上下文smc_ctx_t地址到r0寄存器中。 ->sp_min_exit ->monitor_exit--退出到非安全世界。
4.2.1 sp_min_exit
退出sp_min,此时r0存放SMC上下文。sp_min_exit调用monitor_exit,将SMC上下文恢复。
sp_min_exit被如下函数调用:
- sp_min_entrypoint:冷启动入口,进行必要初始化后主动退出sp_min,进入非安全启动。
- sp_min_handle_smc:处理smc调用。因smc异常进入sp_min,处理完smc请求后,主动退出sp_min。
- sp_min_handle_fiq:因fiq异常进入sp_min,在电泳sp_min_fiq处理fiq之后,主动退出sp_min。
- sp_min_warm_entrypoint:在系统进入suspend之后,唤醒进入Warm Boot流程。在流程结束后主动退出sp_min。
详细流程分析如下:
- 恢复scr、pmcr寄存器。
- 恢复sp_usr、lr_usr,以及irq/firq/svc/abt/und/mon的spsr、sp、lr寄存器。
- 恢复r0-r12寄存器。
- 恢复lr_mon到lr寄存器,并执行eret退出sp_min到非安全环境。
.macro monitor_exit str sp, [r0, #SMC_CTX_SP_MON] mov sp, r0--将当前sp保存到ctx的sp_mon中,并将r0写到sp寄存器。 ldr r1, [r0, #SMC_CTX_SCR] stcopr r1, SCR--从ctx中恢复scr到scr寄存器。 isb /* * Restore PMCR when returning to Non-secure state */ tst r1, #SCR_NS_BIT beq 2f ldr r1, [r0, #SMC_CTX_PMCR] stcopr r1, PMCR--恢复pmcr寄存器。 2: /* Restore the banked registers including the current SPSR */ add r1, r0, #SMC_CTX_SP_USR--r1指向sp_usr。 #if ARM_ARCH_MAJOR == 7 && !defined(ARMV7_SUPPORTS_VIRTUALIZATION) /* Must be in secure state to restore Monitor mode */ ldcopr r4, SCR bic r2, r4, #SCR_NS_BIT stcopr r2, SCR isb cps #MODE32_sys ldm r1!, {sp, lr}--切换到sys模式,将sp_usr和lr_usr写到寄存器sp和lr中。 cps #MODE32_irq--切换到irq模式,设置spsr_fsxc域、sp、lr寄存器。下面一次操作fq、svc、abt、und、mon模式下spsr_fsxc。 ldm r1!, {r2, sp, lr}--更新sp_irq、lr_irq到sp、lr中,spsr_irq到r2中。 msr spsr_fsxc, r2 ... stcopr r4, SCR isb #else #endif /* Restore the LR */ ldr lr, [r0, #SMC_CTX_LR_MON]--将lr_mon恢复到lr寄存器。 /* Restore the rest of the general purpose registers */ ldm r0, {r0-r12}--更新ctx中r0-r12到r0-r12寄存器中。 eret--eret即跳转到lr指向的地址。 .endm
4.2.2 冷启动log
从冷启动log可以看出:
- BL2中加载了BL32、BL33,从启动参数中获取信息准备好跳转到BL32的上下文信息,跳转到BL32。
- BL32中进行RuntimeService初始化后,根据启动参数跳转到下一级BL33。
- BL33从外设中读取Kernel镜像,跳转到Kernel。
- 在内核启动PSCI初始化阶段,psci_probe()中进行4次SMC调用,获取PSCI是否支持CPU_SYSPEND/SYSTEM_SUSPEND/SYSTEM_RESET2。
- 多核环境,调用CPU_ON启动其他核。
=====================================BL2===================================== NOTICE: CPU: STM32MP157DAA Rev.Z ... INFO: BL2: Loading image id 4--BL2加载BL32镜像。 INFO: Loading image id=4 at address 0x2ffed000 INFO: Image id=4 loaded: 0x2ffed000 - 0x2ffff000 INFO: BL2: Loading image id 5--BL2加载BL33镜像。 INFO: Loading image id=5 at address 0xc0100000 INFO: STM32 Image size : 866980 INFO: Image id=5 loaded: 0xc0100000 - 0xc01d3aa4 WARNING: Skip signature check (header option) NOTICE: ROTPK is not deployed on platform. Skipping ROTPK verification. NOTICE: BL2: Booting BL32--BL2跳转到BL32。 INFO: Entry point address = 0x2ffed000 INFO: SPSR = 0x1d3 INFO: Cannot find st,stpmic1 node in DT =====================================BL32===================================== NOTICE: SP_MIN: v2.2-r1.0(debug):a5d819a-dirty--进入BL32。 NOTICE: SP_MIN: Built : 08:59:04, Jul 21 2023 ... INFO: SP_MIN: Initializing runtime services--启动RuntimeService。 arnoldlu enter std_svc_setup arnoldlu enter psci_arch_setup arnoldlu enter stm32mp1_svc_setup INFO: SP_MIN: Preparing exit to normal world--退出BL32到非安全BL33。 ... arnoldlu enter smc_get_next_ctx =====================================BL33===================================== U-Boot 2020.01-stm32mp-r1-gd05f10ca-dirty (Nov 15 2022 - 09:20:59 +0800)--进入BL33,即uboot。 ... Starting kernel ...--BL33启动Linux kernel。 =====================================Kernel===================================== INFO: arnoldlu enter psci_smc_handler smc_fid=0x84000000 [r1:r3]=0x00000000 0x00000000 0x00000000 INFO: arnoldlu exit psci_smc_handler--Kernel中调用psci_get_version()获取PSCI版本号。 INFO: arnoldlu enter psci_smc_handler smc_fid=0x8400000a [r1:r3]=0x80000000 0x00000000 0x00000000 INFO: arnoldlu exit psci_smc_handler--psci_init_smccc()检查PSCI是否支持ARM_SMCCC_VERSION_FUNC_ID。 INFO: arnoldlu enter psci_smc_handler smc_fid=0x8400000a [r1:r3]=0x84000001 0x00000000 0x00000000 INFO: arnoldlu exit psci_smc_handler--psci_init_cpu_suspend()检查PSCI是否支持PSCI_0_2_FN_CPU_SUSPEND。 INFO: arnoldlu enter psci_smc_handler smc_fid=0x8400000a [r1:r3]=0x8400000e 0x00000000 0x00000000 INFO: arnoldlu exit psci_smc_handler--psci_init_system_suspend()检查PSCI是否支持PSCI_1_0_FN_SYSTEM_SUSPEND。 INFO: arnoldlu enter psci_smc_handler smc_fid=0x8400000a [r1:r3]=0x84000012 0x00000000 0x00000000 INFO: arnoldlu exit psci_smc_handler--psci_init_system_reset2()检查PSCI是否支持PSCI_1_1_FN_SYSTEM_RESET2。 INFO: arnoldlu enter psci_smc_handler smc_fid=0x84000003 [r1:r3]=0x00000001 0xc0102600 0x00000000--对于多核,psci_cpu_on调用PSCI_0_2_FN_CPU_ON启动其他核。 [ 0.000000] Booting Linux on physical CPU 0x0 [ 0.000000] psci: probing for conduit method from DT. [ 0.000000] psci: PSCIv1.1 detected in firmware. [ 0.000000] psci: Using standard PSCI v0.2 function IDs [ 0.000000] psci: MIGRATE_INFO_TYPE not supported. [ 0.000000] psci: SMC Calling Convention v1.0 ...
4.3 热启动流程(sp_min_warm_entrypoint)
sp_min_warm_entrypoint()为Warm Boot的入口函数:
sp_min_warm_entrypoint el3_entrypoint_common bl32_plat_enable_mmu--使能MMU,但是否打开DCACHE取决于配置。 ->enable_mmu_svc_mon ->setup_mmu_cfg ->enable_mmu_direct_svc_mon(enable_mmu.S) ->sp_min_warm_boot ->psci_warmboot_entrypoint ->psci_cpu_suspend_finish ->psci_plat_pm_ops->pwr_domain_suspend_finish--即stm32_pwr_domain_suspend_finish。 ->psci_set_suspend_pwrlvl ->cm_prepare_el3_exit ->psci_set_pwr_domains_to_run ->smc_set_next_ctx--空函数。 ->copy_cpu_ctx_to_smc_stx--将regs_t内容拷贝到smc_ctx_t的r0/r1/r2/lr_mon/spsr_mon/scr中。 ->cm_get_context ->smc_get_next_ctx--将当前CPU的smc_ctx_t地址放入到r0寄存器。 ->sp_min_exit ->monitor_exit--根据smc_ctx_t恢复通用寄存器以及banked mode寄存器,从Monitor模式退出。
4.4 SMC注册
SMC Calling Convention (SMCCC) (arm.com)介绍了SMC相关定义,TFA通过DECLARE_RT_SVC()注册了SMC不同SMC ID的操作函数。
#define DECLARE_RT_SVC(_name, _start, _end, _type, _setup, _smch) \ static const rt_svc_desc_t __svc_desc_ ## _name \ __section("rt_svc_descs") __used = { \ .start_oen = (_start), \ .end_oen = (_end), \ .call_type = (_type), \ .name = #_name, \ .init = (_setup), \ .handle = (_smch) \ }
将定义的结构体放入rt_svc_descs段中:
SECTIONS { ... ro . : { /* Ensure 4-byte alignment for descriptors and ensure inclusion */ . = ALIGN(4); __RT_SVC_DESCS_START__ = .; KEEP(*(rt_svc_descs)) __RT_SVC_DESCS_END__ = .; } >RAM ... }
4.5 std_svc注册
std_svc对应OEN为4的调用,即Standard Secure Service Calls。
通过DECLARE_RT_SVC将结构体放入rt_svc_descs段中。
DECLARE_RT_SVC( std_svc, OEN_STD_START, OEN_STD_END, SMC_TYPE_FAST, std_svc_setup, std_svc_smc_handler );
init函数为std_svc_setup(),在runtime_svc_init()中被调用。handle函数为std_svc_smc_handler(),在handle_runtime_svc()中被调用。
std_svc_setup() ->get_arm_std_svc_args() ->psci_setup()
初始化psci_args结构体变量,其中mailbox_ep指向sp_min_warm_entrypoint()。
4.6 PSCI初始化:psci_setup()
std_svc_setup()调用psci_setup()在BL32启动时对后续PSCI调用做好准备。
psci_setup() psci_arch_setup() write_cntfrq_el0(plat_get_syscnt_freq2()) init_cpu_ops()--初始化,并获取(struct cpu_data)->cpu_ops_ptr, print_errata_status--输出ERRATA信息。 plat_get_power_domaiin_tree_desc()--获取平台定义的电源域树。 populate_power_domain_tree()--根据输入的电源域拓扑结构,生成BL32使用的数据结构。 psci_init_pwr_domain_node--初始化psci_non_cpu_pd_nodes或者psci_cpu_pd_nodes。 psci_update_pwrlvl_limits()--需要搞明白PowerDomainTree以及PowerLevel。 psci_init_req_local_pwr_states() psci_set_pwr_domains_to_run() plat_setup_psci_ops()--将具体CPU的psci操作函数赋值给平台psci_plat_pm_ops使用,同时指定Warm启动入口函数。 psci_flush_dcache_range()--刷psci_plat_pm_ops对应的dcache,以免多核需要使用。 psci_caps--在psci_smc_handler()中会对SMC调用做检查,psci_caps提供判断标准。
psci_setup()函数调用plat_setup_psci_ops()将特定平台的plat_psci_ops_t函数集赋给psci_plat_pm_ops。psci_caps记录了psci_plat_pm_ops支持的函数能力。
STM32低功耗实现核心函数集:
static const plat_psci_ops_t stm32_psci_ops = { .cpu_standby = stm32_cpu_standby, .pwr_domain_on = stm32_pwr_domain_on, .pwr_domain_off = stm32_pwr_domain_off, .pwr_domain_suspend = stm32_pwr_domain_suspend, .pwr_domain_on_finish = stm32_pwr_domain_on_finish, .pwr_domain_suspend_finish = stm32_pwr_domain_suspend_finish, .pwr_domain_pwr_down_wfi = stm32_pwr_domain_pwr_down_wfi, .system_off = stm32_system_off, .system_reset = stm32_system_reset, .validate_power_state = stm32_validate_power_state, .validate_ns_entrypoint = stm32_validate_ns_entrypoint, .get_node_hw_state = stm32_node_hw_state, .get_sys_suspend_power_state = stm32_get_sys_suspend_power_state, };
deepest_system_suspend_mode和system_off_mode在stm32mp157d-atk.dtsi中进行配置,为:
system_suspend_supported_soc_modes = < STM32_PM_CSLEEP_RUN STM32_PM_CSTOP_ALLOW_LP_STOP // STM32_PM_CSTOP_ALLOW_LPLV_STOP // STM32_PM_CSTOP_ALLOW_STANDBY_DDR_SR >; system_off_soc_mode = <STM32_PM_CSTOP_ALLOW_STANDBY_DDR_OFF>;
stm32mp1_get_lp_soc_mode()根据psci_mode返回stm32的低功耗模式,分别从stm32mp157d-atk.dtsi中获取。
4.7 CPU操作函数集cpu_ops:复位和下电操作
declare_cpu_ops定义了cpu_ops段内容,里面包含MIDR以及一系列操作函数。
declare_cpu_ops cortex_a7, CORTEX_A7_MIDR, \ cortex_a7_reset_func, \ cortex_a7_core_pwr_dwn, \ cortex_a7_cluster_pwr_dwn .macro declare_cpu_ops _name:req, _midr:req, _resetfunc:req, \ _power_down_ops:vararg .section cpu_ops, "a" .align 2 .type cpu_ops_\_name, %object .word \_midr #if defined(IMAGE_AT_EL3) .word \_resetfunc #endif #ifdef IMAGE_BL32 /* Insert list of functions */ fill_constants CPU_MAX_PWR_DWN_OPS, \_power_down_ops #endif ... .endm
对于A7,最终输出结果为:
800090ac <__CPU_OPS_START__>: 800090ac: 410fc070 .word 0x410fc070--A7 MIDR 800090b0: 80008588 .word 0x80008588--cortex_a7_reset_func 800090b4: 8000858c .word 0x8000858c--cortex_a7_core_pwr_dwn 800090b8: 800085a0 .word 0x800085a0--cortex_a7_cluster_pwr_dwn 800090bc <__CPU_OPS_END__>:
cortex_a7_reset_func
cortex_a7_core_pwr_dwn
cortex_a7_cluster_pwr_dwn
func cortex_a7_cluster_pwr_dwn push {r12, lr} assert_cache_enabled--此时确保cache已经关闭。 mov r0, #DC_OP_CISW bl dcsw_op_level1--刷L1 Cache。 bl plat_disable_acp--对于A7来说为空函数。 /* Exit cluster coherency */ pop {r12, lr} b cortex_a7_disable_smp--对ACTLR.SMP位清0,关闭到处理器的一致性其请求。 endfunc cortex_a7_cluster_pwr_dwn
4.8 SMC处理流程
smc_ctx_t为SMC调用上下文结构体,再进入SMC处理函数时需要将一系列寄存器保存,返回的时候恢复。
smc_ctx_t结构体如下:
typedef struct smc_ctx { u_register_t r0; ... u_register_t r12; /* spsr_usr doesn't exist */ u_register_t sp_usr; u_register_t lr_usr; ... u_register_t spsr_mon; /* * `sp_mon` will point to the C runtime stack in monitor mode. But prior * to exit from SMC, this will point to the `smc_ctx_t` so that * on next entry due to SMC, the `smc_ctx_t` can be easily accessed. */ u_register_t sp_mon; u_register_t lr_mon; u_register_t scr; u_register_t pmcr; u_register_t pad; } smc_ctx_t __aligned(8);
sp_min_handle_smc进行smc调用处理:
func sp_min_handle_smc str lr, [sp, #SMC_CTX_LR_MON]--将lr寄存器保存到smc_ctx_t中。 smccc_save_gp_mode_regs--保存r0-r12/spsr/lr/sp/scr寄存器到SMC context中。 clrex_on_monitor_entry--执行clrex指令,清除exclusive access。 /* * `sp` still points to `smc_ctx_t`. Save it to a register * and restore the C runtime stack pointer to `sp`. */ mov r2, sp /* handle */ ldr sp, [r2, #SMC_CTX_SP_MON]--从ctx中取Monitor SP到sp寄存器中。 ldr r0, [r2, #SMC_CTX_SCR] and r3, r0, #SCR_NS_BIT /* flags */ /* Switch to Secure Mode*/ bic r0, #SCR_NS_BIT stcopr r0, SCR--设置SCR.NS位,切换到安全状态。 isb ldr r0, [r2, #SMC_CTX_GPREG_R0] /* smc_fid */--R0寄存器存放SMC调用的ID。 /* Check whether an SMC64 is issued */ tst r0, #(FUNCID_CC_MASK << FUNCID_CC_SHIFT)--SMC ID的bit30表示32位还是64位,0为32位。 beq 1f /* SMC32 is not detected. Return error back to caller */ mov r0, #SMC_UNK str r0, [r2, #SMC_CTX_GPREG_R0] mov r0, r2 b sp_min_exit--退出Monitor模式,返回smc调用之前模式。 1: mov r1, #0 /* cookie */ bl handle_runtime_svc--进入SMC命令处理。 /* `r0` points to `smc_ctx_t` */ b sp_min_exit--退出Monitor模式,返回smc调用之前模式。 endfunc sp_min_handle_smc
handle_runtime_svc()第一个参数为SMC Function ID。
uintptr_t handle_runtime_svc(uint32_t smc_fid, void *cookie, void *handle, unsigned int flags) { u_register_t x1, x2, x3, x4; unsigned int index; unsigned int idx; const rt_svc_desc_t *rt_svc_descs; assert(handle != NULL); idx = get_unique_oen_from_smc_fid(smc_fid);--根据OEN和Call type确定rt_svc_descs_indices[]中序号。 assert(idx < MAX_RT_SVCS); index = rt_svc_descs_indices[idx];--确定对应smc_fid在rt_svc_descs段中的序号。 if (index >= RT_SVC_DECS_NUM) SMC_RET1(handle, SMC_UNK); rt_svc_descs = (rt_svc_desc_t *) RT_SVC_DESCS_START;--rt_svc_descs段的起始地址。 get_smc_params_from_ctx(handle, x1, x2, x3, x4);--获取SMC上下文中的x1/x2/x3/x4寄存器。 return rt_svc_descs[index].handle(smc_fid, x1, x2, x3, x4, cookie, handle, flags);--根据index和rt_svc_descs找到smc_fid对应的结构体,然后找到handler函数,将获取的smc_fid/x1/x2/x3/x4作为参数输入。 }
4.9 PSCI命令分发处理:std_svc_smc_handler()->psci_smc_handler()
std_svc_smc_hander()是SMC异常处理入口函数,首先处理PSCI请求,然后在处理安全请求。
static 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) { if (is_psci_fid(smc_fid)) {--psci的ID范围是0x84000000-0x8400001f,如果再次范围则进行PSCI处理。 uint64_t ret; ret = psci_smc_handler(smc_fid, x1, x2, x3, x4, cookie, handle, flags); SMC_RET1(handle, ret); } switch (smc_fid) {--进行Standard Secure Service处理。 ... } }
其中psci_smc_handler()函数对PSCI协议中规定的功能进行处理。Linux内核函数、PSCI ID、TF-A函数对应关系:
Linux Caller(psci_ops) |
SMC FID |
TFA Handler |
|
psci_get_version |
PSCI_VERSION |
psci_version() |
|
psci_ops |
psci_ops.cpu_off = psci_cpu_off |
PSCI_CPU_OFF |
psci_cpu_off() |
psci_ops.cpu_suspend = psci_cpu_suspend |
PSCI_CPU_SUSPEND_AARCH32 |
psci_cpu_suspend(r1, r2, r3) |
|
psci_ops.cpu_on = psci_cpu_on |
PSCI_CPU_ON_AARCH32 |
psci_cpu_on(r1, r2, r3) |
|
psci_ops.affinity_info = psci_affinity_info |
PSCI_AFFINITY_INFO_AARCH32 |
psci_affinity_info(r1, r2) |
|
psci_ops.migrate = psci_migrate |
PSCI_MIG_AARCH32 |
psci_migrate(r1) |
|
psci_ops.migrate_info_type = psci_migrate_info_type |
PSCI_MIG_INFO_TYPE |
psci_migrate_info_type() |
|
psci_migrate_info_up_cpu |
PSCI_MIG_INFO_UP_CPU_AARCH32 |
psci_migrate_info_up_cpu() |
|
PSCI_NODE_HW_STATE_AARCH32 |
psci_node_hw_state(r1, r2) |
||
psci_system_suspend |
PSCI_SYSTEM_SUSPEND_AARCH32 |
psci_system_suspend(r1, r2) |
|
pm_power_off |
psci_sys_poweroff |
PSCI_SYSTEM_OFF |
psci_system_off() |
arm_pm_restart |
psci_sys_reset |
PSCI_SYSTEM_RESET |
psci_system_reset() |
psci_features |
PSCI_FEATURES |
psci_features(r1) |
|
arm_pm_restart |
psci_sys_reset |
PSCI_SYSTEM_RESET2_AARCH32 |
psci_system_reset2(r1, r2) |
sp_min_warm_boot |
4.10 TFA suspend流程
TFA的psci_system_suspend负责更底层的suspend流程。
psci_smc_handler psci_system_suspend(PSCI_SYSTEM_SUSPEND_AARCH32)--确认是最后一个CPU,才会进入system suspend。 psci_validate_entry_point stm32_validate_ns_entrypoint--判断entrypoint地址是否有效。 psci_get_ns_ep_info--记录pc、spsr、args、ep_attr等到ep中。 psci_query_sys_suspend_pwrstate stm32_get_sys_suspend_power_state psci_cpu_suspend_start psci_get_parent_pwr_domain_nodes psci_acquire_pwr_domain_locks psci_do_state_coordination read_isr_el1--读取CP15 ISR寄存器,获取A/I/F三种异常是否有Pending。判断是否有pending中断,如果有则停止后续的wfi。 psci_suspend_to_pwrdown_start cm_init_my_context--获取当前CPU的cpu_context_t并初始化,为后续返回非安全状态做准备。 cm_setup_context--保存r0-r3/lr/scr/spsr/sctlr等寄存器。 psci_plat_pm_ops->pwr_domain_suspend--stm32_pwr_domain_suspend。 stm32mp1_get_lp_soc_mode stm32_enter_low_power enter_cstop--低功耗设置。 ddr_set_sr_mode--进入DDR SSR模式。 stm32_save_ddr_training_area--保存DDR training数据。 plat_ic_set_priority_mask--保存GIC mask寄存器到gicc_pmr。 ddr_standby_sr_entry-- ddr_sw_self_refresh_in psci_plat_pm_ops->pwr_domain_pwr_down_wfi--stm32_pwr_domain_pwr_down_wfi。 stm32_pwr_down_wfi stm32mp1_calib_set_wakeup wfi_svc_int_enable --------------------->KEY0/KEY1 Press,之前为suspend流程,之后为resume流程。 gicv2_acknowledge_interrupt--读到的中断ID为1023 stm32_iwdg_refresh--重新喂狗。 stm32_exit_cstop ddr_sw_self_refresh_exit--DDR退出SW SelfRefresh模式。 ddr_restore_sr_mode--返回到保存的SR模式:SSR(Software Self-Refresh)、ASR(Automatic Self-Refresh)、FSR(Full Automatic Self-Refresh)。 plat_ic_set_priority_mask--恢复gicc pmr寄存器值。 disable_mmu_icache_secure
<==================================================
1. 关闭MMU、I/D Cache并不会对Cache Flush。
2. 如果wfi之后对CPU断电,Cache中数据会丢失。唤醒后,可能造成数据不一致。
3. 需要在此之前Flush D-Cache。
==================================================> warm_entrypoint--即stm32_sec_entrypoint,这里对应函数为sp_min_warm_entrypoint。 wfi psci_suspend_to_standby_finisher psci_plat_pm_ops->pwr_domain_suspend_finish--stm32_pwr_domain_suspend_finish
stm32_sec_entrypoint指向psci_args->mailbox_ep,psci_args->mailbox_ep指向sp_min_warm_entrypoint():完成suspend处理,然后恢复上下文退出pl3到Linux中执行Linux的Resume流程。
plat_my_core_pos获取当前core id序号,core id用于唯一标识一个Core。
func plat_stm32mp1_get_core_pos and r1, r0, #MPIDR_CPU_MASK and r0, r0, #MPIDR_CLUSTER_MASK add r0, r1, r0, LSR #6--r1存放CoreId,r0存放ClusterId。r0右移6位,加上r1等同于ClusterID*4+CoreId。 bx lr endfunc plat_stm32mp1_get_core_pos func plat_my_core_pos ldcopr r0, MPIDR--从CP15协处理器中读取MPIDR值到r0中。 b plat_stm32mp1_get_core_pos endfunc plat_my_core_pos
wfi_svc_int_enable()将sp_mon和scr保存到r0/r4中,切换到svc模式打开DataAbort和FIQ进入wfi。从wfi恢复后关闭DataAbort和FIQ使能,将r8恢复到sp中。跳转到lr中地址。
func wfi_svc_int_enable push {r4,r8,lr}--将r4/r8/lr压栈。 ldcopr r4, SCR--将SCR值写入r4。 mov r8, sp--将sp移到r8。 mov sp, r0--将r0指向的地址写入到sp,sp即使用第一个参数作为栈。 add r0, r0, #STM32MP_INT_STACK_SIZE--r0指向栈顶。 str r0, [sp, #SMC_CTX_SP_MON]--将smc_ctx_t的sp_mon写入到r0中。 str r4, [sp, #SMC_CTX_SCR]--将smc_ctx_t的SCR写入到r4中。 cps #MODE32_svc--切换处理器模式到SVC。 cpsie af--打开Data Abort和FIQ的使能。 dsb isb wfi cpsid af--关闭Data Abort和FIQ的使能。 cps #MODE32_mon--切换到Monitor模式。 mov sp, r8 pop {r4,r8,lr}--恢复栈,r4/r8和lr寄存器。 bx lr endfunc wfi_svc_int_enable
5 Poweroff流程
poweroff执行log:
# Stopping network: OK Saving random seed: OK Stopping mdev... stopped process in pidfile '/var/run/mdev.pid' (pid 188) OK Stopping klogd: OK Stopping syslogd: OK umount: devtmpfs busy - remounted read-only [ 14.605608] EXT4-fs (mmcblk1p5): re-mounted. Opts: (null) The system is going down NOW! Sent SIGTERM to all processes Sent SIGKILL to all processes Requesting system poweroff [ 16.669279] reboot: Power down INFO: arnoldlu psci_smc_handler smc_fid=0x84000008 [r1:r3]=0x00000000 0x00000000 0x00000000 INFO: arnoldlu psci_system_off INFO: PSCI Power Domain Map: INFO: Domain Node : Level 1, parent_node -1, State ON (0x0) INFO: Domain Node : Level 0, parent_node 0, State ON (0x0) INFO: CPU Node : MPID 0x0, parent_node 0, State ON (0x0) INFO: CPU Node : MPID 0xffffffff, parent_node 0, State OFF (0x2) INFO: arnoldlu stm32_system_off INFO: arnoldlu stm32_enter_low_power, mode=0x00000005, nsec_addr=0x00000000 INFO: arnoldlu enter_cstop-133 INFO: arnoldlu stm32_pwr_down_wfi-338 INFO: arnoldlu stm32_pwr_down_wfi-343
Linux执行poweroff命令后,调用reboot系统调用:
reboot kernel_power_off kernel_shutdown_prepare migrate_to_reboot_cpu syscore_shutdown machine_power_off local_irq_disable smp_send_stop pm_power_off--使能PSCI后,即调用psci_sys_poweroff,通过SMC发起SYSMTEM_OFF调用。
BL32中处理SYSRTEM_OFF的流程:
std_svc_smc_handler psci_smc_handler psci_system_off(PSCI_SYSTEM_OFF) psci_plat_pm_ops->system_off--stm32_system_off stm32_enter_low_power enter_cstop stm32_pwr_down_wfi
6 Reboot
执行reboot命令后,首先Linux内核执行reboot流程,然后将reboot交给BL32。
Linux reboot log如下:
Stopping network: OK Saving random seed: OK Stopping mdev... stopped process in pidfile '/var/run/mdev.pid' (pid 188) OK Stopping klogd: OK Stopping syslogd: OK umount: devtmpfs busy - remounted read-only [ 440.664728] EXT4-fs (mmcblk1p5): re-mounted. Opts: (null) The system is going down NOW! Sent SIGTERM to all processes Sent SIGKILL to all processes Requesting system reboot [ 442.779231] mmc2: switch to bus width 8 failed [ 442.802316] reboot: Restarting system
Linux reboot命令流程分析:
reboot--系统调用。 kernel_restart kernel_restart_prepare migrate_to_reboot_cpu syscore_shutdown machine_restart local_irq_disable smp_send_stop arm_pm_restart--使能PSCI后即调用psci_sys_reset,触发SMC调用进入BL32。
BL32中PSCI的处理流程:
std_svc_smc_handler psci_smc_handler psci_system_reset(PSCI_SYSTEM_RESET) psci_plat_pm_ops->system_reset--stm32_system_reset写平台相关的复位寄存器。
7 FIQ处理
FIQ中断入口函数为sp_min_handle_fiq:
/* * Secure Interrupts handling function for SP_MIN. */ func sp_min_handle_fiq #if !SP_MIN_WITH_SECURE_FIQ b plat_panic_handler #else /* FIQ has a +4 offset for lr compared to preferred return address */ sub lr, lr, #4 /* On SMC entry, `sp` points to `smc_ctx_t`. Save `lr`. */ str lr, [sp, #SMC_CTX_LR_MON] smccc_save_gp_mode_regs clrex_on_monitor_entry /* load run-time stack */ mov r2, sp ldr sp, [r2, #SMC_CTX_SP_MON] /* Switch to Secure Mode */ ldr r0, [r2, #SMC_CTX_SCR] bic r0, #SCR_NS_BIT stcopr r0, SCR isb push {r2, r3} bl sp_min_fiq pop {r0, r3} b sp_min_exit #endif endfunc sp_min_handle_fiq
sp_min_firq进行安全中断处理:
sp_min_fiq plat_ic_acknowledge_interrupt--读取中断号。 sp_min_plat_fiq_handler--根据中断号进行中断处理。 plat_ic_end_of_interrupt--中断EOI响应。
8 PSCI Power Domain Tree Structure
《5. PSCI Power Domain Tree Structure》定义了TFA中使用的Power Domain Tree。
5.1 Requirements
Requirement 1: 平台提供plat_get_aff_count和plat_get_aff_state接口,以达到导出电源域架构的描述。
Requirement 2: 支持生成MPIDR,用于找到Power Domain Tree中的节点。
Requirement 3: 需要在Power Domain Tree中进行二进制搜索以找到特定的节点。
Requirement 4: Core电源域属性和上一层电源域属性有差异。
5.2 Design
5.2.1. Describing a power domain tree
为了满足Requirement 1,必须定义unsigned char数组:
- 数组第一成员确定highest power level电源域数目。
- 每个子条目关联一个电源域,并且包含直接子电源域数目。
- 数组大小减去第一个条目等于非叶节点电源域数目。
- 数组中每条目的值用于查找下一级条目的数目。
+-+ |0|-----------------------------level0 +-+ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ +-+ +-+ |1| |2|----------------level1 +-+ +-+ / \ / \ / \ / \ / \ / \ / \ / \ +-+ +-+ +-+ +-+ |3| |4| |5| |6|---------level2 +-+ +-+ +-+ +-+ +---+-----+ +----+----| +----+----+ +----+-----+-----+ | | | | | | | | | | | | | | | | | | | | | | | | | | v v v v v v v v v v v v v +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +--+ +--+ +--+ |0| |1| |2| |3| |4| |5| |6| |7| |8| |9| |10| |11| |12|---level3
上述电源域树对应的描述符为:{1(number of highest power level), 2(level 0 node 0 has 2 subsequent), 2(level 1 node 1 has 2 subsequent), 2(level1 node 2 has 2 subsequent), 3(level 2 node 3 has 3 subsequent), 3(level 2 node 4 has 3 subsequent), 3(level 2 node 5 has 3 subsequent), 4(level 2 node 6 has 4 subsequent)}。
最终Core数量为13,电源域数量为13+7=20。
5.2.2. Removing assumptions about MPIDRs used in a platform
对每一个Core电源域分配一个唯一的值:从0~(PLAT_CORE_COUNT - 1)。
plat_core_pos_by_mpidr:根据MPIDR返回Cluster中Core ID。
plat_my_core_pos:平台定义的函数。如A7,返回的ID=Cluster*4+Core ID。
psci_setup()中根据plat_get_power_domain_tree_desc()获取的电源域树描述符,生成psci_non_cpu_pd_nodes和psci_cpu_pd_nodes。
{1, 2}对应的拓扑结构如下:
+-+ |0|-----------------------------level1 +-+ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ +-+ +-+ |0| |1|----------------level0(CPU Core) +-+ +-+
需要定义的相关变量有:
#define PLAT_MAX_PWR_LVL U(1) #define PSCI_CPU_PWR_LVL U(0)--CPU对应的Power Level。 #define PSCI_NUM_PWR_DOMAINS--当前架构中所有电源域数量。 #define PSCI_NUM_NON_CPU_PWR_DOMAINS--当前架构中所有非CPU Core电源域数量。 #define PLATFORM_CORE_COUNT--当前架构中CPU Core数量。 typedef struct non_cpu_pwr_domain_node { unsigned int cpu_start_idx;--当前节点的Level0 CPU电源域节点序号。 unsigned int ncpus;--当前节点CPU电源域数量。 unsigned int parent_node;--当前电源域父节点。 plat_local_state_t local_state;--当前电源域所处低功耗状态。 unsigned char level;--当前电源域Power Level。 unsigned char lock_index; } non_cpu_pd_node_t; typedef struct cpu_pwr_domain_node { u_register_t mpidr;--当前CPU Core MPIDR。 unsigned int parent_node;--父节点的序号。 spinlock_t cpu_lock; } cpu_pd_node_t; non_cpu_pd_node_t psci_non_cpu_pd_nodes[PSCI_NUM_NON_CPU_PWR_DOMAINS]--非CPU电源域节点信息。 cpu_pd_node_t psci_cpu_pd_nodes[PLATFORM_CORE_COUNT]--CPU Core电源域节点信息。 const plat_psci_ops_t *psci_plat_pm_ops;--平台相关低功耗函数集。
5.2.4. Populating the power domain tree
populate_power_domain_tree()根据psci_non_cpu_pd_nodes和psci_cpu_pd_nodes导出电源域树。
PSCI的power domain被组织为一个树形结构,每个power domain通过域中cpu序号和power domain等级确定。
关于power domain等级:
- 0 处理实体,一般为CPU核。
- 1 一组CPU核,一个cluster。
- 2 一组cluster,表示整个系统。
关于PSCI Power Domain《5. PSCI Power Domain Tree Structure — Trusted Firmware-A documentation》。
9 TFA PSCI Porting Guide
以下内容来自于《4.13 Power State Coordination Interface(in BL31) — Trusted Firmware-A documentation》。
4.13.5. Function : plat_get_power_domain_tree_desc() [mandatory]
4.13.6. Function : plat_setup_psci_ops() [mandatory]
给psci_plat_pm_ops指定平台相关的函数集。
int plat_setup_psci_ops(uintptr_t sec_entrypoint, const plat_psci_ops_t **psci_ops) { stm32_sec_entrypoint = sec_entrypoint; *psci_ops = &stm32_psci_ops; return 0; }
4.13.6.1. plat_psci_ops.cpu_standby()
让当前CPU进入指定的plat_local_state_t状态。
4.13.6.2. plat_psci_ops.pwr_domain_on()
对指定MPIDR Core执行平台特定上电流程。
4.13.6.7. plat_psci_ops.pwr_domain_on_finish()
在调用CPU被CPU_ON之后,且释放复位之后调用。执行必要平台相关初始化,以进入normal world和提供安全运行服务。
4.13.6.3. plat_psci_ops.pwr_domain_off()
被PSCI_CPU_OFF调用,根据输入的psci_power_state_t关闭调用CPU及其上层电源域。
4.13.6.5. plat_psci_ops.pwr_domain_suspend()
PSCI CPU_SUSPEND命令调用此函数。
suspend和关闭power domain区别是:
关闭power domain后,在重新上电后需要重新初始化状态。通过pwr_domain_on_finish()实现。
suspend时,重新上电后需要恢复之前保存的状态。通过pwr_domain_suspend_finish()实现。
4.13.6.9. plat_psci_ops.pwr_domain_suspend_finish()
4.13.6.14. plat_psci_ops.get_sys_suspend_power_state()
填充psci_power_state_t结构体,返回系统支持的不同PLAT_MAX_PWR_LVL对应的本地power状态。可选的为:
/* Local power state for power domains in Run state. */ #define ARM_LOCAL_STATE_RUN U(0) /* Local power state for retention. Valid only for CPU power domains */ #define ARM_LOCAL_STATE_RET U(1) /* Local power state for power-down. Valid for CPU and cluster power domains */ #define ARM_LOCAL_STATE_OFF U(2)
4.13.6.6. plat_psci_ops.pwr_domain_pwr_down_wfi()
4.13.6.10. plat_psci_ops.system_off()
在通知Secure Payload Dispatcher后,执行平台特定的power off流程。
4.13.6.11. plat_psci_ops.system_reset()
在通知Secure Payload Dispatcher后,执行平台特定的复位流程。
4.13.6.12. plat_psci_ops.validate_power_state()
4.13.6.13. plat_psci_ops.validate_ns_entrypoint()
验证非安全入口函数是否合法。