From: Andreas Gruenbacher <agruen@suse.de>
Subject: Novell/external support flag in modules
Patch-mainline: Never, SLES feature

Upon module load, check if a module is supported, and set the
N (TAINT_NO_SUPPORT) or X (TAINT_EXTERNAL_SUPPORT) tail flags
for unsupported or externally suported modules.

Changes:
* Feb 21 2008 - jeffm
- 2.6.25 claimed -S and bumped the flags up a bit, modpost now uses -N

Signed-off-by: Andreas Gruenbacher <agruen@suse.de>

---

 Documentation/kernel-parameters.txt |    5 ++
 Documentation/sysctl/kernel.txt     |   12 ++++++
 Makefile                            |    5 ++
 include/linux/kernel.h              |    9 ++++
 include/linux/module.h              |    1 
 kernel/ksysfs.c                     |   23 ++++++++++++
 kernel/module.c                     |   65 ++++++++++++++++++++++++++++++++++++
 kernel/panic.c                      |    9 ++++
 kernel/sysctl.c                     |    9 ++++
 scripts/Makefile.modpost            |    4 +-
 scripts/mod/modpost.c               |   65 +++++++++++++++++++++++++++++++++++-
 11 files changed, 205 insertions(+), 2 deletions(-)

--- a/Documentation/kernel-parameters.txt
+++ b/Documentation/kernel-parameters.txt
@@ -2504,6 +2504,11 @@ and is between 256 and 4096 characters.
 			[X86]
 			Set unknown_nmi_panic=1 early on boot.
 
+	unsupported	Allow loading of unsupported kernel modules:
+			0 = only allow supported modules,
+			1 = warn when loading unsupported modules,
+			2 = don't warn.
+
 	usbcore.autosuspend=
 			[USB] The autosuspend time delay (in seconds) used
 			for newly-detected USB devices (default 2).  This
--- a/Documentation/sysctl/kernel.txt
+++ b/Documentation/sysctl/kernel.txt
@@ -491,6 +491,18 @@ can be ORed together:
         instead of using the one provided by the hardware.
  512 - A kernel warning has occurred.
 1024 - A module from drivers/staging was loaded.
+ 0x40000000 - An unsupported kernel module was loaded.
+ 0x80000000 - An kernel module with external support was loaded.
+
+==============================================================
+
+unsupported:
+
+Allow to load unsupported kernel modules:
+
+  0 - refuse to load unsupported modules,
+  1 - warn when loading unsupported modules,
+  2 - don't warn.
 
 ==============================================================
 
--- a/Makefile
+++ b/Makefile
@@ -362,6 +362,11 @@ KBUILD_AFLAGS_MODULE  := -DMODULE
 KBUILD_CFLAGS_MODULE  := -DMODULE
 KBUILD_LDFLAGS_MODULE := -T $(srctree)/scripts/module-common.lds
 
+# Warn about unsupported modules in kernels built inside Autobuild
+ifneq ($(wildcard /.buildenv),)
+CFLAGS		+= -DUNSUPPORTED_MODULES=2
+endif
+
 # Read KERNELRELEASE from include/config/kernel.release (if it exists)
 KERNELRELEASE = $(shell cat include/config/kernel.release 2> /dev/null)
 KERNELVERSION = $(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)
--- a/include/linux/kernel.h
+++ b/include/linux/kernel.h
@@ -222,8 +222,10 @@ extern int panic_timeout;
 extern int panic_on_oops;
 extern int panic_on_unrecovered_nmi;
 extern int panic_on_io_nmi;
+extern int unsupported;
 extern const char *print_tainted(void);
 extern void add_taint(unsigned flag);
+extern void add_nonfatal_taint(unsigned flag);
 extern int test_taint(unsigned flag);
 extern unsigned long get_taint(void);
 extern int root_mountflags;
@@ -251,6 +253,13 @@ extern enum system_states {
 #define TAINT_CRAP			10
 #define TAINT_FIRMWARE_WORKAROUND	11
 
+/*
+ * Take the upper bits to hopefully allow them
+ * to stay the same for more than one release.
+ */
+#define TAINT_NO_SUPPORT		30
+#define TAINT_EXTERNAL_SUPPORT		31
+
 extern const char hex_asc[];
 #define hex_asc_lo(x)	hex_asc[((x) & 0x0f)]
 #define hex_asc_hi(x)	hex_asc[((x) & 0xf0) >> 4]
--- a/include/linux/module.h
+++ b/include/linux/module.h
@@ -410,6 +410,7 @@ struct module *__module_address(unsigned
 bool is_module_address(unsigned long addr);
 bool is_module_percpu_address(unsigned long addr);
 bool is_module_text_address(unsigned long addr);
+const char *supported_printable(int taint);
 
 static inline int within_module_core(unsigned long addr, struct module *mod)
 {
--- a/kernel/ksysfs.c
+++ b/kernel/ksysfs.c
@@ -157,6 +157,28 @@ static struct bin_attribute notes_attr =
 struct kobject *kernel_kobj;
 EXPORT_SYMBOL_GPL(kernel_kobj);
 
+const char *supported_printable(int taint)
+{
+	int mask = TAINT_PROPRIETARY_MODULE|TAINT_NO_SUPPORT;
+	if ((taint & mask) == mask)
+		return "No, Proprietary and Unsupported modules are loaded";
+	else if (taint & TAINT_PROPRIETARY_MODULE)
+		return "No, Proprietary modules are loaded";
+	else if (taint & TAINT_NO_SUPPORT)
+		return "No, Unsupported modules are loaded";
+	else if (taint & TAINT_EXTERNAL_SUPPORT)
+		return "Yes, External";
+	else
+		return "Yes";
+}
+
+static ssize_t supported_show(struct kobject *kobj,
+			      struct kobj_attribute *attr, char *buf)
+{
+	return sprintf(buf, "%s\n", supported_printable(get_taint()));
+}
+KERNEL_ATTR_RO(supported);
+
 static struct attribute * kernel_attrs[] = {
 #if defined(CONFIG_HOTPLUG)
 	&uevent_seqnum_attr.attr,
@@ -171,6 +193,7 @@ static struct attribute * kernel_attrs[]
 	&kexec_crash_size_attr.attr,
 	&vmcoreinfo_attr.attr,
 #endif
+	&supported_attr.attr,
 	NULL
 };
 
--- a/kernel/module.c
+++ b/kernel/module.c
@@ -73,6 +73,20 @@
 /* If this is set, the section belongs in the init part of the module */
 #define INIT_OFFSET_MASK (1UL << (BITS_PER_LONG-1))
 
+/* Allow unsupported modules switch. */
+#ifdef UNSUPPORTED_MODULES
+int unsupported = UNSUPPORTED_MODULES;
+#else
+int unsupported = 2;  /* don't warn when loading unsupported modules. */
+#endif
+
+static int __init unsupported_setup(char *str)
+{
+	get_option(&str, &unsupported);
+	return 1;
+}
+__setup("unsupported=", unsupported_setup);
+
 /*
  * Mutex protects:
  * 1) List of modules (also safely readable with preempt_disable),
@@ -931,10 +945,36 @@ static struct module_attribute initstate
 	.show = show_initstate,
 };
 
+static void setup_modinfo_supported(struct module *mod, const char *s)
+{
+	if (!s) {
+		mod->taints |= (1 << TAINT_NO_SUPPORT);
+		return;
+	}
+
+	if (strcmp(s, "external") == 0)
+		mod->taints |= (1 << TAINT_EXTERNAL_SUPPORT);
+	else if (strcmp(s, "yes"))
+		mod->taints |= (1 << TAINT_NO_SUPPORT);
+}
+
+static ssize_t show_modinfo_supported(struct module_attribute *mattr,
+	                struct module *mod, char *buffer)
+{
+	return sprintf(buffer, "%s\n", supported_printable(mod->taints));
+}
+
+static struct module_attribute modinfo_supported = {
+	.attr = { .name = "supported", .mode = 0444 },
+	.show = show_modinfo_supported,
+	.setup = setup_modinfo_supported,
+};
+
 static struct module_attribute *modinfo_attrs[] = {
 	&modinfo_version,
 	&modinfo_srcversion,
 	&initstate,
+	&modinfo_supported,
 #ifdef CONFIG_MODULE_UNLOAD
 	&refcnt,
 #endif
@@ -1476,6 +1516,26 @@ static int mod_sysfs_setup(struct module
 	add_sect_attrs(mod, info);
 	add_notes_attrs(mod, info);
 
+	/* We don't use add_taint() here because it also disables lockdep. */
+	if (mod->taints & (1 << TAINT_EXTERNAL_SUPPORT))
+		add_nonfatal_taint(TAINT_EXTERNAL_SUPPORT);
+	else if (mod->taints == (1 << TAINT_NO_SUPPORT)) {
+		if (unsupported == 0) {
+			printk(KERN_WARNING "%s: module not supported by "
+			       "Novell, refusing to load. To override, echo "
+			       "1 > /proc/sys/kernel/unsupported\n", mod->name);
+			err = -ENOEXEC;
+			goto free_hdr;
+		}
+		add_nonfatal_taint(TAINT_NO_SUPPORT);
+		if (unsupported == 1) {
+			printk(KERN_WARNING "%s: module is not supported by "
+			       "Novell. Novell Technical Services may decline "
+			       "your support request if it involves a kernel "
+			       "fault.\n", mod->name);
+		}
+	}
+
 	kobject_uevent(&mod->mkobj.kobj, KOBJ_ADD);
 	return 0;
 
@@ -3005,6 +3065,10 @@ static char *module_flags(struct module
 			buf[bx++] = 'F';
 		if (mod->taints & (1 << TAINT_CRAP))
 			buf[bx++] = 'C';
+		if (mod->taints & (1 << TAINT_NO_SUPPORT))
+			buf[bx++] = 'N';
+		if (mod->taints & (1 << TAINT_EXTERNAL_SUPPORT))
+			buf[bx++] = 'X';
 		/*
 		 * TAINT_FORCED_RMMOD: could be added.
 		 * TAINT_UNSAFE_SMP, TAINT_MACHINE_CHECK, TAINT_BAD_PAGE don't
@@ -3217,6 +3281,7 @@ void print_modules(void)
 	if (last_unloaded_module[0])
 		printk(" [last unloaded: %s]", last_unloaded_module);
 	printk("\n");
+	printk("Supported: %s\n", supported_printable(get_taint()));
 }
 
 #ifdef CONFIG_MODVERSIONS
--- a/kernel/panic.c
+++ b/kernel/panic.c
@@ -174,6 +174,8 @@ static const struct tnt tnts[] = {
 	{ TAINT_WARN,			'W', ' ' },
 	{ TAINT_CRAP,			'C', ' ' },
 	{ TAINT_FIRMWARE_WORKAROUND,	'I', ' ' },
+	{ TAINT_NO_SUPPORT,		'N', ' ' },
+	{ TAINT_EXTERNAL_SUPPORT,	'X', ' ' },
 };
 
 /**
@@ -191,6 +193,8 @@ static const struct tnt tnts[] = {
  *  'W' - Taint on warning.
  *  'C' - modules from drivers/staging are loaded.
  *  'I' - Working around severe firmware bug.
+ *  'N' - Unsuported modules loaded.
+ *  'X' - Modules with external support loaded.
  *
  *	The string is overwritten by the next call to print_tainted().
  */
@@ -226,6 +230,11 @@ unsigned long get_taint(void)
 	return tainted_mask;
 }
 
+void add_nonfatal_taint(unsigned flag)
+{
+	set_bit(flag, &tainted_mask);
+}
+
 void add_taint(unsigned flag)
 {
 	/*
--- a/kernel/sysctl.c
+++ b/kernel/sysctl.c
@@ -664,6 +664,15 @@ static struct ctl_table kern_table[] = {
 		.extra1		= &pid_max_min,
 		.extra2		= &pid_max_max,
 	},
+#ifdef CONFIG_MODULES
+	{
+		.procname	= "unsupported",
+		.data		= &unsupported,
+		.maxlen		= sizeof(int),
+		.mode		= 0644,
+		.proc_handler	= &proc_dointvec,
+	},
+#endif
 	{
 		.procname	= "panic_on_oops",
 		.data		= &panic_on_oops,
--- a/scripts/Makefile.modpost
+++ b/scripts/Makefile.modpost
@@ -81,7 +81,9 @@ modpost = scripts/mod/modpost
  $(if $(KBUILD_EXTMOD),-o $(modulesymfile))      \
  $(if $(CONFIG_DEBUG_SECTION_MISMATCH),,-S)      \
  $(if $(KBUILD_EXTMOD)$(KBUILD_MODPOST_WARN),-w) \
- $(if $(cross_build),-c)
+ $(if $(cross_build),-c)			 \
+ -N $(firstword $(wildcard $(dir $(MODVERDIR))/Module.supported \
+				  $(objtree)/Module.supported /dev/null))
 
 quiet_cmd_modpost = MODPOST $(words $(filter-out vmlinux FORCE, $^)) modules
       cmd_modpost = $(modpost) -s
--- a/scripts/mod/modpost.c
+++ b/scripts/mod/modpost.c
@@ -1639,6 +1639,48 @@ static void check_sec_ref(struct module
 	}
 }
 
+void *supported_file;
+unsigned long supported_size;
+
+static const char *supported(struct module *mod)
+{
+	unsigned long pos = 0;
+	char *line;
+
+	/* In a first shot, do a simple linear scan. */
+	while ((line = get_next_line(&pos, supported_file,
+				     supported_size))) {
+		const char *basename, *how = "yes";
+		char *l = line;
+
+		/* optional type-of-support flag */
+		for (l = line; *l != '\0'; l++) {
+			if (*l == ' ' || *l == '\t') {
+				*l = '\0';
+				how = l + 1;
+				break;
+			}
+		}
+
+		/* skip directory components */
+		if ((l = strrchr(line, '/')))
+			line = l + 1;
+		/* strip .ko extension */
+		l = line + strlen(line);
+		if (l - line > 3 && !strcmp(l-3, ".ko"))
+			*(l-3) = '\0';
+
+		/* skip directory components */
+		if ((basename = strrchr(mod->name, '/')))
+			basename++;
+		else
+			basename = mod->name;
+		if (!strcmp(basename, line))
+			return how;
+	}
+	return NULL;
+}
+
 static void read_symbols(char *modname)
 {
 	const char *symname;
@@ -1826,6 +1868,13 @@ static void add_staging_flag(struct buff
 		buf_printf(b, "\nMODULE_INFO(staging, \"Y\");\n");
 }
 
+static void add_supported_flag(struct buffer *b, struct module *mod)
+{
+	const char *how = supported(mod);
+	if (how)
+		buf_printf(b, "\nMODULE_INFO(supported, \"%s\");\n", how);
+}
+
 /**
  * Record CRCs for unresolved symbols
  **/
@@ -1966,6 +2015,13 @@ static void write_if_changed(struct buff
 	fclose(file);
 }
 
+static void read_supported(const char *fname)
+{
+	supported_file = grab_file(fname, &supported_size);
+	if (!supported_file)
+		; /* ignore error */
+}
+
 /* parse Module.symvers file. line format:
  * 0x12345678<tab>symbol<tab>module[[<tab>export]<tab>something]
  **/
@@ -2059,12 +2115,13 @@ int main(int argc, char **argv)
 	struct buffer buf = { };
 	char *kernel_read = NULL, *module_read = NULL;
 	char *dump_write = NULL;
+	const char *supported = NULL;
 	int opt;
 	int err;
 	struct ext_sym_list *extsym_iter;
 	struct ext_sym_list *extsym_start = NULL;
 
-	while ((opt = getopt(argc, argv, "i:I:e:cmsSo:awM:K:")) != -1) {
+	while ((opt = getopt(argc, argv, "i:I:e:cmsSo:awM:K:N:")) != -1) {
 		switch (opt) {
 		case 'i':
 			kernel_read = optarg;
@@ -2102,11 +2159,16 @@ int main(int argc, char **argv)
 		case 'w':
 			warn_unresolved = 1;
 			break;
+		case 'N':
+			supported = optarg;
+			break;
 		default:
 			exit(1);
 		}
 	}
 
+	if (supported)
+		read_supported(supported);
 	if (kernel_read)
 		read_dump(kernel_read, 1);
 	if (module_read)
@@ -2139,6 +2201,7 @@ int main(int argc, char **argv)
 
 		add_header(&buf, mod);
 		add_staging_flag(&buf, mod->name);
+		add_supported_flag(&buf, mod);
 		err |= add_versions(&buf, mod);
 		add_depends(&buf, mod, modules);
 		add_moddevtable(&buf, mod);
