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 <[email protected]> --- Documentation/dev-tools/kernel-api-spec.rst | 88 ++-- kernel/api/kapi_debugfs.c | 503 ++++++++++++++++++++ 2 files changed, 534 insertions(+), 57 deletions(-) create mode 100644 kernel/api/kapi_debugfs.c diff --git a/Documentation/dev-tools/kernel-api-spec.rst b/Documentation/dev-tools/kernel-api-spec.rst index 7c0c1694f1f4a..6ec6e65a1902a 100644 --- a/Documentation/dev-tools/kernel-api-spec.rst +++ b/Documentation/dev-tools/kernel-api-spec.rst @@ -56,16 +56,10 @@ The framework consists of several key components: 2. **DebugFS Interface** (``kernel/api/kapi_debugfs.c``) - Runtime introspection via ``/sys/kernel/debug/kapi/`` - - JSON and XML export formats - - Per-API detailed information + - Per-API detailed specification output + - List of all registered API specifications -3. **IOCTL Support** (``kernel/api/ioctl_validation.c``) - - - Extended framework for IOCTL specifications - - Automatic validation wrappers - - Structure field validation - -4. **Specification Macros** (``include/linux/kernel_api_spec.h``) +3. **Specification Macros** (``include/linux/kernel_api_spec.h``) - Declarative macros for API documentation - Type-safe parameter specifications @@ -186,8 +180,8 @@ Runtime validation is controlled by kernel configuration: 2. Enable ``CONFIG_KAPI_RUNTIME_CHECKS`` for runtime validation 3. Optionally enable ``CONFIG_KAPI_SPEC_DEBUGFS`` for debugfs interface -Validation Modes ----------------- +Validation Behavior +------------------- When ``CONFIG_KAPI_RUNTIME_CHECKS`` is enabled, all registered API specifications are validated automatically at call time. The framework checks parameter constraints, @@ -225,55 +219,39 @@ Directory Structure :: /sys/kernel/debug/kapi/ - ├── apis/ # All registered APIs - │ ├── kmalloc/ - │ │ ├── specification # Human-readable spec - │ │ ├── json # JSON format - │ │ └── xml # XML format - │ └── open/ - │ └── ... - ├── summary # Overview of all APIs - ├── validation/ # Validation controls - │ ├── enabled # Global enable/disable - │ ├── level # Validation level - │ └── stats # Validation statistics - └── export/ # Bulk export options - ├── all.json # All specs in JSON - └── all.xml # All specs in XML + ├── list # Overview of all registered API specs + └── specs/ # Per-API specification files + ├── sys_open # Human-readable spec for sys_open + ├── sys_close # Human-readable spec for sys_close + ├── sys_read # Human-readable spec for sys_read + └── sys_write # Human-readable spec for sys_write Usage Examples -------------- -Query specific API:: - - $ cat /sys/kernel/debug/kapi/apis/kmalloc/specification - API: kmalloc - Version: 3.0 - Description: Allocate kernel memory +List all available API specifications:: - Parameters: - [0] size (size_t, in): Number of bytes to allocate - Range: 0 - 4194304 - [1] flags (flags, in): Allocation flags (GFP_*) - Mask: 0x1ffffff + $ cat /sys/kernel/debug/kapi/list + Available Kernel API Specifications + =================================== - Returns: pointer - Pointer to allocated memory or NULL + sys_open - Open or create a file + sys_close - Close a file descriptor + sys_read - Read data from a file descriptor + sys_write - Write data to a file descriptor - Errors: - ENOMEM: Out of memory + Total: 4 specifications - Context: process, softirq, hardirq - - Side Effects: - - Allocates memory from kernel heap - -Export all specifications:: - - $ cat /sys/kernel/debug/kapi/export/all.json > kernel-apis.json +Query specific API:: -Enable validation for specific API:: + $ cat /sys/kernel/debug/kapi/specs/sys_open + Kernel API Specification + ======================== - $ echo 1 > /sys/kernel/debug/kapi/apis/kmalloc/validate + Name: sys_open + Version: 1 + Description: Open or create a file + ... Performance Considerations ========================== @@ -304,11 +282,8 @@ Optimization Strategies 1. **Compile-time optimization**: When validation is disabled, all validation code is optimized away by the compiler. -2. **Selective validation**: Enable validation only for specific APIs - or subsystems under test. - -3. **Caching**: The framework caches validation results for repeated - calls with identical parameters. +2. **Selective enablement**: Enable ``CONFIG_KAPI_RUNTIME_CHECKS`` + only in development/testing kernels, not in production. Documentation Generation ------------------------ @@ -414,10 +389,9 @@ Common Issues Debug Options ------------- -Enable verbose debugging:: +Enable verbose kernel logging to see KAPI validation messages:: echo 8 > /proc/sys/kernel/printk - echo 1 > /sys/kernel/debug/kapi/debug/verbose Future Directions ================= diff --git a/kernel/api/kapi_debugfs.c b/kernel/api/kapi_debugfs.c new file mode 100644 index 0000000000000..be187646ee768 --- /dev/null +++ b/kernel/api/kapi_debugfs.c @@ -0,0 +1,503 @@ +// 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 * const param_type_names[] = { + [KAPI_TYPE_VOID] = "void", + [KAPI_TYPE_INT] = "int", + [KAPI_TYPE_UINT] = "uint", + [KAPI_TYPE_PTR] = "ptr", + [KAPI_TYPE_STRUCT] = "struct", + [KAPI_TYPE_UNION] = "union", + [KAPI_TYPE_ARRAY] = "array", + [KAPI_TYPE_FD] = "fd", + [KAPI_TYPE_ENUM] = "enum", + [KAPI_TYPE_USER_PTR] = "user_ptr", + [KAPI_TYPE_PATH] = "path", + [KAPI_TYPE_FUNC_PTR] = "func_ptr", + [KAPI_TYPE_CUSTOM] = "custom", +}; + +static const char *param_type_str(enum kapi_param_type type) +{ + if (type < ARRAY_SIZE(param_type_names) && param_type_names[type]) + return param_type_names[type]; + return "unknown"; +} + +/* Helper to print parameter flags */ +static void print_param_flags(struct seq_file *m, u32 flags) +{ + seq_puts(m, " flags: "); + if ((flags & KAPI_PARAM_INOUT) == KAPI_PARAM_INOUT) + seq_puts(m, "INOUT "); + else if (flags & KAPI_PARAM_IN) + seq_puts(m, "IN "); + else if (flags & KAPI_PARAM_OUT) + seq_puts(m, "OUT "); + if (flags & KAPI_PARAM_OPTIONAL) + seq_puts(m, "OPTIONAL "); + if (flags & KAPI_PARAM_CONST) + seq_puts(m, "CONST "); + if (flags & KAPI_PARAM_USER) + seq_puts(m, "USER "); + if (flags & KAPI_PARAM_VOLATILE) + seq_puts(m, "VOLATILE "); + if (flags & KAPI_PARAM_DMA) + seq_puts(m, "DMA "); + if (flags & KAPI_PARAM_ALIGNED) + seq_puts(m, "ALIGNED "); + seq_puts(m, "\n"); +} + +/* Helper to print context flags */ +static void print_context_flags(struct seq_file *m, u32 flags) +{ + seq_puts(m, "Context flags: "); + if (flags & KAPI_CTX_PROCESS) + seq_puts(m, "PROCESS "); + if (flags & KAPI_CTX_HARDIRQ) + seq_puts(m, "HARDIRQ "); + if (flags & KAPI_CTX_SOFTIRQ) + seq_puts(m, "SOFTIRQ "); + if (flags & KAPI_CTX_NMI) + seq_puts(m, "NMI "); + if (flags & KAPI_CTX_SLEEPABLE) + seq_puts(m, "SLEEPABLE "); + if (flags & KAPI_CTX_ATOMIC) + seq_puts(m, "ATOMIC "); + if (flags & KAPI_CTX_PREEMPT_DISABLED) + seq_puts(m, "PREEMPT_DISABLED "); + if (flags & KAPI_CTX_IRQ_DISABLED) + seq_puts(m, "IRQ_DISABLED "); + seq_puts(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_puts(m, "Kernel API Specification\n"); + seq_puts(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 (strnlen(spec->long_description, sizeof(spec->long_description)) > 0) + seq_printf(m, "Long description: %.*s\n", + (int)sizeof(spec->long_description), spec->long_description); + + /* Context */ + print_context_flags(m, spec->context_flags); + seq_puts(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 < KAPI_MAX_PARAMS; 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 (strnlen(param->description, sizeof(param->description)) > 0) + seq_printf(m, " description: %.*s\n", + (int)sizeof(param->description), 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_puts(m, " constraints:\n"); + switch (param->constraint_type) { + case KAPI_CONSTRAINT_RANGE: + seq_puts(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_puts(m, " type: mask\n"); + seq_printf(m, " valid_bits: 0x%llx\n", param->valid_mask); + break; + case KAPI_CONSTRAINT_ENUM: + seq_puts(m, " type: enum\n"); + seq_printf(m, " count: %u\n", param->enum_count); + break; + case KAPI_CONSTRAINT_USER_STRING: + seq_puts(m, " type: user_string\n"); + seq_printf(m, " min_len: %lld\n", param->min_value); + seq_printf(m, " max_len: %lld\n", param->max_value); + break; + case KAPI_CONSTRAINT_USER_PATH: + seq_puts(m, " type: user_path\n"); + seq_puts(m, " max_len: PATH_MAX (4096)\n"); + break; + case KAPI_CONSTRAINT_USER_PTR: + seq_puts(m, " type: user_ptr\n"); + seq_printf(m, " size: %zu bytes\n", param->size); + break; + case KAPI_CONSTRAINT_ALIGNMENT: + seq_puts(m, " type: alignment\n"); + seq_printf(m, " alignment: %zu\n", param->alignment); + break; + case KAPI_CONSTRAINT_POWER_OF_TWO: + seq_puts(m, " type: power_of_two\n"); + break; + case KAPI_CONSTRAINT_PAGE_ALIGNED: + seq_puts(m, " type: page_aligned\n"); + break; + case KAPI_CONSTRAINT_NONZERO: + seq_puts(m, " type: nonzero\n"); + break; + case KAPI_CONSTRAINT_CUSTOM: + seq_puts(m, " type: custom\n"); + if (strnlen(param->constraints, sizeof(param->constraints)) > 0) + seq_printf(m, " description: %.*s\n", + (int)sizeof(param->constraints), + param->constraints); + break; + default: + break; + } + } + seq_puts(m, "\n"); + } + } + + /* Return value */ + seq_puts(m, "Return value:\n"); + seq_printf(m, " type: %s\n", spec->return_spec.type_name); + if (strnlen(spec->return_spec.description, sizeof(spec->return_spec.description)) > 0) + seq_printf(m, " description: %.*s\n", + (int)sizeof(spec->return_spec.description), + 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_puts(m, " success: valid file descriptor (>= 0)\n"); + break; + case KAPI_RETURN_ERROR_CHECK: + seq_puts(m, " success: error check\n"); + break; + case KAPI_RETURN_CUSTOM: + seq_puts(m, " success: custom check\n"); + break; + default: + break; + } + seq_puts(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 < KAPI_MAX_ERRORS; i++) { + struct kapi_error_spec *err = &spec->errors[i]; + seq_printf(m, " %s (%d): %s\n", + err->name, err->error_code, err->description); + if (strnlen(err->condition, sizeof(err->condition)) > 0) + seq_printf(m, " condition: %s\n", err->condition); + } + seq_puts(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 < KAPI_MAX_CONSTRAINTS; i++) { + struct kapi_lock_spec *lock = &spec->locks[i]; + const char *type_str, *scope_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; + } + switch (lock->scope) { + case KAPI_LOCK_INTERNAL: + scope_str = "acquired and released"; + break; + case KAPI_LOCK_ACQUIRES: + scope_str = "acquired (not released)"; + break; + case KAPI_LOCK_RELEASES: + scope_str = "released (held on entry)"; + break; + case KAPI_LOCK_CALLER_HELD: + scope_str = "held by caller"; + break; + default: + scope_str = "unknown"; + break; + } + seq_printf(m, " %s (%s): %s\n", + lock->lock_name, type_str, lock->description); + seq_printf(m, " scope: %s\n", scope_str); + } + seq_puts(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 < KAPI_MAX_CONSTRAINTS; i++) { + struct kapi_constraint_spec *cons = &spec->constraints[i]; + + seq_printf(m, " - %s", cons->name); + if (cons->description[0]) + seq_printf(m, ": %s", cons->description); + seq_puts(m, "\n"); + if (cons->expression[0]) + seq_printf(m, " expression: %s\n", cons->expression); + } + seq_puts(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 < KAPI_MAX_SIGNALS; i++) { + struct kapi_signal_spec *sig = &spec->signals[i]; + seq_printf(m, " %s (%d):\n", sig->signal_name, sig->signal_num); + seq_puts(m, " direction: "); + if (sig->direction & KAPI_SIGNAL_SEND) + seq_puts(m, "send "); + if (sig->direction & KAPI_SIGNAL_RECEIVE) + seq_puts(m, "receive "); + if (sig->direction & KAPI_SIGNAL_HANDLE) + seq_puts(m, "handle "); + if (sig->direction & KAPI_SIGNAL_BLOCK) + seq_puts(m, "block "); + if (sig->direction & KAPI_SIGNAL_IGNORE) + seq_puts(m, "ignore "); + seq_puts(m, "\n"); + seq_puts(m, " action: "); + switch (sig->action) { + case KAPI_SIGNAL_ACTION_DEFAULT: + seq_puts(m, "default"); + break; + case KAPI_SIGNAL_ACTION_TERMINATE: + seq_puts(m, "terminate"); + break; + case KAPI_SIGNAL_ACTION_COREDUMP: + seq_puts(m, "coredump"); + break; + case KAPI_SIGNAL_ACTION_STOP: + seq_puts(m, "stop"); + break; + case KAPI_SIGNAL_ACTION_CONTINUE: + seq_puts(m, "continue"); + break; + case KAPI_SIGNAL_ACTION_CUSTOM: + seq_puts(m, "custom"); + break; + case KAPI_SIGNAL_ACTION_RETURN: + seq_puts(m, "return"); + break; + case KAPI_SIGNAL_ACTION_RESTART: + seq_puts(m, "restart"); + break; + default: + seq_puts(m, "unknown"); + break; + } + seq_puts(m, "\n"); + if (strnlen(sig->description, sizeof(sig->description)) > 0) + seq_printf(m, " description: %.*s\n", + (int)sizeof(sig->description), sig->description); + } + seq_puts(m, "\n"); + } + + /* Side effects */ + if (spec->side_effect_count > 0) { + seq_printf(m, "Side effects (%u):\n", spec->side_effect_count); + for (i = 0; i < spec->side_effect_count && i < KAPI_MAX_SIDE_EFFECTS; i++) { + const struct kapi_side_effect *eff = &spec->side_effects[i]; + + seq_printf(m, " - %.*s", + (int)sizeof(eff->target), eff->target); + if (strnlen(eff->description, sizeof(eff->description)) > 0) + seq_printf(m, ": %.*s", + (int)sizeof(eff->description), eff->description); + if (eff->reversible) + seq_puts(m, " (reversible)"); + seq_puts(m, "\n"); + } + seq_puts(m, "\n"); + } + + /* State transitions */ + if (spec->state_trans_count > 0) { + seq_printf(m, "State transitions (%u):\n", spec->state_trans_count); + for (i = 0; i < spec->state_trans_count && i < KAPI_MAX_STATE_TRANS; i++) { + const struct kapi_state_transition *trans = &spec->state_transitions[i]; + + seq_printf(m, " %.*s: %.*s -> %.*s\n", + (int)sizeof(trans->object), trans->object, + (int)sizeof(trans->from_state), trans->from_state, + (int)sizeof(trans->to_state), trans->to_state); + if (strnlen(trans->description, sizeof(trans->description)) > 0) + seq_printf(m, " %.*s\n", + (int)sizeof(trans->description), trans->description); + } + seq_puts(m, "\n"); + } + + /* Capabilities */ + if (spec->capability_count > 0) { + seq_printf(m, "Capabilities (%u):\n", spec->capability_count); + for (i = 0; i < spec->capability_count && i < KAPI_MAX_CAPABILITIES; i++) { + const struct kapi_capability_spec *cap = &spec->capabilities[i]; + + seq_printf(m, " %.*s (%d):\n", + (int)sizeof(cap->cap_name), cap->cap_name, + cap->capability); + if (strnlen(cap->allows, sizeof(cap->allows)) > 0) + seq_printf(m, " allows: %.*s\n", + (int)sizeof(cap->allows), cap->allows); + if (strnlen(cap->without_cap, sizeof(cap->without_cap)) > 0) + seq_printf(m, " without: %.*s\n", + (int)sizeof(cap->without_cap), cap->without_cap); + } + seq_puts(m, "\n"); + } + + /* Additional info */ + if (strnlen(spec->examples, sizeof(spec->examples)) > 0) { + seq_printf(m, "Examples:\n%.*s\n\n", + (int)sizeof(spec->examples), spec->examples); + } + if (strnlen(spec->notes, sizeof(spec->notes)) > 0) { + seq_printf(m, "Notes:\n%.*s\n\n", + (int)sizeof(spec->notes), spec->notes); + } + if (strnlen(spec->since_version, sizeof(spec->since_version)) > 0) { + seq_printf(m, "Since: %.*s\n", + (int)sizeof(spec->since_version), spec->since_version); + } + + 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. + * + * Note: This only iterates the static .kapi_specs section. Specs registered + * dynamically via kapi_register_spec() are not included in this listing + * or in the per-spec debugfs files. + */ +static int kapi_list_show(struct seq_file *m, void *v) +{ + struct kernel_api_spec *spec; + int count = 0; + + seq_puts(m, "Available Kernel API Specifications\n"); + seq_puts(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; +} + +/* Initialize as part of kernel, not as a module */ +fs_initcall(kapi_debugfs_init); -- 2.51.0

