【ATF】TF-A(ARM Trusted Firmware)概述

ATF

TF(Trusted Firmware)是ARM在Armv8引入的安全解决方案,为安全提供了整体解决方案。它包括启动和运行过程中的特权级划分,对Armv7中的TrustZone(TZ)进行了提高,补充了启动过程信任链的传导,细化了运行过程的特权级区间。

TF实际有两种Profile,对ARM Profile A的CPU应用TF-A,对ARM Profile M的CPU应用TF-M。我们一般接触的都是TF-A,又因为这个概念是ARM提出的,有时候也缩写做ATF(ARM Trusted Firmware)。

ATF和TZ是不同的,实际上,TZ更多的是和Intel的SGX概念对应,是在CPU和内存中区隔出两个空间:Secure空间和Non-Secure空间。

实际上,TZ更多的是和Intel的SGX概念对应,是在CPU和内存中区隔出两个空间:Secure空间和Non-Secure空间。

它在保有TZ的Secure空间和Non-Secure空间的同时,划分了EL0(Exception level 0)到EL3四个特权级:

image

其中EL0和EL1是ATF必须实现的,EL2和EL3是可选的。实际上,没有EL2和EL3,整个模型就基本退化成了ARMv7的TZ版本。从高EL转低EL通过ERET指令,从低EL转高EL通过exception,从而严格区分不同的特权级。其中EL0、EL1、EL2可以分成NS-ELx(None Secure ELx)和S-ELx(Secure ELx)两种,而EL3只有安全模式一种。

ATF的启动过程

ARM开源了ATF的基本功能模块:

git clone  https://github.com/ARM-software/arm-trusted-firmware.git

这里推荐NXP2160A的代码分支,因为其他的很多芯片部分是源码时缺失的,尤其是和芯片部分和与UEFI联动部分。

支持ATF的ARM机器,启动过程如下:

image

蓝色箭头为启动顺序。

  • BL1 - AP Trusted ROM,一般为Bootroom
  • BL2 - Trusted Boot Firmware,一般为Trusted Bootloader
  • BL31 - EL3 Runtime Frimware,一般为SML,管理SMC执行处理和中断,运行在secure monitor中
  • BL32 - Secure-EL1 Payload,一般为TEE OS Image
  • BL33 - Non - Trusted Firemware,一般为uboot

BL1:Trusted Boot ROM

启动最早的ROM,可以类比Boot Guard的ACM。不过它是在CPU的ROM里而不和BIOS在一起,是一切的信任根。

代码在如如下路径中:

bl1/aarch64/bl1_entrypoint.S

func bl1_entrypoint
	...
	bl	bl1_setup
	...
	bl	pauth_init_enable_el3
	...
	bl	bl1_main
	...
	b	el3_exit
endfunc bl1_entrypoint

bl1_main()开始就是c程序了,那c运行依靠的堆和栈空间在哪里呢?在CPU内部的SRAM里。SRAM一启动就已经可以访问了。

BL1主要目的是建立Trusted SRAM、exception vector、初始化串口console等等。然后找到并验证BL2(验签CSF头),然后跳过去。

BL1位于ROM中,在EL3下从reset vector处开始运行,做的主要工作有:

  • 决定启动路径:冷启动还是热启动。
  • 架构初始化:异常向量、CPU复位处理函数配置、控制寄存器设置(SCRLR_EL3/SCR_EL3/CPTR_EL3/DAIF)
  • 平台初始化:使能Trusted Watchdog、初始化控制台、配置硬件一致性互联、配置MMU、初始化相关存储设备。
  • 固件更新处理
  • BL2镜像加载和执行:
    • BL1输出“Booting Trusted Firmware"。
    • BL1加载BL2到SRAM;如果SRAM不够或者BL2镜像错误,输出“Failed to load BL2 firmware.”。
    • BL1切换到Secure EL1并将执行权交给BL2.

BL2:Trusted Boot Firmware

同样运行在EL3上的BL2和BL1一个显著的不同是它在Flash上,作为外置的一个Firmware,它的可信建立在BL1对它的验证上。它也有完整的源代码路径:

bl2/aarch64/

它会初始化一些关键安全硬件和软件框架。更重要的是在NXP2160A的分支中,BL2会初始化很多硬件,而这些硬件初始化在X86中由BIOS完成的,而在ARM的ATF体系中,很多种CPU是在BL2中完成的。2160A在Plat目录下提供了很多开源的硬件初始化代码,供ATF BL2框架代码调用。比较重要的是bl2_main():

void bl2_main(void)
{
	entry_point_info_t *next_bl_ep_info;

#if ENABLE_RUNTIME_INSTRUMENTATION
	PMF_CAPTURE_TIMESTAMP(bl_svc, BL2_ENTRY, PMF_CACHE_MAINT);
#endif

	NOTICE("BL2: %s\n", version_string);
	NOTICE("BL2: %s\n", build_message);

	/* Perform remaining generic architectural setup in S-EL1 */
	bl2_arch_setup();

#if PSA_FWU_SUPPORT
	fwu_init();
#endif /* PSA_FWU_SUPPORT */

	crypto_mod_init();

	/* Initialize authentication module */
	auth_mod_init();

	/* Initialize the Measured Boot backend */
	bl2_plat_mboot_init();

	/* Initialize boot source */
	bl2_plat_preload_setup();

	/* Load the subsequent bootloader images. */
	next_bl_ep_info = bl2_load_images();

	/* Teardown the Measured Boot backend */
	bl2_plat_mboot_finish();

#if !BL2_RUNS_AT_EL3
#ifndef __aarch64__
	/*
	 * For AArch32 state BL1 and BL2 share the MMU setup.
	 * Given that BL2 does not map BL1 regions, MMU needs
	 * to be disabled in order to go back to BL1.
	 */
	disable_mmu_icache_secure();
#endif /* !__aarch64__ */

#if ENABLE_PAUTH
	/*
	 * Disable pointer authentication before running next boot image
	 */
	pauth_disable_el1();
#endif /* ENABLE_PAUTH */

#if ENABLE_RUNTIME_INSTRUMENTATION
	PMF_CAPTURE_TIMESTAMP(bl_svc, BL2_EXIT, PMF_CACHE_MAINT);
#endif

	console_flush();

	/*
	 * Run next BL image via an SMC to BL1. Information on how to pass
	 * control to the BL32 (if present) and BL33 software images will
	 * be passed to next BL image as an argument.
	 */
	smc(BL1_SMC_RUN_IMAGE, (unsigned long)next_bl_ep_info, 0, 0, 0, 0, 0, 0);
#else /* if BL2_RUNS_AT_EL3 */

	NOTICE("BL2: Booting " NEXT_IMAGE "\n");
	print_entry_point_info(next_bl_ep_info);
#if ENABLE_RUNTIME_INSTRUMENTATION
	PMF_CAPTURE_TIMESTAMP(bl_svc, BL2_EXIT, PMF_CACHE_MAINT);
#endif
	console_flush();

#if ENABLE_PAUTH
	/*
	 * Disable pointer authentication before running next boot image
	 */
	pauth_disable_el3();
#endif /* ENABLE_PAUTH */

	bl2_run_next_image(next_bl_ep_info);
#endif /* BL2_RUNS_AT_EL3 */
}

最重要的两步都在这个函数中完成:初始化硬件和找到BL31。

bl2_plat_preload_setup()中会初始化一堆硬件,包括读取RCW初始化Serdes等,初始化DDR4的代码:dram_init(),它在Plat\nxp\drivers\ddr\nxp-ddr下。比较遗憾的是DDR4 PHY的代码是个Binary,不含源码,这里对DDR4的初始化仅仅聚焦设置timing寄存器和功能寄存器,而没有内存的Training过程。

x86带内初始化硬件的很多代码ARM ATF体系都包括在BL2中,而不在UEFI代码中,这是和x86 UEFI代码的一个显著区别。部分原因这些代码都要求是Secure的。更加糟糕的是,很多ARM平台,BL1和BL2,甚至后面的BL31都是以二进制的形式提供,让定制显得很困难。BL2能否提供足够的信息和定制化选择给固件厂商和提供足够信息给UEFI代码,考验BL2的具体设计实现。NXP在两个方面都做的不错,不但提供RCW等配置接口,还开源了大部分代码,十分方便。

BL2在初始化硬件后,开始寻找BL3的几个小兄弟:BL31,BL32和BL33。它先找到BL31,并验签它,最后转入BL31。

BL2位于SRAM中,运行在Secure EL1主要工作有:

  • 架构初始化:EL1/EL0使能浮点单元和ASMID。
  • 平台初始化:控制台初始化、相关存储设备初始化、MMU、相关设备安全配置、
  • SCP_BL2:系统控制核镜像加载,单独核处理系统功耗、时钟、复位等控制。
  • 加载BL31镜像:BL2将控制权交给BL1;BL1关闭MMU并关cache;BL1将控制权交给BL31。
  • 加载BL32镜像:BL32运行在安全世界,BL2依赖BL31将控制权交给BL32。SPSR通过Secure-EL1 Payload Dispatcher进行初始化。
  • 加载BL33镜像:BL2依赖BL31将控制权交给BL33。

BL31:EL3 Runtime Firmware

BL31作为EL3最后的安全堡垒,它不像BL1和BL2是一次性运行的。如它的runtime名字暗示的那样,它通过SMC为Non-Secure持续提供设计安全的服务。SMC负责找到BL32,验签,并运行BL32。

BL31位于SRAM中,EL3模式。除了做架构初始化和平台初始化外,还做了如下工作:

  • PSCI服务初始化,后续提供CPU功耗管理操作。
  • BL32镜像运行初始化,处于Secure EL1模式。
  • 初始化非安全EL2或EL1,跳转到BL33执行。
  • 负责安全非安全世界切换。
  • 进行安全服务请求的分发。

BL32:OPTee OS + 安全app

BL32实际上是著名的Open Portable Trusted Execution Enveiroment[[5]](https://zhuanlan.zhihu.com/p/391101179#ref_5) OS,它是由Linaro创立的。现在仅需要知道OPTee OS运行在 S-EL1,而其上的安全APP运行在S-EL0。OPTee OS运行完毕后,返回EL3的BL31,BL31找到BL33,验签它并运行。

BL33:Non-Trusted Firmware

BL33实际上就是UEFI firmware或者uboot,也有实现在这里直接放上Linux Kernel。2160A的实现是UEFI和uboot都支持。

BL1启动过程

总体流程图:

image

从bl1/bl.ld.S可以通过ENTRY定有可知,入口定义是bl1_entrypoint函数:

OUTPUT_FORMAT(PLATFORM_LINKER_FORMAT)
OUTPUT_ARCH(PLATFORM_LINKER_ARCH)
ENTRY(bl1_entrypoint)

他先初始化EL3环境,执行平台相关的初始化流程,然后加载下一阶段镜像,为其准备的参数,最后跳转到下一阶段镜像入口处运行。

1.el3_entrypoint_common

该函数是所有在EL3下执行镜像共享的,如BL1和BL31都会通过该函数初始化系统状态。该函数主要初始化系统的初始状态,执行一些必要的fixup操作,以及初始化c运行时环境和设置运行时的栈指针,为后续代码跳转到c语言执行准备条件

2.bl_setup

该函数主要执行一些平台相关的操作,如对于qemu平台会执行串口初始化、内存布局配置、MMU设置和data cache使能操作

3.bl_main

该函数主要用于bl2镜像加载以及跳转前的准备流程,如获取镜像参数、加载镜像内容、安全启动验签、bl2镜像跳转准备以及world switch上下文初始化等

4.el3_exit

该流程执行实际的上下文切换流程,包括保存当前EL3上下文以及跳转到bl2入口地址执行等。接下来我们将对以上流程进行更加详细的分析
  除了由硬件提供默认值的寄存器外,其它寄存器的值都处于不确定状态,因此在启动流程的初始阶段必须要先初始化这些寄存器,以将系统带到一个确定的运行状态。接下来需要设置cpu的异常处理程序和c运行时环境,为代码跳转到c语言做准备。最后,则需要加载BL2镜像,准备下一阶段启动所需的参数和跳转设置,并最终跳转到BL2的入口函数中执行。

参考文章:聊聊SOC启动(二) ATF BL1启动流程 - 知乎 (zhihu.com)

BL2启动过程

通用bl2的启动流程和bl1类似,主要区别是bl2的初始化流程比bl1更简单,但可能需要加载更多的镜像,比如bl31、bl32和bl33。

image

以上流程分为bl2基础初始化、bl2参数设置,bl2镜像加载和下一阶段镜像跳转这几部分

以上流程分为bl2基础初始化、bl2参数设置,bl2镜像加载和下一阶段镜像跳转这几部分

NXP S32G3的BL2流程图:

image

参考文章:

聊聊SOC启动(三) ATF BL2启动流程 - 知乎 (zhihu.com)

BL31启动过程

与bl1和bl2不同,bl31包含两部分功能,在启动时作为启动流程的一部分,执行软硬件初始化以及启动bl32和bl33镜像。在系统启动完成后,将继续驻留于系统中,并处理来自其它异常等级的smc异常,以及其它需要路由到EL3处理的中断等。因此bl31启动流程主要包含以下工作:

(1)cpu初始化

(2)c运行时环境初始化

(3)基本硬件初始化,如gic,串口,timer等

(4)页表创建和cache使能

(5)启动后级镜像的准备以及新镜像的跳转

(6)若bl31支持el3中断,则需要初始化中断处理框架

(7)运行时不同secure状态的smc处理,以及异常等级切换上下文的初始化

(8)用于处理smc命令的运行时服务注册

image

1.save_param参数保存

mov	x20, x0
mov	x21, x1
mov	x22, x2
mov	x23, x3

与bl2相同,将bl2传入的参数从caller寄存器保存到callee寄存器中。

2.el3_entrypoint_common函数

bl31对其的调用方式还是与bl1有所不同。以下是bl31的调用

#if !RESET_TO_BL31
	el3_entrypoint_common					\
		_init_sctlr=0					\
		_warm_boot_mailbox=0				\
		_secondary_cold_boot=0				\
		_init_memory=0					\
		_init_c_runtime=1				\
		_exception_vectors=runtime_exceptions		\
		_pie_fixup_size=BL31_LIMIT - BL31_BASE
#else
		el3_entrypoint_common					\
		_init_sctlr=1					\
		_warm_boot_mailbox=!PROGRAMMABLE_RESET_ADDRESS	\
		_secondary_cold_boot=!COLD_BOOT_SINGLE_CPU	\
		_init_memory=1					\
		_init_c_runtime=1				\
		_exception_vectors=runtime_exceptions		\
		_pie_fixup_size=BL31_LIMIT - BL31_BASE
	mov	x20, 0
	mov	x21, 0
	mov	x22, 0
	mov	x23, 0
#endif

根据是否设置了RESET_TO_BL31,该函数有两套不同的调用参数。这是因为atf支持两种启动方式:

1.启动从bl1开始执行,这是atf默认的启动方式。此时由于bl1已经执行过el3_entrypoint_common函数,系统基本配置都已经设置完成。因此像设置sctlr寄存器、热启动跳转处理、secondary cpu处理,以及内存初始化流程在bl1中都已经完成,bl31中就可以跳过它们了

2.支持从bl31开始启动的基础是armv8支持动态设置cpu的重启地址,armv8架构提供了RVBAR(reset vector base address register)寄存器用于设置reset时cpu的启动位置。该寄存器一共有三个:RVBAR_EL1、RVBAR_EL2和RVBAR_EL3,根据系统实现的最高异常等级确定使用哪一个。我们知道armv8重启总是从最高异常等级开始执行,因此我们只需要设置最高异常等级的RVBAR寄存器即可。由于bl31运行在el3下,故若我们需要支持启动从bl31开始,就可通过将地址设置到RVBAR_EL3寄存器实现。

若启动从bl31开始,则由于它是第一级启动镜像,因此el3_entrypoint_common需要从头设置系统状态,因此该函数中的sctlr寄存器、启动跳转处理、secondary cpu处理,以及内存初始化流程等都需要执行。

虽然el3_entrypoint_common需要做的工作有点多,但这种方式直接跳过了bl1和bl2两级启动流程,相比于第一种方式其启动速度要更快,这也是它的最大优势。

最后这种方式将参数保存寄存器x20 – x23的值清零也非常好理解,因为此时bl31是启动的第一级镜像,自然就没有前级镜像传递的参数,此时将这些值清零可避免后面参数解析时出现问题

BL31参数设置

1.bl31_early_platform_setup2

该函数先初始化qemu控制台,然后解析bl2传入的镜像描述链表参数,并将解析到的bl32和bl33镜像ep_info保存到全局变量中。其主要流程如下:

qemu_console_init();                                                     (1)
	bl_params_t *params_from_bl2 = (bl_params_t *)arg0;                      (2)
		…
	bl_params_node_t *bl_params = params_from_bl2->head;                      (3)
		while (bl_params) {                                               (4)
		if (bl_params->image_id == BL32_IMAGE_ID) {
			bl32_image_ep_info = *bl_params->ep_info;                 (5)
		}

		if (bl_params->image_id == BL33_IMAGE_ID){
			bl33_image_ep_info = *bl_params->ep_info;                  (6)
		}

		bl_params = bl_params->next_params_info;
	}
	if (!bl33_image_ep_info.pc)                                                 (7)
		panic();

(1)控制台初始化

(2)获取arg0传入的镜像描述参数指针

(3)获取镜像链表头节点

(4)遍历镜像链表

(5)若该链表中含有bl32镜像描述符,则将其ep_info保存到全局变量

(6)多该链表中含有bl33镜像描述符,同样将其ep_info保存到全局变量

(7)校验bl33镜像的入口地址

2.bl31_plat_arch_setup

该函数用于为bl31相关内存创建页表,并使能MMU和dcache:

void bl31_plat_arch_setup(void)
{
   qemu_configure_mmu_el3(BL31_BASE, (BL31_END - BL31_BASE),
   BL_CODE_BASE, BL_CODE_END,
   BL_RO_DATA_BASE, BL_RO_DATA_END,
   BL_COHERENT_RAM_BASE, BL_COHERENT_RAM_END);
}

bl31主处理函数

1.bl31_platform_setup

该函数是平台相关的,qemu平台的实现如下:

void bl31_platform_setup(void)
{
	plat_qemu_gic_init();                (1)
	qemu_gpio_init();                    (2)
}

(1)初始化gic,包括gic的distributor,redistributor,cpu interface等的初始化。关于bl31 gic和中断处理的详细流程

(2)初始化qemu平台的gpio,即为其设置gpio基地址和操作相关的回调函数

2.ehf初始化

efh用于初始化el3中断处理相关的功能。在gicv3中中断被分为三个group:group0、secure group1和non secure group 1。它们根据scr_el3的irq和fiq位配置不同可分别路由到不同的异常等级处理。Ehf用于处理group0中断,这种中断总是以fiq形式触发,通过设置scr_el3将其路由到el3处理就可以在bl31中处理这种类型中断了。

ehf初始化流程主要就是设置group 0的路由方式,并为其设置一个总的中断处理函数。其主要流程如下:

void __init ehf_init(void)
{
	unsigned int flags = 0;
	int ret __unused;
    …
	set_interrupt_rm_flag(flags, NON_SECURE);
	set_interrupt_rm_flag(flags, SECURE);                              (1)

	ret = register_interrupt_type_handler(INTR_TYPE_EL3,
			ehf_el3_interrupt_handler, flags);                 (2)
	assert(ret == 0);
}

(1)计算中断路由相关的flag

(2)设置EL3类型(group 0)中断的中断路由方式和bl31总的中断处理函数

bl31中断处理函数ehf_el3_interrupt_handler会由异常向量表处理流程调用,它会继续根据中断优先级调用实际每个优先级对应的处理函数。中断优先级对应处理函数的注册流程分为以下共有两步,以下是中断注册流程的示例:

ehf_pri_desc_t plat_exceptions[] = {
#if RAS_EXTENSION
	EHF_PRI_DESC(PLAT_PRI_BITS, PLAT_RAS_PRI),
#endif
#if SDEI_SUPPORT
	EHF_PRI_DESC(PLAT_PRI_BITS, PLAT_SDEI_CRITICAL_PRI),
	EHF_PRI_DESC(PLAT_PRI_BITS, PLAT_SDEI_NORMAL_PRI),
#endif
#if SPM_MM
	EHF_PRI_DESC(PLAT_PRI_BITS, PLAT_SP_PRI),
#endif
#ifdef PLAT_EHF_DESC
	PLAT_EHF_DESC,
#endif
};

EHF_REGISTER_PRIORITIES(plat_exceptions, ARRAY_SIZE(plat_exceptions), PLAT_PRI_BITS);

上面的例子中注册了RAS、SDEI等中断,并为它们分配了不同的优先级,但是此时只是为中断处理函数占了一个位,而并未实际定义。它们实际上要在驱动中通过ehf_register_priority_handler注册。如对于sdei,其注册流程如下:

void sdei_init(void)
{
	…
	ehf_register_priority_handler(PLAT_SDEI_CRITICAL_PRI,
			sdei_intr_handler);
	ehf_register_priority_handler(PLAT_SDEI_NORMAL_PRI,
			sdei_intr_handler);
}

当ehf_register_priority_handler注册完成后,理论上bl31就可以接收和处理el3中断了。但是实际上bl31正在执行时,PSTATE的irq和fiq中断掩码都是被mask掉的,即el3中断只有在cpu运行于低于EL3异常等级的时候才能真正被触发和处理

3.运行时服务初始化

前面我们提到bl31在系统初始化完成后还需要驻留系统,并处理来自低异常等级的smc异常,其异常处理流程被称为运行时服务。Arm为它们的使用场景定义了一系列的规范,分别用于处理类型不同的任务,如cpu电源管理规范PSCI、代理non secure world处理中断的软件事件代理规范SDEI,以及用于trust os相关调用的SPD等。显然这些服务被使用之前,其服务处理函数需要先注册到bl31中,运行时服务初始化流程即是用于该目的。

在分析运行时服务初始化流程之前,我们先看下其注册方式。以下是其注册接口DECLARE_RT_SVC的定义:

#define DECLARE_RT_SVC(_name, _start, _end, _type, _setup, _smch)	\
	static const rt_svc_desc_t __svc_desc_ ## _name			\                 (1
		__section("rt_svc_descs") __used = {			\                 (2.start_oen = (_start),				\
			.end_oen = (_end),				\
			.call_type = (_type),				\
			.name = #_name,					\
			.init = (_setup),				\
			.handle = (_smch)				\
		}

该接口定义了一个结构体__svc_desc_ ## _name,并将其放到了一个特殊的段rt_svc_descs中。这段的定义位于链接脚本头文件include/common/bl_common.ld.h中,其定义如下:

#define RT_SVC_DESCS                                    \
        . = ALIGN(STRUCT_ALIGN);                        \
        __RT_SVC_DESCS_START__ = .;                     \
        KEEP(*(rt_svc_descs))                           \
        __RT_SVC_DESCS_END__ = .;

即这些被注册的运行时服务结构体都被保存到以__RT_SVC_DESCS_START__开头,__RT_SVC_DESCS_END__结尾的rt_svc_descs段中,其数据可表示为如下结构:

image

因此若需要获取这些结构体指针,只需遍历这段地址就可以了。运行时服务初始化函数runtime_svc_init流即是如此,其定义如下:

void __init runtime_svc_init(void)
{
	…
	rt_svc_descs = (rt_svc_desc_t *) RT_SVC_DESCS_START;1for (index = 0U; index < RT_SVC_DECS_NUM; index++) {2rt_svc_desc_t *service = &rt_svc_descs[index];

			rc = validate_rt_svc_desc(service);3if (rc != 0) {
			ERROR("Invalid runtime service descriptor %p\n",
				(void *) service);
			panic();
		}

		if (service->init != NULL) {          
			rc = service->init();4if (rc != 0) {
				ERROR("Error initializing runtime service %s\n",
						service->name);
				continue;
			}
		}}
}

(1)获取rt_svc_descs段的起始地址RT_SVC_DESCS_START

(2)遍历该段中所有已注册rt_svc_desc_t结构体相应的运行时服务

(3)校验运行时服务有效性

(4)调用该服务对应的初始化回调,该回调函数是在DECLARE_RT_SVC注册宏中通过参数_setup传入的

启动bl33

bl33启动流程与前面各级镜像启动流程,类似,也是根据ep_info设置bl33的上下文、入口地址和参数,然后跳转到入口执行。大家有兴趣可以自行根据代码分析一下,这里不再赘述。好了,atf启动流程总算走完了,接下来就要跳到bl33(uboot)中了。

NXP S32G3的bl31的启动流程

image

PSCI启动

psci是arm提供的一套电源管理接口,可以用于以下场景:

1.cpu的idle管理

2.cpu hotplug以及secondary cpu启动

3.系统的shutdown和reset

但该接口不包含dvfs和设备电源管理(比如GPU之类的外设电源管理)功能

psci主要分为电源管理拓扑结构(power domain)、电源状态(power state)以及armv8安全扩展几个方面。

power domain

aarch64架构下每块soc可能会包含多个cluster(集群),而每个cluster又包含多个Core,它们共同组成了层次化的拓扑结构。如每个soc包含2个cluster,每个cluster又包含4个core。

image

由于其中每个core以及每个cluster的电源都可以独立地执行开关操作,因此若core0 – core3的电源都关闭了,则cluster 0的电源也可以被关闭以降低功耗。若core0 – core3中的任一个core需要上电,则显然cluster 0需要先上电。为了更好地进行层次化电源管理,psci在电源管理流程中将以上这些组件都抽象为power domain。如以下为上例的power domain层次结构:

image

其中system level用于管理整个系统的电源,cluster level用于管理某个特定cluster的电源,而core level用于管理一个单独core的电源。

power state

由于aarch64架构有多种不用的电源状态,不同电源状态的功耗和唤醒延迟不同。如standby状态会关闭power domain的clock,但并不关闭电源。因此它虽然消除了门电路翻转引起的动态功耗,但依然存在漏电流等引起的静态功耗。故其功耗相对较大,但相应地唤醒延迟就比较低。

而对于power down状态,会断开对应power domain的电源,因此其不仅消除了动态功耗,还消除了静态功耗,相应地其唤醒延迟就比较高了。

psci一共为power domain定义了四种power state:

(1)run:电源和时钟都打开,该domain正常工作

(2)standby:关闭时钟,但电源处于打开状态。其寄存器状态得到保存,打开时钟后就可继续运行。功耗相对较大,但唤醒延迟较低。arm执行wfi或wfe指令会进入该状态。

(3)retention:它将core的状态,包括调试设置都保存在低功耗结构中,并使其部分关闭。其状态在从低功耗变为运行时能自动恢复。从操作系统角度看,除了进入方法、延迟等有区别外,其它都与standby相同。它的功耗和唤醒延迟都介于standby和power down之间。

(4)power down:关闭时钟和电源。power domain掉电后,所有状态都丢失,上电以后软件必须重新恢复其状态。它的功耗最低,但唤醒延迟也相应地最高。

显然,power state的睡眠程度从run到power down逐步加深。而高层级power domain的power state不应低于低层级power domain。如以上例子中core 0 – core 2都为power down状态,而core 3为standby状态,则cluster 0不能为retention或power down状态。同样若cluster 0为standby状态,而cluster 1为run状态,则整个系统必须为run状态。

当然,若core 0 –core 3都为power down状态,则cluster 1保持其它状态除了增大功耗之外,并没有其它意义,因此也应该将其设置为power down状态。

为了达到上述约束,不同power domain之间的power state具有以下关系:

image

psci实现了父leve与子level之间的电源关系协调,如cluster 0中最后一个core被设置为power down状态后,psci就会将该cluster也设置为power donw状态。若其某一个core被设置为run状态,则psci会先将其对应cluster的状态设置为run,然后再设置对应core的电源状态,这也是psci名字的由来(power state coordinate interface)

armv8的安全扩展

psci的软件架构

由于psci是由linux内核调用bl31中的安全服务,实现cpu电源管理功能的。因此其软件架构包含三个部分:

(1)内核与bl31之间的调用接口规范

(2)内核中的架构

(3)bl31中的架构

psci接口规范

psci规定了linux内核调用bl31中电源管理相关服务的接口规范,它包含实现以下功能所需的接口:

(1)cpu idle管理

(2)向系统动态添加或从系统动态移除cpu,通常称为hotplug

(3)secondary cpu启动

(4)系统的shutdown和reset

内核中的psci架构

内核psci软件架构包含psci驱动和每个cpu的cpu_ops回调函数实现两部分。其中psci驱动实现了驱动初始化和psci相关接口实现功能,而cpu_ops回调函数最终也会调用psci驱动的接口。

参考文章:

linux cpu管理(三) psci启动 - 知乎 (zhihu.com)

小结

ATF整个信任链条是逐步建立的:

image

ATF的官网一张图包含了更多的信息:

image

关于ATF的UEFI启动路径,下面这张图可能更加简单明了:

image

参考文章:

ARM的安全启动—ATF/TF-A以及它与UEFI的互动 - 知乎 (zhihu.com)

# 学习对象在全民造车、造芯的大时代,在努力去解决卡脖子的时代,ASIC硬件、SOC底层软件、Linux Kernel等操作系统软件(内核/驱动)、软硬件方面的系统架构师等的岗位需求也越来越明显,社会一直都是非常缺人的,缺的是核心的那一小撮、领头的那一小撮,社会所缺的更是能够软硬件融合的那一小撮人……总之,要想在这个时代,站稳自己的脚跟,能够在大公司或行业上拥有一席之地,就必需深入学习底层技术原理,核心技术才是您的看家本领。本课程设计之初,主要针对SOC底层软件开发的者、系统开发者,或者励志成为这样的人。既适合资深/高级工程师来查缺补漏,又适合初级工程师入门。(理论上该课程和ASIC硬件电路设计无关,该课程偏软件,但事实购买该课程的做ASIC的同学已然超过了15%)适用人群1、芯片开发者(包括底层软件、或做ASIC硬件的)。不限行业,例如车、云、物联网、移动端等领域;2、汽车行业开发者(主机厂、tier1、SOC厂家、各级供应商);3、嵌入式开发者、kernel开发者、驱动、软件工程师;4、学生。既适合学生从入门到精通,也适合资深工程师查缺补漏;您的收益:1、全体系的掌握ARMv8/ARMv9的核心知识点(ARM基础、异常中断GIC、MMU/Cache、architecture...);2、掌握ARM架构、掌握SOC架构、掌握常规IP(gic、smmu、timer、AXI/ACE/CHI、TZC400...);3、快速熟悉常规系统软件(bootrom、spl、ATF、TEE、bootloader、kernel...), Secureboot安全启动...4、技术水平提升N个level, 掌握快速的学习方法;# 学习什么在ARM蓬勃发展的年代,不仅仅涉及到物联网IOT、移动领域(如手机)、汽车电子领域,现在还涉及到PC、服务器的,简直就是各行各业。ARMv8出来已经有10年了,ARMv9也2年时间了。在技术不断更新迭代的背景下,此时再去学习十五年前的ARMv7、二十年前的ARMv5/v6显然不是明智的选择。本课程主要基于当前最新的架构ARMv8的aarch64和ARMv9,如涉及具体的ARM Core IP主要还是以最新的ARM Core IP为主,软件架构也是以当前最主流的/未来所趋势的架构来讲解。以下也给大家列举初了一个ARM产品的timeline的总结(在本课程中有着大量的这种总结),从这张图中,您是可以清晰的看到本课程拥有独具一格的风格、拥有全网最新(且唯一)的资料总结或学习路线。# 本课程大纲和规划(课程持续更新中,课程总量统计:2022/10/02  当前是 61节课, 22小时)第一章:主要是快速学习: ARM简介、指令集、寄存器总结等。第二章:本系列视频的一大亮点,系统全面地讲解了arm异常中断gic等相关的软硬件知识,本人一直在倡导“学arm安全其实就是学arm架构,学arm架构其实就是学习arm的异常和中断”,异常中断是领着你进入架构的入门,是让你变成系统软硬件架构师的必走之路。第三章:安全专题,这也是本视频最核心的东西。因为你无论买书还是看博客等,你都很难找到讲解安全的教程,这里就是有和无的区别。本人系统的整理的安全的知识,带领你快速入门。第四章:mmu专题,透过事务看本质的讲解,白话式的演讲。在所有模块中,mmu也算是相对较简单模块。相信人人听得懂,人人学得会。第五章:cache专题,一切追求实事求是,不人云亦云,一切知识点都有迹可循,推翻了网络的很多观念。在众多模块中,cache算是一个比较难的模块。了解了cache后,才能算真正了解系统的软硬件架构。第六章:虚拟化,本人不擅长,会啥就随便讲点啥。(以后学会了再来补)第七章:architecture,就是零散和零碎的系统架构知识,如exclusive、arch timer、reset、系统启动、SOC设计、AMBA/AXI/ACE、DSU、WFE/WFI这样的。第八章: 新增的ARMv9 CCA/RME安全架构专题第九章:主要放置一些直播课。# 课程收益1、知道我学习什么,我要怎么去学习,从此之后有了一个明确的学习路线。2、认识一些共同目标的人,相互讨论问题,共同进步。勤学、共学、助学。3、ARM不再神秘,SOC不在神秘,让您短期内就能cover住全局4、熟悉ARM Architecture架构知识5、熟悉SOC架构知识6、熟悉主流的系统软件框架7、熟悉各项硬件原理和机制,如异常中断、MMU、cache、TLB、VMSA、Trustzone6、深入了解当前的系统架构、软硬件架构,能够看懂这些大家,将来也能够自己设计。7、熟悉系统的启动流程、Secureboot等8、熟悉各类标准和规范9、能够进入芯片厂商干活、能够在非芯片产生成为技术担当。10、学习资料的获取方法,会看11500多页的ARM手册,会看数以百计的ARM各项参考手册。 本课程会持续更新。也希望通过本课程的学习,能够让大家的ARMv8/ARMv9开发技术能有质的飞越,能找到自己心仪的工作。在购买之前,也建议大家看一看第一章第一节的课程介绍。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值