]> mj.ucw.cz Git - pciutils.git/blob - setpci.c
Don't crash when an invalid width is specified.
[pciutils.git] / setpci.c
1 /*
2  *      The PCI Utilities -- Manipulate PCI Configuration Registers
3  *
4  *      Copyright (c) 1998--2004 Martin Mares <mj@ucw.cz>
5  *
6  *      Can be freely distributed and used under the terms of the GNU GPL.
7  */
8
9 #include <stdio.h>
10 #include <string.h>
11 #include <stdlib.h>
12 #include <stdarg.h>
13 #include <unistd.h>
14
15 #include "pciutils.h"
16
17 static int force;                       /* Don't complain if no devices match */
18 static int verbose;                     /* Verbosity level */
19 static int demo_mode;                   /* Only show */
20
21 static struct pci_access *pacc;
22
23 struct value {
24   unsigned int value;
25   unsigned int mask;
26 };
27
28 struct op {
29   struct op *next;
30   struct pci_dev **dev_vector;
31   unsigned int addr;
32   unsigned int width;                   /* Byte width of the access */
33   int num_values;                       /* Number of values to write; <0=read */
34   struct value values[0];
35 };
36
37 static struct op *first_op, **last_op = &first_op;
38 static unsigned int max_values[] = { 0, 0xff, 0xffff, 0, 0xffffffff };
39
40 static struct pci_dev **
41 select_devices(struct pci_filter *filt)
42 {
43   struct pci_dev *z, **a, **b;
44   int cnt = 1;
45
46   for(z=pacc->devices; z; z=z->next)
47     if (pci_filter_match(filt, z))
48       cnt++;
49   a = b = xmalloc(sizeof(struct device *) * cnt);
50   for(z=pacc->devices; z; z=z->next)
51     if (pci_filter_match(filt, z))
52       *a++ = z;
53   *a = NULL;
54   return b;
55 }
56
57 static void
58 exec_op(struct op *op, struct pci_dev *dev)
59 {
60   char *formats[] = { NULL, "%02x", "%04x", NULL, "%08x" };
61   char *mask_formats[] = { NULL, "%02x->(%02x:%02x)->%02x", "%04x->(%04x:%04x)->%04x", NULL, "%08x->(%08x:%08x)->%08x" };
62   unsigned int x, y;
63   int i, addr;
64   int width = op->width;
65
66   if (verbose)
67     printf("%02x:%02x.%x:%02x", dev->bus, dev->dev, dev->func, op->addr);
68   addr = op->addr;
69   if (op->num_values >= 0)
70     {
71       for(i=0; i<op->num_values; i++)
72         {
73           if ((op->values[i].mask & max_values[width]) == max_values[width])
74             {
75               x = op->values[i].value;
76               if (verbose)
77                 {
78                   putchar(' ');
79                   printf(formats[width], op->values[i].value);
80                 }
81             }
82           else
83             {
84               switch (width)
85                 {
86                 case 1:
87                   y = pci_read_byte(dev, addr);
88                   break;
89                 case 2:
90                   y = pci_read_word(dev, addr);
91                   break;
92                 default:
93                   y = pci_read_long(dev, addr);
94                   break;
95                 }
96               x = (y & ~op->values[i].mask) | op->values[i].value;
97               if (verbose)
98                 {
99                   putchar(' ');
100                   printf(mask_formats[width], y, op->values[i].value, op->values[i].mask, x);
101                 }
102             }
103           if (!demo_mode)
104             {
105               switch (width)
106                 {
107                 case 1:
108                   pci_write_byte(dev, addr, x);
109                   break;
110                 case 2:
111                   pci_write_word(dev, addr, x);
112                   break;
113                 default:
114                   pci_write_long(dev, addr, x);
115                   break;
116                 }
117             }
118           addr += width;
119         }
120       if (verbose)
121         putchar('\n');
122     }
123   else
124     {
125       if (verbose)
126         printf(" = ");
127       switch (width)
128         {
129         case 1:
130           x = pci_read_byte(dev, addr);
131           break;
132         case 2:
133           x = pci_read_word(dev, addr);
134           break;
135         default:
136           x = pci_read_long(dev, addr);
137           break;
138         }
139       printf(formats[width], x);
140       putchar('\n');
141     }
142 }
143
144 static void
145 execute(struct op *op)
146 {
147   struct pci_dev **vec = NULL;
148   struct pci_dev **pdev, *dev;
149   struct op *oops;
150
151   while (op)
152     {
153       pdev = vec = op->dev_vector;
154       while (dev = *pdev++)
155         for(oops=op; oops && oops->dev_vector == vec; oops=oops->next)
156           exec_op(oops, dev);
157       while (op && op->dev_vector == vec)
158         op = op->next;
159     }
160 }
161
162 static void
163 scan_ops(struct op *op)
164 {
165   while (op)
166     {
167       if (op->num_values >= 0)
168         pacc->writeable = 1;
169       op = op->next;
170     }
171 }
172
173 struct reg_name {
174   unsigned int offset;
175   unsigned int width;
176   const char *name;
177 };
178
179 static const struct reg_name pci_reg_names[] = {
180   { 0x00, 2, "VENDOR_ID", },
181   { 0x02, 2, "DEVICE_ID", },
182   { 0x04, 2, "COMMAND", },
183   { 0x06, 2, "STATUS", },
184   { 0x08, 1, "REVISION", },
185   { 0x09, 1, "CLASS_PROG", },
186   { 0x0a, 2, "CLASS_DEVICE", },
187   { 0x0c, 1, "CACHE_LINE_SIZE", },
188   { 0x0d, 1, "LATENCY_TIMER", },
189   { 0x0e, 1, "HEADER_TYPE", },
190   { 0x0f, 1, "BIST", },
191   { 0x10, 4, "BASE_ADDRESS_0", },
192   { 0x14, 4, "BASE_ADDRESS_1", },
193   { 0x18, 4, "BASE_ADDRESS_2", },
194   { 0x1c, 4, "BASE_ADDRESS_3", },
195   { 0x20, 4, "BASE_ADDRESS_4", },
196   { 0x24, 4, "BASE_ADDRESS_5", },
197   { 0x28, 4, "CARDBUS_CIS", },
198   { 0x2c, 4, "SUBSYSTEM_VENDOR_ID", },
199   { 0x2e, 2, "SUBSYSTEM_ID", },
200   { 0x30, 4, "ROM_ADDRESS", },
201   { 0x3c, 1, "INTERRUPT_LINE", },
202   { 0x3d, 1, "INTERRUPT_PIN", },
203   { 0x3e, 1, "MIN_GNT", },
204   { 0x3f, 1, "MAX_LAT", },
205   { 0x18, 1, "PRIMARY_BUS", },
206   { 0x19, 1, "SECONDARY_BUS", },
207   { 0x1a, 1, "SUBORDINATE_BUS", },
208   { 0x1b, 1, "SEC_LATENCY_TIMER", },
209   { 0x1c, 1, "IO_BASE", },
210   { 0x1d, 1, "IO_LIMIT", },
211   { 0x1e, 2, "SEC_STATUS", },
212   { 0x20, 2, "MEMORY_BASE", },
213   { 0x22, 2, "MEMORY_LIMIT", },
214   { 0x24, 2, "PREF_MEMORY_BASE", },
215   { 0x26, 2, "PREF_MEMORY_LIMIT", },
216   { 0x28, 4, "PREF_BASE_UPPER32", },
217   { 0x2c, 4, "PREF_LIMIT_UPPER32", },
218   { 0x30, 2, "IO_BASE_UPPER16", },
219   { 0x32, 2, "IO_LIMIT_UPPER16", },
220   { 0x38, 4, "BRIDGE_ROM_ADDRESS", },
221   { 0x3e, 2, "BRIDGE_CONTROL", },
222   { 0x10, 4, "CB_CARDBUS_BASE", },
223   { 0x14, 2, "CB_CAPABILITIES", },
224   { 0x16, 2, "CB_SEC_STATUS", },
225   { 0x18, 1, "CB_BUS_NUMBER", },
226   { 0x19, 1, "CB_CARDBUS_NUMBER", },
227   { 0x1a, 1, "CB_SUBORDINATE_BUS", },
228   { 0x1b, 1, "CB_CARDBUS_LATENCY", },
229   { 0x1c, 4, "CB_MEMORY_BASE_0", },
230   { 0x20, 4, "CB_MEMORY_LIMIT_0", },
231   { 0x24, 4, "CB_MEMORY_BASE_1", },
232   { 0x28, 4, "CB_MEMORY_LIMIT_1", },
233   { 0x2c, 2, "CB_IO_BASE_0", },
234   { 0x2e, 2, "CB_IO_BASE_0_HI", },
235   { 0x30, 2, "CB_IO_LIMIT_0", },
236   { 0x32, 2, "CB_IO_LIMIT_0_HI", },
237   { 0x34, 2, "CB_IO_BASE_1", },
238   { 0x36, 2, "CB_IO_BASE_1_HI", },
239   { 0x38, 2, "CB_IO_LIMIT_1", },
240   { 0x3a, 2, "CB_IO_LIMIT_1_HI", },
241   { 0x40, 2, "CB_SUBSYSTEM_VENDOR_ID", },
242   { 0x42, 2, "CB_SUBSYSTEM_ID", },
243   { 0x44, 4, "CB_LEGACY_MODE_BASE", },
244   { 0x00, 0, NULL }
245 };
246
247 static void NONRET
248 usage(char *msg, ...)
249 {
250   va_list args;
251   va_start(args, msg);
252   if (msg)
253     {
254       fprintf(stderr, "setpci: ");
255       vfprintf(stderr, msg, args);
256       fprintf(stderr, "\n\n");
257     }
258   fprintf(stderr,
259 "Usage: setpci [<options>] (<device>+ <reg>[=<values>]*)*\n\
260 -f\t\tDon't complain if there's nothing to do\n\
261 -v\t\tBe verbose\n\
262 -D\t\tList changes, don't commit them\n"
263 GENERIC_HELP
264 "<device>:\t-s [[[<domain>]:][<bus>]:][<slot>][.[<func>]]\n"
265 "\t|\t-d [<vendor>]:[<device>]\n"
266 "<reg>:\t\t<number>[.(B|W|L)]\n"
267 "     |\t\t<name>\n"
268 "<values>:\t<value>[,<value>...]\n"
269 "<value>:\t<hex>\n"
270 "       |\t<hex>:<mask>\n");
271   exit(1);
272 }
273
274 int
275 main(int argc, char **argv)
276 {
277   enum { STATE_INIT, STATE_GOT_FILTER, STATE_GOT_OP } state = STATE_INIT;
278   struct pci_filter filter;
279   struct pci_dev **selected_devices = NULL;
280   char *opts = GENERIC_OPTIONS ;
281
282   if (argc == 2 && !strcmp(argv[1], "--version"))
283     {
284       puts("setpci version " PCIUTILS_VERSION);
285       return 0;
286     }
287   argc--;
288   argv++;
289
290   pacc = pci_alloc();
291   pacc->error = die;
292
293   while (argc && argv[0][0] == '-')
294     {
295       char *c = argv[0]+1;
296       char *d = c;
297       char *e;
298       while (*c)
299         switch (*c)
300           {
301           case 'v':
302             verbose++;
303             c++;
304             break;
305           case 'f':
306             force++;
307             c++;
308             break;
309           case 'D':
310             demo_mode++;
311             c++;
312             break;
313           case 0:
314             break;
315           default:
316             if (e = strchr(opts, *c))
317               {
318                 char *arg;
319                 c++;
320                 if (e[1] == ':')
321                   {
322                     if (*c)
323                       arg = c;
324                     else if (argc > 1)
325                       {
326                         arg = argv[1];
327                         argc--; argv++;
328                       }
329                     else
330                       usage(NULL);
331                     c = "";
332                   }
333                 else
334                   arg = NULL;
335                 if (!parse_generic_option(*e, pacc, arg))
336                   usage(NULL);
337               }
338             else
339               {
340                 if (c != d)
341                   usage(NULL);
342                 goto next;
343               }
344           }
345       argc--;
346       argv++;
347     }
348 next:
349
350   pci_init(pacc);
351   pci_scan_bus(pacc);
352
353   while (argc)
354     {
355       char *c = argv[0];
356       char *d, *e, *f;
357       int n, i;
358       struct op *op;
359       unsigned long ll;
360       unsigned int lim;
361
362       if (*c == '-')
363         {
364           if (!c[1] || !strchr("sd", c[1]))
365             usage(NULL);
366           if (c[2])
367             d = (c[2] == '=') ? c+3 : c+2;
368           else if (argc > 1)
369             {
370               argc--;
371               argv++;
372               d = argv[0];
373             }
374           else
375             usage(NULL);
376           if (state != STATE_GOT_FILTER)
377             {
378               pci_filter_init(pacc, &filter);
379               state = STATE_GOT_FILTER;
380             }
381           switch (c[1])
382             {
383             case 's':
384               if (d = pci_filter_parse_slot(&filter, d))
385                 die("-s: %s", d);
386               break;
387             case 'd':
388               if (d = pci_filter_parse_id(&filter, d))
389                 die("-d: %s", d);
390               break;
391             default:
392               usage(NULL);
393             }
394         }
395       else if (state == STATE_INIT)
396         usage(NULL);
397       else
398         {
399           if (state == STATE_GOT_FILTER)
400             selected_devices = select_devices(&filter);
401           if (!selected_devices[0] && !force)
402             fprintf(stderr, "setpci: Warning: No devices selected for `%s'.\n", c);
403           state = STATE_GOT_OP;
404           /* look for setting of values and count how many */
405           d = strchr(c, '=');
406           if (d)
407             {
408               *d++ = 0;
409               if (!*d)
410                 usage("Missing value");
411               for(e=d, n=1; *e; e++)
412                 if (*e == ',')
413                   n++;
414               op = xmalloc(sizeof(struct op) + n*sizeof(struct value));
415             }
416           else
417             {
418               n = -1;
419               op = xmalloc(sizeof(struct op));
420             }
421           op->dev_vector = selected_devices;
422           op->num_values = n;
423           e = strchr(c, '.');
424           if (e)
425             {
426               *e++ = 0;
427               if (e[1])
428                 usage("Missing width");
429               switch (*e & 0xdf)
430                 {
431                 case 'B':
432                   op->width = 1; break;
433                 case 'W':
434                   op->width = 2; break;
435                 case 'L':
436                   op->width = 4; break;
437                 default:
438                   usage("Invalid width \"%c\"", *e);
439                 }
440             }
441           else
442             op->width = 1;
443           ll = strtol(c, &f, 16);
444           if (f && *f)
445             {
446               const struct reg_name *r;
447               for(r = pci_reg_names; r->name; r++)
448                 if (!strcasecmp(r->name, c))
449                   break;
450               if (!r->name)
451                 usage("Unknown register \"%s\"", c);
452               if (e && op->width != r->width)
453                 usage("Explicit width doesn't correspond with the named register \"%s\"", c);
454               ll = r->offset;
455               op->width = r->width;
456             }
457           if (ll > 0x1000 || ll + op->width*((n < 0) ? 1 : n) > 0x1000)
458             die("Register number out of range!");
459           if (ll & (op->width - 1))
460             die("Unaligned register address!");
461           op->addr = ll;
462           /* read in all the values to be set */
463           for(i=0; i<n; i++)
464             {
465               e = strchr(d, ',');
466               if (e)
467                 *e++ = 0;
468               ll = strtoul(d, &f, 16);
469               lim = max_values[op->width];
470               if (f && *f && *f != ':')
471                 usage("Invalid value \"%s\"", d);
472               if (ll > lim && ll < ~0UL - lim)
473                 usage("Value \"%s\" is out of range", d);
474               op->values[i].value = ll;
475               if (f && *f == ':')
476                 {
477                   d = ++f;
478                   ll = strtoul(d, &f, 16);
479                   if (f && *f)
480                     usage("Invalid mask \"%s\"", d);
481                   if (ll > lim && ll < ~0UL - lim)
482                     usage("Mask \"%s\" is out of range", d);
483                   op->values[i].mask = ll;
484                   op->values[i].value &= ll;
485                 }
486               else
487                 op->values[i].mask = ~0U;
488               d = e;
489             }
490           *last_op = op;
491           last_op = &op->next;
492           op->next = NULL;
493         }
494       argc--;
495       argv++;
496     }
497   if (state == STATE_INIT)
498     usage("No operation specified");
499
500   scan_ops(first_op);
501   execute(first_op);
502
503   return 0;
504 }