1 /*
2 * Copyright (C) 2022 HiSilicon (Shanghai) Technologies CO., LIMITED.
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 */
18
19 #include <linux/module.h>
20 #include <linux/kernel.h>
21 #include <linux/printk.h>
22 #include <linux/fs.h>
23 #include <linux/seq_file.h>
24 #include <linux/proc_fs.h>
25 #include <linux/slab.h>
26 #include <asm/uaccess.h>
27 #include <linux/version.h>
28 #include <linux/mutex.h>
29 #include <linux/string.h>
30 #include <linux/major.h>
31 #include <linux/uaccess.h>
32 #include <linux/file.h>
33 #include <linux/init.h>
34
35 #include "securec.h"
36 #include "hi_osal.h"
37
38 #define OSAL_PROC_MODE 0440
39 #define OSAL_PROC_PRINT_BUF_SIZE 256
40 #define OSAL_USER_LEN 4
41 #define OSAL_ENG_LEN 3
42 static struct osal_list_head g_msp_list;
43 osal_proc_entry *g_msp_dir = NULL;
44 static char *g_is_user_mode = NULL;
45 static struct osal_list_head g_hisi_list;
46 static osal_proc_entry *g_hisi_dir = NULL;
47 static osal_proc_entry g_hisi_proc_null;
48
49 DEFINE_MUTEX(mutex);
50
51 /* this function get the buildvarant in cmdline
52 * when the buildvariant is eng, return value is OSAL_BUILDVARIANT_ENG
53 * when the buildvariant is user, return value is OSAL_BUILDVARIANT_USER
54 * when the buildvariant is nono,return value is OSAL_BUILDVARIANT_NONE
55 * */
osal_get_buildvariant(void)56 int osal_get_buildvariant(void)
57 {
58 if (g_is_user_mode == NULL) {
59 return OSAL_BUILDVARIANT_NONE;
60 }
61 if (strncmp(g_is_user_mode, "user", OSAL_USER_LEN) == 0) {
62 return OSAL_BUILDVARIANT_USER;
63 } else if (strncmp(g_is_user_mode, "eng", OSAL_ENG_LEN) == 0) {
64 return OSAL_BUILDVARIANT_ENG;
65 }
66 return OSAL_BUILDVARIANT_NONE;
67 }
68
osal_get_build_variant(char * str)69 int __init osal_get_build_variant(char *str)
70 {
71 if (str == NULL) {
72 return 1;
73 }
74 g_is_user_mode = str;
75 return 1;
76 }
77 __setup("buildvariant=", osal_get_build_variant);
78
osal_seq_show(struct seq_file * s,void * p)79 static int osal_seq_show(struct seq_file *s, void *p)
80 {
81 osal_proc_entry *entry = s->private;
82
83 entry->seqfile = s;
84
85 entry->read((void*)entry->seqfile, entry->private);
86 return 0;
87 }
88
osal_proc_parse_para(const char * buf,unsigned int buf_size,char (* argv)[PROC_CMD_SINGEL_LENGTH_MAX],unsigned int count,unsigned int * argc)89 static void osal_proc_parse_para(const char *buf, unsigned int buf_size,
90 char (*argv)[PROC_CMD_SINGEL_LENGTH_MAX],
91 unsigned int count,
92 unsigned int *argc)
93 {
94 int i = 0;
95 int j = 0;
96 int k = 0;
97
98 for (i = 0; i < count; i++) {
99 /* ignore SPACE */
100 while ((i < (buf_size - 1)) && buf[i] == 0x20) {
101 i++;
102 }
103
104 if (buf[i] == 0xa) {
105 break;
106 }
107
108 if ((k >= (PROC_CMD_SINGEL_LENGTH_MAX - 1)) || (j >= (PROC_CMD_NUM_MAX - 1))
109 || i == buf_size - 1) {
110 return;
111 }
112
113 argv[j][k] = buf[i];
114 k++;
115
116 if ((buf[i + 1] == 0x20) || (buf[i + 1] == 0xa)) {
117 argv[j][k] = '\0';
118 k = 0;
119 j++;
120 }
121 }
122
123 *argc = j;
124 }
125
osal_proc_cmd_call(osal_proc_entry * entry,unsigned int argc,char (* argv)[PROC_CMD_SINGEL_LENGTH_MAX])126 static int osal_proc_cmd_call(osal_proc_entry *entry, unsigned int argc, char (*argv)[PROC_CMD_SINGEL_LENGTH_MAX])
127 {
128 unsigned int i = 0;
129
130 for (i = 0; i < entry->cmd_cnt; i++) {
131 if (osal_strncmp(entry->cmd_list[i].name, strlen(entry->cmd_list[i].name), argv[0], strlen(argv[0])) == 0) {
132 if (entry->cmd_list[i].handler != NULL) {
133 return entry->cmd_list[i].handler(argc, argv, entry->private);
134 }
135 }
136 }
137
138 return -1;
139 }
140
osal_proc_write(struct file * file,const char __user * buf,size_t count,loff_t * ppos)141 static ssize_t osal_proc_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
142 {
143 int ret = -1;
144 unsigned int argc = 0;
145 char proc_buf[PROC_CMD_ALL_LENGTH_MAX] = {0};
146 char argv[PROC_CMD_NUM_MAX][PROC_CMD_SINGEL_LENGTH_MAX];
147 osal_proc_entry *entry = NULL;
148
149 if ((buf == NULL) || (file == NULL)) {
150 return -1;
151 }
152
153 entry = ((struct seq_file *)(file->private_data))->private;
154 if (entry == NULL) {
155 printk("[%s]ERROR:can not find entry!\n", __FUNCTION__);
156 return -1;
157 }
158
159 if (entry->write) {
160 return entry->write(entry, buf, count, ppos);
161 }
162
163 if ((count >= PROC_CMD_ALL_LENGTH_MAX) || (count == 0)) {
164 printk("[%s]ERROR:count (%lu) is to large or Zero!\n", __FUNCTION__, (unsigned long)count);
165 return -1;
166 }
167
168 if (copy_from_user(proc_buf, buf, count)) {
169 return -1;
170 }
171
172 proc_buf[PROC_CMD_ALL_LENGTH_MAX - 1] = '\0';
173
174 (void)memset_s(&argv[0][0], PROC_CMD_NUM_MAX*PROC_CMD_SINGEL_LENGTH_MAX, 0,
175 PROC_CMD_NUM_MAX*PROC_CMD_SINGEL_LENGTH_MAX);
176 osal_proc_parse_para(proc_buf, PROC_CMD_ALL_LENGTH_MAX, &argv[0], count, &argc);
177 if (argc == 0) {
178 printk("[%s]ERROR:not valid arg!\n", __FUNCTION__);
179 return -1;
180 }
181
182 ret = osal_proc_cmd_call(entry, argc, argv);
183 if (ret != 0) {
184 printk("%s: cmd is not find or param is wrong!\n", __FUNCTION__);
185 argc = 1;
186 (void)strncpy_s(argv[0], PROC_CMD_SINGEL_LENGTH_MAX, "help", strlen("help"));
187 (void)osal_proc_cmd_call(entry, argc, argv);
188 }
189
190 return count;
191 }
192
osal_proc_open(struct inode * inode,struct file * file)193 static int osal_proc_open(struct inode *inode, struct file *file)
194 {
195 #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 10, 0)
196 osal_proc_entry *item = PDE(inode)->data;
197 #else
198 osal_proc_entry *item = PDE_DATA(inode);
199 #endif
200
201 if ((item != NULL) && (item->read != NULL)) {
202 return single_open(file, osal_seq_show, item);
203 }
204
205 return -ENOSYS;
206 }
207
208 #if defined(LINUX_VERSION_CODE) && (LINUX_VERSION_CODE < KERNEL_VERSION(5,0,0))
209 static struct file_operations g_osal_proc_ops = {
210 .owner = THIS_MODULE,
211 .open = osal_proc_open,
212 .read = seq_read,
213 .write = osal_proc_write,
214 .llseek = seq_lseek,
215 .release = single_release,
216 };
217 #else
218 static struct proc_ops g_osal_proc_ops = {
219 .proc_open = osal_proc_open,
220 .proc_read = seq_read,
221 .proc_write = osal_proc_write,
222 .proc_lseek = seq_lseek,
223 .proc_release = single_release,
224 };
225 #endif
226
osal_proc_find_node(const char * name,struct osal_list_head * list)227 osal_proc_entry *osal_proc_find_node(const char *name, struct osal_list_head *list)
228 {
229 osal_proc_entry *node = NULL;
230
231 osal_list_for_each_entry(node, list, node) {
232 if (osal_strncmp(node->name, strlen(node->name), name, strlen(name)) == 0) {
233 return node;
234 }
235 }
236
237 return NULL;
238 }
239
osal_proc_create_dir(const char * name,osal_proc_entry * parent,struct osal_list_head * list)240 osal_proc_entry *osal_proc_create_dir(const char *name, osal_proc_entry *parent, struct osal_list_head *list)
241 {
242 struct proc_dir_entry *proc = NULL;
243 osal_proc_entry *dir_proc = NULL;
244 errno_t err;
245
246 if ((name == NULL) || (list == NULL)) {
247 return NULL;
248 }
249
250 mutex_lock(&mutex);
251
252 dir_proc = osal_proc_find_node(name, list);
253 if (dir_proc != NULL) {
254 osal_printk("%s - name repeat\n", __FUNCTION__);
255 mutex_unlock(&mutex);
256 return NULL;
257 }
258
259 dir_proc = kmalloc(sizeof(osal_proc_entry), GFP_KERNEL);
260 if (dir_proc == NULL) {
261 osal_printk("%s - kmalloc failed!\n", __FUNCTION__);
262 mutex_unlock(&mutex);
263 return NULL;
264 }
265
266 (void)memset_s(dir_proc, sizeof(osal_proc_entry), 0, sizeof(osal_proc_entry));
267
268 err = strncpy_s(dir_proc->name, sizeof(dir_proc->name), name, strlen(name));
269 if (err != EOK) {
270 osal_printk("[%s][%d]:secure func call error\n", __FUNCTION__, __LINE__);
271 }
272
273 if (parent != NULL) {
274 proc = proc_mkdir(name, parent->proc_dir_entry);
275 } else {
276 proc = proc_mkdir(name, NULL);
277 }
278 if (proc == NULL) {
279 osal_printk("%s - proc_mkdir failed!\n", __FUNCTION__);
280 kfree(dir_proc);
281 dir_proc = NULL;
282 mutex_unlock(&mutex);
283 return NULL;
284 }
285 dir_proc->proc_dir_entry = proc;
286
287 osal_list_add_tail(&(dir_proc->node), list);
288
289 mutex_unlock(&mutex);
290
291 return dir_proc;
292 }
293
294
osal_proc_create_entry(const char * name,osal_proc_entry * parent,struct osal_list_head * list)295 osal_proc_entry *osal_proc_create_entry(const char *name, osal_proc_entry *parent, struct osal_list_head *list)
296 {
297 struct proc_dir_entry *entry = NULL;
298 osal_proc_entry *sentry = NULL;
299 errno_t err;
300
301 if ((name == NULL) || (list == NULL)) {
302 return NULL;
303 }
304
305 mutex_lock(&mutex);
306 sentry = osal_proc_find_node(name, list);
307 if (sentry != NULL) {
308 osal_printk("%s - name repeat\n", __FUNCTION__);
309 mutex_unlock(&mutex);
310 return NULL;
311 }
312
313 sentry = kmalloc(sizeof(osal_proc_entry), GFP_KERNEL);
314 if (sentry == NULL) {
315 osal_printk("%s - kmalloc failed!\n", __FUNCTION__);
316 mutex_unlock(&mutex);
317 return NULL;
318 }
319
320 (void)memset_s(sentry, sizeof(osal_proc_entry), 0, sizeof(osal_proc_entry));
321
322 err = strncpy_s(sentry->name, sizeof(sentry->name), name, strlen(name));
323 if (err != EOK) {
324 osal_printk("[%s][%d]:secure func call error\n", __FUNCTION__, __LINE__);
325 kfree(sentry);
326 mutex_unlock(&mutex);
327 return NULL;
328 }
329
330 if (parent == NULL) {
331 entry = proc_create_data(name, OSAL_PROC_MODE, NULL, &g_osal_proc_ops, sentry);
332 } else {
333 entry = proc_create_data(name, OSAL_PROC_MODE, parent->proc_dir_entry, &g_osal_proc_ops, sentry);
334 }
335
336 if (entry == NULL) {
337 osal_printk("%s - create_proc_entry failed!\n", __FUNCTION__);
338 kfree(sentry);
339 sentry = NULL;
340 mutex_unlock(&mutex);
341 return NULL;
342 }
343
344 sentry->proc_dir_entry = entry;
345
346 osal_list_add_tail(&(sentry->node), list);
347
348 mutex_unlock(&mutex);
349
350 return sentry;
351 }
352
osal_proc_destory_node(const char * name,osal_proc_entry * parent,struct osal_list_head * list)353 void osal_proc_destory_node(const char *name, osal_proc_entry *parent, struct osal_list_head *list)
354 {
355 osal_proc_entry *dir_proc = NULL;
356
357 if ((name == NULL) || (list == NULL)) {
358 return;
359 }
360
361 mutex_lock(&mutex);
362
363 dir_proc = osal_proc_find_node(name, list);
364 if (dir_proc == NULL) {
365 mutex_unlock(&mutex);
366 return;
367 }
368
369 osal_list_del(&(dir_proc->node));
370
371 if (parent != NULL) {
372 remove_proc_entry(name, parent->proc_dir_entry);
373 } else {
374 remove_proc_entry(name, NULL);
375 }
376
377 kfree(dir_proc);
378
379 mutex_unlock(&mutex);
380 }
381
osal_get_hisi_entry(void)382 void *osal_get_hisi_entry(void)
383 {
384 if (g_hisi_dir != NULL) {
385 return (void *)g_hisi_dir->proc_dir_entry;
386 }
387
388 return NULL;
389 }
390
osal_get_msp_entry(void)391 void *osal_get_msp_entry(void)
392 {
393 if (g_msp_dir != NULL) {
394 return (void *)g_msp_dir->proc_dir_entry;
395 }
396
397 return NULL;
398 }
399
400
401 // for user space
osal_proc_user_create_dir(const char * name)402 osal_proc_entry *osal_proc_user_create_dir(const char *name)
403 {
404 return osal_proc_create_dir(name, g_hisi_dir, &g_hisi_list);
405 }
406 EXPORT_SYMBOL(osal_proc_user_create_dir);
407
osal_proc_user_destory_dir(const char * name)408 void osal_proc_user_destory_dir(const char *name)
409 {
410 return osal_proc_destory_node(name, g_hisi_dir, &g_hisi_list);
411 }
412 EXPORT_SYMBOL(osal_proc_user_destory_dir);
413
osal_proc_user_add(const char * name,osal_proc_entry * dir)414 osal_proc_entry *osal_proc_user_add(const char *name, osal_proc_entry *dir)
415 {
416 return osal_proc_create_entry(name, dir, &g_hisi_list);
417 }
418 EXPORT_SYMBOL(osal_proc_user_add);
419
osal_proc_user_remove(const char * name,osal_proc_entry * dir)420 void osal_proc_user_remove(const char *name, osal_proc_entry *dir)
421 {
422 return osal_proc_destory_node(name, g_hisi_dir, &g_hisi_list);
423 }
424 EXPORT_SYMBOL(osal_proc_user_remove);
425
426
osal_proc_init(void)427 int osal_proc_init(void)
428 {
429 OSAL_INIT_LIST_HEAD(&g_msp_list);
430 g_msp_dir = osal_proc_create_dir("msp", OSAL_NULL, &g_msp_list);
431 if (g_msp_dir == OSAL_NULL) {
432 osal_printk("proc msp mkdir error!\n");
433 }
434
435 OSAL_INIT_LIST_HEAD(&g_hisi_list);
436 g_hisi_dir = osal_proc_create_dir("hisi", OSAL_NULL, &g_hisi_list);
437 if (g_hisi_dir == OSAL_NULL) {
438 osal_printk("proc hisi mkdir error!\n");
439 }
440
441 return 0;
442 }
443 EXPORT_SYMBOL(osal_proc_init);
444
445
osal_proc_exit(void)446 void osal_proc_exit(void)
447 {
448 osal_proc_destory_node("msp", OSAL_NULL, &g_msp_list);
449 osal_proc_destory_node("hisi", OSAL_NULL, &g_hisi_list);
450 }
451 EXPORT_SYMBOL(osal_proc_exit);
452
453 #ifdef MODULE
454 module_init(osal_proc_init);
455 module_exit(osal_proc_exit);
456 #elif defined(CFG_HI_USER_DRV)
457 subsys_initcall(osal_proc_init);
458 module_exit(osal_proc_exit);
459 #endif
460
osal_proc_add(const char * name,unsigned long name_size)461 osal_proc_entry *osal_proc_add(const char *name, unsigned long name_size)
462 {
463 if ((name_size >= OSAL_PROC_NAME_LEN) || (strlen(name) > name_size)) {
464 return NULL;
465 }
466 if (osal_get_buildvariant() == OSAL_BUILDVARIANT_USER) {
467 return &g_hisi_proc_null;
468 }
469 return osal_proc_create_entry(name, g_msp_dir, &g_msp_list);
470 }
471 EXPORT_SYMBOL(osal_proc_add);
472
osal_proc_remove(const char * name,unsigned long name_size)473 void osal_proc_remove(const char *name, unsigned long name_size)
474 {
475 if ((name_size >= OSAL_PROC_NAME_LEN) || (strlen(name) > name_size)) {
476 return;
477 }
478 if (osal_get_buildvariant() == OSAL_BUILDVARIANT_USER) {
479 return;
480 }
481
482 return osal_proc_destory_node(name, g_msp_dir, &g_msp_list);
483 }
484 EXPORT_SYMBOL(osal_proc_remove);
485
486 /*
487 * echo string to current terminal display(serial console or tty).
488 * this implement implicit that current task file handle '0' must be terminal device file.
489 * otherwise do nothing.
490 */
osal_proc_print_vargs(char * buf,unsigned int size,const char * fmt,va_list args)491 void osal_proc_print_vargs(char *buf, unsigned int size, const char *fmt, va_list args)
492 {
493 #if !(0 == HI_PROC_SUPPORT)
494
495 #define DEFAULT_ECHO_DEVICE_HANDLE (0)
496
497 struct kstat stat;
498 int ret;
499
500 if (!buf || size == 0) {
501 return;
502 }
503
504 ret = vfs_fstat(DEFAULT_ECHO_DEVICE_HANDLE, &stat);
505 if (ret) {
506 printk("default echo device handle(%u) invalid!\n", DEFAULT_ECHO_DEVICE_HANDLE);
507 return;
508 }
509
510 ret = vsnprintf_s(buf, size, size - 1, fmt, args);
511 if (ret < 0) {
512 printk("%s[%d]: snprintf_s failed\n", __FUNCTION__, __LINE__);
513 return;
514 }
515
516 /* echo device must be chrdev and major number must be TTYAUX_MAJOR or UNIX98_PTY_SLAVE_MAJOR */
517 if (S_ISCHR(stat.mode) && (MAJOR(stat.rdev) == TTYAUX_MAJOR || MAJOR(stat.rdev) == UNIX98_PTY_SLAVE_MAJOR)) {
518 struct file *file = fget(DEFAULT_ECHO_DEVICE_HANDLE);
519 if (file) {
520 mm_segment_t st_old_fs = get_fs();
521 /* file pos is invalid for chrdev */
522 loff_t pos = 0;
523
524 set_fs(KERNEL_DS);
525 ret = vfs_write(file, buf, strlen(buf), &pos);
526 if (ret < 0) {
527 printk("write to echo device failed(%d)!\n", ret);
528 }
529 set_fs(st_old_fs);
530
531 fput(file);
532 }
533 } else {
534 printk("default echo device is invalid!\n");
535 }
536
537 #endif
538 }
539
osal_proc_print(void * seqfile,const char * fmt,...)540 int osal_proc_print(void *seqfile, const char *fmt, ...)
541 {
542 va_list args;
543 int r = 0;
544 if (osal_get_buildvariant() == OSAL_BUILDVARIANT_USER) {
545 return r;
546 }
547
548 va_start(args, fmt);
549 #if LINUX_VERSION_CODE < KERNEL_VERSION(4, 3, 0)
550 r = seq_vprintf(seqfile, fmt, args);
551 #else
552 seq_vprintf(seqfile, fmt, args);
553 #endif
554 va_end(args);
555
556 return r;
557 }
558 EXPORT_SYMBOL(osal_proc_print);
559
osal_printk(const char * fmt,...)560 int osal_printk(const char *fmt, ...)
561 {
562 va_list args;
563 int ret;
564
565 if (!fmt) {
566 return 0;
567 }
568
569 va_start(args, fmt);
570 ret = vprintk(fmt, args);
571 va_end(args);
572 return ret;
573 }
574 EXPORT_SYMBOL(osal_printk);
575