forked from torvalds/linux
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
modules: add modalias file to sysfs for modules.
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>
- Loading branch information
1 parent
2f465b9
commit 6dda398
Showing
10 changed files
with
407 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,241 @@ | ||
// 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 "../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) | ||
|
||
/* 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; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.