1 /*- 2 * Copyright (c) 2007 John Baldwin <jhb@FreeBSD.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. Neither the name of the author nor the names of any co-contributors 14 * may be used to endorse or promote products derived from this software 15 * without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 */ 29 30 #ifndef lint 31 static const char rcsid[] = 32 "$FreeBSD$"; 33 #endif /* not lint */ 34 35 #include <sys/types.h> 36 37 #include <err.h> 38 #include <stdio.h> 39 #include <sys/agpio.h> 40 #include <sys/pciio.h> 41 42 #include <pci/agpreg.h> 43 #include <dev/pci/pcireg.h> 44 45 #include "pciconf.h" 46 47 static void 48 cap_power(int fd, struct pci_conf *p, uint8_t ptr) 49 { 50 uint16_t cap, status; 51 52 cap = read_config(fd, &p->pc_sel, ptr + PCIR_POWER_CAP, 2); 53 status = read_config(fd, &p->pc_sel, ptr + PCIR_POWER_STATUS, 2); 54 printf("powerspec %d supports D0%s%s D3 current D%d", 55 cap & PCIM_PCAP_SPEC, 56 cap & PCIM_PCAP_D1SUPP ? " D1" : "", 57 cap & PCIM_PCAP_D2SUPP ? " D2" : "", 58 status & PCIM_PSTAT_DMASK); 59 } 60 61 static void 62 cap_agp(int fd, struct pci_conf *p, uint8_t ptr) 63 { 64 uint32_t status, command; 65 66 status = read_config(fd, &p->pc_sel, ptr + AGP_STATUS, 4); 67 command = read_config(fd, &p->pc_sel, ptr + AGP_CAPID, 4); 68 printf("AGP "); 69 if (AGP_MODE_GET_MODE_3(status)) { 70 printf("v3 "); 71 if (AGP_MODE_GET_RATE(status) & AGP_MODE_V3_RATE_8x) 72 printf("8x "); 73 if (AGP_MODE_GET_RATE(status) & AGP_MODE_V3_RATE_4x) 74 printf("4x "); 75 } else { 76 if (AGP_MODE_GET_RATE(status) & AGP_MODE_V2_RATE_4x) 77 printf("4x "); 78 if (AGP_MODE_GET_RATE(status) & AGP_MODE_V2_RATE_2x) 79 printf("2x "); 80 if (AGP_MODE_GET_RATE(status) & AGP_MODE_V2_RATE_1x) 81 printf("1x "); 82 } 83 if (AGP_MODE_GET_SBA(status)) 84 printf("SBA "); 85 if (AGP_MODE_GET_AGP(command)) { 86 printf("enabled at "); 87 if (AGP_MODE_GET_MODE_3(command)) { 88 printf("v3 "); 89 switch (AGP_MODE_GET_RATE(command)) { 90 case AGP_MODE_V3_RATE_8x: 91 printf("8x "); 92 break; 93 case AGP_MODE_V3_RATE_4x: 94 printf("4x "); 95 break; 96 } 97 } else 98 switch (AGP_MODE_GET_RATE(command)) { 99 case AGP_MODE_V2_RATE_4x: 100 printf("4x "); 101 break; 102 case AGP_MODE_V2_RATE_2x: 103 printf("2x "); 104 break; 105 case AGP_MODE_V2_RATE_1x: 106 printf("1x "); 107 break; 108 } 109 if (AGP_MODE_GET_SBA(command)) 110 printf("SBA "); 111 } else 112 printf("disabled"); 113 } 114 115 static void 116 cap_vpd(int fd, struct pci_conf *p, uint8_t ptr) 117 { 118 119 printf("VPD"); 120 } 121 122 static void 123 cap_msi(int fd, struct pci_conf *p, uint8_t ptr) 124 { 125 uint16_t ctrl; 126 int msgnum; 127 128 ctrl = read_config(fd, &p->pc_sel, ptr + PCIR_MSI_CTRL, 2); 129 msgnum = 1 << ((ctrl & PCIM_MSICTRL_MMC_MASK) >> 1); 130 printf("MSI supports %d message%s%s%s ", msgnum, 131 (msgnum == 1) ? "" : "s", 132 (ctrl & PCIM_MSICTRL_64BIT) ? ", 64 bit" : "", 133 (ctrl & PCIM_MSICTRL_VECTOR) ? ", vector masks" : ""); 134 if (ctrl & PCIM_MSICTRL_MSI_ENABLE) { 135 msgnum = 1 << ((ctrl & PCIM_MSICTRL_MME_MASK) >> 4); 136 printf("enabled with %d message%s", msgnum, 137 (msgnum == 1) ? "" : "s"); 138 } 139 } 140 141 static void 142 cap_pcix(int fd, struct pci_conf *p, uint8_t ptr) 143 { 144 uint32_t status; 145 int comma, max_splits, max_burst_read; 146 147 status = read_config(fd, &p->pc_sel, ptr + PCIXR_STATUS, 4); 148 printf("PCI-X "); 149 if (status & PCIXM_STATUS_64BIT) 150 printf("64-bit "); 151 if ((p->pc_hdr & PCIM_HDRTYPE) == 1) 152 printf("bridge "); 153 printf("supports"); 154 comma = 0; 155 if (status & PCIXM_STATUS_133CAP) { 156 printf("%s 133MHz", comma ? "," : ""); 157 comma = 1; 158 } 159 if (status & PCIXM_STATUS_266CAP) { 160 printf("%s 266MHz", comma ? "," : ""); 161 comma = 1; 162 } 163 if (status & PCIXM_STATUS_533CAP) { 164 printf("%s 533MHz", comma ? "," : ""); 165 comma = 1; 166 } 167 if ((p->pc_hdr & PCIM_HDRTYPE) == 1) 168 return; 169 switch (status & PCIXM_STATUS_MAX_READ) { 170 case PCIXM_STATUS_MAX_READ_512: 171 max_burst_read = 512; 172 break; 173 case PCIXM_STATUS_MAX_READ_1024: 174 max_burst_read = 1024; 175 break; 176 case PCIXM_STATUS_MAX_READ_2048: 177 max_burst_read = 2048; 178 break; 179 case PCIXM_STATUS_MAX_READ_4096: 180 max_burst_read = 4096; 181 break; 182 } 183 switch (status & PCIXM_STATUS_MAX_SPLITS) { 184 case PCIXM_STATUS_MAX_SPLITS_1: 185 max_splits = 1; 186 break; 187 case PCIXM_STATUS_MAX_SPLITS_2: 188 max_splits = 2; 189 break; 190 case PCIXM_STATUS_MAX_SPLITS_3: 191 max_splits = 3; 192 break; 193 case PCIXM_STATUS_MAX_SPLITS_4: 194 max_splits = 4; 195 break; 196 case PCIXM_STATUS_MAX_SPLITS_8: 197 max_splits = 8; 198 break; 199 case PCIXM_STATUS_MAX_SPLITS_12: 200 max_splits = 12; 201 break; 202 case PCIXM_STATUS_MAX_SPLITS_16: 203 max_splits = 16; 204 break; 205 case PCIXM_STATUS_MAX_SPLITS_32: 206 max_splits = 32; 207 break; 208 } 209 printf("%s %d burst read, %d split transaction%s", comma ? "," : "", 210 max_burst_read, max_splits, max_splits == 1 ? "" : "s"); 211 } 212 213 static void 214 cap_ht(int fd, struct pci_conf *p, uint8_t ptr) 215 { 216 uint32_t reg; 217 uint16_t command; 218 219 command = read_config(fd, &p->pc_sel, ptr + PCIR_HT_COMMAND, 2); 220 printf("HT "); 221 if ((command & 0xe000) == PCIM_HTCAP_SLAVE) 222 printf("slave"); 223 else if ((command & 0xe000) == PCIM_HTCAP_HOST) 224 printf("host"); 225 else 226 switch (command & PCIM_HTCMD_CAP_MASK) { 227 case PCIM_HTCAP_SWITCH: 228 printf("switch"); 229 break; 230 case PCIM_HTCAP_INTERRUPT: 231 printf("interrupt"); 232 break; 233 case PCIM_HTCAP_REVISION_ID: 234 printf("revision ID"); 235 break; 236 case PCIM_HTCAP_UNITID_CLUMPING: 237 printf("unit ID clumping"); 238 break; 239 case PCIM_HTCAP_EXT_CONFIG_SPACE: 240 printf("extended config space"); 241 break; 242 case PCIM_HTCAP_ADDRESS_MAPPING: 243 printf("address mapping"); 244 break; 245 case PCIM_HTCAP_MSI_MAPPING: 246 printf("MSI %saddress window %s at 0x", 247 command & PCIM_HTCMD_MSI_FIXED ? "fixed " : "", 248 command & PCIM_HTCMD_MSI_ENABLE ? "enabled" : 249 "disabled"); 250 if (command & PCIM_HTCMD_MSI_FIXED) 251 printf("fee00000"); 252 else { 253 reg = read_config(fd, &p->pc_sel, 254 ptr + PCIR_HTMSI_ADDRESS_HI, 4); 255 if (reg != 0) 256 printf("%08x", reg); 257 reg = read_config(fd, &p->pc_sel, 258 ptr + PCIR_HTMSI_ADDRESS_LO, 4); 259 printf("%08x", reg); 260 } 261 break; 262 case PCIM_HTCAP_DIRECT_ROUTE: 263 printf("direct route"); 264 break; 265 case PCIM_HTCAP_VCSET: 266 printf("VC set"); 267 break; 268 case PCIM_HTCAP_RETRY_MODE: 269 printf("retry mode"); 270 break; 271 default: 272 printf("unknown %02x", command); 273 break; 274 } 275 } 276 277 static void 278 cap_vendor(int fd, struct pci_conf *p, uint8_t ptr) 279 { 280 uint8_t length; 281 282 length = read_config(fd, &p->pc_sel, ptr + PCIR_VENDOR_LENGTH, 1); 283 printf("vendor (length %d)", length); 284 if (p->pc_vendor == 0x8086) { 285 /* Intel */ 286 uint8_t version; 287 288 version = read_config(fd, &p->pc_sel, ptr + PCIR_VENDOR_DATA, 289 1); 290 printf(" Intel cap %d version %d", version >> 4, version & 0xf); 291 if (version >> 4 == 1 && length == 12) { 292 /* Feature Detection */ 293 uint32_t fvec; 294 int comma; 295 296 comma = 0; 297 fvec = read_config(fd, &p->pc_sel, ptr + 298 PCIR_VENDOR_DATA + 5, 4); 299 printf("\n\t\t features:"); 300 if (fvec & (1 << 0)) { 301 printf(" AMT"); 302 comma = 1; 303 } 304 fvec = read_config(fd, &p->pc_sel, ptr + 305 PCIR_VENDOR_DATA + 1, 4); 306 if (fvec & (1 << 21)) { 307 printf("%s Quick Resume", comma ? "," : ""); 308 comma = 1; 309 } 310 if (fvec & (1 << 18)) { 311 printf("%s SATA RAID-5", comma ? "," : ""); 312 comma = 1; 313 } 314 if (fvec & (1 << 9)) { 315 printf("%s Mobile", comma ? "," : ""); 316 comma = 1; 317 } 318 if (fvec & (1 << 7)) { 319 printf("%s 6 PCI-e x1 slots", comma ? "," : ""); 320 comma = 1; 321 } else { 322 printf("%s 4 PCI-e x1 slots", comma ? "," : ""); 323 comma = 1; 324 } 325 if (fvec & (1 << 5)) { 326 printf("%s SATA RAID-0/1/10", comma ? "," : ""); 327 comma = 1; 328 } 329 if (fvec & (1 << 3)) { 330 printf("%s SATA AHCI", comma ? "," : ""); 331 comma = 1; 332 } 333 } 334 } 335 } 336 337 static void 338 cap_debug(int fd, struct pci_conf *p, uint8_t ptr) 339 { 340 uint16_t debug_port; 341 342 debug_port = read_config(fd, &p->pc_sel, ptr + PCIR_DEBUG_PORT, 2); 343 printf("EHCI Debug Port at offset 0x%x in map 0x%x", debug_port & 344 PCIM_DEBUG_PORT_OFFSET, PCIR_BAR(debug_port >> 13)); 345 } 346 347 static void 348 cap_subvendor(int fd, struct pci_conf *p, uint8_t ptr) 349 { 350 uint32_t id; 351 352 id = read_config(fd, &p->pc_sel, ptr + PCIR_SUBVENDCAP_ID, 4); 353 printf("PCI Bridge card=0x%08x", id); 354 } 355 356 static void 357 cap_express(int fd, struct pci_conf *p, uint8_t ptr) 358 { 359 uint16_t flags; 360 361 flags = read_config(fd, &p->pc_sel, ptr + PCIR_EXPRESS_FLAGS, 2); 362 printf("PCI-Express %d ", flags & PCIM_EXP_FLAGS_VERSION); 363 switch (flags & PCIM_EXP_FLAGS_TYPE) { 364 case PCIM_EXP_TYPE_ENDPOINT: 365 printf("endpoint"); 366 break; 367 case PCIM_EXP_TYPE_LEGACY_ENDPOINT: 368 printf("legacy endpoint"); 369 break; 370 case PCIM_EXP_TYPE_ROOT_PORT: 371 printf("root port"); 372 break; 373 case PCIM_EXP_TYPE_UPSTREAM_PORT: 374 printf("upstream port"); 375 break; 376 case PCIM_EXP_TYPE_DOWNSTREAM_PORT: 377 printf("downstream port"); 378 break; 379 case PCIM_EXP_TYPE_PCI_BRIDGE: 380 printf("PCI bridge"); 381 break; 382 default: 383 printf("type %d", (flags & PCIM_EXP_FLAGS_TYPE) >> 8); 384 break; 385 } 386 if (flags & PCIM_EXP_FLAGS_IRQ) 387 printf(" IRQ %d", (flags & PCIM_EXP_FLAGS_IRQ) >> 17); 388 } 389 390 static void 391 cap_msix(int fd, struct pci_conf *p, uint8_t ptr) 392 { 393 uint32_t val; 394 uint16_t ctrl; 395 int msgnum, table_bar, pba_bar; 396 397 ctrl = read_config(fd, &p->pc_sel, ptr + PCIR_MSIX_CTRL, 2); 398 msgnum = (ctrl & PCIM_MSIXCTRL_TABLE_SIZE) + 1; 399 val = read_config(fd, &p->pc_sel, ptr + PCIR_MSIX_TABLE, 4); 400 table_bar = PCIR_BAR(val & PCIM_MSIX_BIR_MASK); 401 val = read_config(fd, &p->pc_sel, ptr + PCIR_MSIX_PBA, 4); 402 pba_bar = PCIR_BAR(val & PCIM_MSIX_BIR_MASK); 403 printf("MSI-X supports %d message%s ", msgnum, 404 (msgnum == 1) ? "" : "s"); 405 if (table_bar == pba_bar) 406 printf("in map 0x%x", table_bar); 407 else 408 printf("in maps 0x%x and 0x%x", table_bar, pba_bar); 409 if (ctrl & PCIM_MSIXCTRL_MSIX_ENABLE) 410 printf(" enabled"); 411 } 412 413 void 414 list_caps(int fd, struct pci_conf *p) 415 { 416 uint16_t cmd; 417 uint8_t ptr, cap; 418 419 /* Are capabilities present for this device? */ 420 cmd = read_config(fd, &p->pc_sel, PCIR_STATUS, 2); 421 if (!(cmd & PCIM_STATUS_CAPPRESENT)) 422 return; 423 424 switch (p->pc_hdr & PCIM_HDRTYPE) { 425 case 0: 426 case 1: 427 ptr = PCIR_CAP_PTR; 428 break; 429 case 2: 430 ptr = PCIR_CAP_PTR_2; 431 break; 432 default: 433 errx(1, "list_caps: bad header type"); 434 } 435 436 /* Walk the capability list. */ 437 ptr = read_config(fd, &p->pc_sel, ptr, 1); 438 while (ptr != 0 && ptr != 0xff) { 439 cap = read_config(fd, &p->pc_sel, ptr + PCICAP_ID, 1); 440 printf(" cap %02x[%02x] = ", cap, ptr); 441 switch (cap) { 442 case PCIY_PMG: 443 cap_power(fd, p, ptr); 444 break; 445 case PCIY_AGP: 446 cap_agp(fd, p, ptr); 447 break; 448 case PCIY_VPD: 449 cap_vpd(fd, p, ptr); 450 break; 451 case PCIY_MSI: 452 cap_msi(fd, p, ptr); 453 break; 454 case PCIY_PCIX: 455 cap_pcix(fd, p, ptr); 456 break; 457 case PCIY_HT: 458 cap_ht(fd, p, ptr); 459 break; 460 case PCIY_VENDOR: 461 cap_vendor(fd, p, ptr); 462 break; 463 case PCIY_DEBUG: 464 cap_debug(fd, p, ptr); 465 break; 466 case PCIY_SUBVENDOR: 467 cap_subvendor(fd, p, ptr); 468 break; 469 case PCIY_EXPRESS: 470 cap_express(fd, p, ptr); 471 break; 472 case PCIY_MSIX: 473 cap_msix(fd, p, ptr); 474 break; 475 default: 476 printf("unknown"); 477 break; 478 } 479 printf("\n"); 480 ptr = read_config(fd, &p->pc_sel, ptr + PCICAP_NEXTPTR, 1); 481 } 482 } 483