1*54fd6939SJiyong Park /*
2*54fd6939SJiyong Park * Copyright (c) 2017-2018, ARM Limited and Contributors. All rights reserved.
3*54fd6939SJiyong Park *
4*54fd6939SJiyong Park * SPDX-License-Identifier: BSD-3-Clause
5*54fd6939SJiyong Park */
6*54fd6939SJiyong Park
7*54fd6939SJiyong Park /*
8*54fd6939SJiyong Park * Exception handlers at EL3, their priority levels, and management.
9*54fd6939SJiyong Park */
10*54fd6939SJiyong Park
11*54fd6939SJiyong Park #include <assert.h>
12*54fd6939SJiyong Park #include <stdbool.h>
13*54fd6939SJiyong Park
14*54fd6939SJiyong Park #include <bl31/ehf.h>
15*54fd6939SJiyong Park #include <bl31/interrupt_mgmt.h>
16*54fd6939SJiyong Park #include <context.h>
17*54fd6939SJiyong Park #include <common/debug.h>
18*54fd6939SJiyong Park #include <drivers/arm/gic_common.h>
19*54fd6939SJiyong Park #include <lib/el3_runtime/context_mgmt.h>
20*54fd6939SJiyong Park #include <lib/el3_runtime/cpu_data.h>
21*54fd6939SJiyong Park #include <lib/el3_runtime/pubsub_events.h>
22*54fd6939SJiyong Park #include <plat/common/platform.h>
23*54fd6939SJiyong Park
24*54fd6939SJiyong Park /* Output EHF logs as verbose */
25*54fd6939SJiyong Park #define EHF_LOG(...) VERBOSE("EHF: " __VA_ARGS__)
26*54fd6939SJiyong Park
27*54fd6939SJiyong Park #define EHF_INVALID_IDX (-1)
28*54fd6939SJiyong Park
29*54fd6939SJiyong Park /* For a valid handler, return the actual function pointer; otherwise, 0. */
30*54fd6939SJiyong Park #define RAW_HANDLER(h) \
31*54fd6939SJiyong Park ((ehf_handler_t) ((((h) & EHF_PRI_VALID_) != 0U) ? \
32*54fd6939SJiyong Park ((h) & ~EHF_PRI_VALID_) : 0U))
33*54fd6939SJiyong Park
34*54fd6939SJiyong Park #define PRI_BIT(idx) (((ehf_pri_bits_t) 1u) << (idx))
35*54fd6939SJiyong Park
36*54fd6939SJiyong Park /*
37*54fd6939SJiyong Park * Convert index into secure priority using the platform-defined priority bits
38*54fd6939SJiyong Park * field.
39*54fd6939SJiyong Park */
40*54fd6939SJiyong Park #define IDX_TO_PRI(idx) \
41*54fd6939SJiyong Park ((((unsigned) idx) << (7u - exception_data.pri_bits)) & 0x7fU)
42*54fd6939SJiyong Park
43*54fd6939SJiyong Park /* Check whether a given index is valid */
44*54fd6939SJiyong Park #define IS_IDX_VALID(idx) \
45*54fd6939SJiyong Park ((exception_data.ehf_priorities[idx].ehf_handler & EHF_PRI_VALID_) != 0U)
46*54fd6939SJiyong Park
47*54fd6939SJiyong Park /* Returns whether given priority is in secure priority range */
48*54fd6939SJiyong Park #define IS_PRI_SECURE(pri) (((pri) & 0x80U) == 0U)
49*54fd6939SJiyong Park
50*54fd6939SJiyong Park /* To be defined by the platform */
51*54fd6939SJiyong Park extern const ehf_priorities_t exception_data;
52*54fd6939SJiyong Park
53*54fd6939SJiyong Park /* Translate priority to the index in the priority array */
pri_to_idx(unsigned int priority)54*54fd6939SJiyong Park static unsigned int pri_to_idx(unsigned int priority)
55*54fd6939SJiyong Park {
56*54fd6939SJiyong Park unsigned int idx;
57*54fd6939SJiyong Park
58*54fd6939SJiyong Park idx = EHF_PRI_TO_IDX(priority, exception_data.pri_bits);
59*54fd6939SJiyong Park assert(idx < exception_data.num_priorities);
60*54fd6939SJiyong Park assert(IS_IDX_VALID(idx));
61*54fd6939SJiyong Park
62*54fd6939SJiyong Park return idx;
63*54fd6939SJiyong Park }
64*54fd6939SJiyong Park
65*54fd6939SJiyong Park /* Return whether there are outstanding priority activation */
has_valid_pri_activations(pe_exc_data_t * pe_data)66*54fd6939SJiyong Park static bool has_valid_pri_activations(pe_exc_data_t *pe_data)
67*54fd6939SJiyong Park {
68*54fd6939SJiyong Park return pe_data->active_pri_bits != 0U;
69*54fd6939SJiyong Park }
70*54fd6939SJiyong Park
this_cpu_data(void)71*54fd6939SJiyong Park static pe_exc_data_t *this_cpu_data(void)
72*54fd6939SJiyong Park {
73*54fd6939SJiyong Park return &get_cpu_data(ehf_data);
74*54fd6939SJiyong Park }
75*54fd6939SJiyong Park
76*54fd6939SJiyong Park /*
77*54fd6939SJiyong Park * Return the current priority index of this CPU. If no priority is active,
78*54fd6939SJiyong Park * return EHF_INVALID_IDX.
79*54fd6939SJiyong Park */
get_pe_highest_active_idx(pe_exc_data_t * pe_data)80*54fd6939SJiyong Park static int get_pe_highest_active_idx(pe_exc_data_t *pe_data)
81*54fd6939SJiyong Park {
82*54fd6939SJiyong Park if (!has_valid_pri_activations(pe_data))
83*54fd6939SJiyong Park return EHF_INVALID_IDX;
84*54fd6939SJiyong Park
85*54fd6939SJiyong Park /* Current priority is the right-most bit */
86*54fd6939SJiyong Park return (int) __builtin_ctz(pe_data->active_pri_bits);
87*54fd6939SJiyong Park }
88*54fd6939SJiyong Park
89*54fd6939SJiyong Park /*
90*54fd6939SJiyong Park * Mark priority active by setting the corresponding bit in active_pri_bits and
91*54fd6939SJiyong Park * programming the priority mask.
92*54fd6939SJiyong Park *
93*54fd6939SJiyong Park * This API is to be used as part of delegating to lower ELs other than for
94*54fd6939SJiyong Park * interrupts; e.g. while handling synchronous exceptions.
95*54fd6939SJiyong Park *
96*54fd6939SJiyong Park * This API is expected to be invoked before restoring context (Secure or
97*54fd6939SJiyong Park * Non-secure) in preparation for the respective dispatch.
98*54fd6939SJiyong Park */
ehf_activate_priority(unsigned int priority)99*54fd6939SJiyong Park void ehf_activate_priority(unsigned int priority)
100*54fd6939SJiyong Park {
101*54fd6939SJiyong Park int cur_pri_idx;
102*54fd6939SJiyong Park unsigned int old_mask, run_pri, idx;
103*54fd6939SJiyong Park pe_exc_data_t *pe_data = this_cpu_data();
104*54fd6939SJiyong Park
105*54fd6939SJiyong Park /*
106*54fd6939SJiyong Park * Query interrupt controller for the running priority, or idle priority
107*54fd6939SJiyong Park * if no interrupts are being handled. The requested priority must be
108*54fd6939SJiyong Park * less (higher priority) than the active running priority.
109*54fd6939SJiyong Park */
110*54fd6939SJiyong Park run_pri = plat_ic_get_running_priority();
111*54fd6939SJiyong Park if (priority >= run_pri) {
112*54fd6939SJiyong Park ERROR("Running priority higher (0x%x) than requested (0x%x)\n",
113*54fd6939SJiyong Park run_pri, priority);
114*54fd6939SJiyong Park panic();
115*54fd6939SJiyong Park }
116*54fd6939SJiyong Park
117*54fd6939SJiyong Park /*
118*54fd6939SJiyong Park * If there were priority activations already, the requested priority
119*54fd6939SJiyong Park * must be less (higher priority) than the current highest priority
120*54fd6939SJiyong Park * activation so far.
121*54fd6939SJiyong Park */
122*54fd6939SJiyong Park cur_pri_idx = get_pe_highest_active_idx(pe_data);
123*54fd6939SJiyong Park idx = pri_to_idx(priority);
124*54fd6939SJiyong Park if ((cur_pri_idx != EHF_INVALID_IDX) &&
125*54fd6939SJiyong Park (idx >= ((unsigned int) cur_pri_idx))) {
126*54fd6939SJiyong Park ERROR("Activation priority mismatch: req=0x%x current=0x%x\n",
127*54fd6939SJiyong Park priority, IDX_TO_PRI(cur_pri_idx));
128*54fd6939SJiyong Park panic();
129*54fd6939SJiyong Park }
130*54fd6939SJiyong Park
131*54fd6939SJiyong Park /* Set the bit corresponding to the requested priority */
132*54fd6939SJiyong Park pe_data->active_pri_bits |= PRI_BIT(idx);
133*54fd6939SJiyong Park
134*54fd6939SJiyong Park /*
135*54fd6939SJiyong Park * Program priority mask for the activated level. Check that the new
136*54fd6939SJiyong Park * priority mask is setting a higher priority level than the existing
137*54fd6939SJiyong Park * mask.
138*54fd6939SJiyong Park */
139*54fd6939SJiyong Park old_mask = plat_ic_set_priority_mask(priority);
140*54fd6939SJiyong Park if (priority >= old_mask) {
141*54fd6939SJiyong Park ERROR("Requested priority (0x%x) lower than Priority Mask (0x%x)\n",
142*54fd6939SJiyong Park priority, old_mask);
143*54fd6939SJiyong Park panic();
144*54fd6939SJiyong Park }
145*54fd6939SJiyong Park
146*54fd6939SJiyong Park /*
147*54fd6939SJiyong Park * If this is the first activation, save the priority mask. This will be
148*54fd6939SJiyong Park * restored after the last deactivation.
149*54fd6939SJiyong Park */
150*54fd6939SJiyong Park if (cur_pri_idx == EHF_INVALID_IDX)
151*54fd6939SJiyong Park pe_data->init_pri_mask = (uint8_t) old_mask;
152*54fd6939SJiyong Park
153*54fd6939SJiyong Park EHF_LOG("activate prio=%d\n", get_pe_highest_active_idx(pe_data));
154*54fd6939SJiyong Park }
155*54fd6939SJiyong Park
156*54fd6939SJiyong Park /*
157*54fd6939SJiyong Park * Mark priority inactive by clearing the corresponding bit in active_pri_bits,
158*54fd6939SJiyong Park * and programming the priority mask.
159*54fd6939SJiyong Park *
160*54fd6939SJiyong Park * This API is expected to be used as part of delegating to to lower ELs other
161*54fd6939SJiyong Park * than for interrupts; e.g. while handling synchronous exceptions.
162*54fd6939SJiyong Park *
163*54fd6939SJiyong Park * This API is expected to be invoked after saving context (Secure or
164*54fd6939SJiyong Park * Non-secure), having concluded the respective dispatch.
165*54fd6939SJiyong Park */
ehf_deactivate_priority(unsigned int priority)166*54fd6939SJiyong Park void ehf_deactivate_priority(unsigned int priority)
167*54fd6939SJiyong Park {
168*54fd6939SJiyong Park int cur_pri_idx;
169*54fd6939SJiyong Park pe_exc_data_t *pe_data = this_cpu_data();
170*54fd6939SJiyong Park unsigned int old_mask, run_pri, idx;
171*54fd6939SJiyong Park
172*54fd6939SJiyong Park /*
173*54fd6939SJiyong Park * Query interrupt controller for the running priority, or idle priority
174*54fd6939SJiyong Park * if no interrupts are being handled. The requested priority must be
175*54fd6939SJiyong Park * less (higher priority) than the active running priority.
176*54fd6939SJiyong Park */
177*54fd6939SJiyong Park run_pri = plat_ic_get_running_priority();
178*54fd6939SJiyong Park if (priority >= run_pri) {
179*54fd6939SJiyong Park ERROR("Running priority higher (0x%x) than requested (0x%x)\n",
180*54fd6939SJiyong Park run_pri, priority);
181*54fd6939SJiyong Park panic();
182*54fd6939SJiyong Park }
183*54fd6939SJiyong Park
184*54fd6939SJiyong Park /*
185*54fd6939SJiyong Park * Deactivation is allowed only when there are priority activations, and
186*54fd6939SJiyong Park * the deactivation priority level must match the current activated
187*54fd6939SJiyong Park * priority.
188*54fd6939SJiyong Park */
189*54fd6939SJiyong Park cur_pri_idx = get_pe_highest_active_idx(pe_data);
190*54fd6939SJiyong Park idx = pri_to_idx(priority);
191*54fd6939SJiyong Park if ((cur_pri_idx == EHF_INVALID_IDX) ||
192*54fd6939SJiyong Park (idx != ((unsigned int) cur_pri_idx))) {
193*54fd6939SJiyong Park ERROR("Deactivation priority mismatch: req=0x%x current=0x%x\n",
194*54fd6939SJiyong Park priority, IDX_TO_PRI(cur_pri_idx));
195*54fd6939SJiyong Park panic();
196*54fd6939SJiyong Park }
197*54fd6939SJiyong Park
198*54fd6939SJiyong Park /* Clear bit corresponding to highest priority */
199*54fd6939SJiyong Park pe_data->active_pri_bits &= (pe_data->active_pri_bits - 1u);
200*54fd6939SJiyong Park
201*54fd6939SJiyong Park /*
202*54fd6939SJiyong Park * Restore priority mask corresponding to the next priority, or the
203*54fd6939SJiyong Park * one stashed earlier if there are no more to deactivate.
204*54fd6939SJiyong Park */
205*54fd6939SJiyong Park cur_pri_idx = get_pe_highest_active_idx(pe_data);
206*54fd6939SJiyong Park if (cur_pri_idx == EHF_INVALID_IDX)
207*54fd6939SJiyong Park old_mask = plat_ic_set_priority_mask(pe_data->init_pri_mask);
208*54fd6939SJiyong Park else
209*54fd6939SJiyong Park old_mask = plat_ic_set_priority_mask(priority);
210*54fd6939SJiyong Park
211*54fd6939SJiyong Park if (old_mask > priority) {
212*54fd6939SJiyong Park ERROR("Deactivation priority (0x%x) lower than Priority Mask (0x%x)\n",
213*54fd6939SJiyong Park priority, old_mask);
214*54fd6939SJiyong Park panic();
215*54fd6939SJiyong Park }
216*54fd6939SJiyong Park
217*54fd6939SJiyong Park EHF_LOG("deactivate prio=%d\n", get_pe_highest_active_idx(pe_data));
218*54fd6939SJiyong Park }
219*54fd6939SJiyong Park
220*54fd6939SJiyong Park /*
221*54fd6939SJiyong Park * After leaving Non-secure world, stash current Non-secure Priority Mask, and
222*54fd6939SJiyong Park * set Priority Mask to the highest Non-secure priority so that Non-secure
223*54fd6939SJiyong Park * interrupts cannot preempt Secure execution.
224*54fd6939SJiyong Park *
225*54fd6939SJiyong Park * If the current running priority is in the secure range, or if there are
226*54fd6939SJiyong Park * outstanding priority activations, this function does nothing.
227*54fd6939SJiyong Park *
228*54fd6939SJiyong Park * This function subscribes to the 'cm_exited_normal_world' event published by
229*54fd6939SJiyong Park * the Context Management Library.
230*54fd6939SJiyong Park */
ehf_exited_normal_world(const void * arg)231*54fd6939SJiyong Park static void *ehf_exited_normal_world(const void *arg)
232*54fd6939SJiyong Park {
233*54fd6939SJiyong Park unsigned int run_pri;
234*54fd6939SJiyong Park pe_exc_data_t *pe_data = this_cpu_data();
235*54fd6939SJiyong Park
236*54fd6939SJiyong Park /* If the running priority is in the secure range, do nothing */
237*54fd6939SJiyong Park run_pri = plat_ic_get_running_priority();
238*54fd6939SJiyong Park if (IS_PRI_SECURE(run_pri))
239*54fd6939SJiyong Park return NULL;
240*54fd6939SJiyong Park
241*54fd6939SJiyong Park /* Do nothing if there are explicit activations */
242*54fd6939SJiyong Park if (has_valid_pri_activations(pe_data))
243*54fd6939SJiyong Park return NULL;
244*54fd6939SJiyong Park
245*54fd6939SJiyong Park assert(pe_data->ns_pri_mask == 0u);
246*54fd6939SJiyong Park
247*54fd6939SJiyong Park pe_data->ns_pri_mask =
248*54fd6939SJiyong Park (uint8_t) plat_ic_set_priority_mask(GIC_HIGHEST_NS_PRIORITY);
249*54fd6939SJiyong Park
250*54fd6939SJiyong Park /* The previous Priority Mask is not expected to be in secure range */
251*54fd6939SJiyong Park if (IS_PRI_SECURE(pe_data->ns_pri_mask)) {
252*54fd6939SJiyong Park ERROR("Priority Mask (0x%x) already in secure range\n",
253*54fd6939SJiyong Park pe_data->ns_pri_mask);
254*54fd6939SJiyong Park panic();
255*54fd6939SJiyong Park }
256*54fd6939SJiyong Park
257*54fd6939SJiyong Park EHF_LOG("Priority Mask: 0x%x => 0x%x\n", pe_data->ns_pri_mask,
258*54fd6939SJiyong Park GIC_HIGHEST_NS_PRIORITY);
259*54fd6939SJiyong Park
260*54fd6939SJiyong Park return NULL;
261*54fd6939SJiyong Park }
262*54fd6939SJiyong Park
263*54fd6939SJiyong Park /*
264*54fd6939SJiyong Park * Conclude Secure execution and prepare for return to Non-secure world. Restore
265*54fd6939SJiyong Park * the Non-secure Priority Mask previously stashed upon leaving Non-secure
266*54fd6939SJiyong Park * world.
267*54fd6939SJiyong Park *
268*54fd6939SJiyong Park * If there the current running priority is in the secure range, or if there are
269*54fd6939SJiyong Park * outstanding priority activations, this function does nothing.
270*54fd6939SJiyong Park *
271*54fd6939SJiyong Park * This function subscribes to the 'cm_entering_normal_world' event published by
272*54fd6939SJiyong Park * the Context Management Library.
273*54fd6939SJiyong Park */
ehf_entering_normal_world(const void * arg)274*54fd6939SJiyong Park static void *ehf_entering_normal_world(const void *arg)
275*54fd6939SJiyong Park {
276*54fd6939SJiyong Park unsigned int old_pmr, run_pri;
277*54fd6939SJiyong Park pe_exc_data_t *pe_data = this_cpu_data();
278*54fd6939SJiyong Park
279*54fd6939SJiyong Park /* If the running priority is in the secure range, do nothing */
280*54fd6939SJiyong Park run_pri = plat_ic_get_running_priority();
281*54fd6939SJiyong Park if (IS_PRI_SECURE(run_pri))
282*54fd6939SJiyong Park return NULL;
283*54fd6939SJiyong Park
284*54fd6939SJiyong Park /*
285*54fd6939SJiyong Park * If there are explicit activations, do nothing. The Priority Mask will
286*54fd6939SJiyong Park * be restored upon the last deactivation.
287*54fd6939SJiyong Park */
288*54fd6939SJiyong Park if (has_valid_pri_activations(pe_data))
289*54fd6939SJiyong Park return NULL;
290*54fd6939SJiyong Park
291*54fd6939SJiyong Park /* Do nothing if we don't have a valid Priority Mask to restore */
292*54fd6939SJiyong Park if (pe_data->ns_pri_mask == 0U)
293*54fd6939SJiyong Park return NULL;
294*54fd6939SJiyong Park
295*54fd6939SJiyong Park old_pmr = plat_ic_set_priority_mask(pe_data->ns_pri_mask);
296*54fd6939SJiyong Park
297*54fd6939SJiyong Park /*
298*54fd6939SJiyong Park * When exiting secure world, the current Priority Mask must be
299*54fd6939SJiyong Park * GIC_HIGHEST_NS_PRIORITY (as set during entry), or the Non-secure
300*54fd6939SJiyong Park * priority mask set upon calling ehf_allow_ns_preemption()
301*54fd6939SJiyong Park */
302*54fd6939SJiyong Park if ((old_pmr != GIC_HIGHEST_NS_PRIORITY) &&
303*54fd6939SJiyong Park (old_pmr != pe_data->ns_pri_mask)) {
304*54fd6939SJiyong Park ERROR("Invalid Priority Mask (0x%x) restored\n", old_pmr);
305*54fd6939SJiyong Park panic();
306*54fd6939SJiyong Park }
307*54fd6939SJiyong Park
308*54fd6939SJiyong Park EHF_LOG("Priority Mask: 0x%x => 0x%x\n", old_pmr, pe_data->ns_pri_mask);
309*54fd6939SJiyong Park
310*54fd6939SJiyong Park pe_data->ns_pri_mask = 0;
311*54fd6939SJiyong Park
312*54fd6939SJiyong Park return NULL;
313*54fd6939SJiyong Park }
314*54fd6939SJiyong Park
315*54fd6939SJiyong Park /*
316*54fd6939SJiyong Park * Program Priority Mask to the original Non-secure priority such that
317*54fd6939SJiyong Park * Non-secure interrupts may preempt Secure execution (for example, during
318*54fd6939SJiyong Park * Yielding SMC calls). The 'preempt_ret_code' parameter indicates the Yielding
319*54fd6939SJiyong Park * SMC's return value in case the call was preempted.
320*54fd6939SJiyong Park *
321*54fd6939SJiyong Park * This API is expected to be invoked before delegating a yielding SMC to Secure
322*54fd6939SJiyong Park * EL1. I.e. within the window of secure execution after Non-secure context is
323*54fd6939SJiyong Park * saved (after entry into EL3) and Secure context is restored (before entering
324*54fd6939SJiyong Park * Secure EL1).
325*54fd6939SJiyong Park */
ehf_allow_ns_preemption(uint64_t preempt_ret_code)326*54fd6939SJiyong Park void ehf_allow_ns_preemption(uint64_t preempt_ret_code)
327*54fd6939SJiyong Park {
328*54fd6939SJiyong Park cpu_context_t *ns_ctx;
329*54fd6939SJiyong Park unsigned int old_pmr __unused;
330*54fd6939SJiyong Park pe_exc_data_t *pe_data = this_cpu_data();
331*54fd6939SJiyong Park
332*54fd6939SJiyong Park /*
333*54fd6939SJiyong Park * We should have been notified earlier of entering secure world, and
334*54fd6939SJiyong Park * therefore have stashed the Non-secure priority mask.
335*54fd6939SJiyong Park */
336*54fd6939SJiyong Park assert(pe_data->ns_pri_mask != 0U);
337*54fd6939SJiyong Park
338*54fd6939SJiyong Park /* Make sure no priority levels are active when requesting this */
339*54fd6939SJiyong Park if (has_valid_pri_activations(pe_data)) {
340*54fd6939SJiyong Park ERROR("PE %lx has priority activations: 0x%x\n",
341*54fd6939SJiyong Park read_mpidr_el1(), pe_data->active_pri_bits);
342*54fd6939SJiyong Park panic();
343*54fd6939SJiyong Park }
344*54fd6939SJiyong Park
345*54fd6939SJiyong Park /*
346*54fd6939SJiyong Park * Program preempted return code to x0 right away so that, if the
347*54fd6939SJiyong Park * Yielding SMC was indeed preempted before a dispatcher gets a chance
348*54fd6939SJiyong Park * to populate it, the caller would find the correct return value.
349*54fd6939SJiyong Park */
350*54fd6939SJiyong Park ns_ctx = cm_get_context(NON_SECURE);
351*54fd6939SJiyong Park assert(ns_ctx != NULL);
352*54fd6939SJiyong Park write_ctx_reg(get_gpregs_ctx(ns_ctx), CTX_GPREG_X0, preempt_ret_code);
353*54fd6939SJiyong Park
354*54fd6939SJiyong Park old_pmr = plat_ic_set_priority_mask(pe_data->ns_pri_mask);
355*54fd6939SJiyong Park
356*54fd6939SJiyong Park EHF_LOG("Priority Mask: 0x%x => 0x%x\n", old_pmr, pe_data->ns_pri_mask);
357*54fd6939SJiyong Park
358*54fd6939SJiyong Park pe_data->ns_pri_mask = 0;
359*54fd6939SJiyong Park }
360*54fd6939SJiyong Park
361*54fd6939SJiyong Park /*
362*54fd6939SJiyong Park * Return whether Secure execution has explicitly allowed Non-secure interrupts
363*54fd6939SJiyong Park * to preempt itself (for example, during Yielding SMC calls).
364*54fd6939SJiyong Park */
ehf_is_ns_preemption_allowed(void)365*54fd6939SJiyong Park unsigned int ehf_is_ns_preemption_allowed(void)
366*54fd6939SJiyong Park {
367*54fd6939SJiyong Park unsigned int run_pri;
368*54fd6939SJiyong Park pe_exc_data_t *pe_data = this_cpu_data();
369*54fd6939SJiyong Park
370*54fd6939SJiyong Park /* If running priority is in secure range, return false */
371*54fd6939SJiyong Park run_pri = plat_ic_get_running_priority();
372*54fd6939SJiyong Park if (IS_PRI_SECURE(run_pri))
373*54fd6939SJiyong Park return 0;
374*54fd6939SJiyong Park
375*54fd6939SJiyong Park /*
376*54fd6939SJiyong Park * If Non-secure preemption was permitted by calling
377*54fd6939SJiyong Park * ehf_allow_ns_preemption() earlier:
378*54fd6939SJiyong Park *
379*54fd6939SJiyong Park * - There wouldn't have been priority activations;
380*54fd6939SJiyong Park * - We would have cleared the stashed the Non-secure Priority Mask.
381*54fd6939SJiyong Park */
382*54fd6939SJiyong Park if (has_valid_pri_activations(pe_data))
383*54fd6939SJiyong Park return 0;
384*54fd6939SJiyong Park if (pe_data->ns_pri_mask != 0U)
385*54fd6939SJiyong Park return 0;
386*54fd6939SJiyong Park
387*54fd6939SJiyong Park return 1;
388*54fd6939SJiyong Park }
389*54fd6939SJiyong Park
390*54fd6939SJiyong Park /*
391*54fd6939SJiyong Park * Top-level EL3 interrupt handler.
392*54fd6939SJiyong Park */
ehf_el3_interrupt_handler(uint32_t id,uint32_t flags,void * handle,void * cookie)393*54fd6939SJiyong Park static uint64_t ehf_el3_interrupt_handler(uint32_t id, uint32_t flags,
394*54fd6939SJiyong Park void *handle, void *cookie)
395*54fd6939SJiyong Park {
396*54fd6939SJiyong Park int ret = 0;
397*54fd6939SJiyong Park uint32_t intr_raw;
398*54fd6939SJiyong Park unsigned int intr, pri, idx;
399*54fd6939SJiyong Park ehf_handler_t handler;
400*54fd6939SJiyong Park
401*54fd6939SJiyong Park /*
402*54fd6939SJiyong Park * Top-level interrupt type handler from Interrupt Management Framework
403*54fd6939SJiyong Park * doesn't acknowledge the interrupt; so the interrupt ID must be
404*54fd6939SJiyong Park * invalid.
405*54fd6939SJiyong Park */
406*54fd6939SJiyong Park assert(id == INTR_ID_UNAVAILABLE);
407*54fd6939SJiyong Park
408*54fd6939SJiyong Park /*
409*54fd6939SJiyong Park * Acknowledge interrupt. Proceed with handling only for valid interrupt
410*54fd6939SJiyong Park * IDs. This situation may arise because of Interrupt Management
411*54fd6939SJiyong Park * Framework identifying an EL3 interrupt, but before it's been
412*54fd6939SJiyong Park * acknowledged here, the interrupt was either deasserted, or there was
413*54fd6939SJiyong Park * a higher-priority interrupt of another type.
414*54fd6939SJiyong Park */
415*54fd6939SJiyong Park intr_raw = plat_ic_acknowledge_interrupt();
416*54fd6939SJiyong Park intr = plat_ic_get_interrupt_id(intr_raw);
417*54fd6939SJiyong Park if (intr == INTR_ID_UNAVAILABLE)
418*54fd6939SJiyong Park return 0;
419*54fd6939SJiyong Park
420*54fd6939SJiyong Park /* Having acknowledged the interrupt, get the running priority */
421*54fd6939SJiyong Park pri = plat_ic_get_running_priority();
422*54fd6939SJiyong Park
423*54fd6939SJiyong Park /* Check EL3 interrupt priority is in secure range */
424*54fd6939SJiyong Park assert(IS_PRI_SECURE(pri));
425*54fd6939SJiyong Park
426*54fd6939SJiyong Park /*
427*54fd6939SJiyong Park * Translate the priority to a descriptor index. We do this by masking
428*54fd6939SJiyong Park * and shifting the running priority value (platform-supplied).
429*54fd6939SJiyong Park */
430*54fd6939SJiyong Park idx = pri_to_idx(pri);
431*54fd6939SJiyong Park
432*54fd6939SJiyong Park /* Validate priority */
433*54fd6939SJiyong Park assert(pri == IDX_TO_PRI(idx));
434*54fd6939SJiyong Park
435*54fd6939SJiyong Park handler = (ehf_handler_t) RAW_HANDLER(
436*54fd6939SJiyong Park exception_data.ehf_priorities[idx].ehf_handler);
437*54fd6939SJiyong Park if (handler == NULL) {
438*54fd6939SJiyong Park ERROR("No EL3 exception handler for priority 0x%x\n",
439*54fd6939SJiyong Park IDX_TO_PRI(idx));
440*54fd6939SJiyong Park panic();
441*54fd6939SJiyong Park }
442*54fd6939SJiyong Park
443*54fd6939SJiyong Park /*
444*54fd6939SJiyong Park * Call registered handler. Pass the raw interrupt value to registered
445*54fd6939SJiyong Park * handlers.
446*54fd6939SJiyong Park */
447*54fd6939SJiyong Park ret = handler(intr_raw, flags, handle, cookie);
448*54fd6939SJiyong Park
449*54fd6939SJiyong Park return (uint64_t) ret;
450*54fd6939SJiyong Park }
451*54fd6939SJiyong Park
452*54fd6939SJiyong Park /*
453*54fd6939SJiyong Park * Initialize the EL3 exception handling.
454*54fd6939SJiyong Park */
ehf_init(void)455*54fd6939SJiyong Park void __init ehf_init(void)
456*54fd6939SJiyong Park {
457*54fd6939SJiyong Park unsigned int flags = 0;
458*54fd6939SJiyong Park int ret __unused;
459*54fd6939SJiyong Park
460*54fd6939SJiyong Park /* Ensure EL3 interrupts are supported */
461*54fd6939SJiyong Park assert(plat_ic_has_interrupt_type(INTR_TYPE_EL3) != 0);
462*54fd6939SJiyong Park
463*54fd6939SJiyong Park /*
464*54fd6939SJiyong Park * Make sure that priority water mark has enough bits to represent the
465*54fd6939SJiyong Park * whole priority array.
466*54fd6939SJiyong Park */
467*54fd6939SJiyong Park assert(exception_data.num_priorities <= (sizeof(ehf_pri_bits_t) * 8U));
468*54fd6939SJiyong Park
469*54fd6939SJiyong Park assert(exception_data.ehf_priorities != NULL);
470*54fd6939SJiyong Park
471*54fd6939SJiyong Park /*
472*54fd6939SJiyong Park * Bit 7 of GIC priority must be 0 for secure interrupts. This means
473*54fd6939SJiyong Park * platforms must use at least 1 of the remaining 7 bits.
474*54fd6939SJiyong Park */
475*54fd6939SJiyong Park assert((exception_data.pri_bits >= 1U) ||
476*54fd6939SJiyong Park (exception_data.pri_bits < 8U));
477*54fd6939SJiyong Park
478*54fd6939SJiyong Park /* Route EL3 interrupts when in Secure and Non-secure. */
479*54fd6939SJiyong Park set_interrupt_rm_flag(flags, NON_SECURE);
480*54fd6939SJiyong Park set_interrupt_rm_flag(flags, SECURE);
481*54fd6939SJiyong Park
482*54fd6939SJiyong Park /* Register handler for EL3 interrupts */
483*54fd6939SJiyong Park ret = register_interrupt_type_handler(INTR_TYPE_EL3,
484*54fd6939SJiyong Park ehf_el3_interrupt_handler, flags);
485*54fd6939SJiyong Park assert(ret == 0);
486*54fd6939SJiyong Park }
487*54fd6939SJiyong Park
488*54fd6939SJiyong Park /*
489*54fd6939SJiyong Park * Register a handler at the supplied priority. Registration is allowed only if
490*54fd6939SJiyong Park * a handler hasn't been registered before, or one wasn't provided at build
491*54fd6939SJiyong Park * time. The priority for which the handler is being registered must also accord
492*54fd6939SJiyong Park * with the platform-supplied data.
493*54fd6939SJiyong Park */
ehf_register_priority_handler(unsigned int pri,ehf_handler_t handler)494*54fd6939SJiyong Park void ehf_register_priority_handler(unsigned int pri, ehf_handler_t handler)
495*54fd6939SJiyong Park {
496*54fd6939SJiyong Park unsigned int idx;
497*54fd6939SJiyong Park
498*54fd6939SJiyong Park /* Sanity check for handler */
499*54fd6939SJiyong Park assert(handler != NULL);
500*54fd6939SJiyong Park
501*54fd6939SJiyong Park /* Handler ought to be 4-byte aligned */
502*54fd6939SJiyong Park assert((((uintptr_t) handler) & 3U) == 0U);
503*54fd6939SJiyong Park
504*54fd6939SJiyong Park /* Ensure we register for valid priority */
505*54fd6939SJiyong Park idx = pri_to_idx(pri);
506*54fd6939SJiyong Park assert(idx < exception_data.num_priorities);
507*54fd6939SJiyong Park assert(IDX_TO_PRI(idx) == pri);
508*54fd6939SJiyong Park
509*54fd6939SJiyong Park /* Return failure if a handler was already registered */
510*54fd6939SJiyong Park if (exception_data.ehf_priorities[idx].ehf_handler != EHF_NO_HANDLER_) {
511*54fd6939SJiyong Park ERROR("Handler already registered for priority 0x%x\n", pri);
512*54fd6939SJiyong Park panic();
513*54fd6939SJiyong Park }
514*54fd6939SJiyong Park
515*54fd6939SJiyong Park /*
516*54fd6939SJiyong Park * Install handler, and retain the valid bit. We assume that the handler
517*54fd6939SJiyong Park * is 4-byte aligned, which is usually the case.
518*54fd6939SJiyong Park */
519*54fd6939SJiyong Park exception_data.ehf_priorities[idx].ehf_handler =
520*54fd6939SJiyong Park (((uintptr_t) handler) | EHF_PRI_VALID_);
521*54fd6939SJiyong Park
522*54fd6939SJiyong Park EHF_LOG("register pri=0x%x handler=%p\n", pri, handler);
523*54fd6939SJiyong Park }
524*54fd6939SJiyong Park
525*54fd6939SJiyong Park SUBSCRIBE_TO_EVENT(cm_entering_normal_world, ehf_entering_normal_world);
526*54fd6939SJiyong Park SUBSCRIBE_TO_EVENT(cm_exited_normal_world, ehf_exited_normal_world);
527