xref: /dragonfly/usr.sbin/pciconf/cap.c (revision d650e2183cf9557d58c726150ef990202fc85b98)
1 /*-
2  * Copyright (c) 2007 Yahoo!, Inc.
3  * All rights reserved.
4  * Written by: John Baldwin <jhb@FreeBSD.org>
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  * 3. Neither the name of the author nor the names of any co-contributors
15  *    may be used to endorse or promote products derived from this software
16  *    without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28  * SUCH DAMAGE.
29  *
30  * $FreeBSD: src/usr.sbin/pciconf/cap.c,v 1.11 2010/09/16 16:03:12 jhb Exp $
31  */
32 
33 #include <sys/types.h>
34 
35 #include <err.h>
36 #include <stdio.h>
37 #include <sys/agpio.h>
38 #include <sys/pciio.h>
39 
40 #include <dev/agp/agpreg.h>
41 #include <bus/pci/pcireg.h>
42 
43 #include "pciconf.h"
44 
45 static void         list_ecaps(int fd, struct pci_conf *p);
46 static const char *aspm_string(uint8_t aspm);
47 static int          slot_power(uint32_t cap);
48 
49 static void
cap_power(int fd,struct pci_conf * p,uint8_t ptr)50 cap_power(int fd, struct pci_conf *p, uint8_t ptr)
51 {
52           uint16_t cap, status;
53 
54           cap = read_config(fd, &p->pc_sel, ptr + PCIR_POWER_CAP, 2);
55           status = read_config(fd, &p->pc_sel, ptr + PCIR_POWER_STATUS, 2);
56           printf("powerspec %d  supports D0%s%s D3  current D%d",
57               cap & PCIM_PCAP_SPEC,
58               cap & PCIM_PCAP_D1SUPP ? " D1" : "",
59               cap & PCIM_PCAP_D2SUPP ? " D2" : "",
60               status & PCIM_PSTAT_DMASK);
61 }
62 
63 static void
cap_agp(int fd,struct pci_conf * p,uint8_t ptr)64 cap_agp(int fd, struct pci_conf *p, uint8_t ptr)
65 {
66           uint32_t status, command;
67 
68           status = read_config(fd, &p->pc_sel, ptr + AGP_STATUS, 4);
69           command = read_config(fd, &p->pc_sel, ptr + AGP_CAPID, 4);
70           printf("AGP ");
71           if (AGP_MODE_GET_MODE_3(status)) {
72                     printf("v3 ");
73                     if (AGP_MODE_GET_RATE(status) & AGP_MODE_V3_RATE_8x)
74                               printf("8x ");
75                     if (AGP_MODE_GET_RATE(status) & AGP_MODE_V3_RATE_4x)
76                               printf("4x ");
77           } else {
78                     if (AGP_MODE_GET_RATE(status) & AGP_MODE_V2_RATE_4x)
79                               printf("4x ");
80                     if (AGP_MODE_GET_RATE(status) & AGP_MODE_V2_RATE_2x)
81                               printf("2x ");
82                     if (AGP_MODE_GET_RATE(status) & AGP_MODE_V2_RATE_1x)
83                               printf("1x ");
84           }
85           if (AGP_MODE_GET_SBA(status))
86                     printf("SBA ");
87           if (AGP_MODE_GET_AGP(command)) {
88                     printf("enabled at ");
89                     if (AGP_MODE_GET_MODE_3(command)) {
90                               printf("v3 ");
91                               switch (AGP_MODE_GET_RATE(command)) {
92                               case AGP_MODE_V3_RATE_8x:
93                                         printf("8x ");
94                                         break;
95                               case AGP_MODE_V3_RATE_4x:
96                                         printf("4x ");
97                                         break;
98                               }
99                     } else
100                               switch (AGP_MODE_GET_RATE(command)) {
101                               case AGP_MODE_V2_RATE_4x:
102                                         printf("4x ");
103                                         break;
104                               case AGP_MODE_V2_RATE_2x:
105                                         printf("2x ");
106                                         break;
107                               case AGP_MODE_V2_RATE_1x:
108                                         printf("1x ");
109                                         break;
110                               }
111                     if (AGP_MODE_GET_SBA(command))
112                               printf("SBA ");
113           } else
114                     printf("disabled");
115 }
116 
117 static void
cap_vpd(int fd __unused,struct pci_conf * p __unused,uint8_t ptr __unused)118 cap_vpd(int fd __unused, struct pci_conf *p __unused, uint8_t ptr __unused)
119 {
120 
121           printf("VPD");
122 }
123 
124 static void
cap_msi(int fd,struct pci_conf * p,uint8_t ptr)125 cap_msi(int fd, struct pci_conf *p, uint8_t ptr)
126 {
127           uint16_t ctrl;
128           int msgnum;
129 
130           ctrl = read_config(fd, &p->pc_sel, ptr + PCIR_MSI_CTRL, 2);
131           msgnum = 1 << ((ctrl & PCIM_MSICTRL_MMC_MASK) >> 1);
132           printf("MSI supports %d message%s%s%s ", msgnum,
133               (msgnum == 1) ? "" : "s",
134               (ctrl & PCIM_MSICTRL_64BIT) ? ", 64 bit" : "",
135               (ctrl & PCIM_MSICTRL_VECTOR) ? ", vector masks" : "");
136           if (ctrl & PCIM_MSICTRL_MSI_ENABLE) {
137                     msgnum = 1 << ((ctrl & PCIM_MSICTRL_MME_MASK) >> 4);
138                     printf("enabled with %d message%s", msgnum,
139                         (msgnum == 1) ? "" : "s");
140           }
141 }
142 
143 static void
cap_pcix(int fd,struct pci_conf * p,uint8_t ptr)144 cap_pcix(int fd, struct pci_conf *p, uint8_t ptr)
145 {
146           uint32_t status;
147           int comma, max_splits = 0, max_burst_read = 0;
148 
149           status = read_config(fd, &p->pc_sel, ptr + PCIXR_STATUS, 4);
150           printf("PCI-X ");
151           if (status & PCIXM_STATUS_64BIT)
152                     printf("64-bit ");
153           if ((p->pc_hdr & PCIM_HDRTYPE) == 1)
154                     printf("bridge ");
155           if ((p->pc_hdr & PCIM_HDRTYPE) != 1 || (status & (PCIXM_STATUS_133CAP |
156               PCIXM_STATUS_266CAP | PCIXM_STATUS_533CAP)) != 0)
157                     printf("supports");
158           comma = 0;
159           if (status & PCIXM_STATUS_133CAP) {
160                     printf("%s 133MHz", comma ? "," : "");
161                     comma = 1;
162           }
163           if (status & PCIXM_STATUS_266CAP) {
164                     printf("%s 266MHz", comma ? "," : "");
165                     comma = 1;
166           }
167           if (status & PCIXM_STATUS_533CAP) {
168                     printf("%s 533MHz", comma ? "," : "");
169                     comma = 1;
170           }
171           if ((p->pc_hdr & PCIM_HDRTYPE) == 1)
172                     return;
173           switch (status & PCIXM_STATUS_MAX_READ) {
174           case PCIXM_STATUS_MAX_READ_512:
175                     max_burst_read = 512;
176                     break;
177           case PCIXM_STATUS_MAX_READ_1024:
178                     max_burst_read = 1024;
179                     break;
180           case PCIXM_STATUS_MAX_READ_2048:
181                     max_burst_read = 2048;
182                     break;
183           case PCIXM_STATUS_MAX_READ_4096:
184                     max_burst_read = 4096;
185                     break;
186           }
187           switch (status & PCIXM_STATUS_MAX_SPLITS) {
188           case PCIXM_STATUS_MAX_SPLITS_1:
189                     max_splits = 1;
190                     break;
191           case PCIXM_STATUS_MAX_SPLITS_2:
192                     max_splits = 2;
193                     break;
194           case PCIXM_STATUS_MAX_SPLITS_3:
195                     max_splits = 3;
196                     break;
197           case PCIXM_STATUS_MAX_SPLITS_4:
198                     max_splits = 4;
199                     break;
200           case PCIXM_STATUS_MAX_SPLITS_8:
201                     max_splits = 8;
202                     break;
203           case PCIXM_STATUS_MAX_SPLITS_12:
204                     max_splits = 12;
205                     break;
206           case PCIXM_STATUS_MAX_SPLITS_16:
207                     max_splits = 16;
208                     break;
209           case PCIXM_STATUS_MAX_SPLITS_32:
210                     max_splits = 32;
211                     break;
212           }
213           printf("%s %d burst read, %d split transaction%s", comma ? "," : "",
214               max_burst_read, max_splits, max_splits == 1 ? "" : "s");
215 }
216 
217 static void
cap_ht(int fd,struct pci_conf * p,uint8_t ptr)218 cap_ht(int fd, struct pci_conf *p, uint8_t ptr)
219 {
220           uint32_t reg;
221           uint16_t command;
222 
223           command = read_config(fd, &p->pc_sel, ptr + PCIR_HT_COMMAND, 2);
224           printf("HT ");
225           if ((command & 0xe000) == PCIM_HTCAP_SLAVE)
226                     printf("slave");
227           else if ((command & 0xe000) == PCIM_HTCAP_HOST)
228                     printf("host");
229           else
230                     switch (command & PCIM_HTCMD_CAP_MASK) {
231                     case PCIM_HTCAP_SWITCH:
232                               printf("switch");
233                               break;
234                     case PCIM_HTCAP_INTERRUPT:
235                               printf("interrupt");
236                               break;
237                     case PCIM_HTCAP_REVISION_ID:
238                               printf("revision ID");
239                               break;
240                     case PCIM_HTCAP_UNITID_CLUMPING:
241                               printf("unit ID clumping");
242                               break;
243                     case PCIM_HTCAP_EXT_CONFIG_SPACE:
244                               printf("extended config space");
245                               break;
246                     case PCIM_HTCAP_ADDRESS_MAPPING:
247                               printf("address mapping");
248                               break;
249                     case PCIM_HTCAP_MSI_MAPPING:
250                               printf("MSI %saddress window %s at 0x",
251                                   command & PCIM_HTCMD_MSI_FIXED ? "fixed " : "",
252                                   command & PCIM_HTCMD_MSI_ENABLE ? "enabled" :
253                                   "disabled");
254                               if (command & PCIM_HTCMD_MSI_FIXED)
255                                         printf("fee00000");
256                               else {
257                                         reg = read_config(fd, &p->pc_sel,
258                                             ptr + PCIR_HTMSI_ADDRESS_HI, 4);
259                                         if (reg != 0)
260                                                   printf("%08x", reg);
261                                         reg = read_config(fd, &p->pc_sel,
262                                             ptr + PCIR_HTMSI_ADDRESS_LO, 4);
263                                         printf("%08x", reg);
264                               }
265                               break;
266                     case PCIM_HTCAP_DIRECT_ROUTE:
267                               printf("direct route");
268                               break;
269                     case PCIM_HTCAP_VCSET:
270                               printf("VC set");
271                               break;
272                     case PCIM_HTCAP_RETRY_MODE:
273                               printf("retry mode");
274                               break;
275                     case PCIM_HTCAP_X86_ENCODING:
276                               printf("X86 encoding");
277                               break;
278                     default:
279                               printf("unknown %02x", command);
280                               break;
281                     }
282 }
283 
284 static void
cap_vendor(int fd,struct pci_conf * p,uint8_t ptr)285 cap_vendor(int fd, struct pci_conf *p, uint8_t ptr)
286 {
287           uint8_t length;
288 
289           length = read_config(fd, &p->pc_sel, ptr + PCIR_VENDOR_LENGTH, 1);
290           printf("vendor (length %d)", length);
291           if (p->pc_vendor == 0x8086) {
292                     /* Intel */
293                     uint8_t version;
294 
295                     version = read_config(fd, &p->pc_sel, ptr + PCIR_VENDOR_DATA,
296                         1);
297                     printf(" Intel cap %d version %d", version >> 4, version & 0xf);
298                     if (version >> 4 == 1 && length == 12) {
299                               /* Feature Detection */
300                               uint32_t fvec;
301                               int comma;
302 
303                               comma = 0;
304                               fvec = read_config(fd, &p->pc_sel, ptr +
305                                   PCIR_VENDOR_DATA + 5, 4);
306                               printf("\n\t\t features:");
307                               if (fvec & (1 << 0)) {
308                                         printf(" AMT");
309                                         comma = 1;
310                               }
311                               fvec = read_config(fd, &p->pc_sel, ptr +
312                                   PCIR_VENDOR_DATA + 1, 4);
313                               if (fvec & (1 << 21)) {
314                                         printf("%s Quick Resume", comma ? "," : "");
315                                         comma = 1;
316                               }
317                               if (fvec & (1 << 18)) {
318                                         printf("%s SATA RAID-5", comma ? "," : "");
319                                         comma = 1;
320                               }
321                               if (fvec & (1 << 9)) {
322                                         printf("%s Mobile", comma ? "," : "");
323                                         comma = 1;
324                               }
325                               if (fvec & (1 << 7)) {
326                                         printf("%s 6 PCI-e x1 slots", comma ? "," : "");
327                                         comma = 1;
328                               } else {
329                                         printf("%s 4 PCI-e x1 slots", comma ? "," : "");
330                                         comma = 1;
331                               }
332                               if (fvec & (1 << 5)) {
333                                         printf("%s SATA RAID-0/1/10", comma ? "," : "");
334                                         comma = 1;
335                               }
336                               if (fvec & (1 << 3)) {
337                                         printf("%s SATA AHCI", comma ? "," : "");
338                                         comma = 1;
339                               }
340                     }
341           }
342 }
343 
344 static void
cap_debug(int fd,struct pci_conf * p,uint8_t ptr)345 cap_debug(int fd, struct pci_conf *p, uint8_t ptr)
346 {
347           uint16_t debug_port;
348 
349           debug_port = read_config(fd, &p->pc_sel, ptr + PCIR_DEBUG_PORT, 2);
350           printf("EHCI Debug Port at offset 0x%x in map 0x%x", debug_port &
351               PCIM_DEBUG_PORT_OFFSET, PCIR_BAR(debug_port >> 13));
352 }
353 
354 static void
cap_subvendor(int fd,struct pci_conf * p,uint8_t ptr)355 cap_subvendor(int fd, struct pci_conf *p, uint8_t ptr)
356 {
357           uint32_t id;
358 
359           id = read_config(fd, &p->pc_sel, ptr + PCIR_SUBVENDCAP_ID, 4);
360           printf("PCI Bridge card=0x%08x", id);
361 }
362 
363 #define   MAX_PAYLOAD(field)            (128 << (field))
364 
365 static void
cap_express(int fd,struct pci_conf * p,uint8_t ptr)366 cap_express(int fd, struct pci_conf *p, uint8_t ptr)
367 {
368           uint32_t cap;
369           uint16_t ctl, flags, sta;
370           unsigned int version;
371 
372           flags = read_config(fd, &p->pc_sel, ptr + PCIER_CAPABILITY, 2);
373           version = flags & PCIEM_CAP_VER_MASK;
374           printf("PCI-Express %u ", version);
375           switch (flags & PCIEM_CAP_PORT_TYPE) {
376           case PCIE_END_POINT:
377                     printf("endpoint");
378                     break;
379           case PCIE_LEG_END_POINT:
380                     printf("legacy endpoint");
381                     break;
382           case PCIE_ROOT_PORT:
383                     printf("root port");
384                     break;
385           case PCIE_UP_STREAM_PORT:
386                     printf("upstream port");
387                     break;
388           case PCIE_DOWN_STREAM_PORT:
389                     printf("downstream port");
390                     break;
391           case PCIE_PCIE2PCI_BRIDGE:
392                     printf("PCI bridge");
393                     break;
394           case PCIE_PCI2PCIE_BRIDGE:
395                     printf("PCI to PCIe bridge");
396                     break;
397           case PCIE_ROOT_END_POINT:
398                     printf("root endpoint");
399                     break;
400           case PCIE_ROOT_EVT_COLL:
401                     printf("event collector");
402                     break;
403           default:
404                     printf("type %d", (flags & PCIEM_CAP_PORT_TYPE) >> 8);
405                     break;
406           }
407           if (flags & PCIEM_CAP_IRQ_MSGNO)
408                     printf(" IRQ %d", (flags & PCIEM_CAP_IRQ_MSGNO) >> 8);
409           cap = read_config(fd, &p->pc_sel, ptr + PCIER_DEVCAP, 4);
410           ctl = read_config(fd, &p->pc_sel, ptr + PCIER_DEVCTRL, 2);
411           printf(" max data %d(%d)",
412               MAX_PAYLOAD((ctl & PCIEM_DEVCAP_MAX_PAYLOAD) >> 5),
413               MAX_PAYLOAD(cap & PCIEM_DEVCAP_MAX_PAYLOAD));
414           if ((cap & PCIEM_CAP_FLR) != 0)
415                     printf(" FLR");
416           if (ctl & PCIEM_DEVCTL_RELAX_ORDER)
417                     printf(" RO");
418           if (ctl & PCIEM_DEVCTL_NOSNOOP)
419                     printf(" NS");
420           if (ctl & PCIEM_DEVCTL_COR_ENABLE)
421                     printf(" COR");
422           if (ctl & PCIEM_DEVCTL_NFER_ENABLE)
423                     printf(" NFER");
424           if (ctl & PCIEM_DEVCTL_FER_ENABLE)
425                     printf(" FER");
426           if (ctl & PCIEM_DEVCTL_URR_ENABLE)
427                     printf(" URR");
428           if (version >= 2) {
429                     cap = read_config(fd, &p->pc_sel, ptr + PCIER_DEVCAP2, 4);
430                     if ((cap & PCIEM_DEVCAP2_ALT_RID_SUPP) != 0) {
431                               ctl = read_config(fd, &p->pc_sel,
432                                                     ptr + PCIER_DEVCTRL2, 4);
433                               printf(" ARI %s",
434                                      (ctl & PCIEM_DEVCTL2_ALT_RID_ENABLE) ?
435                                      "enabled" : "disabled");
436 
437                     }
438                     if ((cap & PCIEM_DEVCAP2_LTR_SUPP) != 0) {
439                               printf(" LTR %s",
440                                      (ctl & PCIEM_DEVCTL2_LTR_ENABLE) ?
441                                      "enabled" : "disabled");
442                     }
443           }
444 
445           cap = read_config(fd, &p->pc_sel, ptr + PCIER_LINKCAP, 4);
446           sta = read_config(fd, &p->pc_sel, ptr+ PCIER_LINKSTAT, 2);
447           if (cap || sta) {
448                     printf(" link x%d(x%d)", (sta & PCIEM_LNKSTAT_WIDTH) >> 4,
449                         (cap & PCIEM_LNKCAP_MAXW_MASK) >> 4);
450           }
451           if (cap & PCIEM_LNKCAP_ASPM_MASK) {
452                     ctl = read_config(fd, &p->pc_sel, ptr + PCIER_LINKCTRL, 2);
453                     printf(" ASPM %s(%s)",
454                               aspm_string(ctl & PCIEM_LNKCTL_ASPM_MASK),
455                               aspm_string((cap & PCIEM_LNKCAP_ASPM_MASK) >> 10));
456           }
457           if (flags & PCIEM_CAP_SLOT_IMPL) {
458                     cap = read_config(fd, &p->pc_sel, ptr + PCIER_SLOTCAP, 4);
459                     sta = read_config(fd, &p->pc_sel, ptr + PCIER_SLOTSTA, 2);
460                     ctl = read_config(fd, &p->pc_sel, ptr + PCIER_SLOTCTL, 2);
461                     printf("\n                ");
462                     printf(" slot %d", (cap & PCIEM_SLOTCAP_PSN) >> 19);
463                     printf(" power limit %d mW", slot_power(cap));
464                     if (cap & PCIEM_SLOTCAP_HP_CAP)
465                               printf(" HotPlug(%s)", sta & PCIEM_SLOTSTA_PDS ?
466                                      "present" : "empty");
467                     if (cap & PCIEM_SLOTCAP_HP_SURP)
468                               printf(" surprise");
469                     if (cap & PCIEM_SLOTCAP_ATTEN_BTN)
470                               printf(" Attn Button");
471                     if (cap & PCIEM_SLOTCAP_PWR_CTRL)
472                               printf(" PC(%s)", ctl & PCIEM_SLOTCTL_PCC ?
473                                      "off" : "on");
474                     if (cap & PCIEM_SLOTCAP_MRL_SNS)
475                               printf(" MRL(%s)", sta & PCIEM_SLOTSTA_MRLSS ?
476                                      "open" : "closed");
477                     if (cap & PCIEM_SLOTCAP_EIP)
478                               printf(" EI(%s)", sta & PCIEM_SLOTSTA_EIS ?
479                                      "engaged" : "disengaged");
480           }
481 }
482 
483 static void
cap_msix(int fd,struct pci_conf * p,uint8_t ptr)484 cap_msix(int fd, struct pci_conf *p, uint8_t ptr)
485 {
486           uint32_t val;
487           uint16_t ctrl;
488           int msgnum, table_bar, pba_bar;
489 
490           ctrl = read_config(fd, &p->pc_sel, ptr + PCIR_MSIX_CTRL, 2);
491           msgnum = (ctrl & PCIM_MSIXCTRL_TABLE_SIZE) + 1;
492           val = read_config(fd, &p->pc_sel, ptr + PCIR_MSIX_TABLE, 4);
493           table_bar = PCIR_BAR(val & PCIM_MSIX_BIR_MASK);
494           val = read_config(fd, &p->pc_sel, ptr + PCIR_MSIX_PBA, 4);
495           pba_bar = PCIR_BAR(val & PCIM_MSIX_BIR_MASK);
496           printf("MSI-X supports %d message%s ", msgnum,
497               (msgnum == 1) ? "" : "s");
498           if (table_bar == pba_bar)
499                     printf("in map 0x%x", table_bar);
500           else
501                     printf("in maps 0x%x and 0x%x", table_bar, pba_bar);
502           if (ctrl & PCIM_MSIXCTRL_MSIX_ENABLE)
503                     printf(" enabled");
504 }
505 
506 static void
cap_sata(__unused int fd,__unused struct pci_conf * p,__unused uint8_t ptr)507 cap_sata(__unused int fd, __unused struct pci_conf *p, __unused uint8_t ptr)
508 {
509 
510           printf("SATA Index-Data Pair");
511 }
512 
513 static void
cap_pciaf(int fd,struct pci_conf * p,uint8_t ptr)514 cap_pciaf(int fd, struct pci_conf *p, uint8_t ptr)
515 {
516           uint8_t cap;
517 
518           cap = read_config(fd, &p->pc_sel, ptr + PCIR_PCIAF_CAP, 1);
519           printf("PCI Advanced Features:%s%s",
520               cap & PCIM_PCIAFCAP_FLR ? " FLR" : "",
521               cap & PCIM_PCIAFCAP_TP  ? " TP"  : "");
522 }
523 
524 void
list_caps(int fd,struct pci_conf * p)525 list_caps(int fd, struct pci_conf *p)
526 {
527           int express;
528           uint16_t sta;
529           uint8_t ptr, cap;
530 
531           /* Are capabilities present for this device? */
532           sta = read_config(fd, &p->pc_sel, PCIR_STATUS, 2);
533           if (!(sta & PCIM_STATUS_CAPPRESENT))
534                     return;
535 
536           switch (p->pc_hdr & PCIM_HDRTYPE) {
537           case PCIM_HDRTYPE_NORMAL:
538           case PCIM_HDRTYPE_BRIDGE:
539                     ptr = PCIR_CAP_PTR;
540                     break;
541           case PCIM_HDRTYPE_CARDBUS:
542                     ptr = PCIR_CAP_PTR_2;
543                     break;
544           default:
545                     errx(1, "list_caps: bad header type");
546           }
547 
548           /* Walk the capability list. */
549           express = 0;
550           ptr = read_config(fd, &p->pc_sel, ptr, 1);
551           while (ptr != 0 && ptr != 0xff) {
552                     cap = read_config(fd, &p->pc_sel, ptr + PCICAP_ID, 1);
553                     printf("    cap %02x[%02x] = ", cap, ptr);
554                     switch (cap) {
555                     case PCIY_PMG:
556                               cap_power(fd, p, ptr);
557                               break;
558                     case PCIY_AGP:
559                               cap_agp(fd, p, ptr);
560                               break;
561                     case PCIY_VPD:
562                               cap_vpd(fd, p, ptr);
563                               break;
564                     case PCIY_MSI:
565                               cap_msi(fd, p, ptr);
566                               break;
567                     case PCIY_PCIX:
568                               cap_pcix(fd, p, ptr);
569                               break;
570                     case PCIY_HT:
571                               cap_ht(fd, p, ptr);
572                               break;
573                     case PCIY_VENDOR:
574                               cap_vendor(fd, p, ptr);
575                               break;
576                     case PCIY_DEBUG:
577                               cap_debug(fd, p, ptr);
578                               break;
579                     case PCIY_SUBVENDOR:
580                               cap_subvendor(fd, p, ptr);
581                               break;
582                     case PCIY_EXPRESS:
583                               express = 1;
584                               cap_express(fd, p, ptr);
585                               break;
586                     case PCIY_MSIX:
587                               cap_msix(fd, p, ptr);
588                               break;
589                     case PCIY_SATA:
590                               cap_sata(fd, p, ptr);
591                               break;
592                     case PCIY_PCIAF:
593                               cap_pciaf(fd, p, ptr);
594                               break;
595                     default:
596                               printf("unknown");
597                               break;
598                     }
599                     printf("\n");
600                     ptr = read_config(fd, &p->pc_sel, ptr + PCICAP_NEXTPTR, 1);
601           }
602 
603           if (express)
604                     list_ecaps(fd, p);
605 }
606 
607 /* From <sys/systm.h>. */
608 static __inline uint32_t
bitcount32(uint32_t x)609 bitcount32(uint32_t x)
610 {
611 
612           x = (x & 0x55555555) + ((x & 0xaaaaaaaa) >> 1);
613           x = (x & 0x33333333) + ((x & 0xcccccccc) >> 2);
614           x = (x + (x >> 4)) & 0x0f0f0f0f;
615           x = (x + (x >> 8));
616           x = (x + (x >> 16)) & 0x000000ff;
617           return (x);
618 }
619 
620 static void
ecap_aer(int fd,struct pci_conf * p,uint16_t ptr,uint8_t ver)621 ecap_aer(int fd, struct pci_conf *p, uint16_t ptr, uint8_t ver)
622 {
623           uint32_t sta, mask;
624 
625           printf("AER %d", ver);
626           if (ver < 1)
627                     return;
628           sta = read_config(fd, &p->pc_sel, ptr + PCIR_AER_UC_STATUS, 4);
629           mask = read_config(fd, &p->pc_sel, ptr + PCIR_AER_UC_SEVERITY, 4);
630           printf(" %d fatal", bitcount32(sta & mask));
631           printf(" %d non-fatal", bitcount32(sta & ~mask));
632           sta = read_config(fd, &p->pc_sel, ptr + PCIR_AER_COR_STATUS, 4);
633           printf(" %d corrected", bitcount32(sta));
634 }
635 
636 static void
ecap_vc(int fd,struct pci_conf * p,uint16_t ptr,uint8_t ver)637 ecap_vc(int fd, struct pci_conf *p, uint16_t ptr, uint8_t ver)
638 {
639           uint32_t cap1;
640 
641           printf("VC %d", ver);
642           if (ver != 1)
643                     return;
644           cap1 = read_config(fd, &p->pc_sel, ptr + PCIR_VC_CAP1, 4);
645           printf(" max VC%d", cap1 & PCIM_VC_CAP1_EXT_COUNT);
646           if ((cap1 & PCIM_VC_CAP1_LOWPRI_EXT_COUNT) != 0)
647                     printf(" lowpri VC0-VC%d",
648                         (cap1 & PCIM_VC_CAP1_LOWPRI_EXT_COUNT) >> 4);
649 }
650 
651 static void
ecap_sernum(int fd,struct pci_conf * p,uint16_t ptr,uint8_t ver)652 ecap_sernum(int fd, struct pci_conf *p, uint16_t ptr, uint8_t ver)
653 {
654           uint32_t high, low;
655 
656           printf("Serial %d", ver);
657           if (ver != 1)
658                     return;
659           low = read_config(fd, &p->pc_sel, ptr + PCIR_SERIAL_LOW, 4);
660           high = read_config(fd, &p->pc_sel, ptr + PCIR_SERIAL_HIGH, 4);
661           printf(" %08x%08x", high, low);
662 }
663 
664 static void
list_ecaps(int fd,struct pci_conf * p)665 list_ecaps(int fd, struct pci_conf *p)
666 {
667           uint32_t ecap;
668           uint16_t ptr;
669 
670           ptr = PCIR_EXTCAP;
671           ecap = read_config(fd, &p->pc_sel, ptr, 4);
672           if (ecap == 0xffffffff || ecap == 0)
673                     return;
674           for (;;) {
675                     printf("ecap %04x[%03x] = ", PCI_EXTCAP_ID(ecap), ptr);
676                     switch (PCI_EXTCAP_ID(ecap)) {
677                     case PCIZ_AER:
678                               ecap_aer(fd, p, ptr, PCI_EXTCAP_VER(ecap));
679                               break;
680                     case PCIZ_VC:
681                               ecap_vc(fd, p, ptr, PCI_EXTCAP_VER(ecap));
682                               break;
683                     case PCIZ_SERNUM:
684                               ecap_sernum(fd, p, ptr, PCI_EXTCAP_VER(ecap));
685                               break;
686                     default:
687                               printf("unknown %d", PCI_EXTCAP_VER(ecap));
688                               break;
689                     }
690                     printf("\n");
691                     ptr = PCI_EXTCAP_NEXTPTR(ecap);
692                     if (ptr == 0)
693                               break;
694                     ecap = read_config(fd, &p->pc_sel, ptr, 4);
695           }
696 }
697 
698 static const char *
aspm_string(uint8_t aspm)699 aspm_string(uint8_t aspm)
700 {
701           switch (aspm) {
702           case 1:
703                     return("L0s");
704           case 2:
705                     return("L1");
706           case 3:
707                     return("L0s/L1");
708           default:
709                     return("disabled");
710           }
711 }
712 
713 static int
slot_power(uint32_t cap)714 slot_power(uint32_t cap)
715 {
716           int mwatts;
717 
718           mwatts = (cap & PCIEM_SLOTCAP_SPLV) >> 7;
719 
720           switch (cap & PCIEM_SLOTCAP_SPLS) {
721           case 0x0:
722                     mwatts *= 10;
723                     /* fallthrough */
724           case 0x1:
725                     mwatts *= 10;
726                     /* fallthrough */
727           case 0x2:
728                     mwatts *= 10;
729                     /* fallthrough */
730           default:
731                     break;
732           }
733           return mwatts;
734 }
735 
736 uint8_t
pci_find_cap(int fd,struct pci_conf * p,uint8_t id)737 pci_find_cap(int fd, struct pci_conf *p, uint8_t id)
738 {
739           uint16_t sta;
740           uint8_t ptr, cap;
741 
742           /* Are capabilities present for this device? */
743           sta = read_config(fd, &p->pc_sel, PCIR_STATUS, 2);
744           if (!(sta & PCIM_STATUS_CAPPRESENT))
745                     return (0);
746 
747           switch (p->pc_hdr & PCIM_HDRTYPE) {
748           case PCIM_HDRTYPE_NORMAL:
749           case PCIM_HDRTYPE_BRIDGE:
750                     ptr = PCIR_CAP_PTR;
751                     break;
752           case PCIM_HDRTYPE_CARDBUS:
753                     ptr = PCIR_CAP_PTR_2;
754                     break;
755           default:
756                     return (0);
757           }
758 
759           ptr = read_config(fd, &p->pc_sel, ptr, 1);
760           while (ptr != 0 && ptr != 0xff) {
761                     cap = read_config(fd, &p->pc_sel, ptr + PCICAP_ID, 1);
762                     if (cap == id)
763                               return (ptr);
764                     ptr = read_config(fd, &p->pc_sel, ptr + PCICAP_NEXTPTR, 1);
765           }
766           return (0);
767 }
768 
769 /* Find offset of a specific extended capability.  Returns 0 on failure. */
770 uint16_t
pcie_find_cap(int fd,struct pci_conf * p,uint16_t id)771 pcie_find_cap(int fd, struct pci_conf *p, uint16_t id)
772 {
773           uint32_t ecap;
774           uint16_t ptr;
775 
776           ptr = PCIR_EXTCAP;
777           ecap = read_config(fd, &p->pc_sel, ptr, 4);
778           if (ecap == 0xffffffff || ecap == 0)
779                     return (0);
780           for (;;) {
781                     if (PCI_EXTCAP_ID(ecap) == id)
782                               return (ptr);
783                     ptr = PCI_EXTCAP_NEXTPTR(ecap);
784                     if (ptr == 0)
785                               break;
786                     ecap = read_config(fd, &p->pc_sel, ptr, 4);
787           }
788           return (0);
789 }
790