1 // © 2021 Qualcomm Innovation Center, Inc. All rights reserved.
2 //
3 // SPDX-License-Identifier: BSD-3-Clause
4 
5 #include <assert.h>
6 #include <hyptypes.h>
7 
8 #include <hypcontainers.h>
9 
10 #include <atomic.h>
11 #include <compiler.h>
12 #include <panic.h>
13 #include <scheduler.h>
14 #include <trace.h>
15 #include <virq.h>
16 
17 #include "arm_vm_timer.h"
18 #include "event_handlers.h"
19 
20 static void
arm_vm_timer_inject_timer_virq(thread_t * thread,arm_vm_timer_type_t tt)21 arm_vm_timer_inject_timer_virq(thread_t *thread, arm_vm_timer_type_t tt)
22 {
23 	if (tt == ARM_VM_TIMER_TYPE_VIRTUAL) {
24 		(void)virq_assert(&thread->virtual_timer_virq_src, false);
25 	} else if (tt == ARM_VM_TIMER_TYPE_PHYSICAL) {
26 		(void)virq_assert(&thread->physical_timer_virq_src, false);
27 	} else {
28 		panic("Invalid timer");
29 	}
30 }
31 
32 // Handle the timer queue expiry coming from the hyp arch timer
33 static void
arm_vm_timer_type_timer_action(thread_t * thread,arm_vm_timer_type_t tt)34 arm_vm_timer_type_timer_action(thread_t *thread, arm_vm_timer_type_t tt)
35 {
36 	bool is_current = thread_get_self() == thread;
37 
38 	if (is_current && arm_vm_timer_is_irq_pending(tt)) {
39 		arm_vm_timer_inject_timer_virq(thread, tt);
40 	} else if (!is_current &&
41 		   arm_vm_timer_is_irq_enabled_thread(thread, tt)) {
42 		arm_vm_timer_inject_timer_virq(thread, tt);
43 	} else {
44 		TRACE(DEBUG, INFO, "Redundant VM hyp timeout");
45 	}
46 }
47 
48 bool
arm_vm_timer_handle_timer_action(timer_action_t action_type,timer_t * timer)49 arm_vm_timer_handle_timer_action(timer_action_t action_type, timer_t *timer)
50 {
51 	if (action_type == TIMER_ACTION_VIRTUAL_TIMER) {
52 		arm_vm_timer_type_timer_action(
53 			thread_container_of_virtual_timer(timer),
54 			ARM_VM_TIMER_TYPE_VIRTUAL);
55 	} else if (action_type == TIMER_ACTION_PHYSICAL_TIMER) {
56 		arm_vm_timer_type_timer_action(
57 			thread_container_of_physical_timer(timer),
58 			ARM_VM_TIMER_TYPE_PHYSICAL);
59 	} else {
60 		TRACE(DEBUG, INFO, "Spurious VM hyp timeout");
61 	}
62 
63 	return true;
64 }
65 
66 // Handle the VM arch timer expiry
67 static bool
arm_vm_timer_type_irq_received(thread_t * thread,arm_vm_timer_type_t tt)68 arm_vm_timer_type_irq_received(thread_t *thread, arm_vm_timer_type_t tt)
69 	REQUIRE_PREEMPT_DISABLED
70 {
71 	bool injected = false;
72 
73 	if (arm_vm_timer_is_irq_pending(tt)) {
74 		arm_vm_timer_inject_timer_virq(thread, tt);
75 		arm_vm_timer_arch_timer_hw_irq_activated(tt);
76 		injected = true;
77 	} else {
78 		TRACE(DEBUG, INFO, "Spurious VM timer IRQ");
79 	}
80 
81 	return injected;
82 }
83 
84 bool
arm_vm_timer_handle_irq_received(irq_t irq)85 arm_vm_timer_handle_irq_received(irq_t irq)
86 {
87 	bool	  injected = false;
88 	thread_t *thread   = thread_get_self();
89 
90 	if (irq == PLATFORM_VM_ARCH_VIRTUAL_TIMER_IRQ) {
91 		injected = arm_vm_timer_type_irq_received(
92 			thread, ARM_VM_TIMER_TYPE_VIRTUAL);
93 	} else if (irq == PLATFORM_VM_ARCH_PHYSICAL_TIMER_IRQ) {
94 		injected = arm_vm_timer_type_irq_received(
95 			thread, ARM_VM_TIMER_TYPE_PHYSICAL);
96 	} else {
97 		panic("Invalid VM timer IRQ");
98 	}
99 
100 	return !injected;
101 }
102 
103 static bool
arm_vm_timer_virq_check_pending(thread_t * thread,arm_vm_timer_type_t tt)104 arm_vm_timer_virq_check_pending(thread_t *thread, arm_vm_timer_type_t tt)
105 	REQUIRE_PREEMPT_DISABLED
106 {
107 	bool ret = true;
108 
109 	if (thread == thread_get_self()) {
110 		ret = arm_vm_timer_is_irq_pending(tt);
111 
112 		if (!ret) {
113 			arm_vm_timer_arch_timer_hw_irq_deactivate(tt);
114 		}
115 	}
116 
117 	return ret;
118 }
119 
120 bool
arm_vm_timer_handle_virq_check_pending(virq_trigger_t trigger,virq_source_t * source)121 arm_vm_timer_handle_virq_check_pending(virq_trigger_t trigger,
122 				       virq_source_t *source)
123 {
124 	bool ret = true;
125 
126 	if (trigger == VIRQ_TRIGGER_VIRTUAL_TIMER) {
127 		ret = arm_vm_timer_virq_check_pending(
128 			thread_container_of_virtual_timer_virq_src(source),
129 			ARM_VM_TIMER_TYPE_VIRTUAL);
130 
131 	} else if (trigger == VIRQ_TRIGGER_PHYSICAL_TIMER) {
132 		ret = arm_vm_timer_virq_check_pending(
133 			thread_container_of_physical_timer_virq_src(source),
134 			ARM_VM_TIMER_TYPE_PHYSICAL);
135 	} else {
136 		/* Do Nothing */
137 	}
138 
139 	return ret;
140 }
141