summaryrefslogtreecommitdiffstats
path: root/scripts
diff options
context:
space:
mode:
Diffstat (limited to 'scripts')
-rw-r--r--scripts/.gitignore1
-rw-r--r--scripts/Kconfig.include5
-rw-r--r--scripts/Makefile7
-rw-r--r--scripts/Makefile.btf21
-rw-r--r--scripts/Makefile.build46
-rw-r--r--scripts/Makefile.context-analysis11
-rw-r--r--scripts/Makefile.dtbs10
-rw-r--r--scripts/Makefile.lib18
-rw-r--r--scripts/Makefile.modfinal10
-rw-r--r--scripts/Makefile.modinst2
-rw-r--r--scripts/Makefile.package20
-rw-r--r--scripts/Makefile.vdsoinst7
-rw-r--r--scripts/Makefile.vmlinux18
-rw-r--r--scripts/Makefile.vmlinux_o6
-rw-r--r--scripts/Makefile.warn (renamed from scripts/Makefile.extrawarn)30
-rwxr-xr-xscripts/atomic/gen-atomic-instrumented.sh11
-rwxr-xr-xscripts/atomic/gen-rust-atomic-helpers.sh5
-rw-r--r--scripts/atomic/kerneldoc/try_cmpxchg2
-rwxr-xr-xscripts/bloat-o-meter7
-rwxr-xr-xscripts/cc-can-link.sh4
-rwxr-xr-xscripts/check-function-names.sh25
-rwxr-xr-xscripts/check-uapi.sh19
-rwxr-xr-xscripts/check-variable-fonts.sh115
-rwxr-xr-xscripts/checker-valid.sh19
-rwxr-xr-xscripts/checkpatch.pl103
-rwxr-xr-xscripts/checksyscalls.sh11
-rwxr-xr-xscripts/checktransupdate.py307
-rwxr-xr-xscripts/coccicheck27
-rw-r--r--scripts/coccinelle/api/kmalloc_objs.cocci135
-rw-r--r--scripts/coccinelle/api/pm_runtime.cocci3
-rwxr-xr-xscripts/container199
-rw-r--r--scripts/context-analysis-suppression.txt34
-rwxr-xr-xscripts/crypto/gen-fips-testvecs.py46
-rwxr-xr-xscripts/crypto/gen-hash-testvecs.py234
-rwxr-xr-xscripts/decode_stacktrace.sh40
-rwxr-xr-xscripts/decodecode3
-rwxr-xr-xscripts/documentation-file-ref-check245
-rw-r--r--scripts/dtc/checks.c44
-rwxr-xr-xscripts/dtc/dt-extract-compatibles1
-rw-r--r--scripts/dtc/dtc-lexer.l3
-rw-r--r--scripts/dtc/dtc.c5
-rw-r--r--scripts/dtc/dtc.h8
-rw-r--r--scripts/dtc/flattree.c6
-rw-r--r--scripts/dtc/libfdt/fdt.c8
-rw-r--r--scripts/dtc/libfdt/fdt_overlay.c3
-rw-r--r--scripts/dtc/libfdt/fdt_ro.c4
-rw-r--r--scripts/dtc/libfdt/fdt_rw.c9
-rw-r--r--scripts/dtc/libfdt/libfdt.h214
-rw-r--r--scripts/dtc/libfdt/libfdt_env.h27
-rw-r--r--scripts/dtc/libfdt/libfdt_internal.h14
-rw-r--r--scripts/dtc/livetree.c309
-rw-r--r--scripts/dtc/srcpos.c22
-rw-r--r--scripts/dtc/treesource.c114
-rw-r--r--scripts/dtc/version_gen.h2
-rwxr-xr-xscripts/dummy-tools/python34
-rw-r--r--scripts/elf-parse.c198
-rw-r--r--scripts/elf-parse.h305
-rwxr-xr-xscripts/faddr2line19
-rwxr-xr-xscripts/find-unused-docs.sh62
-rw-r--r--scripts/gcc-plugins/gcc-common.h4
-rw-r--r--scripts/gdb/linux/bpf.py253
-rw-r--r--scripts/gdb/linux/constants.py.in5
-rw-r--r--scripts/gdb/linux/mm.py173
-rw-r--r--scripts/gdb/linux/radixtree.py139
-rw-r--r--scripts/gdb/linux/symbols.py105
-rw-r--r--scripts/gdb/linux/timerlist.py2
-rwxr-xr-xscripts/gen-btf.sh147
-rw-r--r--scripts/gendwarfksyms/dwarf.c4
-rw-r--r--scripts/gendwarfksyms/gendwarfksyms.c3
-rw-r--r--scripts/gendwarfksyms/gendwarfksyms.h2
-rw-r--r--scripts/gendwarfksyms/symbols.c9
-rwxr-xr-xscripts/generate_rust_analyzer.py334
-rw-r--r--scripts/genksyms/parse.y4
-rwxr-xr-xscripts/get_abi.py214
-rwxr-xr-xscripts/get_feat.pl641
-rwxr-xr-xscripts/get_maintainer.pl9
-rwxr-xr-xscripts/headers_install.sh32
-rwxr-xr-xscripts/jobserver-exec88
-rw-r--r--scripts/kallsyms.c64
-rw-r--r--scripts/kconfig/Makefile4
-rw-r--r--scripts/kconfig/gconf.c35
-rw-r--r--scripts/kconfig/icons/back.xpm29
-rw-r--r--scripts/kconfig/icons/choice_no.xpm18
-rw-r--r--scripts/kconfig/icons/choice_yes.xpm18
-rw-r--r--scripts/kconfig/icons/load.xpm31
-rw-r--r--scripts/kconfig/icons/menu.xpm18
-rw-r--r--scripts/kconfig/icons/menuback.xpm18
-rw-r--r--scripts/kconfig/icons/save.xpm31
-rw-r--r--scripts/kconfig/icons/single_view.xpm28
-rw-r--r--scripts/kconfig/icons/split_view.xpm28
-rw-r--r--scripts/kconfig/icons/symbol_mod.xpm18
-rw-r--r--scripts/kconfig/icons/symbol_no.xpm18
-rw-r--r--scripts/kconfig/icons/symbol_yes.xpm18
-rw-r--r--scripts/kconfig/icons/tree_view.xpm28
-rw-r--r--scripts/kconfig/images.c328
-rw-r--r--scripts/kconfig/images.h33
-rw-r--r--scripts/kconfig/lexer.l4
-rw-r--r--scripts/kconfig/lkc.h5
-rw-r--r--scripts/kconfig/menu.c12
-rwxr-xr-xscripts/kconfig/merge_config.sh272
-rwxr-xr-xscripts/kconfig/nconf-cfg.sh11
-rw-r--r--scripts/kconfig/parser.y21
-rw-r--r--scripts/kconfig/qconf.cc29
-rwxr-xr-xscripts/kconfig/streamline_config.pl2
-rw-r--r--scripts/kconfig/tests/conditional_dep/Kconfig32
-rw-r--r--scripts/kconfig/tests/conditional_dep/__init__.py14
-rw-r--r--scripts/kconfig/tests/conditional_dep/expected_config111
-rw-r--r--scripts/kconfig/tests/conditional_dep/expected_config29
-rw-r--r--scripts/kconfig/tests/conditional_dep/expected_config311
-rw-r--r--scripts/kconfig/tests/conditional_dep/test_config16
-rw-r--r--scripts/kconfig/tests/conditional_dep/test_config27
-rw-r--r--scripts/kconfig/tests/conditional_dep/test_config36
-rw-r--r--scripts/kconfig/tests/err_repeated_inc/Kconfig3
-rw-r--r--scripts/kconfig/tests/err_repeated_inc/Kconfig.inc14
-rw-r--r--scripts/kconfig/tests/err_repeated_inc/Kconfig.inc23
-rw-r--r--scripts/kconfig/tests/err_repeated_inc/Kconfig.inc31
-rw-r--r--scripts/kconfig/tests/err_repeated_inc/__init__.py10
-rw-r--r--scripts/kconfig/tests/err_repeated_inc/expected_stderr2
-rw-r--r--scripts/kconfig/util.c31
l---------scripts/kernel-doc2
-rwxr-xr-xscripts/kernel-doc.pl2439
-rwxr-xr-xscripts/kernel-doc.py339
-rw-r--r--scripts/lib/abi/abi_parser.py628
-rw-r--r--scripts/lib/abi/abi_regex.py234
-rw-r--r--scripts/lib/abi/helpers.py38
-rw-r--r--scripts/lib/abi/system_symbols.py378
-rw-r--r--scripts/lib/kdoc/kdoc_files.py291
-rw-r--r--scripts/lib/kdoc/kdoc_item.py42
-rw-r--r--scripts/lib/kdoc/kdoc_output.py749
-rw-r--r--scripts/lib/kdoc/kdoc_parser.py1649
-rw-r--r--scripts/lib/kdoc/kdoc_re.py270
-rwxr-xr-xscripts/link-vmlinux.sh57
-rw-r--r--scripts/livepatch/Makefile20
-rwxr-xr-xscripts/livepatch/fix-patch-lines79
-rw-r--r--scripts/livepatch/init.c106
-rwxr-xr-xscripts/livepatch/klp-build855
-rwxr-xr-xscripts/make_fit.py177
-rwxr-xr-xscripts/min-tool-version.sh4
-rw-r--r--scripts/mod/devicetable-offsets.c4
-rw-r--r--scripts/mod/file2alias.c9
-rw-r--r--scripts/mod/modpost.c29
-rw-r--r--scripts/module.lds.S30
-rwxr-xr-xscripts/package/builddeb8
-rwxr-xr-xscripts/package/install-extmod-build6
-rw-r--r--scripts/package/kernel.spec65
-rwxr-xr-xscripts/package/mkspec38
-rwxr-xr-xscripts/rust_is_available.sh36
-rw-r--r--scripts/rust_is_available_bindgen_0_66.h2
-rw-r--r--scripts/rust_is_available_bindgen_libclang_concat.h3
-rwxr-xr-xscripts/rust_is_available_test.py58
-rw-r--r--scripts/rustdoc_test_gen.rs7
-rw-r--r--scripts/sign-file.c118
-rw-r--r--scripts/sorttable.c477
-rwxr-xr-xscripts/spdxcheck.py2
-rw-r--r--scripts/spelling.txt332
-rwxr-xr-xscripts/sphinx-build-wrapper719
-rwxr-xr-xscripts/sphinx-pre-install1621
-rwxr-xr-xscripts/split-man.pl28
-rw-r--r--scripts/syscall.tbl2
-rwxr-xr-xscripts/tags.sh1
-rwxr-xr-xscripts/test_doc_build.py513
-rw-r--r--scripts/tracepoint-update.c266
-rwxr-xr-xscripts/ver_linux63
163 files changed, 5966 insertions, 13454 deletions
diff --git a/scripts/.gitignore b/scripts/.gitignore
index c2ef68848da5..4215c2208f7e 100644
--- a/scripts/.gitignore
+++ b/scripts/.gitignore
@@ -11,4 +11,5 @@
/sign-file
/sorttable
/target.json
+/tracepoint-update
/unifdef
diff --git a/scripts/Kconfig.include b/scripts/Kconfig.include
index 33193ca6e803..fc10671c297c 100644
--- a/scripts/Kconfig.include
+++ b/scripts/Kconfig.include
@@ -65,13 +65,14 @@ cc-option-bit = $(if-success,$(CC) -Werror $(1) -E -x c /dev/null -o /dev/null,$
m32-flag := $(cc-option-bit,-m32)
m64-flag := $(cc-option-bit,-m64)
+# Test whether the compiler can link userspace applications
+cc_can_link_user = $(success,$(srctree)/scripts/cc-can-link.sh $(CC) $(CLANG_FLAGS) $(USERCFLAGS) $(USERLDFLAGS) $(1))
+
rustc-version := $(shell,$(srctree)/scripts/rustc-version.sh $(RUSTC))
rustc-llvm-version := $(shell,$(srctree)/scripts/rustc-llvm-version.sh $(RUSTC))
# $(rustc-option,<flag>)
# Return y if the Rust compiler supports <flag>, n otherwise
-# Calls to this should be guarded so that they are not evaluated if
-# CONFIG_RUST_IS_AVAILABLE is not set.
# If you are testing for unstable features, consider testing RUSTC_VERSION
# instead, as features may have different completeness while available.
rustc-option = $(success,trap "rm -rf .tmp_$$" EXIT; mkdir .tmp_$$; $(RUSTC) $(1) --crate-type=rlib /dev/null --out-dir=.tmp_$$ -o .tmp_$$/tmp.rlib)
diff --git a/scripts/Makefile b/scripts/Makefile
index 46f860529df5..3434a82a119f 100644
--- a/scripts/Makefile
+++ b/scripts/Makefile
@@ -11,6 +11,10 @@ hostprogs-always-$(CONFIG_MODULE_SIG_FORMAT) += sign-file
hostprogs-always-$(CONFIG_SYSTEM_EXTRA_CERTIFICATE) += insert-sys-cert
hostprogs-always-$(CONFIG_RUST_KERNEL_DOCTESTS) += rustdoc_test_builder
hostprogs-always-$(CONFIG_RUST_KERNEL_DOCTESTS) += rustdoc_test_gen
+hostprogs-always-$(CONFIG_TRACEPOINTS) += tracepoint-update
+
+sorttable-objs := sorttable.o elf-parse.o
+tracepoint-update-objs := tracepoint-update.o elf-parse.o
ifneq ($(or $(CONFIG_X86_64),$(CONFIG_X86_32)),)
always-$(CONFIG_RUST) += target.json
@@ -25,10 +29,13 @@ generate_rust_target-rust := y
rustdoc_test_builder-rust := y
rustdoc_test_gen-rust := y
+HOSTCFLAGS_tracepoint-update.o = -I$(srctree)/tools/include
+HOSTCFLAGS_elf-parse.o = -I$(srctree)/tools/include
HOSTCFLAGS_sorttable.o = -I$(srctree)/tools/include
HOSTLDLIBS_sorttable = -lpthread
HOSTCFLAGS_asn1_compiler.o = -I$(srctree)/include
HOSTCFLAGS_sign-file.o = $(shell $(HOSTPKG_CONFIG) --cflags libcrypto 2> /dev/null)
+HOSTCFLAGS_sign-file.o += -I$(srctree)/tools/include/uapi/
HOSTLDLIBS_sign-file = $(shell $(HOSTPKG_CONFIG) --libs libcrypto 2> /dev/null || echo -lcrypto)
ifdef CONFIG_UNWINDER_ORC
diff --git a/scripts/Makefile.btf b/scripts/Makefile.btf
index db76335dd917..e66e13e79653 100644
--- a/scripts/Makefile.btf
+++ b/scripts/Makefile.btf
@@ -7,14 +7,7 @@ JOBS := $(patsubst -j%,%,$(filter -j%,$(MAKEFLAGS)))
ifeq ($(call test-le, $(pahole-ver), 125),y)
-# pahole 1.18 through 1.21 can't handle zero-sized per-CPU vars
-ifeq ($(call test-le, $(pahole-ver), 121),y)
-pahole-flags-$(call test-ge, $(pahole-ver), 118) += --skip_encoding_btf_vars
-endif
-
-pahole-flags-$(call test-ge, $(pahole-ver), 121) += --btf_gen_floats
-
-pahole-flags-$(call test-ge, $(pahole-ver), 122) += -j$(JOBS)
+pahole-flags-y += --btf_gen_floats -j$(JOBS)
pahole-flags-$(call test-ge, $(pahole-ver), 125) += --skip_encoding_btf_inconsistent_proto --btf_gen_optimized
@@ -25,13 +18,17 @@ pahole-flags-$(call test-ge, $(pahole-ver), 126) = -j$(JOBS) --btf_features=enc
pahole-flags-$(call test-ge, $(pahole-ver), 130) += --btf_features=attributes
-ifneq ($(KBUILD_EXTMOD),)
-module-pahole-flags-$(call test-ge, $(pahole-ver), 128) += --btf_features=distilled_base
-endif
+pahole-flags-$(call test-ge, $(pahole-ver), 131) += --btf_features=layout
endif
pahole-flags-$(CONFIG_PAHOLE_HAS_LANG_EXCLUDE) += --lang_exclude=rust
export PAHOLE_FLAGS := $(pahole-flags-y)
-export MODULE_PAHOLE_FLAGS := $(module-pahole-flags-y)
+
+resolve-btfids-flags-y :=
+resolve-btfids-flags-$(CONFIG_WERROR) += --fatal_warnings
+resolve-btfids-flags-$(if $(KBUILD_EXTMOD),y) += --distill_base
+resolve-btfids-flags-$(if $(KBUILD_VERBOSE),y) += --verbose
+
+export RESOLVE_BTFIDS_FLAGS := $(resolve-btfids-flags-y)
diff --git a/scripts/Makefile.build b/scripts/Makefile.build
index d0ee33a487be..3498d25b15e8 100644
--- a/scripts/Makefile.build
+++ b/scripts/Makefile.build
@@ -166,11 +166,13 @@ else ifeq ($(KBUILD_CHECKSRC),2)
cmd_force_checksrc = $(CHECK) $(CHECKFLAGS) $(c_flags) $<
endif
+ifeq ($(KBUILD_EXTMOD),)
ifneq ($(KBUILD_EXTRA_WARN),)
- cmd_checkdoc = PYTHONDONTWRITEBYTECODE=1 $(KERNELDOC) -none $(KDOCFLAGS) \
+ cmd_checkdoc = PYTHONDONTWRITEBYTECODE=1 $(PYTHON3) $(KERNELDOC) -none $(KDOCFLAGS) \
$(if $(findstring 2, $(KBUILD_EXTRA_WARN)), -Wall) \
$<
endif
+endif
# Compile C sources (.c)
# ---------------------------------------------------------------------------
@@ -308,16 +310,14 @@ $(obj)/%.lst: $(obj)/%.c FORCE
# The features in this list are the ones allowed for non-`rust/` code.
#
-# - Stable since Rust 1.81.0: `feature(lint_reasons)`.
-# - Stable since Rust 1.82.0: `feature(asm_const)`,
-# `feature(offset_of_nested)`, `feature(raw_ref_op)`.
# - Stable since Rust 1.87.0: `feature(asm_goto)`.
+# - Stable since Rust 1.89.0: `feature(generic_arg_infer)`.
# - Expected to become stable: `feature(arbitrary_self_types)`.
# - To be determined: `feature(used_with_arg)`.
#
# Please see https://github.com/Rust-for-Linux/linux/issues/2 for details on
# the unstable features in use.
-rust_allowed_features := asm_const,asm_goto,arbitrary_self_types,lint_reasons,offset_of_nested,raw_ref_op,used_with_arg
+rust_allowed_features := arbitrary_self_types,asm_goto,generic_arg_infer,used_with_arg
# `--out-dir` is required to avoid temporaries being created by `rustc` in the
# current working directory, which may be not accessible in the out-of-tree
@@ -330,7 +330,6 @@ rust_common_cmd = \
-Zcrate-attr='feature($(rust_allowed_features))' \
-Zunstable-options --extern pin_init --extern kernel \
--crate-type rlib -L $(objtree)/rust/ \
- --crate-name $(basename $(notdir $@)) \
--sysroot=/dev/null \
--out-dir $(dir $@) --emit=dep-info=$(depfile)
@@ -343,7 +342,12 @@ rust_common_cmd = \
# would not match each other.
quiet_cmd_rustc_o_rs = $(RUSTC_OR_CLIPPY_QUIET) $(quiet_modtag) $@
- cmd_rustc_o_rs = $(rust_common_cmd) --emit=obj=$@ $< $(cmd_objtool)
+ cmd_rustc_o_rs = $(rust_common_cmd) --emit=$(if $(CONFIG_RUST_INLINE_HELPERS),llvm-bc=$(patsubst %.o,%.bc,$@),obj=$@) $< \
+ $(if $(CONFIG_RUST_INLINE_HELPERS),;$(LLVM_LINK) --internalize --suppress-warnings $(patsubst %.o,%.bc,$@) \
+ $(objtree)/rust/helpers/helpers$(if $(part-of-module),_module).bc -o $(patsubst %.o,%.m.bc,$@); \
+ $(CC) $(CLANG_FLAGS) $(KBUILD_CFLAGS) -Wno-override-module -c $(patsubst %.o,%.m.bc,$@) -o $@ \
+ $(cmd_ld_single)) \
+ $(cmd_objtool)
define rule_rustc_o_rs
$(call cmd_and_fixdep,rustc_o_rs)
@@ -356,7 +360,7 @@ $(obj)/%.o: $(obj)/%.rs FORCE
quiet_cmd_rustc_rsi_rs = $(RUSTC_OR_CLIPPY_QUIET) $(quiet_modtag) $@
cmd_rustc_rsi_rs = \
$(rust_common_cmd) -Zunpretty=expanded $< >$@; \
- command -v $(RUSTFMT) >/dev/null && $(RUSTFMT) $@
+ command -v $(RUSTFMT) >/dev/null && $(RUSTFMT) --config-path $(srctree)/.rustfmt.toml $@
$(obj)/%.rsi: $(obj)/%.rs FORCE
+$(call if_changed_dep,rustc_rsi_rs)
@@ -527,18 +531,6 @@ ifneq ($(userprogs),)
include $(srctree)/scripts/Makefile.userprogs
endif
-ifneq ($(need-dtbslist)$(dtb-y)$(dtb-)$(filter %.dtb %.dtb.o %.dtbo.o,$(targets)),)
-include $(srctree)/scripts/Makefile.dtbs
-endif
-
-# Build
-# ---------------------------------------------------------------------------
-
-$(obj)/: $(if $(KBUILD_BUILTIN), $(targets-for-builtin)) \
- $(if $(KBUILD_MODULES), $(targets-for-modules)) \
- $(subdir-ym) $(always-y)
- @:
-
# Single targets
# ---------------------------------------------------------------------------
@@ -568,6 +560,20 @@ FORCE:
targets += $(filter-out $(single-subdir-goals), $(MAKECMDGOALS))
targets := $(filter-out $(PHONY), $(targets))
+# Now that targets is fully known, include dtb rules if needed
+ifneq ($(need-dtbslist)$(dtb-y)$(dtb-)$(filter %.dtb %.dtb.o %.dtbo.o,$(targets)),)
+include $(srctree)/scripts/Makefile.dtbs
+endif
+
+# Build
+# Needs to be after the include of Makefile.dtbs, which updates always-y
+# ---------------------------------------------------------------------------
+
+$(obj)/: $(if $(KBUILD_BUILTIN), $(targets-for-builtin)) \
+ $(if $(KBUILD_MODULES), $(targets-for-modules)) \
+ $(subdir-ym) $(always-y)
+ @:
+
# Read all saved command lines and dependencies for the $(targets) we
# may be building above, using $(if_changed{,_dep}). As an
# optimization, we don't need to read them if the target does not
diff --git a/scripts/Makefile.context-analysis b/scripts/Makefile.context-analysis
new file mode 100644
index 000000000000..cd3bb49d3f09
--- /dev/null
+++ b/scripts/Makefile.context-analysis
@@ -0,0 +1,11 @@
+# SPDX-License-Identifier: GPL-2.0
+
+context-analysis-cflags := -DWARN_CONTEXT_ANALYSIS \
+ -fexperimental-late-parse-attributes -Wthread-safety \
+ -Wthread-safety-pointer -Wthread-safety-beta
+
+ifndef CONFIG_WARN_CONTEXT_ANALYSIS_ALL
+context-analysis-cflags += --warning-suppression-mappings=$(srctree)/scripts/context-analysis-suppression.txt
+endif
+
+export CFLAGS_CONTEXT_ANALYSIS := $(context-analysis-cflags)
diff --git a/scripts/Makefile.dtbs b/scripts/Makefile.dtbs
index 2d321b813600..c4e466390284 100644
--- a/scripts/Makefile.dtbs
+++ b/scripts/Makefile.dtbs
@@ -1,5 +1,7 @@
# SPDX-License-Identifier: GPL-2.0-only
+all-dtb := $(dtb-y) $(dtb-)
+
# If CONFIG_OF_ALL_DTBS is enabled, all DT blobs are built
dtb-$(CONFIG_OF_ALL_DTBS) += $(dtb-)
@@ -10,6 +12,13 @@ real-dtb-y := $(call real-search, $(dtb-y), .dtb, -dtbs)
# Base DTB that overlay is applied onto
base-dtb-y := $(filter %.dtb, $(call real-search, $(multi-dtb-y), .dtb, -dtbs))
+# Ensure that any .dtbo is applied to at least one base .dtb. Otherwise, it
+# does not get validated.
+applied-dtbo := $(filter %.dtbo, \
+ $(call real-search, $(call multi-search, $(all-dtb), .dtb, -dtbs), .dtb, -dtbs))
+unapplied-dtbo := $(filter-out $(applied-dtbo),$(filter %.dtbo, $(dtb-y)))
+$(if $(unapplied-dtbo), $(warning .dtbo is not applied to any base: $(unapplied-dtbo)))
+
dtb-y := $(addprefix $(obj)/, $(dtb-y))
multi-dtb-y := $(addprefix $(obj)/, $(multi-dtb-y))
real-dtb-y := $(addprefix $(obj)/, $(real-dtb-y))
@@ -96,7 +105,6 @@ ifeq ($(findstring 1,$(KBUILD_EXTRA_WARN)),)
DTC_FLAGS += -Wno-unit_address_vs_reg \
-Wno-avoid_unnecessary_addr_size \
-Wno-alias_paths \
- -Wno-graph_child_address \
-Wno-interrupt_map \
-Wno-simple_bus_reg
else
diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib
index 1d581ba5df66..0718e39cedda 100644
--- a/scripts/Makefile.lib
+++ b/scripts/Makefile.lib
@@ -20,7 +20,7 @@ name-fix-token = $(subst $(comma),_,$(subst -,_,$1))
name-fix = $(call stringify,$(call name-fix-token,$1))
basename_flags = -DKBUILD_BASENAME=$(call name-fix,$(basetarget))
modname_flags = -DKBUILD_MODNAME=$(call name-fix,$(modname)) \
- -D__KBUILD_MODNAME=kmod_$(call name-fix-token,$(modname))
+ -D__KBUILD_MODNAME=$(call name-fix-token,$(modname))
modfile_flags = -DKBUILD_MODFILE=$(call stringify,$(modfile))
_c_flags = $(filter-out $(CFLAGS_REMOVE_$(target-stem).o), \
@@ -106,6 +106,16 @@ _c_flags += $(if $(patsubst n%,, \
endif
#
+# Enable context analysis flags only where explicitly opted in.
+# (depends on variables CONTEXT_ANALYSIS_obj.o, CONTEXT_ANALYSIS)
+#
+ifeq ($(CONFIG_WARN_CONTEXT_ANALYSIS),y)
+_c_flags += $(if $(patsubst n%,, \
+ $(CONTEXT_ANALYSIS_$(target-stem).o)$(CONTEXT_ANALYSIS)$(if $(is-kernel-object),$(CONFIG_WARN_CONTEXT_ANALYSIS_ALL))), \
+ $(CFLAGS_CONTEXT_ANALYSIS))
+endif
+
+#
# Enable AutoFDO build flags except some files or directories we don't want to
# enable (depends on variables AUTOFDO_PROFILE_obj.o and AUTOFDO_PROFILE).
#
@@ -191,13 +201,13 @@ objtool-args-$(CONFIG_HAVE_STATIC_CALL_INLINE) += --static-call
objtool-args-$(CONFIG_HAVE_UACCESS_VALIDATION) += --uaccess
objtool-args-$(or $(CONFIG_GCOV_KERNEL),$(CONFIG_KCOV)) += --no-unreachable
objtool-args-$(CONFIG_PREFIX_SYMBOLS) += --prefix=$(CONFIG_FUNCTION_PADDING_BYTES)
-objtool-args-$(CONFIG_OBJTOOL_WERROR) += --Werror
+objtool-args-$(CONFIG_OBJTOOL_WERROR) += --werror
objtool-args = $(objtool-args-y) \
$(if $(delay-objtool), --link) \
$(if $(part-of-module), --module)
-delay-objtool := $(or $(CONFIG_LTO_CLANG),$(CONFIG_X86_KERNEL_IBT))
+delay-objtool := $(or $(CONFIG_LTO_CLANG),$(CONFIG_X86_KERNEL_IBT),$(CONFIG_KLP_BUILD))
cmd_objtool = $(if $(objtool-enabled), ; $(objtool) $(objtool-args) $@)
cmd_gen_objtooldep = $(if $(objtool-enabled), { echo ; echo '$@: $$(wildcard $(objtool))' ; } >> $(dot-target).cmd)
@@ -400,7 +410,7 @@ FIT_COMPRESSION ?= gzip
quiet_cmd_fit = FIT $@
cmd_fit = $(MAKE_FIT) -o $@ --arch $(UIMAGE_ARCH) --os linux \
- --name '$(UIMAGE_NAME)' \
+ --name '$(UIMAGE_NAME)' $(FIT_EXTRA_ARGS) \
$(if $(findstring 1,$(KBUILD_VERBOSE)),-v) \
$(if $(FIT_DECOMPOSE_DTBS),--decompose-dtbs) \
--compress $(FIT_COMPRESSION) -k $< @$(word 2,$^)
diff --git a/scripts/Makefile.modfinal b/scripts/Makefile.modfinal
index 542ba462ed3e..adcbcde16a07 100644
--- a/scripts/Makefile.modfinal
+++ b/scripts/Makefile.modfinal
@@ -28,6 +28,10 @@ ccflags-remove-y := $(CC_FLAGS_CFI)
.module-common.o: $(srctree)/scripts/module-common.c FORCE
$(call if_changed_rule,cc_o_c)
+ifneq ($(WARN_ON_UNUSED_TRACEPOINTS),)
+cmd_check_tracepoint = $(objtree)/scripts/tracepoint-update --module $<;
+endif
+
quiet_cmd_ld_ko_o = LD [M] $@
cmd_ld_ko_o = \
$(LD) -r $(KBUILD_LDFLAGS) \
@@ -38,9 +42,8 @@ quiet_cmd_btf_ko = BTF [M] $@
cmd_btf_ko = \
if [ ! -f $(objtree)/vmlinux ]; then \
printf "Skipping BTF generation for %s due to unavailability of vmlinux\n" $@ 1>&2; \
- else \
- LLVM_OBJCOPY="$(OBJCOPY)" $(PAHOLE) -J $(PAHOLE_FLAGS) $(MODULE_PAHOLE_FLAGS) --btf_base $(objtree)/vmlinux $@; \
- $(RESOLVE_BTFIDS) -b $(objtree)/vmlinux $@; \
+ else \
+ $(CONFIG_SHELL) $(srctree)/scripts/gen-btf.sh --btf_base $(objtree)/vmlinux $@; \
fi;
# Same as newer-prereqs, but allows to exclude specified extra dependencies
@@ -57,6 +60,7 @@ if_changed_except = $(if $(call newer_prereqs_except,$(2))$(cmd-check), \
ifdef CONFIG_DEBUG_INFO_BTF_MODULES
+$(if $(newer-prereqs),$(call cmd,btf_ko))
endif
+ +$(call cmd,check_tracepoint)
targets += $(modules:%.o=%.ko) $(modules:%.o=%.mod.o) .module-common.o
diff --git a/scripts/Makefile.modinst b/scripts/Makefile.modinst
index 1628198f3e83..9ba45e5b32b1 100644
--- a/scripts/Makefile.modinst
+++ b/scripts/Makefile.modinst
@@ -100,7 +100,7 @@ endif
# Don't stop modules_install even if we can't sign external modules.
#
ifeq ($(filter pkcs11:%, $(CONFIG_MODULE_SIG_KEY)),)
-sig-key := $(if $(wildcard $(CONFIG_MODULE_SIG_KEY)),,$(srctree)/)$(CONFIG_MODULE_SIG_KEY)
+sig-key := $(if $(wildcard $(CONFIG_MODULE_SIG_KEY)),,$(objtree)/)$(CONFIG_MODULE_SIG_KEY)
else
sig-key := $(CONFIG_MODULE_SIG_KEY)
endif
diff --git a/scripts/Makefile.package b/scripts/Makefile.package
index 74bcb9e7f7a4..6d36786ba31c 100644
--- a/scripts/Makefile.package
+++ b/scripts/Makefile.package
@@ -189,6 +189,24 @@ tar-pkg: linux-$(KERNELRELEASE)-$(ARCH).tar
tar%-pkg: linux-$(KERNELRELEASE)-$(ARCH).tar.% FORCE
@:
+# modules-cpio-pkg - generate an initramfs with the modules
+# ---------------------------------------------------------------------------
+
+.tmp_modules_cpio: FORCE
+ $(Q)$(MAKE) -f $(srctree)/Makefile
+ $(Q)rm -rf $@
+ $(Q)$(MAKE) -f $(srctree)/Makefile INSTALL_MOD_PATH=$@/$(INSTALL_MOD_PATH) modules_install
+
+quiet_cmd_cpio = CPIO $@
+ cmd_cpio = $(CONFIG_SHELL) $(srctree)/usr/gen_initramfs.sh -o $@ $<
+
+modules-$(KERNELRELEASE)-$(ARCH).cpio: .tmp_modules_cpio
+ $(call cmd,cpio)
+
+PHONY += modules-cpio-pkg
+modules-cpio-pkg: modules-$(KERNELRELEASE)-$(ARCH).cpio
+ @:
+
# perf-tar*-src-pkg - generate a source tarball with perf source
# ---------------------------------------------------------------------------
@@ -245,6 +263,8 @@ help:
@echo ' tarbz2-pkg - Build the kernel as a bzip2 compressed tarball'
@echo ' tarxz-pkg - Build the kernel as a xz compressed tarball'
@echo ' tarzst-pkg - Build the kernel as a zstd compressed tarball'
+ @echo ' modules-cpio-pkg - Build the kernel modules as cpio archive'
+ @echo ' (uses INSTALL_MOD_PATH inside the archive)'
@echo ' perf-tar-src-pkg - Build the perf source tarball with no compression'
@echo ' perf-targz-src-pkg - Build the perf source tarball with gzip compression'
@echo ' perf-tarbz2-src-pkg - Build the perf source tarball with bz2 compression'
diff --git a/scripts/Makefile.vdsoinst b/scripts/Makefile.vdsoinst
index ac85f9a4a569..d9f7243217bc 100644
--- a/scripts/Makefile.vdsoinst
+++ b/scripts/Makefile.vdsoinst
@@ -19,9 +19,10 @@ __default: $$(dest)
$$(dest): $(1) FORCE
$$(call cmd,install)
-# Some architectures create .build-id symlinks
-ifneq ($(filter arm s390 sparc x86, $(SRCARCH)),)
-link := $(install-dir)/.build-id/$$(shell $(READELF) -n $(1) | sed -n 's@^.*Build ID: \(..\)\(.*\)@\1/\2@p').debug
+build-id-file := $$(shell $(READELF) -n $(1) 2>/dev/null | sed -n 's@^.*Build ID: \(..\)\(.*\)@\1/\2@p')
+
+ifneq ($$(build-id-file),)
+link := $(install-dir)/.build-id/$$(build-id-file).debug
__default: $$(link)
$$(link): $$(dest) FORCE
diff --git a/scripts/Makefile.vmlinux b/scripts/Makefile.vmlinux
index ced4379550d7..fcae1e432d9a 100644
--- a/scripts/Makefile.vmlinux
+++ b/scripts/Makefile.vmlinux
@@ -71,7 +71,7 @@ targets += vmlinux.unstripped .vmlinux.export.o
vmlinux.unstripped: scripts/link-vmlinux.sh vmlinux.o .vmlinux.export.o $(KBUILD_LDS) FORCE
+$(call if_changed_dep,link_vmlinux)
ifdef CONFIG_DEBUG_INFO_BTF
-vmlinux.unstripped: $(RESOLVE_BTFIDS)
+vmlinux.unstripped: $(RESOLVE_BTFIDS) $(srctree)/scripts/gen-btf.sh
endif
ifdef CONFIG_BUILDTIME_TABLE_SORT
@@ -102,11 +102,25 @@ vmlinux: vmlinux.unstripped FORCE
# modules.builtin.modinfo
# ---------------------------------------------------------------------------
+# .modinfo in vmlinux.unstripped is aligned to 8 bytes for compatibility with
+# tools that expect vmlinux to have sufficiently aligned sections but the
+# additional bytes used for padding .modinfo to satisfy this requirement break
+# certain versions of kmod with
+#
+# depmod: ERROR: kmod_builtin_iter_next: unexpected string without modname prefix
+#
+# Strip the trailing padding bytes after extracting .modinfo to comply with
+# what kmod expects to parse.
+quiet_cmd_modules_builtin_modinfo = GEN $@
+ cmd_modules_builtin_modinfo = $(cmd_objcopy); \
+ sed -i 's/\x00\+$$/\x00/g' $@; \
+ chmod -x $@
+
OBJCOPYFLAGS_modules.builtin.modinfo := -j .modinfo -O binary
targets += modules.builtin.modinfo
modules.builtin.modinfo: vmlinux.unstripped FORCE
- $(call if_changed,objcopy)
+ $(call if_changed,modules_builtin_modinfo)
# modules.builtin
# ---------------------------------------------------------------------------
diff --git a/scripts/Makefile.vmlinux_o b/scripts/Makefile.vmlinux_o
index 23c8751285d7..527352c222ff 100644
--- a/scripts/Makefile.vmlinux_o
+++ b/scripts/Makefile.vmlinux_o
@@ -41,7 +41,7 @@ objtool-enabled := $(or $(delay-objtool),$(CONFIG_NOINSTR_VALIDATION))
ifeq ($(delay-objtool),y)
vmlinux-objtool-args-y += $(objtool-args-y)
else
-vmlinux-objtool-args-$(CONFIG_OBJTOOL_WERROR) += --Werror
+vmlinux-objtool-args-$(CONFIG_OBJTOOL_WERROR) += --werror
endif
vmlinux-objtool-args-$(CONFIG_NOINSTR_VALIDATION) += --noinstr \
@@ -63,11 +63,15 @@ quiet_cmd_ld_vmlinux.o = LD $@
--start-group $(KBUILD_VMLINUX_LIBS) --end-group \
$(cmd_objtool)
+cmd_check_function_names = $(srctree)/scripts/check-function-names.sh $@
+
define rule_ld_vmlinux.o
$(call cmd_and_savecmd,ld_vmlinux.o)
$(call cmd,gen_objtooldep)
+ $(call cmd,check_function_names)
endef
+
vmlinux.o: $(initcalls-lds) vmlinux.a $(KBUILD_VMLINUX_LIBS) FORCE
$(call if_changed_rule,ld_vmlinux.o)
diff --git a/scripts/Makefile.extrawarn b/scripts/Makefile.warn
index 6af392f9cd02..e77ca875aea4 100644
--- a/scripts/Makefile.extrawarn
+++ b/scripts/Makefile.warn
@@ -16,7 +16,7 @@ KBUILD_CFLAGS += -Werror=return-type
KBUILD_CFLAGS += -Werror=strict-prototypes
KBUILD_CFLAGS += -Wno-format-security
KBUILD_CFLAGS += -Wno-trigraphs
-KBUILD_CFLAGS += $(call cc-option, -Wno-frame-address)
+KBUILD_CFLAGS += -Wno-frame-address
KBUILD_CFLAGS += $(call cc-option, -Wno-address-of-packed-member)
KBUILD_CFLAGS += -Wmissing-declarations
KBUILD_CFLAGS += -Wmissing-prototypes
@@ -28,9 +28,6 @@ endif
KBUILD_CFLAGS-$(CONFIG_CC_NO_ARRAY_BOUNDS) += -Wno-array-bounds
ifdef CONFIG_CC_IS_CLANG
-# The kernel builds with '-std=gnu11' so use of GNU extensions is acceptable.
-KBUILD_CFLAGS += -Wno-gnu
-
# Clang checks for overflow/truncation with '%p', while GCC does not:
# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=111219
KBUILD_CFLAGS += $(call cc-option, -Wno-format-overflow-non-kprintf)
@@ -53,6 +50,9 @@ else
KBUILD_CFLAGS += -Wno-main
endif
+# Too noisy on range checks and in macros handling both signed and unsigned.
+KBUILD_CFLAGS += -Wno-type-limits
+
# These result in bogus false positives
KBUILD_CFLAGS += $(call cc-option, -Wno-dangling-pointer)
@@ -70,7 +70,7 @@ KBUILD_CFLAGS += -Wno-pointer-sign
# In order to make sure new function cast mismatches are not introduced
# in the kernel (to avoid tripping CFI checking), the kernel should be
# globally built with -Wcast-function-type.
-KBUILD_CFLAGS += $(call cc-option, -Wcast-function-type)
+KBUILD_CFLAGS += -Wcast-function-type
# Currently, disable -Wstringop-overflow for GCC 11, globally.
KBUILD_CFLAGS-$(CONFIG_CC_NO_STRINGOP_OVERFLOW) += $(call cc-option, -Wno-stringop-overflow)
@@ -97,7 +97,7 @@ KBUILD_CFLAGS += $(KBUILD_CFLAGS-y) $(CONFIG_CC_IMPLICIT_FALLTHROUGH)
KBUILD_CFLAGS += -Werror=date-time
# enforce correct pointer usage
-KBUILD_CFLAGS += $(call cc-option,-Werror=incompatible-pointer-types)
+KBUILD_CFLAGS += -Werror=incompatible-pointer-types
# Require designated initializers for all marked structures
KBUILD_CFLAGS += $(call cc-option,-Werror=designated-init)
@@ -114,7 +114,7 @@ ifneq ($(findstring 1, $(KBUILD_EXTRA_WARN)),)
KBUILD_CFLAGS += -Wmissing-format-attribute
KBUILD_CFLAGS += -Wmissing-include-dirs
-KBUILD_CFLAGS += $(call cc-option, -Wunused-const-variable)
+KBUILD_CFLAGS += -Wunused-const-variable
KBUILD_CPPFLAGS += -Wundef
KBUILD_CPPFLAGS += -DKBUILD_EXTRA_WARN1
@@ -123,12 +123,12 @@ else
# Some diagnostics enabled by default are noisy.
# Suppress them by using -Wno... except for W=1.
-KBUILD_CFLAGS += $(call cc-option, -Wno-unused-but-set-variable)
-KBUILD_CFLAGS += $(call cc-option, -Wno-unused-const-variable)
+KBUILD_CFLAGS += -Wno-unused-but-set-variable
+KBUILD_CFLAGS += -Wno-unused-const-variable
KBUILD_CFLAGS += $(call cc-option, -Wno-packed-not-aligned)
KBUILD_CFLAGS += $(call cc-option, -Wno-format-overflow)
ifdef CONFIG_CC_IS_GCC
-KBUILD_CFLAGS += $(call cc-option, -Wno-format-truncation)
+KBUILD_CFLAGS += -Wno-format-truncation
endif
KBUILD_CFLAGS += $(call cc-option, -Wno-stringop-truncation)
@@ -143,14 +143,11 @@ KBUILD_CFLAGS += -Wno-format
# problematic.
KBUILD_CFLAGS += -Wformat-extra-args -Wformat-invalid-specifier
KBUILD_CFLAGS += -Wformat-zero-length -Wnonnull
-# Requires clang-12+.
-ifeq ($(call clang-min-version, 120000),y)
KBUILD_CFLAGS += -Wformat-insufficient-args
endif
-endif
-KBUILD_CFLAGS += $(call cc-option, -Wno-pointer-to-enum-cast)
+KBUILD_CFLAGS += -Wno-pointer-to-enum-cast
KBUILD_CFLAGS += -Wno-tautological-constant-out-of-range-compare
-KBUILD_CFLAGS += $(call cc-option, -Wno-unaligned-access)
+KBUILD_CFLAGS += -Wno-unaligned-access
KBUILD_CFLAGS += -Wno-enum-compare-conditional
endif
@@ -164,7 +161,7 @@ ifneq ($(findstring 2, $(KBUILD_EXTRA_WARN)),)
KBUILD_CFLAGS += -Wdisabled-optimization
KBUILD_CFLAGS += -Wshadow
KBUILD_CFLAGS += $(call cc-option, -Wlogical-op)
-KBUILD_CFLAGS += $(call cc-option, -Wunused-macros)
+KBUILD_CFLAGS += -Wunused-macros
KBUILD_CPPFLAGS += -DKBUILD_EXTRA_WARN2
@@ -172,7 +169,6 @@ else
# The following turn off the warnings enabled by -Wextra
KBUILD_CFLAGS += -Wno-missing-field-initializers
-KBUILD_CFLAGS += -Wno-type-limits
KBUILD_CFLAGS += -Wno-shift-negative-value
ifdef CONFIG_CC_IS_CLANG
diff --git a/scripts/atomic/gen-atomic-instrumented.sh b/scripts/atomic/gen-atomic-instrumented.sh
index 592f3ec89b5f..9c1d53f81eb2 100755
--- a/scripts/atomic/gen-atomic-instrumented.sh
+++ b/scripts/atomic/gen-atomic-instrumented.sh
@@ -12,7 +12,7 @@ gen_param_check()
local arg="$1"; shift
local type="${arg%%:*}"
local name="$(gen_param_name "${arg}")"
- local rw="write"
+ local rw="atomic_write"
case "${type#c}" in
i) return;;
@@ -20,14 +20,17 @@ gen_param_check()
if [ ${type#c} != ${type} ]; then
# We don't write to constant parameters.
- rw="read"
+ rw="atomic_read"
+ elif [ "${type}" = "p" ] ; then
+ # The "old" argument in try_cmpxchg() gets accessed non-atomically
+ rw="read_write"
elif [ "${meta}" != "s" ]; then
# An atomic RMW: if this parameter is not a constant, and this atomic is
# not just a 's'tore, this parameter is both read from and written to.
- rw="read_write"
+ rw="atomic_read_write"
fi
- printf "\tinstrument_atomic_${rw}(${name}, sizeof(*${name}));\n"
+ printf "\tinstrument_${rw}(${name}, sizeof(*${name}));\n"
}
#gen_params_checks(meta, arg...)
diff --git a/scripts/atomic/gen-rust-atomic-helpers.sh b/scripts/atomic/gen-rust-atomic-helpers.sh
index 45b1e100ed7c..a3732153af29 100755
--- a/scripts/atomic/gen-rust-atomic-helpers.sh
+++ b/scripts/atomic/gen-rust-atomic-helpers.sh
@@ -47,11 +47,6 @@ cat << EOF
#include <linux/atomic.h>
-// TODO: Remove this after INLINE_HELPERS support is added.
-#ifndef __rust_helper
-#define __rust_helper
-#endif
-
EOF
grep '^[a-z]' "$1" | while read name meta args; do
diff --git a/scripts/atomic/kerneldoc/try_cmpxchg b/scripts/atomic/kerneldoc/try_cmpxchg
index 3ccff29538f5..4dfc7a167ea1 100644
--- a/scripts/atomic/kerneldoc/try_cmpxchg
+++ b/scripts/atomic/kerneldoc/try_cmpxchg
@@ -11,6 +11,6 @@ cat <<EOF
*
* ${desc_noinstr}
*
- * Return: @true if the exchange occured, @false otherwise.
+ * Return: @true if the exchange occurred, @false otherwise.
*/
EOF
diff --git a/scripts/bloat-o-meter b/scripts/bloat-o-meter
index 888ce286a351..9b4fb996d95b 100755
--- a/scripts/bloat-o-meter
+++ b/scripts/bloat-o-meter
@@ -18,8 +18,8 @@ group.add_argument('-c', help='categorize output based on symbol type', action='
group.add_argument('-d', help='Show delta of Data Section', action='store_true')
group.add_argument('-t', help='Show delta of text Section', action='store_true')
parser.add_argument('-p', dest='prefix', help='Arch prefix for the tool being used. Useful in cross build scenarios')
-parser.add_argument('file1', help='First file to compare')
-parser.add_argument('file2', help='Second file to compare')
+parser.add_argument('file_old', help='First file to compare')
+parser.add_argument('file_new', help='Second file to compare')
args = parser.parse_args()
@@ -42,6 +42,7 @@ def getsizes(file, format):
if name.startswith("__se_sys"): continue
if name.startswith("__se_compat_sys"): continue
if name.startswith("__addressable_"): continue
+ if name.startswith("__noinstr_text_start"): continue
if name == "linux_banner": continue
if name == "vermagic": continue
# statics and some other optimizations adds random .NUMBER
@@ -85,7 +86,7 @@ def calc(oldfile, newfile, format):
def print_result(symboltype, symbolformat):
grow, shrink, add, remove, up, down, delta, old, new, otot, ntot = \
- calc(args.file1, args.file2, symbolformat)
+ calc(args.file_old, args.file_new, symbolformat)
print("add/remove: %s/%s grow/shrink: %s/%s up/down: %s/%s (%s)" % \
(add, remove, grow, shrink, up, -down, up-down))
diff --git a/scripts/cc-can-link.sh b/scripts/cc-can-link.sh
index 6efcead31989..58dc7dd6d556 100755
--- a/scripts/cc-can-link.sh
+++ b/scripts/cc-can-link.sh
@@ -1,11 +1,11 @@
#!/bin/sh
# SPDX-License-Identifier: GPL-2.0
-cat << "END" | $@ -x c - -o /dev/null >/dev/null 2>&1
+cat << "END" | $@ -Werror -Wl,--fatal-warnings -x c - -o /dev/null >/dev/null 2>&1
#include <stdio.h>
int main(void)
{
- printf("");
+ printf("\n");
return 0;
}
END
diff --git a/scripts/check-function-names.sh b/scripts/check-function-names.sh
new file mode 100755
index 000000000000..08071133e5a5
--- /dev/null
+++ b/scripts/check-function-names.sh
@@ -0,0 +1,25 @@
+#!/bin/sh
+# SPDX-License-Identifier: GPL-2.0
+#
+# Certain function names are disallowed due to section name ambiguities
+# introduced by -ffunction-sections.
+#
+# See the comment above TEXT_MAIN in include/asm-generic/vmlinux.lds.h.
+
+objfile="$1"
+
+if [ ! -f "$objfile" ]; then
+ echo "usage: $0 <file.o>" >&2
+ exit 1
+fi
+
+bad_symbols=$(${NM:-nm} "$objfile" | awk '$2 ~ /^[TtWw]$/ {print $3}' | grep -E '^(startup|exit|split|unlikely|hot|unknown)(\.|$)')
+
+if [ -n "$bad_symbols" ]; then
+ echo "$bad_symbols" | while read -r sym; do
+ echo "$objfile: error: $sym() function name creates ambiguity with -ffunction-sections" >&2
+ done
+ exit 1
+fi
+
+exit 0
diff --git a/scripts/check-uapi.sh b/scripts/check-uapi.sh
index 955581735cb3..c8beec58871c 100755
--- a/scripts/check-uapi.sh
+++ b/scripts/check-uapi.sh
@@ -33,9 +33,10 @@ Options:
-v Verbose operation (print more information about each header being checked).
Environmental args:
- ABIDIFF Custom path to abidiff binary
- CC C compiler (default is "gcc")
- ARCH Target architecture for the UAPI check (default is host arch)
+ ABIDIFF Custom path to abidiff binary
+ CROSS_COMPILE Toolchain prefix for compiler
+ CC C compiler (default is "\${CROSS_COMPILE}gcc")
+ ARCH Target architecture for the UAPI check (default is host arch)
Exit codes:
$SUCCESS) Success
@@ -178,8 +179,11 @@ do_compile() {
local -r inc_dir="$1"
local -r header="$2"
local -r out="$3"
- printf "int main(void) { return 0; }\n" | \
- "$CC" -c \
+ printf "int f(void) { return 0; }\n" | \
+ "$CC" \
+ -shared \
+ -nostdlib \
+ -fPIC \
-o "$out" \
-x c \
-O0 \
@@ -187,6 +191,7 @@ do_compile() {
-fno-eliminate-unused-debug-types \
-g \
"-I${inc_dir}" \
+ "-Iusr/dummy-include" \
-include "$header" \
-
}
@@ -195,7 +200,7 @@ do_compile() {
run_make_headers_install() {
local -r ref="$1"
local -r install_dir="$(get_header_tree "$ref")"
- make -j "$MAX_THREADS" ARCH="$ARCH" INSTALL_HDR_PATH="$install_dir" \
+ make -j "$MAX_THREADS" CROSS_COMPILE="${CROSS_COMPILE}" ARCH="$ARCH" INSTALL_HDR_PATH="$install_dir" \
headers_install > /dev/null
}
@@ -404,7 +409,7 @@ min_version_is_satisfied() {
# Make sure we have the tools we need and the arguments make sense
check_deps() {
ABIDIFF="${ABIDIFF:-abidiff}"
- CC="${CC:-gcc}"
+ CC="${CC:-${CROSS_COMPILE}gcc}"
ARCH="${ARCH:-$(uname -m)}"
if [ "$ARCH" = "x86_64" ]; then
ARCH="x86"
diff --git a/scripts/check-variable-fonts.sh b/scripts/check-variable-fonts.sh
deleted file mode 100755
index ce63f0acea5f..000000000000
--- a/scripts/check-variable-fonts.sh
+++ /dev/null
@@ -1,115 +0,0 @@
-#!/bin/sh
-# SPDX-License-Identifier: GPL-2.0-only
-# Copyright (C) Akira Yokosawa, 2024
-#
-# For "make pdfdocs", reports of build errors of translations.pdf started
-# arriving early 2024 [1, 2]. It turned out that Fedora and openSUSE
-# tumbleweed have started deploying variable-font [3] format of "Noto CJK"
-# fonts [4, 5]. For PDF, a LaTeX package named xeCJK is used for CJK
-# (Chinese, Japanese, Korean) pages. xeCJK requires XeLaTeX/XeTeX, which
-# does not (and likely never will) understand variable fonts for historical
-# reasons.
-#
-# The build error happens even when both of variable- and non-variable-format
-# fonts are found on the build system. To make matters worse, Fedora enlists
-# variable "Noto CJK" fonts in the requirements of langpacks-ja, -ko, -zh_CN,
-# -zh_TW, etc. Hence developers who have interest in CJK pages are more
-# likely to encounter the build errors.
-#
-# This script is invoked from the error path of "make pdfdocs" and emits
-# suggestions if variable-font files of "Noto CJK" fonts are in the list of
-# fonts accessible from XeTeX.
-#
-# References:
-# [1]: https://lore.kernel.org/r/8734tqsrt7.fsf@meer.lwn.net/
-# [2]: https://lore.kernel.org/r/1708585803.600323099@f111.i.mail.ru/
-# [3]: https://en.wikipedia.org/wiki/Variable_font
-# [4]: https://fedoraproject.org/wiki/Changes/Noto_CJK_Variable_Fonts
-# [5]: https://build.opensuse.org/request/show/1157217
-#
-#===========================================================================
-# Workarounds for building translations.pdf
-#===========================================================================
-#
-# * Denylist "variable font" Noto CJK fonts.
-# - Create $HOME/deny-vf/fontconfig/fonts.conf from template below, with
-# tweaks if necessary. Remove leading "# ".
-# - Path of fontconfig/fonts.conf can be overridden by setting an env
-# variable FONTS_CONF_DENY_VF.
-#
-# * Template:
-# -----------------------------------------------------------------
-# <?xml version="1.0"?>
-# <!DOCTYPE fontconfig SYSTEM "urn:fontconfig:fonts.dtd">
-# <fontconfig>
-# <!--
-# Ignore variable-font glob (not to break xetex)
-# -->
-# <selectfont>
-# <rejectfont>
-# <!--
-# for Fedora
-# -->
-# <glob>/usr/share/fonts/google-noto-*-cjk-vf-fonts</glob>
-# <!--
-# for openSUSE tumbleweed
-# -->
-# <glob>/usr/share/fonts/truetype/Noto*CJK*-VF.otf</glob>
-# </rejectfont>
-# </selectfont>
-# </fontconfig>
-# -----------------------------------------------------------------
-#
-# The denylisting is activated for "make pdfdocs".
-#
-# * For skipping CJK pages in PDF
-# - Uninstall texlive-xecjk.
-# Denylisting is not needed in this case.
-#
-# * For printing CJK pages in PDF
-# - Need non-variable "Noto CJK" fonts.
-# * Fedora
-# - google-noto-sans-cjk-fonts
-# - google-noto-serif-cjk-fonts
-# * openSUSE tumbleweed
-# - Non-variable "Noto CJK" fonts are not available as distro packages
-# as of April, 2024. Fetch a set of font files from upstream Noto
-# CJK Font released at:
-# https://github.com/notofonts/noto-cjk/tree/main/Sans#super-otc
-# and at:
-# https://github.com/notofonts/noto-cjk/tree/main/Serif#super-otc
-# , then uncompress and deploy them.
-# - Remember to update fontconfig cache by running fc-cache.
-#
-# !!! Caution !!!
-# Uninstalling "variable font" packages can be dangerous.
-# They might be depended upon by other packages important for your work.
-# Denylisting should be less invasive, as it is effective only while
-# XeLaTeX runs in "make pdfdocs".
-
-# Default per-user fontconfig path (overridden by env variable)
-: ${FONTS_CONF_DENY_VF:=$HOME/deny-vf}
-
-export XDG_CONFIG_HOME=${FONTS_CONF_DENY_VF}
-
-notocjkvffonts=`fc-list : file family variable | \
- grep 'variable=True' | \
- grep -E -e 'Noto (Sans|Sans Mono|Serif) CJK' | \
- sed -e 's/^/ /' -e 's/: Noto S.*$//' | sort | uniq`
-
-if [ "x$notocjkvffonts" != "x" ] ; then
- echo '============================================================================='
- echo 'XeTeX is confused by "variable font" files listed below:'
- echo "$notocjkvffonts"
- echo
- echo 'For CJK pages in PDF, they need to be hidden from XeTeX by denylisting.'
- echo 'Or, CJK pages can be skipped by uninstalling texlive-xecjk.'
- echo
- echo 'For more info on denylisting, other options, and variable font, see header'
- echo 'comments of scripts/check-variable-fonts.sh.'
- echo '============================================================================='
-fi
-
-# As this script is invoked from Makefile's error path, always error exit
-# regardless of whether any variable font is discovered or not.
-exit 1
diff --git a/scripts/checker-valid.sh b/scripts/checker-valid.sh
new file mode 100755
index 000000000000..625a789ed1c8
--- /dev/null
+++ b/scripts/checker-valid.sh
@@ -0,0 +1,19 @@
+#!/bin/sh -eu
+# SPDX-License-Identifier: GPL-2.0
+
+[ ! -x "$(command -v "$1")" ] && exit 1
+
+tmp_file=$(mktemp)
+trap "rm -f $tmp_file" EXIT
+
+cat << EOF >$tmp_file
+static inline int u(const int *q)
+{
+ __typeof_unqual__(*q) v = *q;
+ return v;
+}
+EOF
+
+# sparse happily exits with 0 on error so validate
+# there is none on stderr. Use awk as grep is a pain with sh -e
+$@ $tmp_file 2>&1 | awk -v c=1 '/error/{c=0}END{print c}'
diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl
index 92669904eecc..0492d6afc9a1 100755
--- a/scripts/checkpatch.pl
+++ b/scripts/checkpatch.pl
@@ -641,6 +641,7 @@ our $signature_tags = qr{(?xi:
Reviewed-by:|
Reported-by:|
Suggested-by:|
+ Assisted-by:|
To:|
Cc:
)};
@@ -860,6 +861,12 @@ our %deprecated_apis = (
"kunmap" => "kunmap_local",
"kmap_atomic" => "kmap_local_page",
"kunmap_atomic" => "kunmap_local",
+ #These should be enough to drive away new IDR users
+ "DEFINE_IDR" => "DEFINE_XARRAY",
+ "idr_init" => "xa_init",
+ "idr_init_base" => "xa_init_flags",
+ "rcu_read_lock_trace" => "rcu_read_lock_tasks_trace",
+ "rcu_read_unlock_trace" => "rcu_read_unlock_tasks_trace",
);
#Create a search pattern for all these strings to speed up a loop below
@@ -1096,7 +1103,9 @@ our $declaration_macros = qr{(?x:
(?:$Storage\s+)?(?:[A-Z_][A-Z0-9]*_){0,2}(?:DEFINE|DECLARE)(?:_[A-Z0-9]+){1,6}\s*\(|
(?:$Storage\s+)?[HLP]?LIST_HEAD\s*\(|
(?:SKCIPHER_REQUEST|SHASH_DESC|AHASH_REQUEST)_ON_STACK\s*\(|
- (?:$Storage\s+)?(?:XA_STATE|XA_STATE_ORDER)\s*\(
+ (?:$Storage\s+)?(?:XA_STATE|XA_STATE_ORDER)\s*\(|
+ __cacheline_group_(?:begin|end)(?:_aligned)?\s*\(|
+ __dma_from_device_group_(?:begin|end)\s*\(
)};
our %allow_repeated_words = (
@@ -2920,7 +2929,7 @@ sub process {
}
$checklicenseline = 1;
- if ($realfile !~ /^MAINTAINERS/) {
+ if ($realfile !~ /^(MAINTAINERS|dev\/null)/) {
my $last_binding_patch = $is_binding_patch;
$is_binding_patch = () = $realfile =~ m@^(?:Documentation/devicetree/|include/dt-bindings/)@;
@@ -3027,6 +3036,16 @@ sub process {
}
}
+# Check for invalid patch separator
+ if ($in_commit_log &&
+ $line =~ /^---.+/) {
+ if (ERROR("BAD_COMMIT_SEPARATOR",
+ "Invalid commit separator - some tools may have problems applying this\n" . $herecurr) &&
+ $fix) {
+ $fixed[$fixlinenr] =~ s/-/=/g;
+ }
+ }
+
# Check for patch separator
if ($line =~ /^---$/) {
$has_patch_separator = 1;
@@ -3087,6 +3106,15 @@ sub process {
}
}
+ # Assisted-by uses AGENT_NAME:MODEL_VERSION format, not email
+ if ($sign_off =~ /^Assisted-by:/i) {
+ if ($email !~ /^\S+:\S+/) {
+ WARN("BAD_SIGN_OFF",
+ "Assisted-by expects 'AGENT_NAME:MODEL_VERSION [TOOL1] [TOOL2]' format\n" . $herecurr);
+ }
+ next;
+ }
+
my ($email_name, $name_comment, $email_address, $comment) = parse_email($email);
my $suggested_email = format_email(($email_name, $name_comment, $email_address, $comment));
if ($suggested_email eq "") {
@@ -3345,6 +3373,13 @@ sub process {
}
}
+# Check for auto-generated unhandled placeholder text (mostly for cover letters)
+ if (($in_commit_log || $in_header_lines) &&
+ $rawline =~ /(?:SUBJECT|BLURB) HERE/) {
+ ERROR("PLACEHOLDER_USE",
+ "Placeholder text detected\n" . $herecurr);
+ }
+
# Check for git id commit length and improperly formed commit descriptions
# A correctly formed commit description is:
# commit <SHA-1 hash length 12+ chars> ("Complete commit subject")
@@ -3831,6 +3866,14 @@ sub process {
"Misplaced SPDX-License-Identifier tag - use line $checklicenseline instead\n" . $herecurr);
}
+# check for disallowed SPDX file tags
+ if ($rawline =~ /\bSPDX-.*:/ &&
+ $rawline !~ /\bSPDX-License-Identifier:/ &&
+ $rawline !~ /\bSPDX-FileCopyrightText:/) {
+ WARN("SPDX_LICENSE_TAG",
+ "Disallowed SPDX tag\n" . $herecurr);
+ }
+
# line length limit (with some exclusions)
#
# There are a few types of lines that may extend beyond $max_line_length:
@@ -6722,6 +6765,13 @@ sub process {
}
}
+# check for context_unsafe without a comment.
+ if ($line =~ /\bcontext_unsafe\b/ &&
+ !ctx_has_comment($first_line, $linenr)) {
+ WARN("CONTEXT_UNSAFE",
+ "context_unsafe without comment\n" . $herecurr);
+ }
+
# check of hardware specific defines
if ($line =~ m@^.\s*\#\s*if.*\b(__i386__|__powerpc64__|__sun__|__s390x__)\b@ && $realfile !~ m@include/asm-@) {
CHK("ARCH_DEFINES",
@@ -7247,17 +7297,42 @@ sub process {
"Prefer $3(sizeof(*$1)...) over $3($4...)\n" . $herecurr);
}
-# check for (kv|k)[mz]alloc with multiplies that could be kmalloc_array/kvmalloc_array/kvcalloc/kcalloc
+# check for (kv|k)[mz]alloc that could be kmalloc_obj/kvmalloc_obj/kzalloc_obj/kvzalloc_obj
+ if ($perl_version_ok &&
+ defined $stat &&
+ $stat =~ /^\+\s*($Lval)\s*\=\s*(?:$balanced_parens)?\s*((?:kv|k)[mz]alloc)\s*\(\s*($FuncArg)\s*,/) {
+ my $oldfunc = $3;
+ my $a1 = $4;
+ my $newfunc = "kmalloc_obj";
+ $newfunc = "kvmalloc_obj" if ($oldfunc eq "kvmalloc");
+ $newfunc = "kvzalloc_obj" if ($oldfunc eq "kvzalloc");
+ $newfunc = "kzalloc_obj" if ($oldfunc eq "kzalloc");
+
+ if ($a1 =~ s/^sizeof\s*\S\(?([^\)]*)\)?$/$1/) {
+ my $cnt = statement_rawlines($stat);
+ my $herectx = get_stat_here($linenr, $cnt, $here);
+
+ if (WARN("ALLOC_WITH_SIZEOF",
+ "Prefer $newfunc over $oldfunc with sizeof\n" . $herectx) &&
+ $cnt == 1 &&
+ $fix) {
+ $fixed[$fixlinenr] =~ s/\b($Lval)\s*\=\s*(?:$balanced_parens)?\s*((?:kv|k)[mz]alloc)\s*\(\s*($FuncArg)\s*,/$1 = $newfunc($a1,/;
+ }
+ }
+ }
+
+
+# check for (kv|k)[mz]alloc with multiplies that could be kmalloc_objs/kvmalloc_objs/kzalloc_objs/kvzalloc_objs
if ($perl_version_ok &&
defined $stat &&
$stat =~ /^\+\s*($Lval)\s*\=\s*(?:$balanced_parens)?\s*((?:kv|k)[mz]alloc)\s*\(\s*($FuncArg)\s*\*\s*($FuncArg)\s*,/) {
my $oldfunc = $3;
my $a1 = $4;
my $a2 = $10;
- my $newfunc = "kmalloc_array";
- $newfunc = "kvmalloc_array" if ($oldfunc eq "kvmalloc");
- $newfunc = "kvcalloc" if ($oldfunc eq "kvzalloc");
- $newfunc = "kcalloc" if ($oldfunc eq "kzalloc");
+ my $newfunc = "kmalloc_objs";
+ $newfunc = "kvmalloc_objs" if ($oldfunc eq "kvmalloc");
+ $newfunc = "kvzalloc_objs" if ($oldfunc eq "kvzalloc");
+ $newfunc = "kzalloc_objs" if ($oldfunc eq "kzalloc");
my $r1 = $a1;
my $r2 = $a2;
if ($a1 =~ /^sizeof\s*\S/) {
@@ -7273,7 +7348,9 @@ sub process {
"Prefer $newfunc over $oldfunc with multiply\n" . $herectx) &&
$cnt == 1 &&
$fix) {
- $fixed[$fixlinenr] =~ s/\b($Lval)\s*\=\s*(?:$balanced_parens)?\s*((?:kv|k)[mz]alloc)\s*\(\s*($FuncArg)\s*\*\s*($FuncArg)/$1 . ' = ' . "$newfunc(" . trim($r1) . ', ' . trim($r2)/e;
+ my $sized = trim($r2);
+ $sized =~ s/^sizeof\s*\S\(?([^\)]*)\)?$/$1/;
+ $fixed[$fixlinenr] =~ s/\b($Lval)\s*\=\s*(?:$balanced_parens)?\s*((?:kv|k)[mz]alloc)\s*\(\s*($FuncArg)\s*\*\s*($FuncArg)/$1 . ' = ' . "$newfunc(" . $sized . ', ' . trim($r1)/e;
}
}
}
@@ -7443,10 +7520,10 @@ sub process {
}
# check for various structs that are normally const (ops, kgdb, device_tree)
-# and avoid what seem like struct definitions 'struct foo {'
+# and avoid what seem like struct definitions 'struct foo {' or forward declarations 'struct foo;'
if (defined($const_structs) &&
$line !~ /\bconst\b/ &&
- $line =~ /\bstruct\s+($const_structs)\b(?!\s*\{)/) {
+ $line =~ /\bstruct\s+($const_structs)\b(?!\s*[\{;])/) {
WARN("CONST_STRUCT",
"struct $1 should normally be const\n" . $herecurr);
}
@@ -7721,6 +7798,12 @@ sub process {
ERROR("MISSING_SENTINEL", "missing sentinel in ID array\n" . "$here\n$stat\n");
}
}
+
+# check for uninitialized pointers with __free attribute
+ while ($line =~ /\*\s*($Ident)\s+__free\s*\(\s*$Ident\s*\)\s*[,;]/g) {
+ ERROR("UNINITIALIZED_PTR_WITH_FREE",
+ "pointer '$1' with __free attribute should be initialized\n" . $herecurr);
+ }
}
# If we have no input at all, then there is nothing to report on
diff --git a/scripts/checksyscalls.sh b/scripts/checksyscalls.sh
index 1e5d2eeb726d..e2970421c1ff 100755
--- a/scripts/checksyscalls.sh
+++ b/scripts/checksyscalls.sh
@@ -10,6 +10,10 @@
# checksyscalls.sh gcc gcc-options
#
+set -e
+
+reference_table="$(dirname $0)/../arch/x86/entry/syscalls/syscall_32.tbl"
+
ignore_list() {
cat << EOF
#include <asm/types.h>
@@ -269,5 +273,10 @@ syscall_list() {
done
}
-(ignore_list && syscall_list $(dirname $0)/../arch/x86/entry/syscalls/syscall_32.tbl) | \
+(ignore_list && syscall_list ${reference_table}) | \
$* -Wno-error -Wno-unused-macros -E -x c - > /dev/null
+
+# For fixdep
+if [ -n "${DEPFILE}" ]; then
+ echo "${0}: ${0} ${reference_table}" >> "${DEPFILE}"
+fi
diff --git a/scripts/checktransupdate.py b/scripts/checktransupdate.py
deleted file mode 100755
index e39529e46c3d..000000000000
--- a/scripts/checktransupdate.py
+++ /dev/null
@@ -1,307 +0,0 @@
-#!/usr/bin/env python3
-# SPDX-License-Identifier: GPL-2.0
-
-"""
-This script helps track the translation status of the documentation
-in different locales, e.g., zh_CN. More specially, it uses `git log`
-commit to find the latest english commit from the translation commit
-(order by author date) and the latest english commits from HEAD. If
-differences occur, report the file and commits that need to be updated.
-
-The usage is as follows:
-- ./scripts/checktransupdate.py -l zh_CN
-This will print all the files that need to be updated or translated in the zh_CN locale.
-- ./scripts/checktransupdate.py Documentation/translations/zh_CN/dev-tools/testing-overview.rst
-This will only print the status of the specified file.
-
-The output is something like:
-Documentation/dev-tools/kfence.rst
-No translation in the locale of zh_CN
-
-Documentation/translations/zh_CN/dev-tools/testing-overview.rst
-commit 42fb9cfd5b18 ("Documentation: dev-tools: Add link to RV docs")
-1 commits needs resolving in total
-"""
-
-import os
-import re
-import time
-import logging
-from argparse import ArgumentParser, ArgumentTypeError, BooleanOptionalAction
-from datetime import datetime
-
-
-def get_origin_path(file_path):
- """Get the origin path from the translation path"""
- paths = file_path.split("/")
- tidx = paths.index("translations")
- opaths = paths[:tidx]
- opaths += paths[tidx + 2 :]
- return "/".join(opaths)
-
-
-def get_latest_commit_from(file_path, commit):
- """Get the latest commit from the specified commit for the specified file"""
- command = f"git log --pretty=format:%H%n%aD%n%cD%n%n%B {commit} -1 -- {file_path}"
- logging.debug(command)
- pipe = os.popen(command)
- result = pipe.read()
- result = result.split("\n")
- if len(result) <= 1:
- return None
-
- logging.debug("Result: %s", result[0])
-
- return {
- "hash": result[0],
- "author_date": datetime.strptime(result[1], "%a, %d %b %Y %H:%M:%S %z"),
- "commit_date": datetime.strptime(result[2], "%a, %d %b %Y %H:%M:%S %z"),
- "message": result[4:],
- }
-
-
-def get_origin_from_trans(origin_path, t_from_head):
- """Get the latest origin commit from the translation commit"""
- o_from_t = get_latest_commit_from(origin_path, t_from_head["hash"])
- while o_from_t is not None and o_from_t["author_date"] > t_from_head["author_date"]:
- o_from_t = get_latest_commit_from(origin_path, o_from_t["hash"] + "^")
- if o_from_t is not None:
- logging.debug("tracked origin commit id: %s", o_from_t["hash"])
- return o_from_t
-
-
-def get_origin_from_trans_smartly(origin_path, t_from_head):
- """Get the latest origin commit from the formatted translation commit:
- (1) update to commit HASH (TITLE)
- (2) Update the translation through commit HASH (TITLE)
- """
- # catch flag for 12-bit commit hash
- HASH = r'([0-9a-f]{12})'
- # pattern 1: contains "update to commit HASH"
- pat_update_to = re.compile(rf'update to commit {HASH}')
- # pattern 2: contains "Update the translation through commit HASH"
- pat_update_translation = re.compile(rf'Update the translation through commit {HASH}')
-
- origin_commit_hash = None
- for line in t_from_head["message"]:
- # check if the line matches the first pattern
- match = pat_update_to.search(line)
- if match:
- origin_commit_hash = match.group(1)
- break
- # check if the line matches the second pattern
- match = pat_update_translation.search(line)
- if match:
- origin_commit_hash = match.group(1)
- break
- if origin_commit_hash is None:
- return None
- o_from_t = get_latest_commit_from(origin_path, origin_commit_hash)
- if o_from_t is not None:
- logging.debug("tracked origin commit id: %s", o_from_t["hash"])
- return o_from_t
-
-
-def get_commits_count_between(opath, commit1, commit2):
- """Get the commits count between two commits for the specified file"""
- command = f"git log --pretty=format:%H {commit1}...{commit2} -- {opath}"
- logging.debug(command)
- pipe = os.popen(command)
- result = pipe.read().split("\n")
- # filter out empty lines
- result = list(filter(lambda x: x != "", result))
- return result
-
-
-def pretty_output(commit):
- """Pretty print the commit message"""
- command = f"git log --pretty='format:%h (\"%s\")' -1 {commit}"
- logging.debug(command)
- pipe = os.popen(command)
- return pipe.read()
-
-
-def valid_commit(commit):
- """Check if the commit is valid or not"""
- msg = pretty_output(commit)
- return "Merge tag" not in msg
-
-def check_per_file(file_path):
- """Check the translation status for the specified file"""
- opath = get_origin_path(file_path)
-
- if not os.path.isfile(opath):
- logging.error("Cannot find the origin path for {file_path}")
- return
-
- o_from_head = get_latest_commit_from(opath, "HEAD")
- t_from_head = get_latest_commit_from(file_path, "HEAD")
-
- if o_from_head is None or t_from_head is None:
- logging.error("Cannot find the latest commit for %s", file_path)
- return
-
- o_from_t = get_origin_from_trans_smartly(opath, t_from_head)
- # notice, o_from_t from get_*_smartly() is always more accurate than from get_*()
- if o_from_t is None:
- o_from_t = get_origin_from_trans(opath, t_from_head)
-
- if o_from_t is None:
- logging.error("Error: Cannot find the latest origin commit for %s", file_path)
- return
-
- if o_from_head["hash"] == o_from_t["hash"]:
- logging.debug("No update needed for %s", file_path)
- else:
- logging.info(file_path)
- commits = get_commits_count_between(
- opath, o_from_t["hash"], o_from_head["hash"]
- )
- count = 0
- for commit in commits:
- if valid_commit(commit):
- logging.info("commit %s", pretty_output(commit))
- count += 1
- logging.info("%d commits needs resolving in total\n", count)
-
-
-def valid_locales(locale):
- """Check if the locale is valid or not"""
- script_path = os.path.dirname(os.path.abspath(__file__))
- linux_path = os.path.join(script_path, "..")
- if not os.path.isdir(f"{linux_path}/Documentation/translations/{locale}"):
- raise ArgumentTypeError("Invalid locale: {locale}")
- return locale
-
-
-def list_files_with_excluding_folders(folder, exclude_folders, include_suffix):
- """List all files with the specified suffix in the folder and its subfolders"""
- files = []
- stack = [folder]
-
- while stack:
- pwd = stack.pop()
- # filter out the exclude folders
- if os.path.basename(pwd) in exclude_folders:
- continue
- # list all files and folders
- for item in os.listdir(pwd):
- ab_item = os.path.join(pwd, item)
- if os.path.isdir(ab_item):
- stack.append(ab_item)
- else:
- if ab_item.endswith(include_suffix):
- files.append(ab_item)
-
- return files
-
-
-class DmesgFormatter(logging.Formatter):
- """Custom dmesg logging formatter"""
- def format(self, record):
- timestamp = time.time()
- formatted_time = f"[{timestamp:>10.6f}]"
- log_message = f"{formatted_time} {record.getMessage()}"
- return log_message
-
-
-def config_logging(log_level, log_file="checktransupdate.log"):
- """configure logging based on the log level"""
- # set up the root logger
- logger = logging.getLogger()
- logger.setLevel(log_level)
-
- # Create console handler
- console_handler = logging.StreamHandler()
- console_handler.setLevel(log_level)
-
- # Create file handler
- file_handler = logging.FileHandler(log_file)
- file_handler.setLevel(log_level)
-
- # Create formatter and add it to the handlers
- formatter = DmesgFormatter()
- console_handler.setFormatter(formatter)
- file_handler.setFormatter(formatter)
-
- # Add the handler to the logger
- logger.addHandler(console_handler)
- logger.addHandler(file_handler)
-
-
-def main():
- """Main function of the script"""
- script_path = os.path.dirname(os.path.abspath(__file__))
- linux_path = os.path.join(script_path, "..")
-
- parser = ArgumentParser(description="Check the translation update")
- parser.add_argument(
- "-l",
- "--locale",
- default="zh_CN",
- type=valid_locales,
- help="Locale to check when files are not specified",
- )
-
- parser.add_argument(
- "--print-missing-translations",
- action=BooleanOptionalAction,
- default=True,
- help="Print files that do not have translations",
- )
-
- parser.add_argument(
- '--log',
- default='INFO',
- choices=['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'],
- help='Set the logging level')
-
- parser.add_argument(
- '--logfile',
- default='checktransupdate.log',
- help='Set the logging file (default: checktransupdate.log)')
-
- parser.add_argument(
- "files", nargs="*", help="Files to check, if not specified, check all files"
- )
- args = parser.parse_args()
-
- # Configure logging based on the --log argument
- log_level = getattr(logging, args.log.upper(), logging.INFO)
- config_logging(log_level)
-
- # Get files related to linux path
- files = args.files
- if len(files) == 0:
- offical_files = list_files_with_excluding_folders(
- os.path.join(linux_path, "Documentation"), ["translations", "output"], "rst"
- )
-
- for file in offical_files:
- # split the path into parts
- path_parts = file.split(os.sep)
- # find the index of the "Documentation" directory
- kindex = path_parts.index("Documentation")
- # insert the translations and locale after the Documentation directory
- new_path_parts = path_parts[:kindex + 1] + ["translations", args.locale] \
- + path_parts[kindex + 1 :]
- # join the path parts back together
- new_file = os.sep.join(new_path_parts)
- if os.path.isfile(new_file):
- files.append(new_file)
- else:
- if args.print_missing_translations:
- logging.info(os.path.relpath(os.path.abspath(file), linux_path))
- logging.info("No translation in the locale of %s\n", args.locale)
-
- files = list(map(lambda x: os.path.relpath(os.path.abspath(x), linux_path), files))
-
- # cd to linux root directory
- os.chdir(linux_path)
-
- for file in files:
- check_per_file(file)
-
-
-if __name__ == "__main__":
- main()
diff --git a/scripts/coccicheck b/scripts/coccicheck
index 0e6bc5a10320..8dd766009de1 100755
--- a/scripts/coccicheck
+++ b/scripts/coccicheck
@@ -138,7 +138,7 @@ run_cmd_parmap() {
if [ $VERBOSE -ne 0 ] ; then
echo "Running ($NPROC in parallel): $@"
fi
- if [ "$DEBUG_FILE" != "/dev/null" -a "$DEBUG_FILE" != "" ]; then
+ if [ "$DEBUG_FILE" != "/dev/null" ]; then
echo $@>>$DEBUG_FILE
$@ 2>>$DEBUG_FILE
else
@@ -259,18 +259,27 @@ coccinelle () {
}
-if [ "$DEBUG_FILE" != "/dev/null" -a "$DEBUG_FILE" != "" ]; then
- if [ -f $DEBUG_FILE ]; then
- echo "Debug file $DEBUG_FILE exists, bailing"
- exit
- fi
-else
- DEBUG_FILE="/dev/null"
+if [ "$DEBUG_FILE" = "" ]; then
+ echo 'You have not explicitly specified the debug file to use.'
+ echo 'Using default "/dev/null" as debug file.'
+ echo 'Debug logs will be printed to stdout.'
+ echo 'You can specify the debug file with "make coccicheck DEBUG_FILE=<debug_file>"'
+ echo ''
+ DEBUG_FILE="/dev/null"
+fi
+
+if [ -f $DEBUG_FILE ]; then
+ echo "Debug file $DEBUG_FILE exists, bailing"
+ exit
fi
if [ "$COCCI" = "" ] ; then
for f in `find $srctree/scripts/coccinelle/ -name '*.cocci' -type f | sort`; do
- coccinelle $f
+ if grep -q "virtual[[:space:]]\+$MODE" "$f"; then
+ coccinelle $f
+ else
+ echo "warning: Skipping $f as it does not match mode '$MODE'"
+ fi
done
else
coccinelle $COCCI
diff --git a/scripts/coccinelle/api/kmalloc_objs.cocci b/scripts/coccinelle/api/kmalloc_objs.cocci
new file mode 100644
index 000000000000..e9a415b7b6f4
--- /dev/null
+++ b/scripts/coccinelle/api/kmalloc_objs.cocci
@@ -0,0 +1,135 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/// Use kmalloc_obj family of macros for allocations
+///
+// Confidence: High
+// Options: --include-headers-for-types --all-includes --include-headers --keep-comments
+
+virtual patch
+
+@initialize:python@
+@@
+import sys
+
+def alloc_array(name):
+ func = "FAILED_RENAME"
+ if name == "kmalloc_array":
+ func = "kmalloc_objs"
+ elif name == "kvmalloc_array":
+ func = "kvmalloc_objs"
+ elif name == "kcalloc":
+ func = "kzalloc_objs"
+ elif name == "kvcalloc":
+ func = "kvzalloc_objs"
+ else:
+ print(f"Unknown transform for {name}", file=sys.stderr)
+ return func
+
+// This excludes anything that is assigning to or from integral types or
+// string literals. Everything else gets the sizeof() extracted for the
+// kmalloc_obj() type/var argument. sizeof(void *) is also excluded because
+// it will need case-by-case double-checking to make sure the right type is
+// being assigned.
+@direct depends on patch && !(file in "tools") && !(file in "samples")@
+typedef u8, u16, u32, u64;
+typedef __u8, __u16, __u32, __u64;
+typedef uint8_t, uint16_t, uint32_t, uint64_t;
+typedef uchar, ushort, uint, ulong;
+typedef __le16, __le32, __le64;
+typedef __be16, __be32, __be64;
+typedef wchar_t;
+type INTEGRAL = {u8,__u8,uint8_t,char,unsigned char,uchar,wchar_t,
+ u16,__u16,uint16_t,unsigned short,ushort,
+ u32,__u32,uint32_t,unsigned int,uint,
+ u64,__u64,uint64_t,unsigned long,ulong,
+ __le16,__le32,__le64,__be16,__be32,__be64};
+char [] STRING;
+INTEGRAL *BYTES;
+INTEGRAL **BYTES_PTRS;
+type TYPE;
+expression VAR;
+expression GFP;
+expression COUNT;
+expression FLEX;
+expression E;
+identifier ALLOC =~ "^kv?[mz]alloc$";
+fresh identifier ALLOC_OBJ = ALLOC ## "_obj";
+fresh identifier ALLOC_FLEX = ALLOC ## "_flex";
+identifier ALLOC_ARRAY = {kmalloc_array,kvmalloc_array,kcalloc,kvcalloc};
+fresh identifier ALLOC_OBJS = script:python(ALLOC_ARRAY) { alloc_array(ALLOC_ARRAY) };
+@@
+
+(
+- VAR = ALLOC((sizeof(*VAR)), GFP)
++ VAR = ALLOC_OBJ(*VAR, GFP)
+|
+ ALLOC((\(sizeof(STRING)\|sizeof(INTEGRAL)\|sizeof(INTEGRAL *)\)), GFP)
+|
+ BYTES = ALLOC((sizeof(E)), GFP)
+|
+ BYTES = ALLOC((sizeof(TYPE)), GFP)
+|
+ BYTES_PTRS = ALLOC((sizeof(E)), GFP)
+|
+ BYTES_PTRS = ALLOC((sizeof(TYPE)), GFP)
+|
+ ALLOC((sizeof(void *)), GFP)
+|
+- ALLOC((sizeof(E)), GFP)
++ ALLOC_OBJ(E, GFP)
+|
+- ALLOC((sizeof(TYPE)), GFP)
++ ALLOC_OBJ(TYPE, GFP)
+|
+ ALLOC_ARRAY(COUNT, (\(sizeof(STRING)\|sizeof(INTEGRAL)\|sizeof(INTEGRAL *)\)), GFP)
+|
+ BYTES = ALLOC_ARRAY(COUNT, (sizeof(E)), GFP)
+|
+ BYTES = ALLOC_ARRAY(COUNT, (sizeof(TYPE)), GFP)
+|
+ BYTES_PTRS = ALLOC_ARRAY(COUNT, (sizeof(E)), GFP)
+|
+ BYTES_PTRS = ALLOC_ARRAY(COUNT, (sizeof(TYPE)), GFP)
+|
+ ALLOC_ARRAY((\(sizeof(STRING)\|sizeof(INTEGRAL)\|sizeof(INTEGRAL *)\)), COUNT, GFP)
+|
+ BYTES = ALLOC_ARRAY((sizeof(E)), COUNT, GFP)
+|
+ BYTES = ALLOC_ARRAY((sizeof(TYPE)), COUNT, GFP)
+|
+ BYTES_PTRS = ALLOC_ARRAY((sizeof(E)), COUNT, GFP)
+|
+ BYTES_PTRS = ALLOC_ARRAY((sizeof(TYPE)), COUNT, GFP)
+|
+ ALLOC_ARRAY(COUNT, (sizeof(void *)), GFP)
+|
+ ALLOC_ARRAY((sizeof(void *)), COUNT, GFP)
+|
+- ALLOC_ARRAY(COUNT, (sizeof(E)), GFP)
++ ALLOC_OBJS(E, COUNT, GFP)
+|
+- ALLOC_ARRAY(COUNT, (sizeof(TYPE)), GFP)
++ ALLOC_OBJS(TYPE, COUNT, GFP)
+|
+- ALLOC_ARRAY((sizeof(E)), COUNT, GFP)
++ ALLOC_OBJS(E, COUNT, GFP)
+|
+- ALLOC_ARRAY((sizeof(TYPE)), COUNT, GFP)
++ ALLOC_OBJS(TYPE, COUNT, GFP)
+|
+- ALLOC(struct_size(VAR, FLEX, COUNT), GFP)
++ ALLOC_FLEX(*VAR, FLEX, COUNT, GFP)
+|
+- ALLOC(struct_size_t(TYPE, FLEX, COUNT), GFP)
++ ALLOC_FLEX(TYPE, FLEX, COUNT, GFP)
+)
+
+@drop_gfp_kernel depends on patch && !(file in "tools") && !(file in "samples")@
+identifier ALLOC = {kmalloc_obj,kmalloc_objs,kmalloc_flex,
+ kzalloc_obj,kzalloc_objs,kzalloc_flex,
+ kvmalloc_obj,kvmalloc_objs,kvmalloc_flex,
+ kvzalloc_obj,kvzalloc_objs,kvzalloc_flex};
+@@
+
+ ALLOC(...
+- , GFP_KERNEL
+ )
diff --git a/scripts/coccinelle/api/pm_runtime.cocci b/scripts/coccinelle/api/pm_runtime.cocci
index 2c931e748dda..b720489418fa 100644
--- a/scripts/coccinelle/api/pm_runtime.cocci
+++ b/scripts/coccinelle/api/pm_runtime.cocci
@@ -37,7 +37,6 @@ ret@p = \(pm_runtime_idle\|
pm_runtime_put_sync_autosuspend\|
pm_runtime_set_active\|
pm_schedule_suspend\|
- pm_runtime_barrier\|
pm_generic_runtime_suspend\|
pm_generic_runtime_resume\)(...);
...
@@ -110,5 +109,5 @@ p2 << r.p2;
pm_runtime_api << r.pm_runtime_api;
@@
-msg = "%s returns < 0 as error. Unecessary IS_ERR_VALUE at line %s" % (pm_runtime_api, p2[0].line)
+msg = "%s returns < 0 as error. Unnecessary IS_ERR_VALUE at line %s" % (pm_runtime_api, p2[0].line)
coccilib.report.print_report(p1[0],msg)
diff --git a/scripts/container b/scripts/container
new file mode 100755
index 000000000000..b05333d8530b
--- /dev/null
+++ b/scripts/container
@@ -0,0 +1,199 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: GPL-2.0-only
+# Copyright (C) 2025 Guillaume Tucker
+
+"""Containerized builds"""
+
+import abc
+import argparse
+import logging
+import os
+import pathlib
+import shutil
+import subprocess
+import sys
+import uuid
+
+
+class ContainerRuntime(abc.ABC):
+ """Base class for a container runtime implementation"""
+
+ name = None # Property defined in each implementation class
+
+ def __init__(self, args, logger):
+ self._uid = args.uid or os.getuid()
+ self._gid = args.gid or args.uid or os.getgid()
+ self._env_file = args.env_file
+ self._shell = args.shell
+ self._logger = logger
+
+ @classmethod
+ def is_present(cls):
+ """Determine whether the runtime is present on the system"""
+ return shutil.which(cls.name) is not None
+
+ @abc.abstractmethod
+ def _do_run(self, image, cmd, container_name):
+ """Runtime-specific handler to run a command in a container"""
+
+ @abc.abstractmethod
+ def _do_abort(self, container_name):
+ """Runtime-specific handler to abort a running container"""
+
+ def run(self, image, cmd):
+ """Run a command in a runtime container"""
+ container_name = str(uuid.uuid4())
+ self._logger.debug("container: %s", container_name)
+ try:
+ return self._do_run(image, cmd, container_name)
+ except KeyboardInterrupt:
+ self._logger.error("user aborted")
+ self._do_abort(container_name)
+ return 1
+
+
+class CommonRuntime(ContainerRuntime):
+ """Common logic for Docker and Podman"""
+
+ def _do_run(self, image, cmd, container_name):
+ cmdline = [self.name, 'run']
+ cmdline += self._get_opts(container_name)
+ cmdline.append(image)
+ cmdline += cmd
+ self._logger.debug('command: %s', ' '.join(cmdline))
+ return subprocess.call(cmdline)
+
+ def _get_opts(self, container_name):
+ opts = [
+ '--name', container_name,
+ '--rm',
+ '--volume', f'{pathlib.Path.cwd()}:/src',
+ '--workdir', '/src',
+ ]
+ if self._env_file:
+ opts += ['--env-file', self._env_file]
+ if self._shell:
+ opts += ['--interactive', '--tty']
+ return opts
+
+ def _do_abort(self, container_name):
+ subprocess.call([self.name, 'kill', container_name])
+
+
+class DockerRuntime(CommonRuntime):
+ """Run a command in a Docker container"""
+
+ name = 'docker'
+
+ def _get_opts(self, container_name):
+ return super()._get_opts(container_name) + [
+ '--user', f'{self._uid}:{self._gid}'
+ ]
+
+
+class PodmanRuntime(CommonRuntime):
+ """Run a command in a Podman container"""
+
+ name = 'podman'
+
+ def _get_opts(self, container_name):
+ return super()._get_opts(container_name) + [
+ '--userns', f'keep-id:uid={self._uid},gid={self._gid}',
+ ]
+
+
+class Runtimes:
+ """List of all supported runtimes"""
+
+ runtimes = [PodmanRuntime, DockerRuntime]
+
+ @classmethod
+ def get_names(cls):
+ """Get a list of all the runtime names"""
+ return list(runtime.name for runtime in cls.runtimes)
+
+ @classmethod
+ def get(cls, name):
+ """Get a single runtime class matching the given name"""
+ for runtime in cls.runtimes:
+ if runtime.name == name:
+ if not runtime.is_present():
+ raise ValueError(f"runtime not found: {name}")
+ return runtime
+ raise ValueError(f"unknown runtime: {name}")
+
+ @classmethod
+ def find(cls):
+ """Find the first runtime present on the system"""
+ for runtime in cls.runtimes:
+ if runtime.is_present():
+ return runtime
+ raise ValueError("no runtime found")
+
+
+def _get_logger(verbose):
+ """Set up a logger with the appropriate level"""
+ logger = logging.getLogger('container')
+ handler = logging.StreamHandler()
+ handler.setFormatter(logging.Formatter(
+ fmt='[container {levelname}] {message}', style='{'
+ ))
+ logger.addHandler(handler)
+ logger.setLevel(logging.DEBUG if verbose is True else logging.INFO)
+ return logger
+
+
+def main(args):
+ """Main entry point for the container tool"""
+ logger = _get_logger(args.verbose)
+ try:
+ cls = Runtimes.get(args.runtime) if args.runtime else Runtimes.find()
+ except ValueError as ex:
+ logger.error(ex)
+ return 1
+ logger.debug("runtime: %s", cls.name)
+ logger.debug("image: %s", args.image)
+ return cls(args, logger).run(args.image, args.cmd)
+
+
+if __name__ == '__main__':
+ parser = argparse.ArgumentParser(
+ 'container',
+ description="See the documentation for more details: "
+ "https://docs.kernel.org/dev-tools/container.html"
+ )
+ parser.add_argument(
+ '-e', '--env-file',
+ help="Path to an environment file to load in the container."
+ )
+ parser.add_argument(
+ '-g', '--gid',
+ help="Group ID to use inside the container."
+ )
+ parser.add_argument(
+ '-i', '--image', required=True,
+ help="Container image name."
+ )
+ parser.add_argument(
+ '-r', '--runtime', choices=Runtimes.get_names(),
+ help="Container runtime name. If not specified, the first one found "
+ "on the system will be used i.e. Podman if present, otherwise Docker."
+ )
+ parser.add_argument(
+ '-s', '--shell', action='store_true',
+ help="Run the container in an interactive shell."
+ )
+ parser.add_argument(
+ '-u', '--uid',
+ help="User ID to use inside the container. If the -g option is not "
+ "specified, the user ID will also be set as the group ID."
+ )
+ parser.add_argument(
+ '-v', '--verbose', action='store_true',
+ help="Enable verbose output."
+ )
+ parser.add_argument(
+ 'cmd', nargs='+',
+ help="Command to run in the container"
+ )
+ sys.exit(main(parser.parse_args(sys.argv[1:])))
diff --git a/scripts/context-analysis-suppression.txt b/scripts/context-analysis-suppression.txt
new file mode 100644
index 000000000000..1c51b6153f08
--- /dev/null
+++ b/scripts/context-analysis-suppression.txt
@@ -0,0 +1,34 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# The suppressions file should only match common paths such as header files.
+# For individual subsytems use Makefile directive CONTEXT_ANALYSIS := [yn].
+#
+# The suppressions are ignored when CONFIG_WARN_CONTEXT_ANALYSIS_ALL is
+# selected.
+
+[thread-safety]
+src:*arch/*/include/*
+src:*include/acpi/*
+src:*include/asm-generic/*
+src:*include/linux/*
+src:*include/net/*
+
+# Opt-in headers:
+src:*include/linux/bit_spinlock.h=emit
+src:*include/linux/cleanup.h=emit
+src:*include/linux/kref.h=emit
+src:*include/linux/list*.h=emit
+src:*include/linux/local_lock*.h=emit
+src:*include/linux/lockdep.h=emit
+src:*include/linux/mutex*.h=emit
+src:*include/linux/rcupdate.h=emit
+src:*include/linux/refcount.h=emit
+src:*include/linux/rhashtable.h=emit
+src:*include/linux/rtmutex*.h=emit
+src:*include/linux/rwlock*.h=emit
+src:*include/linux/rwsem.h=emit
+src:*include/linux/sched*=emit
+src:*include/linux/seqlock*.h=emit
+src:*include/linux/spinlock*.h=emit
+src:*include/linux/srcu*.h=emit
+src:*include/linux/ww_mutex.h=emit
diff --git a/scripts/crypto/gen-fips-testvecs.py b/scripts/crypto/gen-fips-testvecs.py
new file mode 100755
index 000000000000..9f18bcb97412
--- /dev/null
+++ b/scripts/crypto/gen-fips-testvecs.py
@@ -0,0 +1,46 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# Script that generates lib/crypto/fips.h
+#
+# Requires that python-cryptography be installed.
+#
+# Copyright 2025 Google LLC
+
+import cryptography.hazmat.primitives.ciphers
+import cryptography.hazmat.primitives.cmac
+import hashlib
+import hmac
+
+fips_test_data = b"fips test data\0\0"
+fips_test_key = b"fips test key\0\0\0"
+
+def print_static_u8_array_definition(name, value):
+ print('')
+ print(f'static const u8 {name}[] __initconst __maybe_unused = {{')
+ for i in range(0, len(value), 8):
+ line = '\t' + ''.join(f'0x{b:02x}, ' for b in value[i:i+8])
+ print(f'{line.rstrip()}')
+ print('};')
+
+print('/* SPDX-License-Identifier: GPL-2.0-or-later */')
+print(f'/* This file was generated by: gen-fips-testvecs.py */')
+print()
+print('#include <linux/fips.h>')
+
+print_static_u8_array_definition("fips_test_data", fips_test_data)
+print_static_u8_array_definition("fips_test_key", fips_test_key)
+
+for alg in 'sha1', 'sha256', 'sha512':
+ ctx = hmac.new(fips_test_key, digestmod=alg)
+ ctx.update(fips_test_data)
+ print_static_u8_array_definition(f'fips_test_hmac_{alg}_value', ctx.digest())
+
+print_static_u8_array_definition(f'fips_test_sha3_256_value',
+ hashlib.sha3_256(fips_test_data).digest())
+
+aes = cryptography.hazmat.primitives.ciphers.algorithms.AES(fips_test_key)
+aes_cmac = cryptography.hazmat.primitives.cmac.CMAC(aes)
+aes_cmac.update(fips_test_data)
+print_static_u8_array_definition('fips_test_aes_cmac_value',
+ aes_cmac.finalize())
diff --git a/scripts/crypto/gen-hash-testvecs.py b/scripts/crypto/gen-hash-testvecs.py
index fc063f2ee95f..f356f87e1c77 100755
--- a/scripts/crypto/gen-hash-testvecs.py
+++ b/scripts/crypto/gen-hash-testvecs.py
@@ -1,10 +1,14 @@
#!/usr/bin/env python3
# SPDX-License-Identifier: GPL-2.0-or-later
#
-# Script that generates test vectors for the given cryptographic hash function.
+# Script that generates test vectors for the given hash function.
+#
+# Requires that python-cryptography be installed.
#
# Copyright 2025 Google LLC
+import cryptography.hazmat.primitives.ciphers
+import cryptography.hazmat.primitives.cmac
import hashlib
import hmac
import sys
@@ -24,6 +28,20 @@ def rand_bytes(length):
out.append((seed >> 16) % 256)
return bytes(out)
+AES_256_KEY_SIZE = 32
+
+# AES-CMAC. Just wraps the implementation from python-cryptography.
+class AesCmac:
+ def __init__(self, key):
+ aes = cryptography.hazmat.primitives.ciphers.algorithms.AES(key)
+ self.cmac = cryptography.hazmat.primitives.cmac.CMAC(aes)
+
+ def update(self, data):
+ self.cmac.update(data)
+
+ def digest(self):
+ return self.cmac.finalize()
+
POLY1305_KEY_SIZE = 32
# A straightforward, unoptimized implementation of Poly1305.
@@ -50,11 +68,93 @@ class Poly1305:
m = (self.h + self.s) % 2**128
return m.to_bytes(16, byteorder='little')
+GHASH_POLY = sum((1 << i) for i in [128, 7, 2, 1, 0])
+GHASH_BLOCK_SIZE = 16
+
+# A straightforward, unoptimized implementation of GHASH.
+class Ghash:
+
+ @staticmethod
+ def reflect_bits_in_bytes(v):
+ res = 0
+ for offs in range(0, 128, 8):
+ for bit in range(8):
+ if (v & (1 << (offs + bit))) != 0:
+ res ^= 1 << (offs + 7 - bit)
+ return res
+
+ @staticmethod
+ def bytes_to_poly(data):
+ return Ghash.reflect_bits_in_bytes(int.from_bytes(data, byteorder='little'))
+
+ @staticmethod
+ def poly_to_bytes(poly):
+ return Ghash.reflect_bits_in_bytes(poly).to_bytes(16, byteorder='little')
+
+ def __init__(self, key):
+ assert len(key) == 16
+ self.h = Ghash.bytes_to_poly(key)
+ self.acc = 0
+
+ # Note: this supports partial blocks only at the end.
+ def update(self, data):
+ for i in range(0, len(data), 16):
+ # acc += block
+ self.acc ^= Ghash.bytes_to_poly(data[i:i+16])
+ # acc = (acc * h) mod GHASH_POLY
+ product = 0
+ for j in range(127, -1, -1):
+ if (self.h & (1 << j)) != 0:
+ product ^= self.acc << j
+ if (product & (1 << (128 + j))) != 0:
+ product ^= GHASH_POLY << j
+ self.acc = product
+ return self
+
+ def digest(self):
+ return Ghash.poly_to_bytes(self.acc)
+
+POLYVAL_POLY = sum((1 << i) for i in [128, 127, 126, 121, 0])
+POLYVAL_BLOCK_SIZE = 16
+
+# A straightforward, unoptimized implementation of POLYVAL.
+# Reference: https://datatracker.ietf.org/doc/html/rfc8452
+class Polyval:
+ def __init__(self, key):
+ assert len(key) == 16
+ self.h = int.from_bytes(key, byteorder='little')
+ self.acc = 0
+
+ # Note: this supports partial blocks only at the end.
+ def update(self, data):
+ for i in range(0, len(data), 16):
+ # acc += block
+ self.acc ^= int.from_bytes(data[i:i+16], byteorder='little')
+ # acc = (acc * h * x^-128) mod POLYVAL_POLY
+ product = 0
+ for j in range(128):
+ if (self.h & (1 << j)) != 0:
+ product ^= self.acc << j
+ if (product & (1 << j)) != 0:
+ product ^= POLYVAL_POLY << j
+ self.acc = product >> 128
+ return self
+
+ def digest(self):
+ return self.acc.to_bytes(16, byteorder='little')
+
def hash_init(alg):
+ # The keyed hash functions are assigned a fixed random key here, to present
+ # them as unkeyed hash functions. This allows all the test cases for
+ # unkeyed hash functions to work on them.
+ if alg == 'aes-cmac':
+ return AesCmac(rand_bytes(AES_256_KEY_SIZE))
+ if alg == 'ghash':
+ return Ghash(rand_bytes(GHASH_BLOCK_SIZE))
if alg == 'poly1305':
- # Use a fixed random key here, to present Poly1305 as an unkeyed hash.
- # This allows all the test cases for unkeyed hashes to work on Poly1305.
return Poly1305(rand_bytes(POLY1305_KEY_SIZE))
+ if alg == 'polyval':
+ return Polyval(rand_bytes(POLYVAL_BLOCK_SIZE))
return hashlib.new(alg)
def hash_update(ctx, data):
@@ -85,9 +185,11 @@ def print_c_struct_u8_array_field(name, value):
print('\t\t},')
def alg_digest_size_const(alg):
- if alg == 'blake2s':
- return 'BLAKE2S_HASH_SIZE'
- return f'{alg.upper()}_DIGEST_SIZE'
+ if alg == 'aes-cmac':
+ return 'AES_BLOCK_SIZE'
+ if alg.startswith('blake2'):
+ return f'{alg.upper()}_HASH_SIZE'
+ return f"{alg.upper().replace('-', '_')}_DIGEST_SIZE"
def gen_unkeyed_testvecs(alg):
print('')
@@ -111,6 +213,18 @@ def gen_unkeyed_testvecs(alg):
f'hash_testvec_consolidated[{alg_digest_size_const(alg)}]',
hash_final(ctx))
+def gen_additional_sha3_testvecs():
+ max_len = 4096
+ in_data = rand_bytes(max_len)
+ for alg in ['shake128', 'shake256']:
+ ctx = hashlib.new('sha3-256')
+ for in_len in range(max_len + 1):
+ out_len = (in_len * 293) % (max_len + 1)
+ out = hashlib.new(alg, data=in_data[:in_len]).digest(out_len)
+ ctx.update(out)
+ print_static_u8_array_definition(f'{alg}_testvec_consolidated[SHA3_256_DIGEST_SIZE]',
+ ctx.digest())
+
def gen_hmac_testvecs(alg):
ctx = hmac.new(rand_bytes(32), digestmod=alg)
data = rand_bytes(4096)
@@ -124,19 +238,60 @@ def gen_hmac_testvecs(alg):
f'hmac_testvec_consolidated[{alg.upper()}_DIGEST_SIZE]',
ctx.digest())
-BLAKE2S_KEY_SIZE = 32
-BLAKE2S_HASH_SIZE = 32
-
-def gen_additional_blake2s_testvecs():
+def gen_additional_blake2_testvecs(alg):
+ if alg == 'blake2s':
+ (max_key_size, max_hash_size) = (32, 32)
+ elif alg == 'blake2b':
+ (max_key_size, max_hash_size) = (64, 64)
+ else:
+ raise ValueError(f'Unsupported alg: {alg}')
hashes = b''
- for key_len in range(BLAKE2S_KEY_SIZE + 1):
- for out_len in range(1, BLAKE2S_HASH_SIZE + 1):
- h = hashlib.blake2s(digest_size=out_len, key=rand_bytes(key_len))
+ for key_len in range(max_key_size + 1):
+ for out_len in range(1, max_hash_size + 1):
+ h = hashlib.new(alg, digest_size=out_len, key=rand_bytes(key_len))
h.update(rand_bytes(100))
hashes += h.digest()
print_static_u8_array_definition(
- 'blake2s_keyed_testvec_consolidated[BLAKE2S_HASH_SIZE]',
- compute_hash('blake2s', hashes))
+ f'{alg}_keyed_testvec_consolidated[{alg_digest_size_const(alg)}]',
+ compute_hash(alg, hashes))
+
+def nh_extract_int(bytestr, pos, length):
+ assert pos % 8 == 0 and length % 8 == 0
+ return int.from_bytes(bytestr[pos//8 : pos//8 + length//8], byteorder='little')
+
+# The NH "almost-universal hash function" used in Adiantum. This is a
+# straightforward translation of the pseudocode from Section 6.3 of the Adiantum
+# paper (https://eprint.iacr.org/2018/720.pdf), except the outer loop is omitted
+# because we assume len(msg) <= 1024. (The kernel's nh() function is only
+# expected to handle up to 1024 bytes; it's just called repeatedly as needed.)
+def nh(key, msg):
+ (w, s, r, u) = (32, 2, 4, 8192)
+ l = 8 * len(msg)
+ assert l <= u
+ assert l % (2*s*w) == 0
+ h = bytes()
+ for i in range(0, 2*s*w*r, 2*s*w):
+ p = 0
+ for j in range(0, l, 2*s*w):
+ for k in range(0, w*s, w):
+ a0 = nh_extract_int(key, i + j + k, w)
+ a1 = nh_extract_int(key, i + j + k + s*w, w)
+ b0 = nh_extract_int(msg, j + k, w)
+ b1 = nh_extract_int(msg, j + k + s*w, w)
+ p += ((a0 + b0) % 2**w) * ((a1 + b1) % 2**w)
+ h += (p % 2**64).to_bytes(8, byteorder='little')
+ return h
+
+def gen_nh_testvecs():
+ NH_KEY_BYTES = 1072
+ NH_MESSAGE_BYTES = 1024
+ key = rand_bytes(NH_KEY_BYTES)
+ msg = rand_bytes(NH_MESSAGE_BYTES)
+ print_static_u8_array_definition('nh_test_key[NH_KEY_BYTES]', key)
+ print_static_u8_array_definition('nh_test_msg[NH_MESSAGE_BYTES]', msg)
+ for length in [16, 96, 256, 1024]:
+ print_static_u8_array_definition(f'nh_test_val{length}[NH_HASH_BYTES]',
+ nh(key, msg[:length]))
def gen_additional_poly1305_testvecs():
key = b'\xff' * POLY1305_KEY_SIZE
@@ -150,19 +305,60 @@ def gen_additional_poly1305_testvecs():
'poly1305_allones_macofmacs[POLY1305_DIGEST_SIZE]',
Poly1305(key).update(data).digest())
+def gen_additional_ghash_testvecs():
+ key = b'\xff' * GHASH_BLOCK_SIZE
+ hashes = b''
+ for data_len in range(0, 4097, 16):
+ hashes += Ghash(key).update(b'\xff' * data_len).digest()
+ print_static_u8_array_definition(
+ 'ghash_allones_hashofhashes[GHASH_DIGEST_SIZE]',
+ Ghash(key).update(hashes).digest())
+
+def gen_additional_polyval_testvecs():
+ key = b'\xff' * POLYVAL_BLOCK_SIZE
+ hashes = b''
+ for data_len in range(0, 4097, 16):
+ hashes += Polyval(key).update(b'\xff' * data_len).digest()
+ print_static_u8_array_definition(
+ 'polyval_allones_hashofhashes[POLYVAL_DIGEST_SIZE]',
+ Polyval(key).update(hashes).digest())
+
if len(sys.argv) != 2:
sys.stderr.write('Usage: gen-hash-testvecs.py ALGORITHM\n')
- sys.stderr.write('ALGORITHM may be any supported by Python hashlib, or poly1305.\n')
+ sys.stderr.write('ALGORITHM may be any supported by Python hashlib;\n')
+ sys.stderr.write(' or aes-cmac, ghash, nh, poly1305, polyval, or sha3.\n')
sys.stderr.write('Example: gen-hash-testvecs.py sha512\n')
sys.exit(1)
alg = sys.argv[1]
print('/* SPDX-License-Identifier: GPL-2.0-or-later */')
print(f'/* This file was generated by: {sys.argv[0]} {" ".join(sys.argv[1:])} */')
-gen_unkeyed_testvecs(alg)
-if alg == 'blake2s':
- gen_additional_blake2s_testvecs()
+if alg == 'aes-cmac':
+ gen_unkeyed_testvecs(alg)
+elif alg.startswith('blake2'):
+ gen_unkeyed_testvecs(alg)
+ gen_additional_blake2_testvecs(alg)
+elif alg == 'ghash':
+ gen_unkeyed_testvecs(alg)
+ gen_additional_ghash_testvecs()
+elif alg == 'nh':
+ gen_nh_testvecs()
elif alg == 'poly1305':
+ gen_unkeyed_testvecs(alg)
gen_additional_poly1305_testvecs()
+elif alg == 'polyval':
+ gen_unkeyed_testvecs(alg)
+ gen_additional_polyval_testvecs()
+elif alg == 'sha3':
+ print()
+ print('/* SHA3-256 test vectors */')
+ gen_unkeyed_testvecs('sha3-256')
+ print()
+ print('/* SHAKE test vectors */')
+ gen_additional_sha3_testvecs()
+elif alg == 'sm3':
+ gen_unkeyed_testvecs(alg)
+ # Kernel doesn't implement HMAC-SM3 library functions yet.
else:
+ gen_unkeyed_testvecs(alg)
gen_hmac_testvecs(alg)
diff --git a/scripts/decode_stacktrace.sh b/scripts/decode_stacktrace.sh
index c73cb802a0a3..39d60d477bf3 100755
--- a/scripts/decode_stacktrace.sh
+++ b/scripts/decode_stacktrace.sh
@@ -5,9 +5,11 @@
usage() {
echo "Usage:"
- echo " $0 -r <release>"
- echo " $0 [<vmlinux> [<base_path>|auto [<modules_path>]]]"
+ echo " $0 [-R] -r <release>"
+ echo " $0 [-R] [<vmlinux> [<base_path>|auto [<modules_path>]]]"
echo " $0 -h"
+ echo "Options:"
+ echo " -R: decode return address instead of caller address."
}
# Try to find a Rust demangler
@@ -33,11 +35,17 @@ fi
READELF=${UTIL_PREFIX}readelf${UTIL_SUFFIX}
ADDR2LINE=${UTIL_PREFIX}addr2line${UTIL_SUFFIX}
NM=${UTIL_PREFIX}nm${UTIL_SUFFIX}
+decode_retaddr=false
if [[ $1 == "-h" ]] ; then
usage
exit 0
-elif [[ $1 == "-r" ]] ; then
+elif [[ $1 == "-R" ]] ; then
+ decode_retaddr=true
+ shift 1
+fi
+
+if [[ $1 == "-r" ]] ; then
vmlinux=""
basepath="auto"
modpath=""
@@ -176,13 +184,23 @@ parse_symbol() {
# Let's start doing the math to get the exact address into the
# symbol. First, strip out the symbol total length.
local expr=${symbol%/*}
+ # Also parse the offset from symbol.
+ local offset=${expr#*+}
+ offset=$((offset))
# Now, replace the symbol name with the base address we found
# before.
expr=${expr/$name/0x$base_addr}
# Evaluate it to find the actual address
- expr=$((expr))
+ # The stack trace shows the return address, which is the next
+ # instruction after the actual call, so as long as it's in the same
+ # symbol, subtract one from that to point the call instruction.
+ if [[ $decode_retaddr == false && $offset != 0 ]]; then
+ expr=$((expr-1))
+ else
+ expr=$((expr))
+ fi
local address=$(printf "%x\n" "$expr")
# Pass it to addr2line to get filename and line number
@@ -277,12 +295,6 @@ handle_line() {
fi
done
- if [[ ${words[$last]} =~ ^[0-9a-f]+\] ]]; then
- words[$last-1]="${words[$last-1]} ${words[$last]}"
- unset words[$last] spaces[$last]
- last=$(( $last - 1 ))
- fi
-
# Extract info after the symbol if present. E.g.:
# func_name+0x54/0x80 (P)
# ^^^
@@ -295,6 +307,14 @@ handle_line() {
last=$(( $last - 1 ))
fi
+ # Join module name with its build id if present, as these were
+ # split during tokenization (e.g. "[module" and "modbuildid]").
+ if [[ ${words[$last]} =~ ^[0-9a-f]+\] ]]; then
+ words[$last-1]="${words[$last-1]} ${words[$last]}"
+ unset words[$last] spaces[$last]
+ last=$(( $last - 1 ))
+ fi
+
if [[ ${words[$last]} =~ \[([^]]+)\] ]]; then
module=${words[$last]}
# some traces format is "(%pS)", which like "(foo+0x0/0x1 [bar])"
diff --git a/scripts/decodecode b/scripts/decodecode
index 6364218b2178..01d25dc110de 100755
--- a/scripts/decodecode
+++ b/scripts/decodecode
@@ -12,7 +12,6 @@ faultlinenum=1
cleanup() {
rm -f $T $T.s $T.o $T.oo $T.aa $T.dis
- exit 1
}
die() {
@@ -49,7 +48,7 @@ done
if [ -z "$code" ]; then
rm $T
- exit
+ die "Code line not found"
fi
echo $code
diff --git a/scripts/documentation-file-ref-check b/scripts/documentation-file-ref-check
deleted file mode 100755
index 408b1dbe7884..000000000000
--- a/scripts/documentation-file-ref-check
+++ /dev/null
@@ -1,245 +0,0 @@
-#!/usr/bin/env perl
-# SPDX-License-Identifier: GPL-2.0
-#
-# Treewide grep for references to files under Documentation, and report
-# non-existing files in stderr.
-
-use warnings;
-use strict;
-use Getopt::Long qw(:config no_auto_abbrev);
-
-# NOTE: only add things here when the file was gone, but the text wants
-# to mention a past documentation file, for example, to give credits for
-# the original work.
-my %false_positives = (
- "Documentation/scsi/scsi_mid_low_api.rst" => "Documentation/Configure.help",
- "drivers/vhost/vhost.c" => "Documentation/virtual/lguest/lguest.c",
-);
-
-my $scriptname = $0;
-$scriptname =~ s,.*/([^/]+/),$1,;
-
-# Parse arguments
-my $help = 0;
-my $fix = 0;
-my $warn = 0;
-
-if (! -e ".git") {
- printf "Warning: can't check if file exists, as this is not a git tree\n";
- exit 0;
-}
-
-GetOptions(
- 'fix' => \$fix,
- 'warn' => \$warn,
- 'h|help|usage' => \$help,
-);
-
-if ($help != 0) {
- print "$scriptname [--help] [--fix]\n";
- exit -1;
-}
-
-# Step 1: find broken references
-print "Finding broken references. This may take a while... " if ($fix);
-
-my %broken_ref;
-
-my $doc_fix = 0;
-
-open IN, "git grep ':doc:\`' Documentation/|"
- or die "Failed to run git grep";
-while (<IN>) {
- next if (!m,^([^:]+):.*\:doc\:\`([^\`]+)\`,);
- next if (m,sphinx/,);
-
- my $file = $1;
- my $d = $1;
- my $doc_ref = $2;
-
- my $f = $doc_ref;
-
- $d =~ s,(.*/).*,$1,;
- $f =~ s,.*\<([^\>]+)\>,$1,;
-
- if ($f =~ m,^/,) {
- $f = "$f.rst";
- $f =~ s,^/,Documentation/,;
- } else {
- $f = "$d$f.rst";
- }
-
- next if (grep -e, glob("$f"));
-
- if ($fix && !$doc_fix) {
- print STDERR "\nWARNING: Currently, can't fix broken :doc:`` fields\n";
- }
- $doc_fix++;
-
- print STDERR "$file: :doc:`$doc_ref`\n";
-}
-close IN;
-
-open IN, "git grep 'Documentation/'|"
- or die "Failed to run git grep";
-while (<IN>) {
- next if (!m/^([^:]+):(.*)/);
-
- my $f = $1;
- my $ln = $2;
-
- # On linux-next, discard the Next/ directory
- next if ($f =~ m,^Next/,);
-
- # Makefiles and scripts contain nasty expressions to parse docs
- next if ($f =~ m/Makefile/ || $f =~ m/\.(sh|py|pl|~|rej|org|orig)$/);
-
- # It doesn't make sense to parse hidden files
- next if ($f =~ m#/\.#);
-
- # Skip this script
- next if ($f eq $scriptname);
-
- # Ignore the dir where documentation will be built
- next if ($ln =~ m,\b(\S*)Documentation/output,);
-
- if ($ln =~ m,\b(\S*)(Documentation/[A-Za-z0-9\_\.\,\~/\*\[\]\?+-]*)(.*),) {
- my $prefix = $1;
- my $ref = $2;
- my $base = $2;
- my $extra = $3;
-
- # some file references are like:
- # /usr/src/linux/Documentation/DMA-{API,mapping}.txt
- # For now, ignore them
- next if ($extra =~ m/^{/);
-
- # Remove footnotes at the end like:
- # Documentation/devicetree/dt-object-internal.txt[1]
- $ref =~ s/(txt|rst)\[\d+]$/$1/;
-
- # Remove ending ']' without any '['
- $ref =~ s/\].*// if (!($ref =~ m/\[/));
-
- # Remove puntuation marks at the end
- $ref =~ s/[\,\.]+$//;
-
- my $fulref = "$prefix$ref";
-
- $fulref =~ s/^(\<file|ref)://;
- $fulref =~ s/^[\'\`]+//;
- $fulref =~ s,^\$\(.*\)/,,;
- $base =~ s,.*/,,;
-
- # Remove URL false-positives
- next if ($fulref =~ m/^http/);
-
- # Remove sched-pelt false-positive
- next if ($fulref =~ m,^Documentation/scheduler/sched-pelt$,);
-
- # Discard some build examples from Documentation/target/tcm_mod_builder.rst
- next if ($fulref =~ m,mnt/sdb/lio-core-2.6.git/Documentation/target,);
-
- # Check if exists, evaluating wildcards
- next if (grep -e, glob("$ref $fulref"));
-
- # Accept relative Documentation patches for tools/
- if ($f =~ m/tools/) {
- my $path = $f;
- $path =~ s,(.*)/.*,$1,;
- $path =~ s,testing/selftests/bpf,bpf/bpftool,;
- next if (grep -e, glob("$path/$ref $path/../$ref $path/$fulref"));
- }
-
- # Discard known false-positives
- if (defined($false_positives{$f})) {
- next if ($false_positives{$f} eq $fulref);
- }
-
- if ($fix) {
- if (!($ref =~ m/(scripts|Kconfig|Kbuild)/)) {
- $broken_ref{$ref}++;
- }
- } elsif ($warn) {
- print STDERR "Warning: $f references a file that doesn't exist: $fulref\n";
- } else {
- print STDERR "$f: $fulref\n";
- }
- }
-}
-close IN;
-
-exit 0 if (!$fix);
-
-# Step 2: Seek for file name alternatives
-print "Auto-fixing broken references. Please double-check the results\n";
-
-foreach my $ref (keys %broken_ref) {
- my $new =$ref;
-
- my $basedir = ".";
- # On translations, only seek inside the translations directory
- $basedir = $1 if ($ref =~ m,(Documentation/translations/[^/]+),);
-
- # get just the basename
- $new =~ s,.*/,,;
-
- my $f="";
-
- # usual reason for breakage: DT file moved around
- if ($ref =~ /devicetree/) {
- # usual reason for breakage: DT file renamed to .yaml
- if (!$f) {
- my $new_ref = $ref;
- $new_ref =~ s/\.txt$/.yaml/;
- $f=$new_ref if (-f $new_ref);
- }
-
- if (!$f) {
- my $search = $new;
- $search =~ s,^.*/,,;
- $f = qx(find Documentation/devicetree/ -iname "*$search*") if ($search);
- if (!$f) {
- # Manufacturer name may have changed
- $search =~ s/^.*,//;
- $f = qx(find Documentation/devicetree/ -iname "*$search*") if ($search);
- }
- }
- }
-
- # usual reason for breakage: file renamed to .rst
- if (!$f) {
- $new =~ s/\.txt$/.rst/;
- $f=qx(find $basedir -iname $new) if ($new);
- }
-
- # usual reason for breakage: use dash or underline
- if (!$f) {
- $new =~ s/[-_]/[-_]/g;
- $f=qx(find $basedir -iname $new) if ($new);
- }
-
- # Wild guess: seek for the same name on another place
- if (!$f) {
- $f = qx(find $basedir -iname $new) if ($new);
- }
-
- my @find = split /\s+/, $f;
-
- if (!$f) {
- print STDERR "ERROR: Didn't find a replacement for $ref\n";
- } elsif (scalar(@find) > 1) {
- print STDERR "WARNING: Won't auto-replace, as found multiple files close to $ref:\n";
- foreach my $j (@find) {
- $j =~ s,^./,,;
- print STDERR " $j\n";
- }
- } else {
- $f = $find[0];
- $f =~ s,^./,,;
- print "INFO: Replacing $ref to $f\n";
- foreach my $j (qx(git grep -l $ref)) {
- qx(sed "s\@$ref\@$f\@g" -i $j);
- }
- }
-}
diff --git a/scripts/dtc/checks.c b/scripts/dtc/checks.c
index 7e3fed5005b3..946c1429e0f1 100644
--- a/scripts/dtc/checks.c
+++ b/scripts/dtc/checks.c
@@ -324,7 +324,7 @@ ERROR(node_name_chars, check_node_name_chars, NODECHARS);
static void check_node_name_chars_strict(struct check *c, struct dt_info *dti,
struct node *node)
{
- int n = strspn(node->name, c->data);
+ size_t n = strspn(node->name, c->data);
if (n < node->basenamelen)
FAIL(c, dti, node, "Character '%c' not recommended in node name",
@@ -340,6 +340,14 @@ static void check_node_name_format(struct check *c, struct dt_info *dti,
}
ERROR(node_name_format, check_node_name_format, NULL, &node_name_chars);
+static void check_node_name_not_empty(struct check *c, struct dt_info *dti,
+ struct node *node)
+{
+ if (node->basenamelen == 0 && node->parent != NULL)
+ FAIL(c, dti, node, "Empty node name");
+}
+ERROR(node_name_not_empty, check_node_name_not_empty, NULL, &node_name_chars);
+
static void check_node_name_vs_property_name(struct check *c,
struct dt_info *dti,
struct node *node)
@@ -718,11 +726,14 @@ static void check_alias_paths(struct check *c, struct dt_info *dti,
continue;
}
- if (!prop->val.val || !get_node_by_path(dti->dt, prop->val.val)) {
+ /* This check does not work for overlays with external paths */
+ if (!(dti->dtsflags & DTSF_PLUGIN) &&
+ (!prop->val.val || !get_node_by_path(dti->dt, prop->val.val))) {
FAIL_PROP(c, dti, node, prop, "aliases property is not a valid node (%s)",
prop->val.val);
continue;
}
+
if (strspn(prop->name, LOWERCASE DIGITS "-") != strlen(prop->name))
FAIL(c, dti, node, "aliases property name must include only lowercase and '-'");
}
@@ -1894,34 +1905,9 @@ static void check_graph_endpoint(struct check *c, struct dt_info *dti,
}
WARNING(graph_endpoint, check_graph_endpoint, NULL, &graph_nodes);
-static void check_graph_child_address(struct check *c, struct dt_info *dti,
- struct node *node)
-{
- int cnt = 0;
- struct node *child;
-
- if (node->bus != &graph_ports_bus && node->bus != &graph_port_bus)
- return;
-
- for_each_child(node, child) {
- struct property *prop = get_property(child, "reg");
-
- /* No error if we have any non-zero unit address */
- if (prop && propval_cell(prop) != 0 )
- return;
-
- cnt++;
- }
-
- if (cnt == 1 && node->addr_cells != -1)
- FAIL(c, dti, node, "graph node has single child node '%s', #address-cells/#size-cells are not necessary",
- node->children->name);
-}
-WARNING(graph_child_address, check_graph_child_address, NULL, &graph_nodes, &graph_port, &graph_endpoint);
-
static struct check *check_table[] = {
&duplicate_node_names, &duplicate_property_names,
- &node_name_chars, &node_name_format, &property_name_chars,
+ &node_name_chars, &node_name_format, &node_name_not_empty, &property_name_chars,
&name_is_string, &name_properties, &node_name_vs_property_name,
&duplicate_label,
@@ -2005,7 +1991,7 @@ static struct check *check_table[] = {
&alias_paths,
- &graph_nodes, &graph_child_address, &graph_port, &graph_endpoint,
+ &graph_nodes, &graph_port, &graph_endpoint,
&always_fail,
};
diff --git a/scripts/dtc/dt-extract-compatibles b/scripts/dtc/dt-extract-compatibles
index 6570efabaa64..87999d707390 100755
--- a/scripts/dtc/dt-extract-compatibles
+++ b/scripts/dtc/dt-extract-compatibles
@@ -72,6 +72,7 @@ def parse_compatibles(file, compat_ignore_list):
compat_list += parse_of_functions(data, "_is_compatible")
compat_list += parse_of_functions(data, "of_find_compatible_node")
compat_list += parse_of_functions(data, "for_each_compatible_node")
+ compat_list += parse_of_functions(data, "for_each_compatible_node_scoped")
compat_list += parse_of_functions(data, "of_get_compatible_child")
return compat_list
diff --git a/scripts/dtc/dtc-lexer.l b/scripts/dtc/dtc-lexer.l
index 15d585c80798..1b129b118b0f 100644
--- a/scripts/dtc/dtc-lexer.l
+++ b/scripts/dtc/dtc-lexer.l
@@ -39,8 +39,6 @@ extern bool treesource_error;
#define DPRINT(fmt, ...) do { } while (0)
#endif
-static int dts_version = 1;
-
#define BEGIN_DEFAULT() DPRINT("<V1>\n"); \
BEGIN(V1); \
@@ -101,7 +99,6 @@ static void PRINTF(1, 2) lexical_error(const char *fmt, ...);
<*>"/dts-v1/" {
DPRINT("Keyword: /dts-v1/\n");
- dts_version = 1;
BEGIN_DEFAULT();
return DT_V1;
}
diff --git a/scripts/dtc/dtc.c b/scripts/dtc/dtc.c
index b3445b7d6473..6dae60de0ea5 100644
--- a/scripts/dtc/dtc.c
+++ b/scripts/dtc/dtc.c
@@ -338,9 +338,14 @@ int main(int argc, char *argv[])
if (auto_label_aliases)
generate_label_tree(dti, "aliases", false);
+ generate_labels_from_tree(dti, "__symbols__");
+
if (generate_symbols)
generate_label_tree(dti, "__symbols__", true);
+ fixup_phandles(dti, "__fixups__");
+ local_fixup_phandles(dti, "__local_fixups__");
+
if (generate_fixups) {
generate_fixups_tree(dti, "__fixups__");
generate_local_fixups_tree(dti, "__local_fixups__");
diff --git a/scripts/dtc/dtc.h b/scripts/dtc/dtc.h
index 3a220b9afc99..473552ebf017 100644
--- a/scripts/dtc/dtc.h
+++ b/scripts/dtc/dtc.h
@@ -227,7 +227,7 @@ struct node {
struct node *next_sibling;
char *fullpath;
- int basenamelen;
+ size_t basenamelen;
cell_t phandle;
int addr_cells, size_cells;
@@ -339,9 +339,12 @@ struct dt_info *build_dt_info(unsigned int dtsflags,
struct reserve_info *reservelist,
struct node *tree, uint32_t boot_cpuid_phys);
void sort_tree(struct dt_info *dti);
+void generate_labels_from_tree(struct dt_info *dti, const char *name);
void generate_label_tree(struct dt_info *dti, const char *name, bool allocph);
void generate_fixups_tree(struct dt_info *dti, const char *name);
+void fixup_phandles(struct dt_info *dti, const char *name);
void generate_local_fixups_tree(struct dt_info *dti, const char *name);
+void local_fixup_phandles(struct dt_info *dti, const char *name);
/* Checks */
@@ -357,6 +360,9 @@ struct dt_info *dt_from_blob(const char *fname);
/* Tree source */
+void property_add_marker(struct property *prop,
+ enum markertype type, unsigned int offset, char *ref);
+void add_phandle_marker(struct dt_info *dti, struct property *prop, unsigned int offset);
void dt_to_source(FILE *f, struct dt_info *dti);
struct dt_info *dt_from_source(const char *f);
diff --git a/scripts/dtc/flattree.c b/scripts/dtc/flattree.c
index 30e6de2044b2..f3b698c17e89 100644
--- a/scripts/dtc/flattree.c
+++ b/scripts/dtc/flattree.c
@@ -807,6 +807,7 @@ struct dt_info *dt_from_blob(const char *fname)
struct node *tree;
uint32_t val;
int flags = 0;
+ unsigned int dtsflags = DTSF_V1;
f = srcfile_relative_open(fname, NULL);
@@ -919,5 +920,8 @@ struct dt_info *dt_from_blob(const char *fname)
fclose(f);
- return build_dt_info(DTSF_V1, reservelist, tree, boot_cpuid_phys);
+ if (get_subnode(tree, "__fixups__") || get_subnode(tree, "__local_fixups__"))
+ dtsflags |= DTSF_PLUGIN;
+
+ return build_dt_info(dtsflags, reservelist, tree, boot_cpuid_phys);
}
diff --git a/scripts/dtc/libfdt/fdt.c b/scripts/dtc/libfdt/fdt.c
index 95f644c31f94..56d4dcb2dc92 100644
--- a/scripts/dtc/libfdt/fdt.c
+++ b/scripts/dtc/libfdt/fdt.c
@@ -110,6 +110,14 @@ int fdt_check_header(const void *fdt)
|| (fdt_totalsize(fdt) > INT_MAX))
return -FDT_ERR_TRUNCATED;
+ /* memrsv block must be 8 byte aligned */
+ if (fdt_off_mem_rsvmap(fdt) % sizeof(uint64_t))
+ return -FDT_ERR_ALIGNMENT;
+
+ /* Structure block must be 4 byte aligned */
+ if (fdt_off_dt_struct(fdt) % FDT_TAGSIZE)
+ return -FDT_ERR_ALIGNMENT;
+
/* Bounds check memrsv block */
if (!check_off_(hdrsize, fdt_totalsize(fdt),
fdt_off_mem_rsvmap(fdt)))
diff --git a/scripts/dtc/libfdt/fdt_overlay.c b/scripts/dtc/libfdt/fdt_overlay.c
index e6b9eb643958..51a3859620a4 100644
--- a/scripts/dtc/libfdt/fdt_overlay.c
+++ b/scripts/dtc/libfdt/fdt_overlay.c
@@ -407,7 +407,8 @@ static int overlay_fixup_phandle(void *fdt, void *fdto, int symbols_off,
const char *fixup_str = value;
uint32_t path_len, name_len;
uint32_t fixup_len;
- char *sep, *endptr;
+ const char *sep;
+ char *endptr;
int poffset, ret;
fixup_end = memchr(value, '\0', len);
diff --git a/scripts/dtc/libfdt/fdt_ro.c b/scripts/dtc/libfdt/fdt_ro.c
index b78c4e48f1cb..63494fb7ad90 100644
--- a/scripts/dtc/libfdt/fdt_ro.c
+++ b/scripts/dtc/libfdt/fdt_ro.c
@@ -306,8 +306,8 @@ const char *fdt_get_name(const void *fdt, int nodeoffset, int *len)
const char *nameptr;
int err;
- if (((err = fdt_ro_probe_(fdt)) < 0)
- || ((err = fdt_check_node_offset_(fdt, nodeoffset)) < 0))
+ if (!can_assume(VALID_DTB) && (((err = fdt_ro_probe_(fdt)) < 0)
+ || ((err = fdt_check_node_offset_(fdt, nodeoffset)) < 0)))
goto fail;
nameptr = nh->name;
diff --git a/scripts/dtc/libfdt/fdt_rw.c b/scripts/dtc/libfdt/fdt_rw.c
index 7475cafce071..90ea14e944cc 100644
--- a/scripts/dtc/libfdt/fdt_rw.c
+++ b/scripts/dtc/libfdt/fdt_rw.c
@@ -22,6 +22,12 @@ static int fdt_blocks_misordered_(const void *fdt,
(fdt_off_dt_strings(fdt) + fdt_size_dt_strings(fdt)));
}
+static void fdt_downgrade_version(void *fdt)
+{
+ if (!can_assume(LATEST) && fdt_version(fdt) > FDT_LAST_SUPPORTED_VERSION)
+ fdt_set_version(fdt, FDT_LAST_SUPPORTED_VERSION);
+}
+
static int fdt_rw_probe_(void *fdt)
{
if (can_assume(VALID_DTB))
@@ -33,9 +39,8 @@ static int fdt_rw_probe_(void *fdt)
if (fdt_blocks_misordered_(fdt, sizeof(struct fdt_reserve_entry),
fdt_size_dt_struct(fdt)))
return -FDT_ERR_BADLAYOUT;
- if (!can_assume(LATEST) && fdt_version(fdt) > 17)
- fdt_set_version(fdt, 17);
+ fdt_downgrade_version(fdt);
return 0;
}
diff --git a/scripts/dtc/libfdt/libfdt.h b/scripts/dtc/libfdt/libfdt.h
index 914bf90785ab..81aaf7cbae13 100644
--- a/scripts/dtc/libfdt/libfdt.h
+++ b/scripts/dtc/libfdt/libfdt.h
@@ -116,6 +116,20 @@ extern "C" {
/* Low-level functions (you probably don't need these) */
/**********************************************************************/
+/**
+ * fdt_offset_ptr - safely get a byte range within the device tree blob
+ * @fdt: Pointer to the device tree blob
+ * @offset: Offset within the blob to the desired byte range
+ * @checklen: Required length of the byte range
+ *
+ * fdt_offset_ptr() returns a pointer to the byte range of length @checklen at
+ * the given @offset within the device tree blob, after verifying that the byte
+ * range fits entirely within the blob and does not overflow.
+ *
+ * returns:
+ * pointer to the byte range, on success
+ * NULL, if the requested range does not fit within the blob
+ */
#ifndef SWIG /* This function is not useful in Python */
const void *fdt_offset_ptr(const void *fdt, int offset, unsigned int checklen);
#endif
@@ -124,6 +138,20 @@ static inline void *fdt_offset_ptr_w(void *fdt, int offset, int checklen)
return (void *)(uintptr_t)fdt_offset_ptr(fdt, offset, checklen);
}
+/**
+ * fdt_next_tag - get next tag in the device tree
+ * @fdt: Pointer to the device tree blob
+ * @offset: Offset within the blob to start searching
+ * @nextoffset: Pointer to variable to store the offset of the next tag
+ *
+ * fdt_next_tag() returns the tag type of the next tag in the device tree
+ * blob starting from the given @offset. If @nextoffset is non-NULL, it will
+ * be set to the offset immediately following the tag.
+ *
+ * returns:
+ * the tag type (FDT_BEGIN_NODE, FDT_END_NODE, FDT_PROP, FDT_NOP, FDT_END),
+ * FDT_END, if offset is out of bounds
+ */
uint32_t fdt_next_tag(const void *fdt, int offset, int *nextoffset);
/*
@@ -334,6 +362,23 @@ int fdt_move(const void *fdt, void *buf, int bufsize);
/* Read-only functions */
/**********************************************************************/
+/**
+ * fdt_check_full - check device tree validity
+ * @fdt: pointer to the device tree blob
+ * @bufsize: size of the buffer containing the device tree
+ *
+ * fdt_check_full() checks that the given buffer contains a valid
+ * flattened device tree and that the tree structure is internally
+ * consistent. This is a more thorough check than fdt_check_header().
+ *
+ * returns:
+ * 0, on success
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_TRUNCATED, standard meanings
+ */
int fdt_check_full(const void *fdt, size_t bufsize);
/**
@@ -1540,10 +1585,90 @@ int fdt_create_with_flags(void *buf, int bufsize, uint32_t flags);
*/
int fdt_create(void *buf, int bufsize);
+/**
+ * fdt_resize - move and resize a device tree in sequential write state
+ * @fdt: Pointer to the device tree to resize
+ * @buf: Buffer where resized tree should be placed
+ * @bufsize: Size of the buffer at @buf
+ *
+ * fdt_resize() moves the device tree blob from @fdt to @buf and
+ * resizes it to fit in the new buffer size.
+ *
+ * returns:
+ * 0, on success
+ * -FDT_ERR_NOSPACE, if @bufsize is too small
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE, standard meanings
+ */
int fdt_resize(void *fdt, void *buf, int bufsize);
+
+/**
+ * fdt_add_reservemap_entry - add an entry to the memory reserve map
+ * @fdt: Pointer to the device tree blob
+ * @addr: Start address of the reserve map entry
+ * @size: Size of the reserved region
+ *
+ * fdt_add_reservemap_entry() adds a memory reserve map entry to the
+ * device tree blob during the sequential write process. This function
+ * can only be called after fdt_create() and before fdt_finish_reservemap().
+ *
+ * returns:
+ * 0, on success
+ * -FDT_ERR_NOSPACE, if there is insufficient space in the blob
+ * -FDT_ERR_BADSTATE, if not in the correct sequential write state
+ */
int fdt_add_reservemap_entry(void *fdt, uint64_t addr, uint64_t size);
+
+/**
+ * fdt_finish_reservemap - complete the memory reserve map
+ * @fdt: Pointer to the device tree blob
+ *
+ * fdt_finish_reservemap() completes the memory reserve map section
+ * of the device tree blob during sequential write. After calling this
+ * function, no more reserve map entries can be added and the blob
+ * moves to the structure creation phase.
+ *
+ * returns:
+ * 0, on success
+ * -FDT_ERR_BADSTATE, if not in the correct sequential write state
+ */
int fdt_finish_reservemap(void *fdt);
+
+/**
+ * fdt_begin_node - start creation of a new node
+ * @fdt: Pointer to the device tree blob
+ * @name: Name of the node to create
+ *
+ * fdt_begin_node() starts the creation of a new node with the given
+ * @name during sequential write. After calling this function, properties
+ * can be added with fdt_property() and subnodes can be created with
+ * additional fdt_begin_node() calls. The node must be completed with
+ * fdt_end_node().
+ *
+ * returns:
+ * 0, on success
+ * -FDT_ERR_NOSPACE, if there is insufficient space in the blob
+ * -FDT_ERR_BADSTATE, if not in the correct sequential write state
+ */
int fdt_begin_node(void *fdt, const char *name);
+
+/**
+ * fdt_property - add a property to the current node
+ * @fdt: Pointer to the device tree blob
+ * @name: Name of the property to add
+ * @val: Pointer to the property value
+ * @len: Length of the property value in bytes
+ *
+ * fdt_property() adds a property with the given @name and value to
+ * the current node during sequential write. This function can only
+ * be called between fdt_begin_node() and fdt_end_node().
+ *
+ * returns:
+ * 0, on success
+ * -FDT_ERR_NOSPACE, if there is insufficient space in the blob
+ * -FDT_ERR_BADSTATE, if not currently within a node
+ */
int fdt_property(void *fdt, const char *name, const void *val, int len);
static inline int fdt_property_u32(void *fdt, const char *name, uint32_t val)
{
@@ -1580,15 +1705,94 @@ int fdt_property_placeholder(void *fdt, const char *name, int len, void **valp);
#define fdt_property_string(fdt, name, str) \
fdt_property(fdt, name, str, strlen(str)+1)
+
+/**
+ * fdt_end_node - complete the current node
+ * @fdt: Pointer to the device tree blob
+ *
+ * fdt_end_node() completes the current node during sequential write. This
+ * function must be called to close each node started with
+ * fdt_begin_node(). After calling this function, no more properties or subnodes
+ * can be added to the node.
+ *
+ * returns:
+ * 0, on success
+ * -FDT_ERR_BADSTATE, if not currently within a node
+ */
int fdt_end_node(void *fdt);
+
+/**
+ * fdt_finish - complete device tree creation
+ * @fdt: Pointer to the device tree blob
+ *
+ * fdt_finish() completes the device tree creation process started with
+ * fdt_create(). This function finalizes the device tree blob and makes it ready
+ * for use. After calling this function, the blob is complete and can be used
+ * with libfdt read-only and read-write functions, but not with sequential write
+ * functions.
+ *
+ * returns:
+ * 0, on success
+ * -FDT_ERR_BADSTATE, if the sequential write process is incomplete
+ */
int fdt_finish(void *fdt);
/**********************************************************************/
/* Read-write functions */
/**********************************************************************/
+/**
+ * fdt_create_empty_tree - create an empty device tree
+ * @buf: Buffer where the empty tree should be created
+ * @bufsize: Size of the buffer at @buf
+ *
+ * fdt_create_empty_tree() creates a minimal empty device tree blob
+ * in the given buffer. The tree contains only a root node with no
+ * properties or subnodes.
+ *
+ * returns:
+ * 0, on success
+ * -FDT_ERR_NOSPACE, if @bufsize is too small for even an empty tree
+ */
int fdt_create_empty_tree(void *buf, int bufsize);
+
+/**
+ * fdt_open_into - move a device tree into a new buffer and make editable
+ * @fdt: Pointer to the device tree to move
+ * @buf: Buffer where the editable tree should be placed
+ * @bufsize: Size of the buffer at @buf
+ *
+ * fdt_open_into() moves and reorganizes the device tree blob from @fdt
+ * into @buf, converting it to a format suitable for read-write operations.
+ * The new buffer should allow space for modifications.
+ *
+ * returns:
+ * 0, on success
+ * -FDT_ERR_NOSPACE, if @bufsize is too small
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_TRUNCATED, standard meanings
+ */
int fdt_open_into(const void *fdt, void *buf, int bufsize);
+
+/**
+ * fdt_pack - pack a device tree blob
+ * @fdt: Pointer to the device tree blob
+ *
+ * fdt_pack() reorganizes the device tree blob to eliminate any free space
+ * and pack it into the minimum possible size. This is useful after making
+ * modifications that might have left gaps in the blob.
+ *
+ * returns:
+ * 0, on success
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_BADLAYOUT, standard meanings
+ */
int fdt_pack(void *fdt);
/**
@@ -2317,6 +2521,16 @@ int fdt_overlay_target_offset(const void *fdt, const void *fdto,
/* Debugging / informational functions */
/**********************************************************************/
+/**
+ * fdt_strerror - return string description of error code
+ * @errval: Error code returned by a libfdt function
+ *
+ * fdt_strerror() returns a string description of the error code passed
+ * in @errval.
+ *
+ * returns:
+ * pointer to a string describing the error code
+ */
const char *fdt_strerror(int errval);
#ifdef __cplusplus
diff --git a/scripts/dtc/libfdt/libfdt_env.h b/scripts/dtc/libfdt/libfdt_env.h
index 73b6d40450ac..5580b483e6a9 100644
--- a/scripts/dtc/libfdt/libfdt_env.h
+++ b/scripts/dtc/libfdt/libfdt_env.h
@@ -66,31 +66,4 @@ static inline fdt64_t cpu_to_fdt64(uint64_t x)
#undef CPU_TO_FDT16
#undef EXTRACT_BYTE
-#ifdef __APPLE__
-#include <AvailabilityMacros.h>
-
-/* strnlen() is not available on Mac OS < 10.7 */
-# if !defined(MAC_OS_X_VERSION_10_7) || (MAC_OS_X_VERSION_MAX_ALLOWED < \
- MAC_OS_X_VERSION_10_7)
-
-#define strnlen fdt_strnlen
-
-/*
- * fdt_strnlen: returns the length of a string or max_count - which ever is
- * smallest.
- * Input 1 string: the string whose size is to be determined
- * Input 2 max_count: the maximum value returned by this function
- * Output: length of the string or max_count (the smallest of the two)
- */
-static inline size_t fdt_strnlen(const char *string, size_t max_count)
-{
- const char *p = memchr(string, 0, max_count);
- return p ? p - string : max_count;
-}
-
-#endif /* !defined(MAC_OS_X_VERSION_10_7) || (MAC_OS_X_VERSION_MAX_ALLOWED <
- MAC_OS_X_VERSION_10_7) */
-
-#endif /* __APPLE__ */
-
#endif /* LIBFDT_ENV_H */
diff --git a/scripts/dtc/libfdt/libfdt_internal.h b/scripts/dtc/libfdt/libfdt_internal.h
index b60b5456f596..0e103cafa714 100644
--- a/scripts/dtc/libfdt/libfdt_internal.h
+++ b/scripts/dtc/libfdt/libfdt_internal.h
@@ -11,11 +11,13 @@
#define FDT_TAGALIGN(x) (FDT_ALIGN((x), FDT_TAGSIZE))
int32_t fdt_ro_probe_(const void *fdt);
-#define FDT_RO_PROBE(fdt) \
- { \
- int32_t totalsize_; \
- if ((totalsize_ = fdt_ro_probe_(fdt)) < 0) \
- return totalsize_; \
+#define FDT_RO_PROBE(fdt) \
+ { \
+ if (!can_assume(VALID_DTB)) { \
+ int32_t totalsize_; \
+ if ((totalsize_ = fdt_ro_probe_(fdt)) < 0) \
+ return totalsize_; \
+ } \
}
int fdt_check_node_offset_(const void *fdt, int offset);
@@ -92,7 +94,7 @@ static inline uint64_t fdt64_ld_(const fdt64_t *p)
* signature or hash check before using libfdt.
*
* For situations where security is not a concern it may be safe to enable
- * ASSUME_SANE.
+ * ASSUME_PERFECT.
*/
enum {
/*
diff --git a/scripts/dtc/livetree.c b/scripts/dtc/livetree.c
index d51d05830b18..5d72abceb526 100644
--- a/scripts/dtc/livetree.c
+++ b/scripts/dtc/livetree.c
@@ -340,20 +340,73 @@ void append_to_property(struct node *node,
char *name, const void *data, int len,
enum markertype type)
{
- struct data d;
+ struct property *p;
+
+ p = get_property(node, name);
+ if (!p) {
+ p = build_property(name, empty_data, NULL);
+ add_property(node, p);
+ }
+
+ p->val = data_add_marker(p->val, type, name);
+ p->val = data_append_data(p->val, data, len);
+}
+
+static int append_unique_str_to_property(struct node *node,
+ char *name, const char *data, int len)
+{
struct property *p;
p = get_property(node, name);
if (p) {
- d = data_add_marker(p->val, type, name);
- d = data_append_data(d, data, len);
- p->val = d;
+ const char *s;
+
+ if (p->val.len && p->val.val[p->val.len - 1] != '\0')
+ /* The current content doesn't look like a string */
+ return -1;
+
+ for (s = p->val.val; s < p->val.val + p->val.len; s = strchr(s, '\0') + 1) {
+ if (strcmp(data, s) == 0)
+ /* data already contained in node.name */
+ return 0;
+ }
} else {
- d = data_add_marker(empty_data, type, name);
- d = data_append_data(d, data, len);
- p = build_property(name, d, NULL);
+ p = build_property(name, empty_data, NULL);
add_property(node, p);
}
+
+ p->val = data_add_marker(p->val, TYPE_STRING, name);
+ p->val = data_append_data(p->val, data, len);
+
+ return 0;
+}
+
+static int append_unique_u32_to_property(struct node *node, char *name, fdt32_t value)
+{
+ struct property *p;
+
+ p = get_property(node, name);
+ if (p) {
+ const fdt32_t *v, *val_end = (const fdt32_t *)p->val.val + p->val.len / 4;
+
+ if (p->val.len % 4 != 0)
+ /* The current content doesn't look like a u32 array */
+ return -1;
+
+ for (v = (const void *)p->val.val; v < val_end; v++) {
+ if (*v == value)
+ /* value already contained */
+ return 0;
+ }
+ } else {
+ p = build_property(name, empty_data, NULL);
+ add_property(node, p);
+ }
+
+ p->val = data_add_marker(p->val, TYPE_UINT32, name);
+ p->val = data_append_data(p->val, &value, 4);
+
+ return 0;
}
struct reserve_info *build_reserve_entry(uint64_t address, uint64_t size)
@@ -918,11 +971,12 @@ static bool any_fixup_tree(struct dt_info *dti, struct node *node)
return false;
}
-static void add_fixup_entry(struct dt_info *dti, struct node *fn,
- struct node *node, struct property *prop,
- struct marker *m)
+static int add_fixup_entry(struct dt_info *dti, struct node *fn,
+ struct node *node, struct property *prop,
+ struct marker *m)
{
char *entry;
+ int ret;
/* m->ref can only be a REF_PHANDLE, but check anyway */
assert(m->type == REF_PHANDLE);
@@ -939,32 +993,39 @@ static void add_fixup_entry(struct dt_info *dti, struct node *fn,
xasprintf(&entry, "%s:%s:%u",
node->fullpath, prop->name, m->offset);
- append_to_property(fn, m->ref, entry, strlen(entry) + 1, TYPE_STRING);
+ ret = append_unique_str_to_property(fn, m->ref, entry, strlen(entry) + 1);
free(entry);
+
+ return ret;
}
-static void generate_fixups_tree_internal(struct dt_info *dti,
- struct node *fn,
- struct node *node)
+static int generate_fixups_tree_internal(struct dt_info *dti,
+ struct node *fn,
+ struct node *node)
{
struct node *dt = dti->dt;
struct node *c;
struct property *prop;
struct marker *m;
struct node *refnode;
+ int ret = 0;
for_each_property(node, prop) {
m = prop->val.markers;
for_each_marker_of_type(m, REF_PHANDLE) {
refnode = get_node_by_ref(dt, m->ref);
if (!refnode)
- add_fixup_entry(dti, fn, node, prop, m);
+ if (add_fixup_entry(dti, fn, node, prop, m))
+ ret = -1;
}
}
for_each_child(node, c)
- generate_fixups_tree_internal(dti, fn, c);
+ if (generate_fixups_tree_internal(dti, fn, c))
+ ret = -1;
+
+ return ret;
}
static bool any_local_fixup_tree(struct dt_info *dti, struct node *node)
@@ -989,7 +1050,7 @@ static bool any_local_fixup_tree(struct dt_info *dti, struct node *node)
return false;
}
-static void add_local_fixup_entry(struct dt_info *dti,
+static int add_local_fixup_entry(struct dt_info *dti,
struct node *lfn, struct node *node,
struct property *prop, struct marker *m,
struct node *refnode)
@@ -1020,30 +1081,56 @@ static void add_local_fixup_entry(struct dt_info *dti,
free(compp);
value_32 = cpu_to_fdt32(m->offset);
- append_to_property(wn, prop->name, &value_32, sizeof(value_32), TYPE_UINT32);
+ return append_unique_u32_to_property(wn, prop->name, value_32);
}
-static void generate_local_fixups_tree_internal(struct dt_info *dti,
- struct node *lfn,
- struct node *node)
+static int generate_local_fixups_tree_internal(struct dt_info *dti,
+ struct node *lfn,
+ struct node *node)
{
struct node *dt = dti->dt;
struct node *c;
struct property *prop;
struct marker *m;
struct node *refnode;
+ int ret = 0;
for_each_property(node, prop) {
m = prop->val.markers;
for_each_marker_of_type(m, REF_PHANDLE) {
refnode = get_node_by_ref(dt, m->ref);
if (refnode)
- add_local_fixup_entry(dti, lfn, node, prop, m, refnode);
+ if (add_local_fixup_entry(dti, lfn, node, prop, m, refnode))
+ ret = -1;
}
}
for_each_child(node, c)
- generate_local_fixups_tree_internal(dti, lfn, c);
+ if (generate_local_fixups_tree_internal(dti, lfn, c))
+ ret = -1;
+
+ return ret;
+}
+
+void generate_labels_from_tree(struct dt_info *dti, const char *name)
+{
+ struct node *an;
+ struct property *p;
+
+ an = get_subnode(dti->dt, name);
+ if (!an)
+ return;
+
+ for_each_property(an, p) {
+ struct node *labeled_node;
+
+ labeled_node = get_node_by_path(dti->dt, p->val.val);
+ if (labeled_node)
+ add_label(&labeled_node->labels, p->name);
+ else if (quiet < 1)
+ fprintf(stderr, "Warning: Path %s referenced in property %s/%s missing",
+ p->val.val, name, p->name);
+ }
}
void generate_label_tree(struct dt_info *dti, const char *name, bool allocph)
@@ -1056,29 +1143,173 @@ void generate_label_tree(struct dt_info *dti, const char *name, bool allocph)
void generate_fixups_tree(struct dt_info *dti, const char *name)
{
- struct node *n = get_subnode(dti->dt, name);
+ if (!any_fixup_tree(dti, dti->dt))
+ return;
+ if (generate_fixups_tree_internal(dti, build_root_node(dti->dt, name), dti->dt))
+ fprintf(stderr,
+ "Warning: Preexisting data in %s malformed, some content could not be added.\n",
+ name);
+}
- /* Start with an empty __fixups__ node to not get duplicates */
- if (n)
- n->deleted = true;
+void fixup_phandles(struct dt_info *dti, const char *name)
+{
+ struct node *an;
+ struct property *fp;
- if (!any_fixup_tree(dti, dti->dt))
+ an = get_subnode(dti->dt, name);
+ if (!an)
return;
- generate_fixups_tree_internal(dti,
- build_and_name_child_node(dti->dt, name),
- dti->dt);
+
+ for_each_property(an, fp) {
+ char *fnext = fp->val.val;
+ char *fv;
+ unsigned int fl;
+
+ while ((fl = fp->val.len - (fnext - fp->val.val))) {
+ char *propname, *soffset;
+ struct node *n;
+ struct property *p;
+ long offset;
+
+ fv = fnext;
+ fnext = memchr(fv, 0, fl);
+
+ if (!fnext) {
+ if (quiet < 1)
+ fprintf(stderr, "Warning: Malformed fixup entry for label %s\n",
+ fp->name);
+ break;
+ }
+ fnext += 1;
+
+ propname = memchr(fv, ':', fnext - 1 - fv);
+ if (!propname) {
+ if (quiet < 1)
+ fprintf(stderr, "Warning: Malformed fixup entry for label %s\n",
+ fp->name);
+ continue;
+ }
+ propname++;
+
+ soffset = memchr(propname, ':', fnext - 1 - propname);
+ if (!soffset) {
+ if (quiet < 1)
+ fprintf(stderr, "Warning: Malformed fixup entry for label %s\n",
+ fp->name);
+ continue;
+ }
+ soffset++;
+
+ /*
+ * temporarily modify the property to not have to create
+ * a copy for the node path.
+ */
+ *(propname - 1) = '\0';
+
+ n = get_node_by_path(dti->dt, fv);
+ if (!n && quiet < 1)
+ fprintf(stderr, "Warning: Label %s references non-existing node %s\n",
+ fp->name, fv);
+
+ *(propname - 1) = ':';
+
+ if (!n)
+ continue;
+
+ /*
+ * temporarily modify the property to not have to create
+ * a copy for the property name.
+ */
+ *(soffset - 1) = '\0';
+
+ p = get_property(n, propname);
+
+ if (!p && quiet < 1)
+ fprintf(stderr, "Warning: Label %s references non-existing property %s in node %s\n",
+ fp->name, n->fullpath, propname);
+
+ *(soffset - 1) = ':';
+
+ if (!p)
+ continue;
+
+ offset = strtol(soffset, NULL, 0);
+ if (offset < 0 || offset + 4 > p->val.len) {
+ if (quiet < 1)
+ fprintf(stderr,
+ "Warning: Label %s contains invalid offset for property %s in node %s\n",
+ fp->name, p->name, n->fullpath);
+ continue;
+ }
+
+ property_add_marker(p, REF_PHANDLE, offset, fp->name);
+ }
+ }
}
void generate_local_fixups_tree(struct dt_info *dti, const char *name)
{
- struct node *n = get_subnode(dti->dt, name);
-
- /* Start with an empty __local_fixups__ node to not get duplicates */
- if (n)
- n->deleted = true;
if (!any_local_fixup_tree(dti, dti->dt))
return;
- generate_local_fixups_tree_internal(dti,
- build_and_name_child_node(dti->dt, name),
- dti->dt);
+ if (generate_local_fixups_tree_internal(dti, build_root_node(dti->dt, name), dti->dt))
+ fprintf(stderr,
+ "Warning: Preexisting data in %s malformed, some content could not be added.\n",
+ name);
+}
+
+static void local_fixup_phandles_node(struct dt_info *dti, struct node *lf, struct node *n)
+{
+ struct property *lfp;
+ struct node *lfsubnode;
+
+ for_each_property(lf, lfp) {
+ struct property *p = get_property(n, lfp->name);
+ fdt32_t *offsets = (fdt32_t *)lfp->val.val;
+ size_t i;
+
+ if (!p) {
+ if (quiet < 1)
+ fprintf(stderr, "Warning: Property %s in %s referenced in __local_fixups__ missing\n",
+ lfp->name, n->fullpath);
+ continue;
+ }
+
+ /*
+ * Each property in the __local_fixups__ tree is a concatenation
+ * of offsets, so it must be a multiple of sizeof(fdt32_t).
+ */
+ if (lfp->val.len % sizeof(fdt32_t)) {
+ if (quiet < 1)
+ fprintf(stderr, "Warning: property %s in /__local_fixups__%s malformed\n",
+ lfp->name, n->fullpath);
+ continue;
+ }
+
+ for (i = 0; i < lfp->val.len / sizeof(fdt32_t); i++)
+ add_phandle_marker(dti, p, dtb_ld32(offsets + i));
+ }
+
+ for_each_child(lf, lfsubnode) {
+ struct node *subnode = get_subnode(n, lfsubnode->name);
+
+ if (!subnode) {
+ if (quiet < 1)
+ fprintf(stderr, "Warning: node %s/%s referenced in __local_fixups__ missing\n",
+ lfsubnode->name, n->fullpath);
+ continue;
+ }
+
+ local_fixup_phandles_node(dti, lfsubnode, subnode);
+ }
+}
+
+void local_fixup_phandles(struct dt_info *dti, const char *name)
+{
+ struct node *an;
+
+ an = get_subnode(dti->dt, name);
+ if (!an)
+ return;
+
+ local_fixup_phandles_node(dti, an, dti->dt);
}
diff --git a/scripts/dtc/srcpos.c b/scripts/dtc/srcpos.c
index 5bb57bf6856c..fef892fb6fdd 100644
--- a/scripts/dtc/srcpos.c
+++ b/scripts/dtc/srcpos.c
@@ -89,6 +89,26 @@ static char *shorten_to_initial_path(char *fname)
}
/**
+ * Returns true if the given path is an absolute one.
+ *
+ * On Windows, it either needs to begin with a forward slash or with a drive
+ * letter (e.g. "C:").
+ * On all other operating systems, it must begin with a forward slash to be
+ * considered an absolute path.
+ */
+static bool is_absolute_path(const char *path)
+{
+#ifdef WIN32
+ return (
+ path[0] == '/' ||
+ (((path[0] >= 'A' && path[0] <= 'Z') || (path[0] >= 'a' && path[0] <= 'z')) && path[1] == ':')
+ );
+#else
+ return (path[0] == '/');
+#endif
+}
+
+/**
* Try to open a file in a given directory.
*
* If the filename is an absolute path, then dirname is ignored. If it is a
@@ -103,7 +123,7 @@ static char *try_open(const char *dirname, const char *fname, FILE **fp)
{
char *fullname;
- if (!dirname || fname[0] == '/')
+ if (!dirname || is_absolute_path(fname))
fullname = xstrdup(fname);
else
fullname = join_path(dirname, fname);
diff --git a/scripts/dtc/treesource.c b/scripts/dtc/treesource.c
index d25f01fc6937..bf648bf6c21a 100644
--- a/scripts/dtc/treesource.c
+++ b/scripts/dtc/treesource.c
@@ -173,23 +173,59 @@ static struct marker **add_marker(struct marker **mi,
return &nm->next;
}
-static void add_string_markers(struct property *prop)
+void property_add_marker(struct property *prop,
+ enum markertype type, unsigned int offset, char *ref)
{
- int l, len = prop->val.len;
- const char *p = prop->val.val;
+ add_marker(&prop->val.markers, type, offset, ref);
+}
+
+static void add_string_markers(struct property *prop, unsigned int offset, int len)
+{
+ int l;
+ const char *p = prop->val.val + offset;
struct marker **mi = &prop->val.markers;
for (l = strlen(p) + 1; l < len; l += strlen(p + l) + 1)
- mi = add_marker(mi, TYPE_STRING, l, NULL);
+ mi = add_marker(mi, TYPE_STRING, offset + l, NULL);
+}
+
+void add_phandle_marker(struct dt_info *dti, struct property *prop, unsigned int offset)
+{
+ cell_t phandle;
+ struct node *refn;
+ char *ref;
+
+ if (prop->val.len < offset + 4) {
+ if (quiet < 1)
+ fprintf(stderr,
+ "Warning: property %s too short to contain a phandle at offset %u\n",
+ prop->name, offset);
+ return;
+ }
+
+ phandle = dtb_ld32(prop->val.val + offset);
+ refn = get_node_by_phandle(dti->dt, phandle);
+
+ if (!refn) {
+ if (quiet < 1)
+ fprintf(stderr,
+ "Warning: node referenced by phandle 0x%x in property %s not found\n",
+ phandle, prop->name);
+ return;
+ }
+
+ if (refn->labels)
+ ref = refn->labels->label;
+ else
+ ref = refn->fullpath;
+
+ add_marker(&prop->val.markers, REF_PHANDLE, offset, ref);
}
-static enum markertype guess_value_type(struct property *prop)
+static enum markertype guess_value_type(struct property *prop, unsigned int offset, int len)
{
- int len = prop->val.len;
- const char *p = prop->val.val;
- struct marker *m = prop->val.markers;
+ const char *p = prop->val.val + offset;
int nnotstring = 0, nnul = 0;
- int nnotstringlbl = 0, nnotcelllbl = 0;
int i;
for (i = 0; i < len; i++) {
@@ -199,30 +235,49 @@ static enum markertype guess_value_type(struct property *prop)
nnul++;
}
- for_each_marker_of_type(m, LABEL) {
- if ((m->offset > 0) && (prop->val.val[m->offset - 1] != '\0'))
- nnotstringlbl++;
- if ((m->offset % sizeof(cell_t)) != 0)
- nnotcelllbl++;
- }
-
- if ((p[len-1] == '\0') && (nnotstring == 0) && (nnul <= (len-nnul))
- && (nnotstringlbl == 0)) {
+ if ((p[len-1] == '\0') && (nnotstring == 0) && (nnul <= len - nnul)) {
if (nnul > 1)
- add_string_markers(prop);
+ add_string_markers(prop, offset, len);
return TYPE_STRING;
- } else if (((len % sizeof(cell_t)) == 0) && (nnotcelllbl == 0)) {
+ } else if ((len % sizeof(cell_t)) == 0) {
return TYPE_UINT32;
}
return TYPE_UINT8;
}
+static void guess_type_markers(struct property *prop)
+{
+ struct marker **m = &prop->val.markers;
+ unsigned int offset = 0;
+
+ for (m = &prop->val.markers; *m; m = &((*m)->next)) {
+ if (is_type_marker((*m)->type))
+ /* assume the whole property is already marked */
+ return;
+
+ if ((*m)->offset > offset) {
+ m = add_marker(m, guess_value_type(prop, offset, (*m)->offset - offset),
+ offset, NULL);
+
+ offset = (*m)->offset;
+ }
+
+ if ((*m)->type == REF_PHANDLE) {
+ m = add_marker(m, TYPE_UINT32, offset, NULL);
+ offset += 4;
+ }
+ }
+
+ if (offset < prop->val.len)
+ add_marker(m, guess_value_type(prop, offset, prop->val.len - offset),
+ offset, NULL);
+}
+
static void write_propval(FILE *f, struct property *prop)
{
size_t len = prop->val.len;
- struct marker *m = prop->val.markers;
- struct marker dummy_marker;
+ struct marker *m;
enum markertype emit_type = TYPE_NONE;
char *srcstr;
@@ -241,14 +296,8 @@ static void write_propval(FILE *f, struct property *prop)
fprintf(f, " =");
- if (!next_type_marker(m)) {
- /* data type information missing, need to guess */
- dummy_marker.type = guess_value_type(prop);
- dummy_marker.next = prop->val.markers;
- dummy_marker.offset = 0;
- dummy_marker.ref = NULL;
- m = &dummy_marker;
- }
+ guess_type_markers(prop);
+ m = prop->val.markers;
for_each_marker(m) {
size_t chunk_len = (m->next ? m->next->offset : len) - m->offset;
@@ -369,7 +418,10 @@ void dt_to_source(FILE *f, struct dt_info *dti)
{
struct reserve_info *re;
- fprintf(f, "/dts-v1/;\n\n");
+ fprintf(f, "/dts-v1/;\n");
+ if (dti->dtsflags & DTSF_PLUGIN)
+ fprintf(f, "/plugin/;\n");
+ fprintf(f, "\n");
for (re = dti->reservelist; re; re = re->next) {
struct label *l;
diff --git a/scripts/dtc/version_gen.h b/scripts/dtc/version_gen.h
index 226c48bf75dc..122e684e76a1 100644
--- a/scripts/dtc/version_gen.h
+++ b/scripts/dtc/version_gen.h
@@ -1 +1 @@
-#define DTC_VERSION "DTC 1.7.2-g52f07dcc"
+#define DTC_VERSION "DTC 1.7.2-g53373d13"
diff --git a/scripts/dummy-tools/python3 b/scripts/dummy-tools/python3
new file mode 100755
index 000000000000..24c5584861b6
--- /dev/null
+++ b/scripts/dummy-tools/python3
@@ -0,0 +1,4 @@
+#!/bin/sh
+# SPDX-License-Identifier: GPL-2.0-only
+
+true
diff --git a/scripts/elf-parse.c b/scripts/elf-parse.c
new file mode 100644
index 000000000000..99869ff91a8c
--- /dev/null
+++ b/scripts/elf-parse.c
@@ -0,0 +1,198 @@
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include "elf-parse.h"
+
+struct elf_funcs elf_parser;
+
+/*
+ * Get the whole file as a programming convenience in order to avoid
+ * malloc+lseek+read+free of many pieces. If successful, then mmap
+ * avoids copying unused pieces; else just read the whole file.
+ * Open for both read and write.
+ */
+static void *map_file(char const *fname, size_t *size)
+{
+ int fd;
+ struct stat sb;
+ void *addr = NULL;
+
+ fd = open(fname, O_RDWR);
+ if (fd < 0) {
+ perror(fname);
+ return NULL;
+ }
+ if (fstat(fd, &sb) < 0) {
+ perror(fname);
+ goto out;
+ }
+ if (!S_ISREG(sb.st_mode)) {
+ fprintf(stderr, "not a regular file: %s\n", fname);
+ goto out;
+ }
+
+ addr = mmap(0, sb.st_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
+ if (addr == MAP_FAILED) {
+ fprintf(stderr, "Could not mmap file: %s\n", fname);
+ goto out;
+ }
+
+ *size = sb.st_size;
+
+out:
+ close(fd);
+ return addr;
+}
+
+static int elf_parse(const char *fname, void *addr, uint32_t types)
+{
+ Elf_Ehdr *ehdr = addr;
+ uint16_t type;
+
+ switch (ehdr->e32.e_ident[EI_DATA]) {
+ case ELFDATA2LSB:
+ elf_parser.r = rle;
+ elf_parser.r2 = r2le;
+ elf_parser.r8 = r8le;
+ elf_parser.w = wle;
+ elf_parser.w8 = w8le;
+ break;
+ case ELFDATA2MSB:
+ elf_parser.r = rbe;
+ elf_parser.r2 = r2be;
+ elf_parser.r8 = r8be;
+ elf_parser.w = wbe;
+ elf_parser.w8 = w8be;
+ break;
+ default:
+ fprintf(stderr, "unrecognized ELF data encoding %d: %s\n",
+ ehdr->e32.e_ident[EI_DATA], fname);
+ return -1;
+ }
+
+ if (memcmp(ELFMAG, ehdr->e32.e_ident, SELFMAG) != 0 ||
+ ehdr->e32.e_ident[EI_VERSION] != EV_CURRENT) {
+ fprintf(stderr, "unrecognized ELF file %s\n", fname);
+ return -1;
+ }
+
+ type = elf_parser.r2(&ehdr->e32.e_type);
+ if (!((1 << type) & types)) {
+ fprintf(stderr, "Invalid ELF type file %s\n", fname);
+ return -1;
+ }
+
+ switch (ehdr->e32.e_ident[EI_CLASS]) {
+ case ELFCLASS32: {
+ elf_parser.ehdr_shoff = ehdr32_shoff;
+ elf_parser.ehdr_shentsize = ehdr32_shentsize;
+ elf_parser.ehdr_shstrndx = ehdr32_shstrndx;
+ elf_parser.ehdr_shnum = ehdr32_shnum;
+ elf_parser.shdr_addr = shdr32_addr;
+ elf_parser.shdr_offset = shdr32_offset;
+ elf_parser.shdr_link = shdr32_link;
+ elf_parser.shdr_size = shdr32_size;
+ elf_parser.shdr_name = shdr32_name;
+ elf_parser.shdr_type = shdr32_type;
+ elf_parser.shdr_entsize = shdr32_entsize;
+ elf_parser.sym_type = sym32_type;
+ elf_parser.sym_name = sym32_name;
+ elf_parser.sym_value = sym32_value;
+ elf_parser.sym_shndx = sym32_shndx;
+ elf_parser.rela_offset = rela32_offset;
+ elf_parser.rela_info = rela32_info;
+ elf_parser.rela_addend = rela32_addend;
+ elf_parser.rela_write_addend = rela32_write_addend;
+
+ if (elf_parser.r2(&ehdr->e32.e_ehsize) != sizeof(Elf32_Ehdr) ||
+ elf_parser.r2(&ehdr->e32.e_shentsize) != sizeof(Elf32_Shdr)) {
+ fprintf(stderr,
+ "unrecognized ET_EXEC/ET_DYN file: %s\n", fname);
+ return -1;
+ }
+
+ }
+ break;
+ case ELFCLASS64: {
+ elf_parser.ehdr_shoff = ehdr64_shoff;
+ elf_parser.ehdr_shentsize = ehdr64_shentsize;
+ elf_parser.ehdr_shstrndx = ehdr64_shstrndx;
+ elf_parser.ehdr_shnum = ehdr64_shnum;
+ elf_parser.shdr_addr = shdr64_addr;
+ elf_parser.shdr_offset = shdr64_offset;
+ elf_parser.shdr_link = shdr64_link;
+ elf_parser.shdr_size = shdr64_size;
+ elf_parser.shdr_name = shdr64_name;
+ elf_parser.shdr_type = shdr64_type;
+ elf_parser.shdr_entsize = shdr64_entsize;
+ elf_parser.sym_type = sym64_type;
+ elf_parser.sym_name = sym64_name;
+ elf_parser.sym_value = sym64_value;
+ elf_parser.sym_shndx = sym64_shndx;
+ elf_parser.rela_offset = rela64_offset;
+ elf_parser.rela_info = rela64_info;
+ elf_parser.rela_addend = rela64_addend;
+ elf_parser.rela_write_addend = rela64_write_addend;
+
+ if (elf_parser.r2(&ehdr->e64.e_ehsize) != sizeof(Elf64_Ehdr) ||
+ elf_parser.r2(&ehdr->e64.e_shentsize) != sizeof(Elf64_Shdr)) {
+ fprintf(stderr,
+ "unrecognized ET_EXEC/ET_DYN file: %s\n",
+ fname);
+ return -1;
+ }
+
+ }
+ break;
+ default:
+ fprintf(stderr, "unrecognized ELF class %d %s\n",
+ ehdr->e32.e_ident[EI_CLASS], fname);
+ return -1;
+ }
+ return 0;
+}
+
+int elf_map_machine(void *addr)
+{
+ Elf_Ehdr *ehdr = addr;
+
+ return elf_parser.r2(&ehdr->e32.e_machine);
+}
+
+int elf_map_long_size(void *addr)
+{
+ Elf_Ehdr *ehdr = addr;
+
+ return ehdr->e32.e_ident[EI_CLASS] == ELFCLASS32 ? 4 : 8;
+}
+
+void *elf_map(char const *fname, size_t *size, uint32_t types)
+{
+ void *addr;
+ int ret;
+
+ addr = map_file(fname, size);
+ if (!addr)
+ return NULL;
+
+ ret = elf_parse(fname, addr, types);
+ if (ret < 0) {
+ elf_unmap(addr, *size);
+ return NULL;
+ }
+
+ return addr;
+}
+
+void elf_unmap(void *addr, size_t size)
+{
+ munmap(addr, size);
+}
diff --git a/scripts/elf-parse.h b/scripts/elf-parse.h
new file mode 100644
index 000000000000..f4411e03069d
--- /dev/null
+++ b/scripts/elf-parse.h
@@ -0,0 +1,305 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+#ifndef _SCRIPTS_ELF_PARSE_H
+#define _SCRIPTS_ELF_PARSE_H
+
+#include <elf.h>
+
+#include <tools/be_byteshift.h>
+#include <tools/le_byteshift.h>
+
+typedef union {
+ Elf32_Ehdr e32;
+ Elf64_Ehdr e64;
+} Elf_Ehdr;
+
+typedef union {
+ Elf32_Shdr e32;
+ Elf64_Shdr e64;
+} Elf_Shdr;
+
+typedef union {
+ Elf32_Sym e32;
+ Elf64_Sym e64;
+} Elf_Sym;
+
+typedef union {
+ Elf32_Rela e32;
+ Elf64_Rela e64;
+} Elf_Rela;
+
+struct elf_funcs {
+ int (*compare_extable)(const void *a, const void *b);
+ uint64_t (*ehdr_shoff)(Elf_Ehdr *ehdr);
+ uint16_t (*ehdr_shstrndx)(Elf_Ehdr *ehdr);
+ uint16_t (*ehdr_shentsize)(Elf_Ehdr *ehdr);
+ uint16_t (*ehdr_shnum)(Elf_Ehdr *ehdr);
+ uint64_t (*shdr_addr)(Elf_Shdr *shdr);
+ uint64_t (*shdr_offset)(Elf_Shdr *shdr);
+ uint64_t (*shdr_size)(Elf_Shdr *shdr);
+ uint64_t (*shdr_entsize)(Elf_Shdr *shdr);
+ uint32_t (*shdr_link)(Elf_Shdr *shdr);
+ uint32_t (*shdr_name)(Elf_Shdr *shdr);
+ uint32_t (*shdr_type)(Elf_Shdr *shdr);
+ uint8_t (*sym_type)(Elf_Sym *sym);
+ uint32_t (*sym_name)(Elf_Sym *sym);
+ uint64_t (*sym_value)(Elf_Sym *sym);
+ uint16_t (*sym_shndx)(Elf_Sym *sym);
+ uint64_t (*rela_offset)(Elf_Rela *rela);
+ uint64_t (*rela_info)(Elf_Rela *rela);
+ uint64_t (*rela_addend)(Elf_Rela *rela);
+ void (*rela_write_addend)(Elf_Rela *rela, uint64_t val);
+ uint32_t (*r)(const uint32_t *);
+ uint16_t (*r2)(const uint16_t *);
+ uint64_t (*r8)(const uint64_t *);
+ void (*w)(uint32_t, uint32_t *);
+ void (*w8)(uint64_t, uint64_t *);
+};
+
+extern struct elf_funcs elf_parser;
+
+static inline uint64_t ehdr64_shoff(Elf_Ehdr *ehdr)
+{
+ return elf_parser.r8(&ehdr->e64.e_shoff);
+}
+
+static inline uint64_t ehdr32_shoff(Elf_Ehdr *ehdr)
+{
+ return elf_parser.r(&ehdr->e32.e_shoff);
+}
+
+static inline uint64_t ehdr_shoff(Elf_Ehdr *ehdr)
+{
+ return elf_parser.ehdr_shoff(ehdr);
+}
+
+#define EHDR_HALF(fn_name) \
+static inline uint16_t ehdr64_##fn_name(Elf_Ehdr *ehdr) \
+{ \
+ return elf_parser.r2(&ehdr->e64.e_##fn_name); \
+} \
+ \
+static inline uint16_t ehdr32_##fn_name(Elf_Ehdr *ehdr) \
+{ \
+ return elf_parser.r2(&ehdr->e32.e_##fn_name); \
+} \
+ \
+static inline uint16_t ehdr_##fn_name(Elf_Ehdr *ehdr) \
+{ \
+ return elf_parser.ehdr_##fn_name(ehdr); \
+}
+
+EHDR_HALF(shentsize)
+EHDR_HALF(shstrndx)
+EHDR_HALF(shnum)
+
+#define SHDR_WORD(fn_name) \
+static inline uint32_t shdr64_##fn_name(Elf_Shdr *shdr) \
+{ \
+ return elf_parser.r(&shdr->e64.sh_##fn_name); \
+} \
+ \
+static inline uint32_t shdr32_##fn_name(Elf_Shdr *shdr) \
+{ \
+ return elf_parser.r(&shdr->e32.sh_##fn_name); \
+} \
+ \
+static inline uint32_t shdr_##fn_name(Elf_Shdr *shdr) \
+{ \
+ return elf_parser.shdr_##fn_name(shdr); \
+}
+
+#define SHDR_ADDR(fn_name) \
+static inline uint64_t shdr64_##fn_name(Elf_Shdr *shdr) \
+{ \
+ return elf_parser.r8(&shdr->e64.sh_##fn_name); \
+} \
+ \
+static inline uint64_t shdr32_##fn_name(Elf_Shdr *shdr) \
+{ \
+ return elf_parser.r(&shdr->e32.sh_##fn_name); \
+} \
+ \
+static inline uint64_t shdr_##fn_name(Elf_Shdr *shdr) \
+{ \
+ return elf_parser.shdr_##fn_name(shdr); \
+}
+
+#define SHDR_WORD(fn_name) \
+static inline uint32_t shdr64_##fn_name(Elf_Shdr *shdr) \
+{ \
+ return elf_parser.r(&shdr->e64.sh_##fn_name); \
+} \
+ \
+static inline uint32_t shdr32_##fn_name(Elf_Shdr *shdr) \
+{ \
+ return elf_parser.r(&shdr->e32.sh_##fn_name); \
+} \
+static inline uint32_t shdr_##fn_name(Elf_Shdr *shdr) \
+{ \
+ return elf_parser.shdr_##fn_name(shdr); \
+}
+
+SHDR_ADDR(addr)
+SHDR_ADDR(offset)
+SHDR_ADDR(size)
+SHDR_ADDR(entsize)
+
+SHDR_WORD(link)
+SHDR_WORD(name)
+SHDR_WORD(type)
+
+#define SYM_ADDR(fn_name) \
+static inline uint64_t sym64_##fn_name(Elf_Sym *sym) \
+{ \
+ return elf_parser.r8(&sym->e64.st_##fn_name); \
+} \
+ \
+static inline uint64_t sym32_##fn_name(Elf_Sym *sym) \
+{ \
+ return elf_parser.r(&sym->e32.st_##fn_name); \
+} \
+ \
+static inline uint64_t sym_##fn_name(Elf_Sym *sym) \
+{ \
+ return elf_parser.sym_##fn_name(sym); \
+}
+
+#define SYM_WORD(fn_name) \
+static inline uint32_t sym64_##fn_name(Elf_Sym *sym) \
+{ \
+ return elf_parser.r(&sym->e64.st_##fn_name); \
+} \
+ \
+static inline uint32_t sym32_##fn_name(Elf_Sym *sym) \
+{ \
+ return elf_parser.r(&sym->e32.st_##fn_name); \
+} \
+ \
+static inline uint32_t sym_##fn_name(Elf_Sym *sym) \
+{ \
+ return elf_parser.sym_##fn_name(sym); \
+}
+
+#define SYM_HALF(fn_name) \
+static inline uint16_t sym64_##fn_name(Elf_Sym *sym) \
+{ \
+ return elf_parser.r2(&sym->e64.st_##fn_name); \
+} \
+ \
+static inline uint16_t sym32_##fn_name(Elf_Sym *sym) \
+{ \
+ return elf_parser.r2(&sym->e32.st_##fn_name); \
+} \
+ \
+static inline uint16_t sym_##fn_name(Elf_Sym *sym) \
+{ \
+ return elf_parser.sym_##fn_name(sym); \
+}
+
+static inline uint8_t sym64_type(Elf_Sym *sym)
+{
+ return ELF64_ST_TYPE(sym->e64.st_info);
+}
+
+static inline uint8_t sym32_type(Elf_Sym *sym)
+{
+ return ELF32_ST_TYPE(sym->e32.st_info);
+}
+
+static inline uint8_t sym_type(Elf_Sym *sym)
+{
+ return elf_parser.sym_type(sym);
+}
+
+SYM_ADDR(value)
+SYM_WORD(name)
+SYM_HALF(shndx)
+
+#define __maybe_unused __attribute__((__unused__))
+
+#define RELA_ADDR(fn_name) \
+static inline uint64_t rela64_##fn_name(Elf_Rela *rela) \
+{ \
+ return elf_parser.r8((uint64_t *)&rela->e64.r_##fn_name); \
+} \
+ \
+static inline uint64_t rela32_##fn_name(Elf_Rela *rela) \
+{ \
+ return elf_parser.r((uint32_t *)&rela->e32.r_##fn_name); \
+} \
+ \
+static inline uint64_t __maybe_unused rela_##fn_name(Elf_Rela *rela) \
+{ \
+ return elf_parser.rela_##fn_name(rela); \
+}
+
+RELA_ADDR(offset)
+RELA_ADDR(info)
+RELA_ADDR(addend)
+
+static inline void rela64_write_addend(Elf_Rela *rela, uint64_t val)
+{
+ elf_parser.w8(val, (uint64_t *)&rela->e64.r_addend);
+}
+
+static inline void rela32_write_addend(Elf_Rela *rela, uint64_t val)
+{
+ elf_parser.w(val, (uint32_t *)&rela->e32.r_addend);
+}
+
+static inline uint32_t rbe(const uint32_t *x)
+{
+ return get_unaligned_be32(x);
+}
+
+static inline uint16_t r2be(const uint16_t *x)
+{
+ return get_unaligned_be16(x);
+}
+
+static inline uint64_t r8be(const uint64_t *x)
+{
+ return get_unaligned_be64(x);
+}
+
+static inline uint32_t rle(const uint32_t *x)
+{
+ return get_unaligned_le32(x);
+}
+
+static inline uint16_t r2le(const uint16_t *x)
+{
+ return get_unaligned_le16(x);
+}
+
+static inline uint64_t r8le(const uint64_t *x)
+{
+ return get_unaligned_le64(x);
+}
+
+static inline void wbe(uint32_t val, uint32_t *x)
+{
+ put_unaligned_be32(val, x);
+}
+
+static inline void wle(uint32_t val, uint32_t *x)
+{
+ put_unaligned_le32(val, x);
+}
+
+static inline void w8be(uint64_t val, uint64_t *x)
+{
+ put_unaligned_be64(val, x);
+}
+
+static inline void w8le(uint64_t val, uint64_t *x)
+{
+ put_unaligned_le64(val, x);
+}
+
+void *elf_map(char const *fname, size_t *size, uint32_t types);
+void elf_unmap(void *addr, size_t size);
+int elf_map_machine(void *addr);
+int elf_map_long_size(void *addr);
+
+#endif /* _SCRIPTS_ELF_PARSE_H */
diff --git a/scripts/faddr2line b/scripts/faddr2line
index 1fa6beef9f97..622875396bcf 100755
--- a/scripts/faddr2line
+++ b/scripts/faddr2line
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
# SPDX-License-Identifier: GPL-2.0
#
# Translate stack dump function offsets.
@@ -76,6 +76,10 @@ ADDR2LINE="${UTIL_PREFIX}addr2line${UTIL_SUFFIX}"
AWK="awk"
GREP="grep"
+# Enforce ASCII-only output from tools like readelf
+# ensuring sed processes strings correctly.
+export LANG=C
+
command -v ${AWK} >/dev/null 2>&1 || die "${AWK} isn't installed"
command -v ${READELF} >/dev/null 2>&1 || die "${READELF} isn't installed"
command -v ${ADDR2LINE} >/dev/null 2>&1 || die "${ADDR2LINE} isn't installed"
@@ -107,14 +111,19 @@ find_dir_prefix() {
run_readelf() {
local objfile=$1
- local out=$(${READELF} --file-header --section-headers --symbols --wide $objfile)
+ local tmpfile
+ tmpfile=$(mktemp)
+
+ ${READELF} --file-header --section-headers --symbols --wide "$objfile" > "$tmpfile"
# This assumes that readelf first prints the file header, then the section headers, then the symbols.
# Note: It seems that GNU readelf does not prefix section headers with the "There are X section headers"
# line when multiple options are given, so let's also match with the "Section Headers:" line.
- ELF_FILEHEADER=$(echo "${out}" | sed -n '/There are [0-9]* section headers, starting at offset\|Section Headers:/q;p')
- ELF_SECHEADERS=$(echo "${out}" | sed -n '/There are [0-9]* section headers, starting at offset\|Section Headers:/,$p' | sed -n '/Symbol table .* contains [0-9]* entries:/q;p')
- ELF_SYMS=$(echo "${out}" | sed -n '/Symbol table .* contains [0-9]* entries:/,$p')
+ ELF_FILEHEADER=$(sed -n '/There are [0-9]* section headers, starting at offset\|Section Headers:/q;p' "$tmpfile")
+ ELF_SECHEADERS=$(sed -n '/There are [0-9]* section headers, starting at offset\|Section Headers:/,$p' "$tmpfile" | sed -n '/Symbol table .* contains [0-9]* entries:/q;p')
+ ELF_SYMS=$(sed -n '/Symbol table .* contains [0-9]* entries:/,$p' "$tmpfile")
+
+ rm -f -- "$tmpfile"
}
check_vmlinux() {
diff --git a/scripts/find-unused-docs.sh b/scripts/find-unused-docs.sh
deleted file mode 100755
index d6d397fbf917..000000000000
--- a/scripts/find-unused-docs.sh
+++ /dev/null
@@ -1,62 +0,0 @@
-#!/bin/bash
-# (c) 2017, Jonathan Corbet <corbet@lwn.net>
-# sayli karnik <karniksayli1995@gmail.com>
-#
-# This script detects files with kernel-doc comments for exported functions
-# that are not included in documentation.
-#
-# usage: Run 'scripts/find-unused-docs.sh directory' from top level of kernel
-# tree.
-#
-# example: $scripts/find-unused-docs.sh drivers/scsi
-#
-# Licensed under the terms of the GNU GPL License
-
-if ! [ -d "Documentation" ]; then
- echo "Run from top level of kernel tree"
- exit 1
-fi
-
-if [ "$#" -ne 1 ]; then
- echo "Usage: scripts/find-unused-docs.sh directory"
- exit 1
-fi
-
-if ! [ -d "$1" ]; then
- echo "Directory $1 doesn't exist"
- exit 1
-fi
-
-cd "$( dirname "${BASH_SOURCE[0]}" )"
-cd ..
-
-cd Documentation/
-
-echo "The following files contain kerneldoc comments for exported functions \
-that are not used in the formatted documentation"
-
-# FILES INCLUDED
-
-files_included=($(grep -rHR ".. kernel-doc" --include \*.rst | cut -d " " -f 3))
-
-declare -A FILES_INCLUDED
-
-for each in "${files_included[@]}"; do
- FILES_INCLUDED[$each]="$each"
- done
-
-cd ..
-
-# FILES NOT INCLUDED
-
-for file in `find $1 -name '*.c'`; do
-
- if [[ ${FILES_INCLUDED[$file]+_} ]]; then
- continue;
- fi
- str=$(PYTHONDONTWRITEBYTECODE=1 scripts/kernel-doc -export "$file" 2>/dev/null)
- if [[ -n "$str" ]]; then
- echo "$file"
- fi
- done
-
diff --git a/scripts/gcc-plugins/gcc-common.h b/scripts/gcc-plugins/gcc-common.h
index 8f1b3500f8e2..abb1964c44d4 100644
--- a/scripts/gcc-plugins/gcc-common.h
+++ b/scripts/gcc-plugins/gcc-common.h
@@ -309,7 +309,9 @@ typedef const gimple *const_gimple_ptr;
#define gimple gimple_ptr
#define const_gimple const_gimple_ptr
#undef CONST_CAST_GIMPLE
-#define CONST_CAST_GIMPLE(X) CONST_CAST(gimple, (X))
+#define CONST_CAST_GIMPLE(X) const_cast<gimple>((X))
+#undef CONST_CAST_TREE
+#define CONST_CAST_TREE(X) const_cast<tree>((X))
/* gimple related */
static inline gimple gimple_build_assign_with_ops(enum tree_code subcode, tree lhs, tree op1, tree op2 MEM_STAT_DECL)
diff --git a/scripts/gdb/linux/bpf.py b/scripts/gdb/linux/bpf.py
new file mode 100644
index 000000000000..1870534ef6f9
--- /dev/null
+++ b/scripts/gdb/linux/bpf.py
@@ -0,0 +1,253 @@
+# SPDX-License-Identifier: GPL-2.0
+
+import json
+import subprocess
+import tempfile
+
+import gdb
+
+from linux import constants, lists, radixtree, utils
+
+
+if constants.LX_CONFIG_BPF and constants.LX_CONFIG_BPF_JIT:
+ bpf_ksym_type = utils.CachedType("struct bpf_ksym")
+if constants.LX_CONFIG_BPF_SYSCALL:
+ bpf_prog_type = utils.CachedType("struct bpf_prog")
+
+
+def get_ksym_name(ksym):
+ name = ksym["name"].bytes
+ end = name.find(b"\x00")
+ if end != -1:
+ name = name[:end]
+ return name.decode()
+
+
+def list_ksyms():
+ if not (constants.LX_CONFIG_BPF and constants.LX_CONFIG_BPF_JIT):
+ return []
+ bpf_kallsyms = gdb.parse_and_eval("&bpf_kallsyms")
+ bpf_ksym_ptr_type = bpf_ksym_type.get_type().pointer()
+ return list(lists.list_for_each_entry(bpf_kallsyms,
+ bpf_ksym_ptr_type,
+ "lnode"))
+
+
+class KsymAddBreakpoint(gdb.Breakpoint):
+ def __init__(self, monitor):
+ super(KsymAddBreakpoint, self).__init__("bpf_ksym_add", internal=True)
+ self.silent = True
+ self.monitor = monitor
+
+ def stop(self):
+ self.monitor.add(gdb.parse_and_eval("ksym"))
+ return False
+
+
+class KsymRemoveBreakpoint(gdb.Breakpoint):
+ def __init__(self, monitor):
+ super(KsymRemoveBreakpoint, self).__init__("bpf_ksym_del",
+ internal=True)
+ self.silent = True
+ self.monitor = monitor
+
+ def stop(self):
+ self.monitor.remove(gdb.parse_and_eval("ksym"))
+ return False
+
+
+class KsymMonitor:
+ def __init__(self, add, remove):
+ self.add = add
+ self.remove = remove
+
+ self.add_bp = KsymAddBreakpoint(self)
+ self.remove_bp = KsymRemoveBreakpoint(self)
+
+ self.notify_initial()
+
+ def notify_initial(self):
+ for ksym in list_ksyms():
+ self.add(ksym)
+
+ def delete(self):
+ self.add_bp.delete()
+ self.remove_bp.delete()
+
+
+def list_progs():
+ if not constants.LX_CONFIG_BPF_SYSCALL:
+ return []
+ idr_rt = gdb.parse_and_eval("&prog_idr.idr_rt")
+ bpf_prog_ptr_type = bpf_prog_type.get_type().pointer()
+ progs = []
+ for _, slot in radixtree.for_each_slot(idr_rt):
+ prog = slot.dereference().cast(bpf_prog_ptr_type)
+ progs.append(prog)
+ # Subprogs are not registered in prog_idr, fetch them manually.
+ # func[0] is the current prog.
+ aux = prog["aux"]
+ func = aux["func"]
+ real_func_cnt = int(aux["real_func_cnt"])
+ for i in range(1, real_func_cnt):
+ progs.append(func[i])
+ return progs
+
+
+class ProgAddBreakpoint(gdb.Breakpoint):
+ def __init__(self, monitor):
+ super(ProgAddBreakpoint, self).__init__("bpf_prog_kallsyms_add",
+ internal=True)
+ self.silent = True
+ self.monitor = monitor
+
+ def stop(self):
+ self.monitor.add(gdb.parse_and_eval("fp"))
+ return False
+
+
+class ProgRemoveBreakpoint(gdb.Breakpoint):
+ def __init__(self, monitor):
+ super(ProgRemoveBreakpoint, self).__init__("bpf_prog_free_id",
+ internal=True)
+ self.silent = True
+ self.monitor = monitor
+
+ def stop(self):
+ self.monitor.remove(gdb.parse_and_eval("prog"))
+ return False
+
+
+class ProgMonitor:
+ def __init__(self, add, remove):
+ self.add = add
+ self.remove = remove
+
+ self.add_bp = ProgAddBreakpoint(self)
+ self.remove_bp = ProgRemoveBreakpoint(self)
+
+ self.notify_initial()
+
+ def notify_initial(self):
+ for prog in list_progs():
+ self.add(prog)
+
+ def delete(self):
+ self.add_bp.delete()
+ self.remove_bp.delete()
+
+
+def btf_str_by_offset(btf, offset):
+ while offset < btf["start_str_off"]:
+ btf = btf["base_btf"]
+
+ offset -= btf["start_str_off"]
+ if offset < btf["hdr"]["str_len"]:
+ return (btf["strings"] + offset).string()
+
+ return None
+
+
+def bpf_line_info_line_num(line_col):
+ return line_col >> 10
+
+
+def bpf_line_info_line_col(line_col):
+ return line_col & 0x3ff
+
+
+class LInfoIter:
+ def __init__(self, prog):
+ # See bpf_prog_get_file_line() for details.
+ self.pos = 0
+ self.nr_linfo = 0
+
+ if prog is None:
+ return
+
+ self.bpf_func = int(prog["bpf_func"])
+ aux = prog["aux"]
+ self.btf = aux["btf"]
+ linfo_idx = aux["linfo_idx"]
+ self.nr_linfo = int(aux["nr_linfo"]) - linfo_idx
+ if self.nr_linfo == 0:
+ return
+
+ linfo_ptr = aux["linfo"]
+ tpe = linfo_ptr.type.target().array(self.nr_linfo).pointer()
+ self.linfo = (linfo_ptr + linfo_idx).cast(tpe).dereference()
+ jited_linfo_ptr = aux["jited_linfo"]
+ tpe = jited_linfo_ptr.type.target().array(self.nr_linfo).pointer()
+ self.jited_linfo = (jited_linfo_ptr + linfo_idx).cast(tpe).dereference()
+
+ self.filenos = {}
+
+ def get_code_off(self):
+ if self.pos >= self.nr_linfo:
+ return -1
+ return self.jited_linfo[self.pos] - self.bpf_func
+
+ def advance(self):
+ self.pos += 1
+
+ def get_fileno(self):
+ file_name_off = int(self.linfo[self.pos]["file_name_off"])
+ fileno = self.filenos.get(file_name_off)
+ if fileno is not None:
+ return fileno, None
+ file_name = btf_str_by_offset(self.btf, file_name_off)
+ fileno = len(self.filenos) + 1
+ self.filenos[file_name_off] = fileno
+ return fileno, file_name
+
+ def get_line_col(self):
+ line_col = int(self.linfo[self.pos]["line_col"])
+ return bpf_line_info_line_num(line_col), \
+ bpf_line_info_line_col(line_col)
+
+
+def generate_debug_obj(ksym, prog):
+ name = get_ksym_name(ksym)
+ # Avoid read_memory(); it throws bogus gdb.MemoryError in some contexts.
+ start = ksym["start"]
+ code = start.cast(gdb.lookup_type("unsigned char")
+ .array(int(ksym["end"]) - int(start))
+ .pointer()).dereference().bytes
+ linfo_iter = LInfoIter(prog)
+
+ result = tempfile.NamedTemporaryFile(suffix=".o", mode="wb")
+ try:
+ with tempfile.NamedTemporaryFile(suffix=".s", mode="w") as src:
+ # ".loc" does not apply to ".byte"s, only to ".insn"s, but since
+ # this needs to work for all architectures, the latter are not an
+ # option. Ask the assembler to apply ".loc"s to labels as well,
+ # and generate dummy labels after each ".loc".
+ src.write(".loc_mark_labels 1\n")
+
+ src.write(".globl {}\n".format(name))
+ src.write(".type {},@function\n".format(name))
+ src.write("{}:\n".format(name))
+ for code_off, code_byte in enumerate(code):
+ if linfo_iter.get_code_off() == code_off:
+ fileno, file_name = linfo_iter.get_fileno()
+ if file_name is not None:
+ src.write(".file {} {}\n".format(
+ fileno, json.dumps(file_name)))
+ line, col = linfo_iter.get_line_col()
+ src.write(".loc {} {} {}\n".format(fileno, line, col))
+ src.write("0:\n")
+ linfo_iter.advance()
+ src.write(".byte {}\n".format(code_byte))
+ src.write(".size {},{}\n".format(name, len(code)))
+ src.flush()
+
+ try:
+ subprocess.check_call(["as", "-c", src.name, "-o", result.name])
+ except FileNotFoundError:
+ # "as" is not installed.
+ result.close()
+ return None
+ return result
+ except:
+ result.close()
+ raise
diff --git a/scripts/gdb/linux/constants.py.in b/scripts/gdb/linux/constants.py.in
index c3886739a028..dab8b80bed69 100644
--- a/scripts/gdb/linux/constants.py.in
+++ b/scripts/gdb/linux/constants.py.in
@@ -150,8 +150,8 @@ LX_CONFIG(CONFIG_ARM64_64K_PAGES)
if IS_BUILTIN(CONFIG_ARM64):
LX_VALUE(CONFIG_ARM64_PA_BITS)
LX_VALUE(CONFIG_ARM64_VA_BITS)
- LX_VALUE(CONFIG_PAGE_SHIFT)
LX_VALUE(CONFIG_ARCH_FORCE_MAX_ORDER)
+LX_VALUE(CONFIG_PAGE_SHIFT)
LX_CONFIG(CONFIG_SPARSEMEM)
LX_CONFIG(CONFIG_SPARSEMEM_EXTREME)
LX_CONFIG(CONFIG_SPARSEMEM_VMEMMAP)
@@ -170,3 +170,6 @@ LX_CONFIG(CONFIG_PAGE_OWNER)
LX_CONFIG(CONFIG_SLUB_DEBUG)
LX_CONFIG(CONFIG_SLAB_FREELIST_HARDENED)
LX_CONFIG(CONFIG_MMU)
+LX_CONFIG(CONFIG_BPF)
+LX_CONFIG(CONFIG_BPF_JIT)
+LX_CONFIG(CONFIG_BPF_SYSCALL)
diff --git a/scripts/gdb/linux/mm.py b/scripts/gdb/linux/mm.py
index 7571aebbe650..d78908f6664d 100644
--- a/scripts/gdb/linux/mm.py
+++ b/scripts/gdb/linux/mm.py
@@ -26,8 +26,179 @@ class page_ops():
raise gdb.GdbError('Only support CONFIG_SPARSEMEM_VMEMMAP now')
if constants.LX_CONFIG_ARM64 and utils.is_target_arch('aarch64'):
self.ops = aarch64_page_ops()
+ elif utils.is_target_arch('x86_64') or utils.is_target_arch('x86-64'):
+ self.ops = x86_page_ops()
else:
- raise gdb.GdbError('Only support aarch64 now')
+ raise gdb.GdbError('Only support aarch64 and x86_64 now')
+
+class x86_page_ops():
+ def __init__(self):
+ self.struct_page_size = utils.get_page_type().sizeof
+ self.PAGE_SHIFT = constants.LX_CONFIG_PAGE_SHIFT
+ self.PAGE_SIZE = 1 << self.PAGE_SHIFT
+ self.PAGE_MASK = (~(self.PAGE_SIZE - 1)) & ((1 << 64) - 1)
+
+ self.PAGE_OFFSET = int(gdb.parse_and_eval("page_offset_base"))
+ self.VMEMMAP_START = int(gdb.parse_and_eval("vmemmap_base"))
+ self.PHYS_BASE = int(gdb.parse_and_eval("phys_base"))
+ self.START_KERNEL_map = 0xffffffff80000000
+
+ self.KERNEL_START = gdb.parse_and_eval("_text")
+ self.KERNEL_END = gdb.parse_and_eval("_end")
+
+ self.VMALLOC_START = int(gdb.parse_and_eval("vmalloc_base"))
+ if self.VMALLOC_START == 0xffffc90000000000:
+ self.VMALLOC_END = self.VMALLOC_START + (32 * 1024 * 1024 * 1024 * 1024) - 1
+ elif self.VMALLOC_START == 0xffa0000000000000:
+ self.VMALLOC_END = self.VMALLOC_START + (12800 * 1024 * 1024 * 1024 * 1024) - 1
+ else:
+ self.VMALLOC_END = self.VMALLOC_START + (12800 * 1024 * 1024 * 1024 * 1024) - 1
+
+ self.MAX_PHYSMEM_BITS = 46
+ self.SECTION_SIZE_BITS = 27
+ self.MAX_ORDER = 10
+
+ self.SECTIONS_SHIFT = self.MAX_PHYSMEM_BITS - self.SECTION_SIZE_BITS
+ self.NR_MEM_SECTIONS = 1 << self.SECTIONS_SHIFT
+ self.PFN_SECTION_SHIFT = self.SECTION_SIZE_BITS - self.PAGE_SHIFT
+ self.PAGES_PER_SECTION = 1 << self.PFN_SECTION_SHIFT
+ self.PAGE_SECTION_MASK = (~(self.PAGES_PER_SECTION - 1)) & ((1 << 64) - 1)
+
+ if constants.LX_CONFIG_SPARSEMEM_EXTREME:
+ self.SECTIONS_PER_ROOT = self.PAGE_SIZE // gdb.lookup_type("struct mem_section").sizeof
+ else:
+ self.SECTIONS_PER_ROOT = 1
+
+ self.NR_SECTION_ROOTS = DIV_ROUND_UP(self.NR_MEM_SECTIONS, self.SECTIONS_PER_ROOT)
+ self.SECTION_ROOT_MASK = self.SECTIONS_PER_ROOT - 1
+
+ try:
+ self.SECTION_HAS_MEM_MAP = 1 << int(gdb.parse_and_eval('SECTION_HAS_MEM_MAP_BIT'))
+ self.SECTION_IS_EARLY = 1 << int(gdb.parse_and_eval('SECTION_IS_EARLY_BIT'))
+ except:
+ self.SECTION_HAS_MEM_MAP = 1 << 0
+ self.SECTION_IS_EARLY = 1 << 3
+
+ self.SUBSECTION_SHIFT = 21
+ self.PAGES_PER_SUBSECTION = 1 << (self.SUBSECTION_SHIFT - self.PAGE_SHIFT)
+
+ if constants.LX_CONFIG_NUMA and constants.LX_CONFIG_NODES_SHIFT:
+ self.NODE_SHIFT = constants.LX_CONFIG_NODES_SHIFT
+ else:
+ self.NODE_SHIFT = 0
+
+ self.MAX_NUMNODES = 1 << self.NODE_SHIFT
+
+ self.vmemmap = gdb.Value(self.VMEMMAP_START).cast(utils.get_page_type().pointer())
+
+ def kasan_reset_tag(self, addr):
+ return addr
+
+ def SECTION_NR_TO_ROOT(self, sec):
+ return sec // self.SECTIONS_PER_ROOT
+
+ def __nr_to_section(self, nr):
+ root = self.SECTION_NR_TO_ROOT(nr)
+ mem_section = gdb.parse_and_eval("mem_section")
+ return mem_section[root][nr & self.SECTION_ROOT_MASK]
+
+ def pfn_to_section_nr(self, pfn):
+ return pfn >> self.PFN_SECTION_SHIFT
+
+ def section_nr_to_pfn(self, sec):
+ return sec << self.PFN_SECTION_SHIFT
+
+ def __pfn_to_section(self, pfn):
+ return self.__nr_to_section(self.pfn_to_section_nr(pfn))
+
+ def pfn_to_section(self, pfn):
+ return self.__pfn_to_section(pfn)
+
+ def subsection_map_index(self, pfn):
+ return (pfn & ~(self.PAGE_SECTION_MASK)) // self.PAGES_PER_SUBSECTION
+
+ def pfn_section_valid(self, ms, pfn):
+ if constants.LX_CONFIG_SPARSEMEM_VMEMMAP:
+ idx = self.subsection_map_index(pfn)
+ return test_bit(idx, ms['usage']['subsection_map'])
+ else:
+ return True
+
+ def valid_section(self, mem_section):
+ if mem_section != None and (mem_section['section_mem_map'] & self.SECTION_HAS_MEM_MAP):
+ return True
+ return False
+
+ def early_section(self, mem_section):
+ if mem_section != None and (mem_section['section_mem_map'] & self.SECTION_IS_EARLY):
+ return True
+ return False
+
+ def pfn_valid(self, pfn):
+ ms = None
+ if self.PHYS_PFN(self.PFN_PHYS(pfn)) != pfn:
+ return False
+ if self.pfn_to_section_nr(pfn) >= self.NR_MEM_SECTIONS:
+ return False
+ ms = self.__pfn_to_section(pfn)
+
+ if not self.valid_section(ms):
+ return False
+ return self.early_section(ms) or self.pfn_section_valid(ms, pfn)
+
+ def PFN_PHYS(self, pfn):
+ return pfn << self.PAGE_SHIFT
+
+ def PHYS_PFN(self, phys):
+ return phys >> self.PAGE_SHIFT
+
+ def __phys_to_virt(self, pa):
+ return pa + self.PAGE_OFFSET
+
+ def __virt_to_phys(self, va):
+ if va >= self.START_KERNEL_map:
+ return va - self.START_KERNEL_map + self.PHYS_BASE
+ else:
+ return va - self.PAGE_OFFSET
+
+ def virt_to_phys(self, va):
+ return self.__virt_to_phys(va)
+
+ def virt_to_page(self, va):
+ return self.pfn_to_page(self.virt_to_pfn(va))
+
+ def __pa(self, va):
+ return self.__virt_to_phys(va)
+
+ def __va(self, pa):
+ return self.__phys_to_virt(pa)
+
+ def pfn_to_kaddr(self, pfn):
+ return self.__va(pfn << self.PAGE_SHIFT)
+
+ def virt_to_pfn(self, va):
+ return self.PHYS_PFN(self.__virt_to_phys(va))
+
+ def sym_to_pfn(self, x):
+ return self.PHYS_PFN(self.__virt_to_phys(x))
+
+ def page_to_pfn(self, page):
+ return int(page.cast(utils.get_page_type().pointer()) - self.vmemmap)
+
+ def pfn_to_page(self, pfn):
+ return self.vmemmap + pfn
+
+ def page_to_phys(self, page):
+ return self.PFN_PHYS(self.page_to_pfn(page))
+
+ def page_to_virt(self, page):
+ return self.__va(self.page_to_phys(page))
+
+ def page_address(self, page):
+ return self.page_to_virt(page)
+
+ def folio_address(self, folio):
+ return self.page_address(folio['page'].address)
class aarch64_page_ops():
def __init__(self):
diff --git a/scripts/gdb/linux/radixtree.py b/scripts/gdb/linux/radixtree.py
index 074543ac763d..bc2954e45c32 100644
--- a/scripts/gdb/linux/radixtree.py
+++ b/scripts/gdb/linux/radixtree.py
@@ -30,13 +30,16 @@ def entry_to_node(node):
def node_maxindex(node):
return (constants.LX_RADIX_TREE_MAP_SIZE << node['shift']) - 1
-def lookup(root, index):
+def resolve_root(root):
+ if root.type == radix_tree_root_type.get_type():
+ return root
if root.type == radix_tree_root_type.get_type().pointer():
- node = root.dereference()
- elif root.type != radix_tree_root_type.get_type():
- raise gdb.GdbError("must be {} not {}"
- .format(radix_tree_root_type.get_type(), root.type))
+ return root.dereference()
+ raise gdb.GdbError("must be {} not {}"
+ .format(radix_tree_root_type.get_type(), root.type))
+def lookup(root, index):
+ root = resolve_root(root)
node = root['xa_head']
if node == 0:
return None
@@ -71,14 +74,120 @@ def lookup(root, index):
return node
-class LxRadixTree(gdb.Function):
+def descend(parent, index):
+ offset = (index >> int(parent["shift"])) & constants.LX_RADIX_TREE_MAP_MASK
+ return offset, parent["slots"][offset]
+
+def load_root(root):
+ node = root["xa_head"]
+ nodep = node
+
+ if is_internal_node(node):
+ node = entry_to_node(node)
+ maxindex = node_maxindex(node)
+ return int(node["shift"]) + constants.LX_RADIX_TREE_MAP_SHIFT, \
+ nodep, maxindex
+
+ return 0, nodep, 0
+
+class RadixTreeIter:
+ def __init__(self, start):
+ self.index = 0
+ self.next_index = start
+ self.node = None
+
+def xa_mk_internal(v):
+ return (v << 2) | 2
+
+LX_XA_RETRY_ENTRY = xa_mk_internal(256)
+LX_RADIX_TREE_RETRY = LX_XA_RETRY_ENTRY
+
+def next_chunk(root, iter):
+ mask = (1 << (utils.get_ulong_type().sizeof * 8)) - 1
+
+ index = iter.next_index
+ if index == 0 and iter.index != 0:
+ return None
+
+ restart = True
+ while restart:
+ restart = False
+
+ _, child, maxindex = load_root(root)
+ if index > maxindex:
+ return None
+ if not child:
+ return None
+
+ if not is_internal_node(child):
+ iter.index = index
+ iter.next_index = (maxindex + 1) & mask
+ iter.node = None
+ return root["xa_head"].address
+
+ while True:
+ node = entry_to_node(child)
+ offset, child = descend(node, index)
+
+ if not child:
+ while True:
+ offset += 1
+ if offset >= constants.LX_RADIX_TREE_MAP_SIZE:
+ break
+ slot = node["slots"][offset]
+ if slot:
+ break
+ index &= ~node_maxindex(node)
+ index = (index + (offset << int(node["shift"]))) & mask
+ if index == 0:
+ return None
+ if offset == constants.LX_RADIX_TREE_MAP_SIZE:
+ restart = True
+ break
+ child = node["slots"][offset]
+
+ if not child:
+ restart = True
+ break
+ if child == LX_XA_RETRY_ENTRY:
+ break
+ if not node["shift"] or not is_internal_node(child):
+ break
+
+ iter.index = (index & ~node_maxindex(node)) | offset
+ iter.next_index = ((index | node_maxindex(node)) + 1) & mask
+ iter.node = node
+
+ return node["slots"][offset].address
+
+def next_slot(slot, iter):
+ mask = (1 << (utils.get_ulong_type().sizeof * 8)) - 1
+ for _ in range(iter.next_index - iter.index - 1):
+ slot += 1
+ iter.index = (iter.index + 1) & mask
+ if slot.dereference():
+ return slot
+ return None
+
+def for_each_slot(root, start=0):
+ iter = RadixTreeIter(start)
+ slot = None
+ while True:
+ if not slot:
+ slot = next_chunk(root, iter)
+ if not slot:
+ break
+ yield iter.index, slot
+ slot = next_slot(slot, iter)
+
+class LxRadixTreeLookup(gdb.Function):
""" Lookup and return a node from a RadixTree.
$lx_radix_tree_lookup(root_node [, index]): Return the node at the given index.
If index is omitted, the root node is dereference and returned."""
def __init__(self):
- super(LxRadixTree, self).__init__("lx_radix_tree_lookup")
+ super(LxRadixTreeLookup, self).__init__("lx_radix_tree_lookup")
def invoke(self, root, index=0):
result = lookup(root, index)
@@ -87,4 +196,20 @@ If index is omitted, the root node is dereference and returned."""
return result
+class LxRadixTree(gdb.Command):
+ """Show all values stored in a RadixTree."""
+
+ def __init__(self):
+ super(LxRadixTree, self).__init__("lx-radix-tree", gdb.COMMAND_DATA,
+ gdb.COMPLETE_NONE)
+
+ def invoke(self, argument, from_tty):
+ args = gdb.string_to_argv(argument)
+ if len(args) != 1:
+ raise gdb.GdbError("Usage: lx-radix-tree ROOT")
+ root = gdb.parse_and_eval(args[0])
+ for index, slot in for_each_slot(root):
+ gdb.write("[{}] = {}\n".format(index, slot.dereference()))
+
LxRadixTree()
+LxRadixTreeLookup()
diff --git a/scripts/gdb/linux/symbols.py b/scripts/gdb/linux/symbols.py
index 6edb99221675..943ff1228b48 100644
--- a/scripts/gdb/linux/symbols.py
+++ b/scripts/gdb/linux/symbols.py
@@ -11,13 +11,14 @@
# This work is licensed under the terms of the GNU GPL version 2.
#
+import atexit
import gdb
import os
import re
import struct
from itertools import count
-from linux import modules, utils, constants
+from linux import bpf, constants, modules, utils
if hasattr(gdb, 'Breakpoint'):
@@ -114,17 +115,27 @@ class LxSymbols(gdb.Command):
The kernel (vmlinux) is taken from the current working directly. Modules (.ko)
are scanned recursively, starting in the same directory. Optionally, the module
search path can be extended by a space separated list of paths passed to the
-lx-symbols command."""
+lx-symbols command.
+
+When the -bpf flag is specified, symbols from the currently loaded BPF programs
+are loaded as well."""
module_paths = []
module_files = []
module_files_updated = False
loaded_modules = []
breakpoint = None
+ bpf_prog_monitor = None
+ bpf_ksym_monitor = None
+ bpf_progs = {}
+ # The remove-symbol-file command, even when invoked with -a, requires the
+ # respective object file to exist, so keep them around.
+ bpf_debug_objs = {}
def __init__(self):
super(LxSymbols, self).__init__("lx-symbols", gdb.COMMAND_FILES,
gdb.COMPLETE_FILENAME)
+ atexit.register(self.cleanup_bpf)
def _update_module_files(self):
self.module_files = []
@@ -197,6 +208,51 @@ lx-symbols command."""
else:
gdb.write("no module object found for '{0}'\n".format(module_name))
+ def add_bpf_prog(self, prog):
+ if prog["jited"]:
+ self.bpf_progs[int(prog["bpf_func"])] = prog
+
+ def remove_bpf_prog(self, prog):
+ self.bpf_progs.pop(int(prog["bpf_func"]), None)
+
+ def add_bpf_ksym(self, ksym):
+ addr = int(ksym["start"])
+ name = bpf.get_ksym_name(ksym)
+ with utils.pagination_off():
+ gdb.write("loading @{addr}: {name}\n".format(
+ addr=hex(addr), name=name))
+ debug_obj = bpf.generate_debug_obj(ksym, self.bpf_progs.get(addr))
+ if debug_obj is None:
+ return
+ try:
+ cmdline = "add-symbol-file {obj} {addr}".format(
+ obj=debug_obj.name, addr=hex(addr))
+ gdb.execute(cmdline, to_string=True)
+ except:
+ debug_obj.close()
+ raise
+ self.bpf_debug_objs[addr] = debug_obj
+
+ def remove_bpf_ksym(self, ksym):
+ addr = int(ksym["start"])
+ debug_obj = self.bpf_debug_objs.pop(addr, None)
+ if debug_obj is None:
+ return
+ try:
+ name = bpf.get_ksym_name(ksym)
+ gdb.write("unloading @{addr}: {name}\n".format(
+ addr=hex(addr), name=name))
+ cmdline = "remove-symbol-file {path}".format(path=debug_obj.name)
+ gdb.execute(cmdline, to_string=True)
+ finally:
+ debug_obj.close()
+
+ def cleanup_bpf(self):
+ self.bpf_progs = {}
+ while len(self.bpf_debug_objs) > 0:
+ self.bpf_debug_objs.popitem()[1].close()
+
+
def load_all_symbols(self):
gdb.write("loading vmlinux\n")
@@ -224,34 +280,59 @@ lx-symbols command."""
else:
[self.load_module_symbols(module) for module in module_list]
+ self.cleanup_bpf()
+ if self.bpf_prog_monitor is not None:
+ self.bpf_prog_monitor.notify_initial()
+ if self.bpf_ksym_monitor is not None:
+ self.bpf_ksym_monitor.notify_initial()
+
for saved_state in saved_states:
saved_state['breakpoint'].enabled = saved_state['enabled']
def invoke(self, arg, from_tty):
skip_decompressor()
- self.module_paths = [os.path.abspath(os.path.expanduser(p))
- for p in arg.split()]
+ monitor_bpf = False
+ self.module_paths = []
+ for p in arg.split():
+ if p == "-bpf":
+ monitor_bpf = True
+ else:
+ self.module_paths.append(os.path.abspath(os.path.expanduser(p)))
self.module_paths.append(os.getcwd())
+ if self.breakpoint is not None:
+ self.breakpoint.delete()
+ self.breakpoint = None
+ if self.bpf_prog_monitor is not None:
+ self.bpf_prog_monitor.delete()
+ self.bpf_prog_monitor = None
+ if self.bpf_ksym_monitor is not None:
+ self.bpf_ksym_monitor.delete()
+ self.bpf_ksym_monitor = None
+
# enforce update
self.module_files = []
self.module_files_updated = False
self.load_all_symbols()
- if not modules.has_modules():
+ if not hasattr(gdb, 'Breakpoint'):
+ gdb.write("Note: symbol update on module and BPF loading not "
+ "supported with this gdb version\n")
return
- if hasattr(gdb, 'Breakpoint'):
- if self.breakpoint is not None:
- self.breakpoint.delete()
- self.breakpoint = None
+ if modules.has_modules():
self.breakpoint = LoadModuleBreakpoint(
"kernel/module/main.c:do_init_module", self)
- else:
- gdb.write("Note: symbol update on module loading not supported "
- "with this gdb version\n")
+
+ if monitor_bpf:
+ if constants.LX_CONFIG_BPF_SYSCALL:
+ self.bpf_prog_monitor = bpf.ProgMonitor(self.add_bpf_prog,
+ self.remove_bpf_prog)
+ if constants.LX_CONFIG_BPF and constants.LX_CONFIG_BPF_JIT:
+ self.bpf_ksym_monitor = bpf.KsymMonitor(self.add_bpf_ksym,
+ self.remove_bpf_ksym)
LxSymbols()
diff --git a/scripts/gdb/linux/timerlist.py b/scripts/gdb/linux/timerlist.py
index ccc24d30de80..9fb3436a217c 100644
--- a/scripts/gdb/linux/timerlist.py
+++ b/scripts/gdb/linux/timerlist.py
@@ -20,7 +20,7 @@ def ktime_get():
We can't read the hardware timer itself to add any nanoseconds
that need to be added since we last stored the time in the
timekeeper. But this is probably good enough for debug purposes."""
- tk_core = gdb.parse_and_eval("&tk_core")
+ tk_core = gdb.parse_and_eval("&timekeeper_data[TIMEKEEPER_CORE]")
return tk_core['timekeeper']['tkr_mono']['base']
diff --git a/scripts/gen-btf.sh b/scripts/gen-btf.sh
new file mode 100755
index 000000000000..8ca96eb10a69
--- /dev/null
+++ b/scripts/gen-btf.sh
@@ -0,0 +1,147 @@
+#!/bin/sh
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2025 Meta Platforms, Inc. and affiliates.
+#
+# This script generates BTF data for the provided ELF file.
+#
+# Kernel BTF generation involves these conceptual steps:
+# 1. pahole generates BTF from DWARF data
+# 2. resolve_btfids applies kernel-specific btf2btf
+# transformations and computes data for .BTF_ids section
+# 3. the result gets linked/objcopied into the target binary
+#
+# How step (3) should be done differs between vmlinux, and
+# kernel modules, which is the primary reason for the existence
+# of this script.
+#
+# For modules the script expects vmlinux passed in as --btf_base.
+# Generated .BTF, .BTF.base and .BTF_ids sections become embedded
+# into the input ELF file with objcopy.
+#
+# For vmlinux the input file remains unchanged and two files are produced:
+# - ${1}.btf.o ready for linking into vmlinux
+# - ${1}.BTF_ids with .BTF_ids data blob
+# This output is consumed by scripts/link-vmlinux.sh
+
+set -e
+
+usage()
+{
+ echo "Usage: $0 [--btf_base <file>] <target ELF file>"
+ exit 1
+}
+
+BTF_BASE=""
+
+while [ $# -gt 0 ]; do
+ case "$1" in
+ --btf_base)
+ BTF_BASE="$2"
+ shift 2
+ ;;
+ -*)
+ echo "Unknown option: $1" >&2
+ usage
+ ;;
+ *)
+ break
+ ;;
+ esac
+done
+
+if [ $# -ne 1 ]; then
+ usage
+fi
+
+ELF_FILE="$1"
+shift
+
+is_enabled() {
+ grep -q "^$1=y" ${objtree}/include/config/auto.conf
+}
+
+case "${KBUILD_VERBOSE}" in
+*1*)
+ set -x
+ ;;
+esac
+
+gen_btf_data()
+{
+ btf1="${ELF_FILE}.BTF.1"
+ ${PAHOLE} -J ${PAHOLE_FLAGS} \
+ ${BTF_BASE:+--btf_base ${BTF_BASE}} \
+ --btf_encode_detached=${btf1} \
+ "${ELF_FILE}"
+
+ ${RESOLVE_BTFIDS} ${RESOLVE_BTFIDS_FLAGS} \
+ ${BTF_BASE:+--btf_base ${BTF_BASE}} \
+ --btf ${btf1} "${ELF_FILE}"
+}
+
+gen_btf_o()
+{
+ btf_data=${ELF_FILE}.btf.o
+
+ # Create ${btf_data} which contains just .BTF section but no symbols. Add
+ # SHF_ALLOC because .BTF will be part of the vmlinux image. --strip-all
+ # deletes all symbols including __start_BTF and __stop_BTF, which will
+ # be redefined in the linker script.
+ echo "" | ${CC} ${CLANG_FLAGS} ${KBUILD_CPPFLAGS} ${KBUILD_CFLAGS} -fno-lto -c -x c -o ${btf_data} -
+ ${OBJCOPY} --add-section .BTF=${ELF_FILE}.BTF \
+ --set-section-flags .BTF=alloc,readonly ${btf_data}
+ ${OBJCOPY} --only-section=.BTF --strip-all ${btf_data}
+
+ # Change e_type to ET_REL so that it can be used to link final vmlinux.
+ # GNU ld 2.35+ and lld do not allow an ET_EXEC input.
+ if is_enabled CONFIG_CPU_BIG_ENDIAN; then
+ et_rel='\0\1'
+ else
+ et_rel='\1\0'
+ fi
+ printf "${et_rel}" | dd of="${btf_data}" conv=notrunc bs=1 seek=16 status=none
+}
+
+embed_btf_data()
+{
+ ${OBJCOPY} --add-section .BTF=${ELF_FILE}.BTF ${ELF_FILE}
+
+ # a module might not have a .BTF_ids or .BTF.base section
+ btf_base="${ELF_FILE}.BTF.base"
+ if [ -f "${btf_base}" ]; then
+ ${OBJCOPY} --add-section .BTF.base=${btf_base} ${ELF_FILE}
+ fi
+ btf_ids="${ELF_FILE}.BTF_ids"
+ if [ -f "${btf_ids}" ]; then
+ ${RESOLVE_BTFIDS} --patch_btfids ${btf_ids} ${ELF_FILE}
+ fi
+}
+
+cleanup()
+{
+ rm -f "${ELF_FILE}.BTF.1"
+ rm -f "${ELF_FILE}.BTF"
+ if [ "${BTFGEN_MODE}" = "module" ]; then
+ rm -f "${ELF_FILE}.BTF.base"
+ rm -f "${ELF_FILE}.BTF_ids"
+ fi
+}
+trap cleanup EXIT
+
+BTFGEN_MODE="vmlinux"
+if [ -n "${BTF_BASE}" ]; then
+ BTFGEN_MODE="module"
+fi
+
+gen_btf_data
+
+case "${BTFGEN_MODE}" in
+vmlinux)
+ gen_btf_o
+ ;;
+module)
+ embed_btf_data
+ ;;
+esac
+
+exit 0
diff --git a/scripts/gendwarfksyms/dwarf.c b/scripts/gendwarfksyms/dwarf.c
index 3538a7d9cb07..e76d732f5f60 100644
--- a/scripts/gendwarfksyms/dwarf.c
+++ b/scripts/gendwarfksyms/dwarf.c
@@ -750,6 +750,7 @@ static void process_enumerator_type(struct state *state, struct die *cache,
Dwarf_Die *die)
{
bool overridden = false;
+ unsigned long override;
Dwarf_Word value;
if (stable) {
@@ -761,7 +762,8 @@ static void process_enumerator_type(struct state *state, struct die *cache,
return;
overridden = kabi_get_enumerator_value(
- state->expand.current_fqn, cache->fqn, &value);
+ state->expand.current_fqn, cache->fqn, &override);
+ value = override;
}
process_list_comma(state, cache);
diff --git a/scripts/gendwarfksyms/gendwarfksyms.c b/scripts/gendwarfksyms/gendwarfksyms.c
index 08ae61eb327e..f5203d1640ee 100644
--- a/scripts/gendwarfksyms/gendwarfksyms.c
+++ b/scripts/gendwarfksyms/gendwarfksyms.c
@@ -138,7 +138,8 @@ int main(int argc, char **argv)
error("no input files?");
}
- symbol_read_exports(stdin);
+ if (!symbol_read_exports(stdin))
+ return 0;
if (symtypes_file) {
symfile = fopen(symtypes_file, "w");
diff --git a/scripts/gendwarfksyms/gendwarfksyms.h b/scripts/gendwarfksyms/gendwarfksyms.h
index d9c06d2cb1df..32cec8f7695a 100644
--- a/scripts/gendwarfksyms/gendwarfksyms.h
+++ b/scripts/gendwarfksyms/gendwarfksyms.h
@@ -123,7 +123,7 @@ struct symbol {
typedef void (*symbol_callback_t)(struct symbol *, void *arg);
bool is_symbol_ptr(const char *name);
-void symbol_read_exports(FILE *file);
+int symbol_read_exports(FILE *file);
void symbol_read_symtab(int fd);
struct symbol *symbol_get(const char *name);
void symbol_set_ptr(struct symbol *sym, Dwarf_Die *ptr);
diff --git a/scripts/gendwarfksyms/symbols.c b/scripts/gendwarfksyms/symbols.c
index 35ed594f0749..42cd27c9cec4 100644
--- a/scripts/gendwarfksyms/symbols.c
+++ b/scripts/gendwarfksyms/symbols.c
@@ -3,6 +3,7 @@
* Copyright (C) 2024 Google LLC
*/
+#include <inttypes.h>
#include "gendwarfksyms.h"
#define SYMBOL_HASH_BITS 12
@@ -128,7 +129,7 @@ static bool is_exported(const char *name)
return for_each(name, NULL, NULL) > 0;
}
-void symbol_read_exports(FILE *file)
+int symbol_read_exports(FILE *file)
{
struct symbol *sym;
char *line = NULL;
@@ -159,6 +160,8 @@ void symbol_read_exports(FILE *file)
free(line);
debug("%d exported symbols", nsym);
+
+ return nsym;
}
static void get_symbol(struct symbol *sym, void *arg)
@@ -240,7 +243,7 @@ static void elf_for_each_global(int fd, elf_symbol_callback_t func, void *arg)
error("elf_getdata failed: %s", elf_errmsg(-1));
if (shdr->sh_entsize != sym_size)
- error("expected sh_entsize (%lu) to be %zu",
+ error("expected sh_entsize (%" PRIu64 ") to be %zu",
shdr->sh_entsize, sym_size);
nsyms = shdr->sh_size / shdr->sh_entsize;
@@ -290,7 +293,7 @@ static void set_symbol_addr(struct symbol *sym, void *arg)
hash_add(symbol_addrs, &sym->addr_hash,
symbol_addr_hash(&sym->addr));
- debug("%s -> { %u, %lx }", sym->name, sym->addr.section,
+ debug("%s -> { %u, %" PRIx64 " }", sym->name, sym->addr.section,
sym->addr.address);
} else if (sym->addr.section != addr->section ||
sym->addr.address != addr->address) {
diff --git a/scripts/generate_rust_analyzer.py b/scripts/generate_rust_analyzer.py
index fc27f0cca752..d5f9a0ca742c 100755
--- a/scripts/generate_rust_analyzer.py
+++ b/scripts/generate_rust_analyzer.py
@@ -10,172 +10,340 @@ import os
import pathlib
import subprocess
import sys
+from typing import Dict, Iterable, List, Literal, Optional, TypedDict
-def args_crates_cfgs(cfgs):
+def invoke_rustc(args: List[str]) -> str:
+ return subprocess.check_output(
+ [os.environ["RUSTC"]] + args,
+ stdin=subprocess.DEVNULL,
+ ).decode('utf-8').strip()
+
+def args_crates_cfgs(cfgs: List[str]) -> Dict[str, List[str]]:
crates_cfgs = {}
for cfg in cfgs:
crate, vals = cfg.split("=", 1)
- crates_cfgs[crate] = vals.replace("--cfg", "").split()
+ crates_cfgs[crate] = vals.split()
return crates_cfgs
-def generate_crates(srctree, objtree, sysroot_src, external_src, cfgs, core_edition):
+class Dependency(TypedDict):
+ crate: int
+ name: str
+
+
+class Source(TypedDict):
+ include_dirs: List[str]
+ exclude_dirs: List[str]
+
+
+class Crate(TypedDict):
+ display_name: str
+ root_module: str
+ is_workspace_member: bool
+ deps: List[Dependency]
+ cfg: List[str]
+ edition: str
+ env: Dict[str, str]
+
+
+class ProcMacroCrate(Crate):
+ is_proc_macro: Literal[True]
+ proc_macro_dylib_path: str # `pathlib.Path` is not JSON serializable.
+
+
+class CrateWithGenerated(Crate):
+ source: Source
+
+
+def generate_crates(
+ srctree: pathlib.Path,
+ objtree: pathlib.Path,
+ sysroot_src: pathlib.Path,
+ external_src: Optional[pathlib.Path],
+ cfgs: List[str],
+ core_edition: str,
+) -> List[Crate]:
# Generate the configuration list.
- cfg = []
+ generated_cfg = []
with open(objtree / "include" / "generated" / "rustc_cfg") as fd:
for line in fd:
line = line.replace("--cfg=", "")
line = line.replace("\n", "")
- cfg.append(line)
+ generated_cfg.append(line)
- # Now fill the crates list -- dependencies need to come first.
- #
- # Avoid O(n^2) iterations by keeping a map of indexes.
- crates = []
- crates_indexes = {}
+ # Now fill the crates list.
+ crates: List[Crate] = []
crates_cfgs = args_crates_cfgs(cfgs)
- def append_crate(display_name, root_module, deps, cfg=[], is_workspace_member=True, is_proc_macro=False, edition="2021"):
- crate = {
+ def get_crate_name(path: pathlib.Path) -> str:
+ return invoke_rustc(["--print", "crate-name", str(path)])
+
+ def build_crate(
+ display_name: str,
+ root_module: pathlib.Path,
+ deps: List[Dependency],
+ *,
+ cfg: Optional[List[str]],
+ is_workspace_member: Optional[bool],
+ edition: Optional[str],
+ ) -> Crate:
+ cfg = cfg if cfg is not None else crates_cfgs.get(display_name, [])
+ is_workspace_member = (
+ is_workspace_member if is_workspace_member is not None else True
+ )
+ edition = edition if edition is not None else "2021"
+ return {
"display_name": display_name,
"root_module": str(root_module),
"is_workspace_member": is_workspace_member,
- "is_proc_macro": is_proc_macro,
- "deps": [{"crate": crates_indexes[dep], "name": dep} for dep in deps],
+ "deps": deps,
"cfg": cfg,
"edition": edition,
"env": {
"RUST_MODFILE": "This is only for rust-analyzer"
}
}
- if is_proc_macro:
- proc_macro_dylib_name = subprocess.check_output(
- [os.environ["RUSTC"], "--print", "file-names", "--crate-name", display_name, "--crate-type", "proc-macro", "-"],
- stdin=subprocess.DEVNULL,
- ).decode('utf-8').strip()
- crate["proc_macro_dylib_path"] = f"{objtree}/rust/{proc_macro_dylib_name}"
- crates_indexes[display_name] = len(crates)
+
+ def append_proc_macro_crate(
+ display_name: str,
+ root_module: pathlib.Path,
+ deps: List[Dependency],
+ *,
+ cfg: Optional[List[str]] = None,
+ is_workspace_member: Optional[bool] = None,
+ edition: Optional[str] = None,
+ ) -> Dependency:
+ crate = build_crate(
+ display_name,
+ root_module,
+ deps,
+ cfg=cfg,
+ is_workspace_member=is_workspace_member,
+ edition=edition,
+ )
+ proc_macro_dylib_name = invoke_rustc([
+ "--print",
+ "file-names",
+ "--crate-name",
+ display_name,
+ "--crate-type",
+ "proc-macro",
+ "-",
+ ])
+ proc_macro_crate: ProcMacroCrate = {
+ **crate,
+ "is_proc_macro": True,
+ "proc_macro_dylib_path": str(objtree / "rust" / proc_macro_dylib_name),
+ }
+ return register_crate(proc_macro_crate)
+
+ def register_crate(crate: Crate) -> Dependency:
+ index = len(crates)
crates.append(crate)
+ return {"crate": index, "name": crate["display_name"]}
+
+ def append_crate(
+ display_name: str,
+ root_module: pathlib.Path,
+ deps: List[Dependency],
+ *,
+ cfg: Optional[List[str]] = None,
+ is_workspace_member: Optional[bool] = None,
+ edition: Optional[str] = None,
+ ) -> Dependency:
+ return register_crate(
+ build_crate(
+ display_name,
+ root_module,
+ deps,
+ cfg=cfg,
+ is_workspace_member=is_workspace_member,
+ edition=edition,
+ )
+ )
def append_sysroot_crate(
- display_name,
- deps,
- cfg=[],
- edition="2021",
- ):
- append_crate(
+ display_name: str,
+ deps: List[Dependency],
+ *,
+ cfg: Optional[List[str]] = None,
+ ) -> Dependency:
+ return append_crate(
display_name,
sysroot_src / display_name / "src" / "lib.rs",
deps,
- cfg,
+ cfg=cfg,
is_workspace_member=False,
- edition=edition,
+ # Miguel Ojeda writes:
+ #
+ # > ... in principle even the sysroot crates may have different
+ # > editions.
+ # >
+ # > For instance, in the move to 2024, it seems all happened at once
+ # > in 1.87.0 in these upstream commits:
+ # >
+ # > 0e071c2c6a58 ("Migrate core to Rust 2024")
+ # > f505d4e8e380 ("Migrate alloc to Rust 2024")
+ # > 0b2489c226c3 ("Migrate proc_macro to Rust 2024")
+ # > 993359e70112 ("Migrate std to Rust 2024")
+ # >
+ # > But in the previous move to 2021, `std` moved in 1.59.0, while
+ # > the others in 1.60.0:
+ # >
+ # > b656384d8398 ("Update stdlib to the 2021 edition")
+ # > 06a1c14d52a8 ("Switch all libraries to the 2021 edition")
+ #
+ # Link: https://lore.kernel.org/all/CANiq72kd9bHdKaAm=8xCUhSHMy2csyVed69bOc4dXyFAW4sfuw@mail.gmail.com/
+ #
+ # At the time of writing all rust versions we support build the
+ # sysroot crates with the same edition. We may need to relax this
+ # assumption if future edition moves span multiple rust versions.
+ edition=core_edition,
)
# NB: sysroot crates reexport items from one another so setting up our transitive dependencies
# here is important for ensuring that rust-analyzer can resolve symbols. The sources of truth
# for this dependency graph are `(sysroot_src / crate / "Cargo.toml" for crate in crates)`.
- append_sysroot_crate("core", [], cfg=crates_cfgs.get("core", []), edition=core_edition)
- append_sysroot_crate("alloc", ["core"])
- append_sysroot_crate("std", ["alloc", "core"])
- append_sysroot_crate("proc_macro", ["core", "std"])
+ core = append_sysroot_crate("core", [])
+ alloc = append_sysroot_crate("alloc", [core])
+ std = append_sysroot_crate("std", [alloc, core])
+ proc_macro = append_sysroot_crate("proc_macro", [core, std])
- append_crate(
+ compiler_builtins = append_crate(
"compiler_builtins",
srctree / "rust" / "compiler_builtins.rs",
- [],
+ [core],
+ )
+
+ proc_macro2 = append_crate(
+ "proc_macro2",
+ srctree / "rust" / "proc-macro2" / "lib.rs",
+ [core, alloc, std, proc_macro],
+ )
+
+ quote = append_crate(
+ "quote",
+ srctree / "rust" / "quote" / "lib.rs",
+ [core, alloc, std, proc_macro, proc_macro2],
+ edition="2018",
+ )
+
+ syn = append_crate(
+ "syn",
+ srctree / "rust" / "syn" / "lib.rs",
+ [std, proc_macro, proc_macro2, quote],
)
- append_crate(
+ macros = append_proc_macro_crate(
"macros",
srctree / "rust" / "macros" / "lib.rs",
- ["std", "proc_macro"],
- is_proc_macro=True,
+ [std, proc_macro, proc_macro2, quote, syn],
)
- append_crate(
+ build_error = append_crate(
"build_error",
srctree / "rust" / "build_error.rs",
- ["core", "compiler_builtins"],
+ [core, compiler_builtins],
)
- append_crate(
+ pin_init_internal = append_proc_macro_crate(
"pin_init_internal",
srctree / "rust" / "pin-init" / "internal" / "src" / "lib.rs",
- [],
- cfg=["kernel"],
- is_proc_macro=True,
+ [std, proc_macro, proc_macro2, quote, syn],
)
- append_crate(
+ pin_init = append_crate(
"pin_init",
srctree / "rust" / "pin-init" / "src" / "lib.rs",
- ["core", "pin_init_internal", "macros"],
- cfg=["kernel"],
+ [core, compiler_builtins, pin_init_internal, macros],
)
- append_crate(
+ ffi = append_crate(
"ffi",
srctree / "rust" / "ffi.rs",
- ["core", "compiler_builtins"],
+ [core, compiler_builtins],
)
def append_crate_with_generated(
- display_name,
- deps,
- ):
- append_crate(
+ display_name: str,
+ deps: List[Dependency],
+ ) -> Dependency:
+ crate = build_crate(
display_name,
srctree / "rust"/ display_name / "lib.rs",
deps,
- cfg=cfg,
+ cfg=generated_cfg,
+ is_workspace_member=True,
+ edition=None,
)
- crates[-1]["env"]["OBJTREE"] = str(objtree.resolve(True))
- crates[-1]["source"] = {
- "include_dirs": [
- str(srctree / "rust" / display_name),
- str(objtree / "rust")
- ],
- "exclude_dirs": [],
+ crate["env"]["OBJTREE"] = str(objtree.resolve(True))
+ crate_with_generated: CrateWithGenerated = {
+ **crate,
+ "source": {
+ "include_dirs": [
+ str(srctree / "rust" / display_name),
+ str(objtree / "rust"),
+ ],
+ "exclude_dirs": [],
+ },
}
+ return register_crate(crate_with_generated)
+
+ bindings = append_crate_with_generated("bindings", [core, ffi, pin_init])
+ uapi = append_crate_with_generated("uapi", [core, ffi, pin_init])
+ kernel = append_crate_with_generated(
+ "kernel", [core, macros, build_error, pin_init, ffi, bindings, uapi]
+ )
- append_crate_with_generated("bindings", ["core", "ffi", "pin_init"])
- append_crate_with_generated("uapi", ["core", "ffi", "pin_init"])
- append_crate_with_generated("kernel", ["core", "macros", "build_error", "pin_init", "ffi", "bindings", "uapi"])
+ scripts = srctree / "scripts"
+ makefile = (scripts / "Makefile").read_text()
+ for path in scripts.glob("*.rs"):
+ name = path.stem
+ if f"{name}-rust" not in makefile:
+ continue
+ append_crate(
+ name,
+ path,
+ [std],
+ )
- def is_root_crate(build_file, target):
+ def is_root_crate(build_file: pathlib.Path, target: str) -> bool:
try:
- return f"{target}.o" in open(build_file).read()
+ contents = build_file.read_text()
except FileNotFoundError:
return False
+ return f"{target}.o" in contents
# Then, the rest outside of `rust/`.
#
# We explicitly mention the top-level folders we want to cover.
- extra_dirs = map(lambda dir: srctree / dir, ("samples", "drivers"))
+ extra_dirs: Iterable[pathlib.Path] = (
+ srctree / dir for dir in ("samples", "drivers")
+ )
if external_src is not None:
extra_dirs = [external_src]
for folder in extra_dirs:
for path in folder.rglob("*.rs"):
logging.info("Checking %s", path)
- name = path.name.replace(".rs", "")
+ file_name = path.stem
# Skip those that are not crate roots.
- if not is_root_crate(path.parent / "Makefile", name) and \
- not is_root_crate(path.parent / "Kbuild", name):
+ if not is_root_crate(path.parent / "Makefile", file_name) and \
+ not is_root_crate(path.parent / "Kbuild", file_name):
continue
- logging.info("Adding %s", name)
+ crate_name = get_crate_name(path)
+ logging.info("Adding %s", crate_name)
append_crate(
- name,
+ crate_name,
path,
- ["core", "kernel"],
- cfg=cfg,
+ [core, kernel, pin_init],
+ cfg=generated_cfg,
)
return crates
-def main():
+def main() -> None:
parser = argparse.ArgumentParser()
parser.add_argument('--verbose', '-v', action='store_true')
parser.add_argument('--cfgs', action='append', default=[])
@@ -185,16 +353,24 @@ def main():
parser.add_argument("sysroot", type=pathlib.Path)
parser.add_argument("sysroot_src", type=pathlib.Path)
parser.add_argument("exttree", type=pathlib.Path, nargs="?")
- args = parser.parse_args()
+
+ class Args(argparse.Namespace):
+ verbose: bool
+ cfgs: List[str]
+ srctree: pathlib.Path
+ objtree: pathlib.Path
+ sysroot: pathlib.Path
+ sysroot_src: pathlib.Path
+ exttree: Optional[pathlib.Path]
+ core_edition: str
+
+ args = parser.parse_args(namespace=Args())
logging.basicConfig(
format="[%(asctime)s] [%(levelname)s] %(message)s",
level=logging.INFO if args.verbose else logging.WARNING
)
- # Making sure that the `sysroot` and `sysroot_src` belong to the same toolchain.
- assert args.sysroot in args.sysroot_src.parents
-
rust_project = {
"crates": generate_crates(args.srctree, args.objtree, args.sysroot_src, args.exttree, args.cfgs, args.core_edition),
"sysroot": str(args.sysroot),
diff --git a/scripts/genksyms/parse.y b/scripts/genksyms/parse.y
index efdcf07c4eb6..cabcd146f3aa 100644
--- a/scripts/genksyms/parse.y
+++ b/scripts/genksyms/parse.y
@@ -325,8 +325,8 @@ direct_declarator:
{ $$ = $4; }
| direct_declarator BRACKET_PHRASE
{ $$ = $2; }
- | '(' declarator ')'
- { $$ = $3; }
+ | '(' attribute_opt declarator ')'
+ { $$ = $4; }
;
/* Nested declarators differ from regular declarators in that they do
diff --git a/scripts/get_abi.py b/scripts/get_abi.py
deleted file mode 100755
index 7ce4748a46d2..000000000000
--- a/scripts/get_abi.py
+++ /dev/null
@@ -1,214 +0,0 @@
-#!/usr/bin/env python3
-# pylint: disable=R0903
-# Copyright(c) 2025: Mauro Carvalho Chehab <mchehab@kernel.org>.
-# SPDX-License-Identifier: GPL-2.0
-
-"""
-Parse ABI documentation and produce results from it.
-"""
-
-import argparse
-import logging
-import os
-import sys
-
-# Import Python modules
-
-LIB_DIR = "lib/abi"
-SRC_DIR = os.path.dirname(os.path.realpath(__file__))
-
-sys.path.insert(0, os.path.join(SRC_DIR, LIB_DIR))
-
-from abi_parser import AbiParser # pylint: disable=C0413
-from abi_regex import AbiRegex # pylint: disable=C0413
-from helpers import ABI_DIR, DEBUG_HELP # pylint: disable=C0413
-from system_symbols import SystemSymbols # pylint: disable=C0413
-
-# Command line classes
-
-
-REST_DESC = """
-Produce output in ReST format.
-
-The output is done on two sections:
-
-- Symbols: show all parsed symbols in alphabetic order;
-- Files: cross reference the content of each file with the symbols on it.
-"""
-
-class AbiRest:
- """Initialize an argparse subparser for rest output"""
-
- def __init__(self, subparsers):
- """Initialize argparse subparsers"""
-
- parser = subparsers.add_parser("rest",
- formatter_class=argparse.RawTextHelpFormatter,
- description=REST_DESC)
-
- parser.add_argument("--enable-lineno", action="store_true",
- help="enable lineno")
- parser.add_argument("--raw", action="store_true",
- help="output text as contained in the ABI files. "
- "It not used, output will contain dynamically"
- " generated cross references when possible.")
- parser.add_argument("--no-file", action="store_true",
- help="Don't the files section")
- parser.add_argument("--show-hints", help="Show-hints")
-
- parser.set_defaults(func=self.run)
-
- def run(self, args):
- """Run subparser"""
-
- parser = AbiParser(args.dir, debug=args.debug)
- parser.parse_abi()
- parser.check_issues()
-
- for t in parser.doc(args.raw, not args.no_file):
- if args.enable_lineno:
- print (f".. LINENO {t[1]}#{t[2]}\n\n")
-
- print(t[0])
-
-class AbiValidate:
- """Initialize an argparse subparser for ABI validation"""
-
- def __init__(self, subparsers):
- """Initialize argparse subparsers"""
-
- parser = subparsers.add_parser("validate",
- formatter_class=argparse.ArgumentDefaultsHelpFormatter,
- description="list events")
-
- parser.set_defaults(func=self.run)
-
- def run(self, args):
- """Run subparser"""
-
- parser = AbiParser(args.dir, debug=args.debug)
- parser.parse_abi()
- parser.check_issues()
-
-
-class AbiSearch:
- """Initialize an argparse subparser for ABI search"""
-
- def __init__(self, subparsers):
- """Initialize argparse subparsers"""
-
- parser = subparsers.add_parser("search",
- formatter_class=argparse.ArgumentDefaultsHelpFormatter,
- description="Search ABI using a regular expression")
-
- parser.add_argument("expression",
- help="Case-insensitive search pattern for the ABI symbol")
-
- parser.set_defaults(func=self.run)
-
- def run(self, args):
- """Run subparser"""
-
- parser = AbiParser(args.dir, debug=args.debug)
- parser.parse_abi()
- parser.search_symbols(args.expression)
-
-UNDEFINED_DESC="""
-Check undefined ABIs on local machine.
-
-Read sysfs devnodes and check if the devnodes there are defined inside
-ABI documentation.
-
-The search logic tries to minimize the number of regular expressions to
-search per each symbol.
-
-By default, it runs on a single CPU, as Python support for CPU threads
-is still experimental, and multi-process runs on Python is very slow.
-
-On experimental tests, if the number of ABI symbols to search per devnode
-is contained on a limit of ~150 regular expressions, using a single CPU
-is a lot faster than using multiple processes. However, if the number of
-regular expressions to check is at the order of ~30000, using multiple
-CPUs speeds up the check.
-"""
-
-class AbiUndefined:
- """
- Initialize an argparse subparser for logic to check undefined ABI at
- the current machine's sysfs
- """
-
- def __init__(self, subparsers):
- """Initialize argparse subparsers"""
-
- parser = subparsers.add_parser("undefined",
- formatter_class=argparse.RawTextHelpFormatter,
- description=UNDEFINED_DESC)
-
- parser.add_argument("-S", "--sysfs-dir", default="/sys",
- help="directory where sysfs is mounted")
- parser.add_argument("-s", "--search-string",
- help="search string regular expression to limit symbol search")
- parser.add_argument("-H", "--show-hints", action="store_true",
- help="Hints about definitions for missing ABI symbols.")
- parser.add_argument("-j", "--jobs", "--max-workers", type=int, default=1,
- help="If bigger than one, enables multiprocessing.")
- parser.add_argument("-c", "--max-chunk-size", type=int, default=50,
- help="Maximum number of chunk size")
- parser.add_argument("-f", "--found", action="store_true",
- help="Also show found items. "
- "Helpful to debug the parser."),
- parser.add_argument("-d", "--dry-run", action="store_true",
- help="Don't actually search for undefined. "
- "Helpful to debug the parser."),
-
- parser.set_defaults(func=self.run)
-
- def run(self, args):
- """Run subparser"""
-
- abi = AbiRegex(args.dir, debug=args.debug,
- search_string=args.search_string)
-
- abi_symbols = SystemSymbols(abi=abi, hints=args.show_hints,
- sysfs=args.sysfs_dir)
-
- abi_symbols.check_undefined_symbols(dry_run=args.dry_run,
- found=args.found,
- max_workers=args.jobs,
- chunk_size=args.max_chunk_size)
-
-
-def main():
- """Main program"""
-
- parser = argparse.ArgumentParser(formatter_class=argparse.RawTextHelpFormatter)
-
- parser.add_argument("-d", "--debug", type=int, default=0, help="debug level")
- parser.add_argument("-D", "--dir", default=ABI_DIR, help=DEBUG_HELP)
-
- subparsers = parser.add_subparsers()
-
- AbiRest(subparsers)
- AbiValidate(subparsers)
- AbiSearch(subparsers)
- AbiUndefined(subparsers)
-
- args = parser.parse_args()
-
- if args.debug:
- level = logging.DEBUG
- else:
- level = logging.INFO
-
- logging.basicConfig(level=level, format="[%(levelname)s] %(message)s")
-
- if "func" in args:
- args.func(args)
- else:
- sys.exit(f"Please specify a valid command for {sys.argv[0]}")
-
-
-# Call main method
-if __name__ == "__main__":
- main()
diff --git a/scripts/get_feat.pl b/scripts/get_feat.pl
deleted file mode 100755
index 40fb28c8424e..000000000000
--- a/scripts/get_feat.pl
+++ /dev/null
@@ -1,641 +0,0 @@
-#!/usr/bin/env perl
-# SPDX-License-Identifier: GPL-2.0
-
-use strict;
-use Pod::Usage;
-use Getopt::Long;
-use File::Find;
-use Fcntl ':mode';
-use Cwd 'abs_path';
-
-my $help;
-my $man;
-my $debug;
-my $arch;
-my $feat;
-my $enable_fname;
-
-my $basename = abs_path($0);
-$basename =~ s,/[^/]+$,/,;
-
-my $prefix=$basename . "../Documentation/features";
-
-# Used only at for full features output. The script will auto-adjust
-# such values for the minimal possible values
-my $status_size = 1;
-my $description_size = 1;
-
-GetOptions(
- "debug|d+" => \$debug,
- "dir=s" => \$prefix,
- 'help|?' => \$help,
- 'arch=s' => \$arch,
- 'feat=s' => \$feat,
- 'feature=s' => \$feat,
- "enable-fname" => \$enable_fname,
- man => \$man
-) or pod2usage(2);
-
-pod2usage(1) if $help;
-pod2usage(-exitstatus => 0, -verbose => 2) if $man;
-
-pod2usage(1) if (scalar @ARGV < 1 || @ARGV > 2);
-
-my ($cmd, $arg) = @ARGV;
-
-pod2usage(2) if ($cmd ne "current" && $cmd ne "rest" && $cmd ne "validate"
- && $cmd ne "ls" && $cmd ne "list");
-
-require Data::Dumper if ($debug);
-
-my %data;
-my %archs;
-
-#
-# Displays an error message, printing file name and line
-#
-sub parse_error($$$$) {
- my ($file, $ln, $msg, $data) = @_;
-
- $data =~ s/\s+$/\n/;
-
- print STDERR "Warning: file $file#$ln:\n\t$msg";
-
- if ($data ne "") {
- print STDERR ". Line\n\t\t$data";
- } else {
- print STDERR "\n";
- }
-}
-
-#
-# Parse a features file, storing its contents at %data
-#
-
-my $h_name = "Feature";
-my $h_kconfig = "Kconfig";
-my $h_description = "Description";
-my $h_subsys = "Subsystem";
-my $h_status = "Status";
-my $h_arch = "Architecture";
-
-my $max_size_name = length($h_name);
-my $max_size_kconfig = length($h_kconfig);
-my $max_size_description = length($h_description);
-my $max_size_subsys = length($h_subsys);
-my $max_size_status = length($h_status);
-
-my $max_size_arch = 0;
-my $max_size_arch_with_header;
-my $max_description_word = 0;
-
-sub parse_feat {
- my $file = $File::Find::name;
-
- my $mode = (stat($file))[2];
- return if ($mode & S_IFDIR);
- return if ($file =~ m,($prefix)/arch-support.txt,);
- return if (!($file =~ m,arch-support.txt$,));
-
- if ($enable_fname) {
- printf ".. FILE %s\n", abs_path($file);
- }
-
- my $subsys = "";
- $subsys = $2 if ( m,.*($prefix)/([^/]+).*,);
-
- if (length($subsys) > $max_size_subsys) {
- $max_size_subsys = length($subsys);
- }
-
- my $name;
- my $kconfig;
- my $description;
- my $comments = "";
- my $last_status;
- my $ln;
- my %arch_table;
-
- print STDERR "Opening $file\n" if ($debug > 1);
- open IN, $file;
-
- while(<IN>) {
- $ln++;
-
- if (m/^\#\s+Feature\s+name:\s*(.*\S)/) {
- $name = $1;
- if (length($name) > $max_size_name) {
- $max_size_name = length($name);
- }
- next;
- }
- if (m/^\#\s+Kconfig:\s*(.*\S)/) {
- $kconfig = $1;
- if (length($kconfig) > $max_size_kconfig) {
- $max_size_kconfig = length($kconfig);
- }
- next;
- }
- if (m/^\#\s+description:\s*(.*\S)/) {
- $description = $1;
- if (length($description) > $max_size_description) {
- $max_size_description = length($description);
- }
-
- foreach my $word (split /\s+/, $description) {
- if (length($word) > $max_description_word) {
- $max_description_word = length($word);
- }
- }
-
- next;
- }
- next if (m/^\\s*$/);
- next if (m/^\s*\-+\s*$/);
- next if (m/^\s*\|\s*arch\s*\|\s*status\s*\|\s*$/);
-
- if (m/^\#\s*(.*)/) {
- $comments .= "$1\n";
- next;
- }
- if (m/^\s*\|\s*(\S+):\s*\|\s*(\S+)\s*\|\s*$/) {
- my $a = $1;
- my $status = $2;
-
- if (length($status) > $max_size_status) {
- $max_size_status = length($status);
- }
- if (length($a) > $max_size_arch) {
- $max_size_arch = length($a);
- }
-
- $status = "---" if ($status =~ m/^\.\.$/);
-
- $archs{$a} = 1;
- $arch_table{$a} = $status;
- next;
- }
-
- #Everything else is an error
- parse_error($file, $ln, "line is invalid", $_);
- }
- close IN;
-
- if (!$name) {
- parse_error($file, $ln, "Feature name not found", "");
- return;
- }
-
- parse_error($file, $ln, "Subsystem not found", "") if (!$subsys);
- parse_error($file, $ln, "Kconfig not found", "") if (!$kconfig);
- parse_error($file, $ln, "Description not found", "") if (!$description);
-
- if (!%arch_table) {
- parse_error($file, $ln, "Architecture table not found", "");
- return;
- }
-
- $data{$name}->{where} = $file;
- $data{$name}->{subsys} = $subsys;
- $data{$name}->{kconfig} = $kconfig;
- $data{$name}->{description} = $description;
- $data{$name}->{comments} = $comments;
- $data{$name}->{table} = \%arch_table;
-
- $max_size_arch_with_header = $max_size_arch + length($h_arch);
-}
-
-#
-# Output feature(s) for a given architecture
-#
-sub output_arch_table {
- my $title = "Feature status on $arch architecture";
-
- print "=" x length($title) . "\n";
- print "$title\n";
- print "=" x length($title) . "\n\n";
-
- print "=" x $max_size_subsys;
- print " ";
- print "=" x $max_size_name;
- print " ";
- print "=" x $max_size_kconfig;
- print " ";
- print "=" x $max_size_status;
- print " ";
- print "=" x $max_size_description;
- print "\n";
- printf "%-${max_size_subsys}s ", $h_subsys;
- printf "%-${max_size_name}s ", $h_name;
- printf "%-${max_size_kconfig}s ", $h_kconfig;
- printf "%-${max_size_status}s ", $h_status;
- printf "%-${max_size_description}s\n", $h_description;
- print "=" x $max_size_subsys;
- print " ";
- print "=" x $max_size_name;
- print " ";
- print "=" x $max_size_kconfig;
- print " ";
- print "=" x $max_size_status;
- print " ";
- print "=" x $max_size_description;
- print "\n";
-
- foreach my $name (sort {
- ($data{$a}->{subsys} cmp $data{$b}->{subsys}) ||
- ("\L$a" cmp "\L$b")
- } keys %data) {
- next if ($feat && $name ne $feat);
-
- my %arch_table = %{$data{$name}->{table}};
- printf "%-${max_size_subsys}s ", $data{$name}->{subsys};
- printf "%-${max_size_name}s ", $name;
- printf "%-${max_size_kconfig}s ", $data{$name}->{kconfig};
- printf "%-${max_size_status}s ", $arch_table{$arch};
- printf "%-s\n", $data{$name}->{description};
- }
-
- print "=" x $max_size_subsys;
- print " ";
- print "=" x $max_size_name;
- print " ";
- print "=" x $max_size_kconfig;
- print " ";
- print "=" x $max_size_status;
- print " ";
- print "=" x $max_size_description;
- print "\n";
-}
-
-#
-# list feature(s) for a given architecture
-#
-sub list_arch_features {
- print "#\n# Kernel feature support matrix of the '$arch' architecture:\n#\n";
-
- foreach my $name (sort {
- ($data{$a}->{subsys} cmp $data{$b}->{subsys}) ||
- ("\L$a" cmp "\L$b")
- } keys %data) {
- next if ($feat && $name ne $feat);
-
- my %arch_table = %{$data{$name}->{table}};
-
- my $status = $arch_table{$arch};
- $status = " " x ((4 - length($status)) / 2) . $status;
-
- printf " %${max_size_subsys}s/ ", $data{$name}->{subsys};
- printf "%-${max_size_name}s: ", $name;
- printf "%-5s| ", $status;
- printf "%${max_size_kconfig}s # ", $data{$name}->{kconfig};
- printf " %s\n", $data{$name}->{description};
- }
-}
-
-#
-# Output a feature on all architectures
-#
-sub output_feature {
- my $title = "Feature $feat";
-
- print "=" x length($title) . "\n";
- print "$title\n";
- print "=" x length($title) . "\n\n";
-
- print ":Subsystem: $data{$feat}->{subsys} \n" if ($data{$feat}->{subsys});
- print ":Kconfig: $data{$feat}->{kconfig} \n" if ($data{$feat}->{kconfig});
-
- my $desc = $data{$feat}->{description};
- $desc =~ s/^([a-z])/\U$1/;
- $desc =~ s/\.?\s*//;
- print "\n$desc.\n\n";
-
- my $com = $data{$feat}->{comments};
- $com =~ s/^\s+//;
- $com =~ s/\s+$//;
- if ($com) {
- print "Comments\n";
- print "--------\n\n";
- print "$com\n\n";
- }
-
- print "=" x $max_size_arch_with_header;
- print " ";
- print "=" x $max_size_status;
- print "\n";
-
- printf "%-${max_size_arch}s ", $h_arch;
- printf "%-${max_size_status}s", $h_status . "\n";
-
- print "=" x $max_size_arch_with_header;
- print " ";
- print "=" x $max_size_status;
- print "\n";
-
- my %arch_table = %{$data{$feat}->{table}};
- foreach my $arch (sort keys %arch_table) {
- printf "%-${max_size_arch}s ", $arch;
- printf "%-${max_size_status}s\n", $arch_table{$arch};
- }
-
- print "=" x $max_size_arch_with_header;
- print " ";
- print "=" x $max_size_status;
- print "\n";
-}
-
-#
-# Output all features for all architectures
-#
-
-sub matrix_lines($$$) {
- my $desc_size = shift;
- my $status_size = shift;
- my $header = shift;
- my $fill;
- my $ln_marker;
-
- if ($header) {
- $ln_marker = "=";
- } else {
- $ln_marker = "-";
- }
-
- $fill = $ln_marker;
-
- print "+";
- print $fill x $max_size_name;
- print "+";
- print $fill x $desc_size;
- print "+";
- print $ln_marker x $status_size;
- print "+\n";
-}
-
-sub output_matrix {
- my $title = "Feature status on all architectures";
- my $notcompat = "Not compatible";
-
- print "=" x length($title) . "\n";
- print "$title\n";
- print "=" x length($title) . "\n\n";
-
- my $desc_title = "$h_kconfig / $h_description";
-
- my $desc_size = $max_size_kconfig + 4;
- if (!$description_size) {
- $desc_size = $max_size_description if ($max_size_description > $desc_size);
- } else {
- $desc_size = $description_size if ($description_size > $desc_size);
- }
- $desc_size = $max_description_word if ($max_description_word > $desc_size);
-
- $desc_size = length($desc_title) if (length($desc_title) > $desc_size);
-
- $max_size_status = length($notcompat) if (length($notcompat) > $max_size_status);
-
- # Ensure that the status will fit
- my $min_status_size = $max_size_status + $max_size_arch + 6;
- $status_size = $min_status_size if ($status_size < $min_status_size);
-
-
- my $cur_subsys = "";
- foreach my $name (sort {
- ($data{$a}->{subsys} cmp $data{$b}->{subsys}) or
- ("\L$a" cmp "\L$b")
- } keys %data) {
-
- if ($cur_subsys ne $data{$name}->{subsys}) {
- if ($cur_subsys ne "") {
- printf "\n";
- }
-
- $cur_subsys = $data{$name}->{subsys};
-
- my $title = "Subsystem: $cur_subsys";
- print "$title\n";
- print "=" x length($title) . "\n\n";
-
-
- matrix_lines($desc_size, $status_size, 0);
-
- printf "|%-${max_size_name}s", $h_name;
- printf "|%-${desc_size}s", $desc_title;
-
- printf "|%-${status_size}s|\n", "Status per architecture";
- matrix_lines($desc_size, $status_size, 1);
- }
-
- my %arch_table = %{$data{$name}->{table}};
- my $cur_status = "";
-
- my (@lines, @descs);
- my $line = "";
- foreach my $arch (sort {
- ($arch_table{$b} cmp $arch_table{$a}) or
- ("\L$a" cmp "\L$b")
- } keys %arch_table) {
-
- my $status = $arch_table{$arch};
-
- if ($status eq "---") {
- $status = $notcompat;
- }
-
- if ($status ne $cur_status) {
- if ($line ne "") {
- push @lines, $line;
- $line = "";
- }
- $line = "- **" . $status . "**: " . $arch;
- } elsif (length($line) + length ($arch) + 2 < $status_size) {
- $line .= ", " . $arch;
- } else {
- push @lines, $line;
- $line = " " . $arch;
- }
- $cur_status = $status;
- }
- push @lines, $line if ($line ne "");
-
- my $description = $data{$name}->{description};
- while (length($description) > $desc_size) {
- my $d = substr $description, 0, $desc_size;
-
- # Ensure that it will end on a space
- # if it can't, it means that the size is too small
- # Instead of aborting it, let's print what we have
- if (!($d =~ s/^(.*)\s+.*/$1/)) {
- $d = substr $d, 0, -1;
- push @descs, "$d\\";
- $description =~ s/^\Q$d\E//;
- } else {
- push @descs, $d;
- $description =~ s/^\Q$d\E\s+//;
- }
- }
- push @descs, $description;
-
- # Ensure that the full description will be printed
- push @lines, "" while (scalar(@lines) < 2 + scalar(@descs));
-
- my $ln = 0;
- for my $line(@lines) {
- if (!$ln) {
- printf "|%-${max_size_name}s", $name;
- printf "|%-${desc_size}s", "``" . $data{$name}->{kconfig} . "``";
- } elsif ($ln >= 2 && scalar(@descs)) {
- printf "|%-${max_size_name}s", "";
- printf "|%-${desc_size}s", shift @descs;
- } else {
- printf "|%-${max_size_name}s", "";
- printf "|%-${desc_size}s", "";
- }
-
- printf "|%-${status_size}s|\n", $line;
-
- $ln++;
- }
- matrix_lines($desc_size, $status_size, 0);
- }
-}
-
-
-#
-# Parses all feature files located at $prefix dir
-#
-find({wanted =>\&parse_feat, no_chdir => 1}, $prefix);
-
-print STDERR Data::Dumper->Dump([\%data], [qw(*data)]) if ($debug);
-
-#
-# Handles the command
-#
-if ($cmd eq "current") {
- $arch = qx(uname -m | sed 's/x86_64/x86/' | sed 's/i386/x86/' | sed 's/s390x/s390/');
- $arch =~s/\s+$//;
-}
-
-if ($cmd eq "ls" or $cmd eq "list") {
- if (!$arch) {
- $arch = qx(uname -m | sed 's/x86_64/x86/' | sed 's/i386/x86/' | sed 's/s390x/s390/');
- $arch =~s/\s+$//;
- }
-
- list_arch_features;
-
- exit;
-}
-
-if ($cmd ne "validate") {
- if ($arch) {
- output_arch_table;
- } elsif ($feat) {
- output_feature;
- } else {
- output_matrix;
- }
-}
-
-__END__
-
-=head1 NAME
-
-get_feat.pl - parse the Linux Feature files and produce a ReST book.
-
-=head1 SYNOPSIS
-
-B<get_feat.pl> [--debug] [--man] [--help] [--dir=<dir>] [--arch=<arch>]
- [--feature=<feature>|--feat=<feature>] <COMAND> [<ARGUMENT>]
-
-Where <COMMAND> can be:
-
-=over 8
-
-B<current> - output table in ReST compatible ASCII format
- with features for this machine's architecture
-
-B<rest> - output table(s) in ReST compatible ASCII format
- with features in ReST markup language. The output
- is affected by --arch or --feat/--feature flags.
-
-B<validate> - validate the contents of the files under
- Documentation/features.
-
-B<ls> or B<list> - list features for this machine's architecture,
- using an easier to parse format.
- The output is affected by --arch flag.
-
-=back
-
-=head1 OPTIONS
-
-=over 8
-
-=item B<--arch>
-
-Output features for an specific architecture, optionally filtering for
-a single specific feature.
-
-=item B<--feat> or B<--feature>
-
-Output features for a single specific feature.
-
-=item B<--dir>
-
-Changes the location of the Feature files. By default, it uses
-the Documentation/features directory.
-
-=item B<--enable-fname>
-
-Prints the file name of the feature files. This can be used in order to
-track dependencies during documentation build.
-
-=item B<--debug>
-
-Put the script in verbose mode, useful for debugging. Can be called multiple
-times, to increase verbosity.
-
-=item B<--help>
-
-Prints a brief help message and exits.
-
-=item B<--man>
-
-Prints the manual page and exits.
-
-=back
-
-=head1 DESCRIPTION
-
-Parse the Linux feature files from Documentation/features (by default),
-optionally producing results at ReST format.
-
-It supports output data per architecture, per feature or a
-feature x arch matrix.
-
-When used with B<rest> command, it will use either one of the tree formats:
-
-If neither B<--arch> or B<--feature> arguments are used, it will output a
-matrix with features per architecture.
-
-If B<--arch> argument is used, it will output the features availability for
-a given architecture.
-
-If B<--feat> argument is used, it will output the content of the feature
-file using ReStructured Text markup.
-
-=head1 BUGS
-
-Report bugs to Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
-
-=head1 COPYRIGHT
-
-Copyright (c) 2019 by Mauro Carvalho Chehab <mchehab+samsung@kernel.org>.
-
-License GPLv2: GNU GPL version 2 <http://gnu.org/licenses/gpl.html>.
-
-This is free software: you are free to change and redistribute it.
-There is NO WARRANTY, to the extent permitted by law.
-
-=cut
diff --git a/scripts/get_maintainer.pl b/scripts/get_maintainer.pl
index 4414194bedcf..f0ca0db6ddc2 100755
--- a/scripts/get_maintainer.pl
+++ b/scripts/get_maintainer.pl
@@ -375,8 +375,10 @@ sub read_maintainer_file {
##Filename pattern matching
if ($type eq "F" || $type eq "X") {
$value =~ s@\.@\\\.@g; ##Convert . to \.
+ $value =~ s/\*\*/\x00/g; ##Convert ** to placeholder
$value =~ s/\*/\.\*/g; ##Convert * to .*
$value =~ s/\?/\./g; ##Convert ? to .
+ $value =~ s/\x00/(?:.*)/g; ##Convert placeholder to (?:.*)
##if pattern is a directory and it lacks a trailing slash, add one
if ((-d $value)) {
$value =~ s@([^/])$@$1/@;
@@ -746,8 +748,10 @@ sub self_test {
if (($type eq "F" || $type eq "X") &&
($self_test eq "" || $self_test =~ /\bpatterns\b/)) {
$value =~ s@\.@\\\.@g; ##Convert . to \.
+ $value =~ s/\*\*/\x00/g; ##Convert ** to placeholder
$value =~ s/\*/\.\*/g; ##Convert * to .*
$value =~ s/\?/\./g; ##Convert ? to .
+ $value =~ s/\x00/(?:.*)/g; ##Convert placeholder to (?:.*)
##if pattern is a directory and it lacks a trailing slash, add one
if ((-d $value)) {
$value =~ s@([^/])$@$1/@;
@@ -921,7 +925,7 @@ sub get_maintainers {
my $value_pd = ($value =~ tr@/@@);
my $file_pd = ($file =~ tr@/@@);
$value_pd++ if (substr($value,-1,1) ne "/");
- $value_pd = -1 if ($value =~ /^\.\*/);
+ $value_pd = -1 if ($value =~ /^(\.\*|\(\?:\.\*\))/);
if ($value_pd >= $file_pd &&
range_is_maintained($start, $end) &&
range_has_maintainer($start, $end)) {
@@ -955,6 +959,7 @@ sub get_maintainers {
$line =~ s/([^\\])\.([^\*])/$1\?$2/g;
$line =~ s/([^\\])\.$/$1\?/g; ##Convert . back to ?
$line =~ s/\\\./\./g; ##Convert \. to .
+ $line =~ s/\(\?:\.\*\)/\*\*/g; ##Convert (?:.*) to **
$line =~ s/\.\*/\*/g; ##Convert .* to *
}
my $count = $line =~ s/^([A-Z]):/$1:\t/g;
@@ -1048,7 +1053,7 @@ sub file_match_pattern {
if ($file =~ m@^$pattern@) {
my $s1 = ($file =~ tr@/@@);
my $s2 = ($pattern =~ tr@/@@);
- if ($s1 == $s2) {
+ if ($s1 == $s2 || $pattern =~ /\(\?:/) {
return 1;
}
}
diff --git a/scripts/headers_install.sh b/scripts/headers_install.sh
index 4c20c62c4faf..9c15e748761c 100755
--- a/scripts/headers_install.sh
+++ b/scripts/headers_install.sh
@@ -64,38 +64,10 @@ configs=$(sed -e '
d
' $OUTFILE)
-# The entries in the following list do not result in an error.
-# Please do not add a new entry. This list is only for existing ones.
-# The list will be reduced gradually, and deleted eventually. (hopefully)
-#
-# The format is <file-name>:<CONFIG-option> in each line.
-config_leak_ignores="
-arch/arc/include/uapi/asm/page.h:CONFIG_ARC_PAGE_SIZE_16K
-arch/arc/include/uapi/asm/page.h:CONFIG_ARC_PAGE_SIZE_4K
-arch/arc/include/uapi/asm/swab.h:CONFIG_ARC_HAS_SWAPE
-arch/arm/include/uapi/asm/ptrace.h:CONFIG_CPU_ENDIAN_BE8
-arch/nios2/include/uapi/asm/swab.h:CONFIG_NIOS2_CI_SWAB_NO
-arch/nios2/include/uapi/asm/swab.h:CONFIG_NIOS2_CI_SWAB_SUPPORT
-arch/x86/include/uapi/asm/auxvec.h:CONFIG_IA32_EMULATION
-arch/x86/include/uapi/asm/auxvec.h:CONFIG_X86_64
-"
-
for c in $configs
do
- leak_error=1
-
- for ignore in $config_leak_ignores
- do
- if echo "$INFILE:$c" | grep -q "$ignore$"; then
- leak_error=
- break
- fi
- done
-
- if [ "$leak_error" = 1 ]; then
- echo "error: $INFILE: leak $c to user-space" >&2
- exit 1
- fi
+ echo "error: $INFILE: leak $c to user-space" >&2
+ exit 1
done
rm -f $TMPFILE
diff --git a/scripts/jobserver-exec b/scripts/jobserver-exec
index 7eca035472d3..758e947a6fb9 100755
--- a/scripts/jobserver-exec
+++ b/scripts/jobserver-exec
@@ -1,77 +1,35 @@
#!/usr/bin/env python3
# SPDX-License-Identifier: GPL-2.0+
-#
-# This determines how many parallel tasks "make" is expecting, as it is
-# not exposed via an special variables, reserves them all, runs a subprocess
-# with PARALLELISM environment variable set, and releases the jobs back again.
-#
-# https://www.gnu.org/software/make/manual/html_node/POSIX-Jobserver.html#POSIX-Jobserver
-from __future__ import print_function
-import os, sys, errno
-import subprocess
-# Extract and prepare jobserver file descriptors from environment.
-claim = 0
-jobs = b""
-try:
- # Fetch the make environment options.
- flags = os.environ['MAKEFLAGS']
+"""
+Determines how many parallel tasks "make" is expecting, as it is
+not exposed via any special variables, reserves them all, runs a subprocess
+with PARALLELISM environment variable set, and releases the jobs back again.
- # Look for "--jobserver=R,W"
- # Note that GNU Make has used --jobserver-fds and --jobserver-auth
- # so this handles all of them.
- opts = [x for x in flags.split(" ") if x.startswith("--jobserver")]
+See:
+ https://www.gnu.org/software/make/manual/html_node/POSIX-Jobserver.html#POSIX-Jobserver
+"""
- # Parse out R,W file descriptor numbers and set them nonblocking.
- # If the MAKEFLAGS variable contains multiple instances of the
- # --jobserver-auth= option, the last one is relevant.
- fds = opts[-1].split("=", 1)[1]
+import os
+import sys
- # Starting with GNU Make 4.4, named pipes are used for reader and writer.
- # Example argument: --jobserver-auth=fifo:/tmp/GMfifo8134
- _, _, path = fds.partition('fifo:')
+LIB_DIR = "../tools/lib/python"
+SRC_DIR = os.path.dirname(os.path.realpath(__file__))
- if path:
- reader = os.open(path, os.O_RDONLY | os.O_NONBLOCK)
- writer = os.open(path, os.O_WRONLY)
- else:
- reader, writer = [int(x) for x in fds.split(",", 1)]
- # Open a private copy of reader to avoid setting nonblocking
- # on an unexpecting process with the same reader fd.
- reader = os.open("/proc/self/fd/%d" % (reader),
- os.O_RDONLY | os.O_NONBLOCK)
+sys.path.insert(0, os.path.join(SRC_DIR, LIB_DIR))
- # Read out as many jobserver slots as possible.
- while True:
- try:
- slot = os.read(reader, 8)
- jobs += slot
- except (OSError, IOError) as e:
- if e.errno == errno.EWOULDBLOCK:
- # Stop at the end of the jobserver queue.
- break
- # If something went wrong, give back the jobs.
- if len(jobs):
- os.write(writer, jobs)
- raise e
- # Add a bump for our caller's reserveration, since we're just going
- # to sit here blocked on our child.
- claim = len(jobs) + 1
-except (KeyError, IndexError, ValueError, OSError, IOError) as e:
- # Any missing environment strings or bad fds should result in just
- # not being parallel.
- pass
+from jobserver import JobserverExec # pylint: disable=C0415
-# We can only claim parallelism if there was a jobserver (i.e. a top-level
-# "-jN" argument) and there were no other failures. Otherwise leave out the
-# environment variable and let the child figure out what is best.
-if claim > 0:
- os.environ['PARALLELISM'] = '%d' % (claim)
-rc = subprocess.call(sys.argv[1:])
+def main():
+ """Main program"""
+ if len(sys.argv) < 2:
+ name = os.path.basename(__file__)
+ sys.exit("usage: " + name +" command [args ...]\n" + __doc__)
-# Return all the reserved slots.
-if len(jobs):
- os.write(writer, jobs)
+ with JobserverExec() as jobserver:
+ jobserver.run(sys.argv[1:])
-sys.exit(rc)
+
+if __name__ == "__main__":
+ main()
diff --git a/scripts/kallsyms.c b/scripts/kallsyms.c
index 4b0234e4b12f..37d5c095ad22 100644
--- a/scripts/kallsyms.c
+++ b/scripts/kallsyms.c
@@ -46,7 +46,6 @@ struct addr_range {
};
static unsigned long long _text;
-static unsigned long long relative_base;
static struct addr_range text_ranges[] = {
{ "_stext", "_etext" },
{ "_sinittext", "_einittext" },
@@ -57,6 +56,7 @@ static struct addr_range text_ranges[] = {
static struct sym_entry **table;
static unsigned int table_size, table_cnt;
static int all_symbols;
+static int pc_relative;
static int token_profit[0x10000];
@@ -280,7 +280,7 @@ static void read_map(const char *in)
static void output_label(const char *label)
{
printf(".globl %s\n", label);
- printf("\tALGN\n");
+ printf("\t.balign 4\n");
printf("%s:\n", label);
}
@@ -343,15 +343,6 @@ static void write_src(void)
unsigned int *markers, markers_cnt;
char buf[KSYM_NAME_LEN];
- printf("#include <asm/bitsperlong.h>\n");
- printf("#if BITS_PER_LONG == 64\n");
- printf("#define PTR .quad\n");
- printf("#define ALGN .balign 8\n");
- printf("#else\n");
- printf("#define PTR .long\n");
- printf("#define ALGN .balign 4\n");
- printf("#endif\n");
-
printf("\t.section .rodata, \"a\"\n");
output_label("kallsyms_num_syms");
@@ -434,34 +425,24 @@ static void write_src(void)
output_label("kallsyms_offsets");
for (i = 0; i < table_cnt; i++) {
- /*
- * Use the offset relative to the lowest value
- * encountered of all relative symbols, and emit
- * non-relocatable fixed offsets that will be fixed
- * up at runtime.
- */
-
- long long offset;
-
- offset = table[i]->addr - relative_base;
- if (offset < 0 || offset > UINT_MAX) {
- fprintf(stderr, "kallsyms failure: "
- "relative symbol value %#llx out of range\n",
- table[i]->addr);
- exit(EXIT_FAILURE);
+ if (pc_relative) {
+ long long offset = table[i]->addr - _text;
+
+ if (offset < INT_MIN || offset > INT_MAX) {
+ fprintf(stderr, "kallsyms failure: "
+ "relative symbol value %#llx out of range\n",
+ table[i]->addr);
+ exit(EXIT_FAILURE);
+ }
+ printf("\t.long\t_text - . + (%d)\t/* %s */\n",
+ (int)offset, table[i]->sym);
+ } else {
+ printf("\t.long\t%#x\t/* %s */\n",
+ (unsigned int)table[i]->addr, table[i]->sym);
}
- printf("\t.long\t%#x\t/* %s */\n", (int)offset, table[i]->sym);
}
printf("\n");
- output_label("kallsyms_relative_base");
- /* Provide proper symbols relocatability by their '_text' relativeness. */
- if (_text <= relative_base)
- printf("\tPTR\t_text + %#llx\n", relative_base - _text);
- else
- printf("\tPTR\t_text - %#llx\n", _text - relative_base);
- printf("\n");
-
sort_symbols_by_name();
output_label("kallsyms_seqs_of_names");
for (i = 0; i < table_cnt; i++)
@@ -701,22 +682,12 @@ static void sort_symbols(void)
qsort(table, table_cnt, sizeof(table[0]), compare_symbols);
}
-/* find the minimum non-absolute symbol address */
-static void record_relative_base(void)
-{
- /*
- * The table is sorted by address.
- * Take the first symbol value.
- */
- if (table_cnt)
- relative_base = table[0]->addr;
-}
-
int main(int argc, char **argv)
{
while (1) {
static const struct option long_options[] = {
{"all-symbols", no_argument, &all_symbols, 1},
+ {"pc-relative", no_argument, &pc_relative, 1},
{},
};
@@ -734,7 +705,6 @@ int main(int argc, char **argv)
read_map(argv[optind]);
shrink_table();
sort_symbols();
- record_relative_base();
optimize_token_table();
write_src();
diff --git a/scripts/kconfig/Makefile b/scripts/kconfig/Makefile
index fb50bd4f4103..5baf1c44ffa2 100644
--- a/scripts/kconfig/Makefile
+++ b/scripts/kconfig/Makefile
@@ -201,7 +201,7 @@ $(addprefix $(obj)/, mconf.o $(lxdialog)): | $(obj)/mconf-cflags
# qconf: Used for the xconfig target based on Qt
hostprogs += qconf
qconf-cxxobjs := qconf.o qconf-moc.o
-qconf-objs := images.o $(common-objs)
+qconf-objs := $(common-objs)
HOSTLDLIBS_qconf = $(call read-file, $(obj)/qconf-libs)
HOSTCXXFLAGS_qconf.o = -std=c++11 -fPIC $(call read-file, $(obj)/qconf-cflags)
@@ -219,7 +219,7 @@ targets += qconf-moc.cc
# gconf: Used for the gconfig target based on GTK+
hostprogs += gconf
-gconf-objs := gconf.o images.o $(common-objs)
+gconf-objs := gconf.o $(common-objs)
HOSTLDLIBS_gconf = $(call read-file, $(obj)/gconf-libs)
HOSTCFLAGS_gconf.o = $(call read-file, $(obj)/gconf-cflags)
diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c
index 8b164ccfa008..9f8586cb8a3e 100644
--- a/scripts/kconfig/gconf.c
+++ b/scripts/kconfig/gconf.c
@@ -5,7 +5,6 @@
#include <stdlib.h>
#include "lkc.h"
-#include "images.h"
#include <gtk/gtk.h>
@@ -951,12 +950,24 @@ static void fixup_rootmenu(struct menu *menu)
}
/* Main Window Initialization */
-static void replace_button_icon(GtkWidget *widget, const char * const xpm[])
+static void replace_button_icon(GtkWidget *widget, const char *filename)
{
GdkPixbuf *pixbuf;
GtkWidget *image;
+ GError *err = NULL;
+
+ char *env = getenv(SRCTREE);
+ gchar *path = g_strconcat(env ? env : g_get_current_dir(), "/scripts/kconfig/icons/", filename, NULL);
+
+ pixbuf = gdk_pixbuf_new_from_file(path, &err);
+ g_free(path);
+
+ if (err) {
+ g_warning("Failed to load icon %s: %s", filename, err->message);
+ g_error_free(err);
+ return;
+ }
- pixbuf = gdk_pixbuf_new_from_xpm_data((const char **)xpm);
image = gtk_image_new_from_pixbuf(pixbuf);
g_object_unref(pixbuf);
@@ -1078,17 +1089,17 @@ static void init_main_window(const gchar *glade_file)
single_btn = GTK_WIDGET(gtk_builder_get_object(builder, "button4"));
g_signal_connect(single_btn, "clicked",
G_CALLBACK(on_single_clicked), NULL);
- replace_button_icon(single_btn, xpm_single_view);
+ replace_button_icon(single_btn, "single_view.xpm");
split_btn = GTK_WIDGET(gtk_builder_get_object(builder, "button5"));
g_signal_connect(split_btn, "clicked",
G_CALLBACK(on_split_clicked), NULL);
- replace_button_icon(split_btn, xpm_split_view);
+ replace_button_icon(split_btn, "split_view.xpm");
full_btn = GTK_WIDGET(gtk_builder_get_object(builder, "button6"));
g_signal_connect(full_btn, "clicked",
G_CALLBACK(on_full_clicked), NULL);
- replace_button_icon(full_btn, xpm_tree_view);
+ replace_button_icon(full_btn, "tree_view.xpm");
widget = GTK_WIDGET(gtk_builder_get_object(builder, "button7"));
g_signal_connect(widget, "clicked",
@@ -1269,7 +1280,17 @@ static void init_right_tree(void)
g_signal_connect(G_OBJECT(renderer), "edited",
G_CALLBACK(renderer_edited), tree2_w);
- pix_menu = gdk_pixbuf_new_from_xpm_data((const char **)xpm_menu);
+ char *env = getenv(SRCTREE);
+ gchar *path = g_strconcat(env ? env : g_get_current_dir(), "/scripts/kconfig/icons/menu.xpm", NULL);
+ GError *err = NULL;
+
+ pix_menu = gdk_pixbuf_new_from_file(path, &err);
+ g_free(path);
+
+ if (err) {
+ g_warning("Failed to load menu icon: %s", err->message);
+ g_error_free(err);
+ }
for (i = 0; i < COL_VALUE; i++) {
column = gtk_tree_view_get_column(view, i);
diff --git a/scripts/kconfig/icons/back.xpm b/scripts/kconfig/icons/back.xpm
new file mode 100644
index 000000000000..2a4c30127608
--- /dev/null
+++ b/scripts/kconfig/icons/back.xpm
@@ -0,0 +1,29 @@
+/* XPM */
+static char * back_xpm[] = {
+"22 22 3 1",
+". c None",
+"# c #000083",
+"a c #838183",
+"......................",
+"......................",
+"......................",
+"......................",
+"......................",
+"...........######a....",
+"..#......##########...",
+"..##...####......##a..",
+"..###.###.........##..",
+"..######..........##..",
+"..#####...........##..",
+"..######..........##..",
+"..#######.........##..",
+"..########.......##a..",
+"...............a###...",
+"...............###....",
+"......................",
+"......................",
+"......................",
+"......................",
+"......................",
+"......................"
+};
diff --git a/scripts/kconfig/icons/choice_no.xpm b/scripts/kconfig/icons/choice_no.xpm
new file mode 100644
index 000000000000..306e314ed9c6
--- /dev/null
+++ b/scripts/kconfig/icons/choice_no.xpm
@@ -0,0 +1,18 @@
+/* XPM */
+static char * choice_no_xpm[] = {
+"12 12 2 1",
+" c white",
+". c black",
+" ",
+" .... ",
+" .. .. ",
+" . . ",
+" . . ",
+" . . ",
+" . . ",
+" . . ",
+" . . ",
+" .. .. ",
+" .... ",
+" "
+};
diff --git a/scripts/kconfig/icons/choice_yes.xpm b/scripts/kconfig/icons/choice_yes.xpm
new file mode 100644
index 000000000000..edeb91067379
--- /dev/null
+++ b/scripts/kconfig/icons/choice_yes.xpm
@@ -0,0 +1,18 @@
+/* XPM */
+static char * choice_yes_xpm[] = {
+"12 12 2 1",
+" c white",
+". c black",
+" ",
+" .... ",
+" .. .. ",
+" . . ",
+" . .. . ",
+" . .... . ",
+" . .... . ",
+" . .. . ",
+" . . ",
+" .. .. ",
+" .... ",
+" "
+};
diff --git a/scripts/kconfig/icons/load.xpm b/scripts/kconfig/icons/load.xpm
new file mode 100644
index 000000000000..8c2d8725d1ef
--- /dev/null
+++ b/scripts/kconfig/icons/load.xpm
@@ -0,0 +1,31 @@
+/* XPM */
+static char * load_xpm[] = {
+"22 22 5 1",
+". c None",
+"# c #000000",
+"c c #838100",
+"a c #ffff00",
+"b c #ffffff",
+"......................",
+"......................",
+"......................",
+"............####....#.",
+"...........#....##.##.",
+"..................###.",
+".................####.",
+".####...........#####.",
+"#abab##########.......",
+"#babababababab#.......",
+"#ababababababa#.......",
+"#babababababab#.......",
+"#ababab###############",
+"#babab##cccccccccccc##",
+"#abab##cccccccccccc##.",
+"#bab##cccccccccccc##..",
+"#ab##cccccccccccc##...",
+"#b##cccccccccccc##....",
+"###cccccccccccc##.....",
+"##cccccccccccc##......",
+"###############.......",
+"......................"
+};
diff --git a/scripts/kconfig/icons/menu.xpm b/scripts/kconfig/icons/menu.xpm
new file mode 100644
index 000000000000..8ae1b74b3c0c
--- /dev/null
+++ b/scripts/kconfig/icons/menu.xpm
@@ -0,0 +1,18 @@
+/* XPM */
+static char * menu_xpm[] = {
+"12 12 2 1",
+" c white",
+". c black",
+" ",
+" .......... ",
+" . . ",
+" . .. . ",
+" . .... . ",
+" . ...... . ",
+" . ...... . ",
+" . .... . ",
+" . .. . ",
+" . . ",
+" .......... ",
+" "
+};
diff --git a/scripts/kconfig/icons/menuback.xpm b/scripts/kconfig/icons/menuback.xpm
new file mode 100644
index 000000000000..f988c2c323c3
--- /dev/null
+++ b/scripts/kconfig/icons/menuback.xpm
@@ -0,0 +1,18 @@
+/* XPM */
+static char * menuback_xpm[] = {
+"12 12 2 1",
+" c white",
+". c black",
+" ",
+" .......... ",
+" . . ",
+" . .. . ",
+" . .... . ",
+" . ...... . ",
+" . ...... . ",
+" . .... . ",
+" . .. . ",
+" . . ",
+" .......... ",
+" "
+};
diff --git a/scripts/kconfig/icons/save.xpm b/scripts/kconfig/icons/save.xpm
new file mode 100644
index 000000000000..f8be53d83b40
--- /dev/null
+++ b/scripts/kconfig/icons/save.xpm
@@ -0,0 +1,31 @@
+/* XPM */
+static char * save_xpm[] = {
+"22 22 5 1",
+". c None",
+"# c #000000",
+"a c #838100",
+"b c #c5c2c5",
+"c c #cdb6d5",
+"......................",
+".####################.",
+".#aa#bbbbbbbbbbbb#bb#.",
+".#aa#bbbbbbbbbbbb#bb#.",
+".#aa#bbbbbbbbbcbb####.",
+".#aa#bbbccbbbbbbb#aa#.",
+".#aa#bbbccbbbbbbb#aa#.",
+".#aa#bbbbbbbbbbbb#aa#.",
+".#aa#bbbbbbbbbbbb#aa#.",
+".#aa#bbbbbbbbbbbb#aa#.",
+".#aa#bbbbbbbbbbbb#aa#.",
+".#aaa############aaa#.",
+".#aaaaaaaaaaaaaaaaaa#.",
+".#aaaaaaaaaaaaaaaaaa#.",
+".#aaa#############aa#.",
+".#aaa#########bbb#aa#.",
+".#aaa#########bbb#aa#.",
+".#aaa#########bbb#aa#.",
+".#aaa#########bbb#aa#.",
+".#aaa#########bbb#aa#.",
+"..##################..",
+"......................"
+};
diff --git a/scripts/kconfig/icons/single_view.xpm b/scripts/kconfig/icons/single_view.xpm
new file mode 100644
index 000000000000..33c3b239dc8e
--- /dev/null
+++ b/scripts/kconfig/icons/single_view.xpm
@@ -0,0 +1,28 @@
+/* XPM */
+static char * single_view_xpm[] = {
+"22 22 2 1",
+". c None",
+"# c #000000",
+"......................",
+"......................",
+"..........#...........",
+"..........#...........",
+"..........#...........",
+"..........#...........",
+"..........#...........",
+"..........#...........",
+"..........#...........",
+"..........#...........",
+"..........#...........",
+"..........#...........",
+"..........#...........",
+"..........#...........",
+"..........#...........",
+"..........#...........",
+"..........#...........",
+"..........#...........",
+"..........#...........",
+"..........#...........",
+"......................",
+"......................"
+};
diff --git a/scripts/kconfig/icons/split_view.xpm b/scripts/kconfig/icons/split_view.xpm
new file mode 100644
index 000000000000..09e22246d936
--- /dev/null
+++ b/scripts/kconfig/icons/split_view.xpm
@@ -0,0 +1,28 @@
+/* XPM */
+static char * split_view_xpm[] = {
+"22 22 2 1",
+". c None",
+"# c #000000",
+"......................",
+"......................",
+"......#......#........",
+"......#......#........",
+"......#......#........",
+"......#......#........",
+"......#......#........",
+"......#......#........",
+"......#......#........",
+"......#......#........",
+"......#......#........",
+"......#......#........",
+"......#......#........",
+"......#......#........",
+"......#......#........",
+"......#......#........",
+"......#......#........",
+"......#......#........",
+"......#......#........",
+"......#......#........",
+"......................",
+"......................"
+};
diff --git a/scripts/kconfig/icons/symbol_mod.xpm b/scripts/kconfig/icons/symbol_mod.xpm
new file mode 100644
index 000000000000..769465fcb0ce
--- /dev/null
+++ b/scripts/kconfig/icons/symbol_mod.xpm
@@ -0,0 +1,18 @@
+/* XPM */
+static char * symbol_mod_xpm[] = {
+"12 12 2 1",
+" c white",
+". c black",
+" ",
+" .......... ",
+" . . ",
+" . . ",
+" . .. . ",
+" . .... . ",
+" . .... . ",
+" . .. . ",
+" . . ",
+" . . ",
+" .......... ",
+" "
+};
diff --git a/scripts/kconfig/icons/symbol_no.xpm b/scripts/kconfig/icons/symbol_no.xpm
new file mode 100644
index 000000000000..e4e9d46c9aca
--- /dev/null
+++ b/scripts/kconfig/icons/symbol_no.xpm
@@ -0,0 +1,18 @@
+/* XPM */
+static char * symbol_no_xpm[] = {
+"12 12 2 1",
+" c white",
+". c black",
+" ",
+" .......... ",
+" . . ",
+" . . ",
+" . . ",
+" . . ",
+" . . ",
+" . . ",
+" . . ",
+" . . ",
+" .......... ",
+" "
+};
diff --git a/scripts/kconfig/icons/symbol_yes.xpm b/scripts/kconfig/icons/symbol_yes.xpm
new file mode 100644
index 000000000000..dab7e10ae7a9
--- /dev/null
+++ b/scripts/kconfig/icons/symbol_yes.xpm
@@ -0,0 +1,18 @@
+/* XPM */
+static char * symbol_yes_xpm[] = {
+"12 12 2 1",
+" c white",
+". c black",
+" ",
+" .......... ",
+" . . ",
+" . . ",
+" . . . ",
+" . .. . ",
+" . . .. . ",
+" . .... . ",
+" . .. . ",
+" . . ",
+" .......... ",
+" "
+};
diff --git a/scripts/kconfig/icons/tree_view.xpm b/scripts/kconfig/icons/tree_view.xpm
new file mode 100644
index 000000000000..290835b802eb
--- /dev/null
+++ b/scripts/kconfig/icons/tree_view.xpm
@@ -0,0 +1,28 @@
+/* XPM */
+static char * tree_view_xpm[] = {
+"22 22 2 1",
+". c None",
+"# c #000000",
+"......................",
+"......................",
+"......#...............",
+"......#...............",
+"......#...............",
+"......#...............",
+"......#...............",
+"......########........",
+"......#...............",
+"......#...............",
+"......#...............",
+"......#...............",
+"......#...............",
+"......########........",
+"......#...............",
+"......#...............",
+"......#...............",
+"......#...............",
+"......#...............",
+"......########........",
+"......................",
+"......................"
+};
diff --git a/scripts/kconfig/images.c b/scripts/kconfig/images.c
deleted file mode 100644
index 2f9afffa5d79..000000000000
--- a/scripts/kconfig/images.c
+++ /dev/null
@@ -1,328 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
- */
-
-#include "images.h"
-
-const char * const xpm_load[] = {
-"22 22 5 1",
-". c None",
-"# c #000000",
-"c c #838100",
-"a c #ffff00",
-"b c #ffffff",
-"......................",
-"......................",
-"......................",
-"............####....#.",
-"...........#....##.##.",
-"..................###.",
-".................####.",
-".####...........#####.",
-"#abab##########.......",
-"#babababababab#.......",
-"#ababababababa#.......",
-"#babababababab#.......",
-"#ababab###############",
-"#babab##cccccccccccc##",
-"#abab##cccccccccccc##.",
-"#bab##cccccccccccc##..",
-"#ab##cccccccccccc##...",
-"#b##cccccccccccc##....",
-"###cccccccccccc##.....",
-"##cccccccccccc##......",
-"###############.......",
-"......................"};
-
-const char * const xpm_save[] = {
-"22 22 5 1",
-". c None",
-"# c #000000",
-"a c #838100",
-"b c #c5c2c5",
-"c c #cdb6d5",
-"......................",
-".####################.",
-".#aa#bbbbbbbbbbbb#bb#.",
-".#aa#bbbbbbbbbbbb#bb#.",
-".#aa#bbbbbbbbbcbb####.",
-".#aa#bbbccbbbbbbb#aa#.",
-".#aa#bbbccbbbbbbb#aa#.",
-".#aa#bbbbbbbbbbbb#aa#.",
-".#aa#bbbbbbbbbbbb#aa#.",
-".#aa#bbbbbbbbbbbb#aa#.",
-".#aa#bbbbbbbbbbbb#aa#.",
-".#aaa############aaa#.",
-".#aaaaaaaaaaaaaaaaaa#.",
-".#aaaaaaaaaaaaaaaaaa#.",
-".#aaa#############aa#.",
-".#aaa#########bbb#aa#.",
-".#aaa#########bbb#aa#.",
-".#aaa#########bbb#aa#.",
-".#aaa#########bbb#aa#.",
-".#aaa#########bbb#aa#.",
-"..##################..",
-"......................"};
-
-const char * const xpm_back[] = {
-"22 22 3 1",
-". c None",
-"# c #000083",
-"a c #838183",
-"......................",
-"......................",
-"......................",
-"......................",
-"......................",
-"...........######a....",
-"..#......##########...",
-"..##...####......##a..",
-"..###.###.........##..",
-"..######..........##..",
-"..#####...........##..",
-"..######..........##..",
-"..#######.........##..",
-"..########.......##a..",
-"...............a###...",
-"...............###....",
-"......................",
-"......................",
-"......................",
-"......................",
-"......................",
-"......................"};
-
-const char * const xpm_tree_view[] = {
-"22 22 2 1",
-". c None",
-"# c #000000",
-"......................",
-"......................",
-"......#...............",
-"......#...............",
-"......#...............",
-"......#...............",
-"......#...............",
-"......########........",
-"......#...............",
-"......#...............",
-"......#...............",
-"......#...............",
-"......#...............",
-"......########........",
-"......#...............",
-"......#...............",
-"......#...............",
-"......#...............",
-"......#...............",
-"......########........",
-"......................",
-"......................"};
-
-const char * const xpm_single_view[] = {
-"22 22 2 1",
-". c None",
-"# c #000000",
-"......................",
-"......................",
-"..........#...........",
-"..........#...........",
-"..........#...........",
-"..........#...........",
-"..........#...........",
-"..........#...........",
-"..........#...........",
-"..........#...........",
-"..........#...........",
-"..........#...........",
-"..........#...........",
-"..........#...........",
-"..........#...........",
-"..........#...........",
-"..........#...........",
-"..........#...........",
-"..........#...........",
-"..........#...........",
-"......................",
-"......................"};
-
-const char * const xpm_split_view[] = {
-"22 22 2 1",
-". c None",
-"# c #000000",
-"......................",
-"......................",
-"......#......#........",
-"......#......#........",
-"......#......#........",
-"......#......#........",
-"......#......#........",
-"......#......#........",
-"......#......#........",
-"......#......#........",
-"......#......#........",
-"......#......#........",
-"......#......#........",
-"......#......#........",
-"......#......#........",
-"......#......#........",
-"......#......#........",
-"......#......#........",
-"......#......#........",
-"......#......#........",
-"......................",
-"......................"};
-
-const char * const xpm_symbol_no[] = {
-"12 12 2 1",
-" c white",
-". c black",
-" ",
-" .......... ",
-" . . ",
-" . . ",
-" . . ",
-" . . ",
-" . . ",
-" . . ",
-" . . ",
-" . . ",
-" .......... ",
-" "};
-
-const char * const xpm_symbol_mod[] = {
-"12 12 2 1",
-" c white",
-". c black",
-" ",
-" .......... ",
-" . . ",
-" . . ",
-" . .. . ",
-" . .... . ",
-" . .... . ",
-" . .. . ",
-" . . ",
-" . . ",
-" .......... ",
-" "};
-
-const char * const xpm_symbol_yes[] = {
-"12 12 2 1",
-" c white",
-". c black",
-" ",
-" .......... ",
-" . . ",
-" . . ",
-" . . . ",
-" . .. . ",
-" . . .. . ",
-" . .... . ",
-" . .. . ",
-" . . ",
-" .......... ",
-" "};
-
-const char * const xpm_choice_no[] = {
-"12 12 2 1",
-" c white",
-". c black",
-" ",
-" .... ",
-" .. .. ",
-" . . ",
-" . . ",
-" . . ",
-" . . ",
-" . . ",
-" . . ",
-" .. .. ",
-" .... ",
-" "};
-
-const char * const xpm_choice_yes[] = {
-"12 12 2 1",
-" c white",
-". c black",
-" ",
-" .... ",
-" .. .. ",
-" . . ",
-" . .. . ",
-" . .... . ",
-" . .... . ",
-" . .. . ",
-" . . ",
-" .. .. ",
-" .... ",
-" "};
-
-const char * const xpm_menu[] = {
-"12 12 2 1",
-" c white",
-". c black",
-" ",
-" .......... ",
-" . . ",
-" . .. . ",
-" . .... . ",
-" . ...... . ",
-" . ...... . ",
-" . .... . ",
-" . .. . ",
-" . . ",
-" .......... ",
-" "};
-
-const char * const xpm_menu_inv[] = {
-"12 12 2 1",
-" c white",
-". c black",
-" ",
-" .......... ",
-" .......... ",
-" .. ...... ",
-" .. .... ",
-" .. .. ",
-" .. .. ",
-" .. .... ",
-" .. ...... ",
-" .......... ",
-" .......... ",
-" "};
-
-const char * const xpm_menuback[] = {
-"12 12 2 1",
-" c white",
-". c black",
-" ",
-" .......... ",
-" . . ",
-" . .. . ",
-" . .... . ",
-" . ...... . ",
-" . ...... . ",
-" . .... . ",
-" . .. . ",
-" . . ",
-" .......... ",
-" "};
-
-const char * const xpm_void[] = {
-"12 12 2 1",
-" c white",
-". c black",
-" ",
-" ",
-" ",
-" ",
-" ",
-" ",
-" ",
-" ",
-" ",
-" ",
-" ",
-" "};
diff --git a/scripts/kconfig/images.h b/scripts/kconfig/images.h
deleted file mode 100644
index 7212dec2006c..000000000000
--- a/scripts/kconfig/images.h
+++ /dev/null
@@ -1,33 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/*
- * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
- */
-
-#ifndef IMAGES_H
-#define IMAGES_H
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-extern const char * const xpm_load[];
-extern const char * const xpm_save[];
-extern const char * const xpm_back[];
-extern const char * const xpm_tree_view[];
-extern const char * const xpm_single_view[];
-extern const char * const xpm_split_view[];
-extern const char * const xpm_symbol_no[];
-extern const char * const xpm_symbol_mod[];
-extern const char * const xpm_symbol_yes[];
-extern const char * const xpm_choice_no[];
-extern const char * const xpm_choice_yes[];
-extern const char * const xpm_menu[];
-extern const char * const xpm_menu_inv[];
-extern const char * const xpm_menuback[];
-extern const char * const xpm_void[];
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* IMAGES_H */
diff --git a/scripts/kconfig/lexer.l b/scripts/kconfig/lexer.l
index 6d2c92c6095d..a6155422b4a6 100644
--- a/scripts/kconfig/lexer.l
+++ b/scripts/kconfig/lexer.l
@@ -402,7 +402,7 @@ void zconf_initscan(const char *name)
exit(1);
}
- cur_filename = file_lookup(name);
+ cur_filename = file_lookup(name, NULL, 0);
yylineno = 1;
}
@@ -443,7 +443,7 @@ void zconf_nextfile(const char *name)
}
yylineno = 1;
- cur_filename = file_lookup(name);
+ cur_filename = file_lookup(name, cur_filename, cur_lineno);
}
static void zconf_endfile(void)
diff --git a/scripts/kconfig/lkc.h b/scripts/kconfig/lkc.h
index 56548efc14d7..7e6f6ca299cf 100644
--- a/scripts/kconfig/lkc.h
+++ b/scripts/kconfig/lkc.h
@@ -51,7 +51,8 @@ static inline void xfwrite(const void *str, size_t len, size_t count, FILE *out)
}
/* util.c */
-const char *file_lookup(const char *name);
+const char *file_lookup(const char *name,
+ const char *parent_name, int parent_lineno);
/* lexer.l */
int yylex(void);
@@ -82,7 +83,7 @@ void menu_warn(const struct menu *menu, const char *fmt, ...);
struct menu *menu_add_menu(void);
void menu_end_menu(void);
void menu_add_entry(struct symbol *sym, enum menu_type type);
-void menu_add_dep(struct expr *dep);
+void menu_add_dep(struct expr *dep, struct expr *cond);
void menu_add_visibility(struct expr *dep);
struct property *menu_add_prompt(enum prop_type type, const char *prompt,
struct expr *dep);
diff --git a/scripts/kconfig/menu.c b/scripts/kconfig/menu.c
index 0f1a6513987c..b2d8d4e11e07 100644
--- a/scripts/kconfig/menu.c
+++ b/scripts/kconfig/menu.c
@@ -127,8 +127,18 @@ static struct expr *rewrite_m(struct expr *e)
return e;
}
-void menu_add_dep(struct expr *dep)
+void menu_add_dep(struct expr *dep, struct expr *cond)
{
+ if (cond) {
+ /*
+ * We have "depends on X if Y" and we want:
+ * Y != n --> X
+ * Y == n --> y
+ * That simplifies to: (X || (Y == n))
+ */
+ dep = expr_alloc_or(dep,
+ expr_trans_compare(cond, E_EQUAL, &symbol_no));
+ }
current_entry->dep = expr_alloc_and(current_entry->dep, dep);
}
diff --git a/scripts/kconfig/merge_config.sh b/scripts/kconfig/merge_config.sh
index 79c09b378be8..f08e0863b712 100755
--- a/scripts/kconfig/merge_config.sh
+++ b/scripts/kconfig/merge_config.sh
@@ -16,8 +16,8 @@
set -e
clean_up() {
- rm -f $TMP_FILE
- rm -f $MERGE_FILE
+ rm -f "$TMP_FILE"
+ rm -f "$TMP_FILE.new"
}
usage() {
@@ -43,6 +43,10 @@ STRICT=false
CONFIG_PREFIX=${CONFIG_-CONFIG_}
WARNOVERRIDE=echo
+if [ -z "$AWK" ]; then
+ AWK=awk
+fi
+
while true; do
case $1 in
"-n")
@@ -117,11 +121,8 @@ if [ ! -r "$INITFILE" ]; then
fi
MERGE_LIST=$*
-SED_CONFIG_EXP1="s/^\(${CONFIG_PREFIX}[a-zA-Z0-9_]*\)=.*/\1/p"
-SED_CONFIG_EXP2="s/^# \(${CONFIG_PREFIX}[a-zA-Z0-9_]*\) is not set$/\1/p"
TMP_FILE=$(mktemp ./.tmp.config.XXXXXXXXXX)
-MERGE_FILE=$(mktemp ./.merge_tmp.config.XXXXXXXXXX)
echo "Using $INITFILE as base"
@@ -129,6 +130,8 @@ trap clean_up EXIT
cat $INITFILE > $TMP_FILE
+PROCESSED_FILES=""
+
# Merge files, printing warnings on overridden values
for ORIG_MERGE_FILE in $MERGE_LIST ; do
echo "Merging $ORIG_MERGE_FILE"
@@ -136,42 +139,134 @@ for ORIG_MERGE_FILE in $MERGE_LIST ; do
echo "The merge file '$ORIG_MERGE_FILE' does not exist. Exit." >&2
exit 1
fi
- cat $ORIG_MERGE_FILE > $MERGE_FILE
- CFG_LIST=$(sed -n -e "$SED_CONFIG_EXP1" -e "$SED_CONFIG_EXP2" $MERGE_FILE)
-
- for CFG in $CFG_LIST ; do
- grep -q -w $CFG $TMP_FILE || continue
- PREV_VAL=$(grep -w $CFG $TMP_FILE)
- NEW_VAL=$(grep -w $CFG $MERGE_FILE)
- BUILTIN_FLAG=false
- if [ "$BUILTIN" = "true" ] && [ "${NEW_VAL#CONFIG_*=}" = "m" ] && [ "${PREV_VAL#CONFIG_*=}" = "y" ]; then
- ${WARNOVERRIDE} Previous value: $PREV_VAL
- ${WARNOVERRIDE} New value: $NEW_VAL
- ${WARNOVERRIDE} -y passed, will not demote y to m
- ${WARNOVERRIDE}
- BUILTIN_FLAG=true
- elif [ "x$PREV_VAL" != "x$NEW_VAL" ] ; then
- ${WARNOVERRIDE} Value of $CFG is redefined by fragment $ORIG_MERGE_FILE:
- ${WARNOVERRIDE} Previous value: $PREV_VAL
- ${WARNOVERRIDE} New value: $NEW_VAL
- ${WARNOVERRIDE}
- if [ "$STRICT" = "true" ]; then
- STRICT_MODE_VIOLATED=true
- fi
- elif [ "$WARNREDUN" = "true" ]; then
- ${WARNOVERRIDE} Value of $CFG is redundant by fragment $ORIG_MERGE_FILE:
- fi
- if [ "$BUILTIN_FLAG" = "false" ]; then
- sed -i "/$CFG[ =]/d" $TMP_FILE
- else
- sed -i "/$CFG[ =]/d" $MERGE_FILE
- fi
- done
- # In case the previous file lacks a new line at the end
- echo >> $TMP_FILE
- cat $MERGE_FILE >> $TMP_FILE
-done
+ # Check for duplicate input files
+ case " $PROCESSED_FILES " in
+ *" $ORIG_MERGE_FILE "*)
+ ${WARNOVERRIDE} "WARNING: Input file provided multiple times: $ORIG_MERGE_FILE"
+ ;;
+ esac
+
+ # Use awk for single-pass processing instead of per-symbol grep/sed
+ if ! "$AWK" -v prefix="$CONFIG_PREFIX" \
+ -v warnoverride="$WARNOVERRIDE" \
+ -v strict="$STRICT" \
+ -v outfile="$TMP_FILE.new" \
+ -v builtin="$BUILTIN" \
+ -v warnredun="$WARNREDUN" '
+ BEGIN {
+ strict_violated = 0
+ cfg_regex = "^" prefix "[a-zA-Z0-9_]+"
+ notset_regex = "^# " prefix "[a-zA-Z0-9_]+ is not set$"
+ }
+
+ # Extract config name from a line, returns "" if not a config line
+ function get_cfg(line) {
+ if (match(line, cfg_regex)) {
+ return substr(line, RSTART, RLENGTH)
+ } else if (match(line, notset_regex)) {
+ # Extract CONFIG_FOO from "# CONFIG_FOO is not set"
+ sub(/^# /, "", line)
+ sub(/ is not set$/, "", line)
+ return line
+ }
+ return ""
+ }
+
+ function warn_builtin(cfg, prev, new) {
+ if (warnoverride == "true") return
+ print cfg ": -y passed, will not demote y to m"
+ print "Previous value: " prev
+ print "New value: " new
+ print ""
+ }
+
+ function warn_redefined(cfg, prev, new) {
+ if (warnoverride == "true") return
+ print "Value of " cfg " is redefined by fragment " mergefile ":"
+ print "Previous value: " prev
+ print "New value: " new
+ print ""
+ }
+
+ function warn_redundant(cfg) {
+ if (warnredun != "true" || warnoverride == "true") return
+ print "Value of " cfg " is redundant by fragment " mergefile ":"
+ }
+
+ # First pass: read merge file, store all lines and index
+ FILENAME == ARGV[1] {
+ mergefile = FILENAME
+ merge_lines[FNR] = $0
+ merge_total = FNR
+ cfg = get_cfg($0)
+ if (cfg != "") {
+ merge_cfg[cfg] = $0
+ merge_cfg_line[cfg] = FNR
+ }
+ next
+ }
+
+ # Second pass: process base file (TMP_FILE)
+ FILENAME == ARGV[2] {
+ cfg = get_cfg($0)
+
+ # Not a config or not in merge file - keep it
+ if (cfg == "" || !(cfg in merge_cfg)) {
+ print $0 >> outfile
+ next
+ }
+
+ prev_val = $0
+ new_val = merge_cfg[cfg]
+
+ # BUILTIN: do not demote y to m
+ if (builtin == "true" && new_val ~ /=m$/ && prev_val ~ /=y$/) {
+ warn_builtin(cfg, prev_val, new_val)
+ print $0 >> outfile
+ skip_merge[merge_cfg_line[cfg]] = 1
+ next
+ }
+
+ # Values equal - redundant
+ if (prev_val == new_val) {
+ warn_redundant(cfg)
+ next
+ }
+
+ # "=n" is the same as "is not set"
+ if (prev_val ~ /=n$/ && new_val ~ / is not set$/) {
+ print $0 >> outfile
+ next
+ }
+
+ # Values differ - redefined
+ warn_redefined(cfg, prev_val, new_val)
+ if (strict == "true") {
+ strict_violated = 1
+ }
+ }
+
+ END {
+ # Newline in case base file lacks trailing newline
+ print "" >> outfile
+ # Append merge file, skipping lines marked for builtin preservation
+ for (i = 1; i <= merge_total; i++) {
+ if (!(i in skip_merge)) {
+ print merge_lines[i] >> outfile
+ }
+ }
+ if (strict_violated) {
+ exit 1
+ }
+ }' \
+ "$ORIG_MERGE_FILE" "$TMP_FILE"; then
+ # awk exited non-zero, strict mode was violated
+ STRICT_MODE_VIOLATED=true
+ fi
+ mv "$TMP_FILE.new" "$TMP_FILE"
+ PROCESSED_FILES="$PROCESSED_FILES $ORIG_MERGE_FILE"
+done
if [ "$STRICT_MODE_VIOLATED" = "true" ]; then
echo "The fragment redefined a value and strict mode had been passed."
exit 1
@@ -198,16 +293,91 @@ fi
# allnoconfig: Fills in any missing symbols with # CONFIG_* is not set
make KCONFIG_ALLCONFIG=$TMP_FILE $OUTPUT_ARG $ALLTARGET
+# Check all specified config values took effect (might have missed-dependency issues)
+if ! "$AWK" -v prefix="$CONFIG_PREFIX" \
+ -v warnoverride="$WARNOVERRIDE" \
+ -v strict="$STRICT" \
+ -v warnredun="$WARNREDUN" '
+BEGIN {
+ strict_violated = 0
+ cfg_regex = "^" prefix "[a-zA-Z0-9_]+"
+ notset_regex = "^# " prefix "[a-zA-Z0-9_]+ is not set$"
+}
-# Check all specified config values took (might have missed-dependency issues)
-for CFG in $(sed -n -e "$SED_CONFIG_EXP1" -e "$SED_CONFIG_EXP2" $TMP_FILE); do
+# Extract config name from a line, returns "" if not a config line
+function get_cfg(line) {
+ if (match(line, cfg_regex)) {
+ return substr(line, RSTART, RLENGTH)
+ } else if (match(line, notset_regex)) {
+ # Extract CONFIG_FOO from "# CONFIG_FOO is not set"
+ sub(/^# /, "", line)
+ sub(/ is not set$/, "", line)
+ return line
+ }
+ return ""
+}
- REQUESTED_VAL=$(grep -w -e "$CFG" $TMP_FILE)
- ACTUAL_VAL=$(grep -w -e "$CFG" "$KCONFIG_CONFIG" || true)
- if [ "x$REQUESTED_VAL" != "x$ACTUAL_VAL" ] ; then
- echo "Value requested for $CFG not in final .config"
- echo "Requested value: $REQUESTED_VAL"
- echo "Actual value: $ACTUAL_VAL"
- echo ""
- fi
-done
+function warn_mismatch(cfg, merged, final) {
+ if (warnredun == "true") return
+ if (final == "" && !(merged ~ / is not set$/ || merged ~ /=n$/)) {
+ print "WARNING: Value requested for " cfg " not in final .config"
+ print "Requested value: " merged
+ print "Actual value: " final
+ } else if (final == "" && merged ~ / is not set$/) {
+ # not set, pass
+ } else if (merged == "" && final != "") {
+ print "WARNING: " cfg " not in merged config but added in final .config:"
+ print "Requested value: " merged
+ print "Actual value: " final
+ } else {
+ print "WARNING: " cfg " differs:"
+ print "Requested value: " merged
+ print "Actual value: " final
+ }
+}
+
+# First pass: read effective config file, store all lines
+FILENAME == ARGV[1] {
+ cfg = get_cfg($0)
+ if (cfg != "") {
+ config_cfg[cfg] = $0
+ }
+ next
+}
+
+# Second pass: process merged config and compare against effective config
+{
+ cfg = get_cfg($0)
+ if (cfg == "") next
+
+ # strip trailing comment
+ sub(/[[:space:]]+#.*/, "", $0)
+ merged_val = $0
+ final_val = config_cfg[cfg]
+
+ if (merged_val == final_val) next
+
+ if (merged_val ~ /=n$/ && final_val ~ / is not set$/) next
+ if (merged_val ~ /=n$/ && final_val == "") next
+
+ warn_mismatch(cfg, merged_val, final_val)
+
+ if (strict == "true") {
+ strict_violated = 1
+ }
+}
+
+END {
+ if (strict_violated) {
+ exit 1
+ }
+}' \
+"$KCONFIG_CONFIG" "$TMP_FILE"; then
+ # awk exited non-zero, strict mode was violated
+ STRICT_MODE_VIOLATED=true
+fi
+
+if [ "$STRICT" = "true" ] && [ "$STRICT_MODE_VIOLATED" = "true" ]; then
+ echo "Requested and effective config differ"
+ exit 1
+fi
diff --git a/scripts/kconfig/nconf-cfg.sh b/scripts/kconfig/nconf-cfg.sh
index a20290b1a37d..4d08453f9bdb 100755
--- a/scripts/kconfig/nconf-cfg.sh
+++ b/scripts/kconfig/nconf-cfg.sh
@@ -6,8 +6,9 @@ set -eu
cflags=$1
libs=$2
-PKG="ncursesw menuw panelw"
-PKG2="ncurses menu panel"
+# Keep library order for static linking (HOSTCC='cc -static')
+PKG="menuw panelw ncursesw"
+PKG2="menu panel ncurses"
if [ -n "$(command -v ${HOSTPKG_CONFIG})" ]; then
if ${HOSTPKG_CONFIG} --exists $PKG; then
@@ -28,19 +29,19 @@ fi
# find ncurses by pkg-config.)
if [ -f /usr/include/ncursesw/ncurses.h ]; then
echo -D_GNU_SOURCE -I/usr/include/ncursesw > ${cflags}
- echo -lncursesw -lmenuw -lpanelw > ${libs}
+ echo -lmenuw -lpanelw -lncursesw > ${libs}
exit 0
fi
if [ -f /usr/include/ncurses/ncurses.h ]; then
echo -D_GNU_SOURCE -I/usr/include/ncurses > ${cflags}
- echo -lncurses -lmenu -lpanel > ${libs}
+ echo -lmenu -lpanel -lncurses > ${libs}
exit 0
fi
if [ -f /usr/include/ncurses.h ]; then
echo -D_GNU_SOURCE > ${cflags}
- echo -lncurses -lmenu -lpanel > ${libs}
+ echo -lmenu -lpanel -lncurses > ${libs}
exit 0
fi
diff --git a/scripts/kconfig/parser.y b/scripts/kconfig/parser.y
index 49b79dde1725..5fb6f07b6ad2 100644
--- a/scripts/kconfig/parser.y
+++ b/scripts/kconfig/parser.y
@@ -159,14 +159,8 @@ config_stmt: config_entry_start config_option_list
yynerrs++;
}
- /*
- * If the same symbol appears twice in a choice block, the list
- * node would be added twice, leading to a broken linked list.
- * list_empty() ensures that this symbol has not yet added.
- */
- if (list_empty(&current_entry->sym->choice_link))
- list_add_tail(&current_entry->sym->choice_link,
- &current_choice->choice_members);
+ list_add_tail(&current_entry->sym->choice_link,
+ &current_choice->choice_members);
}
printd(DEBUG_PARSE, "%s:%d:endconfig\n", cur_filename, cur_lineno);
@@ -323,7 +317,7 @@ if_entry: T_IF expr T_EOL
{
printd(DEBUG_PARSE, "%s:%d:if\n", cur_filename, cur_lineno);
menu_add_entry(NULL, M_IF);
- menu_add_dep($2);
+ menu_add_dep($2, NULL);
$$ = menu_add_menu();
};
@@ -422,9 +416,9 @@ help: help_start T_HELPTEXT
/* depends option */
-depends: T_DEPENDS T_ON expr T_EOL
+depends: T_DEPENDS T_ON expr if_expr T_EOL
{
- menu_add_dep($3);
+ menu_add_dep($3, $4);
printd(DEBUG_PARSE, "%s:%d:depends on\n", cur_filename, cur_lineno);
};
@@ -546,11 +540,10 @@ static int choice_check_sanity(const struct menu *menu)
ret = -1;
}
- if (prop->menu != menu && prop->type == P_PROMPT &&
- prop->menu->parent != menu->parent) {
+ if (prop->menu != menu && prop->type == P_PROMPT) {
fprintf(stderr, "%s:%d: error: %s",
prop->filename, prop->lineno,
- "choice value has a prompt outside its choice group\n");
+ "choice value must not have a prompt in another entry\n");
ret = -1;
}
}
diff --git a/scripts/kconfig/qconf.cc b/scripts/kconfig/qconf.cc
index b84c9f2485d1..b02ead7a3f98 100644
--- a/scripts/kconfig/qconf.cc
+++ b/scripts/kconfig/qconf.cc
@@ -26,8 +26,6 @@
#include "lkc.h"
#include "qconf.h"
-#include "images.h"
-
static QApplication *configApp;
static ConfigSettings *configSettings;
@@ -1283,13 +1281,14 @@ ConfigMainWindow::ConfigMainWindow(void)
move(x.toInt(), y.toInt());
// set up icons
- ConfigItem::symbolYesIcon = QIcon(QPixmap(xpm_symbol_yes));
- ConfigItem::symbolModIcon = QIcon(QPixmap(xpm_symbol_mod));
- ConfigItem::symbolNoIcon = QIcon(QPixmap(xpm_symbol_no));
- ConfigItem::choiceYesIcon = QIcon(QPixmap(xpm_choice_yes));
- ConfigItem::choiceNoIcon = QIcon(QPixmap(xpm_choice_no));
- ConfigItem::menuIcon = QIcon(QPixmap(xpm_menu));
- ConfigItem::menubackIcon = QIcon(QPixmap(xpm_menuback));
+ QString iconsDir = QString(getenv(SRCTREE) ? getenv(SRCTREE) : QDir::currentPath()) + "/scripts/kconfig/icons/";
+ ConfigItem::symbolYesIcon = QIcon(QPixmap(iconsDir + "symbol_yes.xpm"));
+ ConfigItem::symbolModIcon = QIcon(QPixmap(iconsDir + "symbol_mod.xpm"));
+ ConfigItem::symbolNoIcon = QIcon(QPixmap(iconsDir + "symbol_no.xpm"));
+ ConfigItem::choiceYesIcon = QIcon(QPixmap(iconsDir + "choice_yes.xpm"));
+ ConfigItem::choiceNoIcon = QIcon(QPixmap(iconsDir + "choice_no.xpm"));
+ ConfigItem::menuIcon = QIcon(QPixmap(iconsDir + "menu.xpm"));
+ ConfigItem::menubackIcon = QIcon(QPixmap(iconsDir + "menuback.xpm"));
QWidget *widget = new QWidget(this);
setCentralWidget(widget);
@@ -1312,7 +1311,7 @@ ConfigMainWindow::ConfigMainWindow(void)
configList->setFocus();
- backAction = new QAction(QPixmap(xpm_back), "Back", this);
+ backAction = new QAction(QPixmap(iconsDir + "back.xpm"), "Back", this);
backAction->setShortcut(QKeySequence::Back);
connect(backAction, &QAction::triggered,
this, &ConfigMainWindow::goBack);
@@ -1322,12 +1321,12 @@ ConfigMainWindow::ConfigMainWindow(void)
connect(quitAction, &QAction::triggered,
this, &ConfigMainWindow::close);
- QAction *loadAction = new QAction(QPixmap(xpm_load), "&Open", this);
+ QAction *loadAction = new QAction(QPixmap(iconsDir + "load.xpm"), "&Open", this);
loadAction->setShortcut(QKeySequence::Open);
connect(loadAction, &QAction::triggered,
this, &ConfigMainWindow::loadConfig);
- saveAction = new QAction(QPixmap(xpm_save), "&Save", this);
+ saveAction = new QAction(QPixmap(iconsDir + "save.xpm"), "&Save", this);
saveAction->setShortcut(QKeySequence::Save);
connect(saveAction, &QAction::triggered,
this, &ConfigMainWindow::saveConfig);
@@ -1344,15 +1343,15 @@ ConfigMainWindow::ConfigMainWindow(void)
searchAction->setShortcut(QKeySequence::Find);
connect(searchAction, &QAction::triggered,
this, &ConfigMainWindow::searchConfig);
- singleViewAction = new QAction(QPixmap(xpm_single_view), "Single View", this);
+ singleViewAction = new QAction(QPixmap(iconsDir + "single_view.xpm"), "Single View", this);
singleViewAction->setCheckable(true);
connect(singleViewAction, &QAction::triggered,
this, &ConfigMainWindow::showSingleView);
- splitViewAction = new QAction(QPixmap(xpm_split_view), "Split View", this);
+ splitViewAction = new QAction(QPixmap(iconsDir + "split_view.xpm"), "Split View", this);
splitViewAction->setCheckable(true);
connect(splitViewAction, &QAction::triggered,
this, &ConfigMainWindow::showSplitView);
- fullViewAction = new QAction(QPixmap(xpm_tree_view), "Full View", this);
+ fullViewAction = new QAction(QPixmap(iconsDir + "tree_view.xpm"), "Full View", this);
fullViewAction->setCheckable(true);
connect(fullViewAction, &QAction::triggered,
this, &ConfigMainWindow::showFullView);
diff --git a/scripts/kconfig/streamline_config.pl b/scripts/kconfig/streamline_config.pl
index 8e23faab5d22..8677d1ca06a7 100755
--- a/scripts/kconfig/streamline_config.pl
+++ b/scripts/kconfig/streamline_config.pl
@@ -415,7 +415,7 @@ foreach my $module (keys(%modules)) {
}
} else {
# Most likely, someone has a custom (binary?) module loaded.
- print STDERR "$module config not found!!\n";
+ print STDERR "$module config not found!\n";
}
}
diff --git a/scripts/kconfig/tests/conditional_dep/Kconfig b/scripts/kconfig/tests/conditional_dep/Kconfig
new file mode 100644
index 000000000000..2015dfbce2b1
--- /dev/null
+++ b/scripts/kconfig/tests/conditional_dep/Kconfig
@@ -0,0 +1,32 @@
+# SPDX-License-Identifier: GPL-2.0
+# Test Kconfig file for conditional dependencies.
+
+# Enable module support for tristate testing
+config MODULES
+ bool "Enable loadable module support"
+ modules
+ default y
+
+config FOO
+ bool "FOO symbol"
+
+config BAR
+ bool "BAR symbol"
+
+config TEST_BASIC
+ bool "Test basic conditional dependency"
+ depends on FOO if BAR
+ default y
+
+config TEST_COMPLEX
+ bool "Test complex conditional dependency"
+ depends on (FOO && BAR) if (FOO || BAR)
+ default y
+
+config BAZ
+ tristate "BAZ symbol"
+
+config TEST_OPTIONAL
+ tristate "Test simple optional dependency"
+ depends on BAZ if BAZ
+ default y
diff --git a/scripts/kconfig/tests/conditional_dep/__init__.py b/scripts/kconfig/tests/conditional_dep/__init__.py
new file mode 100644
index 000000000000..ab16df6487ec
--- /dev/null
+++ b/scripts/kconfig/tests/conditional_dep/__init__.py
@@ -0,0 +1,14 @@
+# SPDX-License-Identifier: GPL-2.0
+"""
+Correctly handle conditional dependencies.
+"""
+
+def test(conf):
+ assert conf.oldconfig('test_config1') == 0
+ assert conf.config_matches('expected_config1')
+
+ assert conf.oldconfig('test_config2') == 0
+ assert conf.config_matches('expected_config2')
+
+ assert conf.oldconfig('test_config3') == 0
+ assert conf.config_matches('expected_config3')
diff --git a/scripts/kconfig/tests/conditional_dep/expected_config1 b/scripts/kconfig/tests/conditional_dep/expected_config1
new file mode 100644
index 000000000000..826ed7f541b8
--- /dev/null
+++ b/scripts/kconfig/tests/conditional_dep/expected_config1
@@ -0,0 +1,11 @@
+#
+# Automatically generated file; DO NOT EDIT.
+# Main menu
+#
+CONFIG_MODULES=y
+CONFIG_FOO=y
+CONFIG_BAR=y
+CONFIG_TEST_BASIC=y
+CONFIG_TEST_COMPLEX=y
+CONFIG_BAZ=m
+CONFIG_TEST_OPTIONAL=m
diff --git a/scripts/kconfig/tests/conditional_dep/expected_config2 b/scripts/kconfig/tests/conditional_dep/expected_config2
new file mode 100644
index 000000000000..10d2354f687f
--- /dev/null
+++ b/scripts/kconfig/tests/conditional_dep/expected_config2
@@ -0,0 +1,9 @@
+#
+# Automatically generated file; DO NOT EDIT.
+# Main menu
+#
+CONFIG_MODULES=y
+# CONFIG_FOO is not set
+CONFIG_BAR=y
+CONFIG_BAZ=y
+CONFIG_TEST_OPTIONAL=y
diff --git a/scripts/kconfig/tests/conditional_dep/expected_config3 b/scripts/kconfig/tests/conditional_dep/expected_config3
new file mode 100644
index 000000000000..b04fa6fdfeb4
--- /dev/null
+++ b/scripts/kconfig/tests/conditional_dep/expected_config3
@@ -0,0 +1,11 @@
+#
+# Automatically generated file; DO NOT EDIT.
+# Main menu
+#
+CONFIG_MODULES=y
+# CONFIG_FOO is not set
+# CONFIG_BAR is not set
+CONFIG_TEST_BASIC=y
+CONFIG_TEST_COMPLEX=y
+# CONFIG_BAZ is not set
+CONFIG_TEST_OPTIONAL=y
diff --git a/scripts/kconfig/tests/conditional_dep/test_config1 b/scripts/kconfig/tests/conditional_dep/test_config1
new file mode 100644
index 000000000000..9b05f3ce8a99
--- /dev/null
+++ b/scripts/kconfig/tests/conditional_dep/test_config1
@@ -0,0 +1,6 @@
+# Basic check that everything can be configured if selected.
+CONFIG_FOO=y
+CONFIG_BAR=y
+CONFIG_BAZ=m
+# Ensure that TEST_OPTIONAL=y with BAZ=m is converted to TEST_OPTIONAL=m
+CONFIG_TEST_OPTIONAL=y
diff --git a/scripts/kconfig/tests/conditional_dep/test_config2 b/scripts/kconfig/tests/conditional_dep/test_config2
new file mode 100644
index 000000000000..5e66d230a836
--- /dev/null
+++ b/scripts/kconfig/tests/conditional_dep/test_config2
@@ -0,0 +1,7 @@
+# If FOO is not selected, then TEST_BASIC should fail the conditional
+# dependency since BAR is set.
+# TEST_COMPLEX will fail dependency as it depends on both FOO and BAR
+# if either of those is selected.
+CONFIG_FOO=n
+CONFIG_BAR=y
+CONFIG_BAZ=y
diff --git a/scripts/kconfig/tests/conditional_dep/test_config3 b/scripts/kconfig/tests/conditional_dep/test_config3
new file mode 100644
index 000000000000..86304f3aa557
--- /dev/null
+++ b/scripts/kconfig/tests/conditional_dep/test_config3
@@ -0,0 +1,6 @@
+# If FOO is not selected, but BAR is also not selected, then TEST_BASIC
+# should pass since the dependency on FOO is conditional on BAR.
+# TEST_COMPLEX should be also set since neither FOO nor BAR are selected
+# so it has no dependencies.
+CONFIG_FOO=n
+CONFIG_BAR=n
diff --git a/scripts/kconfig/tests/err_repeated_inc/Kconfig b/scripts/kconfig/tests/err_repeated_inc/Kconfig
new file mode 100644
index 000000000000..09a88fd29cb5
--- /dev/null
+++ b/scripts/kconfig/tests/err_repeated_inc/Kconfig
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+source "Kconfig.inc1"
diff --git a/scripts/kconfig/tests/err_repeated_inc/Kconfig.inc1 b/scripts/kconfig/tests/err_repeated_inc/Kconfig.inc1
new file mode 100644
index 000000000000..495dc38314a1
--- /dev/null
+++ b/scripts/kconfig/tests/err_repeated_inc/Kconfig.inc1
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+source "Kconfig.inc2"
+source "Kconfig.inc3"
diff --git a/scripts/kconfig/tests/err_repeated_inc/Kconfig.inc2 b/scripts/kconfig/tests/err_repeated_inc/Kconfig.inc2
new file mode 100644
index 000000000000..2b630eec2e99
--- /dev/null
+++ b/scripts/kconfig/tests/err_repeated_inc/Kconfig.inc2
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+source "Kconfig.inc3"
diff --git a/scripts/kconfig/tests/err_repeated_inc/Kconfig.inc3 b/scripts/kconfig/tests/err_repeated_inc/Kconfig.inc3
new file mode 100644
index 000000000000..a4e40e534e6a
--- /dev/null
+++ b/scripts/kconfig/tests/err_repeated_inc/Kconfig.inc3
@@ -0,0 +1 @@
+# SPDX-License-Identifier: GPL-2.0-only
diff --git a/scripts/kconfig/tests/err_repeated_inc/__init__.py b/scripts/kconfig/tests/err_repeated_inc/__init__.py
new file mode 100644
index 000000000000..129d740a874b
--- /dev/null
+++ b/scripts/kconfig/tests/err_repeated_inc/__init__.py
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: GPL-2.0
+"""
+Detect repeated inclusion error.
+
+If repeated inclusion is detected, it should fail with error message.
+"""
+
+def test(conf):
+ assert conf.oldaskconfig() != 0
+ assert conf.stderr_contains('expected_stderr')
diff --git a/scripts/kconfig/tests/err_repeated_inc/expected_stderr b/scripts/kconfig/tests/err_repeated_inc/expected_stderr
new file mode 100644
index 000000000000..95d90d6a93c5
--- /dev/null
+++ b/scripts/kconfig/tests/err_repeated_inc/expected_stderr
@@ -0,0 +1,2 @@
+Kconfig.inc1:4: error: Repeated inclusion of Kconfig.inc3
+Kconfig.inc2:3: note: Location of first inclusion of Kconfig.inc3
diff --git a/scripts/kconfig/util.c b/scripts/kconfig/util.c
index 5cdcee144b58..0809aa061b6a 100644
--- a/scripts/kconfig/util.c
+++ b/scripts/kconfig/util.c
@@ -18,25 +18,50 @@ static HASHTABLE_DEFINE(file_hashtable, 1U << 11);
struct file {
struct hlist_node node;
+ struct {
+ const char *name;
+ int lineno;
+ } parent;
char name[];
};
+static void die_duplicated_include(struct file *file,
+ const char *parent, int lineno)
+{
+ fprintf(stderr,
+ "%s:%d: error: repeated inclusion of %s\n"
+ "%s:%d: note: location of first inclusion of %s\n",
+ parent, lineno, file->name,
+ file->parent.name, file->parent.lineno, file->name);
+ exit(1);
+}
+
/* file already present in list? If not add it */
-const char *file_lookup(const char *name)
+const char *file_lookup(const char *name,
+ const char *parent_name, int parent_lineno)
{
+ const char *parent = NULL;
struct file *file;
size_t len;
int hash = hash_str(name);
+ if (parent_name)
+ parent = file_lookup(parent_name, NULL, 0);
+
hash_for_each_possible(file_hashtable, file, node, hash)
- if (!strcmp(name, file->name))
- return file->name;
+ if (!strcmp(name, file->name)) {
+ if (!parent_name)
+ return file->name;
+ die_duplicated_include(file, parent, parent_lineno);
+ }
len = strlen(name);
file = xmalloc(sizeof(*file) + len + 1);
memset(file, 0, sizeof(*file));
memcpy(file->name, name, len);
file->name[len] = '\0';
+ file->parent.name = parent;
+ file->parent.lineno = parent_lineno;
hash_add(file_hashtable, &file->node, hash);
diff --git a/scripts/kernel-doc b/scripts/kernel-doc
index 3b6ef807791a..9cc1459ffcdb 120000
--- a/scripts/kernel-doc
+++ b/scripts/kernel-doc
@@ -1 +1 @@
-kernel-doc.py \ No newline at end of file
+../tools/docs/kernel-doc \ No newline at end of file
diff --git a/scripts/kernel-doc.pl b/scripts/kernel-doc.pl
deleted file mode 100755
index 5db23cbf4eb2..000000000000
--- a/scripts/kernel-doc.pl
+++ /dev/null
@@ -1,2439 +0,0 @@
-#!/usr/bin/env perl
-# SPDX-License-Identifier: GPL-2.0
-# vim: softtabstop=4
-
-use warnings;
-use strict;
-
-## Copyright (c) 1998 Michael Zucchi, All Rights Reserved ##
-## Copyright (C) 2000, 1 Tim Waugh <twaugh@redhat.com> ##
-## Copyright (C) 2001 Simon Huggins ##
-## Copyright (C) 2005-2012 Randy Dunlap ##
-## Copyright (C) 2012 Dan Luedtke ##
-## ##
-## #define enhancements by Armin Kuster <akuster@mvista.com> ##
-## Copyright (c) 2000 MontaVista Software, Inc. ##
-#
-# Copyright (C) 2022 Tomasz Warniełło (POD)
-
-use Pod::Usage qw/pod2usage/;
-
-=head1 NAME
-
-kernel-doc - Print formatted kernel documentation to stdout
-
-=head1 SYNOPSIS
-
- kernel-doc [-h] [-v] [-Werror] [-Wall] [-Wreturn] [-Wshort-desc[ription]] [-Wcontents-before-sections]
- [ -man |
- -rst [-enable-lineno] |
- -none
- ]
- [
- -export |
- -internal |
- [-function NAME] ... |
- [-nosymbol NAME] ...
- ]
- [-no-doc-sections]
- [-export-file FILE] ...
- FILE ...
-
-Run `kernel-doc -h` for details.
-
-=head1 DESCRIPTION
-
-Read C language source or header FILEs, extract embedded documentation comments,
-and print formatted documentation to standard output.
-
-The documentation comments are identified by the "/**" opening comment mark.
-
-See Documentation/doc-guide/kernel-doc.rst for the documentation comment syntax.
-
-=cut
-
-# more perldoc at the end of the file
-
-## init lots of data
-
-my $errors = 0;
-my $warnings = 0;
-my $anon_struct_union = 0;
-
-# match expressions used to find embedded type information
-my $type_constant = '\b``([^\`]+)``\b';
-my $type_constant2 = '\%([-_*\w]+)';
-my $type_func = '(\w+)\(\)';
-my $type_param = '\@(\w*((\.\w+)|(->\w+))*(\.\.\.)?)';
-my $type_param_ref = '([\!~\*]?)\@(\w*((\.\w+)|(->\w+))*(\.\.\.)?)';
-my $type_fp_param = '\@(\w+)\(\)'; # Special RST handling for func ptr params
-my $type_fp_param2 = '\@(\w+->\S+)\(\)'; # Special RST handling for structs with func ptr params
-my $type_env = '(\$\w+)';
-my $type_enum = '\&(enum\s*([_\w]+))';
-my $type_struct = '\&(struct\s*([_\w]+))';
-my $type_typedef = '\&(typedef\s*([_\w]+))';
-my $type_union = '\&(union\s*([_\w]+))';
-my $type_member = '\&([_\w]+)(\.|->)([_\w]+)';
-my $type_fallback = '\&([_\w]+)';
-my $type_member_func = $type_member . '\(\)';
-
-# Output conversion substitutions.
-# One for each output format
-
-# these are pretty rough
-my @highlights_man = (
- [$type_constant, "\$1"],
- [$type_constant2, "\$1"],
- [$type_func, "\\\\fB\$1\\\\fP"],
- [$type_enum, "\\\\fI\$1\\\\fP"],
- [$type_struct, "\\\\fI\$1\\\\fP"],
- [$type_typedef, "\\\\fI\$1\\\\fP"],
- [$type_union, "\\\\fI\$1\\\\fP"],
- [$type_param, "\\\\fI\$1\\\\fP"],
- [$type_param_ref, "\\\\fI\$1\$2\\\\fP"],
- [$type_member, "\\\\fI\$1\$2\$3\\\\fP"],
- [$type_fallback, "\\\\fI\$1\\\\fP"]
- );
-my $blankline_man = "";
-
-# rst-mode
-my @highlights_rst = (
- [$type_constant, "``\$1``"],
- [$type_constant2, "``\$1``"],
-
- # Note: need to escape () to avoid func matching later
- [$type_member_func, "\\:c\\:type\\:`\$1\$2\$3\\\\(\\\\) <\$1>`"],
- [$type_member, "\\:c\\:type\\:`\$1\$2\$3 <\$1>`"],
- [$type_fp_param, "**\$1\\\\(\\\\)**"],
- [$type_fp_param2, "**\$1\\\\(\\\\)**"],
- [$type_func, "\$1()"],
- [$type_enum, "\\:c\\:type\\:`\$1 <\$2>`"],
- [$type_struct, "\\:c\\:type\\:`\$1 <\$2>`"],
- [$type_typedef, "\\:c\\:type\\:`\$1 <\$2>`"],
- [$type_union, "\\:c\\:type\\:`\$1 <\$2>`"],
-
- # in rst this can refer to any type
- [$type_fallback, "\\:c\\:type\\:`\$1`"],
- [$type_param_ref, "**\$1\$2**"]
- );
-my $blankline_rst = "\n";
-
-# read arguments
-if ($#ARGV == -1) {
- pod2usage(
- -message => "No arguments!\n",
- -exitval => 1,
- -verbose => 99,
- -sections => 'SYNOPSIS',
- -output => \*STDERR,
- );
-}
-
-my $kernelversion;
-
-my $dohighlight = "";
-
-my $verbose = 0;
-my $Werror = 0;
-my $Wreturn = 0;
-my $Wshort_desc = 0;
-my $output_mode = "rst";
-my $output_preformatted = 0;
-my $no_doc_sections = 0;
-my $enable_lineno = 0;
-my @highlights = @highlights_rst;
-my $blankline = $blankline_rst;
-my $modulename = "Kernel API";
-
-use constant {
- OUTPUT_ALL => 0, # output all symbols and doc sections
- OUTPUT_INCLUDE => 1, # output only specified symbols
- OUTPUT_EXPORTED => 2, # output exported symbols
- OUTPUT_INTERNAL => 3, # output non-exported symbols
-};
-my $output_selection = OUTPUT_ALL;
-my $show_not_found = 0; # No longer used
-
-my @export_file_list;
-
-my @build_time;
-if (defined($ENV{'KBUILD_BUILD_TIMESTAMP'}) &&
- (my $seconds = `date -d "${ENV{'KBUILD_BUILD_TIMESTAMP'}}" +%s`) ne '') {
- @build_time = gmtime($seconds);
-} else {
- @build_time = localtime;
-}
-
-my $man_date = ('January', 'February', 'March', 'April', 'May', 'June',
- 'July', 'August', 'September', 'October',
- 'November', 'December')[$build_time[4]] .
- " " . ($build_time[5]+1900);
-
-# Essentially these are globals.
-# They probably want to be tidied up, made more localised or something.
-# CAVEAT EMPTOR! Some of the others I localised may not want to be, which
-# could cause "use of undefined value" or other bugs.
-my ($function, %function_table, %parametertypes, $declaration_purpose);
-my %nosymbol_table = ();
-my $declaration_start_line;
-my ($type, $declaration_name, $return_type);
-my ($newsection, $newcontents, $prototype, $brcount);
-
-if (defined($ENV{'KBUILD_VERBOSE'}) && $ENV{'KBUILD_VERBOSE'} =~ '1') {
- $verbose = 1;
-}
-
-if (defined($ENV{'KCFLAGS'})) {
- my $kcflags = "$ENV{'KCFLAGS'}";
-
- if ($kcflags =~ /(\s|^)-Werror(\s|$)/) {
- $Werror = 1;
- }
-}
-
-# reading this variable is for backwards compat just in case
-# someone was calling it with the variable from outside the
-# kernel's build system
-if (defined($ENV{'KDOC_WERROR'})) {
- $Werror = "$ENV{'KDOC_WERROR'}";
-}
-# other environment variables are converted to command-line
-# arguments in cmd_checkdoc in the build system
-
-# Generated docbook code is inserted in a template at a point where
-# docbook v3.1 requires a non-zero sequence of RefEntry's; see:
-# https://www.oasis-open.org/docbook/documentation/reference/html/refentry.html
-# We keep track of number of generated entries and generate a dummy
-# if needs be to ensure the expanded template can be postprocessed
-# into html.
-my $section_counter = 0;
-
-my $lineprefix="";
-
-# Parser states
-use constant {
- STATE_NORMAL => 0, # normal code
- STATE_NAME => 1, # looking for function name
- STATE_BODY_MAYBE => 2, # body - or maybe more description
- STATE_BODY => 3, # the body of the comment
- STATE_BODY_WITH_BLANK_LINE => 4, # the body, which has a blank line
- STATE_PROTO => 5, # scanning prototype
- STATE_DOCBLOCK => 6, # documentation block
- STATE_INLINE => 7, # gathering doc outside main block
-};
-my $state;
-my $leading_space;
-
-# Inline documentation state
-use constant {
- STATE_INLINE_NA => 0, # not applicable ($state != STATE_INLINE)
- STATE_INLINE_NAME => 1, # looking for member name (@foo:)
- STATE_INLINE_TEXT => 2, # looking for member documentation
- STATE_INLINE_END => 3, # done
- STATE_INLINE_ERROR => 4, # error - Comment without header was found.
- # Spit a warning as it's not
- # proper kernel-doc and ignore the rest.
-};
-my $inline_doc_state;
-
-#declaration types: can be
-# 'function', 'struct', 'union', 'enum', 'typedef'
-my $decl_type;
-
-# Name of the kernel-doc identifier for non-DOC markups
-my $identifier;
-
-my $doc_start = '^/\*\*\s*$'; # Allow whitespace at end of comment start.
-my $doc_end = '\*/';
-my $doc_com = '\s*\*\s*';
-my $doc_com_body = '\s*\* ?';
-my $doc_decl = $doc_com . '(\w+)';
-# @params and a strictly limited set of supported section names
-# Specifically:
-# Match @word:
-# @...:
-# @{section-name}:
-# while trying to not match literal block starts like "example::"
-#
-my $doc_sect = $doc_com .
- '\s*(\@[.\w]+|\@\.\.\.|description|context|returns?|notes?|examples?)\s*:([^:].*)?$';
-my $doc_content = $doc_com_body . '(.*)';
-my $doc_block = $doc_com . 'DOC:\s*(.*)?';
-my $doc_inline_start = '^\s*/\*\*\s*$';
-my $doc_inline_sect = '\s*\*\s*(@\s*[\w][\w\.]*\s*):(.*)';
-my $doc_inline_end = '^\s*\*/\s*$';
-my $doc_inline_oneline = '^\s*/\*\*\s*(@[\w\s]+):\s*(.*)\s*\*/\s*$';
-my $export_symbol = '^\s*EXPORT_SYMBOL(_GPL)?\s*\(\s*(\w+)\s*\)\s*;';
-my $export_symbol_ns = '^\s*EXPORT_SYMBOL_NS(_GPL)?\s*\(\s*(\w+)\s*,\s*"\S+"\)\s*;';
-my $function_pointer = qr{([^\(]*\(\*)\s*\)\s*\(([^\)]*)\)};
-my $attribute = qr{__attribute__\s*\(\([a-z0-9,_\*\s\(\)]*\)\)}i;
-
-my %parameterdescs;
-my %parameterdesc_start_lines;
-my @parameterlist;
-my %sections;
-my @sectionlist;
-my %section_start_lines;
-my $sectcheck;
-my $struct_actual;
-
-my $contents = "";
-my $new_start_line = 0;
-
-# the canonical section names. see also $doc_sect above.
-my $section_default = "Description"; # default section
-my $section_intro = "Introduction";
-my $section = $section_default;
-my $section_context = "Context";
-my $section_return = "Return";
-
-my $undescribed = "-- undescribed --";
-
-reset_state();
-
-while ($ARGV[0] =~ m/^--?(.*)/) {
- my $cmd = $1;
- shift @ARGV;
- if ($cmd eq "man") {
- $output_mode = "man";
- @highlights = @highlights_man;
- $blankline = $blankline_man;
- } elsif ($cmd eq "rst") {
- $output_mode = "rst";
- @highlights = @highlights_rst;
- $blankline = $blankline_rst;
- } elsif ($cmd eq "none") {
- $output_mode = "none";
- } elsif ($cmd eq "module") { # not needed for XML, inherits from calling document
- $modulename = shift @ARGV;
- } elsif ($cmd eq "function") { # to only output specific functions
- $output_selection = OUTPUT_INCLUDE;
- $function = shift @ARGV;
- $function_table{$function} = 1;
- } elsif ($cmd eq "nosymbol") { # Exclude specific symbols
- my $symbol = shift @ARGV;
- $nosymbol_table{$symbol} = 1;
- } elsif ($cmd eq "export") { # only exported symbols
- $output_selection = OUTPUT_EXPORTED;
- %function_table = ();
- } elsif ($cmd eq "internal") { # only non-exported symbols
- $output_selection = OUTPUT_INTERNAL;
- %function_table = ();
- } elsif ($cmd eq "export-file") {
- my $file = shift @ARGV;
- push(@export_file_list, $file);
- } elsif ($cmd eq "v") {
- $verbose = 1;
- } elsif ($cmd eq "Werror") {
- $Werror = 1;
- } elsif ($cmd eq "Wreturn") {
- $Wreturn = 1;
- } elsif ($cmd eq "Wshort-desc" or $cmd eq "Wshort-description") {
- $Wshort_desc = 1;
- } elsif ($cmd eq "Wall") {
- $Wreturn = 1;
- $Wshort_desc = 1;
- } elsif (($cmd eq "h") || ($cmd eq "help")) {
- pod2usage(-exitval => 0, -verbose => 2);
- } elsif ($cmd eq 'no-doc-sections') {
- $no_doc_sections = 1;
- } elsif ($cmd eq 'enable-lineno') {
- $enable_lineno = 1;
- } elsif ($cmd eq 'show-not-found') {
- $show_not_found = 1; # A no-op but don't fail
- } else {
- # Unknown argument
- pod2usage(
- -message => "Argument unknown!\n",
- -exitval => 1,
- -verbose => 99,
- -sections => 'SYNOPSIS',
- -output => \*STDERR,
- );
- }
- if ($#ARGV < 0){
- pod2usage(
- -message => "FILE argument missing\n",
- -exitval => 1,
- -verbose => 99,
- -sections => 'SYNOPSIS',
- -output => \*STDERR,
- );
- }
-}
-
-# continue execution near EOF;
-
-sub findprog($)
-{
- foreach(split(/:/, $ENV{PATH})) {
- return "$_/$_[0]" if(-x "$_/$_[0]");
- }
-}
-
-# get kernel version from env
-sub get_kernel_version() {
- my $version = 'unknown kernel version';
-
- if (defined($ENV{'KERNELVERSION'})) {
- $version = $ENV{'KERNELVERSION'};
- }
- return $version;
-}
-
-#
-sub print_lineno {
- my $lineno = shift;
- if ($enable_lineno && defined($lineno)) {
- print ".. LINENO " . $lineno . "\n";
- }
-}
-
-sub emit_warning {
- my $location = shift;
- my $msg = shift;
- print STDERR "$location: warning: $msg";
- ++$warnings;
-}
-##
-# dumps section contents to arrays/hashes intended for that purpose.
-#
-sub dump_section {
- my $file = shift;
- my $name = shift;
- my $contents = join "\n", @_;
-
- if ($name =~ m/$type_param/) {
- $name = $1;
- $parameterdescs{$name} = $contents;
- $sectcheck = $sectcheck . $name . " ";
- $parameterdesc_start_lines{$name} = $new_start_line;
- $new_start_line = 0;
- } elsif ($name eq "@\.\.\.") {
- $name = "...";
- $parameterdescs{$name} = $contents;
- $sectcheck = $sectcheck . $name . " ";
- $parameterdesc_start_lines{$name} = $new_start_line;
- $new_start_line = 0;
- } else {
- if (defined($sections{$name}) && ($sections{$name} ne "")) {
- # Only warn on user specified duplicate section names.
- if ($name ne $section_default) {
- emit_warning("${file}:$.", "duplicate section name '$name'\n");
- }
- $sections{$name} .= $contents;
- } else {
- $sections{$name} = $contents;
- push @sectionlist, $name;
- $section_start_lines{$name} = $new_start_line;
- $new_start_line = 0;
- }
- }
-}
-
-##
-# dump DOC: section after checking that it should go out
-#
-sub dump_doc_section {
- my $file = shift;
- my $name = shift;
- my $contents = join "\n", @_;
-
- if ($no_doc_sections) {
- return;
- }
-
- return if (defined($nosymbol_table{$name}));
-
- if (($output_selection == OUTPUT_ALL) ||
- (($output_selection == OUTPUT_INCLUDE) &&
- defined($function_table{$name})))
- {
- dump_section($file, $name, $contents);
- output_blockhead({'sectionlist' => \@sectionlist,
- 'sections' => \%sections,
- 'module' => $modulename,
- 'content-only' => ($output_selection != OUTPUT_ALL), });
- }
-}
-
-##
-# output function
-#
-# parameterdescs, a hash.
-# function => "function name"
-# parameterlist => @list of parameters
-# parameterdescs => %parameter descriptions
-# sectionlist => @list of sections
-# sections => %section descriptions
-#
-
-sub output_highlight {
- my $contents = join "\n",@_;
- my $line;
-
-# DEBUG
-# if (!defined $contents) {
-# use Carp;
-# confess "output_highlight got called with no args?\n";
-# }
-
-# print STDERR "contents b4:$contents\n";
- eval $dohighlight;
- die $@ if $@;
-# print STDERR "contents af:$contents\n";
-
- foreach $line (split "\n", $contents) {
- if (! $output_preformatted) {
- $line =~ s/^\s*//;
- }
- if ($line eq ""){
- if (! $output_preformatted) {
- print $lineprefix, $blankline;
- }
- } else {
- if ($output_mode eq "man" && substr($line, 0, 1) eq ".") {
- print "\\&$line";
- } else {
- print $lineprefix, $line;
- }
- }
- print "\n";
- }
-}
-
-##
-# output function in man
-sub output_function_man(%) {
- my %args = %{$_[0]};
- my ($parameter, $section);
- my $count;
- my $func_macro = $args{'func_macro'};
- my $paramcount = $#{$args{'parameterlist'}}; # -1 is empty
-
- print ".TH \"$args{'function'}\" 9 \"$args{'function'}\" \"$man_date\" \"Kernel Hacker's Manual\" LINUX\n";
-
- print ".SH NAME\n";
- print $args{'function'} . " \\- " . $args{'purpose'} . "\n";
-
- print ".SH SYNOPSIS\n";
- if ($args{'functiontype'} ne "") {
- print ".B \"" . $args{'functiontype'} . "\" " . $args{'function'} . "\n";
- } else {
- print ".B \"" . $args{'function'} . "\n";
- }
- $count = 0;
- my $parenth = "(";
- my $post = ",";
- foreach my $parameter (@{$args{'parameterlist'}}) {
- if ($count == $#{$args{'parameterlist'}}) {
- $post = ");";
- }
- $type = $args{'parametertypes'}{$parameter};
- if ($type =~ m/$function_pointer/) {
- # pointer-to-function
- print ".BI \"" . $parenth . $1 . "\" " . " \") (" . $2 . ")" . $post . "\"\n";
- } else {
- $type =~ s/([^\*])$/$1 /;
- print ".BI \"" . $parenth . $type . "\" " . " \"" . $post . "\"\n";
- }
- $count++;
- $parenth = "";
- }
-
- $paramcount = $#{$args{'parameterlist'}}; # -1 is empty
- if ($paramcount >= 0) {
- print ".SH ARGUMENTS\n";
- }
- foreach $parameter (@{$args{'parameterlist'}}) {
- my $parameter_name = $parameter;
- $parameter_name =~ s/\[.*//;
-
- print ".IP \"" . $parameter . "\" 12\n";
- output_highlight($args{'parameterdescs'}{$parameter_name});
- }
- foreach $section (@{$args{'sectionlist'}}) {
- print ".SH \"", uc $section, "\"\n";
- output_highlight($args{'sections'}{$section});
- }
-}
-
-##
-# output enum in man
-sub output_enum_man(%) {
- my %args = %{$_[0]};
- my ($parameter, $section);
- my $count;
-
- print ".TH \"$args{'module'}\" 9 \"enum $args{'enum'}\" \"$man_date\" \"API Manual\" LINUX\n";
-
- print ".SH NAME\n";
- print "enum " . $args{'enum'} . " \\- " . $args{'purpose'} . "\n";
-
- print ".SH SYNOPSIS\n";
- print "enum " . $args{'enum'} . " {\n";
- $count = 0;
- foreach my $parameter (@{$args{'parameterlist'}}) {
- print ".br\n.BI \" $parameter\"\n";
- if ($count == $#{$args{'parameterlist'}}) {
- print "\n};\n";
- last;
- } else {
- print ", \n.br\n";
- }
- $count++;
- }
-
- print ".SH Constants\n";
- foreach $parameter (@{$args{'parameterlist'}}) {
- my $parameter_name = $parameter;
- $parameter_name =~ s/\[.*//;
-
- print ".IP \"" . $parameter . "\" 12\n";
- output_highlight($args{'parameterdescs'}{$parameter_name});
- }
- foreach $section (@{$args{'sectionlist'}}) {
- print ".SH \"$section\"\n";
- output_highlight($args{'sections'}{$section});
- }
-}
-
-##
-# output struct in man
-sub output_struct_man(%) {
- my %args = %{$_[0]};
- my ($parameter, $section);
-
- print ".TH \"$args{'module'}\" 9 \"" . $args{'type'} . " " . $args{'struct'} . "\" \"$man_date\" \"API Manual\" LINUX\n";
-
- print ".SH NAME\n";
- print $args{'type'} . " " . $args{'struct'} . " \\- " . $args{'purpose'} . "\n";
-
- my $declaration = $args{'definition'};
- $declaration =~ s/\t/ /g;
- $declaration =~ s/\n/"\n.br\n.BI \"/g;
- print ".SH SYNOPSIS\n";
- print $args{'type'} . " " . $args{'struct'} . " {\n.br\n";
- print ".BI \"$declaration\n};\n.br\n\n";
-
- print ".SH Members\n";
- foreach $parameter (@{$args{'parameterlist'}}) {
- ($parameter =~ /^#/) && next;
-
- my $parameter_name = $parameter;
- $parameter_name =~ s/\[.*//;
-
- ($args{'parameterdescs'}{$parameter_name} ne $undescribed) || next;
- print ".IP \"" . $parameter . "\" 12\n";
- output_highlight($args{'parameterdescs'}{$parameter_name});
- }
- foreach $section (@{$args{'sectionlist'}}) {
- print ".SH \"$section\"\n";
- output_highlight($args{'sections'}{$section});
- }
-}
-
-##
-# output typedef in man
-sub output_typedef_man(%) {
- my %args = %{$_[0]};
- my ($parameter, $section);
-
- print ".TH \"$args{'module'}\" 9 \"$args{'typedef'}\" \"$man_date\" \"API Manual\" LINUX\n";
-
- print ".SH NAME\n";
- print "typedef " . $args{'typedef'} . " \\- " . $args{'purpose'} . "\n";
-
- foreach $section (@{$args{'sectionlist'}}) {
- print ".SH \"$section\"\n";
- output_highlight($args{'sections'}{$section});
- }
-}
-
-sub output_blockhead_man(%) {
- my %args = %{$_[0]};
- my ($parameter, $section);
- my $count;
-
- print ".TH \"$args{'module'}\" 9 \"$args{'module'}\" \"$man_date\" \"API Manual\" LINUX\n";
-
- foreach $section (@{$args{'sectionlist'}}) {
- print ".SH \"$section\"\n";
- output_highlight($args{'sections'}{$section});
- }
-}
-
-##
-# output in restructured text
-#
-
-#
-# This could use some work; it's used to output the DOC: sections, and
-# starts by putting out the name of the doc section itself, but that tends
-# to duplicate a header already in the template file.
-#
-sub output_blockhead_rst(%) {
- my %args = %{$_[0]};
- my ($parameter, $section);
-
- foreach $section (@{$args{'sectionlist'}}) {
- next if (defined($nosymbol_table{$section}));
-
- if ($output_selection != OUTPUT_INCLUDE) {
- print ".. _$section:\n\n";
- print "**$section**\n\n";
- }
- print_lineno($section_start_lines{$section});
- output_highlight_rst($args{'sections'}{$section});
- print "\n";
- }
-}
-
-#
-# Apply the RST highlights to a sub-block of text.
-#
-sub highlight_block($) {
- # The dohighlight kludge requires the text be called $contents
- my $contents = shift;
- eval $dohighlight;
- die $@ if $@;
- return $contents;
-}
-
-#
-# Regexes used only here.
-#
-my $sphinx_literal = '^[^.].*::$';
-my $sphinx_cblock = '^\.\.\ +code-block::';
-
-sub output_highlight_rst {
- my $input = join "\n",@_;
- my $output = "";
- my $line;
- my $in_literal = 0;
- my $litprefix;
- my $block = "";
-
- foreach $line (split "\n",$input) {
- #
- # If we're in a literal block, see if we should drop out
- # of it. Otherwise pass the line straight through unmunged.
- #
- if ($in_literal) {
- if (! ($line =~ /^\s*$/)) {
- #
- # If this is the first non-blank line in a literal
- # block we need to figure out what the proper indent is.
- #
- if ($litprefix eq "") {
- $line =~ /^(\s*)/;
- $litprefix = '^' . $1;
- $output .= $line . "\n";
- } elsif (! ($line =~ /$litprefix/)) {
- $in_literal = 0;
- } else {
- $output .= $line . "\n";
- }
- } else {
- $output .= $line . "\n";
- }
- }
- #
- # Not in a literal block (or just dropped out)
- #
- if (! $in_literal) {
- $block .= $line . "\n";
- if (($line =~ /$sphinx_literal/) || ($line =~ /$sphinx_cblock/)) {
- $in_literal = 1;
- $litprefix = "";
- $output .= highlight_block($block);
- $block = ""
- }
- }
- }
-
- if ($block) {
- $output .= highlight_block($block);
- }
-
- $output =~ s/^\n+//g;
- $output =~ s/\n+$//g;
-
- foreach $line (split "\n", $output) {
- print $lineprefix . $line . "\n";
- }
-}
-
-sub output_function_rst(%) {
- my %args = %{$_[0]};
- my ($parameter, $section);
- my $oldprefix = $lineprefix;
-
- my $signature = "";
- my $func_macro = $args{'func_macro'};
- my $paramcount = $#{$args{'parameterlist'}}; # -1 is empty
-
- if ($func_macro) {
- $signature = $args{'function'};
- } else {
- if ($args{'functiontype'}) {
- $signature = $args{'functiontype'} . " ";
- }
- $signature .= $args{'function'} . " (";
- }
-
- my $count = 0;
- foreach my $parameter (@{$args{'parameterlist'}}) {
- if ($count ne 0) {
- $signature .= ", ";
- }
- $count++;
- $type = $args{'parametertypes'}{$parameter};
-
- if ($type =~ m/$function_pointer/) {
- # pointer-to-function
- $signature .= $1 . $parameter . ") (" . $2 . ")";
- } else {
- $signature .= $type;
- }
- }
-
- if (!$func_macro) {
- $signature .= ")";
- }
-
- if ($args{'typedef'} || $args{'functiontype'} eq "") {
- print ".. c:macro:: ". $args{'function'} . "\n\n";
-
- if ($args{'typedef'}) {
- print_lineno($declaration_start_line);
- print " **Typedef**: ";
- $lineprefix = "";
- output_highlight_rst($args{'purpose'});
- print "\n\n**Syntax**\n\n";
- print " ``$signature``\n\n";
- } else {
- print "``$signature``\n\n";
- }
- } else {
- print ".. c:function:: $signature\n\n";
- }
-
- if (!$args{'typedef'}) {
- print_lineno($declaration_start_line);
- $lineprefix = " ";
- output_highlight_rst($args{'purpose'});
- print "\n";
- }
-
- #
- # Put our descriptive text into a container (thus an HTML <div>) to help
- # set the function prototypes apart.
- #
- $lineprefix = " ";
- if ($paramcount >= 0) {
- print ".. container:: kernelindent\n\n";
- print $lineprefix . "**Parameters**\n\n";
- }
- foreach $parameter (@{$args{'parameterlist'}}) {
- my $parameter_name = $parameter;
- $parameter_name =~ s/\[.*//;
- $type = $args{'parametertypes'}{$parameter};
-
- if ($type ne "") {
- print $lineprefix . "``$type``\n";
- } else {
- print $lineprefix . "``$parameter``\n";
- }
-
- print_lineno($parameterdesc_start_lines{$parameter_name});
-
- $lineprefix = " ";
- if (defined($args{'parameterdescs'}{$parameter_name}) &&
- $args{'parameterdescs'}{$parameter_name} ne $undescribed) {
- output_highlight_rst($args{'parameterdescs'}{$parameter_name});
- } else {
- print $lineprefix . "*undescribed*\n";
- }
- $lineprefix = " ";
- print "\n";
- }
-
- output_section_rst(@_);
- $lineprefix = $oldprefix;
-}
-
-sub output_section_rst(%) {
- my %args = %{$_[0]};
- my $section;
- my $oldprefix = $lineprefix;
-
- foreach $section (@{$args{'sectionlist'}}) {
- print $lineprefix . "**$section**\n\n";
- print_lineno($section_start_lines{$section});
- output_highlight_rst($args{'sections'}{$section});
- print "\n";
- }
- print "\n";
-}
-
-sub output_enum_rst(%) {
- my %args = %{$_[0]};
- my ($parameter);
- my $oldprefix = $lineprefix;
- my $count;
- my $outer;
-
- my $name = $args{'enum'};
- print "\n\n.. c:enum:: " . $name . "\n\n";
-
- print_lineno($declaration_start_line);
- $lineprefix = " ";
- output_highlight_rst($args{'purpose'});
- print "\n";
-
- print ".. container:: kernelindent\n\n";
- $outer = $lineprefix . " ";
- $lineprefix = $outer . " ";
- print $outer . "**Constants**\n\n";
- foreach $parameter (@{$args{'parameterlist'}}) {
- print $outer . "``$parameter``\n";
-
- if ($args{'parameterdescs'}{$parameter} ne $undescribed) {
- output_highlight_rst($args{'parameterdescs'}{$parameter});
- } else {
- print $lineprefix . "*undescribed*\n";
- }
- print "\n";
- }
- print "\n";
- $lineprefix = $oldprefix;
- output_section_rst(@_);
-}
-
-sub output_typedef_rst(%) {
- my %args = %{$_[0]};
- my ($parameter);
- my $oldprefix = $lineprefix;
- my $name;
-
- $name = $args{'typedef'};
-
- print "\n\n.. c:type:: " . $name . "\n\n";
- print_lineno($declaration_start_line);
- $lineprefix = " ";
- output_highlight_rst($args{'purpose'});
- print "\n";
-
- $lineprefix = $oldprefix;
- output_section_rst(@_);
-}
-
-sub output_struct_rst(%) {
- my %args = %{$_[0]};
- my ($parameter);
- my $oldprefix = $lineprefix;
-
- my $name = $args{'struct'};
- if ($args{'type'} eq 'union') {
- print "\n\n.. c:union:: " . $name . "\n\n";
- } else {
- print "\n\n.. c:struct:: " . $name . "\n\n";
- }
-
- print_lineno($declaration_start_line);
- $lineprefix = " ";
- output_highlight_rst($args{'purpose'});
- print "\n";
-
- print ".. container:: kernelindent\n\n";
- print $lineprefix . "**Definition**::\n\n";
- my $declaration = $args{'definition'};
- $lineprefix = $lineprefix . " ";
- $declaration =~ s/\t/$lineprefix/g;
- print $lineprefix . $args{'type'} . " " . $args{'struct'} . " {\n$declaration" . $lineprefix . "};\n\n";
-
- $lineprefix = " ";
- print $lineprefix . "**Members**\n\n";
- foreach $parameter (@{$args{'parameterlist'}}) {
- ($parameter =~ /^#/) && next;
-
- my $parameter_name = $parameter;
- $parameter_name =~ s/\[.*//;
-
- ($args{'parameterdescs'}{$parameter_name} ne $undescribed) || next;
- $type = $args{'parametertypes'}{$parameter};
- print_lineno($parameterdesc_start_lines{$parameter_name});
- print $lineprefix . "``" . $parameter . "``\n";
- $lineprefix = " ";
- output_highlight_rst($args{'parameterdescs'}{$parameter_name});
- $lineprefix = " ";
- print "\n";
- }
- print "\n";
-
- $lineprefix = $oldprefix;
- output_section_rst(@_);
-}
-
-## none mode output functions
-
-sub output_function_none(%) {
-}
-
-sub output_enum_none(%) {
-}
-
-sub output_typedef_none(%) {
-}
-
-sub output_struct_none(%) {
-}
-
-sub output_blockhead_none(%) {
-}
-
-##
-# generic output function for all types (function, struct/union, typedef, enum);
-# calls the generated, variable output_ function name based on
-# functype and output_mode
-sub output_declaration {
- no strict 'refs';
- my $name = shift;
- my $functype = shift;
- my $func = "output_${functype}_$output_mode";
-
- return if (defined($nosymbol_table{$name}));
-
- if (($output_selection == OUTPUT_ALL) ||
- (($output_selection == OUTPUT_INCLUDE ||
- $output_selection == OUTPUT_EXPORTED) &&
- defined($function_table{$name})) ||
- ($output_selection == OUTPUT_INTERNAL &&
- !($functype eq "function" && defined($function_table{$name}))))
- {
- &$func(@_);
- $section_counter++;
- }
-}
-
-##
-# generic output function - calls the right one based on current output mode.
-sub output_blockhead {
- no strict 'refs';
- my $func = "output_blockhead_" . $output_mode;
- &$func(@_);
- $section_counter++;
-}
-
-##
-# takes a declaration (struct, union, enum, typedef) and
-# invokes the right handler. NOT called for functions.
-sub dump_declaration($$) {
- no strict 'refs';
- my ($prototype, $file) = @_;
- my $func = "dump_" . $decl_type;
- &$func(@_);
-}
-
-sub dump_union($$) {
- dump_struct(@_);
-}
-
-sub dump_struct($$) {
- my $x = shift;
- my $file = shift;
- my $decl_type;
- my $members;
- my $type = qr{struct|union};
- # For capturing struct/union definition body, i.e. "{members*}qualifiers*"
- my $qualifiers = qr{$attribute|__packed|__aligned|____cacheline_aligned_in_smp|____cacheline_aligned};
- my $definition_body = qr{\{(.*)\}\s*$qualifiers*};
- my $struct_members = qr{($type)([^\{\};]+)\{([^\{\}]*)\}([^\{\}\;]*)\;};
-
- if ($x =~ /($type)\s+(\w+)\s*$definition_body/) {
- $decl_type = $1;
- $declaration_name = $2;
- $members = $3;
- } elsif ($x =~ /typedef\s+($type)\s*$definition_body\s*(\w+)\s*;/) {
- $decl_type = $1;
- $declaration_name = $3;
- $members = $2;
- }
-
- if ($members) {
- if ($identifier ne $declaration_name) {
- emit_warning("${file}:$.", "expecting prototype for $decl_type $identifier. Prototype was for $decl_type $declaration_name instead\n");
- return;
- }
-
- # ignore members marked private:
- $members =~ s/\/\*\s*private:.*?\/\*\s*public:.*?\*\///gosi;
- $members =~ s/\/\*\s*private:.*//gosi;
- # strip comments:
- $members =~ s/\/\*.*?\*\///gos;
- # strip attributes
- $members =~ s/\s*$attribute/ /gi;
- $members =~ s/\s*__aligned\s*\([^;]*\)/ /gos;
- $members =~ s/\s*__counted_by\s*\([^;]*\)/ /gos;
- $members =~ s/\s*__counted_by_(le|be)\s*\([^;]*\)/ /gos;
- $members =~ s/\s*__packed\s*/ /gos;
- $members =~ s/\s*CRYPTO_MINALIGN_ATTR/ /gos;
- $members =~ s/\s*____cacheline_aligned_in_smp/ /gos;
- $members =~ s/\s*____cacheline_aligned/ /gos;
- # unwrap struct_group():
- # - first eat non-declaration parameters and rewrite for final match
- # - then remove macro, outer parens, and trailing semicolon
- $members =~ s/\bstruct_group\s*\(([^,]*,)/STRUCT_GROUP(/gos;
- $members =~ s/\bstruct_group_attr\s*\(([^,]*,){2}/STRUCT_GROUP(/gos;
- $members =~ s/\bstruct_group_tagged\s*\(([^,]*),([^,]*),/struct $1 $2; STRUCT_GROUP(/gos;
- $members =~ s/\b__struct_group\s*\(([^,]*,){3}/STRUCT_GROUP(/gos;
- $members =~ s/\bSTRUCT_GROUP(\(((?:(?>[^)(]+)|(?1))*)\))[^;]*;/$2/gos;
-
- my $args = qr{([^,)]+)};
- # replace DECLARE_BITMAP
- $members =~ s/__ETHTOOL_DECLARE_LINK_MODE_MASK\s*\(([^\)]+)\)/DECLARE_BITMAP($1, __ETHTOOL_LINK_MODE_MASK_NBITS)/gos;
- $members =~ s/DECLARE_PHY_INTERFACE_MASK\s*\(([^\)]+)\)/DECLARE_BITMAP($1, PHY_INTERFACE_MODE_MAX)/gos;
- $members =~ s/DECLARE_BITMAP\s*\($args,\s*$args\)/unsigned long $1\[BITS_TO_LONGS($2)\]/gos;
- # replace DECLARE_HASHTABLE
- $members =~ s/DECLARE_HASHTABLE\s*\($args,\s*$args\)/unsigned long $1\[1 << (($2) - 1)\]/gos;
- # replace DECLARE_KFIFO
- $members =~ s/DECLARE_KFIFO\s*\($args,\s*$args,\s*$args\)/$2 \*$1/gos;
- # replace DECLARE_KFIFO_PTR
- $members =~ s/DECLARE_KFIFO_PTR\s*\($args,\s*$args\)/$2 \*$1/gos;
- # replace DECLARE_FLEX_ARRAY
- $members =~ s/(?:__)?DECLARE_FLEX_ARRAY\s*\($args,\s*$args\)/$1 $2\[\]/gos;
- #replace DEFINE_DMA_UNMAP_ADDR
- $members =~ s/DEFINE_DMA_UNMAP_ADDR\s*\($args\)/dma_addr_t $1/gos;
- #replace DEFINE_DMA_UNMAP_LEN
- $members =~ s/DEFINE_DMA_UNMAP_LEN\s*\($args\)/__u32 $1/gos;
- my $declaration = $members;
-
- # Split nested struct/union elements as newer ones
- while ($members =~ m/$struct_members/) {
- my $newmember;
- my $maintype = $1;
- my $ids = $4;
- my $content = $3;
- foreach my $id(split /,/, $ids) {
- $newmember .= "$maintype $id; ";
-
- $id =~ s/[:\[].*//;
- $id =~ s/^\s*\**(\S+)\s*/$1/;
- foreach my $arg (split /;/, $content) {
- next if ($arg =~ m/^\s*$/);
- if ($arg =~ m/^([^\(]+\(\*?\s*)([\w\.]*)(\s*\).*)/) {
- # pointer-to-function
- my $type = $1;
- my $name = $2;
- my $extra = $3;
- next if (!$name);
- if ($id =~ m/^\s*$/) {
- # anonymous struct/union
- $newmember .= "$type$name$extra; ";
- } else {
- $newmember .= "$type$id.$name$extra; ";
- }
- } else {
- my $type;
- my $names;
- $arg =~ s/^\s+//;
- $arg =~ s/\s+$//;
- # Handle bitmaps
- $arg =~ s/:\s*\d+\s*//g;
- # Handle arrays
- $arg =~ s/\[.*\]//g;
- # The type may have multiple words,
- # and multiple IDs can be defined, like:
- # const struct foo, *bar, foobar
- # So, we remove spaces when parsing the
- # names, in order to match just names
- # and commas for the names
- $arg =~ s/\s*,\s*/,/g;
- if ($arg =~ m/(.*)\s+([\S+,]+)/) {
- $type = $1;
- $names = $2;
- } else {
- $newmember .= "$arg; ";
- next;
- }
- foreach my $name (split /,/, $names) {
- $name =~ s/^\s*\**(\S+)\s*/$1/;
- next if (($name =~ m/^\s*$/));
- if ($id =~ m/^\s*$/) {
- # anonymous struct/union
- $newmember .= "$type $name; ";
- } else {
- $newmember .= "$type $id.$name; ";
- }
- }
- }
- }
- }
- $members =~ s/$struct_members/$newmember/;
- }
-
- # Ignore other nested elements, like enums
- $members =~ s/(\{[^\{\}]*\})//g;
-
- create_parameterlist($members, ';', $file, $declaration_name);
- check_sections($file, $declaration_name, $decl_type, $sectcheck, $struct_actual);
-
- # Adjust declaration for better display
- $declaration =~ s/([\{;])/$1\n/g;
- $declaration =~ s/\}\s+;/};/g;
- # Better handle inlined enums
- do {} while ($declaration =~ s/(enum\s+\{[^\}]+),([^\n])/$1,\n$2/);
-
- my @def_args = split /\n/, $declaration;
- my $level = 1;
- $declaration = "";
- foreach my $clause (@def_args) {
- $clause =~ s/^\s+//;
- $clause =~ s/\s+$//;
- $clause =~ s/\s+/ /;
- next if (!$clause);
- $level-- if ($clause =~ m/(\})/ && $level > 1);
- if (!($clause =~ m/^\s*#/)) {
- $declaration .= "\t" x $level;
- }
- $declaration .= "\t" . $clause . "\n";
- $level++ if ($clause =~ m/(\{)/ && !($clause =~m/\}/));
- }
- output_declaration($declaration_name,
- 'struct',
- {'struct' => $declaration_name,
- 'module' => $modulename,
- 'definition' => $declaration,
- 'parameterlist' => \@parameterlist,
- 'parameterdescs' => \%parameterdescs,
- 'parametertypes' => \%parametertypes,
- 'sectionlist' => \@sectionlist,
- 'sections' => \%sections,
- 'purpose' => $declaration_purpose,
- 'type' => $decl_type
- });
- } else {
- print STDERR "${file}:$.: error: Cannot parse struct or union!\n";
- ++$errors;
- }
-}
-
-
-sub show_warnings($$) {
- my $functype = shift;
- my $name = shift;
-
- return 0 if (defined($nosymbol_table{$name}));
-
- return 1 if ($output_selection == OUTPUT_ALL);
-
- if ($output_selection == OUTPUT_EXPORTED) {
- if (defined($function_table{$name})) {
- return 1;
- } else {
- return 0;
- }
- }
- if ($output_selection == OUTPUT_INTERNAL) {
- if (!($functype eq "function" && defined($function_table{$name}))) {
- return 1;
- } else {
- return 0;
- }
- }
- if ($output_selection == OUTPUT_INCLUDE) {
- if (defined($function_table{$name})) {
- return 1;
- } else {
- return 0;
- }
- }
- die("Please add the new output type at show_warnings()");
-}
-
-sub dump_enum($$) {
- my $x = shift;
- my $file = shift;
- my $members;
-
- # ignore members marked private:
- $x =~ s/\/\*\s*private:.*?\/\*\s*public:.*?\*\///gosi;
- $x =~ s/\/\*\s*private:.*}/}/gosi;
-
- $x =~ s@/\*.*?\*/@@gos; # strip comments.
- # strip #define macros inside enums
- $x =~ s@#\s*((define|ifdef|if)\s+|endif)[^;]*;@@gos;
-
- if ($x =~ /typedef\s+enum\s*\{(.*)\}\s*(\w*)\s*;/) {
- $declaration_name = $2;
- $members = $1;
- } elsif ($x =~ /enum\s+(\w*)\s*\{(.*)\}/) {
- $declaration_name = $1;
- $members = $2;
- }
-
- if ($members) {
- if ($identifier ne $declaration_name) {
- if ($identifier eq "") {
- emit_warning("${file}:$.", "wrong kernel-doc identifier on line:\n");
- } else {
- emit_warning("${file}:$.", "expecting prototype for enum $identifier. Prototype was for enum $declaration_name instead\n");
- }
- return;
- }
- $declaration_name = "(anonymous)" if ($declaration_name eq "");
-
- my %_members;
-
- $members =~ s/\s+$//;
- $members =~ s/\([^;]*?[\)]//g;
-
- foreach my $arg (split ',', $members) {
- $arg =~ s/^\s*(\w+).*/$1/;
- push @parameterlist, $arg;
- if (!$parameterdescs{$arg}) {
- $parameterdescs{$arg} = $undescribed;
- if (show_warnings("enum", $declaration_name)) {
- emit_warning("${file}:$.", "Enum value '$arg' not described in enum '$declaration_name'\n");
- }
- }
- $_members{$arg} = 1;
- }
-
- while (my ($k, $v) = each %parameterdescs) {
- if (!exists($_members{$k})) {
- if (show_warnings("enum", $declaration_name)) {
- emit_warning("${file}:$.", "Excess enum value '$k' description in '$declaration_name'\n");
- }
- }
- }
-
- output_declaration($declaration_name,
- 'enum',
- {'enum' => $declaration_name,
- 'module' => $modulename,
- 'parameterlist' => \@parameterlist,
- 'parameterdescs' => \%parameterdescs,
- 'sectionlist' => \@sectionlist,
- 'sections' => \%sections,
- 'purpose' => $declaration_purpose
- });
- } else {
- print STDERR "${file}:$.: error: Cannot parse enum!\n";
- ++$errors;
- }
-}
-
-my $typedef_type = qr { ((?:\s+[\w\*]+\b){0,7}\s+(?:\w+\b|\*+))\s* }x;
-my $typedef_ident = qr { \*?\s*(\w\S+)\s* }x;
-my $typedef_args = qr { \s*\((.*)\); }x;
-
-my $typedef1 = qr { typedef$typedef_type\($typedef_ident\)$typedef_args }x;
-my $typedef2 = qr { typedef$typedef_type$typedef_ident$typedef_args }x;
-
-sub dump_typedef($$) {
- my $x = shift;
- my $file = shift;
-
- $x =~ s@/\*.*?\*/@@gos; # strip comments.
-
- # Parse function typedef prototypes
- if ($x =~ $typedef1 || $x =~ $typedef2) {
- $return_type = $1;
- $declaration_name = $2;
- my $args = $3;
- $return_type =~ s/^\s+//;
-
- if ($identifier ne $declaration_name) {
- emit_warning("${file}:$.", "expecting prototype for typedef $identifier. Prototype was for typedef $declaration_name instead\n");
- return;
- }
-
- create_parameterlist($args, ',', $file, $declaration_name);
-
- output_declaration($declaration_name,
- 'function',
- {'function' => $declaration_name,
- 'typedef' => 1,
- 'module' => $modulename,
- 'functiontype' => $return_type,
- 'parameterlist' => \@parameterlist,
- 'parameterdescs' => \%parameterdescs,
- 'parametertypes' => \%parametertypes,
- 'sectionlist' => \@sectionlist,
- 'sections' => \%sections,
- 'purpose' => $declaration_purpose
- });
- return;
- }
-
- while (($x =~ /\(*.\)\s*;$/) || ($x =~ /\[*.\]\s*;$/)) {
- $x =~ s/\(*.\)\s*;$/;/;
- $x =~ s/\[*.\]\s*;$/;/;
- }
-
- if ($x =~ /typedef.*\s+(\w+)\s*;/) {
- $declaration_name = $1;
-
- if ($identifier ne $declaration_name) {
- emit_warning("${file}:$.", "expecting prototype for typedef $identifier. Prototype was for typedef $declaration_name instead\n");
- return;
- }
-
- output_declaration($declaration_name,
- 'typedef',
- {'typedef' => $declaration_name,
- 'module' => $modulename,
- 'sectionlist' => \@sectionlist,
- 'sections' => \%sections,
- 'purpose' => $declaration_purpose
- });
- } else {
- print STDERR "${file}:$.: error: Cannot parse typedef!\n";
- ++$errors;
- }
-}
-
-sub save_struct_actual($) {
- my $actual = shift;
-
- # strip all spaces from the actual param so that it looks like one string item
- $actual =~ s/\s*//g;
- $struct_actual = $struct_actual . $actual . " ";
-}
-
-sub create_parameterlist($$$$) {
- my $args = shift;
- my $splitter = shift;
- my $file = shift;
- my $declaration_name = shift;
- my $type;
- my $param;
-
- # temporarily replace commas inside function pointer definition
- my $arg_expr = qr{\([^\),]+};
- while ($args =~ /$arg_expr,/) {
- $args =~ s/($arg_expr),/$1#/g;
- }
-
- foreach my $arg (split($splitter, $args)) {
- # strip comments
- $arg =~ s/\/\*.*\*\///;
- # ignore argument attributes
- $arg =~ s/\sPOS0?\s/ /;
- # strip leading/trailing spaces
- $arg =~ s/^\s*//;
- $arg =~ s/\s*$//;
- $arg =~ s/\s+/ /;
-
- if ($arg =~ /^#/) {
- # Treat preprocessor directive as a typeless variable just to fill
- # corresponding data structures "correctly". Catch it later in
- # output_* subs.
- push_parameter($arg, "", "", $file);
- } elsif ($arg =~ m/\(.+\)\s*\(/) {
- # pointer-to-function
- $arg =~ tr/#/,/;
- $arg =~ m/[^\(]+\(\*?\s*([\w\[\]\.]*)\s*\)/;
- $param = $1;
- $type = $arg;
- $type =~ s/([^\(]+\(\*?)\s*$param/$1/;
- save_struct_actual($param);
- push_parameter($param, $type, $arg, $file, $declaration_name);
- } elsif ($arg =~ m/\(.+\)\s*\[/) {
- # array-of-pointers
- $arg =~ tr/#/,/;
- $arg =~ m/[^\(]+\(\s*\*\s*([\w\[\]\.]*?)\s*(\s*\[\s*[\w]+\s*\]\s*)*\)/;
- $param = $1;
- $type = $arg;
- $type =~ s/([^\(]+\(\*?)\s*$param/$1/;
- save_struct_actual($param);
- push_parameter($param, $type, $arg, $file, $declaration_name);
- } elsif ($arg) {
- $arg =~ s/\s*:\s*/:/g;
- $arg =~ s/\s*\[/\[/g;
-
- my @args = split('\s*,\s*', $arg);
- if ($args[0] =~ m/\*/) {
- $args[0] =~ s/(\*+)\s*/ $1/;
- }
-
- my @first_arg;
- if ($args[0] =~ /^(.*\s+)(.*?\[.*\].*)$/) {
- shift @args;
- push(@first_arg, split('\s+', $1));
- push(@first_arg, $2);
- } else {
- @first_arg = split('\s+', shift @args);
- }
-
- unshift(@args, pop @first_arg);
- $type = join " ", @first_arg;
-
- foreach $param (@args) {
- if ($param =~ m/^(\*+)\s*(.*)/) {
- save_struct_actual($2);
-
- push_parameter($2, "$type $1", $arg, $file, $declaration_name);
- } elsif ($param =~ m/(.*?):(\w+)/) {
- if ($type ne "") { # skip unnamed bit-fields
- save_struct_actual($1);
- push_parameter($1, "$type:$2", $arg, $file, $declaration_name)
- }
- } else {
- save_struct_actual($param);
- push_parameter($param, $type, $arg, $file, $declaration_name);
- }
- }
- }
- }
-}
-
-sub push_parameter($$$$$) {
- my $param = shift;
- my $type = shift;
- my $org_arg = shift;
- my $file = shift;
- my $declaration_name = shift;
-
- if (($anon_struct_union == 1) && ($type eq "") &&
- ($param eq "}")) {
- return; # ignore the ending }; from anon. struct/union
- }
-
- $anon_struct_union = 0;
- $param =~ s/[\[\)].*//;
-
- if ($type eq "" && $param =~ /\.\.\.$/)
- {
- if (!$param =~ /\w\.\.\.$/) {
- # handles unnamed variable parameters
- $param = "...";
- } elsif ($param =~ /\w\.\.\.$/) {
- # for named variable parameters of the form `x...`, remove the dots
- $param =~ s/\.\.\.$//;
- }
- if (!defined $parameterdescs{$param} || $parameterdescs{$param} eq "") {
- $parameterdescs{$param} = "variable arguments";
- }
- }
- elsif ($type eq "" && ($param eq "" or $param eq "void"))
- {
- $param="void";
- $parameterdescs{void} = "no arguments";
- }
- elsif ($type eq "" && ($param eq "struct" or $param eq "union"))
- # handle unnamed (anonymous) union or struct:
- {
- $type = $param;
- $param = "{unnamed_" . $param . "}";
- $parameterdescs{$param} = "anonymous\n";
- $anon_struct_union = 1;
- }
- elsif ($param =~ "__cacheline_group" )
- # handle cache group enforcing variables: they do not need be described in header files
- {
- return; # ignore __cacheline_group_begin and __cacheline_group_end
- }
-
- # warn if parameter has no description
- # (but ignore ones starting with # as these are not parameters
- # but inline preprocessor statements);
- # Note: It will also ignore void params and unnamed structs/unions
- if (!defined $parameterdescs{$param} && $param !~ /^#/) {
- $parameterdescs{$param} = $undescribed;
-
- if (show_warnings($type, $declaration_name) && $param !~ /\./) {
- emit_warning("${file}:$.", "Function parameter or struct member '$param' not described in '$declaration_name'\n");
- }
- }
-
- # strip spaces from $param so that it is one continuous string
- # on @parameterlist;
- # this fixes a problem where check_sections() cannot find
- # a parameter like "addr[6 + 2]" because it actually appears
- # as "addr[6", "+", "2]" on the parameter list;
- # but it's better to maintain the param string unchanged for output,
- # so just weaken the string compare in check_sections() to ignore
- # "[blah" in a parameter string;
- ###$param =~ s/\s*//g;
- push @parameterlist, $param;
- $org_arg =~ s/\s\s+/ /g;
- $parametertypes{$param} = $org_arg;
-}
-
-sub check_sections($$$$$) {
- my ($file, $decl_name, $decl_type, $sectcheck, $prmscheck) = @_;
- my @sects = split ' ', $sectcheck;
- my @prms = split ' ', $prmscheck;
- my $err;
- my ($px, $sx);
- my $prm_clean; # strip trailing "[array size]" and/or beginning "*"
-
- foreach $sx (0 .. $#sects) {
- $err = 1;
- foreach $px (0 .. $#prms) {
- $prm_clean = $prms[$px];
- $prm_clean =~ s/\[.*\]//;
- $prm_clean =~ s/$attribute//i;
- # ignore array size in a parameter string;
- # however, the original param string may contain
- # spaces, e.g.: addr[6 + 2]
- # and this appears in @prms as "addr[6" since the
- # parameter list is split at spaces;
- # hence just ignore "[..." for the sections check;
- $prm_clean =~ s/\[.*//;
-
- ##$prm_clean =~ s/^\**//;
- if ($prm_clean eq $sects[$sx]) {
- $err = 0;
- last;
- }
- }
- if ($err) {
- if ($decl_type eq "function") {
- emit_warning("${file}:$.",
- "Excess function parameter " .
- "'$sects[$sx]' " .
- "description in '$decl_name'\n");
- } elsif (($decl_type eq "struct") or
- ($decl_type eq "union")) {
- emit_warning("${file}:$.",
- "Excess $decl_type member " .
- "'$sects[$sx]' " .
- "description in '$decl_name'\n");
- }
- }
- }
-}
-
-##
-# Checks the section describing the return value of a function.
-sub check_return_section {
- my $file = shift;
- my $declaration_name = shift;
- my $return_type = shift;
-
- # Ignore an empty return type (It's a macro)
- # Ignore functions with a "void" return type. (But don't ignore "void *")
- if (($return_type eq "") || ($return_type =~ /void\s*\w*\s*$/)) {
- return;
- }
-
- if (!defined($sections{$section_return}) ||
- $sections{$section_return} eq "")
- {
- emit_warning("${file}:$.",
- "No description found for return value of " .
- "'$declaration_name'\n");
- }
-}
-
-##
-# takes a function prototype and the name of the current file being
-# processed and spits out all the details stored in the global
-# arrays/hashes.
-sub dump_function($$) {
- my $prototype = shift;
- my $file = shift;
- my $func_macro = 0;
-
- print_lineno($new_start_line);
-
- $prototype =~ s/^static +//;
- $prototype =~ s/^extern +//;
- $prototype =~ s/^asmlinkage +//;
- $prototype =~ s/^inline +//;
- $prototype =~ s/^__inline__ +//;
- $prototype =~ s/^__inline +//;
- $prototype =~ s/^__always_inline +//;
- $prototype =~ s/^noinline +//;
- $prototype =~ s/^__FORTIFY_INLINE +//;
- $prototype =~ s/__init +//;
- $prototype =~ s/__init_or_module +//;
- $prototype =~ s/__deprecated +//;
- $prototype =~ s/__flatten +//;
- $prototype =~ s/__meminit +//;
- $prototype =~ s/__must_check +//;
- $prototype =~ s/__weak +//;
- $prototype =~ s/__sched +//;
- $prototype =~ s/_noprof//;
- $prototype =~ s/__printf\s*\(\s*\d*\s*,\s*\d*\s*\) +//;
- $prototype =~ s/__(?:re)?alloc_size\s*\(\s*\d+\s*(?:,\s*\d+\s*)?\) +//;
- $prototype =~ s/__diagnose_as\s*\(\s*\S+\s*(?:,\s*\d+\s*)*\) +//;
- $prototype =~ s/DECL_BUCKET_PARAMS\s*\(\s*(\S+)\s*,\s*(\S+)\s*\)/$1, $2/;
- my $define = $prototype =~ s/^#\s*define\s+//; #ak added
- $prototype =~ s/__attribute_const__ +//;
- $prototype =~ s/__attribute__\s*\(\(
- (?:
- [\w\s]++ # attribute name
- (?:\([^)]*+\))? # attribute arguments
- \s*+,? # optional comma at the end
- )+
- \)\)\s+//x;
-
- # Yes, this truly is vile. We are looking for:
- # 1. Return type (may be nothing if we're looking at a macro)
- # 2. Function name
- # 3. Function parameters.
- #
- # All the while we have to watch out for function pointer parameters
- # (which IIRC is what the two sections are for), C types (these
- # regexps don't even start to express all the possibilities), and
- # so on.
- #
- # If you mess with these regexps, it's a good idea to check that
- # the following functions' documentation still comes out right:
- # - parport_register_device (function pointer parameters)
- # - atomic_set (macro)
- # - pci_match_device, __copy_to_user (long return type)
- my $name = qr{[a-zA-Z0-9_~:]+};
- my $prototype_end1 = qr{[^\(]*};
- my $prototype_end2 = qr{[^\{]*};
- my $prototype_end = qr{\(($prototype_end1|$prototype_end2)\)};
- my $type1 = qr{[\w\s]+};
- my $type2 = qr{$type1\*+};
-
- if ($define && $prototype =~ m/^()($name)\s+/) {
- # This is an object-like macro, it has no return type and no parameter
- # list.
- # Function-like macros are not allowed to have spaces between
- # declaration_name and opening parenthesis (notice the \s+).
- $return_type = $1;
- $declaration_name = $2;
- $func_macro = 1;
- } elsif ($prototype =~ m/^()($name)\s*$prototype_end/ ||
- $prototype =~ m/^($type1)\s+($name)\s*$prototype_end/ ||
- $prototype =~ m/^($type2+)\s*($name)\s*$prototype_end/) {
- $return_type = $1;
- $declaration_name = $2;
- my $args = $3;
-
- create_parameterlist($args, ',', $file, $declaration_name);
- } else {
- emit_warning("${file}:$.", "cannot understand function prototype: '$prototype'\n");
- return;
- }
-
- if ($identifier ne $declaration_name) {
- emit_warning("${file}:$.", "expecting prototype for $identifier(). Prototype was for $declaration_name() instead\n");
- return;
- }
-
- my $prms = join " ", @parameterlist;
- check_sections($file, $declaration_name, "function", $sectcheck, $prms);
-
- # This check emits a lot of warnings at the moment, because many
- # functions don't have a 'Return' doc section. So until the number
- # of warnings goes sufficiently down, the check is only performed in
- # -Wreturn mode.
- # TODO: always perform the check.
- if ($Wreturn && !$func_macro) {
- check_return_section($file, $declaration_name, $return_type);
- }
-
- # The function parser can be called with a typedef parameter.
- # Handle it.
- if ($return_type =~ /typedef/) {
- output_declaration($declaration_name,
- 'function',
- {'function' => $declaration_name,
- 'typedef' => 1,
- 'module' => $modulename,
- 'functiontype' => $return_type,
- 'parameterlist' => \@parameterlist,
- 'parameterdescs' => \%parameterdescs,
- 'parametertypes' => \%parametertypes,
- 'sectionlist' => \@sectionlist,
- 'sections' => \%sections,
- 'purpose' => $declaration_purpose,
- 'func_macro' => $func_macro
- });
- } else {
- output_declaration($declaration_name,
- 'function',
- {'function' => $declaration_name,
- 'module' => $modulename,
- 'functiontype' => $return_type,
- 'parameterlist' => \@parameterlist,
- 'parameterdescs' => \%parameterdescs,
- 'parametertypes' => \%parametertypes,
- 'sectionlist' => \@sectionlist,
- 'sections' => \%sections,
- 'purpose' => $declaration_purpose,
- 'func_macro' => $func_macro
- });
- }
-}
-
-sub reset_state {
- $function = "";
- %parameterdescs = ();
- %parametertypes = ();
- @parameterlist = ();
- %sections = ();
- @sectionlist = ();
- $sectcheck = "";
- $struct_actual = "";
- $prototype = "";
-
- $state = STATE_NORMAL;
- $inline_doc_state = STATE_INLINE_NA;
-}
-
-sub tracepoint_munge($) {
- my $file = shift;
- my $tracepointname = 0;
- my $tracepointargs = 0;
-
- if ($prototype =~ m/TRACE_EVENT\((.*?),/) {
- $tracepointname = $1;
- }
- if ($prototype =~ m/DEFINE_SINGLE_EVENT\((.*?),/) {
- $tracepointname = $1;
- }
- if ($prototype =~ m/DEFINE_EVENT\((.*?),(.*?),/) {
- $tracepointname = $2;
- }
- $tracepointname =~ s/^\s+//; #strip leading whitespace
- if ($prototype =~ m/TP_PROTO\((.*?)\)/) {
- $tracepointargs = $1;
- }
- if (($tracepointname eq 0) || ($tracepointargs eq 0)) {
- emit_warning("${file}:$.", "Unrecognized tracepoint format: \n".
- "$prototype\n");
- } else {
- $prototype = "static inline void trace_$tracepointname($tracepointargs)";
- $identifier = "trace_$identifier";
- }
-}
-
-sub syscall_munge() {
- my $void = 0;
-
- $prototype =~ s@[\r\n]+@ @gos; # strip newlines/CR's
-## if ($prototype =~ m/SYSCALL_DEFINE0\s*\(\s*(a-zA-Z0-9_)*\s*\)/) {
- if ($prototype =~ m/SYSCALL_DEFINE0/) {
- $void = 1;
-## $prototype = "long sys_$1(void)";
- }
-
- $prototype =~ s/SYSCALL_DEFINE.*\(/long sys_/; # fix return type & func name
- if ($prototype =~ m/long (sys_.*?),/) {
- $prototype =~ s/,/\(/;
- } elsif ($void) {
- $prototype =~ s/\)/\(void\)/;
- }
-
- # now delete all of the odd-number commas in $prototype
- # so that arg types & arg names don't have a comma between them
- my $count = 0;
- my $len = length($prototype);
- if ($void) {
- $len = 0; # skip the for-loop
- }
- for (my $ix = 0; $ix < $len; $ix++) {
- if (substr($prototype, $ix, 1) eq ',') {
- $count++;
- if ($count % 2 == 1) {
- substr($prototype, $ix, 1) = ' ';
- }
- }
- }
-}
-
-sub process_proto_function($$) {
- my $x = shift;
- my $file = shift;
-
- $x =~ s@\/\/.*$@@gos; # strip C99-style comments to end of line
-
- if ($x =~ /^#/ && $x !~ /^#\s*define/) {
- # do nothing
- } elsif ($x =~ /([^\{]*)/) {
- $prototype .= $1;
- }
-
- if (($x =~ /\{/) || ($x =~ /\#\s*define/) || ($x =~ /;/)) {
- $prototype =~ s@/\*.*?\*/@@gos; # strip comments.
- $prototype =~ s@[\r\n]+@ @gos; # strip newlines/cr's.
- $prototype =~ s@^\s+@@gos; # strip leading spaces
-
- # Handle prototypes for function pointers like:
- # int (*pcs_config)(struct foo)
- $prototype =~ s@^(\S+\s+)\(\s*\*(\S+)\)@$1$2@gos;
-
- if ($prototype =~ /SYSCALL_DEFINE/) {
- syscall_munge();
- }
- if ($prototype =~ /TRACE_EVENT/ || $prototype =~ /DEFINE_EVENT/ ||
- $prototype =~ /DEFINE_SINGLE_EVENT/)
- {
- tracepoint_munge($file);
- }
- dump_function($prototype, $file);
- reset_state();
- }
-}
-
-sub process_proto_type($$) {
- my $x = shift;
- my $file = shift;
-
- $x =~ s@[\r\n]+@ @gos; # strip newlines/cr's.
- $x =~ s@^\s+@@gos; # strip leading spaces
- $x =~ s@\s+$@@gos; # strip trailing spaces
- $x =~ s@\/\/.*$@@gos; # strip C99-style comments to end of line
-
- if ($x =~ /^#/) {
- # To distinguish preprocessor directive from regular declaration later.
- $x .= ";";
- }
-
- while (1) {
- if ( $x =~ /([^\{\};]*)([\{\};])(.*)/ ) {
- if( length $prototype ) {
- $prototype .= " "
- }
- $prototype .= $1 . $2;
- ($2 eq '{') && $brcount++;
- ($2 eq '}') && $brcount--;
- if (($2 eq ';') && ($brcount == 0)) {
- dump_declaration($prototype, $file);
- reset_state();
- last;
- }
- $x = $3;
- } else {
- $prototype .= $x;
- last;
- }
- }
-}
-
-
-sub map_filename($) {
- my $file;
- my ($orig_file) = @_;
-
- if (defined($ENV{'SRCTREE'})) {
- $file = "$ENV{'SRCTREE'}" . "/" . $orig_file;
- } else {
- $file = $orig_file;
- }
-
- return $file;
-}
-
-sub process_export_file($) {
- my ($orig_file) = @_;
- my $file = map_filename($orig_file);
-
- if (!open(IN,"<$file")) {
- print STDERR "Error: Cannot open file $file\n";
- ++$errors;
- return;
- }
-
- while (<IN>) {
- if (/$export_symbol/) {
- next if (defined($nosymbol_table{$2}));
- $function_table{$2} = 1;
- }
- if (/$export_symbol_ns/) {
- next if (defined($nosymbol_table{$2}));
- $function_table{$2} = 1;
- }
- }
-
- close(IN);
-}
-
-#
-# Parsers for the various processing states.
-#
-# STATE_NORMAL: looking for the /** to begin everything.
-#
-sub process_normal() {
- if (/$doc_start/o) {
- $state = STATE_NAME; # next line is always the function name
- $declaration_start_line = $. + 1;
- }
-}
-
-#
-# STATE_NAME: Looking for the "name - description" line
-#
-sub process_name($$) {
- my $file = shift;
- my $descr;
-
- if (/$doc_block/o) {
- $state = STATE_DOCBLOCK;
- $contents = "";
- $new_start_line = $.;
-
- if ( $1 eq "" ) {
- $section = $section_intro;
- } else {
- $section = $1;
- }
- } elsif (/$doc_decl/o) {
- $identifier = $1;
- my $is_kernel_comment = 0;
- my $decl_start = qr{$doc_com};
- # test for pointer declaration type, foo * bar() - desc
- my $fn_type = qr{\w+\s*\*\s*};
- my $parenthesis = qr{\(\w*\)};
- my $decl_end = qr{[-:].*};
- if (/^$decl_start([\w\s]+?)$parenthesis?\s*$decl_end?$/) {
- $identifier = $1;
- }
- if ($identifier =~ m/^(struct|union|enum|typedef)\b\s*(\S*)/) {
- $decl_type = $1;
- $identifier = $2;
- $is_kernel_comment = 1;
- }
- # Look for foo() or static void foo() - description; or misspelt
- # identifier
- elsif (/^$decl_start$fn_type?(\w+)\s*$parenthesis?\s*$decl_end?$/ ||
- /^$decl_start$fn_type?(\w+[^-:]*)$parenthesis?\s*$decl_end$/) {
- $identifier = $1;
- $decl_type = 'function';
- $identifier =~ s/^define\s+//;
- $is_kernel_comment = 1;
- }
- $identifier =~ s/\s+$//;
-
- $state = STATE_BODY;
- # if there's no @param blocks need to set up default section
- # here
- $contents = "";
- $section = $section_default;
- $new_start_line = $. + 1;
- if (/[-:](.*)/) {
- # strip leading/trailing/multiple spaces
- $descr= $1;
- $descr =~ s/^\s*//;
- $descr =~ s/\s*$//;
- $descr =~ s/\s+/ /g;
- $declaration_purpose = $descr;
- $state = STATE_BODY_MAYBE;
- } else {
- $declaration_purpose = "";
- }
-
- if (!$is_kernel_comment) {
- emit_warning("${file}:$.", "This comment starts with '/**', but isn't a kernel-doc comment. Refer Documentation/doc-guide/kernel-doc.rst\n$_");
- $state = STATE_NORMAL;
- }
-
- if (($declaration_purpose eq "") && $Wshort_desc) {
- emit_warning("${file}:$.", "missing initial short description on line:\n$_");
- }
-
- if ($identifier eq "" && $decl_type ne "enum") {
- emit_warning("${file}:$.", "wrong kernel-doc identifier on line:\n$_");
- $state = STATE_NORMAL;
- }
-
- if ($verbose) {
- print STDERR "${file}:$.: info: Scanning doc for $decl_type $identifier\n";
- }
- } else {
- emit_warning("${file}:$.", "Cannot understand $_ on line $. - I thought it was a doc line\n");
- $state = STATE_NORMAL;
- }
-}
-
-
-#
-# STATE_BODY and STATE_BODY_MAYBE: the bulk of a kerneldoc comment.
-#
-sub process_body($$) {
- my $file = shift;
-
- if ($state == STATE_BODY_WITH_BLANK_LINE && /^\s*\*\s?\S/) {
- dump_section($file, $section, $contents);
- $section = $section_default;
- $new_start_line = $.;
- $contents = "";
- }
-
- if (/$doc_sect/i) { # case insensitive for supported section names
- $newsection = $1;
- $newcontents = $2;
-
- # map the supported section names to the canonical names
- if ($newsection =~ m/^description$/i) {
- $newsection = $section_default;
- } elsif ($newsection =~ m/^context$/i) {
- $newsection = $section_context;
- } elsif ($newsection =~ m/^returns?$/i) {
- $newsection = $section_return;
- } elsif ($newsection =~ m/^\@return$/) {
- # special: @return is a section, not a param description
- $newsection = $section_return;
- }
-
- if (($contents ne "") && ($contents ne "\n")) {
- dump_section($file, $section, $contents);
- $section = $section_default;
- }
-
- $state = STATE_BODY;
- $contents = $newcontents;
- $new_start_line = $.;
- while (substr($contents, 0, 1) eq " ") {
- $contents = substr($contents, 1);
- }
- if ($contents ne "") {
- $contents .= "\n";
- }
- $section = $newsection;
- $leading_space = undef;
- } elsif (/$doc_end/) {
- if (($contents ne "") && ($contents ne "\n")) {
- dump_section($file, $section, $contents);
- $section = $section_default;
- $contents = "";
- }
- # look for doc_com + <text> + doc_end:
- if ($_ =~ m'\s*\*\s*[a-zA-Z_0-9:\.]+\*/') {
- emit_warning("${file}:$.", "suspicious ending line: $_");
- }
-
- $prototype = "";
- $state = STATE_PROTO;
- $brcount = 0;
- $new_start_line = $. + 1;
- } elsif (/$doc_content/) {
- if ($1 eq "") {
- if ($section eq $section_context) {
- dump_section($file, $section, $contents);
- $section = $section_default;
- $contents = "";
- $new_start_line = $.;
- $state = STATE_BODY;
- } else {
- if ($section ne $section_default) {
- $state = STATE_BODY_WITH_BLANK_LINE;
- } else {
- $state = STATE_BODY;
- }
- $contents .= "\n";
- }
- } elsif ($state == STATE_BODY_MAYBE) {
- # Continued declaration purpose
- chomp($declaration_purpose);
- $declaration_purpose .= " " . $1;
- $declaration_purpose =~ s/\s+/ /g;
- } else {
- my $cont = $1;
- if ($section =~ m/^@/ || $section eq $section_context) {
- if (!defined $leading_space) {
- if ($cont =~ m/^(\s+)/) {
- $leading_space = $1;
- } else {
- $leading_space = "";
- }
- }
- $cont =~ s/^$leading_space//;
- }
- $contents .= $cont . "\n";
- }
- } else {
- # i dont know - bad line? ignore.
- emit_warning("${file}:$.", "bad line: $_");
- }
-}
-
-
-#
-# STATE_PROTO: reading a function/whatever prototype.
-#
-sub process_proto($$) {
- my $file = shift;
-
- if (/$doc_inline_oneline/) {
- $section = $1;
- $contents = $2;
- if ($contents ne "") {
- $contents .= "\n";
- dump_section($file, $section, $contents);
- $section = $section_default;
- $contents = "";
- }
- } elsif (/$doc_inline_start/) {
- $state = STATE_INLINE;
- $inline_doc_state = STATE_INLINE_NAME;
- } elsif ($decl_type eq 'function') {
- process_proto_function($_, $file);
- } else {
- process_proto_type($_, $file);
- }
-}
-
-#
-# STATE_DOCBLOCK: within a DOC: block.
-#
-sub process_docblock($$) {
- my $file = shift;
-
- if (/$doc_end/) {
- dump_doc_section($file, $section, $contents);
- $section = $section_default;
- $contents = "";
- $function = "";
- %parameterdescs = ();
- %parametertypes = ();
- @parameterlist = ();
- %sections = ();
- @sectionlist = ();
- $prototype = "";
- $state = STATE_NORMAL;
- } elsif (/$doc_content/) {
- if ( $1 eq "" ) {
- $contents .= $blankline;
- } else {
- $contents .= $1 . "\n";
- }
- }
-}
-
-#
-# STATE_INLINE: docbook comments within a prototype.
-#
-sub process_inline($$) {
- my $file = shift;
-
- # First line (state 1) needs to be a @parameter
- if ($inline_doc_state == STATE_INLINE_NAME && /$doc_inline_sect/o) {
- $section = $1;
- $contents = $2;
- $new_start_line = $.;
- if ($contents ne "") {
- while (substr($contents, 0, 1) eq " ") {
- $contents = substr($contents, 1);
- }
- $contents .= "\n";
- }
- $inline_doc_state = STATE_INLINE_TEXT;
- # Documentation block end */
- } elsif (/$doc_inline_end/) {
- if (($contents ne "") && ($contents ne "\n")) {
- dump_section($file, $section, $contents);
- $section = $section_default;
- $contents = "";
- }
- $state = STATE_PROTO;
- $inline_doc_state = STATE_INLINE_NA;
- # Regular text
- } elsif (/$doc_content/) {
- if ($inline_doc_state == STATE_INLINE_TEXT) {
- $contents .= $1 . "\n";
- # nuke leading blank lines
- if ($contents =~ /^\s*$/) {
- $contents = "";
- }
- } elsif ($inline_doc_state == STATE_INLINE_NAME) {
- $inline_doc_state = STATE_INLINE_ERROR;
- emit_warning("${file}:$.", "Incorrect use of kernel-doc format: $_");
- }
- }
-}
-
-
-sub process_file($) {
- my $file;
- my ($orig_file) = @_;
-
- $file = map_filename($orig_file);
-
- if (!open(IN_FILE,"<$file")) {
- print STDERR "Error: Cannot open file $file\n";
- ++$errors;
- return;
- }
-
- $. = 1;
-
- $section_counter = 0;
- while (<IN_FILE>) {
- while (!/^ \*/ && s/\\\s*$//) {
- $_ .= <IN_FILE>;
- }
- # Replace tabs by spaces
- while ($_ =~ s/\t+/' ' x (length($&) * 8 - length($`) % 8)/e) {};
- # Hand this line to the appropriate state handler
- if ($state == STATE_NORMAL) {
- process_normal();
- } elsif ($state == STATE_NAME) {
- process_name($file, $_);
- } elsif ($state == STATE_BODY || $state == STATE_BODY_MAYBE ||
- $state == STATE_BODY_WITH_BLANK_LINE) {
- process_body($file, $_);
- } elsif ($state == STATE_INLINE) { # scanning for inline parameters
- process_inline($file, $_);
- } elsif ($state == STATE_PROTO) {
- process_proto($file, $_);
- } elsif ($state == STATE_DOCBLOCK) {
- process_docblock($file, $_);
- }
- }
-
- # Make sure we got something interesting.
- if (!$section_counter && $output_mode ne "none") {
- if ($output_selection == OUTPUT_INCLUDE) {
- emit_warning("${file}:1", "'$_' not found\n")
- for keys %function_table;
- } else {
- emit_warning("${file}:1", "no structured comments found\n");
- }
- }
- close IN_FILE;
-}
-
-$kernelversion = get_kernel_version();
-
-# generate a sequence of code that will splice in highlighting information
-# using the s// operator.
-for (my $k = 0; $k < @highlights; $k++) {
- my $pattern = $highlights[$k][0];
- my $result = $highlights[$k][1];
-# print STDERR "scanning pattern:$pattern, highlight:($result)\n";
- $dohighlight .= "\$contents =~ s:$pattern:$result:gs;\n";
-}
-
-if ($output_selection == OUTPUT_EXPORTED ||
- $output_selection == OUTPUT_INTERNAL) {
-
- push(@export_file_list, @ARGV);
-
- foreach (@export_file_list) {
- chomp;
- process_export_file($_);
- }
-}
-
-foreach (@ARGV) {
- chomp;
- process_file($_);
-}
-if ($verbose && $errors) {
- print STDERR "$errors errors\n";
-}
-if ($verbose && $warnings) {
- print STDERR "$warnings warnings\n";
-}
-
-if ($Werror && $warnings) {
- print STDERR "$warnings warnings as Errors\n";
- exit($warnings);
-} else {
- exit($output_mode eq "none" ? 0 : $errors)
-}
-
-__END__
-
-=head1 OPTIONS
-
-=head2 Output format selection (mutually exclusive):
-
-=over 8
-
-=item -man
-
-Output troff manual page format.
-
-=item -rst
-
-Output reStructuredText format. This is the default.
-
-=item -none
-
-Do not output documentation, only warnings.
-
-=back
-
-=head2 Output format modifiers
-
-=head3 reStructuredText only
-
-=head2 Output selection (mutually exclusive):
-
-=over 8
-
-=item -export
-
-Only output documentation for the symbols that have been exported using
-EXPORT_SYMBOL() and related macros in any input FILE or -export-file FILE.
-
-=item -internal
-
-Only output documentation for the symbols that have NOT been exported using
-EXPORT_SYMBOL() and related macros in any input FILE or -export-file FILE.
-
-=item -function NAME
-
-Only output documentation for the given function or DOC: section title.
-All other functions and DOC: sections are ignored.
-
-May be specified multiple times.
-
-=item -nosymbol NAME
-
-Exclude the specified symbol from the output documentation.
-
-May be specified multiple times.
-
-=back
-
-=head2 Output selection modifiers:
-
-=over 8
-
-=item -no-doc-sections
-
-Do not output DOC: sections.
-
-=item -export-file FILE
-
-Specify an additional FILE in which to look for EXPORT_SYMBOL information.
-
-To be used with -export or -internal.
-
-May be specified multiple times.
-
-=back
-
-=head3 reStructuredText only
-
-=over 8
-
-=item -enable-lineno
-
-Enable output of .. LINENO lines.
-
-=back
-
-=head2 Other parameters:
-
-=over 8
-
-=item -h, -help
-
-Print this help.
-
-=item -v
-
-Verbose output, more warnings and other information.
-
-=item -Werror
-
-Treat warnings as errors.
-
-=back
-
-=cut
diff --git a/scripts/kernel-doc.py b/scripts/kernel-doc.py
deleted file mode 100755
index d9fe2bcbd39c..000000000000
--- a/scripts/kernel-doc.py
+++ /dev/null
@@ -1,339 +0,0 @@
-#!/usr/bin/env python3
-# SPDX-License-Identifier: GPL-2.0
-# Copyright(c) 2025: Mauro Carvalho Chehab <mchehab@kernel.org>.
-#
-# pylint: disable=C0103,R0912,R0914,R0915
-
-# NOTE: While kernel-doc requires at least version 3.6 to run, the
-# command line should work with Python 3.2+ (tested with 3.4).
-# The rationale is that it shall fail gracefully during Kernel
-# compilation with older Kernel versions. Due to that:
-# - encoding line is needed here;
-# - no f-strings can be used on this file.
-# - the libraries that require newer versions can only be included
-# after Python version is checked.
-
-# Converted from the kernel-doc script originally written in Perl
-# under GPLv2, copyrighted since 1998 by the following authors:
-#
-# Aditya Srivastava <yashsri421@gmail.com>
-# Akira Yokosawa <akiyks@gmail.com>
-# Alexander A. Klimov <grandmaster@al2klimov.de>
-# Alexander Lobakin <aleksander.lobakin@intel.com>
-# André Almeida <andrealmeid@igalia.com>
-# Andy Shevchenko <andriy.shevchenko@linux.intel.com>
-# Anna-Maria Behnsen <anna-maria@linutronix.de>
-# Armin Kuster <akuster@mvista.com>
-# Bart Van Assche <bart.vanassche@sandisk.com>
-# Ben Hutchings <ben@decadent.org.uk>
-# Borislav Petkov <bbpetkov@yahoo.de>
-# Chen-Yu Tsai <wenst@chromium.org>
-# Coco Li <lixiaoyan@google.com>
-# Conchúr Navid <conchur@web.de>
-# Daniel Santos <daniel.santos@pobox.com>
-# Danilo Cesar Lemes de Paula <danilo.cesar@collabora.co.uk>
-# Dan Luedtke <mail@danrl.de>
-# Donald Hunter <donald.hunter@gmail.com>
-# Gabriel Krisman Bertazi <krisman@collabora.co.uk>
-# Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-# Harvey Harrison <harvey.harrison@gmail.com>
-# Horia Geanta <horia.geanta@freescale.com>
-# Ilya Dryomov <idryomov@gmail.com>
-# Jakub Kicinski <kuba@kernel.org>
-# Jani Nikula <jani.nikula@intel.com>
-# Jason Baron <jbaron@redhat.com>
-# Jason Gunthorpe <jgg@nvidia.com>
-# Jérémy Bobbio <lunar@debian.org>
-# Johannes Berg <johannes.berg@intel.com>
-# Johannes Weiner <hannes@cmpxchg.org>
-# Jonathan Cameron <Jonathan.Cameron@huawei.com>
-# Jonathan Corbet <corbet@lwn.net>
-# Jonathan Neuschäfer <j.neuschaefer@gmx.net>
-# Kamil Rytarowski <n54@gmx.com>
-# Kees Cook <kees@kernel.org>
-# Laurent Pinchart <laurent.pinchart@ideasonboard.com>
-# Levin, Alexander (Sasha Levin) <alexander.levin@verizon.com>
-# Linus Torvalds <torvalds@linux-foundation.org>
-# Lucas De Marchi <lucas.demarchi@profusion.mobi>
-# Mark Rutland <mark.rutland@arm.com>
-# Markus Heiser <markus.heiser@darmarit.de>
-# Martin Waitz <tali@admingilde.org>
-# Masahiro Yamada <masahiroy@kernel.org>
-# Matthew Wilcox <willy@infradead.org>
-# Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
-# Michal Wajdeczko <michal.wajdeczko@intel.com>
-# Michael Zucchi
-# Mike Rapoport <rppt@linux.ibm.com>
-# Niklas Söderlund <niklas.soderlund@corigine.com>
-# Nishanth Menon <nm@ti.com>
-# Paolo Bonzini <pbonzini@redhat.com>
-# Pavan Kumar Linga <pavan.kumar.linga@intel.com>
-# Pavel Pisa <pisa@cmp.felk.cvut.cz>
-# Peter Maydell <peter.maydell@linaro.org>
-# Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
-# Randy Dunlap <rdunlap@infradead.org>
-# Richard Kennedy <richard@rsk.demon.co.uk>
-# Rich Walker <rw@shadow.org.uk>
-# Rolf Eike Beer <eike-kernel@sf-tec.de>
-# Sakari Ailus <sakari.ailus@linux.intel.com>
-# Silvio Fricke <silvio.fricke@gmail.com>
-# Simon Huggins
-# Tim Waugh <twaugh@redhat.com>
-# Tomasz Warniełło <tomasz.warniello@gmail.com>
-# Utkarsh Tripathi <utripathi2002@gmail.com>
-# valdis.kletnieks@vt.edu <valdis.kletnieks@vt.edu>
-# Vegard Nossum <vegard.nossum@oracle.com>
-# Will Deacon <will.deacon@arm.com>
-# Yacine Belkadi <yacine.belkadi.1@gmail.com>
-# Yujie Liu <yujie.liu@intel.com>
-
-"""
-kernel_doc
-==========
-
-Print formatted kernel documentation to stdout
-
-Read C language source or header FILEs, extract embedded
-documentation comments, and print formatted documentation
-to standard output.
-
-The documentation comments are identified by the "/**"
-opening comment mark.
-
-See Documentation/doc-guide/kernel-doc.rst for the
-documentation comment syntax.
-"""
-
-import argparse
-import logging
-import os
-import sys
-
-# Import Python modules
-
-LIB_DIR = "lib/kdoc"
-SRC_DIR = os.path.dirname(os.path.realpath(__file__))
-
-sys.path.insert(0, os.path.join(SRC_DIR, LIB_DIR))
-
-DESC = """
-Read C language source or header FILEs, extract embedded documentation comments,
-and print formatted documentation to standard output.
-
-The documentation comments are identified by the "/**" opening comment mark.
-
-See Documentation/doc-guide/kernel-doc.rst for the documentation comment syntax.
-"""
-
-EXPORT_FILE_DESC = """
-Specify an additional FILE in which to look for EXPORT_SYMBOL information.
-
-May be used multiple times.
-"""
-
-EXPORT_DESC = """
-Only output documentation for the symbols that have been
-exported using EXPORT_SYMBOL() and related macros in any input
-FILE or -export-file FILE.
-"""
-
-INTERNAL_DESC = """
-Only output documentation for the symbols that have NOT been
-exported using EXPORT_SYMBOL() and related macros in any input
-FILE or -export-file FILE.
-"""
-
-FUNCTION_DESC = """
-Only output documentation for the given function or DOC: section
-title. All other functions and DOC: sections are ignored.
-
-May be used multiple times.
-"""
-
-NOSYMBOL_DESC = """
-Exclude the specified symbol from the output documentation.
-
-May be used multiple times.
-"""
-
-FILES_DESC = """
-Header and C source files to be parsed.
-"""
-
-WARN_CONTENTS_BEFORE_SECTIONS_DESC = """
-Warns if there are contents before sections (deprecated).
-
-This option is kept just for backward-compatibility, but it does nothing,
-neither here nor at the original Perl script.
-"""
-
-
-class MsgFormatter(logging.Formatter):
- """Helper class to format warnings on a similar way to kernel-doc.pl"""
-
- def format(self, record):
- record.levelname = record.levelname.capitalize()
- return logging.Formatter.format(self, record)
-
-def main():
- """Main program"""
-
- parser = argparse.ArgumentParser(formatter_class=argparse.RawTextHelpFormatter,
- description=DESC)
-
- # Normal arguments
-
- parser.add_argument("-v", "-verbose", "--verbose", action="store_true",
- help="Verbose output, more warnings and other information.")
-
- parser.add_argument("-d", "-debug", "--debug", action="store_true",
- help="Enable debug messages")
-
- parser.add_argument("-M", "-modulename", "--modulename",
- default="Kernel API",
- help="Allow setting a module name at the output.")
-
- parser.add_argument("-l", "-enable-lineno", "--enable_lineno",
- action="store_true",
- help="Enable line number output (only in ReST mode)")
-
- # Arguments to control the warning behavior
-
- parser.add_argument("-Wreturn", "--wreturn", action="store_true",
- help="Warns about the lack of a return markup on functions.")
-
- parser.add_argument("-Wshort-desc", "-Wshort-description", "--wshort-desc",
- action="store_true",
- help="Warns if initial short description is missing")
-
- parser.add_argument("-Wcontents-before-sections",
- "--wcontents-before-sections", action="store_true",
- help=WARN_CONTENTS_BEFORE_SECTIONS_DESC)
-
- parser.add_argument("-Wall", "--wall", action="store_true",
- help="Enable all types of warnings")
-
- parser.add_argument("-Werror", "--werror", action="store_true",
- help="Treat warnings as errors.")
-
- parser.add_argument("-export-file", "--export-file", action='append',
- help=EXPORT_FILE_DESC)
-
- # Output format mutually-exclusive group
-
- out_group = parser.add_argument_group("Output format selection (mutually exclusive)")
-
- out_fmt = out_group.add_mutually_exclusive_group()
-
- out_fmt.add_argument("-m", "-man", "--man", action="store_true",
- help="Output troff manual page format.")
- out_fmt.add_argument("-r", "-rst", "--rst", action="store_true",
- help="Output reStructuredText format (default).")
- out_fmt.add_argument("-N", "-none", "--none", action="store_true",
- help="Do not output documentation, only warnings.")
-
- # Output selection mutually-exclusive group
-
- sel_group = parser.add_argument_group("Output selection (mutually exclusive)")
- sel_mut = sel_group.add_mutually_exclusive_group()
-
- sel_mut.add_argument("-e", "-export", "--export", action='store_true',
- help=EXPORT_DESC)
-
- sel_mut.add_argument("-i", "-internal", "--internal", action='store_true',
- help=INTERNAL_DESC)
-
- sel_mut.add_argument("-s", "-function", "--symbol", action='append',
- help=FUNCTION_DESC)
-
- # Those are valid for all 3 types of filter
- parser.add_argument("-n", "-nosymbol", "--nosymbol", action='append',
- help=NOSYMBOL_DESC)
-
- parser.add_argument("-D", "-no-doc-sections", "--no-doc-sections",
- action='store_true', help="Don't outputt DOC sections")
-
- parser.add_argument("files", metavar="FILE",
- nargs="+", help=FILES_DESC)
-
- args = parser.parse_args()
-
- if args.wall:
- args.wreturn = True
- args.wshort_desc = True
- args.wcontents_before_sections = True
-
- logger = logging.getLogger()
-
- if not args.debug:
- logger.setLevel(logging.INFO)
- else:
- logger.setLevel(logging.DEBUG)
-
- formatter = MsgFormatter('%(levelname)s: %(message)s')
-
- handler = logging.StreamHandler()
- handler.setFormatter(formatter)
-
- logger.addHandler(handler)
-
- python_ver = sys.version_info[:2]
- if python_ver < (3,6):
- # Depending on Kernel configuration, kernel-doc --none is called at
- # build time. As we don't want to break compilation due to the
- # usage of an old Python version, return 0 here.
- if args.none:
- logger.error("Python 3.6 or later is required by kernel-doc. skipping checks")
- sys.exit(0)
-
- sys.exit("Python 3.6 or later is required by kernel-doc. Aborting.")
-
- if python_ver < (3,7):
- logger.warning("Python 3.7 or later is required for correct results")
-
- # Import kernel-doc libraries only after checking Python version
- from kdoc_files import KernelFiles # pylint: disable=C0415
- from kdoc_output import RestFormat, ManFormat # pylint: disable=C0415
-
- if args.man:
- out_style = ManFormat(modulename=args.modulename)
- elif args.none:
- out_style = None
- else:
- out_style = RestFormat()
-
- kfiles = KernelFiles(verbose=args.verbose,
- out_style=out_style, werror=args.werror,
- wreturn=args.wreturn, wshort_desc=args.wshort_desc,
- wcontents_before_sections=args.wcontents_before_sections)
-
- kfiles.parse(args.files, export_file=args.export_file)
-
- for t in kfiles.msg(enable_lineno=args.enable_lineno, export=args.export,
- internal=args.internal, symbol=args.symbol,
- nosymbol=args.nosymbol, export_file=args.export_file,
- no_doc_sections=args.no_doc_sections):
- msg = t[1]
- if msg:
- print(msg)
-
- error_count = kfiles.errors
- if not error_count:
- sys.exit(0)
-
- if args.werror:
- print("%s warnings as errors" % error_count) # pylint: disable=C0209
- sys.exit(error_count)
-
- if args.verbose:
- print("%s errors" % error_count) # pylint: disable=C0209
-
- if args.none:
- sys.exit(0)
-
- sys.exit(error_count)
-
-
-# Call main method
-if __name__ == "__main__":
- main()
diff --git a/scripts/lib/abi/abi_parser.py b/scripts/lib/abi/abi_parser.py
deleted file mode 100644
index 66a738013ce1..000000000000
--- a/scripts/lib/abi/abi_parser.py
+++ /dev/null
@@ -1,628 +0,0 @@
-#!/usr/bin/env python3
-# pylint: disable=R0902,R0903,R0911,R0912,R0913,R0914,R0915,R0917,C0302
-# Copyright(c) 2025: Mauro Carvalho Chehab <mchehab@kernel.org>.
-# SPDX-License-Identifier: GPL-2.0
-
-"""
-Parse ABI documentation and produce results from it.
-"""
-
-from argparse import Namespace
-import logging
-import os
-import re
-
-from pprint import pformat
-from random import randrange, seed
-
-# Import Python modules
-
-from helpers import AbiDebug, ABI_DIR
-
-
-class AbiParser:
- """Main class to parse ABI files"""
-
- TAGS = r"(what|where|date|kernelversion|contact|description|users)"
- XREF = r"(?:^|\s|\()(\/(?:sys|config|proc|dev|kvd)\/[^,.:;\)\s]+)(?:[,.:;\)\s]|\Z)"
-
- def __init__(self, directory, logger=None,
- enable_lineno=False, show_warnings=True, debug=0):
- """Stores arguments for the class and initialize class vars"""
-
- self.directory = directory
- self.enable_lineno = enable_lineno
- self.show_warnings = show_warnings
- self.debug = debug
-
- if not logger:
- self.log = logging.getLogger("get_abi")
- else:
- self.log = logger
-
- self.data = {}
- self.what_symbols = {}
- self.file_refs = {}
- self.what_refs = {}
-
- # Ignore files that contain such suffixes
- self.ignore_suffixes = (".rej", ".org", ".orig", ".bak", "~")
-
- # Regular expressions used on parser
- self.re_abi_dir = re.compile(r"(.*)" + ABI_DIR)
- self.re_tag = re.compile(r"(\S+)(:\s*)(.*)", re.I)
- self.re_valid = re.compile(self.TAGS)
- self.re_start_spc = re.compile(r"(\s*)(\S.*)")
- self.re_whitespace = re.compile(r"^\s+")
-
- # Regular used on print
- self.re_what = re.compile(r"(\/?(?:[\w\-]+\/?){1,2})")
- self.re_escape = re.compile(r"([\.\x01-\x08\x0e-\x1f\x21-\x2f\x3a-\x40\x7b-\xff])")
- self.re_unprintable = re.compile(r"([\x00-\x2f\x3a-\x40\x5b-\x60\x7b-\xff]+)")
- self.re_title_mark = re.compile(r"\n[\-\*\=\^\~]+\n")
- self.re_doc = re.compile(r"Documentation/(?!devicetree)(\S+)\.rst")
- self.re_abi = re.compile(r"(Documentation/ABI/)([\w\/\-]+)")
- self.re_xref_node = re.compile(self.XREF)
-
- def warn(self, fdata, msg, extra=None):
- """Displays a parse error if warning is enabled"""
-
- if not self.show_warnings:
- return
-
- msg = f"{fdata.fname}:{fdata.ln}: {msg}"
- if extra:
- msg += "\n\t\t" + extra
-
- self.log.warning(msg)
-
- def add_symbol(self, what, fname, ln=None, xref=None):
- """Create a reference table describing where each 'what' is located"""
-
- if what not in self.what_symbols:
- self.what_symbols[what] = {"file": {}}
-
- if fname not in self.what_symbols[what]["file"]:
- self.what_symbols[what]["file"][fname] = []
-
- if ln and ln not in self.what_symbols[what]["file"][fname]:
- self.what_symbols[what]["file"][fname].append(ln)
-
- if xref:
- self.what_symbols[what]["xref"] = xref
-
- def _parse_line(self, fdata, line):
- """Parse a single line of an ABI file"""
-
- new_what = False
- new_tag = False
- content = None
-
- match = self.re_tag.match(line)
- if match:
- new = match.group(1).lower()
- sep = match.group(2)
- content = match.group(3)
-
- match = self.re_valid.search(new)
- if match:
- new_tag = match.group(1)
- else:
- if fdata.tag == "description":
- # New "tag" is actually part of description.
- # Don't consider it a tag
- new_tag = False
- elif fdata.tag != "":
- self.warn(fdata, f"tag '{fdata.tag}' is invalid", line)
-
- if new_tag:
- # "where" is Invalid, but was a common mistake. Warn if found
- if new_tag == "where":
- self.warn(fdata, "tag 'Where' is invalid. Should be 'What:' instead")
- new_tag = "what"
-
- if new_tag == "what":
- fdata.space = None
-
- if content not in self.what_symbols:
- self.add_symbol(what=content, fname=fdata.fname, ln=fdata.ln)
-
- if fdata.tag == "what":
- fdata.what.append(content.strip("\n"))
- else:
- if fdata.key:
- if "description" not in self.data.get(fdata.key, {}):
- self.warn(fdata, f"{fdata.key} doesn't have a description")
-
- for w in fdata.what:
- self.add_symbol(what=w, fname=fdata.fname,
- ln=fdata.what_ln, xref=fdata.key)
-
- fdata.label = content
- new_what = True
-
- key = "abi_" + content.lower()
- fdata.key = self.re_unprintable.sub("_", key).strip("_")
-
- # Avoid duplicated keys but using a defined seed, to make
- # the namespace identical if there aren't changes at the
- # ABI symbols
- seed(42)
-
- while fdata.key in self.data:
- char = randrange(0, 51) + ord("A")
- if char > ord("Z"):
- char += ord("a") - ord("Z") - 1
-
- fdata.key += chr(char)
-
- if fdata.key and fdata.key not in self.data:
- self.data[fdata.key] = {
- "what": [content],
- "file": [fdata.file_ref],
- "path": fdata.ftype,
- "line_no": fdata.ln,
- }
-
- fdata.what = self.data[fdata.key]["what"]
-
- self.what_refs[content] = fdata.key
- fdata.tag = new_tag
- fdata.what_ln = fdata.ln
-
- if fdata.nametag["what"]:
- t = (content, fdata.key)
- if t not in fdata.nametag["symbols"]:
- fdata.nametag["symbols"].append(t)
-
- return
-
- if fdata.tag and new_tag:
- fdata.tag = new_tag
-
- if new_what:
- fdata.label = ""
-
- if "description" in self.data[fdata.key]:
- self.data[fdata.key]["description"] += "\n\n"
-
- if fdata.file_ref not in self.data[fdata.key]["file"]:
- self.data[fdata.key]["file"].append(fdata.file_ref)
-
- if self.debug == AbiDebug.WHAT_PARSING:
- self.log.debug("what: %s", fdata.what)
-
- if not fdata.what:
- self.warn(fdata, "'What:' should come first:", line)
- return
-
- if new_tag == "description":
- fdata.space = None
-
- if content:
- sep = sep.replace(":", " ")
-
- c = " " * len(new_tag) + sep + content
- c = c.expandtabs()
-
- match = self.re_start_spc.match(c)
- if match:
- # Preserve initial spaces for the first line
- fdata.space = match.group(1)
- content = match.group(2) + "\n"
-
- self.data[fdata.key][fdata.tag] = content
-
- return
-
- # Store any contents before tags at the database
- if not fdata.tag and "what" in fdata.nametag:
- fdata.nametag["description"] += line
- return
-
- if fdata.tag == "description":
- content = line.expandtabs()
-
- if self.re_whitespace.sub("", content) == "":
- self.data[fdata.key][fdata.tag] += "\n"
- return
-
- if fdata.space is None:
- match = self.re_start_spc.match(content)
- if match:
- # Preserve initial spaces for the first line
- fdata.space = match.group(1)
-
- content = match.group(2) + "\n"
- else:
- if content.startswith(fdata.space):
- content = content[len(fdata.space):]
-
- else:
- fdata.space = ""
-
- if fdata.tag == "what":
- w = content.strip("\n")
- if w:
- self.data[fdata.key][fdata.tag].append(w)
- else:
- self.data[fdata.key][fdata.tag] += content
- return
-
- content = line.strip()
- if fdata.tag:
- if fdata.tag == "what":
- w = content.strip("\n")
- if w:
- self.data[fdata.key][fdata.tag].append(w)
- else:
- self.data[fdata.key][fdata.tag] += "\n" + content.rstrip("\n")
- return
-
- # Everything else is error
- if content:
- self.warn(fdata, "Unexpected content", line)
-
- def parse_readme(self, nametag, fname):
- """Parse ABI README file"""
-
- nametag["what"] = ["Introduction"]
- nametag["path"] = "README"
- with open(fname, "r", encoding="utf8", errors="backslashreplace") as fp:
- for line in fp:
- match = self.re_tag.match(line)
- if match:
- new = match.group(1).lower()
-
- match = self.re_valid.search(new)
- if match:
- nametag["description"] += "\n:" + line
- continue
-
- nametag["description"] += line
-
- def parse_file(self, fname, path, basename):
- """Parse a single file"""
-
- ref = f"abi_file_{path}_{basename}"
- ref = self.re_unprintable.sub("_", ref).strip("_")
-
- # Store per-file state into a namespace variable. This will be used
- # by the per-line parser state machine and by the warning function.
- fdata = Namespace
-
- fdata.fname = fname
- fdata.name = basename
-
- pos = fname.find(ABI_DIR)
- if pos > 0:
- f = fname[pos:]
- else:
- f = fname
-
- fdata.file_ref = (f, ref)
- self.file_refs[f] = ref
-
- fdata.ln = 0
- fdata.what_ln = 0
- fdata.tag = ""
- fdata.label = ""
- fdata.what = []
- fdata.key = None
- fdata.xrefs = None
- fdata.space = None
- fdata.ftype = path.split("/")[0]
-
- fdata.nametag = {}
- fdata.nametag["what"] = [f"ABI file {path}/{basename}"]
- fdata.nametag["type"] = "File"
- fdata.nametag["path"] = fdata.ftype
- fdata.nametag["file"] = [fdata.file_ref]
- fdata.nametag["line_no"] = 1
- fdata.nametag["description"] = ""
- fdata.nametag["symbols"] = []
-
- self.data[ref] = fdata.nametag
-
- if self.debug & AbiDebug.WHAT_OPEN:
- self.log.debug("Opening file %s", fname)
-
- if basename == "README":
- self.parse_readme(fdata.nametag, fname)
- return
-
- with open(fname, "r", encoding="utf8", errors="backslashreplace") as fp:
- for line in fp:
- fdata.ln += 1
-
- self._parse_line(fdata, line)
-
- if "description" in fdata.nametag:
- fdata.nametag["description"] = fdata.nametag["description"].lstrip("\n")
-
- if fdata.key:
- if "description" not in self.data.get(fdata.key, {}):
- self.warn(fdata, f"{fdata.key} doesn't have a description")
-
- for w in fdata.what:
- self.add_symbol(what=w, fname=fname, xref=fdata.key)
-
- def _parse_abi(self, root=None):
- """Internal function to parse documentation ABI recursively"""
-
- if not root:
- root = self.directory
-
- with os.scandir(root) as obj:
- for entry in obj:
- name = os.path.join(root, entry.name)
-
- if entry.is_dir():
- self._parse_abi(name)
- continue
-
- if not entry.is_file():
- continue
-
- basename = os.path.basename(name)
-
- if basename.startswith("."):
- continue
-
- if basename.endswith(self.ignore_suffixes):
- continue
-
- path = self.re_abi_dir.sub("", os.path.dirname(name))
-
- self.parse_file(name, path, basename)
-
- def parse_abi(self, root=None):
- """Parse documentation ABI"""
-
- self._parse_abi(root)
-
- if self.debug & AbiDebug.DUMP_ABI_STRUCTS:
- self.log.debug(pformat(self.data))
-
- def desc_txt(self, desc):
- """Print description as found inside ABI files"""
-
- desc = desc.strip(" \t\n")
-
- return desc + "\n\n"
-
- def xref(self, fname):
- """
- Converts a Documentation/ABI + basename into a ReST cross-reference
- """
-
- xref = self.file_refs.get(fname)
- if not xref:
- return None
- else:
- return xref
-
- def desc_rst(self, desc):
- """Enrich ReST output by creating cross-references"""
-
- # Remove title markups from the description
- # Having titles inside ABI files will only work if extra
- # care would be taken in order to strictly follow the same
- # level order for each markup.
- desc = self.re_title_mark.sub("\n\n", "\n" + desc)
- desc = desc.rstrip(" \t\n").lstrip("\n")
-
- # Python's regex performance for non-compiled expressions is a lot
- # than Perl, as Perl automatically caches them at their
- # first usage. Here, we'll need to do the same, as otherwise the
- # performance penalty is be high
-
- new_desc = ""
- for d in desc.split("\n"):
- if d == "":
- new_desc += "\n"
- continue
-
- # Use cross-references for doc files where needed
- d = self.re_doc.sub(r":doc:`/\1`", d)
-
- # Use cross-references for ABI generated docs where needed
- matches = self.re_abi.findall(d)
- for m in matches:
- abi = m[0] + m[1]
-
- xref = self.file_refs.get(abi)
- if not xref:
- # This may happen if ABI is on a separate directory,
- # like parsing ABI testing and symbol is at stable.
- # The proper solution is to move this part of the code
- # for it to be inside sphinx/kernel_abi.py
- self.log.info("Didn't find ABI reference for '%s'", abi)
- else:
- new = self.re_escape.sub(r"\\\1", m[1])
- d = re.sub(fr"\b{abi}\b", f":ref:`{new} <{xref}>`", d)
-
- # Seek for cross reference symbols like /sys/...
- # Need to be careful to avoid doing it on a code block
- if d[0] not in [" ", "\t"]:
- matches = self.re_xref_node.findall(d)
- for m in matches:
- # Finding ABI here is more complex due to wildcards
- xref = self.what_refs.get(m)
- if xref:
- new = self.re_escape.sub(r"\\\1", m)
- d = re.sub(fr"\b{m}\b", f":ref:`{new} <{xref}>`", d)
-
- new_desc += d + "\n"
-
- return new_desc + "\n\n"
-
- def doc(self, output_in_txt=False, show_symbols=True, show_file=True,
- filter_path=None):
- """Print ABI at stdout"""
-
- part = None
- for key, v in sorted(self.data.items(),
- key=lambda x: (x[1].get("type", ""),
- x[1].get("what"))):
-
- wtype = v.get("type", "Symbol")
- file_ref = v.get("file")
- names = v.get("what", [""])
-
- if wtype == "File":
- if not show_file:
- continue
- else:
- if not show_symbols:
- continue
-
- if filter_path:
- if v.get("path") != filter_path:
- continue
-
- msg = ""
-
- if wtype != "File":
- cur_part = names[0]
- if cur_part.find("/") >= 0:
- match = self.re_what.match(cur_part)
- if match:
- symbol = match.group(1).rstrip("/")
- cur_part = "Symbols under " + symbol
-
- if cur_part and cur_part != part:
- part = cur_part
- msg += part + "\n"+ "-" * len(part) +"\n\n"
-
- msg += f".. _{key}:\n\n"
-
- max_len = 0
- for i in range(0, len(names)): # pylint: disable=C0200
- names[i] = "**" + self.re_escape.sub(r"\\\1", names[i]) + "**"
-
- max_len = max(max_len, len(names[i]))
-
- msg += "+-" + "-" * max_len + "-+\n"
- for name in names:
- msg += f"| {name}" + " " * (max_len - len(name)) + " |\n"
- msg += "+-" + "-" * max_len + "-+\n"
- msg += "\n"
-
- for ref in file_ref:
- if wtype == "File":
- msg += f".. _{ref[1]}:\n\n"
- else:
- base = os.path.basename(ref[0])
- msg += f"Defined on file :ref:`{base} <{ref[1]}>`\n\n"
-
- if wtype == "File":
- msg += names[0] +"\n" + "-" * len(names[0]) +"\n\n"
-
- desc = v.get("description")
- if not desc and wtype != "File":
- msg += f"DESCRIPTION MISSING for {names[0]}\n\n"
-
- if desc:
- if output_in_txt:
- msg += self.desc_txt(desc)
- else:
- msg += self.desc_rst(desc)
-
- symbols = v.get("symbols")
- if symbols:
- msg += "Has the following ABI:\n\n"
-
- for w, label in symbols:
- # Escape special chars from content
- content = self.re_escape.sub(r"\\\1", w)
-
- msg += f"- :ref:`{content} <{label}>`\n\n"
-
- users = v.get("users")
- if users and users.strip(" \t\n"):
- users = users.strip("\n").replace('\n', '\n\t')
- msg += f"Users:\n\t{users}\n\n"
-
- ln = v.get("line_no", 1)
-
- yield (msg, file_ref[0][0], ln)
-
- def check_issues(self):
- """Warn about duplicated ABI entries"""
-
- for what, v in self.what_symbols.items():
- files = v.get("file")
- if not files:
- # Should never happen if the parser works properly
- self.log.warning("%s doesn't have a file associated", what)
- continue
-
- if len(files) == 1:
- continue
-
- f = []
- for fname, lines in sorted(files.items()):
- if not lines:
- f.append(f"{fname}")
- elif len(lines) == 1:
- f.append(f"{fname}:{lines[0]}")
- else:
- m = fname + "lines "
- m += ", ".join(str(x) for x in lines)
- f.append(m)
-
- self.log.warning("%s is defined %d times: %s", what, len(f), "; ".join(f))
-
- def search_symbols(self, expr):
- """ Searches for ABI symbols """
-
- regex = re.compile(expr, re.I)
-
- found_keys = 0
- for t in sorted(self.data.items(), key=lambda x: [0]):
- v = t[1]
-
- wtype = v.get("type", "")
- if wtype == "File":
- continue
-
- for what in v.get("what", [""]):
- if regex.search(what):
- found_keys += 1
-
- kernelversion = v.get("kernelversion", "").strip(" \t\n")
- date = v.get("date", "").strip(" \t\n")
- contact = v.get("contact", "").strip(" \t\n")
- users = v.get("users", "").strip(" \t\n")
- desc = v.get("description", "").strip(" \t\n")
-
- files = []
- for f in v.get("file", ()):
- files.append(f[0])
-
- what = str(found_keys) + ". " + what
- title_tag = "-" * len(what)
-
- print(f"\n{what}\n{title_tag}\n")
-
- if kernelversion:
- print(f"Kernel version:\t\t{kernelversion}")
-
- if date:
- print(f"Date:\t\t\t{date}")
-
- if contact:
- print(f"Contact:\t\t{contact}")
-
- if users:
- print(f"Users:\t\t\t{users}")
-
- print("Defined on file(s):\t" + ", ".join(files))
-
- if desc:
- desc = desc.strip("\n")
- print(f"\n{desc}\n")
-
- if not found_keys:
- print(f"Regular expression /{expr}/ not found.")
diff --git a/scripts/lib/abi/abi_regex.py b/scripts/lib/abi/abi_regex.py
deleted file mode 100644
index 8a57846cbc69..000000000000
--- a/scripts/lib/abi/abi_regex.py
+++ /dev/null
@@ -1,234 +0,0 @@
-#!/usr/bin/env python3
-# xxpylint: disable=R0903
-# Copyright(c) 2025: Mauro Carvalho Chehab <mchehab@kernel.org>.
-# SPDX-License-Identifier: GPL-2.0
-
-"""
-Convert ABI what into regular expressions
-"""
-
-import re
-import sys
-
-from pprint import pformat
-
-from abi_parser import AbiParser
-from helpers import AbiDebug
-
-class AbiRegex(AbiParser):
- """Extends AbiParser to search ABI nodes with regular expressions"""
-
- # Escape only ASCII visible characters
- escape_symbols = r"([\x21-\x29\x2b-\x2d\x3a-\x40\x5c\x60\x7b-\x7e])"
- leave_others = "others"
-
- # Tuples with regular expressions to be compiled and replacement data
- re_whats = [
- # Drop escape characters that might exist
- (re.compile("\\\\"), ""),
-
- # Temporarily escape dot characters
- (re.compile(r"\."), "\xf6"),
-
- # Temporarily change [0-9]+ type of patterns
- (re.compile(r"\[0\-9\]\+"), "\xff"),
-
- # Temporarily change [\d+-\d+] type of patterns
- (re.compile(r"\[0\-\d+\]"), "\xff"),
- (re.compile(r"\[0:\d+\]"), "\xff"),
- (re.compile(r"\[(\d+)\]"), "\xf4\\\\d+\xf5"),
-
- # Temporarily change [0-9] type of patterns
- (re.compile(r"\[(\d)\-(\d)\]"), "\xf4\1-\2\xf5"),
-
- # Handle multiple option patterns
- (re.compile(r"[\{\<\[]([\w_]+)(?:[,|]+([\w_]+)){1,}[\}\>\]]"), r"(\1|\2)"),
-
- # Handle wildcards
- (re.compile(r"([^\/])\*"), "\\1\\\\w\xf7"),
- (re.compile(r"/\*/"), "/.*/"),
- (re.compile(r"/\xf6\xf6\xf6"), "/.*"),
- (re.compile(r"\<[^\>]+\>"), "\\\\w\xf7"),
- (re.compile(r"\{[^\}]+\}"), "\\\\w\xf7"),
- (re.compile(r"\[[^\]]+\]"), "\\\\w\xf7"),
-
- (re.compile(r"XX+"), "\\\\w\xf7"),
- (re.compile(r"([^A-Z])[XYZ]([^A-Z])"), "\\1\\\\w\xf7\\2"),
- (re.compile(r"([^A-Z])[XYZ]$"), "\\1\\\\w\xf7"),
- (re.compile(r"_[AB]_"), "_\\\\w\xf7_"),
-
- # Recover [0-9] type of patterns
- (re.compile(r"\xf4"), "["),
- (re.compile(r"\xf5"), "]"),
-
- # Remove duplicated spaces
- (re.compile(r"\s+"), r" "),
-
- # Special case: drop comparison as in:
- # What: foo = <something>
- # (this happens on a few IIO definitions)
- (re.compile(r"\s*\=.*$"), ""),
-
- # Escape all other symbols
- (re.compile(escape_symbols), r"\\\1"),
- (re.compile(r"\\\\"), r"\\"),
- (re.compile(r"\\([\[\]\(\)\|])"), r"\1"),
- (re.compile(r"(\d+)\\(-\d+)"), r"\1\2"),
-
- (re.compile(r"\xff"), r"\\d+"),
-
- # Special case: IIO ABI which a parenthesis.
- (re.compile(r"sqrt(.*)"), r"sqrt(.*)"),
-
- # Simplify regexes with multiple .*
- (re.compile(r"(?:\.\*){2,}"), ""),
-
- # Recover dot characters
- (re.compile(r"\xf6"), "\\."),
- # Recover plus characters
- (re.compile(r"\xf7"), "+"),
- ]
- re_has_num = re.compile(r"\\d")
-
- # Symbol name after escape_chars that are considered a devnode basename
- re_symbol_name = re.compile(r"(\w|\\[\.\-\:])+$")
-
- # List of popular group names to be skipped to minimize regex group size
- # Use AbiDebug.SUBGROUP_SIZE to detect those
- skip_names = set(["devices", "hwmon"])
-
- def regex_append(self, what, new):
- """
- Get a search group for a subset of regular expressions.
-
- As ABI may have thousands of symbols, using a for to search all
- regular expressions is at least O(n^2). When there are wildcards,
- the complexity increases substantially, eventually becoming exponential.
-
- To avoid spending too much time on them, use a logic to split
- them into groups. The smaller the group, the better, as it would
- mean that searches will be confined to a small number of regular
- expressions.
-
- The conversion to a regex subset is tricky, as we need something
- that can be easily obtained from the sysfs symbol and from the
- regular expression. So, we need to discard nodes that have
- wildcards.
-
- If it can't obtain a subgroup, place the regular expression inside
- a special group (self.leave_others).
- """
-
- search_group = None
-
- for search_group in reversed(new.split("/")):
- if not search_group or search_group in self.skip_names:
- continue
- if self.re_symbol_name.match(search_group):
- break
-
- if not search_group:
- search_group = self.leave_others
-
- if self.debug & AbiDebug.SUBGROUP_MAP:
- self.log.debug("%s: mapped as %s", what, search_group)
-
- try:
- if search_group not in self.regex_group:
- self.regex_group[search_group] = []
-
- self.regex_group[search_group].append(re.compile(new))
- if self.search_string:
- if what.find(self.search_string) >= 0:
- print(f"What: {what}")
- except re.PatternError:
- self.log.warning("Ignoring '%s' as it produced an invalid regex:\n"
- " '%s'", what, new)
-
- def get_regexes(self, what):
- """
- Given an ABI devnode, return a list of all regular expressions that
- may match it, based on the sub-groups created by regex_append()
- """
-
- re_list = []
-
- patches = what.split("/")
- patches.reverse()
- patches.append(self.leave_others)
-
- for search_group in patches:
- if search_group in self.regex_group:
- re_list += self.regex_group[search_group]
-
- return re_list
-
- def __init__(self, *args, **kwargs):
- """
- Override init method to get verbose argument
- """
-
- self.regex_group = None
- self.search_string = None
- self.re_string = None
-
- if "search_string" in kwargs:
- self.search_string = kwargs.get("search_string")
- del kwargs["search_string"]
-
- if self.search_string:
-
- try:
- self.re_string = re.compile(self.search_string)
- except re.PatternError as e:
- msg = f"{self.search_string} is not a valid regular expression"
- raise ValueError(msg) from e
-
- super().__init__(*args, **kwargs)
-
- def parse_abi(self, *args, **kwargs):
-
- super().parse_abi(*args, **kwargs)
-
- self.regex_group = {}
-
- print("Converting ABI What fields into regexes...", file=sys.stderr)
-
- for t in sorted(self.data.items(), key=lambda x: x[0]):
- v = t[1]
- if v.get("type") == "File":
- continue
-
- v["regex"] = []
-
- for what in v.get("what", []):
- if not what.startswith("/sys"):
- continue
-
- new = what
- for r, s in self.re_whats:
- try:
- new = r.sub(s, new)
- except re.PatternError as e:
- # Help debugging troubles with new regexes
- raise re.PatternError(f"{e}\nwhile re.sub('{r.pattern}', {s}, str)") from e
-
- v["regex"].append(new)
-
- if self.debug & AbiDebug.REGEX:
- self.log.debug("%-90s <== %s", new, what)
-
- # Store regex into a subgroup to speedup searches
- self.regex_append(what, new)
-
- if self.debug & AbiDebug.SUBGROUP_DICT:
- self.log.debug("%s", pformat(self.regex_group))
-
- if self.debug & AbiDebug.SUBGROUP_SIZE:
- biggestd_keys = sorted(self.regex_group.keys(),
- key= lambda k: len(self.regex_group[k]),
- reverse=True)
-
- print("Top regex subgroups:", file=sys.stderr)
- for k in biggestd_keys[:10]:
- print(f"{k} has {len(self.regex_group[k])} elements", file=sys.stderr)
diff --git a/scripts/lib/abi/helpers.py b/scripts/lib/abi/helpers.py
deleted file mode 100644
index 639b23e4ca33..000000000000
--- a/scripts/lib/abi/helpers.py
+++ /dev/null
@@ -1,38 +0,0 @@
-#!/usr/bin/env python3
-# Copyright(c) 2025: Mauro Carvalho Chehab <mchehab@kernel.org>.
-# pylint: disable=R0903
-# SPDX-License-Identifier: GPL-2.0
-
-"""
-Helper classes for ABI parser
-"""
-
-ABI_DIR = "Documentation/ABI/"
-
-
-class AbiDebug:
- """Debug levels"""
-
- WHAT_PARSING = 1
- WHAT_OPEN = 2
- DUMP_ABI_STRUCTS = 4
- UNDEFINED = 8
- REGEX = 16
- SUBGROUP_MAP = 32
- SUBGROUP_DICT = 64
- SUBGROUP_SIZE = 128
- GRAPH = 256
-
-
-DEBUG_HELP = """
-1 - enable debug parsing logic
-2 - enable debug messages on file open
-4 - enable debug for ABI parse data
-8 - enable extra debug information to identify troubles
- with ABI symbols found at the local machine that
- weren't found on ABI documentation (used only for
- undefined subcommand)
-16 - enable debug for what to regex conversion
-32 - enable debug for symbol regex subgroups
-64 - enable debug for sysfs graph tree variable
-"""
diff --git a/scripts/lib/abi/system_symbols.py b/scripts/lib/abi/system_symbols.py
deleted file mode 100644
index f15c94a6e33c..000000000000
--- a/scripts/lib/abi/system_symbols.py
+++ /dev/null
@@ -1,378 +0,0 @@
-#!/usr/bin/env python3
-# pylint: disable=R0902,R0912,R0914,R0915,R1702
-# Copyright(c) 2025: Mauro Carvalho Chehab <mchehab@kernel.org>.
-# SPDX-License-Identifier: GPL-2.0
-
-"""
-Parse ABI documentation and produce results from it.
-"""
-
-import os
-import re
-import sys
-
-from concurrent import futures
-from datetime import datetime
-from random import shuffle
-
-from helpers import AbiDebug
-
-class SystemSymbols:
- """Stores arguments for the class and initialize class vars"""
-
- def graph_add_file(self, path, link=None):
- """
- add a file path to the sysfs graph stored at self.root
- """
-
- if path in self.files:
- return
-
- name = ""
- ref = self.root
- for edge in path.split("/"):
- name += edge + "/"
- if edge not in ref:
- ref[edge] = {"__name": [name.rstrip("/")]}
-
- ref = ref[edge]
-
- if link and link not in ref["__name"]:
- ref["__name"].append(link.rstrip("/"))
-
- self.files.add(path)
-
- def print_graph(self, root_prefix="", root=None, level=0):
- """Prints a reference tree graph using UTF-8 characters"""
-
- if not root:
- root = self.root
- level = 0
-
- # Prevent endless traverse
- if level > 5:
- return
-
- if level > 0:
- prefix = "├──"
- last_prefix = "└──"
- else:
- prefix = ""
- last_prefix = ""
-
- items = list(root.items())
-
- names = root.get("__name", [])
- for k, edge in items:
- if k == "__name":
- continue
-
- if not k:
- k = "/"
-
- if len(names) > 1:
- k += " links: " + ",".join(names[1:])
-
- if edge == items[-1][1]:
- print(root_prefix + last_prefix + k)
- p = root_prefix
- if level > 0:
- p += " "
- self.print_graph(p, edge, level + 1)
- else:
- print(root_prefix + prefix + k)
- p = root_prefix + "│ "
- self.print_graph(p, edge, level + 1)
-
- def _walk(self, root):
- """
- Walk through sysfs to get all devnodes that aren't ignored.
-
- By default, uses /sys as sysfs mounting point. If another
- directory is used, it replaces them to /sys at the patches.
- """
-
- with os.scandir(root) as obj:
- for entry in obj:
- path = os.path.join(root, entry.name)
- if self.sysfs:
- p = path.replace(self.sysfs, "/sys", count=1)
- else:
- p = path
-
- if self.re_ignore.search(p):
- return
-
- # Handle link first to avoid directory recursion
- if entry.is_symlink():
- real = os.path.realpath(path)
- if not self.sysfs:
- self.aliases[path] = real
- else:
- real = real.replace(self.sysfs, "/sys", count=1)
-
- # Add absfile location to graph if it doesn't exist
- if not self.re_ignore.search(real):
- # Add link to the graph
- self.graph_add_file(real, p)
-
- elif entry.is_file():
- self.graph_add_file(p)
-
- elif entry.is_dir():
- self._walk(path)
-
- def __init__(self, abi, sysfs="/sys", hints=False):
- """
- Initialize internal variables and get a list of all files inside
- sysfs that can currently be parsed.
-
- Please notice that there are several entries on sysfs that aren't
- documented as ABI. Ignore those.
-
- The real paths will be stored under self.files. Aliases will be
- stored in separate, as self.aliases.
- """
-
- self.abi = abi
- self.log = abi.log
-
- if sysfs != "/sys":
- self.sysfs = sysfs.rstrip("/")
- else:
- self.sysfs = None
-
- self.hints = hints
-
- self.root = {}
- self.aliases = {}
- self.files = set()
-
- dont_walk = [
- # Those require root access and aren't documented at ABI
- f"^{sysfs}/kernel/debug",
- f"^{sysfs}/kernel/tracing",
- f"^{sysfs}/fs/pstore",
- f"^{sysfs}/fs/bpf",
- f"^{sysfs}/fs/fuse",
-
- # This is not documented at ABI
- f"^{sysfs}/module",
-
- f"^{sysfs}/fs/cgroup", # this is big and has zero docs under ABI
- f"^{sysfs}/firmware", # documented elsewhere: ACPI, DT bindings
- "sections|notes", # aren't actually part of ABI
-
- # kernel-parameters.txt - not easy to parse
- "parameters",
- ]
-
- self.re_ignore = re.compile("|".join(dont_walk))
-
- print(f"Reading {sysfs} directory contents...", file=sys.stderr)
- self._walk(sysfs)
-
- def check_file(self, refs, found):
- """Check missing ABI symbols for a given sysfs file"""
-
- res_list = []
-
- try:
- for names in refs:
- fname = names[0]
-
- res = {
- "found": False,
- "fname": fname,
- "msg": "",
- }
- res_list.append(res)
-
- re_what = self.abi.get_regexes(fname)
- if not re_what:
- self.abi.log.warning(f"missing rules for {fname}")
- continue
-
- for name in names:
- for r in re_what:
- if self.abi.debug & AbiDebug.UNDEFINED:
- self.log.debug("check if %s matches '%s'", name, r.pattern)
- if r.match(name):
- res["found"] = True
- if found:
- res["msg"] += f" {fname}: regex:\n\t"
- continue
-
- if self.hints and not res["found"]:
- res["msg"] += f" {fname} not found. Tested regexes:\n"
- for r in re_what:
- res["msg"] += " " + r.pattern + "\n"
-
- except KeyboardInterrupt:
- pass
-
- return res_list
-
- def _ref_interactor(self, root):
- """Recursive function to interact over the sysfs tree"""
-
- for k, v in root.items():
- if isinstance(v, dict):
- yield from self._ref_interactor(v)
-
- if root == self.root or k == "__name":
- continue
-
- if self.abi.re_string:
- fname = v["__name"][0]
- if self.abi.re_string.search(fname):
- yield v
- else:
- yield v
-
-
- def get_fileref(self, all_refs, chunk_size):
- """Interactor to group refs into chunks"""
-
- n = 0
- refs = []
-
- for ref in all_refs:
- refs.append(ref)
-
- n += 1
- if n >= chunk_size:
- yield refs
- n = 0
- refs = []
-
- yield refs
-
- def check_undefined_symbols(self, max_workers=None, chunk_size=50,
- found=None, dry_run=None):
- """Seach ABI for sysfs symbols missing documentation"""
-
- self.abi.parse_abi()
-
- if self.abi.debug & AbiDebug.GRAPH:
- self.print_graph()
-
- all_refs = []
- for ref in self._ref_interactor(self.root):
- all_refs.append(ref["__name"])
-
- if dry_run:
- print("Would check", file=sys.stderr)
- for ref in all_refs:
- print(", ".join(ref))
-
- return
-
- print("Starting to search symbols (it may take several minutes):",
- file=sys.stderr)
- start = datetime.now()
- old_elapsed = None
-
- # Python doesn't support multithreading due to limitations on its
- # global lock (GIL). While Python 3.13 finally made GIL optional,
- # there are still issues related to it. Also, we want to have
- # backward compatibility with older versions of Python.
- #
- # So, use instead multiprocess. However, Python is very slow passing
- # data from/to multiple processes. Also, it may consume lots of memory
- # if the data to be shared is not small. So, we need to group workload
- # in chunks that are big enough to generate performance gains while
- # not being so big that would cause out-of-memory.
-
- num_refs = len(all_refs)
- print(f"Number of references to parse: {num_refs}", file=sys.stderr)
-
- if not max_workers:
- max_workers = os.cpu_count()
- elif max_workers > os.cpu_count():
- max_workers = os.cpu_count()
-
- max_workers = max(max_workers, 1)
-
- max_chunk_size = int((num_refs + max_workers - 1) / max_workers)
- chunk_size = min(chunk_size, max_chunk_size)
- chunk_size = max(1, chunk_size)
-
- if max_workers > 1:
- executor = futures.ProcessPoolExecutor
-
- # Place references in a random order. This may help improving
- # performance, by mixing complex/simple expressions when creating
- # chunks
- shuffle(all_refs)
- else:
- # Python has a high overhead with processes. When there's just
- # one worker, it is faster to not create a new process.
- # Yet, User still deserves to have a progress print. So, use
- # python's "thread", which is actually a single process, using
- # an internal schedule to switch between tasks. No performance
- # gains for non-IO tasks, but still it can be quickly interrupted
- # from time to time to display progress.
- executor = futures.ThreadPoolExecutor
-
- not_found = []
- f_list = []
- with executor(max_workers=max_workers) as exe:
- for refs in self.get_fileref(all_refs, chunk_size):
- if refs:
- try:
- f_list.append(exe.submit(self.check_file, refs, found))
-
- except KeyboardInterrupt:
- return
-
- total = len(f_list)
-
- if not total:
- if self.abi.re_string:
- print(f"No ABI symbol matches {self.abi.search_string}")
- else:
- self.abi.log.warning("No ABI symbols found")
- return
-
- print(f"{len(f_list):6d} jobs queued on {max_workers} workers",
- file=sys.stderr)
-
- while f_list:
- try:
- t = futures.wait(f_list, timeout=1,
- return_when=futures.FIRST_COMPLETED)
-
- done = t[0]
-
- for fut in done:
- res_list = fut.result()
-
- for res in res_list:
- if not res["found"]:
- not_found.append(res["fname"])
- if res["msg"]:
- print(res["msg"])
-
- f_list.remove(fut)
- except KeyboardInterrupt:
- return
-
- except RuntimeError as e:
- self.abi.log.warning(f"Future: {e}")
- break
-
- if sys.stderr.isatty():
- elapsed = str(datetime.now() - start).split(".", maxsplit=1)[0]
- if len(f_list) < total:
- elapsed += f" ({total - len(f_list)}/{total} jobs completed). "
- if elapsed != old_elapsed:
- print(elapsed + "\r", end="", flush=True,
- file=sys.stderr)
- old_elapsed = elapsed
-
- elapsed = str(datetime.now() - start).split(".", maxsplit=1)[0]
- print(elapsed, file=sys.stderr)
-
- for f in sorted(not_found):
- print(f"{f} not found.")
diff --git a/scripts/lib/kdoc/kdoc_files.py b/scripts/lib/kdoc/kdoc_files.py
deleted file mode 100644
index 9e09b45b02fa..000000000000
--- a/scripts/lib/kdoc/kdoc_files.py
+++ /dev/null
@@ -1,291 +0,0 @@
-#!/usr/bin/env python3
-# SPDX-License-Identifier: GPL-2.0
-# Copyright(c) 2025: Mauro Carvalho Chehab <mchehab@kernel.org>.
-#
-# pylint: disable=R0903,R0913,R0914,R0917
-
-"""
-Parse lernel-doc tags on multiple kernel source files.
-"""
-
-import argparse
-import logging
-import os
-import re
-
-from kdoc_parser import KernelDoc
-from kdoc_output import OutputFormat
-
-
-class GlobSourceFiles:
- """
- Parse C source code file names and directories via an Interactor.
- """
-
- def __init__(self, srctree=None, valid_extensions=None):
- """
- Initialize valid extensions with a tuple.
-
- If not defined, assume default C extensions (.c and .h)
-
- It would be possible to use python's glob function, but it is
- very slow, and it is not interactive. So, it would wait to read all
- directories before actually do something.
-
- So, let's use our own implementation.
- """
-
- if not valid_extensions:
- self.extensions = (".c", ".h")
- else:
- self.extensions = valid_extensions
-
- self.srctree = srctree
-
- def _parse_dir(self, dirname):
- """Internal function to parse files recursively"""
-
- with os.scandir(dirname) as obj:
- for entry in obj:
- name = os.path.join(dirname, entry.name)
-
- if entry.is_dir():
- yield from self._parse_dir(name)
-
- if not entry.is_file():
- continue
-
- basename = os.path.basename(name)
-
- if not basename.endswith(self.extensions):
- continue
-
- yield name
-
- def parse_files(self, file_list, file_not_found_cb):
- """
- Define an interator to parse all source files from file_list,
- handling directories if any
- """
-
- if not file_list:
- return
-
- for fname in file_list:
- if self.srctree:
- f = os.path.join(self.srctree, fname)
- else:
- f = fname
-
- if os.path.isdir(f):
- yield from self._parse_dir(f)
- elif os.path.isfile(f):
- yield f
- elif file_not_found_cb:
- file_not_found_cb(fname)
-
-
-class KernelFiles():
- """
- Parse kernel-doc tags on multiple kernel source files.
-
- There are two type of parsers defined here:
- - self.parse_file(): parses both kernel-doc markups and
- EXPORT_SYMBOL* macros;
- - self.process_export_file(): parses only EXPORT_SYMBOL* macros.
- """
-
- def warning(self, msg):
- """Ancillary routine to output a warning and increment error count"""
-
- self.config.log.warning(msg)
- self.errors += 1
-
- def error(self, msg):
- """Ancillary routine to output an error and increment error count"""
-
- self.config.log.error(msg)
- self.errors += 1
-
- def parse_file(self, fname):
- """
- Parse a single Kernel source.
- """
-
- # Prevent parsing the same file twice if results are cached
- if fname in self.files:
- return
-
- doc = KernelDoc(self.config, fname)
- export_table, entries = doc.parse_kdoc()
-
- self.export_table[fname] = export_table
-
- self.files.add(fname)
- self.export_files.add(fname) # parse_kdoc() already check exports
-
- self.results[fname] = entries
-
- def process_export_file(self, fname):
- """
- Parses EXPORT_SYMBOL* macros from a single Kernel source file.
- """
-
- # Prevent parsing the same file twice if results are cached
- if fname in self.export_files:
- return
-
- doc = KernelDoc(self.config, fname)
- export_table = doc.parse_export()
-
- if not export_table:
- self.error(f"Error: Cannot check EXPORT_SYMBOL* on {fname}")
- export_table = set()
-
- self.export_table[fname] = export_table
- self.export_files.add(fname)
-
- def file_not_found_cb(self, fname):
- """
- Callback to warn if a file was not found.
- """
-
- self.error(f"Cannot find file {fname}")
-
- def __init__(self, verbose=False, out_style=None,
- werror=False, wreturn=False, wshort_desc=False,
- wcontents_before_sections=False,
- logger=None):
- """
- Initialize startup variables and parse all files
- """
-
- if not verbose:
- verbose = bool(os.environ.get("KBUILD_VERBOSE", 0))
-
- if out_style is None:
- out_style = OutputFormat()
-
- if not werror:
- kcflags = os.environ.get("KCFLAGS", None)
- if kcflags:
- match = re.search(r"(\s|^)-Werror(\s|$)/", kcflags)
- if match:
- werror = True
-
- # reading this variable is for backwards compat just in case
- # someone was calling it with the variable from outside the
- # kernel's build system
- kdoc_werror = os.environ.get("KDOC_WERROR", None)
- if kdoc_werror:
- werror = kdoc_werror
-
- # Some variables are global to the parser logic as a whole as they are
- # used to send control configuration to KernelDoc class. As such,
- # those variables are read-only inside the KernelDoc.
- self.config = argparse.Namespace
-
- self.config.verbose = verbose
- self.config.werror = werror
- self.config.wreturn = wreturn
- self.config.wshort_desc = wshort_desc
- self.config.wcontents_before_sections = wcontents_before_sections
-
- if not logger:
- self.config.log = logging.getLogger("kernel-doc")
- else:
- self.config.log = logger
-
- self.config.warning = self.warning
-
- self.config.src_tree = os.environ.get("SRCTREE", None)
-
- # Initialize variables that are internal to KernelFiles
-
- self.out_style = out_style
-
- self.errors = 0
- self.results = {}
-
- self.files = set()
- self.export_files = set()
- self.export_table = {}
-
- def parse(self, file_list, export_file=None):
- """
- Parse all files
- """
-
- glob = GlobSourceFiles(srctree=self.config.src_tree)
-
- for fname in glob.parse_files(file_list, self.file_not_found_cb):
- self.parse_file(fname)
-
- for fname in glob.parse_files(export_file, self.file_not_found_cb):
- self.process_export_file(fname)
-
- def out_msg(self, fname, name, arg):
- """
- Return output messages from a file name using the output style
- filtering.
-
- If output type was not handled by the syler, return None.
- """
-
- # NOTE: we can add rules here to filter out unwanted parts,
- # although OutputFormat.msg already does that.
-
- return self.out_style.msg(fname, name, arg)
-
- def msg(self, enable_lineno=False, export=False, internal=False,
- symbol=None, nosymbol=None, no_doc_sections=False,
- filenames=None, export_file=None):
- """
- Interacts over the kernel-doc results and output messages,
- returning kernel-doc markups on each interaction
- """
-
- self.out_style.set_config(self.config)
-
- if not filenames:
- filenames = sorted(self.results.keys())
-
- glob = GlobSourceFiles(srctree=self.config.src_tree)
-
- for fname in filenames:
- function_table = set()
-
- if internal or export:
- if not export_file:
- export_file = [fname]
-
- for f in glob.parse_files(export_file, self.file_not_found_cb):
- function_table |= self.export_table[f]
-
- if symbol:
- for s in symbol:
- function_table.add(s)
-
- self.out_style.set_filter(export, internal, symbol, nosymbol,
- function_table, enable_lineno,
- no_doc_sections)
-
- msg = ""
- if fname not in self.results:
- self.config.log.warning("No kernel-doc for file %s", fname)
- continue
-
- for arg in self.results[fname]:
- m = self.out_msg(fname, arg.name, arg)
-
- if m is None:
- ln = arg.get("ln", 0)
- dtype = arg.get('type', "")
-
- self.config.log.warning("%s:%d Can't handle %s",
- fname, ln, dtype)
- else:
- msg += m
-
- if msg:
- yield fname, msg
diff --git a/scripts/lib/kdoc/kdoc_item.py b/scripts/lib/kdoc/kdoc_item.py
deleted file mode 100644
index b3b225764550..000000000000
--- a/scripts/lib/kdoc/kdoc_item.py
+++ /dev/null
@@ -1,42 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0
-#
-# A class that will, eventually, encapsulate all of the parsed data that we
-# then pass into the output modules.
-#
-
-class KdocItem:
- def __init__(self, name, type, start_line, **other_stuff):
- self.name = name
- self.type = type
- self.declaration_start_line = start_line
- self.sections = {}
- self.sections_start_lines = {}
- self.parameterlist = []
- self.parameterdesc_start_lines = []
- self.parameterdescs = {}
- self.parametertypes = {}
- #
- # Just save everything else into our own dict so that the output
- # side can grab it directly as before. As we move things into more
- # structured data, this will, hopefully, fade away.
- #
- self.other_stuff = other_stuff
-
- def get(self, key, default = None):
- return self.other_stuff.get(key, default)
-
- def __getitem__(self, key):
- return self.get(key)
-
- #
- # Tracking of section and parameter information.
- #
- def set_sections(self, sections, start_lines):
- self.sections = sections
- self.section_start_lines = start_lines
-
- def set_params(self, names, descs, types, starts):
- self.parameterlist = names
- self.parameterdescs = descs
- self.parametertypes = types
- self.parameterdesc_start_lines = starts
diff --git a/scripts/lib/kdoc/kdoc_output.py b/scripts/lib/kdoc/kdoc_output.py
deleted file mode 100644
index ea8914537ba0..000000000000
--- a/scripts/lib/kdoc/kdoc_output.py
+++ /dev/null
@@ -1,749 +0,0 @@
-#!/usr/bin/env python3
-# SPDX-License-Identifier: GPL-2.0
-# Copyright(c) 2025: Mauro Carvalho Chehab <mchehab@kernel.org>.
-#
-# pylint: disable=C0301,R0902,R0911,R0912,R0913,R0914,R0915,R0917
-
-"""
-Implement output filters to print kernel-doc documentation.
-
-The implementation uses a virtual base class (OutputFormat) which
-contains a dispatches to virtual methods, and some code to filter
-out output messages.
-
-The actual implementation is done on one separate class per each type
-of output. Currently, there are output classes for ReST and man/troff.
-"""
-
-import os
-import re
-from datetime import datetime
-
-from kdoc_parser import KernelDoc, type_param
-from kdoc_re import KernRe
-
-
-function_pointer = KernRe(r"([^\(]*\(\*)\s*\)\s*\(([^\)]*)\)", cache=False)
-
-# match expressions used to find embedded type information
-type_constant = KernRe(r"\b``([^\`]+)``\b", cache=False)
-type_constant2 = KernRe(r"\%([-_*\w]+)", cache=False)
-type_func = KernRe(r"(\w+)\(\)", cache=False)
-type_param_ref = KernRe(r"([\!~\*]?)\@(\w*((\.\w+)|(->\w+))*(\.\.\.)?)", cache=False)
-
-# Special RST handling for func ptr params
-type_fp_param = KernRe(r"\@(\w+)\(\)", cache=False)
-
-# Special RST handling for structs with func ptr params
-type_fp_param2 = KernRe(r"\@(\w+->\S+)\(\)", cache=False)
-
-type_env = KernRe(r"(\$\w+)", cache=False)
-type_enum = KernRe(r"\&(enum\s*([_\w]+))", cache=False)
-type_struct = KernRe(r"\&(struct\s*([_\w]+))", cache=False)
-type_typedef = KernRe(r"\&(typedef\s*([_\w]+))", cache=False)
-type_union = KernRe(r"\&(union\s*([_\w]+))", cache=False)
-type_member = KernRe(r"\&([_\w]+)(\.|->)([_\w]+)", cache=False)
-type_fallback = KernRe(r"\&([_\w]+)", cache=False)
-type_member_func = type_member + KernRe(r"\(\)", cache=False)
-
-
-class OutputFormat:
- """
- Base class for OutputFormat. If used as-is, it means that only
- warnings will be displayed.
- """
-
- # output mode.
- OUTPUT_ALL = 0 # output all symbols and doc sections
- OUTPUT_INCLUDE = 1 # output only specified symbols
- OUTPUT_EXPORTED = 2 # output exported symbols
- OUTPUT_INTERNAL = 3 # output non-exported symbols
-
- # Virtual member to be overriden at the inherited classes
- highlights = []
-
- def __init__(self):
- """Declare internal vars and set mode to OUTPUT_ALL"""
-
- self.out_mode = self.OUTPUT_ALL
- self.enable_lineno = None
- self.nosymbol = {}
- self.symbol = None
- self.function_table = None
- self.config = None
- self.no_doc_sections = False
-
- self.data = ""
-
- def set_config(self, config):
- """
- Setup global config variables used by both parser and output.
- """
-
- self.config = config
-
- def set_filter(self, export, internal, symbol, nosymbol, function_table,
- enable_lineno, no_doc_sections):
- """
- Initialize filter variables according with the requested mode.
-
- Only one choice is valid between export, internal and symbol.
-
- The nosymbol filter can be used on all modes.
- """
-
- self.enable_lineno = enable_lineno
- self.no_doc_sections = no_doc_sections
- self.function_table = function_table
-
- if symbol:
- self.out_mode = self.OUTPUT_INCLUDE
- elif export:
- self.out_mode = self.OUTPUT_EXPORTED
- elif internal:
- self.out_mode = self.OUTPUT_INTERNAL
- else:
- self.out_mode = self.OUTPUT_ALL
-
- if nosymbol:
- self.nosymbol = set(nosymbol)
-
-
- def highlight_block(self, block):
- """
- Apply the RST highlights to a sub-block of text.
- """
-
- for r, sub in self.highlights:
- block = r.sub(sub, block)
-
- return block
-
- def out_warnings(self, args):
- """
- Output warnings for identifiers that will be displayed.
- """
-
- for log_msg in args.warnings:
- self.config.warning(log_msg)
-
- def check_doc(self, name, args):
- """Check if DOC should be output"""
-
- if self.no_doc_sections:
- return False
-
- if name in self.nosymbol:
- return False
-
- if self.out_mode == self.OUTPUT_ALL:
- self.out_warnings(args)
- return True
-
- if self.out_mode == self.OUTPUT_INCLUDE:
- if name in self.function_table:
- self.out_warnings(args)
- return True
-
- return False
-
- def check_declaration(self, dtype, name, args):
- """
- Checks if a declaration should be output or not based on the
- filtering criteria.
- """
-
- if name in self.nosymbol:
- return False
-
- if self.out_mode == self.OUTPUT_ALL:
- self.out_warnings(args)
- return True
-
- if self.out_mode in [self.OUTPUT_INCLUDE, self.OUTPUT_EXPORTED]:
- if name in self.function_table:
- return True
-
- if self.out_mode == self.OUTPUT_INTERNAL:
- if dtype != "function":
- self.out_warnings(args)
- return True
-
- if name not in self.function_table:
- self.out_warnings(args)
- return True
-
- return False
-
- def msg(self, fname, name, args):
- """
- Handles a single entry from kernel-doc parser
- """
-
- self.data = ""
-
- dtype = args.type
-
- if dtype == "doc":
- self.out_doc(fname, name, args)
- return self.data
-
- if not self.check_declaration(dtype, name, args):
- return self.data
-
- if dtype == "function":
- self.out_function(fname, name, args)
- return self.data
-
- if dtype == "enum":
- self.out_enum(fname, name, args)
- return self.data
-
- if dtype == "typedef":
- self.out_typedef(fname, name, args)
- return self.data
-
- if dtype in ["struct", "union"]:
- self.out_struct(fname, name, args)
- return self.data
-
- # Warn if some type requires an output logic
- self.config.log.warning("doesn't now how to output '%s' block",
- dtype)
-
- return None
-
- # Virtual methods to be overridden by inherited classes
- # At the base class, those do nothing.
- def out_doc(self, fname, name, args):
- """Outputs a DOC block"""
-
- def out_function(self, fname, name, args):
- """Outputs a function"""
-
- def out_enum(self, fname, name, args):
- """Outputs an enum"""
-
- def out_typedef(self, fname, name, args):
- """Outputs a typedef"""
-
- def out_struct(self, fname, name, args):
- """Outputs a struct"""
-
-
-class RestFormat(OutputFormat):
- """Consts and functions used by ReST output"""
-
- highlights = [
- (type_constant, r"``\1``"),
- (type_constant2, r"``\1``"),
-
- # Note: need to escape () to avoid func matching later
- (type_member_func, r":c:type:`\1\2\3\\(\\) <\1>`"),
- (type_member, r":c:type:`\1\2\3 <\1>`"),
- (type_fp_param, r"**\1\\(\\)**"),
- (type_fp_param2, r"**\1\\(\\)**"),
- (type_func, r"\1()"),
- (type_enum, r":c:type:`\1 <\2>`"),
- (type_struct, r":c:type:`\1 <\2>`"),
- (type_typedef, r":c:type:`\1 <\2>`"),
- (type_union, r":c:type:`\1 <\2>`"),
-
- # in rst this can refer to any type
- (type_fallback, r":c:type:`\1`"),
- (type_param_ref, r"**\1\2**")
- ]
- blankline = "\n"
-
- sphinx_literal = KernRe(r'^[^.].*::$', cache=False)
- sphinx_cblock = KernRe(r'^\.\.\ +code-block::', cache=False)
-
- def __init__(self):
- """
- Creates class variables.
-
- Not really mandatory, but it is a good coding style and makes
- pylint happy.
- """
-
- super().__init__()
- self.lineprefix = ""
-
- def print_lineno(self, ln):
- """Outputs a line number"""
-
- if self.enable_lineno and ln is not None:
- ln += 1
- self.data += f".. LINENO {ln}\n"
-
- def output_highlight(self, args):
- """
- Outputs a C symbol that may require being converted to ReST using
- the self.highlights variable
- """
-
- input_text = args
- output = ""
- in_literal = False
- litprefix = ""
- block = ""
-
- for line in input_text.strip("\n").split("\n"):
-
- # If we're in a literal block, see if we should drop out of it.
- # Otherwise, pass the line straight through unmunged.
- if in_literal:
- if line.strip(): # If the line is not blank
- # If this is the first non-blank line in a literal block,
- # figure out the proper indent.
- if not litprefix:
- r = KernRe(r'^(\s*)')
- if r.match(line):
- litprefix = '^' + r.group(1)
- else:
- litprefix = ""
-
- output += line + "\n"
- elif not KernRe(litprefix).match(line):
- in_literal = False
- else:
- output += line + "\n"
- else:
- output += line + "\n"
-
- # Not in a literal block (or just dropped out)
- if not in_literal:
- block += line + "\n"
- if self.sphinx_literal.match(line) or self.sphinx_cblock.match(line):
- in_literal = True
- litprefix = ""
- output += self.highlight_block(block)
- block = ""
-
- # Handle any remaining block
- if block:
- output += self.highlight_block(block)
-
- # Print the output with the line prefix
- for line in output.strip("\n").split("\n"):
- self.data += self.lineprefix + line + "\n"
-
- def out_section(self, args, out_docblock=False):
- """
- Outputs a block section.
-
- This could use some work; it's used to output the DOC: sections, and
- starts by putting out the name of the doc section itself, but that
- tends to duplicate a header already in the template file.
- """
- for section, text in args.sections.items():
- # Skip sections that are in the nosymbol_table
- if section in self.nosymbol:
- continue
-
- if out_docblock:
- if not self.out_mode == self.OUTPUT_INCLUDE:
- self.data += f".. _{section}:\n\n"
- self.data += f'{self.lineprefix}**{section}**\n\n'
- else:
- self.data += f'{self.lineprefix}**{section}**\n\n'
-
- self.print_lineno(args.section_start_lines.get(section, 0))
- self.output_highlight(text)
- self.data += "\n"
- self.data += "\n"
-
- def out_doc(self, fname, name, args):
- if not self.check_doc(name, args):
- return
- self.out_section(args, out_docblock=True)
-
- def out_function(self, fname, name, args):
-
- oldprefix = self.lineprefix
- signature = ""
-
- func_macro = args.get('func_macro', False)
- if func_macro:
- signature = name
- else:
- if args.get('functiontype'):
- signature = args['functiontype'] + " "
- signature += name + " ("
-
- ln = args.declaration_start_line
- count = 0
- for parameter in args.parameterlist:
- if count != 0:
- signature += ", "
- count += 1
- dtype = args.parametertypes.get(parameter, "")
-
- if function_pointer.search(dtype):
- signature += function_pointer.group(1) + parameter + function_pointer.group(3)
- else:
- signature += dtype
-
- if not func_macro:
- signature += ")"
-
- self.print_lineno(ln)
- if args.get('typedef') or not args.get('functiontype'):
- self.data += f".. c:macro:: {name}\n\n"
-
- if args.get('typedef'):
- self.data += " **Typedef**: "
- self.lineprefix = ""
- self.output_highlight(args.get('purpose', ""))
- self.data += "\n\n**Syntax**\n\n"
- self.data += f" ``{signature}``\n\n"
- else:
- self.data += f"``{signature}``\n\n"
- else:
- self.data += f".. c:function:: {signature}\n\n"
-
- if not args.get('typedef'):
- self.print_lineno(ln)
- self.lineprefix = " "
- self.output_highlight(args.get('purpose', ""))
- self.data += "\n"
-
- # Put descriptive text into a container (HTML <div>) to help set
- # function prototypes apart
- self.lineprefix = " "
-
- if args.parameterlist:
- self.data += ".. container:: kernelindent\n\n"
- self.data += f"{self.lineprefix}**Parameters**\n\n"
-
- for parameter in args.parameterlist:
- parameter_name = KernRe(r'\[.*').sub('', parameter)
- dtype = args.parametertypes.get(parameter, "")
-
- if dtype:
- self.data += f"{self.lineprefix}``{dtype}``\n"
- else:
- self.data += f"{self.lineprefix}``{parameter}``\n"
-
- self.print_lineno(args.parameterdesc_start_lines.get(parameter_name, 0))
-
- self.lineprefix = " "
- if parameter_name in args.parameterdescs and \
- args.parameterdescs[parameter_name] != KernelDoc.undescribed:
-
- self.output_highlight(args.parameterdescs[parameter_name])
- self.data += "\n"
- else:
- self.data += f"{self.lineprefix}*undescribed*\n\n"
- self.lineprefix = " "
-
- self.out_section(args)
- self.lineprefix = oldprefix
-
- def out_enum(self, fname, name, args):
-
- oldprefix = self.lineprefix
- ln = args.declaration_start_line
-
- self.data += f"\n\n.. c:enum:: {name}\n\n"
-
- self.print_lineno(ln)
- self.lineprefix = " "
- self.output_highlight(args.get('purpose', ''))
- self.data += "\n"
-
- self.data += ".. container:: kernelindent\n\n"
- outer = self.lineprefix + " "
- self.lineprefix = outer + " "
- self.data += f"{outer}**Constants**\n\n"
-
- for parameter in args.parameterlist:
- self.data += f"{outer}``{parameter}``\n"
-
- if args.parameterdescs.get(parameter, '') != KernelDoc.undescribed:
- self.output_highlight(args.parameterdescs[parameter])
- else:
- self.data += f"{self.lineprefix}*undescribed*\n\n"
- self.data += "\n"
-
- self.lineprefix = oldprefix
- self.out_section(args)
-
- def out_typedef(self, fname, name, args):
-
- oldprefix = self.lineprefix
- ln = args.declaration_start_line
-
- self.data += f"\n\n.. c:type:: {name}\n\n"
-
- self.print_lineno(ln)
- self.lineprefix = " "
-
- self.output_highlight(args.get('purpose', ''))
-
- self.data += "\n"
-
- self.lineprefix = oldprefix
- self.out_section(args)
-
- def out_struct(self, fname, name, args):
-
- purpose = args.get('purpose', "")
- declaration = args.get('definition', "")
- dtype = args.type
- ln = args.declaration_start_line
-
- self.data += f"\n\n.. c:{dtype}:: {name}\n\n"
-
- self.print_lineno(ln)
-
- oldprefix = self.lineprefix
- self.lineprefix += " "
-
- self.output_highlight(purpose)
- self.data += "\n"
-
- self.data += ".. container:: kernelindent\n\n"
- self.data += f"{self.lineprefix}**Definition**::\n\n"
-
- self.lineprefix = self.lineprefix + " "
-
- declaration = declaration.replace("\t", self.lineprefix)
-
- self.data += f"{self.lineprefix}{dtype} {name}" + ' {' + "\n"
- self.data += f"{declaration}{self.lineprefix}" + "};\n\n"
-
- self.lineprefix = " "
- self.data += f"{self.lineprefix}**Members**\n\n"
- for parameter in args.parameterlist:
- if not parameter or parameter.startswith("#"):
- continue
-
- parameter_name = parameter.split("[", maxsplit=1)[0]
-
- if args.parameterdescs.get(parameter_name) == KernelDoc.undescribed:
- continue
-
- self.print_lineno(args.parameterdesc_start_lines.get(parameter_name, 0))
-
- self.data += f"{self.lineprefix}``{parameter}``\n"
-
- self.lineprefix = " "
- self.output_highlight(args.parameterdescs[parameter_name])
- self.lineprefix = " "
-
- self.data += "\n"
-
- self.data += "\n"
-
- self.lineprefix = oldprefix
- self.out_section(args)
-
-
-class ManFormat(OutputFormat):
- """Consts and functions used by man pages output"""
-
- highlights = (
- (type_constant, r"\1"),
- (type_constant2, r"\1"),
- (type_func, r"\\fB\1\\fP"),
- (type_enum, r"\\fI\1\\fP"),
- (type_struct, r"\\fI\1\\fP"),
- (type_typedef, r"\\fI\1\\fP"),
- (type_union, r"\\fI\1\\fP"),
- (type_param, r"\\fI\1\\fP"),
- (type_param_ref, r"\\fI\1\2\\fP"),
- (type_member, r"\\fI\1\2\3\\fP"),
- (type_fallback, r"\\fI\1\\fP")
- )
- blankline = ""
-
- date_formats = [
- "%a %b %d %H:%M:%S %Z %Y",
- "%a %b %d %H:%M:%S %Y",
- "%Y-%m-%d",
- "%b %d %Y",
- "%B %d %Y",
- "%m %d %Y",
- ]
-
- def __init__(self, modulename):
- """
- Creates class variables.
-
- Not really mandatory, but it is a good coding style and makes
- pylint happy.
- """
-
- super().__init__()
- self.modulename = modulename
-
- dt = None
- tstamp = os.environ.get("KBUILD_BUILD_TIMESTAMP")
- if tstamp:
- for fmt in self.date_formats:
- try:
- dt = datetime.strptime(tstamp, fmt)
- break
- except ValueError:
- pass
-
- if not dt:
- dt = datetime.now()
-
- self.man_date = dt.strftime("%B %Y")
-
- def output_highlight(self, block):
- """
- Outputs a C symbol that may require being highlighted with
- self.highlights variable using troff syntax
- """
-
- contents = self.highlight_block(block)
-
- if isinstance(contents, list):
- contents = "\n".join(contents)
-
- for line in contents.strip("\n").split("\n"):
- line = KernRe(r"^\s*").sub("", line)
- if not line:
- continue
-
- if line[0] == ".":
- self.data += "\\&" + line + "\n"
- else:
- self.data += line + "\n"
-
- def out_doc(self, fname, name, args):
- if not self.check_doc(name, args):
- return
-
- self.data += f'.TH "{self.modulename}" 9 "{self.modulename}" "{self.man_date}" "API Manual" LINUX' + "\n"
-
- for section, text in args.sections.items():
- self.data += f'.SH "{section}"' + "\n"
- self.output_highlight(text)
-
- def out_function(self, fname, name, args):
- """output function in man"""
-
- self.data += f'.TH "{name}" 9 "{name}" "{self.man_date}" "Kernel Hacker\'s Manual" LINUX' + "\n"
-
- self.data += ".SH NAME\n"
- self.data += f"{name} \\- {args['purpose']}\n"
-
- self.data += ".SH SYNOPSIS\n"
- if args.get('functiontype', ''):
- self.data += f'.B "{args["functiontype"]}" {name}' + "\n"
- else:
- self.data += f'.B "{name}' + "\n"
-
- count = 0
- parenth = "("
- post = ","
-
- for parameter in args.parameterlist:
- if count == len(args.parameterlist) - 1:
- post = ");"
-
- dtype = args.parametertypes.get(parameter, "")
- if function_pointer.match(dtype):
- # Pointer-to-function
- self.data += f'".BI "{parenth}{function_pointer.group(1)}" " ") ({function_pointer.group(2)}){post}"' + "\n"
- else:
- dtype = KernRe(r'([^\*])$').sub(r'\1 ', dtype)
-
- self.data += f'.BI "{parenth}{dtype}" "{post}"' + "\n"
- count += 1
- parenth = ""
-
- if args.parameterlist:
- self.data += ".SH ARGUMENTS\n"
-
- for parameter in args.parameterlist:
- parameter_name = re.sub(r'\[.*', '', parameter)
-
- self.data += f'.IP "{parameter}" 12' + "\n"
- self.output_highlight(args.parameterdescs.get(parameter_name, ""))
-
- for section, text in args.sections.items():
- self.data += f'.SH "{section.upper()}"' + "\n"
- self.output_highlight(text)
-
- def out_enum(self, fname, name, args):
- self.data += f'.TH "{self.modulename}" 9 "enum {name}" "{self.man_date}" "API Manual" LINUX' + "\n"
-
- self.data += ".SH NAME\n"
- self.data += f"enum {name} \\- {args['purpose']}\n"
-
- self.data += ".SH SYNOPSIS\n"
- self.data += f"enum {name}" + " {\n"
-
- count = 0
- for parameter in args.parameterlist:
- self.data += f'.br\n.BI " {parameter}"' + "\n"
- if count == len(args.parameterlist) - 1:
- self.data += "\n};\n"
- else:
- self.data += ", \n.br\n"
-
- count += 1
-
- self.data += ".SH Constants\n"
-
- for parameter in args.parameterlist:
- parameter_name = KernRe(r'\[.*').sub('', parameter)
- self.data += f'.IP "{parameter}" 12' + "\n"
- self.output_highlight(args.parameterdescs.get(parameter_name, ""))
-
- for section, text in args.sections.items():
- self.data += f'.SH "{section}"' + "\n"
- self.output_highlight(text)
-
- def out_typedef(self, fname, name, args):
- module = self.modulename
- purpose = args.get('purpose')
-
- self.data += f'.TH "{module}" 9 "{name}" "{self.man_date}" "API Manual" LINUX' + "\n"
-
- self.data += ".SH NAME\n"
- self.data += f"typedef {name} \\- {purpose}\n"
-
- for section, text in args.sections.items():
- self.data += f'.SH "{section}"' + "\n"
- self.output_highlight(text)
-
- def out_struct(self, fname, name, args):
- module = self.modulename
- purpose = args.get('purpose')
- definition = args.get('definition')
-
- self.data += f'.TH "{module}" 9 "{args.type} {name}" "{self.man_date}" "API Manual" LINUX' + "\n"
-
- self.data += ".SH NAME\n"
- self.data += f"{args.type} {name} \\- {purpose}\n"
-
- # Replace tabs with two spaces and handle newlines
- declaration = definition.replace("\t", " ")
- declaration = KernRe(r"\n").sub('"\n.br\n.BI "', declaration)
-
- self.data += ".SH SYNOPSIS\n"
- self.data += f"{args.type} {name} " + "{" + "\n.br\n"
- self.data += f'.BI "{declaration}\n' + "};\n.br\n\n"
-
- self.data += ".SH Members\n"
- for parameter in args.parameterlist:
- if parameter.startswith("#"):
- continue
-
- parameter_name = re.sub(r"\[.*", "", parameter)
-
- if args.parameterdescs.get(parameter_name) == KernelDoc.undescribed:
- continue
-
- self.data += f'.IP "{parameter}" 12' + "\n"
- self.output_highlight(args.parameterdescs.get(parameter_name))
-
- for section, text in args.sections.items():
- self.data += f'.SH "{section}"' + "\n"
- self.output_highlight(text)
diff --git a/scripts/lib/kdoc/kdoc_parser.py b/scripts/lib/kdoc/kdoc_parser.py
deleted file mode 100644
index 2376f180b1fa..000000000000
--- a/scripts/lib/kdoc/kdoc_parser.py
+++ /dev/null
@@ -1,1649 +0,0 @@
-#!/usr/bin/env python3
-# SPDX-License-Identifier: GPL-2.0
-# Copyright(c) 2025: Mauro Carvalho Chehab <mchehab@kernel.org>.
-#
-# pylint: disable=C0301,C0302,R0904,R0912,R0913,R0914,R0915,R0917,R1702
-
-"""
-kdoc_parser
-===========
-
-Read a C language source or header FILE and extract embedded
-documentation comments
-"""
-
-import sys
-import re
-from pprint import pformat
-
-from kdoc_re import NestedMatch, KernRe
-from kdoc_item import KdocItem
-
-#
-# Regular expressions used to parse kernel-doc markups at KernelDoc class.
-#
-# Let's declare them in lowercase outside any class to make easier to
-# convert from the python script.
-#
-# As those are evaluated at the beginning, no need to cache them
-#
-
-# Allow whitespace at end of comment start.
-doc_start = KernRe(r'^/\*\*\s*$', cache=False)
-
-doc_end = KernRe(r'\*/', cache=False)
-doc_com = KernRe(r'\s*\*\s*', cache=False)
-doc_com_body = KernRe(r'\s*\* ?', cache=False)
-doc_decl = doc_com + KernRe(r'(\w+)', cache=False)
-
-# @params and a strictly limited set of supported section names
-# Specifically:
-# Match @word:
-# @...:
-# @{section-name}:
-# while trying to not match literal block starts like "example::"
-#
-known_section_names = 'description|context|returns?|notes?|examples?'
-known_sections = KernRe(known_section_names, flags = re.I)
-doc_sect = doc_com + \
- KernRe(r'\s*(@[.\w]+|@\.\.\.|' + known_section_names + r')\s*:([^:].*)?$',
- flags=re.I, cache=False)
-
-doc_content = doc_com_body + KernRe(r'(.*)', cache=False)
-doc_inline_start = KernRe(r'^\s*/\*\*\s*$', cache=False)
-doc_inline_sect = KernRe(r'\s*\*\s*(@\s*[\w][\w\.]*\s*):(.*)', cache=False)
-doc_inline_end = KernRe(r'^\s*\*/\s*$', cache=False)
-doc_inline_oneline = KernRe(r'^\s*/\*\*\s*(@[\w\s]+):\s*(.*)\s*\*/\s*$', cache=False)
-
-export_symbol = KernRe(r'^\s*EXPORT_SYMBOL(_GPL)?\s*\(\s*(\w+)\s*\)\s*', cache=False)
-export_symbol_ns = KernRe(r'^\s*EXPORT_SYMBOL_NS(_GPL)?\s*\(\s*(\w+)\s*,\s*"\S+"\)\s*', cache=False)
-
-type_param = KernRe(r"@(\w*((\.\w+)|(->\w+))*(\.\.\.)?)", cache=False)
-
-#
-# Tests for the beginning of a kerneldoc block in its various forms.
-#
-doc_block = doc_com + KernRe(r'DOC:\s*(.*)?', cache=False)
-doc_begin_data = KernRe(r"^\s*\*?\s*(struct|union|enum|typedef)\b\s*(\w*)", cache = False)
-doc_begin_func = KernRe(str(doc_com) + # initial " * '
- r"(?:\w+\s*\*\s*)?" + # type (not captured)
- r'(?:define\s+)?' + # possible "define" (not captured)
- r'(\w+)\s*(?:\(\w*\))?\s*' + # name and optional "(...)"
- r'(?:[-:].*)?$', # description (not captured)
- cache = False)
-
-#
-# Here begins a long set of transformations to turn structure member prefixes
-# and macro invocations into something we can parse and generate kdoc for.
-#
-struct_args_pattern = r'([^,)]+)'
-
-struct_xforms = [
- # Strip attributes
- (KernRe(r"__attribute__\s*\(\([a-z0-9,_\*\s\(\)]*\)\)", flags=re.I | re.S, cache=False), ' '),
- (KernRe(r'\s*__aligned\s*\([^;]*\)', re.S), ' '),
- (KernRe(r'\s*__counted_by\s*\([^;]*\)', re.S), ' '),
- (KernRe(r'\s*__counted_by_(le|be)\s*\([^;]*\)', re.S), ' '),
- (KernRe(r'\s*__packed\s*', re.S), ' '),
- (KernRe(r'\s*CRYPTO_MINALIGN_ATTR', re.S), ' '),
- (KernRe(r'\s*____cacheline_aligned_in_smp', re.S), ' '),
- (KernRe(r'\s*____cacheline_aligned', re.S), ' '),
- (KernRe(r'\s*__cacheline_group_(begin|end)\([^\)]+\);'), ''),
- #
- # Unwrap struct_group macros based on this definition:
- # __struct_group(TAG, NAME, ATTRS, MEMBERS...)
- # which has variants like: struct_group(NAME, MEMBERS...)
- # Only MEMBERS arguments require documentation.
- #
- # Parsing them happens on two steps:
- #
- # 1. drop struct group arguments that aren't at MEMBERS,
- # storing them as STRUCT_GROUP(MEMBERS)
- #
- # 2. remove STRUCT_GROUP() ancillary macro.
- #
- # The original logic used to remove STRUCT_GROUP() using an
- # advanced regex:
- #
- # \bSTRUCT_GROUP(\(((?:(?>[^)(]+)|(?1))*)\))[^;]*;
- #
- # with two patterns that are incompatible with
- # Python re module, as it has:
- #
- # - a recursive pattern: (?1)
- # - an atomic grouping: (?>...)
- #
- # I tried a simpler version: but it didn't work either:
- # \bSTRUCT_GROUP\(([^\)]+)\)[^;]*;
- #
- # As it doesn't properly match the end parenthesis on some cases.
- #
- # So, a better solution was crafted: there's now a NestedMatch
- # class that ensures that delimiters after a search are properly
- # matched. So, the implementation to drop STRUCT_GROUP() will be
- # handled in separate.
- #
- (KernRe(r'\bstruct_group\s*\(([^,]*,)', re.S), r'STRUCT_GROUP('),
- (KernRe(r'\bstruct_group_attr\s*\(([^,]*,){2}', re.S), r'STRUCT_GROUP('),
- (KernRe(r'\bstruct_group_tagged\s*\(([^,]*),([^,]*),', re.S), r'struct \1 \2; STRUCT_GROUP('),
- (KernRe(r'\b__struct_group\s*\(([^,]*,){3}', re.S), r'STRUCT_GROUP('),
- #
- # Replace macros
- #
- # TODO: use NestedMatch for FOO($1, $2, ...) matches
- #
- # it is better to also move those to the NestedMatch logic,
- # to ensure that parenthesis will be properly matched.
- #
- (KernRe(r'__ETHTOOL_DECLARE_LINK_MODE_MASK\s*\(([^\)]+)\)', re.S),
- r'DECLARE_BITMAP(\1, __ETHTOOL_LINK_MODE_MASK_NBITS)'),
- (KernRe(r'DECLARE_PHY_INTERFACE_MASK\s*\(([^\)]+)\)', re.S),
- r'DECLARE_BITMAP(\1, PHY_INTERFACE_MODE_MAX)'),
- (KernRe(r'DECLARE_BITMAP\s*\(' + struct_args_pattern + r',\s*' + struct_args_pattern + r'\)',
- re.S), r'unsigned long \1[BITS_TO_LONGS(\2)]'),
- (KernRe(r'DECLARE_HASHTABLE\s*\(' + struct_args_pattern + r',\s*' + struct_args_pattern + r'\)',
- re.S), r'unsigned long \1[1 << ((\2) - 1)]'),
- (KernRe(r'DECLARE_KFIFO\s*\(' + struct_args_pattern + r',\s*' + struct_args_pattern +
- r',\s*' + struct_args_pattern + r'\)', re.S), r'\2 *\1'),
- (KernRe(r'DECLARE_KFIFO_PTR\s*\(' + struct_args_pattern + r',\s*' +
- struct_args_pattern + r'\)', re.S), r'\2 *\1'),
- (KernRe(r'(?:__)?DECLARE_FLEX_ARRAY\s*\(' + struct_args_pattern + r',\s*' +
- struct_args_pattern + r'\)', re.S), r'\1 \2[]'),
- (KernRe(r'DEFINE_DMA_UNMAP_ADDR\s*\(' + struct_args_pattern + r'\)', re.S), r'dma_addr_t \1'),
- (KernRe(r'DEFINE_DMA_UNMAP_LEN\s*\(' + struct_args_pattern + r'\)', re.S), r'__u32 \1'),
-]
-#
-# Regexes here are guaranteed to have the end limiter matching
-# the start delimiter. Yet, right now, only one replace group
-# is allowed.
-#
-struct_nested_prefixes = [
- (re.compile(r'\bSTRUCT_GROUP\('), r'\1'),
-]
-
-#
-# Transforms for function prototypes
-#
-function_xforms = [
- (KernRe(r"^static +"), ""),
- (KernRe(r"^extern +"), ""),
- (KernRe(r"^asmlinkage +"), ""),
- (KernRe(r"^inline +"), ""),
- (KernRe(r"^__inline__ +"), ""),
- (KernRe(r"^__inline +"), ""),
- (KernRe(r"^__always_inline +"), ""),
- (KernRe(r"^noinline +"), ""),
- (KernRe(r"^__FORTIFY_INLINE +"), ""),
- (KernRe(r"__init +"), ""),
- (KernRe(r"__init_or_module +"), ""),
- (KernRe(r"__deprecated +"), ""),
- (KernRe(r"__flatten +"), ""),
- (KernRe(r"__meminit +"), ""),
- (KernRe(r"__must_check +"), ""),
- (KernRe(r"__weak +"), ""),
- (KernRe(r"__sched +"), ""),
- (KernRe(r"_noprof"), ""),
- (KernRe(r"__printf\s*\(\s*\d*\s*,\s*\d*\s*\) +"), ""),
- (KernRe(r"__(?:re)?alloc_size\s*\(\s*\d+\s*(?:,\s*\d+\s*)?\) +"), ""),
- (KernRe(r"__diagnose_as\s*\(\s*\S+\s*(?:,\s*\d+\s*)*\) +"), ""),
- (KernRe(r"DECL_BUCKET_PARAMS\s*\(\s*(\S+)\s*,\s*(\S+)\s*\)"), r"\1, \2"),
- (KernRe(r"__attribute_const__ +"), ""),
- (KernRe(r"__attribute__\s*\(\((?:[\w\s]+(?:\([^)]*\))?\s*,?)+\)\)\s+"), ""),
-]
-
-#
-# Apply a set of transforms to a block of text.
-#
-def apply_transforms(xforms, text):
- for search, subst in xforms:
- text = search.sub(subst, text)
- return text
-
-#
-# A little helper to get rid of excess white space
-#
-multi_space = KernRe(r'\s\s+')
-def trim_whitespace(s):
- return multi_space.sub(' ', s.strip())
-
-#
-# Remove struct/enum members that have been marked "private".
-#
-def trim_private_members(text):
- #
- # First look for a "public:" block that ends a private region, then
- # handle the "private until the end" case.
- #
- text = KernRe(r'/\*\s*private:.*?/\*\s*public:.*?\*/', flags=re.S).sub('', text)
- text = KernRe(r'/\*\s*private:.*', flags=re.S).sub('', text)
- #
- # We needed the comments to do the above, but now we can take them out.
- #
- return KernRe(r'\s*/\*.*?\*/\s*', flags=re.S).sub('', text).strip()
-
-class state:
- """
- State machine enums
- """
-
- # Parser states
- NORMAL = 0 # normal code
- NAME = 1 # looking for function name
- DECLARATION = 2 # We have seen a declaration which might not be done
- BODY = 3 # the body of the comment
- SPECIAL_SECTION = 4 # doc section ending with a blank line
- PROTO = 5 # scanning prototype
- DOCBLOCK = 6 # documentation block
- INLINE_NAME = 7 # gathering doc outside main block
- INLINE_TEXT = 8 # reading the body of inline docs
-
- name = [
- "NORMAL",
- "NAME",
- "DECLARATION",
- "BODY",
- "SPECIAL_SECTION",
- "PROTO",
- "DOCBLOCK",
- "INLINE_NAME",
- "INLINE_TEXT",
- ]
-
-
-SECTION_DEFAULT = "Description" # default section
-
-class KernelEntry:
-
- def __init__(self, config, ln):
- self.config = config
-
- self._contents = []
- self.prototype = ""
-
- self.warnings = []
-
- self.parameterlist = []
- self.parameterdescs = {}
- self.parametertypes = {}
- self.parameterdesc_start_lines = {}
-
- self.section_start_lines = {}
- self.sections = {}
-
- self.anon_struct_union = False
-
- self.leading_space = None
-
- # State flags
- self.brcount = 0
- self.declaration_start_line = ln + 1
-
- #
- # Management of section contents
- #
- def add_text(self, text):
- self._contents.append(text)
-
- def contents(self):
- return '\n'.join(self._contents) + '\n'
-
- # TODO: rename to emit_message after removal of kernel-doc.pl
- def emit_msg(self, log_msg, warning=True):
- """Emit a message"""
-
- if not warning:
- self.config.log.info(log_msg)
- return
-
- # Delegate warning output to output logic, as this way it
- # will report warnings/info only for symbols that are output
-
- self.warnings.append(log_msg)
- return
-
- #
- # Begin a new section.
- #
- def begin_section(self, line_no, title = SECTION_DEFAULT, dump = False):
- if dump:
- self.dump_section(start_new = True)
- self.section = title
- self.new_start_line = line_no
-
- def dump_section(self, start_new=True):
- """
- Dumps section contents to arrays/hashes intended for that purpose.
- """
- #
- # If we have accumulated no contents in the default ("description")
- # section, don't bother.
- #
- if self.section == SECTION_DEFAULT and not self._contents:
- return
- name = self.section
- contents = self.contents()
-
- if type_param.match(name):
- name = type_param.group(1)
-
- self.parameterdescs[name] = contents
- self.parameterdesc_start_lines[name] = self.new_start_line
-
- self.new_start_line = 0
-
- else:
- if name in self.sections and self.sections[name] != "":
- # Only warn on user-specified duplicate section names
- if name != SECTION_DEFAULT:
- self.emit_msg(self.new_start_line,
- f"duplicate section name '{name}'\n")
- # Treat as a new paragraph - add a blank line
- self.sections[name] += '\n' + contents
- else:
- self.sections[name] = contents
- self.section_start_lines[name] = self.new_start_line
- self.new_start_line = 0
-
-# self.config.log.debug("Section: %s : %s", name, pformat(vars(self)))
-
- if start_new:
- self.section = SECTION_DEFAULT
- self._contents = []
-
-
-class KernelDoc:
- """
- Read a C language source or header FILE and extract embedded
- documentation comments.
- """
-
- # Section names
-
- section_context = "Context"
- section_return = "Return"
-
- undescribed = "-- undescribed --"
-
- def __init__(self, config, fname):
- """Initialize internal variables"""
-
- self.fname = fname
- self.config = config
-
- # Initial state for the state machines
- self.state = state.NORMAL
-
- # Store entry currently being processed
- self.entry = None
-
- # Place all potential outputs into an array
- self.entries = []
-
- #
- # We need Python 3.7 for its "dicts remember the insertion
- # order" guarantee
- #
- if sys.version_info.major == 3 and sys.version_info.minor < 7:
- self.emit_msg(0,
- 'Python 3.7 or later is required for correct results')
-
- def emit_msg(self, ln, msg, warning=True):
- """Emit a message"""
-
- log_msg = f"{self.fname}:{ln} {msg}"
-
- if self.entry:
- self.entry.emit_msg(log_msg, warning)
- return
-
- if warning:
- self.config.log.warning(log_msg)
- else:
- self.config.log.info(log_msg)
-
- def dump_section(self, start_new=True):
- """
- Dumps section contents to arrays/hashes intended for that purpose.
- """
-
- if self.entry:
- self.entry.dump_section(start_new)
-
- # TODO: rename it to store_declaration after removal of kernel-doc.pl
- def output_declaration(self, dtype, name, **args):
- """
- Stores the entry into an entry array.
-
- The actual output and output filters will be handled elsewhere
- """
-
- item = KdocItem(name, dtype, self.entry.declaration_start_line, **args)
- item.warnings = self.entry.warnings
-
- # Drop empty sections
- # TODO: improve empty sections logic to emit warnings
- sections = self.entry.sections
- for section in ["Description", "Return"]:
- if section in sections and not sections[section].rstrip():
- del sections[section]
- item.set_sections(sections, self.entry.section_start_lines)
- item.set_params(self.entry.parameterlist, self.entry.parameterdescs,
- self.entry.parametertypes,
- self.entry.parameterdesc_start_lines)
- self.entries.append(item)
-
- self.config.log.debug("Output: %s:%s = %s", dtype, name, pformat(args))
-
- def reset_state(self, ln):
- """
- Ancillary routine to create a new entry. It initializes all
- variables used by the state machine.
- """
-
- self.entry = KernelEntry(self.config, ln)
-
- # State flags
- self.state = state.NORMAL
-
- def push_parameter(self, ln, decl_type, param, dtype,
- org_arg, declaration_name):
- """
- Store parameters and their descriptions at self.entry.
- """
-
- if self.entry.anon_struct_union and dtype == "" and param == "}":
- return # Ignore the ending }; from anonymous struct/union
-
- self.entry.anon_struct_union = False
-
- param = KernRe(r'[\[\)].*').sub('', param, count=1)
-
- #
- # Look at various "anonymous type" cases.
- #
- if dtype == '':
- if param.endswith("..."):
- if len(param) > 3: # there is a name provided, use that
- param = param[:-3]
- if not self.entry.parameterdescs.get(param):
- self.entry.parameterdescs[param] = "variable arguments"
-
- elif (not param) or param == "void":
- param = "void"
- self.entry.parameterdescs[param] = "no arguments"
-
- elif param in ["struct", "union"]:
- # Handle unnamed (anonymous) union or struct
- dtype = param
- param = "{unnamed_" + param + "}"
- self.entry.parameterdescs[param] = "anonymous\n"
- self.entry.anon_struct_union = True
-
- # Warn if parameter has no description
- # (but ignore ones starting with # as these are not parameters
- # but inline preprocessor statements)
- if param not in self.entry.parameterdescs and not param.startswith("#"):
- self.entry.parameterdescs[param] = self.undescribed
-
- if "." not in param:
- if decl_type == 'function':
- dname = f"{decl_type} parameter"
- else:
- dname = f"{decl_type} member"
-
- self.emit_msg(ln,
- f"{dname} '{param}' not described in '{declaration_name}'")
-
- # Strip spaces from param so that it is one continuous string on
- # parameterlist. This fixes a problem where check_sections()
- # cannot find a parameter like "addr[6 + 2]" because it actually
- # appears as "addr[6", "+", "2]" on the parameter list.
- # However, it's better to maintain the param string unchanged for
- # output, so just weaken the string compare in check_sections()
- # to ignore "[blah" in a parameter string.
-
- self.entry.parameterlist.append(param)
- org_arg = KernRe(r'\s\s+').sub(' ', org_arg)
- self.entry.parametertypes[param] = org_arg
-
-
- def create_parameter_list(self, ln, decl_type, args,
- splitter, declaration_name):
- """
- Creates a list of parameters, storing them at self.entry.
- """
-
- # temporarily replace all commas inside function pointer definition
- arg_expr = KernRe(r'(\([^\),]+),')
- while arg_expr.search(args):
- args = arg_expr.sub(r"\1#", args)
-
- for arg in args.split(splitter):
- # Ignore argument attributes
- arg = KernRe(r'\sPOS0?\s').sub(' ', arg)
-
- # Strip leading/trailing spaces
- arg = arg.strip()
- arg = KernRe(r'\s+').sub(' ', arg, count=1)
-
- if arg.startswith('#'):
- # Treat preprocessor directive as a typeless variable just to fill
- # corresponding data structures "correctly". Catch it later in
- # output_* subs.
-
- # Treat preprocessor directive as a typeless variable
- self.push_parameter(ln, decl_type, arg, "",
- "", declaration_name)
- #
- # The pointer-to-function case.
- #
- elif KernRe(r'\(.+\)\s*\(').search(arg):
- arg = arg.replace('#', ',')
- r = KernRe(r'[^\(]+\(\*?\s*' # Everything up to "(*"
- r'([\w\[\].]*)' # Capture the name and possible [array]
- r'\s*\)') # Make sure the trailing ")" is there
- if r.match(arg):
- param = r.group(1)
- else:
- self.emit_msg(ln, f"Invalid param: {arg}")
- param = arg
- dtype = arg.replace(param, '')
- self.push_parameter(ln, decl_type, param, dtype, arg, declaration_name)
- #
- # The array-of-pointers case. Dig the parameter name out from the middle
- # of the declaration.
- #
- elif KernRe(r'\(.+\)\s*\[').search(arg):
- r = KernRe(r'[^\(]+\(\s*\*\s*' # Up to "(" and maybe "*"
- r'([\w.]*?)' # The actual pointer name
- r'\s*(\[\s*\w+\s*\]\s*)*\)') # The [array portion]
- if r.match(arg):
- param = r.group(1)
- else:
- self.emit_msg(ln, f"Invalid param: {arg}")
- param = arg
- dtype = arg.replace(param, '')
- self.push_parameter(ln, decl_type, param, dtype, arg, declaration_name)
- elif arg:
- #
- # Clean up extraneous spaces and split the string at commas; the first
- # element of the resulting list will also include the type information.
- #
- arg = KernRe(r'\s*:\s*').sub(":", arg)
- arg = KernRe(r'\s*\[').sub('[', arg)
- args = KernRe(r'\s*,\s*').split(arg)
- args[0] = re.sub(r'(\*+)\s*', r' \1', args[0])
- #
- # args[0] has a string of "type a". If "a" includes an [array]
- # declaration, we want to not be fooled by any white space inside
- # the brackets, so detect and handle that case specially.
- #
- r = KernRe(r'^([^[\]]*\s+)(.*)$')
- if r.match(args[0]):
- args[0] = r.group(2)
- dtype = r.group(1)
- else:
- # No space in args[0]; this seems wrong but preserves previous behavior
- dtype = ''
-
- bitfield_re = KernRe(r'(.*?):(\w+)')
- for param in args:
- #
- # For pointers, shift the star(s) from the variable name to the
- # type declaration.
- #
- r = KernRe(r'^(\*+)\s*(.*)')
- if r.match(param):
- self.push_parameter(ln, decl_type, r.group(2),
- f"{dtype} {r.group(1)}",
- arg, declaration_name)
- #
- # Perform a similar shift for bitfields.
- #
- elif bitfield_re.search(param):
- if dtype != "": # Skip unnamed bit-fields
- self.push_parameter(ln, decl_type, bitfield_re.group(1),
- f"{dtype}:{bitfield_re.group(2)}",
- arg, declaration_name)
- else:
- self.push_parameter(ln, decl_type, param, dtype,
- arg, declaration_name)
-
- def check_sections(self, ln, decl_name, decl_type):
- """
- Check for errors inside sections, emitting warnings if not found
- parameters are described.
- """
- for section in self.entry.sections:
- if section not in self.entry.parameterlist and \
- not known_sections.search(section):
- if decl_type == 'function':
- dname = f"{decl_type} parameter"
- else:
- dname = f"{decl_type} member"
- self.emit_msg(ln,
- f"Excess {dname} '{section}' description in '{decl_name}'")
-
- def check_return_section(self, ln, declaration_name, return_type):
- """
- If the function doesn't return void, warns about the lack of a
- return description.
- """
-
- if not self.config.wreturn:
- return
-
- # Ignore an empty return type (It's a macro)
- # Ignore functions with a "void" return type (but not "void *")
- if not return_type or KernRe(r'void\s*\w*\s*$').search(return_type):
- return
-
- if not self.entry.sections.get("Return", None):
- self.emit_msg(ln,
- f"No description found for return value of '{declaration_name}'")
-
- #
- # Split apart a structure prototype; returns (struct|union, name, members) or None
- #
- def split_struct_proto(self, proto):
- type_pattern = r'(struct|union)'
- qualifiers = [
- "__attribute__",
- "__packed",
- "__aligned",
- "____cacheline_aligned_in_smp",
- "____cacheline_aligned",
- ]
- definition_body = r'\{(.*)\}\s*' + "(?:" + '|'.join(qualifiers) + ")?"
-
- r = KernRe(type_pattern + r'\s+(\w+)\s*' + definition_body)
- if r.search(proto):
- return (r.group(1), r.group(2), r.group(3))
- else:
- r = KernRe(r'typedef\s+' + type_pattern + r'\s*' + definition_body + r'\s*(\w+)\s*;')
- if r.search(proto):
- return (r.group(1), r.group(3), r.group(2))
- return None
- #
- # Rewrite the members of a structure or union for easier formatting later on.
- # Among other things, this function will turn a member like:
- #
- # struct { inner_members; } foo;
- #
- # into:
- #
- # struct foo; inner_members;
- #
- def rewrite_struct_members(self, members):
- #
- # Process struct/union members from the most deeply nested outward. The
- # trick is in the ^{ below - it prevents a match of an outer struct/union
- # until the inner one has been munged (removing the "{" in the process).
- #
- struct_members = KernRe(r'(struct|union)' # 0: declaration type
- r'([^\{\};]+)' # 1: possible name
- r'(\{)'
- r'([^\{\}]*)' # 3: Contents of declaration
- r'(\})'
- r'([^\{\};]*)(;)') # 5: Remaining stuff after declaration
- tuples = struct_members.findall(members)
- while tuples:
- for t in tuples:
- newmember = ""
- oldmember = "".join(t) # Reconstruct the original formatting
- dtype, name, lbr, content, rbr, rest, semi = t
- #
- # Pass through each field name, normalizing the form and formatting.
- #
- for s_id in rest.split(','):
- s_id = s_id.strip()
- newmember += f"{dtype} {s_id}; "
- #
- # Remove bitfield/array/pointer info, getting the bare name.
- #
- s_id = KernRe(r'[:\[].*').sub('', s_id)
- s_id = KernRe(r'^\s*\**(\S+)\s*').sub(r'\1', s_id)
- #
- # Pass through the members of this inner structure/union.
- #
- for arg in content.split(';'):
- arg = arg.strip()
- #
- # Look for (type)(*name)(args) - pointer to function
- #
- r = KernRe(r'^([^\(]+\(\*?\s*)([\w.]*)(\s*\).*)')
- if r.match(arg):
- dtype, name, extra = r.group(1), r.group(2), r.group(3)
- # Pointer-to-function
- if not s_id:
- # Anonymous struct/union
- newmember += f"{dtype}{name}{extra}; "
- else:
- newmember += f"{dtype}{s_id}.{name}{extra}; "
- #
- # Otherwise a non-function member.
- #
- else:
- #
- # Remove bitmap and array portions and spaces around commas
- #
- arg = KernRe(r':\s*\d+\s*').sub('', arg)
- arg = KernRe(r'\[.*\]').sub('', arg)
- arg = KernRe(r'\s*,\s*').sub(',', arg)
- #
- # Look for a normal decl - "type name[,name...]"
- #
- r = KernRe(r'(.*)\s+([\S+,]+)')
- if r.search(arg):
- for name in r.group(2).split(','):
- name = KernRe(r'^\s*\**(\S+)\s*').sub(r'\1', name)
- if not s_id:
- # Anonymous struct/union
- newmember += f"{r.group(1)} {name}; "
- else:
- newmember += f"{r.group(1)} {s_id}.{name}; "
- else:
- newmember += f"{arg}; "
- #
- # At the end of the s_id loop, replace the original declaration with
- # the munged version.
- #
- members = members.replace(oldmember, newmember)
- #
- # End of the tuple loop - search again and see if there are outer members
- # that now turn up.
- #
- tuples = struct_members.findall(members)
- return members
-
- #
- # Format the struct declaration into a standard form for inclusion in the
- # resulting docs.
- #
- def format_struct_decl(self, declaration):
- #
- # Insert newlines, get rid of extra spaces.
- #
- declaration = KernRe(r'([\{;])').sub(r'\1\n', declaration)
- declaration = KernRe(r'\}\s+;').sub('};', declaration)
- #
- # Format inline enums with each member on its own line.
- #
- r = KernRe(r'(enum\s+\{[^\}]+),([^\n])')
- while r.search(declaration):
- declaration = r.sub(r'\1,\n\2', declaration)
- #
- # Now go through and supply the right number of tabs
- # for each line.
- #
- def_args = declaration.split('\n')
- level = 1
- declaration = ""
- for clause in def_args:
- clause = KernRe(r'\s+').sub(' ', clause.strip(), count=1)
- if clause:
- if '}' in clause and level > 1:
- level -= 1
- if not clause.startswith('#'):
- declaration += "\t" * level
- declaration += "\t" + clause + "\n"
- if "{" in clause and "}" not in clause:
- level += 1
- return declaration
-
-
- def dump_struct(self, ln, proto):
- """
- Store an entry for an struct or union
- """
- #
- # Do the basic parse to get the pieces of the declaration.
- #
- struct_parts = self.split_struct_proto(proto)
- if not struct_parts:
- self.emit_msg(ln, f"{proto} error: Cannot parse struct or union!")
- return
- decl_type, declaration_name, members = struct_parts
-
- if self.entry.identifier != declaration_name:
- self.emit_msg(ln, f"expecting prototype for {decl_type} {self.entry.identifier}. "
- f"Prototype was for {decl_type} {declaration_name} instead\n")
- return
- #
- # Go through the list of members applying all of our transformations.
- #
- members = trim_private_members(members)
- members = apply_transforms(struct_xforms, members)
-
- nested = NestedMatch()
- for search, sub in struct_nested_prefixes:
- members = nested.sub(search, sub, members)
- #
- # Deal with embedded struct and union members, and drop enums entirely.
- #
- declaration = members
- members = self.rewrite_struct_members(members)
- members = re.sub(r'(\{[^\{\}]*\})', '', members)
- #
- # Output the result and we are done.
- #
- self.create_parameter_list(ln, decl_type, members, ';',
- declaration_name)
- self.check_sections(ln, declaration_name, decl_type)
- self.output_declaration(decl_type, declaration_name,
- definition=self.format_struct_decl(declaration),
- purpose=self.entry.declaration_purpose)
-
- def dump_enum(self, ln, proto):
- """
- Stores an enum inside self.entries array.
- """
- #
- # Strip preprocessor directives. Note that this depends on the
- # trailing semicolon we added in process_proto_type().
- #
- proto = KernRe(r'#\s*((define|ifdef|if)\s+|endif)[^;]*;', flags=re.S).sub('', proto)
- #
- # Parse out the name and members of the enum. Typedef form first.
- #
- r = KernRe(r'typedef\s+enum\s*\{(.*)\}\s*(\w*)\s*;')
- if r.search(proto):
- declaration_name = r.group(2)
- members = trim_private_members(r.group(1))
- #
- # Failing that, look for a straight enum
- #
- else:
- r = KernRe(r'enum\s+(\w*)\s*\{(.*)\}')
- if r.match(proto):
- declaration_name = r.group(1)
- members = trim_private_members(r.group(2))
- #
- # OK, this isn't going to work.
- #
- else:
- self.emit_msg(ln, f"{proto}: error: Cannot parse enum!")
- return
- #
- # Make sure we found what we were expecting.
- #
- if self.entry.identifier != declaration_name:
- if self.entry.identifier == "":
- self.emit_msg(ln,
- f"{proto}: wrong kernel-doc identifier on prototype")
- else:
- self.emit_msg(ln,
- f"expecting prototype for enum {self.entry.identifier}. "
- f"Prototype was for enum {declaration_name} instead")
- return
-
- if not declaration_name:
- declaration_name = "(anonymous)"
- #
- # Parse out the name of each enum member, and verify that we
- # have a description for it.
- #
- member_set = set()
- members = KernRe(r'\([^;)]*\)').sub('', members)
- for arg in members.split(','):
- if not arg:
- continue
- arg = KernRe(r'^\s*(\w+).*').sub(r'\1', arg)
- self.entry.parameterlist.append(arg)
- if arg not in self.entry.parameterdescs:
- self.entry.parameterdescs[arg] = self.undescribed
- self.emit_msg(ln,
- f"Enum value '{arg}' not described in enum '{declaration_name}'")
- member_set.add(arg)
- #
- # Ensure that every described member actually exists in the enum.
- #
- for k in self.entry.parameterdescs:
- if k not in member_set:
- self.emit_msg(ln,
- f"Excess enum value '%{k}' description in '{declaration_name}'")
-
- self.output_declaration('enum', declaration_name,
- purpose=self.entry.declaration_purpose)
-
- def dump_declaration(self, ln, prototype):
- """
- Stores a data declaration inside self.entries array.
- """
-
- if self.entry.decl_type == "enum":
- self.dump_enum(ln, prototype)
- elif self.entry.decl_type == "typedef":
- self.dump_typedef(ln, prototype)
- elif self.entry.decl_type in ["union", "struct"]:
- self.dump_struct(ln, prototype)
- else:
- # This would be a bug
- self.emit_message(ln, f'Unknown declaration type: {self.entry.decl_type}')
-
- def dump_function(self, ln, prototype):
- """
- Stores a function of function macro inside self.entries array.
- """
-
- found = func_macro = False
- return_type = ''
- decl_type = 'function'
- #
- # Apply the initial transformations.
- #
- prototype = apply_transforms(function_xforms, prototype)
- #
- # If we have a macro, remove the "#define" at the front.
- #
- new_proto = KernRe(r"^#\s*define\s+").sub("", prototype)
- if new_proto != prototype:
- prototype = new_proto
- #
- # Dispense with the simple "#define A B" case here; the key
- # is the space after the name of the symbol being defined.
- # NOTE that the seemingly misnamed "func_macro" indicates a
- # macro *without* arguments.
- #
- r = KernRe(r'^(\w+)\s+')
- if r.search(prototype):
- return_type = ''
- declaration_name = r.group(1)
- func_macro = True
- found = True
-
- # Yes, this truly is vile. We are looking for:
- # 1. Return type (may be nothing if we're looking at a macro)
- # 2. Function name
- # 3. Function parameters.
- #
- # All the while we have to watch out for function pointer parameters
- # (which IIRC is what the two sections are for), C types (these
- # regexps don't even start to express all the possibilities), and
- # so on.
- #
- # If you mess with these regexps, it's a good idea to check that
- # the following functions' documentation still comes out right:
- # - parport_register_device (function pointer parameters)
- # - atomic_set (macro)
- # - pci_match_device, __copy_to_user (long return type)
-
- name = r'\w+'
- type1 = r'(?:[\w\s]+)?'
- type2 = r'(?:[\w\s]+\*+)+'
- #
- # Attempt to match first on (args) with no internal parentheses; this
- # lets us easily filter out __acquires() and other post-args stuff. If
- # that fails, just grab the rest of the line to the last closing
- # parenthesis.
- #
- proto_args = r'\(([^\(]*|.*)\)'
- #
- # (Except for the simple macro case) attempt to split up the prototype
- # in the various ways we understand.
- #
- if not found:
- patterns = [
- rf'^()({name})\s*{proto_args}',
- rf'^({type1})\s+({name})\s*{proto_args}',
- rf'^({type2})\s*({name})\s*{proto_args}',
- ]
-
- for p in patterns:
- r = KernRe(p)
- if r.match(prototype):
- return_type = r.group(1)
- declaration_name = r.group(2)
- args = r.group(3)
- self.create_parameter_list(ln, decl_type, args, ',',
- declaration_name)
- found = True
- break
- #
- # Parsing done; make sure that things are as we expect.
- #
- if not found:
- self.emit_msg(ln,
- f"cannot understand function prototype: '{prototype}'")
- return
- if self.entry.identifier != declaration_name:
- self.emit_msg(ln, f"expecting prototype for {self.entry.identifier}(). "
- f"Prototype was for {declaration_name}() instead")
- return
- self.check_sections(ln, declaration_name, "function")
- self.check_return_section(ln, declaration_name, return_type)
- #
- # Store the result.
- #
- self.output_declaration(decl_type, declaration_name,
- typedef=('typedef' in return_type),
- functiontype=return_type,
- purpose=self.entry.declaration_purpose,
- func_macro=func_macro)
-
-
- def dump_typedef(self, ln, proto):
- """
- Stores a typedef inside self.entries array.
- """
- #
- # We start by looking for function typedefs.
- #
- typedef_type = r'typedef((?:\s+[\w*]+\b){0,7}\s+(?:\w+\b|\*+))\s*'
- typedef_ident = r'\*?\s*(\w\S+)\s*'
- typedef_args = r'\s*\((.*)\);'
-
- typedef1 = KernRe(typedef_type + r'\(' + typedef_ident + r'\)' + typedef_args)
- typedef2 = KernRe(typedef_type + typedef_ident + typedef_args)
-
- # Parse function typedef prototypes
- for r in [typedef1, typedef2]:
- if not r.match(proto):
- continue
-
- return_type = r.group(1).strip()
- declaration_name = r.group(2)
- args = r.group(3)
-
- if self.entry.identifier != declaration_name:
- self.emit_msg(ln,
- f"expecting prototype for typedef {self.entry.identifier}. Prototype was for typedef {declaration_name} instead\n")
- return
-
- self.create_parameter_list(ln, 'function', args, ',', declaration_name)
-
- self.output_declaration('function', declaration_name,
- typedef=True,
- functiontype=return_type,
- purpose=self.entry.declaration_purpose)
- return
- #
- # Not a function, try to parse a simple typedef.
- #
- r = KernRe(r'typedef.*\s+(\w+)\s*;')
- if r.match(proto):
- declaration_name = r.group(1)
-
- if self.entry.identifier != declaration_name:
- self.emit_msg(ln,
- f"expecting prototype for typedef {self.entry.identifier}. Prototype was for typedef {declaration_name} instead\n")
- return
-
- self.output_declaration('typedef', declaration_name,
- purpose=self.entry.declaration_purpose)
- return
-
- self.emit_msg(ln, "error: Cannot parse typedef!")
-
- @staticmethod
- def process_export(function_set, line):
- """
- process EXPORT_SYMBOL* tags
-
- This method doesn't use any variable from the class, so declare it
- with a staticmethod decorator.
- """
-
- # We support documenting some exported symbols with different
- # names. A horrible hack.
- suffixes = [ '_noprof' ]
-
- # Note: it accepts only one EXPORT_SYMBOL* per line, as having
- # multiple export lines would violate Kernel coding style.
-
- if export_symbol.search(line):
- symbol = export_symbol.group(2)
- elif export_symbol_ns.search(line):
- symbol = export_symbol_ns.group(2)
- else:
- return False
- #
- # Found an export, trim out any special suffixes
- #
- for suffix in suffixes:
- # Be backward compatible with Python < 3.9
- if symbol.endswith(suffix):
- symbol = symbol[:-len(suffix)]
- function_set.add(symbol)
- return True
-
- def process_normal(self, ln, line):
- """
- STATE_NORMAL: looking for the /** to begin everything.
- """
-
- if not doc_start.match(line):
- return
-
- # start a new entry
- self.reset_state(ln)
-
- # next line is always the function name
- self.state = state.NAME
-
- def process_name(self, ln, line):
- """
- STATE_NAME: Looking for the "name - description" line
- """
- #
- # Check for a DOC: block and handle them specially.
- #
- if doc_block.search(line):
-
- if not doc_block.group(1):
- self.entry.begin_section(ln, "Introduction")
- else:
- self.entry.begin_section(ln, doc_block.group(1))
-
- self.entry.identifier = self.entry.section
- self.state = state.DOCBLOCK
- #
- # Otherwise we're looking for a normal kerneldoc declaration line.
- #
- elif doc_decl.search(line):
- self.entry.identifier = doc_decl.group(1)
-
- # Test for data declaration
- if doc_begin_data.search(line):
- self.entry.decl_type = doc_begin_data.group(1)
- self.entry.identifier = doc_begin_data.group(2)
- #
- # Look for a function description
- #
- elif doc_begin_func.search(line):
- self.entry.identifier = doc_begin_func.group(1)
- self.entry.decl_type = "function"
- #
- # We struck out.
- #
- else:
- self.emit_msg(ln,
- f"This comment starts with '/**', but isn't a kernel-doc comment. Refer Documentation/doc-guide/kernel-doc.rst\n{line}")
- self.state = state.NORMAL
- return
- #
- # OK, set up for a new kerneldoc entry.
- #
- self.state = state.BODY
- self.entry.identifier = self.entry.identifier.strip(" ")
- # if there's no @param blocks need to set up default section here
- self.entry.begin_section(ln + 1)
- #
- # Find the description portion, which *should* be there but
- # isn't always.
- # (We should be able to capture this from the previous parsing - someday)
- #
- r = KernRe("[-:](.*)")
- if r.search(line):
- self.entry.declaration_purpose = trim_whitespace(r.group(1))
- self.state = state.DECLARATION
- else:
- self.entry.declaration_purpose = ""
-
- if not self.entry.declaration_purpose and self.config.wshort_desc:
- self.emit_msg(ln,
- f"missing initial short description on line:\n{line}")
-
- if not self.entry.identifier and self.entry.decl_type != "enum":
- self.emit_msg(ln,
- f"wrong kernel-doc identifier on line:\n{line}")
- self.state = state.NORMAL
-
- if self.config.verbose:
- self.emit_msg(ln,
- f"Scanning doc for {self.entry.decl_type} {self.entry.identifier}",
- warning=False)
- #
- # Failed to find an identifier. Emit a warning
- #
- else:
- self.emit_msg(ln, f"Cannot find identifier on line:\n{line}")
-
- #
- # Helper function to determine if a new section is being started.
- #
- def is_new_section(self, ln, line):
- if doc_sect.search(line):
- self.state = state.BODY
- #
- # Pick out the name of our new section, tweaking it if need be.
- #
- newsection = doc_sect.group(1)
- if newsection.lower() == 'description':
- newsection = 'Description'
- elif newsection.lower() == 'context':
- newsection = 'Context'
- self.state = state.SPECIAL_SECTION
- elif newsection.lower() in ["@return", "@returns",
- "return", "returns"]:
- newsection = "Return"
- self.state = state.SPECIAL_SECTION
- elif newsection[0] == '@':
- self.state = state.SPECIAL_SECTION
- #
- # Initialize the contents, and get the new section going.
- #
- newcontents = doc_sect.group(2)
- if not newcontents:
- newcontents = ""
- self.dump_section()
- self.entry.begin_section(ln, newsection)
- self.entry.leading_space = None
-
- self.entry.add_text(newcontents.lstrip())
- return True
- return False
-
- #
- # Helper function to detect (and effect) the end of a kerneldoc comment.
- #
- def is_comment_end(self, ln, line):
- if doc_end.search(line):
- self.dump_section()
-
- # Look for doc_com + <text> + doc_end:
- r = KernRe(r'\s*\*\s*[a-zA-Z_0-9:.]+\*/')
- if r.match(line):
- self.emit_msg(ln, f"suspicious ending line: {line}")
-
- self.entry.prototype = ""
- self.entry.new_start_line = ln + 1
-
- self.state = state.PROTO
- return True
- return False
-
-
- def process_decl(self, ln, line):
- """
- STATE_DECLARATION: We've seen the beginning of a declaration
- """
- if self.is_new_section(ln, line) or self.is_comment_end(ln, line):
- return
- #
- # Look for anything with the " * " line beginning.
- #
- if doc_content.search(line):
- cont = doc_content.group(1)
- #
- # A blank line means that we have moved out of the declaration
- # part of the comment (without any "special section" parameter
- # descriptions).
- #
- if cont == "":
- self.state = state.BODY
- #
- # Otherwise we have more of the declaration section to soak up.
- #
- else:
- self.entry.declaration_purpose = \
- trim_whitespace(self.entry.declaration_purpose + ' ' + cont)
- else:
- # Unknown line, ignore
- self.emit_msg(ln, f"bad line: {line}")
-
-
- def process_special(self, ln, line):
- """
- STATE_SPECIAL_SECTION: a section ending with a blank line
- """
- #
- # If we have hit a blank line (only the " * " marker), then this
- # section is done.
- #
- if KernRe(r"\s*\*\s*$").match(line):
- self.entry.begin_section(ln, dump = True)
- self.state = state.BODY
- return
- #
- # Not a blank line, look for the other ways to end the section.
- #
- if self.is_new_section(ln, line) or self.is_comment_end(ln, line):
- return
- #
- # OK, we should have a continuation of the text for this section.
- #
- if doc_content.search(line):
- cont = doc_content.group(1)
- #
- # If the lines of text after the first in a special section have
- # leading white space, we need to trim it out or Sphinx will get
- # confused. For the second line (the None case), see what we
- # find there and remember it.
- #
- if self.entry.leading_space is None:
- r = KernRe(r'^(\s+)')
- if r.match(cont):
- self.entry.leading_space = len(r.group(1))
- else:
- self.entry.leading_space = 0
- #
- # Otherwise, before trimming any leading chars, be *sure*
- # that they are white space. We should maybe warn if this
- # isn't the case.
- #
- for i in range(0, self.entry.leading_space):
- if cont[i] != " ":
- self.entry.leading_space = i
- break
- #
- # Add the trimmed result to the section and we're done.
- #
- self.entry.add_text(cont[self.entry.leading_space:])
- else:
- # Unknown line, ignore
- self.emit_msg(ln, f"bad line: {line}")
-
- def process_body(self, ln, line):
- """
- STATE_BODY: the bulk of a kerneldoc comment.
- """
- if self.is_new_section(ln, line) or self.is_comment_end(ln, line):
- return
-
- if doc_content.search(line):
- cont = doc_content.group(1)
- self.entry.add_text(cont)
- else:
- # Unknown line, ignore
- self.emit_msg(ln, f"bad line: {line}")
-
- def process_inline_name(self, ln, line):
- """STATE_INLINE_NAME: beginning of docbook comments within a prototype."""
-
- if doc_inline_sect.search(line):
- self.entry.begin_section(ln, doc_inline_sect.group(1))
- self.entry.add_text(doc_inline_sect.group(2).lstrip())
- self.state = state.INLINE_TEXT
- elif doc_inline_end.search(line):
- self.dump_section()
- self.state = state.PROTO
- elif doc_content.search(line):
- self.emit_msg(ln, f"Incorrect use of kernel-doc format: {line}")
- self.state = state.PROTO
- # else ... ??
-
- def process_inline_text(self, ln, line):
- """STATE_INLINE_TEXT: docbook comments within a prototype."""
-
- if doc_inline_end.search(line):
- self.dump_section()
- self.state = state.PROTO
- elif doc_content.search(line):
- self.entry.add_text(doc_content.group(1))
- # else ... ??
-
- def syscall_munge(self, ln, proto): # pylint: disable=W0613
- """
- Handle syscall definitions
- """
-
- is_void = False
-
- # Strip newlines/CR's
- proto = re.sub(r'[\r\n]+', ' ', proto)
-
- # Check if it's a SYSCALL_DEFINE0
- if 'SYSCALL_DEFINE0' in proto:
- is_void = True
-
- # Replace SYSCALL_DEFINE with correct return type & function name
- proto = KernRe(r'SYSCALL_DEFINE.*\(').sub('long sys_', proto)
-
- r = KernRe(r'long\s+(sys_.*?),')
- if r.search(proto):
- proto = KernRe(',').sub('(', proto, count=1)
- elif is_void:
- proto = KernRe(r'\)').sub('(void)', proto, count=1)
-
- # Now delete all of the odd-numbered commas in the proto
- # so that argument types & names don't have a comma between them
- count = 0
- length = len(proto)
-
- if is_void:
- length = 0 # skip the loop if is_void
-
- for ix in range(length):
- if proto[ix] == ',':
- count += 1
- if count % 2 == 1:
- proto = proto[:ix] + ' ' + proto[ix + 1:]
-
- return proto
-
- def tracepoint_munge(self, ln, proto):
- """
- Handle tracepoint definitions
- """
-
- tracepointname = None
- tracepointargs = None
-
- # Match tracepoint name based on different patterns
- r = KernRe(r'TRACE_EVENT\((.*?),')
- if r.search(proto):
- tracepointname = r.group(1)
-
- r = KernRe(r'DEFINE_SINGLE_EVENT\((.*?),')
- if r.search(proto):
- tracepointname = r.group(1)
-
- r = KernRe(r'DEFINE_EVENT\((.*?),(.*?),')
- if r.search(proto):
- tracepointname = r.group(2)
-
- if tracepointname:
- tracepointname = tracepointname.lstrip()
-
- r = KernRe(r'TP_PROTO\((.*?)\)')
- if r.search(proto):
- tracepointargs = r.group(1)
-
- if not tracepointname or not tracepointargs:
- self.emit_msg(ln,
- f"Unrecognized tracepoint format:\n{proto}\n")
- else:
- proto = f"static inline void trace_{tracepointname}({tracepointargs})"
- self.entry.identifier = f"trace_{self.entry.identifier}"
-
- return proto
-
- def process_proto_function(self, ln, line):
- """Ancillary routine to process a function prototype"""
-
- # strip C99-style comments to end of line
- line = KernRe(r"//.*$", re.S).sub('', line)
- #
- # Soak up the line's worth of prototype text, stopping at { or ; if present.
- #
- if KernRe(r'\s*#\s*define').match(line):
- self.entry.prototype = line
- elif not line.startswith('#'): # skip other preprocessor stuff
- r = KernRe(r'([^\{]*)')
- if r.match(line):
- self.entry.prototype += r.group(1) + " "
- #
- # If we now have the whole prototype, clean it up and declare victory.
- #
- if '{' in line or ';' in line or KernRe(r'\s*#\s*define').match(line):
- # strip comments and surrounding spaces
- self.entry.prototype = KernRe(r'/\*.*\*/').sub('', self.entry.prototype).strip()
- #
- # Handle self.entry.prototypes for function pointers like:
- # int (*pcs_config)(struct foo)
- # by turning it into
- # int pcs_config(struct foo)
- #
- r = KernRe(r'^(\S+\s+)\(\s*\*(\S+)\)')
- self.entry.prototype = r.sub(r'\1\2', self.entry.prototype)
- #
- # Handle special declaration syntaxes
- #
- if 'SYSCALL_DEFINE' in self.entry.prototype:
- self.entry.prototype = self.syscall_munge(ln,
- self.entry.prototype)
- else:
- r = KernRe(r'TRACE_EVENT|DEFINE_EVENT|DEFINE_SINGLE_EVENT')
- if r.search(self.entry.prototype):
- self.entry.prototype = self.tracepoint_munge(ln,
- self.entry.prototype)
- #
- # ... and we're done
- #
- self.dump_function(ln, self.entry.prototype)
- self.reset_state(ln)
-
- def process_proto_type(self, ln, line):
- """Ancillary routine to process a type"""
-
- # Strip C99-style comments and surrounding whitespace
- line = KernRe(r"//.*$", re.S).sub('', line).strip()
- if not line:
- return # nothing to see here
-
- # To distinguish preprocessor directive from regular declaration later.
- if line.startswith('#'):
- line += ";"
- #
- # Split the declaration on any of { } or ;, and accumulate pieces
- # until we hit a semicolon while not inside {brackets}
- #
- r = KernRe(r'(.*?)([{};])')
- for chunk in r.split(line):
- if chunk: # Ignore empty matches
- self.entry.prototype += chunk
- #
- # This cries out for a match statement ... someday after we can
- # drop Python 3.9 ...
- #
- if chunk == '{':
- self.entry.brcount += 1
- elif chunk == '}':
- self.entry.brcount -= 1
- elif chunk == ';' and self.entry.brcount <= 0:
- self.dump_declaration(ln, self.entry.prototype)
- self.reset_state(ln)
- return
- #
- # We hit the end of the line while still in the declaration; put
- # in a space to represent the newline.
- #
- self.entry.prototype += ' '
-
- def process_proto(self, ln, line):
- """STATE_PROTO: reading a function/whatever prototype."""
-
- if doc_inline_oneline.search(line):
- self.entry.begin_section(ln, doc_inline_oneline.group(1))
- self.entry.add_text(doc_inline_oneline.group(2))
- self.dump_section()
-
- elif doc_inline_start.search(line):
- self.state = state.INLINE_NAME
-
- elif self.entry.decl_type == 'function':
- self.process_proto_function(ln, line)
-
- else:
- self.process_proto_type(ln, line)
-
- def process_docblock(self, ln, line):
- """STATE_DOCBLOCK: within a DOC: block."""
-
- if doc_end.search(line):
- self.dump_section()
- self.output_declaration("doc", self.entry.identifier)
- self.reset_state(ln)
-
- elif doc_content.search(line):
- self.entry.add_text(doc_content.group(1))
-
- def parse_export(self):
- """
- Parses EXPORT_SYMBOL* macros from a single Kernel source file.
- """
-
- export_table = set()
-
- try:
- with open(self.fname, "r", encoding="utf8",
- errors="backslashreplace") as fp:
-
- for line in fp:
- self.process_export(export_table, line)
-
- except IOError:
- return None
-
- return export_table
-
- #
- # The state/action table telling us which function to invoke in
- # each state.
- #
- state_actions = {
- state.NORMAL: process_normal,
- state.NAME: process_name,
- state.BODY: process_body,
- state.DECLARATION: process_decl,
- state.SPECIAL_SECTION: process_special,
- state.INLINE_NAME: process_inline_name,
- state.INLINE_TEXT: process_inline_text,
- state.PROTO: process_proto,
- state.DOCBLOCK: process_docblock,
- }
-
- def parse_kdoc(self):
- """
- Open and process each line of a C source file.
- The parsing is controlled via a state machine, and the line is passed
- to a different process function depending on the state. The process
- function may update the state as needed.
-
- Besides parsing kernel-doc tags, it also parses export symbols.
- """
-
- prev = ""
- prev_ln = None
- export_table = set()
-
- try:
- with open(self.fname, "r", encoding="utf8",
- errors="backslashreplace") as fp:
- for ln, line in enumerate(fp):
-
- line = line.expandtabs().strip("\n")
-
- # Group continuation lines on prototypes
- if self.state == state.PROTO:
- if line.endswith("\\"):
- prev += line.rstrip("\\")
- if not prev_ln:
- prev_ln = ln
- continue
-
- if prev:
- ln = prev_ln
- line = prev + line
- prev = ""
- prev_ln = None
-
- self.config.log.debug("%d %s: %s",
- ln, state.name[self.state],
- line)
-
- # This is an optimization over the original script.
- # There, when export_file was used for the same file,
- # it was read twice. Here, we use the already-existing
- # loop to parse exported symbols as well.
- #
- if (self.state != state.NORMAL) or \
- not self.process_export(export_table, line):
- # Hand this line to the appropriate state handler
- self.state_actions[self.state](self, ln, line)
-
- except OSError:
- self.config.log.error(f"Error: Cannot open file {self.fname}")
-
- return export_table, self.entries
diff --git a/scripts/lib/kdoc/kdoc_re.py b/scripts/lib/kdoc/kdoc_re.py
deleted file mode 100644
index 612223e1e723..000000000000
--- a/scripts/lib/kdoc/kdoc_re.py
+++ /dev/null
@@ -1,270 +0,0 @@
-#!/usr/bin/env python3
-# SPDX-License-Identifier: GPL-2.0
-# Copyright(c) 2025: Mauro Carvalho Chehab <mchehab@kernel.org>.
-
-"""
-Regular expression ancillary classes.
-
-Those help caching regular expressions and do matching for kernel-doc.
-"""
-
-import re
-
-# Local cache for regular expressions
-re_cache = {}
-
-
-class KernRe:
- """
- Helper class to simplify regex declaration and usage,
-
- It calls re.compile for a given pattern. It also allows adding
- regular expressions and define sub at class init time.
-
- Regular expressions can be cached via an argument, helping to speedup
- searches.
- """
-
- def _add_regex(self, string, flags):
- """
- Adds a new regex or re-use it from the cache.
- """
- self.regex = re_cache.get(string, None)
- if not self.regex:
- self.regex = re.compile(string, flags=flags)
- if self.cache:
- re_cache[string] = self.regex
-
- def __init__(self, string, cache=True, flags=0):
- """
- Compile a regular expression and initialize internal vars.
- """
-
- self.cache = cache
- self.last_match = None
-
- self._add_regex(string, flags)
-
- def __str__(self):
- """
- Return the regular expression pattern.
- """
- return self.regex.pattern
-
- def __add__(self, other):
- """
- Allows adding two regular expressions into one.
- """
-
- return KernRe(str(self) + str(other), cache=self.cache or other.cache,
- flags=self.regex.flags | other.regex.flags)
-
- def match(self, string):
- """
- Handles a re.match storing its results
- """
-
- self.last_match = self.regex.match(string)
- return self.last_match
-
- def search(self, string):
- """
- Handles a re.search storing its results
- """
-
- self.last_match = self.regex.search(string)
- return self.last_match
-
- def findall(self, string):
- """
- Alias to re.findall
- """
-
- return self.regex.findall(string)
-
- def split(self, string):
- """
- Alias to re.split
- """
-
- return self.regex.split(string)
-
- def sub(self, sub, string, count=0):
- """
- Alias to re.sub
- """
-
- return self.regex.sub(sub, string, count=count)
-
- def group(self, num):
- """
- Returns the group results of the last match
- """
-
- return self.last_match.group(num)
-
-
-class NestedMatch:
- """
- Finding nested delimiters is hard with regular expressions. It is
- even harder on Python with its normal re module, as there are several
- advanced regular expressions that are missing.
-
- This is the case of this pattern:
-
- '\\bSTRUCT_GROUP(\\(((?:(?>[^)(]+)|(?1))*)\\))[^;]*;'
-
- which is used to properly match open/close parenthesis of the
- string search STRUCT_GROUP(),
-
- Add a class that counts pairs of delimiters, using it to match and
- replace nested expressions.
-
- The original approach was suggested by:
- https://stackoverflow.com/questions/5454322/python-how-to-match-nested-parentheses-with-regex
-
- Although I re-implemented it to make it more generic and match 3 types
- of delimiters. The logic checks if delimiters are paired. If not, it
- will ignore the search string.
- """
-
- # TODO: make NestedMatch handle multiple match groups
- #
- # Right now, regular expressions to match it are defined only up to
- # the start delimiter, e.g.:
- #
- # \bSTRUCT_GROUP\(
- #
- # is similar to: STRUCT_GROUP\((.*)\)
- # except that the content inside the match group is delimiter's aligned.
- #
- # The content inside parenthesis are converted into a single replace
- # group (e.g. r`\1').
- #
- # It would be nice to change such definition to support multiple
- # match groups, allowing a regex equivalent to.
- #
- # FOO\((.*), (.*), (.*)\)
- #
- # it is probably easier to define it not as a regular expression, but
- # with some lexical definition like:
- #
- # FOO(arg1, arg2, arg3)
-
- DELIMITER_PAIRS = {
- '{': '}',
- '(': ')',
- '[': ']',
- }
-
- RE_DELIM = re.compile(r'[\{\}\[\]\(\)]')
-
- def _search(self, regex, line):
- """
- Finds paired blocks for a regex that ends with a delimiter.
-
- The suggestion of using finditer to match pairs came from:
- https://stackoverflow.com/questions/5454322/python-how-to-match-nested-parentheses-with-regex
- but I ended using a different implementation to align all three types
- of delimiters and seek for an initial regular expression.
-
- The algorithm seeks for open/close paired delimiters and place them
- into a stack, yielding a start/stop position of each match when the
- stack is zeroed.
-
- The algorithm shoud work fine for properly paired lines, but will
- silently ignore end delimiters that preceeds an start delimiter.
- This should be OK for kernel-doc parser, as unaligned delimiters
- would cause compilation errors. So, we don't need to rise exceptions
- to cover such issues.
- """
-
- stack = []
-
- for match_re in regex.finditer(line):
- start = match_re.start()
- offset = match_re.end()
-
- d = line[offset - 1]
- if d not in self.DELIMITER_PAIRS:
- continue
-
- end = self.DELIMITER_PAIRS[d]
- stack.append(end)
-
- for match in self.RE_DELIM.finditer(line[offset:]):
- pos = match.start() + offset
-
- d = line[pos]
-
- if d in self.DELIMITER_PAIRS:
- end = self.DELIMITER_PAIRS[d]
-
- stack.append(end)
- continue
-
- # Does the end delimiter match what it is expected?
- if stack and d == stack[-1]:
- stack.pop()
-
- if not stack:
- yield start, offset, pos + 1
- break
-
- def search(self, regex, line):
- """
- This is similar to re.search:
-
- It matches a regex that it is followed by a delimiter,
- returning occurrences only if all delimiters are paired.
- """
-
- for t in self._search(regex, line):
-
- yield line[t[0]:t[2]]
-
- def sub(self, regex, sub, line, count=0):
- """
- This is similar to re.sub:
-
- It matches a regex that it is followed by a delimiter,
- replacing occurrences only if all delimiters are paired.
-
- if r'\1' is used, it works just like re: it places there the
- matched paired data with the delimiter stripped.
-
- If count is different than zero, it will replace at most count
- items.
- """
- out = ""
-
- cur_pos = 0
- n = 0
-
- for start, end, pos in self._search(regex, line):
- out += line[cur_pos:start]
-
- # Value, ignoring start/end delimiters
- value = line[end:pos - 1]
-
- # replaces \1 at the sub string, if \1 is used there
- new_sub = sub
- new_sub = new_sub.replace(r'\1', value)
-
- out += new_sub
-
- # Drop end ';' if any
- if line[pos] == ';':
- pos += 1
-
- cur_pos = pos
- n += 1
-
- if count and count >= n:
- break
-
- # Append the remaining string
- l = len(line)
- out += line[cur_pos:l]
-
- return out
diff --git a/scripts/link-vmlinux.sh b/scripts/link-vmlinux.sh
index 433849ff7529..f99e196abeea 100755
--- a/scripts/link-vmlinux.sh
+++ b/scripts/link-vmlinux.sh
@@ -60,7 +60,8 @@ vmlinux_link()
# skip output file argument
shift
- if is_enabled CONFIG_LTO_CLANG || is_enabled CONFIG_X86_KERNEL_IBT; then
+ if is_enabled CONFIG_LTO_CLANG || is_enabled CONFIG_X86_KERNEL_IBT ||
+ is_enabled CONFIG_KLP_BUILD; then
# Use vmlinux.o instead of performing the slow LTO link again.
objs=vmlinux.o
libs=
@@ -105,34 +106,6 @@ vmlinux_link()
${kallsymso} ${btf_vmlinux_bin_o} ${arch_vmlinux_o} ${ldlibs}
}
-# generate .BTF typeinfo from DWARF debuginfo
-# ${1} - vmlinux image
-gen_btf()
-{
- local btf_data=${1}.btf.o
-
- info BTF "${btf_data}"
- LLVM_OBJCOPY="${OBJCOPY}" ${PAHOLE} -J ${PAHOLE_FLAGS} ${1}
-
- # Create ${btf_data} which contains just .BTF section but no symbols. Add
- # SHF_ALLOC because .BTF will be part of the vmlinux image. --strip-all
- # deletes all symbols including __start_BTF and __stop_BTF, which will
- # be redefined in the linker script. Add 2>/dev/null to suppress GNU
- # objcopy warnings: "empty loadable segment detected at ..."
- ${OBJCOPY} --only-section=.BTF --set-section-flags .BTF=alloc,readonly \
- --strip-all ${1} "${btf_data}" 2>/dev/null
- # Change e_type to ET_REL so that it can be used to link final vmlinux.
- # GNU ld 2.35+ and lld do not allow an ET_EXEC input.
- if is_enabled CONFIG_CPU_BIG_ENDIAN; then
- et_rel='\0\1'
- else
- et_rel='\1\0'
- fi
- printf "${et_rel}" | dd of="${btf_data}" conv=notrunc bs=1 seek=16 status=none
-
- btf_vmlinux_bin_o=${btf_data}
-}
-
# Create ${2}.o file with all symbols from the ${1} object file
kallsyms()
{
@@ -142,6 +115,10 @@ kallsyms()
kallsymopt="${kallsymopt} --all-symbols"
fi
+ if is_enabled CONFIG_64BIT || is_enabled CONFIG_RELOCATABLE; then
+ kallsymopt="${kallsymopt} --pc-relative"
+ fi
+
info KSYMS "${2}.S"
scripts/kallsyms ${kallsymopt} "${1}" > "${2}.S"
@@ -204,10 +181,18 @@ if is_enabled CONFIG_ARCH_WANTS_PRE_LINK_VMLINUX; then
fi
btf_vmlinux_bin_o=
+btfids_vmlinux=
kallsymso=
strip_debug=
generate_map=
+# Use "make UT=1" to trigger warnings on unused tracepoints
+case "${WARN_ON_UNUSED_TRACEPOINTS}" in
+*1*)
+ ${objtree}/scripts/tracepoint-update vmlinux.o
+ ;;
+esac
+
if is_enabled CONFIG_KALLSYMS; then
true > .tmp_vmlinux0.syms
kallsyms .tmp_vmlinux0.syms .tmp_vmlinux0.kallsyms
@@ -224,11 +209,14 @@ if is_enabled CONFIG_KALLSYMS || is_enabled CONFIG_DEBUG_INFO_BTF; then
fi
if is_enabled CONFIG_DEBUG_INFO_BTF; then
- if ! gen_btf .tmp_vmlinux1; then
+ info BTF .tmp_vmlinux1
+ if ! ${CONFIG_SHELL} ${srctree}/scripts/gen-btf.sh .tmp_vmlinux1; then
echo >&2 "Failed to generate BTF for vmlinux"
echo >&2 "Try to disable CONFIG_DEBUG_INFO_BTF"
exit 1
fi
+ btf_vmlinux_bin_o=.tmp_vmlinux1.btf.o
+ btfids_vmlinux=.tmp_vmlinux1.BTF_ids
fi
if is_enabled CONFIG_KALLSYMS; then
@@ -281,14 +269,9 @@ fi
vmlinux_link "${VMLINUX}"
-# fill in BTF IDs
if is_enabled CONFIG_DEBUG_INFO_BTF; then
- info BTFIDS "${VMLINUX}"
- RESOLVE_BTFIDS_ARGS=""
- if is_enabled CONFIG_WERROR; then
- RESOLVE_BTFIDS_ARGS=" --fatal_warnings "
- fi
- ${RESOLVE_BTFIDS} ${RESOLVE_BTFIDS_ARGS} "${VMLINUX}"
+ info BTFIDS ${VMLINUX}
+ ${RESOLVE_BTFIDS} --patch_btfids ${btfids_vmlinux} ${VMLINUX}
fi
mksysmap "${VMLINUX}" System.map
diff --git a/scripts/livepatch/Makefile b/scripts/livepatch/Makefile
new file mode 100644
index 000000000000..17b590213740
--- /dev/null
+++ b/scripts/livepatch/Makefile
@@ -0,0 +1,20 @@
+# SPDX-License-Identifier: GPL-2.0
+# Standalone Makefile for developer tooling (not part of kbuild).
+
+SHELLCHECK := $(shell which shellcheck 2> /dev/null)
+
+SRCS := \
+ klp-build
+
+.DEFAULT_GOAL := help
+.PHONY: help
+help:
+ @echo " check - Run shellcheck on $(SRCS)"
+ @echo " help - Show this help message"
+
+.PHONY: check
+check:
+ifndef SHELLCHECK
+ $(error shellcheck is not installed. Please install it to run checks)
+endif
+ @$(SHELLCHECK) $(SHELLCHECK_OPTIONS) $(SRCS)
diff --git a/scripts/livepatch/fix-patch-lines b/scripts/livepatch/fix-patch-lines
new file mode 100755
index 000000000000..fa7d4f6592e6
--- /dev/null
+++ b/scripts/livepatch/fix-patch-lines
@@ -0,0 +1,79 @@
+#!/usr/bin/awk -f
+# SPDX-License-Identifier: GPL-2.0
+#
+# Use #line directives to preserve original __LINE__ numbers across patches to
+# avoid unwanted compilation changes.
+
+BEGIN {
+ in_hunk = 0
+ skip = 0
+}
+
+/^--- / {
+ skip = $2 !~ /\.(c|h)$/
+ print
+ next
+}
+
+/^@@/ {
+ if (skip) {
+ print
+ next
+ }
+
+ in_hunk = 1
+
+ # @@ -1,3 +1,4 @@:
+ # 1: line number in old file
+ # 3: how many lines the hunk covers in old file
+ # 1: line number in new file
+ # 4: how many lines the hunk covers in new file
+
+ match($0, /^@@ -([0-9]+)(,([0-9]+))? \+([0-9]+)(,([0-9]+))? @@/, m)
+
+ # Set 'cur' to the old file's line number at the start of the hunk. It
+ # gets incremented for every context line and every line removal, so
+ # that it always represents the old file's current line number.
+ cur = m[1]
+
+ # last = last line number of current hunk
+ last = cur + (m[3] ? m[3] : 1) - 1
+
+ need_line_directive = 0
+
+ print
+ next
+}
+
+{
+ if (skip || !in_hunk || $0 ~ /^\\ No newline at end of file/) {
+ print
+ next
+ }
+
+ # change line
+ if ($0 ~ /^[+-]/) {
+ # inject #line after this group of changes
+ need_line_directive = 1
+
+ if ($0 ~ /^-/)
+ cur++
+
+ print
+ next
+ }
+
+ # If this is the first context line after a group of changes, inject
+ # the #line directive to force the compiler to correct the line
+ # numbering to match the original file.
+ if (need_line_directive) {
+ print "+#line " cur
+ need_line_directive = 0
+ }
+
+ if (cur == last)
+ in_hunk = 0
+
+ cur++
+ print
+}
diff --git a/scripts/livepatch/init.c b/scripts/livepatch/init.c
new file mode 100644
index 000000000000..f14d8c8fb35f
--- /dev/null
+++ b/scripts/livepatch/init.c
@@ -0,0 +1,106 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Init code for a livepatch kernel module
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/livepatch.h>
+
+static struct klp_patch *patch;
+
+static int __init livepatch_mod_init(void)
+{
+ struct klp_object_ext *obj_exts;
+ size_t obj_exts_sec_size;
+ struct klp_object *objs;
+ unsigned int nr_objs;
+ int ret;
+
+ obj_exts = klp_find_section_by_name(THIS_MODULE, ".init.klp_objects",
+ &obj_exts_sec_size);
+ nr_objs = obj_exts_sec_size / sizeof(*obj_exts);
+ if (!nr_objs) {
+ pr_err("nothing to patch!\n");
+ ret = -EINVAL;
+ goto err;
+ }
+
+ patch = kzalloc_obj(*patch);
+ if (!patch) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ objs = kzalloc(sizeof(struct klp_object) * (nr_objs + 1), GFP_KERNEL);
+ if (!objs) {
+ ret = -ENOMEM;
+ goto err_free_patch;
+ }
+
+ for (int i = 0; i < nr_objs; i++) {
+ struct klp_object_ext *obj_ext = obj_exts + i;
+ struct klp_func_ext *funcs_ext = obj_ext->funcs;
+ unsigned int nr_funcs = obj_ext->nr_funcs;
+ struct klp_func *funcs = objs[i].funcs;
+ struct klp_object *obj = objs + i;
+
+ funcs = kzalloc(sizeof(struct klp_func) * (nr_funcs + 1), GFP_KERNEL);
+ if (!funcs) {
+ ret = -ENOMEM;
+ for (int j = 0; j < i; j++)
+ kfree(objs[i].funcs);
+ goto err_free_objs;
+ }
+
+ for (int j = 0; j < nr_funcs; j++) {
+ funcs[j].old_name = funcs_ext[j].old_name;
+ funcs[j].new_func = funcs_ext[j].new_func;
+ funcs[j].old_sympos = funcs_ext[j].sympos;
+ }
+
+ obj->name = obj_ext->name;
+ obj->funcs = funcs;
+
+ memcpy(&obj->callbacks, &obj_ext->callbacks, sizeof(struct klp_callbacks));
+ }
+
+ patch->mod = THIS_MODULE;
+ patch->objs = objs;
+
+ /* TODO patch->states */
+
+#ifdef KLP_NO_REPLACE
+ patch->replace = false;
+#else
+ patch->replace = true;
+#endif
+
+ return klp_enable_patch(patch);
+
+err_free_objs:
+ kfree(objs);
+err_free_patch:
+ kfree(patch);
+err:
+ return ret;
+}
+
+static void __exit livepatch_mod_exit(void)
+{
+ struct klp_object *obj;
+
+ klp_for_each_object_static(patch, obj)
+ kfree(obj->funcs);
+
+ kfree(patch->objs);
+ kfree(patch);
+}
+
+module_init(livepatch_mod_init);
+module_exit(livepatch_mod_exit);
+MODULE_LICENSE("GPL");
+MODULE_INFO(livepatch, "Y");
+MODULE_DESCRIPTION("Livepatch module");
diff --git a/scripts/livepatch/klp-build b/scripts/livepatch/klp-build
new file mode 100755
index 000000000000..0ad7e6631314
--- /dev/null
+++ b/scripts/livepatch/klp-build
@@ -0,0 +1,855 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# Build a livepatch module
+
+# shellcheck disable=SC1090,SC2155
+
+if (( BASH_VERSINFO[0] < 4 || \
+ (BASH_VERSINFO[0] == 4 && BASH_VERSINFO[1] < 4) )); then
+ echo "error: this script requires bash 4.4+" >&2
+ exit 1
+fi
+
+set -o errexit
+set -o errtrace
+set -o pipefail
+set -o nounset
+
+# Allow doing 'cmd | mapfile -t array' instead of 'mapfile -t array < <(cmd)'.
+# This helps keep execution in pipes so pipefail+errexit can catch errors.
+shopt -s lastpipe
+
+unset DEBUG_CLONE DIFF_CHECKSUM SKIP_CLEANUP XTRACE
+
+REPLACE=1
+SHORT_CIRCUIT=0
+JOBS="$(getconf _NPROCESSORS_ONLN)"
+VERBOSE="-s"
+shopt -o xtrace | grep -q 'on' && XTRACE=1
+
+# Avoid removing the previous $TMP_DIR until args have been fully processed.
+KEEP_TMP=1
+
+SCRIPT="$(basename "$0")"
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+FIX_PATCH_LINES="$SCRIPT_DIR/fix-patch-lines"
+
+SRC="$(pwd)"
+OBJ="$(pwd)"
+
+CONFIG="$OBJ/.config"
+TMP_DIR="$OBJ/klp-tmp"
+
+ORIG_DIR="$TMP_DIR/orig"
+PATCHED_DIR="$TMP_DIR/patched"
+DIFF_DIR="$TMP_DIR/diff"
+KMOD_DIR="$TMP_DIR/kmod"
+
+STASH_DIR="$TMP_DIR/stash"
+TIMESTAMP="$TMP_DIR/timestamp"
+PATCH_TMP_DIR="$TMP_DIR/tmp"
+
+KLP_DIFF_LOG="$DIFF_DIR/diff.log"
+
+# Terminal output colors
+read -r COLOR_RESET COLOR_BOLD COLOR_ERROR COLOR_WARN <<< ""
+if [[ -t 1 && -t 2 ]]; then
+ COLOR_RESET="\033[0m"
+ COLOR_BOLD="\033[1m"
+ COLOR_ERROR="\033[0;31m"
+ COLOR_WARN="\033[0;33m"
+fi
+
+grep0() {
+ # shellcheck disable=SC2317
+ command grep "$@" || true
+}
+
+# Because pipefail is enabled, the grep0 helper should be used instead of
+# grep, otherwise a failed match can propagate to an error.
+grep() {
+ echo "error: $SCRIPT: use grep0 or 'command grep' instead of bare grep" >&2
+ exit 1
+}
+
+status() {
+ echo -e "${COLOR_BOLD}$*${COLOR_RESET}"
+}
+
+warn() {
+ echo -e "${COLOR_WARN}warning${COLOR_RESET}: $SCRIPT: $*" >&2
+}
+
+die() {
+ echo -e "${COLOR_ERROR}error${COLOR_RESET}: $SCRIPT: $*" >&2
+ exit 1
+}
+
+declare -a STASHED_FILES
+
+stash_file() {
+ local file="$1"
+ local rel_file="${file#"$SRC"/}"
+
+ [[ ! -e "$file" ]] && die "no file to stash: $file"
+
+ mkdir -p "$STASH_DIR/$(dirname "$rel_file")"
+ cp -f "$file" "$STASH_DIR/$rel_file"
+
+ STASHED_FILES+=("$rel_file")
+}
+
+restore_files() {
+ local file
+
+ for file in "${STASHED_FILES[@]}"; do
+ mv -f "$STASH_DIR/$file" "$SRC/$file" || warn "can't restore file: $file"
+ done
+
+ STASHED_FILES=()
+}
+
+cleanup() {
+ set +o nounset
+ revert_patches
+ restore_files
+ [[ "$KEEP_TMP" -eq 0 ]] && rm -rf "$TMP_DIR"
+ return 0
+}
+
+trap_err() {
+ die "line ${BASH_LINENO[0]}: '$BASH_COMMAND'"
+}
+
+trap cleanup EXIT INT TERM HUP
+trap trap_err ERR
+
+__usage() {
+ cat <<EOF
+Usage: $SCRIPT [OPTIONS] PATCH_FILE(s)
+Generate a livepatch module.
+
+Options:
+ -f, --show-first-changed Show address of first changed instruction
+ -j, --jobs=<jobs> Build jobs to run simultaneously [default: $JOBS]
+ -o, --output=<file.ko> Output file [default: livepatch-<patch-name>.ko]
+ --no-replace Disable livepatch atomic replace
+ -v, --verbose Pass V=1 to kernel/module builds
+
+Advanced Options:
+ -d, --debug Show symbol/reloc cloning decisions
+ -S, --short-circuit=STEP Start at build step (requires prior --keep-tmp)
+ 1|orig Build original kernel (default)
+ 2|patched Build patched kernel
+ 3|diff Diff objects
+ 4|kmod Build patch module
+ -T, --keep-tmp Preserve tmp dir on exit
+
+EOF
+}
+
+usage() {
+ __usage >&2
+}
+
+process_args() {
+ local keep_tmp=0
+ local short
+ local long
+ local args
+
+ short="hfj:o:vdS:T"
+ long="help,show-first-changed,jobs:,output:,no-replace,verbose,debug,short-circuit:,keep-tmp"
+
+ args=$(getopt --options "$short" --longoptions "$long" -- "$@") || {
+ echo; usage; exit
+ }
+ eval set -- "$args"
+
+ while true; do
+ case "$1" in
+ -h | --help)
+ usage
+ exit 0
+ ;;
+ -f | --show-first-changed)
+ DIFF_CHECKSUM=1
+ shift
+ ;;
+ -j | --jobs)
+ JOBS="$2"
+ shift 2
+ ;;
+ -o | --output)
+ [[ "$2" != *.ko ]] && die "output filename should end with .ko"
+ OUTFILE="$2"
+ NAME="$(basename "$OUTFILE")"
+ NAME="${NAME%.ko}"
+ NAME="$(module_name_string "$NAME")"
+ shift 2
+ ;;
+ --no-replace)
+ REPLACE=0
+ shift
+ ;;
+ -v | --verbose)
+ VERBOSE="V=1"
+ shift
+ ;;
+ -d | --debug)
+ DEBUG_CLONE=1
+ keep_tmp=1
+ shift
+ ;;
+ -S | --short-circuit)
+ [[ ! -d "$TMP_DIR" ]] && die "--short-circuit requires preserved klp-tmp dir"
+ keep_tmp=1
+ case "$2" in
+ 1 | orig) SHORT_CIRCUIT=1; ;;
+ 2 | patched) SHORT_CIRCUIT=2; ;;
+ 3 | diff) SHORT_CIRCUIT=3; ;;
+ 4 | mod) SHORT_CIRCUIT=4; ;;
+ *) die "invalid short-circuit step '$2'" ;;
+ esac
+ shift 2
+ ;;
+ -T | --keep-tmp)
+ keep_tmp=1
+ shift
+ ;;
+ --)
+ shift
+ break
+ ;;
+ *)
+ usage
+ exit 1
+ ;;
+ esac
+ done
+
+ if [[ $# -eq 0 ]] && (( SHORT_CIRCUIT <= 2 )); then
+ usage
+ exit 1
+ fi
+
+ KEEP_TMP="$keep_tmp"
+ PATCHES=("$@")
+}
+
+# temporarily disable xtrace for especially verbose code
+xtrace_save() {
+ [[ -v XTRACE ]] && set +x
+ return 0
+}
+
+xtrace_restore() {
+ [[ -v XTRACE ]] && set -x
+ return 0
+}
+
+validate_config() {
+ xtrace_save "reading .config"
+ source "$CONFIG" || die "no .config file in $(dirname "$CONFIG")"
+ xtrace_restore
+
+ [[ -v CONFIG_LIVEPATCH ]] || \
+ die "CONFIG_LIVEPATCH not enabled"
+
+ [[ -v CONFIG_KLP_BUILD ]] || \
+ die "CONFIG_KLP_BUILD not enabled"
+
+ [[ -v CONFIG_GCC_PLUGIN_LATENT_ENTROPY ]] && \
+ die "kernel option 'CONFIG_GCC_PLUGIN_LATENT_ENTROPY' not supported"
+
+ [[ -v CONFIG_GCC_PLUGIN_RANDSTRUCT ]] && \
+ die "kernel option 'CONFIG_GCC_PLUGIN_RANDSTRUCT' not supported"
+
+ [[ -v CONFIG_AS_IS_LLVM ]] && \
+ [[ "$CONFIG_AS_VERSION" -lt 200000 ]] && \
+ die "Clang assembler version < 20 not supported"
+
+ return 0
+}
+
+# Only allow alphanumerics and '_' and '-' in the module name. Everything else
+# is replaced with '-'. Also truncate to 55 chars so the full name + NUL
+# terminator fits in the kernel's 56-byte module name array.
+module_name_string() {
+ echo "${1//[^a-zA-Z0-9_-]/-}" | cut -c 1-55
+}
+
+# If the module name wasn't specified on the cmdline with --output, give it a
+# name based on the patch name.
+set_module_name() {
+ [[ -v NAME ]] && return 0
+
+ if [[ "${#PATCHES[@]}" -eq 1 ]]; then
+ NAME="$(basename "${PATCHES[0]}")"
+ NAME="${NAME%.*}"
+ else
+ NAME="patch"
+ fi
+
+ NAME="livepatch-$NAME"
+ NAME="$(module_name_string "$NAME")"
+
+ OUTFILE="$NAME.ko"
+}
+
+# Hardcode the value printed by the localversion script to prevent patch
+# application from appending it with '+' due to a dirty working tree.
+set_kernelversion() {
+ local file="$SRC/scripts/setlocalversion"
+ local kernelrelease
+
+ stash_file "$file"
+
+ kernelrelease="$(cd "$SRC" && make syncconfig &>/dev/null && make -s kernelrelease)"
+ [[ -z "$kernelrelease" ]] && die "failed to get kernel version"
+
+ sed -i "2i echo $kernelrelease; exit 0" scripts/setlocalversion
+}
+
+get_patch_input_files() {
+ local patch="$1"
+
+ grep0 -E '^--- ' "$patch" \
+ | grep0 -v -e '/dev/null' -e '1969-12-31' -e '1970-01-01' \
+ | gawk '{print $2}' \
+ | sed 's|^[^/]*/||' \
+ | sort -u
+}
+
+get_patch_output_files() {
+ local patch="$1"
+
+ grep0 -E '^\+\+\+ ' "$patch" \
+ | grep0 -v -e '/dev/null' -e '1969-12-31' -e '1970-01-01' \
+ | gawk '{print $2}' \
+ | sed 's|^[^/]*/||' \
+ | sort -u
+}
+
+get_patch_files() {
+ local patch="$1"
+
+ { get_patch_input_files "$patch"; get_patch_output_files "$patch"; } \
+ | sort -u
+}
+
+check_unsupported_patches() {
+ local patch
+
+ for patch in "${PATCHES[@]}"; do
+ local files=()
+
+ get_patch_files "$patch" | mapfile -t files
+
+ for file in "${files[@]}"; do
+ case "$file" in
+ lib/*|*.S)
+ die "${patch}: unsupported patch to $file"
+ ;;
+ esac
+ done
+ done
+}
+
+apply_patch() {
+ local patch="$1"
+ shift
+ local extra_args=("$@")
+ local drift_regex="with fuzz|offset [0-9]+ line"
+ local output
+ local status
+
+ [[ ! -f "$patch" ]] && die "$patch doesn't exist"
+ status=0
+ output=$(patch -d "$SRC" -p1 --dry-run --no-backup-if-mismatch -r /dev/null "${extra_args[@]}" < "$patch" 2>&1) || status=$?
+ if [[ "$status" -ne 0 ]]; then
+ echo "$output" >&2
+ die "$patch did not apply"
+ elif [[ "$output" =~ $drift_regex ]]; then
+ echo "$output" >&2
+ warn "${patch} applied with fuzz"
+ fi
+
+ patch -d "$SRC" -p1 --no-backup-if-mismatch -r /dev/null "${extra_args[@]}" --silent < "$patch"
+ APPLIED_PATCHES+=("$patch")
+}
+
+revert_patch() {
+ local patch="$1"
+ local tmp=()
+
+ patch -d "$SRC" -p1 -R --silent --no-backup-if-mismatch -r /dev/null < "$patch"
+
+ for p in "${APPLIED_PATCHES[@]}"; do
+ [[ "$p" == "$patch" ]] && continue
+ tmp+=("$p")
+ done
+
+ APPLIED_PATCHES=("${tmp[@]}")
+}
+
+apply_patches() {
+ local extra_args=("$@")
+ local patch
+
+ for patch in "${PATCHES[@]}"; do
+ apply_patch "$patch" "${extra_args[@]}"
+ done
+}
+
+revert_patches() {
+ local patches=("${APPLIED_PATCHES[@]}")
+
+ for (( i=${#patches[@]}-1 ; i>=0 ; i-- )) ; do
+ revert_patch "${patches[$i]}"
+ done
+
+ APPLIED_PATCHES=()
+}
+
+validate_patches() {
+ check_unsupported_patches
+ apply_patches
+ revert_patches
+}
+
+do_init() {
+ # We're not yet smart enough to handle anything other than in-tree
+ # builds in pwd.
+ [[ ! "$SRC" -ef "$SCRIPT_DIR/../.." ]] && die "please run from the kernel root directory"
+ [[ ! "$OBJ" -ef "$SCRIPT_DIR/../.." ]] && die "please run from the kernel root directory"
+
+ (( SHORT_CIRCUIT <= 1 )) && rm -rf "$TMP_DIR"
+ mkdir -p "$TMP_DIR"
+
+ APPLIED_PATCHES=()
+
+ [[ -x "$FIX_PATCH_LINES" ]] || die "can't find fix-patch-lines"
+ command -v recountdiff &>/dev/null || die "recountdiff not found (install patchutils)"
+
+ validate_config
+ set_module_name
+ set_kernelversion
+}
+
+# Refresh the patch hunk headers, specifically the line numbers and counts.
+refresh_patch() {
+ local patch="$1"
+ local tmpdir="$PATCH_TMP_DIR"
+ local input_files=()
+ local output_files=()
+
+ rm -rf "$tmpdir"
+ mkdir -p "$tmpdir/a"
+ mkdir -p "$tmpdir/b"
+
+ # Get all source files affected by the patch
+ get_patch_input_files "$patch" | mapfile -t input_files
+ get_patch_output_files "$patch" | mapfile -t output_files
+
+ # Copy orig source files to 'a'
+ ( cd "$SRC" && echo "${input_files[@]}" | xargs cp --parents --target-directory="$tmpdir/a" )
+
+ # Copy patched source files to 'b'
+ apply_patch "$patch" "--silent"
+ ( cd "$SRC" && echo "${output_files[@]}" | xargs cp --parents --target-directory="$tmpdir/b" )
+ revert_patch "$patch"
+
+ # Diff 'a' and 'b' to make a clean patch
+ ( cd "$tmpdir" && diff -Nupr a b > "$patch" ) || true
+}
+
+# Copy the patches to a temporary directory, fix their lines so as not to
+# affect the __LINE__ macro for otherwise unchanged functions further down the
+# file, and update $PATCHES to point to the fixed patches.
+fix_patches() {
+ local idx
+ local i
+
+ rm -f "$TMP_DIR"/*.patch
+
+ idx=0001
+ for i in "${!PATCHES[@]}"; do
+ local old_patch="${PATCHES[$i]}"
+ local tmp_patch="$TMP_DIR/tmp.patch"
+ local patch="${PATCHES[$i]}"
+ local new_patch
+
+ new_patch="$TMP_DIR/$idx-fixed-$(basename "$patch")"
+
+ cp -f "$old_patch" "$tmp_patch"
+ refresh_patch "$tmp_patch"
+ "$FIX_PATCH_LINES" "$tmp_patch" | recountdiff > "$new_patch"
+
+ PATCHES[i]="$new_patch"
+
+ rm -f "$tmp_patch"
+ idx=$(printf "%04d" $(( 10#$idx + 1 )))
+ done
+}
+
+clean_kernel() {
+ local cmd=()
+
+ cmd=("make")
+ cmd+=("--silent")
+ cmd+=("-j$JOBS")
+ cmd+=("clean")
+
+ (
+ cd "$SRC"
+ "${cmd[@]}"
+ )
+}
+
+build_kernel() {
+ local build="$1"
+ local log="$TMP_DIR/build.log"
+ local objtool_args=()
+ local cmd=()
+
+ objtool_args=("--checksum")
+
+ cmd=("make")
+
+ # When a patch to a kernel module references a newly created unexported
+ # symbol which lives in vmlinux or another kernel module, the patched
+ # kernel build fails with the following error:
+ #
+ # ERROR: modpost: "klp_string" [fs/xfs/xfs.ko] undefined!
+ #
+ # The undefined symbols are working as designed in that case. They get
+ # resolved later when the livepatch module build link pulls all the
+ # disparate objects together into the same kernel module.
+ #
+ # It would be good to have a way to tell modpost to skip checking for
+ # undefined symbols altogether. For now, just convert the error to a
+ # warning with KBUILD_MODPOST_WARN, and grep out the warning to avoid
+ # confusing the user.
+ #
+ cmd+=("KBUILD_MODPOST_WARN=1")
+
+ cmd+=("$VERBOSE")
+ cmd+=("-j$JOBS")
+ cmd+=("KCFLAGS=-ffunction-sections -fdata-sections")
+ cmd+=("OBJTOOL_ARGS=${objtool_args[*]}")
+ cmd+=("vmlinux")
+ cmd+=("modules")
+
+ (
+ cd "$SRC"
+ "${cmd[@]}" \
+ 1> >(tee -a "$log") \
+ 2> >(tee -a "$log" | grep0 -v "modpost.*undefined!" >&2)
+ ) || die "$build kernel build failed"
+}
+
+find_objects() {
+ local opts=("$@")
+
+ # Find root-level vmlinux.o and non-root-level .ko files,
+ # excluding klp-tmp/ and .git/
+ find "$OBJ" \( -path "$TMP_DIR" -o -path "$OBJ/.git" -o -regex "$OBJ/[^/][^/]*\.ko" \) -prune -o \
+ -type f "${opts[@]}" \
+ \( -name "*.ko" -o -path "$OBJ/vmlinux.o" \) \
+ -printf '%P\n'
+}
+
+# Copy all .o archives to $ORIG_DIR
+copy_orig_objects() {
+ local files=()
+
+ rm -rf "$ORIG_DIR"
+ mkdir -p "$ORIG_DIR"
+
+ find_objects | mapfile -t files
+
+ xtrace_save "copying orig objects"
+ for _file in "${files[@]}"; do
+ local rel_file="${_file/.ko/.o}"
+ local file="$OBJ/$rel_file"
+ local orig_file="$ORIG_DIR/$rel_file"
+ local orig_dir="$(dirname "$orig_file")"
+
+ [[ ! -f "$file" ]] && die "missing $(basename "$file") for $_file"
+
+ mkdir -p "$orig_dir"
+ cp -f "$file" "$orig_dir"
+ done
+ xtrace_restore
+
+ mv -f "$TMP_DIR/build.log" "$ORIG_DIR"
+ touch "$TIMESTAMP"
+}
+
+# Copy all changed objects to $PATCHED_DIR
+copy_patched_objects() {
+ local files=()
+ local opts=()
+ local found=0
+
+ rm -rf "$PATCHED_DIR"
+ mkdir -p "$PATCHED_DIR"
+
+ # Note this doesn't work with some configs, thus the 'cmp' below.
+ opts=("-newer")
+ opts+=("$TIMESTAMP")
+
+ find_objects "${opts[@]}" | mapfile -t files
+
+ xtrace_save "copying changed objects"
+ for _file in "${files[@]}"; do
+ local rel_file="${_file/.ko/.o}"
+ local file="$OBJ/$rel_file"
+ local orig_file="$ORIG_DIR/$rel_file"
+ local patched_file="$PATCHED_DIR/$rel_file"
+ local patched_dir="$(dirname "$patched_file")"
+
+ [[ ! -f "$file" ]] && die "missing $(basename "$file") for $_file"
+
+ cmp -s "$orig_file" "$file" && continue
+
+ mkdir -p "$patched_dir"
+ cp -f "$file" "$patched_dir"
+ found=1
+ done
+ xtrace_restore
+
+ (( found == 0 )) && die "no changes detected"
+
+ mv -f "$TMP_DIR/build.log" "$PATCHED_DIR"
+}
+
+# Diff changed objects, writing output object to $DIFF_DIR
+diff_objects() {
+ local log="$KLP_DIFF_LOG"
+ local files=()
+ local opts=()
+
+ rm -rf "$DIFF_DIR"
+ mkdir -p "$DIFF_DIR"
+
+ find "$PATCHED_DIR" -type f -name "*.o" | mapfile -t files
+ [[ ${#files[@]} -eq 0 ]] && die "no changes detected"
+
+ [[ -v DEBUG_CLONE ]] && opts=("--debug")
+
+ # Diff all changed objects
+ for file in "${files[@]}"; do
+ local rel_file="${file#"$PATCHED_DIR"/}"
+ local orig_file="$rel_file"
+ local patched_file="$PATCHED_DIR/$rel_file"
+ local out_file="$DIFF_DIR/$rel_file"
+ local filter=()
+ local cmd=()
+
+ mkdir -p "$(dirname "$out_file")"
+
+ cmd=("$SRC/tools/objtool/objtool")
+ cmd+=("klp")
+ cmd+=("diff")
+ (( ${#opts[@]} > 0 )) && cmd+=("${opts[@]}")
+ cmd+=("$orig_file")
+ cmd+=("$patched_file")
+ cmd+=("$out_file")
+
+ if [[ -v DIFF_CHECKSUM ]]; then
+ filter=("grep0")
+ filter+=("-Ev")
+ filter+=("DEBUG: .*checksum: ")
+ else
+ filter=("cat")
+ fi
+
+ (
+ cd "$ORIG_DIR"
+ "${cmd[@]}" \
+ 1> >(tee -a "$log") \
+ 2> >(tee -a "$log" | "${filter[@]}" >&2) || \
+ die "objtool klp diff failed"
+ )
+ done
+}
+
+# For each changed object, run objtool with --debug-checksum to get the
+# per-instruction checksums, and then diff those to find the first changed
+# instruction for each function.
+diff_checksums() {
+ local orig_log="$ORIG_DIR/checksum.log"
+ local patched_log="$PATCHED_DIR/checksum.log"
+ local -A funcs
+ local cmd=()
+ local line
+ local file
+ local func
+
+ gawk '/\.o: changed function: / {
+ sub(/:$/, "", $1)
+ print $1, $NF
+ }' "$KLP_DIFF_LOG" | mapfile -t lines
+
+ for line in "${lines[@]}"; do
+ read -r file func <<< "$line"
+ if [[ ! -v funcs["$file"] ]]; then
+ funcs["$file"]="$func"
+ else
+ funcs["$file"]+=" $func"
+ fi
+ done
+
+ cmd=("$SRC/tools/objtool/objtool")
+ cmd+=("--checksum")
+ cmd+=("--link")
+ cmd+=("--dry-run")
+
+ for file in "${!funcs[@]}"; do
+ local opt="--debug-checksum=${funcs[$file]// /,}"
+
+ (
+ cd "$ORIG_DIR"
+ "${cmd[@]}" "$opt" "$file" &> "$orig_log" || \
+ ( cat "$orig_log" >&2; die "objtool --debug-checksum failed" )
+
+ cd "$PATCHED_DIR"
+ "${cmd[@]}" "$opt" "$file" &> "$patched_log" || \
+ ( cat "$patched_log" >&2; die "objtool --debug-checksum failed" )
+ )
+
+ for func in ${funcs[$file]}; do
+ diff <( grep0 -E "^DEBUG: .*checksum: $func " "$orig_log" | sed "s|$ORIG_DIR/||") \
+ <( grep0 -E "^DEBUG: .*checksum: $func " "$patched_log" | sed "s|$PATCHED_DIR/||") \
+ | gawk '/^< DEBUG: / {
+ gsub(/:/, "")
+ printf "%s: %s: %s\n", $3, $5, $6
+ exit
+ }' || true
+ done
+ done
+}
+
+# Build and post-process livepatch module in $KMOD_DIR
+build_patch_module() {
+ local makefile="$KMOD_DIR/Kbuild"
+ local log="$KMOD_DIR/build.log"
+ local kmod_file
+ local cflags=()
+ local files=()
+ local cmd=()
+
+ rm -rf "$KMOD_DIR"
+ mkdir -p "$KMOD_DIR"
+
+ cp -f "$SRC/scripts/livepatch/init.c" "$KMOD_DIR"
+
+ echo "obj-m := $NAME.o" > "$makefile"
+ echo -n "$NAME-y := init.o" >> "$makefile"
+
+ find "$DIFF_DIR" -type f -name "*.o" | mapfile -t files
+ [[ ${#files[@]} -eq 0 ]] && die "no changes detected"
+
+ for file in "${files[@]}"; do
+ local rel_file="${file#"$DIFF_DIR"/}"
+ local orig_file="$ORIG_DIR/$rel_file"
+ local orig_dir="$(dirname "$orig_file")"
+ local kmod_file="$KMOD_DIR/$rel_file"
+ local kmod_dir="$(dirname "$kmod_file")"
+ local cmd_file="$kmod_dir/.$(basename "$file").cmd"
+
+ mkdir -p "$kmod_dir"
+ cp -f "$file" "$kmod_dir"
+
+ # Tell kbuild this is a prebuilt object
+ cp -f "$file" "${kmod_file}_shipped"
+
+ # Make modpost happy
+ touch "$cmd_file"
+
+ echo -n " $rel_file" >> "$makefile"
+ done
+
+ echo >> "$makefile"
+
+ cflags=("-ffunction-sections")
+ cflags+=("-fdata-sections")
+ [[ $REPLACE -eq 0 ]] && cflags+=("-DKLP_NO_REPLACE")
+
+ cmd=("make")
+ cmd+=("$VERBOSE")
+ cmd+=("-j$JOBS")
+ cmd+=("--directory=.")
+ cmd+=("M=$KMOD_DIR")
+ cmd+=("KCFLAGS=${cflags[*]}")
+
+ # Build a "normal" kernel module with init.c and the diffed objects
+ (
+ cd "$SRC"
+ "${cmd[@]}" \
+ 1> >(tee -a "$log") \
+ 2> >(tee -a "$log" >&2)
+ )
+
+ kmod_file="$KMOD_DIR/$NAME.ko"
+
+ # Save off the intermediate binary for debugging
+ cp -f "$kmod_file" "$kmod_file.orig"
+
+ # Work around issue where slight .config change makes corrupt BTF
+ objcopy --remove-section=.BTF "$kmod_file"
+
+ # Fix (and work around) linker wreckage for klp syms / relocs
+ "$SRC/tools/objtool/objtool" klp post-link "$kmod_file" || die "objtool klp post-link failed"
+
+ cp -f "$kmod_file" "$OUTFILE"
+}
+
+
+################################################################################
+
+process_args "$@"
+do_init
+
+if (( SHORT_CIRCUIT <= 2 )); then
+ status "Validating patch(es)"
+ validate_patches
+fi
+
+if (( SHORT_CIRCUIT <= 1 )); then
+ status "Building original kernel"
+ clean_kernel
+ build_kernel "original"
+ status "Copying original object files"
+ copy_orig_objects
+fi
+
+if (( SHORT_CIRCUIT <= 2 )); then
+ status "Fixing patch(es)"
+ fix_patches
+ apply_patches "--silent"
+ status "Building patched kernel"
+ build_kernel "patched"
+ revert_patches
+ status "Copying patched object files"
+ copy_patched_objects
+fi
+
+if (( SHORT_CIRCUIT <= 3 )); then
+ status "Diffing objects"
+ diff_objects
+ if [[ -v DIFF_CHECKSUM ]]; then
+ status "Finding first changed instructions"
+ diff_checksums
+ fi
+fi
+
+if (( SHORT_CIRCUIT <= 4 )); then
+ status "Building patch module: $OUTFILE"
+ build_patch_module
+fi
+
+status "SUCCESS"
diff --git a/scripts/make_fit.py b/scripts/make_fit.py
index 1683e5ec6e67..15ba26974fd7 100755
--- a/scripts/make_fit.py
+++ b/scripts/make_fit.py
@@ -10,10 +10,14 @@
Usage:
make_fit.py -A arm64 -n 'Linux-6.6' -O linux
-o arch/arm64/boot/image.fit -k /tmp/kern/arch/arm64/boot/image.itk
- @arch/arm64/boot/dts/dtbs-list -E -c gzip
+ -r /boot/initrd.img-6.14.0-27-generic @arch/arm64/boot/dts/dtbs-list
+ -E -c gzip
-Creates a FIT containing the supplied kernel and a set of devicetree files,
-either specified individually or listed in a file (with an '@' prefix).
+Creates a FIT containing the supplied kernel, an optional ramdisk, and a set of
+devicetree files, either specified individually or listed in a file (with an
+'@' prefix).
+
+Use -r to specify an existing ramdisk/initrd file.
Use -E to generate an external FIT (where the data is placed after the
FIT data structure). This allows parsing of the data without loading
@@ -29,12 +33,11 @@ looks at the .cmd files produced by the kernel build.
The resulting FIT can be booted by bootloaders which support FIT, such
as U-Boot, Linuxboot, Tianocore, etc.
-
-Note that this tool does not yet support adding a ramdisk / initrd.
"""
import argparse
import collections
+import multiprocessing
import os
import subprocess
import sys
@@ -48,11 +51,12 @@ import libfdt
CompTool = collections.namedtuple('CompTool', 'ext,tools')
COMP_TOOLS = {
- 'bzip2': CompTool('.bz2', 'bzip2'),
+ 'bzip2': CompTool('.bz2', 'pbzip2,bzip2'),
'gzip': CompTool('.gz', 'pigz,gzip'),
'lz4': CompTool('.lz4', 'lz4'),
'lzma': CompTool('.lzma', 'lzma'),
'lzo': CompTool('.lzo', 'lzop'),
+ 'xz': CompTool('.xz', 'xz'),
'zstd': CompTool('.zstd', 'zstd'),
}
@@ -81,6 +85,8 @@ def parse_args():
help='Specifies the operating system')
parser.add_argument('-k', '--kernel', type=str, required=True,
help='Specifies the (uncompressed) kernel input file (.itk)')
+ parser.add_argument('-r', '--ramdisk', type=str,
+ help='Specifies the ramdisk/initrd input file')
parser.add_argument('-v', '--verbose', action='store_true',
help='Enable verbose output')
parser.add_argument('dtbs', type=str, nargs='*',
@@ -98,7 +104,7 @@ def setup_fit(fsw, name):
fsw (libfdt.FdtSw): Object to use for writing
name (str): Name of kernel image
"""
- fsw.INC_SIZE = 65536
+ fsw.INC_SIZE = 16 << 20
fsw.finish_reservemap()
fsw.begin_node('')
fsw.property_string('description', f'{name} with devicetree set')
@@ -133,7 +139,28 @@ def write_kernel(fsw, data, args):
fsw.property_u32('entry', 0)
-def finish_fit(fsw, entries):
+def write_ramdisk(fsw, data, args):
+ """Write out the ramdisk image
+
+ Writes a ramdisk node along with the required properties
+
+ Args:
+ fsw (libfdt.FdtSw): Object to use for writing
+ data (bytes): Data to write (possibly compressed)
+ args (Namespace): Contains necessary strings:
+ arch: FIT architecture, e.g. 'arm64'
+ fit_os: Operating Systems, e.g. 'linux'
+ """
+ with fsw.add_node('ramdisk'):
+ fsw.property_string('description', 'Ramdisk')
+ fsw.property_string('type', 'ramdisk')
+ fsw.property_string('arch', args.arch)
+ fsw.property_string('compression', 'none')
+ fsw.property_string('os', args.os)
+ fsw.property('data', data)
+
+
+def finish_fit(fsw, entries, has_ramdisk=False):
"""Finish the FIT ready for use
Writes the /configurations node and subnodes
@@ -143,6 +170,7 @@ def finish_fit(fsw, entries):
entries (list of tuple): List of configurations:
str: Description of model
str: Compatible stringlist
+ has_ramdisk (bool): True if a ramdisk is included in the FIT
"""
fsw.end_node()
seq = 0
@@ -154,6 +182,8 @@ def finish_fit(fsw, entries):
fsw.property_string('description', model)
fsw.property('fdt', bytes(''.join(f'fdt-{x}\x00' for x in files), "ascii"))
fsw.property_string('kernel', 'kernel')
+ if has_ramdisk:
+ fsw.property_string('ramdisk', 'ramdisk')
fsw.end_node()
@@ -179,7 +209,12 @@ def compress_data(inf, compress):
done = False
for tool in comp.tools.split(','):
try:
- subprocess.call([tool, '-c'], stdin=inf, stdout=outf)
+ # Add parallel flags for tools that support them
+ cmd = [tool]
+ if tool in ('zstd', 'xz'):
+ cmd.extend(['-T0']) # Use all available cores
+ cmd.append('-c')
+ subprocess.call(cmd, stdin=inf, stdout=outf)
done = True
break
except FileNotFoundError:
@@ -191,15 +226,31 @@ def compress_data(inf, compress):
return comp_data
-def output_dtb(fsw, seq, fname, arch, compress):
+def compress_dtb(fname, compress):
+ """Compress a single DTB file
+
+ Args:
+ fname (str): Filename containing the DTB
+ compress (str): Compression algorithm, e.g. 'gzip'
+
+ Returns:
+ tuple: (str: fname, bytes: compressed_data)
+ """
+ with open(fname, 'rb') as inf:
+ compressed = compress_data(inf, compress)
+ return fname, compressed
+
+
+def output_dtb(fsw, seq, fname, arch, compress, data=None):
"""Write out a single devicetree to the FIT
Args:
fsw (libfdt.FdtSw): Object to use for writing
seq (int): Sequence number (1 for first)
fname (str): Filename containing the DTB
- arch: FIT architecture, e.g. 'arm64'
+ arch (str): FIT architecture, e.g. 'arm64'
compress (str): Compressed algorithm, e.g. 'gzip'
+ data (bytes): Pre-compressed data (optional)
"""
with fsw.add_node(f'fdt-{seq}'):
fsw.property_string('description', os.path.basename(fname))
@@ -207,9 +258,10 @@ def output_dtb(fsw, seq, fname, arch, compress):
fsw.property_string('arch', arch)
fsw.property_string('compression', compress)
- with open(fname, 'rb') as inf:
- compressed = compress_data(inf, compress)
- fsw.property('data', compressed)
+ if data is None:
+ with open(fname, 'rb') as inf:
+ data = compress_data(inf, compress)
+ fsw.property('data', data)
def process_dtb(fname, args):
@@ -249,30 +301,27 @@ def process_dtb(fname, args):
return (model, compat, files)
-def build_fit(args):
- """Build the FIT from the provided files and arguments
+
+def _process_dtbs(args, fsw, entries, fdts):
+ """Process all DTB files and add them to the FIT
Args:
- args (Namespace): Program arguments
+ args: Program arguments
+ fsw: FIT writer object
+ entries: List to append entries to
+ fdts: Dictionary of processed DTBs
Returns:
tuple:
- bytes: FIT data
- int: Number of configurations generated
- size: Total uncompressed size of data
+ Number of files processed
+ Total size of files processed
"""
seq = 0
size = 0
- fsw = libfdt.FdtSw()
- setup_fit(fsw, args.name)
- entries = []
- fdts = {}
- # Handle the kernel
- with open(args.kernel, 'rb') as inf:
- comp_data = compress_data(inf, args.compress)
- size += os.path.getsize(args.kernel)
- write_kernel(fsw, comp_data, args)
+ # First figure out the unique DTB files that need compression
+ todo = []
+ file_info = [] # List of (fname, model, compat, files) tuples
for fname in args.dtbs:
# Ignore non-DTB (*.dtb) files
@@ -282,24 +331,84 @@ def build_fit(args):
try:
(model, compat, files) = process_dtb(fname, args)
except Exception as e:
- sys.stderr.write(f"Error processing {fname}:\n")
+ sys.stderr.write(f'Error processing {fname}:\n')
raise e
+ file_info.append((fname, model, compat, files))
+ for fn in files:
+ if fn not in fdts and fn not in todo:
+ todo.append(fn)
+
+ # Compress all DTBs in parallel
+ cache = {}
+ if todo and args.compress != 'none':
+ if args.verbose:
+ print(f'Compressing {len(todo)} DTBs...')
+
+ with multiprocessing.Pool() as pool:
+ compress_args = [(fn, args.compress) for fn in todo]
+ # unpacks each tuple, calls compress_dtb(fn, compress) in parallel
+ results = pool.starmap(compress_dtb, compress_args)
+
+ cache = dict(results)
+
+ # Now write all DTBs to the FIT using pre-compressed data
+ for fname, model, compat, files in file_info:
for fn in files:
if fn not in fdts:
seq += 1
size += os.path.getsize(fn)
- output_dtb(fsw, seq, fn, args.arch, args.compress)
+ output_dtb(fsw, seq, fn, args.arch, args.compress,
+ cache.get(fn))
fdts[fn] = seq
files_seq = [fdts[fn] for fn in files]
-
entries.append([model, compat, files_seq])
- finish_fit(fsw, entries)
+ return seq, size
+
+
+def build_fit(args):
+ """Build the FIT from the provided files and arguments
+
+ Args:
+ args (Namespace): Program arguments
+
+ Returns:
+ tuple:
+ bytes: FIT data
+ int: Number of configurations generated
+ size: Total uncompressed size of data
+ """
+ size = 0
+ fsw = libfdt.FdtSw()
+ setup_fit(fsw, args.name)
+ entries = []
+ fdts = {}
+
+ # Handle the kernel
+ with open(args.kernel, 'rb') as inf:
+ comp_data = compress_data(inf, args.compress)
+ size += os.path.getsize(args.kernel)
+ write_kernel(fsw, comp_data, args)
+
+ # Handle the ramdisk if provided. Compression is not supported as it is
+ # already compressed.
+ if args.ramdisk:
+ with open(args.ramdisk, 'rb') as inf:
+ data = inf.read()
+ size += len(data)
+ write_ramdisk(fsw, data, args)
+
+ count, fdt_size = _process_dtbs(args, fsw, entries, fdts)
+ size += fdt_size
+
+ finish_fit(fsw, entries, bool(args.ramdisk))
# Include the kernel itself in the returned file count
- return fsw.as_fdt().as_bytearray(), seq + 1, size
+ fdt = fsw.as_fdt()
+ fdt.pack()
+ return fdt.as_bytearray(), count + 1 + bool(args.ramdisk), size
def run_make_fit():
diff --git a/scripts/min-tool-version.sh b/scripts/min-tool-version.sh
index 99b5575c1ef7..b96ec2d379b6 100755
--- a/scripts/min-tool-version.sh
+++ b/scripts/min-tool-version.sh
@@ -31,10 +31,10 @@ llvm)
fi
;;
rustc)
- echo 1.78.0
+ echo 1.85.0
;;
bindgen)
- echo 0.65.1
+ echo 0.71.1
;;
*)
echo "$1: unknown tool" >&2
diff --git a/scripts/mod/devicetable-offsets.c b/scripts/mod/devicetable-offsets.c
index d3d00e85edf7..b4178c42d08f 100644
--- a/scripts/mod/devicetable-offsets.c
+++ b/scripts/mod/devicetable-offsets.c
@@ -1,4 +1,5 @@
// SPDX-License-Identifier: GPL-2.0
+#define COMPILE_OFFSETS
#include <linux/kbuild.h>
#include <linux/mod_devicetable.h>
@@ -198,6 +199,9 @@ int main(void)
DEVID(cpu_feature);
DEVID_FIELD(cpu_feature, feature);
+ DEVID(mcb_device_id);
+ DEVID_FIELD(mcb_device_id, device);
+
DEVID(mei_cl_device_id);
DEVID_FIELD(mei_cl_device_id, name);
DEVID_FIELD(mei_cl_device_id, uuid);
diff --git a/scripts/mod/file2alias.c b/scripts/mod/file2alias.c
index b3333560b95e..4e99393a35f1 100644
--- a/scripts/mod/file2alias.c
+++ b/scripts/mod/file2alias.c
@@ -1110,6 +1110,14 @@ static void do_cpu_entry(struct module *mod, void *symval)
module_alias_printf(mod, false, "cpu:type:*:feature:*%04X*", feature);
}
+/* Looks like: mcb:16zN */
+static void do_mcb_entry(struct module *mod, void *symval)
+{
+ DEF_FIELD(symval, mcb_device_id, device);
+
+ module_alias_printf(mod, false, "mcb:16z%03d", device);
+}
+
/* Looks like: mei:S:uuid:N:* */
static void do_mei_entry(struct module *mod, void *symval)
{
@@ -1444,6 +1452,7 @@ static const struct devtable devtable[] = {
{"mipscdmm", SIZE_mips_cdmm_device_id, do_mips_cdmm_entry},
{"x86cpu", SIZE_x86_cpu_id, do_x86cpu_entry},
{"cpu", SIZE_cpu_feature, do_cpu_entry},
+ {"mcb", SIZE_mcb_device_id, do_mcb_entry},
{"mei", SIZE_mei_cl_device_id, do_mei_entry},
{"rapidio", SIZE_rio_device_id, do_rio_entry},
{"ulpi", SIZE_ulpi_device_id, do_ulpi_entry},
diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c
index 47c8aa2a6939..abbcd3fc1394 100644
--- a/scripts/mod/modpost.c
+++ b/scripts/mod/modpost.c
@@ -56,7 +56,7 @@ static bool allow_missing_ns_imports;
static bool error_occurred;
-static bool extra_warn;
+static bool extra_warn __attribute__((unused));
bool target_is_big_endian;
bool host_is_big_endian;
@@ -244,6 +244,11 @@ static struct symbol *alloc_symbol(const char *name)
return s;
}
+static uint8_t get_symbol_flags(const struct symbol *sym)
+{
+ return sym->is_gpl_only ? KSYM_FLAG_GPL_ONLY : 0;
+}
+
/* For the hash of exported symbols */
static void hash_add_symbol(struct symbol *sym)
{
@@ -602,10 +607,19 @@ static int ignore_undef_symbol(struct elf_info *info, const char *symname)
/* Special register function linked on all modules during final link of .ko */
if (strstarts(symname, "_restgpr0_") ||
strstarts(symname, "_savegpr0_") ||
+ strstarts(symname, "_restgpr1_") ||
+ strstarts(symname, "_savegpr1_") ||
+ strstarts(symname, "_restfpr_") ||
+ strstarts(symname, "_savefpr_") ||
strstarts(symname, "_restvr_") ||
strstarts(symname, "_savevr_") ||
strcmp(symname, ".TOC.") == 0)
return 1;
+
+ /* ignore linker-created section bounds variables */
+ if (strstarts(symname, "__start_") || strstarts(symname, "__stop_"))
+ return 1;
+
/* Do not ignore this symbol */
return 0;
}
@@ -953,7 +967,7 @@ static int secref_whitelist(const char *fromsec, const char *fromsym,
/* symbols in data sections that may refer to any init/exit sections */
if (match(fromsec, PATTERNS(DATA_SECTIONS)) &&
match(tosec, PATTERNS(ALL_INIT_SECTIONS, ALL_EXIT_SECTIONS)) &&
- match(fromsym, PATTERNS("*_ops", "*_probe", "*_console")))
+ match(fromsym, PATTERNS("*_ops", "*_console")))
return 0;
/* Check for pattern 3 */
@@ -1862,9 +1876,12 @@ static void add_exported_symbols(struct buffer *buf, struct module *mod)
if (trim_unused_exports && !sym->used)
continue;
- buf_printf(buf, "KSYMTAB_%s(%s, \"%s\", \"%s\");\n",
+ buf_printf(buf, "KSYMTAB_%s(%s, \"%s\");\n",
sym->is_func ? "FUNC" : "DATA", sym->name,
- sym->is_gpl_only ? "_gpl" : "", sym->namespace);
+ sym->namespace);
+
+ buf_printf(buf, "SYMBOL_FLAGS(%s, 0x%02x);\n",
+ sym->name, get_symbol_flags(sym));
}
if (!modversions)
@@ -1882,8 +1899,8 @@ static void add_exported_symbols(struct buffer *buf, struct module *mod)
sym->name, mod->name, mod->is_vmlinux ? "" : ".ko",
sym->name);
- buf_printf(buf, "SYMBOL_CRC(%s, 0x%08x, \"%s\");\n",
- sym->name, sym->crc, sym->is_gpl_only ? "_gpl" : "");
+ buf_printf(buf, "SYMBOL_CRC(%s, 0x%08x);\n",
+ sym->name, sym->crc);
}
}
diff --git a/scripts/module.lds.S b/scripts/module.lds.S
index ee79c41059f3..b62683061d79 100644
--- a/scripts/module.lds.S
+++ b/scripts/module.lds.S
@@ -20,9 +20,8 @@ SECTIONS {
}
__ksymtab 0 : ALIGN(8) { *(SORT(___ksymtab+*)) }
- __ksymtab_gpl 0 : ALIGN(8) { *(SORT(___ksymtab_gpl+*)) }
__kcrctab 0 : ALIGN(4) { *(SORT(___kcrctab+*)) }
- __kcrctab_gpl 0 : ALIGN(4) { *(SORT(___kcrctab_gpl+*)) }
+ __kflagstab 0 : ALIGN(1) { *(SORT(___kflagstab+*)) }
.ctors 0 : ALIGN(8) { *(SORT(.ctors.*)) *(.ctors) }
.init_array 0 : ALIGN(8) { *(SORT(.init_array.*)) *(.init_array) }
@@ -32,33 +31,36 @@ SECTIONS {
__jump_table 0 : ALIGN(8) { KEEP(*(__jump_table)) }
__ex_table 0 : ALIGN(4) { KEEP(*(__ex_table)) }
- __patchable_function_entries : { *(__patchable_function_entries) }
+ __patchable_function_entries 0 : { *(__patchable_function_entries) }
+
+ .init.klp_funcs 0 : ALIGN(8) { KEEP(*(.init.klp_funcs)) }
+ .init.klp_objects 0 : ALIGN(8) { KEEP(*(.init.klp_objects)) }
#ifdef CONFIG_ARCH_USES_CFI_TRAPS
- __kcfi_traps : { KEEP(*(.kcfi_traps)) }
+ __kcfi_traps 0 : { KEEP(*(.kcfi_traps)) }
+#endif
+
+#ifndef CONFIG_ARCH_WANTS_MODULES_TEXT_SECTIONS
+ .text 0 : {
+ *(.text .text.[0-9a-zA-Z_]*)
+ }
#endif
-#ifdef CONFIG_LTO_CLANG
- /*
- * With CONFIG_LTO_CLANG, LLD always enables -fdata-sections and
- * -ffunction-sections, which increases the size of the final module.
- * Merge the split sections in the final binary.
- */
- .bss : {
+ .bss 0 : {
*(.bss .bss.[0-9a-zA-Z_]*)
*(.bss..L*)
}
- .data : {
+ .data 0 : {
*(.data .data.[0-9a-zA-Z_]*)
*(.data..L*)
}
- .rodata : {
+ .rodata 0 : {
*(.rodata .rodata.[0-9a-zA-Z_]*)
*(.rodata..L*)
}
-#endif
+
MOD_SEPARATE_CODETAG_SECTIONS()
}
diff --git a/scripts/package/builddeb b/scripts/package/builddeb
index 3627ca227e5a..ba1defc61652 100755
--- a/scripts/package/builddeb
+++ b/scripts/package/builddeb
@@ -139,7 +139,13 @@ install_kernel_headers () {
pdir=debian/$1
version=${1#linux-headers-}
- CC="${DEB_HOST_GNU_TYPE}-gcc" "${srctree}/scripts/package/install-extmod-build" "${pdir}/usr/src/linux-headers-${version}"
+ # Override $CC only for cross-compiles, to not unnecessarily rebuild
+ # scripts/ including plugins, which may lead to a full kernel rebuild.
+ if [ -n "${CROSS_COMPILE}" ]; then
+ CC="${DEB_HOST_GNU_TYPE}-gcc" "${srctree}/scripts/package/install-extmod-build" "${pdir}/usr/src/linux-headers-${version}"
+ else
+ "${srctree}/scripts/package/install-extmod-build" "${pdir}/usr/src/linux-headers-${version}"
+ fi
mkdir -p $pdir/lib/modules/$version/
ln -s /usr/src/linux-headers-$version $pdir/lib/modules/$version/build
diff --git a/scripts/package/install-extmod-build b/scripts/package/install-extmod-build
index 054fdf45cc37..f12e1ffe409e 100755
--- a/scripts/package/install-extmod-build
+++ b/scripts/package/install-extmod-build
@@ -32,6 +32,10 @@ mkdir -p "${destdir}"
echo tools/objtool/objtool
fi
+ if is_enabled CONFIG_DEBUG_INFO_BTF_MODULES; then
+ echo tools/bpf/resolve_btfids/resolve_btfids
+ fi
+
echo Module.symvers
echo "arch/${SRCARCH}/include/generated"
echo include/config/auto.conf
@@ -63,7 +67,7 @@ if [ "${CC}" != "${HOSTCC}" ]; then
# Clear VPATH and srcroot because the source files reside in the output
# directory.
# shellcheck disable=SC2016 # $(MAKE) and $(build) will be expanded by Make
- "${MAKE}" run-command KBUILD_RUN_COMMAND='+$(MAKE) HOSTCC='"${CC}"' VPATH= srcroot=. $(build)='"$(realpath --relative-to=. "${destdir}")"/scripts
+ "${MAKE}" run-command KBUILD_RUN_COMMAND='+$(MAKE) HOSTCC="'"${CC}"'" VPATH= srcroot=. $(build)='"$(realpath --relative-to=. "${destdir}")"/scripts
rm -f "${destdir}/scripts/Kbuild"
fi
diff --git a/scripts/package/kernel.spec b/scripts/package/kernel.spec
index 98f206cb7c60..b3c956205af0 100644
--- a/scripts/package/kernel.spec
+++ b/scripts/package/kernel.spec
@@ -45,7 +45,19 @@ This package provides kernel headers and makefiles sufficient to build modules
against the %{version} kernel package.
%endif
-%if %{with_debuginfo}
+%if %{with_debuginfo_manual}
+%package debuginfo
+Summary: Debug information package for the Linux kernel
+Group: Development/Debug
+AutoReq: 0
+AutoProv: 1
+%description debuginfo
+This package provides debug information for the kernel image and modules from the
+%{version} package.
+%define install_mod_strip 1
+%endif
+
+%if %{with_debuginfo_rpm}
# list of debuginfo-related options taken from distribution kernel.spec
# files
%undefine _include_minidebuginfo
@@ -59,11 +71,6 @@ against the %{version} kernel package.
%global _missing_build_ids_terminate_build 1
%global _no_recompute_build_ids 1
%{debug_package}
-%endif
-# some (but not all) versions of rpmbuild emit %%debug_package with
-# %%install. since we've already emitted it manually, that would cause
-# a package redefinition error. ensure that doesn't happen
-%define debug_package %{nil}
# later, we make all modules executable so that find-debuginfo.sh strips
# them up. but they don't actually need to be executable, so remove the
@@ -74,6 +81,13 @@ against the %{version} kernel package.
%{__os_install_post} \
find %{buildroot}/lib/modules/%{KERNELRELEASE} -name "*.ko" -type f \\\
| xargs --no-run-if-empty chmod u-x
+%else
+%define __spec_install_post /usr/lib/rpm/brp-compress || :
+%endif
+# some (but not all) versions of rpmbuild emit %%debug_package with
+# %%install. since we've already emitted it manually, that would cause
+# a package redefinition error. ensure that doesn't happen
+%define debug_package %{nil}
%prep
%setup -q -n linux
@@ -87,7 +101,7 @@ patch -p1 < %{SOURCE2}
mkdir -p %{buildroot}/lib/modules/%{KERNELRELEASE}
cp $(%{make} %{makeflags} -s image_name) %{buildroot}/lib/modules/%{KERNELRELEASE}/vmlinuz
# DEPMOD=true makes depmod no-op. We do not package depmod-generated files.
-%{make} %{makeflags} INSTALL_MOD_PATH=%{buildroot} DEPMOD=true modules_install
+%{make} %{makeflags} INSTALL_MOD_PATH=%{buildroot} %{?install_mod_strip:INSTALL_MOD_STRIP=1} DEPMOD=true modules_install
%{make} %{makeflags} INSTALL_HDR_PATH=%{buildroot}/usr headers_install
cp System.map %{buildroot}/lib/modules/%{KERNELRELEASE}
cp .config %{buildroot}/lib/modules/%{KERNELRELEASE}/config
@@ -118,22 +132,43 @@ ln -fns /usr/src/kernels/%{KERNELRELEASE} %{buildroot}/lib/modules/%{KERNELRELEA
echo "%exclude /lib/modules/%{KERNELRELEASE}/build"
} > %{buildroot}/kernel.list
+%if 0%{with_debuginfo_manual}%{with_debuginfo_rpm} > 0
+# copying vmlinux directly to the debug directory means it will not get
+# stripped (but its source paths will still be collected + fixed up)
+mkdir -p %{buildroot}/usr/lib/debug/lib/modules/%{KERNELRELEASE}
+cp vmlinux %{buildroot}/usr/lib/debug/lib/modules/%{KERNELRELEASE}
+%endif
+
+%if %{with_debuginfo_rpm}
# make modules executable so that find-debuginfo.sh strips them. this
# will be undone later in %%__spec_install_post
find %{buildroot}/lib/modules/%{KERNELRELEASE} -name "*.ko" -type f \
| xargs --no-run-if-empty chmod u+x
+%endif
-%if %{with_debuginfo}
-# copying vmlinux directly to the debug directory means it will not get
-# stripped (but its source paths will still be collected + fixed up)
-mkdir -p %{buildroot}/usr/lib/debug/lib/modules/%{KERNELRELEASE}
-cp vmlinux %{buildroot}/usr/lib/debug/lib/modules/%{KERNELRELEASE}
+%if %{with_debuginfo_manual}
+echo /usr/lib/debug/lib/modules/%{KERNELRELEASE}/vmlinux > %{buildroot}/debuginfo.list
+while read -r mod; do
+ mod="${mod%.o}.ko"
+ dbg="%{buildroot}/usr/lib/debug/lib/modules/%{KERNELRELEASE}/kernel/${mod}"
+ buildid=$("${READELF:-readelf}" -n "${mod}" | sed -n 's@^.*Build ID: \(..\)\(.*\)@\1/\2@p')
+ link="%{buildroot}/usr/lib/debug/.build-id/${buildid}.debug"
+
+ mkdir -p "${dbg%/*}" "${link%/*}"
+ "${OBJCOPY:-objcopy}" --only-keep-debug "${mod}" "${dbg}"
+ ln -sf --relative "${dbg}" "${link}"
+
+ echo "${dbg#%{buildroot}}" >> %{buildroot}/debuginfo.list
+ echo "${link#%{buildroot}}" >> %{buildroot}/debuginfo.list
+done < modules.order
%endif
%clean
rm -rf %{buildroot}
+%if %{with_debuginfo_rpm}
rm -f debugfiles.list debuglinks.list debugsourcefiles.list debugsources.list \
elfbins.list
+%endif
%post
if [ -x /usr/bin/kernel-install ]; then
@@ -172,3 +207,9 @@ fi
/usr/src/kernels/%{KERNELRELEASE}
/lib/modules/%{KERNELRELEASE}/build
%endif
+
+%if %{with_debuginfo_manual}
+%files -f %{buildroot}/debuginfo.list debuginfo
+%defattr (-, root, root)
+%exclude /debuginfo.list
+%endif
diff --git a/scripts/package/mkspec b/scripts/package/mkspec
index c7375bfc25a9..c604f8c174e2 100755
--- a/scripts/package/mkspec
+++ b/scripts/package/mkspec
@@ -23,15 +23,47 @@ else
echo '%define with_devel 0'
fi
+# use %{debug_package} machinery to generate -debuginfo
+with_debuginfo_rpm=0
+# manually generate -debuginfo package
+with_debuginfo_manual=0
# debuginfo package generation uses find-debuginfo.sh under the hood,
# which only works on uncompressed modules that contain debuginfo
if grep -q CONFIG_DEBUG_INFO=y include/config/auto.conf &&
(! grep -q CONFIG_MODULE_COMPRESS=y include/config/auto.conf) &&
(! grep -q CONFIG_DEBUG_INFO_SPLIT=y include/config/auto.conf); then
-echo '%define with_debuginfo %{?_without_debuginfo: 0} %{?!_without_debuginfo: 1}'
-else
-echo '%define with_debuginfo 0'
+ # If module signing is enabled (which may be required to boot with
+ # lockdown enabled), the find-debuginfo.sh machinery cannot be used
+ # because the signatures will be stripped off the modules. However, due
+ # to an rpm bug in versions prior to 4.20.0
+ #
+ # https://github.com/rpm-software-management/rpm/issues/3057
+ # https://github.com/rpm-software-management/rpm/commit/49f906998f3cf1f4152162ca61ac0869251c380f
+ #
+ # We cannot provide our own debuginfo package because it does not listen
+ # to our custom files list, failing the build due to unpackaged files.
+ # Manually generate the debug info package if using rpm 4.20.0. If not
+ # using rpm 4.20.0, avoid generating a -debuginfo package altogether,
+ # as it is not safe.
+ if grep -q CONFIG_MODULE_SIG=y include/config/auto.conf; then
+ rpm_ver_str=$(rpm --version 2>/dev/null)
+ # Split the version on spaces
+ IFS=' '
+ set -- $rpm_ver_str
+ if [ "${1:-}" = RPM -a "${2:-}" = version ]; then
+ IFS=.
+ set -- $3
+ rpm_ver=$(( 1000000 * $1 + 10000 * $2 + 100 * $3 + ${4:-0} ))
+ if [ "$rpm_ver" -ge 4200000 ]; then
+ with_debuginfo_manual='%{?_without_debuginfo:0}%{?!_without_debuginfo:1}'
+ fi
+ fi
+ else
+ with_debuginfo_rpm='%{?_without_debuginfo:0}%{?!_without_debuginfo:1}'
+ fi
fi
+echo "%define with_debuginfo_manual $with_debuginfo_manual"
+echo "%define with_debuginfo_rpm $with_debuginfo_rpm"
cat<<EOF
%define ARCH ${ARCH}
diff --git a/scripts/rust_is_available.sh b/scripts/rust_is_available.sh
index d2323de0692c..551f1ebd0dcb 100755
--- a/scripts/rust_is_available.sh
+++ b/scripts/rust_is_available.sh
@@ -121,14 +121,8 @@ fi
# Check that the Rust bindings generator is suitable.
#
# Non-stable and distributions' versions may have a version suffix, e.g. `-dev`.
-#
-# The dummy parameter `workaround-for-0.69.0` is required to support 0.69.0
-# (https://github.com/rust-lang/rust-bindgen/pull/2678) and 0.71.0
-# (https://github.com/rust-lang/rust-bindgen/pull/3040). It can be removed when
-# the minimum version is upgraded past the latter (0.69.1 and 0.71.1 both fixed
-# the issue).
rust_bindings_generator_output=$( \
- LC_ALL=C "$BINDGEN" --version workaround-for-0.69.0 2>/dev/null
+ LC_ALL=C "$BINDGEN" --version 2>/dev/null
) || rust_bindings_generator_code=$?
if [ -n "$rust_bindings_generator_code" ]; then
echo >&2 "***"
@@ -163,19 +157,6 @@ if [ "$rust_bindings_generator_cversion" -lt "$rust_bindings_generator_min_cvers
echo >&2 "***"
exit 1
fi
-if [ "$rust_bindings_generator_cversion" -eq 6600 ] ||
- [ "$rust_bindings_generator_cversion" -eq 6601 ]; then
- # Distributions may have patched the issue (e.g. Debian did).
- if ! "$BINDGEN" $(dirname $0)/rust_is_available_bindgen_0_66.h >/dev/null; then
- echo >&2 "***"
- echo >&2 "*** Rust bindings generator '$BINDGEN' versions 0.66.0 and 0.66.1 may not"
- echo >&2 "*** work due to a bug (https://github.com/rust-lang/rust-bindgen/pull/2567),"
- echo >&2 "*** unless patched (like Debian's)."
- echo >&2 "*** Your version: $rust_bindings_generator_version"
- echo >&2 "***"
- warning=1
- fi
-fi
# Check that the `libclang` used by the Rust bindings generator is suitable.
#
@@ -227,21 +208,6 @@ if [ "$bindgen_libclang_cversion" -lt "$bindgen_libclang_min_cversion" ]; then
exit 1
fi
-if [ "$bindgen_libclang_cversion" -ge 1900100 ] &&
- [ "$rust_bindings_generator_cversion" -lt 6905 ]; then
- # Distributions may have patched the issue (e.g. Debian did).
- if ! "$BINDGEN" $(dirname $0)/rust_is_available_bindgen_libclang_concat.h | grep -q foofoo; then
- echo >&2 "***"
- echo >&2 "*** Rust bindings generator '$BINDGEN' < 0.69.5 together with libclang >= 19.1"
- echo >&2 "*** may not work due to a bug (https://github.com/rust-lang/rust-bindgen/pull/2824),"
- echo >&2 "*** unless patched (like Debian's)."
- echo >&2 "*** Your bindgen version: $rust_bindings_generator_version"
- echo >&2 "*** Your libclang version: $bindgen_libclang_version"
- echo >&2 "***"
- warning=1
- fi
-fi
-
# If the C compiler is Clang, then we can also check whether its version
# matches the `libclang` version used by the Rust bindings generator.
#
diff --git a/scripts/rust_is_available_bindgen_0_66.h b/scripts/rust_is_available_bindgen_0_66.h
deleted file mode 100644
index c0431293421c..000000000000
--- a/scripts/rust_is_available_bindgen_0_66.h
+++ /dev/null
@@ -1,2 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-#define A "\0"
diff --git a/scripts/rust_is_available_bindgen_libclang_concat.h b/scripts/rust_is_available_bindgen_libclang_concat.h
deleted file mode 100644
index efc6e98d0f1d..000000000000
--- a/scripts/rust_is_available_bindgen_libclang_concat.h
+++ /dev/null
@@ -1,3 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-#define F(x) int x##x
-F(foo);
diff --git a/scripts/rust_is_available_test.py b/scripts/rust_is_available_test.py
index 4fcc319dea84..d6d54b7ea42a 100755
--- a/scripts/rust_is_available_test.py
+++ b/scripts/rust_is_available_test.py
@@ -54,37 +54,23 @@ else:
""")
@classmethod
- def generate_bindgen(cls, version_stdout, libclang_stderr, version_0_66_patched=False, libclang_concat_patched=False):
+ def generate_bindgen(cls, version_stdout, libclang_stderr):
if libclang_stderr is None:
libclang_case = f"raise SystemExit({cls.bindgen_default_bindgen_libclang_failure_exit_code})"
else:
libclang_case = f"print({repr(libclang_stderr)}, file=sys.stderr)"
- if version_0_66_patched:
- version_0_66_case = "pass"
- else:
- version_0_66_case = "raise SystemExit(1)"
-
- if libclang_concat_patched:
- libclang_concat_case = "print('pub static mut foofoo: ::std::os::raw::c_int;')"
- else:
- libclang_concat_case = "pass"
-
return cls.generate_executable(f"""#!/usr/bin/env python3
import sys
if "rust_is_available_bindgen_libclang.h" in " ".join(sys.argv):
{libclang_case}
-elif "rust_is_available_bindgen_0_66.h" in " ".join(sys.argv):
- {version_0_66_case}
-elif "rust_is_available_bindgen_libclang_concat.h" in " ".join(sys.argv):
- {libclang_concat_case}
else:
print({repr(version_stdout)})
""")
@classmethod
- def generate_bindgen_version(cls, stdout, version_0_66_patched=False):
- return cls.generate_bindgen(stdout, cls.bindgen_default_bindgen_libclang_stderr, version_0_66_patched)
+ def generate_bindgen_version(cls, stdout):
+ return cls.generate_bindgen(stdout, cls.bindgen_default_bindgen_libclang_stderr)
@classmethod
def generate_bindgen_libclang_failure(cls):
@@ -245,19 +231,6 @@ else:
result = self.run_script(self.Expected.FAILURE, { "BINDGEN": bindgen })
self.assertIn(f"Rust bindings generator '{bindgen}' is too old.", result.stderr)
- def test_bindgen_bad_version_0_66_0_and_0_66_1(self):
- for version in ("0.66.0", "0.66.1"):
- with self.subTest(version=version):
- bindgen = self.generate_bindgen_version(f"bindgen {version}")
- result = self.run_script(self.Expected.SUCCESS_WITH_WARNINGS, { "BINDGEN": bindgen })
- self.assertIn(f"Rust bindings generator '{bindgen}' versions 0.66.0 and 0.66.1 may not", result.stderr)
-
- def test_bindgen_bad_version_0_66_0_and_0_66_1_patched(self):
- for version in ("0.66.0", "0.66.1"):
- with self.subTest(version=version):
- bindgen = self.generate_bindgen_version(f"bindgen {version}", True)
- result = self.run_script(self.Expected.SUCCESS, { "BINDGEN": bindgen })
-
def test_bindgen_libclang_failure(self):
bindgen = self.generate_bindgen_libclang_failure()
result = self.run_script(self.Expected.FAILURE, { "BINDGEN": bindgen })
@@ -275,31 +248,6 @@ else:
result = self.run_script(self.Expected.FAILURE, { "BINDGEN": bindgen })
self.assertIn(f"libclang (used by the Rust bindings generator '{bindgen}') is too old.", result.stderr)
- def test_bindgen_bad_libclang_concat(self):
- for (bindgen_version, libclang_version, expected_not_patched) in (
- ("0.69.4", "18.0.0", self.Expected.SUCCESS),
- ("0.69.4", "19.1.0", self.Expected.SUCCESS_WITH_WARNINGS),
- ("0.69.4", "19.2.0", self.Expected.SUCCESS_WITH_WARNINGS),
-
- ("0.69.5", "18.0.0", self.Expected.SUCCESS),
- ("0.69.5", "19.1.0", self.Expected.SUCCESS),
- ("0.69.5", "19.2.0", self.Expected.SUCCESS),
-
- ("0.70.0", "18.0.0", self.Expected.SUCCESS),
- ("0.70.0", "19.1.0", self.Expected.SUCCESS),
- ("0.70.0", "19.2.0", self.Expected.SUCCESS),
- ):
- with self.subTest(bindgen_version=bindgen_version, libclang_version=libclang_version):
- cc = self.generate_clang(f"clang version {libclang_version}")
- libclang_stderr = f"scripts/rust_is_available_bindgen_libclang.h:2:9: warning: clang version {libclang_version} [-W#pragma-messages], err: false"
- bindgen = self.generate_bindgen(f"bindgen {bindgen_version}", libclang_stderr)
- result = self.run_script(expected_not_patched, { "BINDGEN": bindgen, "CC": cc })
- if expected_not_patched == self.Expected.SUCCESS_WITH_WARNINGS:
- self.assertIn(f"Rust bindings generator '{bindgen}' < 0.69.5 together with libclang >= 19.1", result.stderr)
-
- bindgen = self.generate_bindgen(f"bindgen {bindgen_version}", libclang_stderr, libclang_concat_patched=True)
- result = self.run_script(self.Expected.SUCCESS, { "BINDGEN": bindgen, "CC": cc })
-
def test_clang_matches_bindgen_libclang_different_bindgen(self):
bindgen = self.generate_bindgen_libclang("scripts/rust_is_available_bindgen_libclang.h:2:9: warning: clang version 999.0.0 [-W#pragma-messages], err: false")
result = self.run_script(self.Expected.SUCCESS_WITH_WARNINGS, { "BINDGEN": bindgen })
diff --git a/scripts/rustdoc_test_gen.rs b/scripts/rustdoc_test_gen.rs
index c8f9dc2ab976..d61a77219a8c 100644
--- a/scripts/rustdoc_test_gen.rs
+++ b/scripts/rustdoc_test_gen.rs
@@ -174,7 +174,7 @@ pub extern "C" fn {kunit_name}(__kunit_test: *mut ::kernel::bindings::kunit) {{
macro_rules! assert {{
($cond:expr $(,)?) => {{{{
::kernel::kunit_assert!(
- "{kunit_name}", "{real_path}", __DOCTEST_ANCHOR - {line}, $cond
+ "{kunit_name}", c"{real_path}", __DOCTEST_ANCHOR - {line}, $cond
);
}}}}
}}
@@ -184,7 +184,7 @@ pub extern "C" fn {kunit_name}(__kunit_test: *mut ::kernel::bindings::kunit) {{
macro_rules! assert_eq {{
($left:expr, $right:expr $(,)?) => {{{{
::kernel::kunit_assert_eq!(
- "{kunit_name}", "{real_path}", __DOCTEST_ANCHOR - {line}, $left, $right
+ "{kunit_name}", c"{real_path}", __DOCTEST_ANCHOR - {line}, $left, $right
);
}}}}
}}
@@ -206,8 +206,9 @@ pub extern "C" fn {kunit_name}(__kunit_test: *mut ::kernel::bindings::kunit) {{
/// The anchor where the test code body starts.
#[allow(unused)]
- static __DOCTEST_ANCHOR: i32 = ::core::line!() as i32 + {body_offset} + 1;
+ static __DOCTEST_ANCHOR: i32 = ::core::line!() as i32 + {body_offset} + 2;
{{
+ #![allow(unreachable_pub, clippy::disallowed_names)]
{body}
main();
}}
diff --git a/scripts/sign-file.c b/scripts/sign-file.c
index 7070245edfc1..86b010ac1514 100644
--- a/scripts/sign-file.c
+++ b/scripts/sign-file.c
@@ -24,10 +24,11 @@
#include <arpa/inet.h>
#include <openssl/opensslv.h>
#include <openssl/bio.h>
+#include <openssl/cms.h>
#include <openssl/evp.h>
#include <openssl/pem.h>
#include <openssl/err.h>
-#if OPENSSL_VERSION_MAJOR >= 3
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
# define USE_PKCS11_PROVIDER
# include <openssl/provider.h>
# include <openssl/store.h>
@@ -39,42 +40,7 @@
#endif
#include "ssl-common.h"
-/*
- * Use CMS if we have openssl-1.0.0 or newer available - otherwise we have to
- * assume that it's not available and its header file is missing and that we
- * should use PKCS#7 instead. Switching to the older PKCS#7 format restricts
- * the options we have on specifying the X.509 certificate we want.
- *
- * Further, older versions of OpenSSL don't support manually adding signers to
- * the PKCS#7 message so have to accept that we get a certificate included in
- * the signature message. Nor do such older versions of OpenSSL support
- * signing with anything other than SHA1 - so we're stuck with that if such is
- * the case.
- */
-#if defined(LIBRESSL_VERSION_NUMBER) || \
- OPENSSL_VERSION_NUMBER < 0x10000000L || \
- defined(OPENSSL_NO_CMS)
-#define USE_PKCS7
-#endif
-#ifndef USE_PKCS7
-#include <openssl/cms.h>
-#else
-#include <openssl/pkcs7.h>
-#endif
-
-struct module_signature {
- uint8_t algo; /* Public-key crypto algorithm [0] */
- uint8_t hash; /* Digest algorithm [0] */
- uint8_t id_type; /* Key identifier type [PKEY_ID_PKCS7] */
- uint8_t signer_len; /* Length of signer's name [0] */
- uint8_t key_id_len; /* Length of key identifier [0] */
- uint8_t __pad[3];
- uint32_t sig_len; /* Length of signature data */
-};
-
-#define PKEY_ID_PKCS7 2
-
-static char magic_number[] = "~Module signature appended~\n";
+#include <linux/module_signature.h>
static __attribute__((noreturn))
void format(void)
@@ -219,7 +185,7 @@ static X509 *read_x509(const char *x509_name)
int main(int argc, char **argv)
{
- struct module_signature sig_info = { .id_type = PKEY_ID_PKCS7 };
+ struct module_signature sig_info = { .id_type = MODULE_SIGNATURE_TYPE_PKCS7 };
char *hash_algo = NULL;
char *private_key_name = NULL, *raw_sig_name = NULL;
char *x509_name, *module_name, *dest_name;
@@ -228,15 +194,10 @@ int main(int argc, char **argv)
bool raw_sig = false;
unsigned char buf[4096];
unsigned long module_size, sig_size;
- unsigned int use_signed_attrs;
const EVP_MD *digest_algo;
EVP_PKEY *private_key;
-#ifndef USE_PKCS7
CMS_ContentInfo *cms = NULL;
unsigned int use_keyid = 0;
-#else
- PKCS7 *pkcs7 = NULL;
-#endif
X509 *x509;
BIO *bd, *bm;
int opt, n;
@@ -246,21 +207,13 @@ int main(int argc, char **argv)
key_pass = getenv("KBUILD_SIGN_PIN");
-#ifndef USE_PKCS7
- use_signed_attrs = CMS_NOATTR;
-#else
- use_signed_attrs = PKCS7_NOATTR;
-#endif
-
do {
opt = getopt(argc, argv, "sdpk");
switch (opt) {
case 's': raw_sig = true; break;
case 'p': save_sig = true; break;
case 'd': sign_only = true; save_sig = true; break;
-#ifndef USE_PKCS7
case 'k': use_keyid = CMS_USE_KEYID; break;
-#endif
case -1: break;
default: format();
}
@@ -289,14 +242,6 @@ int main(int argc, char **argv)
replace_orig = true;
}
-#ifdef USE_PKCS7
- if (strcmp(hash_algo, "sha1") != 0) {
- fprintf(stderr, "sign-file: %s only supports SHA1 signing\n",
- OPENSSL_VERSION_TEXT);
- exit(3);
- }
-#endif
-
/* Open the module file */
bm = BIO_new_file(module_name, "rb");
ERR(!bm, "%s", module_name);
@@ -314,28 +259,39 @@ int main(int argc, char **argv)
digest_algo = EVP_get_digestbyname(hash_algo);
ERR(!digest_algo, "EVP_get_digestbyname");
-#ifndef USE_PKCS7
+ unsigned int flags =
+ CMS_NOCERTS |
+ CMS_NOATTR |
+ CMS_PARTIAL |
+ CMS_BINARY |
+ CMS_DETACHED |
+ CMS_STREAM |
+ CMS_NOSMIMECAP |
+#ifdef CMS_NO_SIGNING_TIME
+ CMS_NO_SIGNING_TIME |
+#endif
+ use_keyid;
+
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L && OPENSSL_VERSION_NUMBER < 0x40000000L
+ if (EVP_PKEY_is_a(private_key, "ML-DSA-44") ||
+ EVP_PKEY_is_a(private_key, "ML-DSA-65") ||
+ EVP_PKEY_is_a(private_key, "ML-DSA-87")) {
+ /* ML-DSA + CMS_NOATTR is not supported in openssl-3.5
+ * and before.
+ */
+ flags &= ~CMS_NOATTR;
+ }
+#endif
+
/* Load the signature message from the digest buffer. */
- cms = CMS_sign(NULL, NULL, NULL, NULL,
- CMS_NOCERTS | CMS_PARTIAL | CMS_BINARY |
- CMS_DETACHED | CMS_STREAM);
+ cms = CMS_sign(NULL, NULL, NULL, NULL, flags);
ERR(!cms, "CMS_sign");
- ERR(!CMS_add1_signer(cms, x509, private_key, digest_algo,
- CMS_NOCERTS | CMS_BINARY |
- CMS_NOSMIMECAP | use_keyid |
- use_signed_attrs),
+ ERR(!CMS_add1_signer(cms, x509, private_key, digest_algo, flags),
"CMS_add1_signer");
- ERR(CMS_final(cms, bm, NULL, CMS_NOCERTS | CMS_BINARY) != 1,
+ ERR(CMS_final(cms, bm, NULL, flags) != 1,
"CMS_final");
-#else
- pkcs7 = PKCS7_sign(x509, private_key, NULL, bm,
- PKCS7_NOCERTS | PKCS7_BINARY |
- PKCS7_DETACHED | use_signed_attrs);
- ERR(!pkcs7, "PKCS7_sign");
-#endif
-
if (save_sig) {
char *sig_file_name;
BIO *b;
@@ -344,13 +300,8 @@ int main(int argc, char **argv)
"asprintf");
b = BIO_new_file(sig_file_name, "wb");
ERR(!b, "%s", sig_file_name);
-#ifndef USE_PKCS7
ERR(i2d_CMS_bio_stream(b, cms, NULL, 0) != 1,
"%s", sig_file_name);
-#else
- ERR(i2d_PKCS7_bio(b, pkcs7) != 1,
- "%s", sig_file_name);
-#endif
BIO_free(b);
}
@@ -377,11 +328,7 @@ int main(int argc, char **argv)
module_size = BIO_number_written(bd);
if (!raw_sig) {
-#ifndef USE_PKCS7
ERR(i2d_CMS_bio_stream(bd, cms, NULL, 0) != 1, "%s", dest_name);
-#else
- ERR(i2d_PKCS7_bio(bd, pkcs7) != 1, "%s", dest_name);
-#endif
} else {
BIO *b;
@@ -398,7 +345,8 @@ int main(int argc, char **argv)
sig_size = BIO_number_written(bd) - module_size;
sig_info.sig_len = htonl(sig_size);
ERR(BIO_write(bd, &sig_info, sizeof(sig_info)) < 0, "%s", dest_name);
- ERR(BIO_write(bd, magic_number, sizeof(magic_number) - 1) < 0, "%s", dest_name);
+ ERR(BIO_write(bd, MODULE_SIGNATURE_MARKER, sizeof(MODULE_SIGNATURE_MARKER) - 1) < 0,
+ "%s", dest_name);
ERR(BIO_free(bd) != 1, "%s", dest_name);
diff --git a/scripts/sorttable.c b/scripts/sorttable.c
index deed676bfe38..e8ed11c680c6 100644
--- a/scripts/sorttable.c
+++ b/scripts/sorttable.c
@@ -21,10 +21,8 @@
*/
#include <sys/types.h>
-#include <sys/mman.h>
#include <sys/stat.h>
#include <getopt.h>
-#include <elf.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
@@ -34,8 +32,7 @@
#include <errno.h>
#include <pthread.h>
-#include <tools/be_byteshift.h>
-#include <tools/le_byteshift.h>
+#include "elf-parse.h"
#ifndef EM_ARCOMPACT
#define EM_ARCOMPACT 93
@@ -65,335 +62,8 @@
#define EM_LOONGARCH 258
#endif
-typedef union {
- Elf32_Ehdr e32;
- Elf64_Ehdr e64;
-} Elf_Ehdr;
-
-typedef union {
- Elf32_Shdr e32;
- Elf64_Shdr e64;
-} Elf_Shdr;
-
-typedef union {
- Elf32_Sym e32;
- Elf64_Sym e64;
-} Elf_Sym;
-
-typedef union {
- Elf32_Rela e32;
- Elf64_Rela e64;
-} Elf_Rela;
-
-static uint32_t (*r)(const uint32_t *);
-static uint16_t (*r2)(const uint16_t *);
-static uint64_t (*r8)(const uint64_t *);
-static void (*w)(uint32_t, uint32_t *);
-static void (*w8)(uint64_t, uint64_t *);
typedef void (*table_sort_t)(char *, int);
-static struct elf_funcs {
- int (*compare_extable)(const void *a, const void *b);
- uint64_t (*ehdr_shoff)(Elf_Ehdr *ehdr);
- uint16_t (*ehdr_shstrndx)(Elf_Ehdr *ehdr);
- uint16_t (*ehdr_shentsize)(Elf_Ehdr *ehdr);
- uint16_t (*ehdr_shnum)(Elf_Ehdr *ehdr);
- uint64_t (*shdr_addr)(Elf_Shdr *shdr);
- uint64_t (*shdr_offset)(Elf_Shdr *shdr);
- uint64_t (*shdr_size)(Elf_Shdr *shdr);
- uint64_t (*shdr_entsize)(Elf_Shdr *shdr);
- uint32_t (*shdr_link)(Elf_Shdr *shdr);
- uint32_t (*shdr_name)(Elf_Shdr *shdr);
- uint32_t (*shdr_type)(Elf_Shdr *shdr);
- uint8_t (*sym_type)(Elf_Sym *sym);
- uint32_t (*sym_name)(Elf_Sym *sym);
- uint64_t (*sym_value)(Elf_Sym *sym);
- uint16_t (*sym_shndx)(Elf_Sym *sym);
- uint64_t (*rela_offset)(Elf_Rela *rela);
- uint64_t (*rela_info)(Elf_Rela *rela);
- uint64_t (*rela_addend)(Elf_Rela *rela);
- void (*rela_write_addend)(Elf_Rela *rela, uint64_t val);
-} e;
-
-static uint64_t ehdr64_shoff(Elf_Ehdr *ehdr)
-{
- return r8(&ehdr->e64.e_shoff);
-}
-
-static uint64_t ehdr32_shoff(Elf_Ehdr *ehdr)
-{
- return r(&ehdr->e32.e_shoff);
-}
-
-static uint64_t ehdr_shoff(Elf_Ehdr *ehdr)
-{
- return e.ehdr_shoff(ehdr);
-}
-
-#define EHDR_HALF(fn_name) \
-static uint16_t ehdr64_##fn_name(Elf_Ehdr *ehdr) \
-{ \
- return r2(&ehdr->e64.e_##fn_name); \
-} \
- \
-static uint16_t ehdr32_##fn_name(Elf_Ehdr *ehdr) \
-{ \
- return r2(&ehdr->e32.e_##fn_name); \
-} \
- \
-static uint16_t ehdr_##fn_name(Elf_Ehdr *ehdr) \
-{ \
- return e.ehdr_##fn_name(ehdr); \
-}
-
-EHDR_HALF(shentsize)
-EHDR_HALF(shstrndx)
-EHDR_HALF(shnum)
-
-#define SHDR_WORD(fn_name) \
-static uint32_t shdr64_##fn_name(Elf_Shdr *shdr) \
-{ \
- return r(&shdr->e64.sh_##fn_name); \
-} \
- \
-static uint32_t shdr32_##fn_name(Elf_Shdr *shdr) \
-{ \
- return r(&shdr->e32.sh_##fn_name); \
-} \
- \
-static uint32_t shdr_##fn_name(Elf_Shdr *shdr) \
-{ \
- return e.shdr_##fn_name(shdr); \
-}
-
-#define SHDR_ADDR(fn_name) \
-static uint64_t shdr64_##fn_name(Elf_Shdr *shdr) \
-{ \
- return r8(&shdr->e64.sh_##fn_name); \
-} \
- \
-static uint64_t shdr32_##fn_name(Elf_Shdr *shdr) \
-{ \
- return r(&shdr->e32.sh_##fn_name); \
-} \
- \
-static uint64_t shdr_##fn_name(Elf_Shdr *shdr) \
-{ \
- return e.shdr_##fn_name(shdr); \
-}
-
-#define SHDR_WORD(fn_name) \
-static uint32_t shdr64_##fn_name(Elf_Shdr *shdr) \
-{ \
- return r(&shdr->e64.sh_##fn_name); \
-} \
- \
-static uint32_t shdr32_##fn_name(Elf_Shdr *shdr) \
-{ \
- return r(&shdr->e32.sh_##fn_name); \
-} \
-static uint32_t shdr_##fn_name(Elf_Shdr *shdr) \
-{ \
- return e.shdr_##fn_name(shdr); \
-}
-
-SHDR_ADDR(addr)
-SHDR_ADDR(offset)
-SHDR_ADDR(size)
-SHDR_ADDR(entsize)
-
-SHDR_WORD(link)
-SHDR_WORD(name)
-SHDR_WORD(type)
-
-#define SYM_ADDR(fn_name) \
-static uint64_t sym64_##fn_name(Elf_Sym *sym) \
-{ \
- return r8(&sym->e64.st_##fn_name); \
-} \
- \
-static uint64_t sym32_##fn_name(Elf_Sym *sym) \
-{ \
- return r(&sym->e32.st_##fn_name); \
-} \
- \
-static uint64_t sym_##fn_name(Elf_Sym *sym) \
-{ \
- return e.sym_##fn_name(sym); \
-}
-
-#define SYM_WORD(fn_name) \
-static uint32_t sym64_##fn_name(Elf_Sym *sym) \
-{ \
- return r(&sym->e64.st_##fn_name); \
-} \
- \
-static uint32_t sym32_##fn_name(Elf_Sym *sym) \
-{ \
- return r(&sym->e32.st_##fn_name); \
-} \
- \
-static uint32_t sym_##fn_name(Elf_Sym *sym) \
-{ \
- return e.sym_##fn_name(sym); \
-}
-
-#define SYM_HALF(fn_name) \
-static uint16_t sym64_##fn_name(Elf_Sym *sym) \
-{ \
- return r2(&sym->e64.st_##fn_name); \
-} \
- \
-static uint16_t sym32_##fn_name(Elf_Sym *sym) \
-{ \
- return r2(&sym->e32.st_##fn_name); \
-} \
- \
-static uint16_t sym_##fn_name(Elf_Sym *sym) \
-{ \
- return e.sym_##fn_name(sym); \
-}
-
-static uint8_t sym64_type(Elf_Sym *sym)
-{
- return ELF64_ST_TYPE(sym->e64.st_info);
-}
-
-static uint8_t sym32_type(Elf_Sym *sym)
-{
- return ELF32_ST_TYPE(sym->e32.st_info);
-}
-
-static uint8_t sym_type(Elf_Sym *sym)
-{
- return e.sym_type(sym);
-}
-
-SYM_ADDR(value)
-SYM_WORD(name)
-SYM_HALF(shndx)
-
-#define __maybe_unused __attribute__((__unused__))
-
-#define RELA_ADDR(fn_name) \
-static uint64_t rela64_##fn_name(Elf_Rela *rela) \
-{ \
- return r8((uint64_t *)&rela->e64.r_##fn_name); \
-} \
- \
-static uint64_t rela32_##fn_name(Elf_Rela *rela) \
-{ \
- return r((uint32_t *)&rela->e32.r_##fn_name); \
-} \
- \
-static uint64_t __maybe_unused rela_##fn_name(Elf_Rela *rela) \
-{ \
- return e.rela_##fn_name(rela); \
-}
-
-RELA_ADDR(offset)
-RELA_ADDR(info)
-RELA_ADDR(addend)
-
-static void rela64_write_addend(Elf_Rela *rela, uint64_t val)
-{
- w8(val, (uint64_t *)&rela->e64.r_addend);
-}
-
-static void rela32_write_addend(Elf_Rela *rela, uint64_t val)
-{
- w(val, (uint32_t *)&rela->e32.r_addend);
-}
-
-/*
- * Get the whole file as a programming convenience in order to avoid
- * malloc+lseek+read+free of many pieces. If successful, then mmap
- * avoids copying unused pieces; else just read the whole file.
- * Open for both read and write.
- */
-static void *mmap_file(char const *fname, size_t *size)
-{
- int fd;
- struct stat sb;
- void *addr = NULL;
-
- fd = open(fname, O_RDWR);
- if (fd < 0) {
- perror(fname);
- return NULL;
- }
- if (fstat(fd, &sb) < 0) {
- perror(fname);
- goto out;
- }
- if (!S_ISREG(sb.st_mode)) {
- fprintf(stderr, "not a regular file: %s\n", fname);
- goto out;
- }
-
- addr = mmap(0, sb.st_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
- if (addr == MAP_FAILED) {
- fprintf(stderr, "Could not mmap file: %s\n", fname);
- goto out;
- }
-
- *size = sb.st_size;
-
-out:
- close(fd);
- return addr;
-}
-
-static uint32_t rbe(const uint32_t *x)
-{
- return get_unaligned_be32(x);
-}
-
-static uint16_t r2be(const uint16_t *x)
-{
- return get_unaligned_be16(x);
-}
-
-static uint64_t r8be(const uint64_t *x)
-{
- return get_unaligned_be64(x);
-}
-
-static uint32_t rle(const uint32_t *x)
-{
- return get_unaligned_le32(x);
-}
-
-static uint16_t r2le(const uint16_t *x)
-{
- return get_unaligned_le16(x);
-}
-
-static uint64_t r8le(const uint64_t *x)
-{
- return get_unaligned_le64(x);
-}
-
-static void wbe(uint32_t val, uint32_t *x)
-{
- put_unaligned_be32(val, x);
-}
-
-static void wle(uint32_t val, uint32_t *x)
-{
- put_unaligned_le32(val, x);
-}
-
-static void w8be(uint64_t val, uint64_t *x)
-{
- put_unaligned_be64(val, x);
-}
-
-static void w8le(uint64_t val, uint64_t *x)
-{
- put_unaligned_le64(val, x);
-}
-
/*
* Move reserved section indices SHN_LORESERVE..SHN_HIRESERVE out of
* the way to -256..-1, to avoid conflicting with real section
@@ -415,13 +85,13 @@ static inline unsigned int get_secindex(unsigned int shndx,
return SPECIAL(shndx);
if (shndx != SHN_XINDEX)
return shndx;
- return r(&symtab_shndx_start[sym_offs]);
+ return elf_parser.r(&symtab_shndx_start[sym_offs]);
}
static int compare_extable_32(const void *a, const void *b)
{
- Elf32_Addr av = r(a);
- Elf32_Addr bv = r(b);
+ Elf32_Addr av = elf_parser.r(a);
+ Elf32_Addr bv = elf_parser.r(b);
if (av < bv)
return -1;
@@ -430,18 +100,15 @@ static int compare_extable_32(const void *a, const void *b)
static int compare_extable_64(const void *a, const void *b)
{
- Elf64_Addr av = r8(a);
- Elf64_Addr bv = r8(b);
+ Elf64_Addr av = elf_parser.r8(a);
+ Elf64_Addr bv = elf_parser.r8(b);
if (av < bv)
return -1;
return av > bv;
}
-static int compare_extable(const void *a, const void *b)
-{
- return e.compare_extable(a, b);
-}
+static int (*compare_extable)(const void *a, const void *b);
static inline void *get_index(void *start, int entsize, int index)
{
@@ -577,7 +244,7 @@ static int (*compare_values)(const void *a, const void *b);
/* Only used for sorting mcount table */
static void rela_write_addend(Elf_Rela *rela, uint64_t val)
{
- e.rela_write_addend(rela, val);
+ elf_parser.rela_write_addend(rela, val);
}
struct func_info {
@@ -792,9 +459,9 @@ static int fill_addrs(void *ptr, uint64_t size, void *addrs)
for (; ptr < end; ptr += long_size, addrs += long_size, count++) {
if (long_size == 4)
- *(uint32_t *)ptr = r(addrs);
+ *(uint32_t *)ptr = elf_parser.r(addrs);
else
- *(uint64_t *)ptr = r8(addrs);
+ *(uint64_t *)ptr = elf_parser.r8(addrs);
}
return count;
}
@@ -805,9 +472,9 @@ static void replace_addrs(void *ptr, uint64_t size, void *addrs)
for (; ptr < end; ptr += long_size, addrs += long_size) {
if (long_size == 4)
- w(*(uint32_t *)ptr, addrs);
+ elf_parser.w(*(uint32_t *)ptr, addrs);
else
- w8(*(uint64_t *)ptr, addrs);
+ elf_parser.w8(*(uint64_t *)ptr, addrs);
}
}
@@ -1111,7 +778,7 @@ static int do_sort(Elf_Ehdr *ehdr,
sym_value(sort_needed_sym) - shdr_addr(sort_needed_sec);
/* extable has been sorted, clear the flag */
- w(0, sort_needed_loc);
+ elf_parser.w(0, sort_needed_loc);
rc = 0;
out:
@@ -1155,8 +822,8 @@ out:
static int compare_relative_table(const void *a, const void *b)
{
- int32_t av = (int32_t)r(a);
- int32_t bv = (int32_t)r(b);
+ int32_t av = (int32_t)elf_parser.r(a);
+ int32_t bv = (int32_t)elf_parser.r(b);
if (av < bv)
return -1;
@@ -1175,7 +842,7 @@ static void sort_relative_table(char *extab_image, int image_size)
*/
while (i < image_size) {
uint32_t *loc = (uint32_t *)(extab_image + i);
- w(r(loc) + i, loc);
+ elf_parser.w(elf_parser.r(loc) + i, loc);
i += 4;
}
@@ -1185,7 +852,7 @@ static void sort_relative_table(char *extab_image, int image_size)
i = 0;
while (i < image_size) {
uint32_t *loc = (uint32_t *)(extab_image + i);
- w(r(loc) - i, loc);
+ elf_parser.w(elf_parser.r(loc) - i, loc);
i += 4;
}
}
@@ -1197,8 +864,8 @@ static void sort_relative_table_with_data(char *extab_image, int image_size)
while (i < image_size) {
uint32_t *loc = (uint32_t *)(extab_image + i);
- w(r(loc) + i, loc);
- w(r(loc + 1) + i + 4, loc + 1);
+ elf_parser.w(elf_parser.r(loc) + i, loc);
+ elf_parser.w(elf_parser.r(loc + 1) + i + 4, loc + 1);
/* Don't touch the fixup type or data */
i += sizeof(uint32_t) * 3;
@@ -1210,8 +877,8 @@ static void sort_relative_table_with_data(char *extab_image, int image_size)
while (i < image_size) {
uint32_t *loc = (uint32_t *)(extab_image + i);
- w(r(loc) - i, loc);
- w(r(loc + 1) - (i + 4), loc + 1);
+ elf_parser.w(elf_parser.r(loc) - i, loc);
+ elf_parser.w(elf_parser.r(loc + 1) - (i + 4), loc + 1);
/* Don't touch the fixup type or data */
i += sizeof(uint32_t) * 3;
@@ -1223,35 +890,7 @@ static int do_file(char const *const fname, void *addr)
Elf_Ehdr *ehdr = addr;
table_sort_t custom_sort = NULL;
- switch (ehdr->e32.e_ident[EI_DATA]) {
- case ELFDATA2LSB:
- r = rle;
- r2 = r2le;
- r8 = r8le;
- w = wle;
- w8 = w8le;
- break;
- case ELFDATA2MSB:
- r = rbe;
- r2 = r2be;
- r8 = r8be;
- w = wbe;
- w8 = w8be;
- break;
- default:
- fprintf(stderr, "unrecognized ELF data encoding %d: %s\n",
- ehdr->e32.e_ident[EI_DATA], fname);
- return -1;
- }
-
- if (memcmp(ELFMAG, ehdr->e32.e_ident, SELFMAG) != 0 ||
- (r2(&ehdr->e32.e_type) != ET_EXEC && r2(&ehdr->e32.e_type) != ET_DYN) ||
- ehdr->e32.e_ident[EI_VERSION] != EV_CURRENT) {
- fprintf(stderr, "unrecognized ET_EXEC/ET_DYN file %s\n", fname);
- return -1;
- }
-
- switch (r2(&ehdr->e32.e_machine)) {
+ switch (elf_map_machine(ehdr)) {
case EM_AARCH64:
#ifdef MCOUNT_SORT_ENABLED
sort_reloc = true;
@@ -1281,85 +920,37 @@ static int do_file(char const *const fname, void *addr)
break;
default:
fprintf(stderr, "unrecognized e_machine %d %s\n",
- r2(&ehdr->e32.e_machine), fname);
+ elf_parser.r2(&ehdr->e32.e_machine), fname);
return -1;
}
- switch (ehdr->e32.e_ident[EI_CLASS]) {
- case ELFCLASS32: {
- struct elf_funcs efuncs = {
- .compare_extable = compare_extable_32,
- .ehdr_shoff = ehdr32_shoff,
- .ehdr_shentsize = ehdr32_shentsize,
- .ehdr_shstrndx = ehdr32_shstrndx,
- .ehdr_shnum = ehdr32_shnum,
- .shdr_addr = shdr32_addr,
- .shdr_offset = shdr32_offset,
- .shdr_link = shdr32_link,
- .shdr_size = shdr32_size,
- .shdr_name = shdr32_name,
- .shdr_type = shdr32_type,
- .shdr_entsize = shdr32_entsize,
- .sym_type = sym32_type,
- .sym_name = sym32_name,
- .sym_value = sym32_value,
- .sym_shndx = sym32_shndx,
- .rela_offset = rela32_offset,
- .rela_info = rela32_info,
- .rela_addend = rela32_addend,
- .rela_write_addend = rela32_write_addend,
- };
-
- e = efuncs;
+ switch (elf_map_long_size(addr)) {
+ case 4:
+ compare_extable = compare_extable_32,
long_size = 4;
extable_ent_size = 8;
- if (r2(&ehdr->e32.e_ehsize) != sizeof(Elf32_Ehdr) ||
- r2(&ehdr->e32.e_shentsize) != sizeof(Elf32_Shdr)) {
+ if (elf_parser.r2(&ehdr->e32.e_ehsize) != sizeof(Elf32_Ehdr) ||
+ elf_parser.r2(&ehdr->e32.e_shentsize) != sizeof(Elf32_Shdr)) {
fprintf(stderr,
"unrecognized ET_EXEC/ET_DYN file: %s\n", fname);
return -1;
}
- }
break;
- case ELFCLASS64: {
- struct elf_funcs efuncs = {
- .compare_extable = compare_extable_64,
- .ehdr_shoff = ehdr64_shoff,
- .ehdr_shentsize = ehdr64_shentsize,
- .ehdr_shstrndx = ehdr64_shstrndx,
- .ehdr_shnum = ehdr64_shnum,
- .shdr_addr = shdr64_addr,
- .shdr_offset = shdr64_offset,
- .shdr_link = shdr64_link,
- .shdr_size = shdr64_size,
- .shdr_name = shdr64_name,
- .shdr_type = shdr64_type,
- .shdr_entsize = shdr64_entsize,
- .sym_type = sym64_type,
- .sym_name = sym64_name,
- .sym_value = sym64_value,
- .sym_shndx = sym64_shndx,
- .rela_offset = rela64_offset,
- .rela_info = rela64_info,
- .rela_addend = rela64_addend,
- .rela_write_addend = rela64_write_addend,
- };
-
- e = efuncs;
+ case 8:
+ compare_extable = compare_extable_64,
long_size = 8;
extable_ent_size = 16;
- if (r2(&ehdr->e64.e_ehsize) != sizeof(Elf64_Ehdr) ||
- r2(&ehdr->e64.e_shentsize) != sizeof(Elf64_Shdr)) {
+ if (elf_parser.r2(&ehdr->e64.e_ehsize) != sizeof(Elf64_Ehdr) ||
+ elf_parser.r2(&ehdr->e64.e_shentsize) != sizeof(Elf64_Shdr)) {
fprintf(stderr,
"unrecognized ET_EXEC/ET_DYN file: %s\n",
fname);
return -1;
}
- }
break;
default:
fprintf(stderr, "unrecognized ELF class %d %s\n",
@@ -1398,7 +989,7 @@ int main(int argc, char *argv[])
/* Process each file in turn, allowing deep failure. */
for (i = optind; i < argc; i++) {
- addr = mmap_file(argv[i], &size);
+ addr = elf_map(argv[i], &size, (1 << ET_EXEC) | (1 << ET_DYN));
if (!addr) {
++n_error;
continue;
@@ -1407,7 +998,7 @@ int main(int argc, char *argv[])
if (do_file(argv[i], addr))
++n_error;
- munmap(addr, size);
+ elf_unmap(addr, size);
}
return !!n_error;
diff --git a/scripts/spdxcheck.py b/scripts/spdxcheck.py
index 8d608f61bf37..908029e45ca2 100755
--- a/scripts/spdxcheck.py
+++ b/scripts/spdxcheck.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
# SPDX-License-Identifier: GPL-2.0
-# Copyright Thomas Gleixner <tglx@linutronix.de>
+# Copyright Linutronix GmbH, Thomas Gleixner <tglx@kernel.org>
from argparse import ArgumentParser
from ply import lex, yacc
diff --git a/scripts/spelling.txt b/scripts/spelling.txt
index 1e89b92c2f9a..2f2e81dbda03 100644
--- a/scripts/spelling.txt
+++ b/scripts/spelling.txt
@@ -57,8 +57,8 @@ acknowledgement||acknowledgment
ackowledge||acknowledge
ackowledged||acknowledged
acording||according
-activete||activate
actived||activated
+activete||activate
actualy||actually
actvie||active
acumulating||accumulating
@@ -66,12 +66,12 @@ acumulative||accumulative
acumulator||accumulator
acutally||actually
adapater||adapter
+adddress||address
adderted||asserted
addional||additional
additionaly||additionally
additonal||additional
addres||address
-adddress||address
addreses||addresses
addresss||address
addrress||address
@@ -95,9 +95,9 @@ alegorical||allegorical
algined||aligned
algorith||algorithm
algorithmical||algorithmically
+algorithmn||algorithm
algoritm||algorithm
algoritms||algorithms
-algorithmn||algorithm
algorrithm||algorithm
algorritm||algorithm
aligment||alignment
@@ -128,20 +128,20 @@ amount of times||number of times
amout||amount
amplifer||amplifier
amplifyer||amplifier
-an union||a union
-an user||a user
-an userspace||a userspace
-an one||a one
analysator||analyzer
ang||and
anniversery||anniversary
annoucement||announcement
anomolies||anomalies
anomoly||anomaly
+an one||a one
anonynous||anonymous
+an union||a union
+an user||a user
+an userspace||a userspace
anway||anyway
-aplication||application
apeared||appeared
+aplication||application
appearence||appearance
applicaion||application
appliction||application
@@ -155,8 +155,8 @@ approriately||appropriately
apropriate||appropriate
aquainted||acquainted
aquired||acquired
-aquisition||acquisition
aquires||acquires
+aquisition||acquisition
arbitary||arbitrary
architechture||architecture
archtecture||architecture
@@ -189,30 +189,30 @@ assum||assume
assumtpion||assumption
asume||assume
asuming||assuming
-asycronous||asynchronous
asychronous||asynchronous
+asycronous||asynchronous
+asymetric||asymmetric
+asymmeric||asymmetric
asynchnous||asynchronous
asynchrnous||asynchronous
-asynchronus||asynchronous
asynchromous||asynchronous
-asymetric||asymmetric
-asymmeric||asymmetric
+asynchronus||asynchronous
+atempt||attempt
atleast||at least
atomatically||automatically
atomicly||atomically
-atempt||attempt
atrributes||attributes
attachement||attachment
attatch||attach
attched||attached
attemp||attempt
-attemps||attempts
attemping||attempting
+attemps||attempts
attepmpt||attempt
attnetion||attention
attruibutes||attributes
-authentification||authentication
authenicated||authenticated
+authentification||authentication
automaticaly||automatically
automaticly||automatically
automatize||automate
@@ -257,6 +257,7 @@ begining||beginning
beter||better
betweeen||between
bianries||binaries
+binded||bound
bitmast||bitmask
bitwiedh||bitwidth
boardcast||broadcast
@@ -287,19 +288,19 @@ calucate||calculate
calulate||calculate
cancelation||cancellation
cancle||cancel
-cant||can't
-cant'||can't
-canot||cannot
-cann't||can't
cannnot||cannot
+cann't||can't
+canot||cannot
+cant'||can't
+cant||can't
capabiity||capability
capabilites||capabilities
capabilties||capabilities
capabilty||capability
capabitilies||capabilities
capablity||capability
-capatibilities||capabilities
capapbilities||capabilities
+capatibilities||capabilities
captuer||capture
caputure||capture
carefuly||carefully
@@ -307,9 +308,9 @@ cariage||carriage
casued||caused
catagory||category
cehck||check
+chache||cache
challange||challenge
challanges||challenges
-chache||cache
chanell||channel
changable||changeable
chanined||chained
@@ -347,6 +348,7 @@ colescing||coalescing
collapsable||collapsible
colorfull||colorful
comand||command
+comaptible||compatible
comit||commit
commerical||commercial
comming||coming
@@ -357,10 +359,6 @@ committ||commit
commmand||command
commnunication||communication
commoditiy||commodity
-comsume||consume
-comsumer||consumer
-comsuming||consuming
-comaptible||compatible
compability||compatibility
compaibility||compatibility
comparsion||comparison
@@ -376,22 +374,25 @@ compleatly||completely
completition||completion
completly||completely
complient||compliant
-componnents||components
compoment||component
+componnents||components
comppatible||compatible
compres||compress
compresion||compression
compresser||compressor
comression||compression
+comsume||consume
comsumed||consumed
+comsumer||consumer
+comsuming||consuming
comunicate||communicate
comunication||communication
conbination||combination
concurent||concurrent
conditionaly||conditionally
conditon||condition
-condtion||condition
condtional||conditional
+condtion||condition
conected||connected
conector||connector
configed||configured
@@ -428,13 +429,13 @@ continous||continuous
continously||continuously
continueing||continuing
contiuous||continuous
-contraints||constraints
-contruct||construct
contol||control
contoller||controller
+contraints||constraints
controled||controlled
controler||controller
controll||control
+contruct||construct
contruction||construction
contry||country
conuntry||country
@@ -465,10 +466,9 @@ debouce||debounce
decendant||descendant
decendants||descendants
decompres||decompress
-decsribed||described
decrese||decrease
decription||description
-detault||default
+decsribed||described
dectected||detected
defailt||default
deferal||deferral
@@ -482,9 +482,9 @@ defintion||definition
defintions||definitions
defualt||default
defult||default
-deintializing||deinitializing
-deintialize||deinitialize
deintialized||deinitialized
+deintialize||deinitialize
+deintializing||deinitializing
deivce||device
delared||declared
delare||declare
@@ -494,8 +494,8 @@ delemiter||delimiter
deley||delay
delibrately||deliberately
delievered||delivered
-demodualtor||demodulator
demension||dimension
+demodualtor||demodulator
dependancies||dependencies
dependancy||dependency
dependant||dependent
@@ -505,15 +505,15 @@ depreacte||deprecate
desactivate||deactivate
desciptor||descriptor
desciptors||descriptors
-descritpor||descriptor
descripto||descriptor
descripton||description
descrition||description
+descritpor||descriptor
descritptor||descriptor
desctiptor||descriptor
+desination||destination
desriptor||descriptor
desriptors||descriptors
-desination||destination
destionation||destination
destoried||destroyed
destory||destroy
@@ -521,6 +521,7 @@ destoryed||destroyed
destorys||destroys
destroied||destroyed
detabase||database
+detault||default
deteced||detected
detecion||detection
detectt||detect
@@ -535,55 +536,54 @@ deveolpment||development
devided||divided
deviece||device
devision||division
-diable||disable
diabled||disabled
+diable||disable
dicline||decline
+diconnected||disconnected
dictionnary||dictionary
didnt||didn't
diferent||different
-differrence||difference
-diffrent||different
differenciate||differentiate
+differrence||difference
diffreential||differential
+diffrent||different
diffrentiate||differentiate
difinition||definition
digial||digital
dimention||dimension
dimesions||dimensions
-diconnected||disconnected
-disabed||disabled
-disasembler||disassembler
-disble||disable
-disgest||digest
-disired||desired
-dispalying||displaying
-dissable||disable
-dissapeared||disappeared
diplay||display
-directon||direction
direcly||directly
+directon||direction
direectly||directly
diregard||disregard
-disassocation||disassociation
-disassocative||disassociative
+disabed||disabled
disapear||disappear
disapeared||disappeared
disappared||disappeared
-disbale||disable
+disasembler||disassembler
+disassocation||disassociation
+disassocative||disassociative
disbaled||disabled
-disble||disable
+disbale||disable
disbled||disabled
+disble||disable
+disble||disable
disconnet||disconnect
discontinous||discontinuous
+disgest||digest
disharge||discharge
+disired||desired
disnabled||disabled
+dispalying||displaying
dispertion||dispersion
+dissable||disable
+dissapeared||disappeared
dissapears||disappears
dissconect||disconnect
distiction||distinction
divisable||divisible
divsiors||divisors
-dsiabled||disabled
docuentation||documentation
documantation||documentation
documentaion||documentation
@@ -598,6 +598,7 @@ downlads||downloads
droped||dropped
droput||dropout
druing||during
+dsiabled||disabled
dyanmic||dynamic
dynmaic||dynamic
eanable||enable
@@ -621,20 +622,20 @@ enble||enable
enchanced||enhanced
encorporating||incorporating
encrupted||encrypted
-encrypiton||encryption
encryped||encrypted
+encrypiton||encryption
encryptio||encryption
endianess||endianness
-enpoint||endpoint
enhaced||enhanced
enlightnment||enlightenment
+enocded||encoded
+enought||enough
+enpoint||endpoint
enqueing||enqueuing
+enterily||entirely
entires||entries
entites||entities
entrys||entries
-enocded||encoded
-enought||enough
-enterily||entirely
enviroiment||environment
enviroment||environment
environement||environment
@@ -653,8 +654,9 @@ evalute||evaluate
evalutes||evaluates
evalution||evaluation
evaulated||evaluated
-excecutable||executable
+exaclty||exactly
excceed||exceed
+excecutable||executable
exceded||exceeded
exceds||exceeds
exceeed||exceed
@@ -668,41 +670,41 @@ exeuction||execution
existance||existence
existant||existent
exixt||exist
-exsits||exists
exlcude||exclude
exlcuding||excluding
exlcusive||exclusive
-exlusive||exclusive
exlicitly||explicitly
+exlusive||exclusive
exmaple||example
expecially||especially
experies||expires
explicite||explicit
-explicity||explicitly
explicitely||explicitly
-explict||explicit
+explicity||explicitly
explictely||explicitly
+explict||explicit
explictly||explicitly
expresion||expression
exprienced||experienced
exprimental||experimental
-extened||extended
+exsits||exists
exteneded||extended
+extened||extended
extensability||extensibility
-extention||extension
extenstion||extension
+extention||extension
extracter||extractor
faied||failed
faield||failed
-faild||failed
failded||failed
+faild||failed
failer||failure
-faill||fail
failied||failed
+faill||fail
faillure||failure
+failng||failing
failue||failure
failuer||failure
-failng||failing
faireness||fairness
falied||failed
faliure||failure
@@ -717,15 +719,15 @@ fetcing||fetching
fileystem||filesystem
fimrware||firmware
fimware||firmware
+finanize||finalize
+findn||find
+finilizes||finalizes
+finsih||finish
firmare||firmware
firmaware||firmware
firtly||firstly
firware||firmware
firwmare||firmware
-finanize||finalize
-findn||find
-finilizes||finalizes
-finsih||finish
fliter||filter
flusing||flushing
folloing||following
@@ -742,9 +744,9 @@ forwared||forwarded
frambuffer||framebuffer
framming||framing
framwork||framework
+frequancy||frequency
frequence||frequency
frequncy||frequency
-frequancy||frequency
frome||from
fronend||frontend
fucntion||function
@@ -766,9 +768,9 @@ gatable||gateable
gateing||gating
gauage||gauge
gaurenteed||guaranteed
-generiously||generously
genereate||generate
genereted||generated
+generiously||generously
genric||generic
gerenal||general
geting||getting
@@ -790,18 +792,17 @@ hanlde||handle
hanled||handled
happend||happened
hardare||hardware
-harware||hardware
hardward||hardware
+harware||hardware
havind||having
+hearbeat||heartbeat
heigth||height
+heirachy||hierarchy
heirarchically||hierarchically
heirarchy||hierarchy
-heirachy||hierarchy
helpfull||helpful
-hearbeat||heartbeat
heterogenous||heterogeneous
hexdecimal||hexadecimal
-hybernate||hibernate
hiearchy||hierarchy
hierachy||hierarchy
hierarchie||hierarchy
@@ -809,14 +810,14 @@ homogenous||homogeneous
horizental||horizontal
howver||however
hsould||should
+hybernate||hibernate
hypervior||hypervisor
hypter||hyper
idel||idle
identidier||identifier
iligal||illegal
-illigal||illegal
illgal||illegal
-iomaped||iomapped
+illigal||illegal
imblance||imbalance
immeadiately||immediately
immedaite||immediate
@@ -831,13 +832,14 @@ implemantation||implementation
implemenation||implementation
implementaiton||implementation
implementated||implemented
-implemention||implementation
implementd||implemented
+implemention||implementation
implemetation||implementation
implemntation||implementation
implentation||implementation
implmentation||implementation
implmenting||implementing
+inavlid||invalid
incative||inactive
incomming||incoming
incompaitiblity||incompatibility
@@ -869,9 +871,9 @@ infromation||information
ingore||ignore
inheritence||inheritance
inital||initial
-initalized||initialized
initalised||initialized
initalise||initialize
+initalized||initialized
initalize||initialize
initation||initiation
initators||initiators
@@ -879,20 +881,20 @@ initialiazation||initialization
initializationg||initialization
initializiation||initialization
initializtion||initialization
-initialze||initialize
initialzed||initialized
+initialze||initialize
initialzing||initializing
initilization||initialization
+initilized||initialized
initilize||initialize
initliaze||initialize
-initilized||initialized
inofficial||unofficial
inrerface||interface
insititute||institute
instace||instance
instal||install
-instanciate||instantiate
instanciated||instantiated
+instanciate||instantiate
instuments||instruments
insufficent||insufficient
intead||instead
@@ -911,16 +913,16 @@ intergrated||integrated
intermittant||intermittent
internel||internal
interoprability||interoperability
-interuupt||interrupt
-interupt||interrupt
-interupts||interrupts
-interurpt||interrupt
interrface||interface
interrrupt||interrupt
interrup||interrupt
interrups||interrupts
interruptted||interrupted
interupted||interrupted
+interupt||interrupt
+interupts||interrupts
+interurpt||interrupt
+interuupt||interrupt
intiailized||initialized
intial||initial
intialisation||initialisation
@@ -934,18 +936,18 @@ intrerrupt||interrupt
intrrupt||interrupt
intterrupt||interrupt
intuative||intuitive
-inavlid||invalid
invaid||invalid
invaild||invalid
invailid||invalid
-invald||invalid
invalde||invalid
+invald||invalid
invalide||invalid
invalidiate||invalidate
invalud||invalid
invididual||individual
invokation||invocation
invokations||invocations
+iomaped||iomapped
ireelevant||irrelevant
irrelevent||irrelevant
isnt||isn't
@@ -991,11 +993,11 @@ losted||lost
maangement||management
machinary||machinery
maibox||mailbox
+mailformed||malformed
maintainance||maintenance
maintainence||maintenance
maintan||maintain
makeing||making
-mailformed||malformed
malplaced||misplaced
malplace||misplace
managable||manageable
@@ -1005,21 +1007,22 @@ mangement||management
manger||manager
manoeuvering||maneuvering
manufaucturing||manufacturing
-mappping||mapping
maping||mapping
+mappping||mapping
matchs||matches
mathimatical||mathematical
mathimatic||mathematic
mathimatics||mathematics
-maxmium||maximum
maximium||maximum
maxium||maximum
+maxmium||maximum
mechamism||mechanism
mechanim||mechanism
meetign||meeting
memeory||memory
memmber||member
memoery||memory
+memomry||memory
memroy||memory
ment||meant
mergable||mergeable
@@ -1036,19 +1039,19 @@ migrateable||migratable
miliseconds||milliseconds
millenium||millennium
milliseonds||milliseconds
-minimim||minimum
-minium||minimum
minimam||minimum
+minimim||minimum
minimun||minimum
+minium||minimum
miniumum||minimum
minumum||minimum
misalinged||misaligned
miscelleneous||miscellaneous
misformed||malformed
-mispelled||misspelled
-mispelt||misspelt
mising||missing
mismactch||mismatch
+mispelled||misspelled
+mispelt||misspelt
missign||missing
missmanaged||mismanaged
missmatch||mismatch
@@ -1061,18 +1064,17 @@ modifer||modifier
modul||module
modulues||modules
momery||memory
-memomry||memory
monitring||monitoring
monochorome||monochrome
monochromo||monochrome
monocrome||monochrome
mopdule||module
mroe||more
-mulitplied||multiplied
muliple||multiple
-multipler||multiplier
+mulitplied||multiplied
multidimensionnal||multidimensional
multipe||multiple
+multipler||multiplier
multple||multiple
mumber||number
muticast||multicast
@@ -1094,6 +1096,7 @@ nerver||never
nescessary||necessary
nessessary||necessary
none existent||non-existent
+notfify||notify
noticable||noticeable
notication||notification
notications||notifications
@@ -1101,7 +1104,6 @@ notifcations||notifications
notifed||notified
notifer||notifier
notity||notify
-notfify||notify
nubmer||number
numebr||number
numer||number
@@ -1119,10 +1121,10 @@ occurence||occurrence
occure||occurred
occuring||occurring
ocurrence||occurrence
-offser||offset
offet||offset
offlaod||offload
offloded||offloaded
+offser||offset
offseting||offsetting
oflload||offload
omited||omitted
@@ -1141,25 +1143,25 @@ optionnal||optional
optmizations||optimizations
orientatied||orientated
orientied||oriented
-orignal||original
originial||original
+orignal||original
orphanded||orphaned
otherise||otherwise
ouput||output
oustanding||outstanding
+oveflow||overflow
overaall||overall
+overflw||overflow
overhread||overhead
+overide||override
overlaping||overlapping
-oveflow||overflow
-overflw||overflow
overlfow||overflow
-overide||override
overrided||overridden
overriden||overridden
overrrun||overrun
overun||overrun
-overwritting||overwriting
overwriten||overwritten
+overwritting||overwriting
pacakge||package
pachage||package
packacge||package
@@ -1169,11 +1171,11 @@ packtes||packets
pakage||package
paket||packet
pallette||palette
-paln||plan
palne||plane
+paln||plan
paramameters||parameters
-paramaters||parameters
paramater||parameter
+paramaters||parameters
paramenters||parameters
parametes||parameters
parametised||parametrised
@@ -1241,8 +1243,6 @@ prefered||preferred
prefferably||preferably
prefitler||prefilter
preform||perform
-previleged||privileged
-previlege||privilege
premption||preemption
prepaired||prepared
prepate||prepare
@@ -1250,6 +1250,8 @@ preperation||preparation
preprare||prepare
pressre||pressure
presuambly||presumably
+previleged||privileged
+previlege||privilege
previosuly||previously
previsously||previously
primative||primitive
@@ -1258,17 +1260,17 @@ priorty||priority
priting||printing
privilaged||privileged
privilage||privilege
-priviledge||privilege
priviledged||privileged
+priviledge||privilege
priviledges||privileges
privleges||privileges
-probaly||probably
probabalistic||probabilistic
+probaly||probably
procceed||proceed
proccesors||processors
procesed||processed
-proces||process
procesing||processing
+proces||process
processessing||processing
processess||processes
processpr||processor
@@ -1288,6 +1290,7 @@ progresss||progress
prohibitted||prohibited
prohibitting||prohibiting
promiscous||promiscuous
+promixity||proximity
promps||prompts
pronnounced||pronounced
prononciation||pronunciation
@@ -1296,15 +1299,14 @@ pronunce||pronounce
propery||property
propigate||propagate
propigation||propagation
-propogation||propagation
propogate||propagate
+propogation||propagation
prosess||process
protable||portable
protcol||protocol
protecion||protection
protedcted||protected
protocoll||protocol
-promixity||proximity
psudo||pseudo
psuedo||pseudo
psychadelic||psychedelic
@@ -1333,8 +1335,8 @@ recieves||receives
recieving||receiving
recogniced||recognised
recognizeable||recognizable
-recompte||recompute
recommanded||recommended
+recompte||recompute
recyle||recycle
redect||reject
redircet||redirect
@@ -1344,8 +1346,8 @@ reename||rename
refcounf||refcount
refence||reference
refered||referred
-referencce||reference
referenace||reference
+referencce||reference
refererence||reference
refering||referring
refernces||references
@@ -1353,8 +1355,8 @@ refernnce||reference
refrence||reference
regiser||register
registed||registered
-registerd||registered
registeration||registration
+registerd||registered
registeresd||registered
registerred||registered
registes||registers
@@ -1371,8 +1373,8 @@ reloade||reload
remoote||remote
remore||remote
removeable||removable
-repective||respective
repectively||respectively
+repective||respective
replacable||replaceable
replacments||replacements
replys||replies
@@ -1389,8 +1391,8 @@ requieres||requires
requirment||requirement
requred||required
requried||required
-requst||request
requsted||requested
+requst||request
reregisteration||reregistration
reseting||resetting
reseved||reserved
@@ -1412,11 +1414,11 @@ retransmited||retransmitted
retreived||retrieved
retreive||retrieve
retreiving||retrieving
-retrive||retrieve
retrived||retrieved
+retrive||retrieve
retrun||return
-retun||return
retuned||returned
+retun||return
reudce||reduce
reuest||request
reuqest||request
@@ -1464,9 +1466,9 @@ seperate||separate
seperatly||separately
seperator||separator
sepperate||separate
-seqeunce||sequence
-seqeuncer||sequencer
seqeuencer||sequencer
+seqeuncer||sequencer
+seqeunce||sequence
sequece||sequence
sequemce||sequence
sequencial||sequential
@@ -1505,8 +1507,8 @@ soley||solely
soluation||solution
souce||source
speach||speech
-specfic||specific
specfication||specification
+specfic||specific
specfield||specified
speciefied||specified
specifc||specific
@@ -1515,8 +1517,8 @@ specificatin||specification
specificaton||specification
specificed||specified
specifing||specifying
-specifiy||specify
specifiying||specifying
+specifiy||specify
speficied||specified
speicify||specify
speling||spelling
@@ -1543,23 +1545,23 @@ stoppped||stopped
straming||streaming
struc||struct
structres||structures
-stuct||struct
strucuture||structure
+stuct||struct
stucture||structure
sturcture||structure
subdirectoires||subdirectories
suble||subtle
-substract||subtract
submited||submitted
submition||submission
+substract||subtract
succeded||succeeded
-suceed||succeed
-succesfuly||successfully
succesfully||successfully
succesful||successful
+succesfuly||successfully
successed||succeeded
successfull||successful
successfuly||successfully
+suceed||succeed
sucessfully||successfully
sucessful||successful
sucess||success
@@ -1568,9 +1570,9 @@ superseeded||superseded
suplied||supplied
suported||supported
suport||support
-supportet||supported
suppored||supported
supporing||supporting
+supportet||supported
supportin||supporting
suppoted||supported
suppported||supported
@@ -1581,27 +1583,27 @@ surpressed||suppressed
surpresses||suppresses
susbsystem||subsystem
suspeneded||suspended
-suspsend||suspend
suspicously||suspiciously
+suspsend||suspend
swaping||swapping
switchs||switches
-swith||switch
swithable||switchable
-swithc||switch
swithced||switched
swithcing||switching
+swithc||switch
swithed||switched
swithing||switching
+swith||switch
swtich||switch
+sychronization||synchronization
+sychronously||synchronously
syfs||sysfs
symetric||symmetric
synax||syntax
synchonized||synchronized
-sychronization||synchronization
-sychronously||synchronously
synchronuously||synchronously
-syncronize||synchronize
syncronized||synchronized
+syncronize||synchronize
syncronizing||synchronizing
syncronus||synchronous
syste||system
@@ -1610,16 +1612,17 @@ sythesis||synthesis
tagert||target
taht||that
tained||tainted
-tarffic||traffic
+tansition||transition
tansmit||transmit
+tarffic||traffic
targetted||targeted
targetting||targeting
taskelt||tasklet
teh||the
temeprature||temperature
temorary||temporary
-temproarily||temporarily
temperture||temperature
+temproarily||temporarily
theads||threads
therfore||therefore
thier||their
@@ -1629,23 +1632,20 @@ threshhold||threshold
thresold||threshold
throtting||throttling
throught||through
-tansition||transition
-trackling||tracking
-troughput||throughput
-trys||tries
thses||these
-tiggers||triggers
tiggered||triggered
tiggerring||triggering
-tipically||typically
+tiggers||triggers
timeing||timing
timming||timing
timout||timeout
+tipically||typically
tmis||this
tolarance||tolerance
toogle||toggle
torerable||tolerable
torlence||tolerance
+trackling||tracking
traget||target
traking||tracking
tramsmitted||transmitted
@@ -1669,20 +1669,20 @@ trasfer||transfer
trasmission||transmission
trasmitter||transmitter
treshold||threshold
-trigged||triggered
-triggerd||triggered
trigerred||triggered
trigerring||triggering
+trigged||triggered
+triggerd||triggered
+troughput||throughput
trun||turn
+trys||tries
tunning||tuning
ture||true
tyep||type
udpate||update
-updtes||updates
uesd||used
-unknwon||unknown
uknown||unknown
-usccess||success
+unamed||unnamed
uncommited||uncommitted
uncompatible||incompatible
uncomressed||uncompressed
@@ -1691,6 +1691,7 @@ undeflow||underflow
undelying||underlying
underun||underrun
unecessary||unnecessary
+uneeded||unneeded
unexecpted||unexpected
unexepected||unexpected
unexpcted||unexpected
@@ -1699,26 +1700,24 @@ unexpeted||unexpected
unexpexted||unexpected
unfortunatelly||unfortunately
unifiy||unify
-uniterrupted||uninterrupted
uninterruptable||uninterruptible
unintialized||uninitialized
+uniterrupted||uninterrupted
unitialized||uninitialized
unkmown||unknown
unknonw||unknown
unknouwn||unknown
unknow||unknown
+unknwon||unknown
unkown||unknown
-unamed||unnamed
-uneeded||unneeded
-unneded||unneeded
+unmached||unmatched
unneccecary||unnecessary
unneccesary||unnecessary
unneccessary||unnecessary
unnecesary||unnecessary
+unneded||unneeded
unneedingly||unnecessarily
unnsupported||unsupported
-unuspported||unsupported
-unmached||unmatched
unprecise||imprecise
unpriviledged||unprivileged
unpriviliged||unprivileged
@@ -1726,18 +1725,21 @@ unregester||unregister
unresgister||unregister
unrgesiter||unregister
unsinged||unsigned
-unstabel||unstable
-unsolicted||unsolicited
unsolicitied||unsolicited
+unsolicted||unsolicited
+unstabel||unstable
unsuccessfull||unsuccessful
unsuported||unsupported
untill||until
ununsed||unused
unuseful||useless
+unuspported||unsupported
unvalid||invalid
upate||update
+updtes||updates
upsupported||unsupported
upto||up to
+usccess||success
useable||usable
usefule||useful
usefull||useful
@@ -1759,14 +1761,14 @@ variantions||variations
varible||variable
varient||variant
vaule||value
-verbse||verbose
veify||verify
+verbse||verbose
verfication||verification
veriosn||version
-versoin||version
verisons||versions
verison||version
veritical||vertical
+versoin||version
verson||version
vicefersa||vice-versa
virtal||virtual
@@ -1779,13 +1781,13 @@ wakeus||wakeups
was't||wasn't
wathdog||watchdog
wating||waiting
-wiat||wait
wether||whether
whataver||whatever
whcih||which
whenver||whenever
wheter||whether
whe||when
+wiat||wait
wierd||weird
wihout||without
wiil||will
diff --git a/scripts/sphinx-build-wrapper b/scripts/sphinx-build-wrapper
deleted file mode 100755
index abe8c26ae137..000000000000
--- a/scripts/sphinx-build-wrapper
+++ /dev/null
@@ -1,719 +0,0 @@
-#!/usr/bin/env python3
-# SPDX-License-Identifier: GPL-2.0
-# Copyright (C) 2025 Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
-#
-# pylint: disable=R0902, R0912, R0913, R0914, R0915, R0917, C0103
-#
-# Converted from docs Makefile and parallel-wrapper.sh, both under
-# GPLv2, copyrighted since 2008 by the following authors:
-#
-# Akira Yokosawa <akiyks@gmail.com>
-# Arnd Bergmann <arnd@arndb.de>
-# Breno Leitao <leitao@debian.org>
-# Carlos Bilbao <carlos.bilbao@amd.com>
-# Dave Young <dyoung@redhat.com>
-# Donald Hunter <donald.hunter@gmail.com>
-# Geert Uytterhoeven <geert+renesas@glider.be>
-# Jani Nikula <jani.nikula@intel.com>
-# Jan Stancek <jstancek@redhat.com>
-# Jonathan Corbet <corbet@lwn.net>
-# Joshua Clayton <stillcompiling@gmail.com>
-# Kees Cook <keescook@chromium.org>
-# Linus Torvalds <torvalds@linux-foundation.org>
-# Magnus Damm <damm+renesas@opensource.se>
-# Masahiro Yamada <masahiroy@kernel.org>
-# Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
-# Maxim Cournoyer <maxim.cournoyer@gmail.com>
-# Peter Foley <pefoley2@pefoley.com>
-# Randy Dunlap <rdunlap@infradead.org>
-# Rob Herring <robh@kernel.org>
-# Shuah Khan <shuahkh@osg.samsung.com>
-# Thorsten Blum <thorsten.blum@toblux.com>
-# Tomas Winkler <tomas.winkler@intel.com>
-
-
-"""
-Sphinx build wrapper that handles Kernel-specific business rules:
-
-- it gets the Kernel build environment vars;
-- it determines what's the best parallelism;
-- it handles SPHINXDIRS
-
-This tool ensures that MIN_PYTHON_VERSION is satisfied. If version is
-below that, it seeks for a new Python version. If found, it re-runs using
-the newer version.
-"""
-
-import argparse
-import locale
-import os
-import re
-import shlex
-import shutil
-import subprocess
-import sys
-
-from concurrent import futures
-from glob import glob
-
-LIB_DIR = "lib"
-SRC_DIR = os.path.dirname(os.path.realpath(__file__))
-
-sys.path.insert(0, os.path.join(SRC_DIR, LIB_DIR))
-
-from jobserver import JobserverExec # pylint: disable=C0413
-
-
-def parse_version(version):
- """Convert a major.minor.patch version into a tuple"""
- return tuple(int(x) for x in version.split("."))
-
-def ver_str(version):
- """Returns a version tuple as major.minor.patch"""
-
- return ".".join([str(x) for x in version])
-
-# Minimal supported Python version needed by Sphinx and its extensions
-MIN_PYTHON_VERSION = parse_version("3.7")
-
-# Default value for --venv parameter
-VENV_DEFAULT = "sphinx_latest"
-
-# List of make targets and its corresponding builder and output directory
-TARGETS = {
- "cleandocs": {
- "builder": "clean",
- },
- "htmldocs": {
- "builder": "html",
- },
- "epubdocs": {
- "builder": "epub",
- "out_dir": "epub",
- },
- "texinfodocs": {
- "builder": "texinfo",
- "out_dir": "texinfo",
- },
- "infodocs": {
- "builder": "texinfo",
- "out_dir": "texinfo",
- },
- "latexdocs": {
- "builder": "latex",
- "out_dir": "latex",
- },
- "pdfdocs": {
- "builder": "latex",
- "out_dir": "latex",
- },
- "xmldocs": {
- "builder": "xml",
- "out_dir": "xml",
- },
- "linkcheckdocs": {
- "builder": "linkcheck"
- },
-}
-
-# Paper sizes. An empty value will pick the default
-PAPER = ["", "a4", "letter"]
-
-class SphinxBuilder:
- """
- Handles a sphinx-build target, adding needed arguments to build
- with the Kernel.
- """
-
- def is_rust_enabled(self):
- """Check if rust is enabled at .config"""
- config_path = os.path.join(self.srctree, ".config")
- if os.path.isfile(config_path):
- with open(config_path, "r", encoding="utf-8") as f:
- return "CONFIG_RUST=y" in f.read()
- return False
-
- def get_path(self, path, abs_path=False):
- """
- Ancillary routine to handle patches the right way, as shell does.
-
- It first expands "~" and "~user". Then, if patch is not absolute,
- join self.srctree. Finally, if requested, convert to abspath.
- """
-
- path = os.path.expanduser(path)
- if not path.startswith("/"):
- path = os.path.join(self.srctree, path)
-
- if abs_path:
- return os.path.abspath(path)
-
- return path
-
- def __init__(self, venv=None, verbose=False, n_jobs=None, interactive=None):
- """Initialize internal variables"""
- self.venv = venv
- self.verbose = None
-
- # Normal variables passed from Kernel's makefile
- self.kernelversion = os.environ.get("KERNELVERSION", "unknown")
- self.kernelrelease = os.environ.get("KERNELRELEASE", "unknown")
- self.pdflatex = os.environ.get("PDFLATEX", "xelatex")
-
- if not interactive:
- self.latexopts = os.environ.get("LATEXOPTS", "-interaction=batchmode -no-shell-escape")
- else:
- self.latexopts = os.environ.get("LATEXOPTS", "")
-
- if not verbose:
- verbose = bool(os.environ.get("KBUILD_VERBOSE", "") != "")
-
- # Handle SPHINXOPTS evironment
- sphinxopts = shlex.split(os.environ.get("SPHINXOPTS", ""))
-
- # As we handle number of jobs and quiet in separate, we need to pick
- # it the same way as sphinx-build would pick, so let's use argparse
- # do to the right argument expansion
- parser = argparse.ArgumentParser()
- parser.add_argument('-j', '--jobs', type=int)
- parser.add_argument('-q', '--quiet', type=int)
-
- # Other sphinx-build arguments go as-is, so place them
- # at self.sphinxopts
- sphinx_args, self.sphinxopts = parser.parse_known_args(sphinxopts)
- if sphinx_args.quiet == True:
- self.verbose = False
-
- if sphinx_args.jobs:
- self.n_jobs = sphinx_args.jobs
-
- # Command line arguments was passed, override SPHINXOPTS
- if verbose is not None:
- self.verbose = verbose
-
- self.n_jobs = n_jobs
-
- # Source tree directory. This needs to be at os.environ, as
- # Sphinx extensions and media uAPI makefile needs it
- self.srctree = os.environ.get("srctree")
- if not self.srctree:
- self.srctree = "."
- os.environ["srctree"] = self.srctree
-
- # Now that we can expand srctree, get other directories as well
- self.sphinxbuild = os.environ.get("SPHINXBUILD", "sphinx-build")
- self.kerneldoc = self.get_path(os.environ.get("KERNELDOC",
- "scripts/kernel-doc.py"))
- self.obj = os.environ.get("obj", "Documentation")
- self.builddir = self.get_path(os.path.join(self.obj, "output"),
- abs_path=True)
-
- # Media uAPI needs it
- os.environ["BUILDDIR"] = self.builddir
-
- # Detect if rust is enabled
- self.config_rust = self.is_rust_enabled()
-
- # Get directory locations for LaTeX build toolchain
- self.pdflatex_cmd = shutil.which(self.pdflatex)
- self.latexmk_cmd = shutil.which("latexmk")
-
- self.env = os.environ.copy()
-
- # If venv parameter is specified, run Sphinx from venv
- if venv:
- bin_dir = os.path.join(venv, "bin")
- if os.path.isfile(os.path.join(bin_dir, "activate")):
- # "activate" virtual env
- self.env["PATH"] = bin_dir + ":" + self.env["PATH"]
- self.env["VIRTUAL_ENV"] = venv
- if "PYTHONHOME" in self.env:
- del self.env["PYTHONHOME"]
- print(f"Setting venv to {venv}")
- else:
- sys.exit(f"Venv {venv} not found.")
-
- def run_sphinx(self, sphinx_build, build_args, *args, **pwargs):
- """
- Executes sphinx-build using current python3 command and setting
- -j parameter if possible to run the build in parallel.
- """
-
- with JobserverExec() as jobserver:
- if jobserver.claim:
- n_jobs = str(jobserver.claim)
- else:
- n_jobs = "auto" # Supported since Sphinx 1.7
-
- cmd = []
-
- if self.venv:
- cmd.append("python")
- else:
- cmd.append(sys.executable)
-
- cmd.append(sphinx_build)
-
- # if present, SPHINXOPTS or command line --jobs overrides default
- if self.n_jobs:
- n_jobs = str(self.n_jobs)
-
- if n_jobs:
- cmd += [f"-j{n_jobs}"]
-
- if not self.verbose:
- cmd.append("-q")
-
- cmd += self.sphinxopts
-
- cmd += build_args
-
- if self.verbose:
- print(" ".join(cmd))
-
- rc = subprocess.call(cmd, *args, **pwargs)
-
- def handle_html(self, css, output_dir):
- """
- Extra steps for HTML and epub output.
-
- For such targets, we need to ensure that CSS will be properly
- copied to the output _static directory
- """
-
- if not css:
- return
-
- css = os.path.expanduser(css)
- if not css.startswith("/"):
- css = os.path.join(self.srctree, css)
-
- static_dir = os.path.join(output_dir, "_static")
- os.makedirs(static_dir, exist_ok=True)
-
- try:
- shutil.copy2(css, static_dir)
- except (OSError, IOError) as e:
- print(f"Warning: Failed to copy CSS: {e}", file=sys.stderr)
-
- def build_pdf_file(self, latex_cmd, from_dir, path):
- """Builds a single pdf file using latex_cmd"""
- try:
- subprocess.run(latex_cmd + [path],
- cwd=from_dir, check=True)
-
- return True
- except subprocess.CalledProcessError:
- # LaTeX PDF error code is almost useless: it returns
- # error codes even when build succeeds but has warnings.
- # So, we'll ignore the results
- return False
-
- def pdf_parallel_build(self, tex_suffix, latex_cmd, tex_files, n_jobs):
- """Build PDF files in parallel if possible"""
- builds = {}
- build_failed = False
- max_len = 0
- has_tex = False
-
- # Process files in parallel
- with futures.ThreadPoolExecutor(max_workers=n_jobs) as executor:
- jobs = {}
-
- for from_dir, pdf_dir, entry in tex_files:
- name = entry.name
-
- if not name.endswith(tex_suffix):
- continue
-
- name = name[:-len(tex_suffix)]
-
- max_len = max(max_len, len(name))
-
- has_tex = True
-
- future = executor.submit(self.build_pdf_file, latex_cmd,
- from_dir, entry.path)
- jobs[future] = (from_dir, name, entry.path)
-
- for future in futures.as_completed(jobs):
- from_dir, name, path = jobs[future]
-
- pdf_name = name + ".pdf"
- pdf_from = os.path.join(from_dir, pdf_name)
-
- try:
- success = future.result()
-
- if success and os.path.exists(pdf_from):
- pdf_to = os.path.join(pdf_dir, pdf_name)
-
- os.rename(pdf_from, pdf_to)
- builds[name] = os.path.relpath(pdf_to, self.builddir)
- else:
- builds[name] = "FAILED"
- build_failed = True
- except Exception as e:
- builds[name] = f"FAILED ({str(e)})"
- build_failed = True
-
- # Handle case where no .tex files were found
- if not has_tex:
- name = "Sphinx LaTeX builder"
- max_len = max(max_len, len(name))
- builds[name] = "FAILED (no .tex file was generated)"
- build_failed = True
-
- return builds, build_failed, max_len
-
- def handle_pdf(self, output_dirs):
- """
- Extra steps for PDF output.
-
- As PDF is handled via a LaTeX output, after building the .tex file,
- a new build is needed to create the PDF output from the latex
- directory.
- """
- builds = {}
- max_len = 0
- tex_suffix = ".tex"
-
- # Get all tex files that will be used for PDF build
- tex_files = []
- for from_dir in output_dirs:
- pdf_dir = os.path.join(from_dir, "../pdf")
- os.makedirs(pdf_dir, exist_ok=True)
-
- if self.latexmk_cmd:
- latex_cmd = [self.latexmk_cmd, f"-{self.pdflatex}"]
- else:
- latex_cmd = [self.pdflatex]
-
- latex_cmd.extend(shlex.split(self.latexopts))
-
- # Get a list of tex files to process
- with os.scandir(from_dir) as it:
- for entry in it:
- if entry.name.endswith(tex_suffix):
- tex_files.append((from_dir, pdf_dir, entry))
-
- # When using make, this won't be used, as the number of jobs comes
- # from POSIX jobserver. So, this covers the case where build comes
- # from command line. On such case, serialize by default, except if
- # the user explicitly sets the number of jobs.
- n_jobs = 1
-
- # n_jobs is either an integer or "auto". Only use it if it is a number
- if self.n_jobs:
- try:
- n_jobs = int(self.n_jobs)
- except ValueError:
- pass
-
- # When using make, jobserver.claim is the number of jobs that were
- # used with "-j" and that aren't used by other make targets
- with JobserverExec() as jobserver:
- n_jobs = 1
-
- # Handle the case when a parameter is passed via command line,
- # using it as default, if jobserver doesn't claim anything
- if self.n_jobs:
- try:
- n_jobs = int(self.n_jobs)
- except ValueError:
- pass
-
- if jobserver.claim:
- n_jobs = jobserver.claim
-
- # Build files in parallel
- builds, build_failed, max_len = self.pdf_parallel_build(tex_suffix,
- latex_cmd,
- tex_files,
- n_jobs)
-
- msg = "Summary"
- msg += "\n" + "=" * len(msg)
- print()
- print(msg)
-
- for pdf_name, pdf_file in builds.items():
- print(f"{pdf_name:<{max_len}}: {pdf_file}")
-
- print()
-
- # return an error if a PDF file is missing
-
- if build_failed:
- sys.exit(f"PDF build failed: not all PDF files were created.")
- else:
- print("All PDF files were built.")
-
- def handle_info(self, output_dirs):
- """
- Extra steps for Info output.
-
- For texinfo generation, an additional make is needed from the
- texinfo directory.
- """
-
- for output_dir in output_dirs:
- try:
- subprocess.run(["make", "info"], cwd=output_dir, check=True)
- except subprocess.CalledProcessError as e:
- sys.exit(f"Error generating info docs: {e}")
-
- def cleandocs(self, builder):
-
- shutil.rmtree(self.builddir, ignore_errors=True)
-
- def build(self, target, sphinxdirs=None, conf="conf.py",
- theme=None, css=None, paper=None):
- """
- Build documentation using Sphinx. This is the core function of this
- module. It prepares all arguments required by sphinx-build.
- """
-
- builder = TARGETS[target]["builder"]
- out_dir = TARGETS[target].get("out_dir", "")
-
- # Cleandocs doesn't require sphinx-build
- if target == "cleandocs":
- self.cleandocs(builder)
- return
-
- # Other targets require sphinx-build
- sphinxbuild = shutil.which(self.sphinxbuild, path=self.env["PATH"])
- if not sphinxbuild:
- sys.exit(f"Error: {self.sphinxbuild} not found in PATH.\n")
-
- if builder == "latex":
- if not self.pdflatex_cmd and not self.latexmk_cmd:
- sys.exit("Error: pdflatex or latexmk required for PDF generation")
-
- docs_dir = os.path.abspath(os.path.join(self.srctree, "Documentation"))
-
- # Prepare base arguments for Sphinx build
- kerneldoc = self.kerneldoc
- if kerneldoc.startswith(self.srctree):
- kerneldoc = os.path.relpath(kerneldoc, self.srctree)
-
- # Prepare common Sphinx options
- args = [
- "-b", builder,
- "-c", docs_dir,
- ]
-
- if builder == "latex":
- if not paper:
- paper = PAPER[1]
-
- args.extend(["-D", f"latex_elements.papersize={paper}paper"])
-
- if self.config_rust:
- args.extend(["-t", "rustdoc"])
-
- if conf:
- self.env["SPHINX_CONF"] = self.get_path(conf, abs_path=True)
-
- if not sphinxdirs:
- sphinxdirs = os.environ.get("SPHINXDIRS", ".")
-
- # The sphinx-build tool has a bug: internally, it tries to set
- # locale with locale.setlocale(locale.LC_ALL, ''). This causes a
- # crash if language is not set. Detect and fix it.
- try:
- locale.setlocale(locale.LC_ALL, '')
- except Exception:
- self.env["LC_ALL"] = "C"
- self.env["LANG"] = "C"
-
- # sphinxdirs can be a list or a whitespace-separated string
- sphinxdirs_list = []
- for sphinxdir in sphinxdirs:
- if isinstance(sphinxdir, list):
- sphinxdirs_list += sphinxdir
- else:
- for name in sphinxdir.split(" "):
- sphinxdirs_list.append(name)
-
- # Build each directory
- output_dirs = []
- for sphinxdir in sphinxdirs_list:
- src_dir = os.path.join(docs_dir, sphinxdir)
- doctree_dir = os.path.join(self.builddir, ".doctrees")
- output_dir = os.path.join(self.builddir, sphinxdir, out_dir)
-
- # Make directory names canonical
- src_dir = os.path.normpath(src_dir)
- doctree_dir = os.path.normpath(doctree_dir)
- output_dir = os.path.normpath(output_dir)
-
- os.makedirs(doctree_dir, exist_ok=True)
- os.makedirs(output_dir, exist_ok=True)
-
- output_dirs.append(output_dir)
-
- build_args = args + [
- "-d", doctree_dir,
- "-D", f"kerneldoc_bin={kerneldoc}",
- "-D", f"version={self.kernelversion}",
- "-D", f"release={self.kernelrelease}",
- "-D", f"kerneldoc_srctree={self.srctree}",
- src_dir,
- output_dir,
- ]
-
- # Execute sphinx-build
- try:
- self.run_sphinx(sphinxbuild, build_args, env=self.env)
- except Exception as e:
- sys.exit(f"Build failed: {e}")
-
- # Ensure that html/epub will have needed static files
- if target in ["htmldocs", "epubdocs"]:
- self.handle_html(css, output_dir)
-
- # PDF and Info require a second build step
- if target == "pdfdocs":
- self.handle_pdf(output_dirs)
- elif target == "infodocs":
- self.handle_info(output_dirs)
-
- @staticmethod
- def get_python_version(cmd):
- """
- Get python version from a Python binary. As we need to detect if
- are out there newer python binaries, we can't rely on sys.release here.
- """
-
- result = subprocess.run([cmd, "--version"], check=True,
- stdout=subprocess.PIPE, stderr=subprocess.PIPE,
- universal_newlines=True)
- version = result.stdout.strip()
-
- match = re.search(r"(\d+\.\d+\.\d+)", version)
- if match:
- return parse_version(match.group(1))
-
- print(f"Can't parse version {version}")
- return (0, 0, 0)
-
- @staticmethod
- def find_python():
- """
- Detect if are out there any python 3.xy version newer than the
- current one.
-
- Note: this routine is limited to up to 2 digits for python3. We
- may need to update it one day, hopefully on a distant future.
- """
- patterns = [
- "python3.[0-9]",
- "python3.[0-9][0-9]",
- ]
-
- # Seek for a python binary newer than MIN_PYTHON_VERSION
- for path in os.getenv("PATH", "").split(":"):
- for pattern in patterns:
- for cmd in glob(os.path.join(path, pattern)):
- if os.path.isfile(cmd) and os.access(cmd, os.X_OK):
- version = SphinxBuilder.get_python_version(cmd)
- if version >= MIN_PYTHON_VERSION:
- return cmd
-
- return None
-
- @staticmethod
- def check_python():
- """
- Check if the current python binary satisfies our minimal requirement
- for Sphinx build. If not, re-run with a newer version if found.
- """
- cur_ver = sys.version_info[:3]
- if cur_ver >= MIN_PYTHON_VERSION:
- return
-
- python_ver = ver_str(cur_ver)
-
- new_python_cmd = SphinxBuilder.find_python()
- if not new_python_cmd:
- sys.exit(f"Python version {python_ver} is not supported anymore.")
-
- # Restart script using the newer version
- script_path = os.path.abspath(sys.argv[0])
- args = [new_python_cmd, script_path] + sys.argv[1:]
-
- print(f"Python {python_ver} not supported. Changing to {new_python_cmd}")
-
- try:
- os.execv(new_python_cmd, args)
- except OSError as e:
- sys.exit(f"Failed to restart with {new_python_cmd}: {e}")
-
-def jobs_type(value):
- """
- Handle valid values for -j. Accepts Sphinx "-jauto", plus a number
- equal or bigger than one.
- """
- if value is None:
- return None
-
- if value.lower() == 'auto':
- return value.lower()
-
- try:
- if int(value) >= 1:
- return value
-
- raise argparse.ArgumentTypeError(f"Minimum jobs is 1, got {value}")
- except ValueError:
- raise argparse.ArgumentTypeError(f"Must be 'auto' or positive integer, got {value}")
-
-def main():
- """
- Main function. The only mandatory argument is the target. If not
- specified, the other arguments will use default values if not
- specified at os.environ.
- """
- parser = argparse.ArgumentParser(description="Kernel documentation builder")
-
- parser.add_argument("target", choices=list(TARGETS.keys()),
- help="Documentation target to build")
- parser.add_argument("--sphinxdirs", nargs="+",
- help="Specific directories to build")
- parser.add_argument("--conf", default="conf.py",
- help="Sphinx configuration file")
-
- parser.add_argument("--theme", help="Sphinx theme to use")
-
- parser.add_argument("--css", help="Custom CSS file for HTML/EPUB")
-
- parser.add_argument("--paper", choices=PAPER, default=PAPER[0],
- help="Paper size for LaTeX/PDF output")
-
- parser.add_argument("-v", "--verbose", action='store_true',
- help="place build in verbose mode")
-
- parser.add_argument('-j', '--jobs', type=jobs_type,
- help="Sets number of jobs to use with sphinx-build")
-
- parser.add_argument('-i', '--interactive', action='store_true',
- help="Change latex default to run in interactive mode")
-
- parser.add_argument("-V", "--venv", nargs='?', const=f'{VENV_DEFAULT}',
- default=None,
- help=f'If used, run Sphinx from a venv dir (default dir: {VENV_DEFAULT})')
-
- args = parser.parse_args()
-
- SphinxBuilder.check_python()
-
- builder = SphinxBuilder(venv=args.venv, verbose=args.verbose,
- n_jobs=args.jobs, interactive=args.interactive)
-
- builder.build(args.target, sphinxdirs=args.sphinxdirs, conf=args.conf,
- theme=args.theme, css=args.css, paper=args.paper)
-
-if __name__ == "__main__":
- main()
diff --git a/scripts/sphinx-pre-install b/scripts/sphinx-pre-install
deleted file mode 100755
index 954ed3dc0645..000000000000
--- a/scripts/sphinx-pre-install
+++ /dev/null
@@ -1,1621 +0,0 @@
-#!/usr/bin/env python3
-# SPDX-License-Identifier: GPL-2.0-or-later
-# Copyright (c) 2017-2025 Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
-#
-# pylint: disable=C0103,C0114,C0115,C0116,C0301,C0302
-# pylint: disable=R0902,R0904,R0911,R0912,R0914,R0915,R1705,R1710,E1121
-
-# Note: this script requires at least Python 3.6 to run.
-# Don't add changes not compatible with it, it is meant to report
-# incompatible python versions.
-
-"""
-Dependency checker for Sphinx documentation Kernel build.
-
-This module provides tools to check for all required dependencies needed to
-build documentation using Sphinx, including system packages, Python modules
-and LaTeX packages for PDF generation.
-
-It detect packages for a subset of Linux distributions used by Kernel
-maintainers, showing hints and missing dependencies.
-
-The main class SphinxDependencyChecker handles the dependency checking logic
-and provides recommendations for installing missing packages. It supports both
-system package installations and Python virtual environments. By default,
-system pacage install is recommended.
-"""
-
-import argparse
-import os
-import re
-import subprocess
-import sys
-from glob import glob
-
-
-def parse_version(version):
- """Convert a major.minor.patch version into a tuple"""
- return tuple(int(x) for x in version.split("."))
-
-
-def ver_str(version):
- """Returns a version tuple as major.minor.patch"""
-
- return ".".join([str(x) for x in version])
-
-
-RECOMMENDED_VERSION = parse_version("3.4.3")
-MIN_PYTHON_VERSION = parse_version("3.7")
-
-
-class DepManager:
- """
- Manage package dependencies. There are three types of dependencies:
-
- - System: dependencies required for docs build;
- - Python: python dependencies for a native distro Sphinx install;
- - PDF: dependencies needed by PDF builds.
-
- Each dependency can be mandatory or optional. Not installing an optional
- dependency won't break the build, but will cause degradation at the
- docs output.
- """
-
- # Internal types of dependencies. Don't use them outside DepManager class.
- _SYS_TYPE = 0
- _PHY_TYPE = 1
- _PDF_TYPE = 2
-
- # Dependencies visible outside the class.
- # The keys are tuple with: (type, is_mandatory flag).
- #
- # Currently we're not using all optional dep types. Yet, we'll keep all
- # possible combinations here. They're not many, and that makes easier
- # if later needed and for the name() method below
-
- SYSTEM_MANDATORY = (_SYS_TYPE, True)
- PYTHON_MANDATORY = (_PHY_TYPE, True)
- PDF_MANDATORY = (_PDF_TYPE, True)
-
- SYSTEM_OPTIONAL = (_SYS_TYPE, False)
- PYTHON_OPTIONAL = (_PHY_TYPE, False)
- PDF_OPTIONAL = (_PDF_TYPE, True)
-
- def __init__(self, pdf):
- """
- Initialize internal vars:
-
- - missing: missing dependencies list, containing a distro-independent
- name for a missing dependency and its type.
- - missing_pkg: ancillary dict containing missing dependencies in
- distro namespace, organized by type.
- - need: total number of needed dependencies. Never cleaned.
- - optional: total number of optional dependencies. Never cleaned.
- - pdf: Is PDF support enabled?
- """
- self.missing = {}
- self.missing_pkg = {}
- self.need = 0
- self.optional = 0
- self.pdf = pdf
-
- @staticmethod
- def name(dtype):
- """
- Ancillary routine to output a warn/error message reporting
- missing dependencies.
- """
- if dtype[0] == DepManager._SYS_TYPE:
- msg = "build"
- elif dtype[0] == DepManager._PHY_TYPE:
- msg = "Python"
- else:
- msg = "PDF"
-
- if dtype[1]:
- return f"ERROR: {msg} mandatory deps missing"
- else:
- return f"Warning: {msg} optional deps missing"
-
- @staticmethod
- def is_optional(dtype):
- """Ancillary routine to report if a dependency is optional"""
- return not dtype[1]
-
- @staticmethod
- def is_pdf(dtype):
- """Ancillary routine to report if a dependency is for PDF generation"""
- if dtype[0] == DepManager._PDF_TYPE:
- return True
-
- return False
-
- def add_package(self, package, dtype):
- """
- Add a package at the self.missing() dictionary.
- Doesn't update missing_pkg.
- """
- is_optional = DepManager.is_optional(dtype)
- self.missing[package] = dtype
- if is_optional:
- self.optional += 1
- else:
- self.need += 1
-
- def del_package(self, package):
- """
- Remove a package at the self.missing() dictionary.
- Doesn't update missing_pkg.
- """
- if package in self.missing:
- del self.missing[package]
-
- def clear_deps(self):
- """
- Clear dependencies without changing needed/optional.
-
- This is an ackward way to have a separate section to recommend
- a package after system main dependencies.
-
- TODO: rework the logic to prevent needing it.
- """
-
- self.missing = {}
- self.missing_pkg = {}
-
- def check_missing(self, progs):
- """
- Update self.missing_pkg, using progs dict to convert from the
- agnostic package name to distro-specific one.
-
- Returns an string with the packages to be installed, sorted and
- with eventual duplicates removed.
- """
-
- self.missing_pkg = {}
-
- for prog, dtype in sorted(self.missing.items()):
- # At least on some LTS distros like CentOS 7, texlive doesn't
- # provide all packages we need. When such distros are
- # detected, we have to disable PDF output.
- #
- # So, we need to ignore the packages that distros would
- # need for LaTeX to work
- if DepManager.is_pdf(dtype) and not self.pdf:
- self.optional -= 1
- continue
-
- if not dtype in self.missing_pkg:
- self.missing_pkg[dtype] = []
-
- self.missing_pkg[dtype].append(progs.get(prog, prog))
-
- install = []
- for dtype, pkgs in self.missing_pkg.items():
- install += pkgs
-
- return " ".join(sorted(set(install)))
-
- def warn_install(self):
- """
- Emit warnings/errors related to missing packages.
- """
-
- output_msg = ""
-
- for dtype in sorted(self.missing_pkg.keys()):
- progs = " ".join(sorted(set(self.missing_pkg[dtype])))
-
- try:
- name = DepManager.name(dtype)
- output_msg += f'{name}:\t{progs}\n'
- except KeyError:
- raise KeyError(f"ERROR!!!: invalid dtype for {progs}: {dtype}")
-
- if output_msg:
- print(f"\n{output_msg}")
-
-class AncillaryMethods:
- """
- Ancillary methods that checks for missing dependencies for different
- types of types, like binaries, python modules, rpm deps, etc.
- """
-
- @staticmethod
- def which(prog):
- """
- Our own implementation of which(). We could instead use
- shutil.which(), but this function is simple enough.
- Probably faster to use this implementation than to import shutil.
- """
- for path in os.environ.get("PATH", "").split(":"):
- full_path = os.path.join(path, prog)
- if os.access(full_path, os.X_OK):
- return full_path
-
- return None
-
- @staticmethod
- def get_python_version(cmd):
- """
- Get python version from a Python binary. As we need to detect if
- are out there newer python binaries, we can't rely on sys.release here.
- """
-
- result = SphinxDependencyChecker.run([cmd, "--version"],
- capture_output=True, text=True)
- version = result.stdout.strip()
-
- match = re.search(r"(\d+\.\d+\.\d+)", version)
- if match:
- return parse_version(match.group(1))
-
- print(f"Can't parse version {version}")
- return (0, 0, 0)
-
- @staticmethod
- def find_python():
- """
- Detect if are out there any python 3.xy version newer than the
- current one.
-
- Note: this routine is limited to up to 2 digits for python3. We
- may need to update it one day, hopefully on a distant future.
- """
- patterns = [
- "python3.[0-9]",
- "python3.[0-9][0-9]",
- ]
-
- # Seek for a python binary newer than MIN_PYTHON_VERSION
- for path in os.getenv("PATH", "").split(":"):
- for pattern in patterns:
- for cmd in glob(os.path.join(path, pattern)):
- if os.path.isfile(cmd) and os.access(cmd, os.X_OK):
- version = SphinxDependencyChecker.get_python_version(cmd)
- if version >= MIN_PYTHON_VERSION:
- return cmd
-
- @staticmethod
- def check_python():
- """
- Check if the current python binary satisfies our minimal requirement
- for Sphinx build. If not, re-run with a newer version if found.
- """
- cur_ver = sys.version_info[:3]
- if cur_ver >= MIN_PYTHON_VERSION:
- ver = ver_str(cur_ver)
- print(f"Python version: {ver}")
-
- # This could be useful for debugging purposes
- if SphinxDependencyChecker.which("docutils"):
- result = SphinxDependencyChecker.run(["docutils", "--version"],
- capture_output=True, text=True)
- ver = result.stdout.strip()
- match = re.search(r"(\d+\.\d+\.\d+)", ver)
- if match:
- ver = match.group(1)
-
- print(f"Docutils version: {ver}")
-
- return
-
- python_ver = ver_str(cur_ver)
-
- new_python_cmd = SphinxDependencyChecker.find_python()
- if not new_python_cmd:
- print(f"ERROR: Python version {python_ver} is not spported anymore\n")
- print(" Can't find a new version. This script may fail")
- return
-
- # Restart script using the newer version
- script_path = os.path.abspath(sys.argv[0])
- args = [new_python_cmd, script_path] + sys.argv[1:]
-
- print(f"Python {python_ver} not supported. Changing to {new_python_cmd}")
-
- try:
- os.execv(new_python_cmd, args)
- except OSError as e:
- sys.exit(f"Failed to restart with {new_python_cmd}: {e}")
-
- @staticmethod
- def run(*args, **kwargs):
- """
- Excecute a command, hiding its output by default.
- Preserve comatibility with older Python versions.
- """
-
- capture_output = kwargs.pop('capture_output', False)
-
- if capture_output:
- if 'stdout' not in kwargs:
- kwargs['stdout'] = subprocess.PIPE
- if 'stderr' not in kwargs:
- kwargs['stderr'] = subprocess.PIPE
- else:
- if 'stdout' not in kwargs:
- kwargs['stdout'] = subprocess.DEVNULL
- if 'stderr' not in kwargs:
- kwargs['stderr'] = subprocess.DEVNULL
-
- # Don't break with older Python versions
- if 'text' in kwargs and sys.version_info < (3, 7):
- kwargs['universal_newlines'] = kwargs.pop('text')
-
- return subprocess.run(*args, **kwargs)
-
-class MissingCheckers(AncillaryMethods):
- """
- Contains some ancillary checkers for different types of binaries and
- package managers.
- """
-
- def __init__(self, args, texlive):
- """
- Initialize its internal variables
- """
- self.pdf = args.pdf
- self.virtualenv = args.virtualenv
- self.version_check = args.version_check
- self.texlive = texlive
-
- self.min_version = (0, 0, 0)
- self.cur_version = (0, 0, 0)
-
- self.deps = DepManager(self.pdf)
-
- self.need_symlink = 0
- self.need_sphinx = 0
-
- self.verbose_warn_install = 1
-
- self.virtenv_dir = ""
- self.install = ""
- self.python_cmd = ""
-
- self.virtenv_prefix = ["sphinx_", "Sphinx_" ]
-
- def check_missing_file(self, files, package, dtype):
- """
- Does the file exists? If not, add it to missing dependencies.
- """
- for f in files:
- if os.path.exists(f):
- return
- self.deps.add_package(package, dtype)
-
- def check_program(self, prog, dtype):
- """
- Does the program exists and it is at the PATH?
- If not, add it to missing dependencies.
- """
- found = self.which(prog)
- if found:
- return found
-
- self.deps.add_package(prog, dtype)
-
- return None
-
- def check_perl_module(self, prog, dtype):
- """
- Does perl have a dependency? Is it available?
- If not, add it to missing dependencies.
-
- Right now, we still need Perl for doc build, as it is required
- by some tools called at docs or kernel build time, like:
-
- scripts/documentation-file-ref-check
-
- Also, checkpatch is on Perl.
- """
-
- # While testing with lxc download template, one of the
- # distros (Oracle) didn't have perl - nor even an option to install
- # before installing oraclelinux-release-el9 package.
- #
- # Check it before running an error. If perl is not there,
- # add it as a mandatory package, as some parts of the doc builder
- # needs it.
- if not self.which("perl"):
- self.deps.add_package("perl", DepManager.SYSTEM_MANDATORY)
- self.deps.add_package(prog, dtype)
- return
-
- try:
- self.run(["perl", f"-M{prog}", "-e", "1"], check=True)
- except subprocess.CalledProcessError:
- self.deps.add_package(prog, dtype)
-
- def check_python_module(self, module, is_optional=False):
- """
- Does a python module exists outside venv? If not, add it to missing
- dependencies.
- """
- if is_optional:
- dtype = DepManager.PYTHON_OPTIONAL
- else:
- dtype = DepManager.PYTHON_MANDATORY
-
- try:
- self.run([self.python_cmd, "-c", f"import {module}"], check=True)
- except subprocess.CalledProcessError:
- self.deps.add_package(module, dtype)
-
- def check_rpm_missing(self, pkgs, dtype):
- """
- Does a rpm package exists? If not, add it to missing dependencies.
- """
- for prog in pkgs:
- try:
- self.run(["rpm", "-q", prog], check=True)
- except subprocess.CalledProcessError:
- self.deps.add_package(prog, dtype)
-
- def check_pacman_missing(self, pkgs, dtype):
- """
- Does a pacman package exists? If not, add it to missing dependencies.
- """
- for prog in pkgs:
- try:
- self.run(["pacman", "-Q", prog], check=True)
- except subprocess.CalledProcessError:
- self.deps.add_package(prog, dtype)
-
- def check_missing_tex(self, is_optional=False):
- """
- Does a LaTeX package exists? If not, add it to missing dependencies.
- """
- if is_optional:
- dtype = DepManager.PDF_OPTIONAL
- else:
- dtype = DepManager.PDF_MANDATORY
-
- kpsewhich = self.which("kpsewhich")
- for prog, package in self.texlive.items():
-
- # If kpsewhich is not there, just add it to deps
- if not kpsewhich:
- self.deps.add_package(package, dtype)
- continue
-
- # Check if the package is needed
- try:
- result = self.run(
- [kpsewhich, prog], stdout=subprocess.PIPE, text=True, check=True
- )
-
- # Didn't find. Add it
- if not result.stdout.strip():
- self.deps.add_package(package, dtype)
-
- except subprocess.CalledProcessError:
- # kpsewhich returned an error. Add it, just in case
- self.deps.add_package(package, dtype)
-
- def get_sphinx_fname(self):
- """
- Gets the binary filename for sphinx-build.
- """
- if "SPHINXBUILD" in os.environ:
- return os.environ["SPHINXBUILD"]
-
- fname = "sphinx-build"
- if self.which(fname):
- return fname
-
- fname = "sphinx-build-3"
- if self.which(fname):
- self.need_symlink = 1
- return fname
-
- return ""
-
- def get_sphinx_version(self, cmd):
- """
- Gets sphinx-build version.
- """
- try:
- result = self.run([cmd, "--version"],
- stdout=subprocess.PIPE,
- stderr=subprocess.STDOUT,
- text=True, check=True)
- except (subprocess.CalledProcessError, FileNotFoundError):
- return None
-
- for line in result.stdout.split("\n"):
- match = re.match(r"^sphinx-build\s+([\d\.]+)(?:\+(?:/[\da-f]+)|b\d+)?\s*$", line)
- if match:
- return parse_version(match.group(1))
-
- match = re.match(r"^Sphinx.*\s+([\d\.]+)\s*$", line)
- if match:
- return parse_version(match.group(1))
-
- def check_sphinx(self, conf):
- """
- Checks Sphinx minimal requirements
- """
- try:
- with open(conf, "r", encoding="utf-8") as f:
- for line in f:
- match = re.match(r"^\s*needs_sphinx\s*=\s*[\'\"]([\d\.]+)[\'\"]", line)
- if match:
- self.min_version = parse_version(match.group(1))
- break
- except IOError:
- sys.exit(f"Can't open {conf}")
-
- if not self.min_version:
- sys.exit(f"Can't get needs_sphinx version from {conf}")
-
- self.virtenv_dir = self.virtenv_prefix[0] + "latest"
-
- sphinx = self.get_sphinx_fname()
- if not sphinx:
- self.need_sphinx = 1
- return
-
- self.cur_version = self.get_sphinx_version(sphinx)
- if not self.cur_version:
- sys.exit(f"{sphinx} didn't return its version")
-
- if self.cur_version < self.min_version:
- curver = ver_str(self.cur_version)
- minver = ver_str(self.min_version)
-
- print(f"ERROR: Sphinx version is {curver}. It should be >= {minver}")
- self.need_sphinx = 1
- return
-
- # On version check mode, just assume Sphinx has all mandatory deps
- if self.version_check and self.cur_version >= RECOMMENDED_VERSION:
- sys.exit(0)
-
- def catcheck(self, filename):
- """
- Reads a file if it exists, returning as string.
- If not found, returns an empty string.
- """
- if os.path.exists(filename):
- with open(filename, "r", encoding="utf-8") as f:
- return f.read().strip()
- return ""
-
- def get_system_release(self):
- """
- Determine the system type. There's no unique way that would work
- with all distros with a minimal package install. So, several
- methods are used here.
-
- By default, it will use lsb_release function. If not available, it will
- fail back to reading the known different places where the distro name
- is stored.
-
- Several modern distros now have /etc/os-release, which usually have
- a decent coverage.
- """
-
- system_release = ""
-
- if self.which("lsb_release"):
- result = self.run(["lsb_release", "-d"], capture_output=True, text=True)
- system_release = result.stdout.replace("Description:", "").strip()
-
- release_files = [
- "/etc/system-release",
- "/etc/redhat-release",
- "/etc/lsb-release",
- "/etc/gentoo-release",
- ]
-
- if not system_release:
- for f in release_files:
- system_release = self.catcheck(f)
- if system_release:
- break
-
- # This seems more common than LSB these days
- if not system_release:
- os_var = {}
- try:
- with open("/etc/os-release", "r", encoding="utf-8") as f:
- for line in f:
- match = re.match(r"^([\w\d\_]+)=\"?([^\"]*)\"?\n", line)
- if match:
- os_var[match.group(1)] = match.group(2)
-
- system_release = os_var.get("NAME", "")
- if "VERSION_ID" in os_var:
- system_release += " " + os_var["VERSION_ID"]
- elif "VERSION" in os_var:
- system_release += " " + os_var["VERSION"]
- except IOError:
- pass
-
- if not system_release:
- system_release = self.catcheck("/etc/issue")
-
- system_release = system_release.strip()
-
- return system_release
-
-class SphinxDependencyChecker(MissingCheckers):
- """
- Main class for checking Sphinx documentation build dependencies.
-
- - Check for missing system packages;
- - Check for missing Python modules;
- - Check for missing LaTeX packages needed by PDF generation;
- - Propose Sphinx install via Python Virtual environment;
- - Propose Sphinx install via distro-specific package install.
- """
- def __init__(self, args):
- """Initialize checker variables"""
-
- # List of required texlive packages on Fedora and OpenSuse
- texlive = {
- "amsfonts.sty": "texlive-amsfonts",
- "amsmath.sty": "texlive-amsmath",
- "amssymb.sty": "texlive-amsfonts",
- "amsthm.sty": "texlive-amscls",
- "anyfontsize.sty": "texlive-anyfontsize",
- "atbegshi.sty": "texlive-oberdiek",
- "bm.sty": "texlive-tools",
- "capt-of.sty": "texlive-capt-of",
- "cmap.sty": "texlive-cmap",
- "ctexhook.sty": "texlive-ctex",
- "ecrm1000.tfm": "texlive-ec",
- "eqparbox.sty": "texlive-eqparbox",
- "eu1enc.def": "texlive-euenc",
- "fancybox.sty": "texlive-fancybox",
- "fancyvrb.sty": "texlive-fancyvrb",
- "float.sty": "texlive-float",
- "fncychap.sty": "texlive-fncychap",
- "footnote.sty": "texlive-mdwtools",
- "framed.sty": "texlive-framed",
- "luatex85.sty": "texlive-luatex85",
- "multirow.sty": "texlive-multirow",
- "needspace.sty": "texlive-needspace",
- "palatino.sty": "texlive-psnfss",
- "parskip.sty": "texlive-parskip",
- "polyglossia.sty": "texlive-polyglossia",
- "tabulary.sty": "texlive-tabulary",
- "threeparttable.sty": "texlive-threeparttable",
- "titlesec.sty": "texlive-titlesec",
- "ucs.sty": "texlive-ucs",
- "upquote.sty": "texlive-upquote",
- "wrapfig.sty": "texlive-wrapfig",
- }
-
- super().__init__(args, texlive)
-
- self.need_pip = False
- self.rec_sphinx_upgrade = 0
-
- self.system_release = self.get_system_release()
- self.activate_cmd = ""
-
- # Some distros may not have a Sphinx shipped package compatible with
- # our minimal requirements
- self.package_supported = True
-
- # Recommend a new python version
- self.recommend_python = None
-
- # Certain hints are meant to be shown only once
- self.distro_msg = None
-
- self.latest_avail_ver = (0, 0, 0)
- self.venv_ver = (0, 0, 0)
-
- prefix = os.environ.get("srctree", ".") + "/"
-
- self.conf = prefix + "Documentation/conf.py"
- self.requirement_file = prefix + "Documentation/sphinx/requirements.txt"
-
- def get_install_progs(self, progs, cmd, extra=None):
- """
- Check for missing dependencies using the provided program mapping.
-
- The actual distro-specific programs are mapped via progs argument.
- """
- install = self.deps.check_missing(progs)
-
- if self.verbose_warn_install:
- self.deps.warn_install()
-
- if not install:
- return
-
- if cmd:
- if self.verbose_warn_install:
- msg = "You should run:"
- else:
- msg = ""
-
- if extra:
- msg += "\n\t" + extra.replace("\n", "\n\t")
-
- return(msg + "\n\tsudo " + cmd + " " + install)
-
- return None
-
- #
- # Distro-specific hints methods
- #
-
- def give_debian_hints(self):
- """
- Provide package installation hints for Debian-based distros.
- """
- progs = {
- "Pod::Usage": "perl-modules",
- "convert": "imagemagick",
- "dot": "graphviz",
- "ensurepip": "python3-venv",
- "python-sphinx": "python3-sphinx",
- "rsvg-convert": "librsvg2-bin",
- "virtualenv": "virtualenv",
- "xelatex": "texlive-xetex",
- "yaml": "python3-yaml",
- }
-
- if self.pdf:
- pdf_pkgs = {
- "fonts-dejavu": [
- "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf",
- ],
- "fonts-noto-cjk": [
- "/usr/share/fonts/noto-cjk/NotoSansCJK-Regular.ttc",
- "/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc",
- "/usr/share/fonts/opentype/noto/NotoSerifCJK-Regular.ttc",
- ],
- "tex-gyre": [
- "/usr/share/texmf/tex/latex/tex-gyre/tgtermes.sty"
- ],
- "texlive-fonts-recommended": [
- "/usr/share/texlive/texmf-dist/fonts/tfm/adobe/zapfding/pzdr.tfm",
- ],
- "texlive-lang-chinese": [
- "/usr/share/texlive/texmf-dist/tex/latex/ctex/ctexhook.sty",
- ],
- }
-
- for package, files in pdf_pkgs.items():
- self.check_missing_file(files, package, DepManager.PDF_MANDATORY)
-
- self.check_program("dvipng", DepManager.PDF_MANDATORY)
-
- if not self.distro_msg:
- self.distro_msg = \
- "Note: ImageMagick is broken on some distros, affecting PDF output. For more details:\n" \
- "\thttps://askubuntu.com/questions/1158894/imagemagick-still-broken-using-with-usr-bin-convert"
-
- return self.get_install_progs(progs, "apt-get install")
-
- def give_redhat_hints(self):
- """
- Provide package installation hints for RedHat-based distros
- (Fedora, RHEL and RHEL-based variants).
- """
- progs = {
- "Pod::Usage": "perl-Pod-Usage",
- "convert": "ImageMagick",
- "dot": "graphviz",
- "python-sphinx": "python3-sphinx",
- "rsvg-convert": "librsvg2-tools",
- "virtualenv": "python3-virtualenv",
- "xelatex": "texlive-xetex-bin",
- "yaml": "python3-pyyaml",
- }
-
- fedora_tex_pkgs = [
- "dejavu-sans-fonts",
- "dejavu-sans-mono-fonts",
- "dejavu-serif-fonts",
- "texlive-collection-fontsrecommended",
- "texlive-collection-latex",
- "texlive-xecjk",
- ]
-
- fedora = False
- rel = None
-
- match = re.search(r"(release|Linux)\s+(\d+)", self.system_release)
- if match:
- rel = int(match.group(2))
-
- if not rel:
- print("Couldn't identify release number")
- noto_sans_redhat = None
- self.pdf = False
- elif re.search("Fedora", self.system_release):
- # Fedora 38 and upper use this CJK font
-
- noto_sans_redhat = "google-noto-sans-cjk-fonts"
- fedora = True
- else:
- # Almalinux, CentOS, RHEL, ...
-
- # at least up to version 9 (and Fedora < 38), that's the CJK font
- noto_sans_redhat = "google-noto-sans-cjk-ttc-fonts"
-
- progs["virtualenv"] = "python-virtualenv"
-
- if not rel or rel < 8:
- print("ERROR: Distro not supported. Too old?")
- return
-
- # RHEL 8 uses Python 3.6, which is not compatible with
- # the build system anymore. Suggest Python 3.11
- if rel == 8:
- self.check_program("python3.9", DepManager.SYSTEM_MANDATORY)
- progs["python3.9"] = "python39"
- progs["yaml"] = "python39-pyyaml"
-
- self.recommend_python = True
-
- # There's no python39-sphinx package. Only pip is supported
- self.package_supported = False
-
- if not self.distro_msg:
- self.distro_msg = \
- "Note: RHEL-based distros typically require extra repositories.\n" \
- "For most, enabling epel and crb are enough:\n" \
- "\tsudo dnf install -y epel-release\n" \
- "\tsudo dnf config-manager --set-enabled crb\n" \
- "Yet, some may have other required repositories. Those commands could be useful:\n" \
- "\tsudo dnf repolist all\n" \
- "\tsudo dnf repoquery --available --info <pkgs>\n" \
- "\tsudo dnf config-manager --set-enabled '*' # enable all - probably not what you want"
-
- if self.pdf:
- pdf_pkgs = [
- "/usr/share/fonts/google-noto-cjk/NotoSansCJK-Regular.ttc",
- "/usr/share/fonts/google-noto-sans-cjk-fonts/NotoSansCJK-Regular.ttc",
- ]
-
- self.check_missing_file(pdf_pkgs, noto_sans_redhat, DepManager.PDF_MANDATORY)
-
- self.check_rpm_missing(fedora_tex_pkgs, DepManager.PDF_MANDATORY)
-
- self.check_missing_tex(DepManager.PDF_MANDATORY)
-
- # There's no texlive-ctex on RHEL 8 repositories. This will
- # likely affect CJK pdf build only.
- if not fedora and rel == 8:
- self.deps.del_package("texlive-ctex")
-
- return self.get_install_progs(progs, "dnf install")
-
- def give_opensuse_hints(self):
- """
- Provide package installation hints for openSUSE-based distros
- (Leap and Tumbleweed).
- """
- progs = {
- "Pod::Usage": "perl-Pod-Usage",
- "convert": "ImageMagick",
- "dot": "graphviz",
- "python-sphinx": "python3-sphinx",
- "virtualenv": "python3-virtualenv",
- "xelatex": "texlive-xetex-bin texlive-dejavu",
- "yaml": "python3-pyyaml",
- }
-
- suse_tex_pkgs = [
- "texlive-babel-english",
- "texlive-caption",
- "texlive-colortbl",
- "texlive-courier",
- "texlive-dvips",
- "texlive-helvetic",
- "texlive-makeindex",
- "texlive-metafont",
- "texlive-metapost",
- "texlive-palatino",
- "texlive-preview",
- "texlive-times",
- "texlive-zapfchan",
- "texlive-zapfding",
- ]
-
- progs["latexmk"] = "texlive-latexmk-bin"
-
- match = re.search(r"(Leap)\s+(\d+).(\d)", self.system_release)
- if match:
- rel = int(match.group(2))
-
- # Leap 15.x uses Python 3.6, which is not compatible with
- # the build system anymore. Suggest Python 3.11
- if rel == 15:
- if not self.which(self.python_cmd):
- self.check_program("python3.11", DepManager.SYSTEM_MANDATORY)
- progs["python3.11"] = "python311"
- self.recommend_python = True
-
- progs.update({
- "python-sphinx": "python311-Sphinx python311-Sphinx-latex",
- "virtualenv": "python311-virtualenv",
- "yaml": "python311-PyYAML",
- })
- else:
- # Tumbleweed defaults to Python 3.11
-
- progs.update({
- "python-sphinx": "python313-Sphinx python313-Sphinx-latex",
- "virtualenv": "python313-virtualenv",
- "yaml": "python313-PyYAML",
- })
-
- # FIXME: add support for installing CJK fonts
- #
- # I tried hard, but was unable to find a way to install
- # "Noto Sans CJK SC" on openSUSE
-
- if self.pdf:
- self.check_rpm_missing(suse_tex_pkgs, DepManager.PDF_MANDATORY)
- if self.pdf:
- self.check_missing_tex()
-
- return self.get_install_progs(progs, "zypper install --no-recommends")
-
- def give_mageia_hints(self):
- """
- Provide package installation hints for Mageia and OpenMandriva.
- """
- progs = {
- "Pod::Usage": "perl-Pod-Usage",
- "convert": "ImageMagick",
- "dot": "graphviz",
- "python-sphinx": "python3-sphinx",
- "rsvg-convert": "librsvg2",
- "virtualenv": "python3-virtualenv",
- "xelatex": "texlive",
- "yaml": "python3-yaml",
- }
-
- tex_pkgs = [
- "texlive-fontsextra",
- "texlive-fonts-asian",
- "fonts-ttf-dejavu",
- ]
-
- if re.search(r"OpenMandriva", self.system_release):
- packager_cmd = "dnf install"
- noto_sans = "noto-sans-cjk-fonts"
- tex_pkgs = [
- "texlive-collection-basic",
- "texlive-collection-langcjk",
- "texlive-collection-fontsextra",
- "texlive-collection-fontsrecommended"
- ]
-
- # Tested on OpenMandriva Lx 4.3
- progs["convert"] = "imagemagick"
- progs["yaml"] = "python-pyyaml"
- progs["python-virtualenv"] = "python-virtualenv"
- progs["python-sphinx"] = "python-sphinx"
- progs["xelatex"] = "texlive"
-
- self.check_program("python-virtualenv", DepManager.PYTHON_MANDATORY)
-
- # On my tests with openMandriva LX 4.0 docker image, upgraded
- # to 4.3, python-virtualenv package is broken: it is missing
- # ensurepip. Without it, the alternative would be to run:
- # python3 -m venv --without-pip ~/sphinx_latest, but running
- # pip there won't install sphinx at venv.
- #
- # Add a note about that.
-
- if not self.distro_msg:
- self.distro_msg = \
- "Notes:\n"\
- "1. for venv, ensurepip could be broken, preventing its install method.\n" \
- "2. at least on OpenMandriva LX 4.3, texlive packages seem broken"
-
- else:
- packager_cmd = "urpmi"
- noto_sans = "google-noto-sans-cjk-ttc-fonts"
-
- progs["latexmk"] = "texlive-collection-basic"
-
- if self.pdf:
- pdf_pkgs = [
- "/usr/share/fonts/google-noto-cjk/NotoSansCJK-Regular.ttc",
- "/usr/share/fonts/TTF/NotoSans-Regular.ttf",
- ]
-
- self.check_missing_file(pdf_pkgs, noto_sans, DepManager.PDF_MANDATORY)
- self.check_rpm_missing(tex_pkgs, DepManager.PDF_MANDATORY)
-
- return self.get_install_progs(progs, packager_cmd)
-
- def give_arch_linux_hints(self):
- """
- Provide package installation hints for ArchLinux.
- """
- progs = {
- "convert": "imagemagick",
- "dot": "graphviz",
- "latexmk": "texlive-core",
- "rsvg-convert": "extra/librsvg",
- "virtualenv": "python-virtualenv",
- "xelatex": "texlive-xetex",
- "yaml": "python-yaml",
- }
-
- archlinux_tex_pkgs = [
- "texlive-basic",
- "texlive-binextra",
- "texlive-core",
- "texlive-fontsrecommended",
- "texlive-langchinese",
- "texlive-langcjk",
- "texlive-latexextra",
- "ttf-dejavu",
- ]
-
- if self.pdf:
- self.check_pacman_missing(archlinux_tex_pkgs,
- DepManager.PDF_MANDATORY)
-
- self.check_missing_file(["/usr/share/fonts/noto-cjk/NotoSansCJK-Regular.ttc"],
- "noto-fonts-cjk",
- DepManager.PDF_MANDATORY)
-
-
- return self.get_install_progs(progs, "pacman -S")
-
- def give_gentoo_hints(self):
- """
- Provide package installation hints for Gentoo.
- """
- texlive_deps = [
- "dev-texlive/texlive-fontsrecommended",
- "dev-texlive/texlive-latexextra",
- "dev-texlive/texlive-xetex",
- "media-fonts/dejavu",
- ]
-
- progs = {
- "convert": "media-gfx/imagemagick",
- "dot": "media-gfx/graphviz",
- "rsvg-convert": "gnome-base/librsvg",
- "virtualenv": "dev-python/virtualenv",
- "xelatex": " ".join(texlive_deps),
- "yaml": "dev-python/pyyaml",
- "python-sphinx": "dev-python/sphinx",
- }
-
- if self.pdf:
- pdf_pkgs = {
- "media-fonts/dejavu": [
- "/usr/share/fonts/dejavu/DejaVuSans.ttf",
- ],
- "media-fonts/noto-cjk": [
- "/usr/share/fonts/noto-cjk/NotoSansCJKsc-Regular.otf",
- "/usr/share/fonts/noto-cjk/NotoSerifCJK-Regular.ttc",
- ],
- }
- for package, files in pdf_pkgs.items():
- self.check_missing_file(files, package, DepManager.PDF_MANDATORY)
-
- # Handling dependencies is a nightmare, as Gentoo refuses to emerge
- # some packages if there's no package.use file describing them.
- # To make it worse, compilation flags shall also be present there
- # for some packages. If USE is not perfect, error/warning messages
- # like those are shown:
- #
- # !!! The following binary packages have been ignored due to non matching USE:
- #
- # =media-gfx/graphviz-12.2.1-r1 X pdf -python_single_target_python3_13 qt6 svg
- # =media-gfx/graphviz-12.2.1-r1 X pdf python_single_target_python3_12 -python_single_target_python3_13 qt6 svg
- # =media-gfx/graphviz-12.2.1-r1 X pdf qt6 svg
- # =media-gfx/graphviz-12.2.1-r1 X pdf -python_single_target_python3_10 qt6 svg
- # =media-gfx/graphviz-12.2.1-r1 X pdf -python_single_target_python3_10 python_single_target_python3_12 -python_single_target_python3_13 qt6 svg
- # =media-fonts/noto-cjk-20190416 X
- # =app-text/texlive-core-2024-r1 X cjk -xetex
- # =app-text/texlive-core-2024-r1 X -xetex
- # =app-text/texlive-core-2024-r1 -xetex
- # =dev-libs/zziplib-0.13.79-r1 sdl
- #
- # And will ignore such packages, installing the remaining ones. That
- # affects mostly the image extension and PDF generation.
-
- # Package dependencies and the minimal needed args:
- portages = {
- "graphviz": "media-gfx/graphviz",
- "imagemagick": "media-gfx/imagemagick",
- "media-libs": "media-libs/harfbuzz icu",
- "media-fonts": "media-fonts/noto-cjk",
- "texlive": "app-text/texlive-core xetex",
- "zziblib": "dev-libs/zziplib sdl",
- }
-
- extra_cmds = ""
- if not self.distro_msg:
- self.distro_msg = "Note: Gentoo requires package.use to be adjusted before emerging packages"
-
- use_base = "/etc/portage/package.use"
- files = glob(f"{use_base}/*")
-
- for fname, portage in portages.items():
- install = False
-
- while install is False:
- if not files:
- # No files under package.usage. Install all
- install = True
- break
-
- args = portage.split(" ")
-
- name = args.pop(0)
-
- cmd = ["grep", "-l", "-E", rf"^{name}\b" ] + files
- result = self.run(cmd, stdout=subprocess.PIPE, text=True)
- if result.returncode or not result.stdout.strip():
- # File containing portage name not found
- install = True
- break
-
- # Ensure that needed USE flags are present
- if args:
- match_fname = result.stdout.strip()
- with open(match_fname, 'r', encoding='utf8',
- errors='backslashreplace') as fp:
- for line in fp:
- for arg in args:
- if arg.startswith("-"):
- continue
-
- if not re.search(rf"\s*{arg}\b", line):
- # Needed file argument not found
- install = True
- break
-
- # Everything looks ok, don't install
- break
-
- # emit a code to setup missing USE
- if install:
- extra_cmds += (f"sudo su -c 'echo \"{portage}\" > {use_base}/{fname}'\n")
-
- # Now, we can use emerge and let it respect USE
- return self.get_install_progs(progs,
- "emerge --ask --changed-use --binpkg-respect-use=y",
- extra_cmds)
-
- def get_install(self):
- """
- OS-specific hints logic. Seeks for a hinter. If found, use it to
- provide package-manager specific install commands.
-
- Otherwise, outputs install instructions for the meta-packages.
-
- Returns a string with the command to be executed to install the
- the needed packages, if distro found. Otherwise, return just a
- list of packages that require installation.
- """
- os_hints = {
- re.compile("Red Hat Enterprise Linux"): self.give_redhat_hints,
- re.compile("Fedora"): self.give_redhat_hints,
- re.compile("AlmaLinux"): self.give_redhat_hints,
- re.compile("Amazon Linux"): self.give_redhat_hints,
- re.compile("CentOS"): self.give_redhat_hints,
- re.compile("openEuler"): self.give_redhat_hints,
- re.compile("Oracle Linux Server"): self.give_redhat_hints,
- re.compile("Rocky Linux"): self.give_redhat_hints,
- re.compile("Springdale Open Enterprise"): self.give_redhat_hints,
-
- re.compile("Ubuntu"): self.give_debian_hints,
- re.compile("Debian"): self.give_debian_hints,
- re.compile("Devuan"): self.give_debian_hints,
- re.compile("Kali"): self.give_debian_hints,
- re.compile("Mint"): self.give_debian_hints,
-
- re.compile("openSUSE"): self.give_opensuse_hints,
-
- re.compile("Mageia"): self.give_mageia_hints,
- re.compile("OpenMandriva"): self.give_mageia_hints,
-
- re.compile("Arch Linux"): self.give_arch_linux_hints,
- re.compile("Gentoo"): self.give_gentoo_hints,
- }
-
- # If the OS is detected, use per-OS hint logic
- for regex, os_hint in os_hints.items():
- if regex.search(self.system_release):
- return os_hint()
-
- #
- # Fall-back to generic hint code for other distros
- # That's far from ideal, specially for LaTeX dependencies.
- #
- progs = {"sphinx-build": "sphinx"}
- if self.pdf:
- self.check_missing_tex()
-
- self.distro_msg = \
- f"I don't know distro {self.system_release}.\n" \
- "So, I can't provide you a hint with the install procedure.\n" \
- "There are likely missing dependencies."
-
- return self.get_install_progs(progs, None)
-
- #
- # Common dependencies
- #
- def deactivate_help(self):
- """
- Print a helper message to disable a virtual environment.
- """
-
- print("\n If you want to exit the virtualenv, you can use:")
- print("\tdeactivate")
-
- def get_virtenv(self):
- """
- Give a hint about how to activate an already-existing virtual
- environment containing sphinx-build.
-
- Returns a tuble with (activate_cmd_path, sphinx_version) with
- the newest available virtual env.
- """
-
- cwd = os.getcwd()
-
- activates = []
-
- # Add all sphinx prefixes with possible version numbers
- for p in self.virtenv_prefix:
- activates += glob(f"{cwd}/{p}[0-9]*/bin/activate")
-
- activates.sort(reverse=True, key=str.lower)
-
- # Place sphinx_latest first, if it exists
- for p in self.virtenv_prefix:
- activates = glob(f"{cwd}/{p}*latest/bin/activate") + activates
-
- ver = (0, 0, 0)
- for f in activates:
- # Discard too old Sphinx virtual environments
- match = re.search(r"(\d+)\.(\d+)\.(\d+)", f)
- if match:
- ver = (int(match.group(1)), int(match.group(2)), int(match.group(3)))
-
- if ver < self.min_version:
- continue
-
- sphinx_cmd = f.replace("activate", "sphinx-build")
- if not os.path.isfile(sphinx_cmd):
- continue
-
- ver = self.get_sphinx_version(sphinx_cmd)
-
- if not ver:
- venv_dir = f.replace("/bin/activate", "")
- print(f"Warning: virtual environment {venv_dir} is not working.\n" \
- "Python version upgrade? Remove it with:\n\n" \
- "\trm -rf {venv_dir}\n\n")
- else:
- if self.need_sphinx and ver >= self.min_version:
- return (f, ver)
- elif parse_version(ver) > self.cur_version:
- return (f, ver)
-
- return ("", ver)
-
- def recommend_sphinx_upgrade(self):
- """
- Check if Sphinx needs to be upgraded.
-
- Returns a tuple with the higest available Sphinx version if found.
- Otherwise, returns None to indicate either that no upgrade is needed
- or no venv was found.
- """
-
- # Avoid running sphinx-builds from venv if cur_version is good
- if self.cur_version and self.cur_version >= RECOMMENDED_VERSION:
- self.latest_avail_ver = self.cur_version
- return None
-
- # Get the highest version from sphinx_*/bin/sphinx-build and the
- # corresponding command to activate the venv/virtenv
- self.activate_cmd, self.venv_ver = self.get_virtenv()
-
- # Store the highest version from Sphinx existing virtualenvs
- if self.activate_cmd and self.venv_ver > self.cur_version:
- self.latest_avail_ver = self.venv_ver
- else:
- if self.cur_version:
- self.latest_avail_ver = self.cur_version
- else:
- self.latest_avail_ver = (0, 0, 0)
-
- # As we don't know package version of Sphinx, and there's no
- # virtual environments, don't check if upgrades are needed
- if not self.virtualenv:
- if not self.latest_avail_ver:
- return None
-
- return self.latest_avail_ver
-
- # Either there are already a virtual env or a new one should be created
- self.need_pip = True
-
- if not self.latest_avail_ver:
- return None
-
- # Return if the reason is due to an upgrade or not
- if self.latest_avail_ver != (0, 0, 0):
- if self.latest_avail_ver < RECOMMENDED_VERSION:
- self.rec_sphinx_upgrade = 1
-
- return self.latest_avail_ver
-
- def recommend_package(self):
- """
- Recommend installing Sphinx as a distro-specific package.
- """
-
- print("\n2) As a package with:")
-
- old_need = self.deps.need
- old_optional = self.deps.optional
-
- self.pdf = False
- self.deps.optional = 0
- old_verbose = self.verbose_warn_install
- self.verbose_warn_install = 0
-
- self.deps.clear_deps()
-
- self.deps.add_package("python-sphinx", DepManager.PYTHON_MANDATORY)
-
- cmd = self.get_install()
- if cmd:
- print(cmd)
-
- self.deps.need = old_need
- self.deps.optional = old_optional
- self.verbose_warn_install = old_verbose
-
- def recommend_sphinx_version(self, virtualenv_cmd):
- """
- Provide recommendations for installing or upgrading Sphinx based
- on current version.
-
- The logic here is complex, as it have to deal with different versions:
-
- - minimal supported version;
- - minimal PDF version;
- - recommended version.
-
- It also needs to work fine with both distro's package and
- venv/virtualenv
- """
-
- if self.recommend_python:
- cur_ver = sys.version_info[:3]
- if cur_ver < MIN_PYTHON_VERSION:
- print(f"\nPython version {cur_ver} is incompatible with doc build.\n" \
- "Please upgrade it and re-run.\n")
- return
-
- # Version is OK. Nothing to do.
- if self.cur_version != (0, 0, 0) and self.cur_version >= RECOMMENDED_VERSION:
- return
-
- if self.latest_avail_ver:
- latest_avail_ver = ver_str(self.latest_avail_ver)
-
- if not self.need_sphinx:
- # sphinx-build is present and its version is >= $min_version
-
- # only recommend enabling a newer virtenv version if makes sense.
- if self.latest_avail_ver and self.latest_avail_ver > self.cur_version:
- print(f"\nYou may also use the newer Sphinx version {latest_avail_ver} with:")
- if f"{self.virtenv_prefix}" in os.getcwd():
- print("\tdeactivate")
- print(f"\t. {self.activate_cmd}")
- self.deactivate_help()
- return
-
- if self.latest_avail_ver and self.latest_avail_ver >= RECOMMENDED_VERSION:
- return
-
- if not self.virtualenv:
- # No sphinx either via package or via virtenv. As we can't
- # Compare the versions here, just return, recommending the
- # user to install it from the package distro.
- if not self.latest_avail_ver or self.latest_avail_ver == (0, 0, 0):
- return
-
- # User doesn't want a virtenv recommendation, but he already
- # installed one via virtenv with a newer version.
- # So, print commands to enable it
- if self.latest_avail_ver > self.cur_version:
- print(f"\nYou may also use the Sphinx virtualenv version {latest_avail_ver} with:")
- if f"{self.virtenv_prefix}" in os.getcwd():
- print("\tdeactivate")
- print(f"\t. {self.activate_cmd}")
- self.deactivate_help()
- return
- print("\n")
- else:
- if self.need_sphinx:
- self.deps.need += 1
-
- # Suggest newer versions if current ones are too old
- if self.latest_avail_ver and self.latest_avail_ver >= self.min_version:
- if self.latest_avail_ver >= RECOMMENDED_VERSION:
- print(f"\nNeed to activate Sphinx (version {latest_avail_ver}) on virtualenv with:")
- print(f"\t. {self.activate_cmd}")
- self.deactivate_help()
- return
-
- # Version is above the minimal required one, but may be
- # below the recommended one. So, print warnings/notes
- if self.latest_avail_ver < RECOMMENDED_VERSION:
- print(f"Warning: It is recommended at least Sphinx version {RECOMMENDED_VERSION}.")
-
- # At this point, either it needs Sphinx or upgrade is recommended,
- # both via pip
-
- if self.rec_sphinx_upgrade:
- if not self.virtualenv:
- print("Instead of install/upgrade Python Sphinx pkg, you could use pip/pypi with:\n\n")
- else:
- print("To upgrade Sphinx, use:\n\n")
- else:
- print("\nSphinx needs to be installed either:\n1) via pip/pypi with:\n")
-
- if not virtualenv_cmd:
- print(" Currently not possible.\n")
- print(" Please upgrade Python to a newer version and run this script again")
- else:
- print(f"\t{virtualenv_cmd} {self.virtenv_dir}")
- print(f"\t. {self.virtenv_dir}/bin/activate")
- print(f"\tpip install -r {self.requirement_file}")
- self.deactivate_help()
-
- if self.package_supported:
- self.recommend_package()
-
- print("\n" \
- " Please note that Sphinx currentlys produce false-positive\n" \
- " warnings when the same name is used for more than one type (functions,\n" \
- " structs, enums,...). This is known Sphinx bug. For more details, see:\n" \
- "\thttps://github.com/sphinx-doc/sphinx/pull/8313")
-
- def check_needs(self):
- """
- Main method that checks needed dependencies and provides
- recommendations.
- """
- self.python_cmd = sys.executable
-
- # Check if Sphinx is already accessible from current environment
- self.check_sphinx(self.conf)
-
- if self.system_release:
- print(f"Detected OS: {self.system_release}.")
- else:
- print("Unknown OS")
- if self.cur_version != (0, 0, 0):
- ver = ver_str(self.cur_version)
- print(f"Sphinx version: {ver}\n")
-
- # Check the type of virtual env, depending on Python version
- virtualenv_cmd = None
-
- if sys.version_info < MIN_PYTHON_VERSION:
- min_ver = ver_str(MIN_PYTHON_VERSION)
- print(f"ERROR: at least python {min_ver} is required to build the kernel docs")
- self.need_sphinx = 1
-
- self.venv_ver = self.recommend_sphinx_upgrade()
-
- if self.need_pip:
- if sys.version_info < MIN_PYTHON_VERSION:
- self.need_pip = False
- print("Warning: python version is not supported.")
- else:
- virtualenv_cmd = f"{self.python_cmd} -m venv"
- self.check_python_module("ensurepip")
-
- # Check for needed programs/tools
- self.check_perl_module("Pod::Usage", DepManager.SYSTEM_MANDATORY)
-
- self.check_program("make", DepManager.SYSTEM_MANDATORY)
- self.check_program("which", DepManager.SYSTEM_MANDATORY)
-
- self.check_program("dot", DepManager.SYSTEM_OPTIONAL)
- self.check_program("convert", DepManager.SYSTEM_OPTIONAL)
-
- self.check_python_module("yaml")
-
- if self.pdf:
- self.check_program("xelatex", DepManager.PDF_MANDATORY)
- self.check_program("rsvg-convert", DepManager.PDF_MANDATORY)
- self.check_program("latexmk", DepManager.PDF_MANDATORY)
-
- # Do distro-specific checks and output distro-install commands
- cmd = self.get_install()
- if cmd:
- print(cmd)
-
- # If distro requires some special instructions, print here.
- # Please notice that get_install() needs to be called first.
- if self.distro_msg:
- print("\n" + self.distro_msg)
-
- if not self.python_cmd:
- if self.need == 1:
- sys.exit("Can't build as 1 mandatory dependency is missing")
- elif self.need:
- sys.exit(f"Can't build as {self.need} mandatory dependencies are missing")
-
- # Check if sphinx-build is called sphinx-build-3
- if self.need_symlink:
- sphinx_path = self.which("sphinx-build-3")
- if sphinx_path:
- print(f"\tsudo ln -sf {sphinx_path} /usr/bin/sphinx-build\n")
-
- self.recommend_sphinx_version(virtualenv_cmd)
- print("")
-
- if not self.deps.optional:
- print("All optional dependencies are met.")
-
- if self.deps.need == 1:
- sys.exit("Can't build as 1 mandatory dependency is missing")
- elif self.deps.need:
- sys.exit(f"Can't build as {self.deps.need} mandatory dependencies are missing")
-
- print("Needed package dependencies are met.")
-
-DESCRIPTION = """
-Process some flags related to Sphinx installation and documentation build.
-"""
-
-
-def main():
- """Main function"""
- parser = argparse.ArgumentParser(description=DESCRIPTION)
-
- parser.add_argument(
- "--no-virtualenv",
- action="store_false",
- dest="virtualenv",
- help="Recommend installing Sphinx instead of using a virtualenv",
- )
-
- parser.add_argument(
- "--no-pdf",
- action="store_false",
- dest="pdf",
- help="Don't check for dependencies required to build PDF docs",
- )
-
- parser.add_argument(
- "--version-check",
- action="store_true",
- dest="version_check",
- help="If version is compatible, don't check for missing dependencies",
- )
-
- args = parser.parse_args()
-
- checker = SphinxDependencyChecker(args)
-
- checker.check_python()
- checker.check_needs()
-
-# Call main if not used as module
-if __name__ == "__main__":
- main()
diff --git a/scripts/split-man.pl b/scripts/split-man.pl
deleted file mode 100755
index 96bd99dc977a..000000000000
--- a/scripts/split-man.pl
+++ /dev/null
@@ -1,28 +0,0 @@
-#!/usr/bin/env perl
-# SPDX-License-Identifier: GPL-2.0
-#
-# Author: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
-#
-# Produce manpages from kernel-doc.
-# See Documentation/doc-guide/kernel-doc.rst for instructions
-
-if ($#ARGV < 0) {
- die "where do I put the results?\n";
-}
-
-mkdir $ARGV[0],0777;
-$state = 0;
-while (<STDIN>) {
- if (/^\.TH \"[^\"]*\" 9 \"([^\"]*)\"/) {
- if ($state == 1) { close OUT }
- $state = 1;
- $fn = "$ARGV[0]/$1.9";
- print STDERR "Creating $fn\n";
- open OUT, ">$fn" or die "can't open $fn: $!\n";
- print OUT $_;
- } elsif ($state != 0) {
- print OUT $_;
- }
-}
-
-close OUT;
diff --git a/scripts/syscall.tbl b/scripts/syscall.tbl
index d1ae5e92c615..7a42b32b6577 100644
--- a/scripts/syscall.tbl
+++ b/scripts/syscall.tbl
@@ -410,3 +410,5 @@
467 common open_tree_attr sys_open_tree_attr
468 common file_getattr sys_file_getattr
469 common file_setattr sys_file_setattr
+470 common listns sys_listns
+471 common rseq_slice_yield sys_rseq_slice_yield
diff --git a/scripts/tags.sh b/scripts/tags.sh
index 99ce427d9a69..243373683f98 100755
--- a/scripts/tags.sh
+++ b/scripts/tags.sh
@@ -221,6 +221,7 @@ regex_c=(
'/^\<DEFINE_GUARD_COND(\([[:alnum:]_]\+\),[[:space:]]*\([[:alnum:]_]\+\)/class_\1\2/'
'/^\<DEFINE_LOCK_GUARD_[[:digit:]](\([[:alnum:]_]\+\)/class_\1/'
'/^\<DEFINE_LOCK_GUARD_[[:digit:]]_COND(\([[:alnum:]_]\+\),[[:space:]]*\([[:alnum:]_]\+\)/class_\1\2/'
+ '/^context_lock_struct(\([^,)]*\)[^)]*)/struct \1/'
)
regex_kconfig=(
'/^[[:blank:]]*\(menu\|\)config[[:blank:]]\+\([[:alnum:]_]\+\)/\2/'
diff --git a/scripts/test_doc_build.py b/scripts/test_doc_build.py
deleted file mode 100755
index 47b4606569f9..000000000000
--- a/scripts/test_doc_build.py
+++ /dev/null
@@ -1,513 +0,0 @@
-#!/usr/bin/env python3
-# SPDX-License-Identifier: GPL-2.0
-# Copyright(c) 2025: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
-#
-# pylint: disable=R0903,R0912,R0913,R0914,R0917,C0301
-
-"""
-Install minimal supported requirements for different Sphinx versions
-and optionally test the build.
-"""
-
-import argparse
-import asyncio
-import os.path
-import shutil
-import sys
-import time
-import subprocess
-
-# Minimal python version supported by the building system.
-
-PYTHON = os.path.basename(sys.executable)
-
-min_python_bin = None
-
-for i in range(9, 13):
- p = f"python3.{i}"
- if shutil.which(p):
- min_python_bin = p
- break
-
-if not min_python_bin:
- min_python_bin = PYTHON
-
-# Starting from 8.0, Python 3.9 is not supported anymore.
-PYTHON_VER_CHANGES = {(8, 0, 0): PYTHON}
-
-DEFAULT_VERSIONS_TO_TEST = [
- (3, 4, 3), # Minimal supported version
- (5, 3, 0), # CentOS Stream 9 / AlmaLinux 9
- (6, 1, 1), # Debian 12
- (7, 2, 1), # openSUSE Leap 15.6
- (7, 2, 6), # Ubuntu 24.04 LTS
- (7, 4, 7), # Ubuntu 24.10
- (7, 3, 0), # openSUSE Tumbleweed
- (8, 1, 3), # Fedora 42
- (8, 2, 3) # Latest version - covers rolling distros
-]
-
-# Sphinx versions to be installed and their incremental requirements
-SPHINX_REQUIREMENTS = {
- # Oldest versions we support for each package required by Sphinx 3.4.3
- (3, 4, 3): {
- "docutils": "0.16",
- "alabaster": "0.7.12",
- "babel": "2.8.0",
- "certifi": "2020.6.20",
- "docutils": "0.16",
- "idna": "2.10",
- "imagesize": "1.2.0",
- "Jinja2": "2.11.2",
- "MarkupSafe": "1.1.1",
- "packaging": "20.4",
- "Pygments": "2.6.1",
- "PyYAML": "5.1",
- "requests": "2.24.0",
- "snowballstemmer": "2.0.0",
- "sphinxcontrib-applehelp": "1.0.2",
- "sphinxcontrib-devhelp": "1.0.2",
- "sphinxcontrib-htmlhelp": "1.0.3",
- "sphinxcontrib-jsmath": "1.0.1",
- "sphinxcontrib-qthelp": "1.0.3",
- "sphinxcontrib-serializinghtml": "1.1.4",
- "urllib3": "1.25.9",
- },
-
- # Update package dependencies to a more modern base. The goal here
- # is to avoid to many incremental changes for the next entries
- (3, 5, 0): {
- "alabaster": "0.7.13",
- "babel": "2.17.0",
- "certifi": "2025.6.15",
- "idna": "3.10",
- "imagesize": "1.4.1",
- "packaging": "25.0",
- "Pygments": "2.8.1",
- "requests": "2.32.4",
- "snowballstemmer": "3.0.1",
- "sphinxcontrib-applehelp": "1.0.4",
- "sphinxcontrib-htmlhelp": "2.0.1",
- "sphinxcontrib-serializinghtml": "1.1.5",
- "urllib3": "2.0.0",
- },
-
- # Starting from here, ensure all docutils versions are covered with
- # supported Sphinx versions. Other packages are upgraded only when
- # required by pip
- (4, 0, 0): {
- "PyYAML": "5.1",
- },
- (4, 1, 0): {
- "docutils": "0.17",
- "Pygments": "2.19.1",
- "Jinja2": "3.0.3",
- "MarkupSafe": "2.0",
- },
- (4, 3, 0): {},
- (4, 4, 0): {},
- (4, 5, 0): {
- "docutils": "0.17.1",
- },
- (5, 0, 0): {},
- (5, 1, 0): {},
- (5, 2, 0): {
- "docutils": "0.18",
- "Jinja2": "3.1.2",
- "MarkupSafe": "2.0",
- "PyYAML": "5.3.1",
- },
- (5, 3, 0): {
- "docutils": "0.18.1",
- },
- (6, 0, 0): {},
- (6, 1, 0): {},
- (6, 2, 0): {
- "PyYAML": "5.4.1",
- },
- (7, 0, 0): {},
- (7, 1, 0): {},
- (7, 2, 0): {
- "docutils": "0.19",
- "PyYAML": "6.0.1",
- "sphinxcontrib-serializinghtml": "1.1.9",
- },
- (7, 2, 6): {
- "docutils": "0.20",
- },
- (7, 3, 0): {
- "alabaster": "0.7.14",
- "PyYAML": "6.0.1",
- "tomli": "2.0.1",
- },
- (7, 4, 0): {
- "docutils": "0.20.1",
- "PyYAML": "6.0.1",
- },
- (8, 0, 0): {
- "docutils": "0.21",
- },
- (8, 1, 0): {
- "docutils": "0.21.1",
- "PyYAML": "6.0.1",
- "sphinxcontrib-applehelp": "1.0.7",
- "sphinxcontrib-devhelp": "1.0.6",
- "sphinxcontrib-htmlhelp": "2.0.6",
- "sphinxcontrib-qthelp": "1.0.6",
- },
- (8, 2, 0): {
- "docutils": "0.21.2",
- "PyYAML": "6.0.1",
- "sphinxcontrib-serializinghtml": "1.1.9",
- },
-}
-
-
-class AsyncCommands:
- """Excecute command synchronously"""
-
- def __init__(self, fp=None):
-
- self.stdout = None
- self.stderr = None
- self.output = None
- self.fp = fp
-
- def log(self, out, verbose, is_info=True):
- out = out.removesuffix('\n')
-
- if verbose:
- if is_info:
- print(out)
- else:
- print(out, file=sys.stderr)
-
- if self.fp:
- self.fp.write(out + "\n")
-
- async def _read(self, stream, verbose, is_info):
- """Ancillary routine to capture while displaying"""
-
- while stream is not None:
- line = await stream.readline()
- if line:
- out = line.decode("utf-8", errors="backslashreplace")
- self.log(out, verbose, is_info)
- if is_info:
- self.stdout += out
- else:
- self.stderr += out
- else:
- break
-
- async def run(self, cmd, capture_output=False, check=False,
- env=None, verbose=True):
-
- """
- Execute an arbitrary command, handling errors.
-
- Please notice that this class is not thread safe
- """
-
- self.stdout = ""
- self.stderr = ""
-
- self.log("$ " + " ".join(cmd), verbose)
-
- proc = await asyncio.create_subprocess_exec(cmd[0],
- *cmd[1:],
- env=env,
- stdout=asyncio.subprocess.PIPE,
- stderr=asyncio.subprocess.PIPE)
-
- # Handle input and output in realtime
- await asyncio.gather(
- self._read(proc.stdout, verbose, True),
- self._read(proc.stderr, verbose, False),
- )
-
- await proc.wait()
-
- if check and proc.returncode > 0:
- raise subprocess.CalledProcessError(returncode=proc.returncode,
- cmd=" ".join(cmd),
- output=self.stdout,
- stderr=self.stderr)
-
- if capture_output:
- if proc.returncode > 0:
- self.log(f"Error {proc.returncode}", verbose=True, is_info=False)
- return ""
-
- return self.output
-
- ret = subprocess.CompletedProcess(args=cmd,
- returncode=proc.returncode,
- stdout=self.stdout,
- stderr=self.stderr)
-
- return ret
-
-
-class SphinxVenv:
- """
- Installs Sphinx on one virtual env per Sphinx version with a minimal
- set of dependencies, adjusting them to each specific version.
- """
-
- def __init__(self):
- """Initialize instance variables"""
-
- self.built_time = {}
- self.first_run = True
-
- async def _handle_version(self, args, fp,
- cur_ver, cur_requirements, python_bin):
- """Handle a single Sphinx version"""
-
- cmd = AsyncCommands(fp)
-
- ver = ".".join(map(str, cur_ver))
-
- if not self.first_run and args.wait_input and args.build:
- ret = input("Press Enter to continue or 'a' to abort: ").strip().lower()
- if ret == "a":
- print("Aborted.")
- sys.exit()
- else:
- self.first_run = False
-
- venv_dir = f"Sphinx_{ver}"
- req_file = f"requirements_{ver}.txt"
-
- cmd.log(f"\nSphinx {ver} with {python_bin}", verbose=True)
-
- # Create venv
- await cmd.run([python_bin, "-m", "venv", venv_dir],
- verbose=args.verbose, check=True)
- pip = os.path.join(venv_dir, "bin/pip")
-
- # Create install list
- reqs = []
- for pkg, verstr in cur_requirements.items():
- reqs.append(f"{pkg}=={verstr}")
-
- reqs.append(f"Sphinx=={ver}")
-
- await cmd.run([pip, "install"] + reqs, check=True, verbose=args.verbose)
-
- # Freeze environment
- result = await cmd.run([pip, "freeze"], verbose=False, check=True)
-
- # Pip install succeeded. Write requirements file
- if args.req_file:
- with open(req_file, "w", encoding="utf-8") as fp:
- fp.write(result.stdout)
-
- if args.build:
- start_time = time.time()
-
- # Prepare a venv environment
- env = os.environ.copy()
- bin_dir = os.path.join(venv_dir, "bin")
- env["PATH"] = bin_dir + ":" + env["PATH"]
- env["VIRTUAL_ENV"] = venv_dir
- if "PYTHONHOME" in env:
- del env["PYTHONHOME"]
-
- # Test doc build
- await cmd.run(["make", "cleandocs"], env=env, check=True)
- make = ["make"]
-
- if args.output:
- sphinx_build = os.path.realpath(f"{bin_dir}/sphinx-build")
- make += [f"O={args.output}", f"SPHINXBUILD={sphinx_build}"]
-
- if args.make_args:
- make += args.make_args
-
- make += args.targets
-
- if args.verbose:
- cmd.log(f". {bin_dir}/activate", verbose=True)
- await cmd.run(make, env=env, check=True, verbose=True)
- if args.verbose:
- cmd.log("deactivate", verbose=True)
-
- end_time = time.time()
- elapsed_time = end_time - start_time
- hours, minutes = divmod(elapsed_time, 3600)
- minutes, seconds = divmod(minutes, 60)
-
- hours = int(hours)
- minutes = int(minutes)
- seconds = int(seconds)
-
- self.built_time[ver] = f"{hours:02d}:{minutes:02d}:{seconds:02d}"
-
- cmd.log(f"Finished doc build for Sphinx {ver}. Elapsed time: {self.built_time[ver]}", verbose=True)
-
- async def run(self, args):
- """
- Navigate though multiple Sphinx versions, handling each of them
- on a loop.
- """
-
- if args.log:
- fp = open(args.log, "w", encoding="utf-8")
- if not args.verbose:
- args.verbose = False
- else:
- fp = None
- if not args.verbose:
- args.verbose = True
-
- cur_requirements = {}
- python_bin = min_python_bin
-
- vers = set(SPHINX_REQUIREMENTS.keys()) | set(args.versions)
-
- for cur_ver in sorted(vers):
- if cur_ver in SPHINX_REQUIREMENTS:
- new_reqs = SPHINX_REQUIREMENTS[cur_ver]
- cur_requirements.update(new_reqs)
-
- if cur_ver in PYTHON_VER_CHANGES: # pylint: disable=R1715
- python_bin = PYTHON_VER_CHANGES[cur_ver]
-
- if cur_ver not in args.versions:
- continue
-
- if args.min_version:
- if cur_ver < args.min_version:
- continue
-
- if args.max_version:
- if cur_ver > args.max_version:
- break
-
- await self._handle_version(args, fp, cur_ver, cur_requirements,
- python_bin)
-
- if args.build:
- cmd = AsyncCommands(fp)
- cmd.log("\nSummary:", verbose=True)
- for ver, elapsed_time in sorted(self.built_time.items()):
- cmd.log(f"\tSphinx {ver} elapsed time: {elapsed_time}",
- verbose=True)
-
- if fp:
- fp.close()
-
-def parse_version(ver_str):
- """Convert a version string into a tuple."""
-
- return tuple(map(int, ver_str.split(".")))
-
-
-DEFAULT_VERS = " - "
-DEFAULT_VERS += "\n - ".join(map(lambda v: f"{v[0]}.{v[1]}.{v[2]}",
- DEFAULT_VERSIONS_TO_TEST))
-
-SCRIPT = os.path.relpath(__file__)
-
-DESCRIPTION = f"""
-This tool allows creating Python virtual environments for different
-Sphinx versions that are supported by the Linux Kernel build system.
-
-Besides creating the virtual environment, it can also test building
-the documentation using "make htmldocs" (and/or other doc targets).
-
-If called without "--versions" argument, it covers the versions shipped
-on major distros, plus the lowest supported version:
-
-{DEFAULT_VERS}
-
-A typical usage is to run:
-
- {SCRIPT} -m -l sphinx_builds.log
-
-This will create one virtual env for the default version set and run
-"make htmldocs" for each version, creating a log file with the
-excecuted commands on it.
-
-NOTE: The build time can be very long, specially on old versions. Also, there
-is a known bug with Sphinx version 6.0.x: each subprocess uses a lot of
-memory. That, together with "-jauto" may cause OOM killer to cause
-failures at the doc generation. To minimize the risk, you may use the
-"-a" command line parameter to constrain the built directories and/or
-reduce the number of threads from "-jauto" to, for instance, "-j4":
-
- {SCRIPT} -m -V 6.0.1 -a "SPHINXDIRS=process" "SPHINXOPTS='-j4'"
-
-"""
-
-MAKE_TARGETS = [
- "htmldocs",
- "texinfodocs",
- "infodocs",
- "latexdocs",
- "pdfdocs",
- "epubdocs",
- "xmldocs",
-]
-
-async def main():
- """Main program"""
-
- parser = argparse.ArgumentParser(description=DESCRIPTION,
- formatter_class=argparse.RawDescriptionHelpFormatter)
-
- ver_group = parser.add_argument_group("Version range options")
-
- ver_group.add_argument('-V', '--versions', nargs="*",
- default=DEFAULT_VERSIONS_TO_TEST,type=parse_version,
- help='Sphinx versions to test')
- ver_group.add_argument('--min-version', "--min", type=parse_version,
- help='Sphinx minimal version')
- ver_group.add_argument('--max-version', "--max", type=parse_version,
- help='Sphinx maximum version')
- ver_group.add_argument('-f', '--full', action='store_true',
- help='Add all Sphinx (major,minor) supported versions to the version range')
-
- build_group = parser.add_argument_group("Build options")
-
- build_group.add_argument('-b', '--build', action='store_true',
- help='Build documentation')
- build_group.add_argument('-a', '--make-args', nargs="*",
- help='extra arguments for make, like SPHINXDIRS=netlink/specs',
- )
- build_group.add_argument('-t', '--targets', nargs="+", choices=MAKE_TARGETS,
- default=[MAKE_TARGETS[0]],
- help="make build targets. Default: htmldocs.")
- build_group.add_argument("-o", '--output',
- help="output directory for the make O=OUTPUT")
-
- other_group = parser.add_argument_group("Other options")
-
- other_group.add_argument('-r', '--req-file', action='store_true',
- help='write a requirements.txt file')
- other_group.add_argument('-l', '--log',
- help='Log command output on a file')
- other_group.add_argument('-v', '--verbose', action='store_true',
- help='Verbose all commands')
- other_group.add_argument('-i', '--wait-input', action='store_true',
- help='Wait for an enter before going to the next version')
-
- args = parser.parse_args()
-
- if not args.make_args:
- args.make_args = []
-
- sphinx_versions = sorted(list(SPHINX_REQUIREMENTS.keys()))
-
- if args.full:
- args.versions += list(SPHINX_REQUIREMENTS.keys())
-
- venv = SphinxVenv()
- await venv.run(args)
-
-
-# Call main method
-if __name__ == "__main__":
- asyncio.run(main())
diff --git a/scripts/tracepoint-update.c b/scripts/tracepoint-update.c
new file mode 100644
index 000000000000..5cf43c0aac89
--- /dev/null
+++ b/scripts/tracepoint-update.c
@@ -0,0 +1,266 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <getopt.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <pthread.h>
+
+#include "elf-parse.h"
+
+static Elf_Shdr *check_data_sec;
+static Elf_Shdr *tracepoint_data_sec;
+
+static inline void *get_index(void *start, int entsize, int index)
+{
+ return start + (entsize * index);
+}
+
+static int compare_strings(const void *a, const void *b)
+{
+ const char *av = *(const char **)a;
+ const char *bv = *(const char **)b;
+
+ return strcmp(av, bv);
+}
+
+struct elf_tracepoint {
+ Elf_Ehdr *ehdr;
+ const char **array;
+ int count;
+};
+
+#define REALLOC_SIZE (1 << 10)
+#define REALLOC_MASK (REALLOC_SIZE - 1)
+
+static int add_string(const char *str, const char ***vals, int *count)
+{
+ const char **array = *vals;
+
+ if (!(*count & REALLOC_MASK)) {
+ int size = (*count) + REALLOC_SIZE;
+
+ array = realloc(array, sizeof(char *) * size);
+ if (!array) {
+ fprintf(stderr, "Failed memory allocation\n");
+ free(*vals);
+ *vals = NULL;
+ return -1;
+ }
+ *vals = array;
+ }
+
+ array[(*count)++] = str;
+ return 0;
+}
+
+/**
+ * for_each_shdr_str - iterator that reads strings that are in an ELF section.
+ * @len: "int" to hold the length of the current string
+ * @ehdr: A pointer to the ehdr of the ELF file
+ * @sec: The section that has the strings to iterate on
+ *
+ * This is a for loop that iterates over all the nul terminated strings
+ * that are in a given ELF section. The variable "str" will hold
+ * the current string for each iteration and the passed in @len will
+ * contain the strlen() of that string.
+ */
+#define for_each_shdr_str(len, ehdr, sec) \
+ for (const char *str = (void *)(ehdr) + shdr_offset(sec), \
+ *end = str + shdr_size(sec); \
+ len = strlen(str), str < end; \
+ str += (len) + 1)
+
+
+static void make_trace_array(struct elf_tracepoint *etrace)
+{
+ Elf_Ehdr *ehdr = etrace->ehdr;
+ const char **vals = NULL;
+ int count = 0;
+ int len;
+
+ etrace->array = NULL;
+
+ /*
+ * The __tracepoint_check section is filled with strings of the
+ * names of tracepoints (in tracepoint_strings). Create an array
+ * that points to each string and then sort the array.
+ */
+ for_each_shdr_str(len, ehdr, check_data_sec) {
+ if (!len)
+ continue;
+ if (add_string(str, &vals, &count) < 0)
+ return;
+ }
+
+ /* If CONFIG_TRACEPOINT_VERIFY_USED is not set, there's nothing to do */
+ if (!count)
+ return;
+
+ qsort(vals, count, sizeof(char *), compare_strings);
+
+ etrace->array = vals;
+ etrace->count = count;
+}
+
+static int find_event(const char *str, void *array, size_t size)
+{
+ return bsearch(&str, array, size, sizeof(char *), compare_strings) != NULL;
+}
+
+static void check_tracepoints(struct elf_tracepoint *etrace, const char *fname)
+{
+ Elf_Ehdr *ehdr = etrace->ehdr;
+ int len;
+
+ if (!etrace->array)
+ return;
+
+ /*
+ * The __tracepoints_strings section holds all the names of the
+ * defined tracepoints. If any of them are not in the
+ * __tracepoint_check_section it means they are not used.
+ */
+ for_each_shdr_str(len, ehdr, tracepoint_data_sec) {
+ if (!len)
+ continue;
+ if (!find_event(str, etrace->array, etrace->count)) {
+ fprintf(stderr, "warning: tracepoint '%s' is unused", str);
+ if (fname)
+ fprintf(stderr, " in module %s\n", fname);
+ else
+ fprintf(stderr, "\n");
+ }
+ }
+
+ free(etrace->array);
+}
+
+static void *tracepoint_check(struct elf_tracepoint *etrace, const char *fname)
+{
+ make_trace_array(etrace);
+ check_tracepoints(etrace, fname);
+
+ return NULL;
+}
+
+static int process_tracepoints(bool mod, void *addr, const char *fname)
+{
+ struct elf_tracepoint etrace = {0};
+ Elf_Ehdr *ehdr = addr;
+ Elf_Shdr *shdr_start;
+ Elf_Shdr *string_sec;
+ const char *secstrings;
+ unsigned int shnum;
+ unsigned int shstrndx;
+ int shentsize;
+ int idx;
+ int done = 2;
+
+ shdr_start = (Elf_Shdr *)((char *)ehdr + ehdr_shoff(ehdr));
+ shentsize = ehdr_shentsize(ehdr);
+
+ shstrndx = ehdr_shstrndx(ehdr);
+ if (shstrndx == SHN_XINDEX)
+ shstrndx = shdr_link(shdr_start);
+ string_sec = get_index(shdr_start, shentsize, shstrndx);
+ secstrings = (const char *)ehdr + shdr_offset(string_sec);
+
+ shnum = ehdr_shnum(ehdr);
+ if (shnum == SHN_UNDEF)
+ shnum = shdr_size(shdr_start);
+
+ for (int i = 0; done && i < shnum; i++) {
+ Elf_Shdr *shdr = get_index(shdr_start, shentsize, i);
+
+ idx = shdr_name(shdr);
+
+ /* locate the __tracepoint_check in vmlinux */
+ if (!strcmp(secstrings + idx, "__tracepoint_check")) {
+ check_data_sec = shdr;
+ done--;
+ }
+
+ /* locate the __tracepoints_ptrs section in vmlinux */
+ if (!strcmp(secstrings + idx, "__tracepoints_strings")) {
+ tracepoint_data_sec = shdr;
+ done--;
+ }
+ }
+
+ /*
+ * Modules may not have either section. But if it has one section,
+ * it should have both of them.
+ */
+ if (mod && !check_data_sec && !tracepoint_data_sec)
+ return 0;
+
+ if (!check_data_sec) {
+ if (mod) {
+ fprintf(stderr, "warning: Module %s has only unused tracepoints\n", fname);
+ /* Do not fail build */
+ return 0;
+ }
+ fprintf(stderr, "no __tracepoint_check in file: %s\n", fname);
+ return -1;
+ }
+
+ if (!tracepoint_data_sec) {
+ /* A module may reference only exported tracepoints */
+ if (mod)
+ return 0;
+ fprintf(stderr, "no __tracepoint_strings in file: %s\n", fname);
+ return -1;
+ }
+
+ if (!mod)
+ fname = NULL;
+
+ etrace.ehdr = ehdr;
+ tracepoint_check(&etrace, fname);
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ int n_error = 0;
+ size_t size = 0;
+ void *addr = NULL;
+ bool mod = false;
+
+ if (argc > 1 && strcmp(argv[1], "--module") == 0) {
+ mod = true;
+ argc--;
+ argv++;
+ }
+
+ if (argc < 2) {
+ if (mod)
+ fprintf(stderr, "usage: tracepoint-update --module module...\n");
+ else
+ fprintf(stderr, "usage: tracepoint-update vmlinux...\n");
+ return 0;
+ }
+
+ /* Process each file in turn, allowing deep failure. */
+ for (int i = 1; i < argc; i++) {
+ addr = elf_map(argv[i], &size, 1 << ET_REL);
+ if (!addr) {
+ ++n_error;
+ continue;
+ }
+
+ if (process_tracepoints(mod, addr, argv[i]))
+ ++n_error;
+
+ elf_unmap(addr, size);
+ }
+
+ return !!n_error;
+}
diff --git a/scripts/ver_linux b/scripts/ver_linux
index d6f2362d3792..00bdaf30d590 100755
--- a/scripts/ver_linux
+++ b/scripts/ver_linux
@@ -7,7 +7,7 @@
BEGIN {
usage = "If some fields are empty or look unusual you may have an old version.\n"
- usage = usage "Compare to the current minimal requirements in Documentation/Changes.\n"
+ usage = usage "Compare to the current minimal requirements in Documentation/process/changes.rst\n"
print usage
system("uname -a")
@@ -17,37 +17,58 @@ BEGIN {
libc = "libc[.]so[.][0-9]+$"
libcpp = "(libg|stdc)[+]+[.]so([.][0-9]+)+$"
+ printversion("bash", version("bash --version"))
+ printversion("bc", version("bc --version"))
+ printversion("bindgen", version("bindgen --version"))
+ printversion("binutils", version("ld -v"))
+ printversion("bison", version("bison --version"))
+ printversion("btrfs-progs", version("btrfs --version"))
+ printversion("Clang", version("clang --version"))
+ printversion("Console-tools", version("loadkeys -V"))
+ printversion("Dynamic linker (ldd)", version("ldd --version"))
+ printversion("e2fsprogs", version("e2fsck -V"))
+ printversion("flex", version("flex --version"))
+ printversion("gdb", version("gdb -version"))
+ printversion("GNU awk", version("gawk --version"))
printversion("GNU C", version("gcc -dumpversion"))
- printversion("GNU Make", version("make --version"))
- printversion("Binutils", version("ld -v"))
- printversion("Util-linux", version("mount --version"))
- printversion("Mount", version("mount --version"))
- printversion("Module-init-tools", version("depmod -V"))
- printversion("E2fsprogs", version("tune2fs"))
- printversion("Jfsutils", version("fsck.jfs -V"))
- printversion("Xfsprogs", version("xfs_db -V"))
- printversion("Pcmciautils", version("pccardctl -V"))
- printversion("Pcmcia-cs", version("cardmgr -V"))
- printversion("Quota-tools", version("quota -V"))
- printversion("PPP", version("pppd --version"))
+ printversion("GNU make", version("make --version"))
+ printversion("GNU tar", version("tar --version"))
+ printversion("GRUB2", version("grub2-install --version"))
+ printversion("GRUB", version("grub-install --version"))
+ printversion("gtags", version("gtags --version"))
+ printversion("iptables", version("iptables -V"))
printversion("Isdn4k-utils", version("isdnctrl"))
- printversion("Nfs-utils", version("showmount --version"))
- printversion("Bison", version("bison --version"))
- printversion("Flex", version("flex --version"))
+ printversion("jfsutils", version("fsck.jfs -V"))
+ printversion("Kbd", version("loadkeys -V"))
+ printversion("kmod", version("kmod -V"))
while ("ldconfig -p 2>/dev/null" | getline > 0)
if ($NF ~ libc || $NF ~ libcpp)
if (!seen[ver = version("readlink " $NF)]++)
printversion("Linux C" ($NF ~ libcpp? "++" : "") " Library", ver)
- printversion("Dynamic linker (ldd)", version("ldd --version"))
- printversion("Procps", version("ps --version"))
+ printversion("mcelog", version("mcelog --version"))
+ printversion("mkimage", version("mkimage --version"))
+ printversion("Module-init-tools", version("depmod -V"))
+ printversion("Mount", version("mount --version"))
printversion("Net-tools", version("ifconfig --version"))
- printversion("Kbd", version("loadkeys -V"))
- printversion("Console-tools", version("loadkeys -V"))
+ printversion("nfs-utils", version("showmount --version"))
+ printversion("openssl", version("openssl version"))
+ printversion("pahole", version("pahole --version"))
+ printversion("Pcmcia-cs", version("cardmgr -V"))
+ printversion("pcmciautils", version("pccardctl -V"))
+ printversion("PPP", version("pppd --version"))
+ printversion("procps", version("ps --version"))
+ printversion("Python", version("python3 -V"))
+ printversion("quota-tools", version("quota -V"))
+ printversion("Rust", version("rustc --version"))
printversion("Sh-utils", version("expr --v"))
- printversion("Udev", version("udevadm --version"))
+ printversion("Sphinx", version("sphinx-build --version"))
+ printversion("squashfs-tools", version("mksquashfs -version"))
+ printversion("udev", version("udevadm --version"))
+ printversion("util-linux", version("mount --version"))
printversion("Wireless-tools", version("iwconfig --version"))
+ printversion("xfsprogs", version("xfs_db -V"))
while ("sort /proc/modules" | getline > 0) {
mods = mods sep $1