xref: /kvm-unit-tests/lib/pci.c (revision 66082ed62971f408a33385bebc0676e0ac051cf2)
1456c55bcSAndrew Jones /*
2456c55bcSAndrew Jones  * Copyright (C) 2013, Red Hat Inc, Michael S. Tsirkin <mst@redhat.com>
3456c55bcSAndrew Jones  *
4456c55bcSAndrew Jones  * This work is licensed under the terms of the GNU LGPL, version 2.
5456c55bcSAndrew Jones  */
64932b58aSMichael S. Tsirkin #include <linux/pci_regs.h>
74932b58aSMichael S. Tsirkin #include "pci.h"
8456c55bcSAndrew Jones #include "asm/pci.h"
94932b58aSMichael S. Tsirkin 
10*66082ed6SPeter Xu void pci_cmd_set_clr(struct pci_dev *dev, uint16_t set, uint16_t clr)
11*66082ed6SPeter Xu {
12*66082ed6SPeter Xu 	uint16_t val = pci_config_readw(dev->bdf, PCI_COMMAND);
13*66082ed6SPeter Xu 
14*66082ed6SPeter Xu 	/* No overlap is allowed */
15*66082ed6SPeter Xu 	assert((set & clr) == 0);
16*66082ed6SPeter Xu 	val |= set;
17*66082ed6SPeter Xu 	val &= ~clr;
18*66082ed6SPeter Xu 
19*66082ed6SPeter Xu 	pci_config_writew(dev->bdf, PCI_COMMAND, val);
20*66082ed6SPeter Xu }
21*66082ed6SPeter Xu 
22e1cad5c8SAlexander Gordeev bool pci_dev_exists(pcidevaddr_t dev)
23e1cad5c8SAlexander Gordeev {
24e1cad5c8SAlexander Gordeev 	return (pci_config_readw(dev, PCI_VENDOR_ID) != 0xffff &&
25e1cad5c8SAlexander Gordeev 		pci_config_readw(dev, PCI_DEVICE_ID) != 0xffff);
26e1cad5c8SAlexander Gordeev }
27e1cad5c8SAlexander Gordeev 
284d6cefa9SPeter Xu void pci_dev_init(struct pci_dev *dev, pcidevaddr_t bdf)
294d6cefa9SPeter Xu {
304d6cefa9SPeter Xu        memset(dev, 0, sizeof(*dev));
314d6cefa9SPeter Xu        dev->bdf = bdf;
324d6cefa9SPeter Xu }
334d6cefa9SPeter Xu 
344932b58aSMichael S. Tsirkin /* Scan bus look for a specific device. Only bus 0 scanned for now. */
354932b58aSMichael S. Tsirkin pcidevaddr_t pci_find_dev(uint16_t vendor_id, uint16_t device_id)
364932b58aSMichael S. Tsirkin {
37ebb58e7eSAlexander Gordeev 	pcidevaddr_t dev;
38ebb58e7eSAlexander Gordeev 
394d6cefa9SPeter Xu 	for (dev = 0; dev < PCI_DEVFN_MAX; ++dev) {
40d8369c77SAlexander Gordeev 		if (pci_config_readw(dev, PCI_VENDOR_ID) == vendor_id &&
41d8369c77SAlexander Gordeev 		    pci_config_readw(dev, PCI_DEVICE_ID) == device_id)
424932b58aSMichael S. Tsirkin 			return dev;
434932b58aSMichael S. Tsirkin 	}
44ebb58e7eSAlexander Gordeev 
454932b58aSMichael S. Tsirkin 	return PCIDEVADDR_INVALID;
464932b58aSMichael S. Tsirkin }
474932b58aSMichael S. Tsirkin 
4833d78b07SAlexander Gordeev uint32_t pci_bar_mask(uint32_t bar)
492455ef20SAlexander Gordeev {
502455ef20SAlexander Gordeev 	return (bar & PCI_BASE_ADDRESS_SPACE_IO) ?
512455ef20SAlexander Gordeev 		PCI_BASE_ADDRESS_IO_MASK : PCI_BASE_ADDRESS_MEM_MASK;
522455ef20SAlexander Gordeev }
532455ef20SAlexander Gordeev 
544d6cefa9SPeter Xu uint32_t pci_bar_get(struct pci_dev *dev, int bar_num)
557aa83307SAlexander Gordeev {
564d6cefa9SPeter Xu 	return pci_config_readl(dev->bdf, PCI_BASE_ADDRESS_0 +
574d6cefa9SPeter Xu 				bar_num * 4);
587aa83307SAlexander Gordeev }
597aa83307SAlexander Gordeev 
604d6cefa9SPeter Xu phys_addr_t pci_bar_get_addr(struct pci_dev *dev, int bar_num)
614932b58aSMichael S. Tsirkin {
627aa83307SAlexander Gordeev 	uint32_t bar = pci_bar_get(dev, bar_num);
632455ef20SAlexander Gordeev 	uint32_t mask = pci_bar_mask(bar);
642455ef20SAlexander Gordeev 	uint64_t addr = bar & mask;
65647d2ab7SAlexander Gordeev 	phys_addr_t phys_addr;
66ebb58e7eSAlexander Gordeev 
672455ef20SAlexander Gordeev 	if (pci_bar_is64(dev, bar_num))
682455ef20SAlexander Gordeev 		addr |= (uint64_t)pci_bar_get(dev, bar_num + 1) << 32;
692455ef20SAlexander Gordeev 
704d6cefa9SPeter Xu 	phys_addr = pci_translate_addr(dev->bdf, addr);
71647d2ab7SAlexander Gordeev 	assert(phys_addr != INVALID_PHYS_ADDR);
72647d2ab7SAlexander Gordeev 
73647d2ab7SAlexander Gordeev 	return phys_addr;
742455ef20SAlexander Gordeev }
752455ef20SAlexander Gordeev 
764d6cefa9SPeter Xu void pci_bar_set_addr(struct pci_dev *dev, int bar_num, phys_addr_t addr)
77647f92c7SAlexander Gordeev {
78647f92c7SAlexander Gordeev 	int off = PCI_BASE_ADDRESS_0 + bar_num * 4;
79647f92c7SAlexander Gordeev 
804d6cefa9SPeter Xu 	pci_config_writel(dev->bdf, off, (uint32_t)addr);
81647f92c7SAlexander Gordeev 
82647f92c7SAlexander Gordeev 	if (pci_bar_is64(dev, bar_num))
834d6cefa9SPeter Xu 		pci_config_writel(dev->bdf, off + 4,
844d6cefa9SPeter Xu 				  (uint32_t)(addr >> 32));
85647f92c7SAlexander Gordeev }
86647f92c7SAlexander Gordeev 
872455ef20SAlexander Gordeev /*
882455ef20SAlexander Gordeev  * To determine the amount of address space needed by a PCI device,
892455ef20SAlexander Gordeev  * one must save the original value of the BAR, write a value of
902455ef20SAlexander Gordeev  * all 1's to the register, and then read it back. The amount of
912455ef20SAlexander Gordeev  * memory can be then determined by masking the information bits,
922455ef20SAlexander Gordeev  * performing a bitwise NOT, and incrementing the value by 1.
932455ef20SAlexander Gordeev  *
942455ef20SAlexander Gordeev  * The following pci_bar_size_helper() and pci_bar_size() functions
952455ef20SAlexander Gordeev  * implement the algorithm.
962455ef20SAlexander Gordeev  */
974d6cefa9SPeter Xu static uint32_t pci_bar_size_helper(struct pci_dev *dev, int bar_num)
982455ef20SAlexander Gordeev {
992455ef20SAlexander Gordeev 	int off = PCI_BASE_ADDRESS_0 + bar_num * 4;
1004d6cefa9SPeter Xu 	uint16_t bdf = dev->bdf;
1012455ef20SAlexander Gordeev 	uint32_t bar, val;
1022455ef20SAlexander Gordeev 
1034d6cefa9SPeter Xu 	bar = pci_config_readl(bdf, off);
1044d6cefa9SPeter Xu 	pci_config_writel(bdf, off, ~0u);
1054d6cefa9SPeter Xu 	val = pci_config_readl(bdf, off);
1064d6cefa9SPeter Xu 	pci_config_writel(bdf, off, bar);
1072455ef20SAlexander Gordeev 
1082455ef20SAlexander Gordeev 	return val;
1092455ef20SAlexander Gordeev }
1102455ef20SAlexander Gordeev 
1114d6cefa9SPeter Xu phys_addr_t pci_bar_size(struct pci_dev *dev, int bar_num)
1122455ef20SAlexander Gordeev {
1132455ef20SAlexander Gordeev 	uint32_t bar, size;
1142455ef20SAlexander Gordeev 
1152455ef20SAlexander Gordeev 	size = pci_bar_size_helper(dev, bar_num);
1162455ef20SAlexander Gordeev 	if (!size)
1172455ef20SAlexander Gordeev 		return 0;
1182455ef20SAlexander Gordeev 
1192455ef20SAlexander Gordeev 	bar = pci_bar_get(dev, bar_num);
1202455ef20SAlexander Gordeev 	size &= pci_bar_mask(bar);
1212455ef20SAlexander Gordeev 
1222455ef20SAlexander Gordeev 	if (pci_bar_is64(dev, bar_num)) {
1232455ef20SAlexander Gordeev 		phys_addr_t size64 = pci_bar_size_helper(dev, bar_num + 1);
1242455ef20SAlexander Gordeev 		size64 = (size64 << 32) | size;
1252455ef20SAlexander Gordeev 
1262455ef20SAlexander Gordeev 		return ~size64 + 1;
1272455ef20SAlexander Gordeev 	} else {
1282455ef20SAlexander Gordeev 		return ~size + 1;
1292455ef20SAlexander Gordeev 	}
1304932b58aSMichael S. Tsirkin }
1314932b58aSMichael S. Tsirkin 
1324d6cefa9SPeter Xu bool pci_bar_is_memory(struct pci_dev *dev, int bar_num)
1334932b58aSMichael S. Tsirkin {
1347aa83307SAlexander Gordeev 	uint32_t bar = pci_bar_get(dev, bar_num);
135ebb58e7eSAlexander Gordeev 
1364932b58aSMichael S. Tsirkin 	return !(bar & PCI_BASE_ADDRESS_SPACE_IO);
1374932b58aSMichael S. Tsirkin }
1384932b58aSMichael S. Tsirkin 
1394d6cefa9SPeter Xu bool pci_bar_is_valid(struct pci_dev *dev, int bar_num)
1404932b58aSMichael S. Tsirkin {
1417aa83307SAlexander Gordeev 	return pci_bar_get(dev, bar_num);
1424932b58aSMichael S. Tsirkin }
1432455ef20SAlexander Gordeev 
1444d6cefa9SPeter Xu bool pci_bar_is64(struct pci_dev *dev, int bar_num)
1452455ef20SAlexander Gordeev {
1462455ef20SAlexander Gordeev 	uint32_t bar = pci_bar_get(dev, bar_num);
1472455ef20SAlexander Gordeev 
1482455ef20SAlexander Gordeev 	if (bar & PCI_BASE_ADDRESS_SPACE_IO)
1492455ef20SAlexander Gordeev 		return false;
1502455ef20SAlexander Gordeev 
1512455ef20SAlexander Gordeev 	return (bar & PCI_BASE_ADDRESS_MEM_TYPE_MASK) ==
1522455ef20SAlexander Gordeev 		      PCI_BASE_ADDRESS_MEM_TYPE_64;
1532455ef20SAlexander Gordeev }
154e4611520SAlexander Gordeev 
1554d6cefa9SPeter Xu void pci_bar_print(struct pci_dev *dev, int bar_num)
156e4611520SAlexander Gordeev {
157e4611520SAlexander Gordeev 	phys_addr_t size, start, end;
158e4611520SAlexander Gordeev 	uint32_t bar;
159e4611520SAlexander Gordeev 
160e4611520SAlexander Gordeev 	size = pci_bar_size(dev, bar_num);
161e4611520SAlexander Gordeev 	if (!size)
162e4611520SAlexander Gordeev 		return;
163e4611520SAlexander Gordeev 
164e4611520SAlexander Gordeev 	bar = pci_bar_get(dev, bar_num);
165e4611520SAlexander Gordeev 	start = pci_bar_get_addr(dev, bar_num);
166e4611520SAlexander Gordeev 	end = start + size - 1;
167e4611520SAlexander Gordeev 
168e4611520SAlexander Gordeev 	if (pci_bar_is64(dev, bar_num)) {
169e4611520SAlexander Gordeev 		printf("BAR#%d,%d [%" PRIx64 "-%" PRIx64 " ",
170e4611520SAlexander Gordeev 		       bar_num, bar_num + 1, start, end);
171e4611520SAlexander Gordeev 	} else {
172e4611520SAlexander Gordeev 		printf("BAR#%d [%02x-%02x ",
173e4611520SAlexander Gordeev 		       bar_num, (uint32_t)start, (uint32_t)end);
174e4611520SAlexander Gordeev 	}
175e4611520SAlexander Gordeev 
176e4611520SAlexander Gordeev 	if (bar & PCI_BASE_ADDRESS_SPACE_IO) {
177e4611520SAlexander Gordeev 		printf("PIO");
178e4611520SAlexander Gordeev 	} else {
179e4611520SAlexander Gordeev 		printf("MEM");
180e4611520SAlexander Gordeev 		switch (bar & PCI_BASE_ADDRESS_MEM_TYPE_MASK) {
181e4611520SAlexander Gordeev 		case PCI_BASE_ADDRESS_MEM_TYPE_32:
182e4611520SAlexander Gordeev 			printf("32");
183e4611520SAlexander Gordeev 			break;
184e4611520SAlexander Gordeev 		case PCI_BASE_ADDRESS_MEM_TYPE_1M:
185e4611520SAlexander Gordeev 			printf("1M");
186e4611520SAlexander Gordeev 			break;
187e4611520SAlexander Gordeev 		case PCI_BASE_ADDRESS_MEM_TYPE_64:
188e4611520SAlexander Gordeev 			printf("64");
189e4611520SAlexander Gordeev 			break;
190e4611520SAlexander Gordeev 		default:
191e4611520SAlexander Gordeev 			assert(0);
192e4611520SAlexander Gordeev 		}
193e4611520SAlexander Gordeev 	}
194e4611520SAlexander Gordeev 
195e4611520SAlexander Gordeev 	if (bar & PCI_BASE_ADDRESS_MEM_PREFETCH)
196e4611520SAlexander Gordeev 		printf("/p");
197e4611520SAlexander Gordeev 
198e4611520SAlexander Gordeev 	printf("]");
199e4611520SAlexander Gordeev }
200e4611520SAlexander Gordeev 
20133d78b07SAlexander Gordeev void pci_dev_print_id(pcidevaddr_t dev)
202e4611520SAlexander Gordeev {
203e4611520SAlexander Gordeev 	printf("00.%02x.%1x %04x:%04x", dev / 8, dev % 8,
204e4611520SAlexander Gordeev 		pci_config_readw(dev, PCI_VENDOR_ID),
205e4611520SAlexander Gordeev 		pci_config_readw(dev, PCI_DEVICE_ID));
206e4611520SAlexander Gordeev }
207e4611520SAlexander Gordeev 
208e4611520SAlexander Gordeev static void pci_dev_print(pcidevaddr_t dev)
209e4611520SAlexander Gordeev {
210e4611520SAlexander Gordeev 	uint8_t header = pci_config_readb(dev, PCI_HEADER_TYPE);
211e4611520SAlexander Gordeev 	uint8_t progif = pci_config_readb(dev, PCI_CLASS_PROG);
212e4611520SAlexander Gordeev 	uint8_t subclass = pci_config_readb(dev, PCI_CLASS_DEVICE);
213e4611520SAlexander Gordeev 	uint8_t class = pci_config_readb(dev, PCI_CLASS_DEVICE + 1);
2144d6cefa9SPeter Xu 	struct pci_dev pci_dev;
215e4611520SAlexander Gordeev 	int i;
216e4611520SAlexander Gordeev 
2174d6cefa9SPeter Xu 	pci_dev_init(&pci_dev, dev);
2184d6cefa9SPeter Xu 
219e4611520SAlexander Gordeev 	pci_dev_print_id(dev);
220e4611520SAlexander Gordeev 	printf(" type %02x progif %02x class %02x subclass %02x\n",
221e4611520SAlexander Gordeev 	       header, progif, class, subclass);
222e4611520SAlexander Gordeev 
223e4611520SAlexander Gordeev 	if ((header & PCI_HEADER_TYPE_MASK) != PCI_HEADER_TYPE_NORMAL)
224e4611520SAlexander Gordeev 		return;
225e4611520SAlexander Gordeev 
226e954ce23SPeter Xu 	for (i = 0; i < PCI_BAR_NUM; i++) {
2274d6cefa9SPeter Xu 		if (pci_bar_size(&pci_dev, i)) {
228e4611520SAlexander Gordeev 			printf("\t");
2294d6cefa9SPeter Xu 			pci_bar_print(&pci_dev, i);
230e4611520SAlexander Gordeev 			printf("\n");
231e4611520SAlexander Gordeev 		}
2324d6cefa9SPeter Xu 		if (pci_bar_is64(&pci_dev, i))
233e4611520SAlexander Gordeev 			i++;
234e4611520SAlexander Gordeev 	}
235e4611520SAlexander Gordeev }
236e4611520SAlexander Gordeev 
237e4611520SAlexander Gordeev void pci_print(void)
238e4611520SAlexander Gordeev {
239e4611520SAlexander Gordeev 	pcidevaddr_t dev;
240e4611520SAlexander Gordeev 
2414d6cefa9SPeter Xu 	for (dev = 0; dev < PCI_DEVFN_MAX; ++dev) {
242e4611520SAlexander Gordeev 		if (pci_dev_exists(dev))
243e4611520SAlexander Gordeev 			pci_dev_print(dev);
244e4611520SAlexander Gordeev 	}
245e4611520SAlexander Gordeev }
246e954ce23SPeter Xu 
247e954ce23SPeter Xu void pci_scan_bars(struct pci_dev *dev)
248e954ce23SPeter Xu {
249e954ce23SPeter Xu 	int i;
250e954ce23SPeter Xu 
251e954ce23SPeter Xu 	for (i = 0; i < PCI_BAR_NUM; i++) {
252e954ce23SPeter Xu 		if (!pci_bar_is_valid(dev, i))
253e954ce23SPeter Xu 			continue;
254e954ce23SPeter Xu 		dev->resource[i] = pci_bar_get_addr(dev, i);
255e954ce23SPeter Xu 		if (pci_bar_is64(dev, i)) {
256e954ce23SPeter Xu 			i++;
257e954ce23SPeter Xu 			dev->resource[i] = (phys_addr_t)0;
258e954ce23SPeter Xu 		}
259e954ce23SPeter Xu 	}
260e954ce23SPeter Xu }
261*66082ed6SPeter Xu 
262*66082ed6SPeter Xu void pci_enable_defaults(struct pci_dev *dev)
263*66082ed6SPeter Xu {
264*66082ed6SPeter Xu 	pci_scan_bars(dev);
265*66082ed6SPeter Xu 	/* Enable device DMA operations */
266*66082ed6SPeter Xu 	pci_cmd_set_clr(dev, PCI_COMMAND_MASTER, 0);
267*66082ed6SPeter Xu }
268