Sandrine Bailleux | 3cd87d7 | 2018-10-09 11:12:55 +0200 | [diff] [blame] | 1 | /* |
Boyan Karatotev | 4e28242 | 2024-10-25 14:34:13 +0100 | [diff] [blame] | 2 | * Copyright (c) 2018-2024, Arm Limited. All rights reserved. |
Sandrine Bailleux | 3cd87d7 | 2018-10-09 11:12:55 +0200 | [diff] [blame] | 3 | * |
| 4 | * SPDX-License-Identifier: BSD-3-Clause |
| 5 | */ |
| 6 | |
| 7 | #include <amu.h> |
| 8 | #include <arch.h> |
Boyan Karatotev | 4e28242 | 2024-10-25 14:34:13 +0100 | [diff] [blame] | 9 | #include <arch_features.h> |
Sandrine Bailleux | 3cd87d7 | 2018-10-09 11:12:55 +0200 | [diff] [blame] | 10 | #include <arch_helpers.h> |
| 11 | #include <assert.h> |
| 12 | #include <debug.h> |
| 13 | #include <irq.h> |
| 14 | #include <plat_topology.h> |
| 15 | #include <platform.h> |
| 16 | #include <power_management.h> |
| 17 | #include <tftf_lib.h> |
| 18 | #include <timer.h> |
| 19 | |
Juan Pablo Conde | c3cf2da | 2024-04-01 13:57:19 -0500 | [diff] [blame] | 20 | #define SUSPEND_TIME_1_SEC 1000 |
| 21 | #define MAX_MPMM_TEST_ITERATIONS 100000U |
Sandrine Bailleux | 3cd87d7 | 2018-10-09 11:12:55 +0200 | [diff] [blame] | 22 | |
| 23 | static volatile int wakeup_irq_received[PLATFORM_CORE_COUNT]; |
| 24 | |
| 25 | /* Dummy timer handler that sets a flag to check it has been called. */ |
| 26 | static int suspend_wakeup_handler(void *data) |
| 27 | { |
| 28 | u_register_t core_mpid = read_mpidr_el1() & MPID_MASK; |
| 29 | unsigned int core_pos = platform_get_core_pos(core_mpid); |
| 30 | |
| 31 | assert(wakeup_irq_received[core_pos] == 0); |
| 32 | |
| 33 | wakeup_irq_received[core_pos] = 1; |
| 34 | |
| 35 | return 0; |
| 36 | } |
| 37 | |
| 38 | /* |
| 39 | * Helper function to suspend a CPU to power level 0 and wake it up with |
| 40 | * a timer. |
| 41 | */ |
| 42 | static test_result_t suspend_and_resume_this_cpu(void) |
| 43 | { |
| 44 | uint32_t stateid; |
| 45 | int psci_ret; |
| 46 | test_result_t result = TEST_RESULT_SUCCESS; |
| 47 | |
| 48 | u_register_t core_mpid = read_mpidr_el1() & MPID_MASK; |
| 49 | unsigned int core_pos = platform_get_core_pos(core_mpid); |
| 50 | |
| 51 | /* Prepare wakeup timer. IRQs need to be enabled. */ |
| 52 | wakeup_irq_received[core_pos] = 0; |
| 53 | |
| 54 | tftf_timer_register_handler(suspend_wakeup_handler); |
| 55 | |
| 56 | /* Program timer to fire interrupt after timer expires */ |
| 57 | tftf_program_timer(SUSPEND_TIME_1_SEC); |
| 58 | |
| 59 | /* |
| 60 | * Suspend the calling CPU to power level 0 and power |
| 61 | * state. |
| 62 | */ |
| 63 | psci_ret = tftf_psci_make_composite_state_id(PSTATE_AFF_LVL_0, |
| 64 | PSTATE_TYPE_POWERDOWN, |
| 65 | &stateid); |
| 66 | if (psci_ret != PSCI_E_SUCCESS) { |
| 67 | mp_printf("Failed to make composite state ID @ CPU %d. rc = %x\n", |
| 68 | core_pos, psci_ret); |
| 69 | result = TEST_RESULT_FAIL; |
| 70 | } else { |
| 71 | unsigned int power_state = tftf_make_psci_pstate(PSTATE_AFF_LVL_0, |
| 72 | PSTATE_TYPE_POWERDOWN, stateid); |
| 73 | psci_ret = tftf_cpu_suspend(power_state); |
| 74 | |
| 75 | if (!wakeup_irq_received[core_pos]) { |
| 76 | mp_printf("Didn't receive wakeup IRQ in CPU %d.\n", |
| 77 | core_pos); |
| 78 | result = TEST_RESULT_FAIL; |
| 79 | } |
| 80 | |
| 81 | if (psci_ret != PSCI_E_SUCCESS) { |
| 82 | mp_printf("Failed to suspend from CPU %d. ret: %x\n", |
| 83 | core_pos, psci_ret); |
| 84 | result = TEST_RESULT_FAIL; |
| 85 | } |
| 86 | } |
| 87 | |
| 88 | /* Wake up. Remove timer after waking up.*/ |
| 89 | tftf_cancel_timer(); |
| 90 | tftf_timer_unregister_handler(); |
| 91 | |
| 92 | return result; |
| 93 | } |
| 94 | |
| 95 | /* |
John Tsichritzis | cae91ca | 2019-05-12 16:06:09 +0100 | [diff] [blame] | 96 | * Helper function that checks whether the value of a group0 counter is valid |
| 97 | * or not. The first 3 counters (0,1,2) cannot have values of zero but the last |
| 98 | * counter that counts "memory stall cycles" can have a value of zero, under |
| 99 | * certain circumstances. |
| 100 | * |
| 101 | * Return values: |
| 102 | * 0 = valid counter value |
| 103 | * -1 = invalid counter value |
Sandrine Bailleux | 3cd87d7 | 2018-10-09 11:12:55 +0200 | [diff] [blame] | 104 | */ |
John Tsichritzis | cae91ca | 2019-05-12 16:06:09 +0100 | [diff] [blame] | 105 | static int amu_group0_cnt_valid(unsigned int idx, uint64_t value) |
| 106 | { |
| 107 | int answer = 0; |
| 108 | |
johpow01 | b7d752a | 2020-10-08 17:29:11 -0500 | [diff] [blame] | 109 | if ((idx <= 2) && (value == 0)) { |
John Tsichritzis | cae91ca | 2019-05-12 16:06:09 +0100 | [diff] [blame] | 110 | answer = -1; |
johpow01 | b7d752a | 2020-10-08 17:29:11 -0500 | [diff] [blame] | 111 | } |
John Tsichritzis | cae91ca | 2019-05-12 16:06:09 +0100 | [diff] [blame] | 112 | |
| 113 | return answer; |
| 114 | } |
| 115 | |
| 116 | /* |
| 117 | * Check that group0 counters are valid. As EL3 has enabled the counters before |
| 118 | * the first entry to NS world, the counters should have increased by the time |
| 119 | * we reach this test case. |
| 120 | */ |
| 121 | test_result_t test_amu_valid_ctr(void) |
Sandrine Bailleux | 3cd87d7 | 2018-10-09 11:12:55 +0200 | [diff] [blame] | 122 | { |
johpow01 | b7d752a | 2020-10-08 17:29:11 -0500 | [diff] [blame] | 123 | unsigned int i; |
Sandrine Bailleux | 3cd87d7 | 2018-10-09 11:12:55 +0200 | [diff] [blame] | 124 | |
johpow01 | b7d752a | 2020-10-08 17:29:11 -0500 | [diff] [blame] | 125 | if (amu_get_version() == 0U) { |
Sandrine Bailleux | 3cd87d7 | 2018-10-09 11:12:55 +0200 | [diff] [blame] | 126 | return TEST_RESULT_SKIPPED; |
johpow01 | b7d752a | 2020-10-08 17:29:11 -0500 | [diff] [blame] | 127 | } |
Sandrine Bailleux | 3cd87d7 | 2018-10-09 11:12:55 +0200 | [diff] [blame] | 128 | |
| 129 | /* If counters are not enabled, then skip the test */ |
johpow01 | b7d752a | 2020-10-08 17:29:11 -0500 | [diff] [blame] | 130 | if (read_amcntenset0_el0() != AMU_GROUP0_COUNTERS_MASK) { |
Sandrine Bailleux | 3cd87d7 | 2018-10-09 11:12:55 +0200 | [diff] [blame] | 131 | return TEST_RESULT_SKIPPED; |
johpow01 | b7d752a | 2020-10-08 17:29:11 -0500 | [diff] [blame] | 132 | } |
Sandrine Bailleux | 3cd87d7 | 2018-10-09 11:12:55 +0200 | [diff] [blame] | 133 | |
johpow01 | b7d752a | 2020-10-08 17:29:11 -0500 | [diff] [blame] | 134 | for (i = 0U; i < AMU_GROUP0_NR_COUNTERS; i++) { |
John Tsichritzis | cae91ca | 2019-05-12 16:06:09 +0100 | [diff] [blame] | 135 | uint64_t value; |
Sandrine Bailleux | 3cd87d7 | 2018-10-09 11:12:55 +0200 | [diff] [blame] | 136 | |
John Tsichritzis | cae91ca | 2019-05-12 16:06:09 +0100 | [diff] [blame] | 137 | value = amu_group0_cnt_read(i); |
| 138 | if (amu_group0_cnt_valid(i, value)) { |
| 139 | tftf_testcase_printf("Group0 counter %d has invalid value %lld\n", i, value); |
Sandrine Bailleux | 3cd87d7 | 2018-10-09 11:12:55 +0200 | [diff] [blame] | 140 | return TEST_RESULT_FAIL; |
| 141 | } |
| 142 | } |
| 143 | |
Sandrine Bailleux | 3cd87d7 | 2018-10-09 11:12:55 +0200 | [diff] [blame] | 144 | return TEST_RESULT_SUCCESS; |
| 145 | } |
| 146 | |
| 147 | /* |
| 148 | * Check that the counters are non-decreasing during |
| 149 | * a suspend/resume cycle. |
| 150 | */ |
| 151 | test_result_t test_amu_suspend_resume(void) |
| 152 | { |
johpow01 | b7d752a | 2020-10-08 17:29:11 -0500 | [diff] [blame] | 153 | uint64_t group0_ctrs[AMU_GROUP0_NR_COUNTERS]; |
| 154 | unsigned int i; |
Sandrine Bailleux | 3cd87d7 | 2018-10-09 11:12:55 +0200 | [diff] [blame] | 155 | |
johpow01 | b7d752a | 2020-10-08 17:29:11 -0500 | [diff] [blame] | 156 | if (amu_get_version() == 0U) { |
Sandrine Bailleux | 3cd87d7 | 2018-10-09 11:12:55 +0200 | [diff] [blame] | 157 | return TEST_RESULT_SKIPPED; |
johpow01 | b7d752a | 2020-10-08 17:29:11 -0500 | [diff] [blame] | 158 | } |
Sandrine Bailleux | 3cd87d7 | 2018-10-09 11:12:55 +0200 | [diff] [blame] | 159 | |
| 160 | /* If counters are not enabled, then skip the test */ |
John Tsichritzis | 68759a8 | 2019-05-12 15:10:27 +0100 | [diff] [blame] | 161 | if (read_amcntenset0_el0() != AMU_GROUP0_COUNTERS_MASK) |
Sandrine Bailleux | 3cd87d7 | 2018-10-09 11:12:55 +0200 | [diff] [blame] | 162 | return TEST_RESULT_SKIPPED; |
| 163 | |
| 164 | /* Save counters values before suspend */ |
johpow01 | b7d752a | 2020-10-08 17:29:11 -0500 | [diff] [blame] | 165 | for (i = 0U; i < AMU_GROUP0_NR_COUNTERS; i++) { |
Sandrine Bailleux | 3cd87d7 | 2018-10-09 11:12:55 +0200 | [diff] [blame] | 166 | group0_ctrs[i] = amu_group0_cnt_read(i); |
johpow01 | b7d752a | 2020-10-08 17:29:11 -0500 | [diff] [blame] | 167 | } |
| 168 | |
| 169 | /* |
| 170 | * If FEAT_AMUv1p1 supported then make sure the save/restore works for |
| 171 | * virtual counter values. Write known values into the virtual offsets |
| 172 | * and then make sure they are still there after resume. The virtual |
| 173 | * offset registers are only accessible in AARCH64 mode in EL2 or EL3. |
| 174 | */ |
| 175 | #if __aarch64__ |
| 176 | if (amu_get_version() >= ID_AA64PFR0_AMU_V1P1) { |
| 177 | /* Enabling voffsets in HCR_EL2. */ |
| 178 | write_hcr_el2(read_hcr_el2() | HCR_AMVOFFEN_BIT); |
| 179 | |
| 180 | /* Writing known values into voffset registers. */ |
| 181 | amu_group0_voffset_write(0U, 0xDEADBEEF); |
| 182 | amu_group0_voffset_write(2U, 0xDEADBEEF); |
| 183 | amu_group0_voffset_write(3U, 0xDEADBEEF); |
| 184 | |
| 185 | #if AMU_GROUP1_NR_COUNTERS |
| 186 | u_register_t amcg1idr = read_amcg1idr_el0() >> 16; |
| 187 | |
| 188 | for (i = 0U; i < AMU_GROUP1_NR_COUNTERS; i++) { |
| 189 | if (((amcg1idr >> i) & 1U) != 0U) { |
| 190 | amu_group1_voffset_write(i, 0xDEADBEEF); |
| 191 | } |
| 192 | } |
| 193 | #endif |
| 194 | } |
| 195 | #endif |
Sandrine Bailleux | 3cd87d7 | 2018-10-09 11:12:55 +0200 | [diff] [blame] | 196 | |
Sandrine Bailleux | 3cd87d7 | 2018-10-09 11:12:55 +0200 | [diff] [blame] | 197 | /* Suspend/resume current core */ |
| 198 | suspend_and_resume_this_cpu(); |
| 199 | |
| 200 | /* |
| 201 | * Check if counter values are >= than the stored values. |
| 202 | * If they are not, the AMU context save/restore in EL3 is buggy. |
| 203 | */ |
| 204 | for (i = 0; i < AMU_GROUP0_NR_COUNTERS; i++) { |
John Tsichritzis | cae91ca | 2019-05-12 16:06:09 +0100 | [diff] [blame] | 205 | uint64_t value; |
Sandrine Bailleux | 3cd87d7 | 2018-10-09 11:12:55 +0200 | [diff] [blame] | 206 | |
John Tsichritzis | cae91ca | 2019-05-12 16:06:09 +0100 | [diff] [blame] | 207 | value = amu_group0_cnt_read(i); |
| 208 | if (value < group0_ctrs[i]) { |
Sandrine Bailleux | 3cd87d7 | 2018-10-09 11:12:55 +0200 | [diff] [blame] | 209 | tftf_testcase_printf("Invalid counter value: before: %llx, after: %llx\n", |
| 210 | (unsigned long long)group0_ctrs[i], |
John Tsichritzis | cae91ca | 2019-05-12 16:06:09 +0100 | [diff] [blame] | 211 | (unsigned long long)value); |
Sandrine Bailleux | 3cd87d7 | 2018-10-09 11:12:55 +0200 | [diff] [blame] | 212 | return TEST_RESULT_FAIL; |
| 213 | } |
| 214 | } |
| 215 | |
johpow01 | b7d752a | 2020-10-08 17:29:11 -0500 | [diff] [blame] | 216 | #if __aarch64__ |
| 217 | if (amu_get_version() >= ID_AA64PFR0_AMU_V1P1) { |
| 218 | for (i = 0U; i < AMU_GROUP0_NR_COUNTERS; i++) { |
| 219 | if ((i != 1U) && |
| 220 | (amu_group0_voffset_read(i) != 0xDEADBEEF)) { |
| 221 | tftf_testcase_printf( |
| 222 | "Invalid G0 voffset %u: 0x%llx\n", i, |
| 223 | amu_group0_voffset_read(i)); |
| 224 | return TEST_RESULT_FAIL; |
| 225 | } |
| 226 | } |
| 227 | |
| 228 | #if AMU_GROUP1_NR_COUNTERS |
| 229 | u_register_t amcg1idr = read_amcg1idr_el0() >> 16; |
| 230 | |
| 231 | for (i = 0U; i < AMU_GROUP1_NR_COUNTERS; i++) { |
| 232 | if (((amcg1idr >> i) & 1U) != 0U) { |
| 233 | if (amu_group1_voffset_read(i) != 0xDEADBEEF) { |
| 234 | tftf_testcase_printf("Invalid G1 " \ |
| 235 | "voffset %u: 0x%llx\n", i, |
| 236 | amu_group1_voffset_read(i)); |
| 237 | return TEST_RESULT_FAIL; |
| 238 | } |
| 239 | } |
| 240 | } |
| 241 | #endif |
| 242 | } |
| 243 | #endif |
| 244 | |
Sandrine Bailleux | 3cd87d7 | 2018-10-09 11:12:55 +0200 | [diff] [blame] | 245 | return TEST_RESULT_SUCCESS; |
| 246 | } |
Juan Pablo Conde | c3cf2da | 2024-04-01 13:57:19 -0500 | [diff] [blame] | 247 | |
| 248 | /* |
| 249 | * Check that group 1 counters read as 0 at ELs lower than EL3 when |
| 250 | * AMCR.CG1RZ is set. |
| 251 | */ |
| 252 | test_result_t test_amu_group1_raz(void) |
| 253 | { |
| 254 | /* Test on TC2 only, as MPMM not implemented in other platforms yet */ |
| 255 | #if PLAT_tc && (TARGET_PLATFORM == 2) |
| 256 | uint64_t counters_initial[AMU_GROUP1_NR_COUNTERS] = {0}; |
| 257 | uint64_t counters_final[AMU_GROUP1_NR_COUNTERS] = {0}; |
| 258 | |
| 259 | for (unsigned int i = 0; i < amu_group1_num_counters(); i++) { |
| 260 | INFO("AMUEVTYPER1%x: 0x%llx\n", i, amu_group1_evtype_read(i)); |
| 261 | counters_initial[i] = amu_group1_cnt_read(i); |
| 262 | } |
| 263 | |
| 264 | for (int i = 0; i < MAX_MPMM_TEST_ITERATIONS; i++) { |
| 265 | // Instruction with activity count 1 |
| 266 | __asm__ volatile("fmov d0,xzr"); |
| 267 | __asm__ volatile("fmov d1,xzr"); |
| 268 | __asm__ volatile("fmul d2,d0,d1"); |
| 269 | __asm__ volatile("fmov d2,xzr"); |
| 270 | |
| 271 | __asm__ volatile("fmov d0,xzr"); |
| 272 | __asm__ volatile("fmov d1,xzr"); |
| 273 | __asm__ volatile("fmov d2,xzr"); |
| 274 | __asm__ volatile("fmadd d3,d2,d1,d0"); |
| 275 | |
| 276 | // Instruction with activity count 2 |
| 277 | __asm__ volatile("ptrue p0.s, ALL"); |
| 278 | __asm__ volatile("index z10.s, #10,13"); |
| 279 | __asm__ volatile("index z11.s, #12,7"); |
| 280 | __asm__ volatile("ucvtf v10.4s, v10.4s"); |
| 281 | __asm__ volatile("ucvtf v11.4s, v11.4s"); |
| 282 | __asm__ volatile("fadd v0.4s, v10.4s, v11.4s"); |
| 283 | } |
| 284 | |
| 285 | for (unsigned int i = 0; i < amu_group1_num_counters(); i++) { |
| 286 | counters_final[i] = amu_group1_cnt_read(i); |
| 287 | if (counters_final[i] == counters_initial[i]) { |
| 288 | return TEST_RESULT_FAIL; |
| 289 | } |
| 290 | } |
| 291 | |
| 292 | return TEST_RESULT_SUCCESS; |
| 293 | #else |
| 294 | return TEST_RESULT_SKIPPED; |
| 295 | #endif /* PLAT_tc && (TARGET_PLATFORM == 2) */ |
| 296 | } |