| /* |
| * Copyright (c) 2013-2016 Google Inc. All rights reserved |
| * |
| * Permission is hereby granted, free of charge, to any person obtaining |
| * a copy of this software and associated documentation files |
| * (the "Software"), to deal in the Software without restriction, |
| * including without limitation the rights to use, copy, modify, merge, |
| * publish, distribute, sublicense, and/or sell copies of the Software, |
| * and to permit persons to whom the Software is furnished to do so, |
| * subject to the following conditions: |
| * |
| * The above copyright notice and this permission notice shall be |
| * included in all copies or substantial portions of the Software. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
| * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
| * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. |
| * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY |
| * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, |
| * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE |
| * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
| */ |
| |
| /* Reference: |
| * ARM document DEN 0028A: SMC CALLING CONVENTION |
| * version: 0.9.0 |
| */ |
| |
| #include <arch/ops.h> |
| #include <compiler.h> |
| #include <debug.h> |
| #include <err.h> |
| #include <kernel/mutex.h> |
| #include <lib/sm.h> |
| #include <lib/sm/sm_err.h> |
| #include <lib/sm/smcall.h> |
| #include <lib/sm/trusty_sched_share_api.h> |
| #include <lk/init.h> |
| #include <string.h> |
| #include <trace.h> |
| #include <version.h> |
| |
| #define LOCAL_TRACE 1 |
| |
| static mutex_t smc_table_lock = MUTEX_INITIAL_VALUE(smc_table_lock); |
| |
| #define TRACE_SMC(msg, args) \ |
| do { \ |
| u_int _i; \ |
| LTRACEF("%s\n", msg); \ |
| LTRACEF("SMC: 0x%x (%s entity %d function 0x%x)\n", (args)->smc_nr, \ |
| SMC_IS_FASTCALL(args->smc_nr) ? "Fastcall" : "Stdcall", \ |
| SMC_ENTITY(args->smc_nr), SMC_FUNCTION(args->smc_nr)); \ |
| for (_i = 0; _i < SMC_NUM_PARAMS; _i++) \ |
| LTRACEF("param%d: 0x%x\n", _i, (args)->params[_i]); \ |
| } while (0) |
| |
| long smc_undefined(struct smc32_args* args) { |
| TRACE_SMC("Undefined monitor call!", args); |
| return SM_ERR_UNDEFINED_SMC; |
| } |
| |
| /* Restarts should never be dispatched like this */ |
| static long smc_restart_stdcall(struct smc32_args* args) { |
| TRACE_SMC("Unexpected stdcall restart!", args); |
| return SM_ERR_UNEXPECTED_RESTART; |
| } |
| |
| /* |
| * Switch to secure mode and return. This function does no work on its own, |
| * but if an interrupt is pending, it will be handled, and can in turn trigger a |
| * context switch that will perform other secure work. |
| */ |
| static long smc_nop_stdcall(struct smc32_args* args) { |
| return 0; |
| } |
| |
| /* |
| * parameterized nop call handler |
| */ |
| static long smc_nop_secure_monitor(struct smc32_args* args) { |
| return (!args->params[0]) ? 0 : SM_ERR_UNDEFINED_SMC; |
| } |
| |
| static smc32_handler_t sm_stdcall_function_table[] = { |
| [SMC_FUNCTION(SMC_SC_RESTART_LAST)] = smc_restart_stdcall, |
| [SMC_FUNCTION(SMC_SC_LOCKED_NOP)] = smc_nop_stdcall, |
| [SMC_FUNCTION(SMC_SC_RESTART_FIQ)] = smc_restart_stdcall, |
| [SMC_FUNCTION(SMC_SC_SCHED_SHARE_REGISTER)] = |
| smc_trusty_sched_share_register, |
| [SMC_FUNCTION(SMC_SC_SCHED_SHARE_UNREGISTER)] = |
| smc_trusty_sched_share_unregister, |
| /* reserve slot in table, not called */ |
| [SMC_FUNCTION(SMC_SC_NOP)] = smc_undefined, |
| }; |
| |
| static long smc_stdcall_secure_monitor(struct smc32_args* args) { |
| u_int function = SMC_FUNCTION(args->smc_nr); |
| smc32_handler_t handler_fn = NULL; |
| |
| if (function < countof(sm_stdcall_function_table)) |
| handler_fn = sm_stdcall_function_table[function]; |
| |
| if (!handler_fn) |
| handler_fn = smc_undefined; |
| |
| return handler_fn(args); |
| } |
| |
| static long smc_fiq_enter(struct smc32_args* args) { |
| return sm_intc_fiq_enter(); |
| } |
| |
| #if !WITH_LIB_SM_MONITOR |
| static long smc_cpu_suspend(struct smc32_args* args) { |
| lk_init_level_all(args->params[0] ? LK_INIT_FLAG_CPU_OFF |
| : LK_INIT_FLAG_CPU_ENTER_IDLE); |
| |
| return 0; |
| } |
| |
| static long smc_cpu_resume(struct smc32_args* args) { |
| lk_init_level_all(args->params[0] ? LK_INIT_FLAG_CPU_ON |
| : LK_INIT_FLAG_CPU_EXIT_IDLE); |
| |
| return 0; |
| } |
| #endif |
| |
| static long smc_get_version_str(struct smc32_args* args) { |
| int32_t index = (int32_t)args->params[0]; |
| size_t version_len = strlen(lk_version); |
| |
| if (index == -1) |
| return version_len; |
| |
| if (index < 0 || (size_t)index >= version_len) |
| return SM_ERR_INVALID_PARAMETERS; |
| |
| return lk_version[index]; |
| } |
| |
| static smc32_handler_t sm_fastcall_function_table[] = { |
| [SMC_FUNCTION(SMC_FC_GET_NEXT_IRQ)] = smc_intc_get_next_irq, |
| [SMC_FUNCTION(SMC_FC_FIQ_ENTER)] = smc_fiq_enter, |
| #if !WITH_LIB_SM_MONITOR |
| [SMC_FUNCTION(SMC_FC_CPU_SUSPEND)] = smc_cpu_suspend, |
| [SMC_FUNCTION(SMC_FC_CPU_RESUME)] = smc_cpu_resume, |
| #endif |
| [SMC_FUNCTION(SMC_FC_GET_VERSION_STR)] = smc_get_version_str, |
| [SMC_FUNCTION(SMC_FC_API_VERSION)] = smc_sm_api_version, |
| [SMC_FUNCTION(SMC_FC_GET_SMP_MAX_CPUS)] = smc_get_smp_max_cpus, |
| }; |
| |
| static long smc_fastcall_secure_monitor(struct smc32_args* args) { |
| smc32_handler_t func = NULL; |
| uint16_t index = SMC_FUNCTION(args->smc_nr); |
| |
| if (index < countof(sm_fastcall_function_table)) { |
| func = sm_fastcall_function_table[index]; |
| } |
| |
| if (func == NULL) { |
| func = smc_undefined; |
| } |
| |
| return func(args); |
| } |
| |
| /* SMC dispatch tables */ |
| smc32_handler_t sm_fastcall_table[SMC_NUM_ENTITIES] = { |
| [0 ... SMC_ENTITY_SECURE_MONITOR - 1] = smc_undefined, |
| [SMC_ENTITY_SECURE_MONITOR] = smc_fastcall_secure_monitor, |
| [SMC_ENTITY_SECURE_MONITOR + 1 ... SMC_NUM_ENTITIES - 1] = |
| smc_undefined}; |
| |
| smc32_handler_t sm_nopcall_table[SMC_NUM_ENTITIES] = { |
| [0] = smc_nop_secure_monitor, |
| [1 ... SMC_NUM_ENTITIES - 1] = smc_undefined}; |
| |
| smc32_handler_t sm_stdcall_table[SMC_NUM_ENTITIES] = { |
| [0 ... SMC_ENTITY_SECURE_MONITOR - 1] = smc_undefined, |
| [SMC_ENTITY_SECURE_MONITOR] = smc_stdcall_secure_monitor, |
| [SMC_ENTITY_SECURE_MONITOR + 1 ... SMC_NUM_ENTITIES - 1] = |
| smc_undefined}; |
| |
| status_t sm_register_entity(uint entity_nr, struct smc32_entity* entity) { |
| status_t err = NO_ERROR; |
| |
| if (entity_nr >= SMC_NUM_ENTITIES) |
| return ERR_INVALID_ARGS; |
| |
| if (entity_nr >= SMC_ENTITY_RESERVED && entity_nr < SMC_ENTITY_TRUSTED_APP) |
| return ERR_NOT_ALLOWED; |
| |
| if (!entity) |
| return ERR_INVALID_ARGS; |
| |
| if (!entity->fastcall_handler && !entity->stdcall_handler && |
| !entity->nopcall_handler) |
| return ERR_NOT_VALID; |
| |
| mutex_acquire(&smc_table_lock); |
| |
| /* Check if entity is already claimed */ |
| if (sm_fastcall_table[entity_nr] != smc_undefined || |
| sm_nopcall_table[entity_nr] != smc_undefined || |
| sm_stdcall_table[entity_nr] != smc_undefined) { |
| err = ERR_ALREADY_EXISTS; |
| goto unlock; |
| } |
| |
| if (entity->fastcall_handler) |
| sm_fastcall_table[entity_nr] = entity->fastcall_handler; |
| |
| if (entity->nopcall_handler) |
| sm_nopcall_table[entity_nr] = entity->nopcall_handler; |
| |
| if (entity->stdcall_handler) |
| sm_stdcall_table[entity_nr] = entity->stdcall_handler; |
| unlock: |
| mutex_release(&smc_table_lock); |
| return err; |
| } |