]>
Commit | Line | Data |
---|---|---|
727ce158 | 1 | /* |
727ce158 MM |
2 | * The PCI Library -- Reading of Bus Dumps |
3 | * | |
f7821e53 | 4 | * Copyright (c) 1997--2005 Martin Mares <mj@ucw.cz> |
727ce158 MM |
5 | * |
6 | * Can be freely distributed and used under the terms of the GNU GPL. | |
7 | */ | |
8 | ||
9 | #include <stdio.h> | |
10 | #include <ctype.h> | |
11 | #include <string.h> | |
12 | #include <errno.h> | |
13 | ||
14 | #include "internal.h" | |
15 | ||
09817437 | 16 | struct dump_data { |
f7821e53 | 17 | int len, allocated; |
09817437 MM |
18 | byte data[1]; |
19 | }; | |
20 | ||
727ce158 MM |
21 | static int |
22 | dump_detect(struct pci_access *a) | |
23 | { | |
24 | return !!a->method_params[PCI_ACCESS_DUMP]; | |
25 | } | |
26 | ||
09817437 MM |
27 | static void |
28 | dump_alloc_data(struct pci_dev *dev, int len) | |
29 | { | |
30 | struct dump_data *dd = pci_malloc(dev->access, sizeof(struct dump_data) + len - 1); | |
f7821e53 MM |
31 | dd->allocated = len; |
32 | dd->len = 0; | |
09817437 MM |
33 | memset(dd->data, 0xff, len); |
34 | dev->aux = dd; | |
35 | } | |
36 | ||
d19394db MM |
37 | static int |
38 | dump_validate(char *s, char *fmt) | |
39 | { | |
40 | while (*fmt) | |
41 | { | |
42 | if (*fmt == '#' ? !isxdigit(*s) : *fmt != *s) | |
43 | return 0; | |
44 | *fmt++, *s++; | |
45 | } | |
46 | return 1; | |
47 | } | |
48 | ||
727ce158 MM |
49 | static void |
50 | dump_init(struct pci_access *a) | |
51 | { | |
52 | char *name = a->method_params[PCI_ACCESS_DUMP]; | |
53 | FILE *f; | |
54 | char buf[256]; | |
55 | struct pci_dev *dev = NULL; | |
1f7c91cc | 56 | int len, mn, bn, dn, fn, i, j; |
727ce158 MM |
57 | |
58 | if (!a) | |
59 | a->error("dump: File name not given."); | |
60 | if (!(f = fopen(name, "r"))) | |
61 | a->error("dump: Cannot open %s: %s", name, strerror(errno)); | |
62 | while (fgets(buf, sizeof(buf)-1, f)) | |
63 | { | |
64 | char *z = strchr(buf, '\n'); | |
65 | if (!z) | |
66 | a->error("dump: line too long or unterminated"); | |
67 | *z-- = 0; | |
68 | if (z >= buf && *z == '\r') | |
69 | *z-- = 0; | |
70 | len = z - buf + 1; | |
1f7c91cc | 71 | mn = 0; |
d19394db MM |
72 | if (dump_validate(buf, "##:##.# ") && sscanf(buf, "%x:%x.%d", &bn, &dn, &fn) == 3 || |
73 | dump_validate(buf, "####:##:##.# ") && sscanf(buf, "%x:%x:%x.%d", &mn, &bn, &dn, &fn) == 4) | |
727ce158 | 74 | { |
1f7c91cc | 75 | dev = pci_get_dev(a, mn, bn, dn, fn); |
09817437 | 76 | dump_alloc_data(dev, 256); |
727ce158 MM |
77 | pci_link_dev(a, dev); |
78 | } | |
79 | else if (!len) | |
80 | dev = NULL; | |
09817437 | 81 | else if (dev && |
d19394db | 82 | (dump_validate(buf, "##: ") || dump_validate(buf, "###: ")) && |
727ce158 MM |
83 | sscanf(buf, "%x: ", &i) == 1) |
84 | { | |
09817437 | 85 | struct dump_data *dd = dev->aux; |
d19394db MM |
86 | z = strchr(buf, ' ') + 1; |
87 | while (isxdigit(z[0]) && isxdigit(z[1]) && (!z[2] || z[2] == ' ') && | |
88 | sscanf(z, "%x", &j) == 1 && j < 256) | |
727ce158 | 89 | { |
09817437 | 90 | if (i >= 4096) |
d19394db MM |
91 | a->error("dump: At most 4096 bytes of config space are supported"); |
92 | if (i >= dd->allocated) /* Need to re-allocate the buffer */ | |
09817437 MM |
93 | { |
94 | dump_alloc_data(dev, 4096); | |
95 | memcpy(((struct dump_data *) dev->aux)->data, dd->data, 256); | |
96 | pci_mfree(dd); | |
97 | dd = dev->aux; | |
98 | } | |
99 | dd->data[i++] = j; | |
f7821e53 MM |
100 | if (i > dd->len) |
101 | dd->len = i; | |
727ce158 | 102 | z += 2; |
d19394db MM |
103 | if (*z) |
104 | z++; | |
727ce158 | 105 | } |
d19394db MM |
106 | if (*z) |
107 | a->error("dump: Malformed line"); | |
727ce158 MM |
108 | } |
109 | } | |
110 | } | |
111 | ||
112 | static void | |
a832f6f1 | 113 | dump_cleanup(struct pci_access *a UNUSED) |
727ce158 MM |
114 | { |
115 | } | |
116 | ||
117 | static void | |
a832f6f1 | 118 | dump_scan(struct pci_access *a UNUSED) |
727ce158 MM |
119 | { |
120 | } | |
121 | ||
122 | static int | |
123 | dump_read(struct pci_dev *d, int pos, byte *buf, int len) | |
124 | { | |
09817437 MM |
125 | struct dump_data *dd; |
126 | if (!(dd = d->aux)) | |
727ce158 MM |
127 | { |
128 | struct pci_dev *e = d->access->devices; | |
d19394db | 129 | while (e && (e->domain != d->domain || e->bus != d->bus || e->dev != d->dev || e->func != d->func)) |
727ce158 | 130 | e = e->next; |
09817437 | 131 | if (!e) |
727ce158 | 132 | return 0; |
09817437 | 133 | dd = e->aux; |
727ce158 | 134 | } |
09817437 MM |
135 | if (pos + len > dd->len) |
136 | return 0; | |
137 | memcpy(buf, dd->data + pos, len); | |
727ce158 MM |
138 | return 1; |
139 | } | |
140 | ||
141 | static int | |
a832f6f1 | 142 | dump_write(struct pci_dev *d UNUSED, int pos UNUSED, byte *buf UNUSED, int len UNUSED) |
727ce158 MM |
143 | { |
144 | d->access->error("Writing to dump files is not supported."); | |
145 | return 0; | |
146 | } | |
147 | ||
148 | static void | |
149 | dump_cleanup_dev(struct pci_dev *d) | |
150 | { | |
151 | if (d->aux) | |
152 | { | |
153 | pci_mfree(d->aux); | |
154 | d->aux = NULL; | |
155 | } | |
156 | } | |
157 | ||
158 | struct pci_methods pm_dump = { | |
159 | "dump", | |
160 | NULL, /* config */ | |
161 | dump_detect, | |
162 | dump_init, | |
163 | dump_cleanup, | |
164 | dump_scan, | |
165 | pci_generic_fill_info, | |
166 | dump_read, | |
167 | dump_write, | |
168 | NULL, /* init_dev */ | |
169 | dump_cleanup_dev | |
170 | }; |