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