Initial revision
[opensuse:hwinfo.git] / src / hd / pci.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <unistd.h>
5 #include <errno.h>
6 #include <fcntl.h>
7 #include <sys/stat.h>
8 #include <sys/types.h>
9 #include <sys/pci.h>
10
11 #include "hd.h"
12 #include "hd_int.h"
13 #include "pci.h"
14
15 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
16  * pci stuff
17  *
18  * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
19  */
20
21
22 static void get_pci_data(hd_data_t *hd_data);
23 static void dump_pci_data(hd_data_t *hd_data);
24 static int f_read(int fd, off_t ofs, void *buf, size_t len);
25 static int f_write(int fd, off_t ofs, void *buf, size_t len);
26 static unsigned get_pci_addr_range(hd_data_t *hd_data, pci_t *pci, int fd, unsigned addr, unsigned mask);
27 static pci_t *add_pci_entry(hd_data_t *hd_data, pci_t *new_pci);
28
29 void hd_scan_pci(hd_data_t *hd_data)
30 {
31   hd_t *hd;
32   pci_t *p;
33   hd_res_t *res;
34   int j;
35   unsigned long ul;
36
37   if(!(hd_data->probe & (1 << pr_pci))) return;
38
39   hd_data->module = mod_pci;
40
41   /* some clean-up */
42   remove_hd_entries(hd_data);
43   hd_data->pci = NULL;
44
45   PROGRESS(1, 0, "get pci data");
46
47   get_pci_data(hd_data);
48   if((hd_data->debug & HD_DEB_PCI)) dump_pci_data(hd_data);
49
50   PROGRESS(4, 0, "build list");
51
52   for(p = hd_data->pci; p; p = p->next) {
53     hd = add_hd_entry(hd_data, __LINE__, 0);
54
55     hd->bus = bus_pci;
56     hd->slot = p->slot + (p->bus << 8);
57     hd->func = p->func;
58     hd->base_class = p->base_class;
59     hd->sub_class = p->sub_class;
60     hd->prog_if = p->prog_if;
61
62     hd->dev = p->dev;
63     hd->vend = p->vend;
64     hd->sub_dev = p->sub_dev;
65     hd->sub_vend = p->sub_vend;
66     hd->rev = p->rev;
67
68     for(j = 0; j < 6; j++) {
69       ul = p->base_addr[j];
70       if(ul & ~PCI_BASE_ADDRESS_SPACE) {
71         if((ul & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_IO) {
72           res = new_mem(sizeof *res);
73           res->io.type = res_io;
74           res->io.enabled = p->cmd & PCI_COMMAND_IO ? 1 : 0;
75           res->io.base =  ul & PCI_BASE_ADDRESS_IO_MASK;
76           res->io.range = p->base_len[j];
77           res->io.access = acc_rw;
78           add_res_entry(&hd->res, res);
79         }
80         if((ul & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_MEMORY) {
81           res = new_mem(sizeof *res);
82           res->mem.type = res_mem;
83           res->mem.enabled = p->cmd & PCI_COMMAND_MEMORY ? 1 : 0;
84           res->mem.base =  ul & PCI_BASE_ADDRESS_MEM_MASK;
85           res->mem.range = p->base_len[j];
86           res->mem.access = acc_rw;
87           res->mem.prefetch = ul & PCI_BASE_ADDRESS_MEM_PREFETCH ? flag_yes : flag_no;
88           add_res_entry(&hd->res, res);
89         }
90       }
91     }
92     if((ul = p->rom_base_addr)) {
93       res = new_mem(sizeof *res);
94       res->mem.type = res_mem;
95       res->mem.enabled = ul & PCI_ROM_ADDRESS_ENABLE ? 1 : 0;
96       res->mem.base =  ul & PCI_ROM_ADDRESS_MASK;
97       res->mem.range = p->rom_base_len;
98       res->mem.access = acc_ro;
99       add_res_entry(&hd->res, res);
100     }
101
102     if(p->irq) {
103       res = new_mem(sizeof *res);
104       res->irq.type = res_irq;
105       res->irq.enabled = 1;
106       res->irq.base = p->irq;
107       add_res_entry(&hd->res, res);
108     }
109
110     hd->detail = new_mem(sizeof *hd->detail);
111     hd->detail->type = hd_detail_pci;
112     hd->detail->pci.data = p;
113   }
114 }
115
116
117 /*
118  * Get the (raw) PCI data.
119  * The device list is taken from /proc/bus/pci/devices,
120  * individual device info from /proc/bus/pci/.
121  *
122  * Note: non-root users can only read the first 64 bytes (of 256)
123  * of the device headers.
124  */
125 void get_pci_data(hd_data_t *hd_data)
126 {
127   unsigned char *t;
128   unsigned long u, ul[10], nxt;
129   uint64 u64;
130   int fd, i, j, o_fl;
131   pci_t *p;
132   char *pci_data_file = NULL;
133   str_list_t *sl, *sl0;
134   int prog_cnt = 0;
135
136   /*
137    * Read the devices file and build a list of all PCI devices.
138    * The list holds preliminary info that gets extended later.
139    */
140   if(!(sl0 = read_file(PROC_PCI_DEVICES, 0, 0))) return;
141   for(sl = sl0; sl; sl = sl->next) {
142     if(
143       sscanf(sl->str,
144         " %lx %lx %lx %lx %lx %lx %lx %lx %lx %lx",
145         ul, ul + 1, ul + 2, ul + 3, ul + 4, ul + 5, ul + 6, ul + 7, ul + 8, ul + 9
146       ) == 10
147     ) {
148       p = add_pci_entry(hd_data, new_mem(sizeof *p));
149
150       p->bus = ul[0] >> 8;
151       /* combine them, as we have no extra field for the bus index */
152       p->slot = PCI_SLOT(ul[0] & 0xff);
153       p->func = PCI_FUNC(ul[0] & 0xff);
154
155       p->dev = ul[1] & 0xffff;
156       p->vend = (ul[1] >> 16) & 0xffff;
157
158       p->irq = ul[2];
159       for(i = 0; i < 6; i++) p->base_addr[i] = ul[i + 3];
160       p->rom_base_addr = ul[9];
161     }
162   }
163
164   sl0 = free_str_list(sl0);
165
166   /*
167    * Now add the full PCI info.
168    */
169   for(p = hd_data->pci; p; p = p->next) {
170     str_printf(&pci_data_file, 0, PROC_PCI_BUS "/%02x/%02x.%x", p->bus, p->slot, p->func);
171     if(
172       (fd = open(pci_data_file, o_fl = O_RDWR)) >= 0 ||
173       (fd = open(pci_data_file, o_fl = O_RDONLY)) >= 0
174     ) {
175       PROGRESS(2, ++prog_cnt, "raw data");
176
177       p->data_len = read(fd, p->data, sizeof p->data);
178       if(p->data_len >= 0x40) {
179         p->hdr_type = p->data[PCI_HEADER_TYPE] & 0x7f;
180         p->cmd = p->data[PCI_COMMAND] + (p->data[PCI_COMMAND + 1] << 8);
181         ul[0] = p->data[PCI_VENDOR_ID] + (p->data[PCI_VENDOR_ID + 1] << 8);
182         ul[1] = p->data[PCI_DEVICE_ID] + (p->data[PCI_DEVICE_ID + 1] << 8);
183         if(ul[0] == p->vend && ul[1] == p->dev) {
184           /* these are header type specific */
185           if(p->hdr_type == PCI_HEADER_TYPE_NORMAL) {
186             p->sub_dev = p->data[PCI_SUBSYSTEM_ID] + (p->data[PCI_SUBSYSTEM_ID + 1] << 8);
187             p->sub_vend = p->data[PCI_SUBSYSTEM_VENDOR_ID] + (p->data[PCI_SUBSYSTEM_VENDOR_ID + 1] << 8);
188           }
189           else if(p->hdr_type == PCI_HEADER_TYPE_CARDBUS) {
190             p->sub_dev = p->data[PCI_CB_SUBSYSTEM_ID] + (p->data[PCI_CB_SUBSYSTEM_ID + 1] << 8);
191             p->sub_vend = p->data[PCI_CB_SUBSYSTEM_VENDOR_ID] + (p->data[PCI_CB_SUBSYSTEM_VENDOR_ID + 1] << 8);
192           }
193
194           p->rev = p->data[PCI_REVISION_ID];
195           p->prog_if = p->data[PCI_CLASS_PROG];
196           p->sub_class = p->data[PCI_CLASS_DEVICE];
197           p->base_class = p->data[PCI_CLASS_DEVICE + 1];
198           p->flags |= (1 << pci_flag_ok);
199
200           /*
201            * See if we can get the adress *ranges*. This does actuall imply
202            * reprogramming the PCI devices. As this is somewhat dangerous in
203            * a running system, this feature (pr_pci_range) is normally turned
204            * off. (The check is actually in get_pci_addr_range().)
205            */
206           if(p->hdr_type == PCI_HEADER_TYPE_NORMAL) {
207             PROGRESS(3, prog_cnt, "address ranges");
208
209             for(j = 0; j < 6; j++) {
210               t = p->data + PCI_BASE_ADDRESS_0 + 4 * j;
211               u = t[0] + (t[1] << 8) + (t[2] << 16) + (t[3] << 24);
212               /* just checking; actually it's paranoid... */
213               if(u == p->base_addr[j]) {
214                 if(u && o_fl == O_RDWR)
215                   p->base_len[j] = get_pci_addr_range(hd_data, p, fd, PCI_BASE_ADDRESS_0 + 4 * j, 0);
216               }
217               else {
218                 p->base_addr[j] = u;
219                 if(u && o_fl == O_RDWR)
220                   p->base_len[j] = get_pci_addr_range(hd_data, p, fd, PCI_BASE_ADDRESS_0 + 4 * j, 0);
221               }
222               if(
223                 (u & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_MEMORY &&
224                 (u & PCI_BASE_ADDRESS_MEM_TYPE_MASK) == PCI_BASE_ADDRESS_MEM_TYPE_64 &&
225                 j < 5
226               ) {
227                 u64 = t[4] + (t[5] << 8) + (t[6] << 16) + (t[7] << 24);
228                 p->base_addr[j] += u64 << 32;
229                 // get the range!
230                 // ##### the get_pci_addr_range() stuff is awkward
231                 j++;
232               }
233             }
234             if(p->rom_base_addr)
235               p->rom_base_len = get_pci_addr_range(hd_data, p, fd, PCI_ROM_ADDRESS, (unsigned) PCI_ROM_ADDRESS_MASK);
236           }
237
238           /* let's get through the capability list */
239           if(p->hdr_type == PCI_HEADER_TYPE_NORMAL && (nxt = p->data[PCI_CAPABILITY_LIST])) {
240             /*
241              * Cut out after 20 capabilities to avoid infinite recursion due
242              * to (potentially) malformed data. 20 *is* completely
243              * arbitrary, though.
244              */
245             for(j = 0; j < 20 && nxt && nxt <= 0xfe; j++) {
246               switch(p->data[nxt]) {
247                 case PCI_CAP_ID_PM:
248                   p->flags |= (1 << pci_flag_pm);
249                   break;
250
251                 case PCI_CAP_ID_AGP:
252                   p->flags |= (1 << pci_flag_agp);
253                   break;
254               }
255               nxt = p->data[nxt + 1];
256             }
257           }
258         }
259       }
260       close(fd);
261     }
262   }
263
264   free_mem(pci_data_file);
265 }
266
267
268 /*
269  * Read from a file.
270  */
271 int f_read(int fd, off_t ofs, void *buf, size_t len)
272 {
273   if(lseek(fd, ofs, SEEK_SET) == -1) return -1;
274   return read(fd, buf, len);
275 }
276
277 /*
278  * Write to a file.
279  */
280 int f_write(int fd, off_t ofs, void *buf, size_t len)
281 {
282   if(lseek(fd, ofs, SEEK_SET) == -1) return -1;
283   return write(fd, buf, len);
284 }
285
286
287 /*
288  * Determine the address ranges by writing -1 to the base regs and
289  * looking on the number of 1-bits.
290  * This assumes the range to be a power of 2.
291  *
292  * This function *is* dangerous to execute - you have been warned... :-)
293  *
294  * ##### FIX: 32bit vs. 64bit !!!!!!!!!!
295  * ##### It breaks if unsigned != 32 bit!
296  */
297 unsigned get_pci_addr_range(hd_data_t *hd_data, pci_t *pci, int fd, unsigned addr, unsigned mask)
298 {
299   unsigned u, u0, u1 = -1, cmd = 0, cmd1;
300   int err = 0;
301
302   /* it's easier to do the check *here* */
303   if(!(hd_data->probe & (1 << pr_pci_range))) return 0;
304
305   /* PCI_COMMAND is a 16 bit value */
306   if(f_read(fd, PCI_COMMAND, &cmd, 2) != 2) return 0;
307
308   if(f_read(fd, addr, &u0, sizeof u0) != sizeof u0) return 0;
309
310   /* disable memory and i/o adresses */
311   cmd1 = cmd & ~(PCI_COMMAND_IO | PCI_COMMAND_MEMORY);
312   if(f_write(fd, PCI_COMMAND, &cmd1, 2) != 2) err = 1;
313
314   if(!err) {
315     /* remember error conditions, but we must get through... */
316
317     /*
318      * write -1 and read the value back
319      *
320      * ???: What about the address type bits? Should they be preserved?
321      */
322     if(f_write(fd, addr, &u1, sizeof u1) != sizeof u1) err = 2;
323     if(f_read(fd, addr, &u1, sizeof u1) != sizeof u1) err = 3;
324
325     /* restore original value */
326     if(f_write(fd, addr, &u0, sizeof u0) != sizeof u0) err = 4;
327   }
328
329   /* restore original value */
330   if(f_write(fd, PCI_COMMAND, &cmd, 2) != 2) err = 5;
331
332   if(!err) {
333     /* a mask of 0 is slightly magic... */
334     if(mask) {
335       u = u1 & mask;
336     }
337     else {
338 #ifdef __i386__
339       /* Intel processors have 16 bit i/o space */
340       if((u0 & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_IO) u1 |= 0xffff0000;
341 #endif
342       u = u1 & (
343         (u0 & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_IO ?
344           PCI_BASE_ADDRESS_IO_MASK :
345           PCI_BASE_ADDRESS_MEM_MASK
346       );
347     }
348   }
349   else {
350     u = 0;
351   }
352
353   if(err)
354     str_printf(&pci->log, -1, "  err %d: %x %x\n", err, u0, -u);
355
356   return -u;
357 }
358
359
360 /*
361  * Store a raw PCI entry; just for convenience.
362  */
363 pci_t *add_pci_entry(hd_data_t *hd_data, pci_t *new_pci)
364 {
365   pci_t **pci = &hd_data->pci;
366
367   while(*pci) pci = &(*pci)->next;
368
369   return *pci = new_pci;
370 }
371
372
373 /*
374  * Add a dump of all raw PCI data to the global log.
375  */
376 void dump_pci_data(hd_data_t *hd_data)
377 {
378   pci_t *p;
379   char *s = NULL;
380   int i, j;
381
382   ADD2LOG("---------- PCI raw data ----------\n");
383
384   for(p = hd_data->pci; p; p = p->next) {
385
386     if(!(p->flags & (1 << pci_flag_ok))) str_printf(&s, -1, "oops");
387     if(p->flags & (1 << pci_flag_pm)) str_printf(&s, -1, ",pm");
388     if(p->flags & (1 << pci_flag_agp)) str_printf(&s, -1, ",agp");
389     if(!s) str_printf(&s, 0, "%s", "");
390
391     ADD2LOG(
392       "bus %02x, slot %02x, func %x, vend:dev:s_vend:s_dev:rev %04x:%04x:%04x:%04x:%02x\n",
393       p->bus, p->slot, p->func, p->vend, p->dev, p->sub_vend, p->sub_dev, p->rev
394     );
395     ADD2LOG(
396       "class %02x, sub_class %02x prog_if %02x, hdr %x, flags <%s>, irq %u\n",
397       p->base_class, p->sub_class, p->prog_if, p->hdr_type, *s == ',' ? s + 1 : s, p->irq 
398     );
399
400     s = free_mem(s);
401
402     for(i = 0; i < 6; i++) {
403       if(p->base_addr[i] || p->base_len[i])
404         ADD2LOG("  addr%d %08x, size %08x\n", i, p->base_addr[i], p->base_len[i]);
405     }
406     if(p->rom_base_addr)
407       ADD2LOG("  rom   %08x\n", p->rom_base_addr);
408
409     if(p->log) ADD2LOG("%s", p->log);
410
411     for(i = 0; i < p->data_len; i += 0x10) {
412       ADD2LOG("  %02x: ", i);
413       j = p->data_len - i;
414       hexdump(&hd_data->log, 1, j > 0x10 ? 0x10 : j, p->data + i);
415       ADD2LOG("\n");
416     }
417
418     if(p->next) ADD2LOG("\n");
419   }
420
421   ADD2LOG("---------- PCI raw data end ----------\n");
422 }
423
424