Skip to content
Navigation Menu
Toggle navigation
Sign in
In this repository
All GitHub Enterprise
↵
Jump to
↵
No suggested jump to results
In this repository
All GitHub Enterprise
↵
Jump to
↵
In this organization
All GitHub Enterprise
↵
Jump to
↵
In this repository
All GitHub Enterprise
↵
Jump to
↵
Sign in
Reseting focus
You signed in with another tab or window.
Reload
to refresh your session.
You signed out in another tab or window.
Reload
to refresh your session.
You switched accounts on another tab or window.
Reload
to refresh your session.
Dismiss alert
{{ message }}
mariux64
/
linux
Public
Notifications
You must be signed in to change notification settings
Fork
0
Star
0
Code
Issues
2
Pull requests
0
Actions
Projects
0
Wiki
Security
Insights
Additional navigation options
Code
Issues
Pull requests
Actions
Projects
Wiki
Security
Insights
Files
8541bf0
Documentation
LICENSES
arch
block
certs
crypto
drivers
fs
include
init
io_uring
ipc
kernel
lib
mm
net
rust
samples
scripts
atomic
basic
clang-tools
coccinelle
dtc
dummy-tools
gcc-plugins
gdb
genksyms
include
ipe
kconfig
ksymoops
mod
.gitignore
Makefile
devicetable-offsets.c
empty.c
file2alias.c
mk_elfconfig.c
modpost.c
modpost.h
sumversion.c
symsearch.c
package
selinux
tracing
.gitignore
Kbuild.include
Kconfig.include
Lindent
Makefile
Makefile.asm-headers
Makefile.autofdo
Makefile.btf
Makefile.build
Makefile.clang
Makefile.clean
Makefile.compiler
Makefile.debug
Makefile.defconf
Makefile.dtbinst
Makefile.dtbs
Makefile.extrawarn
Makefile.gcc-plugins
Makefile.headersinst
Makefile.host
Makefile.kasan
Makefile.kcov
Makefile.kcsan
Makefile.kmsan
Makefile.lib
Makefile.modfinal
Makefile.modinst
Makefile.modpost
Makefile.package
Makefile.propeller
Makefile.randstruct
Makefile.ubsan
Makefile.userprogs
Makefile.vdsoinst
Makefile.vmlinux
Makefile.vmlinux_o
as-version.sh
asn1_compiler.c
bloat-o-meter
bootgraph.pl
bpf_doc.py
build-version
cc-can-link.sh
cc-version.sh
check-git
check-sysctl-docs
check-uapi.sh
check-variable-fonts.sh
check_extable.sh
checkdeclares.pl
checkincludes.pl
checkkconfigsymbols.py
checkpatch.pl
checkstack.pl
checksyscalls.sh
checktransupdate.py
checkversion.pl
cleanfile
cleanpatch
coccicheck
config
const_structs.checkpatch
decode_stacktrace.sh
decodecode
depmod.sh
dev-needs.sh
diffconfig
documentation-file-ref-check
extract-ikconfig
extract-module-sig.pl
extract-sys-certs.pl
extract-vmlinux
extract_xc3028.pl
faddr2line
file-size.sh
find-unused-docs.sh
gcc-x86_32-has-stack-protector.sh
gcc-x86_64-has-stack-protector.sh
gen-randstruct-seed.sh
generate_builtin_ranges.awk
generate_initcall_order.pl
generate_rust_analyzer.py
generate_rust_target.rs
get_abi.pl
get_dvb_firmware
get_feat.pl
get_maintainer.pl
gfp-translate
git.orderFile
head-object-list.txt
headerdep.pl
headers_install.sh
insert-sys-cert.c
install.sh
jobserver-exec
kallsyms.c
kernel-doc
ld-version.sh
leaking_addresses.pl
link-vmlinux.sh
macro_checker.py
make_fit.py
makelst
markup_oops.pl
min-tool-version.sh
misc-check
mkcompile_h
mksysmap
mkuboot.sh
module-common.c
module.lds.S
modules-check.sh
nsdeps
objdiff
objdump-func
orc_hash.sh
pahole-version.sh
parse-maintainers.pl
patch-kernel
profile2linkerlist.pl
prune-kernel
recordmcount.c
recordmcount.h
recordmcount.pl
relocs_check.sh
remove-stale-files
rust_is_available.sh
rust_is_available_bindgen_0_66.h
rust_is_available_bindgen_libclang.h
rust_is_available_bindgen_libclang_concat.h
rust_is_available_test.py
rustc-llvm-version.sh
rustc-version.sh
rustdoc_test_builder.rs
rustdoc_test_gen.rs
setlocalversion
show_delta
sign-file.c
sorttable.c
sorttable.h
spdxcheck-test.sh
spdxcheck.py
spdxexclude
spelling.txt
sphinx-pre-install
split-man.pl
ssl-common.h
stackdelta
stackusage
subarch.include
syscall.tbl
syscallhdr.sh
syscallnr.sh
syscalltbl.sh
tags.sh
tools-support-relr.sh
unifdef.c
ver_linux
verify_builtin_ranges.awk
xen-hypercalls.sh
xz_wrap.sh
security
sound
tools
usr
virt
.clang-format
.clippy.toml
.cocciconfig
.editorconfig
.get_maintainer.ignore
.gitattributes
.gitignore
.mailmap
.rustfmt.toml
COPYING
CREDITS
Kbuild
Kconfig
MAINTAINERS
Makefile
README
Breadcrumbs
linux
/
scripts
/
mod
/
file2alias.c
Copy path
Blame
Blame
Latest commit
History
History
1523 lines (1268 loc) · 44.1 KB
Breadcrumbs
linux
/
scripts
/
mod
/
file2alias.c
Top
File metadata and controls
Code
Blame
1523 lines (1268 loc) · 44.1 KB
Raw
/* Simple code to turn various tables in an ELF file into alias definitions. * This deals with kernel datastructures where they should be * dealt with: in the kernel source. * * Copyright 2002-2003 Rusty Russell, IBM Corporation * 2003 Kai Germaschewski * * * This software may be used and distributed according to the terms * of the GNU General Public License, incorporated herein by reference. */ #include <stdarg.h> #include <stdio.h> #include "list.h" #include "xalloc.h" #include "modpost.h" #include "devicetable-offsets.h" /* We use the ELF typedefs for kernel_ulong_t but bite the bullet and * use either stdint.h or inttypes.h for the rest. */ #if KERNEL_ELFCLASS == ELFCLASS32 typedef Elf32_Addr kernel_ulong_t; #define BITS_PER_LONG 32 #else typedef Elf64_Addr kernel_ulong_t; #define BITS_PER_LONG 64 #endif #ifdef __sun__ #include <inttypes.h> #else #include <stdint.h> #endif #include <ctype.h> #include <stdbool.h> /** * module_alias_printf - add auto-generated MODULE_ALIAS() * * @mod: module * @append_wildcard: append '*' for future extension if not exist yet * @fmt: printf(3)-like format */ static void __attribute__((format (printf, 3, 4))) module_alias_printf(struct module *mod, bool append_wildcard, const char *fmt, ...) { struct module_alias *new, *als; size_t len; int n; va_list ap; /* Determine required size. */ va_start(ap, fmt); n = vsnprintf(NULL, 0, fmt, ap); va_end(ap); if (n < 0) { error("vsnprintf failed\n"); return; } len = n + 1; /* extra byte for '\0' */ if (append_wildcard) len++; /* extra byte for '*' */ new = xmalloc(sizeof(*new) + len); /* Now, really print it to the allocated buffer */ va_start(ap, fmt); n = vsnprintf(new->str, len, fmt, ap); va_end(ap); if (n < 0) { error("vsnprintf failed\n"); free(new); return; } if (append_wildcard && (n == 0 || new->str[n - 1] != '*')) { new->str[n] = '*'; new->str[n + 1] = '\0'; } /* avoid duplication */ list_for_each_entry(als, &mod->aliases, node) { if (!strcmp(als->str, new->str)) { free(new); return; } } list_add_tail(&new->node, &mod->aliases); } typedef uint32_t __u32; typedef uint16_t __u16; typedef unsigned char __u8; /* UUID types for backward compatibility, don't use in new code */ typedef struct { __u8 b[16]; } guid_t; typedef struct { __u8 b[16]; } uuid_t; #define UUID_STRING_LEN 36 /* MEI UUID type, don't use anywhere else */ typedef struct { __u8 b[16]; } uuid_le; /* Big exception to the "don't include kernel headers into userspace, which * even potentially has different endianness and word sizes, since * we handle those differences explicitly below */ #include "../../include/linux/mod_devicetable.h" struct devtable { const char *device_id; unsigned long id_size; void (*do_entry)(struct module *mod, void *symval); }; /* Define a variable f that holds the value of field f of struct devid * based at address m. */ #define DEF_FIELD(m, devid, f) \ typeof(((struct devid *)0)->f) f = TO_NATIVE(*(typeof(f) *)((m) + OFF_##devid##_##f)) /* Define a variable f that holds the address of field f of struct devid * based at address m. Due to the way typeof works, for a field of type * T[N] the variable has type T(*)[N], _not_ T*. */ #define DEF_FIELD_ADDR(m, devid, f) \ typeof(((struct devid *)0)->f) *f = ((m) + OFF_##devid##_##f) #define ADD(str, sep, cond, field) \ do { \ strcat(str, sep); \ if (cond) \ sprintf(str + strlen(str), \ sizeof(field) == 1 ? "%02X" : \ sizeof(field) == 2 ? "%04X" : \ sizeof(field) == 4 ? "%08X" : "", \ field); \ else \ sprintf(str + strlen(str), "*"); \ } while(0) static inline void add_uuid(char *str, uuid_le uuid) { int len = strlen(str); sprintf(str + len, "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x", uuid.b[3], uuid.b[2], uuid.b[1], uuid.b[0], uuid.b[5], uuid.b[4], uuid.b[7], uuid.b[6], uuid.b[8], uuid.b[9], uuid.b[10], uuid.b[11], uuid.b[12], uuid.b[13], uuid.b[14], uuid.b[15]); } static inline void add_guid(char *str, guid_t guid) { int len = strlen(str); sprintf(str + len, "%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X", guid.b[3], guid.b[2], guid.b[1], guid.b[0], guid.b[5], guid.b[4], guid.b[7], guid.b[6], guid.b[8], guid.b[9], guid.b[10], guid.b[11], guid.b[12], guid.b[13], guid.b[14], guid.b[15]); } /* USB is special because the bcdDevice can be matched against a numeric range */ /* Looks like "usb:vNpNdNdcNdscNdpNicNiscNipNinN" */ static void do_usb_entry(void *symval, unsigned int bcdDevice_initial, int bcdDevice_initial_digits, unsigned char range_lo, unsigned char range_hi, unsigned char max, struct module *mod) { char alias[500]; DEF_FIELD(symval, usb_device_id, match_flags); DEF_FIELD(symval, usb_device_id, idVendor); DEF_FIELD(symval, usb_device_id, idProduct); DEF_FIELD(symval, usb_device_id, bcdDevice_lo); DEF_FIELD(symval, usb_device_id, bDeviceClass); DEF_FIELD(symval, usb_device_id, bDeviceSubClass); DEF_FIELD(symval, usb_device_id, bDeviceProtocol); DEF_FIELD(symval, usb_device_id, bInterfaceClass); DEF_FIELD(symval, usb_device_id, bInterfaceSubClass); DEF_FIELD(symval, usb_device_id, bInterfaceProtocol); DEF_FIELD(symval, usb_device_id, bInterfaceNumber); strcpy(alias, "usb:"); ADD(alias, "v", match_flags&USB_DEVICE_ID_MATCH_VENDOR, idVendor); ADD(alias, "p", match_flags&USB_DEVICE_ID_MATCH_PRODUCT, idProduct); strcat(alias, "d"); if (bcdDevice_initial_digits) sprintf(alias + strlen(alias), "%0*X", bcdDevice_initial_digits, bcdDevice_initial); if (range_lo == range_hi) sprintf(alias + strlen(alias), "%X", range_lo); else if (range_lo > 0 || range_hi < max) { if (range_lo > 0x9 || range_hi < 0xA) sprintf(alias + strlen(alias), "[%X-%X]", range_lo, range_hi); else { sprintf(alias + strlen(alias), range_lo < 0x9 ? "[%X-9" : "[%X", range_lo); sprintf(alias + strlen(alias), range_hi > 0xA ? "A-%X]" : "%X]", range_hi); } } if (bcdDevice_initial_digits < (sizeof(bcdDevice_lo) * 2 - 1)) strcat(alias, "*"); ADD(alias, "dc", match_flags&USB_DEVICE_ID_MATCH_DEV_CLASS, bDeviceClass); ADD(alias, "dsc", match_flags&USB_DEVICE_ID_MATCH_DEV_SUBCLASS, bDeviceSubClass); ADD(alias, "dp", match_flags&USB_DEVICE_ID_MATCH_DEV_PROTOCOL, bDeviceProtocol); ADD(alias, "ic", match_flags&USB_DEVICE_ID_MATCH_INT_CLASS, bInterfaceClass); ADD(alias, "isc", match_flags&USB_DEVICE_ID_MATCH_INT_SUBCLASS, bInterfaceSubClass); ADD(alias, "ip", match_flags&USB_DEVICE_ID_MATCH_INT_PROTOCOL, bInterfaceProtocol); ADD(alias, "in", match_flags&USB_DEVICE_ID_MATCH_INT_NUMBER, bInterfaceNumber); module_alias_printf(mod, true, "%s", alias); } /* 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; } static void do_usb_entry_multi(struct module *mod, void *symval) { unsigned int devlo, devhi; unsigned char chi, clo, max; int ndigits; DEF_FIELD(symval, usb_device_id, match_flags); DEF_FIELD(symval, usb_device_id, idVendor); DEF_FIELD(symval, usb_device_id, idProduct); DEF_FIELD(symval, usb_device_id, bcdDevice_lo); DEF_FIELD(symval, usb_device_id, bcdDevice_hi); DEF_FIELD(symval, usb_device_id, bDeviceClass); DEF_FIELD(symval, usb_device_id, bInterfaceClass); devlo = match_flags & USB_DEVICE_ID_MATCH_DEV_LO ? bcdDevice_lo : 0x0U; devhi = match_flags & USB_DEVICE_ID_MATCH_DEV_HI ? 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(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 (!(idVendor | idProduct | bDeviceClass | bInterfaceClass)) return; /* Convert numeric bcdDevice range into fnmatch-able pattern(s) */ for (ndigits = sizeof(bcdDevice_lo) * 2 - 1; devlo <= devhi; ndigits--) { clo = devlo & 0xf; chi = devhi & 0xf; if (chi > max) /* If we are in bcd mode, truncate if necessary */ chi = max; devlo >>= 4; devhi >>= 4; if (devlo == devhi || !ndigits) { do_usb_entry(symval, devlo, ndigits, clo, chi, max, mod); break; } if (clo > 0x0) do_usb_entry(symval, incbcd(&devlo, 1, max, sizeof(bcdDevice_lo) * 2), ndigits, clo, max, max, mod); if (chi < max) do_usb_entry(symval, incbcd(&devhi, -1, max, sizeof(bcdDevice_lo) * 2), ndigits, 0x0, chi, max, mod); } } static void do_of_entry(struct module *mod, void *symval) { char alias[500]; int len; char *tmp; DEF_FIELD_ADDR(symval, of_device_id, name); DEF_FIELD_ADDR(symval, of_device_id, type); DEF_FIELD_ADDR(symval, of_device_id, compatible); len = sprintf(alias, "of:N%sT%s", (*name)[0] ? *name : "*", (*type)[0] ? *type : "*"); if ((*compatible)[0]) sprintf(&alias[len], "%sC%s", (*type)[0] ? "*" : "", *compatible); /* Replace all whitespace with underscores */ for (tmp = alias; tmp && *tmp; tmp++) if (isspace(*tmp)) *tmp = '_'; module_alias_printf(mod, false, "%s", alias); module_alias_printf(mod, false, "%sC*", alias); } /* Looks like: hid:bNvNpN */ static void do_hid_entry(struct module *mod, void *symval) { char alias[256] = {}; DEF_FIELD(symval, hid_device_id, bus); DEF_FIELD(symval, hid_device_id, group); DEF_FIELD(symval, hid_device_id, vendor); DEF_FIELD(symval, hid_device_id, product); ADD(alias, "b", bus != HID_BUS_ANY, bus); ADD(alias, "g", group != HID_GROUP_ANY, group); ADD(alias, "v", vendor != HID_ANY_ID, vendor); ADD(alias, "p", product != HID_ANY_ID, product); module_alias_printf(mod, false, "hid:%s", alias); } /* Looks like: ieee1394:venNmoNspNverN */ static void do_ieee1394_entry(struct module *mod, void *symval) { char alias[256] = {}; DEF_FIELD(symval, ieee1394_device_id, match_flags); DEF_FIELD(symval, ieee1394_device_id, vendor_id); DEF_FIELD(symval, ieee1394_device_id, model_id); DEF_FIELD(symval, ieee1394_device_id, specifier_id); DEF_FIELD(symval, ieee1394_device_id, version); ADD(alias, "ven", match_flags & IEEE1394_MATCH_VENDOR_ID, vendor_id); ADD(alias, "mo", match_flags & IEEE1394_MATCH_MODEL_ID, model_id); ADD(alias, "sp", match_flags & IEEE1394_MATCH_SPECIFIER_ID, specifier_id); ADD(alias, "ver", match_flags & IEEE1394_MATCH_VERSION, version); module_alias_printf(mod, true, "ieee1394:%s", alias); } /* Looks like: pci:vNdNsvNsdNbcNscNiN or <prefix>_pci:vNdNsvNsdNbcNscNiN. */ static void do_pci_entry(struct module *mod, void *symval) { char alias[256]; /* Class field can be divided into these three. */ unsigned char baseclass, subclass, interface, baseclass_mask, subclass_mask, interface_mask; DEF_FIELD(symval, pci_device_id, vendor); DEF_FIELD(symval, pci_device_id, device); DEF_FIELD(symval, pci_device_id, subvendor); DEF_FIELD(symval, pci_device_id, subdevice); DEF_FIELD(symval, pci_device_id, class); DEF_FIELD(symval, pci_device_id, class_mask); DEF_FIELD(symval, pci_device_id, override_only); switch (override_only) { case 0: strcpy(alias, "pci:"); break; case PCI_ID_F_VFIO_DRIVER_OVERRIDE: strcpy(alias, "vfio_pci:"); break; default: warn("Unknown PCI driver_override alias %08X\n", override_only); } ADD(alias, "v", vendor != PCI_ANY_ID, vendor); ADD(alias, "d", device != PCI_ANY_ID, device); ADD(alias, "sv", subvendor != PCI_ANY_ID, subvendor); ADD(alias, "sd", subdevice != PCI_ANY_ID, subdevice); baseclass = (class) >> 16; baseclass_mask = (class_mask) >> 16; subclass = (class) >> 8; subclass_mask = (class_mask) >> 8; interface = class; interface_mask = class_mask; if ((baseclass_mask != 0 && baseclass_mask != 0xFF) || (subclass_mask != 0 && subclass_mask != 0xFF) || (interface_mask != 0 && interface_mask != 0xFF)) { warn("Can't handle masks in %s:%04X\n", mod->name, class_mask); return; } ADD(alias, "bc", baseclass_mask == 0xFF, baseclass); ADD(alias, "sc", subclass_mask == 0xFF, subclass); ADD(alias, "i", interface_mask == 0xFF, interface); module_alias_printf(mod, true, "%s", alias); } /* looks like: "ccw:tNmNdtNdmN" */ static void do_ccw_entry(struct module *mod, void *symval) { char alias[256] = {}; DEF_FIELD(symval, ccw_device_id, match_flags); DEF_FIELD(symval, ccw_device_id, cu_type); DEF_FIELD(symval, ccw_device_id, cu_model); DEF_FIELD(symval, ccw_device_id, dev_type); DEF_FIELD(symval, ccw_device_id, dev_model); ADD(alias, "t", match_flags&CCW_DEVICE_ID_MATCH_CU_TYPE, cu_type); ADD(alias, "m", match_flags&CCW_DEVICE_ID_MATCH_CU_MODEL, cu_model); ADD(alias, "dt", match_flags&CCW_DEVICE_ID_MATCH_DEVICE_TYPE, dev_type); ADD(alias, "dm", match_flags&CCW_DEVICE_ID_MATCH_DEVICE_MODEL, dev_model); module_alias_printf(mod, true, "ccw:%s", alias); } /* looks like: "ap:tN" */ static void do_ap_entry(struct module *mod, void *symval) { DEF_FIELD(symval, ap_device_id, dev_type); module_alias_printf(mod, false, "ap:t%02X*", dev_type); } /* looks like: "css:tN" */ static void do_css_entry(struct module *mod, void *symval) { DEF_FIELD(symval, css_device_id, type); module_alias_printf(mod, false, "css:t%01X", type); } /* Looks like: "serio:tyNprNidNexN" */ static void do_serio_entry(struct module *mod, void *symval) { char alias[256] = {}; DEF_FIELD(symval, serio_device_id, type); DEF_FIELD(symval, serio_device_id, proto); DEF_FIELD(symval, serio_device_id, id); DEF_FIELD(symval, serio_device_id, extra); ADD(alias, "ty", type != SERIO_ANY, type); ADD(alias, "pr", proto != SERIO_ANY, proto); ADD(alias, "id", id != SERIO_ANY, id); ADD(alias, "ex", extra != SERIO_ANY, extra); module_alias_printf(mod, true, "serio:%s", alias); } /* looks like: "acpi:ACPI0003" or "acpi:PNP0C0B" or "acpi:LNXVIDEO" or * "acpi:bbsspp" (bb=base-class, ss=sub-class, pp=prog-if) * * NOTE: Each driver should use one of the following : _HID, _CIDs * or _CLS. Also, bb, ss, and pp can be substituted with ?? * as don't care byte. */ static void do_acpi_entry(struct module *mod, void *symval) { DEF_FIELD_ADDR(symval, acpi_device_id, id); DEF_FIELD(symval, acpi_device_id, cls); DEF_FIELD(symval, acpi_device_id, cls_msk); if ((*id)[0]) module_alias_printf(mod, false, "acpi*:%s:*", *id); else { char alias[256]; int i, byte_shift, cnt = 0; unsigned int msk; for (i = 1; i <= 3; i++) { byte_shift = 8 * (3-i); msk = (cls_msk >> byte_shift) & 0xFF; if (msk) sprintf(&alias[cnt], "%02x", (cls >> byte_shift) & 0xFF); else sprintf(&alias[cnt], "??"); cnt += 2; } module_alias_printf(mod, false, "acpi*:%s:*", alias); } } /* looks like: "pnp:dD" */ static void do_pnp_device_entry(struct module *mod, void *symval) { DEF_FIELD_ADDR(symval, pnp_device_id, id); char acpi_id[sizeof(*id)]; /* fix broken pnp bus lowercasing */ for (unsigned int i = 0; i < sizeof(acpi_id); i++) acpi_id[i] = toupper((*id)[i]); module_alias_printf(mod, false, "pnp:d%s*", *id); module_alias_printf(mod, false, "acpi*:%s:*", acpi_id); } /* looks like: "pnp:dD" for every device of the card */ static void do_pnp_card_entry(struct module *mod, void *symval) { DEF_FIELD_ADDR(symval, pnp_card_device_id, devs); for (unsigned int i = 0; i < PNP_MAX_DEVICES; i++) { const char *id = (char *)(*devs)[i].id; char acpi_id[PNP_ID_LEN]; if (!id[0]) break; /* fix broken pnp bus lowercasing */ for (unsigned int j = 0; j < sizeof(acpi_id); j++) acpi_id[j] = toupper(id[j]); /* add an individual alias for every device entry */ module_alias_printf(mod, false, "pnp:d%s*", id); module_alias_printf(mod, false, "acpi*:%s:*", acpi_id); } } /* Looks like: pcmcia:mNcNfNfnNpfnNvaNvbNvcNvdN. */ static void do_pcmcia_entry(struct module *mod, void *symval) { char alias[256] = {}; unsigned int i; DEF_FIELD(symval, pcmcia_device_id, match_flags); DEF_FIELD(symval, pcmcia_device_id, manf_id); DEF_FIELD(symval, pcmcia_device_id, card_id); DEF_FIELD(symval, pcmcia_device_id, func_id); DEF_FIELD(symval, pcmcia_device_id, function); DEF_FIELD(symval, pcmcia_device_id, device_no); DEF_FIELD_ADDR(symval, pcmcia_device_id, prod_id_hash); for (i=0; i<4; i++) { (*prod_id_hash)[i] = TO_NATIVE((*prod_id_hash)[i]); } ADD(alias, "m", match_flags & PCMCIA_DEV_ID_MATCH_MANF_ID, manf_id); ADD(alias, "c", match_flags & PCMCIA_DEV_ID_MATCH_CARD_ID, card_id); ADD(alias, "f", match_flags & PCMCIA_DEV_ID_MATCH_FUNC_ID, func_id); ADD(alias, "fn", match_flags & PCMCIA_DEV_ID_MATCH_FUNCTION, function); ADD(alias, "pfn", match_flags & PCMCIA_DEV_ID_MATCH_DEVICE_NO, device_no); ADD(alias, "pa", match_flags & PCMCIA_DEV_ID_MATCH_PROD_ID1, (*prod_id_hash)[0]); ADD(alias, "pb", match_flags & PCMCIA_DEV_ID_MATCH_PROD_ID2, (*prod_id_hash)[1]); ADD(alias, "pc", match_flags & PCMCIA_DEV_ID_MATCH_PROD_ID3, (*prod_id_hash)[2]); ADD(alias, "pd", match_flags & PCMCIA_DEV_ID_MATCH_PROD_ID4, (*prod_id_hash)[3]); module_alias_printf(mod, true, "pcmcia:%s", alias); } static void do_vio_entry(struct module *mod, void *symval) { char alias[256]; char *tmp; DEF_FIELD_ADDR(symval, vio_device_id, type); DEF_FIELD_ADDR(symval, vio_device_id, compat); sprintf(alias, "vio:T%sS%s", (*type)[0] ? *type : "*", (*compat)[0] ? *compat : "*"); /* Replace all whitespace with underscores */ for (tmp = alias; tmp && *tmp; tmp++) if (isspace (*tmp)) *tmp = '_'; module_alias_printf(mod, true, "%s", alias); } static void do_input(char *alias, kernel_ulong_t *arr, unsigned int min, unsigned int max) { unsigned int i; for (i = min / BITS_PER_LONG; i < max / BITS_PER_LONG + 1; i++) arr[i] = TO_NATIVE(arr[i]); for (i = min; i < max; i++) if (arr[i / BITS_PER_LONG] & (1ULL << (i%BITS_PER_LONG))) sprintf(alias + strlen(alias), "%X,*", i); } /* input:b0v0p0e0-eXkXrXaXmXlXsXfXwX where X is comma-separated %02X. */ static void do_input_entry(struct module *mod, void *symval) { char alias[256] = {}; DEF_FIELD(symval, input_device_id, flags); DEF_FIELD(symval, input_device_id, bustype); DEF_FIELD(symval, input_device_id, vendor); DEF_FIELD(symval, input_device_id, product); DEF_FIELD(symval, input_device_id, version); DEF_FIELD_ADDR(symval, input_device_id, evbit); DEF_FIELD_ADDR(symval, input_device_id, keybit); DEF_FIELD_ADDR(symval, input_device_id, relbit); DEF_FIELD_ADDR(symval, input_device_id, absbit); DEF_FIELD_ADDR(symval, input_device_id, mscbit); DEF_FIELD_ADDR(symval, input_device_id, ledbit); DEF_FIELD_ADDR(symval, input_device_id, sndbit); DEF_FIELD_ADDR(symval, input_device_id, ffbit); DEF_FIELD_ADDR(symval, input_device_id, swbit); ADD(alias, "b", flags & INPUT_DEVICE_ID_MATCH_BUS, bustype); ADD(alias, "v", flags & INPUT_DEVICE_ID_MATCH_VENDOR, vendor); ADD(alias, "p", flags & INPUT_DEVICE_ID_MATCH_PRODUCT, product); ADD(alias, "e", flags & INPUT_DEVICE_ID_MATCH_VERSION, version); sprintf(alias + strlen(alias), "-e*"); if (flags & INPUT_DEVICE_ID_MATCH_EVBIT) do_input(alias, *evbit, 0, INPUT_DEVICE_ID_EV_MAX); sprintf(alias + strlen(alias), "k*"); if (flags & INPUT_DEVICE_ID_MATCH_KEYBIT) do_input(alias, *keybit, INPUT_DEVICE_ID_KEY_MIN_INTERESTING, INPUT_DEVICE_ID_KEY_MAX); sprintf(alias + strlen(alias), "r*"); if (flags & INPUT_DEVICE_ID_MATCH_RELBIT) do_input(alias, *relbit, 0, INPUT_DEVICE_ID_REL_MAX); sprintf(alias + strlen(alias), "a*"); if (flags & INPUT_DEVICE_ID_MATCH_ABSBIT) do_input(alias, *absbit, 0, INPUT_DEVICE_ID_ABS_MAX); sprintf(alias + strlen(alias), "m*"); if (flags & INPUT_DEVICE_ID_MATCH_MSCIT) do_input(alias, *mscbit, 0, INPUT_DEVICE_ID_MSC_MAX); sprintf(alias + strlen(alias), "l*"); if (flags & INPUT_DEVICE_ID_MATCH_LEDBIT) do_input(alias, *ledbit, 0, INPUT_DEVICE_ID_LED_MAX); sprintf(alias + strlen(alias), "s*"); if (flags & INPUT_DEVICE_ID_MATCH_SNDBIT) do_input(alias, *sndbit, 0, INPUT_DEVICE_ID_SND_MAX); sprintf(alias + strlen(alias), "f*"); if (flags & INPUT_DEVICE_ID_MATCH_FFBIT) do_input(alias, *ffbit, 0, INPUT_DEVICE_ID_FF_MAX); sprintf(alias + strlen(alias), "w*"); if (flags & INPUT_DEVICE_ID_MATCH_SWBIT) do_input(alias, *swbit, 0, INPUT_DEVICE_ID_SW_MAX); module_alias_printf(mod, false, "input:%s", alias); } static void do_eisa_entry(struct module *mod, void *symval) { DEF_FIELD_ADDR(symval, eisa_device_id, sig); module_alias_printf(mod, false, EISA_DEVICE_MODALIAS_FMT "*", *sig); } /* Looks like: parisc:tNhvNrevNsvN */ static void do_parisc_entry(struct module *mod, void *symval) { char alias[256] = {}; DEF_FIELD(symval, parisc_device_id, hw_type); DEF_FIELD(symval, parisc_device_id, hversion); DEF_FIELD(symval, parisc_device_id, hversion_rev); DEF_FIELD(symval, parisc_device_id, sversion); ADD(alias, "t", hw_type != PA_HWTYPE_ANY_ID, hw_type); ADD(alias, "hv", hversion != PA_HVERSION_ANY_ID, hversion); ADD(alias, "rev", hversion_rev != PA_HVERSION_REV_ANY_ID, hversion_rev); ADD(alias, "sv", sversion != PA_SVERSION_ANY_ID, sversion); module_alias_printf(mod, true, "parisc:%s", alias); } /* Looks like: sdio:cNvNdN. */ static void do_sdio_entry(struct module *mod, void *symval) { char alias[256] = {}; DEF_FIELD(symval, sdio_device_id, class); DEF_FIELD(symval, sdio_device_id, vendor); DEF_FIELD(symval, sdio_device_id, device); ADD(alias, "c", class != (__u8)SDIO_ANY_ID, class); ADD(alias, "v", vendor != (__u16)SDIO_ANY_ID, vendor); ADD(alias, "d", device != (__u16)SDIO_ANY_ID, device); module_alias_printf(mod, true, "sdio:%s", alias); } /* Looks like: ssb:vNidNrevN. */ static void do_ssb_entry(struct module *mod, void *symval) { char alias[256] = {}; DEF_FIELD(symval, ssb_device_id, vendor); DEF_FIELD(symval, ssb_device_id, coreid); DEF_FIELD(symval, ssb_device_id, revision); ADD(alias, "v", vendor != SSB_ANY_VENDOR, vendor); ADD(alias, "id", coreid != SSB_ANY_ID, coreid); ADD(alias, "rev", revision != SSB_ANY_REV, revision); module_alias_printf(mod, true, "ssb:%s", alias); } /* Looks like: bcma:mNidNrevNclN. */ static void do_bcma_entry(struct module *mod, void *symval) { char alias[256] = {}; DEF_FIELD(symval, bcma_device_id, manuf); DEF_FIELD(symval, bcma_device_id, id); DEF_FIELD(symval, bcma_device_id, rev); DEF_FIELD(symval, bcma_device_id, class); ADD(alias, "m", manuf != BCMA_ANY_MANUF, manuf); ADD(alias, "id", id != BCMA_ANY_ID, id); ADD(alias, "rev", rev != BCMA_ANY_REV, rev); ADD(alias, "cl", class != BCMA_ANY_CLASS, class); module_alias_printf(mod, true, "bcma:%s", alias); } /* Looks like: virtio:dNvN */ static void do_virtio_entry(struct module *mod, void *symval) { char alias[256] = {}; DEF_FIELD(symval, virtio_device_id, device); DEF_FIELD(symval, virtio_device_id, vendor); ADD(alias, "d", device != VIRTIO_DEV_ANY_ID, device); ADD(alias, "v", vendor != VIRTIO_DEV_ANY_ID, vendor); module_alias_printf(mod, true, "virtio:%s", alias); } /* * Looks like: vmbus:guid * Each byte of the guid will be represented by two hex characters * in the name. */ static void do_vmbus_entry(struct module *mod, void *symval) { int i; DEF_FIELD_ADDR(symval, hv_vmbus_device_id, guid); char guid_name[(sizeof(*guid) + 1) * 2]; for (i = 0; i < (sizeof(*guid) * 2); i += 2) sprintf(&guid_name[i], "%02x", TO_NATIVE((guid->b)[i/2])); module_alias_printf(mod, false, "vmbus:%s", guid_name); } /* Looks like: rpmsg:S */ static void do_rpmsg_entry(struct module *mod, void *symval) { DEF_FIELD_ADDR(symval, rpmsg_device_id, name); module_alias_printf(mod, false, RPMSG_DEVICE_MODALIAS_FMT, *name); } /* Looks like: i2c:S */ static void do_i2c_entry(struct module *mod, void *symval) { DEF_FIELD_ADDR(symval, i2c_device_id, name); module_alias_printf(mod, false, I2C_MODULE_PREFIX "%s", *name); } static void do_i3c_entry(struct module *mod, void *symval) { char alias[256] = {}; DEF_FIELD(symval, i3c_device_id, match_flags); DEF_FIELD(symval, i3c_device_id, dcr); DEF_FIELD(symval, i3c_device_id, manuf_id); DEF_FIELD(symval, i3c_device_id, part_id); DEF_FIELD(symval, i3c_device_id, extra_info); ADD(alias, "dcr", match_flags & I3C_MATCH_DCR, dcr); ADD(alias, "manuf", match_flags & I3C_MATCH_MANUF, manuf_id); ADD(alias, "part", match_flags & I3C_MATCH_PART, part_id); ADD(alias, "ext", match_flags & I3C_MATCH_EXTRA_INFO, extra_info); module_alias_printf(mod, false, "i3c:%s", alias); } static void do_slim_entry(struct module *mod, void *symval) { DEF_FIELD(symval, slim_device_id, manf_id); DEF_FIELD(symval, slim_device_id, prod_code); module_alias_printf(mod, false, "slim:%x:%x:*", manf_id, prod_code); } /* Looks like: spi:S */ static void do_spi_entry(struct module *mod, void *symval) { DEF_FIELD_ADDR(symval, spi_device_id, name); module_alias_printf(mod, false, SPI_MODULE_PREFIX "%s", *name); } static const struct dmifield { const char *prefix; int field; } dmi_fields[] = { { "bvn", DMI_BIOS_VENDOR }, { "bvr", DMI_BIOS_VERSION }, { "bd", DMI_BIOS_DATE }, { "br", DMI_BIOS_RELEASE }, { "efr", DMI_EC_FIRMWARE_RELEASE }, { "svn", DMI_SYS_VENDOR }, { "pn", DMI_PRODUCT_NAME }, { "pvr", DMI_PRODUCT_VERSION }, { "rvn", DMI_BOARD_VENDOR }, { "rn", DMI_BOARD_NAME }, { "rvr", DMI_BOARD_VERSION }, { "cvn", DMI_CHASSIS_VENDOR }, { "ct", DMI_CHASSIS_TYPE }, { "cvr", DMI_CHASSIS_VERSION }, { NULL, DMI_NONE } }; static void dmi_ascii_filter(char *d, const char *s) { /* Filter out characters we don't want to see in the modalias string */ for (; *s; s++) if (*s > ' ' && *s < 127 && *s != ':') *(d++) = *s; *d = 0; } static void do_dmi_entry(struct module *mod, void *symval) { char alias[256] = {}; int i, j; DEF_FIELD_ADDR(symval, dmi_system_id, matches); for (i = 0; i < ARRAY_SIZE(dmi_fields); i++) { for (j = 0; j < 4; j++) { if ((*matches)[j].slot && (*matches)[j].slot == dmi_fields[i].field) { sprintf(alias + strlen(alias), ":%s*", dmi_fields[i].prefix); dmi_ascii_filter(alias + strlen(alias), (*matches)[j].substr); strcat(alias, "*"); } } } module_alias_printf(mod, false, "dmi*%s:", alias); } static void do_platform_entry(struct module *mod, void *symval) { DEF_FIELD_ADDR(symval, platform_device_id, name); module_alias_printf(mod, false, PLATFORM_MODULE_PREFIX "%s", *name); } static void do_mdio_entry(struct module *mod, void *symval) { char id[33]; int i; DEF_FIELD(symval, mdio_device_id, phy_id); DEF_FIELD(symval, mdio_device_id, phy_id_mask); for (i = 0; i < 32; i++) { if (!((phy_id_mask >> (31-i)) & 1)) id[i] = '?'; else if ((phy_id >> (31-i)) & 1) id[i] = '1'; else id[i] = '0'; } /* Terminate the string */ id[32] = '\0'; module_alias_printf(mod, false, MDIO_MODULE_PREFIX "%s", id); } /* Looks like: zorro:iN. */ static void do_zorro_entry(struct module *mod, void *symval) { char alias[256] = {}; DEF_FIELD(symval, zorro_device_id, id); ADD(alias, "i", id != ZORRO_WILDCARD, id); module_alias_printf(mod, false, "zorro:%s", alias); } /* looks like: "pnp:dD" */ static void do_isapnp_entry(struct module *mod, void *symval) { DEF_FIELD(symval, isapnp_device_id, vendor); DEF_FIELD(symval, isapnp_device_id, function); module_alias_printf(mod, false, "pnp:d%c%c%c%x%x%x%x*", 'A' + ((vendor >> 2) & 0x3f) - 1, 'A' + (((vendor & 3) << 3) | ((vendor >> 13) & 7)) - 1, 'A' + ((vendor >> 8) & 0x1f) - 1, (function >> 4) & 0x0f, function & 0x0f, (function >> 12) & 0x0f, (function >> 8) & 0x0f); } /* Looks like: "ipack:fNvNdN". */ static void do_ipack_entry(struct module *mod, void *symval) { char alias[256] = {}; DEF_FIELD(symval, ipack_device_id, format); DEF_FIELD(symval, ipack_device_id, vendor); DEF_FIELD(symval, ipack_device_id, device); ADD(alias, "f", format != IPACK_ANY_FORMAT, format); ADD(alias, "v", vendor != IPACK_ANY_ID, vendor); ADD(alias, "d", device != IPACK_ANY_ID, device); module_alias_printf(mod, true, "ipack:%s", alias); } /* * Append a match expression for a single masked hex digit. * outp points to a pointer to the character at which to append. * *outp is updated on return to point just after the appended text, * to facilitate further appending. */ static void append_nibble_mask(char **outp, unsigned int nibble, unsigned int mask) { char *p = *outp; unsigned int i; switch (mask) { case 0: *p++ = '?'; break; case 0xf: p += sprintf(p, "%X", nibble); break; default: /* * Dumbly emit a match pattern for all possible matching * digits. This could be improved in some cases using ranges, * but it has the advantage of being trivially correct, and is * often optimal. */ *p++ = '['; for (i = 0; i < 0x10; i++) if ((i & mask) == nibble) p += sprintf(p, "%X", i); *p++ = ']'; } /* Ensure that the string remains NUL-terminated: */ *p = '\0'; /* Advance the caller's end-of-string pointer: */ *outp = p; } /* * looks like: "amba:dN" * * N is exactly 8 digits, where each is an upper-case hex digit, or * a ? or [] pattern matching exactly one digit. */ static void do_amba_entry(struct module *mod, void *symval) { char alias[256]; unsigned int digit; char *p = alias; DEF_FIELD(symval, amba_id, id); DEF_FIELD(symval, amba_id, mask); if ((id & mask) != id) fatal("%s: Masked-off bit(s) of AMBA device ID are non-zero: id=0x%08X, mask=0x%08X. Please fix this driver.\n", mod->name, id, mask); for (digit = 0; digit < 8; digit++) append_nibble_mask(&p, (id >> (4 * (7 - digit))) & 0xf, (mask >> (4 * (7 - digit))) & 0xf); module_alias_printf(mod, false, "amba:d%s", alias); } /* * looks like: "mipscdmm:tN" * * N is exactly 2 digits, where each is an upper-case hex digit, or * a ? or [] pattern matching exactly one digit. */ static void do_mips_cdmm_entry(struct module *mod, void *symval) { DEF_FIELD(symval, mips_cdmm_device_id, type); module_alias_printf(mod, false, "mipscdmm:t%02X*", type); } /* LOOKS like cpu:type:x86,venVVVVfamFFFFmodMMMM:feature:*,FEAT,* * All fields are numbers. It would be nicer to use strings for vendor * and feature, but getting those out of the build system here is too * complicated. */ static void do_x86cpu_entry(struct module *mod, void *symval) { char alias[256] = {}; DEF_FIELD(symval, x86_cpu_id, feature); DEF_FIELD(symval, x86_cpu_id, family); DEF_FIELD(symval, x86_cpu_id, model); DEF_FIELD(symval, x86_cpu_id, vendor); ADD(alias, "ven", vendor != X86_VENDOR_ANY, vendor); ADD(alias, "fam", family != X86_FAMILY_ANY, family); ADD(alias, "mod", model != X86_MODEL_ANY, model); strcat(alias, ":feature:*"); if (feature != X86_FEATURE_ANY) sprintf(alias + strlen(alias), "%04X*", feature); module_alias_printf(mod, false, "cpu:type:x86,%s", alias); } /* LOOKS like cpu:type:*:feature:*FEAT* */ static void do_cpu_entry(struct module *mod, void *symval) { DEF_FIELD(symval, cpu_feature, feature); module_alias_printf(mod, false, "cpu:type:*:feature:*%04X*", feature); } /* Looks like: mei:S:uuid:N:* */ static void do_mei_entry(struct module *mod, void *symval) { char alias[256] = {}; DEF_FIELD_ADDR(symval, mei_cl_device_id, name); DEF_FIELD_ADDR(symval, mei_cl_device_id, uuid); DEF_FIELD(symval, mei_cl_device_id, version); add_uuid(alias, *uuid); ADD(alias, ":", version != MEI_CL_VERSION_ANY, version); module_alias_printf(mod, false, MEI_CL_MODULE_PREFIX "%s:%s:*", (*name)[0] ? *name : "*", alias); } /* Looks like: rapidio:vNdNavNadN */ static void do_rio_entry(struct module *mod, void *symval) { char alias[256] = {}; DEF_FIELD(symval, rio_device_id, did); DEF_FIELD(symval, rio_device_id, vid); DEF_FIELD(symval, rio_device_id, asm_did); DEF_FIELD(symval, rio_device_id, asm_vid); ADD(alias, "v", vid != RIO_ANY_ID, vid); ADD(alias, "d", did != RIO_ANY_ID, did); ADD(alias, "av", asm_vid != RIO_ANY_ID, asm_vid); ADD(alias, "ad", asm_did != RIO_ANY_ID, asm_did); module_alias_printf(mod, true, "rapidio:%s", alias); } /* Looks like: ulpi:vNpN */ static void do_ulpi_entry(struct module *mod, void *symval) { DEF_FIELD(symval, ulpi_device_id, vendor); DEF_FIELD(symval, ulpi_device_id, product); module_alias_printf(mod, false, "ulpi:v%04xp%04x", vendor, product); } /* Looks like: hdaudio:vNrNaN */ static void do_hda_entry(struct module *mod, void *symval) { char alias[256] = {}; DEF_FIELD(symval, hda_device_id, vendor_id); DEF_FIELD(symval, hda_device_id, rev_id); DEF_FIELD(symval, hda_device_id, api_version); ADD(alias, "v", vendor_id != 0, vendor_id); ADD(alias, "r", rev_id != 0, rev_id); ADD(alias, "a", api_version != 0, api_version); module_alias_printf(mod, true, "hdaudio:%s", alias); } /* Looks like: sdw:mNpNvNcN */ static void do_sdw_entry(struct module *mod, void *symval) { char alias[256] = {}; DEF_FIELD(symval, sdw_device_id, mfg_id); DEF_FIELD(symval, sdw_device_id, part_id); DEF_FIELD(symval, sdw_device_id, sdw_version); DEF_FIELD(symval, sdw_device_id, class_id); ADD(alias, "m", mfg_id != 0, mfg_id); ADD(alias, "p", part_id != 0, part_id); ADD(alias, "v", sdw_version != 0, sdw_version); ADD(alias, "c", class_id != 0, class_id); module_alias_printf(mod, true, "sdw:%s", alias); } /* Looks like: fsl-mc:vNdN */ static void do_fsl_mc_entry(struct module *mod, void *symval) { DEF_FIELD(symval, fsl_mc_device_id, vendor); DEF_FIELD_ADDR(symval, fsl_mc_device_id, obj_type); module_alias_printf(mod, false, "fsl-mc:v%08Xd%s", vendor, *obj_type); } /* Looks like: tbsvc:kSpNvNrN */ static void do_tbsvc_entry(struct module *mod, void *symval) { char alias[256] = {}; DEF_FIELD(symval, tb_service_id, match_flags); DEF_FIELD_ADDR(symval, tb_service_id, protocol_key); DEF_FIELD(symval, tb_service_id, protocol_id); DEF_FIELD(symval, tb_service_id, protocol_version); DEF_FIELD(symval, tb_service_id, protocol_revision); if (match_flags & TBSVC_MATCH_PROTOCOL_KEY) sprintf(alias + strlen(alias), "k%s", *protocol_key); else strcat(alias + strlen(alias), "k*"); ADD(alias, "p", match_flags & TBSVC_MATCH_PROTOCOL_ID, protocol_id); ADD(alias, "v", match_flags & TBSVC_MATCH_PROTOCOL_VERSION, protocol_version); ADD(alias, "r", match_flags & TBSVC_MATCH_PROTOCOL_REVISION, protocol_revision); module_alias_printf(mod, true, "tbsvc:%s", alias); } /* Looks like: typec:idN */ static void do_typec_entry(struct module *mod, void *symval) { DEF_FIELD(symval, typec_device_id, svid); module_alias_printf(mod, false, "typec:id%04X", svid); } /* Looks like: tee:uuid */ static void do_tee_entry(struct module *mod, void *symval) { DEF_FIELD_ADDR(symval, tee_client_device_id, uuid); module_alias_printf(mod, true, "tee:%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x", uuid->b[0], uuid->b[1], uuid->b[2], uuid->b[3], uuid->b[4], uuid->b[5], uuid->b[6], uuid->b[7], uuid->b[8], uuid->b[9], uuid->b[10], uuid->b[11], uuid->b[12], uuid->b[13], uuid->b[14], uuid->b[15]); } /* Looks like: wmi:guid */ static void do_wmi_entry(struct module *mod, void *symval) { DEF_FIELD_ADDR(symval, wmi_device_id, guid_string); if (strlen(*guid_string) != UUID_STRING_LEN) { warn("Invalid WMI device id 'wmi:%s' in '%s'\n", *guid_string, mod->name); return; } module_alias_printf(mod, false, WMI_MODULE_PREFIX "%s", *guid_string); } /* Looks like: mhi:S */ static void do_mhi_entry(struct module *mod, void *symval) { DEF_FIELD_ADDR(symval, mhi_device_id, chan); module_alias_printf(mod, false, MHI_DEVICE_MODALIAS_FMT, *chan); } /* Looks like: mhi_ep:S */ static void do_mhi_ep_entry(struct module *mod, void *symval) { DEF_FIELD_ADDR(symval, mhi_device_id, chan); module_alias_printf(mod, false, MHI_EP_DEVICE_MODALIAS_FMT, *chan); } /* Looks like: ishtp:{guid} */ static void do_ishtp_entry(struct module *mod, void *symval) { char alias[256] = {}; DEF_FIELD_ADDR(symval, ishtp_device_id, guid); add_guid(alias, *guid); module_alias_printf(mod, false, ISHTP_MODULE_PREFIX "{%s}", alias); } static void do_auxiliary_entry(struct module *mod, void *symval) { DEF_FIELD_ADDR(symval, auxiliary_device_id, name); module_alias_printf(mod, false, AUXILIARY_MODULE_PREFIX "%s", *name); } /* * Looks like: ssam:dNcNtNiNfN * * N is exactly 2 digits, where each is an upper-case hex digit. */ static void do_ssam_entry(struct module *mod, void *symval) { char alias[256] = {}; DEF_FIELD(symval, ssam_device_id, match_flags); DEF_FIELD(symval, ssam_device_id, domain); DEF_FIELD(symval, ssam_device_id, category); DEF_FIELD(symval, ssam_device_id, target); DEF_FIELD(symval, ssam_device_id, instance); DEF_FIELD(symval, ssam_device_id, function); ADD(alias, "t", match_flags & SSAM_MATCH_TARGET, target); ADD(alias, "i", match_flags & SSAM_MATCH_INSTANCE, instance); ADD(alias, "f", match_flags & SSAM_MATCH_FUNCTION, function); module_alias_printf(mod, false, "ssam:d%02Xc%02X%s", domain, category, alias); } /* Looks like: dfl:tNfN */ static void do_dfl_entry(struct module *mod, void *symval) { DEF_FIELD(symval, dfl_device_id, type); DEF_FIELD(symval, dfl_device_id, feature_id); module_alias_printf(mod, true, "dfl:t%04Xf%04X", type, feature_id); } /* Looks like: cdx:vNdN */ static void do_cdx_entry(struct module *mod, void *symval) { char alias[256]; DEF_FIELD(symval, cdx_device_id, vendor); DEF_FIELD(symval, cdx_device_id, device); DEF_FIELD(symval, cdx_device_id, subvendor); DEF_FIELD(symval, cdx_device_id, subdevice); DEF_FIELD(symval, cdx_device_id, class); DEF_FIELD(symval, cdx_device_id, class_mask); DEF_FIELD(symval, cdx_device_id, override_only); switch (override_only) { case 0: strcpy(alias, "cdx:"); break; case CDX_ID_F_VFIO_DRIVER_OVERRIDE: strcpy(alias, "vfio_cdx:"); break; default: warn("Unknown CDX driver_override alias %08X\n", override_only); return; } ADD(alias, "v", vendor != CDX_ANY_ID, vendor); ADD(alias, "d", device != CDX_ANY_ID, device); ADD(alias, "sv", subvendor != CDX_ANY_ID, subvendor); ADD(alias, "sd", subdevice != CDX_ANY_ID, subdevice); ADD(alias, "c", class_mask == 0xFFFFFF, class); module_alias_printf(mod, false, "%s", alias); } static void do_vchiq_entry(struct module *mod, void *symval) { DEF_FIELD_ADDR(symval, vchiq_device_id, name); module_alias_printf(mod, false, "vchiq:%s", *name); } /* Looks like: coreboot:tN */ static void do_coreboot_entry(struct module *mod, void *symval) { DEF_FIELD(symval, coreboot_device_id, tag); module_alias_printf(mod, false, "coreboot:t%08X", tag); } /* Does namelen bytes of name exactly match the symbol? */ static bool sym_is(const char *name, unsigned namelen, const char *symbol) { if (namelen != strlen(symbol)) return false; return memcmp(name, symbol, namelen) == 0; } static void do_table(const char *name, void *symval, unsigned long size, unsigned long id_size, const char *device_id, void (*do_entry)(struct module *mod, void *symval), struct module *mod) { unsigned int i; if (size % id_size || size < id_size) { error("%s: type mismatch between %s[] and MODULE_DEVICE_TABLE(%s, ...)\n", mod->name, name, device_id); return; } /* Verify the last entry is a terminator */ for (i = size - id_size; i < size; i++) { if (*(uint8_t *)(symval + i)) { error("%s: %s[] is not terminated with a NULL entry\n", mod->name, name); return; } } /* Leave last one: it's the terminator. */ size -= id_size; for (i = 0; i < size; i += id_size) do_entry(mod, symval + i); } static const struct devtable devtable[] = { {"hid", SIZE_hid_device_id, do_hid_entry}, {"ieee1394", SIZE_ieee1394_device_id, do_ieee1394_entry}, {"pci", SIZE_pci_device_id, do_pci_entry}, {"ccw", SIZE_ccw_device_id, do_ccw_entry}, {"ap", SIZE_ap_device_id, do_ap_entry}, {"css", SIZE_css_device_id, do_css_entry}, {"serio", SIZE_serio_device_id, do_serio_entry}, {"acpi", SIZE_acpi_device_id, do_acpi_entry}, {"pcmcia", SIZE_pcmcia_device_id, do_pcmcia_entry}, {"vio", SIZE_vio_device_id, do_vio_entry}, {"input", SIZE_input_device_id, do_input_entry}, {"eisa", SIZE_eisa_device_id, do_eisa_entry}, {"parisc", SIZE_parisc_device_id, do_parisc_entry}, {"sdio", SIZE_sdio_device_id, do_sdio_entry}, {"ssb", SIZE_ssb_device_id, do_ssb_entry}, {"bcma", SIZE_bcma_device_id, do_bcma_entry}, {"virtio", SIZE_virtio_device_id, do_virtio_entry}, {"vmbus", SIZE_hv_vmbus_device_id, do_vmbus_entry}, {"rpmsg", SIZE_rpmsg_device_id, do_rpmsg_entry}, {"i2c", SIZE_i2c_device_id, do_i2c_entry}, {"i3c", SIZE_i3c_device_id, do_i3c_entry}, {"slim", SIZE_slim_device_id, do_slim_entry}, {"spi", SIZE_spi_device_id, do_spi_entry}, {"dmi", SIZE_dmi_system_id, do_dmi_entry}, {"platform", SIZE_platform_device_id, do_platform_entry}, {"mdio", SIZE_mdio_device_id, do_mdio_entry}, {"zorro", SIZE_zorro_device_id, do_zorro_entry}, {"isapnp", SIZE_isapnp_device_id, do_isapnp_entry}, {"ipack", SIZE_ipack_device_id, do_ipack_entry}, {"amba", SIZE_amba_id, do_amba_entry}, {"mipscdmm", SIZE_mips_cdmm_device_id, do_mips_cdmm_entry}, {"x86cpu", SIZE_x86_cpu_id, do_x86cpu_entry}, {"cpu", SIZE_cpu_feature, do_cpu_entry}, {"mei", SIZE_mei_cl_device_id, do_mei_entry}, {"rapidio", SIZE_rio_device_id, do_rio_entry}, {"ulpi", SIZE_ulpi_device_id, do_ulpi_entry}, {"hdaudio", SIZE_hda_device_id, do_hda_entry}, {"sdw", SIZE_sdw_device_id, do_sdw_entry}, {"fslmc", SIZE_fsl_mc_device_id, do_fsl_mc_entry}, {"tbsvc", SIZE_tb_service_id, do_tbsvc_entry}, {"typec", SIZE_typec_device_id, do_typec_entry}, {"tee", SIZE_tee_client_device_id, do_tee_entry}, {"wmi", SIZE_wmi_device_id, do_wmi_entry}, {"mhi", SIZE_mhi_device_id, do_mhi_entry}, {"mhi_ep", SIZE_mhi_device_id, do_mhi_ep_entry}, {"auxiliary", SIZE_auxiliary_device_id, do_auxiliary_entry}, {"ssam", SIZE_ssam_device_id, do_ssam_entry}, {"dfl", SIZE_dfl_device_id, do_dfl_entry}, {"ishtp", SIZE_ishtp_device_id, do_ishtp_entry}, {"cdx", SIZE_cdx_device_id, do_cdx_entry}, {"vchiq", SIZE_vchiq_device_id, do_vchiq_entry}, {"coreboot", SIZE_coreboot_device_id, do_coreboot_entry}, {"of", SIZE_of_device_id, do_of_entry}, {"usb", SIZE_usb_device_id, do_usb_entry_multi}, {"pnp", SIZE_pnp_device_id, do_pnp_device_entry}, {"pnp_card", SIZE_pnp_card_device_id, do_pnp_card_entry}, }; /* Create MODULE_ALIAS() statements. * At this time, we cannot write the actual output C source yet, * so we write into the mod->dev_table_buf buffer. */ void handle_moddevtable(struct module *mod, struct elf_info *info, Elf_Sym *sym, const char *symname) { void *symval; char *zeros = NULL; const char *type, *name; size_t typelen; static const char *prefix = "__mod_device_table__"; /* We're looking for a section relative symbol */ if (!sym->st_shndx || get_secindex(info, sym) >= info->num_sections) return; /* We're looking for an object */ if (ELF_ST_TYPE(sym->st_info) != STT_OBJECT) return; /* All our symbols are of form __mod_device_table__<type>__<name>. */ if (!strstarts(symname, prefix)) return; type = symname + strlen(prefix); name = strstr(type, "__"); if (!name) return; typelen = name - type; name += strlen("__"); /* Handle all-NULL symbols allocated into .bss */ if (info->sechdrs[get_secindex(info, sym)].sh_type & SHT_NOBITS) { zeros = calloc(1, sym->st_size); symval = zeros; } else { symval = sym_get_data(info, sym); } for (int i = 0; i < ARRAY_SIZE(devtable); i++) { const struct devtable *p = &devtable[i]; if (sym_is(type, typelen, p->device_id)) { do_table(name, symval, sym->st_size, p->id_size, p->device_id, p->do_entry, mod); break; } } free(zeros); }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
You can’t perform that action at this time.