]>
Commit | Line | Data |
---|---|---|
e4842ff3 | 1 | /* |
4284af58 | 2 | * The PCI Library -- Device Filtering |
e4842ff3 | 3 | * |
511122b8 | 4 | * Copyright (c) 1998--2022 Martin Mares <mj@ucw.cz> |
e4842ff3 MM |
5 | * |
6 | * Can be freely distributed and used under the terms of the GNU GPL. | |
7 | */ | |
8 | ||
e4842ff3 MM |
9 | #include <stdlib.h> |
10 | #include <string.h> | |
e4842ff3 | 11 | |
727ce158 | 12 | #include "internal.h" |
e4842ff3 | 13 | |
511122b8 MM |
14 | void pci_filter_init_v38(struct pci_access *a UNUSED, struct pci_filter *f) VERSIONED_ABI; |
15 | char *pci_filter_parse_slot_v38(struct pci_filter *f, char *str) VERSIONED_ABI; | |
16 | char *pci_filter_parse_id_v38(struct pci_filter *f, char *str) VERSIONED_ABI; | |
17 | int pci_filter_match_v38(struct pci_filter *f, struct pci_dev *d) VERSIONED_ABI; | |
52aecc75 | 18 | |
e4842ff3 | 19 | void |
511122b8 | 20 | pci_filter_init_v38(struct pci_access *a UNUSED, struct pci_filter *f) |
e4842ff3 | 21 | { |
511122b8 | 22 | memset((byte *) f, 0, sizeof(*f)); |
84c8d1bb | 23 | f->domain = f->bus = f->slot = f->func = -1; |
511122b8 MM |
24 | f->vendor = f->device = -1; |
25 | f->device_class = -1; | |
26 | f->device_class_mask = ~0U; | |
27 | f->prog_if = -1; | |
e4842ff3 MM |
28 | } |
29 | ||
511122b8 | 30 | #define BUF_SIZE 64 |
e4842ff3 | 31 | |
511122b8 MM |
32 | static char * |
33 | split_to_fields(char *str, char *buffer, int sep, char **fields, int num_fields) | |
e4842ff3 | 34 | { |
511122b8 MM |
35 | if (buffer) |
36 | { | |
37 | if (strlen(str) >= BUF_SIZE) | |
38 | return "Expression too long"; | |
39 | strcpy(buffer, str); | |
40 | str = buffer; | |
41 | } | |
e4842ff3 | 42 | |
511122b8 MM |
43 | int i = 0; |
44 | ||
45 | for (;;) | |
e4842ff3 | 46 | { |
511122b8 MM |
47 | if (i >= num_fields) |
48 | return "Too many fields"; | |
49 | fields[i++] = str; | |
50 | while (*str && *str != sep) | |
51 | str++; | |
52 | if (!*str) | |
53 | break; | |
54 | *str++ = 0; | |
55 | } | |
56 | ||
57 | while (i < num_fields) | |
58 | fields[i++] = NULL; | |
59 | ||
60 | return NULL; | |
61 | } | |
62 | ||
63 | static int | |
64 | field_defined(char *field) | |
65 | { | |
66 | return field && field[0] && strcmp(field, "*"); | |
67 | } | |
68 | ||
69 | static int | |
70 | parse_hex_field(char *str, int *outp, unsigned int *maskp, unsigned int max) | |
71 | { | |
72 | unsigned int out = 0; | |
73 | unsigned int mask = ~0U; | |
74 | unsigned int bound = 0; | |
75 | ||
76 | if (!field_defined(str)) | |
77 | return 1; // and keep the defaults | |
78 | ||
79 | while (*str) | |
80 | { | |
81 | int c = *str++; | |
82 | int d; | |
83 | ||
84 | if ((c == 'x' || c == 'X') && maskp) | |
e4842ff3 | 85 | { |
511122b8 MM |
86 | out = out << 4; |
87 | bound = (bound << 4) | 1; | |
88 | mask = mask << 4; | |
84c8d1bb MM |
89 | } |
90 | else | |
84c8d1bb | 91 | { |
511122b8 MM |
92 | if (c >= '0' && c <= '9') |
93 | d = c - '0'; | |
94 | else if (c >= 'A' && c <= 'F') | |
95 | d = c - 'A' + 10; | |
96 | else if (c >= 'a' && c <= 'f') | |
97 | d = c - 'a' + 10; | |
98 | else | |
99 | return 0; | |
100 | ||
101 | out = (out << 4) | d; | |
102 | bound = (bound << 4) | d; | |
103 | mask = (mask << 4) | 0xf; | |
e4842ff3 | 104 | } |
511122b8 MM |
105 | |
106 | if (bound > max) | |
107 | return 0; | |
e4842ff3 | 108 | } |
511122b8 MM |
109 | |
110 | *outp = out; | |
111 | if (maskp) | |
112 | *maskp = mask; | |
113 | return 1; | |
e4842ff3 MM |
114 | } |
115 | ||
511122b8 | 116 | /* Slot filter syntax: [[[domain]:][bus]:][slot][.[func]] */ |
e4842ff3 MM |
117 | |
118 | char * | |
511122b8 | 119 | pci_filter_parse_slot_v38(struct pci_filter *f, char *str) |
e4842ff3 | 120 | { |
511122b8 MM |
121 | char buf[BUF_SIZE]; |
122 | char *fields[3]; | |
123 | char *err; | |
124 | ||
125 | if (err = split_to_fields(str, buf, ':', fields, 3)) | |
126 | return err; | |
127 | ||
128 | int i = 0; | |
129 | if (fields[2]) | |
e4842ff3 | 130 | { |
511122b8 MM |
131 | if (!parse_hex_field(fields[0], &f->domain, NULL, 0x7fffffff)) |
132 | return "Invalid domain number"; | |
133 | i++; | |
e4842ff3 | 134 | } |
511122b8 MM |
135 | |
136 | if (fields[i+1]) | |
e4842ff3 | 137 | { |
511122b8 MM |
138 | if (!parse_hex_field(fields[i], &f->bus, NULL, 0xff)) |
139 | return "Invalid bus number"; | |
140 | i++; | |
e4842ff3 | 141 | } |
511122b8 MM |
142 | |
143 | char *fdev = fields[i]; | |
144 | if (field_defined(fdev)) | |
4d1c9525 | 145 | { |
511122b8 MM |
146 | char *sfields[2]; |
147 | if (split_to_fields(fdev, NULL, '.', sfields, 2)) | |
148 | return "Invalid slot/function number"; | |
149 | ||
150 | if (!parse_hex_field(sfields[0], &f->slot, NULL, 0x1f)) | |
151 | return "Invalid slot number"; | |
152 | ||
153 | if (!parse_hex_field(sfields[1], &f->func, NULL, 7)) | |
154 | return "Invalid function number"; | |
4d1c9525 | 155 | } |
511122b8 MM |
156 | |
157 | return NULL; | |
158 | } | |
159 | ||
160 | /* ID filter syntax: [vendor]:[device][:class[:progif]] */ | |
161 | ||
162 | char * | |
163 | pci_filter_parse_id_v38(struct pci_filter *f, char *str) | |
164 | { | |
165 | char buf[BUF_SIZE]; | |
166 | char *fields[4]; | |
167 | char *err; | |
168 | ||
169 | if (err = split_to_fields(str, buf, ':', fields, 4)) | |
170 | return err; | |
171 | ||
172 | if (!fields[1]) | |
173 | return "At least two fields must be given"; | |
174 | ||
175 | if (!parse_hex_field(fields[0], &f->vendor, NULL, 0xffff)) | |
176 | return "Invalid vendor ID"; | |
177 | ||
178 | if (!parse_hex_field(fields[1], &f->device, NULL, 0xffff)) | |
179 | return "Invalid device ID"; | |
180 | ||
181 | if (!parse_hex_field(fields[2], &f->device_class, &f->device_class_mask, 0xffff)) | |
182 | return "Invalid class code"; | |
183 | ||
184 | if (!parse_hex_field(fields[3], &f->prog_if, NULL, 0xff)) | |
185 | return "Invalid programming interface code"; | |
186 | ||
e4842ff3 MM |
187 | return NULL; |
188 | } | |
189 | ||
190 | int | |
511122b8 | 191 | pci_filter_match_v38(struct pci_filter *f, struct pci_dev *d) |
e4842ff3 | 192 | { |
84c8d1bb MM |
193 | if ((f->domain >= 0 && f->domain != d->domain) || |
194 | (f->bus >= 0 && f->bus != d->bus) || | |
727ce158 MM |
195 | (f->slot >= 0 && f->slot != d->dev) || |
196 | (f->func >= 0 && f->func != d->func)) | |
e4842ff3 | 197 | return 0; |
727ce158 MM |
198 | if (f->device >= 0 || f->vendor >= 0) |
199 | { | |
119c1376 | 200 | pci_fill_info_v38(d, PCI_FILL_IDENT); |
727ce158 MM |
201 | if ((f->device >= 0 && f->device != d->device_id) || |
202 | (f->vendor >= 0 && f->vendor != d->vendor_id)) | |
203 | return 0; | |
204 | } | |
52aecc75 | 205 | if (f->device_class >= 0) |
4d1c9525 MW |
206 | { |
207 | pci_fill_info(d, PCI_FILL_CLASS); | |
511122b8 MM |
208 | if ((f->device_class ^ d->device_class) & f->device_class_mask) |
209 | return 0; | |
210 | } | |
211 | if (f->prog_if >= 0) | |
212 | { | |
213 | pci_fill_info(d, PCI_FILL_CLASS_EXT); | |
214 | if (f->prog_if != d->prog_if) | |
4d1c9525 MW |
215 | return 0; |
216 | } | |
e4842ff3 MM |
217 | return 1; |
218 | } | |
52aecc75 MM |
219 | |
220 | /* | |
221 | * Before pciutils v3.3, struct pci_filter had fewer fields, | |
222 | * so we have to provide compatibility wrappers. | |
223 | */ | |
224 | ||
225 | struct pci_filter_v30 { | |
226 | int domain, bus, slot, func; /* -1 = ANY */ | |
227 | int vendor, device; | |
228 | }; | |
229 | ||
230 | void pci_filter_init_v30(struct pci_access *a, struct pci_filter_v30 *f) VERSIONED_ABI; | |
231 | char *pci_filter_parse_slot_v30(struct pci_filter_v30 *f, char *str) VERSIONED_ABI; | |
232 | char *pci_filter_parse_id_v30(struct pci_filter_v30 *f, char *str) VERSIONED_ABI; | |
233 | int pci_filter_match_v30(struct pci_filter_v30 *f, struct pci_dev *d) VERSIONED_ABI; | |
234 | ||
235 | static void | |
236 | pci_filter_import_v30(struct pci_filter_v30 *old, struct pci_filter *new) | |
237 | { | |
238 | new->domain = old->domain; | |
239 | new->bus = old->bus; | |
8ab74b69 | 240 | new->slot = old->slot; |
52aecc75 MM |
241 | new->func = old->func; |
242 | new->vendor = old->vendor; | |
243 | new->device = old->device; | |
244 | new->device_class = -1; | |
511122b8 MM |
245 | new->device_class_mask = ~0U; |
246 | new->prog_if = -1; | |
52aecc75 MM |
247 | } |
248 | ||
249 | static void | |
250 | pci_filter_export_v30(struct pci_filter *new, struct pci_filter_v30 *old) | |
251 | { | |
252 | old->domain = new->domain; | |
253 | old->bus = new->bus; | |
8ab74b69 | 254 | old->slot = new->slot; |
52aecc75 MM |
255 | old->func = new->func; |
256 | old->vendor = new->vendor; | |
257 | old->device = new->device; | |
258 | } | |
259 | ||
260 | void | |
261 | pci_filter_init_v30(struct pci_access *a, struct pci_filter_v30 *f) | |
262 | { | |
263 | struct pci_filter new; | |
511122b8 | 264 | pci_filter_init_v38(a, &new); |
52aecc75 MM |
265 | pci_filter_export_v30(&new, f); |
266 | } | |
267 | ||
268 | char * | |
269 | pci_filter_parse_slot_v30(struct pci_filter_v30 *f, char *str) | |
270 | { | |
271 | struct pci_filter new; | |
272 | char *err; | |
273 | pci_filter_import_v30(f, &new); | |
511122b8 | 274 | if (err = pci_filter_parse_slot_v38(&new, str)) |
52aecc75 MM |
275 | return err; |
276 | pci_filter_export_v30(&new, f); | |
277 | return NULL; | |
278 | } | |
279 | ||
280 | char * | |
281 | pci_filter_parse_id_v30(struct pci_filter_v30 *f, char *str) | |
282 | { | |
283 | struct pci_filter new; | |
284 | char *err; | |
285 | pci_filter_import_v30(f, &new); | |
511122b8 | 286 | if (err = pci_filter_parse_id_v38(&new, str)) |
52aecc75 | 287 | return err; |
511122b8 MM |
288 | if (new.device_class >= 0 || new.prog_if >= 0) |
289 | return "Filtering by class or programming interface not supported in this program"; | |
52aecc75 MM |
290 | pci_filter_export_v30(&new, f); |
291 | return NULL; | |
292 | } | |
293 | ||
294 | int | |
295 | pci_filter_match_v30(struct pci_filter_v30 *f, struct pci_dev *d) | |
296 | { | |
297 | struct pci_filter new; | |
298 | pci_filter_import_v30(f, &new); | |
511122b8 | 299 | return pci_filter_match_v38(&new, d); |
52aecc75 MM |
300 | } |
301 | ||
511122b8 MM |
302 | // Version 3.3 is the same as version 3.8, only device_class_mask and prog_if were not implemented |
303 | // (their positions in struct pci_filter were declared as RFU). | |
304 | ||
305 | STATIC_ALIAS(void pci_filter_init(struct pci_access *a, struct pci_filter *f), pci_filter_init_v38(a, f)); | |
0478e1f3 | 306 | DEFINE_ALIAS(void pci_filter_init_v33(struct pci_access *a, struct pci_filter *f), pci_filter_init_v38); |
52aecc75 | 307 | SYMBOL_VERSION(pci_filter_init_v30, pci_filter_init@LIBPCI_3.0); |
0478e1f3 | 308 | SYMBOL_VERSION(pci_filter_init_v33, pci_filter_init@LIBPCI_3.3); |
511122b8 | 309 | SYMBOL_VERSION(pci_filter_init_v38, pci_filter_init@@LIBPCI_3.8); |
52aecc75 | 310 | |
511122b8 | 311 | STATIC_ALIAS(char *pci_filter_parse_slot(struct pci_filter *f, char *str), pci_filter_parse_slot_v38(f, str)); |
0478e1f3 | 312 | DEFINE_ALIAS(char *pci_filter_parse_slot_v33(struct pci_filter *f, char *str), pci_filter_parse_slot_v38); |
52aecc75 | 313 | SYMBOL_VERSION(pci_filter_parse_slot_v30, pci_filter_parse_slot@LIBPCI_3.0); |
0478e1f3 | 314 | SYMBOL_VERSION(pci_filter_parse_slot_v33, pci_filter_parse_slot@LIBPCI_3.3); |
511122b8 | 315 | SYMBOL_VERSION(pci_filter_parse_slot_v38, pci_filter_parse_slot@@LIBPCI_3.8); |
52aecc75 | 316 | |
511122b8 | 317 | STATIC_ALIAS(char *pci_filter_parse_id(struct pci_filter *f, char *str), pci_filter_parse_id_v38(f, str)); |
0478e1f3 | 318 | DEFINE_ALIAS(char *pci_filter_parse_id_v33(struct pci_filter *f, char *str), pci_filter_parse_id_v38); |
52aecc75 | 319 | SYMBOL_VERSION(pci_filter_parse_id_v30, pci_filter_parse_id@LIBPCI_3.0); |
0478e1f3 | 320 | SYMBOL_VERSION(pci_filter_parse_id_v33, pci_filter_parse_id@LIBPCI_3.3); |
511122b8 | 321 | SYMBOL_VERSION(pci_filter_parse_id_v38, pci_filter_parse_id@@LIBPCI_3.8); |
52aecc75 | 322 | |
511122b8 | 323 | STATIC_ALIAS(int pci_filter_match(struct pci_filter *f, struct pci_dev *d), pci_filter_match_v38(f, d)); |
0478e1f3 | 324 | DEFINE_ALIAS(int pci_filter_match_v33(struct pci_filter *f, struct pci_dev *d), pci_filter_match_v38); |
52aecc75 | 325 | SYMBOL_VERSION(pci_filter_match_v30, pci_filter_match@LIBPCI_3.0); |
0478e1f3 | 326 | SYMBOL_VERSION(pci_filter_match_v33, pci_filter_match@LIBPCI_3.3); |
511122b8 | 327 | SYMBOL_VERSION(pci_filter_match_v38, pci_filter_match@@LIBPCI_3.8); |