0%

nuclei opensbi optee 方案

sm_init 调用栈

1
2
3
4
5
6
7
8
#0  sm_init (cold_boot=cold_boot@entry=1) at /home/liguang/program/riscv-lab/nuclei-linux-sdk/opensbi/lib/sbi/optee/sm.c:120
#1 0x00000000a00026aa in nuclei_final_init (cold_boot=<optimized out>)
at /home/liguang/program/riscv-lab/nuclei-linux-sdk/opensbi/platform/nuclei/evalsoc/platform.c:66
#2 0x00000000a0000c18 in sbi_platform_final_init (cold_boot=1, plat=0xa00138a8 <platform>)
at /home/liguang/program/riscv-lab/nuclei-linux-sdk/opensbi/include/sbi/sbi_platform.h:401
#3 init_coldboot (hartid=0, scratch=0xa002d000) at /home/liguang/program/riscv-lab/nuclei-linux-sdk/opensbi/lib/sbi/sbi_init.c:295
#4 sbi_init (scratch=0xa002d000) at /home/liguang/program/riscv-lab/nuclei-linux-sdk/opensbi/lib/sbi/sbi_init.c:425
#5 0x00000000a00004b6 in _start_warm () at /home/liguang/program/riscv-lab/nuclei-linux-sdk/opensbi/firmware/fw_base.S:443

重点关注 sm_init 函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
-+ void sm_init(bool cold_boot)
\ -|+ if cold_boot
\ -+ sbi_ecall_register_extension(&ecall_optee);
\ - .extid_start = SBI_EXT_OPTEE "0x4F505445"
| - .handle = sbi_ecall_optee_handler, "注册 optee ext handler"
| - sm_region_id = smm_init(); "添加 opensbi text 段 pmp entry, 并保存 id为 sm_region_id"
| - os_region_id = osm_init(); "添加 0xfffffffff bottom entry"
| - tee_region_id = teem_init(); "添加 FW_OPTEE_TZDRAM_BASE optee_os 的text 内存 pmp entry"
| - shm_region_id = shm_init(); "添加 share memory (secure与non sec 的共享内存的) pmp entry"
| - plicm_region_id = plicm_init(); "添加 plic 的 mmio的 pmp entry"
| - timer_region_id = timerm_init(); "添加 timer 的 mmio的pmp entry"
"没有判断 cold_boot 说明是所有核都会走"
| -+ pmp_init() "将所有可用的 pmpaddr pmpcfg 置0"
| -+ pmp_set_keystone(sm_region_id, PMP_NO_PERM); "设置 opensbi text 段不可读,这个是对 S-mode 有效,对 M-mode 不起作用"
| -+ pmp_set_keystone(os_region_id, PMP_ALL_PERM); "设置优先级最低的 osm entry 0xfffffffff 保底读写执行权限,排在它前面的 entry 如果设置了不一样的权限, 以前面的 entry 设置的为准"
| -+ pmp_set_keystone(tee_region_id, PMP_NO_PERM); "设置 optee text 无权限"
| -|+ if cold_boot
\ -+ opteed_init()
\ - sbi_memset(psci_ns_context, 0, sizeof(psci_ns_context)); "normal world context 初始化为 0"
| - sbi_memset(opteed_sp_context, 0, sizeof(opteed_sp_context)); "secure world context 初始化为 0"
| -+ img_entry_point.sec_attr = SECURE; "保存状态, 后面会用. 该结构体最终会被放到 当前 cpu context 中"
| -+ img_entry_point.pc = OPTEE_OS_LOAD_ADDR; "optee 的 text 段起始地址"
| - img_entry_point.arg0 = current_hartid(); "cpu core id"
| - img_entry_point.arg1 = 8*1024*1024;
| - img_entry_point.arg2 = FW_JUMP_FDT_ADDR; "fdt 的地址,optee 和 opensbi 共用一个 fdt"
| -+ cm_init_my_context(&img_entry_point);
\ -+ cm_init_context_by_index(current_hartid(), &img_entry_point);
\ -+ ctx = cm_get_context_by_index(cpu_idx, &img_entry_point->sec_attr); "根据 cpu id 和 sec_attr 属性找到 context 指针"
\ - if secure? &opteed_sp_context[cpu_idx].cpu_ctx : &psci_ns_context[cpu_idx];
"secure 和 non sec 分别有一个context 数组, 按cpuid 保存对应的 当前cpu的context"
| -+ cm_setup_context(ctx, ep); "填 context "
\ - ctx->sec_attr = (ep->sec_attr == SECURE) ? SECURE : NON_SECURE;
| - ctx->gp_regs.mepc = ep->pc;
| - ctx->gp_regs.mstatus = (1 << MSTATUS_MPP_SHIFT); "S-mode"
| - ctx->gp_regs.a0 = ep->arg0;
| - ctx->gp_regs.a1 = ep->arg1;
| - ctx->gp_regs.a2 = ep->arg2;
| - ctx->gp_regs.... 直到 arg7
| -|+ if ep->sec_attr == SECURE
\ - ctx->s_csrs.sie = 0; "初始 sie 为0, S-mode 中断关闭"
| - saved_mie = csr_read(CSR_MIE); "保存中断使能状态"
| - csr_write(CSR_MIE, 0); "关闭所有中断"
| - rc = opteed_synchronous_sp_entry(optee_ctx); "进入 sec world optee_os 的准备工作, 及要进入 optee_os, 下面拆解"
-----------------------------------------------------------------------------------------------------
"回到 M-mode的 opensbi"
-----------------------------------------------------------------------------------------------------
| -+ ... sbi_ecall_optee_handler 处理流程 -->1
| - csr_write(CSR_MIE, saved_mie); "恢复中断状态"
| - sbi_memset(&img_entry_point, 0, sizeof(entry_point_info_t));
| - img_entry_point.sec_attr = NON_SECURE; "下一个 image 为 non sec 的 u-boot"
| - cm_init_my_context(&img_entry_point); "初始化 non sec 的 context"
| - cm_set_next_eret_context(NON_SECURE);

下面对 opteed_synchronous_sp_entry 进行拆解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
-+ uint64_t opteed_synchronous_sp_entry(optee_context_t *optee_ctx)
\ -+ cm_sysregs_context_restore(SECURE); "从context 中恢复 s 开头的 csr, 这里主要是 sepc sie "
| -+ cm_set_next_eret_context(SECURE);
\ -+ ctx = cm_get_context(security_state); "拿 当前 cpu的 context 指针"
| -+ cm_set_next_context(ctx);
\ - next_cpu_context_ptr[current_hartid()] = context; "设置 next_cpu_context_ptr 全局变量, 保存当前cpu的下一个 context"
| -|+ if secure_state == SECURE ?
\ -+ switch_plic_int_enable_mode(SECURE); "设置中断使能状态为 secure_state 方案"
| -+ switch_vector_sec();
\ - csr_write(CSR_MTVEC, &_trap_handler_sec); "设置 secure 方案的 trap handler"
| - osm_pmp_set(PMP_NO_PERM); "bottom pmp entry 设置所有内存无权限"
| - shm_pmp_set(PMP_ALL_PERM); "sec 与 non sec 的 share memory 设置 all 权限"
| - plicm_pmp_set(PMP_ALL_PERM);
| - timerm_pmp_set(PMP_ALL_PERM);
| - teem_pmp_set(PMP_ALL_PERM); "设置 tee 的 text data bss 等的 all 权限"
| -+ rc = opteed_enter_sp(&optee_ctx->c_rt_ctx); "进入汇编函数, 保存当前 opensbi的 cpu context,
恢复 context 中保存的上下文, 即为 optee_os 准备的初始的 cpu context"
\ - REG_L s1, SBI_TRAP_REGS_OFFSET(mepc)(s0)
| - csrw CSR_MEPC, s1
| - REG_L s1, SBI_TRAP_REGS_OFFSET(mstatus)(s0)
| - csrw CSR_MSTATUS, s1
| - mret "进入到 optee_os, M-mode 切换为 S-mode"

–>1 optee_os 初始化完毕后, 会发 ecall 调用, 调用号 SBI_EXT_OPTEE “0x4F505445” 回到 opensbi 中, opensbi 由前面设置的 _trap_handler_sec 处理, 进而由
opensbi sbi_ecall_optee_handler 处理该 ecall 调用,其中 funcid 为 TEESMC_OPTEED_RETURN_ENTRY_DONE

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
-+ static int sbi_ecall_optee_handler(unsigned long extid, unsigned long funcid,
const struct sbi_trap_regs *regs,
unsigned long *out_val,
struct sbi_trap_info *out_trap)
\ -+ switch (funcid)
\ -+ case TEESMC_OPTEED_RETURN_ENTRY_DONE
\ -+ cm_gpregs_context_save(SECURE, regs); "保存 optee_os 的上下文到 context 中"
\ - ctx = cm_get_context(security_state);
| - sbi_memcpy(&ctx->gp_regs, trap_reg, sizeof(struct sbi_trap_regs)); "regs 已经由 _trap_handler_sec 保存下来了, 这里转移到 context 下"
| -+ cm_sysregs_context_save(SECURE); "保存 s 开头的 csr 到 context 下, 这里不用参数, 因为 M-mode 基本上不会改动 S-mode 的寄存器, 直接读即可"
| - optee_vector_table = (optee_vectors_t *) regs->a1; "regs->a1锚点 optee_os的 thread_vector_table 基址在 ecall 时会保存到 a1 寄存器中, 进而转移到这里"
| - if optee_vector_table ? set_optee_pstate(optee_ctx->state, OPTEE_PSTATE_ON) "optee_os的 power state, 这里on 表示正在运行"
| -+ opteed_synchronous_sp_exit(optee_ctx, regs->a1); "回到 进入 optee_os 时的地方"
\ -+ cm_sysregs_context_save(SECURE);
\ -+ opteed_exit_sp(optee_ctx->c_rt_ctx, ret);
\ - REG_L s0, (12-13) * __SIZEOF_POINTER__(sp)
| - ... "恢复 callee 即 s0-s11 寄存器"
| - REG_L ra, (0-13) * __SIZEOF_POINTER__(sp) "恢复 ra"
| - ret "回到 进入 optee_os 时的地方 接着往下执行"

optee_os 启动流程

上面讲到在调用 opteed_synchronous_sp_entry->opteed_enter_sp mret 后进入了 optee_os 中
下面就讲下 optee_os 的启动流程.

optee_os/core/arch/riscv/kernel/entry.S

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
-+ _start
\ - mv s1, a0 "保存hartid"
| - la a0, reset_vect_table
| - csrw stvec, a0 "保存 stvec 中断入口 为 reset_vect_table"
| -+ clear_bss __bss_start, __bss_end
| -+ set_sp s1 "为 hartid 设置 sp"
| -+ jal thread_init_thread_core_local "初始化线程本地存储"
| -+ jal dcache_cleaninv_range __text_start, cached_mem_end "dcache inv clean"
| -+ jal console_init "初始化console"
| -+ jal core_init_mmu_map(0, boot_mmu_config) "初始化mmu 映射mmu 页表"
| -+ jal enable_mmu(hartid) "开启mmu"
| -+ jal boot_init_primary_early "主核初始化"
\ -+ init_primary()
\ -+ thread_init_core_local_stacks() "由于std smc需要支持线程(thread)功能,而每个线程都需要含有独立的线程栈,
用于保存该线程相关的上下文。该函数用于为每个cpu核初始化相关的线程栈, tmp_stack_va_end用于指定每个ARM核的栈空间"
| -+ thread_set_exceptions(THREAD_EXCP_ALL); "由于在接下来的初始化流程中需要切换异常向量表的基地址,
因此在切换之前需要先mask掉相关的异常使能位"
| - primary_save_cntfrq() "未实现"
| - init_vfp_sec() "未实现,初始化浮点运算单元vfp"
| - thread_get_core_local()->curr_thread = 0; "curr_thread用于表示当前核使用的是哪个线程空间。"
| -+ init_runtime(); "初始化tee运行时所需的各种内存"
| -+ thread_init_boot_thread(); "将当前执行上下文转换为启动线程,
它主要包括从当前cpu的线程栈内存中分配一个栈空间,初始化线程页表和线程状态等"
| -+ thread_init_primary(); "该函数主要用于增强线程的运行安全,如设置线程栈的canary值,以用于监测栈溢出"
| -+ thread_init_per_cpu();
\ -+ thread_init_tvec(); "为每个cpu 设置中断入口为 thread_trap_vect"
| -+ init_sec_mon(nsec_entry); "未实现"
| -+ jal boot_init_primary_late(fdt) "该函数用于建立optee的系统运行环境,
包括从devcicetree中解析相关的配置,初始化中断控制器,调用initcall回调函数等"
\ -+ init_external_dt(fdt); "初始化devicetree,包括映射其物理内存,初始化dtb overlay等"
| -+ tpm_map_log_area(get_external_dt()); "初始化tpm的log buffer,其主要操作是为log buffer建立虚拟地址页表"
| -+ discover_nsec_memory(); "optee与non secure world可通过共享内存通信,它一共支持静态映射和动态映射两种共享内存方式。
其中静态映射shm在optee启动时就为其建立了相应的内存页表,
而动态映射方式可以在运行过程中动态地为shm建立页表。该接口用于从devicetree中获取non secure内存,以支持动态共享内存机制"
| -+ update_external_dt(); "在devicetree中添加与optee相关的一些节点和属性,这些节点和属性将由hlos内核解析并用于初始化相应的模块。
如它会创建/firmware/optee节点,并在该节点中配置一些与optee相关的属性"
| -+ configure_console_from_dt() "从devicetree中解析串口参数,并将optee的控制台切换为该串口"
| - main_init_gic(); "未实现"
| - init_vfp_nsec(); "未实现"
| -+ init_tee_runtime();
\ -+ core_mmu_init_ta_ram(); "用户空间中使用的栈空间是从 tee_mm_sec_ddr 内存池中分配出来的,
该内存池属于MEM_AREA_TA_RAM内存区域,该区域是由OPTEE分配,用于运行TA镜像。"
\ -+ tee_mm_init(&tee_mm_sec_ddr, ps, size, CORE_MMU_USER_CODE_SHIFT, TEE_MM_POOL_NO_FLAGS);
| -+ call_initcalls(); -->2 "调用已注册的initcall回调函数, 被各模块用于注册启动时调用的初始化接口, core/include/initcall.h"
| -+ jal thread_clr_boot_thread "thread 结束阶段, 将thread->state 置为 THREAD_STATE_FREE"
| -+ ecall SBI_EXT_OPTEE a6->TEESMC_OPTEED_RETURN_ENTRY_DONE a1-> thread_vector_table load offset -->3
"ecall 回opensbi, 将optee_os 的 vector address load offset(此处就是其物理地址,这里mmu 是直接映射的) 放到 a1 中, 给opensbi 用,
参照 sbi_ecall_optee_handler 的 regs->a1锚点 "

–>2 initcall 按顺序执行

1
2
3
4
5
6
7
8
9
10
11
12
13
#define preinit_early(fn)		__define_initcall(preinit, 1, fn)
#define preinit(fn) __define_initcall(preinit, 2, fn)
#define preinit_late(fn) __define_initcall(preinit, 3, fn)

#define early_init(fn) __define_initcall(init, 1, fn)
#define early_init_late(fn) __define_initcall(init, 2, fn)
#define service_init(fn) __define_initcall(init, 3, fn)
#define service_init_late(fn) __define_initcall(init, 4, fn)
#define driver_init(fn) __define_initcall(init, 5, fn)
#define driver_init_late(fn) __define_initcall(init, 6, fn)
#define release_init_resource(fn) __define_initcall(init, 7, fn)

#define boot_final(fn) __define_initcall(final, 1, fn)

–>3 thread_vector_table

1
2
3
4
5
6
7
8
9
10
11
12
13
14
FUNC thread_vector_table , : , .identity_map, , nobti
.option push
.option norvc
j vector_std_smc_entry
j vector_fast_smc_entry
j vector_cpu_on_entry
j vector_cpu_off_entry
j vector_cpu_resume_entry
j vector_cpu_suspend_entry
j vector_fiq_entry
j vector_system_off_entry
j vector_system_reset_entry
.option pop
END_FUNC thread_vector_table

中断处理

原生的 riscv 下并没有安全世界/非安全世界的区分, 没有对中断进行区分.
而 arm trustzone 下可以根据 SCR.NS bit 位来标记进入安全世界还是非安全世界或者在进 monitor 时, 是来自于安全世界还是非安全世界.
且 arm 在硬件上做到了将非安全中断绑定到 irq 事件, 安全中断绑定到了 fiq 事件. irq 由 REE 处理, fiq 由 TEE 处理.

因此 riscv 想做 tee 方案,

  • 需要由软件管理安全世界/非安全世界的运行状态
  • 需要有软件管理非安全中断和安全中断
    上述这两点增加了软件实现的复杂度

看下 nuclei 上怎么做到上述这两点的

安全状态标记

opensbi 中实现 ATF 类似的功能, 不同的是, 需要由软件维护安全状态.

  • opteeos 初始化阶段, opensbi 在平台初始化节点, 通过 mret 进入 optee_os, optee_os 完成初始化后返回 opensbi, opensbi 需要保存 optee 的 context, 即安全世界上下文
  • ree os 运行阶段, 当需要 optee 服务时, ree os 通过 ecall 发送 optee 请求, opensbi 收到请求, 保存当前 cpu 状态到非安全 context, 然后恢复安全世界上下文, 将请求给 optee_os 处理. optee_os 处理完后, 发送 ecall 到 opensbi, opensbi 保存安全世界上下文恢复非安全世界上下文, 保存 optee 返回值, 恢复到 ree os 执行.
    维护安全状态中引入了多个数组指针, 保存当前 cpu 安全世界上下文/非安全世界上下文的指针. 以及 next context

从 next context 的 sec_attr 判断是来自 安全世界还是非安全世界

1
2
3
4
5
6
static int is_caller_non_secure(void)
{
cpu_context_t *ctx;
ctx = cm_get_next_context();
return (ctx->sec_attr == NON_SECURE);
}

安全世界状态切换时, 需要更新维护 next context. 这点是比较容易理解的, 无论当前 cpu 是处在安全世界(TEE)还是非安全世界(TEE) 中, 进入这个模式前, 都需要 opensbi 切换世界状态的代码参与, 而这个代码 cm_set_next_context 维护了 sec_attr 的状态.

而 arm 类似判断来自于安全世界/非安全世界的 smc 调用时, 用的 SCR.NS 硬件状态.

维护中断状态

要实现类似 arm FIQ/IRQ 的能力, nuclei 做了如下方案

  • 软件维护了一个表记录哪些中断是安全中断 (arm 使用 group 标记)
  • 安全中断应该由 M-mode 响应还是由 S-mode 响应
    • 进入 TEE, 设置安全中断由 S-mode 响应, 非安全中断由 M-mode 响应.
    • 进入 REE, 设置安全中断由 M-mode 响应, 非安全中断由 S-mode 响应.

当处在 M-mode 时, 中断都由 M-mode 响应, 根据中断类型再分发给 TEE/REE

plic_secure_int 数组中每个元素是由 hartid 和中断号组成,各占16bit, plic_secure_int[0]是个特殊值记录安全中断总个数。

  • plic_secure_int[0]:表示有 plic_secure_int[0] & 0xFFFF 个安全中断
  • plic_secure_int[x] 表示 plic_secure_int[x] & 0xFFFF 这个安全中断绑定到了hartid为plic_secure_int[x] >> 16 的core上
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
// 注册安全中断
int plic_register_sec_interrupt(unsigned int intr) {
hartid = current_hartid();
// 检查安全中断是否已经被注册. 已经注册则返回
for (i = 0 ; i < cnt ; i++)
if ((plic_secure_int[i + 1] & 0xFFFF) == intr)
return true;
// 注册安全中断
plic_secure_int[cnt + 1] = (hartid << 16) | intr;
// 更新安全中断总个数
plic_secure_int[0] = (0xFFFF << 16) | (cnt + 1);

}

int plic_is_sec_interrupt(int intr)
{
int i;
// plic_secure_int[0]是个特殊值记录安全中断总个数。
for(i = 0; i < (plic_secure_int[0] & 0xFFFF); i++)
if (intr == (plic_secure_int[i+1] & 0xFFFF))
return true;
return false;
}


void switch_plic_int_enable_mode(int next_state)
{
int en_mode, plic_int_num;
int i, j;
unsigned int* secint;

plic_int_num = irqchip_plic_get_interrupt_num();
secint = plic_get_sec_interrupt_tab();
for(i = 1; i <= plic_int_num; i++) {
en_mode = irqchip_plic_get_en_mode(i, NON_SECURE);
if (en_mode == -1 || plic_is_sec_interrupt(i))
/* skip disabled interrupt and secure interrupt */
continue;
else {
/*
* set all non-secure interrupt enable to S mode when next_state is NON_SECURE
* set all non-secure interrupt enable to M mode when next_state is SECURE
*/
irqchip_plic_set_en_mode((u32*)&i, next_state == NON_SECURE, NON_SECURE);
}
}
/*
* set all secure interrupt enable to M mode when next_state is NON_SECURE
* set all secure interrupt enable to S mode when next_state is SECURE
*/
for(j = 0; j < (secint[0] & 0xFFFF); j++)
irqchip_plic_set_en_mode(&secint[j+1], !(next_state == NON_SECURE), SECURE);
}

int irqchip_plic_set_en_mode(unsigned int *pintid, unsigned int mode, int secure)
{
hartid = current_hartid();
// 判断安全中断是否绑定到了一个hartid 上, 如果没有则重新绑定到当前hartid上
if (secure == 0) {
/* judge if int_id is bind to a hart */
if ((*pintid >> 16) != 0xFFFF) {
/*
* int_id have been bind to a hart, bind hart
* is not equal current return do nothing
*/
if ((*pintid >> 16) != hartid)
return SBI_EFAIL;
} else {
/* bind int_id to current hart id */
*pintid = (hartid << 16) | (*pintid & 0xFFFF);
}
// 安全中断, int_id 取 低16位
int_id = *pintid & 0xFFFF;
}
else
// 非安全中断, int_id = *pintid
int_id = *pintid;
// 取plic 结构体
plic = plic_hartid2data[hartid];
// 取 intid 对应的 plic寄存器里的 stride offset
int_src_idx = int_id / 32;
...
// 设置 plic 寄存器的 对应 int_id的 ie 使能位, 使能位分为 S-mode 和 M-mode的, 分别设置
// plic_hartid2context[hartid][!!mode] 中保存了对应hartid的 world_index, world_index在 plic里实际为 M-mode 还是 S-mode
// mode为0 plic_hartid2context[hartid][0] 存的是 M-mode 的 offset, plic_hartid2context[hartid][!0=1] 存的是 S-mode 的 offset
// mode: next_state == NON_SECURE next_state为sec, 则 mode 为 0, M-mode offset 置1, S-mode offset 置0
// mode: !(next_state == NON_SECURE) next_state为sec, 则 mode 为 1, S-mode offset 置1, M-mode offset 置0
// mode: next_state == NON_SECURE next_state为non_sec, 则 mode 为 1, S-mode offset 置1, M-mode offset 置0
// mode: !(next_state == NON_SECURE) next_state为non_sec, 则 mode 为 0, M-mode offset 置1, S-mode offset 置0
// 即要进入sec 时, 将非安全中断 M-mode 使能, S-mode 关闭, 将安全中断 M-mode 关闭, S-mode 使能
// 即要进入non_sec 时, 将非安全中断 S-mode 使能, M-mode 关闭, 将安全中断 M-mode 使能, S-mode 关闭

/*set M/S mode, M:1,S;0, or M:0,S:1*/
ie_val = plic_get_ie(plic, plic_hartid2context[hartid][!!mode], int_src_idx);
ie_val |= 1 << (int_id % 32);
plic_set_ie(plic, plic_hartid2context[hartid][!!mode], int_src_idx, ie_val);

ie_val = plic_get_ie(plic, plic_hartid2context[hartid][!mode], int_src_idx);
ie_val &= ~(1 << (int_id % 32));
plic_set_ie(plic, plic_hartid2context[hartid][!mode], int_src_idx, ie_val);

return 0;
}

void plic_set_ie(struct plic_data *plic, u32 cntxid, u32 word_index, u32 val)
{
plic_ie = (void *)plic->addr +
PLIC_ENABLE_BASE + PLIC_ENABLE_STRIDE * cntxid;
writel(val, plic_ie + word_index * 4);
}

与 arm 差异

维护中断是否是安全中断/非安全中断

  • nuclei riscv 哪个中断号是安全的, 哪个是不安全的, 由软件维护
  • arm 由设置 gic group 寄存器确定了, 然后硬件维护中断状态, 根据运行模式将中断绑定到 fiq 或 irq
    中断的处理:
  • nuclei riscv , optee 只处理安全中断, ree 只处理非安全中断, M-mode opensbi 处理剩下的, 因为在运行到 tee 时, 非安全中断的 S-mode ie 关闭了, 在运行到 ree 时, 安全中断在 S-mode ie 被关闭了
  • arm 上, tee ree 还是会处理 irq fiq, 需要陷入到 monitor 或 EL3 转给对方.