Loading...
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 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 | /* 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" /* We use the ELF typedefs, since we can't rely on stdint.h being present. */ #if KERNEL_ELFCLASS == ELFCLASS32 typedef Elf32_Addr kernel_ulong_t; #else typedef Elf64_Addr kernel_ulong_t; #endif typedef Elf32_Word __u32; typedef Elf32_Half __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 * we handle those differences explicitly below */ #include "../include/linux/mod_devicetable.h" #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, "*** Warning: 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'; } static void do_table(void *symval, unsigned long size, unsigned long id_size, void *function, struct module *mod) { unsigned int i; char alias[500]; int (*do_entry)(const char *, void *entry, char *alias) = function; if (size % id_size || size < id_size) { fprintf(stderr, "*** Warning: %s ids %lu bad size " "(each on %lu)\n", mod->name, size, id_size); } /* Leave last one: it's the terminator. */ size -= id_size; for (i = 0; i < size; i += id_size) { if (do_entry(mod->name, symval+i, alias)) { /* Always end in a wildcard, for future extension */ if (alias[strlen(alias)-1] != '*') strcat(alias, "*"); buf_printf(&mod->dev_table_buf, "MODULE_ALIAS(\"%s\");\n", alias); } } } /* 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; /* We're looking for a section relative symbol */ if (!sym->st_shndx || sym->st_shndx >= info->hdr->e_shnum) return; symval = (void *)info->hdr + info->sechdrs[sym->st_shndx].sh_offset + sym->st_value; if (sym_is(symname, "__mod_pci_device_table")) do_table(symval, sym->st_size, sizeof(struct pci_device_id), do_pci_entry, mod); else if (sym_is(symname, "__mod_usb_device_table")) do_table(symval, sym->st_size, sizeof(struct usb_device_id), do_usb_entry, mod); } /* Now add out buffered information to the generated C source */ void add_moddevtable(struct buffer *buf, struct module *mod) { buf_printf(buf, "\n"); buf_write(buf, mod->dev_table_buf.p, mod->dev_table_buf.pos); free(mod->dev_table_buf.p); } |