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 6aa2bc84d4af..62544b50e1c1 100644 --- a/include/linux/module.h +++ b/include/linux/module.h @@ -379,6 +379,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 4f0ccf66801f..6b5c1175faea 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 fbe7cb2f2979..0f18b6ee980c 100644 --- a/kernel/module/main.c +++ b/kernel/module/main.c @@ -527,6 +527,7 @@ static struct module_attribute modinfo_##field = { \ MODINFO_ATTR(version); MODINFO_ATTR(srcversion); +MODINFO_ATTR(scmversion); static struct { char name[MODULE_NAME_LEN + 1]; @@ -974,6 +975,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 9aaf1869fecf..cb21c173e15e 100644 --- a/scripts/Makefile.modpost +++ b/scripts/Makefile.modpost @@ -53,6 +53,33 @@ ifneq ($(findstring i,$(filter-out --%,$(MAKEFLAGS))),) modpost-args += -n 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 b31f848dbd3d..8772d5a69e87 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; @@ -1999,6 +2001,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" @@ -2327,7 +2332,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; @@ -2364,6 +2369,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); }