• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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