1 /*
2 * Copyright (c) 2021-2024, Arm Limited. All rights reserved.
3 * Copyright (c) 2022-2024 Cypress Semiconductor Corporation (an Infineon
4 * company) or an affiliate of Cypress Semiconductor Corporation. All rights
5 * reserved.
6 *
7 * SPDX-License-Identifier: BSD-3-Clause
8 *
9 */
10
11 #include "interrupt.h"
12
13 #include "bitops.h"
14 #include "current.h"
15 #include "fih.h"
16 #include "svc_num.h"
17 #include "tfm_arch.h"
18 #include "tfm_hal_interrupt.h"
19 #include "tfm_hal_isolation.h"
20 #include "tfm_svcalls.h"
21 #include "thread.h"
22 #include "utilities.h"
23 #include "load/spm_load_api.h"
24 #include "ffm/backend.h"
25 #include "internal_status_code.h"
26
27 extern uintptr_t spm_boundary;
28
29 #if TFM_ISOLATION_LEVEL != 1
30 extern void tfm_flih_func_return(psa_flih_result_t result);
31
32 __attribute__((naked))
tfm_flih_deprivileged_handling(void * p_pt,uintptr_t fn_flih,void * curr_component)33 static psa_flih_result_t tfm_flih_deprivileged_handling(void *p_pt,
34 uintptr_t fn_flih,
35 void *curr_component)
36 {
37 __ASM volatile("SVC "M2S(TFM_SVC_PREPARE_DEPRIV_FLIH)" \n"
38 "BX LR \n"
39 );
40 }
41
tfm_flih_prepare_depriv_flih(struct partition_t * p_owner_sp,uintptr_t flih_func)42 uint32_t tfm_flih_prepare_depriv_flih(struct partition_t *p_owner_sp,
43 uintptr_t flih_func)
44 {
45 struct partition_t *p_curr_sp;
46 uintptr_t sp_base, sp_limit, curr_stack, ctx_stack;
47 struct context_ctrl_t flih_ctx_ctrl;
48 fih_int fih_rc = FIH_FAILURE;
49 FIH_RET_TYPE(bool) fih_bool;
50
51 /* Come too early before runtime setup, should not happen. */
52 if (!CURRENT_THREAD) {
53 tfm_core_panic();
54 }
55
56 p_curr_sp = GET_CURRENT_COMPONENT();
57 sp_base = LOAD_ALLOCED_STACK_ADDR(p_owner_sp->p_ldinf)
58 + p_owner_sp->p_ldinf->stack_size;
59 sp_limit = LOAD_ALLOCED_STACK_ADDR(p_owner_sp->p_ldinf);
60
61 curr_stack = (uintptr_t)__get_PSP();
62 if (curr_stack < sp_base && curr_stack > sp_limit) {
63 /* The IRQ Partition's stack is being used */
64 ctx_stack = curr_stack;
65 } else {
66 ctx_stack = p_owner_sp->thrd.p_context_ctrl->sp;
67 }
68
69 FIH_CALL(tfm_hal_boundary_need_switch, fih_bool,
70 p_curr_sp->boundary, p_owner_sp->boundary);
71 if (fih_not_eq(fih_bool, fih_int_encode(false))) {
72 FIH_CALL(tfm_hal_activate_boundary, fih_rc,
73 p_owner_sp->p_ldinf, p_owner_sp->boundary);
74 }
75
76 /*
77 * The CURRENT_COMPONENT has been stored on MSP by the SVC call, safe to
78 * update it.
79 */
80 SET_CURRENT_COMPONENT(p_owner_sp);
81
82 flih_ctx_ctrl.sp_limit = sp_limit;
83 flih_ctx_ctrl.sp = ctx_stack;
84
85 tfm_arch_init_context(&flih_ctx_ctrl,
86 flih_func, NULL,
87 (uintptr_t)tfm_flih_func_return);
88
89 (void)tfm_arch_refresh_hardware_context(&flih_ctx_ctrl);
90
91 return flih_ctx_ctrl.exc_ret;
92 }
93
94 /* Go back to ISR from FLIH functions */
tfm_flih_return_to_isr(psa_flih_result_t result,struct context_flih_ret_t * p_ctx_flih_ret)95 uint32_t tfm_flih_return_to_isr(psa_flih_result_t result,
96 struct context_flih_ret_t *p_ctx_flih_ret)
97 {
98 struct partition_t *p_prev_sp, *p_owner_sp;
99 FIH_RET_TYPE(bool) fih_bool;
100 fih_int fih_rc = FIH_FAILURE;
101
102 p_prev_sp = (struct partition_t *)(p_ctx_flih_ret->state_ctx.r2);
103 p_owner_sp = GET_CURRENT_COMPONENT();
104
105 FIH_CALL(tfm_hal_boundary_need_switch, fih_bool,
106 p_owner_sp->boundary, p_prev_sp->boundary);
107 if (fih_not_eq(fih_bool, fih_int_encode(false))) {
108 FIH_CALL(tfm_hal_activate_boundary, fih_rc,
109 p_prev_sp->p_ldinf, p_prev_sp->boundary);
110 }
111
112 /*
113 * If the interrupted Thread mode context was running SPM code, then
114 * Privileged thread mode needs to be restored.
115 */
116 if (tfm_svc_thread_mode_spm_active()) {
117 __set_CONTROL_nPRIV(0);
118 }
119
120 /* Restore current component */
121 SET_CURRENT_COMPONENT(p_prev_sp);
122
123 arch_update_process_sp(p_ctx_flih_ret->psp, p_ctx_flih_ret->psplim);
124
125 /* Set FLIH result to the ISR */
126 p_ctx_flih_ret->state_ctx.r0 = (uint32_t)result;
127
128 return p_ctx_flih_ret->exc_return;
129 }
130 #endif
131
get_irq_info_for_signal(const struct partition_load_info_t * p_ldinf,psa_signal_t signal)132 const struct irq_load_info_t *get_irq_info_for_signal(
133 const struct partition_load_info_t *p_ldinf,
134 psa_signal_t signal)
135 {
136 size_t i;
137 const struct irq_load_info_t *irq_info;
138
139 if (!IS_ONLY_ONE_BIT_IN_UINT32(signal)) {
140 return NULL;
141 }
142
143 irq_info = LOAD_INFO_IRQ(p_ldinf);
144 for (i = 0; i < p_ldinf->nirqs; i++) {
145 if (irq_info[i].signal == signal) {
146 return &irq_info[i];
147 }
148 }
149
150 return NULL;
151 }
152
spm_handle_interrupt(void * p_pt,const struct irq_load_info_t * p_ildi)153 void spm_handle_interrupt(void *p_pt, const struct irq_load_info_t *p_ildi)
154 {
155 psa_flih_result_t flih_result;
156 struct partition_t *p_part;
157 psa_status_t ret;
158 FIH_RET_TYPE(bool) fih_bool;
159
160 if (!p_pt || !p_ildi) {
161 tfm_core_panic();
162 }
163
164 p_part = (struct partition_t *)p_pt;
165
166 if (p_ildi->pid != p_part->p_ldinf->pid) {
167 tfm_core_panic();
168 }
169
170 if (p_ildi->flih_func == NULL) {
171 /* SLIH Model Handling */
172 tfm_hal_irq_disable(p_ildi->source);
173 flih_result = PSA_FLIH_SIGNAL;
174 } else {
175 /* FLIH Model Handling */
176 #if TFM_ISOLATION_LEVEL == 1
177 flih_result = p_ildi->flih_func();
178 (void)fih_bool;
179 #else
180 FIH_CALL(tfm_hal_boundary_need_switch, fih_bool,
181 spm_boundary, p_part->boundary);
182 if (fih_eq(fih_bool, fih_int_encode(false))) {
183 flih_result = p_ildi->flih_func();
184 } else {
185 flih_result = tfm_flih_deprivileged_handling(
186 p_part,
187 (uintptr_t)p_ildi->flih_func,
188 GET_CURRENT_COMPONENT());
189 }
190 #endif
191 }
192
193 if (flih_result == PSA_FLIH_SIGNAL) {
194 ret = backend_assert_signal(p_pt, p_ildi->signal);
195 /* In SFN backend, there is only one thread, no thread switch. */
196 #if CONFIG_TFM_SPM_BACKEND_SFN != 1
197 if (ret == STATUS_NEED_SCHEDULE) {
198 arch_attempt_schedule();
199 }
200 #else
201 (void)ret;
202 #endif
203 }
204 }
205