]> mj.ucw.cz Git - pciutils.git/blobdiff - lspci.c
HT improvements from Maciej
[pciutils.git] / lspci.c
diff --git a/lspci.c b/lspci.c
index 07e91844bcd64b3410ed4dcb8bc77d9ca4c49ee2..aa6b37a8c1c42ceb47c14fcb72f536f090b94fec 100644 (file)
--- a/lspci.c
+++ b/lspci.c
@@ -63,12 +63,31 @@ static struct pci_access *pacc;
 struct device {
   struct device *next;
   struct pci_dev *dev;
-  unsigned int config_cnt;
-  byte config[256];
+  unsigned int config_cnt, config_bufsize;
+  byte *config;
 };
 
 static struct device *first_dev;
 
+static int
+config_fetch(struct device *d, unsigned int pos, unsigned int len)
+{
+  unsigned int end = pos+len;
+  int result;
+  if (end <= d->config_cnt)
+    return 1;
+  if (end > d->config_bufsize)
+    {
+      while (end > d->config_bufsize)
+       d->config_bufsize *= 2;
+      d->config = xrealloc(d->config, d->config_bufsize);
+    }
+  result = pci_read_block(d->dev, pos, d->config + pos, len);
+  if (result && pos == d->config_cnt)
+    d->config_cnt = end;
+  return result;
+}
+
 static struct device *
 scan_device(struct pci_dev *p)
 {
@@ -79,15 +98,16 @@ scan_device(struct pci_dev *p)
   d = xmalloc(sizeof(struct device));
   bzero(d, sizeof(*d));
   d->dev = p;
-  d->config_cnt = 64;
+  d->config_cnt = d->config_bufsize = 64;
+  d->config = xmalloc(64);
   if (!pci_read_block(p, 0, d->config, 64))
     die("Unable to read the configuration space header.");
   if ((d->config[PCI_HEADER_TYPE] & 0x7f) == PCI_HEADER_TYPE_CARDBUS)
     {
-      /* For cardbus bridges, we need to fetch 64 bytes more to get the full standard header... */
-      if (!pci_read_block(p, 64, d->config+64, 64))
+      /* For cardbus bridges, we need to fetch 64 bytes more to get the
+       * full standard header... */
+      if (!config_fetch(d, 64, 64))
        die("Unable to read cardbus bridge extension data.");
-      d->config_cnt = 128;
     }
   pci_setup_cache(p, d->config, d->config_cnt);
   pci_fill_info(p, PCI_FILL_IDENT | PCI_FILL_IRQ | PCI_FILL_BASES | PCI_FILL_ROM_BASE | PCI_FILL_SIZES);
@@ -109,14 +129,6 @@ scan_devices(void)
       }
 }
 
-static int
-config_fetch(struct device *d, unsigned int pos, unsigned int len)
-{
-  if (pos + len < d->config_cnt)
-    return 1;
-  return pci_read_block(d->dev, pos, d->config + pos, len);
-}
-
 /* Config space accesses */
 
 static inline byte
@@ -561,20 +573,34 @@ show_ht_pri(struct device *d, int where, int cmd)
 {
   u16 lctr0, lcnf0, lctr1, lcnf1, eh;
   u8 rid, lfrer0, lfcap0, ftr, lfrer1, lfcap1, mbu, mlu, bn;
+  char *fmt;
 
   printf("HyperTransport: Slave or Primary Interface\n");
   if (verbose < 2)
     return;
 
-  printf("\t\tCommand: BaseUnitID=%u UnitCnt=%u MastHost%c DefDir%c DUL%c\n",
+  if (!config_fetch(d, where + PCI_HT_PRI_LCTR0, PCI_HT_PRI_SIZEOF - PCI_HT_PRI_LCTR0))
+    return;
+  rid = get_conf_byte(d, where + PCI_HT_PRI_RID);
+  if (rid < 0x23 && rid > 0x11)
+    printf("\t!!! Possibly incomplete decoding\n");
+
+  if (rid >= 0x23)
+    fmt = "\t\tCommand: BaseUnitID=%u UnitCnt=%u MastHost%c DefDir%c DUL%c\n";
+  else
+    fmt = "\t\tCommand: BaseUnitID=%u UnitCnt=%u MastHost%c DefDir%c\n";
+  printf(fmt,
         (cmd & PCI_HT_PRI_CMD_BUID),
         (cmd & PCI_HT_PRI_CMD_UC) >> 5,
         FLAG(cmd, PCI_HT_PRI_CMD_MH),
         FLAG(cmd, PCI_HT_PRI_CMD_DD),
         FLAG(cmd, PCI_HT_PRI_CMD_DUL));
-  config_fetch(d, where + PCI_HT_PRI_LCTR0, PCI_HT_PRI_SIZEOF - PCI_HT_PRI_LCTR0);
   lctr0 = get_conf_word(d, where + PCI_HT_PRI_LCTR0);
-  printf("\t\tLink Control 0: CFlE%c CST%c CFE%c <LkFail%c Init%c EOC%c TXO%c <CRCErr=%x IsocEn%c LSEn%c ExtCTL%c 64b%c\n",
+  if (rid >= 0x23)
+    fmt = "\t\tLink Control 0: CFlE%c CST%c CFE%c <LkFail%c Init%c EOC%c TXO%c <CRCErr=%x IsocEn%c LSEn%c ExtCTL%c 64b%c\n";
+  else
+    fmt = "\t\tLink Control 0: CFlE%c CST%c CFE%c <LkFail%c Init%c EOC%c TXO%c <CRCErr=%x\n";
+  printf(fmt,
         FLAG(lctr0, PCI_HT_LCTR_CFLE),
         FLAG(lctr0, PCI_HT_LCTR_CST),
         FLAG(lctr0, PCI_HT_LCTR_CFE),
@@ -588,17 +614,25 @@ show_ht_pri(struct device *d, int where, int cmd)
         FLAG(lctr0, PCI_HT_LCTR_EXTCTL),
         FLAG(lctr0, PCI_HT_LCTR_64B));
   lcnf0 = get_conf_word(d, where + PCI_HT_PRI_LCNF0);
-  printf("\t\tLink Config 0: MLWI=%s DwFcIn%c MLWO=%s DwFcOut%c LWI=%s DwFcInEn%c LWO=%s DwFcOutEn%c\n",
+  if (rid >= 0x23)
+    fmt = "\t\tLink Config 0: MLWI=%1$s DwFcIn%5$c MLWO=%2$s DwFcOut%6$c LWI=%3$s DwFcInEn%7$c LWO=%4$s DwFcOutEn%8$c\n";
+  else
+    fmt = "\t\tLink Config 0: MLWI=%s MLWO=%s LWI=%s LWO=%s\n";
+  printf(fmt,
         ht_link_width(lcnf0 & PCI_HT_LCNF_MLWI),
-        FLAG(lcnf0, PCI_HT_LCNF_DFI),
         ht_link_width((lcnf0 & PCI_HT_LCNF_MLWO) >> 4),
-        FLAG(lcnf0, PCI_HT_LCNF_DFO),
         ht_link_width((lcnf0 & PCI_HT_LCNF_LWI) >> 8),
-        FLAG(lcnf0, PCI_HT_LCNF_DFIE),
         ht_link_width((lcnf0 & PCI_HT_LCNF_LWO) >> 12),
+        FLAG(lcnf0, PCI_HT_LCNF_DFI),
+        FLAG(lcnf0, PCI_HT_LCNF_DFO),
+        FLAG(lcnf0, PCI_HT_LCNF_DFIE),
         FLAG(lcnf0, PCI_HT_LCNF_DFOE));
   lctr1 = get_conf_word(d, where + PCI_HT_PRI_LCTR1);
-  printf("\t\tLink Control 1: CFlE%c CST%c CFE%c <LkFail%c Init%c EOC%c TXO%c <CRCErr=%x IsocEn%c LSEn%c ExtCTL%c 64b%c\n",
+  if (rid >= 0x23)
+    fmt = "\t\tLink Control 1: CFlE%c CST%c CFE%c <LkFail%c Init%c EOC%c TXO%c <CRCErr=%x IsocEn%c LSEn%c ExtCTL%c 64b%c\n";
+  else
+    fmt = "\t\tLink Control 1: CFlE%c CST%c CFE%c <LkFail%c Init%c EOC%c TXO%c <CRCErr=%x\n";
+  printf(fmt,
         FLAG(lctr1, PCI_HT_LCTR_CFLE),
         FLAG(lctr1, PCI_HT_LCTR_CST),
         FLAG(lctr1, PCI_HT_LCTR_CFE),
@@ -612,18 +646,23 @@ show_ht_pri(struct device *d, int where, int cmd)
         FLAG(lctr1, PCI_HT_LCTR_EXTCTL),
         FLAG(lctr1, PCI_HT_LCTR_64B));
   lcnf1 = get_conf_word(d, where + PCI_HT_PRI_LCNF1);
-  printf("\t\tLink Config 1: MLWI=%s DwFcIn%c MLWO=%s DwFcOut%c LWI=%s DwFcInEn%c LWO=%s DwFcOutEn%c\n",
+  if (rid >= 0x23)
+    fmt = "\t\tLink Config 1: MLWI=%1$s DwFcIn%5$c MLWO=%2$s DwFcOut%6$c LWI=%3$s DwFcInEn%7$c LWO=%4$s DwFcOutEn%8$c\n";
+  else
+    fmt = "\t\tLink Config 1: MLWI=%s MLWO=%s LWI=%s LWO=%s\n";
+  printf(fmt,
         ht_link_width(lcnf1 & PCI_HT_LCNF_MLWI),
-        FLAG(lcnf1, PCI_HT_LCNF_DFI),
         ht_link_width((lcnf1 & PCI_HT_LCNF_MLWO) >> 4),
-        FLAG(lcnf1, PCI_HT_LCNF_DFO),
         ht_link_width((lcnf1 & PCI_HT_LCNF_LWI) >> 8),
-        FLAG(lcnf1, PCI_HT_LCNF_DFIE),
         ht_link_width((lcnf1 & PCI_HT_LCNF_LWO) >> 12),
+        FLAG(lcnf1, PCI_HT_LCNF_DFI),
+        FLAG(lcnf1, PCI_HT_LCNF_DFO),
+        FLAG(lcnf1, PCI_HT_LCNF_DFIE),
         FLAG(lcnf1, PCI_HT_LCNF_DFOE));
-  rid = get_conf_byte(d, where + PCI_HT_PRI_RID);
   printf("\t\tRevision ID: %u.%02u\n",
         (rid & PCI_HT_RID_MAJ) >> 5, (rid & PCI_HT_RID_MIN));
+  if (rid < 0x23)
+    return;
   lfrer0 = get_conf_byte(d, where + PCI_HT_PRI_LFRER0);
   printf("\t\tLink Frequency 0: %s\n", ht_link_freq(lfrer0 & PCI_HT_LFRER_FREQ));
   printf("\t\tLink Error 0: <Prot%c <Ovfl%c <EOC%c CTLTm%c\n",
@@ -702,12 +741,23 @@ show_ht_sec(struct device *d, int where, int cmd)
 {
   u16 lctr, lcnf, ftr, eh;
   u8 rid, lfrer, lfcap, mbu, mlu;
+  char *fmt;
 
   printf("HyperTransport: Host or Secondary Interface\n");
   if (verbose < 2)
     return;
 
-  printf("\t\tCommand: WarmRst%c DblEnd%c DevNum=%u ChainSide%c HostHide%c Slave%c <EOCErr%c DUL%c\n",
+  if (!config_fetch(d, where + PCI_HT_SEC_LCTR, PCI_HT_SEC_SIZEOF - PCI_HT_SEC_LCTR))
+    return;
+  rid = get_conf_byte(d, where + PCI_HT_SEC_RID);
+  if (rid < 0x23 && rid > 0x11)
+    printf("\t!!! Possibly incomplete decoding\n");
+
+  if (rid >= 0x23)
+    fmt = "\t\tCommand: WarmRst%c DblEnd%c DevNum=%u ChainSide%c HostHide%c Slave%c <EOCErr%c DUL%c\n";
+  else
+    fmt = "\t\tCommand: WarmRst%c DblEnd%c\n";
+  printf(fmt,
         FLAG(cmd, PCI_HT_SEC_CMD_WR),
         FLAG(cmd, PCI_HT_SEC_CMD_DE),
         (cmd & PCI_HT_SEC_CMD_DN) >> 2,
@@ -716,9 +766,12 @@ show_ht_sec(struct device *d, int where, int cmd)
         FLAG(cmd, PCI_HT_SEC_CMD_AS),
         FLAG(cmd, PCI_HT_SEC_CMD_HIECE),
         FLAG(cmd, PCI_HT_SEC_CMD_DUL));
-  config_fetch(d, where + PCI_HT_SEC_LCTR, PCI_HT_SEC_SIZEOF - PCI_HT_SEC_LCTR);
   lctr = get_conf_word(d, where + PCI_HT_SEC_LCTR);
-  printf("\t\tLink Control: CFlE%c CST%c CFE%c <LkFail%c Init%c EOC%c TXO%c <CRCErr=%x IsocEn%c LSEn%c ExtCTL%c 64b%c\n",
+  if (rid >= 0x23)
+    fmt = "\t\tLink Control: CFlE%c CST%c CFE%c <LkFail%c Init%c EOC%c TXO%c <CRCErr=%x IsocEn%c LSEn%c ExtCTL%c 64b%c\n";
+  else
+    fmt = "\t\tLink Control: CFlE%c CST%c CFE%c <LkFail%c Init%c EOC%c TXO%c <CRCErr=%x\n";
+  printf(fmt,
         FLAG(lctr, PCI_HT_LCTR_CFLE),
         FLAG(lctr, PCI_HT_LCTR_CST),
         FLAG(lctr, PCI_HT_LCTR_CFE),
@@ -732,18 +785,23 @@ show_ht_sec(struct device *d, int where, int cmd)
         FLAG(lctr, PCI_HT_LCTR_EXTCTL),
         FLAG(lctr, PCI_HT_LCTR_64B));
   lcnf = get_conf_word(d, where + PCI_HT_SEC_LCNF);
-  printf("\t\tLink Config: MLWI=%s DwFcIn%c MLWO=%s DwFcOut%c LWI=%s DwFcInEn%c LWO=%s DwFcOutEn%c\n",
+  if (rid >= 0x23)
+    fmt = "\t\tLink Config: MLWI=%1$s DwFcIn%5$c MLWO=%2$s DwFcOut%6$c LWI=%3$s DwFcInEn%7$c LWO=%4$s DwFcOutEn%8$c\n";
+  else
+    fmt = "\t\tLink Config: MLWI=%s MLWO=%s LWI=%s LWO=%s\n";
+  printf(fmt,
         ht_link_width(lcnf & PCI_HT_LCNF_MLWI),
-        FLAG(lcnf, PCI_HT_LCNF_DFI),
         ht_link_width((lcnf & PCI_HT_LCNF_MLWO) >> 4),
-        FLAG(lcnf, PCI_HT_LCNF_DFO),
         ht_link_width((lcnf & PCI_HT_LCNF_LWI) >> 8),
-        FLAG(lcnf, PCI_HT_LCNF_DFIE),
         ht_link_width((lcnf & PCI_HT_LCNF_LWO) >> 12),
+        FLAG(lcnf, PCI_HT_LCNF_DFI),
+        FLAG(lcnf, PCI_HT_LCNF_DFO),
+        FLAG(lcnf, PCI_HT_LCNF_DFIE),
         FLAG(lcnf, PCI_HT_LCNF_DFOE));
-  rid = get_conf_byte(d, where + PCI_HT_SEC_RID);
   printf("\t\tRevision ID: %u.%02u\n",
         (rid & PCI_HT_RID_MAJ) >> 5, (rid & PCI_HT_RID_MIN));
+  if (rid < 0x23)
+    return;
   lfrer = get_conf_byte(d, where + PCI_HT_SEC_LFRER);
   printf("\t\tLink Frequency: %s\n", ht_link_freq(lfrer & PCI_HT_LFRER_FREQ));
   printf("\t\tLink Error: <Prot%c <Ovfl%c <EOC%c CTLTm%c\n",
@@ -907,6 +965,251 @@ show_msi(struct device *d, int where, int cap)
   printf("%08x  Data: %04x\n", t, w);
 }
 
+static void show_vendor(void)
+{
+  printf("Vendor Specific Information\n");
+}
+
+static void show_debug(void)
+{
+  printf("Debug port\n");
+}
+
+static float power_limit(int value, int scale)
+{
+  static const float scales[4] = { 1.0, 0.1, 0.01, 0.001 };
+  return value * scales[scale];
+}
+
+static const char *latency_l0s(int value)
+{
+  static const char *latencies[] = { "<64ns", "<128ns", "<256ns", "<512ns", "<1us", "<2us", "<4us", "unlimited" };
+  return latencies[value];
+}
+
+static const char *latency_l1(int value)
+{
+  static const char *latencies[] = { "<1us", "<2us", "<4us", "<8us", "<16us", "<32us", "<64us", "unlimited" };
+  return latencies[value];
+}
+
+static void show_express_dev(struct device *d, int where, int type)
+{
+  u32 t;
+  u16 w;
+
+  t = get_conf_long(d, where + PCI_EXP_DEVCAP);
+  printf("\t\tDevice: Supported: MaxPayload %d bytes, PhantFunc %d, ExtTag%c\n",
+       128 << (t & PCI_EXP_DEVCAP_PAYLOAD),
+       (1 << ((t & PCI_EXP_DEVCAP_PHANTOM) >> 3)) - 1,
+       FLAG(t, PCI_EXP_DEVCAP_EXT_TAG));
+  printf("\t\tDevice: Latency L0s %s, L1 %s\n",
+       latency_l0s((t & PCI_EXP_DEVCAP_L0S) >> 6),
+       latency_l1((t & PCI_EXP_DEVCAP_L1) >> 9));
+  if ((type == PCI_EXP_TYPE_ENDPOINT) || (type == PCI_EXP_TYPE_LEG_END) ||
+      (type == PCI_EXP_TYPE_UPSTREAM) || (type == PCI_EXP_TYPE_PCI_BRIDGE))
+    printf("\t\tDevice: AtnBtn%c AtnInd%c PwrInd%c\n",
+       FLAG(t, PCI_EXP_DEVCAP_ATN_BUT),
+       FLAG(t, PCI_EXP_DEVCAP_ATN_IND), FLAG(t, PCI_EXP_DEVCAP_PWR_IND));
+  if (type == PCI_EXP_TYPE_UPSTREAM)
+    printf("\t\tDevice: SlotPowerLimit %f\n",
+       power_limit((t & PCI_EXP_DEVCAP_PWR_VAL) >> 18,
+                   (t & PCI_EXP_DEVCAP_PWR_SCL) >> 26));
+
+  w = get_conf_word(d, where + PCI_EXP_DEVCTL);
+  printf("\t\tDevice: Errors: Correctable%c Non-Fatal%c Fatal%c Unsupported%c\n",
+       FLAG(w, PCI_EXP_DEVCTL_CERE), 
+       FLAG(w, PCI_EXP_DEVCTL_NFERE), 
+       FLAG(w, PCI_EXP_DEVCTL_FERE), 
+       FLAG(w, PCI_EXP_DEVCTL_URRE));
+  printf("\t\tDevice: RlxdOrd%c ExtTag%c PhantFunc%c AuxPwr%c NoSnoop%c\n",
+       FLAG(w, PCI_EXP_DEVCTL_RELAXED),
+       FLAG(w, PCI_EXP_DEVCTL_EXT_TAG),
+       FLAG(w, PCI_EXP_DEVCTL_PHANTOM),
+       FLAG(w, PCI_EXP_DEVCTL_AUX_PME),
+       FLAG(w, PCI_EXP_DEVCTL_NOSNOOP));
+  printf("\t\tDevice: MaxPayload %d bytes, MaxReadReq %d bytes\n",
+       128 << ((w & PCI_EXP_DEVCTL_PAYLOAD) >> 5),
+       128 << ((w & PCI_EXP_DEVCTL_READRQ) >> 12));
+}
+
+static char *link_speed(int speed)
+{
+  switch (speed)
+    {
+      case 1:
+       return "2.5Gb/s";
+      default:
+       return "unknown";
+    }
+}
+
+static char *aspm_support(int code)
+{
+  switch (code)
+    {
+      case 1:
+       return "L0s";
+      case 3:
+       return "L0s L1";
+      default:
+       return "unknown";
+    }
+}
+
+static const char *aspm_enabled(int code)
+{
+  static const char *desc[] = { "Disabled", "L0s Enabled", "L1 Enabled", "L0s L1 Enabled" };
+  return desc[code];
+}
+
+static void show_express_link(struct device *d, int where, int type)
+{
+  u32 t;
+  u16 w;
+
+  t = get_conf_long(d, where + PCI_EXP_LNKCAP);
+  printf("\t\tLink: Supported Speed %s, Width x%d, ASPM %s, Port %d\n",
+       link_speed(t & PCI_EXP_LNKCAP_SPEED), (t & PCI_EXP_LNKCAP_WIDTH) >> 4,
+       aspm_support((t & PCI_EXP_LNKCAP_ASPM) >> 10),
+       t >> 24);
+  printf("\t\tLink: Latency L0s %s, L1 %s\n",
+       latency_l0s((t & PCI_EXP_LNKCAP_L0S) >> 12),
+       latency_l1((t & PCI_EXP_LNKCAP_L1) >> 15));
+  w = get_conf_word(d, where + PCI_EXP_LNKCTL);
+  printf("\t\tLink: ASPM %s", aspm_enabled(w & PCI_EXP_LNKCTL_ASPM));
+  if ((type == PCI_EXP_TYPE_ROOT_PORT) || (type == PCI_EXP_TYPE_ENDPOINT) ||
+      (type == PCI_EXP_TYPE_LEG_END))
+    printf(" RCB %d bytes", w & PCI_EXP_LNKCTL_RCB ? 128 : 64);
+  if (w & PCI_EXP_LNKCTL_DISABLE)
+    printf(" Disabled");
+  printf(" CommClk%c ExtSynch%c\n", FLAG(w, PCI_EXP_LNKCTL_CLOCK),
+       FLAG(w, PCI_EXP_LNKCTL_XSYNCH));
+  w = get_conf_word(d, where + PCI_EXP_LNKSTA);
+  printf("\t\tLink: Speed %s, Width x%d\n",
+       link_speed(t & PCI_EXP_LNKSTA_SPEED), (t & PCI_EXP_LNKSTA_WIDTH) >> 4);
+}
+
+static const char *indicator(int code)
+{
+  static const char *names[] = { "Unknown", "On", "Blink", "Off" };
+  return names[code];
+}
+
+static void show_express_slot(struct device *d, int where)
+{
+  u32 t;
+  u16 w;
+
+  t = get_conf_long(d, where + PCI_EXP_SLTCAP);
+  printf("\t\tSlot: AtnBtn%c PwrCtrl%c MRL%c AtnInd%c PwrInd%c HotPlug%c Surpise%c\n",
+       FLAG(t, PCI_EXP_SLTCAP_ATNB),
+       FLAG(t, PCI_EXP_SLTCAP_PWRC),
+       FLAG(t, PCI_EXP_SLTCAP_MRL),
+       FLAG(t, PCI_EXP_SLTCAP_ATNI),
+       FLAG(t, PCI_EXP_SLTCAP_PWRI),
+       FLAG(t, PCI_EXP_SLTCAP_HPC),
+       FLAG(t, PCI_EXP_SLTCAP_HPS));
+  printf("\t\tSlot: Number %d, PowerLimit %f\n", t >> 19,
+               power_limit((t & PCI_EXP_SLTCAP_PWR_VAL) >> 7,
+                       (t & PCI_EXP_SLTCAP_PWR_SCL) >> 15));
+  w = get_conf_word(d, where + PCI_EXP_SLTCTL);
+  printf("\t\tSlot: Enabled AtnBtn%c PwrFlt%c MRL%c PresDet%c CmdCplt%c HPIrq%c\n",
+       FLAG(w, PCI_EXP_SLTCTL_ATNB),
+       FLAG(w, PCI_EXP_SLTCTL_PWRF),
+       FLAG(w, PCI_EXP_SLTCTL_MRLS),
+       FLAG(w, PCI_EXP_SLTCTL_PRSD),
+       FLAG(w, PCI_EXP_SLTCTL_CMDC),
+       FLAG(w, PCI_EXP_SLTCTL_HPIE));
+  printf("\t\tSlot: AttnInd %s, PwrInd %s, Power%c\n",
+       indicator((w & PCI_EXP_SLTCTL_ATNI) >> 6),
+       indicator((w & PCI_EXP_SLTCTL_PWRI) >> 8),
+       FLAG(w, w & PCI_EXP_SLTCTL_PWRC));
+}
+
+static void show_express_root(struct device *d, int where)
+{
+  u16 w = get_conf_word(d, where + PCI_EXP_RTCTL);
+  printf("\t\tRoot: Correctable%c Non-Fatal%c Fatal%c PME%c\n",
+       FLAG(w, PCI_EXP_RTCTL_SECEE),
+       FLAG(w, PCI_EXP_RTCTL_SENFEE),
+       FLAG(w, PCI_EXP_RTCTL_SEFEE),
+       FLAG(w, PCI_EXP_RTCTL_PMEIE));
+}
+
+static void
+show_express(struct device *d, int where, int cap)
+{
+  int type = (cap & PCI_EXP_FLAGS_TYPE) >> 4;
+  int size;
+  int slot = 0;
+
+  printf("Express ");
+  switch (type)
+    {
+    case PCI_EXP_TYPE_ENDPOINT:
+      printf("Endpoint");
+      break;
+    case PCI_EXP_TYPE_LEG_END:
+      printf("Legacy Endpoint");
+      break;
+    case PCI_EXP_TYPE_ROOT_PORT:
+      slot = cap & PCI_EXP_FLAGS_SLOT;
+      printf("Root Port (Slot%c)", FLAG(cap, PCI_EXP_FLAGS_SLOT));
+      break;
+    case PCI_EXP_TYPE_UPSTREAM:
+      printf("Upstream Port");
+      break;
+    case PCI_EXP_TYPE_DOWNSTREAM:
+      slot = cap & PCI_EXP_FLAGS_SLOT;
+      printf("Downstream Port (Slot%c)", FLAG(cap, PCI_EXP_FLAGS_SLOT));
+      break;
+    case PCI_EXP_TYPE_PCI_BRIDGE:
+      printf("PCI/PCI-X Bridge");
+      break;
+    default:
+      printf("Unknown type");
+  }
+  printf(" IRQ %d\n", (cap & PCI_EXP_FLAGS_IRQ) >> 9);
+  if (verbose < 2)
+    return;
+
+  size = 16;
+  if (slot)
+    size = 24;
+  if (type == PCI_EXP_TYPE_ROOT_PORT)
+    size = 32;
+  if (!config_fetch(d, where + PCI_EXP_DEVCAP, size))
+    return;
+
+  show_express_dev(d, where, type);
+  show_express_link(d, where, type);
+  if (slot)
+    show_express_slot(d, where);
+  if (type == PCI_EXP_TYPE_ROOT_PORT)
+    show_express_root(d, where);
+}
+
+static void
+show_msix(struct device *d, int where, int cap)
+{
+  u32 off;
+
+  printf("MSI-X: Enable%c Mask%c TabSize=%d\n",
+        FLAG(cap, PCI_MSIX_ENABLE),
+        FLAG(cap, PCI_MSIX_MASK),
+        (cap & PCI_MSIX_TABSIZE) + 1);
+  if (verbose < 2 || !config_fetch(d, where + PCI_MSIX_TABLE, 8))
+    return;
+
+  off = get_conf_long(d, where + PCI_MSIX_TABLE);
+  printf("\t\tVector table: BAR=%d offset=%08x\n",
+        off & PCI_MSIX_BIR, off & ~PCI_MSIX_BIR);
+  off = get_conf_long(d, where + PCI_MSIX_PBA);
+  printf("\t\tPBA: BAR=%d offset=%08x\n",
+        off & PCI_MSIX_BIR, off & ~PCI_MSIX_BIR);
+}
+
 static void
 show_slotid(int cap)
 {
@@ -919,6 +1222,75 @@ show_slotid(int cap)
         chs);
 }
 
+static void
+show_aer(struct device *d, int where)
+{
+  printf("Advanced Error Reporting\n");
+}
+
+static void
+show_vc(struct device *d, int where)
+{
+  printf("Virtual Channel\n");
+}
+
+static void
+show_dsn(struct device *d, int where)
+{
+  u32 t1, t2;
+  if (!config_fetch(d, where + 4, 8))
+    return;
+  t1 = get_conf_long(d, where + 4);
+  t2 = get_conf_long(d, where + 8);
+  printf("Device Serial Number %02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x\n",
+       t1 & 0xff, (t1 >> 8) & 0xff, (t1 >> 16) & 0xff, t1 >> 24,
+       t2 & 0xff, (t2 >> 8) & 0xff, (t2 >> 16) & 0xff, t2 >> 24);
+}
+
+static void
+show_pb(struct device *d, int where)
+{
+  printf("Power Budgeting\n");
+}
+
+static void
+show_ext_caps(struct device *d)
+{
+  int where = 0x100;
+  do
+    {
+      u32 header;
+      int id;
+
+      if (!config_fetch(d, where, 4))
+       break;
+      header = get_conf_long(d, where);
+      if (!header)
+       break;
+      id = header & 0xffff;
+      printf("\tCapabilities: [%03x] ", where);
+      switch (id)
+       {
+         case PCI_EXT_CAP_ID_AER:
+           show_aer(d, where);
+           break;
+         case PCI_EXT_CAP_ID_VC:
+           show_vc(d, where);
+           break;
+         case PCI_EXT_CAP_ID_DSN:
+           show_dsn(d, where);
+           break;
+         case PCI_EXT_CAP_ID_PB:
+           show_pb(d, where);
+           break;
+         default:
+           printf("Unknown (%d)\n", id);
+           break;
+       }
+      where = header >> 20;
+    } while (where);
+}
+
 static void
 show_caps(struct device *d)
 {
@@ -966,12 +1338,25 @@ show_caps(struct device *d)
            case PCI_CAP_ID_HT:
              show_ht(d, where, cap);
              break;
+           case PCI_CAP_ID_VNDR:
+             show_vendor();
+             break;
+           case PCI_CAP_ID_DBG:
+             show_debug();
+             break;
+           case PCI_CAP_ID_EXP:
+             show_express(d, where, cap);
+             break;
+           case PCI_CAP_ID_MSIX:
+             show_msix(d, where, cap);
+             break;
            default:
              printf("#%02x [%04x]\n", id, cap);
            }
          where = next;
        }
     }
+  show_ext_caps(d);
 }
 
 static void