X-Git-Url: http://mj.ucw.cz/gitweb/?a=blobdiff_plain;ds=sidebyside;f=lib%2Ffilter.c;h=703845156c5d75c26a96e4b986bb95bf587daffa;hb=9001c06d1e15c9e0325d6036a0bb38fb1460239d;hp=375293a7ae3bd8cd9b568fe41d69ef333fed7048;hpb=8ab74b693de8b22f6f9b48ac80b6a3a013ddc55a;p=pciutils.git diff --git a/lib/filter.c b/lib/filter.c index 375293a..7038451 100644 --- a/lib/filter.c +++ b/lib/filter.c @@ -1,7 +1,7 @@ /* * The PCI Library -- Device Filtering * - * Copyright (c) 1998--2014 Martin Mares + * Copyright (c) 1998--2022 Martin Mares * * Can be freely distributed and used under the terms of the GNU GPL. */ @@ -11,116 +11,184 @@ #include "internal.h" -void pci_filter_init_v33(struct pci_access *a UNUSED, struct pci_filter *f) VERSIONED_ABI; -char *pci_filter_parse_slot_v33(struct pci_filter *f, char *str) VERSIONED_ABI; -char *pci_filter_parse_id_v33(struct pci_filter *f, char *str) VERSIONED_ABI; -int pci_filter_match_v33(struct pci_filter *f, struct pci_dev *d) VERSIONED_ABI; +void pci_filter_init_v38(struct pci_access *a UNUSED, struct pci_filter *f) VERSIONED_ABI; +char *pci_filter_parse_slot_v38(struct pci_filter *f, char *str) VERSIONED_ABI; +char *pci_filter_parse_id_v38(struct pci_filter *f, char *str) VERSIONED_ABI; +int pci_filter_match_v38(struct pci_filter *f, struct pci_dev *d) VERSIONED_ABI; void -pci_filter_init_v33(struct pci_access *a UNUSED, struct pci_filter *f) +pci_filter_init_v38(struct pci_access *a UNUSED, struct pci_filter *f) { + memset((byte *) f, 0, sizeof(*f)); f->domain = f->bus = f->slot = f->func = -1; - f->vendor = f->device = f->device_class = -1; + f->vendor = f->device = -1; + f->device_class = -1; + f->device_class_mask = ~0U; + f->prog_if = -1; } -/* Slot filter syntax: [[[domain]:][bus]:][slot][.[func]] */ +#define BUF_SIZE 64 -char * -pci_filter_parse_slot_v33(struct pci_filter *f, char *str) +static char * +split_to_fields(char *str, char *buffer, int sep, char **fields, int num_fields) { - char *colon = strrchr(str, ':'); - char *dot = strchr((colon ? colon + 1 : str), '.'); - char *mid = str; - char *e, *bus, *colon2; + if (buffer) + { + if (strlen(str) >= BUF_SIZE) + return "Expression too long"; + strcpy(buffer, str); + str = buffer; + } - if (colon) + int i = 0; + + for (;;) { - *colon++ = 0; - mid = colon; - colon2 = strchr(str, ':'); - if (colon2) + if (i >= num_fields) + return "Too many fields"; + fields[i++] = str; + while (*str && *str != sep) + str++; + if (!*str) + break; + *str++ = 0; + } + + while (i < num_fields) + fields[i++] = NULL; + + return NULL; +} + +static int +field_defined(char *field) +{ + return field && field[0] && strcmp(field, "*"); +} + +static int +parse_hex_field(char *str, int *outp, unsigned int *maskp, unsigned int max) +{ + unsigned int out = 0; + unsigned int mask = ~0U; + unsigned int bound = 0; + + if (!field_defined(str)) + return 1; // and keep the defaults + + while (*str) + { + int c = *str++; + int d; + + if ((c == 'x' || c == 'X') && maskp) { - *colon2++ = 0; - bus = colon2; - if (str[0] && strcmp(str, "*")) - { - long int x = strtol(str, &e, 16); - if ((e && *e) || (x < 0 || x > 0xffff)) - return "Invalid domain number"; - f->domain = x; - } + out = out << 4; + bound = (bound << 4) | 1; + mask = mask << 4; } else - bus = str; - if (bus[0] && strcmp(bus, "*")) { - long int x = strtol(bus, &e, 16); - if ((e && *e) || (x < 0 || x > 0xff)) - return "Invalid bus number"; - f->bus = x; + if (c >= '0' && c <= '9') + d = c - '0'; + else if (c >= 'A' && c <= 'F') + d = c - 'A' + 10; + else if (c >= 'a' && c <= 'f') + d = c - 'a' + 10; + else + return 0; + + out = (out << 4) | d; + bound = (bound << 4) | d; + mask = (mask << 4) | 0xf; } + + if (bound > max) + return 0; } - if (dot) - *dot++ = 0; - if (mid[0] && strcmp(mid, "*")) - { - long int x = strtol(mid, &e, 16); - if ((e && *e) || (x < 0 || x > 0x1f)) - return "Invalid slot number"; - f->slot = x; - } - if (dot && dot[0] && strcmp(dot, "*")) - { - long int x = strtol(dot, &e, 16); - if ((e && *e) || (x < 0 || x > 7)) - return "Invalid function number"; - f->func = x; - } - return NULL; + + *outp = out; + if (maskp) + *maskp = mask; + return 1; } -/* ID filter syntax: [vendor]:[device][:class] */ +/* Slot filter syntax: [[[domain]:][bus]:][slot][.[func]] */ char * -pci_filter_parse_id_v33(struct pci_filter *f, char *str) +pci_filter_parse_slot_v38(struct pci_filter *f, char *str) { - char *s, *c, *e; - - if (!*str) - return NULL; - s = strchr(str, ':'); - if (!s) - return "':' expected"; - *s++ = 0; - if (str[0] && strcmp(str, "*")) + char buf[BUF_SIZE]; + char *fields[3]; + char *err; + + if (err = split_to_fields(str, buf, ':', fields, 3)) + return err; + + int i = 0; + if (fields[2]) { - long int x = strtol(str, &e, 16); - if ((e && *e) || (x < 0 || x > 0xffff)) - return "Invalid vendor ID"; - f->vendor = x; + if (!parse_hex_field(fields[0], &f->domain, NULL, 0x7fffffff)) + return "Invalid domain number"; + i++; } - c = strchr(s, ':'); - if (c) - *c++ = 0; - if (s[0] && strcmp(s, "*")) + + if (fields[i+1]) { - long int x = strtol(s, &e, 16); - if ((e && *e) || (x < 0 || x > 0xffff)) - return "Invalid device ID"; - f->device = x; + if (!parse_hex_field(fields[i], &f->bus, NULL, 0xff)) + return "Invalid bus number"; + i++; } - if (c && c[0] && strcmp(s, "*")) + + char *fdev = fields[i]; + if (field_defined(fdev)) { - long int x = strtol(c, &e, 16); - if ((e && *e) || (x < 0 || x > 0xffff)) - return "Invalid class code"; - f->device_class = x; + char *sfields[2]; + if (split_to_fields(fdev, NULL, '.', sfields, 2)) + return "Invalid slot/function number"; + + if (!parse_hex_field(sfields[0], &f->slot, NULL, 0x1f)) + return "Invalid slot number"; + + if (!parse_hex_field(sfields[1], &f->func, NULL, 7)) + return "Invalid function number"; } + + return NULL; +} + +/* ID filter syntax: [vendor]:[device][:class[:progif]] */ + +char * +pci_filter_parse_id_v38(struct pci_filter *f, char *str) +{ + char buf[BUF_SIZE]; + char *fields[4]; + char *err; + + if (err = split_to_fields(str, buf, ':', fields, 4)) + return err; + + if (!fields[1]) + return "At least two fields must be given"; + + if (!parse_hex_field(fields[0], &f->vendor, NULL, 0xffff)) + return "Invalid vendor ID"; + + if (!parse_hex_field(fields[1], &f->device, NULL, 0xffff)) + return "Invalid device ID"; + + if (!parse_hex_field(fields[2], &f->device_class, &f->device_class_mask, 0xffff)) + return "Invalid class code"; + + if (!parse_hex_field(fields[3], &f->prog_if, NULL, 0xff)) + return "Invalid programming interface code"; + return NULL; } int -pci_filter_match_v33(struct pci_filter *f, struct pci_dev *d) +pci_filter_match_v38(struct pci_filter *f, struct pci_dev *d) { if ((f->domain >= 0 && f->domain != d->domain) || (f->bus >= 0 && f->bus != d->bus) || @@ -129,15 +197,21 @@ pci_filter_match_v33(struct pci_filter *f, struct pci_dev *d) return 0; if (f->device >= 0 || f->vendor >= 0) { - pci_fill_info_v33(d, PCI_FILL_IDENT); + pci_fill_info_v38(d, PCI_FILL_IDENT); if ((f->device >= 0 && f->device != d->device_id) || (f->vendor >= 0 && f->vendor != d->vendor_id)) return 0; } if (f->device_class >= 0) { - pci_fill_info(d, PCI_FILL_CLASS); - if (f->device_class != d->device_class) + pci_fill_info_v38(d, PCI_FILL_CLASS); + if ((f->device_class ^ d->device_class) & f->device_class_mask) + return 0; + } + if (f->prog_if >= 0) + { + pci_fill_info_v38(d, PCI_FILL_CLASS_EXT); + if (f->prog_if != d->prog_if) return 0; } return 1; @@ -168,6 +242,8 @@ pci_filter_import_v30(struct pci_filter_v30 *old, struct pci_filter *new) new->vendor = old->vendor; new->device = old->device; new->device_class = -1; + new->device_class_mask = ~0U; + new->prog_if = -1; } static void @@ -185,7 +261,7 @@ void pci_filter_init_v30(struct pci_access *a, struct pci_filter_v30 *f) { struct pci_filter new; - pci_filter_init_v33(a, &new); + pci_filter_init_v38(a, &new); pci_filter_export_v30(&new, f); } @@ -195,7 +271,7 @@ pci_filter_parse_slot_v30(struct pci_filter_v30 *f, char *str) struct pci_filter new; char *err; pci_filter_import_v30(f, &new); - if (err = pci_filter_parse_slot_v33(&new, str)) + if (err = pci_filter_parse_slot_v38(&new, str)) return err; pci_filter_export_v30(&new, f); return NULL; @@ -207,10 +283,10 @@ pci_filter_parse_id_v30(struct pci_filter_v30 *f, char *str) struct pci_filter new; char *err; pci_filter_import_v30(f, &new); - if (err = pci_filter_parse_id_v33(&new, str)) + if (err = pci_filter_parse_id_v38(&new, str)) return err; - if (new.device_class >= 0) - return "Filtering by class not supported in this program"; + if (new.device_class >= 0 || new.prog_if >= 0) + return "Filtering by class or programming interface not supported in this program"; pci_filter_export_v30(&new, f); return NULL; } @@ -220,21 +296,32 @@ pci_filter_match_v30(struct pci_filter_v30 *f, struct pci_dev *d) { struct pci_filter new; pci_filter_import_v30(f, &new); - return pci_filter_match_v33(&new, d); + return pci_filter_match_v38(&new, d); } -STATIC_ALIAS(void pci_filter_init(struct pci_access *a, struct pci_filter *f), pci_filter_init_v33(a, f)); +// Version 3.3 is the same as version 3.8, only device_class_mask and prog_if were not implemented +// (their positions in struct pci_filter were declared as RFU). + +STATIC_ALIAS(void pci_filter_init(struct pci_access *a, struct pci_filter *f), pci_filter_init_v38(a, f)); +DEFINE_ALIAS(void pci_filter_init_v33(struct pci_access *a, struct pci_filter *f), pci_filter_init_v38); SYMBOL_VERSION(pci_filter_init_v30, pci_filter_init@LIBPCI_3.0); -SYMBOL_VERSION(pci_filter_init_v33, pci_filter_init@@LIBPCI_3.3); +SYMBOL_VERSION(pci_filter_init_v33, pci_filter_init@LIBPCI_3.3); +SYMBOL_VERSION(pci_filter_init_v38, pci_filter_init@@LIBPCI_3.8); -STATIC_ALIAS(char *pci_filter_parse_slot(struct pci_filter *f, char *str), pci_filter_parse_slot_v33(f, str)); +STATIC_ALIAS(char *pci_filter_parse_slot(struct pci_filter *f, char *str), pci_filter_parse_slot_v38(f, str)); +DEFINE_ALIAS(char *pci_filter_parse_slot_v33(struct pci_filter *f, char *str), pci_filter_parse_slot_v38); SYMBOL_VERSION(pci_filter_parse_slot_v30, pci_filter_parse_slot@LIBPCI_3.0); -SYMBOL_VERSION(pci_filter_parse_slot_v33, pci_filter_parse_slot@@LIBPCI_3.3); +SYMBOL_VERSION(pci_filter_parse_slot_v33, pci_filter_parse_slot@LIBPCI_3.3); +SYMBOL_VERSION(pci_filter_parse_slot_v38, pci_filter_parse_slot@@LIBPCI_3.8); -STATIC_ALIAS(char *pci_filter_parse_id(struct pci_filter *f, char *str), pci_filter_parse_id_v33(f, str)); +STATIC_ALIAS(char *pci_filter_parse_id(struct pci_filter *f, char *str), pci_filter_parse_id_v38(f, str)); +DEFINE_ALIAS(char *pci_filter_parse_id_v33(struct pci_filter *f, char *str), pci_filter_parse_id_v38); SYMBOL_VERSION(pci_filter_parse_id_v30, pci_filter_parse_id@LIBPCI_3.0); -SYMBOL_VERSION(pci_filter_parse_id_v33, pci_filter_parse_id@@LIBPCI_3.3); +SYMBOL_VERSION(pci_filter_parse_id_v33, pci_filter_parse_id@LIBPCI_3.3); +SYMBOL_VERSION(pci_filter_parse_id_v38, pci_filter_parse_id@@LIBPCI_3.8); -STATIC_ALIAS(int pci_filter_match(struct pci_filter *f, struct pci_dev *d), pci_filter_match_v33(f, d)); +STATIC_ALIAS(int pci_filter_match(struct pci_filter *f, struct pci_dev *d), pci_filter_match_v38(f, d)); +DEFINE_ALIAS(int pci_filter_match_v33(struct pci_filter *f, struct pci_dev *d), pci_filter_match_v38); SYMBOL_VERSION(pci_filter_match_v30, pci_filter_match@LIBPCI_3.0); -SYMBOL_VERSION(pci_filter_match_v33, pci_filter_match@@LIBPCI_3.3); +SYMBOL_VERSION(pci_filter_match_v33, pci_filter_match@LIBPCI_3.3); +SYMBOL_VERSION(pci_filter_match_v38, pci_filter_match@@LIBPCI_3.8);