1 /*
2 * Copyright 2024 The Hafnium Authors.
3 *
4 * Use of this source code is governed by a BSD-style
5 * license that can be found in the LICENSE file or at
6 * https://opensource.org/licenses/BSD-3-Clause.
7 */
8
9 #include "hf/hf_ipi.h"
10
11 #include "hf/cpu.h"
12 #include "hf/ffa/notifications.h"
13 #include "hf/plat/interrupts.h"
14
15 /** Interrupt priority for Inter-Processor Interrupt. */
16 #define IPI_PRIORITY 0x0U
17
18 /**
19 * Initialize the IPI SGI.
20 */
hf_ipi_init_interrupt(void)21 void hf_ipi_init_interrupt(void)
22 {
23 /* Configure as a Secure SGI. */
24 struct interrupt_descriptor ipi_desc = {
25 .interrupt_id = HF_IPI_INTID,
26 .type = INT_DESC_TYPE_SGI,
27 .sec_state = INT_DESC_SEC_STATE_S,
28 .priority = IPI_PRIORITY,
29 .valid = true,
30 .enabled = true,
31 };
32
33 plat_interrupts_configure_interrupt(ipi_desc);
34 }
35
36 /**
37 * Returns the next target_vcpu with a pending IPI and removes it from
38 * the list for the current CPU to show it has been retrieved.
39 * The running vCPU is prioritised to prevent it being put into
40 * the PREEMPTED state before it has handled it's IPI, this could happen in
41 * the case a vCPU in the WAITING state also has a pending IPI.
42 * In the case of a spurious IPI physical interrupt, where the target
43 * vCPUs have already handled their pending IPIs return NULL.
44 */
hf_ipi_get_pending_target_vcpu(struct vcpu * current)45 struct vcpu *hf_ipi_get_pending_target_vcpu(struct vcpu *current)
46 {
47 struct list_entry *list;
48 struct vcpu *target_vcpu;
49
50 /* Lock the CPU the list belongs to. */
51 sl_lock(¤t->cpu->lock);
52
53 /*
54 * Check if the current vcpu has a pending interrupt,
55 * if so prioritise this.
56 */
57 if (!list_empty(¤t->ipi_list_node)) {
58 list = ¤t->ipi_list_node;
59 } else {
60 /*
61 * If the current cpu doesn't have a pending IPI check other
62 * vcpus on the current CPU.
63 */
64 list = ¤t->cpu->pending_ipis;
65
66 if (list_empty(list)) {
67 target_vcpu = NULL;
68 goto out;
69 }
70
71 /*
72 * The list is circular, the root element does not belong to a
73 * vCPU but is used to track if the list is empty and if not
74 * point to the first vCPU with a pending IPI.
75 */
76 list = list->next;
77 }
78
79 /*
80 * The next vCPU with a pending IPI has been retrieved to be handled
81 * so remove it from the list.
82 */
83 list_remove(list);
84 target_vcpu = CONTAINER_OF(list, struct vcpu, ipi_list_node);
85
86 out:
87 sl_unlock(¤t->cpu->lock);
88 return target_vcpu;
89 }
90
91 /**
92 * Send and record the IPI for the target vCPU.
93 */
hf_ipi_send_interrupt(struct vm * vm,ffa_vcpu_index_t target_vcpu_index)94 void hf_ipi_send_interrupt(struct vm *vm, ffa_vcpu_index_t target_vcpu_index)
95 {
96 struct vcpu *target_vcpu = vm_get_vcpu(vm, target_vcpu_index);
97 struct cpu *target_cpu = target_vcpu->cpu;
98
99 sl_lock(&target_cpu->lock);
100 /*
101 * Since vCPUs are pinned to a physical cpu they can only belong
102 * to one list. Therefore check if the vCPU is in a list. If not
103 * add it and send the IPI SGI.
104 */
105 if (list_empty(&target_vcpu->ipi_list_node)) {
106 list_prepend(&target_cpu->pending_ipis,
107 &target_vcpu->ipi_list_node);
108
109 plat_interrupts_send_sgi(HF_IPI_INTID, target_cpu, true);
110 }
111
112 sl_unlock(&target_cpu->lock);
113 }
114
115 /**
116 * Enum to track the next SRI action that should be performed for an IPI to
117 * a vCPU in the WAITING state.
118 */
119 enum ipi_sri_action {
120 /* First entry into the handling function. */
121 IPI_SRI_ACTION_INIT,
122 /* For a waiting state trigger and SRI not delayed. */
123 IPI_SRI_ACTION_NOT_DELAYED,
124 /*
125 * For a waiting state set delayed SRI to prioritize a running vCPU,
126 * preventing the running vCPU becoming preempted.
127 */
128 IPI_SRI_ACTION_DELAYED,
129 /* SRI already set. */
130 IPI_SRI_ACTION_NONE,
131 };
132
133 /**
134 * IPI IRQ handling for each vCPU state, the ipi_sri_action is used to know
135 * which SRI action to use when there is a vCPU in the WAITING state.
136 * Elements of the list of vCPUs on the CPU with pending IPIs will be traversed
137 * and depending of the state of each, the handling specific to IPIs will be
138 * taken:
139 * - RUNNING: Set the ipi_sri_action to IPI_SRI_ACTION_DELAYED, so if an SRI
140 * is required for a different vCPU, the running (current) vCPU will still
141 * handle the IPI. Return false so that the normal secure interrupt handling
142 * continues.
143 * - WAITING: If the ipi_sri_action is IPI_SRI_ACTION_NONE, an SRI has either
144 * already been triggered or set to delayed so we don't need to do anything.
145 * Otherwise:
146 * - If the running vCPU has a pending IPI, the ipi_sri_action will be
147 * IPI_SRI_ACTION_DELAYED so set the SRI to delayed, this means the SRI
148 * will be triggered on the next world switch to NWd and the running
149 * vCPU will not be stopped before it has handled it's IPI. Set the
150 * ipi_sri_action to IPI_SRI_ACTION_NONE, as we only need to set the
151 * SRI once.
152 * - If the running vCPU does not have a pending IPI, the ipi_sri_action
153 * will either be IPI_SRI_ACTION_INIT, if we are in the head of the list,
154 * or IPI_SRI_ACTION_NOT_DELAYED, in these cases we want to trigger the
155 * SRI immediately, so the NWd can schedule the target vCPU to handle
156 * the IPI. Set the ipi_sri_action to IPI_SRI_ACTION_NONE as we only need
157 * to trigger the SRI once.
158 * - PREEMPTED/BLOCKED:
159 * - If it's the head of the list (indicated by
160 * IPI_SRI_ACTION_INIT), return false and allow normal secure interrupt
161 * handling to handle the interrupt as usual.
162 * - Otherwise queue the interrupt for the vCPU.
163 * Returns True if the IPI SGI has been fully handled.
164 * False if further secure interrupt handling is required, this will
165 * only be the case for the target vCPU head of the pending ipi list, if it
166 * is in the RUNNING, PREEMPTED or BLOCKED state.
167 */
hf_ipi_handle_list_element(struct vcpu_locked target_vcpu_locked,enum ipi_sri_action * ipi_sri_action)168 static bool hf_ipi_handle_list_element(struct vcpu_locked target_vcpu_locked,
169 enum ipi_sri_action *ipi_sri_action)
170 {
171 bool ret = true;
172 struct vcpu *target_vcpu = target_vcpu_locked.vcpu;
173
174 assert(ipi_sri_action != NULL);
175
176 vcpu_virt_interrupt_inject(target_vcpu_locked, HF_IPI_INTID);
177
178 switch (target_vcpu->state) {
179 case VCPU_STATE_RUNNING:
180 if (*ipi_sri_action != IPI_SRI_ACTION_INIT) {
181 panic("%s: If present the RUNNING vCPU should be the "
182 "first to be handled.\n",
183 __func__);
184 }
185 /*
186 * Any SRI should be delayed to prioritize the running vCPU,
187 * preventing it from entering the PREEMPTED state by the SRI
188 * before the IPI is handled.
189 */
190 *ipi_sri_action = IPI_SRI_ACTION_DELAYED;
191 ret = false;
192 break;
193 case VCPU_STATE_WAITING:
194 if (*ipi_sri_action == IPI_SRI_ACTION_INIT ||
195 *ipi_sri_action == IPI_SRI_ACTION_NOT_DELAYED) {
196 /*
197 * The current target vCPU is either the first element
198 * in the pending list or there is not running vCPU in
199 * the list, so we are ok to trigger the SRI
200 * immediately.
201 */
202 ffa_notifications_sri_trigger_not_delayed(
203 target_vcpu->cpu);
204 } else if (*ipi_sri_action == IPI_SRI_ACTION_DELAYED) {
205 /*
206 * Otherwise a running vCPU has a pending IPI so set a
207 * delayed SRI, so as not to preempt the running vCPU
208 * before it is able to handle it's IPI.
209 */
210 ffa_notifications_sri_set_delayed(target_vcpu->cpu);
211 }
212 *ipi_sri_action = IPI_SRI_ACTION_NONE;
213 break;
214 case VCPU_STATE_BLOCKED:
215 case VCPU_STATE_PREEMPTED:
216 if (*ipi_sri_action == IPI_SRI_ACTION_INIT) {
217 /*
218 * The current target vCPU is the top of the list of
219 * pending IPIs so allow it to be handled by the default
220 * secure interrupt handling. Change the state to
221 * IPI_SRI_ACTION_NOT_DELAYED since there now can't be
222 * any running vCPUs with pending IPIs (it would have
223 * been the head of the list) so we are safe to trigger
224 * the SRI for any waiting vCPUs immediately.
225 */
226 *ipi_sri_action = IPI_SRI_ACTION_NOT_DELAYED;
227 ret = false;
228 }
229 break;
230 default:
231 dlog_error(
232 "%s: unexpected state: %u handling an IPI for [%x %u]",
233 __func__, target_vcpu->state, target_vcpu->vm->id,
234 vcpu_index(target_vcpu));
235 }
236
237 return ret;
238 }
239
240 /**
241 * IPI IRQ specific handling for the secure interrupt.
242 */
hf_ipi_handle(struct vcpu_locked target_vcpu_locked)243 bool hf_ipi_handle(struct vcpu_locked target_vcpu_locked)
244 {
245 enum ipi_sri_action ipi_sri_action = IPI_SRI_ACTION_INIT;
246 bool ret = true;
247
248 ret = hf_ipi_handle_list_element(target_vcpu_locked, &ipi_sri_action);
249
250 /*
251 * Clear the pending ipi list, handling the ipi for the remaining
252 * target vCPUs.
253 */
254 for (struct vcpu *target_vcpu =
255 hf_ipi_get_pending_target_vcpu(target_vcpu_locked.vcpu);
256 target_vcpu != NULL;
257 target_vcpu = hf_ipi_get_pending_target_vcpu(target_vcpu)) {
258 target_vcpu_locked = vcpu_lock(target_vcpu);
259 hf_ipi_handle_list_element(target_vcpu_locked, &ipi_sri_action);
260 vcpu_unlock(&target_vcpu_locked);
261 }
262
263 return ret;
264 }
265