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