【ARM64 ATF 系列 2 -- ATF SMC 异常处理流程 2】

文章目录

SMC 触发及处理

Linux kernel 运行在 Non-Secure EL1,如果要进入TEE,首先需要调用汇编指令 smc 进入 EL3,由 monitor(ATF)来完成 Non-Secure world到 Secure world的切换。在 mtk 平台上函数 mt_secure_call 是进入EL3 的入口函数,它调用 smc 指令并通过x0~x3传入四个参数。其中x0中是多个位域的一个编码,根据它可以找到哪个service以及service中的哪一项服务。

static noinline int mt_secure_call(u64 function_id, u64 arg0, u64 arg1, u64 arg2)
{   
    register u64 reg0 __asm__("x0") = function_id;
    register u64 reg1 __asm__("x1") = arg0;
    register u64 reg2 __asm__("x2") = arg1;
    register u64 reg3 __asm__("x3") = arg2;
    int ret = 0;
 
    asm volatile ("smc    #0\n" : "+r" (reg0) :"r"(reg1), "r"(reg2), "r"(reg3));
 
    ret = (int)reg0;
    return ret;
}

前面运行指令smc触发一个同步异常,进入EL3异常向量表对应同步异常入口,如下(bl31/aarch64/runtime_exceptions.S

        /* ---------------------------------------------------------------------
         * The following code handles secure monitor calls.
         * Depending upon the execution state from where the SMC has been
         * invoked, it frees some general purpose registers to perform the
         * remaining tasks. They involve finding the runtime service handler
         * that is the target of the SMC & switching to runtime stacks (SP_EL0)
         * before calling the handler.
         *
         * Note that x30 has been explicitly saved and can be used here
         * ---------------------------------------------------------------------
         */
func smc_handler
smc_handler32:
        /* Check whether aarch32 issued an SMC64 */
        tbnz    x0, #FUNCID_CC_SHIFT, smc_prohibited

smc_handler64:
        /* NOTE: The code below must preserve x0-x4 */

        /*
         * 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.
         * Also set the PSTATE to a known state.
         */
        bl      prepare_el3_entry

#if ENABLE_PAUTH
        /* Load and program APIAKey firmware key */
        bl      pauth_load_bl31_apiakey
#endif

        /*
         * 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.
         */
        mov     x5, xzr
        mov     x6, sp
        /*
         * 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]

        /* Switch to SP_EL0 */
        msr     spsel, #MODE_SP_EL0

        /*
         * 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]

        /* Clear flag register */
        mov     x7, xzr

#if ENABLE_RME
        /* Copy SCR_EL3.NSE bit to the flag to indicate caller's security */
        ubfx    x7, x18, #SCR_NSE_SHIFT, 1

        /*
         * Shift copied SCR_EL3.NSE bit by 5 to create space for
         * SCR_EL3.NS bit. Bit 5 of the flag correspondes to
         * the SCR_EL3.NSE bit.
         */
        lsl     x7, x7, #5
#endif /* ENABLE_RME */

        /* Copy SCR_EL3.NS bit to the flag to indicate caller's security */
        bfi     x7, x18, #0, #1
        mov     sp, x12

        /* 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

        /* Load descriptor index from array of indices */
        adrp    x14, rt_svc_descs_indices
        add     x14, x14, :lo12:rt_svc_descs_indices
        ldrb    w15, [x14, x16]

        /* Any index greater than 127 is invalid. Check bit 7. */
        tbnz    w15, 7, smc_unknown

        /*
         * Get the descriptor using the index
         * x11 = (base + off), w15 = index
         *
         * handler = (base + off) + (index << log2(size))
         */
        adr     x11, (__RT_SVC_DESCS_START__ + RT_SVC_DESC_HANDLE)
        lsl     w10, w15, #RT_SVC_SIZE_LOG2
        ldr     x15, [x11, w10, uxtw]

        /*
         * 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
        blr     x15

        b       el3_exit

smc_unknown:
        /*
         * Unknown SMC call. Populate return value with SMC_UNK and call
         * el3_exit() which will restore the remaining architectural state
         * i.e., SYS, GP and PAuth registers(if any) prior to issuing the ERET
         * to the desired lower EL.
         */
        mov     x0, #SMC_UNK
        str     x0, [x6, #CTX_GPREGS_OFFSET + CTX_GPREG_X0]
        b       el3_exit

smc_prohibited:
        restore_ptw_el1_sys_regs
        ldp     x28, x29, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X28]
        ldr     x30, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_LR]
        mov     x0, #SMC_UNK
        exception_return

函数 smc_handler64主要做了下面事情:

  • 保存Non-Secure world中的 spsr_el3elr_el3scr_el3到栈中。
  • 根据 function_id 找到对应的 runtime service, 查找方法:
    Index = (function_id >> 24 & 0x3f) | ((function_id >> 31) << 6)
    Handler = __RT_SVC_DESCS_START__ + RT_SVC_DESC_HANDLE + rt_svc_descs_indices[Index] << 5
    __RT_SVC_DESCS_START__rt_svc_descs 的起始地址,RT_SVC_DESC_HANDLE 为服务处理函数 handle 在结构体rt_svc_desc 中的偏移,左移5,是因为结构体 rt_svc_desc 大小为 32字节。
  • 执行指令 blr x15 跳转到 runtime service 的处理函数 handler 中执行,runtime service 的注册一般都是通过宏DECLARE_RT_SVC(_name, _start, _end, _type, _setup, _handle), 注册四填入的最后一个参数即是对应的中断处理函数。

smc_handler32 中使用的宏定义
include/common/runtime_svc.h

/*
 * Constants to allow the assembler access a runtime service
 * descriptor
 */
#ifdef __aarch64__
#define RT_SVC_SIZE_LOG2        U(5)
#define RT_SVC_DESC_INIT        U(16)
#define RT_SVC_DESC_HANDLE      U(24)
#else
#define RT_SVC_SIZE_LOG2        U(4)
#define RT_SVC_DESC_INIT        U(8)
#define RT_SVC_DESC_HANDLE      U(12)
#endif /* __aarch64__ */
#define SIZEOF_RT_SVC_DESC      (U(1) << RT_SVC_SIZE_LOG2)

include/lib/smccc.h

/*******************************************************************************
 * Bit definitions inside the function id as per the SMC calling convention
 ******************************************************************************/
#define FUNCID_TYPE_SHIFT               U(31)
#define FUNCID_TYPE_MASK                U(0x1)
#define FUNCID_TYPE_WIDTH               U(1)

#define FUNCID_CC_SHIFT                 U(30)
#define FUNCID_CC_MASK                  U(0x1)
#define FUNCID_CC_WIDTH                 U(1)

#define FUNCID_OEN_SHIFT                U(24)
#define FUNCID_OEN_MASK                 U(0x3f)
#define FUNCID_OEN_WIDTH                U(6)

#define FUNCID_NUM_SHIFT                U(0)
#define FUNCID_NUM_MASK                 U(0xffff)
#define FUNCID_NUM_WIDTH                U(16)

#define GET_SMC_NUM(id)                 (((id) >> FUNCID_NUM_SHIFT) & \
                                         FUNCID_NUM_MASK)
#define GET_SMC_TYPE(id)                (((id) >> FUNCID_TYPE_SHIFT) & \
                                         FUNCID_TYPE_MASK)
#define GET_SMC_CC(id)                  (((id) >> FUNCID_CC_SHIFT) & \
                                         FUNCID_CC_MASK)
#define GET_SMC_OEN(id)                 (((id) >> FUNCID_OEN_SHIFT) & \
                                         FUNCID_OEN_MASK)
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
# 学习对象在全民造车、造芯的大时代,在努力去解决卡脖子的时代,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
发出的红包

打赏作者

主公CodingCos

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值