/* 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 == ELFCLASS32
typedef Elf32_Addr kernel_ulong_t;
#define BITS_PER_LONG 32
#else
typedef 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 = \
get_unaligned_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;
}
s
|