1 /*
2  * Copyright (c) 2022, 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 /*
25  * This trusty library module contains the SMC API for communication with
26  * the Linux trusty-driver for shared memory registration/unregistration.
27  * After a register request (SMC-Call) is received from the trusty-driver,
28  * access to the shared-memory block, created by the trusty-driver, is
29  * established. Likewise, when an unregister request is received, the
30  * associated resources are release and the access is removed.
31  *
32  * This library module also provides the APIs to the trusty kernel for
33  * exchanging various information in both directions. One such information
34  * exchanged is the per-cpu trusty shadow-priorities.
35  */
36 
37 #include <err.h>
38 #include <kernel/spinlock.h>
39 #include <kernel/thread.h>
40 #include <kernel/vm.h>
41 #include <lib/extmem/extmem.h>
42 #include <lib/sm.h>
43 #include <lib/sm/sm_err.h>
44 #include <lib/sm/trusty_sched_share.h>
45 #include <platform.h>
46 #include <string.h>
47 #include <trace.h>
48 
49 #define LOCAL_TRACE (0)
50 
51 /* Trusty Shared Resources Info */
52 struct share_info {
53     ext_mem_client_id_t client_id;
54     ext_mem_obj_id_t buf_id;
55     uint32_t cpu_count;
56     uint32_t header_size;
57     uint32_t percpu_data_size;
58 };
59 
60 static struct share_info shareinfo = {0};
61 
62 struct trusty_sched_shared_mem* sched_shared_mem = NULL;
63 static spin_lock_t sched_shared_datalock = SPIN_LOCK_INITIAL_VALUE;
64 
args_get_id(struct smc32_args * args)65 static ext_mem_obj_id_t args_get_id(struct smc32_args* args) {
66     return (((uint64_t)args->params[1] << 32) | args->params[0]);
67 }
68 
args_get_sz(struct smc32_args * args)69 static size_t args_get_sz(struct smc32_args* args) {
70     return (size_t)args->params[2];
71 }
72 
trusty_share_register(ext_mem_client_id_t client_id,ext_mem_obj_id_t buf_id,uint32_t buf_size)73 static long trusty_share_register(ext_mem_client_id_t client_id,
74                                   ext_mem_obj_id_t buf_id,
75                                   uint32_t buf_size) {
76     struct trusty_sched_shared_mem* share_ptr;
77     spin_lock_saved_state_t state;
78     uint32_t needed_buf_sz;
79     uint32_t nr_cpu;
80     uint32_t hdr_sz;
81     uint32_t percpu_data_sz;
82     uint32_t struct_sz;
83     void* va;
84     status_t status;
85     int retval = SM_ERR_INVALID_PARAMETERS;
86 
87     LTRACEF_LEVEL(5, "client_id=%llx,  buf_id= %llx,  buf_size=%d\n",
88                   (unsigned long long)client_id, (unsigned long long)buf_id,
89                   buf_size);
90 
91     status = ext_mem_map_obj_id(vmm_get_kernel_aspace(),
92                                 "trusty_sched_shared_mem", client_id, buf_id, 0,
93                                 0, buf_size, &va, PAGE_SIZE_SHIFT, 0,
94                                 ARCH_MMU_FLAG_PERM_NO_EXECUTE);
95     if (status) {
96         TRACEF("Error: ext_mem_map_obj_id() failed.\n");
97         return SM_ERR_INTERNAL_FAILURE;
98     }
99     share_ptr = va;
100 
101     nr_cpu = READ_ONCE(share_ptr->cpu_count);
102     hdr_sz = READ_ONCE(share_ptr->hdr_size);
103     percpu_data_sz = READ_ONCE(share_ptr->percpu_data_size);
104 
105     struct_sz = sizeof(struct trusty_sched_shared_mem);
106     if (hdr_sz < struct_sz) {
107         TRACEF("Error: mismatched header-size=%d, struct-size=%d\n", hdr_sz,
108                struct_sz);
109         goto err_invalid_params;
110     }
111     LTRACEF_LEVEL(45, "header-size=%d, struct-size=%d\n", hdr_sz, struct_sz);
112 
113     struct_sz = sizeof(struct trusty_percpu_shared_data);
114     if (percpu_data_sz < struct_sz) {
115         TRACEF("Error: mismatched percpu-data-size=%d, struct-size=%d\n",
116                percpu_data_sz, struct_sz);
117         goto err_invalid_params;
118     }
119     LTRACEF_LEVEL(45, "percpu-data-size=%d, struct-size=%d\n", percpu_data_sz,
120                   struct_sz);
121 
122     if (__builtin_mul_overflow(nr_cpu, percpu_data_sz, &needed_buf_sz)) {
123         TRACEF("Error: multiply overflow while computing (nr_cpu * percpu_data_sz).\n");
124         goto err_invalid_params;
125     }
126     if (__builtin_add_overflow(needed_buf_sz, hdr_sz, &needed_buf_sz)) {
127         TRACEF("Error: Add overflow while adding header_size.\n");
128         goto err_invalid_params;
129     }
130 
131     if (needed_buf_sz > buf_size) {
132         TRACEF("Error: Buffer size is not adequate.\n");
133         goto err_invalid_params;
134     }
135 
136     spin_lock_irqsave(&sched_shared_datalock, state);
137 
138     if (sched_shared_mem) {
139         spin_unlock_irqrestore(&sched_shared_datalock, state);
140         TRACEF("Error: Shared-Memory is already present from a previous call.\n");
141         goto err_shared_mem_busy;
142     }
143 
144     shareinfo.cpu_count = nr_cpu;
145     shareinfo.header_size = hdr_sz;
146     shareinfo.percpu_data_size = percpu_data_sz;
147     shareinfo.client_id = client_id;
148     shareinfo.buf_id = buf_id;
149     sched_shared_mem = share_ptr;
150 
151     spin_unlock_irqrestore(&sched_shared_datalock, state);
152     return 0;
153 
154 err_shared_mem_busy:
155 err_invalid_params:
156     status = vmm_free_region(vmm_get_kernel_aspace(), (vaddr_t)share_ptr);
157     if (status) {
158         TRACEF("Error: failed to free the allocated virtual-memory.\n");
159         retval = SM_ERR_INTERNAL_FAILURE;
160     }
161     return retval;
162 }
163 
trusty_share_unregister(ext_mem_client_id_t client_id,ext_mem_obj_id_t buf_id)164 static long trusty_share_unregister(ext_mem_client_id_t client_id,
165                                     ext_mem_obj_id_t buf_id) {
166     struct trusty_sched_shared_mem* share_ptr;
167     spin_lock_saved_state_t state;
168     status_t status;
169     int retval = SM_ERR_INVALID_PARAMETERS;
170 
171     spin_lock_irqsave(&sched_shared_datalock, state);
172 
173     share_ptr = sched_shared_mem;
174     if (!share_ptr) {
175         spin_unlock_irqrestore(&sched_shared_datalock, state);
176         TRACEF("Error: Trusty ShareInfo not setup by register call.\n");
177         return retval;
178     }
179 
180     if ((client_id != shareinfo.client_id) || (buf_id != shareinfo.buf_id)) {
181         spin_unlock_irqrestore(&sched_shared_datalock, state);
182         TRACEF("Error: invalid arguments.\n");
183         return retval;
184     }
185 
186     sched_shared_mem = NULL;
187     memset(&shareinfo, 0, sizeof(struct share_info));
188 
189     spin_unlock_irqrestore(&sched_shared_datalock, state);
190     retval = 0;
191 
192     status = vmm_free_region(vmm_get_kernel_aspace(), (vaddr_t)share_ptr);
193     if (status) {
194         TRACEF("Error: failed to free the allocated virtual-memory.\n");
195         retval = SM_ERR_INTERNAL_FAILURE;
196     }
197     return retval;
198 }
199 
smc_trusty_sched_share_register(struct smc32_args * args)200 long smc_trusty_sched_share_register(struct smc32_args* args) {
201     ext_mem_client_id_t client_id = args->client_id;
202     ext_mem_obj_id_t buf_id = args_get_id(args);
203     uint32_t buf_size = args_get_sz(args);
204 
205     if (!IS_PAGE_ALIGNED(buf_size)) {
206         TRACEF("Error: argument buffer-size is not page-aligned.\n");
207         return SM_ERR_INVALID_PARAMETERS;
208     }
209 
210     return trusty_share_register(client_id, buf_id, buf_size);
211 }
212 
smc_trusty_sched_share_unregister(struct smc32_args * args)213 long smc_trusty_sched_share_unregister(struct smc32_args* args) {
214     ext_mem_client_id_t client_id = args->client_id;
215     ext_mem_obj_id_t buf_id = args_get_id(args);
216 
217     return trusty_share_unregister(client_id, buf_id);
218 }
219 
get_percpu_share_ptr(uint32_t cpu_nr)220 static struct trusty_percpu_shared_data* get_percpu_share_ptr(uint32_t cpu_nr) {
221     struct trusty_percpu_shared_data* percpu_data_ptr;
222     unsigned char* tmp;
223 
224     DEBUG_ASSERT(cpu_nr < shareinfo.cpu_count);
225 
226     tmp = (unsigned char*)sched_shared_mem;
227     tmp += shareinfo.header_size;
228     tmp += cpu_nr * shareinfo.percpu_data_size;
229 
230     percpu_data_ptr = (struct trusty_percpu_shared_data*)tmp;
231     return percpu_data_ptr;
232 }
233 
234 /*
235  * Following function is called from trusty kernel thread.c
236  */
platform_cpu_priority_set(uint32_t cpu_nr,uint32_t priority)237 void platform_cpu_priority_set(uint32_t cpu_nr, uint32_t priority) {
238     spin_lock_saved_state_t state;
239     struct trusty_percpu_shared_data* percpu_data_ptr;
240     uint32_t requested_priority;
241 
242     /* Ignore the set request by irq-ns-switch-* threads, which exclusively
243      * run at the HIGHEST_PRIORITY. The problem is that the irq-ns-switch-*
244      * threads run on behalf of linux (or any other normal world client os)
245      * and are always the threads that return to linux (client os) while
246      * trusty is busy,  but those are not the threads whose priority, the
247      * linux side wants to know.
248      */
249     if (priority >= HIGHEST_PRIORITY) {
250         return;
251     }
252 
253     if (priority >= HIGH_PRIORITY) {
254         requested_priority = TRUSTY_SHADOW_PRIORITY_HIGH;
255     } else if (priority <= LOW_PRIORITY) {
256         requested_priority = TRUSTY_SHADOW_PRIORITY_LOW;
257     } else {
258         requested_priority = TRUSTY_SHADOW_PRIORITY_NORMAL;
259     }
260 
261     /*
262      * if the shared-memory is established and the reuesting
263      * cpu_nr is less than the max number of CPUs supported by
264      * the Linux side, proceed with the value change.
265      */
266     spin_lock_irqsave(&sched_shared_datalock, state);
267     if ((sched_shared_mem) && (cpu_nr < shareinfo.cpu_count)) {
268         percpu_data_ptr = get_percpu_share_ptr(cpu_nr);
269         percpu_data_ptr->ask_shadow_priority = requested_priority;
270     }
271     spin_unlock_irqrestore(&sched_shared_datalock, state);
272 }
273