Antonio Nino Diaz 09d40e0e08 Sanitise includes across codebase
Enforce full include path for includes. Deprecate old paths.

The following folders inside include/lib have been left unchanged:

- include/lib/cpus/${ARCH}
- include/lib/el3_runtime/${ARCH}

The reason for this change is that having a global namespace for
includes isn't a good idea. It defeats one of the advantages of having
folders and it introduces problems that are sometimes subtle (because
you may not know the header you are actually including if there are two
of them).

For example, this patch had to be created because two headers were
called the same way: e0ea0928d5b7 ("Fix gpio includes of mt8173 platform
to avoid collision."). More recently, this patch has had similar
problems: 46f9b2c3a282 ("drivers: add tzc380 support").

This problem was introduced in commit 4ecca33988b9 ("Move include and
source files to logical locations"). At that time, there weren't too
many headers so it wasn't a real issue. However, time has shown that
this creates problems.

Platforms that want to preserve the way they include headers may add the
removed paths to PLAT_INCLUDES, but this is discouraged.

Change-Id: I39dc53ed98f9e297a5966e723d1936d6ccf2fc8f
Signed-off-by: Antonio Nino Diaz <antonio.ninodiaz@arm.com>
2019-01-04 10:43:17 +00:00

1094 lines
25 KiB
C

/*
* Copyright (c) 2017-2018, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <arch_helpers.h>
#include <assert.h>
#include <stddef.h>
#include <string.h>
#include <bl31/bl31.h>
#include <bl31/ehf.h>
#include <bl31/interrupt_mgmt.h>
#include <common/bl_common.h>
#include <common/debug.h>
#include <common/runtime_svc.h>
#include <context.h>
#include <lib/cassert.h>
#include <lib/el3_runtime/pubsub.h>
#include <lib/utils.h>
#include <plat/common/platform.h>
#include <services/sdei.h>
#include "sdei_private.h"
#define MAJOR_VERSION 1ULL
#define MINOR_VERSION 0ULL
#define VENDOR_VERSION 0ULL
#define MAKE_SDEI_VERSION(_major, _minor, _vendor) \
((((_major)) << 48ULL) | (((_minor)) << 32ULL) | (_vendor))
#define LOWEST_INTR_PRIORITY 0xff
#define is_valid_affinity(_mpidr) (plat_core_pos_by_mpidr(_mpidr) >= 0)
CASSERT(PLAT_SDEI_CRITICAL_PRI < PLAT_SDEI_NORMAL_PRI,
sdei_critical_must_have_higher_priority);
static unsigned int num_dyn_priv_slots, num_dyn_shrd_slots;
/* Initialise SDEI map entries */
static void init_map(sdei_ev_map_t *map)
{
map->reg_count = 0;
}
/* Convert mapping to SDEI class */
static sdei_class_t map_to_class(sdei_ev_map_t *map)
{
return is_event_critical(map) ? SDEI_CRITICAL : SDEI_NORMAL;
}
/* Clear SDEI event entries except state */
static void clear_event_entries(sdei_entry_t *se)
{
se->ep = 0;
se->arg = 0;
se->affinity = 0;
se->reg_flags = 0;
}
/* Perform CPU-specific state initialisation */
static void *sdei_cpu_on_init(const void *arg)
{
unsigned int i;
sdei_ev_map_t *map;
sdei_entry_t *se;
/* Initialize private mappings on this CPU */
for_each_private_map(i, map) {
se = get_event_entry(map);
clear_event_entries(se);
se->state = 0;
}
SDEI_LOG("Private events initialized on %lx\n", read_mpidr_el1());
/* All PEs start with SDEI events masked */
(void) sdei_pe_mask();
return NULL;
}
/* CPU initialisation after wakeup from suspend */
static void *sdei_cpu_wakeup_init(const void *arg)
{
SDEI_LOG("Events masked on %lx\n", read_mpidr_el1());
/* All PEs wake up with SDEI events masked */
sdei_pe_mask();
return 0;
}
/* Initialise an SDEI class */
static void sdei_class_init(sdei_class_t class)
{
unsigned int i;
bool zero_found __unused = false;
int ev_num_so_far __unused;
sdei_ev_map_t *map;
/* Sanity check and configuration of shared events */
ev_num_so_far = -1;
for_each_shared_map(i, map) {
#if ENABLE_ASSERTIONS
/* Ensure mappings are sorted */
assert((ev_num_so_far < 0) || (map->ev_num > ev_num_so_far));
ev_num_so_far = map->ev_num;
/* Event 0 must not be shared */
assert(map->ev_num != SDEI_EVENT_0);
/* Check for valid event */
assert(map->ev_num >= 0);
/* Make sure it's a shared event */
assert(is_event_shared(map));
/* No shared mapping should have signalable property */
assert(!is_event_signalable(map));
/* Shared mappings can't be explicit */
assert(!is_map_explicit(map));
#endif
/* Skip initializing the wrong priority */
if (map_to_class(map) != class)
continue;
/* Platform events are always bound, so set the bound flag */
if (is_map_dynamic(map)) {
assert(map->intr == SDEI_DYN_IRQ);
assert(is_event_normal(map));
num_dyn_shrd_slots++;
} else {
/* Shared mappings must be bound to shared interrupt */
assert(plat_ic_is_spi(map->intr) != 0);
set_map_bound(map);
}
init_map(map);
}
/* Sanity check and configuration of private events for this CPU */
ev_num_so_far = -1;
for_each_private_map(i, map) {
#if ENABLE_ASSERTIONS
/* Ensure mappings are sorted */
assert((ev_num_so_far < 0) || (map->ev_num > ev_num_so_far));
ev_num_so_far = map->ev_num;
if (map->ev_num == SDEI_EVENT_0) {
zero_found = true;
/* Event 0 must be a Secure SGI */
assert(is_secure_sgi(map->intr));
/*
* Event 0 can have only have signalable flag (apart
* from being private
*/
assert(map->map_flags == (SDEI_MAPF_SIGNALABLE |
SDEI_MAPF_PRIVATE));
} else {
/* No other mapping should have signalable property */
assert(!is_event_signalable(map));
}
/* Check for valid event */
assert(map->ev_num >= 0);
/* Make sure it's a private event */
assert(is_event_private(map));
/*
* Other than priority, explicit events can only have explicit
* and private flags set.
*/
if (is_map_explicit(map)) {
assert((map->map_flags | SDEI_MAPF_CRITICAL) ==
(SDEI_MAPF_EXPLICIT | SDEI_MAPF_PRIVATE
| SDEI_MAPF_CRITICAL));
}
#endif
/* Skip initializing the wrong priority */
if (map_to_class(map) != class)
continue;
/* Platform events are always bound, so set the bound flag */
if (map->ev_num != SDEI_EVENT_0) {
if (is_map_dynamic(map)) {
assert(map->intr == SDEI_DYN_IRQ);
assert(is_event_normal(map));
num_dyn_priv_slots++;
} else if (is_map_explicit(map)) {
/*
* Explicit mappings don't have a backing
* SDEI interrupt, but verify that anyway.
*/
assert(map->intr == SDEI_DYN_IRQ);
} else {
/*
* Private mappings must be bound to private
* interrupt.
*/
assert(plat_ic_is_ppi((unsigned) map->intr) != 0);
set_map_bound(map);
}
}
init_map(map);
}
/* Ensure event 0 is in the mapping */
assert(zero_found);
(void) sdei_cpu_on_init(NULL);
}
/* SDEI dispatcher initialisation */
void sdei_init(void)
{
sdei_class_init(SDEI_CRITICAL);
sdei_class_init(SDEI_NORMAL);
/* Register priority level handlers */
ehf_register_priority_handler(PLAT_SDEI_CRITICAL_PRI,
sdei_intr_handler);
ehf_register_priority_handler(PLAT_SDEI_NORMAL_PRI,
sdei_intr_handler);
}
/* Populate SDEI event entry */
static void set_sdei_entry(sdei_entry_t *se, uint64_t ep, uint64_t arg,
unsigned int flags, uint64_t affinity)
{
assert(se != NULL);
se->ep = ep;
se->arg = arg;
se->affinity = (affinity & MPIDR_AFFINITY_MASK);
se->reg_flags = flags;
}
static uint64_t sdei_version(void)
{
return MAKE_SDEI_VERSION(MAJOR_VERSION, MINOR_VERSION, VENDOR_VERSION);
}
/* Validate flags and MPIDR values for REGISTER and ROUTING_SET calls */
static int validate_flags(uint64_t flags, uint64_t mpidr)
{
/* Validate flags */
switch (flags) {
case SDEI_REGF_RM_PE:
if (!is_valid_affinity(mpidr))
return SDEI_EINVAL;
break;
case SDEI_REGF_RM_ANY:
break;
default:
/* Unknown flags */
return SDEI_EINVAL;
}
return 0;
}
/* Set routing of an SDEI event */
static int sdei_event_routing_set(int ev_num, uint64_t flags, uint64_t mpidr)
{
int ret;
unsigned int routing;
sdei_ev_map_t *map;
sdei_entry_t *se;
ret = validate_flags(flags, mpidr);
if (ret != 0)
return ret;
/* Check if valid event number */
map = find_event_map(ev_num);
if (map == NULL)
return SDEI_EINVAL;
/* The event must not be private */
if (is_event_private(map))
return SDEI_EINVAL;
se = get_event_entry(map);
sdei_map_lock(map);
if (!is_map_bound(map) || is_event_private(map)) {
ret = SDEI_EINVAL;
goto finish;
}
if (!can_sdei_state_trans(se, DO_ROUTING)) {
ret = SDEI_EDENY;
goto finish;
}
/* Choose appropriate routing */
routing = (unsigned int) ((flags == SDEI_REGF_RM_ANY) ?
INTR_ROUTING_MODE_ANY : INTR_ROUTING_MODE_PE);
/* Update event registration flag */
se->reg_flags = (unsigned int) flags;
/*
* ROUTING_SET is permissible only when event composite state is
* 'registered, disabled, and not running'. This means that the
* interrupt is currently disabled, and not active.
*/
plat_ic_set_spi_routing(map->intr, routing, (u_register_t) mpidr);
finish:
sdei_map_unlock(map);
return ret;
}
/* Register handler and argument for an SDEI event */
static int64_t sdei_event_register(int ev_num, uint64_t ep, uint64_t arg,
uint64_t flags, uint64_t mpidr)
{
int ret;
unsigned int routing;
sdei_entry_t *se;
sdei_ev_map_t *map;
sdei_state_t backup_state;
if ((ep == 0U) || (plat_sdei_validate_entry_point(
ep, sdei_client_el()) != 0)) {
return SDEI_EINVAL;
}
ret = validate_flags(flags, mpidr);
if (ret != 0)
return ret;
/* Check if valid event number */
map = find_event_map(ev_num);
if (map == NULL)
return SDEI_EINVAL;
/* Private events always target the PE */
if (is_event_private(map))
flags = SDEI_REGF_RM_PE;
se = get_event_entry(map);
/*
* Even though register operation is per-event (additionally for private
* events, registration is required individually), it has to be
* serialised with respect to bind/release, which are global operations.
* So we hold the lock throughout, unconditionally.
*/
sdei_map_lock(map);
backup_state = se->state;
if (!can_sdei_state_trans(se, DO_REGISTER))
goto fallback;
/*
* When registering for dynamic events, make sure it's been bound
* already. This has to be the case as, without binding, the client
* can't know about the event number to register for.
*/
if (is_map_dynamic(map) && !is_map_bound(map))
goto fallback;
if (is_event_private(map)) {
/* Multiple calls to register are possible for private events */
assert(map->reg_count >= 0);
} else {
/* Only single call to register is possible for shared events */
assert(map->reg_count == 0);
}
if (is_map_bound(map)) {
/* Meanwhile, did any PE ACK the interrupt? */
if (plat_ic_get_interrupt_active(map->intr) != 0U)
goto fallback;
/* The interrupt must currently owned by Non-secure */
if (plat_ic_get_interrupt_type(map->intr) != INTR_TYPE_NS)
goto fallback;
/*
* Disable forwarding of new interrupt triggers to CPU
* interface.
*/
plat_ic_disable_interrupt(map->intr);
/*
* Any events that are triggered after register and before
* enable should remain pending. Clear any previous interrupt
* triggers which are pending (except for SGIs). This has no
* affect on level-triggered interrupts.
*/
if (ev_num != SDEI_EVENT_0)
plat_ic_clear_interrupt_pending(map->intr);
/* Map interrupt to EL3 and program the correct priority */
plat_ic_set_interrupt_type(map->intr, INTR_TYPE_EL3);
/* Program the appropriate interrupt priority */
plat_ic_set_interrupt_priority(map->intr, sdei_event_priority(map));
/*
* Set the routing mode for shared event as requested. We
* already ensure that shared events get bound to SPIs.
*/
if (is_event_shared(map)) {
routing = (unsigned int) ((flags == SDEI_REGF_RM_ANY) ?
INTR_ROUTING_MODE_ANY : INTR_ROUTING_MODE_PE);
plat_ic_set_spi_routing(map->intr, routing,
(u_register_t) mpidr);
}
}
/* Populate event entries */
set_sdei_entry(se, ep, arg, (unsigned int) flags, mpidr);
/* Increment register count */
map->reg_count++;
sdei_map_unlock(map);
return 0;
fallback:
/* Reinstate previous state */
se->state = backup_state;
sdei_map_unlock(map);
return SDEI_EDENY;
}
/* Enable SDEI event */
static int64_t sdei_event_enable(int ev_num)
{
sdei_ev_map_t *map;
sdei_entry_t *se;
int ret;
bool before, after;
/* Check if valid event number */
map = find_event_map(ev_num);
if (map == NULL)
return SDEI_EINVAL;
se = get_event_entry(map);
ret = SDEI_EDENY;
if (is_event_shared(map))
sdei_map_lock(map);
before = GET_EV_STATE(se, ENABLED);
if (!can_sdei_state_trans(se, DO_ENABLE))
goto finish;
after = GET_EV_STATE(se, ENABLED);
/*
* Enable interrupt for bound events only if there's a change in enabled
* state.
*/
if (is_map_bound(map) && (!before && after))
plat_ic_enable_interrupt(map->intr);
ret = 0;
finish:
if (is_event_shared(map))
sdei_map_unlock(map);
return ret;
}
/* Disable SDEI event */
static int sdei_event_disable(int ev_num)
{
sdei_ev_map_t *map;
sdei_entry_t *se;
int ret;
bool before, after;
/* Check if valid event number */
map = find_event_map(ev_num);
if (map == NULL)
return SDEI_EINVAL;
se = get_event_entry(map);
ret = SDEI_EDENY;
if (is_event_shared(map))
sdei_map_lock(map);
before = GET_EV_STATE(se, ENABLED);
if (!can_sdei_state_trans(se, DO_DISABLE))
goto finish;
after = GET_EV_STATE(se, ENABLED);
/*
* Disable interrupt for bound events only if there's a change in
* enabled state.
*/
if (is_map_bound(map) && (before && !after))
plat_ic_disable_interrupt(map->intr);
ret = 0;
finish:
if (is_event_shared(map))
sdei_map_unlock(map);
return ret;
}
/* Query SDEI event information */
static int64_t sdei_event_get_info(int ev_num, int info)
{
sdei_entry_t *se;
sdei_ev_map_t *map;
uint64_t flags;
bool registered;
uint64_t affinity;
/* Check if valid event number */
map = find_event_map(ev_num);
if (map == NULL)
return SDEI_EINVAL;
se = get_event_entry(map);
if (is_event_shared(map))
sdei_map_lock(map);
/* Sample state under lock */
registered = GET_EV_STATE(se, REGISTERED);
flags = se->reg_flags;
affinity = se->affinity;
if (is_event_shared(map))
sdei_map_unlock(map);
switch (info) {
case SDEI_INFO_EV_TYPE:
return is_event_shared(map);
case SDEI_INFO_EV_NOT_SIGNALED:
return !is_event_signalable(map);
case SDEI_INFO_EV_PRIORITY:
return is_event_critical(map);
case SDEI_INFO_EV_ROUTING_MODE:
if (!is_event_shared(map))
return SDEI_EINVAL;
if (!registered)
return SDEI_EDENY;
return (flags == SDEI_REGF_RM_PE);
case SDEI_INFO_EV_ROUTING_AFF:
if (!is_event_shared(map))
return SDEI_EINVAL;
if (!registered)
return SDEI_EDENY;
if (flags != SDEI_REGF_RM_PE)
return SDEI_EINVAL;
return affinity;
default:
return SDEI_EINVAL;
}
}
/* Unregister an SDEI event */
static int sdei_event_unregister(int ev_num)
{
int ret = 0;
sdei_entry_t *se;
sdei_ev_map_t *map;
/* Check if valid event number */
map = find_event_map(ev_num);
if (map == NULL)
return SDEI_EINVAL;
se = get_event_entry(map);
/*
* Even though unregister operation is per-event (additionally for
* private events, unregistration is required individually), it has to
* be serialised with respect to bind/release, which are global
* operations. So we hold the lock throughout, unconditionally.
*/
sdei_map_lock(map);
if (!can_sdei_state_trans(se, DO_UNREGISTER)) {
/*
* Even if the call is invalid, and the handler is running (for
* example, having unregistered from a running handler earlier),
* return pending error code; otherwise, return deny.
*/
ret = GET_EV_STATE(se, RUNNING) ? SDEI_EPEND : SDEI_EDENY;
goto finish;
}
map->reg_count--;
if (is_event_private(map)) {
/* Multiple calls to register are possible for private events */
assert(map->reg_count >= 0);
} else {
/* Only single call to register is possible for shared events */
assert(map->reg_count == 0);
}
if (is_map_bound(map)) {
plat_ic_disable_interrupt(map->intr);
/*
* Clear pending interrupt. Skip for SGIs as they may not be
* cleared on interrupt controllers.
*/
if (ev_num != SDEI_EVENT_0)
plat_ic_clear_interrupt_pending(map->intr);
assert(plat_ic_get_interrupt_type(map->intr) == INTR_TYPE_EL3);
plat_ic_set_interrupt_type(map->intr, INTR_TYPE_NS);
plat_ic_set_interrupt_priority(map->intr, LOWEST_INTR_PRIORITY);
}
clear_event_entries(se);
/*
* If the handler is running at the time of unregister, return the
* pending error code.
*/
if (GET_EV_STATE(se, RUNNING))
ret = SDEI_EPEND;
finish:
sdei_map_unlock(map);
return ret;
}
/* Query status of an SDEI event */
static int sdei_event_status(int ev_num)
{
sdei_ev_map_t *map;
sdei_entry_t *se;
sdei_state_t state;
/* Check if valid event number */
map = find_event_map(ev_num);
if (map == NULL)
return SDEI_EINVAL;
se = get_event_entry(map);
if (is_event_shared(map))
sdei_map_lock(map);
/* State value directly maps to the expected return format */
state = se->state;
if (is_event_shared(map))
sdei_map_unlock(map);
return (int) state;
}
/* Bind an SDEI event to an interrupt */
static int sdei_interrupt_bind(unsigned int intr_num)
{
sdei_ev_map_t *map;
bool retry = true, shared_mapping;
/* SGIs are not allowed to be bound */
if (plat_ic_is_sgi(intr_num) != 0)
return SDEI_EINVAL;
shared_mapping = (plat_ic_is_spi(intr_num) != 0);
do {
/*
* Bail out if there is already an event for this interrupt,
* either platform-defined or dynamic.
*/
map = find_event_map_by_intr(intr_num, shared_mapping);
if (map != NULL) {
if (is_map_dynamic(map)) {
if (is_map_bound(map)) {
/*
* Dynamic event, already bound. Return
* event number.
*/
return map->ev_num;
}
} else {
/* Binding non-dynamic event */
return SDEI_EINVAL;
}
}
/*
* The interrupt is not bound yet. Try to find a free slot to
* bind it. Free dynamic mappings have their interrupt set as
* SDEI_DYN_IRQ.
*/
map = find_event_map_by_intr(SDEI_DYN_IRQ, shared_mapping);
if (map == NULL)
return SDEI_ENOMEM;
/* The returned mapping must be dynamic */
assert(is_map_dynamic(map));
/*
* We cannot assert for bound maps here, as we might be racing
* with another bind.
*/
/* The requested interrupt must already belong to NS */
if (plat_ic_get_interrupt_type(intr_num) != INTR_TYPE_NS)
return SDEI_EDENY;
/*
* Interrupt programming and ownership transfer are deferred
* until register.
*/
sdei_map_lock(map);
if (!is_map_bound(map)) {
map->intr = intr_num;
set_map_bound(map);
retry = false;
}
sdei_map_unlock(map);
} while (retry);
return map->ev_num;
}
/* Release a bound SDEI event previously to an interrupt */
static int sdei_interrupt_release(int ev_num)
{
int ret = 0;
sdei_ev_map_t *map;
sdei_entry_t *se;
/* Check if valid event number */
map = find_event_map(ev_num);
if (map == NULL)
return SDEI_EINVAL;
if (!is_map_dynamic(map))
return SDEI_EINVAL;
se = get_event_entry(map);
sdei_map_lock(map);
/* Event must have been unregistered before release */
if (map->reg_count != 0) {
ret = SDEI_EDENY;
goto finish;
}
/*
* Interrupt release never causes the state to change. We only check
* whether it's permissible or not.
*/
if (!can_sdei_state_trans(se, DO_RELEASE)) {
ret = SDEI_EDENY;
goto finish;
}
if (is_map_bound(map)) {
/*
* Deny release if the interrupt is active, which means it's
* probably being acknowledged and handled elsewhere.
*/
if (plat_ic_get_interrupt_active(map->intr) != 0U) {
ret = SDEI_EDENY;
goto finish;
}
/*
* Interrupt programming and ownership transfer are already done
* during unregister.
*/
map->intr = SDEI_DYN_IRQ;
clr_map_bound(map);
} else {
SDEI_LOG("Error release bound:%d cnt:%d\n", is_map_bound(map),
map->reg_count);
ret = SDEI_EINVAL;
}
finish:
sdei_map_unlock(map);
return ret;
}
/* Perform reset of private SDEI events */
static int sdei_private_reset(void)
{
sdei_ev_map_t *map;
int ret = 0, final_ret = 0;
unsigned int i;
/* Unregister all private events */
for_each_private_map(i, map) {
/*
* The unregister can fail if the event is not registered, which
* is allowed, and a deny will be returned. But if the event is
* running or unregister pending, the call fails.
*/
ret = sdei_event_unregister(map->ev_num);
if ((ret == SDEI_EPEND) && (final_ret == 0))
final_ret = SDEI_EDENY;
}
return final_ret;
}
/* Perform reset of shared SDEI events */
static int sdei_shared_reset(void)
{
const sdei_mapping_t *mapping;
sdei_ev_map_t *map;
int ret = 0, final_ret = 0;
unsigned int i, j;
/* Unregister all shared events */
for_each_shared_map(i, map) {
/*
* The unregister can fail if the event is not registered, which
* is allowed, and a deny will be returned. But if the event is
* running or unregister pending, the call fails.
*/
ret = sdei_event_unregister(map->ev_num);
if ((ret == SDEI_EPEND) && (final_ret == 0))
final_ret = SDEI_EDENY;
}
if (final_ret != 0)
return final_ret;
/*
* Loop through both private and shared mappings, and release all
* bindings.
*/
for_each_mapping_type(i, mapping) {
iterate_mapping(mapping, j, map) {
/*
* Release bindings for mappings that are dynamic and
* bound.
*/
if (is_map_dynamic(map) && is_map_bound(map)) {
/*
* Any failure to release would mean there is at
* least a PE registered for the event.
*/
ret = sdei_interrupt_release(map->ev_num);
if ((ret != 0) && (final_ret == 0))
final_ret = ret;
}
}
}
return final_ret;
}
/* Send a signal to another SDEI client PE */
static int sdei_signal(int ev_num, uint64_t target_pe)
{
sdei_ev_map_t *map;
/* Only event 0 can be signalled */
if (ev_num != SDEI_EVENT_0)
return SDEI_EINVAL;
/* Find mapping for event 0 */
map = find_event_map(SDEI_EVENT_0);
if (map == NULL)
return SDEI_EINVAL;
/* The event must be signalable */
if (!is_event_signalable(map))
return SDEI_EINVAL;
/* Validate target */
if (plat_core_pos_by_mpidr(target_pe) < 0)
return SDEI_EINVAL;
/* Raise SGI. Platform will validate target_pe */
plat_ic_raise_el3_sgi((int) map->intr, (u_register_t) target_pe);
return 0;
}
/* Query SDEI dispatcher features */
static uint64_t sdei_features(unsigned int feature)
{
if (feature == SDEI_FEATURE_BIND_SLOTS) {
return FEATURE_BIND_SLOTS(num_dyn_priv_slots,
num_dyn_shrd_slots);
}
return (uint64_t) SDEI_EINVAL;
}
/* SDEI top level handler for servicing SMCs */
uint64_t sdei_smc_handler(uint32_t smc_fid,
uint64_t x1,
uint64_t x2,
uint64_t x3,
uint64_t x4,
void *cookie,
void *handle,
uint64_t flags)
{
uint64_t x5;
unsigned int ss = (unsigned int) get_interrupt_src_ss(flags);
int64_t ret;
bool resume = false;
cpu_context_t *ctx = handle;
int ev_num = (int) x1;
if (ss != NON_SECURE)
SMC_RET1(ctx, SMC_UNK);
/* Verify the caller EL */
if (GET_EL(read_spsr_el3()) != sdei_client_el())
SMC_RET1(ctx, SMC_UNK);
switch (smc_fid) {
case SDEI_VERSION:
SDEI_LOG("> VER\n");
ret = (int64_t) sdei_version();
SDEI_LOG("< VER:%llx\n", ret);
SMC_RET1(ctx, ret);
case SDEI_EVENT_REGISTER:
x5 = SMC_GET_GP(ctx, CTX_GPREG_X5);
SDEI_LOG("> REG(n:%d e:%llx a:%llx f:%x m:%llx)\n", ev_num,
x2, x3, (int) x4, x5);
ret = sdei_event_register(ev_num, x2, x3, x4, x5);
SDEI_LOG("< REG:%lld\n", ret);
SMC_RET1(ctx, ret);
case SDEI_EVENT_ENABLE:
SDEI_LOG("> ENABLE(n:%d)\n", (int) x1);
ret = sdei_event_enable(ev_num);
SDEI_LOG("< ENABLE:%lld\n", ret);
SMC_RET1(ctx, ret);
case SDEI_EVENT_DISABLE:
SDEI_LOG("> DISABLE(n:%d)\n", ev_num);
ret = sdei_event_disable(ev_num);
SDEI_LOG("< DISABLE:%lld\n", ret);
SMC_RET1(ctx, ret);
case SDEI_EVENT_CONTEXT:
SDEI_LOG("> CTX(p:%d):%lx\n", (int) x1, read_mpidr_el1());
ret = sdei_event_context(ctx, (unsigned int) x1);
SDEI_LOG("< CTX:%lld\n", ret);
SMC_RET1(ctx, ret);
case SDEI_EVENT_COMPLETE_AND_RESUME:
resume = true;
/* Fallthrough */
case SDEI_EVENT_COMPLETE:
SDEI_LOG("> COMPLETE(r:%u sta/ep:%llx):%lx\n",
(unsigned int) resume, x1, read_mpidr_el1());
ret = sdei_event_complete(resume, x1);
SDEI_LOG("< COMPLETE:%llx\n", ret);
/*
* Set error code only if the call failed. If the call
* succeeded, we discard the dispatched context, and restore the
* interrupted context to a pristine condition, and therefore
* shouldn't be modified. We don't return to the caller in this
* case anyway.
*/
if (ret != 0)
SMC_RET1(ctx, ret);
SMC_RET0(ctx);
case SDEI_EVENT_STATUS:
SDEI_LOG("> STAT(n:%d)\n", ev_num);
ret = sdei_event_status(ev_num);
SDEI_LOG("< STAT:%lld\n", ret);
SMC_RET1(ctx, ret);
case SDEI_EVENT_GET_INFO:
SDEI_LOG("> INFO(n:%d, %d)\n", ev_num, (int) x2);
ret = sdei_event_get_info(ev_num, (int) x2);
SDEI_LOG("< INFO:%lld\n", ret);
SMC_RET1(ctx, ret);
case SDEI_EVENT_UNREGISTER:
SDEI_LOG("> UNREG(n:%d)\n", ev_num);
ret = sdei_event_unregister(ev_num);
SDEI_LOG("< UNREG:%lld\n", ret);
SMC_RET1(ctx, ret);
case SDEI_PE_UNMASK:
SDEI_LOG("> UNMASK:%lx\n", read_mpidr_el1());
sdei_pe_unmask();
SDEI_LOG("< UNMASK:%d\n", 0);
SMC_RET1(ctx, 0);
case SDEI_PE_MASK:
SDEI_LOG("> MASK:%lx\n", read_mpidr_el1());
ret = sdei_pe_mask();
SDEI_LOG("< MASK:%lld\n", ret);
SMC_RET1(ctx, ret);
case SDEI_INTERRUPT_BIND:
SDEI_LOG("> BIND(%d)\n", (int) x1);
ret = sdei_interrupt_bind((unsigned int) x1);
SDEI_LOG("< BIND:%lld\n", ret);
SMC_RET1(ctx, ret);
case SDEI_INTERRUPT_RELEASE:
SDEI_LOG("> REL(%d)\n", ev_num);
ret = sdei_interrupt_release(ev_num);
SDEI_LOG("< REL:%lld\n", ret);
SMC_RET1(ctx, ret);
case SDEI_SHARED_RESET:
SDEI_LOG("> S_RESET():%lx\n", read_mpidr_el1());
ret = sdei_shared_reset();
SDEI_LOG("< S_RESET:%lld\n", ret);
SMC_RET1(ctx, ret);
case SDEI_PRIVATE_RESET:
SDEI_LOG("> P_RESET():%lx\n", read_mpidr_el1());
ret = sdei_private_reset();
SDEI_LOG("< P_RESET:%lld\n", ret);
SMC_RET1(ctx, ret);
case SDEI_EVENT_ROUTING_SET:
SDEI_LOG("> ROUTE_SET(n:%d f:%llx aff:%llx)\n", ev_num, x2, x3);
ret = sdei_event_routing_set(ev_num, x2, x3);
SDEI_LOG("< ROUTE_SET:%lld\n", ret);
SMC_RET1(ctx, ret);
case SDEI_FEATURES:
SDEI_LOG("> FTRS(f:%llx)\n", x1);
ret = (int64_t) sdei_features((unsigned int) x1);
SDEI_LOG("< FTRS:%llx\n", ret);
SMC_RET1(ctx, ret);
case SDEI_EVENT_SIGNAL:
SDEI_LOG("> SIGNAL(e:%d t:%llx)\n", ev_num, x2);
ret = sdei_signal(ev_num, x2);
SDEI_LOG("< SIGNAL:%lld\n", ret);
SMC_RET1(ctx, ret);
default:
/* Do nothing in default case */
break;
}
WARN("Unimplemented SDEI Call: 0x%x\n", smc_fid);
SMC_RET1(ctx, SMC_UNK);
}
/* Subscribe to PSCI CPU on to initialize per-CPU SDEI configuration */
SUBSCRIBE_TO_EVENT(psci_cpu_on_finish, sdei_cpu_on_init);
/* Subscribe to PSCI CPU suspend finisher for per-CPU configuration */
SUBSCRIBE_TO_EVENT(psci_suspend_pwrdown_finish, sdei_cpu_wakeup_init);