xref: /kvm-unit-tests/lib/pci.c (revision e954ce23cd9f942b3ddf16f4932087b3ee789259)
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 
10e1cad5c8SAlexander Gordeev bool pci_dev_exists(pcidevaddr_t dev)
11e1cad5c8SAlexander Gordeev {
12e1cad5c8SAlexander Gordeev 	return (pci_config_readw(dev, PCI_VENDOR_ID) != 0xffff &&
13e1cad5c8SAlexander Gordeev 		pci_config_readw(dev, PCI_DEVICE_ID) != 0xffff);
14e1cad5c8SAlexander Gordeev }
15e1cad5c8SAlexander Gordeev 
164d6cefa9SPeter Xu void pci_dev_init(struct pci_dev *dev, pcidevaddr_t bdf)
174d6cefa9SPeter Xu {
184d6cefa9SPeter Xu        memset(dev, 0, sizeof(*dev));
194d6cefa9SPeter Xu        dev->bdf = bdf;
204d6cefa9SPeter Xu }
214d6cefa9SPeter Xu 
224932b58aSMichael S. Tsirkin /* Scan bus look for a specific device. Only bus 0 scanned for now. */
234932b58aSMichael S. Tsirkin pcidevaddr_t pci_find_dev(uint16_t vendor_id, uint16_t device_id)
244932b58aSMichael S. Tsirkin {
25ebb58e7eSAlexander Gordeev 	pcidevaddr_t dev;
26ebb58e7eSAlexander Gordeev 
274d6cefa9SPeter Xu 	for (dev = 0; dev < PCI_DEVFN_MAX; ++dev) {
28d8369c77SAlexander Gordeev 		if (pci_config_readw(dev, PCI_VENDOR_ID) == vendor_id &&
29d8369c77SAlexander Gordeev 		    pci_config_readw(dev, PCI_DEVICE_ID) == device_id)
304932b58aSMichael S. Tsirkin 			return dev;
314932b58aSMichael S. Tsirkin 	}
32ebb58e7eSAlexander Gordeev 
334932b58aSMichael S. Tsirkin 	return PCIDEVADDR_INVALID;
344932b58aSMichael S. Tsirkin }
354932b58aSMichael S. Tsirkin 
3633d78b07SAlexander Gordeev uint32_t pci_bar_mask(uint32_t bar)
372455ef20SAlexander Gordeev {
382455ef20SAlexander Gordeev 	return (bar & PCI_BASE_ADDRESS_SPACE_IO) ?
392455ef20SAlexander Gordeev 		PCI_BASE_ADDRESS_IO_MASK : PCI_BASE_ADDRESS_MEM_MASK;
402455ef20SAlexander Gordeev }
412455ef20SAlexander Gordeev 
424d6cefa9SPeter Xu uint32_t pci_bar_get(struct pci_dev *dev, int bar_num)
437aa83307SAlexander Gordeev {
444d6cefa9SPeter Xu 	return pci_config_readl(dev->bdf, PCI_BASE_ADDRESS_0 +
454d6cefa9SPeter Xu 				bar_num * 4);
467aa83307SAlexander Gordeev }
477aa83307SAlexander Gordeev 
484d6cefa9SPeter Xu phys_addr_t pci_bar_get_addr(struct pci_dev *dev, int bar_num)
494932b58aSMichael S. Tsirkin {
507aa83307SAlexander Gordeev 	uint32_t bar = pci_bar_get(dev, bar_num);
512455ef20SAlexander Gordeev 	uint32_t mask = pci_bar_mask(bar);
522455ef20SAlexander Gordeev 	uint64_t addr = bar & mask;
53647d2ab7SAlexander Gordeev 	phys_addr_t phys_addr;
54ebb58e7eSAlexander Gordeev 
552455ef20SAlexander Gordeev 	if (pci_bar_is64(dev, bar_num))
562455ef20SAlexander Gordeev 		addr |= (uint64_t)pci_bar_get(dev, bar_num + 1) << 32;
572455ef20SAlexander Gordeev 
584d6cefa9SPeter Xu 	phys_addr = pci_translate_addr(dev->bdf, addr);
59647d2ab7SAlexander Gordeev 	assert(phys_addr != INVALID_PHYS_ADDR);
60647d2ab7SAlexander Gordeev 
61647d2ab7SAlexander Gordeev 	return phys_addr;
622455ef20SAlexander Gordeev }
632455ef20SAlexander Gordeev 
644d6cefa9SPeter Xu void pci_bar_set_addr(struct pci_dev *dev, int bar_num, phys_addr_t addr)
65647f92c7SAlexander Gordeev {
66647f92c7SAlexander Gordeev 	int off = PCI_BASE_ADDRESS_0 + bar_num * 4;
67647f92c7SAlexander Gordeev 
684d6cefa9SPeter Xu 	pci_config_writel(dev->bdf, off, (uint32_t)addr);
69647f92c7SAlexander Gordeev 
70647f92c7SAlexander Gordeev 	if (pci_bar_is64(dev, bar_num))
714d6cefa9SPeter Xu 		pci_config_writel(dev->bdf, off + 4,
724d6cefa9SPeter Xu 				  (uint32_t)(addr >> 32));
73647f92c7SAlexander Gordeev }
74647f92c7SAlexander Gordeev 
752455ef20SAlexander Gordeev /*
762455ef20SAlexander Gordeev  * To determine the amount of address space needed by a PCI device,
772455ef20SAlexander Gordeev  * one must save the original value of the BAR, write a value of
782455ef20SAlexander Gordeev  * all 1's to the register, and then read it back. The amount of
792455ef20SAlexander Gordeev  * memory can be then determined by masking the information bits,
802455ef20SAlexander Gordeev  * performing a bitwise NOT, and incrementing the value by 1.
812455ef20SAlexander Gordeev  *
822455ef20SAlexander Gordeev  * The following pci_bar_size_helper() and pci_bar_size() functions
832455ef20SAlexander Gordeev  * implement the algorithm.
842455ef20SAlexander Gordeev  */
854d6cefa9SPeter Xu static uint32_t pci_bar_size_helper(struct pci_dev *dev, int bar_num)
862455ef20SAlexander Gordeev {
872455ef20SAlexander Gordeev 	int off = PCI_BASE_ADDRESS_0 + bar_num * 4;
884d6cefa9SPeter Xu 	uint16_t bdf = dev->bdf;
892455ef20SAlexander Gordeev 	uint32_t bar, val;
902455ef20SAlexander Gordeev 
914d6cefa9SPeter Xu 	bar = pci_config_readl(bdf, off);
924d6cefa9SPeter Xu 	pci_config_writel(bdf, off, ~0u);
934d6cefa9SPeter Xu 	val = pci_config_readl(bdf, off);
944d6cefa9SPeter Xu 	pci_config_writel(bdf, off, bar);
952455ef20SAlexander Gordeev 
962455ef20SAlexander Gordeev 	return val;
972455ef20SAlexander Gordeev }
982455ef20SAlexander Gordeev 
994d6cefa9SPeter Xu phys_addr_t pci_bar_size(struct pci_dev *dev, int bar_num)
1002455ef20SAlexander Gordeev {
1012455ef20SAlexander Gordeev 	uint32_t bar, size;
1022455ef20SAlexander Gordeev 
1032455ef20SAlexander Gordeev 	size = pci_bar_size_helper(dev, bar_num);
1042455ef20SAlexander Gordeev 	if (!size)
1052455ef20SAlexander Gordeev 		return 0;
1062455ef20SAlexander Gordeev 
1072455ef20SAlexander Gordeev 	bar = pci_bar_get(dev, bar_num);
1082455ef20SAlexander Gordeev 	size &= pci_bar_mask(bar);
1092455ef20SAlexander Gordeev 
1102455ef20SAlexander Gordeev 	if (pci_bar_is64(dev, bar_num)) {
1112455ef20SAlexander Gordeev 		phys_addr_t size64 = pci_bar_size_helper(dev, bar_num + 1);
1122455ef20SAlexander Gordeev 		size64 = (size64 << 32) | size;
1132455ef20SAlexander Gordeev 
1142455ef20SAlexander Gordeev 		return ~size64 + 1;
1152455ef20SAlexander Gordeev 	} else {
1162455ef20SAlexander Gordeev 		return ~size + 1;
1172455ef20SAlexander Gordeev 	}
1184932b58aSMichael S. Tsirkin }
1194932b58aSMichael S. Tsirkin 
1204d6cefa9SPeter Xu bool pci_bar_is_memory(struct pci_dev *dev, int bar_num)
1214932b58aSMichael S. Tsirkin {
1227aa83307SAlexander Gordeev 	uint32_t bar = pci_bar_get(dev, bar_num);
123ebb58e7eSAlexander Gordeev 
1244932b58aSMichael S. Tsirkin 	return !(bar & PCI_BASE_ADDRESS_SPACE_IO);
1254932b58aSMichael S. Tsirkin }
1264932b58aSMichael S. Tsirkin 
1274d6cefa9SPeter Xu bool pci_bar_is_valid(struct pci_dev *dev, int bar_num)
1284932b58aSMichael S. Tsirkin {
1297aa83307SAlexander Gordeev 	return pci_bar_get(dev, bar_num);
1304932b58aSMichael S. Tsirkin }
1312455ef20SAlexander Gordeev 
1324d6cefa9SPeter Xu bool pci_bar_is64(struct pci_dev *dev, int bar_num)
1332455ef20SAlexander Gordeev {
1342455ef20SAlexander Gordeev 	uint32_t bar = pci_bar_get(dev, bar_num);
1352455ef20SAlexander Gordeev 
1362455ef20SAlexander Gordeev 	if (bar & PCI_BASE_ADDRESS_SPACE_IO)
1372455ef20SAlexander Gordeev 		return false;
1382455ef20SAlexander Gordeev 
1392455ef20SAlexander Gordeev 	return (bar & PCI_BASE_ADDRESS_MEM_TYPE_MASK) ==
1402455ef20SAlexander Gordeev 		      PCI_BASE_ADDRESS_MEM_TYPE_64;
1412455ef20SAlexander Gordeev }
142e4611520SAlexander Gordeev 
1434d6cefa9SPeter Xu void pci_bar_print(struct pci_dev *dev, int bar_num)
144e4611520SAlexander Gordeev {
145e4611520SAlexander Gordeev 	phys_addr_t size, start, end;
146e4611520SAlexander Gordeev 	uint32_t bar;
147e4611520SAlexander Gordeev 
148e4611520SAlexander Gordeev 	size = pci_bar_size(dev, bar_num);
149e4611520SAlexander Gordeev 	if (!size)
150e4611520SAlexander Gordeev 		return;
151e4611520SAlexander Gordeev 
152e4611520SAlexander Gordeev 	bar = pci_bar_get(dev, bar_num);
153e4611520SAlexander Gordeev 	start = pci_bar_get_addr(dev, bar_num);
154e4611520SAlexander Gordeev 	end = start + size - 1;
155e4611520SAlexander Gordeev 
156e4611520SAlexander Gordeev 	if (pci_bar_is64(dev, bar_num)) {
157e4611520SAlexander Gordeev 		printf("BAR#%d,%d [%" PRIx64 "-%" PRIx64 " ",
158e4611520SAlexander Gordeev 		       bar_num, bar_num + 1, start, end);
159e4611520SAlexander Gordeev 	} else {
160e4611520SAlexander Gordeev 		printf("BAR#%d [%02x-%02x ",
161e4611520SAlexander Gordeev 		       bar_num, (uint32_t)start, (uint32_t)end);
162e4611520SAlexander Gordeev 	}
163e4611520SAlexander Gordeev 
164e4611520SAlexander Gordeev 	if (bar & PCI_BASE_ADDRESS_SPACE_IO) {
165e4611520SAlexander Gordeev 		printf("PIO");
166e4611520SAlexander Gordeev 	} else {
167e4611520SAlexander Gordeev 		printf("MEM");
168e4611520SAlexander Gordeev 		switch (bar & PCI_BASE_ADDRESS_MEM_TYPE_MASK) {
169e4611520SAlexander Gordeev 		case PCI_BASE_ADDRESS_MEM_TYPE_32:
170e4611520SAlexander Gordeev 			printf("32");
171e4611520SAlexander Gordeev 			break;
172e4611520SAlexander Gordeev 		case PCI_BASE_ADDRESS_MEM_TYPE_1M:
173e4611520SAlexander Gordeev 			printf("1M");
174e4611520SAlexander Gordeev 			break;
175e4611520SAlexander Gordeev 		case PCI_BASE_ADDRESS_MEM_TYPE_64:
176e4611520SAlexander Gordeev 			printf("64");
177e4611520SAlexander Gordeev 			break;
178e4611520SAlexander Gordeev 		default:
179e4611520SAlexander Gordeev 			assert(0);
180e4611520SAlexander Gordeev 		}
181e4611520SAlexander Gordeev 	}
182e4611520SAlexander Gordeev 
183e4611520SAlexander Gordeev 	if (bar & PCI_BASE_ADDRESS_MEM_PREFETCH)
184e4611520SAlexander Gordeev 		printf("/p");
185e4611520SAlexander Gordeev 
186e4611520SAlexander Gordeev 	printf("]");
187e4611520SAlexander Gordeev }
188e4611520SAlexander Gordeev 
18933d78b07SAlexander Gordeev void pci_dev_print_id(pcidevaddr_t dev)
190e4611520SAlexander Gordeev {
191e4611520SAlexander Gordeev 	printf("00.%02x.%1x %04x:%04x", dev / 8, dev % 8,
192e4611520SAlexander Gordeev 		pci_config_readw(dev, PCI_VENDOR_ID),
193e4611520SAlexander Gordeev 		pci_config_readw(dev, PCI_DEVICE_ID));
194e4611520SAlexander Gordeev }
195e4611520SAlexander Gordeev 
196e4611520SAlexander Gordeev static void pci_dev_print(pcidevaddr_t dev)
197e4611520SAlexander Gordeev {
198e4611520SAlexander Gordeev 	uint8_t header = pci_config_readb(dev, PCI_HEADER_TYPE);
199e4611520SAlexander Gordeev 	uint8_t progif = pci_config_readb(dev, PCI_CLASS_PROG);
200e4611520SAlexander Gordeev 	uint8_t subclass = pci_config_readb(dev, PCI_CLASS_DEVICE);
201e4611520SAlexander Gordeev 	uint8_t class = pci_config_readb(dev, PCI_CLASS_DEVICE + 1);
2024d6cefa9SPeter Xu 	struct pci_dev pci_dev;
203e4611520SAlexander Gordeev 	int i;
204e4611520SAlexander Gordeev 
2054d6cefa9SPeter Xu 	pci_dev_init(&pci_dev, dev);
2064d6cefa9SPeter Xu 
207e4611520SAlexander Gordeev 	pci_dev_print_id(dev);
208e4611520SAlexander Gordeev 	printf(" type %02x progif %02x class %02x subclass %02x\n",
209e4611520SAlexander Gordeev 	       header, progif, class, subclass);
210e4611520SAlexander Gordeev 
211e4611520SAlexander Gordeev 	if ((header & PCI_HEADER_TYPE_MASK) != PCI_HEADER_TYPE_NORMAL)
212e4611520SAlexander Gordeev 		return;
213e4611520SAlexander Gordeev 
214*e954ce23SPeter Xu 	for (i = 0; i < PCI_BAR_NUM; i++) {
2154d6cefa9SPeter Xu 		if (pci_bar_size(&pci_dev, i)) {
216e4611520SAlexander Gordeev 			printf("\t");
2174d6cefa9SPeter Xu 			pci_bar_print(&pci_dev, i);
218e4611520SAlexander Gordeev 			printf("\n");
219e4611520SAlexander Gordeev 		}
2204d6cefa9SPeter Xu 		if (pci_bar_is64(&pci_dev, i))
221e4611520SAlexander Gordeev 			i++;
222e4611520SAlexander Gordeev 	}
223e4611520SAlexander Gordeev }
224e4611520SAlexander Gordeev 
225e4611520SAlexander Gordeev void pci_print(void)
226e4611520SAlexander Gordeev {
227e4611520SAlexander Gordeev 	pcidevaddr_t dev;
228e4611520SAlexander Gordeev 
2294d6cefa9SPeter Xu 	for (dev = 0; dev < PCI_DEVFN_MAX; ++dev) {
230e4611520SAlexander Gordeev 		if (pci_dev_exists(dev))
231e4611520SAlexander Gordeev 			pci_dev_print(dev);
232e4611520SAlexander Gordeev 	}
233e4611520SAlexander Gordeev }
234*e954ce23SPeter Xu 
235*e954ce23SPeter Xu void pci_scan_bars(struct pci_dev *dev)
236*e954ce23SPeter Xu {
237*e954ce23SPeter Xu 	int i;
238*e954ce23SPeter Xu 
239*e954ce23SPeter Xu 	for (i = 0; i < PCI_BAR_NUM; i++) {
240*e954ce23SPeter Xu 		if (!pci_bar_is_valid(dev, i))
241*e954ce23SPeter Xu 			continue;
242*e954ce23SPeter Xu 		dev->resource[i] = pci_bar_get_addr(dev, i);
243*e954ce23SPeter Xu 		if (pci_bar_is64(dev, i)) {
244*e954ce23SPeter Xu 			i++;
245*e954ce23SPeter Xu 			dev->resource[i] = (phys_addr_t)0;
246*e954ce23SPeter Xu 		}
247*e954ce23SPeter Xu 	}
248*e954ce23SPeter Xu }
249