ARM Trusted Firmware分析——中断、异常

发布时间 2023-08-21 09:33:18作者: ArnoldLu

 

中断如何送到不同EL?如何配置?

1. BL31异常向量表

BL31异常向量表根据如下定义实现:

ARMv8-A规定Exception Vector Table大小为2KB,并且是2KB对齐;一共6个Vector Entry,每一个Vector Entry大小为128B。

    .macro vector_base  label, section_name=.vectors
    .section \section_name, "ax"
    .align 11, 0------------------------------------------定义Exception Vector Table,2KB对齐。
    \label:
    .endm

    .macro vector_entry  label, section_name=.vectors-----创建一个Exception Vector Entry,128B对齐,大小也为128B。
    .cfi_sections .debug_frame
    .section \section_name, "ax"
    .align 7, 0
    .type \label, %function
    .cfi_startproc
    \label:
    .endm

    .macro end_vector_entry label------------------------一个Exception Vector Entry大小为128B,不足部分用0补齐。超出128B,则报错。
    .cfi_endproc
    .fill    \label + (32 * 4) - .
    .endm

vector_base定义了Exception Vector Table基地址,vector_entry/end_vector_entry定义了Exception Vector Entry。

vector_base runtime_exceptions

    /* ---------------------------------------------------------------------
     * Current EL with SP_EL0 : 0x0 - 0x200
     * ---------------------------------------------------------------------
     */
vector_entry sync_exception_sp_el0
    b    report_unhandled_exception
end_vector_entry sync_exception_sp_el0

vector_entry irq_sp_el0
    b    report_unhandled_interrupt
end_vector_entry irq_sp_el0

vector_entry fiq_sp_el0
    b    report_unhandled_interrupt
end_vector_entry fiq_sp_el0

vector_entry serror_sp_el0
    no_ret    plat_handle_el3_ea--------------------------------------调用report_unhandled_exception。
end_vector_entry serror_sp_el0

    /* ---------------------------------------------------------------------
     * Current EL with SP_ELx: 0x200 - 0x400
     * ---------------------------------------------------------------------
     */
vector_entry sync_exception_sp_elx
    b    report_unhandled_exception
end_vector_entry sync_exception_sp_elx

vector_entry irq_sp_elx
    b    report_unhandled_interrupt
end_vector_entry irq_sp_elx

vector_entry fiq_sp_elx
    b    report_unhandled_interrupt
end_vector_entry fiq_sp_elx

vector_entry serror_sp_elx
    no_ret    plat_handle_el3_ea--------------------------------------调用report_unhandled_exception。
end_vector_entry serror_sp_elx

    /* ---------------------------------------------------------------------
     * Lower EL using AArch64 : 0x400 - 0x600
     * ---------------------------------------------------------------------
     */
vector_entry sync_exception_aarch64
    apply_at_speculative_wa
    check_and_unmask_ea
    handle_sync_exception---------------------------------------------处理SMC指令或者External Aborts。
end_vector_entry sync_exception_aarch64

vector_entry irq_aarch64
    apply_at_speculative_wa
    check_and_unmask_ea
    handle_interrupt_exception irq_aarch64
end_vector_entry irq_aarch64

vector_entry fiq_aarch64
    apply_at_speculative_wa
    check_and_unmask_ea
    handle_interrupt_exception fiq_aarch64
end_vector_entry fiq_aarch64

vector_entry serror_aarch64
    apply_at_speculative_wa
    msr    daifclr, #DAIF_ABT_BIT
    b    enter_lower_el_async_ea
end_vector_entry serror_aarch64

    /* ---------------------------------------------------------------------
     * Lower EL using AArch32 : 0x600 - 0x800
     * ---------------------------------------------------------------------
     */
...

bl2_entrypoint中将early_exceptions异常向量表设置到vbar_el1异常向量表中。

tsp_entrypoint中将tsp_exceptions异常向量表设置到vbar_el1异常向量表中,替换early_exception。同时将tsp_vector_table地址返回给BL31。

func bl2_entrypoint
...
    /* ---------------------------------------------
     * Set the exception vector to something sane.
     * ---------------------------------------------
     */
    adr    x0, early_exceptions
    msr    vbar_el1, x0
    isb
...
endfunc bl2_entrypoint

func tsp_entrypoint _align=3
...
    /* ---------------------------------------------
     * Set the exception vector to something sane.
     * ---------------------------------------------
     */
    adr    x0, tsp_exceptions
    msr    vbar_el1, x0
    isb
...
    /* ---------------------------------------------
     * Jump to main function.
     * ---------------------------------------------
     */
    bl    tsp_main----------------------------------------------返回值为tsp_vector_table的地址。

    /* ---------------------------------------------
     * Tell TSPD that we are done initialising
     * ---------------------------------------------
     */
    mov    x1, x0-----------------------------------------------x1为tsp_vector_table地址。
    mov    x0, #TSP_ENTRY_DONE----------------------------------x0为TSP_ENTRY_DONE,即SMC第一个参数SMC FID。
    smc    #0---------------------------------------------------执行smc从S.EL1进入EL3。

tsp_entrypoint_panic:
    b    tsp_entrypoint_panic
endfunc tsp_entrypoint

 

2. 异常处理框架

参考文档:《Exception Handling Framework》。

本文主要介绍除SMC之外的指向EL3的由BL31处理的异常。本文主要覆盖如下指向EL3的异常:

  • 中断
  • 同步外部异常
  • 异步外部异常

当EL3_EXCEPTION_HANDLING置为1时,EL3将会处理相关异常。

4.1 Introduction

通过配置SCR_EL3寄存器,ARM架构允许异步异常被路由到EL3。

基于选择的中断路由模型,TF-A适当的配置SCR_EL3的FIQ和IRQ来决定次路由。

除非为了实现安全和非安全世界切换,大多数情况下,路由到EL3的FIQ和IRQ不需要在EL3中处理。

4.2 The role of Exception Handling Framework

一个实例:runtime_exceptions(BL31) -> irq_aarch64/fiq_aarch64(BL31) -> handle_interrupt_exception(BL31) -> tspd_sel1_interrupt_handler/tspd_ns_interrupt_handler(BL31) -> tsp_sel1_intr_entry(BL32)。

3. 中断管理框架

参考文档:《Interrupt Management Framework》。

FEL:First Exception Level capable of handling interrupts。第一个能够处理中断的EL。

TEL3:Target Exception Level 3

CSS:Current Security State

此框架负责管理路由到EL3的中断,允许EL3软件配置中断路由行为。他主要目标是实现如下两个需求:

  • 当CPU处于非安全状态时,负责将由安全软件处理的中断路由到EL3。
  • 当CPU处于EL3以下安全状态时,负责将由非安全软件处理的中断路由到安全世界最近执行EL。

5.1 Concepts

5.1.1 Interrupt types

此框架根据中断在哪个EL中进行处理划分为如下几种:

  • S.EL1中断。
  • NS中断。
  • EL3中断。

5.1.2 Routing model

一个中断可以作为IRQ或者FIQ,中断的目标EL可以通过SCR_EL3.IRQ和SCR_EL3.FIQ进行配置。

SCR_EL3.FIQ=1,FIQ被路由到EL3。

SCR_EL3.IRQ=1,IRQ被路由到EL3。

5.1.3 Valid routing models

CSS:0表示安全状态,1表示非安全状态。

TEL3:0表示目标位FEL,1表示目标为EL3。

 

Secure-EL1 interrupts

Non-secure interrupts

EL3 interrupts

CSS=0 TEL3=0

执行于安全状态时,路由到FEL

valid:安全软件处理安全中断

执行于安全状态时,路由到FEL

valid:安全软件控制自身执行如何被非安全中断抢占。安全软件捕获非安全中断,对中断记账并将其通过EL3转交给非安全软件。

执行于S.EL1/S.EL0时,路由到FEL

valid:S.EL1/S.EL0的安全软件负责控制自身执行如何被EL3中断抢占,并且将中断转交给EL3处理。

当设置了EL3_EXCEPTION_HANDLING=1时,则invalid,因为EL3 interrupts被无条件的路由到EL3,EL3中断此时总是抢占S.EL1/S.EL0执行然后再转交给EL3进行处理。

CSS=0 TEL3=1

执行于安全状态时,路由到EL3

valid:EL3安全软件可将中断转交给S.EL1处理

执行于安全状态时,路由到EL3

valid:EL3安全软件在将中断转交给非安全软件前,保存S.EL1/S.EL0状态。这需要S.EL1和EL3软件协调保证前者的状态被后者正确保存了。

执行S.EL1/S.EL0时,路由到EL3

valid:EL3中安全软件可以处理中断

CSS=1 TEL3=0

执行于非安全状态时,路由到FEL

invalid:安全中断对非安全软件不可见,违反了ARM安全扩展的初衷。

执行于非安全状态时,路由到FEL

valid:非安全软件处理非安全中断

执行于非安全状态时,路由到FEL

invalid:安全中断对非安全软件不可见,,违反了ARM安全扩展的初衷。

CSS=1 TEL3=1

执行于非安全状态时,路由到EL3

valid:EL3安全软件可见中断转交给S.EL1处理。

执行于非安全状态时,路由到EL3

invalid:没理由将中断路由到EL3,然后EL3再将其递回给非安全软件进行处理。

执行于非安全状态时,路由到EL3

valid:EL3安全软件可以处理EL3中断。

 

5.1.4. Mapping of interrupt type to signal

5.2. Assumptions in Interrupt Management Framework

 此框架做了如下假定来简化实现:

  • 虽然框架支持两种类型安全中断(EL3和S.EL1中断),但是仅有GICv3以Group 0中断形式支持EL3中断。假设GICv2的所有安全中断在S.EL1中被处理。他们可以通过EL3传递到S.EL1,但是不能被EL3处理。
  • EL3执行时中断异常(PSTATE.I和F)被屏蔽。
  • 中断管理:
    • 提供注册某一类型中断的路由规格和处理函数。
    • 支持将一种类型中断控制权转交给注册处理函数处理。

5.3. Software components

中断管理角色和职责被细分为运行在EL3和S.EL1件的一系列组件:

  • EL3 Runtime FIrmware。
  • SPD(Secure Payload Dispatcher)服务。SPD和运行在S.EL1/S.EL0中软件交互,并负责安全和非安全状态切换。中断和SMC都会触发安全和非安全切换。SPD属于EL3 Runtime Firmware一部分。
  • SP(Secure Payload)。SP指的是运行于S.EL1/S.EL0的安全OS。它和SPD服务交互,处理和非安全软件的通信。

5.4. Interrupt registration

 下面介绍不同软件组件在中断处理函数注册细节。

5.4.1. EL3 runtime firmware

中断处理函数定义如下:

typedef uint64_t (*interrupt_type_handler_t)(uint32_t id,
                         uint32_t flags,
                         void *handle,
                         void *cookie);

id:保留参数。

flags:

  • 安全状态,bit[0]。1表示处于非安全状态,0表示处于安全状态。
  • 保留,bit[31:1]。

handle:指向flags指向当前CPU特定安全状态的cpu_context结构体。

EL3 Runtime Firmware提供如下特定类型中断注册接口。SPD可以使用这些API来为S.EL1或者非安全中断注册处理函数。

int32_t register_interrupt_type_handler(uint32_t type,
                    interrupt_type_handler_t handler,
                    uint32_t flags);

type:INTR_TYPE_S_EL1、INTR_TYPE_EL3、INTR_TYPE_NS

flags:bit[1:0]保存中断路由模型。bit[0]保存安全状态路由模型,bit[1]保存非安全状态路由模型。0表示中断路由目的地是FEL,1表示中断路由目的地是EL3。

hander:中断处理函数。

函数调用成功返回0,中断已注册返回-EALREADY。type、flags、handler非法,则返回-EINVAL。

框架提供如下API给EL3 Runtime Firmware配置和查看当前CPU不同安全状态的中断路由。

u_register_t cm_get_scr_el3(uint32_t security_state);
void cm_write_scr_el3_bit(uint32_t security_state,
              uint32_t bit_pos,
              uint32_t value);

cm_get_scr_el3()返回当前CPU指定安全状态的SCR_EL3寄存器值。

cm_write_scr_el3_bit()对SCR_EL3指定位写0或1。register_interrupt_type_handler()调用set_routing_model()来设置SCR_EL3,间接调用了cm_get_scr_el3()和cm_write_scr_el3_bit()函数。

int32_t register_interrupt_type_handler(uint32_t type,
                    interrupt_type_handler_t handler,
                    uint32_t flags)
{
    int32_t rc;
...
    rc = set_routing_model(type, flags);
    if (rc != 0)
        return rc;

    /* Save the handler */
    intr_type_descs[type].handler = handler;

    return 0;
}

int32_t set_routing_model(uint32_t type, uint32_t flags)
{
    int32_t rc;

    rc = validate_interrupt_type(type);----------------检查中断类型是否有效。
    if (rc != 0)
        return rc;

    rc = validate_routing_model(type, flags);----------根据中断类型,检查中断路由是否有效。
    if (rc != 0)
        return rc;

    /* Update the routing model in internal data structures */
    intr_type_descs[type].flags = flags;
    set_scr_el3_from_rm(type, flags, SECURE);
    set_scr_el3_from_rm(type, flags, NON_SECURE);

    return 0;
}

static void set_scr_el3_from_rm(uint32_t type,
                uint32_t interrupt_type_flags,
                uint32_t security_state)
{
    uint32_t flag, bit_pos;

    flag = get_interrupt_rm_flag(interrupt_type_flags, security_state);
    bit_pos = plat_interrupt_type_to_line(type, security_state);
    intr_type_descs[type].scr_el3[security_state] = (u_register_t)flag << bit_pos;

    if (cm_get_context(security_state) != NULL)
        cm_write_scr_el3_bit(security_state, bit_pos, flag);
}

void cm_write_scr_el3_bit(uint32_t security_state,
              uint32_t bit_pos,
              uint32_t value)
{
    cpu_context_t *ctx;
    el3_state_t *state;
    u_register_t scr_el3;

    ctx = cm_get_context(security_state);
    assert(ctx != NULL);
...
    state = get_el3state_ctx(ctx);
    scr_el3 = read_ctx_reg(state, CTX_SCR_EL3);-------------------从寄存器SCR_EL3中读取值到scr_el3中。
    scr_el3 &= ~(1UL << bit_pos);
    scr_el3 |= (u_register_t)value << bit_pos;--------------------更新SCR_EL3寄存器的某个bit值。
    write_ctx_reg(state, CTX_SCR_EL3, scr_el3);-------------------将scr_el3写入SCR_EL3寄存器中。
}

u_register_t cm_get_scr_el3(uint32_t security_state)
{
    cpu_context_t *ctx;
    el3_state_t *state;

    ctx = cm_get_context(security_state);
    assert(ctx != NULL);

    state = get_el3state_ctx(ctx);
    return read_ctx_reg(state, CTX_SCR_EL3);
}

当前框架中,EL3软件负责对路由模型编程,SPD负责确保在接收到中断时遵循路由模型。

/* Routed to EL3 from NS. Taken to S-EL1 from Secure */
#define INTR_SEL1_VALID_RM0        U(0x2)------------------------S.EL1中断发生在非安全世界,路由到EL3,最终送到S.EL1/* Routed to EL3 from NS and Secure */
#define INTR_SEL1_VALID_RM1        U(0x3)------------------------S.EL1中断发生在安全或非安全世界,都路由到EL3.
/* Routed to EL1/EL2 from NS and to S-EL1 from Secure */
#define INTR_NS_VALID_RM0        U(0x0)--------------------------非安全中断发生在非安全世界,路由到EL1/EL2;发生在安全世界,路由到S.EL1/* Routed to EL1/EL2 from NS and to EL3 from Secure */
#define INTR_NS_VALID_RM1        U(0x1)--------------------------非安全中断发生在非安全世界,路由到EL1/EL2;发生在安全世界,路由到EL3/* Routed to EL3 from NS. Taken to S-EL1 from Secure and handed over to EL3 */
#define INTR_EL3_VALID_RM0        U(0x2)-------------------------EL3中断发生在非安全世界,路由到EL3;发生在安全世界,先带到S.EL1,再被转交到EL3/* Routed to EL3 from NS and Secure */
#define INTR_EL3_VALID_RM1        U(0x3)-------------------------发生在安全或非安全世界,路由到EL3/* This is the default routing model */
#define INTR_DEFAULT_RM            U(0x0) 

5.4.2. Secure payload dispatcher

SPD负责决定和维护自身和SP支持的中断的路由模型。它还负责根据路由模型在安全软件和非安全软件之间传递中断

路由模型可以在编译时或运行时确定,EL3软件使用register_interrupt_type_handler()为每个中断注册处理函数。

SPD必须要SP初始化完成后,才能对路由模型进行编程。

在接收EL3软件中断后,SPD必须确定将控制权移交给SP的机制

5.4.3. Test secure payload dispatcher behavior

 TSPD仅处理S.EL1中断,并且在构建时提供以下路由模型。
  • 当处于非安全状态时,S.EL1中断路由到EL3;当处于安全状态时,即CSS=0,TEL3=0&CSS=1,TEL3=1S.EL1中断路由到FEL
  • 当TSP_NS_INTR_ASYNC_PREEMPT为0时,默认路由模型用于非安全中断。对于非安全中断,它们在任一安全状态下被路由到FEL,即CSS=0,TEL3=0&CSS=1,TEL3=0
  • 当TSP_NS_INTR_ASYNC_PREEMPT为1时,当执行处于安全状态时,非安全中断被路由到EL3,即CSS=0,TEL3=1表示非安全中断。这有效地抢占了Secure-EL1。默认路由模型用于非安全状态下的非安全中断,CSS=1,TEL3=0

tspd_init()执行以下操作以满足前面提到的需求:

  • 将控制权传递给TSP以执行其初始化。

  • TSPD实现了Secure-EL1中断的处理函数。

  • 当TSP_NS_INTR_ASYNC_PREEMPT为1时,TSPD为非安全中断实现一个处理函数。

tspd_setup()对跳转到SP做一些简单配置,并且赋值tspd_init()给bl32_init()。bl31_main()中调用bl32_init()进行跳转到BL32,即SP的工作。

static int32_t tspd_setup(void)
{
    entry_point_info_t *tsp_ep_info;
    uint32_t linear_id;

    linear_id = plat_my_core_pos();

    tsp_ep_info = bl31_plat_get_next_image_ep_info(SECURE);------------------获取BL32,即SP的入口信息entry_point_info_t,后面跳转到SP需要。
    if (!tsp_ep_info) {
        WARN("No TSP provided by BL2 boot loader, Booting device"
            " without TSP initialization. SMC`s destined for TSP"
            " will return SMC_UNK\n");
        return 1;
    }

    if (!tsp_ep_info->pc)
        return 1;

    tspd_init_tsp_ep_state(tsp_ep_info,
                TSP_AARCH64,
                tsp_ep_info->pc,
                &tspd_sp_context[linear_id]);-------------------------------初始化tsp_ep_info和tspd_sp_context[]。结果分别放入全局变量bl32_image_ep_infotspd_sp_context[linear_id]中。

#if TSP_INIT_ASYNC
    bl31_set_next_image_type(SECURE);
#else
    /*
     * All TSPD initialization done. Now register our init function with
     * BL31 for deferred invocation
     */
    bl31_register_bl32_init(&tspd_init);-----------------------------------注册tspd_init,bl32_init指向tspd_init,后续bl31_main()中会调用。
#endif
    return 0;
}

int32_t tspd_init(void)
{
    uint32_t linear_id = plat_my_core_pos();
    tsp_context_t *tsp_ctx = &tspd_sp_context[linear_id];------------------获取当前CPU的tspd和sp的上下文。
    entry_point_info_t *tsp_entry_point;
    uint64_t rc;

    tsp_entry_point = bl31_plat_get_next_image_ep_info(SECURE);------------获取BL32的入口信息。
    assert(tsp_entry_point);

    cm_init_my_context(tsp_entry_point);

    rc = tspd_synchronous_sp_entry(tsp_ctx);-------------------------------跳转到SP执行。
    assert(rc != 0);

    return rc;
}

tspd_smc_handler()是tspd的核心,处理安全和非安全世界发起的SMC调用。

static uintptr_t tspd_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)
{
    cpu_context_t *ns_cpu_context;
    uint32_t linear_id = plat_my_core_pos(), ns;
    tsp_context_t *tsp_ctx = &tspd_sp_context[linear_id];
    uint64_t rc;
...
    ns = is_caller_non_secure(flags);----------------------------------------发起SMC调用的是安全还是非安全世界,ns为1表示非安全世界,ns为0表示安全世界。

    switch (smc_fid) {

    case TSP_PREEMPTED:
        if (ns)
            SMC_RET1(handle, SMC_UNK);

        return tspd_handle_sp_preemption(handle);

    case TSP_HANDLED_S_EL1_INTR:
        if (ns)
            SMC_RET1(handle, SMC_UNK);

        assert(handle == cm_get_context(SECURE));

        /*
         * Restore the relevant EL3 state which saved to service
         * this SMC.
         */
        if (get_yield_smc_active_flag(tsp_ctx->state)) {
            SMC_SET_EL3(&tsp_ctx->cpu_ctx,
                    CTX_SPSR_EL3,
                    tsp_ctx->saved_spsr_el3);
            SMC_SET_EL3(&tsp_ctx->cpu_ctx,
                    CTX_ELR_EL3,
                    tsp_ctx->saved_elr_el3);
#if TSP_NS_INTR_ASYNC_PREEMPT
            /*
             * Need to restore the previously interrupted
             * secure context.
             */
            memcpy(&tsp_ctx->cpu_ctx, &tsp_ctx->sp_ctx,
                TSPD_SP_CTX_SIZE);
#endif
        }

        /* Get a reference to the non-secure context */
        ns_cpu_context = cm_get_context(NON_SECURE);
        assert(ns_cpu_context);

        cm_el1_sysregs_context_restore(NON_SECURE);
        cm_set_next_eret_context(NON_SECURE);

        SMC_RET0((uint64_t) ns_cpu_context);

    case TSP_ENTRY_DONE:----------------------------------------------------BL32 SP告诉EL3软件自身初始化完成,此时TSPD注册PSCI钩子函数、注册非安全状态下S.EL1中断处理函数等。
        if (ns)
            SMC_RET1(handle, SMC_UNK);

        /*
         * Stash the SP entry points information. This is done
         * only once on the primary cpu
         */
        assert(tsp_vectors == NULL);
        tsp_vectors = (tsp_vectors_t *) x1;---------------------------------BL32返回的第二个参数,BL31可以直接调用tsp_vectors来完成BL32提供的功能。

        if (tsp_vectors) {
            set_tsp_pstate(tsp_ctx->state, TSP_PSTATE_ON);

            /*
             * TSP has been successfully initialized. Register power
             * management hooks with PSCI
             */
            psci_register_spd_pm_hook(&tspd_pm);----------------------------注册SPD提供的功耗管理狗子函数。

            flags = 0;
            set_interrupt_rm_flag(flags, NON_SECURE);
            rc = register_interrupt_type_handler(INTR_TYPE_S_EL1,
                        tspd_sel1_interrupt_handler,
                        flags);---------------------------------------------注册INTR_TYPE_S_EL1类型中断的处理函数tspd_sel1_interrupt_handler()。当CPU执行于安全状态,注册非安全中断路由到EL3进行处理函数。
            if (rc)
                panic();

#if TSP_NS_INTR_ASYNC_PREEMPT
            flags = 0;
            set_interrupt_rm_flag(flags, SECURE);

            rc = register_interrupt_type_handler(INTR_TYPE_NS,
                        tspd_ns_interrupt_handler,
                        flags);-----------------------------------------------注册INTR_TYPE_NS类型中断的处理函数tspd_ns_interrupt_handler。
            if (rc)
                panic();

            disable_intr_rm_local(INTR_TYPE_NS, SECURE);
#endif
        }

#if TSP_INIT_ASYNC
...
#else
        tspd_synchronous_sp_exit(tsp_ctx, x1);
        break;
#endif
...
default:
        break;
    }

    SMC_RET1(handle, SMC_UNK);
}

5.4.4. Secure payload

SP必须在S.EL1实现中断处理框架,以支持其选择的中断路由模型。

SP会在以下情况之间交替进行:

  • 在启用IRQ、FIQ或两个中断都使能的代码中,如果中断类型以FEL为目标,那么它将被路由到Secure-EL1异常向量表。这被定义为处理中断的异步模式。此模式适用于S.EL1和非安全中断。
  • 在两个中断都被禁用的代码中,如果中断类型以FEL为目标,那么执行最终将迁移到非安全状态。任何不安全的中断都将按照路由模型中的描述进行处理,其中CSS=1,TEL3=0。Secure-EL1中断将路由到EL3(根据CSS=1和TEL3=1的路由模型),SPD服务将把它们交给SP。这被定义为处理中断的同步模式。

5.4.5. Test secure payload behavior

TSP实现了一个入口点(tsp_sel1_intr_entry()),用于处理在非安全状态下发生并通过TSPD服务路由的Secure-EL1中断(同步处理模型)。它通过tsp_vectors将对该入口点的引用传递给TSPD服务。

TSP还用一个能够处理S.EL1异常级别上发生的FIQ和IRQ异常的向量表来替换通过early_exceptions(BL2使用)变量引用的默认异常向量表。此表通过tsp_exceptions(BL32使用)变量引用并编程到VBAR_EL1中。它迎合了异步处理模型。

TSP还对Arm通用计时器块中的安全物理计时器进行编程,以引发周期性中断(每半秒一次),以便测试软件组件中列出的所有软件组件的中断管理。

vector_base early_exceptions--------------------------------------------BL2使用的异常向量表。

    /* -----------------------------------------------------
     * Current EL with SP0 : 0x0 - 0x200
     * -----------------------------------------------------
     */...
    /* -----------------------------------------------------
     * Lower EL using AArch32 : 0x600 - 0x800
     * -----------------------------------------------------
     */
...

vector_base tsp_exceptions----------------------------------------------BL32使用的异常向量表。
    /* -----------------------------------------------------
     * Current EL with _sp_el0 : 0x0 - 0x200. No exceptions
     * are expected and treated as irrecoverable errors.
     * -----------------------------------------------------
     */...
    /* -----------------------------------------------------
     * Lower EL using AArch32 : 0x600 - 0x800. No exceptions
     * handled since the TSP does not implement a lower EL.
     * -----------------------------------------------------
     */
...

typedef uint32_t tsp_vector_isn_t;

typedef struct tsp_vectors {
    tsp_vector_isn_t yield_smc_entry;
    tsp_vector_isn_t fast_smc_entry;
    tsp_vector_isn_t cpu_on_entry;
    tsp_vector_isn_t cpu_off_entry;
    tsp_vector_isn_t cpu_resume_entry;
    tsp_vector_isn_t cpu_suspend_entry;
    tsp_vector_isn_t sel1_intr_entry;
    tsp_vector_isn_t system_off_entry;
    tsp_vector_isn_t system_reset_entry;
    tsp_vector_isn_t abort_yield_smc_entry;
} tsp_vectors_t;

vector_base tsp_vector_table---------------------------------------------BL32运行起来后使用的向量表。
    b    tsp_yield_smc_entry
    b    tsp_fast_smc_entry
    b    tsp_cpu_on_entry
    b    tsp_cpu_off_entry
    b    tsp_cpu_resume_entry
    b    tsp_cpu_suspend_entry
    b    tsp_sel1_intr_entry
    b    tsp_system_off_entry
    b    tsp_system_reset_entry
    b    tsp_abort_yield_smc_entry

5.5. Interrupt handling

5.5.1. EL3 runtime firmware

EL3软件的异常向量表为runtime_exceptions,其中handle_interrupt_exception负责处理送到EL3的IRQ、FIQ中断。

当中断发生时,每个中断的异常向量负责如下任务:

1. 进入异常立即保存所有的通用寄存器上下文x0-x30。寄存器保存在SP_EL3寄存器引用的per-cpu数据结构cpu_context中。

2. 保存系统寄存器ELR_EL3, SP_EL0 and SPSR_EL3到SP_EL3应用的per-cpu数据结构cpu_context中。

3. 通过从S_EL0的per-cpu cpu_context数据结构中恢复CTX_RUNTIME_SP值切换到C执行环境,执行msr spsel, #0指令。

4. 确定中断类型。Secure-EL1中断将在FIQ向量处发出信号。非安全中断将在IRQ向量处发出信号。平台应该实现以下API来确定挂起中断的类型。

uint32_t plat_ic_get_interrupt_type(void);

它应该返回INTR_TYPE_S_EL1 或INTR_TYPE_NS。

5. 确定已生成的中断类型的处理程序。

interrupt_type_handler get_interrupt_type_handler(uint32_t interrupt_type);

 

    .macro    handle_interrupt_exception label

    /*
     * 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.
     */
    bl    save_gp_pmcr_pauth_regs----------------------------1.

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

    /* Save the EL3 system registers needed to return from this exception */
    mrs    x0, spsr_el3---------------------------------------2.
    mrs    x1, elr_el3
    stp    x0, x1, [sp, #CTX_EL3STATE_OFFSET + CTX_SPSR_EL3]

    /* Switch to the runtime stack i.e. SP_EL0 */
    ldr    x2, [sp, #CTX_EL3STATE_OFFSET + CTX_RUNTIME_SP]----3.
    mov    x20, sp
    msr    spsel, #MODE_SP_EL0
    mov    sp, x2

    /*
     * Find out whether this is a valid interrupt type.
     * If the interrupt controller reports a spurious interrupt then return
     * to where we came from.
     */
    bl    plat_ic_get_pending_interrupt_type-------------------4. 获取中断类型INTR_TYPE_NS、INTR_TYPE_S_EL1、INTR_TYPE_EL3。
    cmp    x0, #INTR_TYPE_INVAL--------------------------------验证中断有效性。
    b.eq    interrupt_exit_\label

    bl    get_interrupt_type_handler---------------------------5.根据中断类型获取中断处理函数。
    cbz    x0, interrupt_exit_\label---------------------------检查中断处理函数是否正确。
    mov    x21, x0

    mov    x0, #INTR_ID_UNAVAILABLE

    /* Set the current security state in the 'flags' parameter */
    mrs    x2, scr_el3
    ubfx    x1, x2, #0, #1

    /* Restore the reference to the 'handle' i.e. SP_EL3 */
    mov    x2, x20

    /* x3 will point to a cookie (not used now) */
    mov    x3, xzr

    /* Call the interrupt type handler */
    blr    x21--------------------------------------------------调用中断函数进行同步处理。

interrupt_exit_\label:
    /* Return from exception, possibly in a different security state */
    b    el3_exit

    .endm

 

5.5.2. Secure payload dispatcher

5.5.2.1. Interrupt entry

 

5.5.2.2. Interrupt exit

 

5.5.2.3. Test secure payload dispatcher Secure-EL1 interrupt handling

TSPD调用tspd_sel1_interrupt_handler()处理S.EL1中断,它首先验证中断有效性,成功后进入TSP调用tsp_vectors->sel1_intr_entry处理中断。

static uint64_t tspd_sel1_interrupt_handler(uint32_t id,
                        uint32_t flags,
                        void *handle,
                        void *cookie)
{
    uint32_t linear_id;
    tsp_context_t *tsp_ctx;

    /* Check the security state when the exception was generated */
    assert(get_interrupt_src_ss(flags) == NON_SECURE);

    /* Sanity check the pointer to this cpu's context */
    assert(handle == cm_get_context(NON_SECURE));

    /* Save the non-secure context before entering the TSP */
    cm_el1_sysregs_context_save(NON_SECURE);

    /* Get a reference to this cpu's TSP context */
    linear_id = plat_my_core_pos();
    tsp_ctx = &tspd_sp_context[linear_id];
    assert(&tsp_ctx->cpu_ctx == cm_get_context(SECURE));

    if (get_yield_smc_active_flag(tsp_ctx->state)) {
        tsp_ctx->saved_spsr_el3 = (uint32_t)SMC_GET_EL3(&tsp_ctx->cpu_ctx,
                              CTX_SPSR_EL3);
        tsp_ctx->saved_elr_el3 = SMC_GET_EL3(&tsp_ctx->cpu_ctx,
                             CTX_ELR_EL3);
#if TSP_NS_INTR_ASYNC_PREEMPT
        /*Need to save the previously interrupted secure context */
        memcpy(&tsp_ctx->sp_ctx, &tsp_ctx->cpu_ctx, TSPD_SP_CTX_SIZE);
#endif
    }

    cm_el1_sysregs_context_restore(SECURE);-----------------------------------切换到S.EL1。
    cm_set_elr_spsr_el3(SECURE, (uint64_t) &tsp_vectors->sel1_intr_entry,
            SPSR_64(MODE_EL1, MODE_SP_ELX, DISABLE_ALL_EXCEPTIONS));----------在S.EL1执行中断处理函数。

    cm_set_next_eret_context(SECURE);-----------------------------------------返回EL3.

    SMC_RET2(&tsp_ctx->cpu_ctx, TSP_HANDLE_SEL1_INTR_AND_RETURN, read_elr_el3());
}

 

5.5.2.4. Test secure payload dispatcher non-secure interrupt handling

5.5.3. Secure payload interrupt handling

 

5.5.3.1. Test secure payload behavior

 

4. 平台中断控制器API

 

参考文档:《Platform Interrupt Controller API》。

vector_entry