1 /*
2 * Copyright 2018 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/arch/timer.h"
10
11 #include <stdbool.h>
12 #include <stddef.h>
13 #include <stdint.h>
14
15 #include "hf/arch/cpu.h"
16 #include "hf/arch/vm/timer.h"
17
18 #include "hf/addr.h"
19
20 #include "msr.h"
21 #include "sysregs.h"
22
23 #define CNTV_CTL_EL0_ENABLE (1u << 0)
24 #define CNTV_CTL_EL0_IMASK (1u << 1)
25 #define CNTV_CTL_EL0_ISTATUS (1u << 2)
26
27 /**
28 * Sets the bit to mask virtual timer interrupts.
29 */
arch_timer_mask(struct arch_regs * regs)30 void arch_timer_mask(struct arch_regs *regs)
31 {
32 regs->peripherals.cntv_ctl_el0 |= CNTV_CTL_EL0_IMASK;
33 }
34
35 /**
36 * Checks whether the virtual timer is enabled and its interrupt not masked.
37 */
arch_timer_enabled(struct arch_regs * regs)38 bool arch_timer_enabled(struct arch_regs *regs)
39 {
40 uintreg_t cntv_ctl_el0 = regs->peripherals.cntv_ctl_el0;
41
42 return (cntv_ctl_el0 & CNTV_CTL_EL0_ENABLE) &&
43 !(cntv_ctl_el0 & CNTV_CTL_EL0_IMASK);
44 }
45
46 /**
47 * Converts a number of timer ticks to the equivalent number of nanoseconds.
48 */
ticks_to_ns(uint64_t ticks)49 static uint64_t ticks_to_ns(uint64_t ticks)
50 {
51 return (ticks * NANOS_PER_UNIT) / read_msr(cntfrq_el0);
52 }
53
54 /**
55 * Returns the number of ticks remaining on the virtual timer as stored in
56 * the given `arch_regs`, or 0 if it has already expired. This is undefined if
57 * the timer is not enabled.
58 */
arch_timer_remaining_ticks(struct arch_regs * regs)59 static uint64_t arch_timer_remaining_ticks(struct arch_regs *regs)
60 {
61 /*
62 * Calculate the value from the saved CompareValue (cntv_cval_el0) and
63 * the virtual count value.
64 */
65 uintreg_t cntv_cval_el0 = regs->peripherals.cntv_cval_el0;
66 uintreg_t cntvct_el0 = read_msr(cntvct_el0);
67
68 if (cntv_cval_el0 >= cntvct_el0) {
69 return cntv_cval_el0 - cntvct_el0;
70 }
71
72 return 0;
73 }
74
75 /**
76 * Returns the number of nanoseconds remaining on the virtual timer as stored in
77 * the given `arch_regs`, or 0 if it has already expired. This is undefined if
78 * the timer is not enabled.
79 */
arch_timer_remaining_ns(struct arch_regs * regs)80 uint64_t arch_timer_remaining_ns(struct arch_regs *regs)
81 {
82 return ticks_to_ns(arch_timer_remaining_ticks(regs));
83 }
84
85 /**
86 * Returns whether the timer is ready to fire: i.e. it is enabled, not masked,
87 * and the condition is met.
88 */
arch_timer_pending(struct arch_regs * regs)89 bool arch_timer_pending(struct arch_regs *regs)
90 {
91 if (!arch_timer_enabled(regs)) {
92 return false;
93 }
94
95 if (regs->peripherals.cntv_ctl_el0 & CNTV_CTL_EL0_ISTATUS) {
96 return true;
97 }
98
99 if (arch_timer_remaining_ticks(regs) == 0) {
100 /*
101 * This can happen even if the (stored) ISTATUS bit is not set,
102 * because time has passed between when the registers were
103 * stored and now.
104 */
105 return true;
106 }
107
108 return false;
109 }
110
111 /**
112 * Checks whether the virtual timer is enabled and its interrupt not masked, for
113 * the currently active vCPU.
114 */
arch_timer_enabled_current(void)115 bool arch_timer_enabled_current(void)
116 {
117 uintreg_t cntv_ctl_el0 = has_vhe_support() ? read_msr(MSR_CNTV_CTL_EL02)
118 : read_msr(cntv_ctl_el0);
119
120 return (cntv_ctl_el0 & CNTV_CTL_EL0_ENABLE) &&
121 !(cntv_ctl_el0 & CNTV_CTL_EL0_IMASK);
122 }
123
124 /**
125 * Disables the virtual timer for the currently active vCPU.
126 */
arch_timer_disable_current(void)127 void arch_timer_disable_current(void)
128 {
129 has_vhe_support() ? write_msr(MSR_CNTV_CTL_EL02, 0x0)
130 : write_msr(cntv_ctl_el0, 0x0);
131 }
132
133 /**
134 * Returns the number of ticks remaining on the virtual timer of the currently
135 * active vCPU, or 0 if it has already expired. This is undefined if the timer
136 * is not enabled.
137 */
arch_timer_remaining_ticks_current(void)138 static uint64_t arch_timer_remaining_ticks_current(void)
139 {
140 uintreg_t cntv_cval_el0 = has_vhe_support()
141 ? read_msr(MSR_CNTV_CVAL_EL02)
142 : read_msr(cntv_cval_el0);
143 uintreg_t cntvct_el0 = read_msr(cntvct_el0);
144
145 if (cntv_cval_el0 >= cntvct_el0) {
146 return cntv_cval_el0 - cntvct_el0;
147 }
148
149 return 0;
150 }
151
152 /**
153 * Returns the number of nanoseconds remaining on the virtual timer of the
154 * currently active vCPU, or 0 if it has already expired. This is undefined if
155 * the timer is not enabled.
156 */
arch_timer_remaining_ns_current(void)157 uint64_t arch_timer_remaining_ns_current(void)
158 {
159 return ticks_to_ns(arch_timer_remaining_ticks_current());
160 }
161