1 /* 2 * Copyright (c) 2017-2021, ARM Limited and Contributors. All rights reserved. 3 * 4 * SPDX-License-Identifier: BSD-3-Clause 5 */ 6 7 #include <assert.h> 8 #include <cdefs.h> 9 #include <stdbool.h> 10 11 #include <arch.h> 12 #include <arch_helpers.h> 13 14 #include <lib/el3_runtime/pubsub_events.h> 15 #include <lib/extensions/amu.h> 16 #include <lib/extensions/amu_private.h> 17 18 #include <plat/common/platform.h> 19 20 static struct amu_ctx amu_ctxs[PLATFORM_CORE_COUNT]; 21 22 static inline __unused uint32_t read_id_pfr0_amu(void) 23 { 24 return (read_id_pfr0() >> ID_PFR0_AMU_SHIFT) & 25 ID_PFR0_AMU_MASK; 26 } 27 28 static inline __unused void write_hcptr_tam(uint32_t value) 29 { 30 write_hcptr((read_hcptr() & ~TAM_BIT) | 31 ((value << TAM_SHIFT) & TAM_BIT)); 32 } 33 34 static inline __unused void write_amcr_cg1rz(uint32_t value) 35 { 36 write_amcr((read_amcr() & ~AMCR_CG1RZ_BIT) | 37 ((value << AMCR_CG1RZ_SHIFT) & AMCR_CG1RZ_BIT)); 38 } 39 40 static inline __unused uint32_t read_amcfgr_ncg(void) 41 { 42 return (read_amcfgr() >> AMCFGR_NCG_SHIFT) & 43 AMCFGR_NCG_MASK; 44 } 45 46 static inline __unused uint32_t read_amcgcr_cg0nc(void) 47 { 48 return (read_amcgcr() >> AMCGCR_CG0NC_SHIFT) & 49 AMCGCR_CG0NC_MASK; 50 } 51 52 static inline __unused uint32_t read_amcgcr_cg1nc(void) 53 { 54 return (read_amcgcr() >> AMCGCR_CG1NC_SHIFT) & 55 AMCGCR_CG1NC_MASK; 56 } 57 58 static inline __unused uint32_t read_amcntenset0_px(void) 59 { 60 return (read_amcntenset0() >> AMCNTENSET0_Pn_SHIFT) & 61 AMCNTENSET0_Pn_MASK; 62 } 63 64 static inline __unused uint32_t read_amcntenset1_px(void) 65 { 66 return (read_amcntenset1() >> AMCNTENSET1_Pn_SHIFT) & 67 AMCNTENSET1_Pn_MASK; 68 } 69 70 static inline __unused void write_amcntenset0_px(uint32_t px) 71 { 72 uint32_t value = read_amcntenset0(); 73 74 value &= ~AMCNTENSET0_Pn_MASK; 75 value |= (px << AMCNTENSET0_Pn_SHIFT) & 76 AMCNTENSET0_Pn_MASK; 77 78 write_amcntenset0(value); 79 } 80 81 static inline __unused void write_amcntenset1_px(uint32_t px) 82 { 83 uint32_t value = read_amcntenset1(); 84 85 value &= ~AMCNTENSET1_Pn_MASK; 86 value |= (px << AMCNTENSET1_Pn_SHIFT) & 87 AMCNTENSET1_Pn_MASK; 88 89 write_amcntenset1(value); 90 } 91 92 static inline __unused void write_amcntenclr0_px(uint32_t px) 93 { 94 uint32_t value = read_amcntenclr0(); 95 96 value &= ~AMCNTENCLR0_Pn_MASK; 97 value |= (px << AMCNTENCLR0_Pn_SHIFT) & AMCNTENCLR0_Pn_MASK; 98 99 write_amcntenclr0(value); 100 } 101 102 static inline __unused void write_amcntenclr1_px(uint32_t px) 103 { 104 uint32_t value = read_amcntenclr1(); 105 106 value &= ~AMCNTENCLR1_Pn_MASK; 107 value |= (px << AMCNTENCLR1_Pn_SHIFT) & AMCNTENCLR1_Pn_MASK; 108 109 write_amcntenclr1(value); 110 } 111 112 static bool amu_supported(void) 113 { 114 return read_id_pfr0_amu() >= ID_PFR0_AMU_V1; 115 } 116 117 static bool amu_v1p1_supported(void) 118 { 119 return read_id_pfr0_amu() >= ID_PFR0_AMU_V1P1; 120 } 121 122 #if ENABLE_AMU_AUXILIARY_COUNTERS 123 static bool amu_group1_supported(void) 124 { 125 return read_amcfgr_ncg() > 0U; 126 } 127 #endif 128 129 /* 130 * Enable counters. This function is meant to be invoked 131 * by the context management library before exiting from EL3. 132 */ 133 void amu_enable(bool el2_unused) 134 { 135 if (!amu_supported()) { 136 return; 137 } 138 139 #if ENABLE_AMU_AUXILIARY_COUNTERS 140 if (AMU_GROUP1_NR_COUNTERS > 0U) { 141 /* Check and set presence of group 1 counters */ 142 if (!amu_group1_supported()) { 143 ERROR("AMU Counter Group 1 is not implemented\n"); 144 panic(); 145 } 146 147 /* Check number of group 1 counters */ 148 uint32_t cnt_num = read_amcgcr_cg1nc(); 149 150 VERBOSE("%s%u. %s%u\n", 151 "Number of AMU Group 1 Counters ", cnt_num, 152 "Requested number ", AMU_GROUP1_NR_COUNTERS); 153 154 if (cnt_num < AMU_GROUP1_NR_COUNTERS) { 155 ERROR("%s%u is less than %s%u\n", 156 "Number of AMU Group 1 Counters ", cnt_num, 157 "Requested number ", AMU_GROUP1_NR_COUNTERS); 158 panic(); 159 } 160 } 161 #endif 162 163 if (el2_unused) { 164 /* 165 * Non-secure access from EL0 or EL1 to the Activity Monitor 166 * registers do not trap to EL2. 167 */ 168 write_hcptr_tam(0U); 169 } 170 171 /* Enable group 0 counters */ 172 write_amcntenset0_px((UINT32_C(1) << read_amcgcr_cg0nc()) - 1U); 173 174 #if ENABLE_AMU_AUXILIARY_COUNTERS 175 if (AMU_GROUP1_NR_COUNTERS > 0U) { 176 /* Enable group 1 counters */ 177 write_amcntenset1_px(AMU_GROUP1_COUNTERS_MASK); 178 } 179 #endif 180 181 /* Initialize FEAT_AMUv1p1 features if present. */ 182 if (!amu_v1p1_supported()) { 183 return; 184 } 185 186 #if AMU_RESTRICT_COUNTERS 187 /* 188 * FEAT_AMUv1p1 adds a register field to restrict access to group 1 189 * counters at all but the highest implemented EL. This is controlled 190 * with the AMU_RESTRICT_COUNTERS compile time flag, when set, system 191 * register reads at lower ELs return zero. Reads from the memory 192 * mapped view are unaffected. 193 */ 194 VERBOSE("AMU group 1 counter access restricted.\n"); 195 write_amcr_cg1rz(1U); 196 #else 197 write_amcr_cg1rz(0U); 198 #endif 199 } 200 201 /* Read the group 0 counter identified by the given `idx`. */ 202 static uint64_t amu_group0_cnt_read(unsigned int idx) 203 { 204 assert(amu_supported()); 205 assert(idx < read_amcgcr_cg0nc()); 206 207 return amu_group0_cnt_read_internal(idx); 208 } 209 210 /* Write the group 0 counter identified by the given `idx` with `val` */ 211 static void amu_group0_cnt_write(unsigned int idx, uint64_t val) 212 { 213 assert(amu_supported()); 214 assert(idx < read_amcgcr_cg0nc()); 215 216 amu_group0_cnt_write_internal(idx, val); 217 isb(); 218 } 219 220 #if ENABLE_AMU_AUXILIARY_COUNTERS 221 /* Read the group 1 counter identified by the given `idx` */ 222 static uint64_t amu_group1_cnt_read(unsigned int idx) 223 { 224 assert(amu_supported()); 225 assert(amu_group1_supported()); 226 assert(idx < AMU_GROUP1_NR_COUNTERS); 227 228 return amu_group1_cnt_read_internal(idx); 229 } 230 231 /* Write the group 1 counter identified by the given `idx` with `val` */ 232 static void amu_group1_cnt_write(unsigned int idx, uint64_t val) 233 { 234 assert(amu_supported()); 235 assert(amu_group1_supported()); 236 assert(idx < AMU_GROUP1_NR_COUNTERS); 237 238 amu_group1_cnt_write_internal(idx, val); 239 isb(); 240 } 241 #endif 242 243 static void *amu_context_save(const void *arg) 244 { 245 struct amu_ctx *ctx = &amu_ctxs[plat_my_core_pos()]; 246 unsigned int i; 247 248 if (!amu_supported()) { 249 return (void *)-1; 250 } 251 252 #if ENABLE_AMU_AUXILIARY_COUNTERS 253 if (AMU_GROUP1_NR_COUNTERS > 0U) { 254 if (!amu_group1_supported()) { 255 return (void *)-1; 256 } 257 } 258 #endif 259 260 /* Assert that group 0/1 counter configuration is what we expect */ 261 assert(read_amcntenset0_px() == 262 ((UINT32_C(1) << read_amcgcr_cg0nc()) - 1U)); 263 264 #if ENABLE_AMU_AUXILIARY_COUNTERS 265 if (AMU_GROUP1_NR_COUNTERS > 0U) { 266 assert(read_amcntenset1_px() == AMU_GROUP1_COUNTERS_MASK); 267 } 268 #endif 269 /* 270 * Disable group 0/1 counters to avoid other observers like SCP sampling 271 * counter values from the future via the memory mapped view. 272 */ 273 write_amcntenclr0_px((UINT32_C(1) << read_amcgcr_cg0nc()) - 1U); 274 275 #if ENABLE_AMU_AUXILIARY_COUNTERS 276 if (AMU_GROUP1_NR_COUNTERS > 0U) { 277 write_amcntenclr1_px(AMU_GROUP1_COUNTERS_MASK); 278 } 279 #endif 280 281 isb(); 282 283 /* Save all group 0 counters */ 284 for (i = 0U; i < read_amcgcr_cg0nc(); i++) { 285 ctx->group0_cnts[i] = amu_group0_cnt_read(i); 286 } 287 288 #if ENABLE_AMU_AUXILIARY_COUNTERS 289 if (AMU_GROUP1_NR_COUNTERS > 0U) { 290 /* Save group 1 counters */ 291 for (i = 0U; i < AMU_GROUP1_NR_COUNTERS; i++) { 292 if ((AMU_GROUP1_COUNTERS_MASK & (1U << i)) != 0U) { 293 ctx->group1_cnts[i] = amu_group1_cnt_read(i); 294 } 295 } 296 } 297 #endif 298 299 return (void *)0; 300 } 301 302 static void *amu_context_restore(const void *arg) 303 { 304 struct amu_ctx *ctx = &amu_ctxs[plat_my_core_pos()]; 305 unsigned int i; 306 307 if (!amu_supported()) { 308 return (void *)-1; 309 } 310 311 #if ENABLE_AMU_AUXILIARY_COUNTERS 312 if (AMU_GROUP1_NR_COUNTERS > 0U) { 313 if (!amu_group1_supported()) { 314 return (void *)-1; 315 } 316 } 317 #endif 318 319 /* Counters were disabled in `amu_context_save()` */ 320 assert(read_amcntenset0_px() == 0U); 321 322 #if ENABLE_AMU_AUXILIARY_COUNTERS 323 if (AMU_GROUP1_NR_COUNTERS > 0U) { 324 assert(read_amcntenset1_px() == 0U); 325 } 326 #endif 327 328 /* Restore all group 0 counters */ 329 for (i = 0U; i < read_amcgcr_cg0nc(); i++) { 330 amu_group0_cnt_write(i, ctx->group0_cnts[i]); 331 } 332 333 /* Restore group 0 counter configuration */ 334 write_amcntenset0_px((UINT32_C(1) << read_amcgcr_cg0nc()) - 1U); 335 336 #if ENABLE_AMU_AUXILIARY_COUNTERS 337 if (AMU_GROUP1_NR_COUNTERS > 0U) { 338 /* Restore group 1 counters */ 339 for (i = 0U; i < AMU_GROUP1_NR_COUNTERS; i++) { 340 if ((AMU_GROUP1_COUNTERS_MASK & (1U << i)) != 0U) { 341 amu_group1_cnt_write(i, ctx->group1_cnts[i]); 342 } 343 } 344 345 /* Restore group 1 counter configuration */ 346 write_amcntenset1_px(AMU_GROUP1_COUNTERS_MASK); 347 } 348 #endif 349 350 return (void *)0; 351 } 352 353 SUBSCRIBE_TO_EVENT(psci_suspend_pwrdown_start, amu_context_save); 354 SUBSCRIBE_TO_EVENT(psci_suspend_pwrdown_finish, amu_context_restore); 355