cap.c revision 173059
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
31static const char rcsid[] =
32  "$FreeBSD: head/usr.sbin/pciconf/cap.c 173059 2007-10-27 13:16:25Z jhb $";
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
47static void
48cap_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
61static void
62cap_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
115static void
116cap_vpd(int fd, struct pci_conf *p, uint8_t ptr)
117{
118
119	printf("VPD");
120}
121
122static void
123cap_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
141static void
142cap_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
213static void
214cap_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		case PCIM_HTCAP_X86_ENCODING:
272			printf("X86 encoding");
273			break;
274		default:
275			printf("unknown %02x", command);
276			break;
277		}
278}
279
280static void
281cap_vendor(int fd, struct pci_conf *p, uint8_t ptr)
282{
283	uint8_t length;
284
285	length = read_config(fd, &p->pc_sel, ptr + PCIR_VENDOR_LENGTH, 1);
286	printf("vendor (length %d)", length);
287	if (p->pc_vendor == 0x8086) {
288		/* Intel */
289		uint8_t version;
290
291		version = read_config(fd, &p->pc_sel, ptr + PCIR_VENDOR_DATA,
292		    1);
293		printf(" Intel cap %d version %d", version >> 4, version & 0xf);
294		if (version >> 4 == 1 && length == 12) {
295			/* Feature Detection */
296			uint32_t fvec;
297			int comma;
298
299			comma = 0;
300			fvec = read_config(fd, &p->pc_sel, ptr +
301			    PCIR_VENDOR_DATA + 5, 4);
302			printf("\n\t\t features:");
303			if (fvec & (1 << 0)) {
304				printf(" AMT");
305				comma = 1;
306			}
307			fvec = read_config(fd, &p->pc_sel, ptr +
308			    PCIR_VENDOR_DATA + 1, 4);
309			if (fvec & (1 << 21)) {
310				printf("%s Quick Resume", comma ? "," : "");
311				comma = 1;
312			}
313			if (fvec & (1 << 18)) {
314				printf("%s SATA RAID-5", comma ? "," : "");
315				comma = 1;
316			}
317			if (fvec & (1 << 9)) {
318				printf("%s Mobile", comma ? "," : "");
319				comma = 1;
320			}
321			if (fvec & (1 << 7)) {
322				printf("%s 6 PCI-e x1 slots", comma ? "," : "");
323				comma = 1;
324			} else {
325				printf("%s 4 PCI-e x1 slots", comma ? "," : "");
326				comma = 1;
327			}
328			if (fvec & (1 << 5)) {
329				printf("%s SATA RAID-0/1/10", comma ? "," : "");
330				comma = 1;
331			}
332			if (fvec & (1 << 3)) {
333				printf("%s SATA AHCI", comma ? "," : "");
334				comma = 1;
335			}
336		}
337	}
338}
339
340static void
341cap_debug(int fd, struct pci_conf *p, uint8_t ptr)
342{
343	uint16_t debug_port;
344
345	debug_port = read_config(fd, &p->pc_sel, ptr + PCIR_DEBUG_PORT, 2);
346	printf("EHCI Debug Port at offset 0x%x in map 0x%x", debug_port &
347	    PCIM_DEBUG_PORT_OFFSET, PCIR_BAR(debug_port >> 13));
348}
349
350static void
351cap_subvendor(int fd, struct pci_conf *p, uint8_t ptr)
352{
353	uint32_t id;
354
355	id = read_config(fd, &p->pc_sel, ptr + PCIR_SUBVENDCAP_ID, 4);
356	printf("PCI Bridge card=0x%08x", id);
357}
358
359static void
360cap_express(int fd, struct pci_conf *p, uint8_t ptr)
361{
362	uint16_t flags;
363
364	flags = read_config(fd, &p->pc_sel, ptr + PCIR_EXPRESS_FLAGS, 2);
365	printf("PCI-Express %d ", flags & PCIM_EXP_FLAGS_VERSION);
366	switch (flags & PCIM_EXP_FLAGS_TYPE) {
367	case PCIM_EXP_TYPE_ENDPOINT:
368		printf("endpoint");
369		break;
370	case PCIM_EXP_TYPE_LEGACY_ENDPOINT:
371		printf("legacy endpoint");
372		break;
373	case PCIM_EXP_TYPE_ROOT_PORT:
374		printf("root port");
375		break;
376	case PCIM_EXP_TYPE_UPSTREAM_PORT:
377		printf("upstream port");
378		break;
379	case PCIM_EXP_TYPE_DOWNSTREAM_PORT:
380		printf("downstream port");
381		break;
382	case PCIM_EXP_TYPE_PCI_BRIDGE:
383		printf("PCI bridge");
384		break;
385	default:
386		printf("type %d", (flags & PCIM_EXP_FLAGS_TYPE) >> 8);
387		break;
388	}
389	if (flags & PCIM_EXP_FLAGS_IRQ)
390		printf(" IRQ %d", (flags & PCIM_EXP_FLAGS_IRQ) >> 17);
391}
392
393static void
394cap_msix(int fd, struct pci_conf *p, uint8_t ptr)
395{
396	uint32_t val;
397	uint16_t ctrl;
398	int msgnum, table_bar, pba_bar;
399
400	ctrl = read_config(fd, &p->pc_sel, ptr + PCIR_MSIX_CTRL, 2);
401	msgnum = (ctrl & PCIM_MSIXCTRL_TABLE_SIZE) + 1;
402	val = read_config(fd, &p->pc_sel, ptr + PCIR_MSIX_TABLE, 4);
403	table_bar = PCIR_BAR(val & PCIM_MSIX_BIR_MASK);
404	val = read_config(fd, &p->pc_sel, ptr + PCIR_MSIX_PBA, 4);
405	pba_bar = PCIR_BAR(val & PCIM_MSIX_BIR_MASK);
406	printf("MSI-X supports %d message%s ", msgnum,
407	    (msgnum == 1) ? "" : "s");
408	if (table_bar == pba_bar)
409		printf("in map 0x%x", table_bar);
410	else
411		printf("in maps 0x%x and 0x%x", table_bar, pba_bar);
412	if (ctrl & PCIM_MSIXCTRL_MSIX_ENABLE)
413		printf(" enabled");
414}
415
416void
417list_caps(int fd, struct pci_conf *p)
418{
419	uint16_t cmd;
420	uint8_t ptr, cap;
421
422	/* Are capabilities present for this device? */
423	cmd = read_config(fd, &p->pc_sel, PCIR_STATUS, 2);
424	if (!(cmd & PCIM_STATUS_CAPPRESENT))
425		return;
426
427	switch (p->pc_hdr & PCIM_HDRTYPE) {
428	case 0:
429	case 1:
430		ptr = PCIR_CAP_PTR;
431		break;
432	case 2:
433		ptr = PCIR_CAP_PTR_2;
434		break;
435	default:
436		errx(1, "list_caps: bad header type");
437	}
438
439	/* Walk the capability list. */
440	ptr = read_config(fd, &p->pc_sel, ptr, 1);
441	while (ptr != 0 && ptr != 0xff) {
442		cap = read_config(fd, &p->pc_sel, ptr + PCICAP_ID, 1);
443		printf("    cap %02x[%02x] = ", cap, ptr);
444		switch (cap) {
445		case PCIY_PMG:
446			cap_power(fd, p, ptr);
447			break;
448		case PCIY_AGP:
449			cap_agp(fd, p, ptr);
450			break;
451		case PCIY_VPD:
452			cap_vpd(fd, p, ptr);
453			break;
454		case PCIY_MSI:
455			cap_msi(fd, p, ptr);
456			break;
457		case PCIY_PCIX:
458			cap_pcix(fd, p, ptr);
459			break;
460		case PCIY_HT:
461			cap_ht(fd, p, ptr);
462			break;
463		case PCIY_VENDOR:
464			cap_vendor(fd, p, ptr);
465			break;
466		case PCIY_DEBUG:
467			cap_debug(fd, p, ptr);
468			break;
469		case PCIY_SUBVENDOR:
470			cap_subvendor(fd, p, ptr);
471			break;
472		case PCIY_EXPRESS:
473			cap_express(fd, p, ptr);
474			break;
475		case PCIY_MSIX:
476			cap_msix(fd, p, ptr);
477			break;
478		default:
479			printf("unknown");
480			break;
481		}
482		printf("\n");
483		ptr = read_config(fd, &p->pc_sel, ptr + PCICAP_NEXTPTR, 1);
484	}
485}
486