aboutsummaryrefslogtreecommitdiff
path: root/src/arch/arm64/armv8/secmon/psci.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/arch/arm64/armv8/secmon/psci.c')
-rw-r--r--src/arch/arm64/armv8/secmon/psci.c705
1 files changed, 0 insertions, 705 deletions
diff --git a/src/arch/arm64/armv8/secmon/psci.c b/src/arch/arm64/armv8/secmon/psci.c
deleted file mode 100644
index 9ae8cc5c7f..0000000000
--- a/src/arch/arm64/armv8/secmon/psci.c
+++ /dev/null
@@ -1,705 +0,0 @@
-/*
- * This file is part of the coreboot project.
- *
- * Copyright 2014 Google Inc.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-
-#include <gic.h>
-#include <string.h>
-#include <stdlib.h>
-#include <smp/spinlock.h>
-#include <arch/cpu.h>
-#include <arch/psci.h>
-#include <arch/smc.h>
-#include <arch/transition.h>
-#include <arch/lib_helpers.h>
-#include <console/console.h>
-#include "secmon.h"
-
-DECLARE_SPIN_LOCK(psci_spinlock);
-
-/* Root of PSCI node tree. */
-static struct psci_node psci_root;
-
-/* Array of all the psci_nodes in system. */
-static size_t psci_num_nodes;
-static struct psci_node **psci_nodes;
-
-static inline void psci_lock(void)
-{
- spin_lock(&psci_spinlock);
-}
-
-static inline void psci_unlock(void)
-{
- spin_unlock(&psci_spinlock);
-}
-
-static inline int psci_state_locked(const struct psci_node *e)
-{
- return e->state;
-}
-
-static inline void psci_set_state_locked(struct psci_node *e, int s)
-{
- e->state = s;
-}
-
-static struct psci_node *psci_node_lookup(uint64_t mpidr, int level)
-{
- size_t i;
-
- /* The array of node pointers are in depth-first order of the tree. */
- for (i = 0; i < psci_num_nodes; i++) {
- struct psci_node *current = psci_nodes[i];
-
- if (current->mpidr > mpidr)
- break;
- if (current->mpidr < mpidr)
- continue;
- if (current->level == level)
- return current;
- }
- return NULL;
-}
-
-static inline struct psci_node *node_self(void)
-{
- return psci_node_lookup(cpu_info()->mpidr, PSCI_AFFINITY_LEVEL_0);
-}
-
-/* Find the ancestor of node affected by a state transition limited by level. */
-static struct psci_node *psci_find_ancestor(struct psci_node *e, int level,
- int state)
-{
- struct psci_node *p;
-
- /* If all siblings of the node are already off then parent can be
- * set to off as well. */
- if (state == PSCI_STATE_OFF) {
- while (1) {
- size_t i;
- struct psci_node *s;
-
- if (psci_root_node(e))
- return e;
-
- p = psci_node_parent(e);
-
- if (p->level > level)
- return e;
-
- for (i = 0; i < p->children.num; i++) {
- s = &p->children.nodes[i];
- /* Don't check target. */
- if (s == e)
- continue;
- if (psci_state_locked(s) != PSCI_STATE_OFF)
- return e;
- }
-
- e = p;
- }
- }
-
- /* All ancestors in state OFF are affected. */
- if (state == PSCI_STATE_ON_PENDING) {
- while (1) {
- /* At the root. Return last affected node. */
- if (psci_root_node(e))
- return e;
-
- p = psci_node_parent(e);
-
- if (p->level > level)
- return e;
-
- /* This parent is already ON. */
- if (psci_state_locked(p) != PSCI_STATE_OFF)
- return e;
-
- e = p;
- }
- }
-
- /* Default to returning node passed in. */
- return e;
-}
-
-static void psci_set_hierarchy_state(struct psci_node *from,
- struct psci_node *to,
- int state)
-{
- struct psci_node *end;
-
- end = psci_node_parent(to);
-
- while (from != end) {
- /* Raced with another CPU as state is already set. */
- if (psci_state_locked(from) == state)
- break;
- psci_set_state_locked(from, state);
- from = psci_node_parent(from);
- }
-}
-
-static void psci_cpu_on_callback(void *arg)
-{
- struct exc_state state;
- int target_el;
- struct psci_node *e = arg;
-
- psci_lock();
- psci_set_hierarchy_state(e, e->cpu_state.ancestor, PSCI_STATE_ON);
- psci_unlock();
-
- /* Target EL is determined if HVC is enabled or not. */
- target_el = (raw_read_scr_el3() & SCR_HVC_ENABLE) ? EL2 : EL1;
-
- memset(&state, 0, sizeof(state));
- state.elx.spsr = get_eret_el(target_el, SPSR_USE_H);
- transition_with_entry(e->cpu_state.startup.run,
- e->cpu_state.startup.arg, &state);
-}
-
-static void psci_cpu_on_prepare(struct psci_cmd *cmd,
- const struct cpu_action *a)
-{
- struct psci_node *ancestor;
- struct psci_node *e;
- int state = PSCI_STATE_ON_PENDING;
-
- e = cmd->target;
- e->cpu_state.startup = *a;
- ancestor = psci_find_ancestor(e, PSCI_AFFINITY_LEVEL_HIGHEST, state);
- e->cpu_state.ancestor = ancestor;
- cmd->ancestor = ancestor;
-}
-
-static int psci_schedule_cpu_on(struct psci_node *e)
-{
- struct cpu_info *ci;
- struct cpu_action action = {
- .run = &psci_cpu_on_callback,
- .arg = e,
- };
-
- ci = e->cpu_state.ci;
- if (ci == NULL || arch_run_on_cpu_async(ci->id, &action)) {
- psci_set_hierarchy_state(e, e->cpu_state.ancestor,
- PSCI_STATE_OFF);
- return PSCI_RET_INTERNAL_FAILURE;
- }
-
- return PSCI_RET_SUCCESS;
-}
-
-static void psci_cpu_resume_prepare(struct psci_cmd *cmd,
- const struct cpu_action *a)
-{
- struct psci_node *ancestor;
- struct psci_node *e;
- int state = PSCI_STATE_ON_PENDING;
-
- e = cmd->target;
- e->cpu_state.resume = *a;
- ancestor = psci_find_ancestor(e, PSCI_AFFINITY_LEVEL_HIGHEST, state);
- e->cpu_state.ancestor = ancestor;
- cmd->ancestor = ancestor;
-}
-
-static void psci_schedule_cpu_resume(struct psci_node *e)
-{
- struct cpu_info *ci;
- struct cpu_action *action;
-
- if (e->cpu_state.resume.run == NULL)
- return;
-
- ci = e->cpu_state.ci;
- action = &e->cpu_state.resume;
-
- arch_run_on_cpu(ci->id, action);
-}
-
-void psci_turn_on_self(const struct cpu_action *action)
-{
- struct psci_node *e = node_self();
- struct psci_cmd cmd = {
- .type = PSCI_CMD_ON,
- };
-
- if (e == NULL) {
- printk(BIOS_ERR, "Couldn't turn on self: mpidr %llx\n",
- cpu_info()->mpidr);
- return;
- }
-
- cmd.target = e;
-
- psci_lock();
- psci_cpu_on_prepare(&cmd, action);
- psci_set_hierarchy_state(e, cmd.ancestor, PSCI_STATE_ON_PENDING);
- psci_unlock();
-
- psci_schedule_cpu_on(e);
-}
-
-void psci_cpu_entry(void)
-{
- gic_enable();
-
- /*
- * Just wait for an action to be performed.
- */
- psci_schedule_cpu_resume(node_self());
- secmon_wait_for_action();
-}
-
-static void psci_cpu_resume(void *arg)
-{
- uint64_t power_state = (uint64_t)arg;
- struct psci_node *e;
- struct psci_power_state state;
- struct psci_cmd cmd = {
- .type = PSCI_CMD_RESUME,
- };
-
- psci_power_state_unpack(power_state, &state);
-
- psci_lock();
-
- e = node_self();
- /* clear the resume action after resume */
- e->cpu_state.resume.run = NULL;
- e->cpu_state.resume.arg = NULL;
-
- cmd.target = e;
- cmd.state = &state;
- soc_psci_ops.cmd_prepare(&cmd);
-
- psci_unlock();
-
- soc_psci_ops.cmd_commit(&cmd);
-
- psci_lock();
- psci_set_hierarchy_state(e, e->cpu_state.ancestor, PSCI_STATE_ON);
- psci_unlock();
-
- psci_schedule_cpu_on(node_self());
-}
-
-static void psci_cpu_suspend(struct psci_func *pf)
-{
- uint64_t power_state;
- uint64_t entry;
- uint64_t context_id;
- struct psci_node *e;
- struct psci_power_state state;
- struct cpu_action action;
- struct cpu_action resume_action;
- struct psci_cmd cmd = {
- .type = PSCI_CMD_SUSPEND,
- };
- int ret;
-
- power_state = psci64_arg(pf, PSCI_PARAM_0);
- entry = psci64_arg(pf, PSCI_PARAM_1);
- context_id = psci64_arg(pf, PSCI_PARAM_2);
- psci_power_state_unpack(power_state, &state);
-
- psci_lock();
-
- e = node_self();
- cmd.target = e;
- cmd.state = &state;
- action.run = (void *)entry;
- action.arg = (void *)context_id;
- resume_action.run = &psci_cpu_resume;
- resume_action.arg = (void*)power_state;
-
- psci_cpu_on_prepare(&cmd, &action);
- psci_cpu_resume_prepare(&cmd, &resume_action);
-
- ret = soc_psci_ops.cmd_prepare(&cmd);
-
- if (ret == PSCI_RET_SUCCESS)
- psci_set_hierarchy_state(e, cmd.ancestor, PSCI_STATE_OFF);
-
- psci_unlock();
-
- if (ret != PSCI_RET_SUCCESS)
- return psci32_return(pf, ret);
-
- gic_disable();
-
- ret = soc_psci_ops.cmd_commit(&cmd);
-
- /* PSCI_POWER_STATE_TYPE_STANDBY mode only */
-
- psci_lock();
- resume_action.run = NULL;
- resume_action.arg = NULL;
- psci_cpu_resume_prepare(&cmd, &resume_action);
- psci_unlock();
-
- if (ret != PSCI_RET_SUCCESS)
- return psci32_return(pf, ret);
-
- psci_lock();
- psci_set_hierarchy_state(e, e->cpu_state.ancestor, PSCI_STATE_ON);
- psci_unlock();
-
- psci32_return(pf, PSCI_RET_SUCCESS);
-}
-
-static void psci_cpu_on(struct psci_func *pf)
-{
- uint64_t entry;
- uint64_t target_mpidr;
- uint64_t context_id;
- int cpu_state;
- int ret;
- struct psci_node *e;
- struct cpu_action action;
- struct psci_cmd cmd = {
- .type = PSCI_CMD_ON,
- };
-
- target_mpidr = psci64_arg(pf, PSCI_PARAM_0);
- entry = psci64_arg(pf, PSCI_PARAM_1);
- context_id = psci64_arg(pf, PSCI_PARAM_2);
-
- e = psci_node_lookup(target_mpidr, PSCI_AFFINITY_LEVEL_0);
-
- if (e == NULL) {
- psci32_return(pf, PSCI_RET_INVALID_PARAMETERS);
- return;
- }
-
- psci_lock();
- cpu_state = psci_state_locked(e);
-
- if (cpu_state == PSCI_STATE_ON_PENDING) {
- psci32_return(pf, PSCI_RET_ON_PENDING);
- psci_unlock();
- return;
- } else if (cpu_state == PSCI_STATE_ON) {
- psci32_return(pf, PSCI_RET_ALREADY_ON);
- psci_unlock();
- return;
- }
-
- cmd.target = e;
- action.run = (void *)entry;
- action.arg = (void *)context_id;
- psci_cpu_on_prepare(&cmd, &action);
-
- ret = soc_psci_ops.cmd_prepare(&cmd);
-
- if (ret == PSCI_RET_SUCCESS)
- psci_set_hierarchy_state(e, cmd.ancestor,
- PSCI_STATE_ON_PENDING);
-
- psci_unlock();
-
- if (ret != PSCI_RET_SUCCESS)
- return psci32_return(pf, ret);
-
- ret = soc_psci_ops.cmd_commit(&cmd);
-
- if (ret != PSCI_RET_SUCCESS) {
- psci_lock();
- psci_set_hierarchy_state(e, cmd.ancestor, PSCI_STATE_OFF);
- psci_unlock();
- return psci32_return(pf, ret);
- }
-
- psci32_return(pf, psci_schedule_cpu_on(e));
-}
-
-static int psci_turn_off_node(struct psci_node *e, int level,
- int state_id)
-{
- int ret;
- struct psci_cmd cmd = {
- .type = PSCI_CMD_OFF,
- .state_id = state_id,
- .target = e,
- };
-
- psci_lock();
-
- cmd.ancestor = psci_find_ancestor(e, level, PSCI_STATE_OFF);
-
- ret = soc_psci_ops.cmd_prepare(&cmd);
-
- if (ret == PSCI_RET_SUCCESS)
- psci_set_hierarchy_state(e, cmd.ancestor, PSCI_STATE_OFF);
-
- psci_unlock();
-
- if (ret != PSCI_RET_SUCCESS)
- return ret;
-
- gic_disable();
-
- /* Should never return. */
- ret = soc_psci_ops.cmd_commit(&cmd);
-
- /* Adjust ret to be an error. */
- if (ret == PSCI_RET_SUCCESS)
- ret = PSCI_RET_INTERNAL_FAILURE;
-
- /* Turn things back on. */
- psci_lock();
- psci_set_hierarchy_state(e, cmd.ancestor, PSCI_STATE_ON);
- psci_unlock();
-
- return ret;
-}
-
-int psci_turn_off_self(void)
-{
- struct psci_node *e = node_self();
-
- if (e == NULL) {
- printk(BIOS_ERR, "No PSCI node for MPIDR %llx.\n",
- cpu_info()->mpidr);
- return PSCI_RET_INTERNAL_FAILURE;
- }
-
- /* -1 state id indicates to SoC to make its own decision for
- * internal state when powering off the node. */
- return psci_turn_off_node(e, PSCI_AFFINITY_LEVEL_HIGHEST, -1);
-}
-
-static int psci_handler(struct smc_call *smc)
-{
- struct psci_func pf_storage;
- struct psci_func *pf = &pf_storage;
-
- psci_func_init(pf, smc);
-
- switch (pf->id) {
- case PSCI_CPU_SUSPEND64:
- psci_cpu_suspend(pf);
- break;
- case PSCI_CPU_ON64:
- psci_cpu_on(pf);
- break;
- case PSCI_CPU_OFF32:
- psci32_return(pf, psci_turn_off_self());
- break;
- default:
- psci32_return(pf, PSCI_RET_NOT_SUPPORTED);
- break;
- }
-
- return 0;
-}
-
-static void psci_link_cpu_info(void *arg)
-{
- struct psci_node *e = node_self();
-
- if (e == NULL) {
- printk(BIOS_ERR, "No PSCI node for MPIDR %llx.\n",
- cpu_info()->mpidr);
- return;
- }
-
- e->cpu_state.ci = cpu_info();
-}
-
-static int psci_init_node(struct psci_node *e,
- struct psci_node *parent,
- int level, uint64_t mpidr)
-{
- size_t i;
- uint64_t mpidr_inc;
- struct psci_node_group *ng;
- size_t num_children;
-
- memset(e, 0, sizeof(*e));
- e->mpidr = mpidr;
- psci_set_state_locked(e, PSCI_STATE_OFF);
- e->parent = parent;
- e->level = level;
-
- if (level == PSCI_AFFINITY_LEVEL_0)
- return 0;
-
- num_children = soc_psci_ops.children_at_level(level, mpidr);
-
- if (num_children == 0)
- return 0;
-
- ng = &e->children;
- ng->num = num_children;
- ng->nodes = malloc(ng->num * sizeof(struct psci_node));
- if (ng->nodes == NULL) {
- printk(BIOS_DEBUG, "PSCI: Allocation failure at level %d\n",
- level);
- return -1;
- }
-
- /* Switch to next level below. */
- level = psci_level_below(level);
- mpidr_inc = mpidr_mask(!!(level == PSCI_AFFINITY_LEVEL_3),
- !!(level == PSCI_AFFINITY_LEVEL_2),
- !!(level == PSCI_AFFINITY_LEVEL_1),
- !!(level == PSCI_AFFINITY_LEVEL_0));
-
- for (i = 0; i < ng->num; i++) {
- struct psci_node *c = &ng->nodes[i];
-
- /* Recursively initialize the nodes. */
- if (psci_init_node(c, e, level, mpidr))
- return -1;
- mpidr += mpidr_inc;
- }
-
- return 0;
-}
-
-static size_t psci_count_children(struct psci_node *e)
-{
- size_t i;
- size_t count;
-
- if (e->level == PSCI_AFFINITY_LEVEL_0)
- return 0;
-
- count = e->children.num;
- for (i = 0; i < e->children.num; i++)
- count += psci_count_children(&e->children.nodes[i]);
-
- return count;
-}
-
-static size_t psci_write_nodes(struct psci_node *e, size_t index)
-{
- size_t i;
-
- /*
- * Recursively save node pointers in array. Node pointers are
- * ordered in ascending mpidr and descending level within same mpidr.
- * i.e. each node is saved in depth-first order of the tree.
- */
- if (e->level != PSCI_AFFINITY_ROOT) {
- psci_nodes[index] = e;
- index++;
- }
-
- if (e->level == PSCI_AFFINITY_LEVEL_0)
- return index;
-
- for (i = 0; i < e->children.num; i++)
- index = psci_write_nodes(&e->children.nodes[i], index);
-
- return index;
-}
-
-static int psci_allocate_nodes(void)
-{
- int level;
- size_t num_children;
- uint64_t mpidr;
- struct psci_node *e;
-
- mpidr = 0;
- level = PSCI_AFFINITY_ROOT;
-
- /* Find where the root should start. */
- while (psci_level_below(level) >= PSCI_AFFINITY_LEVEL_0) {
- num_children = soc_psci_ops.children_at_level(level, mpidr);
-
- if (num_children == 0) {
- printk(BIOS_ERR, "PSCI: No children at level %d!\n",
- level);
- return -1;
- }
-
- /* The root starts where the affinity levels branch. */
- if (num_children > 1)
- break;
-
- level = psci_level_below(level);
- }
-
- if (psci_init_node(&psci_root, NULL, level, mpidr)) {
- printk(BIOS_ERR, "PSCI init node failure.\n");
- return -1;
- }
-
- num_children = psci_count_children(&psci_root);
- /* Count the root node if isn't a fake node. */
- if (psci_root.level != PSCI_AFFINITY_ROOT)
- num_children++;
-
- psci_nodes = malloc(num_children * sizeof(void *));
- psci_num_nodes = num_children;
-
- if (psci_nodes == NULL) {
- printk(BIOS_ERR, "PSCI node pointer array failure.\n");
- return -1;
- }
-
- num_children = psci_write_nodes(&psci_root, 0);
- if (num_children != psci_num_nodes) {
- printk(BIOS_ERR, "Wrong nodes written: %zd vs %zd.\n",
- num_children, psci_num_nodes);
- return -1;
- }
-
- /*
- * By default all nodes are set to PSCI_STATE_OFF. In order not
- * to race with other CPUs turning themselves off set the BSPs
- * affinity node to ON.
- */
- e = node_self();
- if (e == NULL) {
- printk(BIOS_ERR, "No PSCI node for BSP.\n");
- return -1;
- }
- psci_set_state_locked(e, PSCI_STATE_ON);
-
- return 0;
-}
-
-void psci_init(uintptr_t cpu_on_entry)
-{
- struct cpu_action action = {
- .run = &psci_link_cpu_info,
- };
-
- if (psci_allocate_nodes()) {
- printk(BIOS_ERR, "PSCI support not enabled.\n");
- return;
- }
-
- if (arch_run_on_all_cpus_async(&action))
- printk(BIOS_ERR, "Error linking cpu_info to PSCI nodes.\n");
-
- /* Register PSCI handlers. */
- if (smc_register_range(PSCI_CPU_SUSPEND32, PSCI_CPU_ON32,
- &psci_handler))
- printk(BIOS_ERR, "Couldn't register PSCI handler.\n");
-
- if (smc_register_range(PSCI_CPU_SUSPEND64, PSCI_CPU_ON64,
- &psci_handler))
- printk(BIOS_ERR, "Couldn't register PSCI handler.\n");
-
- /* Inform SoC layer of CPU_ON entry point. */
- psci_soc_init(cpu_on_entry);
-}