Commit 7421b465 authored by Achin Gupta's avatar Achin Gupta Committed by Dan Handley
Browse files

Add runtime services framework

This patch introduces the framework to enable registration and
initialisation of runtime services. PSCI is registered and initialised
as a runtime service. Handling of runtime service requests will be
implemented in subsequent patches.

Change-Id: Id21e7ddc5a33d42b7d6e455b41155fc5441a9547
Showing with 276 additions and 16 deletions
+276 -16
......@@ -36,8 +36,11 @@ else
KBUILD_VERBOSE = 0
endif
CHECKPATCH_ARGS = --no-tree --no-signoff
CHECKCODE_ARGS = --no-patch --no-tree --no-signoff
# Checkpatch ignores
CHECK_IGNORE = --ignore COMPLEX_MACRO
CHECKPATCH_ARGS = --no-tree --no-signoff ${CHECK_IGNORE}
CHECKCODE_ARGS = --no-patch --no-tree --no-signoff ${CHECK_IGNORE}
ifeq "${KBUILD_VERBOSE}" "0"
Q=@
......
......@@ -50,6 +50,13 @@ SECTIONS
*bl31_entrypoint.o(.text)
*(.text)
*(.rodata*)
/* Ensure 8-byte alignment for descriptors */
. = ALIGN(8);
__RT_SVC_DESCS_START__ = .;
*(rt_svc_descs)
__RT_SVC_DESCS_END__ = .;
*(.vectors)
__RO_END_UNALIGNED__ = .;
/*
......
......@@ -57,7 +57,6 @@ void bl31_arch_next_el_setup(void);
void bl31_main(void)
{
el_change_info *image_info;
unsigned long mpidr = read_mpidr();
/* Perform remaining generic architectural setup from EL3 */
bl31_arch_setup();
......@@ -72,7 +71,7 @@ void bl31_main(void)
bl31_lib_init();
/* Initialize the runtime services e.g. psci */
runtime_svc_init(mpidr);
runtime_svc_init();
/* Clean caches before re-entering normal world */
dcsw_op_all(DCCSW);
......
......@@ -36,6 +36,7 @@
#include <platform.h>
#include <psci_private.h>
#include <context_mgmt.h>
#include <runtime_svc.h>
/*******************************************************************************
* Per cpu non-secure contexts used to program the architectural state prior
......@@ -275,8 +276,9 @@ static unsigned int psci_init_aff_map(unsigned long mpidr,
* level within the 'psci_aff_map' array. This allows restricting search of a
* node at an affinity level between the indices in the limits array.
******************************************************************************/
void psci_setup(unsigned long mpidr)
int32_t psci_setup(void)
{
unsigned long mpidr = read_mpidr();
int afflvl, affmap_idx, max_afflvl;
aff_map_node *node;
......@@ -335,5 +337,16 @@ void psci_setup(unsigned long mpidr)
platform_setup_pm(&psci_plat_pm_ops);
assert(psci_plat_pm_ops);
return;
return 0;
}
/* Register PSCI as a run time service */
DECLARE_RT_SVC(
psci,
OEN_STD_START,
OEN_STD_END,
SMC_TYPE_FAST,
psci_setup,
NULL
);
......@@ -39,13 +39,106 @@
#include <bl_common.h>
#include <psci.h>
#include <runtime_svc.h>
#include <debug.h>
/*******************************************************************************
* Perform initialization of runtime services possibly across exception levels
* in the secure address space e.g. psci & interrupt handling.
* The 'rt_svc_descs' array holds the runtime service descriptors exported by
* services by placing them in the 'rt_svc_descs' linker section.
* The 'rt_svc_descs_indices' array holds the index of a descriptor in the
* 'rt_svc_descs' array. When an SMC arrives, the OEN[29:24] bits and the call
* type[31] bit in the function id are combined to get an index into the
* 'rt_svc_descs_indices' array. This gives the index of the descriptor in the
* 'rt_svc_descs' array which contains the SMC handler.
******************************************************************************/
void runtime_svc_init(unsigned long mpidr)
#define RT_SVC_DESCS_START ((uint64_t) (&__RT_SVC_DESCS_START__))
#define RT_SVC_DESCS_END ((uint64_t) (&__RT_SVC_DESCS_END__))
uint8_t rt_svc_descs_indices[MAX_RT_SVCS];
static rt_svc_desc *rt_svc_descs;
/*******************************************************************************
* Simple routine to sanity check a runtime service descriptor before using it
******************************************************************************/
static int32_t validate_rt_svc_desc(rt_svc_desc *desc)
{
if (desc == NULL)
return -EINVAL;
if (desc->start_oen > desc->end_oen)
return -EINVAL;
if (desc->end_oen >= OEN_LIMIT)
return -EINVAL;
if (desc->call_type != SMC_TYPE_FAST && desc->call_type != SMC_TYPE_STD)
return -EINVAL;
/* A runtime service having no init or handle function doesn't make sense */
if (desc->init == NULL && desc->handle == NULL)
return -EINVAL;
return 0;
}
/*******************************************************************************
* This function calls the initialisation routine in the descriptor exported by
* a runtime service. Once a descriptor has been validated, its start & end
* owning entity numbers and the call type are combined to form a unique oen.
* The unique oen is used as an index into the 'rt_svc_descs_indices' array.
* The index of the runtime service descriptor is stored at this index.
******************************************************************************/
void runtime_svc_init()
{
psci_setup(mpidr);
int32_t rc = 0;
uint32_t index, start_idx, end_idx;
uint64_t rt_svc_descs_num;
/* If no runtime services are implemented then simply bail out */
rt_svc_descs_num = RT_SVC_DESCS_END - RT_SVC_DESCS_START;
rt_svc_descs_num /= sizeof(rt_svc_desc);
if (rt_svc_descs_num == 0)
return;
/* Initialise internal variables to invalid state */
memset(rt_svc_descs_indices, -1, sizeof(rt_svc_descs_indices));
rt_svc_descs = (rt_svc_desc *) RT_SVC_DESCS_START;
for (index = 0; index < rt_svc_descs_num; index++) {
/*
* An invalid descriptor is an error condition since it is
* difficult to predict the system behaviour in the absence
* of this service.
*/
rc = validate_rt_svc_desc(&rt_svc_descs[index]);
if (rc) {
ERROR("Invalid runtime service descriptor 0x%x (%s)\n",
&rt_svc_descs[index],
rt_svc_descs[index].name);
goto error;
}
/*
* Fill the indices corresponding to the start and end owning
* entity numbers with the index of the descriptor which will
* handle the SMCs for this owning entity range.
*/
start_idx = get_unique_oen(rt_svc_descs[index].start_oen,
rt_svc_descs[index].call_type);
end_idx = get_unique_oen(rt_svc_descs[index].end_oen,
rt_svc_descs[index].call_type);
for (; start_idx <= end_idx; start_idx++)
rt_svc_descs_indices[start_idx] = index;
/* Call the initialisation routine for this runtime service */
rc = rt_svc_descs[index].init();
if (rc) {
ERROR("Error initializing runtime service %s\n",
rt_svc_descs[index].name);
goto error;
}
}
return;
error:
panic();
}
......@@ -156,7 +156,6 @@ extern int psci_cpu_on(unsigned long,
unsigned long);
extern void psci_aff_on_finish_entry(void);
extern void psci_aff_suspend_finish_entry(void);
extern void psci_setup(unsigned long);
#endif /*__ASSEMBLY__*/
......
......@@ -38,20 +38,47 @@
******************************************************************************/
#define FUNCID_TYPE_SHIFT 31
#define FUNCID_CC_SHIFT 30
#define FUNCID_OWNER_SHIFT 24
#define FUNCID_OEN_SHIFT 24
#define FUNCID_NUM_SHIFT 0
#define FUNCID_TYPE_MASK 0x1
#define FUNCID_CC_MASK 0x1
#define FUNCID_OWNER_MASK 0x3f
#define FUNCID_OEN_MASK 0x3f
#define FUNCID_NUM_MASK 0xffff
#define FUNCID_TYPE_WIDTH 1
#define FUNCID_CC_WIDTH 1
#define FUNCID_OEN_WIDTH 6
#define FUNCID_NUM_WIDTH 16
#define GET_SMC_CC(id) ((id >> FUNCID_CC_SHIFT) & \
FUNCID_CC_MASK)
#define SMC_64 1
#define SMC_32 0
#define SMC_UNK 0xffffffff
#define SMC_TYPE_FAST 1
#define SMC_TYPE_STD 0
/*******************************************************************************
* Owning entity number definitions inside the function id as per the SMC
* calling convention
******************************************************************************/
#define OEN_ARM_START 0
#define OEN_ARM_END 0
#define OEN_CPU_START 1
#define OEN_CPU_END 1
#define OEN_SIP_START 2
#define OEN_SIP_END 2
#define OEN_OEM_START 3
#define OEN_OEM_END 3
#define OEN_STD_START 4 /* Standard Calls */
#define OEN_STD_END 4
#define OEN_TAP_START 48 /* Trusted Applications */
#define OEN_TAP_END 49
#define OEN_TOS_START 50 /* Trusted OS */
#define OEN_TOS_END 63
#define OEN_LIMIT 64
/*******************************************************************************
* Constants to indicate type of exception to the common exception handler.
......@@ -112,8 +139,77 @@
#define GPREGS_FP_OFF 0x100
#define GPREGS_LR_OFF 0x108
/*
* Constants to allow the assembler access a runtime service
* descriptor
*/
#define SIZEOF_RT_SVC_DESC 32
#define RT_SVC_DESC_INIT 16
#define RT_SVC_DESC_HANDLE 24
/*
* The function identifier has 6 bits for the owning entity number and
* single bit for the type of smc call. When taken together these
* values limit the maximum number of runtime services to 128.
*/
#define MAX_RT_SVCS 128
#ifndef __ASSEMBLY__
/* Prototype for runtime service initializing function */
typedef int32_t (*rt_svc_init)(void);
/* Convenience macros to return from SMC handler */
#define SMC_RET1(_h, _x0) { \
write_ctx_reg(get_gpregs_ctx(_h), CTX_GPREG_X0, (_x0)); \
return _x0; \
}
#define SMC_RET2(_h, _x0, _x1) { \
write_ctx_reg(get_gpregs_ctx(_h), CTX_GPREG_X1, (_x1)); \
SMC_RET1(_h, (_x0)); \
}
#define SMC_RET3(_h, _x0, _x1, _x2) { \
write_ctx_reg(get_gpregs_ctx(_h), CTX_GPREG_X2, (_x2)); \
SMC_RET2(_h, (_x0), (_x1)); \
}
#define SMC_RET4(_h, _x0, _x1, _x2, _x3) { \
write_ctx_reg(get_gpregs_ctx(_h), CTX_GPREG_X3, (_x3)); \
SMC_RET3(_h, (_x0), (_x1), (_x2)); \
}
/*
* Convenience macros to access general purpose registers using handle provided
* to SMC handler. These takes the offset values defined in context.h
*/
#define SMC_GET_GP(_h, _g) \
read_ctx_reg(get_gpregs_ctx(_h), (_g));
#define SMC_SET_GP(_h, _g, _v) \
write_ctx_reg(get_gpregs_ctx(_h), (_g), (_v));
/*
* Convenience macros to access EL3 context registers using handle provided to
* SMC handler. These takes the offset values defined in context.h
*/
#define SMC_GET_EL3(_h, _e) \
read_ctx_reg(get_el3state_ctx(_h), (_e));
#define SMC_SET_EL3(_h, _e, _v) \
write_ctx_reg(get_el3state_ctx(_h), (_e), (_v));
/*
* Prototype for runtime service SMC handler function. x0 (SMC Function ID) to
* x4 are as passed by the caller. Rest of the arguments to SMC and the context
* can be accessed using the handle pointer. The cookie parameter is reserved
* for future use
*/
typedef uint64_t (*rt_svc_handle)(uint32_t smc_fid,
uint64_t x1,
uint64_t x2,
uint64_t x3,
uint64_t x4,
void *cookie,
void *handle,
uint64_t flags);
typedef struct {
uint64_t x0;
uint64_t x1;
......@@ -150,9 +246,31 @@ typedef struct {
* Alignment constraint which allows save & restore of fp & lr on the
* stack during exception handling
*/
uint64_t fp __attribute__((__aligned__(16)));
uint64_t fp __aligned(16);
uint64_t lr;
} __attribute__((__aligned__(16))) gp_regs;
} __aligned(16) gp_regs;
typedef struct {
uint8_t start_oen;
uint8_t end_oen;
uint8_t call_type;
const char *name;
rt_svc_init init;
rt_svc_handle handle;
} rt_svc_desc;
/*
* Convenience macro to declare a service descriptor
*/
#define DECLARE_RT_SVC(_name, _start, _end, _type, _setup, _smch) \
static const rt_svc_desc __svc_desc_ ## _name \
__attribute__ ((section("rt_svc_descs"), used)) = { \
_start, \
_end, \
_type, \
#_name, \
_setup, \
_smch }
/*******************************************************************************
* Compile time assertions to ensure that:
......@@ -164,11 +282,39 @@ typedef struct {
CASSERT((sizeof(gp_regs) == SIZEOF_GPREGS), assert_sizeof_gpregs_mismatch);
CASSERT(GPREGS_FP_OFF == __builtin_offsetof(gp_regs, fp), \
assert_gpregs_fp_offset_mismatch);
/*
* Compile time assertions related to the 'rt_svc_desc' structure to:
* 1. ensure that the assembler and the compiler view of the size
* of the structure are the same.
* 2. ensure that the assembler and the compiler see the initialisation
* routine at the same offset.
* 2. ensure that the assembler and the compiler see the handler
* routine at the same offset.
*/
CASSERT((sizeof(rt_svc_desc) == SIZEOF_RT_SVC_DESC), \
assert_sizeof_rt_svc_desc_mismatch);
CASSERT(RT_SVC_DESC_INIT == __builtin_offsetof(rt_svc_desc, init), \
assert_rt_svc_desc_init_offset_mismatch);
CASSERT(RT_SVC_DESC_HANDLE == __builtin_offsetof(rt_svc_desc, handle), \
assert_rt_svc_desc_handle_offset_mismatch);
/*
* This macro combines the call type and the owning entity number corresponding
* to a runtime service to generate a unique owning entity number. This unique
* oen is used to access an entry in the 'rt_svc_descs_indices' array. The entry
* contains the index of the service descriptor in the 'rt_svc_descs' array.
*/
#define get_unique_oen(oen, call_type) ((oen & FUNCID_OEN_MASK) | \
((call_type & FUNCID_TYPE_MASK) \
<< FUNCID_OEN_WIDTH))
/*******************************************************************************
* Function & variable prototypes
******************************************************************************/
extern void runtime_svc_init(unsigned long mpidr);
extern void runtime_svc_init();
extern uint64_t __RT_SVC_DESCS_START__;
extern uint64_t __RT_SVC_DESCS_END__;
#endif /*__ASSEMBLY__*/
#endif /* __RUNTIME_SVC_H__ */
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment