diff mbox series

modules: add modalias file to sysfs for modules.

Message ID 20221128201332.3482092-1-allenwebb@google.com
State Superseded
Headers show
Series modules: add modalias file to sysfs for modules. | expand

Commit Message

Allen Webb Nov. 28, 2022, 8:13 p.m. UTC
USB devices support the authorized attribute which can be used by
user-space to implement trust-based systems for enabling USB devices. It
would be helpful when building these systems to be able to know in
advance which kernel drivers (or modules) are reachable from a
particular USB device.

This information is readily available for external modules in
modules.alias. However, builtin kernel modules are not covered. This
patch adds a sys-fs attribute to both builtin and loaded modules
exposing the matching rules in the modalias format for integration
with tools like USBGuard.

Signed-off-by: Allen Webb <allenwebb@google.com>
---
 drivers/base/Makefile          |   2 +-
 drivers/base/base.h            |   8 ++
 drivers/base/bus.c             |  42 ++++++
 drivers/base/mod_devicetable.c | 247 +++++++++++++++++++++++++++++++++
 drivers/usb/core/driver.c      |   2 +
 include/linux/device/bus.h     |   8 ++
 include/linux/module.h         |   1 +
 kernel/module/internal.h       |   2 +
 kernel/module/sysfs.c          | 100 +++++++++++++
 kernel/params.c                |   2 +
 10 files changed, 413 insertions(+), 1 deletion(-)
 create mode 100644 drivers/base/mod_devicetable.c

Comments

Christophe Leroy Nov. 29, 2022, 7:46 a.m. UTC | #1
Le 28/11/2022 à 21:13, Allen Webb a écrit :
> USB devices support the authorized attribute which can be used by
> user-space to implement trust-based systems for enabling USB devices. It
> would be helpful when building these systems to be able to know in
> advance which kernel drivers (or modules) are reachable from a
> particular USB device.
> 
> This information is readily available for external modules in
> modules.alias. However, builtin kernel modules are not covered. This
> patch adds a sys-fs attribute to both builtin and loaded modules
> exposing the matching rules in the modalias format for integration
> with tools like USBGuard.
> 
> Signed-off-by: Allen Webb <allenwebb@google.com>
> ---

You already sent a patch with the same title on Nov 11th. Why are you 
resending ?

The line count is different from previous patch. Is that just a rebase ?

The commit message is different, so is it a new version ? If so it 
should be marked [PATCH v2] in the title.

Also, there should be some explanation of the resend here below the ---, 
so that everyone knows the reason you are resending or sending a new 
version.

>   drivers/base/Makefile          |   2 +-
>   drivers/base/base.h            |   8 ++
>   drivers/base/bus.c             |  42 ++++++
>   drivers/base/mod_devicetable.c | 247 +++++++++++++++++++++++++++++++++
>   drivers/usb/core/driver.c      |   2 +
>   include/linux/device/bus.h     |   8 ++
>   include/linux/module.h         |   1 +
>   kernel/module/internal.h       |   2 +
>   kernel/module/sysfs.c          | 100 +++++++++++++
>   kernel/params.c                |   2 +
>   10 files changed, 413 insertions(+), 1 deletion(-)
>   create mode 100644 drivers/base/mod_devicetable.c
> 
> diff --git a/drivers/base/Makefile b/drivers/base/Makefile
> index 83217d243c25b..924d46ae987f4 100644
> --- a/drivers/base/Makefile
> +++ b/drivers/base/Makefile
> @@ -15,7 +15,7 @@ obj-y				+= firmware_loader/
>   obj-$(CONFIG_NUMA)	+= node.o
>   obj-$(CONFIG_MEMORY_HOTPLUG) += memory.o
>   ifeq ($(CONFIG_SYSFS),y)
> -obj-$(CONFIG_MODULES)	+= module.o
> +obj-$(CONFIG_MODULES)	+= mod_devicetable.o module.o
>   endif
>   obj-$(CONFIG_SYS_HYPERVISOR) += hypervisor.o
>   obj-$(CONFIG_REGMAP)	+= regmap/
> diff --git a/drivers/base/base.h b/drivers/base/base.h
> index b902d1ecc247f..beaa252c04388 100644
> --- a/drivers/base/base.h
> +++ b/drivers/base/base.h
> @@ -173,6 +173,14 @@ static inline void module_add_driver(struct module *mod,
>   static inline void module_remove_driver(struct device_driver *drv) { }
>   #endif
>   
> +#if defined(CONFIG_SYSFS)
> +ssize_t usb_drv_to_modalias(struct device_driver *drv, char *buf,
> +			    size_t count);
> +#else
> +static inline ssize_t usb_drv_to_modalias(struct device_driver *drv, char *buf,
> +					  size_t count) { return -EINVAL; }
> +#endif
> +
>   #ifdef CONFIG_DEVTMPFS
>   extern int devtmpfs_init(void);
>   #else
> diff --git a/drivers/base/bus.c b/drivers/base/bus.c
> index 7ca47e5b3c1f4..4e0c5925545e5 100644
> --- a/drivers/base/bus.c
> +++ b/drivers/base/bus.c
> @@ -178,6 +178,48 @@ static const struct kset_uevent_ops bus_uevent_ops = {
>   
>   static struct kset *bus_kset;
>   
> +/**
> + * bus_for_each - bus iterator.
> + * @start: bus to start iterating from.
> + * @data: data for the callback.
> + * @fn: function to be called for each device.
> + *
> + * Iterate over list of buses, and call @fn for each,
> + * passing it @data. If @start is not NULL, we use that bus to
> + * begin iterating from.
> + *
> + * We check the return of @fn each time. If it returns anything
> + * other than 0, we break out and return that value.
> + *
> + * NOTE: The bus that returns a non-zero value is not retained
> + * in any way, nor is its refcount incremented. If the caller needs
> + * to retain this data, it should do so, and increment the reference
> + * count in the supplied callback.
> + */
> +int bus_for_each(void *data, int (*fn)(struct bus_type *, void *))
> +{
> +	int error = 0;
> +	struct bus_type *bus;
> +	struct subsys_private *bus_prv;
> +	struct kset *subsys;
> +	struct kobject *k;
> +
> +	spin_lock(&bus_kset->list_lock);
> +
> +	list_for_each_entry(k, &bus_kset->list, entry) {
> +		subsys = container_of(k, struct kset, kobj);
> +		bus_prv = container_of(subsys, struct subsys_private, subsys);
> +		bus = bus_prv->bus;
> +		error = fn(bus, data);
> +		if (error)
> +			break;
> +	}
> +
> +	spin_unlock(&bus_kset->list_lock);
> +	return error;
> +}
> +EXPORT_SYMBOL_GPL(bus_for_each);
> +
>   /* Manually detach a device from its associated driver. */
>   static ssize_t unbind_store(struct device_driver *drv, const char *buf,
>   			    size_t count)
> diff --git a/drivers/base/mod_devicetable.c b/drivers/base/mod_devicetable.c
> new file mode 100644
> index 0000000000000..b04442207668c
> --- /dev/null
> +++ b/drivers/base/mod_devicetable.c
> @@ -0,0 +1,247 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * mod_devicetable.c - helpers for displaying modaliases through sysfs.
> + *
> + * This borrows a lot from file2alias.c
> + */
> +
> +#include <linux/device/bus.h>
> +#include <linux/device.h>
> +#include <linux/usb.h>
> +
> +#include "base.h"
> +#include "../usb/core/usb.h"
> +
> +#define ADD(buf, count, len, sep, cond, field)				\
> +do {									\
> +	if (cond)							\
> +		(len) += scnprintf(&(buf)[len],				\
> +			(count) - (len),				\
> +			sizeof(field) == 1 ? (sep "%02X") :		\
> +			sizeof(field) == 2 ? (sep "%04X") :		\
> +			sizeof(field) == 4 ? (sep "%08X") : "",		\
> +			(field));					\
> +	else								\
> +		(len) += scnprintf(&(buf)[len], (count) - (len), (sep "*")); \
> +} while (0)
> +
> +#ifdef CONFIG_USB
> +/* USB related modaliases can be split because of device number matching, so
> + * this function handles individual modaliases for one segment of the range.
> + *
> + *
> + */
> +static ssize_t usb_id_to_modalias(const struct usb_device_id *id,
> +				  unsigned int bcdDevice_initial,
> +				  int bcdDevice_initial_digits,
> +				  unsigned char range_lo,
> +				  unsigned char range_hi,
> +				  unsigned char max, const char *mod_name,
> +				  char *buf, size_t count)
> +{
> +	ssize_t len = 0;
> +
> +	ADD(buf, count, len, "alias usb:v",
> +	    id->match_flags & USB_DEVICE_ID_MATCH_VENDOR, id->idVendor);
> +	ADD(buf, count, len, "p", id->match_flags & USB_DEVICE_ID_MATCH_PRODUCT,
> +	    id->idProduct);
> +
> +	len += scnprintf(&buf[len], count - len, "d");
> +	if (bcdDevice_initial_digits)
> +		len += scnprintf(&buf[len], count - len, "%0*X",
> +			bcdDevice_initial_digits, bcdDevice_initial);
> +	if (range_lo == range_hi) {
> +		len += scnprintf(&buf[len], count - len, "%X", range_lo);
> +	} else if (range_lo > 0 || range_hi < max) {
> +		if (range_lo > 0x9 || range_hi < 0xA) {
> +			len += scnprintf(&buf[len], count - len, "[%X-%X]",
> +					 range_lo, range_hi);
> +		} else {
> +			len += scnprintf(&buf[len], count - len,
> +				range_lo < 0x9 ? "[%X-9" : "[%X",
> +				range_lo);
> +			len += scnprintf(&buf[len], count - len,
> +				range_hi > 0xA ? "A-%X]" : "%X]",
> +				range_hi);
> +		}
> +	}
> +	if (bcdDevice_initial_digits < (sizeof(id->bcdDevice_lo) * 2 - 1))
> +		len += scnprintf(&buf[len], count - len, "*");
> +
> +	ADD(buf, count, len, "dc",
> +	    id->match_flags & USB_DEVICE_ID_MATCH_DEV_CLASS, id->bDeviceClass);
> +	ADD(buf, count, len, "dsc",
> +	    id->match_flags & USB_DEVICE_ID_MATCH_DEV_SUBCLASS,
> +	    id->bDeviceSubClass);
> +	ADD(buf, count, len, "dp",
> +	    id->match_flags & USB_DEVICE_ID_MATCH_DEV_PROTOCOL,
> +	    id->bDeviceProtocol);
> +	ADD(buf, count, len, "ic",
> +	    id->match_flags & USB_DEVICE_ID_MATCH_INT_CLASS,
> +	    id->bInterfaceClass);
> +	ADD(buf, count, len, "isc",
> +	    id->match_flags & USB_DEVICE_ID_MATCH_INT_SUBCLASS,
> +	    id->bInterfaceSubClass);
> +	ADD(buf, count, len, "ip",
> +	    id->match_flags & USB_DEVICE_ID_MATCH_INT_PROTOCOL,
> +	    id->bInterfaceProtocol);
> +	ADD(buf, count, len, "in",
> +	    id->match_flags & USB_DEVICE_ID_MATCH_INT_NUMBER,
> +	    id->bInterfaceNumber);
> +
> +	len += scnprintf(&buf[len], count - len, " %s\n", mod_name);
> +	return len;
> +}
> +
> +/* Handles increment/decrement of BCD formatted integers */
> +/* Returns the previous value, so it works like i++ or i-- */
> +static unsigned int incbcd(unsigned int *bcd,
> +			   int inc,
> +			   unsigned char max,
> +			   size_t chars)
> +{
> +	unsigned int init = *bcd, i, j;
> +	unsigned long long c, dec = 0;
> +
> +	/* If bcd is not in BCD format, just increment */
> +	if (max > 0x9) {
> +		*bcd += inc;
> +		return init;
> +	}
> +
> +	/* Convert BCD to Decimal */
> +	for (i = 0 ; i < chars ; i++) {
> +		c = (*bcd >> (i << 2)) & 0xf;
> +		c = c > 9 ? 9 : c; /* force to bcd just in case */
> +		for (j = 0 ; j < i ; j++)
> +			c = c * 10;
> +		dec += c;
> +	}
> +
> +	/* Do our increment/decrement */
> +	dec += inc;
> +	*bcd  = 0;
> +
> +	/* Convert back to BCD */
> +	for (i = 0 ; i < chars ; i++) {
> +		for (c = 1, j = 0 ; j < i ; j++)
> +			c = c * 10;
> +		c = (dec / c) % 10;
> +		*bcd += c << (i << 2);
> +	}
> +	return init;
> +}
> +
> +/* Print the modaliases for the specified struct usb_device_id.
> + */
> +static ssize_t usb_id_to_modalias_multi(const struct usb_device_id *id,
> +					const char *mod_name, char *buf,
> +					size_t count)
> +{
> +	ssize_t len = 0;
> +	unsigned int devlo, devhi;
> +	unsigned char chi, clo, max;
> +	int ndigits;
> +
> +	devlo = id->match_flags & USB_DEVICE_ID_MATCH_DEV_LO ?
> +		id->bcdDevice_lo : 0x0U;
> +	devhi = id->match_flags & USB_DEVICE_ID_MATCH_DEV_HI ?
> +		id->bcdDevice_hi : ~0x0U;
> +
> +	/* Figure out if this entry is in bcd or hex format */
> +	max = 0x9; /* Default to decimal format */
> +	for (ndigits = 0 ; ndigits < sizeof(id->bcdDevice_lo) * 2 ; ndigits++) {
> +		clo = (devlo >> (ndigits << 2)) & 0xf;
> +		chi = ((devhi > 0x9999 ? 0x9999 : devhi) >>
> +		       (ndigits << 2)) & 0xf;
> +		if (clo > max || chi > max) {
> +			max = 0xf;
> +			break;
> +		}
> +	}
> +
> +	/*
> +	 * Some modules (visor) have empty slots as placeholder for
> +	 * run-time specification that results in catch-all alias
> +	 */
> +	if (!(id->idVendor || id->idProduct || id->bDeviceClass ||
> +	      id->bInterfaceClass))
> +		return len;
> +
> +	/* Convert numeric bcdDevice range into fnmatch-able pattern(s) */
> +	for (ndigits = sizeof(id->bcdDevice_lo) * 2 - 1; devlo <= devhi;
> +	     ndigits--) {
> +		clo = devlo & 0xf;
> +		chi = devhi & 0xf;
> +		/* If we are in bcd mode, truncate if necessary */
> +		if (chi > max)
> +			chi = max;
> +		devlo >>= 4;
> +		devhi >>= 4;
> +
> +		if (devlo == devhi || !ndigits) {
> +			len += usb_id_to_modalias(id, devlo, ndigits, clo, chi,
> +						  max, mod_name, buf + len,
> +						  count - len);
> +			break;
> +		}
> +
> +		if (clo > 0x0)
> +			len += usb_id_to_modalias(id,
> +				incbcd(&devlo, 1, max,
> +				       sizeof(id->bcdDevice_lo) * 2),
> +				ndigits, clo, max, max, mod_name, buf + len,
> +				count - len);
> +
> +		if (chi < max)
> +			len += usb_id_to_modalias(id,
> +				incbcd(&devhi, -1, max,
> +				       sizeof(id->bcdDevice_lo) * 2),
> +				ndigits, 0x0, chi, max, mod_name, buf + len,
> +				count - len);
> +	}
> +	return len;
> +}
> +
> +/* Print the modaliases for the given driver assumed to be an usb_driver or
> + * usb_device_driver.
> + *
> + * "alias" is prepended and the module name is appended to each modalias to
> + * match the format in modules.aliases.
> + *
> + * The modaliases will be written out to @buf with @count being the maximum
> + * bytes to write. The return value is a negative errno on error or the number
> + * of bytes written to @buf on success.
> + */
> +ssize_t usb_drv_to_modalias(struct device_driver *drv, char *buf,
> +			    size_t count)
> +{
> +	ssize_t len = 0;
> +	const struct usb_device_id *id;
> +	const char *mod_name;
> +
> +	if (drv->bus != &usb_bus_type)
> +		return -EINVAL;
> +
> +	if (drv->owner)
> +		mod_name = drv->owner->name;
> +	else
> +		mod_name = drv->mod_name;
> +
> +	if (is_usb_device_driver(drv))
> +		id = to_usb_device_driver(drv)->id_table;
> +	else
> +		id = to_usb_driver(drv)->id_table;
> +	if (!id)
> +		return len;
> +
> +	for (; id->match_flags; id++) {
> +		len += usb_id_to_modalias_multi(id, mod_name, buf + len,
> +						count - len);
> +	}
> +	return len;
> +}
> +#else
> +inline ssize_t usb_drv_to_modalias(struct device_driver *drv, char *buf,
> +				   size_t count){ return 0; }
> +#endif
> diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c
> index 7e7e119c253fb..fdbc197b64c9c 100644
> --- a/drivers/usb/core/driver.c
> +++ b/drivers/usb/core/driver.c
> @@ -32,6 +32,7 @@
>   #include <linux/usb/quirks.h>
>   #include <linux/usb/hcd.h>
>   
> +#include "../../base/base.h"
>   #include "usb.h"
>   
>   
> @@ -2030,4 +2031,5 @@ struct bus_type usb_bus_type = {
>   	.match =	usb_device_match,
>   	.uevent =	usb_uevent,
>   	.need_parent_lock =	true,
> +	.drv_to_modalias = usb_drv_to_modalias,
>   };
> diff --git a/include/linux/device/bus.h b/include/linux/device/bus.h
> index d8b29ccd07e56..cce0bedec63d9 100644
> --- a/include/linux/device/bus.h
> +++ b/include/linux/device/bus.h
> @@ -61,6 +61,10 @@ struct fwnode_handle;
>    *			this bus.
>    * @dma_cleanup:	Called to cleanup DMA configuration on a device on
>    *			this bus.
> + * @drv_to_modalias:    Called to convert the matching IDs in a
> + *                      struct device_driver to their corresponding modaliases.
> + *                      Note that the struct device_driver is expected to belong
> + *                      to this bus.
>    * @pm:		Power management operations of this bus, callback the specific
>    *		device driver's pm-ops.
>    * @iommu_ops:  IOMMU specific operations for this bus, used to attach IOMMU
> @@ -107,6 +111,9 @@ struct bus_type {
>   	int (*dma_configure)(struct device *dev);
>   	void (*dma_cleanup)(struct device *dev);
>   
> +	ssize_t (*drv_to_modalias)(struct device_driver *drv, char *buf,
> +				   size_t count);
> +
>   	const struct dev_pm_ops *pm;
>   
>   	const struct iommu_ops *iommu_ops;
> @@ -161,6 +168,7 @@ void subsys_dev_iter_init(struct subsys_dev_iter *iter,
>   struct device *subsys_dev_iter_next(struct subsys_dev_iter *iter);
>   void subsys_dev_iter_exit(struct subsys_dev_iter *iter);
>   
> +int bus_for_each(void *data, int (*fn)(struct bus_type *, void *));
>   int bus_for_each_dev(struct bus_type *bus, struct device *start, void *data,
>   		     int (*fn)(struct device *dev, void *data));
>   struct device *bus_find_device(struct bus_type *bus, struct device *start,
> diff --git a/include/linux/module.h b/include/linux/module.h
> index ec61fb53979a9..0bfa859a21566 100644
> --- a/include/linux/module.h
> +++ b/include/linux/module.h
> @@ -47,6 +47,7 @@ struct module_kobject {
>   	struct kobject *drivers_dir;
>   	struct module_param_attrs *mp;
>   	struct completion *kobj_completion;
> +	struct bin_attribute modalias_attr;
>   } __randomize_layout;
>   
>   struct module_attribute {
> diff --git a/kernel/module/internal.h b/kernel/module/internal.h
> index 2e2bf236f5582..8d7ae37584868 100644
> --- a/kernel/module/internal.h
> +++ b/kernel/module/internal.h
> @@ -259,11 +259,13 @@ static inline void add_kallsyms(struct module *mod, const struct load_info *info
>   #endif /* CONFIG_KALLSYMS */
>   
>   #ifdef CONFIG_SYSFS
> +void add_modalias_attr(struct module_kobject *mk);
>   int mod_sysfs_setup(struct module *mod, const struct load_info *info,
>   		    struct kernel_param *kparam, unsigned int num_params);
>   void mod_sysfs_teardown(struct module *mod);
>   void init_param_lock(struct module *mod);
>   #else /* !CONFIG_SYSFS */
> +static inline void add_modalias_attr(struct module_kobject *mk) {}
>   static inline int mod_sysfs_setup(struct module *mod,
>   			   	  const struct load_info *info,
>   			   	  struct kernel_param *kparam,
> diff --git a/kernel/module/sysfs.c b/kernel/module/sysfs.c
> index ce68f821dcd12..e80bfa4639765 100644
> --- a/kernel/module/sysfs.c
> +++ b/kernel/module/sysfs.c
> @@ -5,6 +5,8 @@
>    * Copyright (C) 2008 Rusty Russell
>    */
>   
> +#include <linux/device/bus.h>
> +#include <linux/device/driver.h>
>   #include <linux/module.h>
>   #include <linux/kernel.h>
>   #include <linux/fs.h>
> @@ -240,6 +242,102 @@ static inline void add_notes_attrs(struct module *mod, const struct load_info *i
>   static inline void remove_notes_attrs(struct module *mod) { }
>   #endif /* CONFIG_KALLSYMS */
>   
> +/* Track of the buffer and module identity in callbacks when walking the list of
> + * drivers for each bus.
> + */
> +struct modalias_bus_print_state {
> +	struct module_kobject *mk;
> +	char *buf;
> +	size_t count;
> +	ssize_t len;
> +};
> +
> +static int print_modalias_for_drv(struct device_driver *drv, void *p)
> +{
> +	struct modalias_bus_print_state *s = p;
> +	struct module_kobject *mk = s->mk;
> +	ssize_t len;
> +	/* Skip drivers that do not match this module. */
> +	if (mk->mod) {
> +		if (mk->mod != drv->owner)
> +			return 0;
> +	} else if (!mk->kobj.name || !drv->mod_name ||
> +		   strcmp(mk->kobj.name, drv->mod_name))
> +		return 0;
> +
> +	if (drv->bus && drv->bus->drv_to_modalias) {
> +		len = drv->bus->drv_to_modalias(drv, s->buf + s->len,
> +						s->count - s->len);
> +		if (len < 0)
> +			return len;
> +		s->len += len;
> +	}
> +
> +	s->len += scnprintf(&s->buf[s->len], s->count - s->len, "driver %s\n",
> +			    drv->name);
> +	return 0;
> +}
> +
> +static int print_modalias_for_bus(struct bus_type *type, void *p)
> +{
> +	return bus_for_each_drv(type, NULL, p, print_modalias_for_drv);
> +}
> +
> +static ssize_t module_modalias_read(struct file *filp, struct kobject *kobj,
> +				    struct bin_attribute *bin_attr,
> +				    char *buf, loff_t pos, size_t count)
> +{
> +	struct module_kobject *mk = container_of(kobj, struct module_kobject,
> +						 kobj);
> +	struct modalias_bus_print_state state = {mk, buf, count, 0};
> +	int error = 0;
> +
> +	if (pos != 0)
> +		return -EINVAL;
> +
> +	error = bus_for_each(&state, print_modalias_for_bus);
> +	if (error)
> +		return error;
> +
> +	if (mk->mod)
> +		state.len += scnprintf(&buf[state.len], count - state.len,
> +				       "modalias %s %s\n", kobject_name(kobj),
> +				       mk->mod->name);
> +	else
> +		state.len += scnprintf(&buf[state.len], count - state.len,
> +				       "modalias %s NULL\n",
> +				       kobject_name(kobj));
> +
> +	/*
> +	 * The caller checked the pos and count against our size.
> +	 */
> +	return state.len;
> +}
> +
> +/* Used in kernel/params.c for builtin modules.
> + *
> + * `struct module_kobject` is used instead of `struct module` because for
> + * builtin modules, the `struct module` is not available when this is called.
> + */
> +void add_modalias_attr(struct module_kobject *mk)
> +{
> +	sysfs_bin_attr_init(&mk->modalias_attr);
> +	mk->modalias_attr.attr.name = "modalias";
> +	mk->modalias_attr.attr.mode = 0444;
> +	mk->modalias_attr.read = module_modalias_read;
> +	if (sysfs_create_bin_file(&mk->kobj, &mk->modalias_attr)) {
> +		/* We shouldn't ignore the return type, but there is nothing to
> +		 * do.
> +		 */
> +		return;
> +	}
> +}
> +
> +static void remove_modalias_attr(struct module_kobject *mk)
> +{
> +	sysfs_remove_bin_file(&mk->kobj, &mk->modalias_attr);
> +}
> +
>   static void del_usage_links(struct module *mod)
>   {
>   #ifdef CONFIG_MODULE_UNLOAD
> @@ -398,6 +496,7 @@ int mod_sysfs_setup(struct module *mod,
>   
>   	add_sect_attrs(mod, info);
>   	add_notes_attrs(mod, info);
> +	add_modalias_attr(&mod->mkobj);
>   
>   	return 0;
>   
> @@ -415,6 +514,7 @@ int mod_sysfs_setup(struct module *mod,
>   
>   static void mod_sysfs_fini(struct module *mod)
>   {
> +	remove_modalias_attr(&mod->mkobj);
>   	remove_notes_attrs(mod);
>   	remove_sect_attrs(mod);
>   	mod_kobject_put(mod);
> diff --git a/kernel/params.c b/kernel/params.c
> index 5b92310425c50..111024196361a 100644
> --- a/kernel/params.c
> +++ b/kernel/params.c
> @@ -13,6 +13,7 @@
>   #include <linux/slab.h>
>   #include <linux/ctype.h>
>   #include <linux/security.h>
> +#include "module/internal.h"
>   
>   #ifdef CONFIG_SYSFS
>   /* Protects all built-in parameters, modules use their own param_lock */
> @@ -815,6 +816,7 @@ static void __init kernel_add_sysfs_param(const char *name,
>   	BUG_ON(err);
>   	kobject_uevent(&mk->kobj, KOBJ_ADD);
>   	kobject_put(&mk->kobj);
> +	add_modalias_attr(mk);
>   }
>   
>   /*
kernel test robot Nov. 30, 2022, 2:22 a.m. UTC | #2
Hi Allen,

Thank you for the patch! Yet something to improve:

[auto build test ERROR on driver-core/driver-core-testing]
[also build test ERROR on driver-core/driver-core-next driver-core/driver-core-linus mcgrof/modules-next usb/usb-testing usb/usb-next usb/usb-linus linus/master v6.1-rc7 next-20221129]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Allen-Webb/modules-add-modalias-file-to-sysfs-for-modules/20221129-041511
patch link:    https://lore.kernel.org/r/20221128201332.3482092-1-allenwebb%40google.com
patch subject: [PATCH] modules: add modalias file to sysfs for modules.
config: arm-buildonly-randconfig-r003-20221128
compiler: arm-linux-gnueabi-gcc (GCC) 12.1.0
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # https://github.com/intel-lab-lkp/linux/commit/5f45459f50fc0240781c397065e1b7bafcc79e08
        git remote add linux-review https://github.com/intel-lab-lkp/linux
        git fetch --no-tags linux-review Allen-Webb/modules-add-modalias-file-to-sysfs-for-modules/20221129-041511
        git checkout 5f45459f50fc0240781c397065e1b7bafcc79e08
        # save the config file
        mkdir build_dir && cp config build_dir/.config
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-12.1.0 make.cross W=1 O=build_dir ARCH=arm SHELL=/bin/bash

If you fix the issue, kindly add following tag where applicable
| Reported-by: kernel test robot <lkp@intel.com>

All errors (new ones prefixed by >>):

   In file included from include/linux/container_of.h:5,
                    from include/linux/kernel.h:21,
                    from kernel/params.c:6:
   kernel/module/internal.h: In function 'mod_find':
>> include/linux/container_of.h:20:54: error: invalid use of undefined type 'struct module'
      20 |         static_assert(__same_type(*(ptr), ((type *)0)->member) ||       \
         |                                                      ^~
   include/linux/build_bug.h:78:56: note: in definition of macro '__static_assert'
      78 | #define __static_assert(expr, msg, ...) _Static_assert(expr, msg)
         |                                                        ^~~~
   include/linux/container_of.h:20:9: note: in expansion of macro 'static_assert'
      20 |         static_assert(__same_type(*(ptr), ((type *)0)->member) ||       \
         |         ^~~~~~~~~~~~~
   include/linux/container_of.h:20:23: note: in expansion of macro '__same_type'
      20 |         static_assert(__same_type(*(ptr), ((type *)0)->member) ||       \
         |                       ^~~~~~~~~~~
   include/linux/rculist.h:307:9: note: in expansion of macro 'container_of'
     307 |         container_of(READ_ONCE(ptr), type, member)
         |         ^~~~~~~~~~~~
   include/linux/rculist.h:391:20: note: in expansion of macro 'list_entry_rcu'
     391 |              pos = list_entry_rcu((head)->next, typeof(*pos), member);  \
         |                    ^~~~~~~~~~~~~~
   kernel/module/internal.h:212:9: note: in expansion of macro 'list_for_each_entry_rcu'
     212 |         list_for_each_entry_rcu(mod, &modules, list,
         |         ^~~~~~~~~~~~~~~~~~~~~~~
   include/linux/compiler_types.h:298:27: error: expression in static assertion is not an integer
     298 | #define __same_type(a, b) __builtin_types_compatible_p(typeof(a), typeof(b))
         |                           ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
   include/linux/build_bug.h:78:56: note: in definition of macro '__static_assert'
      78 | #define __static_assert(expr, msg, ...) _Static_assert(expr, msg)
         |                                                        ^~~~
   include/linux/container_of.h:20:9: note: in expansion of macro 'static_assert'
      20 |         static_assert(__same_type(*(ptr), ((type *)0)->member) ||       \
         |         ^~~~~~~~~~~~~
   include/linux/container_of.h:20:23: note: in expansion of macro '__same_type'
      20 |         static_assert(__same_type(*(ptr), ((type *)0)->member) ||       \
         |                       ^~~~~~~~~~~
   include/linux/rculist.h:307:9: note: in expansion of macro 'container_of'
     307 |         container_of(READ_ONCE(ptr), type, member)
         |         ^~~~~~~~~~~~
   include/linux/rculist.h:391:20: note: in expansion of macro 'list_entry_rcu'
     391 |              pos = list_entry_rcu((head)->next, typeof(*pos), member);  \
         |                    ^~~~~~~~~~~~~~
   kernel/module/internal.h:212:9: note: in expansion of macro 'list_for_each_entry_rcu'
     212 |         list_for_each_entry_rcu(mod, &modules, list,
         |         ^~~~~~~~~~~~~~~~~~~~~~~
   In file included from include/uapi/linux/posix_types.h:5,
                    from include/uapi/linux/types.h:14,
                    from include/linux/types.h:6,
                    from include/linux/limits.h:6,
                    from include/linux/kernel.h:16:
>> include/linux/stddef.h:16:33: error: invalid use of undefined type 'struct module'
      16 | #define offsetof(TYPE, MEMBER)  __builtin_offsetof(TYPE, MEMBER)
         |                                 ^~~~~~~~~~~~~~~~~~
   include/linux/container_of.h:23:28: note: in expansion of macro 'offsetof'
      23 |         ((type *)(__mptr - offsetof(type, member))); })
         |                            ^~~~~~~~
   include/linux/rculist.h:307:9: note: in expansion of macro 'container_of'
     307 |         container_of(READ_ONCE(ptr), type, member)
         |         ^~~~~~~~~~~~
   include/linux/rculist.h:391:20: note: in expansion of macro 'list_entry_rcu'
     391 |              pos = list_entry_rcu((head)->next, typeof(*pos), member);  \
         |                    ^~~~~~~~~~~~~~
   kernel/module/internal.h:212:9: note: in expansion of macro 'list_for_each_entry_rcu'
     212 |         list_for_each_entry_rcu(mod, &modules, list,
         |         ^~~~~~~~~~~~~~~~~~~~~~~
   In file included from include/linux/pid.h:5,
                    from include/linux/sched.h:14,
                    from include/linux/sched/mm.h:7,
                    from include/linux/xarray.h:19,
                    from include/linux/radix-tree.h:21,
                    from include/linux/idr.h:15,
                    from include/linux/kernfs.h:12,
                    from include/linux/sysfs.h:16,
                    from include/linux/kobject.h:20,
                    from include/linux/module.h:21,
                    from kernel/params.c:9:
>> include/linux/rculist.h:392:21: error: invalid use of undefined type 'struct module'
     392 |                 &pos->member != (head);                                 \
         |                     ^~
   kernel/module/internal.h:212:9: note: in expansion of macro 'list_for_each_entry_rcu'
     212 |         list_for_each_entry_rcu(mod, &modules, list,
         |         ^~~~~~~~~~~~~~~~~~~~~~~
   include/linux/rculist.h:393:41: error: invalid use of undefined type 'struct module'
     393 |                 pos = list_entry_rcu(pos->member.next, typeof(*pos), member))
         |                                         ^~
   include/linux/container_of.h:19:33: note: in definition of macro 'container_of'
      19 |         void *__mptr = (void *)(ptr);                                   \
         |                                 ^~~
   include/linux/compiler_types.h:345:9: note: in expansion of macro '__compiletime_assert'
     345 |         __compiletime_assert(condition, msg, prefix, suffix)
         |         ^~~~~~~~~~~~~~~~~~~~
   include/linux/compiler_types.h:357:9: note: in expansion of macro '_compiletime_assert'
     357 |         _compiletime_assert(condition, msg, __compiletime_assert_, __COUNTER__)
         |         ^~~~~~~~~~~~~~~~~~~
   include/asm-generic/rwonce.h:36:9: note: in expansion of macro 'compiletime_assert'
      36 |         compiletime_assert(__native_word(t) || sizeof(t) == sizeof(long long),  \
         |         ^~~~~~~~~~~~~~~~~~
   include/asm-generic/rwonce.h:36:28: note: in expansion of macro '__native_word'
      36 |         compiletime_assert(__native_word(t) || sizeof(t) == sizeof(long long),  \
         |                            ^~~~~~~~~~~~~
   include/asm-generic/rwonce.h:49:9: note: in expansion of macro 'compiletime_assert_rwonce_type'
      49 |         compiletime_assert_rwonce_type(x);                              \
         |         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   include/linux/rculist.h:307:22: note: in expansion of macro 'READ_ONCE'
     307 |         container_of(READ_ONCE(ptr), type, member)
         |                      ^~~~~~~~~
   include/linux/rculist.h:393:23: note: in expansion of macro 'list_entry_rcu'
     393 |                 pos = list_entry_rcu(pos->member.next, typeof(*pos), member))
         |                       ^~~~~~~~~~~~~~
   kernel/module/internal.h:212:9: note: in expansion of macro 'list_for_each_entry_rcu'
     212 |         list_for_each_entry_rcu(mod, &modules, list,
         |         ^~~~~~~~~~~~~~~~~~~~~~~
   include/linux/rculist.h:393:41: error: invalid use of undefined type 'struct module'
     393 |                 pos = list_entry_rcu(pos->member.next, typeof(*pos), member))
         |                                         ^~
   include/linux/container_of.h:19:33: note: in definition of macro 'container_of'
      19 |         void *__mptr = (void *)(ptr);                                   \
         |                                 ^~~
   include/linux/compiler_types.h:345:9: note: in expansion of macro '__compiletime_assert'
     345 |         __compiletime_assert(condition, msg, prefix, suffix)
         |         ^~~~~~~~~~~~~~~~~~~~
   include/linux/compiler_types.h:357:9: note: in expansion of macro '_compiletime_assert'
     357 |         _compiletime_assert(condition, msg, __compiletime_assert_, __COUNTER__)
         |         ^~~~~~~~~~~~~~~~~~~
   include/asm-generic/rwonce.h:36:9: note: in expansion of macro 'compiletime_assert'
      36 |         compiletime_assert(__native_word(t) || sizeof(t) == sizeof(long long),  \
         |         ^~~~~~~~~~~~~~~~~~
   include/asm-generic/rwonce.h:36:28: note: in expansion of macro '__native_word'
      36 |         compiletime_assert(__native_word(t) || sizeof(t) == sizeof(long long),  \
         |                            ^~~~~~~~~~~~~
   include/asm-generic/rwonce.h:49:9: note: in expansion of macro 'compiletime_assert_rwonce_type'
      49 |         compiletime_assert_rwonce_type(x);                              \
         |         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   include/linux/rculist.h:307:22: note: in expansion of macro 'READ_ONCE'
     307 |         container_of(READ_ONCE(ptr), type, member)
         |                      ^~~~~~~~~
   include/linux/rculist.h:393:23: note: in expansion of macro 'list_entry_rcu'
     393 |                 pos = list_entry_rcu(pos->member.next, typeof(*pos), member))
         |                       ^~~~~~~~~~~~~~
   kernel/module/internal.h:212:9: note: in expansion of macro 'list_for_each_entry_rcu'
     212 |         list_for_each_entry_rcu(mod, &modules, list,
         |         ^~~~~~~~~~~~~~~~~~~~~~~
   include/linux/rculist.h:393:41: error: invalid use of undefined type 'struct module'
     393 |                 pos = list_entry_rcu(pos->member.next, typeof(*pos), member))
         |                                         ^~
   include/linux/container_of.h:19:33: note: in definition of macro 'container_of'
      19 |         void *__mptr = (void *)(ptr);                                   \
         |                                 ^~~
   include/linux/compiler_types.h:345:9: note: in expansion of macro '__compiletime_assert'
     345 |         __compiletime_assert(condition, msg, prefix, suffix)
         |         ^~~~~~~~~~~~~~~~~~~~
   include/linux/compiler_types.h:357:9: note: in expansion of macro '_compiletime_assert'
     357 |         _compiletime_assert(condition, msg, __compiletime_assert_, __COUNTER__)
         |         ^~~~~~~~~~~~~~~~~~~
   include/asm-generic/rwonce.h:36:9: note: in expansion of macro 'compiletime_assert'
      36 |         compiletime_assert(__native_word(t) || sizeof(t) == sizeof(long long),  \
         |         ^~~~~~~~~~~~~~~~~~
   include/asm-generic/rwonce.h:36:28: note: in expansion of macro '__native_word'
      36 |         compiletime_assert(__native_word(t) || sizeof(t) == sizeof(long long),  \
         |                            ^~~~~~~~~~~~~
   include/asm-generic/rwonce.h:49:9: note: in expansion of macro 'compiletime_assert_rwonce_type'
      49 |         compiletime_assert_rwonce_type(x);                              \
         |         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   include/linux/rculist.h:307:22: note: in expansion of macro 'READ_ONCE'
     307 |         container_of(READ_ONCE(ptr), type, member)
         |                      ^~~~~~~~~
   include/linux/rculist.h:393:23: note: in expansion of macro 'list_entry_rcu'
     393 |                 pos = list_entry_rcu(pos->member.next, typeof(*pos), member))
         |                       ^~~~~~~~~~~~~~
   kernel/module/internal.h:212:9: note: in expansion of macro 'list_for_each_entry_rcu'
     212 |         list_for_each_entry_rcu(mod, &modules, list,
         |         ^~~~~~~~~~~~~~~~~~~~~~~
   include/linux/rculist.h:393:41: error: invalid use of undefined type 'struct module'
     393 |                 pos = list_entry_rcu(pos->member.next, typeof(*pos), member))
         |                                         ^~
   include/linux/container_of.h:19:33: note: in definition of macro 'container_of'
      19 |         void *__mptr = (void *)(ptr);                                   \
--
         |                      ^~~~~~~~~
   include/linux/rculist.h:393:23: note: in expansion of macro 'list_entry_rcu'
     393 |                 pos = list_entry_rcu(pos->member.next, typeof(*pos), member))
         |                       ^~~~~~~~~~~~~~
   kernel/module/internal.h:212:9: note: in expansion of macro 'list_for_each_entry_rcu'
     212 |         list_for_each_entry_rcu(mod, &modules, list,
         |         ^~~~~~~~~~~~~~~~~~~~~~~
   include/linux/rculist.h:393:41: error: invalid use of undefined type 'struct module'
     393 |                 pos = list_entry_rcu(pos->member.next, typeof(*pos), member))
         |                                         ^~
   include/linux/build_bug.h:78:56: note: in definition of macro '__static_assert'
      78 | #define __static_assert(expr, msg, ...) _Static_assert(expr, msg)
         |                                                        ^~~~
   include/linux/container_of.h:20:9: note: in expansion of macro 'static_assert'
      20 |         static_assert(__same_type(*(ptr), ((type *)0)->member) ||       \
         |         ^~~~~~~~~~~~~
   include/linux/container_of.h:20:23: note: in expansion of macro '__same_type'
      20 |         static_assert(__same_type(*(ptr), ((type *)0)->member) ||       \
         |                       ^~~~~~~~~~~
   include/linux/rculist.h:307:9: note: in expansion of macro 'container_of'
     307 |         container_of(READ_ONCE(ptr), type, member)
         |         ^~~~~~~~~~~~
   include/linux/compiler_types.h:345:9: note: in expansion of macro '__compiletime_assert'
     345 |         __compiletime_assert(condition, msg, prefix, suffix)
         |         ^~~~~~~~~~~~~~~~~~~~
   include/linux/compiler_types.h:357:9: note: in expansion of macro '_compiletime_assert'
     357 |         _compiletime_assert(condition, msg, __compiletime_assert_, __COUNTER__)
         |         ^~~~~~~~~~~~~~~~~~~
   include/asm-generic/rwonce.h:36:9: note: in expansion of macro 'compiletime_assert'
      36 |         compiletime_assert(__native_word(t) || sizeof(t) == sizeof(long long),  \
         |         ^~~~~~~~~~~~~~~~~~
   include/asm-generic/rwonce.h:49:9: note: in expansion of macro 'compiletime_assert_rwonce_type'
      49 |         compiletime_assert_rwonce_type(x);                              \
         |         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   include/linux/rculist.h:307:22: note: in expansion of macro 'READ_ONCE'
     307 |         container_of(READ_ONCE(ptr), type, member)
         |                      ^~~~~~~~~
   include/linux/rculist.h:393:23: note: in expansion of macro 'list_entry_rcu'
     393 |                 pos = list_entry_rcu(pos->member.next, typeof(*pos), member))
         |                       ^~~~~~~~~~~~~~
   kernel/module/internal.h:212:9: note: in expansion of macro 'list_for_each_entry_rcu'
     212 |         list_for_each_entry_rcu(mod, &modules, list,
         |         ^~~~~~~~~~~~~~~~~~~~~~~
   include/linux/rculist.h:393:41: error: invalid use of undefined type 'struct module'
     393 |                 pos = list_entry_rcu(pos->member.next, typeof(*pos), member))
         |                                         ^~
   include/linux/build_bug.h:78:56: note: in definition of macro '__static_assert'
      78 | #define __static_assert(expr, msg, ...) _Static_assert(expr, msg)
         |                                                        ^~~~
   include/linux/container_of.h:20:9: note: in expansion of macro 'static_assert'
      20 |         static_assert(__same_type(*(ptr), ((type *)0)->member) ||       \
         |         ^~~~~~~~~~~~~
   include/linux/container_of.h:20:23: note: in expansion of macro '__same_type'
      20 |         static_assert(__same_type(*(ptr), ((type *)0)->member) ||       \
         |                       ^~~~~~~~~~~
   include/linux/rculist.h:307:9: note: in expansion of macro 'container_of'
     307 |         container_of(READ_ONCE(ptr), type, member)
         |         ^~~~~~~~~~~~
   include/asm-generic/rwonce.h:44:43: note: in expansion of macro '__unqual_scalar_typeof'
      44 | #define __READ_ONCE(x)  (*(const volatile __unqual_scalar_typeof(x) *)&(x))
         |                                           ^~~~~~~~~~~~~~~~~~~~~~
   include/asm-generic/rwonce.h:50:9: note: in expansion of macro '__READ_ONCE'
      50 |         __READ_ONCE(x);                                                 \
         |         ^~~~~~~~~~~
   include/linux/rculist.h:307:22: note: in expansion of macro 'READ_ONCE'
     307 |         container_of(READ_ONCE(ptr), type, member)
         |                      ^~~~~~~~~
   include/linux/rculist.h:393:23: note: in expansion of macro 'list_entry_rcu'
     393 |                 pos = list_entry_rcu(pos->member.next, typeof(*pos), member))
         |                       ^~~~~~~~~~~~~~
   kernel/module/internal.h:212:9: note: in expansion of macro 'list_for_each_entry_rcu'
     212 |         list_for_each_entry_rcu(mod, &modules, list,
         |         ^~~~~~~~~~~~~~~~~~~~~~~
   include/linux/rculist.h:393:41: error: invalid use of undefined type 'struct module'
     393 |                 pos = list_entry_rcu(pos->member.next, typeof(*pos), member))
         |                                         ^~
   include/linux/build_bug.h:78:56: note: in definition of macro '__static_assert'
      78 | #define __static_assert(expr, msg, ...) _Static_assert(expr, msg)
         |                                                        ^~~~
   include/linux/container_of.h:20:9: note: in expansion of macro 'static_assert'
      20 |         static_assert(__same_type(*(ptr), ((type *)0)->member) ||       \
         |         ^~~~~~~~~~~~~
   include/linux/container_of.h:20:23: note: in expansion of macro '__same_type'
      20 |         static_assert(__same_type(*(ptr), ((type *)0)->member) ||       \
         |                       ^~~~~~~~~~~
   include/linux/rculist.h:307:9: note: in expansion of macro 'container_of'
     307 |         container_of(READ_ONCE(ptr), type, member)
         |         ^~~~~~~~~~~~
   include/asm-generic/rwonce.h:50:9: note: in expansion of macro '__READ_ONCE'
      50 |         __READ_ONCE(x);                                                 \
         |         ^~~~~~~~~~~
   include/linux/rculist.h:307:22: note: in expansion of macro 'READ_ONCE'
     307 |         container_of(READ_ONCE(ptr), type, member)
         |                      ^~~~~~~~~
   include/linux/rculist.h:393:23: note: in expansion of macro 'list_entry_rcu'
     393 |                 pos = list_entry_rcu(pos->member.next, typeof(*pos), member))
         |                       ^~~~~~~~~~~~~~
   kernel/module/internal.h:212:9: note: in expansion of macro 'list_for_each_entry_rcu'
     212 |         list_for_each_entry_rcu(mod, &modules, list,
         |         ^~~~~~~~~~~~~~~~~~~~~~~
>> include/linux/container_of.h:20:54: error: invalid use of undefined type 'struct module'
      20 |         static_assert(__same_type(*(ptr), ((type *)0)->member) ||       \
         |                                                      ^~
   include/linux/build_bug.h:78:56: note: in definition of macro '__static_assert'
      78 | #define __static_assert(expr, msg, ...) _Static_assert(expr, msg)
         |                                                        ^~~~
   include/linux/container_of.h:20:9: note: in expansion of macro 'static_assert'
      20 |         static_assert(__same_type(*(ptr), ((type *)0)->member) ||       \
         |         ^~~~~~~~~~~~~
   include/linux/container_of.h:20:23: note: in expansion of macro '__same_type'
      20 |         static_assert(__same_type(*(ptr), ((type *)0)->member) ||       \
         |                       ^~~~~~~~~~~
   include/linux/rculist.h:307:9: note: in expansion of macro 'container_of'
     307 |         container_of(READ_ONCE(ptr), type, member)
         |         ^~~~~~~~~~~~
   include/linux/rculist.h:393:23: note: in expansion of macro 'list_entry_rcu'
     393 |                 pos = list_entry_rcu(pos->member.next, typeof(*pos), member))
         |                       ^~~~~~~~~~~~~~
   kernel/module/internal.h:212:9: note: in expansion of macro 'list_for_each_entry_rcu'
     212 |         list_for_each_entry_rcu(mod, &modules, list,
         |         ^~~~~~~~~~~~~~~~~~~~~~~
   include/linux/rculist.h:393:41: error: invalid use of undefined type 'struct module'
     393 |                 pos = list_entry_rcu(pos->member.next, typeof(*pos), member))
         |                                         ^~
   include/linux/build_bug.h:78:56: note: in definition of macro '__static_assert'
      78 | #define __static_assert(expr, msg, ...) _Static_assert(expr, msg)
         |                                                        ^~~~
   include/linux/container_of.h:20:9: note: in expansion of macro 'static_assert'
      20 |         static_assert(__same_type(*(ptr), ((type *)0)->member) ||       \
         |         ^~~~~~~~~~~~~
   include/linux/container_of.h:21:23: note: in expansion of macro '__same_type'
      21 |                       __same_type(*(ptr), void),                        \
         |                       ^~~~~~~~~~~
   include/linux/rculist.h:307:9: note: in expansion of macro 'container_of'
     307 |         container_of(READ_ONCE(ptr), type, member)
         |         ^~~~~~~~~~~~
   include/linux/compiler_types.h:345:9: note: in expansion of macro '__compiletime_assert'
     345 |         __compiletime_assert(condition, msg, prefix, suffix)
         |         ^~~~~~~~~~~~~~~~~~~~
   include/linux/compiler_types.h:357:9: note: in expansion of macro '_compiletime_assert'
     357 |         _compiletime_assert(condition, msg, __compiletime_assert_, __COUNTER__)
         |         ^~~~~~~~~~~~~~~~~~~
   include/asm-generic/rwonce.h:36:9: note: in expansion of macro 'compiletime_assert'
      36 |         compiletime_assert(__native_word(t) || sizeof(t) == sizeof(long long),  \
         |         ^~~~~~~~~~~~~~~~~~
   include/asm-generic/rwonce.h:36:28: note: in expansion of macro '__native_word'
      36 |         compiletime_assert(__native_word(t) || sizeof(t) == sizeof(long long),  \
         |                            ^~~~~~~~~~~~~
   include/asm-generic/rwonce.h:49:9: note: in expansion of macro 'compiletime_assert_rwonce_type'
      49 |         compiletime_assert_rwonce_type(x);                              \
         |         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   include/linux/rculist.h:307:22: note: in expansion of macro 'READ_ONCE'
     307 |         container_of(READ_ONCE(ptr), type, member)
         |                      ^~~~~~~~~
   include/linux/rculist.h:393:23: note: in expansion of macro 'list_entry_rcu'
     393 |                 pos = list_entry_rcu(pos->member.next, typeof(*pos), member))
         |                       ^~~~~~~~~~~~~~
   kernel/module/internal.h:212:9: note: in expansion of macro 'list_for_each_entry_rcu'
     212 |         list_for_each_entry_rcu(mod, &modules, list,
         |         ^~~~~~~~~~~~~~~~~~~~~~~
   include/linux/rculist.h:393:41: error: invalid use of undefined type 'struct module'
     393 |                 pos = list_entry_rcu(pos->member.next, typeof(*pos), member))
         |                                         ^~
   include/linux/build_bug.h:78:56: note: in definition of macro '__static_assert'
      78 | #define __static_assert(expr, msg, ...) _Static_assert(expr, msg)
         |                                                        ^~~~
   include/linux/container_of.h:20:9: note: in expansion of macro 'static_assert'
      20 |         static_assert(__same_type(*(ptr), ((type *)0)->member) ||       \
         |         ^~~~~~~~~~~~~
   include/linux/container_of.h:21:23: note: in expansion of macro '__same_type'
      21 |                       __same_type(*(ptr), void),                        \
         |                       ^~~~~~~~~~~
   include/linux/rculist.h:307:9: note: in expansion of macro 'container_of'
     307 |         container_of(READ_ONCE(ptr), type, member)
         |         ^~~~~~~~~~~~
   include/linux/compiler_types.h:345:9: note: in expansion of macro '__compiletime_assert'
     345 |         __compiletime_assert(condition, msg, prefix, suffix)
         |         ^~~~~~~~~~~~~~~~~~~~
   include/linux/compiler_types.h:357:9: note: in expansion of macro '_compiletime_assert'
     357 |         _compiletime_assert(condition, msg, __compiletime_assert_, __COUNTER__)
         |         ^~~~~~~~~~~~~~~~~~~
   include/asm-generic/rwonce.h:36:9: note: in expansion of macro 'compiletime_assert'
      36 |         compiletime_assert(__native_word(t) || sizeof(t) == sizeof(long long),  \
         |         ^~~~~~~~~~~~~~~~~~
   include/asm-generic/rwonce.h:36:28: note: in expansion of macro '__native_word'
      36 |         compiletime_assert(__native_word(t) || sizeof(t) == sizeof(long long),  \
         |                            ^~~~~~~~~~~~~
   include/asm-generic/rwonce.h:49:9: note: in expansion of macro 'compiletime_assert_rwonce_type'
      49 |         compiletime_assert_rwonce_type(x);                              \
         |         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   include/linux/rculist.h:307:22: note: in expansion of macro 'READ_ONCE'
     307 |         container_of(READ_ONCE(ptr), type, member)
         |                      ^~~~~~~~~
   include/linux/rculist.h:393:23: note: in expansion of macro 'list_entry_rcu'
     393 |                 pos = list_entry_rcu(pos->member.next, typeof(*pos), member))
         |                       ^~~~~~~~~~~~~~
   kernel/module/internal.h:212:9: note: in expansion of macro 'list_for_each_entry_rcu'
     212 |         list_for_each_entry_rcu(mod, &modules, list,
         |         ^~~~~~~~~~~~~~~~~~~~~~~
   include/linux/rculist.h:393:41: error: invalid use of undefined type 'struct module'
     393 |                 pos = list_entry_rcu(pos->member.next, typeof(*pos), member))
..


vim +20 include/linux/container_of.h

d2a8ebbf8192b8 Andy Shevchenko  2021-11-08   9  
d2a8ebbf8192b8 Andy Shevchenko  2021-11-08  10  /**
d2a8ebbf8192b8 Andy Shevchenko  2021-11-08  11   * container_of - cast a member of a structure out to the containing structure
d2a8ebbf8192b8 Andy Shevchenko  2021-11-08  12   * @ptr:	the pointer to the member.
d2a8ebbf8192b8 Andy Shevchenko  2021-11-08  13   * @type:	the type of the container struct this is embedded in.
d2a8ebbf8192b8 Andy Shevchenko  2021-11-08  14   * @member:	the name of the member within the struct.
d2a8ebbf8192b8 Andy Shevchenko  2021-11-08  15   *
7376e561fd2e01 Sakari Ailus     2022-10-24  16   * WARNING: any const qualifier of @ptr is lost.
d2a8ebbf8192b8 Andy Shevchenko  2021-11-08  17   */
d2a8ebbf8192b8 Andy Shevchenko  2021-11-08  18  #define container_of(ptr, type, member) ({				\
d2a8ebbf8192b8 Andy Shevchenko  2021-11-08  19  	void *__mptr = (void *)(ptr);					\
e1edc277e6f6df Rasmus Villemoes 2021-11-08 @20  	static_assert(__same_type(*(ptr), ((type *)0)->member) ||	\
e1edc277e6f6df Rasmus Villemoes 2021-11-08  21  		      __same_type(*(ptr), void),			\
d2a8ebbf8192b8 Andy Shevchenko  2021-11-08  22  		      "pointer type mismatch in container_of()");	\
d2a8ebbf8192b8 Andy Shevchenko  2021-11-08  23  	((type *)(__mptr - offsetof(type, member))); })
d2a8ebbf8192b8 Andy Shevchenko  2021-11-08  24
diff mbox series

Patch

diff --git a/drivers/base/Makefile b/drivers/base/Makefile
index 83217d243c25b..924d46ae987f4 100644
--- a/drivers/base/Makefile
+++ b/drivers/base/Makefile
@@ -15,7 +15,7 @@  obj-y				+= firmware_loader/
 obj-$(CONFIG_NUMA)	+= node.o
 obj-$(CONFIG_MEMORY_HOTPLUG) += memory.o
 ifeq ($(CONFIG_SYSFS),y)
-obj-$(CONFIG_MODULES)	+= module.o
+obj-$(CONFIG_MODULES)	+= mod_devicetable.o module.o
 endif
 obj-$(CONFIG_SYS_HYPERVISOR) += hypervisor.o
 obj-$(CONFIG_REGMAP)	+= regmap/
diff --git a/drivers/base/base.h b/drivers/base/base.h
index b902d1ecc247f..beaa252c04388 100644
--- a/drivers/base/base.h
+++ b/drivers/base/base.h
@@ -173,6 +173,14 @@  static inline void module_add_driver(struct module *mod,
 static inline void module_remove_driver(struct device_driver *drv) { }
 #endif
 
+#if defined(CONFIG_SYSFS)
+ssize_t usb_drv_to_modalias(struct device_driver *drv, char *buf,
+			    size_t count);
+#else
+static inline ssize_t usb_drv_to_modalias(struct device_driver *drv, char *buf,
+					  size_t count) { return -EINVAL; }
+#endif
+
 #ifdef CONFIG_DEVTMPFS
 extern int devtmpfs_init(void);
 #else
diff --git a/drivers/base/bus.c b/drivers/base/bus.c
index 7ca47e5b3c1f4..4e0c5925545e5 100644
--- a/drivers/base/bus.c
+++ b/drivers/base/bus.c
@@ -178,6 +178,48 @@  static const struct kset_uevent_ops bus_uevent_ops = {
 
 static struct kset *bus_kset;
 
+/**
+ * bus_for_each - bus iterator.
+ * @start: bus to start iterating from.
+ * @data: data for the callback.
+ * @fn: function to be called for each device.
+ *
+ * Iterate over list of buses, and call @fn for each,
+ * passing it @data. If @start is not NULL, we use that bus to
+ * begin iterating from.
+ *
+ * We check the return of @fn each time. If it returns anything
+ * other than 0, we break out and return that value.
+ *
+ * NOTE: The bus that returns a non-zero value is not retained
+ * in any way, nor is its refcount incremented. If the caller needs
+ * to retain this data, it should do so, and increment the reference
+ * count in the supplied callback.
+ */
+int bus_for_each(void *data, int (*fn)(struct bus_type *, void *))
+{
+	int error = 0;
+	struct bus_type *bus;
+	struct subsys_private *bus_prv;
+	struct kset *subsys;
+	struct kobject *k;
+
+	spin_lock(&bus_kset->list_lock);
+
+	list_for_each_entry(k, &bus_kset->list, entry) {
+		subsys = container_of(k, struct kset, kobj);
+		bus_prv = container_of(subsys, struct subsys_private, subsys);
+		bus = bus_prv->bus;
+		error = fn(bus, data);
+		if (error)
+			break;
+	}
+
+	spin_unlock(&bus_kset->list_lock);
+	return error;
+}
+EXPORT_SYMBOL_GPL(bus_for_each);
+
 /* Manually detach a device from its associated driver. */
 static ssize_t unbind_store(struct device_driver *drv, const char *buf,
 			    size_t count)
diff --git a/drivers/base/mod_devicetable.c b/drivers/base/mod_devicetable.c
new file mode 100644
index 0000000000000..b04442207668c
--- /dev/null
+++ b/drivers/base/mod_devicetable.c
@@ -0,0 +1,247 @@ 
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * mod_devicetable.c - helpers for displaying modaliases through sysfs.
+ *
+ * This borrows a lot from file2alias.c
+ */
+
+#include <linux/device/bus.h>
+#include <linux/device.h>
+#include <linux/usb.h>
+
+#include "base.h"
+#include "../usb/core/usb.h"
+
+#define ADD(buf, count, len, sep, cond, field)				\
+do {									\
+	if (cond)							\
+		(len) += scnprintf(&(buf)[len],				\
+			(count) - (len),				\
+			sizeof(field) == 1 ? (sep "%02X") :		\
+			sizeof(field) == 2 ? (sep "%04X") :		\
+			sizeof(field) == 4 ? (sep "%08X") : "",		\
+			(field));					\
+	else								\
+		(len) += scnprintf(&(buf)[len], (count) - (len), (sep "*")); \
+} while (0)
+
+#ifdef CONFIG_USB
+/* USB related modaliases can be split because of device number matching, so
+ * this function handles individual modaliases for one segment of the range.
+ *
+ *
+ */
+static ssize_t usb_id_to_modalias(const struct usb_device_id *id,
+				  unsigned int bcdDevice_initial,
+				  int bcdDevice_initial_digits,
+				  unsigned char range_lo,
+				  unsigned char range_hi,
+				  unsigned char max, const char *mod_name,
+				  char *buf, size_t count)
+{
+	ssize_t len = 0;
+
+	ADD(buf, count, len, "alias usb:v",
+	    id->match_flags & USB_DEVICE_ID_MATCH_VENDOR, id->idVendor);
+	ADD(buf, count, len, "p", id->match_flags & USB_DEVICE_ID_MATCH_PRODUCT,
+	    id->idProduct);
+
+	len += scnprintf(&buf[len], count - len, "d");
+	if (bcdDevice_initial_digits)
+		len += scnprintf(&buf[len], count - len, "%0*X",
+			bcdDevice_initial_digits, bcdDevice_initial);
+	if (range_lo == range_hi) {
+		len += scnprintf(&buf[len], count - len, "%X", range_lo);
+	} else if (range_lo > 0 || range_hi < max) {
+		if (range_lo > 0x9 || range_hi < 0xA) {
+			len += scnprintf(&buf[len], count - len, "[%X-%X]",
+					 range_lo, range_hi);
+		} else {
+			len += scnprintf(&buf[len], count - len,
+				range_lo < 0x9 ? "[%X-9" : "[%X",
+				range_lo);
+			len += scnprintf(&buf[len], count - len,
+				range_hi > 0xA ? "A-%X]" : "%X]",
+				range_hi);
+		}
+	}
+	if (bcdDevice_initial_digits < (sizeof(id->bcdDevice_lo) * 2 - 1))
+		len += scnprintf(&buf[len], count - len, "*");
+
+	ADD(buf, count, len, "dc",
+	    id->match_flags & USB_DEVICE_ID_MATCH_DEV_CLASS, id->bDeviceClass);
+	ADD(buf, count, len, "dsc",
+	    id->match_flags & USB_DEVICE_ID_MATCH_DEV_SUBCLASS,
+	    id->bDeviceSubClass);
+	ADD(buf, count, len, "dp",
+	    id->match_flags & USB_DEVICE_ID_MATCH_DEV_PROTOCOL,
+	    id->bDeviceProtocol);
+	ADD(buf, count, len, "ic",
+	    id->match_flags & USB_DEVICE_ID_MATCH_INT_CLASS,
+	    id->bInterfaceClass);
+	ADD(buf, count, len, "isc",
+	    id->match_flags & USB_DEVICE_ID_MATCH_INT_SUBCLASS,
+	    id->bInterfaceSubClass);
+	ADD(buf, count, len, "ip",
+	    id->match_flags & USB_DEVICE_ID_MATCH_INT_PROTOCOL,
+	    id->bInterfaceProtocol);
+	ADD(buf, count, len, "in",
+	    id->match_flags & USB_DEVICE_ID_MATCH_INT_NUMBER,
+	    id->bInterfaceNumber);
+
+	len += scnprintf(&buf[len], count - len, " %s\n", mod_name);
+	return len;
+}
+
+/* Handles increment/decrement of BCD formatted integers */
+/* Returns the previous value, so it works like i++ or i-- */
+static unsigned int incbcd(unsigned int *bcd,
+			   int inc,
+			   unsigned char max,
+			   size_t chars)
+{
+	unsigned int init = *bcd, i, j;
+	unsigned long long c, dec = 0;
+
+	/* If bcd is not in BCD format, just increment */
+	if (max > 0x9) {
+		*bcd += inc;
+		return init;
+	}
+
+	/* Convert BCD to Decimal */
+	for (i = 0 ; i < chars ; i++) {
+		c = (*bcd >> (i << 2)) & 0xf;
+		c = c > 9 ? 9 : c; /* force to bcd just in case */
+		for (j = 0 ; j < i ; j++)
+			c = c * 10;
+		dec += c;
+	}
+
+	/* Do our increment/decrement */
+	dec += inc;
+	*bcd  = 0;
+
+	/* Convert back to BCD */
+	for (i = 0 ; i < chars ; i++) {
+		for (c = 1, j = 0 ; j < i ; j++)
+			c = c * 10;
+		c = (dec / c) % 10;
+		*bcd += c << (i << 2);
+	}
+	return init;
+}
+
+/* Print the modaliases for the specified struct usb_device_id.
+ */
+static ssize_t usb_id_to_modalias_multi(const struct usb_device_id *id,
+					const char *mod_name, char *buf,
+					size_t count)
+{
+	ssize_t len = 0;
+	unsigned int devlo, devhi;
+	unsigned char chi, clo, max;
+	int ndigits;
+
+	devlo = id->match_flags & USB_DEVICE_ID_MATCH_DEV_LO ?
+		id->bcdDevice_lo : 0x0U;
+	devhi = id->match_flags & USB_DEVICE_ID_MATCH_DEV_HI ?
+		id->bcdDevice_hi : ~0x0U;
+
+	/* Figure out if this entry is in bcd or hex format */
+	max = 0x9; /* Default to decimal format */
+	for (ndigits = 0 ; ndigits < sizeof(id->bcdDevice_lo) * 2 ; ndigits++) {
+		clo = (devlo >> (ndigits << 2)) & 0xf;
+		chi = ((devhi > 0x9999 ? 0x9999 : devhi) >>
+		       (ndigits << 2)) & 0xf;
+		if (clo > max || chi > max) {
+			max = 0xf;
+			break;
+		}
+	}
+
+	/*
+	 * Some modules (visor) have empty slots as placeholder for
+	 * run-time specification that results in catch-all alias
+	 */
+	if (!(id->idVendor || id->idProduct || id->bDeviceClass ||
+	      id->bInterfaceClass))
+		return len;
+
+	/* Convert numeric bcdDevice range into fnmatch-able pattern(s) */
+	for (ndigits = sizeof(id->bcdDevice_lo) * 2 - 1; devlo <= devhi;
+	     ndigits--) {
+		clo = devlo & 0xf;
+		chi = devhi & 0xf;
+		/* If we are in bcd mode, truncate if necessary */
+		if (chi > max)
+			chi = max;
+		devlo >>= 4;
+		devhi >>= 4;
+
+		if (devlo == devhi || !ndigits) {
+			len += usb_id_to_modalias(id, devlo, ndigits, clo, chi,
+						  max, mod_name, buf + len,
+						  count - len);
+			break;
+		}
+
+		if (clo > 0x0)
+			len += usb_id_to_modalias(id,
+				incbcd(&devlo, 1, max,
+				       sizeof(id->bcdDevice_lo) * 2),
+				ndigits, clo, max, max, mod_name, buf + len,
+				count - len);
+
+		if (chi < max)
+			len += usb_id_to_modalias(id,
+				incbcd(&devhi, -1, max,
+				       sizeof(id->bcdDevice_lo) * 2),
+				ndigits, 0x0, chi, max, mod_name, buf + len,
+				count - len);
+	}
+	return len;
+}
+
+/* Print the modaliases for the given driver assumed to be an usb_driver or
+ * usb_device_driver.
+ *
+ * "alias" is prepended and the module name is appended to each modalias to
+ * match the format in modules.aliases.
+ *
+ * The modaliases will be written out to @buf with @count being the maximum
+ * bytes to write. The return value is a negative errno on error or the number
+ * of bytes written to @buf on success.
+ */
+ssize_t usb_drv_to_modalias(struct device_driver *drv, char *buf,
+			    size_t count)
+{
+	ssize_t len = 0;
+	const struct usb_device_id *id;
+	const char *mod_name;
+
+	if (drv->bus != &usb_bus_type)
+		return -EINVAL;
+
+	if (drv->owner)
+		mod_name = drv->owner->name;
+	else
+		mod_name = drv->mod_name;
+
+	if (is_usb_device_driver(drv))
+		id = to_usb_device_driver(drv)->id_table;
+	else
+		id = to_usb_driver(drv)->id_table;
+	if (!id)
+		return len;
+
+	for (; id->match_flags; id++) {
+		len += usb_id_to_modalias_multi(id, mod_name, buf + len,
+						count - len);
+	}
+	return len;
+}
+#else
+inline ssize_t usb_drv_to_modalias(struct device_driver *drv, char *buf,
+				   size_t count){ return 0; }
+#endif
diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c
index 7e7e119c253fb..fdbc197b64c9c 100644
--- a/drivers/usb/core/driver.c
+++ b/drivers/usb/core/driver.c
@@ -32,6 +32,7 @@ 
 #include <linux/usb/quirks.h>
 #include <linux/usb/hcd.h>
 
+#include "../../base/base.h"
 #include "usb.h"
 
 
@@ -2030,4 +2031,5 @@  struct bus_type usb_bus_type = {
 	.match =	usb_device_match,
 	.uevent =	usb_uevent,
 	.need_parent_lock =	true,
+	.drv_to_modalias = usb_drv_to_modalias,
 };
diff --git a/include/linux/device/bus.h b/include/linux/device/bus.h
index d8b29ccd07e56..cce0bedec63d9 100644
--- a/include/linux/device/bus.h
+++ b/include/linux/device/bus.h
@@ -61,6 +61,10 @@  struct fwnode_handle;
  *			this bus.
  * @dma_cleanup:	Called to cleanup DMA configuration on a device on
  *			this bus.
+ * @drv_to_modalias:    Called to convert the matching IDs in a
+ *                      struct device_driver to their corresponding modaliases.
+ *                      Note that the struct device_driver is expected to belong
+ *                      to this bus.
  * @pm:		Power management operations of this bus, callback the specific
  *		device driver's pm-ops.
  * @iommu_ops:  IOMMU specific operations for this bus, used to attach IOMMU
@@ -107,6 +111,9 @@  struct bus_type {
 	int (*dma_configure)(struct device *dev);
 	void (*dma_cleanup)(struct device *dev);
 
+	ssize_t (*drv_to_modalias)(struct device_driver *drv, char *buf,
+				   size_t count);
+
 	const struct dev_pm_ops *pm;
 
 	const struct iommu_ops *iommu_ops;
@@ -161,6 +168,7 @@  void subsys_dev_iter_init(struct subsys_dev_iter *iter,
 struct device *subsys_dev_iter_next(struct subsys_dev_iter *iter);
 void subsys_dev_iter_exit(struct subsys_dev_iter *iter);
 
+int bus_for_each(void *data, int (*fn)(struct bus_type *, void *));
 int bus_for_each_dev(struct bus_type *bus, struct device *start, void *data,
 		     int (*fn)(struct device *dev, void *data));
 struct device *bus_find_device(struct bus_type *bus, struct device *start,
diff --git a/include/linux/module.h b/include/linux/module.h
index ec61fb53979a9..0bfa859a21566 100644
--- a/include/linux/module.h
+++ b/include/linux/module.h
@@ -47,6 +47,7 @@  struct module_kobject {
 	struct kobject *drivers_dir;
 	struct module_param_attrs *mp;
 	struct completion *kobj_completion;
+	struct bin_attribute modalias_attr;
 } __randomize_layout;
 
 struct module_attribute {
diff --git a/kernel/module/internal.h b/kernel/module/internal.h
index 2e2bf236f5582..8d7ae37584868 100644
--- a/kernel/module/internal.h
+++ b/kernel/module/internal.h
@@ -259,11 +259,13 @@  static inline void add_kallsyms(struct module *mod, const struct load_info *info
 #endif /* CONFIG_KALLSYMS */
 
 #ifdef CONFIG_SYSFS
+void add_modalias_attr(struct module_kobject *mk);
 int mod_sysfs_setup(struct module *mod, const struct load_info *info,
 		    struct kernel_param *kparam, unsigned int num_params);
 void mod_sysfs_teardown(struct module *mod);
 void init_param_lock(struct module *mod);
 #else /* !CONFIG_SYSFS */
+static inline void add_modalias_attr(struct module_kobject *mk) {}
 static inline int mod_sysfs_setup(struct module *mod,
 			   	  const struct load_info *info,
 			   	  struct kernel_param *kparam,
diff --git a/kernel/module/sysfs.c b/kernel/module/sysfs.c
index ce68f821dcd12..e80bfa4639765 100644
--- a/kernel/module/sysfs.c
+++ b/kernel/module/sysfs.c
@@ -5,6 +5,8 @@ 
  * Copyright (C) 2008 Rusty Russell
  */
 
+#include <linux/device/bus.h>
+#include <linux/device/driver.h>
 #include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/fs.h>
@@ -240,6 +242,102 @@  static inline void add_notes_attrs(struct module *mod, const struct load_info *i
 static inline void remove_notes_attrs(struct module *mod) { }
 #endif /* CONFIG_KALLSYMS */
 
+/* Track of the buffer and module identity in callbacks when walking the list of
+ * drivers for each bus.
+ */
+struct modalias_bus_print_state {
+	struct module_kobject *mk;
+	char *buf;
+	size_t count;
+	ssize_t len;
+};
+
+static int print_modalias_for_drv(struct device_driver *drv, void *p)
+{
+	struct modalias_bus_print_state *s = p;
+	struct module_kobject *mk = s->mk;
+	ssize_t len;
+	/* Skip drivers that do not match this module. */
+	if (mk->mod) {
+		if (mk->mod != drv->owner)
+			return 0;
+	} else if (!mk->kobj.name || !drv->mod_name ||
+		   strcmp(mk->kobj.name, drv->mod_name))
+		return 0;
+
+	if (drv->bus && drv->bus->drv_to_modalias) {
+		len = drv->bus->drv_to_modalias(drv, s->buf + s->len,
+						s->count - s->len);
+		if (len < 0)
+			return len;
+		s->len += len;
+	}
+
+	s->len += scnprintf(&s->buf[s->len], s->count - s->len, "driver %s\n",
+			    drv->name);
+	return 0;
+}
+
+static int print_modalias_for_bus(struct bus_type *type, void *p)
+{
+	return bus_for_each_drv(type, NULL, p, print_modalias_for_drv);
+}
+
+static ssize_t module_modalias_read(struct file *filp, struct kobject *kobj,
+				    struct bin_attribute *bin_attr,
+				    char *buf, loff_t pos, size_t count)
+{
+	struct module_kobject *mk = container_of(kobj, struct module_kobject,
+						 kobj);
+	struct modalias_bus_print_state state = {mk, buf, count, 0};
+	int error = 0;
+
+	if (pos != 0)
+		return -EINVAL;
+
+	error = bus_for_each(&state, print_modalias_for_bus);
+	if (error)
+		return error;
+
+	if (mk->mod)
+		state.len += scnprintf(&buf[state.len], count - state.len,
+				       "modalias %s %s\n", kobject_name(kobj),
+				       mk->mod->name);
+	else
+		state.len += scnprintf(&buf[state.len], count - state.len,
+				       "modalias %s NULL\n",
+				       kobject_name(kobj));
+
+	/*
+	 * The caller checked the pos and count against our size.
+	 */
+	return state.len;
+}
+
+/* Used in kernel/params.c for builtin modules.
+ *
+ * `struct module_kobject` is used instead of `struct module` because for
+ * builtin modules, the `struct module` is not available when this is called.
+ */
+void add_modalias_attr(struct module_kobject *mk)
+{
+	sysfs_bin_attr_init(&mk->modalias_attr);
+	mk->modalias_attr.attr.name = "modalias";
+	mk->modalias_attr.attr.mode = 0444;
+	mk->modalias_attr.read = module_modalias_read;
+	if (sysfs_create_bin_file(&mk->kobj, &mk->modalias_attr)) {
+		/* We shouldn't ignore the return type, but there is nothing to
+		 * do.
+		 */
+		return;
+	}
+}
+
+static void remove_modalias_attr(struct module_kobject *mk)
+{
+	sysfs_remove_bin_file(&mk->kobj, &mk->modalias_attr);
+}
+
 static void del_usage_links(struct module *mod)
 {
 #ifdef CONFIG_MODULE_UNLOAD
@@ -398,6 +496,7 @@  int mod_sysfs_setup(struct module *mod,
 
 	add_sect_attrs(mod, info);
 	add_notes_attrs(mod, info);
+	add_modalias_attr(&mod->mkobj);
 
 	return 0;
 
@@ -415,6 +514,7 @@  int mod_sysfs_setup(struct module *mod,
 
 static void mod_sysfs_fini(struct module *mod)
 {
+	remove_modalias_attr(&mod->mkobj);
 	remove_notes_attrs(mod);
 	remove_sect_attrs(mod);
 	mod_kobject_put(mod);
diff --git a/kernel/params.c b/kernel/params.c
index 5b92310425c50..111024196361a 100644
--- a/kernel/params.c
+++ b/kernel/params.c
@@ -13,6 +13,7 @@ 
 #include <linux/slab.h>
 #include <linux/ctype.h>
 #include <linux/security.h>
+#include "module/internal.h"
 
 #ifdef CONFIG_SYSFS
 /* Protects all built-in parameters, modules use their own param_lock */
@@ -815,6 +816,7 @@  static void __init kernel_add_sysfs_param(const char *name,
 	BUG_ON(err);
 	kobject_uevent(&mk->kobj, KOBJ_ADD);
 	kobject_put(&mk->kobj);
+	add_modalias_attr(mk);
 }
 
 /*