文章目录 1、ATF里都有什么? 2、ATF的编译 3、ATF的启动 4、进入ATF的和退出ATF方式 (1)、进入ATF的方式 (2)、退出ATF的方式 5、ATF中向量表的介绍 6、ATF中栈的设置 7、ATF中寄存器的保存和恢复 8、ATF的rt_svc介绍(runtime service) (1)、SMC Calling convention文档 (2)、DECLARE_RT_SVC的使用 (3)、DECLARE_RT_SVC的定义 (4)、在同步异常中smc_handler64,跳转到响应的rt_svc (5)、smc在驱动中的调用 5、smc流程下的代码分析

1、ATF里都有什么? 最初的功能很简单:

  • cpu_context的保存和恢复,即: 双系统的切换

  • 电源管理、PSCI等 但是随着技术的发展,功能也越来越多,越来越复杂,以下列举了当前的部分功能:

  • 安全世界的初始化,例如异常向量表、一些控制寄存器和中断寄

  • CPU reset和power down的时序。包括Arm DynamIQ cpu的支持。

  • 标准的system IP的驱动,例如Generic Interrupt Controller (GIC), Cache Coherent Interconnect (CCI), Cache Coherent Network (CCN), Network Interconnect (NIC) and TrustZone Controller (TZC).

  • 一种通用的SCMI驱动程序, 适用于电源控制接口,例如ARM SYSTEM Control Processor(SCP)

  • smc处理,using an EL3 runtime services framework

  • PSCI库的支持,用于CPU/Cluster/system的电源管理,这个库集成到了aarch64 el3的runtime中,也适用于aarch32 el3

  • secure monitor代码,用于world切换、中断routing

  • SPDs for the OP-TEE Secure OS, NVIDIA Trusted Little Kernel and Trusty Secure OS

  • SecureBoot实现

  • 预集成TBB与Arm CryptoCell产品,利用其硬件Root的信任和加密加速服务。 如需更详细,请参考《ATF里面都有什么?》 一文

2、ATF的编译 不同平台之间的设计肯定都是不一样的,但大多数类似如下,请注意 RESET_TO_BL31=1,表示该ATF从BL31启动。

  • make -C $DIRPATH RESET_TO_BL31=1 PLAT=xxx clean
  • make -C $DIRPATH RESET_TO_BL31=1 PLAT=xxx HIGHADDR_DEVICE=1 all

3、ATF的启动 废话不多说,直接上图,请自行理解:BL1 BL2 BL31 BL32 BL33的概念、EL3 S-EL1 NS-EL1的概念。image.png 4、进入ATF的和退出ATF方式 透过事务看本质, 进入和退出ATF,就是就是EL等级切换的过程,那么EL等级都是怎么切换的呢?通过下面一张图就可以说明这一切: image.png (事实上除了以上同步异常指令,如果是触发异步异常,也会trapped到ATF)

(1)、进入ATF的方式 进入ATF的方式触发异常:同步异常SMC、异步异常(irq,fiq)

➨ 如果是同步异常,那么一定是在linux或tee中发生了smc调用,此时进入跳转ATF中异常向量表中的同步异常程序smc_handler64或smc_handler32 . 在该程序中,解析smc id,来选择跳转到具体哪一个rt-svc(runtime service)

➨ 如果是异步异常,那么一定是触发了irq或fiq或serror中断等,此时进入跳转ATF中异常向量表中的异步异常程序,进而跳转到响应的中断处理函数. . 在ATF中仅实现irq_aarch64、fiq_aarch64、irq_aarch32、fiq_aarch32 四个异常中断处理函数

  1. vector_entry irq_aarch64
  2. check_and_unmask_ea
  3. handle_interrupt_exception irq_aarch64
  4. end_vector_entry irq_aarch64
  5. vector_entry fiq_aarch64
  6. check_and_unmask_ea
  7. handle_interrupt_exception fiq_aarch64
  8. end_vector_entry fiq_aarch64
  9. vector_entry irq_aarch32
  10. check_and_unmask_ea
    
  11. handle_interrupt_exception irq_aarch32
    
  12. end_vector_entry irq_aarch32
  13. vector_entry fiq_aarch32
  14. check_and_unmask_ea
    
  15. handle_interrupt_exception fiq_aarch32
    
  16. end_vector_entry fiq_aarch32 在中断函数中,先调用plat_ic_get_pending_interrupt_type获取interrupt_type,其实就是通过读取寄存器read_icc_hppir0_el1() ,判断中断是从那里来的,然后返回下面三种interrupt_type:

#define INTR_TYPE_S_EL1 U(0) #define INTR_TYPE_EL3 U(1) #define INTR_TYPE_NS U(2) 有了type,再get_interrupt_type_handler获取handler程序,进而跳转到相应的handler程序。

ATF中中断的注册(这三种类型的handler程序的注册),以INTR_TYPE_S_EL1为例: 在开机bl32_main调用opteed_setup()时,将opteed_sel1_interrupt_handler()函数注册成了INTR_TYPE_S_EL1类型中断,同时也会将REE(Linux)使用的SCR_EL3.FIQ配置成1, 也意味着当CPU运行在TEE时,来了一个secure group1的中断,此中断在REE中被标记FIQ后将被target到EL3,进入EL3(ATF)的中断处理函数,也就是刚才注册的opteed_sel1_interrupt_handler()函数,在该函数中,会将cpu切换到TEE中,去处理这个中断;

(2)、退出ATF的方式 进入EL3(ATF)的方式触发异常:ERET指令、或是主动修改PSTATE寄存器

在ATF中执行smc_handler或中断handler结束后,会调用el3_exit,el3_exit会调用ERET指令,恢复Secure或non-secure的PC指针和PSTATE,回到secure EL1或non-secure EL1.

下图是一个smc进入ATF,处理完任务后再返回EL1的过程: image.png 5、ATF中向量表的介绍 请参考《Linux Kernel/optee/ATF等操作系统的异常向量表的速查》 一文

6、ATF中栈的设置 请参考《思想解读:TF-A(ATF)中栈指针和栈内存的设计思想解读》 一文

7、ATF中寄存器的保存和恢复 请参考《TF-A代码阅读: 双系统切换时是如何保存寄存器的(cpu_context介绍)》 一文

8、ATF的rt_svc介绍(runtime service) image.png (1)、SMC Calling convention文档 image.png

我们重点看下这张表,对应smc id的定义

bit31决定是fast call,还是std call(yield对应的就是std call) bit30表示是以32位传参,还是以64位传参, 注意我们看了optee在linux的driver,都是以32位方式 bit29:24 决定服务的类型 bit23:16 reserved bit15:0 每种call类型下,表示range bit31、bit30、bit23:16、bit15:0 都是很好理解,我们来讲一下bit29:24 在ATF中定义rt_svc(runtime service)时,需按照该文档的描述来定义

例如在opteed_main.c中,定义了一个service,它的call类型是OEN_TOS_START–OEN_TOS_END,对应的恰好是bit29:24 = 50–63

  1. DECLARE_RT_SVC(
  2. opteed_fast,
  3. OEN_TOS_START,
  4. OEN_TOS_END,
  5. SMC_TYPE_FAST,
  6. opteed_setup,
  7. opteed_smc_handler
  8. ); 那么我们在linux kernel中,调用smc时的smc id的bit29:24需要等于50,那么此次的smc调用才会调用到这个runtime service的handler程序

例如在arm_arch_svc_setup.c中,定义了一个service,它的call类型是OEN_ARM_START–OEN_ARM_END,对应的恰好是bit29:24 = 0–0

  1. /* Register Standard Service Calls as runtime service */
  2. DECLARE_RT_SVC(
  3.  arm_arch_svc,
    
  4.  OEN_ARM_START,
    
  5.  OEN_ARM_END,
    
  6.  SMC_TYPE_FAST,
    
  7.  NULL,
    
  8.  arm_arch_svc_smc_handler
    
  9. ); 那么我们在linux kernel中,调用smc时的smc id的bit29:24需要等于0,那么此次的smc调用才会调用到这个runtime service的handler程序

例如mtk代码中,mtk_sip_svc.c中,定义了一个service,它的call类型是OEN_SIP_START–OEN_SIP_END,对应的恰好是bit29:24 = 2–2

  1. /* Define a runtime service descriptor for fast SMC calls */
  2. DECLARE_RT_SVC(
  3. mediatek_sip_svc,
  4. OEN_SIP_START,
  5. OEN_SIP_END,
  6. SMC_TYPE_FAST,
  7. NULL,
  8. sip_smc_handler
  9. ); 那么我们在linux kernel中,调用smc时的smc id的bit29:24需要等于2,那么此次的smc调用才会调用到这个runtime service的handler程序

(2)、DECLARE_RT_SVC的使用 DECLARE_RT_SVC是一个宏,用于定义一组service,例如在opteed_main.c中的使用

  1. /* Define an OPTEED runtime service descriptor for fast SMC calls */
  2. DECLARE_RT_SVC(
  3. opteed_fast,
  4. OEN_TOS_START,
  5. OEN_TOS_END,
  6. SMC_TYPE_FAST,
  7. opteed_setup,
  8. opteed_smc_handler
  9. );
  10. /* Define an OPTEED runtime service descriptor for yielding SMC calls */
  11. DECLARE_RT_SVC(
  12. opteed_std,
    
  13. OEN_TOS_START,
    
  14. OEN_TOS_END,
    
  15. SMC_TYPE_YIELD,
    
  16. NULL,
    
  17. opteed_smc_handler
    
  18. );
  19. 其中OEN_TOS_START和OEN_TOS_END、SMC_TYPE_FAST和SMC_TYPE_YIELD都是按照SMC Calling convention文档来定义的
  20. #define OEN_ARM_START U(0)
  21. #define OEN_ARM_END U(0)
  22. #define OEN_CPU_START U(1)
  23. #define OEN_CPU_END U(1)
  24. #define OEN_SIP_START U(2)
  25. #define OEN_SIP_END U(2)
  26. #define OEN_OEM_START U(3)
  27. #define OEN_OEM_END U(3)
  28. #define OEN_STD_START U(4) /* Standard Service Calls */
  29. #define OEN_STD_END U(4)
  30. #define OEN_STD_HYP_START U(5) /* Standard Hypervisor Service calls */
  31. #define OEN_STD_HYP_END U(5)
  32. #define OEN_VEN_HYP_START U(6) /* Vendor Hypervisor Service calls */
  33. #define OEN_VEN_HYP_END U(6)
  34. #define OEN_TAP_START U(48) /* Trusted Applications */
  35. #define OEN_TAP_END U(49)
  36. #define OEN_TOS_START U(50) /* Trusted OS */
  37. #define OEN_TOS_END U(63)
  38. #define OEN_LIMIT U(64)
  39. #define SMC_TYPE_FAST ULL(1)
  40. #define SMC_TYPE_YIELD ULL(0) SMC_TYPE_FAST和SMC_TYPE_YIELD也是根据SMC Calling convention文档定义

(3)、DECLARE_RT_SVC的定义 在runtime_svc.h中,其实就是在section(“rt_svc_descs”)段中定义了一个全局变量.

  1. /*
    • Convenience macros to declare a service descriptor
  2. */
  3. #define DECLARE_RT_SVC(_name, _start, _end, _type, _setup, _smch) \
  4. static const rt_svc_desc_t _svc_desc ## _name \
  5.  __section("rt_svc_descs") __used = {			\
    
  6.  	.start_oen = (_start),				\
    
  7.  	.end_oen = (_end),				\
    
  8.  	.call_type = (_type),				\
    
  9. 		.name = #_name,					\
    
  10. 		.init = (_setup),				\
    
  11. 		.handle = (_smch)				\
    
  12. 	}
    

section “rt_svc_descs”在RT_SVC_DESCS宏中

  1. #define RT_SVC_DESCS \
  2. . = ALIGN(STRUCT_ALIGN); \
  3. RT_SVC_DESCS_START = .; \
  4. KEEP(*(rt_svc_descs)) \
  5. RT_SVC_DESCS_END = .; 而在rodata_common的宏中,定义了RT_SVC_DESCS
  6. #define RODATA_COMMON \
  7. RT_SVC_DESCS \
  8. FCONF_POPULATOR \
  9. PMF_SVC_DESCS \
  10. PARSER_LIB_DESCS \
  11. CPU_OPS \
  12. GOT \
  13. BASE_XLAT_TABLE_RO

在bl31.ld.S中,将RODATA_COMMON放入了rodata段

  1. .rodata . : {
    
  2.     __RODATA_START__ = .;
    
  3.     *(SORT_BY_ALIGNMENT(.rodata*))
    
  4. RODATA_COMMON
  5.     /* Place pubsub sections for events */
    
  6.     . = ALIGN(8);
    
  7. #include <lib/el3_runtime/pubsub_events.h>
  8.     . = ALIGN(PAGE_SIZE);
    
  9.     __RODATA_END__ = .;
    
  10. } >RAM
    

(4)、在同步异常中smc_handler64,跳转到响应的rt_svc 附上完整代码和注释

  1. smc_handler64:
  2. /* NOTE: The code below must preserve x0-x4 */
  3. /*
    • Save general purpose and ARMv8.3-PAuth registers (if enabled).
    • If Secure Cycle Counter is not disabled in MDCR_EL3 when
    • ARMv8.5-PMU is implemented, save PMCR_EL0 and disable Cycle Counter.
  4. */
  5. bl save_gp_pmcr_pauth_regs
  6. #if ENABLE_PAUTH
  7. /* Load and program APIAKey firmware key */
    
  8. bl	pauth_load_bl31_apiakey
    
  9. #endif
  10. /*
    
  11.  * Populate the parameters for the SMC handler.
    
  12.  * We already have x0-x4 in place. x5 will point to a cookie (not used
    
  13.  * now). x6 will point to the context structure (SP_EL3) and x7 will
    
  14.  * contain flags we need to pass to the handler.
    
  15.  */
    
  16. mov	x5, xzr
    
  17. mov	x6, sp
    
  18. /*
    
  19.  * Restore the saved C runtime stack value which will become the new
    
  20.  * SP_EL0 i.e. EL3 runtime stack. It was saved in the 'cpu_context'
    
  21.  * structure prior to the last ERET from EL3.
    
  22.  */
    
  23. ldr	x12, [x6, #CTX_EL3STATE_OFFSET + CTX_RUNTIME_SP]
    
  24. /* Switch to SP_EL0 */
    
  25. msr	spsel, #MODE_SP_EL0
    
  26. /*
    
  27.  * Save the SPSR_EL3, ELR_EL3, & SCR_EL3 in case there is a world
    
  28.  * switch during SMC handling.
    
  29.  * TODO: Revisit if all system registers can be saved later.
    
  30.  */
    
  31. mrs	x16, spsr_el3
    
  32. mrs	x17, elr_el3
    
  33. mrs	x18, scr_el3
    
  34. stp	x16, x17, [x6, #CTX_EL3STATE_OFFSET + CTX_SPSR_EL3]
    
  35. str	x18, [x6, #CTX_EL3STATE_OFFSET + CTX_SCR_EL3]
    
  36. /* Copy SCR_EL3.NS bit to the flag to indicate caller's security */
    
  37. bfi	x7, x18, #0, #1
    
  38. mov	sp, x12
    
  39. /* Get the unique owning entity number */
    
  40. ubfx	x16, x0, #FUNCID_OEN_SHIFT, #FUNCID_OEN_WIDTH  ---------------- 获取FUNCID_OEN_SHIFT,对应第一节中的OEN_TOS_START
    
  41. ubfx	x15, x0, #FUNCID_TYPE_SHIFT, #FUNCID_TYPE_WIDTH  ---------------- 获取FUNCID_TYPE_SHIFT,对应第一节中的SMC_TYPE_FAST(fast还是yield,yield其实就是standard)
    
  42. orr	x16, x16, x15, lsl #FUNCID_OEN_WIDTH
    
  43. /* Load descriptor index from array of indices */
    
  44. adrp	x14, rt_svc_descs_indices  ----在runtime_svc_init()中会将所有的section rt_svc_descs段放入rt_svc_descs_indices数组,这里获取该数组地址
    
  45. add	x14, x14, :lo12:rt_svc_descs_indices
    
  46. ldrb	w15, [x14, x16]   ---找到rt_svc在rt_svc_descs_indices数组中的index
    
  47. /* Any index greater than 127 is invalid. Check bit 7. */
    
  48. tbnz	w15, 7, smc_unknown
    
  49. /*
    
  50.  * Get the descriptor using the index
    
  51.  * x11 = (base + off), w15 = index    -------------------------重要的注释
    
  52.  *
    
  53.  * handler = (base + off) + (index << log2(size))  ------ 这句注释特别重要,整段汇编看不懂没关系,这句注释看懂就行
    
  54.  */
    
  55. adr	x11, (__RT_SVC_DESCS_START__ + RT_SVC_DESC_HANDLE)
    
  56. lsl	w10, w15, #RT_SVC_SIZE_LOG2
    
  57. ldr	x15, [x11, w10, uxtw]    ------------------------------这句话对应的就是上述注释:handler = (base + off) + (index << log2(size))
    
  58. /*
    
  59.  * Call the Secure Monitor Call handler and then drop directly into
    
  60.  * el3_exit() which will program any remaining architectural state
    
  61.  * prior to issuing the ERET to the desired lower EL.
    
  62.  */
    
  63. #if DEBUG
  64. cbz	x15, rt_svc_fw_critical_error
    
  65. #endif
  66. blr	x15     -------------------------------------跳转到handler
    
  67. b	el3_exit
    

(5)、smc在驱动中的调用 在optee_smc.h中,我们可以查看linux kernel中给driver定义的smc的类型有:

首先是两个宏,一个用于定义fast call,一个用于定义std call

  1. #define OPTEE_SMC_STD_CALL_VAL(func_num) \
  2. ARM_SMCCC_CALL_VAL(ARM_SMCCC_STD_CALL, ARM_SMCCC_SMC_32, \
  3.  	   ARM_SMCCC_OWNER_TRUSTED_OS, (func_num))
    
  4. #define OPTEE_SMC_FAST_CALL_VAL(func_num) \
  5. ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, ARM_SMCCC_SMC_32, \
  6.  	   ARM_SMCCC_OWNER_TRUSTED_OS, (func_num))
    

std call只有两个cmd,一个用于正向调用,一个用于rpc调用

  1. #define OPTEE_SMC_FUNCID_RETURN_FROM_RPC 3
  2. #define OPTEE_SMC_CALL_RETURN_FROM_RPC \
  3. OPTEE_SMC_STD_CALL_VAL(OPTEE_SMC_FUNCID_RETURN_FROM_RPC)
  4. #define OPTEE_SMC_FUNCID_CALL_WITH_ARG OPTEE_MSG_FUNCID_CALL_WITH_ARG
  5. #define OPTEE_SMC_CALL_WITH_ARG \
  6. OPTEE_SMC_STD_CALL_VAL(OPTEE_SMC_FUNCID_CALL_WITH_ARG)
  7. fast call有5个分别用于: get_os_uuid、get_shm_config、exchange_capabilities、disable_shm_cache、enable_shm_cache
  8. #define OPTEE_SMC_FUNCID_GET_OS_UUID OPTEE_MSG_FUNCID_GET_OS_UUID
  9. #define OPTEE_SMC_CALL_GET_OS_UUID \
  10. OPTEE_SMC_FAST_CALL_VAL(OPTEE_SMC_FUNCID_GET_OS_UUID)
  11. #define OPTEE_SMC_FUNCID_GET_SHM_CONFIG 7
  12. #define OPTEE_SMC_GET_SHM_CONFIG \
  13. OPTEE_SMC_FAST_CALL_VAL(OPTEE_SMC_FUNCID_GET_SHM_CONFIG)
  14. #define OPTEE_SMC_FUNCID_EXCHANGE_CAPABILITIES 9
  15. #define OPTEE_SMC_EXCHANGE_CAPABILITIES \
  16. OPTEE_SMC_FAST_CALL_VAL(OPTEE_SMC_FUNCID_EXCHANGE_CAPABILITIES)
    
  17. #define OPTEE_SMC_FUNCID_DISABLE_SHM_CACHE 10
  18. #define OPTEE_SMC_DISABLE_SHM_CACHE \
  19. OPTEE_SMC_FAST_CALL_VAL(OPTEE_SMC_FUNCID_DISABLE_SHM_CACHE)
    
  20. #define OPTEE_SMC_FUNCID_ENABLE_SHM_CACHE 11
  21. #define OPTEE_SMC_ENABLE_SHM_CACHE \
  22. OPTEE_SMC_FAST_CALL_VAL(OPTEE_SMC_FUNCID_ENABLE_SHM_CACHE)
    

5、smc流程下的代码分析 image.png