linuxdriver: support dump tee log [1/1]
[GitHub/LineageOS/G12/android_hardware_amlogic_kernel-modules_optee.git] / optee / log.c
CommitLineData
5e6a685d
LJ
1/*
2 * Copyright (c) 2017, Amlogic, Inc.
3 *
4 * This software is licensed under the terms of the GNU General Public
5 * License version 2, as published by the Free Software Foundation, and
6 * may be copied, distributed, and modified under those terms.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 */
14#include <linux/errno.h>
15#include <linux/io.h>
16#include <linux/module.h>
17#include <linux/slab.h>
18#include <linux/string.h>
19#include <linux/types.h>
20#include <linux/uaccess.h>
21#include <linux/sysfs.h>
22#include <linux/kthread.h>
02eed488 23#include <generated/uapi/linux/version.h>
5e6a685d
LJ
24
25#include "optee_smc.h"
26#include "optee_private.h"
27#include "../tee_private.h"
28
29//#define OPTEE_LOG_BUFFER_DEBUG 1
30
31#define OPTEE_LOG_BUFFER_MAGIC 0xAA00AA00
32#define OPTEE_LOG_BUFFER_OFFSET 0x00000080
33#define OPTEE_LOG_READ_MAX PAGE_SIZE
34#define OPTEE_LOG_LINE_MAX 1024
35#define OPTEE_LOG_TIMER_INTERVAL 1
36
37#undef pr_fmt
38#define pr_fmt(fmt) "[TEE] " fmt
39
40struct optee_log_ctl_s {
41 unsigned int magic;
42 unsigned int inited;
43 unsigned int total_size;
44 unsigned int fill_size;
45 unsigned int mode;
46 unsigned int reader;
47 unsigned int writer;
48
49 unsigned char *buffer;
50};
51
52static struct optee_log_ctl_s *optee_log_ctl;
53static unsigned char *optee_log_buff;
54static uint32_t optee_log_mode = 1;
55static struct timer_list optee_log_timer;
56static uint8_t line_buff[OPTEE_LOG_LINE_MAX];
1a9b79f2 57static uint32_t looped = 0;
5e6a685d
LJ
58static void *g_shm_va;
59
60static bool init_shm(phys_addr_t shm_pa, uint32_t shm_size)
61{
62 struct arm_smccc_res smccc;
63 uint32_t start = 1;
64
65 if (pfn_valid(__phys_to_pfn(shm_pa)))
66 g_shm_va = (void __iomem *)__phys_to_virt(shm_pa);
67 else
68 g_shm_va = ioremap_cache(shm_pa, shm_size);
69
70 if (!g_shm_va) {
71 pr_err("map logger share-mem failed\n");
72 return false;
73 }
74
75 arm_smccc_smc(OPTEE_SMC_SET_LOGGER, start, shm_pa, shm_size, 0, 0, 0, 0,
76 &smccc);
77
78 return (0 == smccc.a0);
79}
80
81static void uninit_shm(void)
82{
83 struct arm_smccc_res smccc;
84 uint32_t start = 0;
85
86 if (g_shm_va)
87 iounmap(g_shm_va);
88
89 arm_smccc_smc(OPTEE_SMC_SET_LOGGER, start, 0, 0, 0, 0, 0, 0, &smccc);
90}
91
92static ssize_t log_mode_show(struct class *cla,
93 struct class_attribute *attr, char *buf)
94{
95 return sprintf(buf, "%d\n", optee_log_mode);
96}
97
98static ssize_t log_mode_store(struct class *cla,
99 struct class_attribute *attr,
100 const char *buf, size_t count)
101{
102 int res = 0;
103 int rc = 0;
104 struct optee_log_ctl_s *ctl = optee_log_ctl;
105
106 rc = kstrtoint(buf, 0, &res);
107 pr_notice("log_mode: %d->%d\n", optee_log_mode, res);
108 optee_log_mode = res;
109 if (ctl && ctl->mode != optee_log_mode)
110 ctl->mode = optee_log_mode;
111
112 return count;
113}
114
115static ssize_t log_buff_get_read_buff(char **buf, int len)
116{
117 int writer;
118 int reader;
119 int read_size = 0;
120 struct optee_log_ctl_s *ctl = optee_log_ctl;
121
122 if ((!ctl) || (len <= 0))
123 return 0;
124
125 writer = ctl->writer;
126 reader = ctl->reader;
127
128 if (reader == writer)
129 read_size = 0;
130 else if (reader < writer)
131 read_size = writer - reader;
1a9b79f2
PZ
132 else {
133 looped = 1;
5e6a685d 134 read_size = ctl->total_size - reader;
1a9b79f2 135 }
5e6a685d
LJ
136
137 if (read_size > len)
138 read_size = len;
139
140 *buf = optee_log_buff + reader;
141 ctl->reader += read_size;
142 if (ctl->reader == ctl->total_size)
143 ctl->reader = 0;
144
145 return read_size;
146}
147
148static size_t log_print_text(char *buf, size_t size)
149{
150 const char *text = buf;
151 size_t text_size = size;
152 size_t len = 0;
153 char *line = line_buff;
154
1a9b79f2
PZ
155 if (size == 0)
156 return 0;
157
5e6a685d
LJ
158 do {
159 const char *next = memchr(text, '\n', text_size);
160 size_t line_size;
161
162 if (next) {
163 line_size = next - text;
164 next++;
165 text_size -= next - text;
166 } else
167 line_size = text_size;
168
169 memcpy(line, text, line_size);
170 len += line_size;
171 if (next)
172 len++;
173 else if (line[line_size] == '\0')
174 len++;
175 line[line_size] = '\n';
176 line[line_size + 1] = '\0';
177 pr_notice("%s", line);
178 text = next;
179 } while (text && (len < size));
180
181 return len;
182}
183
1a9b79f2 184static ssize_t log_buff_dump(void)
5e6a685d
LJ
185{
186 ssize_t len;
187 char *ptr = NULL;
1a9b79f2 188 unsigned int writer = 0;
5e6a685d
LJ
189 struct optee_log_ctl_s *ctl = optee_log_ctl;
190
191 if (!ctl)
192 return 0;
193
1a9b79f2 194 writer = ctl->writer;
5e6a685d 195
1a9b79f2
PZ
196 if (looped) {
197 ptr = optee_log_buff + writer;
198 len = ctl->total_size - writer;
199
200 log_print_text(ptr, len);
5e6a685d 201 }
5e6a685d 202
1a9b79f2
PZ
203 ptr = optee_log_buff;
204 len = writer;
205
206 log_print_text(ptr, len);
207
208 return 0;
5e6a685d
LJ
209}
210
211static void log_buff_reset(void)
212{
213 struct optee_log_ctl_s *ctl = optee_log_ctl;
214
215 if (!ctl)
216 return;
217
218 ctl->writer = 0;
219 ctl->reader = 0;
220
221 return;
222}
223
224static void log_buff_info(void)
225{
226#ifdef OPTEE_LOG_BUFFER_DEBUG
227 struct optee_log_ctl_s *ctl = optee_log_ctl;
228
229 if (!ctl)
230 return;
231
232 pr_notice(" total_size: %d\n", ctl->total_size);
233 pr_notice(" fill_size: %d\n", ctl->fill_size);
234 pr_notice(" mode: %d\n", ctl->mode);
235 pr_notice(" reader: %d\n", ctl->reader);
236 pr_notice(" writer: %d\n", ctl->writer);
237#endif
238}
239
240static ssize_t log_buff_store(struct class *cla, struct class_attribute *attr,
241 const char *buf, size_t count)
242{
243 int res = 0;
244 int rc = 0;
245
246 rc = kstrtoint(buf, 0, &res);
247 if (res == 0)
248 /* reset log buffer */
249 log_buff_reset();
250 else if (res == 1)
251 /* show log buffer info */
252 log_buff_info();
253 else
254 pr_notice("set 0 to reset tee log buffer\n");
255
256 return count;
257}
258
259static ssize_t log_buff_show(struct class *cla,
260 struct class_attribute *attr, char *buf)
261{
1a9b79f2
PZ
262 log_buff_dump();
263
264 return 0;
5e6a685d
LJ
265}
266
267static struct class_attribute log_class_attrs[] = {
268 __ATTR(log_mode, S_IRUGO | S_IWUSR, log_mode_show, log_mode_store),
269 __ATTR(log_buff, S_IRUGO | S_IWUSR, log_buff_show, log_buff_store),
270};
271
272static void log_buff_output(void)
273{
274 size_t len;
275 char *read_buff = NULL;
276
277 if (optee_log_mode == 0)
278 return;
279
280 len = log_buff_get_read_buff(&read_buff, OPTEE_LOG_READ_MAX);
281 if (len > 0)
282 log_print_text(read_buff, len);
283}
284
02eed488
LJ
285#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 34)
286static void log_timer_func(struct timer_list *timer)
287#else
5e6a685d 288static void log_timer_func(unsigned long arg)
02eed488 289#endif
5e6a685d
LJ
290{
291 log_buff_output();
292 mod_timer(&optee_log_timer, jiffies + OPTEE_LOG_TIMER_INTERVAL * HZ);
293}
294
295int optee_log_init(struct tee_device *tee_dev, phys_addr_t shm_pa,
296 uint32_t shm_size)
297{
298 int rc = 0;
299 int i = 0;
300 int n = 0;
301
302 if (!init_shm(shm_pa, shm_size))
303 return -1;
304
305 optee_log_buff = (unsigned char *)(g_shm_va + OPTEE_LOG_BUFFER_OFFSET);
306 optee_log_ctl = (struct optee_log_ctl_s *)g_shm_va;
307 if ((optee_log_ctl->magic != OPTEE_LOG_BUFFER_MAGIC)
308 || (optee_log_ctl->inited != 1)) {
309 uninit_shm();
310 optee_log_ctl = NULL;
311 rc = -1;
312 pr_err("tee log buffer init failed\n");
313 goto err;
314 }
315 optee_log_ctl->mode = optee_log_mode;
316
317 /* create attr files */
318 n = sizeof(log_class_attrs) / sizeof(struct class_attribute);
319 for (i = 0; i < n; i++) {
320 rc = class_create_file(tee_dev->dev.class, &log_class_attrs[i]);
321 if (rc)
322 goto err;
323 }
324
325 /* init timer */
02eed488
LJ
326#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 34)
327 timer_setup(&optee_log_timer, log_timer_func, 0);
328#else
329 setup_timer(&optee_log_timer, log_timer_func, 0);
330#endif
5e6a685d
LJ
331 optee_log_timer.expires = jiffies + HZ;
332 add_timer(&optee_log_timer);
333
334err:
335 return rc;
336}
337
338void optee_log_exit(struct tee_device *tee_dev)
339{
340 int i = 0;
341 int n = 0;
342
343 del_timer_sync(&optee_log_timer);
344
345 n = sizeof(log_class_attrs) / sizeof(struct class_attribute);
346 for (i = 0; i < n; i++)
347 class_remove_file(tee_dev->dev.class, &log_class_attrs[i]);
348
349 uninit_shm();
350}