1 /* Simple code to turn various tables in an ELF file into alias definitions. 2 * This deals with kernel datastructures where they should be 3 * dealt with: in the kernel source. 4 * 5 * Copyright 2002-2003 Rusty Russell, IBM Corporation 6 * 2003 Kai Germaschewski 7 * 8 * 9 * This software may be used and distributed according to the terms 10 * of the GNU General Public License, incorporated herein by reference. 11 */ 12 13 #include "modpost.h" 14 15 /* We use the ELF typedefs for kernel_ulong_t but bite the bullet and 16 * use either stdint.h or inttypes.h for the rest. */ 17 #if KERNEL_ELFCLASS == ELFCLASS32 18 typedef Elf32_Addr kernel_ulong_t; 19 #else 20 typedef Elf64_Addr kernel_ulong_t; 21 #endif 22 #ifdef __sun__ 23 #include <inttypes.h> 24 #else 25 #include <stdint.h> 26 #endif 27 28 typedef uint32_t __u32; 29 typedef uint16_t __u16; 30 typedef unsigned char __u8; 31 32 /* Big exception to the "don't include kernel headers into userspace, which 33 * even potentially has different endianness and word sizes, since 34 * we handle those differences explicitly below */ 35 #include "../../include/linux/mod_devicetable.h" 36 37 #define ADD(str, sep, cond, field) \ 38 do { \ 39 strcat(str, sep); \ 40 if (cond) \ 41 sprintf(str + strlen(str), \ 42 sizeof(field) == 1 ? "%02X" : \ 43 sizeof(field) == 2 ? "%04X" : \ 44 sizeof(field) == 4 ? "%08X" : "", \ 45 field); \ 46 else \ 47 sprintf(str + strlen(str), "*"); \ 48 } while(0) 49 50 /* Looks like "usb:vNpNdlNdhNdcNdscNdpNicNiscNipN" */ 51 static int do_usb_entry(const char *filename, 52 struct usb_device_id *id, char *alias) 53 { 54 id->match_flags = TO_NATIVE(id->match_flags); 55 id->idVendor = TO_NATIVE(id->idVendor); 56 id->idProduct = TO_NATIVE(id->idProduct); 57 id->bcdDevice_lo = TO_NATIVE(id->bcdDevice_lo); 58 id->bcdDevice_hi = TO_NATIVE(id->bcdDevice_hi); 59 60 /* 61 * Some modules (visor) have empty slots as placeholder for 62 * run-time specification that results in catch-all alias 63 */ 64 if (!(id->idVendor | id->bDeviceClass | id->bInterfaceClass)) 65 return 1; 66 67 strcpy(alias, "usb:"); 68 ADD(alias, "v", id->match_flags&USB_DEVICE_ID_MATCH_VENDOR, 69 id->idVendor); 70 ADD(alias, "p", id->match_flags&USB_DEVICE_ID_MATCH_PRODUCT, 71 id->idProduct); 72 ADD(alias, "dl", id->match_flags&USB_DEVICE_ID_MATCH_DEV_LO, 73 id->bcdDevice_lo); 74 ADD(alias, "dh", id->match_flags&USB_DEVICE_ID_MATCH_DEV_HI, 75 id->bcdDevice_hi); 76 ADD(alias, "dc", id->match_flags&USB_DEVICE_ID_MATCH_DEV_CLASS, 77 id->bDeviceClass); 78 ADD(alias, "dsc", 79 id->match_flags&USB_DEVICE_ID_MATCH_DEV_SUBCLASS, 80 id->bDeviceSubClass); 81 ADD(alias, "dp", 82 id->match_flags&USB_DEVICE_ID_MATCH_DEV_PROTOCOL, 83 id->bDeviceProtocol); 84 ADD(alias, "ic", 85 id->match_flags&USB_DEVICE_ID_MATCH_INT_CLASS, 86 id->bInterfaceClass); 87 ADD(alias, "isc", 88 id->match_flags&USB_DEVICE_ID_MATCH_INT_SUBCLASS, 89 id->bInterfaceSubClass); 90 ADD(alias, "ip", 91 id->match_flags&USB_DEVICE_ID_MATCH_INT_PROTOCOL, 92 id->bInterfaceProtocol); 93 return 1; 94 } 95 96 /* Looks like: ieee1394:venNmoNspNverN */ 97 static int do_ieee1394_entry(const char *filename, 98 struct ieee1394_device_id *id, char *alias) 99 { 100 id->match_flags = TO_NATIVE(id->match_flags); 101 id->vendor_id = TO_NATIVE(id->vendor_id); 102 id->model_id = TO_NATIVE(id->model_id); 103 id->specifier_id = TO_NATIVE(id->specifier_id); 104 id->version = TO_NATIVE(id->version); 105 106 strcpy(alias, "ieee1394:"); 107 ADD(alias, "ven", id->match_flags & IEEE1394_MATCH_VENDOR_ID, 108 id->vendor_id); 109 ADD(alias, "mo", id->match_flags & IEEE1394_MATCH_MODEL_ID, 110 id->model_id); 111 ADD(alias, "sp", id->match_flags & IEEE1394_MATCH_SPECIFIER_ID, 112 id->specifier_id); 113 ADD(alias, "ver", id->match_flags & IEEE1394_MATCH_VERSION, 114 id->version); 115 116 return 1; 117 } 118 119 /* Looks like: pci:vNdNsvNsdNbcNscNiN. */ 120 static int do_pci_entry(const char *filename, 121 struct pci_device_id *id, char *alias) 122 { 123 /* Class field can be divided into these three. */ 124 unsigned char baseclass, subclass, interface, 125 baseclass_mask, subclass_mask, interface_mask; 126 127 id->vendor = TO_NATIVE(id->vendor); 128 id->device = TO_NATIVE(id->device); 129 id->subvendor = TO_NATIVE(id->subvendor); 130 id->subdevice = TO_NATIVE(id->subdevice); 131 id->class = TO_NATIVE(id->class); 132 id->class_mask = TO_NATIVE(id->class_mask); 133 134 strcpy(alias, "pci:"); 135 ADD(alias, "v", id->vendor != PCI_ANY_ID, id->vendor); 136 ADD(alias, "d", id->device != PCI_ANY_ID, id->device); 137 ADD(alias, "sv", id->subvendor != PCI_ANY_ID, id->subvendor); 138 ADD(alias, "sd", id->subdevice != PCI_ANY_ID, id->subdevice); 139 140 baseclass = (id->class) >> 16; 141 baseclass_mask = (id->class_mask) >> 16; 142 subclass = (id->class) >> 8; 143 subclass_mask = (id->class_mask) >> 8; 144 interface = id->class; 145 interface_mask = id->class_mask; 146 147 if ((baseclass_mask != 0 && baseclass_mask != 0xFF) 148 || (subclass_mask != 0 && subclass_mask != 0xFF) 149 || (interface_mask != 0 && interface_mask != 0xFF)) { 150 fprintf(stderr, 151 "*** Warning: Can't handle masks in %s:%04X\n", 152 filename, id->class_mask); 153 return 0; 154 } 155 156 ADD(alias, "bc", baseclass_mask == 0xFF, baseclass); 157 ADD(alias, "sc", subclass_mask == 0xFF, subclass); 158 ADD(alias, "i", interface_mask == 0xFF, interface); 159 return 1; 160 } 161 162 /* looks like: "ccw:tNmNdtNdmN" */ 163 static int do_ccw_entry(const char *filename, 164 struct ccw_device_id *id, char *alias) 165 { 166 id->match_flags = TO_NATIVE(id->match_flags); 167 id->cu_type = TO_NATIVE(id->cu_type); 168 id->cu_model = TO_NATIVE(id->cu_model); 169 id->dev_type = TO_NATIVE(id->dev_type); 170 id->dev_model = TO_NATIVE(id->dev_model); 171 172 strcpy(alias, "ccw:"); 173 ADD(alias, "t", id->match_flags&CCW_DEVICE_ID_MATCH_CU_TYPE, 174 id->cu_type); 175 ADD(alias, "m", id->match_flags&CCW_DEVICE_ID_MATCH_CU_MODEL, 176 id->cu_model); 177 ADD(alias, "dt", id->match_flags&CCW_DEVICE_ID_MATCH_DEVICE_TYPE, 178 id->dev_type); 179 ADD(alias, "dm", id->match_flags&CCW_DEVICE_ID_MATCH_DEVICE_TYPE, 180 id->dev_model); 181 return 1; 182 } 183 184 /* Looks like: "serio:tyNprNidNexN" */ 185 static int do_serio_entry(const char *filename, 186 struct serio_device_id *id, char *alias) 187 { 188 id->type = TO_NATIVE(id->type); 189 id->proto = TO_NATIVE(id->proto); 190 id->id = TO_NATIVE(id->id); 191 id->extra = TO_NATIVE(id->extra); 192 193 strcpy(alias, "serio:"); 194 ADD(alias, "ty", id->type != SERIO_ANY, id->type); 195 ADD(alias, "pr", id->proto != SERIO_ANY, id->proto); 196 ADD(alias, "id", id->id != SERIO_ANY, id->id); 197 ADD(alias, "ex", id->extra != SERIO_ANY, id->extra); 198 199 return 1; 200 } 201 202 /* looks like: "pnp:dD" */ 203 static int do_pnp_entry(const char *filename, 204 struct pnp_device_id *id, char *alias) 205 { 206 sprintf(alias, "pnp:d%s", id->id); 207 return 1; 208 } 209 210 /* looks like: "pnp:cCdD..." */ 211 static int do_pnp_card_entry(const char *filename, 212 struct pnp_card_device_id *id, char *alias) 213 { 214 int i; 215 216 sprintf(alias, "pnp:c%s", id->id); 217 for (i = 0; i < PNP_MAX_DEVICES; i++) { 218 if (! *id->devs[i].id) 219 break; 220 sprintf(alias + strlen(alias), "d%s", id->devs[i].id); 221 } 222 return 1; 223 } 224 225 /* Ignore any prefix, eg. v850 prepends _ */ 226 static inline int sym_is(const char *symbol, const char *name) 227 { 228 const char *match; 229 230 match = strstr(symbol, name); 231 if (!match) 232 return 0; 233 return match[strlen(symbol)] == '\0'; 234 } 235 236 static void do_table(void *symval, unsigned long size, 237 unsigned long id_size, 238 void *function, 239 struct module *mod) 240 { 241 unsigned int i; 242 char alias[500]; 243 int (*do_entry)(const char *, void *entry, char *alias) = function; 244 245 if (size % id_size || size < id_size) { 246 fprintf(stderr, "*** Warning: %s ids %lu bad size " 247 "(each on %lu)\n", mod->name, size, id_size); 248 } 249 /* Leave last one: it's the terminator. */ 250 size -= id_size; 251 252 for (i = 0; i < size; i += id_size) { 253 if (do_entry(mod->name, symval+i, alias)) { 254 /* Always end in a wildcard, for future extension */ 255 if (alias[strlen(alias)-1] != '*') 256 strcat(alias, "*"); 257 buf_printf(&mod->dev_table_buf, 258 "MODULE_ALIAS(\"%s\");\n", alias); 259 } 260 } 261 } 262 263 /* Create MODULE_ALIAS() statements. 264 * At this time, we cannot write the actual output C source yet, 265 * so we write into the mod->dev_table_buf buffer. */ 266 void handle_moddevtable(struct module *mod, struct elf_info *info, 267 Elf_Sym *sym, const char *symname) 268 { 269 void *symval; 270 271 /* We're looking for a section relative symbol */ 272 if (!sym->st_shndx || sym->st_shndx >= info->hdr->e_shnum) 273 return; 274 275 symval = (void *)info->hdr 276 + info->sechdrs[sym->st_shndx].sh_offset 277 + sym->st_value; 278 279 if (sym_is(symname, "__mod_pci_device_table")) 280 do_table(symval, sym->st_size, sizeof(struct pci_device_id), 281 do_pci_entry, mod); 282 else if (sym_is(symname, "__mod_usb_device_table")) 283 do_table(symval, sym->st_size, sizeof(struct usb_device_id), 284 do_usb_entry, mod); 285 else if (sym_is(symname, "__mod_ieee1394_device_table")) 286 do_table(symval, sym->st_size, sizeof(struct ieee1394_device_id), 287 do_ieee1394_entry, mod); 288 else if (sym_is(symname, "__mod_ccw_device_table")) 289 do_table(symval, sym->st_size, sizeof(struct ccw_device_id), 290 do_ccw_entry, mod); 291 else if (sym_is(symname, "__mod_serio_device_table")) 292 do_table(symval, sym->st_size, sizeof(struct serio_device_id), 293 do_serio_entry, mod); 294 else if (sym_is(symname, "__mod_pnp_device_table")) 295 do_table(symval, sym->st_size, sizeof(struct pnp_device_id), 296 do_pnp_entry, mod); 297 else if (sym_is(symname, "__mod_pnp_card_device_table")) 298 do_table(symval, sym->st_size, sizeof(struct pnp_card_device_id), 299 do_pnp_card_entry, mod); 300 } 301 302 /* Now add out buffered information to the generated C source */ 303 void add_moddevtable(struct buffer *buf, struct module *mod) 304 { 305 buf_printf(buf, "\n"); 306 buf_write(buf, mod->dev_table_buf.p, mod->dev_table_buf.pos); 307 free(mod->dev_table_buf.p); 308 } 309