1 /*
2  * Copyright (c) 2013-2016 Google Inc. All rights reserved
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining
5  * a copy of this software and associated documentation files
6  * (the "Software"), to deal in the Software without restriction,
7  * including without limitation the rights to use, copy, modify, merge,
8  * publish, distribute, sublicense, and/or sell copies of the Software,
9  * and to permit persons to whom the Software is furnished to do so,
10  * subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be
13  * included in all copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22  */
23 
24 /* Reference:
25  * ARM document DEN 0028A: SMC CALLING CONVENTION
26  * version: 0.9.0
27  */
28 
29 #include <arch/ops.h>
30 #include <compiler.h>
31 #include <debug.h>
32 #include <err.h>
33 #include <kernel/mutex.h>
34 #include <lib/sm.h>
35 #include <lib/sm/sm_err.h>
36 #include <lib/sm/smcall.h>
37 #include <lib/sm/trusty_sched_share_api.h>
38 #include <lk/init.h>
39 #include <string.h>
40 #include <trace.h>
41 #include <version.h>
42 
43 #define LOCAL_TRACE 1
44 
45 static mutex_t smc_table_lock = MUTEX_INITIAL_VALUE(smc_table_lock);
46 
47 #define TRACE_SMC(msg, args)                                                \
48     do {                                                                    \
49         u_int _i;                                                           \
50         LTRACEF("%s\n", msg);                                               \
51         LTRACEF("SMC: 0x%x (%s entity %d function 0x%x)\n", (args)->smc_nr, \
52                 SMC_IS_FASTCALL(args->smc_nr) ? "Fastcall" : "Stdcall",     \
53                 SMC_ENTITY(args->smc_nr), SMC_FUNCTION(args->smc_nr));      \
54         for (_i = 0; _i < SMC_NUM_PARAMS; _i++)                             \
55             LTRACEF("param%d: 0x%x\n", _i, (args)->params[_i]);             \
56     } while (0)
57 
smc_undefined(struct smc32_args * args)58 long smc_undefined(struct smc32_args* args) {
59     TRACE_SMC("Undefined monitor call!", args);
60     return SM_ERR_UNDEFINED_SMC;
61 }
62 
63 /* Restarts should never be dispatched like this */
smc_restart_stdcall(struct smc32_args * args)64 static long smc_restart_stdcall(struct smc32_args* args) {
65     TRACE_SMC("Unexpected stdcall restart!", args);
66     return SM_ERR_UNEXPECTED_RESTART;
67 }
68 
69 /*
70  * Switch to secure mode and return. This function does no work on its own,
71  * but if an interrupt is pending, it will be handled, and can in turn trigger a
72  * context switch that will perform other secure work.
73  */
smc_nop_stdcall(struct smc32_args * args)74 static long smc_nop_stdcall(struct smc32_args* args) {
75     return 0;
76 }
77 
78 /*
79  * parameterized nop call handler
80  */
smc_nop_secure_monitor(struct smc32_args * args)81 static long smc_nop_secure_monitor(struct smc32_args* args) {
82     return (!args->params[0]) ? 0 : SM_ERR_UNDEFINED_SMC;
83 }
84 
85 static smc32_handler_t sm_stdcall_function_table[] = {
86         [SMC_FUNCTION(SMC_SC_RESTART_LAST)] = smc_restart_stdcall,
87         [SMC_FUNCTION(SMC_SC_LOCKED_NOP)] = smc_nop_stdcall,
88         [SMC_FUNCTION(SMC_SC_RESTART_FIQ)] = smc_restart_stdcall,
89         [SMC_FUNCTION(SMC_SC_SCHED_SHARE_REGISTER)] =
90                 smc_trusty_sched_share_register,
91         [SMC_FUNCTION(SMC_SC_SCHED_SHARE_UNREGISTER)] =
92                 smc_trusty_sched_share_unregister,
93         /* reserve slot in table, not called */
94         [SMC_FUNCTION(SMC_SC_NOP)] = smc_undefined,
95 };
96 
smc_stdcall_secure_monitor(struct smc32_args * args)97 static long smc_stdcall_secure_monitor(struct smc32_args* args) {
98     u_int function = SMC_FUNCTION(args->smc_nr);
99     smc32_handler_t handler_fn = NULL;
100 
101     if (function < countof(sm_stdcall_function_table))
102         handler_fn = sm_stdcall_function_table[function];
103 
104     if (!handler_fn)
105         handler_fn = smc_undefined;
106 
107     return handler_fn(args);
108 }
109 
smc_fiq_enter(struct smc32_args * args)110 static long smc_fiq_enter(struct smc32_args* args) {
111     return sm_intc_fiq_enter();
112 }
113 
114 #if !WITH_LIB_SM_MONITOR
smc_cpu_suspend(struct smc32_args * args)115 static long smc_cpu_suspend(struct smc32_args* args) {
116     lk_init_level_all(args->params[0] ? LK_INIT_FLAG_CPU_OFF
117                                       : LK_INIT_FLAG_CPU_ENTER_IDLE);
118 
119     return 0;
120 }
121 
smc_cpu_resume(struct smc32_args * args)122 static long smc_cpu_resume(struct smc32_args* args) {
123     lk_init_level_all(args->params[0] ? LK_INIT_FLAG_CPU_ON
124                                       : LK_INIT_FLAG_CPU_EXIT_IDLE);
125 
126     return 0;
127 }
128 #endif
129 
smc_get_version_str(struct smc32_args * args)130 static long smc_get_version_str(struct smc32_args* args) {
131     int32_t index = (int32_t)args->params[0];
132     size_t version_len = strlen(lk_version);
133 
134     if (index == -1)
135         return version_len;
136 
137     if (index < 0 || (size_t)index >= version_len)
138         return SM_ERR_INVALID_PARAMETERS;
139 
140     return lk_version[index];
141 }
142 
143 static smc32_handler_t sm_fastcall_function_table[] = {
144         [SMC_FUNCTION(SMC_FC_GET_NEXT_IRQ)] = smc_intc_get_next_irq,
145         [SMC_FUNCTION(SMC_FC_FIQ_ENTER)] = smc_fiq_enter,
146 #if !WITH_LIB_SM_MONITOR
147         [SMC_FUNCTION(SMC_FC_CPU_SUSPEND)] = smc_cpu_suspend,
148         [SMC_FUNCTION(SMC_FC_CPU_RESUME)] = smc_cpu_resume,
149 #endif
150         [SMC_FUNCTION(SMC_FC_GET_VERSION_STR)] = smc_get_version_str,
151         [SMC_FUNCTION(SMC_FC_API_VERSION)] = smc_sm_api_version,
152         [SMC_FUNCTION(SMC_FC_GET_SMP_MAX_CPUS)] = smc_get_smp_max_cpus,
153 };
154 
smc_fastcall_secure_monitor(struct smc32_args * args)155 static long smc_fastcall_secure_monitor(struct smc32_args* args) {
156     smc32_handler_t func = NULL;
157     uint16_t index = SMC_FUNCTION(args->smc_nr);
158 
159     if (index < countof(sm_fastcall_function_table)) {
160         func = sm_fastcall_function_table[index];
161     }
162 
163     if (func == NULL) {
164         func = smc_undefined;
165     }
166 
167     return func(args);
168 }
169 
170 /* SMC dispatch tables */
171 smc32_handler_t sm_fastcall_table[SMC_NUM_ENTITIES] = {
172         [0 ... SMC_ENTITY_SECURE_MONITOR - 1] = smc_undefined,
173         [SMC_ENTITY_SECURE_MONITOR] = smc_fastcall_secure_monitor,
174         [SMC_ENTITY_SECURE_MONITOR + 1 ... SMC_NUM_ENTITIES - 1] =
175                 smc_undefined};
176 
177 smc32_handler_t sm_nopcall_table[SMC_NUM_ENTITIES] = {
178         [0] = smc_nop_secure_monitor,
179         [1 ... SMC_NUM_ENTITIES - 1] = smc_undefined};
180 
181 smc32_handler_t sm_stdcall_table[SMC_NUM_ENTITIES] = {
182         [0 ... SMC_ENTITY_SECURE_MONITOR - 1] = smc_undefined,
183         [SMC_ENTITY_SECURE_MONITOR] = smc_stdcall_secure_monitor,
184         [SMC_ENTITY_SECURE_MONITOR + 1 ... SMC_NUM_ENTITIES - 1] =
185                 smc_undefined};
186 
sm_register_entity(uint entity_nr,struct smc32_entity * entity)187 status_t sm_register_entity(uint entity_nr, struct smc32_entity* entity) {
188     status_t err = NO_ERROR;
189 
190     if (entity_nr >= SMC_NUM_ENTITIES)
191         return ERR_INVALID_ARGS;
192 
193     if (entity_nr >= SMC_ENTITY_RESERVED && entity_nr < SMC_ENTITY_TRUSTED_APP)
194         return ERR_NOT_ALLOWED;
195 
196     if (!entity)
197         return ERR_INVALID_ARGS;
198 
199     if (!entity->fastcall_handler && !entity->stdcall_handler &&
200         !entity->nopcall_handler)
201         return ERR_NOT_VALID;
202 
203     mutex_acquire(&smc_table_lock);
204 
205     /* Check if entity is already claimed */
206     if (sm_fastcall_table[entity_nr] != smc_undefined ||
207         sm_nopcall_table[entity_nr] != smc_undefined ||
208         sm_stdcall_table[entity_nr] != smc_undefined) {
209         err = ERR_ALREADY_EXISTS;
210         goto unlock;
211     }
212 
213     if (entity->fastcall_handler)
214         sm_fastcall_table[entity_nr] = entity->fastcall_handler;
215 
216     if (entity->nopcall_handler)
217         sm_nopcall_table[entity_nr] = entity->nopcall_handler;
218 
219     if (entity->stdcall_handler)
220         sm_stdcall_table[entity_nr] = entity->stdcall_handler;
221 unlock:
222     mutex_release(&smc_table_lock);
223     return err;
224 }
225