blob: a084671536f5cd4197721b7aee076664eaf06793 [file] [log] [blame]
Fuad Tabba5c738432019-12-02 11:02:42 +00001/*
2 * Copyright 2019 The Hafnium Authors.
3 *
Andrew Walbrane959ec12020-06-17 15:01:09 +01004 * 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.
Fuad Tabba5c738432019-12-02 11:02:42 +00007 */
8
9#include "hf/vcpu.h"
10
Olivier Depreze6f7b9d2021-02-01 11:55:48 +010011#include "hf/arch/cpu.h"
12
Fuad Tabba5c738432019-12-02 11:02:42 +000013#include "hf/check.h"
14#include "hf/dlog.h"
15#include "hf/std.h"
16#include "hf/vm.h"
17
J-Alves7ac49052022-02-08 17:20:53 +000018/** GP register to be used to pass the current vCPU ID, at core bring up. */
19#define PHYS_CORE_IDX_GP_REG 4
20
Fuad Tabba5c738432019-12-02 11:02:42 +000021/**
22 * Locks the given vCPU and updates `locked` to hold the newly locked vCPU.
23 */
24struct vcpu_locked vcpu_lock(struct vcpu *vcpu)
25{
26 struct vcpu_locked locked = {
27 .vcpu = vcpu,
28 };
29
30 sl_lock(&vcpu->lock);
31
32 return locked;
33}
34
35/**
Olivier Deprez0b6f10a2020-08-05 18:21:33 +020036 * Locks two vCPUs ensuring that the locking order is according to the locks'
37 * addresses.
38 */
39struct two_vcpu_locked vcpu_lock_both(struct vcpu *vcpu1, struct vcpu *vcpu2)
40{
41 struct two_vcpu_locked dual_lock;
42
43 sl_lock_both(&vcpu1->lock, &vcpu2->lock);
44 dual_lock.vcpu1.vcpu = vcpu1;
45 dual_lock.vcpu2.vcpu = vcpu2;
46
47 return dual_lock;
48}
49
50/**
Fuad Tabba5c738432019-12-02 11:02:42 +000051 * Unlocks a vCPU previously locked with vpu_lock, and updates `locked` to
52 * reflect the fact that the vCPU is no longer locked.
53 */
54void vcpu_unlock(struct vcpu_locked *locked)
55{
56 sl_unlock(&locked->vcpu->lock);
57 locked->vcpu = NULL;
58}
59
60void vcpu_init(struct vcpu *vcpu, struct vm *vm)
61{
62 memset_s(vcpu, sizeof(*vcpu), 0, sizeof(*vcpu));
63 sl_init(&vcpu->lock);
64 vcpu->regs_available = true;
65 vcpu->vm = vm;
66 vcpu->state = VCPU_STATE_OFF;
Kathleen Capellae468c112023-12-13 17:56:28 -050067 vcpu->direct_request_origin.is_ffa_req2 = false;
68 vcpu->direct_request_origin.vm_id = HF_INVALID_VM_ID;
Olivier Deprezb2808332023-02-02 15:25:40 +010069 vcpu->rt_model = RTM_SP_INIT;
Madhukar Pappireddyeed861e2024-09-25 13:50:54 -050070 list_init(&vcpu->timer_node);
Daniel Boulby7011b5a2024-10-15 18:27:26 +010071 list_init(&vcpu->ipi_list_node);
Fuad Tabba5c738432019-12-02 11:02:42 +000072}
73
74/**
75 * Initialise the registers for the given vCPU and set the state to
Madhukar Pappireddyb11e0d12021-08-02 19:44:35 -050076 * VCPU_STATE_WAITING. The caller must hold the vCPU lock while calling this.
Fuad Tabba5c738432019-12-02 11:02:42 +000077 */
78void vcpu_on(struct vcpu_locked vcpu, ipaddr_t entry, uintreg_t arg)
79{
80 arch_regs_set_pc_arg(&vcpu.vcpu->regs, entry, arg);
Madhukar Pappireddyb11e0d12021-08-02 19:44:35 -050081 vcpu.vcpu->state = VCPU_STATE_WAITING;
Fuad Tabba5c738432019-12-02 11:02:42 +000082}
83
Andrew Walbranb5ab43c2020-04-30 11:32:54 +010084ffa_vcpu_index_t vcpu_index(const struct vcpu *vcpu)
Fuad Tabba5c738432019-12-02 11:02:42 +000085{
86 size_t index = vcpu - vcpu->vm->vcpus;
87
88 CHECK(index < UINT16_MAX);
89 return index;
90}
91
92/**
93 * Check whether the given vcpu_state is an off state, for the purpose of
Madhukar Pappireddyb11e0d12021-08-02 19:44:35 -050094 * turning vCPUs on and off. Note that Aborted still counts as ON for the
95 * purposes of PSCI, because according to the PSCI specification (section
Olivier Depreze7eb1682022-03-16 17:09:03 +010096 * 5.7.1) a core is only considered to be off if it has been turned off
97 * with a CPU_OFF call or hasn't yet been turned on with a CPU_ON call.
Fuad Tabba5c738432019-12-02 11:02:42 +000098 */
99bool vcpu_is_off(struct vcpu_locked vcpu)
100{
Madhukar Pappireddyb11e0d12021-08-02 19:44:35 -0500101 return (vcpu.vcpu->state == VCPU_STATE_OFF);
Fuad Tabba5c738432019-12-02 11:02:42 +0000102}
103
104/**
105 * Starts a vCPU of a secondary VM.
106 *
107 * Returns true if the secondary was reset and started, or false if it was
108 * already on and so nothing was done.
109 */
Max Shvetsov40108e72020-08-27 12:39:50 +0100110bool vcpu_secondary_reset_and_start(struct vcpu_locked vcpu_locked,
111 ipaddr_t entry, uintreg_t arg)
Fuad Tabba5c738432019-12-02 11:02:42 +0000112{
Max Shvetsov40108e72020-08-27 12:39:50 +0100113 struct vm *vm = vcpu_locked.vcpu->vm;
Fuad Tabba5c738432019-12-02 11:02:42 +0000114 bool vcpu_was_off;
115
116 CHECK(vm->id != HF_PRIMARY_VM_ID);
117
Fuad Tabba5c738432019-12-02 11:02:42 +0000118 vcpu_was_off = vcpu_is_off(vcpu_locked);
119 if (vcpu_was_off) {
120 /*
121 * Set vCPU registers to a clean state ready for boot. As this
122 * is a secondary which can migrate between pCPUs, the ID of the
123 * vCPU is defined as the index and does not match the ID of the
124 * pCPU it is running on.
125 */
Max Shvetsov40108e72020-08-27 12:39:50 +0100126 arch_regs_reset(vcpu_locked.vcpu);
Fuad Tabba5c738432019-12-02 11:02:42 +0000127 vcpu_on(vcpu_locked, entry, arg);
128 }
Fuad Tabba5c738432019-12-02 11:02:42 +0000129
130 return vcpu_was_off;
131}
132
133/**
134 * Handles a page fault. It does so by determining if it's a legitimate or
135 * spurious fault, and recovering from the latter.
136 *
Fuad Tabbaed294af2019-12-20 10:43:01 +0000137 * Returns true if the caller should resume the current vCPU, or false if its VM
Fuad Tabba5c738432019-12-02 11:02:42 +0000138 * should be aborted.
139 */
140bool vcpu_handle_page_fault(const struct vcpu *current,
141 struct vcpu_fault_info *f)
142{
143 struct vm *vm = current->vm;
144 uint32_t mode;
145 uint32_t mask = f->mode | MM_MODE_INVALID;
146 bool resume;
Raghu Krishnamurthy785d52f2021-02-13 00:02:40 -0800147 struct vm_locked locked_vm;
Fuad Tabba5c738432019-12-02 11:02:42 +0000148
Raghu Krishnamurthy785d52f2021-02-13 00:02:40 -0800149 locked_vm = vm_lock(vm);
Fuad Tabba5c738432019-12-02 11:02:42 +0000150 /*
151 * Check if this is a legitimate fault, i.e., if the page table doesn't
152 * allow the access attempted by the VM.
153 *
154 * Otherwise, this is a spurious fault, likely because another CPU is
155 * updating the page table. It is responsible for issuing global TLB
156 * invalidations while holding the VM lock, so we don't need to do
157 * anything else to recover from it. (Acquiring/releasing the lock
158 * ensured that the invalidations have completed.)
159 */
Raghu Krishnamurthyb5775d22021-02-26 18:54:40 -0800160 if (!locked_vm.vm->el0_partition) {
161 resume = vm_mem_get_mode(locked_vm, f->ipaddr,
162 ipa_add(f->ipaddr, 1), &mode) &&
163 (mode & mask) == f->mode;
164 } else {
165 /*
166 * For EL0 partitions we need to get the mode for the faulting
167 * vaddr.
168 */
169 resume =
170 vm_mem_get_mode(locked_vm, ipa_init(va_addr(f->vaddr)),
171 ipa_add(ipa_init(va_addr(f->vaddr)), 1),
172 &mode) &&
173 (mode & mask) == f->mode;
Raghu Krishnamurthyf16b2ce2021-11-02 07:48:38 -0700174
175 /*
176 * For EL0 partitions, if there is an instruction abort and the
177 * mode of the page is RWX, we don't resume since Hafnium does
178 * not allow write and executable pages.
179 */
180 if ((f->mode == MM_MODE_X) &&
181 ((mode & MM_MODE_W) == MM_MODE_W)) {
182 resume = false;
183 }
Raghu Krishnamurthyb5775d22021-02-26 18:54:40 -0800184 }
Fuad Tabba5c738432019-12-02 11:02:42 +0000185
Raghu Krishnamurthy785d52f2021-02-13 00:02:40 -0800186 vm_unlock(&locked_vm);
Fuad Tabba5c738432019-12-02 11:02:42 +0000187
188 if (!resume) {
Andrew Walbran17eebf92020-02-05 16:35:49 +0000189 dlog_warning(
Karl Meakine8937d92024-03-19 16:04:25 +0000190 "Stage-%d page fault: pc=%#lx, vmid=%#x, vcpu=%u, "
191 "vaddr=%#lx, ipaddr=%#lx, mode=%#x %#x\n",
192 current->vm->el0_partition ? 1 : 2, va_addr(f->pc),
193 vm->id, vcpu_index(current), va_addr(f->vaddr),
194 ipa_addr(f->ipaddr), f->mode, mode);
Fuad Tabba5c738432019-12-02 11:02:42 +0000195 }
196
197 return resume;
198}
Olivier Deprez2ebae3a2020-06-11 16:34:30 +0200199
J-Alves7ac49052022-02-08 17:20:53 +0000200void vcpu_set_phys_core_idx(struct vcpu *vcpu)
201{
202 arch_regs_set_gp_reg(&vcpu->regs, cpu_index(vcpu->cpu),
203 PHYS_CORE_IDX_GP_REG);
204}
Olivier Deprez181074b2023-02-02 14:53:23 +0100205
206/**
Olivier Deprez632249e2022-09-26 09:18:31 +0200207 * Sets the designated GP register through which the vCPU expects to receive the
208 * boot info's address.
209 */
210void vcpu_set_boot_info_gp_reg(struct vcpu *vcpu)
211{
212 struct vm *vm = vcpu->vm;
213 uint32_t gp_register_num = vm->boot_info.gp_register_num;
214
215 if (vm->boot_info.blob_addr.ipa != 0U) {
216 arch_regs_set_gp_reg(&vcpu->regs,
217 ipa_addr(vm->boot_info.blob_addr),
218 gp_register_num);
219 }
220}
221
Daniel Boulbyd633a612025-03-07 18:08:04 +0000222static bool vcpu_is_virt_interrupt_enabled(struct interrupts *interrupts,
223 uint32_t intid)
224{
225 return interrupt_bitmap_get_value(&interrupts->interrupt_enabled,
226 intid) == 1U;
227}
228
229static void vcpu_virt_interrupt_set_enabled(struct interrupts *interrupts,
230 uint32_t intid)
231{
232 interrupt_bitmap_set_value(&interrupts->interrupt_enabled, intid);
233}
234
235static void vcpu_virt_interrupt_clear_enabled(struct interrupts *interrupts,
236 uint32_t intid)
237{
238 interrupt_bitmap_clear_value(&interrupts->interrupt_enabled, intid);
239}
240
Daniel Boulbyd21e9b32025-02-13 15:53:21 +0000241static void vcpu_virt_interrupt_set_pending(struct interrupts *interrupts,
242 uint32_t intid)
243{
244 interrupt_bitmap_set_value(&interrupts->interrupt_pending, intid);
245}
246
247static void vcpu_virt_interrupt_clear_pending(struct interrupts *interrupts,
248 uint32_t intid)
249{
250 interrupt_bitmap_clear_value(&interrupts->interrupt_pending, intid);
251}
252
253static void vcpu_irq_count_increment(struct vcpu_locked vcpu_locked)
254{
255 vcpu_locked.vcpu->interrupts.enabled_and_pending_irq_count++;
256}
257
258static void vcpu_irq_count_decrement(struct vcpu_locked vcpu_locked)
259{
260 vcpu_locked.vcpu->interrupts.enabled_and_pending_irq_count--;
261}
262
263static void vcpu_fiq_count_increment(struct vcpu_locked vcpu_locked)
264{
265 vcpu_locked.vcpu->interrupts.enabled_and_pending_fiq_count++;
266}
267
268static void vcpu_fiq_count_decrement(struct vcpu_locked vcpu_locked)
269{
270 vcpu_locked.vcpu->interrupts.enabled_and_pending_fiq_count--;
271}
272
273static void vcpu_interrupt_count_increment(struct vcpu_locked vcpu_locked,
274 uint32_t intid)
275{
276 struct interrupts *interrupts = &vcpu_locked.vcpu->interrupts;
277
278 if (vcpu_virt_interrupt_get_type(interrupts, intid) ==
279 INTERRUPT_TYPE_IRQ) {
280 vcpu_irq_count_increment(vcpu_locked);
281 } else {
282 vcpu_fiq_count_increment(vcpu_locked);
283 }
284}
285
286static void vcpu_interrupt_count_decrement(struct vcpu_locked vcpu_locked,
287 uint32_t intid)
288{
289 struct interrupts *interrupts = &vcpu_locked.vcpu->interrupts;
290
291 if (vcpu_virt_interrupt_get_type(interrupts, intid) ==
292 INTERRUPT_TYPE_IRQ) {
293 vcpu_irq_count_decrement(vcpu_locked);
294 } else {
295 vcpu_fiq_count_decrement(vcpu_locked);
296 }
297}
298
299uint32_t vcpu_virt_interrupt_irq_count_get(struct vcpu_locked vcpu_locked)
300{
301 return vcpu_locked.vcpu->interrupts.enabled_and_pending_irq_count;
302}
303
304uint32_t vcpu_virt_interrupt_fiq_count_get(struct vcpu_locked vcpu_locked)
305{
306 return vcpu_locked.vcpu->interrupts.enabled_and_pending_fiq_count;
307}
308
309uint32_t vcpu_virt_interrupt_count_get(struct vcpu_locked vcpu_locked)
310{
311 return vcpu_virt_interrupt_irq_count_get(vcpu_locked) +
312 vcpu_virt_interrupt_fiq_count_get(vcpu_locked);
313}
314
J-Alvesb8730e92024-08-07 18:28:55 +0100315void vcpu_interrupt_clear_decrement(struct vcpu_locked vcpu_locked,
316 uint32_t intid)
317{
318 struct interrupts *interrupts = &(vcpu_locked.vcpu->interrupts);
319
Daniel Boulby1f2babf2024-08-29 16:39:47 +0100320 /* Clear any specifics for the current intid. */
321 switch (intid) {
322 case HF_IPI_INTID:
323 vcpu_ipi_clear_info_get_retrieved(vcpu_locked);
324 break;
325 default:
326 /* Do no additional work. */
327 break;
328 }
329
Daniel Boulbyd21e9b32025-02-13 15:53:21 +0000330 /*
331 * Mark the virtual interrupt as no longer pending and decrement
332 * the interrupt count.
333 */
J-Alvesb8730e92024-08-07 18:28:55 +0100334 vcpu_virt_interrupt_clear_pending(interrupts, intid);
Daniel Boulbyd21e9b32025-02-13 15:53:21 +0000335 vcpu_interrupt_count_decrement(vcpu_locked, intid);
J-Alvesb8730e92024-08-07 18:28:55 +0100336}
337
J-Alves0247fe62024-02-23 10:21:46 +0000338/**
339 * Sets the vcpu in the VCPU_STATE_RUNNING.
J-Alves67a79262024-07-17 12:01:39 +0100340 * With that, its register are set as "not available".
341 * If there are registers to be written to vCPU's context, do so.
342 * However, this action is restricted to WAITING and BLOCKED states,
343 * as such, assert accordingly.
J-Alves0247fe62024-02-23 10:21:46 +0000344 */
J-Alves478faac2024-10-23 10:35:57 +0100345void vcpu_set_running(struct vcpu_locked target_locked,
346 const struct ffa_value *args)
J-Alves12cedae2023-08-04 14:37:37 +0100347{
348 struct vcpu *target_vcpu = target_locked.vcpu;
349
J-Alves67a79262024-07-17 12:01:39 +0100350 if (args != NULL) {
J-Alves0247fe62024-02-23 10:21:46 +0000351 CHECK(target_vcpu->regs_available);
J-Alves67a79262024-07-17 12:01:39 +0100352 assert(target_vcpu->state == VCPU_STATE_WAITING ||
353 target_vcpu->state == VCPU_STATE_BLOCKED);
J-Alves0247fe62024-02-23 10:21:46 +0000354
355 arch_regs_set_retval(&target_vcpu->regs, *args);
356 }
J-Alves12cedae2023-08-04 14:37:37 +0100357
358 /* Mark the registers as unavailable now. */
359 target_vcpu->regs_available = false;
360
361 /* We are about to resume target vCPU. */
362 target_vcpu->state = VCPU_STATE_RUNNING;
363}
364
J-Alves12cedae2023-08-04 14:37:37 +0100365void vcpu_enter_secure_interrupt_rtm(struct vcpu_locked vcpu_locked)
366{
367 struct vcpu *target_vcpu = vcpu_locked.vcpu;
368
369 assert(target_vcpu->scheduling_mode == NONE);
370 assert(target_vcpu->call_chain.prev_node == NULL);
371 assert(target_vcpu->call_chain.next_node == NULL);
372 assert(target_vcpu->rt_model == RTM_NONE);
373
374 target_vcpu->scheduling_mode = SPMC_MODE;
375 target_vcpu->rt_model = RTM_SEC_INTERRUPT;
376}
Madhukar Pappireddy32913cb2024-07-19 13:04:05 -0500377
378static uint16_t queue_increment_index(uint16_t current_idx)
379{
380 /* Look at the next index. Wrap around if necessary. */
381 if (current_idx == VINT_QUEUE_MAX - 1) {
382 return 0;
383 }
384
385 return current_idx + 1;
386}
387
J-Alves23a73032025-03-04 16:20:54 +0000388/**
389 * If tail reaches head of the queue, and the count of queued interrupts
390 * 0, then the queue is empty.
391 */
Madhukar Pappireddy32913cb2024-07-19 13:04:05 -0500392static bool is_queue_empty(struct interrupt_queue *q)
393{
J-Alves23a73032025-03-04 16:20:54 +0000394 return q->head == q->tail && q->queued_vint_count == 0U;
395}
Madhukar Pappireddy32913cb2024-07-19 13:04:05 -0500396
J-Alves23a73032025-03-04 16:20:54 +0000397/**
398 * If tail reaches head of the queue, and the count of queued interrupts
399 * matches the size of the buffer, then the queue is full.
400 */
401static bool is_queue_full(struct interrupt_queue *q)
402{
403 return q->head == q->tail && q->queued_vint_count == VINT_QUEUE_MAX;
Madhukar Pappireddy32913cb2024-07-19 13:04:05 -0500404}
405
406/**
407 * Queue the pending virtual interrupt for target vCPU.
408 *
409 * Returns true if successful in pushing a new entry to the queue, or false
410 * otherwise.
411 */
Daniel Boulby3c1506b2025-02-25 10:49:51 +0000412static bool vcpu_interrupt_queue_push(struct vcpu_locked vcpu_locked,
413 uint32_t vint_id)
Madhukar Pappireddy32913cb2024-07-19 13:04:05 -0500414{
415 struct interrupt_queue *q;
416 uint16_t new_tail;
417
418 assert(vint_id != HF_INVALID_INTID);
419
420 q = &vcpu_locked.vcpu->interrupts.vint_q;
421
422 /*
423 * A new entry is pushed at the tail of the queue. Upon successful
424 * push operation, the tail increments or wraps around.
425 */
426 new_tail = queue_increment_index(q->tail);
427
J-Alves23a73032025-03-04 16:20:54 +0000428 if (is_queue_full(q)) {
Madhukar Pappireddy32913cb2024-07-19 13:04:05 -0500429 return false;
430 }
431
432 /* Add the virtual interrupt to the queue. */
433 q->vint_buffer[q->tail] = vint_id;
434 q->tail = new_tail;
435
J-Alves23a73032025-03-04 16:20:54 +0000436 assert(q->queued_vint_count < VINT_QUEUE_MAX);
437 q->queued_vint_count++;
438
Madhukar Pappireddy32913cb2024-07-19 13:04:05 -0500439 return true;
440}
441
442/**
443 * Remove an entry from the specified vCPU's queue at the head.
Madhukar Pappireddy32913cb2024-07-19 13:04:05 -0500444 * Returns true if successful in removing the entry, or false otherwise.
445 */
Daniel Boulby3c1506b2025-02-25 10:49:51 +0000446static bool vcpu_interrupt_queue_pop(struct vcpu_locked vcpu_locked,
447 uint32_t *vint_id)
Madhukar Pappireddy32913cb2024-07-19 13:04:05 -0500448{
449 struct interrupt_queue *q;
450 uint16_t new_head;
451
452 assert(vint_id != NULL);
453
454 q = &vcpu_locked.vcpu->interrupts.vint_q;
455
456 /* Check if queue is empty. */
457 if (is_queue_empty(q)) {
458 return false;
459 }
460
461 /*
462 * An entry is removed from the head of the queue. Once successful, the
463 * head is incremented or wrapped around if needed.
464 */
465 new_head = queue_increment_index(q->head);
466 *vint_id = q->vint_buffer[q->head];
467 q->head = new_head;
468
J-Alves23a73032025-03-04 16:20:54 +0000469 assert(q->queued_vint_count > 0);
470 q->queued_vint_count--;
471
Madhukar Pappireddy32913cb2024-07-19 13:04:05 -0500472 return true;
473}
474
475/**
476 * Look for the first pending virtual interrupt from the vcpu's queue. Note
477 * that the entry is not removed from the queue.
478 *
479 * Returns true if a valid entry exists in the queue, or false otherwise.
480 */
Daniel Boulby3c1506b2025-02-25 10:49:51 +0000481static bool vcpu_interrupt_queue_peek(struct vcpu_locked vcpu_locked,
482 uint32_t *vint_id)
Madhukar Pappireddy32913cb2024-07-19 13:04:05 -0500483{
484 struct interrupt_queue *q;
485 uint32_t queued_vint;
486
487 assert(vint_id != NULL);
488
489 q = &vcpu_locked.vcpu->interrupts.vint_q;
490
491 /* Check if queue is empty. */
492 if (is_queue_empty(q)) {
493 return false;
494 }
495
496 queued_vint = q->vint_buffer[q->head];
497 assert(queued_vint != HF_INVALID_INTID);
498
499 *vint_id = queued_vint;
500 return true;
501}
502
503/**
J-Alves3b31f092024-08-07 13:26:29 +0100504 * When interrupt handling is complete the preempted_vcpu field should go back
505 * to NULL.
506 */
507void vcpu_secure_interrupt_complete(struct vcpu_locked vcpu_locked)
508{
509 struct vcpu *vcpu;
510
511 vcpu = vcpu_locked.vcpu;
512 vcpu->preempted_vcpu = NULL;
J-Alves3b31f092024-08-07 13:26:29 +0100513}
Daniel Boulby3c1506b2025-02-25 10:49:51 +0000514
Daniel Boulbyd21e9b32025-02-13 15:53:21 +0000515void vcpu_virt_interrupt_enable(struct vcpu_locked vcpu_locked,
516 uint32_t vint_id, bool enable)
517{
518 struct interrupts *interrupts = &vcpu_locked.vcpu->interrupts;
519
520 if (enable) {
521 /*
522 * If it is pending and was not enabled before, increment the
523 * count.
524 */
525 if (vcpu_is_virt_interrupt_pending(interrupts, vint_id) &&
526 !vcpu_is_virt_interrupt_enabled(interrupts, vint_id)) {
527 vcpu_interrupt_count_increment(vcpu_locked, vint_id);
528 }
529 vcpu_virt_interrupt_set_enabled(interrupts, vint_id);
530 } else {
531 /*
532 * If it is pending and was enabled before, decrement the count.
533 */
534 if (vcpu_is_virt_interrupt_pending(interrupts, vint_id) &&
535 vcpu_is_virt_interrupt_enabled(interrupts, vint_id)) {
536 vcpu_interrupt_count_decrement(vcpu_locked, vint_id);
537 }
538 vcpu_virt_interrupt_clear_enabled(interrupts, vint_id);
539 }
540}
541
Daniel Boulby3c1506b2025-02-25 10:49:51 +0000542/*
543 * Find and return the first intid that is pending and enabled, the interrupt
544 * struct for this intid will be at the head of the list so can be popped.
545 * Intid returned in the vint_id argument.
546 * True returned if a pending and enabled interrupt is found. False otherwise.
547 */
548uint32_t vcpu_virt_interrupt_peek_pending_and_enabled(
549 struct vcpu_locked vcpu_locked)
550{
551 uint32_t vint_id = HF_INVALID_INTID;
552 struct interrupts *interrupts = &vcpu_locked.vcpu->interrupts;
553 uint32_t pending_and_enabled_count =
Daniel Boulbyd21e9b32025-02-13 15:53:21 +0000554 vcpu_virt_interrupt_count_get(vcpu_locked);
Daniel Boulby3c1506b2025-02-25 10:49:51 +0000555
556 /* First check there is a pending and enabled interrupt to return. */
557 if (pending_and_enabled_count == 0) {
558 return HF_INVALID_INTID;
559 }
560
561 /*
562 * We know here there is a pending and enabled interrupt in
563 * the queue. So push any interrupts that are not enabled to
564 * the back of the queue until we reach the first enabled one.
565 */
566 while (vcpu_interrupt_queue_peek(vcpu_locked, &vint_id) &&
567 !vcpu_is_virt_interrupt_enabled(interrupts, vint_id)) {
568 /* Push disabled interrupt to the back of the queue. */
569 vcpu_interrupt_queue_pop(vcpu_locked, &vint_id);
570 vcpu_interrupt_queue_push(vcpu_locked, vint_id);
571 }
572
573 assert(vint_id != HF_INVALID_INTID);
574
575 return vint_id;
576}
577
578/*
579 * Get the next pending and enabled virtual interrupt ID.
580 * Pops from the queue and clears the bitmap.
581 */
582uint32_t vcpu_virt_interrupt_get_pending_and_enabled(
583 struct vcpu_locked vcpu_locked)
584{
585 uint32_t vint_id =
586 vcpu_virt_interrupt_peek_pending_and_enabled(vcpu_locked);
587
588 if (vint_id != HF_INVALID_INTID) {
589 vcpu_interrupt_queue_pop(vcpu_locked, &vint_id);
590 vcpu_interrupt_clear_decrement(vcpu_locked, vint_id);
591 }
592
593 return vint_id;
594}
595
596/*
597 * Set a virtual interrupt to pending. Add it to the queue and set the bitmap.
598 */
599void vcpu_virt_interrupt_inject(struct vcpu_locked vcpu_locked,
600 uint32_t vint_id)
601{
602 struct interrupts *interrupts = &vcpu_locked.vcpu->interrupts;
603
604 /*
605 * An interrupt can only be pending once so return if it is
606 * already pending.
607 */
608 if (vcpu_is_virt_interrupt_pending(interrupts, vint_id)) {
609 return;
610 }
611
612 /* Push to the queue and set the bitmap. */
613 if (!vcpu_interrupt_queue_push(vcpu_locked, vint_id)) {
614 panic("Exhausted interrupt queue for vCPU %d of SP %#x\n",
615 vcpu_locked.vcpu->cpu->id, vcpu_locked.vcpu->vm->id);
616 }
617 vcpu_virt_interrupt_set_pending(interrupts, vint_id);
618
619 if (vcpu_is_virt_interrupt_enabled(interrupts, vint_id)) {
Daniel Boulbyd21e9b32025-02-13 15:53:21 +0000620 vcpu_interrupt_count_increment(vcpu_locked, vint_id);
Daniel Boulby3c1506b2025-02-25 10:49:51 +0000621 }
622}