summaryrefslogtreecommitdiff
path: root/scripts/file2alias.c
diff options
context:
space:
mode:
authorRusty Russell <rusty@rustcorp.com.au>2003-02-14 08:35:21 -0600
committerKai Germaschewski <kai@tp1.ruhr-uni-bochum.de>2003-02-14 08:35:21 -0600
commit0e5064f80d41d75abbb10e040bbd7c600e08299c (patch)
tree6eb8bdc574cf01754670b2f34396b1a152092b1c /scripts/file2alias.c
parent0571739cd43b80de3bfa08f55fbcf410878cbe75 (diff)
kbuild: Module alias and device table support
Introduces "MODULE_ALIAS" which modules can use to embed their own aliases for modprobe to use. Also adds a "finishing" step to modules to supplement their aliases based on MODULE_TABLE declarations, eg. 'usb:v0506p4601dl*dh*dc*dsc*dp*ic*isc*ip*' for drivers/usb/net/pegasus.o
Diffstat (limited to 'scripts/file2alias.c')
-rw-r--r--scripts/file2alias.c259
1 files changed, 259 insertions, 0 deletions
diff --git a/scripts/file2alias.c b/scripts/file2alias.c
new file mode 100644
index 000000000000..525a766bab3e
--- /dev/null
+++ b/scripts/file2alias.c
@@ -0,0 +1,259 @@
+/* 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.
+ (C) 2002 Rusty Russell IBM Corporation.
+*/
+#include <stdio.h>
+#include <fcntl.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <elf.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+/* 32 bits: if it turns out to be 64, we add explicitly (see EXTRA_SIZE). */
+typedef int kernel_ulong_t;
+#include "../include/linux/types.h"
+#include "../include/linux/mod_devicetable.h"
+
+static int switch_endian;
+
+static void __endian(const void *src, void *dest, unsigned int size)
+{
+ unsigned int i;
+ for (i = 0; i < size; i++)
+ ((unsigned char*)dest)[i] = ((unsigned char*)src)[size - i-1];
+}
+
+#define TO_NATIVE(x) \
+({ \
+ typeof(x) __x; \
+ if (switch_endian) __endian(&(x), &(__x), sizeof(__x)); \
+ else __x = x; \
+ __x; \
+})
+
+#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)
+
+/* Looks like "usb:vNpNdlNdhNdcNdscNdpNicNiscNipN" */
+static int do_usb_entry(const char *filename,
+ struct usb_device_id *id, char *alias)
+{
+ id->match_flags = TO_NATIVE(id->match_flags);
+ id->idVendor = TO_NATIVE(id->idVendor);
+ id->idProduct = TO_NATIVE(id->idProduct);
+ id->bcdDevice_lo = TO_NATIVE(id->bcdDevice_lo);
+ id->bcdDevice_hi = TO_NATIVE(id->bcdDevice_hi);
+
+ strcpy(alias, "usb:");
+ ADD(alias, "v", id->match_flags&USB_DEVICE_ID_MATCH_VENDOR,
+ id->idVendor);
+ ADD(alias, "p", id->match_flags&USB_DEVICE_ID_MATCH_PRODUCT,
+ id->idProduct);
+ ADD(alias, "dl", id->match_flags&USB_DEVICE_ID_MATCH_DEV_LO,
+ id->bcdDevice_lo);
+ ADD(alias, "dh", id->match_flags&USB_DEVICE_ID_MATCH_DEV_HI,
+ id->bcdDevice_hi);
+ ADD(alias, "dc", id->match_flags&USB_DEVICE_ID_MATCH_DEV_CLASS,
+ id->bDeviceClass);
+ ADD(alias, "dsc",
+ id->match_flags&USB_DEVICE_ID_MATCH_DEV_SUBCLASS,
+ id->bDeviceSubClass);
+ ADD(alias, "dp",
+ id->match_flags&USB_DEVICE_ID_MATCH_DEV_PROTOCOL,
+ id->bDeviceProtocol);
+ ADD(alias, "ic",
+ id->match_flags&USB_DEVICE_ID_MATCH_INT_CLASS,
+ id->bInterfaceClass);
+ ADD(alias, "isc",
+ id->match_flags&USB_DEVICE_ID_MATCH_INT_SUBCLASS,
+ id->bInterfaceSubClass);
+ ADD(alias, "ip",
+ id->match_flags&USB_DEVICE_ID_MATCH_INT_PROTOCOL,
+ id->bInterfaceProtocol);
+ return 1;
+}
+
+/* Looks like: pci:vNdNsvNsdNcN. */
+static int do_pci_entry(const char *filename,
+ struct pci_device_id *id, char *alias)
+{
+ id->vendor = TO_NATIVE(id->vendor);
+ id->device = TO_NATIVE(id->device);
+ id->subvendor = TO_NATIVE(id->subvendor);
+ id->subdevice = TO_NATIVE(id->subdevice);
+ id->class = TO_NATIVE(id->class);
+ id->class_mask = TO_NATIVE(id->class_mask);
+
+ strcpy(alias, "pci:");
+ ADD(alias, "v", id->vendor != PCI_ANY_ID, id->vendor);
+ ADD(alias, "d", id->device != PCI_ANY_ID, id->device);
+ ADD(alias, "sv", id->subvendor != PCI_ANY_ID, id->subvendor);
+ ADD(alias, "sd", id->subdevice != PCI_ANY_ID, id->subdevice);
+ if (id->class_mask != 0 && id->class_mask != ~0) {
+ fprintf(stderr,
+ "file2alias: Can't handle class_mask in %s:%04X\n",
+ filename, id->class_mask);
+ return 0;
+ }
+ ADD(alias, "c", id->class_mask == ~0, id->class);
+ return 1;
+}
+
+/* Ignore any prefix, eg. v850 prepends _ */
+static inline int sym_is(const char *symbol, const char *name)
+{
+ const char *match;
+
+ match = strstr(symbol, name);
+ if (!match)
+ return 0;
+ return match[strlen(symbol)] == '\0';
+}
+
+/* Returns 1 if we output anything. */
+static int do_table(void *symval, unsigned long size,
+ unsigned long id_size,
+ void *function,
+ const char *filename, int *first)
+{
+ unsigned int i;
+ char alias[500];
+ int (*do_entry)(const char *, void *entry, char *alias) = function;
+ int wrote = 0;
+
+ if (size % id_size || size < id_size) {
+ fprintf(stderr, "WARNING: %s ids %lu bad size (each on %lu)\n",
+ filename, size, id_size);
+ return 0;
+ }
+ /* Leave last one: it's the terminator. */
+ size -= id_size;
+
+ for (i = 0; i < size; i += id_size) {
+ if (do_entry(filename, symval+i, alias)) {
+ /* Always end in a wildcard, for future extension */
+ if (alias[strlen(alias)-1] != '*')
+ strcat(alias, "*");
+ if (*first) {
+ printf("#include <linux/module.h>\n\n");
+ *first = 0;
+ }
+ printf("MODULE_ALIAS(\"%s\");\n", alias);
+ wrote = 1;
+ }
+ }
+ return wrote;
+}
+
+/* This is the best way of doing this without making a complete mess
+ of the code. */
+#undef analyse_file
+#undef Elf_Ehdr
+#undef Elf_Shdr
+#undef Elf_Sym
+#undef EXTRA_SIZE
+#define analyse_file analyze_file32
+#define Elf_Ehdr Elf32_Ehdr
+#define Elf_Shdr Elf32_Shdr
+#define Elf_Sym Elf32_Sym
+#define EXTRA_SIZE 0
+#include "file2alias_inc.c"
+#undef analyse_file
+#undef Elf_Ehdr
+#undef Elf_Shdr
+#undef Elf_Sym
+#undef EXTRA_SIZE
+#define analyse_file analyze_file64
+#define Elf_Ehdr Elf64_Ehdr
+#define Elf_Shdr Elf64_Shdr
+#define Elf_Sym Elf64_Sym
+#define EXTRA_SIZE 4
+#include "file2alias_inc.c"
+
+static void *grab_file(const char *filename, unsigned long *size)
+{
+ struct stat st;
+ void *map;
+ int fd;
+
+ fd = open(filename, O_RDONLY);
+ if (fd < 0)
+ return NULL;
+ if (fstat(fd, &st) != 0) {
+ close(fd);
+ return NULL;
+ }
+ *size = st.st_size;
+ map = mmap(NULL, *size, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0);
+ if (mmap == MAP_FAILED) {
+ close(fd);
+ return NULL;
+ }
+ close(fd);
+ return map;
+}
+
+/* Look through files for __mod_*_device_table: emit alias definitions
+ for compiling in. */
+int main(int argc, char *argv[])
+{
+ void *file;
+ unsigned long size;
+ int endian;
+ union { short s; char c[2]; } endian_test;
+
+ endian_test.s = 1;
+ if (endian_test.c[1] == 1) endian = ELFDATA2MSB;
+ else if (endian_test.c[0] == 1) endian = ELFDATA2LSB;
+ else
+ abort();
+
+ for (; argv[1]; argv++) {
+ file = grab_file(argv[1], &size);
+ if (!file) {
+ fprintf(stderr, "file2alias: opening %s: %s\n",
+ argv[1], strerror(errno));
+ continue;
+ }
+
+ if (size < SELFMAG || memcmp(file, ELFMAG, SELFMAG) != 0)
+ goto bad_elf;
+
+ if (((unsigned char *)file)[EI_DATA] != endian)
+ switch_endian = 1;
+
+ switch (((unsigned char *)file)[EI_CLASS]) {
+ case ELFCLASS32:
+ analyze_file32(file, size, argv[1]);
+ break;
+ case ELFCLASS64:
+ analyze_file64(file, size, argv[1]);
+ break;
+ default:
+ goto bad_elf;
+ }
+ munmap(file, size);
+ continue;
+
+ bad_elf:
+ fprintf(stderr, "file2alias: %s is not elf\n", argv[1]);
+ return 1;
+ }
+ return 0;
+}