Merge tag 'mtd/for-5.20' of git://git.kernel.org/pub/scm/linux/kernel/git/mtd/linux
[linux-2.6-microblaze.git] / kernel / printk / index.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Userspace indexing of printk formats
4  */
5
6 #include <linux/debugfs.h>
7 #include <linux/module.h>
8 #include <linux/printk.h>
9 #include <linux/slab.h>
10 #include <linux/string_helpers.h>
11
12 #include "internal.h"
13
14 extern struct pi_entry *__start_printk_index[];
15 extern struct pi_entry *__stop_printk_index[];
16
17 /* The base dir for module formats, typically debugfs/printk/index/ */
18 static struct dentry *dfs_index;
19
20 static struct pi_entry *pi_get_entry(const struct module *mod, loff_t pos)
21 {
22         struct pi_entry **entries;
23         unsigned int nr_entries;
24
25 #ifdef CONFIG_MODULES
26         if (mod) {
27                 entries = mod->printk_index_start;
28                 nr_entries = mod->printk_index_size;
29         } else
30 #endif
31         {
32                 /* vmlinux, comes from linker symbols */
33                 entries = __start_printk_index;
34                 nr_entries = __stop_printk_index - __start_printk_index;
35         }
36
37         if (pos >= nr_entries)
38                 return NULL;
39
40         return entries[pos];
41 }
42
43 static void *pi_next(struct seq_file *s, void *v, loff_t *pos)
44 {
45         const struct module *mod = s->file->f_inode->i_private;
46         struct pi_entry *entry = pi_get_entry(mod, *pos);
47
48         (*pos)++;
49
50         return entry;
51 }
52
53 static void *pi_start(struct seq_file *s, loff_t *pos)
54 {
55         /*
56          * Make show() print the header line. Do not update *pos because
57          * pi_next() still has to return the entry at index 0 later.
58          */
59         if (*pos == 0)
60                 return SEQ_START_TOKEN;
61
62         return pi_next(s, NULL, pos);
63 }
64
65 /*
66  * We need both ESCAPE_ANY and explicit characters from ESCAPE_SPECIAL in @only
67  * because otherwise ESCAPE_NAP will cause double quotes and backslashes to be
68  * ignored for quoting.
69  */
70 #define seq_escape_printf_format(s, src) \
71         seq_escape_str(s, src, ESCAPE_ANY | ESCAPE_NAP | ESCAPE_APPEND, "\"\\")
72
73 static int pi_show(struct seq_file *s, void *v)
74 {
75         const struct pi_entry *entry = v;
76         int level = LOGLEVEL_DEFAULT;
77         enum printk_info_flags flags = 0;
78         u16 prefix_len = 0;
79
80         if (v == SEQ_START_TOKEN) {
81                 seq_puts(s, "# <level/flags> filename:line function \"format\"\n");
82                 return 0;
83         }
84
85         if (!entry->fmt)
86                 return 0;
87
88         if (entry->level)
89                 printk_parse_prefix(entry->level, &level, &flags);
90         else
91                 prefix_len = printk_parse_prefix(entry->fmt, &level, &flags);
92
93
94         if (flags & LOG_CONT) {
95                 /*
96                  * LOGLEVEL_DEFAULT here means "use the same level as the
97                  * message we're continuing from", not the default message
98                  * loglevel, so don't display it as such.
99                  */
100                 if (level == LOGLEVEL_DEFAULT)
101                         seq_puts(s, "<c>");
102                 else
103                         seq_printf(s, "<%d,c>", level);
104         } else
105                 seq_printf(s, "<%d>", level);
106
107         seq_printf(s, " %s:%d %s \"", entry->file, entry->line, entry->func);
108         if (entry->subsys_fmt_prefix)
109                 seq_escape_printf_format(s, entry->subsys_fmt_prefix);
110         seq_escape_printf_format(s, entry->fmt + prefix_len);
111         seq_puts(s, "\"\n");
112
113         return 0;
114 }
115
116 static void pi_stop(struct seq_file *p, void *v) { }
117
118 static const struct seq_operations dfs_index_sops = {
119         .start = pi_start,
120         .next  = pi_next,
121         .show  = pi_show,
122         .stop  = pi_stop,
123 };
124
125 DEFINE_SEQ_ATTRIBUTE(dfs_index);
126
127 #ifdef CONFIG_MODULES
128 static const char *pi_get_module_name(struct module *mod)
129 {
130         return mod ? mod->name : "vmlinux";
131 }
132 #else
133 static const char *pi_get_module_name(struct module *mod)
134 {
135         return "vmlinux";
136 }
137 #endif
138
139 static void pi_create_file(struct module *mod)
140 {
141         debugfs_create_file(pi_get_module_name(mod), 0444, dfs_index,
142                                        mod, &dfs_index_fops);
143 }
144
145 #ifdef CONFIG_MODULES
146 static void pi_remove_file(struct module *mod)
147 {
148         debugfs_remove(debugfs_lookup(pi_get_module_name(mod), dfs_index));
149 }
150
151 static int pi_module_notify(struct notifier_block *nb, unsigned long op,
152                             void *data)
153 {
154         struct module *mod = data;
155
156         switch (op) {
157         case MODULE_STATE_COMING:
158                 pi_create_file(mod);
159                 break;
160         case MODULE_STATE_GOING:
161                 pi_remove_file(mod);
162                 break;
163         default: /* we don't care about other module states */
164                 break;
165         }
166
167         return NOTIFY_OK;
168 }
169
170 static struct notifier_block module_printk_fmts_nb = {
171         .notifier_call = pi_module_notify,
172 };
173
174 static void __init pi_setup_module_notifier(void)
175 {
176         register_module_notifier(&module_printk_fmts_nb);
177 }
178 #else
179 static inline void __init pi_setup_module_notifier(void) { }
180 #endif
181
182 static int __init pi_init(void)
183 {
184         struct dentry *dfs_root = debugfs_create_dir("printk", NULL);
185
186         dfs_index = debugfs_create_dir("index", dfs_root);
187         pi_setup_module_notifier();
188         pi_create_file(NULL);
189
190         return 0;
191 }
192
193 /* debugfs comes up on core and must be initialised first */
194 postcore_initcall(pi_init);