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