From 034dd340b08be1f2f0477ad16131d609f9dbd53c Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sun, 30 Aug 2026 09:22:00 -0700 Subject: Merge tag 'trace-v7.3-2' of git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-trace Pull tracing fixes from Steven Rostedt: - Fix error output of boot instance creation failure Currently if a boot instance creation fails, instead of printing out the name of the instance that failed, it prints "(null)". That is because it prints "cur_str" that had already been processed by strsep(). Print the saved name instead. While at it, print the error code of the failure. - Fix use-after-free for same named historgrams Histograms can be named so that they can be used in multiple events. But if the named histogram has a variable attached, the second event that uses the named histogram which duplicates it and needs to free the original after duplication leaves the old variable in place and still visible. If another histogram uses than variable, it will use the stale one which will try to reference the freed duplicate histogram and crash the kernel. Free the duplicate variables along with the duplicated histogram data. - Check return value of kthread_run() in event self test The events self tests uses a kthread for testing but does not check if it succeeded in creating a kthread. If the kthread creation were to fail, the code will still try to call kthread_stop() on the error returned. - Fix race between reading trace_pipe and updating subbuffer size If a user is reading the trace_pipe file at the same time they update the ring buffer sub-buffer size, can cause the trace_pipe read to read stale data. Add trace_access_lock() around updating the ring buffer sub-buffer size. - Fix eventfs_inode on failure path in creation of the events directory In the creation of the "events" directory, if after allocating the eventfs_inode a failure is detected, it calls cleanup_ei() which calls free_ei(). The free_ei() will test if eventfs_inode being freed has no children. It is a bug if it does. But on the failure case of the creation of the "events" directory, the children lists have not yet been initialized and the free will trigger a warning because list_empty() on an uninitialized list returns false. Move the initialization into init_ei() where it makes more sense and makes sure that a created eventfs_inode has its lists initialized upon creation. - Check return value of kthread_run() in ftrace direct sample code The sample code that shows how to use the ftrace direct calls does not test the return of kthread_run() to see if it succeeds. Return a failure if the kthread_run() doesn't succeed. - Clear user events state on fork in case of alloc failure On fork, the child gets a pointer to the parent's user events state. It makes a copy of it then updates the child's pointer to it. But if the allocation fails, the duplication function leaves the child with a pointer to its parent's descriptor. When the child cleans up its data, it will free the parent's descriptor while the parent is still using it. In the duplication function, set the child's user_event_mm to NULL before testing if the allocation succeeded, and when it exits it will not free the parent's descriptor. - Fix retry exhaustion in simple ring buffer reader swap simple_ring_buffer_swap_reader_page() starts with retry set to 8 and post-decrements it only after a failed link replacement. On the final attempt, a successful replacement leaves retry at zero, while a failed replacement leaves it at -1. But the check for success expects the retry value to be non-zero and exits with an error on zero. This is the opposite result. Fix it. - Fail nicely when the remote swap_reader_page() returns an error Currently, if the swap_reader_page() of a remote buffer fails, it triggers a WARN_ON_ONCE() and continues normally. Instead, have it exit with an error and a pr_warn() print instead of a full WARNING. * tag 'trace-v7.3-2' of git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-trace: ring-buffer: Stop remote reader update when page swap fails tracing: Fix retry exhaustion in simple ring buffer reader swap tracing/user_events: Clear copied tracing state before fork duplication samples/ftrace: Fix kthread_stop() on ERR_PTR in ftrace-direct-multi-modify samples/ftrace: Fix kthread_stop() on ERR_PTR in ftrace-direct-modify eventfs: Initialize ei->children and ei->list in init_ei() tracing: Fix use-after-free in trace_pipe read on sub-buffer order change tracing: Fix crash passing ERR_PTR to kthread_stop() tracing: Fix use-after-free with same-name named triggers tracing: Fix logged instance name on creation failure --- scripts/mod/file2alias.c | 1610 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1610 insertions(+) create mode 100644 scripts/mod/file2alias.c (limited to 'scripts/mod/file2alias.c') diff --git a/scripts/mod/file2alias.c b/scripts/mod/file2alias.c new file mode 100644 index 000000000..8d36c74de --- /dev/null +++ b/scripts/mod/file2alias.c @@ -0,0 +1,1610 @@ +/* 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 +#include + +#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 +#else +#include +#endif + +#include +#include + +/** + * 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; + } + } + + new->builtin_modname = NULL; + 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 = \ + get_unaligned_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 _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] = {}; + + 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); + + 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, + get_unaligned_native(*prod_id_hash + 0)); + ADD(alias, "pb", match_flags & PCMCIA_DEV_ID_MATCH_PROD_ID2, + get_unaligned_native(*prod_id_hash + 1)); + ADD(alias, "pc", match_flags & PCMCIA_DEV_ID_MATCH_PROD_ID3, + get_unaligned_native(*prod_id_hash + 2)); + ADD(alias, "pd", match_flags & PCMCIA_DEV_ID_MATCH_PROD_ID4, + get_unaligned_native(*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 __attribute__((format(printf, 3, 4))) +alias_append(char *alias, size_t size, const char *fmt, ...) +{ + size_t len = strlen(alias); + va_list args; + int n; + + if (len >= size) + fatal("alias buffer (%zu) overflow before append\n", size); + + va_start(args, fmt); + n = vsnprintf(alias + len, size - len, fmt, args); + va_end(args); + + if (n < 0 || (size_t)n >= size - len) + fatal("alias buffer (%zu) overflow on append (need %d, have %zu)\n", + size, n, size - len); +} + +static void do_input(char *alias, size_t size, + kernel_ulong_t *arr, unsigned int min, unsigned int max) +{ + unsigned int i; + + for (i = min; i <= max; i++) + if (get_unaligned_native(arr + i / BITS_PER_LONG) & + (1ULL << (i % BITS_PER_LONG))) + alias_append(alias, size, "%X,*", i); +} + +/* input:b0v0p0e0-eXkXrXaXmXlXsXfXwX where X is comma-separated %02X. */ +static void do_input_entry(struct module *mod, void *symval) +{ + char alias[256] = {}; + const size_t sizeof_alias = sizeof(alias); + + 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); + + alias_append(alias, sizeof_alias, "-e*"); + if (flags & INPUT_DEVICE_ID_MATCH_EVBIT) + do_input(alias, sizeof_alias, *evbit, 0, INPUT_DEVICE_ID_EV_MAX); + alias_append(alias, sizeof_alias, "k*"); + if (flags & INPUT_DEVICE_ID_MATCH_KEYBIT) + do_input(alias, sizeof_alias, *keybit, + INPUT_DEVICE_ID_KEY_MIN_INTERESTING, + INPUT_DEVICE_ID_KEY_MAX); + alias_append(alias, sizeof_alias, "r*"); + if (flags & INPUT_DEVICE_ID_MATCH_RELBIT) + do_input(alias, sizeof_alias, *relbit, 0, INPUT_DEVICE_ID_REL_MAX); + alias_append(alias, sizeof_alias, "a*"); + if (flags & INPUT_DEVICE_ID_MATCH_ABSBIT) + do_input(alias, sizeof_alias, *absbit, 0, INPUT_DEVICE_ID_ABS_MAX); + alias_append(alias, sizeof_alias, "m*"); + if (flags & INPUT_DEVICE_ID_MATCH_MSCIT) + do_input(alias, sizeof_alias, *mscbit, 0, INPUT_DEVICE_ID_MSC_MAX); + alias_append(alias, sizeof_alias, "l*"); + if (flags & INPUT_DEVICE_ID_MATCH_LEDBIT) + do_input(alias, sizeof_alias, *ledbit, 0, INPUT_DEVICE_ID_LED_MAX); + alias_append(alias, sizeof_alias, "s*"); + if (flags & INPUT_DEVICE_ID_MATCH_SNDBIT) + do_input(alias, sizeof_alias, *sndbit, 0, INPUT_DEVICE_ID_SND_MAX); + alias_append(alias, sizeof_alias, "f*"); + if (flags & INPUT_DEVICE_ID_MATCH_FFBIT) + do_input(alias, sizeof_alias, *ffbit, 0, INPUT_DEVICE_ID_FF_MAX); + alias_append(alias, sizeof_alias, "w*"); + if (flags & INPUT_DEVICE_ID_MATCH_SWBIT) + do_input(alias, sizeof_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) +{ + DEF_FIELD_ADDR(symval, hv_vmbus_device_id, guid); + char guid_name[sizeof(*guid) * 2 + 1]; + + for (int i = 0; i < sizeof(*guid); i++) + sprintf(&guid_name[i * 2], "%02x", guid->b[i]); + + 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, size_t avail, const char *s) +{ + /* Filter out characters we don't want to see in the modalias string */ + for (; *s; s++) + if (*s > ' ' && *s < 127 && *s != ':') { + if (avail <= 1) + fatal("%s: alias buffer overflow\n", __func__); + *(d++) = *s; + avail--; + } + + *d = 0; +} + + +static void do_dmi_entry(struct module *mod, void *symval) +{ + char alias[256] = {}; + const size_t sizeof_alias = sizeof(alias); + size_t len; + 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) { + alias_append(alias, sizeof_alias, ":%s*", + dmi_fields[i].prefix); + len = strlen(alias); + dmi_ascii_filter(alias + len, sizeof_alias - len, + (*matches)[j].substr); + alias_append(alias, sizeof_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: mcb:16zN */ +static void do_mcb_entry(struct module *mod, void *symval) +{ + DEF_FIELD(symval, mcb_device_id, device); + + module_alias_printf(mod, false, "mcb:16z%03d", device); +} + +/* 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); + char result[sizeof(*guid_string)]; + int i; + + if (strlen(*guid_string) != UUID_STRING_LEN) { + warn("Invalid WMI device id 'wmi:%s' in '%s'\n", + *guid_string, mod->name); + return; + } + + for (i = 0; i < UUID_STRING_LEN; i++) { + char value = (*guid_string)[i]; + bool valid = false; + + if (i == 8 || i == 13 || i == 18 || i == 23) { + if (value == '-') + valid = true; + } else { + if (isxdigit(value)) + valid = true; + } + + if (!valid) { + warn("Invalid character %c inside WMI GUID string '%s' in '%s'\n", + value, *guid_string, mod->name); + return; + } + + /* Some GUIDs from BMOF definitions contain lowercase characters */ + result[i] = toupper(value); + } + + result[i] = '\0'; + + module_alias_printf(mod, false, WMI_MODULE_PREFIX "%s", result); +} + +/* 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}, + {"mcb", SIZE_mcb_device_id, do_mcb_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, *modname; + size_t typelen, modnamelen; + 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__kmod_____. */ + if (!strstarts(symname, prefix)) + return; + + modname = strstr(symname, "__kmod_"); + if (!modname) + return; + modname += strlen("__kmod_"); + + type = strstr(modname, "__"); + if (!type) + return; + modnamelen = type - modname; + type += strlen("__"); + + 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; + } + } + + if (mod->is_vmlinux) { + struct module_alias *alias; + + /* + * If this is vmlinux, record the name of the builtin module. + * Traverse the linked list in the reverse order, and set the + * builtin_modname unless it has already been set in the + * previous call. + */ + list_for_each_entry_reverse(alias, &mod->aliases, node) { + if (alias->builtin_modname) + break; + alias->builtin_modname = xstrndup(modname, modnamelen); + } + } + + free(zeros); +} -- cgit v1.3.1