• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2012-2017 ARM Limited or its affiliates.
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License version 2 as
6  * published by the Free Software Foundation.
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  * You should have received a copy of the GNU General Public License
14  * along with this program; if not, see <http://www.gnu.org/licenses/>.
15  */
16 
17 #include <linux/kernel.h>
18 #include "ssi_config.h"
19 #include "ssi_driver.h"
20 #include "cc_crypto_ctx.h"
21 #include "ssi_sysfs.h"
22 
23 #ifdef ENABLE_CC_SYSFS
24 
25 static struct ssi_drvdata *sys_get_drvdata(void);
26 
27 #ifdef CC_CYCLE_COUNT
28 
29 #include <asm/timex.h>
30 
31 struct stat_item {
32 	unsigned int min;
33 	unsigned int max;
34 	cycles_t sum;
35 	unsigned int count;
36 };
37 
38 struct stat_name {
39 	const char *op_type_name;
40 	const char *stat_phase_name[MAX_STAT_PHASES];
41 };
42 
43 static struct stat_name stat_name_db[MAX_STAT_OP_TYPES] = {
44 	{
45 		/* STAT_OP_TYPE_NULL */
46 		.op_type_name = "NULL",
47 		.stat_phase_name = {NULL},
48 	},
49 	{
50 		.op_type_name = "Encode",
51 		.stat_phase_name[STAT_PHASE_0] = "Init and sanity checks",
52 		.stat_phase_name[STAT_PHASE_1] = "Map buffers",
53 		.stat_phase_name[STAT_PHASE_2] = "Create sequence",
54 		.stat_phase_name[STAT_PHASE_3] = "Send Request",
55 		.stat_phase_name[STAT_PHASE_4] = "HW-Q push",
56 		.stat_phase_name[STAT_PHASE_5] = "Sequence completion",
57 		.stat_phase_name[STAT_PHASE_6] = "HW cycles",
58 	},
59 	{	.op_type_name = "Decode",
60 		.stat_phase_name[STAT_PHASE_0] = "Init and sanity checks",
61 		.stat_phase_name[STAT_PHASE_1] = "Map buffers",
62 		.stat_phase_name[STAT_PHASE_2] = "Create sequence",
63 		.stat_phase_name[STAT_PHASE_3] = "Send Request",
64 		.stat_phase_name[STAT_PHASE_4] = "HW-Q push",
65 		.stat_phase_name[STAT_PHASE_5] = "Sequence completion",
66 		.stat_phase_name[STAT_PHASE_6] = "HW cycles",
67 	},
68 	{	.op_type_name = "Setkey",
69 		.stat_phase_name[STAT_PHASE_0] = "Init and sanity checks",
70 		.stat_phase_name[STAT_PHASE_1] = "Copy key to ctx",
71 		.stat_phase_name[STAT_PHASE_2] = "Create sequence",
72 		.stat_phase_name[STAT_PHASE_3] = "Send Request",
73 		.stat_phase_name[STAT_PHASE_4] = "HW-Q push",
74 		.stat_phase_name[STAT_PHASE_5] = "Sequence completion",
75 		.stat_phase_name[STAT_PHASE_6] = "HW cycles",
76 	},
77 	{
78 		.op_type_name = "Generic",
79 		.stat_phase_name[STAT_PHASE_0] = "Interrupt",
80 		.stat_phase_name[STAT_PHASE_1] = "ISR-to-Tasklet",
81 		.stat_phase_name[STAT_PHASE_2] = "Tasklet start-to-end",
82 		.stat_phase_name[STAT_PHASE_3] = "Tasklet:user_cb()",
83 		.stat_phase_name[STAT_PHASE_4] = "Tasklet:dx_X_complete() - w/o X_complete()",
84 		.stat_phase_name[STAT_PHASE_5] = "",
85 		.stat_phase_name[STAT_PHASE_6] = "HW cycles",
86 	}
87 };
88 
89 /*
90  * Structure used to create a directory
91  * and its attributes in sysfs.
92  */
93 struct sys_dir {
94 	struct kobject *sys_dir_kobj;
95 	struct attribute_group sys_dir_attr_group;
96 	struct attribute **sys_dir_attr_list;
97 	u32 num_of_attrs;
98 	struct ssi_drvdata *drvdata; /* Associated driver context */
99 };
100 
101 /* top level directory structures */
102 struct sys_dir sys_top_dir;
103 
104 static DEFINE_SPINLOCK(stat_lock);
105 
106 /* List of DBs */
107 static struct stat_item stat_host_db[MAX_STAT_OP_TYPES][MAX_STAT_PHASES];
108 static struct stat_item stat_cc_db[MAX_STAT_OP_TYPES][MAX_STAT_PHASES];
109 
init_db(struct stat_item item[MAX_STAT_OP_TYPES][MAX_STAT_PHASES])110 static void init_db(struct stat_item item[MAX_STAT_OP_TYPES][MAX_STAT_PHASES])
111 {
112 	unsigned int i, j;
113 
114 	/* Clear db */
115 	for (i = 0; i < MAX_STAT_OP_TYPES; i++) {
116 		for (j = 0; j < MAX_STAT_PHASES; j++) {
117 			item[i][j].min = 0xFFFFFFFF;
118 			item[i][j].max = 0;
119 			item[i][j].sum = 0;
120 			item[i][j].count = 0;
121 		}
122 	}
123 }
124 
update_db(struct stat_item * item,unsigned int result)125 static void update_db(struct stat_item *item, unsigned int result)
126 {
127 	item->count++;
128 	item->sum += result;
129 	if (result < item->min)
130 		item->min = result;
131 	if (result > item->max)
132 		item->max = result;
133 }
134 
display_db(struct stat_item item[MAX_STAT_OP_TYPES][MAX_STAT_PHASES])135 static void display_db(struct stat_item item[MAX_STAT_OP_TYPES][MAX_STAT_PHASES])
136 {
137 	unsigned int i, j;
138 	u64 avg;
139 
140 	for (i = STAT_OP_TYPE_ENCODE; i < MAX_STAT_OP_TYPES; i++) {
141 		for (j = 0; j < MAX_STAT_PHASES; j++) {
142 			if (item[i][j].count > 0) {
143 				avg = (u64)item[i][j].sum;
144 				do_div(avg, item[i][j].count);
145 				SSI_LOG_ERR("%s, %s: min=%d avg=%d max=%d sum=%lld count=%d\n",
146 					    stat_name_db[i].op_type_name,
147 					    stat_name_db[i].stat_phase_name[j],
148 					    item[i][j].min, (int)avg,
149 					    item[i][j].max,
150 					    (long long)item[i][j].sum,
151 					    item[i][j].count);
152 			}
153 		}
154 	}
155 }
156 
157 /**************************************
158  * Attributes show functions section  *
159  **************************************/
160 
ssi_sys_stats_host_db_clear(struct kobject * kobj,struct kobj_attribute * attr,const char * buf,size_t count)161 static ssize_t ssi_sys_stats_host_db_clear(struct kobject *kobj,
162 					   struct kobj_attribute *attr,
163 					   const char *buf, size_t count)
164 {
165 	init_db(stat_host_db);
166 	return count;
167 }
168 
ssi_sys_stats_cc_db_clear(struct kobject * kobj,struct kobj_attribute * attr,const char * buf,size_t count)169 static ssize_t ssi_sys_stats_cc_db_clear(struct kobject *kobj,
170 					 struct kobj_attribute *attr,
171 					 const char *buf, size_t count)
172 {
173 	init_db(stat_cc_db);
174 	return count;
175 }
176 
ssi_sys_stat_host_db_show(struct kobject * kobj,struct kobj_attribute * attr,char * buf)177 static ssize_t ssi_sys_stat_host_db_show(struct kobject *kobj,
178 					 struct kobj_attribute *attr, char *buf)
179 {
180 	int i, j;
181 	char line[512];
182 	u32 min_cyc, max_cyc;
183 	u64 avg;
184 	ssize_t buf_len, tmp_len = 0;
185 
186 	buf_len = scnprintf(buf, PAGE_SIZE,
187 			    "phase\t\t\t\t\t\t\tmin[cy]\tavg[cy]\tmax[cy]\t#samples\n");
188 	if (buf_len < 0)/* scnprintf shouldn't return negative value according to its implementation*/
189 		return buf_len;
190 	for (i = STAT_OP_TYPE_ENCODE; i < MAX_STAT_OP_TYPES; i++) {
191 		for (j = 0; j < MAX_STAT_PHASES - 1; j++) {
192 			if (stat_host_db[i][j].count > 0) {
193 				avg = (u64)stat_host_db[i][j].sum;
194 				do_div(avg, stat_host_db[i][j].count);
195 				min_cyc = stat_host_db[i][j].min;
196 				max_cyc = stat_host_db[i][j].max;
197 			} else {
198 				avg = min_cyc = max_cyc = 0;
199 			}
200 			tmp_len = scnprintf(line, 512,
201 					    "%s::%s\t\t\t\t\t%6u\t%6u\t%6u\t%7u\n",
202 					    stat_name_db[i].op_type_name,
203 					    stat_name_db[i].stat_phase_name[j],
204 					    min_cyc, (unsigned int)avg, max_cyc,
205 					    stat_host_db[i][j].count);
206 			if (tmp_len < 0)/* scnprintf shouldn't return negative value according to its implementation*/
207 				return buf_len;
208 			if (buf_len + tmp_len >= PAGE_SIZE)
209 				return buf_len;
210 			buf_len += tmp_len;
211 			strncat(buf, line, 512);
212 		}
213 	}
214 	return buf_len;
215 }
216 
ssi_sys_stat_cc_db_show(struct kobject * kobj,struct kobj_attribute * attr,char * buf)217 static ssize_t ssi_sys_stat_cc_db_show(struct kobject *kobj,
218 				       struct kobj_attribute *attr, char *buf)
219 {
220 	int i;
221 	char line[256];
222 	u32 min_cyc, max_cyc;
223 	u64 avg;
224 	ssize_t buf_len, tmp_len = 0;
225 
226 	buf_len = scnprintf(buf, PAGE_SIZE,
227 			    "phase\tmin[cy]\tavg[cy]\tmax[cy]\t#samples\n");
228 	if (buf_len < 0)/* scnprintf shouldn't return negative value according to its implementation*/
229 		return buf_len;
230 	for (i = STAT_OP_TYPE_ENCODE; i < MAX_STAT_OP_TYPES; i++) {
231 		if (stat_cc_db[i][STAT_PHASE_6].count > 0) {
232 			avg = (u64)stat_cc_db[i][STAT_PHASE_6].sum;
233 			do_div(avg, stat_cc_db[i][STAT_PHASE_6].count);
234 			min_cyc = stat_cc_db[i][STAT_PHASE_6].min;
235 			max_cyc = stat_cc_db[i][STAT_PHASE_6].max;
236 		} else {
237 			avg = min_cyc = max_cyc = 0;
238 		}
239 		tmp_len = scnprintf(line, 256, "%s\t%6u\t%6u\t%6u\t%7u\n",
240 				    stat_name_db[i].op_type_name, min_cyc,
241 				    (unsigned int)avg, max_cyc,
242 				    stat_cc_db[i][STAT_PHASE_6].count);
243 
244 		if (tmp_len < 0)/* scnprintf shouldn't return negative value according to its implementation*/
245 			return buf_len;
246 
247 		if (buf_len + tmp_len >= PAGE_SIZE)
248 			return buf_len;
249 		buf_len += tmp_len;
250 		strncat(buf, line, 256);
251 	}
252 	return buf_len;
253 }
254 
update_host_stat(unsigned int op_type,unsigned int phase,cycles_t result)255 void update_host_stat(unsigned int op_type, unsigned int phase, cycles_t result)
256 {
257 	unsigned long flags;
258 
259 	spin_lock_irqsave(&stat_lock, flags);
260 	update_db(&stat_host_db[op_type][phase], (unsigned int)result);
261 	spin_unlock_irqrestore(&stat_lock, flags);
262 }
263 
update_cc_stat(unsigned int op_type,unsigned int phase,unsigned int elapsed_cycles)264 void update_cc_stat(
265 	unsigned int op_type,
266 	unsigned int phase,
267 	unsigned int elapsed_cycles)
268 {
269 	update_db(&stat_cc_db[op_type][phase], elapsed_cycles);
270 }
271 
display_all_stat_db(void)272 void display_all_stat_db(void)
273 {
274 	SSI_LOG_ERR("\n=======    CYCLE COUNT STATS    =======\n");
275 	display_db(stat_host_db);
276 	SSI_LOG_ERR("\n======= CC HW CYCLE COUNT STATS =======\n");
277 	display_db(stat_cc_db);
278 }
279 #endif /*CC_CYCLE_COUNT*/
280 
ssi_sys_regdump_show(struct kobject * kobj,struct kobj_attribute * attr,char * buf)281 static ssize_t ssi_sys_regdump_show(struct kobject *kobj,
282 				    struct kobj_attribute *attr, char *buf)
283 {
284 	struct ssi_drvdata *drvdata = sys_get_drvdata();
285 	u32 register_value;
286 	void __iomem *cc_base = drvdata->cc_base;
287 	int offset = 0;
288 
289 	register_value = CC_HAL_READ_REGISTER(CC_REG_OFFSET(HOST_RGF, HOST_SIGNATURE));
290 	offset += scnprintf(buf + offset, PAGE_SIZE - offset, "%s \t(0x%lX)\t 0x%08X\n", "HOST_SIGNATURE       ", DX_HOST_SIGNATURE_REG_OFFSET, register_value);
291 	register_value = CC_HAL_READ_REGISTER(CC_REG_OFFSET(HOST_RGF, HOST_IRR));
292 	offset += scnprintf(buf + offset, PAGE_SIZE - offset, "%s \t(0x%lX)\t 0x%08X\n", "HOST_IRR             ", DX_HOST_IRR_REG_OFFSET, register_value);
293 	register_value = CC_HAL_READ_REGISTER(CC_REG_OFFSET(HOST_RGF, HOST_POWER_DOWN_EN));
294 	offset += scnprintf(buf + offset, PAGE_SIZE - offset, "%s \t(0x%lX)\t 0x%08X\n", "HOST_POWER_DOWN_EN   ", DX_HOST_POWER_DOWN_EN_REG_OFFSET, register_value);
295 	register_value =  CC_HAL_READ_REGISTER(CC_REG_OFFSET(CRY_KERNEL, AXIM_MON_ERR));
296 	offset += scnprintf(buf + offset, PAGE_SIZE - offset, "%s \t(0x%lX)\t 0x%08X\n", "AXIM_MON_ERR         ", DX_AXIM_MON_ERR_REG_OFFSET, register_value);
297 	register_value = CC_HAL_READ_REGISTER(CC_REG_OFFSET(CRY_KERNEL, DSCRPTR_QUEUE_CONTENT));
298 	offset += scnprintf(buf + offset, PAGE_SIZE - offset, "%s \t(0x%lX)\t 0x%08X\n", "DSCRPTR_QUEUE_CONTENT", DX_DSCRPTR_QUEUE_CONTENT_REG_OFFSET, register_value);
299 	return offset;
300 }
301 
ssi_sys_help_show(struct kobject * kobj,struct kobj_attribute * attr,char * buf)302 static ssize_t ssi_sys_help_show(struct kobject *kobj,
303 				 struct kobj_attribute *attr, char *buf)
304 {
305 	char *help_str[] = {
306 				"cat reg_dump              ", "Print several of CC register values",
307 		#if defined CC_CYCLE_COUNT
308 				"cat stats_host            ", "Print host statistics",
309 				"echo <number> > stats_host", "Clear host statistics database",
310 				"cat stats_cc              ", "Print CC statistics",
311 				"echo <number> > stats_cc  ", "Clear CC statistics database",
312 		#endif
313 				};
314 	int i = 0, offset = 0;
315 
316 	offset += scnprintf(buf + offset, PAGE_SIZE - offset, "Usage:\n");
317 	for (i = 0; i < ARRAY_SIZE(help_str); i += 2)
318 	   offset += scnprintf(buf + offset, PAGE_SIZE - offset, "%s\t\t%s\n", help_str[i], help_str[i + 1]);
319 
320 	return offset;
321 }
322 
323 /********************************************************
324  *		SYSFS objects				*
325  ********************************************************/
326 /*
327  * Structure used to create a directory
328  * and its attributes in sysfs.
329  */
330 struct sys_dir {
331 	struct kobject *sys_dir_kobj;
332 	struct attribute_group sys_dir_attr_group;
333 	struct attribute **sys_dir_attr_list;
334 	u32 num_of_attrs;
335 	struct ssi_drvdata *drvdata; /* Associated driver context */
336 };
337 
338 /* top level directory structures */
339 static struct sys_dir sys_top_dir;
340 
341 /* TOP LEVEL ATTRIBUTES */
342 static struct kobj_attribute ssi_sys_top_level_attrs[] = {
343 	__ATTR(dump_regs, 0444, ssi_sys_regdump_show, NULL),
344 	__ATTR(help, 0444, ssi_sys_help_show, NULL),
345 #if defined CC_CYCLE_COUNT
346 	__ATTR(stats_host, 0664, ssi_sys_stat_host_db_show, ssi_sys_stats_host_db_clear),
347 	__ATTR(stats_cc, 0664, ssi_sys_stat_cc_db_show, ssi_sys_stats_cc_db_clear),
348 #endif
349 
350 };
351 
sys_get_drvdata(void)352 static struct ssi_drvdata *sys_get_drvdata(void)
353 {
354 	/* TODO: supporting multiple SeP devices would require avoiding
355 	 * global "top_dir" and finding associated "top_dir" by traversing
356 	 * up the tree to the kobject which matches one of the top_dir's
357 	 */
358 	return sys_top_dir.drvdata;
359 }
360 
sys_init_dir(struct sys_dir * sys_dir,struct ssi_drvdata * drvdata,struct kobject * parent_dir_kobj,const char * dir_name,struct kobj_attribute * attrs,u32 num_of_attrs)361 static int sys_init_dir(struct sys_dir *sys_dir, struct ssi_drvdata *drvdata,
362 			struct kobject *parent_dir_kobj, const char *dir_name,
363 			struct kobj_attribute *attrs, u32 num_of_attrs)
364 {
365 	int i;
366 
367 	memset(sys_dir, 0, sizeof(struct sys_dir));
368 
369 	sys_dir->drvdata = drvdata;
370 
371 	/* initialize directory kobject */
372 	sys_dir->sys_dir_kobj =
373 		kobject_create_and_add(dir_name, parent_dir_kobj);
374 
375 	if (!(sys_dir->sys_dir_kobj))
376 		return -ENOMEM;
377 	/* allocate memory for directory's attributes list */
378 	sys_dir->sys_dir_attr_list =
379 		kzalloc(sizeof(struct attribute *) * (num_of_attrs + 1),
380 			GFP_KERNEL);
381 
382 	if (!(sys_dir->sys_dir_attr_list)) {
383 		kobject_put(sys_dir->sys_dir_kobj);
384 		return -ENOMEM;
385 	}
386 
387 	sys_dir->num_of_attrs = num_of_attrs;
388 
389 	/* initialize attributes list */
390 	for (i = 0; i < num_of_attrs; ++i)
391 		sys_dir->sys_dir_attr_list[i] = &attrs[i].attr;
392 
393 	/* last list entry should be NULL */
394 	sys_dir->sys_dir_attr_list[num_of_attrs] = NULL;
395 
396 	sys_dir->sys_dir_attr_group.attrs = sys_dir->sys_dir_attr_list;
397 
398 	return sysfs_create_group(sys_dir->sys_dir_kobj,
399 			&sys_dir->sys_dir_attr_group);
400 }
401 
sys_free_dir(struct sys_dir * sys_dir)402 static void sys_free_dir(struct sys_dir *sys_dir)
403 {
404 	if (!sys_dir)
405 		return;
406 
407 	kfree(sys_dir->sys_dir_attr_list);
408 
409 	if (sys_dir->sys_dir_kobj)
410 		kobject_put(sys_dir->sys_dir_kobj);
411 }
412 
ssi_sysfs_init(struct kobject * sys_dev_obj,struct ssi_drvdata * drvdata)413 int ssi_sysfs_init(struct kobject *sys_dev_obj, struct ssi_drvdata *drvdata)
414 {
415 	int retval;
416 
417 #if defined CC_CYCLE_COUNT
418 	/* Init. statistics */
419 	init_db(stat_host_db);
420 	init_db(stat_cc_db);
421 #endif
422 
423 	SSI_LOG_ERR("setup sysfs under %s\n", sys_dev_obj->name);
424 
425 	/* Initialize top directory */
426 	retval = sys_init_dir(&sys_top_dir, drvdata, sys_dev_obj, "cc_info",
427 			      ssi_sys_top_level_attrs,
428 			      ARRAY_SIZE(ssi_sys_top_level_attrs));
429 	return retval;
430 }
431 
ssi_sysfs_fini(void)432 void ssi_sysfs_fini(void)
433 {
434 	sys_free_dir(&sys_top_dir);
435 }
436 
437 #endif /*ENABLE_CC_SYSFS*/
438 
439