1 /*
2  * Copyright (c) 2015 Google, Inc.
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 #include <arch/ops.h>
25 #include <debug.h>
26 #include <err.h>
27 #include <kernel/thread.h>
28 #include <kernel/vm.h>
29 #include <lib/io.h>
30 #include <lib/sm.h>
31 #include <lib/sm/sm_err.h>
32 #include <lib/sm/smcall.h>
33 #include <list.h>
34 #include <lk/init.h>
35 #include <platform.h>
36 #include <pow2.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 
41 #include "trusty-log.h"
42 
43 #define LOG_LOCK_FLAGS SPIN_LOCK_FLAG_IRQ_FIQ
44 
45 struct memlog {
46     struct log_rb* rb;
47     size_t rb_sz;
48 
49     ext_mem_client_id_t client_id;
50     ext_mem_obj_id_t buf_id;
51     size_t buf_sz;
52 
53     print_callback_t cb;
54     struct list_node entry;
55 };
56 
57 static spin_lock_t log_lock;
58 static struct list_node log_list = LIST_INITIAL_VALUE(log_list);
59 
memlog_get_by_id(ext_mem_client_id_t client_id,ext_mem_obj_id_t buf_id)60 static struct memlog* memlog_get_by_id(ext_mem_client_id_t client_id,
61                                        ext_mem_obj_id_t buf_id) {
62     struct memlog* log;
63     list_for_every_entry(&log_list, log, struct memlog, entry) {
64         if (log->client_id == client_id && log->buf_id == buf_id) {
65             return log;
66         }
67     }
68 
69     return NULL;
70 }
71 
lower_pow2(uint32_t v)72 static uint32_t lower_pow2(uint32_t v) {
73     return 1u << (31 - __builtin_clz(v));
74 }
75 
__memlog_write(struct memlog * log,const char * str,size_t len)76 static void __memlog_write(struct memlog* log, const char* str, size_t len) {
77     size_t i;
78     uint32_t log_offset;
79     struct log_rb* rb = log->rb;
80 
81     log_offset = rb->alloc;
82 
83     __builtin_add_overflow(rb->alloc, len, &rb->alloc);
84 
85     /* Updates to alloc should be visible before the data is written. */
86     wmb();
87 
88     for (i = 0; i < len; i++) {
89         uint32_t offset;
90         __builtin_add_overflow(log_offset, i, &offset);
91         offset &= (log->rb_sz - 1);
92         volatile char* ptr = &rb->data[offset];
93         *ptr = str[i];
94     }
95 }
96 
memlog_write(struct memlog * log,const char * str,size_t len)97 static void memlog_write(struct memlog* log, const char* str, size_t len) {
98     size_t i;
99     const int chunk_size = 128;
100     size_t rem;
101     spin_lock_saved_state_t state;
102 
103     spin_lock_save(&log_lock, &state, LOG_LOCK_FLAGS);
104     for (i = 0; i < len / chunk_size; i++) {
105         __memlog_write(log, &str[i * chunk_size], chunk_size);
106     }
107     rem = len - i * chunk_size;
108     if (rem)
109         __memlog_write(log, &str[i * chunk_size], rem);
110     spin_unlock_restore(&log_lock, state, LOG_LOCK_FLAGS);
111 }
112 
113 /* Signal that the buffered data is ready to read. */
memlog_commit(struct memlog * log)114 static void memlog_commit(struct memlog* log) {
115     spin_lock_saved_state_t state;
116     spin_lock_save(&log_lock, &state, LOG_LOCK_FLAGS);
117 
118     /*
119      * Updates to the data should be visible before put is written.
120      * Arguably the existing spinlock implementations should take care of the
121      * ordering, but spinlocks for a non-SMP version of Trusty would not be
122      * required to use barriers. This code needs a barrier, however, because it
123      * is synchonizing with code that runs outside of Trusty, possibly on a
124      * different processor. (Even if Trusty itself is non-SMP.)
125      */
126     wmb();
127 
128     log->rb->put = log->rb->alloc;
129 
130     spin_unlock_restore(&log_lock, state, LOG_LOCK_FLAGS);
131 }
132 
map_rb(ext_mem_client_id_t client_id,ext_mem_obj_id_t mem_obj_id,size_t sz,vaddr_t * va)133 static status_t map_rb(ext_mem_client_id_t client_id,
134                        ext_mem_obj_id_t mem_obj_id,
135                        size_t sz,
136                        vaddr_t* va) {
137     return ext_mem_map_obj_id(vmm_get_kernel_aspace(), "logmem", client_id,
138                               mem_obj_id, 0, 0, sz, (void**)va, PAGE_SIZE_SHIFT,
139                               0, ARCH_MMU_FLAG_PERM_NO_EXECUTE);
140 }
141 
args_get_id(struct smc32_args * args)142 static ext_mem_obj_id_t args_get_id(struct smc32_args* args) {
143     return (((uint64_t)args->params[1] << 32) | args->params[0]);
144 }
145 
args_get_sz(struct smc32_args * args)146 static size_t args_get_sz(struct smc32_args* args) {
147     return (size_t)args->params[2];
148 }
149 
memlog_print_callback(print_callback_t * cb,const char * str,size_t len)150 void memlog_print_callback(print_callback_t* cb, const char* str, size_t len) {
151     struct memlog* log = containerof(cb, struct memlog, cb);
152     memlog_write(log, str, len);
153 }
154 
memlog_commit_callback(print_callback_t * cb)155 void memlog_commit_callback(print_callback_t* cb) {
156     struct memlog* log = containerof(cb, struct memlog, cb);
157     memlog_commit(log);
158 }
159 
memlog_add(ext_mem_client_id_t client_id,ext_mem_obj_id_t buf_id,size_t sz)160 static long memlog_add(ext_mem_client_id_t client_id,
161                        ext_mem_obj_id_t buf_id,
162                        size_t sz) {
163     struct memlog* log;
164     vaddr_t va;
165     long status;
166     status_t result;
167     struct log_rb* rb;
168 
169     if (!IS_PAGE_ALIGNED(sz) || sz == 0) {
170         return SM_ERR_INVALID_PARAMETERS;
171     }
172 
173     log = malloc(sizeof(*log));
174     if (!log) {
175         return SM_ERR_INTERNAL_FAILURE;
176     }
177     memset(log, 0, sizeof(*log));
178     log->client_id = client_id;
179     log->buf_id = buf_id;
180     log->buf_sz = sz;
181 
182     result = map_rb(client_id, buf_id, sz, &va);
183     if (result != NO_ERROR) {
184         status = SM_ERR_INTERNAL_FAILURE;
185         goto error_failed_to_map;
186     }
187     rb = (struct log_rb*)va;
188     log->rb = rb;
189     log->rb_sz = lower_pow2(log->buf_sz - offsetof(struct log_rb, data));
190 
191     rb->sz = log->rb_sz;
192     rb->alloc = 0;
193     rb->put = 0;
194 
195     list_add_head(&log_list, &log->entry);
196 
197     log->cb.print = memlog_print_callback;
198     log->cb.commit = memlog_commit_callback;
199     register_print_callback(&log->cb);
200     return 0;
201 
202 error_failed_to_map:
203     free(log);
204     return status;
205 }
206 
memlog_rm(ext_mem_client_id_t client_id,ext_mem_obj_id_t buf_id)207 static long memlog_rm(ext_mem_client_id_t client_id, ext_mem_obj_id_t buf_id) {
208     struct memlog* log;
209     status_t result;
210 
211     log = memlog_get_by_id(client_id, buf_id);
212     if (!log) {
213         return SM_ERR_INVALID_PARAMETERS;
214     }
215     unregister_print_callback(&log->cb);
216     list_delete(&log->entry);
217     result = vmm_free_region(vmm_get_kernel_aspace(), (vaddr_t)log->rb);
218     free(log);
219     if (result != NO_ERROR) {
220         return SM_ERR_INTERNAL_FAILURE;
221     }
222     return 0;
223 }
224 
memlog_stdcall(struct smc32_args * args)225 static long memlog_stdcall(struct smc32_args* args) {
226     switch (args->smc_nr) {
227     case SMC_SC_SHARED_LOG_VERSION:
228         return TRUSTY_LOG_API_VERSION;
229     case SMC_SC_SHARED_LOG_ADD:
230         return memlog_add(args->client_id, args_get_id(args),
231                           args_get_sz(args));
232     case SMC_SC_SHARED_LOG_RM:
233         return memlog_rm(args->client_id, args_get_id(args));
234     default:
235         return SM_ERR_UNDEFINED_SMC;
236     }
237     return 0;
238 }
239 
240 static struct smc32_entity log_sm_entity = {
241         .stdcall_handler = memlog_stdcall,
242 };
243 
memlog_init(uint level)244 static void memlog_init(uint level) {
245     int err;
246 
247     err = sm_register_entity(SMC_ENTITY_LOGGING, &log_sm_entity);
248     if (err) {
249         printf("trusty error register entity: %d\n", err);
250     }
251 }
252 LK_INIT_HOOK(memlog, memlog_init, LK_INIT_LEVEL_APPS);
253