summaryrefslogtreecommitdiff
path: root/scripts
diff options
context:
space:
mode:
Diffstat (limited to 'scripts')
-rw-r--r--scripts/Makefile.autofdo6
-rw-r--r--scripts/Makefile.build1
-rw-r--r--scripts/Makefile.kasan2
-rw-r--r--scripts/Makefile.lib18
-rw-r--r--scripts/Makefile.modfinal10
-rw-r--r--scripts/Makefile.thinlto40
-rw-r--r--scripts/Makefile.vmlinux_a82
-rw-r--r--scripts/Makefile.warn10
-rwxr-xr-xscripts/checkpatch.pl5
-rwxr-xr-xscripts/clang-tools/gen_compile_commands.py2
-rwxr-xr-xscripts/clang-tools/run-clang-tools.py15
-rwxr-xr-xscripts/dtc/dt-check-style1192
-rw-r--r--scripts/dtc/dt-style-selftest/bad/dts-spaces.dts12
-rw-r--r--scripts/dtc/dt-style-selftest/bad/yaml-child-addr-order.yaml41
-rw-r--r--scripts/dtc/dt-style-selftest/bad/yaml-child-name-order.yaml37
-rw-r--r--scripts/dtc/dt-style-selftest/bad/yaml-cont-align.yaml30
-rw-r--r--scripts/dtc/dt-style-selftest/bad/yaml-digit-node-order.yaml37
-rw-r--r--scripts/dtc/dt-style-selftest/bad/yaml-hex-case.yaml29
-rw-r--r--scripts/dtc/dt-style-selftest/bad/yaml-indent-strict.yaml29
-rw-r--r--scripts/dtc/dt-style-selftest/bad/yaml-label-in-string.yaml30
-rw-r--r--scripts/dtc/dt-style-selftest/bad/yaml-line-length.yaml29
-rw-r--r--scripts/dtc/dt-style-selftest/bad/yaml-mixed-indent.yaml29
-rw-r--r--scripts/dtc/dt-style-selftest/bad/yaml-multi-close.yaml35
-rw-r--r--scripts/dtc/dt-style-selftest/bad/yaml-node-close.yaml31
-rw-r--r--scripts/dtc/dt-style-selftest/bad/yaml-prop-order.yaml29
-rw-r--r--scripts/dtc/dt-style-selftest/bad/yaml-prop-pairing.yaml33
-rw-r--r--scripts/dtc/dt-style-selftest/bad/yaml-required-blank.yaml33
-rw-r--r--scripts/dtc/dt-style-selftest/bad/yaml-tab.yaml29
-rw-r--r--scripts/dtc/dt-style-selftest/bad/yaml-trailing-comment.yaml26
-rw-r--r--scripts/dtc/dt-style-selftest/bad/yaml-trailing-ws.yaml29
-rw-r--r--scripts/dtc/dt-style-selftest/bad/yaml-unclosed-comment.yaml30
-rw-r--r--scripts/dtc/dt-style-selftest/bad/yaml-unit-addr-prefix.yaml29
-rw-r--r--scripts/dtc/dt-style-selftest/bad/yaml-unit-addr.yaml29
-rw-r--r--scripts/dtc/dt-style-selftest/bad/yaml-unused-label.yaml29
-rw-r--r--scripts/dtc/dt-style-selftest/bad/yaml-value-ws-multiline.yaml27
-rw-r--r--scripts/dtc/dt-style-selftest/bad/yaml-value-ws.yaml29
-rw-r--r--scripts/dtc/dt-style-selftest/expected/dts-spaces.dts.txt2
-rw-r--r--scripts/dtc/dt-style-selftest/expected/yaml-child-addr-order.yaml.txt2
-rw-r--r--scripts/dtc/dt-style-selftest/expected/yaml-child-name-order.yaml.txt2
-rw-r--r--scripts/dtc/dt-style-selftest/expected/yaml-cont-align.yaml.txt2
-rw-r--r--scripts/dtc/dt-style-selftest/expected/yaml-digit-node-order.yaml.txt2
-rw-r--r--scripts/dtc/dt-style-selftest/expected/yaml-hex-case.yaml.txt2
-rw-r--r--scripts/dtc/dt-style-selftest/expected/yaml-indent-strict.yaml.txt2
-rw-r--r--scripts/dtc/dt-style-selftest/expected/yaml-label-in-string.yaml.txt2
-rw-r--r--scripts/dtc/dt-style-selftest/expected/yaml-line-length.yaml.txt2
-rw-r--r--scripts/dtc/dt-style-selftest/expected/yaml-mixed-indent.yaml.txt3
-rw-r--r--scripts/dtc/dt-style-selftest/expected/yaml-multi-close.yaml.txt3
-rw-r--r--scripts/dtc/dt-style-selftest/expected/yaml-node-close.yaml.txt2
-rw-r--r--scripts/dtc/dt-style-selftest/expected/yaml-prop-order.yaml.txt2
-rw-r--r--scripts/dtc/dt-style-selftest/expected/yaml-prop-pairing.yaml.txt3
-rw-r--r--scripts/dtc/dt-style-selftest/expected/yaml-required-blank.yaml.txt3
-rw-r--r--scripts/dtc/dt-style-selftest/expected/yaml-tab.yaml.txt2
-rw-r--r--scripts/dtc/dt-style-selftest/expected/yaml-trailing-comment.yaml.txt2
-rw-r--r--scripts/dtc/dt-style-selftest/expected/yaml-trailing-ws.yaml.txt2
-rw-r--r--scripts/dtc/dt-style-selftest/expected/yaml-unclosed-comment.yaml.txt2
-rw-r--r--scripts/dtc/dt-style-selftest/expected/yaml-unit-addr-prefix.yaml.txt2
-rw-r--r--scripts/dtc/dt-style-selftest/expected/yaml-unit-addr.yaml.txt2
-rw-r--r--scripts/dtc/dt-style-selftest/expected/yaml-unused-label.yaml.txt2
-rw-r--r--scripts/dtc/dt-style-selftest/expected/yaml-value-ws-multiline.yaml.txt2
-rw-r--r--scripts/dtc/dt-style-selftest/expected/yaml-value-ws.yaml.txt2
-rw-r--r--scripts/dtc/dt-style-selftest/good/dts-cont-align.dts26
-rw-r--r--scripts/dtc/dt-style-selftest/good/dts-tab.dts29
-rw-r--r--scripts/dtc/dt-style-selftest/good/yaml-4space.yaml41
-rw-r--r--scripts/dtc/dt-style-selftest/good/yaml-tricky-parsing.yaml57
-rwxr-xr-xscripts/dtc/dt-style-selftest/run.sh71
-rw-r--r--scripts/gdb/linux/interrupts.py106
-rw-r--r--scripts/gdb/linux/timerlist.py4
-rwxr-xr-xscripts/generate_rust_analyzer.py38
-rw-r--r--scripts/generate_rust_target.rs2
-rwxr-xr-xscripts/jobserver-exec4
-rw-r--r--scripts/kconfig/conf.c6
-rwxr-xr-xscripts/kconfig/kconfig-sym-check.pl132
-rw-r--r--scripts/kconfig/tests/no_write_if_dep_unmet/__init__.py2
-rwxr-xr-xscripts/livepatch/klp-build250
-rwxr-xr-xscripts/min-tool-version.sh8
-rw-r--r--scripts/mod/modpost.c28
-rw-r--r--scripts/package/PKGBUILD3
-rw-r--r--scripts/package/kernel.spec2
-rwxr-xr-xscripts/timer_migration_tree.py122
-rwxr-xr-xscripts/update-intel-ucode-defs.py130
80 files changed, 3005 insertions, 212 deletions
diff --git a/scripts/Makefile.autofdo b/scripts/Makefile.autofdo
index 1caf2457e585..1442043da139 100644
--- a/scripts/Makefile.autofdo
+++ b/scripts/Makefile.autofdo
@@ -3,14 +3,18 @@
# Enable available and selected Clang AutoFDO features.
CFLAGS_AUTOFDO_CLANG := -fdebug-info-for-profiling -mllvm -enable-fs-discriminator=true -mllvm -improved-fs-discriminator=true
+RUSTFLAGS_AUTOFDO_CLANG := $(if $(call rustc-min-version,109800),-Zdebuginfo-for-profiling,-Zdebug-info-for-profiling) -Cllvm-args=-enable-fs-discriminator=true -Cllvm-args=-improved-fs-discriminator=true
ifndef CONFIG_DEBUG_INFO
CFLAGS_AUTOFDO_CLANG += -gmlt
+ RUSTFLAGS_AUTOFDO_CLANG += -Cdebuginfo=line-tables-only
endif
ifdef CLANG_AUTOFDO_PROFILE
CFLAGS_AUTOFDO_CLANG += -fprofile-sample-use=$(CLANG_AUTOFDO_PROFILE) -ffunction-sections
CFLAGS_AUTOFDO_CLANG += -fsplit-machine-functions
+ RUSTFLAGS_AUTOFDO_CLANG += -Zprofile-sample-use=$(CLANG_AUTOFDO_PROFILE) -Zfunction-sections=y
+ RUSTFLAGS_AUTOFDO_CLANG += -Cllvm-args=-split-machine-functions
endif
ifdef CONFIG_LTO_CLANG_THIN
@@ -21,4 +25,4 @@ ifdef CONFIG_LTO_CLANG_THIN
KBUILD_LDFLAGS += -plugin-opt=-split-machine-functions
endif
-export CFLAGS_AUTOFDO_CLANG
+export CFLAGS_AUTOFDO_CLANG RUSTFLAGS_AUTOFDO_CLANG
diff --git a/scripts/Makefile.build b/scripts/Makefile.build
index 3498d25b15e8..911745743246 100644
--- a/scripts/Makefile.build
+++ b/scripts/Makefile.build
@@ -329,6 +329,7 @@ rust_common_cmd = \
-Zcrate-attr=no_std \
-Zcrate-attr='feature($(rust_allowed_features))' \
-Zunstable-options --extern pin_init --extern kernel \
+ --extern zerocopy --extern zerocopy_derive \
--crate-type rlib -L $(objtree)/rust/ \
--sysroot=/dev/null \
--out-dir $(dir $@) --emit=dep-info=$(depfile)
diff --git a/scripts/Makefile.kasan b/scripts/Makefile.kasan
index 0ba2aac3b8dc..91504e81247a 100644
--- a/scripts/Makefile.kasan
+++ b/scripts/Makefile.kasan
@@ -71,8 +71,6 @@ ifdef CONFIG_KASAN_SW_TAGS
CFLAGS_KASAN := -fsanitize=kernel-hwaddress
-# This sets flags that will enable SW_TAGS KASAN once enabled in Rust. These
-# will not work today, and is guarded against in dependencies for CONFIG_RUST.
RUSTFLAGS_KASAN := -Zsanitizer=kernel-hwaddress \
-Zsanitizer-recover=kernel-hwaddress
diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib
index 0718e39cedda..0a4fdd8bd975 100644
--- a/scripts/Makefile.lib
+++ b/scripts/Makefile.lib
@@ -123,6 +123,9 @@ ifeq ($(CONFIG_AUTOFDO_CLANG),y)
_c_flags += $(if $(patsubst n%,, \
$(AUTOFDO_PROFILE_$(target-stem).o)$(AUTOFDO_PROFILE)$(is-kernel-object)), \
$(CFLAGS_AUTOFDO_CLANG))
+_rust_flags += $(if $(patsubst n%,, \
+ $(AUTOFDO_PROFILE_$(target-stem).o)$(AUTOFDO_PROFILE)$(is-kernel-object)), \
+ $(RUSTFLAGS_AUTOFDO_CLANG))
endif
#
@@ -187,7 +190,11 @@ objtool-args-$(CONFIG_HAVE_JUMP_LABEL_HACK) += --hacks=jump_label
objtool-args-$(CONFIG_HAVE_NOINSTR_HACK) += --hacks=noinstr
objtool-args-$(CONFIG_MITIGATION_CALL_DEPTH_TRACKING) += --hacks=skylake
objtool-args-$(CONFIG_X86_KERNEL_IBT) += --ibt
-objtool-args-$(CONFIG_FINEIBT) += --cfi
+objtool-args-$(CONFIG_CALL_PADDING) += --prefix=$(CONFIG_FUNCTION_PADDING_BYTES)
+ifdef CONFIG_CALL_PADDING
+objtool-args-$(CONFIG_CFI) += --cfi
+objtool-args-$(CONFIG_FINEIBT) += --fineibt
+endif
objtool-args-$(CONFIG_FTRACE_MCOUNT_USE_OBJTOOL) += --mcount
ifdef CONFIG_FTRACE_MCOUNT_USE_OBJTOOL
objtool-args-$(CONFIG_HAVE_OBJTOOL_NOP_MCOUNT) += --mnop
@@ -200,7 +207,6 @@ objtool-args-$(CONFIG_STACK_VALIDATION) += --stackval
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 = $(objtool-args-y) \
@@ -249,6 +255,13 @@ ifdef CONFIG_LTO_CLANG
cmd_ld_single = $(if $(objtool-enabled)$(is-single-obj-m), ; $(LD) $(ld_flags) -r -o $(tmp-target) $@; mv $(tmp-target) $@)
endif
+ifdef CONFIG_LTO_CLANG_THIN_DIST
+# Save the _c_flags, sliently.
+quiet_cmd_save_c_flags =
+ saved_c_flags = $(_c_flags) $(modkern_cflags)
+ cmd_save_c_flags = printf '\n%s\n' 'saved_c_flags_$@ := $(call escsq,$(saved_c_flags))' >> $(dot-target).cmd
+endif
+
quiet_cmd_cc_o_c = CC $(quiet_modtag) $@
cmd_cc_o_c = $(CC) $(c_flags) -c -o $@ $< \
$(cmd_ld_single) \
@@ -256,6 +269,7 @@ quiet_cmd_cc_o_c = CC $(quiet_modtag) $@
define rule_cc_o_c
$(call cmd_and_fixdep,cc_o_c)
+ $(call cmd,save_c_flags)
$(call cmd,checksrc)
$(call cmd,checkdoc)
$(call cmd,gen_objtooldep)
diff --git a/scripts/Makefile.modfinal b/scripts/Makefile.modfinal
index adcbcde16a07..01a37ec872b9 100644
--- a/scripts/Makefile.modfinal
+++ b/scripts/Makefile.modfinal
@@ -46,17 +46,9 @@ quiet_cmd_btf_ko = BTF [M] $@
$(CONFIG_SHELL) $(srctree)/scripts/gen-btf.sh --btf_base $(objtree)/vmlinux $@; \
fi;
-# Same as newer-prereqs, but allows to exclude specified extra dependencies
-newer_prereqs_except = $(filter-out $(PHONY) $(1),$?)
-
-# Same as if_changed, but allows to exclude specified extra dependencies
-if_changed_except = $(if $(call newer_prereqs_except,$(2))$(cmd-check), \
- $(cmd); \
- printf '%s\n' 'savedcmd_$@ := $(make-cmd)' > $(dot-target).cmd, @:)
-
# Re-generate module BTFs if either module's .ko or vmlinux changed
%.ko: %.o %.mod.o .module-common.o $(objtree)/scripts/module.lds $(and $(CONFIG_DEBUG_INFO_BTF_MODULES),$(KBUILD_BUILTIN),$(objtree)/vmlinux) FORCE
- +$(call if_changed_except,ld_ko_o,$(objtree)/vmlinux)
+ +$(call if_changed,ld_ko_o)
ifdef CONFIG_DEBUG_INFO_BTF_MODULES
+$(if $(newer-prereqs),$(call cmd,btf_ko))
endif
diff --git a/scripts/Makefile.thinlto b/scripts/Makefile.thinlto
new file mode 100644
index 000000000000..bb83f13f3cd6
--- /dev/null
+++ b/scripts/Makefile.thinlto
@@ -0,0 +1,40 @@
+PHONY := __default
+__default:
+
+include include/config/auto.conf
+include $(srctree)/scripts/Kbuild.include
+include $(srctree)/scripts/Makefile.lib
+
+native-objs := $(patsubst %.o,%.thinlto-native.o,$(call read-file, vmlinux.thinlto-index))
+
+__default: $(native-objs)
+
+# Generate .thinlto-native.o (obj) from .o (bitcode) and .thinlto.bc (summary) files
+# ---------------------------------------------------------------------------
+quiet_cmd_cc_o_bc = CC $(quiet_modtag) $@
+ be_flags = $(shell sed -n '/saved_c_flags_/s/.*:= //p' \
+ $(dir $(<)).$(notdir $(<)).cmd)
+ cmd_cc_o_bc = \
+ $(CC) $(be_flags) -x ir -fno-lto -Wno-unused-command-line-argument \
+ -fthinlto-index=$(word 2, $^) -c -o $@ $<
+
+targets += $(native-objs)
+$(native-objs): %.thinlto-native.o: %.o %.o.thinlto.bc FORCE
+ $(call if_changed,cc_o_bc)
+
+# Add FORCE to the prerequisites of a target to force it to be always rebuilt.
+# ---------------------------------------------------------------------------
+
+PHONY += FORCE
+FORCE:
+
+# 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
+# exist, we will rebuild anyway in that case.
+
+existing-targets := $(wildcard $(sort $(targets)))
+
+-include $(foreach f, $(existing-targets),$(dir $(f)).$(notdir $(f)).cmd)
+
+.PHONY: $(PHONY)
diff --git a/scripts/Makefile.vmlinux_a b/scripts/Makefile.vmlinux_a
new file mode 100644
index 000000000000..395e29998d7d
--- /dev/null
+++ b/scripts/Makefile.vmlinux_a
@@ -0,0 +1,82 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+PHONY := __default
+__default: vmlinux.a
+
+include include/config/auto.conf
+include $(srctree)/scripts/Kbuild.include
+include $(srctree)/scripts/Makefile.lib
+
+# Link of built-in-fixup.a
+# ---------------------------------------------------------------------------
+
+quiet_cmd_ar_builtin_fixup = AR $@
+ cmd_ar_builtin_fixup = \
+ rm -f $@; \
+ $(AR) cDPrST $@ $(KBUILD_VMLINUX_OBJS); \
+ $(AR) mPi $$($(AR) t $@ | sed -n 1p) $@ $$($(AR) t $@ | grep -F -f $(srctree)/scripts/head-object-list.txt)
+
+targets += built-in-fixup.a
+built-in-fixup.a: $(KBUILD_VMLINUX_OBJS) scripts/head-object-list.txt FORCE
+ $(call if_changed,ar_builtin_fixup)
+
+ifdef CONFIG_LTO_CLANG_THIN_DIST
+
+quiet_cmd_builtin.order = GEN $@
+ cmd_builtin.order = $(AR) t $< > $@
+
+targets += builtin.order
+builtin.order: built-in-fixup.a FORCE
+ $(call if_changed,builtin.order)
+
+quiet_cmd_ld_thinlto_index = LD $@
+ cmd_ld_thinlto_index = \
+ $(LD) $(KBUILD_LDFLAGS) -r --thinlto-index-only=$@ @$<
+
+targets += vmlinux.thinlto-index
+vmlinux.thinlto-index: builtin.order FORCE
+ $(call if_changed,ld_thinlto_index)
+
+quiet_cmd_ar_vmlinux.a = GEN $@
+ cmd_ar_vmlinux.a = \
+ rm -f $@; \
+ while read -r obj; do \
+ if grep -Fqx $${obj} $(word 2, $^); then \
+ echo $${obj%.o}.thinlto-native.o; \
+ else \
+ echo $${obj}; \
+ fi; \
+ done < $< | xargs $(AR) cDPrS --thin $@
+
+targets += vmlinux.a
+vmlinux.a: builtin.order vmlinux.thinlto-index FORCE
+ $(Q)$(MAKE) -f $(srctree)/scripts/Makefile.thinlto
+ $(call if_changed,ar_vmlinux.a)
+
+else
+
+# vmlinux.a
+# ---------------------------------------------------------------------------
+
+targets += vmlinux.a
+vmlinux.a: built-in-fixup.a FORCE
+ $(call if_changed,copy)
+
+endif
+
+# Add FORCE to the prerequisites of a target to force it to be always rebuilt.
+# ---------------------------------------------------------------------------
+
+PHONY += FORCE
+FORCE:
+
+# 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
+# exist, we will rebuild anyway in that case.
+
+existing-targets := $(wildcard $(sort $(targets)))
+
+-include $(foreach f,$(existing-targets),$(dir $(f)).$(notdir $(f)).cmd)
+
+.PHONY: $(PHONY)
diff --git a/scripts/Makefile.warn b/scripts/Makefile.warn
index e77ca875aea4..35af7d6c6d18 100644
--- a/scripts/Makefile.warn
+++ b/scripts/Makefile.warn
@@ -135,16 +135,6 @@ KBUILD_CFLAGS += $(call cc-option, -Wno-stringop-truncation)
KBUILD_CFLAGS += -Wno-override-init # alias for -Wno-initializer-overrides in clang
ifdef CONFIG_CC_IS_CLANG
-# Clang before clang-16 would warn on default argument promotions.
-ifneq ($(call clang-min-version, 160000),y)
-# Disable -Wformat
-KBUILD_CFLAGS += -Wno-format
-# Then re-enable flags that were part of the -Wformat group that aren't
-# problematic.
-KBUILD_CFLAGS += -Wformat-extra-args -Wformat-invalid-specifier
-KBUILD_CFLAGS += -Wformat-zero-length -Wnonnull
-KBUILD_CFLAGS += -Wformat-insufficient-args
-endif
KBUILD_CFLAGS += -Wno-pointer-to-enum-cast
KBUILD_CFLAGS += -Wno-tautological-constant-out-of-range-compare
KBUILD_CFLAGS += -Wno-unaligned-access
diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl
index 0492d6afc9a1..cc5bbd70cb84 100755
--- a/scripts/checkpatch.pl
+++ b/scripts/checkpatch.pl
@@ -865,8 +865,6 @@ our %deprecated_apis = (
"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
@@ -7596,12 +7594,15 @@ sub process {
# Complain about RCU Tasks Trace used outside of BPF (and of course, RCU).
our $rcu_trace_funcs = qr{(?x:
+ rcu_read_lock_tasks_trace |
rcu_read_lock_trace |
rcu_read_lock_trace_held |
rcu_read_unlock_trace |
+ rcu_read_unlock_tasks_trace |
call_rcu_tasks_trace |
synchronize_rcu_tasks_trace |
rcu_barrier_tasks_trace |
+ rcu_tasks_trace_expedite_current |
rcu_request_urgent_qs_task
)};
our $rcu_trace_paths = qr{(?x:
diff --git a/scripts/clang-tools/gen_compile_commands.py b/scripts/clang-tools/gen_compile_commands.py
index 96e6e46ad1a7..8d14b81efd73 100755
--- a/scripts/clang-tools/gen_compile_commands.py
+++ b/scripts/clang-tools/gen_compile_commands.py
@@ -201,6 +201,8 @@ def main():
# Modules are listed in modules.order.
if os.path.isdir(path):
cmdfiles = cmdfiles_in_dir(path)
+ elif os.path.basename(path) == 'libgcc.a':
+ cmdfiles = []
elif path.endswith('.a'):
cmdfiles = cmdfiles_for_a(path, ar)
elif path.endswith('modules.order'):
diff --git a/scripts/clang-tools/run-clang-tools.py b/scripts/clang-tools/run-clang-tools.py
index f31ffd09e1ea..e78be82aa693 100755
--- a/scripts/clang-tools/run-clang-tools.py
+++ b/scripts/clang-tools/run-clang-tools.py
@@ -79,14 +79,15 @@ def run_analysis(entry):
def main():
- try:
- args = parse_arguments()
+ args = parse_arguments()
+
+ # Read JSON data into the datastore variable
+ with open(args.path) as f:
+ datastore = json.load(f)
- lock = multiprocessing.Lock()
- pool = multiprocessing.Pool(initializer=init, initargs=(lock, args))
- # Read JSON data into the datastore variable
- with open(args.path, "r") as f:
- datastore = json.load(f)
+ lock = multiprocessing.Lock()
+ try:
+ with multiprocessing.Pool(initializer=init, initargs=(lock, args)) as pool:
pool.map(run_analysis, datastore)
except BrokenPipeError:
# Python flushes standard streams on exit; redirect remaining output
diff --git a/scripts/dtc/dt-check-style b/scripts/dtc/dt-check-style
new file mode 100755
index 000000000000..2d5723d41ea3
--- /dev/null
+++ b/scripts/dtc/dt-check-style
@@ -0,0 +1,1192 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Check DTS coding style on YAML binding examples and on
+# .dts/.dtsi/.dtso source files. Enforces rules from
+# Documentation/devicetree/bindings/dts-coding-style.rst.
+#
+# Two modes:
+# --mode=relaxed (default)
+# Only rules that produce zero warnings on the current tree.
+# Suitable for dt_binding_check.
+# --mode=strict
+# All rules. Required for new submissions.
+#
+# Two input types (auto-detected by file extension):
+# *.yaml -- DT binding; check each example block
+# *.dts/*.dtsi/*.dtso -- DTS source; whole file is one block
+#
+# Rules are declared in a registry (see RULES below); each rule is
+# tagged with the lowest mode that runs it. Promoting a rule from
+# 'strict' to 'relaxed' is a one-line change.
+
+import argparse
+import re
+import sys
+from enum import Enum, auto
+
+import ruamel.yaml
+
+
+# ---------------------------------------------------------------------------
+# Line classification
+# ---------------------------------------------------------------------------
+
+class LineType(Enum):
+ BLANK = auto()
+ COMMENT = auto() # // ... or /* ... */ on one line
+ COMMENT_START = auto() # /* without closing */
+ COMMENT_BODY = auto() # inside a multi-line comment
+ COMMENT_END = auto() # closing */
+ PREPROCESSOR = auto() # #include / #define / #ifdef / ...
+ NODE_OPEN = auto() # something { (with optional label/name/addr)
+ NODE_CLOSE = auto() # };
+ PROPERTY = auto() # name = value; or name;
+ CONTINUATION = auto() # continuation of a multi-line property
+
+
+re_cpp_directive = re.compile(
+ r'^#\s*(include|define|undef|ifdef|ifndef|if|else|elif|endif|'
+ r'pragma|error|warning)\b')
+
+# label: name@addr { -- label and addr optional; name can be "/"
+# Per the DT spec a node name may start with a digit (e.g. 1wire@...).
+# The address part is captured loosely (any non-space, non-brace run) so
+# malformed addresses (e.g. memory@0x1000) still reach
+# check_unit_address_format() instead of silently bypassing the check.
+re_node_header = re.compile(
+ r'^(?:([a-zA-Z_][a-zA-Z0-9_]*):\s*)?'
+ r'([a-zA-Z0-9][a-zA-Z0-9,._+-]*|/)'
+ r'(?:@([^\s{]+))?'
+ r'\s*\{$')
+
+re_ref_node = re.compile(
+ r'^&([a-zA-Z_][a-zA-Z0-9_]*)\s*\{$')
+
+
+def is_preprocessor(stripped):
+ """Tell C preprocessor directives apart from DTS '#'-prefixed props."""
+ return re_cpp_directive.match(stripped) is not None
+
+
+class DtsLine:
+ __slots__ = ('lineno', 'raw', 'linetype', 'indent_str', 'stripped',
+ 'prop_name', 'continuations',
+ 'node_name', 'node_addr', 'label', 'ref_name', 'depth',
+ 'closures')
+
+ def __init__(self, lineno, raw, linetype, indent_str, stripped):
+ self.lineno = lineno # 1-based within the block
+ self.raw = raw
+ self.linetype = linetype
+ self.indent_str = indent_str # leading whitespace as-is
+ self.stripped = stripped
+ self.prop_name = None
+ self.continuations = []
+ self.node_name = None
+ self.node_addr = None
+ self.label = None
+ self.ref_name = None
+ self.depth = 0 # filled in by classify_lines
+ self.closures = 1 # count of '}' on a NODE_CLOSE line
+
+
+def _split_code(text):
+ """Return (code, opens_block) for a leading-stripped line: the
+ code portion with // and /* */ comments removed (string literals
+ kept verbatim), and whether a /* */ block comment is left open.
+ The code portion is right-stripped so the endswith() checks in
+ classify_lines see code only, not a trailing comment or blanks."""
+ out = []
+ i = 0
+ n = len(text)
+ while i < n:
+ c = text[i]
+ if c == '"':
+ j = i + 1
+ while j < n:
+ if text[j] == '\\':
+ j += 2
+ continue
+ if text[j] == '"':
+ j += 1
+ break
+ j += 1
+ out.append(text[i:j])
+ i = j
+ continue
+ if c == '/' and i + 1 < n and text[i + 1] == '/':
+ break
+ if c == '/' and i + 1 < n and text[i + 1] == '*':
+ end = text.find('*/', i + 2)
+ if end < 0:
+ return (''.join(out).rstrip(), True)
+ i = end + 2
+ continue
+ out.append(c)
+ i += 1
+ return (''.join(out).rstrip(), False)
+
+
+re_only_closures = re.compile(r'(?:\}\s*;?\s*)+$')
+
+
+def classify_lines(text):
+ """Return a list of DtsLine. Tracks { } depth and groups
+ continuation lines onto their leading PROPERTY line."""
+ out = []
+ in_block_comment = False
+ in_cpp_macro = False
+ prev_complete = True
+ depth = 0
+
+ # Split preserving the indent string verbatim
+ re_lead = re.compile(r'^([ \t]*)(.*)$')
+
+ for i, raw in enumerate(text.split('\n'), start=1):
+ m = re_lead.match(raw)
+ indent_str = m.group(1)
+ stripped = m.group(2)
+
+ # Continuation of a multi-line C preprocessor directive: the
+ # previous PREPROCESSOR line ended with a '\\' line splice, so
+ # this line is part of the same macro. Treat it as
+ # PREPROCESSOR until the splice chain ends (no trailing '\\'
+ # or a blank line).
+ if in_cpp_macro:
+ dl = DtsLine(i, raw, LineType.PREPROCESSOR,
+ indent_str, stripped)
+ dl.depth = depth
+ out.append(dl)
+ in_cpp_macro = (bool(stripped) and
+ stripped.rstrip().endswith('\\'))
+ continue
+
+ if not stripped:
+ dl = DtsLine(i, raw, LineType.BLANK, '', '')
+ dl.depth = depth
+ out.append(dl)
+ continue
+
+ if in_block_comment:
+ ltype = (LineType.COMMENT_END if '*/' in stripped
+ else LineType.COMMENT_BODY)
+ if ltype == LineType.COMMENT_END:
+ in_block_comment = False
+ dl = DtsLine(i, raw, ltype, indent_str, stripped)
+ dl.depth = depth
+ out.append(dl)
+ continue
+
+ if stripped.startswith('#') and is_preprocessor(stripped):
+ dl = DtsLine(i, raw, LineType.PREPROCESSOR,
+ indent_str, stripped)
+ dl.depth = depth
+ out.append(dl)
+ prev_complete = True
+ in_cpp_macro = stripped.rstrip().endswith('\\')
+ continue
+
+ # Strip comments first so all later structural checks see code
+ # only. An unclosed /* sets in_block_comment for the next line.
+ code, opens_block = _split_code(stripped)
+ if opens_block:
+ in_block_comment = True
+
+ # Pure-comment line: nothing left after stripping. Classify as
+ # COMMENT_START (carries to next line) or COMMENT, and skip the
+ # structural classification entirely.
+ if not code:
+ ltype = LineType.COMMENT_START if opens_block else LineType.COMMENT
+ dl = DtsLine(i, raw, ltype, indent_str, stripped)
+ dl.depth = depth
+ out.append(dl)
+ continue
+
+ if not prev_complete:
+ dl = DtsLine(i, raw, LineType.CONTINUATION, indent_str, code)
+ dl.depth = depth
+ out.append(dl)
+ prev_complete = (code.endswith(';') or
+ code.endswith('{') or
+ code.endswith('};'))
+ continue
+
+ # NODE_CLOSE: the canonical form is "}" or "};" alone. A line
+ # that is nothing but closures (e.g. "}; };") is still treated
+ # as NODE_CLOSE for depth tracking, but the multi-closure case
+ # is flagged separately by check_node_close_alone via
+ # dl.closures.
+ if re_only_closures.match(code):
+ closures = code.count('}')
+ depth = max(depth - closures, 0)
+ dl = DtsLine(i, raw, LineType.NODE_CLOSE, indent_str, code)
+ dl.depth = depth
+ dl.closures = closures
+ out.append(dl)
+ prev_complete = True
+ continue
+
+ if code.endswith('{'):
+ dl = DtsLine(i, raw, LineType.NODE_OPEN, indent_str, code)
+ parse_node_header(dl)
+ dl.depth = depth
+ out.append(dl)
+ depth += 1
+ prev_complete = True
+ continue
+
+ # Property (or first line of a multi-line property).
+ dl = DtsLine(i, raw, LineType.PROPERTY, indent_str, code)
+ parse_property_name(dl)
+ dl.depth = depth
+ out.append(dl)
+ prev_complete = code.endswith(';')
+
+ # Group continuation lines onto their leading PROPERTY.
+ last_prop = None
+ grouped = []
+ for dl in out:
+ if dl.linetype == LineType.CONTINUATION and last_prop is not None:
+ last_prop.continuations.append(dl)
+ continue
+ if dl.linetype == LineType.PROPERTY:
+ last_prop = dl
+ elif dl.linetype != LineType.BLANK and \
+ dl.linetype not in (LineType.COMMENT, LineType.COMMENT_BODY,
+ LineType.COMMENT_END,
+ LineType.COMMENT_START):
+ last_prop = None
+ grouped.append(dl)
+ return grouped
+
+
+def parse_node_header(dl):
+ m = re_node_header.match(dl.stripped)
+ if m:
+ dl.label = m.group(1)
+ dl.node_name = m.group(2)
+ dl.node_addr = m.group(3)
+ return
+ m = re_ref_node.match(dl.stripped)
+ if m:
+ dl.ref_name = m.group(1)
+
+
+def parse_property_name(dl):
+ m = re.match(r'^([a-zA-Z0-9#][a-zA-Z0-9,._+#-]*)\s*[=;]', dl.stripped)
+ if m:
+ dl.prop_name = m.group(1)
+
+
+def collect_labels_and_refs(text):
+ """Return (defined_labels, referenced_labels) found anywhere outside
+ /* */ comments and string literals. Labels named fake_intc* (injected
+ by dt-extract-example) are skipped."""
+ # Strip block comments first so labels inside them don't count
+ stripped = re.sub(r'/\*.*?\*/', '', text, flags=re.DOTALL)
+ # Strip line comments
+ stripped = re.sub(r'//[^\n]*', '', stripped)
+ # Strip string literals so words inside quotes (e.g. "Error: foo")
+ # are not picked up as label definitions or &-references.
+ stripped = re.sub(r'"(?:[^"\\]|\\.)*"', '""', stripped)
+ defined = set()
+ referenced = set()
+ # A label precedes a node header; the next non-space token may start
+ # with a letter (foo, &ref), a digit (1wire), or '/' (root node).
+ for m in re.finditer(
+ r'(?:^|[\s{])([a-zA-Z_][a-zA-Z0-9_]*):\s*[a-zA-Z0-9/&]',
+ stripped):
+ name = m.group(1)
+ if not name.startswith('fake_intc'):
+ defined.add(name)
+ for m in re.finditer(r'&([a-zA-Z_][a-zA-Z0-9_]*)', stripped):
+ referenced.add(m.group(1))
+ return defined, referenced
+
+
+# ---------------------------------------------------------------------------
+# Rule registry
+# ---------------------------------------------------------------------------
+
+class Ctx:
+ """Context passed to each rule check. Carries the parsed lines,
+ raw text, mode, and indent kind."""
+
+ def __init__(self, lines, text, mode, indent_kind):
+ self.lines = lines
+ self.text = text
+ self.mode = mode # 'relaxed' or 'strict'
+ self.indent_kind = indent_kind # 'spaces' or 'tab'
+
+
+class Rule:
+ __slots__ = ('name', 'mode', 'description', 'check', 'applies_to')
+
+ def __init__(self, name, mode, description, check,
+ applies_to=('yaml', 'dts', 'dtsi', 'dtso')):
+ self.name = name
+ self.mode = mode # 'relaxed' or 'strict'
+ self.description = description
+ self.check = check
+ self.applies_to = applies_to # input types this rule covers
+
+
+# --- individual rule check functions --------------------------------------
+
+def check_trailing_whitespace(ctx):
+ for dl in ctx.lines:
+ if dl.raw != dl.raw.rstrip():
+ yield (dl.lineno, 'trailing whitespace')
+
+
+def check_tab_in_dts(ctx):
+ """Reject literal tabs in DTS lines when input is YAML.
+
+ For YAML examples, indent and content must use spaces. Tabs inside
+ a #define value are tolerated (those are CPP macros, not DTS).
+ For .dts files, this rule does not apply -- tabs are required.
+ """
+ if ctx.indent_kind != 'spaces':
+ return
+ for dl in ctx.lines:
+ if dl.linetype == LineType.PREPROCESSOR:
+ continue
+ if dl.linetype == LineType.BLANK:
+ continue
+ if '\t' in dl.raw:
+ yield (dl.lineno, 'tab character not allowed in DTS example')
+
+
+def check_mixed_indent_chars(ctx):
+ """Indent must be all-spaces or all-tabs, never mixed on one line."""
+ for dl in ctx.lines:
+ if not dl.indent_str:
+ continue
+ if dl.linetype == LineType.PREPROCESSOR:
+ continue
+ if ' ' in dl.indent_str and '\t' in dl.indent_str:
+ yield (dl.lineno, 'mixed tabs and spaces in indent')
+
+
+def detect_indent_unit(ctx):
+ """Find the indent unit used at depth 1 in this block.
+
+ Returns one of: ' ' (2 spaces), ' ' (4 spaces), '\\t' (tab),
+ or None if depth-1 is empty or ambiguous."""
+ for dl in ctx.lines:
+ if dl.depth != 1:
+ continue
+ if dl.linetype in (LineType.BLANK, LineType.PREPROCESSOR):
+ continue
+ if dl.linetype in (LineType.COMMENT_BODY, LineType.COMMENT_END):
+ continue
+ if not dl.indent_str:
+ continue
+ if dl.indent_str == '\t':
+ return '\t'
+ if dl.indent_str == ' ':
+ return ' '
+ if dl.indent_str == ' ':
+ return ' '
+ # Anything else at depth 1 is non-canonical; flag elsewhere.
+ return dl.indent_str
+ return None
+
+
+def check_indent_unit_relaxed(ctx):
+ """YAML examples: 2 or 4 spaces. Never tabs or other widths."""
+ unit = detect_indent_unit(ctx)
+ if unit is None:
+ return
+ if unit not in (' ', ' '):
+ yield (1, 'indent unit must be 2 or 4 spaces, got %r' % unit)
+
+
+def check_indent_unit_dts(ctx):
+ """DTS files: 1 tab per level. Always required."""
+ unit = detect_indent_unit(ctx)
+ if unit is None:
+ return
+ if unit != '\t':
+ yield (1, 'indent unit must be 1 tab in DTS, got %r' % unit)
+
+
+def check_indent_unit_strict(ctx):
+ """YAML: must be exactly 4 spaces. DTS: 1 tab (same as relaxed)."""
+ unit = detect_indent_unit(ctx)
+ if unit is None:
+ return
+ if ctx.indent_kind == 'spaces':
+ if unit != ' ':
+ yield (1, 'indent unit must be 4 spaces in strict mode, '
+ 'got %r' % unit)
+
+
+def check_indent_consistent(ctx):
+ """All indented lines must be a multiple of the detected unit."""
+ unit = detect_indent_unit(ctx)
+ if unit is None:
+ return
+ if ctx.indent_kind == 'spaces':
+ if unit not in (' ', ' '):
+ return # let check_indent_unit_* report this
+ else:
+ if unit != '\t':
+ return
+
+ for dl in ctx.lines:
+ if dl.linetype in (LineType.BLANK, LineType.PREPROCESSOR):
+ continue
+ if dl.linetype == LineType.CONTINUATION:
+ continue # continuations align to <, not to indent unit
+ if dl.linetype in (LineType.COMMENT_BODY, LineType.COMMENT_END):
+ continue
+ if not dl.indent_str:
+ continue
+ # The indent must be 'unit' repeated dl.depth times, exactly.
+ # NODE_CLOSE lines have depth equal to the post-decrement value,
+ # which matches the indent expected.
+ expected = unit * dl.depth
+ if dl.indent_str != expected:
+ yield (dl.lineno,
+ 'indent mismatch (expected depth %d * %r)' %
+ (dl.depth, unit))
+
+
+def check_blank_lines(ctx):
+ """No two consecutive blank lines, no leading/trailing blank lines
+ in any node body."""
+ lines = ctx.lines
+ # Consecutive blanks
+ for i in range(1, len(lines)):
+ if lines[i].linetype == LineType.BLANK and \
+ lines[i - 1].linetype == LineType.BLANK:
+ yield (lines[i].lineno, 'consecutive blank lines')
+ # Blank right after { or right before }
+ for i, dl in enumerate(lines):
+ if dl.linetype != LineType.BLANK:
+ continue
+ prev = lines[i - 1] if i > 0 else None
+ nxt = lines[i + 1] if i + 1 < len(lines) else None
+ if prev is not None and prev.linetype == LineType.NODE_OPEN:
+ yield (dl.lineno, 'blank line at start of node body')
+ if nxt is not None and nxt.linetype == LineType.NODE_CLOSE:
+ yield (dl.lineno, 'blank line at end of node body')
+
+
+def _walk_bodies(lines):
+ """Yield lists of immediate-child NODE_OPEN lines for each node body
+ in the input. Skips ref-nodes (&label) since those don't have an
+ intrinsic ordering."""
+ body_stack = [[]]
+ for dl in lines:
+ if dl.linetype == LineType.NODE_OPEN:
+ body_stack[-1].append(dl)
+ body_stack.append([])
+ continue
+ if dl.linetype == LineType.NODE_CLOSE:
+ if len(body_stack) <= 1:
+ # Unbalanced; ignore to avoid crashing on malformed input
+ continue
+ yield body_stack.pop()
+ continue
+ while body_stack:
+ yield body_stack.pop()
+
+
+def _natural_sort_key(s):
+ """Split a string into a tuple of (kind, value) pairs that compares
+ numeric runs as ints, so 'foo10' sorts after 'foo2'."""
+ parts = []
+ for part in re.split(r'(\d+)', s):
+ if part.isdigit():
+ parts.append((0, int(part)))
+ else:
+ parts.append((1, part))
+ return tuple(parts)
+
+
+def check_child_address_order(ctx):
+ """Addressed siblings (foo@N) must appear in ascending address
+ order within their parent node body."""
+ for children in _walk_bodies(ctx.lines):
+ addressed = []
+ for c in children:
+ if c.node_addr is None:
+ continue
+ try:
+ parts = tuple(int(p, 16) for p in c.node_addr.split(','))
+ except ValueError:
+ continue
+ addressed.append((parts, c))
+ for i in range(1, len(addressed)):
+ if addressed[i][0] < addressed[i - 1][0]:
+ dl = addressed[i][1]
+ yield (dl.lineno,
+ 'child node @%s out of address order' %
+ dl.node_addr)
+
+
+def check_child_name_order(ctx):
+ """Unaddressed siblings must appear in natural-sort order by node
+ name within their parent node body. Addressed children are scoped
+ by check_child_address_order; reference nodes (&label { ... }) and
+ the root node are skipped."""
+ for children in _walk_bodies(ctx.lines):
+ unaddressed = []
+ for c in children:
+ if c.node_addr is not None:
+ continue
+ if c.node_name in (None, '/'):
+ continue
+ if c.ref_name is not None:
+ continue
+ unaddressed.append((_natural_sort_key(c.node_name), c))
+ for i in range(1, len(unaddressed)):
+ if unaddressed[i][0] < unaddressed[i - 1][0]:
+ dl = unaddressed[i][1]
+ yield (dl.lineno,
+ 'child node %r out of name order' % dl.node_name)
+
+
+def _property_bucket(name):
+ """Return the canonical bucket index for a property:
+ 0 compatible
+ 1 reg / reg-names
+ 2 ranges
+ 3 standard properties (no vendor comma in #-stripped name)
+ 4 vendor-specific properties
+ 5 status
+ Plus a sub-key inside the bucket for fixed slots (compatible, reg,
+ reg-names, ranges, status). 'standard' and 'vendor' return None for
+ the sub-key, signalling that the within-bucket key is computed by
+ the pairing rules."""
+ stripped = name.lstrip('#')
+ if name == 'compatible':
+ return (0, 0)
+ if name == 'reg':
+ return (1, 0)
+ if name == 'reg-names':
+ return (1, 1)
+ if name == 'ranges':
+ return (2, 0)
+ if name == 'status':
+ return (5, 0)
+ return (4 if ',' in stripped else 3, None)
+
+
+# Declarative pairing rules: each is a callable
+# (name, all_names) -> anchor_name_or_None
+# If a rule returns an anchor, the property sorts immediately after the
+# anchor. Rules are tried in order; the first match wins. If none
+# matches, the within-bucket key falls back to natural sort by the
+# #-stripped name.
+
+def _pair_pinctrl_names(name, all_names):
+ """pinctrl-names follows the highest pinctrl-N in the same node."""
+ if name != 'pinctrl-names':
+ return None
+ cands = [n for n in all_names if re.match(r'^pinctrl-\d+$', n)]
+ if not cands:
+ return None
+ return max(cands, key=_natural_sort_key)
+
+
+def _pair_x_names(name, all_names):
+ """Generic <x>-names follows its owning property. The owner is
+ usually plural (clocks/clock-names, dmas/dma-names,
+ resets/reset-names) but occasionally singular (reg/reg-names is
+ handled by the fixed slot above; this rule catches anything else)."""
+ if not name.endswith('-names'):
+ return None
+ base = name[:-len('-names')]
+ # Try plural and singular forms.
+ if (base + 's') in all_names:
+ return base + 's'
+ if base in all_names:
+ return base
+ return None
+
+
+PAIRING_RULES = (_pair_pinctrl_names, _pair_x_names)
+
+
+def _property_sort_key(name, all_names):
+ """Sort key for a property among its node-body siblings.
+
+ Format: (bucket, within_key, tiebreak). 'within_key' for
+ standard/vendor buckets follows pairing rules: a property paired
+ with anchor X sorts as if it were X with a higher tiebreak."""
+ bucket, fixed_sub = _property_bucket(name)
+ if fixed_sub is not None:
+ return (bucket, (), fixed_sub)
+
+ for rule in PAIRING_RULES:
+ anchor = rule(name, all_names)
+ if anchor is not None:
+ return (bucket, _natural_sort_key(anchor.lstrip('#')), 1)
+
+ return (bucket, _natural_sort_key(name.lstrip('#')), 0)
+
+
+def check_property_order(ctx):
+ """Properties within a node body must appear in canonical order:
+ compatible, reg(/reg-names), ranges, then the standard group, then
+ the vendor-specific group, then status. Inside the standard and
+ vendor groups, pairing rules apply (e.g. <x>-names follows <x>);
+ everything else falls back to natural sort by the #-stripped name."""
+ lines = ctx.lines
+ for i, dl in enumerate(lines):
+ if dl.linetype != LineType.NODE_OPEN:
+ continue
+ body_depth = dl.depth + 1
+ props = []
+ for j in range(i + 1, len(lines)):
+ d = lines[j]
+ if d.linetype == LineType.NODE_CLOSE and \
+ d.depth == body_depth - 1:
+ break
+ if d.linetype == LineType.PROPERTY and d.depth == body_depth \
+ and d.prop_name is not None:
+ props.append(d)
+ if len(props) < 2:
+ continue
+ all_names = [p.prop_name for p in props]
+ keyed = [(p, _property_sort_key(p.prop_name, all_names))
+ for p in props]
+ for k in range(1, len(keyed)):
+ if keyed[k][1] < keyed[k - 1][1]:
+ p = keyed[k][0]
+ prev = keyed[k - 1][0]
+ yield (p.lineno,
+ 'property %r out of canonical order '
+ '(should sort before %r)' %
+ (p.prop_name, prev.prop_name))
+
+
+def _strip_strings_and_comments(text):
+ """Remove string literals and /* */ + // comments from a single
+ line, replacing them with empty strings. Used so syntactic checks
+ (whitespace, hex case, etc.) don't false-positive on contents of
+ quoted strings or comments. An unclosed /* on the line is treated
+ as a comment running to end of line."""
+ text = re.sub(r'"(?:[^"\\]|\\.)*"', '""', text)
+ text = re.sub(r'/\*.*?\*/', '', text)
+ text = re.sub(r'/\*.*$', '', text)
+ text = re.sub(r'//.*$', '', text)
+ return text
+
+
+def check_required_blank_lines(ctx):
+ """A blank line must precede each child node and the 'status'
+ property within a node body, except when these are the first
+ substantive item in the body."""
+ lines = ctx.lines
+ for i, open_dl in enumerate(lines):
+ if open_dl.linetype != LineType.NODE_OPEN:
+ continue
+ body_depth = open_dl.depth + 1
+ prev_substantive = None
+ between_blanks = 0
+ depth_inside = 0
+ for j in range(i + 1, len(lines)):
+ d = lines[j]
+ if d.linetype == LineType.NODE_CLOSE and \
+ d.depth == body_depth - 1 and depth_inside == 0:
+ break
+ # Track depth inside nested children so we only look at
+ # immediate-body items.
+ if d.linetype == LineType.NODE_OPEN and \
+ d.depth >= body_depth and depth_inside > 0:
+ depth_inside += 1
+ continue
+ if d.linetype == LineType.NODE_CLOSE and depth_inside > 0:
+ depth_inside -= 1
+ continue
+ if depth_inside > 0:
+ continue
+ if d.linetype == LineType.BLANK:
+ if prev_substantive is not None:
+ between_blanks += 1
+ continue
+ if d.linetype in (LineType.COMMENT, LineType.COMMENT_START,
+ LineType.COMMENT_BODY, LineType.COMMENT_END,
+ LineType.PREPROCESSOR):
+ continue
+ if d.linetype == LineType.CONTINUATION:
+ continue
+
+ needs_blank = False
+ if d.linetype == LineType.NODE_OPEN:
+ needs_blank = True
+ depth_inside = 1 # entered the child body
+ elif d.linetype == LineType.PROPERTY and d.prop_name == 'status':
+ needs_blank = True
+
+ if needs_blank and prev_substantive is not None and \
+ between_blanks == 0:
+ if d.linetype == LineType.NODE_OPEN:
+ yield (d.lineno,
+ 'child node must be preceded by a blank line')
+ else:
+ yield (d.lineno,
+ '"status" must be preceded by a blank line')
+
+ prev_substantive = d
+ between_blanks = 0
+
+
+def check_hex_case(ctx):
+ """Hex literals (0xN) must use lowercase digits and prefix."""
+ for dl in ctx.lines:
+ if dl.linetype in (LineType.BLANK, LineType.COMMENT,
+ LineType.COMMENT_START, LineType.COMMENT_BODY,
+ LineType.COMMENT_END, LineType.PREPROCESSOR):
+ continue
+ text = _strip_strings_and_comments(dl.raw)
+ for m in re.finditer(r'\b0[xX][0-9a-fA-F]+\b', text):
+ lit = m.group(0)
+ if any(c.isupper() for c in lit[2:]) or lit[1] == 'X':
+ yield (dl.lineno,
+ 'hex literal %r must be lowercase' % lit)
+
+
+def check_unit_address_format(ctx):
+ """Unit addresses must be lowercase hex without leading zeros and
+ without a '0x' prefix. For multi-cell addresses (comma-separated),
+ each part is checked independently. A single '0' is permitted
+ (canonical zero)."""
+ for dl in ctx.lines:
+ if dl.linetype != LineType.NODE_OPEN:
+ continue
+ if dl.node_addr is None:
+ continue
+ addr = dl.node_addr
+ for part in addr.split(','):
+ if part[:2] in ('0x', '0X'):
+ yield (dl.lineno,
+ 'unit address %r must not have a "0x" prefix' %
+ addr)
+ break
+ if not re.match(r'^[0-9a-fA-F]+$', part):
+ yield (dl.lineno,
+ 'unit address %r is not valid hex' % addr)
+ break
+ if any(c in 'ABCDEF' for c in part):
+ yield (dl.lineno,
+ 'unit address %r must be lowercase hex' % addr)
+ break
+ if len(part) > 1 and part.startswith('0'):
+ yield (dl.lineno,
+ 'unit address %r has leading zeros' % addr)
+ break
+
+
+def check_value_whitespace(ctx):
+ """A <...> cell list must have no whitespace directly after '<'
+ or directly before '>'. Continuation lines are joined onto the
+ property so a <...> split across lines is checked too; a '<' or
+ '>' at a line break is glued straight to the neighbouring value,
+ so the break itself is not counted as padding. Outside strings
+ and comments only."""
+ for dl in ctx.lines:
+ if dl.linetype != LineType.PROPERTY:
+ continue
+ segs = [_strip_strings_and_comments(dl.raw).strip()]
+ for cont in dl.continuations:
+ segs.append(_strip_strings_and_comments(cont.stripped).strip())
+ text = ''
+ for s in segs:
+ if not s:
+ continue
+ if not text or text.endswith('<') or s.startswith('>'):
+ text += s
+ else:
+ text += ' ' + s
+ for m in re.finditer(r'<([^<>]*)>', text):
+ content = m.group(1)
+ if content and content != content.strip():
+ yield (dl.lineno, 'extra whitespace inside <...>')
+ break
+
+
+def check_node_close_alone(ctx):
+ """The closing '};' of a node must be on its own line. The
+ classifier accepts a canonical "}" or "};" as NODE_CLOSE; a line
+ that is all closures (e.g. "}; };") is still NODE_CLOSE for depth
+ tracking but is flagged here via dl.closures. Any other line that
+ still contains '};' (in code, not in strings or comments) is
+ mixing a node close with something else."""
+ for dl in ctx.lines:
+ if dl.linetype == LineType.NODE_CLOSE:
+ if dl.closures > 1:
+ yield (dl.lineno,
+ 'closing brace must be on its own line')
+ continue
+ if dl.linetype in (LineType.BLANK, LineType.COMMENT,
+ LineType.COMMENT_START, LineType.COMMENT_BODY,
+ LineType.COMMENT_END, LineType.PREPROCESSOR):
+ continue
+ text = _strip_strings_and_comments(dl.raw)
+ if '};' in text:
+ yield (dl.lineno,
+ 'closing brace must be on its own line')
+
+
+def _display_col(text):
+ """Visual column width of text, with tabs expanded to the next
+ 8-column stop, matching how printf and most editors render a
+ line and the kernel-wide line length convention."""
+ col = 0
+ for ch in text:
+ if ch == '\t':
+ col = (col // 8 + 1) * 8
+ else:
+ col += 1
+ return col
+
+
+def check_line_length(ctx):
+ """Lines must not exceed 80 columns; tabs count as 8 (see
+ _display_col)."""
+ for dl in ctx.lines:
+ if dl.linetype == LineType.BLANK:
+ continue
+ cols = _display_col(dl.raw)
+ if cols > 80:
+ yield (dl.lineno,
+ 'line exceeds 80 columns (%d)' % cols)
+
+
+def check_continuation_alignment(ctx):
+ """A multi-line property's continuation lines must align their
+ first non-whitespace character to the display column of the first
+ '<' or '"' after the '=' in the leading line. Display columns are
+ used so tab-indented .dts files (where a continuation aligns with
+ tabs plus spaces) are compared correctly."""
+ for dl in ctx.lines:
+ if dl.linetype != LineType.PROPERTY:
+ continue
+ if not dl.continuations:
+ continue
+ eq = dl.raw.find('=')
+ if eq < 0:
+ continue
+ # First '<' or '"' after '='
+ rest = dl.raw[eq + 1:]
+ m = re.search(r'[<"]', rest)
+ if not m:
+ continue
+ target_col = _display_col(dl.raw[:eq + 1 + m.start()])
+ for cont in dl.continuations:
+ if _display_col(cont.indent_str) != target_col:
+ yield (cont.lineno,
+ 'continuation should align to column %d '
+ '(under "<" or \\")' % (target_col + 1))
+
+
+def check_unclosed_block_comment(ctx):
+ """Every /* must have a matching */ in the same block. Catches both
+ a comment opened on its own line (COMMENT_START) and a tail comment
+ opened on a PROPERTY or other code line (where in_block_comment is
+ set by _split_code so the next line becomes COMMENT_BODY without a
+ preceding COMMENT_START)."""
+ open_lineno = None
+ for dl in ctx.lines:
+ if dl.linetype == LineType.COMMENT_START:
+ open_lineno = dl.lineno
+ elif dl.linetype == LineType.COMMENT_END:
+ open_lineno = None
+ elif dl.linetype == LineType.COMMENT_BODY and open_lineno is None:
+ # Block was opened by a /* tail on a code line; report at
+ # the first orphan body line since the originating line is
+ # already classified as something else.
+ open_lineno = dl.lineno
+ if open_lineno is not None:
+ yield (open_lineno, 'unclosed /* block comment')
+
+
+def check_unused_labels(ctx):
+ """Labels defined but never referenced are clutter."""
+ defined, referenced = collect_labels_and_refs(ctx.text)
+ for label in sorted(defined - referenced):
+ # Find the line where this label is defined for line-number
+ # reporting.
+ m = re.search(r'(?m)^.*\b' + re.escape(label) + r'\s*:', ctx.text)
+ lineno = ctx.text[:m.start()].count('\n') + 1 if m else 1
+ yield (lineno, 'label %r defined but never &-referenced' % label)
+
+
+# --- registry --------------------------------------------------------------
+
+RULES = [
+ # 'relaxed' is the default; rules in this group must produce zero
+ # output on a clean kernel tree (post the small prep-cleanup
+ # commit at the head of this series).
+ Rule('trailing-whitespace', 'relaxed',
+ 'no trailing whitespace on any line',
+ check_trailing_whitespace),
+ Rule('tab-in-dts', 'relaxed',
+ 'YAML examples may not contain tab characters',
+ check_tab_in_dts, applies_to=('yaml',)),
+ Rule('mixed-indent-chars', 'relaxed',
+ 'indent must not mix tabs and spaces',
+ check_mixed_indent_chars),
+ Rule('unclosed-block-comment', 'relaxed',
+ 'every /* block comment must close with */',
+ check_unclosed_block_comment),
+
+ # DTS files always use tabs; this is not negotiable per kernel
+ # coding style (.dts files are real source). Relaxed mode.
+ Rule('indent-unit-dts', 'relaxed',
+ 'DTS files: 1 tab per nesting level',
+ check_indent_unit_dts,
+ applies_to=('dts', 'dtsi', 'dtso')),
+
+ # 'strict' rules are opt-in (e.g. for new submissions via
+ # checkpatch.pl in a follow-up series). They flag many existing
+ # files and can be promoted to relaxed once those are cleaned up.
+ Rule('indent-unit', 'strict',
+ 'YAML: 2 or 4 spaces per level',
+ check_indent_unit_relaxed, applies_to=('yaml',)),
+ Rule('indent-unit-strict', 'strict',
+ 'YAML: must be 4 spaces per level',
+ check_indent_unit_strict, applies_to=('yaml',)),
+ Rule('indent-consistent', 'strict',
+ 'every line indented at depth * unit',
+ check_indent_consistent),
+ Rule('blank-lines', 'strict',
+ 'no consecutive blanks; no blanks at node body edges',
+ check_blank_lines),
+ Rule('child-address-order', 'strict',
+ 'addressed siblings must be in ascending address order',
+ check_child_address_order),
+ Rule('child-name-order', 'strict',
+ 'unaddressed siblings must be in natural-sort name order',
+ check_child_name_order),
+ Rule('property-order', 'strict',
+ 'canonical bucket + pairing + natural-sort order of properties',
+ check_property_order),
+ Rule('required-blank-lines', 'strict',
+ 'blank line before child nodes and before "status"',
+ check_required_blank_lines),
+ Rule('hex-case', 'strict',
+ 'hex literals must be lowercase',
+ check_hex_case),
+ Rule('unit-address-format', 'strict',
+ 'unit addresses must be lowercase hex without leading zeros',
+ check_unit_address_format),
+ Rule('value-whitespace', 'strict',
+ 'no whitespace directly inside <...> brackets',
+ check_value_whitespace),
+ Rule('node-close-alone', 'strict',
+ 'closing brace must be on its own line',
+ check_node_close_alone),
+ Rule('line-length', 'strict',
+ 'lines must not exceed 80 columns',
+ check_line_length),
+ Rule('continuation-alignment', 'strict',
+ 'multi-line property continuations align under "<" or "\\""',
+ check_continuation_alignment),
+ Rule('unused-labels', 'strict',
+ 'every label must be &-referenced in the same example/file '
+ '(skipped for .dtsi/.dtso since labels there are exported)',
+ check_unused_labels, applies_to=('yaml', 'dts')),
+]
+
+
+def select_rules(mode, input_kind):
+ """Return rules that apply to the given mode and input type."""
+ rank = {'relaxed': 0, 'strict': 1}
+ out = []
+ for r in RULES:
+ if rank[r.mode] > rank[mode]:
+ continue
+ if input_kind not in r.applies_to:
+ continue
+ out.append(r)
+ return out
+
+
+# ---------------------------------------------------------------------------
+# Block runner
+# ---------------------------------------------------------------------------
+
+def check_block(text, mode, indent_kind, input_type):
+ """Run all selected rules on a single block of DTS text. Returns a
+ list of (lineno, rule_name, message) tuples."""
+ lines = classify_lines(text)
+ ctx = Ctx(lines, text, mode, indent_kind)
+ rules = select_rules(mode, input_type)
+ findings = []
+ for r in rules:
+ for lineno, msg in r.check(ctx):
+ findings.append((lineno, r.name, msg))
+ findings.sort(key=lambda t: (t[0], t[1]))
+ return findings
+
+
+# ---------------------------------------------------------------------------
+# Input drivers (YAML examples vs raw DTS)
+# ---------------------------------------------------------------------------
+
+def _yaml_loader():
+ return ruamel.yaml.YAML()
+
+
+def iter_yaml_examples(filepath):
+ """Yield (example_text, base_lineno_in_file, example_index) tuples."""
+ yaml = _yaml_loader()
+ try:
+ with open(filepath, encoding='utf-8') as f:
+ data = yaml.load(f)
+ except Exception as e:
+ print('%s: error loading YAML: %s' % (filepath, e),
+ file=sys.stderr)
+ return
+ if not isinstance(data, dict) or 'examples' not in data:
+ return
+ examples = data['examples']
+ if not hasattr(examples, '__iter__'):
+ return
+ for i, ex in enumerate(examples):
+ if not isinstance(ex, str):
+ continue
+ try:
+ base = examples.lc.item(i)[0] + 2
+ except Exception:
+ base = 1
+ yield (str(ex), base, i)
+
+
+def iter_dts_file(filepath):
+ """Treat the whole file as a single block."""
+ try:
+ with open(filepath, encoding='utf-8') as f:
+ text = f.read()
+ except Exception as e:
+ print('%s: error reading: %s' % (filepath, e), file=sys.stderr)
+ return
+ yield (text, 1, None)
+
+
+# ---------------------------------------------------------------------------
+# Top-level processing
+# ---------------------------------------------------------------------------
+
+def input_kind(filepath):
+ p = filepath.lower()
+ if p.endswith('.yaml') or p.endswith('.yml'):
+ return 'yaml'
+ if p.endswith('.dts'):
+ return 'dts'
+ if p.endswith('.dtsi'):
+ return 'dtsi'
+ if p.endswith('.dtso'):
+ return 'dtso'
+ return None
+
+
+# All input types that use tab indentation and follow DTS coding style.
+DTS_FAMILY = ('dts', 'dtsi', 'dtso')
+
+
+def collect_findings(filepath, mode):
+ """Return a (lines, count) pair for filepath. lines is a list of
+ formatted output strings; count is the number of findings."""
+ kind = input_kind(filepath)
+ if kind == 'yaml':
+ indent_kind = 'spaces'
+ iterator = iter_yaml_examples(filepath)
+ elif kind in DTS_FAMILY:
+ indent_kind = 'tab'
+ iterator = iter_dts_file(filepath)
+ else:
+ return (['%s: unknown file type, skipping' % filepath], 0)
+
+ out = []
+ for text, base, idx in iterator:
+ for lineno, rule, msg in check_block(text, mode, indent_kind, kind):
+ abs_line = base + lineno - 1
+ ex_tag = '' if idx is None else ' example %d' % idx
+ out.append('%s:%d:%s [%s] %s' %
+ (filepath, abs_line, ex_tag, rule, msg))
+ return (out, len(out))
+
+
+# Worker entry point for ProcessPoolExecutor.map(). Top-level so it is
+# picklable on every platform.
+def _worker(args):
+ filepath, mode = args
+ return collect_findings(filepath, mode)
+
+
+def main():
+ import os
+ ap = argparse.ArgumentParser(
+ description='Check DTS coding style on YAML examples and '
+ '.dts/.dtsi/.dtso files.',
+ fromfile_prefix_chars='@')
+ ap.add_argument('--mode', choices=('relaxed', 'strict'),
+ default='relaxed',
+ help='which rule set to apply (default: relaxed)')
+ ap.add_argument('-j', '--jobs', type=int, default=0,
+ metavar='N',
+ help='run N workers in parallel (default: respect '
+ 'the make jobserver via $PARALLELISM, otherwise '
+ 'os.cpu_count(); use 1 to disable multiprocessing)')
+ ap.add_argument('--list-rules', action='store_true',
+ help='print all rules with their mode and exit')
+ ap.add_argument('files', nargs='*', metavar='file',
+ help='YAML binding files or .dts/.dtsi/.dtso files; '
+ 'use @argfile to read paths from a file')
+ args = ap.parse_args()
+
+ if args.list_rules:
+ for r in RULES:
+ applies = ','.join(r.applies_to)
+ print('%-22s %-7s [%s] %s' %
+ (r.name, r.mode, applies, r.description))
+ return 0
+
+ if not args.files:
+ ap.error('no input files')
+
+ if args.jobs > 0:
+ jobs = args.jobs
+ else:
+ # When invoked under scripts/jobserver-exec, $PARALLELISM
+ # holds the slot count make has reserved for us; this lets
+ # `make -j N dt_binding_check` constrain our worker pool to N.
+ try:
+ jobs = int(os.environ['PARALLELISM'])
+ except (KeyError, ValueError):
+ jobs = os.cpu_count() or 1
+ # Single-process path: keep import surface small for tests and
+ # easy debugging.
+ if jobs == 1 or len(args.files) == 1:
+ total = 0
+ for f in args.files:
+ lines, n = collect_findings(f, args.mode)
+ for line in lines:
+ print(line, file=sys.stderr)
+ total += n
+ return 1 if total else 0
+
+ # Multi-process path. ex.map preserves input order so output is
+ # deterministic across runs.
+ from concurrent.futures import ProcessPoolExecutor
+ total = 0
+ work = [(f, args.mode) for f in args.files]
+ chunk = max(1, len(work) // (jobs * 8)) if work else 1
+ with ProcessPoolExecutor(max_workers=jobs) as ex:
+ for lines, n in ex.map(_worker, work, chunksize=chunk):
+ for line in lines:
+ print(line, file=sys.stderr)
+ total += n
+ return 1 if total else 0
+
+
+if __name__ == '__main__':
+ sys.exit(main())
diff --git a/scripts/dtc/dt-style-selftest/bad/dts-spaces.dts b/scripts/dtc/dt-style-selftest/bad/dts-spaces.dts
new file mode 100644
index 000000000000..9dad22adce51
--- /dev/null
+++ b/scripts/dtc/dt-style-selftest/bad/dts-spaces.dts
@@ -0,0 +1,12 @@
+// SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+/*
+ * Test fixture: a .dts using space indent (must use tabs).
+ */
+
+/dts-v1/;
+
+/ {
+ compatible = "example,test-board";
+ #address-cells = <1>;
+ #size-cells = <1>;
+};
diff --git a/scripts/dtc/dt-style-selftest/bad/yaml-child-addr-order.yaml b/scripts/dtc/dt-style-selftest/bad/yaml-child-addr-order.yaml
new file mode 100644
index 000000000000..3df56e69a1ff
--- /dev/null
+++ b/scripts/dtc/dt-style-selftest/bad/yaml-child-addr-order.yaml
@@ -0,0 +1,41 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/test-bad-child-order.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Test fixture with addressed children out of order
+
+maintainers:
+ - Test User <test@example.com>
+
+properties:
+ compatible:
+ const: example,test-child-order
+ reg:
+ maxItems: 1
+
+required:
+ - compatible
+ - reg
+
+additionalProperties: false
+
+examples:
+ - |
+ bus@10000 {
+ compatible = "simple-bus";
+ reg = <0x10000 0x1000>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ device@200 {
+ compatible = "example,test-child-order";
+ reg = <0x200 0x10>;
+ };
+
+ device@100 {
+ compatible = "example,test-child-order";
+ reg = <0x100 0x10>;
+ };
+ };
diff --git a/scripts/dtc/dt-style-selftest/bad/yaml-child-name-order.yaml b/scripts/dtc/dt-style-selftest/bad/yaml-child-name-order.yaml
new file mode 100644
index 000000000000..35d85e5573c2
--- /dev/null
+++ b/scripts/dtc/dt-style-selftest/bad/yaml-child-name-order.yaml
@@ -0,0 +1,37 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/test-bad-child-name-order.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Test fixture with unaddressed children out of name order
+
+maintainers:
+ - Test User <test@example.com>
+
+properties:
+ compatible:
+ const: example,test-child-name-order
+ reg:
+ maxItems: 1
+
+required:
+ - compatible
+ - reg
+
+additionalProperties: false
+
+examples:
+ - |
+ bus@10000 {
+ compatible = "simple-bus";
+ reg = <0x10000 0x1000>;
+
+ foo {
+ label = "foo";
+ };
+
+ bar {
+ label = "bar";
+ };
+ };
diff --git a/scripts/dtc/dt-style-selftest/bad/yaml-cont-align.yaml b/scripts/dtc/dt-style-selftest/bad/yaml-cont-align.yaml
new file mode 100644
index 000000000000..92778540b056
--- /dev/null
+++ b/scripts/dtc/dt-style-selftest/bad/yaml-cont-align.yaml
@@ -0,0 +1,30 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/test-bad-cont-align.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Test fixture with mis-aligned multi-line property
+
+maintainers:
+ - Test User <test@example.com>
+
+properties:
+ compatible:
+ const: example,test-cont-align
+ reg:
+ maxItems: 2
+
+required:
+ - compatible
+ - reg
+
+additionalProperties: false
+
+examples:
+ - |
+ foo@1000 {
+ compatible = "example,test-cont-align";
+ reg = <0x1000 0x100>,
+ <0x2000 0x100>;
+ };
diff --git a/scripts/dtc/dt-style-selftest/bad/yaml-digit-node-order.yaml b/scripts/dtc/dt-style-selftest/bad/yaml-digit-node-order.yaml
new file mode 100644
index 000000000000..44a9d25e5ba0
--- /dev/null
+++ b/scripts/dtc/dt-style-selftest/bad/yaml-digit-node-order.yaml
@@ -0,0 +1,37 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/test-bad-digit-node-order.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Test fixture with digit-leading nodes out of address order
+
+maintainers:
+ - Test User <test@example.com>
+
+properties:
+ compatible:
+ const: example,test-digit-node-order
+
+required:
+ - compatible
+
+additionalProperties: false
+
+examples:
+ - |
+ bus@0 {
+ compatible = "simple-bus";
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ 3d-engine@20 {
+ compatible = "example,3d-engine";
+ reg = <0x20 0x4>;
+ };
+
+ 1wire@10 {
+ compatible = "example,1wire";
+ reg = <0x10 0x4>;
+ };
+ };
diff --git a/scripts/dtc/dt-style-selftest/bad/yaml-hex-case.yaml b/scripts/dtc/dt-style-selftest/bad/yaml-hex-case.yaml
new file mode 100644
index 000000000000..b26d1bf58de9
--- /dev/null
+++ b/scripts/dtc/dt-style-selftest/bad/yaml-hex-case.yaml
@@ -0,0 +1,29 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/test-bad-hex-case.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Test fixture with uppercase hex literals
+
+maintainers:
+ - Test User <test@example.com>
+
+properties:
+ compatible:
+ const: example,test-hex-case
+ reg:
+ maxItems: 1
+
+required:
+ - compatible
+ - reg
+
+additionalProperties: false
+
+examples:
+ - |
+ foo@1000 {
+ compatible = "example,test-hex-case";
+ reg = <0xABCD 0x100>;
+ };
diff --git a/scripts/dtc/dt-style-selftest/bad/yaml-indent-strict.yaml b/scripts/dtc/dt-style-selftest/bad/yaml-indent-strict.yaml
new file mode 100644
index 000000000000..bee4cf118d73
--- /dev/null
+++ b/scripts/dtc/dt-style-selftest/bad/yaml-indent-strict.yaml
@@ -0,0 +1,29 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/test-bad-indent-strict.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Test fixture using 2-space indent (rejected by strict mode)
+
+maintainers:
+ - Test User <test@example.com>
+
+properties:
+ compatible:
+ const: example,test-indent-strict
+ reg:
+ maxItems: 1
+
+required:
+ - compatible
+ - reg
+
+additionalProperties: false
+
+examples:
+ - |
+ device@1000 {
+ compatible = "example,test-indent-strict";
+ reg = <0x1000 0x100>;
+ };
diff --git a/scripts/dtc/dt-style-selftest/bad/yaml-label-in-string.yaml b/scripts/dtc/dt-style-selftest/bad/yaml-label-in-string.yaml
new file mode 100644
index 000000000000..ba512869b702
--- /dev/null
+++ b/scripts/dtc/dt-style-selftest/bad/yaml-label-in-string.yaml
@@ -0,0 +1,30 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/test-bad-label-in-string.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Test fixture where a label is only "referenced" inside a string
+
+maintainers:
+ - Test User <test@example.com>
+
+properties:
+ compatible:
+ const: example,test-label-in-string
+ reg:
+ maxItems: 1
+
+required:
+ - compatible
+ - reg
+
+additionalProperties: false
+
+examples:
+ - |
+ foo: device@1000 {
+ compatible = "example,test-label-in-string";
+ reg = <0x1000 0x100>;
+ info = "see &foo for details";
+ };
diff --git a/scripts/dtc/dt-style-selftest/bad/yaml-line-length.yaml b/scripts/dtc/dt-style-selftest/bad/yaml-line-length.yaml
new file mode 100644
index 000000000000..64427bf1c385
--- /dev/null
+++ b/scripts/dtc/dt-style-selftest/bad/yaml-line-length.yaml
@@ -0,0 +1,29 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/test-bad-line-length.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Test fixture exceeding 80 columns
+
+maintainers:
+ - Test User <test@example.com>
+
+properties:
+ compatible:
+ const: example,test-line-length
+ reg:
+ maxItems: 1
+
+required:
+ - compatible
+ - reg
+
+additionalProperties: false
+
+examples:
+ - |
+ foo@1000 {
+ compatible = "example,test-line-length-this-is-a-very-long-name-indeed-yeah";
+ reg = <0x1000 0x100>;
+ };
diff --git a/scripts/dtc/dt-style-selftest/bad/yaml-mixed-indent.yaml b/scripts/dtc/dt-style-selftest/bad/yaml-mixed-indent.yaml
new file mode 100644
index 000000000000..5401d1a423a1
--- /dev/null
+++ b/scripts/dtc/dt-style-selftest/bad/yaml-mixed-indent.yaml
@@ -0,0 +1,29 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/test-bad-mixed-indent.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Test fixture mixing tabs and spaces in indent
+
+maintainers:
+ - Test User <test@example.com>
+
+properties:
+ compatible:
+ const: example,test-mixed
+ reg:
+ maxItems: 1
+
+required:
+ - compatible
+ - reg
+
+additionalProperties: false
+
+examples:
+ - |
+ device@1000 {
+ compatible = "example,test-mixed";
+ reg = <0x1000 0x100>;
+ };
diff --git a/scripts/dtc/dt-style-selftest/bad/yaml-multi-close.yaml b/scripts/dtc/dt-style-selftest/bad/yaml-multi-close.yaml
new file mode 100644
index 000000000000..4d9fa27b50a2
--- /dev/null
+++ b/scripts/dtc/dt-style-selftest/bad/yaml-multi-close.yaml
@@ -0,0 +1,35 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/test-bad-multi-close.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Test fixture with two closing braces on one line
+
+maintainers:
+ - Test User <test@example.com>
+
+properties:
+ compatible:
+ const: example,test-multi-close
+ reg:
+ maxItems: 1
+
+required:
+ - compatible
+ - reg
+
+additionalProperties: false
+
+examples:
+ - |
+ bus@10000 {
+ compatible = "simple-bus";
+ reg = <0x10000 0x1000>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ device@100 {
+ compatible = "example,test-multi-close";
+ reg = <0x100 0x10>;
+ }; };
diff --git a/scripts/dtc/dt-style-selftest/bad/yaml-node-close.yaml b/scripts/dtc/dt-style-selftest/bad/yaml-node-close.yaml
new file mode 100644
index 000000000000..e107659fd9e8
--- /dev/null
+++ b/scripts/dtc/dt-style-selftest/bad/yaml-node-close.yaml
@@ -0,0 +1,31 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/test-bad-node-close.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Test fixture with closing brace not on its own line
+
+maintainers:
+ - Test User <test@example.com>
+
+properties:
+ compatible:
+ const: example,test-node-close
+ reg:
+ maxItems: 1
+
+required:
+ - compatible
+ - reg
+
+additionalProperties: false
+
+examples:
+ - |
+ bus@10000 {
+ compatible = "simple-bus";
+ reg = <0x10000 0x1000>;
+
+ empty {};
+ };
diff --git a/scripts/dtc/dt-style-selftest/bad/yaml-prop-order.yaml b/scripts/dtc/dt-style-selftest/bad/yaml-prop-order.yaml
new file mode 100644
index 000000000000..75582a3d2f6e
--- /dev/null
+++ b/scripts/dtc/dt-style-selftest/bad/yaml-prop-order.yaml
@@ -0,0 +1,29 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/test-bad-prop-order.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Test fixture with reg before compatible
+
+maintainers:
+ - Test User <test@example.com>
+
+properties:
+ compatible:
+ const: example,test-prop-order
+ reg:
+ maxItems: 1
+
+required:
+ - compatible
+ - reg
+
+additionalProperties: false
+
+examples:
+ - |
+ device@1000 {
+ reg = <0x1000 0x100>;
+ compatible = "example,test-prop-order";
+ };
diff --git a/scripts/dtc/dt-style-selftest/bad/yaml-prop-pairing.yaml b/scripts/dtc/dt-style-selftest/bad/yaml-prop-pairing.yaml
new file mode 100644
index 000000000000..767ab21c39f3
--- /dev/null
+++ b/scripts/dtc/dt-style-selftest/bad/yaml-prop-pairing.yaml
@@ -0,0 +1,33 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/test-bad-prop-pairing.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Test fixture exercising <x>-names and pinctrl-names pairing
+
+maintainers:
+ - Test User <test@example.com>
+
+properties:
+ compatible:
+ const: example,test-prop-pairing
+ reg:
+ maxItems: 1
+
+required:
+ - compatible
+ - reg
+
+additionalProperties: false
+
+examples:
+ - |
+ foo@1000 {
+ compatible = "example,test-prop-pairing";
+ reg = <0x1000 0x100>;
+ clock-names = "bus";
+ clocks = <&clk 0>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&p0>;
+ };
diff --git a/scripts/dtc/dt-style-selftest/bad/yaml-required-blank.yaml b/scripts/dtc/dt-style-selftest/bad/yaml-required-blank.yaml
new file mode 100644
index 000000000000..8bb53240cffa
--- /dev/null
+++ b/scripts/dtc/dt-style-selftest/bad/yaml-required-blank.yaml
@@ -0,0 +1,33 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/test-bad-required-blank.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Test fixture missing required blank lines
+
+maintainers:
+ - Test User <test@example.com>
+
+properties:
+ compatible:
+ const: example,test-required-blank
+ reg:
+ maxItems: 1
+
+required:
+ - compatible
+ - reg
+
+additionalProperties: false
+
+examples:
+ - |
+ bus@10000 {
+ compatible = "simple-bus";
+ reg = <0x10000 0x1000>;
+ status = "okay";
+ child@100 {
+ reg = <0x100>;
+ };
+ };
diff --git a/scripts/dtc/dt-style-selftest/bad/yaml-tab.yaml b/scripts/dtc/dt-style-selftest/bad/yaml-tab.yaml
new file mode 100644
index 000000000000..487d07ff8cb6
--- /dev/null
+++ b/scripts/dtc/dt-style-selftest/bad/yaml-tab.yaml
@@ -0,0 +1,29 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/test-bad-tab.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Test fixture with a tab in a DTS line
+
+maintainers:
+ - Test User <test@example.com>
+
+properties:
+ compatible:
+ const: example,test-tab
+ reg:
+ maxItems: 1
+
+required:
+ - compatible
+ - reg
+
+additionalProperties: false
+
+examples:
+ - |
+ device@1000 {
+ compatible = "example,test-tab";
+ reg = <0x1000 0x100>; /* registers */
+ };
diff --git a/scripts/dtc/dt-style-selftest/bad/yaml-trailing-comment.yaml b/scripts/dtc/dt-style-selftest/bad/yaml-trailing-comment.yaml
new file mode 100644
index 000000000000..2368ada8106f
--- /dev/null
+++ b/scripts/dtc/dt-style-selftest/bad/yaml-trailing-comment.yaml
@@ -0,0 +1,26 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/test-bad-trailing-comment.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Test fixture with properties out of order behind trailing comments
+
+maintainers:
+ - Test User <test@example.com>
+
+properties:
+ compatible:
+ const: example,test-trailing-comment
+
+required:
+ - compatible
+
+additionalProperties: false
+
+examples:
+ - |
+ foo@0 { /* the device node */
+ reg = <0x0 0x4>; /* registers */
+ compatible = "example,test-trailing-comment"; // misplaced
+ };
diff --git a/scripts/dtc/dt-style-selftest/bad/yaml-trailing-ws.yaml b/scripts/dtc/dt-style-selftest/bad/yaml-trailing-ws.yaml
new file mode 100644
index 000000000000..5c4b4bd833c5
--- /dev/null
+++ b/scripts/dtc/dt-style-selftest/bad/yaml-trailing-ws.yaml
@@ -0,0 +1,29 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/test-bad-trailing.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Test fixture with trailing whitespace
+
+maintainers:
+ - Test User <test@example.com>
+
+properties:
+ compatible:
+ const: example,test-trailing
+ reg:
+ maxItems: 1
+
+required:
+ - compatible
+ - reg
+
+additionalProperties: false
+
+examples:
+ - |
+ device@1000 {
+ compatible = "example,test-trailing";
+ reg = <0x1000 0x100>;
+ };
diff --git a/scripts/dtc/dt-style-selftest/bad/yaml-unclosed-comment.yaml b/scripts/dtc/dt-style-selftest/bad/yaml-unclosed-comment.yaml
new file mode 100644
index 000000000000..63c1c08712a5
--- /dev/null
+++ b/scripts/dtc/dt-style-selftest/bad/yaml-unclosed-comment.yaml
@@ -0,0 +1,30 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/test-bad-unclosed-comment.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Test fixture with an unclosed /* block comment
+
+maintainers:
+ - Test User <test@example.com>
+
+properties:
+ compatible:
+ const: example,test-unclosed-comment
+ reg:
+ maxItems: 1
+
+required:
+ - compatible
+ - reg
+
+additionalProperties: false
+
+examples:
+ - |
+ /* this comment never closes
+ device@1000 {
+ compatible = "example,test-unclosed-comment";
+ reg = <0x1000 0x100>;
+ };
diff --git a/scripts/dtc/dt-style-selftest/bad/yaml-unit-addr-prefix.yaml b/scripts/dtc/dt-style-selftest/bad/yaml-unit-addr-prefix.yaml
new file mode 100644
index 000000000000..9b3fe508c5fd
--- /dev/null
+++ b/scripts/dtc/dt-style-selftest/bad/yaml-unit-addr-prefix.yaml
@@ -0,0 +1,29 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/test-bad-unit-addr-prefix.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Test fixture with 0x-prefixed unit address
+
+maintainers:
+ - Test User <test@example.com>
+
+properties:
+ compatible:
+ const: example,test-unit-addr-prefix
+ reg:
+ maxItems: 1
+
+required:
+ - compatible
+ - reg
+
+additionalProperties: false
+
+examples:
+ - |
+ memory@0x1000 {
+ compatible = "example,test-unit-addr-prefix";
+ reg = <0x1000 0x100>;
+ };
diff --git a/scripts/dtc/dt-style-selftest/bad/yaml-unit-addr.yaml b/scripts/dtc/dt-style-selftest/bad/yaml-unit-addr.yaml
new file mode 100644
index 000000000000..93705cd45410
--- /dev/null
+++ b/scripts/dtc/dt-style-selftest/bad/yaml-unit-addr.yaml
@@ -0,0 +1,29 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/test-bad-unit-addr.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Test fixture with malformed unit address
+
+maintainers:
+ - Test User <test@example.com>
+
+properties:
+ compatible:
+ const: example,test-unit-addr
+ reg:
+ maxItems: 1
+
+required:
+ - compatible
+ - reg
+
+additionalProperties: false
+
+examples:
+ - |
+ foo@01000 {
+ compatible = "example,test-unit-addr";
+ reg = <0x1000 0x100>;
+ };
diff --git a/scripts/dtc/dt-style-selftest/bad/yaml-unused-label.yaml b/scripts/dtc/dt-style-selftest/bad/yaml-unused-label.yaml
new file mode 100644
index 000000000000..28d7176cbf08
--- /dev/null
+++ b/scripts/dtc/dt-style-selftest/bad/yaml-unused-label.yaml
@@ -0,0 +1,29 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/test-bad-unused-label.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Test fixture with an unused label
+
+maintainers:
+ - Test User <test@example.com>
+
+properties:
+ compatible:
+ const: example,test-unused-label
+ reg:
+ maxItems: 1
+
+required:
+ - compatible
+ - reg
+
+additionalProperties: false
+
+examples:
+ - |
+ dev: device@1000 {
+ compatible = "example,test-unused-label";
+ reg = <0x1000 0x100>;
+ };
diff --git a/scripts/dtc/dt-style-selftest/bad/yaml-value-ws-multiline.yaml b/scripts/dtc/dt-style-selftest/bad/yaml-value-ws-multiline.yaml
new file mode 100644
index 000000000000..504bf0931c27
--- /dev/null
+++ b/scripts/dtc/dt-style-selftest/bad/yaml-value-ws-multiline.yaml
@@ -0,0 +1,27 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/test-bad-value-ws-multiline.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Test fixture with extra whitespace in a multi-line cell array
+
+maintainers:
+ - Test User <test@example.com>
+
+properties:
+ compatible:
+ const: example,test-value-ws-multiline
+
+required:
+ - compatible
+
+additionalProperties: false
+
+examples:
+ - |
+ foo@0 {
+ compatible = "example,test-value-ws-multiline";
+ reg = < 0x0 0x4
+ 0x8 0xc>;
+ };
diff --git a/scripts/dtc/dt-style-selftest/bad/yaml-value-ws.yaml b/scripts/dtc/dt-style-selftest/bad/yaml-value-ws.yaml
new file mode 100644
index 000000000000..342ab9f399f1
--- /dev/null
+++ b/scripts/dtc/dt-style-selftest/bad/yaml-value-ws.yaml
@@ -0,0 +1,29 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/test-bad-value-ws.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Test fixture with extra whitespace inside <...>
+
+maintainers:
+ - Test User <test@example.com>
+
+properties:
+ compatible:
+ const: example,test-value-ws
+ reg:
+ maxItems: 1
+
+required:
+ - compatible
+ - reg
+
+additionalProperties: false
+
+examples:
+ - |
+ foo@1000 {
+ compatible = "example,test-value-ws";
+ reg = < 0x1000 0x100 >;
+ };
diff --git a/scripts/dtc/dt-style-selftest/expected/dts-spaces.dts.txt b/scripts/dtc/dt-style-selftest/expected/dts-spaces.dts.txt
new file mode 100644
index 000000000000..070025c4568c
--- /dev/null
+++ b/scripts/dtc/dt-style-selftest/expected/dts-spaces.dts.txt
@@ -0,0 +1,2 @@
+# mode=relaxed
+bad/dts-spaces.dts:1: [indent-unit-dts] indent unit must be 1 tab in DTS, got ' '
diff --git a/scripts/dtc/dt-style-selftest/expected/yaml-child-addr-order.yaml.txt b/scripts/dtc/dt-style-selftest/expected/yaml-child-addr-order.yaml.txt
new file mode 100644
index 000000000000..f0db79a0018b
--- /dev/null
+++ b/scripts/dtc/dt-style-selftest/expected/yaml-child-addr-order.yaml.txt
@@ -0,0 +1,2 @@
+# mode=strict
+bad/yaml-child-addr-order.yaml:37: example 0 [child-address-order] child node @100 out of address order
diff --git a/scripts/dtc/dt-style-selftest/expected/yaml-child-name-order.yaml.txt b/scripts/dtc/dt-style-selftest/expected/yaml-child-name-order.yaml.txt
new file mode 100644
index 000000000000..bb434b126191
--- /dev/null
+++ b/scripts/dtc/dt-style-selftest/expected/yaml-child-name-order.yaml.txt
@@ -0,0 +1,2 @@
+# mode=strict
+bad/yaml-child-name-order.yaml:34: example 0 [child-name-order] child node 'bar' out of name order
diff --git a/scripts/dtc/dt-style-selftest/expected/yaml-cont-align.yaml.txt b/scripts/dtc/dt-style-selftest/expected/yaml-cont-align.yaml.txt
new file mode 100644
index 000000000000..b5576dd0f6b1
--- /dev/null
+++ b/scripts/dtc/dt-style-selftest/expected/yaml-cont-align.yaml.txt
@@ -0,0 +1,2 @@
+# mode=strict
+bad/yaml-cont-align.yaml:29: example 0 [continuation-alignment] continuation should align to column 11 (under "<" or \")
diff --git a/scripts/dtc/dt-style-selftest/expected/yaml-digit-node-order.yaml.txt b/scripts/dtc/dt-style-selftest/expected/yaml-digit-node-order.yaml.txt
new file mode 100644
index 000000000000..6de275e2dcb5
--- /dev/null
+++ b/scripts/dtc/dt-style-selftest/expected/yaml-digit-node-order.yaml.txt
@@ -0,0 +1,2 @@
+# mode=strict
+bad/yaml-digit-node-order.yaml:33: example 0 [child-address-order] child node @10 out of address order
diff --git a/scripts/dtc/dt-style-selftest/expected/yaml-hex-case.yaml.txt b/scripts/dtc/dt-style-selftest/expected/yaml-hex-case.yaml.txt
new file mode 100644
index 000000000000..6600f7cd1ba5
--- /dev/null
+++ b/scripts/dtc/dt-style-selftest/expected/yaml-hex-case.yaml.txt
@@ -0,0 +1,2 @@
+# mode=strict
+bad/yaml-hex-case.yaml:28: example 0 [hex-case] hex literal '0xABCD' must be lowercase
diff --git a/scripts/dtc/dt-style-selftest/expected/yaml-indent-strict.yaml.txt b/scripts/dtc/dt-style-selftest/expected/yaml-indent-strict.yaml.txt
new file mode 100644
index 000000000000..5ef290d3a847
--- /dev/null
+++ b/scripts/dtc/dt-style-selftest/expected/yaml-indent-strict.yaml.txt
@@ -0,0 +1,2 @@
+# mode=strict
+bad/yaml-indent-strict.yaml:26: example 0 [indent-unit-strict] indent unit must be 4 spaces in strict mode, got ' '
diff --git a/scripts/dtc/dt-style-selftest/expected/yaml-label-in-string.yaml.txt b/scripts/dtc/dt-style-selftest/expected/yaml-label-in-string.yaml.txt
new file mode 100644
index 000000000000..05da06f81364
--- /dev/null
+++ b/scripts/dtc/dt-style-selftest/expected/yaml-label-in-string.yaml.txt
@@ -0,0 +1,2 @@
+# mode=strict
+bad/yaml-label-in-string.yaml:26: example 0 [unused-labels] label 'foo' defined but never &-referenced
diff --git a/scripts/dtc/dt-style-selftest/expected/yaml-line-length.yaml.txt b/scripts/dtc/dt-style-selftest/expected/yaml-line-length.yaml.txt
new file mode 100644
index 000000000000..89b36360caa4
--- /dev/null
+++ b/scripts/dtc/dt-style-selftest/expected/yaml-line-length.yaml.txt
@@ -0,0 +1,2 @@
+# mode=strict
+bad/yaml-line-length.yaml:27: example 0 [line-length] line exceeds 80 columns (81)
diff --git a/scripts/dtc/dt-style-selftest/expected/yaml-mixed-indent.yaml.txt b/scripts/dtc/dt-style-selftest/expected/yaml-mixed-indent.yaml.txt
new file mode 100644
index 000000000000..c989f8f19853
--- /dev/null
+++ b/scripts/dtc/dt-style-selftest/expected/yaml-mixed-indent.yaml.txt
@@ -0,0 +1,3 @@
+# mode=relaxed
+bad/yaml-mixed-indent.yaml:27: example 0 [mixed-indent-chars] mixed tabs and spaces in indent
+bad/yaml-mixed-indent.yaml:27: example 0 [tab-in-dts] tab character not allowed in DTS example
diff --git a/scripts/dtc/dt-style-selftest/expected/yaml-multi-close.yaml.txt b/scripts/dtc/dt-style-selftest/expected/yaml-multi-close.yaml.txt
new file mode 100644
index 000000000000..637d0f8ea103
--- /dev/null
+++ b/scripts/dtc/dt-style-selftest/expected/yaml-multi-close.yaml.txt
@@ -0,0 +1,3 @@
+# mode=strict
+bad/yaml-multi-close.yaml:35: example 0 [indent-consistent] indent mismatch (expected depth 0 * ' ')
+bad/yaml-multi-close.yaml:35: example 0 [node-close-alone] closing brace must be on its own line
diff --git a/scripts/dtc/dt-style-selftest/expected/yaml-node-close.yaml.txt b/scripts/dtc/dt-style-selftest/expected/yaml-node-close.yaml.txt
new file mode 100644
index 000000000000..ee894747b5b9
--- /dev/null
+++ b/scripts/dtc/dt-style-selftest/expected/yaml-node-close.yaml.txt
@@ -0,0 +1,2 @@
+# mode=strict
+bad/yaml-node-close.yaml:30: example 0 [node-close-alone] closing brace must be on its own line
diff --git a/scripts/dtc/dt-style-selftest/expected/yaml-prop-order.yaml.txt b/scripts/dtc/dt-style-selftest/expected/yaml-prop-order.yaml.txt
new file mode 100644
index 000000000000..578df7209170
--- /dev/null
+++ b/scripts/dtc/dt-style-selftest/expected/yaml-prop-order.yaml.txt
@@ -0,0 +1,2 @@
+# mode=strict
+bad/yaml-prop-order.yaml:28: example 0 [property-order] property 'compatible' out of canonical order (should sort before 'reg')
diff --git a/scripts/dtc/dt-style-selftest/expected/yaml-prop-pairing.yaml.txt b/scripts/dtc/dt-style-selftest/expected/yaml-prop-pairing.yaml.txt
new file mode 100644
index 000000000000..e6e21349a939
--- /dev/null
+++ b/scripts/dtc/dt-style-selftest/expected/yaml-prop-pairing.yaml.txt
@@ -0,0 +1,3 @@
+# mode=strict
+bad/yaml-prop-pairing.yaml:30: example 0 [property-order] property 'clocks' out of canonical order (should sort before 'clock-names')
+bad/yaml-prop-pairing.yaml:32: example 0 [property-order] property 'pinctrl-0' out of canonical order (should sort before 'pinctrl-names')
diff --git a/scripts/dtc/dt-style-selftest/expected/yaml-required-blank.yaml.txt b/scripts/dtc/dt-style-selftest/expected/yaml-required-blank.yaml.txt
new file mode 100644
index 000000000000..04ea0bacdcb9
--- /dev/null
+++ b/scripts/dtc/dt-style-selftest/expected/yaml-required-blank.yaml.txt
@@ -0,0 +1,3 @@
+# mode=strict
+bad/yaml-required-blank.yaml:29: example 0 [required-blank-lines] "status" must be preceded by a blank line
+bad/yaml-required-blank.yaml:30: example 0 [required-blank-lines] child node must be preceded by a blank line
diff --git a/scripts/dtc/dt-style-selftest/expected/yaml-tab.yaml.txt b/scripts/dtc/dt-style-selftest/expected/yaml-tab.yaml.txt
new file mode 100644
index 000000000000..9e83246fbaa1
--- /dev/null
+++ b/scripts/dtc/dt-style-selftest/expected/yaml-tab.yaml.txt
@@ -0,0 +1,2 @@
+# mode=relaxed
+bad/yaml-tab.yaml:28: example 0 [tab-in-dts] tab character not allowed in DTS example
diff --git a/scripts/dtc/dt-style-selftest/expected/yaml-trailing-comment.yaml.txt b/scripts/dtc/dt-style-selftest/expected/yaml-trailing-comment.yaml.txt
new file mode 100644
index 000000000000..69dbb1d03239
--- /dev/null
+++ b/scripts/dtc/dt-style-selftest/expected/yaml-trailing-comment.yaml.txt
@@ -0,0 +1,2 @@
+# mode=strict
+bad/yaml-trailing-comment.yaml:25: example 0 [property-order] property 'compatible' out of canonical order (should sort before 'reg')
diff --git a/scripts/dtc/dt-style-selftest/expected/yaml-trailing-ws.yaml.txt b/scripts/dtc/dt-style-selftest/expected/yaml-trailing-ws.yaml.txt
new file mode 100644
index 000000000000..cfdbc8476c73
--- /dev/null
+++ b/scripts/dtc/dt-style-selftest/expected/yaml-trailing-ws.yaml.txt
@@ -0,0 +1,2 @@
+# mode=relaxed
+bad/yaml-trailing-ws.yaml:27: example 0 [trailing-whitespace] trailing whitespace
diff --git a/scripts/dtc/dt-style-selftest/expected/yaml-unclosed-comment.yaml.txt b/scripts/dtc/dt-style-selftest/expected/yaml-unclosed-comment.yaml.txt
new file mode 100644
index 000000000000..9a30ee7145e6
--- /dev/null
+++ b/scripts/dtc/dt-style-selftest/expected/yaml-unclosed-comment.yaml.txt
@@ -0,0 +1,2 @@
+# mode=strict
+bad/yaml-unclosed-comment.yaml:26: example 0 [unclosed-block-comment] unclosed /* block comment
diff --git a/scripts/dtc/dt-style-selftest/expected/yaml-unit-addr-prefix.yaml.txt b/scripts/dtc/dt-style-selftest/expected/yaml-unit-addr-prefix.yaml.txt
new file mode 100644
index 000000000000..8dec6c1176b5
--- /dev/null
+++ b/scripts/dtc/dt-style-selftest/expected/yaml-unit-addr-prefix.yaml.txt
@@ -0,0 +1,2 @@
+# mode=strict
+bad/yaml-unit-addr-prefix.yaml:26: example 0 [unit-address-format] unit address '0x1000' must not have a "0x" prefix
diff --git a/scripts/dtc/dt-style-selftest/expected/yaml-unit-addr.yaml.txt b/scripts/dtc/dt-style-selftest/expected/yaml-unit-addr.yaml.txt
new file mode 100644
index 000000000000..b52f0ef20bee
--- /dev/null
+++ b/scripts/dtc/dt-style-selftest/expected/yaml-unit-addr.yaml.txt
@@ -0,0 +1,2 @@
+# mode=strict
+bad/yaml-unit-addr.yaml:26: example 0 [unit-address-format] unit address '01000' has leading zeros
diff --git a/scripts/dtc/dt-style-selftest/expected/yaml-unused-label.yaml.txt b/scripts/dtc/dt-style-selftest/expected/yaml-unused-label.yaml.txt
new file mode 100644
index 000000000000..4f00202f0902
--- /dev/null
+++ b/scripts/dtc/dt-style-selftest/expected/yaml-unused-label.yaml.txt
@@ -0,0 +1,2 @@
+# mode=strict
+bad/yaml-unused-label.yaml:26: example 0 [unused-labels] label 'dev' defined but never &-referenced
diff --git a/scripts/dtc/dt-style-selftest/expected/yaml-value-ws-multiline.yaml.txt b/scripts/dtc/dt-style-selftest/expected/yaml-value-ws-multiline.yaml.txt
new file mode 100644
index 000000000000..3df55b1762d0
--- /dev/null
+++ b/scripts/dtc/dt-style-selftest/expected/yaml-value-ws-multiline.yaml.txt
@@ -0,0 +1,2 @@
+# mode=strict
+bad/yaml-value-ws-multiline.yaml:25: example 0 [value-whitespace] extra whitespace inside <...>
diff --git a/scripts/dtc/dt-style-selftest/expected/yaml-value-ws.yaml.txt b/scripts/dtc/dt-style-selftest/expected/yaml-value-ws.yaml.txt
new file mode 100644
index 000000000000..cbb5f88fe85f
--- /dev/null
+++ b/scripts/dtc/dt-style-selftest/expected/yaml-value-ws.yaml.txt
@@ -0,0 +1,2 @@
+# mode=strict
+bad/yaml-value-ws.yaml:28: example 0 [value-whitespace] extra whitespace inside <...>
diff --git a/scripts/dtc/dt-style-selftest/good/dts-cont-align.dts b/scripts/dtc/dt-style-selftest/good/dts-cont-align.dts
new file mode 100644
index 000000000000..36fb4eefcd83
--- /dev/null
+++ b/scripts/dtc/dt-style-selftest/good/dts-cont-align.dts
@@ -0,0 +1,26 @@
+// SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+/*
+ * Test fixture: tab-indented .dts with a tab-and-space aligned
+ * multi-line property. Continuation lines mix tabs for indent and
+ * spaces for alignment by design; that must not be flagged.
+ */
+
+/dts-v1/;
+
+/ {
+ compatible = "example,test-board";
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ interrupt-controller@10000 {
+ compatible = "example,intc";
+ reg = <0x10000 0x1000>;
+ interrupts = <1 2 3>,
+ <4 5 6>,
+ <7 8 9>;
+ pinmux = <
+ 0x01
+ 0x02
+ >;
+ };
+};
diff --git a/scripts/dtc/dt-style-selftest/good/dts-tab.dts b/scripts/dtc/dt-style-selftest/good/dts-tab.dts
new file mode 100644
index 000000000000..ab7b5d1242ba
--- /dev/null
+++ b/scripts/dtc/dt-style-selftest/good/dts-tab.dts
@@ -0,0 +1,29 @@
+// SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+/*
+ * Test fixture: a properly formatted .dts using one-tab indent.
+ */
+
+/dts-v1/;
+
+/ {
+ compatible = "example,test-board";
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ bus@10000 {
+ compatible = "simple-bus";
+ reg = <0x10000 0x1000>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ device@100 {
+ compatible = "example,test";
+ reg = <0x100 0x10>;
+ };
+
+ device@200 {
+ compatible = "example,test";
+ reg = <0x200 0x10>;
+ };
+ };
+};
diff --git a/scripts/dtc/dt-style-selftest/good/yaml-4space.yaml b/scripts/dtc/dt-style-selftest/good/yaml-4space.yaml
new file mode 100644
index 000000000000..1502f803c24c
--- /dev/null
+++ b/scripts/dtc/dt-style-selftest/good/yaml-4space.yaml
@@ -0,0 +1,41 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/test-good-4space.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Test fixture for dt-check-style
+
+maintainers:
+ - Test User <test@example.com>
+
+properties:
+ compatible:
+ const: example,test-4space
+ reg:
+ maxItems: 1
+
+required:
+ - compatible
+ - reg
+
+additionalProperties: false
+
+examples:
+ - |
+ bus@10000 {
+ compatible = "simple-bus";
+ reg = <0x10000 0x1000>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ device@100 {
+ compatible = "example,test-4space";
+ reg = <0x100 0x10>;
+ };
+
+ device@200 {
+ compatible = "example,test-4space";
+ reg = <0x200 0x10>;
+ };
+ };
diff --git a/scripts/dtc/dt-style-selftest/good/yaml-tricky-parsing.yaml b/scripts/dtc/dt-style-selftest/good/yaml-tricky-parsing.yaml
new file mode 100644
index 000000000000..a836d5f36b93
--- /dev/null
+++ b/scripts/dtc/dt-style-selftest/good/yaml-tricky-parsing.yaml
@@ -0,0 +1,57 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/test-good-tricky-parsing.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Test fixture exercising parser corner cases
+
+description: |
+ Covers patterns that previously broke the classifier:
+ - "/* ... */ code;" with code following the closing of a one-line
+ block comment must still parse the code.
+ - a label on a node whose name starts with a digit (1wire@10).
+ - a multi-line C preprocessor macro using backslash continuations
+ must not be parsed as DTS lines.
+ - a /* comment that opens and closes on the same code line must
+ not leave the parser in block-comment state.
+
+maintainers:
+ - Test User <test@example.com>
+
+properties:
+ compatible:
+ const: example,test-tricky-parsing
+ reg:
+ maxItems: 1
+
+required:
+ - compatible
+ - reg
+
+additionalProperties: false
+
+examples:
+ - |
+ #define MY_REG(a, b) \
+ ((a) << 16 | \
+ (b) << 0)
+
+ one_wire: 1wire@10 {
+ compatible = "example,test-tricky-parsing";
+ reg = <MY_REG(0x10, 0)>;
+ /* inline-closed */ status = "okay";
+ };
+
+ other: device@20 {
+ compatible = "example,test-tricky-parsing";
+ reg = <0x20 0x10>;
+ };
+
+ &one_wire {
+ status = "okay";
+ };
+
+ &other {
+ status = "okay";
+ };
diff --git a/scripts/dtc/dt-style-selftest/run.sh b/scripts/dtc/dt-style-selftest/run.sh
new file mode 100755
index 000000000000..8117dd9be90a
--- /dev/null
+++ b/scripts/dtc/dt-style-selftest/run.sh
@@ -0,0 +1,71 @@
+#!/bin/sh
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Run dt-check-style against fixtures under good/ and bad/.
+# good/ files must produce no output and exit 0 in both modes.
+# bad/ files must produce the expected output (in expected/<name>.txt)
+# and exit 1.
+#
+# The mode used for a bad fixture is whichever produces a violation:
+# trailing-whitespace and tab fixtures use the default (relaxed),
+# the rest use --mode=strict. The expected output files name the
+# mode in their first line.
+
+set -u
+
+here=$(cd "$(dirname "$0")" && pwd)
+tool="$here/../dt-check-style"
+fail=0
+
+run() {
+ file=$1
+ mode=$2
+ "$tool" --mode="$mode" "$file" 2>&1
+}
+
+# good/ -- must exit 0 and produce no output in both modes
+for f in "$here"/good/*; do
+ [ -e "$f" ] || continue
+ for mode in relaxed strict; do
+ out=$(run "$f" "$mode")
+ rc=$?
+ if [ -n "$out" ] || [ "$rc" -ne 0 ]; then
+ echo "FAIL good/$mode: $(basename "$f") (exit $rc, want 0):"
+ echo "$out" | sed 's/^/ /'
+ fail=$((fail + 1))
+ fi
+ done
+done
+
+# bad/ -- must match expected/<name>.txt
+for f in "$here"/bad/*; do
+ [ -e "$f" ] || continue
+ name=$(basename "$f")
+ expected="$here/expected/$name.txt"
+ if [ ! -f "$expected" ]; then
+ echo "FAIL bad: missing $expected"
+ fail=$((fail + 1))
+ continue
+ fi
+ mode=$(head -1 "$expected" | sed 's/^# mode=//')
+ body=$(tail -n +2 "$expected")
+ out=$(run "$f" "$mode")
+ rc=$?
+ # Strip the directory prefix so expected files are portable.
+ out=$(printf '%s\n' "$out" | sed "s|$here/bad/|bad/|g")
+ if [ "$out" != "$body" ] || [ "$rc" -ne 1 ]; then
+ echo "FAIL bad/$mode: $name (exit $rc, want 1):"
+ bf=$(mktemp)
+ printf '%s\n' "$body" > "$bf"
+ printf '%s\n' "$out" | diff -u "$bf" - | sed 's/^/ /'
+ rm -f "$bf"
+ fail=$((fail + 1))
+ fi
+done
+
+if [ "$fail" -eq 0 ]; then
+ echo "PASS"
+ exit 0
+fi
+echo "FAILED ($fail)"
+exit 1
diff --git a/scripts/gdb/linux/interrupts.py b/scripts/gdb/linux/interrupts.py
index f4f715a8f0e3..a68ae91b4531 100644
--- a/scripts/gdb/linux/interrupts.py
+++ b/scripts/gdb/linux/interrupts.py
@@ -20,7 +20,7 @@ def irq_desc_is_chained(desc):
def irqd_is_level(desc):
return desc['irq_data']['common']['state_use_accessors'] & constants.LX_IRQD_LEVEL
-def show_irq_desc(prec, irq):
+def show_irq_desc(prec, chip_width, irq):
text = ""
desc = mapletree.mtree_load(gdb.parse_and_eval("&sparse_irqs"), irq)
@@ -48,7 +48,7 @@ def show_irq_desc(prec, irq):
count = cpus.per_cpu(desc['kstat_irqs'], cpu)['cnt']
else:
count = 0
- text += "%10u" % (count)
+ text += "%10u " % (count)
name = "None"
if desc['irq_data']['chip']:
@@ -58,7 +58,7 @@ def show_irq_desc(prec, irq):
else:
name = "-"
- text += " %8s" % (name)
+ text += " %-*s" % (chip_width, name)
if desc['irq_data']['domain']:
text += " %*lu" % (prec, desc['irq_data']['hwirq'])
@@ -97,64 +97,29 @@ def show_irq_err_count(prec):
text += "%*s: %10u\n" % (prec, "ERR", cnt['counter'])
return text
-def x86_show_irqstat(prec, pfx, field, desc):
- irq_stat = gdb.parse_and_eval("&irq_stat")
+def x86_show_irqstat(prec, pfx, idx, desc):
+ irq_stat = gdb.parse_and_eval("&irq_stat.counts[%d]" %idx)
text = "%*s: " % (prec, pfx)
for cpu in cpus.each_online_cpu():
stat = cpus.per_cpu(irq_stat, cpu)
- text += "%10u " % (stat[field])
- text += " %s\n" % (desc)
- return text
-
-def x86_show_mce(prec, var, pfx, desc):
- pvar = gdb.parse_and_eval(var)
- text = "%*s: " % (prec, pfx)
- for cpu in cpus.each_online_cpu():
- text += "%10u " % (cpus.per_cpu(pvar, cpu).dereference())
- text += " %s\n" % (desc)
+ text += "%10u " % (stat.dereference())
+ text += desc
return text
def x86_show_interupts(prec):
- text = x86_show_irqstat(prec, "NMI", '__nmi_count', 'Non-maskable interrupts')
-
- if constants.LX_CONFIG_X86_LOCAL_APIC:
- text += x86_show_irqstat(prec, "LOC", 'apic_timer_irqs', "Local timer interrupts")
- text += x86_show_irqstat(prec, "SPU", 'irq_spurious_count', "Spurious interrupts")
- text += x86_show_irqstat(prec, "PMI", 'apic_perf_irqs', "Performance monitoring interrupts")
- text += x86_show_irqstat(prec, "IWI", 'apic_irq_work_irqs', "IRQ work interrupts")
- text += x86_show_irqstat(prec, "RTR", 'icr_read_retry_count', "APIC ICR read retries")
- if utils.gdb_eval_or_none("x86_platform_ipi_callback") is not None:
- text += x86_show_irqstat(prec, "PLT", 'x86_platform_ipis', "Platform interrupts")
-
- if constants.LX_CONFIG_SMP:
- text += x86_show_irqstat(prec, "RES", 'irq_resched_count', "Rescheduling interrupts")
- text += x86_show_irqstat(prec, "CAL", 'irq_call_count', "Function call interrupts")
- text += x86_show_irqstat(prec, "TLB", 'irq_tlb_count', "TLB shootdowns")
-
- if constants.LX_CONFIG_X86_THERMAL_VECTOR:
- text += x86_show_irqstat(prec, "TRM", 'irq_thermal_count', "Thermal events interrupts")
-
- if constants.LX_CONFIG_X86_MCE_THRESHOLD:
- text += x86_show_irqstat(prec, "THR", 'irq_threshold_count', "Threshold APIC interrupts")
-
- if constants.LX_CONFIG_X86_MCE_AMD:
- text += x86_show_irqstat(prec, "DFR", 'irq_deferred_error_count', "Deferred Error APIC interrupts")
+ info_type = gdb.lookup_type('struct irq_stat_info')
+ info = gdb.parse_and_eval('irq_stat_info')
+ bitmap = gdb.parse_and_eval('irq_stat_count_show')
+ bitsperlong = 8 * int(bitmap.type.target().sizeof)
- if constants.LX_CONFIG_X86_MCE:
- text += x86_show_mce(prec, "&mce_exception_count", "MCE", "Machine check exceptions")
- text += x86_show_mce(prec, "&mce_poll_count", "MCP", "Machine check polls")
-
- text += show_irq_err_count(prec)
-
- if constants.LX_CONFIG_X86_IO_APIC:
- cnt = utils.gdb_eval_or_none("irq_mis_count")
- if cnt is not None:
- text += "%*s: %10u\n" % (prec, "MIS", cnt['counter'])
-
- if constants.LX_CONFIG_KVM:
- text += x86_show_irqstat(prec, "PIN", 'kvm_posted_intr_ipis', 'Posted-interrupt notification event')
- text += x86_show_irqstat(prec, "NPI", 'kvm_posted_intr_nested_ipis', 'Nested posted-interrupt event')
- text += x86_show_irqstat(prec, "PIW", 'kvm_posted_intr_wakeup_ipis', 'Posted-interrupt wakeup event')
+ text = ""
+ for idx in range(int(info.type.sizeof / info_type.sizeof)):
+ show = bitmap[int(idx / bitsperlong)]
+ if not show & 1 << int(idx % bitsperlong):
+ continue
+ pfx = info[idx]['symbol'].string()
+ desc = info[idx]['text'].string()
+ text += x86_show_irqstat(prec, pfx, idx, desc)
return text
@@ -166,23 +131,19 @@ def arm_common_show_interrupts(prec):
if nr_ipi is None or ipi_desc is None or ipi_types is None:
return text
- if prec >= 4:
- sep = " "
- else:
- sep = ""
-
for ipi in range(nr_ipi):
- text += "%*s%u:%s" % (prec - 1, "IPI", ipi, sep)
+ text += "%*s%u: " % (prec - 1, "IPI", ipi)
desc = ipi_desc[ipi].cast(irq_desc_type.get_type().pointer())
if desc == 0:
continue
for cpu in cpus.each_online_cpu():
- text += "%10u" % (cpus.per_cpu(desc['kstat_irqs'], cpu)['cnt'])
- text += " %s" % (ipi_types[ipi].string())
+ text += "%10u " % (cpus.per_cpu(desc['kstat_irqs'], cpu)['cnt'])
+ text += "%s" % (ipi_types[ipi].string())
text += "\n"
return text
def aarch64_show_interrupts(prec):
+ # Does not work for ARM64 as "ipi_desc" is not available there
text = arm_common_show_interrupts(prec)
text += "%*s: %10lu\n" % (prec, "ERR", gdb.parse_and_eval("irq_err_count"))
return text
@@ -209,12 +170,19 @@ class LxInterruptList(gdb.Command):
super(LxInterruptList, self).__init__("lx-interruptlist", gdb.COMMAND_DATA)
def invoke(self, arg, from_tty):
- nr_irqs = gdb.parse_and_eval("nr_irqs")
- prec = 3
- j = 1000
- while prec < 10 and j <= nr_irqs:
- prec += 1
- j *= 10
+ nr_irqs = gdb.parse_and_eval("total_nr_irqs")
+ constr = utils.gdb_eval_or_none('irq_proc_constraints')
+
+ if constr:
+ prec = int(constr['num_prec'])
+ chip_width = int(constr['chip_width'])
+ else:
+ prec = 4
+ j = 10000
+ while prec < 10 and j <= nr_irqs:
+ prec += 1
+ j *= 10
+ chip_width = 8
gdb.write("%*s" % (prec + 8, ""))
for cpu in cpus.each_online_cpu():
@@ -225,7 +193,7 @@ class LxInterruptList(gdb.Command):
raise gdb.GdbError("Unable to find the sparse IRQ tree, is CONFIG_SPARSE_IRQ enabled?")
for irq in range(nr_irqs):
- gdb.write(show_irq_desc(prec, irq))
+ gdb.write(show_irq_desc(prec, chip_width, irq))
gdb.write(arch_show_interrupts(prec))
diff --git a/scripts/gdb/linux/timerlist.py b/scripts/gdb/linux/timerlist.py
index 9fb3436a217c..744b032e4d38 100644
--- a/scripts/gdb/linux/timerlist.py
+++ b/scripts/gdb/linux/timerlist.py
@@ -90,14 +90,10 @@ def print_cpu(hrtimer_bases, cpu, max_clock_bases):
text += f" .{'nohz':15s}: {int(bool(ts['flags'] & TS_FLAG_NOHZ))}\n"
text += f" .{'last_tick':15s}: {ts['last_tick']}\n"
text += f" .{'tick_stopped':15s}: {int(bool(ts['flags'] & TS_FLAG_STOPPED))}\n"
- text += f" .{'idle_jiffies':15s}: {ts['idle_jiffies']}\n"
text += f" .{'idle_calls':15s}: {ts['idle_calls']}\n"
text += f" .{'idle_sleeps':15s}: {ts['idle_sleeps']}\n"
text += f" .{'idle_entrytime':15s}: {ts['idle_entrytime']} nsecs\n"
text += f" .{'idle_waketime':15s}: {ts['idle_waketime']} nsecs\n"
- text += f" .{'idle_exittime':15s}: {ts['idle_exittime']} nsecs\n"
- text += f" .{'idle_sleeptime':15s}: {ts['idle_sleeptime']} nsecs\n"
- text += f" .{'iowait_sleeptime':15s}: {ts['iowait_sleeptime']} nsecs\n"
text += f" .{'last_jiffies':15s}: {ts['last_jiffies']}\n"
text += f" .{'next_timer':15s}: {ts['next_timer']}\n"
text += f" .{'idle_expires':15s}: {ts['idle_expires']} nsecs\n"
diff --git a/scripts/generate_rust_analyzer.py b/scripts/generate_rust_analyzer.py
index d5f9a0ca742c..dc1219736f77 100755
--- a/scripts/generate_rust_analyzer.py
+++ b/scripts/generate_rust_analyzer.py
@@ -26,6 +26,14 @@ def args_crates_cfgs(cfgs: List[str]) -> Dict[str, List[str]]:
return crates_cfgs
+def args_crates_envs(envs: List[str]) -> Dict[str, Dict[str, str]]:
+ crates_envs = {}
+ for env in envs:
+ crate, vals = env.split("=", 1)
+ crates_envs[crate] = dict(v.split("=", 1) for v in vals.split())
+
+ return crates_envs
+
class Dependency(TypedDict):
crate: int
name: str
@@ -61,6 +69,7 @@ def generate_crates(
sysroot_src: pathlib.Path,
external_src: Optional[pathlib.Path],
cfgs: List[str],
+ envs: List[str],
core_edition: str,
) -> List[Crate]:
# Generate the configuration list.
@@ -74,6 +83,7 @@ def generate_crates(
# Now fill the crates list.
crates: List[Crate] = []
crates_cfgs = args_crates_cfgs(cfgs)
+ crates_envs = args_crates_envs(envs)
def get_crate_name(path: pathlib.Path) -> str:
return invoke_rustc(["--print", "crate-name", str(path)])
@@ -92,6 +102,10 @@ def generate_crates(
is_workspace_member if is_workspace_member is not None else True
)
edition = edition if edition is not None else "2021"
+ crate_env = {
+ "RUST_MODFILE": "This is only for rust-analyzer",
+ **crates_envs.get(display_name, {}),
+ }
return {
"display_name": display_name,
"root_module": str(root_module),
@@ -99,9 +113,7 @@ def generate_crates(
"deps": deps,
"cfg": cfg,
"edition": edition,
- "env": {
- "RUST_MODFILE": "This is only for rust-analyzer"
- }
+ "env": crate_env,
}
def append_proc_macro_crate(
@@ -240,6 +252,12 @@ def generate_crates(
[std, proc_macro, proc_macro2, quote, syn],
)
+ zerocopy_derive = append_proc_macro_crate(
+ "zerocopy_derive",
+ srctree / "rust" / "zerocopy-derive" / "lib.rs",
+ [std, proc_macro, proc_macro2, quote, syn],
+ )
+
build_error = append_crate(
"build_error",
srctree / "rust" / "build_error.rs",
@@ -264,6 +282,12 @@ def generate_crates(
[core, compiler_builtins],
)
+ zerocopy = append_crate(
+ "zerocopy",
+ srctree / "rust" / "zerocopy" / "src" / "lib.rs",
+ [core, compiler_builtins],
+ )
+
def append_crate_with_generated(
display_name: str,
deps: List[Dependency],
@@ -292,7 +316,7 @@ def generate_crates(
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]
+ "kernel", [core, macros, build_error, pin_init, ffi, bindings, uapi, zerocopy, zerocopy_derive]
)
scripts = srctree / "scripts"
@@ -337,7 +361,7 @@ def generate_crates(
append_crate(
crate_name,
path,
- [core, kernel, pin_init],
+ [core, kernel, pin_init, zerocopy, zerocopy_derive],
cfg=generated_cfg,
)
@@ -347,6 +371,7 @@ def main() -> None:
parser = argparse.ArgumentParser()
parser.add_argument('--verbose', '-v', action='store_true')
parser.add_argument('--cfgs', action='append', default=[])
+ parser.add_argument('--envs', action='append', default=[])
parser.add_argument("core_edition")
parser.add_argument("srctree", type=pathlib.Path)
parser.add_argument("objtree", type=pathlib.Path)
@@ -357,6 +382,7 @@ def main() -> None:
class Args(argparse.Namespace):
verbose: bool
cfgs: List[str]
+ envs: List[str]
srctree: pathlib.Path
objtree: pathlib.Path
sysroot: pathlib.Path
@@ -372,7 +398,7 @@ def main() -> None:
)
rust_project = {
- "crates": generate_crates(args.srctree, args.objtree, args.sysroot_src, args.exttree, args.cfgs, args.core_edition),
+ "crates": generate_crates(args.srctree, args.objtree, args.sysroot_src, args.exttree, args.cfgs, args.envs, args.core_edition),
"sysroot": str(args.sysroot),
}
diff --git a/scripts/generate_rust_target.rs b/scripts/generate_rust_target.rs
index 16f7e855e012..3bf296581a88 100644
--- a/scripts/generate_rust_target.rs
+++ b/scripts/generate_rust_target.rs
@@ -260,6 +260,8 @@ fn main() {
}
} else if cfg.has("LOONGARCH") {
panic!("loongarch uses the builtin rustc loongarch64-unknown-none-softfloat target");
+ } else if cfg.has("S390") {
+ panic!("s390 uses the builtin rustc s390x-unknown-none-softfloat target");
} else {
panic!("Unsupported architecture");
}
diff --git a/scripts/jobserver-exec b/scripts/jobserver-exec
index 758e947a6fb9..21b319e6c9a5 100755
--- a/scripts/jobserver-exec
+++ b/scripts/jobserver-exec
@@ -28,8 +28,8 @@ def main():
sys.exit("usage: " + name +" command [args ...]\n" + __doc__)
with JobserverExec() as jobserver:
- jobserver.run(sys.argv[1:])
+ return jobserver.run(sys.argv[1:])
if __name__ == "__main__":
- main()
+ sys.exit(main())
diff --git a/scripts/kconfig/conf.c b/scripts/kconfig/conf.c
index a7b44cd8ae14..c368bec5ab60 100644
--- a/scripts/kconfig/conf.c
+++ b/scripts/kconfig/conf.c
@@ -297,9 +297,7 @@ static int conf_askvalue(struct symbol *sym, const char *def)
line[1] = 0;
if (!sym_is_changeable(sym)) {
- printf("%s\n", def);
- line[0] = '\n';
- line[1] = 0;
+ printf("%s\n", def ?: "");
return 0;
}
@@ -307,7 +305,7 @@ static int conf_askvalue(struct symbol *sym, const char *def)
case oldconfig:
case syncconfig:
if (sym_has_value(sym)) {
- printf("%s\n", def);
+ printf("%s\n", def ?: "");
return 0;
}
/* fall through */
diff --git a/scripts/kconfig/kconfig-sym-check.pl b/scripts/kconfig/kconfig-sym-check.pl
new file mode 100755
index 000000000000..daa5285fdefc
--- /dev/null
+++ b/scripts/kconfig/kconfig-sym-check.pl
@@ -0,0 +1,132 @@
+#!/usr/bin/env perl
+# SPDX-License-Identifier: GPL-2.0
+
+use warnings;
+use strict;
+
+my $srctree = shift @ARGV;
+unless (defined $srctree) {
+ $srctree = `git rev-parse --show-toplevel 2>/dev/null`;
+ chomp $srctree;
+ my $msg = "Usage: $0 <srctree> [excludes file]\n";
+ $msg .= "Please provide <srctree>.";
+ $msg .= " Is it '$srctree'?" if $srctree;
+ $msg .= "\n";
+ die $msg;
+}
+my $kconfig_sym_check_excludes = defined $ARGV[0] ? $ARGV[0] : undef;
+
+sub indent_depth {
+ my ($ws) = @_;
+ my $col = 0;
+ for my $c (split //, $ws) {
+ $col = $c eq "\t" ? int($col / 8) * 8 + 8 : $col + 1;
+ }
+ return $col;
+}
+
+my @files = `git -C \Q$srctree\E ls-files '*Kconfig*' 2>/dev/null`;
+if (@files) {
+ chomp @files;
+ @files = map { "$srctree/$_" } @files;
+} else {
+ @files = `find \Q$srctree\E -name '*Kconfig*'`;
+ chomp @files;
+}
+
+@files = grep { !m{/scripts/kconfig/tests/} } @files;
+
+my %configs = ();
+my %refs = ();
+
+foreach my $file (@files) {
+ open F, $file or die "Cannot open $file: $!";
+
+ my $help = 0;
+ my $help_level;
+ my $level;
+
+ while (<F>) {
+ chomp;
+
+ while (/\\\s*$/) {
+ s/\\\s*$/ /;
+ my $cont = <F> // last;
+ chomp $cont;
+ $_ .= $cont;
+ }
+
+ next if /^\s*$/;
+ next if /^\s*#/;
+
+ /^(\s*)/;
+ $level = indent_depth($1);
+
+ if ($help && $level < $help_level) {
+ $help = 0;
+ }
+
+ next if ($help);
+
+ if (/^\s*(help|\-\-\-help\-\-\-)$/) {
+ $help = 1;
+ my $next;
+ while (defined($next = <F>)) {
+ last unless $next =~ /^\s*(?:#.*)?$/;
+ }
+ last unless defined $next;
+ $next =~ /^(\s*)/;
+ if (indent_depth($1) >= $level) {
+ $help_level = indent_depth($1);
+ } else {
+ $help = 0;
+ }
+ $_ = $next;
+ redo;
+ }
+
+ if (/^\s*(config|menuconfig)\s+([a-zA-Z0-9_]+)\s*(#.*)?$/) {
+ $configs{$2}++;
+ next;
+ }
+
+ if (/^\s*(default|def_bool|def_tristate|select|depends\s+on|imply|visible\s+if|range|if|bool|tristate|int|hex|string|prompt)\s+(.+)\s*$/) {
+ my $s = $2;
+ $s =~ s/"(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*'//g;
+ $s =~ s/#.*//;
+ $s =~ s/\$\((?:[^()]*|\((?:[^()]*|\([^()]*\))*\))*\)//g;
+ $s =~ s/%%[^%]*%%//g;
+ my @syms = split /[^a-zA-Z0-9_]+/, $s;
+ map {
+ $refs{$_}++ if (/[a-zA-Z]/ && $_ ne "if" && $_ ne "y" && $_ ne "n" && $_ ne "m" && !/^0[xX][0-9a-fA-F]+$/);
+ } @syms
+ }
+ }
+
+ close F;
+}
+
+my %known_syms = ();
+if (defined $kconfig_sym_check_excludes) {
+ my $file = $kconfig_sym_check_excludes;
+ open(F, "<", $file) or die "Cannot open $file: $!";
+ while (<F>) {
+ chomp;
+ next if /^\s*$/;
+ next if /^\s*#/;
+ $known_syms{$1}++ if (/^\s*([a-zA-Z0-9_]+)\s*(#.*)?$/);
+ }
+}
+
+my $ret = 0;
+foreach my $k (sort keys %refs) {
+ next if (exists $configs{$k} || exists $known_syms{$k});
+
+ print "$k";
+ print " - warning: '$k' is probably not what you want; Kconfig tristate literals are always lowercase ('n', 'y', 'm')" if ($k eq "N" || $k eq "Y" || $k eq "M");
+ print "\n";
+
+ $ret = 1;
+}
+
+exit $ret;
diff --git a/scripts/kconfig/tests/no_write_if_dep_unmet/__init__.py b/scripts/kconfig/tests/no_write_if_dep_unmet/__init__.py
index ffd469d1f226..791ed659c76b 100644
--- a/scripts/kconfig/tests/no_write_if_dep_unmet/__init__.py
+++ b/scripts/kconfig/tests/no_write_if_dep_unmet/__init__.py
@@ -8,7 +8,7 @@ for symbols with unmet dependency.
This was not working correctly for choice values because choice needs
a bit different symbol computation.
-This checks that no unneeded "# COFIG_... is not set" is contained in
+This checks that no unneeded "# CONFIG_... is not set" is contained in
the .config file.
Related Linux commit: cb67ab2cd2b8abd9650292c986c79901e3073a59
diff --git a/scripts/livepatch/klp-build b/scripts/livepatch/klp-build
index 0ad7e6631314..c4a7acf8edc3 100755
--- a/scripts/livepatch/klp-build
+++ b/scripts/livepatch/klp-build
@@ -3,7 +3,7 @@
#
# Build a livepatch module
-# shellcheck disable=SC1090,SC2155
+# shellcheck disable=SC1090,SC2155,SC2164
if (( BASH_VERSINFO[0] < 4 || \
(BASH_VERSINFO[0] == 4 && BASH_VERSINFO[1] < 4) )); then
@@ -11,21 +11,19 @@ if (( BASH_VERSINFO[0] < 4 || \
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.
+# This helps keep execution in pipes so pipefail+ERR trap can catch errors.
shopt -s lastpipe
-unset DEBUG_CLONE DIFF_CHECKSUM SKIP_CLEANUP XTRACE
+unset DEBUG_CLONE DIFF_CHECKSUM SKIP_CLEANUP VERBOSE 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.
@@ -35,16 +33,16 @@ SCRIPT="$(basename "$0")"
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
FIX_PATCH_LINES="$SCRIPT_DIR/fix-patch-lines"
-SRC="$(pwd)"
-OBJ="$(pwd)"
+OBJTOOL="$PWD/tools/objtool/objtool"
+CONFIG="$PWD/.config"
+TMP_DIR="$PWD/klp-tmp"
-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"
+ORIG_DIR="$TMP_DIR/1-orig"
+PATCHED_DIR="$TMP_DIR/2-patched"
+ORIG_CSUM_DIR="$TMP_DIR/3-checksum-orig"
+PATCHED_CSUM_DIR="$TMP_DIR/3-checksum-patched"
+DIFF_DIR="$TMP_DIR/4-diff"
+KMOD_DIR="$TMP_DIR/5-kmod"
STASH_DIR="$TMP_DIR/stash"
TIMESTAMP="$TMP_DIR/timestamp"
@@ -90,7 +88,7 @@ declare -a STASHED_FILES
stash_file() {
local file="$1"
- local rel_file="${file#"$SRC"/}"
+ local rel_file="${file#"$PWD"/}"
[[ ! -e "$file" ]] && die "no file to stash: $file"
@@ -104,7 +102,7 @@ restore_files() {
local file
for file in "${STASHED_FILES[@]}"; do
- mv -f "$STASH_DIR/$file" "$SRC/$file" || warn "can't restore file: $file"
+ mv -f "$STASH_DIR/$file" "$PWD/$file" || warn "can't restore file: $file"
done
STASHED_FILES=()
@@ -140,10 +138,11 @@ Options:
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
+ 1|orig Build original kernel (default)
+ 2|patched Build patched kernel
+ 3|checksum Generate checksums
+ 4|diff Diff objects
+ 5|kmod Build patch module
-T, --keep-tmp Preserve tmp dir on exit
EOF
@@ -158,6 +157,7 @@ process_args() {
local short
local long
local args
+ local patch
short="hfj:o:vdS:T"
long="help,show-first-changed,jobs:,output:,no-replace,verbose,debug,short-circuit:,keep-tmp"
@@ -194,7 +194,7 @@ process_args() {
shift
;;
-v | --verbose)
- VERBOSE="V=1"
+ VERBOSE=1
shift
;;
-d | --debug)
@@ -206,10 +206,11 @@ process_args() {
[[ ! -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; ;;
+ 1 | orig) SHORT_CIRCUIT=1; ;;
+ 2 | patched) SHORT_CIRCUIT=2; ;;
+ 3 | checksum) SHORT_CIRCUIT=3; ;;
+ 4 | diff) SHORT_CIRCUIT=4; ;;
+ 5 | kmod) SHORT_CIRCUIT=5; ;;
*) die "invalid short-circuit step '$2'" ;;
esac
shift 2
@@ -236,6 +237,10 @@ process_args() {
KEEP_TMP="$keep_tmp"
PATCHES=("$@")
+
+ for patch in "${PATCHES[@]}"; do
+ [[ -f "$patch" ]] || die "$patch doesn't exist"
+ done
}
# temporarily disable xtrace for especially verbose code
@@ -270,6 +275,9 @@ validate_config() {
[[ "$CONFIG_AS_VERSION" -lt 200000 ]] && \
die "Clang assembler version < 20 not supported"
+ [[ -x "$OBJTOOL" ]] && "$OBJTOOL" klp 2>&1 | command grep -q "not implemented" && \
+ die "objtool not built with KLP support; install xxhash-devel/libxxhash-dev (version >= 0.8) and recompile"
+
return 0
}
@@ -301,12 +309,17 @@ set_module_name() {
# 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 file="$PWD/scripts/setlocalversion"
local kernelrelease
stash_file "$file"
- kernelrelease="$(cd "$SRC" && make syncconfig &>/dev/null && make -s kernelrelease)"
+ if [[ -n "$(make -s listnewconfig 2>/dev/null)" ]]; then
+ die ".config mismatch, check your .config or run 'make olddefconfig'"
+ fi
+ make syncconfig &>/dev/null || die "make syncconfig failed"
+
+ kernelrelease="$(make -s kernelrelease)"
[[ -z "$kernelrelease" ]] && die "failed to get kernel version"
sed -i "2i echo $kernelrelease; exit 0" scripts/setlocalversion
@@ -349,7 +362,7 @@ check_unsupported_patches() {
for file in "${files[@]}"; do
case "$file" in
- lib/*|*.S)
+ lib/*|*/vdso/*|*/realmode/rm/*|*.S)
die "${patch}: unsupported patch to $file"
;;
esac
@@ -367,24 +380,24 @@ apply_patch() {
[[ ! -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=$?
+ output=$(patch -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
+ [[ -v VERBOSE ]] && 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")
+ patch -p1 --no-backup-if-mismatch -r /dev/null "${extra_args[@]}" --silent < "$patch"
}
revert_patch() {
local patch="$1"
local tmp=()
- patch -d "$SRC" -p1 -R --silent --no-backup-if-mismatch -r /dev/null < "$patch"
+ patch -p1 -R --force --no-backup-if-mismatch -r /dev/null &> /dev/null < "$patch" || true
for p in "${APPLIED_PATCHES[@]}"; do
[[ "$p" == "$patch" ]] && continue
@@ -422,8 +435,21 @@ validate_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"
+ [[ ! "$PWD" -ef "$SCRIPT_DIR/../.." ]] && die "please run from the kernel root directory"
+
+ if (( SHORT_CIRCUIT >= 2 )); then
+ [[ -f "$ORIG_DIR/.complete" ]] || die "-S $SHORT_CIRCUIT requires completed $ORIG_DIR"
+ fi
+ if (( SHORT_CIRCUIT >= 3 )); then
+ [[ -f "$PATCHED_DIR/.complete" ]] || die "-S $SHORT_CIRCUIT requires completed $PATCHED_DIR"
+ fi
+ if (( SHORT_CIRCUIT >= 4 )); then
+ [[ -f "$ORIG_CSUM_DIR/.complete" ]] || die "-S $SHORT_CIRCUIT requires completed $ORIG_CSUM_DIR"
+ [[ -f "$PATCHED_CSUM_DIR/.complete" ]] || die "-S $SHORT_CIRCUIT requires completed $PATCHED_CSUM_DIR"
+ fi
+ if (( SHORT_CIRCUIT >= 5 )); then
+ [[ -f "$DIFF_DIR/.complete" ]] || die "-S $SHORT_CIRCUIT requires completed $DIFF_DIR"
+ fi
(( SHORT_CIRCUIT <= 1 )) && rm -rf "$TMP_DIR"
mkdir -p "$TMP_DIR"
@@ -454,11 +480,11 @@ refresh_patch() {
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" )
+ 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" )
+ echo "${output_files[@]}" | xargs cp --parents --target-directory="$tmpdir/b"
revert_patch "$patch"
# Diff 'a' and 'b' to make a clean patch
@@ -502,20 +528,14 @@ clean_kernel() {
cmd+=("-j$JOBS")
cmd+=("clean")
- (
- cd "$SRC"
- "${cmd[@]}"
- )
+ "${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
@@ -535,19 +555,20 @@ build_kernel() {
#
cmd+=("KBUILD_MODPOST_WARN=1")
- cmd+=("$VERBOSE")
+ if [[ -v VERBOSE ]]; then
+ cmd+=("V=1")
+ else
+ cmd+=("-s")
+ fi
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"
+ "${cmd[@]}" \
+ 1> >(tee -a "$log") \
+ 2> >(tee -a "$log" | grep0 -v "modpost.*undefined!" >&2) \
+ || die "$build kernel build failed"
}
find_objects() {
@@ -555,9 +576,9 @@ find_objects() {
# 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 \
+ find "$PWD" \( -path "$TMP_DIR" -o -path "$PWD/.git" -o -regex "$PWD/[^/][^/]*\.ko" \) -prune -o \
-type f "${opts[@]}" \
- \( -name "*.ko" -o -path "$OBJ/vmlinux.o" \) \
+ \( -name "*.ko" -o -path "$PWD/vmlinux.o" \) \
-printf '%P\n'
}
@@ -570,10 +591,10 @@ copy_orig_objects() {
find_objects | mapfile -t files
- xtrace_save "copying orig objects"
+ xtrace_save "copying original objects"
for _file in "${files[@]}"; do
local rel_file="${_file/.ko/.o}"
- local file="$OBJ/$rel_file"
+ local file="$PWD/$rel_file"
local orig_file="$ORIG_DIR/$rel_file"
local orig_dir="$(dirname "$orig_file")"
@@ -586,6 +607,7 @@ copy_orig_objects() {
mv -f "$TMP_DIR/build.log" "$ORIG_DIR"
touch "$TIMESTAMP"
+ touch "$ORIG_DIR/.complete"
}
# Copy all changed objects to $PATCHED_DIR
@@ -606,7 +628,7 @@ copy_patched_objects() {
xtrace_save "copying changed objects"
for _file in "${files[@]}"; do
local rel_file="${_file/.ko/.o}"
- local file="$OBJ/$rel_file"
+ local file="$PWD/$rel_file"
local orig_file="$ORIG_DIR/$rel_file"
local patched_file="$PATCHED_DIR/$rel_file"
local patched_dir="$(dirname "$patched_file")"
@@ -624,6 +646,36 @@ copy_patched_objects() {
(( found == 0 )) && die "no changes detected"
mv -f "$TMP_DIR/build.log" "$PATCHED_DIR"
+ touch "$PATCHED_DIR/.complete"
+}
+
+# Copy .o files to a separate directory and run "objtool klp checksum" on each
+# copy. The checksums are written to a .discard.sym_checksum section.
+#
+# If match_dir is given, only process files which also exist there.
+generate_checksums() {
+ local src_dir="$1"
+ local dest_dir="$2"
+ local match_dir="${3:-}"
+ local files=()
+ local file
+
+ rm -rf "$dest_dir"
+ mkdir -p "$dest_dir"
+
+ find "$src_dir" -type f -name "*.o" | mapfile -t files
+ for file in "${files[@]}"; do
+ local rel="${file#"$src_dir"/}"
+ local dest="$dest_dir/$rel"
+
+ [[ -n "$match_dir" && ! -f "$match_dir/$rel" ]] && continue
+
+ mkdir -p "$(dirname "$dest")"
+ cp -f "$file" "$dest"
+ "$OBJTOOL" klp checksum "$dest"
+ done
+
+ touch "$dest_dir/.complete"
}
# Diff changed objects, writing output object to $DIFF_DIR
@@ -635,23 +687,23 @@ diff_objects() {
rm -rf "$DIFF_DIR"
mkdir -p "$DIFF_DIR"
- find "$PATCHED_DIR" -type f -name "*.o" | mapfile -t files
+ find "$PATCHED_CSUM_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 rel_file="${file#"$PATCHED_CSUM_DIR"/}"
local orig_file="$rel_file"
- local patched_file="$PATCHED_DIR/$rel_file"
+ local patched_file="$PATCHED_CSUM_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=("$OBJTOOL")
cmd+=("klp")
cmd+=("diff")
(( ${#opts[@]} > 0 )) && cmd+=("${opts[@]}")
@@ -668,18 +720,21 @@ diff_objects() {
fi
(
- cd "$ORIG_DIR"
+ cd "$ORIG_CSUM_DIR"
+ [[ -v VERBOSE ]] && echo "cd $ORIG_CSUM_DIR && ${cmd[*]}"
"${cmd[@]}" \
1> >(tee -a "$log") \
2> >(tee -a "$log" | "${filter[@]}" >&2) || \
die "objtool klp diff failed"
)
done
+
+ touch "$DIFF_DIR/.complete"
}
-# 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.
+# For each changed object, run "objtool klp checksum" 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"
@@ -703,9 +758,8 @@ diff_checksums() {
fi
done
- cmd=("$SRC/tools/objtool/objtool")
- cmd+=("--checksum")
- cmd+=("--link")
+ cmd=("$OBJTOOL")
+ cmd+=("klp" "checksum")
cmd+=("--dry-run")
for file in "${!funcs[@]}"; do
@@ -714,21 +768,37 @@ diff_checksums() {
(
cd "$ORIG_DIR"
"${cmd[@]}" "$opt" "$file" &> "$orig_log" || \
- ( cat "$orig_log" >&2; die "objtool --debug-checksum failed" )
+ ( cat "$orig_log" >&2; die "objtool klp checksum failed" )
cd "$PATCHED_DIR"
"${cmd[@]}" "$opt" "$file" &> "$patched_log" || \
- ( cat "$patched_log" >&2; die "objtool --debug-checksum failed" )
+ ( cat "$patched_log" >&2; die "objtool klp 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
+ local -a orig patched
+ paste <(grep0 -E "^DEBUG: .*checksum: $func " "$orig_log") \
+ <(grep0 -E "^DEBUG: .*checksum: $func " "$patched_log") |
+ while IFS= read -r line; do
+ read -ra orig <<< "${line%%$'\t'*}"
+ read -ra patched <<< "${line#*$'\t'}"
+
+ if [[ ${#patched[@]} -eq 0 ]]; then
+ printf "%s: %s: %s (removed)\n" "${orig[1]%:}" "${orig[3]}" "${orig[-2]}"
+ break
+ elif [[ ${#orig[@]} -eq 0 ]]; then
+ printf "%s: %s: %s (added)\n" "${patched[1]%:}" "${patched[3]}" "${patched[-2]}"
+ break
+ fi
+
+ [[ "${orig[-1]}" == "${patched[-1]}" ]] && continue
+
+ printf "%s: %s: %s" "${orig[1]%:}" "${orig[3]}" "${orig[-2]}"
+ [[ "${orig[-2]}" != "${patched[-2]}" ]] && \
+ printf " (patched: %s)" "${patched[-2]}"
+ printf "\n"
+ break
+ done || true
done
done
}
@@ -745,7 +815,7 @@ build_patch_module() {
rm -rf "$KMOD_DIR"
mkdir -p "$KMOD_DIR"
- cp -f "$SRC/scripts/livepatch/init.c" "$KMOD_DIR"
+ cp -f "$SCRIPT_DIR/init.c" "$KMOD_DIR"
echo "obj-m := $NAME.o" > "$makefile"
echo -n "$NAME-y := init.o" >> "$makefile"
@@ -780,19 +850,20 @@ build_patch_module() {
[[ $REPLACE -eq 0 ]] && cflags+=("-DKLP_NO_REPLACE")
cmd=("make")
- cmd+=("$VERBOSE")
+ if [[ -v VERBOSE ]]; then
+ cmd+=("V=1")
+ else
+ cmd+=("-s")
+ fi
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)
- )
+ "${cmd[@]}" \
+ 1> >(tee -a "$log") \
+ 2> >(tee -a "$log" >&2)
kmod_file="$KMOD_DIR/$NAME.ko"
@@ -803,7 +874,7 @@ build_patch_module() {
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"
+ "$OBJTOOL" klp post-link "$kmod_file" || die "objtool klp post-link failed"
cp -f "$kmod_file" "$OUTFILE"
}
@@ -839,6 +910,13 @@ if (( SHORT_CIRCUIT <= 2 )); then
fi
if (( SHORT_CIRCUIT <= 3 )); then
+ status "Generating original checksums"
+ generate_checksums "$ORIG_DIR" "$ORIG_CSUM_DIR" "$PATCHED_DIR"
+ status "Generating patched checksums"
+ generate_checksums "$PATCHED_DIR" "$PATCHED_CSUM_DIR"
+fi
+
+if (( SHORT_CIRCUIT <= 4 )); then
status "Diffing objects"
diff_objects
if [[ -v DIFF_CHECKSUM ]]; then
@@ -847,7 +925,7 @@ if (( SHORT_CIRCUIT <= 3 )); then
fi
fi
-if (( SHORT_CIRCUIT <= 4 )); then
+if (( SHORT_CIRCUIT <= 5 )); then
status "Building patch module: $OUTFILE"
build_patch_module
fi
diff --git a/scripts/min-tool-version.sh b/scripts/min-tool-version.sh
index b96ec2d379b6..031f2192b390 100755
--- a/scripts/min-tool-version.sh
+++ b/scripts/min-tool-version.sh
@@ -27,11 +27,15 @@ llvm)
if [ "$SRCARCH" = loongarch ]; then
echo 18.0.0
else
- echo 15.0.0
+ echo 17.0.1
fi
;;
rustc)
- echo 1.85.0
+ if [ "$SRCARCH" = "s390" ]; then
+ echo 1.96.0
+ else
+ echo 1.85.0
+ fi
;;
bindgen)
echo 0.71.1
diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c
index abbcd3fc1394..d592548cbd60 100644
--- a/scripts/mod/modpost.c
+++ b/scripts/mod/modpost.c
@@ -765,6 +765,8 @@ static const char *const section_white_list[] =
".gnu.lto*",
".discard.*",
".llvm.call-graph-profile", /* call graph */
+ "__llvm_covfun",
+ "__llvm_covmap",
NULL
};
@@ -1487,13 +1489,22 @@ static void extract_crcs_for_object(const char *object, struct module *mod)
char cmd_file[PATH_MAX];
char *buf, *p;
const char *base;
- int dirlen, ret;
+ int dirlen, baselen_without_suffix, ret;
base = get_basename(object);
dirlen = base - object;
- ret = snprintf(cmd_file, sizeof(cmd_file), "%.*s.%s.cmd",
- dirlen, object, base);
+ baselen_without_suffix = strlen(object) - dirlen - strlen(".o");
+
+ /*
+ * When CONFIG_LTO_CLANG_THIN_DIST=y, the ELF is *.thinlto-native.o
+ * but the symbol CRCs are recorded in *.o.cmd file.
+ */
+ if (strends(object, ".thinlto-native.o"))
+ baselen_without_suffix -= strlen(".thinlto-native");
+
+ ret = snprintf(cmd_file, sizeof(cmd_file), "%.*s.%.*s.o.cmd",
+ dirlen, object, baselen_without_suffix, base);
if (ret >= sizeof(cmd_file)) {
error("%s: too long path was truncated\n", cmd_file);
return;
@@ -1689,8 +1700,17 @@ void __attribute__((format(printf, 2, 3))) buf_printf(struct buffer *buf,
va_start(ap, fmt);
len = vsnprintf(tmp, SZ, fmt, ap);
- buf_write(buf, tmp, len);
va_end(ap);
+
+ if (len < 0) {
+ perror("vsnprintf failed");
+ exit(1);
+ }
+ if (len >= SZ)
+ fatal("buf_printf output truncated for string %s: %d bytes needed, %d available\n",
+ tmp, len + 1, SZ);
+
+ buf_write(buf, tmp, len);
}
void buf_write(struct buffer *buf, const char *s, int len)
diff --git a/scripts/package/PKGBUILD b/scripts/package/PKGBUILD
index 1213c8e04671..66e4b6a37783 100644
--- a/scripts/package/PKGBUILD
+++ b/scripts/package/PKGBUILD
@@ -121,6 +121,9 @@ _package-debug(){
install -Dt "${debugdir}" -m644 vmlinux
mkdir -p "${builddir}"
ln -sr "${debugdir}/vmlinux" "${builddir}/vmlinux"
+
+ echo "Installing unstripped vDSO(s)..."
+ ${MAKE} INSTALL_MOD_PATH="${pkgdir}/usr" vdso_install
}
for _p in "${pkgname[@]}"; do
diff --git a/scripts/package/kernel.spec b/scripts/package/kernel.spec
index b3c956205af0..c732415662ef 100644
--- a/scripts/package/kernel.spec
+++ b/scripts/package/kernel.spec
@@ -6,7 +6,7 @@
Name: kernel
Summary: The Linux Kernel
Version: %(echo %{KERNELRELEASE} | sed -e 's/-/_/g')
-Release: %{pkg_release}
+Release: %{pkg_release}%{?dist}
License: GPL
Group: System Environment/Kernel
Vendor: The Linux Community
diff --git a/scripts/timer_migration_tree.py b/scripts/timer_migration_tree.py
new file mode 100755
index 000000000000..faac9de854bd
--- /dev/null
+++ b/scripts/timer_migration_tree.py
@@ -0,0 +1,122 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: GPL-2.0
+
+"""
+Draw the timer migration tree.
+
+1) Boot with trace_event==tmigr_connect_cpu_parent,tmigr_connect_child_parent
+2) ./timer_migration_tree.py < /sys/kernel/tracing/trace
+"""
+
+import re, sys
+from ete3 import Tree
+
+class Node:
+ def __init__(self, group):
+ self.group = group
+ self.children = []
+ self.parent = None
+ self.num_children = 0
+ self.groupmask = 0
+ self.lvl = -1
+
+ def set_groupmask(self, groupmask):
+ self.groupmask = groupmask
+
+ def set_parent(self, parent):
+ self.parent = parent
+
+ def add_child(self, child):
+ self.children.append(child)
+
+ def set_lvl(self, lvl):
+ self.lvl = lvl
+
+ def set_numa(self, numa):
+ self.numa = numa
+
+ def set_num_children(self, num_children):
+ self.num_children = num_children
+
+ def __repr__(self):
+ if self.parent:
+ parent_grp = self.parent.group
+ else:
+ parent_grp = "-"
+ return "Group: %s mask: %s parent: %s lvl: %d numa: %d num_children: %d" % (self.group, self.groupmask, parent_grp, self.lvl, self.numa, self.num_children)
+
+hierarchies = { }
+
+def get_hierarchy(capacity):
+ if capacity not in hierarchies:
+ hierarchies[capacity] = {}
+ return hierarchies[capacity]
+
+def get_node(capacity, group):
+ hier = get_hierarchy(capacity)
+ if group in hier:
+ return hier[group]
+ else:
+ n = Node(group)
+ hier[group] = n
+ return n
+
+def tmigr_connect_cpu_parent(ts, line):
+ s = re.search("tmigr_connect_cpu_parent: cpu=([0-9]+) groupmask=([0-9a-zA-Z]+) parent=([0-9a-zA-Z]+) lvl=([0-9]+) numa=([-]?[0-9]+) capacity=([-]?[0-9]+) num_children=([0-9]+)", line)
+ if s is None:
+ return False
+ (cpu, groupmask, parent, lvl, numa, capacity, num_children) = (int(s.group(1)), s.group(2), s.group(3), int(s.group(4)), int(s.group(5)), int(s.group(6)), int(s.group(7)))
+ n = get_node(capacity, cpu)
+ p = get_node(capacity, parent)
+ n.set_parent(p)
+ n.set_groupmask(groupmask)
+ n.set_lvl(-1)
+ p.set_lvl(lvl)
+ p.set_numa(numa)
+ n.set_numa(numa)
+ p.set_num_children(num_children)
+ p.add_child(n)
+
+def tmigr_connect_child_parent(ts, line):
+ s = re.search("tmigr_connect_child_parent: group=([0-9a-zA-Z]+) groupmask=([0-9a-zA-Z]+) parent=([0-9a-zA-Z]+) lvl=([0-9]+) numa=([-]?[0-9]+) capacity=([-]?[0-9]+) num_children=([0-9]+)", line)
+ if s is None:
+ return False
+ (group, groupmask, parent, lvl, numa, capacity, num_children) = (s.group(1), s.group(2), s.group(3), int(s.group(4)), int(s.group(5)), int(s.group(6)), int(s.group(7)))
+ n = get_node(capacity, group)
+ p = get_node(capacity, parent)
+ n.set_parent(p)
+ n.set_groupmask(groupmask)
+ p.set_lvl(lvl)
+ p.set_numa(numa)
+ p.set_num_children(num_children)
+ p.add_child(n)
+
+def populate(enode, node):
+ enode = enode.add_child(name = node.group)
+ enode.add_feature("groupmask", "m:%s" % node.groupmask)
+ enode.add_feature("lvl", "lvl:%d" % node.lvl)
+ enode.add_feature("numa", "node %d" % node.numa)
+ enode.add_feature("num_children", "c=%d" % node.num_children)
+ for child in node.children:
+ populate(enode, child)
+
+if __name__ == "__main__":
+ for line in sys.stdin:
+ s = re.search("([0-9]+[.][0-9]{6}): (.+?)$", line, re.S)
+ if s is not None:
+ if tmigr_connect_cpu_parent(float(s.group(1)), s.group(2)):
+ continue
+ if tmigr_connect_child_parent(float(s.group(1)), s.group(2)):
+ continue
+
+ for cap in hierarchies:
+ h = hierarchies[cap]
+ print("Tree for capacity %d" % cap)
+ for k in h:
+ n = h[k]
+ while n.parent != None:
+ n = n.parent
+ root = Tree()
+ populate(root, n)
+ print(root.get_ascii(show_internal=True, attributes=["name", "numa", "lvl"]))
+ break
diff --git a/scripts/update-intel-ucode-defs.py b/scripts/update-intel-ucode-defs.py
new file mode 100755
index 000000000000..9d6cc2c6075f
--- /dev/null
+++ b/scripts/update-intel-ucode-defs.py
@@ -0,0 +1,130 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: GPL-2.0
+import argparse
+import re
+import shutil
+import subprocess
+import sys
+import os
+
+script = os.path.relpath(__file__)
+
+DESCRIPTION = f"""
+For Intel CPUs, update the microcode revisions that determine
+X86_BUG_OLD_MICROCODE.
+
+This script is intended to be run in response to releases of the
+official Intel microcode GitHub repository:
+https://github.com/intel/Intel-Linux-Processor-Microcode-Data-Files.git
+
+It takes the Intel microcode files as input and uses iucode-tool to
+extract the revision information. It prints the output in the format
+expected by intel-ucode-defs.h.
+
+Usage:
+ ./{script} /path/to/microcode/files > /path/to/intel-ucode-defs.h
+
+Typically, someone at Intel would see a new public release, wait for at
+least three months to ensure the update is stable, run this script to
+refresh the intel-ucode-defs.h file, and send a patch upstream to update
+the mainline and stable versions.
+
+Any exception to this process should be supported with an appropriate
+justification.
+"""
+
+SIG_RE = re.compile(r'sig (0x[0-9a-fA-F]+)')
+PFM_RE = re.compile(r'pf_mask (0x[0-9a-fA-F]+)')
+REV_RE = re.compile(r'rev (0x[0-9a-fA-F]+)')
+
+# Functions to extract family, model, and stepping
+def bits(val, bottom, top):
+ mask = (1 << (top + 1 - bottom)) - 1
+ return (val >> bottom) & mask
+
+def family(sig):
+ if bits(sig, 8, 11) == 0xf:
+ return bits(sig, 8, 11) + bits(sig, 20, 27)
+ return bits(sig, 8, 11)
+
+def model(sig):
+ return bits(sig, 4, 7) | (bits(sig, 16, 19) << 4)
+
+def step(sig):
+ return bits(sig, 0, 3)
+
+class Ucode:
+ def __init__(self, sig, pfm, rev):
+ self.family = family(sig)
+ self.model = model(sig)
+ self.steppings = 1 << step(sig)
+ self.platforms = pfm
+ self.rev = rev
+
+ self.key = (self.family, self.model, self.steppings, self.platforms)
+
+ def __eq__(self, other):
+ return self.key == other.key
+
+ def __hash__(self):
+ return hash(self.key)
+
+ def __str__(self):
+ return "{ .flags = X86_CPU_ID_FLAG_ENTRY_VALID, .vendor = X86_VENDOR_INTEL, .family = 0x%x, .model = 0x%02x, .steppings = 0x%04x, .platform_mask = 0x%02x, .driver_data = 0x%x }," % \
+ (self.family, self.model, self.steppings, self.platforms, self.rev)
+
+def main():
+ parser = argparse.ArgumentParser(description=DESCRIPTION,
+ formatter_class=argparse.RawDescriptionHelpFormatter)
+ parser.add_argument('ucode_files', nargs='+', help='Path(s) to the microcode files')
+
+ args = parser.parse_args()
+
+ # Process the microcode files using iucode-tool
+ iucode_tool = shutil.which("iucode-tool") or shutil.which("iucode_tool")
+ if iucode_tool is None:
+ print("Error: iucode-tool not found, please install it", file=sys.stderr)
+ sys.exit(1)
+
+ cmd = [iucode_tool, '--list-all'] + args.ucode_files
+
+ result = subprocess.run(cmd, capture_output=True, text=True)
+ if result.returncode != 0:
+ print("Error: iucode-tool ran into an error, exiting", file=sys.stderr)
+ if result.stderr:
+ print(result.stderr, file=sys.stderr, end='')
+ sys.exit(1)
+
+ ucodes = set()
+
+ # Parse the output of iucode-tool
+ for line in result.stdout.splitlines():
+ sig_match = SIG_RE.search(line)
+ pfm_match = PFM_RE.search(line)
+ rev_match = REV_RE.search(line)
+
+ if not (sig_match and pfm_match and rev_match):
+ continue
+
+ sig = int(sig_match.group(1), 16)
+ pfm = int(pfm_match.group(1), 16)
+ rev = int(rev_match.group(1), 16)
+ debug_rev = bits(rev, 31, 31)
+ if debug_rev != 0:
+ print("Error: Debug ucode file found, exiting", file=sys.stderr)
+ sys.exit(1)
+
+ ucodes.add(Ucode(sig, pfm, rev))
+
+ if not ucodes:
+ print("Error: No valid microcode files found, exiting", file=sys.stderr)
+ sys.exit(1)
+
+ # Sort and print the microcode entries
+ print("/* SPDX-License-Identifier: GPL-2.0 */")
+ print("/* Auto-generated by scripts/update-intel-ucode-defs.py */")
+ for u in sorted(ucodes, key=lambda x: x.key):
+ print(u)
+
+if __name__ == "__main__":
+ main()