Add a debugfs interface to expose kernel API specifications at runtime. This allows tools and users to query the complete API specifications through the debugfs filesystem.
The interface provides: - /sys/kernel/debug/kapi/list - lists all available API specifications - /sys/kernel/debug/kapi/specs/<name> - detailed info for each API Each specification file includes: - Function name, version, and descriptions - Execution context requirements and flags - Parameter details with types, flags, and constraints - Return value specifications and success conditions - Error codes with descriptions and conditions - Locking requirements and constraints - Signal handling specifications - Examples, notes, and deprecation status This enables runtime introspection of kernel APIs for documentation tools, static analyzers, and debugging purposes. Signed-off-by: Sasha Levin <sas...@kernel.org> --- kernel/api/Kconfig | 20 +++ kernel/api/Makefile | 5 +- kernel/api/kapi_debugfs.c | 340 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 364 insertions(+), 1 deletion(-) create mode 100644 kernel/api/kapi_debugfs.c diff --git a/kernel/api/Kconfig b/kernel/api/Kconfig index fde25ec70e134..d2754b21acc43 100644 --- a/kernel/api/Kconfig +++ b/kernel/api/Kconfig @@ -33,3 +33,23 @@ config KAPI_RUNTIME_CHECKS development. The checks use WARN_ONCE to report violations. If unsure, say N. + +config KAPI_SPEC_DEBUGFS + bool "Export kernel API specifications via debugfs" + depends on KAPI_SPEC + depends on DEBUG_FS + help + This option enables exporting kernel API specifications through + the debugfs filesystem. When enabled, specifications can be + accessed at /sys/kernel/debug/kapi/. + + The debugfs interface provides: + - A list of all available API specifications + - Detailed information for each API including parameters, + return values, errors, locking requirements, and constraints + - Complete machine-readable representation of the specs + + This is useful for documentation tools, static analyzers, and + runtime introspection of kernel APIs. + + If unsure, say N. diff --git a/kernel/api/Makefile b/kernel/api/Makefile index 4120ded7e5cf1..07b8c007ec156 100644 --- a/kernel/api/Makefile +++ b/kernel/api/Makefile @@ -4,4 +4,7 @@ # # Core API specification framework -obj-$(CONFIG_KAPI_SPEC) += kernel_api_spec.o \ No newline at end of file +obj-$(CONFIG_KAPI_SPEC) += kernel_api_spec.o + +# Debugfs interface for kernel API specs +obj-$(CONFIG_KAPI_SPEC_DEBUGFS) += kapi_debugfs.o \ No newline at end of file diff --git a/kernel/api/kapi_debugfs.c b/kernel/api/kapi_debugfs.c new file mode 100644 index 0000000000000..bf65ea6a49205 --- /dev/null +++ b/kernel/api/kapi_debugfs.c @@ -0,0 +1,340 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Kernel API specification debugfs interface + * + * This provides a debugfs interface to expose kernel API specifications + * at runtime, allowing tools and users to query the complete API specs. + */ + +#include <linux/debugfs.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/seq_file.h> +#include <linux/kernel_api_spec.h> +#include <linux/slab.h> +#include <linux/string.h> + +/* External symbols for kernel API spec section */ +extern struct kernel_api_spec __start_kapi_specs[]; +extern struct kernel_api_spec __stop_kapi_specs[]; + +static struct dentry *kapi_debugfs_root; + +/* Helper function to print parameter type as string */ +static const char *param_type_str(enum kapi_param_type type) +{ + switch (type) { + case KAPI_TYPE_INT: return "int"; + case KAPI_TYPE_UINT: return "uint"; + case KAPI_TYPE_PTR: return "ptr"; + case KAPI_TYPE_STRUCT: return "struct"; + case KAPI_TYPE_UNION: return "union"; + case KAPI_TYPE_ARRAY: return "array"; + case KAPI_TYPE_FD: return "fd"; + case KAPI_TYPE_ENUM: return "enum"; + case KAPI_TYPE_USER_PTR: return "user_ptr"; + case KAPI_TYPE_PATH: return "path"; + case KAPI_TYPE_FUNC_PTR: return "func_ptr"; + case KAPI_TYPE_CUSTOM: return "custom"; + default: return "unknown"; + } +} + +/* Helper to print parameter flags */ +static void print_param_flags(struct seq_file *m, u32 flags) +{ + seq_printf(m, " flags: "); + if (flags & KAPI_PARAM_IN) seq_printf(m, "IN "); + if (flags & KAPI_PARAM_OUT) seq_printf(m, "OUT "); + if (flags & KAPI_PARAM_INOUT) seq_printf(m, "INOUT "); + if (flags & KAPI_PARAM_OPTIONAL) seq_printf(m, "OPTIONAL "); + if (flags & KAPI_PARAM_CONST) seq_printf(m, "CONST "); + if (flags & KAPI_PARAM_USER) seq_printf(m, "USER "); + if (flags & KAPI_PARAM_VOLATILE) seq_printf(m, "VOLATILE "); + if (flags & KAPI_PARAM_DMA) seq_printf(m, "DMA "); + if (flags & KAPI_PARAM_ALIGNED) seq_printf(m, "ALIGNED "); + seq_printf(m, "\n"); +} + +/* Helper to print context flags */ +static void print_context_flags(struct seq_file *m, u32 flags) +{ + seq_printf(m, "Context flags: "); + if (flags & KAPI_CTX_PROCESS) seq_printf(m, "PROCESS "); + if (flags & KAPI_CTX_HARDIRQ) seq_printf(m, "HARDIRQ "); + if (flags & KAPI_CTX_SOFTIRQ) seq_printf(m, "SOFTIRQ "); + if (flags & KAPI_CTX_NMI) seq_printf(m, "NMI "); + if (flags & KAPI_CTX_SLEEPABLE) seq_printf(m, "SLEEPABLE "); + if (flags & KAPI_CTX_ATOMIC) seq_printf(m, "ATOMIC "); + if (flags & KAPI_CTX_PREEMPT_DISABLED) seq_printf(m, "PREEMPT_DISABLED "); + if (flags & KAPI_CTX_IRQ_DISABLED) seq_printf(m, "IRQ_DISABLED "); + seq_printf(m, "\n"); +} + +/* Show function for individual API spec */ +static int kapi_spec_show(struct seq_file *m, void *v) +{ + struct kernel_api_spec *spec = m->private; + int i; + + seq_printf(m, "Kernel API Specification\n"); + seq_printf(m, "========================\n\n"); + + /* Basic info */ + seq_printf(m, "Name: %s\n", spec->name); + seq_printf(m, "Version: %u\n", spec->version); + seq_printf(m, "Description: %s\n", spec->description); + if (strlen(spec->long_description) > 0) + seq_printf(m, "Long description: %s\n", spec->long_description); + + /* Context */ + print_context_flags(m, spec->context_flags); + seq_printf(m, "\n"); + + /* Parameters */ + if (spec->param_count > 0) { + seq_printf(m, "Parameters (%u):\n", spec->param_count); + for (i = 0; i < spec->param_count; i++) { + struct kapi_param_spec *param = &spec->params[i]; + seq_printf(m, " [%d] %s:\n", i, param->name); + seq_printf(m, " type: %s (%s)\n", + param_type_str(param->type), param->type_name); + print_param_flags(m, param->flags); + if (strlen(param->description) > 0) + seq_printf(m, " description: %s\n", param->description); + if (param->size > 0) + seq_printf(m, " size: %zu\n", param->size); + if (param->alignment > 0) + seq_printf(m, " alignment: %zu\n", param->alignment); + + /* Print constraints if any */ + if (param->constraint_type != KAPI_CONSTRAINT_NONE) { + seq_printf(m, " constraints:\n"); + switch (param->constraint_type) { + case KAPI_CONSTRAINT_RANGE: + seq_printf(m, " type: range\n"); + seq_printf(m, " min: %lld\n", param->min_value); + seq_printf(m, " max: %lld\n", param->max_value); + break; + case KAPI_CONSTRAINT_MASK: + seq_printf(m, " type: mask\n"); + seq_printf(m, " valid_bits: 0x%llx\n", param->valid_mask); + break; + case KAPI_CONSTRAINT_ENUM: + seq_printf(m, " type: enum\n"); + seq_printf(m, " count: %u\n", param->enum_count); + break; + case KAPI_CONSTRAINT_CUSTOM: + seq_printf(m, " type: custom\n"); + if (strlen(param->constraints) > 0) + seq_printf(m, " description: %s\n", + param->constraints); + break; + default: + break; + } + } + seq_printf(m, "\n"); + } + } + + /* Return value */ + seq_printf(m, "Return value:\n"); + seq_printf(m, " type: %s\n", spec->return_spec.type_name); + if (strlen(spec->return_spec.description) > 0) + seq_printf(m, " description: %s\n", spec->return_spec.description); + + switch (spec->return_spec.check_type) { + case KAPI_RETURN_EXACT: + seq_printf(m, " success: == %lld\n", spec->return_spec.success_value); + break; + case KAPI_RETURN_RANGE: + seq_printf(m, " success: [%lld, %lld]\n", + spec->return_spec.success_min, + spec->return_spec.success_max); + break; + case KAPI_RETURN_FD: + seq_printf(m, " success: valid file descriptor (>= 0)\n"); + break; + case KAPI_RETURN_ERROR_CHECK: + seq_printf(m, " success: error check\n"); + break; + case KAPI_RETURN_CUSTOM: + seq_printf(m, " success: custom check\n"); + break; + default: + break; + } + seq_printf(m, "\n"); + + /* Errors */ + if (spec->error_count > 0) { + seq_printf(m, "Errors (%u):\n", spec->error_count); + for (i = 0; i < spec->error_count; i++) { + struct kapi_error_spec *err = &spec->errors[i]; + seq_printf(m, " %s (%d): %s\n", + err->name, err->error_code, err->description); + if (strlen(err->condition) > 0) + seq_printf(m, " condition: %s\n", err->condition); + } + seq_printf(m, "\n"); + } + + /* Locks */ + if (spec->lock_count > 0) { + seq_printf(m, "Locks (%u):\n", spec->lock_count); + for (i = 0; i < spec->lock_count; i++) { + struct kapi_lock_spec *lock = &spec->locks[i]; + const char *type_str; + switch (lock->lock_type) { + case KAPI_LOCK_MUTEX: type_str = "mutex"; break; + case KAPI_LOCK_SPINLOCK: type_str = "spinlock"; break; + case KAPI_LOCK_RWLOCK: type_str = "rwlock"; break; + case KAPI_LOCK_SEMAPHORE: type_str = "semaphore"; break; + case KAPI_LOCK_RCU: type_str = "rcu"; break; + case KAPI_LOCK_SEQLOCK: type_str = "seqlock"; break; + default: type_str = "unknown"; break; + } + seq_printf(m, " %s (%s): %s\n", + lock->lock_name, type_str, lock->description); + if (lock->acquired) + seq_printf(m, " acquired by function\n"); + if (lock->released) + seq_printf(m, " released by function\n"); + } + seq_printf(m, "\n"); + } + + /* Constraints */ + if (spec->constraint_count > 0) { + seq_printf(m, "Additional constraints (%u):\n", spec->constraint_count); + for (i = 0; i < spec->constraint_count; i++) { + seq_printf(m, " - %s\n", spec->constraints[i].description); + } + seq_printf(m, "\n"); + } + + /* Signals */ + if (spec->signal_count > 0) { + seq_printf(m, "Signal handling (%u):\n", spec->signal_count); + for (i = 0; i < spec->signal_count; i++) { + struct kapi_signal_spec *sig = &spec->signals[i]; + seq_printf(m, " %s (%d):\n", sig->signal_name, sig->signal_num); + seq_printf(m, " direction: "); + if (sig->direction & KAPI_SIGNAL_SEND) seq_printf(m, "send "); + if (sig->direction & KAPI_SIGNAL_RECEIVE) seq_printf(m, "receive "); + if (sig->direction & KAPI_SIGNAL_HANDLE) seq_printf(m, "handle "); + if (sig->direction & KAPI_SIGNAL_BLOCK) seq_printf(m, "block "); + if (sig->direction & KAPI_SIGNAL_IGNORE) seq_printf(m, "ignore "); + seq_printf(m, "\n"); + seq_printf(m, " action: "); + switch (sig->action) { + case KAPI_SIGNAL_ACTION_DEFAULT: seq_printf(m, "default"); break; + case KAPI_SIGNAL_ACTION_TERMINATE: seq_printf(m, "terminate"); break; + case KAPI_SIGNAL_ACTION_COREDUMP: seq_printf(m, "coredump"); break; + case KAPI_SIGNAL_ACTION_STOP: seq_printf(m, "stop"); break; + case KAPI_SIGNAL_ACTION_CONTINUE: seq_printf(m, "continue"); break; + case KAPI_SIGNAL_ACTION_CUSTOM: seq_printf(m, "custom"); break; + case KAPI_SIGNAL_ACTION_RETURN: seq_printf(m, "return"); break; + case KAPI_SIGNAL_ACTION_RESTART: seq_printf(m, "restart"); break; + default: seq_printf(m, "unknown"); break; + } + seq_printf(m, "\n"); + if (strlen(sig->description) > 0) + seq_printf(m, " description: %s\n", sig->description); + } + seq_printf(m, "\n"); + } + + /* Additional info */ + if (strlen(spec->examples) > 0) { + seq_printf(m, "Examples:\n%s\n\n", spec->examples); + } + if (strlen(spec->notes) > 0) { + seq_printf(m, "Notes:\n%s\n\n", spec->notes); + } + if (strlen(spec->since_version) > 0) { + seq_printf(m, "Since: %s\n", spec->since_version); + } + if (spec->deprecated) { + seq_printf(m, "DEPRECATED"); + if (strlen(spec->replacement) > 0) + seq_printf(m, " - use %s instead", spec->replacement); + seq_printf(m, "\n"); + } + + return 0; +} + +static int kapi_spec_open(struct inode *inode, struct file *file) +{ + return single_open(file, kapi_spec_show, inode->i_private); +} + +static const struct file_operations kapi_spec_fops = { + .open = kapi_spec_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +/* Show all available API specs */ +static int kapi_list_show(struct seq_file *m, void *v) +{ + struct kernel_api_spec *spec; + int count = 0; + + seq_printf(m, "Available Kernel API Specifications\n"); + seq_printf(m, "===================================\n\n"); + + for (spec = __start_kapi_specs; spec < __stop_kapi_specs; spec++) { + seq_printf(m, "%s - %s\n", spec->name, spec->description); + count++; + } + + seq_printf(m, "\nTotal: %d specifications\n", count); + return 0; +} + +static int kapi_list_open(struct inode *inode, struct file *file) +{ + return single_open(file, kapi_list_show, NULL); +} + +static const struct file_operations kapi_list_fops = { + .open = kapi_list_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int __init kapi_debugfs_init(void) +{ + struct kernel_api_spec *spec; + struct dentry *spec_dir; + + /* Create main directory */ + kapi_debugfs_root = debugfs_create_dir("kapi", NULL); + + /* Create list file */ + debugfs_create_file("list", 0444, kapi_debugfs_root, NULL, &kapi_list_fops); + + /* Create specs subdirectory */ + spec_dir = debugfs_create_dir("specs", kapi_debugfs_root); + + /* Create a file for each API spec */ + for (spec = __start_kapi_specs; spec < __stop_kapi_specs; spec++) { + debugfs_create_file(spec->name, 0444, spec_dir, spec, &kapi_spec_fops); + } + + pr_info("Kernel API debugfs interface initialized\n"); + return 0; +} + +static void __exit kapi_debugfs_exit(void) +{ + debugfs_remove_recursive(kapi_debugfs_root); +} + +/* Initialize as part of kernel, not as a module */ +fs_initcall(kapi_debugfs_init); \ No newline at end of file -- 2.39.5