1528 lines · 44.2 KB · C
RawDownload
/* 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 == ELFCLASS32typedef Elf32_Addr	kernel_ulong_t;#define BITS_PER_LONG 32#elsetypedef 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); 

Lines 1–500 of 1528.