diff --git a/Documentation/ABI/stable/sysfs-module b/Documentation/ABI/stable/sysfs-module index 41b1f16e8795..47b8fd0e196e 100644 --- a/Documentation/ABI/stable/sysfs-module +++ b/Documentation/ABI/stable/sysfs-module @@ -45,3 +45,21 @@ Date: Jun 2005 Description: If the module source has MODULE_VERSION, this file will contain the version of the source code. + +What: /sys/module/MODULENAME/scmversion +Date: November 2020 +KernelVersion: 5.12 +Contact: Will McVicker +Description: This read-only file will appear if modpost was supplied with an + SCM version for the module. It can be enabled with the config + MODULE_SCMVERSION. The SCM version is retrieved by + scripts/setlocalversion, which means that the presence of this + file depends on CONFIG_LOCALVERSION_AUTO=y. When read, the SCM + version that the module was compiled with is returned. The SCM + version is returned in the following format:: + + === + Git: g[a-f0-9]\+(-dirty)\? + Mercurial: hg[a-f0-9]\+(-dirty)\? + Subversion: svn[0-9]\+ + === diff --git a/include/linux/module.h b/include/linux/module.h index 33883dbe97c4..b991505c7f04 100644 --- a/include/linux/module.h +++ b/include/linux/module.h @@ -387,6 +387,7 @@ struct module { struct module_attribute *modinfo_attrs; const char *version; const char *srcversion; + const char *scmversion; struct kobject *holders_dir; /* Exported symbols */ diff --git a/kernel/module/Kconfig b/kernel/module/Kconfig index c8d4212db319..5058c2196011 100644 --- a/kernel/module/Kconfig +++ b/kernel/module/Kconfig @@ -88,6 +88,20 @@ config MODULE_SRCVERSION_ALL the version). With this option, such a "srcversion" field will be created for all modules. If unsure, say N. +config MODULE_SCMVERSION + bool "SCM version for modules" + depends on LOCALVERSION_AUTO + help + This enables the module attribute "scmversion" which can be used + by developers to identify the SCM version of a given module, e.g. + git sha1 or hg sha1. The SCM version can be queried by modinfo or + via the sysfs node: /sys/modules/MODULENAME/scmversion. This is + useful when the kernel or kernel modules are updated separately + since that causes the vermagic of the kernel and the module to + differ. + + If unsure, say N. + config MODULE_SIG bool "Module signature verification" select MODULE_SIG_FORMAT diff --git a/kernel/module/main.c b/kernel/module/main.c index e4cad7f89a68..17f0c0322f59 100644 --- a/kernel/module/main.c +++ b/kernel/module/main.c @@ -525,6 +525,7 @@ static struct module_attribute modinfo_##field = { \ MODINFO_ATTR(version); MODINFO_ATTR(srcversion); +MODINFO_ATTR(scmversion); static struct { char name[MODULE_NAME_LEN + 1]; @@ -972,6 +973,7 @@ struct module_attribute *modinfo_attrs[] = { &module_uevent, &modinfo_version, &modinfo_srcversion, + &modinfo_scmversion, &modinfo_initstate, &modinfo_coresize, #ifdef CONFIG_ARCH_WANTS_MODULES_DATA_IN_VMALLOC diff --git a/scripts/Makefile.modpost b/scripts/Makefile.modpost index 931a3272a4ba..01df0bad6fa3 100644 --- a/scripts/Makefile.modpost +++ b/scripts/Makefile.modpost @@ -65,6 +65,33 @@ modpost-args += -T $(MODORDER) modpost-deps += $(MODORDER) endif +ifeq ($(CONFIG_MODULE_SCMVERSION),y) +ifeq ($(KBUILD_EXTMOD),) +module_srcpath := $(srctree) +else +# Get the external module's source path. KBUILD_EXTMOD could either be an +# absolute path or relative path from $(srctree). This makes sure that we +# aren't using a relative path from a separate working directory (O= or +# KBUILD_OUTPUT) since that may not be the actual module's SCM project path. So +# check the path relative to $(srctree) first. +ifneq ($(realpath $(srctree)/$(KBUILD_EXTMOD) 2>/dev/null),) + module_srcpath := $(srctree)/$(KBUILD_EXTMOD) +else + module_srcpath := $(KBUILD_EXTMOD) +endif +endif + +# Get the SCM version of the module. Sed verifies setlocalversion returns +# a proper revision based on the SCM type, e.g. git, mercurial, or svn. +# Note: relative M= paths are not supported when building the kernel out of the +# srctree since setlocalversion won't be able to find the module srctree. +module_scmversion := $(shell $(srctree)/scripts/setlocalversion $(module_srcpath) | \ + sed -n 's/.*-\(\(g\|hg\)[a-fA-F0-9]\+\(-dirty\)\?\|svn[0-9]\+\).*/\1/p') +ifneq ($(module_scmversion),) +modpost-args += -v $(module_scmversion) +endif +endif + ifeq ($(KBUILD_EXTMOD),) # Generate the list of in-tree objects in vmlinux diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c index 9466b6a2abae..f043dbe7f751 100644 --- a/scripts/mod/modpost.c +++ b/scripts/mod/modpost.c @@ -29,6 +29,8 @@ static bool modversions; static bool all_versions; /* If we are modposting external module set to 1 */ static bool external_module; +#define MODULE_SCMVERSION_SIZE 64 +static char module_scmversion[MODULE_SCMVERSION_SIZE]; /* Only warn about unresolved symbols */ static bool warn_unresolved; @@ -2006,6 +2008,9 @@ static void add_header(struct buffer *b, struct module *mod) if (!external_module) buf_printf(b, "\nMODULE_INFO(intree, \"Y\");\n"); + if (module_scmversion[0] != '\0') + buf_printf(b, "\nMODULE_INFO(scmversion, \"%s\");\n", module_scmversion); + buf_printf(b, "\n" "#ifdef CONFIG_RETPOLINE\n" @@ -2334,7 +2339,7 @@ int main(int argc, char **argv) LIST_HEAD(dump_lists); struct dump_list *dl, *dl2; - while ((opt = getopt(argc, argv, "ei:mnT:o:awENd:")) != -1) { + while ((opt = getopt(argc, argv, "ei:mnT:o:awENd:v:")) != -1) { switch (opt) { case 'e': external_module = true; @@ -2371,6 +2376,9 @@ int main(int argc, char **argv) case 'd': missing_namespace_deps = optarg; break; + case 'v': + strncpy(module_scmversion, optarg, sizeof(module_scmversion) - 1); + break; default: exit(1); }