xref: /kvm-unit-tests/lib/pci.c (revision 33d78b078aa03e2e42d4a7477609085b6a5ec95c)
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 
164932b58aSMichael S. Tsirkin /* Scan bus look for a specific device. Only bus 0 scanned for now. */
174932b58aSMichael S. Tsirkin pcidevaddr_t pci_find_dev(uint16_t vendor_id, uint16_t device_id)
184932b58aSMichael S. Tsirkin {
19ebb58e7eSAlexander Gordeev 	pcidevaddr_t dev;
20ebb58e7eSAlexander Gordeev 
214932b58aSMichael S. Tsirkin 	for (dev = 0; dev < 256; ++dev) {
22d8369c77SAlexander Gordeev 		if (pci_config_readw(dev, PCI_VENDOR_ID) == vendor_id &&
23d8369c77SAlexander Gordeev 		    pci_config_readw(dev, PCI_DEVICE_ID) == device_id)
244932b58aSMichael S. Tsirkin 			return dev;
254932b58aSMichael S. Tsirkin 	}
26ebb58e7eSAlexander Gordeev 
274932b58aSMichael S. Tsirkin 	return PCIDEVADDR_INVALID;
284932b58aSMichael S. Tsirkin }
294932b58aSMichael S. Tsirkin 
30*33d78b07SAlexander Gordeev uint32_t pci_bar_mask(uint32_t bar)
312455ef20SAlexander Gordeev {
322455ef20SAlexander Gordeev 	return (bar & PCI_BASE_ADDRESS_SPACE_IO) ?
332455ef20SAlexander Gordeev 		PCI_BASE_ADDRESS_IO_MASK : PCI_BASE_ADDRESS_MEM_MASK;
342455ef20SAlexander Gordeev }
352455ef20SAlexander Gordeev 
36*33d78b07SAlexander Gordeev uint32_t pci_bar_get(pcidevaddr_t dev, int bar_num)
377aa83307SAlexander Gordeev {
387aa83307SAlexander Gordeev 	return pci_config_readl(dev, PCI_BASE_ADDRESS_0 + bar_num * 4);
397aa83307SAlexander Gordeev }
407aa83307SAlexander Gordeev 
41647f92c7SAlexander Gordeev phys_addr_t pci_bar_get_addr(pcidevaddr_t dev, int bar_num)
424932b58aSMichael S. Tsirkin {
437aa83307SAlexander Gordeev 	uint32_t bar = pci_bar_get(dev, bar_num);
442455ef20SAlexander Gordeev 	uint32_t mask = pci_bar_mask(bar);
452455ef20SAlexander Gordeev 	uint64_t addr = bar & mask;
46ebb58e7eSAlexander Gordeev 
472455ef20SAlexander Gordeev 	if (pci_bar_is64(dev, bar_num))
482455ef20SAlexander Gordeev 		addr |= (uint64_t)pci_bar_get(dev, bar_num + 1) << 32;
492455ef20SAlexander Gordeev 
502455ef20SAlexander Gordeev 	return pci_translate_addr(dev, addr);
512455ef20SAlexander Gordeev }
522455ef20SAlexander Gordeev 
53647f92c7SAlexander Gordeev void pci_bar_set_addr(pcidevaddr_t dev, int bar_num, phys_addr_t addr)
54647f92c7SAlexander Gordeev {
55647f92c7SAlexander Gordeev 	int off = PCI_BASE_ADDRESS_0 + bar_num * 4;
56647f92c7SAlexander Gordeev 
57647f92c7SAlexander Gordeev 	pci_config_writel(dev, off, (uint32_t)addr);
58647f92c7SAlexander Gordeev 
59647f92c7SAlexander Gordeev 	if (pci_bar_is64(dev, bar_num))
60647f92c7SAlexander Gordeev 		pci_config_writel(dev, off + 4, (uint32_t)(addr >> 32));
61647f92c7SAlexander Gordeev }
62647f92c7SAlexander Gordeev 
632455ef20SAlexander Gordeev /*
642455ef20SAlexander Gordeev  * To determine the amount of address space needed by a PCI device,
652455ef20SAlexander Gordeev  * one must save the original value of the BAR, write a value of
662455ef20SAlexander Gordeev  * all 1's to the register, and then read it back. The amount of
672455ef20SAlexander Gordeev  * memory can be then determined by masking the information bits,
682455ef20SAlexander Gordeev  * performing a bitwise NOT, and incrementing the value by 1.
692455ef20SAlexander Gordeev  *
702455ef20SAlexander Gordeev  * The following pci_bar_size_helper() and pci_bar_size() functions
712455ef20SAlexander Gordeev  * implement the algorithm.
722455ef20SAlexander Gordeev  */
732455ef20SAlexander Gordeev static uint32_t pci_bar_size_helper(pcidevaddr_t dev, int bar_num)
742455ef20SAlexander Gordeev {
752455ef20SAlexander Gordeev 	int off = PCI_BASE_ADDRESS_0 + bar_num * 4;
762455ef20SAlexander Gordeev 	uint32_t bar, val;
772455ef20SAlexander Gordeev 
782455ef20SAlexander Gordeev 	bar = pci_config_readl(dev, off);
792455ef20SAlexander Gordeev 	pci_config_writel(dev, off, ~0u);
802455ef20SAlexander Gordeev 	val = pci_config_readl(dev, off);
812455ef20SAlexander Gordeev 	pci_config_writel(dev, off, bar);
822455ef20SAlexander Gordeev 
832455ef20SAlexander Gordeev 	return val;
842455ef20SAlexander Gordeev }
852455ef20SAlexander Gordeev 
862455ef20SAlexander Gordeev phys_addr_t pci_bar_size(pcidevaddr_t dev, int bar_num)
872455ef20SAlexander Gordeev {
882455ef20SAlexander Gordeev 	uint32_t bar, size;
892455ef20SAlexander Gordeev 
902455ef20SAlexander Gordeev 	size = pci_bar_size_helper(dev, bar_num);
912455ef20SAlexander Gordeev 	if (!size)
922455ef20SAlexander Gordeev 		return 0;
932455ef20SAlexander Gordeev 
942455ef20SAlexander Gordeev 	bar = pci_bar_get(dev, bar_num);
952455ef20SAlexander Gordeev 	size &= pci_bar_mask(bar);
962455ef20SAlexander Gordeev 
972455ef20SAlexander Gordeev 	if (pci_bar_is64(dev, bar_num)) {
982455ef20SAlexander Gordeev 		phys_addr_t size64 = pci_bar_size_helper(dev, bar_num + 1);
992455ef20SAlexander Gordeev 		size64 = (size64 << 32) | size;
1002455ef20SAlexander Gordeev 
1012455ef20SAlexander Gordeev 		return ~size64 + 1;
1022455ef20SAlexander Gordeev 	} else {
1032455ef20SAlexander Gordeev 		return ~size + 1;
1042455ef20SAlexander Gordeev 	}
1054932b58aSMichael S. Tsirkin }
1064932b58aSMichael S. Tsirkin 
1074932b58aSMichael S. Tsirkin bool pci_bar_is_memory(pcidevaddr_t dev, int bar_num)
1084932b58aSMichael S. Tsirkin {
1097aa83307SAlexander Gordeev 	uint32_t bar = pci_bar_get(dev, bar_num);
110ebb58e7eSAlexander Gordeev 
1114932b58aSMichael S. Tsirkin 	return !(bar & PCI_BASE_ADDRESS_SPACE_IO);
1124932b58aSMichael S. Tsirkin }
1134932b58aSMichael S. Tsirkin 
1144932b58aSMichael S. Tsirkin bool pci_bar_is_valid(pcidevaddr_t dev, int bar_num)
1154932b58aSMichael S. Tsirkin {
1167aa83307SAlexander Gordeev 	return pci_bar_get(dev, bar_num);
1174932b58aSMichael S. Tsirkin }
1182455ef20SAlexander Gordeev 
1192455ef20SAlexander Gordeev bool pci_bar_is64(pcidevaddr_t dev, int bar_num)
1202455ef20SAlexander Gordeev {
1212455ef20SAlexander Gordeev 	uint32_t bar = pci_bar_get(dev, bar_num);
1222455ef20SAlexander Gordeev 
1232455ef20SAlexander Gordeev 	if (bar & PCI_BASE_ADDRESS_SPACE_IO)
1242455ef20SAlexander Gordeev 		return false;
1252455ef20SAlexander Gordeev 
1262455ef20SAlexander Gordeev 	return (bar & PCI_BASE_ADDRESS_MEM_TYPE_MASK) ==
1272455ef20SAlexander Gordeev 		      PCI_BASE_ADDRESS_MEM_TYPE_64;
1282455ef20SAlexander Gordeev }
129e4611520SAlexander Gordeev 
130*33d78b07SAlexander Gordeev void pci_bar_print(pcidevaddr_t dev, int bar_num)
131e4611520SAlexander Gordeev {
132e4611520SAlexander Gordeev 	phys_addr_t size, start, end;
133e4611520SAlexander Gordeev 	uint32_t bar;
134e4611520SAlexander Gordeev 
135e4611520SAlexander Gordeev 	size = pci_bar_size(dev, bar_num);
136e4611520SAlexander Gordeev 	if (!size)
137e4611520SAlexander Gordeev 		return;
138e4611520SAlexander Gordeev 
139e4611520SAlexander Gordeev 	bar = pci_bar_get(dev, bar_num);
140e4611520SAlexander Gordeev 	start = pci_bar_get_addr(dev, bar_num);
141e4611520SAlexander Gordeev 	end = start + size - 1;
142e4611520SAlexander Gordeev 
143e4611520SAlexander Gordeev 	if (pci_bar_is64(dev, bar_num)) {
144e4611520SAlexander Gordeev 		printf("BAR#%d,%d [%" PRIx64 "-%" PRIx64 " ",
145e4611520SAlexander Gordeev 		       bar_num, bar_num + 1, start, end);
146e4611520SAlexander Gordeev 	} else {
147e4611520SAlexander Gordeev 		printf("BAR#%d [%02x-%02x ",
148e4611520SAlexander Gordeev 		       bar_num, (uint32_t)start, (uint32_t)end);
149e4611520SAlexander Gordeev 	}
150e4611520SAlexander Gordeev 
151e4611520SAlexander Gordeev 	if (bar & PCI_BASE_ADDRESS_SPACE_IO) {
152e4611520SAlexander Gordeev 		printf("PIO");
153e4611520SAlexander Gordeev 	} else {
154e4611520SAlexander Gordeev 		printf("MEM");
155e4611520SAlexander Gordeev 		switch (bar & PCI_BASE_ADDRESS_MEM_TYPE_MASK) {
156e4611520SAlexander Gordeev 		case PCI_BASE_ADDRESS_MEM_TYPE_32:
157e4611520SAlexander Gordeev 			printf("32");
158e4611520SAlexander Gordeev 			break;
159e4611520SAlexander Gordeev 		case PCI_BASE_ADDRESS_MEM_TYPE_1M:
160e4611520SAlexander Gordeev 			printf("1M");
161e4611520SAlexander Gordeev 			break;
162e4611520SAlexander Gordeev 		case PCI_BASE_ADDRESS_MEM_TYPE_64:
163e4611520SAlexander Gordeev 			printf("64");
164e4611520SAlexander Gordeev 			break;
165e4611520SAlexander Gordeev 		default:
166e4611520SAlexander Gordeev 			assert(0);
167e4611520SAlexander Gordeev 		}
168e4611520SAlexander Gordeev 	}
169e4611520SAlexander Gordeev 
170e4611520SAlexander Gordeev 	if (bar & PCI_BASE_ADDRESS_MEM_PREFETCH)
171e4611520SAlexander Gordeev 		printf("/p");
172e4611520SAlexander Gordeev 
173e4611520SAlexander Gordeev 	printf("]");
174e4611520SAlexander Gordeev }
175e4611520SAlexander Gordeev 
176*33d78b07SAlexander Gordeev void pci_dev_print_id(pcidevaddr_t dev)
177e4611520SAlexander Gordeev {
178e4611520SAlexander Gordeev 	printf("00.%02x.%1x %04x:%04x", dev / 8, dev % 8,
179e4611520SAlexander Gordeev 		pci_config_readw(dev, PCI_VENDOR_ID),
180e4611520SAlexander Gordeev 		pci_config_readw(dev, PCI_DEVICE_ID));
181e4611520SAlexander Gordeev }
182e4611520SAlexander Gordeev 
183e4611520SAlexander Gordeev static void pci_dev_print(pcidevaddr_t dev)
184e4611520SAlexander Gordeev {
185e4611520SAlexander Gordeev 	uint8_t header = pci_config_readb(dev, PCI_HEADER_TYPE);
186e4611520SAlexander Gordeev 	uint8_t progif = pci_config_readb(dev, PCI_CLASS_PROG);
187e4611520SAlexander Gordeev 	uint8_t subclass = pci_config_readb(dev, PCI_CLASS_DEVICE);
188e4611520SAlexander Gordeev 	uint8_t class = pci_config_readb(dev, PCI_CLASS_DEVICE + 1);
189e4611520SAlexander Gordeev 	int i;
190e4611520SAlexander Gordeev 
191e4611520SAlexander Gordeev 	pci_dev_print_id(dev);
192e4611520SAlexander Gordeev 	printf(" type %02x progif %02x class %02x subclass %02x\n",
193e4611520SAlexander Gordeev 	       header, progif, class, subclass);
194e4611520SAlexander Gordeev 
195e4611520SAlexander Gordeev 	if ((header & PCI_HEADER_TYPE_MASK) != PCI_HEADER_TYPE_NORMAL)
196e4611520SAlexander Gordeev 		return;
197e4611520SAlexander Gordeev 
198e4611520SAlexander Gordeev 	for (i = 0; i < 6; i++) {
199e4611520SAlexander Gordeev 		if (pci_bar_size(dev, i)) {
200e4611520SAlexander Gordeev 			printf("\t");
201e4611520SAlexander Gordeev 			pci_bar_print(dev, i);
202e4611520SAlexander Gordeev 			printf("\n");
203e4611520SAlexander Gordeev 		}
204e4611520SAlexander Gordeev 		if (pci_bar_is64(dev, i))
205e4611520SAlexander Gordeev 			i++;
206e4611520SAlexander Gordeev 	}
207e4611520SAlexander Gordeev }
208e4611520SAlexander Gordeev 
209e4611520SAlexander Gordeev void pci_print(void)
210e4611520SAlexander Gordeev {
211e4611520SAlexander Gordeev 	pcidevaddr_t dev;
212e4611520SAlexander Gordeev 
213e4611520SAlexander Gordeev 	for (dev = 0; dev < 256; ++dev) {
214e4611520SAlexander Gordeev 		if (pci_dev_exists(dev))
215e4611520SAlexander Gordeev 			pci_dev_print(dev);
216e4611520SAlexander Gordeev 	}
217e4611520SAlexander Gordeev }
218