feat(arch timer): handle host timer interrupt tracking live deadline
When the timer deadline set by currently running vCPU expires, it
triggers the host(S-EL2 Physical) timer interrupt. SPMC then injects the
timer virtual interrupt to the current vCPU and either signals or
queues the timer virtual interrupt similar to any other secure virtual
interrupt.
SPMC also disables the host timer and deactivates the corresponding PPI
interrupt.
Change-Id: Ibc06e5a17fae060f8efd754e3866b08d64a3c28c
Signed-off-by: Madhukar Pappireddy <madhukar.pappireddy@arm.com>
diff --git a/src/timer_mgmt.c b/src/timer_mgmt.c
index 604ccb1..43d1c30 100644
--- a/src/timer_mgmt.c
+++ b/src/timer_mgmt.c
@@ -61,3 +61,78 @@
timer_list_remove_vcpu(vcpu->cpu, vcpu);
}
}
+
+/**
+ * A vCPU's timer entry is the last entry in the list if it's `next` field
+ * points to `root_entry` of the list.
+ */
+static inline bool timer_is_list_end(struct vcpu *vcpu,
+ struct timer_pending_vcpu_list *timer_list)
+{
+ return (vcpu->timer_node.next == &timer_list->root_entry);
+}
+
+/**
+ * Find the vCPU with the nearest timer deadline, being tracked by partition
+ * manager, on current CPU.
+ */
+struct vcpu *timer_find_vcpu_nearest_deadline(struct cpu *cpu)
+{
+ struct vcpu *vcpu_with_deadline = NULL;
+ struct vcpu *it_vcpu = NULL;
+ struct timer_pending_vcpu_list *timer_list;
+ uint64_t near_deadline = UINT64_MAX;
+ struct list_entry *next_timer_entry;
+
+ assert(cpu != NULL);
+
+ timer_list = &cpu->pending_timer_vcpus_list;
+ sl_lock(&cpu->lock);
+
+ if (list_empty(&timer_list->root_entry)) {
+ goto out;
+ }
+
+ next_timer_entry = timer_list->root_entry.next;
+
+ /* Iterate to find the vCPU with nearest deadline. */
+ do {
+ uint64_t expiry_ns;
+
+ /* vCPU iterator. */
+ it_vcpu =
+ CONTAINER_OF(next_timer_entry, struct vcpu, timer_node);
+ assert(arch_timer_enabled(&it_vcpu->regs));
+
+ expiry_ns = arch_timer_remaining_ns(&it_vcpu->regs);
+
+ if (expiry_ns < near_deadline) {
+ near_deadline = expiry_ns;
+ vcpu_with_deadline = it_vcpu;
+ }
+
+ /* Look at the next entry in the list. */
+ next_timer_entry = it_vcpu->timer_node.next;
+ } while (!timer_is_list_end(it_vcpu, timer_list));
+
+out:
+ sl_unlock(&cpu->lock);
+ return vcpu_with_deadline;
+}
+
+/**
+ * Find the vCPU whose timer deadline has expired and needs to be resumed at
+ * the earliest.
+ */
+struct vcpu *timer_find_target_vcpu(struct vcpu *current)
+{
+ struct vcpu *target_vcpu;
+
+ if (current->vm->id == HF_OTHER_WORLD_ID) {
+ target_vcpu = timer_find_vcpu_nearest_deadline(current->cpu);
+ } else {
+ target_vcpu = current;
+ }
+
+ return target_vcpu;
+}