]>
Commit | Line | Data |
---|---|---|
727ce158 | 1 | /* |
727ce158 MM |
2 | * The PCI Library -- Reading of Bus Dumps |
3 | * | |
cb6ee324 | 4 | * Copyright (c) 1997--2008 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 | ||
cb6ee324 MM |
21 | static void |
22 | dump_config(struct pci_access *a) | |
23 | { | |
24 | pci_define_param(a, "dump.name", "", "Name of the bus dump file to read from"); | |
25 | } | |
26 | ||
727ce158 MM |
27 | static int |
28 | dump_detect(struct pci_access *a) | |
29 | { | |
cb6ee324 MM |
30 | char *name = pci_get_param(a, "dump.name"); |
31 | return name && name[0]; | |
727ce158 MM |
32 | } |
33 | ||
09817437 MM |
34 | static void |
35 | dump_alloc_data(struct pci_dev *dev, int len) | |
36 | { | |
37 | struct dump_data *dd = pci_malloc(dev->access, sizeof(struct dump_data) + len - 1); | |
f7821e53 MM |
38 | dd->allocated = len; |
39 | dd->len = 0; | |
09817437 MM |
40 | memset(dd->data, 0xff, len); |
41 | dev->aux = dd; | |
42 | } | |
43 | ||
d19394db MM |
44 | static int |
45 | dump_validate(char *s, char *fmt) | |
46 | { | |
47 | while (*fmt) | |
48 | { | |
49 | if (*fmt == '#' ? !isxdigit(*s) : *fmt != *s) | |
50 | return 0; | |
dc01dd60 | 51 | fmt++, s++; |
d19394db MM |
52 | } |
53 | return 1; | |
54 | } | |
55 | ||
727ce158 MM |
56 | static void |
57 | dump_init(struct pci_access *a) | |
58 | { | |
cb6ee324 | 59 | char *name = pci_get_param(a, "dump.name"); |
727ce158 MM |
60 | FILE *f; |
61 | char buf[256]; | |
62 | struct pci_dev *dev = NULL; | |
1f7c91cc | 63 | int len, mn, bn, dn, fn, i, j; |
727ce158 | 64 | |
364275f8 | 65 | if (!name) |
727ce158 MM |
66 | a->error("dump: File name not given."); |
67 | if (!(f = fopen(name, "r"))) | |
68 | a->error("dump: Cannot open %s: %s", name, strerror(errno)); | |
69 | while (fgets(buf, sizeof(buf)-1, f)) | |
70 | { | |
71 | char *z = strchr(buf, '\n'); | |
72 | if (!z) | |
202a8f8a MM |
73 | { |
74 | fclose(f); | |
75 | a->error("dump: line too long or unterminated"); | |
76 | } | |
727ce158 MM |
77 | *z-- = 0; |
78 | if (z >= buf && *z == '\r') | |
79 | *z-- = 0; | |
80 | len = z - buf + 1; | |
1f7c91cc | 81 | mn = 0; |
d19394db MM |
82 | if (dump_validate(buf, "##:##.# ") && sscanf(buf, "%x:%x.%d", &bn, &dn, &fn) == 3 || |
83 | dump_validate(buf, "####:##:##.# ") && sscanf(buf, "%x:%x:%x.%d", &mn, &bn, &dn, &fn) == 4) | |
727ce158 | 84 | { |
1f7c91cc | 85 | dev = pci_get_dev(a, mn, bn, dn, fn); |
09817437 | 86 | dump_alloc_data(dev, 256); |
727ce158 MM |
87 | pci_link_dev(a, dev); |
88 | } | |
89 | else if (!len) | |
90 | dev = NULL; | |
09817437 | 91 | else if (dev && |
d19394db | 92 | (dump_validate(buf, "##: ") || dump_validate(buf, "###: ")) && |
727ce158 MM |
93 | sscanf(buf, "%x: ", &i) == 1) |
94 | { | |
09817437 | 95 | struct dump_data *dd = dev->aux; |
d19394db MM |
96 | z = strchr(buf, ' ') + 1; |
97 | while (isxdigit(z[0]) && isxdigit(z[1]) && (!z[2] || z[2] == ' ') && | |
98 | sscanf(z, "%x", &j) == 1 && j < 256) | |
727ce158 | 99 | { |
09817437 | 100 | if (i >= 4096) |
202a8f8a MM |
101 | { |
102 | fclose(f); | |
103 | a->error("dump: At most 4096 bytes of config space are supported"); | |
104 | } | |
d19394db | 105 | if (i >= dd->allocated) /* Need to re-allocate the buffer */ |
09817437 MM |
106 | { |
107 | dump_alloc_data(dev, 4096); | |
108 | memcpy(((struct dump_data *) dev->aux)->data, dd->data, 256); | |
109 | pci_mfree(dd); | |
110 | dd = dev->aux; | |
111 | } | |
112 | dd->data[i++] = j; | |
f7821e53 MM |
113 | if (i > dd->len) |
114 | dd->len = i; | |
727ce158 | 115 | z += 2; |
d19394db MM |
116 | if (*z) |
117 | z++; | |
727ce158 | 118 | } |
d19394db | 119 | if (*z) |
202a8f8a MM |
120 | { |
121 | fclose(f); | |
122 | a->error("dump: Malformed line"); | |
123 | } | |
727ce158 MM |
124 | } |
125 | } | |
202a8f8a | 126 | fclose(f); |
727ce158 MM |
127 | } |
128 | ||
129 | static void | |
a832f6f1 | 130 | dump_cleanup(struct pci_access *a UNUSED) |
727ce158 MM |
131 | { |
132 | } | |
133 | ||
134 | static void | |
a832f6f1 | 135 | dump_scan(struct pci_access *a UNUSED) |
727ce158 MM |
136 | { |
137 | } | |
138 | ||
139 | static int | |
140 | dump_read(struct pci_dev *d, int pos, byte *buf, int len) | |
141 | { | |
09817437 MM |
142 | struct dump_data *dd; |
143 | if (!(dd = d->aux)) | |
727ce158 MM |
144 | { |
145 | struct pci_dev *e = d->access->devices; | |
d19394db | 146 | while (e && (e->domain != d->domain || e->bus != d->bus || e->dev != d->dev || e->func != d->func)) |
727ce158 | 147 | e = e->next; |
09817437 | 148 | if (!e) |
727ce158 | 149 | return 0; |
09817437 | 150 | dd = e->aux; |
727ce158 | 151 | } |
09817437 MM |
152 | if (pos + len > dd->len) |
153 | return 0; | |
154 | memcpy(buf, dd->data + pos, len); | |
727ce158 MM |
155 | return 1; |
156 | } | |
157 | ||
158 | static int | |
a832f6f1 | 159 | dump_write(struct pci_dev *d UNUSED, int pos UNUSED, byte *buf UNUSED, int len UNUSED) |
727ce158 MM |
160 | { |
161 | d->access->error("Writing to dump files is not supported."); | |
162 | return 0; | |
163 | } | |
164 | ||
165 | static void | |
166 | dump_cleanup_dev(struct pci_dev *d) | |
167 | { | |
168 | if (d->aux) | |
169 | { | |
170 | pci_mfree(d->aux); | |
171 | d->aux = NULL; | |
172 | } | |
173 | } | |
174 | ||
175 | struct pci_methods pm_dump = { | |
176 | "dump", | |
9ff67879 | 177 | "Reading of register dumps (set the `dump.name' parameter)", |
cb6ee324 | 178 | dump_config, |
727ce158 MM |
179 | dump_detect, |
180 | dump_init, | |
181 | dump_cleanup, | |
182 | dump_scan, | |
183 | pci_generic_fill_info, | |
184 | dump_read, | |
185 | dump_write, | |
52c81519 | 186 | NULL, /* read_vpd */ |
727ce158 MM |
187 | NULL, /* init_dev */ |
188 | dump_cleanup_dev | |
189 | }; |