summaryrefslogtreecommitdiff
path: root/scripts
diff options
context:
space:
mode:
authorJoonas Lahtinen <joonas.lahtinen@linux.intel.com>2026-05-12 11:16:35 +0300
committerJoonas Lahtinen <joonas.lahtinen@linux.intel.com>2026-05-12 11:16:35 +0300
commit8edf8b09fc44990977b3fbcb708035b1740d0b7e (patch)
tree0069cfbd4a05bd6c4c6057c3a36d0849f8a7414c /scripts
parent5ba54393dcd7adf75a9f39f5a933b1538349cad5 (diff)
parentf96538285cfdbb3acf5e3356e0bb88c38815790b (diff)
Merge drm/drm-next into drm-intel-gt-next
Backmerging to pull in commit 5401b9adebc9 ("i915: don't use a vma that didn't match the context VM") to revert it. Signed-off-by: Joonas Lahtinen <joonas.lahtinen@linux.intel.com>
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.dtbs11
-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.vmlinux96
-rw-r--r--scripts/Makefile.vmlinux_o32
-rw-r--r--scripts/Makefile.warn (renamed from scripts/Makefile.extrawarn)52
-rwxr-xr-xscripts/atomic/gen-atomic-instrumented.sh11
-rwxr-xr-xscripts/atomic/gen-atomics.sh1
-rwxr-xr-xscripts/atomic/gen-rust-atomic-helpers.sh62
-rw-r--r--scripts/atomic/kerneldoc/try_cmpxchg2
-rwxr-xr-xscripts/bloat-o-meter7
-rwxr-xr-xscripts/bpf_doc.py1
-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.pl117
-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/platform_no_drv_owner.cocci9
-rw-r--r--scripts/coccinelle/api/pm_runtime.cocci3
-rw-r--r--scripts/coccinelle/misc/of_table.cocci14
-rw-r--r--scripts/coccinelle/misc/ptr_err_to_pe.cocci34
-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.py233
-rwxr-xr-xscripts/decode_stacktrace.sh73
-rwxr-xr-xscripts/decodecode3
-rwxr-xr-xscripts/documentation-file-ref-check245
-rw-r--r--scripts/dtc/checks.c67
-rw-r--r--scripts/dtc/data.c47
-rwxr-xr-xscripts/dtc/dt-extract-compatibles1
-rwxr-xr-xscripts/dtc/dt_to_config8
-rw-r--r--scripts/dtc/dtc-lexer.l18
-rw-r--r--scripts/dtc/dtc.c11
-rw-r--r--scripts/dtc/dtc.h13
-rw-r--r--scripts/dtc/fdtoverlay.c8
-rw-r--r--scripts/dtc/flattree.c8
-rw-r--r--scripts/dtc/libfdt/fdt.c16
-rw-r--r--scripts/dtc/libfdt/fdt.h4
-rw-r--r--scripts/dtc/libfdt/fdt_overlay.c11
-rw-r--r--scripts/dtc/libfdt/fdt_ro.c4
-rw-r--r--scripts/dtc/libfdt/fdt_rw.c50
-rw-r--r--scripts/dtc/libfdt/libfdt.h393
-rw-r--r--scripts/dtc/libfdt/libfdt_env.h27
-rw-r--r--scripts/dtc/libfdt/libfdt_internal.h28
-rw-r--r--scripts/dtc/livetree.c306
-rw-r--r--scripts/dtc/srcpos.c39
-rw-r--r--scripts/dtc/srcpos.h1
-rw-r--r--scripts/dtc/treesource.c164
-rw-r--r--scripts/dtc/util.c16
-rw-r--r--scripts/dtc/util.h5
-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/extract-vmlinux8
-rwxr-xr-xscripts/faddr2line19
-rwxr-xr-xscripts/find-unused-docs.sh62
-rw-r--r--scripts/gcc-plugins/gcc-common.h7
-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.py4
-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.sh34
-rwxr-xr-xscripts/jobserver-exec88
-rw-r--r--scripts/kallsyms.c64
-rw-r--r--scripts/kconfig/Makefile4
-rw-r--r--scripts/kconfig/expr.h1
-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.l5
-rw-r--r--scripts/kconfig/lkc.h5
-rw-r--r--scripts/kconfig/mconf.c3
-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/nconf.c3
-rw-r--r--scripts/kconfig/nconf.gui.c8
-rw-r--r--scripts/kconfig/parser.y68
-rw-r--r--scripts/kconfig/qconf.cc42
-rwxr-xr-xscripts/kconfig/streamline_config.pl2
-rw-r--r--scripts/kconfig/symbol.c22
-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/conftest.py17
-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/tests/err_transitional/Kconfig52
-rw-r--r--scripts/kconfig/tests/err_transitional/__init__.py14
-rw-r--r--scripts/kconfig/tests/err_transitional/expected_stderr7
-rw-r--r--scripts/kconfig/tests/transitional/Kconfig132
-rw-r--r--scripts/kconfig/tests/transitional/__init__.py25
-rw-r--r--scripts/kconfig/tests/transitional/expected_config15
-rw-r--r--scripts/kconfig/tests/transitional/expected_stdout1
-rw-r--r--scripts/kconfig/tests/transitional/initial_config20
-rw-r--r--scripts/kconfig/util.c31
l---------scripts/kernel-doc2
-rwxr-xr-xscripts/kernel-doc.pl2439
-rwxr-xr-xscripts/kernel-doc.py325
-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.py1669
-rw-r--r--scripts/lib/kdoc/kdoc_re.py270
-rwxr-xr-xscripts/link-vmlinux.sh62
-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.sh10
-rwxr-xr-xscripts/misc-check4
-rwxr-xr-xscripts/mksysmap6
-rw-r--r--scripts/mod/devicetable-offsets.c4
-rw-r--r--scripts/mod/file2alias.c44
-rw-r--r--scripts/mod/modpost.c44
-rw-r--r--scripts/mod/modpost.h2
-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.rs9
-rwxr-xr-xscripts/selinux/install_policy.sh2
-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-pre-install1056
-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
193 files changed, 6987 insertions, 12371 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 8d56c0815f33..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,7 @@ 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
DTC_FLAGS += -Wunique_unit_address_if_enabled
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 b64862dc6f08..fcae1e432d9a 100644
--- a/scripts/Makefile.vmlinux
+++ b/scripts/Makefile.vmlinux
@@ -9,20 +9,6 @@ include $(srctree)/scripts/Makefile.lib
targets :=
-ifdef CONFIG_ARCH_VMLINUX_NEEDS_RELOCS
-vmlinux-final := vmlinux.unstripped
-
-quiet_cmd_strip_relocs = RSTRIP $@
- cmd_strip_relocs = $(OBJCOPY) --remove-section='.rel*' --remove-section=!'.rel*.dyn' $< $@
-
-vmlinux: $(vmlinux-final) FORCE
- $(call if_changed,strip_relocs)
-
-targets += vmlinux
-else
-vmlinux-final := vmlinux
-endif
-
%.o: %.c FORCE
$(call if_changed_rule,cc_o_c)
@@ -61,19 +47,14 @@ targets += .builtin-dtbs-list
ifdef CONFIG_GENERIC_BUILTIN_DTB
targets += .builtin-dtbs.S .builtin-dtbs.o
-$(vmlinux-final): .builtin-dtbs.o
+vmlinux.unstripped: .builtin-dtbs.o
endif
-# vmlinux
+# vmlinux.unstripped
# ---------------------------------------------------------------------------
-ifdef CONFIG_MODULES
-targets += .vmlinux.export.o
-$(vmlinux-final): .vmlinux.export.o
-endif
-
ifdef CONFIG_ARCH_WANTS_PRE_LINK_VMLINUX
-$(vmlinux-final): arch/$(SRCARCH)/tools/vmlinux.arch.o
+vmlinux.unstripped: arch/$(SRCARCH)/tools/vmlinux.arch.o
arch/$(SRCARCH)/tools/vmlinux.arch.o: vmlinux.o FORCE
$(Q)$(MAKE) $(build)=arch/$(SRCARCH)/tools $@
@@ -86,17 +67,78 @@ cmd_link_vmlinux = \
$< "$(LD)" "$(KBUILD_LDFLAGS)" "$(LDFLAGS_vmlinux)" "$@"; \
$(if $(ARCH_POSTLINK), $(MAKE) -f $(ARCH_POSTLINK) $@, true)
-targets += $(vmlinux-final)
-$(vmlinux-final): scripts/link-vmlinux.sh vmlinux.o $(KBUILD_LDS) FORCE
+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-final): $(RESOLVE_BTFIDS)
+vmlinux.unstripped: $(RESOLVE_BTFIDS) $(srctree)/scripts/gen-btf.sh
endif
ifdef CONFIG_BUILDTIME_TABLE_SORT
-$(vmlinux-final): scripts/sorttable
+vmlinux.unstripped: scripts/sorttable
endif
+# vmlinux
+# ---------------------------------------------------------------------------
+
+remove-section-y := .modinfo
+remove-section-$(CONFIG_ARCH_VMLINUX_NEEDS_RELOCS) += '.rel*' '!.rel*.dyn'
+# for compatibility with binutils < 2.32
+# https://sourceware.org/git/?p=binutils-gdb.git;a=commit;h=c12d9fa2afe7abcbe407a00e15719e1a1350c2a7
+remove-section-$(CONFIG_ARCH_VMLINUX_NEEDS_RELOCS) += '.rel.*'
+
+remove-symbols := -w --strip-unneeded-symbol='__mod_device_table__*'
+
+# To avoid warnings: "empty loadable segment detected at ..." from GNU objcopy,
+# it is necessary to remove the PT_LOAD flag from the segment.
+quiet_cmd_strip_relocs = OBJCOPY $@
+ cmd_strip_relocs = $(OBJCOPY) $(patsubst %,--set-section-flags %=noload,$(remove-section-y)) $< $@; \
+ $(OBJCOPY) $(addprefix --remove-section=,$(remove-section-y)) $(remove-symbols) $@
+
+targets += vmlinux
+vmlinux: vmlinux.unstripped FORCE
+ $(call if_changed,strip_relocs)
+
+# 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,modules_builtin_modinfo)
+
+# modules.builtin
+# ---------------------------------------------------------------------------
+
+__default: modules.builtin
+
+# The second line aids cases where multiple modules share the same object.
+
+quiet_cmd_modules_builtin = GEN $@
+ cmd_modules_builtin = \
+ tr '\0' '\n' < $< | \
+ sed -n 's/^[[:alnum:]:_]*\.file=//p' | \
+ tr ' ' '\n' | uniq | sed -e 's:^:kernel/:' -e 's/$$/.ko/' > $@
+
+targets += modules.builtin
+modules.builtin: modules.builtin.modinfo FORCE
+ $(call if_changed,modules_builtin)
+
# modules.builtin.ranges
# ---------------------------------------------------------------------------
ifdef CONFIG_BUILTIN_MODULE_RANGES
@@ -110,7 +152,7 @@ modules.builtin.ranges: $(srctree)/scripts/generate_builtin_ranges.awk \
modules.builtin vmlinux.map vmlinux.o.map FORCE
$(call if_changed,modules_builtin_ranges)
-vmlinux.map: $(vmlinux-final)
+vmlinux.map: vmlinux.unstripped
@:
endif
diff --git a/scripts/Makefile.vmlinux_o b/scripts/Makefile.vmlinux_o
index b024ffb3e201..527352c222ff 100644
--- a/scripts/Makefile.vmlinux_o
+++ b/scripts/Makefile.vmlinux_o
@@ -1,7 +1,7 @@
# SPDX-License-Identifier: GPL-2.0-only
PHONY := __default
-__default: vmlinux.o modules.builtin.modinfo modules.builtin
+__default: vmlinux.o
include include/config/auto.conf
include $(srctree)/scripts/Kbuild.include
@@ -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,40 +63,20 @@ 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)
targets += vmlinux.o
-# modules.builtin.modinfo
-# ---------------------------------------------------------------------------
-
-OBJCOPYFLAGS_modules.builtin.modinfo := -j .modinfo -O binary
-
-targets += modules.builtin.modinfo
-modules.builtin.modinfo: vmlinux.o FORCE
- $(call if_changed,objcopy)
-
-# modules.builtin
-# ---------------------------------------------------------------------------
-
-# The second line aids cases where multiple modules share the same object.
-
-quiet_cmd_modules_builtin = GEN $@
- cmd_modules_builtin = \
- tr '\0' '\n' < $< | \
- sed -n 's/^[[:alnum:]:_]*\.file=//p' | \
- tr ' ' '\n' | uniq | sed -e 's:^:kernel/:' -e 's/$$/.ko/' > $@
-
-targets += modules.builtin
-modules.builtin: modules.builtin.modinfo FORCE
- $(call if_changed,modules_builtin)
-
# Add FORCE to the prerequisites of a target to force it to be always rebuilt.
# ---------------------------------------------------------------------------
diff --git a/scripts/Makefile.extrawarn b/scripts/Makefile.warn
index dca175fffcab..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
@@ -25,14 +25,9 @@ ifneq ($(CONFIG_FRAME_WARN),0)
KBUILD_CFLAGS += -Wframe-larger-than=$(CONFIG_FRAME_WARN)
endif
-KBUILD_CPPFLAGS-$(CONFIG_WERROR) += -Werror
-KBUILD_CPPFLAGS += $(KBUILD_CPPFLAGS-y)
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)
@@ -55,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)
@@ -72,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)
@@ -99,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)
@@ -116,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
@@ -125,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)
@@ -145,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
@@ -166,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
@@ -174,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
@@ -214,10 +208,22 @@ KBUILD_CFLAGS += -Wno-unused-parameter
endif
#
-# W=e - error out on warnings
+# W=e and CONFIG_WERROR - error out on warnings
#
-ifneq ($(findstring e, $(KBUILD_EXTRA_WARN)),)
-
-KBUILD_CFLAGS += -Werror
+ifneq ($(findstring e, $(KBUILD_EXTRA_WARN))$(CONFIG_WERROR),)
+
+KBUILD_CPPFLAGS += -Werror
+KBUILD_AFLAGS += -Wa,--fatal-warnings
+KBUILD_LDFLAGS += --fatal-warnings
+KBUILD_USERCFLAGS += -Werror
+KBUILD_USERLDFLAGS += -Wl,--fatal-warnings
+KBUILD_RUSTFLAGS += -Dwarnings
+
+# While hostprog flags are used during build bootstrapping (thus should not
+# depend on CONFIG_ symbols), -Werror is disruptive and should be opted into.
+# Only apply -Werror to hostprogs built after the initial Kconfig stage.
+KBUILD_HOSTCFLAGS += -Werror
+KBUILD_HOSTLDFLAGS += -Wl,--fatal-warnings
+KBUILD_HOSTRUSTFLAGS += -Dwarnings
endif
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-atomics.sh b/scripts/atomic/gen-atomics.sh
index 5b98a8307693..02508d0d6fe4 100755
--- a/scripts/atomic/gen-atomics.sh
+++ b/scripts/atomic/gen-atomics.sh
@@ -11,6 +11,7 @@ cat <<EOF |
gen-atomic-instrumented.sh linux/atomic/atomic-instrumented.h
gen-atomic-long.sh linux/atomic/atomic-long.h
gen-atomic-fallback.sh linux/atomic/atomic-arch-fallback.h
+gen-rust-atomic-helpers.sh ../rust/helpers/atomic.c
EOF
while read script header args; do
/bin/sh ${ATOMICDIR}/${script} ${ATOMICTBL} ${args} > ${LINUXDIR}/include/${header}
diff --git a/scripts/atomic/gen-rust-atomic-helpers.sh b/scripts/atomic/gen-rust-atomic-helpers.sh
new file mode 100755
index 000000000000..a3732153af29
--- /dev/null
+++ b/scripts/atomic/gen-rust-atomic-helpers.sh
@@ -0,0 +1,62 @@
+#!/bin/sh
+# SPDX-License-Identifier: GPL-2.0
+
+ATOMICDIR=$(dirname $0)
+
+. ${ATOMICDIR}/atomic-tbl.sh
+
+#gen_proto_order_variant(meta, pfx, name, sfx, order, atomic, int, arg...)
+gen_proto_order_variant()
+{
+ local meta="$1"; shift
+ local pfx="$1"; shift
+ local name="$1"; shift
+ local sfx="$1"; shift
+ local order="$1"; shift
+ local atomic="$1"; shift
+ local int="$1"; shift
+
+ local atomicname="${atomic}_${pfx}${name}${sfx}${order}"
+
+ local ret="$(gen_ret_type "${meta}" "${int}")"
+ local params="$(gen_params "${int}" "${atomic}" "$@")"
+ local args="$(gen_args "$@")"
+ local retstmt="$(gen_ret_stmt "${meta}")"
+
+cat <<EOF
+__rust_helper ${ret}
+rust_helper_${atomicname}(${params})
+{
+ ${retstmt}${atomicname}(${args});
+}
+
+EOF
+}
+
+cat << EOF
+// SPDX-License-Identifier: GPL-2.0
+
+// Generated by $0
+// DO NOT MODIFY THIS FILE DIRECTLY
+
+/*
+ * This file provides helpers for the various atomic functions for Rust.
+ */
+#ifndef _RUST_ATOMIC_API_H
+#define _RUST_ATOMIC_API_H
+
+#include <linux/atomic.h>
+
+EOF
+
+grep '^[a-z]' "$1" | while read name meta args; do
+ gen_proto "${meta}" "${name}" "atomic" "int" ${args}
+done
+
+grep '^[a-z]' "$1" | while read name meta args; do
+ gen_proto "${meta}" "${name}" "atomic64" "s64" ${args}
+done
+
+cat <<EOF
+#endif /* _RUST_ATOMIC_API_H */
+EOF
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/bpf_doc.py b/scripts/bpf_doc.py
index c77dc40f7689..15d113a1bc1d 100755
--- a/scripts/bpf_doc.py
+++ b/scripts/bpf_doc.py
@@ -788,6 +788,7 @@ class PrinterHelpersHeader(Printer):
'struct task_struct',
'struct cgroup',
'struct path',
+ 'const struct path',
'struct btf_ptr',
'struct inode',
'struct socket',
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 e722dd6fa8ef..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 = (
@@ -2636,6 +2645,11 @@ sub exclude_global_initialisers {
$realfile =~ m@/bpf/.*\.bpf\.c$@;
}
+sub is_userspace {
+ my ($realfile) = @_;
+ return ($realfile =~ m@^tools/@ || $realfile =~ m@^scripts/@);
+}
+
sub process {
my $filename = shift;
@@ -2915,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/)@;
@@ -3022,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;
@@ -3082,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 "") {
@@ -3294,7 +3327,7 @@ sub process {
# file delta changes
$line =~ /^\s*(?:[\w\.\-\+]*\/)++[\w\.\-\+]+:/ ||
# filename then :
- $line =~ /^\s*(?:Fixes:|$link_tags_search|$signature_tags)/i ||
+ $line =~ /^\s*(?:Fixes:|https?:|$link_tags_search|$signature_tags)/i ||
# A Fixes:, link or signature tag line
$commit_log_possible_stack_dump)) {
WARN("COMMIT_LOG_LONG_LINE",
@@ -3340,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")
@@ -3826,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:
@@ -6717,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",
@@ -7018,21 +7073,20 @@ sub process {
# }
# }
# }
-
# strcpy uses that should likely be strscpy
- if ($line =~ /\bstrcpy\s*\(/) {
+ if ($line =~ /\bstrcpy\s*\(/ && !is_userspace($realfile)) {
WARN("STRCPY",
"Prefer strscpy over strcpy - see: https://github.com/KSPP/linux/issues/88\n" . $herecurr);
}
# strlcpy uses that should likely be strscpy
- if ($line =~ /\bstrlcpy\s*\(/) {
+ if ($line =~ /\bstrlcpy\s*\(/ && !is_userspace($realfile)) {
WARN("STRLCPY",
"Prefer strscpy over strlcpy - see: https://github.com/KSPP/linux/issues/89\n" . $herecurr);
}
# strncpy uses that should likely be strscpy or strscpy_pad
- if ($line =~ /\bstrncpy\s*\(/) {
+ if ($line =~ /\bstrncpy\s*\(/ && !is_userspace($realfile)) {
WARN("STRNCPY",
"Prefer strscpy, strscpy_pad, or __nonstring over strncpy - see: https://github.com/KSPP/linux/issues/90\n" . $herecurr);
}
@@ -7243,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/) {
@@ -7269,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;
}
}
}
@@ -7439,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);
}
@@ -7717,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/platform_no_drv_owner.cocci b/scripts/coccinelle/api/platform_no_drv_owner.cocci
index 8fa050eeb7e5..5e869858bda8 100644
--- a/scripts/coccinelle/api/platform_no_drv_owner.cocci
+++ b/scripts/coccinelle/api/platform_no_drv_owner.cocci
@@ -10,12 +10,21 @@ virtual org
virtual report
@match1@
+declarer name builtin_i2c_driver;
+declarer name builtin_platform_driver;
+declarer name builtin_platform_driver_probe;
declarer name module_i2c_driver;
declarer name module_platform_driver;
declarer name module_platform_driver_probe;
identifier __driver;
@@
(
+ builtin_i2c_driver(__driver);
+|
+ builtin_platform_driver(__driver);
+|
+ builtin_platform_driver_probe(__driver, ...);
+|
module_i2c_driver(__driver);
|
module_platform_driver(__driver);
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/coccinelle/misc/of_table.cocci b/scripts/coccinelle/misc/of_table.cocci
index 4693ea744753..17881cb0884b 100644
--- a/scripts/coccinelle/misc/of_table.cocci
+++ b/scripts/coccinelle/misc/of_table.cocci
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: GPL-2.0
-/// Make sure (of/i2c/platform)_device_id tables are NULL terminated
+/// Make sure (of/i2c/platform/spi)_device_id tables are NULL terminated
//
// Keywords: of_table i2c_table platform_table
// Confidence: Medium
@@ -15,14 +15,14 @@ identifier var, arr;
expression E;
@@
(
-struct \(of_device_id \| i2c_device_id \| platform_device_id\) arr[] = {
+struct \(of_device_id \| i2c_device_id \| platform_device_id \| spi_device_id\) arr[] = {
...,
{
.var = E,
* }
};
|
-struct \(of_device_id \| i2c_device_id \| platform_device_id\) arr[] = {
+struct \(of_device_id \| i2c_device_id \| platform_device_id \| spi_device_id\) arr[] = {
...,
* { ..., E, ... },
};
@@ -33,7 +33,7 @@ identifier var, arr;
expression E;
@@
(
-struct \(of_device_id \| i2c_device_id \| platform_device_id\) arr[] = {
+struct \(of_device_id \| i2c_device_id \| platform_device_id \| spi_device_id\) arr[] = {
...,
{
.var = E,
@@ -42,7 +42,7 @@ struct \(of_device_id \| i2c_device_id \| platform_device_id\) arr[] = {
+ { }
};
|
-struct \(of_device_id \| i2c_device_id \| platform_device_id\) arr[] = {
+struct \(of_device_id \| i2c_device_id \| platform_device_id \| spi_device_id\) arr[] = {
...,
{ ..., E, ... },
+ { },
@@ -55,7 +55,7 @@ identifier var, arr;
expression E;
@@
(
-struct \(of_device_id \| i2c_device_id \| platform_device_id\) arr[] = {
+struct \(of_device_id \| i2c_device_id \| platform_device_id \| spi_device_id\) arr[] = {
...,
{
.var = E,
@@ -63,7 +63,7 @@ struct \(of_device_id \| i2c_device_id \| platform_device_id\) arr[] = {
@p1
};
|
-struct \(of_device_id \| i2c_device_id \| platform_device_id\) arr[] = {
+struct \(of_device_id \| i2c_device_id \| platform_device_id \| spi_device_id\) arr[] = {
...,
{ ..., E, ... }
@p1
diff --git a/scripts/coccinelle/misc/ptr_err_to_pe.cocci b/scripts/coccinelle/misc/ptr_err_to_pe.cocci
new file mode 100644
index 000000000000..0494c7709245
--- /dev/null
+++ b/scripts/coccinelle/misc/ptr_err_to_pe.cocci
@@ -0,0 +1,34 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/// Use %pe format specifier instead of PTR_ERR() for printing error pointers.
+///
+/// For printing error pointers (i.e., a pointer for which IS_ERR() is true)
+/// %pe will print a symbolic error name (e.g., -EINVAL), opposed to the raw
+/// errno (e.g., -22) produced by PTR_ERR().
+/// It also makes the code cleaner by saving a redundant call to PTR_ERR().
+///
+// Confidence: High
+// Copyright: (C) 2025 NVIDIA CORPORATION & AFFILIATES.
+// URL: https://coccinelle.gitlabpages.inria.fr/website
+// Options: --no-includes --include-headers
+
+virtual context
+virtual org
+virtual report
+
+@r@
+expression ptr;
+constant fmt;
+position p;
+identifier print_func;
+@@
+* print_func(..., fmt, ..., PTR_ERR@p(ptr), ...)
+
+@script:python depends on r && report@
+p << r.p;
+@@
+coccilib.report.print_report(p[0], "WARNING: Consider using %pe to print PTR_ERR()")
+
+@script:python depends on r && org@
+p << r.p;
+@@
+coccilib.org.print_todo(p[0], "WARNING: Consider using %pe to print PTR_ERR()")
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 4ac927d40cf5..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):
@@ -84,11 +184,18 @@ def print_c_struct_u8_array_field(name, value):
print_bytes('\t\t\t', value, 8)
print('\t\t},')
+def alg_digest_size_const(alg):
+ 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('')
print('static const struct {')
print('\tsize_t data_len;')
- print(f'\tu8 digest[{alg.upper()}_DIGEST_SIZE];')
+ print(f'\tu8 digest[{alg_digest_size_const(alg)}];')
print('} hash_testvecs[] = {')
for data_len in DATA_LENS:
data = rand_bytes(data_len)
@@ -103,9 +210,21 @@ def gen_unkeyed_testvecs(alg):
for data_len in range(len(data) + 1):
hash_update(ctx, compute_hash(alg, data[:data_len]))
print_static_u8_array_definition(
- f'hash_testvec_consolidated[{alg.upper()}_DIGEST_SIZE]',
+ 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)
@@ -119,6 +238,61 @@ def gen_hmac_testvecs(alg):
f'hmac_testvec_consolidated[{alg.upper()}_DIGEST_SIZE]',
ctx.digest())
+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(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(
+ 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
data = b''
@@ -131,17 +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 == 'poly1305':
+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 17abc4e7a985..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
@@ -242,8 +260,10 @@ debuginfod_get_vmlinux() {
decode_code() {
local scripts=`dirname "${BASH_SOURCE[0]}"`
+ local lim="Code: "
- echo "$1" | $scripts/decodecode
+ echo -n "${1%%${lim}*}"
+ echo "${lim}${1##*${lim}}" | $scripts/decodecode
}
handle_line() {
@@ -255,10 +275,11 @@ handle_line() {
basepath=${basepath%/init/main.c:*)}
fi
- local words
+ local words spaces
- # Tokenize
- read -a words <<<"$1"
+ # Tokenize: words and spaces to preserve the alignment
+ read -ra words <<<"$1"
+ IFS='#' read -ra spaces <<<"$(shopt -s extglob; echo "${1//+([^[:space:]])/#}")"
# Remove hex numbers. Do it ourselves until it happens in the
# kernel
@@ -270,22 +291,10 @@ handle_line() {
for i in "${!words[@]}"; do
# Remove the address
if [[ ${words[$i]} =~ \[\<([^]]+)\>\] ]]; then
- unset words[$i]
- fi
-
- # Format timestamps with tabs
- if [[ ${words[$i]} == \[ && ${words[$i+1]} == *\] ]]; then
- unset words[$i]
- words[$i+1]=$(printf "[%13s\n" "${words[$i+1]}")
+ unset words[$i] spaces[$i]
fi
done
- if [[ ${words[$last]} =~ ^[0-9a-f]+\] ]]; then
- words[$last-1]="${words[$last-1]} ${words[$last]}"
- unset words[$last]
- last=$(( $last - 1 ))
- fi
-
# Extract info after the symbol if present. E.g.:
# func_name+0x54/0x80 (P)
# ^^^
@@ -294,7 +303,15 @@ handle_line() {
local info_str=""
if [[ ${words[$last]} =~ \([A-Z]*\) ]]; then
info_str=${words[$last]}
- unset words[$last]
+ unset words[$last] spaces[$last]
+ 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
@@ -311,7 +328,7 @@ handle_line() {
modbuildid=
fi
symbol=${words[$last-1]}
- unset words[$last-1]
+ unset words[$last-1] spaces[$last-1]
else
# The symbol is the last element, process it
symbol=${words[$last]}
@@ -323,12 +340,10 @@ handle_line() {
parse_symbol # modifies $symbol
# Add up the line number to the symbol
- if [[ -z ${module} ]]
- then
- echo "${words[@]}" "$symbol ${info_str}"
- else
- echo "${words[@]}" "$symbol $module ${info_str}"
- fi
+ for i in "${!words[@]}"; do
+ echo -n "${spaces[i]}${words[i]}"
+ done
+ echo "${spaces[$last]}${symbol}${module:+ ${module}}${info_str:+ ${info_str}}"
}
while read line; do
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 6e06aeab5503..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 '-'");
}
@@ -1024,7 +1035,7 @@ static void check_i2c_bus_bridge(struct check *c, struct dt_info *dti, struct no
} else if (strprefixeq(node->name, node->basenamelen, "i2c")) {
struct node *child;
for_each_child(node, child) {
- if (strprefixeq(child->name, node->basenamelen, "i2c-bus"))
+ if (strprefixeq(child->name, child->basenamelen, "i2c-bus"))
return;
}
node->bus = &i2c_bus;
@@ -1217,9 +1228,7 @@ WARNING(avoid_default_addr_size, check_avoid_default_addr_size, NULL,
static void check_avoid_unnecessary_addr_size(struct check *c, struct dt_info *dti,
struct node *node)
{
- struct property *prop;
struct node *child;
- bool has_reg = false;
if (!node->parent || node->addr_cells < 0 || node->size_cells < 0)
return;
@@ -1228,13 +1237,18 @@ static void check_avoid_unnecessary_addr_size(struct check *c, struct dt_info *d
return;
for_each_child(node, child) {
- prop = get_property(child, "reg");
- if (prop)
- has_reg = true;
+ /*
+ * Even if the child devices' address space is not mapped into
+ * the parent bus (no 'ranges' property on node), children can
+ * still have registers on a local bus, or map local addresses
+ * to another subordinate address space. The properties on the
+ * child nodes then make #address-cells/#size-cells necessary:
+ */
+ if (get_property(child, "reg") || get_property(child, "ranges"))
+ return;
}
- if (!has_reg)
- FAIL(c, dti, node, "unnecessary #address-cells/#size-cells without \"ranges\", \"dma-ranges\" or child \"reg\" property");
+ FAIL(c, dti, node, "unnecessary #address-cells/#size-cells without \"ranges\", \"dma-ranges\" or child \"reg\" or \"ranges\" property");
}
WARNING(avoid_unnecessary_addr_size, check_avoid_unnecessary_addr_size, NULL, &avoid_default_addr_size);
@@ -1673,6 +1687,10 @@ static void check_interrupt_map(struct check *c,
cellprop = get_property(provider_node, "#address-cells");
if (cellprop)
parent_cellsize += propval_cell(cellprop);
+ else
+ FAIL_PROP(c, dti, node, irq_map_prop,
+ "Missing property '#address-cells' in node %s, using 0 as fallback",
+ provider_node->fullpath);
cell += 1 + parent_cellsize;
if (cell > map_cells)
@@ -1887,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,
@@ -1998,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/data.c b/scripts/dtc/data.c
index 14734233ad8b..5b25aa060416 100644
--- a/scripts/dtc/data.c
+++ b/scripts/dtc/data.c
@@ -228,11 +228,7 @@ struct data data_add_marker(struct data d, enum markertype type, char *ref)
{
struct marker *m;
- m = xmalloc(sizeof(*m));
- m->offset = d.len;
- m->type = type;
- m->ref = ref;
- m->next = NULL;
+ m = alloc_marker(d.len, type, ref);
return data_append_markers(d, m);
}
@@ -254,3 +250,44 @@ bool data_is_one_string(struct data d)
return true;
}
+
+struct data data_insert_data(struct data d, struct marker *m, struct data old)
+{
+ unsigned int offset = m->offset;
+ struct marker *next = m->next;
+ struct marker *marker;
+ struct data new_data;
+ char *ref;
+
+ new_data = data_insert_at_marker(d, m, old.val, old.len);
+
+ /* Copy all markers from old value */
+ marker = old.markers;
+ for_each_marker(marker) {
+ ref = NULL;
+
+ if (marker->ref)
+ ref = xstrdup(marker->ref);
+
+ m->next = alloc_marker(marker->offset + offset, marker->type,
+ ref);
+ m = m->next;
+ }
+ m->next = next;
+
+ return new_data;
+}
+
+struct marker *alloc_marker(unsigned int offset, enum markertype type,
+ char *ref)
+{
+ struct marker *m;
+
+ m = xmalloc(sizeof(*m));
+ m->offset = offset;
+ m->type = type;
+ m->ref = ref;
+ m->next = NULL;
+
+ return m;
+}
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/dt_to_config b/scripts/dtc/dt_to_config
index 299d1c2b20d7..70d6d5f06bdc 100755
--- a/scripts/dtc/dt_to_config
+++ b/scripts/dtc/dt_to_config
@@ -51,10 +51,10 @@ $num_pr_flags = $pr_flag_pos_config_test_fail + 1;
"compatible is white listed",
"matching driver and/or kernel config is hard coded",
"kernel config hard coded in Makefile",
- "one or more kernel config file options is not set",
- "one or more kernel config file options is set to 'm'",
- "one or more kernel config file options is set to 'y'",
- "one of more kernel config file options fails to have correct value"
+ "one or more kernel config file options are not set",
+ "one or more kernel config file options are set to 'm'",
+ "one or more kernel config file options are set to 'y'",
+ "one or more kernel config file options fail to have correct value"
);
diff --git a/scripts/dtc/dtc-lexer.l b/scripts/dtc/dtc-lexer.l
index de60a70b6bdb..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;
}
@@ -151,6 +148,21 @@ static void PRINTF(1, 2) lexical_error(const char *fmt, ...);
return DT_LABEL;
}
+<V1>{LABEL} {
+ /* Missed includes or macro definitions while
+ * preprocessing can lead to unexpected identifiers in
+ * the input. Report a slightly more informative error
+ * in this case */
+
+ lexical_error("Unexpected '%s'", yytext);
+
+ /* Treat it as a literal which often generates further
+ * useful error messages */
+
+ yylval.integer = 0;
+ return DT_LITERAL;
+ }
+
<V1>([0-9]+|0[xX][0-9a-fA-F]+)(U|L|UL|LL|ULL)? {
char *e;
DPRINT("Integer Literal: '%s'\n", yytext);
diff --git a/scripts/dtc/dtc.c b/scripts/dtc/dtc.c
index 0655c2e2c362..6dae60de0ea5 100644
--- a/scripts/dtc/dtc.c
+++ b/scripts/dtc/dtc.c
@@ -15,7 +15,7 @@ int quiet; /* Level of quietness */
unsigned int reservenum;/* Number of memory reservation slots */
int minsize; /* Minimum blob size */
int padsize; /* Additional padding to blob */
-int alignsize; /* Additional padding to blob accroding to the alignsize */
+int alignsize; /* Additional padding to blob according to the alignsize */
int phandle_format = PHANDLE_EPAPR; /* Use linux,phandle or phandle properties */
int generate_symbols; /* enable symbols & fixup support */
int generate_fixups; /* suppress generation of fixups on symbol support */
@@ -289,7 +289,9 @@ int main(int argc, char *argv[])
if (!depfile)
die("Couldn't open dependency file %s: %s\n", depname,
strerror(errno));
- fprintf(depfile, "%s:", outname);
+
+ fprint_path_escaped(depfile, outname);
+ fputc(':', depfile);
}
if (inform == NULL)
@@ -336,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 4c4aaca1fc41..473552ebf017 100644
--- a/scripts/dtc/dtc.h
+++ b/scripts/dtc/dtc.h
@@ -38,7 +38,7 @@ extern int quiet; /* Level of quietness */
extern unsigned int reservenum; /* Number of memory reservation slots */
extern int minsize; /* Minimum blob size */
extern int padsize; /* Additional padding to blob */
-extern int alignsize; /* Additional padding to blob accroding to the alignsize */
+extern int alignsize; /* Additional padding to blob according to the alignsize */
extern int phandle_format; /* Use linux,phandle or phandle properties */
extern int generate_symbols; /* generate symbols for nodes with labels */
extern int generate_fixups; /* generate fixups */
@@ -182,7 +182,10 @@ struct data data_append_addr(struct data d, uint64_t addr);
struct data data_append_byte(struct data d, uint8_t byte);
struct data data_append_zeroes(struct data d, int len);
struct data data_append_align(struct data d, int align);
+struct data data_insert_data(struct data d, struct marker *m, struct data old);
+struct marker *alloc_marker(unsigned int offset, enum markertype type,
+ char *ref);
struct data data_add_marker(struct data d, enum markertype type, char *ref);
bool data_is_one_string(struct data d);
@@ -224,7 +227,7 @@ struct node {
struct node *next_sibling;
char *fullpath;
- int basenamelen;
+ size_t basenamelen;
cell_t phandle;
int addr_cells, size_cells;
@@ -336,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 */
@@ -354,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/fdtoverlay.c b/scripts/dtc/fdtoverlay.c
index 699b4f616502..ee1eb8f3ad28 100644
--- a/scripts/dtc/fdtoverlay.c
+++ b/scripts/dtc/fdtoverlay.c
@@ -46,6 +46,7 @@ static void *apply_one(char *base, const char *overlay, size_t *buf_len,
char *tmp = NULL;
char *tmpo;
int ret;
+ bool has_symbols;
/*
* We take copies first, because a failed apply can trash
@@ -62,6 +63,8 @@ static void *apply_one(char *base, const char *overlay, size_t *buf_len,
fdt_strerror(ret));
goto fail;
}
+ ret = fdt_path_offset(tmp, "/__symbols__");
+ has_symbols = ret >= 0;
memcpy(tmpo, overlay, fdt_totalsize(overlay));
@@ -74,6 +77,11 @@ static void *apply_one(char *base, const char *overlay, size_t *buf_len,
if (ret) {
fprintf(stderr, "\nFailed to apply '%s': %s\n",
name, fdt_strerror(ret));
+ if (!has_symbols) {
+ fprintf(stderr,
+ "base blob does not have a '/__symbols__' node, "
+ "make sure you have compiled the base blob with '-@' option\n");
+ }
goto fail;
}
diff --git a/scripts/dtc/flattree.c b/scripts/dtc/flattree.c
index 1bcd8089c5b9..f3b698c17e89 100644
--- a/scripts/dtc/flattree.c
+++ b/scripts/dtc/flattree.c
@@ -503,7 +503,7 @@ void dt_to_asm(FILE *f, struct dt_info *dti, int version)
* Reserve map entries.
* Align the reserve map to a doubleword boundary.
* Each entry is an (address, size) pair of u64 values.
- * Always supply a zero-sized temination entry.
+ * Always supply a zero-sized termination entry.
*/
asm_emit_align(f, 8);
emit_label(f, symprefix, "reserve_map");
@@ -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 20c6415b9ced..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)))
@@ -312,14 +320,14 @@ int fdt_next_subnode(const void *fdt, int offset)
return offset;
}
-const char *fdt_find_string_(const char *strtab, int tabsize, const char *s)
+const char *fdt_find_string_len_(const char *strtab, int tabsize, const char *s,
+ int slen)
{
- int len = strlen(s) + 1;
- const char *last = strtab + tabsize - len;
+ const char *last = strtab + tabsize - (slen + 1);
const char *p;
for (p = strtab; p <= last; p++)
- if (memcmp(p, s, len) == 0)
+ if (memcmp(p, s, slen) == 0 && p[slen] == '\0')
return p;
return NULL;
}
diff --git a/scripts/dtc/libfdt/fdt.h b/scripts/dtc/libfdt/fdt.h
index 0c91aa7f67b5..a07abfcc7108 100644
--- a/scripts/dtc/libfdt/fdt.h
+++ b/scripts/dtc/libfdt/fdt.h
@@ -7,7 +7,7 @@
* Copyright 2012 Kim Phillips, Freescale Semiconductor.
*/
-#ifndef __ASSEMBLY__
+#ifndef __ASSEMBLER__
struct fdt_header {
fdt32_t magic; /* magic word FDT_MAGIC */
@@ -45,7 +45,7 @@ struct fdt_property {
char data[];
};
-#endif /* !__ASSEMBLY */
+#endif /* !__ASSEMBLER__ */
#define FDT_MAGIC 0xd00dfeed /* 4: version, 4: total size */
#define FDT_TAGSIZE sizeof(fdt32_t)
diff --git a/scripts/dtc/libfdt/fdt_overlay.c b/scripts/dtc/libfdt/fdt_overlay.c
index 28b667ffc490..51a3859620a4 100644
--- a/scripts/dtc/libfdt/fdt_overlay.c
+++ b/scripts/dtc/libfdt/fdt_overlay.c
@@ -307,7 +307,6 @@ static int overlay_update_local_references(void *fdto, uint32_t delta)
/**
* overlay_fixup_one_phandle - Set an overlay phandle to the base one
- * @fdt: Base Device Tree blob
* @fdto: Device tree overlay blob
* @symbols_off: Node offset of the symbols node in the base device tree
* @path: Path to a node holding a phandle in the overlay
@@ -328,8 +327,7 @@ static int overlay_update_local_references(void *fdto, uint32_t delta)
* 0 on success
* Negative error code on failure
*/
-static int overlay_fixup_one_phandle(void *fdt, void *fdto,
- int symbols_off,
+static int overlay_fixup_one_phandle(void *fdto, int symbols_off,
const char *path, uint32_t path_len,
const char *name, uint32_t name_len,
int poffset, uint32_t phandle)
@@ -351,7 +349,7 @@ static int overlay_fixup_one_phandle(void *fdt, void *fdto,
name, name_len, poffset,
&phandle_prop,
sizeof(phandle_prop));
-};
+}
/**
* overlay_fixup_phandle - Set an overlay phandle to the base one
@@ -409,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);
@@ -443,7 +442,7 @@ static int overlay_fixup_phandle(void *fdt, void *fdto, int symbols_off,
if ((*endptr != '\0') || (endptr <= (sep + 1)))
return -FDT_ERR_BADOVERLAY;
- ret = overlay_fixup_one_phandle(fdt, fdto, symbols_off,
+ ret = overlay_fixup_one_phandle(fdto, symbols_off,
path, path_len, name, name_len,
poffset, phandle);
if (ret)
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 3621d3651d3f..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;
}
@@ -124,31 +129,33 @@ static int fdt_splice_string_(void *fdt, int newlen)
* allocated. Ignored if can_assume(NO_ROLLBACK)
* @return offset of string in the string table (whether found or added)
*/
-static int fdt_find_add_string_(void *fdt, const char *s, int *allocated)
+static int fdt_find_add_string_(void *fdt, const char *s, int slen,
+ int *allocated)
{
char *strtab = (char *)fdt + fdt_off_dt_strings(fdt);
const char *p;
char *new;
- int len = strlen(s) + 1;
int err;
if (!can_assume(NO_ROLLBACK))
*allocated = 0;
- p = fdt_find_string_(strtab, fdt_size_dt_strings(fdt), s);
+ p = fdt_find_string_len_(strtab, fdt_size_dt_strings(fdt), s, slen);
if (p)
/* found it */
return (p - strtab);
new = strtab + fdt_size_dt_strings(fdt);
- err = fdt_splice_string_(fdt, len);
+ err = fdt_splice_string_(fdt, slen + 1);
if (err)
return err;
if (!can_assume(NO_ROLLBACK))
*allocated = 1;
- memcpy(new, s, len);
+ memcpy(new, s, slen);
+ new[slen] = '\0';
+
return (new - strtab);
}
@@ -181,13 +188,15 @@ int fdt_del_mem_rsv(void *fdt, int n)
return fdt_splice_mem_rsv_(fdt, re, 1, 0);
}
-static int fdt_resize_property_(void *fdt, int nodeoffset, const char *name,
+static int fdt_resize_property_(void *fdt, int nodeoffset,
+ const char *name, int namelen,
int len, struct fdt_property **prop)
{
int oldlen;
int err;
- *prop = fdt_get_property_w(fdt, nodeoffset, name, &oldlen);
+ *prop = fdt_get_property_namelen_w(fdt, nodeoffset, name, namelen,
+ &oldlen);
if (!*prop)
return oldlen;
@@ -200,7 +209,7 @@ static int fdt_resize_property_(void *fdt, int nodeoffset, const char *name,
}
static int fdt_add_property_(void *fdt, int nodeoffset, const char *name,
- int len, struct fdt_property **prop)
+ int namelen, int len, struct fdt_property **prop)
{
int proplen;
int nextoffset;
@@ -211,7 +220,7 @@ static int fdt_add_property_(void *fdt, int nodeoffset, const char *name,
if ((nextoffset = fdt_check_node_offset_(fdt, nodeoffset)) < 0)
return nextoffset;
- namestroff = fdt_find_add_string_(fdt, name, &allocated);
+ namestroff = fdt_find_add_string_(fdt, name, namelen, &allocated);
if (namestroff < 0)
return namestroff;
@@ -255,17 +264,18 @@ int fdt_set_name(void *fdt, int nodeoffset, const char *name)
return 0;
}
-int fdt_setprop_placeholder(void *fdt, int nodeoffset, const char *name,
- int len, void **prop_data)
+int fdt_setprop_placeholder_namelen(void *fdt, int nodeoffset, const char *name,
+ int namelen, int len, void **prop_data)
{
struct fdt_property *prop;
int err;
FDT_RW_PROBE(fdt);
- err = fdt_resize_property_(fdt, nodeoffset, name, len, &prop);
+ err = fdt_resize_property_(fdt, nodeoffset, name, namelen, len, &prop);
if (err == -FDT_ERR_NOTFOUND)
- err = fdt_add_property_(fdt, nodeoffset, name, len, &prop);
+ err = fdt_add_property_(fdt, nodeoffset, name, namelen, len,
+ &prop);
if (err)
return err;
@@ -273,13 +283,14 @@ int fdt_setprop_placeholder(void *fdt, int nodeoffset, const char *name,
return 0;
}
-int fdt_setprop(void *fdt, int nodeoffset, const char *name,
- const void *val, int len)
+int fdt_setprop_namelen(void *fdt, int nodeoffset, const char *name,
+ int namelen, const void *val, int len)
{
void *prop_data;
int err;
- err = fdt_setprop_placeholder(fdt, nodeoffset, name, len, &prop_data);
+ err = fdt_setprop_placeholder_namelen(fdt, nodeoffset, name, namelen,
+ len, &prop_data);
if (err)
return err;
@@ -307,7 +318,8 @@ int fdt_appendprop(void *fdt, int nodeoffset, const char *name,
prop->len = cpu_to_fdt32(newlen);
memcpy(prop->data + oldlen, val, len);
} else {
- err = fdt_add_property_(fdt, nodeoffset, name, len, &prop);
+ err = fdt_add_property_(fdt, nodeoffset, name, strlen(name),
+ len, &prop);
if (err)
return err;
memcpy(prop->data, val, len);
diff --git a/scripts/dtc/libfdt/libfdt.h b/scripts/dtc/libfdt/libfdt.h
index 2d409d8e829b..81aaf7cbae13 100644
--- a/scripts/dtc/libfdt/libfdt.h
+++ b/scripts/dtc/libfdt/libfdt.h
@@ -14,7 +14,7 @@ extern "C" {
#endif
#define FDT_FIRST_SUPPORTED_VERSION 0x02
-#define FDT_LAST_COMPATIBLE_VERSION 0x10
+#define FDT_LAST_COMPATIBLE_VERSION 0x10
#define FDT_LAST_SUPPORTED_VERSION 0x11
/* Error codes: informative error codes */
@@ -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);
/*
@@ -263,16 +291,16 @@ int fdt_next_subnode(const void *fdt, int offset);
struct fdt_header *fdth = (struct fdt_header *)fdt; \
fdth->name = cpu_to_fdt32(val); \
}
-fdt_set_hdr_(magic);
-fdt_set_hdr_(totalsize);
-fdt_set_hdr_(off_dt_struct);
-fdt_set_hdr_(off_dt_strings);
-fdt_set_hdr_(off_mem_rsvmap);
-fdt_set_hdr_(version);
-fdt_set_hdr_(last_comp_version);
-fdt_set_hdr_(boot_cpuid_phys);
-fdt_set_hdr_(size_dt_strings);
-fdt_set_hdr_(size_dt_struct);
+fdt_set_hdr_(magic)
+fdt_set_hdr_(totalsize)
+fdt_set_hdr_(off_dt_struct)
+fdt_set_hdr_(off_dt_strings)
+fdt_set_hdr_(off_mem_rsvmap)
+fdt_set_hdr_(version)
+fdt_set_hdr_(last_comp_version)
+fdt_set_hdr_(boot_cpuid_phys)
+fdt_set_hdr_(size_dt_strings)
+fdt_set_hdr_(size_dt_struct)
#undef fdt_set_hdr_
/**
@@ -285,7 +313,7 @@ size_t fdt_header_size(const void *fdt);
/**
* fdt_header_size_ - internal function to get header size from a version number
- * @version: devicetree version number
+ * @version: device tree version number
*
* Return: size of DTB header in bytes
*/
@@ -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);
/**
@@ -554,7 +599,7 @@ int fdt_path_offset_namelen(const void *fdt, const char *path, int namelen);
* -FDT_ERR_BADPATH, given path does not begin with '/' and the first
* component is not a valid alias
* -FDT_ERR_NOTFOUND, if the requested node does not exist
- * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADMAGIC,
* -FDT_ERR_BADVERSION,
* -FDT_ERR_BADSTATE,
* -FDT_ERR_BADSTRUCTURE,
@@ -599,7 +644,7 @@ const char *fdt_get_name(const void *fdt, int nodeoffset, int *lenp);
* structure block offset of the property (>=0), on success
* -FDT_ERR_NOTFOUND, if the requested node has no properties
* -FDT_ERR_BADOFFSET, if nodeoffset did not point to an FDT_BEGIN_NODE tag
- * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADMAGIC,
* -FDT_ERR_BADVERSION,
* -FDT_ERR_BADSTATE,
* -FDT_ERR_BADSTRUCTURE,
@@ -620,7 +665,7 @@ int fdt_first_property_offset(const void *fdt, int nodeoffset);
* structure block offset of the next property (>=0), on success
* -FDT_ERR_NOTFOUND, if the given property is the last in its node
* -FDT_ERR_BADOFFSET, if nodeoffset did not point to an FDT_PROP tag
- * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADMAGIC,
* -FDT_ERR_BADVERSION,
* -FDT_ERR_BADSTATE,
* -FDT_ERR_BADSTRUCTURE,
@@ -712,6 +757,13 @@ const struct fdt_property *fdt_get_property_namelen(const void *fdt,
int nodeoffset,
const char *name,
int namelen, int *lenp);
+static inline struct fdt_property *
+fdt_get_property_namelen_w(void *fdt, int nodeoffset, const char *name,
+ int namelen, int *lenp)
+{
+ return (struct fdt_property *)(uintptr_t)fdt_get_property_namelen(
+ fdt, nodeoffset, name, namelen, lenp);
+}
#endif
/**
@@ -764,7 +816,7 @@ static inline struct fdt_property *fdt_get_property_w(void *fdt, int nodeoffset,
* to within the device blob itself, not a copy of the value). If
* lenp is non-NULL, the length of the property value is also
* returned, in the integer pointed to by lenp. If namep is non-NULL,
- * the property's namne will also be returned in the char * pointed to
+ * the property's name will also be returned in the char * pointed to
* by namep (this will be a pointer to within the device tree's string
* block, not a new copy of the name).
*
@@ -772,7 +824,7 @@ static inline struct fdt_property *fdt_get_property_w(void *fdt, int nodeoffset,
* pointer to the property's value
* if lenp is non-NULL, *lenp contains the length of the property
* value (>=0)
- * if namep is non-NULL *namep contiains a pointer to the property
+ * if namep is non-NULL *namep contains a pointer to the property
* name.
* NULL, on error
* if lenp is non-NULL, *lenp contains an error code (<0):
@@ -866,7 +918,7 @@ uint32_t fdt_get_phandle(const void *fdt, int nodeoffset);
/**
* fdt_get_alias_namelen - get alias based on substring
* @fdt: pointer to the device tree blob
- * @name: name of the alias th look up
+ * @name: name of the alias to look up
* @namelen: number of characters of name to consider
*
* Identical to fdt_get_alias(), but only examine the first @namelen
@@ -883,7 +935,7 @@ const char *fdt_get_alias_namelen(const void *fdt,
/**
* fdt_get_alias - retrieve the path referenced by a given alias
* @fdt: pointer to the device tree blob
- * @name: name of the alias th look up
+ * @name: name of the alias to look up
*
* fdt_get_alias() retrieves the value of a given alias. That is, the
* value of the property named @name in the node /aliases.
@@ -1259,8 +1311,8 @@ const char *fdt_stringlist_get(const void *fdt, int nodeoffset,
*
* returns:
* 0 <= n < FDT_MAX_NCELLS, on success
- * 2, if the node has no #address-cells property
- * -FDT_ERR_BADNCELLS, if the node has a badly formatted or invalid
+ * 2, if the node has no #address-cells property
+ * -FDT_ERR_BADNCELLS, if the node has a badly formatted or invalid
* #address-cells property
* -FDT_ERR_BADMAGIC,
* -FDT_ERR_BADVERSION,
@@ -1280,8 +1332,8 @@ int fdt_address_cells(const void *fdt, int nodeoffset);
*
* returns:
* 0 <= n < FDT_MAX_NCELLS, on success
- * 1, if the node has no #size-cells property
- * -FDT_ERR_BADNCELLS, if the node has a badly formatted or invalid
+ * 1, if the node has no #size-cells property
+ * -FDT_ERR_BADNCELLS, if the node has a badly formatted or invalid
* #size-cells property
* -FDT_ERR_BADMAGIC,
* -FDT_ERR_BADVERSION,
@@ -1533,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)
{
@@ -1562,7 +1694,7 @@ static inline int fdt_property_cell(void *fdt, const char *name, uint32_t val)
* @fdt: pointer to the device tree blob
* @name: name of property to add
* @len: length of property value in bytes
- * @valp: returns a pointer to where where the value should be placed
+ * @valp: returns a pointer to where the value should be placed
*
* returns:
* 0, on success
@@ -1573,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);
/**
@@ -1660,6 +1871,38 @@ int fdt_del_mem_rsv(void *fdt, int n);
int fdt_set_name(void *fdt, int nodeoffset, const char *name);
/**
+ * fdt_setprop_namelen - create or change a property
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose property to change
+ * @name: name of the property to change
+ * @namelen: length of the name
+ * @val: pointer to data to set the property value to
+ * @len: length of the property value
+ *
+ * fdt_setprop_namelen() sets the value of the named property in the given
+ * node to the given value and length, creating the property if it
+ * does not already exist.
+ *
+ * This function may insert or delete data from the blob, and will
+ * therefore change the offsets of some existing nodes.
+ *
+ * returns:
+ * 0, on success
+ * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to
+ * contain the new property value
+ * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
+ * -FDT_ERR_BADLAYOUT,
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_BADLAYOUT,
+ * -FDT_ERR_TRUNCATED, standard meanings
+ */
+int fdt_setprop_namelen(void *fdt, int nodeoffset, const char *name,
+ int namelen, const void *val, int len);
+
+/**
* fdt_setprop - create or change a property
* @fdt: pointer to the device tree blob
* @nodeoffset: offset of the node whose property to change
@@ -1687,8 +1930,44 @@ int fdt_set_name(void *fdt, int nodeoffset, const char *name);
* -FDT_ERR_BADLAYOUT,
* -FDT_ERR_TRUNCATED, standard meanings
*/
-int fdt_setprop(void *fdt, int nodeoffset, const char *name,
- const void *val, int len);
+static inline int fdt_setprop(void *fdt, int nodeoffset, const char *name,
+ const void *val, int len)
+{
+ return fdt_setprop_namelen(fdt, nodeoffset, name, strlen(name), val,
+ len);
+}
+
+/**
+ * fdt_setprop_placeholder_namelen - allocate space for a property
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose property to change
+ * @name: name of the property to change
+ * @namelen: length of the name
+ * @len: length of the property value
+ * @prop_data: return pointer to property data
+ *
+ * fdt_setprop_placeholder_namelen() allocates the named property in the given node.
+ * If the property exists it is resized. In either case a pointer to the
+ * property data is returned.
+ *
+ * This function may insert or delete data from the blob, and will
+ * therefore change the offsets of some existing nodes.
+ *
+ * returns:
+ * 0, on success
+ * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to
+ * contain the new property value
+ * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
+ * -FDT_ERR_BADLAYOUT,
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_BADLAYOUT,
+ * -FDT_ERR_TRUNCATED, standard meanings
+ */
+int fdt_setprop_placeholder_namelen(void *fdt, int nodeoffset, const char *name,
+ int namelen, int len, void **prop_data);
/**
* fdt_setprop_placeholder - allocate space for a property
@@ -1698,7 +1977,7 @@ int fdt_setprop(void *fdt, int nodeoffset, const char *name,
* @len: length of the property value
* @prop_data: return pointer to property data
*
- * fdt_setprop_placeholer() allocates the named property in the given node.
+ * fdt_setprop_placeholder() allocates the named property in the given node.
* If the property exists it is resized. In either case a pointer to the
* property data is returned.
*
@@ -1718,8 +1997,13 @@ int fdt_setprop(void *fdt, int nodeoffset, const char *name,
* -FDT_ERR_BADLAYOUT,
* -FDT_ERR_TRUNCATED, standard meanings
*/
-int fdt_setprop_placeholder(void *fdt, int nodeoffset, const char *name,
- int len, void **prop_data);
+static inline int fdt_setprop_placeholder(void *fdt, int nodeoffset,
+ const char *name, int len,
+ void **prop_data)
+{
+ return fdt_setprop_placeholder_namelen(fdt, nodeoffset, name,
+ strlen(name), len, prop_data);
+}
/**
* fdt_setprop_u32 - set a property to a 32-bit integer
@@ -1839,6 +2123,38 @@ static inline int fdt_setprop_cell(void *fdt, int nodeoffset, const char *name,
#define fdt_setprop_string(fdt, nodeoffset, name, str) \
fdt_setprop((fdt), (nodeoffset), (name), (str), strlen(str)+1)
+/**
+ * fdt_setprop_namelen_string - set a property to a string value
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose property to change
+ * @name: name of the property to change
+ * @namelen: number of characters of name to consider
+ * @str: string value for the property
+ *
+ * fdt_setprop_namelen_string() sets the value of the named property in the
+ * given node to the given string value (using the length of the
+ * string to determine the new length of the property), or creates a
+ * new property with that value if it does not already exist.
+ *
+ * This function may insert or delete data from the blob, and will
+ * therefore change the offsets of some existing nodes.
+ *
+ * returns:
+ * 0, on success
+ * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to
+ * contain the new property value
+ * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
+ * -FDT_ERR_BADLAYOUT,
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_BADLAYOUT,
+ * -FDT_ERR_TRUNCATED, standard meanings
+ */
+#define fdt_setprop_namelen_string(fdt, nodeoffset, name, namelen, str) \
+ fdt_setprop_namelen((fdt), (nodeoffset), (name), (namelen), (str), \
+ strlen(str) + 1)
/**
* fdt_setprop_empty - set a property to an empty value
@@ -2059,7 +2375,7 @@ int fdt_appendprop_addrrange(void *fdt, int parent, int nodeoffset,
* @nodeoffset: offset of the node whose property to nop
* @name: name of the property to nop
*
- * fdt_del_property() will delete the given property.
+ * fdt_delprop() will delete the given property.
*
* This function will delete data from the blob, and will therefore
* change the offsets of some existing nodes.
@@ -2111,8 +2427,7 @@ int fdt_add_subnode_namelen(void *fdt, int parentoffset,
* change the offsets of some existing nodes.
*
* returns:
- * structure block offset of the created nodeequested subnode (>=0), on
- * success
+ * structure block offset of the created subnode (>=0), on success
* -FDT_ERR_NOTFOUND, if the requested subnode does not exist
* -FDT_ERR_BADOFFSET, if parentoffset did not point to an FDT_BEGIN_NODE
* tag
@@ -2122,7 +2437,7 @@ int fdt_add_subnode_namelen(void *fdt, int parentoffset,
* blob to contain the new node
* -FDT_ERR_NOSPACE
* -FDT_ERR_BADLAYOUT
- * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADMAGIC,
* -FDT_ERR_BADVERSION,
* -FDT_ERR_BADSTATE,
* -FDT_ERR_BADSTRUCTURE,
@@ -2167,7 +2482,7 @@ int fdt_del_node(void *fdt, int nodeoffset);
* returns:
* 0, on success
* -FDT_ERR_NOSPACE, there's not enough space in the base device tree
- * -FDT_ERR_NOTFOUND, the overlay points to some inexistant nodes or
+ * -FDT_ERR_NOTFOUND, the overlay points to some nonexistent nodes or
* properties in the base DT
* -FDT_ERR_BADPHANDLE,
* -FDT_ERR_BADOVERLAY,
@@ -2206,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 16bda1906a7b..0e103cafa714 100644
--- a/scripts/dtc/libfdt/libfdt_internal.h
+++ b/scripts/dtc/libfdt/libfdt_internal.h
@@ -11,16 +11,26 @@
#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);
int fdt_check_prop_offset_(const void *fdt, int offset);
-const char *fdt_find_string_(const char *strtab, int tabsize, const char *s);
+
+const char *fdt_find_string_len_(const char *strtab, int tabsize, const char *s,
+ int s_len);
+static inline const char *fdt_find_string_(const char *strtab, int tabsize,
+ const char *s)
+{
+ return fdt_find_string_len_(strtab, tabsize, s, strlen(s));
+}
+
int fdt_node_end_offset_(void *fdt, int nodeoffset);
static inline const void *fdt_offset_ptr_(const void *fdt, int offset)
@@ -47,8 +57,8 @@ static inline struct fdt_reserve_entry *fdt_mem_rsv_w_(void *fdt, int n)
}
/*
- * Internal helpers to access tructural elements of the device tree
- * blob (rather than for exaple reading integers from within property
+ * Internal helpers to access structural elements of the device tree
+ * blob (rather than for example reading integers from within property
* values). We assume that we are either given a naturally aligned
* address for the platform or if we are not, we are on a platform
* where unaligned memory reads will be handled in a graceful manner.
@@ -84,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 49f723002f85..5d72abceb526 100644
--- a/scripts/dtc/livetree.c
+++ b/scripts/dtc/livetree.c
@@ -174,7 +174,7 @@ struct node *merge_nodes(struct node *old_node, struct node *new_node)
old_prop->val = new_prop->val;
old_prop->deleted = 0;
- free(old_prop->srcpos);
+ srcpos_free(old_prop->srcpos);
old_prop->srcpos = new_prop->srcpos;
free(new_prop);
new_prop = NULL;
@@ -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) {
+ 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 {
+ 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) {
- d = data_add_marker(p->val, type, name);
- d = data_append_data(d, data, len);
- p->val = d;
+ 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 {
- 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_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)
@@ -504,7 +557,7 @@ struct node *get_subnode(struct node *node, const char *nodename)
struct node *child;
for_each_child(node, child)
- if (streq(child->name, nodename))
+ if (streq(child->name, nodename) && !child->deleted)
return child;
return NULL;
@@ -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)
@@ -1014,38 +1075,62 @@ static void add_local_fixup_entry(struct dt_info *dti,
/* walk the path components creating nodes if they don't exist */
for (wn = lfn, i = 1; i < depth; i++, wn = nwn) {
/* if no node exists, create it */
- nwn = get_subnode(wn, compp[i]);
- if (!nwn)
- nwn = build_and_name_child_node(wn, compp[i]);
+ nwn = build_root_node(wn, compp[i]);
}
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)
@@ -1060,14 +1145,171 @@ void generate_fixups_tree(struct dt_info *dti, const char *name)
{
if (!any_fixup_tree(dti, dti->dt))
return;
- generate_fixups_tree_internal(dti, build_root_node(dti->dt, name),
- dti->dt);
+ 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);
+}
+
+void fixup_phandles(struct dt_info *dti, const char *name)
+{
+ struct node *an;
+ struct property *fp;
+
+ an = get_subnode(dti->dt, name);
+ if (!an)
+ return;
+
+ 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)
{
if (!any_local_fixup_tree(dti, dti->dt))
return;
- generate_local_fixups_tree_internal(dti, build_root_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 8e4d18a90b47..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);
@@ -160,8 +180,10 @@ FILE *srcfile_relative_open(const char *fname, char **fullnamep)
strerror(errno));
}
- if (depfile)
- fprintf(depfile, " %s", fullname);
+ if (depfile) {
+ fputc(' ', depfile);
+ fprint_path_escaped(depfile, fullname);
+ }
if (fullnamep)
*fullnamep = fullname;
@@ -285,6 +307,17 @@ struct srcpos *srcpos_extend(struct srcpos *pos, struct srcpos *newtail)
return pos;
}
+void srcpos_free(struct srcpos *pos)
+{
+ struct srcpos *p_next;
+
+ while (pos) {
+ p_next = pos->next;
+ free(pos);
+ pos = p_next;
+ }
+}
+
char *
srcpos_string(struct srcpos *pos)
{
diff --git a/scripts/dtc/srcpos.h b/scripts/dtc/srcpos.h
index 4318d7ad34d9..4d60b50e3119 100644
--- a/scripts/dtc/srcpos.h
+++ b/scripts/dtc/srcpos.h
@@ -88,6 +88,7 @@ extern void srcpos_update(struct srcpos *pos, const char *text, int len);
extern struct srcpos *srcpos_copy(struct srcpos *pos);
extern struct srcpos *srcpos_extend(struct srcpos *new_srcpos,
struct srcpos *old_srcpos);
+extern void srcpos_free(struct srcpos *pos);
extern char *srcpos_string(struct srcpos *pos);
extern char *srcpos_string_first(struct srcpos *pos, int level);
extern char *srcpos_string_last(struct srcpos *pos, int level);
diff --git a/scripts/dtc/treesource.c b/scripts/dtc/treesource.c
index ae15839ba6a5..bf648bf6c21a 100644
--- a/scripts/dtc/treesource.c
+++ b/scripts/dtc/treesource.c
@@ -139,35 +139,93 @@ static const char *delim_end[] = {
[TYPE_STRING] = "",
};
-static void add_string_markers(struct property *prop)
+/*
+ * The invariants in the marker list are:
+ * - offsets are non-strictly monotonically increasing
+ * - for a single offset there is at most one type marker
+ * - for a single offset that has both a type marker and non-type markers, the
+ * type marker appears before the others.
+ */
+static struct marker **add_marker(struct marker **mi,
+ enum markertype type, unsigned int offset, char *ref)
{
- int l, len = prop->val.len;
- const char *p = prop->val.val;
-
- for (l = strlen(p) + 1; l < len; l += strlen(p + l) + 1) {
- struct marker *m, **nextp;
-
- m = xmalloc(sizeof(*m));
- m->offset = l;
- m->type = TYPE_STRING;
- m->ref = NULL;
- m->next = NULL;
-
- /* Find the end of the markerlist */
- nextp = &prop->val.markers;
- while (*nextp)
- nextp = &((*nextp)->next);
- *nextp = m;
+ struct marker *nm;
+
+ while (*mi && (*mi)->offset < offset)
+ mi = &(*mi)->next;
+
+ if (*mi && (*mi)->offset == offset && is_type_marker((*mi)->type)) {
+ if (is_type_marker(type))
+ return mi;
+ mi = &(*mi)->next;
}
+
+ if (*mi && (*mi)->offset == offset && type == (*mi)->type)
+ return mi;
+
+ nm = xmalloc(sizeof(*nm));
+ nm->type = type;
+ nm->offset = offset;
+ nm->ref = ref;
+ nm->next = *mi;
+ *mi = nm;
+
+ return &nm->next;
}
-static enum markertype guess_value_type(struct property *prop)
+void property_add_marker(struct property *prop,
+ enum markertype type, unsigned int offset, char *ref)
{
- int len = prop->val.len;
- const char *p = prop->val.val;
- struct marker *m = prop->val.markers;
+ 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, 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, unsigned int offset, int len)
+{
+ 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++) {
@@ -177,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;
@@ -219,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;
@@ -347,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/util.c b/scripts/dtc/util.c
index 507f0120cd13..412592320265 100644
--- a/scripts/dtc/util.c
+++ b/scripts/dtc/util.c
@@ -23,6 +23,22 @@
#include "util.h"
#include "version_gen.h"
+void fprint_path_escaped(FILE *fp, const char *path)
+{
+ const char *p = path;
+
+ while (*p) {
+ if (*p == ' ') {
+ fputc('\\', fp);
+ fputc(' ', fp);
+ } else {
+ fputc(*p, fp);
+ }
+
+ p++;
+ }
+}
+
char *xstrdup(const char *s)
{
int len = strlen(s) + 1;
diff --git a/scripts/dtc/util.h b/scripts/dtc/util.h
index b448cd79efd3..800f2e2c55b1 100644
--- a/scripts/dtc/util.h
+++ b/scripts/dtc/util.h
@@ -42,6 +42,11 @@ static inline void NORETURN PRINTF(1, 2) die(const char *str, ...)
exit(1);
}
+/**
+ * Writes path to fp, escaping spaces with a backslash.
+ */
+void fprint_path_escaped(FILE *fp, const char *path);
+
static inline void *xmalloc(size_t len)
{
void *new = malloc(len);
diff --git a/scripts/dtc/version_gen.h b/scripts/dtc/version_gen.h
index bf81ce593685..122e684e76a1 100644
--- a/scripts/dtc/version_gen.h
+++ b/scripts/dtc/version_gen.h
@@ -1 +1 @@
-#define DTC_VERSION "DTC 1.7.0-gbcd02b52"
+#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/extract-vmlinux b/scripts/extract-vmlinux
index 189956b5a5c8..266df9bc7a48 100755
--- a/scripts/extract-vmlinux
+++ b/scripts/extract-vmlinux
@@ -10,12 +10,15 @@
#
# ----------------------------------------------------------------------
+me=${0##*/}
+
check_vmlinux()
{
if file "$1" | grep -q 'Linux kernel.*boot executable' ||
readelf -h "$1" > /dev/null 2>&1
then
cat "$1"
+ echo "$me: Extracted vmlinux using '$2' from offset $3" >&2
exit 0
fi
}
@@ -30,12 +33,11 @@ try_decompress()
do
pos=${pos%%:*}
tail -c+$pos "$img" | $3 > $tmp 2> /dev/null
- check_vmlinux $tmp
+ check_vmlinux $tmp "$3" $pos
done
}
# Check invocation:
-me=${0##*/}
img=$1
if [ $# -ne 1 -o ! -s "$img" ]
then
@@ -57,7 +59,7 @@ try_decompress '\002!L\030' xxx 'lz4 -d'
try_decompress '(\265/\375' xxx unzstd
# Finally check for uncompressed images or objects:
-check_vmlinux $img
+check_vmlinux "$img" cat 0
# Bail out:
echo "$me: Cannot find vmlinux." >&2
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 6cb6d1051815..8f1b3500f8e2 100644
--- a/scripts/gcc-plugins/gcc-common.h
+++ b/scripts/gcc-plugins/gcc-common.h
@@ -173,10 +173,17 @@ static inline opt_pass *get_pass_for_id(int id)
return g->get_passes()->get_pass_for_id(id);
}
+#if BUILDING_GCC_VERSION < 16000
#define TODO_verify_ssa TODO_verify_il
#define TODO_verify_flow TODO_verify_il
#define TODO_verify_stmts TODO_verify_il
#define TODO_verify_rtl_sharing TODO_verify_il
+#else
+#define TODO_verify_ssa 0
+#define TODO_verify_flow 0
+#define TODO_verify_stmts 0
+#define TODO_verify_rtl_sharing 0
+#endif
#define INSN_DELETED_P(insn) (insn)->deleted()
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 98445671fe83..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']
@@ -56,8 +56,6 @@ def print_base(base):
text += " .index: {}\n".format(base['index'])
text += " .resolution: {} nsecs\n".format(constants.LX_hrtimer_resolution)
-
- text += " .get_time: {}\n".format(base['get_time'])
if constants.LX_CONFIG_HIGH_RES_TIMERS:
text += " .offset: {} nsecs\n".format(base['offset'])
text += "active timers:\n"
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 7c3ea2b55041..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"])
- append_crate_with_generated("uapi", ["core", "ffi"])
- 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 6bbccb43f7e7..9c15e748761c 100755
--- a/scripts/headers_install.sh
+++ b/scripts/headers_install.sh
@@ -32,7 +32,7 @@ fi
sed -E -e '
s/([[:space:](])(__user|__force|__iomem)[[:space:]]/\1/g
s/__attribute_const__([[:space:]]|$)/\1/g
- s@^#include <linux/compiler(|_types).h>@@
+ s@^#include <linux/compiler.h>@@
s/(^|[^a-zA-Z0-9])__packed([^a-zA-Z0-9_]|$)/\1__attribute__((packed))\2/g
s/(^|[[:space:](])(inline|asm|volatile)([[:space:](]|$)/\1__\2__\3/g
s@#(ifndef|define|endif[[:space:]]*/[*])[[:space:]]*_UAPI@#\1 @
@@ -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/expr.h b/scripts/kconfig/expr.h
index fe2231e0e6a4..5f900d18dae0 100644
--- a/scripts/kconfig/expr.h
+++ b/scripts/kconfig/expr.h
@@ -145,6 +145,7 @@ struct symbol {
#define SYMBOL_CONST 0x0001 /* symbol is const */
#define SYMBOL_CHECK 0x0008 /* used during dependency checking */
#define SYMBOL_VALID 0x0080 /* set when symbol.curr is calculated */
+#define SYMBOL_TRANS 0x0100 /* symbol is transitional only (not visible)*/
#define SYMBOL_WRITE 0x0200 /* write symbol to file (KCONFIG_CONFIG) */
#define SYMBOL_WRITTEN 0x0800 /* track info to avoid double-write to .config */
#define SYMBOL_CHECKED 0x2000 /* used during dependency checking */
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 9c2cdfc33c6f..a6155422b4a6 100644
--- a/scripts/kconfig/lexer.l
+++ b/scripts/kconfig/lexer.l
@@ -126,6 +126,7 @@ n [A-Za-z0-9_-]
"select" return T_SELECT;
"source" return T_SOURCE;
"string" return T_STRING;
+"transitional" return T_TRANSITIONAL;
"tristate" return T_TRISTATE;
"visible" return T_VISIBLE;
"||" return T_OR;
@@ -401,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;
}
@@ -442,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/mconf.c b/scripts/kconfig/mconf.c
index 84ea9215c0a7..b8b7bba84a65 100644
--- a/scripts/kconfig/mconf.c
+++ b/scripts/kconfig/mconf.c
@@ -12,6 +12,7 @@
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
+#include <locale.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
@@ -931,6 +932,8 @@ int main(int ac, char **av)
signal(SIGINT, sig_handler);
+ setlocale(LC_ALL, "");
+
if (ac > 1 && strcmp(av[1], "-s") == 0) {
silent = 1;
/* Silence conf_read() until the real callback is set up */
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/nconf.c b/scripts/kconfig/nconf.c
index ae1fe5f60327..521700ed7152 100644
--- a/scripts/kconfig/nconf.c
+++ b/scripts/kconfig/nconf.c
@@ -7,6 +7,7 @@
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
+#include <locale.h>
#include <string.h>
#include <strings.h>
#include <stdlib.h>
@@ -1478,6 +1479,8 @@ int main(int ac, char **av)
int lines, columns;
char *mode;
+ setlocale(LC_ALL, "");
+
if (ac > 1 && strcmp(av[1], "-s") == 0) {
/* Silence conf_read() until the real callback is set up */
conf_set_message_callback(NULL);
diff --git a/scripts/kconfig/nconf.gui.c b/scripts/kconfig/nconf.gui.c
index 7206437e784a..2d097bc7ef1a 100644
--- a/scripts/kconfig/nconf.gui.c
+++ b/scripts/kconfig/nconf.gui.c
@@ -173,12 +173,10 @@ void fill_window(WINDOW *win, const char *text)
/* do not go over end of line */
total_lines = min(total_lines, y);
for (i = 0; i < total_lines; i++) {
- char tmp[x+10];
const char *line = get_line(text, i);
- int len = get_line_length(line);
- strncpy(tmp, line, min(len, x));
- tmp[len] = '\0';
- mvwprintw(win, i, 0, "%s", tmp);
+ int len = min(get_line_length(line), x);
+
+ mvwprintw(win, i, 0, "%.*s", len, line);
}
}
diff --git a/scripts/kconfig/parser.y b/scripts/kconfig/parser.y
index e9c3c664e925..5fb6f07b6ad2 100644
--- a/scripts/kconfig/parser.y
+++ b/scripts/kconfig/parser.y
@@ -75,6 +75,7 @@ struct menu *current_menu, *current_entry, *current_choice;
%token T_SELECT
%token T_SOURCE
%token T_STRING
+%token T_TRANSITIONAL
%token T_TRISTATE
%token T_VISIBLE
%token T_EOL
@@ -158,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);
@@ -205,6 +200,12 @@ config_option: T_PROMPT T_WORD_QUOTE if_expr T_EOL
printd(DEBUG_PARSE, "%s:%d:prompt\n", cur_filename, cur_lineno);
};
+config_option: T_TRANSITIONAL T_EOL
+{
+ current_entry->sym->flags |= SYMBOL_TRANS;
+ printd(DEBUG_PARSE, "%s:%d:transitional\n", cur_filename, cur_lineno);
+};
+
config_option: default expr if_expr T_EOL
{
menu_add_expr(P_DEFAULT, $2, $3);
@@ -316,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();
};
@@ -415,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);
};
@@ -483,6 +484,43 @@ assign_val:
%%
/**
+ * transitional_check_sanity - check transitional symbols have no other
+ * properties
+ *
+ * @menu: menu of the potentially transitional symbol
+ *
+ * Return: -1 if an error is found, 0 otherwise.
+ */
+static int transitional_check_sanity(const struct menu *menu)
+{
+ struct property *prop;
+
+ if (!menu->sym || !(menu->sym->flags & SYMBOL_TRANS))
+ return 0;
+
+ /* Check for depends and visible conditions. */
+ if ((menu->dep && !expr_is_yes(menu->dep)) ||
+ (menu->visibility && !expr_is_yes(menu->visibility))) {
+ fprintf(stderr, "%s:%d: error: %s",
+ menu->filename, menu->lineno,
+ "transitional symbols can only have help sections\n");
+ return -1;
+ }
+
+ /* Check for any property other than "help". */
+ for (prop = menu->sym->prop; prop; prop = prop->next) {
+ if (prop->type != P_COMMENT) {
+ fprintf(stderr, "%s:%d: error: %s",
+ prop->filename, prop->lineno,
+ "transitional symbols can only have help sections\n");
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+/**
* choice_check_sanity - check sanity of a choice member
*
* @menu: menu of the choice member
@@ -502,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;
}
}
@@ -558,6 +595,9 @@ void conf_parse(const char *name)
if (menu->sym && sym_check_deps(menu->sym))
yynerrs++;
+ if (transitional_check_sanity(menu))
+ yynerrs++;
+
if (menu->sym && sym_is_choice(menu->sym)) {
menu_for_each_sub_entry(child, menu)
if (child->sym && choice_check_sanity(child))
diff --git a/scripts/kconfig/qconf.cc b/scripts/kconfig/qconf.cc
index f8992db1870a..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);
@@ -1377,6 +1376,19 @@ ConfigMainWindow::ConfigMainWindow(void)
ConfigList::showPromptAction = new QAction("Show Prompt Options", optGroup);
ConfigList::showPromptAction->setCheckable(true);
+ switch (configList->optMode) {
+ case allOpt:
+ ConfigList::showAllAction->setChecked(true);
+ break;
+ case promptOpt:
+ ConfigList::showPromptAction->setChecked(true);
+ break;
+ case normalOpt:
+ default:
+ ConfigList::showNormalAction->setChecked(true);
+ break;
+ }
+
QAction *showDebugAction = new QAction("Show Debug Info", this);
showDebugAction->setCheckable(true);
connect(showDebugAction, &QAction::toggled,
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/symbol.c b/scripts/kconfig/symbol.c
index 26ab10c0fd76..7e81b3676ee9 100644
--- a/scripts/kconfig/symbol.c
+++ b/scripts/kconfig/symbol.c
@@ -214,6 +214,11 @@ static void sym_calc_visibility(struct symbol *sym)
struct property *prop;
tristate tri;
+ if (sym->flags & SYMBOL_TRANS) {
+ sym->visible = yes;
+ return;
+ }
+
/* any prompt visible? */
tri = no;
for_all_prompts(sym, prop) {
@@ -406,7 +411,7 @@ bool sym_dep_errors(void)
void sym_calc_value(struct symbol *sym)
{
struct symbol_value newval, oldval;
- struct property *prop;
+ struct property *prop = NULL;
struct menu *choice_menu;
if (!sym)
@@ -515,6 +520,19 @@ void sym_calc_value(struct symbol *sym)
;
}
+ /*
+ * If the symbol lacks a user value but its value comes from a
+ * single transitional symbol with an existing user value, mark
+ * this symbol as having a user value to avoid prompting.
+ */
+ if (prop && !sym_has_value(sym)) {
+ struct symbol *ds = prop_get_symbol(prop);
+ if (ds && (ds->flags & SYMBOL_TRANS) && sym_has_value(ds)) {
+ sym->def[S_DEF_USER] = newval;
+ sym->flags |= SYMBOL_DEF_USER;
+ }
+ }
+
sym->curr = newval;
sym_validate_range(sym);
@@ -526,7 +544,7 @@ void sym_calc_value(struct symbol *sym)
}
}
- if (sym_is_choice(sym))
+ if (sym_is_choice(sym) || sym->flags & SYMBOL_TRANS)
sym->flags &= ~SYMBOL_WRITE;
}
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/conftest.py b/scripts/kconfig/tests/conftest.py
index 2a2a7e2da060..d94b79e012c0 100644
--- a/scripts/kconfig/tests/conftest.py
+++ b/scripts/kconfig/tests/conftest.py
@@ -81,7 +81,22 @@ class Conf:
# For interactive modes such as oldaskconfig, oldconfig,
# send 'Enter' key until the program finishes.
if interactive:
- ps.stdin.write(b'\n')
+ try:
+ ps.stdin.write(b'\n')
+ ps.stdin.flush()
+ except (BrokenPipeError, OSError):
+ # Process has exited, stop sending input
+ break
+
+ # Close stdin gracefully
+ try:
+ ps.stdin.close()
+ except (BrokenPipeError, OSError):
+ # Ignore broken pipe on close
+ pass
+
+ # Wait for process to complete
+ ps.wait()
self.retcode = ps.returncode
self.stdout = ps.stdout.read().decode()
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/tests/err_transitional/Kconfig b/scripts/kconfig/tests/err_transitional/Kconfig
new file mode 100644
index 000000000000..a75ed3b2fe5e
--- /dev/null
+++ b/scripts/kconfig/tests/err_transitional/Kconfig
@@ -0,0 +1,52 @@
+# SPDX-License-Identifier: GPL-2.0
+# Test that transitional symbols cannot have properties other than help
+
+config BAD_DEFAULT
+ bool
+ transitional
+ default y
+ help
+ This transitional symbol illegally has a default property.
+
+config BAD_PROMPT
+ bool
+ transitional
+ prompt "Bad prompt"
+ help
+ This transitional symbol illegally has a prompt.
+
+config BAD_SELECT
+ bool
+ transitional
+ select OTHER_SYMBOL
+ help
+ This transitional symbol illegally has a select.
+
+config BAD_IMPLY
+ bool
+ transitional
+ imply OTHER_SYMBOL
+ help
+ This transitional symbol illegally has an imply.
+
+config BAD_DEPENDS
+ bool
+ transitional
+ depends on OTHER_SYMBOL
+ help
+ This transitional symbol illegally has a depends.
+
+config BAD_RANGE
+ int
+ transitional
+ range 1 10
+ help
+ This transitional symbol illegally has a range.
+
+config BAD_NO_TYPE
+ transitional
+ help
+ This transitional symbol illegally has no type specified.
+
+config OTHER_SYMBOL
+ bool
diff --git a/scripts/kconfig/tests/err_transitional/__init__.py b/scripts/kconfig/tests/err_transitional/__init__.py
new file mode 100644
index 000000000000..7dffb5b0833f
--- /dev/null
+++ b/scripts/kconfig/tests/err_transitional/__init__.py
@@ -0,0 +1,14 @@
+# SPDX-License-Identifier: GPL-2.0
+"""
+Test that transitional symbols with invalid properties are rejected.
+
+Transitional symbols can only have help sections. Any other properties
+(default, select, depends, etc.) should cause a parser error.
+"""
+
+def test(conf):
+ # This should fail with exit code 1 due to invalid transitional symbol
+ assert conf.olddefconfig() == 1
+
+ # Check that the error message is about transitional symbols
+ assert conf.stderr_contains('expected_stderr')
diff --git a/scripts/kconfig/tests/err_transitional/expected_stderr b/scripts/kconfig/tests/err_transitional/expected_stderr
new file mode 100644
index 000000000000..b52db4f680f4
--- /dev/null
+++ b/scripts/kconfig/tests/err_transitional/expected_stderr
@@ -0,0 +1,7 @@
+Kconfig:46:warning: config symbol defined without type
+Kconfig:7: error: transitional symbols can only have help sections
+Kconfig:14: error: transitional symbols can only have help sections
+Kconfig:21: error: transitional symbols can only have help sections
+Kconfig:28: error: transitional symbols can only have help sections
+Kconfig:32: error: transitional symbols can only have help sections
+Kconfig:42: error: transitional symbols can only have help sections
diff --git a/scripts/kconfig/tests/transitional/Kconfig b/scripts/kconfig/tests/transitional/Kconfig
new file mode 100644
index 000000000000..faa4d396f828
--- /dev/null
+++ b/scripts/kconfig/tests/transitional/Kconfig
@@ -0,0 +1,132 @@
+# SPDX-License-Identifier: GPL-2.0
+# Test transitional symbols for config migration with all Kconfig types
+
+# Enable module support for tristate testing
+config MODULES
+ bool "Enable loadable module support"
+ modules
+ default y
+
+# Basic migration tests for all types
+config NEW_BOOL
+ bool "New bool option"
+ default OLD_BOOL
+
+config OLD_BOOL
+ bool
+ transitional
+
+config NEW_TRISTATE
+ tristate "New tristate option"
+ default OLD_TRISTATE
+
+config OLD_TRISTATE
+ tristate
+ transitional
+
+config NEW_STRING
+ string "New string option"
+ default OLD_STRING
+
+config OLD_STRING
+ string
+ transitional
+
+config NEW_HEX
+ hex "New hex option"
+ default OLD_HEX
+
+config OLD_HEX
+ hex
+ transitional
+
+config NEW_INT
+ int "New int option"
+ default OLD_INT
+
+config OLD_INT
+ int
+ transitional
+
+# Precedence tests for all types
+config NEW_BOOL_PRECEDENCE
+ bool "New bool option with precedence"
+ default OLD_BOOL_PRECEDENCE
+
+config OLD_BOOL_PRECEDENCE
+ bool
+ transitional
+
+config NEW_STRING_PRECEDENCE
+ string "New string option with precedence"
+ default OLD_STRING_PRECEDENCE
+
+config OLD_STRING_PRECEDENCE
+ string
+ transitional
+
+config NEW_TRISTATE_PRECEDENCE
+ tristate "New tristate option with precedence"
+ default OLD_TRISTATE_PRECEDENCE
+
+config OLD_TRISTATE_PRECEDENCE
+ tristate
+ transitional
+
+config NEW_HEX_PRECEDENCE
+ hex "New hex option with precedence"
+ default OLD_HEX_PRECEDENCE
+
+config OLD_HEX_PRECEDENCE
+ hex
+ transitional
+
+config NEW_INT_PRECEDENCE
+ int "New int option with precedence"
+ default OLD_INT_PRECEDENCE
+
+config OLD_INT_PRECEDENCE
+ int
+ transitional
+
+# Test that help sections are allowed for transitional symbols
+config OLD_WITH_HELP
+ bool
+ transitional
+ help
+ This transitional symbol has a help section to validate that help is allowed.
+
+# Test that we can set something to =n via transitional symbol
+config NEW_DISABLED
+ tristate "Check for setting to disabled"
+ default OLD_DISABLED
+
+config OLD_DISABLED
+ tristate
+ transitional
+
+# Test that a potential new value disappears if it lacks a prompt
+config NEW_DISABLED_UNSAVED
+ tristate
+ default OLD_DISABLED
+
+config OLD_DISABLED_UNSAVED
+ tristate
+ transitional
+
+# Test conditional default: transitional value should not prevent prompting
+# when default visibility makes the expression evaluate to 'no'
+config DEPENDENCY_TEST
+ bool "Dependency for testing"
+ default n
+
+config NEW_CONDITIONAL_DEFAULT
+ bool "New option with conditional default"
+ default OLD_CONDITIONAL_DEFAULT if DEPENDENCY_TEST
+
+config OLD_CONDITIONAL_DEFAULT
+ bool
+ transitional
+
+config REGULAR_OPTION
+ bool "Regular option"
diff --git a/scripts/kconfig/tests/transitional/__init__.py b/scripts/kconfig/tests/transitional/__init__.py
new file mode 100644
index 000000000000..b50ba2397548
--- /dev/null
+++ b/scripts/kconfig/tests/transitional/__init__.py
@@ -0,0 +1,25 @@
+# SPDX-License-Identifier: GPL-2.0
+"""
+Test transitional symbol migration functionality for all Kconfig types.
+
+This tests that:
+- OLD_* options in existing .config cause NEW_* options to be set
+- OLD_* options are not written to the new .config file
+- NEW_* options appear in the new .config file with correct values
+- NEW_* options with defaults from transitional symbols are not prompted
+- All Kconfig types work correctly: bool, tristate, string, hex, int
+- User-set NEW values take precedence over conflicting OLD transitional values
+"""
+
+def test(conf):
+ # Run olddefconfig to process the migration with the initial config
+ assert conf.olddefconfig(dot_config='initial_config') == 0
+
+ # Check that the configuration matches expected output
+ assert conf.config_contains('expected_config')
+
+ # Test oldconfig to ensure symbols with transitional defaults are not prompted
+ assert conf.oldconfig(dot_config='initial_config', in_keys='n\n') == 0
+
+ # Except for when conditional default evaluates to 'no'
+ assert conf.stdout_contains('expected_stdout')
diff --git a/scripts/kconfig/tests/transitional/expected_config b/scripts/kconfig/tests/transitional/expected_config
new file mode 100644
index 000000000000..e01f5f070a26
--- /dev/null
+++ b/scripts/kconfig/tests/transitional/expected_config
@@ -0,0 +1,15 @@
+CONFIG_MODULES=y
+CONFIG_NEW_BOOL=y
+CONFIG_NEW_TRISTATE=m
+CONFIG_NEW_STRING="test string"
+CONFIG_NEW_HEX=0x1234
+CONFIG_NEW_INT=42
+# CONFIG_NEW_BOOL_PRECEDENCE is not set
+CONFIG_NEW_STRING_PRECEDENCE="user value"
+CONFIG_NEW_TRISTATE_PRECEDENCE=y
+CONFIG_NEW_HEX_PRECEDENCE=0xABCD
+CONFIG_NEW_INT_PRECEDENCE=100
+# CONFIG_NEW_DISABLED is not set
+# CONFIG_DEPENDENCY_TEST is not set
+# CONFIG_NEW_CONDITIONAL_DEFAULT is not set
+# CONFIG_REGULAR_OPTION is not set
diff --git a/scripts/kconfig/tests/transitional/expected_stdout b/scripts/kconfig/tests/transitional/expected_stdout
new file mode 100644
index 000000000000..6f0b285d6469
--- /dev/null
+++ b/scripts/kconfig/tests/transitional/expected_stdout
@@ -0,0 +1 @@
+New option with conditional default (NEW_CONDITIONAL_DEFAULT) [N/y/?] (NEW) n
diff --git a/scripts/kconfig/tests/transitional/initial_config b/scripts/kconfig/tests/transitional/initial_config
new file mode 100644
index 000000000000..68b7da672426
--- /dev/null
+++ b/scripts/kconfig/tests/transitional/initial_config
@@ -0,0 +1,20 @@
+CONFIG_MODULES=y
+CONFIG_OLD_BOOL=y
+CONFIG_OLD_TRISTATE=m
+CONFIG_OLD_STRING="test string"
+CONFIG_OLD_HEX=0x1234
+CONFIG_OLD_INT=42
+# CONFIG_NEW_BOOL_PRECEDENCE is not set
+CONFIG_OLD_BOOL_PRECEDENCE=y
+CONFIG_NEW_STRING_PRECEDENCE="user value"
+CONFIG_OLD_STRING_PRECEDENCE="old value"
+CONFIG_NEW_TRISTATE_PRECEDENCE=y
+CONFIG_OLD_TRISTATE_PRECEDENCE=m
+CONFIG_NEW_HEX_PRECEDENCE=0xABCD
+CONFIG_OLD_HEX_PRECEDENCE=0x5678
+CONFIG_NEW_INT_PRECEDENCE=100
+CONFIG_OLD_INT_PRECEDENCE=200
+# CONFIG_OLD_DISABLED is not set
+# CONFIG_OLD_DISABLED_UNSAVED is not set
+# CONFIG_DEPENDENCY_TEST is not set
+CONFIG_OLD_CONDITIONAL_DEFAULT=y
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 fc3d46ef519f..000000000000
--- a/scripts/kernel-doc.py
+++ /dev/null
@@ -1,325 +0,0 @@
-#!/usr/bin/env python3
-# SPDX-License-Identifier: GPL-2.0
-# Copyright(c) 2025: Mauro Carvalho Chehab <mchehab@kernel.org>.
-#
-# pylint: disable=C0103,R0915
-#
-# 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))
-
-from kdoc_files import KernelFiles # pylint: disable=C0413
-from kdoc_output import RestFormat, ManFormat # pylint: disable=C0413
-
-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):
- logger.warning("Python 3.6 or later is required by kernel-doc")
-
- # Return 0 here to avoid breaking compilation
- sys.exit(0)
-
- if python_ver < (3,7):
- logger.warning("Python 3.7 or later is required for correct results")
-
- 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(f"{error_count} warnings as errors")
- sys.exit(error_count)
-
- if args.verbose:
- print(f"{error_count} errors")
-
- 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 fe730099eca8..000000000000
--- a/scripts/lib/kdoc/kdoc_parser.py
+++ /dev/null
@@ -1,1669 +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)
-attribute = KernRe(r"__attribute__\s*\(\([a-z0-9,_\*\s\(\)]*\)\)",
- flags=re.I | re.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)
-
-#
-# 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())
-
-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)
-
- if dtype == "" and param.endswith("..."):
- if KernRe(r'\w\.\.\.$').search(param):
- # For named variable parameters of the form `x...`,
- # remove the dots
- param = param[:-3]
- else:
- # Handles unnamed variable parameters
- param = "..."
-
- if param not in self.entry.parameterdescs or \
- not self.entry.parameterdescs[param]:
-
- self.entry.parameterdescs[param] = "variable arguments"
-
- elif dtype == "" and (not param or param == "void"):
- param = "void"
- self.entry.parameterdescs[param] = "no arguments"
-
- elif dtype == "" and 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
-
- # Handle cache group enforcing variables: they do not need
- # to be described in header files
- elif "__cacheline_group" in param:
- # Ignore __cacheline_group_begin and __cacheline_group_end
- return
-
- # 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):
- # Strip comments
- arg = KernRe(r'\/\*.*\*\/').sub('', arg)
-
- # 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)
-
- elif KernRe(r'\(.+\)\s*\(').search(arg):
- # Pointer-to-function
-
- arg = arg.replace('#', ',')
-
- r = KernRe(r'[^\(]+\(\*?\s*([\w\[\]\.]*)\s*\)')
- if r.match(arg):
- param = r.group(1)
- else:
- self.emit_msg(ln, f"Invalid param: {arg}")
- param = arg
-
- dtype = KernRe(r'([^\(]+\(\*?)\s*' + re.escape(param)).sub(r'\1', arg)
- self.push_parameter(ln, decl_type, param, dtype,
- arg, declaration_name)
-
- elif KernRe(r'\(.+\)\s*\[').search(arg):
- # Array-of-pointers
-
- arg = arg.replace('#', ',')
- r = KernRe(r'[^\(]+\(\s*\*\s*([\w\[\]\.]*?)\s*(\s*\[\s*[\w]+\s*\]\s*)*\)')
- if r.match(arg):
- param = r.group(1)
- else:
- self.emit_msg(ln, f"Invalid param: {arg}")
- param = arg
-
- dtype = KernRe(r'([^\(]+\(\*?)\s*' + re.escape(param)).sub(r'\1', arg)
-
- self.push_parameter(ln, decl_type, param, dtype,
- arg, declaration_name)
-
- elif arg:
- arg = KernRe(r'\s*:\s*').sub(":", arg)
- arg = KernRe(r'\s*\[').sub('[', arg)
-
- args = KernRe(r'\s*,\s*').split(arg)
- if args[0] and '*' in args[0]:
- args[0] = re.sub(r'(\*+)\s*', r' \1', args[0])
-
- first_arg = []
- r = KernRe(r'^(.*\s+)(.*?\[.*\].*)$')
- if args[0] and r.match(args[0]):
- args.pop(0)
- first_arg.extend(r.group(1))
- first_arg.append(r.group(2))
- else:
- first_arg = KernRe(r'\s+').split(args.pop(0))
-
- args.insert(0, first_arg.pop())
- dtype = ' '.join(first_arg)
-
- for param in args:
- if KernRe(r'^(\*+)\s*(.*)').match(param):
- r = KernRe(r'^(\*+)\s*(.*)')
- if not r.match(param):
- self.emit_msg(ln, f"Invalid param: {param}")
- continue
-
- param = r.group(1)
-
- self.push_parameter(ln, decl_type, r.group(2),
- f"{dtype} {r.group(1)}",
- arg, declaration_name)
-
- elif KernRe(r'(.*?):(\w+)').search(param):
- r = KernRe(r'(.*?):(\w+)')
- if not r.match(param):
- self.emit_msg(ln, f"Invalid param: {param}")
- continue
-
- if dtype != "": # Skip unnamed bit-fields
- self.push_parameter(ln, decl_type, r.group(1),
- f"{dtype}:{r.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}'")
-
- def dump_struct(self, ln, proto):
- """
- Store an entry for an struct or union
- """
-
- type_pattern = r'(struct|union)'
-
- qualifiers = [
- "__attribute__",
- "__packed",
- "__aligned",
- "____cacheline_aligned_in_smp",
- "____cacheline_aligned",
- ]
-
- definition_body = r'\{(.*)\}\s*' + "(?:" + '|'.join(qualifiers) + ")?"
- struct_members = KernRe(type_pattern + r'([^\{\};]+)(\{)([^\{\}]*)(\})([^\{\}\;]*)(\;)')
-
- # Extract struct/union definition
- members = None
- declaration_name = None
- decl_type = None
-
- r = KernRe(type_pattern + r'\s+(\w+)\s*' + definition_body)
- if r.search(proto):
- decl_type = r.group(1)
- declaration_name = r.group(2)
- members = r.group(3)
- else:
- r = KernRe(r'typedef\s+' + type_pattern + r'\s*' + definition_body + r'\s*(\w+)\s*;')
-
- if r.search(proto):
- decl_type = r.group(1)
- declaration_name = r.group(3)
- members = r.group(2)
-
- if not members:
- self.emit_msg(ln, f"{proto} error: Cannot parse struct or union!")
- return
-
- if self.entry.identifier != declaration_name:
- self.emit_msg(ln,
- f"expecting prototype for {decl_type} {self.entry.identifier}. Prototype was for {decl_type} {declaration_name} instead\n")
- return
-
- args_pattern = r'([^,)]+)'
-
- sub_prefixes = [
- (KernRe(r'\/\*\s*private:.*?\/\*\s*public:.*?\*\/', re.S | re.I), ''),
- (KernRe(r'\/\*\s*private:.*', re.S | re.I), ''),
-
- # Strip comments
- (KernRe(r'\/\*.*?\*\/', re.S), ''),
-
- # Strip attributes
- (attribute, ' '),
- (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), ' '),
-
- # 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*\(' + args_pattern + r',\s*' + args_pattern + r'\)', re.S), r'unsigned long \1[BITS_TO_LONGS(\2)]'),
- (KernRe(r'DECLARE_HASHTABLE\s*\(' + args_pattern + r',\s*' + args_pattern + r'\)', re.S), r'unsigned long \1[1 << ((\2) - 1)]'),
- (KernRe(r'DECLARE_KFIFO\s*\(' + args_pattern + r',\s*' + args_pattern + r',\s*' + args_pattern + r'\)', re.S), r'\2 *\1'),
- (KernRe(r'DECLARE_KFIFO_PTR\s*\(' + args_pattern + r',\s*' + args_pattern + r'\)', re.S), r'\2 *\1'),
- (KernRe(r'(?:__)?DECLARE_FLEX_ARRAY\s*\(' + args_pattern + r',\s*' + args_pattern + r'\)', re.S), r'\1 \2[]'),
- (KernRe(r'DEFINE_DMA_UNMAP_ADDR\s*\(' + args_pattern + r'\)', re.S), r'dma_addr_t \1'),
- (KernRe(r'DEFINE_DMA_UNMAP_LEN\s*\(' + args_pattern + r'\)', re.S), r'__u32 \1'),
- (KernRe(r'VIRTIO_DECLARE_FEATURES\s*\(' + args_pattern + r'\)', re.S), r'u64 \1; u64 \1_array[VIRTIO_FEATURES_DWORDS]'),
- ]
-
- # Regexes here are guaranteed to have the end limiter matching
- # the start delimiter. Yet, right now, only one replace group
- # is allowed.
-
- sub_nested_prefixes = [
- (re.compile(r'\bSTRUCT_GROUP\('), r'\1'),
- ]
-
- for search, sub in sub_prefixes:
- members = search.sub(sub, members)
-
- nested = NestedMatch()
-
- for search, sub in sub_nested_prefixes:
- members = nested.sub(search, sub, members)
-
- # Keeps the original declaration as-is
- declaration = members
-
- # Split nested struct/union elements
- #
- # This loop was simpler at the original kernel-doc perl version, as
- # while ($members =~ m/$struct_members/) { ... }
- # reads 'members' string on each interaction.
- #
- # Python behavior is different: it parses 'members' only once,
- # creating a list of tuples from the first interaction.
- #
- # On other words, this won't get nested structs.
- #
- # So, we need to have an extra loop on Python to override such
- # re limitation.
-
- while True:
- tuples = struct_members.findall(members)
- if not tuples:
- break
-
- for t in tuples:
- newmember = ""
- maintype = t[0]
- s_ids = t[5]
- content = t[3]
-
- oldmember = "".join(t)
-
- for s_id in s_ids.split(','):
- s_id = s_id.strip()
-
- newmember += f"{maintype} {s_id}; "
- s_id = KernRe(r'[:\[].*').sub('', s_id)
- s_id = KernRe(r'^\s*\**(\S+)\s*').sub(r'\1', s_id)
-
- for arg in content.split(';'):
- arg = arg.strip()
-
- if not arg:
- continue
-
- r = KernRe(r'^([^\(]+\(\*?\s*)([\w\.]*)(\s*\).*)')
- if r.match(arg):
- # Pointer-to-function
- dtype = r.group(1)
- name = r.group(2)
- extra = r.group(3)
-
- if not name:
- continue
-
- if not s_id:
- # Anonymous struct/union
- newmember += f"{dtype}{name}{extra}; "
- else:
- newmember += f"{dtype}{s_id}.{name}{extra}; "
-
- else:
- arg = arg.strip()
- # Handle bitmaps
- arg = KernRe(r':\s*\d+\s*').sub('', arg)
-
- # Handle arrays
- arg = KernRe(r'\[.*\]').sub('', arg)
-
- # Handle multiple IDs
- arg = KernRe(r'\s*,\s*').sub(',', arg)
-
- r = KernRe(r'(.*)\s+([\S+,]+)')
-
- if r.search(arg):
- dtype = r.group(1)
- names = r.group(2)
- else:
- newmember += f"{arg}; "
- continue
-
- for name in names.split(','):
- name = KernRe(r'^\s*\**(\S+)\s*').sub(r'\1', name).strip()
-
- if not name:
- continue
-
- if not s_id:
- # Anonymous struct/union
- newmember += f"{dtype} {name}; "
- else:
- newmember += f"{dtype} {s_id}.{name}; "
-
- members = members.replace(oldmember, newmember)
-
- # Ignore other nested elements, like enums
- members = re.sub(r'(\{[^\{\}]*\})', '', members)
-
- self.create_parameter_list(ln, decl_type, members, ';',
- declaration_name)
- self.check_sections(ln, declaration_name, decl_type)
-
- # Adjust declaration for better display
- declaration = KernRe(r'([\{;])').sub(r'\1\n', declaration)
- declaration = KernRe(r'\}\s+;').sub('};', declaration)
-
- # Better handle inlined enums
- while True:
- r = KernRe(r'(enum\s+\{[^\}]+),([^\n])')
- if not r.search(declaration):
- break
-
- declaration = r.sub(r'\1,\n\2', declaration)
-
- def_args = declaration.split('\n')
- level = 1
- declaration = ""
- for clause in def_args:
-
- clause = clause.strip()
- clause = KernRe(r'\s+').sub(' ', clause, count=1)
-
- if not clause:
- continue
-
- if '}' in clause and level > 1:
- level -= 1
-
- if not KernRe(r'^\s*#').match(clause):
- declaration += "\t" * level
-
- declaration += "\t" + clause + "\n"
- if "{" in clause and "}" not in clause:
- level += 1
-
- self.output_declaration(decl_type, declaration_name,
- definition=declaration,
- purpose=self.entry.declaration_purpose)
-
- def dump_enum(self, ln, proto):
- """
- Stores an enum inside self.entries array.
- """
-
- # Ignore members marked private
- proto = KernRe(r'\/\*\s*private:.*?\/\*\s*public:.*?\*\/', flags=re.S).sub('', proto)
- proto = KernRe(r'\/\*\s*private:.*}', flags=re.S).sub('}', proto)
-
- # Strip comments
- proto = KernRe(r'\/\*.*?\*\/', flags=re.S).sub('', proto)
-
- # Strip #define macros inside enums
- 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 = r.group(1).rstrip()
- #
- # 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 = r.group(2).rstrip()
- #
- # 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.
- """
-
- func_macro = False
- return_type = ''
- decl_type = 'function'
-
- # Prefixes that would be removed
- sub_prefixes = [
- (r"^static +", "", 0),
- (r"^extern +", "", 0),
- (r"^asmlinkage +", "", 0),
- (r"^inline +", "", 0),
- (r"^__inline__ +", "", 0),
- (r"^__inline +", "", 0),
- (r"^__always_inline +", "", 0),
- (r"^noinline +", "", 0),
- (r"^__FORTIFY_INLINE +", "", 0),
- (r"__init +", "", 0),
- (r"__init_or_module +", "", 0),
- (r"__deprecated +", "", 0),
- (r"__flatten +", "", 0),
- (r"__meminit +", "", 0),
- (r"__must_check +", "", 0),
- (r"__weak +", "", 0),
- (r"__sched +", "", 0),
- (r"_noprof", "", 0),
- (r"__printf\s*\(\s*\d*\s*,\s*\d*\s*\) +", "", 0),
- (r"__(?:re)?alloc_size\s*\(\s*\d+\s*(?:,\s*\d+\s*)?\) +", "", 0),
- (r"__diagnose_as\s*\(\s*\S+\s*(?:,\s*\d+\s*)*\) +", "", 0),
- (r"DECL_BUCKET_PARAMS\s*\(\s*(\S+)\s*,\s*(\S+)\s*\)", r"\1, \2", 0),
- (r"__attribute_const__ +", "", 0),
-
- # It seems that Python support for re.X is broken:
- # At least for me (Python 3.13), this didn't work
-# (r"""
-# __attribute__\s*\(\(
-# (?:
-# [\w\s]+ # attribute name
-# (?:\([^)]*\))? # attribute arguments
-# \s*,? # optional comma at the end
-# )+
-# \)\)\s+
-# """, "", re.X),
-
- # So, remove whitespaces and comments from it
- (r"__attribute__\s*\(\((?:[\w\s]+(?:\([^)]*\))?\s*,?)+\)\)\s+", "", 0),
- ]
-
- for search, sub, flags in sub_prefixes:
- prototype = KernRe(search, flags).sub(sub, prototype)
-
- # Macros are a special case, as they change the prototype format
- new_proto = KernRe(r"^#\s*define\s+").sub("", prototype)
- if new_proto != prototype:
- is_define_proto = True
- prototype = new_proto
- else:
- is_define_proto = False
-
- # 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'[a-zA-Z0-9_~:]+'
- prototype_end1 = r'[^\(]*'
- prototype_end2 = r'[^\{]*'
- prototype_end = fr'\(({prototype_end1}|{prototype_end2})\)'
-
- # Besides compiling, Perl qr{[\w\s]+} works as a non-capturing group.
- # So, this needs to be mapped in Python with (?:...)? or (?:...)+
-
- type1 = r'(?:[\w\s]+)?'
- type2 = r'(?:[\w\s]+\*+)+'
-
- found = False
-
- if is_define_proto:
- r = KernRe(r'^()(' + name + r')\s+')
-
- if r.search(prototype):
- return_type = ''
- declaration_name = r.group(2)
- func_macro = True
-
- found = True
-
- if not found:
- patterns = [
- rf'^()({name})\s*{prototype_end}',
- rf'^({type1})\s+({name})\s*{prototype_end}',
- rf'^({type2})\s*({name})\s*{prototype_end}',
- ]
-
- 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
- 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}(). Prototype was for {declaration_name}() instead")
- return
-
- self.check_sections(ln, declaration_name, "function")
-
- self.check_return_section(ln, declaration_name, return_type)
-
- if 'typedef' in return_type:
- self.output_declaration(decl_type, declaration_name,
- typedef=True,
- functiontype=return_type,
- purpose=self.entry.declaration_purpose,
- func_macro=func_macro)
- else:
- self.output_declaration(decl_type, declaration_name,
- typedef=False,
- 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.
- """
-
- typedef_type = r'((?:\s+[\w\*]+\b){0,7}\s+(?:\w+\b|\*+))\s*'
- typedef_ident = r'\*?\s*(\w\S+)\s*'
- typedef_args = r'\s*\((.*)\);'
-
- typedef1 = KernRe(r'typedef' + typedef_type + r'\(' + typedef_ident + r'\)' + typedef_args)
- typedef2 = KernRe(r'typedef' + typedef_type + typedef_ident + typedef_args)
-
- # Strip comments
- proto = KernRe(r'/\*.*?\*/', flags=re.S).sub('', proto)
-
- # 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
-
- decl_type = 'function'
- self.create_parameter_list(ln, decl_type, args, ',', declaration_name)
-
- self.output_declaration(decl_type, declaration_name,
- typedef=True,
- functiontype=return_type,
- purpose=self.entry.declaration_purpose)
- return
-
- # Handle nested parentheses or brackets
- r = KernRe(r'(\(*.\)\s*|\[*.\]\s*);$')
- while r.search(proto):
- proto = r.sub('', proto)
-
- # Parse simple typedefs
- 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 51367c2bfc21..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=
@@ -73,10 +74,7 @@ vmlinux_link()
objs="${objs} .builtin-dtbs.o"
fi
- if is_enabled CONFIG_MODULES; then
- objs="${objs} .vmlinux.export.o"
- fi
-
+ objs="${objs} .vmlinux.export.o"
objs="${objs} init/version-timestamp.o"
if [ "${SRCARCH}" = "um" ]; then
@@ -108,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()
{
@@ -145,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"
@@ -207,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
@@ -227,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
@@ -284,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 0d223b4a9445..b96ec2d379b6 100755
--- a/scripts/min-tool-version.sh
+++ b/scripts/min-tool-version.sh
@@ -24,19 +24,17 @@ gcc)
fi
;;
llvm)
- if [ "$SRCARCH" = s390 -o "$SRCARCH" = x86 ]; then
- echo 15.0.0
- elif [ "$SRCARCH" = loongarch ]; then
+ if [ "$SRCARCH" = loongarch ]; then
echo 18.0.0
else
- echo 13.0.1
+ echo 15.0.0
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/misc-check b/scripts/misc-check
index 84f08da17b2c..40e5a4b01ff4 100755
--- a/scripts/misc-check
+++ b/scripts/misc-check
@@ -45,7 +45,7 @@ check_tracked_ignored_files () {
# does not automatically fix it.
check_missing_include_linux_export_h () {
- git -C "${srctree:-.}" grep --files-with-matches -E 'EXPORT_SYMBOL((_NS)?(_GPL)?|_GPL_FOR_MODULES)\(.*\)' \
+ git -C "${srctree:-.}" grep --files-with-matches -E 'EXPORT_SYMBOL((_NS)?(_GPL)?|_FOR_MODULES)\(.*\)' \
-- '*.[ch]' :^tools/ :^include/linux/export.h |
xargs -r git -C "${srctree:-.}" grep --files-without-match '#include[[:space:]]*<linux/export\.h>' |
xargs -r printf "%s: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing\n" >&2
@@ -58,7 +58,7 @@ check_unnecessary_include_linux_export_h () {
git -C "${srctree:-.}" grep --files-with-matches '#include[[:space:]]*<linux/export\.h>' \
-- '*.[c]' :^tools/ |
- xargs -r git -C "${srctree:-.}" grep --files-without-match -E 'EXPORT_SYMBOL((_NS)?(_GPL)?|_GPL_FOR_MODULES)\(.*\)' |
+ xargs -r git -C "${srctree:-.}" grep --files-without-match -E 'EXPORT_SYMBOL((_NS)?(_GPL)?|_FOR_MODULES)\(.*\)' |
xargs -r printf "%s: warning: EXPORT_SYMBOL() is not used, but #include <linux/export.h> is present\n" >&2
}
diff --git a/scripts/mksysmap b/scripts/mksysmap
index 3accbdb269ac..c4531eacde20 100755
--- a/scripts/mksysmap
+++ b/scripts/mksysmap
@@ -59,6 +59,9 @@
# EXPORT_SYMBOL (namespace)
/ __kstrtabns_/d
+# MODULE_DEVICE_TABLE (symbol name)
+/ __mod_device_table__/d
+
# ---------------------------------------------------------------------------
# Ignored suffixes
# (do not forget '$' after each pattern)
@@ -79,6 +82,9 @@
/ _SDA_BASE_$/d
/ _SDA2_BASE_$/d
+# MODULE_INFO()
+/ __UNIQUE_ID_modinfo[0-9]*$/d
+
# ---------------------------------------------------------------------------
# Ignored patterns
# (symbols that contain the pattern are ignored)
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 00586119a25b..4e99393a35f1 100644
--- a/scripts/mod/file2alias.c
+++ b/scripts/mod/file2alias.c
@@ -94,6 +94,7 @@ module_alias_printf(struct module *mod, bool append_wildcard,
}
}
+ new->builtin_modname = NULL;
list_add_tail(&new->node, &mod->aliases);
}
@@ -1109,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)
{
@@ -1443,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},
@@ -1476,8 +1486,8 @@ void handle_moddevtable(struct module *mod, struct elf_info *info,
{
void *symval;
char *zeros = NULL;
- const char *type, *name;
- size_t typelen;
+ const char *type, *name, *modname;
+ size_t typelen, modnamelen;
static const char *prefix = "__mod_device_table__";
/* We're looking for a section relative symbol */
@@ -1488,10 +1498,20 @@ void handle_moddevtable(struct module *mod, struct elf_info *info,
if (ELF_ST_TYPE(sym->st_info) != STT_OBJECT)
return;
- /* All our symbols are of form __mod_device_table__<type>__<name>. */
+ /* All our symbols are of form __mod_device_table__kmod_<modname>__<type>__<name>. */
if (!strstarts(symname, prefix))
return;
- type = symname + strlen(prefix);
+
+ modname = strstr(symname, "__kmod_");
+ if (!modname)
+ return;
+ modname += strlen("__kmod_");
+
+ type = strstr(modname, "__");
+ if (!type)
+ return;
+ modnamelen = type - modname;
+ type += strlen("__");
name = strstr(type, "__");
if (!name)
@@ -1517,5 +1537,21 @@ void handle_moddevtable(struct module *mod, struct elf_info *info,
}
}
+ if (mod->is_vmlinux) {
+ struct module_alias *alias;
+
+ /*
+ * If this is vmlinux, record the name of the builtin module.
+ * Traverse the linked list in the reverse order, and set the
+ * builtin_modname unless it has already been set in the
+ * previous call.
+ */
+ list_for_each_entry_reverse(alias, &mod->aliases, node) {
+ if (alias->builtin_modname)
+ break;
+ alias->builtin_modname = xstrndup(modname, modnamelen);
+ }
+ }
+
free(zeros);
}
diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c
index 5ca7c268294e..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);
}
}
@@ -2067,11 +2084,26 @@ static void write_if_changed(struct buffer *b, const char *fname)
static void write_vmlinux_export_c_file(struct module *mod)
{
struct buffer buf = { };
+ struct module_alias *alias, *next;
buf_printf(&buf,
"#include <linux/export-internal.h>\n");
add_exported_symbols(&buf, mod);
+
+ buf_printf(&buf,
+ "#include <linux/module.h>\n"
+ "#undef __MODULE_INFO_PREFIX\n"
+ "#define __MODULE_INFO_PREFIX\n");
+
+ list_for_each_entry_safe(alias, next, &mod->aliases, node) {
+ buf_printf(&buf, "MODULE_INFO(%s.alias, \"%s\");\n",
+ alias->builtin_modname, alias->str);
+ list_del(&alias->node);
+ free(alias->builtin_modname);
+ free(alias);
+ }
+
write_if_changed(&buf, ".vmlinux.export.c");
free(buf.p);
}
diff --git a/scripts/mod/modpost.h b/scripts/mod/modpost.h
index 9133e4c3803f..2aecb8f25c87 100644
--- a/scripts/mod/modpost.h
+++ b/scripts/mod/modpost.h
@@ -99,10 +99,12 @@ buf_write(struct buffer *buf, const char *s, int len);
* struct module_alias - auto-generated MODULE_ALIAS()
*
* @node: linked to module::aliases
+ * @modname: name of the builtin module (only for vmlinux)
* @str: a string for MODULE_ALIAS()
*/
struct module_alias {
struct list_head node;
+ char *builtin_modname;
char str[];
};
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 b96538787f3d..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-base=. "${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 abb34ada2508..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
);
}}}}
}}
@@ -202,12 +202,13 @@ pub extern "C" fn {kunit_name}(__kunit_test: *mut ::kernel::bindings::kunit) {{
// This follows the syntax for declaring test metadata in the proposed KTAP v2 spec, which may
// be used for the proposed KUnit test attributes API. Thus hopefully this will make migration
// easier later on.
- ::kernel::kunit::info(format_args!(" # {kunit_name}.location: {real_path}:{line}\n"));
+ ::kernel::kunit::info(fmt!(" # {kunit_name}.location: {real_path}:{line}\n"));
/// 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/selinux/install_policy.sh b/scripts/selinux/install_policy.sh
index db40237e60ce..77368a73f111 100755
--- a/scripts/selinux/install_policy.sh
+++ b/scripts/selinux/install_policy.sh
@@ -74,7 +74,7 @@ cd /etc/selinux/dummy/contexts/files
$SF -F file_contexts /
mounts=`cat /proc/$$/mounts | \
- grep -E "ext[234]|jfs|xfs|reiserfs|jffs2|gfs2|btrfs|f2fs|ocfs2" | \
+ grep -E "ext[234]|jfs|xfs|jffs2|gfs2|btrfs|f2fs|ocfs2" | \
awk '{ print $2 '}`
$SF -F file_contexts $mounts
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-pre-install b/scripts/sphinx-pre-install
deleted file mode 100755
index 3f8d6925e896..000000000000
--- a/scripts/sphinx-pre-install
+++ /dev/null
@@ -1,1056 +0,0 @@
-#!/usr/bin/env perl
-# SPDX-License-Identifier: GPL-2.0-or-later
-use strict;
-
-# Copyright (c) 2017-2020 Mauro Carvalho Chehab <mchehab@kernel.org>
-#
-
-my $prefix = "./";
-$prefix = "$ENV{'srctree'}/" if ($ENV{'srctree'});
-
-my $conf = $prefix . "Documentation/conf.py";
-my $requirement_file = $prefix . "Documentation/sphinx/requirements.txt";
-my $virtenv_prefix = "sphinx_";
-
-#
-# Static vars
-#
-
-my %missing;
-my $system_release;
-my $need = 0;
-my $optional = 0;
-my $need_symlink = 0;
-my $need_sphinx = 0;
-my $need_pip = 0;
-my $need_virtualenv = 0;
-my $rec_sphinx_upgrade = 0;
-my $verbose_warn_install = 1;
-my $install = "";
-my $virtenv_dir = "";
-my $python_cmd = "";
-my $activate_cmd;
-my $min_version;
-my $cur_version;
-my $rec_version = "3.4.3";
-my $latest_avail_ver;
-
-#
-# Command line arguments
-#
-
-my $pdf = 1;
-my $virtualenv = 1;
-my $version_check = 0;
-
-#
-# List of required texlive packages on Fedora and OpenSuse
-#
-
-my %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',
- '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',
- 'ctexhook.sty' => 'texlive-ctex',
-);
-
-#
-# Subroutines that checks if a feature exists
-#
-
-sub check_missing(%)
-{
- my %map = %{$_[0]};
-
- foreach my $prog (sort keys %missing) {
- my $is_optional = $missing{$prog};
-
- # 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 ($is_optional == 2 && !$pdf) {
- $optional--;
- next;
- }
-
- if ($verbose_warn_install) {
- if ($is_optional) {
- print "Warning: better to also install \"$prog\".\n";
- } else {
- print "ERROR: please install \"$prog\", otherwise, build won't work.\n";
- }
- }
- if (defined($map{$prog})) {
- $install .= " " . $map{$prog};
- } else {
- $install .= " " . $prog;
- }
- }
-
- $install =~ s/^\s//;
-}
-
-sub add_package($$)
-{
- my $package = shift;
- my $is_optional = shift;
-
- $missing{$package} = $is_optional;
- if ($is_optional) {
- $optional++;
- } else {
- $need++;
- }
-}
-
-sub check_missing_file($$$)
-{
- my $files = shift;
- my $package = shift;
- my $is_optional = shift;
-
- for (@$files) {
- return if(-e $_);
- }
-
- add_package($package, $is_optional);
-}
-
-sub findprog($)
-{
- foreach(split(/:/, $ENV{PATH})) {
- return "$_/$_[0]" if(-x "$_/$_[0]");
- }
-}
-
-sub find_python_no_venv()
-{
- my $prog = shift;
-
- my $cur_dir = qx(pwd);
- $cur_dir =~ s/\s+$//;
-
- foreach my $dir (split(/:/, $ENV{PATH})) {
- next if ($dir =~ m,($cur_dir)/sphinx,);
- return "$dir/python3" if(-x "$dir/python3");
- }
- foreach my $dir (split(/:/, $ENV{PATH})) {
- next if ($dir =~ m,($cur_dir)/sphinx,);
- return "$dir/python" if(-x "$dir/python");
- }
- return "python";
-}
-
-sub check_program($$)
-{
- my $prog = shift;
- my $is_optional = shift;
-
- return $prog if findprog($prog);
-
- add_package($prog, $is_optional);
-}
-
-sub check_perl_module($$)
-{
- my $prog = shift;
- my $is_optional = shift;
-
- my $err = system("perl -M$prog -e 1 2>/dev/null /dev/null");
- return if ($err == 0);
-
- add_package($prog, $is_optional);
-}
-
-sub check_python_module($$)
-{
- my $prog = shift;
- my $is_optional = shift;
-
- return if (!$python_cmd);
-
- my $err = system("$python_cmd -c 'import $prog' 2>/dev/null /dev/null");
- return if ($err == 0);
-
- add_package($prog, $is_optional);
-}
-
-sub check_rpm_missing($$)
-{
- my @pkgs = @{$_[0]};
- my $is_optional = $_[1];
-
- foreach my $prog(@pkgs) {
- my $err = system("rpm -q '$prog' 2>/dev/null >/dev/null");
- add_package($prog, $is_optional) if ($err);
- }
-}
-
-sub check_pacman_missing($$)
-{
- my @pkgs = @{$_[0]};
- my $is_optional = $_[1];
-
- foreach my $prog(@pkgs) {
- my $err = system("pacman -Q '$prog' 2>/dev/null >/dev/null");
- add_package($prog, $is_optional) if ($err);
- }
-}
-
-sub check_missing_tex($)
-{
- my $is_optional = shift;
- my $kpsewhich = findprog("kpsewhich");
-
- foreach my $prog(keys %texlive) {
- my $package = $texlive{$prog};
- if (!$kpsewhich) {
- add_package($package, $is_optional);
- next;
- }
- my $file = qx($kpsewhich $prog);
- add_package($package, $is_optional) if ($file =~ /^\s*$/);
- }
-}
-
-sub get_sphinx_fname()
-{
- if ($ENV{'SPHINXBUILD'}) {
- return $ENV{'SPHINXBUILD'};
- }
-
- my $fname = "sphinx-build";
- return $fname if findprog($fname);
-
- $fname = "sphinx-build-3";
- if (findprog($fname)) {
- $need_symlink = 1;
- return $fname;
- }
-
- return "";
-}
-
-sub get_sphinx_version($)
-{
- my $cmd = shift;
- my $ver;
-
- open IN, "$cmd --version 2>&1 |";
- while (<IN>) {
- if (m/^\s*sphinx-build\s+([\d\.]+)((\+\/[\da-f]+)|(b\d+))?$/) {
- $ver=$1;
- last;
- }
- # Sphinx 1.2.x uses a different format
- if (m/^\s*Sphinx.*\s+([\d\.]+)$/) {
- $ver=$1;
- last;
- }
- }
- close IN;
- return $ver;
-}
-
-sub check_sphinx()
-{
- open IN, $conf or die "Can't open $conf";
- while (<IN>) {
- if (m/^\s*needs_sphinx\s*=\s*[\'\"]([\d\.]+)[\'\"]/) {
- $min_version=$1;
- last;
- }
- }
- close IN;
-
- die "Can't get needs_sphinx version from $conf" if (!$min_version);
-
- $virtenv_dir = $virtenv_prefix . "latest";
-
- my $sphinx = get_sphinx_fname();
- if ($sphinx eq "") {
- $need_sphinx = 1;
- return;
- }
-
- $cur_version = get_sphinx_version($sphinx);
- die "$sphinx didn't return its version" if (!$cur_version);
-
- if ($cur_version lt $min_version) {
- printf "ERROR: Sphinx version is %s. It should be >= %s\n",
- $cur_version, $min_version;
- $need_sphinx = 1;
- return;
- }
-
- return if ($cur_version lt $rec_version);
-
- # On version check mode, just assume Sphinx has all mandatory deps
- exit (0) if ($version_check);
-}
-
-#
-# Ancillary subroutines
-#
-
-sub catcheck($)
-{
- my $res = "";
- $res = qx(cat $_[0]) if (-r $_[0]);
- return $res;
-}
-
-sub which($)
-{
- my $file = shift;
- my @path = split ":", $ENV{PATH};
-
- foreach my $dir(@path) {
- my $name = $dir.'/'.$file;
- return $name if (-x $name );
- }
- return undef;
-}
-
-#
-# Subroutines that check distro-specific hints
-#
-
-sub give_debian_hints()
-{
- my %map = (
- "python-sphinx" => "python3-sphinx",
- "yaml" => "python3-yaml",
- "ensurepip" => "python3-venv",
- "virtualenv" => "virtualenv",
- "dot" => "graphviz",
- "convert" => "imagemagick",
- "Pod::Usage" => "perl-modules",
- "xelatex" => "texlive-xetex",
- "rsvg-convert" => "librsvg2-bin",
- );
-
- if ($pdf) {
- check_missing_file(["/usr/share/texlive/texmf-dist/tex/latex/ctex/ctexhook.sty"],
- "texlive-lang-chinese", 2);
-
- check_missing_file(["/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf"],
- "fonts-dejavu", 2);
-
- check_missing_file(["/usr/share/fonts/noto-cjk/NotoSansCJK-Regular.ttc",
- "/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc",
- "/usr/share/fonts/opentype/noto/NotoSerifCJK-Regular.ttc"],
- "fonts-noto-cjk", 2);
- }
-
- check_program("dvipng", 2) if ($pdf);
- check_missing(\%map);
-
- return if (!$need && !$optional);
- printf("You should run:\n") if ($verbose_warn_install);
- printf("\n\tsudo apt-get install $install\n");
-}
-
-sub give_redhat_hints()
-{
- my %map = (
- "python-sphinx" => "python3-sphinx",
- "yaml" => "python3-pyyaml",
- "virtualenv" => "python3-virtualenv",
- "dot" => "graphviz",
- "convert" => "ImageMagick",
- "Pod::Usage" => "perl-Pod-Usage",
- "xelatex" => "texlive-xetex-bin",
- "rsvg-convert" => "librsvg2-tools",
- );
-
- my @fedora26_opt_pkgs = (
- "graphviz-gd", # Fedora 26: needed for PDF support
- );
-
- my @fedora_tex_pkgs = (
- "texlive-collection-fontsrecommended",
- "texlive-collection-latex",
- "texlive-xecjk",
- "dejavu-sans-fonts",
- "dejavu-serif-fonts",
- "dejavu-sans-mono-fonts",
- );
-
- #
- # Checks valid for RHEL/CentOS version 7.x.
- #
- my $old = 0;
- my $rel;
- my $noto_sans_redhat = "google-noto-sans-cjk-ttc-fonts";
- $rel = $1 if ($system_release =~ /(release|Linux)\s+(\d+)/);
-
- if (!($system_release =~ /Fedora/)) {
- $map{"virtualenv"} = "python-virtualenv";
-
- if ($rel && $rel < 8) {
- $old = 1;
- $pdf = 0;
-
- printf("Note: texlive packages on RHEL/CENTOS <= 7 are incomplete. Can't support PDF output\n");
- printf("If you want to build PDF, please read:\n");
- printf("\thttps://www.systutorials.com/241660/how-to-install-tex-live-on-centos-7-linux/\n");
- }
- } else {
- if ($rel && $rel < 26) {
- $old = 1;
- }
- if ($rel && $rel >= 38) {
- $noto_sans_redhat = "google-noto-sans-cjk-fonts";
- }
- }
- if (!$rel) {
- printf("Couldn't identify release number\n");
- $old = 1;
- $pdf = 0;
- }
-
- if ($pdf) {
- check_missing_file(["/usr/share/fonts/google-noto-cjk/NotoSansCJK-Regular.ttc",
- "/usr/share/fonts/google-noto-sans-cjk-fonts/NotoSansCJK-Regular.ttc"],
- $noto_sans_redhat, 2);
- }
-
- check_rpm_missing(\@fedora26_opt_pkgs, 2) if ($pdf && !$old);
- check_rpm_missing(\@fedora_tex_pkgs, 2) if ($pdf);
- check_missing_tex(2) if ($pdf);
- check_missing(\%map);
-
- return if (!$need && !$optional);
-
- if (!$old) {
- # dnf, for Fedora 18+
- printf("You should run:\n") if ($verbose_warn_install);
- printf("\n\tsudo dnf install -y $install\n");
- } else {
- # yum, for RHEL (and clones) or Fedora version < 18
- printf("You should run:\n") if ($verbose_warn_install);
- printf("\n\tsudo yum install -y $install\n");
- }
-}
-
-sub give_opensuse_hints()
-{
- my %map = (
- "python-sphinx" => "python3-sphinx",
- "yaml" => "python3-pyyaml",
- "virtualenv" => "python3-virtualenv",
- "dot" => "graphviz",
- "convert" => "ImageMagick",
- "Pod::Usage" => "perl-Pod-Usage",
- "xelatex" => "texlive-xetex-bin",
- );
-
- # On Tumbleweed, this package is also named rsvg-convert
- $map{"rsvg-convert"} = "rsvg-view" if (!($system_release =~ /Tumbleweed/));
-
- my @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",
- );
-
- $map{"latexmk"} = "texlive-latexmk-bin";
-
- # 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
-
- check_rpm_missing(\@suse_tex_pkgs, 2) if ($pdf);
- check_missing_tex(2) if ($pdf);
- check_missing(\%map);
-
- return if (!$need && !$optional);
- printf("You should run:\n") if ($verbose_warn_install);
- printf("\n\tsudo zypper install --no-recommends $install\n");
-}
-
-sub give_mageia_hints()
-{
- my %map = (
- "python-sphinx" => "python3-sphinx",
- "yaml" => "python3-yaml",
- "virtualenv" => "python3-virtualenv",
- "dot" => "graphviz",
- "convert" => "ImageMagick",
- "Pod::Usage" => "perl-Pod-Usage",
- "xelatex" => "texlive",
- "rsvg-convert" => "librsvg2",
- );
-
- my @tex_pkgs = (
- "texlive-fontsextra",
- );
-
- $map{"latexmk"} = "texlive-collection-basic";
-
- my $packager_cmd;
- my $noto_sans;
- if ($system_release =~ /OpenMandriva/) {
- $packager_cmd = "dnf install";
- $noto_sans = "noto-sans-cjk-fonts";
- @tex_pkgs = ( "texlive-collection-fontsextra" );
- } else {
- $packager_cmd = "urpmi";
- $noto_sans = "google-noto-sans-cjk-ttc-fonts";
- }
-
-
- if ($pdf) {
- check_missing_file(["/usr/share/fonts/google-noto-cjk/NotoSansCJK-Regular.ttc",
- "/usr/share/fonts/TTF/NotoSans-Regular.ttf"],
- $noto_sans, 2);
- }
-
- check_rpm_missing(\@tex_pkgs, 2) if ($pdf);
- check_missing(\%map);
-
- return if (!$need && !$optional);
- printf("You should run:\n") if ($verbose_warn_install);
- printf("\n\tsudo $packager_cmd $install\n");
-}
-
-sub give_arch_linux_hints()
-{
- my %map = (
- "yaml" => "python-yaml",
- "virtualenv" => "python-virtualenv",
- "dot" => "graphviz",
- "convert" => "imagemagick",
- "xelatex" => "texlive-xetex",
- "latexmk" => "texlive-core",
- "rsvg-convert" => "extra/librsvg",
- );
-
- my @archlinux_tex_pkgs = (
- "texlive-core",
- "texlive-latexextra",
- "ttf-dejavu",
- );
- check_pacman_missing(\@archlinux_tex_pkgs, 2) if ($pdf);
-
- if ($pdf) {
- check_missing_file(["/usr/share/fonts/noto-cjk/NotoSansCJK-Regular.ttc"],
- "noto-fonts-cjk", 2);
- }
-
- check_missing(\%map);
-
- return if (!$need && !$optional);
- printf("You should run:\n") if ($verbose_warn_install);
- printf("\n\tsudo pacman -S $install\n");
-}
-
-sub give_gentoo_hints()
-{
- my %map = (
- "yaml" => "dev-python/pyyaml",
- "virtualenv" => "dev-python/virtualenv",
- "dot" => "media-gfx/graphviz",
- "convert" => "media-gfx/imagemagick",
- "xelatex" => "dev-texlive/texlive-xetex media-fonts/dejavu",
- "rsvg-convert" => "gnome-base/librsvg",
- );
-
- check_missing_file(["/usr/share/fonts/dejavu/DejaVuSans.ttf"],
- "media-fonts/dejavu", 2) if ($pdf);
-
- if ($pdf) {
- check_missing_file(["/usr/share/fonts/noto-cjk/NotoSansCJKsc-Regular.otf",
- "/usr/share/fonts/noto-cjk/NotoSerifCJK-Regular.ttc"],
- "media-fonts/noto-cjk", 2);
- }
-
- check_missing(\%map);
-
- return if (!$need && !$optional);
-
- printf("You should run:\n") if ($verbose_warn_install);
- printf("\n");
-
- my $imagemagick = "media-gfx/imagemagick svg png";
- my $cairo = "media-gfx/graphviz cairo pdf";
- my $portage_imagemagick = "/etc/portage/package.use/imagemagick";
- my $portage_cairo = "/etc/portage/package.use/graphviz";
-
- if (qx(grep imagemagick $portage_imagemagick 2>/dev/null) eq "") {
- printf("\tsudo su -c 'echo \"$imagemagick\" > $portage_imagemagick'\n")
- }
- if (qx(grep graphviz $portage_cairo 2>/dev/null) eq "") {
- printf("\tsudo su -c 'echo \"$cairo\" > $portage_cairo'\n");
- }
-
- printf("\tsudo emerge --ask $install\n");
-
-}
-
-sub check_distros()
-{
- # Distro-specific hints
- if ($system_release =~ /Red Hat Enterprise Linux/) {
- give_redhat_hints;
- return;
- }
- if ($system_release =~ /CentOS/) {
- give_redhat_hints;
- return;
- }
- if ($system_release =~ /Scientific Linux/) {
- give_redhat_hints;
- return;
- }
- if ($system_release =~ /Oracle Linux Server/) {
- give_redhat_hints;
- return;
- }
- if ($system_release =~ /Fedora/) {
- give_redhat_hints;
- return;
- }
- if ($system_release =~ /Ubuntu/) {
- give_debian_hints;
- return;
- }
- if ($system_release =~ /Debian/) {
- give_debian_hints;
- return;
- }
- if ($system_release =~ /openSUSE/) {
- give_opensuse_hints;
- return;
- }
- if ($system_release =~ /Mageia/) {
- give_mageia_hints;
- return;
- }
- if ($system_release =~ /OpenMandriva/) {
- give_mageia_hints;
- return;
- }
- if ($system_release =~ /Arch Linux/) {
- give_arch_linux_hints;
- return;
- }
- if ($system_release =~ /Gentoo/) {
- give_gentoo_hints;
- return;
- }
-
- #
- # Fall-back to generic hint code for other distros
- # That's far from ideal, specially for LaTeX dependencies.
- #
- my %map = (
- "sphinx-build" => "sphinx"
- );
- check_missing_tex(2) if ($pdf);
- check_missing(\%map);
- print "I don't know distro $system_release.\n";
- print "So, I can't provide you a hint with the install procedure.\n";
- print "There are likely missing dependencies.\n";
-}
-
-#
-# Common dependencies
-#
-
-sub deactivate_help()
-{
- printf "\n If you want to exit the virtualenv, you can use:\n";
- printf "\tdeactivate\n";
-}
-
-sub get_virtenv()
-{
- my $ver;
- my $min_activate = "$ENV{'PWD'}/${virtenv_prefix}${min_version}/bin/activate";
- my @activates = glob "$ENV{'PWD'}/${virtenv_prefix}*/bin/activate";
-
- @activates = sort {$b cmp $a} @activates;
-
- foreach my $f (@activates) {
- next if ($f lt $min_activate);
-
- my $sphinx_cmd = $f;
- $sphinx_cmd =~ s/activate/sphinx-build/;
- next if (! -f $sphinx_cmd);
-
- my $ver = get_sphinx_version($sphinx_cmd);
-
- if (!$ver) {
- $f =~ s#/bin/activate##;
- print("Warning: virtual environment $f is not working.\nPython version upgrade? Remove it with:\n\n\trm -rf $f\n\n");
- }
-
- if ($need_sphinx && ($ver ge $min_version)) {
- return ($f, $ver);
- } elsif ($ver gt $cur_version) {
- return ($f, $ver);
- }
- }
- return ("", "");
-}
-
-sub recommend_sphinx_upgrade()
-{
- my $venv_ver;
-
- # Avoid running sphinx-builds from venv if $cur_version is good
- if ($cur_version && ($cur_version ge $rec_version)) {
- $latest_avail_ver = $cur_version;
- return;
- }
-
- # Get the highest version from sphinx_*/bin/sphinx-build and the
- # corresponding command to activate the venv/virtenv
- ($activate_cmd, $venv_ver) = get_virtenv();
-
- # Store the highest version from Sphinx existing virtualenvs
- if (($activate_cmd ne "") && ($venv_ver gt $cur_version)) {
- $latest_avail_ver = $venv_ver;
- } else {
- $latest_avail_ver = $cur_version if ($cur_version);
- }
-
- # As we don't know package version of Sphinx, and there's no
- # virtual environments, don't check if upgrades are needed
- if (!$virtualenv) {
- return if (!$latest_avail_ver);
- }
-
- # Either there are already a virtual env or a new one should be created
- $need_pip = 1;
-
- return if (!$latest_avail_ver);
-
- # Return if the reason is due to an upgrade or not
- if ($latest_avail_ver lt $rec_version) {
- $rec_sphinx_upgrade = 1;
- }
-
- return $latest_avail_ver;
-}
-
-#
-# 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
-sub recommend_sphinx_version($)
-{
- my $virtualenv_cmd = shift;
-
- # Version is OK. Nothing to do.
- if ($cur_version && ($cur_version ge $rec_version)) {
- return;
- };
-
- if (!$need_sphinx) {
- # sphinx-build is present and its version is >= $min_version
-
- #only recommend enabling a newer virtenv version if makes sense.
- if ($latest_avail_ver gt $cur_version) {
- printf "\nYou may also use the newer Sphinx version $latest_avail_ver with:\n";
- printf "\tdeactivate\n" if ($ENV{'PWD'} =~ /${virtenv_prefix}/);
- printf "\t. $activate_cmd\n";
- deactivate_help();
-
- return;
- }
- return if ($latest_avail_ver ge $rec_version);
- }
-
- if (!$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.
- return if (!$latest_avail_ver);
-
- # 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 ($latest_avail_ver gt $cur_version) {
- printf "\nYou may also use the Sphinx virtualenv version $latest_avail_ver with:\n";
- printf "\tdeactivate\n" if ($ENV{'PWD'} =~ /${virtenv_prefix}/);
- printf "\t. $activate_cmd\n";
- deactivate_help();
-
- return;
- }
- print "\n";
- } else {
- $need++ if ($need_sphinx);
- }
-
- # Suggest newer versions if current ones are too old
- if ($latest_avail_ver && $latest_avail_ver ge $min_version) {
- # If there's a good enough version, ask the user to enable it
- if ($latest_avail_ver ge $rec_version) {
- printf "\nNeed to activate Sphinx (version $latest_avail_ver) on virtualenv with:\n";
- printf "\t. $activate_cmd\n";
- deactivate_help();
-
- return;
- }
-
- # Version is above the minimal required one, but may be
- # below the recommended one. So, print warnings/notes
-
- if ($latest_avail_ver lt $rec_version) {
- print "Warning: It is recommended at least Sphinx version $rec_version.\n";
- }
- }
-
- # At this point, either it needs Sphinx or upgrade is recommended,
- # both via pip
-
- if ($rec_sphinx_upgrade) {
- if (!$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\n";
- }
-
- $python_cmd = find_python_no_venv();
-
- printf "\t$virtualenv_cmd $virtenv_dir\n";
-
- printf "\t. $virtenv_dir/bin/activate\n";
- printf "\tpip install -r $requirement_file\n";
- deactivate_help();
-
- printf "\n2) As a package with:\n";
-
- my $old_need = $need;
- my $old_optional = $optional;
- %missing = ();
- $pdf = 0;
- $optional = 0;
- $install = "";
- $verbose_warn_install = 0;
-
- add_package("python-sphinx", 0);
-
- check_distros();
-
- $need = $old_need;
- $optional = $old_optional;
-
- printf "\n Please note that Sphinx >= 3.0 will currently produce false-positive\n";
- printf " warning when the same name is used for more than one type (functions,\n";
- printf " structs, enums,...). This is known Sphinx bug. For more details, see:\n";
- printf "\thttps://github.com/sphinx-doc/sphinx/pull/8313\n";
-}
-
-sub check_needs()
-{
- # Check if Sphinx is already accessible from current environment
- check_sphinx();
-
- if ($system_release) {
- print "Detected OS: $system_release.\n";
- } else {
- print "Unknown OS\n";
- }
- printf "Sphinx version: %s\n\n", $cur_version if ($cur_version);
-
- # Check python command line, trying first python3
- $python_cmd = findprog("python3");
- $python_cmd = check_program("python", 0) if (!$python_cmd);
-
- # Check the type of virtual env, depending on Python version
- if ($python_cmd) {
- if ($virtualenv) {
- my $tmp = qx($python_cmd --version 2>&1);
- if ($tmp =~ m/(\d+\.)(\d+\.)/) {
- if ($1 < 3) {
- # Fail if it finds python2 (or worse)
- die "Python 3 is required to build the kernel docs\n";
- }
- if ($1 == 3 && $2 < 3) {
- # Need Python 3.3 or upper for venv
- $need_virtualenv = 1;
- }
- } else {
- die "Warning: couldn't identify $python_cmd version!";
- }
- } else {
- add_package("python-sphinx", 0);
- }
- }
-
- my $venv_ver = recommend_sphinx_upgrade();
-
- my $virtualenv_cmd;
-
- if ($need_pip) {
- # Set virtualenv command line, if python < 3.3
- if ($need_virtualenv) {
- $virtualenv_cmd = findprog("virtualenv-3");
- $virtualenv_cmd = findprog("virtualenv-3.5") if (!$virtualenv_cmd);
- if (!$virtualenv_cmd) {
- check_program("virtualenv", 0);
- $virtualenv_cmd = "virtualenv";
- }
- } else {
- $virtualenv_cmd = "$python_cmd -m venv";
- check_python_module("ensurepip", 0);
- }
- }
-
- # Check for needed programs/tools
- check_perl_module("Pod::Usage", 0);
- check_python_module("yaml", 0);
- check_program("make", 0);
- check_program("gcc", 0);
- check_program("dot", 1);
- check_program("convert", 1);
-
- # Extra PDF files - should use 2 for is_optional
- check_program("xelatex", 2) if ($pdf);
- check_program("rsvg-convert", 2) if ($pdf);
- check_program("latexmk", 2) if ($pdf);
-
- # Do distro-specific checks and output distro-install commands
- check_distros();
-
- if (!$python_cmd) {
- if ($need == 1) {
- die "Can't build as $need mandatory dependency is missing";
- } elsif ($need) {
- die "Can't build as $need mandatory dependencies are missing";
- }
- }
-
- # Check if sphinx-build is called sphinx-build-3
- if ($need_symlink) {
- printf "\tsudo ln -sf %s /usr/bin/sphinx-build\n\n",
- which("sphinx-build-3");
- }
-
- recommend_sphinx_version($virtualenv_cmd);
- printf "\n";
-
- print "All optional dependencies are met.\n" if (!$optional);
-
- if ($need == 1) {
- die "Can't build as $need mandatory dependency is missing";
- } elsif ($need) {
- die "Can't build as $need mandatory dependencies are missing";
- }
-
- print "Needed package dependencies are met.\n";
-}
-
-#
-# Main
-#
-
-while (@ARGV) {
- my $arg = shift(@ARGV);
-
- if ($arg eq "--no-virtualenv") {
- $virtualenv = 0;
- } elsif ($arg eq "--no-pdf"){
- $pdf = 0;
- } elsif ($arg eq "--version-check"){
- $version_check = 1;
- } else {
- print "Usage:\n\t$0 <--no-virtualenv> <--no-pdf> <--version-check>\n\n";
- print "Where:\n";
- print "\t--no-virtualenv\t- Recommend installing Sphinx instead of using a virtualenv\n";
- print "\t--version-check\t- if version is compatible, don't check for missing dependencies\n";
- print "\t--no-pdf\t- don't check for dependencies required to build PDF docs\n\n";
- exit -1;
- }
-}
-
-#
-# Determine the system type. There's no standard 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
-#
-
-$system_release = qx(lsb_release -d) if which("lsb_release");
-$system_release =~ s/Description:\s*// if ($system_release);
-$system_release = catcheck("/etc/system-release") if !$system_release;
-$system_release = catcheck("/etc/redhat-release") if !$system_release;
-$system_release = catcheck("/etc/lsb-release") if !$system_release;
-$system_release = catcheck("/etc/gentoo-release") if !$system_release;
-
-# This seems more common than LSB these days
-if (!$system_release) {
- my %os_var;
- if (open IN, "cat /etc/os-release|") {
- while (<IN>) {
- if (m/^([\w\d\_]+)=\"?([^\"]*)\"?\n/) {
- $os_var{$1}=$2;
- }
- }
- $system_release = $os_var{"NAME"};
- if (defined($os_var{"VERSION_ID"})) {
- $system_release .= " " . $os_var{"VERSION_ID"} if (defined($os_var{"VERSION_ID"}));
- } else {
- $system_release .= " " . $os_var{"VERSION"};
- }
- }
-}
-$system_release = catcheck("/etc/issue") if !$system_release;
-$system_release =~ s/\s+$//;
-
-check_needs;
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