电源管理入门-关机重启基础知识详解_第1页
电源管理入门-关机重启基础知识详解_第2页
电源管理入门-关机重启基础知识详解_第3页
电源管理入门-关机重启基础知识详解_第4页
电源管理入门-关机重启基础知识详解_第5页
已阅读5页,还剩11页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

第第页电源管理入门-关机重启基础知识详解当我们接触(电源管理)的时候,最简单的流程就是

关机重启

,但是仔细分析其涉及的所有源代码就会发现,关机重启虽然简单,但是“

麻雀虽小,五脏俱全

”,涉及到的软件模块非常的多,涉及的流程:(Linux)应用(busybox)-》Linux内核-》BL31-》SCP-》(PMIC)/CRU等(硬件)。所以是一个入门学习,特别是还没接触过Linux内核代码的好机会,下面进入代码的海洋遨游,

超级干货

1.关机重启软件流程框图

在Linux系统上的处理分为**用户态**空间、**内核**空间、**ATF**、**SCP**四个阶段(ATF是(ARM)独有的,SCP在复杂SoC上才有应用)来处理:

BL31中smc异常触发流程图

执行SMC指令后会触发异常,进入ATF的BL31中继续执行:

在Linux侧调用smc异常之后,会根据中断向量表触发cpu的同步异常sync_exception_aarch64/32

然后跳转执行到handle_sync_exception->smc_handler64/32中

根据_RT_SVC_DESCS_START_+RT_SVC_DESC_HANDLE的位置,跳转执行rt_svc_desc_t结构体保存的服务std_svc_smc_handler

执行psci相关处理,找到psci_system_off和psci_system_rese处理函数。ATF直接处理如果是关机就执行halt指令,重启则通过设置gpio,或者转送给SCP处理。

最后跳转到el3_exit返回Linux侧。

SMC异常触发执行流程:

(32*4)

//这个.应该是当前位置-段的开头地址如果大于32条指令

.error"Vectorexceeds

32instructions"

//向量超过32条指令

.endif4.3

runtime服务程序初始化

bl31_entrypoint入口向下执行首先是bl31_setup,然后是bl31_main

void

bl31_setup(u_register_targ0,u_register_targ1,u_register_targ2,

u_register_targ3)

{

/*Pe(rf)ormearly

platform-specificsetup*/

bl31_early_platform_setup2(arg0,arg1,arg2,arg3);

/*Performlate

platform-specificsetup*/

bl31_plat_arch_setup();bl31_main()函数:

void

bl31_main(void)

{

NOTICE("BL31:%s",

version_string);

NOTICE("BL31:%s",

build_message);

bl31_platform_setup();

//通用和安全(时钟)初始化,其他(芯片)相关功能初始化

bl31_lib_init();

//空函数

INFO("BL31:Initializing

runtimeservices");

runtime_svc_init();

//重点下面展开分析

if(bl32_init){

INFO("BL31:

InitializingBL32");

(*bl32_init)();

}

bl31_prepare_next_image_entry();

//加载下一阶段的入口地址

console_flush();

//控制台刷新

bl31_plat_runtime_setup();

//空函数

}runtime_svc_init()函数

//注册smc指令相关的服务

voidruntime_svc_init(void)

{

intrc=0;

unsignedintindex,start_idx,

end_idx;

/*Assertthenumberof

descriptorsdetectedarelessthan(maxim)umindices*/

//这句话表明

RT_SVC_DECS_NUM时当前加载的服务数量

assert((RT_SVC_DESCS_END>=

RT_SVC_DESCS_START)

if(RT_SVC_DECS_NUM==0)

//如果没有服务要注册

return;

(mems)et(rt_svc_descs_indices,

-1,sizeof(rt_svc_descs_indices));//初始化rt_svc_descs_indices

rt_svc_descs=(rt_svc_desc_t

*)RT_SVC_DESCS_START;//建立一个注册表结构体

for(index=0;indexinit)

{

//该服务是否需要初始化

rc=

service->init();

//进行初始化

if(rc){

//初始化是否成功

ERROR("Errorinitializingruntimeservice%s",

service->name);

continue;

}

}

start_idx=

get_unique_oen(rt_svc_descs[index].start_oen,

service->call_type);

//八位的id号

assert(start_idxcall_type);

//八位的id号

assert(end_idx(RAM)

#else

ro.:{

__RO_START__=.;

*bl31_entrypoint.o(.text*)

*(SORT_BY_ALIGNMENT(.text*))

*(SORT_BY_ALIGNMENT(.rodata*))

RODATA_COMMON在include/common/bl_common.ld.h中

#define

RODATA_COMMON

RT_SVC_DESCS

FCONF_POPULATOR

PMF_SVC_DESCS

PARSER_LIB_DESCS

CPU_OPS

GOT

BASE_XLAT_TABLE_RO

EL3_LP_DESCS

#defineRT_SVC_DESCS

.=ALIGN(STRUCT_ALIGN);

__RT_SVC_DESCS_START__=

.;

KEEP(*(rt_svc_descs))

__RT_SVC_DESCS_END__=.;rt_svc_descs段存放的内容是通过DECLARE_RT_SVC宏来定义的:

//其中__setion("rt_svc_descs")的意思就是注册到rt_svc_descs段中

#define

DECLARE_RT_SVC(_name,_start,_end,_type,

_setup,_smch)

staticconstrt_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)

}例如在services/std_svc/std_svc_setup.c中

/*

RegisterStandardServiceCallsasruntimeservice*/

DECLARE_RT_SVC(

std_svc,

OEN_STD_START,

OEN_STD_END,

SMC_TYPE_FAST,

std_svc_setup,

std_svc_smc_handler

);

#defineOEN_STD_START

U(4)

/*StandardService

Calls*/

#defineOEN_STD_END

U(4)

#defineSMC_TYPE_FAST

UL(1)

#defineSMC_TYPE_YIELD

UL(0)staticconstrt_svc_desc_t__svc_desc_std_svc服务。其服务id为SMC_TYPE_FASTinit()会执行std_svc_setup()函数

->psci_setup((constpsci_lib_args_t*)svc_arg)

(void)plat_setup_psci_ops((uintptr_t)lib_args->mailbox_ep,

plat_setup_psci_ops()的定义根据平台,我们使用的是qemu,对应plat/qemu/qemu_sbsa/sbsa_pm.c文件中:

*psci_ops=

static

constplat_psci_ops_tplat_qemu_psci_pm_ops={

.cpu_standby=

qemu_cpu_standby,

.pwr_domain_on=

qemu_pwr_domain_on,

.pwr_domain_off=

qemu_pwr_domain_off,

.pwr_domain_pwr_down_wfi=

qemu_pwr_domain_pwr_down_wfi,

.pwr_domain_suspend=qemu_pwr_domain_suspend,

.pwr_domain_on_finish=

qemu_pwr_domain_on_finish,

.pwr_domain_suspend_finish=

qemu_pwr_domain_suspend_finish,

.system_off=qemu_system_off,

.system_reset=

qemu_system_reset,

.validate_power_state=

qemu_validate_power_state

};

4.4SMC异常处理入口分析

SMC命令执行后,CPU会根据异常向量表找到sync_exception_aarch64的入口

会执行handle_sync_exception,在bl31/aarch64/runtime_exceptions.S中

/*

*Thismacrohandles

Synchronousexceptions.

*OnlySMCexceptionsare

supported.

*

*/

.macro

handle_sync_exception

#ifENABLE_RUNTIME_INSTRUMENTATION

/*

*Readthetimestampvalueand

storeitinper-cpudata.Thevalue

*willbeextractedfrom

per-cpudatabytheClevelSMChandlerand

*savedtothePMFtimestamp

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

//将esr_el3存入x30

//#defineESR_EC_SHIFTU(26)

#defineESR_EC_LENGTHU(6)

//相当于保留x30的bit[31-26]并将这几位提到bit[6-0]

ubfx

x30,x30,#ESR_EC_SHIFT,#ESR_EC_LENGTH

/*HandleSMCexceptions

separatelyfromothersynchronousexceptions*/

cmp

x30,#EC_AARCH32_SMC

b.eq

smc_handler32

cmp

x30,#EC_AARCH64_SMC

b.eq

sync_handler64

cmp

x30,#EC_AARCH64_SYS

b.eq

sync_handler64

/*Synchronousexceptionsother

thantheaboveareassumedtobeEA*/

ldr

x30,[sp,#CTX_GPREGS_OFFSET+

CTX_GPREG_LR]

b

enter_lower_el_sync_ea

.endm三种跳转选项其中smc_handler32/64能够正确触发异常,report_unhand(led)_exception则是错误的流程

#define

EC_AARCH32_SMC

U(0x13)

#defineEC_AARCH64_SVC

U(0x15)

#defineEC_AARCH64_HVC

U(0x16)

#defineEC_AARCH64_SMC

U(0x17)x30里面存储的是esr_el3的26-32位,里面是什么判断了smc64

当前平台架构是aarch64的,看一下sync_handler64这个处理,在bl31/aarch64/runtime_exceptions.S中

/*Loaddescriptorindexfromarray

ofindices*/

//在runtime_svc_init()中会将所有的sectionrt_svc_descs段放入rt_svc_descs_indices数组,

//这里获取该数组地址

adrp

x14,rt_svc_descs_indices

add

x14,x14,rt_svc_descs_indices

ldrb

w15,[x14,x16]//找到rt_svc在rt_svc_descs_indices数组中的index

/*

*Getthedescriptorusingthe

index

*x11=(base+off),w15=

index这个index就是rt_svc_descs结构体数组下标

*

*handler=(base+off)+

(index

•bit31决定是fastcall,还是stdcall(yield对应的就是stdcall)

•bit30表示是以32位传参,还是以64位传参,注意我们看了optee在linux的driver,都是以32位方式

•bit29:24决定服务的类型

•bit23:16reserved

•bit15:0每种call类型下,表示range

这个地方值为4,

/*

RegisterStandardServiceCallsasruntimeservice*/

DECLARE_RT_SVC(

std_svc,

OEN_STD_START,

OEN_STD_END,

SMC_TYPE_FAST,

std_svc_setup,

std_svc_smc_handler

);

#defineOEN_STD_START

U(4)

/*StandardServiceCalls*/

#defineOEN_STD_END

U(4)

系统启动的时候会把index信息存入到rt_svc_descs_indices里面,根据4取出来就可以了。

start_idx

=(uint8_t)get_unique_oen(service->start_oen,service->call_type);

end_idx=

(uint8_t)get_unique_oen(service->end_oen,service->call_type);

assert(start_idxsystem_off!=NULL);

/*NotifytheSecurePayload

Dispatcher*/

if((psci_spd_pm!=NULL)

}

console_flush();

/*Calltheplatformspecific

hook*/

psci_plat_pm_ops->system_off();

/*Thisfunctiondoesnot

return.Weshouldnevergethere*/

}psci_print_power_domain_map()的打印再和设备重启时的日志进行对比,发现是一致的。

4.5

硬件平台相关处理

在qemu平台上的实现如下:

psci_plat_pm_ops系统初始化的时候会赋值

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论