arm64: add psci support to secmon

The PSCI functionality initially includes CPU_ON and CPU_OFF
functions. Upon entering secmon if the parameters are non-NULL
then a PSCI CPU_ON action is done for the current CPU.

BUG=chrome-os-partner:32112
BRANCH=None
TEST=Booted kernel with PSCI support. Brought up all CPUs in kernel
     using PSCI. Turned CPUs on and off.

Change-Id: I256fa45a1c9889ff9d7990eb1898df1ec241c117
Signed-off-by: Patrick Georgi <pgeorgi@chromium.org>
Original-Commit-Id: 689ba03e313e7e52e9b74aa774897b55cbd52748
Original-Change-Id: I943826b7dbcc8e3f6c8c4b66344af8fac12ba94e
Original-Signed-off-by: Aaron Durbin <adurbin@chromium.org>
Original-Reviewed-on: https://chromium-review.googlesource.com/218923
Original-Reviewed-by: Furquan Shaikh <furquan@chromium.org>
Reviewed-on: http://review.coreboot.org/9097
Reviewed-by: Patrick Georgi <pgeorgi@google.com>
Tested-by: build bot (Jenkins)
This commit is contained in:
Aaron Durbin 2014-09-19 15:52:31 -05:00 committed by Patrick Georgi
parent 93eea8822d
commit bda3577d0c
4 changed files with 329 additions and 15 deletions

View File

@ -32,6 +32,7 @@ secmon-c-ccopts += -I$(src)/arch/arm64/include/armv8/ -include $(src)/include/kc
secmon-S-ccopts += -I$(src)/arch/arm64/include/armv8/ -include $(src)/include/kconfig.h -D__SECMON__
secmon-y += secmon_init.c
secmon-y += psci.c
secmon-y += smc.c
secmon-y += trampoline.S
secmon-y += ../exception.c

View File

@ -0,0 +1,208 @@
/*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA, 02110-1301 USA
*/
#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"
enum {
PSCI_CPU_STATE_OFF = 0,
PSCI_CPU_STATE_ON_PENDING,
PSCI_CPU_STATE_ON,
};
struct psci_cpu_state {
uint64_t mpidr;
void *entry;
void *arg;
int state;
};
DECLARE_SPIN_LOCK(psci_spinlock);
static struct psci_cpu_state psci_state[CONFIG_MAX_CPUS];
static inline void psci_lock(void)
{
spin_lock(&psci_spinlock);
}
static inline void psci_unlock(void)
{
spin_unlock(&psci_spinlock);
}
static inline int psci_cpu_state_locked(int i)
{
return psci_state[i].state;
}
static inline void psci_cpu_set_state_locked(int i, int s)
{
psci_state[i].state = s;
}
static struct cpu_info *mpidr_to_cpu_info(uint64_t mpidr)
{
int i;
for (i = 0; i < ARRAY_SIZE(psci_state); i++) {
if (mpidr == psci_state[i].mpidr)
return cpu_info_for_cpu(i);
}
return NULL;
}
static void psci_cpu_on_callback(void *arg)
{
struct psci_cpu_state *s = arg;
psci_turn_on_self(s->entry, s->arg);
}
static void psci_cpu_on(struct psci_func *pf)
{
uint64_t entry;
uint64_t target_mpidr;
uint64_t context_id;
struct cpu_info *ci;
int cpu_state;
struct cpu_action action;
target_mpidr = psci64_arg(pf, PSCI_PARAM_0);
entry = psci64_arg(pf, PSCI_PARAM_1);
context_id = psci64_arg(pf, PSCI_PARAM_2);
ci = mpidr_to_cpu_info(target_mpidr);
if (ci == NULL) {
psci32_return(pf, PSCI_RET_INVALID_PARAMETERS);
return;
}
psci_lock();
cpu_state = psci_cpu_state_locked(ci->id);
if (cpu_state == PSCI_CPU_STATE_ON_PENDING) {
psci32_return(pf, PSCI_RET_ON_PENDING);
psci_unlock();
return;
} else if (cpu_state == PSCI_CPU_STATE_ON) {
psci32_return(pf, PSCI_RET_ALREADY_ON);
psci_unlock();
return;
}
psci_cpu_set_state_locked(ci->id, PSCI_CPU_STATE_ON_PENDING);
/* Set the parameters and initialize the action. */
psci_state[ci->id].entry = (void *)(uintptr_t)entry;
psci_state[ci->id].arg = (void *)(uintptr_t)context_id;
action.run = &psci_cpu_on_callback;
action.arg = &psci_state[ci->id];
if (arch_run_on_cpu_async(ci->id, &action)) {
psci32_return(pf, PSCI_RET_INTERNAL_FAILURE);
psci_unlock();
return;
}
psci_unlock();
psci32_return(pf, PSCI_RET_SUCCESS);
}
static void psci_cpu_off(struct psci_func *pf)
{
psci_lock();
psci_cpu_set_state_locked(cpu_info()->id, PSCI_CPU_STATE_OFF);
psci_unlock();
/* TODO(adurbin): writeback cache and actually turn off CPU. */
secmon_trampoline(&secmon_wait_for_action, NULL);
}
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_ON64:
psci_cpu_on(pf);
break;
case PSCI_CPU_OFF64:
psci_cpu_off(pf);
break;
default:
psci32_return(pf, PSCI_RET_NOT_SUPPORTED);
break;
}
return 0;
}
void psci_init(void)
{
struct cpu_info *ci;
uint64_t mpidr;
/* Set this CPUs MPIDR clearing the bits that are not per-cpu. */
ci = cpu_info();
mpidr = raw_read_mpidr_el1();
mpidr &= ~(1ULL << 31); /* RES1 */
mpidr &= ~(1ULL << 30); /* U */
mpidr &= ~(1ULL << 24); /* MT */
psci_state[ci->id].mpidr = mpidr;
if (!cpu_is_bsp())
return;
/* Register PSCI handlers. */
if (smc_register_range(PSCI_CPU_OFF64, PSCI_CPU_ON64, &psci_handler))
printk(BIOS_ERR, "Couldn't register PSCI handler.\n");
}
void psci_turn_on_self(void *entry, void *arg)
{
struct exc_state state;
int target_el;
struct cpu_info *ci = cpu_info();
psci_lock();
psci_cpu_set_state_locked(ci->id, PSCI_CPU_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(entry, arg, &state);
}

View File

@ -23,11 +23,10 @@
#include <arch/io.h>
#include <arch/exception.h>
#include <arch/lib_helpers.h>
#include <arch/psci.h>
#include <arch/secmon.h>
#include <arch/smc.h>
#include <arch/transition.h>
#include <console/console.h>
#include <rmodule.h>
#include <stddef.h>
#include "secmon.h"
@ -44,25 +43,16 @@ static void cpu_init(int bsp)
static void secmon_init(struct secmon_params *params, int bsp)
{
struct exc_state exc_state;
exception_hwinit();
cpu_init(bsp);
smc_init();
psci_init();
/*
* Check if the arg is non-NULL
* 1) If yes, we make an EL2 transition to that entry point
* 2) If no, we just wait
*/
if (params != NULL) {
memset(&exc_state, 0, sizeof(exc_state));
exc_state.elx.spsr =
get_eret_el(params->elx_el, params->elx_mode);
/* Turn on CPU if params are not NULL. */
if (params != NULL)
psci_turn_on_self(params->entry, params->arg);
transition_with_entry(params->entry, params->arg, &exc_state);
}
secmon_wait_for_action();
}

View File

@ -0,0 +1,115 @@
/*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA, 02110-1301 USA
*/
#ifndef __ARCH_PSCI_H__
#define __ARCH_PSCI_H__
#include <arch/smc.h>
/* Return Values */
enum {
PSCI_RET_SUCCESS = 0,
PSCI_RET_NOT_SUPPORTED = -1,
PSCI_RET_INVALID_PARAMETERS = -2,
PSCI_RET_DENIED = -3,
PSCI_RET_ALREADY_ON = -4,
PSCI_RET_ON_PENDING = -5,
PSCI_RET_INTERNAL_FAILURE = -6,
PSCI_RET_NOT_PRESENT = -7,
PSCI_RET_DISABLED = -8,
};
/* PSCI Functions. */
enum {
/* 32-bit System level functions. */
PSCI_VERSION = SMC_FUNC_FAST32(0x4, 0x0),
PSCI_SYSTEM_OFF = SMC_FUNC_FAST32(0x4, 0x8),
PSCI_SYSTEM_RESET = SMC_FUNC_FAST32(0x4, 0x9),
/* 32-bit CPU support functions. */
PSCI_CPU_SUSPEND32 = SMC_FUNC_FAST32(0x4, 0x1),
PSCI_CPU_OFF32 = SMC_FUNC_FAST32(0x4, 0x2),
PSCI_CPU_ON32 = SMC_FUNC_FAST32(0x4, 0x3),
/* 64-bit CPU support functions. */
PSCI_CPU_SUSPEND64 = SMC_FUNC_FAST64(0x4, 0x1),
PSCI_CPU_OFF64 = SMC_FUNC_FAST64(0x4, 0x2),
PSCI_CPU_ON64 = SMC_FUNC_FAST64(0x4, 0x3),
};
/* Parameter arguments. */
enum {
PSCI_PARAM_0 = 1,
PSCI_PARAM_1,
PSCI_PARAM_2,
PSCI_PARAM_3,
PSCI_RETURN_0 = 1,
PSCI_RETURN_1,
PSCI_RETURN_2,
PSCI_RETURN_3,
};
struct psci_func {
uint32_t id;
struct smc_call *smc;
};
static inline void psci_func_init(struct psci_func *pf, struct smc_call *smc)
{
pf->id = smc_function_id(smc);
pf->smc = smc;
}
static inline uint64_t psci64_arg(struct psci_func *pf, unsigned i)
{
return smc64_arg(pf->smc, i);
}
static inline uint32_t psci32_arg(struct psci_func *pf, unsigned i)
{
return psci64_arg(pf, i);
}
static inline void psci64_result(struct psci_func *pf, unsigned i, uint64_t v)
{
smc64_result(pf->smc, i, v);
}
static inline void psci32_result(struct psci_func *pf, unsigned i, uint32_t v)
{
uint64_t v64 = v;
psci64_result(pf, i, v64);
}
static inline void psci32_return(struct psci_func *pf, int32_t val)
{
psci32_result(pf, 0, val);
}
static inline void psci64_return(struct psci_func *pf, int64_t val)
{
psci64_result(pf, 0, val);
}
void psci_init(void);
/* Turn on the current CPU within the PSCI subsystem. */
void psci_turn_on_self(void *entry, void *arg);
#endif /* __ARCH_PSCI_H__ */