@@ -32,3 +32,21 @@ Description:
Note: If the module is built into the kernel, or if the
CONFIG_MODULE_UNLOAD kernel configuration value is not enabled,
this file will not be present.
+
+What: /sys/module/MODULENAME/scmversion
+Date: November 2020
+KernelVersion: 5.11
+Contact: Will McVicker <willmcvicker@google.com>
+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 or LOCALVERSION=.
+ 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]\+
+ ===
@@ -372,6 +372,7 @@ struct module {
struct module_attribute *modinfo_attrs;
const char *version;
const char *srcversion;
+ const char *scmversion;
struct kobject *holders_dir;
/* Exported symbols */
@@ -2131,6 +2131,18 @@ 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.
+
config MODULE_SIG
bool "Module signature verification"
select MODULE_SIG_FORMAT
@@ -807,6 +807,7 @@ static struct module_attribute modinfo_##field = { \
MODINFO_ATTR(version);
MODINFO_ATTR(srcversion);
+MODINFO_ATTR(scmversion);
static char last_unloaded_module[MODULE_NAME_LEN+1];
@@ -1269,6 +1270,7 @@ static struct module_attribute *modinfo_attrs[] = {
&module_uevent,
&modinfo_version,
&modinfo_srcversion,
+ &modinfo_scmversion,
&modinfo_initstate,
&modinfo_coresize,
&modinfo_initsize,
@@ -66,6 +66,7 @@ ifeq ($(KBUILD_EXTMOD),)
input-symdump := vmlinux.symvers
output-symdump := Module.symvers
+module_srcpath := $(srctree)
else
@@ -77,6 +78,17 @@ src := $(obj)
include $(if $(wildcard $(KBUILD_EXTMOD)/Kbuild), \
$(KBUILD_EXTMOD)/Kbuild, $(KBUILD_EXTMOD)/Makefile)
+# 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
+
# modpost option for external modules
MODPOST += -e
@@ -85,6 +97,16 @@ output-symdump := $(KBUILD_EXTMOD)/Module.symvers
endif
+ifeq ($(CONFIG_MODULE_SCMVERSION),y)
+# 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.
+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 += -v$(module_scmversion)
+endif
+endif
+
# modpost options for modules (both in-kernel and external)
MODPOST += \
$(addprefix -i ,$(wildcard $(input-symdump))) \
@@ -30,6 +30,8 @@ static int have_vmlinux = 0;
static int all_versions = 0;
/* If we are modposting external module set to 1 */
static int external_module = 0;
+#define MODULE_SCMVERSION_SIZE 64
+static char module_scmversion[MODULE_SCMVERSION_SIZE];
/* Only warn about unresolved symbols */
static int warn_unresolved = 0;
/* How a symbol is exported */
@@ -2272,6 +2274,20 @@ static void add_intree_flag(struct buffer *b, int is_intree)
buf_printf(b, "\nMODULE_INFO(intree, \"Y\");\n");
}
+/**
+ * add_scmversion() - Adds the MODULE_INFO macro for the scmversion.
+ * @b: Buffer to append to.
+ *
+ * This function fills in the module attribute `scmversion` for the kernel
+ * module. This is useful for determining a given module's SCM version on
+ * device via /sys/modules/<module>/scmversion and/or using the modinfo tool.
+ */
+static void add_scmversion(struct buffer *b)
+{
+ if (module_scmversion[0] != '\0')
+ buf_printf(b, "\nMODULE_INFO(scmversion, \"%s\");\n", module_scmversion);
+}
+
/* Cannot check for assembler */
static void add_retpoline(struct buffer *b)
{
@@ -2559,7 +2575,7 @@ int main(int argc, char **argv)
struct dump_list *dump_read_start = NULL;
struct dump_list **dump_read_iter = &dump_read_start;
- 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 = 1;
@@ -2597,6 +2613,11 @@ int main(int argc, char **argv)
case 'd':
missing_namespace_deps = optarg;
break;
+ case 'v':
+ if (!optarg)
+ fatal("'-v' requires an argument defining the SCM version.");
+ strncpy(module_scmversion, optarg, sizeof(module_scmversion) - 1);
+ break;
default:
exit(1);
}
@@ -2645,6 +2666,7 @@ int main(int argc, char **argv)
add_depends(&buf, mod);
add_moddevtable(&buf, mod);
add_srcversion(&buf, mod);
+ add_scmversion(&buf);
sprintf(fname, "%s.mod.c", mod->name);
write_if_changed(&buf, fname);