1/** 2 * \file psci.c 3 * \brief 4 */ 5 6 7/* 8 * Copyright (c) 2017 ETH Zurich. 9 * All rights reserved. 10 * 11 * This file is distributed under the terms in the attached LICENSE file. 12 * If you do not find this file, copies can be found by writing to: 13 * ETH Zurich D-INFK, Universitaetsstrasse 6, CH-8092 Zurich. Attn: Systems Group. 14 */ 15 16#include <kernel.h> 17#include <stddef.h> 18#include <errno.h> 19 20#include <arch/arm/smc_hvc.h> 21 22#include <psci.h> 23 24 25static uint64_t psci_use_hvc = 0; 26 27static inline void psci_invoke(psci_fn_t fn, uintptr_t arg0, uintptr_t arg1, 28 uintptr_t arg2, struct arm_smc_hvc_retval *ret_val) 29{ 30 /* 31 * TODO: we need to distinguish between SMC and HVC instructions to 32 * call the PSCI function 33 */ 34 if (psci_use_hvc) 35 invoke_arm_hvc(fn, arg0, arg1, arg2, 0, 0, 0, 0, ret_val); 36 else 37 invoke_arm_smc(fn, arg0, arg1, arg2, 0, 0, 0, 0, ret_val); 38} 39 40 41static errval_t psci_error_to_barrelfish(struct arm_smc_hvc_retval retval) 42{ 43 int32_t err = (int32_t)(retval.a0 & 0xffffffff); 44 45 if (err >= 0) { 46 return SYS_ERR_OK; 47 } 48 49 if (err == PSCI_ERRNO_NOT_SUPPORTED) { 50 return PSCI_ERR_NOT_SUPPORTED; 51 } else if (err == PSCI_ERRNO_INVALID_PARAMETER) { 52 return PSCI_ERR_INVALID_PARAMETER; 53 } else if (err == PSCI_ERRNO_DENIED) { 54 return PSCI_ERR_DENIED; 55 } else if (err == PSCI_ERRNO_ALREADY_ON) { 56 return PSCI_ERR_ALREADY_ON; 57 } else if (err == PSCI_ERRNO_ON_PENDING) { 58 return PSCI_ERR_ON_PENDING; 59 } else if (err == PSCI_ERRNO_INTERNAL_FAILURE) { 60 return PSCI_ERR_INTERNAL_FAILURE; 61 } else if (err == PSCI_ERRNO_NOT_PRESENT) { 62 return PSCI_ERR_NOT_PRESENT; 63 } else if (err == PSCI_ERRNO_DISABLED) { 64 return PSCI_ERR_DISABLED; 65 } else if (err == PSCI_ERRNO_INVALID_ADDRESS) { 66 return PSCI_ERR_INVALID_ADDRESS; 67 } 68 69 return PSCI_ERR_UNKNOWN_ERROR; 70} 71 72/** 73 * @brief Return the version of PSCI implemented. 74 * 75 * @param major returns the major version 76 * @param minor returns the minor version 77 * 78 * @return SYS_ERR_OK on success 79 * PSCI_ERR_* on failure 80 */ 81errval_t psci_version(uint16_t *major, uint16_t *minor) 82{ 83 struct arm_smc_hvc_retval retval; 84 psci_invoke(PSCI_FN_VERSION, 0, 0, 0, &retval); 85 86 uint32_t version = (uint32_t)retval.a0; 87 if (major) { 88 *major = (version >> 16) & 0xffff; 89 } 90 if (minor) { 91 *minor = version & 0xffff; 92 } 93 94 return SYS_ERR_OK; 95} 96 97/** 98 * @brief Suspend execution on a core or higher level topology node. 99 * 100 * @param power_state the power state to 101 * @param entry_point Physical address of the location to resume execution 102 * @param context_id Value left in x0/r0 when resuming execution 103 * 104 * @return SYS_ERR_OK on success 105 * PSCI_ERR_INVALID_PARAMTER 106 * PSCI_ERR_INVALID_ADDRESS 107 * PSCI_ERR_DENIED 108 * 109 * This call is intended for use in idle subsystems where the core is expected 110 * to return to execution through a wakeup event. See section 5.4. 111 */ 112errval_t psci_cpu_suspend(uint32_t power_state, lpaddr_t entry_point, 113 uintptr_t context_id) 114{ 115 struct arm_smc_hvc_retval retval; 116 psci_invoke(PSCI_FN_CPU_SUSPEND, power_state, entry_point, context_id, &retval); 117 118 return psci_error_to_barrelfish(retval); 119} 120 121/** 122 * @brief Power down the calling core. 123 * 124 * @return PSCI_ERR_DENIED on failure 125 * The call does not return on success 126 * 127 * This call is intended for use in hotplug. A core that is powered down by 128 * CPU_OFF can only be powered up again in response to a CPU_ON. 129 */ 130errval_t psci_cpu_off(void) 131{ 132 struct arm_smc_hvc_retval retval; 133 psci_invoke(PSCI_FN_CPU_OFF, 0, 0, 0, &retval); 134 135 return psci_error_to_barrelfish(retval); 136} 137 138/** 139 * @brief Power up a core. 140 * 141 * @param target_cpu MPIDR of the target processors 142 * @param entry_point Address at which the core must commence execution 143 * @param context_id Value left in x0/r0 when resuming execution 144 * 145 * @return SYS_ERR_OK on success 146 * PSCI_ERR_INVALID_PARAMETERS 147 * PSCI_ERR_INVALID_ADDRESS 148 * PSCI_ERR_ALREADY_ON 149 * PSCI_ERR_ON_PENDING 150 * PSCI_ERR_INTERNAL_FAILURE 151 * 152 * This call is used to power up cores that either: 153 * - Have not yet been booted into the calling supervisory software. 154 * - Have been previously powered down with a CPU_OFF call. 155 */ 156errval_t psci_cpu_on(uintptr_t target_cpu, lpaddr_t entry_point, 157 uintptr_t context_id) 158{ 159 struct arm_smc_hvc_retval retval; 160 psci_invoke(PSCI_FN_CPU_ON, target_cpu, entry_point, context_id, &retval); 161 162 return psci_error_to_barrelfish(retval); 163} 164 165/** 166 * @brief Places the core into an IMPLEMENTATION DEFINED low-power state 167 * 168 * @return on success, does not return 169 * PSCI_ERR_NOT_SUPPORTED 170 * PSCI_ERR_DENIED 171 */ 172errval_t psci_cpu_freeze(void) 173{ 174 struct arm_smc_hvc_retval retval; 175 psci_invoke(PSCI_FN_CPU_FREEZE, 0, 0, 0, &retval); 176 177 return psci_error_to_barrelfish(retval); 178} 179 180/** 181 * @brief Will place a core into an IMPLEMENTATION DEFINED low-power state 182 * 183 * @param entry_point_address Address to be executed when waking up 184 * @param context_id Context pointer to be left in x0/r0 185 * 186 * @return SYS_ERR_OK on success 187 * PSCI_ERR_INVALID_ADDRESS 188 */ 189errval_t psci_cpu_default_suspend(lpaddr_t entry_point_address, 190 uintptr_t context_id) 191{ 192 struct arm_smc_hvc_retval retval; 193 psci_invoke(PSCI_FN_CPU_DEFAULT_SUSPEND64, entry_point_address, context_id, 194 0, &retval); 195 196 return psci_error_to_barrelfish(retval); 197} 198 199 200/** 201 * @brief Enable the caller to request status of an affinity instance 202 * 203 * @param target_affinity MPDIR of the target cpu 204 * @param lowest_affinity_level Denotes the lowest valid affinity level field 205 * @param ret_info 206 * 207 * @return 208 */ 209errval_t psci_affinity_info(uintptr_t target_affinity, 210 uint32_t lowest_affinity_level, 211 psci_affinity_t *ret_info) 212{ 213 struct arm_smc_hvc_retval retval; 214 psci_invoke(PSCI_FN_AFFINITY_INFO64, target_affinity, lowest_affinity_level, 215 0, &retval); 216 217 errval_t err = psci_error_to_barrelfish(retval); 218 if (err_is_ok(err) && ret_info) { 219 *ret_info = (psci_affinity_t)retval.a0; 220 } 221 222 return err; 223} 224 225/** 226 * @brief Optional. This is used to ask a uniprocessor Trusted OS to migrate 227 * its context to a specific core. 228 * 229 * @param target_cpu MPDIR of the target core 230 * 231 * @return SYS_ERR_OK on success 232 * PSCI_ERR_NOT_SUPPORTED 233 * PSCI_ERR_INVALID_PARAMETERS 234 * PSCI_ERR_DENIED 235 * PSCI_ERR_INTERNAL_FAILURE 236 * PSCI_ERR_NOT_PRESENT 237 */ 238errval_t psci_migrate(uintptr_t target_cpu) 239{ 240 struct arm_smc_hvc_retval retval; 241 psci_invoke(PSCI_FN_MIGRATE64, target_cpu, 0, 0, &retval); 242 243 return psci_error_to_barrelfish(retval); 244} 245 246/** 247 * @brief Optional. This function allows a caller to identify the level of 248 * multicore support present in the Trusted OS 249 * 250 * @param ret_migrate_type Migration type capabilities 251 * 252 * @return SYS_ERR_OK on success 253 * PSCI_ERR_NOT_SUPPORTED if the operation is not supported 254 */ 255errval_t psci_migrate_info_type(psci_migrate_t *ret_migrate_type) 256{ 257 struct arm_smc_hvc_retval retval; 258 psci_invoke(PSCI_FN_MIGRATE_INFO_TYPE, 0, 0, 0, &retval); 259 260 errval_t err = psci_error_to_barrelfish(retval); 261 if (err_is_ok(err) && ret_migrate_type) { 262 *ret_migrate_type = (psci_migrate_t)(retval.a0 & 0xffffffff); 263 } 264 265 return err; 266} 267 268 269/** 270 * @brief Optional. For a uniprocessor Trusted OS, this function returns the 271 * current resident core 272 * 273 * @param ret_mpdir MPIDR based value of core where the Trusted OS is resident 274 * 275 * @return SYS_ERR_OK on success 276 * PSCI_ERR_NOT_SUPPORTED if not supported 277 */ 278errval_t psci_migrate_info_up_cpu(uintptr_t *ret_mpdir) 279{ 280 struct arm_smc_hvc_retval retval; 281 psci_invoke(PSCI_FN_MIGRATE_INFO_UP_CPU64, 0, 0, 0, &retval); 282 283 errval_t err = psci_error_to_barrelfish(retval); 284 if (err_is_ok(err) && ret_mpdir) { 285 *ret_mpdir = retval.a0; 286 } 287 288 return err; 289} 290 291/** 292 * @brief Shutdown the system. 293 */ 294void psci_system_off(void) 295{ 296 struct arm_smc_hvc_retval retval; 297 psci_invoke(PSCI_FN_SYSTEM_OFF, 0, 0, 0, &retval); 298 panic("SHOULD NOT BE REACHED!\n"); 299} 300 301/** 302 * @brief Reset the system. 303 */ 304void psci_system_reset(void) 305{ 306 struct arm_smc_hvc_retval retval; 307 psci_invoke(PSCI_FN_SYSTEM_REST, 0, 0, 0, &retval); 308 panic("SHOULD NOT BE REACHED!\n"); 309} 310 311/** 312 * @brief Used to implement suspend to RAM. The semantics are equivalent to a 313 * CPU_SUSPEND to the deepest low-power state. 314 * 315 * @param entry_point_address physical address to be executed when returning 316 * @param context_id context id to be stored in x0/r0 317 * 318 * @return does not retturn on success 319 * PSCI_ERR_NOT_SUPPORTED 320 * PSCI_ERR_INVALID_ADDRESS 321 * PSCI_ERR_ALREADY_ON 322 */ 323errval_t psci_system_suspend(lpaddr_t entry_point_address, uintptr_t context_id) 324{ 325 struct arm_smc_hvc_retval retval; 326 psci_invoke(PSCI_FN_SYSTEM_SUPSEND64, entry_point_address, context_id, 0, 327 &retval); 328 329 return psci_error_to_barrelfish(retval); 330} 331 332/** 333 * @brief Query API to discover whether a specific PSCI function is implemented 334 * 335 * @param psci_fn_id Function ID for a PSCI Function 336 * @param ret_feature_flags Returns a set of feature flags of the function 337 * 338 * @return SYS_ERR_OK on success 339 * PSCI_ERR_NOT_SUPPORTED if the function is not supported or invalid 340 */ 341errval_t psci_features(uint32_t psci_fn_id, uint32_t *ret_feature_flags) 342{ 343 struct arm_smc_hvc_retval retval; 344 psci_invoke(PSCI_FN_PSCI_FEATURES, psci_fn_id, 0, 0, &retval); 345 346 errval_t err = psci_error_to_barrelfish(retval); 347 if (err_is_ok(err) && ret_feature_flags) { 348 *ret_feature_flags = (uint32_t)(retval.a0 & 0x7ffffff); 349 } 350 351 return err; 352} 353 354 355/** 356 * @brief This API is intended to return the true HW state of a node in the 357 * power domain topology of the system 358 * 359 * @param target_cpu Target CPU MPDIR 360 * @param power_level Power domain level for the node 361 * @param ret_node_hw return the power node hw state 362 * 363 * @return SYS_ERR_OK on success 364 * PSCI_ERR_NOT_SUPPORTED 365 * PSCI_ERR_INVALID_PARAMETERS 366 */ 367errval_t psci_node_hw_state(uintptr_t target_cpu, uint32_t power_level, 368 psci_node_hw_state_t *ret_node_hw) 369{ 370 struct arm_smc_hvc_retval retval; 371 psci_invoke(PSCI_FN_NODE_HW_STATE64, target_cpu, power_level, 372 0, &retval); 373 374 errval_t err = psci_error_to_barrelfish(retval); 375 if (err_is_ok(err) && ret_node_hw) { 376 *ret_node_hw = (psci_node_hw_state_t)retval.a0; 377 } 378 379 return err; 380} 381 382 383/** 384 * @brief This API allows setting the mode used by CPU_SUSPEND to coordinate 385 * power states. 386 * 387 * @param mode Mode to put the system in 388 * 389 * @return SYS_ERR_OK on success, 390 * PSCI_ERR_NOT_SUPPORTED 391 * PSCI_ERR_INVALID_PARAMETERS 392 * PSCI_ERR_DENIED 393 */ 394errval_t psci_set_suspend_mode(psci_suspend_mode_t mode) 395{ 396 struct arm_smc_hvc_retval retval; 397 psci_invoke(PSCI_FN_PSCI_SET_SUSPEND_MODE, mode, 0, 0, &retval); 398 399 return psci_error_to_barrelfish(retval); 400} 401 402/** 403 * @brief Returns the amount of time the platform has spent in the given power 404 * state since cold boot. 405 * 406 * @param target_cpu target CPU MPDIR 407 * @param power_state power state to query 408 * @param ret_residency Returns the amount of time, in microseconds, spent in state 409 * 410 * @return SYS_ERR_OK 411 */ 412errval_t psci_stat_residency(uintptr_t target_cpu, uint32_t power_state, 413 uintptr_t *ret_residency) 414{ 415 struct arm_smc_hvc_retval retval; 416 psci_invoke(PSCI_FN_PSCI_STAT_RESIDENCY64, target_cpu, power_state, 417 0, &retval); 418 419 errval_t err = psci_error_to_barrelfish(retval); 420 if (err_is_ok(err) && ret_residency) { 421 *ret_residency = retval.a0; 422 } 423 424 return err; 425} 426 427/** 428 * @brief Return the number of times the platform has used the given power state 429 * since cold boot. 430 * 431 * @param target_cpu target CPU MPDIR 432 * @param power_state power state to query 433 * @param count returns the count 434 * 435 * @return SYS_ERR_OK 436 */ 437errval_t psci_stat_count(uintptr_t target_cpu, uint32_t power_state, 438 uintptr_t *count) 439{ 440 struct arm_smc_hvc_retval retval; 441 psci_invoke(PSCI_FN_PSCI_STAT_COUNT64, target_cpu, power_state, 442 0, &retval); 443 444 errval_t err = psci_error_to_barrelfish(retval); 445 if (err_is_ok(err) && count) { 446 *count = retval.a0; 447 } 448 449 return err; 450} 451 452/** 453 * Change the PSCI's conduit to use hvc instead of smc 454 * @param use_hvc True, if hvc should be used 455 */ 456void psci_set_use_hvc(uint64_t use_hvc) 457{ 458 psci_use_hvc = use_hvc; 459} 460