162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Userspace indexing of printk formats 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <linux/debugfs.h> 762306a36Sopenharmony_ci#include <linux/module.h> 862306a36Sopenharmony_ci#include <linux/printk.h> 962306a36Sopenharmony_ci#include <linux/slab.h> 1062306a36Sopenharmony_ci#include <linux/string_helpers.h> 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include "internal.h" 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ciextern struct pi_entry *__start_printk_index[]; 1562306a36Sopenharmony_ciextern struct pi_entry *__stop_printk_index[]; 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci/* The base dir for module formats, typically debugfs/printk/index/ */ 1862306a36Sopenharmony_cistatic struct dentry *dfs_index; 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_cistatic struct pi_entry *pi_get_entry(const struct module *mod, loff_t pos) 2162306a36Sopenharmony_ci{ 2262306a36Sopenharmony_ci struct pi_entry **entries; 2362306a36Sopenharmony_ci unsigned int nr_entries; 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#ifdef CONFIG_MODULES 2662306a36Sopenharmony_ci if (mod) { 2762306a36Sopenharmony_ci entries = mod->printk_index_start; 2862306a36Sopenharmony_ci nr_entries = mod->printk_index_size; 2962306a36Sopenharmony_ci } else 3062306a36Sopenharmony_ci#endif 3162306a36Sopenharmony_ci { 3262306a36Sopenharmony_ci /* vmlinux, comes from linker symbols */ 3362306a36Sopenharmony_ci entries = __start_printk_index; 3462306a36Sopenharmony_ci nr_entries = __stop_printk_index - __start_printk_index; 3562306a36Sopenharmony_ci } 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci if (pos >= nr_entries) 3862306a36Sopenharmony_ci return NULL; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci return entries[pos]; 4162306a36Sopenharmony_ci} 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_cistatic void *pi_next(struct seq_file *s, void *v, loff_t *pos) 4462306a36Sopenharmony_ci{ 4562306a36Sopenharmony_ci const struct module *mod = s->file->f_inode->i_private; 4662306a36Sopenharmony_ci struct pi_entry *entry = pi_get_entry(mod, *pos); 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci (*pos)++; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci return entry; 5162306a36Sopenharmony_ci} 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_cistatic void *pi_start(struct seq_file *s, loff_t *pos) 5462306a36Sopenharmony_ci{ 5562306a36Sopenharmony_ci /* 5662306a36Sopenharmony_ci * Make show() print the header line. Do not update *pos because 5762306a36Sopenharmony_ci * pi_next() still has to return the entry at index 0 later. 5862306a36Sopenharmony_ci */ 5962306a36Sopenharmony_ci if (*pos == 0) 6062306a36Sopenharmony_ci return SEQ_START_TOKEN; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci return pi_next(s, NULL, pos); 6362306a36Sopenharmony_ci} 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci/* 6662306a36Sopenharmony_ci * We need both ESCAPE_ANY and explicit characters from ESCAPE_SPECIAL in @only 6762306a36Sopenharmony_ci * because otherwise ESCAPE_NAP will cause double quotes and backslashes to be 6862306a36Sopenharmony_ci * ignored for quoting. 6962306a36Sopenharmony_ci */ 7062306a36Sopenharmony_ci#define seq_escape_printf_format(s, src) \ 7162306a36Sopenharmony_ci seq_escape_str(s, src, ESCAPE_ANY | ESCAPE_NAP | ESCAPE_APPEND, "\"\\") 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_cistatic int pi_show(struct seq_file *s, void *v) 7462306a36Sopenharmony_ci{ 7562306a36Sopenharmony_ci const struct pi_entry *entry = v; 7662306a36Sopenharmony_ci int level = LOGLEVEL_DEFAULT; 7762306a36Sopenharmony_ci enum printk_info_flags flags = 0; 7862306a36Sopenharmony_ci u16 prefix_len = 0; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci if (v == SEQ_START_TOKEN) { 8162306a36Sopenharmony_ci seq_puts(s, "# <level/flags> filename:line function \"format\"\n"); 8262306a36Sopenharmony_ci return 0; 8362306a36Sopenharmony_ci } 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci if (!entry->fmt) 8662306a36Sopenharmony_ci return 0; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci if (entry->level) 8962306a36Sopenharmony_ci printk_parse_prefix(entry->level, &level, &flags); 9062306a36Sopenharmony_ci else 9162306a36Sopenharmony_ci prefix_len = printk_parse_prefix(entry->fmt, &level, &flags); 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci if (flags & LOG_CONT) { 9562306a36Sopenharmony_ci /* 9662306a36Sopenharmony_ci * LOGLEVEL_DEFAULT here means "use the same level as the 9762306a36Sopenharmony_ci * message we're continuing from", not the default message 9862306a36Sopenharmony_ci * loglevel, so don't display it as such. 9962306a36Sopenharmony_ci */ 10062306a36Sopenharmony_ci if (level == LOGLEVEL_DEFAULT) 10162306a36Sopenharmony_ci seq_puts(s, "<c>"); 10262306a36Sopenharmony_ci else 10362306a36Sopenharmony_ci seq_printf(s, "<%d,c>", level); 10462306a36Sopenharmony_ci } else 10562306a36Sopenharmony_ci seq_printf(s, "<%d>", level); 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci seq_printf(s, " %s:%d %s \"", entry->file, entry->line, entry->func); 10862306a36Sopenharmony_ci if (entry->subsys_fmt_prefix) 10962306a36Sopenharmony_ci seq_escape_printf_format(s, entry->subsys_fmt_prefix); 11062306a36Sopenharmony_ci seq_escape_printf_format(s, entry->fmt + prefix_len); 11162306a36Sopenharmony_ci seq_puts(s, "\"\n"); 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci return 0; 11462306a36Sopenharmony_ci} 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_cistatic void pi_stop(struct seq_file *p, void *v) { } 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_cistatic const struct seq_operations dfs_index_sops = { 11962306a36Sopenharmony_ci .start = pi_start, 12062306a36Sopenharmony_ci .next = pi_next, 12162306a36Sopenharmony_ci .show = pi_show, 12262306a36Sopenharmony_ci .stop = pi_stop, 12362306a36Sopenharmony_ci}; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ciDEFINE_SEQ_ATTRIBUTE(dfs_index); 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci#ifdef CONFIG_MODULES 12862306a36Sopenharmony_cistatic const char *pi_get_module_name(struct module *mod) 12962306a36Sopenharmony_ci{ 13062306a36Sopenharmony_ci return mod ? mod->name : "vmlinux"; 13162306a36Sopenharmony_ci} 13262306a36Sopenharmony_ci#else 13362306a36Sopenharmony_cistatic const char *pi_get_module_name(struct module *mod) 13462306a36Sopenharmony_ci{ 13562306a36Sopenharmony_ci return "vmlinux"; 13662306a36Sopenharmony_ci} 13762306a36Sopenharmony_ci#endif 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_cistatic void pi_create_file(struct module *mod) 14062306a36Sopenharmony_ci{ 14162306a36Sopenharmony_ci debugfs_create_file(pi_get_module_name(mod), 0444, dfs_index, 14262306a36Sopenharmony_ci mod, &dfs_index_fops); 14362306a36Sopenharmony_ci} 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci#ifdef CONFIG_MODULES 14662306a36Sopenharmony_cistatic void pi_remove_file(struct module *mod) 14762306a36Sopenharmony_ci{ 14862306a36Sopenharmony_ci debugfs_lookup_and_remove(pi_get_module_name(mod), dfs_index); 14962306a36Sopenharmony_ci} 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_cistatic int pi_module_notify(struct notifier_block *nb, unsigned long op, 15262306a36Sopenharmony_ci void *data) 15362306a36Sopenharmony_ci{ 15462306a36Sopenharmony_ci struct module *mod = data; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci switch (op) { 15762306a36Sopenharmony_ci case MODULE_STATE_COMING: 15862306a36Sopenharmony_ci pi_create_file(mod); 15962306a36Sopenharmony_ci break; 16062306a36Sopenharmony_ci case MODULE_STATE_GOING: 16162306a36Sopenharmony_ci pi_remove_file(mod); 16262306a36Sopenharmony_ci break; 16362306a36Sopenharmony_ci default: /* we don't care about other module states */ 16462306a36Sopenharmony_ci break; 16562306a36Sopenharmony_ci } 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci return NOTIFY_OK; 16862306a36Sopenharmony_ci} 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_cistatic struct notifier_block module_printk_fmts_nb = { 17162306a36Sopenharmony_ci .notifier_call = pi_module_notify, 17262306a36Sopenharmony_ci}; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_cistatic void __init pi_setup_module_notifier(void) 17562306a36Sopenharmony_ci{ 17662306a36Sopenharmony_ci register_module_notifier(&module_printk_fmts_nb); 17762306a36Sopenharmony_ci} 17862306a36Sopenharmony_ci#else 17962306a36Sopenharmony_cistatic inline void __init pi_setup_module_notifier(void) { } 18062306a36Sopenharmony_ci#endif 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_cistatic int __init pi_init(void) 18362306a36Sopenharmony_ci{ 18462306a36Sopenharmony_ci struct dentry *dfs_root = debugfs_create_dir("printk", NULL); 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci dfs_index = debugfs_create_dir("index", dfs_root); 18762306a36Sopenharmony_ci pi_setup_module_notifier(); 18862306a36Sopenharmony_ci pi_create_file(NULL); 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci return 0; 19162306a36Sopenharmony_ci} 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci/* debugfs comes up on core and must be initialised first */ 19462306a36Sopenharmony_cipostcore_initcall(pi_init); 195