Skip to content
Snippets Groups Projects
file2alias.c 37.1 KiB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
/* 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 "modpost.h"
#include "devicetable-offsets.h"
Linus Torvalds's avatar
Linus Torvalds committed

/* 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
Linus Torvalds's avatar
Linus Torvalds committed
#else
typedef Elf64_Addr	kernel_ulong_t;
#define BITS_PER_LONG 64
Linus Torvalds's avatar
Linus Torvalds committed
#endif
#ifdef __sun__
#include <inttypes.h>
#else
#include <stdint.h>
#endif

Linus Torvalds's avatar
Linus Torvalds committed
typedef uint32_t	__u32;
typedef uint16_t	__u16;
typedef unsigned char	__u8;

/* Big exception to the "don't include kernel headers into userspace, which
 * even potentially has different endianness and word sizes, since
Linus Torvalds's avatar
Linus Torvalds committed
 * we handle those differences explicitly below */
#include "../../include/linux/mod_devicetable.h"

/* This array collects all instances that use the generic do_table */
struct devtable {
	const char *device_id; /* name of table, __mod_<name>_device_table. */
	unsigned long id_size;
	void *function;
};

#define ___cat(a,b) a ## b
#define __cat(a,b) ___cat(a,b)

/* we need some special handling for this host tool running eventually on
 * Darwin. The Mach-O section handling is a bit different than ELF section
 * handling. The differnces in detail are:
 *  a) we have segments which have sections
 *  b) we need a API call to get the respective section symbols */
#if defined(__MACH__)
#include <mach-o/getsect.h>

#define INIT_SECTION(name)  do {					\
		unsigned long name ## _len;				\
		char *__cat(pstart_,name) = getsectdata("__TEXT",	\
			#name, &__cat(name,_len));			\
		char *__cat(pstop_,name) = __cat(pstart_,name) +	\
			__cat(name, _len);				\
		__cat(__start_,name) = (void *)__cat(pstart_,name);	\
		__cat(__stop_,name) = (void *)__cat(pstop_,name);	\
	} while (0)
#define SECTION(name)   __attribute__((section("__TEXT, " #name)))

struct devtable **__start___devtable, **__stop___devtable;
#else
#define INIT_SECTION(name) /* no-op for ELF */
#define SECTION(name)   __attribute__((section(#name)))

/* We construct a table of pointers in an ELF section (pointers generally
 * go unpadded by gcc).  ld creates boundary syms for us. */
extern struct devtable *__start___devtable[], *__stop___devtable[];
#if !defined(__used)
# if __GNUC__ == 3 && __GNUC_MINOR__ < 3
#  define __used			__attribute__((__unused__))
# else
#  define __used			__attribute__((__used__))
# endif
/* 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)

/* Add a table entry.  We test function type matches while we're here. */
#define ADD_TO_DEVTABLE(device_id, type, function) \
	static struct devtable __cat(devtable,__LINE__) = {	\
		device_id + 0*sizeof((function)((const char *)NULL,	\
						(char *)NULL)),		\
	static struct devtable *SECTION(__devtable) __used \
		__cat(devtable_ptr,__LINE__) = &__cat(devtable,__LINE__)
Linus Torvalds's avatar
Linus Torvalds committed
#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)

/* Always end in a wildcard, for future extension */
static inline void add_wildcard(char *str)
{
	int len = strlen(str);

	if (str[len - 1] != '*')
		strcat(str + len, "*");
}

/**
 * Check that sizeof(device_id type) are consistent with size of section
 * in .o file. If in-consistent then userspace and kernel does not agree
 * on actual size which is a bug.
 * Also verify that the final entry in the table is all zeros.
 * Ignore both checks if build host differ from target host and size differs.
static void device_id_check(const char *modname, const char *device_id,
			    unsigned long size, unsigned long id_size,
			    void *symval)
	if (size % id_size || size < id_size) {
		fatal("%s: sizeof(struct %s_device_id)=%lu is not a modulo "
		      "of the size of section __mod_%s_device_table=%lu.\n"
		      "Fix definition of struct %s_device_id "
		      "in mod_devicetable.h\n",
		      modname, device_id, id_size, device_id, size, device_id);
	}
	/* Verify last one is a terminator */
	for (i = 0; i < id_size; i++ ) {
		if (*(uint8_t*)(symval+size-id_size+i)) {
			fprintf(stderr,"%s: struct %s_device_id is %lu bytes.  "
				"The last of %lu is:\n",
				modname, device_id, id_size, size / id_size);
			for (i = 0; i < id_size; i++ )
				fprintf(stderr,"0x%02x ",
					*(uint8_t*)(symval+size-id_size+i) );
			fprintf(stderr,"\n");
			fatal("%s: struct %s_device_id is not terminated "
				"with a NULL entry!\n", modname, device_id);
		}
	}
/* 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)
Linus Torvalds's avatar
Linus Torvalds committed
{
	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);

Linus Torvalds's avatar
Linus Torvalds committed
	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)
Loading
Loading full blame...